##// END OF EJS Templates
extend network protocol to stop clients from locking servers...
Vadim Gelfer -
r2439:e8c4f3d3 default
parent child Browse files
Show More
@@ -71,6 +71,7 b' def netlocunsplit(host, port, user=None,'
71 71
72 72 class httprepository(remoterepository):
73 73 def __init__(self, ui, path):
74 self.capabilities = ()
74 75 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
75 76 if query or frag:
76 77 raise util.Abort(_('unsupported URL component: "%s"') %
@@ -234,5 +235,8 b' class httprepository(remoterepository):'
234 235
235 236 return util.chunkbuffer(zgenerator(util.filechunkiter(f)))
236 237
238 def unbundle(self, cg, heads, source):
239 raise util.Abort(_('operation not supported over http'))
240
237 241 class httpsrepository(httprepository):
238 242 pass
@@ -15,6 +15,8 b' demandload(globals(), "re lock transacti'
15 15 demandload(globals(), "revlog")
16 16
17 17 class localrepository(object):
18 capabilities = ()
19
18 20 def __del__(self):
19 21 self.transhandle = None
20 22 def __init__(self, parentui, path=None, create=0):
@@ -1105,8 +1107,20 b' class localrepository(object):'
1105 1107 return self.addchangegroup(cg, 'pull')
1106 1108
1107 1109 def push(self, remote, force=False, revs=None):
1108 lock = remote.lock()
1110 # there are two ways to push to remote repo:
1111 #
1112 # addchangegroup assumes local user can lock remote
1113 # repo (local filesystem, old ssh servers).
1114 #
1115 # unbundle assumes local user cannot lock remote repo (new ssh
1116 # servers, http servers).
1109 1117
1118 if 'unbundle' in remote.capabilities:
1119 self.push_unbundle(remote, force, revs)
1120 else:
1121 self.push_addchangegroup(remote, force, revs)
1122
1123 def prepush(self, remote, force, revs):
1110 1124 base = {}
1111 1125 remote_heads = remote.heads()
1112 1126 inc = self.findincoming(remote, base, remote_heads, force=force)
@@ -1114,7 +1128,7 b' class localrepository(object):'
1114 1128 self.ui.warn(_("abort: unsynced remote changes!\n"))
1115 1129 self.ui.status(_("(did you forget to sync?"
1116 1130 " use push -f to force)\n"))
1117 return 1
1131 return None, 1
1118 1132
1119 1133 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1120 1134 if revs is not None:
@@ -1124,7 +1138,7 b' class localrepository(object):'
1124 1138
1125 1139 if not bases:
1126 1140 self.ui.status(_("no changes found\n"))
1127 return 1
1141 return None, 1
1128 1142 elif not force:
1129 1143 # FIXME we don't properly detect creation of new heads
1130 1144 # in the push -r case, assume the user knows what he's doing
@@ -1133,13 +1147,35 b' class localrepository(object):'
1133 1147 self.ui.warn(_("abort: push creates new remote branches!\n"))
1134 1148 self.ui.status(_("(did you forget to merge?"
1135 1149 " use push -f to force)\n"))
1136 return 1
1150 return None, 1
1137 1151
1138 1152 if revs is None:
1139 1153 cg = self.changegroup(update, 'push')
1140 1154 else:
1141 1155 cg = self.changegroupsubset(update, revs, 'push')
1142 return remote.addchangegroup(cg, 'push')
1156 return cg, remote_heads
1157
1158 def push_addchangegroup(self, remote, force, revs):
1159 lock = remote.lock()
1160
1161 ret = self.prepush(remote, force, revs)
1162 if ret[0] is not None:
1163 cg, remote_heads = ret
1164 return remote.addchangegroup(cg, 'push')
1165 return ret[1]
1166
1167 def push_unbundle(self, remote, force, revs):
1168 # local repo finds heads on server, finds out what revs it
1169 # must push. once revs transferred, if server finds it has
1170 # different heads (someone else won commit/push race), server
1171 # aborts.
1172
1173 ret = self.prepush(remote, force, revs)
1174 if ret[0] is not None:
1175 cg, remote_heads = ret
1176 if force: remote_heads = ['force']
1177 return remote.unbundle(cg, remote_heads, 'push')
1178 return ret[1]
1143 1179
1144 1180 def changegroupsubset(self, bases, heads, source):
1145 1181 """This function generates a changegroup consisting of all the nodes
@@ -141,11 +141,36 b' class sshrepository(remoterepository):'
141 141 f = self.do_cmd("changegroup", roots=n)
142 142 return self.pipei
143 143
144 def unbundle(self, cg, heads, source):
145 d = self.call("unbundle", heads=' '.join(map(hex, heads)))
146 if d:
147 raise hg.RepoError(_("push refused: %s") % d)
148
149 while 1:
150 d = cg.read(4096)
151 if not d: break
152 self.pipeo.write(str(len(d)) + '\n')
153 self.pipeo.write(d)
154 self.readerr()
155
156 self.pipeo.write('0\n')
157 self.pipeo.flush()
158
159 self.readerr()
160 d = self.pipei.readline()
161 if d != '\n':
162 return 1
163
164 l = int(self.pipei.readline())
165 r = self.pipei.read(l)
166 if not r:
167 return 1
168 return int(r)
169
144 170 def addchangegroup(self, cg, source):
145 171 d = self.call("addchangegroup")
146 172 if d:
147 raise hg.RepoError(_("push refused: %s"), d)
148
173 raise hg.RepoError(_("push refused: %s") % d)
149 174 while 1:
150 175 d = cg.read(4096)
151 176 if not d: break
@@ -8,7 +8,7 b''
8 8 from demandload import demandload
9 9 from i18n import gettext as _
10 10 from node import *
11 demandload(globals(), "sys util")
11 demandload(globals(), "os sys tempfile util")
12 12
13 13 class sshserver(object):
14 14 def __init__(self, ui, repo):
@@ -60,14 +60,18 b' class sshserver(object):'
60 60 capabilities: space separated list of tokens
61 61 '''
62 62
63 r = "capabilities:\n"
63 r = "capabilities: unbundle\n"
64 64 self.respond(r)
65 65
66 66 def do_lock(self):
67 '''DEPRECATED - allowing remote client to lock repo is not safe'''
68
67 69 self.lock = self.repo.lock()
68 70 self.respond("")
69 71
70 72 def do_unlock(self):
73 '''DEPRECATED'''
74
71 75 if self.lock:
72 76 self.lock.release()
73 77 self.lock = None
@@ -104,6 +108,8 b' class sshserver(object):'
104 108 self.fout.flush()
105 109
106 110 def do_addchangegroup(self):
111 '''DEPRECATED'''
112
107 113 if not self.lock:
108 114 self.respond("not locked")
109 115 return
@@ -111,3 +117,53 b' class sshserver(object):'
111 117 self.respond("")
112 118 r = self.repo.addchangegroup(self.fin, 'serve')
113 119 self.respond(str(r))
120
121 def do_unbundle(self):
122 their_heads = self.getarg()[1].split()
123
124 def check_heads():
125 heads = map(hex, self.repo.heads())
126 return their_heads == [hex('force')] or their_heads == heads
127
128 # fail early if possible
129 if not check_heads():
130 self.respond(_('unsynced changes'))
131 return
132
133 self.respond('')
134
135 # write bundle data to temporary file because it can be big
136
137 try:
138 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
139 fp = os.fdopen(fd, 'wb+')
140
141 count = int(self.fin.readline())
142 while count:
143 fp.write(self.fin.read(count))
144 count = int(self.fin.readline())
145
146 was_locked = self.lock is not None
147 if not was_locked:
148 self.lock = self.repo.lock()
149 try:
150 if not check_heads():
151 # someone else committed/pushed/unbundled while we
152 # were transferring data
153 self.respond(_('unsynced changes'))
154 return
155 self.respond('')
156
157 # push can proceed
158
159 fp.seek(0)
160 r = self.repo.addchangegroup(fp, 'serve')
161 self.respond(str(r))
162 finally:
163 if not was_locked:
164 self.lock.release()
165 self.lock = None
166 finally:
167 fp.close()
168 os.unlink(tempname)
169
@@ -66,5 +66,18 b' hg tip'
66 66 hg verify
67 67 hg cat foo
68 68
69 echo z > z
70 hg ci -A -m z -d '1000001 0' z
71
72 cd ../local
73 echo r > r
74 hg ci -A -m z -d '1000002 0' r
75
76 echo "# push should fail"
77 hg push
78
79 echo "# push should succeed"
80 hg push -f
81
69 82 cd ..
70 83 cat dummylog
@@ -55,8 +55,22 b' crosschecking files in changesets and ma'
55 55 checking files
56 56 1 files, 2 changesets, 2 total revisions
57 57 bleah
58 # push should fail
59 pushing to ssh://user@dummy/remote
60 searching for changes
61 abort: unsynced remote changes!
62 (did you forget to sync? use push -f to force)
63 # push should succeed
64 pushing to ssh://user@dummy/remote
65 searching for changes
66 remote: adding changesets
67 remote: adding manifests
68 remote: adding file changes
69 remote: added 1 changesets with 1 changes to 1 files
58 70 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
59 71 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
60 72 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
61 73 Got arguments 1:user@dummy 2:hg -R local serve --stdio 3: 4: 5:
62 74 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
75 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
76 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
General Comments 0
You need to be logged in to leave comments. Login now