##// END OF EJS Templates
copies: make _checkcopies handle copy sequences spanning the TCA (issue4028)...
Gábor Stefanik -
r30204:1894c830 default
parent child Browse files
Show More
@@ -1,693 +1,703
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 _combinecopies(copyfrom, copyto, finalcopy, diverge, incompletediverge):
293 293 """combine partial copy paths"""
294 294 remainder = {}
295 295 for f in copyfrom:
296 296 if f in copyto:
297 297 finalcopy[copyto[f]] = copyfrom[f]
298 298 del copyto[f]
299 299 for f in incompletediverge:
300 300 assert f not in diverge
301 301 ic = incompletediverge[f]
302 302 if ic[0] in copyto:
303 303 diverge[f] = [copyto[ic[0]], ic[1]]
304 304 else:
305 305 remainder[f] = ic
306 306 return remainder
307 307
308 308 def mergecopies(repo, c1, c2, base):
309 309 """
310 310 Find moves and copies between context c1 and c2 that are relevant
311 311 for merging. 'base' will be used as the merge base.
312 312
313 313 Returns four dicts: "copy", "movewithdir", "diverge", and
314 314 "renamedelete".
315 315
316 316 "copy" is a mapping from destination name -> source name,
317 317 where source is in c1 and destination is in c2 or vice-versa.
318 318
319 319 "movewithdir" is a mapping from source name -> destination name,
320 320 where the file at source present in one context but not the other
321 321 needs to be moved to destination by the merge process, because the
322 322 other context moved the directory it is in.
323 323
324 324 "diverge" is a mapping of source name -> list of destination names
325 325 for divergent renames.
326 326
327 327 "renamedelete" is a mapping of source name -> list of destination
328 328 names for files deleted in c1 that were renamed in c2 or vice-versa.
329 329 """
330 330 # avoid silly behavior for update from empty dir
331 331 if not c1 or not c2 or c1 == c2:
332 332 return {}, {}, {}, {}
333 333
334 334 # avoid silly behavior for parent -> working dir
335 335 if c2.node() is None and c1.node() == repo.dirstate.p1():
336 336 return repo.dirstate.copies(), {}, {}, {}
337 337
338 338 # Copy trace disabling is explicitly below the node == p1 logic above
339 339 # because the logic above is required for a simple copy to be kept across a
340 340 # rebase.
341 341 if repo.ui.configbool('experimental', 'disablecopytrace'):
342 342 return {}, {}, {}, {}
343 343
344 344 # In certain scenarios (e.g. graft, update or rebase), base can be
345 345 # overridden We still need to know a real common ancestor in this case We
346 346 # can't just compute _c1.ancestor(_c2) and compare it to ca, because there
347 347 # can be multiple common ancestors, e.g. in case of bidmerge. Because our
348 348 # caller may not know if the revision passed in lieu of the CA is a genuine
349 349 # common ancestor or not without explicitly checking it, it's better to
350 350 # determine that here.
351 351 #
352 352 # base.descendant(wc) and base.descendant(base) are False, work around that
353 353 _c1 = c1.p1() if c1.rev() is None else c1
354 354 _c2 = c2.p1() if c2.rev() is None else c2
355 355 # an endpoint is "dirty" if it isn't a descendant of the merge base
356 356 # if we have a dirty endpoint, we need to trigger graft logic, and also
357 357 # keep track of which endpoint is dirty
358 358 dirtyc1 = not (base == _c1 or base.descendant(_c1))
359 359 dirtyc2 = not (base== _c2 or base.descendant(_c2))
360 360 graft = dirtyc1 or dirtyc2
361 361 tca = base
362 362 if graft:
363 363 tca = _c1.ancestor(_c2)
364 364
365 365 limit = _findlimit(repo, c1.rev(), c2.rev())
366 366 if limit is None:
367 367 # no common ancestor, no copies
368 368 return {}, {}, {}, {}
369 369 repo.ui.debug(" searching for copies back to rev %d\n" % limit)
370 370
371 371 m1 = c1.manifest()
372 372 m2 = c2.manifest()
373 373 mb = base.manifest()
374 374
375 375 # gather data from _checkcopies:
376 376 # - diverge = record all diverges in this dict
377 377 # - copy = record all non-divergent copies in this dict
378 378 # - fullcopy = record all copies in this dict
379 379 # - incomplete = record non-divergent partial copies here
380 380 # - incompletediverge = record divergent partial copies here
381 381 diverge = {} # divergence data is shared
382 382 incompletediverge = {}
383 383 data1 = {'copy': {},
384 384 'fullcopy': {},
385 385 'incomplete': {},
386 386 'diverge': diverge,
387 387 'incompletediverge': incompletediverge,
388 388 }
389 389 data2 = {'copy': {},
390 390 'fullcopy': {},
391 391 'incomplete': {},
392 392 'diverge': diverge,
393 393 'incompletediverge': incompletediverge,
394 394 }
395 395
396 396 # find interesting file sets from manifests
397 397 addedinm1 = m1.filesnotin(mb)
398 398 addedinm2 = m2.filesnotin(mb)
399 399 bothnew = sorted(addedinm1 & addedinm2)
400 400 if tca == base:
401 401 # unmatched file from base
402 402 u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2)
403 403 u1u, u2u = u1r, u2r
404 404 else:
405 405 # unmatched file from base (DAG rotation in the graft case)
406 406 u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2,
407 407 baselabel='base')
408 408 # unmatched file from topological common ancestors (no DAG rotation)
409 409 # need to recompute this for directory move handling when grafting
410 410 mta = tca.manifest()
411 411 u1u, u2u = _computenonoverlap(repo, c1, c2, m1.filesnotin(mta),
412 412 m2.filesnotin(mta),
413 413 baselabel='topological common ancestor')
414 414
415 415 for f in u1u:
416 416 _checkcopies(c1, f, m1, m2, base, tca, dirtyc1, limit, data1)
417 417
418 418 for f in u2u:
419 419 _checkcopies(c2, f, m2, m1, base, tca, dirtyc2, limit, data2)
420 420
421 421 copy = dict(data1['copy'].items() + data2['copy'].items())
422 422 fullcopy = dict(data1['fullcopy'].items() + data2['fullcopy'].items())
423 423
424 424 if dirtyc1:
425 425 _combinecopies(data2['incomplete'], data1['incomplete'], copy, diverge,
426 426 incompletediverge)
427 427 else:
428 428 _combinecopies(data1['incomplete'], data2['incomplete'], copy, diverge,
429 429 incompletediverge)
430 430
431 431 renamedelete = {}
432 432 renamedeleteset = set()
433 433 divergeset = set()
434 434 for of, fl in diverge.items():
435 435 if len(fl) == 1 or of in c1 or of in c2:
436 436 del diverge[of] # not actually divergent, or not a rename
437 437 if of not in c1 and of not in c2:
438 438 # renamed on one side, deleted on the other side, but filter
439 439 # out files that have been renamed and then deleted
440 440 renamedelete[of] = [f for f in fl if f in c1 or f in c2]
441 441 renamedeleteset.update(fl) # reverse map for below
442 442 else:
443 443 divergeset.update(fl) # reverse map for below
444 444
445 445 if bothnew:
446 446 repo.ui.debug(" unmatched files new in both:\n %s\n"
447 447 % "\n ".join(bothnew))
448 448 bothdiverge = {}
449 449 bothincompletediverge = {}
450 450 both1 = {'copy': {},
451 451 'fullcopy': {},
452 452 'incomplete': {},
453 453 'diverge': bothdiverge,
454 454 'incompletediverge': bothincompletediverge
455 455 }
456 456 both2 = {'copy': {},
457 457 'fullcopy': {},
458 458 'incomplete': {},
459 459 'diverge': bothdiverge,
460 460 'incompletediverge': bothincompletediverge
461 461 }
462 462 for f in bothnew:
463 463 _checkcopies(c1, f, m1, m2, base, tca, dirtyc1, limit, both1)
464 464 _checkcopies(c2, f, m2, m1, base, tca, dirtyc2, limit, both2)
465 465 if dirtyc1:
466 466 assert both2['incomplete'] == {}
467 467 remainder = _combinecopies({}, both1['incomplete'], copy, bothdiverge,
468 468 bothincompletediverge)
469 469 else:
470 470 assert both1['incomplete'] == {}
471 471 remainder = _combinecopies({}, both2['incomplete'], copy, bothdiverge,
472 472 bothincompletediverge)
473 473 for f in remainder:
474 474 assert f not in bothdiverge
475 475 ic = remainder[f]
476 476 if ic[0] in (m1 if dirtyc1 else m2):
477 477 # backed-out rename on one side, but watch out for deleted files
478 478 bothdiverge[f] = ic
479 479 for of, fl in bothdiverge.items():
480 480 if len(fl) == 2 and fl[0] == fl[1]:
481 481 copy[fl[0]] = of # not actually divergent, just matching renames
482 482
483 483 if fullcopy and repo.ui.debugflag:
484 484 repo.ui.debug(" all copies found (* = to merge, ! = divergent, "
485 485 "% = renamed and deleted):\n")
486 486 for f in sorted(fullcopy):
487 487 note = ""
488 488 if f in copy:
489 489 note += "*"
490 490 if f in divergeset:
491 491 note += "!"
492 492 if f in renamedeleteset:
493 493 note += "%"
494 494 repo.ui.debug(" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f,
495 495 note))
496 496 del divergeset
497 497
498 498 if not fullcopy:
499 499 return copy, {}, diverge, renamedelete
500 500
501 501 repo.ui.debug(" checking for directory renames\n")
502 502
503 503 # generate a directory move map
504 504 d1, d2 = c1.dirs(), c2.dirs()
505 505 # Hack for adding '', which is not otherwise added, to d1 and d2
506 506 d1.addpath('/')
507 507 d2.addpath('/')
508 508 invalid = set()
509 509 dirmove = {}
510 510
511 511 # examine each file copy for a potential directory move, which is
512 512 # when all the files in a directory are moved to a new directory
513 513 for dst, src in fullcopy.iteritems():
514 514 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
515 515 if dsrc in invalid:
516 516 # already seen to be uninteresting
517 517 continue
518 518 elif dsrc in d1 and ddst in d1:
519 519 # directory wasn't entirely moved locally
520 520 invalid.add(dsrc + "/")
521 521 elif dsrc in d2 and ddst in d2:
522 522 # directory wasn't entirely moved remotely
523 523 invalid.add(dsrc + "/")
524 524 elif dsrc + "/" in dirmove and dirmove[dsrc + "/"] != ddst + "/":
525 525 # files from the same directory moved to two different places
526 526 invalid.add(dsrc + "/")
527 527 else:
528 528 # looks good so far
529 529 dirmove[dsrc + "/"] = ddst + "/"
530 530
531 531 for i in invalid:
532 532 if i in dirmove:
533 533 del dirmove[i]
534 534 del d1, d2, invalid
535 535
536 536 if not dirmove:
537 537 return copy, {}, diverge, renamedelete
538 538
539 539 for d in dirmove:
540 540 repo.ui.debug(" discovered dir src: '%s' -> dst: '%s'\n" %
541 541 (d, dirmove[d]))
542 542
543 543 movewithdir = {}
544 544 # check unaccounted nonoverlapping files against directory moves
545 545 for f in u1r + u2r:
546 546 if f not in fullcopy:
547 547 for d in dirmove:
548 548 if f.startswith(d):
549 549 # new file added in a directory that was moved, move it
550 550 df = dirmove[d] + f[len(d):]
551 551 if df not in copy:
552 552 movewithdir[f] = df
553 553 repo.ui.debug((" pending file src: '%s' -> "
554 554 "dst: '%s'\n") % (f, df))
555 555 break
556 556
557 557 return copy, movewithdir, diverge, renamedelete
558 558
559 559 def _related(f1, f2, limit):
560 560 """return True if f1 and f2 filectx have a common ancestor
561 561
562 562 Walk back to common ancestor to see if the two files originate
563 563 from the same file. Since workingfilectx's rev() is None it messes
564 564 up the integer comparison logic, hence the pre-step check for
565 565 None (f1 and f2 can only be workingfilectx's initially).
566 566 """
567 567
568 568 if f1 == f2:
569 569 return f1 # a match
570 570
571 571 g1, g2 = f1.ancestors(), f2.ancestors()
572 572 try:
573 573 f1r, f2r = f1.linkrev(), f2.linkrev()
574 574
575 575 if f1r is None:
576 576 f1 = next(g1)
577 577 if f2r is None:
578 578 f2 = next(g2)
579 579
580 580 while True:
581 581 f1r, f2r = f1.linkrev(), f2.linkrev()
582 582 if f1r > f2r:
583 583 f1 = next(g1)
584 584 elif f2r > f1r:
585 585 f2 = next(g2)
586 586 elif f1 == f2:
587 587 return f1 # a match
588 588 elif f1r == f2r or f1r < limit or f2r < limit:
589 589 return False # copy no longer relevant
590 590 except StopIteration:
591 591 return False
592 592
593 593 def _checkcopies(ctx, f, m1, m2, base, tca, remotebase, limit, data):
594 594 """
595 595 check possible copies of f from m1 to m2
596 596
597 597 ctx = starting context for f in m1
598 598 f = the filename to check (as in m1)
599 599 m1 = the source manifest
600 600 m2 = the destination manifest
601 601 base = the changectx used as a merge base
602 602 tca = topological common ancestor for graft-like scenarios
603 603 remotebase = True if base is outside tca::ctx, False otherwise
604 604 limit = the rev number to not search beyond
605 605 data = dictionary of dictionary to store copy data. (see mergecopies)
606 606
607 607 note: limit is only an optimization, and there is no guarantee that
608 608 irrelevant revisions will not be limited
609 609 there is no easy way to make this algorithm stop in a guaranteed way
610 610 once it "goes behind a certain revision".
611 611 """
612 612
613 613 mb = base.manifest()
614 mta = tca.manifest()
614 615 # Might be true if this call is about finding backward renames,
615 616 # This happens in the case of grafts because the DAG is then rotated.
616 617 # If the file exists in both the base and the source, we are not looking
617 618 # for a rename on the source side, but on the part of the DAG that is
618 619 # traversed backwards.
619 620 #
620 621 # In the case there is both backward and forward renames (before and after
621 622 # the base) this is more complicated as we must detect a divergence.
622 623 # We use 'backwards = False' in that case.
623 624 backwards = not remotebase and base != tca and f in mb
624 625 getfctx = _makegetfctx(ctx)
625 626
626 627 of = None
627 628 seen = set([f])
628 629 for oc in getfctx(f, m1[f]).ancestors():
629 630 ocr = oc.linkrev()
630 631 of = oc.path()
631 632 if of in seen:
632 633 # check limit late - grab last rename before
633 634 if ocr < limit:
634 635 break
635 636 continue
636 637 seen.add(of)
637 638
638 639 # remember for dir rename detection
639 640 if backwards:
640 641 data['fullcopy'][of] = f # grafting backwards through renames
641 642 else:
642 643 data['fullcopy'][f] = of
643 644 if of not in m2:
644 645 continue # no match, keep looking
645 646 if m2[of] == mb.get(of):
646 647 return # no merge needed, quit early
647 648 c2 = getfctx(of, m2[of])
648 649 # c2 might be a plain new file on added on destination side that is
649 650 # unrelated to the droids we are looking for.
650 651 cr = _related(oc, c2, tca.rev())
651 652 if cr and (of == f or of == c2.path()): # non-divergent
652 653 if backwards:
653 654 data['copy'][of] = f
654 655 elif of in mb:
655 656 data['copy'][f] = of
656 657 elif remotebase: # special case: a <- b <- a -> b "ping-pong" rename
657 658 data['copy'][of] = f
658 659 del data['fullcopy'][f]
659 660 data['fullcopy'][of] = f
660 661 else: # divergence w.r.t. graft CA on one side of topological CA
661 662 for sf in seen:
662 663 if sf in mb:
663 664 assert sf not in data['diverge']
664 665 data['diverge'][sf] = [f, of]
665 666 break
666 667 return
667 668
668 if of in mb:
669 data['diverge'].setdefault(of, []).append(f)
669 if of in mta:
670 if backwards or remotebase:
671 data['incomplete'][of] = f
672 else:
673 for sf in seen:
674 if sf in mb:
675 if tca == base:
676 data['diverge'].setdefault(sf, []).append(f)
677 else:
678 data['incompletediverge'][sf] = [of, f]
679 return
670 680
671 681 def duplicatecopies(repo, rev, fromrev, skiprev=None):
672 682 '''reproduce copies from fromrev to rev in the dirstate
673 683
674 684 If skiprev is specified, it's a revision that should be used to
675 685 filter copy records. Any copies that occur between fromrev and
676 686 skiprev will not be duplicated, even if they appear in the set of
677 687 copies between fromrev and rev.
678 688 '''
679 689 exclude = {}
680 690 if (skiprev is not None and
681 691 not repo.ui.configbool('experimental', 'disablecopytrace')):
682 692 # disablecopytrace skips this line, but not the entire function because
683 693 # the line below is O(size of the repo) during a rebase, while the rest
684 694 # of the function is much faster (and is required for carrying copy
685 695 # metadata across the rebase anyway).
686 696 exclude = pathcopies(repo[fromrev], repo[skiprev])
687 697 for dst, src in pathcopies(repo[fromrev], repo[rev]).iteritems():
688 698 # copies.pathcopies returns backward renames, so dst might not
689 699 # actually be in the dirstate
690 700 if dst in exclude:
691 701 continue
692 702 if repo.dirstate[dst] in "nma":
693 703 repo.dirstate.copy(src, dst)
@@ -1,1336 +1,1282
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 warning: conflicts while merging f5a! (edit, then use 'hg resolve --mark')
961 abort: unresolved conflicts, can't continue
962 (use 'hg resolve' and 'hg graft --continue')
963 [255]
964 $ hg resolve f5a -t ':other' # XXX work around failure
965 (no more unresolved files)
966 continue: hg graft --continue
967 $ hg graft --continue # XXX work around failure
968 grafting 2:f58c7e2b28fa "C0"
969 960 warning: can't find ancestor for 'f5a' copied from 'f5b'!
970 961 $ hg status --change .
971 962 M f1a
972 963 M f5a
973 964 A f2c
974 965 R f2a
975 966 $ hg cat f1a
976 967 c1c
977 968 $ hg cat f1b
978 f1b: no such file in rev 43e4b415492d
969 f1b: no such file in rev c9763722f9bd
979 970 [1]
980 971
981 972 Test the cases A.0 (f4x) and A.6 (f3x)
982 973
983 974 $ HGEDITOR="echo D1 >" hg graft -r 'desc("D0")' --edit
984 975 grafting 3:b69f5839d2d9 "D0"
985 976 note: possible conflict - f3b was renamed multiple times to:
986 977 f3d
987 978 f3a
988 979 warning: can't find ancestor for 'f3d' copied from 'f3b'!
989 980
990 981 Set up the repository for some further tests
991 982
992 983 $ hg up -q "min(desc("A0"))"
993 984 $ hg mv f1a f1e
994 985 $ echo c2e > f2a
995 986 $ hg mv f3a f3e
996 987 $ hg mv f4a f4e
997 988 $ hg mv f5a f5b
998 989 $ hg ci -qAm "E0"
999 990 $ hg log -G
1000 @ changeset: 6:ebba59d1fb02
991 @ changeset: 6:6bd1736cab86
1001 992 | tag: tip
1002 993 | parent: 0:11f7a1b56675
1003 994 | user: test
1004 995 | date: Thu Jan 01 00:00:00 1970 +0000
1005 996 | summary: E0
1006 997 |
1007 | o changeset: 5:4f4ba7a6e606
998 | o changeset: 5:560daee679da
1008 999 | | user: test
1009 1000 | | date: Thu Jan 01 00:00:00 1970 +0000
1010 1001 | | summary: D1
1011 1002 | |
1012 | o changeset: 4:43e4b415492d
1003 | o changeset: 4:c9763722f9bd
1013 1004 |/ parent: 0:11f7a1b56675
1014 1005 | user: test
1015 1006 | date: Thu Jan 01 00:00:00 1970 +0000
1016 | summary: C0
1007 | summary: C1
1017 1008 |
1018 1009 | o changeset: 3:b69f5839d2d9
1019 1010 | | user: test
1020 1011 | | date: Thu Jan 01 00:00:00 1970 +0000
1021 1012 | | summary: D0
1022 1013 | |
1023 1014 | o changeset: 2:f58c7e2b28fa
1024 1015 | | user: test
1025 1016 | | date: Thu Jan 01 00:00:00 1970 +0000
1026 1017 | | summary: C0
1027 1018 | |
1028 1019 | o changeset: 1:3d7bba921b5d
1029 1020 |/ user: test
1030 1021 | date: Thu Jan 01 00:00:00 1970 +0000
1031 1022 | summary: B0
1032 1023 |
1033 1024 o changeset: 0:11f7a1b56675
1034 1025 user: test
1035 1026 date: Thu Jan 01 00:00:00 1970 +0000
1036 1027 summary: A0
1037 1028
1038 1029
1039 1030 Test the cases A.4 (f1x), the "ping-pong" special case of A.7 (f5x),
1040 1031 and A.3 with a local content change to be preserved (f2x).
1041 1032
1042 1033 $ HGEDITOR="echo C2 >" hg graft -r 'desc("C0")' --edit
1043 1034 grafting 2:f58c7e2b28fa "C0"
1044 other [graft] changed f1b which local [local] deleted
1045 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u
1035 merging f1e and f1b to f1e
1046 1036 merging f2a and f2c to f2c
1047 1037 merging f5b and f5a to f5a
1048 abort: unresolved conflicts, can't continue
1049 (use 'hg resolve' and 'hg graft --continue')
1050 [255]
1051 $ hg resolve f1b -t ':other' # XXX work around failure
1052 (no more unresolved files)
1053 continue: hg graft --continue
1054 $ hg graft --continue # XXX work around failure
1055 grafting 2:f58c7e2b28fa "C0"
1056 grafting 4:43e4b415492d "C0"
1057 merging f1e and f1a to f1e
1058 merging f2c
1059 warning: can't find ancestor for 'f2c' copied from 'f2a'!
1060 1038
1061 1039 Test the cases A.1 (f4x) and A.7 (f3x).
1062 1040
1063 1041 $ HGEDITOR="echo D2 >" hg graft -r 'desc("D0")' --edit
1064 1042 grafting 3:b69f5839d2d9 "D0"
1043 note: possible conflict - f3b was renamed multiple times to:
1044 f3e
1045 f3d
1065 1046 merging f4e and f4a to f4e
1066 1047 warning: can't find ancestor for 'f3d' copied from 'f3b'!
1067 1048
1068 1049 Check the results of the grafts tested
1069 1050
1070 1051 $ hg log -CGv --patch --git
1071 @ changeset: 9:100f4d78e056
1052 @ changeset: 8:93ee502e8b0a
1072 1053 | tag: tip
1073 1054 | user: test
1074 1055 | date: Thu Jan 01 00:00:00 1970 +0000
1075 1056 | files: f3d f4e
1076 1057 | description:
1077 1058 | D2
1078 1059 |
1079 1060 |
1080 1061 | diff --git a/f3d b/f3d
1081 1062 | new file mode 100644
1082 1063 | --- /dev/null
1083 1064 | +++ b/f3d
1084 1065 | @@ -0,0 +1,1 @@
1085 1066 | +c3a
1086 1067 | diff --git a/f4e b/f4e
1087 1068 | --- a/f4e
1088 1069 | +++ b/f4e
1089 1070 | @@ -1,1 +1,1 @@
1090 1071 | -c4a
1091 1072 | +c4d
1092 1073 |
1093 o changeset: 8:84915a7da133
1074 o changeset: 7:539cf145f496
1094 1075 | user: test
1095 1076 | date: Thu Jan 01 00:00:00 1970 +0000
1096 | files: f1e f5a.orig
1077 | files: f1e f2a f2c f5a f5b
1078 | copies: f2c (f2a) f5a (f5b)
1097 1079 | description:
1098 | C0
1080 | C2
1099 1081 |
1100 1082 |
1101 1083 | diff --git a/f1e b/f1e
1102 1084 | --- a/f1e
1103 1085 | +++ b/f1e
1104 1086 | @@ -1,1 +1,1 @@
1105 1087 | -c1a
1106 1088 | +c1c
1107 | diff --git a/f5a.orig b/f5a.orig
1108 | deleted file mode 100644
1109 | --- a/f5a.orig
1110 | +++ /dev/null
1111 | @@ -1,5 +0,0 @@
1112 | -<<<<<<< local: 11f7a1b56675 - test: A0
1113 | -c5a
1114 | -=======
1115 | -c5c
1116 | ->>>>>>> graft: f58c7e2b28fa - test: C0
1117 |
1118 o changeset: 7:dc778749ee9a
1119 | user: test
1120 | date: Thu Jan 01 00:00:00 1970 +0000
1121 | files: f1b f2a f2c f5a f5b
1122 | copies: f2c (f2a) f5a (f5b)
1123 | description:
1124 | C0
1125 |
1126 |
1127 | diff --git a/f1b b/f1b
1128 | new file mode 100644
1129 | --- /dev/null
1130 | +++ b/f1b
1131 | @@ -0,0 +1,1 @@
1132 | +c1c
1133 1089 | diff --git a/f2a b/f2c
1134 1090 | rename from f2a
1135 1091 | rename to f2c
1136 1092 | diff --git a/f5b b/f5a
1137 1093 | rename from f5b
1138 1094 | rename to f5a
1139 1095 | --- a/f5b
1140 1096 | +++ b/f5a
1141 1097 | @@ -1,1 +1,1 @@
1142 1098 | -c5a
1143 1099 | +c5c
1144 1100 |
1145 o changeset: 6:ebba59d1fb02
1101 o changeset: 6:6bd1736cab86
1146 1102 | parent: 0:11f7a1b56675
1147 1103 | user: test
1148 1104 | date: Thu Jan 01 00:00:00 1970 +0000
1149 | files: f1a f1e f2a f3a f3e f4a f4e f5a f5a.orig f5b
1105 | files: f1a f1e f2a f3a f3e f4a f4e f5a f5b
1150 1106 | copies: f1e (f1a) f3e (f3a) f4e (f4a) f5b (f5a)
1151 1107 | description:
1152 1108 | E0
1153 1109 |
1154 1110 |
1155 1111 | diff --git a/f1a b/f1e
1156 1112 | rename from f1a
1157 1113 | rename to f1e
1158 1114 | diff --git a/f2a b/f2a
1159 1115 | --- a/f2a
1160 1116 | +++ b/f2a
1161 1117 | @@ -1,1 +1,1 @@
1162 1118 | -c2a
1163 1119 | +c2e
1164 1120 | diff --git a/f3a b/f3e
1165 1121 | rename from f3a
1166 1122 | rename to f3e
1167 1123 | diff --git a/f4a b/f4e
1168 1124 | rename from f4a
1169 1125 | rename to f4e
1170 | diff --git a/f5a.orig b/f5a.orig
1171 | new file mode 100644
1172 | --- /dev/null
1173 | +++ b/f5a.orig
1174 | @@ -0,0 +1,5 @@
1175 | +<<<<<<< local: 11f7a1b56675 - test: A0
1176 | +c5a
1177 | +=======
1178 | +c5c
1179 | +>>>>>>> graft: f58c7e2b28fa - test: C0
1180 1126 | diff --git a/f5a b/f5b
1181 1127 | rename from f5a
1182 1128 | rename to f5b
1183 1129 |
1184 | o changeset: 5:4f4ba7a6e606
1130 | o changeset: 5:560daee679da
1185 1131 | | user: test
1186 1132 | | date: Thu Jan 01 00:00:00 1970 +0000
1187 1133 | | files: f3d f4a
1188 1134 | | description:
1189 1135 | | D1
1190 1136 | |
1191 1137 | |
1192 1138 | | diff --git a/f3d b/f3d
1193 1139 | | new file mode 100644
1194 1140 | | --- /dev/null
1195 1141 | | +++ b/f3d
1196 1142 | | @@ -0,0 +1,1 @@
1197 1143 | | +c3a
1198 1144 | | diff --git a/f4a b/f4a
1199 1145 | | --- a/f4a
1200 1146 | | +++ b/f4a
1201 1147 | | @@ -1,1 +1,1 @@
1202 1148 | | -c4a
1203 1149 | | +c4d
1204 1150 | |
1205 | o changeset: 4:43e4b415492d
1151 | o changeset: 4:c9763722f9bd
1206 1152 |/ parent: 0:11f7a1b56675
1207 1153 | user: test
1208 1154 | date: Thu Jan 01 00:00:00 1970 +0000
1209 1155 | files: f1a f2a f2c f5a
1210 1156 | copies: f2c (f2a)
1211 1157 | description:
1212 | C0
1158 | C1
1213 1159 |
1214 1160 |
1215 1161 | diff --git a/f1a b/f1a
1216 1162 | --- a/f1a
1217 1163 | +++ b/f1a
1218 1164 | @@ -1,1 +1,1 @@
1219 1165 | -c1a
1220 1166 | +c1c
1221 1167 | diff --git a/f2a b/f2c
1222 1168 | rename from f2a
1223 1169 | rename to f2c
1224 1170 | diff --git a/f5a b/f5a
1225 1171 | --- a/f5a
1226 1172 | +++ b/f5a
1227 1173 | @@ -1,1 +1,1 @@
1228 1174 | -c5a
1229 1175 | +c5c
1230 1176 |
1231 1177 | o changeset: 3:b69f5839d2d9
1232 1178 | | user: test
1233 1179 | | date: Thu Jan 01 00:00:00 1970 +0000
1234 1180 | | files: f3b f3d f4a
1235 1181 | | copies: f3d (f3b)
1236 1182 | | description:
1237 1183 | | D0
1238 1184 | |
1239 1185 | |
1240 1186 | | diff --git a/f3b b/f3d
1241 1187 | | rename from f3b
1242 1188 | | rename to f3d
1243 1189 | | diff --git a/f4a b/f4a
1244 1190 | | --- a/f4a
1245 1191 | | +++ b/f4a
1246 1192 | | @@ -1,1 +1,1 @@
1247 1193 | | -c4a
1248 1194 | | +c4d
1249 1195 | |
1250 1196 | o changeset: 2:f58c7e2b28fa
1251 1197 | | user: test
1252 1198 | | date: Thu Jan 01 00:00:00 1970 +0000
1253 1199 | | files: f1b f2a f2c f5a f5b
1254 1200 | | copies: f2c (f2a) f5a (f5b)
1255 1201 | | description:
1256 1202 | | C0
1257 1203 | |
1258 1204 | |
1259 1205 | | diff --git a/f1b b/f1b
1260 1206 | | --- a/f1b
1261 1207 | | +++ b/f1b
1262 1208 | | @@ -1,1 +1,1 @@
1263 1209 | | -c1a
1264 1210 | | +c1c
1265 1211 | | diff --git a/f2a b/f2c
1266 1212 | | rename from f2a
1267 1213 | | rename to f2c
1268 1214 | | diff --git a/f5b b/f5a
1269 1215 | | rename from f5b
1270 1216 | | rename to f5a
1271 1217 | | --- a/f5b
1272 1218 | | +++ b/f5a
1273 1219 | | @@ -1,1 +1,1 @@
1274 1220 | | -c5a
1275 1221 | | +c5c
1276 1222 | |
1277 1223 | o changeset: 1:3d7bba921b5d
1278 1224 |/ user: test
1279 1225 | date: Thu Jan 01 00:00:00 1970 +0000
1280 1226 | files: f1a f1b f3a f3b f5a f5b
1281 1227 | copies: f1b (f1a) f3b (f3a) f5b (f5a)
1282 1228 | description:
1283 1229 | B0
1284 1230 |
1285 1231 |
1286 1232 | diff --git a/f1a b/f1b
1287 1233 | rename from f1a
1288 1234 | rename to f1b
1289 1235 | diff --git a/f3a b/f3b
1290 1236 | rename from f3a
1291 1237 | rename to f3b
1292 1238 | diff --git a/f5a b/f5b
1293 1239 | rename from f5a
1294 1240 | rename to f5b
1295 1241 |
1296 1242 o changeset: 0:11f7a1b56675
1297 1243 user: test
1298 1244 date: Thu Jan 01 00:00:00 1970 +0000
1299 1245 files: f1a f2a f3a f4a f5a
1300 1246 description:
1301 1247 A0
1302 1248
1303 1249
1304 1250 diff --git a/f1a b/f1a
1305 1251 new file mode 100644
1306 1252 --- /dev/null
1307 1253 +++ b/f1a
1308 1254 @@ -0,0 +1,1 @@
1309 1255 +c1a
1310 1256 diff --git a/f2a b/f2a
1311 1257 new file mode 100644
1312 1258 --- /dev/null
1313 1259 +++ b/f2a
1314 1260 @@ -0,0 +1,1 @@
1315 1261 +c2a
1316 1262 diff --git a/f3a b/f3a
1317 1263 new file mode 100644
1318 1264 --- /dev/null
1319 1265 +++ b/f3a
1320 1266 @@ -0,0 +1,1 @@
1321 1267 +c3a
1322 1268 diff --git a/f4a b/f4a
1323 1269 new file mode 100644
1324 1270 --- /dev/null
1325 1271 +++ b/f4a
1326 1272 @@ -0,0 +1,1 @@
1327 1273 +c4a
1328 1274 diff --git a/f5a b/f5a
1329 1275 new file mode 100644
1330 1276 --- /dev/null
1331 1277 +++ b/f5a
1332 1278 @@ -0,0 +1,1 @@
1333 1279 +c5a
1334 1280
1335 1281 $ hg cat f2c
1336 1282 c2e
General Comments 0
You need to be logged in to leave comments. Login now