{"thread":{"id":"32585","subject":"[PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs","startedAt":"2013-01-11T03:32:32Z","lastAt":"2013-01-15T06:44:09Z","messageCount":37,"participants":["Junio C Hamano","Eric S. Raymond","Chris Rorvick","Michael Haggerty","Jonathan Nieder","John Keeping"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"206486","messageId":"1357875152-19899-1-git-send-email-gitster@pobox.com","threadId":"32585","inReplyTo":null,"subject":"[PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-11T03:32:32Z","receivedAt":"2013-01-11T03:32:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"From: \"Eric S. Raymond\" <esr@thyrsus.com>\n\nThe combination of git-cvsimport and cvsps had serious problems.\n\nAmong these were:\n\n (1) Analysis of branchy repos was buggy in multiple ways in both\n     programs, leading to incorrect repo translations.\n\n (2) Even after a correct branch analysis, extra (redundant) fileops\n     would often be generated on the new-branch side.\n\n (3) Inability to report more than one tag pointing to the same revision.\n\n (4) Failure in certain cases of clock-skew reported by the t9603 test.\n\n (5) Failure to use commitids for changeset ordering in cases were this\n     would have prevented clock skew from causing incorrect grouping.\n\nProblems 2-5 and portions of problem 1 have been solved by a major\nrewrite of cvsps (the 3.x release series); it now emits a git\nfast-import stream.  Also, the buggy attempt at ancestry-branch\ntracking previously invoked by -A has been replaced with a simpler and\nbetter topo analysis.  cvsps is now about 20% smaller than formerly.\n\nAll this changed cvsps's interface enough to require a complete\nrewrite of git-cvsimport (hence this patch). In the process the code\nsize of the wrapper script dropped by about x3 and it can now support\nalternate conversion engines; the first new engine is cvs2git, with\nparsecvs expected to follow shortly.\n\nThe old Perl git-cvsimport is moved to git-cvsimport-fallback; new\ngit-cvsimport will hand off to the fallback script when it detects\nthat the user has a pre-3.x cvsps.  The fallback is only there so that\npeople with simple enough repositories that can be correctly handled\nby cvsps-2.x but without the bleeding-edge cvsps 3.x installed does\nnot have to be left without any cvsimport that works for them (with\nthe same bugs and all).  The fallback support will be removed after\ncvsps 3.x and the rewritten cvsimport matures and gets widely\navailable.\n\nThis patch also removes Michael Haggerty's git-cvsimport tests\n(t960[123]) from the git tree.  These are actually conversion-engine\ntests and have been merged into a larger cvsps test suite, which I\nintend to spin out into a general CVS-lifting test that can also be\napplied to utilities such as cvs2git and parsecvs.  The t9604 test\nwill move in a future patch, when I likewise have it integrated\ninto the general test suite.\n\nThe following known bug has not been fixed: \"If any files were ever\n\"cvs import\"ed more than once (e.g., import of more than one vendor\nrelease) the HEAD contains the wrong content.\" However, cvsps now\nemits a warning in this case. There is also one pathological tagging\ncase that was successful in the former t9602 test that now fails\n(with a warning).\n\nI plan to address these problems. This patch at least gets the\ncvsps-3.x/git-cvsimport combination to a state that is not too\nbroken to ship - that is, in all failure cases known to me it\nnow emits useful warnings rather than silently botching the\nimport.\n\nSigned-off-by: Eric S. Raymond <esr@thyrsus.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * This was (re)sent privately to me; the previous attempt by Eric\n   somehow did not reach the list.  Let me see if I have better\n   luck, as we really would want to be reviewing these patches.\n\n   I rewrote one paragraph in the log about the fallback, though.\n   For the record the original read like this:\n\n     The old Perl git-cvsimport is moved to git-cvsimport-fallback; new\n     git-cvsimport will hand off to the fallback script when it detects\n     that the user has a pre-3.x cvsps.  I make no warranties that\n     cvsps-2.x will actually work or do anything more useful than make\n     demons fly out of your nose; the fallback is only there because Junio\n     disliked the idea of a flag day, and should be removed after a decent\n     interval.\n\n   which I felt somewhat irresponsible to existing users.\n\n Makefile                                           |   3 +-\n git-cvsimport.perl => git-cvsimport-fallback.perl  |   8 +\n git-cvsimport.py                                   | 354 +++++++++++++++++++++\n t/t9601/cvsroot/.gitattributes                     |   1 -\n t/t9601/cvsroot/CVSROOT/.gitignore                 |   2 -\n t/t9601/cvsroot/module/added-imported.txt,v        |  44 ---\n t/t9601/cvsroot/module/imported-anonymously.txt,v  |  42 ---\n .../module/imported-modified-imported.txt,v        |  76 -----\n t/t9601/cvsroot/module/imported-modified.txt,v     |  59 ----\n t/t9601/cvsroot/module/imported-once.txt,v         |  43 ---\n t/t9601/cvsroot/module/imported-twice.txt,v        |  60 ----\n t/t9602/README                                     |  62 ----\n t/t9602/cvsroot/.gitattributes                     |   1 -\n t/t9602/cvsroot/CVSROOT/.gitignore                 |   2 -\n t/t9602/cvsroot/module/default,v                   | 102 ------\n t/t9602/cvsroot/module/sub1/default,v              | 102 ------\n t/t9602/cvsroot/module/sub1/subsubA/default,v      | 101 ------\n t/t9602/cvsroot/module/sub1/subsubB/default,v      | 107 -------\n .../module/sub2/Attic/branch_B_MIXED_only,v        |  59 ----\n t/t9602/cvsroot/module/sub2/default,v              | 102 ------\n t/t9602/cvsroot/module/sub2/subsubA/default,v      | 102 ------\n t/t9602/cvsroot/module/sub3/default,v              | 102 ------\n t/t9603/cvsroot/.gitattributes                     |   1 -\n t/t9603/cvsroot/CVSROOT/.gitignore                 |   2 -\n t/t9603/cvsroot/module/a,v                         |  74 -----\n t/t9603/cvsroot/module/b,v                         |  90 ------\n 26 files changed, 364 insertions(+), 1337 deletions(-)\n rename git-cvsimport.perl => git-cvsimport-fallback.perl (98%)\n create mode 100755 git-cvsimport.py\n delete mode 100644 t/t9601/cvsroot/.gitattributes\n delete mode 100644 t/t9601/cvsroot/CVSROOT/.gitignore\n delete mode 100644 t/t9601/cvsroot/module/added-imported.txt,v\n delete mode 100644 t/t9601/cvsroot/module/imported-anonymously.txt,v\n delete mode 100644 t/t9601/cvsroot/module/imported-modified-imported.txt,v\n delete mode 100644 t/t9601/cvsroot/module/imported-modified.txt,v\n delete mode 100644 t/t9601/cvsroot/module/imported-once.txt,v\n delete mode 100644 t/t9601/cvsroot/module/imported-twice.txt,v\n delete mode 100644 t/t9602/README\n delete mode 100644 t/t9602/cvsroot/.gitattributes\n delete mode 100644 t/t9602/cvsroot/CVSROOT/.gitignore\n delete mode 100644 t/t9602/cvsroot/module/default,v\n delete mode 100644 t/t9602/cvsroot/module/sub1/default,v\n delete mode 100644 t/t9602/cvsroot/module/sub1/subsubA/default,v\n delete mode 100644 t/t9602/cvsroot/module/sub1/subsubB/default,v\n delete mode 100644 t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v\n delete mode 100644 t/t9602/cvsroot/module/sub2/default,v\n delete mode 100644 t/t9602/cvsroot/module/sub2/subsubA/default,v\n delete mode 100644 t/t9602/cvsroot/module/sub3/default,v\n delete mode 100644 t/t9603/cvsroot/.gitattributes\n delete mode 100644 t/t9603/cvsroot/CVSROOT/.gitignore\n delete mode 100644 t/t9603/cvsroot/module/a,v\n delete mode 100644 t/t9603/cvsroot/module/b,v\n\ndiff --git a/Makefile b/Makefile\nindex 736ecd4..ca5d9e9 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -464,7 +464,7 @@ SCRIPT_PERL += git-add--interactive.perl\n SCRIPT_PERL += git-difftool.perl\n SCRIPT_PERL += git-archimport.perl\n SCRIPT_PERL += git-cvsexportcommit.perl\n-SCRIPT_PERL += git-cvsimport.perl\n+SCRIPT_PERL += git-cvsimport-fallback.perl\n SCRIPT_PERL += git-cvsserver.perl\n SCRIPT_PERL += git-relink.perl\n SCRIPT_PERL += git-send-email.perl\n@@ -472,6 +472,7 @@ SCRIPT_PERL += git-svn.perl\n \n SCRIPT_PYTHON += git-remote-testgit.py\n SCRIPT_PYTHON += git-p4.py\n+SCRIPT_PYTHON += git-cvsimport.py\n \n SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \\\n \t  $(patsubst %.perl,%,$(SCRIPT_PERL)) \\\ndiff --git a/git-cvsimport.perl b/git-cvsimport-fallback.perl\nsimilarity index 98%\nrename from git-cvsimport.perl\nrename to git-cvsimport-fallback.perl\nindex 0a31ebd..4bc0717 100755\n--- a/git-cvsimport.perl\n+++ b/git-cvsimport-fallback.perl\n@@ -1,4 +1,8 @@\n #!/usr/bin/perl\n+# This code became obsolete in January 2013, and is retained only as a\n+# fallback from git-cvsimport.py for users who have only cvsps-2.x.\n+# It (and the code in cvsimport.py that calls it) should be removed\n+# once the 3.x version has had a reasonable time to propagate.\n \n # This tool is copyright (c) 2005, Matthias Urlichs.\n # It is released under the Gnu Public License, version 2.\n@@ -27,6 +31,10 @@\n use POSIX qw(strftime tzset dup2 ENOENT);\n use IPC::Open2;\n \n+print(STDERR \"You do not appear to have cvsps 3.x.\\n\");\n+print(STDERR \"Falling back to unmaintained Perl cvsimport for cvsps 2.x.\\n\");\n+print(STDERR \"Upgrade your cvsps for best results.\\n\");\n+\n $SIG{'PIPE'}=\"IGNORE\";\n set_timezone('UTC');\n \ndiff --git a/git-cvsimport.py b/git-cvsimport.py\nnew file mode 100755\nindex 0000000..129471e\n--- /dev/null\n+++ b/git-cvsimport.py\n@@ -0,0 +1,354 @@\n+#!/usr/bin/env python\n+#\n+# Import CVS history into git\n+#\n+# Intended to be a near-workalike of Matthias Urlichs's Perl implementation.\n+#\n+# By Eric S. Raymond <esr@thyrsus.com>, December 2012\n+# May be redistributed under the license of the git project.\n+\n+import sys\n+\n+if sys.hexversion < 0x02060000:\n+    sys.stderr.write(\"git cvsimport: requires Python 2.6 or later.\\n\")\n+    sys.exit(1)\n+\n+import os, getopt, subprocess, tempfile, shutil\n+\n+DEBUG_COMMANDS = 1\n+\n+class Fatal(Exception):\n+    \"Unrecoverable error.\"\n+    def __init__(self, msg):\n+        Exception.__init__(self)\n+        self.msg = msg\n+\n+def do_or_die(dcmd, legend=\"\"):\n+    \"Either execute a command or raise a fatal exception.\"\n+    if legend:\n+        legend = \" \"  + legend\n+    if verbose >= DEBUG_COMMANDS:\n+        sys.stdout.write(\"git cvsimport: executing '%s'%s\\n\" % (dcmd, legend))\n+    try:\n+        retcode = subprocess.call(dcmd, shell=True)\n+        if retcode < 0:\n+            raise Fatal(\"git cvsimport: child was terminated by signal %d.\" % -retcode)\n+        elif retcode != 0:\n+            raise Fatal(\"git cvsimport: child returned %d.\" % retcode)\n+    except (OSError, IOError) as e:\n+        raise Fatal(\"git cvsimport: execution of %s%s failed: %s\" % (dcmd, legend, e))\n+\n+def capture_or_die(dcmd, legend=\"\"):\n+    \"Either execute a command and capture its output or die.\"\n+    if legend:\n+        legend = \" \"  + legend\n+    if verbose >= DEBUG_COMMANDS:\n+        sys.stdout.write(\"git cvsimport: executing '%s'%s\\n\" % (dcmd, legend))\n+    try:\n+        return subprocess.check_output(dcmd, shell=True)\n+    except subprocess.CalledProcessError as e:\n+        if e.returncode < 0:\n+            sys.stderr.write(\"git cvsimport: child was terminated by signal %d.\" % -e.returncode)\n+        elif e.returncode != 0:\n+            sys.stderr.write(\"git cvsimport: child returned %d.\" % e.returncode)\n+        sys.exit(1)\n+\n+class cvsps:\n+    \"Method class for cvsps back end.\"\n+    def __init__(self):\n+        self.opts = \"\"\n+        self.revmap = None\n+    def set_repo(self, val):\n+        \"Set the repository root option.\"\n+        if not val.startswith(\":\"):\n+            if not val.startswith(os.sep):\n+                val = os.path.abspath(val)\n+            val = \":local:\" + val\n+        self.opts += \" --root '%s'\" % val\n+    def set_authormap(self, val):\n+        \"Set the author-map file.\"\n+        self.opts += \" -A '%s'\" % val\n+    def set_fuzz(self, val):\n+        \"Set the commit-similarity window.\"\n+        self.opts += \" -z %s\" % val\n+    def set_nokeywords(self):\n+        \"Suppress CVS keyword expansion.\"\n+        self.opts += \" -k\"\n+    def add_opts(self, val):\n+        \"Add options to the engine command line.\"\n+        self.opts += \" \" + val\n+    def set_exclusion(self, val):\n+        \"Set a file exclusion regexp.\"\n+        self.opts += \" -n -f '%s'\" % val\n+    def set_after(self, val):\n+        \"Set a date threshold for incremental import.\"\n+        self.opts += \" -d '%s'\" % val\n+    def set_revmap(self, val):\n+        \"Set the file to which the engine should dump a reference map.\"\n+        self.revmap = val\n+        self.opts += \" -R '%s'\" % self.revmap\n+    def set_module(self, val):\n+        \"Set the module to query.\"\n+        self.opts += \" \" + val\n+    def command(self):\n+        \"Emit the command implied by all previous options.\"\n+        return \"cvsps --fast-export \" + self.opts\n+\n+class cvs2git:\n+    \"Method class for cvs2git back end.\"\n+    def __init__(self):\n+        self.opts = \"\"\n+        self.modulepath = \".\"\n+    def set_authormap(self, _val):\n+        \"Set the author-map file.\"\n+        sys.stderr.write(\"git cvsimport: author maping is not supported with cvs2git.\\n\")\n+        sys.exit(1)\n+    def set_repo(self, _val):\n+        \"Set the repository root option.\"\n+        sys.stderr.write(\"git cvsimport: cvs2git must run within a repository checkout directory.\\n\")\n+        sys.exit(1)\n+    def set_fuzz(self, _val):\n+        \"Set the commit-similarity window.\"\n+        sys.stderr.write(\"git cvsimport: fuzz setting is not supported with cvs2git.\\n\")\n+        sys.exit(1)\n+    def set_nokeywords(self):\n+        \"Suppress CVS keyword expansion.\"\n+        self.opts += \" --keywords-off\"\n+    def add_opts(self, val):\n+        \"Add options to the engine command line.\"\n+        self.opts += \" \" + val\n+    def set_exclusion(self, val):\n+        \"Set a file exclusion regexp.\"\n+        self.opts += \" --exclude='%s'\" % val\n+    def set_after(self, _val):\n+        \"Set a date threshold for incremental import.\"\n+        sys.stderr.write(\"git cvsimport: incremental import is not supported with cvs2git.\\n\")\n+        sys.exit(1)\n+    def set_revmap(self, _val):\n+        \"Set the file to which the engine should dump a reference map.\"\n+        sys.stderr.write(\"git cvsimport: can't get a reference map from cvs2git.\\n\")\n+        sys.exit(1)\n+    def set_module(self, val):\n+        \"Set the module to query.\"\n+        self.modulepath = \" \" + val\n+    def command(self):\n+        \"Emit the command implied by all previous options.\"\n+        return \"(cvs2git --username=git-cvsimport --quiet --quiet --blobfile={0} --dumpfile={1} {2} {3} && cat {0} {1} && rm {0} {1})\".format(tempfile.mkstemp()[1], tempfile.mkstemp()[1], self.opts, self.modulepath)\n+\n+class filesource:\n+    \"Method class for file-source back end.\"\n+    def __init__(self, filename):\n+        self.filename = filename\n+    def __complain(self, legend):\n+        sys.stderr.write(\"git cvsimport: %s with file source.\\n\" % legend)\n+        sys.exit(1)\n+    def set_repo(self, _val):\n+        \"Set the repository root option.\"\n+        self.__complain(\"repository can't be set\")\n+    def set_authormap(self, _val):\n+        \"Set the author-map file.\"\n+        sys.stderr.write(\"git cvsimport: author mapping is not supported with filesource.\\n\")\n+        sys.exit(1)\n+    def set_fuzz(self, _val):\n+        \"Set the commit-similarity window.\"\n+        self.__complain(\"fuzz can't be set\")\n+    def set_nokeywords(self, _val):\n+        \"Suppress CVS keyword expansion.\"\n+        self.__complain(\"keyword suppression can't be set\")\n+    def add_opts(self, _val):\n+        \"Add options to the engine command line.\"\n+        self.__complain(\"other options can't be set\")\n+    def set_exclusion(self, _val):\n+        \"Set a file exclusion regexp.\"\n+        self.__complain(\"exclusions can't be set\")\n+    def set_after(self, _val):\n+        \"Set a date threshold for incremental import.\"\n+        pass\n+    def set_revmap(self, _val):\n+        \"Set the file to which the engine should dump a reference map.\"\n+        sys.stderr.write(\"git cvsimport: can't get a reference map from cvs2git.\\n\")\n+        sys.exit(1)\n+    def set_module(self, _val):\n+        \"Set the module to query.\"\n+        self.__complain(\"module can't be set\")\n+    def command(self):\n+        \"Emit the command implied by all previous options.\"\n+        return \"cat \" + self.filename\n+\n+if __name__ == '__main__':\n+    if sys.hexversion < 0x02060000:\n+        sys.stderr.write(\"git cvsimport: requires Python 2.6 or later.\\n\")\n+        sys.exit(1)\n+    (options, arguments) = getopt.getopt(sys.argv[1:], \"vbe:d:C:r:o:ikus:p:z:P:S:aL:A:Rh\")\n+    verbose = 0\n+    bare = False\n+    root = None\n+    outdir = os.getcwd()\n+    remotize = False\n+    import_only = False\n+    underscore_to_dot = False\n+    slashsubst = None\n+    authormap = None\n+    revisionmap = False\n+    backend = cvsps()\n+    for (opt, val) in options:\n+        if opt == '-v':\n+            verbose += 1\n+        elif opt == '-b':\n+            bare = True\n+        elif opt == '-e':\n+            for cls in (cvsps, cvs2git):\n+                if cls.__name__ == val:\n+                    backend = cls()\n+                    break\n+            else:\n+                sys.stderr.write(\"git cvsimport: unknown engine %s.\\n\" % val)\n+                sys.exit(1)\n+        elif opt == '-d':\n+            backend.set_repo(val)\n+        elif opt == '-C':\n+            outdir = val\n+        elif opt == '-r':\n+            remotize = True\n+        elif opt == '-o':\n+            sys.stderr.write(\"git cvsimport: -o is no longer supported.\\n\")\n+            sys.exit(1)\n+        elif opt == '-i':\n+            import_only = True\n+        elif opt == '-k':\n+            backend.set_nokeywords()\n+        elif opt == '-u':\n+            underscore_to_dot = True\n+        elif opt == '-s':\n+            slashsubst = val\n+        elif opt == '-p':\n+            backend.add_opts(val.replace(\",\", \" \"))\n+        elif opt == '-z':\n+            backend.set_fuzz(val)\n+        elif opt == '-P':\n+            backend = filesource(val)\n+            sys.exit(1)\n+        elif opt in ('-m', '-M'):\n+            sys.stderr.write(\"git cvsimport: -m and -M are no longer supported: use reposurgeon instead.\\n\")\n+            sys.exit(1)\n+        elif opt == '-S':\n+            backend.set_exclusion(val)\n+        elif opt == '-a':\n+            sys.stderr.write(\"git cvsimport: -a is no longer supported.\\n\")\n+            sys.exit(1)\n+        elif opt == '-L':\n+            sys.stderr.write(\"git cvsimport: -L is no longer supported.\\n\")\n+            sys.exit(1)\n+        elif opt == '-A':\n+            authormap = os.path.abspath(val)\n+        elif opt == '-R':\n+            revisionmap = True\n+        else:\n+            print \"\"\"\\\n+git cvsimport [-A <author-conv-file>] [-C <git_repository>] [-b] [-d <CVSROOT>]\n+     [-e engine] [-h] [-i] [-k] [-p <options-for-cvsps>] [-P <source-file>]\n+     [-r <remote>] [-R] [-s <subst>] [-S <regex>] [-u] [-v] [-z <fuzz>]\n+     [<CVS_module>]\n+\"\"\"\n+    def metadata(fn, outdir='.'):\n+        if bare:\n+            return os.path.join(outdir, fn)\n+        else:\n+            return os.path.join(outdir, \".git\", fn)\n+    # Ugly fallback code for people with only cvsps-2.x\n+    # Added January 2013 - should be removed after a decent interval.\n+    if backend.__class__.__name__ == \"cvsps\":\n+        try:\n+            subprocess.check_output(\"cvsps -V 2>/dev/null\", shell=True)\n+        except subprocess.CalledProcessError as e:\n+            if e.returncode == 1:\n+                sys.stderr.write(\"cvsimport: falling back to old version...\\n\")\n+                sys.exit(os.system(\"git-cvsimport-fallback \" + \" \".join(sys.argv[1:])))\n+            else:\n+                sys.stderr.write(\"cvsimport: cannot execute cvsps.\\n\")\n+                sys.exit(1)\n+    # Real mainline code begins here\n+    try:\n+        if outdir:\n+            try:\n+                # If the output directory does not exist, create it\n+                # and initialize it as a git repository.\n+                os.mkdir(outdir)\n+                do_or_die(\"git init --quiet \" + outdir)\n+            except OSError:\n+                # Otherwise, assume user wants incremental import.\n+                if not bare and not os.path.exists(os.path.join(outdir, \".git\")):\n+                    raise Fatal(\"output directory is not a git repository\")\n+                threshold = capture_or_die(\"git log -1 --format=%ct\").strip()\n+                backend.set_after(threshold)\n+        if revisionmap:\n+            backend.set_revmap(tempfile.mkstemp()[1])\n+            markmap = tempfile.mkstemp()[1]\n+        if arguments:\n+            backend.set_module(arguments[0])\n+        gitopts = \"\"\n+        if bare:\n+            gitopts += \" --bare\"\n+        if revisionmap:\n+            gitopts += \" --export-marks='%s'\" % markmap\n+        if authormap:\n+            shutil.copyfile(authormap, metadata(\"cvs-authors\", outdir))\n+        if os.path.exists(metadata(\"cvs-authors\", outdir)):\n+            backend.set_authormap(metadata(\"cvs-authors\", outdir))\n+        do_or_die(\"%s | (cd %s >/dev/null; git fast-import --quiet %s)\" \\\n+                  % (backend.command(), outdir, gitopts))\n+        os.chdir(outdir)\n+        if underscore_to_dot or slashsubst:\n+            tagnames = capture_or_die(\"git tag -l\")\n+            for tag in tagnames.split():\n+                if tag:\n+                    changed = tag\n+                    if underscore_to_dot:\n+                        changed = changed.replace(\"_\", \".\")\n+                    if slashsubst:\n+                        changed = changed.replace(os.sep, slashsubst)\n+                    if changed != tag:\n+                        do_or_die(\"git tag -f %s %s >/dev/null\" % (tag, changed))\n+        if underscore_to_dot or slashsubst or remotize:\n+            branchnames = capture_or_die(\"git branch -l\")\n+            for branch in branchnames.split():\n+                if branch:\n+                    # Ugh - fragile dependency on branch -l output format\n+                    branch = branch[2:]\n+                    changed = branch\n+                    if underscore_to_dot:\n+                        changed = changed.replace(\"_\", \".\")\n+                    if slashsubst:\n+                        changed = changed.replace(os.sep, slashsubst)\n+                    if remotize:\n+                        changed = os.path.join(\"remotes\", remotize, branch)\n+                    if changed != branch:\n+                        do_or_die(\"branch --m %s %s >/dev/null\" % (branch, changed))\n+        if revisionmap:\n+            refd = {}\n+            for line in open(backend.revmap):\n+                if line.startswith(\"#\"):\n+                    continue\n+                (fn, rev, mark) = line.split()\n+                refd[(fn, rev)] = mark\n+            markd = {}\n+            for line in open(markmap):\n+                if line.startswith(\"#\"):\n+                    continue\n+                (mark, hashd) = line.split()\n+                markd[mark] = hashd\n+            with open(metadata(\"cvs-revisions\"), \"a\") as wfp:\n+                for ((fn, rev), val) in refd.items():\n+                    if val in markd:\n+                        wfp.write(\"%s %s %s\\n\" % (fn, rev, markd[val]))\n+            os.remove(markmap)\n+            os.remove(backend.revmap)\n+        if not import_only and not bare:\n+            do_or_die(\"git checkout -q\")\n+    except Fatal, err:\n+        sys.stderr.write(\"git_cvsimport: \" + err.msg + \"\\n\")\n+        sys.exit(1)\n+    except KeyboardInterrupt:\n+        pass\n+\n+# end\ndiff --git a/t/t9601/cvsroot/.gitattributes b/t/t9601/cvsroot/.gitattributes\ndeleted file mode 100644\nindex 562b12e..0000000\n--- a/t/t9601/cvsroot/.gitattributes\n+++ /dev/null\n@@ -1 +0,0 @@\n-* -whitespace\ndiff --git a/t/t9601/cvsroot/CVSROOT/.gitignore b/t/t9601/cvsroot/CVSROOT/.gitignore\ndeleted file mode 100644\nindex 3bb9b34..0000000\n--- a/t/t9601/cvsroot/CVSROOT/.gitignore\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-history\n-val-tags\ndiff --git a/t/t9601/cvsroot/module/added-imported.txt,v b/t/t9601/cvsroot/module/added-imported.txt,v\ndeleted file mode 100644\nindex 5f83072..0000000\n--- a/t/t9601/cvsroot/module/added-imported.txt,v\n+++ /dev/null\n@@ -1,44 +0,0 @@\n-head\t1.1;\n-access;\n-symbols\n-\tvtag-4:1.1.1.1\n-\tvbranchA:1.1.1;\n-locks; strict;\n-comment\t@# @;\n-\n-\n-1.1\n-date\t2004.02.09.15.43.15;\tauthor kfogel;\tstate Exp;\n-branches\n-\t1.1.1.1;\n-next\t;\n-\n-1.1.1.1\n-date\t2004.02.09.15.43.16;\tauthor kfogel;\tstate Exp;\n-branches;\n-next\t;\n-\n-\n-desc\n-@@\n-\n-\n-1.1\n-log\n-@Add a file to the working copy.\n-@\n-text\n-@Adding this file, before importing it with different contents.\n-@\n-\n-\n-1.1.1.1\n-log\n-@Import (vbranchA, vtag-4).\n-@\n-text\n-@d1 1\n-a1 1\n-This is vtag-4 (on vbranchA) of added-then-imported.txt.\n-@\n-\ndiff --git a/t/t9601/cvsroot/module/imported-anonymously.txt,v b/t/t9601/cvsroot/module/imported-anonymously.txt,v\ndeleted file mode 100644\nindex 55e1b0c..0000000\n--- a/t/t9601/cvsroot/module/imported-anonymously.txt,v\n+++ /dev/null\n@@ -1,42 +0,0 @@\n-head\t1.1;\n-branch\t1.1.1;\n-access;\n-symbols\n-\tvtag-1:1.1.1.1;\n-locks; strict;\n-comment\t@# @;\n-\n-\n-1.1\n-date\t2004.02.09.15.43.13;\tauthor kfogel;\tstate Exp;\n-branches\n-\t1.1.1.1;\n-next\t;\n-\n-1.1.1.1\n-date\t2004.02.09.15.43.13;\tauthor kfogel;\tstate Exp;\n-branches;\n-next\t;\n-\n-\n-desc\n-@@\n-\n-\n-1.1\n-log\n-@Initial revision\n-@\n-text\n-@This is vtag-1 (on vbranchA) of imported-anonymously.txt.\n-@\n-\n-\n-1.1.1.1\n-log\n-@Import (vbranchA, vtag-1).\n-@\n-text\n-@@\n-\n-\ndiff --git a/t/t9601/cvsroot/module/imported-modified-imported.txt,v b/t/t9601/cvsroot/module/imported-modified-imported.txt,v\ndeleted file mode 100644\nindex e5830ae..0000000\n--- a/t/t9601/cvsroot/module/imported-modified-imported.txt,v\n+++ /dev/null\n@@ -1,76 +0,0 @@\n-head\t1.2;\n-access;\n-symbols\n-\tvtag-2:1.1.1.2\n-\tvtag-1:1.1.1.1\n-\tvbranchA:1.1.1;\n-locks; strict;\n-comment\t@# @;\n-\n-\n-1.2\n-date\t2004.02.09.15.43.14;\tauthor kfogel;\tstate Exp;\n-branches;\n-next\t1.1;\n-\n-1.1\n-date\t2004.02.09.15.43.13;\tauthor kfogel;\tstate Exp;\n-branches\n-\t1.1.1.1;\n-next\t;\n-\n-1.1.1.1\n-date\t2004.02.09.15.43.13;\tauthor kfogel;\tstate Exp;\n-branches;\n-next\t1.1.1.2;\n-\n-1.1.1.2\n-date\t2004.02.09.15.43.13;\tauthor kfogel;\tstate Exp;\n-branches;\n-next\t;\n-\n-\n-desc\n-@@\n-\n-\n-1.2\n-log\n-@First regular commit, to imported-modified-imported.txt, on HEAD.\n-@\n-text\n-@This is a modification of imported-modified-imported.txt on HEAD.\n-It should supersede the version from the vendor branch.\n-@\n-\n-\n-1.1\n-log\n-@Initial revision\n-@\n-text\n-@d1 2\n-a2 1\n-This is vtag-1 (on vbranchA) of imported-modified-imported.txt.\n-@\n-\n-\n-1.1.1.1\n-log\n-@Import (vbranchA, vtag-1).\n-@\n-text\n-@@\n-\n-\n-1.1.1.2\n-log\n-@Import (vbranchA, vtag-2).\n-@\n-text\n-@d1 1\n-a1 1\n-This is vtag-2 (on vbranchA) of imported-modified-imported.txt.\n-@\n-\n-\ndiff --git a/t/t9601/cvsroot/module/imported-modified.txt,v b/t/t9601/cvsroot/module/imported-modified.txt,v\ndeleted file mode 100644\nindex bbcfe44..0000000\n--- a/t/t9601/cvsroot/module/imported-modified.txt,v\n+++ /dev/null\n@@ -1,59 +0,0 @@\n-head\t1.2;\n-access;\n-symbols\n-\tvtag-1:1.1.1.1\n-\tvbranchA:1.1.1;\n-locks; strict;\n-comment\t@# @;\n-\n-\n-1.2\n-date\t2004.02.09.15.43.14;\tauthor kfogel;\tstate Exp;\n-branches;\n-next\t1.1;\n-\n-1.1\n-date\t2004.02.09.15.43.13;\tauthor kfogel;\tstate Exp;\n-branches\n-\t1.1.1.1;\n-next\t;\n-\n-1.1.1.1\n-date\t2004.02.09.15.43.13;\tauthor kfogel;\tstate Exp;\n-branches;\n-next\t;\n-\n-\n-desc\n-@@\n-\n-\n-1.2\n-log\n-@Commit on HEAD.\n-@\n-text\n-@This is a modification of imported-modified.txt on HEAD.\n-It should supersede the version from the vendor branch.\n-@\n-\n-\n-1.1\n-log\n-@Initial revision\n-@\n-text\n-@d1 2\n-a2 1\n-This is vtag-1 (on vbranchA) of imported-modified.txt.\n-@\n-\n-\n-1.1.1.1\n-log\n-@Import (vbranchA, vtag-1).\n-@\n-text\n-@@\n-\n-\ndiff --git a/t/t9601/cvsroot/module/imported-once.txt,v b/t/t9601/cvsroot/module/imported-once.txt,v\ndeleted file mode 100644\nindex c5dd82b..0000000\n--- a/t/t9601/cvsroot/module/imported-once.txt,v\n+++ /dev/null\n@@ -1,43 +0,0 @@\n-head\t1.1;\n-branch\t1.1.1;\n-access;\n-symbols\n-\tvtag-1:1.1.1.1\n-\tvbranchA:1.1.1;\n-locks; strict;\n-comment\t@# @;\n-\n-\n-1.1\n-date\t2004.02.09.15.43.13;\tauthor kfogel;\tstate Exp;\n-branches\n-\t1.1.1.1;\n-next\t;\n-\n-1.1.1.1\n-date\t2004.02.09.15.43.13;\tauthor kfogel;\tstate Exp;\n-branches;\n-next\t;\n-\n-\n-desc\n-@@\n-\n-\n-1.1\n-log\n-@Initial revision\n-@\n-text\n-@This is vtag-1 (on vbranchA) of imported-once.txt.\n-@\n-\n-\n-1.1.1.1\n-log\n-@Import (vbranchA, vtag-1).\n-@\n-text\n-@@\n-\n-\ndiff --git a/t/t9601/cvsroot/module/imported-twice.txt,v b/t/t9601/cvsroot/module/imported-twice.txt,v\ndeleted file mode 100644\nindex d1f3f1b..0000000\n--- a/t/t9601/cvsroot/module/imported-twice.txt,v\n+++ /dev/null\n@@ -1,60 +0,0 @@\n-head\t1.1;\n-branch\t1.1.1;\n-access;\n-symbols\n-\tvtag-2:1.1.1.2\n-\tvtag-1:1.1.1.1\n-\tvbranchA:1.1.1;\n-locks; strict;\n-comment\t@# @;\n-\n-\n-1.1\n-date\t2004.02.09.15.43.13;\tauthor kfogel;\tstate Exp;\n-branches\n-\t1.1.1.1;\n-next\t;\n-\n-1.1.1.1\n-date\t2004.02.09.15.43.13;\tauthor kfogel;\tstate Exp;\n-branches;\n-next\t1.1.1.2;\n-\n-1.1.1.2\n-date\t2004.02.09.15.43.13;\tauthor kfogel;\tstate Exp;\n-branches;\n-next\t;\n-\n-\n-desc\n-@@\n-\n-\n-1.1\n-log\n-@Initial revision\n-@\n-text\n-@This is vtag-1 (on vbranchA) of imported-twice.txt.\n-@\n-\n-\n-1.1.1.1\n-log\n-@Import (vbranchA, vtag-1).\n-@\n-text\n-@@\n-\n-\n-1.1.1.2\n-log\n-@Import (vbranchA, vtag-2).\n-@\n-text\n-@d1 1\n-a1 1\n-This is vtag-2 (on vbranchA) of imported-twice.txt.\n-@\n-\n-\ndiff --git a/t/t9602/README b/t/t9602/README\ndeleted file mode 100644\nindex c231e0f..0000000\n--- a/t/t9602/README\n+++ /dev/null\n@@ -1,62 +0,0 @@\n-This repository is for testing the ability to group revisions\n-correctly along tags and branches.  Here is its history:\n-\n-  1.  The initial import (revision 1.1 of everybody) created a\n-      directory structure with a file named `default' in each dir:\n-\n-            ./\n-              default\n-              sub1/default\n-                   subsubA/default\n-                   subsubB/default\n-              sub2/default\n-                   subsubA/default\n-              sub3/default\n-\n-  2.  Then tagged everyone with T_ALL_INITIAL_FILES.\n-\n-  3.  Then tagged everyone except sub1/subsubB/default with\n-      T_ALL_INITIAL_FILES_BUT_ONE.\n-\n-  4.  Then created branch B_FROM_INITIALS on everyone.\n-\n-  5.  Then created branch B_FROM_INITIALS_BUT_ONE on everyone except\n-      /sub1/subsubB/default.\n-\n-  6.  Then committed modifications to two files: sub3/default, and\n-      sub1/subsubA/default.\n-\n-  7.  Then committed a modification to all 7 files.\n-\n-  8.  Then backdated sub3/default to revision 1.2, and\n-      sub2/subsubA/default to revision 1.1, and tagged with T_MIXED.\n-\n-  9.  Same as 8, but tagged with -b to create branch B_MIXED.\n-\n-  10. Switched the working copy to B_MIXED, and added\n-      sub2/branch_B_MIXED_only.  (That's why the RCS file is in\n-      sub2/Attic/ -- it never existed on trunk.)\n-\n-  11. In one commit, modified default, sub1/default, and\n-      sub2/subsubA/default, on branch B_MIXED.\n-\n-  12. Did \"cvs up -A\" on sub2/default, then in one commit, made a\n-      change to sub2/default and sub2/branch_B_MIXED_only.  So this\n-      commit should be spread between the branch and the trunk.\n-\n-  13. Do \"cvs up -A\" to get everyone back to trunk, then make a new\n-      branch B_SPLIT on everyone except sub1/subsubB/default,v.\n-\n-  14. Switch to branch B_SPLIT (see sub1/subsubB/default disappear)\n-      and commit a change that affects everyone except sub3/default.\n-\n-  15. An hour or so later, \"cvs up -A\" to get sub1/subsubB/default\n-      back, then commit a change on that file, on trunk.  (It's\n-      important that this change happened after the previous commits\n-      on B_SPLIT.)\n-\n-  16. Branch sub1/subsubB/default to B_SPLIT, then \"cvs up -r B_SPLIT\"\n-      to switch the whole working copy to the branch.\n-\n-  17. Commit a change on B_SPLIT, to sub1/subsubB/default and\n-      sub3/default.\ndiff --git a/t/t9602/cvsroot/.gitattributes b/t/t9602/cvsroot/.gitattributes\ndeleted file mode 100644\nindex 562b12e..0000000\n--- a/t/t9602/cvsroot/.gitattributes\n+++ /dev/null\n@@ -1 +0,0 @@\n-* -whitespace\ndiff --git a/t/t9602/cvsroot/CVSROOT/.gitignore b/t/t9602/cvsroot/CVSROOT/.gitignore\ndeleted file mode 100644\nindex 3bb9b34..0000000\n--- a/t/t9602/cvsroot/CVSROOT/.gitignore\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-history\n-val-tags\ndiff --git a/t/t9602/cvsroot/module/default,v b/t/t9602/cvsroot/module/default,v\ndeleted file mode 100644\nindex 3b68382..0000000\n--- a/t/t9602/cvsroot/module/default,v\n+++ /dev/null\n@@ -1,102 +0,0 @@\n-head\t1.2;\n-access;\n-symbols\n-\tB_SPLIT:1.2.0.4\n-\tB_MIXED:1.2.0.2\n-\tT_MIXED:1.2\n-\tB_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4\n-\tB_FROM_INITIALS:1.1.1.1.0.2\n-\tT_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1\n-\tT_ALL_INITIAL_FILES:1.1.1.1\n-\tvendortag:1.1.1.1\n-\tvendorbranch:1.1.1;\n-locks; strict;\n-comment\t@# @;\n-\n-\n-1.2\n-date\t2003.05.23.00.17.53;\tauthor jrandom;\tstate Exp;\n-branches\n-\t1.2.2.1\n-\t1.2.4.1;\n-next\t1.1;\n-\n-1.1\n-date\t2003.05.22.23.20.19;\tauthor jrandom;\tstate Exp;\n-branches\n-\t1.1.1.1;\n-next\t;\n-\n-1.1.1.1\n-date\t2003.05.22.23.20.19;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-1.2.2.1\n-date\t2003.05.23.00.31.36;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-1.2.4.1\n-date\t2003.06.03.03.20.31;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-\n-desc\n-@@\n-\n-\n-1.2\n-log\n-@Second commit to proj, affecting all 7 files.\n-@\n-text\n-@This is the file `default' in the top level of the project.\n-\n-Every directory in the `proj' project has a file named `default'.\n-\n-This line was added in the second commit (affecting all 7 files).\n-@\n-\n-\n-1.2.4.1\n-log\n-@First change on branch B_SPLIT.\n-\n-This change excludes sub3/default, because it was not part of this\n-commit, and sub1/subsubB/default, which is not even on the branch yet.\n-@\n-text\n-@a5 2\n-\n-First change on branch B_SPLIT.\n-@\n-\n-\n-1.2.2.1\n-log\n-@Modify three files, on branch B_MIXED.\n-@\n-text\n-@a5 2\n-\n-This line was added on branch B_MIXED only (affecting 3 files).\n-@\n-\n-\n-1.1\n-log\n-@Initial revision\n-@\n-text\n-@d4 2\n-@\n-\n-\n-1.1.1.1\n-log\n-@Initial import.\n-@\n-text\n-@@\ndiff --git a/t/t9602/cvsroot/module/sub1/default,v b/t/t9602/cvsroot/module/sub1/default,v\ndeleted file mode 100644\nindex b7fdccd..0000000\n--- a/t/t9602/cvsroot/module/sub1/default,v\n+++ /dev/null\n@@ -1,102 +0,0 @@\n-head\t1.2;\n-access;\n-symbols\n-\tB_SPLIT:1.2.0.4\n-\tB_MIXED:1.2.0.2\n-\tT_MIXED:1.2\n-\tB_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4\n-\tB_FROM_INITIALS:1.1.1.1.0.2\n-\tT_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1\n-\tT_ALL_INITIAL_FILES:1.1.1.1\n-\tvendortag:1.1.1.1\n-\tvendorbranch:1.1.1;\n-locks; strict;\n-comment\t@# @;\n-\n-\n-1.2\n-date\t2003.05.23.00.17.53;\tauthor jrandom;\tstate Exp;\n-branches\n-\t1.2.2.1\n-\t1.2.4.1;\n-next\t1.1;\n-\n-1.1\n-date\t2003.05.22.23.20.19;\tauthor jrandom;\tstate Exp;\n-branches\n-\t1.1.1.1;\n-next\t;\n-\n-1.1.1.1\n-date\t2003.05.22.23.20.19;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-1.2.2.1\n-date\t2003.05.23.00.31.36;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-1.2.4.1\n-date\t2003.06.03.03.20.31;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-\n-desc\n-@@\n-\n-\n-1.2\n-log\n-@Second commit to proj, affecting all 7 files.\n-@\n-text\n-@This is sub1/default.\n-\n-Every directory in the `proj' project has a file named `default'.\n-\n-This line was added in the second commit (affecting all 7 files).\n-@\n-\n-\n-1.2.4.1\n-log\n-@First change on branch B_SPLIT.\n-\n-This change excludes sub3/default, because it was not part of this\n-commit, and sub1/subsubB/default, which is not even on the branch yet.\n-@\n-text\n-@a5 2\n-\n-First change on branch B_SPLIT.\n-@\n-\n-\n-1.2.2.1\n-log\n-@Modify three files, on branch B_MIXED.\n-@\n-text\n-@a5 2\n-\n-This line was added on branch B_MIXED only (affecting 3 files).\n-@\n-\n-\n-1.1\n-log\n-@Initial revision\n-@\n-text\n-@d4 2\n-@\n-\n-\n-1.1.1.1\n-log\n-@Initial import.\n-@\n-text\n-@@\ndiff --git a/t/t9602/cvsroot/module/sub1/subsubA/default,v b/t/t9602/cvsroot/module/sub1/subsubA/default,v\ndeleted file mode 100644\nindex 472b7b2..0000000\n--- a/t/t9602/cvsroot/module/sub1/subsubA/default,v\n+++ /dev/null\n@@ -1,101 +0,0 @@\n-head\t1.3;\n-access;\n-symbols\n-\tB_SPLIT:1.3.0.4\n-\tB_MIXED:1.3.0.2\n-\tT_MIXED:1.3\n-\tB_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4\n-\tB_FROM_INITIALS:1.1.1.1.0.2\n-\tT_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1\n-\tT_ALL_INITIAL_FILES:1.1.1.1\n-\tvendortag:1.1.1.1\n-\tvendorbranch:1.1.1;\n-locks; strict;\n-comment\t@# @;\n-\n-\n-1.3\n-date\t2003.05.23.00.17.53;\tauthor jrandom;\tstate Exp;\n-branches\n-\t1.3.4.1;\n-next\t1.2;\n-\n-1.2\n-date\t2003.05.23.00.15.26;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t1.1;\n-\n-1.1\n-date\t2003.05.22.23.20.19;\tauthor jrandom;\tstate Exp;\n-branches\n-\t1.1.1.1;\n-next\t;\n-\n-1.1.1.1\n-date\t2003.05.22.23.20.19;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-1.3.4.1\n-date\t2003.06.03.03.20.31;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-\n-desc\n-@@\n-\n-\n-1.3\n-log\n-@Second commit to proj, affecting all 7 files.\n-@\n-text\n-@This is sub1/subsubA/default.\n-\n-Every directory in the `proj' project has a file named `default'.\n-\n-This line was added by the first commit (affecting two files).\n-\n-This line was added in the second commit (affecting all 7 files).\n-@\n-\n-\n-1.3.4.1\n-log\n-@First change on branch B_SPLIT.\n-\n-This change excludes sub3/default, because it was not part of this\n-commit, and sub1/subsubB/default, which is not even on the branch yet.\n-@\n-text\n-@a7 2\n-\n-First change on branch B_SPLIT.\n-@\n-\n-\n-1.2\n-log\n-@First commit to proj, affecting two files.\n-@\n-text\n-@d6 2\n-@\n-\n-\n-1.1\n-log\n-@Initial revision\n-@\n-text\n-@d4 2\n-@\n-\n-\n-1.1.1.1\n-log\n-@Initial import.\n-@\n-text\n-@@\ndiff --git a/t/t9602/cvsroot/module/sub1/subsubB/default,v b/t/t9602/cvsroot/module/sub1/subsubB/default,v\ndeleted file mode 100644\nindex fe6efa4..0000000\n--- a/t/t9602/cvsroot/module/sub1/subsubB/default,v\n+++ /dev/null\n@@ -1,107 +0,0 @@\n-head\t1.3;\n-access;\n-symbols\n-\tB_SPLIT:1.3.0.2\n-\tB_MIXED:1.2.0.2\n-\tT_MIXED:1.2\n-\tB_FROM_INITIALS:1.1.1.1.0.2\n-\tT_ALL_INITIAL_FILES:1.1.1.1\n-\tvendortag:1.1.1.1\n-\tvendorbranch:1.1.1;\n-locks; strict;\n-comment\t@# @;\n-\n-\n-1.3\n-date\t2003.06.03.04.29.14;\tauthor jrandom;\tstate Exp;\n-branches\n-\t1.3.2.1;\n-next\t1.2;\n-\n-1.2\n-date\t2003.05.23.00.17.53;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t1.1;\n-\n-1.1\n-date\t2003.05.22.23.20.19;\tauthor jrandom;\tstate Exp;\n-branches\n-\t1.1.1.1;\n-next\t;\n-\n-1.1.1.1\n-date\t2003.05.22.23.20.19;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-1.3.2.1\n-date\t2003.06.03.04.33.13;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-\n-desc\n-@@\n-\n-\n-1.3\n-log\n-@A trunk change to sub1/subsubB/default.  This was committed about an\n-hour after an earlier change that affected most files on branch\n-B_SPLIT.  This file is not on that branch yet, but after this commit,\n-we'll branch to B_SPLIT, albeit rooted in a revision that didn't exist\n-at the time the rest of B_SPLIT was created.\n-@\n-text\n-@This is sub1/subsubB/default.\n-\n-Every directory in the `proj' project has a file named `default'.\n-\n-This line was added in the second commit (affecting all 7 files).\n-\n-This bit was committed on trunk about an hour after an earlier change\n-to everyone else on branch B_SPLIT.  Afterwards, we'll finally branch\n-this file to B_SPLIT, but rooted in a revision that didn't exist at\n-the time the rest of B_SPLIT was created.\n-@\n-\n-\n-1.3.2.1\n-log\n-@This change affects sub3/default and sub1/subsubB/default, on branch\n-B_SPLIT.  Note that the latter file did not even exist on this branch\n-until after some other files had had revisions committed on B_SPLIT.\n-@\n-text\n-@a10 4\n-\n-This change affects sub3/default and sub1/subsubB/default, on branch\n-B_SPLIT.  Note that the latter file did not even exist on this branch\n-until after some other files had had revisions committed on B_SPLIT.\n-@\n-\n-\n-1.2\n-log\n-@Second commit to proj, affecting all 7 files.\n-@\n-text\n-@d6 5\n-@\n-\n-\n-1.1\n-log\n-@Initial revision\n-@\n-text\n-@d4 2\n-@\n-\n-\n-1.1.1.1\n-log\n-@Initial import.\n-@\n-text\n-@@\ndiff --git a/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v b/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v\ndeleted file mode 100644\nindex 34c9789..0000000\n--- a/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v\n+++ /dev/null\n@@ -1,59 +0,0 @@\n-head\t1.1;\n-access;\n-symbols\n-\tB_MIXED:1.1.0.2;\n-locks; strict;\n-comment\t@# @;\n-\n-\n-1.1\n-date\t2003.05.23.00.25.26;\tauthor jrandom;\tstate dead;\n-branches\n-\t1.1.2.1;\n-next\t;\n-\n-1.1.2.1\n-date\t2003.05.23.00.25.26;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t1.1.2.2;\n-\n-1.1.2.2\n-date\t2003.05.23.00.48.51;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-\n-desc\n-@@\n-\n-\n-1.1\n-log\n-@file branch_B_MIXED_only was initially added on branch B_MIXED.\n-@\n-text\n-@@\n-\n-\n-1.1.2.1\n-log\n-@Add a file on branch B_MIXED.\n-@\n-text\n-@a0 1\n-This file was added on branch B_MIXED.  It never existed on trunk.\n-@\n-\n-\n-1.1.2.2\n-log\n-@A single commit affecting one file on branch B_MIXED and one on trunk.\n-@\n-text\n-@a1 3\n-\n-The same commit added these two lines here on branch B_MIXED, and two\n-similar lines to ./default on trunk.\n-@\n-\n-\ndiff --git a/t/t9602/cvsroot/module/sub2/default,v b/t/t9602/cvsroot/module/sub2/default,v\ndeleted file mode 100644\nindex 018f7f8..0000000\n--- a/t/t9602/cvsroot/module/sub2/default,v\n+++ /dev/null\n@@ -1,102 +0,0 @@\n-head\t1.3;\n-access;\n-symbols\n-\tB_SPLIT:1.3.0.2\n-\tB_MIXED:1.2.0.2\n-\tT_MIXED:1.2\n-\tB_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4\n-\tB_FROM_INITIALS:1.1.1.1.0.2\n-\tT_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1\n-\tT_ALL_INITIAL_FILES:1.1.1.1\n-\tvendortag:1.1.1.1\n-\tvendorbranch:1.1.1;\n-locks; strict;\n-comment\t@# @;\n-\n-\n-1.3\n-date\t2003.05.23.00.48.51;\tauthor jrandom;\tstate Exp;\n-branches\n-\t1.3.2.1;\n-next\t1.2;\n-\n-1.2\n-date\t2003.05.23.00.17.53;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t1.1;\n-\n-1.1\n-date\t2003.05.22.23.20.19;\tauthor jrandom;\tstate Exp;\n-branches\n-\t1.1.1.1;\n-next\t;\n-\n-1.1.1.1\n-date\t2003.05.22.23.20.19;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-1.3.2.1\n-date\t2003.06.03.03.20.31;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-\n-desc\n-@@\n-\n-\n-1.3\n-log\n-@A single commit affecting one file on branch B_MIXED and one on trunk.\n-@\n-text\n-@This is sub2/default.\n-\n-Every directory in the `proj' project has a file named `default'.\n-\n-This line was added in the second commit (affecting all 7 files).\n-\n-The same commit added these two lines here on trunk, and two similar\n-lines to ./branch_B_MIXED_only on branch B_MIXED.\n-@\n-\n-\n-1.3.2.1\n-log\n-@First change on branch B_SPLIT.\n-\n-This change excludes sub3/default, because it was not part of this\n-commit, and sub1/subsubB/default, which is not even on the branch yet.\n-@\n-text\n-@a8 2\n-\n-First change on branch B_SPLIT.\n-@\n-\n-\n-1.2\n-log\n-@Second commit to proj, affecting all 7 files.\n-@\n-text\n-@d6 3\n-@\n-\n-\n-1.1\n-log\n-@Initial revision\n-@\n-text\n-@d4 2\n-@\n-\n-\n-1.1.1.1\n-log\n-@Initial import.\n-@\n-text\n-@@\ndiff --git a/t/t9602/cvsroot/module/sub2/subsubA/default,v b/t/t9602/cvsroot/module/sub2/subsubA/default,v\ndeleted file mode 100644\nindex d13242c..0000000\n--- a/t/t9602/cvsroot/module/sub2/subsubA/default,v\n+++ /dev/null\n@@ -1,102 +0,0 @@\n-head\t1.2;\n-access;\n-symbols\n-\tB_SPLIT:1.2.0.2\n-\tB_MIXED:1.1.0.2\n-\tT_MIXED:1.1\n-\tB_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4\n-\tB_FROM_INITIALS:1.1.1.1.0.2\n-\tT_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1\n-\tT_ALL_INITIAL_FILES:1.1.1.1\n-\tvendortag:1.1.1.1\n-\tvendorbranch:1.1.1;\n-locks; strict;\n-comment\t@# @;\n-\n-\n-1.2\n-date\t2003.05.23.00.17.53;\tauthor jrandom;\tstate Exp;\n-branches\n-\t1.2.2.1;\n-next\t1.1;\n-\n-1.1\n-date\t2003.05.22.23.20.19;\tauthor jrandom;\tstate Exp;\n-branches\n-\t1.1.1.1\n-\t1.1.2.1;\n-next\t;\n-\n-1.1.1.1\n-date\t2003.05.22.23.20.19;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-1.1.2.1\n-date\t2003.05.23.00.31.36;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-1.2.2.1\n-date\t2003.06.03.03.20.31;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-\n-desc\n-@@\n-\n-\n-1.2\n-log\n-@Second commit to proj, affecting all 7 files.\n-@\n-text\n-@This is sub2/subsub2/default.\n-\n-Every directory in the `proj' project has a file named `default'.\n-\n-This line was added in the second commit (affecting all 7 files).\n-@\n-\n-\n-1.2.2.1\n-log\n-@First change on branch B_SPLIT.\n-\n-This change excludes sub3/default, because it was not part of this\n-commit, and sub1/subsubB/default, which is not even on the branch yet.\n-@\n-text\n-@a5 2\n-\n-First change on branch B_SPLIT.\n-@\n-\n-\n-1.1\n-log\n-@Initial revision\n-@\n-text\n-@d4 2\n-@\n-\n-\n-1.1.2.1\n-log\n-@Modify three files, on branch B_MIXED.\n-@\n-text\n-@a3 2\n-\n-This line was added on branch B_MIXED only (affecting 3 files).\n-@\n-\n-\n-1.1.1.1\n-log\n-@Initial import.\n-@\n-text\n-@@\ndiff --git a/t/t9602/cvsroot/module/sub3/default,v b/t/t9602/cvsroot/module/sub3/default,v\ndeleted file mode 100644\nindex 88e4567..0000000\n--- a/t/t9602/cvsroot/module/sub3/default,v\n+++ /dev/null\n@@ -1,102 +0,0 @@\n-head\t1.3;\n-access;\n-symbols\n-\tB_SPLIT:1.3.0.2\n-\tB_MIXED:1.2.0.2\n-\tT_MIXED:1.2\n-\tB_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4\n-\tB_FROM_INITIALS:1.1.1.1.0.2\n-\tT_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1\n-\tT_ALL_INITIAL_FILES:1.1.1.1\n-\tvendortag:1.1.1.1\n-\tvendorbranch:1.1.1;\n-locks; strict;\n-comment\t@# @;\n-\n-\n-1.3\n-date\t2003.05.23.00.17.53;\tauthor jrandom;\tstate Exp;\n-branches\n-\t1.3.2.1;\n-next\t1.2;\n-\n-1.2\n-date\t2003.05.23.00.15.26;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t1.1;\n-\n-1.1\n-date\t2003.05.22.23.20.19;\tauthor jrandom;\tstate Exp;\n-branches\n-\t1.1.1.1;\n-next\t;\n-\n-1.1.1.1\n-date\t2003.05.22.23.20.19;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-1.3.2.1\n-date\t2003.06.03.04.33.13;\tauthor jrandom;\tstate Exp;\n-branches;\n-next\t;\n-\n-\n-desc\n-@@\n-\n-\n-1.3\n-log\n-@Second commit to proj, affecting all 7 files.\n-@\n-text\n-@This is sub3/default.\n-\n-Every directory in the `proj' project has a file named `default'.\n-\n-This line was added by the first commit (affecting two files).\n-\n-This line was added in the second commit (affecting all 7 files).\n-@\n-\n-\n-1.3.2.1\n-log\n-@This change affects sub3/default and sub1/subsubB/default, on branch\n-B_SPLIT.  Note that the latter file did not even exist on this branch\n-until after some other files had had revisions committed on B_SPLIT.\n-@\n-text\n-@a7 4\n-\n-This change affects sub3/default and sub1/subsubB/default, on branch\n-B_SPLIT.  Note that the latter file did not even exist on this branch\n-until after some other files had had revisions committed on B_SPLIT.\n-@\n-\n-\n-1.2\n-log\n-@First commit to proj, affecting two files.\n-@\n-text\n-@d6 2\n-@\n-\n-\n-1.1\n-log\n-@Initial revision\n-@\n-text\n-@d4 2\n-@\n-\n-\n-1.1.1.1\n-log\n-@Initial import.\n-@\n-text\n-@@\ndiff --git a/t/t9603/cvsroot/.gitattributes b/t/t9603/cvsroot/.gitattributes\ndeleted file mode 100644\nindex 562b12e..0000000\n--- a/t/t9603/cvsroot/.gitattributes\n+++ /dev/null\n@@ -1 +0,0 @@\n-* -whitespace\ndiff --git a/t/t9603/cvsroot/CVSROOT/.gitignore b/t/t9603/cvsroot/CVSROOT/.gitignore\ndeleted file mode 100644\nindex 3bb9b34..0000000\n--- a/t/t9603/cvsroot/CVSROOT/.gitignore\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-history\n-val-tags\ndiff --git a/t/t9603/cvsroot/module/a,v b/t/t9603/cvsroot/module/a,v\ndeleted file mode 100644\nindex ba8fd5a..0000000\n--- a/t/t9603/cvsroot/module/a,v\n+++ /dev/null\n@@ -1,74 +0,0 @@\n-head\t1.2;\n-access;\n-symbols\n-\tA:1.2.0.2;\n-locks; strict;\n-comment\t@# @;\n-\n-\n-1.2\n-date\t2009.02.21.18.11.14;\tauthor tester;\tstate Exp;\n-branches\n-\t1.2.2.1;\n-next\t1.1;\n-\n-1.1\n-date\t2009.02.21.18.11.43;\tauthor tester;\tstate Exp;\n-branches;\n-next\t;\n-\n-1.2.2.1\n-date\t2009.03.11.19.03.52;\tauthor tester;\tstate Exp;\n-branches;\n-next\t1.2.2.2;\n-\n-1.2.2.2\n-date\t2009.03.11.19.09.10;\tauthor tester;\tstate Exp;\n-branches;\n-next\t;\n-\n-\n-desc\n-@@\n-\n-\n-1.2\n-log\n-@Rev 2\n-@\n-text\n-@1.2\n-@\n-\n-\n-1.2.2.1\n-log\n-@Rev 4 Branch A\n-@\n-text\n-@d1 1\n-a1 1\n-1.2.2.1\n-@\n-\n-\n-1.2.2.2\n-log\n-@Rev 5 Branch A\n-@\n-text\n-@d1 1\n-a1 1\n-1.2.2.2\n-@\n-\n-\n-1.1\n-log\n-@Rev 1\n-@\n-text\n-@d1 1\n-a1 1\n-1.1\n-@\ndiff --git a/t/t9603/cvsroot/module/b,v b/t/t9603/cvsroot/module/b,v\ndeleted file mode 100644\nindex d268855..0000000\n--- a/t/t9603/cvsroot/module/b,v\n+++ /dev/null\n@@ -1,90 +0,0 @@\n-head\t1.3;\n-access;\n-symbols\n-\tA:1.2.0.2;\n-locks; strict;\n-comment\t@# @;\n-\n-\n-1.3\n-date\t2009.03.11.19.05.08;\tauthor tester;\tstate Exp;\n-branches;\n-next\t1.2;\n-\n-1.2\n-date\t2009.02.21.18.11.43;\tauthor tester;\tstate Exp;\n-branches\n-\t1.2.2.1;\n-next\t1.1;\n-\n-1.1\n-date\t2009.02.21.18.11.14;\tauthor tester;\tstate Exp;\n-branches;\n-next\t;\n-\n-1.2.2.1\n-date\t2009.03.11.19.03.52;\tauthor tester;\tstate Exp;\n-branches;\n-next\t1.2.2.2;\n-\n-1.2.2.2\n-date\t2009.03.11.19.09.10;\tauthor tester;\tstate Exp;\n-branches;\n-next\t;\n-\n-\n-desc\n-@@\n-\n-\n-1.3\n-log\n-@Rev 4\n-@\n-text\n-@1.3\n-@\n-\n-\n-1.2\n-log\n-@Rev 3\n-@\n-text\n-@d1 1\n-a1 1\n-1.2\n-@\n-\n-\n-1.2.2.1\n-log\n-@Rev 4 Branch A\n-@\n-text\n-@d1 1\n-a1 1\n-1.2.2.1\n-@\n-\n-\n-1.2.2.2\n-log\n-@Rev 5 Branch A\n-@\n-text\n-@d1 1\n-a1 1\n-1.2\n-@\n-\n-\n-1.1\n-log\n-@Rev 2\n-@\n-text\n-@d1 1\n-a1 1\n-1.1\n-@\n-- \n1.8.1.407.g59f98af\n"},{"id":"206538","messageId":"7va9sfd6lk.fsf@alter.siamese.dyndns.org","threadId":"32585","inReplyTo":"1357875152-19899-1-git-send-email-gitster@pobox.com","subject":"Re: [PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-11T16:31:19Z","receivedAt":"2013-01-11T16:31:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"> From: \"Eric S. Raymond\" <esr@thyrsus.com>\n> ...\n> diff --git a/git-cvsimport.perl b/git-cvsimport-fallback.perl\n> similarity index 98%\n> rename from git-cvsimport.perl\n> rename to git-cvsimport-fallback.perl\n> index 0a31ebd..4bc0717 100755\n> --- a/git-cvsimport.perl\n> +++ b/git-cvsimport-fallback.perl\n> @@ -1,4 +1,8 @@\n>  #!/usr/bin/perl\n> +# This code became obsolete in January 2013, and is retained only as a\n> +# fallback from git-cvsimport.py for users who have only cvsps-2.x.\n> +# It (and the code in cvsimport.py that calls it) should be removed\n> +# once the 3.x version has had a reasonable time to propagate.\n>  \n>  # This tool is copyright (c) 2005, Matthias Urlichs.\n>  # It is released under the Gnu Public License, version 2.\n> @@ -27,6 +31,10 @@\n>  use POSIX qw(strftime tzset dup2 ENOENT);\n>  use IPC::Open2;\n>  \n> +print(STDERR \"You do not appear to have cvsps 3.x.\\n\");\n> +print(STDERR \"Falling back to unmaintained Perl cvsimport for cvsps 2.x.\\n\");\n> +print(STDERR \"Upgrade your cvsps for best results.\\n\");\n\nI think the prevalent style in this script is to write \"print\"\nwithout parentheses:\n\n\tprint STDERR \"msg\\n\";\n\n> diff --git a/git-cvsimport.py b/git-cvsimport.py\n> new file mode 100755\n> index 0000000..129471e\n> --- /dev/null\n> +++ b/git-cvsimport.py\n> @@ -0,0 +1,354 @@\n> +#!/usr/bin/env python\n> +#\n> +# Import CVS history into git\n> +#\n> +# Intended to be a near-workalike of Matthias Urlichs's Perl implementation.\n> ...\n> +class cvsps:\n> +    \"Method class for cvsps back end.\"\n> +    def __init__(self):\n> +        self.opts = \"\"\n> +        self.revmap = None\n> +    def set_repo(self, val):\n> +        \"Set the repository root option.\"\n> +        if not val.startswith(\":\"):\n> +            if not val.startswith(os.sep):\n> +                val = os.path.abspath(val)\n> +            val = \":local:\" + val\n> +        self.opts += \" --root '%s'\" % val\n\nThis looks lazy and unsafe quoting.  Is there anything that makes\nsure repository path does not contain a single quote?\n\n> +    def set_authormap(self, val):\n> +        \"Set the author-map file.\"\n> +        self.opts += \" -A '%s'\" % val\n> +    def set_fuzz(self, val):\n> +        \"Set the commit-similarity window.\"\n> +        self.opts += \" -z %s\" % val\n> +    def set_nokeywords(self):\n> +        \"Suppress CVS keyword expansion.\"\n> +        self.opts += \" -k\"\n> +    def add_opts(self, val):\n> +        \"Add options to the engine command line.\"\n> +        self.opts += \" \" + val\n\n... especially for callers of this method.\n\nThe same comment applies to many uses of \"val\" in the method\nimplementations of this class and the cvs2git class.\n\n> +    def command(self):\n> +        \"Emit the command implied by all previous options.\"\n> +        return \"(cvs2git --username=git-cvsimport --quiet --quiet --blobfile={0} --dumpfile={1} {2} {3} && cat {0} {1} && rm {0} {1})\".format(tempfile.mkstemp()[1], tempfile.mkstemp()[1], self.opts, self.modulepath)\n\nCould we do something better with this overlong source line?\n\n> +if __name__ == '__main__':\n> ...\n> +    for (opt, val) in options:\n> +        if opt == '-v':\n> +            verbose += 1\n> +        elif opt == '-b':\n> +            bare = True\n> +        elif opt == '-e':\n> +            for cls in (cvsps, cvs2git):\n> +                if cls.__name__ == val:\n> +                    backend = cls()\n> +                    break\n> +            else:\n> +                sys.stderr.write(\"git cvsimport: unknown engine %s.\\n\" % val)\n> +                sys.exit(1)\n> +        elif opt == '-d':\n> +            backend.set_repo(val)\n> +        elif opt == '-C':\n> +            outdir = val\n> +        elif opt == '-r':\n> +            remotize = True\n> +        elif opt == '-o':\n> +            sys.stderr.write(\"git cvsimport: -o is no longer supported.\\n\")\n> +            sys.exit(1)\n\nIsn't this a regression?\n\n> +        elif opt == '-i':\n> +            import_only = True\n> +        elif opt == '-k':\n> +            backend.set_nokeywords()\n> +        elif opt == '-u':\n> +            underscore_to_dot = True\n> +        elif opt == '-s':\n> +            slashsubst = val\n> +        elif opt == '-p':\n> +            backend.add_opts(val.replace(\",\", \" \"))\n> +        elif opt == '-z':\n> ...\n> +        elif opt == '-P':\n> +            backend = filesource(val)\n> +            sys.exit(1)\n\n???\n\n> +        elif opt in ('-m', '-M'):\n> +            sys.stderr.write(\"git cvsimport: -m and -M are no longer supported: use reposurgeon instead.\\n\")\n> +            sys.exit(1)\n\nI wonder if it is better to ignore these options with a warning but\nstill let the command continue; cvsps-3.x was supposed to get merges\nright without the help of these ad-hoc options, no?\n\nOtherwise it looks like a regression to me.\n\n> +        elif opt == '-S':\n> +            backend.set_exclusion(val)\n> +        elif opt == '-a':\n> +            sys.stderr.write(\"git cvsimport: -a is no longer supported.\\n\")\n> +            sys.exit(1)\n> +        elif opt == '-L':\n> +            sys.stderr.write(\"git cvsimport: -L is no longer supported.\\n\")\n> +            sys.exit(1)\n> +        elif opt == '-A':\n> +            authormap = os.path.abspath(val)\n> +        elif opt == '-R':\n> +            revisionmap = True\n> +        else:\n> +            print \"\"\"\\\n> +git cvsimport [-A <author-conv-file>] [-C <git_repository>] [-b] [-d <CVSROOT>]\n> +     [-e engine] [-h] [-i] [-k] [-p <options-for-cvsps>] [-P <source-file>]\n> +     [-r <remote>] [-R] [-s <subst>] [-S <regex>] [-u] [-v] [-z <fuzz>]\n> +     [<CVS_module>]\n> +\"\"\"\n> +    def metadata(fn, outdir='.'):\n> +        if bare:\n> +            return os.path.join(outdir, fn)\n> +        else:\n> +            return os.path.join(outdir, \".git\", fn)\n> +    # Ugly fallback code for people with only cvsps-2.x\n> +    # Added January 2013 - should be removed after a decent interval.\n> +    if backend.__class__.__name__ == \"cvsps\":\n> +        try:\n> +            subprocess.check_output(\"cvsps -V 2>/dev/null\", shell=True)\n> +        except subprocess.CalledProcessError as e:\n> +            if e.returncode == 1:\n> +                sys.stderr.write(\"cvsimport: falling back to old version...\\n\")\n> +                sys.exit(os.system(\"git-cvsimport-fallback \" + \" \".join(sys.argv[1:])))\n> +            else:\n> +                sys.stderr.write(\"cvsimport: cannot execute cvsps.\\n\")\n> +                sys.exit(1)\n\nHaving the code to die when it sees options the rewritten version\ndoes not yet support before it calls the fallback makes the fallback\nmuch less effective, no?\n\n> +    # Real mainline code begins here\n> +    try:\n> +        if outdir:\n> +            try:\n> +                # If the output directory does not exist, create it\n> +                # and initialize it as a git repository.\n> +                os.mkdir(outdir)\n> +                do_or_die(\"git init --quiet \" + outdir)\n\nDid anything made sure outdir is without $IFS chars up to this\npoint?\n\nNot very impressed (yet).  The advertised \"fix major bugs\" sounds\nmore like \"trade major bugs with different ones with a couple of\nfeature removals\" at this point.\n"},{"id":"206550","messageId":"20130111185818.GA30398@thyrsus.com","threadId":"32585","inReplyTo":"7va9sfd6lk.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs","fromName":"Eric S. Raymond","fromEmail":"esr@thyrsus.com","sentAt":"2013-01-11T18:58:18Z","receivedAt":"2013-01-11T18:58:18Z","isPatch":true,"sender":{"key":"esr@thyrsus.com","avatar":"https://avatars.githubusercontent.com/u/727961?v=4"},"body":"Junio C Hamano <gitster@pobox.com>:\n> I think the prevalent style in this script is to write \"print\"\n> without parentheses:\n> \n> \tprint STDERR \"msg\\n\";\n\nThat can be easily fixed.\n\n> This looks lazy and unsafe quoting.  Is there anything that makes\n> sure repository path does not contain a single quote?\n\nNo. But...wait, checking...the Perl code didn't have the analogous\ncheck, so there's no increased vulnerability here.  I'll put it on the\nto-do list for after I ship parsecvs.\n\n> > +    def command(self):\n> > +        \"Emit the command implied by all previous options.\"\n> > +        return \"(cvs2git --username=git-cvsimport --quiet --quiet --blobfile={0} --dumpfile={1} {2} {3} && cat {0} {1} && rm {0} {1})\".format(tempfile.mkstemp()[1], tempfile.mkstemp()[1], self.opts, self.modulepath)\n> \n> Could we do something better with this overlong source line?\n\nYes.  The correct fix is to simplify cvs2git's rather baroque command-line \ninterface.  Michael Haggerty has accepted that patch.  Soon that line will\nlook like this:\n\n     return \"cvs2git --quiet --quiet {0} {1}\".format(self.opts, self.modulepath)\n\nOlder versions of cvs2git will terminate cleanly with an error message \nwhen called this way.\n\n> > +        elif opt == '-o':\n> > +            sys.stderr.write(\"git cvsimport: -o is no longer supported.\\n\")\n> > +            sys.exit(1)\n> \n> Isn't this a regression?\n\nIt would be if the -o behavior were consistent and decently\ndocumented.  When I tested this option with the Perl version I got no\nresult.  Possibly my usage was incorrect; if anyone can be found who\nactually understands how it's supposed to work in detail and will tell\nme, I can probably support it.\n\n> > +        elif opt in ('-m', '-M'):\n> > +            sys.stderr.write(\"git cvsimport: -m and -M are no longer supported: use reposurgeon instead.\\n\")\n> > +            sys.exit(1)\n> \n> I wonder if it is better to ignore these options with a warning but\n> still let the command continue; cvsps-3.x was supposed to get merges\n> right without the help of these ad-hoc options, no?\n\nSorry, I don't know where you got that idea. I don't think the general merge\ndetection that would need is possible even in principle.\n\n> Otherwise it looks like a regression to me.\n\nThere are two reasons -m and -M aren't supported.\n\nOne is implementation-level.  The wrapper script no longer deals with\nindividual files or changesets or branches; it relies on the\nconversion engine to do all that.  (As it should - the old design was\na mess with lots of stuff being done at the wrong level.)  But the\nconversion engines don't implement -m or -M, and aren't ever going to\n(see next paragraph).\n\nThe other is a design-level problem - these options were a bad idea to\nbegin with.  In earlier list mail I said\n\n    An example of the batchiness mistake close to home is the -m and -M\n    options in the old version of cvsimport.  It takes human judgment\n    looking at the whole commit DAG in gitspace to decide what merge\n    points would best express the (as you say, sometimes ambiguous) CVS\n    history - what's needed is a scalpel and sutures in a surgeon's hand,\n    not a regexp hammer.\n\nOne specific problem with the regexp hammer is false-positive matches\nleading to unintended merges.\n\nThat's why I won't implement these in cvsps or parsecvs. Instead I've\njust added DAG visualization via graphviz in reposurgeon, so a human\ncan quickly see candidate merges in the visualization and do them by\nhand.  This is better and safer.\n\n> Having the code to die when it sees options the rewritten version\n> does not yet support before it calls the fallback makes the fallback\n> much less effective, no?\n\nOnly to the extent that -o/-m/-M are really important, which I doubt.\nBut that might be fixable, and I'll put it on the to-do list.\n\n> Not very impressed (yet).  The advertised \"fix major bugs\" sounds\n> more like \"trade major bugs with different ones with a couple of\n> feature removals\" at this point.\n\nIf you think that, you have failed to understand just how broken and\ndangerous the old combination is.  None of the details you've called\nout are \"major\" by any stretch of the imagination compared to it\nsilently botching the translation of repositories.\n\nAlso bear in mind that leaving the old Perl code in place is not going\nto be a viable option for more than a few months out.  As cvsps-3.x\npropagates to the distros what you have is going to stop even its\ncurrent pretense of working.\n\nFinally...my own purposes are fulfilled by having CVS exporters that can\ndo a decent job of front-ending for reposurgeon. Rewriting git's\nwrapper script was extra work that I took on only because I wanted to\nbe friendly to the git project, *but*... \n\n...there is a limit to the amount of what I consider pointless\nhoop-jumping that friendliness will buy you, and the 2.x fallback eas\nalready pushing that limit.  Tread a little more gently, Junio; I've\nput in a lot of hard, boring work on git-cvsimport over the last two\nweeks when I would rather have been doing other things, and my\npatience for being nit-picked without appreciation or reward has a\ncorrespondingly low limit.  We'll both be happier if you don't reach\nit.\n-- \n\t\t<a href=\"http://www.catb.org/~esr/\">Eric S. Raymond</a>\n"},{"id":"206552","messageId":"7vr4lrbkcs.fsf@alter.siamese.dyndns.org","threadId":"32585","inReplyTo":"20130111185818.GA30398@thyrsus.com","subject":"Re: [PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-11T19:17:07Z","receivedAt":"2013-01-11T19:17:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Eric S. Raymond\" <esr@thyrsus.com> writes:\n\n> Junio C Hamano <gitster@pobox.com>:\n>> I think the prevalent style in this script is to write \"print\"\n>> without parentheses:\n>> \n>> \tprint STDERR \"msg\\n\";\n>\n> That can be easily fixed.\n>\n>> This looks lazy and unsafe quoting.  Is there anything that makes\n>> sure repository path does not contain a single quote?\n>\n> No. But...wait, checking...the Perl code didn't have the analogous\n> check, so there's no increased vulnerability here.  I'll put it on the\n> to-do list for after I ship parsecvs.\n\nI checked before I sent that review, and as far as I could tell, it\nwas fairly consistently avoiding the lazy and insecure forms, e.g.\n\n\tsystem(\"com mand \" . $param);\n\topen($fh, \"com mand \" . $param . \" |\"); while (<$fh>)\t{ ... }\n\nbut used the more sequre list form, e.g.\n\n\tsystem(qw(com mand), $param);\n        open($fh, \"-|\", qw(com mand), $param); while (<$fh>)\t{ ... }\n\nBut of course there may be some places that were careless that I\ndidn't spot (and previous reviewers of the current cvsimport\ndidn't).\n"},{"id":"206553","messageId":"7vmwwfbjv3.fsf@alter.siamese.dyndns.org","threadId":"32585","inReplyTo":"20130111185818.GA30398@thyrsus.com","subject":"Re: [PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-11T19:27:44Z","receivedAt":"2013-01-11T19:27:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Eric S. Raymond\" <esr@thyrsus.com> writes:\n\n> Junio C Hamano <gitster@pobox.com>:\n> ...\n> The other is a design-level problem - these options were a bad idea to\n> begin with.  In earlier list mail I said\n>\n>     An example of the batchiness mistake close to home is the -m and -M\n>     options in the old version of cvsimport.  It takes human judgment\n>     looking at the whole commit DAG in gitspace to decide what merge\n>     points would best express the (as you say, sometimes ambiguous) CVS\n>     history - what's needed is a scalpel and sutures in a surgeon's hand,\n>     not a regexp hammer.\n>\n> One specific problem with the regexp hammer is false-positive matches\n> leading to unintended merges.\n\nYeah, it is OK to _discourage_ its use, but to me it looks like that\nthe above is a fairly subjective policy decision, not something I\nshould let you impose on the users of the old cvsimport, which you\ndo not seem to even treat as your users.\n\n>> Having the code to die when it sees options the rewritten version\n>> does not yet support before it calls the fallback makes the fallback\n>> much less effective, no?\n>\n> Only to the extent that -o/-m/-M are really important, which I doubt.\n> But that might be fixable, and I'll put it on the to-do list.\n>\n>> Not very impressed (yet).  The advertised \"fix major bugs\" sounds\n>> more like \"trade major bugs with different ones with a couple of\n>> feature removals\" at this point.\n>\n> If you think that, you have failed to understand just how broken and\n> dangerous the old combination is.  None of the details you've called\n> out are \"major\" by any stretch of the imagination compared to it\n> silently botching the translation of repositories.\n\nThe \"major\" in my sentence was from your description (the bugs you\nfixed), and not about the new ones you still have in this draft.  I\ndid not mean to say that you are trading fixes to \"major\" bugs with\ndifferent \"major\" bugs.\n\nInsecure quoting of parameters is much easier to fix; it does need\nto be addressed, though.\n\nIt is just that looking at the state of the patch as submitted left\nme with a feeling that this topic needs a lot more time to mature\nthan I previously was led to believe by your earlier messages, which\nmade me someaht sad.\n"},{"id":"206574","messageId":"20130112050431.GA31727@thyrsus.com","threadId":"32585","inReplyTo":"7vmwwfbjv3.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs","fromName":"Eric S. Raymond","fromEmail":"esr@thyrsus.com","sentAt":"2013-01-12T05:04:31Z","receivedAt":"2013-01-12T05:04:31Z","isPatch":true,"sender":{"key":"esr@thyrsus.com","avatar":"https://avatars.githubusercontent.com/u/727961?v=4"},"body":"Junio C Hamano <gitster@pobox.com>:\n> Yeah, it is OK to _discourage_ its use, but to me it looks like that\n> the above is a fairly subjective policy decision, not something I\n> should let you impose on the users of the old cvsimport, which you\n> do not seem to even treat as your users.\n\nEr.  You still don't seem to grasp the fundamentals of this\nsituation. I'm not imposing any damn thing on the users.  What's\nimposing is the fact that cvsps-2.x and the Perl cvsimport are both\nindividually and collectively *broken right now*, and within a few\nmonths the Perl git-cvsimport is going to cease even pretending to\nwork.  I'm trying to *fix that problem* as best I can, fixing it\nrequired two radical rewrites, and criticizing me for not emulating\nevery last detail and misfeature immediately is every bit as pointless\nand annoying as arguing about the fabric on the deck chairs while the\nship is sinking.\n\nTo put it bluntly, you should be grateful to be getting back any\nfunctionality at all - because the alternative is that the Perl\ngit-cvsimport will hang out in your tree as a dead piece of cruft.\nYour choice is between making it easy for me replace it with minimum\ndisruption now and hoping for someone else to replace it months from\nnow after you've had a bunch of unhappy users bitching at you.\n\nSo let me be more direct.  I think the -M and -m options are\nsufficiently bad ideas that I am *not willing* to put in the quite\nlarge amount of effort that would be required to implement them in cvsps\nor parsecvs.  That would be a bad use of my time.\n\nThis is not the case with -o; that might be a good idea if I\nunderstood it. This is also not like the 2.x fallback; I thought that\nwas a bad idea (because it would be better for users that the\ncombination break in an obvious way than continue breaking in a silent\none), but it was a small enough effort that I was willing to do it\nanyway to keep the git maintainer happy. The effort to fix the quoting\nbugs is even easier for me to justify; they are actual bugs.\n\nThose are my engineering judgments; go ahead and call them\n\"subjective\" if you like, but neither the facts nor my judgment will\nchange on that account.\n\n> The \"major\" in my sentence was from your description (the bugs you\n> fixed), and not about the new ones you still have in this draft.  I\n> did not mean to say that you are trading fixes to \"major\" bugs with\n> different \"major\" bugs.\n\nOK, thank you.  In the future I will try to bear in mind that English\nis not your primary language when I evaluate statements that seems a bit\noffensive.\n\nSo what's your next bid? Note that you can't increase my friction and\nhassle costs much more before I give up and let you deal with the\nconsequences without me. I want to do the right thing, but I have\nmore other projects clamoring for my attention than you could easily\nguess.  I need to get git-cvsimport *finished* and off my hands -\nI may already have given it more time than I really should have.\n\nSo give me your minimum list of deliverables before you'll merge,\nplease, and then stick to it.  I assume fixes for the quoting bugs\nwill be on that list.\n-- \n\t\t<a href=\"http://www.catb.org/~esr/\">Eric S. Raymond</a>\n"},{"id":"206575","messageId":"7v62339du4.fsf@alter.siamese.dyndns.org","threadId":"32585","inReplyTo":"1357875152-19899-1-git-send-email-gitster@pobox.com","subject":"Re: [PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-12T05:20:51Z","receivedAt":"2013-01-12T05:20:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"I cloned git://gitorious.org/cvsps/cvsps.git and installed cvsps-3.7\nat c2ce6cc (More fun with test loads, sigh.  Timezones suck.,\n2013-01-09) earlier on my $PATH, and tried to run t96xx series with\nthis patch applied on top of Git 1.8.1.\n\nThe first thing I noticed was that all the tests were skipped.\nA patch to t/lib-cvs.sh might be sufficient, \n\n--------------------- >8 -------------------------\ndiff --git a/t/lib-cvs.sh b/t/lib-cvs.sh\nindex 44263ad..423953f 100644\n--- a/t/lib-cvs.sh\n+++ b/t/lib-cvs.sh\n@@ -17,6 +17,8 @@ cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`\n case \"$cvsps_version\" in\n 2.1 | 2.2*)\n \t;;\n+3.*)\n+\t;;\n '')\n \tskip_all='skipping cvsimport tests, cvsps not found'\n \ttest_done\n--------------------- 8< -------------------------\n\nbut I didn't check more than \"now it seems not to skip them\".\nAnd here is what I got:\n\n--------------------- >8 -------------------------\nTest Summary Report\n-------------------\nt9600-cvsimport.sh              (Wstat: 256 Tests: 15 Failed: 9)\n  Failed tests:  4-6, 8-9, 11-13, 15\n  Non-zero exit status: 1\nt9601-cvsimport-vendor-branch.sh (Wstat: 256 Tests: 9 Failed: 8)\n  Failed tests:  1-4, 6-9\n  Non-zero exit status: 1\nt9602-cvsimport-branches-tags.sh (Wstat: 256 Tests: 11 Failed: 5)\n  Failed tests:  1-3, 7, 9\n  Non-zero exit status: 1\nt9604-cvsimport-timestamps.sh   (Wstat: 256 Tests: 2 Failed: 2)\n  Failed tests:  1-2\n  Non-zero exit status: 1\nFiles=5, Tests=38,  5 wallclock secs ( 0.05 usr  0.01 sys +  0.49\n  cusr  0.16 csys =  0.71 CPU)\nResult: FAIL\n--------------------- 8< -------------------------\n\nA funny thing was that without cvsps-3.7 on $PATH (which means I am\ngetting distro packaged cvsps 2.1), I got identical errors.  Looking\nat the log message, it seems that you meant to remove t960[123], so\nperhaps the patch simply forgot to remove 9601 and 9602?\n\nAs neither test runs \"git cvsimport\" with -o/-m/-M options, ideally\nwe should be able to pass them with and without having cvsps-3.x.\nNot passing them without cvsps-3.x would mean that the fallback mode\nof rewritten cvsimport is not working as expected. Not passing them\nwith cvsps-3.x may mean the tests were expecting a wrong conversion\nresult, or they uncover bugs in the replacement cvsimport.\n\nt9600 fails with \"-a is no longer supported\", even without having\ncvsps-3.x on the $PATH (i.e. attempting to use the fallback).  I\nwonder if this is an option the updated cvsimport would want to\nsimply ignore?\n\nIt is a way to tell the old cvsps/cvsimport to disable its\nheuristics to ignore commits made within the last 10 minutes (this\nis done in the hope of waiting for the per-file nature of CVS\ncommits to stabilize, IIUC); the user tells the command that he\nknows that the CVS repository is now quiescent and it is safe to\nimport the whole thing.\n\nIf the updated cvsps can identify the changeset more reliably and it\nno longer needs \"-a\" option, it may be more helpful to the users to\nmigrate their script if it allowed, warned and then ignored the\noption.  It certainly would help sharing of this test script between\nruns that use the old and new cvsps as backends.\n\nt9601 (after resurrecting the t/t9601/cvsroot directory) fails in an\ninteresting way.\n\n--------------------- >8 -------------------------\n$ sh t9601-cvsimport-vendor-branch.sh -i -v\nInitialized empty Git repository in /git/git.build/t/trash directory.t9601-cvsimport-vendor-branch/.git/\nexpecting success:\n\n        git cvsimport -C module-git module\n\nTraceback (most recent call last):\n  File \"/git/git.build/git-cvsimport\", line 262, in <module>\n    subprocess.check_output(\"cvsps -V 2>/dev/null\", shell=True)\nAttributeError: 'module' object has no attribute 'check_output'\nnot ok - 1 import a module with a vendor branch\n--------------------- 8< -------------------------\n\nApparently, the copy of \"subprocess.py\" I have does not give us the\ncheck_output thing:\n\n--------------------- >8 -------------------------\n$ python\nPython 2.6.6 (r266:84292, Dec 26 2010, 22:31:48)\n[GCC 4.4.5] on linux2\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n>>> import subprocess\n>>> dir(subprocess)\n['CalledProcessError', 'MAXFD', 'PIPE', 'Popen', 'STDOUT', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '_active', '_cleanup', '_demo_posix', '_demo_windows', '_eintr_retry_call', 'call', 'check_call', 'errno', 'fcntl', 'gc', 'list2cmdline', 'mswindows', 'os', 'pickle', 'select', 'signal', 'sys', 'traceback', 'types']\n--------------------- 8< -------------------------\n\nThe story is the same for t9602 and t9603 (again after resurrecting\nthe necessary files).\n\nhttp://docs.python.org/2/library/subprocess.html tells me that\ncheck_output has first become available in 2.7.\n\nSo... does this mean that we now set the minimum required version of\nPython to 2.7?  I dunno.\n"},{"id":"206576","messageId":"7v1udr9d0g.fsf_-_@alter.siamese.dyndns.org","threadId":"32585","inReplyTo":"7v62339du4.fsf@alter.siamese.dyndns.org","subject":"[PATCH] t/t960[123]: remove leftover scripts","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-12T05:38:39Z","receivedAt":"2013-01-12T05:38:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The rewrite patch was supposed to remove these scripts, but somehow\nwe ended up removing only the supporting files for them but not the\ntest script themselves.  Remove them for real.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n * I'll queue this on top of your patch together with a few fix-up\n   patches from Chris Rorvick.  This may have been caused by your\n   private patch e-mail mangled somewhere between us before I resent\n   your patch, or perhaps you simply may have forgot to remove them,\n   but at this point I do not really care where these deletions were\n   lost---the only thing I care about is to make sure that you\n   _meant_ to remove them in your patch (i.e. if you didn't mean to,\n   then I am further breaking the tests in a way you did not intend\n   to), so I'd appreciate either \"Yup, these three files should be\n   removed\", or \"No, they should stay; removal of their supporting\n   data is no longer needed\" from you (I and this patch expect the\n   former, of course).\n\n   By the way, Chris, we'll need your Sign-off on the three paches\n   (t/lib-cvs.sh fix to allow cvsps v3, t9600 fix and t9604 fix).\n\n t/t9601-cvsimport-vendor-branch.sh | 85 --------------------------------------\n t/t9602-cvsimport-branches-tags.sh | 78 ----------------------------------\n t/t9603-cvsimport-patchsets.sh     | 39 -----------------\n 3 files changed, 202 deletions(-)\n delete mode 100755 t/t9601-cvsimport-vendor-branch.sh\n delete mode 100755 t/t9602-cvsimport-branches-tags.sh\n delete mode 100755 t/t9603-cvsimport-patchsets.sh\n\ndiff --git a/t/t9601-cvsimport-vendor-branch.sh b/t/t9601-cvsimport-vendor-branch.sh\ndeleted file mode 100755\nindex 827d39f..0000000\n--- a/t/t9601-cvsimport-vendor-branch.sh\n+++ /dev/null\n@@ -1,85 +0,0 @@\n-#!/bin/sh\n-\n-# Description of the files in the repository:\n-#\n-#    imported-once.txt:\n-#\n-#       Imported once.  1.1 and 1.1.1.1 should be identical.\n-#\n-#    imported-twice.txt:\n-#\n-#       Imported twice.  HEAD should reflect the contents of the\n-#       second import (i.e., have the same contents as 1.1.1.2).\n-#\n-#    imported-modified.txt:\n-#\n-#       Imported, then modified on HEAD.  HEAD should reflect the\n-#       modification.\n-#\n-#    imported-modified-imported.txt:\n-#\n-#       Imported, then modified on HEAD, then imported again.\n-#\n-#    added-imported.txt,v:\n-#\n-#       Added with 'cvs add' to create 1.1, then imported with\n-#       completely different contents to create 1.1.1.1, therefore the\n-#       vendor branch was never the default branch.\n-#\n-#    imported-anonymously.txt:\n-#\n-#       Like imported-twice.txt, but with a vendor branch whose branch\n-#       tag has been removed.\n-\n-test_description='git cvsimport handling of vendor branches'\n-. ./lib-cvs.sh\n-\n-setup_cvs_test_repository t9601\n-\n-test_expect_success PERL 'import a module with a vendor branch' '\n-\n-\tgit cvsimport -C module-git module\n-\n-'\n-\n-test_expect_success PERL 'check HEAD out of cvs repository' 'test_cvs_co master'\n-\n-test_expect_success PERL 'check master out of git repository' 'test_git_co master'\n-\n-test_expect_success PERL 'check a file that was imported once' '\n-\n-\ttest_cmp_branch_file master imported-once.txt\n-\n-'\n-\n-test_expect_failure PERL 'check a file that was imported twice' '\n-\n-\ttest_cmp_branch_file master imported-twice.txt\n-\n-'\n-\n-test_expect_success PERL 'check a file that was imported then modified on HEAD' '\n-\n-\ttest_cmp_branch_file master imported-modified.txt\n-\n-'\n-\n-test_expect_success PERL 'check a file that was imported, modified, then imported again' '\n-\n-\ttest_cmp_branch_file master imported-modified-imported.txt\n-\n-'\n-\n-test_expect_success PERL 'check a file that was added to HEAD then imported' '\n-\n-\ttest_cmp_branch_file master added-imported.txt\n-\n-'\n-\n-test_expect_success PERL 'a vendor branch whose tag has been removed' '\n-\n-\ttest_cmp_branch_file master imported-anonymously.txt\n-\n-'\n-\n-test_done\ndiff --git a/t/t9602-cvsimport-branches-tags.sh b/t/t9602-cvsimport-branches-tags.sh\ndeleted file mode 100755\nindex e1db323..0000000\n--- a/t/t9602-cvsimport-branches-tags.sh\n+++ /dev/null\n@@ -1,78 +0,0 @@\n-#!/bin/sh\n-\n-# A description of the repository used for this test can be found in\n-# t9602/README.\n-\n-test_description='git cvsimport handling of branches and tags'\n-. ./lib-cvs.sh\n-\n-setup_cvs_test_repository t9602\n-\n-test_expect_success PERL 'import module' '\n-\n-\tgit cvsimport -C module-git module\n-\n-'\n-\n-test_expect_success PERL 'test branch master' '\n-\n-\ttest_cmp_branch_tree master\n-\n-'\n-\n-test_expect_success PERL 'test branch vendorbranch' '\n-\n-\ttest_cmp_branch_tree vendorbranch\n-\n-'\n-\n-test_expect_failure PERL 'test branch B_FROM_INITIALS' '\n-\n-\ttest_cmp_branch_tree B_FROM_INITIALS\n-\n-'\n-\n-test_expect_failure PERL 'test branch B_FROM_INITIALS_BUT_ONE' '\n-\n-\ttest_cmp_branch_tree B_FROM_INITIALS_BUT_ONE\n-\n-'\n-\n-test_expect_failure PERL 'test branch B_MIXED' '\n-\n-\ttest_cmp_branch_tree B_MIXED\n-\n-'\n-\n-test_expect_success PERL 'test branch B_SPLIT' '\n-\n-\ttest_cmp_branch_tree B_SPLIT\n-\n-'\n-\n-test_expect_failure PERL 'test tag vendortag' '\n-\n-\ttest_cmp_branch_tree vendortag\n-\n-'\n-\n-test_expect_success PERL 'test tag T_ALL_INITIAL_FILES' '\n-\n-\ttest_cmp_branch_tree T_ALL_INITIAL_FILES\n-\n-'\n-\n-test_expect_failure PERL 'test tag T_ALL_INITIAL_FILES_BUT_ONE' '\n-\n-\ttest_cmp_branch_tree T_ALL_INITIAL_FILES_BUT_ONE\n-\n-'\n-\n-test_expect_failure PERL 'test tag T_MIXED' '\n-\n-\ttest_cmp_branch_tree T_MIXED\n-\n-'\n-\n-\n-test_done\ndiff --git a/t/t9603-cvsimport-patchsets.sh b/t/t9603-cvsimport-patchsets.sh\ndeleted file mode 100755\nindex 52034c8..0000000\n--- a/t/t9603-cvsimport-patchsets.sh\n+++ /dev/null\n@@ -1,39 +0,0 @@\n-#!/bin/sh\n-\n-# Structure of the test cvs repository\n-#\n-# Message   File:Content         Commit Time\n-# Rev 1     a: 1.1               2009-02-21 19:11:43 +0100\n-# Rev 2     a: 1.2    b: 1.1     2009-02-21 19:11:14 +0100\n-# Rev 3               b: 1.2     2009-02-21 19:11:43 +0100\n-#\n-# As you can see the commit of Rev 3 has the same time as\n-# Rev 1 this leads to a broken import because of a cvsps\n-# bug.\n-\n-test_description='git cvsimport testing for correct patchset estimation'\n-. ./lib-cvs.sh\n-\n-setup_cvs_test_repository t9603\n-\n-test_expect_failure 'import with criss cross times on revisions' '\n-\n-    git cvsimport -p\"-x\" -C module-git module &&\n-    (cd module-git &&\n-        git log --pretty=format:%s > ../actual-master &&\n-        git log A~2..A --pretty=\"format:%s %ad\" -- > ../actual-A &&\n-        echo \"\" >> ../actual-master &&\n-\techo \"\" >> ../actual-A\n-    ) &&\n-    echo \"Rev 4\n-Rev 3\n-Rev 2\n-Rev 1\" > expect-master &&\n-    test_cmp actual-master expect-master &&\n-\n-    echo \"Rev 5 Branch A Wed Mar 11 19:09:10 2009 +0000\n-Rev 4 Branch A Wed Mar 11 19:03:52 2009 +0000\" > expect-A &&\n-    test_cmp actual-A expect-A\n-'\n-\n-test_done\n-- \n1.8.1.421.g6236851\n"},{"id":"206581","messageId":"CAEUsAPayhLXrMR9K4KTkV+yCVjV+TzTi48ytj1NfcAgOEhCN+g@mail.gmail.com","threadId":"32585","inReplyTo":"7v1udr9d0g.fsf_-_@alter.siamese.dyndns.org","subject":"Re: [PATCH] t/t960[123]: remove leftover scripts","fromName":"Chris Rorvick","fromEmail":"chris@rorvick.com","sentAt":"2013-01-12T06:06:30Z","receivedAt":"2013-01-12T06:06:30Z","isPatch":true,"sender":{"key":"chris@rorvick.com","avatar":"https://avatars.githubusercontent.com/u/824726?v=4"},"body":"On Fri, Jan 11, 2013 at 11:38 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>    By the way, Chris, we'll need your Sign-off on the three paches\n>    (t/lib-cvs.sh fix to allow cvsps v3, t9600 fix and t9604 fix).\n\nSure.  I was just maintaining them for myself but thought I'd share\nwhen I saw the follow-up patch.  Didn't think to amend them.\n\nChris\n"},{"id":"206591","messageId":"7vip72iykx.fsf_-_@alter.siamese.dyndns.org","threadId":"32585","inReplyTo":"7v62339du4.fsf@alter.siamese.dyndns.org","subject":"[PATCH] t/lib-cvs: cvsimport no longer works without Python >= 2.7","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-12T08:40:14Z","receivedAt":"2013-01-12T08:40:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The new cvsimport requires at least Python 2.7 to work; do not fail\nthe cvsimport tests on platforms without one.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n Junio C Hamano <gitster@pobox.com> writes:\n\n > http://docs.python.org/2/library/subprocess.html tells me that\n > check_output has first become available in 2.7.\n >\n > So... does this mean that we now set the minimum required version of\n > Python to 2.7?  I dunno.\n\n Even if we were to rip out the fallback code that uses the 2.7-only\n subprocess.check_output() on \"cvsps -V\", the function is also used\n for doing the real work interacting with cvsps-3.x, so I think this\n patch will be necessary.  Unless new cvsimport is tweaked not to\n use the method, that is.\n\n A suggestion for a better alternative is of course very much\n appreciated.\n\n I do not want to keep pushing integration results that do not pass\n tests even for myself for too many integration cycles, so I'll keep\n this patch at the tip of the topic, at least for now.\n\n t/lib-cvs.sh | 15 +++++++++++++++\n 1 file changed, 15 insertions(+)\n\ndiff --git a/t/lib-cvs.sh b/t/lib-cvs.sh\nindex b55e861..4e890ea 100644\n--- a/t/lib-cvs.sh\n+++ b/t/lib-cvs.sh\n@@ -27,6 +27,21 @@ case \"$cvsps_version\" in\n \t;;\n esac\n \n+if ! test_have_prereq PYTHON\n+then\n+\tskipall='skipping cvsimport tests, no python'\n+\ttest_done\n+fi\n+\n+python -c '\n+import sys\n+if sys.hexversion < 0x02070000:\n+\tsys.exit(1)\n+' || {\n+\tskip_all='skipping cvsimport tests, python too old (< 2.7)'\n+\ttest_done\n+}\n+\n setup_cvs_test_repository () {\n \tCVSROOT=\"$(pwd)/.cvsroot\" &&\n \tcp -r \"$TEST_DIRECTORY/$1/cvsroot\" \"$CVSROOT\" &&\n-- \n1.8.1.421.g6236851\n"},{"id":"206601","messageId":"50F17DB0.2050802@alum.mit.edu","threadId":"32585","inReplyTo":"1357875152-19899-1-git-send-email-gitster@pobox.com","subject":"Re: [PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-01-12T15:13:52Z","receivedAt":"2013-01-12T15:13:52Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 01/11/2013 04:32 AM, Junio C Hamano wrote:\n> From: \"Eric S. Raymond\" <esr@thyrsus.com>\n> \n> The combination of git-cvsimport and cvsps had serious problems.\n\nAgreed.\n\n> [...]\n> This patch also removes Michael Haggerty's git-cvsimport tests\n> (t960[123]) from the git tree.  These are actually conversion-engine\n> tests and have been merged into a larger cvsps test suite, which I\n> intend to spin out into a general CVS-lifting test that can also be\n> applied to utilities such as cvs2git and parsecvs.  The t9604 test\n> will move in a future patch, when I likewise have it integrated\n> into the general test suite.\n> \n> The following known bug has not been fixed: \"If any files were ever\n> \"cvs import\"ed more than once (e.g., import of more than one vendor\n> release) the HEAD contains the wrong content.\" However, cvsps now\n> emits a warning in this case. There is also one pathological tagging\n> case that was successful in the former t9602 test that now fails\n> (with a warning).\n> \n> I plan to address these problems. This patch at least gets the\n> cvsps-3.x/git-cvsimport combination to a state that is not too\n> broken to ship - that is, in all failure cases known to me it\n> now emits useful warnings rather than silently botching the\n> import.\n\nI don't understand the logic of removing the cvsimport tests, at least\nnot at this time.  It is true that the tests mostly ensure that the\nconversion engine is working correctly, especially with your new version\nof cvsps.  But I think the git project, by implicitly endorsing the use\nof cvsps, has some responsibility to verify that the combination cvsps +\ngit-cvsimport continues to work and to document any known breakages via\nits test suite.\n\nOtherwise, how do we know that cvsps currently works with git-cvsimport?\n (OK, you claim that it does, but in the next breath you admit that\nthere is a new failure in \"one pathological tagging case\".)  How can we\nunderstand its strengths/weaknesses?  How can we gain confidence that it\nworks on different platforms?  How will we find out if a future versions\nof cvsps stops working (e.g., because of a breakage or a\nnon-backwards-compatible change)?\n\nNormally one would expect an improvement like this to be combined with\npatches that turn test expected failures into expected successes, not to\nrip out the very tests that establish the correctness of the change that\nis being proposed!\n\n\nLet me describe what I would consider to be the optimum state of the\ntest suite.  Maybe your idea of \"optimum\" differs from mine, or maybe\nthe optimum is unrealistic due to lack of resources or for some other\nreason.  But if so, let's explicitly spell out why we are deviating from\nwhatever optimum we define.\n\n* The old tests should be retained (and possibly new tests added to show\noff your improvements).\n\n* There should be a way for users to choose which cvsps executable to\nuse when running test suite.  (In the future, the selection might be\nexpanded to cover altogether different conversion engines.)\n\n* The tests should determine which version of cvsps has been selected\n(e.g., by running \"cvsps --version\").\n\n* The individual tests should be marked expected success/expected\nfailure based on the selected version of cvsps; in other words, some\ntests might be marked \"expected failure\" if cvsps 2.x is being used but\n\"expected success\" if cvsps 3.x is being used.\n\n\nRegarding your claim that \"within a few months the Perl git-cvsimport is\ngoing to cease even pretending to work\": It might be that the old\ngit-cvsimport will stop working *for people who upgrade to cvsps 3.x*.\nBut it is not realistic to expect people to synchronize their git and\ncvsps version upgrades.  It is even quite possible that this or that\nLinux distribution will package incompatible versions of the two packages.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"206602","messageId":"50F180E8.8010907@alum.mit.edu","threadId":"32585","inReplyTo":"7vip72iykx.fsf_-_@alter.siamese.dyndns.org","subject":"Re: [PATCH] t/lib-cvs: cvsimport no longer works without Python >= 2.7","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-01-12T15:27:36Z","receivedAt":"2013-01-12T15:27:36Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"I have the feeling I'm only seeing one side of this conversation...\n\nOn 01/12/2013 09:40 AM, Junio C Hamano wrote:\n> The new cvsimport requires at least Python 2.7 to work; do not fail\n> the cvsimport tests on platforms without one.\n> \n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n> \n>  Junio C Hamano <gitster@pobox.com> writes:\n> \n>  > http://docs.python.org/2/library/subprocess.html tells me that\n>  > check_output has first become available in 2.7.\n>  >\n>  > So... does this mean that we now set the minimum required version of\n>  > Python to 2.7?  I dunno.\n\nIt would be unfortunate to set the minimum Python version to 2.7 if \"git\ncvsimport\" is considered an integral part of git.\n\n>  Even if we were to rip out the fallback code that uses the 2.7-only\n>  subprocess.check_output() on \"cvsps -V\", the function is also used\n>  for doing the real work interacting with cvsps-3.x, so I think this\n>  patch will be necessary.  Unless new cvsimport is tweaked not to\n>  use the method, that is.\n> \n>  A suggestion for a better alternative is of course very much\n>  appreciated.\n\nIf the only reason to require Python 2.7 is subprocess.check_output(),\nit would be easy to reimplement it (it is only 12 lines of\nstraightforward code, plus a few lines to define the exception type\nCalledProcessError).  According to [1], the Python license is\nGPL-compatible; therefore these lines could even be copied into\ngit-cvsimport.\n\nMichael\n\n[1] http://www.gnu.org/licenses/license-list.html#Python\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"206603","messageId":"20130112154719.GA3270@thyrsus.com","threadId":"32585","inReplyTo":"7v62339du4.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs","fromName":"Eric S. Raymond","fromEmail":"esr@thyrsus.com","sentAt":"2013-01-12T15:47:20Z","receivedAt":"2013-01-12T15:47:20Z","isPatch":true,"sender":{"key":"esr@thyrsus.com","avatar":"https://avatars.githubusercontent.com/u/727961?v=4"},"body":"Junio C Hamano <gitster@pobox.com>:\n> And here is what I got:\n\nHm. In my version of these tests, I only have one regression from the\nold combo (in the pathological tags test, t9602).  You're seeing more\nbreakage than that, obviously.\n\n> A funny thing was that without cvsps-3.7 on $PATH (which means I am\n> getting distro packaged cvsps 2.1), I got identical errors.\n\nThat suggests that something in your test setup has gone bad and is\nintroducing spurious errors. Which would be consistent with the above.\n\n>                                                     Looking\n> at the log message, it seems that you meant to remove t960[123], so\n> perhaps the patch simply forgot to remove 9601 and 9602?\n\nYes.\n \n> As neither test runs \"git cvsimport\" with -o/-m/-M options, ideally\n> we should be able to pass them with and without having cvsps-3.x.\n> Not passing them without cvsps-3.x would mean that the fallback mode\n> of rewritten cvsimport is not working as expected. Not passing them\n> with cvsps-3.x may mean the tests were expecting a wrong conversion\n> result, or they uncover bugs in the replacement cvsimport.\n\nThat's possible, but seems unlikely.  Because the new cvsimport is\nsuch a thin wrapper around the conversion engine, bugs in it should\nlead to obvious crashes or failure to run the engine rather than the \nsort of conversion error the t960* tests are designed to check.  Really\nall it does is assemble options to pass to the conversion engines.\n\nMy test strategy is aimed at the engine, not the wrapper. I took the\nrepos from t960*  and wrote a small Python framework to check the same \nassertions as the git-tree tests do, but using the engine.  For example,\nhere's how my t9602 looks:\n\nimport os, cvspstest\n\ncc = cvspstest.ConvertComparison(\"t9602\", \"module\")\ncc.cmp_branch_tree(\"test of branch\", \"master\", True)\ncc.cmp_branch_tree(\"test of branch\", \"vendorbranch\", True)\ncc.cmp_branch_tree(\"test of branch\", \"B_FROM_INITIALS\", False)\ncc.cmp_branch_tree(\"test of branch\", \"B_FROM_INITIALS_BUT_ONE\", False)\ncc.cmp_branch_tree(\"test of branch\", \"B_MIXED\", False)\ncc.cmp_branch_tree(\"test of branch\", \"B_SPLIT\", True)\ncc.cmp_branch_tree(\"test of tag\", \"vendortag\", False)\n# This is the only test new cvsps fails that old git-cvsimport passed.\ncc.cmp_branch_tree(\"test of tag\", \"T_ALL_INITIAL_FILES\", True)\ncc.cmp_branch_tree(\"test of tag\", \"T_ALL_INITIAL_FILES_BUT_ONE\", False)\ncc.cmp_branch_tree(\"test of tag\", \"T_MIXED\", False)\ncc.cleanup()\n \n> t9600 fails with \"-a is no longer supported\", even without having\n> cvsps-3.x on the $PATH (i.e. attempting to use the fallback).  I\n> wonder if this is an option the updated cvsimport would want to\n> simply ignore?\n\nProbably.  But I don't think you should keep these tests in the git tree.\nThat wasn't a great idea even when you were supporting just one engine;\nwith two (and soon three) it's going to be just silly.  Let sanity-checking\nthe engines be *my* problem, since I have to do it anyway.\n\n(I'm working towards the generalized test suite as fast as I can.  First\nresults probably in four days or so.)\n\n> It is a way to tell the old cvsps/cvsimport to disable its\n> heuristics to ignore commits made within the last 10 minutes (this\n> is done in the hope of waiting for the per-file nature of CVS\n> commits to stabilize, IIUC); the user tells the command that he\n> knows that the CVS repository is now quiescent and it is safe to\n> import the whole thing.\n\nYes, that's just what -a is supposed to do.  But is should be\nirrelevant for testing - in the test framework CVS is running locally, \nso there's no network lag.\n\n> So... does this mean that we now set the minimum required version of\n> Python to 2.7?  I dunno.\n\nThat would be bad, IMO.  I'll put backporting to 2.6 high on my to-do list.\n-- \n\t\t<a href=\"http://www.catb.org/~esr/\">Eric S. Raymond</a>\n"},{"id":"206606","messageId":"20130112161105.GB3270@thyrsus.com","threadId":"32585","inReplyTo":"50F17DB0.2050802@alum.mit.edu","subject":"Re: [PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs","fromName":"Eric S. Raymond","fromEmail":"esr@thyrsus.com","sentAt":"2013-01-12T16:11:05Z","receivedAt":"2013-01-12T16:11:05Z","isPatch":true,"sender":{"key":"esr@thyrsus.com","avatar":"https://avatars.githubusercontent.com/u/727961?v=4"},"body":"Michael Haggerty <mhagger@alum.mit.edu>:\n> Otherwise, how do we know that cvsps currently works with git-cvsimport?\n> (OK, you claim that it does, but in the next breath you admit that\n> there is a new failure in \"one pathological tagging case\".)  How can we\n> understand its strengths/weaknesses?  How can we gain confidence that it\n> works on different platforms?  How will we find out if a future versions\n> of cvsps stops working (e.g., because of a breakage or a\n> non-backwards-compatible change)?\n\nYou can't.  But in practice the git crew was going to lose that\ncapability anyway simply because the new wrapper will support three\nengines rather than just one.  It's not practical for the git tests to\nhandle that many variant external dependencies.\n\nHowever, there is a solution.\n\nThe solution is for git to test that the wrapper is *generating the\nexpected commands*.  So what the git tree ends up with is conditional\nassurance; the wrapper will do the right thing if the engine it calls\nis working correctly.  I think that's really all the git-tree tests\ncan hope for.\n\nMichael, the engines are my problem and yours - it's *our*\nresponsibility to develop a (hopefully shared) test suite to verify\nthat they convert repos correctly.  I'm working my end as fast as I can;\nI hope to have the test suite factored out of cvsps and ready to check \nmultiple engines by around Wednesday.  I still need to convert t9604,\ntoo.\n\nI have parsecvs working since yesterday, so we really are up to three\nengines.\n\nI have two minor features I need to merge into parsecvs before \nI can start on splitting out the test suite.\n-- \n\t\t<a href=\"http://www.catb.org/~esr/\">Eric S. Raymond</a>\n"},{"id":"206611","messageId":"20130112181633.GB4624@elie.Belkin","threadId":"32585","inReplyTo":"20130112161105.GB3270@thyrsus.com","subject":"Re: [PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2013-01-12T18:16:33Z","receivedAt":"2013-01-12T18:16:33Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi Eric,\n\nEric S. Raymond wrote:\n\n>             But in practice the git crew was going to lose that\n> capability anyway simply because the new wrapper will support three\n> engines rather than just one.  It's not practical for the git tests to\n> handle that many variant external dependencies.\n\nSee the git-blame/git-annotate tests for an example of how the\ntestsuite handles \"variations on a theme\".\n\nIt works fine.\n\nHope that helps,\nJonathan\n"},{"id":"206613","messageId":"20130112182649.GC4624@elie.Belkin","threadId":"32585","inReplyTo":"50F17DB0.2050802@alum.mit.edu","subject":"Re: [PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2013-01-12T18:26:49Z","receivedAt":"2013-01-12T18:26:49Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Michael Haggerty wrote:\n\n> Regarding your claim that \"within a few months the Perl git-cvsimport is\n> going to cease even pretending to work\": It might be that the old\n> git-cvsimport will stop working *for people who upgrade to cvsps 3.x*.\n> But it is not realistic to expect people to synchronize their git and\n> cvsps version upgrades.  It is even quite possible that this or that\n> Linux distribution will package incompatible versions of the two packages.\n\nMoreover, I feel an obligation to point the following out:\n\nIn a hypothetical world where cvsps 3.x simply breaks \"git cvsimport\"\nit is likely that some distributions would just stick to the existing\ncvsps and not upgrade to 3.x.  Maybe that's a wrong choice, but that's\na choice some would make.  An even more likely outcome in that\nhypothetical world is that they would ship it renamed to something\nlike \"cvsps3\" alongside the existing cvsps.  Or they could rename the\nold version to \"cvsps2\".  If we were the last holdout, we could even\nbundle it as compat/cvsps.\n\nSo please do not act as though the cvsps upgrade is a crisis that we\nneed to break ourselves for at threat of no longer working at all.\nThe threat doesn't hold water.\n\nLuckily you have already written patches to make \"git cvsimport\" work\nwith cvsps 3.x, and through your work you are making a better\nargument: \"The new cvsimport + cvsps will work better, at least for\nsome users, than the old tool.\"\n\nJust don't pretend you have the power to force a change for a less\nsensible reason than that!\n\nHope that helps,\nJonathan\n"},{"id":"206701","messageId":"20130113171720.GM4574@serenity.lan","threadId":"32585","inReplyTo":"50F180E8.8010907@alum.mit.edu","subject":"Re: [PATCH] t/lib-cvs: cvsimport no longer works without Python >= 2.7","fromName":"John Keeping","fromEmail":"john@keeping.me.uk","sentAt":"2013-01-13T17:17:20Z","receivedAt":"2013-01-13T17:17:20Z","isPatch":true,"sender":{"key":"john@keeping.me.uk","avatar":"https://avatars.githubusercontent.com/u/1702081?v=4"},"body":"On Sat, Jan 12, 2013 at 04:27:36PM +0100, Michael Haggerty wrote:\n>>  Even if we were to rip out the fallback code that uses the 2.7-only\n>>  subprocess.check_output() on \"cvsps -V\", the function is also used\n>>  for doing the real work interacting with cvsps-3.x, so I think this\n>>  patch will be necessary.  Unless new cvsimport is tweaked not to\n>>  use the method, that is.\n>> \n>>  A suggestion for a better alternative is of course very much\n>>  appreciated.\n> \n> If the only reason to require Python 2.7 is subprocess.check_output(),\n> it would be easy to reimplement it (it is only 12 lines of\n> straightforward code, plus a few lines to define the exception type\n> CalledProcessError).  According to [1], the Python license is\n> GPL-compatible; therefore these lines could even be copied into\n> git-cvsimport.\n\nNote that this has already be done in git_remote_helpers.util.  Is there\nany reason not to just reference that?\n\n\nJohn\n"},{"id":"206714","messageId":"7v8v7wiv3a.fsf@alter.siamese.dyndns.org","threadId":"32585","inReplyTo":"20130112182649.GC4624@elie.Belkin","subject":"Re: [PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-13T22:20:09Z","receivedAt":"2013-01-13T22:20:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan Nieder <jrnieder@gmail.com> writes:\n\n> Michael Haggerty wrote:\n>\n>> Regarding your claim that \"within a few months the Perl git-cvsimport is\n>> going to cease even pretending to work\": It might be that the old\n>> git-cvsimport will stop working *for people who upgrade to cvsps 3.x*.\n>> But it is not realistic to expect people to synchronize their git and\n>> cvsps version upgrades.  It is even quite possible that this or that\n>> Linux distribution will package incompatible versions of the two packages.\n>\n> Moreover, I feel an obligation to point the following out:\n>\n> In a hypothetical world where cvsps 3.x simply breaks \"git cvsimport\"\n> it is likely that some distributions would just stick to the existing\n> cvsps and not upgrade to 3.x.  Maybe that's a wrong choice, but that's\n> a choice some would make.  An even more likely outcome in that\n> hypothetical world is that they would ship it renamed to something\n> like \"cvsps3\" alongside the existing cvsps.  Or they could rename the\n> old version to \"cvsps2\".  If we were the last holdout, we could even\n> bundle it as compat/cvsps.\n>\n> So please do not act as though the cvsps upgrade is a crisis that we\n> need to break ourselves for at threat of no longer working at all.\n> The threat doesn't hold water.\n>\n> Luckily you have already written patches to make \"git cvsimport\" work\n> with cvsps 3.x, and through your work you are making a better\n> argument: \"The new cvsimport + cvsps will work better, at least for\n> some users, than the old tool.\"\n>\n> Just don't pretend you have the power to force a change for a less\n> sensible reason than that!\n\nAfter a quick survey of various distros, I think it is very unlikely\nthat we will see \"distros move on to newer cvsps, leaving cvsimport\nbroken\" situation. If anything, it is more like \"distros decide to\nignore the new cvsps, until it is made to work with cvsimport\" [*1*].\n\nI think it is probably sensible to rename the current cvsimport to\ncvsimport-2, write a small wrapper git-cvsimport.sh which is\nsomething like this:\n\n----- >8 -----\n#!/bin/sh\n\nif test -z \"$GIT_CVSPS_VERSION\"\nthen\n\tcase \"$(cvsps -h 2>&1 | grep 'cvsps version')\" in\n        2.*)\n\t\tGIT_CVSPS_VERSION=2\n                ;;\n\t3.*)\n\t\tGIT_CVSPS_VERSION=3\n                ;;\n\tesac\nfi\n\nif test -z \"$GIT_CVSPS_VERSION\" \nthen\n\techo >&2 \"No supported cvsps available\"\n\texit 1\nfi\n\nexec git cvsimport-$GIT_CVSPS_VERSION \"$@\"\n----- 8< -----\n\nand put Eric's one as git-cvsimport-3 (after ripping out the code to\nfallback to the old cvsimport).  The longer term trend will be to\nmove away from cvsimport-2, as it is unlikely cvsps-2.x will gain\nimprovements, if any; keeping fallback code outside cvsimport-3 will\nbe a better first step in the healthier long term code evolution.\n\nWe will keep the current t96xx series of tests, and have them export\nGIT_CVSPS_VERSION=2 at the beginning, protect them with test prereq\nthat requires presence of cvsps 2.x; this will still make sure that\nthe current cvsimport users will not see any regressions.\n\nEric's one should be polished enough to produce good results on the\nsimpler sample CVS histories t96xx deal with soonish if not right\nnow, so we can use a method similar to how we shared tests between\nblame and annotate while both were _different_ implementations to\nmake sure the newer blame did not inroduce regression by running the\nsame set of tests.  Where the result _ought_ to differ, we should\nalso add tests that work only with the new cvsimport, of course.\n\nI could help getting the ball rolling on this, if everybody agrees\nthat the above is a sensible direction to go, given the real world\nconstraints of distro inertia.\n\nAgreed?\n\n\n[References]\n\n*1* Fedora, Debian and Ubuntu do not even have cvsps 3.x in their\nbleeding edges, OpenBSD and NetBSD do not seem to have it either,\nand Gentoo and ArchLinux have the cvsps 3.x blocked due to\nincompatiblity.\n\nhttp://pkgs.fedoraproject.org/cgit/cvsps.git/\nhttp://packages.debian.org/search?keywords=cvsps\nhttp://packages.ubuntu.com/search?keywords=cvsps\n\nhttp://packages.gentoo.org/package/dev-vcs/cvsps\nhttps://bugs.gentoo.org/show_bug.cgi?id=450424\n\nhttps://bugs.archlinux.org/task/33363?project=1&cat%5B0%5D=2&string=cvsps\n"},{"id":"206722","messageId":"7v38y4hder.fsf@alter.siamese.dyndns.org","threadId":"32585","inReplyTo":"7v8v7wiv3a.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-13T23:27:24Z","receivedAt":"2013-01-13T23:27:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Jonathan Nieder <jrnieder@gmail.com> writes:\n>\n>> Michael Haggerty wrote:\n>>\n>>> Regarding your claim that \"within a few months the Perl git-cvsimport is\n>>> going to cease even pretending to work\": It might be that the old\n>>> git-cvsimport will stop working *for people who upgrade to cvsps 3.x*.\n>>> But it is not realistic to expect people to synchronize their git and\n>>> cvsps version upgrades.  It is even quite possible that this or that\n>>> Linux distribution will package incompatible versions of the two packages.\n>>\n>> Moreover, I feel an obligation to point the following out:\n>>\n>> In a hypothetical world where cvsps 3.x simply breaks \"git cvsimport\"\n>> it is likely that some distributions would just stick to the existing\n>> cvsps and not upgrade to 3.x.  Maybe that's a wrong choice, but that's\n>> a choice some would make.  An even more likely outcome in that\n>> hypothetical world is that they would ship it renamed to something\n>> like \"cvsps3\" alongside the existing cvsps.  Or they could rename the\n>> old version to \"cvsps2\".  If we were the last holdout, we could even\n>> bundle it as compat/cvsps.\n>>\n>> So please do not act as though the cvsps upgrade is a crisis that we\n>> need to break ourselves for at threat of no longer working at all.\n>> The threat doesn't hold water.\n>>\n>> Luckily you have already written patches to make \"git cvsimport\" work\n>> with cvsps 3.x, and through your work you are making a better\n>> argument: \"The new cvsimport + cvsps will work better, at least for\n>> some users, than the old tool.\"\n>>\n>> Just don't pretend you have the power to force a change for a less\n>> sensible reason than that!\n>\n> After a quick survey of various distros, I think it is very unlikely\n> that we will see \"distros move on to newer cvsps, leaving cvsimport\n> broken\" situation. If anything, it is more like \"distros decide to\n> ignore the new cvsps, until it is made to work with cvsimport\" [*1*].\n>\n> I think it is probably sensible to rename the current cvsimport to\n> cvsimport-2, write a small wrapper git-cvsimport.sh which is\n> something like this:\n>\n> ----- >8 -----\n> #!/bin/sh\n>\n> if test -z \"$GIT_CVSPS_VERSION\"\n> then\n> \tcase \"$(cvsps -h 2>&1 | grep 'cvsps version')\" in\n>         2.*)\n> \t\tGIT_CVSPS_VERSION=2\n>                 ;;\n> \t3.*)\n> \t\tGIT_CVSPS_VERSION=3\n>                 ;;\n> \tesac\n> fi\n>\n> if test -z \"$GIT_CVSPS_VERSION\" \n> then\n> \techo >&2 \"No supported cvsps available\"\n> \texit 1\n> fi\n>\n> exec git cvsimport-$GIT_CVSPS_VERSION \"$@\"\n> ----- 8< -----\n>\n> and put Eric's one as git-cvsimport-3 (after ripping out the code to\n> fallback to the old cvsimport).  The longer term trend will be to\n> move away from cvsimport-2, as it is unlikely cvsps-2.x will gain\n> improvements, if any; keeping fallback code outside cvsimport-3 will\n> be a better first step in the healthier long term code evolution.\n>\n> We will keep the current t96xx series of tests, and have them export\n> GIT_CVSPS_VERSION=2 at the beginning, protect them with test prereq\n> that requires presence of cvsps 2.x; this will still make sure that\n> the current cvsimport users will not see any regressions.\n>\n> Eric's one should be polished enough to produce good results on the\n\ns/should be polished enough/should be in a polished enough state/\nthat is.  Also \"if not right now\" may better convey what I meant if\nwritten \"if not already\".\n\n> simpler sample CVS histories t96xx deal with soonish if not right\n> now, so we can use a method similar to how we shared tests between\n> blame and annotate while both were _different_ implementations to\n> make sure the newer blame did not inroduce regression by running the\n> same set of tests.  Where the result _ought_ to differ, we should\n> also add tests that work only with the new cvsimport, of course.\n>\n> I could help getting the ball rolling on this, if everybody agrees\n> that the above is a sensible direction to go, given the real world\n> constraints of distro inertia.\n>\n> Agreed?\n>\n>\n> [References]\n>\n> *1* Fedora, Debian and Ubuntu do not even have cvsps 3.x in their\n> bleeding edges, OpenBSD and NetBSD do not seem to have it either,\n> and Gentoo and ArchLinux have the cvsps 3.x blocked due to\n> incompatiblity.\n>\n> http://pkgs.fedoraproject.org/cgit/cvsps.git/\n> http://packages.debian.org/search?keywords=cvsps\n> http://packages.ubuntu.com/search?keywords=cvsps\n>\n> http://packages.gentoo.org/package/dev-vcs/cvsps\n> https://bugs.gentoo.org/show_bug.cgi?id=450424\n>\n> https://bugs.archlinux.org/task/33363?project=1&cat%5B0%5D=2&string=cvsps\n"},{"id":"206727","messageId":"1358127629-7500-1-git-send-email-gitster@pobox.com","threadId":"32585","inReplyTo":"7v8v7wiv3a.fsf@alter.siamese.dyndns.org","subject":"[PATCH 0/3] A smoother transition plan for cvsimport","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-14T01:40:26Z","receivedAt":"2013-01-14T01:40:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"So here is a start of how such a transition plan outlined in the\nprevious message may look like.\n\nThe first two are preparatory step to allow the current code to\nstill work even when cvsps2 and cvsps3 are both available on the\nsystem.\n\nThe last patch is mostly for illustration purposes; the cvsimport-3.py\nscript it adds was taken from the patch Eric sent earlier and I relayed\nto the list, and does not have later improvements in Eric's tree or\nany of Chris's patches.\n\nJunio C Hamano (3):\n  cvsimport: allow setting a custom cvsps (2.x) program name\n  cvsimport: introduce a version-switch wrapper\n  cvsimport: start adding cvsps 3.x support\n\n .gitignore           |    1 +\n Makefile             |   28 +-\n git-cvsimport-2.perl | 1179 ++++++++++++++++++++++++++++++++++++++++++++++++++\n git-cvsimport-3.py   |  344 +++++++++++++++\n git-cvsimport.perl   | 1177 -------------------------------------------------\n git-cvsimport.sh     |    5 +\n t/lib-cvs.sh         |    4 +-\n 7 files changed, 1553 insertions(+), 1185 deletions(-)\n create mode 100755 git-cvsimport-2.perl\n create mode 100755 git-cvsimport-3.py\n delete mode 100755 git-cvsimport.perl\n create mode 100755 git-cvsimport.sh\n\n-- \n1.8.1.421.g6236851\n"},{"id":"206728","messageId":"1358127629-7500-2-git-send-email-gitster@pobox.com","threadId":"32585","inReplyTo":"1358127629-7500-1-git-send-email-gitster@pobox.com","subject":"[PATCH 1/3] cvsimport: allow setting a custom cvsps (2.x) program name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-14T01:40:27Z","receivedAt":"2013-01-14T01:40:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Distros may ship old cvsps under a different name, or the user may\ninstall it outside the normal $PATH.  Allow setting CVSPS2_PATH from\nthe build environment.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Makefile           | 9 +++++++--\n git-cvsimport.perl | 4 +++-\n t/lib-cvs.sh       | 4 +++-\n 3 files changed, 13 insertions(+), 4 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 1b30d7b..8cb4a1b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -571,9 +571,11 @@ endif\n ifndef PYTHON_PATH\n \tPYTHON_PATH = /usr/bin/python\n endif\n+ifndef CVSPS2_PATH\n+\tCVSPS2_PATH = cvsps\n+endif\n \n-export PERL_PATH\n-export PYTHON_PATH\n+export PERL_PATH PYTHON_PATH CVSPS2_PATH\n \n LIB_FILE = libgit.a\n XDIFF_LIB = xdiff/lib.a\n@@ -1511,6 +1513,7 @@ SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH))\n PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n PYTHON_PATH_SQ = $(subst ','\\'',$(PYTHON_PATH))\n TCLTK_PATH_SQ = $(subst ','\\'',$(TCLTK_PATH))\n+CVSPS2_PATH_SQ = $(subst ','\\'',$(CVSPS2_PATH))\n DIFF_SQ = $(subst ','\\'',$(DIFF))\n \n LIBS = $(GITLIBS) $(EXTLIBS)\n@@ -1724,6 +1727,7 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl GIT-VERSION-FILE\n \t    -e '\tH' \\\n \t    -e '\tx' \\\n \t    -e '}' \\\n+\t    -e 's|@@CVSPS2_PATH@@|$(CVSPS2_PATH_SQ)|g' \\\n \t    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \\\n \t    $@.perl >$@+ && \\\n \tchmod +x $@+ && \\\n@@ -2102,6 +2106,7 @@ GIT-LDFLAGS: FORCE\n GIT-BUILD-OPTIONS: FORCE\n \t@echo SHELL_PATH=\\''$(subst ','\\'',$(SHELL_PATH_SQ))'\\' >$@\n \t@echo PERL_PATH=\\''$(subst ','\\'',$(PERL_PATH_SQ))'\\' >>$@\n+\t@echo CVSPS2_PATH=\\''$(subst ','\\'',$(CVSPS2_PATH_SQ))'\\' >>$@\n \t@echo DIFF=\\''$(subst ','\\'',$(subst ','\\'',$(DIFF)))'\\' >>$@\n \t@echo PYTHON_PATH=\\''$(subst ','\\'',$(PYTHON_PATH_SQ))'\\' >>$@\n \t@echo TAR=\\''$(subst ','\\'',$(subst ','\\'',$(TAR)))'\\' >>$@\ndiff --git a/git-cvsimport.perl b/git-cvsimport.perl\nindex 0a31ebd..ad460a5 100755\n--- a/git-cvsimport.perl\n+++ b/git-cvsimport.perl\n@@ -33,6 +33,8 @@\n our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r, $opt_R);\n my (%conv_author_name, %conv_author_email, %conv_author_tz);\n \n+my $cvsps2 = \"@@CVSPS2_PATH@@\";\n+\n sub usage(;$) {\n \tmy $msg = shift;\n \tprint(STDERR \"Error: $msg\\n\") if $msg;\n@@ -751,7 +753,7 @@ sub munge_user_filename {\n \t\tunless (defined($opt_p) && $opt_p =~ m/--no-cvs-direct/) {\n \t\t\tpush @opt, '--cvs-direct';\n \t\t}\n-\t\texec(\"cvsps\",\"--norc\",@opt,\"-u\",\"-A\",'--root',$opt_d,$cvs_tree);\n+\t\texec($cvsps2,\"--norc\",@opt,\"-u\",\"-A\",'--root',$opt_d,$cvs_tree);\n \t\tdie \"Could not start cvsps: $!\\n\";\n \t}\n \t($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',\ndiff --git a/t/lib-cvs.sh b/t/lib-cvs.sh\nindex 44263ad..bdab63c 100644\n--- a/t/lib-cvs.sh\n+++ b/t/lib-cvs.sh\n@@ -13,7 +13,9 @@ fi\n CVS=\"cvs -f\"\n export CVS\n \n-cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`\n+CVSPS=\"$CVSPS2_PATH\"\n+\n+cvsps_version=`$CVSPS -h 2>&1 | sed -ne 's/cvsps version //p'`\n case \"$cvsps_version\" in\n 2.1 | 2.2*)\n \t;;\n-- \n1.8.1.421.g6236851\n"},{"id":"206730","messageId":"1358127629-7500-3-git-send-email-gitster@pobox.com","threadId":"32585","inReplyTo":"1358127629-7500-1-git-send-email-gitster@pobox.com","subject":"[PATCH 2/3] cvsimport: introduce a version-switch wrapper","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-14T01:40:28Z","receivedAt":"2013-01-14T01:40:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"In preparation to have both old and new cvsimport during the\ntransition period, rename the cvsimport script to cvsimport-2\nand introduce a small wrapper that we can later change it to\nallow users to run either frontend (with their corresponding\ncvsps backends).\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n .gitignore           |    1 +\n Makefile             |    3 +-\n git-cvsimport-2.perl | 1179 ++++++++++++++++++++++++++++++++++++++++++++++++++\n git-cvsimport.perl   | 1179 --------------------------------------------------\n git-cvsimport.sh     |    5 +\n 5 files changed, 1187 insertions(+), 1180 deletions(-)\n create mode 100755 git-cvsimport-2.perl\n delete mode 100755 git-cvsimport.perl\n create mode 100755 git-cvsimport.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex aa258a6..8cb799c 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -40,6 +40,7 @@\n /git-credential-store\n /git-cvsexportcommit\n /git-cvsimport\n+/git-cvsimport-2\n /git-cvsserver\n /git-daemon\n /git-diff\ndiff --git a/Makefile b/Makefile\nindex 8cb4a1b..b022db2 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -435,6 +435,7 @@ unexport CDPATH\n \n SCRIPT_SH += git-am.sh\n SCRIPT_SH += git-bisect.sh\n+SCRIPT_SH += git-cvsimport.sh\n SCRIPT_SH += git-difftool--helper.sh\n SCRIPT_SH += git-filter-branch.sh\n SCRIPT_SH += git-lost-found.sh\n@@ -463,7 +464,7 @@ SCRIPT_PERL += git-add--interactive.perl\n SCRIPT_PERL += git-difftool.perl\n SCRIPT_PERL += git-archimport.perl\n SCRIPT_PERL += git-cvsexportcommit.perl\n-SCRIPT_PERL += git-cvsimport.perl\n+SCRIPT_PERL += git-cvsimport-2.perl\n SCRIPT_PERL += git-cvsserver.perl\n SCRIPT_PERL += git-relink.perl\n SCRIPT_PERL += git-send-email.perl\ndiff --git a/git-cvsimport-2.perl b/git-cvsimport-2.perl\nnew file mode 100755\nindex 0000000..ad460a5\n--- /dev/null\n+++ b/git-cvsimport-2.perl\n@@ -0,0 +1,1179 @@\n+#!/usr/bin/perl\n+\n+# This tool is copyright (c) 2005, Matthias Urlichs.\n+# It is released under the Gnu Public License, version 2.\n+#\n+# The basic idea is to aggregate CVS check-ins into related changes.\n+# Fortunately, \"cvsps\" does that for us; all we have to do is to parse\n+# its output.\n+#\n+# Checking out the files is done by a single long-running CVS connection\n+# / server process.\n+#\n+# The head revision is on branch \"origin\" by default.\n+# You can change that with the '-o' option.\n+\n+use 5.008;\n+use strict;\n+use warnings;\n+use Getopt::Long;\n+use File::Spec;\n+use File::Temp qw(tempfile tmpnam);\n+use File::Path qw(mkpath);\n+use File::Basename qw(basename dirname);\n+use Time::Local;\n+use IO::Socket;\n+use IO::Pipe;\n+use POSIX qw(strftime tzset dup2 ENOENT);\n+use IPC::Open2;\n+\n+$SIG{'PIPE'}=\"IGNORE\";\n+set_timezone('UTC');\n+\n+our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r, $opt_R);\n+my (%conv_author_name, %conv_author_email, %conv_author_tz);\n+\n+my $cvsps2 = \"@@CVSPS2_PATH@@\";\n+\n+sub usage(;$) {\n+\tmy $msg = shift;\n+\tprint(STDERR \"Error: $msg\\n\") if $msg;\n+\tprint STDERR <<END;\n+Usage: git cvsimport     # fetch/update GIT from CVS\n+       [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]\n+       [-p opts-for-cvsps] [-P file] [-C GIT_repository] [-z fuzz] [-i] [-k]\n+       [-u] [-s subst] [-a] [-m] [-M regex] [-S regex] [-L commitlimit]\n+       [-r remote] [-R] [CVS_module]\n+END\n+\texit(1);\n+}\n+\n+sub read_author_info($) {\n+\tmy ($file) = @_;\n+\tmy $user;\n+\topen my $f, '<', \"$file\" or die(\"Failed to open $file: $!\\n\");\n+\n+\twhile (<$f>) {\n+\t\t# Expected format is this:\n+\t\t#   exon=Andreas Ericsson <ae@op5.se>\n+\t\tif (m/^(\\S+?)\\s*=\\s*(.+?)\\s*<(.+)>\\s*$/) {\n+\t\t\t$user = $1;\n+\t\t\t$conv_author_name{$user} = $2;\n+\t\t\t$conv_author_email{$user} = $3;\n+\t\t}\n+\t\t# or with an optional timezone:\n+\t\t#   spawn=Simon Pawn <spawn@frog-pond.org> America/Chicago\n+\t\telsif (m/^(\\S+?)\\s*=\\s*(.+?)\\s*<(.+)>\\s*(\\S+?)\\s*$/) {\n+\t\t\t$user = $1;\n+\t\t\t$conv_author_name{$user} = $2;\n+\t\t\t$conv_author_email{$user} = $3;\n+\t\t\t$conv_author_tz{$user} = $4;\n+\t\t}\n+\t\t# However, we also read from CVSROOT/users format\n+\t\t# to ease migration.\n+\t\telsif (/^(\\w+):(['\"]?)(.+?)\\2\\s*$/) {\n+\t\t\tmy $mapped;\n+\t\t\t($user, $mapped) = ($1, $3);\n+\t\t\tif ($mapped =~ /^\\s*(.*?)\\s*<(.*)>\\s*$/) {\n+\t\t\t\t$conv_author_name{$user} = $1;\n+\t\t\t\t$conv_author_email{$user} = $2;\n+\t\t\t}\n+\t\t\telsif ($mapped =~ /^<?(.*)>?$/) {\n+\t\t\t\t$conv_author_name{$user} = $user;\n+\t\t\t\t$conv_author_email{$user} = $1;\n+\t\t\t}\n+\t\t}\n+\t\t# NEEDSWORK: Maybe warn on unrecognized lines?\n+\t}\n+\tclose ($f);\n+}\n+\n+sub write_author_info($) {\n+\tmy ($file) = @_;\n+\topen my $f, '>', $file or\n+\t  die(\"Failed to open $file for writing: $!\");\n+\n+\tforeach (keys %conv_author_name) {\n+\t\tprint $f \"$_=$conv_author_name{$_} <$conv_author_email{$_}>\";\n+\t\tprint $f \" $conv_author_tz{$_}\" if ($conv_author_tz{$_});\n+\t\tprint $f \"\\n\";\n+\t}\n+\tclose ($f);\n+}\n+\n+# Versions of perl before 5.10.0 may not automatically check $TZ each\n+# time localtime is run (most platforms will do so only the first time).\n+# We can work around this by using tzset() to update the internal\n+# variable whenever we change the environment.\n+sub set_timezone {\n+\t$ENV{TZ} = shift;\n+\ttzset();\n+}\n+\n+# convert getopts specs for use by git config\n+my %longmap = (\n+\t'A:' => 'authors-file',\n+\t'M:' => 'merge-regex',\n+\t'P:' => undef,\n+\t'R' => 'track-revisions',\n+\t'S:' => 'ignore-paths',\n+);\n+\n+sub read_repo_config {\n+\t# Split the string between characters, unless there is a ':'\n+\t# So \"abc:de\" becomes [\"a\", \"b\", \"c:\", \"d\", \"e\"]\n+\tmy @opts = split(/ *(?!:)/, shift);\n+\tforeach my $o (@opts) {\n+\t\tmy $key = $o;\n+\t\t$key =~ s/://g;\n+\t\tmy $arg = 'git config';\n+\t\t$arg .= ' --bool' if ($o !~ /:$/);\n+\t\tmy $ckey = $key;\n+\n+\t\tif (exists $longmap{$o}) {\n+\t\t\t# An uppercase option like -R cannot be\n+\t\t\t# expressed in the configuration, as the\n+\t\t\t# variable names are downcased.\n+\t\t\t$ckey = $longmap{$o};\n+\t\t\tnext if (! defined $ckey);\n+\t\t\t$ckey =~ s/-//g;\n+\t\t}\n+\t\tchomp(my $tmp = `$arg --get cvsimport.$ckey`);\n+\t\tif ($tmp && !($arg =~ /--bool/ && $tmp eq 'false')) {\n+\t\t\tno strict 'refs';\n+\t\t\tmy $opt_name = \"opt_\" . $key;\n+\t\t\tif (!$$opt_name) {\n+\t\t\t\t$$opt_name = $tmp;\n+\t\t\t}\n+\t\t}\n+\t}\n+}\n+\n+my $opts = \"haivmkuo:d:p:r:C:z:s:M:P:A:S:L:R\";\n+read_repo_config($opts);\n+Getopt::Long::Configure( 'no_ignore_case', 'bundling' );\n+\n+# turn the Getopt::Std specification in a Getopt::Long one,\n+# with support for multiple -M options\n+GetOptions( map { s/:/=s/; /M/ ? \"$_\\@\" : $_ } split( /(?!:)/, $opts ) )\n+    or usage();\n+usage if $opt_h;\n+\n+if (@ARGV == 0) {\n+\t\tchomp(my $module = `git config --get cvsimport.module`);\n+\t\tpush(@ARGV, $module) if $? == 0;\n+}\n+@ARGV <= 1 or usage(\"You can't specify more than one CVS module\");\n+\n+if ($opt_d) {\n+\t$ENV{\"CVSROOT\"} = $opt_d;\n+} elsif (-f 'CVS/Root') {\n+\topen my $f, '<', 'CVS/Root' or die 'Failed to open CVS/Root';\n+\t$opt_d = <$f>;\n+\tchomp $opt_d;\n+\tclose $f;\n+\t$ENV{\"CVSROOT\"} = $opt_d;\n+} elsif ($ENV{\"CVSROOT\"}) {\n+\t$opt_d = $ENV{\"CVSROOT\"};\n+} else {\n+\tusage(\"CVSROOT needs to be set\");\n+}\n+$opt_s ||= \"-\";\n+$opt_a ||= 0;\n+\n+my $git_tree = $opt_C;\n+$git_tree ||= \".\";\n+\n+my $remote;\n+if (defined $opt_r) {\n+\t$remote = 'refs/remotes/' . $opt_r;\n+\t$opt_o ||= \"master\";\n+} else {\n+\t$opt_o ||= \"origin\";\n+\t$remote = 'refs/heads';\n+}\n+\n+my $cvs_tree;\n+if ($#ARGV == 0) {\n+\t$cvs_tree = $ARGV[0];\n+} elsif (-f 'CVS/Repository') {\n+\topen my $f, '<', 'CVS/Repository' or\n+\t    die 'Failed to open CVS/Repository';\n+\t$cvs_tree = <$f>;\n+\tchomp $cvs_tree;\n+\tclose $f;\n+} else {\n+\tusage(\"CVS module has to be specified\");\n+}\n+\n+our @mergerx = ();\n+if ($opt_m) {\n+\t@mergerx = ( qr/\\b(?:from|of|merge|merging|merged) ([-\\w]+)/i );\n+}\n+if (@opt_M) {\n+\tpush (@mergerx, map { qr/$_/ } @opt_M);\n+}\n+\n+# Remember UTC of our starting time\n+# we'll want to avoid importing commits\n+# that are too recent\n+our $starttime = time();\n+\n+select(STDERR); $|=1; select(STDOUT);\n+\n+\n+package CVSconn;\n+# Basic CVS dialog.\n+# We're only interested in connecting and downloading, so ...\n+\n+use File::Spec;\n+use File::Temp qw(tempfile);\n+use POSIX qw(strftime dup2);\n+\n+sub new {\n+\tmy ($what,$repo,$subdir) = @_;\n+\t$what=ref($what) if ref($what);\n+\n+\tmy $self = {};\n+\t$self->{'buffer'} = \"\";\n+\tbless($self,$what);\n+\n+\t$repo =~ s#/+$##;\n+\t$self->{'fullrep'} = $repo;\n+\t$self->conn();\n+\n+\t$self->{'subdir'} = $subdir;\n+\t$self->{'lines'} = undef;\n+\n+\treturn $self;\n+}\n+\n+sub find_password_entry {\n+\tmy ($cvspass, @cvsroot) = @_;\n+\tmy ($file, $delim) = @$cvspass;\n+\tmy $pass;\n+\tlocal ($_);\n+\n+\tif (open(my $fh, $file)) {\n+\t\t# :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z\n+\t\tCVSPASSFILE:\n+\t\twhile (<$fh>) {\n+\t\t\tchomp;\n+\t\t\ts/^\\/\\d+\\s+//;\n+\t\t\tmy ($w, $p) = split($delim,$_,2);\n+\t\t\tfor my $cvsroot (@cvsroot) {\n+\t\t\t\tif ($w eq $cvsroot) {\n+\t\t\t\t\t$pass = $p;\n+\t\t\t\t\tlast CVSPASSFILE;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\tclose($fh);\n+\t}\n+\treturn $pass;\n+}\n+\n+sub conn {\n+\tmy $self = shift;\n+\tmy $repo = $self->{'fullrep'};\n+\tif ($repo =~ s/^:pserver(?:([^:]*)):(?:(.*?)(?::(.*?))?@)?([^:\\/]*)(?::(\\d*))?//) {\n+\t\tmy ($param,$user,$pass,$serv,$port) = ($1,$2,$3,$4,$5);\n+\n+\t\tmy ($proxyhost,$proxyport);\n+\t\tif ($param && ($param =~ m/proxy=([^;]+)/)) {\n+\t\t\t$proxyhost = $1;\n+\t\t\t# Default proxyport, if not specified, is 8080.\n+\t\t\t$proxyport = 8080;\n+\t\t\tif ($ENV{\"CVS_PROXY_PORT\"}) {\n+\t\t\t\t$proxyport = $ENV{\"CVS_PROXY_PORT\"};\n+\t\t\t}\n+\t\t\tif ($param =~ m/proxyport=([^;]+)/) {\n+\t\t\t\t$proxyport = $1;\n+\t\t\t}\n+\t\t}\n+\t\t$repo ||= '/';\n+\n+\t\t# if username is not explicit in CVSROOT, then use current user, as cvs would\n+\t\t$user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || \"anonymous\") unless $user;\n+\t\tmy $rr2 = \"-\";\n+\t\tunless ($port) {\n+\t\t\t$rr2 = \":pserver:$user\\@$serv:$repo\";\n+\t\t\t$port=2401;\n+\t\t}\n+\t\tmy $rr = \":pserver:$user\\@$serv:$port$repo\";\n+\n+\t\tif ($pass) {\n+\t\t\t$pass = $self->_scramble($pass);\n+\t\t} else {\n+\t\t\tmy @cvspass = ([$ENV{'HOME'}.\"/.cvspass\", qr/\\s/],\n+\t\t\t\t       [$ENV{'HOME'}.\"/.cvs/cvspass\", qr/=/]);\n+\t\t\tmy @loc = ();\n+\t\t\tforeach my $cvspass (@cvspass) {\n+\t\t\t\tmy $p = find_password_entry($cvspass, $rr, $rr2);\n+\t\t\t\tif ($p) {\n+\t\t\t\t\tpush @loc, $cvspass->[0];\n+\t\t\t\t\t$pass = $p;\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\tif (1 < @loc) {\n+\t\t\t\tdie(\"Multiple cvs password files have \".\n+\t\t\t\t    \"entries for CVSROOT $opt_d: @loc\");\n+\t\t\t} elsif (!$pass) {\n+\t\t\t\t$pass = \"A\";\n+\t\t\t}\n+\t\t}\n+\n+\t\tmy ($s, $rep);\n+\t\tif ($proxyhost) {\n+\n+\t\t\t# Use a HTTP Proxy. Only works for HTTP proxies that\n+\t\t\t# don't require user authentication\n+\t\t\t#\n+\t\t\t# See: http://www.ietf.org/rfc/rfc2817.txt\n+\n+\t\t\t$s = IO::Socket::INET->new(PeerHost => $proxyhost, PeerPort => $proxyport);\n+\t\t\tdie \"Socket to $proxyhost: $!\\n\" unless defined $s;\n+\t\t\t$s->write(\"CONNECT $serv:$port HTTP/1.1\\r\\nHost: $serv:$port\\r\\n\\r\\n\")\n+\t                        or die \"Write to $proxyhost: $!\\n\";\n+\t                $s->flush();\n+\n+\t\t\t$rep = <$s>;\n+\n+\t\t\t# The answer should look like 'HTTP/1.x 2yy ....'\n+\t\t\tif (!($rep =~ m#^HTTP/1\\.. 2[0-9][0-9]#)) {\n+\t\t\t\tdie \"Proxy connect: $rep\\n\";\n+\t\t\t}\n+\t\t\t# Skip up to the empty line of the proxy server output\n+\t\t\t# including the response headers.\n+\t\t\twhile ($rep = <$s>) {\n+\t\t\t\tlast if (!defined $rep ||\n+\t\t\t\t\t $rep eq \"\\n\" ||\n+\t\t\t\t\t $rep eq \"\\r\\n\");\n+\t\t\t}\n+\t\t} else {\n+\t\t\t$s = IO::Socket::INET->new(PeerHost => $serv, PeerPort => $port);\n+\t\t\tdie \"Socket to $serv: $!\\n\" unless defined $s;\n+\t\t}\n+\n+\t\t$s->write(\"BEGIN AUTH REQUEST\\n$repo\\n$user\\n$pass\\nEND AUTH REQUEST\\n\")\n+\t\t\tor die \"Write to $serv: $!\\n\";\n+\t\t$s->flush();\n+\n+\t\t$rep = <$s>;\n+\n+\t\tif ($rep ne \"I LOVE YOU\\n\") {\n+\t\t\t$rep=\"<unknown>\" unless $rep;\n+\t\t\tdie \"AuthReply: $rep\\n\";\n+\t\t}\n+\t\t$self->{'socketo'} = $s;\n+\t\t$self->{'socketi'} = $s;\n+\t} else { # local or ext: Fork off our own cvs server.\n+\t\tmy $pr = IO::Pipe->new();\n+\t\tmy $pw = IO::Pipe->new();\n+\t\tmy $pid = fork();\n+\t\tdie \"Fork: $!\\n\" unless defined $pid;\n+\t\tmy $cvs = 'cvs';\n+\t\t$cvs = $ENV{CVS_SERVER} if exists $ENV{CVS_SERVER};\n+\t\tmy $rsh = 'rsh';\n+\t\t$rsh = $ENV{CVS_RSH} if exists $ENV{CVS_RSH};\n+\n+\t\tmy @cvs = ($cvs, 'server');\n+\t\tmy ($local, $user, $host);\n+\t\t$local = $repo =~ s/:local://;\n+\t\tif (!$local) {\n+\t\t    $repo =~ s/:ext://;\n+\t\t    $local = !($repo =~ s/^(?:([^\\@:]+)\\@)?([^:]+)://);\n+\t\t    ($user, $host) = ($1, $2);\n+\t\t}\n+\t\tif (!$local) {\n+\t\t    if ($user) {\n+\t\t\tunshift @cvs, $rsh, '-l', $user, $host;\n+\t\t    } else {\n+\t\t\tunshift @cvs, $rsh, $host;\n+\t\t    }\n+\t\t}\n+\n+\t\tunless ($pid) {\n+\t\t\t$pr->writer();\n+\t\t\t$pw->reader();\n+\t\t\tdup2($pw->fileno(),0);\n+\t\t\tdup2($pr->fileno(),1);\n+\t\t\t$pr->close();\n+\t\t\t$pw->close();\n+\t\t\texec(@cvs);\n+\t\t}\n+\t\t$pw->writer();\n+\t\t$pr->reader();\n+\t\t$self->{'socketo'} = $pw;\n+\t\t$self->{'socketi'} = $pr;\n+\t}\n+\t$self->{'socketo'}->write(\"Root $repo\\n\");\n+\n+\t# Trial and error says that this probably is the minimum set\n+\t$self->{'socketo'}->write(\"Valid-responses ok error Valid-requests Mode M Mbinary E Checked-in Created Updated Merged Removed\\n\");\n+\n+\t$self->{'socketo'}->write(\"valid-requests\\n\");\n+\t$self->{'socketo'}->flush();\n+\n+\tmy $rep=$self->readline();\n+\tdie \"Failed to read from server\" unless defined $rep;\n+\tchomp($rep);\n+\tif ($rep !~ s/^Valid-requests\\s*//) {\n+\t\t$rep=\"<unknown>\" unless $rep;\n+\t\tdie \"Expected Valid-requests from server, but got: $rep\\n\";\n+\t}\n+\tchomp(my $res=$self->readline());\n+\tdie \"validReply: $res\\n\" if $res ne \"ok\";\n+\n+\t$self->{'socketo'}->write(\"UseUnchanged\\n\") if $rep =~ /\\bUseUnchanged\\b/;\n+\t$self->{'repo'} = $repo;\n+}\n+\n+sub readline {\n+\tmy ($self) = @_;\n+\treturn $self->{'socketi'}->getline();\n+}\n+\n+sub _file {\n+\t# Request a file with a given revision.\n+\t# Trial and error says this is a good way to do it. :-/\n+\tmy ($self,$fn,$rev) = @_;\n+\t$self->{'socketo'}->write(\"Argument -N\\n\") or return undef;\n+\t$self->{'socketo'}->write(\"Argument -P\\n\") or return undef;\n+\t# -kk: Linus' version doesn't use it - defaults to off\n+\tif ($opt_k) {\n+\t    $self->{'socketo'}->write(\"Argument -kk\\n\") or return undef;\n+\t}\n+\t$self->{'socketo'}->write(\"Argument -r\\n\") or return undef;\n+\t$self->{'socketo'}->write(\"Argument $rev\\n\") or return undef;\n+\t$self->{'socketo'}->write(\"Argument --\\n\") or return undef;\n+\t$self->{'socketo'}->write(\"Argument $self->{'subdir'}/$fn\\n\") or return undef;\n+\t$self->{'socketo'}->write(\"Directory .\\n\") or return undef;\n+\t$self->{'socketo'}->write(\"$self->{'repo'}\\n\") or return undef;\n+\t# $self->{'socketo'}->write(\"Sticky T1.0\\n\") or return undef;\n+\t$self->{'socketo'}->write(\"co\\n\") or return undef;\n+\t$self->{'socketo'}->flush() or return undef;\n+\t$self->{'lines'} = 0;\n+\treturn 1;\n+}\n+sub _line {\n+\t# Read a line from the server.\n+\t# ... except that 'line' may be an entire file. ;-)\n+\tmy ($self, $fh) = @_;\n+\tdie \"Not in lines\" unless defined $self->{'lines'};\n+\n+\tmy $line;\n+\tmy $res=0;\n+\twhile (defined($line = $self->readline())) {\n+\t\t# M U gnupg-cvs-rep/AUTHORS\n+\t\t# Updated gnupg-cvs-rep/\n+\t\t# /daten/src/rsync/gnupg-cvs-rep/AUTHORS\n+\t\t# /AUTHORS/1.1///T1.1\n+\t\t# u=rw,g=rw,o=rw\n+\t\t# 0\n+\t\t# ok\n+\n+\t\tif ($line =~ s/^(?:Created|Updated) //) {\n+\t\t\t$line = $self->readline(); # path\n+\t\t\t$line = $self->readline(); # Entries line\n+\t\t\tmy $mode = $self->readline(); chomp $mode;\n+\t\t\t$self->{'mode'} = $mode;\n+\t\t\tdefined (my $cnt = $self->readline())\n+\t\t\t\tor die \"EOF from server after 'Changed'\\n\";\n+\t\t\tchomp $cnt;\n+\t\t\tdie \"Duh: Filesize $cnt\" if $cnt !~ /^\\d+$/;\n+\t\t\t$line=\"\";\n+\t\t\t$res = $self->_fetchfile($fh, $cnt);\n+\t\t} elsif ($line =~ s/^ //) {\n+\t\t\tprint $fh $line;\n+\t\t\t$res += length($line);\n+\t\t} elsif ($line =~ /^M\\b/) {\n+\t\t\t# output, do nothing\n+\t\t} elsif ($line =~ /^Mbinary\\b/) {\n+\t\t\tmy $cnt;\n+\t\t\tdie \"EOF from server after 'Mbinary'\" unless defined ($cnt = $self->readline());\n+\t\t\tchomp $cnt;\n+\t\t\tdie \"Duh: Mbinary $cnt\" if $cnt !~ /^\\d+$/ or $cnt<1;\n+\t\t\t$line=\"\";\n+\t\t\t$res += $self->_fetchfile($fh, $cnt);\n+\t\t} else {\n+\t\t\tchomp $line;\n+\t\t\tif ($line eq \"ok\") {\n+\t\t\t\t# print STDERR \"S: ok (\".length($res).\")\\n\";\n+\t\t\t\treturn $res;\n+\t\t\t} elsif ($line =~ s/^E //) {\n+\t\t\t\t# print STDERR \"S: $line\\n\";\n+\t\t\t} elsif ($line =~ /^(Remove-entry|Removed) /i) {\n+\t\t\t\t$line = $self->readline(); # filename\n+\t\t\t\t$line = $self->readline(); # OK\n+\t\t\t\tchomp $line;\n+\t\t\t\tdie \"Unknown: $line\" if $line ne \"ok\";\n+\t\t\t\treturn -1;\n+\t\t\t} else {\n+\t\t\t\tdie \"Unknown: $line\\n\";\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn undef;\n+}\n+sub file {\n+\tmy ($self,$fn,$rev) = @_;\n+\tmy $res;\n+\n+\tmy ($fh, $name) = tempfile('gitcvs.XXXXXX',\n+\t\t    DIR => File::Spec->tmpdir(), UNLINK => 1);\n+\n+\t$self->_file($fn,$rev) and $res = $self->_line($fh);\n+\n+\tif (!defined $res) {\n+\t    print STDERR \"Server has gone away while fetching $fn $rev, retrying...\\n\";\n+\t    truncate $fh, 0;\n+\t    $self->conn();\n+\t    $self->_file($fn,$rev) or die \"No file command send\";\n+\t    $res = $self->_line($fh);\n+\t    die \"Retry failed\" unless defined $res;\n+\t}\n+\tclose ($fh);\n+\n+\treturn ($name, $res);\n+}\n+sub _fetchfile {\n+\tmy ($self, $fh, $cnt) = @_;\n+\tmy $res = 0;\n+\tmy $bufsize = 1024 * 1024;\n+\twhile ($cnt) {\n+\t    if ($bufsize > $cnt) {\n+\t\t$bufsize = $cnt;\n+\t    }\n+\t    my $buf;\n+\t    my $num = $self->{'socketi'}->read($buf,$bufsize);\n+\t    die \"Server: Filesize $cnt: $num: $!\\n\" if not defined $num or $num<=0;\n+\t    print $fh $buf;\n+\t    $res += $num;\n+\t    $cnt -= $num;\n+\t}\n+\treturn $res;\n+}\n+\n+sub _scramble {\n+\tmy ($self, $pass) = @_;\n+\tmy $scrambled = \"A\";\n+\n+\treturn $scrambled unless $pass;\n+\n+\tmy $pass_len = length($pass);\n+\tmy @pass_arr = split(\"\", $pass);\n+\tmy $i;\n+\n+\t# from cvs/src/scramble.c\n+\tmy @shifts = (\n+\t\t  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,\n+\t\t 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,\n+\t\t114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,\n+\t\t111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,\n+\t\t 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,\n+\t\t125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,\n+\t\t 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,\n+\t\t 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,\n+\t\t225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,\n+\t\t199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,\n+\t\t174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,\n+\t\t207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,\n+\t\t192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,\n+\t\t227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,\n+\t\t182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,\n+\t\t243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152\n+\t);\n+\n+\tfor ($i = 0; $i < $pass_len; $i++) {\n+\t\t$scrambled .= pack(\"C\", $shifts[ord($pass_arr[$i])]);\n+\t}\n+\n+\treturn $scrambled;\n+}\n+\n+package main;\n+\n+my $cvs = CVSconn->new($opt_d, $cvs_tree);\n+\n+\n+sub pdate($) {\n+\tmy ($d) = @_;\n+\tm#(\\d{2,4})/(\\d\\d)/(\\d\\d)\\s(\\d\\d):(\\d\\d)(?::(\\d\\d))?#\n+\t\tor die \"Unparseable date: $d\\n\";\n+\tmy $y=$1; $y-=1900 if $y>1900;\n+\treturn timegm($6||0,$5,$4,$3,$2-1,$y);\n+}\n+\n+sub pmode($) {\n+\tmy ($mode) = @_;\n+\tmy $m = 0;\n+\tmy $mm = 0;\n+\tmy $um = 0;\n+\tfor my $x(split(//,$mode)) {\n+\t\tif ($x eq \",\") {\n+\t\t\t$m |= $mm&$um;\n+\t\t\t$mm = 0;\n+\t\t\t$um = 0;\n+\t\t} elsif ($x eq \"u\") { $um |= 0700;\n+\t\t} elsif ($x eq \"g\") { $um |= 0070;\n+\t\t} elsif ($x eq \"o\") { $um |= 0007;\n+\t\t} elsif ($x eq \"r\") { $mm |= 0444;\n+\t\t} elsif ($x eq \"w\") { $mm |= 0222;\n+\t\t} elsif ($x eq \"x\") { $mm |= 0111;\n+\t\t} elsif ($x eq \"=\") { # do nothing\n+\t\t} else { die \"Unknown mode: $mode\\n\";\n+\t\t}\n+\t}\n+\t$m |= $mm&$um;\n+\treturn $m;\n+}\n+\n+sub getwd() {\n+\tmy $pwd = `pwd`;\n+\tchomp $pwd;\n+\treturn $pwd;\n+}\n+\n+sub is_sha1 {\n+\tmy $s = shift;\n+\treturn $s =~ /^[a-f0-9]{40}$/;\n+}\n+\n+sub get_headref ($) {\n+\tmy $name = shift;\n+\tmy $r = `git rev-parse --verify '$name' 2>/dev/null`;\n+\treturn undef unless $? == 0;\n+\tchomp $r;\n+\treturn $r;\n+}\n+\n+my $user_filename_prepend = '';\n+sub munge_user_filename {\n+\tmy $name = shift;\n+\treturn File::Spec->file_name_is_absolute($name) ?\n+\t\t$name :\n+\t\t$user_filename_prepend . $name;\n+}\n+\n+-d $git_tree\n+\tor mkdir($git_tree,0777)\n+\tor die \"Could not create $git_tree: $!\";\n+if ($git_tree ne '.') {\n+\t$user_filename_prepend = getwd() . '/';\n+\tchdir($git_tree);\n+}\n+\n+my $last_branch = \"\";\n+my $orig_branch = \"\";\n+my %branch_date;\n+my $tip_at_start = undef;\n+\n+my $git_dir = $ENV{\"GIT_DIR\"} || \".git\";\n+$git_dir = getwd().\"/\".$git_dir unless $git_dir =~ m#^/#;\n+$ENV{\"GIT_DIR\"} = $git_dir;\n+my $orig_git_index;\n+$orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE};\n+\n+my %index; # holds filenames of one index per branch\n+\n+unless (-d $git_dir) {\n+\tsystem(qw(git init));\n+\tdie \"Cannot init the GIT db at $git_tree: $?\\n\" if $?;\n+\tsystem(qw(git read-tree --empty));\n+\tdie \"Cannot init an empty tree: $?\\n\" if $?;\n+\n+\t$last_branch = $opt_o;\n+\t$orig_branch = \"\";\n+} else {\n+\topen(F, \"-|\", qw(git symbolic-ref HEAD)) or\n+\t\tdie \"Cannot run git symbolic-ref: $!\\n\";\n+\tchomp ($last_branch = <F>);\n+\t$last_branch = basename($last_branch);\n+\tclose(F);\n+\tunless ($last_branch) {\n+\t\twarn \"Cannot read the last branch name: $! -- assuming 'master'\\n\";\n+\t\t$last_branch = \"master\";\n+\t}\n+\t$orig_branch = $last_branch;\n+\t$tip_at_start = `git rev-parse --verify HEAD`;\n+\n+\t# Get the last import timestamps\n+\tmy $fmt = '($ref, $author) = (%(refname), %(author));';\n+\tmy @cmd = ('git', 'for-each-ref', '--perl', \"--format=$fmt\", $remote);\n+\topen(H, \"-|\", @cmd) or die \"Cannot run git for-each-ref: $!\\n\";\n+\twhile (defined(my $entry = <H>)) {\n+\t\tmy ($ref, $author);\n+\t\teval($entry) || die \"cannot eval refs list: $@\";\n+\t\tmy ($head) = ($ref =~ m|^$remote/(.*)|);\n+\t\t$author =~ /^.*\\s(\\d+)\\s[-+]\\d{4}$/;\n+\t\t$branch_date{$head} = $1;\n+\t}\n+\tclose(H);\n+        if (!exists $branch_date{$opt_o}) {\n+\t\tdie \"Branch '$opt_o' does not exist.\\n\".\n+\t\t       \"Either use the correct '-o branch' option,\\n\".\n+\t\t       \"or import to a new repository.\\n\";\n+        }\n+}\n+\n+-d $git_dir\n+\tor die \"Could not create git subdir ($git_dir).\\n\";\n+\n+# now we read (and possibly save) author-info as well\n+-f \"$git_dir/cvs-authors\" and\n+  read_author_info(\"$git_dir/cvs-authors\");\n+if ($opt_A) {\n+\tread_author_info(munge_user_filename($opt_A));\n+\twrite_author_info(\"$git_dir/cvs-authors\");\n+}\n+\n+# open .git/cvs-revisions, if requested\n+open my $revision_map, '>>', \"$git_dir/cvs-revisions\"\n+    or die \"Can't open $git_dir/cvs-revisions for appending: $!\\n\"\n+\tif defined $opt_R;\n+\n+\n+#\n+# run cvsps into a file unless we are getting\n+# it passed as a file via $opt_P\n+#\n+my $cvspsfile;\n+unless ($opt_P) {\n+\tprint \"Running cvsps...\\n\" if $opt_v;\n+\tmy $pid = open(CVSPS,\"-|\");\n+\tmy $cvspsfh;\n+\tdie \"Cannot fork: $!\\n\" unless defined $pid;\n+\tunless ($pid) {\n+\t\tmy @opt;\n+\t\t@opt = split(/,/,$opt_p) if defined $opt_p;\n+\t\tunshift @opt, '-z', $opt_z if defined $opt_z;\n+\t\tunshift @opt, '-q'         unless defined $opt_v;\n+\t\tunless (defined($opt_p) && $opt_p =~ m/--no-cvs-direct/) {\n+\t\t\tpush @opt, '--cvs-direct';\n+\t\t}\n+\t\texec($cvsps2,\"--norc\",@opt,\"-u\",\"-A\",'--root',$opt_d,$cvs_tree);\n+\t\tdie \"Could not start cvsps: $!\\n\";\n+\t}\n+\t($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',\n+\t\t\t\t\t  DIR => File::Spec->tmpdir());\n+\twhile (<CVSPS>) {\n+\t    print $cvspsfh $_;\n+\t}\n+\tclose CVSPS;\n+\t$? == 0 or die \"git cvsimport: fatal: cvsps reported error\\n\";\n+\tclose $cvspsfh;\n+} else {\n+\t$cvspsfile = munge_user_filename($opt_P);\n+}\n+\n+open(CVS, \"<$cvspsfile\") or die $!;\n+\n+## cvsps output:\n+#---------------------\n+#PatchSet 314\n+#Date: 1999/09/18 13:03:59\n+#Author: wkoch\n+#Branch: STABLE-BRANCH-1-0\n+#Ancestor branch: HEAD\n+#Tag: (none)\n+#Log:\n+#    See ChangeLog: Sat Sep 18 13:03:28 CEST 1999  Werner Koch\n+#Members:\n+#\tREADME:1.57->1.57.2.1\n+#\tVERSION:1.96->1.96.2.1\n+#\n+#---------------------\n+\n+my $state = 0;\n+\n+sub update_index (\\@\\@) {\n+\tmy $old = shift;\n+\tmy $new = shift;\n+\topen(my $fh, '|-', qw(git update-index -z --index-info))\n+\t\tor die \"unable to open git update-index: $!\";\n+\tprint $fh\n+\t\t(map { \"0 0000000000000000000000000000000000000000\\t$_\\0\" }\n+\t\t\t@$old),\n+\t\t(map { '100' . sprintf('%o', $_->[0]) . \" $_->[1]\\t$_->[2]\\0\" }\n+\t\t\t@$new)\n+\t\tor die \"unable to write to git update-index: $!\";\n+\tclose $fh\n+\t\tor die \"unable to write to git update-index: $!\";\n+\t$? and die \"git update-index reported error: $?\";\n+}\n+\n+sub write_tree () {\n+\topen(my $fh, '-|', qw(git write-tree))\n+\t\tor die \"unable to open git write-tree: $!\";\n+\tchomp(my $tree = <$fh>);\n+\tis_sha1($tree)\n+\t\tor die \"Cannot get tree id ($tree): $!\";\n+\tclose($fh)\n+\t\tor die \"Error running git write-tree: $?\\n\";\n+\tprint \"Tree ID $tree\\n\" if $opt_v;\n+\treturn $tree;\n+}\n+\n+my ($patchset,$date,$author_name,$author_email,$author_tz,$branch,$ancestor,$tag,$logmsg);\n+my (@old,@new,@skipped,%ignorebranch,@commit_revisions);\n+\n+# commits that cvsps cannot place anywhere...\n+$ignorebranch{'#CVSPS_NO_BRANCH'} = 1;\n+\n+sub commit {\n+\tif ($branch eq $opt_o && !$index{branch} &&\n+\t\t!get_headref(\"$remote/$branch\")) {\n+\t    # looks like an initial commit\n+\t    # use the index primed by git init\n+\t    $ENV{GIT_INDEX_FILE} = \"$git_dir/index\";\n+\t    $index{$branch} = \"$git_dir/index\";\n+\t} else {\n+\t    # use an index per branch to speed up\n+\t    # imports of projects with many branches\n+\t    unless ($index{$branch}) {\n+\t\t$index{$branch} = tmpnam();\n+\t\t$ENV{GIT_INDEX_FILE} = $index{$branch};\n+\t\tif ($ancestor) {\n+\t\t    system(\"git\", \"read-tree\", \"$remote/$ancestor\");\n+\t\t} else {\n+\t\t    system(\"git\", \"read-tree\", \"$remote/$branch\");\n+\t\t}\n+\t\tdie \"read-tree failed: $?\\n\" if $?;\n+\t    }\n+\t}\n+        $ENV{GIT_INDEX_FILE} = $index{$branch};\n+\n+\tupdate_index(@old, @new);\n+\t@old = @new = ();\n+\tmy $tree = write_tree();\n+\tmy $parent = get_headref(\"$remote/$last_branch\");\n+\tprint \"Parent ID \" . ($parent ? $parent : \"(empty)\") . \"\\n\" if $opt_v;\n+\n+\tmy @commit_args;\n+\tpush @commit_args, (\"-p\", $parent) if $parent;\n+\n+\t# loose detection of merges\n+\t# based on the commit msg\n+\tforeach my $rx (@mergerx) {\n+\t\tnext unless $logmsg =~ $rx && $1;\n+\t\tmy $mparent = $1 eq 'HEAD' ? $opt_o : $1;\n+\t\tif (my $sha1 = get_headref(\"$remote/$mparent\")) {\n+\t\t\tpush @commit_args, '-p', \"$remote/$mparent\";\n+\t\t\tprint \"Merge parent branch: $mparent\\n\" if $opt_v;\n+\t\t}\n+\t}\n+\n+\tset_timezone($author_tz);\n+\tmy $commit_date = strftime(\"%s %z\", localtime($date));\n+\tset_timezone('UTC');\n+\t$ENV{GIT_AUTHOR_NAME} = $author_name;\n+\t$ENV{GIT_AUTHOR_EMAIL} = $author_email;\n+\t$ENV{GIT_AUTHOR_DATE} = $commit_date;\n+\t$ENV{GIT_COMMITTER_NAME} = $author_name;\n+\t$ENV{GIT_COMMITTER_EMAIL} = $author_email;\n+\t$ENV{GIT_COMMITTER_DATE} = $commit_date;\n+\tmy $pid = open2(my $commit_read, my $commit_write,\n+\t\t'git', 'commit-tree', $tree, @commit_args);\n+\n+\t# compatibility with git2cvs\n+\tsubstr($logmsg,32767) = \"\" if length($logmsg) > 32767;\n+\t$logmsg =~ s/[\\s\\n]+\\z//;\n+\n+\tif (@skipped) {\n+\t    $logmsg .= \"\\n\\n\\nSKIPPED:\\n\\t\";\n+\t    $logmsg .= join(\"\\n\\t\", @skipped) . \"\\n\";\n+\t    @skipped = ();\n+\t}\n+\n+\tprint($commit_write \"$logmsg\\n\") && close($commit_write)\n+\t\tor die \"Error writing to git commit-tree: $!\\n\";\n+\n+\tprint \"Committed patch $patchset ($branch $commit_date)\\n\" if $opt_v;\n+\tchomp(my $cid = <$commit_read>);\n+\tis_sha1($cid) or die \"Cannot get commit id ($cid): $!\\n\";\n+\tprint \"Commit ID $cid\\n\" if $opt_v;\n+\tclose($commit_read);\n+\n+\twaitpid($pid,0);\n+\tdie \"Error running git commit-tree: $?\\n\" if $?;\n+\n+\tsystem('git' , 'update-ref', \"$remote/$branch\", $cid) == 0\n+\t\tor die \"Cannot write branch $branch for update: $!\\n\";\n+\n+\tif ($revision_map) {\n+\t\tprint $revision_map \"@$_ $cid\\n\" for @commit_revisions;\n+\t}\n+\t@commit_revisions = ();\n+\n+\tif ($tag) {\n+\t        my ($xtag) = $tag;\n+\t\t$xtag =~ s/\\s+\\*\\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY **\n+\t\t$xtag =~ tr/_/\\./ if ( $opt_u );\n+\t\t$xtag =~ s/[\\/]/$opt_s/g;\n+\n+\t\t# See refs.c for these rules.\n+\t\t# Tag cannot contain bad chars. (See bad_ref_char in refs.c.)\n+\t\t$xtag =~ s/[ ~\\^:\\\\\\*\\?\\[]//g;\n+\t\t# Other bad strings for tags:\n+\t\t# (See check_refname_component in refs.c.)\n+\t\t1 while $xtag =~ s/\n+\t\t\t(?: \\.\\.        # Tag cannot contain '..'.\n+\t\t\t|   \\@{         # Tag cannot contain '@{'.\n+\t\t\t| ^ -           # Tag cannot begin with '-'.\n+\t\t\t|   \\.lock $    # Tag cannot end with '.lock'.\n+\t\t\t| ^ \\.          # Tag cannot begin...\n+\t\t\t|   \\. $        # ...or end with '.'\n+\t\t\t)//xg;\n+\t\t# Tag cannot be empty.\n+\t\tif ($xtag eq '') {\n+\t\t\twarn(\"warning: ignoring tag '$tag'\",\n+\t\t\t\" with invalid tagname\\n\");\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tif (system('git' , 'tag', '-f', $xtag, $cid) != 0) {\n+\t\t\t# We did our best to sanitize the tag, but still failed\n+\t\t\t# for whatever reason. Bail out, and give the user\n+\t\t\t# enough information to understand if/how we should\n+\t\t\t# improve the translation in the future.\n+\t\t\tif ($tag ne $xtag) {\n+\t\t\t\tprint \"Translated '$tag' tag to '$xtag'\\n\";\n+\t\t\t}\n+\t\t\tdie \"Cannot create tag $xtag: $!\\n\";\n+\t\t}\n+\n+\t\tprint \"Created tag '$xtag' on '$branch'\\n\" if $opt_v;\n+\t}\n+};\n+\n+my $commitcount = 1;\n+while (<CVS>) {\n+\tchomp;\n+\tif ($state == 0 and /^-+$/) {\n+\t\t$state = 1;\n+\t} elsif ($state == 0) {\n+\t\t$state = 1;\n+\t\tredo;\n+\t} elsif (($state==0 or $state==1) and s/^PatchSet\\s+//) {\n+\t\t$patchset = 0+$_;\n+\t\t$state=2;\n+\t} elsif ($state == 2 and s/^Date:\\s+//) {\n+\t\t$date = pdate($_);\n+\t\tunless ($date) {\n+\t\t\tprint STDERR \"Could not parse date: $_\\n\";\n+\t\t\t$state=0;\n+\t\t\tnext;\n+\t\t}\n+\t\t$state=3;\n+\t} elsif ($state == 3 and s/^Author:\\s+//) {\n+\t\t$author_tz = \"UTC\";\n+\t\ts/\\s+$//;\n+\t\tif (/^(.*?)\\s+<(.*)>/) {\n+\t\t    ($author_name, $author_email) = ($1, $2);\n+\t\t} elsif ($conv_author_name{$_}) {\n+\t\t\t$author_name = $conv_author_name{$_};\n+\t\t\t$author_email = $conv_author_email{$_};\n+\t\t\t$author_tz = $conv_author_tz{$_} if ($conv_author_tz{$_});\n+\t\t} else {\n+\t\t    $author_name = $author_email = $_;\n+\t\t}\n+\t\t$state = 4;\n+\t} elsif ($state == 4 and s/^Branch:\\s+//) {\n+\t\ts/\\s+$//;\n+\t\ttr/_/\\./ if ( $opt_u );\n+\t\ts/[\\/]/$opt_s/g;\n+\t\t$branch = $_;\n+\t\t$state = 5;\n+\t} elsif ($state == 5 and s/^Ancestor branch:\\s+//) {\n+\t\ts/\\s+$//;\n+\t\t$ancestor = $_;\n+\t\t$ancestor = $opt_o if $ancestor eq \"HEAD\";\n+\t\t$state = 6;\n+\t} elsif ($state == 5) {\n+\t\t$ancestor = undef;\n+\t\t$state = 6;\n+\t\tredo;\n+\t} elsif ($state == 6 and s/^Tag:\\s+//) {\n+\t\ts/\\s+$//;\n+\t\tif ($_ eq \"(none)\") {\n+\t\t\t$tag = undef;\n+\t\t} else {\n+\t\t\t$tag = $_;\n+\t\t}\n+\t\t$state = 7;\n+\t} elsif ($state == 7 and /^Log:/) {\n+\t\t$logmsg = \"\";\n+\t\t$state = 8;\n+\t} elsif ($state == 8 and /^Members:/) {\n+\t\t$branch = $opt_o if $branch eq \"HEAD\";\n+\t\tif (defined $branch_date{$branch} and $branch_date{$branch} >= $date) {\n+\t\t\t# skip\n+\t\t\tprint \"skip patchset $patchset: $date before $branch_date{$branch}\\n\" if $opt_v;\n+\t\t\t$state = 11;\n+\t\t\tnext;\n+\t\t}\n+\t\tif (!$opt_a && $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {\n+\t\t\t# skip if the commit is too recent\n+\t\t\t# given that the cvsps default fuzz is 300s, we give ourselves another\n+\t\t\t# 300s just in case -- this also prevents skipping commits\n+\t\t\t# due to server clock drift\n+\t\t\tprint \"skip patchset $patchset: $date too recent\\n\" if $opt_v;\n+\t\t\t$state = 11;\n+\t\t\tnext;\n+\t\t}\n+\t\tif (exists $ignorebranch{$branch}) {\n+\t\t\tprint STDERR \"Skipping $branch\\n\";\n+\t\t\t$state = 11;\n+\t\t\tnext;\n+\t\t}\n+\t\tif ($ancestor) {\n+\t\t\tif ($ancestor eq $branch) {\n+\t\t\t\tprint STDERR \"Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\\n\";\n+\t\t\t\t$ancestor = $opt_o;\n+\t\t\t}\n+\t\t\tif (defined get_headref(\"$remote/$branch\")) {\n+\t\t\t\tprint STDERR \"Branch $branch already exists!\\n\";\n+\t\t\t\t$state=11;\n+\t\t\t\tnext;\n+\t\t\t}\n+\t\t\tmy $id = get_headref(\"$remote/$ancestor\");\n+\t\t\tif (!$id) {\n+\t\t\t\tprint STDERR \"Branch $ancestor does not exist!\\n\";\n+\t\t\t\t$ignorebranch{$branch} = 1;\n+\t\t\t\t$state=11;\n+\t\t\t\tnext;\n+\t\t\t}\n+\n+\t\t\tsystem(qw(git update-ref -m cvsimport),\n+\t\t\t\t\"$remote/$branch\", $id);\n+\t\t\tif($? != 0) {\n+\t\t\t\tprint STDERR \"Could not create branch $branch\\n\";\n+\t\t\t\t$ignorebranch{$branch} = 1;\n+\t\t\t\t$state=11;\n+\t\t\t\tnext;\n+\t\t\t}\n+\t\t}\n+\t\t$last_branch = $branch if $branch ne $last_branch;\n+\t\t$state = 9;\n+\t} elsif ($state == 8) {\n+\t\t$logmsg .= \"$_\\n\";\n+\t} elsif ($state == 9 and /^\\s+(.+?):(INITIAL|\\d+(?:\\.\\d+)+)->(\\d+(?:\\.\\d+)+)\\s*$/) {\n+#\tVERSION:1.96->1.96.2.1\n+\t\tmy $init = ($2 eq \"INITIAL\");\n+\t\tmy $fn = $1;\n+\t\tmy $rev = $3;\n+\t\t$fn =~ s#^/+##;\n+\t\tif ($opt_S && $fn =~ m/$opt_S/) {\n+\t\t    print \"SKIPPING $fn v $rev\\n\";\n+\t\t    push(@skipped, $fn);\n+\t\t    next;\n+\t\t}\n+\t\tpush @commit_revisions, [$fn, $rev];\n+\t\tprint \"Fetching $fn   v $rev\\n\" if $opt_v;\n+\t\tmy ($tmpname, $size) = $cvs->file($fn,$rev);\n+\t\tif ($size == -1) {\n+\t\t\tpush(@old,$fn);\n+\t\t\tprint \"Drop $fn\\n\" if $opt_v;\n+\t\t} else {\n+\t\t\tprint \"\".($init ? \"New\" : \"Update\").\" $fn: $size bytes\\n\" if $opt_v;\n+\t\t\tmy $pid = open(my $F, '-|');\n+\t\t\tdie $! unless defined $pid;\n+\t\t\tif (!$pid) {\n+\t\t\t    exec(\"git\", \"hash-object\", \"-w\", $tmpname)\n+\t\t\t\tor die \"Cannot create object: $!\\n\";\n+\t\t\t}\n+\t\t\tmy $sha = <$F>;\n+\t\t\tchomp $sha;\n+\t\t\tclose $F;\n+\t\t\tmy $mode = pmode($cvs->{'mode'});\n+\t\t\tpush(@new,[$mode, $sha, $fn]); # may be resurrected!\n+\t\t}\n+\t\tunlink($tmpname);\n+\t} elsif ($state == 9 and /^\\s+(.+?):\\d+(?:\\.\\d+)+->(\\d+(?:\\.\\d+)+)\\(DEAD\\)\\s*$/) {\n+\t\tmy $fn = $1;\n+\t\tmy $rev = $2;\n+\t\t$fn =~ s#^/+##;\n+\t\tpush @commit_revisions, [$fn, $rev];\n+\t\tpush(@old,$fn);\n+\t\tprint \"Delete $fn\\n\" if $opt_v;\n+\t} elsif ($state == 9 and /^\\s*$/) {\n+\t\t$state = 10;\n+\t} elsif (($state == 9 or $state == 10) and /^-+$/) {\n+\t\t$commitcount++;\n+\t\tif ($opt_L && $commitcount > $opt_L) {\n+\t\t\tlast;\n+\t\t}\n+\t\tcommit();\n+\t\tif (($commitcount & 1023) == 0) {\n+\t\t\tsystem(qw(git repack -a -d));\n+\t\t}\n+\t\t$state = 1;\n+\t} elsif ($state == 11 and /^-+$/) {\n+\t\t$state = 1;\n+\t} elsif (/^-+$/) { # end of unknown-line processing\n+\t\t$state = 1;\n+\t} elsif ($state != 11) { # ignore stuff when skipping\n+\t\tprint STDERR \"* UNKNOWN LINE * $_\\n\";\n+\t}\n+}\n+commit() if $branch and $state != 11;\n+\n+unless ($opt_P) {\n+\tunlink($cvspsfile);\n+}\n+\n+# The heuristic of repacking every 1024 commits can leave a\n+# lot of unpacked data.  If there is more than 1MB worth of\n+# not-packed objects, repack once more.\n+my $line = `git count-objects`;\n+if ($line =~ /^(\\d+) objects, (\\d+) kilobytes$/) {\n+  my ($n_objects, $kb) = ($1, $2);\n+  1024 < $kb\n+    and system(qw(git repack -a -d));\n+}\n+\n+foreach my $git_index (values %index) {\n+    if ($git_index ne \"$git_dir/index\") {\n+\tunlink($git_index);\n+    }\n+}\n+\n+if (defined $orig_git_index) {\n+\t$ENV{GIT_INDEX_FILE} = $orig_git_index;\n+} else {\n+\tdelete $ENV{GIT_INDEX_FILE};\n+}\n+\n+# Now switch back to the branch we were in before all of this happened\n+if ($orig_branch) {\n+\tprint \"DONE.\\n\" if $opt_v;\n+\tif ($opt_i) {\n+\t\texit 0;\n+\t}\n+\tmy $tip_at_end = `git rev-parse --verify HEAD`;\n+\tif ($tip_at_start ne $tip_at_end) {\n+\t\tfor ($tip_at_start, $tip_at_end) { chomp; }\n+\t\tprint \"Fetched into the current branch.\\n\" if $opt_v;\n+\t\tsystem(qw(git read-tree -u -m),\n+\t\t       $tip_at_start, $tip_at_end);\n+\t\tdie \"Fast-forward update failed: $?\\n\" if $?;\n+\t}\n+\telse {\n+\t\tsystem(qw(git merge cvsimport HEAD), \"$remote/$opt_o\");\n+\t\tdie \"Could not merge $opt_o into the current branch.\\n\" if $?;\n+\t}\n+} else {\n+\t$orig_branch = \"master\";\n+\tprint \"DONE; creating $orig_branch branch\\n\" if $opt_v;\n+\tsystem(\"git\", \"update-ref\", \"refs/heads/master\", \"$remote/$opt_o\")\n+\t\tunless defined get_headref('refs/heads/master');\n+\tsystem(\"git\", \"symbolic-ref\", \"$remote/HEAD\", \"$remote/$opt_o\")\n+\t\tif ($opt_r && $opt_o ne 'HEAD');\n+\tsystem('git', 'update-ref', 'HEAD', \"$orig_branch\");\n+\tunless ($opt_i) {\n+\t\tsystem(qw(git checkout -f));\n+\t\tdie \"checkout failed: $?\\n\" if $?;\n+\t}\n+}\ndiff --git a/git-cvsimport.perl b/git-cvsimport.perl\ndeleted file mode 100755\nindex ad460a5..0000000\n--- a/git-cvsimport.perl\n+++ /dev/null\n@@ -1,1179 +0,0 @@\n-#!/usr/bin/perl\n-\n-# This tool is copyright (c) 2005, Matthias Urlichs.\n-# It is released under the Gnu Public License, version 2.\n-#\n-# The basic idea is to aggregate CVS check-ins into related changes.\n-# Fortunately, \"cvsps\" does that for us; all we have to do is to parse\n-# its output.\n-#\n-# Checking out the files is done by a single long-running CVS connection\n-# / server process.\n-#\n-# The head revision is on branch \"origin\" by default.\n-# You can change that with the '-o' option.\n-\n-use 5.008;\n-use strict;\n-use warnings;\n-use Getopt::Long;\n-use File::Spec;\n-use File::Temp qw(tempfile tmpnam);\n-use File::Path qw(mkpath);\n-use File::Basename qw(basename dirname);\n-use Time::Local;\n-use IO::Socket;\n-use IO::Pipe;\n-use POSIX qw(strftime tzset dup2 ENOENT);\n-use IPC::Open2;\n-\n-$SIG{'PIPE'}=\"IGNORE\";\n-set_timezone('UTC');\n-\n-our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r, $opt_R);\n-my (%conv_author_name, %conv_author_email, %conv_author_tz);\n-\n-my $cvsps2 = \"@@CVSPS2_PATH@@\";\n-\n-sub usage(;$) {\n-\tmy $msg = shift;\n-\tprint(STDERR \"Error: $msg\\n\") if $msg;\n-\tprint STDERR <<END;\n-Usage: git cvsimport     # fetch/update GIT from CVS\n-       [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]\n-       [-p opts-for-cvsps] [-P file] [-C GIT_repository] [-z fuzz] [-i] [-k]\n-       [-u] [-s subst] [-a] [-m] [-M regex] [-S regex] [-L commitlimit]\n-       [-r remote] [-R] [CVS_module]\n-END\n-\texit(1);\n-}\n-\n-sub read_author_info($) {\n-\tmy ($file) = @_;\n-\tmy $user;\n-\topen my $f, '<', \"$file\" or die(\"Failed to open $file: $!\\n\");\n-\n-\twhile (<$f>) {\n-\t\t# Expected format is this:\n-\t\t#   exon=Andreas Ericsson <ae@op5.se>\n-\t\tif (m/^(\\S+?)\\s*=\\s*(.+?)\\s*<(.+)>\\s*$/) {\n-\t\t\t$user = $1;\n-\t\t\t$conv_author_name{$user} = $2;\n-\t\t\t$conv_author_email{$user} = $3;\n-\t\t}\n-\t\t# or with an optional timezone:\n-\t\t#   spawn=Simon Pawn <spawn@frog-pond.org> America/Chicago\n-\t\telsif (m/^(\\S+?)\\s*=\\s*(.+?)\\s*<(.+)>\\s*(\\S+?)\\s*$/) {\n-\t\t\t$user = $1;\n-\t\t\t$conv_author_name{$user} = $2;\n-\t\t\t$conv_author_email{$user} = $3;\n-\t\t\t$conv_author_tz{$user} = $4;\n-\t\t}\n-\t\t# However, we also read from CVSROOT/users format\n-\t\t# to ease migration.\n-\t\telsif (/^(\\w+):(['\"]?)(.+?)\\2\\s*$/) {\n-\t\t\tmy $mapped;\n-\t\t\t($user, $mapped) = ($1, $3);\n-\t\t\tif ($mapped =~ /^\\s*(.*?)\\s*<(.*)>\\s*$/) {\n-\t\t\t\t$conv_author_name{$user} = $1;\n-\t\t\t\t$conv_author_email{$user} = $2;\n-\t\t\t}\n-\t\t\telsif ($mapped =~ /^<?(.*)>?$/) {\n-\t\t\t\t$conv_author_name{$user} = $user;\n-\t\t\t\t$conv_author_email{$user} = $1;\n-\t\t\t}\n-\t\t}\n-\t\t# NEEDSWORK: Maybe warn on unrecognized lines?\n-\t}\n-\tclose ($f);\n-}\n-\n-sub write_author_info($) {\n-\tmy ($file) = @_;\n-\topen my $f, '>', $file or\n-\t  die(\"Failed to open $file for writing: $!\");\n-\n-\tforeach (keys %conv_author_name) {\n-\t\tprint $f \"$_=$conv_author_name{$_} <$conv_author_email{$_}>\";\n-\t\tprint $f \" $conv_author_tz{$_}\" if ($conv_author_tz{$_});\n-\t\tprint $f \"\\n\";\n-\t}\n-\tclose ($f);\n-}\n-\n-# Versions of perl before 5.10.0 may not automatically check $TZ each\n-# time localtime is run (most platforms will do so only the first time).\n-# We can work around this by using tzset() to update the internal\n-# variable whenever we change the environment.\n-sub set_timezone {\n-\t$ENV{TZ} = shift;\n-\ttzset();\n-}\n-\n-# convert getopts specs for use by git config\n-my %longmap = (\n-\t'A:' => 'authors-file',\n-\t'M:' => 'merge-regex',\n-\t'P:' => undef,\n-\t'R' => 'track-revisions',\n-\t'S:' => 'ignore-paths',\n-);\n-\n-sub read_repo_config {\n-\t# Split the string between characters, unless there is a ':'\n-\t# So \"abc:de\" becomes [\"a\", \"b\", \"c:\", \"d\", \"e\"]\n-\tmy @opts = split(/ *(?!:)/, shift);\n-\tforeach my $o (@opts) {\n-\t\tmy $key = $o;\n-\t\t$key =~ s/://g;\n-\t\tmy $arg = 'git config';\n-\t\t$arg .= ' --bool' if ($o !~ /:$/);\n-\t\tmy $ckey = $key;\n-\n-\t\tif (exists $longmap{$o}) {\n-\t\t\t# An uppercase option like -R cannot be\n-\t\t\t# expressed in the configuration, as the\n-\t\t\t# variable names are downcased.\n-\t\t\t$ckey = $longmap{$o};\n-\t\t\tnext if (! defined $ckey);\n-\t\t\t$ckey =~ s/-//g;\n-\t\t}\n-\t\tchomp(my $tmp = `$arg --get cvsimport.$ckey`);\n-\t\tif ($tmp && !($arg =~ /--bool/ && $tmp eq 'false')) {\n-\t\t\tno strict 'refs';\n-\t\t\tmy $opt_name = \"opt_\" . $key;\n-\t\t\tif (!$$opt_name) {\n-\t\t\t\t$$opt_name = $tmp;\n-\t\t\t}\n-\t\t}\n-\t}\n-}\n-\n-my $opts = \"haivmkuo:d:p:r:C:z:s:M:P:A:S:L:R\";\n-read_repo_config($opts);\n-Getopt::Long::Configure( 'no_ignore_case', 'bundling' );\n-\n-# turn the Getopt::Std specification in a Getopt::Long one,\n-# with support for multiple -M options\n-GetOptions( map { s/:/=s/; /M/ ? \"$_\\@\" : $_ } split( /(?!:)/, $opts ) )\n-    or usage();\n-usage if $opt_h;\n-\n-if (@ARGV == 0) {\n-\t\tchomp(my $module = `git config --get cvsimport.module`);\n-\t\tpush(@ARGV, $module) if $? == 0;\n-}\n-@ARGV <= 1 or usage(\"You can't specify more than one CVS module\");\n-\n-if ($opt_d) {\n-\t$ENV{\"CVSROOT\"} = $opt_d;\n-} elsif (-f 'CVS/Root') {\n-\topen my $f, '<', 'CVS/Root' or die 'Failed to open CVS/Root';\n-\t$opt_d = <$f>;\n-\tchomp $opt_d;\n-\tclose $f;\n-\t$ENV{\"CVSROOT\"} = $opt_d;\n-} elsif ($ENV{\"CVSROOT\"}) {\n-\t$opt_d = $ENV{\"CVSROOT\"};\n-} else {\n-\tusage(\"CVSROOT needs to be set\");\n-}\n-$opt_s ||= \"-\";\n-$opt_a ||= 0;\n-\n-my $git_tree = $opt_C;\n-$git_tree ||= \".\";\n-\n-my $remote;\n-if (defined $opt_r) {\n-\t$remote = 'refs/remotes/' . $opt_r;\n-\t$opt_o ||= \"master\";\n-} else {\n-\t$opt_o ||= \"origin\";\n-\t$remote = 'refs/heads';\n-}\n-\n-my $cvs_tree;\n-if ($#ARGV == 0) {\n-\t$cvs_tree = $ARGV[0];\n-} elsif (-f 'CVS/Repository') {\n-\topen my $f, '<', 'CVS/Repository' or\n-\t    die 'Failed to open CVS/Repository';\n-\t$cvs_tree = <$f>;\n-\tchomp $cvs_tree;\n-\tclose $f;\n-} else {\n-\tusage(\"CVS module has to be specified\");\n-}\n-\n-our @mergerx = ();\n-if ($opt_m) {\n-\t@mergerx = ( qr/\\b(?:from|of|merge|merging|merged) ([-\\w]+)/i );\n-}\n-if (@opt_M) {\n-\tpush (@mergerx, map { qr/$_/ } @opt_M);\n-}\n-\n-# Remember UTC of our starting time\n-# we'll want to avoid importing commits\n-# that are too recent\n-our $starttime = time();\n-\n-select(STDERR); $|=1; select(STDOUT);\n-\n-\n-package CVSconn;\n-# Basic CVS dialog.\n-# We're only interested in connecting and downloading, so ...\n-\n-use File::Spec;\n-use File::Temp qw(tempfile);\n-use POSIX qw(strftime dup2);\n-\n-sub new {\n-\tmy ($what,$repo,$subdir) = @_;\n-\t$what=ref($what) if ref($what);\n-\n-\tmy $self = {};\n-\t$self->{'buffer'} = \"\";\n-\tbless($self,$what);\n-\n-\t$repo =~ s#/+$##;\n-\t$self->{'fullrep'} = $repo;\n-\t$self->conn();\n-\n-\t$self->{'subdir'} = $subdir;\n-\t$self->{'lines'} = undef;\n-\n-\treturn $self;\n-}\n-\n-sub find_password_entry {\n-\tmy ($cvspass, @cvsroot) = @_;\n-\tmy ($file, $delim) = @$cvspass;\n-\tmy $pass;\n-\tlocal ($_);\n-\n-\tif (open(my $fh, $file)) {\n-\t\t# :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z\n-\t\tCVSPASSFILE:\n-\t\twhile (<$fh>) {\n-\t\t\tchomp;\n-\t\t\ts/^\\/\\d+\\s+//;\n-\t\t\tmy ($w, $p) = split($delim,$_,2);\n-\t\t\tfor my $cvsroot (@cvsroot) {\n-\t\t\t\tif ($w eq $cvsroot) {\n-\t\t\t\t\t$pass = $p;\n-\t\t\t\t\tlast CVSPASSFILE;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t}\n-\t\tclose($fh);\n-\t}\n-\treturn $pass;\n-}\n-\n-sub conn {\n-\tmy $self = shift;\n-\tmy $repo = $self->{'fullrep'};\n-\tif ($repo =~ s/^:pserver(?:([^:]*)):(?:(.*?)(?::(.*?))?@)?([^:\\/]*)(?::(\\d*))?//) {\n-\t\tmy ($param,$user,$pass,$serv,$port) = ($1,$2,$3,$4,$5);\n-\n-\t\tmy ($proxyhost,$proxyport);\n-\t\tif ($param && ($param =~ m/proxy=([^;]+)/)) {\n-\t\t\t$proxyhost = $1;\n-\t\t\t# Default proxyport, if not specified, is 8080.\n-\t\t\t$proxyport = 8080;\n-\t\t\tif ($ENV{\"CVS_PROXY_PORT\"}) {\n-\t\t\t\t$proxyport = $ENV{\"CVS_PROXY_PORT\"};\n-\t\t\t}\n-\t\t\tif ($param =~ m/proxyport=([^;]+)/) {\n-\t\t\t\t$proxyport = $1;\n-\t\t\t}\n-\t\t}\n-\t\t$repo ||= '/';\n-\n-\t\t# if username is not explicit in CVSROOT, then use current user, as cvs would\n-\t\t$user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || \"anonymous\") unless $user;\n-\t\tmy $rr2 = \"-\";\n-\t\tunless ($port) {\n-\t\t\t$rr2 = \":pserver:$user\\@$serv:$repo\";\n-\t\t\t$port=2401;\n-\t\t}\n-\t\tmy $rr = \":pserver:$user\\@$serv:$port$repo\";\n-\n-\t\tif ($pass) {\n-\t\t\t$pass = $self->_scramble($pass);\n-\t\t} else {\n-\t\t\tmy @cvspass = ([$ENV{'HOME'}.\"/.cvspass\", qr/\\s/],\n-\t\t\t\t       [$ENV{'HOME'}.\"/.cvs/cvspass\", qr/=/]);\n-\t\t\tmy @loc = ();\n-\t\t\tforeach my $cvspass (@cvspass) {\n-\t\t\t\tmy $p = find_password_entry($cvspass, $rr, $rr2);\n-\t\t\t\tif ($p) {\n-\t\t\t\t\tpush @loc, $cvspass->[0];\n-\t\t\t\t\t$pass = $p;\n-\t\t\t\t}\n-\t\t\t}\n-\n-\t\t\tif (1 < @loc) {\n-\t\t\t\tdie(\"Multiple cvs password files have \".\n-\t\t\t\t    \"entries for CVSROOT $opt_d: @loc\");\n-\t\t\t} elsif (!$pass) {\n-\t\t\t\t$pass = \"A\";\n-\t\t\t}\n-\t\t}\n-\n-\t\tmy ($s, $rep);\n-\t\tif ($proxyhost) {\n-\n-\t\t\t# Use a HTTP Proxy. Only works for HTTP proxies that\n-\t\t\t# don't require user authentication\n-\t\t\t#\n-\t\t\t# See: http://www.ietf.org/rfc/rfc2817.txt\n-\n-\t\t\t$s = IO::Socket::INET->new(PeerHost => $proxyhost, PeerPort => $proxyport);\n-\t\t\tdie \"Socket to $proxyhost: $!\\n\" unless defined $s;\n-\t\t\t$s->write(\"CONNECT $serv:$port HTTP/1.1\\r\\nHost: $serv:$port\\r\\n\\r\\n\")\n-\t                        or die \"Write to $proxyhost: $!\\n\";\n-\t                $s->flush();\n-\n-\t\t\t$rep = <$s>;\n-\n-\t\t\t# The answer should look like 'HTTP/1.x 2yy ....'\n-\t\t\tif (!($rep =~ m#^HTTP/1\\.. 2[0-9][0-9]#)) {\n-\t\t\t\tdie \"Proxy connect: $rep\\n\";\n-\t\t\t}\n-\t\t\t# Skip up to the empty line of the proxy server output\n-\t\t\t# including the response headers.\n-\t\t\twhile ($rep = <$s>) {\n-\t\t\t\tlast if (!defined $rep ||\n-\t\t\t\t\t $rep eq \"\\n\" ||\n-\t\t\t\t\t $rep eq \"\\r\\n\");\n-\t\t\t}\n-\t\t} else {\n-\t\t\t$s = IO::Socket::INET->new(PeerHost => $serv, PeerPort => $port);\n-\t\t\tdie \"Socket to $serv: $!\\n\" unless defined $s;\n-\t\t}\n-\n-\t\t$s->write(\"BEGIN AUTH REQUEST\\n$repo\\n$user\\n$pass\\nEND AUTH REQUEST\\n\")\n-\t\t\tor die \"Write to $serv: $!\\n\";\n-\t\t$s->flush();\n-\n-\t\t$rep = <$s>;\n-\n-\t\tif ($rep ne \"I LOVE YOU\\n\") {\n-\t\t\t$rep=\"<unknown>\" unless $rep;\n-\t\t\tdie \"AuthReply: $rep\\n\";\n-\t\t}\n-\t\t$self->{'socketo'} = $s;\n-\t\t$self->{'socketi'} = $s;\n-\t} else { # local or ext: Fork off our own cvs server.\n-\t\tmy $pr = IO::Pipe->new();\n-\t\tmy $pw = IO::Pipe->new();\n-\t\tmy $pid = fork();\n-\t\tdie \"Fork: $!\\n\" unless defined $pid;\n-\t\tmy $cvs = 'cvs';\n-\t\t$cvs = $ENV{CVS_SERVER} if exists $ENV{CVS_SERVER};\n-\t\tmy $rsh = 'rsh';\n-\t\t$rsh = $ENV{CVS_RSH} if exists $ENV{CVS_RSH};\n-\n-\t\tmy @cvs = ($cvs, 'server');\n-\t\tmy ($local, $user, $host);\n-\t\t$local = $repo =~ s/:local://;\n-\t\tif (!$local) {\n-\t\t    $repo =~ s/:ext://;\n-\t\t    $local = !($repo =~ s/^(?:([^\\@:]+)\\@)?([^:]+)://);\n-\t\t    ($user, $host) = ($1, $2);\n-\t\t}\n-\t\tif (!$local) {\n-\t\t    if ($user) {\n-\t\t\tunshift @cvs, $rsh, '-l', $user, $host;\n-\t\t    } else {\n-\t\t\tunshift @cvs, $rsh, $host;\n-\t\t    }\n-\t\t}\n-\n-\t\tunless ($pid) {\n-\t\t\t$pr->writer();\n-\t\t\t$pw->reader();\n-\t\t\tdup2($pw->fileno(),0);\n-\t\t\tdup2($pr->fileno(),1);\n-\t\t\t$pr->close();\n-\t\t\t$pw->close();\n-\t\t\texec(@cvs);\n-\t\t}\n-\t\t$pw->writer();\n-\t\t$pr->reader();\n-\t\t$self->{'socketo'} = $pw;\n-\t\t$self->{'socketi'} = $pr;\n-\t}\n-\t$self->{'socketo'}->write(\"Root $repo\\n\");\n-\n-\t# Trial and error says that this probably is the minimum set\n-\t$self->{'socketo'}->write(\"Valid-responses ok error Valid-requests Mode M Mbinary E Checked-in Created Updated Merged Removed\\n\");\n-\n-\t$self->{'socketo'}->write(\"valid-requests\\n\");\n-\t$self->{'socketo'}->flush();\n-\n-\tmy $rep=$self->readline();\n-\tdie \"Failed to read from server\" unless defined $rep;\n-\tchomp($rep);\n-\tif ($rep !~ s/^Valid-requests\\s*//) {\n-\t\t$rep=\"<unknown>\" unless $rep;\n-\t\tdie \"Expected Valid-requests from server, but got: $rep\\n\";\n-\t}\n-\tchomp(my $res=$self->readline());\n-\tdie \"validReply: $res\\n\" if $res ne \"ok\";\n-\n-\t$self->{'socketo'}->write(\"UseUnchanged\\n\") if $rep =~ /\\bUseUnchanged\\b/;\n-\t$self->{'repo'} = $repo;\n-}\n-\n-sub readline {\n-\tmy ($self) = @_;\n-\treturn $self->{'socketi'}->getline();\n-}\n-\n-sub _file {\n-\t# Request a file with a given revision.\n-\t# Trial and error says this is a good way to do it. :-/\n-\tmy ($self,$fn,$rev) = @_;\n-\t$self->{'socketo'}->write(\"Argument -N\\n\") or return undef;\n-\t$self->{'socketo'}->write(\"Argument -P\\n\") or return undef;\n-\t# -kk: Linus' version doesn't use it - defaults to off\n-\tif ($opt_k) {\n-\t    $self->{'socketo'}->write(\"Argument -kk\\n\") or return undef;\n-\t}\n-\t$self->{'socketo'}->write(\"Argument -r\\n\") or return undef;\n-\t$self->{'socketo'}->write(\"Argument $rev\\n\") or return undef;\n-\t$self->{'socketo'}->write(\"Argument --\\n\") or return undef;\n-\t$self->{'socketo'}->write(\"Argument $self->{'subdir'}/$fn\\n\") or return undef;\n-\t$self->{'socketo'}->write(\"Directory .\\n\") or return undef;\n-\t$self->{'socketo'}->write(\"$self->{'repo'}\\n\") or return undef;\n-\t# $self->{'socketo'}->write(\"Sticky T1.0\\n\") or return undef;\n-\t$self->{'socketo'}->write(\"co\\n\") or return undef;\n-\t$self->{'socketo'}->flush() or return undef;\n-\t$self->{'lines'} = 0;\n-\treturn 1;\n-}\n-sub _line {\n-\t# Read a line from the server.\n-\t# ... except that 'line' may be an entire file. ;-)\n-\tmy ($self, $fh) = @_;\n-\tdie \"Not in lines\" unless defined $self->{'lines'};\n-\n-\tmy $line;\n-\tmy $res=0;\n-\twhile (defined($line = $self->readline())) {\n-\t\t# M U gnupg-cvs-rep/AUTHORS\n-\t\t# Updated gnupg-cvs-rep/\n-\t\t# /daten/src/rsync/gnupg-cvs-rep/AUTHORS\n-\t\t# /AUTHORS/1.1///T1.1\n-\t\t# u=rw,g=rw,o=rw\n-\t\t# 0\n-\t\t# ok\n-\n-\t\tif ($line =~ s/^(?:Created|Updated) //) {\n-\t\t\t$line = $self->readline(); # path\n-\t\t\t$line = $self->readline(); # Entries line\n-\t\t\tmy $mode = $self->readline(); chomp $mode;\n-\t\t\t$self->{'mode'} = $mode;\n-\t\t\tdefined (my $cnt = $self->readline())\n-\t\t\t\tor die \"EOF from server after 'Changed'\\n\";\n-\t\t\tchomp $cnt;\n-\t\t\tdie \"Duh: Filesize $cnt\" if $cnt !~ /^\\d+$/;\n-\t\t\t$line=\"\";\n-\t\t\t$res = $self->_fetchfile($fh, $cnt);\n-\t\t} elsif ($line =~ s/^ //) {\n-\t\t\tprint $fh $line;\n-\t\t\t$res += length($line);\n-\t\t} elsif ($line =~ /^M\\b/) {\n-\t\t\t# output, do nothing\n-\t\t} elsif ($line =~ /^Mbinary\\b/) {\n-\t\t\tmy $cnt;\n-\t\t\tdie \"EOF from server after 'Mbinary'\" unless defined ($cnt = $self->readline());\n-\t\t\tchomp $cnt;\n-\t\t\tdie \"Duh: Mbinary $cnt\" if $cnt !~ /^\\d+$/ or $cnt<1;\n-\t\t\t$line=\"\";\n-\t\t\t$res += $self->_fetchfile($fh, $cnt);\n-\t\t} else {\n-\t\t\tchomp $line;\n-\t\t\tif ($line eq \"ok\") {\n-\t\t\t\t# print STDERR \"S: ok (\".length($res).\")\\n\";\n-\t\t\t\treturn $res;\n-\t\t\t} elsif ($line =~ s/^E //) {\n-\t\t\t\t# print STDERR \"S: $line\\n\";\n-\t\t\t} elsif ($line =~ /^(Remove-entry|Removed) /i) {\n-\t\t\t\t$line = $self->readline(); # filename\n-\t\t\t\t$line = $self->readline(); # OK\n-\t\t\t\tchomp $line;\n-\t\t\t\tdie \"Unknown: $line\" if $line ne \"ok\";\n-\t\t\t\treturn -1;\n-\t\t\t} else {\n-\t\t\t\tdie \"Unknown: $line\\n\";\n-\t\t\t}\n-\t\t}\n-\t}\n-\treturn undef;\n-}\n-sub file {\n-\tmy ($self,$fn,$rev) = @_;\n-\tmy $res;\n-\n-\tmy ($fh, $name) = tempfile('gitcvs.XXXXXX',\n-\t\t    DIR => File::Spec->tmpdir(), UNLINK => 1);\n-\n-\t$self->_file($fn,$rev) and $res = $self->_line($fh);\n-\n-\tif (!defined $res) {\n-\t    print STDERR \"Server has gone away while fetching $fn $rev, retrying...\\n\";\n-\t    truncate $fh, 0;\n-\t    $self->conn();\n-\t    $self->_file($fn,$rev) or die \"No file command send\";\n-\t    $res = $self->_line($fh);\n-\t    die \"Retry failed\" unless defined $res;\n-\t}\n-\tclose ($fh);\n-\n-\treturn ($name, $res);\n-}\n-sub _fetchfile {\n-\tmy ($self, $fh, $cnt) = @_;\n-\tmy $res = 0;\n-\tmy $bufsize = 1024 * 1024;\n-\twhile ($cnt) {\n-\t    if ($bufsize > $cnt) {\n-\t\t$bufsize = $cnt;\n-\t    }\n-\t    my $buf;\n-\t    my $num = $self->{'socketi'}->read($buf,$bufsize);\n-\t    die \"Server: Filesize $cnt: $num: $!\\n\" if not defined $num or $num<=0;\n-\t    print $fh $buf;\n-\t    $res += $num;\n-\t    $cnt -= $num;\n-\t}\n-\treturn $res;\n-}\n-\n-sub _scramble {\n-\tmy ($self, $pass) = @_;\n-\tmy $scrambled = \"A\";\n-\n-\treturn $scrambled unless $pass;\n-\n-\tmy $pass_len = length($pass);\n-\tmy @pass_arr = split(\"\", $pass);\n-\tmy $i;\n-\n-\t# from cvs/src/scramble.c\n-\tmy @shifts = (\n-\t\t  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,\n-\t\t 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,\n-\t\t114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,\n-\t\t111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,\n-\t\t 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,\n-\t\t125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,\n-\t\t 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,\n-\t\t 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,\n-\t\t225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,\n-\t\t199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,\n-\t\t174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,\n-\t\t207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,\n-\t\t192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,\n-\t\t227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,\n-\t\t182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,\n-\t\t243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152\n-\t);\n-\n-\tfor ($i = 0; $i < $pass_len; $i++) {\n-\t\t$scrambled .= pack(\"C\", $shifts[ord($pass_arr[$i])]);\n-\t}\n-\n-\treturn $scrambled;\n-}\n-\n-package main;\n-\n-my $cvs = CVSconn->new($opt_d, $cvs_tree);\n-\n-\n-sub pdate($) {\n-\tmy ($d) = @_;\n-\tm#(\\d{2,4})/(\\d\\d)/(\\d\\d)\\s(\\d\\d):(\\d\\d)(?::(\\d\\d))?#\n-\t\tor die \"Unparseable date: $d\\n\";\n-\tmy $y=$1; $y-=1900 if $y>1900;\n-\treturn timegm($6||0,$5,$4,$3,$2-1,$y);\n-}\n-\n-sub pmode($) {\n-\tmy ($mode) = @_;\n-\tmy $m = 0;\n-\tmy $mm = 0;\n-\tmy $um = 0;\n-\tfor my $x(split(//,$mode)) {\n-\t\tif ($x eq \",\") {\n-\t\t\t$m |= $mm&$um;\n-\t\t\t$mm = 0;\n-\t\t\t$um = 0;\n-\t\t} elsif ($x eq \"u\") { $um |= 0700;\n-\t\t} elsif ($x eq \"g\") { $um |= 0070;\n-\t\t} elsif ($x eq \"o\") { $um |= 0007;\n-\t\t} elsif ($x eq \"r\") { $mm |= 0444;\n-\t\t} elsif ($x eq \"w\") { $mm |= 0222;\n-\t\t} elsif ($x eq \"x\") { $mm |= 0111;\n-\t\t} elsif ($x eq \"=\") { # do nothing\n-\t\t} else { die \"Unknown mode: $mode\\n\";\n-\t\t}\n-\t}\n-\t$m |= $mm&$um;\n-\treturn $m;\n-}\n-\n-sub getwd() {\n-\tmy $pwd = `pwd`;\n-\tchomp $pwd;\n-\treturn $pwd;\n-}\n-\n-sub is_sha1 {\n-\tmy $s = shift;\n-\treturn $s =~ /^[a-f0-9]{40}$/;\n-}\n-\n-sub get_headref ($) {\n-\tmy $name = shift;\n-\tmy $r = `git rev-parse --verify '$name' 2>/dev/null`;\n-\treturn undef unless $? == 0;\n-\tchomp $r;\n-\treturn $r;\n-}\n-\n-my $user_filename_prepend = '';\n-sub munge_user_filename {\n-\tmy $name = shift;\n-\treturn File::Spec->file_name_is_absolute($name) ?\n-\t\t$name :\n-\t\t$user_filename_prepend . $name;\n-}\n-\n--d $git_tree\n-\tor mkdir($git_tree,0777)\n-\tor die \"Could not create $git_tree: $!\";\n-if ($git_tree ne '.') {\n-\t$user_filename_prepend = getwd() . '/';\n-\tchdir($git_tree);\n-}\n-\n-my $last_branch = \"\";\n-my $orig_branch = \"\";\n-my %branch_date;\n-my $tip_at_start = undef;\n-\n-my $git_dir = $ENV{\"GIT_DIR\"} || \".git\";\n-$git_dir = getwd().\"/\".$git_dir unless $git_dir =~ m#^/#;\n-$ENV{\"GIT_DIR\"} = $git_dir;\n-my $orig_git_index;\n-$orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE};\n-\n-my %index; # holds filenames of one index per branch\n-\n-unless (-d $git_dir) {\n-\tsystem(qw(git init));\n-\tdie \"Cannot init the GIT db at $git_tree: $?\\n\" if $?;\n-\tsystem(qw(git read-tree --empty));\n-\tdie \"Cannot init an empty tree: $?\\n\" if $?;\n-\n-\t$last_branch = $opt_o;\n-\t$orig_branch = \"\";\n-} else {\n-\topen(F, \"-|\", qw(git symbolic-ref HEAD)) or\n-\t\tdie \"Cannot run git symbolic-ref: $!\\n\";\n-\tchomp ($last_branch = <F>);\n-\t$last_branch = basename($last_branch);\n-\tclose(F);\n-\tunless ($last_branch) {\n-\t\twarn \"Cannot read the last branch name: $! -- assuming 'master'\\n\";\n-\t\t$last_branch = \"master\";\n-\t}\n-\t$orig_branch = $last_branch;\n-\t$tip_at_start = `git rev-parse --verify HEAD`;\n-\n-\t# Get the last import timestamps\n-\tmy $fmt = '($ref, $author) = (%(refname), %(author));';\n-\tmy @cmd = ('git', 'for-each-ref', '--perl', \"--format=$fmt\", $remote);\n-\topen(H, \"-|\", @cmd) or die \"Cannot run git for-each-ref: $!\\n\";\n-\twhile (defined(my $entry = <H>)) {\n-\t\tmy ($ref, $author);\n-\t\teval($entry) || die \"cannot eval refs list: $@\";\n-\t\tmy ($head) = ($ref =~ m|^$remote/(.*)|);\n-\t\t$author =~ /^.*\\s(\\d+)\\s[-+]\\d{4}$/;\n-\t\t$branch_date{$head} = $1;\n-\t}\n-\tclose(H);\n-        if (!exists $branch_date{$opt_o}) {\n-\t\tdie \"Branch '$opt_o' does not exist.\\n\".\n-\t\t       \"Either use the correct '-o branch' option,\\n\".\n-\t\t       \"or import to a new repository.\\n\";\n-        }\n-}\n-\n--d $git_dir\n-\tor die \"Could not create git subdir ($git_dir).\\n\";\n-\n-# now we read (and possibly save) author-info as well\n--f \"$git_dir/cvs-authors\" and\n-  read_author_info(\"$git_dir/cvs-authors\");\n-if ($opt_A) {\n-\tread_author_info(munge_user_filename($opt_A));\n-\twrite_author_info(\"$git_dir/cvs-authors\");\n-}\n-\n-# open .git/cvs-revisions, if requested\n-open my $revision_map, '>>', \"$git_dir/cvs-revisions\"\n-    or die \"Can't open $git_dir/cvs-revisions for appending: $!\\n\"\n-\tif defined $opt_R;\n-\n-\n-#\n-# run cvsps into a file unless we are getting\n-# it passed as a file via $opt_P\n-#\n-my $cvspsfile;\n-unless ($opt_P) {\n-\tprint \"Running cvsps...\\n\" if $opt_v;\n-\tmy $pid = open(CVSPS,\"-|\");\n-\tmy $cvspsfh;\n-\tdie \"Cannot fork: $!\\n\" unless defined $pid;\n-\tunless ($pid) {\n-\t\tmy @opt;\n-\t\t@opt = split(/,/,$opt_p) if defined $opt_p;\n-\t\tunshift @opt, '-z', $opt_z if defined $opt_z;\n-\t\tunshift @opt, '-q'         unless defined $opt_v;\n-\t\tunless (defined($opt_p) && $opt_p =~ m/--no-cvs-direct/) {\n-\t\t\tpush @opt, '--cvs-direct';\n-\t\t}\n-\t\texec($cvsps2,\"--norc\",@opt,\"-u\",\"-A\",'--root',$opt_d,$cvs_tree);\n-\t\tdie \"Could not start cvsps: $!\\n\";\n-\t}\n-\t($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',\n-\t\t\t\t\t  DIR => File::Spec->tmpdir());\n-\twhile (<CVSPS>) {\n-\t    print $cvspsfh $_;\n-\t}\n-\tclose CVSPS;\n-\t$? == 0 or die \"git cvsimport: fatal: cvsps reported error\\n\";\n-\tclose $cvspsfh;\n-} else {\n-\t$cvspsfile = munge_user_filename($opt_P);\n-}\n-\n-open(CVS, \"<$cvspsfile\") or die $!;\n-\n-## cvsps output:\n-#---------------------\n-#PatchSet 314\n-#Date: 1999/09/18 13:03:59\n-#Author: wkoch\n-#Branch: STABLE-BRANCH-1-0\n-#Ancestor branch: HEAD\n-#Tag: (none)\n-#Log:\n-#    See ChangeLog: Sat Sep 18 13:03:28 CEST 1999  Werner Koch\n-#Members:\n-#\tREADME:1.57->1.57.2.1\n-#\tVERSION:1.96->1.96.2.1\n-#\n-#---------------------\n-\n-my $state = 0;\n-\n-sub update_index (\\@\\@) {\n-\tmy $old = shift;\n-\tmy $new = shift;\n-\topen(my $fh, '|-', qw(git update-index -z --index-info))\n-\t\tor die \"unable to open git update-index: $!\";\n-\tprint $fh\n-\t\t(map { \"0 0000000000000000000000000000000000000000\\t$_\\0\" }\n-\t\t\t@$old),\n-\t\t(map { '100' . sprintf('%o', $_->[0]) . \" $_->[1]\\t$_->[2]\\0\" }\n-\t\t\t@$new)\n-\t\tor die \"unable to write to git update-index: $!\";\n-\tclose $fh\n-\t\tor die \"unable to write to git update-index: $!\";\n-\t$? and die \"git update-index reported error: $?\";\n-}\n-\n-sub write_tree () {\n-\topen(my $fh, '-|', qw(git write-tree))\n-\t\tor die \"unable to open git write-tree: $!\";\n-\tchomp(my $tree = <$fh>);\n-\tis_sha1($tree)\n-\t\tor die \"Cannot get tree id ($tree): $!\";\n-\tclose($fh)\n-\t\tor die \"Error running git write-tree: $?\\n\";\n-\tprint \"Tree ID $tree\\n\" if $opt_v;\n-\treturn $tree;\n-}\n-\n-my ($patchset,$date,$author_name,$author_email,$author_tz,$branch,$ancestor,$tag,$logmsg);\n-my (@old,@new,@skipped,%ignorebranch,@commit_revisions);\n-\n-# commits that cvsps cannot place anywhere...\n-$ignorebranch{'#CVSPS_NO_BRANCH'} = 1;\n-\n-sub commit {\n-\tif ($branch eq $opt_o && !$index{branch} &&\n-\t\t!get_headref(\"$remote/$branch\")) {\n-\t    # looks like an initial commit\n-\t    # use the index primed by git init\n-\t    $ENV{GIT_INDEX_FILE} = \"$git_dir/index\";\n-\t    $index{$branch} = \"$git_dir/index\";\n-\t} else {\n-\t    # use an index per branch to speed up\n-\t    # imports of projects with many branches\n-\t    unless ($index{$branch}) {\n-\t\t$index{$branch} = tmpnam();\n-\t\t$ENV{GIT_INDEX_FILE} = $index{$branch};\n-\t\tif ($ancestor) {\n-\t\t    system(\"git\", \"read-tree\", \"$remote/$ancestor\");\n-\t\t} else {\n-\t\t    system(\"git\", \"read-tree\", \"$remote/$branch\");\n-\t\t}\n-\t\tdie \"read-tree failed: $?\\n\" if $?;\n-\t    }\n-\t}\n-        $ENV{GIT_INDEX_FILE} = $index{$branch};\n-\n-\tupdate_index(@old, @new);\n-\t@old = @new = ();\n-\tmy $tree = write_tree();\n-\tmy $parent = get_headref(\"$remote/$last_branch\");\n-\tprint \"Parent ID \" . ($parent ? $parent : \"(empty)\") . \"\\n\" if $opt_v;\n-\n-\tmy @commit_args;\n-\tpush @commit_args, (\"-p\", $parent) if $parent;\n-\n-\t# loose detection of merges\n-\t# based on the commit msg\n-\tforeach my $rx (@mergerx) {\n-\t\tnext unless $logmsg =~ $rx && $1;\n-\t\tmy $mparent = $1 eq 'HEAD' ? $opt_o : $1;\n-\t\tif (my $sha1 = get_headref(\"$remote/$mparent\")) {\n-\t\t\tpush @commit_args, '-p', \"$remote/$mparent\";\n-\t\t\tprint \"Merge parent branch: $mparent\\n\" if $opt_v;\n-\t\t}\n-\t}\n-\n-\tset_timezone($author_tz);\n-\tmy $commit_date = strftime(\"%s %z\", localtime($date));\n-\tset_timezone('UTC');\n-\t$ENV{GIT_AUTHOR_NAME} = $author_name;\n-\t$ENV{GIT_AUTHOR_EMAIL} = $author_email;\n-\t$ENV{GIT_AUTHOR_DATE} = $commit_date;\n-\t$ENV{GIT_COMMITTER_NAME} = $author_name;\n-\t$ENV{GIT_COMMITTER_EMAIL} = $author_email;\n-\t$ENV{GIT_COMMITTER_DATE} = $commit_date;\n-\tmy $pid = open2(my $commit_read, my $commit_write,\n-\t\t'git', 'commit-tree', $tree, @commit_args);\n-\n-\t# compatibility with git2cvs\n-\tsubstr($logmsg,32767) = \"\" if length($logmsg) > 32767;\n-\t$logmsg =~ s/[\\s\\n]+\\z//;\n-\n-\tif (@skipped) {\n-\t    $logmsg .= \"\\n\\n\\nSKIPPED:\\n\\t\";\n-\t    $logmsg .= join(\"\\n\\t\", @skipped) . \"\\n\";\n-\t    @skipped = ();\n-\t}\n-\n-\tprint($commit_write \"$logmsg\\n\") && close($commit_write)\n-\t\tor die \"Error writing to git commit-tree: $!\\n\";\n-\n-\tprint \"Committed patch $patchset ($branch $commit_date)\\n\" if $opt_v;\n-\tchomp(my $cid = <$commit_read>);\n-\tis_sha1($cid) or die \"Cannot get commit id ($cid): $!\\n\";\n-\tprint \"Commit ID $cid\\n\" if $opt_v;\n-\tclose($commit_read);\n-\n-\twaitpid($pid,0);\n-\tdie \"Error running git commit-tree: $?\\n\" if $?;\n-\n-\tsystem('git' , 'update-ref', \"$remote/$branch\", $cid) == 0\n-\t\tor die \"Cannot write branch $branch for update: $!\\n\";\n-\n-\tif ($revision_map) {\n-\t\tprint $revision_map \"@$_ $cid\\n\" for @commit_revisions;\n-\t}\n-\t@commit_revisions = ();\n-\n-\tif ($tag) {\n-\t        my ($xtag) = $tag;\n-\t\t$xtag =~ s/\\s+\\*\\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY **\n-\t\t$xtag =~ tr/_/\\./ if ( $opt_u );\n-\t\t$xtag =~ s/[\\/]/$opt_s/g;\n-\n-\t\t# See refs.c for these rules.\n-\t\t# Tag cannot contain bad chars. (See bad_ref_char in refs.c.)\n-\t\t$xtag =~ s/[ ~\\^:\\\\\\*\\?\\[]//g;\n-\t\t# Other bad strings for tags:\n-\t\t# (See check_refname_component in refs.c.)\n-\t\t1 while $xtag =~ s/\n-\t\t\t(?: \\.\\.        # Tag cannot contain '..'.\n-\t\t\t|   \\@{         # Tag cannot contain '@{'.\n-\t\t\t| ^ -           # Tag cannot begin with '-'.\n-\t\t\t|   \\.lock $    # Tag cannot end with '.lock'.\n-\t\t\t| ^ \\.          # Tag cannot begin...\n-\t\t\t|   \\. $        # ...or end with '.'\n-\t\t\t)//xg;\n-\t\t# Tag cannot be empty.\n-\t\tif ($xtag eq '') {\n-\t\t\twarn(\"warning: ignoring tag '$tag'\",\n-\t\t\t\" with invalid tagname\\n\");\n-\t\t\treturn;\n-\t\t}\n-\n-\t\tif (system('git' , 'tag', '-f', $xtag, $cid) != 0) {\n-\t\t\t# We did our best to sanitize the tag, but still failed\n-\t\t\t# for whatever reason. Bail out, and give the user\n-\t\t\t# enough information to understand if/how we should\n-\t\t\t# improve the translation in the future.\n-\t\t\tif ($tag ne $xtag) {\n-\t\t\t\tprint \"Translated '$tag' tag to '$xtag'\\n\";\n-\t\t\t}\n-\t\t\tdie \"Cannot create tag $xtag: $!\\n\";\n-\t\t}\n-\n-\t\tprint \"Created tag '$xtag' on '$branch'\\n\" if $opt_v;\n-\t}\n-};\n-\n-my $commitcount = 1;\n-while (<CVS>) {\n-\tchomp;\n-\tif ($state == 0 and /^-+$/) {\n-\t\t$state = 1;\n-\t} elsif ($state == 0) {\n-\t\t$state = 1;\n-\t\tredo;\n-\t} elsif (($state==0 or $state==1) and s/^PatchSet\\s+//) {\n-\t\t$patchset = 0+$_;\n-\t\t$state=2;\n-\t} elsif ($state == 2 and s/^Date:\\s+//) {\n-\t\t$date = pdate($_);\n-\t\tunless ($date) {\n-\t\t\tprint STDERR \"Could not parse date: $_\\n\";\n-\t\t\t$state=0;\n-\t\t\tnext;\n-\t\t}\n-\t\t$state=3;\n-\t} elsif ($state == 3 and s/^Author:\\s+//) {\n-\t\t$author_tz = \"UTC\";\n-\t\ts/\\s+$//;\n-\t\tif (/^(.*?)\\s+<(.*)>/) {\n-\t\t    ($author_name, $author_email) = ($1, $2);\n-\t\t} elsif ($conv_author_name{$_}) {\n-\t\t\t$author_name = $conv_author_name{$_};\n-\t\t\t$author_email = $conv_author_email{$_};\n-\t\t\t$author_tz = $conv_author_tz{$_} if ($conv_author_tz{$_});\n-\t\t} else {\n-\t\t    $author_name = $author_email = $_;\n-\t\t}\n-\t\t$state = 4;\n-\t} elsif ($state == 4 and s/^Branch:\\s+//) {\n-\t\ts/\\s+$//;\n-\t\ttr/_/\\./ if ( $opt_u );\n-\t\ts/[\\/]/$opt_s/g;\n-\t\t$branch = $_;\n-\t\t$state = 5;\n-\t} elsif ($state == 5 and s/^Ancestor branch:\\s+//) {\n-\t\ts/\\s+$//;\n-\t\t$ancestor = $_;\n-\t\t$ancestor = $opt_o if $ancestor eq \"HEAD\";\n-\t\t$state = 6;\n-\t} elsif ($state == 5) {\n-\t\t$ancestor = undef;\n-\t\t$state = 6;\n-\t\tredo;\n-\t} elsif ($state == 6 and s/^Tag:\\s+//) {\n-\t\ts/\\s+$//;\n-\t\tif ($_ eq \"(none)\") {\n-\t\t\t$tag = undef;\n-\t\t} else {\n-\t\t\t$tag = $_;\n-\t\t}\n-\t\t$state = 7;\n-\t} elsif ($state == 7 and /^Log:/) {\n-\t\t$logmsg = \"\";\n-\t\t$state = 8;\n-\t} elsif ($state == 8 and /^Members:/) {\n-\t\t$branch = $opt_o if $branch eq \"HEAD\";\n-\t\tif (defined $branch_date{$branch} and $branch_date{$branch} >= $date) {\n-\t\t\t# skip\n-\t\t\tprint \"skip patchset $patchset: $date before $branch_date{$branch}\\n\" if $opt_v;\n-\t\t\t$state = 11;\n-\t\t\tnext;\n-\t\t}\n-\t\tif (!$opt_a && $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {\n-\t\t\t# skip if the commit is too recent\n-\t\t\t# given that the cvsps default fuzz is 300s, we give ourselves another\n-\t\t\t# 300s just in case -- this also prevents skipping commits\n-\t\t\t# due to server clock drift\n-\t\t\tprint \"skip patchset $patchset: $date too recent\\n\" if $opt_v;\n-\t\t\t$state = 11;\n-\t\t\tnext;\n-\t\t}\n-\t\tif (exists $ignorebranch{$branch}) {\n-\t\t\tprint STDERR \"Skipping $branch\\n\";\n-\t\t\t$state = 11;\n-\t\t\tnext;\n-\t\t}\n-\t\tif ($ancestor) {\n-\t\t\tif ($ancestor eq $branch) {\n-\t\t\t\tprint STDERR \"Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\\n\";\n-\t\t\t\t$ancestor = $opt_o;\n-\t\t\t}\n-\t\t\tif (defined get_headref(\"$remote/$branch\")) {\n-\t\t\t\tprint STDERR \"Branch $branch already exists!\\n\";\n-\t\t\t\t$state=11;\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\tmy $id = get_headref(\"$remote/$ancestor\");\n-\t\t\tif (!$id) {\n-\t\t\t\tprint STDERR \"Branch $ancestor does not exist!\\n\";\n-\t\t\t\t$ignorebranch{$branch} = 1;\n-\t\t\t\t$state=11;\n-\t\t\t\tnext;\n-\t\t\t}\n-\n-\t\t\tsystem(qw(git update-ref -m cvsimport),\n-\t\t\t\t\"$remote/$branch\", $id);\n-\t\t\tif($? != 0) {\n-\t\t\t\tprint STDERR \"Could not create branch $branch\\n\";\n-\t\t\t\t$ignorebranch{$branch} = 1;\n-\t\t\t\t$state=11;\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t}\n-\t\t$last_branch = $branch if $branch ne $last_branch;\n-\t\t$state = 9;\n-\t} elsif ($state == 8) {\n-\t\t$logmsg .= \"$_\\n\";\n-\t} elsif ($state == 9 and /^\\s+(.+?):(INITIAL|\\d+(?:\\.\\d+)+)->(\\d+(?:\\.\\d+)+)\\s*$/) {\n-#\tVERSION:1.96->1.96.2.1\n-\t\tmy $init = ($2 eq \"INITIAL\");\n-\t\tmy $fn = $1;\n-\t\tmy $rev = $3;\n-\t\t$fn =~ s#^/+##;\n-\t\tif ($opt_S && $fn =~ m/$opt_S/) {\n-\t\t    print \"SKIPPING $fn v $rev\\n\";\n-\t\t    push(@skipped, $fn);\n-\t\t    next;\n-\t\t}\n-\t\tpush @commit_revisions, [$fn, $rev];\n-\t\tprint \"Fetching $fn   v $rev\\n\" if $opt_v;\n-\t\tmy ($tmpname, $size) = $cvs->file($fn,$rev);\n-\t\tif ($size == -1) {\n-\t\t\tpush(@old,$fn);\n-\t\t\tprint \"Drop $fn\\n\" if $opt_v;\n-\t\t} else {\n-\t\t\tprint \"\".($init ? \"New\" : \"Update\").\" $fn: $size bytes\\n\" if $opt_v;\n-\t\t\tmy $pid = open(my $F, '-|');\n-\t\t\tdie $! unless defined $pid;\n-\t\t\tif (!$pid) {\n-\t\t\t    exec(\"git\", \"hash-object\", \"-w\", $tmpname)\n-\t\t\t\tor die \"Cannot create object: $!\\n\";\n-\t\t\t}\n-\t\t\tmy $sha = <$F>;\n-\t\t\tchomp $sha;\n-\t\t\tclose $F;\n-\t\t\tmy $mode = pmode($cvs->{'mode'});\n-\t\t\tpush(@new,[$mode, $sha, $fn]); # may be resurrected!\n-\t\t}\n-\t\tunlink($tmpname);\n-\t} elsif ($state == 9 and /^\\s+(.+?):\\d+(?:\\.\\d+)+->(\\d+(?:\\.\\d+)+)\\(DEAD\\)\\s*$/) {\n-\t\tmy $fn = $1;\n-\t\tmy $rev = $2;\n-\t\t$fn =~ s#^/+##;\n-\t\tpush @commit_revisions, [$fn, $rev];\n-\t\tpush(@old,$fn);\n-\t\tprint \"Delete $fn\\n\" if $opt_v;\n-\t} elsif ($state == 9 and /^\\s*$/) {\n-\t\t$state = 10;\n-\t} elsif (($state == 9 or $state == 10) and /^-+$/) {\n-\t\t$commitcount++;\n-\t\tif ($opt_L && $commitcount > $opt_L) {\n-\t\t\tlast;\n-\t\t}\n-\t\tcommit();\n-\t\tif (($commitcount & 1023) == 0) {\n-\t\t\tsystem(qw(git repack -a -d));\n-\t\t}\n-\t\t$state = 1;\n-\t} elsif ($state == 11 and /^-+$/) {\n-\t\t$state = 1;\n-\t} elsif (/^-+$/) { # end of unknown-line processing\n-\t\t$state = 1;\n-\t} elsif ($state != 11) { # ignore stuff when skipping\n-\t\tprint STDERR \"* UNKNOWN LINE * $_\\n\";\n-\t}\n-}\n-commit() if $branch and $state != 11;\n-\n-unless ($opt_P) {\n-\tunlink($cvspsfile);\n-}\n-\n-# The heuristic of repacking every 1024 commits can leave a\n-# lot of unpacked data.  If there is more than 1MB worth of\n-# not-packed objects, repack once more.\n-my $line = `git count-objects`;\n-if ($line =~ /^(\\d+) objects, (\\d+) kilobytes$/) {\n-  my ($n_objects, $kb) = ($1, $2);\n-  1024 < $kb\n-    and system(qw(git repack -a -d));\n-}\n-\n-foreach my $git_index (values %index) {\n-    if ($git_index ne \"$git_dir/index\") {\n-\tunlink($git_index);\n-    }\n-}\n-\n-if (defined $orig_git_index) {\n-\t$ENV{GIT_INDEX_FILE} = $orig_git_index;\n-} else {\n-\tdelete $ENV{GIT_INDEX_FILE};\n-}\n-\n-# Now switch back to the branch we were in before all of this happened\n-if ($orig_branch) {\n-\tprint \"DONE.\\n\" if $opt_v;\n-\tif ($opt_i) {\n-\t\texit 0;\n-\t}\n-\tmy $tip_at_end = `git rev-parse --verify HEAD`;\n-\tif ($tip_at_start ne $tip_at_end) {\n-\t\tfor ($tip_at_start, $tip_at_end) { chomp; }\n-\t\tprint \"Fetched into the current branch.\\n\" if $opt_v;\n-\t\tsystem(qw(git read-tree -u -m),\n-\t\t       $tip_at_start, $tip_at_end);\n-\t\tdie \"Fast-forward update failed: $?\\n\" if $?;\n-\t}\n-\telse {\n-\t\tsystem(qw(git merge cvsimport HEAD), \"$remote/$opt_o\");\n-\t\tdie \"Could not merge $opt_o into the current branch.\\n\" if $?;\n-\t}\n-} else {\n-\t$orig_branch = \"master\";\n-\tprint \"DONE; creating $orig_branch branch\\n\" if $opt_v;\n-\tsystem(\"git\", \"update-ref\", \"refs/heads/master\", \"$remote/$opt_o\")\n-\t\tunless defined get_headref('refs/heads/master');\n-\tsystem(\"git\", \"symbolic-ref\", \"$remote/HEAD\", \"$remote/$opt_o\")\n-\t\tif ($opt_r && $opt_o ne 'HEAD');\n-\tsystem('git', 'update-ref', 'HEAD', \"$orig_branch\");\n-\tunless ($opt_i) {\n-\t\tsystem(qw(git checkout -f));\n-\t\tdie \"checkout failed: $?\\n\" if $?;\n-\t}\n-}\ndiff --git a/git-cvsimport.sh b/git-cvsimport.sh\nnew file mode 100755\nindex 0000000..71ba11d\n--- /dev/null\n+++ b/git-cvsimport.sh\n@@ -0,0 +1,5 @@\n+#!/bin/sh\n+\n+GIT_CVSPS_VERSION=2\n+\n+exec git cvsimport-$GIT_CVSPS_VERSION \"$@\"\n-- \n1.8.1.421.g6236851\n"},{"id":"206729","messageId":"1358127629-7500-4-git-send-email-gitster@pobox.com","threadId":"32585","inReplyTo":"1358127629-7500-1-git-send-email-gitster@pobox.com","subject":"[PATCH 3/3] cvsimport: start adding cvsps 3.x support","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-14T01:40:29Z","receivedAt":"2013-01-14T01:40:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The new cvsps 3.x series lacks support of some options cvsps 2.x\nseries had and used by cvsimport-2; add a replacement program from\nthe author of cvsps 3.x and allow users to choose it by setting the\nGIT_CVSPS_VERSION environment variable to 3.  We would later add\nsupport to auto-detect the version of installed cvsps to this code\nwhen the environment variable is not set.\n\nNote that in this step, cvsimport-3 that relies on cvsps 3.x does\nnot have any test coverage.  As cvsimport-3 supports most of the\ncommand line options cvsimport-2, we should be able to tweak some of\nt96xx tests and run them with GIT_CVSPS_VERSION set to both 2 and 3,\nbut that is a topic of later patches that should come on top.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Makefile           |  18 ++-\n git-cvsimport-3.py | 344 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n git-cvsimport.sh   |   4 +-\n 3 files changed, 359 insertions(+), 7 deletions(-)\n create mode 100755 git-cvsimport-3.py\n\ndiff --git a/Makefile b/Makefile\nindex b022db2..060cdc2 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -470,8 +470,9 @@ SCRIPT_PERL += git-relink.perl\n SCRIPT_PERL += git-send-email.perl\n SCRIPT_PERL += git-svn.perl\n \n-SCRIPT_PYTHON += git-remote-testpy.py\n+SCRIPT_PYTHON += git-cvsimport-3.py\n SCRIPT_PYTHON += git-p4.py\n+SCRIPT_PYTHON += git-remote-testpy.py\n \n SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \\\n \t  $(patsubst %.perl,%,$(SCRIPT_PERL)) \\\n@@ -575,8 +576,11 @@ endif\n ifndef CVSPS2_PATH\n \tCVSPS2_PATH = cvsps\n endif\n+ifndef CVSPS3_PATH\n+\tCVSPS3_PATH = cvsps\n+endif\n \n-export PERL_PATH PYTHON_PATH CVSPS2_PATH\n+export PERL_PATH PYTHON_PATH CVSPS2_PATH CVSPS3_PATH\n \n LIB_FILE = libgit.a\n XDIFF_LIB = xdiff/lib.a\n@@ -1515,6 +1519,7 @@ PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n PYTHON_PATH_SQ = $(subst ','\\'',$(PYTHON_PATH))\n TCLTK_PATH_SQ = $(subst ','\\'',$(TCLTK_PATH))\n CVSPS2_PATH_SQ = $(subst ','\\'',$(CVSPS2_PATH))\n+CVSPS3_PATH_SQ = $(subst ','\\'',$(CVSPS3_PATH))\n DIFF_SQ = $(subst ','\\'',$(DIFF))\n \n LIBS = $(GITLIBS) $(EXTLIBS)\n@@ -1757,12 +1762,15 @@ ifndef NO_PYTHON\n $(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS\n $(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py\n \t$(QUIET_GEN)$(RM) $@ $@+ && \\\n-\tINSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \\\n+\tINSTLIBDIR_SQ=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \\\n \t\t--no-print-directory prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' \\\n-\t\tinstlibdir` && \\\n+\t\tinstlibdir | \\\n+\t\tsed -e \"s/'/'\\''/g\"` && \\\n+\techo \"InstLibDir is <$$INSTLIBDIR_SQ>\" && \\\n \tsed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \\\n \t    -e 's|\\(os\\.getenv(\"GITPYTHONLIB\"\\)[^)]*)|\\1,\"@@INSTLIBDIR@@\")|' \\\n-\t    -e 's|@@INSTLIBDIR@@|'\"$$INSTLIBDIR\"'|g' \\\n+\t    -e 's|@@CVSPS3_PATH@@|$(CVSPS3_PATH_SQ)|g' \\\n+\t    -e 's|@@INSTLIBDIR@@|'\"$$INSTLIBDIR_SQ\"'|g' \\\n \t    $@.py >$@+ && \\\n \tchmod +x $@+ && \\\n \tmv $@+ $@\ndiff --git a/git-cvsimport-3.py b/git-cvsimport-3.py\nnew file mode 100755\nindex 0000000..57f13b7\n--- /dev/null\n+++ b/git-cvsimport-3.py\n@@ -0,0 +1,344 @@\n+#!/usr/bin/env python\n+#\n+# Import CVS history into git\n+#\n+# Intended to be a near-workalike of Matthias Urlichs's Perl implementation.\n+#\n+# By Eric S. Raymond <esr@thyrsus.com>, December 2012\n+# May be redistributed under the license of the git project.\n+\n+import sys\n+\n+if sys.hexversion < 0x02060000:\n+    sys.stderr.write(\"git cvsimport: requires Python 2.6 or later.\\n\")\n+    sys.exit(1)\n+\n+import os, getopt, subprocess, tempfile, shutil\n+\n+DEBUG_COMMANDS = 1\n+\n+class Fatal(Exception):\n+    \"Unrecoverable error.\"\n+    def __init__(self, msg):\n+        Exception.__init__(self)\n+        self.msg = msg\n+\n+def do_or_die(dcmd, legend=\"\"):\n+    \"Either execute a command or raise a fatal exception.\"\n+    if legend:\n+        legend = \" \"  + legend\n+    if verbose >= DEBUG_COMMANDS:\n+        sys.stdout.write(\"git cvsimport: executing '%s'%s\\n\" % (dcmd, legend))\n+    try:\n+        retcode = subprocess.call(dcmd, shell=True)\n+        if retcode < 0:\n+            raise Fatal(\"git cvsimport: child was terminated by signal %d.\" % -retcode)\n+        elif retcode != 0:\n+            raise Fatal(\"git cvsimport: child returned %d.\" % retcode)\n+    except (OSError, IOError) as e:\n+        raise Fatal(\"git cvsimport: execution of %s%s failed: %s\" % (dcmd, legend, e))\n+\n+def capture_or_die(dcmd, legend=\"\"):\n+    \"Either execute a command and capture its output or die.\"\n+    if legend:\n+        legend = \" \"  + legend\n+    if verbose >= DEBUG_COMMANDS:\n+        sys.stdout.write(\"git cvsimport: executing '%s'%s\\n\" % (dcmd, legend))\n+    try:\n+        return subprocess.check_output(dcmd, shell=True)\n+    except subprocess.CalledProcessError as e:\n+        if e.returncode < 0:\n+            sys.stderr.write(\"git cvsimport: child was terminated by signal %d.\" % -e.returncode)\n+        elif e.returncode != 0:\n+            sys.stderr.write(\"git cvsimport: child returned %d.\" % e.returncode)\n+        sys.exit(1)\n+\n+class cvsps:\n+    \"Method class for cvsps back end.\"\n+\n+    cvsps = \"@@CVSPS3_PATH@@\"\n+    def __init__(self):\n+        self.opts = \"\"\n+        self.revmap = None\n+    def set_repo(self, val):\n+        \"Set the repository root option.\"\n+        if not val.startswith(\":\"):\n+            if not val.startswith(os.sep):\n+                val = os.path.abspath(val)\n+            val = \":local:\" + val\n+        self.opts += \" --root '%s'\" % val\n+    def set_authormap(self, val):\n+        \"Set the author-map file.\"\n+        self.opts += \" -A '%s'\" % val\n+    def set_fuzz(self, val):\n+        \"Set the commit-similarity window.\"\n+        self.opts += \" -z %s\" % val\n+    def set_nokeywords(self):\n+        \"Suppress CVS keyword expansion.\"\n+        self.opts += \" -k\"\n+    def add_opts(self, val):\n+        \"Add options to the engine command line.\"\n+        self.opts += \" \" + val\n+    def set_exclusion(self, val):\n+        \"Set a file exclusion regexp.\"\n+        self.opts += \" -n -f '%s'\" % val\n+    def set_after(self, val):\n+        \"Set a date threshold for incremental import.\"\n+        self.opts += \" -d '%s'\" % val\n+    def set_revmap(self, val):\n+        \"Set the file to which the engine should dump a reference map.\"\n+        self.revmap = val\n+        self.opts += \" -R '%s'\" % self.revmap\n+    def set_module(self, val):\n+        \"Set the module to query.\"\n+        self.opts += \" \" + val\n+    def command(self):\n+        \"Emit the command implied by all previous options.\"\n+        return self.cvsps + \"--fast-export \" + self.opts\n+\n+class cvs2git:\n+    \"Method class for cvs2git back end.\"\n+    def __init__(self):\n+        self.opts = \"\"\n+        self.modulepath = \".\"\n+    def set_authormap(self, _val):\n+        \"Set the author-map file.\"\n+        sys.stderr.write(\"git cvsimport: author maping is not supported with cvs2git.\\n\")\n+        sys.exit(1)\n+    def set_repo(self, _val):\n+        \"Set the repository root option.\"\n+        sys.stderr.write(\"git cvsimport: cvs2git must run within a repository checkout directory.\\n\")\n+        sys.exit(1)\n+    def set_fuzz(self, _val):\n+        \"Set the commit-similarity window.\"\n+        sys.stderr.write(\"git cvsimport: fuzz setting is not supported with cvs2git.\\n\")\n+        sys.exit(1)\n+    def set_nokeywords(self):\n+        \"Suppress CVS keyword expansion.\"\n+        self.opts += \" --keywords-off\"\n+    def add_opts(self, val):\n+        \"Add options to the engine command line.\"\n+        self.opts += \" \" + val\n+    def set_exclusion(self, val):\n+        \"Set a file exclusion regexp.\"\n+        self.opts += \" --exclude='%s'\" % val\n+    def set_after(self, _val):\n+        \"Set a date threshold for incremental import.\"\n+        sys.stderr.write(\"git cvsimport: incremental import is not supported with cvs2git.\\n\")\n+        sys.exit(1)\n+    def set_revmap(self, _val):\n+        \"Set the file to which the engine should dump a reference map.\"\n+        sys.stderr.write(\"git cvsimport: can't get a reference map from cvs2git.\\n\")\n+        sys.exit(1)\n+    def set_module(self, val):\n+        \"Set the module to query.\"\n+        self.modulepath = \" \" + val\n+    def command(self):\n+        \"Emit the command implied by all previous options.\"\n+        return \"(cvs2git --username=git-cvsimport --quiet --quiet --blobfile={0} --dumpfile={1} {2} {3} && cat {0} {1} && rm {0} {1})\".format(tempfile.mkstemp()[1], tempfile.mkstemp()[1], self.opts, self.modulepath)\n+\n+class filesource:\n+    \"Method class for file-source back end.\"\n+    def __init__(self, filename):\n+        self.filename = filename\n+    def __complain(self, legend):\n+        sys.stderr.write(\"git cvsimport: %s with file source.\\n\" % legend)\n+        sys.exit(1)\n+    def set_repo(self, _val):\n+        \"Set the repository root option.\"\n+        self.__complain(\"repository can't be set\")\n+    def set_authormap(self, _val):\n+        \"Set the author-map file.\"\n+        sys.stderr.write(\"git cvsimport: author mapping is not supported with filesource.\\n\")\n+        sys.exit(1)\n+    def set_fuzz(self, _val):\n+        \"Set the commit-similarity window.\"\n+        self.__complain(\"fuzz can't be set\")\n+    def set_nokeywords(self, _val):\n+        \"Suppress CVS keyword expansion.\"\n+        self.__complain(\"keyword suppression can't be set\")\n+    def add_opts(self, _val):\n+        \"Add options to the engine command line.\"\n+        self.__complain(\"other options can't be set\")\n+    def set_exclusion(self, _val):\n+        \"Set a file exclusion regexp.\"\n+        self.__complain(\"exclusions can't be set\")\n+    def set_after(self, _val):\n+        \"Set a date threshold for incremental import.\"\n+        pass\n+    def set_revmap(self, _val):\n+        \"Set the file to which the engine should dump a reference map.\"\n+        sys.stderr.write(\"git cvsimport: can't get a reference map from cvs2git.\\n\")\n+        sys.exit(1)\n+    def set_module(self, _val):\n+        \"Set the module to query.\"\n+        self.__complain(\"module can't be set\")\n+    def command(self):\n+        \"Emit the command implied by all previous options.\"\n+        return \"cat \" + self.filename\n+\n+if __name__ == '__main__':\n+    if sys.hexversion < 0x02060000:\n+        sys.stderr.write(\"git cvsimport: requires Python 2.6 or later.\\n\")\n+        sys.exit(1)\n+    (options, arguments) = getopt.getopt(sys.argv[1:], \"vbe:d:C:r:o:ikus:p:z:P:S:aL:A:Rh\")\n+    verbose = 0\n+    bare = False\n+    root = None\n+    outdir = os.getcwd()\n+    remotize = False\n+    import_only = False\n+    underscore_to_dot = False\n+    slashsubst = None\n+    authormap = None\n+    revisionmap = False\n+    backend = cvsps()\n+    for (opt, val) in options:\n+        if opt == '-v':\n+            verbose += 1\n+        elif opt == '-b':\n+            bare = True\n+        elif opt == '-e':\n+            for cls in (cvsps, cvs2git):\n+                if cls.__name__ == val:\n+                    backend = cls()\n+                    break\n+            else:\n+                sys.stderr.write(\"git cvsimport: unknown engine %s.\\n\" % val)\n+                sys.exit(1)\n+        elif opt == '-d':\n+            backend.set_repo(val)\n+        elif opt == '-C':\n+            outdir = val\n+        elif opt == '-r':\n+            remotize = True\n+        elif opt == '-o':\n+            sys.stderr.write(\"git cvsimport: -o is no longer supported.\\n\")\n+            sys.exit(1)\n+        elif opt == '-i':\n+            import_only = True\n+        elif opt == '-k':\n+            backend.set_nokeywords()\n+        elif opt == '-u':\n+            underscore_to_dot = True\n+        elif opt == '-s':\n+            slashsubst = val\n+        elif opt == '-p':\n+            backend.add_opts(val.replace(\",\", \" \"))\n+        elif opt == '-z':\n+            backend.set_fuzz(val)\n+        elif opt == '-P':\n+            backend = filesource(val)\n+            sys.exit(1)\n+        elif opt in ('-m', '-M'):\n+            sys.stderr.write(\"git cvsimport: -m and -M are no longer supported: use reposurgeon instead.\\n\")\n+            sys.exit(1)\n+        elif opt == '-S':\n+            backend.set_exclusion(val)\n+        elif opt == '-a':\n+            sys.stderr.write(\"git cvsimport: -a is no longer supported.\\n\")\n+            sys.exit(1)\n+        elif opt == '-L':\n+            sys.stderr.write(\"git cvsimport: -L is no longer supported.\\n\")\n+            sys.exit(1)\n+        elif opt == '-A':\n+            authormap = os.path.abspath(val)\n+        elif opt == '-R':\n+            revisionmap = True\n+        else:\n+            print \"\"\"\\\n+git cvsimport [-A <author-conv-file>] [-C <git_repository>] [-b] [-d <CVSROOT>]\n+     [-e engine] [-h] [-i] [-k] [-p <options-for-cvsps>] [-P <source-file>]\n+     [-r <remote>] [-R] [-s <subst>] [-S <regex>] [-u] [-v] [-z <fuzz>]\n+     [<CVS_module>]\n+\"\"\"\n+    def metadata(fn, outdir='.'):\n+        if bare:\n+            return os.path.join(outdir, fn)\n+        else:\n+            return os.path.join(outdir, \".git\", fn)\n+\n+    try:\n+        if outdir:\n+            try:\n+                # If the output directory does not exist, create it\n+                # and initialize it as a git repository.\n+                os.mkdir(outdir)\n+                do_or_die(\"git init --quiet \" + outdir)\n+            except OSError:\n+                # Otherwise, assume user wants incremental import.\n+                if not bare and not os.path.exists(os.path.join(outdir, \".git\")):\n+                    raise Fatal(\"output directory is not a git repository\")\n+                threshold = capture_or_die(\"git log -1 --format=%ct\").strip()\n+                backend.set_after(threshold)\n+        if revisionmap:\n+            backend.set_revmap(tempfile.mkstemp()[1])\n+            markmap = tempfile.mkstemp()[1]\n+        if arguments:\n+            backend.set_module(arguments[0])\n+        gitopts = \"\"\n+        if bare:\n+            gitopts += \" --bare\"\n+        if revisionmap:\n+            gitopts += \" --export-marks='%s'\" % markmap\n+        if authormap:\n+            shutil.copyfile(authormap, metadata(\"cvs-authors\", outdir))\n+        if os.path.exists(metadata(\"cvs-authors\", outdir)):\n+            backend.set_authormap(metadata(\"cvs-authors\", outdir))\n+        do_or_die(\"%s | (cd %s >/dev/null; git fast-import --quiet %s)\" \\\n+                  % (backend.command(), outdir, gitopts))\n+        os.chdir(outdir)\n+        if underscore_to_dot or slashsubst:\n+            tagnames = capture_or_die(\"git tag -l\")\n+            for tag in tagnames.split():\n+                if tag:\n+                    changed = tag\n+                    if underscore_to_dot:\n+                        changed = changed.replace(\"_\", \".\")\n+                    if slashsubst:\n+                        changed = changed.replace(os.sep, slashsubst)\n+                    if changed != tag:\n+                        do_or_die(\"git tag -f %s %s >/dev/null\" % (tag, changed))\n+        if underscore_to_dot or slashsubst or remotize:\n+            branchnames = capture_or_die(\"git branch -l\")\n+            for branch in branchnames.split():\n+                if branch:\n+                    # Ugh - fragile dependency on branch -l output format\n+                    branch = branch[2:]\n+                    changed = branch\n+                    if underscore_to_dot:\n+                        changed = changed.replace(\"_\", \".\")\n+                    if slashsubst:\n+                        changed = changed.replace(os.sep, slashsubst)\n+                    if remotize:\n+                        changed = os.path.join(\"remotes\", remotize, branch)\n+                    if changed != branch:\n+                        do_or_die(\"branch --m %s %s >/dev/null\" % (branch, changed))\n+        if revisionmap:\n+            refd = {}\n+            for line in open(backend.revmap):\n+                if line.startswith(\"#\"):\n+                    continue\n+                (fn, rev, mark) = line.split()\n+                refd[(fn, rev)] = mark\n+            markd = {}\n+            for line in open(markmap):\n+                if line.startswith(\"#\"):\n+                    continue\n+                (mark, hashd) = line.split()\n+                markd[mark] = hashd\n+            with open(metadata(\"cvs-revisions\"), \"a\") as wfp:\n+                for ((fn, rev), val) in refd.items():\n+                    if val in markd:\n+                        wfp.write(\"%s %s %s\\n\" % (fn, rev, markd[val]))\n+            os.remove(markmap)\n+            os.remove(backend.revmap)\n+        if not import_only and not bare:\n+            do_or_die(\"git checkout -q\")\n+    except Fatal, err:\n+        sys.stderr.write(\"git_cvsimport: \" + err.msg + \"\\n\")\n+        sys.exit(1)\n+    except KeyboardInterrupt:\n+        pass\n+\n+# end\ndiff --git a/git-cvsimport.sh b/git-cvsimport.sh\nindex 71ba11d..52ce112 100755\n--- a/git-cvsimport.sh\n+++ b/git-cvsimport.sh\n@@ -1,5 +1,5 @@\n #!/bin/sh\n \n-GIT_CVSPS_VERSION=2\n+: ${GIT_CVSPS_VERSION=2}\n \n-exec git cvsimport-$GIT_CVSPS_VERSION \"$@\"\n+exec git \"cvsimport-$GIT_CVSPS_VERSION\" \"$@\"\n-- \n1.8.1.421.g6236851\n"},{"id":"206731","messageId":"7vr4lofscj.fsf@alter.siamese.dyndns.org","threadId":"32585","inReplyTo":"1358127629-7500-3-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 2/3] cvsimport: introduce a version-switch wrapper","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-14T01:47:40Z","receivedAt":"2013-01-14T01:47:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> In preparation to have both old and new cvsimport during the\n> transition period, rename the cvsimport script to cvsimport-2\n> and introduce a small wrapper that we can later change it to\n> allow users to run either frontend (with their corresponding\n> cvsps backends).\n>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  .gitignore           |    1 +\n>  Makefile             |    3 +-\n>  git-cvsimport-2.perl | 1179 ++++++++++++++++++++++++++++++++++++++++++++++++++\n>  git-cvsimport.perl   | 1179 --------------------------------------------------\n\nSorry, I should have done \"format-patch -M\" for this one.\n"},{"id":"206744","messageId":"50F393D6.2010603@alum.mit.edu","threadId":"32585","inReplyTo":"7v8v7wiv3a.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-01-14T05:12:54Z","receivedAt":"2013-01-14T05:12:54Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 01/13/2013 11:20 PM, Junio C Hamano wrote:\n> After a quick survey of various distros, I think it is very unlikely\n> that we will see \"distros move on to newer cvsps, leaving cvsimport\n> broken\" situation. If anything, it is more like \"distros decide to\n> ignore the new cvsps, until it is made to work with cvsimport\" [*1*].\n\nA better predictor of the distros' decisions is probably which other\npackages depend on cvsps.  As one data point: on Debian squeeze and on\nUbuntu precise, only two packages depend on cvsps (git-cvs and\nbzr-cvsps-import) and one suggests it (chora2, \"a code repository\nviewing component for horde framework\").  So also by this standard they\nare unlikely to feel a lot of pressure to update quickly to cvsps3.\n\n> I think it is probably sensible to [...]\n> \n> Agreed?\n\nYes, I agree that what you propose is a good strategy.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"206772","messageId":"1358148351-31552-1-git-send-email-gitster@pobox.com","threadId":"32585","inReplyTo":"7v8v7wiv3a.fsf@alter.siamese.dyndns.org","subject":"[PATCH v2 0/6] A smoother transition plan for cvsimport","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-14T07:25:45Z","receivedAt":"2013-01-14T07:25:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The theme is the same as the previous 3-patch series ($gmane/213415).\n\nThe first one is unrelated.\n\nThe last two are new, adding the \"dual-use test\" framework hinted\nat the end of the previous round.\n\nOh, also this time the series is formatted with \"format-patch -M\",\nto avoid overwhelming the readers with the rename.\n\nJunio C Hamano (6):\n  Makefile: add description on PERL/PYTHON_PATH\n  cvsimport: allow setting a custom cvsps (2.x) program name\n  cvsimport: introduce a version-switch wrapper\n  cvsimport: start adding cvsps 3.x support\n  cvsimport: make tests reusable for cvsimport-3\n  cvsimport-3: add a sample test\n\n .gitignore                                 |   1 +\n Makefile                                   |  43 +++-\n git-cvsimport.perl => git-cvsimport-2.perl |   4 +-\n git-cvsimport-3.py                         | 344 +++++++++++++++++++++++++++++\n git-cvsimport.sh                           |   5 +\n t/lib-cvs.sh                               |  55 ++++-\n t/t9650-cvsimport3.sh                      |   4 +\n 7 files changed, 440 insertions(+), 16 deletions(-)\n rename git-cvsimport.perl => git-cvsimport-2.perl (99%)\n create mode 100755 git-cvsimport-3.py\n create mode 100755 git-cvsimport.sh\n create mode 100755 t/t9650-cvsimport3.sh\n\n-- \n1.8.1.421.g6236851\n"},{"id":"206771","messageId":"1358148351-31552-2-git-send-email-gitster@pobox.com","threadId":"32585","inReplyTo":"1358148351-31552-1-git-send-email-gitster@pobox.com","subject":"[PATCH v2 1/6] Makefile: add description on PERL/PYTHON_PATH","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-14T07:25:46Z","receivedAt":"2013-01-14T07:25:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Signed-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Makefile | 5 +++++\n 1 file changed, 5 insertions(+)\n\ndiff --git a/Makefile b/Makefile\nindex 1b30d7b..1695075 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -241,11 +241,16 @@ all::\n # apostrophes to be ASCII so that cut&pasting examples to the shell\n # will work.\n #\n+# Define PERL_PATH to the path of your Perl binary (usually /usr/bin/perl).\n+#\n # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's\n # MakeMaker (e.g. using ActiveState under Cygwin).\n #\n # Define NO_PERL if you do not want Perl scripts or libraries at all.\n #\n+# Define PYTHON_PATH to the path of your Python binary (often /usr/bin/python\n+# but /usr/bin/python2.7 on some platforms).\n+#\n # Define NO_PYTHON if you do not want Python scripts or libraries at all.\n #\n # Define NO_TCLTK if you do not want Tcl/Tk GUI.\n-- \n1.8.1.421.g6236851\n"},{"id":"206775","messageId":"1358148351-31552-3-git-send-email-gitster@pobox.com","threadId":"32585","inReplyTo":"1358148351-31552-1-git-send-email-gitster@pobox.com","subject":"[PATCH v2 2/6] cvsimport: allow setting a custom cvsps (2.x) program name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-14T07:25:47Z","receivedAt":"2013-01-14T07:25:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Distros may ship old cvsps under a different name, or the user may\ninstall it outside the normal $PATH.  Allow setting CVSPS2_PATH from\nthe build environment.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Makefile           | 9 +++++++--\n git-cvsimport.perl | 4 +++-\n t/lib-cvs.sh       | 4 +++-\n 3 files changed, 13 insertions(+), 4 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 1695075..dd2a024 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -576,9 +576,11 @@ endif\n ifndef PYTHON_PATH\n \tPYTHON_PATH = /usr/bin/python\n endif\n+ifndef CVSPS2_PATH\n+\tCVSPS2_PATH = cvsps\n+endif\n \n-export PERL_PATH\n-export PYTHON_PATH\n+export PERL_PATH PYTHON_PATH CVSPS2_PATH\n \n LIB_FILE = libgit.a\n XDIFF_LIB = xdiff/lib.a\n@@ -1516,6 +1518,7 @@ SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH))\n PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n PYTHON_PATH_SQ = $(subst ','\\'',$(PYTHON_PATH))\n TCLTK_PATH_SQ = $(subst ','\\'',$(TCLTK_PATH))\n+CVSPS2_PATH_SQ = $(subst ','\\'',$(CVSPS2_PATH))\n DIFF_SQ = $(subst ','\\'',$(DIFF))\n \n LIBS = $(GITLIBS) $(EXTLIBS)\n@@ -1729,6 +1732,7 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl GIT-VERSION-FILE\n \t    -e '\tH' \\\n \t    -e '\tx' \\\n \t    -e '}' \\\n+\t    -e 's|@@CVSPS2_PATH@@|$(CVSPS2_PATH_SQ)|g' \\\n \t    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \\\n \t    $@.perl >$@+ && \\\n \tchmod +x $@+ && \\\n@@ -2107,6 +2111,7 @@ GIT-LDFLAGS: FORCE\n GIT-BUILD-OPTIONS: FORCE\n \t@echo SHELL_PATH=\\''$(subst ','\\'',$(SHELL_PATH_SQ))'\\' >$@\n \t@echo PERL_PATH=\\''$(subst ','\\'',$(PERL_PATH_SQ))'\\' >>$@\n+\t@echo CVSPS2_PATH=\\''$(subst ','\\'',$(CVSPS2_PATH_SQ))'\\' >>$@\n \t@echo DIFF=\\''$(subst ','\\'',$(subst ','\\'',$(DIFF)))'\\' >>$@\n \t@echo PYTHON_PATH=\\''$(subst ','\\'',$(PYTHON_PATH_SQ))'\\' >>$@\n \t@echo TAR=\\''$(subst ','\\'',$(subst ','\\'',$(TAR)))'\\' >>$@\ndiff --git a/git-cvsimport.perl b/git-cvsimport.perl\nindex 0a31ebd..ad460a5 100755\n--- a/git-cvsimport.perl\n+++ b/git-cvsimport.perl\n@@ -33,6 +33,8 @@\n our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r, $opt_R);\n my (%conv_author_name, %conv_author_email, %conv_author_tz);\n \n+my $cvsps2 = \"@@CVSPS2_PATH@@\";\n+\n sub usage(;$) {\n \tmy $msg = shift;\n \tprint(STDERR \"Error: $msg\\n\") if $msg;\n@@ -751,7 +753,7 @@ sub munge_user_filename {\n \t\tunless (defined($opt_p) && $opt_p =~ m/--no-cvs-direct/) {\n \t\t\tpush @opt, '--cvs-direct';\n \t\t}\n-\t\texec(\"cvsps\",\"--norc\",@opt,\"-u\",\"-A\",'--root',$opt_d,$cvs_tree);\n+\t\texec($cvsps2,\"--norc\",@opt,\"-u\",\"-A\",'--root',$opt_d,$cvs_tree);\n \t\tdie \"Could not start cvsps: $!\\n\";\n \t}\n \t($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',\ndiff --git a/t/lib-cvs.sh b/t/lib-cvs.sh\nindex 44263ad..bdab63c 100644\n--- a/t/lib-cvs.sh\n+++ b/t/lib-cvs.sh\n@@ -13,7 +13,9 @@ fi\n CVS=\"cvs -f\"\n export CVS\n \n-cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`\n+CVSPS=\"$CVSPS2_PATH\"\n+\n+cvsps_version=`$CVSPS -h 2>&1 | sed -ne 's/cvsps version //p'`\n case \"$cvsps_version\" in\n 2.1 | 2.2*)\n \t;;\n-- \n1.8.1.421.g6236851\n"},{"id":"206777","messageId":"1358148351-31552-4-git-send-email-gitster@pobox.com","threadId":"32585","inReplyTo":"1358148351-31552-1-git-send-email-gitster@pobox.com","subject":"[PATCH v2 3/6] cvsimport: introduce a version-switch wrapper","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-14T07:25:48Z","receivedAt":"2013-01-14T07:25:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"In preparation to have both old and new cvsimport during the\ntransition period, rename the cvsimport script to cvsimport-2\nand introduce a small wrapper that we can later change it to\nallow users to run either frontend (with their corresponding\ncvsps backends).\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n .gitignore                                 | 1 +\n Makefile                                   | 9 ++++++++-\n git-cvsimport.perl => git-cvsimport-2.perl | 0\n git-cvsimport.sh                           | 5 +++++\n 4 files changed, 14 insertions(+), 1 deletion(-)\n rename git-cvsimport.perl => git-cvsimport-2.perl (100%)\n create mode 100755 git-cvsimport.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex aa258a6..8cb799c 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -40,6 +40,7 @@\n /git-credential-store\n /git-cvsexportcommit\n /git-cvsimport\n+/git-cvsimport-2\n /git-cvsserver\n /git-daemon\n /git-diff\ndiff --git a/Makefile b/Makefile\nindex dd2a024..f3113a9 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -253,6 +253,9 @@ all::\n #\n # Define NO_PYTHON if you do not want Python scripts or libraries at all.\n #\n+# Define CVSPS2_PATH if you cannot invoke cvsps (version 2.x) as \"cvsps\"\n+# using your $PATH; if you do not have any, define CVSPS2_PATH=NoThanks.\n+#\n # Define NO_TCLTK if you do not want Tcl/Tk GUI.\n #\n # The TCL_PATH variable governs the location of the Tcl interpreter\n@@ -440,6 +443,7 @@ unexport CDPATH\n \n SCRIPT_SH += git-am.sh\n SCRIPT_SH += git-bisect.sh\n+SCRIPT_SH += git-cvsimport.sh\n SCRIPT_SH += git-difftool--helper.sh\n SCRIPT_SH += git-filter-branch.sh\n SCRIPT_SH += git-lost-found.sh\n@@ -468,12 +472,15 @@ SCRIPT_PERL += git-add--interactive.perl\n SCRIPT_PERL += git-difftool.perl\n SCRIPT_PERL += git-archimport.perl\n SCRIPT_PERL += git-cvsexportcommit.perl\n-SCRIPT_PERL += git-cvsimport.perl\n+ifneq ($(CVSPS2_PATH),NoThanks)\n+SCRIPT_PERL += git-cvsimport-2.perl\n+endif\n SCRIPT_PERL += git-cvsserver.perl\n SCRIPT_PERL += git-relink.perl\n SCRIPT_PERL += git-send-email.perl\n SCRIPT_PERL += git-svn.perl\n \n+SCRIPT_PYTHON += git-p4.py\n SCRIPT_PYTHON += git-remote-testpy.py\n SCRIPT_PYTHON += git-p4.py\n \ndiff --git a/git-cvsimport.perl b/git-cvsimport-2.perl\nsimilarity index 100%\nrename from git-cvsimport.perl\nrename to git-cvsimport-2.perl\ndiff --git a/git-cvsimport.sh b/git-cvsimport.sh\nnew file mode 100755\nindex 0000000..71ba11d\n--- /dev/null\n+++ b/git-cvsimport.sh\n@@ -0,0 +1,5 @@\n+#!/bin/sh\n+\n+GIT_CVSPS_VERSION=2\n+\n+exec git cvsimport-$GIT_CVSPS_VERSION \"$@\"\n-- \n1.8.1.421.g6236851\n"},{"id":"206776","messageId":"1358148351-31552-5-git-send-email-gitster@pobox.com","threadId":"32585","inReplyTo":"1358148351-31552-1-git-send-email-gitster@pobox.com","subject":"[PATCH v2 4/6] cvsimport: start adding cvsps 3.x support","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-14T07:25:49Z","receivedAt":"2013-01-14T07:25:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The new cvsps 3.x series lacks support of some options cvsps 2.x\nseries had and used by cvsimport-2; add a replacement program from\nthe author of cvsps 3.x and allow users to choose it by setting the\nGIT_CVSPS_VERSION environment variable to 3.  We would later add\nsupport to auto-detect the version of installed cvsps to this code\nwhen the environment variable is not set.\n\nNote that in this step, cvsimport-3 that relies on cvsps 3.x does\nnot have any test coverage.  As cvsimport-3 supports most of the\ncommand line options cvsimport-2, we should be able to tweak some of\nthe t96?? tests and run them with GIT_CVSPS_VERSION set to both 2\nand 3, but that is a topic of later patches that should come on top.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Makefile           |  22 +++-\n git-cvsimport-3.py | 344 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n git-cvsimport.sh   |   4 +-\n 3 files changed, 363 insertions(+), 7 deletions(-)\n create mode 100755 git-cvsimport-3.py\n\ndiff --git a/Makefile b/Makefile\nindex f3113a9..00bd3a6 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -256,6 +256,9 @@ all::\n # Define CVSPS2_PATH if you cannot invoke cvsps (version 2.x) as \"cvsps\"\n # using your $PATH; if you do not have any, define CVSPS2_PATH=NoThanks.\n #\n+# Define CVSPS3_PATH if you cannot invoke cvsps (version 3.x) as \"cvsps\"\n+# using your $PATH; if you do not have any, define CVSPS3_PATH=NoThanks.\n+#\n # Define NO_TCLTK if you do not want Tcl/Tk GUI.\n #\n # The TCL_PATH variable governs the location of the Tcl interpreter\n@@ -480,9 +483,11 @@ SCRIPT_PERL += git-relink.perl\n SCRIPT_PERL += git-send-email.perl\n SCRIPT_PERL += git-svn.perl\n \n+ifneq ($(CVSPS3_PATH),NoThanks)\n+SCRIPT_PYTHON += git-cvsimport-3.py\n+endif\n SCRIPT_PYTHON += git-p4.py\n SCRIPT_PYTHON += git-remote-testpy.py\n-SCRIPT_PYTHON += git-p4.py\n \n SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \\\n \t  $(patsubst %.perl,%,$(SCRIPT_PERL)) \\\n@@ -586,8 +591,11 @@ endif\n ifndef CVSPS2_PATH\n \tCVSPS2_PATH = cvsps\n endif\n+ifndef CVSPS3_PATH\n+\tCVSPS3_PATH = cvsps\n+endif\n \n-export PERL_PATH PYTHON_PATH CVSPS2_PATH\n+export PERL_PATH PYTHON_PATH CVSPS2_PATH CVSPS3_PATH\n \n LIB_FILE = libgit.a\n XDIFF_LIB = xdiff/lib.a\n@@ -1526,6 +1534,7 @@ PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n PYTHON_PATH_SQ = $(subst ','\\'',$(PYTHON_PATH))\n TCLTK_PATH_SQ = $(subst ','\\'',$(TCLTK_PATH))\n CVSPS2_PATH_SQ = $(subst ','\\'',$(CVSPS2_PATH))\n+CVSPS3_PATH_SQ = $(subst ','\\'',$(CVSPS3_PATH))\n DIFF_SQ = $(subst ','\\'',$(DIFF))\n \n LIBS = $(GITLIBS) $(EXTLIBS)\n@@ -1768,12 +1777,14 @@ ifndef NO_PYTHON\n $(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS\n $(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py\n \t$(QUIET_GEN)$(RM) $@ $@+ && \\\n-\tINSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \\\n+\tINSTLIBDIR_SQ=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \\\n \t\t--no-print-directory prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' \\\n-\t\tinstlibdir` && \\\n+\t\tinstlibdir | \\\n+\t\tsed -e \"s/'/'\\''/g\"` && \\\n \tsed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \\\n \t    -e 's|\\(os\\.getenv(\"GITPYTHONLIB\"\\)[^)]*)|\\1,\"@@INSTLIBDIR@@\")|' \\\n-\t    -e 's|@@INSTLIBDIR@@|'\"$$INSTLIBDIR\"'|g' \\\n+\t    -e 's|@@CVSPS3_PATH@@|$(CVSPS3_PATH_SQ)|g' \\\n+\t    -e 's|@@INSTLIBDIR@@|'\"$$INSTLIBDIR_SQ\"'|g' \\\n \t    $@.py >$@+ && \\\n \tchmod +x $@+ && \\\n \tmv $@+ $@\n@@ -2119,6 +2130,7 @@ GIT-BUILD-OPTIONS: FORCE\n \t@echo SHELL_PATH=\\''$(subst ','\\'',$(SHELL_PATH_SQ))'\\' >$@\n \t@echo PERL_PATH=\\''$(subst ','\\'',$(PERL_PATH_SQ))'\\' >>$@\n \t@echo CVSPS2_PATH=\\''$(subst ','\\'',$(CVSPS2_PATH_SQ))'\\' >>$@\n+\t@echo CVSPS3_PATH=\\''$(subst ','\\'',$(CVSPS3_PATH_SQ))'\\' >>$@\n \t@echo DIFF=\\''$(subst ','\\'',$(subst ','\\'',$(DIFF)))'\\' >>$@\n \t@echo PYTHON_PATH=\\''$(subst ','\\'',$(PYTHON_PATH_SQ))'\\' >>$@\n \t@echo TAR=\\''$(subst ','\\'',$(subst ','\\'',$(TAR)))'\\' >>$@\ndiff --git a/git-cvsimport-3.py b/git-cvsimport-3.py\nnew file mode 100755\nindex 0000000..57f13b7\n--- /dev/null\n+++ b/git-cvsimport-3.py\n@@ -0,0 +1,344 @@\n+#!/usr/bin/env python\n+#\n+# Import CVS history into git\n+#\n+# Intended to be a near-workalike of Matthias Urlichs's Perl implementation.\n+#\n+# By Eric S. Raymond <esr@thyrsus.com>, December 2012\n+# May be redistributed under the license of the git project.\n+\n+import sys\n+\n+if sys.hexversion < 0x02060000:\n+    sys.stderr.write(\"git cvsimport: requires Python 2.6 or later.\\n\")\n+    sys.exit(1)\n+\n+import os, getopt, subprocess, tempfile, shutil\n+\n+DEBUG_COMMANDS = 1\n+\n+class Fatal(Exception):\n+    \"Unrecoverable error.\"\n+    def __init__(self, msg):\n+        Exception.__init__(self)\n+        self.msg = msg\n+\n+def do_or_die(dcmd, legend=\"\"):\n+    \"Either execute a command or raise a fatal exception.\"\n+    if legend:\n+        legend = \" \"  + legend\n+    if verbose >= DEBUG_COMMANDS:\n+        sys.stdout.write(\"git cvsimport: executing '%s'%s\\n\" % (dcmd, legend))\n+    try:\n+        retcode = subprocess.call(dcmd, shell=True)\n+        if retcode < 0:\n+            raise Fatal(\"git cvsimport: child was terminated by signal %d.\" % -retcode)\n+        elif retcode != 0:\n+            raise Fatal(\"git cvsimport: child returned %d.\" % retcode)\n+    except (OSError, IOError) as e:\n+        raise Fatal(\"git cvsimport: execution of %s%s failed: %s\" % (dcmd, legend, e))\n+\n+def capture_or_die(dcmd, legend=\"\"):\n+    \"Either execute a command and capture its output or die.\"\n+    if legend:\n+        legend = \" \"  + legend\n+    if verbose >= DEBUG_COMMANDS:\n+        sys.stdout.write(\"git cvsimport: executing '%s'%s\\n\" % (dcmd, legend))\n+    try:\n+        return subprocess.check_output(dcmd, shell=True)\n+    except subprocess.CalledProcessError as e:\n+        if e.returncode < 0:\n+            sys.stderr.write(\"git cvsimport: child was terminated by signal %d.\" % -e.returncode)\n+        elif e.returncode != 0:\n+            sys.stderr.write(\"git cvsimport: child returned %d.\" % e.returncode)\n+        sys.exit(1)\n+\n+class cvsps:\n+    \"Method class for cvsps back end.\"\n+\n+    cvsps = \"@@CVSPS3_PATH@@\"\n+    def __init__(self):\n+        self.opts = \"\"\n+        self.revmap = None\n+    def set_repo(self, val):\n+        \"Set the repository root option.\"\n+        if not val.startswith(\":\"):\n+            if not val.startswith(os.sep):\n+                val = os.path.abspath(val)\n+            val = \":local:\" + val\n+        self.opts += \" --root '%s'\" % val\n+    def set_authormap(self, val):\n+        \"Set the author-map file.\"\n+        self.opts += \" -A '%s'\" % val\n+    def set_fuzz(self, val):\n+        \"Set the commit-similarity window.\"\n+        self.opts += \" -z %s\" % val\n+    def set_nokeywords(self):\n+        \"Suppress CVS keyword expansion.\"\n+        self.opts += \" -k\"\n+    def add_opts(self, val):\n+        \"Add options to the engine command line.\"\n+        self.opts += \" \" + val\n+    def set_exclusion(self, val):\n+        \"Set a file exclusion regexp.\"\n+        self.opts += \" -n -f '%s'\" % val\n+    def set_after(self, val):\n+        \"Set a date threshold for incremental import.\"\n+        self.opts += \" -d '%s'\" % val\n+    def set_revmap(self, val):\n+        \"Set the file to which the engine should dump a reference map.\"\n+        self.revmap = val\n+        self.opts += \" -R '%s'\" % self.revmap\n+    def set_module(self, val):\n+        \"Set the module to query.\"\n+        self.opts += \" \" + val\n+    def command(self):\n+        \"Emit the command implied by all previous options.\"\n+        return self.cvsps + \"--fast-export \" + self.opts\n+\n+class cvs2git:\n+    \"Method class for cvs2git back end.\"\n+    def __init__(self):\n+        self.opts = \"\"\n+        self.modulepath = \".\"\n+    def set_authormap(self, _val):\n+        \"Set the author-map file.\"\n+        sys.stderr.write(\"git cvsimport: author maping is not supported with cvs2git.\\n\")\n+        sys.exit(1)\n+    def set_repo(self, _val):\n+        \"Set the repository root option.\"\n+        sys.stderr.write(\"git cvsimport: cvs2git must run within a repository checkout directory.\\n\")\n+        sys.exit(1)\n+    def set_fuzz(self, _val):\n+        \"Set the commit-similarity window.\"\n+        sys.stderr.write(\"git cvsimport: fuzz setting is not supported with cvs2git.\\n\")\n+        sys.exit(1)\n+    def set_nokeywords(self):\n+        \"Suppress CVS keyword expansion.\"\n+        self.opts += \" --keywords-off\"\n+    def add_opts(self, val):\n+        \"Add options to the engine command line.\"\n+        self.opts += \" \" + val\n+    def set_exclusion(self, val):\n+        \"Set a file exclusion regexp.\"\n+        self.opts += \" --exclude='%s'\" % val\n+    def set_after(self, _val):\n+        \"Set a date threshold for incremental import.\"\n+        sys.stderr.write(\"git cvsimport: incremental import is not supported with cvs2git.\\n\")\n+        sys.exit(1)\n+    def set_revmap(self, _val):\n+        \"Set the file to which the engine should dump a reference map.\"\n+        sys.stderr.write(\"git cvsimport: can't get a reference map from cvs2git.\\n\")\n+        sys.exit(1)\n+    def set_module(self, val):\n+        \"Set the module to query.\"\n+        self.modulepath = \" \" + val\n+    def command(self):\n+        \"Emit the command implied by all previous options.\"\n+        return \"(cvs2git --username=git-cvsimport --quiet --quiet --blobfile={0} --dumpfile={1} {2} {3} && cat {0} {1} && rm {0} {1})\".format(tempfile.mkstemp()[1], tempfile.mkstemp()[1], self.opts, self.modulepath)\n+\n+class filesource:\n+    \"Method class for file-source back end.\"\n+    def __init__(self, filename):\n+        self.filename = filename\n+    def __complain(self, legend):\n+        sys.stderr.write(\"git cvsimport: %s with file source.\\n\" % legend)\n+        sys.exit(1)\n+    def set_repo(self, _val):\n+        \"Set the repository root option.\"\n+        self.__complain(\"repository can't be set\")\n+    def set_authormap(self, _val):\n+        \"Set the author-map file.\"\n+        sys.stderr.write(\"git cvsimport: author mapping is not supported with filesource.\\n\")\n+        sys.exit(1)\n+    def set_fuzz(self, _val):\n+        \"Set the commit-similarity window.\"\n+        self.__complain(\"fuzz can't be set\")\n+    def set_nokeywords(self, _val):\n+        \"Suppress CVS keyword expansion.\"\n+        self.__complain(\"keyword suppression can't be set\")\n+    def add_opts(self, _val):\n+        \"Add options to the engine command line.\"\n+        self.__complain(\"other options can't be set\")\n+    def set_exclusion(self, _val):\n+        \"Set a file exclusion regexp.\"\n+        self.__complain(\"exclusions can't be set\")\n+    def set_after(self, _val):\n+        \"Set a date threshold for incremental import.\"\n+        pass\n+    def set_revmap(self, _val):\n+        \"Set the file to which the engine should dump a reference map.\"\n+        sys.stderr.write(\"git cvsimport: can't get a reference map from cvs2git.\\n\")\n+        sys.exit(1)\n+    def set_module(self, _val):\n+        \"Set the module to query.\"\n+        self.__complain(\"module can't be set\")\n+    def command(self):\n+        \"Emit the command implied by all previous options.\"\n+        return \"cat \" + self.filename\n+\n+if __name__ == '__main__':\n+    if sys.hexversion < 0x02060000:\n+        sys.stderr.write(\"git cvsimport: requires Python 2.6 or later.\\n\")\n+        sys.exit(1)\n+    (options, arguments) = getopt.getopt(sys.argv[1:], \"vbe:d:C:r:o:ikus:p:z:P:S:aL:A:Rh\")\n+    verbose = 0\n+    bare = False\n+    root = None\n+    outdir = os.getcwd()\n+    remotize = False\n+    import_only = False\n+    underscore_to_dot = False\n+    slashsubst = None\n+    authormap = None\n+    revisionmap = False\n+    backend = cvsps()\n+    for (opt, val) in options:\n+        if opt == '-v':\n+            verbose += 1\n+        elif opt == '-b':\n+            bare = True\n+        elif opt == '-e':\n+            for cls in (cvsps, cvs2git):\n+                if cls.__name__ == val:\n+                    backend = cls()\n+                    break\n+            else:\n+                sys.stderr.write(\"git cvsimport: unknown engine %s.\\n\" % val)\n+                sys.exit(1)\n+        elif opt == '-d':\n+            backend.set_repo(val)\n+        elif opt == '-C':\n+            outdir = val\n+        elif opt == '-r':\n+            remotize = True\n+        elif opt == '-o':\n+            sys.stderr.write(\"git cvsimport: -o is no longer supported.\\n\")\n+            sys.exit(1)\n+        elif opt == '-i':\n+            import_only = True\n+        elif opt == '-k':\n+            backend.set_nokeywords()\n+        elif opt == '-u':\n+            underscore_to_dot = True\n+        elif opt == '-s':\n+            slashsubst = val\n+        elif opt == '-p':\n+            backend.add_opts(val.replace(\",\", \" \"))\n+        elif opt == '-z':\n+            backend.set_fuzz(val)\n+        elif opt == '-P':\n+            backend = filesource(val)\n+            sys.exit(1)\n+        elif opt in ('-m', '-M'):\n+            sys.stderr.write(\"git cvsimport: -m and -M are no longer supported: use reposurgeon instead.\\n\")\n+            sys.exit(1)\n+        elif opt == '-S':\n+            backend.set_exclusion(val)\n+        elif opt == '-a':\n+            sys.stderr.write(\"git cvsimport: -a is no longer supported.\\n\")\n+            sys.exit(1)\n+        elif opt == '-L':\n+            sys.stderr.write(\"git cvsimport: -L is no longer supported.\\n\")\n+            sys.exit(1)\n+        elif opt == '-A':\n+            authormap = os.path.abspath(val)\n+        elif opt == '-R':\n+            revisionmap = True\n+        else:\n+            print \"\"\"\\\n+git cvsimport [-A <author-conv-file>] [-C <git_repository>] [-b] [-d <CVSROOT>]\n+     [-e engine] [-h] [-i] [-k] [-p <options-for-cvsps>] [-P <source-file>]\n+     [-r <remote>] [-R] [-s <subst>] [-S <regex>] [-u] [-v] [-z <fuzz>]\n+     [<CVS_module>]\n+\"\"\"\n+    def metadata(fn, outdir='.'):\n+        if bare:\n+            return os.path.join(outdir, fn)\n+        else:\n+            return os.path.join(outdir, \".git\", fn)\n+\n+    try:\n+        if outdir:\n+            try:\n+                # If the output directory does not exist, create it\n+                # and initialize it as a git repository.\n+                os.mkdir(outdir)\n+                do_or_die(\"git init --quiet \" + outdir)\n+            except OSError:\n+                # Otherwise, assume user wants incremental import.\n+                if not bare and not os.path.exists(os.path.join(outdir, \".git\")):\n+                    raise Fatal(\"output directory is not a git repository\")\n+                threshold = capture_or_die(\"git log -1 --format=%ct\").strip()\n+                backend.set_after(threshold)\n+        if revisionmap:\n+            backend.set_revmap(tempfile.mkstemp()[1])\n+            markmap = tempfile.mkstemp()[1]\n+        if arguments:\n+            backend.set_module(arguments[0])\n+        gitopts = \"\"\n+        if bare:\n+            gitopts += \" --bare\"\n+        if revisionmap:\n+            gitopts += \" --export-marks='%s'\" % markmap\n+        if authormap:\n+            shutil.copyfile(authormap, metadata(\"cvs-authors\", outdir))\n+        if os.path.exists(metadata(\"cvs-authors\", outdir)):\n+            backend.set_authormap(metadata(\"cvs-authors\", outdir))\n+        do_or_die(\"%s | (cd %s >/dev/null; git fast-import --quiet %s)\" \\\n+                  % (backend.command(), outdir, gitopts))\n+        os.chdir(outdir)\n+        if underscore_to_dot or slashsubst:\n+            tagnames = capture_or_die(\"git tag -l\")\n+            for tag in tagnames.split():\n+                if tag:\n+                    changed = tag\n+                    if underscore_to_dot:\n+                        changed = changed.replace(\"_\", \".\")\n+                    if slashsubst:\n+                        changed = changed.replace(os.sep, slashsubst)\n+                    if changed != tag:\n+                        do_or_die(\"git tag -f %s %s >/dev/null\" % (tag, changed))\n+        if underscore_to_dot or slashsubst or remotize:\n+            branchnames = capture_or_die(\"git branch -l\")\n+            for branch in branchnames.split():\n+                if branch:\n+                    # Ugh - fragile dependency on branch -l output format\n+                    branch = branch[2:]\n+                    changed = branch\n+                    if underscore_to_dot:\n+                        changed = changed.replace(\"_\", \".\")\n+                    if slashsubst:\n+                        changed = changed.replace(os.sep, slashsubst)\n+                    if remotize:\n+                        changed = os.path.join(\"remotes\", remotize, branch)\n+                    if changed != branch:\n+                        do_or_die(\"branch --m %s %s >/dev/null\" % (branch, changed))\n+        if revisionmap:\n+            refd = {}\n+            for line in open(backend.revmap):\n+                if line.startswith(\"#\"):\n+                    continue\n+                (fn, rev, mark) = line.split()\n+                refd[(fn, rev)] = mark\n+            markd = {}\n+            for line in open(markmap):\n+                if line.startswith(\"#\"):\n+                    continue\n+                (mark, hashd) = line.split()\n+                markd[mark] = hashd\n+            with open(metadata(\"cvs-revisions\"), \"a\") as wfp:\n+                for ((fn, rev), val) in refd.items():\n+                    if val in markd:\n+                        wfp.write(\"%s %s %s\\n\" % (fn, rev, markd[val]))\n+            os.remove(markmap)\n+            os.remove(backend.revmap)\n+        if not import_only and not bare:\n+            do_or_die(\"git checkout -q\")\n+    except Fatal, err:\n+        sys.stderr.write(\"git_cvsimport: \" + err.msg + \"\\n\")\n+        sys.exit(1)\n+    except KeyboardInterrupt:\n+        pass\n+\n+# end\ndiff --git a/git-cvsimport.sh b/git-cvsimport.sh\nindex 71ba11d..52ce112 100755\n--- a/git-cvsimport.sh\n+++ b/git-cvsimport.sh\n@@ -1,5 +1,5 @@\n #!/bin/sh\n \n-GIT_CVSPS_VERSION=2\n+: ${GIT_CVSPS_VERSION=2}\n \n-exec git cvsimport-$GIT_CVSPS_VERSION \"$@\"\n+exec git \"cvsimport-$GIT_CVSPS_VERSION\" \"$@\"\n-- \n1.8.1.421.g6236851\n"},{"id":"206773","messageId":"1358148351-31552-6-git-send-email-gitster@pobox.com","threadId":"32585","inReplyTo":"1358148351-31552-1-git-send-email-gitster@pobox.com","subject":"[PATCH v2 5/6] cvsimport: make tests reusable for cvsimport-3","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-14T07:25:50Z","receivedAt":"2013-01-14T07:25:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This way, a dual-use test can be dot-sourced into a new test script\nafter defining GIT_CVSPS_VERSION=3 to test that the new cvsimport\nwith cvsps (3.x) works on simple test histories that old cvsimport\ncan grok correctly.\n\nAlso allow CVSPS2_PATH and CVSPS3_PATH to be defined as \"NoThanks\"\nto cause the tests in t96?? series to be skipped.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n t/lib-cvs.sh | 57 +++++++++++++++++++++++++++++++++++++++++++++++----------\n 1 file changed, 47 insertions(+), 10 deletions(-)\n\ndiff --git a/t/lib-cvs.sh b/t/lib-cvs.sh\nindex bdab63c..3a55b8a 100644\n--- a/t/lib-cvs.sh\n+++ b/t/lib-cvs.sh\n@@ -1,5 +1,8 @@\n #!/bin/sh\n \n+: ${TEST_CVSPS_VERSION=2}\n+export TEST_CVSPS_VERSION\n+\n . ./test-lib.sh\n \n unset CVS_SERVER\n@@ -13,22 +16,56 @@ fi\n CVS=\"cvs -f\"\n export CVS\n \n-CVSPS=\"$CVSPS2_PATH\"\n-\n-cvsps_version=`$CVSPS -h 2>&1 | sed -ne 's/cvsps version //p'`\n-case \"$cvsps_version\" in\n-2.1 | 2.2*)\n+skip_all=\n+case \"$TEST_CVSPS_VERSION\" in\n+2)\n+\tif test \"$CVSPS2_PATH\" = NoThanks\n+\tthen\n+\t\tskip_all='skipping cvsimport tests, cvsps(2) not used'\n+\telse\n+\t\tcase $($CVSPS2_PATH -h 2>&1 | sed -ne 's/cvsps version //p') in\n+\t\t2.1 | 2.2*)\n+\t\t\t;;\n+\t\t'')\n+\t\t\tskip_all='skipping cvsimport tests, cvsps(2) not found'\n+\t\t\t;;\n+\t\t*)\n+\t\t\tskip_all='skipping cvsimport tests, unsupported cvsps(2)'\n+\t\t\t;;\n+\t\tesac\n+\tfi\n \t;;\n-'')\n-\tskip_all='skipping cvsimport tests, cvsps not found'\n-\ttest_done\n+3)\n+\tif test \"$CVSPS3_PATH\" = NoThanks\n+\tthen\n+\t\tskip_all='skipping cvsimport tests, cvsps(3) not used'\n+\telse\n+\t\tcase $($CVSPS3_PATH -h 2>&1 | sed -ne 's/cvsps version //p') in\n+\t\t3.*)\n+\t\t\t;;\n+\t\t'')\n+\t\t\tskip_all='skipping cvsimport tests, cvsps(3) not found'\n+\t\t\t;;\n+\t\t*)\n+\t\t\tskip_all='skipping cvsimport tests, unsupported cvsps(3)'\n+\t\t\t;;\n+\t\tesac\n+\tfi\n \t;;\n *)\n-\tskip_all='skipping cvsimport tests, unsupported cvsps version'\n-\ttest_done\n+\techo >&2 \"Bug in test: set TEST_CVSPS_VESION to either 2 or 3\"\n+\texit 1\n \t;;\n esac\n \n+GIT_CVSPS_VERSION=$TEST_CVSPS_VERSION\n+export GIT_CVSPS_VERSION\n+\n+if test -n \"$skip_all\"\n+then\n+\ttest_done\n+fi\n+\n setup_cvs_test_repository () {\n \tCVSROOT=\"$(pwd)/.cvsroot\" &&\n \tcp -r \"$TEST_DIRECTORY/$1/cvsroot\" \"$CVSROOT\" &&\n-- \n1.8.1.421.g6236851\n"},{"id":"206774","messageId":"1358148351-31552-7-git-send-email-gitster@pobox.com","threadId":"32585","inReplyTo":"1358148351-31552-1-git-send-email-gitster@pobox.com","subject":"[PATCH v2 6/6] cvsimport-3: add a sample test","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-14T07:25:51Z","receivedAt":"2013-01-14T07:25:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This is primarily for illustration to show how the support\nintroduced by the previous step can be used.\n\nSome parts of t9600 needs to be made conditional to the value of\nTEST_CVSPS_VERSION to avoid passing options the new cvsimport does\nnot need or understand, before this can become usable as a real\ntest.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n t/t9650-cvsimport3.sh | 4 ++++\n 1 file changed, 4 insertions(+)\n create mode 100755 t/t9650-cvsimport3.sh\n\ndiff --git a/t/t9650-cvsimport3.sh b/t/t9650-cvsimport3.sh\nnew file mode 100755\nindex 0000000..f7ca986\n--- /dev/null\n+++ b/t/t9650-cvsimport3.sh\n@@ -0,0 +1,4 @@\n+#!/bin/sh\n+\n+TEST_CVSPS_VERSION=3\n+. ./t9600-cvsimport.sh\n-- \n1.8.1.421.g6236851\n"},{"id":"206778","messageId":"20130114074741.GM3125@elie.Belkin","threadId":"32585","inReplyTo":"1358148351-31552-1-git-send-email-gitster@pobox.com","subject":"Re: [PATCH v2 0/6] A smoother transition plan for cvsimport","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2013-01-14T07:47:41Z","receivedAt":"2013-01-14T07:47:41Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Junio C Hamano wrote:\n\n> Junio C Hamano (6):\n>   Makefile: add description on PERL/PYTHON_PATH\n>   cvsimport: allow setting a custom cvsps (2.x) program name\n>   cvsimport: introduce a version-switch wrapper\n>   cvsimport: start adding cvsps 3.x support\n>   cvsimport: make tests reusable for cvsimport-3\n>   cvsimport-3: add a sample test\n\nLooks very sane.\n"},{"id":"206779","messageId":"7vmwwcfbn8.fsf_-_@alter.siamese.dyndns.org","threadId":"32585","inReplyTo":"1358148351-31552-1-git-send-email-gitster@pobox.com","subject":"[PATCH v2 7/6] t9600: further prepare for sharing","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-14T07:48:27Z","receivedAt":"2013-01-14T07:48:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When t9650, which dot-sources this script, really becomes ready to\ntest the new cvsimport, we no longer depend on having PERL to run\nthis test through it.  Move the PERL dependency to lib-cvs.sh\ninstead.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n t/lib-cvs.sh         |  7 +++++++\n t/t9600-cvsimport.sh | 30 +++++++++++++++---------------\n 2 files changed, 22 insertions(+), 15 deletions(-)\n\ndiff --git a/t/lib-cvs.sh b/t/lib-cvs.sh\nindex 3a55b8a..d2bf5bf 100644\n--- a/t/lib-cvs.sh\n+++ b/t/lib-cvs.sh\n@@ -22,6 +22,9 @@ case \"$TEST_CVSPS_VERSION\" in\n \tif test \"$CVSPS2_PATH\" = NoThanks\n \tthen\n \t\tskip_all='skipping cvsimport tests, cvsps(2) not used'\n+\telif ! test_have_prereq PERL\n+\tthen\n+\t\tskip_all='skipping cvsimport tests, Perl not available'\n \telse\n \t\tcase $($CVSPS2_PATH -h 2>&1 | sed -ne 's/cvsps version //p') in\n \t\t2.1 | 2.2*)\n@@ -39,6 +42,10 @@ case \"$TEST_CVSPS_VERSION\" in\n \tif test \"$CVSPS3_PATH\" = NoThanks\n \tthen\n \t\tskip_all='skipping cvsimport tests, cvsps(3) not used'\n+\telif ! test_have_prereq PYTHON\n+\tthen\n+\t\t# NEEDSWORK: we may need Python lower-bound prerequisite\n+\t\tskip_all='skipping cvsimport tests, Python not available'\n \telse\n \t\tcase $($CVSPS3_PATH -h 2>&1 | sed -ne 's/cvsps version //p') in\n \t\t3.*)\ndiff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh\nindex 4c384ff..906cbdc 100755\n--- a/t/t9600-cvsimport.sh\n+++ b/t/t9600-cvsimport.sh\n@@ -3,14 +3,14 @@\n test_description='git cvsimport basic tests'\n . ./lib-cvs.sh\n \n-test_expect_success PERL 'setup cvsroot environment' '\n+test_expect_success 'setup cvsroot environment' '\n \tCVSROOT=$(pwd)/cvsroot &&\n \texport CVSROOT\n '\n \n-test_expect_success PERL 'setup cvsroot' '$CVS init'\n+test_expect_success 'setup cvsroot' '$CVS init'\n \n-test_expect_success PERL 'setup a cvs module' '\n+test_expect_success 'setup a cvs module' '\n \n \tmkdir \"$CVSROOT/module\" &&\n \t$CVS co -d module-cvs module &&\n@@ -42,23 +42,23 @@ EOF\n \t)\n '\n \n-test_expect_success PERL 'import a trivial module' '\n+test_expect_success 'import a trivial module' '\n \n \tgit cvsimport -a -R -z 0 -C module-git module &&\n \ttest_cmp module-cvs/o_fortuna module-git/o_fortuna\n \n '\n \n-test_expect_success PERL 'pack refs' '(cd module-git && git gc)'\n+test_expect_success 'pack refs' '(cd module-git && git gc)'\n \n-test_expect_success PERL 'initial import has correct .git/cvs-revisions' '\n+test_expect_success 'initial import has correct .git/cvs-revisions' '\n \n \t(cd module-git &&\n \t git log --format=\"o_fortuna 1.1 %H\" -1) > expected &&\n \ttest_cmp expected module-git/.git/cvs-revisions\n '\n \n-test_expect_success PERL 'update cvs module' '\n+test_expect_success 'update cvs module' '\n \t(cd module-cvs &&\n \tcat <<EOF >o_fortuna &&\n O Fortune,\n@@ -86,7 +86,7 @@ EOF\n \t)\n '\n \n-test_expect_success PERL 'update git module' '\n+test_expect_success 'update git module' '\n \n \t(cd module-git &&\n \tgit config cvsimport.trackRevisions true &&\n@@ -97,7 +97,7 @@ test_expect_success PERL 'update git module' '\n \n '\n \n-test_expect_success PERL 'update has correct .git/cvs-revisions' '\n+test_expect_success 'update has correct .git/cvs-revisions' '\n \n \t(cd module-git &&\n \t git log --format=\"o_fortuna 1.1 %H\" -1 HEAD^ &&\n@@ -105,7 +105,7 @@ test_expect_success PERL 'update has correct .git/cvs-revisions' '\n \ttest_cmp expected module-git/.git/cvs-revisions\n '\n \n-test_expect_success PERL 'update cvs module' '\n+test_expect_success 'update cvs module' '\n \n \t(cd module-cvs &&\n \t\techo 1 >tick &&\n@@ -114,7 +114,7 @@ test_expect_success PERL 'update cvs module' '\n \t)\n '\n \n-test_expect_success PERL 'cvsimport.module config works' '\n+test_expect_success 'cvsimport.module config works' '\n \n \t(cd module-git &&\n \t\tgit config cvsimport.module module &&\n@@ -126,7 +126,7 @@ test_expect_success PERL 'cvsimport.module config works' '\n \n '\n \n-test_expect_success PERL 'second update has correct .git/cvs-revisions' '\n+test_expect_success 'second update has correct .git/cvs-revisions' '\n \n \t(cd module-git &&\n \t git log --format=\"o_fortuna 1.1 %H\" -1 HEAD^^ &&\n@@ -135,7 +135,7 @@ test_expect_success PERL 'second update has correct .git/cvs-revisions' '\n \ttest_cmp expected module-git/.git/cvs-revisions\n '\n \n-test_expect_success PERL 'import from a CVS working tree' '\n+test_expect_success 'import from a CVS working tree' '\n \n \t$CVS co -d import-from-wt module &&\n \t(cd import-from-wt &&\n@@ -148,12 +148,12 @@ test_expect_success PERL 'import from a CVS working tree' '\n \n '\n \n-test_expect_success PERL 'no .git/cvs-revisions created by default' '\n+test_expect_success 'no .git/cvs-revisions created by default' '\n \n \t! test -e import-from-wt/.git/cvs-revisions\n \n '\n \n-test_expect_success PERL 'test entire HEAD' 'test_cmp_branch_tree master'\n+test_expect_success 'test entire HEAD' 'test_cmp_branch_tree master'\n \n test_done\n-- \n1.8.1.421.g6236851\n"},{"id":"206780","messageId":"7vip70fbh8.fsf_-_@alter.siamese.dyndns.org","threadId":"32585","inReplyTo":"1358148351-31552-1-git-send-email-gitster@pobox.com","subject":"[PATCH v2 8/6] t9600: adjust for new cvsimport","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-14T07:52:03Z","receivedAt":"2013-01-14T07:52:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The rewritten cvsimport does not skip the latest 10 minutes worth of\nCVS commits by default, so there is no need to pass the \"-a\" option;\nit will barf if it sees \"-a\".\n\nAlso it will do the merge itself, so there is no need to merge\n\"origin\" ourselves, either.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * This is still untested and primarily to illustrate the concept\n   introduced by the 5/6 patch, together with 6/6.  Testing by\n   interested parties with a working cvsps 3 may be nice.  Something\n   like:\n\n    $ make CVSPS3_PATH=/path/to/your/cvsps3/bin/cvsps\n    $ cd t && sh t9650-cvsimport3.sh\n\n t/t9600-cvsimport.sh | 24 +++++++++++++++++-------\n 1 file changed, 17 insertions(+), 7 deletions(-)\n\ndiff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh\nindex 906cbdc..7b5a9a0 100755\n--- a/t/t9600-cvsimport.sh\n+++ b/t/t9600-cvsimport.sh\n@@ -42,9 +42,13 @@ EOF\n \t)\n '\n \n-test_expect_success 'import a trivial module' '\n+case \"$TEST_CVSPS_VERSION\" in\n+3)\timport_all= ;;\n+*)\timport_all=-a ;;\n+esac &&\n \n-\tgit cvsimport -a -R -z 0 -C module-git module &&\n+test_expect_success 'import a trivial module' '\n+\tgit cvsimport $import_all -R -z 0 -C module-git module &&\n \ttest_cmp module-cvs/o_fortuna module-git/o_fortuna\n \n '\n@@ -90,8 +94,11 @@ test_expect_success 'update git module' '\n \n \t(cd module-git &&\n \tgit config cvsimport.trackRevisions true &&\n-\tgit cvsimport -a -z 0 module &&\n-\tgit merge origin\n+\tgit cvsimport $import_all -z 0 module &&\n+\tif test \"$TEST_CVSPS_VERSION\" = 2\n+\tthen\n+\t\tgit merge origin\n+\tfi\n \t) &&\n \ttest_cmp module-cvs/o_fortuna module-git/o_fortuna\n \n@@ -119,8 +126,11 @@ test_expect_success 'cvsimport.module config works' '\n \t(cd module-git &&\n \t\tgit config cvsimport.module module &&\n \t\tgit config cvsimport.trackRevisions true &&\n-\t\tgit cvsimport -a -z0 &&\n-\t\tgit merge origin\n+\t\tgit cvsimport $import_all -z0 &&\n+\t\tif test \"$TEST_CVSPS_VERSION\" = 2\n+\t\tthen\n+\t\t\tgit merge origin\n+\t\tfi\n \t) &&\n \ttest_cmp module-cvs/tick module-git/tick\n \n@@ -140,7 +150,7 @@ test_expect_success 'import from a CVS working tree' '\n \t$CVS co -d import-from-wt module &&\n \t(cd import-from-wt &&\n \t\tgit config cvsimport.trackRevisions false &&\n-\t\tgit cvsimport -a -z0 &&\n+\t\tgit cvsimport $import_all -z0 &&\n \t\techo 1 >expect &&\n \t\tgit log -1 --pretty=format:%s%n >actual &&\n \t\ttest_cmp actual expect\n-- \n1.8.1.421.g6236851\n"},{"id":"206905","messageId":"CAEUsAPZV6rdFz5R6NN55qYr5se4bFJftE0xGSPAtXLp8jcO0vw@mail.gmail.com","threadId":"32585","inReplyTo":"1358127629-7500-4-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 3/3] cvsimport: start adding cvsps 3.x support","fromName":"Chris Rorvick","fromEmail":"chris@rorvick.com","sentAt":"2013-01-15T06:19:43Z","receivedAt":"2013-01-15T06:19:43Z","isPatch":true,"sender":{"key":"chris@rorvick.com","avatar":"https://avatars.githubusercontent.com/u/824726?v=4"},"body":"On Sun, Jan 13, 2013 at 7:40 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> The new cvsps 3.x series lacks support of some options cvsps 2.x\n> series had and used by cvsimport-2; add a replacement program from\n> the author of cvsps 3.x and allow users to choose it by setting the\n> GIT_CVSPS_VERSION environment variable to 3.  We would later add\n> support to auto-detect the version of installed cvsps to this code\n> when the environment variable is not set.\n>\n> Note that in this step, cvsimport-3 that relies on cvsps 3.x does\n> not have any test coverage.  As cvsimport-3 supports most of the\n> command line options cvsimport-2, we should be able to tweak some of\n> t96xx tests and run them with GIT_CVSPS_VERSION set to both 2 and 3,\n> but that is a topic of later patches that should come on top.\n>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  Makefile           |  18 ++-\n>  git-cvsimport-3.py | 344 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n>  git-cvsimport.sh   |   4 +-\n>  3 files changed, 359 insertions(+), 7 deletions(-)\n>  create mode 100755 git-cvsimport-3.py\n>\n> diff --git a/Makefile b/Makefile\n> index b022db2..060cdc2 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -470,8 +470,9 @@ SCRIPT_PERL += git-relink.perl\n>  SCRIPT_PERL += git-send-email.perl\n>  SCRIPT_PERL += git-svn.perl\n>\n> -SCRIPT_PYTHON += git-remote-testpy.py\n> +SCRIPT_PYTHON += git-cvsimport-3.py\n>  SCRIPT_PYTHON += git-p4.py\n> +SCRIPT_PYTHON += git-remote-testpy.py\n>\n>  SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \\\n>           $(patsubst %.perl,%,$(SCRIPT_PERL)) \\\n> @@ -575,8 +576,11 @@ endif\n>  ifndef CVSPS2_PATH\n>         CVSPS2_PATH = cvsps\n>  endif\n> +ifndef CVSPS3_PATH\n> +       CVSPS3_PATH = cvsps\n> +endif\n>\n> -export PERL_PATH PYTHON_PATH CVSPS2_PATH\n> +export PERL_PATH PYTHON_PATH CVSPS2_PATH CVSPS3_PATH\n>\n>  LIB_FILE = libgit.a\n>  XDIFF_LIB = xdiff/lib.a\n> @@ -1515,6 +1519,7 @@ PERL_PATH_SQ = $(subst ','\\'',$(PERL_PATH))\n>  PYTHON_PATH_SQ = $(subst ','\\'',$(PYTHON_PATH))\n>  TCLTK_PATH_SQ = $(subst ','\\'',$(TCLTK_PATH))\n>  CVSPS2_PATH_SQ = $(subst ','\\'',$(CVSPS2_PATH))\n> +CVSPS3_PATH_SQ = $(subst ','\\'',$(CVSPS3_PATH))\n>  DIFF_SQ = $(subst ','\\'',$(DIFF))\n>\n>  LIBS = $(GITLIBS) $(EXTLIBS)\n> @@ -1757,12 +1762,15 @@ ifndef NO_PYTHON\n>  $(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS\n>  $(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py\n>         $(QUIET_GEN)$(RM) $@ $@+ && \\\n> -       INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \\\n> +       INSTLIBDIR_SQ=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \\\n>                 --no-print-directory prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' \\\n> -               instlibdir` && \\\n> +               instlibdir | \\\n> +               sed -e \"s/'/'\\''/g\"` && \\\n> +       echo \"InstLibDir is <$$INSTLIBDIR_SQ>\" && \\\n>         sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \\\n>             -e 's|\\(os\\.getenv(\"GITPYTHONLIB\"\\)[^)]*)|\\1,\"@@INSTLIBDIR@@\")|' \\\n> -           -e 's|@@INSTLIBDIR@@|'\"$$INSTLIBDIR\"'|g' \\\n> +           -e 's|@@CVSPS3_PATH@@|$(CVSPS3_PATH_SQ)|g' \\\n> +           -e 's|@@INSTLIBDIR@@|'\"$$INSTLIBDIR_SQ\"'|g' \\\n>             $@.py >$@+ && \\\n>         chmod +x $@+ && \\\n>         mv $@+ $@\n> diff --git a/git-cvsimport-3.py b/git-cvsimport-3.py\n> new file mode 100755\n> index 0000000..57f13b7\n> --- /dev/null\n> +++ b/git-cvsimport-3.py\n> @@ -0,0 +1,344 @@\n> +#!/usr/bin/env python\n> +#\n> +# Import CVS history into git\n> +#\n> +# Intended to be a near-workalike of Matthias Urlichs's Perl implementation.\n> +#\n> +# By Eric S. Raymond <esr@thyrsus.com>, December 2012\n> +# May be redistributed under the license of the git project.\n> +\n> +import sys\n> +\n> +if sys.hexversion < 0x02060000:\n> +    sys.stderr.write(\"git cvsimport: requires Python 2.6 or later.\\n\")\n> +    sys.exit(1)\n> +\n> +import os, getopt, subprocess, tempfile, shutil\n> +\n> +DEBUG_COMMANDS = 1\n> +\n> +class Fatal(Exception):\n> +    \"Unrecoverable error.\"\n> +    def __init__(self, msg):\n> +        Exception.__init__(self)\n> +        self.msg = msg\n> +\n> +def do_or_die(dcmd, legend=\"\"):\n> +    \"Either execute a command or raise a fatal exception.\"\n> +    if legend:\n> +        legend = \" \"  + legend\n> +    if verbose >= DEBUG_COMMANDS:\n> +        sys.stdout.write(\"git cvsimport: executing '%s'%s\\n\" % (dcmd, legend))\n> +    try:\n> +        retcode = subprocess.call(dcmd, shell=True)\n> +        if retcode < 0:\n> +            raise Fatal(\"git cvsimport: child was terminated by signal %d.\" % -retcode)\n> +        elif retcode != 0:\n> +            raise Fatal(\"git cvsimport: child returned %d.\" % retcode)\n> +    except (OSError, IOError) as e:\n> +        raise Fatal(\"git cvsimport: execution of %s%s failed: %s\" % (dcmd, legend, e))\n> +\n> +def capture_or_die(dcmd, legend=\"\"):\n> +    \"Either execute a command and capture its output or die.\"\n> +    if legend:\n> +        legend = \" \"  + legend\n> +    if verbose >= DEBUG_COMMANDS:\n> +        sys.stdout.write(\"git cvsimport: executing '%s'%s\\n\" % (dcmd, legend))\n> +    try:\n> +        return subprocess.check_output(dcmd, shell=True)\n> +    except subprocess.CalledProcessError as e:\n> +        if e.returncode < 0:\n> +            sys.stderr.write(\"git cvsimport: child was terminated by signal %d.\" % -e.returncode)\n> +        elif e.returncode != 0:\n> +            sys.stderr.write(\"git cvsimport: child returned %d.\" % e.returncode)\n> +        sys.exit(1)\n> +\n> +class cvsps:\n> +    \"Method class for cvsps back end.\"\n> +\n> +    cvsps = \"@@CVSPS3_PATH@@\"\n> +    def __init__(self):\n> +        self.opts = \"\"\n> +        self.revmap = None\n> +    def set_repo(self, val):\n> +        \"Set the repository root option.\"\n> +        if not val.startswith(\":\"):\n> +            if not val.startswith(os.sep):\n> +                val = os.path.abspath(val)\n> +            val = \":local:\" + val\n> +        self.opts += \" --root '%s'\" % val\n> +    def set_authormap(self, val):\n> +        \"Set the author-map file.\"\n> +        self.opts += \" -A '%s'\" % val\n> +    def set_fuzz(self, val):\n> +        \"Set the commit-similarity window.\"\n> +        self.opts += \" -z %s\" % val\n> +    def set_nokeywords(self):\n> +        \"Suppress CVS keyword expansion.\"\n> +        self.opts += \" -k\"\n> +    def add_opts(self, val):\n> +        \"Add options to the engine command line.\"\n> +        self.opts += \" \" + val\n> +    def set_exclusion(self, val):\n> +        \"Set a file exclusion regexp.\"\n> +        self.opts += \" -n -f '%s'\" % val\n> +    def set_after(self, val):\n> +        \"Set a date threshold for incremental import.\"\n> +        self.opts += \" -d '%s'\" % val\n> +    def set_revmap(self, val):\n> +        \"Set the file to which the engine should dump a reference map.\"\n> +        self.revmap = val\n> +        self.opts += \" -R '%s'\" % self.revmap\n> +    def set_module(self, val):\n> +        \"Set the module to query.\"\n> +        self.opts += \" \" + val\n> +    def command(self):\n> +        \"Emit the command implied by all previous options.\"\n> +        return self.cvsps + \"--fast-export \" + self.opts\n\n\"--fast-export\" string is missing a leading space.  With this fix and\nthe latest cvsps build I'm seeing 6 of 15 failures for t9650 which is\nwhat I was getting out of the patched t9600.\n\nChris\n"},{"id":"206908","messageId":"7vehhn54jq.fsf@alter.siamese.dyndns.org","threadId":"32585","inReplyTo":"CAEUsAPZV6rdFz5R6NN55qYr5se4bFJftE0xGSPAtXLp8jcO0vw@mail.gmail.com","subject":"Re: [PATCH 3/3] cvsimport: start adding cvsps 3.x support","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-15T06:44:09Z","receivedAt":"2013-01-15T06:44:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Chris Rorvick <chris@rorvick.com> writes:\n\n[jc: please elide parts you are not responding to, leaving enough\nlines to understand the context]\n\n>> +    def command(self):\n>> +        \"Emit the command implied by all previous options.\"\n>> +        return self.cvsps + \"--fast-export \" + self.opts\n>\n> \"--fast-export\" string is missing a leading space.  With this fix and\n> the latest cvsps build I'm seeing 6 of 15 failures for t9650 which is\n> what I was getting out of the patched t9600.\n\nThanks; I'll amend it and push the result out, but I think we will\nneed to rip this part out and form the command string in a saner way\n(e.g. if the path needs to have SP in it, e.g. \"/Program Files/cvsps3\",\nthe above would not work correctly anyway).\n"}]}