Fix hg push and hg push -r sometimes creating new heads without --force.
Fixing
issue179.
The algorithm checks if there not more new heads on the remote side than heads
which become non-heads due to getting children.
Pushing this repo:
m
/\
3 3a|
|/ /
2 2a
|/
1
to a repo only having 1, 2 and 3 didn't abort requiring --force before.
Added test cases for this and some doc strings for used methods.
--- a/mercurial/localrepo.py Wed Mar 29 10:31:58 2006 -0800
+++ b/mercurial/localrepo.py Wed Mar 29 22:35:21 2006 +0200
@@ -923,6 +923,14 @@
return fetch.keys()
def findoutgoing(self, remote, base=None, heads=None, force=False):
+ """Return list of nodes that are roots of subsets not in remote
+
+ If base dict is specified, assume that these nodes and their parents
+ exist on the remote side.
+ If a list of heads is specified, return only nodes which are heads
+ or ancestors of these heads, and return a second element which
+ contains all remote heads which get new children.
+ """
if base == None:
base = {}
self.findincoming(remote, base, heads, force=force)
@@ -944,13 +952,23 @@
# find every node whose parents have been pruned
subset = []
+ # find every remote head that will get new children
+ updated_heads = {}
for n in remain:
p1, p2 = self.changelog.parents(n)
if p1 not in remain and p2 not in remain:
subset.append(n)
+ if heads:
+ if p1 in heads:
+ updated_heads[p1] = True
+ if p2 in heads:
+ updated_heads[p2] = True
# this is the set of all roots we have to push
- return subset
+ if heads:
+ return subset, updated_heads.keys()
+ else:
+ return subset
def pull(self, remote, heads=None, force=False):
l = self.lock()
@@ -976,14 +994,15 @@
lock = remote.lock()
base = {}
- heads = remote.heads()
- inc = self.findincoming(remote, base, heads, force=force)
+ remote_heads = remote.heads()
+ inc = self.findincoming(remote, base, remote_heads, force=force)
if not force and inc:
self.ui.warn(_("abort: unsynced remote changes!\n"))
- self.ui.status(_("(did you forget to sync? use push -f to force)\n"))
+ self.ui.status(_("(did you forget to sync?"
+ " use push -f to force)\n"))
return 1
- update = self.findoutgoing(remote, base)
+ update, updated_heads = self.findoutgoing(remote, base, remote_heads)
if revs is not None:
msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
else:
@@ -993,7 +1012,14 @@
self.ui.status(_("no changes found\n"))
return 1
elif not force:
- if len(bases) < len(heads):
+ if revs is not None:
+ updated_heads = {}
+ for base in msng_cl:
+ for parent in self.changelog.parents(base):
+ if parent in remote_heads:
+ updated_heads[parent] = True
+ updated_heads = updated_heads.keys()
+ if len(updated_heads) < len(heads):
self.ui.warn(_("abort: push creates new remote branches!\n"))
self.ui.status(_("(did you forget to merge?"
" use push -f to force)\n"))
--- a/tests/test-push-warn Wed Mar 29 10:31:58 2006 -0800
+++ b/tests/test-push-warn Wed Mar 29 22:35:21 2006 +0200
@@ -26,3 +26,30 @@
hg up -m
hg commit -m "4" -d "1000000 0"
hg push ../a
+cd ..
+
+hg init c
+cd c
+for i in 0 1 2; do
+ echo $i >> foo
+ hg ci -Am $i -d "1000000 0"
+done
+cd ..
+
+hg clone c d
+cd d
+for i in 0 1; do
+ hg co -C $i
+ echo d-$i >> foo
+ hg ci -m d-$i -d "1000000 0"
+done
+
+HGMERGE=true hg co -m 3
+hg ci -m c-d -d "1000000 0"
+
+hg push ../c
+hg push -r 2 ../c
+hg push -r 3 -r 4 ../c
+hg push -r 5 ../c
+
+exit 0
--- a/tests/test-push-warn.out Wed Mar 29 10:31:58 2006 -0800
+++ b/tests/test-push-warn.out Wed Mar 29 22:35:21 2006 +0200
@@ -19,3 +19,20 @@
adding manifests
adding file changes
added 2 changesets with 1 changes to 1 files
+adding foo
+merging foo
+pushing to ../c
+searching for changes
+abort: push creates new remote branches!
+(did you forget to merge? use push -f to force)
+pushing to ../c
+searching for changes
+no changes found
+pushing to ../c
+searching for changes
+abort: push creates new remote branches!
+(did you forget to merge? use push -f to force)
+pushing to ../c
+searching for changes
+abort: push creates new remote branches!
+(did you forget to merge? use push -f to force)