manifestmerge: handle abort on local unknown, remote created files
authorSiddharth Agarwal <sid0@fb.com>
Sat, 09 Feb 2013 15:36:00 +0000
changeset 18606 95773237df7f
parent 18605 bcf29565d89f
child 18607 26627c30735a
manifestmerge: handle abort on local unknown, remote created files This replaces the _checkunknown call in calculateupdates with a more performant version. On a repository with over 150,000 files, this speeds up an update by 0.6-0.8 seconds, which is up to 25%. This does not introduce any UI changes. There is existing test coverage for every case, mostly in test-merge*.t.
mercurial/merge.py
--- a/mercurial/merge.py	Fri Feb 08 15:23:23 2013 +0000
+++ b/mercurial/merge.py	Sat Feb 09 15:36:00 2013 +0000
@@ -224,7 +224,7 @@
                 m1['.hgsubstate'] += "+"
                 break
 
-    prompts = []
+    aborts, prompts = [], []
     # Compare manifests
     for f, n in m1.iteritems():
         if partial and not partial(f):
@@ -285,15 +285,40 @@
                 actions.append((f2, "m", (f, f, True),
                                 "remote moved to " + f))
         elif f not in ma:
-            if (not overwrite
-                and _checkunknownfile(repo, p1, p2, f)):
-                actions.append((f, "m", (f, f, False),
-                                "remote differs from untracked local"))
+            # local unknown, remote created: the logic is described by the
+            # following table:
+            #
+            # force  branchmerge  different  |  action
+            #   n         *           n      |    get
+            #   n         *           y      |   abort
+            #   y         n           *      |    get
+            #   y         y           n      |    get
+            #   y         y           y      |   merge
+            #
+            # Checking whether the files are different is expensive, so we
+            # don't do that when we can avoid it.
+            if force and not branchmerge:
+                actions.append((f, "g", (m2.flags(f),), "remote created"))
             else:
-                actions.append((f, "g", (m2.flags(f),), "remote created"))
+                different = _checkunknownfile(repo, p1, p2, f)
+                if force and branchmerge and different:
+                    actions.append((f, "m", (f, f, False),
+                                    "remote differs from untracked local"))
+                elif not force and different:
+                    aborts.append((f, "ud"))
+                else:
+                    actions.append((f, "g", (m2.flags(f),), "remote created"))
         elif n != ma[f]:
             prompts.append((f, "dc")) # prompt deleted/changed
 
+    for f, m in sorted(aborts):
+        if m == "ud":
+            repo.ui.warn(_("%s: untracked file differs\n") % f)
+        else: assert False, m
+    if aborts:
+        raise util.Abort(_("untracked files in working directory differ "
+                           "from files in requested revision"))
+
     for f, m in sorted(prompts):
         if m == "cd":
             if repo.ui.promptchoice(
@@ -447,8 +472,6 @@
             _checkcollision(mctx, None)
         else:
             _checkcollision(mctx, (tctx, ancestor))
-    if not force:
-        _checkunknown(repo, tctx, mctx)
     if tctx.rev() is None:
         actions += _forgetremoved(tctx, mctx, branchmerge)
     actions += manifestmerge(repo, tctx, mctx,