##// 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 class httprepository(remoterepository):
72 class httprepository(remoterepository):
73 def __init__(self, ui, path):
73 def __init__(self, ui, path):
74 self.capabilities = ()
74 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
75 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
75 if query or frag:
76 if query or frag:
76 raise util.Abort(_('unsupported URL component: "%s"') %
77 raise util.Abort(_('unsupported URL component: "%s"') %
@@ -234,5 +235,8 b' class httprepository(remoterepository):'
234
235
235 return util.chunkbuffer(zgenerator(util.filechunkiter(f)))
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 class httpsrepository(httprepository):
241 class httpsrepository(httprepository):
238 pass
242 pass
@@ -15,6 +15,8 b' demandload(globals(), "re lock transacti'
15 demandload(globals(), "revlog")
15 demandload(globals(), "revlog")
16
16
17 class localrepository(object):
17 class localrepository(object):
18 capabilities = ()
19
18 def __del__(self):
20 def __del__(self):
19 self.transhandle = None
21 self.transhandle = None
20 def __init__(self, parentui, path=None, create=0):
22 def __init__(self, parentui, path=None, create=0):
@@ -1105,8 +1107,20 b' class localrepository(object):'
1105 return self.addchangegroup(cg, 'pull')
1107 return self.addchangegroup(cg, 'pull')
1106
1108
1107 def push(self, remote, force=False, revs=None):
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 base = {}
1124 base = {}
1111 remote_heads = remote.heads()
1125 remote_heads = remote.heads()
1112 inc = self.findincoming(remote, base, remote_heads, force=force)
1126 inc = self.findincoming(remote, base, remote_heads, force=force)
@@ -1114,7 +1128,7 b' class localrepository(object):'
1114 self.ui.warn(_("abort: unsynced remote changes!\n"))
1128 self.ui.warn(_("abort: unsynced remote changes!\n"))
1115 self.ui.status(_("(did you forget to sync?"
1129 self.ui.status(_("(did you forget to sync?"
1116 " use push -f to force)\n"))
1130 " use push -f to force)\n"))
1117 return 1
1131 return None, 1
1118
1132
1119 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1133 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1120 if revs is not None:
1134 if revs is not None:
@@ -1124,7 +1138,7 b' class localrepository(object):'
1124
1138
1125 if not bases:
1139 if not bases:
1126 self.ui.status(_("no changes found\n"))
1140 self.ui.status(_("no changes found\n"))
1127 return 1
1141 return None, 1
1128 elif not force:
1142 elif not force:
1129 # FIXME we don't properly detect creation of new heads
1143 # FIXME we don't properly detect creation of new heads
1130 # in the push -r case, assume the user knows what he's doing
1144 # in the push -r case, assume the user knows what he's doing
@@ -1133,13 +1147,35 b' class localrepository(object):'
1133 self.ui.warn(_("abort: push creates new remote branches!\n"))
1147 self.ui.warn(_("abort: push creates new remote branches!\n"))
1134 self.ui.status(_("(did you forget to merge?"
1148 self.ui.status(_("(did you forget to merge?"
1135 " use push -f to force)\n"))
1149 " use push -f to force)\n"))
1136 return 1
1150 return None, 1
1137
1151
1138 if revs is None:
1152 if revs is None:
1139 cg = self.changegroup(update, 'push')
1153 cg = self.changegroup(update, 'push')
1140 else:
1154 else:
1141 cg = self.changegroupsubset(update, revs, 'push')
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 def changegroupsubset(self, bases, heads, source):
1180 def changegroupsubset(self, bases, heads, source):
1145 """This function generates a changegroup consisting of all the nodes
1181 """This function generates a changegroup consisting of all the nodes
@@ -141,11 +141,36 b' class sshrepository(remoterepository):'
141 f = self.do_cmd("changegroup", roots=n)
141 f = self.do_cmd("changegroup", roots=n)
142 return self.pipei
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 def addchangegroup(self, cg, source):
170 def addchangegroup(self, cg, source):
145 d = self.call("addchangegroup")
171 d = self.call("addchangegroup")
146 if d:
172 if d:
147 raise hg.RepoError(_("push refused: %s"), d)
173 raise hg.RepoError(_("push refused: %s") % d)
148
149 while 1:
174 while 1:
150 d = cg.read(4096)
175 d = cg.read(4096)
151 if not d: break
176 if not d: break
@@ -8,7 +8,7 b''
8 from demandload import demandload
8 from demandload import demandload
9 from i18n import gettext as _
9 from i18n import gettext as _
10 from node import *
10 from node import *
11 demandload(globals(), "sys util")
11 demandload(globals(), "os sys tempfile util")
12
12
13 class sshserver(object):
13 class sshserver(object):
14 def __init__(self, ui, repo):
14 def __init__(self, ui, repo):
@@ -60,14 +60,18 b' class sshserver(object):'
60 capabilities: space separated list of tokens
60 capabilities: space separated list of tokens
61 '''
61 '''
62
62
63 r = "capabilities:\n"
63 r = "capabilities: unbundle\n"
64 self.respond(r)
64 self.respond(r)
65
65
66 def do_lock(self):
66 def do_lock(self):
67 '''DEPRECATED - allowing remote client to lock repo is not safe'''
68
67 self.lock = self.repo.lock()
69 self.lock = self.repo.lock()
68 self.respond("")
70 self.respond("")
69
71
70 def do_unlock(self):
72 def do_unlock(self):
73 '''DEPRECATED'''
74
71 if self.lock:
75 if self.lock:
72 self.lock.release()
76 self.lock.release()
73 self.lock = None
77 self.lock = None
@@ -104,6 +108,8 b' class sshserver(object):'
104 self.fout.flush()
108 self.fout.flush()
105
109
106 def do_addchangegroup(self):
110 def do_addchangegroup(self):
111 '''DEPRECATED'''
112
107 if not self.lock:
113 if not self.lock:
108 self.respond("not locked")
114 self.respond("not locked")
109 return
115 return
@@ -111,3 +117,53 b' class sshserver(object):'
111 self.respond("")
117 self.respond("")
112 r = self.repo.addchangegroup(self.fin, 'serve')
118 r = self.repo.addchangegroup(self.fin, 'serve')
113 self.respond(str(r))
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 hg verify
66 hg verify
67 hg cat foo
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 cd ..
82 cd ..
70 cat dummylog
83 cat dummylog
@@ -55,8 +55,22 b' crosschecking files in changesets and ma'
55 checking files
55 checking files
56 1 files, 2 changesets, 2 total revisions
56 1 files, 2 changesets, 2 total revisions
57 bleah
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 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
70 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
59 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
71 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
60 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
72 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
61 Got arguments 1:user@dummy 2:hg -R local serve --stdio 3: 4: 5:
73 Got arguments 1:user@dummy 2:hg -R local serve --stdio 3: 4: 5:
62 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
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