Patchwork [stable] merge: don't overwrite file untracked after remove, abort with 'untracked files'

login
register
mail settings
Submitter Mads Kiilerich
Date Feb. 9, 2014, 11:46 p.m.
Message ID <e4d7cbc94219e54f5e73.1391989602@localhost.localdomain>
Download mbox | patch
Permalink /patch/3524/
State Accepted
Commit e4d7cbc94219e54f5e73df9c2f88eca3d46d7f90
Headers show

Comments

Mads Kiilerich - Feb. 9, 2014, 11:46 p.m.
# HG changeset patch
# User Mads Kiilerich <madski@unity3d.com>
# Date 1391989434 -3600
#      Mon Feb 10 00:43:54 2014 +0100
# Branch stable
# Node ID e4d7cbc94219e54f5e73df9c2f88eca3d46d7f90
# Parent  6863d42eb59a39ea483eec0728b3a5d6836d20b6
merge: don't overwrite file untracked after remove, abort with 'untracked files'

Merge could overwrite untracked files and cause data loss.

Instead we now handle the 'local side removed file and has untracked file
instead' case as the 'other side added file that local has untracked' case:

  FILE: untracked file exists
  abort: untracked files in working directory differ from files in requested revision

It could perhaps make sense to create .orig files when overwriting, either
instead of aborting or when overwriting anyway because of force ... but for now
we stay consistent with similar cases.
Matt Mackall - Feb. 10, 2014, 12:16 a.m.
On Mon, 2014-02-10 at 00:46 +0100, Mads Kiilerich wrote:
> # HG changeset patch
> # User Mads Kiilerich <madski@unity3d.com>
> # Date 1391989434 -3600
> #      Mon Feb 10 00:43:54 2014 +0100
> # Branch stable
> # Node ID e4d7cbc94219e54f5e73df9c2f88eca3d46d7f90
> # Parent  6863d42eb59a39ea483eec0728b3a5d6836d20b6
> merge: don't overwrite file untracked after remove, abort with 'untracked files'

Queued for stable, thanks.

Patch

diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -343,7 +343,12 @@  def manifestmerge(repo, wctx, p2, pa, br
                 else:
                     actions.append((f, "g", (fl2,), "remote created"))
         elif n2 and n2 != ma[f]:
-            prompts.append((f, "dc")) # prompt deleted/changed
+            different = _checkunknownfile(repo, wctx, p2, f)
+            if not force and different:
+                aborts.append((f, "ud"))
+            else:
+                # if different: old untracked f may be overwritten and lost
+                prompts.append((f, "dc")) # prompt deleted/changed
 
     for f, m in sorted(aborts):
         if m == "ud":
diff --git a/tests/test-merge-remove.t b/tests/test-merge-remove.t
--- a/tests/test-merge-remove.t
+++ b/tests/test-merge-remove.t
@@ -85,3 +85,30 @@  Reverting foo1 and bar:
 
   $ hg diff
 
+Merge should not overwrite local file that is untracked after remove
+
+  $ rm *
+  $ hg up -qC
+  $ hg rm bar
+  $ hg ci -m 'remove bar'
+  $ echo 'memories of buried pirate treasure' > bar
+  $ hg merge
+  bar: untracked file differs
+  abort: untracked files in working directory differ from files in requested revision
+  [255]
+  $ cat bar
+  memories of buried pirate treasure
+
+Those who use force will lose
+
+  $ hg merge -f
+  remote changed bar which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  merging foo1 and foo to foo1
+  1 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ cat bar
+  bleh
+  $ hg st
+  M bar
+  M foo1