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 |
|
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: | |
|
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: | |
@@ -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(" |
|
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(" |
|
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, |
|
631 | cn = self.changelog.addgroup(csg, report, tr) | |
775 |
|
632 | |||
776 |
self.ui.status(" |
|
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(" |
|
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. |
|
681 | if f in new: self.ui.debug("%s updated in resolve\n" % f) | |
825 |
|
|
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