comparison mercurial/merge.py @ 18778:1ef89df2c248

rebase: fix --collapse when a file was added then removed When a series of commits first adds a file and then removes it, hg rebase --collapse prompts whether to keep the file or delete it. This is due to it reusing the branch merge code. In a noninteractive terminal it defaults to keeping the file, which results in a collapsed commit that is has a file that should be deleted. This bug resulted in developers accidentally commiting unintentional changes to our repo twice today, so it's fairly important to get fixed. This change allows rebase --collapse to tell the merge code to accept the latest version every time without prompting. Adds a test as well.
author Durham Goode <durham@fb.com>
date Fri, 15 Mar 2013 11:23:29 -0700
parents e556659340f0
children 0705ad73e878
comparison
equal deleted inserted replaced
18777:8048c519dc6a 18778:1ef89df2c248
183 if f not in mctx: 183 if f not in mctx:
184 actions.append((f, "f", None, "forget removed")) 184 actions.append((f, "f", None, "forget removed"))
185 185
186 return actions 186 return actions
187 187
188 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial): 188 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
189 acceptremote=False):
189 """ 190 """
190 Merge p1 and p2 with ancestor pa and generate merge action list 191 Merge p1 and p2 with ancestor pa and generate merge action list
191 192
192 branchmerge and force are as passed in to update 193 branchmerge and force are as passed in to update
193 partial = function to filter file lists 194 partial = function to filter file lists
195 acceptremote = accept the incoming changes without prompting
194 """ 196 """
195 197
196 overwrite = force and not branchmerge 198 overwrite = force and not branchmerge
197 actions, copy, movewithdir = [], {}, {} 199 actions, copy, movewithdir = [], {}, {}
198 200
329 raise util.Abort(_("untracked files in working directory differ " 331 raise util.Abort(_("untracked files in working directory differ "
330 "from files in requested revision")) 332 "from files in requested revision"))
331 333
332 for f, m in sorted(prompts): 334 for f, m in sorted(prompts):
333 if m == "cd": 335 if m == "cd":
334 if repo.ui.promptchoice( 336 if acceptremote:
337 actions.append((f, "r", None, "remote delete"))
338 elif repo.ui.promptchoice(
335 _("local changed %s which remote deleted\n" 339 _("local changed %s which remote deleted\n"
336 "use (c)hanged version or (d)elete?") % f, 340 "use (c)hanged version or (d)elete?") % f,
337 (_("&Changed"), _("&Delete")), 0): 341 (_("&Changed"), _("&Delete")), 0):
338 actions.append((f, "r", None, "prompt delete")) 342 actions.append((f, "r", None, "prompt delete"))
339 else: 343 else:
340 actions.append((f, "a", None, "prompt keep")) 344 actions.append((f, "a", None, "prompt keep"))
341 elif m == "dc": 345 elif m == "dc":
342 if repo.ui.promptchoice( 346 if acceptremote:
347 actions.append((f, "g", (m2.flags(f),), "remote recreating"))
348 elif repo.ui.promptchoice(
343 _("remote changed %s which local deleted\n" 349 _("remote changed %s which local deleted\n"
344 "use (c)hanged version or leave (d)eleted?") % f, 350 "use (c)hanged version or leave (d)eleted?") % f,
345 (_("&Changed"), _("&Deleted")), 0) == 0: 351 (_("&Changed"), _("&Deleted")), 0) == 0:
346 actions.append((f, "g", (m2.flags(f),), "prompt recreating")) 352 actions.append((f, "g", (m2.flags(f),), "prompt recreating"))
347 else: assert False, m 353 else: assert False, m
510 ms.commit() 516 ms.commit()
511 progress(_updating, None, total=numupdates, unit=_files) 517 progress(_updating, None, total=numupdates, unit=_files)
512 518
513 return updated, merged, removed, unresolved 519 return updated, merged, removed, unresolved
514 520
515 def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial): 521 def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial,
522 acceptremote=False):
516 "Calculate the actions needed to merge mctx into tctx" 523 "Calculate the actions needed to merge mctx into tctx"
517 actions = [] 524 actions = []
518 folding = not util.checkcase(repo.path) 525 folding = not util.checkcase(repo.path)
519 if folding: 526 if folding:
520 # collision check is not needed for clean update 527 # collision check is not needed for clean update
524 else: 531 else:
525 _checkcollision(mctx, (tctx, ancestor)) 532 _checkcollision(mctx, (tctx, ancestor))
526 actions += manifestmerge(repo, tctx, mctx, 533 actions += manifestmerge(repo, tctx, mctx,
527 ancestor, 534 ancestor,
528 branchmerge, force, 535 branchmerge, force,
529 partial) 536 partial, acceptremote)
530 if tctx.rev() is None: 537 if tctx.rev() is None:
531 actions += _forgetremoved(tctx, mctx, branchmerge) 538 actions += _forgetremoved(tctx, mctx, branchmerge)
532 return actions 539 return actions
533 540
534 def recordupdates(repo, actions, branchmerge): 541 def recordupdates(repo, actions, branchmerge):
600 607
601 node = the node to update to, or None if unspecified 608 node = the node to update to, or None if unspecified
602 branchmerge = whether to merge between branches 609 branchmerge = whether to merge between branches
603 force = whether to force branch merging or file overwriting 610 force = whether to force branch merging or file overwriting
604 partial = a function to filter file lists (dirstate not updated) 611 partial = a function to filter file lists (dirstate not updated)
605 mergeancestor = if false, merging with an ancestor (fast-forward) 612 mergeancestor = whether it is merging with an ancestor. If true,
606 is only allowed between different named branches. This flag 613 we should accept the incoming changes for any prompts that occur.
607 is used by rebase extension as a temporary fix and should be 614 If false, merging with an ancestor (fast-forward) is only allowed
608 avoided in general. 615 between different named branches. This flag is used by rebase extension
616 as a temporary fix and should be avoided in general.
609 617
610 The table below shows all the behaviors of the update command 618 The table below shows all the behaviors of the update command
611 given the -c and -C or no options, whether the working directory 619 given the -c and -C or no options, whether the working directory
612 is dirty, whether a revision is specified, and the relationship of 620 is dirty, whether a revision is specified, and the relationship of
613 the parent rev to the target rev (linear, on the same named 621 the parent rev to the target rev (linear, on the same named
691 # Allow jumping branches if clean and specific rev given 699 # Allow jumping branches if clean and specific rev given
692 pa = p1 700 pa = p1
693 701
694 ### calculate phase 702 ### calculate phase
695 actions = calculateupdates(repo, wc, p2, pa, 703 actions = calculateupdates(repo, wc, p2, pa,
696 branchmerge, force, partial) 704 branchmerge, force, partial, mergeancestor)
697 705
698 ### apply phase 706 ### apply phase
699 if not branchmerge: # just jump to the new rev 707 if not branchmerge: # just jump to the new rev
700 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, '' 708 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
701 if not partial: 709 if not partial: