merge with self.
--- a/mercurial/httprepo.py Thu Jun 15 15:42:16 2006 -0700
+++ b/mercurial/httprepo.py Thu Jun 15 16:38:23 2006 -0700
@@ -71,6 +71,7 @@
class httprepository(remoterepository):
def __init__(self, ui, path):
+ self.capabilities = ()
scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
if query or frag:
raise util.Abort(_('unsupported URL component: "%s"') %
@@ -234,5 +235,8 @@
return util.chunkbuffer(zgenerator(util.filechunkiter(f)))
+ def unbundle(self, cg, heads, source):
+ raise util.Abort(_('operation not supported over http'))
+
class httpsrepository(httprepository):
pass
--- a/mercurial/localrepo.py Thu Jun 15 15:42:16 2006 -0700
+++ b/mercurial/localrepo.py Thu Jun 15 16:38:23 2006 -0700
@@ -15,6 +15,8 @@
demandload(globals(), "revlog")
class localrepository(object):
+ capabilities = ()
+
def __del__(self):
self.transhandle = None
def __init__(self, parentui, path=None, create=0):
@@ -1105,8 +1107,20 @@
return self.addchangegroup(cg, 'pull')
def push(self, remote, force=False, revs=None):
- lock = remote.lock()
+ # there are two ways to push to remote repo:
+ #
+ # addchangegroup assumes local user can lock remote
+ # repo (local filesystem, old ssh servers).
+ #
+ # unbundle assumes local user cannot lock remote repo (new ssh
+ # servers, http servers).
+ if 'unbundle' in remote.capabilities:
+ self.push_unbundle(remote, force, revs)
+ else:
+ self.push_addchangegroup(remote, force, revs)
+
+ def prepush(self, remote, force, revs):
base = {}
remote_heads = remote.heads()
inc = self.findincoming(remote, base, remote_heads, force=force)
@@ -1114,7 +1128,7 @@
self.ui.warn(_("abort: unsynced remote changes!\n"))
self.ui.status(_("(did you forget to sync?"
" use push -f to force)\n"))
- return 1
+ return None, 1
update, updated_heads = self.findoutgoing(remote, base, remote_heads)
if revs is not None:
@@ -1124,7 +1138,7 @@
if not bases:
self.ui.status(_("no changes found\n"))
- return 1
+ return None, 1
elif not force:
# FIXME we don't properly detect creation of new heads
# in the push -r case, assume the user knows what he's doing
@@ -1133,13 +1147,35 @@
self.ui.warn(_("abort: push creates new remote branches!\n"))
self.ui.status(_("(did you forget to merge?"
" use push -f to force)\n"))
- return 1
+ return None, 1
if revs is None:
cg = self.changegroup(update, 'push')
else:
cg = self.changegroupsubset(update, revs, 'push')
- return remote.addchangegroup(cg, 'push')
+ return cg, remote_heads
+
+ def push_addchangegroup(self, remote, force, revs):
+ lock = remote.lock()
+
+ ret = self.prepush(remote, force, revs)
+ if ret[0] is not None:
+ cg, remote_heads = ret
+ return remote.addchangegroup(cg, 'push')
+ return ret[1]
+
+ def push_unbundle(self, remote, force, revs):
+ # local repo finds heads on server, finds out what revs it
+ # must push. once revs transferred, if server finds it has
+ # different heads (someone else won commit/push race), server
+ # aborts.
+
+ ret = self.prepush(remote, force, revs)
+ if ret[0] is not None:
+ cg, remote_heads = ret
+ if force: remote_heads = ['force']
+ return remote.unbundle(cg, remote_heads, 'push')
+ return ret[1]
def changegroupsubset(self, bases, heads, source):
"""This function generates a changegroup consisting of all the nodes
--- a/mercurial/sshrepo.py Thu Jun 15 15:42:16 2006 -0700
+++ b/mercurial/sshrepo.py Thu Jun 15 16:38:23 2006 -0700
@@ -141,11 +141,36 @@
f = self.do_cmd("changegroup", roots=n)
return self.pipei
+ def unbundle(self, cg, heads, source):
+ d = self.call("unbundle", heads=' '.join(map(hex, heads)))
+ if d:
+ raise hg.RepoError(_("push refused: %s") % d)
+
+ while 1:
+ d = cg.read(4096)
+ if not d: break
+ self.pipeo.write(str(len(d)) + '\n')
+ self.pipeo.write(d)
+ self.readerr()
+
+ self.pipeo.write('0\n')
+ self.pipeo.flush()
+
+ self.readerr()
+ d = self.pipei.readline()
+ if d != '\n':
+ return 1
+
+ l = int(self.pipei.readline())
+ r = self.pipei.read(l)
+ if not r:
+ return 1
+ return int(r)
+
def addchangegroup(self, cg, source):
d = self.call("addchangegroup")
if d:
raise hg.RepoError(_("push refused: %s") % d)
-
while 1:
d = cg.read(4096)
if not d: break
--- a/mercurial/sshserver.py Thu Jun 15 15:42:16 2006 -0700
+++ b/mercurial/sshserver.py Thu Jun 15 16:38:23 2006 -0700
@@ -8,7 +8,7 @@
from demandload import demandload
from i18n import gettext as _
from node import *
-demandload(globals(), "sys util")
+demandload(globals(), "os sys tempfile util")
class sshserver(object):
def __init__(self, ui, repo):
@@ -60,14 +60,18 @@
capabilities: space separated list of tokens
'''
- r = "capabilities:\n"
+ r = "capabilities: unbundle\n"
self.respond(r)
def do_lock(self):
+ '''DEPRECATED - allowing remote client to lock repo is not safe'''
+
self.lock = self.repo.lock()
self.respond("")
def do_unlock(self):
+ '''DEPRECATED'''
+
if self.lock:
self.lock.release()
self.lock = None
@@ -104,6 +108,8 @@
self.fout.flush()
def do_addchangegroup(self):
+ '''DEPRECATED'''
+
if not self.lock:
self.respond("not locked")
return
@@ -111,3 +117,53 @@
self.respond("")
r = self.repo.addchangegroup(self.fin, 'serve')
self.respond(str(r))
+
+ def do_unbundle(self):
+ their_heads = self.getarg()[1].split()
+
+ def check_heads():
+ heads = map(hex, self.repo.heads())
+ return their_heads == [hex('force')] or their_heads == heads
+
+ # fail early if possible
+ if not check_heads():
+ self.respond(_('unsynced changes'))
+ return
+
+ self.respond('')
+
+ # write bundle data to temporary file because it can be big
+
+ try:
+ fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
+ fp = os.fdopen(fd, 'wb+')
+
+ count = int(self.fin.readline())
+ while count:
+ fp.write(self.fin.read(count))
+ count = int(self.fin.readline())
+
+ was_locked = self.lock is not None
+ if not was_locked:
+ self.lock = self.repo.lock()
+ try:
+ if not check_heads():
+ # someone else committed/pushed/unbundled while we
+ # were transferring data
+ self.respond(_('unsynced changes'))
+ return
+ self.respond('')
+
+ # push can proceed
+
+ fp.seek(0)
+ r = self.repo.addchangegroup(fp, 'serve')
+ self.respond(str(r))
+ finally:
+ if not was_locked:
+ self.lock.release()
+ self.lock = None
+ finally:
+ fp.close()
+ os.unlink(tempname)
+
--- a/tests/test-ssh Thu Jun 15 15:42:16 2006 -0700
+++ b/tests/test-ssh Thu Jun 15 16:38:23 2006 -0700
@@ -66,5 +66,18 @@
hg verify
hg cat foo
+echo z > z
+hg ci -A -m z -d '1000001 0' z
+
+cd ../local
+echo r > r
+hg ci -A -m z -d '1000002 0' r
+
+echo "# push should fail"
+hg push
+
+echo "# push should succeed"
+hg push -f
+
cd ..
cat dummylog
--- a/tests/test-ssh.out Thu Jun 15 15:42:16 2006 -0700
+++ b/tests/test-ssh.out Thu Jun 15 16:38:23 2006 -0700
@@ -55,8 +55,22 @@
checking files
1 files, 2 changesets, 2 total revisions
bleah
+# push should fail
+pushing to ssh://user@dummy/remote
+searching for changes
+abort: unsynced remote changes!
+(did you forget to sync? use push -f to force)
+# push should succeed
+pushing to ssh://user@dummy/remote
+searching for changes
+remote: adding changesets
+remote: adding manifests
+remote: adding file changes
+remote: added 1 changesets with 1 changes to 1 files
Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
Got arguments 1:user@dummy 2:hg -R local serve --stdio 3: 4: 5:
Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
+Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
+Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5: