diff --git a/hgext/largefiles/lfcommands.py b/hgext/largefiles/lfcommands.py --- a/hgext/largefiles/lfcommands.py +++ b/hgext/largefiles/lfcommands.py @@ -248,7 +248,7 @@ def _commitcontext(rdst, parents, ctx, d mctx = context.memctx(rdst, parents, ctx.description(), dstfiles, getfilectx, ctx.user(), ctx.date(), ctx.extra()) ret = rdst.commitctx(mctx) - rdst.dirstate.setparents(ret) + rdst.setparents(ret) revmap[ctx.node()] = rdst.changelog.tip() # Generate list of changed files diff --git a/hgext/mq.py b/hgext/mq.py --- a/hgext/mq.py +++ b/hgext/mq.py @@ -749,7 +749,7 @@ class queue(object): for f in merged: repo.dirstate.merge(f) p1, p2 = repo.dirstate.parents() - repo.dirstate.setparents(p1, merge) + repo.setparents(p1, merge) match = scmutil.matchfiles(repo, files or []) oldtip = repo['tip'] @@ -1355,7 +1355,7 @@ class queue(object): fctx = ctx[f] repo.wwrite(f, fctx.data(), fctx.flags()) repo.dirstate.normal(f) - repo.dirstate.setparents(qp, nullid) + repo.setparents(qp, nullid) for patch in reversed(self.applied[start:end]): self.ui.status(_("popping %s\n") % patch.name) del self.applied[start:end] @@ -1546,7 +1546,7 @@ class queue(object): oldphase = repo[top].phase() # assumes strip can roll itself back if interrupted - repo.dirstate.setparents(*cparents) + repo.setparents(*cparents) self.applied.pop() self.applieddirty = True self.strip(repo, [top], update=False, diff --git a/hgext/rebase.py b/hgext/rebase.py --- a/hgext/rebase.py +++ b/hgext/rebase.py @@ -277,7 +277,7 @@ def rebase(ui, repo, **opts): editor=editor) else: # Skip commit if we are collapsing - repo.dirstate.setparents(repo[p1].node()) + repo.setparents(repo[p1].node()) newrev = None # Update the state if newrev is not None: @@ -361,7 +361,7 @@ def checkexternal(repo, state, targetanc def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None): 'Commit the changes and store useful information in extra' try: - repo.dirstate.setparents(repo[p1].node(), repo[p2].node()) + repo.setparents(repo[p1].node(), repo[p2].node()) ctx = repo[rev] if commitmsg is None: commitmsg = ctx.description() diff --git a/hgext/transplant.py b/hgext/transplant.py --- a/hgext/transplant.py +++ b/hgext/transplant.py @@ -274,7 +274,7 @@ class transplanter(object): files = None if merge: p1, p2 = repo.dirstate.parents() - repo.dirstate.setparents(p1, node) + repo.setparents(p1, node) m = match.always(repo.root, '') else: m = match.exact(repo.root, '', files) @@ -340,7 +340,7 @@ class transplanter(object): _('working dir not at transplant parent %s') % revlog.hex(parent)) if merge: - repo.dirstate.setparents(p1, parents[1]) + repo.setparents(p1, parents[1]) n = repo.commit(message, user, date, extra=extra, editor=self.editor) if not n: diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py --- a/mercurial/cmdutil.py +++ b/mercurial/cmdutil.py @@ -1384,7 +1384,7 @@ def amend(ui, repo, commitfunc, old, ext newid = repo.commitctx(new) if newid != old.node(): # Reroute the working copy parent to the new changeset - repo.dirstate.setparents(newid, nullid) + repo.setparents(newid, nullid) # Move bookmarks from old parent to amend commit bms = repo.nodebookmarks(old.node()) diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -2270,7 +2270,7 @@ def debugsetparents(ui, repo, rev1, rev2 wlock = repo.wlock() try: - repo.dirstate.setparents(r1, r2) + repo.setparents(r1, r2) finally: wlock.release() @@ -2693,7 +2693,7 @@ def graft(ui, repo, *revs, **opts): finally: ui.setconfig('ui', 'forcemerge', '') # drop the second merge parent - repo.dirstate.setparents(current.node(), nullid) + repo.setparents(current.node(), nullid) repo.dirstate.write() # fix up dirstate for copies and renames cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev()) @@ -3635,7 +3635,7 @@ def import_(ui, repo, patch1=None, *patc if p1 != parents[0]: hg.clean(repo, p1.node()) if p2 != parents[1]: - repo.dirstate.setparents(p1.node(), p2.node()) + repo.setparents(p1.node(), p2.node()) if opts.get('exact') or opts.get('import_branch'): repo.dirstate.setbranch(branch or 'default') diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -237,14 +237,26 @@ class dirstate(object): return encoding.tolocal(self._branch) def setparents(self, p1, p2=nullid): + """Set dirstate parents to p1 and p2. + + When moving from two parents to one, 'm' merged entries a + adjusted to normal and previous copy records discarded and + returned by the call. + + See localrepo.setparents() + """ self._dirty = self._dirtypl = True oldp2 = self._pl[1] self._pl = p1, p2 + copies = {} if oldp2 != nullid and p2 == nullid: # Discard 'm' markers when moving away from a merge state for f, s in self._map.iteritems(): if s[0] == 'm': + if f in self._copymap: + copies[f] = self._copymap[f] self.normallookup(f) + return copies def setbranch(self, branch): if branch in ['tip', '.', 'null']: diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -633,6 +633,17 @@ class localrepository(repo.repository): '''get list of changectxs for parents of changeid''' return self[changeid].parents() + def setparents(self, p1, p2=nullid): + copies = self.dirstate.setparents(p1, p2) + if copies: + # Adjust copy records, the dirstate cannot do it, it + # requires access to parents manifests. Preserve them + # only for entries added to first parent. + pctx = self[p1] + for f in copies: + if f not in pctx and copies[f] in pctx: + self.dirstate.copy(copies[f], f) + def filectx(self, path, changeid=None, fileid=None): """changeid can be a changeset revision, node, or tag. fileid can be a file revision or node.""" diff --git a/mercurial/merge.py b/mercurial/merge.py --- a/mercurial/merge.py +++ b/mercurial/merge.py @@ -596,7 +596,7 @@ def update(repo, node, branchmerge, forc stats = applyupdates(repo, action, wc, p2, pa, overwrite) if not partial: - repo.dirstate.setparents(fp1, fp2) + repo.setparents(fp1, fp2) recordupdates(repo, action, branchmerge) if not branchmerge: repo.dirstate.setbranch(p2.branch()) diff --git a/tests/bundles/rename.sh b/tests/bundles/rename.sh new file mode 100755 --- /dev/null +++ b/tests/bundles/rename.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# @ 3: 'move2' +# | +# o 2: 'move1' +# | +# | o 1: 'change' +# |/ +# o 0: 'add' + +hg init copies +cd copies +echo a > a +echo b > b +echo c > c +hg ci -Am add +echo a >> a +echo b >> b +echo c >> c +hg ci -m change +hg up -qC 0 +hg cp a d +hg mv b e +hg mv c f +hg ci -m move1 +hg mv e g +hg mv f c +hg ci -m move2 +hg bundle -a ../renames.hg +cd .. diff --git a/tests/bundles/renames.hg b/tests/bundles/renames.hg new file mode 100644 index 0000000000000000000000000000000000000000..977a8454d038945828ee3ff6f30d7c0fc9b8ee66 GIT binary patch literal 1600 zc$@)12EX}8M=>x$T4*^jL0KkKS!T~X@f>7`kI)~$WK!v zCK)DZjXfF-G8$-}fF@5sG}?_anDnO5BYLN`JtIvtWjzO}h#F--R6qa#20#F415E=# zp`Zb#fXDyY0WC7{} z)EWR98UO$Q0LTMC0B8UJ02%-Yr7|=OfCE4P&;S4c05miPfB*mkOn?9y00SdH41fRt z0000G0004y02%<(K+tGt0BN8y000bzMhF3*G6A8WX{LZ^27mx$0g;daz_>~0I9?$~ zvok(XYRL*N%j!|)7QU>`9799{O}=DMiJ&Cd z!HE=rF)1Y3iJ+C-fYL>boOx4ck)yWsBJ@|F!-&|;0@sZ6D+q3~l^!=h@>>H7bd@9! zdSuKDOjaic5aKH#LD1@ott?6(mq?Ora7PDp1_b7TqFG!Eft(4tzLjis3cvyLk7_pv zXt9)u{rGxG*-d(o-n5l)pQYR;BUz*$ctspYF)F*ZzNEtKGN52j6k0ztiN1AEW(kHA zn5TmpX4T+kdCH*+8P(^sy$1D}NE6v9Hqc3FePQqhmncE$q$fYPPE@j?k$tBz5@JMT z6KFE1Irat|!^29qSx~8=;$gHsrWsLnCL}_NqcI5ulvW#C54GHELujZZ_0fOO`P^eH0lpNL2`yMO{K*0gvkv@^Y zCT|R=I!dHPcQhnc#y*-hjiy(IvQ}Q7!ItCO z`oNW-llNJa34e4~RHxr9K5V38G$2NmMWvBFmr8Qg?-*9X>&}4(#3C{KeW2?~j-P&L zP!Dn1lD8eI48j3$(g79eMGOm2&A?bL7Z+-$M8z(`ti=fuEu`!TLz9RY0Rji2!x@!w zRYp*HQ1=5oqkSk)aBWz1L(K#@X3XI2FmSuq5>W{NNh!e~lWVgc9%(qwW;r8xm&3_kM&d}Ds18vkaSc??WvtiB%e$UrctwovqPG?Yfne^T|@P zc@JbLvFnO5a1N}Rs)R1+549Q6(fOe`Rp;`RYIc-+kT1DSwyRj+gkCpr11tzQbg+}! z%fD@7ab-o+13fkc;PB{v)}GSl=J(UdP4(TCUar diff --git a/tests/test-rebase-collapse.t b/tests/test-rebase-collapse.t --- a/tests/test-rebase-collapse.t +++ b/tests/test-rebase-collapse.t @@ -541,3 +541,52 @@ Interactions between collapse and keepbr @@ -0,0 +1,2 @@ +d +blah + + $ cd .. + +Rebase, collapse and copies + + $ hg init copies + $ cd copies + $ hg unbundle "$TESTDIR/bundles/renames.hg" + adding changesets + adding manifests + adding file changes + added 4 changesets with 11 changes to 7 files (+1 heads) + (run 'hg heads' to see heads, 'hg merge' to merge) + $ hg up -q tip + $ hg tglog + @ 3: 'move2' + | + o 2: 'move1' + | + | o 1: 'change' + |/ + o 0: 'add' + + $ hg rebase --collapse -d 1 + merging a and d to d + merging b and e to e + merging c and f to f + merging e and g to g + merging f and c to c + saved backup bundle to $TESTTMP/copies/.hg/strip-backup/*-backup.hg (glob) + $ hg st + $ hg st --copies --change . + A d + a + A g + b + R b + $ cat c + c + c + $ cat d + a + a + $ cat g + b + b + $ hg log -r . --template "{file_copies}\n" + d (a)g (b) + $ cd ..