aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/CommandLineParser.cpp3
-rw-r--r--src/CommandLineParser.h4
-rw-r--r--src/main.cpp10
-rw-r--r--src/repository.cpp2
-rw-r--r--src/ruleparser.cpp47
-rw-r--r--src/ruleparser.h48
-rw-r--r--src/svn.cpp78
-rw-r--r--src/svn.h2
8 files changed, 141 insertions, 53 deletions
diff --git a/src/CommandLineParser.cpp b/src/CommandLineParser.cpp
index 7e7658a..39f40d6 100644
--- a/src/CommandLineParser.cpp
+++ b/src/CommandLineParser.cpp
@@ -77,7 +77,8 @@ void CommandLineParser::Private::addDefinitions(const CommandLineOption * option
for (int i=0; options[i].specification != 0; i++) {
OptionDefinition definition;
QString option = QString::fromLatin1(options[i].specification);
- if (option.indexOf(QLatin1Char(',')) >= 0) {
+ // options with optional params are written as "--option required[, optional]
+ if (option.indexOf(QLatin1Char(',')) >= 0 && ( option.indexOf(QLatin1Char('[')) < 0 || option.indexOf(QLatin1Char(']')) < 0) ) {
QStringList optionParts = option.split(QLatin1Char(','), QString::SkipEmptyParts);
if (optionParts.count() != 2) {
qWarning() << "option definition '" << option << "' is faulty; only one ',' allowed";
diff --git a/src/CommandLineParser.h b/src/CommandLineParser.h
index 62c04bb..f5515d1 100644
--- a/src/CommandLineParser.h
+++ b/src/CommandLineParser.h
@@ -29,11 +29,13 @@ struct CommandLineOption {
* <li>"-a, --all" </li>
* <li>"--version" </li>
* <li>"--type name" </li>
+ * <li>"--list item[,item]" </li>
* <li>"-f, --format name [suffix] [foo]" </li> </ol>
* Number 1 allows the user to either type -a or --all (or /A on Windows) to activate this option.
* Number 2 allows the user to type --version to activate this option.
* Number 3 requires the user to type a single argument after the option.
- * Number 4 Allows the user to either use -f or --format, which is followed by one required argument
+ * Number 4 allows the user to use an option that takes a required argument and one or more optional ones
+ * Number 5 Allows the user to either use -f or --format, which is followed by one required argument
* and optionally 2 more arguments.
*/
const char *specification;
diff --git a/src/main.cpp b/src/main.cpp
index 73d2378..7de7e45 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -69,7 +69,7 @@ QHash<QByteArray, QByteArray> loadIdentityMapFile(const QString &fileName)
static const CommandLineOption options[] = {
{"--identity-map FILENAME", "provide map between svn username and email"},
- {"--rules FILENAME", "the rules file that determines what goes where"},
+ {"--rules FILENAME[,FILENAME]", "the rules file(s) that determines what goes where"},
{"--add-metadata", "if passed, each git commit will have svn commit info"},
{"--resume-from revision", "start importing at svn revision number"},
{"--max-rev revision", "stop importing at svn revision number"},
@@ -114,8 +114,8 @@ int main(int argc, char **argv)
QCoreApplication app(argc, argv);
// Load the configuration
- Rules rules(args->optionArgument(QLatin1String("rules")));
- rules.load();
+ RulesList rulesList(args->optionArgument(QLatin1String("rules")));
+ rulesList.load();
int resume_from = args->optionArgument(QLatin1String("resume-from")).toInt();
int max_rev = args->optionArgument(QLatin1String("max-rev")).toInt();
@@ -126,7 +126,7 @@ int main(int argc, char **argv)
int cutoff = resume_from ? resume_from : INT_MAX;
retry:
int min_rev = 1;
- foreach (Rules::Repository rule, rules.repositories()) {
+ foreach (Rules::Repository rule, rulesList.allRepositories()) {
Repository *repo = makeRepository(rule, repositories);
if (!repo)
return EXIT_FAILURE;
@@ -172,7 +172,7 @@ int main(int argc, char **argv)
Svn::initialize();
Svn svn(args->arguments().first());
- svn.setMatchRules(rules.matchRules());
+ svn.setMatchRules(rulesList.allMatchRules());
svn.setRepositories(repositories);
svn.setIdentityMap(loadIdentityMapFile(args->optionArgument("identity-map")));
diff --git a/src/repository.cpp b/src/repository.cpp
index d4cb2ad..2a2b436 100644
--- a/src/repository.cpp
+++ b/src/repository.cpp
@@ -275,7 +275,7 @@ Repository *makeRepository(const Rules::Repository &rule, const QHash<QString, R
return new FastImportRepository(rule);
Repository *r = repositories[rule.forwardTo];
if (!r) {
- qCritical() << "no repository with name" << rule.forwardTo << "found at line" << rule.lineNumber;
+ qCritical() << "no repository with name" << rule.forwardTo << "found at" << rule.info();
return r;
}
return new PrefixingRepository(r, rule.prefix);
diff --git a/src/ruleparser.cpp b/src/ruleparser.cpp
index 10a22ac..4ab8f08 100644
--- a/src/ruleparser.cpp
+++ b/src/ruleparser.cpp
@@ -22,6 +22,41 @@
#include "ruleparser.h"
+RulesList::RulesList(const QString &filenames)
+ : m_filenames(filenames)
+{
+}
+
+RulesList::~RulesList() {}
+
+void RulesList::load()
+{
+ foreach(const QString filename, m_filenames.split(',') ) {
+ qDebug() << "Loading rules from:" << filename;
+ Rules *rules = new Rules(filename);
+ m_rules.append(rules);
+ rules->load();
+ m_allrepositories.append(rules->repositories());
+ QList<Rules::Match> matchRules = rules->matchRules();
+ m_allMatchRules.append( QList<Rules::Match>(matchRules));
+ }
+}
+
+const QList<Rules::Repository> RulesList::allRepositories() const
+{
+ return m_allrepositories;
+}
+
+const QList<QList<Rules::Match> > RulesList::allMatchRules() const
+{
+ return m_allMatchRules;
+}
+
+const QList<Rules*> RulesList::rules() const
+{
+ return m_rules;
+}
+
Rules::Rules(const QString &fn)
: filename(fn)
{
@@ -31,12 +66,12 @@ Rules::~Rules()
{
}
-QList<Rules::Repository> Rules::repositories()
+const QList<Rules::Repository> Rules::repositories() const
{
return m_repositories;
}
-QList<Rules::Match> Rules::matchRules()
+const QList<Rules::Match> Rules::matchRules() const
{
return m_matchRules;
}
@@ -187,12 +222,18 @@ void Rules::load()
repo = Repository(); // clear
repo.name = repoLine.cap(1);
repo.lineNumber = lineNumber;
+ repo.filename = filename;
} else if (isMatchRule) {
// match rule
state = ReadingMatch;
match = Match();
match.rx = QRegExp(matchLine.cap(1), Qt::CaseSensitive, QRegExp::RegExp2);
+ if( !match.rx.isValid() )
+ qFatal("Malformed regular expression '%s' in file:'%s':%d, Error: %s",
+ qPrintable(matchLine.cap(1)), qPrintable(filename), lineNumber,
+ qPrintable(match.rx.errorString()));
match.lineNumber = lineNumber;
+ match.filename = filename;
} else if (isVariableRule) {
QString variable = declareLine.cap(1);
QString value = declareLine.cap(2);
@@ -207,7 +248,7 @@ void Rules::load()
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug s, const Rules::Match &rule)
{
- s.nospace() << rule.rx.pattern() << " (line " << rule.lineNumber << ")";
+ s.nospace() << rule.info();
return s.space();
}
diff --git a/src/ruleparser.h b/src/ruleparser.h
index afd1db2..5d05d1c 100644
--- a/src/ruleparser.h
+++ b/src/ruleparser.h
@@ -22,11 +22,18 @@
#include <QRegExp>
#include <QString>
#include <QStringList>
+#include <QStringBuilder>
class Rules
{
public:
- struct Repository
+ struct Rule
+ {
+ QString filename;
+ int lineNumber;
+ Rule() : lineNumber(0) {}
+ };
+ struct Repository : Rule
{
struct Branch
{
@@ -35,15 +42,19 @@ public:
QString name;
QList<Branch> branches;
- int lineNumber;
QString forwardTo;
QString prefix;
- Repository() : lineNumber(0) { }
+ Repository() { }
+ const QString info() const {
+ const QString info = Rule::filename % ":" % QByteArray::number(Rule::lineNumber);
+ return info;
+ }
+
};
- struct Match
+ struct Match : Rule
{
QRegExp rx;
QString repository;
@@ -51,7 +62,6 @@ public:
QString prefix;
int minRevision;
int maxRevision;
- int lineNumber;
bool annotate;
enum Action {
@@ -60,14 +70,18 @@ public:
Recurse
} action;
- Match() : minRevision(-1), maxRevision(-1), lineNumber(0), annotate(false), action(Ignore) { }
+ Match() : minRevision(-1), maxRevision(-1), annotate(false), action(Ignore) { }
+ const QString info() const {
+ const QString info = rx.pattern() % " (" % Rule::filename % ":" % QByteArray::number(Rule::lineNumber) % ")";
+ return info;
+ }
};
Rules(const QString &filename);
~Rules();
- QList<Repository> repositories();
- QList<Match> matchRules();
+ const QList<Repository> repositories() const;
+ const QList<Match> matchRules() const;
void load();
QStringList readRules(const QString &filename) const;
@@ -78,6 +92,24 @@ private:
QList<Match> m_matchRules;
};
+class RulesList
+{
+public:
+ RulesList( const QString &filenames);
+ ~RulesList();
+
+ const QList<Rules::Repository> allRepositories() const;
+ const QList<QList<Rules::Match> > allMatchRules() const;
+ const QList<Rules*> rules() const;
+ void load();
+
+private:
+ QString m_filenames;
+ QList<Rules*> m_rules;
+ QList<Rules::Repository> m_allrepositories;
+ QList<QList<Rules::Match> > m_allMatchRules;
+};
+
#ifndef QT_NO_DEBUG_STREAM
class QDebug;
QDebug operator<<(QDebug, const Rules::Match &);
diff --git a/src/svn.cpp b/src/svn.cpp
index 7511326..28d6396 100644
--- a/src/svn.cpp
+++ b/src/svn.cpp
@@ -74,7 +74,7 @@ public:
class SvnPrivate
{
public:
- MatchRuleList matchRules;
+ QList<MatchRuleList> allMatchRules;
RepositoryHash repositories;
IdentityHash identities;
@@ -113,9 +113,9 @@ Svn::~Svn()
delete d;
}
-void Svn::setMatchRules(const MatchRuleList &matchRules)
+void Svn::setMatchRules(const QList<MatchRuleList> &allMatchRules)
{
- d->matchRules = matchRules;
+ d->allMatchRules = allMatchRules;
}
void Svn::setRepositories(const RepositoryHash &repositories)
@@ -376,7 +376,7 @@ class SvnRevision
public:
AprAutoPool pool;
QHash<QString, Repository::Transaction *> transactions;
- MatchRuleList matchRules;
+ QList<MatchRuleList> allMatchRules;
RepositoryHash repositories;
IdentityHash identities;
@@ -410,19 +410,19 @@ public:
int exportDispatch(const char *path, const svn_fs_path_change_t *change,
const char *path_from, svn_revnum_t rev_from,
apr_hash_t *changes, const QString &current, const Rules::Match &rule,
- apr_pool_t *pool);
+ const MatchRuleList &matchRules, apr_pool_t *pool);
int exportInternal(const char *path, const svn_fs_path_change_t *change,
const char *path_from, svn_revnum_t rev_from,
- const QString &current, const Rules::Match &rule);
+ const QString &current, const Rules::Match &rule, const MatchRuleList &matchRules);
int recurse(const char *path, const svn_fs_path_change_t *change,
- const char *path_from, svn_revnum_t rev_from,
+ const char *path_from, const MatchRuleList &matchRules, svn_revnum_t rev_from,
apr_hash_t *changes, apr_pool_t *pool);
};
int SvnPrivate::exportRevision(int revnum)
{
SvnRevision rev(revnum, fs, global_pool);
- rev.matchRules = matchRules;
+ rev.allMatchRules = allMatchRules;
rev.repositories = repositories;
rev.identities = identities;
@@ -550,20 +550,33 @@ int SvnRevision::exportEntry(const char *key, const svn_fs_path_change_t *change
if (is_dir)
current += '/';
- // find the first rule that matches this pathname
- MatchRuleList::ConstIterator match = findMatchRule(matchRules, revnum, current);
- if (match != matchRules.constEnd()) {
- const Rules::Match &rule = *match;
- return exportDispatch(key, change, path_from, rev_from, changes, current, rule, revpool);
+ //MultiRule: loop start
+ //Replace all returns with continue,
+ bool isHandled = false;
+ foreach ( const MatchRuleList matchRules, allMatchRules ) {
+ // find the first rule that matches this pathname
+ MatchRuleList::ConstIterator match = findMatchRule(matchRules, revnum, current);
+ if (match != matchRules.constEnd()) {
+ const Rules::Match &rule = *match;
+ if ( exportDispatch(key, change, path_from, rev_from, changes, current, rule, matchRules, revpool) == EXIT_FAILURE )
+ return EXIT_FAILURE;
+ isHandled = true;
+ } else if (is_dir && path_from != NULL) {
+ qDebug() << current << "is a copy-with-history, auto-recursing";
+ if ( recurse(key, change, path_from, matchRules, rev_from, changes, revpool) == EXIT_FAILURE )
+ return EXIT_FAILURE;
+ isHandled = true;
+ } else if (is_dir && change->change_kind == svn_fs_path_change_delete) {
+ qDebug() << current << "deleted, auto-recursing";
+ if ( recurse(key, change, path_from, matchRules, rev_from, changes, revpool) == EXIT_FAILURE )
+ return EXIT_FAILURE;
+ isHandled = true;
+ }
}
-
- 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)) {
+ if ( isHandled ) {
+ return EXIT_SUCCESS;
+ }
+ if (wasDir(fs, revnum - 1, key, revpool)) {
qDebug() << current << "was a directory; ignoring";
} else if (change->change_kind == svn_fs_path_change_delete) {
qDebug() << current << "is being deleted but I don't know anything about it; ignoring";
@@ -571,14 +584,13 @@ int SvnRevision::exportEntry(const char *key, const svn_fs_path_change_t *change
qCritical() << current << "did not match any rules; cannot continue";
return EXIT_FAILURE;
}
-
return EXIT_SUCCESS;
}
int SvnRevision::exportDispatch(const char *key, const svn_fs_path_change_t *change,
const char *path_from, svn_revnum_t rev_from,
apr_hash_t *changes, const QString &current,
- const Rules::Match &rule, apr_pool_t *pool)
+ const Rules::Match &rule, const MatchRuleList &matchRules, apr_pool_t *pool)
{
//if(ruledebug)
// qDebug() << "rev" << revnum << qPrintable(current) << "matched rule:" << rule.lineNumber << "(" << rule.rx.pattern() << ")";
@@ -590,23 +602,23 @@ int SvnRevision::exportDispatch(const char *key, const svn_fs_path_change_t *cha
case Rules::Match::Recurse:
if(ruledebug)
- qDebug() << "rev" << revnum << qPrintable(current) << "matched rule:" << rule.lineNumber << "(" << rule.rx.pattern() << ")" << " " << "recursing.";
- return recurse(key, change, path_from, rev_from, changes, pool);
+ qDebug() << "rev" << revnum << qPrintable(current) << "matched rule:" << rule.info() << " " << "recursing.";
+ return recurse(key, change, path_from, matchRules, rev_from, changes, pool);
case Rules::Match::Export:
if(ruledebug)
- qDebug() << "rev" << revnum << qPrintable(current) << "matched rule:" << rule.lineNumber << "(" << rule.rx.pattern() << ")" << " " << "exporting.";
- if (exportInternal(key, change, path_from, rev_from, current, rule) == EXIT_SUCCESS)
+ qDebug() << "rev" << revnum << qPrintable(current) << "matched rule:" << rule.info() << " " << "exporting.";
+ if (exportInternal(key, change, path_from, rev_from, current, rule, matchRules) == EXIT_SUCCESS)
return EXIT_SUCCESS;
if (change->change_kind != svn_fs_path_change_delete) {
if(ruledebug)
- qDebug() << "rev" << revnum << qPrintable(current) << "matched rule:" << rule.lineNumber << "(" << rule.rx.pattern() << ")" << " " << "Unable to export non path removal.";
+ qDebug() << "rev" << revnum << qPrintable(current) << "matched rule:" << rule.info() << " " << "Unable to export non path removal.";
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);
+ return recurse(key, change, path_from, matchRules, rev_from, changes, pool);
}
// never reached
@@ -615,7 +627,7 @@ int SvnRevision::exportDispatch(const char *key, const svn_fs_path_change_t *cha
int SvnRevision::exportInternal(const char *key, const svn_fs_path_change_t *change,
const char *path_from, svn_revnum_t rev_from,
- const QString &current, const Rules::Match &rule)
+ const QString &current, const Rules::Match &rule, const MatchRuleList &matchRules)
{
QString svnprefix, repository, branch, path;
splitPathName(rule, current, &svnprefix, &repository, &branch, &path);
@@ -752,7 +764,7 @@ int SvnRevision::exportInternal(const char *key, const svn_fs_path_change_t *cha
}
int SvnRevision::recurse(const char *path, const svn_fs_path_change_t *change,
- const char *path_from, svn_revnum_t rev_from,
+ const char *path_from, const MatchRuleList &matchRules, svn_revnum_t rev_from,
apr_hash_t *changes, apr_pool_t *pool)
{
svn_fs_root_t *fs_root = this->fs_root;
@@ -803,13 +815,13 @@ int SvnRevision::recurse(const char *path, const svn_fs_path_change_t *change,
MatchRuleList::ConstIterator match = findMatchRule(matchRules, revnum, current);
if (match != matchRules.constEnd()) {
if (exportDispatch(entry, change, entryFrom.isNull() ? 0 : entryFrom.constData(),
- rev_from, changes, current, *match, dirpool) == EXIT_FAILURE)
+ rev_from, changes, current, *match, matchRules, dirpool) == EXIT_FAILURE)
return EXIT_FAILURE;
} else {
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)
+ matchRules, rev_from, changes, dirpool) == EXIT_FAILURE)
return EXIT_FAILURE;
}
}
diff --git a/src/svn.h b/src/svn.h
index 10fd22c..5fb3245 100644
--- a/src/svn.h
+++ b/src/svn.h
@@ -33,7 +33,7 @@ public:
Svn(const QString &pathToRepository);
~Svn();
- void setMatchRules(const QList<Rules::Match> &matchRules);
+ void setMatchRules(const QList<QList<Rules::Match> > &matchRules);
void setRepositories(const QHash<QString, Repository *> &repositories);
void setIdentityMap(const QHash<QByteArray, QByteArray> &identityMap);