##// END OF EJS Templates
checkcopies: handle divergences contained entirely in tca::ctx...
Gábor Stefanik -
r30201:856ead83 default
parent child Browse files
Show More
@@ -1,630 +1,635
1 1 # copies.py - copy detection for Mercurial
2 2 #
3 3 # Copyright 2008 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 __future__ import absolute_import
9 9
10 10 import heapq
11 11
12 12 from . import (
13 13 node,
14 14 pathutil,
15 15 scmutil,
16 16 util,
17 17 )
18 18
19 19 def _findlimit(repo, a, b):
20 20 """
21 21 Find the last revision that needs to be checked to ensure that a full
22 22 transitive closure for file copies can be properly calculated.
23 23 Generally, this means finding the earliest revision number that's an
24 24 ancestor of a or b but not both, except when a or b is a direct descendent
25 25 of the other, in which case we can return the minimum revnum of a and b.
26 26 None if no such revision exists.
27 27 """
28 28
29 29 # basic idea:
30 30 # - mark a and b with different sides
31 31 # - if a parent's children are all on the same side, the parent is
32 32 # on that side, otherwise it is on no side
33 33 # - walk the graph in topological order with the help of a heap;
34 34 # - add unseen parents to side map
35 35 # - clear side of any parent that has children on different sides
36 36 # - track number of interesting revs that might still be on a side
37 37 # - track the lowest interesting rev seen
38 38 # - quit when interesting revs is zero
39 39
40 40 cl = repo.changelog
41 41 working = len(cl) # pseudo rev for the working directory
42 42 if a is None:
43 43 a = working
44 44 if b is None:
45 45 b = working
46 46
47 47 side = {a: -1, b: 1}
48 48 visit = [-a, -b]
49 49 heapq.heapify(visit)
50 50 interesting = len(visit)
51 51 hascommonancestor = False
52 52 limit = working
53 53
54 54 while interesting:
55 55 r = -heapq.heappop(visit)
56 56 if r == working:
57 57 parents = [cl.rev(p) for p in repo.dirstate.parents()]
58 58 else:
59 59 parents = cl.parentrevs(r)
60 60 for p in parents:
61 61 if p < 0:
62 62 continue
63 63 if p not in side:
64 64 # first time we see p; add it to visit
65 65 side[p] = side[r]
66 66 if side[p]:
67 67 interesting += 1
68 68 heapq.heappush(visit, -p)
69 69 elif side[p] and side[p] != side[r]:
70 70 # p was interesting but now we know better
71 71 side[p] = 0
72 72 interesting -= 1
73 73 hascommonancestor = True
74 74 if side[r]:
75 75 limit = r # lowest rev visited
76 76 interesting -= 1
77 77
78 78 if not hascommonancestor:
79 79 return None
80 80
81 81 # Consider the following flow (see test-commit-amend.t under issue4405):
82 82 # 1/ File 'a0' committed
83 83 # 2/ File renamed from 'a0' to 'a1' in a new commit (call it 'a1')
84 84 # 3/ Move back to first commit
85 85 # 4/ Create a new commit via revert to contents of 'a1' (call it 'a1-amend')
86 86 # 5/ Rename file from 'a1' to 'a2' and commit --amend 'a1-msg'
87 87 #
88 88 # During the amend in step five, we will be in this state:
89 89 #
90 90 # @ 3 temporary amend commit for a1-amend
91 91 # |
92 92 # o 2 a1-amend
93 93 # |
94 94 # | o 1 a1
95 95 # |/
96 96 # o 0 a0
97 97 #
98 98 # When _findlimit is called, a and b are revs 3 and 0, so limit will be 2,
99 99 # yet the filelog has the copy information in rev 1 and we will not look
100 100 # back far enough unless we also look at the a and b as candidates.
101 101 # This only occurs when a is a descendent of b or visa-versa.
102 102 return min(limit, a, b)
103 103
104 104 def _chain(src, dst, a, b):
105 105 '''chain two sets of copies a->b'''
106 106 t = a.copy()
107 107 for k, v in b.iteritems():
108 108 if v in t:
109 109 # found a chain
110 110 if t[v] != k:
111 111 # file wasn't renamed back to itself
112 112 t[k] = t[v]
113 113 if v not in dst:
114 114 # chain was a rename, not a copy
115 115 del t[v]
116 116 if v in src:
117 117 # file is a copy of an existing file
118 118 t[k] = v
119 119
120 120 # remove criss-crossed copies
121 121 for k, v in t.items():
122 122 if k in src and v in dst:
123 123 del t[k]
124 124
125 125 return t
126 126
127 127 def _tracefile(fctx, am, limit=-1):
128 128 '''return file context that is the ancestor of fctx present in ancestor
129 129 manifest am, stopping after the first ancestor lower than limit'''
130 130
131 131 for f in fctx.ancestors():
132 132 if am.get(f.path(), None) == f.filenode():
133 133 return f
134 134 if limit >= 0 and f.linkrev() < limit and f.rev() < limit:
135 135 return None
136 136
137 137 def _dirstatecopies(d):
138 138 ds = d._repo.dirstate
139 139 c = ds.copies().copy()
140 140 for k in c.keys():
141 141 if ds[k] not in 'anm':
142 142 del c[k]
143 143 return c
144 144
145 145 def _computeforwardmissing(a, b, match=None):
146 146 """Computes which files are in b but not a.
147 147 This is its own function so extensions can easily wrap this call to see what
148 148 files _forwardcopies is about to process.
149 149 """
150 150 ma = a.manifest()
151 151 mb = b.manifest()
152 152 if match:
153 153 ma = ma.matches(match)
154 154 mb = mb.matches(match)
155 155 return mb.filesnotin(ma)
156 156
157 157 def _forwardcopies(a, b, match=None):
158 158 '''find {dst@b: src@a} copy mapping where a is an ancestor of b'''
159 159
160 160 # check for working copy
161 161 w = None
162 162 if b.rev() is None:
163 163 w = b
164 164 b = w.p1()
165 165 if a == b:
166 166 # short-circuit to avoid issues with merge states
167 167 return _dirstatecopies(w)
168 168
169 169 # files might have to be traced back to the fctx parent of the last
170 170 # one-side-only changeset, but not further back than that
171 171 limit = _findlimit(a._repo, a.rev(), b.rev())
172 172 if limit is None:
173 173 limit = -1
174 174 am = a.manifest()
175 175
176 176 # find where new files came from
177 177 # we currently don't try to find where old files went, too expensive
178 178 # this means we can miss a case like 'hg rm b; hg cp a b'
179 179 cm = {}
180 180
181 181 # Computing the forward missing is quite expensive on large manifests, since
182 182 # it compares the entire manifests. We can optimize it in the common use
183 183 # case of computing what copies are in a commit versus its parent (like
184 184 # during a rebase or histedit). Note, we exclude merge commits from this
185 185 # optimization, since the ctx.files() for a merge commit is not correct for
186 186 # this comparison.
187 187 forwardmissingmatch = match
188 188 if not match and b.p1() == a and b.p2().node() == node.nullid:
189 189 forwardmissingmatch = scmutil.matchfiles(a._repo, b.files())
190 190 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
191 191
192 192 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
193 193 for f in missing:
194 194 fctx = b[f]
195 195 fctx._ancestrycontext = ancestrycontext
196 196 ofctx = _tracefile(fctx, am, limit)
197 197 if ofctx:
198 198 cm[f] = ofctx.path()
199 199
200 200 # combine copies from dirstate if necessary
201 201 if w is not None:
202 202 cm = _chain(a, w, cm, _dirstatecopies(w))
203 203
204 204 return cm
205 205
206 206 def _backwardrenames(a, b):
207 207 if a._repo.ui.configbool('experimental', 'disablecopytrace'):
208 208 return {}
209 209
210 210 # Even though we're not taking copies into account, 1:n rename situations
211 211 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
212 212 # arbitrarily pick one of the renames.
213 213 f = _forwardcopies(b, a)
214 214 r = {}
215 215 for k, v in sorted(f.iteritems()):
216 216 # remove copies
217 217 if v in a:
218 218 continue
219 219 r[v] = k
220 220 return r
221 221
222 222 def pathcopies(x, y, match=None):
223 223 '''find {dst@y: src@x} copy mapping for directed compare'''
224 224 if x == y or not x or not y:
225 225 return {}
226 226 a = y.ancestor(x)
227 227 if a == x:
228 228 return _forwardcopies(x, y, match=match)
229 229 if a == y:
230 230 return _backwardrenames(x, y)
231 231 return _chain(x, y, _backwardrenames(x, a),
232 232 _forwardcopies(a, y, match=match))
233 233
234 234 def _computenonoverlap(repo, c1, c2, addedinm1, addedinm2, baselabel=''):
235 235 """Computes, based on addedinm1 and addedinm2, the files exclusive to c1
236 236 and c2. This is its own function so extensions can easily wrap this call
237 237 to see what files mergecopies is about to process.
238 238
239 239 Even though c1 and c2 are not used in this function, they are useful in
240 240 other extensions for being able to read the file nodes of the changed files.
241 241
242 242 "baselabel" can be passed to help distinguish the multiple computations
243 243 done in the graft case.
244 244 """
245 245 u1 = sorted(addedinm1 - addedinm2)
246 246 u2 = sorted(addedinm2 - addedinm1)
247 247
248 248 header = " unmatched files in %s"
249 249 if baselabel:
250 250 header += ' (from %s)' % baselabel
251 251 if u1:
252 252 repo.ui.debug("%s:\n %s\n" % (header % 'local', "\n ".join(u1)))
253 253 if u2:
254 254 repo.ui.debug("%s:\n %s\n" % (header % 'other', "\n ".join(u2)))
255 255 return u1, u2
256 256
257 257 def _makegetfctx(ctx):
258 258 """return a 'getfctx' function suitable for _checkcopies usage
259 259
260 260 We have to re-setup the function building 'filectx' for each
261 261 '_checkcopies' to ensure the linkrev adjustment is properly setup for
262 262 each. Linkrev adjustment is important to avoid bug in rename
263 263 detection. Moreover, having a proper '_ancestrycontext' setup ensures
264 264 the performance impact of this adjustment is kept limited. Without it,
265 265 each file could do a full dag traversal making the time complexity of
266 266 the operation explode (see issue4537).
267 267
268 268 This function exists here mostly to limit the impact on stable. Feel
269 269 free to refactor on default.
270 270 """
271 271 rev = ctx.rev()
272 272 repo = ctx._repo
273 273 ac = getattr(ctx, '_ancestrycontext', None)
274 274 if ac is None:
275 275 revs = [rev]
276 276 if rev is None:
277 277 revs = [p.rev() for p in ctx.parents()]
278 278 ac = repo.changelog.ancestors(revs, inclusive=True)
279 279 ctx._ancestrycontext = ac
280 280 def makectx(f, n):
281 281 if len(n) != 20: # in a working context?
282 282 if ctx.rev() is None:
283 283 return ctx.filectx(f)
284 284 return repo[None][f]
285 285 fctx = repo.filectx(f, fileid=n)
286 286 # setup only needed for filectx not create from a changectx
287 287 fctx._ancestrycontext = ac
288 288 fctx._descendantrev = rev
289 289 return fctx
290 290 return util.lrucachefunc(makectx)
291 291
292 292 def mergecopies(repo, c1, c2, base):
293 293 """
294 294 Find moves and copies between context c1 and c2 that are relevant
295 295 for merging. 'base' will be used as the merge base.
296 296
297 297 Returns four dicts: "copy", "movewithdir", "diverge", and
298 298 "renamedelete".
299 299
300 300 "copy" is a mapping from destination name -> source name,
301 301 where source is in c1 and destination is in c2 or vice-versa.
302 302
303 303 "movewithdir" is a mapping from source name -> destination name,
304 304 where the file at source present in one context but not the other
305 305 needs to be moved to destination by the merge process, because the
306 306 other context moved the directory it is in.
307 307
308 308 "diverge" is a mapping of source name -> list of destination names
309 309 for divergent renames.
310 310
311 311 "renamedelete" is a mapping of source name -> list of destination
312 312 names for files deleted in c1 that were renamed in c2 or vice-versa.
313 313 """
314 314 # avoid silly behavior for update from empty dir
315 315 if not c1 or not c2 or c1 == c2:
316 316 return {}, {}, {}, {}
317 317
318 318 # avoid silly behavior for parent -> working dir
319 319 if c2.node() is None and c1.node() == repo.dirstate.p1():
320 320 return repo.dirstate.copies(), {}, {}, {}
321 321
322 322 # Copy trace disabling is explicitly below the node == p1 logic above
323 323 # because the logic above is required for a simple copy to be kept across a
324 324 # rebase.
325 325 if repo.ui.configbool('experimental', 'disablecopytrace'):
326 326 return {}, {}, {}, {}
327 327
328 328 # In certain scenarios (e.g. graft, update or rebase), base can be
329 329 # overridden We still need to know a real common ancestor in this case We
330 330 # can't just compute _c1.ancestor(_c2) and compare it to ca, because there
331 331 # can be multiple common ancestors, e.g. in case of bidmerge. Because our
332 332 # caller may not know if the revision passed in lieu of the CA is a genuine
333 333 # common ancestor or not without explicitly checking it, it's better to
334 334 # determine that here.
335 335 #
336 336 # base.descendant(wc) and base.descendant(base) are False, work around that
337 337 _c1 = c1.p1() if c1.rev() is None else c1
338 338 _c2 = c2.p1() if c2.rev() is None else c2
339 339 # an endpoint is "dirty" if it isn't a descendant of the merge base
340 340 # if we have a dirty endpoint, we need to trigger graft logic, and also
341 341 # keep track of which endpoint is dirty
342 342 dirtyc1 = not (base == _c1 or base.descendant(_c1))
343 343 dirtyc2 = not (base== _c2 or base.descendant(_c2))
344 344 graft = dirtyc1 or dirtyc2
345 345 tca = base
346 346 if graft:
347 347 tca = _c1.ancestor(_c2)
348 348
349 349 limit = _findlimit(repo, c1.rev(), c2.rev())
350 350 if limit is None:
351 351 # no common ancestor, no copies
352 352 return {}, {}, {}, {}
353 353 repo.ui.debug(" searching for copies back to rev %d\n" % limit)
354 354
355 355 m1 = c1.manifest()
356 356 m2 = c2.manifest()
357 357 mb = base.manifest()
358 358
359 359 # gather data from _checkcopies:
360 360 # - diverge = record all diverges in this dict
361 361 # - copy = record all non-divergent copies in this dict
362 362 # - fullcopy = record all copies in this dict
363 363 diverge = {} # divergence data is shared
364 364 data1 = {'copy': {},
365 365 'fullcopy': {},
366 366 'diverge': diverge,
367 367 }
368 368 data2 = {'copy': {},
369 369 'fullcopy': {},
370 370 'diverge': diverge,
371 371 }
372 372
373 373 # find interesting file sets from manifests
374 374 addedinm1 = m1.filesnotin(mb)
375 375 addedinm2 = m2.filesnotin(mb)
376 376 bothnew = sorted(addedinm1 & addedinm2)
377 377 if tca == base:
378 378 # unmatched file from base
379 379 u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2)
380 380 u1u, u2u = u1r, u2r
381 381 else:
382 382 # unmatched file from base (DAG rotation in the graft case)
383 383 u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2,
384 384 baselabel='base')
385 385 # unmatched file from topological common ancestors (no DAG rotation)
386 386 # need to recompute this for directory move handling when grafting
387 387 mta = tca.manifest()
388 388 u1u, u2u = _computenonoverlap(repo, c1, c2, m1.filesnotin(mta),
389 389 m2.filesnotin(mta),
390 390 baselabel='topological common ancestor')
391 391
392 392 for f in u1u:
393 393 _checkcopies(c1, f, m1, m2, base, tca, limit, data1)
394 394
395 395 for f in u2u:
396 396 _checkcopies(c2, f, m2, m1, base, tca, limit, data2)
397 397
398 398 copy = dict(data1['copy'].items() + data2['copy'].items())
399 399 fullcopy = dict(data1['fullcopy'].items() + data2['fullcopy'].items())
400 400
401 401 renamedelete = {}
402 402 renamedeleteset = set()
403 403 divergeset = set()
404 404 for of, fl in diverge.items():
405 405 if len(fl) == 1 or of in c1 or of in c2:
406 406 del diverge[of] # not actually divergent, or not a rename
407 407 if of not in c1 and of not in c2:
408 408 # renamed on one side, deleted on the other side, but filter
409 409 # out files that have been renamed and then deleted
410 410 renamedelete[of] = [f for f in fl if f in c1 or f in c2]
411 411 renamedeleteset.update(fl) # reverse map for below
412 412 else:
413 413 divergeset.update(fl) # reverse map for below
414 414
415 415 if bothnew:
416 416 repo.ui.debug(" unmatched files new in both:\n %s\n"
417 417 % "\n ".join(bothnew))
418 418 bothdiverge = {}
419 419 bothdata = {'copy': {},
420 420 'fullcopy': {},
421 421 'diverge': bothdiverge,
422 422 }
423 423 for f in bothnew:
424 424 _checkcopies(c1, f, m1, m2, base, tca, limit, bothdata)
425 425 _checkcopies(c2, f, m2, m1, base, tca, limit, bothdata)
426 426 for of, fl in bothdiverge.items():
427 427 if len(fl) == 2 and fl[0] == fl[1]:
428 428 copy[fl[0]] = of # not actually divergent, just matching renames
429 429
430 430 if fullcopy and repo.ui.debugflag:
431 431 repo.ui.debug(" all copies found (* = to merge, ! = divergent, "
432 432 "% = renamed and deleted):\n")
433 433 for f in sorted(fullcopy):
434 434 note = ""
435 435 if f in copy:
436 436 note += "*"
437 437 if f in divergeset:
438 438 note += "!"
439 439 if f in renamedeleteset:
440 440 note += "%"
441 441 repo.ui.debug(" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f,
442 442 note))
443 443 del divergeset
444 444
445 445 if not fullcopy:
446 446 return copy, {}, diverge, renamedelete
447 447
448 448 repo.ui.debug(" checking for directory renames\n")
449 449
450 450 # generate a directory move map
451 451 d1, d2 = c1.dirs(), c2.dirs()
452 452 # Hack for adding '', which is not otherwise added, to d1 and d2
453 453 d1.addpath('/')
454 454 d2.addpath('/')
455 455 invalid = set()
456 456 dirmove = {}
457 457
458 458 # examine each file copy for a potential directory move, which is
459 459 # when all the files in a directory are moved to a new directory
460 460 for dst, src in fullcopy.iteritems():
461 461 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
462 462 if dsrc in invalid:
463 463 # already seen to be uninteresting
464 464 continue
465 465 elif dsrc in d1 and ddst in d1:
466 466 # directory wasn't entirely moved locally
467 467 invalid.add(dsrc + "/")
468 468 elif dsrc in d2 and ddst in d2:
469 469 # directory wasn't entirely moved remotely
470 470 invalid.add(dsrc + "/")
471 471 elif dsrc + "/" in dirmove and dirmove[dsrc + "/"] != ddst + "/":
472 472 # files from the same directory moved to two different places
473 473 invalid.add(dsrc + "/")
474 474 else:
475 475 # looks good so far
476 476 dirmove[dsrc + "/"] = ddst + "/"
477 477
478 478 for i in invalid:
479 479 if i in dirmove:
480 480 del dirmove[i]
481 481 del d1, d2, invalid
482 482
483 483 if not dirmove:
484 484 return copy, {}, diverge, renamedelete
485 485
486 486 for d in dirmove:
487 487 repo.ui.debug(" discovered dir src: '%s' -> dst: '%s'\n" %
488 488 (d, dirmove[d]))
489 489
490 490 movewithdir = {}
491 491 # check unaccounted nonoverlapping files against directory moves
492 492 for f in u1r + u2r:
493 493 if f not in fullcopy:
494 494 for d in dirmove:
495 495 if f.startswith(d):
496 496 # new file added in a directory that was moved, move it
497 497 df = dirmove[d] + f[len(d):]
498 498 if df not in copy:
499 499 movewithdir[f] = df
500 500 repo.ui.debug((" pending file src: '%s' -> "
501 501 "dst: '%s'\n") % (f, df))
502 502 break
503 503
504 504 return copy, movewithdir, diverge, renamedelete
505 505
506 506 def _related(f1, f2, limit):
507 507 """return True if f1 and f2 filectx have a common ancestor
508 508
509 509 Walk back to common ancestor to see if the two files originate
510 510 from the same file. Since workingfilectx's rev() is None it messes
511 511 up the integer comparison logic, hence the pre-step check for
512 512 None (f1 and f2 can only be workingfilectx's initially).
513 513 """
514 514
515 515 if f1 == f2:
516 516 return f1 # a match
517 517
518 518 g1, g2 = f1.ancestors(), f2.ancestors()
519 519 try:
520 520 f1r, f2r = f1.linkrev(), f2.linkrev()
521 521
522 522 if f1r is None:
523 523 f1 = next(g1)
524 524 if f2r is None:
525 525 f2 = next(g2)
526 526
527 527 while True:
528 528 f1r, f2r = f1.linkrev(), f2.linkrev()
529 529 if f1r > f2r:
530 530 f1 = next(g1)
531 531 elif f2r > f1r:
532 532 f2 = next(g2)
533 533 elif f1 == f2:
534 534 return f1 # a match
535 535 elif f1r == f2r or f1r < limit or f2r < limit:
536 536 return False # copy no longer relevant
537 537 except StopIteration:
538 538 return False
539 539
540 540 def _checkcopies(ctx, f, m1, m2, base, tca, limit, data):
541 541 """
542 542 check possible copies of f from m1 to m2
543 543
544 544 ctx = starting context for f in m1
545 545 f = the filename to check (as in m1)
546 546 m1 = the source manifest
547 547 m2 = the destination manifest
548 548 base = the changectx used as a merge base
549 549 tca = topological common ancestor for graft-like scenarios
550 550 limit = the rev number to not search beyond
551 551 data = dictionary of dictionary to store copy data. (see mergecopies)
552 552
553 553 note: limit is only an optimization, and there is no guarantee that
554 554 irrelevant revisions will not be limited
555 555 there is no easy way to make this algorithm stop in a guaranteed way
556 556 once it "goes behind a certain revision".
557 557 """
558 558
559 559 mb = base.manifest()
560 560 # Might be true if this call is about finding backward renames,
561 561 # This happens in the case of grafts because the DAG is then rotated.
562 562 # If the file exists in both the base and the source, we are not looking
563 563 # for a rename on the source side, but on the part of the DAG that is
564 564 # traversed backwards.
565 565 #
566 566 # In the case there is both backward and forward renames (before and after
567 # the base) this is more complicated as we must detect a divergence. This
568 # is currently broken and hopefully some later code update will make that
569 # work (we use 'backwards = False' in that case)
567 # the base) this is more complicated as we must detect a divergence.
568 # We use 'backwards = False' in that case.
570 569 backwards = base != tca and f in mb
571 570 getfctx = _makegetfctx(ctx)
572 571
573 572 of = None
574 573 seen = set([f])
575 574 for oc in getfctx(f, m1[f]).ancestors():
576 575 ocr = oc.linkrev()
577 576 of = oc.path()
578 577 if of in seen:
579 578 # check limit late - grab last rename before
580 579 if ocr < limit:
581 580 break
582 581 continue
583 582 seen.add(of)
584 583
585 584 # remember for dir rename detection
586 585 if backwards:
587 586 data['fullcopy'][of] = f # grafting backwards through renames
588 587 else:
589 588 data['fullcopy'][f] = of
590 589 if of not in m2:
591 590 continue # no match, keep looking
592 591 if m2[of] == mb.get(of):
593 592 return # no merge needed, quit early
594 593 c2 = getfctx(of, m2[of])
595 594 # c2 might be a plain new file on added on destination side that is
596 595 # unrelated to the droids we are looking for.
597 596 cr = _related(oc, c2, tca.rev())
598 597 if cr and (of == f or of == c2.path()): # non-divergent
599 598 if backwards:
600 599 data['copy'][of] = f
601 600 elif of in mb:
602 601 data['copy'][f] = of
602 else: # divergence w.r.t. graft CA on one side of topological CA
603 for sf in seen:
604 if sf in mb:
605 assert sf not in data['diverge']
606 data['diverge'][sf] = [f, of]
607 break
603 608 return
604 609
605 610 if of in mb:
606 611 data['diverge'].setdefault(of, []).append(f)
607 612
608 613 def duplicatecopies(repo, rev, fromrev, skiprev=None):
609 614 '''reproduce copies from fromrev to rev in the dirstate
610 615
611 616 If skiprev is specified, it's a revision that should be used to
612 617 filter copy records. Any copies that occur between fromrev and
613 618 skiprev will not be duplicated, even if they appear in the set of
614 619 copies between fromrev and rev.
615 620 '''
616 621 exclude = {}
617 622 if (skiprev is not None and
618 623 not repo.ui.configbool('experimental', 'disablecopytrace')):
619 624 # disablecopytrace skips this line, but not the entire function because
620 625 # the line below is O(size of the repo) during a rebase, while the rest
621 626 # of the function is much faster (and is required for carrying copy
622 627 # metadata across the rebase anyway).
623 628 exclude = pathcopies(repo[fromrev], repo[skiprev])
624 629 for dst, src in pathcopies(repo[fromrev], repo[rev]).iteritems():
625 630 # copies.pathcopies returns backward renames, so dst might not
626 631 # actually be in the dirstate
627 632 if dst in exclude:
628 633 continue
629 634 if repo.dirstate[dst] in "nma":
630 635 repo.dirstate.copy(src, dst)
@@ -1,1333 +1,1336
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extdiff]
3 3 > # for portability:
4 4 > pdiff = sh "$RUNTESTDIR/pdiff"
5 5 > EOF
6 6
7 7 Create a repo with some stuff in it:
8 8
9 9 $ hg init a
10 10 $ cd a
11 11 $ echo a > a
12 12 $ echo a > d
13 13 $ echo a > e
14 14 $ hg ci -qAm0
15 15 $ echo b > a
16 16 $ hg ci -m1 -u bar
17 17 $ hg mv a b
18 18 $ hg ci -m2
19 19 $ hg cp b c
20 20 $ hg ci -m3 -u baz
21 21 $ echo b > d
22 22 $ echo f > e
23 23 $ hg ci -m4
24 24 $ hg up -q 3
25 25 $ echo b > e
26 26 $ hg branch -q stable
27 27 $ hg ci -m5
28 28 $ hg merge -q default --tool internal:local
29 29 $ hg branch -q default
30 30 $ hg ci -m6
31 31 $ hg phase --public 3
32 32 $ hg phase --force --secret 6
33 33
34 34 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
35 35 @ test@6.secret: 6
36 36 |\
37 37 | o test@5.draft: 5
38 38 | |
39 39 o | test@4.draft: 4
40 40 |/
41 41 o baz@3.public: 3
42 42 |
43 43 o test@2.public: 2
44 44 |
45 45 o bar@1.public: 1
46 46 |
47 47 o test@0.public: 0
48 48
49 49 Can't continue without starting:
50 50
51 51 $ hg rm -q e
52 52 $ hg graft --continue
53 53 abort: no graft in progress
54 54 [255]
55 55 $ hg revert -r . -q e
56 56
57 57 Need to specify a rev:
58 58
59 59 $ hg graft
60 60 abort: no revisions specified
61 61 [255]
62 62
63 63 Can't graft ancestor:
64 64
65 65 $ hg graft 1 2
66 66 skipping ancestor revision 1:5d205f8b35b6
67 67 skipping ancestor revision 2:5c095ad7e90f
68 68 [255]
69 69
70 70 Specify revisions with -r:
71 71
72 72 $ hg graft -r 1 -r 2
73 73 skipping ancestor revision 1:5d205f8b35b6
74 74 skipping ancestor revision 2:5c095ad7e90f
75 75 [255]
76 76
77 77 $ hg graft -r 1 2
78 78 warning: inconsistent use of --rev might give unexpected revision ordering!
79 79 skipping ancestor revision 2:5c095ad7e90f
80 80 skipping ancestor revision 1:5d205f8b35b6
81 81 [255]
82 82
83 83 Can't graft with dirty wd:
84 84
85 85 $ hg up -q 0
86 86 $ echo foo > a
87 87 $ hg graft 1
88 88 abort: uncommitted changes
89 89 [255]
90 90 $ hg revert a
91 91
92 92 Graft a rename:
93 93 (this also tests that editor is invoked if '--edit' is specified)
94 94
95 95 $ hg status --rev "2^1" --rev 2
96 96 A b
97 97 R a
98 98 $ HGEDITOR=cat hg graft 2 -u foo --edit
99 99 grafting 2:5c095ad7e90f "2"
100 100 merging a and b to b
101 101 2
102 102
103 103
104 104 HG: Enter commit message. Lines beginning with 'HG:' are removed.
105 105 HG: Leave message empty to abort commit.
106 106 HG: --
107 107 HG: user: foo
108 108 HG: branch 'default'
109 109 HG: added b
110 110 HG: removed a
111 111 $ hg export tip --git
112 112 # HG changeset patch
113 113 # User foo
114 114 # Date 0 0
115 115 # Thu Jan 01 00:00:00 1970 +0000
116 116 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
117 117 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
118 118 2
119 119
120 120 diff --git a/a b/b
121 121 rename from a
122 122 rename to b
123 123
124 124 Look for extra:source
125 125
126 126 $ hg log --debug -r tip
127 127 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
128 128 tag: tip
129 129 phase: draft
130 130 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
131 131 parent: -1:0000000000000000000000000000000000000000
132 132 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
133 133 user: foo
134 134 date: Thu Jan 01 00:00:00 1970 +0000
135 135 files+: b
136 136 files-: a
137 137 extra: branch=default
138 138 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
139 139 description:
140 140 2
141 141
142 142
143 143
144 144 Graft out of order, skipping a merge and a duplicate
145 145 (this also tests that editor is not invoked if '--edit' is not specified)
146 146
147 147 $ hg graft 1 5 4 3 'merge()' 2 -n
148 148 skipping ungraftable merge revision 6
149 149 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
150 150 grafting 1:5d205f8b35b6 "1"
151 151 grafting 5:97f8bfe72746 "5"
152 152 grafting 4:9c233e8e184d "4"
153 153 grafting 3:4c60f11aa304 "3"
154 154
155 155 $ HGEDITOR=cat hg graft 1 5 'merge()' 2 --debug
156 156 skipping ungraftable merge revision 6
157 157 scanning for duplicate grafts
158 158 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
159 159 grafting 1:5d205f8b35b6 "1"
160 160 searching for copies back to rev 1
161 161 unmatched files in local:
162 162 b
163 163 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
164 164 src: 'a' -> dst: 'b' *
165 165 checking for directory renames
166 166 resolving manifests
167 167 branchmerge: True, force: True, partial: False
168 168 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
169 169 preserving b for resolve of b
170 170 starting 4 threads for background file closing (?)
171 171 b: local copied/moved from a -> m (premerge)
172 172 picked tool ':merge' for b (binary False symlink False changedelete False)
173 173 merging b and a to b
174 174 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
175 175 premerge successful
176 176 committing files:
177 177 b
178 178 committing manifest
179 179 committing changelog
180 180 grafting 5:97f8bfe72746 "5"
181 181 searching for copies back to rev 1
182 182 unmatched files in other (from topological common ancestor):
183 183 c
184 184 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
185 185 src: 'c' -> dst: 'b' *
186 186 checking for directory renames
187 187 resolving manifests
188 188 branchmerge: True, force: True, partial: False
189 189 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
190 190 e: remote is newer -> g
191 191 getting e
192 192 b: remote unchanged -> k
193 193 committing files:
194 194 e
195 195 committing manifest
196 196 committing changelog
197 197 $ HGEDITOR=cat hg graft 4 3 --log --debug
198 198 scanning for duplicate grafts
199 199 grafting 4:9c233e8e184d "4"
200 200 searching for copies back to rev 1
201 201 unmatched files in other (from topological common ancestor):
202 202 c
203 203 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
204 204 src: 'c' -> dst: 'b' *
205 205 checking for directory renames
206 206 resolving manifests
207 207 branchmerge: True, force: True, partial: False
208 208 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
209 209 preserving e for resolve of e
210 210 d: remote is newer -> g
211 211 getting d
212 212 b: remote unchanged -> k
213 213 e: versions differ -> m (premerge)
214 214 picked tool ':merge' for e (binary False symlink False changedelete False)
215 215 merging e
216 216 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
217 217 e: versions differ -> m (merge)
218 218 picked tool ':merge' for e (binary False symlink False changedelete False)
219 219 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
220 220 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
221 221 abort: unresolved conflicts, can't continue
222 222 (use 'hg resolve' and 'hg graft --continue --log')
223 223 [255]
224 224
225 225 Summary should mention graft:
226 226
227 227 $ hg summary |grep graft
228 228 commit: 2 modified, 2 unknown, 1 unresolved (graft in progress)
229 229
230 230 Commit while interrupted should fail:
231 231
232 232 $ hg ci -m 'commit interrupted graft'
233 233 abort: graft in progress
234 234 (use 'hg graft --continue' or 'hg update' to abort)
235 235 [255]
236 236
237 237 Abort the graft and try committing:
238 238
239 239 $ hg up -C .
240 240 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
241 241 $ echo c >> e
242 242 $ hg ci -mtest
243 243
244 244 $ hg strip . --config extensions.strip=
245 245 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
246 246 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
247 247
248 248 Graft again:
249 249
250 250 $ hg graft 1 5 4 3 'merge()' 2
251 251 skipping ungraftable merge revision 6
252 252 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
253 253 skipping revision 1:5d205f8b35b6 (already grafted to 8:6b9e5368ca4e)
254 254 skipping revision 5:97f8bfe72746 (already grafted to 9:1905859650ec)
255 255 grafting 4:9c233e8e184d "4"
256 256 merging e
257 257 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
258 258 abort: unresolved conflicts, can't continue
259 259 (use 'hg resolve' and 'hg graft --continue')
260 260 [255]
261 261
262 262 Continue without resolve should fail:
263 263
264 264 $ hg graft -c
265 265 grafting 4:9c233e8e184d "4"
266 266 abort: unresolved merge conflicts (see 'hg help resolve')
267 267 [255]
268 268
269 269 Fix up:
270 270
271 271 $ echo b > e
272 272 $ hg resolve -m e
273 273 (no more unresolved files)
274 274 continue: hg graft --continue
275 275
276 276 Continue with a revision should fail:
277 277
278 278 $ hg graft -c 6
279 279 abort: can't specify --continue and revisions
280 280 [255]
281 281
282 282 $ hg graft -c -r 6
283 283 abort: can't specify --continue and revisions
284 284 [255]
285 285
286 286 Continue for real, clobber usernames
287 287
288 288 $ hg graft -c -U
289 289 grafting 4:9c233e8e184d "4"
290 290 grafting 3:4c60f11aa304 "3"
291 291
292 292 Compare with original:
293 293
294 294 $ hg diff -r 6
295 295 $ hg status --rev 0:. -C
296 296 M d
297 297 M e
298 298 A b
299 299 a
300 300 A c
301 301 a
302 302 R a
303 303
304 304 View graph:
305 305
306 306 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
307 307 @ test@11.draft: 3
308 308 |
309 309 o test@10.draft: 4
310 310 |
311 311 o test@9.draft: 5
312 312 |
313 313 o bar@8.draft: 1
314 314 |
315 315 o foo@7.draft: 2
316 316 |
317 317 | o test@6.secret: 6
318 318 | |\
319 319 | | o test@5.draft: 5
320 320 | | |
321 321 | o | test@4.draft: 4
322 322 | |/
323 323 | o baz@3.public: 3
324 324 | |
325 325 | o test@2.public: 2
326 326 | |
327 327 | o bar@1.public: 1
328 328 |/
329 329 o test@0.public: 0
330 330
331 331 Graft again onto another branch should preserve the original source
332 332 $ hg up -q 0
333 333 $ echo 'g'>g
334 334 $ hg add g
335 335 $ hg ci -m 7
336 336 created new head
337 337 $ hg graft 7
338 338 grafting 7:ef0ef43d49e7 "2"
339 339
340 340 $ hg log -r 7 --template '{rev}:{node}\n'
341 341 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
342 342 $ hg log -r 2 --template '{rev}:{node}\n'
343 343 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
344 344
345 345 $ hg log --debug -r tip
346 346 changeset: 13:7a4785234d87ec1aa420ed6b11afe40fa73e12a9
347 347 tag: tip
348 348 phase: draft
349 349 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
350 350 parent: -1:0000000000000000000000000000000000000000
351 351 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
352 352 user: foo
353 353 date: Thu Jan 01 00:00:00 1970 +0000
354 354 files+: b
355 355 files-: a
356 356 extra: branch=default
357 357 extra: intermediate-source=ef0ef43d49e79e81ddafdc7997401ba0041efc82
358 358 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
359 359 description:
360 360 2
361 361
362 362
363 363 Disallow grafting an already grafted cset onto its original branch
364 364 $ hg up -q 6
365 365 $ hg graft 7
366 366 skipping already grafted revision 7:ef0ef43d49e7 (was grafted from 2:5c095ad7e90f)
367 367 [255]
368 368
369 369 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13
370 370 --- */hg-5c095ad7e90f.patch * (glob)
371 371 +++ */hg-7a4785234d87.patch * (glob)
372 372 @@ -1,18 +1,18 @@
373 373 # HG changeset patch
374 374 -# User test
375 375 +# User foo
376 376 # Date 0 0
377 377 # Thu Jan 01 00:00:00 1970 +0000
378 378 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
379 379 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
380 380 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
381 381 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
382 382 2
383 383
384 384 -diff -r 5d205f8b35b6 -r 5c095ad7e90f a
385 385 +diff -r b592ea63bb0c -r 7a4785234d87 a
386 386 --- a/a Thu Jan 01 00:00:00 1970 +0000
387 387 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
388 388 @@ -1,1 +0,0 @@
389 389 --b
390 390 -diff -r 5d205f8b35b6 -r 5c095ad7e90f b
391 391 +-a
392 392 +diff -r b592ea63bb0c -r 7a4785234d87 b
393 393 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
394 394 +++ b/b Thu Jan 01 00:00:00 1970 +0000
395 395 @@ -0,0 +1,1 @@
396 396 -+b
397 397 ++a
398 398 [1]
399 399
400 400 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13 -X .
401 401 --- */hg-5c095ad7e90f.patch * (glob)
402 402 +++ */hg-7a4785234d87.patch * (glob)
403 403 @@ -1,8 +1,8 @@
404 404 # HG changeset patch
405 405 -# User test
406 406 +# User foo
407 407 # Date 0 0
408 408 # Thu Jan 01 00:00:00 1970 +0000
409 409 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
410 410 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
411 411 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
412 412 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
413 413 2
414 414
415 415 [1]
416 416
417 417 Disallow grafting already grafted csets with the same origin onto each other
418 418 $ hg up -q 13
419 419 $ hg graft 2
420 420 skipping revision 2:5c095ad7e90f (already grafted to 13:7a4785234d87)
421 421 [255]
422 422 $ hg graft 7
423 423 skipping already grafted revision 7:ef0ef43d49e7 (13:7a4785234d87 also has origin 2:5c095ad7e90f)
424 424 [255]
425 425
426 426 $ hg up -q 7
427 427 $ hg graft 2
428 428 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
429 429 [255]
430 430 $ hg graft tip
431 431 skipping already grafted revision 13:7a4785234d87 (7:ef0ef43d49e7 also has origin 2:5c095ad7e90f)
432 432 [255]
433 433
434 434 Graft with --log
435 435
436 436 $ hg up -Cq 1
437 437 $ hg graft 3 --log -u foo
438 438 grafting 3:4c60f11aa304 "3"
439 439 warning: can't find ancestor for 'c' copied from 'b'!
440 440 $ hg log --template '{rev}:{node|short} {parents} {desc}\n' -r tip
441 441 14:0c921c65ef1e 1:5d205f8b35b6 3
442 442 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
443 443
444 444 Resolve conflicted graft
445 445 $ hg up -q 0
446 446 $ echo b > a
447 447 $ hg ci -m 8
448 448 created new head
449 449 $ echo c > a
450 450 $ hg ci -m 9
451 451 $ hg graft 1 --tool internal:fail
452 452 grafting 1:5d205f8b35b6 "1"
453 453 abort: unresolved conflicts, can't continue
454 454 (use 'hg resolve' and 'hg graft --continue')
455 455 [255]
456 456 $ hg resolve --all
457 457 merging a
458 458 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
459 459 [1]
460 460 $ cat a
461 461 <<<<<<< local: aaa4406d4f0a - test: 9
462 462 c
463 463 =======
464 464 b
465 465 >>>>>>> graft: 5d205f8b35b6 - bar: 1
466 466 $ echo b > a
467 467 $ hg resolve -m a
468 468 (no more unresolved files)
469 469 continue: hg graft --continue
470 470 $ hg graft -c
471 471 grafting 1:5d205f8b35b6 "1"
472 472 $ hg export tip --git
473 473 # HG changeset patch
474 474 # User bar
475 475 # Date 0 0
476 476 # Thu Jan 01 00:00:00 1970 +0000
477 477 # Node ID f67661df0c4804d301f064f332b57e7d5ddaf2be
478 478 # Parent aaa4406d4f0ae9befd6e58c82ec63706460cbca6
479 479 1
480 480
481 481 diff --git a/a b/a
482 482 --- a/a
483 483 +++ b/a
484 484 @@ -1,1 +1,1 @@
485 485 -c
486 486 +b
487 487
488 488 Resolve conflicted graft with rename
489 489 $ echo c > a
490 490 $ hg ci -m 10
491 491 $ hg graft 2 --tool internal:fail
492 492 grafting 2:5c095ad7e90f "2"
493 493 abort: unresolved conflicts, can't continue
494 494 (use 'hg resolve' and 'hg graft --continue')
495 495 [255]
496 496 $ hg resolve --all
497 497 merging a and b to b
498 498 (no more unresolved files)
499 499 continue: hg graft --continue
500 500 $ hg graft -c
501 501 grafting 2:5c095ad7e90f "2"
502 502 $ hg export tip --git
503 503 # HG changeset patch
504 504 # User test
505 505 # Date 0 0
506 506 # Thu Jan 01 00:00:00 1970 +0000
507 507 # Node ID 9627f653b421c61fc1ea4c4e366745070fa3d2bc
508 508 # Parent ee295f490a40b97f3d18dd4c4f1c8936c233b612
509 509 2
510 510
511 511 diff --git a/a b/b
512 512 rename from a
513 513 rename to b
514 514
515 515 Test simple origin(), with and without args
516 516 $ hg log -r 'origin()'
517 517 changeset: 1:5d205f8b35b6
518 518 user: bar
519 519 date: Thu Jan 01 00:00:00 1970 +0000
520 520 summary: 1
521 521
522 522 changeset: 2:5c095ad7e90f
523 523 user: test
524 524 date: Thu Jan 01 00:00:00 1970 +0000
525 525 summary: 2
526 526
527 527 changeset: 3:4c60f11aa304
528 528 user: baz
529 529 date: Thu Jan 01 00:00:00 1970 +0000
530 530 summary: 3
531 531
532 532 changeset: 4:9c233e8e184d
533 533 user: test
534 534 date: Thu Jan 01 00:00:00 1970 +0000
535 535 summary: 4
536 536
537 537 changeset: 5:97f8bfe72746
538 538 branch: stable
539 539 parent: 3:4c60f11aa304
540 540 user: test
541 541 date: Thu Jan 01 00:00:00 1970 +0000
542 542 summary: 5
543 543
544 544 $ hg log -r 'origin(7)'
545 545 changeset: 2:5c095ad7e90f
546 546 user: test
547 547 date: Thu Jan 01 00:00:00 1970 +0000
548 548 summary: 2
549 549
550 550 Now transplant a graft to test following through copies
551 551 $ hg up -q 0
552 552 $ hg branch -q dev
553 553 $ hg ci -qm "dev branch"
554 554 $ hg --config extensions.transplant= transplant -q 7
555 555 $ hg log -r 'origin(.)'
556 556 changeset: 2:5c095ad7e90f
557 557 user: test
558 558 date: Thu Jan 01 00:00:00 1970 +0000
559 559 summary: 2
560 560
561 561 Test that the graft and transplant markers in extra are converted, allowing
562 562 origin() to still work. Note that these recheck the immediately preceeding two
563 563 tests.
564 564 $ hg --quiet --config extensions.convert= --config convert.hg.saverev=True convert . ../converted
565 565
566 566 The graft case
567 567 $ hg -R ../converted log -r 7 --template "{rev}: {node}\n{join(extras, '\n')}\n"
568 568 7: 7ae846e9111fc8f57745634250c7b9ac0a60689b
569 569 branch=default
570 570 convert_revision=ef0ef43d49e79e81ddafdc7997401ba0041efc82
571 571 source=e0213322b2c1a5d5d236c74e79666441bee67a7d
572 572 $ hg -R ../converted log -r 'origin(7)'
573 573 changeset: 2:e0213322b2c1
574 574 user: test
575 575 date: Thu Jan 01 00:00:00 1970 +0000
576 576 summary: 2
577 577
578 578 Test that template correctly expands more than one 'extra' (issue4362), and that
579 579 'intermediate-source' is converted.
580 580 $ hg -R ../converted log -r 13 --template "{extras % ' Extra: {extra}\n'}"
581 581 Extra: branch=default
582 582 Extra: convert_revision=7a4785234d87ec1aa420ed6b11afe40fa73e12a9
583 583 Extra: intermediate-source=7ae846e9111fc8f57745634250c7b9ac0a60689b
584 584 Extra: source=e0213322b2c1a5d5d236c74e79666441bee67a7d
585 585
586 586 The transplant case
587 587 $ hg -R ../converted log -r tip --template "{rev}: {node}\n{join(extras, '\n')}\n"
588 588 21: fbb6c5cc81002f2b4b49c9d731404688bcae5ade
589 589 branch=dev
590 590 convert_revision=7e61b508e709a11d28194a5359bc3532d910af21
591 591 transplant_source=z\xe8F\xe9\x11\x1f\xc8\xf5wEcBP\xc7\xb9\xac (esc)
592 592 `h\x9b (esc)
593 593 $ hg -R ../converted log -r 'origin(tip)'
594 594 changeset: 2:e0213322b2c1
595 595 user: test
596 596 date: Thu Jan 01 00:00:00 1970 +0000
597 597 summary: 2
598 598
599 599
600 600 Test simple destination
601 601 $ hg log -r 'destination()'
602 602 changeset: 7:ef0ef43d49e7
603 603 parent: 0:68795b066622
604 604 user: foo
605 605 date: Thu Jan 01 00:00:00 1970 +0000
606 606 summary: 2
607 607
608 608 changeset: 8:6b9e5368ca4e
609 609 user: bar
610 610 date: Thu Jan 01 00:00:00 1970 +0000
611 611 summary: 1
612 612
613 613 changeset: 9:1905859650ec
614 614 user: test
615 615 date: Thu Jan 01 00:00:00 1970 +0000
616 616 summary: 5
617 617
618 618 changeset: 10:52dc0b4c6907
619 619 user: test
620 620 date: Thu Jan 01 00:00:00 1970 +0000
621 621 summary: 4
622 622
623 623 changeset: 11:882b35362a6b
624 624 user: test
625 625 date: Thu Jan 01 00:00:00 1970 +0000
626 626 summary: 3
627 627
628 628 changeset: 13:7a4785234d87
629 629 user: foo
630 630 date: Thu Jan 01 00:00:00 1970 +0000
631 631 summary: 2
632 632
633 633 changeset: 14:0c921c65ef1e
634 634 parent: 1:5d205f8b35b6
635 635 user: foo
636 636 date: Thu Jan 01 00:00:00 1970 +0000
637 637 summary: 3
638 638
639 639 changeset: 17:f67661df0c48
640 640 user: bar
641 641 date: Thu Jan 01 00:00:00 1970 +0000
642 642 summary: 1
643 643
644 644 changeset: 19:9627f653b421
645 645 user: test
646 646 date: Thu Jan 01 00:00:00 1970 +0000
647 647 summary: 2
648 648
649 649 changeset: 21:7e61b508e709
650 650 branch: dev
651 651 tag: tip
652 652 user: foo
653 653 date: Thu Jan 01 00:00:00 1970 +0000
654 654 summary: 2
655 655
656 656 $ hg log -r 'destination(2)'
657 657 changeset: 7:ef0ef43d49e7
658 658 parent: 0:68795b066622
659 659 user: foo
660 660 date: Thu Jan 01 00:00:00 1970 +0000
661 661 summary: 2
662 662
663 663 changeset: 13:7a4785234d87
664 664 user: foo
665 665 date: Thu Jan 01 00:00:00 1970 +0000
666 666 summary: 2
667 667
668 668 changeset: 19:9627f653b421
669 669 user: test
670 670 date: Thu Jan 01 00:00:00 1970 +0000
671 671 summary: 2
672 672
673 673 changeset: 21:7e61b508e709
674 674 branch: dev
675 675 tag: tip
676 676 user: foo
677 677 date: Thu Jan 01 00:00:00 1970 +0000
678 678 summary: 2
679 679
680 680 Transplants of grafts can find a destination...
681 681 $ hg log -r 'destination(7)'
682 682 changeset: 21:7e61b508e709
683 683 branch: dev
684 684 tag: tip
685 685 user: foo
686 686 date: Thu Jan 01 00:00:00 1970 +0000
687 687 summary: 2
688 688
689 689 ... grafts of grafts unfortunately can't
690 690 $ hg graft -q 13
691 691 warning: can't find ancestor for 'b' copied from 'a'!
692 692 $ hg log -r 'destination(13)'
693 693 All copies of a cset
694 694 $ hg log -r 'origin(13) or destination(origin(13))'
695 695 changeset: 2:5c095ad7e90f
696 696 user: test
697 697 date: Thu Jan 01 00:00:00 1970 +0000
698 698 summary: 2
699 699
700 700 changeset: 7:ef0ef43d49e7
701 701 parent: 0:68795b066622
702 702 user: foo
703 703 date: Thu Jan 01 00:00:00 1970 +0000
704 704 summary: 2
705 705
706 706 changeset: 13:7a4785234d87
707 707 user: foo
708 708 date: Thu Jan 01 00:00:00 1970 +0000
709 709 summary: 2
710 710
711 711 changeset: 19:9627f653b421
712 712 user: test
713 713 date: Thu Jan 01 00:00:00 1970 +0000
714 714 summary: 2
715 715
716 716 changeset: 21:7e61b508e709
717 717 branch: dev
718 718 user: foo
719 719 date: Thu Jan 01 00:00:00 1970 +0000
720 720 summary: 2
721 721
722 722 changeset: 22:d1cb6591fa4b
723 723 branch: dev
724 724 tag: tip
725 725 user: foo
726 726 date: Thu Jan 01 00:00:00 1970 +0000
727 727 summary: 2
728 728
729 729
730 730 graft works on complex revset
731 731
732 732 $ hg graft 'origin(13) or destination(origin(13))'
733 733 skipping ancestor revision 21:7e61b508e709
734 734 skipping ancestor revision 22:d1cb6591fa4b
735 735 skipping revision 2:5c095ad7e90f (already grafted to 22:d1cb6591fa4b)
736 736 grafting 7:ef0ef43d49e7 "2"
737 737 warning: can't find ancestor for 'b' copied from 'a'!
738 738 grafting 13:7a4785234d87 "2"
739 739 warning: can't find ancestor for 'b' copied from 'a'!
740 740 grafting 19:9627f653b421 "2"
741 741 merging b
742 742 warning: can't find ancestor for 'b' copied from 'a'!
743 743
744 744 graft with --force (still doesn't graft merges)
745 745
746 746 $ hg graft 19 0 6
747 747 skipping ungraftable merge revision 6
748 748 skipping ancestor revision 0:68795b066622
749 749 skipping already grafted revision 19:9627f653b421 (22:d1cb6591fa4b also has origin 2:5c095ad7e90f)
750 750 [255]
751 751 $ hg graft 19 0 6 --force
752 752 skipping ungraftable merge revision 6
753 753 grafting 19:9627f653b421 "2"
754 754 merging b
755 755 warning: can't find ancestor for 'b' copied from 'a'!
756 756 grafting 0:68795b066622 "0"
757 757
758 758 graft --force after backout
759 759
760 760 $ echo abc > a
761 761 $ hg ci -m 28
762 762 $ hg backout 28
763 763 reverting a
764 764 changeset 29:53177ba928f6 backs out changeset 28:50a516bb8b57
765 765 $ hg graft 28
766 766 skipping ancestor revision 28:50a516bb8b57
767 767 [255]
768 768 $ hg graft 28 --force
769 769 grafting 28:50a516bb8b57 "28"
770 770 merging a
771 771 $ cat a
772 772 abc
773 773
774 774 graft --continue after --force
775 775
776 776 $ echo def > a
777 777 $ hg ci -m 31
778 778 $ hg graft 28 --force --tool internal:fail
779 779 grafting 28:50a516bb8b57 "28"
780 780 abort: unresolved conflicts, can't continue
781 781 (use 'hg resolve' and 'hg graft --continue')
782 782 [255]
783 783 $ hg resolve --all
784 784 merging a
785 785 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
786 786 [1]
787 787 $ echo abc > a
788 788 $ hg resolve -m a
789 789 (no more unresolved files)
790 790 continue: hg graft --continue
791 791 $ hg graft -c
792 792 grafting 28:50a516bb8b57 "28"
793 793 $ cat a
794 794 abc
795 795
796 796 Continue testing same origin policy, using revision numbers from test above
797 797 but do some destructive editing of the repo:
798 798
799 799 $ hg up -qC 7
800 800 $ hg tag -l -r 13 tmp
801 801 $ hg --config extensions.strip= strip 2
802 802 saved backup bundle to $TESTTMP/a/.hg/strip-backup/5c095ad7e90f-d323a1e4-backup.hg (glob)
803 803 $ hg graft tmp
804 804 skipping already grafted revision 8:7a4785234d87 (2:ef0ef43d49e7 also has unknown origin 5c095ad7e90f)
805 805 [255]
806 806
807 807 Empty graft
808 808
809 809 $ hg up -qr 26
810 810 $ hg tag -f something
811 811 $ hg graft -qr 27
812 812 $ hg graft -f 27
813 813 grafting 27:ed6c7e54e319 "28"
814 814 note: graft of 27:ed6c7e54e319 created no changes to commit
815 815
816 816 $ cd ..
817 817
818 818 Graft to duplicate a commit
819 819
820 820 $ hg init graftsibling
821 821 $ cd graftsibling
822 822 $ touch a
823 823 $ hg commit -qAm a
824 824 $ touch b
825 825 $ hg commit -qAm b
826 826 $ hg log -G -T '{rev}\n'
827 827 @ 1
828 828 |
829 829 o 0
830 830
831 831 $ hg up -q 0
832 832 $ hg graft -r 1
833 833 grafting 1:0e067c57feba "b" (tip)
834 834 $ hg log -G -T '{rev}\n'
835 835 @ 2
836 836 |
837 837 | o 1
838 838 |/
839 839 o 0
840 840
841 841 Graft to duplicate a commit twice
842 842
843 843 $ hg up -q 0
844 844 $ hg graft -r 2
845 845 grafting 2:044ec77f6389 "b" (tip)
846 846 $ hg log -G -T '{rev}\n'
847 847 @ 3
848 848 |
849 849 | o 2
850 850 |/
851 851 | o 1
852 852 |/
853 853 o 0
854 854
855 855 Graft from behind a move or rename
856 856 ==================================
857 857
858 858 NOTE: This is affected by issue5343, and will need updating when it's fixed
859 859
860 860 Possible cases during a regular graft (when ca is between cta and c2):
861 861
862 862 name | c1<-cta | cta<->ca | ca->c2
863 863 A.0 | | |
864 864 A.1 | X | |
865 865 A.2 | | X |
866 866 A.3 | | | X
867 867 A.4 | X | X |
868 868 A.5 | X | | X
869 869 A.6 | | X | X
870 870 A.7 | X | X | X
871 871
872 872 A.0 is trivial, and doesn't need copy tracking.
873 873 For A.1, a forward rename is recorded in the c1 pass, to be followed later.
874 874 In A.2, the rename is recorded in the c2 pass and followed backwards.
875 875 A.3 is recorded in the c2 pass as a forward rename to be duplicated on target.
876 876 In A.4, both passes of checkcopies record incomplete renames, which are
877 877 then joined in mergecopies to record a rename to be followed.
878 878 In A.5 and A.7, the c1 pass records an incomplete rename, while the c2 pass
879 879 records an incomplete divergence. The incomplete rename is then joined to the
880 880 appropriate side of the incomplete divergence, and the result is recorded as a
881 881 divergence. The code doesn't distinguish at all between these two cases, since
882 882 the end result of them is the same: an incomplete divergence joined with an
883 883 incomplete rename into a divergence.
884 884 Finally, A.6 records a divergence entirely in the c2 pass.
885 885
886 886 A.4 has a degenerate case a<-b<-a->a, where checkcopies isn't needed at all.
887 887 A.5 has a special case a<-b<-b->a, which is treated like a<-b->a in a merge.
888 888 A.6 has a special case a<-a<-b->a. Here, checkcopies will find a spurious
889 889 incomplete divergence, which is in fact complete. This is handled later in
890 890 mergecopies.
891 891 A.7 has 4 special cases: a<-b<-a->b (the "ping-pong" case), a<-b<-c->b,
892 892 a<-b<-a->c and a<-b<-c->a. Of these, only the "ping-pong" case is interesting,
893 893 the others are fairly trivial (a<-b<-c->b and a<-b<-a->c proceed like the base
894 894 case, a<-b<-c->a is treated the same as a<-b<-b->a).
895 895
896 896 f5a therefore tests the "ping-pong" rename case, where a file is renamed to the
897 897 same name on both branches, then the rename is backed out on one branch, and
898 898 the backout is grafted to the other branch. This creates a challenging rename
899 899 sequence of a<-b<-a->b in the graft target, topological CA, graft CA and graft
900 900 source, respectively. Since rename detection will run on the c1 side for such a
901 901 sequence (as for technical reasons, we split the c1 and c2 sides not at the
902 902 graft CA, but rather at the topological CA), it will pick up a false rename,
903 903 and cause a spurious merge conflict. This false rename is always exactly the
904 904 reverse of the true rename that would be detected on the c2 side, so we can
905 905 correct for it by detecting this condition and reversing as necessary.
906 906
907 907 First, set up the repository with commits to be grafted
908 908
909 909 $ hg init ../graftmove
910 910 $ cd ../graftmove
911 911 $ echo c1a > f1a
912 912 $ echo c2a > f2a
913 913 $ echo c3a > f3a
914 914 $ echo c4a > f4a
915 915 $ echo c5a > f5a
916 916 $ hg ci -qAm A0
917 917 $ hg mv f1a f1b
918 918 $ hg mv f3a f3b
919 919 $ hg mv f5a f5b
920 920 $ hg ci -qAm B0
921 921 $ echo c1c > f1b
922 922 $ hg mv f2a f2c
923 923 $ hg mv f5b f5a
924 924 $ echo c5c > f5a
925 925 $ hg ci -qAm C0
926 926 $ hg mv f3b f3d
927 927 $ echo c4d > f4a
928 928 $ hg ci -qAm D0
929 929 $ hg log -G
930 930 @ changeset: 3:b69f5839d2d9
931 931 | tag: tip
932 932 | user: test
933 933 | date: Thu Jan 01 00:00:00 1970 +0000
934 934 | summary: D0
935 935 |
936 936 o changeset: 2:f58c7e2b28fa
937 937 | user: test
938 938 | date: Thu Jan 01 00:00:00 1970 +0000
939 939 | summary: C0
940 940 |
941 941 o changeset: 1:3d7bba921b5d
942 942 | user: test
943 943 | date: Thu Jan 01 00:00:00 1970 +0000
944 944 | summary: B0
945 945 |
946 946 o changeset: 0:11f7a1b56675
947 947 user: test
948 948 date: Thu Jan 01 00:00:00 1970 +0000
949 949 summary: A0
950 950
951 951
952 952 Test the cases A.2 (f1x), A.3 (f2x) and a special case of A.6 (f5x) where the
953 953 two renames actually converge to the same name (thus no actual divergence).
954 954
955 955 $ hg up -q 'desc("A0")'
956 956 $ HGEDITOR="echo C1 >" hg graft -r 'desc("C0")' --edit
957 957 grafting 2:f58c7e2b28fa "C0"
958 958 merging f1a and f1b to f1a
959 959 merging f5a
960 960 warning: conflicts while merging f5a! (edit, then use 'hg resolve --mark')
961 961 abort: unresolved conflicts, can't continue
962 962 (use 'hg resolve' and 'hg graft --continue')
963 963 [255]
964 964 $ hg resolve f5a -t ':other' # XXX work around failure
965 965 (no more unresolved files)
966 966 continue: hg graft --continue
967 967 $ hg graft --continue # XXX work around failure
968 968 grafting 2:f58c7e2b28fa "C0"
969 969 warning: can't find ancestor for 'f5a' copied from 'f5b'!
970 970 $ hg status --change .
971 971 M f1a
972 972 M f5a
973 973 A f2c
974 974 R f2a
975 975 $ hg cat f1a
976 976 c1c
977 977 $ hg cat f1b
978 978 f1b: no such file in rev 43e4b415492d
979 979 [1]
980 980
981 981 Test the cases A.0 (f4x) and A.6 (f3x)
982 982
983 983 $ HGEDITOR="echo D1 >" hg graft -r 'desc("D0")' --edit
984 984 grafting 3:b69f5839d2d9 "D0"
985 note: possible conflict - f3b was renamed multiple times to:
986 f3d
987 f3a
985 988 warning: can't find ancestor for 'f3d' copied from 'f3b'!
986 989
987 990 Set up the repository for some further tests
988 991
989 992 $ hg up -q "min(desc("A0"))"
990 993 $ hg mv f1a f1e
991 994 $ echo c2e > f2a
992 995 $ hg mv f3a f3e
993 996 $ hg mv f4a f4e
994 997 $ hg mv f5a f5b
995 998 $ hg ci -qAm "E0"
996 999 $ hg log -G
997 1000 @ changeset: 6:ebba59d1fb02
998 1001 | tag: tip
999 1002 | parent: 0:11f7a1b56675
1000 1003 | user: test
1001 1004 | date: Thu Jan 01 00:00:00 1970 +0000
1002 1005 | summary: E0
1003 1006 |
1004 1007 | o changeset: 5:4f4ba7a6e606
1005 1008 | | user: test
1006 1009 | | date: Thu Jan 01 00:00:00 1970 +0000
1007 1010 | | summary: D1
1008 1011 | |
1009 1012 | o changeset: 4:43e4b415492d
1010 1013 |/ parent: 0:11f7a1b56675
1011 1014 | user: test
1012 1015 | date: Thu Jan 01 00:00:00 1970 +0000
1013 1016 | summary: C0
1014 1017 |
1015 1018 | o changeset: 3:b69f5839d2d9
1016 1019 | | user: test
1017 1020 | | date: Thu Jan 01 00:00:00 1970 +0000
1018 1021 | | summary: D0
1019 1022 | |
1020 1023 | o changeset: 2:f58c7e2b28fa
1021 1024 | | user: test
1022 1025 | | date: Thu Jan 01 00:00:00 1970 +0000
1023 1026 | | summary: C0
1024 1027 | |
1025 1028 | o changeset: 1:3d7bba921b5d
1026 1029 |/ user: test
1027 1030 | date: Thu Jan 01 00:00:00 1970 +0000
1028 1031 | summary: B0
1029 1032 |
1030 1033 o changeset: 0:11f7a1b56675
1031 1034 user: test
1032 1035 date: Thu Jan 01 00:00:00 1970 +0000
1033 1036 summary: A0
1034 1037
1035 1038
1036 1039 Test the cases A.4 (f1x), the "ping-pong" special case of A.7 (f5x),
1037 1040 and A.3 with a local content change to be preserved (f2x).
1038 1041
1039 1042 $ HGEDITOR="echo C2 >" hg graft -r 'desc("C0")' --edit
1040 1043 grafting 2:f58c7e2b28fa "C0"
1041 1044 other [graft] changed f1b which local [local] deleted
1042 1045 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u
1043 1046 merging f2a and f2c to f2c
1044 1047 merging f5b and f5a to f5a
1045 1048 abort: unresolved conflicts, can't continue
1046 1049 (use 'hg resolve' and 'hg graft --continue')
1047 1050 [255]
1048 1051 $ hg resolve f1b -t ':other' # XXX work around failure
1049 1052 (no more unresolved files)
1050 1053 continue: hg graft --continue
1051 1054 $ hg graft --continue # XXX work around failure
1052 1055 grafting 2:f58c7e2b28fa "C0"
1053 1056 grafting 4:43e4b415492d "C0"
1054 1057 merging f1e and f1a to f1e
1055 1058 merging f2c
1056 1059 warning: can't find ancestor for 'f2c' copied from 'f2a'!
1057 1060
1058 1061 Test the cases A.1 (f4x) and A.7 (f3x).
1059 1062
1060 1063 $ HGEDITOR="echo D2 >" hg graft -r 'desc("D0")' --edit
1061 1064 grafting 3:b69f5839d2d9 "D0"
1062 1065 merging f4e and f4a to f4e
1063 1066 warning: can't find ancestor for 'f3d' copied from 'f3b'!
1064 1067
1065 1068 Check the results of the grafts tested
1066 1069
1067 1070 $ hg log -CGv --patch --git
1068 1071 @ changeset: 9:100f4d78e056
1069 1072 | tag: tip
1070 1073 | user: test
1071 1074 | date: Thu Jan 01 00:00:00 1970 +0000
1072 1075 | files: f3d f4e
1073 1076 | description:
1074 1077 | D2
1075 1078 |
1076 1079 |
1077 1080 | diff --git a/f3d b/f3d
1078 1081 | new file mode 100644
1079 1082 | --- /dev/null
1080 1083 | +++ b/f3d
1081 1084 | @@ -0,0 +1,1 @@
1082 1085 | +c3a
1083 1086 | diff --git a/f4e b/f4e
1084 1087 | --- a/f4e
1085 1088 | +++ b/f4e
1086 1089 | @@ -1,1 +1,1 @@
1087 1090 | -c4a
1088 1091 | +c4d
1089 1092 |
1090 1093 o changeset: 8:84915a7da133
1091 1094 | user: test
1092 1095 | date: Thu Jan 01 00:00:00 1970 +0000
1093 1096 | files: f1e f5a.orig
1094 1097 | description:
1095 1098 | C0
1096 1099 |
1097 1100 |
1098 1101 | diff --git a/f1e b/f1e
1099 1102 | --- a/f1e
1100 1103 | +++ b/f1e
1101 1104 | @@ -1,1 +1,1 @@
1102 1105 | -c1a
1103 1106 | +c1c
1104 1107 | diff --git a/f5a.orig b/f5a.orig
1105 1108 | deleted file mode 100644
1106 1109 | --- a/f5a.orig
1107 1110 | +++ /dev/null
1108 1111 | @@ -1,5 +0,0 @@
1109 1112 | -<<<<<<< local: 11f7a1b56675 - test: A0
1110 1113 | -c5a
1111 1114 | -=======
1112 1115 | -c5c
1113 1116 | ->>>>>>> graft: f58c7e2b28fa - test: C0
1114 1117 |
1115 1118 o changeset: 7:dc778749ee9a
1116 1119 | user: test
1117 1120 | date: Thu Jan 01 00:00:00 1970 +0000
1118 1121 | files: f1b f2a f2c f5a f5b
1119 1122 | copies: f2c (f2a) f5a (f5b)
1120 1123 | description:
1121 1124 | C0
1122 1125 |
1123 1126 |
1124 1127 | diff --git a/f1b b/f1b
1125 1128 | new file mode 100644
1126 1129 | --- /dev/null
1127 1130 | +++ b/f1b
1128 1131 | @@ -0,0 +1,1 @@
1129 1132 | +c1c
1130 1133 | diff --git a/f2a b/f2c
1131 1134 | rename from f2a
1132 1135 | rename to f2c
1133 1136 | diff --git a/f5b b/f5a
1134 1137 | rename from f5b
1135 1138 | rename to f5a
1136 1139 | --- a/f5b
1137 1140 | +++ b/f5a
1138 1141 | @@ -1,1 +1,1 @@
1139 1142 | -c5a
1140 1143 | +c5c
1141 1144 |
1142 1145 o changeset: 6:ebba59d1fb02
1143 1146 | parent: 0:11f7a1b56675
1144 1147 | user: test
1145 1148 | date: Thu Jan 01 00:00:00 1970 +0000
1146 1149 | files: f1a f1e f2a f3a f3e f4a f4e f5a f5a.orig f5b
1147 1150 | copies: f1e (f1a) f3e (f3a) f4e (f4a) f5b (f5a)
1148 1151 | description:
1149 1152 | E0
1150 1153 |
1151 1154 |
1152 1155 | diff --git a/f1a b/f1e
1153 1156 | rename from f1a
1154 1157 | rename to f1e
1155 1158 | diff --git a/f2a b/f2a
1156 1159 | --- a/f2a
1157 1160 | +++ b/f2a
1158 1161 | @@ -1,1 +1,1 @@
1159 1162 | -c2a
1160 1163 | +c2e
1161 1164 | diff --git a/f3a b/f3e
1162 1165 | rename from f3a
1163 1166 | rename to f3e
1164 1167 | diff --git a/f4a b/f4e
1165 1168 | rename from f4a
1166 1169 | rename to f4e
1167 1170 | diff --git a/f5a.orig b/f5a.orig
1168 1171 | new file mode 100644
1169 1172 | --- /dev/null
1170 1173 | +++ b/f5a.orig
1171 1174 | @@ -0,0 +1,5 @@
1172 1175 | +<<<<<<< local: 11f7a1b56675 - test: A0
1173 1176 | +c5a
1174 1177 | +=======
1175 1178 | +c5c
1176 1179 | +>>>>>>> graft: f58c7e2b28fa - test: C0
1177 1180 | diff --git a/f5a b/f5b
1178 1181 | rename from f5a
1179 1182 | rename to f5b
1180 1183 |
1181 1184 | o changeset: 5:4f4ba7a6e606
1182 1185 | | user: test
1183 1186 | | date: Thu Jan 01 00:00:00 1970 +0000
1184 1187 | | files: f3d f4a
1185 1188 | | description:
1186 1189 | | D1
1187 1190 | |
1188 1191 | |
1189 1192 | | diff --git a/f3d b/f3d
1190 1193 | | new file mode 100644
1191 1194 | | --- /dev/null
1192 1195 | | +++ b/f3d
1193 1196 | | @@ -0,0 +1,1 @@
1194 1197 | | +c3a
1195 1198 | | diff --git a/f4a b/f4a
1196 1199 | | --- a/f4a
1197 1200 | | +++ b/f4a
1198 1201 | | @@ -1,1 +1,1 @@
1199 1202 | | -c4a
1200 1203 | | +c4d
1201 1204 | |
1202 1205 | o changeset: 4:43e4b415492d
1203 1206 |/ parent: 0:11f7a1b56675
1204 1207 | user: test
1205 1208 | date: Thu Jan 01 00:00:00 1970 +0000
1206 1209 | files: f1a f2a f2c f5a
1207 1210 | copies: f2c (f2a)
1208 1211 | description:
1209 1212 | C0
1210 1213 |
1211 1214 |
1212 1215 | diff --git a/f1a b/f1a
1213 1216 | --- a/f1a
1214 1217 | +++ b/f1a
1215 1218 | @@ -1,1 +1,1 @@
1216 1219 | -c1a
1217 1220 | +c1c
1218 1221 | diff --git a/f2a b/f2c
1219 1222 | rename from f2a
1220 1223 | rename to f2c
1221 1224 | diff --git a/f5a b/f5a
1222 1225 | --- a/f5a
1223 1226 | +++ b/f5a
1224 1227 | @@ -1,1 +1,1 @@
1225 1228 | -c5a
1226 1229 | +c5c
1227 1230 |
1228 1231 | o changeset: 3:b69f5839d2d9
1229 1232 | | user: test
1230 1233 | | date: Thu Jan 01 00:00:00 1970 +0000
1231 1234 | | files: f3b f3d f4a
1232 1235 | | copies: f3d (f3b)
1233 1236 | | description:
1234 1237 | | D0
1235 1238 | |
1236 1239 | |
1237 1240 | | diff --git a/f3b b/f3d
1238 1241 | | rename from f3b
1239 1242 | | rename to f3d
1240 1243 | | diff --git a/f4a b/f4a
1241 1244 | | --- a/f4a
1242 1245 | | +++ b/f4a
1243 1246 | | @@ -1,1 +1,1 @@
1244 1247 | | -c4a
1245 1248 | | +c4d
1246 1249 | |
1247 1250 | o changeset: 2:f58c7e2b28fa
1248 1251 | | user: test
1249 1252 | | date: Thu Jan 01 00:00:00 1970 +0000
1250 1253 | | files: f1b f2a f2c f5a f5b
1251 1254 | | copies: f2c (f2a) f5a (f5b)
1252 1255 | | description:
1253 1256 | | C0
1254 1257 | |
1255 1258 | |
1256 1259 | | diff --git a/f1b b/f1b
1257 1260 | | --- a/f1b
1258 1261 | | +++ b/f1b
1259 1262 | | @@ -1,1 +1,1 @@
1260 1263 | | -c1a
1261 1264 | | +c1c
1262 1265 | | diff --git a/f2a b/f2c
1263 1266 | | rename from f2a
1264 1267 | | rename to f2c
1265 1268 | | diff --git a/f5b b/f5a
1266 1269 | | rename from f5b
1267 1270 | | rename to f5a
1268 1271 | | --- a/f5b
1269 1272 | | +++ b/f5a
1270 1273 | | @@ -1,1 +1,1 @@
1271 1274 | | -c5a
1272 1275 | | +c5c
1273 1276 | |
1274 1277 | o changeset: 1:3d7bba921b5d
1275 1278 |/ user: test
1276 1279 | date: Thu Jan 01 00:00:00 1970 +0000
1277 1280 | files: f1a f1b f3a f3b f5a f5b
1278 1281 | copies: f1b (f1a) f3b (f3a) f5b (f5a)
1279 1282 | description:
1280 1283 | B0
1281 1284 |
1282 1285 |
1283 1286 | diff --git a/f1a b/f1b
1284 1287 | rename from f1a
1285 1288 | rename to f1b
1286 1289 | diff --git a/f3a b/f3b
1287 1290 | rename from f3a
1288 1291 | rename to f3b
1289 1292 | diff --git a/f5a b/f5b
1290 1293 | rename from f5a
1291 1294 | rename to f5b
1292 1295 |
1293 1296 o changeset: 0:11f7a1b56675
1294 1297 user: test
1295 1298 date: Thu Jan 01 00:00:00 1970 +0000
1296 1299 files: f1a f2a f3a f4a f5a
1297 1300 description:
1298 1301 A0
1299 1302
1300 1303
1301 1304 diff --git a/f1a b/f1a
1302 1305 new file mode 100644
1303 1306 --- /dev/null
1304 1307 +++ b/f1a
1305 1308 @@ -0,0 +1,1 @@
1306 1309 +c1a
1307 1310 diff --git a/f2a b/f2a
1308 1311 new file mode 100644
1309 1312 --- /dev/null
1310 1313 +++ b/f2a
1311 1314 @@ -0,0 +1,1 @@
1312 1315 +c2a
1313 1316 diff --git a/f3a b/f3a
1314 1317 new file mode 100644
1315 1318 --- /dev/null
1316 1319 +++ b/f3a
1317 1320 @@ -0,0 +1,1 @@
1318 1321 +c3a
1319 1322 diff --git a/f4a b/f4a
1320 1323 new file mode 100644
1321 1324 --- /dev/null
1322 1325 +++ b/f4a
1323 1326 @@ -0,0 +1,1 @@
1324 1327 +c4a
1325 1328 diff --git a/f5a b/f5a
1326 1329 new file mode 100644
1327 1330 --- /dev/null
1328 1331 +++ b/f5a
1329 1332 @@ -0,0 +1,1 @@
1330 1333 +c5a
1331 1334
1332 1335 $ hg cat f2c
1333 1336 c2e
General Comments 0
You need to be logged in to leave comments. Login now