{"thread":{"id":"5190","subject":"cvs2svn and git progress","startedAt":"2006-08-10T01:29:00Z","lastAt":"2006-08-10T20:36:14Z","messageCount":4,"participants":["Jon Smirl","Martin Langhoff","Jakub Narebski"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"25057","messageId":"9e4733910608091828l1744822cjf001b2722fea6c5a@mail.gmail.com","threadId":"5190","inReplyTo":null,"subject":"cvs2svn and git progress","fromName":"Jon Smirl","fromEmail":"jonsmirl@gmail.com","sentAt":"2006-08-10T01:29:00Z","receivedAt":"2006-08-10T01:29:00Z","isPatch":false,"sender":{"key":"jonsmirl@gmail.com","avatar":"https://gravatar.com/avatar/cff3bf5bfdfa6708b905712ff91f0f9b8aaca161659f38c02b787920d5d28b7e?d=mp&s=160"},"body":"I've finally got cvs2svn running through pass 7 now. It took me a\nwhile to fix errors where it would run five hours and then die with\n\"key error 357\" or something similar.\n\npass 1:  7663 seconds\npass 2:     1 second\npass 3:  1043 seconds\npass 4:     3 seconds\npass 5:   204 seconds\npass 6:  1165 seconds\npass 7:    37 seconds\n\nTotal is a little less than three hours. The new cvs2svn code is many\ntimes faster than the old version which ran for several days. The\ntotal includes the time needed to write the git pack and index file\nfor the revisions.\n\nPass 8 is where the final output is generated. Now that I have a\ndatabase I can only run pass 8 as needed to work on the back-end code.\n\nI believe I have the sha1 from the git revisions correctly in the\nfinal database but I need to write my first try at dumping the change\nsets to be sure.\n\n-- \nJon Smirl\njonsmirl@gmail.com\n"},{"id":"25064","messageId":"46a038f90608100028h3c74d6bx7618f56e3d600657@mail.gmail.com","threadId":"5190","inReplyTo":"9e4733910608091828l1744822cjf001b2722fea6c5a@mail.gmail.com","subject":"Re: cvs2svn and git progress","fromName":"Martin Langhoff","fromEmail":"martin.langhoff@gmail.com","sentAt":"2006-08-10T07:28:33Z","receivedAt":"2006-08-10T07:28:33Z","isPatch":false,"sender":{"key":"martin.langhoff@gmail.com","avatar":"https://gravatar.com/avatar/1e3f311b6c4c15836501901ca58f8c0b0667246488084ba524d8bc9867e22fd9?d=mp&s=160"},"body":"On 8/10/06, Jon Smirl <jonsmirl@gmail.com> wrote:\n> I've finally got cvs2svn running through pass 7 now. It took me a\n\nJon,\n\ngreat stuff. Is this published somewhere I can pull it from?\n\ncheers,\n\n\nmartin\n"},{"id":"25088","messageId":"9e4733910608100632s75539102oda883d08d035ebf9@mail.gmail.com","threadId":"5190","inReplyTo":"46a038f90608100028h3c74d6bx7618f56e3d600657@mail.gmail.com","subject":"Re: cvs2svn and git progress","fromName":"Jon Smirl","fromEmail":"jonsmirl@gmail.com","sentAt":"2006-08-10T13:32:39Z","receivedAt":"2006-08-10T13:32:39Z","isPatch":false,"sender":{"key":"jonsmirl@gmail.com","avatar":"https://gravatar.com/avatar/cff3bf5bfdfa6708b905712ff91f0f9b8aaca161659f38c02b787920d5d28b7e?d=mp&s=160"},"body":"On 8/10/06, Martin Langhoff <martin.langhoff@gmail.com> wrote:\n> On 8/10/06, Jon Smirl <jonsmirl@gmail.com> wrote:\n> > I've finally got cvs2svn running through pass 7 now. It took me a\n>\n> Jon,\n>\n> great stuff. Is this published somewhere I can pull it from?\n\nThis is a diff relative to your git repo of cvs2svn\n\n>\n> cheers,\n>\n>\n> martin\n>\n\n\n-- \nJon Smirl\njonsmirl@gmail.com\n\n\ndiff --git a/cvs2svn_lib/collect_data.py b/cvs2svn_lib/collect_data.py\nindex c05e387..364582d 100644\n--- a/cvs2svn_lib/collect_data.py\n+++ b/cvs2svn_lib/collect_data.py\n@@ -24,6 +24,10 @@ import os\n import re\n import time\n import stat\n+import sha\n+import zlib\n+import struct\n+from subprocess import Popen,PIPE\n \n from cvs2svn_lib.boolean import *\n from cvs2svn_lib.set_support import *\n@@ -52,7 +56,6 @@ from cvs2svn_lib.metadata_database impor\n \n import cvs2svn_rcsparse\n \n-\n branch_tag_re = re.compile(r'''\n     ^\n     ((?:\\d+\\.\\d+\\.)+)   # A nonzero even number of digit groups w/trailing dot\n@@ -70,6 +73,10 @@ # assuming that the non-standard vendor \n # default branch anyway, so we don't want this to match them anyway.\n vendor_revision = re.compile(r'^1\\.1\\.1\\.\\d+$')\n \n+# Used to parse revision deltas which either add or delete text\n+# format is add/delete start range\n+deltaPattern = re.compile(r'^([ad])(\\d+)\\D+(\\d+)$')\n+\n \n def is_trunk_revision(rev):\n   \"\"\"Return True iff REV is a trunk revision.\"\"\"\n@@ -413,6 +420,14 @@ class _FileDataCollector(cvs2svn_rcspars\n \n     pass\n \n+  def set_head_revision(self, revision):\n+    \"\"\"This is a callback method declared in Sink.\"\"\"\n+    \n+    self.git_head = revision\n+    self.git_branches = {} \n+    self.git_next = {}\n+    self.git_text = {}\n+\n   def define_revision(self, revision, timestamp, author, state,\n                       branches, next):\n     \"\"\"This is a callback method declared in Sink.\"\"\"\n@@ -460,6 +475,11 @@ class _FileDataCollector(cvs2svn_rcspars\n         self._primary_dependencies.append( (next, revision,) )\n       else:\n         self._primary_dependencies.append( (revision, next,) )\n+        \n+    # record the CVS diff structure, git code will build the revisions\n+    self.git_branches[revision] = branches\n+    self.git_next[revision] = next\n+\n \n   def _resolve_dependencies(self):\n     \"\"\"Store the primary and branch dependencies into the rev_data objects.\"\"\"\n@@ -476,7 +496,14 @@ class _FileDataCollector(cvs2svn_rcspars\n     for branch_data in self.sdc.branches_data.values():\n       # The branch_data's parent has the branch as a child regardless\n       # of whether the branch had any subsequent commits:\n-      parent_data = self._rev_data[branch_data.parent]\n+      try:\n+        parent_data = self._rev_data[branch_data.parent]\n+      except KeyError:\n+        sys.stderr.write(\"%s: in '%s':\\n\"\n+                       \"   Missing revision %s\\n\"\n+                       % (warning_prefix,\n+                          self.cvs_file.filename, branch_data.parent))\n+        continue\n       parent_data.branches_data.append(branch_data)\n \n       if not Ctx().trunk_only and parent_data.child is not None:\n@@ -497,7 +524,14 @@ class _FileDataCollector(cvs2svn_rcspars\n     for tag_data_list in self.sdc.tags_data.values():\n       for tag_data in tag_data_list:\n         # The tag_data's rev has the tag as a child:\n-        parent_data = self._rev_data[tag_data.rev]\n+        try:\n+          parent_data = self._rev_data[tag_data.rev]\n+        except KeyError:\n+            sys.stderr.write(\"%s: in '%s':\\n\"\n+                       \"   Missing revision %s\\n\"\n+                       % (warning_prefix,\n+                          self.cvs_file.filename, tag_data.rev))\n+        continue\n         parent_data.tags_data.append(tag_data)\n \n         if not Ctx().trunk_only and parent_data.child is not None:\n@@ -708,12 +742,14 @@ class _FileDataCollector(cvs2svn_rcspars\n         self._get_rev_id(rev_data.child),\n         self._determine_operation(rev_data),\n         revision,\n-        bool(text),\n+        bool(text), \"\",\n         lod,\n         rev_data.is_first_on_branch(),\n         tag_ids, branch_ids, closed_symbol_ids)\n     rev_data.c_rev = c_rev\n-    self.collect_data.add_cvs_revision(c_rev)\n+\n+    # record the CVS tree so that git can build the revisions\n+    self.git_text[revision] = text\n \n   def parse_completed(self):\n     \"\"\"Walk through all branches and tags and register them with their\n@@ -725,6 +761,66 @@ class _FileDataCollector(cvs2svn_rcspars\n \n     self.sdc.register_branch_blockers()\n \n+    def git_write_file(revision, text):\n+      header = 'blob ' + str(len(text)) + '\\0'\n+      sha1 = sha.new(header)\n+      sha1.update(text)\n+      digest = sha1.digest()\n+      rev_data = self._rev_data[revision]\n+      rev_data.c_rev.sha = sha1.hexdigest()\n+      self.collect_data.add_cvs_revision(rev_data.c_rev)\n+      print rev_data.c_rev.id, rev_data.c_rev.sha\n+      \n+      if digest not in self.collect_data.object_names:\n+        self.collect_data.object_names[digest] = \"\"\n+        self.collect_data.fimport.stdin.write(struct.pack(\"l\",len(text)))\n+        self.collect_data.fimport.stdin.write(text)\n+        \n+    def git_process_diffs(lines, deltas):\n+      stack = []\n+      delta = 0\n+      while delta < len(deltas):\n+        ops = deltaPattern.search(deltas[delta]).groups()\n+\n+        delta += 1\n+        x = int(ops[1]) - 1\n+        y = int(ops[2])\n+        stack.append([ops[0], x, y, delta])\n+        \n+        if ops[0] == 'a':\n+            delta += y\n+\n+      while stack != []:\n+        ops = stack.pop()\n+        if ops[0] == 'd':\n+          lines[ops[1] : ops[1] + ops[2]] = {}\n+        elif ops[0] == 'a':\n+          lines[ops[1] + 1: ops[1] + 1] = deltas[ops[3] : ops[3] + ops[2]]\n+\n+    def git_process_revs(lines, revision):\n+      \n+      while revision:\n+        deltas = self.git_text[revision].split('\\n')\n+        deltas.pop()\n+        if len(deltas) > 0:\n+          git_process_diffs(lines, deltas)\n+\n+        git_write_file(revision, ''.join(lines))\n+\n+        if len(self.git_branches[revision]):\n+          for branch in self.git_branches[revision]:\n+            git_process_revs(lines[:], branch)\n+\n+        revision = self.git_next[revision]\n+        \n+    revision = self.git_head\n+\n+    git_write_file(revision, self.git_text[revision])\n+    \n+    lines = self.git_text[revision].splitlines(True)\n+    revision = self.git_next[revision]\n+    if revision:\n+      git_process_revs(lines, revision)\n \n ctrl_characters_regexp = re.compile('[\\\\\\x00-\\\\\\x1f\\\\\\x7f]')\n \n@@ -746,8 +842,14 @@ class _ProjectDataCollector:\n     self.found_valid_file = False\n     self.fatal_errors = []\n     self.num_files = 0\n+    collect_data.fimport = Popen(['git-fast-import', 'testme', '760000'], stdin = PIPE)\n+    collect_data.object_names = {}\n+\n     os.path.walk(self.project.project_cvs_repos_path,\n                  _ProjectDataCollector._visit_directory, self)\n+    collect_data.fimport.stdin.close()\n+    collect_data.object_names = {}\n+\n     if not self.fatal_errors and not self.found_valid_file:\n       self.fatal_errors.append(\n           '\\n'\n@@ -783,6 +885,7 @@ class _ProjectDataCollector:\n     self.num_files += 1\n \n   def _visit_directory(self, dirname, files):\n+    \n     for fname in files:\n       verify_filename_legal(fname)\n       if not fname.endswith(',v'):\n@@ -793,7 +896,6 @@ class _ProjectDataCollector:\n \n       self._process_file(pathname)\n \n-\n class CollectData:\n   \"\"\"Repository for data collected by parsing the CVS repository files.\n \n@@ -814,6 +916,7 @@ class CollectData:\n     self.num_files = 0\n     self.symbol_stats = SymbolStatisticsCollector()\n     self.stats_keeper = stats_keeper\n+    self.object_names = []\n \n     # Key generator to generate unique keys for each CVSRevision object:\n     self.key_generator = KeyGenerator()\n@@ -836,5 +939,3 @@ class CollectData:\n \n   def write_symbol_stats(self):\n     self.symbol_stats.write()\n-\n-\ndiff --git a/cvs2svn_lib/cvs_item.py b/cvs2svn_lib/cvs_item.py\nindex beabd7c..995fb76 100644\n--- a/cvs2svn_lib/cvs_item.py\n+++ b/cvs2svn_lib/cvs_item.py\n@@ -46,7 +46,7 @@ class CVSRevision(CVSItem):\n                id, cvs_file,\n                timestamp, metadata_id,\n                prev_id, next_id,\n-               op, rev, deltatext_exists,\n+               op, rev, deltatext_exists, sha,\n                lod, first_on_branch,\n                tag_ids, branch_ids, closed_symbol_ids):\n     \"\"\"Initialize a new CVSRevision object.\n@@ -61,6 +61,7 @@ class CVSRevision(CVSItem):\n        OP              -->  (char) OP_ADD, OP_CHANGE, or OP_DELETE\n        REV             -->  (string) this CVS rev, e.g., '1.3'\n        DELTATEXT_EXISTS-->  (bool) true iff non-empty deltatext\n+       SHA             -->  sha1 of git revision\n        LOD             -->  (LineOfDevelopment) LOD where this rev occurred\n        FIRST_ON_BRANCH -->  (bool) true iff the first rev on its branch\n        TAG_IDS         -->  (list of int) ids of all tags on this revision\n@@ -79,6 +80,7 @@ class CVSRevision(CVSItem):\n     self.prev_id = prev_id\n     self.next_id = next_id\n     self.deltatext_exists = deltatext_exists\n+    self.sha = sha\n     self.lod = lod\n     self.first_on_branch = first_on_branch\n     self.tag_ids = tag_ids\n@@ -113,6 +115,7 @@ class CVSRevision(CVSItem):\n         self.op,\n         self.rev,\n         self.deltatext_exists,\n+        self.sha,\n         lod_id,\n         self.first_on_branch,\n         ' '.join(['%x' % id for id in self.tag_ids]),\n@@ -122,7 +125,7 @@ class CVSRevision(CVSItem):\n   def __setstate__(self, data):\n     (self.id, cvs_file_id, self.timestamp, self.metadata_id,\n      self.prev_id, self.next_id, self.op, self.rev,\n-     self.deltatext_exists, lod_id, self.first_on_branch,\n+     self.deltatext_exists, self.sha, lod_id, self.first_on_branch,\n      tag_ids, branch_ids, closed_symbol_ids) = data\n     self.cvs_file = Ctx()._cvs_file_db.get_file(cvs_file_id)\n     if lod_id is None:\ndiff --git a/cvs2svn_lib/dumpfile_delegate.py b/cvs2svn_lib/dumpfile_delegate.py\nindex fb0606f..03a06d4 100644\n--- a/cvs2svn_lib/dumpfile_delegate.py\n+++ b/cvs2svn_lib/dumpfile_delegate.py\n@@ -232,67 +232,20 @@ class DumpfileDelegate(SVNRepositoryMirr\n     # If the file has keywords, we must prevent CVS/RCS from expanding\n     # the keywords because they must be unexpanded in the repository,\n     # or Subversion will get confused.\n-    pipe_cmd, pipe = Ctx().project.cvs_repository.get_co_pipe(\n-        c_rev, suppress_keyword_substitution=s_item.has_keywords)\n+    #pipe_cmd, pipe = Ctx().project.cvs_repository.get_co_pipe(\n+    #    c_rev, suppress_keyword_substitution=s_item.has_keywords)\n \n     self.dumpfile.write('Node-path: %s\\n'\n                         'Node-kind: file\\n'\n                         'Node-action: %s\\n'\n                         '%s'  # no property header if no props\n-                        'Text-content-length: '\n                         % (self._utf8_path(c_rev.svn_path),\n                            action, props_header))\n \n-    pos = self.dumpfile.tell()\n-\n-    self.dumpfile.write('0000000000000000\\n'\n-                        'Text-content-md5: 00000000000000000000000000000000\\n'\n-                        'Content-length: 0000000000000000\\n'\n-                        '\\n')\n-\n     if prop_contents:\n       self.dumpfile.write(prop_contents)\n \n-    # Insert a filter to convert all EOLs to LFs if neccessary\n-    if s_item.needs_eol_filter:\n-      data_reader = LF_EOL_Filter(pipe.stdout)\n-    else:\n-      data_reader = pipe.stdout\n-\n-    # Insert the rev contents, calculating length and checksum as we go.\n-    checksum = md5.new()\n-    length = 0\n-    while True:\n-      buf = data_reader.read(config.PIPE_READ_SIZE)\n-      if buf == '':\n-        break\n-      checksum.update(buf)\n-      length += len(buf)\n-      self.dumpfile.write(buf)\n-\n-    pipe.stdout.close()\n-    error_output = pipe.stderr.read()\n-    exit_status = pipe.wait()\n-    if exit_status:\n-      raise FatalError(\"The command '%s' failed with exit status: %s\\n\"\n-                       \"and the following output:\\n\"\n-                       \"%s\" % (pipe_cmd, exit_status, error_output))\n-\n-    # Go back to patch up the length and checksum headers:\n-    self.dumpfile.seek(pos, 0)\n-    # We left 16 zeros for the text length; replace them with the real\n-    # length, padded on the left with spaces:\n-    self.dumpfile.write('%16d' % length)\n-    # 16... + 1 newline + len('Text-content-md5: ') == 35\n-    self.dumpfile.seek(pos + 35, 0)\n-    self.dumpfile.write(checksum.hexdigest())\n-    # 35... + 32 bytes of checksum + 1 newline + len('Content-length: ') == 84\n-    self.dumpfile.seek(pos + 84, 0)\n-    # The content length is the length of property data, text data,\n-    # and any metadata around/inside around them.\n-    self.dumpfile.write('%16d' % (length + len(prop_contents)))\n-    # Jump back to the end of the stream\n-    self.dumpfile.seek(0, 2)\n+    self.dumpfile.write('Git-sha1: %s\\n' % (c_rev.sha))\n \n     # This record is done (write two newlines -- one to terminate\n     # contents that weren't themselves newline-termination, one to\ndiff --git a/cvs2svn_rcsparse/debug.py b/cvs2svn_rcsparse/debug.py\nindex cfeaf2b..c2d143d 100644\n--- a/cvs2svn_rcsparse/debug.py\n+++ b/cvs2svn_rcsparse/debug.py\n@@ -1,22 +1,113 @@\n-# -*-python-*-\n #\n-# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.\n+# Copyright (C) 2000-2002 The ViewCVS Group. All Rights Reserved.\n #\n # By using this file, you agree to the terms and conditions set forth in\n-# the LICENSE.html file which can be found at the top level of the ViewVC\n-# distribution or at http://viewvc.org/license-1.html.\n+# the LICENSE.html file which can be found at the top level of the ViewCVS\n+# distribution or at http://viewcvs.sourceforge.net/license-1.html.\n #\n-# For more information, visit http://viewvc.org/\n+# Contact information:\n+#   Greg Stein, PO Box 760, Palo Alto, CA, 94302\n+#   gstein@lyra.org, http://viewcvs.sourceforge.net/\n+#\n+# -----------------------------------------------------------------------\n+#\n+# This software is being maintained as part of the ViewCVS project.\n+# Information is available at:\n+#    http://viewcvs.sourceforge.net/\n #\n # -----------------------------------------------------------------------\n \n \"\"\"debug.py: various debugging tools for the rcsparse package.\"\"\"\n \n import time\n+import re\n+import sha\n+import zlib\n+import os\n+import struct\n+from subprocess import Popen,PIPE\n \n from __init__ import parse\n import common\n \n+deltaPattern = re.compile(r'^([ad])(\\d+)\\D+(\\d+)$')\n+\n+class RevisionsSink(common.Sink):\n+  \n+  def __init__(self, fimport):\n+    self.fimport = fimport\n+                         \n+  def set_head_revision(self, revision):\n+    self.git_head = revision\n+    self.git_branches = {} \n+    self.git_next = {}\n+    self.git_text = {}\n+\n+  def define_revision(self, revision, timestamp, author, state,\n+                      branches, next):\n+    self.git_branches[revision] = branches\n+    self.git_next[revision] = next\n+\n+  def set_revision_info(self, revision, log, text):\n+    self.git_text[revision] = text\n+\n+  def parse_completed(self):\n+\n+    def write_file(text):\n+      header = 'blob ' + str(len(text)) + '\\0'\n+      sha1 = sha.new(header)\n+      sha1.update(text)\n+      name = sha1.hexdigest()\n+      \n+      print 'length is ', len(text)\n+      self.fimport.stdin.write(struct.pack(\"l\",len(text)))\n+      self.fimport.stdin.write(text)\n+        \n+    def process_diffs(lines, deltas):\n+      stack = []\n+      delta = 0\n+      while delta < len(deltas):\n+        ops = deltaPattern.search(deltas[delta]).groups()\n+\n+        delta += 1\n+        x = int(ops[1]) - 1\n+        y = int(ops[2])\n+        stack.append([ops[0], x, y, delta])\n+        \n+        if ops[0] == 'a':\n+            delta += y\n+\n+      while stack != []:\n+        ops = stack.pop()\n+        if ops[0] == 'd':\n+          lines[ops[1] : ops[1] + ops[2]] = {}\n+        elif ops[0] == 'a':\n+          lines[ops[1] + 1: ops[1] + 1] = deltas[ops[3] : ops[3] + ops[2]]\n+\n+    def process_revs(lines, revision):\n+      \n+      while revision:\n+        deltas = self.git_text[revision].split('\\n')\n+        deltas.pop()\n+        if len(deltas) > 0:\n+          process_diffs(lines, deltas)\n+\n+        write_file(''.join(lines))\n+\n+        if len(self.git_branches[revision]):\n+          for branch in self.git_branches[revision]:\n+            process_revs(lines[:], branch)\n+\n+        revision = self.git_next[revision]\n+        \n+    revision = self.git_head\n+\n+    write_file(self.git_text[revision])\n+    \n+    lines = self.git_text[revision].splitlines(True)\n+    revision = self.git_next[revision]\n+    if revision:\n+      process_revs(lines, revision)\n \n class DebugSink(common.Sink):\n   def set_head_revision(self, revision):\n@@ -46,7 +137,7 @@ class DebugSink(common.Sink):\n   def set_revision_info(self, revision, log, text):\n     print 'revision:', revision\n     print '    log:', log\n-    print '    text:', text[:100], '...'\n+    print '    text:', text\n \n \n class DumpSink(common.Sink):\n@@ -90,9 +181,17 @@ class DumpSink(common.Sink):\n     print 'parse_completed'\n \n \n+def debug_file(fname):\n+  parse(open(fname, 'rb'), DebugSink())\n+\n def dump_file(fname):\n   parse(open(fname, 'rb'), DumpSink())\n \n+def revisions_file(fname):\n+  fimport = Popen(['git-fast-import', 'testme'], stdin = PIPE)\n+  parse(open(fname, 'rb'), RevisionsSink(fimport))\n+  fimport.stdin.close()\n+\n def time_file(fname):\n   f = open(fname, 'rb')\n   s = common.Sink()\n@@ -116,7 +215,11 @@ if __name__ == '__main__':\n     _usage()\n   if sys.argv[1] == 'dump':\n     dump_file(sys.argv[2])\n+  elif sys.argv[1] == 'debug':\n+    debug_file(sys.argv[2])\n   elif sys.argv[1] == 'time':\n     time_file(sys.argv[2])\n+  elif sys.argv[1] == 'revisions':\n+    revisions_file(sys.argv[2])\n   else:\n     _usage()\ndiff --git a/cvs2svn_rcsparse/default.py b/cvs2svn_rcsparse/default.py\nindex 14c9958..4e108f4 100644\n--- a/cvs2svn_rcsparse/default.py\n+++ b/cvs2svn_rcsparse/default.py\n@@ -24,7 +24,7 @@ class _TokenStream:\n   # the algorithm is about the same speed for any CHUNK_SIZE chosen.\n   # grab a good-sized chunk, but not too large to overwhelm memory.\n   # note: we use a multiple of a standard block size\n-  CHUNK_SIZE  = 192 * 512  # about 100k\n+  CHUNK_SIZE  = 4096 * 512  # about 2MB\n \n #  CHUNK_SIZE  = 5\t# for debugging, make the function grind...\n \n"},{"id":"25114","messageId":"ebg5b5$5rc$1@sea.gmane.org","threadId":"5190","inReplyTo":"9e4733910608091828l1744822cjf001b2722fea6c5a@mail.gmail.com","subject":"Re: cvs2svn and git progress","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-08-10T20:36:14Z","receivedAt":"2006-08-10T20:36:14Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Could you please put proper info about your cvs2svn (cvs2git?) work on\nGitWiki?\n  http://git.or.cz/gitwiki/InterfacesFrontendsAndTools\nThanks in advance\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"}]}