Patchwork [1,of,2,V2,STABLE] add: add back forgotten files even when not matching exactly

login
register
mail settings
Submitter Martin von Zweigbergk
Date Nov. 11, 2014, 5:59 p.m.
Message ID <48a648e2b6d64082109c.1415728799@martinvonz.mtv.corp.google.com>
Download mbox | patch
Permalink /patch/6683/
State Accepted
Headers show

Comments

Martin von Zweigbergk - Nov. 11, 2014, 5:59 p.m.
# HG changeset patch
# User Martin von Zweigbergk <martinvonz@google.com>
# Date 1415659878 28800
#      Mon Nov 10 14:51:18 2014 -0800
# Node ID 48a648e2b6d64082109c722fac4e4ea105167635
# Parent  2d54aa5397cdb1c697673ba10b7618d5ac25c69e
add: add back forgotten files even when not matching exactly

I accidentally did 'hg forget .' and tried to undo the operation with
'hg add .'. I expected the files to be reported as either modified or
clean, but they were still reported as removed. It turns out that
forgotten files are only added back if they are listed explicitly, as
shown by the following two invocations. This makes it hard to recover
from the mistake of forgetting a lot of files.

  $ hg forget README && hg add README && hg status -A README
  C README
  $ hg forget README && hg add . && hg status -A README
  R README

The problem lies in cmdutil.add(). That method checks that the file
isn't already tracked before adding it, but it does so by checking the
dirstate, which does have an entry for forgotten files (state 'r'). We
should instead be checking whether the file exists in the
workingctx. The workingctx is also what we later call add() on, and
that method takes care of transforming the add() into a normallookup()
on the dirstate.

Since we're changing repo.dirstate into wctx, let's also change
repo.walk into wctx.walk for consistency (repo.walk calls wctx.walk,
so we're simply inlining the call).

Patch

diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1982,9 +1982,9 @@ 
     abort, warn = scmutil.checkportabilityalert(ui)
     if abort or warn:
         cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
-    for f in repo.walk(match):
+    for f in wctx.walk(match):
         exact = match.exact(f)
-        if exact or not explicitonly and f not in repo.dirstate:
+        if exact or not explicitonly and f not in wctx:
             if cca:
                 cca(f)
             names.append(f)
diff --git a/tests/test-add.t b/tests/test-add.t
--- a/tests/test-add.t
+++ b/tests/test-add.t
@@ -126,6 +126,19 @@ 
   M a
   ? a.orig
 
+Forgotten file can be added back (as either clean or modified)
+
+  $ hg forget b
+  $ hg add b
+  $ hg st -A b
+  C b
+  $ hg forget b
+  $ echo modified > b
+  $ hg add b
+  $ hg st -A b
+  M b
+  $ hg revert -qC b
+
   $ hg add c && echo "unexpected addition of missing file"
   c: * (glob)
   [1]