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)
next 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