aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRaja R Harinath <harinath@hurrynot.org>2010-07-06 23:08:23 +0530
committerRaja R Harinath <harinath@hurrynot.org>2010-07-08 19:22:34 +0530
commit4cbcd3021d803012d31356b5cb36982a9921b2df (patch)
treec66d8f0ce1386d343e75441d096fe6bf17aa97eb /src
parent8d462d3531a702655c5b81c6c6c9409d6a320150 (diff)
downloadsvn2git-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.cpp26
-rw-r--r--src/repository.h1
-rw-r--r--src/svn.cpp63
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;
}
}