[RFC PATCH] Add new git-cp command, transposed from stgit.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This git-cp command is recursive when acting on directories, but only
copies files under git control.

---

Addition of this command would be along the lines of "move into git
things from stgit that are not specific to stgit".

A known annoying limitation of this version (very quickly adapted) is
that quoted files in the "git ls-files" output trigger the assert()
clause.  I'd use "-z", but it looks like noone wrote a zero-separated
file reader for python (how that makes me regret perl ;).

I can surely write one, but do we want this to be in python after all ?
At least in perl it would be easy to fix this issue, but do we even
accept perl scripts for such features ?  OTOH, I'm not sure it is
worth rewriting in C...

 git-cp.py |  141 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 141 insertions(+), 0 deletions(-)

diff --git a/git-cp.py b/git-cp.py
new file mode 100755
index 0000000..8df2e73
--- /dev/null
+++ b/git-cp.py
@@ -0,0 +1,141 @@
+#!/usr/bin/python
+#
+# This tool is copyright (c) 2007, Yann Dirson.
+# Parts copyright (c) 2005 Catalin Marinas.
+# It is released under the Gnu Public License, version 2.
+#
+
+import os, sys, getopt, popen2, re
+from shutil import copyfile
+
+def usage():
+    print "USAGE: git-cp <source>... <destination>"
+    sys.exit(1)
+
+## functions copied from stgit.git 0.12
+
+def _output_lines(cmd):
+    p=popen2.Popen3(cmd, True)
+    lines = p.fromchild.readlines()
+    if p.wait():
+        raise Exception, '%s failed (%s)' % (str(cmd),
+                                             p.childerr.read().strip())
+    return lines
+
+def __run(cmd, args=None):
+    """__run: runs cmd using spawnvp.
+
+    Runs cmd using spawnvp.  The shell is avoided so it won't mess up
+    our arguments.  If args is very large, the command is run multiple
+    times; args is split xargs style: cmd is passed on each
+    invocation.  Unlike xargs, returns immediately if any non-zero
+    return code is received.  
+    """
+    
+    args_l=cmd.split()
+    if args is None:
+        args = []
+    for i in range(0, len(args)+1, 100):
+        r=os.spawnvp(os.P_WAIT, args_l[0], args_l + args[i:min(i+100, len(args))])
+    if r:
+        return r
+    return 0
+
+# functions adapted from stgit.git 0.12
+
+def addfiles(names):
+    """Add the files
+    """
+    # check the file list
+    for i in names:
+        if not os.path.exists(i):
+            raise Exception, 'Unknown file: %s' % i
+        if not os.path.isfile(i):
+            raise Exception, '%s is not a plain file' % i
+
+    if names:
+        if __run('git-update-index --add --', names):
+            raise Exception, 'Unable to add file'
+
+# core "git-cp" implementation
+
+def __copy_singlefile(source, target, target2=''):
+    """Copy file or dir named 'source' to name target+target2"""
+
+    # "source" (file or dir) must match one or more git-controlled file
+    realfiles = _output_lines(['git-ls-files', source])
+    if len(realfiles) == 0:
+        raise Exception, '"%s" matches no git-controled files' % source
+
+    if os.path.isdir(source):
+        # physically copy the files, and record them to add them in one run
+        newfiles = []
+        re_string='^'+source+'/(.*)$'
+        prefix_regexp = re.compile(re_string)
+        for f in [f.strip() for f in realfiles]:
+            m = prefix_regexp.match(f)
+            if not m:
+                print '"%s" does not match "%s"' % (f, re_string)
+                assert(m)
+            newname = target+target2+'/'+m.group(1)
+            if not os.path.exists(os.path.dirname(newname)):
+                os.makedirs(os.path.dirname(newname))
+            copyfile(f, newname)
+            newfiles.append(newname)
+
+        addfiles(newfiles)
+    else: # files, symlinks, ...
+        newname = target+target2
+        copyfile(source, newname)
+        addfiles([newname])
+
+
+def copy(filespecs, target):
+    if os.path.isdir(target):
+        # target is a directory: copy each entry on the command line,
+        # with the same name, into the target
+        target = target.rstrip('/')
+        
+        # first, check that none of the children of the target
+        # matching the command line aleady exist
+        for filespec in filespecs:
+            entry = target+ '/' + os.path.basename(filespec.rstrip('/'))
+            if os.path.exists(entry):
+                raise Exception, 'Target "%s" already exists' % entry
+        
+        for filespec in filespecs:
+            filespec = filespec.rstrip('/')
+            basename = '/' + os.path.basename(filespec)
+            __copy_singlefile(filespec, target, basename)
+
+    elif os.path.exists(target):
+        raise Exception, 'Target "%s" exists but is not a directory' % target
+    elif len(filespecs) != 1:
+        raise Exception, 'Cannot copy more than one file to non-directory'
+
+    else:
+        # at this point: len(filespecs)==1 and target does not exist
+
+        # check target directory
+        targetdir = os.path.dirname(target)
+        if targetdir != '' and not os.path.isdir(targetdir):
+            raise Exception, 'Target directory "%s" does not exist' % targetdir
+
+        __copy_singlefile(filespecs[0].rstrip('/'), target)
+
+## main
+
+try:
+    opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
+except getopt.GetoptError:
+    usage()
+
+for o, a in opts:
+    if o in ("-h", "--help"):
+        usage()
+
+try:
+    copy(args[0:-1], args[-1])
+except Exception, ex:
+    print "git-cp error: %s" % ex
+    sys.exit(1)

-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux