##// END OF EJS Templates
merge: separate out "both created" cases...
Martin von Zweigbergk -
r23396:6a254a2d default
parent child Browse files
Show More
@@ -1,1141 +1,1142 b''
1 1 # merge.py - directory-level update/merge handling for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import struct
9 9
10 10 from node import nullid, nullrev, hex, bin
11 11 from i18n import _
12 12 from mercurial import obsolete
13 13 import error as errormod, util, filemerge, copies, subrepo, worker
14 14 import errno, os, shutil
15 15
16 16 _pack = struct.pack
17 17 _unpack = struct.unpack
18 18
19 19 def _droponode(data):
20 20 # used for compatibility for v1
21 21 bits = data.split('\0')
22 22 bits = bits[:-2] + bits[-1:]
23 23 return '\0'.join(bits)
24 24
25 25 class mergestate(object):
26 26 '''track 3-way merge state of individual files
27 27
28 28 it is stored on disk when needed. Two file are used, one with an old
29 29 format, one with a new format. Both contains similar data, but the new
30 30 format can store new kind of field.
31 31
32 32 Current new format is a list of arbitrary record of the form:
33 33
34 34 [type][length][content]
35 35
36 36 Type is a single character, length is a 4 bytes integer, content is an
37 37 arbitrary suites of bytes of length `length`.
38 38
39 39 Type should be a letter. Capital letter are mandatory record, Mercurial
40 40 should abort if they are unknown. lower case record can be safely ignored.
41 41
42 42 Currently known record:
43 43
44 44 L: the node of the "local" part of the merge (hexified version)
45 45 O: the node of the "other" part of the merge (hexified version)
46 46 F: a file to be merged entry
47 47 '''
48 48 statepathv1 = 'merge/state'
49 49 statepathv2 = 'merge/state2'
50 50
51 51 def __init__(self, repo):
52 52 self._repo = repo
53 53 self._dirty = False
54 54 self._read()
55 55
56 56 def reset(self, node=None, other=None):
57 57 self._state = {}
58 58 self._local = None
59 59 self._other = None
60 60 if node:
61 61 self._local = node
62 62 self._other = other
63 63 shutil.rmtree(self._repo.join('merge'), True)
64 64 self._dirty = False
65 65
66 66 def _read(self):
67 67 """Analyse each record content to restore a serialized state from disk
68 68
69 69 This function process "record" entry produced by the de-serialization
70 70 of on disk file.
71 71 """
72 72 self._state = {}
73 73 self._local = None
74 74 self._other = None
75 75 records = self._readrecords()
76 76 for rtype, record in records:
77 77 if rtype == 'L':
78 78 self._local = bin(record)
79 79 elif rtype == 'O':
80 80 self._other = bin(record)
81 81 elif rtype == 'F':
82 82 bits = record.split('\0')
83 83 self._state[bits[0]] = bits[1:]
84 84 elif not rtype.islower():
85 85 raise util.Abort(_('unsupported merge state record: %s')
86 86 % rtype)
87 87 self._dirty = False
88 88
89 89 def _readrecords(self):
90 90 """Read merge state from disk and return a list of record (TYPE, data)
91 91
92 92 We read data from both v1 and v2 files and decide which one to use.
93 93
94 94 V1 has been used by version prior to 2.9.1 and contains less data than
95 95 v2. We read both versions and check if no data in v2 contradicts
96 96 v1. If there is not contradiction we can safely assume that both v1
97 97 and v2 were written at the same time and use the extract data in v2. If
98 98 there is contradiction we ignore v2 content as we assume an old version
99 99 of Mercurial has overwritten the mergestate file and left an old v2
100 100 file around.
101 101
102 102 returns list of record [(TYPE, data), ...]"""
103 103 v1records = self._readrecordsv1()
104 104 v2records = self._readrecordsv2()
105 105 oldv2 = set() # old format version of v2 record
106 106 for rec in v2records:
107 107 if rec[0] == 'L':
108 108 oldv2.add(rec)
109 109 elif rec[0] == 'F':
110 110 # drop the onode data (not contained in v1)
111 111 oldv2.add(('F', _droponode(rec[1])))
112 112 for rec in v1records:
113 113 if rec not in oldv2:
114 114 # v1 file is newer than v2 file, use it
115 115 # we have to infer the "other" changeset of the merge
116 116 # we cannot do better than that with v1 of the format
117 117 mctx = self._repo[None].parents()[-1]
118 118 v1records.append(('O', mctx.hex()))
119 119 # add place holder "other" file node information
120 120 # nobody is using it yet so we do no need to fetch the data
121 121 # if mctx was wrong `mctx[bits[-2]]` may fails.
122 122 for idx, r in enumerate(v1records):
123 123 if r[0] == 'F':
124 124 bits = r[1].split('\0')
125 125 bits.insert(-2, '')
126 126 v1records[idx] = (r[0], '\0'.join(bits))
127 127 return v1records
128 128 else:
129 129 return v2records
130 130
131 131 def _readrecordsv1(self):
132 132 """read on disk merge state for version 1 file
133 133
134 134 returns list of record [(TYPE, data), ...]
135 135
136 136 Note: the "F" data from this file are one entry short
137 137 (no "other file node" entry)
138 138 """
139 139 records = []
140 140 try:
141 141 f = self._repo.opener(self.statepathv1)
142 142 for i, l in enumerate(f):
143 143 if i == 0:
144 144 records.append(('L', l[:-1]))
145 145 else:
146 146 records.append(('F', l[:-1]))
147 147 f.close()
148 148 except IOError, err:
149 149 if err.errno != errno.ENOENT:
150 150 raise
151 151 return records
152 152
153 153 def _readrecordsv2(self):
154 154 """read on disk merge state for version 2 file
155 155
156 156 returns list of record [(TYPE, data), ...]
157 157 """
158 158 records = []
159 159 try:
160 160 f = self._repo.opener(self.statepathv2)
161 161 data = f.read()
162 162 off = 0
163 163 end = len(data)
164 164 while off < end:
165 165 rtype = data[off]
166 166 off += 1
167 167 length = _unpack('>I', data[off:(off + 4)])[0]
168 168 off += 4
169 169 record = data[off:(off + length)]
170 170 off += length
171 171 records.append((rtype, record))
172 172 f.close()
173 173 except IOError, err:
174 174 if err.errno != errno.ENOENT:
175 175 raise
176 176 return records
177 177
178 178 def active(self):
179 179 """Whether mergestate is active.
180 180
181 181 Returns True if there appears to be mergestate. This is a rough proxy
182 182 for "is a merge in progress."
183 183 """
184 184 # Check local variables before looking at filesystem for performance
185 185 # reasons.
186 186 return bool(self._local) or bool(self._state) or \
187 187 self._repo.opener.exists(self.statepathv1) or \
188 188 self._repo.opener.exists(self.statepathv2)
189 189
190 190 def commit(self):
191 191 """Write current state on disk (if necessary)"""
192 192 if self._dirty:
193 193 records = []
194 194 records.append(('L', hex(self._local)))
195 195 records.append(('O', hex(self._other)))
196 196 for d, v in self._state.iteritems():
197 197 records.append(('F', '\0'.join([d] + v)))
198 198 self._writerecords(records)
199 199 self._dirty = False
200 200
201 201 def _writerecords(self, records):
202 202 """Write current state on disk (both v1 and v2)"""
203 203 self._writerecordsv1(records)
204 204 self._writerecordsv2(records)
205 205
206 206 def _writerecordsv1(self, records):
207 207 """Write current state on disk in a version 1 file"""
208 208 f = self._repo.opener(self.statepathv1, 'w')
209 209 irecords = iter(records)
210 210 lrecords = irecords.next()
211 211 assert lrecords[0] == 'L'
212 212 f.write(hex(self._local) + '\n')
213 213 for rtype, data in irecords:
214 214 if rtype == 'F':
215 215 f.write('%s\n' % _droponode(data))
216 216 f.close()
217 217
218 218 def _writerecordsv2(self, records):
219 219 """Write current state on disk in a version 2 file"""
220 220 f = self._repo.opener(self.statepathv2, 'w')
221 221 for key, data in records:
222 222 assert len(key) == 1
223 223 format = '>sI%is' % len(data)
224 224 f.write(_pack(format, key, len(data), data))
225 225 f.close()
226 226
227 227 def add(self, fcl, fco, fca, fd):
228 228 """add a new (potentially?) conflicting file the merge state
229 229 fcl: file context for local,
230 230 fco: file context for remote,
231 231 fca: file context for ancestors,
232 232 fd: file path of the resulting merge.
233 233
234 234 note: also write the local version to the `.hg/merge` directory.
235 235 """
236 236 hash = util.sha1(fcl.path()).hexdigest()
237 237 self._repo.opener.write('merge/' + hash, fcl.data())
238 238 self._state[fd] = ['u', hash, fcl.path(),
239 239 fca.path(), hex(fca.filenode()),
240 240 fco.path(), hex(fco.filenode()),
241 241 fcl.flags()]
242 242 self._dirty = True
243 243
244 244 def __contains__(self, dfile):
245 245 return dfile in self._state
246 246
247 247 def __getitem__(self, dfile):
248 248 return self._state[dfile][0]
249 249
250 250 def __iter__(self):
251 251 return iter(sorted(self._state))
252 252
253 253 def files(self):
254 254 return self._state.keys()
255 255
256 256 def mark(self, dfile, state):
257 257 self._state[dfile][0] = state
258 258 self._dirty = True
259 259
260 260 def unresolved(self):
261 261 """Obtain the paths of unresolved files."""
262 262
263 263 for f, entry in self._state.items():
264 264 if entry[0] == 'u':
265 265 yield f
266 266
267 267 def resolve(self, dfile, wctx, labels=None):
268 268 """rerun merge process for file path `dfile`"""
269 269 if self[dfile] == 'r':
270 270 return 0
271 271 stateentry = self._state[dfile]
272 272 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
273 273 octx = self._repo[self._other]
274 274 fcd = wctx[dfile]
275 275 fco = octx[ofile]
276 276 fca = self._repo.filectx(afile, fileid=anode)
277 277 # "premerge" x flags
278 278 flo = fco.flags()
279 279 fla = fca.flags()
280 280 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
281 281 if fca.node() == nullid:
282 282 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
283 283 afile)
284 284 elif flags == fla:
285 285 flags = flo
286 286 # restore local
287 287 f = self._repo.opener('merge/' + hash)
288 288 self._repo.wwrite(dfile, f.read(), flags)
289 289 f.close()
290 290 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca,
291 291 labels=labels)
292 292 if r is None:
293 293 # no real conflict
294 294 del self._state[dfile]
295 295 self._dirty = True
296 296 elif not r:
297 297 self.mark(dfile, 'r')
298 298 return r
299 299
300 300 def _checkunknownfile(repo, wctx, mctx, f):
301 301 return (not repo.dirstate._ignore(f)
302 302 and os.path.isfile(repo.wjoin(f))
303 303 and repo.wopener.audit.check(f)
304 304 and repo.dirstate.normalize(f) not in repo.dirstate
305 305 and mctx[f].cmp(wctx[f]))
306 306
307 307 def _forgetremoved(wctx, mctx, branchmerge):
308 308 """
309 309 Forget removed files
310 310
311 311 If we're jumping between revisions (as opposed to merging), and if
312 312 neither the working directory nor the target rev has the file,
313 313 then we need to remove it from the dirstate, to prevent the
314 314 dirstate from listing the file when it is no longer in the
315 315 manifest.
316 316
317 317 If we're merging, and the other revision has removed a file
318 318 that is not present in the working directory, we need to mark it
319 319 as removed.
320 320 """
321 321
322 322 ractions = []
323 323 factions = xactions = []
324 324 if branchmerge:
325 325 xactions = ractions
326 326 for f in wctx.deleted():
327 327 if f not in mctx:
328 328 xactions.append((f, None, "forget deleted"))
329 329
330 330 if not branchmerge:
331 331 for f in wctx.removed():
332 332 if f not in mctx:
333 333 factions.append((f, None, "forget removed"))
334 334
335 335 return ractions, factions
336 336
337 337 def _checkcollision(repo, wmf, actions):
338 338 # build provisional merged manifest up
339 339 pmmf = set(wmf)
340 340
341 341 if actions:
342 342 # k, dr, e and rd are no-op
343 343 for m in 'a', 'f', 'g', 'cd', 'dc':
344 344 for f, args, msg in actions[m]:
345 345 pmmf.add(f)
346 346 for f, args, msg in actions['r']:
347 347 pmmf.discard(f)
348 348 for f, args, msg in actions['dm']:
349 349 f2, flags = args
350 350 pmmf.discard(f2)
351 351 pmmf.add(f)
352 352 for f, args, msg in actions['dg']:
353 353 pmmf.add(f)
354 354 for f, args, msg in actions['m']:
355 355 f1, f2, fa, move, anc = args
356 356 if move:
357 357 pmmf.discard(f1)
358 358 pmmf.add(f)
359 359
360 360 # check case-folding collision in provisional merged manifest
361 361 foldmap = {}
362 362 for f in sorted(pmmf):
363 363 fold = util.normcase(f)
364 364 if fold in foldmap:
365 365 raise util.Abort(_("case-folding collision between %s and %s")
366 366 % (f, foldmap[fold]))
367 367 foldmap[fold] = f
368 368
369 369 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
370 370 acceptremote, followcopies):
371 371 """
372 372 Merge p1 and p2 with ancestor pa and generate merge action list
373 373
374 374 branchmerge and force are as passed in to update
375 375 partial = function to filter file lists
376 376 acceptremote = accept the incoming changes without prompting
377 377 """
378 378
379 379 actions = dict((m, []) for m in 'a f g cd dc r dm dg m dr e rd k'.split())
380 380 copy, movewithdir = {}, {}
381 381
382 382 # manifests fetched in order are going to be faster, so prime the caches
383 383 [x.manifest() for x in
384 384 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
385 385
386 386 if followcopies:
387 387 ret = copies.mergecopies(repo, wctx, p2, pa)
388 388 copy, movewithdir, diverge, renamedelete = ret
389 389 for of, fl in diverge.iteritems():
390 390 actions['dr'].append((of, (fl,), "divergent renames"))
391 391 for of, fl in renamedelete.iteritems():
392 392 actions['rd'].append((of, (fl,), "rename and delete"))
393 393
394 394 repo.ui.note(_("resolving manifests\n"))
395 395 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
396 396 % (bool(branchmerge), bool(force), bool(partial)))
397 397 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
398 398
399 399 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
400 400 copied = set(copy.values())
401 401 copied.update(movewithdir.values())
402 402
403 403 if '.hgsubstate' in m1:
404 404 # check whether sub state is modified
405 405 for s in sorted(wctx.substate):
406 406 if wctx.sub(s).dirty():
407 407 m1['.hgsubstate'] += '+'
408 408 break
409 409
410 410 aborts = []
411 411 # Compare manifests
412 412 diff = m1.diff(m2)
413 413
414 414 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
415 415 if partial and not partial(f):
416 416 continue
417 417 if n1 and n2:
418 if True:
419 fa = f
420 a = ma.get(f, nullid)
421 if a == nullid:
422 fa = copy.get(f, f)
423 # Note: f as default is wrong - we can't really make a 3-way
424 # merge without an ancestor file.
425 fla = ma.flags(fa)
418 if f not in ma:
419 # Note: f as ancestor is wrong - we can't really make a 3-way
420 # merge without an ancestor file.
421 fa = copy.get(f, f)
422 actions['m'].append((f, (f, f, fa, False, pa.node()),
423 "both created"))
424 else:
425 a = ma[f]
426 fla = ma.flags(f)
426 427 nol = 'l' not in fl1 + fl2 + fla
427 428 if n2 == a and fl2 == fla:
428 429 actions['k'].append((f, (), "keep")) # remote unchanged
429 430 elif n1 == a and fl1 == fla: # local unchanged - use remote
430 431 if n1 == n2: # optimization: keep local content
431 432 actions['e'].append((f, (fl2,), "update permissions"))
432 433 else:
433 434 actions['g'].append((f, (fl2,), "remote is newer"))
434 435 elif nol and n2 == a: # remote only changed 'x'
435 436 actions['e'].append((f, (fl2,), "update permissions"))
436 437 elif nol and n1 == a: # local only changed 'x'
437 438 actions['g'].append((f, (fl1,), "remote is newer"))
438 439 else: # both changed something
439 actions['m'].append((f, (f, f, fa, False, pa.node()),
440 actions['m'].append((f, (f, f, f, False, pa.node()),
440 441 "versions differ"))
441 442 elif f in copied: # files we'll deal with on m2 side
442 443 pass
443 444 elif n1 and f in movewithdir: # directory rename, move local
444 445 f2 = movewithdir[f]
445 446 actions['dm'].append((f2, (f, fl1),
446 447 "remote directory rename - move from " + f))
447 448 elif n1 and f in copy:
448 449 f2 = copy[f]
449 450 actions['m'].append((f, (f, f2, f2, False, pa.node()),
450 451 "local copied/moved from " + f2))
451 452 elif n1 and f in ma: # clean, a different, no remote
452 453 if n1 != ma[f]:
453 454 if acceptremote:
454 455 actions['r'].append((f, None, "remote delete"))
455 456 else:
456 457 actions['cd'].append((f, None, "prompt changed/deleted"))
457 458 elif n1[20:] == 'a': # added, no remote
458 459 actions['f'].append((f, None, "remote deleted"))
459 460 else:
460 461 actions['r'].append((f, None, "other deleted"))
461 462 elif n2 and f in movewithdir:
462 463 f2 = movewithdir[f]
463 464 actions['dg'].append((f2, (f, fl2),
464 465 "local directory rename - get from " + f))
465 466 elif n2 and f in copy:
466 467 f2 = copy[f]
467 468 if f2 in m2:
468 469 actions['m'].append((f, (f2, f, f2, False, pa.node()),
469 470 "remote copied from " + f2))
470 471 else:
471 472 actions['m'].append((f, (f2, f, f2, True, pa.node()),
472 473 "remote moved from " + f2))
473 474 elif n2 and f not in ma:
474 475 # local unknown, remote created: the logic is described by the
475 476 # following table:
476 477 #
477 478 # force branchmerge different | action
478 479 # n * n | get
479 480 # n * y | abort
480 481 # y n * | get
481 482 # y y n | get
482 483 # y y y | merge
483 484 #
484 485 # Checking whether the files are different is expensive, so we
485 486 # don't do that when we can avoid it.
486 487 if force and not branchmerge:
487 488 actions['g'].append((f, (fl2,), "remote created"))
488 489 else:
489 490 different = _checkunknownfile(repo, wctx, p2, f)
490 491 if force and branchmerge and different:
491 492 # FIXME: This is wrong - f is not in ma ...
492 493 actions['m'].append((f, (f, f, f, False, pa.node()),
493 494 "remote differs from untracked local"))
494 495 elif not force and different:
495 496 aborts.append((f, 'ud'))
496 497 else:
497 498 actions['g'].append((f, (fl2,), "remote created"))
498 499 elif n2 and n2 != ma[f]:
499 500 different = _checkunknownfile(repo, wctx, p2, f)
500 501 if not force and different:
501 502 aborts.append((f, 'ud'))
502 503 else:
503 504 if acceptremote:
504 505 actions['g'].append((f, (fl2,), "remote recreating"))
505 506 else:
506 507 actions['dc'].append((f, (fl2,), "prompt deleted/changed"))
507 508
508 509 for f, m in sorted(aborts):
509 510 if m == 'ud':
510 511 repo.ui.warn(_("%s: untracked file differs\n") % f)
511 512 else: assert False, m
512 513 if aborts:
513 514 raise util.Abort(_("untracked files in working directory differ "
514 515 "from files in requested revision"))
515 516
516 517 if not util.checkcase(repo.path):
517 518 # check collision between files only in p2 for clean update
518 519 if (not branchmerge and
519 520 (force or not wctx.dirty(missing=True, branch=False))):
520 521 _checkcollision(repo, m2, None)
521 522 else:
522 523 _checkcollision(repo, m1, actions)
523 524
524 525 return actions
525 526
526 527 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
527 528 acceptremote, followcopies):
528 529 "Calculate the actions needed to merge mctx into wctx using ancestors"
529 530
530 531 if len(ancestors) == 1: # default
531 532 actions = manifestmerge(repo, wctx, mctx, ancestors[0],
532 533 branchmerge, force,
533 534 partial, acceptremote, followcopies)
534 535
535 536 else: # only when merge.preferancestor=* - the default
536 537 repo.ui.note(
537 538 _("note: merging %s and %s using bids from ancestors %s\n") %
538 539 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
539 540
540 541 # Call for bids
541 542 fbids = {} # mapping filename to bids (action method to list af actions)
542 543 for ancestor in ancestors:
543 544 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
544 545 actions = manifestmerge(repo, wctx, mctx, ancestor,
545 546 branchmerge, force,
546 547 partial, acceptremote, followcopies)
547 548 for m, l in sorted(actions.items()):
548 549 for a in l:
549 550 f, args, msg = a
550 551 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
551 552 if f in fbids:
552 553 d = fbids[f]
553 554 if m in d:
554 555 d[m].append(a)
555 556 else:
556 557 d[m] = [a]
557 558 else:
558 559 fbids[f] = {m: [a]}
559 560
560 561 # Pick the best bid for each file
561 562 repo.ui.note(_('\nauction for merging merge bids\n'))
562 563 actions = dict((m, []) for m in actions.keys())
563 564 for f, bids in sorted(fbids.items()):
564 565 # bids is a mapping from action method to list af actions
565 566 # Consensus?
566 567 if len(bids) == 1: # all bids are the same kind of method
567 568 m, l = bids.items()[0]
568 569 if util.all(a == l[0] for a in l[1:]): # len(bids) is > 1
569 570 repo.ui.note(" %s: consensus for %s\n" % (f, m))
570 571 actions[m].append(l[0])
571 572 continue
572 573 # If keep is an option, just do it.
573 574 if 'k' in bids:
574 575 repo.ui.note(" %s: picking 'keep' action\n" % f)
575 576 actions['k'].append(bids['k'][0])
576 577 continue
577 578 # If there are gets and they all agree [how could they not?], do it.
578 579 if 'g' in bids:
579 580 ga0 = bids['g'][0]
580 581 if util.all(a == ga0 for a in bids['g'][1:]):
581 582 repo.ui.note(" %s: picking 'get' action\n" % f)
582 583 actions['g'].append(ga0)
583 584 continue
584 585 # TODO: Consider other simple actions such as mode changes
585 586 # Handle inefficient democrazy.
586 587 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
587 588 for m, l in sorted(bids.items()):
588 589 for _f, args, msg in l:
589 590 repo.ui.note(' %s -> %s\n' % (msg, m))
590 591 # Pick random action. TODO: Instead, prompt user when resolving
591 592 m, l = bids.items()[0]
592 593 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
593 594 (f, m))
594 595 actions[m].append(l[0])
595 596 continue
596 597 repo.ui.note(_('end of auction\n\n'))
597 598
598 599 # Prompt and create actions. TODO: Move this towards resolve phase.
599 600 for f, args, msg in actions['cd']:
600 601 if repo.ui.promptchoice(
601 602 _("local changed %s which remote deleted\n"
602 603 "use (c)hanged version or (d)elete?"
603 604 "$$ &Changed $$ &Delete") % f, 0):
604 605 actions['r'].append((f, None, "prompt delete"))
605 606 else:
606 607 actions['a'].append((f, None, "prompt keep"))
607 608 del actions['cd'][:]
608 609
609 610 for f, args, msg in actions['dc']:
610 611 flags, = args
611 612 if repo.ui.promptchoice(
612 613 _("remote changed %s which local deleted\n"
613 614 "use (c)hanged version or leave (d)eleted?"
614 615 "$$ &Changed $$ &Deleted") % f, 0) == 0:
615 616 actions['g'].append((f, (flags,), "prompt recreating"))
616 617 del actions['dc'][:]
617 618
618 619 if wctx.rev() is None:
619 620 ractions, factions = _forgetremoved(wctx, mctx, branchmerge)
620 621 actions['r'].extend(ractions)
621 622 actions['f'].extend(factions)
622 623
623 624 return actions
624 625
625 626 def batchremove(repo, actions):
626 627 """apply removes to the working directory
627 628
628 629 yields tuples for progress updates
629 630 """
630 631 verbose = repo.ui.verbose
631 632 unlink = util.unlinkpath
632 633 wjoin = repo.wjoin
633 634 audit = repo.wopener.audit
634 635 i = 0
635 636 for f, args, msg in actions:
636 637 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
637 638 if verbose:
638 639 repo.ui.note(_("removing %s\n") % f)
639 640 audit(f)
640 641 try:
641 642 unlink(wjoin(f), ignoremissing=True)
642 643 except OSError, inst:
643 644 repo.ui.warn(_("update failed to remove %s: %s!\n") %
644 645 (f, inst.strerror))
645 646 if i == 100:
646 647 yield i, f
647 648 i = 0
648 649 i += 1
649 650 if i > 0:
650 651 yield i, f
651 652
652 653 def batchget(repo, mctx, actions):
653 654 """apply gets to the working directory
654 655
655 656 mctx is the context to get from
656 657
657 658 yields tuples for progress updates
658 659 """
659 660 verbose = repo.ui.verbose
660 661 fctx = mctx.filectx
661 662 wwrite = repo.wwrite
662 663 i = 0
663 664 for f, args, msg in actions:
664 665 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
665 666 if verbose:
666 667 repo.ui.note(_("getting %s\n") % f)
667 668 wwrite(f, fctx(f).data(), args[0])
668 669 if i == 100:
669 670 yield i, f
670 671 i = 0
671 672 i += 1
672 673 if i > 0:
673 674 yield i, f
674 675
675 676 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
676 677 """apply the merge action list to the working directory
677 678
678 679 wctx is the working copy context
679 680 mctx is the context to be merged into the working copy
680 681
681 682 Return a tuple of counts (updated, merged, removed, unresolved) that
682 683 describes how many files were affected by the update.
683 684 """
684 685
685 686 updated, merged, removed, unresolved = 0, 0, 0, 0
686 687 ms = mergestate(repo)
687 688 ms.reset(wctx.p1().node(), mctx.node())
688 689 moves = []
689 690 for m, l in actions.items():
690 691 l.sort()
691 692
692 693 # prescan for merges
693 694 for f, args, msg in actions['m']:
694 695 f1, f2, fa, move, anc = args
695 696 if f == '.hgsubstate': # merged internally
696 697 continue
697 698 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
698 699 fcl = wctx[f1]
699 700 fco = mctx[f2]
700 701 actx = repo[anc]
701 702 if fa in actx:
702 703 fca = actx[fa]
703 704 else:
704 705 fca = repo.filectx(f1, fileid=nullrev)
705 706 ms.add(fcl, fco, fca, f)
706 707 if f1 != f and move:
707 708 moves.append(f1)
708 709
709 710 audit = repo.wopener.audit
710 711 _updating = _('updating')
711 712 _files = _('files')
712 713 progress = repo.ui.progress
713 714
714 715 # remove renamed files after safely stored
715 716 for f in moves:
716 717 if os.path.lexists(repo.wjoin(f)):
717 718 repo.ui.debug("removing %s\n" % f)
718 719 audit(f)
719 720 util.unlinkpath(repo.wjoin(f))
720 721
721 722 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
722 723
723 724 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
724 725 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
725 726
726 727 # remove in parallel (must come first)
727 728 z = 0
728 729 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
729 730 for i, item in prog:
730 731 z += i
731 732 progress(_updating, z, item=item, total=numupdates, unit=_files)
732 733 removed = len(actions['r'])
733 734
734 735 # get in parallel
735 736 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
736 737 for i, item in prog:
737 738 z += i
738 739 progress(_updating, z, item=item, total=numupdates, unit=_files)
739 740 updated = len(actions['g'])
740 741
741 742 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
742 743 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
743 744
744 745 # forget (manifest only, just log it) (must come first)
745 746 for f, args, msg in actions['f']:
746 747 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
747 748 z += 1
748 749 progress(_updating, z, item=f, total=numupdates, unit=_files)
749 750
750 751 # re-add (manifest only, just log it)
751 752 for f, args, msg in actions['a']:
752 753 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
753 754 z += 1
754 755 progress(_updating, z, item=f, total=numupdates, unit=_files)
755 756
756 757 # keep (noop, just log it)
757 758 for f, args, msg in actions['k']:
758 759 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
759 760 # no progress
760 761
761 762 # merge
762 763 for f, args, msg in actions['m']:
763 764 repo.ui.debug(" %s: %s -> m\n" % (f, msg))
764 765 z += 1
765 766 progress(_updating, z, item=f, total=numupdates, unit=_files)
766 767 if f == '.hgsubstate': # subrepo states need updating
767 768 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
768 769 overwrite)
769 770 continue
770 771 audit(f)
771 772 r = ms.resolve(f, wctx, labels=labels)
772 773 if r is not None and r > 0:
773 774 unresolved += 1
774 775 else:
775 776 if r is None:
776 777 updated += 1
777 778 else:
778 779 merged += 1
779 780
780 781 # directory rename, move local
781 782 for f, args, msg in actions['dm']:
782 783 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
783 784 z += 1
784 785 progress(_updating, z, item=f, total=numupdates, unit=_files)
785 786 f0, flags = args
786 787 repo.ui.note(_("moving %s to %s\n") % (f0, f))
787 788 audit(f)
788 789 repo.wwrite(f, wctx.filectx(f0).data(), flags)
789 790 util.unlinkpath(repo.wjoin(f0))
790 791 updated += 1
791 792
792 793 # local directory rename, get
793 794 for f, args, msg in actions['dg']:
794 795 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
795 796 z += 1
796 797 progress(_updating, z, item=f, total=numupdates, unit=_files)
797 798 f0, flags = args
798 799 repo.ui.note(_("getting %s to %s\n") % (f0, f))
799 800 repo.wwrite(f, mctx.filectx(f0).data(), flags)
800 801 updated += 1
801 802
802 803 # divergent renames
803 804 for f, args, msg in actions['dr']:
804 805 repo.ui.debug(" %s: %s -> dr\n" % (f, msg))
805 806 z += 1
806 807 progress(_updating, z, item=f, total=numupdates, unit=_files)
807 808 fl, = args
808 809 repo.ui.warn(_("note: possible conflict - %s was renamed "
809 810 "multiple times to:\n") % f)
810 811 for nf in fl:
811 812 repo.ui.warn(" %s\n" % nf)
812 813
813 814 # rename and delete
814 815 for f, args, msg in actions['rd']:
815 816 repo.ui.debug(" %s: %s -> rd\n" % (f, msg))
816 817 z += 1
817 818 progress(_updating, z, item=f, total=numupdates, unit=_files)
818 819 fl, = args
819 820 repo.ui.warn(_("note: possible conflict - %s was deleted "
820 821 "and renamed to:\n") % f)
821 822 for nf in fl:
822 823 repo.ui.warn(" %s\n" % nf)
823 824
824 825 # exec
825 826 for f, args, msg in actions['e']:
826 827 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
827 828 z += 1
828 829 progress(_updating, z, item=f, total=numupdates, unit=_files)
829 830 flags, = args
830 831 audit(f)
831 832 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
832 833 updated += 1
833 834
834 835 ms.commit()
835 836 progress(_updating, None, total=numupdates, unit=_files)
836 837
837 838 return updated, merged, removed, unresolved
838 839
839 840 def recordupdates(repo, actions, branchmerge):
840 841 "record merge actions to the dirstate"
841 842 # remove (must come first)
842 843 for f, args, msg in actions['r']:
843 844 if branchmerge:
844 845 repo.dirstate.remove(f)
845 846 else:
846 847 repo.dirstate.drop(f)
847 848
848 849 # forget (must come first)
849 850 for f, args, msg in actions['f']:
850 851 repo.dirstate.drop(f)
851 852
852 853 # re-add
853 854 for f, args, msg in actions['a']:
854 855 if not branchmerge:
855 856 repo.dirstate.add(f)
856 857
857 858 # exec change
858 859 for f, args, msg in actions['e']:
859 860 repo.dirstate.normallookup(f)
860 861
861 862 # keep
862 863 for f, args, msg in actions['k']:
863 864 pass
864 865
865 866 # get
866 867 for f, args, msg in actions['g']:
867 868 if branchmerge:
868 869 repo.dirstate.otherparent(f)
869 870 else:
870 871 repo.dirstate.normal(f)
871 872
872 873 # merge
873 874 for f, args, msg in actions['m']:
874 875 f1, f2, fa, move, anc = args
875 876 if branchmerge:
876 877 # We've done a branch merge, mark this file as merged
877 878 # so that we properly record the merger later
878 879 repo.dirstate.merge(f)
879 880 if f1 != f2: # copy/rename
880 881 if move:
881 882 repo.dirstate.remove(f1)
882 883 if f1 != f:
883 884 repo.dirstate.copy(f1, f)
884 885 else:
885 886 repo.dirstate.copy(f2, f)
886 887 else:
887 888 # We've update-merged a locally modified file, so
888 889 # we set the dirstate to emulate a normal checkout
889 890 # of that file some time in the past. Thus our
890 891 # merge will appear as a normal local file
891 892 # modification.
892 893 if f2 == f: # file not locally copied/moved
893 894 repo.dirstate.normallookup(f)
894 895 if move:
895 896 repo.dirstate.drop(f1)
896 897
897 898 # directory rename, move local
898 899 for f, args, msg in actions['dm']:
899 900 f0, flag = args
900 901 if branchmerge:
901 902 repo.dirstate.add(f)
902 903 repo.dirstate.remove(f0)
903 904 repo.dirstate.copy(f0, f)
904 905 else:
905 906 repo.dirstate.normal(f)
906 907 repo.dirstate.drop(f0)
907 908
908 909 # directory rename, get
909 910 for f, args, msg in actions['dg']:
910 911 f0, flag = args
911 912 if branchmerge:
912 913 repo.dirstate.add(f)
913 914 repo.dirstate.copy(f0, f)
914 915 else:
915 916 repo.dirstate.normal(f)
916 917
917 918 def update(repo, node, branchmerge, force, partial, ancestor=None,
918 919 mergeancestor=False, labels=None):
919 920 """
920 921 Perform a merge between the working directory and the given node
921 922
922 923 node = the node to update to, or None if unspecified
923 924 branchmerge = whether to merge between branches
924 925 force = whether to force branch merging or file overwriting
925 926 partial = a function to filter file lists (dirstate not updated)
926 927 mergeancestor = whether it is merging with an ancestor. If true,
927 928 we should accept the incoming changes for any prompts that occur.
928 929 If false, merging with an ancestor (fast-forward) is only allowed
929 930 between different named branches. This flag is used by rebase extension
930 931 as a temporary fix and should be avoided in general.
931 932
932 933 The table below shows all the behaviors of the update command
933 934 given the -c and -C or no options, whether the working directory
934 935 is dirty, whether a revision is specified, and the relationship of
935 936 the parent rev to the target rev (linear, on the same named
936 937 branch, or on another named branch).
937 938
938 939 This logic is tested by test-update-branches.t.
939 940
940 941 -c -C dirty rev | linear same cross
941 942 n n n n | ok (1) x
942 943 n n n y | ok ok ok
943 944 n n y n | merge (2) (2)
944 945 n n y y | merge (3) (3)
945 946 n y * * | --- discard ---
946 947 y n y * | --- (4) ---
947 948 y n n * | --- ok ---
948 949 y y * * | --- (5) ---
949 950
950 951 x = can't happen
951 952 * = don't-care
952 953 1 = abort: not a linear update (merge or update --check to force update)
953 954 2 = abort: uncommitted changes (commit and merge, or update --clean to
954 955 discard changes)
955 956 3 = abort: uncommitted changes (commit or update --clean to discard changes)
956 957 4 = abort: uncommitted changes (checked in commands.py)
957 958 5 = incompatible options (checked in commands.py)
958 959
959 960 Return the same tuple as applyupdates().
960 961 """
961 962
962 963 onode = node
963 964 wlock = repo.wlock()
964 965 try:
965 966 wc = repo[None]
966 967 pl = wc.parents()
967 968 p1 = pl[0]
968 969 pas = [None]
969 970 if ancestor:
970 971 pas = [repo[ancestor]]
971 972
972 973 if node is None:
973 974 # Here is where we should consider bookmarks, divergent bookmarks,
974 975 # foreground changesets (successors), and tip of current branch;
975 976 # but currently we are only checking the branch tips.
976 977 try:
977 978 node = repo.branchtip(wc.branch())
978 979 except errormod.RepoLookupError:
979 980 if wc.branch() == 'default': # no default branch!
980 981 node = repo.lookup('tip') # update to tip
981 982 else:
982 983 raise util.Abort(_("branch %s not found") % wc.branch())
983 984
984 985 if p1.obsolete() and not p1.children():
985 986 # allow updating to successors
986 987 successors = obsolete.successorssets(repo, p1.node())
987 988
988 989 # behavior of certain cases is as follows,
989 990 #
990 991 # divergent changesets: update to highest rev, similar to what
991 992 # is currently done when there are more than one head
992 993 # (i.e. 'tip')
993 994 #
994 995 # replaced changesets: same as divergent except we know there
995 996 # is no conflict
996 997 #
997 998 # pruned changeset: no update is done; though, we could
998 999 # consider updating to the first non-obsolete parent,
999 1000 # similar to what is current done for 'hg prune'
1000 1001
1001 1002 if successors:
1002 1003 # flatten the list here handles both divergent (len > 1)
1003 1004 # and the usual case (len = 1)
1004 1005 successors = [n for sub in successors for n in sub]
1005 1006
1006 1007 # get the max revision for the given successors set,
1007 1008 # i.e. the 'tip' of a set
1008 1009 node = repo.revs('max(%ln)', successors).first()
1009 1010 pas = [p1]
1010 1011
1011 1012 overwrite = force and not branchmerge
1012 1013
1013 1014 p2 = repo[node]
1014 1015 if pas[0] is None:
1015 1016 if repo.ui.config('merge', 'preferancestor', '*') == '*':
1016 1017 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1017 1018 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1018 1019 else:
1019 1020 pas = [p1.ancestor(p2, warn=branchmerge)]
1020 1021
1021 1022 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1022 1023
1023 1024 ### check phase
1024 1025 if not overwrite and len(pl) > 1:
1025 1026 raise util.Abort(_("outstanding uncommitted merge"))
1026 1027 if branchmerge:
1027 1028 if pas == [p2]:
1028 1029 raise util.Abort(_("merging with a working directory ancestor"
1029 1030 " has no effect"))
1030 1031 elif pas == [p1]:
1031 1032 if not mergeancestor and p1.branch() == p2.branch():
1032 1033 raise util.Abort(_("nothing to merge"),
1033 1034 hint=_("use 'hg update' "
1034 1035 "or check 'hg heads'"))
1035 1036 if not force and (wc.files() or wc.deleted()):
1036 1037 raise util.Abort(_("uncommitted changes"),
1037 1038 hint=_("use 'hg status' to list changes"))
1038 1039 for s in sorted(wc.substate):
1039 1040 if wc.sub(s).dirty():
1040 1041 raise util.Abort(_("uncommitted changes in "
1041 1042 "subrepository '%s'") % s)
1042 1043
1043 1044 elif not overwrite:
1044 1045 if p1 == p2: # no-op update
1045 1046 # call the hooks and exit early
1046 1047 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1047 1048 repo.hook('update', parent1=xp2, parent2='', error=0)
1048 1049 return 0, 0, 0, 0
1049 1050
1050 1051 if pas not in ([p1], [p2]): # nonlinear
1051 1052 dirty = wc.dirty(missing=True)
1052 1053 if dirty or onode is None:
1053 1054 # Branching is a bit strange to ensure we do the minimal
1054 1055 # amount of call to obsolete.background.
1055 1056 foreground = obsolete.foreground(repo, [p1.node()])
1056 1057 # note: the <node> variable contains a random identifier
1057 1058 if repo[node].node() in foreground:
1058 1059 pas = [p1] # allow updating to successors
1059 1060 elif dirty:
1060 1061 msg = _("uncommitted changes")
1061 1062 if onode is None:
1062 1063 hint = _("commit and merge, or update --clean to"
1063 1064 " discard changes")
1064 1065 else:
1065 1066 hint = _("commit or update --clean to discard"
1066 1067 " changes")
1067 1068 raise util.Abort(msg, hint=hint)
1068 1069 else: # node is none
1069 1070 msg = _("not a linear update")
1070 1071 hint = _("merge or update --check to force update")
1071 1072 raise util.Abort(msg, hint=hint)
1072 1073 else:
1073 1074 # Allow jumping branches if clean and specific rev given
1074 1075 pas = [p1]
1075 1076
1076 1077 followcopies = False
1077 1078 if overwrite:
1078 1079 pas = [wc]
1079 1080 elif pas == [p2]: # backwards
1080 1081 pas = [wc.p1()]
1081 1082 elif not branchmerge and not wc.dirty(missing=True):
1082 1083 pass
1083 1084 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1084 1085 followcopies = True
1085 1086
1086 1087 ### calculate phase
1087 1088 actions = calculateupdates(repo, wc, p2, pas, branchmerge, force,
1088 1089 partial, mergeancestor, followcopies)
1089 1090
1090 1091 ### apply phase
1091 1092 if not branchmerge: # just jump to the new rev
1092 1093 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1093 1094 if not partial:
1094 1095 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1095 1096 # note that we're in the middle of an update
1096 1097 repo.vfs.write('updatestate', p2.hex())
1097 1098
1098 1099 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1099 1100
1100 1101 if not partial:
1101 1102 repo.dirstate.beginparentchange()
1102 1103 repo.setparents(fp1, fp2)
1103 1104 recordupdates(repo, actions, branchmerge)
1104 1105 # update completed, clear state
1105 1106 util.unlink(repo.join('updatestate'))
1106 1107
1107 1108 if not branchmerge:
1108 1109 repo.dirstate.setbranch(p2.branch())
1109 1110 repo.dirstate.endparentchange()
1110 1111 finally:
1111 1112 wlock.release()
1112 1113
1113 1114 if not partial:
1114 1115 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1115 1116 return stats
1116 1117
1117 1118 def graft(repo, ctx, pctx, labels):
1118 1119 """Do a graft-like merge.
1119 1120
1120 1121 This is a merge where the merge ancestor is chosen such that one
1121 1122 or more changesets are grafted onto the current changeset. In
1122 1123 addition to the merge, this fixes up the dirstate to include only
1123 1124 a single parent and tries to duplicate any renames/copies
1124 1125 appropriately.
1125 1126
1126 1127 ctx - changeset to rebase
1127 1128 pctx - merge base, usually ctx.p1()
1128 1129 labels - merge labels eg ['local', 'graft']
1129 1130
1130 1131 """
1131 1132
1132 1133 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1133 1134 labels=labels)
1134 1135 # drop the second merge parent
1135 1136 repo.dirstate.beginparentchange()
1136 1137 repo.setparents(repo['.'].node(), nullid)
1137 1138 repo.dirstate.write()
1138 1139 # fix up dirstate for copies and renames
1139 1140 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1140 1141 repo.dirstate.endparentchange()
1141 1142 return stats
@@ -1,962 +1,962 b''
1 1
2 2 $ mkdir -p t
3 3 $ cd t
4 4 $ cat <<EOF > merge
5 5 > import sys, os
6 6 > f = open(sys.argv[1], "wb")
7 7 > f.write("merge %s %s %s" % (sys.argv[1], sys.argv[2], sys.argv[3]))
8 8 > f.close()
9 9 > EOF
10 10
11 11 perform a test merge with possible renaming
12 12 args:
13 13 $1 = action in local branch
14 14 $2 = action in remote branch
15 15 $3 = action in working dir
16 16 $4 = expected result
17 17
18 18 $ tm()
19 19 > {
20 20 > hg init t
21 21 > cd t
22 22 > echo "[merge]" >> .hg/hgrc
23 23 > echo "followcopies = 1" >> .hg/hgrc
24 24 >
25 25 > # base
26 26 > echo base > a
27 27 > echo base > rev # used to force commits
28 28 > hg add a rev
29 29 > hg ci -m "base"
30 30 >
31 31 > # remote
32 32 > echo remote > rev
33 33 > if [ "$2" != "" ] ; then $2 ; fi
34 34 > hg ci -m "remote"
35 35 >
36 36 > # local
37 37 > hg co -q 0
38 38 > echo local > rev
39 39 > if [ "$1" != "" ] ; then $1 ; fi
40 40 > hg ci -m "local"
41 41 >
42 42 > # working dir
43 43 > echo local > rev
44 44 > if [ "$3" != "" ] ; then $3 ; fi
45 45 >
46 46 > # merge
47 47 > echo "--------------"
48 48 > echo "test L:$1 R:$2 W:$3 - $4"
49 49 > echo "--------------"
50 50 > hg merge -y --debug --traceback --tool="python ../merge"
51 51 >
52 52 > echo "--------------"
53 53 > hg status -camC -X rev
54 54 >
55 55 > hg ci -m "merge"
56 56 >
57 57 > echo "--------------"
58 58 > echo
59 59 >
60 60 > cd ..
61 61 > rm -r t
62 62 > }
63 63 $ up() {
64 64 > cp rev $1
65 65 > hg add $1 2> /dev/null
66 66 > if [ "$2" != "" ] ; then
67 67 > cp rev $2
68 68 > hg add $2 2> /dev/null
69 69 > fi
70 70 > }
71 71 $ uc() { up $1; hg cp $1 $2; } # update + copy
72 72 $ um() { up $1; hg mv $1 $2; }
73 73 $ nc() { hg cp $1 $2; } # just copy
74 74 $ nm() { hg mv $1 $2; } # just move
75 75 $ tm "up a " "nc a b" " " "1 get local a to b"
76 76 created new head
77 77 --------------
78 78 test L:up a R:nc a b W: - 1 get local a to b
79 79 --------------
80 80 searching for copies back to rev 1
81 81 unmatched files in other:
82 82 b
83 83 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
84 84 src: 'a' -> dst: 'b' *
85 85 checking for directory renames
86 86 resolving manifests
87 87 branchmerge: True, force: False, partial: False
88 88 ancestor: 924404dff337, local: e300d1c794ec+, remote: 4ce40f5aca24
89 89 preserving a for resolve of b
90 90 preserving rev for resolve of rev
91 91 a: keep -> k
92 92 b: remote copied from a -> m
93 93 updating: b 1/2 files (50.00%)
94 94 picked tool 'python ../merge' for b (binary False symlink False)
95 95 merging a and b to b
96 96 my b@e300d1c794ec+ other b@4ce40f5aca24 ancestor a@924404dff337
97 97 premerge successful
98 98 rev: versions differ -> m
99 99 updating: rev 2/2 files (100.00%)
100 100 picked tool 'python ../merge' for rev (binary False symlink False)
101 101 merging rev
102 102 my rev@e300d1c794ec+ other rev@4ce40f5aca24 ancestor rev@924404dff337
103 103 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
104 104 (branch merge, don't forget to commit)
105 105 --------------
106 106 M b
107 107 a
108 108 C a
109 109 --------------
110 110
111 111 $ tm "nc a b" "up a " " " "2 get rem change to a and b"
112 112 created new head
113 113 --------------
114 114 test L:nc a b R:up a W: - 2 get rem change to a and b
115 115 --------------
116 116 searching for copies back to rev 1
117 117 unmatched files in local:
118 118 b
119 119 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
120 120 src: 'a' -> dst: 'b' *
121 121 checking for directory renames
122 122 resolving manifests
123 123 branchmerge: True, force: False, partial: False
124 124 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: f4db7e329e71
125 125 preserving b for resolve of b
126 126 preserving rev for resolve of rev
127 127 a: remote is newer -> g
128 128 getting a
129 129 updating: a 1/3 files (33.33%)
130 130 b: local copied/moved from a -> m
131 131 updating: b 2/3 files (66.67%)
132 132 picked tool 'python ../merge' for b (binary False symlink False)
133 133 merging b and a to b
134 134 my b@86a2aa42fc76+ other a@f4db7e329e71 ancestor a@924404dff337
135 135 premerge successful
136 136 rev: versions differ -> m
137 137 updating: rev 3/3 files (100.00%)
138 138 picked tool 'python ../merge' for rev (binary False symlink False)
139 139 merging rev
140 140 my rev@86a2aa42fc76+ other rev@f4db7e329e71 ancestor rev@924404dff337
141 141 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
142 142 (branch merge, don't forget to commit)
143 143 --------------
144 144 M a
145 145 M b
146 146 a
147 147 --------------
148 148
149 149 $ tm "up a " "nm a b" " " "3 get local a change to b, remove a"
150 150 created new head
151 151 --------------
152 152 test L:up a R:nm a b W: - 3 get local a change to b, remove a
153 153 --------------
154 154 searching for copies back to rev 1
155 155 unmatched files in other:
156 156 b
157 157 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
158 158 src: 'a' -> dst: 'b' *
159 159 checking for directory renames
160 160 resolving manifests
161 161 branchmerge: True, force: False, partial: False
162 162 ancestor: 924404dff337, local: e300d1c794ec+, remote: bdb19105162a
163 163 preserving a for resolve of b
164 164 preserving rev for resolve of rev
165 165 removing a
166 166 b: remote moved from a -> m
167 167 updating: b 1/2 files (50.00%)
168 168 picked tool 'python ../merge' for b (binary False symlink False)
169 169 merging a and b to b
170 170 my b@e300d1c794ec+ other b@bdb19105162a ancestor a@924404dff337
171 171 premerge successful
172 172 rev: versions differ -> m
173 173 updating: rev 2/2 files (100.00%)
174 174 picked tool 'python ../merge' for rev (binary False symlink False)
175 175 merging rev
176 176 my rev@e300d1c794ec+ other rev@bdb19105162a ancestor rev@924404dff337
177 177 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
178 178 (branch merge, don't forget to commit)
179 179 --------------
180 180 M b
181 181 a
182 182 --------------
183 183
184 184 $ tm "nm a b" "up a " " " "4 get remote change to b"
185 185 created new head
186 186 --------------
187 187 test L:nm a b R:up a W: - 4 get remote change to b
188 188 --------------
189 189 searching for copies back to rev 1
190 190 unmatched files in local:
191 191 b
192 192 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
193 193 src: 'a' -> dst: 'b' *
194 194 checking for directory renames
195 195 resolving manifests
196 196 branchmerge: True, force: False, partial: False
197 197 ancestor: 924404dff337, local: 02963e448370+, remote: f4db7e329e71
198 198 preserving b for resolve of b
199 199 preserving rev for resolve of rev
200 200 b: local copied/moved from a -> m
201 201 updating: b 1/2 files (50.00%)
202 202 picked tool 'python ../merge' for b (binary False symlink False)
203 203 merging b and a to b
204 204 my b@02963e448370+ other a@f4db7e329e71 ancestor a@924404dff337
205 205 premerge successful
206 206 rev: versions differ -> m
207 207 updating: rev 2/2 files (100.00%)
208 208 picked tool 'python ../merge' for rev (binary False symlink False)
209 209 merging rev
210 210 my rev@02963e448370+ other rev@f4db7e329e71 ancestor rev@924404dff337
211 211 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
212 212 (branch merge, don't forget to commit)
213 213 --------------
214 214 M b
215 215 a
216 216 --------------
217 217
218 218 $ tm " " "nc a b" " " "5 get b"
219 219 created new head
220 220 --------------
221 221 test L: R:nc a b W: - 5 get b
222 222 --------------
223 223 searching for copies back to rev 1
224 224 unmatched files in other:
225 225 b
226 226 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
227 227 src: 'a' -> dst: 'b'
228 228 checking for directory renames
229 229 resolving manifests
230 230 branchmerge: True, force: False, partial: False
231 231 ancestor: 924404dff337, local: 94b33a1b7f2d+, remote: 4ce40f5aca24
232 232 preserving rev for resolve of rev
233 233 b: remote created -> g
234 234 getting b
235 235 updating: b 1/2 files (50.00%)
236 236 rev: versions differ -> m
237 237 updating: rev 2/2 files (100.00%)
238 238 picked tool 'python ../merge' for rev (binary False symlink False)
239 239 merging rev
240 240 my rev@94b33a1b7f2d+ other rev@4ce40f5aca24 ancestor rev@924404dff337
241 241 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
242 242 (branch merge, don't forget to commit)
243 243 --------------
244 244 M b
245 245 C a
246 246 --------------
247 247
248 248 $ tm "nc a b" " " " " "6 nothing"
249 249 created new head
250 250 --------------
251 251 test L:nc a b R: W: - 6 nothing
252 252 --------------
253 253 searching for copies back to rev 1
254 254 unmatched files in local:
255 255 b
256 256 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
257 257 src: 'a' -> dst: 'b'
258 258 checking for directory renames
259 259 resolving manifests
260 260 branchmerge: True, force: False, partial: False
261 261 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 97c705ade336
262 262 preserving rev for resolve of rev
263 263 rev: versions differ -> m
264 264 updating: rev 1/1 files (100.00%)
265 265 picked tool 'python ../merge' for rev (binary False symlink False)
266 266 merging rev
267 267 my rev@86a2aa42fc76+ other rev@97c705ade336 ancestor rev@924404dff337
268 268 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
269 269 (branch merge, don't forget to commit)
270 270 --------------
271 271 C a
272 272 C b
273 273 --------------
274 274
275 275 $ tm " " "nm a b" " " "7 get b"
276 276 created new head
277 277 --------------
278 278 test L: R:nm a b W: - 7 get b
279 279 --------------
280 280 searching for copies back to rev 1
281 281 unmatched files in other:
282 282 b
283 283 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
284 284 src: 'a' -> dst: 'b'
285 285 checking for directory renames
286 286 resolving manifests
287 287 branchmerge: True, force: False, partial: False
288 288 ancestor: 924404dff337, local: 94b33a1b7f2d+, remote: bdb19105162a
289 289 preserving rev for resolve of rev
290 290 a: other deleted -> r
291 291 removing a
292 292 updating: a 1/3 files (33.33%)
293 293 b: remote created -> g
294 294 getting b
295 295 updating: b 2/3 files (66.67%)
296 296 rev: versions differ -> m
297 297 updating: rev 3/3 files (100.00%)
298 298 picked tool 'python ../merge' for rev (binary False symlink False)
299 299 merging rev
300 300 my rev@94b33a1b7f2d+ other rev@bdb19105162a ancestor rev@924404dff337
301 301 1 files updated, 1 files merged, 1 files removed, 0 files unresolved
302 302 (branch merge, don't forget to commit)
303 303 --------------
304 304 M b
305 305 --------------
306 306
307 307 $ tm "nm a b" " " " " "8 nothing"
308 308 created new head
309 309 --------------
310 310 test L:nm a b R: W: - 8 nothing
311 311 --------------
312 312 searching for copies back to rev 1
313 313 unmatched files in local:
314 314 b
315 315 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
316 316 src: 'a' -> dst: 'b'
317 317 checking for directory renames
318 318 resolving manifests
319 319 branchmerge: True, force: False, partial: False
320 320 ancestor: 924404dff337, local: 02963e448370+, remote: 97c705ade336
321 321 preserving rev for resolve of rev
322 322 rev: versions differ -> m
323 323 updating: rev 1/1 files (100.00%)
324 324 picked tool 'python ../merge' for rev (binary False symlink False)
325 325 merging rev
326 326 my rev@02963e448370+ other rev@97c705ade336 ancestor rev@924404dff337
327 327 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
328 328 (branch merge, don't forget to commit)
329 329 --------------
330 330 C b
331 331 --------------
332 332
333 333 $ tm "um a b" "um a b" " " "9 do merge with ancestor in a"
334 334 created new head
335 335 --------------
336 336 test L:um a b R:um a b W: - 9 do merge with ancestor in a
337 337 --------------
338 338 searching for copies back to rev 1
339 339 unmatched files new in both:
340 340 b
341 341 resolving manifests
342 342 branchmerge: True, force: False, partial: False
343 343 ancestor: 924404dff337, local: 62e7bf090eba+, remote: 49b6d8032493
344 344 preserving b for resolve of b
345 345 preserving rev for resolve of rev
346 b: versions differ -> m
346 b: both created -> m
347 347 updating: b 1/2 files (50.00%)
348 348 picked tool 'python ../merge' for b (binary False symlink False)
349 349 merging b
350 350 my b@62e7bf090eba+ other b@49b6d8032493 ancestor a@924404dff337
351 351 rev: versions differ -> m
352 352 updating: rev 2/2 files (100.00%)
353 353 picked tool 'python ../merge' for rev (binary False symlink False)
354 354 merging rev
355 355 my rev@62e7bf090eba+ other rev@49b6d8032493 ancestor rev@924404dff337
356 356 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
357 357 (branch merge, don't forget to commit)
358 358 --------------
359 359 M b
360 360 --------------
361 361
362 362
363 363 m "um a c" "um x c" " " "10 do merge with no ancestor"
364 364
365 365 $ tm "nm a b" "nm a c" " " "11 get c, keep b"
366 366 created new head
367 367 --------------
368 368 test L:nm a b R:nm a c W: - 11 get c, keep b
369 369 --------------
370 370 searching for copies back to rev 1
371 371 unmatched files in local:
372 372 b
373 373 unmatched files in other:
374 374 c
375 375 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
376 376 src: 'a' -> dst: 'b' !
377 377 src: 'a' -> dst: 'c' !
378 378 checking for directory renames
379 379 resolving manifests
380 380 branchmerge: True, force: False, partial: False
381 381 ancestor: 924404dff337, local: 02963e448370+, remote: fe905ef2c33e
382 382 preserving rev for resolve of rev
383 383 c: remote created -> g
384 384 getting c
385 385 updating: c 1/3 files (33.33%)
386 386 rev: versions differ -> m
387 387 updating: rev 2/3 files (66.67%)
388 388 picked tool 'python ../merge' for rev (binary False symlink False)
389 389 merging rev
390 390 my rev@02963e448370+ other rev@fe905ef2c33e ancestor rev@924404dff337
391 391 a: divergent renames -> dr
392 392 updating: a 3/3 files (100.00%)
393 393 note: possible conflict - a was renamed multiple times to:
394 394 b
395 395 c
396 396 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
397 397 (branch merge, don't forget to commit)
398 398 --------------
399 399 M c
400 400 C b
401 401 --------------
402 402
403 403 $ tm "nc a b" "up b " " " "12 merge b no ancestor"
404 404 created new head
405 405 --------------
406 406 test L:nc a b R:up b W: - 12 merge b no ancestor
407 407 --------------
408 408 searching for copies back to rev 1
409 409 unmatched files new in both:
410 410 b
411 411 resolving manifests
412 412 branchmerge: True, force: False, partial: False
413 413 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: af30c7647fc7
414 414 preserving b for resolve of b
415 415 preserving rev for resolve of rev
416 b: versions differ -> m
416 b: both created -> m
417 417 updating: b 1/2 files (50.00%)
418 418 picked tool 'python ../merge' for b (binary False symlink False)
419 419 merging b
420 420 my b@86a2aa42fc76+ other b@af30c7647fc7 ancestor b@000000000000
421 421 rev: versions differ -> m
422 422 updating: rev 2/2 files (100.00%)
423 423 picked tool 'python ../merge' for rev (binary False symlink False)
424 424 merging rev
425 425 my rev@86a2aa42fc76+ other rev@af30c7647fc7 ancestor rev@924404dff337
426 426 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
427 427 (branch merge, don't forget to commit)
428 428 --------------
429 429 M b
430 430 C a
431 431 --------------
432 432
433 433 $ tm "up b " "nm a b" " " "13 merge b no ancestor"
434 434 created new head
435 435 --------------
436 436 test L:up b R:nm a b W: - 13 merge b no ancestor
437 437 --------------
438 438 searching for copies back to rev 1
439 439 unmatched files new in both:
440 440 b
441 441 resolving manifests
442 442 branchmerge: True, force: False, partial: False
443 443 ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a
444 444 preserving b for resolve of b
445 445 preserving rev for resolve of rev
446 446 a: other deleted -> r
447 447 removing a
448 448 updating: a 1/3 files (33.33%)
449 b: versions differ -> m
449 b: both created -> m
450 450 updating: b 2/3 files (66.67%)
451 451 picked tool 'python ../merge' for b (binary False symlink False)
452 452 merging b
453 453 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
454 454 rev: versions differ -> m
455 455 updating: rev 3/3 files (100.00%)
456 456 picked tool 'python ../merge' for rev (binary False symlink False)
457 457 merging rev
458 458 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
459 459 0 files updated, 2 files merged, 1 files removed, 0 files unresolved
460 460 (branch merge, don't forget to commit)
461 461 --------------
462 462 M b
463 463 --------------
464 464
465 465 $ tm "nc a b" "up a b" " " "14 merge b no ancestor"
466 466 created new head
467 467 --------------
468 468 test L:nc a b R:up a b W: - 14 merge b no ancestor
469 469 --------------
470 470 searching for copies back to rev 1
471 471 unmatched files new in both:
472 472 b
473 473 resolving manifests
474 474 branchmerge: True, force: False, partial: False
475 475 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a
476 476 preserving b for resolve of b
477 477 preserving rev for resolve of rev
478 478 a: remote is newer -> g
479 479 getting a
480 480 updating: a 1/3 files (33.33%)
481 b: versions differ -> m
481 b: both created -> m
482 482 updating: b 2/3 files (66.67%)
483 483 picked tool 'python ../merge' for b (binary False symlink False)
484 484 merging b
485 485 my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
486 486 rev: versions differ -> m
487 487 updating: rev 3/3 files (100.00%)
488 488 picked tool 'python ../merge' for rev (binary False symlink False)
489 489 merging rev
490 490 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
491 491 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
492 492 (branch merge, don't forget to commit)
493 493 --------------
494 494 M a
495 495 M b
496 496 --------------
497 497
498 498 $ tm "up b " "nm a b" " " "15 merge b no ancestor, remove a"
499 499 created new head
500 500 --------------
501 501 test L:up b R:nm a b W: - 15 merge b no ancestor, remove a
502 502 --------------
503 503 searching for copies back to rev 1
504 504 unmatched files new in both:
505 505 b
506 506 resolving manifests
507 507 branchmerge: True, force: False, partial: False
508 508 ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a
509 509 preserving b for resolve of b
510 510 preserving rev for resolve of rev
511 511 a: other deleted -> r
512 512 removing a
513 513 updating: a 1/3 files (33.33%)
514 b: versions differ -> m
514 b: both created -> m
515 515 updating: b 2/3 files (66.67%)
516 516 picked tool 'python ../merge' for b (binary False symlink False)
517 517 merging b
518 518 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
519 519 rev: versions differ -> m
520 520 updating: rev 3/3 files (100.00%)
521 521 picked tool 'python ../merge' for rev (binary False symlink False)
522 522 merging rev
523 523 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
524 524 0 files updated, 2 files merged, 1 files removed, 0 files unresolved
525 525 (branch merge, don't forget to commit)
526 526 --------------
527 527 M b
528 528 --------------
529 529
530 530 $ tm "nc a b" "up a b" " " "16 get a, merge b no ancestor"
531 531 created new head
532 532 --------------
533 533 test L:nc a b R:up a b W: - 16 get a, merge b no ancestor
534 534 --------------
535 535 searching for copies back to rev 1
536 536 unmatched files new in both:
537 537 b
538 538 resolving manifests
539 539 branchmerge: True, force: False, partial: False
540 540 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a
541 541 preserving b for resolve of b
542 542 preserving rev for resolve of rev
543 543 a: remote is newer -> g
544 544 getting a
545 545 updating: a 1/3 files (33.33%)
546 b: versions differ -> m
546 b: both created -> m
547 547 updating: b 2/3 files (66.67%)
548 548 picked tool 'python ../merge' for b (binary False symlink False)
549 549 merging b
550 550 my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
551 551 rev: versions differ -> m
552 552 updating: rev 3/3 files (100.00%)
553 553 picked tool 'python ../merge' for rev (binary False symlink False)
554 554 merging rev
555 555 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
556 556 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
557 557 (branch merge, don't forget to commit)
558 558 --------------
559 559 M a
560 560 M b
561 561 --------------
562 562
563 563 $ tm "up a b" "nc a b" " " "17 keep a, merge b no ancestor"
564 564 created new head
565 565 --------------
566 566 test L:up a b R:nc a b W: - 17 keep a, merge b no ancestor
567 567 --------------
568 568 searching for copies back to rev 1
569 569 unmatched files new in both:
570 570 b
571 571 resolving manifests
572 572 branchmerge: True, force: False, partial: False
573 573 ancestor: 924404dff337, local: 0b76e65c8289+, remote: 4ce40f5aca24
574 574 preserving b for resolve of b
575 575 preserving rev for resolve of rev
576 576 a: keep -> k
577 b: versions differ -> m
577 b: both created -> m
578 578 updating: b 1/2 files (50.00%)
579 579 picked tool 'python ../merge' for b (binary False symlink False)
580 580 merging b
581 581 my b@0b76e65c8289+ other b@4ce40f5aca24 ancestor b@000000000000
582 582 rev: versions differ -> m
583 583 updating: rev 2/2 files (100.00%)
584 584 picked tool 'python ../merge' for rev (binary False symlink False)
585 585 merging rev
586 586 my rev@0b76e65c8289+ other rev@4ce40f5aca24 ancestor rev@924404dff337
587 587 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
588 588 (branch merge, don't forget to commit)
589 589 --------------
590 590 M b
591 591 C a
592 592 --------------
593 593
594 594 $ tm "nm a b" "up a b" " " "18 merge b no ancestor"
595 595 created new head
596 596 --------------
597 597 test L:nm a b R:up a b W: - 18 merge b no ancestor
598 598 --------------
599 599 searching for copies back to rev 1
600 600 unmatched files new in both:
601 601 b
602 602 resolving manifests
603 603 branchmerge: True, force: False, partial: False
604 604 ancestor: 924404dff337, local: 02963e448370+, remote: 8dbce441892a
605 605 remote changed a which local deleted
606 606 use (c)hanged version or leave (d)eleted? c
607 607 preserving b for resolve of b
608 608 preserving rev for resolve of rev
609 609 a: prompt recreating -> g
610 610 getting a
611 611 updating: a 1/3 files (33.33%)
612 b: versions differ -> m
612 b: both created -> m
613 613 updating: b 2/3 files (66.67%)
614 614 picked tool 'python ../merge' for b (binary False symlink False)
615 615 merging b
616 616 my b@02963e448370+ other b@8dbce441892a ancestor b@000000000000
617 617 rev: versions differ -> m
618 618 updating: rev 3/3 files (100.00%)
619 619 picked tool 'python ../merge' for rev (binary False symlink False)
620 620 merging rev
621 621 my rev@02963e448370+ other rev@8dbce441892a ancestor rev@924404dff337
622 622 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
623 623 (branch merge, don't forget to commit)
624 624 --------------
625 625 M a
626 626 M b
627 627 --------------
628 628
629 629 $ tm "up a b" "nm a b" " " "19 merge b no ancestor, prompt remove a"
630 630 created new head
631 631 --------------
632 632 test L:up a b R:nm a b W: - 19 merge b no ancestor, prompt remove a
633 633 --------------
634 634 searching for copies back to rev 1
635 635 unmatched files new in both:
636 636 b
637 637 resolving manifests
638 638 branchmerge: True, force: False, partial: False
639 639 ancestor: 924404dff337, local: 0b76e65c8289+, remote: bdb19105162a
640 640 local changed a which remote deleted
641 641 use (c)hanged version or (d)elete? c
642 642 preserving b for resolve of b
643 643 preserving rev for resolve of rev
644 644 a: prompt keep -> a
645 645 updating: a 1/3 files (33.33%)
646 b: versions differ -> m
646 b: both created -> m
647 647 updating: b 2/3 files (66.67%)
648 648 picked tool 'python ../merge' for b (binary False symlink False)
649 649 merging b
650 650 my b@0b76e65c8289+ other b@bdb19105162a ancestor b@000000000000
651 651 rev: versions differ -> m
652 652 updating: rev 3/3 files (100.00%)
653 653 picked tool 'python ../merge' for rev (binary False symlink False)
654 654 merging rev
655 655 my rev@0b76e65c8289+ other rev@bdb19105162a ancestor rev@924404dff337
656 656 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
657 657 (branch merge, don't forget to commit)
658 658 --------------
659 659 M b
660 660 C a
661 661 --------------
662 662
663 663 $ tm "up a " "um a b" " " "20 merge a and b to b, remove a"
664 664 created new head
665 665 --------------
666 666 test L:up a R:um a b W: - 20 merge a and b to b, remove a
667 667 --------------
668 668 searching for copies back to rev 1
669 669 unmatched files in other:
670 670 b
671 671 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
672 672 src: 'a' -> dst: 'b' *
673 673 checking for directory renames
674 674 resolving manifests
675 675 branchmerge: True, force: False, partial: False
676 676 ancestor: 924404dff337, local: e300d1c794ec+, remote: 49b6d8032493
677 677 preserving a for resolve of b
678 678 preserving rev for resolve of rev
679 679 removing a
680 680 b: remote moved from a -> m
681 681 updating: b 1/2 files (50.00%)
682 682 picked tool 'python ../merge' for b (binary False symlink False)
683 683 merging a and b to b
684 684 my b@e300d1c794ec+ other b@49b6d8032493 ancestor a@924404dff337
685 685 rev: versions differ -> m
686 686 updating: rev 2/2 files (100.00%)
687 687 picked tool 'python ../merge' for rev (binary False symlink False)
688 688 merging rev
689 689 my rev@e300d1c794ec+ other rev@49b6d8032493 ancestor rev@924404dff337
690 690 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
691 691 (branch merge, don't forget to commit)
692 692 --------------
693 693 M b
694 694 a
695 695 --------------
696 696
697 697 $ tm "um a b" "up a " " " "21 merge a and b to b"
698 698 created new head
699 699 --------------
700 700 test L:um a b R:up a W: - 21 merge a and b to b
701 701 --------------
702 702 searching for copies back to rev 1
703 703 unmatched files in local:
704 704 b
705 705 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
706 706 src: 'a' -> dst: 'b' *
707 707 checking for directory renames
708 708 resolving manifests
709 709 branchmerge: True, force: False, partial: False
710 710 ancestor: 924404dff337, local: 62e7bf090eba+, remote: f4db7e329e71
711 711 preserving b for resolve of b
712 712 preserving rev for resolve of rev
713 713 b: local copied/moved from a -> m
714 714 updating: b 1/2 files (50.00%)
715 715 picked tool 'python ../merge' for b (binary False symlink False)
716 716 merging b and a to b
717 717 my b@62e7bf090eba+ other a@f4db7e329e71 ancestor a@924404dff337
718 718 rev: versions differ -> m
719 719 updating: rev 2/2 files (100.00%)
720 720 picked tool 'python ../merge' for rev (binary False symlink False)
721 721 merging rev
722 722 my rev@62e7bf090eba+ other rev@f4db7e329e71 ancestor rev@924404dff337
723 723 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
724 724 (branch merge, don't forget to commit)
725 725 --------------
726 726 M b
727 727 a
728 728 --------------
729 729
730 730
731 731 m "nm a b" "um x a" " " "22 get a, keep b"
732 732
733 733 $ tm "nm a b" "up a c" " " "23 get c, keep b"
734 734 created new head
735 735 --------------
736 736 test L:nm a b R:up a c W: - 23 get c, keep b
737 737 --------------
738 738 searching for copies back to rev 1
739 739 unmatched files in local:
740 740 b
741 741 unmatched files in other:
742 742 c
743 743 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
744 744 src: 'a' -> dst: 'b' *
745 745 checking for directory renames
746 746 resolving manifests
747 747 branchmerge: True, force: False, partial: False
748 748 ancestor: 924404dff337, local: 02963e448370+, remote: 2b958612230f
749 749 preserving b for resolve of b
750 750 preserving rev for resolve of rev
751 751 c: remote created -> g
752 752 getting c
753 753 updating: c 1/3 files (33.33%)
754 754 b: local copied/moved from a -> m
755 755 updating: b 2/3 files (66.67%)
756 756 picked tool 'python ../merge' for b (binary False symlink False)
757 757 merging b and a to b
758 758 my b@02963e448370+ other a@2b958612230f ancestor a@924404dff337
759 759 premerge successful
760 760 rev: versions differ -> m
761 761 updating: rev 3/3 files (100.00%)
762 762 picked tool 'python ../merge' for rev (binary False symlink False)
763 763 merging rev
764 764 my rev@02963e448370+ other rev@2b958612230f ancestor rev@924404dff337
765 765 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
766 766 (branch merge, don't forget to commit)
767 767 --------------
768 768 M b
769 769 a
770 770 M c
771 771 --------------
772 772
773 773
774 774 $ cd ..
775 775
776 776
777 777 Systematic and terse testing of merge merges and ancestor calculation:
778 778
779 779 Expected result:
780 780
781 781 \ a m1 m2 dst
782 782 0 - f f f "versions differ"
783 783 1 f g g g "versions differ"
784 784 2 f f f f "versions differ"
785 785 3 f f g f+g "remote copied to " + f
786 786 4 f f g g "remote moved to " + f
787 787 5 f g f f+g "local copied to " + f2
788 788 6 f g f g "local moved to " + f2
789 789 7 - (f) f f "remote differs from untracked local"
790 790 8 f (f) f f "remote differs from untracked local"
791 791
792 792 $ hg init ancestortest
793 793 $ cd ancestortest
794 794 $ for x in 1 2 3 4 5 6 8; do mkdir $x; echo a > $x/f; done
795 795 $ hg ci -Aqm "a"
796 796 $ mkdir 0
797 797 $ touch 0/f
798 798 $ hg mv 1/f 1/g
799 799 $ hg cp 5/f 5/g
800 800 $ hg mv 6/f 6/g
801 801 $ hg rm 8/f
802 802 $ for x in */*; do echo m1 > $x; done
803 803 $ hg ci -Aqm "m1"
804 804 $ hg up -qr0
805 805 $ mkdir 0 7
806 806 $ touch 0/f 7/f
807 807 $ hg mv 1/f 1/g
808 808 $ hg cp 3/f 3/g
809 809 $ hg mv 4/f 4/g
810 810 $ for x in */*; do echo m2 > $x; done
811 811 $ hg ci -Aqm "m2"
812 812 $ hg up -qr1
813 813 $ mkdir 7 8
814 814 $ echo m > 7/f
815 815 $ echo m > 8/f
816 816 $ hg merge -f --tool internal:dump -v --debug -r2 | sed '/^updating:/,$d' 2> /dev/null
817 817 searching for copies back to rev 1
818 818 unmatched files in local:
819 819 5/g
820 820 6/g
821 821 unmatched files in other:
822 822 3/g
823 823 4/g
824 824 7/f
825 825 unmatched files new in both:
826 826 0/f
827 827 1/g
828 828 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
829 829 src: '3/f' -> dst: '3/g' *
830 830 src: '4/f' -> dst: '4/g' *
831 831 src: '5/f' -> dst: '5/g' *
832 832 src: '6/f' -> dst: '6/g' *
833 833 checking for directory renames
834 834 resolving manifests
835 835 branchmerge: True, force: True, partial: False
836 836 ancestor: e6cb3cf11019, local: ec44bf929ab5+, remote: c62e34d0b898
837 837 remote changed 8/f which local deleted
838 838 use (c)hanged version or leave (d)eleted? c
839 839 preserving 0/f for resolve of 0/f
840 840 preserving 1/g for resolve of 1/g
841 841 preserving 2/f for resolve of 2/f
842 842 preserving 3/f for resolve of 3/f
843 843 preserving 3/f for resolve of 3/g
844 844 preserving 4/f for resolve of 4/g
845 845 preserving 5/f for resolve of 5/f
846 846 preserving 5/g for resolve of 5/g
847 847 preserving 6/g for resolve of 6/g
848 848 preserving 7/f for resolve of 7/f
849 849 removing 4/f
850 850 8/f: prompt recreating -> g
851 851 getting 8/f
852 852 $ hg mani
853 853 0/f
854 854 1/g
855 855 2/f
856 856 3/f
857 857 4/f
858 858 5/f
859 859 5/g
860 860 6/g
861 861 $ for f in */*; do echo $f:; cat $f; done
862 862 0/f:
863 863 m1
864 864 0/f.base:
865 865 0/f.local:
866 866 m1
867 867 0/f.orig:
868 868 m1
869 869 0/f.other:
870 870 m2
871 871 1/g:
872 872 m1
873 873 1/g.base:
874 874 a
875 875 1/g.local:
876 876 m1
877 877 1/g.orig:
878 878 m1
879 879 1/g.other:
880 880 m2
881 881 2/f:
882 882 m1
883 883 2/f.base:
884 884 a
885 885 2/f.local:
886 886 m1
887 887 2/f.orig:
888 888 m1
889 889 2/f.other:
890 890 m2
891 891 3/f:
892 892 m1
893 893 3/f.base:
894 894 a
895 895 3/f.local:
896 896 m1
897 897 3/f.orig:
898 898 m1
899 899 3/f.other:
900 900 m2
901 901 3/g:
902 902 m1
903 903 3/g.base:
904 904 a
905 905 3/g.local:
906 906 m1
907 907 3/g.orig:
908 908 m1
909 909 3/g.other:
910 910 m2
911 911 4/g:
912 912 m1
913 913 4/g.base:
914 914 a
915 915 4/g.local:
916 916 m1
917 917 4/g.orig:
918 918 m1
919 919 4/g.other:
920 920 m2
921 921 5/f:
922 922 m1
923 923 5/f.base:
924 924 a
925 925 5/f.local:
926 926 m1
927 927 5/f.orig:
928 928 m1
929 929 5/f.other:
930 930 m2
931 931 5/g:
932 932 m1
933 933 5/g.base:
934 934 a
935 935 5/g.local:
936 936 m1
937 937 5/g.orig:
938 938 m1
939 939 5/g.other:
940 940 m2
941 941 6/g:
942 942 m1
943 943 6/g.base:
944 944 a
945 945 6/g.local:
946 946 m1
947 947 6/g.orig:
948 948 m1
949 949 6/g.other:
950 950 m2
951 951 7/f:
952 952 m
953 953 7/f.base:
954 954 7/f.local:
955 955 m
956 956 7/f.orig:
957 957 m
958 958 7/f.other:
959 959 m2
960 960 8/f:
961 961 m2
962 962 $ cd ..
@@ -1,241 +1,241 b''
1 1 $ HGMERGE=true; export HGMERGE
2 2
3 3 $ hg init r1
4 4 $ cd r1
5 5 $ echo a > a
6 6 $ hg addremove
7 7 adding a
8 8 $ hg commit -m "1"
9 9
10 10 $ hg clone . ../r2
11 11 updating to branch default
12 12 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 13 $ cd ../r2
14 14 $ hg up
15 15 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
16 16 $ echo abc > a
17 17 $ hg diff --nodates
18 18 diff -r c19d34741b0a a
19 19 --- a/a
20 20 +++ b/a
21 21 @@ -1,1 +1,1 @@
22 22 -a
23 23 +abc
24 24
25 25 $ cd ../r1
26 26 $ echo b > b
27 27 $ echo a2 > a
28 28 $ hg addremove
29 29 adding b
30 30 $ hg commit -m "2"
31 31
32 32 $ cd ../r2
33 33 $ hg -q pull ../r1
34 34 $ hg status
35 35 M a
36 36 $ hg parents
37 37 changeset: 0:c19d34741b0a
38 38 user: test
39 39 date: Thu Jan 01 00:00:00 1970 +0000
40 40 summary: 1
41 41
42 42 $ hg --debug up
43 43 searching for copies back to rev 1
44 44 unmatched files in other:
45 45 b
46 46 resolving manifests
47 47 branchmerge: False, force: False, partial: False
48 48 ancestor: c19d34741b0a, local: c19d34741b0a+, remote: 1e71731e6fbb
49 49 preserving a for resolve of a
50 50 b: remote created -> g
51 51 getting b
52 52 updating: b 1/2 files (50.00%)
53 53 a: versions differ -> m
54 54 updating: a 2/2 files (100.00%)
55 55 picked tool 'true' for a (binary False symlink False)
56 56 merging a
57 57 my a@c19d34741b0a+ other a@1e71731e6fbb ancestor a@c19d34741b0a
58 58 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
59 59 $ hg parents
60 60 changeset: 1:1e71731e6fbb
61 61 tag: tip
62 62 user: test
63 63 date: Thu Jan 01 00:00:00 1970 +0000
64 64 summary: 2
65 65
66 66 $ hg --debug up 0
67 67 resolving manifests
68 68 branchmerge: False, force: False, partial: False
69 69 ancestor: 1e71731e6fbb, local: 1e71731e6fbb+, remote: c19d34741b0a
70 70 preserving a for resolve of a
71 71 b: other deleted -> r
72 72 removing b
73 73 updating: b 1/2 files (50.00%)
74 74 a: versions differ -> m
75 75 updating: a 2/2 files (100.00%)
76 76 picked tool 'true' for a (binary False symlink False)
77 77 merging a
78 78 my a@1e71731e6fbb+ other a@c19d34741b0a ancestor a@1e71731e6fbb
79 79 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
80 80 $ hg parents
81 81 changeset: 0:c19d34741b0a
82 82 user: test
83 83 date: Thu Jan 01 00:00:00 1970 +0000
84 84 summary: 1
85 85
86 86 $ hg --debug merge
87 87 abort: nothing to merge
88 88 (use 'hg update' instead)
89 89 [255]
90 90 $ hg parents
91 91 changeset: 0:c19d34741b0a
92 92 user: test
93 93 date: Thu Jan 01 00:00:00 1970 +0000
94 94 summary: 1
95 95
96 96 $ hg --debug up
97 97 searching for copies back to rev 1
98 98 unmatched files in other:
99 99 b
100 100 resolving manifests
101 101 branchmerge: False, force: False, partial: False
102 102 ancestor: c19d34741b0a, local: c19d34741b0a+, remote: 1e71731e6fbb
103 103 preserving a for resolve of a
104 104 b: remote created -> g
105 105 getting b
106 106 updating: b 1/2 files (50.00%)
107 107 a: versions differ -> m
108 108 updating: a 2/2 files (100.00%)
109 109 picked tool 'true' for a (binary False symlink False)
110 110 merging a
111 111 my a@c19d34741b0a+ other a@1e71731e6fbb ancestor a@c19d34741b0a
112 112 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
113 113 $ hg parents
114 114 changeset: 1:1e71731e6fbb
115 115 tag: tip
116 116 user: test
117 117 date: Thu Jan 01 00:00:00 1970 +0000
118 118 summary: 2
119 119
120 120 $ hg -v history
121 121 changeset: 1:1e71731e6fbb
122 122 tag: tip
123 123 user: test
124 124 date: Thu Jan 01 00:00:00 1970 +0000
125 125 files: a b
126 126 description:
127 127 2
128 128
129 129
130 130 changeset: 0:c19d34741b0a
131 131 user: test
132 132 date: Thu Jan 01 00:00:00 1970 +0000
133 133 files: a
134 134 description:
135 135 1
136 136
137 137
138 138 $ hg diff --nodates
139 139 diff -r 1e71731e6fbb a
140 140 --- a/a
141 141 +++ b/a
142 142 @@ -1,1 +1,1 @@
143 143 -a2
144 144 +abc
145 145
146 146
147 147 create a second head
148 148
149 149 $ cd ../r1
150 150 $ hg up 0
151 151 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
152 152 $ echo b2 > b
153 153 $ echo a3 > a
154 154 $ hg addremove
155 155 adding b
156 156 $ hg commit -m "3"
157 157 created new head
158 158
159 159 $ cd ../r2
160 160 $ hg -q pull ../r1
161 161 $ hg status
162 162 M a
163 163 $ hg parents
164 164 changeset: 1:1e71731e6fbb
165 165 user: test
166 166 date: Thu Jan 01 00:00:00 1970 +0000
167 167 summary: 2
168 168
169 169 $ hg --debug up
170 170 abort: uncommitted changes
171 171 (commit and merge, or update --clean to discard changes)
172 172 [255]
173 173 $ hg --debug merge
174 174 abort: uncommitted changes
175 175 (use 'hg status' to list changes)
176 176 [255]
177 177 $ hg --debug merge -f
178 178 searching for copies back to rev 1
179 179 unmatched files new in both:
180 180 b
181 181 resolving manifests
182 182 branchmerge: True, force: True, partial: False
183 183 ancestor: c19d34741b0a, local: 1e71731e6fbb+, remote: 83c51d0caff4
184 184 preserving a for resolve of a
185 185 preserving b for resolve of b
186 186 a: versions differ -> m
187 187 updating: a 1/2 files (50.00%)
188 188 picked tool 'true' for a (binary False symlink False)
189 189 merging a
190 190 my a@1e71731e6fbb+ other a@83c51d0caff4 ancestor a@c19d34741b0a
191 b: versions differ -> m
191 b: both created -> m
192 192 updating: b 2/2 files (100.00%)
193 193 picked tool 'true' for b (binary False symlink False)
194 194 merging b
195 195 my b@1e71731e6fbb+ other b@83c51d0caff4 ancestor b@000000000000
196 196 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
197 197 (branch merge, don't forget to commit)
198 198 $ hg parents
199 199 changeset: 1:1e71731e6fbb
200 200 user: test
201 201 date: Thu Jan 01 00:00:00 1970 +0000
202 202 summary: 2
203 203
204 204 changeset: 2:83c51d0caff4
205 205 tag: tip
206 206 parent: 0:c19d34741b0a
207 207 user: test
208 208 date: Thu Jan 01 00:00:00 1970 +0000
209 209 summary: 3
210 210
211 211 $ hg diff --nodates
212 212 diff -r 1e71731e6fbb a
213 213 --- a/a
214 214 +++ b/a
215 215 @@ -1,1 +1,1 @@
216 216 -a2
217 217 +abc
218 218
219 219
220 220 test a local add
221 221
222 222 $ cd ..
223 223 $ hg init a
224 224 $ hg init b
225 225 $ echo a > a/a
226 226 $ echo a > b/a
227 227 $ hg --cwd a commit -A -m a
228 228 adding a
229 229 $ cd b
230 230 $ hg add a
231 231 $ hg pull -u ../a
232 232 pulling from ../a
233 233 requesting all changes
234 234 adding changesets
235 235 adding manifests
236 236 adding file changes
237 237 added 1 changesets with 1 changes to 1 files
238 238 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
239 239 $ hg st
240 240
241 241 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now