diff options
Diffstat (limited to 'src/repository.cpp')
| -rw-r--r-- | src/repository.cpp | 621 |
1 files changed, 505 insertions, 116 deletions
diff --git a/src/repository.cpp b/src/repository.cpp index be73df6..5bcd8dd 100644 --- a/src/repository.cpp +++ b/src/repository.cpp @@ -25,10 +25,161 @@ static const int maxSimultaneousProcesses = 100; -class ProcessCache: QLinkedList<Repository *> +static const int maxMark = (1 << 20) - 1; // some versions of git-fast-import are buggy for larger values of maxMark + +class FastImportRepository : public Repository +{ +public: + class Transaction : public Repository::Transaction + { + Q_DISABLE_COPY(Transaction) + friend class FastImportRepository; + + FastImportRepository *repository; + QByteArray branch; + QByteArray svnprefix; + QByteArray author; + QByteArray log; + uint datetime; + int revnum; + + QVector<int> merges; + + QStringList deletedFiles; + QByteArray modifiedFiles; + + inline Transaction() {} + public: + ~Transaction(); + void commit(); + + void setAuthor(const QByteArray &author); + void setDateTime(uint dt); + void setLog(const QByteArray &log); + + void noteCopyFromBranch (const QString &prevbranch, int revFrom); + + void deleteFile(const QString &path); + QIODevice *addFile(const QString &path, int mode, qint64 length); + }; + FastImportRepository(const Rules::Repository &rule); + int setupIncremental(int &cutoff); + void restoreLog(); + ~FastImportRepository(); + + void reloadBranches(); + int createBranch(const QString &branch, int revnum, + const QString &branchFrom, int revFrom); + int deleteBranch(const QString &branch, int revnum); + Repository::Transaction *newTransaction(const QString &branch, const QString &svnprefix, int revnum); + + void createAnnotatedTag(const QString &name, const QString &svnprefix, int revnum, + const QByteArray &author, uint dt, + const QByteArray &log); + void finalizeTags(); + +private: + struct Branch + { + int created; + QVector<int> commits; + QVector<int> marks; + }; + struct AnnotatedTag + { + QString supportingRef; + QByteArray svnprefix; + QByteArray author; + QByteArray log; + uint dt; + int revnum; + }; + + QHash<QString, Branch> branches; + QHash<QString, AnnotatedTag> annotatedTags; + QString name; + QProcess fastImport; + int commitCount; + int outstandingTransactions; + + /* starts at 0, and counts up. */ + int last_commit_mark; + + /* starts at maxMark and counts down. Reset after each SVN revision */ + int next_file_mark; + + bool processHasStarted; + + void startFastImport(); + void closeFastImport(); + + // called when a transaction is deleted + void forgetTransaction(Transaction *t); + + int resetBranch(const QString &branch, int revnum, int mark, const QByteArray &resetTo, const QByteArray &comment); + int markFrom(const QString &branchFrom, int branchRevNum, QByteArray &desc); + + friend class ProcessCache; + Q_DISABLE_COPY(FastImportRepository) +}; + +class PrefixingRepository : public Repository +{ + Repository *repo; + QString prefix; +public: + class Transaction : public Repository::Transaction + { + Q_DISABLE_COPY(Transaction) + + Repository::Transaction *txn; + QString prefix; + public: + Transaction(Repository::Transaction *t, const QString &p) : txn(t), prefix(p) {} + ~Transaction() { delete txn; } + void commit() { txn->commit(); } + + void setAuthor(const QByteArray &author) { txn->setAuthor(author); } + void setDateTime(uint dt) { txn->setDateTime(dt); } + void setLog(const QByteArray &log) { txn->setLog(log); } + + void noteCopyFromBranch (const QString &prevbranch, int revFrom) + { txn->noteCopyFromBranch(prevbranch, revFrom); } + + void deleteFile(const QString &path) { txn->deleteFile(prefix + path); } + QIODevice *addFile(const QString &path, int mode, qint64 length) + { return txn->addFile(prefix + path, mode, length); } + }; + + PrefixingRepository(Repository *r, const QString &p) : repo(r), prefix(p) {} + + int setupIncremental(int &) { return 1; } + void restoreLog() {} + + int createBranch(const QString &branch, int revnum, + const QString &branchFrom, int revFrom) + { return repo->createBranch(branch, revnum, branchFrom, revFrom); } + + int deleteBranch(const QString &branch, int revnum) + { return repo->deleteBranch(branch, revnum); } + + Repository::Transaction *newTransaction(const QString &branch, const QString &svnprefix, int revnum) + { + Repository::Transaction *t = repo->newTransaction(branch, svnprefix, revnum); + return new Transaction(t, prefix); + } + + void createAnnotatedTag(const QString &name, const QString &svnprefix, int revnum, + const QByteArray &author, uint dt, + const QByteArray &log) + { repo->createAnnotatedTag(name, svnprefix, revnum, author, dt, log); } + void finalizeTags() { /* loop that called this will invoke it on 'repo' too */ } +}; + +class ProcessCache: QLinkedList<FastImportRepository *> { public: - void touch(Repository *repo) + void touch(FastImportRepository *repo) { remove(repo); @@ -40,7 +191,7 @@ public: append(repo); } - inline void remove(Repository *repo) + inline void remove(FastImportRepository *repo) { #if QT_VERSION >= 0x040400 removeOne(repo); @@ -51,8 +202,27 @@ public: }; static ProcessCache processCache; -Repository::Repository(const Rules::Repository &rule) - : name(rule.name), commitCount(0), outstandingTransactions(0), lastmark(0), processHasStarted(false) +Repository *makeRepository(const Rules::Repository &rule, const QHash<QString, Repository *> &repositories) +{ + if (rule.forwardTo.isEmpty()) + return new FastImportRepository(rule); + Repository *r = repositories[rule.forwardTo]; + if (!r) { + qCritical() << "no repository with name" << rule.forwardTo << "found at line" << rule.lineNumber; + return r; + } + return new PrefixingRepository(r, rule.prefix); +} + +static QString marksFileName(QString name) +{ + name.replace('/', '_'); + name.prepend("marks-"); + return name; +} + +FastImportRepository::FastImportRepository(const Rules::Repository &rule) + : name(rule.name), commitCount(0), outstandingTransactions(0), last_commit_mark(0), next_file_mark(maxMark), processHasStarted(false) { foreach (Rules::Repository::Branch branchRule, rule.branches) { Branch branch; @@ -73,17 +243,167 @@ Repository::Repository(const Rules::Repository &rule) init.setWorkingDirectory(name); init.start("git", QStringList() << "--bare" << "init"); init.waitForFinished(-1); + { + QFile marks(name + "/" + marksFileName(name)); + marks.open(QIODevice::WriteOnly); + marks.close(); + } } } } -Repository::~Repository() +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 FastImportRepository::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(); + + if (revnum >= cutoff) + goto beyond_cutoff; + + if (revnum < last_revnum) + qWarning() << name << "revision numbers are not monotonic: " + << "got" << QString::number(last_revnum) + << "and then" << QString::number(revnum); + + if (mark > last_valid_mark) { + qWarning() << name << "unknown commit mark found: rewinding -- did you hit Ctrl-C?"; + cutoff = revnum; + goto beyond_cutoff; + } + + last_revnum = revnum; + + if (last_commit_mark < mark) + last_commit_mark = mark; + + Branch &br = branches[branch]; + if (!br.created || !mark || !br.marks.last()) + br.created = revnum; + br.commits.append(revnum); + br.marks.append(mark); + } + + retval = last_revnum + 1; + if (retval == cutoff) + /* + * If a stale backup file exists already, remove it, so that + * we don't confuse ourselves in 'restoreLog()' + */ + QFile::remove(bkup); + + return retval; + + beyond_cutoff: + // backup file, since we'll truncate + QFile::remove(bkup); + logfile.copy(bkup); + + // truncate, so that we ignore the rest of the revisions + qDebug() << name << "truncating history to revision" << cutoff; + logfile.resize(pos); + return cutoff; +} + +void FastImportRepository::restoreLog() +{ + QString file = logFileName(name); + QString bkup = file + ".old"; + if (!QFile::exists(bkup)) + return; + QFile::remove(file); + QFile::rename(bkup, file); +} + +FastImportRepository::~FastImportRepository() { Q_ASSERT(outstandingTransactions == 0); closeFastImport(); } -void Repository::closeFastImport() +void FastImportRepository::closeFastImport() { if (fastImport.state() != QProcess::NotRunning) { fastImport.write("checkpoint\n"); @@ -97,43 +417,52 @@ void Repository::closeFastImport() } processHasStarted = false; processCache.remove(this); - // Save the exported marks - QString revsFile = name; - revsFile.replace('/', '_'); - revsFile.prepend("revisions-"); - QFile exportedMarks(revsFile); - qDebug() << exportedMarks.open(QIODevice::Truncate | QIODevice::Text | QIODevice::WriteOnly); - - int mark; - foreach(mark, commitMarks.keys()) - { - exportedMarks.write(QString(":%2 r%1\n").arg(mark).arg(commitMarks.value(mark)).toLocal8Bit()); +} + +void FastImportRepository::reloadBranches() +{ + foreach (QString branch, branches.keys()) { + Branch &br = branches[branch]; + + if (!br.marks.count() || !br.marks.last()) + continue; + + QByteArray branchRef = branch.toUtf8(); + if (!branchRef.startsWith("refs/")) + branchRef.prepend("refs/heads/"); + + fastImport.write("reset " + branchRef + + "\nfrom :" + QByteArray::number(br.marks.last()) + "\n\n" + "progress Branch " + branchRef + " reloaded\n"); } - exportedMarks.close(); } -void Repository::reloadBranches() +int FastImportRepository::markFrom(const QString &branchFrom, int branchRevNum, QByteArray &branchFromDesc) { - QProcess revParse; - revParse.setWorkingDirectory(name); - revParse.start("git", QStringList() << "rev-parse" << "--symbolic" << "--branches"); - revParse.waitForFinished(-1); - - if (revParse.exitCode() == 0 && revParse.bytesAvailable()) { - while (revParse.canReadLine()) { - QByteArray branchName = revParse.readLine().trimmed(); - - //qDebug() << "Repo" << name << "reloaded branch" << branchName; - branches[branchName].created = 1; - fastImport.write("reset refs/heads/" + branchName + - "\nfrom refs/heads/" + branchName + "^0\n\n" - "progress Branch refs/heads/" + branchName + " reloaded\n"); - } + Branch &brFrom = branches[branchFrom]; + if (!brFrom.created) + return -1; + + if (branchRevNum == brFrom.commits.last()) + return brFrom.marks.last(); + + QVector<int>::const_iterator it = qUpperBound(brFrom.commits, branchRevNum); + if (it == brFrom.commits.begin()) + return 0; + + int closestCommit = *--it; + + if (!branchFromDesc.isEmpty()) { + branchFromDesc += " at r" + QByteArray::number(branchRevNum); + if (closestCommit != branchRevNum) + branchFromDesc += " => r" + QByteArray::number(closestCommit); } + + return brFrom.marks[it - brFrom.commits.begin()]; } -void Repository::createBranch(const QString &branch, int revnum, - const QString &branchFrom, int branchRevNum) +int FastImportRepository::createBranch(const QString &branch, int revnum, + const QString &branchFrom, int branchRevNum) { startFastImport(); if (!branches.contains(branch)) { @@ -141,64 +470,64 @@ void Repository::createBranch(const QString &branch, int revnum, << "Going to create it automatically"; } - QByteArray branchRef = branch.toUtf8(); - if (!branchRef.startsWith("refs/")) - branchRef.prepend("refs/heads/"); - - - Branch &br = branches[branch]; - if (br.created && br.created != revnum) { - QByteArray backupBranch = branchRef + '_' + QByteArray::number(revnum); - qWarning() << branch << "already exists; backing up to" << backupBranch; + QByteArray branchFromDesc = "from branch " + branchFrom.toUtf8(); + int mark = markFrom(branchFrom, branchRevNum, branchFromDesc); - fastImport.write("reset " + backupBranch + "\nfrom " + branchRef + "\n\n"); + if (mark == -1) { + qCritical() << branch << "in repository" << name + << "is branching from branch" << branchFrom + << "but the latter doesn't exist. Can't continue."; + return EXIT_FAILURE; } - // now create the branch - br.created = revnum; - QByteArray branchFromRef; - const int closestCommit = *qLowerBound(commitMarks.keys(), branchRevNum); - if(exportedCommits.contains(closestCommit)) - { - bool pathFound = false; - QString path; - foreach(path, exportedCommits[closestCommit]) { - if(path.contains(branchFrom)) { - pathFound = true; - break; - } - } - - if(pathFound) { - branchFromRef = ":" + QByteArray::number(commitMarks.value(closestCommit)); - qDebug() << branch << "in repository" << name << "is branching from" << closestCommit << "(svn reports r" << branchRevNum << ") git mark:" << branchFromRef; - } else { - qWarning() << branch << "in repository" << name << "is branching from a revision that doesn't touch the branch from path, branching from current revision"; - branchFromRef = branchFrom.toUtf8(); - if (!branchFromRef.startsWith("refs/")) - branchFromRef.prepend("refs/heads/"); - } - } else { + QByteArray branchFromRef = ":" + QByteArray::number(mark); + if (!mark) { qWarning() << branch << "in repository" << name << "is branching but no exported commits exist in repository" << "creating an empty branch."; branchFromRef = branchFrom.toUtf8(); if (!branchFromRef.startsWith("refs/")) branchFromRef.prepend("refs/heads/"); + branchFromDesc += ", deleted/unknown"; } - if (!branches.contains(branchFrom) || !branches.value(branchFrom).created) { - qCritical() << branch << "in repository" << name - << "is branching from branch" << branchFrom - << "but the latter doesn't exist. Can't continue."; - exit(1); + return resetBranch(branch, revnum, mark, branchFromRef, branchFromDesc); +} + +int FastImportRepository::deleteBranch(const QString &branch, int revnum) +{ + startFastImport(); + + static QByteArray null_sha(40, '0'); + return resetBranch(branch, revnum, 0, null_sha, "delete"); +} + +int FastImportRepository::resetBranch(const QString &branch, int revnum, int mark, const QByteArray &resetTo, const QByteArray &comment) +{ + QByteArray branchRef = branch.toUtf8(); + if (!branchRef.startsWith("refs/")) + branchRef.prepend("refs/heads/"); + + Branch &br = branches[branch]; + if (br.created && br.created != revnum && br.marks.last()) { + QByteArray backupBranch = "refs/backups/r" + QByteArray::number(revnum) + branchRef.mid(4); + qWarning() << "backing up branch" << branch << "to" << backupBranch; + + fastImport.write("reset " + backupBranch + "\nfrom " + branchRef + "\n\n"); } - fastImport.write("reset " + branchRef + "\nfrom " + branchFromRef + "\n\n" - "progress Branch " + branchRef + " created from " - + branchFromRef + " r" + QByteArray::number(branchRevNum) + "(at SVN" + QByteArray::number(revnum) + ")\n\n"); + br.created = revnum; + br.commits.append(revnum); + br.marks.append(mark); + + fastImport.write("reset " + branchRef + "\nfrom " + resetTo + "\n\n" + "progress SVN r" + QByteArray::number(revnum) + + " branch " + branch.toUtf8() + " = :" + QByteArray::number(mark) + + " # " + comment + "\n\n"); + + return EXIT_SUCCESS; } -Repository::Transaction *Repository::newTransaction(const QString &branch, const QString &svnprefix, +Repository::Transaction *FastImportRepository::newTransaction(const QString &branch, const QString &svnprefix, int revnum) { startFastImport(); @@ -221,7 +550,13 @@ Repository::Transaction *Repository::newTransaction(const QString &branch, const return txn; } -void Repository::createAnnotatedTag(const QString &ref, const QString &svnprefix, +void FastImportRepository::forgetTransaction(Transaction *) +{ + if (!--outstandingTransactions) + next_file_mark = maxMark; +} + +void FastImportRepository::createAnnotatedTag(const QString &ref, const QString &svnprefix, int revnum, const QByteArray &author, uint dt, const QByteArray &log) @@ -244,7 +579,7 @@ void Repository::createAnnotatedTag(const QString &ref, const QString &svnprefix tag.dt = dt; } -void Repository::finalizeTags() +void FastImportRepository::finalizeTags() { if (annotatedTags.isEmpty()) return; @@ -291,7 +626,7 @@ void Repository::finalizeTags() printf("\n"); } -void Repository::startFastImport() +void FastImportRepository::startFastImport() { if (fastImport.state() == QProcess::NotRunning) { if (processHasStarted) @@ -299,15 +634,13 @@ void Repository::startFastImport() processHasStarted = true; // start the process - QString marksFile = name; - marksFile.replace('/', '_'); - marksFile.prepend("marks-"); + QString marksFile = marksFileName(name); QStringList marksOptions; + marksOptions << "--import-marks=" + marksFile; marksOptions << "--export-marks=" + marksFile; - QString outputFile = name; - outputFile.replace('/', '_'); - outputFile.prepend("log-"); - fastImport.setStandardOutputFile(outputFile, QIODevice::Append); + marksOptions << "--force"; + + fastImport.setStandardOutputFile(logFileName(name), QIODevice::Append); fastImport.setProcessChannelMode(QProcess::MergedChannels); if (!CommandLineParser::instance()->contains("dry-run")) { @@ -320,38 +653,60 @@ void Repository::startFastImport() } } -Repository::Transaction::~Transaction() +FastImportRepository::Transaction::~Transaction() { - --repository->outstandingTransactions; + repository->forgetTransaction(this); } -void Repository::Transaction::setAuthor(const QByteArray &a) +void FastImportRepository::Transaction::setAuthor(const QByteArray &a) { author = a; } -void Repository::Transaction::setDateTime(uint dt) +void FastImportRepository::Transaction::setDateTime(uint dt) { datetime = dt; } -void Repository::Transaction::setLog(const QByteArray &l) +void FastImportRepository::Transaction::setLog(const QByteArray &l) { log = l; } -void Repository::Transaction::deleteFile(const QString &path) +void FastImportRepository::Transaction::noteCopyFromBranch(const QString &branchFrom, int branchRevNum) +{ + static QByteArray dummy; + int mark = repository->markFrom(branchFrom, branchRevNum, dummy); + Q_ASSERT(dummy.isEmpty()); + + if (mark == -1) { + qWarning() << branch << "is copying from branch" << branchFrom + << "but the latter doesn't exist. Continuing, assuming the files exist."; + } else if (mark == 0) { + qWarning() << "Unknown revision r" << QByteArray::number(branchRevNum) + << ". Continuing, assuming the files exist."; + } else { + qWarning() << "repository " + repository->name + " branch " + branch + " has some files copied from " + branchFrom + "@" + QByteArray::number(branchRevNum); + + if (!merges.contains(mark)) + merges.append(mark); + } +} + +void FastImportRepository::Transaction::deleteFile(const QString &path) { QString pathNoSlash = path; if(pathNoSlash.endsWith('/')) pathNoSlash.chop(1); deletedFiles.append(pathNoSlash); - modifiedPaths.append(path); } -QIODevice *Repository::Transaction::addFile(const QString &path, int mode, qint64 length) +QIODevice *FastImportRepository::Transaction::addFile(const QString &path, int mode, qint64 length) { - int mark = ++repository->lastmark; + int mark = repository->next_file_mark--; + + // in case the two mark allocations meet, we might as well just abort + Q_ASSERT(mark > repository->last_commit_mark + 1); if (modifiedFiles.capacity() == 0) modifiedFiles.reserve(2048); @@ -370,14 +725,22 @@ QIODevice *Repository::Transaction::addFile(const QString &path, int mode, qint6 repository->fastImport.write(QByteArray::number(length)); repository->fastImport.write("\n", 1); } - modifiedPaths.append(branch + "/" + path); + return &repository->fastImport; } -void Repository::Transaction::commit() +void FastImportRepository::Transaction::commit() { processCache.touch(repository); + // We might be tempted to use the SVN revision number as the fast-import commit mark. + // However, a single SVN revision can modify multple branches, and thus lead to multiple + // commits in the same repo. So, we need to maintain a separate commit mark counter. + int mark = ++repository->last_commit_mark; + + // in case the two mark allocations meet, we might as well just abort + Q_ASSERT(mark < repository->next_file_mark - 1); + // create the commit message QByteArray message = log; if (!message.endsWith('\n')) @@ -385,6 +748,18 @@ void Repository::Transaction::commit() if (CommandLineParser::instance()->contains("add-metadata")) message += "\nsvn path=" + svnprefix + "; revision=" + QByteArray::number(revnum) + "\n"; + int parentmark = 0; + Branch &br = repository->branches[branch]; + if (br.created) { + parentmark = br.marks.last(); + } else { + qWarning() << "Branch" << branch << "in repository" << repository->name << "doesn't exist at revision" + << revnum << "-- did you resume from the wrong revision?"; + br.created = revnum; + } + br.commits.append(revnum); + br.marks.append(mark); + { QByteArray branchRef = branch; if (!branchRef.startsWith("refs/")) @@ -392,24 +767,38 @@ void Repository::Transaction::commit() QTextStream s(&repository->fastImport); s << "commit " << branchRef << endl; - s << "mark :" << QByteArray::number(++repository->lastmark) << endl; - repository->commitMarks.insert(revnum, repository->lastmark); - repository->exportedCommits.insert(revnum, modifiedPaths); + s << "mark :" << QByteArray::number(mark) << endl; s << "committer " << QString::fromUtf8(author) << ' ' << datetime << " -0000" << endl; - Branch &br = repository->branches[branch]; - if (!br.created) { - qWarning() << "Branch" << branch << "in repository" << repository->name << "doesn't exist at revision" - << revnum << "-- did you resume from the wrong revision?"; - br.created = revnum; - } - s << "data " << message.length() << endl; } repository->fastImport.write(message); repository->fastImport.putChar('\n'); + // note some of the inferred merges + QByteArray desc = ""; + int i = !!parentmark; // if parentmark != 0, there's at least one parent + foreach (int merge, merges) { + if (merge == parentmark) + continue; + + if (++i > 16) { + // FIXME: options: + // (1) ignore the 16 parent limit + // (2) don't emit more than 16 parents + // (3) create another commit on branch to soak up additional parents + // we've chosen option (2) for now, since only artificial commits + // created by cvs2svn seem to have this issue + qWarning() << "too many merge parents"; + break; + } + + QByteArray m = " :" + QByteArray::number(merge); + desc += m; + repository->fastImport.write("merge" + m + "\n"); + } + // write the file deletions if (deletedFiles.contains("")) repository->fastImport.write("deleteall\n"); @@ -420,10 +809,10 @@ void Repository::Transaction::commit() // write the file modifications repository->fastImport.write(modifiedFiles); - repository->fastImport.write("\nprogress Commit #" + - QByteArray::number(repository->commitCount) + - " branch " + branch + - " = SVN r" + QByteArray::number(revnum) + "\n\n"); + repository->fastImport.write("\nprogress SVN r" + QByteArray::number(revnum) + + " branch " + branch + " = :" + QByteArray::number(mark) + + (desc.isEmpty() ? "" : " # merge from") + desc + + "\n\n"); printf(" %d modifications from SVN %s to %s/%s", deletedFiles.count() + modifiedFiles.count(), svnprefix.data(), qPrintable(repository->name), branch.data()); |
