Git development
 help / color / mirror / Atom feed
From: Yann Dirson <ydirson@altern.org>
To: git@vger.kernel.org
Subject: [RFC PATCH] Add new git-cp command, transposed from stgit.
Date: Thu, 17 May 2007 22:49:00 +0200	[thread overview]
Message-ID: <20070517204129.17502.21864.stgit@gandelf.nowhere.earth> (raw)

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)

             reply	other threads:[~2007-05-17 20:50 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-05-17 20:49 Yann Dirson [this message]
2007-05-18  7:57 ` [RFC PATCH] Add new git-cp command, transposed from stgit Karl Hasselström

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070517204129.17502.21864.stgit@gandelf.nowhere.earth \
    --to=ydirson@altern.org \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox