{"thread":{"id":"8193","subject":"[RFC PATCH] Add new git-cp command, transposed from stgit.","startedAt":"2007-05-17T20:49:00Z","lastAt":"2007-05-18T07:57:00Z","messageCount":2,"participants":["Yann Dirson","Karl Hasselström"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"42431","messageId":"20070517204129.17502.21864.stgit@gandelf.nowhere.earth","threadId":"8193","inReplyTo":null,"subject":"[RFC PATCH] Add new git-cp command, transposed from stgit.","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2007-05-17T20:49:00Z","receivedAt":"2007-05-17T20:49:00Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"This git-cp command is recursive when acting on directories, but only\ncopies files under git control.\n\n---\n\nAddition of this command would be along the lines of \"move into git\nthings from stgit that are not specific to stgit\".\n\nA known annoying limitation of this version (very quickly adapted) is\nthat quoted files in the \"git ls-files\" output trigger the assert()\nclause.  I'd use \"-z\", but it looks like noone wrote a zero-separated\nfile reader for python (how that makes me regret perl ;).\n\nI can surely write one, but do we want this to be in python after all ?\nAt least in perl it would be easy to fix this issue, but do we even\naccept perl scripts for such features ?  OTOH, I'm not sure it is\nworth rewriting in C...\n\n git-cp.py |  141 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 141 insertions(+), 0 deletions(-)\n\ndiff --git a/git-cp.py b/git-cp.py\nnew file mode 100755\nindex 0000000..8df2e73\n--- /dev/null\n+++ b/git-cp.py\n@@ -0,0 +1,141 @@\n+#!/usr/bin/python\n+#\n+# This tool is copyright (c) 2007, Yann Dirson.\n+# Parts copyright (c) 2005 Catalin Marinas.\n+# It is released under the Gnu Public License, version 2.\n+#\n+\n+import os, sys, getopt, popen2, re\n+from shutil import copyfile\n+\n+def usage():\n+    print \"USAGE: git-cp <source>... <destination>\"\n+    sys.exit(1)\n+\n+## functions copied from stgit.git 0.12\n+\n+def _output_lines(cmd):\n+    p=popen2.Popen3(cmd, True)\n+    lines = p.fromchild.readlines()\n+    if p.wait():\n+        raise Exception, '%s failed (%s)' % (str(cmd),\n+                                             p.childerr.read().strip())\n+    return lines\n+\n+def __run(cmd, args=None):\n+    \"\"\"__run: runs cmd using spawnvp.\n+\n+    Runs cmd using spawnvp.  The shell is avoided so it won't mess up\n+    our arguments.  If args is very large, the command is run multiple\n+    times; args is split xargs style: cmd is passed on each\n+    invocation.  Unlike xargs, returns immediately if any non-zero\n+    return code is received.  \n+    \"\"\"\n+    \n+    args_l=cmd.split()\n+    if args is None:\n+        args = []\n+    for i in range(0, len(args)+1, 100):\n+        r=os.spawnvp(os.P_WAIT, args_l[0], args_l + args[i:min(i+100, len(args))])\n+    if r:\n+        return r\n+    return 0\n+\n+# functions adapted from stgit.git 0.12\n+\n+def addfiles(names):\n+    \"\"\"Add the files\n+    \"\"\"\n+    # check the file list\n+    for i in names:\n+        if not os.path.exists(i):\n+            raise Exception, 'Unknown file: %s' % i\n+        if not os.path.isfile(i):\n+            raise Exception, '%s is not a plain file' % i\n+\n+    if names:\n+        if __run('git-update-index --add --', names):\n+            raise Exception, 'Unable to add file'\n+\n+# core \"git-cp\" implementation\n+\n+def __copy_singlefile(source, target, target2=''):\n+    \"\"\"Copy file or dir named 'source' to name target+target2\"\"\"\n+\n+    # \"source\" (file or dir) must match one or more git-controlled file\n+    realfiles = _output_lines(['git-ls-files', source])\n+    if len(realfiles) == 0:\n+        raise Exception, '\"%s\" matches no git-controled files' % source\n+\n+    if os.path.isdir(source):\n+        # physically copy the files, and record them to add them in one run\n+        newfiles = []\n+        re_string='^'+source+'/(.*)$'\n+        prefix_regexp = re.compile(re_string)\n+        for f in [f.strip() for f in realfiles]:\n+            m = prefix_regexp.match(f)\n+            if not m:\n+                print '\"%s\" does not match \"%s\"' % (f, re_string)\n+                assert(m)\n+            newname = target+target2+'/'+m.group(1)\n+            if not os.path.exists(os.path.dirname(newname)):\n+                os.makedirs(os.path.dirname(newname))\n+            copyfile(f, newname)\n+            newfiles.append(newname)\n+\n+        addfiles(newfiles)\n+    else: # files, symlinks, ...\n+        newname = target+target2\n+        copyfile(source, newname)\n+        addfiles([newname])\n+\n+\n+def copy(filespecs, target):\n+    if os.path.isdir(target):\n+        # target is a directory: copy each entry on the command line,\n+        # with the same name, into the target\n+        target = target.rstrip('/')\n+        \n+        # first, check that none of the children of the target\n+        # matching the command line aleady exist\n+        for filespec in filespecs:\n+            entry = target+ '/' + os.path.basename(filespec.rstrip('/'))\n+            if os.path.exists(entry):\n+                raise Exception, 'Target \"%s\" already exists' % entry\n+        \n+        for filespec in filespecs:\n+            filespec = filespec.rstrip('/')\n+            basename = '/' + os.path.basename(filespec)\n+            __copy_singlefile(filespec, target, basename)\n+\n+    elif os.path.exists(target):\n+        raise Exception, 'Target \"%s\" exists but is not a directory' % target\n+    elif len(filespecs) != 1:\n+        raise Exception, 'Cannot copy more than one file to non-directory'\n+\n+    else:\n+        # at this point: len(filespecs)==1 and target does not exist\n+\n+        # check target directory\n+        targetdir = os.path.dirname(target)\n+        if targetdir != '' and not os.path.isdir(targetdir):\n+            raise Exception, 'Target directory \"%s\" does not exist' % targetdir\n+\n+        __copy_singlefile(filespecs[0].rstrip('/'), target)\n+\n+## main\n+\n+try:\n+    opts, args = getopt.getopt(sys.argv[1:], \"h\", [\"help\"])\n+except getopt.GetoptError:\n+    usage()\n+\n+for o, a in opts:\n+    if o in (\"-h\", \"--help\"):\n+        usage()\n+\n+try:\n+    copy(args[0:-1], args[-1])\n+except Exception, ex:\n+    print \"git-cp error: %s\" % ex\n+    sys.exit(1)\n"},{"id":"42450","messageId":"20070518075700.GA14813@diana.vm.bytemark.co.uk","threadId":"8193","inReplyTo":"20070517204129.17502.21864.stgit@gandelf.nowhere.earth","subject":"Re: [RFC PATCH] Add new git-cp command, transposed from stgit.","fromName":"Karl Hasselström","fromEmail":"kha@treskal.com","sentAt":"2007-05-18T07:57:00Z","receivedAt":"2007-05-18T07:57:00Z","isPatch":true,"sender":{"key":"kha@treskal.com","avatar":"https://gravatar.com/avatar/f0120c734b5279b345075a28521e1ac66acb20c9913ffe9bf6ae97e53f7f3f13?d=mp&s=160"},"body":"On 2007-05-17 22:49:00 +0200, Yann Dirson wrote:\n\n> A known annoying limitation of this version (very quickly adapted)\n> is that quoted files in the \"git ls-files\" output trigger the\n> assert() clause. I'd use \"-z\", but it looks like noone wrote a\n> zero-separated file reader for python (how that makes me regret perl\n> ;).\n>\n> I can surely write one, but do we want this to be in python after\n> all ? At least in perl it would be easy to fix this issue, but do we\n> even accept perl scripts for such features ? OTOH, I'm not sure it\n> is worth rewriting in C...\n\nI was under the impression that perl was somewhat acceptable, while\npython is not used in git anymore?\n\n-- \nKarl Hasselström, kha@treskal.com\n      www.treskal.com/kalle\n"}]}