[PATCH 1 of 1] Handle prepush logic for named branches
Henrik Stuart
henrik.stuart at edlund.dk
Tue May 19 01:31:10 CDT 2009
# HG changeset patch
# User Sune Foldager <cryo at cyanite.org>
# Date 1241528982 -7200
# Node ID 94ac3cbde1772b46ed4b86bfe140b42b73d4b784
# Parent 252232621165917755727729c7f0b9a1f1263668
Handle prepush logic for named branches
diff -r 252232621165 -r 94ac3cbde177 mercurial/hgweb/protocol.py
--- a/mercurial/hgweb/protocol.py Tue May 19 01:37:38 2009 +0200
+++ b/mercurial/hgweb/protocol.py Tue May 05 15:09:42 2009 +0200
@@ -17,6 +17,7 @@
__all__ = [
'lookup', 'heads', 'branches', 'between', 'changegroup',
'changegroupsubset', 'capabilities', 'unbundle', 'stream_out',
+ 'branchmap',
]
HGTYPE = 'application/mercurial-0.1'
@@ -37,6 +38,12 @@
req.respond(HTTP_OK, HGTYPE, length=len(resp))
yield resp
+def branchmap(repo, req):
+ branches = repo.branchmap()
+ resp = "\n".join(['%s %s' % (b, " ".join([hex(x) for x in branches[b]])) for b in branches])
+ req.respond(HTTP_OK, HGTYPE, length=len(resp))
+ yield resp
+
def branches(repo, req):
nodes = []
if 'nodes' in req.form:
@@ -97,7 +104,7 @@
yield z.flush()
def capabilities(repo, req):
- caps = ['lookup', 'changegroupsubset']
+ caps = ['lookup', 'changegroupsubset', 'branchmap']
if repo.ui.configbool('server', 'uncompressed', untrusted=True):
caps.append('stream=%d' % repo.changelog.version)
if changegroupmod.bundlepriority:
diff -r 252232621165 -r 94ac3cbde177 mercurial/httprepo.py
--- a/mercurial/httprepo.py Tue May 19 01:37:38 2009 +0200
+++ b/mercurial/httprepo.py Tue May 05 15:09:42 2009 +0200
@@ -145,6 +145,19 @@
except:
raise error.ResponseError(_("unexpected response:"), d)
+ def branchmap(self):
+ d = self.do_read("branchmap")
+ try:
+ branchmap = {}
+ for branchpart in d.splitlines():
+ branchheads = branchpart.split(' ')
+ branchname = branchheads[0]
+ branchheads = [bin(x) for x in branchheads[1:]]
+ branchmap[branchname] = branchheads
+ return branchmap
+ except:
+ raise error.ResponseError(_("unexpected response:"), d)
+
def branches(self, nodes):
n = " ".join(map(hex, nodes))
d = self.do_read("branches", nodes=n)
diff -r 252232621165 -r 94ac3cbde177 mercurial/localrepo.py
--- a/mercurial/localrepo.py Tue May 19 01:37:38 2009 +0200
+++ b/mercurial/localrepo.py Tue May 05 15:09:42 2009 +0200
@@ -18,7 +18,7 @@
propertycache = util.propertycache
class localrepository(repo.repository):
- capabilities = set(('lookup', 'changegroupsubset'))
+ capabilities = set(('lookup', 'changegroupsubset', 'branchmap'))
supported = set('revlogv1 store fncache'.split())
def __init__(self, baseui, path=None, create=0):
@@ -360,6 +360,9 @@
return partial
+ def branchmap(self):
+ return self._branchheads()
+
def _branchheads(self):
tip = self.changelog.tip()
if self.branchcache is not None and self._branchcachetip == tip:
@@ -1429,38 +1432,77 @@
self.ui.status(_("no changes found\n"))
return None, 1
elif not force:
- # check if we're creating new remote heads
- # to be a remote head after push, node must be either
+ # Check for each named branch if we're creating new remote heads.
+ # To be a remote head after push, node must be either:
# - unknown locally
# - a local outgoing head descended from update
# - a remote head that's known locally and not
# ancestral to an outgoing head
+ #
+ # New named branches cannot be created without --force.
- warn = 0
+ if remote_heads != [nullid]:
+ def creates_no_new_heads(lheads, rheads, updatelh):
+ warn = 0
- if remote_heads == [nullid]:
- warn = 0
- elif not revs and len(heads) > len(remote_heads):
- warn = 1
- else:
- newheads = list(heads)
- for r in remote_heads:
- if r in self.changelog.nodemap:
- desc = self.changelog.heads(r, heads)
- l = [h for h in heads if h in desc]
- if not l:
- newheads.append(r)
+ if not revs and len(lheads) > len(rheads):
+ warn = 1
else:
- newheads.append(r)
- if len(newheads) > len(remote_heads):
- warn = 1
+ newheads = set(sum([self.changelog.heads(x, lheads) for x in updatelh], []))
+ newheads &= set(lheads)
- if warn:
- self.ui.warn(_("abort: push creates new remote heads!\n"))
- self.ui.status(_("(did you forget to merge?"
- " use push -f to force)\n"))
- return None, 0
- elif inc:
+ if not newheads:
+ return True
+
+ for r in rheads:
+ if r in self.changelog.nodemap:
+ desc = self.changelog.heads(r, heads)
+ l = [h for h in heads if h in desc]
+ if not l:
+ newheads.add(r)
+ else:
+ newheads.add(r)
+ if len(newheads) > len(rheads):
+ warn = 1
+
+ if warn:
+ if not rheads: # new branch requires --force
+ self.ui.warn(_("abort: push creates new remote branch '%s'!\n" % self[updatelh[0]].branch()))
+ else:
+ self.ui.warn(_("abort: push creates new remote heads!\n"))
+
+ self.ui.status(_("(did you forget to merge?"
+ " use push -f to force)\n"))
+ return False
+ return True
+
+ if remote.capable('branchmap'):
+ local_hds = {}
+ if not revs:
+ local_hds = self.branchmap()
+ else:
+ for n in heads:
+ branch = self[n].branch()
+ if local_hds.has_key(branch):
+ local_hds[branch].append(n)
+ else:
+ local_hds[branch] = [n]
+
+ remote_hds = remote.branchmap()
+
+ for lh in local_hds:
+ rheads = remote_hds[lh] if remote_hds.has_key(lh) else []
+ lheads = local_hds[lh]
+ updatelh = [upd for upd in update if self[upd].branch() == lh]
+ if not updatelh:
+ continue
+ if not creates_no_new_heads(lheads, rheads, updatelh):
+ return None, 0
+ else:
+ if not creates_no_new_heads(heads, remote_heads, update):
+ return None, 0
+
+ if inc:
self.ui.warn(_("note: unsynced remote changes!\n"))
diff -r 252232621165 -r 94ac3cbde177 mercurial/sshrepo.py
--- a/mercurial/sshrepo.py Tue May 19 01:37:38 2009 +0200
+++ b/mercurial/sshrepo.py Tue May 05 15:09:42 2009 +0200
@@ -166,6 +166,19 @@
except:
self.abort(error.ResponseError(_("unexpected response:"), d))
+ def branchmap(self):
+ d = self.call("branchmap")
+ try:
+ branchmap = {}
+ for branchpart in d.splitlines():
+ branchheads = branchpart.split(' ')
+ branchname = branchheads[0]
+ branchheads = [bin(x) for x in branchheads[1:]]
+ branchmap[branchname] = branchheads
+ return branchmap
+ except:
+ raise error.ResponseError(_("unexpected response:"), d)
+
def branches(self, nodes):
n = " ".join(map(hex, nodes))
d = self.call("branches", nodes=n)
diff -r 252232621165 -r 94ac3cbde177 mercurial/sshserver.py
--- a/mercurial/sshserver.py Tue May 19 01:37:38 2009 +0200
+++ b/mercurial/sshserver.py Tue May 05 15:09:42 2009 +0200
@@ -64,6 +64,10 @@
success = 0
self.respond("%s %s\n" % (success, r))
+ def do_branchmap(self):
+ h = self.repo.branchmap()
+ self.respond("\n".join(['%s %s' % (b, " ".join([hex(x) for x in h[b]])) for b in h]))
+
def do_heads(self):
h = self.repo.heads()
self.respond(" ".join(map(hex, h)) + "\n")
@@ -77,7 +81,7 @@
capabilities: space separated list of tokens
'''
- caps = ['unbundle', 'lookup', 'changegroupsubset']
+ caps = ['unbundle', 'lookup', 'changegroupsubset', 'branchmap']
if self.ui.configbool('server', 'uncompressed'):
caps.append('stream=%d' % self.repo.changelog.version)
self.respond("capabilities: %s\n" % (' '.join(caps),))
diff -r 252232621165 -r 94ac3cbde177 tests/test-acl.out
--- a/tests/test-acl.out Tue May 19 01:37:38 2009 +0200
+++ b/tests/test-acl.out Tue May 05 15:09:42 2009 +0200
@@ -42,6 +42,7 @@
pushing to ../b
searching for changes
common changesets up to 6675d58eff77
+invalidating branch cache (tip differs)
3 changesets found
list of changesets:
ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -74,6 +75,7 @@
pushing to ../b
searching for changes
common changesets up to 6675d58eff77
+invalidating branch cache (tip differs)
3 changesets found
list of changesets:
ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -111,6 +113,7 @@
pushing to ../b
searching for changes
common changesets up to 6675d58eff77
+invalidating branch cache (tip differs)
3 changesets found
list of changesets:
ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -408,6 +411,7 @@
pushing to ../b
searching for changes
common changesets up to 6675d58eff77
+invalidating branch cache (tip differs)
3 changesets found
list of changesets:
ef1ea85a6374b77d6da9dcda9541f498f2d17df7
diff -r 252232621165 -r 94ac3cbde177 tests/test-hgweb-commands.out
Binary file tests/test-hgweb-commands.out has changed
diff -r 252232621165 -r 94ac3cbde177 tests/test-push-logic
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-push-logic Tue May 05 15:09:42 2009 +0200
@@ -0,0 +1,118 @@
+#!/bin/sh
+
+echo push on existing branch and new branch
+hg init a
+hg --cwd a branch a
+echo 0 >> a/foo
+hg --cwd a add foo
+hg --cwd a ci -m "0"
+
+hg clone a b
+echo 1 >> b/foo
+hg --cwd b ci -m "1"
+hg --cwd b up -r 0 -C
+hg --cwd b branch b
+echo 2 >> b/foo
+hg --cwd b ci -m "2"
+hg --cwd b push | sed 's/pushing to.*/pushing/'
+echo
+
+rm -rf a b
+
+echo push on existing branch and existing branch with multiple heads
+hg init a
+hg --cwd a branch a
+echo 0 >> a/foo
+hg --cwd a add foo
+hg --cwd a ci -m "0"
+hg --cwd a branch b
+echo 1 >> a/foo
+hg --cwd a ci -m "1"
+
+hg clone a b
+hg --cwd b up -r 0 -C
+echo 2 >> b/foo
+hg --cwd b ci -m "2"
+hg --cwd b up -r 1 -C
+echo 3 >> b/foo
+hg --cwd b ci -m "3"
+hg --cwd b up -r 1 -C
+echo 4 >> b/foo
+hg --cwd b ci -m "4"
+hg --cwd b push | sed 's/pushing to.*/pushing/'
+hg --cwd b push -r 2 -r 3 | sed 's/pushing to.*/pushing/'
+echo
+
+rm -rf a b
+
+echo fail on multiple head push
+
+hg init a
+echo 0 >> a/foo
+hg --cwd a branch a
+hg --cwd a add foo
+hg --cwd a ci -m "0"
+
+hg clone a b
+echo 1 >> b/foo
+hg --cwd b ci -m "1"
+hg --cwd b up -r 0 -C
+echo 2 >> b/foo
+hg --cwd b ci -m "2"
+hg --cwd b push | sed 's/pushing to.*/pushing/'
+
+echo
+
+rm -rf a b
+
+echo merge of branch a to other branch b followed by unrelated push on branch a
+hg init a
+echo 0 > a/foo
+hg --cwd a branch a
+hg --cwd a add foo
+hg --cwd a ci -m "0"
+echo 1 > a/foo
+hg --cwd a branch b
+hg --cwd a ci -m "1"
+hg --cwd a up -r 0 -C
+echo 2 > a/foo
+hg --cwd a ci -m "2"
+
+hg clone a b
+hg clone a c
+
+HGMERGE=internal:merge hg --cwd b merge -r 1 # merge b down to a
+hg --cwd b resolve -m foo
+hg --cwd b ci -m "3 on b"
+hg --cwd b push | sed 's/pushing to.*/pushing/'
+
+hg --cwd c up -r 1
+echo 3 > c/foo
+hg --cwd c ci -m "3 on c"
+hg --cwd c push | sed 's/pushing to.*/pushing/'
+echo
+
+rm -rf a b c
+
+echo cheating the count algorithm
+hg init a
+echo 0 > a/foo
+hg --cwd a add foo
+hg --cwd a ci -m "0"
+echo 1 > a/foo
+hg --cwd a ci -m "1"
+hg --cwd a up -r 0 -C
+echo 2 > a/foo
+hg --cwd a ci -m "2"
+
+hg clone a b
+HGMERGE=internal:merge hg --cwd b merge -r 1
+hg --cwd b resolve -m foo
+hg --cwd b ci -m "3"
+hg --cwd b up -r 0
+echo 4 > b/foo
+hg --cwd b ci -m "4"
+hg --cwd b push | sed 's/pushing to.*/pushing/'
+echo
+
+exit 0
diff -r 252232621165 -r 94ac3cbde177 tests/test-push-logic.out
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-push-logic.out Tue May 05 15:09:42 2009 +0200
@@ -0,0 +1,92 @@
+push on existing branch and new branch
+marked working directory as branch a
+updating working directory
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+marked working directory as branch b
+created new head
+abort: push creates new remote branch 'b'!
+pushing
+searching for changes
+(did you forget to merge? use push -f to force)
+
+push on existing branch and existing branch with multiple heads
+marked working directory as branch a
+marked working directory as branch b
+updating working directory
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
+abort: push creates new remote heads!
+pushing
+searching for changes
+(did you forget to merge? use push -f to force)
+pushing
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 2 changesets with 2 changes to 1 files (+1 heads)
+
+fail on multiple head push
+marked working directory as branch a
+updating working directory
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
+abort: push creates new remote heads!
+pushing
+searching for changes
+(did you forget to merge? use push -f to force)
+
+merge of branch a to other branch b followed by unrelated push on branch a
+marked working directory as branch a
+marked working directory as branch b
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
+updating working directory
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+updating working directory
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+merging foo
+warning: conflicts during merge.
+merging foo failed!
+0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+use 'hg resolve' to retry unresolved file merges or 'hg up --clean' to abandon
+pushing
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files (-1 heads)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+note: unsynced remote changes!
+pushing
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files (+1 heads)
+
+cheating the count algorithm
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
+updating working directory
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+merging foo
+warning: conflicts during merge.
+merging foo failed!
+0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+use 'hg resolve' to retry unresolved file merges or 'hg up --clean' to abandon
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
+pushing
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 2 changesets with 2 changes to 1 files
+
More information about the Mercurial-devel
mailing list