##// END OF EJS Templates
bundle2: feed a binary stream to `peer.unbundle`...
Pierre-Yves David -
r21070:408877d4 default
parent child Browse files
Show More
@@ -1,708 +1,707 b''
1 # exchange.py - utility to exchange data between repos.
1 # exchange.py - utility to exchange data between repos.
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 from node import hex, nullid
9 from node import hex, nullid
10 import errno
10 import errno
11 import util, scmutil, changegroup, base85
11 import util, scmutil, changegroup, base85
12 import discovery, phases, obsolete, bookmarks, bundle2
12 import discovery, phases, obsolete, bookmarks, bundle2
13
13
14 def readbundle(ui, fh, fname, vfs=None):
14 def readbundle(ui, fh, fname, vfs=None):
15 header = changegroup.readexactly(fh, 4)
15 header = changegroup.readexactly(fh, 4)
16
16
17 alg = None
17 alg = None
18 if not fname:
18 if not fname:
19 fname = "stream"
19 fname = "stream"
20 if not header.startswith('HG') and header.startswith('\0'):
20 if not header.startswith('HG') and header.startswith('\0'):
21 fh = changegroup.headerlessfixup(fh, header)
21 fh = changegroup.headerlessfixup(fh, header)
22 header = "HG10"
22 header = "HG10"
23 alg = 'UN'
23 alg = 'UN'
24 elif vfs:
24 elif vfs:
25 fname = vfs.join(fname)
25 fname = vfs.join(fname)
26
26
27 magic, version = header[0:2], header[2:4]
27 magic, version = header[0:2], header[2:4]
28
28
29 if magic != 'HG':
29 if magic != 'HG':
30 raise util.Abort(_('%s: not a Mercurial bundle') % fname)
30 raise util.Abort(_('%s: not a Mercurial bundle') % fname)
31 if version == '10':
31 if version == '10':
32 if alg is None:
32 if alg is None:
33 alg = changegroup.readexactly(fh, 2)
33 alg = changegroup.readexactly(fh, 2)
34 return changegroup.unbundle10(fh, alg)
34 return changegroup.unbundle10(fh, alg)
35 elif version == '20':
35 elif version == '20':
36 return bundle2.unbundle20(ui, fh, header=magic + version)
36 return bundle2.unbundle20(ui, fh, header=magic + version)
37 else:
37 else:
38 raise util.Abort(_('%s: unknown bundle version %s') % (fname, version))
38 raise util.Abort(_('%s: unknown bundle version %s') % (fname, version))
39
39
40
40
41 class pushoperation(object):
41 class pushoperation(object):
42 """A object that represent a single push operation
42 """A object that represent a single push operation
43
43
44 It purpose is to carry push related state and very common operation.
44 It purpose is to carry push related state and very common operation.
45
45
46 A new should be created at the beginning of each push and discarded
46 A new should be created at the beginning of each push and discarded
47 afterward.
47 afterward.
48 """
48 """
49
49
50 def __init__(self, repo, remote, force=False, revs=None, newbranch=False):
50 def __init__(self, repo, remote, force=False, revs=None, newbranch=False):
51 # repo we push from
51 # repo we push from
52 self.repo = repo
52 self.repo = repo
53 self.ui = repo.ui
53 self.ui = repo.ui
54 # repo we push to
54 # repo we push to
55 self.remote = remote
55 self.remote = remote
56 # force option provided
56 # force option provided
57 self.force = force
57 self.force = force
58 # revs to be pushed (None is "all")
58 # revs to be pushed (None is "all")
59 self.revs = revs
59 self.revs = revs
60 # allow push of new branch
60 # allow push of new branch
61 self.newbranch = newbranch
61 self.newbranch = newbranch
62 # did a local lock get acquired?
62 # did a local lock get acquired?
63 self.locallocked = None
63 self.locallocked = None
64 # Integer version of the push result
64 # Integer version of the push result
65 # - None means nothing to push
65 # - None means nothing to push
66 # - 0 means HTTP error
66 # - 0 means HTTP error
67 # - 1 means we pushed and remote head count is unchanged *or*
67 # - 1 means we pushed and remote head count is unchanged *or*
68 # we have outgoing changesets but refused to push
68 # we have outgoing changesets but refused to push
69 # - other values as described by addchangegroup()
69 # - other values as described by addchangegroup()
70 self.ret = None
70 self.ret = None
71 # discover.outgoing object (contains common and outgoing data)
71 # discover.outgoing object (contains common and outgoing data)
72 self.outgoing = None
72 self.outgoing = None
73 # all remote heads before the push
73 # all remote heads before the push
74 self.remoteheads = None
74 self.remoteheads = None
75 # testable as a boolean indicating if any nodes are missing locally.
75 # testable as a boolean indicating if any nodes are missing locally.
76 self.incoming = None
76 self.incoming = None
77 # set of all heads common after changeset bundle push
77 # set of all heads common after changeset bundle push
78 self.commonheads = None
78 self.commonheads = None
79
79
80 def push(repo, remote, force=False, revs=None, newbranch=False):
80 def push(repo, remote, force=False, revs=None, newbranch=False):
81 '''Push outgoing changesets (limited by revs) from a local
81 '''Push outgoing changesets (limited by revs) from a local
82 repository to remote. Return an integer:
82 repository to remote. Return an integer:
83 - None means nothing to push
83 - None means nothing to push
84 - 0 means HTTP error
84 - 0 means HTTP error
85 - 1 means we pushed and remote head count is unchanged *or*
85 - 1 means we pushed and remote head count is unchanged *or*
86 we have outgoing changesets but refused to push
86 we have outgoing changesets but refused to push
87 - other values as described by addchangegroup()
87 - other values as described by addchangegroup()
88 '''
88 '''
89 pushop = pushoperation(repo, remote, force, revs, newbranch)
89 pushop = pushoperation(repo, remote, force, revs, newbranch)
90 if pushop.remote.local():
90 if pushop.remote.local():
91 missing = (set(pushop.repo.requirements)
91 missing = (set(pushop.repo.requirements)
92 - pushop.remote.local().supported)
92 - pushop.remote.local().supported)
93 if missing:
93 if missing:
94 msg = _("required features are not"
94 msg = _("required features are not"
95 " supported in the destination:"
95 " supported in the destination:"
96 " %s") % (', '.join(sorted(missing)))
96 " %s") % (', '.join(sorted(missing)))
97 raise util.Abort(msg)
97 raise util.Abort(msg)
98
98
99 # there are two ways to push to remote repo:
99 # there are two ways to push to remote repo:
100 #
100 #
101 # addchangegroup assumes local user can lock remote
101 # addchangegroup assumes local user can lock remote
102 # repo (local filesystem, old ssh servers).
102 # repo (local filesystem, old ssh servers).
103 #
103 #
104 # unbundle assumes local user cannot lock remote repo (new ssh
104 # unbundle assumes local user cannot lock remote repo (new ssh
105 # servers, http servers).
105 # servers, http servers).
106
106
107 if not pushop.remote.canpush():
107 if not pushop.remote.canpush():
108 raise util.Abort(_("destination does not support push"))
108 raise util.Abort(_("destination does not support push"))
109 # get local lock as we might write phase data
109 # get local lock as we might write phase data
110 locallock = None
110 locallock = None
111 try:
111 try:
112 locallock = pushop.repo.lock()
112 locallock = pushop.repo.lock()
113 pushop.locallocked = True
113 pushop.locallocked = True
114 except IOError, err:
114 except IOError, err:
115 pushop.locallocked = False
115 pushop.locallocked = False
116 if err.errno != errno.EACCES:
116 if err.errno != errno.EACCES:
117 raise
117 raise
118 # source repo cannot be locked.
118 # source repo cannot be locked.
119 # We do not abort the push, but just disable the local phase
119 # We do not abort the push, but just disable the local phase
120 # synchronisation.
120 # synchronisation.
121 msg = 'cannot lock source repository: %s\n' % err
121 msg = 'cannot lock source repository: %s\n' % err
122 pushop.ui.debug(msg)
122 pushop.ui.debug(msg)
123 try:
123 try:
124 pushop.repo.checkpush(pushop)
124 pushop.repo.checkpush(pushop)
125 lock = None
125 lock = None
126 unbundle = pushop.remote.capable('unbundle')
126 unbundle = pushop.remote.capable('unbundle')
127 if not unbundle:
127 if not unbundle:
128 lock = pushop.remote.lock()
128 lock = pushop.remote.lock()
129 try:
129 try:
130 _pushdiscovery(pushop)
130 _pushdiscovery(pushop)
131 if _pushcheckoutgoing(pushop):
131 if _pushcheckoutgoing(pushop):
132 pushop.repo.prepushoutgoinghooks(pushop.repo,
132 pushop.repo.prepushoutgoinghooks(pushop.repo,
133 pushop.remote,
133 pushop.remote,
134 pushop.outgoing)
134 pushop.outgoing)
135 if pushop.remote.capable('bundle2'):
135 if pushop.remote.capable('bundle2'):
136 _pushbundle2(pushop)
136 _pushbundle2(pushop)
137 else:
137 else:
138 _pushchangeset(pushop)
138 _pushchangeset(pushop)
139 _pushcomputecommonheads(pushop)
139 _pushcomputecommonheads(pushop)
140 _pushsyncphase(pushop)
140 _pushsyncphase(pushop)
141 _pushobsolete(pushop)
141 _pushobsolete(pushop)
142 finally:
142 finally:
143 if lock is not None:
143 if lock is not None:
144 lock.release()
144 lock.release()
145 finally:
145 finally:
146 if locallock is not None:
146 if locallock is not None:
147 locallock.release()
147 locallock.release()
148
148
149 _pushbookmark(pushop)
149 _pushbookmark(pushop)
150 return pushop.ret
150 return pushop.ret
151
151
152 def _pushdiscovery(pushop):
152 def _pushdiscovery(pushop):
153 # discovery
153 # discovery
154 unfi = pushop.repo.unfiltered()
154 unfi = pushop.repo.unfiltered()
155 fci = discovery.findcommonincoming
155 fci = discovery.findcommonincoming
156 commoninc = fci(unfi, pushop.remote, force=pushop.force)
156 commoninc = fci(unfi, pushop.remote, force=pushop.force)
157 common, inc, remoteheads = commoninc
157 common, inc, remoteheads = commoninc
158 fco = discovery.findcommonoutgoing
158 fco = discovery.findcommonoutgoing
159 outgoing = fco(unfi, pushop.remote, onlyheads=pushop.revs,
159 outgoing = fco(unfi, pushop.remote, onlyheads=pushop.revs,
160 commoninc=commoninc, force=pushop.force)
160 commoninc=commoninc, force=pushop.force)
161 pushop.outgoing = outgoing
161 pushop.outgoing = outgoing
162 pushop.remoteheads = remoteheads
162 pushop.remoteheads = remoteheads
163 pushop.incoming = inc
163 pushop.incoming = inc
164
164
165 def _pushcheckoutgoing(pushop):
165 def _pushcheckoutgoing(pushop):
166 outgoing = pushop.outgoing
166 outgoing = pushop.outgoing
167 unfi = pushop.repo.unfiltered()
167 unfi = pushop.repo.unfiltered()
168 if not outgoing.missing:
168 if not outgoing.missing:
169 # nothing to push
169 # nothing to push
170 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
170 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
171 return False
171 return False
172 # something to push
172 # something to push
173 if not pushop.force:
173 if not pushop.force:
174 # if repo.obsstore == False --> no obsolete
174 # if repo.obsstore == False --> no obsolete
175 # then, save the iteration
175 # then, save the iteration
176 if unfi.obsstore:
176 if unfi.obsstore:
177 # this message are here for 80 char limit reason
177 # this message are here for 80 char limit reason
178 mso = _("push includes obsolete changeset: %s!")
178 mso = _("push includes obsolete changeset: %s!")
179 mst = "push includes %s changeset: %s!"
179 mst = "push includes %s changeset: %s!"
180 # plain versions for i18n tool to detect them
180 # plain versions for i18n tool to detect them
181 _("push includes unstable changeset: %s!")
181 _("push includes unstable changeset: %s!")
182 _("push includes bumped changeset: %s!")
182 _("push includes bumped changeset: %s!")
183 _("push includes divergent changeset: %s!")
183 _("push includes divergent changeset: %s!")
184 # If we are to push if there is at least one
184 # If we are to push if there is at least one
185 # obsolete or unstable changeset in missing, at
185 # obsolete or unstable changeset in missing, at
186 # least one of the missinghead will be obsolete or
186 # least one of the missinghead will be obsolete or
187 # unstable. So checking heads only is ok
187 # unstable. So checking heads only is ok
188 for node in outgoing.missingheads:
188 for node in outgoing.missingheads:
189 ctx = unfi[node]
189 ctx = unfi[node]
190 if ctx.obsolete():
190 if ctx.obsolete():
191 raise util.Abort(mso % ctx)
191 raise util.Abort(mso % ctx)
192 elif ctx.troubled():
192 elif ctx.troubled():
193 raise util.Abort(_(mst)
193 raise util.Abort(_(mst)
194 % (ctx.troubles()[0],
194 % (ctx.troubles()[0],
195 ctx))
195 ctx))
196 newbm = pushop.ui.configlist('bookmarks', 'pushing')
196 newbm = pushop.ui.configlist('bookmarks', 'pushing')
197 discovery.checkheads(unfi, pushop.remote, outgoing,
197 discovery.checkheads(unfi, pushop.remote, outgoing,
198 pushop.remoteheads,
198 pushop.remoteheads,
199 pushop.newbranch,
199 pushop.newbranch,
200 bool(pushop.incoming),
200 bool(pushop.incoming),
201 newbm)
201 newbm)
202 return True
202 return True
203
203
204 def _pushbundle2(pushop):
204 def _pushbundle2(pushop):
205 """push data to the remote using bundle2
205 """push data to the remote using bundle2
206
206
207 The only currently supported type of data is changegroup but this will
207 The only currently supported type of data is changegroup but this will
208 evolve in the future."""
208 evolve in the future."""
209 # Send known head to the server for race detection.
209 # Send known head to the server for race detection.
210 bundler = bundle2.bundle20(pushop.ui)
210 bundler = bundle2.bundle20(pushop.ui)
211 if not pushop.force:
211 if not pushop.force:
212 part = bundle2.bundlepart('CHECK:HEADS', data=iter(pushop.remoteheads))
212 part = bundle2.bundlepart('CHECK:HEADS', data=iter(pushop.remoteheads))
213 bundler.addpart(part)
213 bundler.addpart(part)
214 # add the changegroup bundle
214 # add the changegroup bundle
215 cg = changegroup.getlocalbundle(pushop.repo, 'push', pushop.outgoing)
215 cg = changegroup.getlocalbundle(pushop.repo, 'push', pushop.outgoing)
216 cgpart = bundle2.bundlepart('CHANGEGROUP', data=cg.getchunks())
216 cgpart = bundle2.bundlepart('CHANGEGROUP', data=cg.getchunks())
217 bundler.addpart(cgpart)
217 bundler.addpart(cgpart)
218 stream = util.chunkbuffer(bundler.getchunks())
218 stream = util.chunkbuffer(bundler.getchunks())
219 sent = bundle2.unbundle20(pushop.repo.ui, stream)
219 reply = pushop.remote.unbundle(stream, ['force'], 'push')
220 reply = pushop.remote.unbundle(sent, ['force'], 'push')
221 try:
220 try:
222 op = bundle2.processbundle(pushop.repo, reply)
221 op = bundle2.processbundle(pushop.repo, reply)
223 except KeyError, exc:
222 except KeyError, exc:
224 raise util.Abort('missing support for %s' % exc)
223 raise util.Abort('missing support for %s' % exc)
225 cgreplies = op.records.getreplies(cgpart.id)
224 cgreplies = op.records.getreplies(cgpart.id)
226 assert len(cgreplies['changegroup']) == 1
225 assert len(cgreplies['changegroup']) == 1
227 pushop.ret = cgreplies['changegroup'][0]['return']
226 pushop.ret = cgreplies['changegroup'][0]['return']
228
227
229 def _pushchangeset(pushop):
228 def _pushchangeset(pushop):
230 """Make the actual push of changeset bundle to remote repo"""
229 """Make the actual push of changeset bundle to remote repo"""
231 outgoing = pushop.outgoing
230 outgoing = pushop.outgoing
232 unbundle = pushop.remote.capable('unbundle')
231 unbundle = pushop.remote.capable('unbundle')
233 # TODO: get bundlecaps from remote
232 # TODO: get bundlecaps from remote
234 bundlecaps = None
233 bundlecaps = None
235 # create a changegroup from local
234 # create a changegroup from local
236 if pushop.revs is None and not (outgoing.excluded
235 if pushop.revs is None and not (outgoing.excluded
237 or pushop.repo.changelog.filteredrevs):
236 or pushop.repo.changelog.filteredrevs):
238 # push everything,
237 # push everything,
239 # use the fast path, no race possible on push
238 # use the fast path, no race possible on push
240 bundler = changegroup.bundle10(pushop.repo, bundlecaps)
239 bundler = changegroup.bundle10(pushop.repo, bundlecaps)
241 cg = changegroup.getsubset(pushop.repo,
240 cg = changegroup.getsubset(pushop.repo,
242 outgoing,
241 outgoing,
243 bundler,
242 bundler,
244 'push',
243 'push',
245 fastpath=True)
244 fastpath=True)
246 else:
245 else:
247 cg = changegroup.getlocalbundle(pushop.repo, 'push', outgoing,
246 cg = changegroup.getlocalbundle(pushop.repo, 'push', outgoing,
248 bundlecaps)
247 bundlecaps)
249
248
250 # apply changegroup to remote
249 # apply changegroup to remote
251 if unbundle:
250 if unbundle:
252 # local repo finds heads on server, finds out what
251 # local repo finds heads on server, finds out what
253 # revs it must push. once revs transferred, if server
252 # revs it must push. once revs transferred, if server
254 # finds it has different heads (someone else won
253 # finds it has different heads (someone else won
255 # commit/push race), server aborts.
254 # commit/push race), server aborts.
256 if pushop.force:
255 if pushop.force:
257 remoteheads = ['force']
256 remoteheads = ['force']
258 else:
257 else:
259 remoteheads = pushop.remoteheads
258 remoteheads = pushop.remoteheads
260 # ssh: return remote's addchangegroup()
259 # ssh: return remote's addchangegroup()
261 # http: return remote's addchangegroup() or 0 for error
260 # http: return remote's addchangegroup() or 0 for error
262 pushop.ret = pushop.remote.unbundle(cg, remoteheads,
261 pushop.ret = pushop.remote.unbundle(cg, remoteheads,
263 'push')
262 'push')
264 else:
263 else:
265 # we return an integer indicating remote head count
264 # we return an integer indicating remote head count
266 # change
265 # change
267 pushop.ret = pushop.remote.addchangegroup(cg, 'push', pushop.repo.url())
266 pushop.ret = pushop.remote.addchangegroup(cg, 'push', pushop.repo.url())
268
267
269 def _pushcomputecommonheads(pushop):
268 def _pushcomputecommonheads(pushop):
270 unfi = pushop.repo.unfiltered()
269 unfi = pushop.repo.unfiltered()
271 if pushop.ret:
270 if pushop.ret:
272 # push succeed, synchronize target of the push
271 # push succeed, synchronize target of the push
273 cheads = pushop.outgoing.missingheads
272 cheads = pushop.outgoing.missingheads
274 elif pushop.revs is None:
273 elif pushop.revs is None:
275 # All out push fails. synchronize all common
274 # All out push fails. synchronize all common
276 cheads = pushop.outgoing.commonheads
275 cheads = pushop.outgoing.commonheads
277 else:
276 else:
278 # I want cheads = heads(::missingheads and ::commonheads)
277 # I want cheads = heads(::missingheads and ::commonheads)
279 # (missingheads is revs with secret changeset filtered out)
278 # (missingheads is revs with secret changeset filtered out)
280 #
279 #
281 # This can be expressed as:
280 # This can be expressed as:
282 # cheads = ( (missingheads and ::commonheads)
281 # cheads = ( (missingheads and ::commonheads)
283 # + (commonheads and ::missingheads))"
282 # + (commonheads and ::missingheads))"
284 # )
283 # )
285 #
284 #
286 # while trying to push we already computed the following:
285 # while trying to push we already computed the following:
287 # common = (::commonheads)
286 # common = (::commonheads)
288 # missing = ((commonheads::missingheads) - commonheads)
287 # missing = ((commonheads::missingheads) - commonheads)
289 #
288 #
290 # We can pick:
289 # We can pick:
291 # * missingheads part of common (::commonheads)
290 # * missingheads part of common (::commonheads)
292 common = set(pushop.outgoing.common)
291 common = set(pushop.outgoing.common)
293 nm = pushop.repo.changelog.nodemap
292 nm = pushop.repo.changelog.nodemap
294 cheads = [node for node in pushop.revs if nm[node] in common]
293 cheads = [node for node in pushop.revs if nm[node] in common]
295 # and
294 # and
296 # * commonheads parents on missing
295 # * commonheads parents on missing
297 revset = unfi.set('%ln and parents(roots(%ln))',
296 revset = unfi.set('%ln and parents(roots(%ln))',
298 pushop.outgoing.commonheads,
297 pushop.outgoing.commonheads,
299 pushop.outgoing.missing)
298 pushop.outgoing.missing)
300 cheads.extend(c.node() for c in revset)
299 cheads.extend(c.node() for c in revset)
301 pushop.commonheads = cheads
300 pushop.commonheads = cheads
302
301
303 def _pushsyncphase(pushop):
302 def _pushsyncphase(pushop):
304 """synchronise phase information locally and remotely"""
303 """synchronise phase information locally and remotely"""
305 unfi = pushop.repo.unfiltered()
304 unfi = pushop.repo.unfiltered()
306 cheads = pushop.commonheads
305 cheads = pushop.commonheads
307 if pushop.ret:
306 if pushop.ret:
308 # push succeed, synchronize target of the push
307 # push succeed, synchronize target of the push
309 cheads = pushop.outgoing.missingheads
308 cheads = pushop.outgoing.missingheads
310 elif pushop.revs is None:
309 elif pushop.revs is None:
311 # All out push fails. synchronize all common
310 # All out push fails. synchronize all common
312 cheads = pushop.outgoing.commonheads
311 cheads = pushop.outgoing.commonheads
313 else:
312 else:
314 # I want cheads = heads(::missingheads and ::commonheads)
313 # I want cheads = heads(::missingheads and ::commonheads)
315 # (missingheads is revs with secret changeset filtered out)
314 # (missingheads is revs with secret changeset filtered out)
316 #
315 #
317 # This can be expressed as:
316 # This can be expressed as:
318 # cheads = ( (missingheads and ::commonheads)
317 # cheads = ( (missingheads and ::commonheads)
319 # + (commonheads and ::missingheads))"
318 # + (commonheads and ::missingheads))"
320 # )
319 # )
321 #
320 #
322 # while trying to push we already computed the following:
321 # while trying to push we already computed the following:
323 # common = (::commonheads)
322 # common = (::commonheads)
324 # missing = ((commonheads::missingheads) - commonheads)
323 # missing = ((commonheads::missingheads) - commonheads)
325 #
324 #
326 # We can pick:
325 # We can pick:
327 # * missingheads part of common (::commonheads)
326 # * missingheads part of common (::commonheads)
328 common = set(pushop.outgoing.common)
327 common = set(pushop.outgoing.common)
329 nm = pushop.repo.changelog.nodemap
328 nm = pushop.repo.changelog.nodemap
330 cheads = [node for node in pushop.revs if nm[node] in common]
329 cheads = [node for node in pushop.revs if nm[node] in common]
331 # and
330 # and
332 # * commonheads parents on missing
331 # * commonheads parents on missing
333 revset = unfi.set('%ln and parents(roots(%ln))',
332 revset = unfi.set('%ln and parents(roots(%ln))',
334 pushop.outgoing.commonheads,
333 pushop.outgoing.commonheads,
335 pushop.outgoing.missing)
334 pushop.outgoing.missing)
336 cheads.extend(c.node() for c in revset)
335 cheads.extend(c.node() for c in revset)
337 pushop.commonheads = cheads
336 pushop.commonheads = cheads
338 # even when we don't push, exchanging phase data is useful
337 # even when we don't push, exchanging phase data is useful
339 remotephases = pushop.remote.listkeys('phases')
338 remotephases = pushop.remote.listkeys('phases')
340 if (pushop.ui.configbool('ui', '_usedassubrepo', False)
339 if (pushop.ui.configbool('ui', '_usedassubrepo', False)
341 and remotephases # server supports phases
340 and remotephases # server supports phases
342 and pushop.ret is None # nothing was pushed
341 and pushop.ret is None # nothing was pushed
343 and remotephases.get('publishing', False)):
342 and remotephases.get('publishing', False)):
344 # When:
343 # When:
345 # - this is a subrepo push
344 # - this is a subrepo push
346 # - and remote support phase
345 # - and remote support phase
347 # - and no changeset was pushed
346 # - and no changeset was pushed
348 # - and remote is publishing
347 # - and remote is publishing
349 # We may be in issue 3871 case!
348 # We may be in issue 3871 case!
350 # We drop the possible phase synchronisation done by
349 # We drop the possible phase synchronisation done by
351 # courtesy to publish changesets possibly locally draft
350 # courtesy to publish changesets possibly locally draft
352 # on the remote.
351 # on the remote.
353 remotephases = {'publishing': 'True'}
352 remotephases = {'publishing': 'True'}
354 if not remotephases: # old server or public only reply from non-publishing
353 if not remotephases: # old server or public only reply from non-publishing
355 _localphasemove(pushop, cheads)
354 _localphasemove(pushop, cheads)
356 # don't push any phase data as there is nothing to push
355 # don't push any phase data as there is nothing to push
357 else:
356 else:
358 ana = phases.analyzeremotephases(pushop.repo, cheads,
357 ana = phases.analyzeremotephases(pushop.repo, cheads,
359 remotephases)
358 remotephases)
360 pheads, droots = ana
359 pheads, droots = ana
361 ### Apply remote phase on local
360 ### Apply remote phase on local
362 if remotephases.get('publishing', False):
361 if remotephases.get('publishing', False):
363 _localphasemove(pushop, cheads)
362 _localphasemove(pushop, cheads)
364 else: # publish = False
363 else: # publish = False
365 _localphasemove(pushop, pheads)
364 _localphasemove(pushop, pheads)
366 _localphasemove(pushop, cheads, phases.draft)
365 _localphasemove(pushop, cheads, phases.draft)
367 ### Apply local phase on remote
366 ### Apply local phase on remote
368
367
369 # Get the list of all revs draft on remote by public here.
368 # Get the list of all revs draft on remote by public here.
370 # XXX Beware that revset break if droots is not strictly
369 # XXX Beware that revset break if droots is not strictly
371 # XXX root we may want to ensure it is but it is costly
370 # XXX root we may want to ensure it is but it is costly
372 outdated = unfi.set('heads((%ln::%ln) and public())',
371 outdated = unfi.set('heads((%ln::%ln) and public())',
373 droots, cheads)
372 droots, cheads)
374 for newremotehead in outdated:
373 for newremotehead in outdated:
375 r = pushop.remote.pushkey('phases',
374 r = pushop.remote.pushkey('phases',
376 newremotehead.hex(),
375 newremotehead.hex(),
377 str(phases.draft),
376 str(phases.draft),
378 str(phases.public))
377 str(phases.public))
379 if not r:
378 if not r:
380 pushop.ui.warn(_('updating %s to public failed!\n')
379 pushop.ui.warn(_('updating %s to public failed!\n')
381 % newremotehead)
380 % newremotehead)
382
381
383 def _localphasemove(pushop, nodes, phase=phases.public):
382 def _localphasemove(pushop, nodes, phase=phases.public):
384 """move <nodes> to <phase> in the local source repo"""
383 """move <nodes> to <phase> in the local source repo"""
385 if pushop.locallocked:
384 if pushop.locallocked:
386 phases.advanceboundary(pushop.repo, phase, nodes)
385 phases.advanceboundary(pushop.repo, phase, nodes)
387 else:
386 else:
388 # repo is not locked, do not change any phases!
387 # repo is not locked, do not change any phases!
389 # Informs the user that phases should have been moved when
388 # Informs the user that phases should have been moved when
390 # applicable.
389 # applicable.
391 actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()]
390 actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()]
392 phasestr = phases.phasenames[phase]
391 phasestr = phases.phasenames[phase]
393 if actualmoves:
392 if actualmoves:
394 pushop.ui.status(_('cannot lock source repo, skipping '
393 pushop.ui.status(_('cannot lock source repo, skipping '
395 'local %s phase update\n') % phasestr)
394 'local %s phase update\n') % phasestr)
396
395
397 def _pushobsolete(pushop):
396 def _pushobsolete(pushop):
398 """utility function to push obsolete markers to a remote"""
397 """utility function to push obsolete markers to a remote"""
399 pushop.ui.debug('try to push obsolete markers to remote\n')
398 pushop.ui.debug('try to push obsolete markers to remote\n')
400 repo = pushop.repo
399 repo = pushop.repo
401 remote = pushop.remote
400 remote = pushop.remote
402 if (obsolete._enabled and repo.obsstore and
401 if (obsolete._enabled and repo.obsstore and
403 'obsolete' in remote.listkeys('namespaces')):
402 'obsolete' in remote.listkeys('namespaces')):
404 rslts = []
403 rslts = []
405 remotedata = repo.listkeys('obsolete')
404 remotedata = repo.listkeys('obsolete')
406 for key in sorted(remotedata, reverse=True):
405 for key in sorted(remotedata, reverse=True):
407 # reverse sort to ensure we end with dump0
406 # reverse sort to ensure we end with dump0
408 data = remotedata[key]
407 data = remotedata[key]
409 rslts.append(remote.pushkey('obsolete', key, '', data))
408 rslts.append(remote.pushkey('obsolete', key, '', data))
410 if [r for r in rslts if not r]:
409 if [r for r in rslts if not r]:
411 msg = _('failed to push some obsolete markers!\n')
410 msg = _('failed to push some obsolete markers!\n')
412 repo.ui.warn(msg)
411 repo.ui.warn(msg)
413
412
414 def _pushbookmark(pushop):
413 def _pushbookmark(pushop):
415 """Update bookmark position on remote"""
414 """Update bookmark position on remote"""
416 ui = pushop.ui
415 ui = pushop.ui
417 repo = pushop.repo.unfiltered()
416 repo = pushop.repo.unfiltered()
418 remote = pushop.remote
417 remote = pushop.remote
419 ui.debug("checking for updated bookmarks\n")
418 ui.debug("checking for updated bookmarks\n")
420 revnums = map(repo.changelog.rev, pushop.revs or [])
419 revnums = map(repo.changelog.rev, pushop.revs or [])
421 ancestors = [a for a in repo.changelog.ancestors(revnums, inclusive=True)]
420 ancestors = [a for a in repo.changelog.ancestors(revnums, inclusive=True)]
422 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid
421 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid
423 ) = bookmarks.compare(repo, repo._bookmarks, remote.listkeys('bookmarks'),
422 ) = bookmarks.compare(repo, repo._bookmarks, remote.listkeys('bookmarks'),
424 srchex=hex)
423 srchex=hex)
425
424
426 for b, scid, dcid in advsrc:
425 for b, scid, dcid in advsrc:
427 if ancestors and repo[scid].rev() not in ancestors:
426 if ancestors and repo[scid].rev() not in ancestors:
428 continue
427 continue
429 if remote.pushkey('bookmarks', b, dcid, scid):
428 if remote.pushkey('bookmarks', b, dcid, scid):
430 ui.status(_("updating bookmark %s\n") % b)
429 ui.status(_("updating bookmark %s\n") % b)
431 else:
430 else:
432 ui.warn(_('updating bookmark %s failed!\n') % b)
431 ui.warn(_('updating bookmark %s failed!\n') % b)
433
432
434 class pulloperation(object):
433 class pulloperation(object):
435 """A object that represent a single pull operation
434 """A object that represent a single pull operation
436
435
437 It purpose is to carry push related state and very common operation.
436 It purpose is to carry push related state and very common operation.
438
437
439 A new should be created at the beginning of each pull and discarded
438 A new should be created at the beginning of each pull and discarded
440 afterward.
439 afterward.
441 """
440 """
442
441
443 def __init__(self, repo, remote, heads=None, force=False):
442 def __init__(self, repo, remote, heads=None, force=False):
444 # repo we pull into
443 # repo we pull into
445 self.repo = repo
444 self.repo = repo
446 # repo we pull from
445 # repo we pull from
447 self.remote = remote
446 self.remote = remote
448 # revision we try to pull (None is "all")
447 # revision we try to pull (None is "all")
449 self.heads = heads
448 self.heads = heads
450 # do we force pull?
449 # do we force pull?
451 self.force = force
450 self.force = force
452 # the name the pull transaction
451 # the name the pull transaction
453 self._trname = 'pull\n' + util.hidepassword(remote.url())
452 self._trname = 'pull\n' + util.hidepassword(remote.url())
454 # hold the transaction once created
453 # hold the transaction once created
455 self._tr = None
454 self._tr = None
456 # set of common changeset between local and remote before pull
455 # set of common changeset between local and remote before pull
457 self.common = None
456 self.common = None
458 # set of pulled head
457 # set of pulled head
459 self.rheads = None
458 self.rheads = None
460 # list of missing changeset to fetch remotely
459 # list of missing changeset to fetch remotely
461 self.fetch = None
460 self.fetch = None
462 # result of changegroup pulling (used as return code by pull)
461 # result of changegroup pulling (used as return code by pull)
463 self.cgresult = None
462 self.cgresult = None
464 # list of step remaining todo (related to future bundle2 usage)
463 # list of step remaining todo (related to future bundle2 usage)
465 self.todosteps = set(['changegroup', 'phases', 'obsmarkers'])
464 self.todosteps = set(['changegroup', 'phases', 'obsmarkers'])
466
465
467 @util.propertycache
466 @util.propertycache
468 def pulledsubset(self):
467 def pulledsubset(self):
469 """heads of the set of changeset target by the pull"""
468 """heads of the set of changeset target by the pull"""
470 # compute target subset
469 # compute target subset
471 if self.heads is None:
470 if self.heads is None:
472 # We pulled every thing possible
471 # We pulled every thing possible
473 # sync on everything common
472 # sync on everything common
474 c = set(self.common)
473 c = set(self.common)
475 ret = list(self.common)
474 ret = list(self.common)
476 for n in self.rheads:
475 for n in self.rheads:
477 if n not in c:
476 if n not in c:
478 ret.append(n)
477 ret.append(n)
479 return ret
478 return ret
480 else:
479 else:
481 # We pulled a specific subset
480 # We pulled a specific subset
482 # sync on this subset
481 # sync on this subset
483 return self.heads
482 return self.heads
484
483
485 def gettransaction(self):
484 def gettransaction(self):
486 """get appropriate pull transaction, creating it if needed"""
485 """get appropriate pull transaction, creating it if needed"""
487 if self._tr is None:
486 if self._tr is None:
488 self._tr = self.repo.transaction(self._trname)
487 self._tr = self.repo.transaction(self._trname)
489 return self._tr
488 return self._tr
490
489
491 def closetransaction(self):
490 def closetransaction(self):
492 """close transaction if created"""
491 """close transaction if created"""
493 if self._tr is not None:
492 if self._tr is not None:
494 self._tr.close()
493 self._tr.close()
495
494
496 def releasetransaction(self):
495 def releasetransaction(self):
497 """release transaction if created"""
496 """release transaction if created"""
498 if self._tr is not None:
497 if self._tr is not None:
499 self._tr.release()
498 self._tr.release()
500
499
501 def pull(repo, remote, heads=None, force=False):
500 def pull(repo, remote, heads=None, force=False):
502 pullop = pulloperation(repo, remote, heads, force)
501 pullop = pulloperation(repo, remote, heads, force)
503 if pullop.remote.local():
502 if pullop.remote.local():
504 missing = set(pullop.remote.requirements) - pullop.repo.supported
503 missing = set(pullop.remote.requirements) - pullop.repo.supported
505 if missing:
504 if missing:
506 msg = _("required features are not"
505 msg = _("required features are not"
507 " supported in the destination:"
506 " supported in the destination:"
508 " %s") % (', '.join(sorted(missing)))
507 " %s") % (', '.join(sorted(missing)))
509 raise util.Abort(msg)
508 raise util.Abort(msg)
510
509
511 lock = pullop.repo.lock()
510 lock = pullop.repo.lock()
512 try:
511 try:
513 _pulldiscovery(pullop)
512 _pulldiscovery(pullop)
514 if pullop.remote.capable('bundle2'):
513 if pullop.remote.capable('bundle2'):
515 _pullbundle2(pullop)
514 _pullbundle2(pullop)
516 if 'changegroup' in pullop.todosteps:
515 if 'changegroup' in pullop.todosteps:
517 _pullchangeset(pullop)
516 _pullchangeset(pullop)
518 if 'phases' in pullop.todosteps:
517 if 'phases' in pullop.todosteps:
519 _pullphase(pullop)
518 _pullphase(pullop)
520 if 'obsmarkers' in pullop.todosteps:
519 if 'obsmarkers' in pullop.todosteps:
521 _pullobsolete(pullop)
520 _pullobsolete(pullop)
522 pullop.closetransaction()
521 pullop.closetransaction()
523 finally:
522 finally:
524 pullop.releasetransaction()
523 pullop.releasetransaction()
525 lock.release()
524 lock.release()
526
525
527 return pullop.cgresult
526 return pullop.cgresult
528
527
529 def _pulldiscovery(pullop):
528 def _pulldiscovery(pullop):
530 """discovery phase for the pull
529 """discovery phase for the pull
531
530
532 Current handle changeset discovery only, will change handle all discovery
531 Current handle changeset discovery only, will change handle all discovery
533 at some point."""
532 at some point."""
534 tmp = discovery.findcommonincoming(pullop.repo.unfiltered(),
533 tmp = discovery.findcommonincoming(pullop.repo.unfiltered(),
535 pullop.remote,
534 pullop.remote,
536 heads=pullop.heads,
535 heads=pullop.heads,
537 force=pullop.force)
536 force=pullop.force)
538 pullop.common, pullop.fetch, pullop.rheads = tmp
537 pullop.common, pullop.fetch, pullop.rheads = tmp
539
538
540 def _pullbundle2(pullop):
539 def _pullbundle2(pullop):
541 """pull data using bundle2
540 """pull data using bundle2
542
541
543 For now, the only supported data are changegroup."""
542 For now, the only supported data are changegroup."""
544 kwargs = {'bundlecaps': set(['HG20'])}
543 kwargs = {'bundlecaps': set(['HG20'])}
545 # pulling changegroup
544 # pulling changegroup
546 pullop.todosteps.remove('changegroup')
545 pullop.todosteps.remove('changegroup')
547 if not pullop.fetch:
546 if not pullop.fetch:
548 pullop.repo.ui.status(_("no changes found\n"))
547 pullop.repo.ui.status(_("no changes found\n"))
549 pullop.cgresult = 0
548 pullop.cgresult = 0
550 else:
549 else:
551 kwargs['common'] = pullop.common
550 kwargs['common'] = pullop.common
552 kwargs['heads'] = pullop.heads or pullop.rheads
551 kwargs['heads'] = pullop.heads or pullop.rheads
553 if pullop.heads is None and list(pullop.common) == [nullid]:
552 if pullop.heads is None and list(pullop.common) == [nullid]:
554 pullop.repo.ui.status(_("requesting all changes\n"))
553 pullop.repo.ui.status(_("requesting all changes\n"))
555 if kwargs.keys() == ['format']:
554 if kwargs.keys() == ['format']:
556 return # nothing to pull
555 return # nothing to pull
557 bundle = pullop.remote.getbundle('pull', **kwargs)
556 bundle = pullop.remote.getbundle('pull', **kwargs)
558 try:
557 try:
559 op = bundle2.processbundle(pullop.repo, bundle, pullop.gettransaction)
558 op = bundle2.processbundle(pullop.repo, bundle, pullop.gettransaction)
560 except KeyError, exc:
559 except KeyError, exc:
561 raise util.Abort('missing support for %s' % exc)
560 raise util.Abort('missing support for %s' % exc)
562 assert len(op.records['changegroup']) == 1
561 assert len(op.records['changegroup']) == 1
563 pullop.cgresult = op.records['changegroup'][0]['return']
562 pullop.cgresult = op.records['changegroup'][0]['return']
564
563
565 def _pullchangeset(pullop):
564 def _pullchangeset(pullop):
566 """pull changeset from unbundle into the local repo"""
565 """pull changeset from unbundle into the local repo"""
567 # We delay the open of the transaction as late as possible so we
566 # We delay the open of the transaction as late as possible so we
568 # don't open transaction for nothing or you break future useful
567 # don't open transaction for nothing or you break future useful
569 # rollback call
568 # rollback call
570 pullop.todosteps.remove('changegroup')
569 pullop.todosteps.remove('changegroup')
571 if not pullop.fetch:
570 if not pullop.fetch:
572 pullop.repo.ui.status(_("no changes found\n"))
571 pullop.repo.ui.status(_("no changes found\n"))
573 pullop.cgresult = 0
572 pullop.cgresult = 0
574 return
573 return
575 pullop.gettransaction()
574 pullop.gettransaction()
576 if pullop.heads is None and list(pullop.common) == [nullid]:
575 if pullop.heads is None and list(pullop.common) == [nullid]:
577 pullop.repo.ui.status(_("requesting all changes\n"))
576 pullop.repo.ui.status(_("requesting all changes\n"))
578 elif pullop.heads is None and pullop.remote.capable('changegroupsubset'):
577 elif pullop.heads is None and pullop.remote.capable('changegroupsubset'):
579 # issue1320, avoid a race if remote changed after discovery
578 # issue1320, avoid a race if remote changed after discovery
580 pullop.heads = pullop.rheads
579 pullop.heads = pullop.rheads
581
580
582 if pullop.remote.capable('getbundle'):
581 if pullop.remote.capable('getbundle'):
583 # TODO: get bundlecaps from remote
582 # TODO: get bundlecaps from remote
584 cg = pullop.remote.getbundle('pull', common=pullop.common,
583 cg = pullop.remote.getbundle('pull', common=pullop.common,
585 heads=pullop.heads or pullop.rheads)
584 heads=pullop.heads or pullop.rheads)
586 elif pullop.heads is None:
585 elif pullop.heads is None:
587 cg = pullop.remote.changegroup(pullop.fetch, 'pull')
586 cg = pullop.remote.changegroup(pullop.fetch, 'pull')
588 elif not pullop.remote.capable('changegroupsubset'):
587 elif not pullop.remote.capable('changegroupsubset'):
589 raise util.Abort(_("partial pull cannot be done because "
588 raise util.Abort(_("partial pull cannot be done because "
590 "other repository doesn't support "
589 "other repository doesn't support "
591 "changegroupsubset."))
590 "changegroupsubset."))
592 else:
591 else:
593 cg = pullop.remote.changegroupsubset(pullop.fetch, pullop.heads, 'pull')
592 cg = pullop.remote.changegroupsubset(pullop.fetch, pullop.heads, 'pull')
594 pullop.cgresult = changegroup.addchangegroup(pullop.repo, cg, 'pull',
593 pullop.cgresult = changegroup.addchangegroup(pullop.repo, cg, 'pull',
595 pullop.remote.url())
594 pullop.remote.url())
596
595
597 def _pullphase(pullop):
596 def _pullphase(pullop):
598 # Get remote phases data from remote
597 # Get remote phases data from remote
599 pullop.todosteps.remove('phases')
598 pullop.todosteps.remove('phases')
600 remotephases = pullop.remote.listkeys('phases')
599 remotephases = pullop.remote.listkeys('phases')
601 publishing = bool(remotephases.get('publishing', False))
600 publishing = bool(remotephases.get('publishing', False))
602 if remotephases and not publishing:
601 if remotephases and not publishing:
603 # remote is new and unpublishing
602 # remote is new and unpublishing
604 pheads, _dr = phases.analyzeremotephases(pullop.repo,
603 pheads, _dr = phases.analyzeremotephases(pullop.repo,
605 pullop.pulledsubset,
604 pullop.pulledsubset,
606 remotephases)
605 remotephases)
607 phases.advanceboundary(pullop.repo, phases.public, pheads)
606 phases.advanceboundary(pullop.repo, phases.public, pheads)
608 phases.advanceboundary(pullop.repo, phases.draft,
607 phases.advanceboundary(pullop.repo, phases.draft,
609 pullop.pulledsubset)
608 pullop.pulledsubset)
610 else:
609 else:
611 # Remote is old or publishing all common changesets
610 # Remote is old or publishing all common changesets
612 # should be seen as public
611 # should be seen as public
613 phases.advanceboundary(pullop.repo, phases.public,
612 phases.advanceboundary(pullop.repo, phases.public,
614 pullop.pulledsubset)
613 pullop.pulledsubset)
615
614
616 def _pullobsolete(pullop):
615 def _pullobsolete(pullop):
617 """utility function to pull obsolete markers from a remote
616 """utility function to pull obsolete markers from a remote
618
617
619 The `gettransaction` is function that return the pull transaction, creating
618 The `gettransaction` is function that return the pull transaction, creating
620 one if necessary. We return the transaction to inform the calling code that
619 one if necessary. We return the transaction to inform the calling code that
621 a new transaction have been created (when applicable).
620 a new transaction have been created (when applicable).
622
621
623 Exists mostly to allow overriding for experimentation purpose"""
622 Exists mostly to allow overriding for experimentation purpose"""
624 pullop.todosteps.remove('obsmarkers')
623 pullop.todosteps.remove('obsmarkers')
625 tr = None
624 tr = None
626 if obsolete._enabled:
625 if obsolete._enabled:
627 pullop.repo.ui.debug('fetching remote obsolete markers\n')
626 pullop.repo.ui.debug('fetching remote obsolete markers\n')
628 remoteobs = pullop.remote.listkeys('obsolete')
627 remoteobs = pullop.remote.listkeys('obsolete')
629 if 'dump0' in remoteobs:
628 if 'dump0' in remoteobs:
630 tr = pullop.gettransaction()
629 tr = pullop.gettransaction()
631 for key in sorted(remoteobs, reverse=True):
630 for key in sorted(remoteobs, reverse=True):
632 if key.startswith('dump'):
631 if key.startswith('dump'):
633 data = base85.b85decode(remoteobs[key])
632 data = base85.b85decode(remoteobs[key])
634 pullop.repo.obsstore.mergemarkers(tr, data)
633 pullop.repo.obsstore.mergemarkers(tr, data)
635 pullop.repo.invalidatevolatilesets()
634 pullop.repo.invalidatevolatilesets()
636 return tr
635 return tr
637
636
638 def getbundle(repo, source, heads=None, common=None, bundlecaps=None):
637 def getbundle(repo, source, heads=None, common=None, bundlecaps=None):
639 """return a full bundle (with potentially multiple kind of parts)
638 """return a full bundle (with potentially multiple kind of parts)
640
639
641 Could be a bundle HG10 or a bundle HG20 depending on bundlecaps
640 Could be a bundle HG10 or a bundle HG20 depending on bundlecaps
642 passed. For now, the bundle can contain only changegroup, but this will
641 passed. For now, the bundle can contain only changegroup, but this will
643 changes when more part type will be available for bundle2.
642 changes when more part type will be available for bundle2.
644
643
645 This is different from changegroup.getbundle that only returns an HG10
644 This is different from changegroup.getbundle that only returns an HG10
646 changegroup bundle. They may eventually get reunited in the future when we
645 changegroup bundle. They may eventually get reunited in the future when we
647 have a clearer idea of the API we what to query different data.
646 have a clearer idea of the API we what to query different data.
648
647
649 The implementation is at a very early stage and will get massive rework
648 The implementation is at a very early stage and will get massive rework
650 when the API of bundle is refined.
649 when the API of bundle is refined.
651 """
650 """
652 # build bundle here.
651 # build bundle here.
653 cg = changegroup.getbundle(repo, source, heads=heads,
652 cg = changegroup.getbundle(repo, source, heads=heads,
654 common=common, bundlecaps=bundlecaps)
653 common=common, bundlecaps=bundlecaps)
655 if bundlecaps is None or 'HG20' not in bundlecaps:
654 if bundlecaps is None or 'HG20' not in bundlecaps:
656 return cg
655 return cg
657 # very crude first implementation,
656 # very crude first implementation,
658 # the bundle API will change and the generation will be done lazily.
657 # the bundle API will change and the generation will be done lazily.
659 bundler = bundle2.bundle20(repo.ui)
658 bundler = bundle2.bundle20(repo.ui)
660 part = bundle2.bundlepart('changegroup', data=cg.getchunks())
659 part = bundle2.bundlepart('changegroup', data=cg.getchunks())
661 bundler.addpart(part)
660 bundler.addpart(part)
662 return util.chunkbuffer(bundler.getchunks())
661 return util.chunkbuffer(bundler.getchunks())
663
662
664 class PushRaced(RuntimeError):
663 class PushRaced(RuntimeError):
665 """An exception raised during unbundling that indicate a push race"""
664 """An exception raised during unbundling that indicate a push race"""
666
665
667 def check_heads(repo, their_heads, context):
666 def check_heads(repo, their_heads, context):
668 """check if the heads of a repo have been modified
667 """check if the heads of a repo have been modified
669
668
670 Used by peer for unbundling.
669 Used by peer for unbundling.
671 """
670 """
672 heads = repo.heads()
671 heads = repo.heads()
673 heads_hash = util.sha1(''.join(sorted(heads))).digest()
672 heads_hash = util.sha1(''.join(sorted(heads))).digest()
674 if not (their_heads == ['force'] or their_heads == heads or
673 if not (their_heads == ['force'] or their_heads == heads or
675 their_heads == ['hashed', heads_hash]):
674 their_heads == ['hashed', heads_hash]):
676 # someone else committed/pushed/unbundled while we
675 # someone else committed/pushed/unbundled while we
677 # were transferring data
676 # were transferring data
678 raise PushRaced('repository changed while %s - '
677 raise PushRaced('repository changed while %s - '
679 'please try again' % context)
678 'please try again' % context)
680
679
681 def unbundle(repo, cg, heads, source, url):
680 def unbundle(repo, cg, heads, source, url):
682 """Apply a bundle to a repo.
681 """Apply a bundle to a repo.
683
682
684 this function makes sure the repo is locked during the application and have
683 this function makes sure the repo is locked during the application and have
685 mechanism to check that no push race occurred between the creation of the
684 mechanism to check that no push race occurred between the creation of the
686 bundle and its application.
685 bundle and its application.
687
686
688 If the push was raced as PushRaced exception is raised."""
687 If the push was raced as PushRaced exception is raised."""
689 r = 0
688 r = 0
690 # need a transaction when processing a bundle2 stream
689 # need a transaction when processing a bundle2 stream
691 tr = None
690 tr = None
692 lock = repo.lock()
691 lock = repo.lock()
693 try:
692 try:
694 check_heads(repo, heads, 'uploading changes')
693 check_heads(repo, heads, 'uploading changes')
695 # push can proceed
694 # push can proceed
696 if util.safehasattr(cg, 'params'):
695 if util.safehasattr(cg, 'params'):
697 tr = repo.transaction('unbundle')
696 tr = repo.transaction('unbundle')
698 ret = bundle2.processbundle(repo, cg, lambda: tr)
697 ret = bundle2.processbundle(repo, cg, lambda: tr)
699 tr.close()
698 tr.close()
700 stream = util.chunkbuffer(ret.reply.getchunks())
699 stream = util.chunkbuffer(ret.reply.getchunks())
701 r = bundle2.unbundle20(repo.ui, stream)
700 r = bundle2.unbundle20(repo.ui, stream)
702 else:
701 else:
703 r = changegroup.addchangegroup(repo, cg, source, url)
702 r = changegroup.addchangegroup(repo, cg, source, url)
704 finally:
703 finally:
705 if tr is not None:
704 if tr is not None:
706 tr.release()
705 tr.release()
707 lock.release()
706 lock.release()
708 return r
707 return r
@@ -1,1898 +1,1899 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 from node import hex, nullid, short
7 from node import hex, nullid, short
8 from i18n import _
8 from i18n import _
9 import peer, changegroup, subrepo, pushkey, obsolete, repoview
9 import peer, changegroup, subrepo, pushkey, obsolete, repoview
10 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
10 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
11 import lock as lockmod
11 import lock as lockmod
12 import transaction, store, encoding, exchange, bundle2
12 import transaction, store, encoding, exchange, bundle2
13 import scmutil, util, extensions, hook, error, revset
13 import scmutil, util, extensions, hook, error, revset
14 import match as matchmod
14 import match as matchmod
15 import merge as mergemod
15 import merge as mergemod
16 import tags as tagsmod
16 import tags as tagsmod
17 from lock import release
17 from lock import release
18 import weakref, errno, os, time, inspect
18 import weakref, errno, os, time, inspect
19 import branchmap, pathutil
19 import branchmap, pathutil
20 propertycache = util.propertycache
20 propertycache = util.propertycache
21 filecache = scmutil.filecache
21 filecache = scmutil.filecache
22
22
23 class repofilecache(filecache):
23 class repofilecache(filecache):
24 """All filecache usage on repo are done for logic that should be unfiltered
24 """All filecache usage on repo are done for logic that should be unfiltered
25 """
25 """
26
26
27 def __get__(self, repo, type=None):
27 def __get__(self, repo, type=None):
28 return super(repofilecache, self).__get__(repo.unfiltered(), type)
28 return super(repofilecache, self).__get__(repo.unfiltered(), type)
29 def __set__(self, repo, value):
29 def __set__(self, repo, value):
30 return super(repofilecache, self).__set__(repo.unfiltered(), value)
30 return super(repofilecache, self).__set__(repo.unfiltered(), value)
31 def __delete__(self, repo):
31 def __delete__(self, repo):
32 return super(repofilecache, self).__delete__(repo.unfiltered())
32 return super(repofilecache, self).__delete__(repo.unfiltered())
33
33
34 class storecache(repofilecache):
34 class storecache(repofilecache):
35 """filecache for files in the store"""
35 """filecache for files in the store"""
36 def join(self, obj, fname):
36 def join(self, obj, fname):
37 return obj.sjoin(fname)
37 return obj.sjoin(fname)
38
38
39 class unfilteredpropertycache(propertycache):
39 class unfilteredpropertycache(propertycache):
40 """propertycache that apply to unfiltered repo only"""
40 """propertycache that apply to unfiltered repo only"""
41
41
42 def __get__(self, repo, type=None):
42 def __get__(self, repo, type=None):
43 unfi = repo.unfiltered()
43 unfi = repo.unfiltered()
44 if unfi is repo:
44 if unfi is repo:
45 return super(unfilteredpropertycache, self).__get__(unfi)
45 return super(unfilteredpropertycache, self).__get__(unfi)
46 return getattr(unfi, self.name)
46 return getattr(unfi, self.name)
47
47
48 class filteredpropertycache(propertycache):
48 class filteredpropertycache(propertycache):
49 """propertycache that must take filtering in account"""
49 """propertycache that must take filtering in account"""
50
50
51 def cachevalue(self, obj, value):
51 def cachevalue(self, obj, value):
52 object.__setattr__(obj, self.name, value)
52 object.__setattr__(obj, self.name, value)
53
53
54
54
55 def hasunfilteredcache(repo, name):
55 def hasunfilteredcache(repo, name):
56 """check if a repo has an unfilteredpropertycache value for <name>"""
56 """check if a repo has an unfilteredpropertycache value for <name>"""
57 return name in vars(repo.unfiltered())
57 return name in vars(repo.unfiltered())
58
58
59 def unfilteredmethod(orig):
59 def unfilteredmethod(orig):
60 """decorate method that always need to be run on unfiltered version"""
60 """decorate method that always need to be run on unfiltered version"""
61 def wrapper(repo, *args, **kwargs):
61 def wrapper(repo, *args, **kwargs):
62 return orig(repo.unfiltered(), *args, **kwargs)
62 return orig(repo.unfiltered(), *args, **kwargs)
63 return wrapper
63 return wrapper
64
64
65 moderncaps = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle',
65 moderncaps = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle',
66 'bundle2', 'unbundle'))
66 'bundle2', 'unbundle'))
67 legacycaps = moderncaps.union(set(['changegroupsubset']))
67 legacycaps = moderncaps.union(set(['changegroupsubset']))
68
68
69 class localpeer(peer.peerrepository):
69 class localpeer(peer.peerrepository):
70 '''peer for a local repo; reflects only the most recent API'''
70 '''peer for a local repo; reflects only the most recent API'''
71
71
72 def __init__(self, repo, caps=moderncaps):
72 def __init__(self, repo, caps=moderncaps):
73 peer.peerrepository.__init__(self)
73 peer.peerrepository.__init__(self)
74 self._repo = repo.filtered('served')
74 self._repo = repo.filtered('served')
75 self.ui = repo.ui
75 self.ui = repo.ui
76 self._caps = repo._restrictcapabilities(caps)
76 self._caps = repo._restrictcapabilities(caps)
77 self.requirements = repo.requirements
77 self.requirements = repo.requirements
78 self.supportedformats = repo.supportedformats
78 self.supportedformats = repo.supportedformats
79
79
80 def close(self):
80 def close(self):
81 self._repo.close()
81 self._repo.close()
82
82
83 def _capabilities(self):
83 def _capabilities(self):
84 return self._caps
84 return self._caps
85
85
86 def local(self):
86 def local(self):
87 return self._repo
87 return self._repo
88
88
89 def canpush(self):
89 def canpush(self):
90 return True
90 return True
91
91
92 def url(self):
92 def url(self):
93 return self._repo.url()
93 return self._repo.url()
94
94
95 def lookup(self, key):
95 def lookup(self, key):
96 return self._repo.lookup(key)
96 return self._repo.lookup(key)
97
97
98 def branchmap(self):
98 def branchmap(self):
99 return self._repo.branchmap()
99 return self._repo.branchmap()
100
100
101 def heads(self):
101 def heads(self):
102 return self._repo.heads()
102 return self._repo.heads()
103
103
104 def known(self, nodes):
104 def known(self, nodes):
105 return self._repo.known(nodes)
105 return self._repo.known(nodes)
106
106
107 def getbundle(self, source, heads=None, common=None, bundlecaps=None,
107 def getbundle(self, source, heads=None, common=None, bundlecaps=None,
108 format='HG10'):
108 format='HG10'):
109 cg = exchange.getbundle(self._repo, source, heads=heads,
109 cg = exchange.getbundle(self._repo, source, heads=heads,
110 common=common, bundlecaps=bundlecaps)
110 common=common, bundlecaps=bundlecaps)
111 if bundlecaps is not None and 'HG20' in bundlecaps:
111 if bundlecaps is not None and 'HG20' in bundlecaps:
112 # When requesting a bundle2, getbundle returns a stream to make the
112 # When requesting a bundle2, getbundle returns a stream to make the
113 # wire level function happier. We need to build a proper object
113 # wire level function happier. We need to build a proper object
114 # from it in local peer.
114 # from it in local peer.
115 cg = bundle2.unbundle20(self.ui, cg)
115 cg = bundle2.unbundle20(self.ui, cg)
116 return cg
116 return cg
117
117
118 # TODO We might want to move the next two calls into legacypeer and add
118 # TODO We might want to move the next two calls into legacypeer and add
119 # unbundle instead.
119 # unbundle instead.
120
120
121 def unbundle(self, cg, heads, url):
121 def unbundle(self, cg, heads, url):
122 """apply a bundle on a repo
122 """apply a bundle on a repo
123
123
124 This function handles the repo locking itself."""
124 This function handles the repo locking itself."""
125 try:
125 try:
126 cg = exchange.readbundle(self.ui, cg, None)
126 return exchange.unbundle(self._repo, cg, heads, 'push', url)
127 return exchange.unbundle(self._repo, cg, heads, 'push', url)
127 except exchange.PushRaced, exc:
128 except exchange.PushRaced, exc:
128 raise error.ResponseError(_('push failed:'), exc.message)
129 raise error.ResponseError(_('push failed:'), exc.message)
129
130
130 def lock(self):
131 def lock(self):
131 return self._repo.lock()
132 return self._repo.lock()
132
133
133 def addchangegroup(self, cg, source, url):
134 def addchangegroup(self, cg, source, url):
134 return changegroup.addchangegroup(self._repo, cg, source, url)
135 return changegroup.addchangegroup(self._repo, cg, source, url)
135
136
136 def pushkey(self, namespace, key, old, new):
137 def pushkey(self, namespace, key, old, new):
137 return self._repo.pushkey(namespace, key, old, new)
138 return self._repo.pushkey(namespace, key, old, new)
138
139
139 def listkeys(self, namespace):
140 def listkeys(self, namespace):
140 return self._repo.listkeys(namespace)
141 return self._repo.listkeys(namespace)
141
142
142 def debugwireargs(self, one, two, three=None, four=None, five=None):
143 def debugwireargs(self, one, two, three=None, four=None, five=None):
143 '''used to test argument passing over the wire'''
144 '''used to test argument passing over the wire'''
144 return "%s %s %s %s %s" % (one, two, three, four, five)
145 return "%s %s %s %s %s" % (one, two, three, four, five)
145
146
146 class locallegacypeer(localpeer):
147 class locallegacypeer(localpeer):
147 '''peer extension which implements legacy methods too; used for tests with
148 '''peer extension which implements legacy methods too; used for tests with
148 restricted capabilities'''
149 restricted capabilities'''
149
150
150 def __init__(self, repo):
151 def __init__(self, repo):
151 localpeer.__init__(self, repo, caps=legacycaps)
152 localpeer.__init__(self, repo, caps=legacycaps)
152
153
153 def branches(self, nodes):
154 def branches(self, nodes):
154 return self._repo.branches(nodes)
155 return self._repo.branches(nodes)
155
156
156 def between(self, pairs):
157 def between(self, pairs):
157 return self._repo.between(pairs)
158 return self._repo.between(pairs)
158
159
159 def changegroup(self, basenodes, source):
160 def changegroup(self, basenodes, source):
160 return changegroup.changegroup(self._repo, basenodes, source)
161 return changegroup.changegroup(self._repo, basenodes, source)
161
162
162 def changegroupsubset(self, bases, heads, source):
163 def changegroupsubset(self, bases, heads, source):
163 return changegroup.changegroupsubset(self._repo, bases, heads, source)
164 return changegroup.changegroupsubset(self._repo, bases, heads, source)
164
165
165 class localrepository(object):
166 class localrepository(object):
166
167
167 supportedformats = set(('revlogv1', 'generaldelta'))
168 supportedformats = set(('revlogv1', 'generaldelta'))
168 _basesupported = supportedformats | set(('store', 'fncache', 'shared',
169 _basesupported = supportedformats | set(('store', 'fncache', 'shared',
169 'dotencode'))
170 'dotencode'))
170 openerreqs = set(('revlogv1', 'generaldelta'))
171 openerreqs = set(('revlogv1', 'generaldelta'))
171 requirements = ['revlogv1']
172 requirements = ['revlogv1']
172 filtername = None
173 filtername = None
173
174
174 # a list of (ui, featureset) functions.
175 # a list of (ui, featureset) functions.
175 # only functions defined in module of enabled extensions are invoked
176 # only functions defined in module of enabled extensions are invoked
176 featuresetupfuncs = set()
177 featuresetupfuncs = set()
177
178
178 def _baserequirements(self, create):
179 def _baserequirements(self, create):
179 return self.requirements[:]
180 return self.requirements[:]
180
181
181 def __init__(self, baseui, path=None, create=False):
182 def __init__(self, baseui, path=None, create=False):
182 self.wvfs = scmutil.vfs(path, expandpath=True, realpath=True)
183 self.wvfs = scmutil.vfs(path, expandpath=True, realpath=True)
183 self.wopener = self.wvfs
184 self.wopener = self.wvfs
184 self.root = self.wvfs.base
185 self.root = self.wvfs.base
185 self.path = self.wvfs.join(".hg")
186 self.path = self.wvfs.join(".hg")
186 self.origroot = path
187 self.origroot = path
187 self.auditor = pathutil.pathauditor(self.root, self._checknested)
188 self.auditor = pathutil.pathauditor(self.root, self._checknested)
188 self.vfs = scmutil.vfs(self.path)
189 self.vfs = scmutil.vfs(self.path)
189 self.opener = self.vfs
190 self.opener = self.vfs
190 self.baseui = baseui
191 self.baseui = baseui
191 self.ui = baseui.copy()
192 self.ui = baseui.copy()
192 self.ui.copy = baseui.copy # prevent copying repo configuration
193 self.ui.copy = baseui.copy # prevent copying repo configuration
193 # A list of callback to shape the phase if no data were found.
194 # A list of callback to shape the phase if no data were found.
194 # Callback are in the form: func(repo, roots) --> processed root.
195 # Callback are in the form: func(repo, roots) --> processed root.
195 # This list it to be filled by extension during repo setup
196 # This list it to be filled by extension during repo setup
196 self._phasedefaults = []
197 self._phasedefaults = []
197 try:
198 try:
198 self.ui.readconfig(self.join("hgrc"), self.root)
199 self.ui.readconfig(self.join("hgrc"), self.root)
199 extensions.loadall(self.ui)
200 extensions.loadall(self.ui)
200 except IOError:
201 except IOError:
201 pass
202 pass
202
203
203 if self.featuresetupfuncs:
204 if self.featuresetupfuncs:
204 self.supported = set(self._basesupported) # use private copy
205 self.supported = set(self._basesupported) # use private copy
205 extmods = set(m.__name__ for n, m
206 extmods = set(m.__name__ for n, m
206 in extensions.extensions(self.ui))
207 in extensions.extensions(self.ui))
207 for setupfunc in self.featuresetupfuncs:
208 for setupfunc in self.featuresetupfuncs:
208 if setupfunc.__module__ in extmods:
209 if setupfunc.__module__ in extmods:
209 setupfunc(self.ui, self.supported)
210 setupfunc(self.ui, self.supported)
210 else:
211 else:
211 self.supported = self._basesupported
212 self.supported = self._basesupported
212
213
213 if not self.vfs.isdir():
214 if not self.vfs.isdir():
214 if create:
215 if create:
215 if not self.wvfs.exists():
216 if not self.wvfs.exists():
216 self.wvfs.makedirs()
217 self.wvfs.makedirs()
217 self.vfs.makedir(notindexed=True)
218 self.vfs.makedir(notindexed=True)
218 requirements = self._baserequirements(create)
219 requirements = self._baserequirements(create)
219 if self.ui.configbool('format', 'usestore', True):
220 if self.ui.configbool('format', 'usestore', True):
220 self.vfs.mkdir("store")
221 self.vfs.mkdir("store")
221 requirements.append("store")
222 requirements.append("store")
222 if self.ui.configbool('format', 'usefncache', True):
223 if self.ui.configbool('format', 'usefncache', True):
223 requirements.append("fncache")
224 requirements.append("fncache")
224 if self.ui.configbool('format', 'dotencode', True):
225 if self.ui.configbool('format', 'dotencode', True):
225 requirements.append('dotencode')
226 requirements.append('dotencode')
226 # create an invalid changelog
227 # create an invalid changelog
227 self.vfs.append(
228 self.vfs.append(
228 "00changelog.i",
229 "00changelog.i",
229 '\0\0\0\2' # represents revlogv2
230 '\0\0\0\2' # represents revlogv2
230 ' dummy changelog to prevent using the old repo layout'
231 ' dummy changelog to prevent using the old repo layout'
231 )
232 )
232 if self.ui.configbool('format', 'generaldelta', False):
233 if self.ui.configbool('format', 'generaldelta', False):
233 requirements.append("generaldelta")
234 requirements.append("generaldelta")
234 requirements = set(requirements)
235 requirements = set(requirements)
235 else:
236 else:
236 raise error.RepoError(_("repository %s not found") % path)
237 raise error.RepoError(_("repository %s not found") % path)
237 elif create:
238 elif create:
238 raise error.RepoError(_("repository %s already exists") % path)
239 raise error.RepoError(_("repository %s already exists") % path)
239 else:
240 else:
240 try:
241 try:
241 requirements = scmutil.readrequires(self.vfs, self.supported)
242 requirements = scmutil.readrequires(self.vfs, self.supported)
242 except IOError, inst:
243 except IOError, inst:
243 if inst.errno != errno.ENOENT:
244 if inst.errno != errno.ENOENT:
244 raise
245 raise
245 requirements = set()
246 requirements = set()
246
247
247 self.sharedpath = self.path
248 self.sharedpath = self.path
248 try:
249 try:
249 vfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'),
250 vfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'),
250 realpath=True)
251 realpath=True)
251 s = vfs.base
252 s = vfs.base
252 if not vfs.exists():
253 if not vfs.exists():
253 raise error.RepoError(
254 raise error.RepoError(
254 _('.hg/sharedpath points to nonexistent directory %s') % s)
255 _('.hg/sharedpath points to nonexistent directory %s') % s)
255 self.sharedpath = s
256 self.sharedpath = s
256 except IOError, inst:
257 except IOError, inst:
257 if inst.errno != errno.ENOENT:
258 if inst.errno != errno.ENOENT:
258 raise
259 raise
259
260
260 self.store = store.store(requirements, self.sharedpath, scmutil.vfs)
261 self.store = store.store(requirements, self.sharedpath, scmutil.vfs)
261 self.spath = self.store.path
262 self.spath = self.store.path
262 self.svfs = self.store.vfs
263 self.svfs = self.store.vfs
263 self.sopener = self.svfs
264 self.sopener = self.svfs
264 self.sjoin = self.store.join
265 self.sjoin = self.store.join
265 self.vfs.createmode = self.store.createmode
266 self.vfs.createmode = self.store.createmode
266 self._applyrequirements(requirements)
267 self._applyrequirements(requirements)
267 if create:
268 if create:
268 self._writerequirements()
269 self._writerequirements()
269
270
270
271
271 self._branchcaches = {}
272 self._branchcaches = {}
272 self.filterpats = {}
273 self.filterpats = {}
273 self._datafilters = {}
274 self._datafilters = {}
274 self._transref = self._lockref = self._wlockref = None
275 self._transref = self._lockref = self._wlockref = None
275
276
276 # A cache for various files under .hg/ that tracks file changes,
277 # A cache for various files under .hg/ that tracks file changes,
277 # (used by the filecache decorator)
278 # (used by the filecache decorator)
278 #
279 #
279 # Maps a property name to its util.filecacheentry
280 # Maps a property name to its util.filecacheentry
280 self._filecache = {}
281 self._filecache = {}
281
282
282 # hold sets of revision to be filtered
283 # hold sets of revision to be filtered
283 # should be cleared when something might have changed the filter value:
284 # should be cleared when something might have changed the filter value:
284 # - new changesets,
285 # - new changesets,
285 # - phase change,
286 # - phase change,
286 # - new obsolescence marker,
287 # - new obsolescence marker,
287 # - working directory parent change,
288 # - working directory parent change,
288 # - bookmark changes
289 # - bookmark changes
289 self.filteredrevcache = {}
290 self.filteredrevcache = {}
290
291
291 def close(self):
292 def close(self):
292 pass
293 pass
293
294
294 def _restrictcapabilities(self, caps):
295 def _restrictcapabilities(self, caps):
295 # bundle2 is not ready for prime time, drop it unless explicitly
296 # bundle2 is not ready for prime time, drop it unless explicitly
296 # required by the tests (or some brave tester)
297 # required by the tests (or some brave tester)
297 if not self.ui.configbool('server', 'bundle2', False):
298 if not self.ui.configbool('server', 'bundle2', False):
298 caps = set(caps)
299 caps = set(caps)
299 caps.discard('bundle2')
300 caps.discard('bundle2')
300 return caps
301 return caps
301
302
302 def _applyrequirements(self, requirements):
303 def _applyrequirements(self, requirements):
303 self.requirements = requirements
304 self.requirements = requirements
304 self.sopener.options = dict((r, 1) for r in requirements
305 self.sopener.options = dict((r, 1) for r in requirements
305 if r in self.openerreqs)
306 if r in self.openerreqs)
306 chunkcachesize = self.ui.configint('format', 'chunkcachesize')
307 chunkcachesize = self.ui.configint('format', 'chunkcachesize')
307 if chunkcachesize is not None:
308 if chunkcachesize is not None:
308 self.sopener.options['chunkcachesize'] = chunkcachesize
309 self.sopener.options['chunkcachesize'] = chunkcachesize
309
310
310 def _writerequirements(self):
311 def _writerequirements(self):
311 reqfile = self.opener("requires", "w")
312 reqfile = self.opener("requires", "w")
312 for r in sorted(self.requirements):
313 for r in sorted(self.requirements):
313 reqfile.write("%s\n" % r)
314 reqfile.write("%s\n" % r)
314 reqfile.close()
315 reqfile.close()
315
316
316 def _checknested(self, path):
317 def _checknested(self, path):
317 """Determine if path is a legal nested repository."""
318 """Determine if path is a legal nested repository."""
318 if not path.startswith(self.root):
319 if not path.startswith(self.root):
319 return False
320 return False
320 subpath = path[len(self.root) + 1:]
321 subpath = path[len(self.root) + 1:]
321 normsubpath = util.pconvert(subpath)
322 normsubpath = util.pconvert(subpath)
322
323
323 # XXX: Checking against the current working copy is wrong in
324 # XXX: Checking against the current working copy is wrong in
324 # the sense that it can reject things like
325 # the sense that it can reject things like
325 #
326 #
326 # $ hg cat -r 10 sub/x.txt
327 # $ hg cat -r 10 sub/x.txt
327 #
328 #
328 # if sub/ is no longer a subrepository in the working copy
329 # if sub/ is no longer a subrepository in the working copy
329 # parent revision.
330 # parent revision.
330 #
331 #
331 # However, it can of course also allow things that would have
332 # However, it can of course also allow things that would have
332 # been rejected before, such as the above cat command if sub/
333 # been rejected before, such as the above cat command if sub/
333 # is a subrepository now, but was a normal directory before.
334 # is a subrepository now, but was a normal directory before.
334 # The old path auditor would have rejected by mistake since it
335 # The old path auditor would have rejected by mistake since it
335 # panics when it sees sub/.hg/.
336 # panics when it sees sub/.hg/.
336 #
337 #
337 # All in all, checking against the working copy seems sensible
338 # All in all, checking against the working copy seems sensible
338 # since we want to prevent access to nested repositories on
339 # since we want to prevent access to nested repositories on
339 # the filesystem *now*.
340 # the filesystem *now*.
340 ctx = self[None]
341 ctx = self[None]
341 parts = util.splitpath(subpath)
342 parts = util.splitpath(subpath)
342 while parts:
343 while parts:
343 prefix = '/'.join(parts)
344 prefix = '/'.join(parts)
344 if prefix in ctx.substate:
345 if prefix in ctx.substate:
345 if prefix == normsubpath:
346 if prefix == normsubpath:
346 return True
347 return True
347 else:
348 else:
348 sub = ctx.sub(prefix)
349 sub = ctx.sub(prefix)
349 return sub.checknested(subpath[len(prefix) + 1:])
350 return sub.checknested(subpath[len(prefix) + 1:])
350 else:
351 else:
351 parts.pop()
352 parts.pop()
352 return False
353 return False
353
354
354 def peer(self):
355 def peer(self):
355 return localpeer(self) # not cached to avoid reference cycle
356 return localpeer(self) # not cached to avoid reference cycle
356
357
357 def unfiltered(self):
358 def unfiltered(self):
358 """Return unfiltered version of the repository
359 """Return unfiltered version of the repository
359
360
360 Intended to be overwritten by filtered repo."""
361 Intended to be overwritten by filtered repo."""
361 return self
362 return self
362
363
363 def filtered(self, name):
364 def filtered(self, name):
364 """Return a filtered version of a repository"""
365 """Return a filtered version of a repository"""
365 # build a new class with the mixin and the current class
366 # build a new class with the mixin and the current class
366 # (possibly subclass of the repo)
367 # (possibly subclass of the repo)
367 class proxycls(repoview.repoview, self.unfiltered().__class__):
368 class proxycls(repoview.repoview, self.unfiltered().__class__):
368 pass
369 pass
369 return proxycls(self, name)
370 return proxycls(self, name)
370
371
371 @repofilecache('bookmarks')
372 @repofilecache('bookmarks')
372 def _bookmarks(self):
373 def _bookmarks(self):
373 return bookmarks.bmstore(self)
374 return bookmarks.bmstore(self)
374
375
375 @repofilecache('bookmarks.current')
376 @repofilecache('bookmarks.current')
376 def _bookmarkcurrent(self):
377 def _bookmarkcurrent(self):
377 return bookmarks.readcurrent(self)
378 return bookmarks.readcurrent(self)
378
379
379 def bookmarkheads(self, bookmark):
380 def bookmarkheads(self, bookmark):
380 name = bookmark.split('@', 1)[0]
381 name = bookmark.split('@', 1)[0]
381 heads = []
382 heads = []
382 for mark, n in self._bookmarks.iteritems():
383 for mark, n in self._bookmarks.iteritems():
383 if mark.split('@', 1)[0] == name:
384 if mark.split('@', 1)[0] == name:
384 heads.append(n)
385 heads.append(n)
385 return heads
386 return heads
386
387
387 @storecache('phaseroots')
388 @storecache('phaseroots')
388 def _phasecache(self):
389 def _phasecache(self):
389 return phases.phasecache(self, self._phasedefaults)
390 return phases.phasecache(self, self._phasedefaults)
390
391
391 @storecache('obsstore')
392 @storecache('obsstore')
392 def obsstore(self):
393 def obsstore(self):
393 store = obsolete.obsstore(self.sopener)
394 store = obsolete.obsstore(self.sopener)
394 if store and not obsolete._enabled:
395 if store and not obsolete._enabled:
395 # message is rare enough to not be translated
396 # message is rare enough to not be translated
396 msg = 'obsolete feature not enabled but %i markers found!\n'
397 msg = 'obsolete feature not enabled but %i markers found!\n'
397 self.ui.warn(msg % len(list(store)))
398 self.ui.warn(msg % len(list(store)))
398 return store
399 return store
399
400
400 @storecache('00changelog.i')
401 @storecache('00changelog.i')
401 def changelog(self):
402 def changelog(self):
402 c = changelog.changelog(self.sopener)
403 c = changelog.changelog(self.sopener)
403 if 'HG_PENDING' in os.environ:
404 if 'HG_PENDING' in os.environ:
404 p = os.environ['HG_PENDING']
405 p = os.environ['HG_PENDING']
405 if p.startswith(self.root):
406 if p.startswith(self.root):
406 c.readpending('00changelog.i.a')
407 c.readpending('00changelog.i.a')
407 return c
408 return c
408
409
409 @storecache('00manifest.i')
410 @storecache('00manifest.i')
410 def manifest(self):
411 def manifest(self):
411 return manifest.manifest(self.sopener)
412 return manifest.manifest(self.sopener)
412
413
413 @repofilecache('dirstate')
414 @repofilecache('dirstate')
414 def dirstate(self):
415 def dirstate(self):
415 warned = [0]
416 warned = [0]
416 def validate(node):
417 def validate(node):
417 try:
418 try:
418 self.changelog.rev(node)
419 self.changelog.rev(node)
419 return node
420 return node
420 except error.LookupError:
421 except error.LookupError:
421 if not warned[0]:
422 if not warned[0]:
422 warned[0] = True
423 warned[0] = True
423 self.ui.warn(_("warning: ignoring unknown"
424 self.ui.warn(_("warning: ignoring unknown"
424 " working parent %s!\n") % short(node))
425 " working parent %s!\n") % short(node))
425 return nullid
426 return nullid
426
427
427 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
428 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
428
429
429 def __getitem__(self, changeid):
430 def __getitem__(self, changeid):
430 if changeid is None:
431 if changeid is None:
431 return context.workingctx(self)
432 return context.workingctx(self)
432 return context.changectx(self, changeid)
433 return context.changectx(self, changeid)
433
434
434 def __contains__(self, changeid):
435 def __contains__(self, changeid):
435 try:
436 try:
436 return bool(self.lookup(changeid))
437 return bool(self.lookup(changeid))
437 except error.RepoLookupError:
438 except error.RepoLookupError:
438 return False
439 return False
439
440
440 def __nonzero__(self):
441 def __nonzero__(self):
441 return True
442 return True
442
443
443 def __len__(self):
444 def __len__(self):
444 return len(self.changelog)
445 return len(self.changelog)
445
446
446 def __iter__(self):
447 def __iter__(self):
447 return iter(self.changelog)
448 return iter(self.changelog)
448
449
449 def revs(self, expr, *args):
450 def revs(self, expr, *args):
450 '''Return a list of revisions matching the given revset'''
451 '''Return a list of revisions matching the given revset'''
451 expr = revset.formatspec(expr, *args)
452 expr = revset.formatspec(expr, *args)
452 m = revset.match(None, expr)
453 m = revset.match(None, expr)
453 return m(self, revset.spanset(self))
454 return m(self, revset.spanset(self))
454
455
455 def set(self, expr, *args):
456 def set(self, expr, *args):
456 '''
457 '''
457 Yield a context for each matching revision, after doing arg
458 Yield a context for each matching revision, after doing arg
458 replacement via revset.formatspec
459 replacement via revset.formatspec
459 '''
460 '''
460 for r in self.revs(expr, *args):
461 for r in self.revs(expr, *args):
461 yield self[r]
462 yield self[r]
462
463
463 def url(self):
464 def url(self):
464 return 'file:' + self.root
465 return 'file:' + self.root
465
466
466 def hook(self, name, throw=False, **args):
467 def hook(self, name, throw=False, **args):
467 return hook.hook(self.ui, self, name, throw, **args)
468 return hook.hook(self.ui, self, name, throw, **args)
468
469
469 @unfilteredmethod
470 @unfilteredmethod
470 def _tag(self, names, node, message, local, user, date, extra={}):
471 def _tag(self, names, node, message, local, user, date, extra={}):
471 if isinstance(names, str):
472 if isinstance(names, str):
472 names = (names,)
473 names = (names,)
473
474
474 branches = self.branchmap()
475 branches = self.branchmap()
475 for name in names:
476 for name in names:
476 self.hook('pretag', throw=True, node=hex(node), tag=name,
477 self.hook('pretag', throw=True, node=hex(node), tag=name,
477 local=local)
478 local=local)
478 if name in branches:
479 if name in branches:
479 self.ui.warn(_("warning: tag %s conflicts with existing"
480 self.ui.warn(_("warning: tag %s conflicts with existing"
480 " branch name\n") % name)
481 " branch name\n") % name)
481
482
482 def writetags(fp, names, munge, prevtags):
483 def writetags(fp, names, munge, prevtags):
483 fp.seek(0, 2)
484 fp.seek(0, 2)
484 if prevtags and prevtags[-1] != '\n':
485 if prevtags and prevtags[-1] != '\n':
485 fp.write('\n')
486 fp.write('\n')
486 for name in names:
487 for name in names:
487 m = munge and munge(name) or name
488 m = munge and munge(name) or name
488 if (self._tagscache.tagtypes and
489 if (self._tagscache.tagtypes and
489 name in self._tagscache.tagtypes):
490 name in self._tagscache.tagtypes):
490 old = self.tags().get(name, nullid)
491 old = self.tags().get(name, nullid)
491 fp.write('%s %s\n' % (hex(old), m))
492 fp.write('%s %s\n' % (hex(old), m))
492 fp.write('%s %s\n' % (hex(node), m))
493 fp.write('%s %s\n' % (hex(node), m))
493 fp.close()
494 fp.close()
494
495
495 prevtags = ''
496 prevtags = ''
496 if local:
497 if local:
497 try:
498 try:
498 fp = self.opener('localtags', 'r+')
499 fp = self.opener('localtags', 'r+')
499 except IOError:
500 except IOError:
500 fp = self.opener('localtags', 'a')
501 fp = self.opener('localtags', 'a')
501 else:
502 else:
502 prevtags = fp.read()
503 prevtags = fp.read()
503
504
504 # local tags are stored in the current charset
505 # local tags are stored in the current charset
505 writetags(fp, names, None, prevtags)
506 writetags(fp, names, None, prevtags)
506 for name in names:
507 for name in names:
507 self.hook('tag', node=hex(node), tag=name, local=local)
508 self.hook('tag', node=hex(node), tag=name, local=local)
508 return
509 return
509
510
510 try:
511 try:
511 fp = self.wfile('.hgtags', 'rb+')
512 fp = self.wfile('.hgtags', 'rb+')
512 except IOError, e:
513 except IOError, e:
513 if e.errno != errno.ENOENT:
514 if e.errno != errno.ENOENT:
514 raise
515 raise
515 fp = self.wfile('.hgtags', 'ab')
516 fp = self.wfile('.hgtags', 'ab')
516 else:
517 else:
517 prevtags = fp.read()
518 prevtags = fp.read()
518
519
519 # committed tags are stored in UTF-8
520 # committed tags are stored in UTF-8
520 writetags(fp, names, encoding.fromlocal, prevtags)
521 writetags(fp, names, encoding.fromlocal, prevtags)
521
522
522 fp.close()
523 fp.close()
523
524
524 self.invalidatecaches()
525 self.invalidatecaches()
525
526
526 if '.hgtags' not in self.dirstate:
527 if '.hgtags' not in self.dirstate:
527 self[None].add(['.hgtags'])
528 self[None].add(['.hgtags'])
528
529
529 m = matchmod.exact(self.root, '', ['.hgtags'])
530 m = matchmod.exact(self.root, '', ['.hgtags'])
530 tagnode = self.commit(message, user, date, extra=extra, match=m)
531 tagnode = self.commit(message, user, date, extra=extra, match=m)
531
532
532 for name in names:
533 for name in names:
533 self.hook('tag', node=hex(node), tag=name, local=local)
534 self.hook('tag', node=hex(node), tag=name, local=local)
534
535
535 return tagnode
536 return tagnode
536
537
537 def tag(self, names, node, message, local, user, date):
538 def tag(self, names, node, message, local, user, date):
538 '''tag a revision with one or more symbolic names.
539 '''tag a revision with one or more symbolic names.
539
540
540 names is a list of strings or, when adding a single tag, names may be a
541 names is a list of strings or, when adding a single tag, names may be a
541 string.
542 string.
542
543
543 if local is True, the tags are stored in a per-repository file.
544 if local is True, the tags are stored in a per-repository file.
544 otherwise, they are stored in the .hgtags file, and a new
545 otherwise, they are stored in the .hgtags file, and a new
545 changeset is committed with the change.
546 changeset is committed with the change.
546
547
547 keyword arguments:
548 keyword arguments:
548
549
549 local: whether to store tags in non-version-controlled file
550 local: whether to store tags in non-version-controlled file
550 (default False)
551 (default False)
551
552
552 message: commit message to use if committing
553 message: commit message to use if committing
553
554
554 user: name of user to use if committing
555 user: name of user to use if committing
555
556
556 date: date tuple to use if committing'''
557 date: date tuple to use if committing'''
557
558
558 if not local:
559 if not local:
559 for x in self.status()[:5]:
560 for x in self.status()[:5]:
560 if '.hgtags' in x:
561 if '.hgtags' in x:
561 raise util.Abort(_('working copy of .hgtags is changed '
562 raise util.Abort(_('working copy of .hgtags is changed '
562 '(please commit .hgtags manually)'))
563 '(please commit .hgtags manually)'))
563
564
564 self.tags() # instantiate the cache
565 self.tags() # instantiate the cache
565 self._tag(names, node, message, local, user, date)
566 self._tag(names, node, message, local, user, date)
566
567
567 @filteredpropertycache
568 @filteredpropertycache
568 def _tagscache(self):
569 def _tagscache(self):
569 '''Returns a tagscache object that contains various tags related
570 '''Returns a tagscache object that contains various tags related
570 caches.'''
571 caches.'''
571
572
572 # This simplifies its cache management by having one decorated
573 # This simplifies its cache management by having one decorated
573 # function (this one) and the rest simply fetch things from it.
574 # function (this one) and the rest simply fetch things from it.
574 class tagscache(object):
575 class tagscache(object):
575 def __init__(self):
576 def __init__(self):
576 # These two define the set of tags for this repository. tags
577 # These two define the set of tags for this repository. tags
577 # maps tag name to node; tagtypes maps tag name to 'global' or
578 # maps tag name to node; tagtypes maps tag name to 'global' or
578 # 'local'. (Global tags are defined by .hgtags across all
579 # 'local'. (Global tags are defined by .hgtags across all
579 # heads, and local tags are defined in .hg/localtags.)
580 # heads, and local tags are defined in .hg/localtags.)
580 # They constitute the in-memory cache of tags.
581 # They constitute the in-memory cache of tags.
581 self.tags = self.tagtypes = None
582 self.tags = self.tagtypes = None
582
583
583 self.nodetagscache = self.tagslist = None
584 self.nodetagscache = self.tagslist = None
584
585
585 cache = tagscache()
586 cache = tagscache()
586 cache.tags, cache.tagtypes = self._findtags()
587 cache.tags, cache.tagtypes = self._findtags()
587
588
588 return cache
589 return cache
589
590
590 def tags(self):
591 def tags(self):
591 '''return a mapping of tag to node'''
592 '''return a mapping of tag to node'''
592 t = {}
593 t = {}
593 if self.changelog.filteredrevs:
594 if self.changelog.filteredrevs:
594 tags, tt = self._findtags()
595 tags, tt = self._findtags()
595 else:
596 else:
596 tags = self._tagscache.tags
597 tags = self._tagscache.tags
597 for k, v in tags.iteritems():
598 for k, v in tags.iteritems():
598 try:
599 try:
599 # ignore tags to unknown nodes
600 # ignore tags to unknown nodes
600 self.changelog.rev(v)
601 self.changelog.rev(v)
601 t[k] = v
602 t[k] = v
602 except (error.LookupError, ValueError):
603 except (error.LookupError, ValueError):
603 pass
604 pass
604 return t
605 return t
605
606
606 def _findtags(self):
607 def _findtags(self):
607 '''Do the hard work of finding tags. Return a pair of dicts
608 '''Do the hard work of finding tags. Return a pair of dicts
608 (tags, tagtypes) where tags maps tag name to node, and tagtypes
609 (tags, tagtypes) where tags maps tag name to node, and tagtypes
609 maps tag name to a string like \'global\' or \'local\'.
610 maps tag name to a string like \'global\' or \'local\'.
610 Subclasses or extensions are free to add their own tags, but
611 Subclasses or extensions are free to add their own tags, but
611 should be aware that the returned dicts will be retained for the
612 should be aware that the returned dicts will be retained for the
612 duration of the localrepo object.'''
613 duration of the localrepo object.'''
613
614
614 # XXX what tagtype should subclasses/extensions use? Currently
615 # XXX what tagtype should subclasses/extensions use? Currently
615 # mq and bookmarks add tags, but do not set the tagtype at all.
616 # mq and bookmarks add tags, but do not set the tagtype at all.
616 # Should each extension invent its own tag type? Should there
617 # Should each extension invent its own tag type? Should there
617 # be one tagtype for all such "virtual" tags? Or is the status
618 # be one tagtype for all such "virtual" tags? Or is the status
618 # quo fine?
619 # quo fine?
619
620
620 alltags = {} # map tag name to (node, hist)
621 alltags = {} # map tag name to (node, hist)
621 tagtypes = {}
622 tagtypes = {}
622
623
623 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
624 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
624 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
625 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
625
626
626 # Build the return dicts. Have to re-encode tag names because
627 # Build the return dicts. Have to re-encode tag names because
627 # the tags module always uses UTF-8 (in order not to lose info
628 # the tags module always uses UTF-8 (in order not to lose info
628 # writing to the cache), but the rest of Mercurial wants them in
629 # writing to the cache), but the rest of Mercurial wants them in
629 # local encoding.
630 # local encoding.
630 tags = {}
631 tags = {}
631 for (name, (node, hist)) in alltags.iteritems():
632 for (name, (node, hist)) in alltags.iteritems():
632 if node != nullid:
633 if node != nullid:
633 tags[encoding.tolocal(name)] = node
634 tags[encoding.tolocal(name)] = node
634 tags['tip'] = self.changelog.tip()
635 tags['tip'] = self.changelog.tip()
635 tagtypes = dict([(encoding.tolocal(name), value)
636 tagtypes = dict([(encoding.tolocal(name), value)
636 for (name, value) in tagtypes.iteritems()])
637 for (name, value) in tagtypes.iteritems()])
637 return (tags, tagtypes)
638 return (tags, tagtypes)
638
639
639 def tagtype(self, tagname):
640 def tagtype(self, tagname):
640 '''
641 '''
641 return the type of the given tag. result can be:
642 return the type of the given tag. result can be:
642
643
643 'local' : a local tag
644 'local' : a local tag
644 'global' : a global tag
645 'global' : a global tag
645 None : tag does not exist
646 None : tag does not exist
646 '''
647 '''
647
648
648 return self._tagscache.tagtypes.get(tagname)
649 return self._tagscache.tagtypes.get(tagname)
649
650
650 def tagslist(self):
651 def tagslist(self):
651 '''return a list of tags ordered by revision'''
652 '''return a list of tags ordered by revision'''
652 if not self._tagscache.tagslist:
653 if not self._tagscache.tagslist:
653 l = []
654 l = []
654 for t, n in self.tags().iteritems():
655 for t, n in self.tags().iteritems():
655 r = self.changelog.rev(n)
656 r = self.changelog.rev(n)
656 l.append((r, t, n))
657 l.append((r, t, n))
657 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
658 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
658
659
659 return self._tagscache.tagslist
660 return self._tagscache.tagslist
660
661
661 def nodetags(self, node):
662 def nodetags(self, node):
662 '''return the tags associated with a node'''
663 '''return the tags associated with a node'''
663 if not self._tagscache.nodetagscache:
664 if not self._tagscache.nodetagscache:
664 nodetagscache = {}
665 nodetagscache = {}
665 for t, n in self._tagscache.tags.iteritems():
666 for t, n in self._tagscache.tags.iteritems():
666 nodetagscache.setdefault(n, []).append(t)
667 nodetagscache.setdefault(n, []).append(t)
667 for tags in nodetagscache.itervalues():
668 for tags in nodetagscache.itervalues():
668 tags.sort()
669 tags.sort()
669 self._tagscache.nodetagscache = nodetagscache
670 self._tagscache.nodetagscache = nodetagscache
670 return self._tagscache.nodetagscache.get(node, [])
671 return self._tagscache.nodetagscache.get(node, [])
671
672
672 def nodebookmarks(self, node):
673 def nodebookmarks(self, node):
673 marks = []
674 marks = []
674 for bookmark, n in self._bookmarks.iteritems():
675 for bookmark, n in self._bookmarks.iteritems():
675 if n == node:
676 if n == node:
676 marks.append(bookmark)
677 marks.append(bookmark)
677 return sorted(marks)
678 return sorted(marks)
678
679
679 def branchmap(self):
680 def branchmap(self):
680 '''returns a dictionary {branch: [branchheads]} with branchheads
681 '''returns a dictionary {branch: [branchheads]} with branchheads
681 ordered by increasing revision number'''
682 ordered by increasing revision number'''
682 branchmap.updatecache(self)
683 branchmap.updatecache(self)
683 return self._branchcaches[self.filtername]
684 return self._branchcaches[self.filtername]
684
685
685 def branchtip(self, branch):
686 def branchtip(self, branch):
686 '''return the tip node for a given branch'''
687 '''return the tip node for a given branch'''
687 try:
688 try:
688 return self.branchmap().branchtip(branch)
689 return self.branchmap().branchtip(branch)
689 except KeyError:
690 except KeyError:
690 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
691 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
691
692
692 def lookup(self, key):
693 def lookup(self, key):
693 return self[key].node()
694 return self[key].node()
694
695
695 def lookupbranch(self, key, remote=None):
696 def lookupbranch(self, key, remote=None):
696 repo = remote or self
697 repo = remote or self
697 if key in repo.branchmap():
698 if key in repo.branchmap():
698 return key
699 return key
699
700
700 repo = (remote and remote.local()) and remote or self
701 repo = (remote and remote.local()) and remote or self
701 return repo[key].branch()
702 return repo[key].branch()
702
703
703 def known(self, nodes):
704 def known(self, nodes):
704 nm = self.changelog.nodemap
705 nm = self.changelog.nodemap
705 pc = self._phasecache
706 pc = self._phasecache
706 result = []
707 result = []
707 for n in nodes:
708 for n in nodes:
708 r = nm.get(n)
709 r = nm.get(n)
709 resp = not (r is None or pc.phase(self, r) >= phases.secret)
710 resp = not (r is None or pc.phase(self, r) >= phases.secret)
710 result.append(resp)
711 result.append(resp)
711 return result
712 return result
712
713
713 def local(self):
714 def local(self):
714 return self
715 return self
715
716
716 def cancopy(self):
717 def cancopy(self):
717 # so statichttprepo's override of local() works
718 # so statichttprepo's override of local() works
718 if not self.local():
719 if not self.local():
719 return False
720 return False
720 if not self.ui.configbool('phases', 'publish', True):
721 if not self.ui.configbool('phases', 'publish', True):
721 return True
722 return True
722 # if publishing we can't copy if there is filtered content
723 # if publishing we can't copy if there is filtered content
723 return not self.filtered('visible').changelog.filteredrevs
724 return not self.filtered('visible').changelog.filteredrevs
724
725
725 def join(self, f):
726 def join(self, f):
726 return os.path.join(self.path, f)
727 return os.path.join(self.path, f)
727
728
728 def wjoin(self, f):
729 def wjoin(self, f):
729 return os.path.join(self.root, f)
730 return os.path.join(self.root, f)
730
731
731 def file(self, f):
732 def file(self, f):
732 if f[0] == '/':
733 if f[0] == '/':
733 f = f[1:]
734 f = f[1:]
734 return filelog.filelog(self.sopener, f)
735 return filelog.filelog(self.sopener, f)
735
736
736 def changectx(self, changeid):
737 def changectx(self, changeid):
737 return self[changeid]
738 return self[changeid]
738
739
739 def parents(self, changeid=None):
740 def parents(self, changeid=None):
740 '''get list of changectxs for parents of changeid'''
741 '''get list of changectxs for parents of changeid'''
741 return self[changeid].parents()
742 return self[changeid].parents()
742
743
743 def setparents(self, p1, p2=nullid):
744 def setparents(self, p1, p2=nullid):
744 copies = self.dirstate.setparents(p1, p2)
745 copies = self.dirstate.setparents(p1, p2)
745 pctx = self[p1]
746 pctx = self[p1]
746 if copies:
747 if copies:
747 # Adjust copy records, the dirstate cannot do it, it
748 # Adjust copy records, the dirstate cannot do it, it
748 # requires access to parents manifests. Preserve them
749 # requires access to parents manifests. Preserve them
749 # only for entries added to first parent.
750 # only for entries added to first parent.
750 for f in copies:
751 for f in copies:
751 if f not in pctx and copies[f] in pctx:
752 if f not in pctx and copies[f] in pctx:
752 self.dirstate.copy(copies[f], f)
753 self.dirstate.copy(copies[f], f)
753 if p2 == nullid:
754 if p2 == nullid:
754 for f, s in sorted(self.dirstate.copies().items()):
755 for f, s in sorted(self.dirstate.copies().items()):
755 if f not in pctx and s not in pctx:
756 if f not in pctx and s not in pctx:
756 self.dirstate.copy(None, f)
757 self.dirstate.copy(None, f)
757
758
758 def filectx(self, path, changeid=None, fileid=None):
759 def filectx(self, path, changeid=None, fileid=None):
759 """changeid can be a changeset revision, node, or tag.
760 """changeid can be a changeset revision, node, or tag.
760 fileid can be a file revision or node."""
761 fileid can be a file revision or node."""
761 return context.filectx(self, path, changeid, fileid)
762 return context.filectx(self, path, changeid, fileid)
762
763
763 def getcwd(self):
764 def getcwd(self):
764 return self.dirstate.getcwd()
765 return self.dirstate.getcwd()
765
766
766 def pathto(self, f, cwd=None):
767 def pathto(self, f, cwd=None):
767 return self.dirstate.pathto(f, cwd)
768 return self.dirstate.pathto(f, cwd)
768
769
769 def wfile(self, f, mode='r'):
770 def wfile(self, f, mode='r'):
770 return self.wopener(f, mode)
771 return self.wopener(f, mode)
771
772
772 def _link(self, f):
773 def _link(self, f):
773 return self.wvfs.islink(f)
774 return self.wvfs.islink(f)
774
775
775 def _loadfilter(self, filter):
776 def _loadfilter(self, filter):
776 if filter not in self.filterpats:
777 if filter not in self.filterpats:
777 l = []
778 l = []
778 for pat, cmd in self.ui.configitems(filter):
779 for pat, cmd in self.ui.configitems(filter):
779 if cmd == '!':
780 if cmd == '!':
780 continue
781 continue
781 mf = matchmod.match(self.root, '', [pat])
782 mf = matchmod.match(self.root, '', [pat])
782 fn = None
783 fn = None
783 params = cmd
784 params = cmd
784 for name, filterfn in self._datafilters.iteritems():
785 for name, filterfn in self._datafilters.iteritems():
785 if cmd.startswith(name):
786 if cmd.startswith(name):
786 fn = filterfn
787 fn = filterfn
787 params = cmd[len(name):].lstrip()
788 params = cmd[len(name):].lstrip()
788 break
789 break
789 if not fn:
790 if not fn:
790 fn = lambda s, c, **kwargs: util.filter(s, c)
791 fn = lambda s, c, **kwargs: util.filter(s, c)
791 # Wrap old filters not supporting keyword arguments
792 # Wrap old filters not supporting keyword arguments
792 if not inspect.getargspec(fn)[2]:
793 if not inspect.getargspec(fn)[2]:
793 oldfn = fn
794 oldfn = fn
794 fn = lambda s, c, **kwargs: oldfn(s, c)
795 fn = lambda s, c, **kwargs: oldfn(s, c)
795 l.append((mf, fn, params))
796 l.append((mf, fn, params))
796 self.filterpats[filter] = l
797 self.filterpats[filter] = l
797 return self.filterpats[filter]
798 return self.filterpats[filter]
798
799
799 def _filter(self, filterpats, filename, data):
800 def _filter(self, filterpats, filename, data):
800 for mf, fn, cmd in filterpats:
801 for mf, fn, cmd in filterpats:
801 if mf(filename):
802 if mf(filename):
802 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
803 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
803 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
804 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
804 break
805 break
805
806
806 return data
807 return data
807
808
808 @unfilteredpropertycache
809 @unfilteredpropertycache
809 def _encodefilterpats(self):
810 def _encodefilterpats(self):
810 return self._loadfilter('encode')
811 return self._loadfilter('encode')
811
812
812 @unfilteredpropertycache
813 @unfilteredpropertycache
813 def _decodefilterpats(self):
814 def _decodefilterpats(self):
814 return self._loadfilter('decode')
815 return self._loadfilter('decode')
815
816
816 def adddatafilter(self, name, filter):
817 def adddatafilter(self, name, filter):
817 self._datafilters[name] = filter
818 self._datafilters[name] = filter
818
819
819 def wread(self, filename):
820 def wread(self, filename):
820 if self._link(filename):
821 if self._link(filename):
821 data = self.wvfs.readlink(filename)
822 data = self.wvfs.readlink(filename)
822 else:
823 else:
823 data = self.wopener.read(filename)
824 data = self.wopener.read(filename)
824 return self._filter(self._encodefilterpats, filename, data)
825 return self._filter(self._encodefilterpats, filename, data)
825
826
826 def wwrite(self, filename, data, flags):
827 def wwrite(self, filename, data, flags):
827 data = self._filter(self._decodefilterpats, filename, data)
828 data = self._filter(self._decodefilterpats, filename, data)
828 if 'l' in flags:
829 if 'l' in flags:
829 self.wopener.symlink(data, filename)
830 self.wopener.symlink(data, filename)
830 else:
831 else:
831 self.wopener.write(filename, data)
832 self.wopener.write(filename, data)
832 if 'x' in flags:
833 if 'x' in flags:
833 self.wvfs.setflags(filename, False, True)
834 self.wvfs.setflags(filename, False, True)
834
835
835 def wwritedata(self, filename, data):
836 def wwritedata(self, filename, data):
836 return self._filter(self._decodefilterpats, filename, data)
837 return self._filter(self._decodefilterpats, filename, data)
837
838
838 def transaction(self, desc, report=None):
839 def transaction(self, desc, report=None):
839 tr = self._transref and self._transref() or None
840 tr = self._transref and self._transref() or None
840 if tr and tr.running():
841 if tr and tr.running():
841 return tr.nest()
842 return tr.nest()
842
843
843 # abort here if the journal already exists
844 # abort here if the journal already exists
844 if self.svfs.exists("journal"):
845 if self.svfs.exists("journal"):
845 raise error.RepoError(
846 raise error.RepoError(
846 _("abandoned transaction found - run hg recover"))
847 _("abandoned transaction found - run hg recover"))
847
848
848 def onclose():
849 def onclose():
849 self.store.write(tr)
850 self.store.write(tr)
850
851
851 self._writejournal(desc)
852 self._writejournal(desc)
852 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
853 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
853 rp = report and report or self.ui.warn
854 rp = report and report or self.ui.warn
854 tr = transaction.transaction(rp, self.sopener,
855 tr = transaction.transaction(rp, self.sopener,
855 "journal",
856 "journal",
856 aftertrans(renames),
857 aftertrans(renames),
857 self.store.createmode,
858 self.store.createmode,
858 onclose)
859 onclose)
859 self._transref = weakref.ref(tr)
860 self._transref = weakref.ref(tr)
860 return tr
861 return tr
861
862
862 def _journalfiles(self):
863 def _journalfiles(self):
863 return ((self.svfs, 'journal'),
864 return ((self.svfs, 'journal'),
864 (self.vfs, 'journal.dirstate'),
865 (self.vfs, 'journal.dirstate'),
865 (self.vfs, 'journal.branch'),
866 (self.vfs, 'journal.branch'),
866 (self.vfs, 'journal.desc'),
867 (self.vfs, 'journal.desc'),
867 (self.vfs, 'journal.bookmarks'),
868 (self.vfs, 'journal.bookmarks'),
868 (self.svfs, 'journal.phaseroots'))
869 (self.svfs, 'journal.phaseroots'))
869
870
870 def undofiles(self):
871 def undofiles(self):
871 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
872 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
872
873
873 def _writejournal(self, desc):
874 def _writejournal(self, desc):
874 self.opener.write("journal.dirstate",
875 self.opener.write("journal.dirstate",
875 self.opener.tryread("dirstate"))
876 self.opener.tryread("dirstate"))
876 self.opener.write("journal.branch",
877 self.opener.write("journal.branch",
877 encoding.fromlocal(self.dirstate.branch()))
878 encoding.fromlocal(self.dirstate.branch()))
878 self.opener.write("journal.desc",
879 self.opener.write("journal.desc",
879 "%d\n%s\n" % (len(self), desc))
880 "%d\n%s\n" % (len(self), desc))
880 self.opener.write("journal.bookmarks",
881 self.opener.write("journal.bookmarks",
881 self.opener.tryread("bookmarks"))
882 self.opener.tryread("bookmarks"))
882 self.sopener.write("journal.phaseroots",
883 self.sopener.write("journal.phaseroots",
883 self.sopener.tryread("phaseroots"))
884 self.sopener.tryread("phaseroots"))
884
885
885 def recover(self):
886 def recover(self):
886 lock = self.lock()
887 lock = self.lock()
887 try:
888 try:
888 if self.svfs.exists("journal"):
889 if self.svfs.exists("journal"):
889 self.ui.status(_("rolling back interrupted transaction\n"))
890 self.ui.status(_("rolling back interrupted transaction\n"))
890 transaction.rollback(self.sopener, "journal",
891 transaction.rollback(self.sopener, "journal",
891 self.ui.warn)
892 self.ui.warn)
892 self.invalidate()
893 self.invalidate()
893 return True
894 return True
894 else:
895 else:
895 self.ui.warn(_("no interrupted transaction available\n"))
896 self.ui.warn(_("no interrupted transaction available\n"))
896 return False
897 return False
897 finally:
898 finally:
898 lock.release()
899 lock.release()
899
900
900 def rollback(self, dryrun=False, force=False):
901 def rollback(self, dryrun=False, force=False):
901 wlock = lock = None
902 wlock = lock = None
902 try:
903 try:
903 wlock = self.wlock()
904 wlock = self.wlock()
904 lock = self.lock()
905 lock = self.lock()
905 if self.svfs.exists("undo"):
906 if self.svfs.exists("undo"):
906 return self._rollback(dryrun, force)
907 return self._rollback(dryrun, force)
907 else:
908 else:
908 self.ui.warn(_("no rollback information available\n"))
909 self.ui.warn(_("no rollback information available\n"))
909 return 1
910 return 1
910 finally:
911 finally:
911 release(lock, wlock)
912 release(lock, wlock)
912
913
913 @unfilteredmethod # Until we get smarter cache management
914 @unfilteredmethod # Until we get smarter cache management
914 def _rollback(self, dryrun, force):
915 def _rollback(self, dryrun, force):
915 ui = self.ui
916 ui = self.ui
916 try:
917 try:
917 args = self.opener.read('undo.desc').splitlines()
918 args = self.opener.read('undo.desc').splitlines()
918 (oldlen, desc, detail) = (int(args[0]), args[1], None)
919 (oldlen, desc, detail) = (int(args[0]), args[1], None)
919 if len(args) >= 3:
920 if len(args) >= 3:
920 detail = args[2]
921 detail = args[2]
921 oldtip = oldlen - 1
922 oldtip = oldlen - 1
922
923
923 if detail and ui.verbose:
924 if detail and ui.verbose:
924 msg = (_('repository tip rolled back to revision %s'
925 msg = (_('repository tip rolled back to revision %s'
925 ' (undo %s: %s)\n')
926 ' (undo %s: %s)\n')
926 % (oldtip, desc, detail))
927 % (oldtip, desc, detail))
927 else:
928 else:
928 msg = (_('repository tip rolled back to revision %s'
929 msg = (_('repository tip rolled back to revision %s'
929 ' (undo %s)\n')
930 ' (undo %s)\n')
930 % (oldtip, desc))
931 % (oldtip, desc))
931 except IOError:
932 except IOError:
932 msg = _('rolling back unknown transaction\n')
933 msg = _('rolling back unknown transaction\n')
933 desc = None
934 desc = None
934
935
935 if not force and self['.'] != self['tip'] and desc == 'commit':
936 if not force and self['.'] != self['tip'] and desc == 'commit':
936 raise util.Abort(
937 raise util.Abort(
937 _('rollback of last commit while not checked out '
938 _('rollback of last commit while not checked out '
938 'may lose data'), hint=_('use -f to force'))
939 'may lose data'), hint=_('use -f to force'))
939
940
940 ui.status(msg)
941 ui.status(msg)
941 if dryrun:
942 if dryrun:
942 return 0
943 return 0
943
944
944 parents = self.dirstate.parents()
945 parents = self.dirstate.parents()
945 self.destroying()
946 self.destroying()
946 transaction.rollback(self.sopener, 'undo', ui.warn)
947 transaction.rollback(self.sopener, 'undo', ui.warn)
947 if self.vfs.exists('undo.bookmarks'):
948 if self.vfs.exists('undo.bookmarks'):
948 self.vfs.rename('undo.bookmarks', 'bookmarks')
949 self.vfs.rename('undo.bookmarks', 'bookmarks')
949 if self.svfs.exists('undo.phaseroots'):
950 if self.svfs.exists('undo.phaseroots'):
950 self.svfs.rename('undo.phaseroots', 'phaseroots')
951 self.svfs.rename('undo.phaseroots', 'phaseroots')
951 self.invalidate()
952 self.invalidate()
952
953
953 parentgone = (parents[0] not in self.changelog.nodemap or
954 parentgone = (parents[0] not in self.changelog.nodemap or
954 parents[1] not in self.changelog.nodemap)
955 parents[1] not in self.changelog.nodemap)
955 if parentgone:
956 if parentgone:
956 self.vfs.rename('undo.dirstate', 'dirstate')
957 self.vfs.rename('undo.dirstate', 'dirstate')
957 try:
958 try:
958 branch = self.opener.read('undo.branch')
959 branch = self.opener.read('undo.branch')
959 self.dirstate.setbranch(encoding.tolocal(branch))
960 self.dirstate.setbranch(encoding.tolocal(branch))
960 except IOError:
961 except IOError:
961 ui.warn(_('named branch could not be reset: '
962 ui.warn(_('named branch could not be reset: '
962 'current branch is still \'%s\'\n')
963 'current branch is still \'%s\'\n')
963 % self.dirstate.branch())
964 % self.dirstate.branch())
964
965
965 self.dirstate.invalidate()
966 self.dirstate.invalidate()
966 parents = tuple([p.rev() for p in self.parents()])
967 parents = tuple([p.rev() for p in self.parents()])
967 if len(parents) > 1:
968 if len(parents) > 1:
968 ui.status(_('working directory now based on '
969 ui.status(_('working directory now based on '
969 'revisions %d and %d\n') % parents)
970 'revisions %d and %d\n') % parents)
970 else:
971 else:
971 ui.status(_('working directory now based on '
972 ui.status(_('working directory now based on '
972 'revision %d\n') % parents)
973 'revision %d\n') % parents)
973 # TODO: if we know which new heads may result from this rollback, pass
974 # TODO: if we know which new heads may result from this rollback, pass
974 # them to destroy(), which will prevent the branchhead cache from being
975 # them to destroy(), which will prevent the branchhead cache from being
975 # invalidated.
976 # invalidated.
976 self.destroyed()
977 self.destroyed()
977 return 0
978 return 0
978
979
979 def invalidatecaches(self):
980 def invalidatecaches(self):
980
981
981 if '_tagscache' in vars(self):
982 if '_tagscache' in vars(self):
982 # can't use delattr on proxy
983 # can't use delattr on proxy
983 del self.__dict__['_tagscache']
984 del self.__dict__['_tagscache']
984
985
985 self.unfiltered()._branchcaches.clear()
986 self.unfiltered()._branchcaches.clear()
986 self.invalidatevolatilesets()
987 self.invalidatevolatilesets()
987
988
988 def invalidatevolatilesets(self):
989 def invalidatevolatilesets(self):
989 self.filteredrevcache.clear()
990 self.filteredrevcache.clear()
990 obsolete.clearobscaches(self)
991 obsolete.clearobscaches(self)
991
992
992 def invalidatedirstate(self):
993 def invalidatedirstate(self):
993 '''Invalidates the dirstate, causing the next call to dirstate
994 '''Invalidates the dirstate, causing the next call to dirstate
994 to check if it was modified since the last time it was read,
995 to check if it was modified since the last time it was read,
995 rereading it if it has.
996 rereading it if it has.
996
997
997 This is different to dirstate.invalidate() that it doesn't always
998 This is different to dirstate.invalidate() that it doesn't always
998 rereads the dirstate. Use dirstate.invalidate() if you want to
999 rereads the dirstate. Use dirstate.invalidate() if you want to
999 explicitly read the dirstate again (i.e. restoring it to a previous
1000 explicitly read the dirstate again (i.e. restoring it to a previous
1000 known good state).'''
1001 known good state).'''
1001 if hasunfilteredcache(self, 'dirstate'):
1002 if hasunfilteredcache(self, 'dirstate'):
1002 for k in self.dirstate._filecache:
1003 for k in self.dirstate._filecache:
1003 try:
1004 try:
1004 delattr(self.dirstate, k)
1005 delattr(self.dirstate, k)
1005 except AttributeError:
1006 except AttributeError:
1006 pass
1007 pass
1007 delattr(self.unfiltered(), 'dirstate')
1008 delattr(self.unfiltered(), 'dirstate')
1008
1009
1009 def invalidate(self):
1010 def invalidate(self):
1010 unfiltered = self.unfiltered() # all file caches are stored unfiltered
1011 unfiltered = self.unfiltered() # all file caches are stored unfiltered
1011 for k in self._filecache:
1012 for k in self._filecache:
1012 # dirstate is invalidated separately in invalidatedirstate()
1013 # dirstate is invalidated separately in invalidatedirstate()
1013 if k == 'dirstate':
1014 if k == 'dirstate':
1014 continue
1015 continue
1015
1016
1016 try:
1017 try:
1017 delattr(unfiltered, k)
1018 delattr(unfiltered, k)
1018 except AttributeError:
1019 except AttributeError:
1019 pass
1020 pass
1020 self.invalidatecaches()
1021 self.invalidatecaches()
1021 self.store.invalidatecaches()
1022 self.store.invalidatecaches()
1022
1023
1023 def invalidateall(self):
1024 def invalidateall(self):
1024 '''Fully invalidates both store and non-store parts, causing the
1025 '''Fully invalidates both store and non-store parts, causing the
1025 subsequent operation to reread any outside changes.'''
1026 subsequent operation to reread any outside changes.'''
1026 # extension should hook this to invalidate its caches
1027 # extension should hook this to invalidate its caches
1027 self.invalidate()
1028 self.invalidate()
1028 self.invalidatedirstate()
1029 self.invalidatedirstate()
1029
1030
1030 def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc):
1031 def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc):
1031 try:
1032 try:
1032 l = lockmod.lock(vfs, lockname, 0, releasefn, desc=desc)
1033 l = lockmod.lock(vfs, lockname, 0, releasefn, desc=desc)
1033 except error.LockHeld, inst:
1034 except error.LockHeld, inst:
1034 if not wait:
1035 if not wait:
1035 raise
1036 raise
1036 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1037 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1037 (desc, inst.locker))
1038 (desc, inst.locker))
1038 # default to 600 seconds timeout
1039 # default to 600 seconds timeout
1039 l = lockmod.lock(vfs, lockname,
1040 l = lockmod.lock(vfs, lockname,
1040 int(self.ui.config("ui", "timeout", "600")),
1041 int(self.ui.config("ui", "timeout", "600")),
1041 releasefn, desc=desc)
1042 releasefn, desc=desc)
1042 self.ui.warn(_("got lock after %s seconds\n") % l.delay)
1043 self.ui.warn(_("got lock after %s seconds\n") % l.delay)
1043 if acquirefn:
1044 if acquirefn:
1044 acquirefn()
1045 acquirefn()
1045 return l
1046 return l
1046
1047
1047 def _afterlock(self, callback):
1048 def _afterlock(self, callback):
1048 """add a callback to the current repository lock.
1049 """add a callback to the current repository lock.
1049
1050
1050 The callback will be executed on lock release."""
1051 The callback will be executed on lock release."""
1051 l = self._lockref and self._lockref()
1052 l = self._lockref and self._lockref()
1052 if l:
1053 if l:
1053 l.postrelease.append(callback)
1054 l.postrelease.append(callback)
1054 else:
1055 else:
1055 callback()
1056 callback()
1056
1057
1057 def lock(self, wait=True):
1058 def lock(self, wait=True):
1058 '''Lock the repository store (.hg/store) and return a weak reference
1059 '''Lock the repository store (.hg/store) and return a weak reference
1059 to the lock. Use this before modifying the store (e.g. committing or
1060 to the lock. Use this before modifying the store (e.g. committing or
1060 stripping). If you are opening a transaction, get a lock as well.)'''
1061 stripping). If you are opening a transaction, get a lock as well.)'''
1061 l = self._lockref and self._lockref()
1062 l = self._lockref and self._lockref()
1062 if l is not None and l.held:
1063 if l is not None and l.held:
1063 l.lock()
1064 l.lock()
1064 return l
1065 return l
1065
1066
1066 def unlock():
1067 def unlock():
1067 if hasunfilteredcache(self, '_phasecache'):
1068 if hasunfilteredcache(self, '_phasecache'):
1068 self._phasecache.write()
1069 self._phasecache.write()
1069 for k, ce in self._filecache.items():
1070 for k, ce in self._filecache.items():
1070 if k == 'dirstate' or k not in self.__dict__:
1071 if k == 'dirstate' or k not in self.__dict__:
1071 continue
1072 continue
1072 ce.refresh()
1073 ce.refresh()
1073
1074
1074 l = self._lock(self.svfs, "lock", wait, unlock,
1075 l = self._lock(self.svfs, "lock", wait, unlock,
1075 self.invalidate, _('repository %s') % self.origroot)
1076 self.invalidate, _('repository %s') % self.origroot)
1076 self._lockref = weakref.ref(l)
1077 self._lockref = weakref.ref(l)
1077 return l
1078 return l
1078
1079
1079 def wlock(self, wait=True):
1080 def wlock(self, wait=True):
1080 '''Lock the non-store parts of the repository (everything under
1081 '''Lock the non-store parts of the repository (everything under
1081 .hg except .hg/store) and return a weak reference to the lock.
1082 .hg except .hg/store) and return a weak reference to the lock.
1082 Use this before modifying files in .hg.'''
1083 Use this before modifying files in .hg.'''
1083 l = self._wlockref and self._wlockref()
1084 l = self._wlockref and self._wlockref()
1084 if l is not None and l.held:
1085 if l is not None and l.held:
1085 l.lock()
1086 l.lock()
1086 return l
1087 return l
1087
1088
1088 def unlock():
1089 def unlock():
1089 self.dirstate.write()
1090 self.dirstate.write()
1090 self._filecache['dirstate'].refresh()
1091 self._filecache['dirstate'].refresh()
1091
1092
1092 l = self._lock(self.vfs, "wlock", wait, unlock,
1093 l = self._lock(self.vfs, "wlock", wait, unlock,
1093 self.invalidatedirstate, _('working directory of %s') %
1094 self.invalidatedirstate, _('working directory of %s') %
1094 self.origroot)
1095 self.origroot)
1095 self._wlockref = weakref.ref(l)
1096 self._wlockref = weakref.ref(l)
1096 return l
1097 return l
1097
1098
1098 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1099 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1099 """
1100 """
1100 commit an individual file as part of a larger transaction
1101 commit an individual file as part of a larger transaction
1101 """
1102 """
1102
1103
1103 fname = fctx.path()
1104 fname = fctx.path()
1104 text = fctx.data()
1105 text = fctx.data()
1105 flog = self.file(fname)
1106 flog = self.file(fname)
1106 fparent1 = manifest1.get(fname, nullid)
1107 fparent1 = manifest1.get(fname, nullid)
1107 fparent2 = fparent2o = manifest2.get(fname, nullid)
1108 fparent2 = fparent2o = manifest2.get(fname, nullid)
1108
1109
1109 meta = {}
1110 meta = {}
1110 copy = fctx.renamed()
1111 copy = fctx.renamed()
1111 if copy and copy[0] != fname:
1112 if copy and copy[0] != fname:
1112 # Mark the new revision of this file as a copy of another
1113 # Mark the new revision of this file as a copy of another
1113 # file. This copy data will effectively act as a parent
1114 # file. This copy data will effectively act as a parent
1114 # of this new revision. If this is a merge, the first
1115 # of this new revision. If this is a merge, the first
1115 # parent will be the nullid (meaning "look up the copy data")
1116 # parent will be the nullid (meaning "look up the copy data")
1116 # and the second one will be the other parent. For example:
1117 # and the second one will be the other parent. For example:
1117 #
1118 #
1118 # 0 --- 1 --- 3 rev1 changes file foo
1119 # 0 --- 1 --- 3 rev1 changes file foo
1119 # \ / rev2 renames foo to bar and changes it
1120 # \ / rev2 renames foo to bar and changes it
1120 # \- 2 -/ rev3 should have bar with all changes and
1121 # \- 2 -/ rev3 should have bar with all changes and
1121 # should record that bar descends from
1122 # should record that bar descends from
1122 # bar in rev2 and foo in rev1
1123 # bar in rev2 and foo in rev1
1123 #
1124 #
1124 # this allows this merge to succeed:
1125 # this allows this merge to succeed:
1125 #
1126 #
1126 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1127 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1127 # \ / merging rev3 and rev4 should use bar@rev2
1128 # \ / merging rev3 and rev4 should use bar@rev2
1128 # \- 2 --- 4 as the merge base
1129 # \- 2 --- 4 as the merge base
1129 #
1130 #
1130
1131
1131 cfname = copy[0]
1132 cfname = copy[0]
1132 crev = manifest1.get(cfname)
1133 crev = manifest1.get(cfname)
1133 newfparent = fparent2
1134 newfparent = fparent2
1134
1135
1135 if manifest2: # branch merge
1136 if manifest2: # branch merge
1136 if fparent2 == nullid or crev is None: # copied on remote side
1137 if fparent2 == nullid or crev is None: # copied on remote side
1137 if cfname in manifest2:
1138 if cfname in manifest2:
1138 crev = manifest2[cfname]
1139 crev = manifest2[cfname]
1139 newfparent = fparent1
1140 newfparent = fparent1
1140
1141
1141 # find source in nearest ancestor if we've lost track
1142 # find source in nearest ancestor if we've lost track
1142 if not crev:
1143 if not crev:
1143 self.ui.debug(" %s: searching for copy revision for %s\n" %
1144 self.ui.debug(" %s: searching for copy revision for %s\n" %
1144 (fname, cfname))
1145 (fname, cfname))
1145 for ancestor in self[None].ancestors():
1146 for ancestor in self[None].ancestors():
1146 if cfname in ancestor:
1147 if cfname in ancestor:
1147 crev = ancestor[cfname].filenode()
1148 crev = ancestor[cfname].filenode()
1148 break
1149 break
1149
1150
1150 if crev:
1151 if crev:
1151 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1152 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1152 meta["copy"] = cfname
1153 meta["copy"] = cfname
1153 meta["copyrev"] = hex(crev)
1154 meta["copyrev"] = hex(crev)
1154 fparent1, fparent2 = nullid, newfparent
1155 fparent1, fparent2 = nullid, newfparent
1155 else:
1156 else:
1156 self.ui.warn(_("warning: can't find ancestor for '%s' "
1157 self.ui.warn(_("warning: can't find ancestor for '%s' "
1157 "copied from '%s'!\n") % (fname, cfname))
1158 "copied from '%s'!\n") % (fname, cfname))
1158
1159
1159 elif fparent1 == nullid:
1160 elif fparent1 == nullid:
1160 fparent1, fparent2 = fparent2, nullid
1161 fparent1, fparent2 = fparent2, nullid
1161 elif fparent2 != nullid:
1162 elif fparent2 != nullid:
1162 # is one parent an ancestor of the other?
1163 # is one parent an ancestor of the other?
1163 fparentancestors = flog.commonancestors(fparent1, fparent2)
1164 fparentancestors = flog.commonancestors(fparent1, fparent2)
1164 if fparent1 in fparentancestors:
1165 if fparent1 in fparentancestors:
1165 fparent1, fparent2 = fparent2, nullid
1166 fparent1, fparent2 = fparent2, nullid
1166 elif fparent2 in fparentancestors:
1167 elif fparent2 in fparentancestors:
1167 fparent2 = nullid
1168 fparent2 = nullid
1168
1169
1169 # is the file changed?
1170 # is the file changed?
1170 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1171 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1171 changelist.append(fname)
1172 changelist.append(fname)
1172 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1173 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1173
1174
1174 # are just the flags changed during merge?
1175 # are just the flags changed during merge?
1175 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
1176 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
1176 changelist.append(fname)
1177 changelist.append(fname)
1177
1178
1178 return fparent1
1179 return fparent1
1179
1180
1180 @unfilteredmethod
1181 @unfilteredmethod
1181 def commit(self, text="", user=None, date=None, match=None, force=False,
1182 def commit(self, text="", user=None, date=None, match=None, force=False,
1182 editor=False, extra={}):
1183 editor=False, extra={}):
1183 """Add a new revision to current repository.
1184 """Add a new revision to current repository.
1184
1185
1185 Revision information is gathered from the working directory,
1186 Revision information is gathered from the working directory,
1186 match can be used to filter the committed files. If editor is
1187 match can be used to filter the committed files. If editor is
1187 supplied, it is called to get a commit message.
1188 supplied, it is called to get a commit message.
1188 """
1189 """
1189
1190
1190 def fail(f, msg):
1191 def fail(f, msg):
1191 raise util.Abort('%s: %s' % (f, msg))
1192 raise util.Abort('%s: %s' % (f, msg))
1192
1193
1193 if not match:
1194 if not match:
1194 match = matchmod.always(self.root, '')
1195 match = matchmod.always(self.root, '')
1195
1196
1196 if not force:
1197 if not force:
1197 vdirs = []
1198 vdirs = []
1198 match.explicitdir = vdirs.append
1199 match.explicitdir = vdirs.append
1199 match.bad = fail
1200 match.bad = fail
1200
1201
1201 wlock = self.wlock()
1202 wlock = self.wlock()
1202 try:
1203 try:
1203 wctx = self[None]
1204 wctx = self[None]
1204 merge = len(wctx.parents()) > 1
1205 merge = len(wctx.parents()) > 1
1205
1206
1206 if (not force and merge and match and
1207 if (not force and merge and match and
1207 (match.files() or match.anypats())):
1208 (match.files() or match.anypats())):
1208 raise util.Abort(_('cannot partially commit a merge '
1209 raise util.Abort(_('cannot partially commit a merge '
1209 '(do not specify files or patterns)'))
1210 '(do not specify files or patterns)'))
1210
1211
1211 changes = self.status(match=match, clean=force)
1212 changes = self.status(match=match, clean=force)
1212 if force:
1213 if force:
1213 changes[0].extend(changes[6]) # mq may commit unchanged files
1214 changes[0].extend(changes[6]) # mq may commit unchanged files
1214
1215
1215 # check subrepos
1216 # check subrepos
1216 subs = []
1217 subs = []
1217 commitsubs = set()
1218 commitsubs = set()
1218 newstate = wctx.substate.copy()
1219 newstate = wctx.substate.copy()
1219 # only manage subrepos and .hgsubstate if .hgsub is present
1220 # only manage subrepos and .hgsubstate if .hgsub is present
1220 if '.hgsub' in wctx:
1221 if '.hgsub' in wctx:
1221 # we'll decide whether to track this ourselves, thanks
1222 # we'll decide whether to track this ourselves, thanks
1222 for c in changes[:3]:
1223 for c in changes[:3]:
1223 if '.hgsubstate' in c:
1224 if '.hgsubstate' in c:
1224 c.remove('.hgsubstate')
1225 c.remove('.hgsubstate')
1225
1226
1226 # compare current state to last committed state
1227 # compare current state to last committed state
1227 # build new substate based on last committed state
1228 # build new substate based on last committed state
1228 oldstate = wctx.p1().substate
1229 oldstate = wctx.p1().substate
1229 for s in sorted(newstate.keys()):
1230 for s in sorted(newstate.keys()):
1230 if not match(s):
1231 if not match(s):
1231 # ignore working copy, use old state if present
1232 # ignore working copy, use old state if present
1232 if s in oldstate:
1233 if s in oldstate:
1233 newstate[s] = oldstate[s]
1234 newstate[s] = oldstate[s]
1234 continue
1235 continue
1235 if not force:
1236 if not force:
1236 raise util.Abort(
1237 raise util.Abort(
1237 _("commit with new subrepo %s excluded") % s)
1238 _("commit with new subrepo %s excluded") % s)
1238 if wctx.sub(s).dirty(True):
1239 if wctx.sub(s).dirty(True):
1239 if not self.ui.configbool('ui', 'commitsubrepos'):
1240 if not self.ui.configbool('ui', 'commitsubrepos'):
1240 raise util.Abort(
1241 raise util.Abort(
1241 _("uncommitted changes in subrepo %s") % s,
1242 _("uncommitted changes in subrepo %s") % s,
1242 hint=_("use --subrepos for recursive commit"))
1243 hint=_("use --subrepos for recursive commit"))
1243 subs.append(s)
1244 subs.append(s)
1244 commitsubs.add(s)
1245 commitsubs.add(s)
1245 else:
1246 else:
1246 bs = wctx.sub(s).basestate()
1247 bs = wctx.sub(s).basestate()
1247 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1248 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1248 if oldstate.get(s, (None, None, None))[1] != bs:
1249 if oldstate.get(s, (None, None, None))[1] != bs:
1249 subs.append(s)
1250 subs.append(s)
1250
1251
1251 # check for removed subrepos
1252 # check for removed subrepos
1252 for p in wctx.parents():
1253 for p in wctx.parents():
1253 r = [s for s in p.substate if s not in newstate]
1254 r = [s for s in p.substate if s not in newstate]
1254 subs += [s for s in r if match(s)]
1255 subs += [s for s in r if match(s)]
1255 if subs:
1256 if subs:
1256 if (not match('.hgsub') and
1257 if (not match('.hgsub') and
1257 '.hgsub' in (wctx.modified() + wctx.added())):
1258 '.hgsub' in (wctx.modified() + wctx.added())):
1258 raise util.Abort(
1259 raise util.Abort(
1259 _("can't commit subrepos without .hgsub"))
1260 _("can't commit subrepos without .hgsub"))
1260 changes[0].insert(0, '.hgsubstate')
1261 changes[0].insert(0, '.hgsubstate')
1261
1262
1262 elif '.hgsub' in changes[2]:
1263 elif '.hgsub' in changes[2]:
1263 # clean up .hgsubstate when .hgsub is removed
1264 # clean up .hgsubstate when .hgsub is removed
1264 if ('.hgsubstate' in wctx and
1265 if ('.hgsubstate' in wctx and
1265 '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
1266 '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
1266 changes[2].insert(0, '.hgsubstate')
1267 changes[2].insert(0, '.hgsubstate')
1267
1268
1268 # make sure all explicit patterns are matched
1269 # make sure all explicit patterns are matched
1269 if not force and match.files():
1270 if not force and match.files():
1270 matched = set(changes[0] + changes[1] + changes[2])
1271 matched = set(changes[0] + changes[1] + changes[2])
1271
1272
1272 for f in match.files():
1273 for f in match.files():
1273 f = self.dirstate.normalize(f)
1274 f = self.dirstate.normalize(f)
1274 if f == '.' or f in matched or f in wctx.substate:
1275 if f == '.' or f in matched or f in wctx.substate:
1275 continue
1276 continue
1276 if f in changes[3]: # missing
1277 if f in changes[3]: # missing
1277 fail(f, _('file not found!'))
1278 fail(f, _('file not found!'))
1278 if f in vdirs: # visited directory
1279 if f in vdirs: # visited directory
1279 d = f + '/'
1280 d = f + '/'
1280 for mf in matched:
1281 for mf in matched:
1281 if mf.startswith(d):
1282 if mf.startswith(d):
1282 break
1283 break
1283 else:
1284 else:
1284 fail(f, _("no match under directory!"))
1285 fail(f, _("no match under directory!"))
1285 elif f not in self.dirstate:
1286 elif f not in self.dirstate:
1286 fail(f, _("file not tracked!"))
1287 fail(f, _("file not tracked!"))
1287
1288
1288 cctx = context.workingctx(self, text, user, date, extra, changes)
1289 cctx = context.workingctx(self, text, user, date, extra, changes)
1289
1290
1290 if (not force and not extra.get("close") and not merge
1291 if (not force and not extra.get("close") and not merge
1291 and not cctx.files()
1292 and not cctx.files()
1292 and wctx.branch() == wctx.p1().branch()):
1293 and wctx.branch() == wctx.p1().branch()):
1293 return None
1294 return None
1294
1295
1295 if merge and cctx.deleted():
1296 if merge and cctx.deleted():
1296 raise util.Abort(_("cannot commit merge with missing files"))
1297 raise util.Abort(_("cannot commit merge with missing files"))
1297
1298
1298 ms = mergemod.mergestate(self)
1299 ms = mergemod.mergestate(self)
1299 for f in changes[0]:
1300 for f in changes[0]:
1300 if f in ms and ms[f] == 'u':
1301 if f in ms and ms[f] == 'u':
1301 raise util.Abort(_("unresolved merge conflicts "
1302 raise util.Abort(_("unresolved merge conflicts "
1302 "(see hg help resolve)"))
1303 "(see hg help resolve)"))
1303
1304
1304 if editor:
1305 if editor:
1305 cctx._text = editor(self, cctx, subs)
1306 cctx._text = editor(self, cctx, subs)
1306 edited = (text != cctx._text)
1307 edited = (text != cctx._text)
1307
1308
1308 # Save commit message in case this transaction gets rolled back
1309 # Save commit message in case this transaction gets rolled back
1309 # (e.g. by a pretxncommit hook). Leave the content alone on
1310 # (e.g. by a pretxncommit hook). Leave the content alone on
1310 # the assumption that the user will use the same editor again.
1311 # the assumption that the user will use the same editor again.
1311 msgfn = self.savecommitmessage(cctx._text)
1312 msgfn = self.savecommitmessage(cctx._text)
1312
1313
1313 # commit subs and write new state
1314 # commit subs and write new state
1314 if subs:
1315 if subs:
1315 for s in sorted(commitsubs):
1316 for s in sorted(commitsubs):
1316 sub = wctx.sub(s)
1317 sub = wctx.sub(s)
1317 self.ui.status(_('committing subrepository %s\n') %
1318 self.ui.status(_('committing subrepository %s\n') %
1318 subrepo.subrelpath(sub))
1319 subrepo.subrelpath(sub))
1319 sr = sub.commit(cctx._text, user, date)
1320 sr = sub.commit(cctx._text, user, date)
1320 newstate[s] = (newstate[s][0], sr)
1321 newstate[s] = (newstate[s][0], sr)
1321 subrepo.writestate(self, newstate)
1322 subrepo.writestate(self, newstate)
1322
1323
1323 p1, p2 = self.dirstate.parents()
1324 p1, p2 = self.dirstate.parents()
1324 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1325 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1325 try:
1326 try:
1326 self.hook("precommit", throw=True, parent1=hookp1,
1327 self.hook("precommit", throw=True, parent1=hookp1,
1327 parent2=hookp2)
1328 parent2=hookp2)
1328 ret = self.commitctx(cctx, True)
1329 ret = self.commitctx(cctx, True)
1329 except: # re-raises
1330 except: # re-raises
1330 if edited:
1331 if edited:
1331 self.ui.write(
1332 self.ui.write(
1332 _('note: commit message saved in %s\n') % msgfn)
1333 _('note: commit message saved in %s\n') % msgfn)
1333 raise
1334 raise
1334
1335
1335 # update bookmarks, dirstate and mergestate
1336 # update bookmarks, dirstate and mergestate
1336 bookmarks.update(self, [p1, p2], ret)
1337 bookmarks.update(self, [p1, p2], ret)
1337 cctx.markcommitted(ret)
1338 cctx.markcommitted(ret)
1338 ms.reset()
1339 ms.reset()
1339 finally:
1340 finally:
1340 wlock.release()
1341 wlock.release()
1341
1342
1342 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1343 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1343 self.hook("commit", node=node, parent1=parent1, parent2=parent2)
1344 self.hook("commit", node=node, parent1=parent1, parent2=parent2)
1344 self._afterlock(commithook)
1345 self._afterlock(commithook)
1345 return ret
1346 return ret
1346
1347
1347 @unfilteredmethod
1348 @unfilteredmethod
1348 def commitctx(self, ctx, error=False):
1349 def commitctx(self, ctx, error=False):
1349 """Add a new revision to current repository.
1350 """Add a new revision to current repository.
1350 Revision information is passed via the context argument.
1351 Revision information is passed via the context argument.
1351 """
1352 """
1352
1353
1353 tr = lock = None
1354 tr = lock = None
1354 removed = list(ctx.removed())
1355 removed = list(ctx.removed())
1355 p1, p2 = ctx.p1(), ctx.p2()
1356 p1, p2 = ctx.p1(), ctx.p2()
1356 user = ctx.user()
1357 user = ctx.user()
1357
1358
1358 lock = self.lock()
1359 lock = self.lock()
1359 try:
1360 try:
1360 tr = self.transaction("commit")
1361 tr = self.transaction("commit")
1361 trp = weakref.proxy(tr)
1362 trp = weakref.proxy(tr)
1362
1363
1363 if ctx.files():
1364 if ctx.files():
1364 m1 = p1.manifest().copy()
1365 m1 = p1.manifest().copy()
1365 m2 = p2.manifest()
1366 m2 = p2.manifest()
1366
1367
1367 # check in files
1368 # check in files
1368 new = {}
1369 new = {}
1369 changed = []
1370 changed = []
1370 linkrev = len(self)
1371 linkrev = len(self)
1371 for f in sorted(ctx.modified() + ctx.added()):
1372 for f in sorted(ctx.modified() + ctx.added()):
1372 self.ui.note(f + "\n")
1373 self.ui.note(f + "\n")
1373 try:
1374 try:
1374 fctx = ctx[f]
1375 fctx = ctx[f]
1375 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1376 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1376 changed)
1377 changed)
1377 m1.set(f, fctx.flags())
1378 m1.set(f, fctx.flags())
1378 except OSError, inst:
1379 except OSError, inst:
1379 self.ui.warn(_("trouble committing %s!\n") % f)
1380 self.ui.warn(_("trouble committing %s!\n") % f)
1380 raise
1381 raise
1381 except IOError, inst:
1382 except IOError, inst:
1382 errcode = getattr(inst, 'errno', errno.ENOENT)
1383 errcode = getattr(inst, 'errno', errno.ENOENT)
1383 if error or errcode and errcode != errno.ENOENT:
1384 if error or errcode and errcode != errno.ENOENT:
1384 self.ui.warn(_("trouble committing %s!\n") % f)
1385 self.ui.warn(_("trouble committing %s!\n") % f)
1385 raise
1386 raise
1386 else:
1387 else:
1387 removed.append(f)
1388 removed.append(f)
1388
1389
1389 # update manifest
1390 # update manifest
1390 m1.update(new)
1391 m1.update(new)
1391 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1392 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1392 drop = [f for f in removed if f in m1]
1393 drop = [f for f in removed if f in m1]
1393 for f in drop:
1394 for f in drop:
1394 del m1[f]
1395 del m1[f]
1395 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1396 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1396 p2.manifestnode(), (new, drop))
1397 p2.manifestnode(), (new, drop))
1397 files = changed + removed
1398 files = changed + removed
1398 else:
1399 else:
1399 mn = p1.manifestnode()
1400 mn = p1.manifestnode()
1400 files = []
1401 files = []
1401
1402
1402 # update changelog
1403 # update changelog
1403 self.changelog.delayupdate()
1404 self.changelog.delayupdate()
1404 n = self.changelog.add(mn, files, ctx.description(),
1405 n = self.changelog.add(mn, files, ctx.description(),
1405 trp, p1.node(), p2.node(),
1406 trp, p1.node(), p2.node(),
1406 user, ctx.date(), ctx.extra().copy())
1407 user, ctx.date(), ctx.extra().copy())
1407 p = lambda: self.changelog.writepending() and self.root or ""
1408 p = lambda: self.changelog.writepending() and self.root or ""
1408 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1409 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1409 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1410 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1410 parent2=xp2, pending=p)
1411 parent2=xp2, pending=p)
1411 self.changelog.finalize(trp)
1412 self.changelog.finalize(trp)
1412 # set the new commit is proper phase
1413 # set the new commit is proper phase
1413 targetphase = subrepo.newcommitphase(self.ui, ctx)
1414 targetphase = subrepo.newcommitphase(self.ui, ctx)
1414 if targetphase:
1415 if targetphase:
1415 # retract boundary do not alter parent changeset.
1416 # retract boundary do not alter parent changeset.
1416 # if a parent have higher the resulting phase will
1417 # if a parent have higher the resulting phase will
1417 # be compliant anyway
1418 # be compliant anyway
1418 #
1419 #
1419 # if minimal phase was 0 we don't need to retract anything
1420 # if minimal phase was 0 we don't need to retract anything
1420 phases.retractboundary(self, targetphase, [n])
1421 phases.retractboundary(self, targetphase, [n])
1421 tr.close()
1422 tr.close()
1422 branchmap.updatecache(self.filtered('served'))
1423 branchmap.updatecache(self.filtered('served'))
1423 return n
1424 return n
1424 finally:
1425 finally:
1425 if tr:
1426 if tr:
1426 tr.release()
1427 tr.release()
1427 lock.release()
1428 lock.release()
1428
1429
1429 @unfilteredmethod
1430 @unfilteredmethod
1430 def destroying(self):
1431 def destroying(self):
1431 '''Inform the repository that nodes are about to be destroyed.
1432 '''Inform the repository that nodes are about to be destroyed.
1432 Intended for use by strip and rollback, so there's a common
1433 Intended for use by strip and rollback, so there's a common
1433 place for anything that has to be done before destroying history.
1434 place for anything that has to be done before destroying history.
1434
1435
1435 This is mostly useful for saving state that is in memory and waiting
1436 This is mostly useful for saving state that is in memory and waiting
1436 to be flushed when the current lock is released. Because a call to
1437 to be flushed when the current lock is released. Because a call to
1437 destroyed is imminent, the repo will be invalidated causing those
1438 destroyed is imminent, the repo will be invalidated causing those
1438 changes to stay in memory (waiting for the next unlock), or vanish
1439 changes to stay in memory (waiting for the next unlock), or vanish
1439 completely.
1440 completely.
1440 '''
1441 '''
1441 # When using the same lock to commit and strip, the phasecache is left
1442 # When using the same lock to commit and strip, the phasecache is left
1442 # dirty after committing. Then when we strip, the repo is invalidated,
1443 # dirty after committing. Then when we strip, the repo is invalidated,
1443 # causing those changes to disappear.
1444 # causing those changes to disappear.
1444 if '_phasecache' in vars(self):
1445 if '_phasecache' in vars(self):
1445 self._phasecache.write()
1446 self._phasecache.write()
1446
1447
1447 @unfilteredmethod
1448 @unfilteredmethod
1448 def destroyed(self):
1449 def destroyed(self):
1449 '''Inform the repository that nodes have been destroyed.
1450 '''Inform the repository that nodes have been destroyed.
1450 Intended for use by strip and rollback, so there's a common
1451 Intended for use by strip and rollback, so there's a common
1451 place for anything that has to be done after destroying history.
1452 place for anything that has to be done after destroying history.
1452 '''
1453 '''
1453 # When one tries to:
1454 # When one tries to:
1454 # 1) destroy nodes thus calling this method (e.g. strip)
1455 # 1) destroy nodes thus calling this method (e.g. strip)
1455 # 2) use phasecache somewhere (e.g. commit)
1456 # 2) use phasecache somewhere (e.g. commit)
1456 #
1457 #
1457 # then 2) will fail because the phasecache contains nodes that were
1458 # then 2) will fail because the phasecache contains nodes that were
1458 # removed. We can either remove phasecache from the filecache,
1459 # removed. We can either remove phasecache from the filecache,
1459 # causing it to reload next time it is accessed, or simply filter
1460 # causing it to reload next time it is accessed, or simply filter
1460 # the removed nodes now and write the updated cache.
1461 # the removed nodes now and write the updated cache.
1461 self._phasecache.filterunknown(self)
1462 self._phasecache.filterunknown(self)
1462 self._phasecache.write()
1463 self._phasecache.write()
1463
1464
1464 # update the 'served' branch cache to help read only server process
1465 # update the 'served' branch cache to help read only server process
1465 # Thanks to branchcache collaboration this is done from the nearest
1466 # Thanks to branchcache collaboration this is done from the nearest
1466 # filtered subset and it is expected to be fast.
1467 # filtered subset and it is expected to be fast.
1467 branchmap.updatecache(self.filtered('served'))
1468 branchmap.updatecache(self.filtered('served'))
1468
1469
1469 # Ensure the persistent tag cache is updated. Doing it now
1470 # Ensure the persistent tag cache is updated. Doing it now
1470 # means that the tag cache only has to worry about destroyed
1471 # means that the tag cache only has to worry about destroyed
1471 # heads immediately after a strip/rollback. That in turn
1472 # heads immediately after a strip/rollback. That in turn
1472 # guarantees that "cachetip == currenttip" (comparing both rev
1473 # guarantees that "cachetip == currenttip" (comparing both rev
1473 # and node) always means no nodes have been added or destroyed.
1474 # and node) always means no nodes have been added or destroyed.
1474
1475
1475 # XXX this is suboptimal when qrefresh'ing: we strip the current
1476 # XXX this is suboptimal when qrefresh'ing: we strip the current
1476 # head, refresh the tag cache, then immediately add a new head.
1477 # head, refresh the tag cache, then immediately add a new head.
1477 # But I think doing it this way is necessary for the "instant
1478 # But I think doing it this way is necessary for the "instant
1478 # tag cache retrieval" case to work.
1479 # tag cache retrieval" case to work.
1479 self.invalidate()
1480 self.invalidate()
1480
1481
1481 def walk(self, match, node=None):
1482 def walk(self, match, node=None):
1482 '''
1483 '''
1483 walk recursively through the directory tree or a given
1484 walk recursively through the directory tree or a given
1484 changeset, finding all files matched by the match
1485 changeset, finding all files matched by the match
1485 function
1486 function
1486 '''
1487 '''
1487 return self[node].walk(match)
1488 return self[node].walk(match)
1488
1489
1489 def status(self, node1='.', node2=None, match=None,
1490 def status(self, node1='.', node2=None, match=None,
1490 ignored=False, clean=False, unknown=False,
1491 ignored=False, clean=False, unknown=False,
1491 listsubrepos=False):
1492 listsubrepos=False):
1492 """return status of files between two nodes or node and working
1493 """return status of files between two nodes or node and working
1493 directory.
1494 directory.
1494
1495
1495 If node1 is None, use the first dirstate parent instead.
1496 If node1 is None, use the first dirstate parent instead.
1496 If node2 is None, compare node1 with working directory.
1497 If node2 is None, compare node1 with working directory.
1497 """
1498 """
1498
1499
1499 def mfmatches(ctx):
1500 def mfmatches(ctx):
1500 mf = ctx.manifest().copy()
1501 mf = ctx.manifest().copy()
1501 if match.always():
1502 if match.always():
1502 return mf
1503 return mf
1503 for fn in mf.keys():
1504 for fn in mf.keys():
1504 if not match(fn):
1505 if not match(fn):
1505 del mf[fn]
1506 del mf[fn]
1506 return mf
1507 return mf
1507
1508
1508 ctx1 = self[node1]
1509 ctx1 = self[node1]
1509 ctx2 = self[node2]
1510 ctx2 = self[node2]
1510
1511
1511 working = ctx2.rev() is None
1512 working = ctx2.rev() is None
1512 parentworking = working and ctx1 == self['.']
1513 parentworking = working and ctx1 == self['.']
1513 match = match or matchmod.always(self.root, self.getcwd())
1514 match = match or matchmod.always(self.root, self.getcwd())
1514 listignored, listclean, listunknown = ignored, clean, unknown
1515 listignored, listclean, listunknown = ignored, clean, unknown
1515
1516
1516 # load earliest manifest first for caching reasons
1517 # load earliest manifest first for caching reasons
1517 if not working and ctx2.rev() < ctx1.rev():
1518 if not working and ctx2.rev() < ctx1.rev():
1518 ctx2.manifest()
1519 ctx2.manifest()
1519
1520
1520 if not parentworking:
1521 if not parentworking:
1521 def bad(f, msg):
1522 def bad(f, msg):
1522 # 'f' may be a directory pattern from 'match.files()',
1523 # 'f' may be a directory pattern from 'match.files()',
1523 # so 'f not in ctx1' is not enough
1524 # so 'f not in ctx1' is not enough
1524 if f not in ctx1 and f not in ctx1.dirs():
1525 if f not in ctx1 and f not in ctx1.dirs():
1525 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1526 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1526 match.bad = bad
1527 match.bad = bad
1527
1528
1528 if working: # we need to scan the working dir
1529 if working: # we need to scan the working dir
1529 subrepos = []
1530 subrepos = []
1530 if '.hgsub' in self.dirstate:
1531 if '.hgsub' in self.dirstate:
1531 subrepos = sorted(ctx2.substate)
1532 subrepos = sorted(ctx2.substate)
1532 s = self.dirstate.status(match, subrepos, listignored,
1533 s = self.dirstate.status(match, subrepos, listignored,
1533 listclean, listunknown)
1534 listclean, listunknown)
1534 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1535 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1535
1536
1536 # check for any possibly clean files
1537 # check for any possibly clean files
1537 if parentworking and cmp:
1538 if parentworking and cmp:
1538 fixup = []
1539 fixup = []
1539 # do a full compare of any files that might have changed
1540 # do a full compare of any files that might have changed
1540 for f in sorted(cmp):
1541 for f in sorted(cmp):
1541 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1542 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1542 or ctx1[f].cmp(ctx2[f])):
1543 or ctx1[f].cmp(ctx2[f])):
1543 modified.append(f)
1544 modified.append(f)
1544 else:
1545 else:
1545 fixup.append(f)
1546 fixup.append(f)
1546
1547
1547 # update dirstate for files that are actually clean
1548 # update dirstate for files that are actually clean
1548 if fixup:
1549 if fixup:
1549 if listclean:
1550 if listclean:
1550 clean += fixup
1551 clean += fixup
1551
1552
1552 try:
1553 try:
1553 # updating the dirstate is optional
1554 # updating the dirstate is optional
1554 # so we don't wait on the lock
1555 # so we don't wait on the lock
1555 wlock = self.wlock(False)
1556 wlock = self.wlock(False)
1556 try:
1557 try:
1557 for f in fixup:
1558 for f in fixup:
1558 self.dirstate.normal(f)
1559 self.dirstate.normal(f)
1559 finally:
1560 finally:
1560 wlock.release()
1561 wlock.release()
1561 except error.LockError:
1562 except error.LockError:
1562 pass
1563 pass
1563
1564
1564 if not parentworking:
1565 if not parentworking:
1565 mf1 = mfmatches(ctx1)
1566 mf1 = mfmatches(ctx1)
1566 if working:
1567 if working:
1567 # we are comparing working dir against non-parent
1568 # we are comparing working dir against non-parent
1568 # generate a pseudo-manifest for the working dir
1569 # generate a pseudo-manifest for the working dir
1569 mf2 = mfmatches(self['.'])
1570 mf2 = mfmatches(self['.'])
1570 for f in cmp + modified + added:
1571 for f in cmp + modified + added:
1571 mf2[f] = None
1572 mf2[f] = None
1572 mf2.set(f, ctx2.flags(f))
1573 mf2.set(f, ctx2.flags(f))
1573 for f in removed:
1574 for f in removed:
1574 if f in mf2:
1575 if f in mf2:
1575 del mf2[f]
1576 del mf2[f]
1576 else:
1577 else:
1577 # we are comparing two revisions
1578 # we are comparing two revisions
1578 deleted, unknown, ignored = [], [], []
1579 deleted, unknown, ignored = [], [], []
1579 mf2 = mfmatches(ctx2)
1580 mf2 = mfmatches(ctx2)
1580
1581
1581 modified, added, clean = [], [], []
1582 modified, added, clean = [], [], []
1582 withflags = mf1.withflags() | mf2.withflags()
1583 withflags = mf1.withflags() | mf2.withflags()
1583 for fn, mf2node in mf2.iteritems():
1584 for fn, mf2node in mf2.iteritems():
1584 if fn in mf1:
1585 if fn in mf1:
1585 if (fn not in deleted and
1586 if (fn not in deleted and
1586 ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or
1587 ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or
1587 (mf1[fn] != mf2node and
1588 (mf1[fn] != mf2node and
1588 (mf2node or ctx1[fn].cmp(ctx2[fn]))))):
1589 (mf2node or ctx1[fn].cmp(ctx2[fn]))))):
1589 modified.append(fn)
1590 modified.append(fn)
1590 elif listclean:
1591 elif listclean:
1591 clean.append(fn)
1592 clean.append(fn)
1592 del mf1[fn]
1593 del mf1[fn]
1593 elif fn not in deleted:
1594 elif fn not in deleted:
1594 added.append(fn)
1595 added.append(fn)
1595 removed = mf1.keys()
1596 removed = mf1.keys()
1596
1597
1597 if working and modified and not self.dirstate._checklink:
1598 if working and modified and not self.dirstate._checklink:
1598 # Symlink placeholders may get non-symlink-like contents
1599 # Symlink placeholders may get non-symlink-like contents
1599 # via user error or dereferencing by NFS or Samba servers,
1600 # via user error or dereferencing by NFS or Samba servers,
1600 # so we filter out any placeholders that don't look like a
1601 # so we filter out any placeholders that don't look like a
1601 # symlink
1602 # symlink
1602 sane = []
1603 sane = []
1603 for f in modified:
1604 for f in modified:
1604 if ctx2.flags(f) == 'l':
1605 if ctx2.flags(f) == 'l':
1605 d = ctx2[f].data()
1606 d = ctx2[f].data()
1606 if d == '' or len(d) >= 1024 or '\n' in d or util.binary(d):
1607 if d == '' or len(d) >= 1024 or '\n' in d or util.binary(d):
1607 self.ui.debug('ignoring suspect symlink placeholder'
1608 self.ui.debug('ignoring suspect symlink placeholder'
1608 ' "%s"\n' % f)
1609 ' "%s"\n' % f)
1609 continue
1610 continue
1610 sane.append(f)
1611 sane.append(f)
1611 modified = sane
1612 modified = sane
1612
1613
1613 r = modified, added, removed, deleted, unknown, ignored, clean
1614 r = modified, added, removed, deleted, unknown, ignored, clean
1614
1615
1615 if listsubrepos:
1616 if listsubrepos:
1616 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1617 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1617 if working:
1618 if working:
1618 rev2 = None
1619 rev2 = None
1619 else:
1620 else:
1620 rev2 = ctx2.substate[subpath][1]
1621 rev2 = ctx2.substate[subpath][1]
1621 try:
1622 try:
1622 submatch = matchmod.narrowmatcher(subpath, match)
1623 submatch = matchmod.narrowmatcher(subpath, match)
1623 s = sub.status(rev2, match=submatch, ignored=listignored,
1624 s = sub.status(rev2, match=submatch, ignored=listignored,
1624 clean=listclean, unknown=listunknown,
1625 clean=listclean, unknown=listunknown,
1625 listsubrepos=True)
1626 listsubrepos=True)
1626 for rfiles, sfiles in zip(r, s):
1627 for rfiles, sfiles in zip(r, s):
1627 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1628 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1628 except error.LookupError:
1629 except error.LookupError:
1629 self.ui.status(_("skipping missing subrepository: %s\n")
1630 self.ui.status(_("skipping missing subrepository: %s\n")
1630 % subpath)
1631 % subpath)
1631
1632
1632 for l in r:
1633 for l in r:
1633 l.sort()
1634 l.sort()
1634 return r
1635 return r
1635
1636
1636 def heads(self, start=None):
1637 def heads(self, start=None):
1637 heads = self.changelog.heads(start)
1638 heads = self.changelog.heads(start)
1638 # sort the output in rev descending order
1639 # sort the output in rev descending order
1639 return sorted(heads, key=self.changelog.rev, reverse=True)
1640 return sorted(heads, key=self.changelog.rev, reverse=True)
1640
1641
1641 def branchheads(self, branch=None, start=None, closed=False):
1642 def branchheads(self, branch=None, start=None, closed=False):
1642 '''return a (possibly filtered) list of heads for the given branch
1643 '''return a (possibly filtered) list of heads for the given branch
1643
1644
1644 Heads are returned in topological order, from newest to oldest.
1645 Heads are returned in topological order, from newest to oldest.
1645 If branch is None, use the dirstate branch.
1646 If branch is None, use the dirstate branch.
1646 If start is not None, return only heads reachable from start.
1647 If start is not None, return only heads reachable from start.
1647 If closed is True, return heads that are marked as closed as well.
1648 If closed is True, return heads that are marked as closed as well.
1648 '''
1649 '''
1649 if branch is None:
1650 if branch is None:
1650 branch = self[None].branch()
1651 branch = self[None].branch()
1651 branches = self.branchmap()
1652 branches = self.branchmap()
1652 if branch not in branches:
1653 if branch not in branches:
1653 return []
1654 return []
1654 # the cache returns heads ordered lowest to highest
1655 # the cache returns heads ordered lowest to highest
1655 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
1656 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
1656 if start is not None:
1657 if start is not None:
1657 # filter out the heads that cannot be reached from startrev
1658 # filter out the heads that cannot be reached from startrev
1658 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1659 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1659 bheads = [h for h in bheads if h in fbheads]
1660 bheads = [h for h in bheads if h in fbheads]
1660 return bheads
1661 return bheads
1661
1662
1662 def branches(self, nodes):
1663 def branches(self, nodes):
1663 if not nodes:
1664 if not nodes:
1664 nodes = [self.changelog.tip()]
1665 nodes = [self.changelog.tip()]
1665 b = []
1666 b = []
1666 for n in nodes:
1667 for n in nodes:
1667 t = n
1668 t = n
1668 while True:
1669 while True:
1669 p = self.changelog.parents(n)
1670 p = self.changelog.parents(n)
1670 if p[1] != nullid or p[0] == nullid:
1671 if p[1] != nullid or p[0] == nullid:
1671 b.append((t, n, p[0], p[1]))
1672 b.append((t, n, p[0], p[1]))
1672 break
1673 break
1673 n = p[0]
1674 n = p[0]
1674 return b
1675 return b
1675
1676
1676 def between(self, pairs):
1677 def between(self, pairs):
1677 r = []
1678 r = []
1678
1679
1679 for top, bottom in pairs:
1680 for top, bottom in pairs:
1680 n, l, i = top, [], 0
1681 n, l, i = top, [], 0
1681 f = 1
1682 f = 1
1682
1683
1683 while n != bottom and n != nullid:
1684 while n != bottom and n != nullid:
1684 p = self.changelog.parents(n)[0]
1685 p = self.changelog.parents(n)[0]
1685 if i == f:
1686 if i == f:
1686 l.append(n)
1687 l.append(n)
1687 f = f * 2
1688 f = f * 2
1688 n = p
1689 n = p
1689 i += 1
1690 i += 1
1690
1691
1691 r.append(l)
1692 r.append(l)
1692
1693
1693 return r
1694 return r
1694
1695
1695 def pull(self, remote, heads=None, force=False):
1696 def pull(self, remote, heads=None, force=False):
1696 return exchange.pull (self, remote, heads, force)
1697 return exchange.pull (self, remote, heads, force)
1697
1698
1698 def checkpush(self, pushop):
1699 def checkpush(self, pushop):
1699 """Extensions can override this function if additional checks have
1700 """Extensions can override this function if additional checks have
1700 to be performed before pushing, or call it if they override push
1701 to be performed before pushing, or call it if they override push
1701 command.
1702 command.
1702 """
1703 """
1703 pass
1704 pass
1704
1705
1705 @unfilteredpropertycache
1706 @unfilteredpropertycache
1706 def prepushoutgoinghooks(self):
1707 def prepushoutgoinghooks(self):
1707 """Return util.hooks consists of "(repo, remote, outgoing)"
1708 """Return util.hooks consists of "(repo, remote, outgoing)"
1708 functions, which are called before pushing changesets.
1709 functions, which are called before pushing changesets.
1709 """
1710 """
1710 return util.hooks()
1711 return util.hooks()
1711
1712
1712 def push(self, remote, force=False, revs=None, newbranch=False):
1713 def push(self, remote, force=False, revs=None, newbranch=False):
1713 return exchange.push(self, remote, force, revs, newbranch)
1714 return exchange.push(self, remote, force, revs, newbranch)
1714
1715
1715 def stream_in(self, remote, requirements):
1716 def stream_in(self, remote, requirements):
1716 lock = self.lock()
1717 lock = self.lock()
1717 try:
1718 try:
1718 # Save remote branchmap. We will use it later
1719 # Save remote branchmap. We will use it later
1719 # to speed up branchcache creation
1720 # to speed up branchcache creation
1720 rbranchmap = None
1721 rbranchmap = None
1721 if remote.capable("branchmap"):
1722 if remote.capable("branchmap"):
1722 rbranchmap = remote.branchmap()
1723 rbranchmap = remote.branchmap()
1723
1724
1724 fp = remote.stream_out()
1725 fp = remote.stream_out()
1725 l = fp.readline()
1726 l = fp.readline()
1726 try:
1727 try:
1727 resp = int(l)
1728 resp = int(l)
1728 except ValueError:
1729 except ValueError:
1729 raise error.ResponseError(
1730 raise error.ResponseError(
1730 _('unexpected response from remote server:'), l)
1731 _('unexpected response from remote server:'), l)
1731 if resp == 1:
1732 if resp == 1:
1732 raise util.Abort(_('operation forbidden by server'))
1733 raise util.Abort(_('operation forbidden by server'))
1733 elif resp == 2:
1734 elif resp == 2:
1734 raise util.Abort(_('locking the remote repository failed'))
1735 raise util.Abort(_('locking the remote repository failed'))
1735 elif resp != 0:
1736 elif resp != 0:
1736 raise util.Abort(_('the server sent an unknown error code'))
1737 raise util.Abort(_('the server sent an unknown error code'))
1737 self.ui.status(_('streaming all changes\n'))
1738 self.ui.status(_('streaming all changes\n'))
1738 l = fp.readline()
1739 l = fp.readline()
1739 try:
1740 try:
1740 total_files, total_bytes = map(int, l.split(' ', 1))
1741 total_files, total_bytes = map(int, l.split(' ', 1))
1741 except (ValueError, TypeError):
1742 except (ValueError, TypeError):
1742 raise error.ResponseError(
1743 raise error.ResponseError(
1743 _('unexpected response from remote server:'), l)
1744 _('unexpected response from remote server:'), l)
1744 self.ui.status(_('%d files to transfer, %s of data\n') %
1745 self.ui.status(_('%d files to transfer, %s of data\n') %
1745 (total_files, util.bytecount(total_bytes)))
1746 (total_files, util.bytecount(total_bytes)))
1746 handled_bytes = 0
1747 handled_bytes = 0
1747 self.ui.progress(_('clone'), 0, total=total_bytes)
1748 self.ui.progress(_('clone'), 0, total=total_bytes)
1748 start = time.time()
1749 start = time.time()
1749
1750
1750 tr = self.transaction(_('clone'))
1751 tr = self.transaction(_('clone'))
1751 try:
1752 try:
1752 for i in xrange(total_files):
1753 for i in xrange(total_files):
1753 # XXX doesn't support '\n' or '\r' in filenames
1754 # XXX doesn't support '\n' or '\r' in filenames
1754 l = fp.readline()
1755 l = fp.readline()
1755 try:
1756 try:
1756 name, size = l.split('\0', 1)
1757 name, size = l.split('\0', 1)
1757 size = int(size)
1758 size = int(size)
1758 except (ValueError, TypeError):
1759 except (ValueError, TypeError):
1759 raise error.ResponseError(
1760 raise error.ResponseError(
1760 _('unexpected response from remote server:'), l)
1761 _('unexpected response from remote server:'), l)
1761 if self.ui.debugflag:
1762 if self.ui.debugflag:
1762 self.ui.debug('adding %s (%s)\n' %
1763 self.ui.debug('adding %s (%s)\n' %
1763 (name, util.bytecount(size)))
1764 (name, util.bytecount(size)))
1764 # for backwards compat, name was partially encoded
1765 # for backwards compat, name was partially encoded
1765 ofp = self.sopener(store.decodedir(name), 'w')
1766 ofp = self.sopener(store.decodedir(name), 'w')
1766 for chunk in util.filechunkiter(fp, limit=size):
1767 for chunk in util.filechunkiter(fp, limit=size):
1767 handled_bytes += len(chunk)
1768 handled_bytes += len(chunk)
1768 self.ui.progress(_('clone'), handled_bytes,
1769 self.ui.progress(_('clone'), handled_bytes,
1769 total=total_bytes)
1770 total=total_bytes)
1770 ofp.write(chunk)
1771 ofp.write(chunk)
1771 ofp.close()
1772 ofp.close()
1772 tr.close()
1773 tr.close()
1773 finally:
1774 finally:
1774 tr.release()
1775 tr.release()
1775
1776
1776 # Writing straight to files circumvented the inmemory caches
1777 # Writing straight to files circumvented the inmemory caches
1777 self.invalidate()
1778 self.invalidate()
1778
1779
1779 elapsed = time.time() - start
1780 elapsed = time.time() - start
1780 if elapsed <= 0:
1781 if elapsed <= 0:
1781 elapsed = 0.001
1782 elapsed = 0.001
1782 self.ui.progress(_('clone'), None)
1783 self.ui.progress(_('clone'), None)
1783 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1784 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1784 (util.bytecount(total_bytes), elapsed,
1785 (util.bytecount(total_bytes), elapsed,
1785 util.bytecount(total_bytes / elapsed)))
1786 util.bytecount(total_bytes / elapsed)))
1786
1787
1787 # new requirements = old non-format requirements +
1788 # new requirements = old non-format requirements +
1788 # new format-related
1789 # new format-related
1789 # requirements from the streamed-in repository
1790 # requirements from the streamed-in repository
1790 requirements.update(set(self.requirements) - self.supportedformats)
1791 requirements.update(set(self.requirements) - self.supportedformats)
1791 self._applyrequirements(requirements)
1792 self._applyrequirements(requirements)
1792 self._writerequirements()
1793 self._writerequirements()
1793
1794
1794 if rbranchmap:
1795 if rbranchmap:
1795 rbheads = []
1796 rbheads = []
1796 for bheads in rbranchmap.itervalues():
1797 for bheads in rbranchmap.itervalues():
1797 rbheads.extend(bheads)
1798 rbheads.extend(bheads)
1798
1799
1799 if rbheads:
1800 if rbheads:
1800 rtiprev = max((int(self.changelog.rev(node))
1801 rtiprev = max((int(self.changelog.rev(node))
1801 for node in rbheads))
1802 for node in rbheads))
1802 cache = branchmap.branchcache(rbranchmap,
1803 cache = branchmap.branchcache(rbranchmap,
1803 self[rtiprev].node(),
1804 self[rtiprev].node(),
1804 rtiprev)
1805 rtiprev)
1805 # Try to stick it as low as possible
1806 # Try to stick it as low as possible
1806 # filter above served are unlikely to be fetch from a clone
1807 # filter above served are unlikely to be fetch from a clone
1807 for candidate in ('base', 'immutable', 'served'):
1808 for candidate in ('base', 'immutable', 'served'):
1808 rview = self.filtered(candidate)
1809 rview = self.filtered(candidate)
1809 if cache.validfor(rview):
1810 if cache.validfor(rview):
1810 self._branchcaches[candidate] = cache
1811 self._branchcaches[candidate] = cache
1811 cache.write(rview)
1812 cache.write(rview)
1812 break
1813 break
1813 self.invalidate()
1814 self.invalidate()
1814 return len(self.heads()) + 1
1815 return len(self.heads()) + 1
1815 finally:
1816 finally:
1816 lock.release()
1817 lock.release()
1817
1818
1818 def clone(self, remote, heads=[], stream=False):
1819 def clone(self, remote, heads=[], stream=False):
1819 '''clone remote repository.
1820 '''clone remote repository.
1820
1821
1821 keyword arguments:
1822 keyword arguments:
1822 heads: list of revs to clone (forces use of pull)
1823 heads: list of revs to clone (forces use of pull)
1823 stream: use streaming clone if possible'''
1824 stream: use streaming clone if possible'''
1824
1825
1825 # now, all clients that can request uncompressed clones can
1826 # now, all clients that can request uncompressed clones can
1826 # read repo formats supported by all servers that can serve
1827 # read repo formats supported by all servers that can serve
1827 # them.
1828 # them.
1828
1829
1829 # if revlog format changes, client will have to check version
1830 # if revlog format changes, client will have to check version
1830 # and format flags on "stream" capability, and use
1831 # and format flags on "stream" capability, and use
1831 # uncompressed only if compatible.
1832 # uncompressed only if compatible.
1832
1833
1833 if not stream:
1834 if not stream:
1834 # if the server explicitly prefers to stream (for fast LANs)
1835 # if the server explicitly prefers to stream (for fast LANs)
1835 stream = remote.capable('stream-preferred')
1836 stream = remote.capable('stream-preferred')
1836
1837
1837 if stream and not heads:
1838 if stream and not heads:
1838 # 'stream' means remote revlog format is revlogv1 only
1839 # 'stream' means remote revlog format is revlogv1 only
1839 if remote.capable('stream'):
1840 if remote.capable('stream'):
1840 return self.stream_in(remote, set(('revlogv1',)))
1841 return self.stream_in(remote, set(('revlogv1',)))
1841 # otherwise, 'streamreqs' contains the remote revlog format
1842 # otherwise, 'streamreqs' contains the remote revlog format
1842 streamreqs = remote.capable('streamreqs')
1843 streamreqs = remote.capable('streamreqs')
1843 if streamreqs:
1844 if streamreqs:
1844 streamreqs = set(streamreqs.split(','))
1845 streamreqs = set(streamreqs.split(','))
1845 # if we support it, stream in and adjust our requirements
1846 # if we support it, stream in and adjust our requirements
1846 if not streamreqs - self.supportedformats:
1847 if not streamreqs - self.supportedformats:
1847 return self.stream_in(remote, streamreqs)
1848 return self.stream_in(remote, streamreqs)
1848 return self.pull(remote, heads)
1849 return self.pull(remote, heads)
1849
1850
1850 def pushkey(self, namespace, key, old, new):
1851 def pushkey(self, namespace, key, old, new):
1851 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
1852 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
1852 old=old, new=new)
1853 old=old, new=new)
1853 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
1854 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
1854 ret = pushkey.push(self, namespace, key, old, new)
1855 ret = pushkey.push(self, namespace, key, old, new)
1855 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
1856 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
1856 ret=ret)
1857 ret=ret)
1857 return ret
1858 return ret
1858
1859
1859 def listkeys(self, namespace):
1860 def listkeys(self, namespace):
1860 self.hook('prelistkeys', throw=True, namespace=namespace)
1861 self.hook('prelistkeys', throw=True, namespace=namespace)
1861 self.ui.debug('listing keys for "%s"\n' % namespace)
1862 self.ui.debug('listing keys for "%s"\n' % namespace)
1862 values = pushkey.list(self, namespace)
1863 values = pushkey.list(self, namespace)
1863 self.hook('listkeys', namespace=namespace, values=values)
1864 self.hook('listkeys', namespace=namespace, values=values)
1864 return values
1865 return values
1865
1866
1866 def debugwireargs(self, one, two, three=None, four=None, five=None):
1867 def debugwireargs(self, one, two, three=None, four=None, five=None):
1867 '''used to test argument passing over the wire'''
1868 '''used to test argument passing over the wire'''
1868 return "%s %s %s %s %s" % (one, two, three, four, five)
1869 return "%s %s %s %s %s" % (one, two, three, four, five)
1869
1870
1870 def savecommitmessage(self, text):
1871 def savecommitmessage(self, text):
1871 fp = self.opener('last-message.txt', 'wb')
1872 fp = self.opener('last-message.txt', 'wb')
1872 try:
1873 try:
1873 fp.write(text)
1874 fp.write(text)
1874 finally:
1875 finally:
1875 fp.close()
1876 fp.close()
1876 return self.pathto(fp.name[len(self.root) + 1:])
1877 return self.pathto(fp.name[len(self.root) + 1:])
1877
1878
1878 # used to avoid circular references so destructors work
1879 # used to avoid circular references so destructors work
1879 def aftertrans(files):
1880 def aftertrans(files):
1880 renamefiles = [tuple(t) for t in files]
1881 renamefiles = [tuple(t) for t in files]
1881 def a():
1882 def a():
1882 for vfs, src, dest in renamefiles:
1883 for vfs, src, dest in renamefiles:
1883 try:
1884 try:
1884 vfs.rename(src, dest)
1885 vfs.rename(src, dest)
1885 except OSError: # journal file does not yet exist
1886 except OSError: # journal file does not yet exist
1886 pass
1887 pass
1887 return a
1888 return a
1888
1889
1889 def undoname(fn):
1890 def undoname(fn):
1890 base, name = os.path.split(fn)
1891 base, name = os.path.split(fn)
1891 assert name.startswith('journal')
1892 assert name.startswith('journal')
1892 return os.path.join(base, name.replace('journal', 'undo', 1))
1893 return os.path.join(base, name.replace('journal', 'undo', 1))
1893
1894
1894 def instance(ui, path, create):
1895 def instance(ui, path, create):
1895 return localrepository(ui, util.urllocalpath(path), create)
1896 return localrepository(ui, util.urllocalpath(path), create)
1896
1897
1897 def islocal(path):
1898 def islocal(path):
1898 return True
1899 return True
@@ -1,2139 +1,2139 b''
1 > do_push()
1 > do_push()
2 > {
2 > {
3 > user=$1
3 > user=$1
4 > shift
4 > shift
5 > echo "Pushing as user $user"
5 > echo "Pushing as user $user"
6 > echo 'hgrc = """'
6 > echo 'hgrc = """'
7 > sed -e 1,2d b/.hg/hgrc | grep -v fakegroups.py
7 > sed -e 1,2d b/.hg/hgrc | grep -v fakegroups.py
8 > echo '"""'
8 > echo '"""'
9 > if test -f acl.config; then
9 > if test -f acl.config; then
10 > echo 'acl.config = """'
10 > echo 'acl.config = """'
11 > cat acl.config
11 > cat acl.config
12 > echo '"""'
12 > echo '"""'
13 > fi
13 > fi
14 > # On AIX /etc/profile sets LOGNAME read-only. So
14 > # On AIX /etc/profile sets LOGNAME read-only. So
15 > # LOGNAME=$user hg --cws a --debug push ../b
15 > # LOGNAME=$user hg --cws a --debug push ../b
16 > # fails with "This variable is read only."
16 > # fails with "This variable is read only."
17 > # Use env to work around this.
17 > # Use env to work around this.
18 > env LOGNAME=$user hg --cwd a --debug push ../b
18 > env LOGNAME=$user hg --cwd a --debug push ../b
19 > hg --cwd b rollback
19 > hg --cwd b rollback
20 > hg --cwd b --quiet tip
20 > hg --cwd b --quiet tip
21 > echo
21 > echo
22 > }
22 > }
23
23
24 > init_config()
24 > init_config()
25 > {
25 > {
26 > cat > fakegroups.py <<EOF
26 > cat > fakegroups.py <<EOF
27 > from hgext import acl
27 > from hgext import acl
28 > def fakegetusers(ui, group):
28 > def fakegetusers(ui, group):
29 > try:
29 > try:
30 > return acl._getusersorig(ui, group)
30 > return acl._getusersorig(ui, group)
31 > except:
31 > except:
32 > return ["fred", "betty"]
32 > return ["fred", "betty"]
33 > acl._getusersorig = acl._getusers
33 > acl._getusersorig = acl._getusers
34 > acl._getusers = fakegetusers
34 > acl._getusers = fakegetusers
35 > EOF
35 > EOF
36 > rm -f acl.config
36 > rm -f acl.config
37 > cat > $config <<EOF
37 > cat > $config <<EOF
38 > [hooks]
38 > [hooks]
39 > pretxnchangegroup.acl = python:hgext.acl.hook
39 > pretxnchangegroup.acl = python:hgext.acl.hook
40 > [acl]
40 > [acl]
41 > sources = push
41 > sources = push
42 > [extensions]
42 > [extensions]
43 > f=`pwd`/fakegroups.py
43 > f=`pwd`/fakegroups.py
44 > EOF
44 > EOF
45 > }
45 > }
46
46
47 $ hg init a
47 $ hg init a
48 $ cd a
48 $ cd a
49 $ mkdir foo foo/Bar quux
49 $ mkdir foo foo/Bar quux
50 $ echo 'in foo' > foo/file.txt
50 $ echo 'in foo' > foo/file.txt
51 $ echo 'in foo/Bar' > foo/Bar/file.txt
51 $ echo 'in foo/Bar' > foo/Bar/file.txt
52 $ echo 'in quux' > quux/file.py
52 $ echo 'in quux' > quux/file.py
53 $ hg add -q
53 $ hg add -q
54 $ hg ci -m 'add files' -d '1000000 0'
54 $ hg ci -m 'add files' -d '1000000 0'
55 $ echo >> foo/file.txt
55 $ echo >> foo/file.txt
56 $ hg ci -m 'change foo/file' -d '1000001 0'
56 $ hg ci -m 'change foo/file' -d '1000001 0'
57 $ echo >> foo/Bar/file.txt
57 $ echo >> foo/Bar/file.txt
58 $ hg ci -m 'change foo/Bar/file' -d '1000002 0'
58 $ hg ci -m 'change foo/Bar/file' -d '1000002 0'
59 $ echo >> quux/file.py
59 $ echo >> quux/file.py
60 $ hg ci -m 'change quux/file' -d '1000003 0'
60 $ hg ci -m 'change quux/file' -d '1000003 0'
61 $ hg tip --quiet
61 $ hg tip --quiet
62 3:911600dab2ae
62 3:911600dab2ae
63
63
64 $ cd ..
64 $ cd ..
65 $ hg clone -r 0 a b
65 $ hg clone -r 0 a b
66 adding changesets
66 adding changesets
67 adding manifests
67 adding manifests
68 adding file changes
68 adding file changes
69 added 1 changesets with 3 changes to 3 files
69 added 1 changesets with 3 changes to 3 files
70 updating to branch default
70 updating to branch default
71 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
72
72
73 $ config=b/.hg/hgrc
73 $ config=b/.hg/hgrc
74
74
75 Extension disabled for lack of a hook
75 Extension disabled for lack of a hook
76
76
77 $ do_push fred
77 $ do_push fred
78 Pushing as user fred
78 Pushing as user fred
79 hgrc = """
79 hgrc = """
80 """
80 """
81 pushing to ../b
81 pushing to ../b
82 query 1; heads
82 query 1; heads
83 searching for changes
83 searching for changes
84 all remote heads known locally
84 all remote heads known locally
85 listing keys for "bookmarks"
85 listing keys for "bookmarks"
86 3 changesets found
86 3 changesets found
87 list of changesets:
87 list of changesets:
88 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
88 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
89 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
89 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
90 911600dab2ae7a9baff75958b84fe606851ce955
90 911600dab2ae7a9baff75958b84fe606851ce955
91 adding changesets
92 bundling: 1/3 changesets (33.33%)
91 bundling: 1/3 changesets (33.33%)
93 bundling: 2/3 changesets (66.67%)
92 bundling: 2/3 changesets (66.67%)
94 bundling: 3/3 changesets (100.00%)
93 bundling: 3/3 changesets (100.00%)
95 bundling: 1/3 manifests (33.33%)
94 bundling: 1/3 manifests (33.33%)
96 bundling: 2/3 manifests (66.67%)
95 bundling: 2/3 manifests (66.67%)
97 bundling: 3/3 manifests (100.00%)
96 bundling: 3/3 manifests (100.00%)
98 bundling: foo/Bar/file.txt 1/3 files (33.33%)
97 bundling: foo/Bar/file.txt 1/3 files (33.33%)
99 bundling: foo/file.txt 2/3 files (66.67%)
98 bundling: foo/file.txt 2/3 files (66.67%)
100 bundling: quux/file.py 3/3 files (100.00%)
99 bundling: quux/file.py 3/3 files (100.00%)
100 adding changesets
101 changesets: 1 chunks
101 changesets: 1 chunks
102 add changeset ef1ea85a6374
102 add changeset ef1ea85a6374
103 changesets: 2 chunks
103 changesets: 2 chunks
104 add changeset f9cafe1212c8
104 add changeset f9cafe1212c8
105 changesets: 3 chunks
105 changesets: 3 chunks
106 add changeset 911600dab2ae
106 add changeset 911600dab2ae
107 adding manifests
107 adding manifests
108 manifests: 1/3 chunks (33.33%)
108 manifests: 1/3 chunks (33.33%)
109 manifests: 2/3 chunks (66.67%)
109 manifests: 2/3 chunks (66.67%)
110 manifests: 3/3 chunks (100.00%)
110 manifests: 3/3 chunks (100.00%)
111 adding file changes
111 adding file changes
112 adding foo/Bar/file.txt revisions
112 adding foo/Bar/file.txt revisions
113 files: 1/3 chunks (33.33%)
113 files: 1/3 chunks (33.33%)
114 adding foo/file.txt revisions
114 adding foo/file.txt revisions
115 files: 2/3 chunks (66.67%)
115 files: 2/3 chunks (66.67%)
116 adding quux/file.py revisions
116 adding quux/file.py revisions
117 files: 3/3 chunks (100.00%)
117 files: 3/3 chunks (100.00%)
118 added 3 changesets with 3 changes to 3 files
118 added 3 changesets with 3 changes to 3 files
119 updating the branch cache
119 updating the branch cache
120 listing keys for "phases"
120 listing keys for "phases"
121 try to push obsolete markers to remote
121 try to push obsolete markers to remote
122 checking for updated bookmarks
122 checking for updated bookmarks
123 listing keys for "bookmarks"
123 listing keys for "bookmarks"
124 repository tip rolled back to revision 0 (undo push)
124 repository tip rolled back to revision 0 (undo push)
125 0:6675d58eff77
125 0:6675d58eff77
126
126
127
127
128 $ echo '[hooks]' >> $config
128 $ echo '[hooks]' >> $config
129 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
129 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
130
130
131 Extension disabled for lack of acl.sources
131 Extension disabled for lack of acl.sources
132
132
133 $ do_push fred
133 $ do_push fred
134 Pushing as user fred
134 Pushing as user fred
135 hgrc = """
135 hgrc = """
136 [hooks]
136 [hooks]
137 pretxnchangegroup.acl = python:hgext.acl.hook
137 pretxnchangegroup.acl = python:hgext.acl.hook
138 """
138 """
139 pushing to ../b
139 pushing to ../b
140 query 1; heads
140 query 1; heads
141 searching for changes
141 searching for changes
142 all remote heads known locally
142 all remote heads known locally
143 invalid branchheads cache (served): tip differs
143 invalid branchheads cache (served): tip differs
144 listing keys for "bookmarks"
144 listing keys for "bookmarks"
145 3 changesets found
145 3 changesets found
146 list of changesets:
146 list of changesets:
147 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
147 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
148 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
148 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
149 911600dab2ae7a9baff75958b84fe606851ce955
149 911600dab2ae7a9baff75958b84fe606851ce955
150 adding changesets
151 bundling: 1/3 changesets (33.33%)
150 bundling: 1/3 changesets (33.33%)
152 bundling: 2/3 changesets (66.67%)
151 bundling: 2/3 changesets (66.67%)
153 bundling: 3/3 changesets (100.00%)
152 bundling: 3/3 changesets (100.00%)
154 bundling: 1/3 manifests (33.33%)
153 bundling: 1/3 manifests (33.33%)
155 bundling: 2/3 manifests (66.67%)
154 bundling: 2/3 manifests (66.67%)
156 bundling: 3/3 manifests (100.00%)
155 bundling: 3/3 manifests (100.00%)
157 bundling: foo/Bar/file.txt 1/3 files (33.33%)
156 bundling: foo/Bar/file.txt 1/3 files (33.33%)
158 bundling: foo/file.txt 2/3 files (66.67%)
157 bundling: foo/file.txt 2/3 files (66.67%)
159 bundling: quux/file.py 3/3 files (100.00%)
158 bundling: quux/file.py 3/3 files (100.00%)
159 adding changesets
160 changesets: 1 chunks
160 changesets: 1 chunks
161 add changeset ef1ea85a6374
161 add changeset ef1ea85a6374
162 changesets: 2 chunks
162 changesets: 2 chunks
163 add changeset f9cafe1212c8
163 add changeset f9cafe1212c8
164 changesets: 3 chunks
164 changesets: 3 chunks
165 add changeset 911600dab2ae
165 add changeset 911600dab2ae
166 adding manifests
166 adding manifests
167 manifests: 1/3 chunks (33.33%)
167 manifests: 1/3 chunks (33.33%)
168 manifests: 2/3 chunks (66.67%)
168 manifests: 2/3 chunks (66.67%)
169 manifests: 3/3 chunks (100.00%)
169 manifests: 3/3 chunks (100.00%)
170 adding file changes
170 adding file changes
171 adding foo/Bar/file.txt revisions
171 adding foo/Bar/file.txt revisions
172 files: 1/3 chunks (33.33%)
172 files: 1/3 chunks (33.33%)
173 adding foo/file.txt revisions
173 adding foo/file.txt revisions
174 files: 2/3 chunks (66.67%)
174 files: 2/3 chunks (66.67%)
175 adding quux/file.py revisions
175 adding quux/file.py revisions
176 files: 3/3 chunks (100.00%)
176 files: 3/3 chunks (100.00%)
177 added 3 changesets with 3 changes to 3 files
177 added 3 changesets with 3 changes to 3 files
178 calling hook pretxnchangegroup.acl: hgext.acl.hook
178 calling hook pretxnchangegroup.acl: hgext.acl.hook
179 acl: changes have source "push" - skipping
179 acl: changes have source "push" - skipping
180 updating the branch cache
180 updating the branch cache
181 listing keys for "phases"
181 listing keys for "phases"
182 try to push obsolete markers to remote
182 try to push obsolete markers to remote
183 checking for updated bookmarks
183 checking for updated bookmarks
184 listing keys for "bookmarks"
184 listing keys for "bookmarks"
185 repository tip rolled back to revision 0 (undo push)
185 repository tip rolled back to revision 0 (undo push)
186 0:6675d58eff77
186 0:6675d58eff77
187
187
188
188
189 No [acl.allow]/[acl.deny]
189 No [acl.allow]/[acl.deny]
190
190
191 $ echo '[acl]' >> $config
191 $ echo '[acl]' >> $config
192 $ echo 'sources = push' >> $config
192 $ echo 'sources = push' >> $config
193 $ do_push fred
193 $ do_push fred
194 Pushing as user fred
194 Pushing as user fred
195 hgrc = """
195 hgrc = """
196 [hooks]
196 [hooks]
197 pretxnchangegroup.acl = python:hgext.acl.hook
197 pretxnchangegroup.acl = python:hgext.acl.hook
198 [acl]
198 [acl]
199 sources = push
199 sources = push
200 """
200 """
201 pushing to ../b
201 pushing to ../b
202 query 1; heads
202 query 1; heads
203 searching for changes
203 searching for changes
204 all remote heads known locally
204 all remote heads known locally
205 invalid branchheads cache (served): tip differs
205 invalid branchheads cache (served): tip differs
206 listing keys for "bookmarks"
206 listing keys for "bookmarks"
207 3 changesets found
207 3 changesets found
208 list of changesets:
208 list of changesets:
209 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
209 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
210 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
210 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
211 911600dab2ae7a9baff75958b84fe606851ce955
211 911600dab2ae7a9baff75958b84fe606851ce955
212 adding changesets
213 bundling: 1/3 changesets (33.33%)
212 bundling: 1/3 changesets (33.33%)
214 bundling: 2/3 changesets (66.67%)
213 bundling: 2/3 changesets (66.67%)
215 bundling: 3/3 changesets (100.00%)
214 bundling: 3/3 changesets (100.00%)
216 bundling: 1/3 manifests (33.33%)
215 bundling: 1/3 manifests (33.33%)
217 bundling: 2/3 manifests (66.67%)
216 bundling: 2/3 manifests (66.67%)
218 bundling: 3/3 manifests (100.00%)
217 bundling: 3/3 manifests (100.00%)
219 bundling: foo/Bar/file.txt 1/3 files (33.33%)
218 bundling: foo/Bar/file.txt 1/3 files (33.33%)
220 bundling: foo/file.txt 2/3 files (66.67%)
219 bundling: foo/file.txt 2/3 files (66.67%)
221 bundling: quux/file.py 3/3 files (100.00%)
220 bundling: quux/file.py 3/3 files (100.00%)
221 adding changesets
222 changesets: 1 chunks
222 changesets: 1 chunks
223 add changeset ef1ea85a6374
223 add changeset ef1ea85a6374
224 changesets: 2 chunks
224 changesets: 2 chunks
225 add changeset f9cafe1212c8
225 add changeset f9cafe1212c8
226 changesets: 3 chunks
226 changesets: 3 chunks
227 add changeset 911600dab2ae
227 add changeset 911600dab2ae
228 adding manifests
228 adding manifests
229 manifests: 1/3 chunks (33.33%)
229 manifests: 1/3 chunks (33.33%)
230 manifests: 2/3 chunks (66.67%)
230 manifests: 2/3 chunks (66.67%)
231 manifests: 3/3 chunks (100.00%)
231 manifests: 3/3 chunks (100.00%)
232 adding file changes
232 adding file changes
233 adding foo/Bar/file.txt revisions
233 adding foo/Bar/file.txt revisions
234 files: 1/3 chunks (33.33%)
234 files: 1/3 chunks (33.33%)
235 adding foo/file.txt revisions
235 adding foo/file.txt revisions
236 files: 2/3 chunks (66.67%)
236 files: 2/3 chunks (66.67%)
237 adding quux/file.py revisions
237 adding quux/file.py revisions
238 files: 3/3 chunks (100.00%)
238 files: 3/3 chunks (100.00%)
239 added 3 changesets with 3 changes to 3 files
239 added 3 changesets with 3 changes to 3 files
240 calling hook pretxnchangegroup.acl: hgext.acl.hook
240 calling hook pretxnchangegroup.acl: hgext.acl.hook
241 acl: checking access for user "fred"
241 acl: checking access for user "fred"
242 acl: acl.allow.branches not enabled
242 acl: acl.allow.branches not enabled
243 acl: acl.deny.branches not enabled
243 acl: acl.deny.branches not enabled
244 acl: acl.allow not enabled
244 acl: acl.allow not enabled
245 acl: acl.deny not enabled
245 acl: acl.deny not enabled
246 acl: branch access granted: "ef1ea85a6374" on branch "default"
246 acl: branch access granted: "ef1ea85a6374" on branch "default"
247 acl: path access granted: "ef1ea85a6374"
247 acl: path access granted: "ef1ea85a6374"
248 acl: branch access granted: "f9cafe1212c8" on branch "default"
248 acl: branch access granted: "f9cafe1212c8" on branch "default"
249 acl: path access granted: "f9cafe1212c8"
249 acl: path access granted: "f9cafe1212c8"
250 acl: branch access granted: "911600dab2ae" on branch "default"
250 acl: branch access granted: "911600dab2ae" on branch "default"
251 acl: path access granted: "911600dab2ae"
251 acl: path access granted: "911600dab2ae"
252 updating the branch cache
252 updating the branch cache
253 listing keys for "phases"
253 listing keys for "phases"
254 try to push obsolete markers to remote
254 try to push obsolete markers to remote
255 checking for updated bookmarks
255 checking for updated bookmarks
256 listing keys for "bookmarks"
256 listing keys for "bookmarks"
257 repository tip rolled back to revision 0 (undo push)
257 repository tip rolled back to revision 0 (undo push)
258 0:6675d58eff77
258 0:6675d58eff77
259
259
260
260
261 Empty [acl.allow]
261 Empty [acl.allow]
262
262
263 $ echo '[acl.allow]' >> $config
263 $ echo '[acl.allow]' >> $config
264 $ do_push fred
264 $ do_push fred
265 Pushing as user fred
265 Pushing as user fred
266 hgrc = """
266 hgrc = """
267 [hooks]
267 [hooks]
268 pretxnchangegroup.acl = python:hgext.acl.hook
268 pretxnchangegroup.acl = python:hgext.acl.hook
269 [acl]
269 [acl]
270 sources = push
270 sources = push
271 [acl.allow]
271 [acl.allow]
272 """
272 """
273 pushing to ../b
273 pushing to ../b
274 query 1; heads
274 query 1; heads
275 searching for changes
275 searching for changes
276 all remote heads known locally
276 all remote heads known locally
277 invalid branchheads cache (served): tip differs
277 invalid branchheads cache (served): tip differs
278 listing keys for "bookmarks"
278 listing keys for "bookmarks"
279 3 changesets found
279 3 changesets found
280 list of changesets:
280 list of changesets:
281 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
281 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
282 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
282 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
283 911600dab2ae7a9baff75958b84fe606851ce955
283 911600dab2ae7a9baff75958b84fe606851ce955
284 adding changesets
285 bundling: 1/3 changesets (33.33%)
284 bundling: 1/3 changesets (33.33%)
286 bundling: 2/3 changesets (66.67%)
285 bundling: 2/3 changesets (66.67%)
287 bundling: 3/3 changesets (100.00%)
286 bundling: 3/3 changesets (100.00%)
288 bundling: 1/3 manifests (33.33%)
287 bundling: 1/3 manifests (33.33%)
289 bundling: 2/3 manifests (66.67%)
288 bundling: 2/3 manifests (66.67%)
290 bundling: 3/3 manifests (100.00%)
289 bundling: 3/3 manifests (100.00%)
291 bundling: foo/Bar/file.txt 1/3 files (33.33%)
290 bundling: foo/Bar/file.txt 1/3 files (33.33%)
292 bundling: foo/file.txt 2/3 files (66.67%)
291 bundling: foo/file.txt 2/3 files (66.67%)
293 bundling: quux/file.py 3/3 files (100.00%)
292 bundling: quux/file.py 3/3 files (100.00%)
293 adding changesets
294 changesets: 1 chunks
294 changesets: 1 chunks
295 add changeset ef1ea85a6374
295 add changeset ef1ea85a6374
296 changesets: 2 chunks
296 changesets: 2 chunks
297 add changeset f9cafe1212c8
297 add changeset f9cafe1212c8
298 changesets: 3 chunks
298 changesets: 3 chunks
299 add changeset 911600dab2ae
299 add changeset 911600dab2ae
300 adding manifests
300 adding manifests
301 manifests: 1/3 chunks (33.33%)
301 manifests: 1/3 chunks (33.33%)
302 manifests: 2/3 chunks (66.67%)
302 manifests: 2/3 chunks (66.67%)
303 manifests: 3/3 chunks (100.00%)
303 manifests: 3/3 chunks (100.00%)
304 adding file changes
304 adding file changes
305 adding foo/Bar/file.txt revisions
305 adding foo/Bar/file.txt revisions
306 files: 1/3 chunks (33.33%)
306 files: 1/3 chunks (33.33%)
307 adding foo/file.txt revisions
307 adding foo/file.txt revisions
308 files: 2/3 chunks (66.67%)
308 files: 2/3 chunks (66.67%)
309 adding quux/file.py revisions
309 adding quux/file.py revisions
310 files: 3/3 chunks (100.00%)
310 files: 3/3 chunks (100.00%)
311 added 3 changesets with 3 changes to 3 files
311 added 3 changesets with 3 changes to 3 files
312 calling hook pretxnchangegroup.acl: hgext.acl.hook
312 calling hook pretxnchangegroup.acl: hgext.acl.hook
313 acl: checking access for user "fred"
313 acl: checking access for user "fred"
314 acl: acl.allow.branches not enabled
314 acl: acl.allow.branches not enabled
315 acl: acl.deny.branches not enabled
315 acl: acl.deny.branches not enabled
316 acl: acl.allow enabled, 0 entries for user fred
316 acl: acl.allow enabled, 0 entries for user fred
317 acl: acl.deny not enabled
317 acl: acl.deny not enabled
318 acl: branch access granted: "ef1ea85a6374" on branch "default"
318 acl: branch access granted: "ef1ea85a6374" on branch "default"
319 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
319 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
320 transaction abort!
320 transaction abort!
321 rollback completed
321 rollback completed
322 abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
322 abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
323 no rollback information available
323 no rollback information available
324 0:6675d58eff77
324 0:6675d58eff77
325
325
326
326
327 fred is allowed inside foo/
327 fred is allowed inside foo/
328
328
329 $ echo 'foo/** = fred' >> $config
329 $ echo 'foo/** = fred' >> $config
330 $ do_push fred
330 $ do_push fred
331 Pushing as user fred
331 Pushing as user fred
332 hgrc = """
332 hgrc = """
333 [hooks]
333 [hooks]
334 pretxnchangegroup.acl = python:hgext.acl.hook
334 pretxnchangegroup.acl = python:hgext.acl.hook
335 [acl]
335 [acl]
336 sources = push
336 sources = push
337 [acl.allow]
337 [acl.allow]
338 foo/** = fred
338 foo/** = fred
339 """
339 """
340 pushing to ../b
340 pushing to ../b
341 query 1; heads
341 query 1; heads
342 searching for changes
342 searching for changes
343 all remote heads known locally
343 all remote heads known locally
344 invalid branchheads cache (served): tip differs
344 invalid branchheads cache (served): tip differs
345 listing keys for "bookmarks"
345 listing keys for "bookmarks"
346 3 changesets found
346 3 changesets found
347 list of changesets:
347 list of changesets:
348 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
348 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
349 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
349 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
350 911600dab2ae7a9baff75958b84fe606851ce955
350 911600dab2ae7a9baff75958b84fe606851ce955
351 adding changesets
352 bundling: 1/3 changesets (33.33%)
351 bundling: 1/3 changesets (33.33%)
353 bundling: 2/3 changesets (66.67%)
352 bundling: 2/3 changesets (66.67%)
354 bundling: 3/3 changesets (100.00%)
353 bundling: 3/3 changesets (100.00%)
355 bundling: 1/3 manifests (33.33%)
354 bundling: 1/3 manifests (33.33%)
356 bundling: 2/3 manifests (66.67%)
355 bundling: 2/3 manifests (66.67%)
357 bundling: 3/3 manifests (100.00%)
356 bundling: 3/3 manifests (100.00%)
358 bundling: foo/Bar/file.txt 1/3 files (33.33%)
357 bundling: foo/Bar/file.txt 1/3 files (33.33%)
359 bundling: foo/file.txt 2/3 files (66.67%)
358 bundling: foo/file.txt 2/3 files (66.67%)
360 bundling: quux/file.py 3/3 files (100.00%)
359 bundling: quux/file.py 3/3 files (100.00%)
360 adding changesets
361 changesets: 1 chunks
361 changesets: 1 chunks
362 add changeset ef1ea85a6374
362 add changeset ef1ea85a6374
363 changesets: 2 chunks
363 changesets: 2 chunks
364 add changeset f9cafe1212c8
364 add changeset f9cafe1212c8
365 changesets: 3 chunks
365 changesets: 3 chunks
366 add changeset 911600dab2ae
366 add changeset 911600dab2ae
367 adding manifests
367 adding manifests
368 manifests: 1/3 chunks (33.33%)
368 manifests: 1/3 chunks (33.33%)
369 manifests: 2/3 chunks (66.67%)
369 manifests: 2/3 chunks (66.67%)
370 manifests: 3/3 chunks (100.00%)
370 manifests: 3/3 chunks (100.00%)
371 adding file changes
371 adding file changes
372 adding foo/Bar/file.txt revisions
372 adding foo/Bar/file.txt revisions
373 files: 1/3 chunks (33.33%)
373 files: 1/3 chunks (33.33%)
374 adding foo/file.txt revisions
374 adding foo/file.txt revisions
375 files: 2/3 chunks (66.67%)
375 files: 2/3 chunks (66.67%)
376 adding quux/file.py revisions
376 adding quux/file.py revisions
377 files: 3/3 chunks (100.00%)
377 files: 3/3 chunks (100.00%)
378 added 3 changesets with 3 changes to 3 files
378 added 3 changesets with 3 changes to 3 files
379 calling hook pretxnchangegroup.acl: hgext.acl.hook
379 calling hook pretxnchangegroup.acl: hgext.acl.hook
380 acl: checking access for user "fred"
380 acl: checking access for user "fred"
381 acl: acl.allow.branches not enabled
381 acl: acl.allow.branches not enabled
382 acl: acl.deny.branches not enabled
382 acl: acl.deny.branches not enabled
383 acl: acl.allow enabled, 1 entries for user fred
383 acl: acl.allow enabled, 1 entries for user fred
384 acl: acl.deny not enabled
384 acl: acl.deny not enabled
385 acl: branch access granted: "ef1ea85a6374" on branch "default"
385 acl: branch access granted: "ef1ea85a6374" on branch "default"
386 acl: path access granted: "ef1ea85a6374"
386 acl: path access granted: "ef1ea85a6374"
387 acl: branch access granted: "f9cafe1212c8" on branch "default"
387 acl: branch access granted: "f9cafe1212c8" on branch "default"
388 acl: path access granted: "f9cafe1212c8"
388 acl: path access granted: "f9cafe1212c8"
389 acl: branch access granted: "911600dab2ae" on branch "default"
389 acl: branch access granted: "911600dab2ae" on branch "default"
390 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
390 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
391 transaction abort!
391 transaction abort!
392 rollback completed
392 rollback completed
393 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
393 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
394 no rollback information available
394 no rollback information available
395 0:6675d58eff77
395 0:6675d58eff77
396
396
397
397
398 Empty [acl.deny]
398 Empty [acl.deny]
399
399
400 $ echo '[acl.deny]' >> $config
400 $ echo '[acl.deny]' >> $config
401 $ do_push barney
401 $ do_push barney
402 Pushing as user barney
402 Pushing as user barney
403 hgrc = """
403 hgrc = """
404 [hooks]
404 [hooks]
405 pretxnchangegroup.acl = python:hgext.acl.hook
405 pretxnchangegroup.acl = python:hgext.acl.hook
406 [acl]
406 [acl]
407 sources = push
407 sources = push
408 [acl.allow]
408 [acl.allow]
409 foo/** = fred
409 foo/** = fred
410 [acl.deny]
410 [acl.deny]
411 """
411 """
412 pushing to ../b
412 pushing to ../b
413 query 1; heads
413 query 1; heads
414 searching for changes
414 searching for changes
415 all remote heads known locally
415 all remote heads known locally
416 invalid branchheads cache (served): tip differs
416 invalid branchheads cache (served): tip differs
417 listing keys for "bookmarks"
417 listing keys for "bookmarks"
418 3 changesets found
418 3 changesets found
419 list of changesets:
419 list of changesets:
420 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
420 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
421 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
421 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
422 911600dab2ae7a9baff75958b84fe606851ce955
422 911600dab2ae7a9baff75958b84fe606851ce955
423 adding changesets
424 bundling: 1/3 changesets (33.33%)
423 bundling: 1/3 changesets (33.33%)
425 bundling: 2/3 changesets (66.67%)
424 bundling: 2/3 changesets (66.67%)
426 bundling: 3/3 changesets (100.00%)
425 bundling: 3/3 changesets (100.00%)
427 bundling: 1/3 manifests (33.33%)
426 bundling: 1/3 manifests (33.33%)
428 bundling: 2/3 manifests (66.67%)
427 bundling: 2/3 manifests (66.67%)
429 bundling: 3/3 manifests (100.00%)
428 bundling: 3/3 manifests (100.00%)
430 bundling: foo/Bar/file.txt 1/3 files (33.33%)
429 bundling: foo/Bar/file.txt 1/3 files (33.33%)
431 bundling: foo/file.txt 2/3 files (66.67%)
430 bundling: foo/file.txt 2/3 files (66.67%)
432 bundling: quux/file.py 3/3 files (100.00%)
431 bundling: quux/file.py 3/3 files (100.00%)
432 adding changesets
433 changesets: 1 chunks
433 changesets: 1 chunks
434 add changeset ef1ea85a6374
434 add changeset ef1ea85a6374
435 changesets: 2 chunks
435 changesets: 2 chunks
436 add changeset f9cafe1212c8
436 add changeset f9cafe1212c8
437 changesets: 3 chunks
437 changesets: 3 chunks
438 add changeset 911600dab2ae
438 add changeset 911600dab2ae
439 adding manifests
439 adding manifests
440 manifests: 1/3 chunks (33.33%)
440 manifests: 1/3 chunks (33.33%)
441 manifests: 2/3 chunks (66.67%)
441 manifests: 2/3 chunks (66.67%)
442 manifests: 3/3 chunks (100.00%)
442 manifests: 3/3 chunks (100.00%)
443 adding file changes
443 adding file changes
444 adding foo/Bar/file.txt revisions
444 adding foo/Bar/file.txt revisions
445 files: 1/3 chunks (33.33%)
445 files: 1/3 chunks (33.33%)
446 adding foo/file.txt revisions
446 adding foo/file.txt revisions
447 files: 2/3 chunks (66.67%)
447 files: 2/3 chunks (66.67%)
448 adding quux/file.py revisions
448 adding quux/file.py revisions
449 files: 3/3 chunks (100.00%)
449 files: 3/3 chunks (100.00%)
450 added 3 changesets with 3 changes to 3 files
450 added 3 changesets with 3 changes to 3 files
451 calling hook pretxnchangegroup.acl: hgext.acl.hook
451 calling hook pretxnchangegroup.acl: hgext.acl.hook
452 acl: checking access for user "barney"
452 acl: checking access for user "barney"
453 acl: acl.allow.branches not enabled
453 acl: acl.allow.branches not enabled
454 acl: acl.deny.branches not enabled
454 acl: acl.deny.branches not enabled
455 acl: acl.allow enabled, 0 entries for user barney
455 acl: acl.allow enabled, 0 entries for user barney
456 acl: acl.deny enabled, 0 entries for user barney
456 acl: acl.deny enabled, 0 entries for user barney
457 acl: branch access granted: "ef1ea85a6374" on branch "default"
457 acl: branch access granted: "ef1ea85a6374" on branch "default"
458 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
458 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
459 transaction abort!
459 transaction abort!
460 rollback completed
460 rollback completed
461 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
461 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
462 no rollback information available
462 no rollback information available
463 0:6675d58eff77
463 0:6675d58eff77
464
464
465
465
466 fred is allowed inside foo/, but not foo/bar/ (case matters)
466 fred is allowed inside foo/, but not foo/bar/ (case matters)
467
467
468 $ echo 'foo/bar/** = fred' >> $config
468 $ echo 'foo/bar/** = fred' >> $config
469 $ do_push fred
469 $ do_push fred
470 Pushing as user fred
470 Pushing as user fred
471 hgrc = """
471 hgrc = """
472 [hooks]
472 [hooks]
473 pretxnchangegroup.acl = python:hgext.acl.hook
473 pretxnchangegroup.acl = python:hgext.acl.hook
474 [acl]
474 [acl]
475 sources = push
475 sources = push
476 [acl.allow]
476 [acl.allow]
477 foo/** = fred
477 foo/** = fred
478 [acl.deny]
478 [acl.deny]
479 foo/bar/** = fred
479 foo/bar/** = fred
480 """
480 """
481 pushing to ../b
481 pushing to ../b
482 query 1; heads
482 query 1; heads
483 searching for changes
483 searching for changes
484 all remote heads known locally
484 all remote heads known locally
485 invalid branchheads cache (served): tip differs
485 invalid branchheads cache (served): tip differs
486 listing keys for "bookmarks"
486 listing keys for "bookmarks"
487 3 changesets found
487 3 changesets found
488 list of changesets:
488 list of changesets:
489 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
489 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
490 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
490 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
491 911600dab2ae7a9baff75958b84fe606851ce955
491 911600dab2ae7a9baff75958b84fe606851ce955
492 adding changesets
493 bundling: 1/3 changesets (33.33%)
492 bundling: 1/3 changesets (33.33%)
494 bundling: 2/3 changesets (66.67%)
493 bundling: 2/3 changesets (66.67%)
495 bundling: 3/3 changesets (100.00%)
494 bundling: 3/3 changesets (100.00%)
496 bundling: 1/3 manifests (33.33%)
495 bundling: 1/3 manifests (33.33%)
497 bundling: 2/3 manifests (66.67%)
496 bundling: 2/3 manifests (66.67%)
498 bundling: 3/3 manifests (100.00%)
497 bundling: 3/3 manifests (100.00%)
499 bundling: foo/Bar/file.txt 1/3 files (33.33%)
498 bundling: foo/Bar/file.txt 1/3 files (33.33%)
500 bundling: foo/file.txt 2/3 files (66.67%)
499 bundling: foo/file.txt 2/3 files (66.67%)
501 bundling: quux/file.py 3/3 files (100.00%)
500 bundling: quux/file.py 3/3 files (100.00%)
501 adding changesets
502 changesets: 1 chunks
502 changesets: 1 chunks
503 add changeset ef1ea85a6374
503 add changeset ef1ea85a6374
504 changesets: 2 chunks
504 changesets: 2 chunks
505 add changeset f9cafe1212c8
505 add changeset f9cafe1212c8
506 changesets: 3 chunks
506 changesets: 3 chunks
507 add changeset 911600dab2ae
507 add changeset 911600dab2ae
508 adding manifests
508 adding manifests
509 manifests: 1/3 chunks (33.33%)
509 manifests: 1/3 chunks (33.33%)
510 manifests: 2/3 chunks (66.67%)
510 manifests: 2/3 chunks (66.67%)
511 manifests: 3/3 chunks (100.00%)
511 manifests: 3/3 chunks (100.00%)
512 adding file changes
512 adding file changes
513 adding foo/Bar/file.txt revisions
513 adding foo/Bar/file.txt revisions
514 files: 1/3 chunks (33.33%)
514 files: 1/3 chunks (33.33%)
515 adding foo/file.txt revisions
515 adding foo/file.txt revisions
516 files: 2/3 chunks (66.67%)
516 files: 2/3 chunks (66.67%)
517 adding quux/file.py revisions
517 adding quux/file.py revisions
518 files: 3/3 chunks (100.00%)
518 files: 3/3 chunks (100.00%)
519 added 3 changesets with 3 changes to 3 files
519 added 3 changesets with 3 changes to 3 files
520 calling hook pretxnchangegroup.acl: hgext.acl.hook
520 calling hook pretxnchangegroup.acl: hgext.acl.hook
521 acl: checking access for user "fred"
521 acl: checking access for user "fred"
522 acl: acl.allow.branches not enabled
522 acl: acl.allow.branches not enabled
523 acl: acl.deny.branches not enabled
523 acl: acl.deny.branches not enabled
524 acl: acl.allow enabled, 1 entries for user fred
524 acl: acl.allow enabled, 1 entries for user fred
525 acl: acl.deny enabled, 1 entries for user fred
525 acl: acl.deny enabled, 1 entries for user fred
526 acl: branch access granted: "ef1ea85a6374" on branch "default"
526 acl: branch access granted: "ef1ea85a6374" on branch "default"
527 acl: path access granted: "ef1ea85a6374"
527 acl: path access granted: "ef1ea85a6374"
528 acl: branch access granted: "f9cafe1212c8" on branch "default"
528 acl: branch access granted: "f9cafe1212c8" on branch "default"
529 acl: path access granted: "f9cafe1212c8"
529 acl: path access granted: "f9cafe1212c8"
530 acl: branch access granted: "911600dab2ae" on branch "default"
530 acl: branch access granted: "911600dab2ae" on branch "default"
531 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
531 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
532 transaction abort!
532 transaction abort!
533 rollback completed
533 rollback completed
534 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
534 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
535 no rollback information available
535 no rollback information available
536 0:6675d58eff77
536 0:6675d58eff77
537
537
538
538
539 fred is allowed inside foo/, but not foo/Bar/
539 fred is allowed inside foo/, but not foo/Bar/
540
540
541 $ echo 'foo/Bar/** = fred' >> $config
541 $ echo 'foo/Bar/** = fred' >> $config
542 $ do_push fred
542 $ do_push fred
543 Pushing as user fred
543 Pushing as user fred
544 hgrc = """
544 hgrc = """
545 [hooks]
545 [hooks]
546 pretxnchangegroup.acl = python:hgext.acl.hook
546 pretxnchangegroup.acl = python:hgext.acl.hook
547 [acl]
547 [acl]
548 sources = push
548 sources = push
549 [acl.allow]
549 [acl.allow]
550 foo/** = fred
550 foo/** = fred
551 [acl.deny]
551 [acl.deny]
552 foo/bar/** = fred
552 foo/bar/** = fred
553 foo/Bar/** = fred
553 foo/Bar/** = fred
554 """
554 """
555 pushing to ../b
555 pushing to ../b
556 query 1; heads
556 query 1; heads
557 searching for changes
557 searching for changes
558 all remote heads known locally
558 all remote heads known locally
559 invalid branchheads cache (served): tip differs
559 invalid branchheads cache (served): tip differs
560 listing keys for "bookmarks"
560 listing keys for "bookmarks"
561 3 changesets found
561 3 changesets found
562 list of changesets:
562 list of changesets:
563 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
563 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
564 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
564 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
565 911600dab2ae7a9baff75958b84fe606851ce955
565 911600dab2ae7a9baff75958b84fe606851ce955
566 adding changesets
567 bundling: 1/3 changesets (33.33%)
566 bundling: 1/3 changesets (33.33%)
568 bundling: 2/3 changesets (66.67%)
567 bundling: 2/3 changesets (66.67%)
569 bundling: 3/3 changesets (100.00%)
568 bundling: 3/3 changesets (100.00%)
570 bundling: 1/3 manifests (33.33%)
569 bundling: 1/3 manifests (33.33%)
571 bundling: 2/3 manifests (66.67%)
570 bundling: 2/3 manifests (66.67%)
572 bundling: 3/3 manifests (100.00%)
571 bundling: 3/3 manifests (100.00%)
573 bundling: foo/Bar/file.txt 1/3 files (33.33%)
572 bundling: foo/Bar/file.txt 1/3 files (33.33%)
574 bundling: foo/file.txt 2/3 files (66.67%)
573 bundling: foo/file.txt 2/3 files (66.67%)
575 bundling: quux/file.py 3/3 files (100.00%)
574 bundling: quux/file.py 3/3 files (100.00%)
575 adding changesets
576 changesets: 1 chunks
576 changesets: 1 chunks
577 add changeset ef1ea85a6374
577 add changeset ef1ea85a6374
578 changesets: 2 chunks
578 changesets: 2 chunks
579 add changeset f9cafe1212c8
579 add changeset f9cafe1212c8
580 changesets: 3 chunks
580 changesets: 3 chunks
581 add changeset 911600dab2ae
581 add changeset 911600dab2ae
582 adding manifests
582 adding manifests
583 manifests: 1/3 chunks (33.33%)
583 manifests: 1/3 chunks (33.33%)
584 manifests: 2/3 chunks (66.67%)
584 manifests: 2/3 chunks (66.67%)
585 manifests: 3/3 chunks (100.00%)
585 manifests: 3/3 chunks (100.00%)
586 adding file changes
586 adding file changes
587 adding foo/Bar/file.txt revisions
587 adding foo/Bar/file.txt revisions
588 files: 1/3 chunks (33.33%)
588 files: 1/3 chunks (33.33%)
589 adding foo/file.txt revisions
589 adding foo/file.txt revisions
590 files: 2/3 chunks (66.67%)
590 files: 2/3 chunks (66.67%)
591 adding quux/file.py revisions
591 adding quux/file.py revisions
592 files: 3/3 chunks (100.00%)
592 files: 3/3 chunks (100.00%)
593 added 3 changesets with 3 changes to 3 files
593 added 3 changesets with 3 changes to 3 files
594 calling hook pretxnchangegroup.acl: hgext.acl.hook
594 calling hook pretxnchangegroup.acl: hgext.acl.hook
595 acl: checking access for user "fred"
595 acl: checking access for user "fred"
596 acl: acl.allow.branches not enabled
596 acl: acl.allow.branches not enabled
597 acl: acl.deny.branches not enabled
597 acl: acl.deny.branches not enabled
598 acl: acl.allow enabled, 1 entries for user fred
598 acl: acl.allow enabled, 1 entries for user fred
599 acl: acl.deny enabled, 2 entries for user fred
599 acl: acl.deny enabled, 2 entries for user fred
600 acl: branch access granted: "ef1ea85a6374" on branch "default"
600 acl: branch access granted: "ef1ea85a6374" on branch "default"
601 acl: path access granted: "ef1ea85a6374"
601 acl: path access granted: "ef1ea85a6374"
602 acl: branch access granted: "f9cafe1212c8" on branch "default"
602 acl: branch access granted: "f9cafe1212c8" on branch "default"
603 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
603 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
604 transaction abort!
604 transaction abort!
605 rollback completed
605 rollback completed
606 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
606 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
607 no rollback information available
607 no rollback information available
608 0:6675d58eff77
608 0:6675d58eff77
609
609
610
610
611 $ echo 'barney is not mentioned => not allowed anywhere'
611 $ echo 'barney is not mentioned => not allowed anywhere'
612 barney is not mentioned => not allowed anywhere
612 barney is not mentioned => not allowed anywhere
613 $ do_push barney
613 $ do_push barney
614 Pushing as user barney
614 Pushing as user barney
615 hgrc = """
615 hgrc = """
616 [hooks]
616 [hooks]
617 pretxnchangegroup.acl = python:hgext.acl.hook
617 pretxnchangegroup.acl = python:hgext.acl.hook
618 [acl]
618 [acl]
619 sources = push
619 sources = push
620 [acl.allow]
620 [acl.allow]
621 foo/** = fred
621 foo/** = fred
622 [acl.deny]
622 [acl.deny]
623 foo/bar/** = fred
623 foo/bar/** = fred
624 foo/Bar/** = fred
624 foo/Bar/** = fred
625 """
625 """
626 pushing to ../b
626 pushing to ../b
627 query 1; heads
627 query 1; heads
628 searching for changes
628 searching for changes
629 all remote heads known locally
629 all remote heads known locally
630 invalid branchheads cache (served): tip differs
630 invalid branchheads cache (served): tip differs
631 listing keys for "bookmarks"
631 listing keys for "bookmarks"
632 3 changesets found
632 3 changesets found
633 list of changesets:
633 list of changesets:
634 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
634 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
635 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
635 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
636 911600dab2ae7a9baff75958b84fe606851ce955
636 911600dab2ae7a9baff75958b84fe606851ce955
637 adding changesets
638 bundling: 1/3 changesets (33.33%)
637 bundling: 1/3 changesets (33.33%)
639 bundling: 2/3 changesets (66.67%)
638 bundling: 2/3 changesets (66.67%)
640 bundling: 3/3 changesets (100.00%)
639 bundling: 3/3 changesets (100.00%)
641 bundling: 1/3 manifests (33.33%)
640 bundling: 1/3 manifests (33.33%)
642 bundling: 2/3 manifests (66.67%)
641 bundling: 2/3 manifests (66.67%)
643 bundling: 3/3 manifests (100.00%)
642 bundling: 3/3 manifests (100.00%)
644 bundling: foo/Bar/file.txt 1/3 files (33.33%)
643 bundling: foo/Bar/file.txt 1/3 files (33.33%)
645 bundling: foo/file.txt 2/3 files (66.67%)
644 bundling: foo/file.txt 2/3 files (66.67%)
646 bundling: quux/file.py 3/3 files (100.00%)
645 bundling: quux/file.py 3/3 files (100.00%)
646 adding changesets
647 changesets: 1 chunks
647 changesets: 1 chunks
648 add changeset ef1ea85a6374
648 add changeset ef1ea85a6374
649 changesets: 2 chunks
649 changesets: 2 chunks
650 add changeset f9cafe1212c8
650 add changeset f9cafe1212c8
651 changesets: 3 chunks
651 changesets: 3 chunks
652 add changeset 911600dab2ae
652 add changeset 911600dab2ae
653 adding manifests
653 adding manifests
654 manifests: 1/3 chunks (33.33%)
654 manifests: 1/3 chunks (33.33%)
655 manifests: 2/3 chunks (66.67%)
655 manifests: 2/3 chunks (66.67%)
656 manifests: 3/3 chunks (100.00%)
656 manifests: 3/3 chunks (100.00%)
657 adding file changes
657 adding file changes
658 adding foo/Bar/file.txt revisions
658 adding foo/Bar/file.txt revisions
659 files: 1/3 chunks (33.33%)
659 files: 1/3 chunks (33.33%)
660 adding foo/file.txt revisions
660 adding foo/file.txt revisions
661 files: 2/3 chunks (66.67%)
661 files: 2/3 chunks (66.67%)
662 adding quux/file.py revisions
662 adding quux/file.py revisions
663 files: 3/3 chunks (100.00%)
663 files: 3/3 chunks (100.00%)
664 added 3 changesets with 3 changes to 3 files
664 added 3 changesets with 3 changes to 3 files
665 calling hook pretxnchangegroup.acl: hgext.acl.hook
665 calling hook pretxnchangegroup.acl: hgext.acl.hook
666 acl: checking access for user "barney"
666 acl: checking access for user "barney"
667 acl: acl.allow.branches not enabled
667 acl: acl.allow.branches not enabled
668 acl: acl.deny.branches not enabled
668 acl: acl.deny.branches not enabled
669 acl: acl.allow enabled, 0 entries for user barney
669 acl: acl.allow enabled, 0 entries for user barney
670 acl: acl.deny enabled, 0 entries for user barney
670 acl: acl.deny enabled, 0 entries for user barney
671 acl: branch access granted: "ef1ea85a6374" on branch "default"
671 acl: branch access granted: "ef1ea85a6374" on branch "default"
672 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
672 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
673 transaction abort!
673 transaction abort!
674 rollback completed
674 rollback completed
675 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
675 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
676 no rollback information available
676 no rollback information available
677 0:6675d58eff77
677 0:6675d58eff77
678
678
679
679
680 barney is allowed everywhere
680 barney is allowed everywhere
681
681
682 $ echo '[acl.allow]' >> $config
682 $ echo '[acl.allow]' >> $config
683 $ echo '** = barney' >> $config
683 $ echo '** = barney' >> $config
684 $ do_push barney
684 $ do_push barney
685 Pushing as user barney
685 Pushing as user barney
686 hgrc = """
686 hgrc = """
687 [hooks]
687 [hooks]
688 pretxnchangegroup.acl = python:hgext.acl.hook
688 pretxnchangegroup.acl = python:hgext.acl.hook
689 [acl]
689 [acl]
690 sources = push
690 sources = push
691 [acl.allow]
691 [acl.allow]
692 foo/** = fred
692 foo/** = fred
693 [acl.deny]
693 [acl.deny]
694 foo/bar/** = fred
694 foo/bar/** = fred
695 foo/Bar/** = fred
695 foo/Bar/** = fred
696 [acl.allow]
696 [acl.allow]
697 ** = barney
697 ** = barney
698 """
698 """
699 pushing to ../b
699 pushing to ../b
700 query 1; heads
700 query 1; heads
701 searching for changes
701 searching for changes
702 all remote heads known locally
702 all remote heads known locally
703 invalid branchheads cache (served): tip differs
703 invalid branchheads cache (served): tip differs
704 listing keys for "bookmarks"
704 listing keys for "bookmarks"
705 3 changesets found
705 3 changesets found
706 list of changesets:
706 list of changesets:
707 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
707 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
708 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
708 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
709 911600dab2ae7a9baff75958b84fe606851ce955
709 911600dab2ae7a9baff75958b84fe606851ce955
710 adding changesets
711 bundling: 1/3 changesets (33.33%)
710 bundling: 1/3 changesets (33.33%)
712 bundling: 2/3 changesets (66.67%)
711 bundling: 2/3 changesets (66.67%)
713 bundling: 3/3 changesets (100.00%)
712 bundling: 3/3 changesets (100.00%)
714 bundling: 1/3 manifests (33.33%)
713 bundling: 1/3 manifests (33.33%)
715 bundling: 2/3 manifests (66.67%)
714 bundling: 2/3 manifests (66.67%)
716 bundling: 3/3 manifests (100.00%)
715 bundling: 3/3 manifests (100.00%)
717 bundling: foo/Bar/file.txt 1/3 files (33.33%)
716 bundling: foo/Bar/file.txt 1/3 files (33.33%)
718 bundling: foo/file.txt 2/3 files (66.67%)
717 bundling: foo/file.txt 2/3 files (66.67%)
719 bundling: quux/file.py 3/3 files (100.00%)
718 bundling: quux/file.py 3/3 files (100.00%)
719 adding changesets
720 changesets: 1 chunks
720 changesets: 1 chunks
721 add changeset ef1ea85a6374
721 add changeset ef1ea85a6374
722 changesets: 2 chunks
722 changesets: 2 chunks
723 add changeset f9cafe1212c8
723 add changeset f9cafe1212c8
724 changesets: 3 chunks
724 changesets: 3 chunks
725 add changeset 911600dab2ae
725 add changeset 911600dab2ae
726 adding manifests
726 adding manifests
727 manifests: 1/3 chunks (33.33%)
727 manifests: 1/3 chunks (33.33%)
728 manifests: 2/3 chunks (66.67%)
728 manifests: 2/3 chunks (66.67%)
729 manifests: 3/3 chunks (100.00%)
729 manifests: 3/3 chunks (100.00%)
730 adding file changes
730 adding file changes
731 adding foo/Bar/file.txt revisions
731 adding foo/Bar/file.txt revisions
732 files: 1/3 chunks (33.33%)
732 files: 1/3 chunks (33.33%)
733 adding foo/file.txt revisions
733 adding foo/file.txt revisions
734 files: 2/3 chunks (66.67%)
734 files: 2/3 chunks (66.67%)
735 adding quux/file.py revisions
735 adding quux/file.py revisions
736 files: 3/3 chunks (100.00%)
736 files: 3/3 chunks (100.00%)
737 added 3 changesets with 3 changes to 3 files
737 added 3 changesets with 3 changes to 3 files
738 calling hook pretxnchangegroup.acl: hgext.acl.hook
738 calling hook pretxnchangegroup.acl: hgext.acl.hook
739 acl: checking access for user "barney"
739 acl: checking access for user "barney"
740 acl: acl.allow.branches not enabled
740 acl: acl.allow.branches not enabled
741 acl: acl.deny.branches not enabled
741 acl: acl.deny.branches not enabled
742 acl: acl.allow enabled, 1 entries for user barney
742 acl: acl.allow enabled, 1 entries for user barney
743 acl: acl.deny enabled, 0 entries for user barney
743 acl: acl.deny enabled, 0 entries for user barney
744 acl: branch access granted: "ef1ea85a6374" on branch "default"
744 acl: branch access granted: "ef1ea85a6374" on branch "default"
745 acl: path access granted: "ef1ea85a6374"
745 acl: path access granted: "ef1ea85a6374"
746 acl: branch access granted: "f9cafe1212c8" on branch "default"
746 acl: branch access granted: "f9cafe1212c8" on branch "default"
747 acl: path access granted: "f9cafe1212c8"
747 acl: path access granted: "f9cafe1212c8"
748 acl: branch access granted: "911600dab2ae" on branch "default"
748 acl: branch access granted: "911600dab2ae" on branch "default"
749 acl: path access granted: "911600dab2ae"
749 acl: path access granted: "911600dab2ae"
750 updating the branch cache
750 updating the branch cache
751 listing keys for "phases"
751 listing keys for "phases"
752 try to push obsolete markers to remote
752 try to push obsolete markers to remote
753 checking for updated bookmarks
753 checking for updated bookmarks
754 listing keys for "bookmarks"
754 listing keys for "bookmarks"
755 repository tip rolled back to revision 0 (undo push)
755 repository tip rolled back to revision 0 (undo push)
756 0:6675d58eff77
756 0:6675d58eff77
757
757
758
758
759 wilma can change files with a .txt extension
759 wilma can change files with a .txt extension
760
760
761 $ echo '**/*.txt = wilma' >> $config
761 $ echo '**/*.txt = wilma' >> $config
762 $ do_push wilma
762 $ do_push wilma
763 Pushing as user wilma
763 Pushing as user wilma
764 hgrc = """
764 hgrc = """
765 [hooks]
765 [hooks]
766 pretxnchangegroup.acl = python:hgext.acl.hook
766 pretxnchangegroup.acl = python:hgext.acl.hook
767 [acl]
767 [acl]
768 sources = push
768 sources = push
769 [acl.allow]
769 [acl.allow]
770 foo/** = fred
770 foo/** = fred
771 [acl.deny]
771 [acl.deny]
772 foo/bar/** = fred
772 foo/bar/** = fred
773 foo/Bar/** = fred
773 foo/Bar/** = fred
774 [acl.allow]
774 [acl.allow]
775 ** = barney
775 ** = barney
776 **/*.txt = wilma
776 **/*.txt = wilma
777 """
777 """
778 pushing to ../b
778 pushing to ../b
779 query 1; heads
779 query 1; heads
780 searching for changes
780 searching for changes
781 all remote heads known locally
781 all remote heads known locally
782 invalid branchheads cache (served): tip differs
782 invalid branchheads cache (served): tip differs
783 listing keys for "bookmarks"
783 listing keys for "bookmarks"
784 3 changesets found
784 3 changesets found
785 list of changesets:
785 list of changesets:
786 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
786 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
787 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
787 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
788 911600dab2ae7a9baff75958b84fe606851ce955
788 911600dab2ae7a9baff75958b84fe606851ce955
789 adding changesets
790 bundling: 1/3 changesets (33.33%)
789 bundling: 1/3 changesets (33.33%)
791 bundling: 2/3 changesets (66.67%)
790 bundling: 2/3 changesets (66.67%)
792 bundling: 3/3 changesets (100.00%)
791 bundling: 3/3 changesets (100.00%)
793 bundling: 1/3 manifests (33.33%)
792 bundling: 1/3 manifests (33.33%)
794 bundling: 2/3 manifests (66.67%)
793 bundling: 2/3 manifests (66.67%)
795 bundling: 3/3 manifests (100.00%)
794 bundling: 3/3 manifests (100.00%)
796 bundling: foo/Bar/file.txt 1/3 files (33.33%)
795 bundling: foo/Bar/file.txt 1/3 files (33.33%)
797 bundling: foo/file.txt 2/3 files (66.67%)
796 bundling: foo/file.txt 2/3 files (66.67%)
798 bundling: quux/file.py 3/3 files (100.00%)
797 bundling: quux/file.py 3/3 files (100.00%)
798 adding changesets
799 changesets: 1 chunks
799 changesets: 1 chunks
800 add changeset ef1ea85a6374
800 add changeset ef1ea85a6374
801 changesets: 2 chunks
801 changesets: 2 chunks
802 add changeset f9cafe1212c8
802 add changeset f9cafe1212c8
803 changesets: 3 chunks
803 changesets: 3 chunks
804 add changeset 911600dab2ae
804 add changeset 911600dab2ae
805 adding manifests
805 adding manifests
806 manifests: 1/3 chunks (33.33%)
806 manifests: 1/3 chunks (33.33%)
807 manifests: 2/3 chunks (66.67%)
807 manifests: 2/3 chunks (66.67%)
808 manifests: 3/3 chunks (100.00%)
808 manifests: 3/3 chunks (100.00%)
809 adding file changes
809 adding file changes
810 adding foo/Bar/file.txt revisions
810 adding foo/Bar/file.txt revisions
811 files: 1/3 chunks (33.33%)
811 files: 1/3 chunks (33.33%)
812 adding foo/file.txt revisions
812 adding foo/file.txt revisions
813 files: 2/3 chunks (66.67%)
813 files: 2/3 chunks (66.67%)
814 adding quux/file.py revisions
814 adding quux/file.py revisions
815 files: 3/3 chunks (100.00%)
815 files: 3/3 chunks (100.00%)
816 added 3 changesets with 3 changes to 3 files
816 added 3 changesets with 3 changes to 3 files
817 calling hook pretxnchangegroup.acl: hgext.acl.hook
817 calling hook pretxnchangegroup.acl: hgext.acl.hook
818 acl: checking access for user "wilma"
818 acl: checking access for user "wilma"
819 acl: acl.allow.branches not enabled
819 acl: acl.allow.branches not enabled
820 acl: acl.deny.branches not enabled
820 acl: acl.deny.branches not enabled
821 acl: acl.allow enabled, 1 entries for user wilma
821 acl: acl.allow enabled, 1 entries for user wilma
822 acl: acl.deny enabled, 0 entries for user wilma
822 acl: acl.deny enabled, 0 entries for user wilma
823 acl: branch access granted: "ef1ea85a6374" on branch "default"
823 acl: branch access granted: "ef1ea85a6374" on branch "default"
824 acl: path access granted: "ef1ea85a6374"
824 acl: path access granted: "ef1ea85a6374"
825 acl: branch access granted: "f9cafe1212c8" on branch "default"
825 acl: branch access granted: "f9cafe1212c8" on branch "default"
826 acl: path access granted: "f9cafe1212c8"
826 acl: path access granted: "f9cafe1212c8"
827 acl: branch access granted: "911600dab2ae" on branch "default"
827 acl: branch access granted: "911600dab2ae" on branch "default"
828 error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
828 error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
829 transaction abort!
829 transaction abort!
830 rollback completed
830 rollback completed
831 abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
831 abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
832 no rollback information available
832 no rollback information available
833 0:6675d58eff77
833 0:6675d58eff77
834
834
835
835
836 file specified by acl.config does not exist
836 file specified by acl.config does not exist
837
837
838 $ echo '[acl]' >> $config
838 $ echo '[acl]' >> $config
839 $ echo 'config = ../acl.config' >> $config
839 $ echo 'config = ../acl.config' >> $config
840 $ do_push barney
840 $ do_push barney
841 Pushing as user barney
841 Pushing as user barney
842 hgrc = """
842 hgrc = """
843 [hooks]
843 [hooks]
844 pretxnchangegroup.acl = python:hgext.acl.hook
844 pretxnchangegroup.acl = python:hgext.acl.hook
845 [acl]
845 [acl]
846 sources = push
846 sources = push
847 [acl.allow]
847 [acl.allow]
848 foo/** = fred
848 foo/** = fred
849 [acl.deny]
849 [acl.deny]
850 foo/bar/** = fred
850 foo/bar/** = fred
851 foo/Bar/** = fred
851 foo/Bar/** = fred
852 [acl.allow]
852 [acl.allow]
853 ** = barney
853 ** = barney
854 **/*.txt = wilma
854 **/*.txt = wilma
855 [acl]
855 [acl]
856 config = ../acl.config
856 config = ../acl.config
857 """
857 """
858 pushing to ../b
858 pushing to ../b
859 query 1; heads
859 query 1; heads
860 searching for changes
860 searching for changes
861 all remote heads known locally
861 all remote heads known locally
862 invalid branchheads cache (served): tip differs
862 invalid branchheads cache (served): tip differs
863 listing keys for "bookmarks"
863 listing keys for "bookmarks"
864 3 changesets found
864 3 changesets found
865 list of changesets:
865 list of changesets:
866 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
866 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
867 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
867 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
868 911600dab2ae7a9baff75958b84fe606851ce955
868 911600dab2ae7a9baff75958b84fe606851ce955
869 adding changesets
870 bundling: 1/3 changesets (33.33%)
869 bundling: 1/3 changesets (33.33%)
871 bundling: 2/3 changesets (66.67%)
870 bundling: 2/3 changesets (66.67%)
872 bundling: 3/3 changesets (100.00%)
871 bundling: 3/3 changesets (100.00%)
873 bundling: 1/3 manifests (33.33%)
872 bundling: 1/3 manifests (33.33%)
874 bundling: 2/3 manifests (66.67%)
873 bundling: 2/3 manifests (66.67%)
875 bundling: 3/3 manifests (100.00%)
874 bundling: 3/3 manifests (100.00%)
876 bundling: foo/Bar/file.txt 1/3 files (33.33%)
875 bundling: foo/Bar/file.txt 1/3 files (33.33%)
877 bundling: foo/file.txt 2/3 files (66.67%)
876 bundling: foo/file.txt 2/3 files (66.67%)
878 bundling: quux/file.py 3/3 files (100.00%)
877 bundling: quux/file.py 3/3 files (100.00%)
878 adding changesets
879 changesets: 1 chunks
879 changesets: 1 chunks
880 add changeset ef1ea85a6374
880 add changeset ef1ea85a6374
881 changesets: 2 chunks
881 changesets: 2 chunks
882 add changeset f9cafe1212c8
882 add changeset f9cafe1212c8
883 changesets: 3 chunks
883 changesets: 3 chunks
884 add changeset 911600dab2ae
884 add changeset 911600dab2ae
885 adding manifests
885 adding manifests
886 manifests: 1/3 chunks (33.33%)
886 manifests: 1/3 chunks (33.33%)
887 manifests: 2/3 chunks (66.67%)
887 manifests: 2/3 chunks (66.67%)
888 manifests: 3/3 chunks (100.00%)
888 manifests: 3/3 chunks (100.00%)
889 adding file changes
889 adding file changes
890 adding foo/Bar/file.txt revisions
890 adding foo/Bar/file.txt revisions
891 files: 1/3 chunks (33.33%)
891 files: 1/3 chunks (33.33%)
892 adding foo/file.txt revisions
892 adding foo/file.txt revisions
893 files: 2/3 chunks (66.67%)
893 files: 2/3 chunks (66.67%)
894 adding quux/file.py revisions
894 adding quux/file.py revisions
895 files: 3/3 chunks (100.00%)
895 files: 3/3 chunks (100.00%)
896 added 3 changesets with 3 changes to 3 files
896 added 3 changesets with 3 changes to 3 files
897 calling hook pretxnchangegroup.acl: hgext.acl.hook
897 calling hook pretxnchangegroup.acl: hgext.acl.hook
898 acl: checking access for user "barney"
898 acl: checking access for user "barney"
899 error: pretxnchangegroup.acl hook raised an exception: [Errno *] *: '../acl.config' (glob)
899 error: pretxnchangegroup.acl hook raised an exception: [Errno *] *: '../acl.config' (glob)
900 transaction abort!
900 transaction abort!
901 rollback completed
901 rollback completed
902 abort: *: ../acl.config (glob)
902 abort: *: ../acl.config (glob)
903 no rollback information available
903 no rollback information available
904 0:6675d58eff77
904 0:6675d58eff77
905
905
906
906
907 betty is allowed inside foo/ by a acl.config file
907 betty is allowed inside foo/ by a acl.config file
908
908
909 $ echo '[acl.allow]' >> acl.config
909 $ echo '[acl.allow]' >> acl.config
910 $ echo 'foo/** = betty' >> acl.config
910 $ echo 'foo/** = betty' >> acl.config
911 $ do_push betty
911 $ do_push betty
912 Pushing as user betty
912 Pushing as user betty
913 hgrc = """
913 hgrc = """
914 [hooks]
914 [hooks]
915 pretxnchangegroup.acl = python:hgext.acl.hook
915 pretxnchangegroup.acl = python:hgext.acl.hook
916 [acl]
916 [acl]
917 sources = push
917 sources = push
918 [acl.allow]
918 [acl.allow]
919 foo/** = fred
919 foo/** = fred
920 [acl.deny]
920 [acl.deny]
921 foo/bar/** = fred
921 foo/bar/** = fred
922 foo/Bar/** = fred
922 foo/Bar/** = fred
923 [acl.allow]
923 [acl.allow]
924 ** = barney
924 ** = barney
925 **/*.txt = wilma
925 **/*.txt = wilma
926 [acl]
926 [acl]
927 config = ../acl.config
927 config = ../acl.config
928 """
928 """
929 acl.config = """
929 acl.config = """
930 [acl.allow]
930 [acl.allow]
931 foo/** = betty
931 foo/** = betty
932 """
932 """
933 pushing to ../b
933 pushing to ../b
934 query 1; heads
934 query 1; heads
935 searching for changes
935 searching for changes
936 all remote heads known locally
936 all remote heads known locally
937 invalid branchheads cache (served): tip differs
937 invalid branchheads cache (served): tip differs
938 listing keys for "bookmarks"
938 listing keys for "bookmarks"
939 3 changesets found
939 3 changesets found
940 list of changesets:
940 list of changesets:
941 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
941 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
942 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
942 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
943 911600dab2ae7a9baff75958b84fe606851ce955
943 911600dab2ae7a9baff75958b84fe606851ce955
944 adding changesets
945 bundling: 1/3 changesets (33.33%)
944 bundling: 1/3 changesets (33.33%)
946 bundling: 2/3 changesets (66.67%)
945 bundling: 2/3 changesets (66.67%)
947 bundling: 3/3 changesets (100.00%)
946 bundling: 3/3 changesets (100.00%)
948 bundling: 1/3 manifests (33.33%)
947 bundling: 1/3 manifests (33.33%)
949 bundling: 2/3 manifests (66.67%)
948 bundling: 2/3 manifests (66.67%)
950 bundling: 3/3 manifests (100.00%)
949 bundling: 3/3 manifests (100.00%)
951 bundling: foo/Bar/file.txt 1/3 files (33.33%)
950 bundling: foo/Bar/file.txt 1/3 files (33.33%)
952 bundling: foo/file.txt 2/3 files (66.67%)
951 bundling: foo/file.txt 2/3 files (66.67%)
953 bundling: quux/file.py 3/3 files (100.00%)
952 bundling: quux/file.py 3/3 files (100.00%)
953 adding changesets
954 changesets: 1 chunks
954 changesets: 1 chunks
955 add changeset ef1ea85a6374
955 add changeset ef1ea85a6374
956 changesets: 2 chunks
956 changesets: 2 chunks
957 add changeset f9cafe1212c8
957 add changeset f9cafe1212c8
958 changesets: 3 chunks
958 changesets: 3 chunks
959 add changeset 911600dab2ae
959 add changeset 911600dab2ae
960 adding manifests
960 adding manifests
961 manifests: 1/3 chunks (33.33%)
961 manifests: 1/3 chunks (33.33%)
962 manifests: 2/3 chunks (66.67%)
962 manifests: 2/3 chunks (66.67%)
963 manifests: 3/3 chunks (100.00%)
963 manifests: 3/3 chunks (100.00%)
964 adding file changes
964 adding file changes
965 adding foo/Bar/file.txt revisions
965 adding foo/Bar/file.txt revisions
966 files: 1/3 chunks (33.33%)
966 files: 1/3 chunks (33.33%)
967 adding foo/file.txt revisions
967 adding foo/file.txt revisions
968 files: 2/3 chunks (66.67%)
968 files: 2/3 chunks (66.67%)
969 adding quux/file.py revisions
969 adding quux/file.py revisions
970 files: 3/3 chunks (100.00%)
970 files: 3/3 chunks (100.00%)
971 added 3 changesets with 3 changes to 3 files
971 added 3 changesets with 3 changes to 3 files
972 calling hook pretxnchangegroup.acl: hgext.acl.hook
972 calling hook pretxnchangegroup.acl: hgext.acl.hook
973 acl: checking access for user "betty"
973 acl: checking access for user "betty"
974 acl: acl.allow.branches not enabled
974 acl: acl.allow.branches not enabled
975 acl: acl.deny.branches not enabled
975 acl: acl.deny.branches not enabled
976 acl: acl.allow enabled, 1 entries for user betty
976 acl: acl.allow enabled, 1 entries for user betty
977 acl: acl.deny enabled, 0 entries for user betty
977 acl: acl.deny enabled, 0 entries for user betty
978 acl: branch access granted: "ef1ea85a6374" on branch "default"
978 acl: branch access granted: "ef1ea85a6374" on branch "default"
979 acl: path access granted: "ef1ea85a6374"
979 acl: path access granted: "ef1ea85a6374"
980 acl: branch access granted: "f9cafe1212c8" on branch "default"
980 acl: branch access granted: "f9cafe1212c8" on branch "default"
981 acl: path access granted: "f9cafe1212c8"
981 acl: path access granted: "f9cafe1212c8"
982 acl: branch access granted: "911600dab2ae" on branch "default"
982 acl: branch access granted: "911600dab2ae" on branch "default"
983 error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
983 error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
984 transaction abort!
984 transaction abort!
985 rollback completed
985 rollback completed
986 abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
986 abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
987 no rollback information available
987 no rollback information available
988 0:6675d58eff77
988 0:6675d58eff77
989
989
990
990
991 acl.config can set only [acl.allow]/[acl.deny]
991 acl.config can set only [acl.allow]/[acl.deny]
992
992
993 $ echo '[hooks]' >> acl.config
993 $ echo '[hooks]' >> acl.config
994 $ echo 'changegroup.acl = false' >> acl.config
994 $ echo 'changegroup.acl = false' >> acl.config
995 $ do_push barney
995 $ do_push barney
996 Pushing as user barney
996 Pushing as user barney
997 hgrc = """
997 hgrc = """
998 [hooks]
998 [hooks]
999 pretxnchangegroup.acl = python:hgext.acl.hook
999 pretxnchangegroup.acl = python:hgext.acl.hook
1000 [acl]
1000 [acl]
1001 sources = push
1001 sources = push
1002 [acl.allow]
1002 [acl.allow]
1003 foo/** = fred
1003 foo/** = fred
1004 [acl.deny]
1004 [acl.deny]
1005 foo/bar/** = fred
1005 foo/bar/** = fred
1006 foo/Bar/** = fred
1006 foo/Bar/** = fred
1007 [acl.allow]
1007 [acl.allow]
1008 ** = barney
1008 ** = barney
1009 **/*.txt = wilma
1009 **/*.txt = wilma
1010 [acl]
1010 [acl]
1011 config = ../acl.config
1011 config = ../acl.config
1012 """
1012 """
1013 acl.config = """
1013 acl.config = """
1014 [acl.allow]
1014 [acl.allow]
1015 foo/** = betty
1015 foo/** = betty
1016 [hooks]
1016 [hooks]
1017 changegroup.acl = false
1017 changegroup.acl = false
1018 """
1018 """
1019 pushing to ../b
1019 pushing to ../b
1020 query 1; heads
1020 query 1; heads
1021 searching for changes
1021 searching for changes
1022 all remote heads known locally
1022 all remote heads known locally
1023 invalid branchheads cache (served): tip differs
1023 invalid branchheads cache (served): tip differs
1024 listing keys for "bookmarks"
1024 listing keys for "bookmarks"
1025 3 changesets found
1025 3 changesets found
1026 list of changesets:
1026 list of changesets:
1027 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1027 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1028 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1028 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1029 911600dab2ae7a9baff75958b84fe606851ce955
1029 911600dab2ae7a9baff75958b84fe606851ce955
1030 adding changesets
1031 bundling: 1/3 changesets (33.33%)
1030 bundling: 1/3 changesets (33.33%)
1032 bundling: 2/3 changesets (66.67%)
1031 bundling: 2/3 changesets (66.67%)
1033 bundling: 3/3 changesets (100.00%)
1032 bundling: 3/3 changesets (100.00%)
1034 bundling: 1/3 manifests (33.33%)
1033 bundling: 1/3 manifests (33.33%)
1035 bundling: 2/3 manifests (66.67%)
1034 bundling: 2/3 manifests (66.67%)
1036 bundling: 3/3 manifests (100.00%)
1035 bundling: 3/3 manifests (100.00%)
1037 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1036 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1038 bundling: foo/file.txt 2/3 files (66.67%)
1037 bundling: foo/file.txt 2/3 files (66.67%)
1039 bundling: quux/file.py 3/3 files (100.00%)
1038 bundling: quux/file.py 3/3 files (100.00%)
1039 adding changesets
1040 changesets: 1 chunks
1040 changesets: 1 chunks
1041 add changeset ef1ea85a6374
1041 add changeset ef1ea85a6374
1042 changesets: 2 chunks
1042 changesets: 2 chunks
1043 add changeset f9cafe1212c8
1043 add changeset f9cafe1212c8
1044 changesets: 3 chunks
1044 changesets: 3 chunks
1045 add changeset 911600dab2ae
1045 add changeset 911600dab2ae
1046 adding manifests
1046 adding manifests
1047 manifests: 1/3 chunks (33.33%)
1047 manifests: 1/3 chunks (33.33%)
1048 manifests: 2/3 chunks (66.67%)
1048 manifests: 2/3 chunks (66.67%)
1049 manifests: 3/3 chunks (100.00%)
1049 manifests: 3/3 chunks (100.00%)
1050 adding file changes
1050 adding file changes
1051 adding foo/Bar/file.txt revisions
1051 adding foo/Bar/file.txt revisions
1052 files: 1/3 chunks (33.33%)
1052 files: 1/3 chunks (33.33%)
1053 adding foo/file.txt revisions
1053 adding foo/file.txt revisions
1054 files: 2/3 chunks (66.67%)
1054 files: 2/3 chunks (66.67%)
1055 adding quux/file.py revisions
1055 adding quux/file.py revisions
1056 files: 3/3 chunks (100.00%)
1056 files: 3/3 chunks (100.00%)
1057 added 3 changesets with 3 changes to 3 files
1057 added 3 changesets with 3 changes to 3 files
1058 calling hook pretxnchangegroup.acl: hgext.acl.hook
1058 calling hook pretxnchangegroup.acl: hgext.acl.hook
1059 acl: checking access for user "barney"
1059 acl: checking access for user "barney"
1060 acl: acl.allow.branches not enabled
1060 acl: acl.allow.branches not enabled
1061 acl: acl.deny.branches not enabled
1061 acl: acl.deny.branches not enabled
1062 acl: acl.allow enabled, 1 entries for user barney
1062 acl: acl.allow enabled, 1 entries for user barney
1063 acl: acl.deny enabled, 0 entries for user barney
1063 acl: acl.deny enabled, 0 entries for user barney
1064 acl: branch access granted: "ef1ea85a6374" on branch "default"
1064 acl: branch access granted: "ef1ea85a6374" on branch "default"
1065 acl: path access granted: "ef1ea85a6374"
1065 acl: path access granted: "ef1ea85a6374"
1066 acl: branch access granted: "f9cafe1212c8" on branch "default"
1066 acl: branch access granted: "f9cafe1212c8" on branch "default"
1067 acl: path access granted: "f9cafe1212c8"
1067 acl: path access granted: "f9cafe1212c8"
1068 acl: branch access granted: "911600dab2ae" on branch "default"
1068 acl: branch access granted: "911600dab2ae" on branch "default"
1069 acl: path access granted: "911600dab2ae"
1069 acl: path access granted: "911600dab2ae"
1070 updating the branch cache
1070 updating the branch cache
1071 listing keys for "phases"
1071 listing keys for "phases"
1072 try to push obsolete markers to remote
1072 try to push obsolete markers to remote
1073 checking for updated bookmarks
1073 checking for updated bookmarks
1074 listing keys for "bookmarks"
1074 listing keys for "bookmarks"
1075 repository tip rolled back to revision 0 (undo push)
1075 repository tip rolled back to revision 0 (undo push)
1076 0:6675d58eff77
1076 0:6675d58eff77
1077
1077
1078
1078
1079 asterisk
1079 asterisk
1080
1080
1081 $ init_config
1081 $ init_config
1082
1082
1083 asterisk test
1083 asterisk test
1084
1084
1085 $ echo '[acl.allow]' >> $config
1085 $ echo '[acl.allow]' >> $config
1086 $ echo "** = fred" >> $config
1086 $ echo "** = fred" >> $config
1087
1087
1088 fred is always allowed
1088 fred is always allowed
1089
1089
1090 $ do_push fred
1090 $ do_push fred
1091 Pushing as user fred
1091 Pushing as user fred
1092 hgrc = """
1092 hgrc = """
1093 [acl]
1093 [acl]
1094 sources = push
1094 sources = push
1095 [extensions]
1095 [extensions]
1096 [acl.allow]
1096 [acl.allow]
1097 ** = fred
1097 ** = fred
1098 """
1098 """
1099 pushing to ../b
1099 pushing to ../b
1100 query 1; heads
1100 query 1; heads
1101 searching for changes
1101 searching for changes
1102 all remote heads known locally
1102 all remote heads known locally
1103 invalid branchheads cache (served): tip differs
1103 invalid branchheads cache (served): tip differs
1104 listing keys for "bookmarks"
1104 listing keys for "bookmarks"
1105 3 changesets found
1105 3 changesets found
1106 list of changesets:
1106 list of changesets:
1107 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1107 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1108 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1108 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1109 911600dab2ae7a9baff75958b84fe606851ce955
1109 911600dab2ae7a9baff75958b84fe606851ce955
1110 adding changesets
1111 bundling: 1/3 changesets (33.33%)
1110 bundling: 1/3 changesets (33.33%)
1112 bundling: 2/3 changesets (66.67%)
1111 bundling: 2/3 changesets (66.67%)
1113 bundling: 3/3 changesets (100.00%)
1112 bundling: 3/3 changesets (100.00%)
1114 bundling: 1/3 manifests (33.33%)
1113 bundling: 1/3 manifests (33.33%)
1115 bundling: 2/3 manifests (66.67%)
1114 bundling: 2/3 manifests (66.67%)
1116 bundling: 3/3 manifests (100.00%)
1115 bundling: 3/3 manifests (100.00%)
1117 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1116 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1118 bundling: foo/file.txt 2/3 files (66.67%)
1117 bundling: foo/file.txt 2/3 files (66.67%)
1119 bundling: quux/file.py 3/3 files (100.00%)
1118 bundling: quux/file.py 3/3 files (100.00%)
1119 adding changesets
1120 changesets: 1 chunks
1120 changesets: 1 chunks
1121 add changeset ef1ea85a6374
1121 add changeset ef1ea85a6374
1122 changesets: 2 chunks
1122 changesets: 2 chunks
1123 add changeset f9cafe1212c8
1123 add changeset f9cafe1212c8
1124 changesets: 3 chunks
1124 changesets: 3 chunks
1125 add changeset 911600dab2ae
1125 add changeset 911600dab2ae
1126 adding manifests
1126 adding manifests
1127 manifests: 1/3 chunks (33.33%)
1127 manifests: 1/3 chunks (33.33%)
1128 manifests: 2/3 chunks (66.67%)
1128 manifests: 2/3 chunks (66.67%)
1129 manifests: 3/3 chunks (100.00%)
1129 manifests: 3/3 chunks (100.00%)
1130 adding file changes
1130 adding file changes
1131 adding foo/Bar/file.txt revisions
1131 adding foo/Bar/file.txt revisions
1132 files: 1/3 chunks (33.33%)
1132 files: 1/3 chunks (33.33%)
1133 adding foo/file.txt revisions
1133 adding foo/file.txt revisions
1134 files: 2/3 chunks (66.67%)
1134 files: 2/3 chunks (66.67%)
1135 adding quux/file.py revisions
1135 adding quux/file.py revisions
1136 files: 3/3 chunks (100.00%)
1136 files: 3/3 chunks (100.00%)
1137 added 3 changesets with 3 changes to 3 files
1137 added 3 changesets with 3 changes to 3 files
1138 calling hook pretxnchangegroup.acl: hgext.acl.hook
1138 calling hook pretxnchangegroup.acl: hgext.acl.hook
1139 acl: checking access for user "fred"
1139 acl: checking access for user "fred"
1140 acl: acl.allow.branches not enabled
1140 acl: acl.allow.branches not enabled
1141 acl: acl.deny.branches not enabled
1141 acl: acl.deny.branches not enabled
1142 acl: acl.allow enabled, 1 entries for user fred
1142 acl: acl.allow enabled, 1 entries for user fred
1143 acl: acl.deny not enabled
1143 acl: acl.deny not enabled
1144 acl: branch access granted: "ef1ea85a6374" on branch "default"
1144 acl: branch access granted: "ef1ea85a6374" on branch "default"
1145 acl: path access granted: "ef1ea85a6374"
1145 acl: path access granted: "ef1ea85a6374"
1146 acl: branch access granted: "f9cafe1212c8" on branch "default"
1146 acl: branch access granted: "f9cafe1212c8" on branch "default"
1147 acl: path access granted: "f9cafe1212c8"
1147 acl: path access granted: "f9cafe1212c8"
1148 acl: branch access granted: "911600dab2ae" on branch "default"
1148 acl: branch access granted: "911600dab2ae" on branch "default"
1149 acl: path access granted: "911600dab2ae"
1149 acl: path access granted: "911600dab2ae"
1150 updating the branch cache
1150 updating the branch cache
1151 listing keys for "phases"
1151 listing keys for "phases"
1152 try to push obsolete markers to remote
1152 try to push obsolete markers to remote
1153 checking for updated bookmarks
1153 checking for updated bookmarks
1154 listing keys for "bookmarks"
1154 listing keys for "bookmarks"
1155 repository tip rolled back to revision 0 (undo push)
1155 repository tip rolled back to revision 0 (undo push)
1156 0:6675d58eff77
1156 0:6675d58eff77
1157
1157
1158
1158
1159 $ echo '[acl.deny]' >> $config
1159 $ echo '[acl.deny]' >> $config
1160 $ echo "foo/Bar/** = *" >> $config
1160 $ echo "foo/Bar/** = *" >> $config
1161
1161
1162 no one is allowed inside foo/Bar/
1162 no one is allowed inside foo/Bar/
1163
1163
1164 $ do_push fred
1164 $ do_push fred
1165 Pushing as user fred
1165 Pushing as user fred
1166 hgrc = """
1166 hgrc = """
1167 [acl]
1167 [acl]
1168 sources = push
1168 sources = push
1169 [extensions]
1169 [extensions]
1170 [acl.allow]
1170 [acl.allow]
1171 ** = fred
1171 ** = fred
1172 [acl.deny]
1172 [acl.deny]
1173 foo/Bar/** = *
1173 foo/Bar/** = *
1174 """
1174 """
1175 pushing to ../b
1175 pushing to ../b
1176 query 1; heads
1176 query 1; heads
1177 searching for changes
1177 searching for changes
1178 all remote heads known locally
1178 all remote heads known locally
1179 invalid branchheads cache (served): tip differs
1179 invalid branchheads cache (served): tip differs
1180 listing keys for "bookmarks"
1180 listing keys for "bookmarks"
1181 3 changesets found
1181 3 changesets found
1182 list of changesets:
1182 list of changesets:
1183 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1183 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1184 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1184 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1185 911600dab2ae7a9baff75958b84fe606851ce955
1185 911600dab2ae7a9baff75958b84fe606851ce955
1186 adding changesets
1187 bundling: 1/3 changesets (33.33%)
1186 bundling: 1/3 changesets (33.33%)
1188 bundling: 2/3 changesets (66.67%)
1187 bundling: 2/3 changesets (66.67%)
1189 bundling: 3/3 changesets (100.00%)
1188 bundling: 3/3 changesets (100.00%)
1190 bundling: 1/3 manifests (33.33%)
1189 bundling: 1/3 manifests (33.33%)
1191 bundling: 2/3 manifests (66.67%)
1190 bundling: 2/3 manifests (66.67%)
1192 bundling: 3/3 manifests (100.00%)
1191 bundling: 3/3 manifests (100.00%)
1193 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1192 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1194 bundling: foo/file.txt 2/3 files (66.67%)
1193 bundling: foo/file.txt 2/3 files (66.67%)
1195 bundling: quux/file.py 3/3 files (100.00%)
1194 bundling: quux/file.py 3/3 files (100.00%)
1195 adding changesets
1196 changesets: 1 chunks
1196 changesets: 1 chunks
1197 add changeset ef1ea85a6374
1197 add changeset ef1ea85a6374
1198 changesets: 2 chunks
1198 changesets: 2 chunks
1199 add changeset f9cafe1212c8
1199 add changeset f9cafe1212c8
1200 changesets: 3 chunks
1200 changesets: 3 chunks
1201 add changeset 911600dab2ae
1201 add changeset 911600dab2ae
1202 adding manifests
1202 adding manifests
1203 manifests: 1/3 chunks (33.33%)
1203 manifests: 1/3 chunks (33.33%)
1204 manifests: 2/3 chunks (66.67%)
1204 manifests: 2/3 chunks (66.67%)
1205 manifests: 3/3 chunks (100.00%)
1205 manifests: 3/3 chunks (100.00%)
1206 adding file changes
1206 adding file changes
1207 adding foo/Bar/file.txt revisions
1207 adding foo/Bar/file.txt revisions
1208 files: 1/3 chunks (33.33%)
1208 files: 1/3 chunks (33.33%)
1209 adding foo/file.txt revisions
1209 adding foo/file.txt revisions
1210 files: 2/3 chunks (66.67%)
1210 files: 2/3 chunks (66.67%)
1211 adding quux/file.py revisions
1211 adding quux/file.py revisions
1212 files: 3/3 chunks (100.00%)
1212 files: 3/3 chunks (100.00%)
1213 added 3 changesets with 3 changes to 3 files
1213 added 3 changesets with 3 changes to 3 files
1214 calling hook pretxnchangegroup.acl: hgext.acl.hook
1214 calling hook pretxnchangegroup.acl: hgext.acl.hook
1215 acl: checking access for user "fred"
1215 acl: checking access for user "fred"
1216 acl: acl.allow.branches not enabled
1216 acl: acl.allow.branches not enabled
1217 acl: acl.deny.branches not enabled
1217 acl: acl.deny.branches not enabled
1218 acl: acl.allow enabled, 1 entries for user fred
1218 acl: acl.allow enabled, 1 entries for user fred
1219 acl: acl.deny enabled, 1 entries for user fred
1219 acl: acl.deny enabled, 1 entries for user fred
1220 acl: branch access granted: "ef1ea85a6374" on branch "default"
1220 acl: branch access granted: "ef1ea85a6374" on branch "default"
1221 acl: path access granted: "ef1ea85a6374"
1221 acl: path access granted: "ef1ea85a6374"
1222 acl: branch access granted: "f9cafe1212c8" on branch "default"
1222 acl: branch access granted: "f9cafe1212c8" on branch "default"
1223 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1223 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1224 transaction abort!
1224 transaction abort!
1225 rollback completed
1225 rollback completed
1226 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1226 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1227 no rollback information available
1227 no rollback information available
1228 0:6675d58eff77
1228 0:6675d58eff77
1229
1229
1230
1230
1231 Groups
1231 Groups
1232
1232
1233 $ init_config
1233 $ init_config
1234
1234
1235 OS-level groups
1235 OS-level groups
1236
1236
1237 $ echo '[acl.allow]' >> $config
1237 $ echo '[acl.allow]' >> $config
1238 $ echo "** = @group1" >> $config
1238 $ echo "** = @group1" >> $config
1239
1239
1240 @group1 is always allowed
1240 @group1 is always allowed
1241
1241
1242 $ do_push fred
1242 $ do_push fred
1243 Pushing as user fred
1243 Pushing as user fred
1244 hgrc = """
1244 hgrc = """
1245 [acl]
1245 [acl]
1246 sources = push
1246 sources = push
1247 [extensions]
1247 [extensions]
1248 [acl.allow]
1248 [acl.allow]
1249 ** = @group1
1249 ** = @group1
1250 """
1250 """
1251 pushing to ../b
1251 pushing to ../b
1252 query 1; heads
1252 query 1; heads
1253 searching for changes
1253 searching for changes
1254 all remote heads known locally
1254 all remote heads known locally
1255 invalid branchheads cache (served): tip differs
1255 invalid branchheads cache (served): tip differs
1256 listing keys for "bookmarks"
1256 listing keys for "bookmarks"
1257 3 changesets found
1257 3 changesets found
1258 list of changesets:
1258 list of changesets:
1259 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1259 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1260 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1260 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1261 911600dab2ae7a9baff75958b84fe606851ce955
1261 911600dab2ae7a9baff75958b84fe606851ce955
1262 adding changesets
1263 bundling: 1/3 changesets (33.33%)
1262 bundling: 1/3 changesets (33.33%)
1264 bundling: 2/3 changesets (66.67%)
1263 bundling: 2/3 changesets (66.67%)
1265 bundling: 3/3 changesets (100.00%)
1264 bundling: 3/3 changesets (100.00%)
1266 bundling: 1/3 manifests (33.33%)
1265 bundling: 1/3 manifests (33.33%)
1267 bundling: 2/3 manifests (66.67%)
1266 bundling: 2/3 manifests (66.67%)
1268 bundling: 3/3 manifests (100.00%)
1267 bundling: 3/3 manifests (100.00%)
1269 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1268 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1270 bundling: foo/file.txt 2/3 files (66.67%)
1269 bundling: foo/file.txt 2/3 files (66.67%)
1271 bundling: quux/file.py 3/3 files (100.00%)
1270 bundling: quux/file.py 3/3 files (100.00%)
1271 adding changesets
1272 changesets: 1 chunks
1272 changesets: 1 chunks
1273 add changeset ef1ea85a6374
1273 add changeset ef1ea85a6374
1274 changesets: 2 chunks
1274 changesets: 2 chunks
1275 add changeset f9cafe1212c8
1275 add changeset f9cafe1212c8
1276 changesets: 3 chunks
1276 changesets: 3 chunks
1277 add changeset 911600dab2ae
1277 add changeset 911600dab2ae
1278 adding manifests
1278 adding manifests
1279 manifests: 1/3 chunks (33.33%)
1279 manifests: 1/3 chunks (33.33%)
1280 manifests: 2/3 chunks (66.67%)
1280 manifests: 2/3 chunks (66.67%)
1281 manifests: 3/3 chunks (100.00%)
1281 manifests: 3/3 chunks (100.00%)
1282 adding file changes
1282 adding file changes
1283 adding foo/Bar/file.txt revisions
1283 adding foo/Bar/file.txt revisions
1284 files: 1/3 chunks (33.33%)
1284 files: 1/3 chunks (33.33%)
1285 adding foo/file.txt revisions
1285 adding foo/file.txt revisions
1286 files: 2/3 chunks (66.67%)
1286 files: 2/3 chunks (66.67%)
1287 adding quux/file.py revisions
1287 adding quux/file.py revisions
1288 files: 3/3 chunks (100.00%)
1288 files: 3/3 chunks (100.00%)
1289 added 3 changesets with 3 changes to 3 files
1289 added 3 changesets with 3 changes to 3 files
1290 calling hook pretxnchangegroup.acl: hgext.acl.hook
1290 calling hook pretxnchangegroup.acl: hgext.acl.hook
1291 acl: checking access for user "fred"
1291 acl: checking access for user "fred"
1292 acl: acl.allow.branches not enabled
1292 acl: acl.allow.branches not enabled
1293 acl: acl.deny.branches not enabled
1293 acl: acl.deny.branches not enabled
1294 acl: "group1" not defined in [acl.groups]
1294 acl: "group1" not defined in [acl.groups]
1295 acl: acl.allow enabled, 1 entries for user fred
1295 acl: acl.allow enabled, 1 entries for user fred
1296 acl: acl.deny not enabled
1296 acl: acl.deny not enabled
1297 acl: branch access granted: "ef1ea85a6374" on branch "default"
1297 acl: branch access granted: "ef1ea85a6374" on branch "default"
1298 acl: path access granted: "ef1ea85a6374"
1298 acl: path access granted: "ef1ea85a6374"
1299 acl: branch access granted: "f9cafe1212c8" on branch "default"
1299 acl: branch access granted: "f9cafe1212c8" on branch "default"
1300 acl: path access granted: "f9cafe1212c8"
1300 acl: path access granted: "f9cafe1212c8"
1301 acl: branch access granted: "911600dab2ae" on branch "default"
1301 acl: branch access granted: "911600dab2ae" on branch "default"
1302 acl: path access granted: "911600dab2ae"
1302 acl: path access granted: "911600dab2ae"
1303 updating the branch cache
1303 updating the branch cache
1304 listing keys for "phases"
1304 listing keys for "phases"
1305 try to push obsolete markers to remote
1305 try to push obsolete markers to remote
1306 checking for updated bookmarks
1306 checking for updated bookmarks
1307 listing keys for "bookmarks"
1307 listing keys for "bookmarks"
1308 repository tip rolled back to revision 0 (undo push)
1308 repository tip rolled back to revision 0 (undo push)
1309 0:6675d58eff77
1309 0:6675d58eff77
1310
1310
1311
1311
1312 $ echo '[acl.deny]' >> $config
1312 $ echo '[acl.deny]' >> $config
1313 $ echo "foo/Bar/** = @group1" >> $config
1313 $ echo "foo/Bar/** = @group1" >> $config
1314
1314
1315 @group is allowed inside anything but foo/Bar/
1315 @group is allowed inside anything but foo/Bar/
1316
1316
1317 $ do_push fred
1317 $ do_push fred
1318 Pushing as user fred
1318 Pushing as user fred
1319 hgrc = """
1319 hgrc = """
1320 [acl]
1320 [acl]
1321 sources = push
1321 sources = push
1322 [extensions]
1322 [extensions]
1323 [acl.allow]
1323 [acl.allow]
1324 ** = @group1
1324 ** = @group1
1325 [acl.deny]
1325 [acl.deny]
1326 foo/Bar/** = @group1
1326 foo/Bar/** = @group1
1327 """
1327 """
1328 pushing to ../b
1328 pushing to ../b
1329 query 1; heads
1329 query 1; heads
1330 searching for changes
1330 searching for changes
1331 all remote heads known locally
1331 all remote heads known locally
1332 invalid branchheads cache (served): tip differs
1332 invalid branchheads cache (served): tip differs
1333 listing keys for "bookmarks"
1333 listing keys for "bookmarks"
1334 3 changesets found
1334 3 changesets found
1335 list of changesets:
1335 list of changesets:
1336 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1336 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1337 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1337 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1338 911600dab2ae7a9baff75958b84fe606851ce955
1338 911600dab2ae7a9baff75958b84fe606851ce955
1339 adding changesets
1340 bundling: 1/3 changesets (33.33%)
1339 bundling: 1/3 changesets (33.33%)
1341 bundling: 2/3 changesets (66.67%)
1340 bundling: 2/3 changesets (66.67%)
1342 bundling: 3/3 changesets (100.00%)
1341 bundling: 3/3 changesets (100.00%)
1343 bundling: 1/3 manifests (33.33%)
1342 bundling: 1/3 manifests (33.33%)
1344 bundling: 2/3 manifests (66.67%)
1343 bundling: 2/3 manifests (66.67%)
1345 bundling: 3/3 manifests (100.00%)
1344 bundling: 3/3 manifests (100.00%)
1346 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1345 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1347 bundling: foo/file.txt 2/3 files (66.67%)
1346 bundling: foo/file.txt 2/3 files (66.67%)
1348 bundling: quux/file.py 3/3 files (100.00%)
1347 bundling: quux/file.py 3/3 files (100.00%)
1348 adding changesets
1349 changesets: 1 chunks
1349 changesets: 1 chunks
1350 add changeset ef1ea85a6374
1350 add changeset ef1ea85a6374
1351 changesets: 2 chunks
1351 changesets: 2 chunks
1352 add changeset f9cafe1212c8
1352 add changeset f9cafe1212c8
1353 changesets: 3 chunks
1353 changesets: 3 chunks
1354 add changeset 911600dab2ae
1354 add changeset 911600dab2ae
1355 adding manifests
1355 adding manifests
1356 manifests: 1/3 chunks (33.33%)
1356 manifests: 1/3 chunks (33.33%)
1357 manifests: 2/3 chunks (66.67%)
1357 manifests: 2/3 chunks (66.67%)
1358 manifests: 3/3 chunks (100.00%)
1358 manifests: 3/3 chunks (100.00%)
1359 adding file changes
1359 adding file changes
1360 adding foo/Bar/file.txt revisions
1360 adding foo/Bar/file.txt revisions
1361 files: 1/3 chunks (33.33%)
1361 files: 1/3 chunks (33.33%)
1362 adding foo/file.txt revisions
1362 adding foo/file.txt revisions
1363 files: 2/3 chunks (66.67%)
1363 files: 2/3 chunks (66.67%)
1364 adding quux/file.py revisions
1364 adding quux/file.py revisions
1365 files: 3/3 chunks (100.00%)
1365 files: 3/3 chunks (100.00%)
1366 added 3 changesets with 3 changes to 3 files
1366 added 3 changesets with 3 changes to 3 files
1367 calling hook pretxnchangegroup.acl: hgext.acl.hook
1367 calling hook pretxnchangegroup.acl: hgext.acl.hook
1368 acl: checking access for user "fred"
1368 acl: checking access for user "fred"
1369 acl: acl.allow.branches not enabled
1369 acl: acl.allow.branches not enabled
1370 acl: acl.deny.branches not enabled
1370 acl: acl.deny.branches not enabled
1371 acl: "group1" not defined in [acl.groups]
1371 acl: "group1" not defined in [acl.groups]
1372 acl: acl.allow enabled, 1 entries for user fred
1372 acl: acl.allow enabled, 1 entries for user fred
1373 acl: "group1" not defined in [acl.groups]
1373 acl: "group1" not defined in [acl.groups]
1374 acl: acl.deny enabled, 1 entries for user fred
1374 acl: acl.deny enabled, 1 entries for user fred
1375 acl: branch access granted: "ef1ea85a6374" on branch "default"
1375 acl: branch access granted: "ef1ea85a6374" on branch "default"
1376 acl: path access granted: "ef1ea85a6374"
1376 acl: path access granted: "ef1ea85a6374"
1377 acl: branch access granted: "f9cafe1212c8" on branch "default"
1377 acl: branch access granted: "f9cafe1212c8" on branch "default"
1378 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1378 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1379 transaction abort!
1379 transaction abort!
1380 rollback completed
1380 rollback completed
1381 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1381 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1382 no rollback information available
1382 no rollback information available
1383 0:6675d58eff77
1383 0:6675d58eff77
1384
1384
1385
1385
1386 Invalid group
1386 Invalid group
1387
1387
1388 Disable the fakegroups trick to get real failures
1388 Disable the fakegroups trick to get real failures
1389
1389
1390 $ grep -v fakegroups $config > config.tmp
1390 $ grep -v fakegroups $config > config.tmp
1391 $ mv config.tmp $config
1391 $ mv config.tmp $config
1392 $ echo '[acl.allow]' >> $config
1392 $ echo '[acl.allow]' >> $config
1393 $ echo "** = @unlikelytoexist" >> $config
1393 $ echo "** = @unlikelytoexist" >> $config
1394 $ do_push fred 2>&1 | grep unlikelytoexist
1394 $ do_push fred 2>&1 | grep unlikelytoexist
1395 ** = @unlikelytoexist
1395 ** = @unlikelytoexist
1396 acl: "unlikelytoexist" not defined in [acl.groups]
1396 acl: "unlikelytoexist" not defined in [acl.groups]
1397 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1397 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1398 abort: group 'unlikelytoexist' is undefined
1398 abort: group 'unlikelytoexist' is undefined
1399
1399
1400
1400
1401 Branch acl tests setup
1401 Branch acl tests setup
1402
1402
1403 $ init_config
1403 $ init_config
1404 $ cd b
1404 $ cd b
1405 $ hg up
1405 $ hg up
1406 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1406 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1407 $ hg branch foobar
1407 $ hg branch foobar
1408 marked working directory as branch foobar
1408 marked working directory as branch foobar
1409 (branches are permanent and global, did you want a bookmark?)
1409 (branches are permanent and global, did you want a bookmark?)
1410 $ hg commit -m 'create foobar'
1410 $ hg commit -m 'create foobar'
1411 $ echo 'foo contents' > abc.txt
1411 $ echo 'foo contents' > abc.txt
1412 $ hg add abc.txt
1412 $ hg add abc.txt
1413 $ hg commit -m 'foobar contents'
1413 $ hg commit -m 'foobar contents'
1414 $ cd ..
1414 $ cd ..
1415 $ hg --cwd a pull ../b
1415 $ hg --cwd a pull ../b
1416 pulling from ../b
1416 pulling from ../b
1417 searching for changes
1417 searching for changes
1418 adding changesets
1418 adding changesets
1419 adding manifests
1419 adding manifests
1420 adding file changes
1420 adding file changes
1421 added 2 changesets with 1 changes to 1 files (+1 heads)
1421 added 2 changesets with 1 changes to 1 files (+1 heads)
1422 (run 'hg heads' to see heads)
1422 (run 'hg heads' to see heads)
1423
1423
1424 Create additional changeset on foobar branch
1424 Create additional changeset on foobar branch
1425
1425
1426 $ cd a
1426 $ cd a
1427 $ hg up -C foobar
1427 $ hg up -C foobar
1428 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1428 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1429 $ echo 'foo contents2' > abc.txt
1429 $ echo 'foo contents2' > abc.txt
1430 $ hg commit -m 'foobar contents2'
1430 $ hg commit -m 'foobar contents2'
1431 $ cd ..
1431 $ cd ..
1432
1432
1433
1433
1434 No branch acls specified
1434 No branch acls specified
1435
1435
1436 $ do_push astro
1436 $ do_push astro
1437 Pushing as user astro
1437 Pushing as user astro
1438 hgrc = """
1438 hgrc = """
1439 [acl]
1439 [acl]
1440 sources = push
1440 sources = push
1441 [extensions]
1441 [extensions]
1442 """
1442 """
1443 pushing to ../b
1443 pushing to ../b
1444 query 1; heads
1444 query 1; heads
1445 searching for changes
1445 searching for changes
1446 all remote heads known locally
1446 all remote heads known locally
1447 listing keys for "bookmarks"
1447 listing keys for "bookmarks"
1448 4 changesets found
1448 4 changesets found
1449 list of changesets:
1449 list of changesets:
1450 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1450 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1451 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1451 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1452 911600dab2ae7a9baff75958b84fe606851ce955
1452 911600dab2ae7a9baff75958b84fe606851ce955
1453 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1453 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1454 adding changesets
1455 bundling: 1/4 changesets (25.00%)
1454 bundling: 1/4 changesets (25.00%)
1456 bundling: 2/4 changesets (50.00%)
1455 bundling: 2/4 changesets (50.00%)
1457 bundling: 3/4 changesets (75.00%)
1456 bundling: 3/4 changesets (75.00%)
1458 bundling: 4/4 changesets (100.00%)
1457 bundling: 4/4 changesets (100.00%)
1459 bundling: 1/4 manifests (25.00%)
1458 bundling: 1/4 manifests (25.00%)
1460 bundling: 2/4 manifests (50.00%)
1459 bundling: 2/4 manifests (50.00%)
1461 bundling: 3/4 manifests (75.00%)
1460 bundling: 3/4 manifests (75.00%)
1462 bundling: 4/4 manifests (100.00%)
1461 bundling: 4/4 manifests (100.00%)
1463 bundling: abc.txt 1/4 files (25.00%)
1462 bundling: abc.txt 1/4 files (25.00%)
1464 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1463 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1465 bundling: foo/file.txt 3/4 files (75.00%)
1464 bundling: foo/file.txt 3/4 files (75.00%)
1466 bundling: quux/file.py 4/4 files (100.00%)
1465 bundling: quux/file.py 4/4 files (100.00%)
1466 adding changesets
1467 changesets: 1 chunks
1467 changesets: 1 chunks
1468 add changeset ef1ea85a6374
1468 add changeset ef1ea85a6374
1469 changesets: 2 chunks
1469 changesets: 2 chunks
1470 add changeset f9cafe1212c8
1470 add changeset f9cafe1212c8
1471 changesets: 3 chunks
1471 changesets: 3 chunks
1472 add changeset 911600dab2ae
1472 add changeset 911600dab2ae
1473 changesets: 4 chunks
1473 changesets: 4 chunks
1474 add changeset e8fc755d4d82
1474 add changeset e8fc755d4d82
1475 adding manifests
1475 adding manifests
1476 manifests: 1/4 chunks (25.00%)
1476 manifests: 1/4 chunks (25.00%)
1477 manifests: 2/4 chunks (50.00%)
1477 manifests: 2/4 chunks (50.00%)
1478 manifests: 3/4 chunks (75.00%)
1478 manifests: 3/4 chunks (75.00%)
1479 manifests: 4/4 chunks (100.00%)
1479 manifests: 4/4 chunks (100.00%)
1480 adding file changes
1480 adding file changes
1481 adding abc.txt revisions
1481 adding abc.txt revisions
1482 files: 1/4 chunks (25.00%)
1482 files: 1/4 chunks (25.00%)
1483 adding foo/Bar/file.txt revisions
1483 adding foo/Bar/file.txt revisions
1484 files: 2/4 chunks (50.00%)
1484 files: 2/4 chunks (50.00%)
1485 adding foo/file.txt revisions
1485 adding foo/file.txt revisions
1486 files: 3/4 chunks (75.00%)
1486 files: 3/4 chunks (75.00%)
1487 adding quux/file.py revisions
1487 adding quux/file.py revisions
1488 files: 4/4 chunks (100.00%)
1488 files: 4/4 chunks (100.00%)
1489 added 4 changesets with 4 changes to 4 files (+1 heads)
1489 added 4 changesets with 4 changes to 4 files (+1 heads)
1490 calling hook pretxnchangegroup.acl: hgext.acl.hook
1490 calling hook pretxnchangegroup.acl: hgext.acl.hook
1491 acl: checking access for user "astro"
1491 acl: checking access for user "astro"
1492 acl: acl.allow.branches not enabled
1492 acl: acl.allow.branches not enabled
1493 acl: acl.deny.branches not enabled
1493 acl: acl.deny.branches not enabled
1494 acl: acl.allow not enabled
1494 acl: acl.allow not enabled
1495 acl: acl.deny not enabled
1495 acl: acl.deny not enabled
1496 acl: branch access granted: "ef1ea85a6374" on branch "default"
1496 acl: branch access granted: "ef1ea85a6374" on branch "default"
1497 acl: path access granted: "ef1ea85a6374"
1497 acl: path access granted: "ef1ea85a6374"
1498 acl: branch access granted: "f9cafe1212c8" on branch "default"
1498 acl: branch access granted: "f9cafe1212c8" on branch "default"
1499 acl: path access granted: "f9cafe1212c8"
1499 acl: path access granted: "f9cafe1212c8"
1500 acl: branch access granted: "911600dab2ae" on branch "default"
1500 acl: branch access granted: "911600dab2ae" on branch "default"
1501 acl: path access granted: "911600dab2ae"
1501 acl: path access granted: "911600dab2ae"
1502 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1502 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1503 acl: path access granted: "e8fc755d4d82"
1503 acl: path access granted: "e8fc755d4d82"
1504 updating the branch cache
1504 updating the branch cache
1505 listing keys for "phases"
1505 listing keys for "phases"
1506 try to push obsolete markers to remote
1506 try to push obsolete markers to remote
1507 checking for updated bookmarks
1507 checking for updated bookmarks
1508 listing keys for "bookmarks"
1508 listing keys for "bookmarks"
1509 repository tip rolled back to revision 2 (undo push)
1509 repository tip rolled back to revision 2 (undo push)
1510 2:fb35475503ef
1510 2:fb35475503ef
1511
1511
1512
1512
1513 Branch acl deny test
1513 Branch acl deny test
1514
1514
1515 $ echo "[acl.deny.branches]" >> $config
1515 $ echo "[acl.deny.branches]" >> $config
1516 $ echo "foobar = *" >> $config
1516 $ echo "foobar = *" >> $config
1517 $ do_push astro
1517 $ do_push astro
1518 Pushing as user astro
1518 Pushing as user astro
1519 hgrc = """
1519 hgrc = """
1520 [acl]
1520 [acl]
1521 sources = push
1521 sources = push
1522 [extensions]
1522 [extensions]
1523 [acl.deny.branches]
1523 [acl.deny.branches]
1524 foobar = *
1524 foobar = *
1525 """
1525 """
1526 pushing to ../b
1526 pushing to ../b
1527 query 1; heads
1527 query 1; heads
1528 searching for changes
1528 searching for changes
1529 all remote heads known locally
1529 all remote heads known locally
1530 listing keys for "bookmarks"
1530 listing keys for "bookmarks"
1531 4 changesets found
1531 4 changesets found
1532 list of changesets:
1532 list of changesets:
1533 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1533 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1534 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1534 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1535 911600dab2ae7a9baff75958b84fe606851ce955
1535 911600dab2ae7a9baff75958b84fe606851ce955
1536 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1536 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1537 adding changesets
1538 bundling: 1/4 changesets (25.00%)
1537 bundling: 1/4 changesets (25.00%)
1539 bundling: 2/4 changesets (50.00%)
1538 bundling: 2/4 changesets (50.00%)
1540 bundling: 3/4 changesets (75.00%)
1539 bundling: 3/4 changesets (75.00%)
1541 bundling: 4/4 changesets (100.00%)
1540 bundling: 4/4 changesets (100.00%)
1542 bundling: 1/4 manifests (25.00%)
1541 bundling: 1/4 manifests (25.00%)
1543 bundling: 2/4 manifests (50.00%)
1542 bundling: 2/4 manifests (50.00%)
1544 bundling: 3/4 manifests (75.00%)
1543 bundling: 3/4 manifests (75.00%)
1545 bundling: 4/4 manifests (100.00%)
1544 bundling: 4/4 manifests (100.00%)
1546 bundling: abc.txt 1/4 files (25.00%)
1545 bundling: abc.txt 1/4 files (25.00%)
1547 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1546 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1548 bundling: foo/file.txt 3/4 files (75.00%)
1547 bundling: foo/file.txt 3/4 files (75.00%)
1549 bundling: quux/file.py 4/4 files (100.00%)
1548 bundling: quux/file.py 4/4 files (100.00%)
1549 adding changesets
1550 changesets: 1 chunks
1550 changesets: 1 chunks
1551 add changeset ef1ea85a6374
1551 add changeset ef1ea85a6374
1552 changesets: 2 chunks
1552 changesets: 2 chunks
1553 add changeset f9cafe1212c8
1553 add changeset f9cafe1212c8
1554 changesets: 3 chunks
1554 changesets: 3 chunks
1555 add changeset 911600dab2ae
1555 add changeset 911600dab2ae
1556 changesets: 4 chunks
1556 changesets: 4 chunks
1557 add changeset e8fc755d4d82
1557 add changeset e8fc755d4d82
1558 adding manifests
1558 adding manifests
1559 manifests: 1/4 chunks (25.00%)
1559 manifests: 1/4 chunks (25.00%)
1560 manifests: 2/4 chunks (50.00%)
1560 manifests: 2/4 chunks (50.00%)
1561 manifests: 3/4 chunks (75.00%)
1561 manifests: 3/4 chunks (75.00%)
1562 manifests: 4/4 chunks (100.00%)
1562 manifests: 4/4 chunks (100.00%)
1563 adding file changes
1563 adding file changes
1564 adding abc.txt revisions
1564 adding abc.txt revisions
1565 files: 1/4 chunks (25.00%)
1565 files: 1/4 chunks (25.00%)
1566 adding foo/Bar/file.txt revisions
1566 adding foo/Bar/file.txt revisions
1567 files: 2/4 chunks (50.00%)
1567 files: 2/4 chunks (50.00%)
1568 adding foo/file.txt revisions
1568 adding foo/file.txt revisions
1569 files: 3/4 chunks (75.00%)
1569 files: 3/4 chunks (75.00%)
1570 adding quux/file.py revisions
1570 adding quux/file.py revisions
1571 files: 4/4 chunks (100.00%)
1571 files: 4/4 chunks (100.00%)
1572 added 4 changesets with 4 changes to 4 files (+1 heads)
1572 added 4 changesets with 4 changes to 4 files (+1 heads)
1573 calling hook pretxnchangegroup.acl: hgext.acl.hook
1573 calling hook pretxnchangegroup.acl: hgext.acl.hook
1574 acl: checking access for user "astro"
1574 acl: checking access for user "astro"
1575 acl: acl.allow.branches not enabled
1575 acl: acl.allow.branches not enabled
1576 acl: acl.deny.branches enabled, 1 entries for user astro
1576 acl: acl.deny.branches enabled, 1 entries for user astro
1577 acl: acl.allow not enabled
1577 acl: acl.allow not enabled
1578 acl: acl.deny not enabled
1578 acl: acl.deny not enabled
1579 acl: branch access granted: "ef1ea85a6374" on branch "default"
1579 acl: branch access granted: "ef1ea85a6374" on branch "default"
1580 acl: path access granted: "ef1ea85a6374"
1580 acl: path access granted: "ef1ea85a6374"
1581 acl: branch access granted: "f9cafe1212c8" on branch "default"
1581 acl: branch access granted: "f9cafe1212c8" on branch "default"
1582 acl: path access granted: "f9cafe1212c8"
1582 acl: path access granted: "f9cafe1212c8"
1583 acl: branch access granted: "911600dab2ae" on branch "default"
1583 acl: branch access granted: "911600dab2ae" on branch "default"
1584 acl: path access granted: "911600dab2ae"
1584 acl: path access granted: "911600dab2ae"
1585 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1585 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1586 transaction abort!
1586 transaction abort!
1587 rollback completed
1587 rollback completed
1588 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1588 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1589 no rollback information available
1589 no rollback information available
1590 2:fb35475503ef
1590 2:fb35475503ef
1591
1591
1592
1592
1593 Branch acl empty allow test
1593 Branch acl empty allow test
1594
1594
1595 $ init_config
1595 $ init_config
1596 $ echo "[acl.allow.branches]" >> $config
1596 $ echo "[acl.allow.branches]" >> $config
1597 $ do_push astro
1597 $ do_push astro
1598 Pushing as user astro
1598 Pushing as user astro
1599 hgrc = """
1599 hgrc = """
1600 [acl]
1600 [acl]
1601 sources = push
1601 sources = push
1602 [extensions]
1602 [extensions]
1603 [acl.allow.branches]
1603 [acl.allow.branches]
1604 """
1604 """
1605 pushing to ../b
1605 pushing to ../b
1606 query 1; heads
1606 query 1; heads
1607 searching for changes
1607 searching for changes
1608 all remote heads known locally
1608 all remote heads known locally
1609 listing keys for "bookmarks"
1609 listing keys for "bookmarks"
1610 4 changesets found
1610 4 changesets found
1611 list of changesets:
1611 list of changesets:
1612 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1612 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1613 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1613 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1614 911600dab2ae7a9baff75958b84fe606851ce955
1614 911600dab2ae7a9baff75958b84fe606851ce955
1615 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1615 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1616 adding changesets
1617 bundling: 1/4 changesets (25.00%)
1616 bundling: 1/4 changesets (25.00%)
1618 bundling: 2/4 changesets (50.00%)
1617 bundling: 2/4 changesets (50.00%)
1619 bundling: 3/4 changesets (75.00%)
1618 bundling: 3/4 changesets (75.00%)
1620 bundling: 4/4 changesets (100.00%)
1619 bundling: 4/4 changesets (100.00%)
1621 bundling: 1/4 manifests (25.00%)
1620 bundling: 1/4 manifests (25.00%)
1622 bundling: 2/4 manifests (50.00%)
1621 bundling: 2/4 manifests (50.00%)
1623 bundling: 3/4 manifests (75.00%)
1622 bundling: 3/4 manifests (75.00%)
1624 bundling: 4/4 manifests (100.00%)
1623 bundling: 4/4 manifests (100.00%)
1625 bundling: abc.txt 1/4 files (25.00%)
1624 bundling: abc.txt 1/4 files (25.00%)
1626 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1625 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1627 bundling: foo/file.txt 3/4 files (75.00%)
1626 bundling: foo/file.txt 3/4 files (75.00%)
1628 bundling: quux/file.py 4/4 files (100.00%)
1627 bundling: quux/file.py 4/4 files (100.00%)
1628 adding changesets
1629 changesets: 1 chunks
1629 changesets: 1 chunks
1630 add changeset ef1ea85a6374
1630 add changeset ef1ea85a6374
1631 changesets: 2 chunks
1631 changesets: 2 chunks
1632 add changeset f9cafe1212c8
1632 add changeset f9cafe1212c8
1633 changesets: 3 chunks
1633 changesets: 3 chunks
1634 add changeset 911600dab2ae
1634 add changeset 911600dab2ae
1635 changesets: 4 chunks
1635 changesets: 4 chunks
1636 add changeset e8fc755d4d82
1636 add changeset e8fc755d4d82
1637 adding manifests
1637 adding manifests
1638 manifests: 1/4 chunks (25.00%)
1638 manifests: 1/4 chunks (25.00%)
1639 manifests: 2/4 chunks (50.00%)
1639 manifests: 2/4 chunks (50.00%)
1640 manifests: 3/4 chunks (75.00%)
1640 manifests: 3/4 chunks (75.00%)
1641 manifests: 4/4 chunks (100.00%)
1641 manifests: 4/4 chunks (100.00%)
1642 adding file changes
1642 adding file changes
1643 adding abc.txt revisions
1643 adding abc.txt revisions
1644 files: 1/4 chunks (25.00%)
1644 files: 1/4 chunks (25.00%)
1645 adding foo/Bar/file.txt revisions
1645 adding foo/Bar/file.txt revisions
1646 files: 2/4 chunks (50.00%)
1646 files: 2/4 chunks (50.00%)
1647 adding foo/file.txt revisions
1647 adding foo/file.txt revisions
1648 files: 3/4 chunks (75.00%)
1648 files: 3/4 chunks (75.00%)
1649 adding quux/file.py revisions
1649 adding quux/file.py revisions
1650 files: 4/4 chunks (100.00%)
1650 files: 4/4 chunks (100.00%)
1651 added 4 changesets with 4 changes to 4 files (+1 heads)
1651 added 4 changesets with 4 changes to 4 files (+1 heads)
1652 calling hook pretxnchangegroup.acl: hgext.acl.hook
1652 calling hook pretxnchangegroup.acl: hgext.acl.hook
1653 acl: checking access for user "astro"
1653 acl: checking access for user "astro"
1654 acl: acl.allow.branches enabled, 0 entries for user astro
1654 acl: acl.allow.branches enabled, 0 entries for user astro
1655 acl: acl.deny.branches not enabled
1655 acl: acl.deny.branches not enabled
1656 acl: acl.allow not enabled
1656 acl: acl.allow not enabled
1657 acl: acl.deny not enabled
1657 acl: acl.deny not enabled
1658 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1658 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1659 transaction abort!
1659 transaction abort!
1660 rollback completed
1660 rollback completed
1661 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1661 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1662 no rollback information available
1662 no rollback information available
1663 2:fb35475503ef
1663 2:fb35475503ef
1664
1664
1665
1665
1666 Branch acl allow other
1666 Branch acl allow other
1667
1667
1668 $ init_config
1668 $ init_config
1669 $ echo "[acl.allow.branches]" >> $config
1669 $ echo "[acl.allow.branches]" >> $config
1670 $ echo "* = george" >> $config
1670 $ echo "* = george" >> $config
1671 $ do_push astro
1671 $ do_push astro
1672 Pushing as user astro
1672 Pushing as user astro
1673 hgrc = """
1673 hgrc = """
1674 [acl]
1674 [acl]
1675 sources = push
1675 sources = push
1676 [extensions]
1676 [extensions]
1677 [acl.allow.branches]
1677 [acl.allow.branches]
1678 * = george
1678 * = george
1679 """
1679 """
1680 pushing to ../b
1680 pushing to ../b
1681 query 1; heads
1681 query 1; heads
1682 searching for changes
1682 searching for changes
1683 all remote heads known locally
1683 all remote heads known locally
1684 listing keys for "bookmarks"
1684 listing keys for "bookmarks"
1685 4 changesets found
1685 4 changesets found
1686 list of changesets:
1686 list of changesets:
1687 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1687 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1688 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1688 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1689 911600dab2ae7a9baff75958b84fe606851ce955
1689 911600dab2ae7a9baff75958b84fe606851ce955
1690 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1690 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1691 adding changesets
1692 bundling: 1/4 changesets (25.00%)
1691 bundling: 1/4 changesets (25.00%)
1693 bundling: 2/4 changesets (50.00%)
1692 bundling: 2/4 changesets (50.00%)
1694 bundling: 3/4 changesets (75.00%)
1693 bundling: 3/4 changesets (75.00%)
1695 bundling: 4/4 changesets (100.00%)
1694 bundling: 4/4 changesets (100.00%)
1696 bundling: 1/4 manifests (25.00%)
1695 bundling: 1/4 manifests (25.00%)
1697 bundling: 2/4 manifests (50.00%)
1696 bundling: 2/4 manifests (50.00%)
1698 bundling: 3/4 manifests (75.00%)
1697 bundling: 3/4 manifests (75.00%)
1699 bundling: 4/4 manifests (100.00%)
1698 bundling: 4/4 manifests (100.00%)
1700 bundling: abc.txt 1/4 files (25.00%)
1699 bundling: abc.txt 1/4 files (25.00%)
1701 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1700 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1702 bundling: foo/file.txt 3/4 files (75.00%)
1701 bundling: foo/file.txt 3/4 files (75.00%)
1703 bundling: quux/file.py 4/4 files (100.00%)
1702 bundling: quux/file.py 4/4 files (100.00%)
1703 adding changesets
1704 changesets: 1 chunks
1704 changesets: 1 chunks
1705 add changeset ef1ea85a6374
1705 add changeset ef1ea85a6374
1706 changesets: 2 chunks
1706 changesets: 2 chunks
1707 add changeset f9cafe1212c8
1707 add changeset f9cafe1212c8
1708 changesets: 3 chunks
1708 changesets: 3 chunks
1709 add changeset 911600dab2ae
1709 add changeset 911600dab2ae
1710 changesets: 4 chunks
1710 changesets: 4 chunks
1711 add changeset e8fc755d4d82
1711 add changeset e8fc755d4d82
1712 adding manifests
1712 adding manifests
1713 manifests: 1/4 chunks (25.00%)
1713 manifests: 1/4 chunks (25.00%)
1714 manifests: 2/4 chunks (50.00%)
1714 manifests: 2/4 chunks (50.00%)
1715 manifests: 3/4 chunks (75.00%)
1715 manifests: 3/4 chunks (75.00%)
1716 manifests: 4/4 chunks (100.00%)
1716 manifests: 4/4 chunks (100.00%)
1717 adding file changes
1717 adding file changes
1718 adding abc.txt revisions
1718 adding abc.txt revisions
1719 files: 1/4 chunks (25.00%)
1719 files: 1/4 chunks (25.00%)
1720 adding foo/Bar/file.txt revisions
1720 adding foo/Bar/file.txt revisions
1721 files: 2/4 chunks (50.00%)
1721 files: 2/4 chunks (50.00%)
1722 adding foo/file.txt revisions
1722 adding foo/file.txt revisions
1723 files: 3/4 chunks (75.00%)
1723 files: 3/4 chunks (75.00%)
1724 adding quux/file.py revisions
1724 adding quux/file.py revisions
1725 files: 4/4 chunks (100.00%)
1725 files: 4/4 chunks (100.00%)
1726 added 4 changesets with 4 changes to 4 files (+1 heads)
1726 added 4 changesets with 4 changes to 4 files (+1 heads)
1727 calling hook pretxnchangegroup.acl: hgext.acl.hook
1727 calling hook pretxnchangegroup.acl: hgext.acl.hook
1728 acl: checking access for user "astro"
1728 acl: checking access for user "astro"
1729 acl: acl.allow.branches enabled, 0 entries for user astro
1729 acl: acl.allow.branches enabled, 0 entries for user astro
1730 acl: acl.deny.branches not enabled
1730 acl: acl.deny.branches not enabled
1731 acl: acl.allow not enabled
1731 acl: acl.allow not enabled
1732 acl: acl.deny not enabled
1732 acl: acl.deny not enabled
1733 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1733 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1734 transaction abort!
1734 transaction abort!
1735 rollback completed
1735 rollback completed
1736 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1736 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1737 no rollback information available
1737 no rollback information available
1738 2:fb35475503ef
1738 2:fb35475503ef
1739
1739
1740 $ do_push george
1740 $ do_push george
1741 Pushing as user george
1741 Pushing as user george
1742 hgrc = """
1742 hgrc = """
1743 [acl]
1743 [acl]
1744 sources = push
1744 sources = push
1745 [extensions]
1745 [extensions]
1746 [acl.allow.branches]
1746 [acl.allow.branches]
1747 * = george
1747 * = george
1748 """
1748 """
1749 pushing to ../b
1749 pushing to ../b
1750 query 1; heads
1750 query 1; heads
1751 searching for changes
1751 searching for changes
1752 all remote heads known locally
1752 all remote heads known locally
1753 listing keys for "bookmarks"
1753 listing keys for "bookmarks"
1754 4 changesets found
1754 4 changesets found
1755 list of changesets:
1755 list of changesets:
1756 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1756 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1757 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1757 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1758 911600dab2ae7a9baff75958b84fe606851ce955
1758 911600dab2ae7a9baff75958b84fe606851ce955
1759 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1759 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1760 adding changesets
1761 bundling: 1/4 changesets (25.00%)
1760 bundling: 1/4 changesets (25.00%)
1762 bundling: 2/4 changesets (50.00%)
1761 bundling: 2/4 changesets (50.00%)
1763 bundling: 3/4 changesets (75.00%)
1762 bundling: 3/4 changesets (75.00%)
1764 bundling: 4/4 changesets (100.00%)
1763 bundling: 4/4 changesets (100.00%)
1765 bundling: 1/4 manifests (25.00%)
1764 bundling: 1/4 manifests (25.00%)
1766 bundling: 2/4 manifests (50.00%)
1765 bundling: 2/4 manifests (50.00%)
1767 bundling: 3/4 manifests (75.00%)
1766 bundling: 3/4 manifests (75.00%)
1768 bundling: 4/4 manifests (100.00%)
1767 bundling: 4/4 manifests (100.00%)
1769 bundling: abc.txt 1/4 files (25.00%)
1768 bundling: abc.txt 1/4 files (25.00%)
1770 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1769 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1771 bundling: foo/file.txt 3/4 files (75.00%)
1770 bundling: foo/file.txt 3/4 files (75.00%)
1772 bundling: quux/file.py 4/4 files (100.00%)
1771 bundling: quux/file.py 4/4 files (100.00%)
1772 adding changesets
1773 changesets: 1 chunks
1773 changesets: 1 chunks
1774 add changeset ef1ea85a6374
1774 add changeset ef1ea85a6374
1775 changesets: 2 chunks
1775 changesets: 2 chunks
1776 add changeset f9cafe1212c8
1776 add changeset f9cafe1212c8
1777 changesets: 3 chunks
1777 changesets: 3 chunks
1778 add changeset 911600dab2ae
1778 add changeset 911600dab2ae
1779 changesets: 4 chunks
1779 changesets: 4 chunks
1780 add changeset e8fc755d4d82
1780 add changeset e8fc755d4d82
1781 adding manifests
1781 adding manifests
1782 manifests: 1/4 chunks (25.00%)
1782 manifests: 1/4 chunks (25.00%)
1783 manifests: 2/4 chunks (50.00%)
1783 manifests: 2/4 chunks (50.00%)
1784 manifests: 3/4 chunks (75.00%)
1784 manifests: 3/4 chunks (75.00%)
1785 manifests: 4/4 chunks (100.00%)
1785 manifests: 4/4 chunks (100.00%)
1786 adding file changes
1786 adding file changes
1787 adding abc.txt revisions
1787 adding abc.txt revisions
1788 files: 1/4 chunks (25.00%)
1788 files: 1/4 chunks (25.00%)
1789 adding foo/Bar/file.txt revisions
1789 adding foo/Bar/file.txt revisions
1790 files: 2/4 chunks (50.00%)
1790 files: 2/4 chunks (50.00%)
1791 adding foo/file.txt revisions
1791 adding foo/file.txt revisions
1792 files: 3/4 chunks (75.00%)
1792 files: 3/4 chunks (75.00%)
1793 adding quux/file.py revisions
1793 adding quux/file.py revisions
1794 files: 4/4 chunks (100.00%)
1794 files: 4/4 chunks (100.00%)
1795 added 4 changesets with 4 changes to 4 files (+1 heads)
1795 added 4 changesets with 4 changes to 4 files (+1 heads)
1796 calling hook pretxnchangegroup.acl: hgext.acl.hook
1796 calling hook pretxnchangegroup.acl: hgext.acl.hook
1797 acl: checking access for user "george"
1797 acl: checking access for user "george"
1798 acl: acl.allow.branches enabled, 1 entries for user george
1798 acl: acl.allow.branches enabled, 1 entries for user george
1799 acl: acl.deny.branches not enabled
1799 acl: acl.deny.branches not enabled
1800 acl: acl.allow not enabled
1800 acl: acl.allow not enabled
1801 acl: acl.deny not enabled
1801 acl: acl.deny not enabled
1802 acl: branch access granted: "ef1ea85a6374" on branch "default"
1802 acl: branch access granted: "ef1ea85a6374" on branch "default"
1803 acl: path access granted: "ef1ea85a6374"
1803 acl: path access granted: "ef1ea85a6374"
1804 acl: branch access granted: "f9cafe1212c8" on branch "default"
1804 acl: branch access granted: "f9cafe1212c8" on branch "default"
1805 acl: path access granted: "f9cafe1212c8"
1805 acl: path access granted: "f9cafe1212c8"
1806 acl: branch access granted: "911600dab2ae" on branch "default"
1806 acl: branch access granted: "911600dab2ae" on branch "default"
1807 acl: path access granted: "911600dab2ae"
1807 acl: path access granted: "911600dab2ae"
1808 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1808 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1809 acl: path access granted: "e8fc755d4d82"
1809 acl: path access granted: "e8fc755d4d82"
1810 updating the branch cache
1810 updating the branch cache
1811 listing keys for "phases"
1811 listing keys for "phases"
1812 try to push obsolete markers to remote
1812 try to push obsolete markers to remote
1813 checking for updated bookmarks
1813 checking for updated bookmarks
1814 listing keys for "bookmarks"
1814 listing keys for "bookmarks"
1815 repository tip rolled back to revision 2 (undo push)
1815 repository tip rolled back to revision 2 (undo push)
1816 2:fb35475503ef
1816 2:fb35475503ef
1817
1817
1818
1818
1819 Branch acl conflicting allow
1819 Branch acl conflicting allow
1820 asterisk ends up applying to all branches and allowing george to
1820 asterisk ends up applying to all branches and allowing george to
1821 push foobar into the remote
1821 push foobar into the remote
1822
1822
1823 $ init_config
1823 $ init_config
1824 $ echo "[acl.allow.branches]" >> $config
1824 $ echo "[acl.allow.branches]" >> $config
1825 $ echo "foobar = astro" >> $config
1825 $ echo "foobar = astro" >> $config
1826 $ echo "* = george" >> $config
1826 $ echo "* = george" >> $config
1827 $ do_push george
1827 $ do_push george
1828 Pushing as user george
1828 Pushing as user george
1829 hgrc = """
1829 hgrc = """
1830 [acl]
1830 [acl]
1831 sources = push
1831 sources = push
1832 [extensions]
1832 [extensions]
1833 [acl.allow.branches]
1833 [acl.allow.branches]
1834 foobar = astro
1834 foobar = astro
1835 * = george
1835 * = george
1836 """
1836 """
1837 pushing to ../b
1837 pushing to ../b
1838 query 1; heads
1838 query 1; heads
1839 searching for changes
1839 searching for changes
1840 all remote heads known locally
1840 all remote heads known locally
1841 listing keys for "bookmarks"
1841 listing keys for "bookmarks"
1842 4 changesets found
1842 4 changesets found
1843 list of changesets:
1843 list of changesets:
1844 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1844 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1845 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1845 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1846 911600dab2ae7a9baff75958b84fe606851ce955
1846 911600dab2ae7a9baff75958b84fe606851ce955
1847 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1847 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1848 adding changesets
1849 bundling: 1/4 changesets (25.00%)
1848 bundling: 1/4 changesets (25.00%)
1850 bundling: 2/4 changesets (50.00%)
1849 bundling: 2/4 changesets (50.00%)
1851 bundling: 3/4 changesets (75.00%)
1850 bundling: 3/4 changesets (75.00%)
1852 bundling: 4/4 changesets (100.00%)
1851 bundling: 4/4 changesets (100.00%)
1853 bundling: 1/4 manifests (25.00%)
1852 bundling: 1/4 manifests (25.00%)
1854 bundling: 2/4 manifests (50.00%)
1853 bundling: 2/4 manifests (50.00%)
1855 bundling: 3/4 manifests (75.00%)
1854 bundling: 3/4 manifests (75.00%)
1856 bundling: 4/4 manifests (100.00%)
1855 bundling: 4/4 manifests (100.00%)
1857 bundling: abc.txt 1/4 files (25.00%)
1856 bundling: abc.txt 1/4 files (25.00%)
1858 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1857 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1859 bundling: foo/file.txt 3/4 files (75.00%)
1858 bundling: foo/file.txt 3/4 files (75.00%)
1860 bundling: quux/file.py 4/4 files (100.00%)
1859 bundling: quux/file.py 4/4 files (100.00%)
1860 adding changesets
1861 changesets: 1 chunks
1861 changesets: 1 chunks
1862 add changeset ef1ea85a6374
1862 add changeset ef1ea85a6374
1863 changesets: 2 chunks
1863 changesets: 2 chunks
1864 add changeset f9cafe1212c8
1864 add changeset f9cafe1212c8
1865 changesets: 3 chunks
1865 changesets: 3 chunks
1866 add changeset 911600dab2ae
1866 add changeset 911600dab2ae
1867 changesets: 4 chunks
1867 changesets: 4 chunks
1868 add changeset e8fc755d4d82
1868 add changeset e8fc755d4d82
1869 adding manifests
1869 adding manifests
1870 manifests: 1/4 chunks (25.00%)
1870 manifests: 1/4 chunks (25.00%)
1871 manifests: 2/4 chunks (50.00%)
1871 manifests: 2/4 chunks (50.00%)
1872 manifests: 3/4 chunks (75.00%)
1872 manifests: 3/4 chunks (75.00%)
1873 manifests: 4/4 chunks (100.00%)
1873 manifests: 4/4 chunks (100.00%)
1874 adding file changes
1874 adding file changes
1875 adding abc.txt revisions
1875 adding abc.txt revisions
1876 files: 1/4 chunks (25.00%)
1876 files: 1/4 chunks (25.00%)
1877 adding foo/Bar/file.txt revisions
1877 adding foo/Bar/file.txt revisions
1878 files: 2/4 chunks (50.00%)
1878 files: 2/4 chunks (50.00%)
1879 adding foo/file.txt revisions
1879 adding foo/file.txt revisions
1880 files: 3/4 chunks (75.00%)
1880 files: 3/4 chunks (75.00%)
1881 adding quux/file.py revisions
1881 adding quux/file.py revisions
1882 files: 4/4 chunks (100.00%)
1882 files: 4/4 chunks (100.00%)
1883 added 4 changesets with 4 changes to 4 files (+1 heads)
1883 added 4 changesets with 4 changes to 4 files (+1 heads)
1884 calling hook pretxnchangegroup.acl: hgext.acl.hook
1884 calling hook pretxnchangegroup.acl: hgext.acl.hook
1885 acl: checking access for user "george"
1885 acl: checking access for user "george"
1886 acl: acl.allow.branches enabled, 1 entries for user george
1886 acl: acl.allow.branches enabled, 1 entries for user george
1887 acl: acl.deny.branches not enabled
1887 acl: acl.deny.branches not enabled
1888 acl: acl.allow not enabled
1888 acl: acl.allow not enabled
1889 acl: acl.deny not enabled
1889 acl: acl.deny not enabled
1890 acl: branch access granted: "ef1ea85a6374" on branch "default"
1890 acl: branch access granted: "ef1ea85a6374" on branch "default"
1891 acl: path access granted: "ef1ea85a6374"
1891 acl: path access granted: "ef1ea85a6374"
1892 acl: branch access granted: "f9cafe1212c8" on branch "default"
1892 acl: branch access granted: "f9cafe1212c8" on branch "default"
1893 acl: path access granted: "f9cafe1212c8"
1893 acl: path access granted: "f9cafe1212c8"
1894 acl: branch access granted: "911600dab2ae" on branch "default"
1894 acl: branch access granted: "911600dab2ae" on branch "default"
1895 acl: path access granted: "911600dab2ae"
1895 acl: path access granted: "911600dab2ae"
1896 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1896 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1897 acl: path access granted: "e8fc755d4d82"
1897 acl: path access granted: "e8fc755d4d82"
1898 updating the branch cache
1898 updating the branch cache
1899 listing keys for "phases"
1899 listing keys for "phases"
1900 try to push obsolete markers to remote
1900 try to push obsolete markers to remote
1901 checking for updated bookmarks
1901 checking for updated bookmarks
1902 listing keys for "bookmarks"
1902 listing keys for "bookmarks"
1903 repository tip rolled back to revision 2 (undo push)
1903 repository tip rolled back to revision 2 (undo push)
1904 2:fb35475503ef
1904 2:fb35475503ef
1905
1905
1906 Branch acl conflicting deny
1906 Branch acl conflicting deny
1907
1907
1908 $ init_config
1908 $ init_config
1909 $ echo "[acl.deny.branches]" >> $config
1909 $ echo "[acl.deny.branches]" >> $config
1910 $ echo "foobar = astro" >> $config
1910 $ echo "foobar = astro" >> $config
1911 $ echo "default = astro" >> $config
1911 $ echo "default = astro" >> $config
1912 $ echo "* = george" >> $config
1912 $ echo "* = george" >> $config
1913 $ do_push george
1913 $ do_push george
1914 Pushing as user george
1914 Pushing as user george
1915 hgrc = """
1915 hgrc = """
1916 [acl]
1916 [acl]
1917 sources = push
1917 sources = push
1918 [extensions]
1918 [extensions]
1919 [acl.deny.branches]
1919 [acl.deny.branches]
1920 foobar = astro
1920 foobar = astro
1921 default = astro
1921 default = astro
1922 * = george
1922 * = george
1923 """
1923 """
1924 pushing to ../b
1924 pushing to ../b
1925 query 1; heads
1925 query 1; heads
1926 searching for changes
1926 searching for changes
1927 all remote heads known locally
1927 all remote heads known locally
1928 listing keys for "bookmarks"
1928 listing keys for "bookmarks"
1929 4 changesets found
1929 4 changesets found
1930 list of changesets:
1930 list of changesets:
1931 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1931 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1932 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1932 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1933 911600dab2ae7a9baff75958b84fe606851ce955
1933 911600dab2ae7a9baff75958b84fe606851ce955
1934 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1934 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1935 adding changesets
1936 bundling: 1/4 changesets (25.00%)
1935 bundling: 1/4 changesets (25.00%)
1937 bundling: 2/4 changesets (50.00%)
1936 bundling: 2/4 changesets (50.00%)
1938 bundling: 3/4 changesets (75.00%)
1937 bundling: 3/4 changesets (75.00%)
1939 bundling: 4/4 changesets (100.00%)
1938 bundling: 4/4 changesets (100.00%)
1940 bundling: 1/4 manifests (25.00%)
1939 bundling: 1/4 manifests (25.00%)
1941 bundling: 2/4 manifests (50.00%)
1940 bundling: 2/4 manifests (50.00%)
1942 bundling: 3/4 manifests (75.00%)
1941 bundling: 3/4 manifests (75.00%)
1943 bundling: 4/4 manifests (100.00%)
1942 bundling: 4/4 manifests (100.00%)
1944 bundling: abc.txt 1/4 files (25.00%)
1943 bundling: abc.txt 1/4 files (25.00%)
1945 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1944 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1946 bundling: foo/file.txt 3/4 files (75.00%)
1945 bundling: foo/file.txt 3/4 files (75.00%)
1947 bundling: quux/file.py 4/4 files (100.00%)
1946 bundling: quux/file.py 4/4 files (100.00%)
1947 adding changesets
1948 changesets: 1 chunks
1948 changesets: 1 chunks
1949 add changeset ef1ea85a6374
1949 add changeset ef1ea85a6374
1950 changesets: 2 chunks
1950 changesets: 2 chunks
1951 add changeset f9cafe1212c8
1951 add changeset f9cafe1212c8
1952 changesets: 3 chunks
1952 changesets: 3 chunks
1953 add changeset 911600dab2ae
1953 add changeset 911600dab2ae
1954 changesets: 4 chunks
1954 changesets: 4 chunks
1955 add changeset e8fc755d4d82
1955 add changeset e8fc755d4d82
1956 adding manifests
1956 adding manifests
1957 manifests: 1/4 chunks (25.00%)
1957 manifests: 1/4 chunks (25.00%)
1958 manifests: 2/4 chunks (50.00%)
1958 manifests: 2/4 chunks (50.00%)
1959 manifests: 3/4 chunks (75.00%)
1959 manifests: 3/4 chunks (75.00%)
1960 manifests: 4/4 chunks (100.00%)
1960 manifests: 4/4 chunks (100.00%)
1961 adding file changes
1961 adding file changes
1962 adding abc.txt revisions
1962 adding abc.txt revisions
1963 files: 1/4 chunks (25.00%)
1963 files: 1/4 chunks (25.00%)
1964 adding foo/Bar/file.txt revisions
1964 adding foo/Bar/file.txt revisions
1965 files: 2/4 chunks (50.00%)
1965 files: 2/4 chunks (50.00%)
1966 adding foo/file.txt revisions
1966 adding foo/file.txt revisions
1967 files: 3/4 chunks (75.00%)
1967 files: 3/4 chunks (75.00%)
1968 adding quux/file.py revisions
1968 adding quux/file.py revisions
1969 files: 4/4 chunks (100.00%)
1969 files: 4/4 chunks (100.00%)
1970 added 4 changesets with 4 changes to 4 files (+1 heads)
1970 added 4 changesets with 4 changes to 4 files (+1 heads)
1971 calling hook pretxnchangegroup.acl: hgext.acl.hook
1971 calling hook pretxnchangegroup.acl: hgext.acl.hook
1972 acl: checking access for user "george"
1972 acl: checking access for user "george"
1973 acl: acl.allow.branches not enabled
1973 acl: acl.allow.branches not enabled
1974 acl: acl.deny.branches enabled, 1 entries for user george
1974 acl: acl.deny.branches enabled, 1 entries for user george
1975 acl: acl.allow not enabled
1975 acl: acl.allow not enabled
1976 acl: acl.deny not enabled
1976 acl: acl.deny not enabled
1977 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1977 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1978 transaction abort!
1978 transaction abort!
1979 rollback completed
1979 rollback completed
1980 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1980 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1981 no rollback information available
1981 no rollback information available
1982 2:fb35475503ef
1982 2:fb35475503ef
1983
1983
1984 User 'astro' must not be denied
1984 User 'astro' must not be denied
1985
1985
1986 $ init_config
1986 $ init_config
1987 $ echo "[acl.deny.branches]" >> $config
1987 $ echo "[acl.deny.branches]" >> $config
1988 $ echo "default = !astro" >> $config
1988 $ echo "default = !astro" >> $config
1989 $ do_push astro
1989 $ do_push astro
1990 Pushing as user astro
1990 Pushing as user astro
1991 hgrc = """
1991 hgrc = """
1992 [acl]
1992 [acl]
1993 sources = push
1993 sources = push
1994 [extensions]
1994 [extensions]
1995 [acl.deny.branches]
1995 [acl.deny.branches]
1996 default = !astro
1996 default = !astro
1997 """
1997 """
1998 pushing to ../b
1998 pushing to ../b
1999 query 1; heads
1999 query 1; heads
2000 searching for changes
2000 searching for changes
2001 all remote heads known locally
2001 all remote heads known locally
2002 listing keys for "bookmarks"
2002 listing keys for "bookmarks"
2003 4 changesets found
2003 4 changesets found
2004 list of changesets:
2004 list of changesets:
2005 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2005 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2006 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2006 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2007 911600dab2ae7a9baff75958b84fe606851ce955
2007 911600dab2ae7a9baff75958b84fe606851ce955
2008 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2008 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2009 adding changesets
2010 bundling: 1/4 changesets (25.00%)
2009 bundling: 1/4 changesets (25.00%)
2011 bundling: 2/4 changesets (50.00%)
2010 bundling: 2/4 changesets (50.00%)
2012 bundling: 3/4 changesets (75.00%)
2011 bundling: 3/4 changesets (75.00%)
2013 bundling: 4/4 changesets (100.00%)
2012 bundling: 4/4 changesets (100.00%)
2014 bundling: 1/4 manifests (25.00%)
2013 bundling: 1/4 manifests (25.00%)
2015 bundling: 2/4 manifests (50.00%)
2014 bundling: 2/4 manifests (50.00%)
2016 bundling: 3/4 manifests (75.00%)
2015 bundling: 3/4 manifests (75.00%)
2017 bundling: 4/4 manifests (100.00%)
2016 bundling: 4/4 manifests (100.00%)
2018 bundling: abc.txt 1/4 files (25.00%)
2017 bundling: abc.txt 1/4 files (25.00%)
2019 bundling: foo/Bar/file.txt 2/4 files (50.00%)
2018 bundling: foo/Bar/file.txt 2/4 files (50.00%)
2020 bundling: foo/file.txt 3/4 files (75.00%)
2019 bundling: foo/file.txt 3/4 files (75.00%)
2021 bundling: quux/file.py 4/4 files (100.00%)
2020 bundling: quux/file.py 4/4 files (100.00%)
2021 adding changesets
2022 changesets: 1 chunks
2022 changesets: 1 chunks
2023 add changeset ef1ea85a6374
2023 add changeset ef1ea85a6374
2024 changesets: 2 chunks
2024 changesets: 2 chunks
2025 add changeset f9cafe1212c8
2025 add changeset f9cafe1212c8
2026 changesets: 3 chunks
2026 changesets: 3 chunks
2027 add changeset 911600dab2ae
2027 add changeset 911600dab2ae
2028 changesets: 4 chunks
2028 changesets: 4 chunks
2029 add changeset e8fc755d4d82
2029 add changeset e8fc755d4d82
2030 adding manifests
2030 adding manifests
2031 manifests: 1/4 chunks (25.00%)
2031 manifests: 1/4 chunks (25.00%)
2032 manifests: 2/4 chunks (50.00%)
2032 manifests: 2/4 chunks (50.00%)
2033 manifests: 3/4 chunks (75.00%)
2033 manifests: 3/4 chunks (75.00%)
2034 manifests: 4/4 chunks (100.00%)
2034 manifests: 4/4 chunks (100.00%)
2035 adding file changes
2035 adding file changes
2036 adding abc.txt revisions
2036 adding abc.txt revisions
2037 files: 1/4 chunks (25.00%)
2037 files: 1/4 chunks (25.00%)
2038 adding foo/Bar/file.txt revisions
2038 adding foo/Bar/file.txt revisions
2039 files: 2/4 chunks (50.00%)
2039 files: 2/4 chunks (50.00%)
2040 adding foo/file.txt revisions
2040 adding foo/file.txt revisions
2041 files: 3/4 chunks (75.00%)
2041 files: 3/4 chunks (75.00%)
2042 adding quux/file.py revisions
2042 adding quux/file.py revisions
2043 files: 4/4 chunks (100.00%)
2043 files: 4/4 chunks (100.00%)
2044 added 4 changesets with 4 changes to 4 files (+1 heads)
2044 added 4 changesets with 4 changes to 4 files (+1 heads)
2045 calling hook pretxnchangegroup.acl: hgext.acl.hook
2045 calling hook pretxnchangegroup.acl: hgext.acl.hook
2046 acl: checking access for user "astro"
2046 acl: checking access for user "astro"
2047 acl: acl.allow.branches not enabled
2047 acl: acl.allow.branches not enabled
2048 acl: acl.deny.branches enabled, 0 entries for user astro
2048 acl: acl.deny.branches enabled, 0 entries for user astro
2049 acl: acl.allow not enabled
2049 acl: acl.allow not enabled
2050 acl: acl.deny not enabled
2050 acl: acl.deny not enabled
2051 acl: branch access granted: "ef1ea85a6374" on branch "default"
2051 acl: branch access granted: "ef1ea85a6374" on branch "default"
2052 acl: path access granted: "ef1ea85a6374"
2052 acl: path access granted: "ef1ea85a6374"
2053 acl: branch access granted: "f9cafe1212c8" on branch "default"
2053 acl: branch access granted: "f9cafe1212c8" on branch "default"
2054 acl: path access granted: "f9cafe1212c8"
2054 acl: path access granted: "f9cafe1212c8"
2055 acl: branch access granted: "911600dab2ae" on branch "default"
2055 acl: branch access granted: "911600dab2ae" on branch "default"
2056 acl: path access granted: "911600dab2ae"
2056 acl: path access granted: "911600dab2ae"
2057 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
2057 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
2058 acl: path access granted: "e8fc755d4d82"
2058 acl: path access granted: "e8fc755d4d82"
2059 updating the branch cache
2059 updating the branch cache
2060 listing keys for "phases"
2060 listing keys for "phases"
2061 try to push obsolete markers to remote
2061 try to push obsolete markers to remote
2062 checking for updated bookmarks
2062 checking for updated bookmarks
2063 listing keys for "bookmarks"
2063 listing keys for "bookmarks"
2064 repository tip rolled back to revision 2 (undo push)
2064 repository tip rolled back to revision 2 (undo push)
2065 2:fb35475503ef
2065 2:fb35475503ef
2066
2066
2067
2067
2068 Non-astro users must be denied
2068 Non-astro users must be denied
2069
2069
2070 $ do_push george
2070 $ do_push george
2071 Pushing as user george
2071 Pushing as user george
2072 hgrc = """
2072 hgrc = """
2073 [acl]
2073 [acl]
2074 sources = push
2074 sources = push
2075 [extensions]
2075 [extensions]
2076 [acl.deny.branches]
2076 [acl.deny.branches]
2077 default = !astro
2077 default = !astro
2078 """
2078 """
2079 pushing to ../b
2079 pushing to ../b
2080 query 1; heads
2080 query 1; heads
2081 searching for changes
2081 searching for changes
2082 all remote heads known locally
2082 all remote heads known locally
2083 listing keys for "bookmarks"
2083 listing keys for "bookmarks"
2084 4 changesets found
2084 4 changesets found
2085 list of changesets:
2085 list of changesets:
2086 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2086 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2087 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2087 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2088 911600dab2ae7a9baff75958b84fe606851ce955
2088 911600dab2ae7a9baff75958b84fe606851ce955
2089 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2089 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2090 adding changesets
2091 bundling: 1/4 changesets (25.00%)
2090 bundling: 1/4 changesets (25.00%)
2092 bundling: 2/4 changesets (50.00%)
2091 bundling: 2/4 changesets (50.00%)
2093 bundling: 3/4 changesets (75.00%)
2092 bundling: 3/4 changesets (75.00%)
2094 bundling: 4/4 changesets (100.00%)
2093 bundling: 4/4 changesets (100.00%)
2095 bundling: 1/4 manifests (25.00%)
2094 bundling: 1/4 manifests (25.00%)
2096 bundling: 2/4 manifests (50.00%)
2095 bundling: 2/4 manifests (50.00%)
2097 bundling: 3/4 manifests (75.00%)
2096 bundling: 3/4 manifests (75.00%)
2098 bundling: 4/4 manifests (100.00%)
2097 bundling: 4/4 manifests (100.00%)
2099 bundling: abc.txt 1/4 files (25.00%)
2098 bundling: abc.txt 1/4 files (25.00%)
2100 bundling: foo/Bar/file.txt 2/4 files (50.00%)
2099 bundling: foo/Bar/file.txt 2/4 files (50.00%)
2101 bundling: foo/file.txt 3/4 files (75.00%)
2100 bundling: foo/file.txt 3/4 files (75.00%)
2102 bundling: quux/file.py 4/4 files (100.00%)
2101 bundling: quux/file.py 4/4 files (100.00%)
2102 adding changesets
2103 changesets: 1 chunks
2103 changesets: 1 chunks
2104 add changeset ef1ea85a6374
2104 add changeset ef1ea85a6374
2105 changesets: 2 chunks
2105 changesets: 2 chunks
2106 add changeset f9cafe1212c8
2106 add changeset f9cafe1212c8
2107 changesets: 3 chunks
2107 changesets: 3 chunks
2108 add changeset 911600dab2ae
2108 add changeset 911600dab2ae
2109 changesets: 4 chunks
2109 changesets: 4 chunks
2110 add changeset e8fc755d4d82
2110 add changeset e8fc755d4d82
2111 adding manifests
2111 adding manifests
2112 manifests: 1/4 chunks (25.00%)
2112 manifests: 1/4 chunks (25.00%)
2113 manifests: 2/4 chunks (50.00%)
2113 manifests: 2/4 chunks (50.00%)
2114 manifests: 3/4 chunks (75.00%)
2114 manifests: 3/4 chunks (75.00%)
2115 manifests: 4/4 chunks (100.00%)
2115 manifests: 4/4 chunks (100.00%)
2116 adding file changes
2116 adding file changes
2117 adding abc.txt revisions
2117 adding abc.txt revisions
2118 files: 1/4 chunks (25.00%)
2118 files: 1/4 chunks (25.00%)
2119 adding foo/Bar/file.txt revisions
2119 adding foo/Bar/file.txt revisions
2120 files: 2/4 chunks (50.00%)
2120 files: 2/4 chunks (50.00%)
2121 adding foo/file.txt revisions
2121 adding foo/file.txt revisions
2122 files: 3/4 chunks (75.00%)
2122 files: 3/4 chunks (75.00%)
2123 adding quux/file.py revisions
2123 adding quux/file.py revisions
2124 files: 4/4 chunks (100.00%)
2124 files: 4/4 chunks (100.00%)
2125 added 4 changesets with 4 changes to 4 files (+1 heads)
2125 added 4 changesets with 4 changes to 4 files (+1 heads)
2126 calling hook pretxnchangegroup.acl: hgext.acl.hook
2126 calling hook pretxnchangegroup.acl: hgext.acl.hook
2127 acl: checking access for user "george"
2127 acl: checking access for user "george"
2128 acl: acl.allow.branches not enabled
2128 acl: acl.allow.branches not enabled
2129 acl: acl.deny.branches enabled, 1 entries for user george
2129 acl: acl.deny.branches enabled, 1 entries for user george
2130 acl: acl.allow not enabled
2130 acl: acl.allow not enabled
2131 acl: acl.deny not enabled
2131 acl: acl.deny not enabled
2132 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2132 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2133 transaction abort!
2133 transaction abort!
2134 rollback completed
2134 rollback completed
2135 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2135 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2136 no rollback information available
2136 no rollback information available
2137 2:fb35475503ef
2137 2:fb35475503ef
2138
2138
2139
2139
General Comments 0
You need to be logged in to leave comments. Login now