diff options
Diffstat (limited to 'src/svn.cpp')
| -rw-r--r-- | src/svn.cpp | 162 |
1 files changed, 102 insertions, 60 deletions
diff --git a/src/svn.cpp b/src/svn.cpp index 2a9ffcb..594a750 100644 --- a/src/svn.cpp +++ b/src/svn.cpp @@ -512,7 +512,6 @@ int SvnRevision::exportEntry(const char *key, const svn_fs_path_change_t *change svn_boolean_t is_dir; SVN_ERR(svn_fs_is_dir(&is_dir, fs_root, key, revpool)); if (is_dir) { - current += '/'; if (change->change_kind == svn_fs_path_change_modify || change->change_kind == svn_fs_path_change_add) { if (path_from == NULL) { @@ -523,8 +522,6 @@ int SvnRevision::exportEntry(const char *key, const svn_fs_path_change_t *change } qDebug() << " " << key << "was copied from" << path_from << "rev" << rev_from; - } else if (change->change_kind == svn_fs_path_change_delete) { - qDebug() << " " << key << "was deleted"; } else if (change->change_kind == svn_fs_path_change_replace) { if (path_from == NULL) qDebug() << " " << key << "was replaced"; @@ -534,11 +531,17 @@ int SvnRevision::exportEntry(const char *key, const svn_fs_path_change_t *change qCritical() << " " << key << "was reset, panic!"; return EXIT_FAILURE; } else { + // if change_kind == delete, it shouldn't come into this arm of the 'is_dir' test qCritical() << " " << key << "has unhandled change kind " << change->change_kind << ", panic!"; return EXIT_FAILURE; } + } else if (change->change_kind == svn_fs_path_change_delete) { + is_dir = wasDir(fs, revnum - 1, key, revpool); } + if (is_dir) + current += '/'; + // find the first rule that matches this pathname MatchRuleList::ConstIterator match = findMatchRule(matchRules, revnum, current); if (match != matchRules.constEnd()) { @@ -549,6 +552,9 @@ int SvnRevision::exportEntry(const char *key, const svn_fs_path_change_t *change if (is_dir && path_from != NULL) { qDebug() << current << "is a copy-with-history, auto-recursing"; return recurse(key, change, path_from, rev_from, changes, revpool); + } else if (is_dir && change->change_kind == svn_fs_path_change_delete) { + qDebug() << current << "deleted, auto-recursing"; + return recurse(key, change, path_from, rev_from, changes, revpool); } else if (wasDir(fs, revnum - 1, key, revpool)) { qDebug() << current << "was a directory; ignoring"; } else if (change->change_kind == svn_fs_path_change_delete) { @@ -579,7 +585,14 @@ int SvnRevision::exportDispatch(const char *key, const svn_fs_path_change_t *cha return recurse(key, change, path_from, rev_from, changes, pool); case Rules::Match::Export: - return exportInternal(key, change, path_from, rev_from, current, rule); + if (exportInternal(key, change, path_from, rev_from, current, rule) == EXIT_SUCCESS) + return EXIT_SUCCESS; + if (change->change_kind != svn_fs_path_change_delete) + return EXIT_FAILURE; + // we know that the default action inside recurse is to recurse further or to ignore, + // either of which is reasonably safe for deletion + qWarning() << "deleting unknown path" << current << "; auto-recursing"; + return recurse(key, change, path_from, rev_from, changes, pool); } // never reached @@ -593,72 +606,86 @@ int SvnRevision::exportInternal(const char *key, const svn_fs_path_change_t *cha QString svnprefix, repository, branch, path; splitPathName(rule, current, &svnprefix, &repository, &branch, &path); + Repository *repo = repositories.value(repository, 0); + if (!repo) { + if (change->change_kind != svn_fs_path_change_delete) + qCritical() << "Rule" << rule + << "references unknown repository" << repository; + return EXIT_FAILURE; + } + printf("."); fflush(stdout); // qDebug() << " " << qPrintable(current) << "rev" << revnum << "->" // << qPrintable(repository) << qPrintable(branch) << qPrintable(path); - if (path.isEmpty() && path_from != NULL) { - QString previous = QString::fromUtf8(path_from) + '/'; + if (change->change_kind == svn_fs_path_change_delete && current == svnprefix) { + qDebug() << "repository" << repository << "branch" << branch << "deleted"; + return repo->deleteBranch(branch, revnum); + } + + QString previous; + QString prevsvnprefix, prevrepository, prevbranch, prevpath; + + if (path_from != NULL) { + previous = QString::fromUtf8(path_from) + '/'; MatchRuleList::ConstIterator prevmatch = findMatchRule(matchRules, rev_from, previous, NoIgnoreRule); - if (prevmatch != matchRules.constEnd()) { - QString prevsvnprefix, prevrepository, prevbranch, prevpath; + if (prevmatch != matchRules.constEnd()) splitPathName(*prevmatch, previous, &prevsvnprefix, &prevrepository, &prevbranch, &prevpath); - - if (!prevpath.isEmpty()) { - qDebug() << qPrintable(current) << "is a partial branch of repository" - << qPrintable(prevrepository) << "branch" - << qPrintable(prevbranch) << "subdir" - << qPrintable(prevpath); - } else if (prevrepository != repository) { - qWarning() << qPrintable(current) << "rev" << revnum - << "is a cross-repository copy (from repository" - << qPrintable(prevrepository) << "branch" - << qPrintable(prevbranch) << "path" - << qPrintable(prevpath) << "rev" << rev_from << ")"; - } else if (prevbranch == branch) { - // same branch and same repository - qDebug() << qPrintable(current) << "rev" << revnum - << "is an SVN rename from" - << qPrintable(previous) << "rev" << rev_from; - return EXIT_SUCCESS; - } else { - // same repository but not same branch - // this means this is a plain branch - qDebug() << qPrintable(repository) << ": branch" - << qPrintable(branch) << "is branching from" - << qPrintable(prevbranch); - - Repository *repo = repositories.value(repository, 0); - if (!repo) { - qCritical() << "Rule" << rule - << "references unknown repository" << repository; - return EXIT_FAILURE; - } - - repo->createBranch(branch, revnum, prevbranch, rev_from); - if (rule.annotate) { - // create an annotated tag - fetchRevProps(); - repo->createAnnotatedTag(branch, svnprefix, revnum, authorident, - epoch, log); - } - return EXIT_SUCCESS; - } - } + else + path_from = NULL; } + // current == svnprefix => we're dealing with the contents of the whole branch here + if (path_from != NULL && current == svnprefix) { + if (previous != prevsvnprefix) { + // source is not the whole of its branch + qDebug() << qPrintable(current) << "is a partial branch of repository" + << qPrintable(prevrepository) << "branch" + << qPrintable(prevbranch) << "subdir" + << qPrintable(prevpath); + } else if (prevrepository != repository) { + qWarning() << qPrintable(current) << "rev" << revnum + << "is a cross-repository copy (from repository" + << qPrintable(prevrepository) << "branch" + << qPrintable(prevbranch) << "path" + << qPrintable(prevpath) << "rev" << rev_from << ")"; + } else if (path != prevpath) { + qDebug() << qPrintable(current) + << "is a branch copy which renames base directory of all contents" + << qPrintable(prevpath) << "to" << qPrintable(path); + // FIXME: Handle with fast-import 'file rename' facility + // ??? Might need special handling when path == / or prevpath == / + } else { + if (prevbranch == branch) { + // same branch and same repository + qDebug() << qPrintable(current) << "rev" << revnum + << "is reseating branch" << qPrintable(branch) + << "to an earlier revision" + << qPrintable(previous) << "rev" << rev_from; + } else { + // same repository but not same branch + // this means this is a plain branch + qDebug() << qPrintable(repository) << ": branch" + << qPrintable(branch) << "is branching from" + << qPrintable(prevbranch); + } + + if (repo->createBranch(branch, revnum, prevbranch, rev_from) == EXIT_FAILURE) + return EXIT_FAILURE; + if (rule.annotate) { + // create an annotated tag + fetchRevProps(); + repo->createAnnotatedTag(branch, svnprefix, revnum, authorident, + epoch, log); + } + return EXIT_SUCCESS; + } + } Repository::Transaction *txn = transactions.value(repository + branch, 0); if (!txn) { - Repository *repo = repositories.value(repository, 0); - if (!repo) { - qCritical() << "Rule" << rule - << "references unknown repository" << repository; - return EXIT_FAILURE; - } - txn = repo->newTransaction(branch, svnprefix, revnum); if (!txn) return EXIT_FAILURE; @@ -666,6 +693,15 @@ int SvnRevision::exportInternal(const char *key, const svn_fs_path_change_t *cha transactions.insert(repository + branch, txn); } + // + // If this path was copied from elsewhere, use it to infer _some_ + // merge points. This heuristic is fairly useful for tracking + // changes across directory re-organizations and wholesale branch + // imports. + // + if (path_from != NULL && prevrepository == repository) + txn->noteCopyFromBranch (prevbranch, rev_from); + if (change->change_kind == svn_fs_path_change_replace && path_from == NULL) txn->deleteFile(path); if (change->change_kind == svn_fs_path_change_delete) { @@ -684,6 +720,10 @@ int SvnRevision::recurse(const char *path, const svn_fs_path_change_t *change, const char *path_from, svn_revnum_t rev_from, apr_hash_t *changes, apr_pool_t *pool) { + svn_fs_root_t *fs_root = this->fs_root; + if (change->change_kind == svn_fs_path_change_delete) + SVN_ERR(svn_fs_revision_root(&fs_root, fs, revnum - 1, pool)); + // get the dir listing apr_hash_t *entries; SVN_ERR(svn_fs_dir_entries(&entries, fs_root, path, pool)); @@ -724,9 +764,11 @@ int SvnRevision::recurse(const char *path, const svn_fs_path_change_t *change, rev_from, changes, current, *match, dirpool) == EXIT_FAILURE) return EXIT_FAILURE; } else { - qCritical() << current << "rev" << revnum - << "did not match any rules; cannot continue"; - return EXIT_FAILURE; + qDebug() << current << "rev" << revnum + << "did not match any rules; auto-recursing"; + if (recurse(entry, change, entryFrom.isNull() ? 0 : entryFrom.constData(), + rev_from, changes, dirpool) == EXIT_FAILURE) + return EXIT_FAILURE; } } |
