Add CVS import scripts and programs
authorLinus Torvalds <torvalds@ppc970.osdl.org>
Tue, 7 Jun 2005 22:11:28 +0000 (15:11 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Tue, 7 Jun 2005 22:11:28 +0000 (15:11 -0700)
This gets the "cvs2git" program from the old git-tools
archive, and adds a nice script around it that makes it
much easier to use.

With this, you should be able to import a CVS archive
using just a simple

git cvsimport <cvsroot> <module>

and you're done. At least it worked for my one single test.

NOTE!! This may need tweaking. It currently expects (and
verifies) that cvsps version 2.1 is installed, but you
can't actually set any of the cvsps parameters, like the
time fuzz.

Makefile
cvs2git.c [new file with mode: 0644]
git-cvsimport-script [new file with mode: 0755]

index 50eb01c1ac27a3afe01d05050686a761683e3103..b02c234543dd3104a852821024df9c4a0cd2af5a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -23,7 +23,7 @@ INSTALL=install
 SCRIPTS=git git-apply-patch-script git-merge-one-file-script git-prune-script \
        git-pull-script git-tag-script git-resolve-script git-whatchanged \
        git-deltafy-script git-fetch-script git-status-script git-commit-script \
-       git-log-script git-shortlog
+       git-log-script git-shortlog git-cvsimport-script
 
 PROG=   git-update-cache git-diff-files git-init-db git-write-tree \
        git-read-tree git-commit-tree git-cat-file git-fsck-cache \
@@ -32,7 +32,8 @@ PROG=   git-update-cache git-diff-files git-init-db git-write-tree \
        git-unpack-file git-export git-diff-cache git-convert-cache \
        git-http-pull git-ssh-push git-ssh-pull git-rev-list git-mktag \
        git-diff-helper git-tar-tree git-local-pull git-write-blob \
-       git-get-tar-commit-id git-mkdelta git-apply git-stripspace
+       git-get-tar-commit-id git-mkdelta git-apply git-stripspace \
+       git-cvs2git
 
 all: $(PROG)
 
@@ -115,6 +116,7 @@ git-tar-tree: tar-tree.c
 git-write-blob: write-blob.c
 git-mkdelta: mkdelta.c
 git-stripspace: stripspace.c
+git-cvs2git: cvs2git.c
 
 git-http-pull: LIBS += -lcurl
 git-rev-list: LIBS += -lssl
diff --git a/cvs2git.c b/cvs2git.c
new file mode 100644 (file)
index 0000000..06dd74b
--- /dev/null
+++ b/cvs2git.c
@@ -0,0 +1,307 @@
+/*
+ * cvs2git
+ *
+ * Copyright (C) Linus Torvalds 2005
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static int verbose = 0;
+
+/*
+ * This is a really stupid program that takes cvsps output, and
+ * generates a a long _shell_script_ that will create the GIT archive
+ * from it. 
+ *
+ * You've been warned. I told you it was stupid.
+ *
+ * NOTE NOTE NOTE! In order to do branches correctly, this needs
+ * the fixed cvsps that has the "Ancestor branch" tag output.
+ * Hopefully David Mansfield will update his distribution soon
+ * enough (he's the one who wrote the patch, so at least we don't
+ * have to figt maintainer issues ;)
+ *
+ * Usage:
+ *
+ *     TZ=UTC cvsps -A |
+ *             cvs2git --cvsroot=[root] --module=[module] > script
+ *
+ * Creates a shell script that will generate the .git archive of
+ * the names CVS repository.
+ *
+ * IMPORTANT NOTE ABOUT "cvsps"! This requires version 2.1 or better,
+ * and the "TZ=UTC" and the "-A" flag is required for sane results!
+ */
+enum state {
+       Header,
+       Log,
+       Members
+};
+
+static const char *cvsroot;
+static const char *cvsmodule;
+
+static char date[100];
+static char author[100];
+static char branch[100];
+static char ancestor[100];
+static char tag[100];
+static char log[32768];
+static int loglen = 0;
+static int initial_commit = 1;
+
+static void lookup_author(char *n, char **name, char **email)
+{
+       /*
+        * FIXME!!! I'm lazy and stupid.
+        *
+        * This could be something like
+        *
+        *      printf("lookup_author '%s'\n", n);
+        *      *name = "$author_name";
+        *      *email = "$author_email";
+        *
+        * and that would allow the script to do its own
+        * lookups at run-time.
+        */
+       *name = n;
+       *email = n;
+}
+
+static void prepare_commit(void)
+{
+       char *author_name, *author_email;
+       char *src_branch;
+
+       lookup_author(author, &author_name, &author_email);
+
+       printf("export GIT_COMMITTER_NAME=%s\n", author_name);
+       printf("export GIT_COMMITTER_EMAIL=%s\n", author_email);
+       printf("export GIT_COMMITTER_DATE='+0000 %s'\n", date);
+
+       printf("export GIT_AUTHOR_NAME=%s\n", author_name);
+       printf("export GIT_AUTHOR_EMAIL=%s\n", author_email);
+       printf("export GIT_AUTHOR_DATE='+0000 %s'\n", date);
+
+       if (initial_commit)
+               return;
+
+       src_branch = *ancestor ? ancestor : branch;
+       if (!strcmp(src_branch, "HEAD"))
+               src_branch = "master";
+       printf("ln -sf refs/heads/'%s' .git/HEAD\n", src_branch);
+
+       /*
+        * Even if cvsps claims an ancestor, we'll let the new
+        * branch name take precedence if it already exists
+        */
+       if (*ancestor) {
+               src_branch = branch;
+               if (!strcmp(src_branch, "HEAD"))
+                       src_branch = "master";
+               printf("[ -e .git/refs/heads/'%s' ] && ln -sf refs/heads/'%s' .git/HEAD\n",
+                       src_branch, src_branch);
+       }
+
+       printf("git-read-tree -m HEAD || exit 1\n");
+       printf("git-checkout-cache -f -u -a\n");
+}
+
+static void commit(void)
+{
+       const char *cmit_parent = initial_commit ? "" : "-p HEAD";
+       const char *dst_branch;
+       int i;
+
+       printf("tree=$(git-write-tree)\n");
+       printf("cat > .cmitmsg <<EOFMSG\n");
+
+       /* Escape $ characters, and remove control characters */
+       for (i = 0; i < loglen; i++) {
+               unsigned char c = log[i];
+
+               switch (c) {
+               case '$':
+               case '\\':
+               case '`':
+                       putchar('\\');
+                       break;
+               case 0 ... 31:
+                       if (c == '\n' || c == '\t')
+                               break;
+               case 128 ... 159:
+                       continue;
+               }
+               putchar(c);
+       }
+       printf("\nEOFMSG\n");
+       printf("commit=$(cat .cmitmsg | git-commit-tree $tree %s)\n", cmit_parent);
+
+       dst_branch = branch;
+       if (!strcmp(dst_branch, "HEAD"))
+               dst_branch = "master";
+
+       printf("echo $commit > .git/refs/heads/'%s'\n", dst_branch);
+
+       printf("echo 'Committed (to %s):' ; cat .cmitmsg; echo\n", dst_branch);
+
+       *date = 0;
+       *author = 0;
+       *branch = 0;
+       *ancestor = 0;
+       *tag = 0;
+       loglen = 0;
+
+       initial_commit = 0;
+}
+
+static void update_file(char *line)
+{
+       char *name, *version;
+       char *dir;
+
+       while (isspace(*line))
+               line++;
+       name = line;
+       line = strchr(line, ':');
+       if (!line)
+               return;
+       *line++ = 0;
+       line = strchr(line, '>');
+       if (!line)
+               return;
+       *line++ = 0;
+       version = line;
+       line = strchr(line, '(');
+       if (line) {     /* "(DEAD)" */
+               printf("git-update-cache --force-remove '%s'\n", name);
+               return;
+       }
+
+       dir = strrchr(name, '/');
+       if (dir)
+               printf("mkdir -p %.*s\n", (int)(dir - name), name);
+
+       printf("cvs -q -d %s checkout -r%s -p '%s/%s' > '%s'\n", cvsroot, version, cvsmodule, name, name);
+       printf("git-update-cache --add -- '%s'\n", name);
+}
+
+struct hdrentry {
+       const char *name;
+       char *dest;
+} hdrs[] = {
+       { "Date:", date },
+       { "Author:", author },
+       { "Branch:", branch },
+       { "Ancestor branch:", ancestor },
+       { "Tag:", tag },
+       { "Log:", NULL },
+       { NULL, NULL }
+};
+
+int main(int argc, char **argv)
+{
+       static char line[1000];
+       enum state state = Header;
+       int i;
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (!memcmp(arg, "--cvsroot=", 10)) {
+                       cvsroot = arg + 10;
+                       continue;
+               }
+               if (!memcmp(arg, "--module=", 9)) {
+                       cvsmodule = arg+9;
+                       continue;
+               } 
+               if (!strcmp(arg, "-v")) {
+                       verbose = 1;
+                       continue;
+               }
+       }
+
+
+       if (!cvsroot)
+               cvsroot = getenv("CVSROOT");
+
+       if (!cvsmodule || !cvsroot) {
+               fprintf(stderr, "I need a CVSROOT and module name\n");
+               exit(1);
+       }
+
+       printf("[ -d .git ] && exit 1\n");
+       printf("git-init-db\n");
+       printf("mkdir -p .git/refs/heads\n");
+       printf("mkdir -p .git/refs/tags\n");
+       printf("ln -sf refs/heads/master .git/HEAD\n");
+
+       while (fgets(line, sizeof(line), stdin) != NULL) {
+               int linelen = strlen(line);
+
+               while (linelen && isspace(line[linelen-1]))
+                       line[--linelen] = 0;
+
+               switch (state) {
+               struct hdrentry *entry;
+
+               case Header:
+                       if (verbose)
+                               printf("# H: %s\n", line);
+                       for (entry = hdrs ; entry->name ; entry++) {
+                               int len = strlen(entry->name);
+                               char *val;
+
+                               if (memcmp(entry->name, line, len))
+                                       continue;
+                               if (!entry->dest) {
+                                       state = Log;
+                                       break;
+                               }
+                               val = line + len;
+                               linelen -= len;
+                               while (isspace(*val)) {
+                                       val++;
+                                       linelen--;
+                               }
+                               memcpy(entry->dest, val, linelen+1);
+                               break;
+                       }
+                       continue;
+
+               case Log:
+                       if (verbose)
+                               printf("# L: %s\n", line);
+                       if (!strcmp(line, "Members:")) {
+                               while (loglen && isspace(log[loglen-1]))
+                                       log[--loglen] = 0;
+                               prepare_commit();
+                               state = Members;
+                               continue;
+                       }
+                               
+                       if (loglen + linelen + 5 > sizeof(log))
+                               continue;
+                       memcpy(log + loglen, line, linelen);
+                       loglen += linelen;
+                       log[loglen++] = '\n';
+                       continue;
+
+               case Members:
+                       if (verbose)
+                               printf("# M: %s\n", line);
+                       if (!linelen) {
+                               commit();
+                               state = Header;
+                               continue;
+                       }
+                       update_file(line);
+                       continue;
+               }
+       }
+       return 0;
+}
diff --git a/git-cvsimport-script b/git-cvsimport-script
new file mode 100755 (executable)
index 0000000..7a43e65
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+ARGS=""
+if [ "$1" == "-v" ]; then
+       ARGS=$1
+       shift
+fi
+
+export CVSROOT="$1"
+export MODULE="$2"
+if [ ! "$CVSROOT" ] || [ ! "$MODULE" ] || [ ! -d $CVSROOT ] || [ ! -d $CVSROOT/CVSROOT ] || [ ! -d $CVSROOT/$MODULE ] ; then
+       echo "Usage: git cvsimport <cvsroot> <module>"
+       exit 1
+fi
+
+cvsps -h 2>&1 | grep -q "cvsps version 2.1" >& /dev/null || {
+       echo "I need cvsps version 2.1"
+       exit 1
+}
+
+mkdir "$MODULE" || exit 1
+cd "$MODULE"
+
+TZ=UTC cvsps -A $MODULE | git-cvs2git $ARGS --cvsroot="$CVSROOT" --module="$MODULE" > .git-create-script || exit 1
+sh .git-create-script
+