aboutsummaryrefslogtreecommitdiffstats
path: root/src/svn.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/svn.cpp')
-rw-r--r--src/svn.cpp162
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;
}
}