chiark / gitweb /
Add 'stg cp' command.
authorYann Dirson <ydirson@altern.org>
Mon, 12 Mar 2007 22:44:21 +0000 (22:44 +0000)
committerCatalin Marinas <catalin.marinas@gmail.com>
Mon, 12 Mar 2007 22:44:21 +0000 (22:44 +0000)
Supports copying several files and dirs into existing dir, as well as
copying single file or dir with a new name.

In the case of directory copying, only copies files known to git (ie,
no generated files).  Should maybe add --all as alternative.

Does not check whether we're going to lose data (overwriting any
modified file, or any file not tracked by git).  This should probably
be done first, before doing any real change (preparing the changes
in-memory, then do the work if OK).  Will give us --dry-run at no
additional cost.

Signed-off-by: Yann Dirson <ydirson@altern.org>
Documentation/stg-cp.txt [new file with mode: 0644]
Documentation/stg.txt
contrib/stgit-completion.bash
stgit/commands/copy.py [new file with mode: 0644]
stgit/git.py
stgit/main.py

diff --git a/Documentation/stg-cp.txt b/Documentation/stg-cp.txt
new file mode 100644 (file)
index 0000000..f499309
--- /dev/null
@@ -0,0 +1,59 @@
+stg-cp(1)
+=========
+Yann Dirson <ydirson@altern.org>
+v0.13, March 2007
+
+NAME
+----
+stg-cp - stgdesc:cp[]
+
+SYNOPSIS
+--------
+stg cp [OPTIONS] <file|dir> <newname>
+
+stg cp [OPTIONS] <files|dirs...> <dir>
+
+DESCRIPTION
+-----------
+
+Make git-controlled copies of git-controlled files.  The copies are
+added to the Git index, so you can add them to a patch with
+stglink:refresh[].
+
+In the first form, copy a single file or a single directory, with a
+new name.  The parent directory of <newname> must already exist;
+<newname> itself must not already exist, or the command will be
+interpreted as one of the second form.
+
+In the second form, copy one or several files and/or directories, into
+an existing directory.
+
+Directories are copied recursively.  Only the git-controlled files
+under the named directories are copied and added to the index.  Any
+file not known to Git will not be copied.
+
+CAVEATS
+-------
+
+The first form doesn't allow yet to overwrite an existing file
+(whether it could be recovered from Git or not), and the second form
+does not check before overwriting any file, possibly leading to loss
+of non-committed modifications.
+
+FUTURE OPTIONS
+--------------
+
+--all::
+       Also copy files not known to Git when copying a directory.
+
+--force::
+       Force overwriting of target files, even if overwritten files
+       have non-committed changes or are not known to Git.
+
+--dry-run::
+       Show which files would be added, and which would be modified
+       if --force would be added.
+
+StGIT
+-----
+Part of the StGIT suite - see gitlink:stg[7].
index 571e53a49a2e48485f1818ef2b91587109c2348b..002606f689ae5676bfca137524d1279f30e7a5c7 100644 (file)
@@ -185,6 +185,8 @@ stglink:add[]::
        stgdesc:add[]
 stglink:rm[]::
        stgdesc:rm[]
+stglink:cp[]::
+       stgdesc:cp[]
 stglink:status[]::
        stgdesc:status[]
 stglink:diff[]::
index 18645dcb3c342849b4d24c84ea71628d2b69d066..09614dc6671420a67916193b473808434615a563 100644 (file)
@@ -20,6 +20,7 @@ _stg_commands="
     clean
     clone
     commit
+    cp
     export
     files
     float
diff --git a/stgit/commands/copy.py b/stgit/commands/copy.py
new file mode 100644 (file)
index 0000000..76e3bf9
--- /dev/null
@@ -0,0 +1,44 @@
+
+__copyright__ = """
+Copyright (C) 2007, Yann Dirson <ydirson@altern.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+import sys, os
+from optparse import OptionParser, make_option
+
+from stgit.commands.common import *
+from stgit.utils import *
+from stgit import stack, git
+
+
+help = 'copy files inside the repository'
+usage = """%prog [options] [<file/dir> <newname> | <files/dirs...> <dir>]
+
+Copy of the files and dirs passed as arguments under another name or
+location inside the same repository."""
+
+options = []
+
+def func(parser, options, args):
+    """Copy files inside the repository
+    """
+    if len(args) < 1:
+        parser.error('incorrect number of arguments')
+
+    if not crt_series.get_current():
+        raise CmdException, 'No patches applied'
+
+    git.copy(args[0:-1], args[-1])
index f9094cdaac670666fce3e14283e8e48a1405b9a8..9129c92a303c9e5f0013a4b04cbe18db6b5e1b39 100644 (file)
@@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 """
 
 import sys, os, popen2, re, gitmergeonefile
+from shutil import copyfile
 
 from stgit import basedir
 from stgit.utils import *
@@ -428,6 +429,62 @@ def add(names):
         if __run('git-update-index --add --', files):
             raise GitException, 'Unable to add file'
 
+def __copy_single(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 GitException, '"%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)
+
+        add(newfiles)
+    else: # files, symlinks, ...
+        newname = target+target2
+        copyfile(source, newname)
+        add([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
+        for filespec in filespecs:
+            filespec = filespec.rstrip('/')
+            basename = '/' + os.path.basename(filespec)
+            __copy_single(filespec, target, basename)
+
+    elif os.path.exists(target):
+        raise GitException, 'Target "%s" exists but is not a directory' % target
+    elif len(filespecs) != 1:
+        raise GitException, '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 GitException, 'Target directory "%s" does not exist' % targetdir
+
+        __copy_single(filespecs[0].rstrip('/'), target)
+        
+
 def rm(files, force = False):
     """Remove a file from the repository
     """
index 9fcbbc2bff88fc0ab0de41b5670ebf659fb99476..856b86866220596ea700610089d65dd80ea3fe20 100644 (file)
@@ -68,6 +68,7 @@ commands = Commands({
     'clean':            'clean',
     'clone':            'clone',
     'commit':           'commit',
+    'cp':              'copy',
     'export':           'export',
     'files':            'files',
     'float':            'float',
@@ -143,6 +144,7 @@ patchcommands = (
     )
 wccommands = (
     'add',
+    'cp',
     'diff',
     'resolved',
     'rm',