diff mercurial/commands.py @ 14163:38184a72d793

debugbuilddag: use memctx for speed This drops the options to run arbitrary shell commands within commits and the option to create a file that gets appended to in every revision. It now supports to not write file data at all, which is very fast for generating a pure 00changelog.i (useful for discovery tests, for instance). Timings for 1000 linear nodes: * Old `hg debugbuilddag -o '+1000'` took 4.5 secs. * New `hg debugbuilddag -o '+1000'` takes 2 secs. * New `hg debugbuilddag '+1000'` takes 0.8 secs. (The last one creates only 00changelog.i).
author Peter Arrenbrecht <peter.arrenbrecht@gmail.com>
date Mon, 02 May 2011 19:21:30 +0200
parents a8f136f430da
children cb98fed52495
line wrap: on
line diff
--- a/mercurial/commands.py	Mon May 02 19:20:29 2011 +0200
+++ b/mercurial/commands.py	Mon May 02 19:21:30 2011 +0200
@@ -14,7 +14,7 @@
 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
 import merge as mergemod
 import minirst, revset, templatefilters
-import dagparser
+import dagparser, context, simplemerge
 
 # Commands start here, listed alphabetically
 
@@ -962,7 +962,6 @@
 
 def debugbuilddag(ui, repo, text,
                   mergeable_file=False,
-                  appended_file=False,
                   overwritten_file=False,
                   new_file=False):
     """builds a repo with a given dag from scratch in the current empty repo
@@ -979,8 +978,6 @@
      - "/p2" is a merge of the preceding node and p2
      - ":tag" defines a local tag for the preceding node
      - "@branch" sets the named branch for subsequent nodes
-     - "!command" runs the command using your shell
-     - "!!my command\\n" is like "!", but to the end of the line
      - "#...\\n" is a comment up to the end of the line
 
     Whitespace between the above elements is ignored.
@@ -994,28 +991,12 @@
 
     All string valued-elements are either strictly alphanumeric, or must
     be enclosed in double quotes ("..."), with "\\" as escape character.
-
-    Note that the --overwritten-file and --appended-file options imply the
-    use of "HGMERGE=internal:local" during DAG buildup.
     """
 
-    if not (mergeable_file or appended_file or overwritten_file or new_file):
-        raise util.Abort(_('need at least one of -m, -a, -o, -n'))
-
-    if len(repo.changelog) > 0:
+    cl = repo.changelog
+    if len(cl) > 0:
         raise util.Abort(_('repository is not empty'))
 
-    if overwritten_file or appended_file:
-        # we don't want to fail in merges during buildup
-        os.environ['HGMERGE'] = 'internal:local'
-
-    def writefile(fname, text, fmode="wb"):
-        f = open(fname, fmode)
-        try:
-            f.write(text)
-        finally:
-            f.close()
-
     if mergeable_file:
         linesperrev = 2
         # determine number of revs in DAG
@@ -1024,58 +1005,95 @@
             if type == 'n':
                 n += 1
         # make a file with k lines per rev
-        writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
-                  + "\n")
-
-    at = -1
-    atbranch = 'default'
-    for type, data in dagparser.parsedag(text):
-        if type == 'n':
-            ui.status('node %s\n' % str(data))
-            id, ps = data
-            p1 = ps[0]
-            if p1 != at:
-                update(ui, repo, node=str(p1), clean=True)
-                at = p1
-            if repo.dirstate.branch() != atbranch:
-                branch(ui, repo, atbranch, force=True)
-            if len(ps) > 1:
-                p2 = ps[1]
-                merge(ui, repo, node=p2)
-
-            if mergeable_file:
-                f = open("mf", "rb+")
-                try:
-                    lines = f.read().split("\n")
-                    lines[id * linesperrev] += " r%i" % id
-                    f.seek(0)
-                    f.write("\n".join(lines))
-                finally:
-                    f.close()
-
-            if appended_file:
-                writefile("af", "r%i\n" % id, "ab")
-
-            if overwritten_file:
-                writefile("of", "r%i\n" % id)
-
-            if new_file:
-                writefile("nf%i" % id, "r%i\n" % id)
-
-            commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
-            at = id
-        elif type == 'l':
-            id, name = data
-            ui.status('tag %s\n' % name)
-            tag(ui, repo, name, local=True)
-        elif type == 'a':
-            ui.status('branch %s\n' % data)
-            atbranch = data
-        elif type in 'cC':
-            r = util.system(data, cwd=repo.root)
-            if r:
-                desc, r = util.explain_exit(r)
-                raise util.Abort(_('%s command %s') % (data, desc))
+        initialmergedlines = [str(i) for i in xrange(0, n * linesperrev)]
+        initialmergedlines.append("")
+
+    tags = []
+
+    tr = repo.transaction("builddag")
+    try:
+
+        at = -1
+        atbranch = 'default'
+        nodeids = []
+        for type, data in dagparser.parsedag(text):
+            if type == 'n':
+                ui.note('node %s\n' % str(data))
+                id, ps = data
+
+                files = []
+                fctxs = {}
+
+                p2 = None
+                if mergeable_file:
+                    fn = "mf"
+                    p1 = repo[ps[0]]
+                    if len(ps) > 1:
+                        p2 = repo[ps[1]]
+                        pa = p1.ancestor(p2)
+                        base, local, other = [x[fn].data() for x in pa, p1, p2]
+                        m3 = simplemerge.Merge3Text(base, local, other)
+                        ml = [l.strip() for l in m3.merge_lines()]
+                        ml.append("")
+                    elif at > 0:
+                        ml = p1[fn].data().split("\n")
+                    else:
+                        ml = initialmergedlines
+                    ml[id * linesperrev] += " r%i" % id
+                    mergedtext = "\n".join(ml)
+                    files.append(fn)
+                    fctxs[fn] = context.memfilectx(fn, mergedtext)
+
+                if overwritten_file:
+                    fn = "of"
+                    files.append(fn)
+                    fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
+
+                if new_file:
+                    fn = "nf%i" % id
+                    files.append(fn)
+                    fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
+                    if len(ps) > 1:
+                        if not p2:
+                            p2 = repo[ps[1]]
+                        for fn in p2:
+                            if fn.startswith("nf"):
+                                files.append(fn)
+                                fctxs[fn] = p2[fn]
+
+                def fctxfn(repo, cx, path):
+                    return fctxs.get(path)
+
+                if len(ps) == 0 or ps[0] < 0:
+                    pars = [None, None]
+                elif len(ps) == 1:
+                    pars = [nodeids[ps[0]], None]
+                else:
+                    pars = [nodeids[p] for p in ps]
+                cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
+                                    date=(id, 0),
+                                    user="debugbuilddag",
+                                    extra={'branch': atbranch})
+                nodeid = repo.commitctx(cx)
+                nodeids.append(nodeid)
+                at = id
+            elif type == 'l':
+                id, name = data
+                ui.note('tag %s\n' % name)
+                tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
+            elif type == 'a':
+                ui.note('branch %s\n' % data)
+                atbranch = data
+        tr.close()
+    finally:
+        tr.release()
+
+    if tags:
+        tagsf = repo.opener("localtags", "w")
+        try:
+            tagsf.write("".join(tags))
+        finally:
+            tagsf.close()
 
 def debugcommands(ui, cmd='', *args):
     """list all available commands and options"""
@@ -4467,7 +4485,6 @@
     "debugbuilddag":
         (debugbuilddag,
          [('m', 'mergeable-file', None, _('add single file mergeable changes')),
-          ('a', 'appended-file', None, _('add single file all revs append to')),
           ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
           ('n', 'new-file', None, _('add new file at each rev')),
          ],