Patchwork [1,of,2,V2,STABLE] unshelve: don't commit unknown files during unshelve (issue4113)

login
register
mail settings
Submitter Durham Goode
Date Nov. 27, 2013, 12:43 a.m.
Message ID <8b6d3738dee65def3b8c.1385512988@dev350.prn1.facebook.com>
Download mbox | patch
Permalink /patch/3172/
State Accepted
Commit 578b888c820e9b556d9ff63c6a83be213232d625
Headers show

Comments

Durham Goode - Nov. 27, 2013, 12:43 a.m.
# HG changeset patch
# User Durham Goode <durham@fb.com>
# Date 1385511785 28800
#      Tue Nov 26 16:23:05 2013 -0800
# Branch stable
# Node ID 8b6d3738dee65def3b8c6db2fa0970bf4f31dc18
# Parent  2ca325ea57fa13909e28cc2caeae3c73e60693f8
unshelve: don't commit unknown files during unshelve (issue4113)

Previously, unshelve would temporarily commit unknown files (via addremove) in
an attempt to allow unshelving into unknown files.  This produced unexpected
results, like the file time stamp changing and a .i file being created.

This change makes it no longer use addremove.  It ignores unknown files
completely.  If an unshelve would overwrite an unknown file, the unknown file is
moved to *.orig

The shelve continue/abort format is changed, but it just removes stuff from the
end of the file, so it can still read the old format.
Thomas Arendsen Hein - Nov. 27, 2013, 8:51 a.m.
* Durham Goode <durham@fb.com> [20131127 01:43]:
> # HG changeset patch
> # User Durham Goode <durham@fb.com>
> # Date 1385511785 28800
> #      Tue Nov 26 16:23:05 2013 -0800
> # Branch stable
> # Node ID 8b6d3738dee65def3b8c6db2fa0970bf4f31dc18
> # Parent  2ca325ea57fa13909e28cc2caeae3c73e60693f8
> unshelve: don't commit unknown files during unshelve (issue4113)

As mentioned in the tracker:
This patch fixes (at least) my test case for this issue and
the critical issue4112, too.

But if an unknown file should be changed by the unshelve, I get:

remote changed foo which local deleted
use (c)hanged version or leave (d)eleted?

When I select (c), the file gets overwritten with the shelved
version, but no .orig file is created!

When I select (d), the unknown file is kept, but the shelved version
is lost!

Expected behaviour: No prompt, the unknown file should be moved to
.orig and the shelved file should be checked out.

Regards,
Thomas

Patch

diff --git a/hgext/shelve.py b/hgext/shelve.py
--- a/hgext/shelve.py
+++ b/hgext/shelve.py
@@ -91,7 +91,6 @@ 
             pendingctx = fp.readline().strip()
             parents = [bin(h) for h in fp.readline().split()]
             stripnodes = [bin(h) for h in fp.readline().split()]
-            unknownfiles = fp.readline()[:-1].split('\0')
         finally:
             fp.close()
 
@@ -101,13 +100,11 @@ 
         obj.pendingctx = repo[bin(pendingctx)]
         obj.parents = parents
         obj.stripnodes = stripnodes
-        obj.unknownfiles = unknownfiles
 
         return obj
 
     @classmethod
-    def save(cls, repo, name, originalwctx, pendingctx, stripnodes,
-             unknownfiles):
+    def save(cls, repo, name, originalwctx, pendingctx, stripnodes):
         fp = repo.opener(cls._filename, 'wb')
         fp.write('%i\n' % cls._version)
         fp.write('%s\n' % name)
@@ -115,7 +112,6 @@ 
         fp.write('%s\n' % hex(pendingctx.node()))
         fp.write('%s\n' % ' '.join([hex(p) for p in repo.dirstate.parents()]))
         fp.write('%s\n' % ' '.join([hex(n) for n in stripnodes]))
-        fp.write('%s\n' % '\0'.join(unknownfiles))
         fp.close()
 
     @classmethod
@@ -379,7 +375,7 @@ 
 
         lock = repo.lock()
 
-        mergefiles(ui, repo, state.wctx, state.pendingctx, state.unknownfiles)
+        mergefiles(ui, repo, state.wctx, state.pendingctx)
 
         repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
         shelvedstate.clear(repo)
@@ -387,9 +383,9 @@ 
     finally:
         lockmod.release(lock, wlock)
 
-def mergefiles(ui, repo, wctx, shelvectx, unknownfiles):
+def mergefiles(ui, repo, wctx, shelvectx):
     """updates to wctx and merges the changes from shelvectx into the
-    dirstate. drops any files in unknownfiles from the dirstate."""
+    dirstate."""
     oldquiet = ui.quiet
     try:
         ui.quiet = True
@@ -397,17 +393,18 @@ 
         files = []
         files.extend(shelvectx.files())
         files.extend(shelvectx.parents()[0].files())
+
+        # revert will overwrite unknown files, so move them out of the way
+        m, a, r, d, u = repo.status(unknown=True)[:5]
+        for file in u:
+            if file in files:
+                util.rename(file, file + ".orig")
         cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
                        *pathtofiles(repo, files),
                        **{'no_backup': True})
     finally:
         ui.quiet = oldquiet
 
-    # Send untracked files back to being untracked
-    dirstate = repo.dirstate
-    for f in unknownfiles:
-        dirstate.drop(f)
-
 def unshelvecleanup(ui, repo, name, opts):
     """remove related files after an unshelve"""
     if not opts['keep']:
@@ -446,7 +443,7 @@ 
             # rebase was a no-op, so it produced no child commit
             shelvectx = state.pendingctx
 
-        mergefiles(ui, repo, state.wctx, shelvectx, state.unknownfiles)
+        mergefiles(ui, repo, state.wctx, shelvectx)
 
         state.stripnodes.append(shelvectx.node())
         repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
@@ -538,8 +535,8 @@ 
         # to the original wctx.
 
         # Store pending changes in a commit
-        m, a, r, d, u = repo.status(unknown=True)[:5]
-        if m or a or r or d or u:
+        m, a, r, d = repo.status()[:4]
+        if m or a or r or d:
             def commitfunc(ui, repo, message, match, opts):
                 hasmq = util.safehasattr(repo, 'mq')
                 if hasmq:
@@ -554,7 +551,6 @@ 
 
             tempopts = {}
             tempopts['message'] = "pending changes temporary commit"
-            tempopts['addremove'] = True
             oldquiet = ui.quiet
             try:
                 ui.quiet = True
@@ -588,7 +584,7 @@ 
 
                 stripnodes = [repo.changelog.node(rev)
                               for rev in xrange(oldtiprev, len(repo))]
-                shelvedstate.save(repo, basename, wctx, tmpwctx, stripnodes, u)
+                shelvedstate.save(repo, basename, wctx, tmpwctx, stripnodes)
 
                 util.rename(repo.join('rebasestate'),
                             repo.join('unshelverebasestate'))
@@ -603,7 +599,7 @@ 
                 # rebase was a no-op, so it produced no child commit
                 shelvectx = tmpwctx
 
-        mergefiles(ui, repo, wctx, shelvectx, u)
+        mergefiles(ui, repo, wctx, shelvectx)
         shelvedstate.clear(repo)
 
         # The transaction aborting will strip all the commits for us,