diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -30,42 +30,6 @@ class filelog(revlog): def add(self, text, transaction, link, p1=None, p2=None): return self.addrevision(text, transaction, link, p1, p2) - def resolvedag(self, old, new, transaction, link): - """resolve unmerged heads in our DAG""" - if old == new: return None - a = self.ancestor(old, new) - if old == a: return None - return self.merge3(old, new, a, transaction, link) - - def merge3(self, my, other, base, transaction, link): - """perform a 3-way merge and append the result""" - def temp(prefix, node): - (fd, name) = tempfile.mkstemp(prefix) - f = os.fdopen(fd, "w") - f.write(self.revision(node)) - f.close() - return name - - a = temp("local", my) - b = temp("remote", other) - c = temp("parent", base) - - cmd = os.environ["HGMERGE"] - r = os.system("%s %s %s %s" % (cmd, a, b, c)) - if r: - raise "Merge failed, implement rollback!" - - t = open(a).read() - os.unlink(a) - os.unlink(b) - os.unlink(c) - return self.addrevision(t, transaction, link, my, other) - - def merge(self, other, transaction, linkseq, link): - """perform a merge and resolve resulting heads""" - (o, n) = self.mergedag(other, transaction, linkseq) - return self.resolvedag(o, n, transaction, link) - def annotate(self, node): revs = [] while node != nullid: @@ -160,9 +124,6 @@ class changelog(revlog): text = "\n".join(l) return self.addrevision(text, transaction, self.count(), p1, p2) - def merge3(self, my, other, base): - pass - class dircache: def __init__(self, opener, ui): self.opener = opener @@ -335,122 +296,6 @@ class localrepository: def transaction(self): return transaction(self.opener, self.join("journal")) - def merge(self, other): - tr = self.transaction() - changed = {} - new = {} - seqrev = self.changelog.count() - # some magic to allow fiddling in nested scope - nextrev = [seqrev] - - # helpers for back-linking file revisions to local changeset - # revisions so we can immediately get to changeset from annotate - def accumulate(text): - # track which files are added in which changeset and the - # corresponding _local_ changeset revision - files = self.changelog.extract(text)[3] - for f in files: - changed.setdefault(f, []).append(nextrev[0]) - nextrev[0] += 1 - - def seq(start): - while 1: - yield start - start += 1 - - def lseq(l): - for r in l: - yield r - - # begin the import/merge of changesets - self.ui.status("merging new changesets\n") - (co, cn) = self.changelog.mergedag(other.changelog, tr, - seq(seqrev), accumulate) - resolverev = self.changelog.count() - - # is there anything to do? - if co == cn: - tr.close() - return - - # do we need to resolve? - simple = (co == self.changelog.ancestor(co, cn)) - - # merge all files changed by the changesets, - # keeping track of the new tips - changelist = changed.keys() - changelist.sort() - for f in changelist: - sys.stdout.write(".") - sys.stdout.flush() - r = self.file(f) - node = r.merge(other.file(f), tr, lseq(changed[f]), resolverev) - if node: - new[f] = node - sys.stdout.write("\n") - - # begin the merge of the manifest - self.ui.status("merging manifests\n") - (mm, mo) = self.manifest.mergedag(other.manifest, tr, seq(seqrev)) - - # For simple merges, we don't need to resolve manifests or changesets - if simple: - tr.close() - return - - ma = self.manifest.ancestor(mm, mo) - - # resolve the manifest to point to all the merged files - self.ui.status("resolving manifests\n") - omap = self.manifest.read(mo) # other - amap = self.manifest.read(ma) # ancestor - mmap = self.manifest.read(mm) # mine - nmap = {} - - for f, mid in mmap.iteritems(): - if f in omap: - if mid != omap[f]: - nmap[f] = new.get(f, mid) # use merged version - else: - nmap[f] = new.get(f, mid) # they're the same - del omap[f] - elif f in amap: - if mid != amap[f]: - pass # we should prompt here - else: - pass # other deleted it - else: - nmap[f] = new.get(f, mid) # we created it - - del mmap - - for f, oid in omap.iteritems(): - if f in amap: - if oid != amap[f]: - pass # this is the nasty case, we should prompt - else: - pass # probably safe - else: - nmap[f] = new.get(f, oid) # remote created it - - del omap - del amap - - node = self.manifest.add(nmap, tr, resolverev, mm, mo) - - # Now all files and manifests are merged, we add the changed files - # and manifest id to the changelog - self.ui.status("committing merge changeset\n") - new = new.keys() - new.sort() - if co == cn: cn = -1 - - edittext = "\n"+"".join(["HG: changed %s\n" % f for f in new]) - edittext = self.ui.edit(edittext) - n = self.changelog.add(node, new, edittext, tr, co, cn) - - tr.close() - def commit(self, parent, update = None, text = ""): tr = self.transaction() @@ -640,19 +485,26 @@ class localrepository: def newer(self, nodes): m = {} nl = [] + pm = {} cl = self.changelog t = l = cl.count() + + # find the lowest numbered node for n in nodes: l = min(l, cl.rev(n)) - for p in cl.parents(n): - m[p] = 1 + m[n] = 1 for i in xrange(l, t): n = cl.node(i) + if n in m: # explicitly listed + pm[n] = 1 + nl.append(n) + continue for p in cl.parents(n): - if p in m and n not in m: - m[n] = 1 + if p in pm: # parent listed + pm[n] = 1 nl.append(n) + break return nl @@ -676,13 +528,14 @@ class localrepository: self.ui.debug("found incomplete branch %s\n" % short(n[1])) search.append(n) # schedule branch range for scanning else: + if n[2] in m and n[3] in m: + if n[1] not in fetch: + self.ui.debug("found new changeset %s\n" % + short(n[1])) + fetch.append(n[1]) # earliest unknown + continue for b in remote.branches([n[2], n[3]]): - if b[0] in m: - if n[1] not in fetch: - self.ui.debug("found new changeset %s\n" % - short(n[1])) - fetch.append(n[1]) # earliest unknown - else: + if b[0] not in m: unknown.append(b) while search: @@ -707,7 +560,7 @@ class localrepository: if f in m: raise "already have", short(f[:4]) - self.ui.note("merging new changesets starting at " + + self.ui.note("adding new changesets starting at " + " ".join([short(f) for f in fetch]) + "\n") return remote.changegroup(fetch) @@ -767,13 +620,17 @@ class localrepository: tr = self.transaction() simple = True - self.ui.status("merging changesets\n") + self.ui.status("adding changesets\n") # pull off the changeset group + def report(x): + self.ui.debug("add changeset %s\n" % short(x)) + return self.changelog.count() + csg = getchunk() co = self.changelog.tip() - cn = self.changelog.addgroup(csg, lambda x: self.changelog.count(), tr) + cn = self.changelog.addgroup(csg, report, tr) - self.ui.status("merging manifests\n") + self.ui.status("adding manifests\n") # pull off the manifest group mfg = getchunk() mm = self.manifest.tip() @@ -785,21 +642,21 @@ class localrepository: resolverev = self.changelog.count() # process the files - self.ui.status("merging files\n") + self.ui.status("adding files\n") new = {} while 1: f = getchunk(4) if not f: break fg = getchunk() - + self.ui.debug("adding %s revisions\n" % f) fl = self.file(f) o = fl.tip() n = fl.addgroup(fg, lambda x: self.changelog.rev(x), tr) if not simple: - nn = fl.resolvedag(o, n, tr, resolverev) - if nn: - self.ui.note("merged %s\n", f) - new[f] = nn + if o == n: continue + # this file has changed between branches, so it must be + # represented in the merge changeset + new[f] = self.merge3(fl, f, o, n, tr, resolverev) # For simple merges, we don't need to resolve manifests or changesets if simple: @@ -821,8 +678,9 @@ class localrepository: if f in omap: if mid != omap[f]: self.ui.debug("%s versions differ\n" % f) - if f in new: self.ui.note("%s updated in resolve\n" % f) - nmap[f] = new.get(f, mid) # use merged version + if f in new: self.ui.debug("%s updated in resolve\n" % f) + # use merged version or local version + nmap[f] = new.get(f, mid) else: nmap[f] = mid # keep ours del omap[f] diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -143,12 +143,6 @@ class revlog: return None - def revisions(self, list): - # this can be optimized to do spans, etc - # be stupid for now - for node in list: - yield self.revision(node) - def diff(self, a, b): return mdiff.textdiff(a, b) @@ -272,34 +266,9 @@ class revlog: return nullid - def mergedag(self, other, transaction, linkseq, accumulate = None): - """combine the nodes from other's DAG into ours""" - old = self.tip() - i = self.count() - l = [] - - # merge the other revision log into our DAG - for r in range(other.count()): - id = other.node(r) - if id not in self.nodemap: - (xn, yn) = other.parents(id) - l.append((id, xn, yn)) - self.nodemap[id] = i - i += 1 - - # merge node date for new nodes - r = other.revisions([e[0] for e in l]) - for e in l: - t = r.next() - if accumulate: accumulate(t) - self.addrevision(t, transaction, linkseq.next(), e[1], e[2]) - - # return the unmerged heads for later resolving - return (old, self.tip()) - def group(self, linkmap): # given a list of changeset revs, return a set of deltas and - # metadata corresponding to nodes the first delta is + # metadata corresponding to nodes. the first delta is # parent(nodes[0]) -> nodes[0] the receiver is guaranteed to # have this parent as it has all history before these # changesets. parent is parent[0] @@ -440,9 +409,9 @@ class revlog: while pos < len(data): l, node, p1, p2, cs = struct.unpack(">l20s20s20s20s", data[pos:pos+84]) + link = linkmapper(cs) if node in self.nodemap: raise "already have %s" % hex(node[:4]) - link = linkmapper(cs) delta = data[pos + 84:pos + l] pos += l