changeset 580:353a2ce50423

[PATCH] New export patch -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 [PATCH] New export patch From: Bryan O'Sullivan <bos@serpentine.com> Modify export command to accept rev ranges and output file spec. It can now export a range of revisions, and print exported patches to files whose names are generated using format strings. manifest hash: e0085c205cdc31a168bcd25c85772ef00d53031d -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.0 (GNU/Linux) iD8DBQFCx02iywK+sNU5EO8RAtCKAJ0V2K9+i1OGa27KyC5/nq3m+OdvtgCgpnav 3vfEODMzJVOZoJt9wzI1UCg= =YAdI -----END PGP SIGNATURE-----
author mpm@selenic.com
date Sat, 02 Jul 2005 18:29:54 -0800
parents ffeb2c3a1966
children 9db6d5455642
files doc/hg.1.txt mercurial/commands.py
diffstat 2 files changed, 175 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/doc/hg.1.txt	Sat Jul 02 18:25:15 2005 -0800
+++ b/doc/hg.1.txt	Sat Jul 02 18:29:54 2005 -0800
@@ -129,11 +129,27 @@
     revisions are specified, the working directory files are compared
     to its parent.
 
-export [revision]::
-    Print the changeset header and diffs for a particular revision.
+export [-o filespec] [revision] ...::
+    Print the changeset header and diffs for one or more revisions.
+
+    The information shown in the changeset header is: author,
+    changeset hash, parent and commit comment.
+
+    Output may be to a file, in which case the name of the file is
+    given using a format string.  The formatting rules are as follows:
 
-    The information shown in the changeset header is: author, changeset hash,
-    parent and commit comment.
+    %%   literal "%" character
+    %H   changeset hash (40 bytes of hexadecimal)
+    %N   number of patches being generated
+    %R   changeset revision number
+    %b   basename of the exporting repository
+    %h   short-form changeset hash (12 bytes of hexadecimal)
+    %n   zero-padded sequence number, starting at 1
+    %r   zero-padded changeset revision number
+
+    Options:
+
+    -o, --output <filespec>   print output to file with formatted named
 
 forget [files]::
     Undo an 'hg add' scheduled for the next commit.
@@ -317,6 +333,49 @@
     the changelog, manifest, and tracked files, as well as the
     integrity of their crosslinks and indices.
 
+SPECIFYING SINGLE REVISIONS
+---------------------------
+
+    Mercurial accepts several notations for identifying individual
+    revisions.
+
+    A plain integer is treated as a revision number.  Negative
+    integers are treated as offsets from the tip, with -1 denoting the
+    tip.
+
+    A 40-digit hexadecimal string is treated as a unique revision
+    identifier.
+
+    A hexadecimal string less than 40 characters long is treated as a
+    unique revision identifier, and referred to as a short-form
+    identifier.  A short-form identifier is only valid if it is the
+    prefix of one full-length identifier.
+
+    Any other string is treated as a tag name, which is a symbolic
+    name associated with a revision identifier.  Tag names may not
+    contain the ":" character.
+
+    The reserved name "tip" is a special tag that always identifies
+    the most recent revision.
+
+SPECIFYING MULTIPLE REVISIONS
+-----------------------------
+
+    When Mercurial accepts more than one revision, they may be
+    specified individually, or provided as a continuous range,
+    separated by the ":" character.
+
+    The syntax of range notation is [BEGIN]:[END], where BEGIN and END
+    are revision identifiers.  Both BEGIN and END are optional.  If
+    BEGIN is not specified, it defaults to revision number 0.  If END
+    is not specified, it defaults to the tip.  The range ":" thus
+    means "all revisions".
+
+    If BEGIN is greater than END, revisions are treated in reverse
+    order.
+
+    A range acts as an open interval.  This means that a range of 3:5
+    gives 3, 4 and 5.  Similarly, a range of 4:2 gives 4, 3, and 2.
 
 ENVIRONMENT VARIABLES
 ---------------------
--- a/mercurial/commands.py	Sat Jul 02 18:25:15 2005 -0800
+++ b/mercurial/commands.py	Sat Jul 02 18:29:54 2005 -0800
@@ -33,7 +33,47 @@
                  for x in args ]
     return args
 
-def dodiff(ui, repo, files = None, node1 = None, node2 = None):
+revrangesep = ':'
+
+def revrange(ui, repo, revs = [], revlog = None):
+    if revlog is None:
+        revlog = repo.changelog
+    revcount = revlog.count()
+    def fix(val, defval):
+        if not val: return defval
+        try:
+            num = int(val)
+            if str(num) != val: raise ValueError
+            if num < 0: num += revcount
+            if not (0 <= num < revcount):
+                raise ValueError
+        except ValueError:
+            try:
+                num = repo.changelog.rev(repo.lookup(val))
+            except KeyError:
+                try:
+                    num = revlog.rev(revlog.lookup(val))
+                except KeyError:
+                    ui.warn('abort: invalid revision identifier %s\n' % val)
+                    sys.exit(1)
+        return num
+    for spec in revs:
+        if spec.find(revrangesep) >= 0:
+            start, end = spec.split(revrangesep, 1)
+            start = fix(start, 0)
+            end = fix(end, revcount - 1)
+            if end > start:
+                end += 1
+                step = 1
+            else:
+                end -= 1
+                step = -1
+            for rev in xrange(start, end, step):
+                yield str(rev)
+        else:
+            yield spec
+
+def dodiff(fp, ui, repo, files = None, node1 = None, node2 = None):
     def date(c):
         return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
 
@@ -70,15 +110,15 @@
         if f in mmap:
             to = repo.file(f).read(mmap[f])
         tn = read(f)
-        sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
+        fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
     for f in a:
         to = None
         tn = read(f)
-        sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
+        fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
     for f in d:
         to = repo.file(f).read(mmap[f])
         tn = None
-        sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
+        fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
 
 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
     """show a single changeset or file revision"""
@@ -421,24 +461,68 @@
     else:
         files = relpath(repo, [""])
 
-    dodiff(ui, repo, files, *revs)
+    dodiff(sys.stdout, ui, repo, files, *revs)
 
-def export(ui, repo, changeset):
-    """dump the changeset header and diffs for a revision"""
+def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
     node = repo.lookup(changeset)
     prev, other = repo.changelog.parents(node)
     change = repo.changelog.read(node)
-    print "# HG changeset patch"
-    print "# User %s" % change[1]
-    print "# Node ID %s" % hg.hex(node)
-    print "# Parent  %s" % hg.hex(prev)
-    print
+
+    def expand(name):
+        expansions = {
+            '%': lambda: '%',
+            'H': lambda: hg.hex(node),
+            'N': lambda: str(total),
+            'R': lambda: str(repo.changelog.rev(node)),
+            'b': lambda: os.path.basename(repo.root),
+            'h': lambda: hg.short(node),
+            'n': lambda: str(seqno).zfill(len(str(total))),
+            'r': lambda: str(repo.changelog.rev(node)).zfill(revwidth),
+            }
+        newname = []
+        namelen = len(name)
+        i = 0
+        while i < namelen:
+            c = name[i]
+            if c == '%':
+                i += 1
+                c = name[i]
+                c = expansions[c]()
+            newname.append(c)
+            i += 1
+        return ''.join(newname)
+
+    if opts['output'] and opts['output'] != '-':
+        try:
+            fp = open(expand(opts['output']), 'w')
+        except KeyError, inst:
+            ui.warn("error: invalid format spec '%%%s' in output file name\n" %
+                    inst.args[0])
+            sys.exit(1)
+    else:
+        fp = sys.stdout
+
+    print >> fp, "# HG changeset patch"
+    print >> fp, "# User %s" % change[1]
+    print >> fp, "# Node ID %s" % hg.hex(node)
+    print >> fp, "# Parent  %s" % hg.hex(prev)
+    print >> fp
     if other != hg.nullid:
-        print "# Parent  %s" % hg.hex(other)
-    print change[4].rstrip()
-    print
+        print >> fp, "# Parent  %s" % hg.hex(other)
+    print >> fp, change[4].rstrip()
+    print >> fp
+
+    dodiff(fp, ui, repo, None, prev, node)
 
-    dodiff(ui, repo, None, prev, node)
+def export(ui, repo, *changesets, **opts):
+    """dump the header and diffs for one or more changesets"""
+    seqno = 0
+    revs = list(revrange(ui, repo, changesets))
+    total = len(revs)
+    revwidth = max(len(revs[0]), len(revs[-1]))
+    for cset in revs:
+        seqno += 1
+        doexport(ui, repo, cset, seqno, total, revwidth, opts)
 
 def forget(ui, repo, file, *files):
     """don't add the specified files on the next commit"""
@@ -585,7 +669,7 @@
     if cg and not r:
         if opts['update']:
             return update(ui, repo)
-	else:
+        else:
             ui.status("(run 'hg update' to get a working copy)\n")
 
     return r
@@ -679,15 +763,18 @@
     """add a tag for the current tip or a given revision"""
 
     if name == "tip":
-	ui.warn("abort: 'tip' is a reserved name!\n")
-	return -1
+        ui.warn("abort: 'tip' is a reserved name!\n")
+        return -1
+    if name.find(revrangesep) >= 0:
+        ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
+        return -1
 
     (c, a, d, u) = repo.changes(None, None)
     for x in (c, a, d, u):
-	if ".hgtags" in x:
-	    ui.warn("abort: working copy of .hgtags is changed!\n")
+        if ".hgtags" in x:
+            ui.warn("abort: working copy of .hgtags is changed!\n")
             ui.status("(please commit .hgtags manually)\n")
-	    return -1
+            return -1
 
     if rev:
         r = hg.hex(repo.lookup(rev))
@@ -773,7 +860,8 @@
     "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
     "diff": (diff, [('r', 'rev', [], 'revision')],
              'hg diff [-r A] [-r B] [files]'),
-    "export": (export, [], "hg export <changeset>"),
+    "export": (export, [('o', 'output', "", 'output to file')],
+               "hg export [-o file] <changeset> ..."),
     "forget": (forget, [], "hg forget [files]"),
     "heads": (heads, [], 'hg heads'),
     "help": (help, [], 'hg help [command]'),
@@ -790,7 +878,7 @@
     "parents": (parents, [], 'hg parents [node]'),
     "pull": (pull,
                   [('u', 'update', None, 'update working directory')],
-		  'hg pull [options] [source]'),
+                  'hg pull [options] [source]'),
     "push": (push, [], 'hg push <destination>'),
     "rawcommit": (rawcommit,
                   [('p', 'parent', [], 'parent'),
@@ -943,4 +1031,3 @@
         help(u, cmd)
 
     sys.exit(-1)
-