Add support for annotated tags
authorThiago Macieira <thiago@kde.org>
Mon, 8 Jun 2009 09:06:17 +0000 (8 11:06 +0200)
committerThiago Macieira <thiago@kde.org>
Mon, 8 Jun 2009 09:06:17 +0000 (8 11:06 +0200)
src/main.cpp
src/repository.cpp
src/repository.h
src/ruleparser.cpp
src/ruleparser.h
src/svn.cpp

index 8308eb6..abe9c28 100644 (file)
@@ -85,7 +85,11 @@ int main(int argc, char **argv)
         if (!svn.exportRevision(i))
             break;
 
-    qDeleteAll(repositories);
+    foreach (Repository *repo, repositories) {
+        repo->finalizeTags();
+        delete repo;
+    }
+
     // success
     return 0;
 }
index cfe4db9..182309c 100644 (file)
@@ -142,7 +142,8 @@ void Repository::createBranch(const QString &branch, int revnum,
     }
 
     fastImport.write("reset " + branchRef + "\nfrom " + branchFromRef + "\n\n"
-        "progress Branch " + branchRef + " created from " + branchFromRef + "\n\n");
+                     "progress Branch " + branchRef + " created from "
+                     + branchFromRef + " r" + QByteArray::number(revnum) + "\n\n");
 }
 
 Repository::Transaction *Repository::newTransaction(const QString &branch, const QString &svnprefix,
@@ -169,6 +170,76 @@ Repository::Transaction *Repository::newTransaction(const QString &branch, const
     return txn;
 }
 
+void Repository::createAnnotatedTag(const QString &ref, const QString &svnprefix,
+                                    int revnum,
+                                    const QByteArray &author, uint dt,
+                                    const QByteArray &log)
+{
+    QString tagName = ref;
+    if (tagName.startsWith("refs/tags/"))
+        tagName.remove(0, 10);
+
+    if (!annotatedTags.contains(tagName))
+        printf("Creating annotated tag %s (%s)\n", qPrintable(tagName), qPrintable(ref));
+    else
+        printf("Re-creating annotated tag %s\n", qPrintable(tagName));
+
+    AnnotatedTag &tag = annotatedTags[tagName];
+    tag.supportingRef = ref;
+    tag.svnprefix = svnprefix.toUtf8();
+    tag.revnum = revnum;
+    tag.author = author;
+    tag.log = log;
+    tag.dt = dt;
+}
+
+void Repository::finalizeTags()
+{
+    if (annotatedTags.isEmpty())
+        return;
+
+    printf("Finalising tags for %s...", qPrintable(name));
+    startFastImport();
+
+    QHash<QString, AnnotatedTag>::ConstIterator it = annotatedTags.constBegin();
+    for ( ; it != annotatedTags.constEnd(); ++it) {
+        const QString &tagName = it.key();
+        const AnnotatedTag &tag = it.value();
+
+        QByteArray message = tag.log;
+        if (!message.endsWith('\n'))
+            message += '\n';
+        if (Options::globalOptions->switches.value("metadata", true))
+            message += "\nsvn path=" + tag.svnprefix + "; revision=" + QByteArray::number(tag.revnum) + "\n";
+
+        {
+            QByteArray branchRef = tag.supportingRef.toUtf8();
+            if (!branchRef.startsWith("refs/"))
+                branchRef.prepend("refs/heads/");
+
+            QTextStream s(&fastImport);
+            s << "progress Creating annotated tag " << tagName << " from ref " << branchRef << endl
+              << "tag " << tagName << endl
+              << "from " << branchRef << endl
+              << "tagger " << QString::fromUtf8(tag.author) << ' ' << tag.dt << " -0000" << endl
+              << "data " << message.length() << endl;
+        }
+
+        fastImport.write(message);
+        fastImport.putChar('\n');
+        if (!fastImport.waitForBytesWritten(-1))
+            qFatal("Failed to write to process: %s", qPrintable(fastImport.errorString()));
+
+        printf(" %s", qPrintable(tagName));
+        fflush(stdout);
+    }
+
+    while (fastImport.bytesToWrite())
+        if (!fastImport.waitForBytesWritten(-1))
+            qFatal("Failed to write to process: %s", qPrintable(fastImport.errorString()));
+    printf("\n");
+}
+
 void Repository::startFastImport()
 {
     if (fastImport.state() == QProcess::NotRunning) {
index c5e70fe..ad23559 100644 (file)
@@ -62,13 +62,28 @@ public:
                       const QString &branchFrom, int revFrom);
     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;
     };
+    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;
index 0f675dd..6e799c4 100644 (file)
@@ -55,6 +55,7 @@ void Rules::load()
     QRegExp matchRepoLine("repository\\s+(\\S+)", Qt::CaseInsensitive);
     QRegExp matchBranchLine("branch\\s+(\\S+)", Qt::CaseInsensitive);
     QRegExp matchRevLine("(min|max) revision (\\d+)", Qt::CaseInsensitive);
+    QRegExp matchAnnotateLine("annotated\\s+(\\S+)", Qt::CaseInsensitive);
 
     QTextStream s(&file);
     enum { ReadingNone, ReadingRepository, ReadingMatch } state = ReadingNone;
@@ -109,6 +110,9 @@ void Rules::load()
                 else
                     qFatal("Invalid action \"%s\" on line %d", qPrintable(action), lineNumber);
                 continue;
+            } else if (matchAnnotateLine.exactMatch(line)) {
+                match.annotate = matchAnnotateLine.cap(1) == "true";
+                continue;
             } else if (line == "end match") {
                 if (!match.repository.isEmpty())
                     match.action = Match::Export;
index 9525f14..2e0904e 100644 (file)
@@ -47,6 +47,7 @@ public:
         int minRevision;
         int maxRevision;
         int lineNumber;
+        bool annotate;
 
         enum Action {
             Ignore,
@@ -54,7 +55,7 @@ public:
             Recurse
         } action;
 
-        Match() : minRevision(-1), maxRevision(-1), lineNumber(0), action(Ignore) { }
+        Match() : minRevision(-1), maxRevision(-1), lineNumber(0), annotate(false), action(Ignore) { }
     };
 
     Rules(const QString &filename);
index 4bf4e9e..aff7191 100644 (file)
@@ -365,6 +365,11 @@ public:
     svn_fs_root_t *fs_root;
     int revnum;
 
+    // must call fetchRevProps first:
+    QByteArray authorident;
+    QByteArray log;
+    uint epoch;
+
     SvnRevision(int revision, svn_fs_t *f, apr_pool_t *parent_pool)
         : pool(parent_pool), fs(f), fs_root(0), revnum(revision)
     {
@@ -377,6 +382,7 @@ public:
     }
 
     int prepareTransactions();
+    int fetchRevProps();
     int commit();
 
     int exportEntry(const char *path, const svn_fs_path_change_t *change, apr_hash_t *changes);
@@ -440,18 +446,17 @@ int SvnRevision::prepareTransactions()
     return EXIT_SUCCESS;
 }
 
-int SvnRevision::commit()
+int SvnRevision::fetchRevProps()
 {
-    // now create the commit
     apr_hash_t *revprops;
     SVN_ERR(svn_fs_revision_proplist(&revprops, fs, revnum, pool));
     svn_string_t *svnauthor = (svn_string_t*)apr_hash_get(revprops, "svn:author", APR_HASH_KEY_STRING);
     svn_string_t *svndate = (svn_string_t*)apr_hash_get(revprops, "svn:date", APR_HASH_KEY_STRING);
     svn_string_t *svnlog = (svn_string_t*)apr_hash_get(revprops, "svn:log", APR_HASH_KEY_STRING);
 
-    QByteArray log = (char *)svnlog->data;
-    QByteArray authorident = svnauthor ? identities.value((char *)svnauthor->data) : QByteArray();
-    time_t epoch = get_epoch((char*)svndate->data);
+    log = (char *)svnlog->data;
+    authorident = svnauthor ? identities.value((char *)svnauthor->data) : QByteArray();
+    epoch = get_epoch((char*)svndate->data);
     if (authorident.isEmpty()) {
         if (!svnauthor || svn_string_isempty(svnauthor))
             authorident = "nobody <nobody@localhost>";
@@ -459,7 +464,14 @@ int SvnRevision::commit()
             authorident = svnauthor->data + QByteArray(" <") +
                           svnauthor->data + QByteArray("@localhost>");
     }
+    return EXIT_SUCCESS;
+}
 
+int SvnRevision::commit()
+{
+    // now create the commit
+    if (fetchRevProps() != EXIT_SUCCESS)
+        return EXIT_FAILURE;
     foreach (Repository::Transaction *txn, transactions) {
         txn->setAuthor(authorident);
         txn->setDateTime(epoch);
@@ -596,6 +608,12 @@ int SvnRevision::exportInternal(const char *key, const svn_fs_path_change_t *cha
                 }
 
                 repo->createBranch(branch, revnum, prevbranch, rev_from);
+                if (rule.annotate) {
+                    // create an annotated tag
+                    fetchRevProps();
+                    repo->createAnnotatedTag(branch, svnprefix, revnum, authorident,
+                                             epoch, log);
+                }
                 return EXIT_SUCCESS;
             }
         }