##// END OF EJS Templates
Refactor merge code...
mpm@selenic.com -
r94:7daef883 default
parent child Browse files
Show More
@@ -30,42 +30,6 b' class filelog(revlog):'
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
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
69 def annotate(self, node):
33 def annotate(self, node):
70 revs = []
34 revs = []
71 while node != nullid:
35 while node != nullid:
@@ -160,9 +124,6 b' class changelog(revlog):'
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
@@ -335,122 +296,6 b' class localrepository:'
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
298
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
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
@@ -640,19 +485,26 b' class localrepository:'
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
@@ -676,13 +528,14 b' class localrepository:'
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:
679 for b in remote.branches([n[2], n[3]]):
531 if n[2] in m and n[3] in m:
680 if b[0] in m:
681 if n[1] not in fetch:
532 if n[1] not in fetch:
682 self.ui.debug("found new changeset %s\n" %
533 self.ui.debug("found new changeset %s\n" %
683 short(n[1]))
534 short(n[1]))
684 fetch.append(n[1]) # earliest unknown
535 fetch.append(n[1]) # earliest unknown
685 else:
536 continue
537 for b in remote.branches([n[2], n[3]]):
538 if b[0] not in m:
686 unknown.append(b)
539 unknown.append(b)
687
540
688 while search:
541 while search:
@@ -707,7 +560,7 b' class localrepository:'
707 if f in m:
560 if f in m:
708 raise "already have", short(f[:4])
561 raise "already have", short(f[:4])
709
562
710 self.ui.note("merging new changesets starting at " +
563 self.ui.note("adding new changesets starting at " +
711 " ".join([short(f) for f in fetch]) + "\n")
564 " ".join([short(f) for f in fetch]) + "\n")
712
565
713 return remote.changegroup(fetch)
566 return remote.changegroup(fetch)
@@ -767,13 +620,17 b' class localrepository:'
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()
@@ -785,21 +642,21 b' class localrepository:'
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:
@@ -821,8 +678,9 b' class localrepository:'
821 if f in omap:
678 if f in omap:
822 if mid != omap[f]:
679 if mid != omap[f]:
823 self.ui.debug("%s versions differ\n" % f)
680 self.ui.debug("%s versions differ\n" % f)
824 if f in new: self.ui.note("%s updated in resolve\n" % f)
681 if f in new: self.ui.debug("%s updated in resolve\n" % f)
825 nmap[f] = new.get(f, mid) # use merged version
682 # use merged version or local version
683 nmap[f] = new.get(f, mid)
826 else:
684 else:
827 nmap[f] = mid # keep ours
685 nmap[f] = mid # keep ours
828 del omap[f]
686 del omap[f]
@@ -143,12 +143,6 b' class revlog:'
143
143
144 return None
144 return None
145
145
146 def revisions(self, list):
147 # this can be optimized to do spans, etc
148 # be stupid for now
149 for node in list:
150 yield self.revision(node)
151
152 def diff(self, a, b):
146 def diff(self, a, b):
153 return mdiff.textdiff(a, b)
147 return mdiff.textdiff(a, b)
154
148
@@ -272,34 +266,9 b' class revlog:'
272
266
273 return nullid
267 return nullid
274
268
275 def mergedag(self, other, transaction, linkseq, accumulate = None):
276 """combine the nodes from other's DAG into ours"""
277 old = self.tip()
278 i = self.count()
279 l = []
280
281 # merge the other revision log into our DAG
282 for r in range(other.count()):
283 id = other.node(r)
284 if id not in self.nodemap:
285 (xn, yn) = other.parents(id)
286 l.append((id, xn, yn))
287 self.nodemap[id] = i
288 i += 1
289
290 # merge node date for new nodes
291 r = other.revisions([e[0] for e in l])
292 for e in l:
293 t = r.next()
294 if accumulate: accumulate(t)
295 self.addrevision(t, transaction, linkseq.next(), e[1], e[2])
296
297 # return the unmerged heads for later resolving
298 return (old, self.tip())
299
300 def group(self, linkmap):
269 def group(self, linkmap):
301 # given a list of changeset revs, return a set of deltas and
270 # given a list of changeset revs, return a set of deltas and
302 # metadata corresponding to nodes the first delta is
271 # metadata corresponding to nodes. the first delta is
303 # parent(nodes[0]) -> nodes[0] the receiver is guaranteed to
272 # parent(nodes[0]) -> nodes[0] the receiver is guaranteed to
304 # have this parent as it has all history before these
273 # have this parent as it has all history before these
305 # changesets. parent is parent[0]
274 # changesets. parent is parent[0]
@@ -440,9 +409,9 b' class revlog:'
440 while pos < len(data):
409 while pos < len(data):
441 l, node, p1, p2, cs = struct.unpack(">l20s20s20s20s",
410 l, node, p1, p2, cs = struct.unpack(">l20s20s20s20s",
442 data[pos:pos+84])
411 data[pos:pos+84])
412 link = linkmapper(cs)
443 if node in self.nodemap:
413 if node in self.nodemap:
444 raise "already have %s" % hex(node[:4])
414 raise "already have %s" % hex(node[:4])
445 link = linkmapper(cs)
446 delta = data[pos + 84:pos + l]
415 delta = data[pos + 84:pos + l]
447 pos += l
416 pos += l
448
417
General Comments 0
You need to be logged in to leave comments. Login now