From owner-svn-src-user@FreeBSD.ORG Fri Dec 7 13:00:42 2012 Return-Path: Delivered-To: svn-src-user@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 6AE3B495; Fri, 7 Dec 2012 13:00:42 +0000 (UTC) (envelope-from uqs@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id 4E8168FC12; Fri, 7 Dec 2012 13:00:42 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.5/8.14.5) with ESMTP id qB7D0gpQ098845; Fri, 7 Dec 2012 13:00:42 GMT (envelope-from uqs@svn.freebsd.org) Received: (from uqs@localhost) by svn.freebsd.org (8.14.5/8.14.5/Submit) id qB7D0f3I098809; Fri, 7 Dec 2012 13:00:41 GMT (envelope-from uqs@svn.freebsd.org) Message-Id: <201212071300.qB7D0f3I098809@svn.freebsd.org> From: Ulrich Spoerlein Date: Fri, 7 Dec 2012 13:00:41 +0000 (UTC) To: src-committers@freebsd.org, svn-src-user@freebsd.org Subject: svn commit: r243982 - in user/uqs/git_conv/svn2git: . src X-SVN-Group: user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-user@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the experimental " user" src tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 07 Dec 2012 13:00:42 -0000 Author: uqs Date: Fri Dec 7 13:00:41 2012 New Revision: 243982 URL: http://svnweb.freebsd.org/changeset/base/243982 Log: Add a copy of the svn2git converter from https://gitorious.org/svn2git/svn2git Added: user/uqs/git_conv/svn2git/ user/uqs/git_conv/svn2git/fast-export2.pro user/uqs/git_conv/svn2git/src/ user/uqs/git_conv/svn2git/src/CommandLineParser.cpp (contents, props changed) user/uqs/git_conv/svn2git/src/CommandLineParser.h (contents, props changed) user/uqs/git_conv/svn2git/src/main.cpp (contents, props changed) user/uqs/git_conv/svn2git/src/repository.cpp (contents, props changed) user/uqs/git_conv/svn2git/src/repository.h (contents, props changed) user/uqs/git_conv/svn2git/src/ruleparser.cpp (contents, props changed) user/uqs/git_conv/svn2git/src/ruleparser.h (contents, props changed) user/uqs/git_conv/svn2git/src/src.pro user/uqs/git_conv/svn2git/src/svn.cpp (contents, props changed) user/uqs/git_conv/svn2git/src/svn.h (contents, props changed) Added: user/uqs/git_conv/svn2git/fast-export2.pro ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/uqs/git_conv/svn2git/fast-export2.pro Fri Dec 7 13:00:41 2012 (r243982) @@ -0,0 +1,8 @@ +###################################################################### +# Automatically generated by qmake (2.01a) dim. dec. 23 13:48:52 2007 +###################################################################### + +TEMPLATE = subdirs + +# Directories +SUBDIRS = src Added: user/uqs/git_conv/svn2git/src/CommandLineParser.cpp ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/uqs/git_conv/svn2git/src/CommandLineParser.cpp Fri Dec 7 13:00:41 2012 (r243982) @@ -0,0 +1,403 @@ +/* + * This file is part of the vng project + * Copyright (C) 2008 Thomas Zander + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "CommandLineParser.h" + +#include +#include +#include +#include +#include + +CommandLineParser *CommandLineParser::self = 0; + +class CommandLineParser::Private +{ +public: + Private(int argc, char **argv); + + // functions + void addDefinitions(const CommandLineOption * options); + void setArgumentDefinition(const char *definition); + void parse(); + + // variables; + const int argumentCount; + char ** const argumentStrings; + bool dirty; + int requiredArguments; + QString argumentDefinition; + + struct OptionDefinition { + OptionDefinition() : optionalParameters(0), requiredParameters(0) { } + QString name; + QString comment; + QChar shortName; + int optionalParameters; + int requiredParameters; + }; + + // result of what the user typed + struct ParsedOption { + QString option; + QList parameters; + }; + + QList definitions; + QHash options; + QList arguments; + QList undefinedOptions; + QList errors; +}; + + +CommandLineParser::Private::Private(int argc, char **argv) + : argumentCount(argc), argumentStrings(argv), dirty(true), + requiredArguments(0) +{ +} + +void CommandLineParser::Private::addDefinitions(const CommandLineOption * options) +{ + for (int i=0; options[i].specification != 0; i++) { + OptionDefinition definition; + QString option = QString::fromLatin1(options[i].specification); + // 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() << "WARN: option definition '" << option << "' is faulty; only one ',' allowed"; + continue; + } + foreach (QString s, optionParts) { + s = s.trimmed(); + if (s.startsWith(QLatin1String("--")) && s.length() > 2) + definition.name = s.mid(2); + else if (s.startsWith(QLatin1String("-")) && s.length() > 1) + definition.shortName = s.at(1); + else { + qWarning() << "WARN: option definition '" << option << "' is faulty; the option should start with a -"; + break; + } + } + } + else if (option.startsWith(QLatin1String("--")) && option.length() > 2) + definition.name = option.mid(2); + else + qWarning() << "WARN: option definition '" << option << "' has unrecognized format. See the api docs for CommandLineParser for a howto"; + + if(definition.name.isEmpty()) + continue; + if (option.indexOf(QLatin1Char(' ')) > 0) { + QStringList optionParts = definition.name.split(QLatin1Char(' '), QString::SkipEmptyParts); + definition.name = optionParts[0]; + bool first = true; + foreach (QString s, optionParts) { + if (first) { + first = false; + continue; + } + s = s.trimmed(); + if (s[0].unicode() == '[' && s.endsWith(QLatin1Char(']'))) + definition.optionalParameters++; + else + definition.requiredParameters++; + } + } + + definition.comment = QString::fromLatin1(options[i].description); + definitions << definition; + } +/* + foreach (OptionDefinition def, definitions) { + qDebug() << "definition:" << (def.shortName != 0 ? def.shortName : QChar(32)) << "|" << def.name << "|" << def.comment + << "|" << def.requiredParameters << "+" << def.optionalParameters; + } +*/ + + dirty = true; +} + +void CommandLineParser::Private::setArgumentDefinition(const char *defs) +{ + requiredArguments = 0; + argumentDefinition = QString::fromLatin1(defs); + QStringList optionParts = argumentDefinition.split(QLatin1Char(' '), QString::SkipEmptyParts); + bool inArg = false; + foreach (QString s, optionParts) { + s = s.trimmed(); + if (s[0].unicode() == '<') { + inArg = true; + requiredArguments++; + } + else if (s[0].unicode() == '[') + inArg = true; + if (s.endsWith(QLatin1Char('>'))) + inArg = false; + else if (!inArg) + requiredArguments++; + } +} + +void CommandLineParser::Private::parse() +{ + if (dirty == false) + return; + errors.clear(); + options.clear(); + arguments.clear(); + undefinedOptions.clear(); + dirty = false; + + class OptionProcessor { + public: + OptionProcessor(Private *d) : clp(d) { } + + void next(Private::ParsedOption &option) { + if (! option.option.isEmpty()) { + // find the definition to match. + OptionDefinition def; + foreach (Private::OptionDefinition definition, clp->definitions) { + if (definition.name == option.option) { + def = definition; + break; + } + } + if (! def.name.isEmpty() && def.requiredParameters >= option.parameters.count() && + def.requiredParameters + def.optionalParameters <= option.parameters.count()) + clp->options.insert(option.option, option); + else if (!clp->undefinedOptions.contains(option.option)) + clp->undefinedOptions << option.option; + else + clp->errors.append(QLatin1String("Not enough arguments passed for option `") + + option.option +QLatin1Char('\'')); + } + option.option.clear(); + option.parameters.clear(); + } + + private: + CommandLineParser::Private *clp; + }; + OptionProcessor processor(this); + + bool optionsAllowed = true; + ParsedOption option; + OptionDefinition currentDefinition; + for (int i = 1; i < argumentCount; i++) { + QString arg = QString::fromLocal8Bit(argumentStrings[i]); + if (optionsAllowed) { + if (arg == QLatin1String("--")) { + optionsAllowed = false; + continue; + } + if (arg.startsWith(QLatin1String("--"))) { + processor.next(option); + int end = arg.indexOf(QLatin1Char('=')); + option.option = arg.mid(2, end - 2); + if (end > 0) + option.parameters << arg.mid(end+1); + continue; + } + if (arg[0].unicode() == '-' && arg.length() > 1) { + for (int x = 1; x < arg.length(); x++) { + bool resolved = false; + foreach (OptionDefinition definition, definitions) { + if (definition.shortName == arg[x]) { + resolved = true; + processor.next(option); + currentDefinition = definition; + option.option = definition.name; + + if (definition.requiredParameters == 1 && arg.length() >= x+2) { + option.parameters << arg.mid(x+1, arg.length()); + x = arg.length(); + } + break; + } + } + if (!resolved) { // nothing found; copy char so it ends up in unrecognized + option.option = arg[x]; + processor.next(option); + } + } + continue; + } + } + if (! option.option.isEmpty()) { + if (currentDefinition.name != option.option) { + // find the definition to match. + foreach (OptionDefinition definition, definitions) { + if (definition.name == option.option) { + currentDefinition = definition; + break; + } + } + } + if (currentDefinition.requiredParameters + currentDefinition.optionalParameters <= option.parameters.count()) + processor.next(option); + } + if (option.option.isEmpty()) + arguments << arg; + else + option.parameters << arg; + } + processor.next(option); + + if (requiredArguments > arguments.count()) + errors.append(QLatin1String("Not enough arguments, usage: ") + QString::fromLocal8Bit(argumentStrings[0]) + + QLatin1Char(' ') + argumentDefinition); + +/* + foreach (QString key, options.keys()) { + ParsedOption p = options[key]; + qDebug() << "-> " << p.option; + foreach (QString v, p.parameters) + qDebug() << " +" << v; + } + qDebug() << "---"; + foreach (QString arg, arguments) { + qDebug() << arg; + } +*/ +} + +// ----------------------------------- + + +// static +void CommandLineParser::init(int argc, char **argv) +{ + if (self) + delete self; + self = new CommandLineParser(argc, argv); +} + +// static +void CommandLineParser::addOptionDefinitions(const CommandLineOption * optionList) +{ + if (!self) { + qWarning() << "WARN: CommandLineParser:: Use init before addOptionDefinitions!"; + return; + } + self->d->addDefinitions(optionList); +} + +// static +CommandLineParser *CommandLineParser::instance() +{ + return self; +} + +// static +void CommandLineParser::setArgumentDefinition(const char *definition) +{ + if (!self) { + qWarning() << "WARN: CommandLineParser:: Use init before addOptionDefinitions!"; + return; + } + self->d->setArgumentDefinition(definition); +} + + +CommandLineParser::CommandLineParser(int argc, char **argv) + : d(new Private(argc, argv)) +{ +} + +CommandLineParser::~CommandLineParser() +{ + delete d; +} + +void CommandLineParser::usage(const QString &name, const QString &argumentDescription) +{ + QTextStream cout(stdout, QIODevice::WriteOnly); + cout << "Usage: " << d->argumentStrings[0]; + if (! name.isEmpty()) + cout << " " << name; + if (d->definitions.count()) + cout << " [OPTION]"; + if (! argumentDescription.isEmpty()) + cout << " " << argumentDescription; + cout << endl << endl; + + if (d->definitions.count() > 0) + cout << "Options:" << endl; + int commandLength = 0; + foreach (Private::OptionDefinition definition, d->definitions) + commandLength = qMax(definition.name.length(), commandLength); + + foreach (Private::OptionDefinition definition, d->definitions) { + cout << " "; + if (definition.shortName == 0) + cout << " --"; + else + cout << "-" << definition.shortName << " --"; + cout << definition.name; + for (int i = definition.name.length(); i <= commandLength; i++) + cout << ' '; + cout << definition.comment <parse(); + return d->options.keys(); +} + +bool CommandLineParser::contains(const QString & key) const +{ + d->parse(); + return d->options.contains(key); +} + +QStringList CommandLineParser::arguments() const +{ + d->parse(); + return d->arguments; +} + +QStringList CommandLineParser::undefinedOptions() const +{ + d->parse(); + return d->undefinedOptions; +} + +QString CommandLineParser::optionArgument(const QString &optionName, const QString &defaultValue) const +{ + QStringList answer = optionArguments(optionName); + if (answer.isEmpty()) + return defaultValue; + return answer.first(); +} + +QStringList CommandLineParser::optionArguments(const QString &optionName) const +{ + if (! contains(optionName)) + return QStringList(); + Private::ParsedOption po = d->options[optionName]; + return po.parameters; +} + +QStringList CommandLineParser::parseErrors() const +{ + d->parse(); + return d->errors; +} Added: user/uqs/git_conv/svn2git/src/CommandLineParser.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/uqs/git_conv/svn2git/src/CommandLineParser.h Fri Dec 7 13:00:41 2012 (r243982) @@ -0,0 +1,100 @@ +/* + * This file is part of the vng project + * Copyright (C) 2008 Thomas Zander + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef COMMANDLINEPARSER_H +#define COMMANDLINEPARSER_H + +#include + +struct CommandLineOption { + /** + * The specification of an option includes the name of the option the user must pass and optional arguments it has. + * Example specifications are; + *
    + *
  1. "-a, --all"
  2. + *
  3. "--version"
  4. + *
  5. "--type name"
  6. + *
  7. "--list item[,item]"
  8. + *
  9. "-f, --format name [suffix] [foo]"
+ * 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 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; + /** + * A textual description of the option that will be printed when the user asks for help. + */ + const char *description; +}; + +#define CommandLineLastOption { 0, 0 } + +/** + * The CommandLineParser singleton + */ +class CommandLineParser +{ +public: + static void init(int argc, char *argv[]); + static void addOptionDefinitions(const CommandLineOption *definitions); + static void setArgumentDefinition(const char *definition); + static CommandLineParser *instance(); + + ~CommandLineParser(); + + void usage(const QString &name, const QString &argumentDescription = QString()); + + /// return the options that the user passed + QStringList options() const; + /** + * returns true if the option was found. + * Consider the following definition "--expert level" The user can type as an argument + * "--expert 10". Calling contains("expert") will return true. + * @see optionArgument() + */ + bool contains(const QString & key) const; + + /// returns the list of items that are not options, note that the first one is the name of the command called + QStringList arguments() const; + + /// return the list of options that the user passed but we don't have a definition for. + QStringList undefinedOptions() const; + + /** + * Return the argument passed to an option. + * Consider the following definition "--expert level" The user can type as an argument + * "--expert 10". Calling optionArgument("expert") will return a string "10" + * @see contains() + */ + QString optionArgument(const QString &optionName, const QString &defaultValue = QString()) const; + QStringList optionArguments(const QString &optionName) const; + + QStringList parseErrors() const; + +private: + CommandLineParser(int argc, char **argv); + class Private; + Private * const d; + static CommandLineParser *self; +}; + +#endif + Added: user/uqs/git_conv/svn2git/src/main.cpp ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/uqs/git_conv/svn2git/src/main.cpp Fri Dec 7 13:00:41 2012 (r243982) @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2007 Thiago Macieira + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "CommandLineParser.h" +#include "ruleparser.h" +#include "repository.h" +#include "svn.h" + +QHash loadIdentityMapFile(const QString &fileName) +{ + QHash result; + if (fileName.isEmpty()) + return result; + + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) { + fprintf(stderr, "Could not open file %s: %s", + qPrintable(fileName), qPrintable(file.errorString())); + return result; + } + + while (!file.atEnd()) { + QByteArray line = file.readLine(); + int comment_pos = line.indexOf('#'); + if (comment_pos != -1) + line.truncate(comment_pos); + line = line.trimmed(); + int space = line.indexOf(' '); + if (space == -1) + continue; // invalid line + + // Support git-svn author files, too + // - svn2git native: loginname Joe User + // - git-svn: loginname = Joe User + int rightspace = line.indexOf(" = "); + int leftspace = space; + if (rightspace == -1) { + rightspace = space; + } else { + leftspace = rightspace; + rightspace += 2; + } + + QByteArray realname = line.mid(rightspace).trimmed(); + line.truncate(leftspace); + + result.insert(line, realname); + }; + file.close(); + + return result; +} + +QSet loadRevisionsFile( const QString &fileName, Svn &svn ) +{ + QRegExp revint("(\\d+)\\s*(?:-\\s*(\\d+|HEAD))?"); + QSet revisions; + if(fileName.isEmpty()) + return revisions; + + QFile file(fileName); + if( !file.open(QIODevice::ReadOnly)) { + fprintf(stderr, "Could not open file %s: %s\n", qPrintable(fileName), qPrintable(file.errorString())); + return revisions; + } + + bool ok; + while(!file.atEnd()) { + QByteArray line = file.readLine().trimmed(); + revint.indexIn(line); + if( revint.cap(2).isEmpty() ) { + int rev = revint.cap(1).toInt(&ok); + if(ok) { + revisions.insert(rev); + } else { + fprintf(stderr, "Unable to convert %s to int, skipping revision.\n", qPrintable(QString(line))); + } + } else if( revint.captureCount() == 2 ) { + int rev = revint.cap(1).toInt(&ok); + if(!ok) { + fprintf(stderr, "Unable to convert %s (%s) to int, skipping revisions.\n", qPrintable(revint.cap(1)), qPrintable(QString(line))); + continue; + } + int lastrev = 0; + if(revint.cap(2) == "HEAD") { + lastrev = svn.youngestRevision(); + ok = true; + } else { + lastrev = revint.cap(2).toInt(&ok); + } + if(!ok) { + fprintf(stderr, "Unable to convert %s (%s) to int, skipping revisions.\n", qPrintable(revint.cap(2)), qPrintable(QString(line))); + continue; + } + for(; rev <= lastrev; ++rev ) + revisions.insert(rev); + } else { + fprintf(stderr, "Unable to convert %s to int, skipping revision.\n", qPrintable(QString(line))); + } + } + file.close(); + return revisions; +} + +static const CommandLineOption options[] = { + {"--identity-map FILENAME", "provide map between svn username and email"}, + {"--identity-domain DOMAIN", "provide user domain if no map was given"}, + {"--revisions-file FILENAME", "provide a file with revision number that should be processed"}, + {"--rules FILENAME[,FILENAME]", "the rules file(s) that determines what goes where"}, + {"--add-metadata", "if passed, each git commit will have svn commit info"}, + {"--add-metadata-notes", "if passed, each git commit will have notes with svn commit info"}, + {"--resume-from revision", "start importing at svn revision number"}, + {"--max-rev revision", "stop importing at svn revision number"}, + {"--dry-run", "don't actually write anything"}, + {"--debug-rules", "print what rule is being used for each file"}, + {"--commit-interval NUMBER", "if passed the cache will be flushed to git every NUMBER of commits"}, + {"--stats", "after a run print some statistics about the rules"}, + {"--svn-branches", "Use the contents of SVN when creating branches, Note: SVN tags are branches as well"}, + {"-h, --help", "show help"}, + {"-v, --version", "show version"}, + CommandLineLastOption +}; + +int main(int argc, char **argv) +{ + printf("Invoked as:'"); + for(int i = 0; i < argc; ++i) + printf(" %s", argv[i]); + printf("'\n"); + CommandLineParser::init(argc, argv); + CommandLineParser::addOptionDefinitions(options); + Stats::init(); + CommandLineParser *args = CommandLineParser::instance(); + if(args->contains(QLatin1String("version"))) { + printf("Git version: %s\n", VER); + return 0; + } + if (args->contains(QLatin1String("help")) || args->arguments().count() != 1) { + args->usage(QString(), "[Path to subversion repo]"); + return 0; + } + if (args->undefinedOptions().count()) { + QTextStream out(stderr); + out << "svn-all-fast-export failed: "; + bool first = true; + foreach (QString option, args->undefinedOptions()) { + if (!first) + out << " : "; + out << "unrecognized option or missing argument for; `" << option << "'" << endl; + first = false; + } + return 10; + } + if (!args->contains("rules")) { + QTextStream out(stderr); + out << "svn-all-fast-export failed: please specify the rules using the 'rules' argument\n"; + return 11; + } + if (!args->contains("identity-map") && !args->contains("identity-domain")) { + QTextStream out(stderr); + out << "WARNING; no identity-map or -domain specified, all commits will use default @localhost email address\n\n"; + } + + QCoreApplication app(argc, argv); + // Load the configuration + 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(); + + // create the repository list + QHash repositories; + + int cutoff = resume_from ? resume_from : INT_MAX; + retry: + int min_rev = 1; + foreach (Rules::Repository rule, rulesList.allRepositories()) { + Repository *repo = new Repository(rule); + if (!repo) + return EXIT_FAILURE; + repositories.insert(rule.name, repo); + + int repo_next = repo->setupIncremental(cutoff); + + /* + * cutoff < resume_from => error exit eventually + * repo_next == cutoff => probably truncated log + */ + if (cutoff < resume_from && repo_next == cutoff) + /* + * Restore the log file so we fail the next time + * svn2git is invoked with the same arguments + */ + repo->restoreLog(); + + if (cutoff < min_rev) + /* + * We've rewound before the last revision of some + * repository that we've already seen. Start over + * from the beginning. (since cutoff is decreasing, + * we're sure we'll make forward progress eventually) + */ + goto retry; + + if (min_rev < repo_next) + min_rev = repo_next; + } + + if (cutoff < resume_from) { + qCritical() << "Cannot resume from" << resume_from + << "as there are errors in revision" << cutoff; + return EXIT_FAILURE; + } + + if (min_rev < resume_from) + qDebug() << "skipping revisions" << min_rev << "to" << resume_from - 1 << "as requested"; + + if (resume_from) + min_rev = resume_from; + + Svn::initialize(); + Svn svn(args->arguments().first()); + svn.setMatchRules(rulesList.allMatchRules()); + svn.setRepositories(repositories); + svn.setIdentityMap(loadIdentityMapFile(args->optionArgument("identity-map"))); + // Massage user input a little, no guarantees that input makes sense. + QString domain = args->optionArgument("identity-domain").simplified().remove(QChar('@')); + if (domain.isEmpty()) + domain = QString("localhost"); + svn.setIdentityDomain(domain); + + if (max_rev < 1) + max_rev = svn.youngestRevision(); + + bool errors = false; + QSet revisions = loadRevisionsFile(args->optionArgument(QLatin1String("revisions-file")), svn); + const bool filerRevisions = !revisions.isEmpty(); + for (int i = min_rev; i <= max_rev; ++i) { + if(filerRevisions) { + if( !revisions.contains(i) ) { + printf("."); + continue; + } else { + printf("\n"); + } + } + if (!svn.exportRevision(i)) { + errors = true; + break; + } + } + + foreach (Repository *repo, repositories) { + repo->finalizeTags(); + delete repo; + } + Stats::instance()->printStats(); + return errors ? EXIT_FAILURE : EXIT_SUCCESS; +} Added: user/uqs/git_conv/svn2git/src/repository.cpp ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/uqs/git_conv/svn2git/src/repository.cpp Fri Dec 7 13:00:41 2012 (r243982) @@ -0,0 +1,802 @@ +/* + * Copyright (C) 2007 Thiago Macieira + * Copyright (C) 2009 Thomas Zander + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "repository.h" +#include "CommandLineParser.h" +#include +#include +#include +#include +#include + +static const int maxSimultaneousProcesses = 100; + +static const int maxMark = (1 << 20) - 2; // some versions of git-fast-import are buggy for larger values of maxMark + +class ProcessCache: QLinkedList +{ +public: + void touch(Repository *repo) + { + remove(repo); + + // if the cache is too big, remove from the front + while (size() >= maxSimultaneousProcesses) + takeFirst()->closeFastImport(); + + // append to the end + append(repo); + } + + inline void remove(Repository *repo) + { +#if QT_VERSION >= 0x040400 + removeOne(repo); +#else + removeAll(repo); +#endif + } +}; +static ProcessCache processCache; + +static QString marksFileName(QString name) +{ + name.replace('/', '_'); + name.prepend("marks-"); + return name; +} + +Repository::Repository(const Rules::Repository &rule) + : name(rule.name), prefix(rule.forwardTo), fastImport(name), commitCount(0), outstandingTransactions(0), + last_commit_mark(0), next_file_mark(maxMark), processHasStarted(false) +{ + foreach (Rules::Repository::Branch branchRule, rule.branches) { + Branch branch; + branch.created = 1; + + branches.insert(branchRule.name, branch); + } + + // create the default branch + branches["master"].created = 1; + + fastImport.setWorkingDirectory(name); + if (!CommandLineParser::instance()->contains("dry-run")) { + if (!QDir(name).exists()) { // repo doesn't exist yet. + qDebug() << "Creating new repository" << name; + QDir::current().mkpath(name); + QProcess init; + init.setWorkingDirectory(name); + init.start("git", QStringList() << "--bare" << "init"); + init.waitForFinished(-1); + // Write description + if (!rule.description.isEmpty()) { + QFile fDesc(QDir(name).filePath("description")); + if (fDesc.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { + fDesc.write(rule.description.toUtf8()); + fDesc.putChar('\n'); + fDesc.close(); + } + } + { + QFile marks(name + "/" + marksFileName(name)); + marks.open(QIODevice::WriteOnly); + marks.close(); + } + } + } +} + +static QString logFileName(QString name) +{ + name.replace('/', '_'); + name.prepend("log-"); + return name; +} + +static int lastValidMark(QString name) +{ + QFile marksfile(name + "/" + marksFileName(name)); + if (!marksfile.open(QIODevice::ReadOnly)) + return 0; + + int prev_mark = 0; + + int lineno = 0; + while (!marksfile.atEnd()) { + QString line = marksfile.readLine(); + ++lineno; + if (line.isEmpty()) + continue; + + int mark = 0; + if (line[0] == ':') { + int sp = line.indexOf(' '); + if (sp != -1) { + QString m = line.mid(1, sp-1); + mark = m.toInt(); + } + } + + if (!mark) { + qCritical() << marksfile.fileName() << "line" << lineno << "marks file corrupt?"; + return 0; + } + + if (mark == prev_mark) { + qCritical() << marksfile.fileName() << "line" << lineno << "marks file has duplicates"; + return 0; + } + + if (mark < prev_mark) { + qCritical() << marksfile.fileName() << "line" << lineno << "marks file not sorted"; + return 0; + } + + if (mark > prev_mark + 1) + break; + + prev_mark = mark; + } + + return prev_mark; +} + +int Repository::setupIncremental(int &cutoff) +{ + QFile logfile(logFileName(name)); + if (!logfile.exists()) + return 1; + + logfile.open(QIODevice::ReadWrite); + + QRegExp progress("progress SVN r(\\d+) branch (.*) = :(\\d+)"); + + int last_valid_mark = lastValidMark(name); + + int last_revnum = 0; + qint64 pos = 0; + int retval = 0; + QString bkup = logfile.fileName() + ".old"; + + while (!logfile.atEnd()) { + pos = logfile.pos(); + QByteArray line = logfile.readLine(); + int hash = line.indexOf('#'); + if (hash != -1) + line.truncate(hash); + line = line.trimmed(); + if (line.isEmpty()) + continue; + if (!progress.exactMatch(line)) + continue; + + int revnum = progress.cap(1).toInt(); + QString branch = progress.cap(2); + int mark = progress.cap(3).toInt(); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***