[PATCH 2 of 2] bisect: track the current changeset (issue3382)

Bryan O'Sullivan bos at serpentine.com
Wed May 2 19:20:13 CDT 2012


# HG changeset patch
# User Bryan O'Sullivan <bryano at fb.com>
# Date 1336004407 25200
# Branch stable
# Node ID 1dbc32d47be045832b727f3d9a43981ef1962100
# Parent  09909f8f01279b8236ee5a4b1d6db8e6e9a81caf
bisect: track the current changeset (issue3382)

These changes allow --command and --noupdate to be used together,
as otherwise the command being run cannot readily find out what
changeset is currently being bisected.

Introduce a new revset feature, bisect(current), that identifies
the changeset currently being bisected.

When running a command, set the environment variable HG_NODE to
tell the command which changeset is being visited.

The ability to bisect without updating the working dir makes a
big difference to performance under certain circumstances.

diff -r 09909f8f0127 -r 1dbc32d47be0 mercurial/commands.py
--- a/mercurial/commands.py	Wed May 02 17:15:11 2012 -0700
+++ b/mercurial/commands.py	Wed May 02 17:20:07 2012 -0700
@@ -518,10 +518,12 @@
     revision as good or bad without checking it out first.
 
     If you supply a command, it will be used for automatic bisection.
-    Its exit status will be used to mark revisions as good or bad:
-    status 0 means good, 125 means to skip the revision, 127
-    (command not found) will abort the bisection, and any other
-    non-zero exit status means the revision is bad.
+    The environment variable HG_NODE will contain the ID of the
+    changeset being tested. The exit status of the command will be
+    used to mark revisions as good or bad: status 0 means good, 125
+    means to skip the revision, 127 (command not found) will abort the
+    bisection, and any other non-zero exit status means the revision
+    is bad.
 
     .. container:: verbose
 
@@ -561,6 +563,11 @@
 
           hg log -r "bisect(pruned)"
 
+      - see the changeset currently being bisected (especially useful
+        if running with -U/--noupdate)::
+
+          hg log -r "bisect(current)"
+
       - see all changesets that took part in the current bisection::
 
           hg log -r "bisect(range)"
@@ -645,10 +652,22 @@
     if command:
         changesets = 1
         try:
+            node = state['current'][0]
+        except:
+            if noupdate:
+                raise util.Abort(_('current bisect revision is unknown - '
+                                   'start a new bisect to fix'))
+            node, p2 = repo.dirstate.parents()
+            if p2 != nullid:
+                raise util.Abort(_('current bisect revision is a merge'))
+        try:
             while changesets:
                 # update state
+                state['current'] = [node]
                 hbisect.save_state(repo, state)
-                status = util.system(command, out=ui.fout)
+                status = util.system(command,
+                                     environ={'HG_NODE': hex(node)},
+                                     out=ui.fout)
                 if status == 125:
                     transition = "skip"
                 elif status == 0:
@@ -660,7 +679,7 @@
                     raise util.Abort(_("%s killed") % command)
                 else:
                     transition = "bad"
-                ctx = scmutil.revsingle(repo, rev)
+                ctx = scmutil.revsingle(repo, rev, node)
                 rev = None # clear for future iterations
                 state[transition].append(ctx.node())
                 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
@@ -668,9 +687,12 @@
                 # bisect
                 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
                 # update to next check
-                cmdutil.bailifchanged(repo)
-                hg.clean(repo, nodes[0], show_stats=False)
+                node = nodes[0]
+                if not noupdate:
+                    cmdutil.bailifchanged(repo)
+                    hg.clean(repo, node, show_stats=False)
         finally:
+            state['current'] = [node]
             hbisect.save_state(repo, state)
         print_result(nodes, good)
         return
@@ -702,6 +724,8 @@
             if extendnode is not None:
                 ui.write(_("Extending search to changeset %d:%s\n"
                          % (extendnode.rev(), extendnode)))
+                state['current'] = [extendnode]
+                hbisect.save_state(repo, state)
                 if noupdate:
                     return
                 cmdutil.bailifchanged(repo)
@@ -721,6 +745,8 @@
         ui.write(_("Testing changeset %d:%s "
                    "(%d changesets remaining, ~%d tests)\n")
                  % (rev, short(node), changesets, tests))
+        state['current'] = [node]
+        hbisect.save_state(repo, state)
         if not noupdate:
             cmdutil.bailifchanged(repo)
             return hg.clean(repo, node)
diff -r 09909f8f0127 -r 1dbc32d47be0 mercurial/hbisect.py
--- a/mercurial/hbisect.py	Wed May 02 17:15:11 2012 -0700
+++ b/mercurial/hbisect.py	Wed May 02 17:20:07 2012 -0700
@@ -132,7 +132,7 @@
 
 
 def load_state(repo):
-    state = {'good': [], 'bad': [], 'skip': []}
+    state = {'current': [], 'good': [], 'bad': [], 'skip': []}
     if os.path.exists(repo.join("bisect.state")):
         for l in repo.opener("bisect.state"):
             kind, node = l[:-1].split()
@@ -164,10 +164,11 @@
     - ``pruned``             : csets that are goods, bads or skipped
     - ``untested``           : csets whose fate is yet unknown
     - ``ignored``            : csets ignored due to DAG topology
+    - ``current``            : the cset currently being bisected
     """
     state = load_state(repo)
-    if status in ('good', 'bad', 'skip'):
-        return [repo.changelog.rev(n) for n in state[status]]
+    if status in ('good', 'bad', 'skip', 'current'):
+        return map(repo.changelog.rev, state[status])
     else:
         # In the floowing sets, we do *not* call 'bisect()' with more
         # than one level of recusrsion, because that can be very, very
@@ -233,6 +234,9 @@
     if rev in get(repo, 'skip'):
         # i18n: bisect changeset status
         return _('skipped')
+    if rev in get(repo, 'current'):
+        # i18n: bisect changeset status
+        return _('current')
     if rev in get(repo, 'untested'):
         # i18n: bisect changeset status
         return _('untested')
diff -r 09909f8f0127 -r 1dbc32d47be0 mercurial/revset.py
--- a/mercurial/revset.py	Wed May 02 17:15:11 2012 -0700
+++ b/mercurial/revset.py	Wed May 02 17:20:07 2012 -0700
@@ -289,6 +289,7 @@
     - ``pruned``             : csets that are goods, bads or skipped
     - ``untested``           : csets whose fate is yet unknown
     - ``ignored``            : csets ignored due to DAG topology
+    - ``current``            : the cset currently being bisected
     """
     status = getstring(x, _("bisect requires a string")).lower()
     state = set(hbisect.get(repo, status))
diff -r 09909f8f0127 -r 1dbc32d47be0 tests/test-bisect.t
--- a/tests/test-bisect.t	Wed May 02 17:15:11 2012 -0700
+++ b/tests/test-bisect.t	Wed May 02 17:20:07 2012 -0700
@@ -224,6 +224,7 @@
   Testing changeset 12:1941b52820a5 (23 changesets remaining, ~4 tests)
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cat .hg/bisect.state
+  current 1941b52820a544549596820a8ae006842b0e2c64
   skip 9d7d07bc967ca98ad0600c24953fd289ad5fa991
   skip ce8f0998e922c179e80819d5066fbe46e2998784
   skip e7fa0811edb063f6319531f0d0a865882138e180
@@ -396,6 +397,12 @@
   date:        Thu Jan 01 00:00:06 1970 +0000
   summary:     msg 6
   
+  $ hg log -r "bisect(current)"
+  changeset:   5:7874a09ea728
+  user:        test
+  date:        Thu Jan 01 00:00:05 1970 +0000
+  summary:     msg 5
+  
   $ hg log -r "bisect(skip)"
   changeset:   1:5cd978ea5149
   user:        test
@@ -466,3 +473,40 @@
   date:        Thu Jan 01 00:00:06 1970 +0000
   summary:     msg 6
   
+
+
+test bisecting via a command without updating the working dir, and
+ensure that the bisect state file is updated before running a test
+command
+
+  $ hg update null
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ cat > script.sh <<'EOF'
+  > #!/bin/sh
+  > test -n "$HG_NODE" || (echo HG_NODE missing; exit 127)
+  > current="`hg log -r \"bisect(current)\" --template {node}`"
+  > test "$current" = "$HG_NODE" || (echo current is bad: $current; exit 127)
+  > rev="`hg log -r $HG_NODE --template {rev}`"
+  > test "$rev" -ge 6
+  > EOF
+  $ chmod +x script.sh
+  $ hg bisect -r
+  $ hg bisect --good tip --noupdate
+  $ hg bisect --bad 0 --noupdate
+  Testing changeset 15:e7fa0811edb0 (31 changesets remaining, ~4 tests)
+  $ hg bisect --command "'`pwd`/script.sh' and some params" --noupdate
+  Changeset 15:e7fa0811edb0: good
+  Changeset 7:03750880c6b5: good
+  Changeset 3:b53bea5e2fcb: bad
+  Changeset 5:7874a09ea728: bad
+  Changeset 6:a3d5c6fdf0d3: good
+  The first good revision is:
+  changeset:   6:a3d5c6fdf0d3
+  user:        test
+  date:        Thu Jan 01 00:00:06 1970 +0000
+  summary:     msg 6
+  
+
+ensure that we still don't have a working dir
+
+  $ hg parents


More information about the Mercurial-devel mailing list