svn commit: r243982 - in user/uqs/git_conv/svn2git: . src
Ulrich Spoerlein
uqs at FreeBSD.org
Fri Dec 7 13:00:42 UTC 2012
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 <tzander at trolltech.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "CommandLineParser.h"
+
+#include <QDebug>
+#include <QTextStream>
+#include <QStringList>
+#include <QList>
+#include <QHash>
+
+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<QString> parameters;
+ };
+
+ QList<OptionDefinition> definitions;
+ QHash<QString, ParsedOption> options;
+ QList<QString> arguments;
+ QList<QString> undefinedOptions;
+ QList<QString> 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 <<endl;
+ }
+}
+
+QStringList CommandLineParser::options() const
+{
+ d->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 <tzander at trolltech.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef COMMANDLINEPARSER_H
+#define COMMANDLINEPARSER_H
+
+#include <QString>
+
+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;
+ * <ol>
+ * <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 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 <thiago at kde.org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <QCoreApplication>
+#include <QFile>
+#include <QStringList>
+#include <QTextStream>
+#include <QDebug>
+
+#include <limits.h>
+#include <stdio.h>
+
+#include "CommandLineParser.h"
+#include "ruleparser.h"
+#include "repository.h"
+#include "svn.h"
+
+QHash<QByteArray, QByteArray> loadIdentityMapFile(const QString &fileName)
+{
+ QHash<QByteArray, QByteArray> 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 <user at example.com>
+ // - git-svn: loginname = Joe User <user at example.com>
+ 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<int> loadRevisionsFile( const QString &fileName, Svn &svn )
+{
+ QRegExp revint("(\\d+)\\s*(?:-\\s*(\\d+|HEAD))?");
+ QSet<int> 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<QString, Repository *> 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<int> 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 <thiago at kde.org>
+ * Copyright (C) 2009 Thomas Zander <zander at kde.org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "repository.h"
+#include "CommandLineParser.h"
+#include <QTextStream>
+#include <QDebug>
+#include <QDir>
+#include <QFile>
+#include <QLinkedList>
+
+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<Repository *>
+{
+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 ***
More information about the svn-src-user
mailing list