changeset 22405:6f63c47cbb86

dirstate: wrap setparent calls with begin/endparentchange (issue4353) This wraps all the locations of dirstate.setparent with the appropriate begin/endparentchange calls. This will prevent exceptions during those calls from causing incoherent dirstates (issue4353).
author Durham Goode <durham@fb.com>
date Fri, 05 Sep 2014 11:36:20 -0700
parents 12bc7f06fc41
children 2e47e461d2ca
files hgext/histedit.py hgext/mq.py hgext/rebase.py mercurial/cmdutil.py mercurial/commands.py mercurial/context.py mercurial/localrepo.py mercurial/merge.py
diffstat 8 files changed, 28 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/histedit.py	Fri Sep 05 11:34:29 2014 -0700
+++ b/hgext/histedit.py	Fri Sep 05 11:36:20 2014 -0700
@@ -221,6 +221,7 @@
         cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True)
         stats = None
     else:
+        repo.dirstate.beginparentchange()
         try:
             # ui.forcemerge is an internal variable, do not document
             repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
@@ -230,6 +231,7 @@
         finally:
             repo.ui.setconfig('ui', 'forcemerge', '', 'histedit')
         repo.setparents(wcpar, node.nullid)
+        repo.dirstate.endparentchange()
         repo.dirstate.write()
         # fix up dirstate for copies and renames
     cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
--- a/hgext/mq.py	Fri Sep 05 11:34:29 2014 -0700
+++ b/hgext/mq.py	Fri Sep 05 11:36:20 2014 -0700
@@ -816,12 +816,14 @@
                         merged.append(f)
                     else:
                         removed.append(f)
+                repo.dirstate.beginparentchange()
                 for f in removed:
                     repo.dirstate.remove(f)
                 for f in merged:
                     repo.dirstate.merge(f)
                 p1, p2 = repo.dirstate.parents()
                 repo.setparents(p1, merge)
+                repo.dirstate.endparentchange()
 
             if all_files and '.hgsubstate' in all_files:
                 wctx = repo[None]
@@ -1451,7 +1453,7 @@
                 if keepchanges and tobackup:
                     raise util.Abort(_("local changes found, refresh first"))
                 self.backup(repo, tobackup)
-
+                repo.dirstate.beginparentchange()
                 for f in a:
                     util.unlinkpath(repo.wjoin(f), ignoremissing=True)
                     repo.dirstate.drop(f)
@@ -1460,6 +1462,7 @@
                     repo.wwrite(f, fctx.data(), fctx.flags())
                     repo.dirstate.normal(f)
                 repo.setparents(qp, nullid)
+                repo.dirstate.endparentchange()
             for patch in reversed(self.applied[start:end]):
                 self.ui.status(_("popping %s\n") % patch.name)
             del self.applied[start:end]
@@ -1599,6 +1602,7 @@
             bmlist = repo[top].bookmarks()
 
             try:
+                repo.dirstate.beginparentchange()
                 if diffopts.git or diffopts.upgrade:
                     copies = {}
                     for dst in a:
@@ -1651,6 +1655,7 @@
 
                 # assumes strip can roll itself back if interrupted
                 repo.setparents(*cparents)
+                repo.dirstate.endparentchange()
                 self.applied.pop()
                 self.applieddirty = True
                 strip(self.ui, repo, [top], update=False, backup=False)
--- a/hgext/rebase.py	Fri Sep 05 11:34:29 2014 -0700
+++ b/hgext/rebase.py	Fri Sep 05 11:36:20 2014 -0700
@@ -366,7 +366,9 @@
                                           editor=editor)
                 else:
                     # Skip commit if we are collapsing
+                    repo.dirstate.beginparentchange()
                     repo.setparents(repo[p1].node())
+                    repo.dirstate.endparentchange()
                     newrev = None
                 # Update the state
                 if newrev is not None:
@@ -472,7 +474,9 @@
 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
     'Commit the changes and store useful information in extra'
     try:
+        repo.dirstate.beginparentchange()
         repo.setparents(repo[p1].node(), repo[p2].node())
+        repo.dirstate.endparentchange()
         ctx = repo[rev]
         if commitmsg is None:
             commitmsg = ctx.description()
--- a/mercurial/cmdutil.py	Fri Sep 05 11:34:29 2014 -0700
+++ b/mercurial/cmdutil.py	Fri Sep 05 11:36:20 2014 -0700
@@ -659,6 +659,7 @@
 
         n = None
         if update:
+            repo.dirstate.beginparentchange()
             if p1 != parents[0]:
                 updatefunc(repo, p1.node())
             if p2 != parents[1]:
@@ -698,6 +699,7 @@
                 n = repo.commit(message, opts.get('user') or user,
                                 opts.get('date') or date, match=m,
                                 editor=editor, force=partial)
+            repo.dirstate.endparentchange()
         else:
             if opts.get('exact') or opts.get('import_branch'):
                 branch = branch or 'default'
--- a/mercurial/commands.py	Fri Sep 05 11:34:29 2014 -0700
+++ b/mercurial/commands.py	Fri Sep 05 11:36:20 2014 -0700
@@ -483,9 +483,11 @@
             try:
                 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
                              'backout')
+                repo.dirstate.beginparentchange()
                 stats = mergemod.update(repo, parent, True, True, False,
                                         node, False)
                 repo.setparents(op1, op2)
+                repo.dirstate.endparentchange()
                 hg._showstats(repo, stats)
                 if stats[3]:
                     repo.ui.status(_("use 'hg resolve' to retry unresolved "
@@ -2750,7 +2752,9 @@
 
     wlock = repo.wlock()
     try:
+        repo.dirstate.beginparentchange()
         repo.setparents(r1, r2)
+        repo.dirstate.endparentchange()
     finally:
         wlock.release()
 
@@ -3310,10 +3314,12 @@
                 cont = False
 
             # drop the second merge parent
+            repo.dirstate.beginparentchange()
             repo.setparents(current.node(), nullid)
             repo.dirstate.write()
             # fix up dirstate for copies and renames
             cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
+            repo.dirstate.endparentchange()
 
             # commit
             node = repo.commit(text=message, user=user,
@@ -3922,6 +3928,7 @@
     try:
         try:
             wlock = repo.wlock()
+            repo.dirstate.beginparentchange()
             if not opts.get('no_commit'):
                 lock = repo.lock()
                 tr = repo.transaction('import')
@@ -3962,6 +3969,7 @@
                 tr.close()
             if msgs:
                 repo.savecommitmessage('\n* * *\n'.join(msgs))
+            repo.dirstate.endparentchange()
             return ret
         except: # re-raises
             # wlock.release() indirectly calls dirstate.write(): since
--- a/mercurial/context.py	Fri Sep 05 11:34:29 2014 -0700
+++ b/mercurial/context.py	Fri Sep 05 11:36:20 2014 -0700
@@ -1173,11 +1173,13 @@
 
         """
 
+        self._repo.dirstate.beginparentchange()
         for f in self.modified() + self.added():
             self._repo.dirstate.normal(f)
         for f in self.removed():
             self._repo.dirstate.drop(f)
         self._repo.dirstate.setparents(node)
+        self._repo.dirstate.endparentchange()
 
     def dirs(self):
         return self._repo.dirstate.dirs()
--- a/mercurial/localrepo.py	Fri Sep 05 11:34:29 2014 -0700
+++ b/mercurial/localrepo.py	Fri Sep 05 11:36:20 2014 -0700
@@ -758,6 +758,7 @@
         return self[changeid].parents()
 
     def setparents(self, p1, p2=nullid):
+        self.dirstate.beginparentchange()
         copies = self.dirstate.setparents(p1, p2)
         pctx = self[p1]
         if copies:
@@ -771,6 +772,7 @@
             for f, s in sorted(self.dirstate.copies().items()):
                 if f not in pctx and s not in pctx:
                     self.dirstate.copy(None, f)
+        self.dirstate.endparentchange()
 
     def filectx(self, path, changeid=None, fileid=None):
         """changeid can be a changeset revision, node, or tag.
--- a/mercurial/merge.py	Fri Sep 05 11:34:29 2014 -0700
+++ b/mercurial/merge.py	Fri Sep 05 11:36:20 2014 -0700
@@ -1134,6 +1134,7 @@
         stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
 
         if not partial:
+            repo.dirstate.beginparentchange()
             repo.setparents(fp1, fp2)
             recordupdates(repo, actions, branchmerge)
             # update completed, clear state
@@ -1141,6 +1142,7 @@
 
             if not branchmerge:
                 repo.dirstate.setbranch(p2.branch())
+            repo.dirstate.endparentchange()
     finally:
         wlock.release()