##// END OF EJS Templates
merge: process files in sorted order
Mads Kiilerich -
r18360:760c0d67 default
parent child Browse files
Show More
@@ -1,636 +1,636 b''
1 1 # merge.py - directory-level update/merge handling for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import nullid, nullrev, hex, bin
9 9 from i18n import _
10 10 import error, util, filemerge, copies, subrepo
11 11 import errno, os, shutil
12 12
13 13 class mergestate(object):
14 14 '''track 3-way merge state of individual files'''
15 15 def __init__(self, repo):
16 16 self._repo = repo
17 17 self._dirty = False
18 18 self._read()
19 19 def reset(self, node=None):
20 20 self._state = {}
21 21 if node:
22 22 self._local = node
23 23 shutil.rmtree(self._repo.join("merge"), True)
24 24 self._dirty = False
25 25 def _read(self):
26 26 self._state = {}
27 27 try:
28 28 f = self._repo.opener("merge/state")
29 29 for i, l in enumerate(f):
30 30 if i == 0:
31 31 self._local = bin(l[:-1])
32 32 else:
33 33 bits = l[:-1].split("\0")
34 34 self._state[bits[0]] = bits[1:]
35 35 f.close()
36 36 except IOError, err:
37 37 if err.errno != errno.ENOENT:
38 38 raise
39 39 self._dirty = False
40 40 def commit(self):
41 41 if self._dirty:
42 42 f = self._repo.opener("merge/state", "w")
43 43 f.write(hex(self._local) + "\n")
44 44 for d, v in self._state.iteritems():
45 45 f.write("\0".join([d] + v) + "\n")
46 46 f.close()
47 47 self._dirty = False
48 48 def add(self, fcl, fco, fca, fd):
49 49 hash = util.sha1(fcl.path()).hexdigest()
50 50 self._repo.opener.write("merge/" + hash, fcl.data())
51 51 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
52 52 hex(fca.filenode()), fco.path(), fcl.flags()]
53 53 self._dirty = True
54 54 def __contains__(self, dfile):
55 55 return dfile in self._state
56 56 def __getitem__(self, dfile):
57 57 return self._state[dfile][0]
58 58 def __iter__(self):
59 59 l = self._state.keys()
60 60 l.sort()
61 61 for f in l:
62 62 yield f
63 63 def mark(self, dfile, state):
64 64 self._state[dfile][0] = state
65 65 self._dirty = True
66 66 def resolve(self, dfile, wctx, octx):
67 67 if self[dfile] == 'r':
68 68 return 0
69 69 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
70 70 fcd = wctx[dfile]
71 71 fco = octx[ofile]
72 72 fca = self._repo.filectx(afile, fileid=anode)
73 73 # "premerge" x flags
74 74 flo = fco.flags()
75 75 fla = fca.flags()
76 76 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
77 77 if fca.node() == nullid:
78 78 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
79 79 afile)
80 80 elif flags == fla:
81 81 flags = flo
82 82 # restore local
83 83 f = self._repo.opener("merge/" + hash)
84 84 self._repo.wwrite(dfile, f.read(), flags)
85 85 f.close()
86 86 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
87 87 if r is None:
88 88 # no real conflict
89 89 del self._state[dfile]
90 90 elif not r:
91 91 self.mark(dfile, 'r')
92 92 return r
93 93
94 94 def _checkunknownfile(repo, wctx, mctx, f):
95 95 return (not repo.dirstate._ignore(f)
96 96 and os.path.isfile(repo.wjoin(f))
97 97 and repo.dirstate.normalize(f) not in repo.dirstate
98 98 and mctx[f].cmp(wctx[f]))
99 99
100 100 def _checkunknown(repo, wctx, mctx):
101 101 "check for collisions between unknown files and files in mctx"
102 102
103 103 error = False
104 104 for f in mctx:
105 105 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
106 106 error = True
107 107 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
108 108 if error:
109 109 raise util.Abort(_("untracked files in working directory differ "
110 110 "from files in requested revision"))
111 111
112 112 def _remains(f, m, ma, workingctx=False):
113 113 """check whether specified file remains after merge.
114 114
115 115 It is assumed that specified file is not contained in the manifest
116 116 of the other context.
117 117 """
118 118 if f in ma:
119 119 n = m[f]
120 120 if n != ma[f]:
121 121 return True # because it is changed locally
122 122 # even though it doesn't remain, if "remote deleted" is
123 123 # chosen in manifestmerge()
124 124 elif workingctx and n[20:] == "a":
125 125 return True # because it is added locally (linear merge specific)
126 126 else:
127 127 return False # because it is removed remotely
128 128 else:
129 129 return True # because it is added locally
130 130
131 131 def _checkcollision(mctx, extractxs):
132 132 "check for case folding collisions in the destination context"
133 133 folded = {}
134 134 for fn in mctx:
135 135 fold = util.normcase(fn)
136 136 if fold in folded:
137 137 raise util.Abort(_("case-folding collision between %s and %s")
138 138 % (fn, folded[fold]))
139 139 folded[fold] = fn
140 140
141 141 if extractxs:
142 142 wctx, actx = extractxs
143 143 # class to delay looking up copy mapping
144 144 class pathcopies(object):
145 145 @util.propertycache
146 146 def map(self):
147 147 # {dst@mctx: src@wctx} copy mapping
148 148 return copies.pathcopies(wctx, mctx)
149 149 pc = pathcopies()
150 150
151 151 for fn in wctx:
152 152 fold = util.normcase(fn)
153 153 mfn = folded.get(fold, None)
154 154 if (mfn and mfn != fn and pc.map.get(mfn) != fn and
155 155 _remains(fn, wctx.manifest(), actx.manifest(), True) and
156 156 _remains(mfn, mctx.manifest(), actx.manifest())):
157 157 raise util.Abort(_("case-folding collision between %s and %s")
158 158 % (mfn, fn))
159 159
160 160 def _forgetremoved(wctx, mctx, branchmerge):
161 161 """
162 162 Forget removed files
163 163
164 164 If we're jumping between revisions (as opposed to merging), and if
165 165 neither the working directory nor the target rev has the file,
166 166 then we need to remove it from the dirstate, to prevent the
167 167 dirstate from listing the file when it is no longer in the
168 168 manifest.
169 169
170 170 If we're merging, and the other revision has removed a file
171 171 that is not present in the working directory, we need to mark it
172 172 as removed.
173 173 """
174 174
175 175 actions = []
176 176 state = branchmerge and 'r' or 'f'
177 177 for f in wctx.deleted():
178 178 if f not in mctx:
179 179 actions.append((f, state))
180 180
181 181 if not branchmerge:
182 182 for f in wctx.removed():
183 183 if f not in mctx:
184 184 actions.append((f, "f"))
185 185
186 186 return actions
187 187
188 188 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
189 189 """
190 190 Merge p1 and p2 with ancestor pa and generate merge action list
191 191
192 192 overwrite = whether we clobber working files
193 193 partial = function to filter file lists
194 194 """
195 195
196 196 def act(msg, m, f, *args):
197 197 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
198 198 actions.append((f, m) + args)
199 199
200 200 actions, copy, movewithdir = [], {}, {}
201 201
202 202 if overwrite:
203 203 pa = p1
204 204 elif pa == p2: # backwards
205 205 pa = p1.p1()
206 206 elif pa and repo.ui.configbool("merge", "followcopies", True):
207 207 ret = copies.mergecopies(repo, p1, p2, pa)
208 208 copy, movewithdir, diverge, renamedelete = ret
209 209 for of, fl in diverge.iteritems():
210 210 act("divergent renames", "dr", of, fl)
211 211 for of, fl in renamedelete.iteritems():
212 212 act("rename and delete", "rd", of, fl)
213 213
214 214 repo.ui.note(_("resolving manifests\n"))
215 215 repo.ui.debug(" overwrite: %s, partial: %s\n"
216 216 % (bool(overwrite), bool(partial)))
217 217 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, p1, p2))
218 218
219 219 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
220 220 copied = set(copy.values())
221 221 copied.update(movewithdir.values())
222 222
223 223 if '.hgsubstate' in m1:
224 224 # check whether sub state is modified
225 225 for s in p1.substate:
226 226 if p1.sub(s).dirty():
227 227 m1['.hgsubstate'] += "+"
228 228 break
229 229
230 230 # Compare manifests
231 for f, n in m1.iteritems():
231 for f, n in sorted(m1.iteritems()):
232 232 if partial and not partial(f):
233 233 continue
234 234 if f in m2:
235 235 n2 = m2[f]
236 236 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
237 237 nol = 'l' not in fl1 + fl2 + fla
238 238 a = ma.get(f, nullid)
239 239 if n == n2 and fl1 == fl2:
240 240 pass # same - keep local
241 241 elif n2 == a and fl2 == fla:
242 242 pass # remote unchanged - keep local
243 243 elif n == a and fl1 == fla: # local unchanged - use remote
244 244 if n == n2: # optimization: keep local content
245 245 act("update permissions", "e", f, fl2)
246 246 else:
247 247 act("remote is newer", "g", f, fl2)
248 248 elif nol and n2 == a: # remote only changed 'x'
249 249 act("update permissions", "e", f, fl2)
250 250 elif nol and n == a: # local only changed 'x'
251 251 act("remote is newer", "g", f, fl)
252 252 else: # both changed something
253 253 act("versions differ", "m", f, f, f, False)
254 254 elif f in copied: # files we'll deal with on m2 side
255 255 pass
256 256 elif f in movewithdir: # directory rename
257 257 f2 = movewithdir[f]
258 258 act("remote renamed directory to " + f2, "d", f, None, f2,
259 259 m1.flags(f))
260 260 elif f in copy:
261 261 f2 = copy[f]
262 262 act("local copied/moved to " + f2, "m", f, f2, f, False)
263 263 elif f in ma: # clean, a different, no remote
264 264 if n != ma[f]:
265 265 if repo.ui.promptchoice(
266 266 _(" local changed %s which remote deleted\n"
267 267 "use (c)hanged version or (d)elete?") % f,
268 268 (_("&Changed"), _("&Delete")), 0):
269 269 act("prompt delete", "r", f)
270 270 else:
271 271 act("prompt keep", "a", f)
272 272 elif n[20:] == "a": # added, no remote
273 273 act("remote deleted", "f", f)
274 274 else:
275 275 act("other deleted", "r", f)
276 276
277 for f, n in m2.iteritems():
277 for f, n in sorted(m2.iteritems()):
278 278 if partial and not partial(f):
279 279 continue
280 280 if f in m1 or f in copied: # files already visited
281 281 continue
282 282 if f in movewithdir:
283 283 f2 = movewithdir[f]
284 284 act("local renamed directory to " + f2, "d", None, f, f2,
285 285 m2.flags(f))
286 286 elif f in copy:
287 287 f2 = copy[f]
288 288 if f2 in m2:
289 289 act("remote copied to " + f, "m",
290 290 f2, f, f, False)
291 291 else:
292 292 act("remote moved to " + f, "m",
293 293 f2, f, f, True)
294 294 elif f not in ma:
295 295 if (not overwrite
296 296 and _checkunknownfile(repo, p1, p2, f)):
297 297 act("remote differs from untracked local",
298 298 "m", f, f, f, False)
299 299 else:
300 300 act("remote created", "g", f, m2.flags(f))
301 301 elif n != ma[f]:
302 302 if repo.ui.promptchoice(
303 303 _("remote changed %s which local deleted\n"
304 304 "use (c)hanged version or leave (d)eleted?") % f,
305 305 (_("&Changed"), _("&Deleted")), 0) == 0:
306 306 act("prompt recreating", "g", f, m2.flags(f))
307 307
308 308 return actions
309 309
310 310 def actionkey(a):
311 311 return a[1] == "r" and -1 or 0, a
312 312
313 313 def applyupdates(repo, actions, wctx, mctx, actx, overwrite):
314 314 """apply the merge action list to the working directory
315 315
316 316 wctx is the working copy context
317 317 mctx is the context to be merged into the working copy
318 318 actx is the context of the common ancestor
319 319
320 320 Return a tuple of counts (updated, merged, removed, unresolved) that
321 321 describes how many files were affected by the update.
322 322 """
323 323
324 324 updated, merged, removed, unresolved = 0, 0, 0, 0
325 325 ms = mergestate(repo)
326 326 ms.reset(wctx.p1().node())
327 327 moves = []
328 328 actions.sort(key=actionkey)
329 329
330 330 # prescan for merges
331 331 for a in actions:
332 332 f, m = a[:2]
333 333 if m == "m": # merge
334 334 f2, fd, move = a[2:]
335 335 if fd == '.hgsubstate': # merged internally
336 336 continue
337 337 repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
338 338 fcl = wctx[f]
339 339 fco = mctx[f2]
340 340 if mctx == actx: # backwards, use working dir parent as ancestor
341 341 if fcl.parents():
342 342 fca = fcl.p1()
343 343 else:
344 344 fca = repo.filectx(f, fileid=nullrev)
345 345 else:
346 346 fca = fcl.ancestor(fco, actx)
347 347 if not fca:
348 348 fca = repo.filectx(f, fileid=nullrev)
349 349 ms.add(fcl, fco, fca, fd)
350 350 if f != fd and move:
351 351 moves.append(f)
352 352
353 353 audit = repo.wopener.audit
354 354
355 355 # remove renamed files after safely stored
356 356 for f in moves:
357 357 if os.path.lexists(repo.wjoin(f)):
358 358 repo.ui.debug("removing %s\n" % f)
359 359 audit(f)
360 360 util.unlinkpath(repo.wjoin(f))
361 361
362 362 numupdates = len(actions)
363 363 for i, a in enumerate(actions):
364 364 f, m = a[:2]
365 365 repo.ui.progress(_('updating'), i + 1, item=f, total=numupdates,
366 366 unit=_('files'))
367 367 if m == "r": # remove
368 368 repo.ui.note(_("removing %s\n") % f)
369 369 audit(f)
370 370 if f == '.hgsubstate': # subrepo states need updating
371 371 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
372 372 try:
373 373 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
374 374 except OSError, inst:
375 375 repo.ui.warn(_("update failed to remove %s: %s!\n") %
376 376 (f, inst.strerror))
377 377 removed += 1
378 378 elif m == "m": # merge
379 379 if fd == '.hgsubstate': # subrepo states need updating
380 380 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
381 381 overwrite)
382 382 continue
383 383 f2, fd, move = a[2:]
384 384 audit(fd)
385 385 r = ms.resolve(fd, wctx, mctx)
386 386 if r is not None and r > 0:
387 387 unresolved += 1
388 388 else:
389 389 if r is None:
390 390 updated += 1
391 391 else:
392 392 merged += 1
393 393 elif m == "g": # get
394 394 flags = a[2]
395 395 repo.ui.note(_("getting %s\n") % f)
396 396 repo.wwrite(f, mctx.filectx(f).data(), flags)
397 397 updated += 1
398 398 if f == '.hgsubstate': # subrepo states need updating
399 399 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
400 400 elif m == "d": # directory rename
401 401 f2, fd, flags = a[2:]
402 402 if f:
403 403 repo.ui.note(_("moving %s to %s\n") % (f, fd))
404 404 audit(f)
405 405 repo.wwrite(fd, wctx.filectx(f).data(), flags)
406 406 util.unlinkpath(repo.wjoin(f))
407 407 if f2:
408 408 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
409 409 repo.wwrite(fd, mctx.filectx(f2).data(), flags)
410 410 updated += 1
411 411 elif m == "dr": # divergent renames
412 412 fl = a[2]
413 413 repo.ui.warn(_("note: possible conflict - %s was renamed "
414 414 "multiple times to:\n") % f)
415 415 for nf in fl:
416 416 repo.ui.warn(" %s\n" % nf)
417 417 elif m == "rd": # rename and delete
418 418 fl = a[2]
419 419 repo.ui.warn(_("note: possible conflict - %s was deleted "
420 420 "and renamed to:\n") % f)
421 421 for nf in fl:
422 422 repo.ui.warn(" %s\n" % nf)
423 423 elif m == "e": # exec
424 424 flags = a[2]
425 425 audit(f)
426 426 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
427 427 updated += 1
428 428 ms.commit()
429 429 repo.ui.progress(_('updating'), None, total=numupdates, unit=_('files'))
430 430
431 431 return updated, merged, removed, unresolved
432 432
433 433 def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial):
434 434 "Calculate the actions needed to merge mctx into tctx"
435 435 actions = []
436 436 folding = not util.checkcase(repo.path)
437 437 if folding:
438 438 # collision check is not needed for clean update
439 439 if (not branchmerge and
440 440 (force or not tctx.dirty(missing=True, branch=False))):
441 441 _checkcollision(mctx, None)
442 442 else:
443 443 _checkcollision(mctx, (tctx, ancestor))
444 444 if not force:
445 445 _checkunknown(repo, tctx, mctx)
446 446 if tctx.rev() is None:
447 447 actions += _forgetremoved(tctx, mctx, branchmerge)
448 448 actions += manifestmerge(repo, tctx, mctx,
449 449 ancestor,
450 450 force and not branchmerge,
451 451 partial)
452 452 return actions
453 453
454 454 def recordupdates(repo, actions, branchmerge):
455 455 "record merge actions to the dirstate"
456 456
457 457 for a in actions:
458 458 f, m = a[:2]
459 459 if m == "r": # remove
460 460 if branchmerge:
461 461 repo.dirstate.remove(f)
462 462 else:
463 463 repo.dirstate.drop(f)
464 464 elif m == "a": # re-add
465 465 if not branchmerge:
466 466 repo.dirstate.add(f)
467 467 elif m == "f": # forget
468 468 repo.dirstate.drop(f)
469 469 elif m == "e": # exec change
470 470 repo.dirstate.normallookup(f)
471 471 elif m == "g": # get
472 472 if branchmerge:
473 473 repo.dirstate.otherparent(f)
474 474 else:
475 475 repo.dirstate.normal(f)
476 476 elif m == "m": # merge
477 477 f2, fd, move = a[2:]
478 478 if branchmerge:
479 479 # We've done a branch merge, mark this file as merged
480 480 # so that we properly record the merger later
481 481 repo.dirstate.merge(fd)
482 482 if f != f2: # copy/rename
483 483 if move:
484 484 repo.dirstate.remove(f)
485 485 if f != fd:
486 486 repo.dirstate.copy(f, fd)
487 487 else:
488 488 repo.dirstate.copy(f2, fd)
489 489 else:
490 490 # We've update-merged a locally modified file, so
491 491 # we set the dirstate to emulate a normal checkout
492 492 # of that file some time in the past. Thus our
493 493 # merge will appear as a normal local file
494 494 # modification.
495 495 if f2 == fd: # file not locally copied/moved
496 496 repo.dirstate.normallookup(fd)
497 497 if move:
498 498 repo.dirstate.drop(f)
499 499 elif m == "d": # directory rename
500 500 f2, fd, flag = a[2:]
501 501 if not f2 and f not in repo.dirstate:
502 502 # untracked file moved
503 503 continue
504 504 if branchmerge:
505 505 repo.dirstate.add(fd)
506 506 if f:
507 507 repo.dirstate.remove(f)
508 508 repo.dirstate.copy(f, fd)
509 509 if f2:
510 510 repo.dirstate.copy(f2, fd)
511 511 else:
512 512 repo.dirstate.normal(fd)
513 513 if f:
514 514 repo.dirstate.drop(f)
515 515
516 516 def update(repo, node, branchmerge, force, partial, ancestor=None,
517 517 mergeancestor=False):
518 518 """
519 519 Perform a merge between the working directory and the given node
520 520
521 521 node = the node to update to, or None if unspecified
522 522 branchmerge = whether to merge between branches
523 523 force = whether to force branch merging or file overwriting
524 524 partial = a function to filter file lists (dirstate not updated)
525 525 mergeancestor = if false, merging with an ancestor (fast-forward)
526 526 is only allowed between different named branches. This flag
527 527 is used by rebase extension as a temporary fix and should be
528 528 avoided in general.
529 529
530 530 The table below shows all the behaviors of the update command
531 531 given the -c and -C or no options, whether the working directory
532 532 is dirty, whether a revision is specified, and the relationship of
533 533 the parent rev to the target rev (linear, on the same named
534 534 branch, or on another named branch).
535 535
536 536 This logic is tested by test-update-branches.t.
537 537
538 538 -c -C dirty rev | linear same cross
539 539 n n n n | ok (1) x
540 540 n n n y | ok ok ok
541 541 n n y * | merge (2) (2)
542 542 n y * * | --- discard ---
543 543 y n y * | --- (3) ---
544 544 y n n * | --- ok ---
545 545 y y * * | --- (4) ---
546 546
547 547 x = can't happen
548 548 * = don't-care
549 549 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
550 550 2 = abort: crosses branches (use 'hg merge' to merge or
551 551 use 'hg update -C' to discard changes)
552 552 3 = abort: uncommitted local changes
553 553 4 = incompatible options (checked in commands.py)
554 554
555 555 Return the same tuple as applyupdates().
556 556 """
557 557
558 558 onode = node
559 559 wlock = repo.wlock()
560 560 try:
561 561 wc = repo[None]
562 562 if node is None:
563 563 # tip of current branch
564 564 try:
565 565 node = repo.branchtip(wc.branch())
566 566 except error.RepoLookupError:
567 567 if wc.branch() == "default": # no default branch!
568 568 node = repo.lookup("tip") # update to tip
569 569 else:
570 570 raise util.Abort(_("branch %s not found") % wc.branch())
571 571 overwrite = force and not branchmerge
572 572 pl = wc.parents()
573 573 p1, p2 = pl[0], repo[node]
574 574 if ancestor:
575 575 pa = repo[ancestor]
576 576 else:
577 577 pa = p1.ancestor(p2)
578 578
579 579 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
580 580
581 581 ### check phase
582 582 if not overwrite and len(pl) > 1:
583 583 raise util.Abort(_("outstanding uncommitted merges"))
584 584 if branchmerge:
585 585 if pa == p2:
586 586 raise util.Abort(_("merging with a working directory ancestor"
587 587 " has no effect"))
588 588 elif pa == p1:
589 589 if not mergeancestor and p1.branch() == p2.branch():
590 590 raise util.Abort(_("nothing to merge"),
591 591 hint=_("use 'hg update' "
592 592 "or check 'hg heads'"))
593 593 if not force and (wc.files() or wc.deleted()):
594 594 raise util.Abort(_("outstanding uncommitted changes"),
595 595 hint=_("use 'hg status' to list changes"))
596 596 for s in wc.substate:
597 597 if wc.sub(s).dirty():
598 598 raise util.Abort(_("outstanding uncommitted changes in "
599 599 "subrepository '%s'") % s)
600 600
601 601 elif not overwrite:
602 602 if pa == p1 or pa == p2: # linear
603 603 pass # all good
604 604 elif wc.dirty(missing=True):
605 605 raise util.Abort(_("crosses branches (merge branches or use"
606 606 " --clean to discard changes)"))
607 607 elif onode is None:
608 608 raise util.Abort(_("crosses branches (merge branches or update"
609 609 " --check to force update)"))
610 610 else:
611 611 # Allow jumping branches if clean and specific rev given
612 612 pa = p1
613 613
614 614 ### calculate phase
615 615 actions = calculateupdates(repo, wc, p2, pa,
616 616 branchmerge, force, partial)
617 617
618 618 ### apply phase
619 619 if not branchmerge: # just jump to the new rev
620 620 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
621 621 if not partial:
622 622 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
623 623
624 624 stats = applyupdates(repo, actions, wc, p2, pa, overwrite)
625 625
626 626 if not partial:
627 627 repo.setparents(fp1, fp2)
628 628 recordupdates(repo, actions, branchmerge)
629 629 if not branchmerge:
630 630 repo.dirstate.setbranch(p2.branch())
631 631 finally:
632 632 wlock.release()
633 633
634 634 if not partial:
635 635 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
636 636 return stats
@@ -1,64 +1,64 b''
1 1 $ hg init t
2 2 $ cd t
3 3
4 4 $ echo 1 > a
5 5 $ hg ci -qAm "first"
6 6
7 7 $ hg cp a b
8 8 $ hg mv a c
9 9 $ echo 2 >> b
10 10 $ echo 2 >> c
11 11
12 12 $ hg ci -qAm "second"
13 13
14 14 $ hg co -C 0
15 15 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
16 16
17 17 $ echo 0 > a
18 18 $ echo 1 >> a
19 19
20 20 $ hg ci -qAm "other"
21 21
22 22 $ hg merge --debug
23 23 searching for copies back to rev 1
24 24 unmatched files in other:
25 25 b
26 26 c
27 27 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
28 28 src: 'a' -> dst: 'c' *
29 29 src: 'a' -> dst: 'b' *
30 30 checking for directory renames
31 31 resolving manifests
32 32 overwrite: False, partial: False
33 33 ancestor: b8bf91eeebbc, local: add3f11052fa+, remote: 17c05bb7fcb6
34 a: remote moved to b -> m
34 35 a: remote moved to c -> m
35 a: remote moved to b -> m
36 36 preserving a for resolve of b
37 37 preserving a for resolve of c
38 38 removing a
39 39 updating: a 1/2 files (50.00%)
40 40 picked tool 'internal:merge' for b (binary False symlink False)
41 41 merging a and b to b
42 42 my b@add3f11052fa+ other b@17c05bb7fcb6 ancestor a@b8bf91eeebbc
43 43 premerge successful
44 44 updating: a 2/2 files (100.00%)
45 45 picked tool 'internal:merge' for c (binary False symlink False)
46 46 merging a and c to c
47 47 my c@add3f11052fa+ other c@17c05bb7fcb6 ancestor a@b8bf91eeebbc
48 48 premerge successful
49 49 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
50 50 (branch merge, don't forget to commit)
51 51
52 52 file b
53 53 $ cat b
54 54 0
55 55 1
56 56 2
57 57
58 58 file c
59 59 $ cat c
60 60 0
61 61 1
62 62 2
63 63
64 64 $ cd ..
@@ -1,535 +1,535 b''
1 1 Create a repo with some stuff in it:
2 2
3 3 $ hg init a
4 4 $ cd a
5 5 $ echo a > a
6 6 $ echo a > d
7 7 $ echo a > e
8 8 $ hg ci -qAm0
9 9 $ echo b > a
10 10 $ hg ci -m1 -u bar
11 11 $ hg mv a b
12 12 $ hg ci -m2
13 13 $ hg cp b c
14 14 $ hg ci -m3 -u baz
15 15 $ echo b > d
16 16 $ echo f > e
17 17 $ hg ci -m4
18 18 $ hg up -q 3
19 19 $ echo b > e
20 20 $ hg branch -q stable
21 21 $ hg ci -m5
22 22 $ hg merge -q default --tool internal:local
23 23 $ hg branch -q default
24 24 $ hg ci -m6
25 25 $ hg phase --public 3
26 26 $ hg phase --force --secret 6
27 27
28 28 $ hg --config extensions.graphlog= log -G --template '{author}@{rev}.{phase}: {desc}\n'
29 29 @ test@6.secret: 6
30 30 |\
31 31 | o test@5.draft: 5
32 32 | |
33 33 o | test@4.draft: 4
34 34 |/
35 35 o baz@3.public: 3
36 36 |
37 37 o test@2.public: 2
38 38 |
39 39 o bar@1.public: 1
40 40 |
41 41 o test@0.public: 0
42 42
43 43
44 44 Need to specify a rev:
45 45
46 46 $ hg graft
47 47 abort: no revisions specified
48 48 [255]
49 49
50 50 Can't graft ancestor:
51 51
52 52 $ hg graft 1 2
53 53 skipping ancestor revision 1
54 54 skipping ancestor revision 2
55 55 [255]
56 56
57 57 Specify revisions with -r:
58 58
59 59 $ hg graft -r 1 -r 2
60 60 skipping ancestor revision 1
61 61 skipping ancestor revision 2
62 62 [255]
63 63
64 64 $ hg graft -r 1 2
65 65 skipping ancestor revision 2
66 66 skipping ancestor revision 1
67 67 [255]
68 68
69 69 Can't graft with dirty wd:
70 70
71 71 $ hg up -q 0
72 72 $ echo foo > a
73 73 $ hg graft 1
74 74 abort: outstanding uncommitted changes
75 75 [255]
76 76 $ hg revert a
77 77
78 78 Graft a rename:
79 79
80 80 $ hg graft 2 -u foo
81 81 grafting revision 2
82 82 merging a and b to b
83 83 $ hg export tip --git
84 84 # HG changeset patch
85 85 # User foo
86 86 # Date 0 0
87 87 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
88 88 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
89 89 2
90 90
91 91 diff --git a/a b/b
92 92 rename from a
93 93 rename to b
94 94
95 95 Look for extra:source
96 96
97 97 $ hg log --debug -r tip
98 98 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
99 99 tag: tip
100 100 phase: draft
101 101 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
102 102 parent: -1:0000000000000000000000000000000000000000
103 103 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
104 104 user: foo
105 105 date: Thu Jan 01 00:00:00 1970 +0000
106 106 files+: b
107 107 files-: a
108 108 extra: branch=default
109 109 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
110 110 description:
111 111 2
112 112
113 113
114 114
115 115 Graft out of order, skipping a merge and a duplicate
116 116
117 117 $ hg graft 1 5 4 3 'merge()' 2 -n
118 118 skipping ungraftable merge revision 6
119 119 skipping already grafted revision 2
120 120 grafting revision 1
121 121 grafting revision 5
122 122 grafting revision 4
123 123 grafting revision 3
124 124
125 125 $ hg graft 1 5 4 3 'merge()' 2 --debug
126 126 skipping ungraftable merge revision 6
127 127 scanning for duplicate grafts
128 128 skipping already grafted revision 2
129 129 grafting revision 1
130 130 searching for copies back to rev 1
131 131 unmatched files in local:
132 132 b
133 133 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
134 134 src: 'a' -> dst: 'b' *
135 135 checking for directory renames
136 136 resolving manifests
137 137 overwrite: False, partial: False
138 138 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
139 139 b: local copied/moved to a -> m
140 140 preserving b for resolve of b
141 141 updating: b 1/1 files (100.00%)
142 142 picked tool 'internal:merge' for b (binary False symlink False)
143 143 merging b and a to b
144 144 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
145 145 premerge successful
146 146 b
147 147 grafting revision 5
148 148 searching for copies back to rev 1
149 149 resolving manifests
150 150 overwrite: False, partial: False
151 151 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
152 152 e: remote is newer -> g
153 153 updating: e 1/1 files (100.00%)
154 154 getting e
155 155 e
156 156 grafting revision 4
157 157 searching for copies back to rev 1
158 158 resolving manifests
159 159 overwrite: False, partial: False
160 160 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
161 d: remote is newer -> g
161 162 e: versions differ -> m
162 d: remote is newer -> g
163 163 preserving e for resolve of e
164 164 updating: d 1/2 files (50.00%)
165 165 getting d
166 166 updating: e 2/2 files (100.00%)
167 167 picked tool 'internal:merge' for e (binary False symlink False)
168 168 merging e
169 169 my e@1905859650ec+ other e@9c233e8e184d ancestor e@68795b066622
170 170 warning: conflicts during merge.
171 171 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
172 172 abort: unresolved conflicts, can't continue
173 173 (use hg resolve and hg graft --continue)
174 174 [255]
175 175
176 176 Continue without resolve should fail:
177 177
178 178 $ hg graft -c
179 179 grafting revision 4
180 180 abort: unresolved merge conflicts (see hg help resolve)
181 181 [255]
182 182
183 183 Fix up:
184 184
185 185 $ echo b > e
186 186 $ hg resolve -m e
187 187
188 188 Continue with a revision should fail:
189 189
190 190 $ hg graft -c 6
191 191 abort: can't specify --continue and revisions
192 192 [255]
193 193
194 194 $ hg graft -c -r 6
195 195 abort: can't specify --continue and revisions
196 196 [255]
197 197
198 198 Continue for real, clobber usernames
199 199
200 200 $ hg graft -c -U
201 201 grafting revision 4
202 202 grafting revision 3
203 203
204 204 Compare with original:
205 205
206 206 $ hg diff -r 6
207 207 $ hg status --rev 0:. -C
208 208 M d
209 209 M e
210 210 A b
211 211 a
212 212 A c
213 213 a
214 214 R a
215 215
216 216 View graph:
217 217
218 218 $ hg --config extensions.graphlog= log -G --template '{author}@{rev}.{phase}: {desc}\n'
219 219 @ test@11.draft: 3
220 220 |
221 221 o test@10.draft: 4
222 222 |
223 223 o test@9.draft: 5
224 224 |
225 225 o bar@8.draft: 1
226 226 |
227 227 o foo@7.draft: 2
228 228 |
229 229 | o test@6.secret: 6
230 230 | |\
231 231 | | o test@5.draft: 5
232 232 | | |
233 233 | o | test@4.draft: 4
234 234 | |/
235 235 | o baz@3.public: 3
236 236 | |
237 237 | o test@2.public: 2
238 238 | |
239 239 | o bar@1.public: 1
240 240 |/
241 241 o test@0.public: 0
242 242
243 243 Graft again onto another branch should preserve the original source
244 244 $ hg up -q 0
245 245 $ echo 'g'>g
246 246 $ hg add g
247 247 $ hg ci -m 7
248 248 created new head
249 249 $ hg graft 7
250 250 grafting revision 7
251 251
252 252 $ hg log -r 7 --template '{rev}:{node}\n'
253 253 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
254 254 $ hg log -r 2 --template '{rev}:{node}\n'
255 255 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
256 256
257 257 $ hg log --debug -r tip
258 258 changeset: 13:9db0f28fd3747e92c57d015f53b5593aeec53c2d
259 259 tag: tip
260 260 phase: draft
261 261 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
262 262 parent: -1:0000000000000000000000000000000000000000
263 263 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
264 264 user: foo
265 265 date: Thu Jan 01 00:00:00 1970 +0000
266 266 files+: b
267 267 files-: a
268 268 extra: branch=default
269 269 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
270 270 description:
271 271 2
272 272
273 273
274 274 Disallow grafting an already grafted cset onto its original branch
275 275 $ hg up -q 6
276 276 $ hg graft 7
277 277 skipping already grafted revision 7 (was grafted from 2)
278 278 [255]
279 279
280 280 Disallow grafting already grafted csets with the same origin onto each other
281 281 $ hg up -q 13
282 282 $ hg graft 2
283 283 skipping already grafted revision 2
284 284 [255]
285 285 $ hg graft 7
286 286 skipping already grafted revision 7 (same origin 2)
287 287 [255]
288 288
289 289 $ hg up -q 7
290 290 $ hg graft 2
291 291 skipping already grafted revision 2
292 292 [255]
293 293 $ hg graft tip
294 294 skipping already grafted revision 13 (same origin 2)
295 295 [255]
296 296
297 297 Graft with --log
298 298
299 299 $ hg up -Cq 1
300 300 $ hg graft 3 --log -u foo
301 301 grafting revision 3
302 302 warning: can't find ancestor for 'c' copied from 'b'!
303 303 $ hg log --template '{rev} {parents} {desc}\n' -r tip
304 304 14 1:5d205f8b35b6 3
305 305 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
306 306
307 307 Resolve conflicted graft
308 308 $ hg up -q 0
309 309 $ echo b > a
310 310 $ hg ci -m 8
311 311 created new head
312 312 $ echo a > a
313 313 $ hg ci -m 9
314 314 $ hg graft 1 --tool internal:fail
315 315 grafting revision 1
316 316 abort: unresolved conflicts, can't continue
317 317 (use hg resolve and hg graft --continue)
318 318 [255]
319 319 $ hg resolve --all
320 320 merging a
321 321 $ hg graft -c
322 322 grafting revision 1
323 323 $ hg export tip --git
324 324 # HG changeset patch
325 325 # User bar
326 326 # Date 0 0
327 327 # Node ID 64ecd9071ce83c6e62f538d8ce7709d53f32ebf7
328 328 # Parent 4bdb9a9d0b84ffee1d30f0dfc7744cade17aa19c
329 329 1
330 330
331 331 diff --git a/a b/a
332 332 --- a/a
333 333 +++ b/a
334 334 @@ -1,1 +1,1 @@
335 335 -a
336 336 +b
337 337
338 338 Resolve conflicted graft with rename
339 339 $ echo c > a
340 340 $ hg ci -m 10
341 341 $ hg graft 2 --tool internal:fail
342 342 grafting revision 2
343 343 abort: unresolved conflicts, can't continue
344 344 (use hg resolve and hg graft --continue)
345 345 [255]
346 346 $ hg resolve --all
347 347 merging a and b to b
348 348 $ hg graft -c
349 349 grafting revision 2
350 350 $ hg export tip --git
351 351 # HG changeset patch
352 352 # User test
353 353 # Date 0 0
354 354 # Node ID 2e80e1351d6ed50302fe1e05f8bd1d4d412b6e11
355 355 # Parent e5a51ae854a8bbaaf25cc5c6a57ff46042dadbb4
356 356 2
357 357
358 358 diff --git a/a b/b
359 359 rename from a
360 360 rename to b
361 361
362 362 Test simple origin(), with and without args
363 363 $ hg log -r 'origin()'
364 364 changeset: 1:5d205f8b35b6
365 365 user: bar
366 366 date: Thu Jan 01 00:00:00 1970 +0000
367 367 summary: 1
368 368
369 369 changeset: 2:5c095ad7e90f
370 370 user: test
371 371 date: Thu Jan 01 00:00:00 1970 +0000
372 372 summary: 2
373 373
374 374 changeset: 3:4c60f11aa304
375 375 user: baz
376 376 date: Thu Jan 01 00:00:00 1970 +0000
377 377 summary: 3
378 378
379 379 changeset: 4:9c233e8e184d
380 380 user: test
381 381 date: Thu Jan 01 00:00:00 1970 +0000
382 382 summary: 4
383 383
384 384 changeset: 5:97f8bfe72746
385 385 branch: stable
386 386 parent: 3:4c60f11aa304
387 387 user: test
388 388 date: Thu Jan 01 00:00:00 1970 +0000
389 389 summary: 5
390 390
391 391 $ hg log -r 'origin(7)'
392 392 changeset: 2:5c095ad7e90f
393 393 user: test
394 394 date: Thu Jan 01 00:00:00 1970 +0000
395 395 summary: 2
396 396
397 397 Now transplant a graft to test following through copies
398 398 $ hg up -q 0
399 399 $ hg branch -q dev
400 400 $ hg ci -qm "dev branch"
401 401 $ hg --config extensions.transplant= transplant -q 7
402 402 $ hg log -r 'origin(.)'
403 403 changeset: 2:5c095ad7e90f
404 404 user: test
405 405 date: Thu Jan 01 00:00:00 1970 +0000
406 406 summary: 2
407 407
408 408 Test simple destination
409 409 $ hg log -r 'destination()'
410 410 changeset: 7:ef0ef43d49e7
411 411 parent: 0:68795b066622
412 412 user: foo
413 413 date: Thu Jan 01 00:00:00 1970 +0000
414 414 summary: 2
415 415
416 416 changeset: 8:6b9e5368ca4e
417 417 user: bar
418 418 date: Thu Jan 01 00:00:00 1970 +0000
419 419 summary: 1
420 420
421 421 changeset: 9:1905859650ec
422 422 user: test
423 423 date: Thu Jan 01 00:00:00 1970 +0000
424 424 summary: 5
425 425
426 426 changeset: 10:52dc0b4c6907
427 427 user: test
428 428 date: Thu Jan 01 00:00:00 1970 +0000
429 429 summary: 4
430 430
431 431 changeset: 11:882b35362a6b
432 432 user: test
433 433 date: Thu Jan 01 00:00:00 1970 +0000
434 434 summary: 3
435 435
436 436 changeset: 13:9db0f28fd374
437 437 user: foo
438 438 date: Thu Jan 01 00:00:00 1970 +0000
439 439 summary: 2
440 440
441 441 changeset: 14:f64defefacee
442 442 parent: 1:5d205f8b35b6
443 443 user: foo
444 444 date: Thu Jan 01 00:00:00 1970 +0000
445 445 summary: 3
446 446
447 447 changeset: 17:64ecd9071ce8
448 448 user: bar
449 449 date: Thu Jan 01 00:00:00 1970 +0000
450 450 summary: 1
451 451
452 452 changeset: 19:2e80e1351d6e
453 453 user: test
454 454 date: Thu Jan 01 00:00:00 1970 +0000
455 455 summary: 2
456 456
457 457 changeset: 21:7e61b508e709
458 458 branch: dev
459 459 tag: tip
460 460 user: foo
461 461 date: Thu Jan 01 00:00:00 1970 +0000
462 462 summary: 2
463 463
464 464 $ hg log -r 'destination(2)'
465 465 changeset: 7:ef0ef43d49e7
466 466 parent: 0:68795b066622
467 467 user: foo
468 468 date: Thu Jan 01 00:00:00 1970 +0000
469 469 summary: 2
470 470
471 471 changeset: 13:9db0f28fd374
472 472 user: foo
473 473 date: Thu Jan 01 00:00:00 1970 +0000
474 474 summary: 2
475 475
476 476 changeset: 19:2e80e1351d6e
477 477 user: test
478 478 date: Thu Jan 01 00:00:00 1970 +0000
479 479 summary: 2
480 480
481 481 changeset: 21:7e61b508e709
482 482 branch: dev
483 483 tag: tip
484 484 user: foo
485 485 date: Thu Jan 01 00:00:00 1970 +0000
486 486 summary: 2
487 487
488 488 Transplants of grafts can find a destination...
489 489 $ hg log -r 'destination(7)'
490 490 changeset: 21:7e61b508e709
491 491 branch: dev
492 492 tag: tip
493 493 user: foo
494 494 date: Thu Jan 01 00:00:00 1970 +0000
495 495 summary: 2
496 496
497 497 ... grafts of grafts unfortunately can't
498 498 $ hg graft -q 13
499 499 $ hg log -r 'destination(13)'
500 500 All copies of a cset
501 501 $ hg log -r 'origin(13) or destination(origin(13))'
502 502 changeset: 2:5c095ad7e90f
503 503 user: test
504 504 date: Thu Jan 01 00:00:00 1970 +0000
505 505 summary: 2
506 506
507 507 changeset: 7:ef0ef43d49e7
508 508 parent: 0:68795b066622
509 509 user: foo
510 510 date: Thu Jan 01 00:00:00 1970 +0000
511 511 summary: 2
512 512
513 513 changeset: 13:9db0f28fd374
514 514 user: foo
515 515 date: Thu Jan 01 00:00:00 1970 +0000
516 516 summary: 2
517 517
518 518 changeset: 19:2e80e1351d6e
519 519 user: test
520 520 date: Thu Jan 01 00:00:00 1970 +0000
521 521 summary: 2
522 522
523 523 changeset: 21:7e61b508e709
524 524 branch: dev
525 525 user: foo
526 526 date: Thu Jan 01 00:00:00 1970 +0000
527 527 summary: 2
528 528
529 529 changeset: 22:1313d0a825e2
530 530 branch: dev
531 531 tag: tip
532 532 user: foo
533 533 date: Thu Jan 01 00:00:00 1970 +0000
534 534 summary: 2
535 535
@@ -1,159 +1,159 b''
1 1 $ hg init t
2 2 $ cd t
3 3
4 4 $ mkdir a
5 5 $ echo foo > a/a
6 6 $ echo bar > a/b
7 7 $ hg ci -Am "0"
8 8 adding a/a
9 9 adding a/b
10 10
11 11 $ hg co -C 0
12 12 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 13 $ hg mv a b
14 14 moving a/a to b/a (glob)
15 15 moving a/b to b/b (glob)
16 16 $ hg ci -m "1 mv a/ b/"
17 17
18 18 $ hg co -C 0
19 19 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
20 20 $ echo baz > a/c
21 21 $ echo quux > a/d
22 22 $ hg add a/c
23 23 $ hg ci -m "2 add a/c"
24 24 created new head
25 25
26 26 $ hg merge --debug 1
27 27 searching for copies back to rev 1
28 28 unmatched files in local:
29 29 a/c
30 30 unmatched files in other:
31 31 b/a
32 32 b/b
33 33 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
34 34 src: 'a/a' -> dst: 'b/a'
35 35 src: 'a/b' -> dst: 'b/b'
36 36 checking for directory renames
37 37 discovered dir src: 'a/' -> dst: 'b/'
38 38 pending file src: 'a/c' -> dst: 'b/c'
39 39 resolving manifests
40 40 overwrite: False, partial: False
41 41 ancestor: f9b20c0d4c51, local: ce36d17b18fb+, remote: 397f8b00a740
42 a/c: remote renamed directory to b/c -> d
42 a/a: other deleted -> r
43 43 a/b: other deleted -> r
44 a/a: other deleted -> r
44 a/c: remote renamed directory to b/c -> d
45 45 b/a: remote created -> g
46 46 b/b: remote created -> g
47 47 updating: a/a 1/5 files (20.00%)
48 48 removing a/a
49 49 updating: a/b 2/5 files (40.00%)
50 50 removing a/b
51 51 updating: a/c 3/5 files (60.00%)
52 52 moving a/c to b/c
53 53 updating: b/a 4/5 files (80.00%)
54 54 getting b/a
55 55 updating: b/b 5/5 files (100.00%)
56 56 getting b/b
57 57 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
58 58 (branch merge, don't forget to commit)
59 59
60 60 $ echo a/* b/*
61 61 a/d b/a b/b b/c
62 62 $ hg st -C
63 63 M b/a
64 64 M b/b
65 65 A b/c
66 66 a/c
67 67 R a/a
68 68 R a/b
69 69 R a/c
70 70 ? a/d
71 71 $ hg ci -m "3 merge 2+1"
72 72 $ hg debugrename b/c
73 73 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88 (glob)
74 74
75 75 $ hg co -C 1
76 76 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
77 77 $ hg merge --debug 2
78 78 searching for copies back to rev 1
79 79 unmatched files in local:
80 80 b/a
81 81 b/b
82 82 unmatched files in other:
83 83 a/c
84 84 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
85 85 src: 'a/a' -> dst: 'b/a'
86 86 src: 'a/b' -> dst: 'b/b'
87 87 checking for directory renames
88 88 discovered dir src: 'a/' -> dst: 'b/'
89 89 pending file src: 'a/c' -> dst: 'b/c'
90 90 resolving manifests
91 91 overwrite: False, partial: False
92 92 ancestor: f9b20c0d4c51, local: 397f8b00a740+, remote: ce36d17b18fb
93 93 None: local renamed directory to b/c -> d
94 94 updating:None 1/1 files (100.00%)
95 95 getting a/c to b/c
96 96 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
97 97 (branch merge, don't forget to commit)
98 98
99 99 $ echo a/* b/*
100 100 a/d b/a b/b b/c
101 101 $ hg st -C
102 102 A b/c
103 103 a/c
104 104 ? a/d
105 105 $ hg ci -m "4 merge 1+2"
106 106 created new head
107 107 $ hg debugrename b/c
108 108 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88 (glob)
109 109
110 110
111 111 Second scenario with two repos:
112 112
113 113 $ cd ..
114 114 $ hg init r1
115 115 $ cd r1
116 116 $ mkdir a
117 117 $ echo foo > a/f
118 118 $ hg add a
119 119 adding a/f (glob)
120 120 $ hg ci -m "a/f == foo"
121 121 $ cd ..
122 122
123 123 $ hg clone r1 r2
124 124 updating to branch default
125 125 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
126 126 $ cd r2
127 127 $ hg mv a b
128 128 moving a/f to b/f (glob)
129 129 $ echo foo1 > b/f
130 130 $ hg ci -m" a -> b, b/f == foo1"
131 131 $ cd ..
132 132
133 133 $ cd r1
134 134 $ mkdir a/aa
135 135 $ echo bar > a/aa/g
136 136 $ hg add a/aa
137 137 adding a/aa/g (glob)
138 138 $ hg ci -m "a/aa/g"
139 139 $ hg pull ../r2
140 140 pulling from ../r2
141 141 searching for changes
142 142 adding changesets
143 143 adding manifests
144 144 adding file changes
145 145 added 1 changesets with 1 changes to 1 files (+1 heads)
146 146 (run 'hg heads' to see heads, 'hg merge' to merge)
147 147
148 148 $ hg merge
149 149 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
150 150 (branch merge, don't forget to commit)
151 151
152 152 $ hg st -C
153 153 M b/f
154 154 A b/aa/g
155 155 a/aa/g
156 156 R a/aa/g
157 157 R a/f
158 158
159 159 $ cd ..
@@ -1,85 +1,85 b''
1 1 $ hg init
2 2
3 3 $ touch a
4 4 $ hg add a
5 5 $ hg commit -m "Added a"
6 6
7 7 $ touch main
8 8 $ hg add main
9 9 $ hg commit -m "Added main"
10 10 $ hg checkout 0
11 11 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
12 12
13 13 'main' should be gone:
14 14
15 15 $ ls
16 16 a
17 17
18 18 $ touch side1
19 19 $ hg add side1
20 20 $ hg commit -m "Added side1"
21 21 created new head
22 22 $ touch side2
23 23 $ hg add side2
24 24 $ hg commit -m "Added side2"
25 25
26 26 $ hg log
27 27 changeset: 3:91ebc10ed028
28 28 tag: tip
29 29 user: test
30 30 date: Thu Jan 01 00:00:00 1970 +0000
31 31 summary: Added side2
32 32
33 33 changeset: 2:b932d7dbb1e1
34 34 parent: 0:c2eda428b523
35 35 user: test
36 36 date: Thu Jan 01 00:00:00 1970 +0000
37 37 summary: Added side1
38 38
39 39 changeset: 1:71a760306caf
40 40 user: test
41 41 date: Thu Jan 01 00:00:00 1970 +0000
42 42 summary: Added main
43 43
44 44 changeset: 0:c2eda428b523
45 45 user: test
46 46 date: Thu Jan 01 00:00:00 1970 +0000
47 47 summary: Added a
48 48
49 49
50 50 $ hg heads
51 51 changeset: 3:91ebc10ed028
52 52 tag: tip
53 53 user: test
54 54 date: Thu Jan 01 00:00:00 1970 +0000
55 55 summary: Added side2
56 56
57 57 changeset: 1:71a760306caf
58 58 user: test
59 59 date: Thu Jan 01 00:00:00 1970 +0000
60 60 summary: Added main
61 61
62 62 $ ls
63 63 a
64 64 side1
65 65 side2
66 66
67 67 $ hg update --debug -C 1
68 68 resolving manifests
69 69 overwrite: True, partial: False
70 70 ancestor: 91ebc10ed028+, local: 91ebc10ed028+, remote: 71a760306caf
71 side1: other deleted -> r
71 72 side2: other deleted -> r
72 side1: other deleted -> r
73 73 main: remote created -> g
74 74 updating: side1 1/3 files (33.33%)
75 75 removing side1
76 76 updating: side2 2/3 files (66.67%)
77 77 removing side2
78 78 updating: main 3/3 files (100.00%)
79 79 getting main
80 80 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
81 81
82 82 $ ls
83 83 a
84 84 main
85 85
General Comments 0
You need to be logged in to leave comments. Login now