Fix the import --url command
[stgit] / stgit / commands / imprt.py
index 150d9ee..8067beb 100644 (file)
@@ -15,7 +15,7 @@ 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, re, email
+import sys, os, re, email, tarfile
 from mailbox import UnixMailbox
 from StringIO import StringIO
 from stgit.argparse import opt
@@ -44,13 +44,15 @@ stack.
 The patch description has to be separated from the data with a '---'
 line."""
 
+args = [argparse.files]
 options = [
     opt('-m', '--mail', action = 'store_true',
         short = 'Import the patch from a standard e-mail file'),
     opt('-M', '--mbox', action = 'store_true',
         short = 'Import a series of patches from an mbox file'),
     opt('-s', '--series', action = 'store_true',
-        short = 'Import a series of patches'),
+        short = 'Import a series of patches', long = """
+        Import a series of patches from a series file or a tar archive."""),
     opt('-u', '--url', action = 'store_true',
         short = 'Import a patch from a URL'),
     opt('-n', '--name',
@@ -61,8 +63,10 @@ options = [
         short = 'Ignore the applied patches in the series'),
     opt('--replace', action = 'store_true',
         short = 'Replace the unapplied patches in the series'),
-    opt('-b', '--base',
+    opt('-b', '--base', args = [argparse.commit],
         short = 'Use BASE instead of HEAD for file importing'),
+    opt('--reject', action = 'store_true',
+        short = 'leave the rejected hunks in corresponding *.rej files'),
     opt('-e', '--edit', action = 'store_true',
         short = 'Invoke an editor for the patch description'),
     opt('-p', '--showpatch', action = 'store_true',
@@ -75,13 +79,9 @@ options = [
         short = 'Use AUTHEMAIL as the author e-mail'),
     opt('--authdate',
         short = 'Use AUTHDATE as the author date'),
-    opt('--commname',
-        short = 'Use COMMNAME as the committer name'),
-    opt('--commemail',
-        short = 'Use COMMEMAIL as the committer e-mail'),
     ] + argparse.sign_options()
 
-directory = DirectoryHasRepository()
+directory = DirectoryHasRepository(log = True)
 
 def __strip_patch_name(name):
     stripped = re.sub('^[0-9]+-(.*)$', '\g<1>', name)
@@ -128,8 +128,6 @@ def __create_patch(filename, message, author_name, author_email,
     if not message:
         can_edit = False
 
-    committer_name = committer_email = None
-
     if options.author:
         options.authname, options.authemail = name_email(options.author)
 
@@ -140,27 +138,21 @@ def __create_patch(filename, message, author_name, author_email,
         author_email = options.authemail
     if options.authdate:
         author_date = options.authdate
-    if options.commname:
-        committer_name = options.commname
-    if options.commemail:
-        committer_email = options.commemail
 
     crt_series.new_patch(patch, message = message, can_edit = False,
                          author_name = author_name,
                          author_email = author_email,
-                         author_date = author_date,
-                         committer_name = committer_name,
-                         committer_email = committer_email)
+                         author_date = author_date)
 
     if not diff:
         out.warn('No diff found, creating empty patch')
     else:
         out.start('Importing patch "%s"' % patch)
         if options.base:
-            git.apply_patch(diff = diff,
-                            base = git_id(crt_series, options.base))
+            base = git_id(crt_series, options.base)
         else:
-            git.apply_patch(diff = diff)
+            base = None
+        git.apply_patch(diff = diff, base = base, reject = options.reject)
         crt_series.refresh_patch(edit = options.edit,
                                  show_patch = options.showpatch,
                                  sign_str = options.sign_str,
@@ -212,7 +204,7 @@ def __import_file(filename, options, patch = None):
                  parse_mail(msg)
     else:
         message, author_name, author_email, author_date, diff = \
-                 parse_patch(f.read())
+                 parse_patch(f.read(), contains_diff = True)
 
     if filename:
         f.close()
@@ -226,6 +218,9 @@ def __import_series(filename, options):
     applied = crt_series.get_applied()
 
     if filename:
+        if tarfile.is_tarfile(filename):
+            __import_tarfile(filename, options)
+            return
         f = file(filename)
         patchdir = os.path.dirname(filename)
     else:
@@ -272,13 +267,51 @@ def __import_url(url, options):
     import tempfile
 
     if not url:
-        parser.error('URL argument required')
+        raise CmdException('URL argument required')
 
     patch = os.path.basename(urllib.unquote(url))
     filename = os.path.join(tempfile.gettempdir(), patch)
     urllib.urlretrieve(url, filename)
     __import_file(filename, options)
 
+def __import_tarfile(tar, options):
+    """Import patch series from a tar archive
+    """
+    import tempfile
+    import shutil
+
+    if not tarfile.is_tarfile(tar):
+        raise CmdException, "%s is not a tarfile!" % tar
+
+    t = tarfile.open(tar, 'r')
+    names = t.getnames()
+
+    # verify paths in the tarfile are safe
+    for n in names:
+        if n.startswith('/'):
+            raise CmdException, "Absolute path found in %s" % tar
+        if n.find("..") > -1:
+            raise CmdException, "Relative path found in %s" % tar
+
+    # find the series file
+    seriesfile = '';
+    for m in names:
+        if m.endswith('/series') or m == 'series':
+            seriesfile = m
+            break
+    if seriesfile == '':
+        raise CmdException, "no 'series' file found in %s" % tar
+
+    # unpack into a tmp dir
+    tmpdir = tempfile.mkdtemp('.stg')
+    t.extractall(tmpdir)
+
+    # apply the series
+    __import_series(os.path.join(tmpdir, seriesfile), options)
+
+    # cleanup the tmpdir
+    shutil.rmtree(tmpdir)
+
 def func(parser, options, args):
     """Import a GNU diff file as a new patch
     """
@@ -294,7 +327,7 @@ def func(parser, options, args):
     else:
         filename = None
 
-    if filename:
+    if not options.url and filename:
         filename = os.path.abspath(filename)
     directory.cd_to_topdir()