27 |
27 |
28 def read(self, node): |
28 def read(self, node): |
29 return self.revision(node) |
29 return self.revision(node) |
30 def add(self, text, transaction, link, p1=None, p2=None): |
30 def add(self, text, transaction, link, p1=None, p2=None): |
31 return self.addrevision(text, transaction, link, p1, p2) |
31 return self.addrevision(text, transaction, link, p1, p2) |
32 |
|
33 def resolvedag(self, old, new, transaction, link): |
|
34 """resolve unmerged heads in our DAG""" |
|
35 if old == new: return None |
|
36 a = self.ancestor(old, new) |
|
37 if old == a: return None |
|
38 return self.merge3(old, new, a, transaction, link) |
|
39 |
|
40 def merge3(self, my, other, base, transaction, link): |
|
41 """perform a 3-way merge and append the result""" |
|
42 def temp(prefix, node): |
|
43 (fd, name) = tempfile.mkstemp(prefix) |
|
44 f = os.fdopen(fd, "w") |
|
45 f.write(self.revision(node)) |
|
46 f.close() |
|
47 return name |
|
48 |
|
49 a = temp("local", my) |
|
50 b = temp("remote", other) |
|
51 c = temp("parent", base) |
|
52 |
|
53 cmd = os.environ["HGMERGE"] |
|
54 r = os.system("%s %s %s %s" % (cmd, a, b, c)) |
|
55 if r: |
|
56 raise "Merge failed, implement rollback!" |
|
57 |
|
58 t = open(a).read() |
|
59 os.unlink(a) |
|
60 os.unlink(b) |
|
61 os.unlink(c) |
|
62 return self.addrevision(t, transaction, link, my, other) |
|
63 |
|
64 def merge(self, other, transaction, linkseq, link): |
|
65 """perform a merge and resolve resulting heads""" |
|
66 (o, n) = self.mergedag(other, transaction, linkseq) |
|
67 return self.resolvedag(o, n, transaction, link) |
|
68 |
32 |
69 def annotate(self, node): |
33 def annotate(self, node): |
70 revs = [] |
34 revs = [] |
71 while node != nullid: |
35 while node != nullid: |
72 revs.append(node) |
36 revs.append(node) |
158 list.sort() |
122 list.sort() |
159 l = [hex(manifest), user, date] + list + ["", desc] |
123 l = [hex(manifest), user, date] + list + ["", desc] |
160 text = "\n".join(l) |
124 text = "\n".join(l) |
161 return self.addrevision(text, transaction, self.count(), p1, p2) |
125 return self.addrevision(text, transaction, self.count(), p1, p2) |
162 |
126 |
163 def merge3(self, my, other, base): |
|
164 pass |
|
165 |
|
166 class dircache: |
127 class dircache: |
167 def __init__(self, opener, ui): |
128 def __init__(self, opener, ui): |
168 self.opener = opener |
129 self.opener = opener |
169 self.dirty = 0 |
130 self.dirty = 0 |
170 self.ui = ui |
131 self.ui = ui |
332 def file(self, f): |
293 def file(self, f): |
333 return filelog(self.opener, f) |
294 return filelog(self.opener, f) |
334 |
295 |
335 def transaction(self): |
296 def transaction(self): |
336 return transaction(self.opener, self.join("journal")) |
297 return transaction(self.opener, self.join("journal")) |
337 |
|
338 def merge(self, other): |
|
339 tr = self.transaction() |
|
340 changed = {} |
|
341 new = {} |
|
342 seqrev = self.changelog.count() |
|
343 # some magic to allow fiddling in nested scope |
|
344 nextrev = [seqrev] |
|
345 |
|
346 # helpers for back-linking file revisions to local changeset |
|
347 # revisions so we can immediately get to changeset from annotate |
|
348 def accumulate(text): |
|
349 # track which files are added in which changeset and the |
|
350 # corresponding _local_ changeset revision |
|
351 files = self.changelog.extract(text)[3] |
|
352 for f in files: |
|
353 changed.setdefault(f, []).append(nextrev[0]) |
|
354 nextrev[0] += 1 |
|
355 |
|
356 def seq(start): |
|
357 while 1: |
|
358 yield start |
|
359 start += 1 |
|
360 |
|
361 def lseq(l): |
|
362 for r in l: |
|
363 yield r |
|
364 |
|
365 # begin the import/merge of changesets |
|
366 self.ui.status("merging new changesets\n") |
|
367 (co, cn) = self.changelog.mergedag(other.changelog, tr, |
|
368 seq(seqrev), accumulate) |
|
369 resolverev = self.changelog.count() |
|
370 |
|
371 # is there anything to do? |
|
372 if co == cn: |
|
373 tr.close() |
|
374 return |
|
375 |
|
376 # do we need to resolve? |
|
377 simple = (co == self.changelog.ancestor(co, cn)) |
|
378 |
|
379 # merge all files changed by the changesets, |
|
380 # keeping track of the new tips |
|
381 changelist = changed.keys() |
|
382 changelist.sort() |
|
383 for f in changelist: |
|
384 sys.stdout.write(".") |
|
385 sys.stdout.flush() |
|
386 r = self.file(f) |
|
387 node = r.merge(other.file(f), tr, lseq(changed[f]), resolverev) |
|
388 if node: |
|
389 new[f] = node |
|
390 sys.stdout.write("\n") |
|
391 |
|
392 # begin the merge of the manifest |
|
393 self.ui.status("merging manifests\n") |
|
394 (mm, mo) = self.manifest.mergedag(other.manifest, tr, seq(seqrev)) |
|
395 |
|
396 # For simple merges, we don't need to resolve manifests or changesets |
|
397 if simple: |
|
398 tr.close() |
|
399 return |
|
400 |
|
401 ma = self.manifest.ancestor(mm, mo) |
|
402 |
|
403 # resolve the manifest to point to all the merged files |
|
404 self.ui.status("resolving manifests\n") |
|
405 omap = self.manifest.read(mo) # other |
|
406 amap = self.manifest.read(ma) # ancestor |
|
407 mmap = self.manifest.read(mm) # mine |
|
408 nmap = {} |
|
409 |
|
410 for f, mid in mmap.iteritems(): |
|
411 if f in omap: |
|
412 if mid != omap[f]: |
|
413 nmap[f] = new.get(f, mid) # use merged version |
|
414 else: |
|
415 nmap[f] = new.get(f, mid) # they're the same |
|
416 del omap[f] |
|
417 elif f in amap: |
|
418 if mid != amap[f]: |
|
419 pass # we should prompt here |
|
420 else: |
|
421 pass # other deleted it |
|
422 else: |
|
423 nmap[f] = new.get(f, mid) # we created it |
|
424 |
|
425 del mmap |
|
426 |
|
427 for f, oid in omap.iteritems(): |
|
428 if f in amap: |
|
429 if oid != amap[f]: |
|
430 pass # this is the nasty case, we should prompt |
|
431 else: |
|
432 pass # probably safe |
|
433 else: |
|
434 nmap[f] = new.get(f, oid) # remote created it |
|
435 |
|
436 del omap |
|
437 del amap |
|
438 |
|
439 node = self.manifest.add(nmap, tr, resolverev, mm, mo) |
|
440 |
|
441 # Now all files and manifests are merged, we add the changed files |
|
442 # and manifest id to the changelog |
|
443 self.ui.status("committing merge changeset\n") |
|
444 new = new.keys() |
|
445 new.sort() |
|
446 if co == cn: cn = -1 |
|
447 |
|
448 edittext = "\n"+"".join(["HG: changed %s\n" % f for f in new]) |
|
449 edittext = self.ui.edit(edittext) |
|
450 n = self.changelog.add(node, new, edittext, tr, co, cn) |
|
451 |
|
452 tr.close() |
|
453 |
298 |
454 def commit(self, parent, update = None, text = ""): |
299 def commit(self, parent, update = None, text = ""): |
455 tr = self.transaction() |
300 tr = self.transaction() |
456 |
301 |
457 try: |
302 try: |
638 return r |
483 return r |
639 |
484 |
640 def newer(self, nodes): |
485 def newer(self, nodes): |
641 m = {} |
486 m = {} |
642 nl = [] |
487 nl = [] |
|
488 pm = {} |
643 cl = self.changelog |
489 cl = self.changelog |
644 t = l = cl.count() |
490 t = l = cl.count() |
|
491 |
|
492 # find the lowest numbered node |
645 for n in nodes: |
493 for n in nodes: |
646 l = min(l, cl.rev(n)) |
494 l = min(l, cl.rev(n)) |
647 for p in cl.parents(n): |
495 m[n] = 1 |
648 m[p] = 1 |
|
649 |
496 |
650 for i in xrange(l, t): |
497 for i in xrange(l, t): |
651 n = cl.node(i) |
498 n = cl.node(i) |
|
499 if n in m: # explicitly listed |
|
500 pm[n] = 1 |
|
501 nl.append(n) |
|
502 continue |
652 for p in cl.parents(n): |
503 for p in cl.parents(n): |
653 if p in m and n not in m: |
504 if p in pm: # parent listed |
654 m[n] = 1 |
505 pm[n] = 1 |
655 nl.append(n) |
506 nl.append(n) |
|
507 break |
656 |
508 |
657 return nl |
509 return nl |
658 |
510 |
659 def getchangegroup(self, remote): |
511 def getchangegroup(self, remote): |
660 tip = remote.branches([])[0] |
512 tip = remote.branches([])[0] |
674 if n == nullid: break |
526 if n == nullid: break |
675 if n[1] and n[1] in m: # do we know the base? |
527 if n[1] and n[1] in m: # do we know the base? |
676 self.ui.debug("found incomplete branch %s\n" % short(n[1])) |
528 self.ui.debug("found incomplete branch %s\n" % short(n[1])) |
677 search.append(n) # schedule branch range for scanning |
529 search.append(n) # schedule branch range for scanning |
678 else: |
530 else: |
|
531 if n[2] in m and n[3] in m: |
|
532 if n[1] not in fetch: |
|
533 self.ui.debug("found new changeset %s\n" % |
|
534 short(n[1])) |
|
535 fetch.append(n[1]) # earliest unknown |
|
536 continue |
679 for b in remote.branches([n[2], n[3]]): |
537 for b in remote.branches([n[2], n[3]]): |
680 if b[0] in m: |
538 if b[0] not in m: |
681 if n[1] not in fetch: |
|
682 self.ui.debug("found new changeset %s\n" % |
|
683 short(n[1])) |
|
684 fetch.append(n[1]) # earliest unknown |
|
685 else: |
|
686 unknown.append(b) |
539 unknown.append(b) |
687 |
540 |
688 while search: |
541 while search: |
689 n = search.pop(0) |
542 n = search.pop(0) |
690 l = remote.between([(n[0], n[1])])[0] |
543 l = remote.between([(n[0], n[1])])[0] |
765 return source.read(l - 4 + add) |
618 return source.read(l - 4 + add) |
766 |
619 |
767 tr = self.transaction() |
620 tr = self.transaction() |
768 simple = True |
621 simple = True |
769 |
622 |
770 self.ui.status("merging changesets\n") |
623 self.ui.status("adding changesets\n") |
771 # pull off the changeset group |
624 # pull off the changeset group |
|
625 def report(x): |
|
626 self.ui.debug("add changeset %s\n" % short(x)) |
|
627 return self.changelog.count() |
|
628 |
772 csg = getchunk() |
629 csg = getchunk() |
773 co = self.changelog.tip() |
630 co = self.changelog.tip() |
774 cn = self.changelog.addgroup(csg, lambda x: self.changelog.count(), tr) |
631 cn = self.changelog.addgroup(csg, report, tr) |
775 |
632 |
776 self.ui.status("merging manifests\n") |
633 self.ui.status("adding manifests\n") |
777 # pull off the manifest group |
634 # pull off the manifest group |
778 mfg = getchunk() |
635 mfg = getchunk() |
779 mm = self.manifest.tip() |
636 mm = self.manifest.tip() |
780 mo = self.manifest.addgroup(mfg, lambda x: self.changelog.rev(x), tr) |
637 mo = self.manifest.addgroup(mfg, lambda x: self.changelog.rev(x), tr) |
781 |
638 |
783 if self.changelog.ancestor(co, cn) != co: |
640 if self.changelog.ancestor(co, cn) != co: |
784 simple = False |
641 simple = False |
785 resolverev = self.changelog.count() |
642 resolverev = self.changelog.count() |
786 |
643 |
787 # process the files |
644 # process the files |
788 self.ui.status("merging files\n") |
645 self.ui.status("adding files\n") |
789 new = {} |
646 new = {} |
790 while 1: |
647 while 1: |
791 f = getchunk(4) |
648 f = getchunk(4) |
792 if not f: break |
649 if not f: break |
793 fg = getchunk() |
650 fg = getchunk() |
794 |
651 self.ui.debug("adding %s revisions\n" % f) |
795 fl = self.file(f) |
652 fl = self.file(f) |
796 o = fl.tip() |
653 o = fl.tip() |
797 n = fl.addgroup(fg, lambda x: self.changelog.rev(x), tr) |
654 n = fl.addgroup(fg, lambda x: self.changelog.rev(x), tr) |
798 if not simple: |
655 if not simple: |
799 nn = fl.resolvedag(o, n, tr, resolverev) |
656 if o == n: continue |
800 if nn: |
657 # this file has changed between branches, so it must be |
801 self.ui.note("merged %s\n", f) |
658 # represented in the merge changeset |
802 new[f] = nn |
659 new[f] = self.merge3(fl, f, o, n, tr, resolverev) |
803 |
660 |
804 # For simple merges, we don't need to resolve manifests or changesets |
661 # For simple merges, we don't need to resolve manifests or changesets |
805 if simple: |
662 if simple: |
806 self.ui.debug("simple merge, skipping resolve\n") |
663 self.ui.debug("simple merge, skipping resolve\n") |
807 tr.close() |
664 tr.close() |