diff options
-rw-r--r-- | src/CommandLineParser.cpp | 3 | ||||
-rw-r--r-- | src/CommandLineParser.h | 4 | ||||
-rw-r--r-- | src/main.cpp | 10 | ||||
-rw-r--r-- | src/repository.cpp | 2 | ||||
-rw-r--r-- | src/ruleparser.cpp | 47 | ||||
-rw-r--r-- | src/ruleparser.h | 48 | ||||
-rw-r--r-- | src/svn.cpp | 78 | ||||
-rw-r--r-- | src/svn.h | 2 |
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 ¤t, 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 ¤t, const Rules::Match &rule); + const QString ¤t, 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 ¤t, - 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 ¤t, const Rules::Match &rule) + const QString ¤t, 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; } } @@ -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); |