diff options
author | Raja R Harinath <harinath@hurrynot.org> | 2010-07-06 23:08:23 +0530 |
---|---|---|
committer | Raja R Harinath <harinath@hurrynot.org> | 2010-07-08 19:22:34 +0530 |
commit | 4cbcd3021d803012d31356b5cb36982a9921b2df (patch) | |
tree | c66d8f0ce1386d343e75441d096fe6bf17aa97eb /src | |
parent | 8d462d3531a702655c5b81c6c6c9409d6a320150 (diff) | |
download | svn2git-4cbcd3021d803012d31356b5cb36982a9921b2df.tar svn2git-4cbcd3021d803012d31356b5cb36982a9921b2df.tar.gz svn2git-4cbcd3021d803012d31356b5cb36982a9921b2df.tar.bz2 svn2git-4cbcd3021d803012d31356b5cb36982a9921b2df.tar.xz svn2git-4cbcd3021d803012d31356b5cb36982a9921b2df.zip |
Handle SVN directory deletes that lead to branch deletes
SVN directory deletes often indicate one or more branch deletions. However,
since the deleted directory isn't present in the resulting revision, several
of the indicators used by the rule-application mechanism aren't present.
This forces us to introduce several useless dummy rules to avoid errors.
We now note deletions and use the previous revision to determine several
properties, including whether the deleted item is a directory or not, and to
enumerate the contents of the directory in recurse mode.
We add an additional heuristic for unknown repositories -- i.e., when a rule
fired, but it's repository was invalid. We recurse in this case hoping to
catch a more specific rule. I believe this is safe: because some other rule
must've seen the same directory before, when it or a subdirectory was created,
and decided _not_ to create a repository at that point -- so recursing and/or
ignoring the contents of the just deleted directory won't corrupt the history,
it can only improve it.
We use mark :0 to note mark deletions internally, and in the progress logs.
(Note that cvs2svn creates wierd commits where a whole tree is copied first,
and then subtrees are pruned. In such cases, neither the previous revision
nor the current revision have the deleted directory -- we ignore this case
as before. There's no information loss since the final contents of the revision
are exactly what is desired.)
Diffstat (limited to 'src')
-rw-r--r-- | src/repository.cpp | 26 | ||||
-rw-r--r-- | src/repository.h | 1 | ||||
-rw-r--r-- | src/svn.cpp | 63 |
3 files changed, 67 insertions, 23 deletions
diff --git a/src/repository.cpp b/src/repository.cpp index 5250f71..847771f 100644 --- a/src/repository.cpp +++ b/src/repository.cpp @@ -141,7 +141,7 @@ void Repository::createBranch(const QString &branch, int revnum, branchRef.prepend("refs/heads/"); Branch &br = branches[branch]; - if (br.created && br.created != revnum) { + if (br.created && br.created != revnum && br.marks.last()) { QByteArray backupBranch = branchRef + '_' + QByteArray::number(revnum); qWarning() << branch << "already exists; backing up to" << backupBranch; @@ -193,6 +193,30 @@ void Repository::createBranch(const QString &branch, int revnum, + "\n\n"); } +void Repository::deleteBranch(const QString &branch, int revnum) +{ + QByteArray branchRef = branch.toUtf8(); + if (!branchRef.startsWith("refs/")) + branchRef.prepend("refs/heads/"); + + Branch &br = branches[branch]; + if (br.created && br.created != revnum && br.marks.last()) { + QByteArray backupBranch = branchRef + '_' + QByteArray::number(revnum); + qWarning() << "backing up branch" << branch << "to" << backupBranch; + + fastImport.write("reset " + backupBranch + "\nfrom " + branchRef + "\n\n"); + } + + br.created = revnum; + br.commits.append(revnum); + br.marks.append(0); + + static QByteArray null_sha(40, '0'); + fastImport.write("reset " + branchRef + "\nfrom " + null_sha + "\n\n" + "progress SVN r" + QByteArray::number(revnum) + + " branch " + branch.toUtf8() + " = :0 # delete\n\n"); +} + Repository::Transaction *Repository::newTransaction(const QString &branch, const QString &svnprefix, int revnum) { diff --git a/src/repository.h b/src/repository.h index f30b49f..65802bb 100644 --- a/src/repository.h +++ b/src/repository.h @@ -65,6 +65,7 @@ public: void reloadBranches(); void createBranch(const QString &branch, int revnum, const QString &branchFrom, int revFrom); + void deleteBranch(const QString &branch, int revnum); Transaction *newTransaction(const QString &branch, const QString &svnprefix, int revnum); void createAnnotatedTag(const QString &name, const QString &svnprefix, int revnum, diff --git a/src/svn.cpp b/src/svn.cpp index d29328a..69f91af 100644 --- a/src/svn.cpp +++ b/src/svn.cpp @@ -509,7 +509,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) { @@ -520,8 +519,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"; @@ -531,11 +528,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()) { @@ -546,6 +549,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) { @@ -576,7 +582,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 @@ -590,16 +603,30 @@ 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 (change->change_kind == svn_fs_path_change_delete && current == svnprefix) { + qDebug() << "repository" << repository << "branch" << branch << "deleted"; + repo->deleteBranch(branch, revnum); + return EXIT_SUCCESS; + } + QString previous; QString prevsvnprefix, prevrepository, prevbranch, prevpath; if (path_from != NULL) { - QString previous = QString::fromUtf8(path_from) + '/'; + previous = QString::fromUtf8(path_from) + '/'; MatchRuleList::ConstIterator prevmatch = findMatchRule(matchRules, rev_from, previous, NoIgnoreRule); if (prevmatch != matchRules.constEnd()) @@ -634,12 +661,6 @@ int SvnRevision::exportInternal(const char *key, const svn_fs_path_change_t *cha << 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) { @@ -651,16 +672,8 @@ int SvnRevision::exportInternal(const char *key, const svn_fs_path_change_t *cha 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; @@ -695,6 +708,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)); @@ -735,9 +752,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; } } |