# HG changeset patch # User bos@eng-25.internal.keyresearch.com # Date 1124741203 25200 # Node ID 254ab35709e63870b3e41ae0ce9c6e45f8f9749a # Parent f859e9cba1b92000abe2c7628e0957918be5c32d# Parent ab3939ccbf10d2cce3516645a32c2be415876f88 Merge with MPM. diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -613,12 +613,12 @@ def debugindex(ui, file_): """dump the contents of an index file""" r = hg.revlog(hg.opener(""), file_, "") ui.write(" rev offset length base linkrev" + - " p1 p2 nodeid\n") + " nodeid p1 p2\n") for i in range(r.count()): e = r.index[i] - ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % ( + ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % ( i, e[0], e[1], e[2], e[3], - hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))) + hg.short(e[6]), hg.short(e[4]), hg.short(e[5]))) def debugindexdot(ui, file_): """dump an index DAG as a .dot file""" diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -824,6 +824,7 @@ class localrepository: m1 = self.manifest.read(c1[0]) mf1 = self.manifest.readflags(c1[0]) m2 = self.manifest.read(c2[0]) + changed = [] if orig_parent == p1: update_dirstate = 1 @@ -840,8 +841,31 @@ class localrepository: tm = util.is_exec(self.wjoin(f), mfm.get(f, False)) r = self.file(f) mfm[f] = tm - mm[f] = r.add(t, {}, tr, linkrev, - m1.get(f, nullid), m2.get(f, nullid)) + + fp1 = m1.get(f, nullid) + fp2 = m2.get(f, nullid) + + # is the same revision on two branches of a merge? + if fp2 == fp1: + fp2 = nullid + + if fp2 != nullid: + # is one parent an ancestor of the other? + fpa = r.ancestor(fp1, fp2) + if fpa == fp1: + fp1, fp2 = fp2, nullid + elif fpa == fp2: + fp2 = nullid + + # is the file unmodified from the parent? + if t == r.read(fp1): + # record the proper existing parent in manifest + # no need to add a revision + mm[f] = fp1 + continue + + mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2) + changed.append(f) if update_dirstate: self.dirstate.update([f], "n") except IOError: @@ -856,7 +880,7 @@ class localrepository: mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0]) user = user or self.ui.username() - n = self.changelog.add(mnode, files, text, tr, p1, p2, user, date) + n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date) tr.close() if update_dirstate: self.dirstate.setparents(n, nullid) @@ -865,6 +889,8 @@ class localrepository: match = util.always, force=False): commit = [] remove = [] + changed = [] + if files: for f in files: s = self.dirstate.state(f) @@ -879,19 +905,20 @@ class localrepository: commit = c + a remove = d - if not commit and not remove and not force: + p1, p2 = self.dirstate.parents() + c1 = self.changelog.read(p1) + c2 = self.changelog.read(p2) + m1 = self.manifest.read(c1[0]) + mf1 = self.manifest.readflags(c1[0]) + m2 = self.manifest.read(c2[0]) + + if not commit and not remove and not force and p2 == nullid: self.ui.status("nothing changed\n") return None if not self.hook("precommit"): return None - p1, p2 = self.dirstate.parents() - c1 = self.changelog.read(p1) - c2 = self.changelog.read(p2) - m1 = self.manifest.read(c1[0]) - mf1 = self.manifest.readflags(c1[0]) - m2 = self.manifest.read(c2[0]) lock = self.lock() tr = self.transaction() @@ -918,7 +945,30 @@ class localrepository: r = self.file(f) fp1 = m1.get(f, nullid) fp2 = m2.get(f, nullid) + + # is the same revision on two branches of a merge? + if fp2 == fp1: + fp2 = nullid + + if fp2 != nullid: + # is one parent an ancestor of the other? + fpa = r.ancestor(fp1, fp2) + if fpa == fp1: + fp1, fp2 = fp2, nullid + elif fpa == fp2: + fp2 = nullid + + # is the file unmodified from the parent? + if not meta and t == r.read(fp1): + # record the proper existing parent in manifest + # no need to add a revision + new[f] = fp1 + continue + new[f] = r.add(t, meta, tr, linkrev, fp1, fp2) + # remember what we've added so that we can later calculate + # the files to pull from a set of changesets + changed.append(f) # update manifest m1.update(new) @@ -933,16 +983,21 @@ class localrepository: new.sort() if not text: - edittext = "\n" + "HG: manifest hash %s\n" % hex(mn) - edittext += "".join(["HG: changed %s\n" % f for f in new]) + edittext = "" + if p2 != nullid: + edittext += "HG: branch merge\n" + edittext += "\n" + "HG: manifest hash %s\n" % hex(mn) + edittext += "".join(["HG: changed %s\n" % f for f in changed]) edittext += "".join(["HG: removed %s\n" % f for f in remove]) + if not changed and not remove: + edittext += "HG: no files changed\n" edittext = self.ui.edit(edittext) if not edittext.rstrip(): return None text = edittext user = user or self.ui.username() - n = self.changelog.add(mn, new, text, tr, p1, p2, user, date) + n = self.changelog.add(mn, changed, text, tr, p1, p2, user, date) tr.close() self.dirstate.setparents(n) @@ -967,7 +1022,7 @@ class localrepository: def fcmp(fn, mf): t1 = self.wfile(fn).read() - t2 = self.file(fn).revision(mf[fn]) + t2 = self.file(fn).revision(mf.get(fn, nullid)) return cmp(t1, t2) def mfmatches(node): @@ -1574,7 +1629,6 @@ class localrepository: merge = {} get = {} remove = [] - mark = {} # construct a working dir manifest mw = m1.copy() @@ -1607,7 +1661,6 @@ class localrepository: t1 = self.wfile(f).read() t2 = self.file(f).revision(m2[f]) if cmp(t1, t2) == 0: - mark[f] = 1 n = m2[f] del t1, t2 @@ -1630,8 +1683,6 @@ class localrepository: self.ui.debug(" remote %s is newer, get\n" % f) get[f] = m2[f] s = 1 - else: - mark[f] = 1 elif f in umap: # this unknown file is the same as the checkout get[f] = m2[f] @@ -1646,7 +1697,6 @@ class localrepository: if mode != b: self.ui.debug(" updating permissions for %s\n" % f) util.set_exec(self.wjoin(f), mode) - mark[f] = 1 del m2[f] elif f in ma: if n != ma[f]: @@ -1715,12 +1765,7 @@ class localrepository: self.ui.status("(use update -m to merge across branches" + " or -C to lose changes)\n") return 1 - # we have to remember what files we needed to get/change - # because any file that's different from either one of its - # parents must be in the changeset mode = 'm' - if moddirstate: - self.dirstate.update(mark.keys(), "m") if moddirstate: self.dirstate.setparents(p1, p2) @@ -1739,7 +1784,10 @@ class localrepository: self.wfile(f, "w").write(t) util.set_exec(self.wjoin(f), mf2[f]) if moddirstate: - self.dirstate.update([f], mode) + if mode == 'm': + self.dirstate.update([f], 'n', st_mtime=0) + else: + self.dirstate.update([f], 'n') # merge the tricky bits files = merge.keys() diff --git a/tests/test-filebranch b/tests/test-filebranch new file mode 100755 --- /dev/null +++ b/tests/test-filebranch @@ -0,0 +1,73 @@ +#!/bin/sh + +# This test makes sure that we don't mark a file as merged with its ancestor +# when we do a merge. + +cat <<'EOF' > merge +#!/bin/sh +echo merging for `basename $1` +EOF +chmod +x merge + +echo creating base +hg init a +cd a +echo 1 > foo +echo 1 > bar +echo 1 > baz +echo 1 > quux +hg add foo bar baz quux +hg commit -m "base" -d "0 0" + +cd .. +hg clone a b + +echo creating branch a +cd a +echo 2a > foo +echo 2a > bar +hg commit -m "branch a" -d "0 0" + +echo creating branch b + +cd .. +cd b +echo 2b > foo +echo 2b > baz +hg commit -m "branch b" -d "0 0" + +echo "we shouldn't have anything but n state here" +hg debugstate | cut -b 1-16,35- + +echo merging +hg pull ../a +env HGMERGE=../merge hg update -vm --debug + +echo 2m > foo +echo 2b > baz +echo new > quux + +echo "we shouldn't have anything but foo in merge state here" +hg debugstate | cut -b 1-16,35- | grep "^m" + +hg ci -m "merge" -d "0 0" + +echo "main: we should have a merge here" +hg debugindex .hg/00changelog.i + +echo "foo: we should have a merge here" +hg debugindex .hg/data/foo.i + +echo "bar: we shouldn't have a merge here" +hg debugindex .hg/data/bar.i + +echo "baz: we shouldn't have a merge here" +hg debugindex .hg/data/baz.i + +echo "quux: we shouldn't have a merge here" +hg debugindex .hg/data/quux.i + +echo "everything should be clean now" +hg status + +hg verify diff --git a/tests/test-filebranch.out b/tests/test-filebranch.out new file mode 100644 --- /dev/null +++ b/tests/test-filebranch.out @@ -0,0 +1,58 @@ +creating base +creating branch a +creating branch b +we shouldn't have anything but n state here +n 644 2 bar +n 644 3 baz +n 644 3 foo +n 644 2 quux +merging +pulling from ../a +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 2 changes to 2 files +(run 'hg update' to get a working copy) +merging for foo +resolving manifests + force None allow 1 moddirstate True linear False + ancestor a0486579db29 local ef1b4dbe2193 remote 336d8406d617 + remote bar is newer, get + foo versions differ, resolve +getting bar +merging foo +resolving foo +file foo: other 33d1fb69067a ancestor b8e02f643373 +we shouldn't have anything but foo in merge state here +m 644 3 foo +main: we should have a merge here + rev offset length base linkrev nodeid p1 p2 + 0 0 73 0 0 cdca01651b96 000000000000 000000000000 + 1 73 68 1 1 f6718a9cb7f3 cdca01651b96 000000000000 + 2 141 68 2 2 bdd988058d16 cdca01651b96 000000000000 + 3 209 66 3 3 9da9fbd62226 f6718a9cb7f3 bdd988058d16 +foo: we should have a merge here + rev offset length base linkrev nodeid p1 p2 + 0 0 3 0 0 b8e02f643373 000000000000 000000000000 + 1 3 4 1 1 2ffeddde1b65 b8e02f643373 000000000000 + 2 7 4 2 2 33d1fb69067a b8e02f643373 000000000000 + 3 11 4 3 3 aa27919ee430 2ffeddde1b65 33d1fb69067a +bar: we shouldn't have a merge here + rev offset length base linkrev nodeid p1 p2 + 0 0 3 0 0 b8e02f643373 000000000000 000000000000 + 1 3 4 1 2 33d1fb69067a b8e02f643373 000000000000 +baz: we shouldn't have a merge here + rev offset length base linkrev nodeid p1 p2 + 0 0 3 0 0 b8e02f643373 000000000000 000000000000 + 1 3 4 1 1 2ffeddde1b65 b8e02f643373 000000000000 +quux: we shouldn't have a merge here + rev offset length base linkrev nodeid p1 p2 + 0 0 3 0 0 b8e02f643373 000000000000 000000000000 + 1 3 5 1 3 6128c0f33108 b8e02f643373 000000000000 +everything should be clean now +checking changesets +checking manifests +crosschecking files in changesets and manifests +checking files +4 files, 4 changesets, 10 total revisions diff --git a/tests/test-merge6.out b/tests/test-merge6.out --- a/tests/test-merge6.out +++ b/tests/test-merge6.out @@ -6,7 +6,7 @@ adding file changes added 1 changesets with 1 changes to 1 files (run 'hg update' to get a working copy) bar should remain deleted. -6b70e9e451a5a33faad7bbebe627e46b937b7364 644 foo +f405ac83a5611071d6b54dd5eb26943b1fdc4460 644 foo pulling from ../A2 searching for changes adding changesets @@ -15,4 +15,4 @@ adding file changes added 1 changesets with 0 changes to 0 files (run 'hg update' to get a working copy) bar should remain deleted. -6b70e9e451a5a33faad7bbebe627e46b937b7364 644 foo +f9b0e817f6a48de3564c6b2957687c5e7297c5a0 644 foo diff --git a/tests/test-push-warn.out b/tests/test-push-warn.out --- a/tests/test-push-warn.out +++ b/tests/test-push-warn.out @@ -18,4 +18,4 @@ searching for changes adding changesets adding manifests adding file changes -added 2 changesets with 2 changes to 2 files +added 2 changesets with 1 changes to 1 files diff --git a/tests/test-tags.out b/tests/test-tags.out --- a/tests/test-tags.out +++ b/tests/test-tags.out @@ -10,6 +10,4 @@ acb14030fe0a+ first acb14030fe0a21b60322c440ad2d20cf7685a376+ first M a c8edf04160c7 tip -c8edf04160c7+b9154636be93+ tip -M .hgtags -M a +c8edf04160c7+b9154636be93 tip