##// END OF EJS Templates
branchmap: disable fallback to unfiltered branchcache...
Pierre-Yves David -
r18222:47f00b0d default
parent child Browse files
Show More
@@ -1,2586 +1,2584 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, discovery, pushkey, obsolete, repoview
9 import peer, changegroup, subrepo, discovery, pushkey, obsolete, repoview
10 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
10 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
11 import lock, transaction, store, encoding, base85
11 import lock, transaction, store, encoding, base85
12 import scmutil, util, extensions, hook, error, revset
12 import scmutil, util, extensions, hook, error, revset
13 import match as matchmod
13 import match as matchmod
14 import merge as mergemod
14 import merge as mergemod
15 import tags as tagsmod
15 import tags as tagsmod
16 from lock import release
16 from lock import release
17 import weakref, errno, os, time, inspect
17 import weakref, errno, os, time, inspect
18 import branchmap
18 import branchmap
19 propertycache = util.propertycache
19 propertycache = util.propertycache
20 filecache = scmutil.filecache
20 filecache = scmutil.filecache
21
21
22 class repofilecache(filecache):
22 class repofilecache(filecache):
23 """All filecache usage on repo are done for logic that should be unfiltered
23 """All filecache usage on repo are done for logic that should be unfiltered
24 """
24 """
25
25
26 def __get__(self, repo, type=None):
26 def __get__(self, repo, type=None):
27 return super(repofilecache, self).__get__(repo.unfiltered(), type)
27 return super(repofilecache, self).__get__(repo.unfiltered(), type)
28 def __set__(self, repo, value):
28 def __set__(self, repo, value):
29 return super(repofilecache, self).__set__(repo.unfiltered(), value)
29 return super(repofilecache, self).__set__(repo.unfiltered(), value)
30 def __delete__(self, repo):
30 def __delete__(self, repo):
31 return super(repofilecache, self).__delete__(repo.unfiltered())
31 return super(repofilecache, self).__delete__(repo.unfiltered())
32
32
33 class storecache(repofilecache):
33 class storecache(repofilecache):
34 """filecache for files in the store"""
34 """filecache for files in the store"""
35 def join(self, obj, fname):
35 def join(self, obj, fname):
36 return obj.sjoin(fname)
36 return obj.sjoin(fname)
37
37
38 class unfilteredpropertycache(propertycache):
38 class unfilteredpropertycache(propertycache):
39 """propertycache that apply to unfiltered repo only"""
39 """propertycache that apply to unfiltered repo only"""
40
40
41 def __get__(self, repo, type=None):
41 def __get__(self, repo, type=None):
42 return super(unfilteredpropertycache, self).__get__(repo.unfiltered())
42 return super(unfilteredpropertycache, self).__get__(repo.unfiltered())
43
43
44 class filteredpropertycache(propertycache):
44 class filteredpropertycache(propertycache):
45 """propertycache that must take filtering in account"""
45 """propertycache that must take filtering in account"""
46
46
47 def cachevalue(self, obj, value):
47 def cachevalue(self, obj, value):
48 object.__setattr__(obj, self.name, value)
48 object.__setattr__(obj, self.name, value)
49
49
50
50
51 def hasunfilteredcache(repo, name):
51 def hasunfilteredcache(repo, name):
52 """check if an repo and a unfilteredproperty cached value for <name>"""
52 """check if an repo and a unfilteredproperty cached value for <name>"""
53 return name in vars(repo.unfiltered())
53 return name in vars(repo.unfiltered())
54
54
55 def unfilteredmethod(orig):
55 def unfilteredmethod(orig):
56 """decorate method that always need to be run on unfiltered version"""
56 """decorate method that always need to be run on unfiltered version"""
57 def wrapper(repo, *args, **kwargs):
57 def wrapper(repo, *args, **kwargs):
58 return orig(repo.unfiltered(), *args, **kwargs)
58 return orig(repo.unfiltered(), *args, **kwargs)
59 return wrapper
59 return wrapper
60
60
61 MODERNCAPS = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle'))
61 MODERNCAPS = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle'))
62 LEGACYCAPS = MODERNCAPS.union(set(['changegroupsubset']))
62 LEGACYCAPS = MODERNCAPS.union(set(['changegroupsubset']))
63
63
64 class localpeer(peer.peerrepository):
64 class localpeer(peer.peerrepository):
65 '''peer for a local repo; reflects only the most recent API'''
65 '''peer for a local repo; reflects only the most recent API'''
66
66
67 def __init__(self, repo, caps=MODERNCAPS):
67 def __init__(self, repo, caps=MODERNCAPS):
68 peer.peerrepository.__init__(self)
68 peer.peerrepository.__init__(self)
69 self._repo = repo
69 self._repo = repo
70 self.ui = repo.ui
70 self.ui = repo.ui
71 self._caps = repo._restrictcapabilities(caps)
71 self._caps = repo._restrictcapabilities(caps)
72 self.requirements = repo.requirements
72 self.requirements = repo.requirements
73 self.supportedformats = repo.supportedformats
73 self.supportedformats = repo.supportedformats
74
74
75 def close(self):
75 def close(self):
76 self._repo.close()
76 self._repo.close()
77
77
78 def _capabilities(self):
78 def _capabilities(self):
79 return self._caps
79 return self._caps
80
80
81 def local(self):
81 def local(self):
82 return self._repo
82 return self._repo
83
83
84 def canpush(self):
84 def canpush(self):
85 return True
85 return True
86
86
87 def url(self):
87 def url(self):
88 return self._repo.url()
88 return self._repo.url()
89
89
90 def lookup(self, key):
90 def lookup(self, key):
91 return self._repo.lookup(key)
91 return self._repo.lookup(key)
92
92
93 def branchmap(self):
93 def branchmap(self):
94 return discovery.visiblebranchmap(self._repo)
94 return discovery.visiblebranchmap(self._repo)
95
95
96 def heads(self):
96 def heads(self):
97 return discovery.visibleheads(self._repo)
97 return discovery.visibleheads(self._repo)
98
98
99 def known(self, nodes):
99 def known(self, nodes):
100 return self._repo.known(nodes)
100 return self._repo.known(nodes)
101
101
102 def getbundle(self, source, heads=None, common=None):
102 def getbundle(self, source, heads=None, common=None):
103 return self._repo.getbundle(source, heads=heads, common=common)
103 return self._repo.getbundle(source, heads=heads, common=common)
104
104
105 # TODO We might want to move the next two calls into legacypeer and add
105 # TODO We might want to move the next two calls into legacypeer and add
106 # unbundle instead.
106 # unbundle instead.
107
107
108 def lock(self):
108 def lock(self):
109 return self._repo.lock()
109 return self._repo.lock()
110
110
111 def addchangegroup(self, cg, source, url):
111 def addchangegroup(self, cg, source, url):
112 return self._repo.addchangegroup(cg, source, url)
112 return self._repo.addchangegroup(cg, source, url)
113
113
114 def pushkey(self, namespace, key, old, new):
114 def pushkey(self, namespace, key, old, new):
115 return self._repo.pushkey(namespace, key, old, new)
115 return self._repo.pushkey(namespace, key, old, new)
116
116
117 def listkeys(self, namespace):
117 def listkeys(self, namespace):
118 return self._repo.listkeys(namespace)
118 return self._repo.listkeys(namespace)
119
119
120 def debugwireargs(self, one, two, three=None, four=None, five=None):
120 def debugwireargs(self, one, two, three=None, four=None, five=None):
121 '''used to test argument passing over the wire'''
121 '''used to test argument passing over the wire'''
122 return "%s %s %s %s %s" % (one, two, three, four, five)
122 return "%s %s %s %s %s" % (one, two, three, four, five)
123
123
124 class locallegacypeer(localpeer):
124 class locallegacypeer(localpeer):
125 '''peer extension which implements legacy methods too; used for tests with
125 '''peer extension which implements legacy methods too; used for tests with
126 restricted capabilities'''
126 restricted capabilities'''
127
127
128 def __init__(self, repo):
128 def __init__(self, repo):
129 localpeer.__init__(self, repo, caps=LEGACYCAPS)
129 localpeer.__init__(self, repo, caps=LEGACYCAPS)
130
130
131 def branches(self, nodes):
131 def branches(self, nodes):
132 return self._repo.branches(nodes)
132 return self._repo.branches(nodes)
133
133
134 def between(self, pairs):
134 def between(self, pairs):
135 return self._repo.between(pairs)
135 return self._repo.between(pairs)
136
136
137 def changegroup(self, basenodes, source):
137 def changegroup(self, basenodes, source):
138 return self._repo.changegroup(basenodes, source)
138 return self._repo.changegroup(basenodes, source)
139
139
140 def changegroupsubset(self, bases, heads, source):
140 def changegroupsubset(self, bases, heads, source):
141 return self._repo.changegroupsubset(bases, heads, source)
141 return self._repo.changegroupsubset(bases, heads, source)
142
142
143 class localrepository(object):
143 class localrepository(object):
144
144
145 supportedformats = set(('revlogv1', 'generaldelta'))
145 supportedformats = set(('revlogv1', 'generaldelta'))
146 supported = supportedformats | set(('store', 'fncache', 'shared',
146 supported = supportedformats | set(('store', 'fncache', 'shared',
147 'dotencode'))
147 'dotencode'))
148 openerreqs = set(('revlogv1', 'generaldelta'))
148 openerreqs = set(('revlogv1', 'generaldelta'))
149 requirements = ['revlogv1']
149 requirements = ['revlogv1']
150 filtername = None
150 filtername = None
151
151
152 def _baserequirements(self, create):
152 def _baserequirements(self, create):
153 return self.requirements[:]
153 return self.requirements[:]
154
154
155 def __init__(self, baseui, path=None, create=False):
155 def __init__(self, baseui, path=None, create=False):
156 self.wvfs = scmutil.vfs(path, expand=True)
156 self.wvfs = scmutil.vfs(path, expand=True)
157 self.wopener = self.wvfs
157 self.wopener = self.wvfs
158 self.root = self.wvfs.base
158 self.root = self.wvfs.base
159 self.path = self.wvfs.join(".hg")
159 self.path = self.wvfs.join(".hg")
160 self.origroot = path
160 self.origroot = path
161 self.auditor = scmutil.pathauditor(self.root, self._checknested)
161 self.auditor = scmutil.pathauditor(self.root, self._checknested)
162 self.vfs = scmutil.vfs(self.path)
162 self.vfs = scmutil.vfs(self.path)
163 self.opener = self.vfs
163 self.opener = self.vfs
164 self.baseui = baseui
164 self.baseui = baseui
165 self.ui = baseui.copy()
165 self.ui = baseui.copy()
166 # A list of callback to shape the phase if no data were found.
166 # A list of callback to shape the phase if no data were found.
167 # Callback are in the form: func(repo, roots) --> processed root.
167 # Callback are in the form: func(repo, roots) --> processed root.
168 # This list it to be filled by extension during repo setup
168 # This list it to be filled by extension during repo setup
169 self._phasedefaults = []
169 self._phasedefaults = []
170 try:
170 try:
171 self.ui.readconfig(self.join("hgrc"), self.root)
171 self.ui.readconfig(self.join("hgrc"), self.root)
172 extensions.loadall(self.ui)
172 extensions.loadall(self.ui)
173 except IOError:
173 except IOError:
174 pass
174 pass
175
175
176 if not self.vfs.isdir():
176 if not self.vfs.isdir():
177 if create:
177 if create:
178 if not self.wvfs.exists():
178 if not self.wvfs.exists():
179 self.wvfs.makedirs()
179 self.wvfs.makedirs()
180 self.vfs.makedir(notindexed=True)
180 self.vfs.makedir(notindexed=True)
181 requirements = self._baserequirements(create)
181 requirements = self._baserequirements(create)
182 if self.ui.configbool('format', 'usestore', True):
182 if self.ui.configbool('format', 'usestore', True):
183 self.vfs.mkdir("store")
183 self.vfs.mkdir("store")
184 requirements.append("store")
184 requirements.append("store")
185 if self.ui.configbool('format', 'usefncache', True):
185 if self.ui.configbool('format', 'usefncache', True):
186 requirements.append("fncache")
186 requirements.append("fncache")
187 if self.ui.configbool('format', 'dotencode', True):
187 if self.ui.configbool('format', 'dotencode', True):
188 requirements.append('dotencode')
188 requirements.append('dotencode')
189 # create an invalid changelog
189 # create an invalid changelog
190 self.vfs.append(
190 self.vfs.append(
191 "00changelog.i",
191 "00changelog.i",
192 '\0\0\0\2' # represents revlogv2
192 '\0\0\0\2' # represents revlogv2
193 ' dummy changelog to prevent using the old repo layout'
193 ' dummy changelog to prevent using the old repo layout'
194 )
194 )
195 if self.ui.configbool('format', 'generaldelta', False):
195 if self.ui.configbool('format', 'generaldelta', False):
196 requirements.append("generaldelta")
196 requirements.append("generaldelta")
197 requirements = set(requirements)
197 requirements = set(requirements)
198 else:
198 else:
199 raise error.RepoError(_("repository %s not found") % path)
199 raise error.RepoError(_("repository %s not found") % path)
200 elif create:
200 elif create:
201 raise error.RepoError(_("repository %s already exists") % path)
201 raise error.RepoError(_("repository %s already exists") % path)
202 else:
202 else:
203 try:
203 try:
204 requirements = scmutil.readrequires(self.vfs, self.supported)
204 requirements = scmutil.readrequires(self.vfs, self.supported)
205 except IOError, inst:
205 except IOError, inst:
206 if inst.errno != errno.ENOENT:
206 if inst.errno != errno.ENOENT:
207 raise
207 raise
208 requirements = set()
208 requirements = set()
209
209
210 self.sharedpath = self.path
210 self.sharedpath = self.path
211 try:
211 try:
212 s = os.path.realpath(self.opener.read("sharedpath").rstrip('\n'))
212 s = os.path.realpath(self.opener.read("sharedpath").rstrip('\n'))
213 if not os.path.exists(s):
213 if not os.path.exists(s):
214 raise error.RepoError(
214 raise error.RepoError(
215 _('.hg/sharedpath points to nonexistent directory %s') % s)
215 _('.hg/sharedpath points to nonexistent directory %s') % s)
216 self.sharedpath = s
216 self.sharedpath = s
217 except IOError, inst:
217 except IOError, inst:
218 if inst.errno != errno.ENOENT:
218 if inst.errno != errno.ENOENT:
219 raise
219 raise
220
220
221 self.store = store.store(requirements, self.sharedpath, scmutil.vfs)
221 self.store = store.store(requirements, self.sharedpath, scmutil.vfs)
222 self.spath = self.store.path
222 self.spath = self.store.path
223 self.svfs = self.store.vfs
223 self.svfs = self.store.vfs
224 self.sopener = self.svfs
224 self.sopener = self.svfs
225 self.sjoin = self.store.join
225 self.sjoin = self.store.join
226 self.vfs.createmode = self.store.createmode
226 self.vfs.createmode = self.store.createmode
227 self._applyrequirements(requirements)
227 self._applyrequirements(requirements)
228 if create:
228 if create:
229 self._writerequirements()
229 self._writerequirements()
230
230
231
231
232 self._branchcaches = {}
232 self._branchcaches = {}
233 self.filterpats = {}
233 self.filterpats = {}
234 self._datafilters = {}
234 self._datafilters = {}
235 self._transref = self._lockref = self._wlockref = None
235 self._transref = self._lockref = self._wlockref = None
236
236
237 # A cache for various files under .hg/ that tracks file changes,
237 # A cache for various files under .hg/ that tracks file changes,
238 # (used by the filecache decorator)
238 # (used by the filecache decorator)
239 #
239 #
240 # Maps a property name to its util.filecacheentry
240 # Maps a property name to its util.filecacheentry
241 self._filecache = {}
241 self._filecache = {}
242
242
243 # hold sets of revision to be filtered
243 # hold sets of revision to be filtered
244 # should be cleared when something might have changed the filter value:
244 # should be cleared when something might have changed the filter value:
245 # - new changesets,
245 # - new changesets,
246 # - phase change,
246 # - phase change,
247 # - new obsolescence marker,
247 # - new obsolescence marker,
248 # - working directory parent change,
248 # - working directory parent change,
249 # - bookmark changes
249 # - bookmark changes
250 self.filteredrevcache = {}
250 self.filteredrevcache = {}
251
251
252 def close(self):
252 def close(self):
253 pass
253 pass
254
254
255 def _restrictcapabilities(self, caps):
255 def _restrictcapabilities(self, caps):
256 return caps
256 return caps
257
257
258 def _applyrequirements(self, requirements):
258 def _applyrequirements(self, requirements):
259 self.requirements = requirements
259 self.requirements = requirements
260 self.sopener.options = dict((r, 1) for r in requirements
260 self.sopener.options = dict((r, 1) for r in requirements
261 if r in self.openerreqs)
261 if r in self.openerreqs)
262
262
263 def _writerequirements(self):
263 def _writerequirements(self):
264 reqfile = self.opener("requires", "w")
264 reqfile = self.opener("requires", "w")
265 for r in self.requirements:
265 for r in self.requirements:
266 reqfile.write("%s\n" % r)
266 reqfile.write("%s\n" % r)
267 reqfile.close()
267 reqfile.close()
268
268
269 def _checknested(self, path):
269 def _checknested(self, path):
270 """Determine if path is a legal nested repository."""
270 """Determine if path is a legal nested repository."""
271 if not path.startswith(self.root):
271 if not path.startswith(self.root):
272 return False
272 return False
273 subpath = path[len(self.root) + 1:]
273 subpath = path[len(self.root) + 1:]
274 normsubpath = util.pconvert(subpath)
274 normsubpath = util.pconvert(subpath)
275
275
276 # XXX: Checking against the current working copy is wrong in
276 # XXX: Checking against the current working copy is wrong in
277 # the sense that it can reject things like
277 # the sense that it can reject things like
278 #
278 #
279 # $ hg cat -r 10 sub/x.txt
279 # $ hg cat -r 10 sub/x.txt
280 #
280 #
281 # if sub/ is no longer a subrepository in the working copy
281 # if sub/ is no longer a subrepository in the working copy
282 # parent revision.
282 # parent revision.
283 #
283 #
284 # However, it can of course also allow things that would have
284 # However, it can of course also allow things that would have
285 # been rejected before, such as the above cat command if sub/
285 # been rejected before, such as the above cat command if sub/
286 # is a subrepository now, but was a normal directory before.
286 # is a subrepository now, but was a normal directory before.
287 # The old path auditor would have rejected by mistake since it
287 # The old path auditor would have rejected by mistake since it
288 # panics when it sees sub/.hg/.
288 # panics when it sees sub/.hg/.
289 #
289 #
290 # All in all, checking against the working copy seems sensible
290 # All in all, checking against the working copy seems sensible
291 # since we want to prevent access to nested repositories on
291 # since we want to prevent access to nested repositories on
292 # the filesystem *now*.
292 # the filesystem *now*.
293 ctx = self[None]
293 ctx = self[None]
294 parts = util.splitpath(subpath)
294 parts = util.splitpath(subpath)
295 while parts:
295 while parts:
296 prefix = '/'.join(parts)
296 prefix = '/'.join(parts)
297 if prefix in ctx.substate:
297 if prefix in ctx.substate:
298 if prefix == normsubpath:
298 if prefix == normsubpath:
299 return True
299 return True
300 else:
300 else:
301 sub = ctx.sub(prefix)
301 sub = ctx.sub(prefix)
302 return sub.checknested(subpath[len(prefix) + 1:])
302 return sub.checknested(subpath[len(prefix) + 1:])
303 else:
303 else:
304 parts.pop()
304 parts.pop()
305 return False
305 return False
306
306
307 def peer(self):
307 def peer(self):
308 return localpeer(self) # not cached to avoid reference cycle
308 return localpeer(self) # not cached to avoid reference cycle
309
309
310 def unfiltered(self):
310 def unfiltered(self):
311 """Return unfiltered version of the repository
311 """Return unfiltered version of the repository
312
312
313 Intended to be ovewritten by filtered repo."""
313 Intended to be ovewritten by filtered repo."""
314 return self
314 return self
315
315
316 def filtered(self, name):
316 def filtered(self, name):
317 """Return a filtered version of a repository"""
317 """Return a filtered version of a repository"""
318 # build a new class with the mixin and the current class
318 # build a new class with the mixin and the current class
319 # (possibily subclass of the repo)
319 # (possibily subclass of the repo)
320 class proxycls(repoview.repoview, self.unfiltered().__class__):
320 class proxycls(repoview.repoview, self.unfiltered().__class__):
321 pass
321 pass
322 return proxycls(self, name)
322 return proxycls(self, name)
323
323
324 @repofilecache('bookmarks')
324 @repofilecache('bookmarks')
325 def _bookmarks(self):
325 def _bookmarks(self):
326 return bookmarks.bmstore(self)
326 return bookmarks.bmstore(self)
327
327
328 @repofilecache('bookmarks.current')
328 @repofilecache('bookmarks.current')
329 def _bookmarkcurrent(self):
329 def _bookmarkcurrent(self):
330 return bookmarks.readcurrent(self)
330 return bookmarks.readcurrent(self)
331
331
332 def bookmarkheads(self, bookmark):
332 def bookmarkheads(self, bookmark):
333 name = bookmark.split('@', 1)[0]
333 name = bookmark.split('@', 1)[0]
334 heads = []
334 heads = []
335 for mark, n in self._bookmarks.iteritems():
335 for mark, n in self._bookmarks.iteritems():
336 if mark.split('@', 1)[0] == name:
336 if mark.split('@', 1)[0] == name:
337 heads.append(n)
337 heads.append(n)
338 return heads
338 return heads
339
339
340 @storecache('phaseroots')
340 @storecache('phaseroots')
341 def _phasecache(self):
341 def _phasecache(self):
342 return phases.phasecache(self, self._phasedefaults)
342 return phases.phasecache(self, self._phasedefaults)
343
343
344 @storecache('obsstore')
344 @storecache('obsstore')
345 def obsstore(self):
345 def obsstore(self):
346 store = obsolete.obsstore(self.sopener)
346 store = obsolete.obsstore(self.sopener)
347 if store and not obsolete._enabled:
347 if store and not obsolete._enabled:
348 # message is rare enough to not be translated
348 # message is rare enough to not be translated
349 msg = 'obsolete feature not enabled but %i markers found!\n'
349 msg = 'obsolete feature not enabled but %i markers found!\n'
350 self.ui.warn(msg % len(list(store)))
350 self.ui.warn(msg % len(list(store)))
351 return store
351 return store
352
352
353 @unfilteredpropertycache
353 @unfilteredpropertycache
354 def hiddenrevs(self):
354 def hiddenrevs(self):
355 """hiddenrevs: revs that should be hidden by command and tools
355 """hiddenrevs: revs that should be hidden by command and tools
356
356
357 This set is carried on the repo to ease initialization and lazy
357 This set is carried on the repo to ease initialization and lazy
358 loading; it'll probably move back to changelog for efficiency and
358 loading; it'll probably move back to changelog for efficiency and
359 consistency reasons.
359 consistency reasons.
360
360
361 Note that the hiddenrevs will needs invalidations when
361 Note that the hiddenrevs will needs invalidations when
362 - a new changesets is added (possible unstable above extinct)
362 - a new changesets is added (possible unstable above extinct)
363 - a new obsolete marker is added (possible new extinct changeset)
363 - a new obsolete marker is added (possible new extinct changeset)
364
364
365 hidden changesets cannot have non-hidden descendants
365 hidden changesets cannot have non-hidden descendants
366 """
366 """
367 hidden = set()
367 hidden = set()
368 if self.obsstore:
368 if self.obsstore:
369 ### hide extinct changeset that are not accessible by any mean
369 ### hide extinct changeset that are not accessible by any mean
370 hiddenquery = 'extinct() - ::(. + bookmark())'
370 hiddenquery = 'extinct() - ::(. + bookmark())'
371 hidden.update(self.revs(hiddenquery))
371 hidden.update(self.revs(hiddenquery))
372 return hidden
372 return hidden
373
373
374 @storecache('00changelog.i')
374 @storecache('00changelog.i')
375 def changelog(self):
375 def changelog(self):
376 c = changelog.changelog(self.sopener)
376 c = changelog.changelog(self.sopener)
377 if 'HG_PENDING' in os.environ:
377 if 'HG_PENDING' in os.environ:
378 p = os.environ['HG_PENDING']
378 p = os.environ['HG_PENDING']
379 if p.startswith(self.root):
379 if p.startswith(self.root):
380 c.readpending('00changelog.i.a')
380 c.readpending('00changelog.i.a')
381 return c
381 return c
382
382
383 @storecache('00manifest.i')
383 @storecache('00manifest.i')
384 def manifest(self):
384 def manifest(self):
385 return manifest.manifest(self.sopener)
385 return manifest.manifest(self.sopener)
386
386
387 @repofilecache('dirstate')
387 @repofilecache('dirstate')
388 def dirstate(self):
388 def dirstate(self):
389 warned = [0]
389 warned = [0]
390 def validate(node):
390 def validate(node):
391 try:
391 try:
392 self.changelog.rev(node)
392 self.changelog.rev(node)
393 return node
393 return node
394 except error.LookupError:
394 except error.LookupError:
395 if not warned[0]:
395 if not warned[0]:
396 warned[0] = True
396 warned[0] = True
397 self.ui.warn(_("warning: ignoring unknown"
397 self.ui.warn(_("warning: ignoring unknown"
398 " working parent %s!\n") % short(node))
398 " working parent %s!\n") % short(node))
399 return nullid
399 return nullid
400
400
401 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
401 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
402
402
403 def __getitem__(self, changeid):
403 def __getitem__(self, changeid):
404 if changeid is None:
404 if changeid is None:
405 return context.workingctx(self)
405 return context.workingctx(self)
406 return context.changectx(self, changeid)
406 return context.changectx(self, changeid)
407
407
408 def __contains__(self, changeid):
408 def __contains__(self, changeid):
409 try:
409 try:
410 return bool(self.lookup(changeid))
410 return bool(self.lookup(changeid))
411 except error.RepoLookupError:
411 except error.RepoLookupError:
412 return False
412 return False
413
413
414 def __nonzero__(self):
414 def __nonzero__(self):
415 return True
415 return True
416
416
417 def __len__(self):
417 def __len__(self):
418 return len(self.changelog)
418 return len(self.changelog)
419
419
420 def __iter__(self):
420 def __iter__(self):
421 return iter(self.changelog)
421 return iter(self.changelog)
422
422
423 def revs(self, expr, *args):
423 def revs(self, expr, *args):
424 '''Return a list of revisions matching the given revset'''
424 '''Return a list of revisions matching the given revset'''
425 expr = revset.formatspec(expr, *args)
425 expr = revset.formatspec(expr, *args)
426 m = revset.match(None, expr)
426 m = revset.match(None, expr)
427 return [r for r in m(self, list(self))]
427 return [r for r in m(self, list(self))]
428
428
429 def set(self, expr, *args):
429 def set(self, expr, *args):
430 '''
430 '''
431 Yield a context for each matching revision, after doing arg
431 Yield a context for each matching revision, after doing arg
432 replacement via revset.formatspec
432 replacement via revset.formatspec
433 '''
433 '''
434 for r in self.revs(expr, *args):
434 for r in self.revs(expr, *args):
435 yield self[r]
435 yield self[r]
436
436
437 def url(self):
437 def url(self):
438 return 'file:' + self.root
438 return 'file:' + self.root
439
439
440 def hook(self, name, throw=False, **args):
440 def hook(self, name, throw=False, **args):
441 return hook.hook(self.ui, self, name, throw, **args)
441 return hook.hook(self.ui, self, name, throw, **args)
442
442
443 @unfilteredmethod
443 @unfilteredmethod
444 def _tag(self, names, node, message, local, user, date, extra={}):
444 def _tag(self, names, node, message, local, user, date, extra={}):
445 if isinstance(names, str):
445 if isinstance(names, str):
446 names = (names,)
446 names = (names,)
447
447
448 branches = self.branchmap()
448 branches = self.branchmap()
449 for name in names:
449 for name in names:
450 self.hook('pretag', throw=True, node=hex(node), tag=name,
450 self.hook('pretag', throw=True, node=hex(node), tag=name,
451 local=local)
451 local=local)
452 if name in branches:
452 if name in branches:
453 self.ui.warn(_("warning: tag %s conflicts with existing"
453 self.ui.warn(_("warning: tag %s conflicts with existing"
454 " branch name\n") % name)
454 " branch name\n") % name)
455
455
456 def writetags(fp, names, munge, prevtags):
456 def writetags(fp, names, munge, prevtags):
457 fp.seek(0, 2)
457 fp.seek(0, 2)
458 if prevtags and prevtags[-1] != '\n':
458 if prevtags and prevtags[-1] != '\n':
459 fp.write('\n')
459 fp.write('\n')
460 for name in names:
460 for name in names:
461 m = munge and munge(name) or name
461 m = munge and munge(name) or name
462 if (self._tagscache.tagtypes and
462 if (self._tagscache.tagtypes and
463 name in self._tagscache.tagtypes):
463 name in self._tagscache.tagtypes):
464 old = self.tags().get(name, nullid)
464 old = self.tags().get(name, nullid)
465 fp.write('%s %s\n' % (hex(old), m))
465 fp.write('%s %s\n' % (hex(old), m))
466 fp.write('%s %s\n' % (hex(node), m))
466 fp.write('%s %s\n' % (hex(node), m))
467 fp.close()
467 fp.close()
468
468
469 prevtags = ''
469 prevtags = ''
470 if local:
470 if local:
471 try:
471 try:
472 fp = self.opener('localtags', 'r+')
472 fp = self.opener('localtags', 'r+')
473 except IOError:
473 except IOError:
474 fp = self.opener('localtags', 'a')
474 fp = self.opener('localtags', 'a')
475 else:
475 else:
476 prevtags = fp.read()
476 prevtags = fp.read()
477
477
478 # local tags are stored in the current charset
478 # local tags are stored in the current charset
479 writetags(fp, names, None, prevtags)
479 writetags(fp, names, None, prevtags)
480 for name in names:
480 for name in names:
481 self.hook('tag', node=hex(node), tag=name, local=local)
481 self.hook('tag', node=hex(node), tag=name, local=local)
482 return
482 return
483
483
484 try:
484 try:
485 fp = self.wfile('.hgtags', 'rb+')
485 fp = self.wfile('.hgtags', 'rb+')
486 except IOError, e:
486 except IOError, e:
487 if e.errno != errno.ENOENT:
487 if e.errno != errno.ENOENT:
488 raise
488 raise
489 fp = self.wfile('.hgtags', 'ab')
489 fp = self.wfile('.hgtags', 'ab')
490 else:
490 else:
491 prevtags = fp.read()
491 prevtags = fp.read()
492
492
493 # committed tags are stored in UTF-8
493 # committed tags are stored in UTF-8
494 writetags(fp, names, encoding.fromlocal, prevtags)
494 writetags(fp, names, encoding.fromlocal, prevtags)
495
495
496 fp.close()
496 fp.close()
497
497
498 self.invalidatecaches()
498 self.invalidatecaches()
499
499
500 if '.hgtags' not in self.dirstate:
500 if '.hgtags' not in self.dirstate:
501 self[None].add(['.hgtags'])
501 self[None].add(['.hgtags'])
502
502
503 m = matchmod.exact(self.root, '', ['.hgtags'])
503 m = matchmod.exact(self.root, '', ['.hgtags'])
504 tagnode = self.commit(message, user, date, extra=extra, match=m)
504 tagnode = self.commit(message, user, date, extra=extra, match=m)
505
505
506 for name in names:
506 for name in names:
507 self.hook('tag', node=hex(node), tag=name, local=local)
507 self.hook('tag', node=hex(node), tag=name, local=local)
508
508
509 return tagnode
509 return tagnode
510
510
511 def tag(self, names, node, message, local, user, date):
511 def tag(self, names, node, message, local, user, date):
512 '''tag a revision with one or more symbolic names.
512 '''tag a revision with one or more symbolic names.
513
513
514 names is a list of strings or, when adding a single tag, names may be a
514 names is a list of strings or, when adding a single tag, names may be a
515 string.
515 string.
516
516
517 if local is True, the tags are stored in a per-repository file.
517 if local is True, the tags are stored in a per-repository file.
518 otherwise, they are stored in the .hgtags file, and a new
518 otherwise, they are stored in the .hgtags file, and a new
519 changeset is committed with the change.
519 changeset is committed with the change.
520
520
521 keyword arguments:
521 keyword arguments:
522
522
523 local: whether to store tags in non-version-controlled file
523 local: whether to store tags in non-version-controlled file
524 (default False)
524 (default False)
525
525
526 message: commit message to use if committing
526 message: commit message to use if committing
527
527
528 user: name of user to use if committing
528 user: name of user to use if committing
529
529
530 date: date tuple to use if committing'''
530 date: date tuple to use if committing'''
531
531
532 if not local:
532 if not local:
533 for x in self.status()[:5]:
533 for x in self.status()[:5]:
534 if '.hgtags' in x:
534 if '.hgtags' in x:
535 raise util.Abort(_('working copy of .hgtags is changed '
535 raise util.Abort(_('working copy of .hgtags is changed '
536 '(please commit .hgtags manually)'))
536 '(please commit .hgtags manually)'))
537
537
538 self.tags() # instantiate the cache
538 self.tags() # instantiate the cache
539 self._tag(names, node, message, local, user, date)
539 self._tag(names, node, message, local, user, date)
540
540
541 @filteredpropertycache
541 @filteredpropertycache
542 def _tagscache(self):
542 def _tagscache(self):
543 '''Returns a tagscache object that contains various tags related
543 '''Returns a tagscache object that contains various tags related
544 caches.'''
544 caches.'''
545
545
546 # This simplifies its cache management by having one decorated
546 # This simplifies its cache management by having one decorated
547 # function (this one) and the rest simply fetch things from it.
547 # function (this one) and the rest simply fetch things from it.
548 class tagscache(object):
548 class tagscache(object):
549 def __init__(self):
549 def __init__(self):
550 # These two define the set of tags for this repository. tags
550 # These two define the set of tags for this repository. tags
551 # maps tag name to node; tagtypes maps tag name to 'global' or
551 # maps tag name to node; tagtypes maps tag name to 'global' or
552 # 'local'. (Global tags are defined by .hgtags across all
552 # 'local'. (Global tags are defined by .hgtags across all
553 # heads, and local tags are defined in .hg/localtags.)
553 # heads, and local tags are defined in .hg/localtags.)
554 # They constitute the in-memory cache of tags.
554 # They constitute the in-memory cache of tags.
555 self.tags = self.tagtypes = None
555 self.tags = self.tagtypes = None
556
556
557 self.nodetagscache = self.tagslist = None
557 self.nodetagscache = self.tagslist = None
558
558
559 cache = tagscache()
559 cache = tagscache()
560 cache.tags, cache.tagtypes = self._findtags()
560 cache.tags, cache.tagtypes = self._findtags()
561
561
562 return cache
562 return cache
563
563
564 def tags(self):
564 def tags(self):
565 '''return a mapping of tag to node'''
565 '''return a mapping of tag to node'''
566 t = {}
566 t = {}
567 if self.changelog.filteredrevs:
567 if self.changelog.filteredrevs:
568 tags, tt = self._findtags()
568 tags, tt = self._findtags()
569 else:
569 else:
570 tags = self._tagscache.tags
570 tags = self._tagscache.tags
571 for k, v in tags.iteritems():
571 for k, v in tags.iteritems():
572 try:
572 try:
573 # ignore tags to unknown nodes
573 # ignore tags to unknown nodes
574 self.changelog.rev(v)
574 self.changelog.rev(v)
575 t[k] = v
575 t[k] = v
576 except (error.LookupError, ValueError):
576 except (error.LookupError, ValueError):
577 pass
577 pass
578 return t
578 return t
579
579
580 def _findtags(self):
580 def _findtags(self):
581 '''Do the hard work of finding tags. Return a pair of dicts
581 '''Do the hard work of finding tags. Return a pair of dicts
582 (tags, tagtypes) where tags maps tag name to node, and tagtypes
582 (tags, tagtypes) where tags maps tag name to node, and tagtypes
583 maps tag name to a string like \'global\' or \'local\'.
583 maps tag name to a string like \'global\' or \'local\'.
584 Subclasses or extensions are free to add their own tags, but
584 Subclasses or extensions are free to add their own tags, but
585 should be aware that the returned dicts will be retained for the
585 should be aware that the returned dicts will be retained for the
586 duration of the localrepo object.'''
586 duration of the localrepo object.'''
587
587
588 # XXX what tagtype should subclasses/extensions use? Currently
588 # XXX what tagtype should subclasses/extensions use? Currently
589 # mq and bookmarks add tags, but do not set the tagtype at all.
589 # mq and bookmarks add tags, but do not set the tagtype at all.
590 # Should each extension invent its own tag type? Should there
590 # Should each extension invent its own tag type? Should there
591 # be one tagtype for all such "virtual" tags? Or is the status
591 # be one tagtype for all such "virtual" tags? Or is the status
592 # quo fine?
592 # quo fine?
593
593
594 alltags = {} # map tag name to (node, hist)
594 alltags = {} # map tag name to (node, hist)
595 tagtypes = {}
595 tagtypes = {}
596
596
597 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
597 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
598 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
598 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
599
599
600 # Build the return dicts. Have to re-encode tag names because
600 # Build the return dicts. Have to re-encode tag names because
601 # the tags module always uses UTF-8 (in order not to lose info
601 # the tags module always uses UTF-8 (in order not to lose info
602 # writing to the cache), but the rest of Mercurial wants them in
602 # writing to the cache), but the rest of Mercurial wants them in
603 # local encoding.
603 # local encoding.
604 tags = {}
604 tags = {}
605 for (name, (node, hist)) in alltags.iteritems():
605 for (name, (node, hist)) in alltags.iteritems():
606 if node != nullid:
606 if node != nullid:
607 tags[encoding.tolocal(name)] = node
607 tags[encoding.tolocal(name)] = node
608 tags['tip'] = self.changelog.tip()
608 tags['tip'] = self.changelog.tip()
609 tagtypes = dict([(encoding.tolocal(name), value)
609 tagtypes = dict([(encoding.tolocal(name), value)
610 for (name, value) in tagtypes.iteritems()])
610 for (name, value) in tagtypes.iteritems()])
611 return (tags, tagtypes)
611 return (tags, tagtypes)
612
612
613 def tagtype(self, tagname):
613 def tagtype(self, tagname):
614 '''
614 '''
615 return the type of the given tag. result can be:
615 return the type of the given tag. result can be:
616
616
617 'local' : a local tag
617 'local' : a local tag
618 'global' : a global tag
618 'global' : a global tag
619 None : tag does not exist
619 None : tag does not exist
620 '''
620 '''
621
621
622 return self._tagscache.tagtypes.get(tagname)
622 return self._tagscache.tagtypes.get(tagname)
623
623
624 def tagslist(self):
624 def tagslist(self):
625 '''return a list of tags ordered by revision'''
625 '''return a list of tags ordered by revision'''
626 if not self._tagscache.tagslist:
626 if not self._tagscache.tagslist:
627 l = []
627 l = []
628 for t, n in self.tags().iteritems():
628 for t, n in self.tags().iteritems():
629 r = self.changelog.rev(n)
629 r = self.changelog.rev(n)
630 l.append((r, t, n))
630 l.append((r, t, n))
631 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
631 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
632
632
633 return self._tagscache.tagslist
633 return self._tagscache.tagslist
634
634
635 def nodetags(self, node):
635 def nodetags(self, node):
636 '''return the tags associated with a node'''
636 '''return the tags associated with a node'''
637 if not self._tagscache.nodetagscache:
637 if not self._tagscache.nodetagscache:
638 nodetagscache = {}
638 nodetagscache = {}
639 for t, n in self._tagscache.tags.iteritems():
639 for t, n in self._tagscache.tags.iteritems():
640 nodetagscache.setdefault(n, []).append(t)
640 nodetagscache.setdefault(n, []).append(t)
641 for tags in nodetagscache.itervalues():
641 for tags in nodetagscache.itervalues():
642 tags.sort()
642 tags.sort()
643 self._tagscache.nodetagscache = nodetagscache
643 self._tagscache.nodetagscache = nodetagscache
644 return self._tagscache.nodetagscache.get(node, [])
644 return self._tagscache.nodetagscache.get(node, [])
645
645
646 def nodebookmarks(self, node):
646 def nodebookmarks(self, node):
647 marks = []
647 marks = []
648 for bookmark, n in self._bookmarks.iteritems():
648 for bookmark, n in self._bookmarks.iteritems():
649 if n == node:
649 if n == node:
650 marks.append(bookmark)
650 marks.append(bookmark)
651 return sorted(marks)
651 return sorted(marks)
652
652
653 def branchmap(self):
653 def branchmap(self):
654 '''returns a dictionary {branch: [branchheads]}'''
654 '''returns a dictionary {branch: [branchheads]}'''
655 if self.filtername and not self.changelog.filteredrevs:
656 return self.unfiltered().branchmap()
657 branchmap.updatecache(self)
655 branchmap.updatecache(self)
658 return self._branchcaches[self.filtername]
656 return self._branchcaches[self.filtername]
659
657
660
658
661 def _branchtip(self, heads):
659 def _branchtip(self, heads):
662 '''return the tipmost branch head in heads'''
660 '''return the tipmost branch head in heads'''
663 tip = heads[-1]
661 tip = heads[-1]
664 for h in reversed(heads):
662 for h in reversed(heads):
665 if not self[h].closesbranch():
663 if not self[h].closesbranch():
666 tip = h
664 tip = h
667 break
665 break
668 return tip
666 return tip
669
667
670 def branchtip(self, branch):
668 def branchtip(self, branch):
671 '''return the tip node for a given branch'''
669 '''return the tip node for a given branch'''
672 if branch not in self.branchmap():
670 if branch not in self.branchmap():
673 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
671 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
674 return self._branchtip(self.branchmap()[branch])
672 return self._branchtip(self.branchmap()[branch])
675
673
676 def branchtags(self):
674 def branchtags(self):
677 '''return a dict where branch names map to the tipmost head of
675 '''return a dict where branch names map to the tipmost head of
678 the branch, open heads come before closed'''
676 the branch, open heads come before closed'''
679 bt = {}
677 bt = {}
680 for bn, heads in self.branchmap().iteritems():
678 for bn, heads in self.branchmap().iteritems():
681 bt[bn] = self._branchtip(heads)
679 bt[bn] = self._branchtip(heads)
682 return bt
680 return bt
683
681
684 def lookup(self, key):
682 def lookup(self, key):
685 return self[key].node()
683 return self[key].node()
686
684
687 def lookupbranch(self, key, remote=None):
685 def lookupbranch(self, key, remote=None):
688 repo = remote or self
686 repo = remote or self
689 if key in repo.branchmap():
687 if key in repo.branchmap():
690 return key
688 return key
691
689
692 repo = (remote and remote.local()) and remote or self
690 repo = (remote and remote.local()) and remote or self
693 return repo[key].branch()
691 return repo[key].branch()
694
692
695 def known(self, nodes):
693 def known(self, nodes):
696 nm = self.changelog.nodemap
694 nm = self.changelog.nodemap
697 pc = self._phasecache
695 pc = self._phasecache
698 result = []
696 result = []
699 for n in nodes:
697 for n in nodes:
700 r = nm.get(n)
698 r = nm.get(n)
701 resp = not (r is None or pc.phase(self, r) >= phases.secret)
699 resp = not (r is None or pc.phase(self, r) >= phases.secret)
702 result.append(resp)
700 result.append(resp)
703 return result
701 return result
704
702
705 def local(self):
703 def local(self):
706 return self
704 return self
707
705
708 def cancopy(self):
706 def cancopy(self):
709 return self.local() # so statichttprepo's override of local() works
707 return self.local() # so statichttprepo's override of local() works
710
708
711 def join(self, f):
709 def join(self, f):
712 return os.path.join(self.path, f)
710 return os.path.join(self.path, f)
713
711
714 def wjoin(self, f):
712 def wjoin(self, f):
715 return os.path.join(self.root, f)
713 return os.path.join(self.root, f)
716
714
717 def file(self, f):
715 def file(self, f):
718 if f[0] == '/':
716 if f[0] == '/':
719 f = f[1:]
717 f = f[1:]
720 return filelog.filelog(self.sopener, f)
718 return filelog.filelog(self.sopener, f)
721
719
722 def changectx(self, changeid):
720 def changectx(self, changeid):
723 return self[changeid]
721 return self[changeid]
724
722
725 def parents(self, changeid=None):
723 def parents(self, changeid=None):
726 '''get list of changectxs for parents of changeid'''
724 '''get list of changectxs for parents of changeid'''
727 return self[changeid].parents()
725 return self[changeid].parents()
728
726
729 def setparents(self, p1, p2=nullid):
727 def setparents(self, p1, p2=nullid):
730 copies = self.dirstate.setparents(p1, p2)
728 copies = self.dirstate.setparents(p1, p2)
731 if copies:
729 if copies:
732 # Adjust copy records, the dirstate cannot do it, it
730 # Adjust copy records, the dirstate cannot do it, it
733 # requires access to parents manifests. Preserve them
731 # requires access to parents manifests. Preserve them
734 # only for entries added to first parent.
732 # only for entries added to first parent.
735 pctx = self[p1]
733 pctx = self[p1]
736 for f in copies:
734 for f in copies:
737 if f not in pctx and copies[f] in pctx:
735 if f not in pctx and copies[f] in pctx:
738 self.dirstate.copy(copies[f], f)
736 self.dirstate.copy(copies[f], f)
739
737
740 def filectx(self, path, changeid=None, fileid=None):
738 def filectx(self, path, changeid=None, fileid=None):
741 """changeid can be a changeset revision, node, or tag.
739 """changeid can be a changeset revision, node, or tag.
742 fileid can be a file revision or node."""
740 fileid can be a file revision or node."""
743 return context.filectx(self, path, changeid, fileid)
741 return context.filectx(self, path, changeid, fileid)
744
742
745 def getcwd(self):
743 def getcwd(self):
746 return self.dirstate.getcwd()
744 return self.dirstate.getcwd()
747
745
748 def pathto(self, f, cwd=None):
746 def pathto(self, f, cwd=None):
749 return self.dirstate.pathto(f, cwd)
747 return self.dirstate.pathto(f, cwd)
750
748
751 def wfile(self, f, mode='r'):
749 def wfile(self, f, mode='r'):
752 return self.wopener(f, mode)
750 return self.wopener(f, mode)
753
751
754 def _link(self, f):
752 def _link(self, f):
755 return os.path.islink(self.wjoin(f))
753 return os.path.islink(self.wjoin(f))
756
754
757 def _loadfilter(self, filter):
755 def _loadfilter(self, filter):
758 if filter not in self.filterpats:
756 if filter not in self.filterpats:
759 l = []
757 l = []
760 for pat, cmd in self.ui.configitems(filter):
758 for pat, cmd in self.ui.configitems(filter):
761 if cmd == '!':
759 if cmd == '!':
762 continue
760 continue
763 mf = matchmod.match(self.root, '', [pat])
761 mf = matchmod.match(self.root, '', [pat])
764 fn = None
762 fn = None
765 params = cmd
763 params = cmd
766 for name, filterfn in self._datafilters.iteritems():
764 for name, filterfn in self._datafilters.iteritems():
767 if cmd.startswith(name):
765 if cmd.startswith(name):
768 fn = filterfn
766 fn = filterfn
769 params = cmd[len(name):].lstrip()
767 params = cmd[len(name):].lstrip()
770 break
768 break
771 if not fn:
769 if not fn:
772 fn = lambda s, c, **kwargs: util.filter(s, c)
770 fn = lambda s, c, **kwargs: util.filter(s, c)
773 # Wrap old filters not supporting keyword arguments
771 # Wrap old filters not supporting keyword arguments
774 if not inspect.getargspec(fn)[2]:
772 if not inspect.getargspec(fn)[2]:
775 oldfn = fn
773 oldfn = fn
776 fn = lambda s, c, **kwargs: oldfn(s, c)
774 fn = lambda s, c, **kwargs: oldfn(s, c)
777 l.append((mf, fn, params))
775 l.append((mf, fn, params))
778 self.filterpats[filter] = l
776 self.filterpats[filter] = l
779 return self.filterpats[filter]
777 return self.filterpats[filter]
780
778
781 def _filter(self, filterpats, filename, data):
779 def _filter(self, filterpats, filename, data):
782 for mf, fn, cmd in filterpats:
780 for mf, fn, cmd in filterpats:
783 if mf(filename):
781 if mf(filename):
784 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
782 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
785 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
783 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
786 break
784 break
787
785
788 return data
786 return data
789
787
790 @unfilteredpropertycache
788 @unfilteredpropertycache
791 def _encodefilterpats(self):
789 def _encodefilterpats(self):
792 return self._loadfilter('encode')
790 return self._loadfilter('encode')
793
791
794 @unfilteredpropertycache
792 @unfilteredpropertycache
795 def _decodefilterpats(self):
793 def _decodefilterpats(self):
796 return self._loadfilter('decode')
794 return self._loadfilter('decode')
797
795
798 def adddatafilter(self, name, filter):
796 def adddatafilter(self, name, filter):
799 self._datafilters[name] = filter
797 self._datafilters[name] = filter
800
798
801 def wread(self, filename):
799 def wread(self, filename):
802 if self._link(filename):
800 if self._link(filename):
803 data = os.readlink(self.wjoin(filename))
801 data = os.readlink(self.wjoin(filename))
804 else:
802 else:
805 data = self.wopener.read(filename)
803 data = self.wopener.read(filename)
806 return self._filter(self._encodefilterpats, filename, data)
804 return self._filter(self._encodefilterpats, filename, data)
807
805
808 def wwrite(self, filename, data, flags):
806 def wwrite(self, filename, data, flags):
809 data = self._filter(self._decodefilterpats, filename, data)
807 data = self._filter(self._decodefilterpats, filename, data)
810 if 'l' in flags:
808 if 'l' in flags:
811 self.wopener.symlink(data, filename)
809 self.wopener.symlink(data, filename)
812 else:
810 else:
813 self.wopener.write(filename, data)
811 self.wopener.write(filename, data)
814 if 'x' in flags:
812 if 'x' in flags:
815 util.setflags(self.wjoin(filename), False, True)
813 util.setflags(self.wjoin(filename), False, True)
816
814
817 def wwritedata(self, filename, data):
815 def wwritedata(self, filename, data):
818 return self._filter(self._decodefilterpats, filename, data)
816 return self._filter(self._decodefilterpats, filename, data)
819
817
820 def transaction(self, desc):
818 def transaction(self, desc):
821 tr = self._transref and self._transref() or None
819 tr = self._transref and self._transref() or None
822 if tr and tr.running():
820 if tr and tr.running():
823 return tr.nest()
821 return tr.nest()
824
822
825 # abort here if the journal already exists
823 # abort here if the journal already exists
826 if os.path.exists(self.sjoin("journal")):
824 if os.path.exists(self.sjoin("journal")):
827 raise error.RepoError(
825 raise error.RepoError(
828 _("abandoned transaction found - run hg recover"))
826 _("abandoned transaction found - run hg recover"))
829
827
830 self._writejournal(desc)
828 self._writejournal(desc)
831 renames = [(x, undoname(x)) for x in self._journalfiles()]
829 renames = [(x, undoname(x)) for x in self._journalfiles()]
832
830
833 tr = transaction.transaction(self.ui.warn, self.sopener,
831 tr = transaction.transaction(self.ui.warn, self.sopener,
834 self.sjoin("journal"),
832 self.sjoin("journal"),
835 aftertrans(renames),
833 aftertrans(renames),
836 self.store.createmode)
834 self.store.createmode)
837 self._transref = weakref.ref(tr)
835 self._transref = weakref.ref(tr)
838 return tr
836 return tr
839
837
840 def _journalfiles(self):
838 def _journalfiles(self):
841 return (self.sjoin('journal'), self.join('journal.dirstate'),
839 return (self.sjoin('journal'), self.join('journal.dirstate'),
842 self.join('journal.branch'), self.join('journal.desc'),
840 self.join('journal.branch'), self.join('journal.desc'),
843 self.join('journal.bookmarks'),
841 self.join('journal.bookmarks'),
844 self.sjoin('journal.phaseroots'))
842 self.sjoin('journal.phaseroots'))
845
843
846 def undofiles(self):
844 def undofiles(self):
847 return [undoname(x) for x in self._journalfiles()]
845 return [undoname(x) for x in self._journalfiles()]
848
846
849 def _writejournal(self, desc):
847 def _writejournal(self, desc):
850 self.opener.write("journal.dirstate",
848 self.opener.write("journal.dirstate",
851 self.opener.tryread("dirstate"))
849 self.opener.tryread("dirstate"))
852 self.opener.write("journal.branch",
850 self.opener.write("journal.branch",
853 encoding.fromlocal(self.dirstate.branch()))
851 encoding.fromlocal(self.dirstate.branch()))
854 self.opener.write("journal.desc",
852 self.opener.write("journal.desc",
855 "%d\n%s\n" % (len(self), desc))
853 "%d\n%s\n" % (len(self), desc))
856 self.opener.write("journal.bookmarks",
854 self.opener.write("journal.bookmarks",
857 self.opener.tryread("bookmarks"))
855 self.opener.tryread("bookmarks"))
858 self.sopener.write("journal.phaseroots",
856 self.sopener.write("journal.phaseroots",
859 self.sopener.tryread("phaseroots"))
857 self.sopener.tryread("phaseroots"))
860
858
861 def recover(self):
859 def recover(self):
862 lock = self.lock()
860 lock = self.lock()
863 try:
861 try:
864 if os.path.exists(self.sjoin("journal")):
862 if os.path.exists(self.sjoin("journal")):
865 self.ui.status(_("rolling back interrupted transaction\n"))
863 self.ui.status(_("rolling back interrupted transaction\n"))
866 transaction.rollback(self.sopener, self.sjoin("journal"),
864 transaction.rollback(self.sopener, self.sjoin("journal"),
867 self.ui.warn)
865 self.ui.warn)
868 self.invalidate()
866 self.invalidate()
869 return True
867 return True
870 else:
868 else:
871 self.ui.warn(_("no interrupted transaction available\n"))
869 self.ui.warn(_("no interrupted transaction available\n"))
872 return False
870 return False
873 finally:
871 finally:
874 lock.release()
872 lock.release()
875
873
876 def rollback(self, dryrun=False, force=False):
874 def rollback(self, dryrun=False, force=False):
877 wlock = lock = None
875 wlock = lock = None
878 try:
876 try:
879 wlock = self.wlock()
877 wlock = self.wlock()
880 lock = self.lock()
878 lock = self.lock()
881 if os.path.exists(self.sjoin("undo")):
879 if os.path.exists(self.sjoin("undo")):
882 return self._rollback(dryrun, force)
880 return self._rollback(dryrun, force)
883 else:
881 else:
884 self.ui.warn(_("no rollback information available\n"))
882 self.ui.warn(_("no rollback information available\n"))
885 return 1
883 return 1
886 finally:
884 finally:
887 release(lock, wlock)
885 release(lock, wlock)
888
886
889 @unfilteredmethod # Until we get smarter cache management
887 @unfilteredmethod # Until we get smarter cache management
890 def _rollback(self, dryrun, force):
888 def _rollback(self, dryrun, force):
891 ui = self.ui
889 ui = self.ui
892 try:
890 try:
893 args = self.opener.read('undo.desc').splitlines()
891 args = self.opener.read('undo.desc').splitlines()
894 (oldlen, desc, detail) = (int(args[0]), args[1], None)
892 (oldlen, desc, detail) = (int(args[0]), args[1], None)
895 if len(args) >= 3:
893 if len(args) >= 3:
896 detail = args[2]
894 detail = args[2]
897 oldtip = oldlen - 1
895 oldtip = oldlen - 1
898
896
899 if detail and ui.verbose:
897 if detail and ui.verbose:
900 msg = (_('repository tip rolled back to revision %s'
898 msg = (_('repository tip rolled back to revision %s'
901 ' (undo %s: %s)\n')
899 ' (undo %s: %s)\n')
902 % (oldtip, desc, detail))
900 % (oldtip, desc, detail))
903 else:
901 else:
904 msg = (_('repository tip rolled back to revision %s'
902 msg = (_('repository tip rolled back to revision %s'
905 ' (undo %s)\n')
903 ' (undo %s)\n')
906 % (oldtip, desc))
904 % (oldtip, desc))
907 except IOError:
905 except IOError:
908 msg = _('rolling back unknown transaction\n')
906 msg = _('rolling back unknown transaction\n')
909 desc = None
907 desc = None
910
908
911 if not force and self['.'] != self['tip'] and desc == 'commit':
909 if not force and self['.'] != self['tip'] and desc == 'commit':
912 raise util.Abort(
910 raise util.Abort(
913 _('rollback of last commit while not checked out '
911 _('rollback of last commit while not checked out '
914 'may lose data'), hint=_('use -f to force'))
912 'may lose data'), hint=_('use -f to force'))
915
913
916 ui.status(msg)
914 ui.status(msg)
917 if dryrun:
915 if dryrun:
918 return 0
916 return 0
919
917
920 parents = self.dirstate.parents()
918 parents = self.dirstate.parents()
921 transaction.rollback(self.sopener, self.sjoin('undo'), ui.warn)
919 transaction.rollback(self.sopener, self.sjoin('undo'), ui.warn)
922 if os.path.exists(self.join('undo.bookmarks')):
920 if os.path.exists(self.join('undo.bookmarks')):
923 util.rename(self.join('undo.bookmarks'),
921 util.rename(self.join('undo.bookmarks'),
924 self.join('bookmarks'))
922 self.join('bookmarks'))
925 if os.path.exists(self.sjoin('undo.phaseroots')):
923 if os.path.exists(self.sjoin('undo.phaseroots')):
926 util.rename(self.sjoin('undo.phaseroots'),
924 util.rename(self.sjoin('undo.phaseroots'),
927 self.sjoin('phaseroots'))
925 self.sjoin('phaseroots'))
928 self.invalidate()
926 self.invalidate()
929
927
930 # Discard all cache entries to force reloading everything.
928 # Discard all cache entries to force reloading everything.
931 self._filecache.clear()
929 self._filecache.clear()
932
930
933 parentgone = (parents[0] not in self.changelog.nodemap or
931 parentgone = (parents[0] not in self.changelog.nodemap or
934 parents[1] not in self.changelog.nodemap)
932 parents[1] not in self.changelog.nodemap)
935 if parentgone:
933 if parentgone:
936 util.rename(self.join('undo.dirstate'), self.join('dirstate'))
934 util.rename(self.join('undo.dirstate'), self.join('dirstate'))
937 try:
935 try:
938 branch = self.opener.read('undo.branch')
936 branch = self.opener.read('undo.branch')
939 self.dirstate.setbranch(encoding.tolocal(branch))
937 self.dirstate.setbranch(encoding.tolocal(branch))
940 except IOError:
938 except IOError:
941 ui.warn(_('named branch could not be reset: '
939 ui.warn(_('named branch could not be reset: '
942 'current branch is still \'%s\'\n')
940 'current branch is still \'%s\'\n')
943 % self.dirstate.branch())
941 % self.dirstate.branch())
944
942
945 self.dirstate.invalidate()
943 self.dirstate.invalidate()
946 parents = tuple([p.rev() for p in self.parents()])
944 parents = tuple([p.rev() for p in self.parents()])
947 if len(parents) > 1:
945 if len(parents) > 1:
948 ui.status(_('working directory now based on '
946 ui.status(_('working directory now based on '
949 'revisions %d and %d\n') % parents)
947 'revisions %d and %d\n') % parents)
950 else:
948 else:
951 ui.status(_('working directory now based on '
949 ui.status(_('working directory now based on '
952 'revision %d\n') % parents)
950 'revision %d\n') % parents)
953 # TODO: if we know which new heads may result from this rollback, pass
951 # TODO: if we know which new heads may result from this rollback, pass
954 # them to destroy(), which will prevent the branchhead cache from being
952 # them to destroy(), which will prevent the branchhead cache from being
955 # invalidated.
953 # invalidated.
956 self.destroyed()
954 self.destroyed()
957 return 0
955 return 0
958
956
959 def invalidatecaches(self):
957 def invalidatecaches(self):
960
958
961 if '_tagscache' in vars(self):
959 if '_tagscache' in vars(self):
962 # can't use delattr on proxy
960 # can't use delattr on proxy
963 del self.__dict__['_tagscache']
961 del self.__dict__['_tagscache']
964
962
965 self.unfiltered()._branchcaches.clear()
963 self.unfiltered()._branchcaches.clear()
966 self.invalidatevolatilesets()
964 self.invalidatevolatilesets()
967
965
968 def invalidatevolatilesets(self):
966 def invalidatevolatilesets(self):
969 self.filteredrevcache.clear()
967 self.filteredrevcache.clear()
970 obsolete.clearobscaches(self)
968 obsolete.clearobscaches(self)
971 if 'hiddenrevs' in vars(self):
969 if 'hiddenrevs' in vars(self):
972 del self.hiddenrevs
970 del self.hiddenrevs
973
971
974 def invalidatedirstate(self):
972 def invalidatedirstate(self):
975 '''Invalidates the dirstate, causing the next call to dirstate
973 '''Invalidates the dirstate, causing the next call to dirstate
976 to check if it was modified since the last time it was read,
974 to check if it was modified since the last time it was read,
977 rereading it if it has.
975 rereading it if it has.
978
976
979 This is different to dirstate.invalidate() that it doesn't always
977 This is different to dirstate.invalidate() that it doesn't always
980 rereads the dirstate. Use dirstate.invalidate() if you want to
978 rereads the dirstate. Use dirstate.invalidate() if you want to
981 explicitly read the dirstate again (i.e. restoring it to a previous
979 explicitly read the dirstate again (i.e. restoring it to a previous
982 known good state).'''
980 known good state).'''
983 if hasunfilteredcache(self, 'dirstate'):
981 if hasunfilteredcache(self, 'dirstate'):
984 for k in self.dirstate._filecache:
982 for k in self.dirstate._filecache:
985 try:
983 try:
986 delattr(self.dirstate, k)
984 delattr(self.dirstate, k)
987 except AttributeError:
985 except AttributeError:
988 pass
986 pass
989 delattr(self.unfiltered(), 'dirstate')
987 delattr(self.unfiltered(), 'dirstate')
990
988
991 def invalidate(self):
989 def invalidate(self):
992 unfiltered = self.unfiltered() # all filecaches are stored on unfiltered
990 unfiltered = self.unfiltered() # all filecaches are stored on unfiltered
993 for k in self._filecache:
991 for k in self._filecache:
994 # dirstate is invalidated separately in invalidatedirstate()
992 # dirstate is invalidated separately in invalidatedirstate()
995 if k == 'dirstate':
993 if k == 'dirstate':
996 continue
994 continue
997
995
998 try:
996 try:
999 delattr(unfiltered, k)
997 delattr(unfiltered, k)
1000 except AttributeError:
998 except AttributeError:
1001 pass
999 pass
1002 self.invalidatecaches()
1000 self.invalidatecaches()
1003
1001
1004 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
1002 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
1005 try:
1003 try:
1006 l = lock.lock(lockname, 0, releasefn, desc=desc)
1004 l = lock.lock(lockname, 0, releasefn, desc=desc)
1007 except error.LockHeld, inst:
1005 except error.LockHeld, inst:
1008 if not wait:
1006 if not wait:
1009 raise
1007 raise
1010 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1008 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1011 (desc, inst.locker))
1009 (desc, inst.locker))
1012 # default to 600 seconds timeout
1010 # default to 600 seconds timeout
1013 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
1011 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
1014 releasefn, desc=desc)
1012 releasefn, desc=desc)
1015 if acquirefn:
1013 if acquirefn:
1016 acquirefn()
1014 acquirefn()
1017 return l
1015 return l
1018
1016
1019 def _afterlock(self, callback):
1017 def _afterlock(self, callback):
1020 """add a callback to the current repository lock.
1018 """add a callback to the current repository lock.
1021
1019
1022 The callback will be executed on lock release."""
1020 The callback will be executed on lock release."""
1023 l = self._lockref and self._lockref()
1021 l = self._lockref and self._lockref()
1024 if l:
1022 if l:
1025 l.postrelease.append(callback)
1023 l.postrelease.append(callback)
1026 else:
1024 else:
1027 callback()
1025 callback()
1028
1026
1029 def lock(self, wait=True):
1027 def lock(self, wait=True):
1030 '''Lock the repository store (.hg/store) and return a weak reference
1028 '''Lock the repository store (.hg/store) and return a weak reference
1031 to the lock. Use this before modifying the store (e.g. committing or
1029 to the lock. Use this before modifying the store (e.g. committing or
1032 stripping). If you are opening a transaction, get a lock as well.)'''
1030 stripping). If you are opening a transaction, get a lock as well.)'''
1033 l = self._lockref and self._lockref()
1031 l = self._lockref and self._lockref()
1034 if l is not None and l.held:
1032 if l is not None and l.held:
1035 l.lock()
1033 l.lock()
1036 return l
1034 return l
1037
1035
1038 def unlock():
1036 def unlock():
1039 self.store.write()
1037 self.store.write()
1040 if hasunfilteredcache(self, '_phasecache'):
1038 if hasunfilteredcache(self, '_phasecache'):
1041 self._phasecache.write()
1039 self._phasecache.write()
1042 for k, ce in self._filecache.items():
1040 for k, ce in self._filecache.items():
1043 if k == 'dirstate':
1041 if k == 'dirstate':
1044 continue
1042 continue
1045 ce.refresh()
1043 ce.refresh()
1046
1044
1047 l = self._lock(self.sjoin("lock"), wait, unlock,
1045 l = self._lock(self.sjoin("lock"), wait, unlock,
1048 self.invalidate, _('repository %s') % self.origroot)
1046 self.invalidate, _('repository %s') % self.origroot)
1049 self._lockref = weakref.ref(l)
1047 self._lockref = weakref.ref(l)
1050 return l
1048 return l
1051
1049
1052 def wlock(self, wait=True):
1050 def wlock(self, wait=True):
1053 '''Lock the non-store parts of the repository (everything under
1051 '''Lock the non-store parts of the repository (everything under
1054 .hg except .hg/store) and return a weak reference to the lock.
1052 .hg except .hg/store) and return a weak reference to the lock.
1055 Use this before modifying files in .hg.'''
1053 Use this before modifying files in .hg.'''
1056 l = self._wlockref and self._wlockref()
1054 l = self._wlockref and self._wlockref()
1057 if l is not None and l.held:
1055 if l is not None and l.held:
1058 l.lock()
1056 l.lock()
1059 return l
1057 return l
1060
1058
1061 def unlock():
1059 def unlock():
1062 self.dirstate.write()
1060 self.dirstate.write()
1063 ce = self._filecache.get('dirstate')
1061 ce = self._filecache.get('dirstate')
1064 if ce:
1062 if ce:
1065 ce.refresh()
1063 ce.refresh()
1066
1064
1067 l = self._lock(self.join("wlock"), wait, unlock,
1065 l = self._lock(self.join("wlock"), wait, unlock,
1068 self.invalidatedirstate, _('working directory of %s') %
1066 self.invalidatedirstate, _('working directory of %s') %
1069 self.origroot)
1067 self.origroot)
1070 self._wlockref = weakref.ref(l)
1068 self._wlockref = weakref.ref(l)
1071 return l
1069 return l
1072
1070
1073 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1071 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1074 """
1072 """
1075 commit an individual file as part of a larger transaction
1073 commit an individual file as part of a larger transaction
1076 """
1074 """
1077
1075
1078 fname = fctx.path()
1076 fname = fctx.path()
1079 text = fctx.data()
1077 text = fctx.data()
1080 flog = self.file(fname)
1078 flog = self.file(fname)
1081 fparent1 = manifest1.get(fname, nullid)
1079 fparent1 = manifest1.get(fname, nullid)
1082 fparent2 = fparent2o = manifest2.get(fname, nullid)
1080 fparent2 = fparent2o = manifest2.get(fname, nullid)
1083
1081
1084 meta = {}
1082 meta = {}
1085 copy = fctx.renamed()
1083 copy = fctx.renamed()
1086 if copy and copy[0] != fname:
1084 if copy and copy[0] != fname:
1087 # Mark the new revision of this file as a copy of another
1085 # Mark the new revision of this file as a copy of another
1088 # file. This copy data will effectively act as a parent
1086 # file. This copy data will effectively act as a parent
1089 # of this new revision. If this is a merge, the first
1087 # of this new revision. If this is a merge, the first
1090 # parent will be the nullid (meaning "look up the copy data")
1088 # parent will be the nullid (meaning "look up the copy data")
1091 # and the second one will be the other parent. For example:
1089 # and the second one will be the other parent. For example:
1092 #
1090 #
1093 # 0 --- 1 --- 3 rev1 changes file foo
1091 # 0 --- 1 --- 3 rev1 changes file foo
1094 # \ / rev2 renames foo to bar and changes it
1092 # \ / rev2 renames foo to bar and changes it
1095 # \- 2 -/ rev3 should have bar with all changes and
1093 # \- 2 -/ rev3 should have bar with all changes and
1096 # should record that bar descends from
1094 # should record that bar descends from
1097 # bar in rev2 and foo in rev1
1095 # bar in rev2 and foo in rev1
1098 #
1096 #
1099 # this allows this merge to succeed:
1097 # this allows this merge to succeed:
1100 #
1098 #
1101 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1099 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1102 # \ / merging rev3 and rev4 should use bar@rev2
1100 # \ / merging rev3 and rev4 should use bar@rev2
1103 # \- 2 --- 4 as the merge base
1101 # \- 2 --- 4 as the merge base
1104 #
1102 #
1105
1103
1106 cfname = copy[0]
1104 cfname = copy[0]
1107 crev = manifest1.get(cfname)
1105 crev = manifest1.get(cfname)
1108 newfparent = fparent2
1106 newfparent = fparent2
1109
1107
1110 if manifest2: # branch merge
1108 if manifest2: # branch merge
1111 if fparent2 == nullid or crev is None: # copied on remote side
1109 if fparent2 == nullid or crev is None: # copied on remote side
1112 if cfname in manifest2:
1110 if cfname in manifest2:
1113 crev = manifest2[cfname]
1111 crev = manifest2[cfname]
1114 newfparent = fparent1
1112 newfparent = fparent1
1115
1113
1116 # find source in nearest ancestor if we've lost track
1114 # find source in nearest ancestor if we've lost track
1117 if not crev:
1115 if not crev:
1118 self.ui.debug(" %s: searching for copy revision for %s\n" %
1116 self.ui.debug(" %s: searching for copy revision for %s\n" %
1119 (fname, cfname))
1117 (fname, cfname))
1120 for ancestor in self[None].ancestors():
1118 for ancestor in self[None].ancestors():
1121 if cfname in ancestor:
1119 if cfname in ancestor:
1122 crev = ancestor[cfname].filenode()
1120 crev = ancestor[cfname].filenode()
1123 break
1121 break
1124
1122
1125 if crev:
1123 if crev:
1126 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1124 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1127 meta["copy"] = cfname
1125 meta["copy"] = cfname
1128 meta["copyrev"] = hex(crev)
1126 meta["copyrev"] = hex(crev)
1129 fparent1, fparent2 = nullid, newfparent
1127 fparent1, fparent2 = nullid, newfparent
1130 else:
1128 else:
1131 self.ui.warn(_("warning: can't find ancestor for '%s' "
1129 self.ui.warn(_("warning: can't find ancestor for '%s' "
1132 "copied from '%s'!\n") % (fname, cfname))
1130 "copied from '%s'!\n") % (fname, cfname))
1133
1131
1134 elif fparent2 != nullid:
1132 elif fparent2 != nullid:
1135 # is one parent an ancestor of the other?
1133 # is one parent an ancestor of the other?
1136 fparentancestor = flog.ancestor(fparent1, fparent2)
1134 fparentancestor = flog.ancestor(fparent1, fparent2)
1137 if fparentancestor == fparent1:
1135 if fparentancestor == fparent1:
1138 fparent1, fparent2 = fparent2, nullid
1136 fparent1, fparent2 = fparent2, nullid
1139 elif fparentancestor == fparent2:
1137 elif fparentancestor == fparent2:
1140 fparent2 = nullid
1138 fparent2 = nullid
1141
1139
1142 # is the file changed?
1140 # is the file changed?
1143 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1141 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1144 changelist.append(fname)
1142 changelist.append(fname)
1145 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1143 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1146
1144
1147 # are just the flags changed during merge?
1145 # are just the flags changed during merge?
1148 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
1146 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
1149 changelist.append(fname)
1147 changelist.append(fname)
1150
1148
1151 return fparent1
1149 return fparent1
1152
1150
1153 @unfilteredmethod
1151 @unfilteredmethod
1154 def commit(self, text="", user=None, date=None, match=None, force=False,
1152 def commit(self, text="", user=None, date=None, match=None, force=False,
1155 editor=False, extra={}):
1153 editor=False, extra={}):
1156 """Add a new revision to current repository.
1154 """Add a new revision to current repository.
1157
1155
1158 Revision information is gathered from the working directory,
1156 Revision information is gathered from the working directory,
1159 match can be used to filter the committed files. If editor is
1157 match can be used to filter the committed files. If editor is
1160 supplied, it is called to get a commit message.
1158 supplied, it is called to get a commit message.
1161 """
1159 """
1162
1160
1163 def fail(f, msg):
1161 def fail(f, msg):
1164 raise util.Abort('%s: %s' % (f, msg))
1162 raise util.Abort('%s: %s' % (f, msg))
1165
1163
1166 if not match:
1164 if not match:
1167 match = matchmod.always(self.root, '')
1165 match = matchmod.always(self.root, '')
1168
1166
1169 if not force:
1167 if not force:
1170 vdirs = []
1168 vdirs = []
1171 match.dir = vdirs.append
1169 match.dir = vdirs.append
1172 match.bad = fail
1170 match.bad = fail
1173
1171
1174 wlock = self.wlock()
1172 wlock = self.wlock()
1175 try:
1173 try:
1176 wctx = self[None]
1174 wctx = self[None]
1177 merge = len(wctx.parents()) > 1
1175 merge = len(wctx.parents()) > 1
1178
1176
1179 if (not force and merge and match and
1177 if (not force and merge and match and
1180 (match.files() or match.anypats())):
1178 (match.files() or match.anypats())):
1181 raise util.Abort(_('cannot partially commit a merge '
1179 raise util.Abort(_('cannot partially commit a merge '
1182 '(do not specify files or patterns)'))
1180 '(do not specify files or patterns)'))
1183
1181
1184 changes = self.status(match=match, clean=force)
1182 changes = self.status(match=match, clean=force)
1185 if force:
1183 if force:
1186 changes[0].extend(changes[6]) # mq may commit unchanged files
1184 changes[0].extend(changes[6]) # mq may commit unchanged files
1187
1185
1188 # check subrepos
1186 # check subrepos
1189 subs = []
1187 subs = []
1190 commitsubs = set()
1188 commitsubs = set()
1191 newstate = wctx.substate.copy()
1189 newstate = wctx.substate.copy()
1192 # only manage subrepos and .hgsubstate if .hgsub is present
1190 # only manage subrepos and .hgsubstate if .hgsub is present
1193 if '.hgsub' in wctx:
1191 if '.hgsub' in wctx:
1194 # we'll decide whether to track this ourselves, thanks
1192 # we'll decide whether to track this ourselves, thanks
1195 if '.hgsubstate' in changes[0]:
1193 if '.hgsubstate' in changes[0]:
1196 changes[0].remove('.hgsubstate')
1194 changes[0].remove('.hgsubstate')
1197 if '.hgsubstate' in changes[2]:
1195 if '.hgsubstate' in changes[2]:
1198 changes[2].remove('.hgsubstate')
1196 changes[2].remove('.hgsubstate')
1199
1197
1200 # compare current state to last committed state
1198 # compare current state to last committed state
1201 # build new substate based on last committed state
1199 # build new substate based on last committed state
1202 oldstate = wctx.p1().substate
1200 oldstate = wctx.p1().substate
1203 for s in sorted(newstate.keys()):
1201 for s in sorted(newstate.keys()):
1204 if not match(s):
1202 if not match(s):
1205 # ignore working copy, use old state if present
1203 # ignore working copy, use old state if present
1206 if s in oldstate:
1204 if s in oldstate:
1207 newstate[s] = oldstate[s]
1205 newstate[s] = oldstate[s]
1208 continue
1206 continue
1209 if not force:
1207 if not force:
1210 raise util.Abort(
1208 raise util.Abort(
1211 _("commit with new subrepo %s excluded") % s)
1209 _("commit with new subrepo %s excluded") % s)
1212 if wctx.sub(s).dirty(True):
1210 if wctx.sub(s).dirty(True):
1213 if not self.ui.configbool('ui', 'commitsubrepos'):
1211 if not self.ui.configbool('ui', 'commitsubrepos'):
1214 raise util.Abort(
1212 raise util.Abort(
1215 _("uncommitted changes in subrepo %s") % s,
1213 _("uncommitted changes in subrepo %s") % s,
1216 hint=_("use --subrepos for recursive commit"))
1214 hint=_("use --subrepos for recursive commit"))
1217 subs.append(s)
1215 subs.append(s)
1218 commitsubs.add(s)
1216 commitsubs.add(s)
1219 else:
1217 else:
1220 bs = wctx.sub(s).basestate()
1218 bs = wctx.sub(s).basestate()
1221 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1219 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1222 if oldstate.get(s, (None, None, None))[1] != bs:
1220 if oldstate.get(s, (None, None, None))[1] != bs:
1223 subs.append(s)
1221 subs.append(s)
1224
1222
1225 # check for removed subrepos
1223 # check for removed subrepos
1226 for p in wctx.parents():
1224 for p in wctx.parents():
1227 r = [s for s in p.substate if s not in newstate]
1225 r = [s for s in p.substate if s not in newstate]
1228 subs += [s for s in r if match(s)]
1226 subs += [s for s in r if match(s)]
1229 if subs:
1227 if subs:
1230 if (not match('.hgsub') and
1228 if (not match('.hgsub') and
1231 '.hgsub' in (wctx.modified() + wctx.added())):
1229 '.hgsub' in (wctx.modified() + wctx.added())):
1232 raise util.Abort(
1230 raise util.Abort(
1233 _("can't commit subrepos without .hgsub"))
1231 _("can't commit subrepos without .hgsub"))
1234 changes[0].insert(0, '.hgsubstate')
1232 changes[0].insert(0, '.hgsubstate')
1235
1233
1236 elif '.hgsub' in changes[2]:
1234 elif '.hgsub' in changes[2]:
1237 # clean up .hgsubstate when .hgsub is removed
1235 # clean up .hgsubstate when .hgsub is removed
1238 if ('.hgsubstate' in wctx and
1236 if ('.hgsubstate' in wctx and
1239 '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
1237 '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
1240 changes[2].insert(0, '.hgsubstate')
1238 changes[2].insert(0, '.hgsubstate')
1241
1239
1242 # make sure all explicit patterns are matched
1240 # make sure all explicit patterns are matched
1243 if not force and match.files():
1241 if not force and match.files():
1244 matched = set(changes[0] + changes[1] + changes[2])
1242 matched = set(changes[0] + changes[1] + changes[2])
1245
1243
1246 for f in match.files():
1244 for f in match.files():
1247 f = self.dirstate.normalize(f)
1245 f = self.dirstate.normalize(f)
1248 if f == '.' or f in matched or f in wctx.substate:
1246 if f == '.' or f in matched or f in wctx.substate:
1249 continue
1247 continue
1250 if f in changes[3]: # missing
1248 if f in changes[3]: # missing
1251 fail(f, _('file not found!'))
1249 fail(f, _('file not found!'))
1252 if f in vdirs: # visited directory
1250 if f in vdirs: # visited directory
1253 d = f + '/'
1251 d = f + '/'
1254 for mf in matched:
1252 for mf in matched:
1255 if mf.startswith(d):
1253 if mf.startswith(d):
1256 break
1254 break
1257 else:
1255 else:
1258 fail(f, _("no match under directory!"))
1256 fail(f, _("no match under directory!"))
1259 elif f not in self.dirstate:
1257 elif f not in self.dirstate:
1260 fail(f, _("file not tracked!"))
1258 fail(f, _("file not tracked!"))
1261
1259
1262 if (not force and not extra.get("close") and not merge
1260 if (not force and not extra.get("close") and not merge
1263 and not (changes[0] or changes[1] or changes[2])
1261 and not (changes[0] or changes[1] or changes[2])
1264 and wctx.branch() == wctx.p1().branch()):
1262 and wctx.branch() == wctx.p1().branch()):
1265 return None
1263 return None
1266
1264
1267 if merge and changes[3]:
1265 if merge and changes[3]:
1268 raise util.Abort(_("cannot commit merge with missing files"))
1266 raise util.Abort(_("cannot commit merge with missing files"))
1269
1267
1270 ms = mergemod.mergestate(self)
1268 ms = mergemod.mergestate(self)
1271 for f in changes[0]:
1269 for f in changes[0]:
1272 if f in ms and ms[f] == 'u':
1270 if f in ms and ms[f] == 'u':
1273 raise util.Abort(_("unresolved merge conflicts "
1271 raise util.Abort(_("unresolved merge conflicts "
1274 "(see hg help resolve)"))
1272 "(see hg help resolve)"))
1275
1273
1276 cctx = context.workingctx(self, text, user, date, extra, changes)
1274 cctx = context.workingctx(self, text, user, date, extra, changes)
1277 if editor:
1275 if editor:
1278 cctx._text = editor(self, cctx, subs)
1276 cctx._text = editor(self, cctx, subs)
1279 edited = (text != cctx._text)
1277 edited = (text != cctx._text)
1280
1278
1281 # commit subs and write new state
1279 # commit subs and write new state
1282 if subs:
1280 if subs:
1283 for s in sorted(commitsubs):
1281 for s in sorted(commitsubs):
1284 sub = wctx.sub(s)
1282 sub = wctx.sub(s)
1285 self.ui.status(_('committing subrepository %s\n') %
1283 self.ui.status(_('committing subrepository %s\n') %
1286 subrepo.subrelpath(sub))
1284 subrepo.subrelpath(sub))
1287 sr = sub.commit(cctx._text, user, date)
1285 sr = sub.commit(cctx._text, user, date)
1288 newstate[s] = (newstate[s][0], sr)
1286 newstate[s] = (newstate[s][0], sr)
1289 subrepo.writestate(self, newstate)
1287 subrepo.writestate(self, newstate)
1290
1288
1291 # Save commit message in case this transaction gets rolled back
1289 # Save commit message in case this transaction gets rolled back
1292 # (e.g. by a pretxncommit hook). Leave the content alone on
1290 # (e.g. by a pretxncommit hook). Leave the content alone on
1293 # the assumption that the user will use the same editor again.
1291 # the assumption that the user will use the same editor again.
1294 msgfn = self.savecommitmessage(cctx._text)
1292 msgfn = self.savecommitmessage(cctx._text)
1295
1293
1296 p1, p2 = self.dirstate.parents()
1294 p1, p2 = self.dirstate.parents()
1297 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1295 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1298 try:
1296 try:
1299 self.hook("precommit", throw=True, parent1=hookp1,
1297 self.hook("precommit", throw=True, parent1=hookp1,
1300 parent2=hookp2)
1298 parent2=hookp2)
1301 ret = self.commitctx(cctx, True)
1299 ret = self.commitctx(cctx, True)
1302 except: # re-raises
1300 except: # re-raises
1303 if edited:
1301 if edited:
1304 self.ui.write(
1302 self.ui.write(
1305 _('note: commit message saved in %s\n') % msgfn)
1303 _('note: commit message saved in %s\n') % msgfn)
1306 raise
1304 raise
1307
1305
1308 # update bookmarks, dirstate and mergestate
1306 # update bookmarks, dirstate and mergestate
1309 bookmarks.update(self, [p1, p2], ret)
1307 bookmarks.update(self, [p1, p2], ret)
1310 for f in changes[0] + changes[1]:
1308 for f in changes[0] + changes[1]:
1311 self.dirstate.normal(f)
1309 self.dirstate.normal(f)
1312 for f in changes[2]:
1310 for f in changes[2]:
1313 self.dirstate.drop(f)
1311 self.dirstate.drop(f)
1314 self.dirstate.setparents(ret)
1312 self.dirstate.setparents(ret)
1315 ms.reset()
1313 ms.reset()
1316 finally:
1314 finally:
1317 wlock.release()
1315 wlock.release()
1318
1316
1319 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1317 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1320 self.hook("commit", node=node, parent1=parent1, parent2=parent2)
1318 self.hook("commit", node=node, parent1=parent1, parent2=parent2)
1321 self._afterlock(commithook)
1319 self._afterlock(commithook)
1322 return ret
1320 return ret
1323
1321
1324 @unfilteredmethod
1322 @unfilteredmethod
1325 def commitctx(self, ctx, error=False):
1323 def commitctx(self, ctx, error=False):
1326 """Add a new revision to current repository.
1324 """Add a new revision to current repository.
1327 Revision information is passed via the context argument.
1325 Revision information is passed via the context argument.
1328 """
1326 """
1329
1327
1330 tr = lock = None
1328 tr = lock = None
1331 removed = list(ctx.removed())
1329 removed = list(ctx.removed())
1332 p1, p2 = ctx.p1(), ctx.p2()
1330 p1, p2 = ctx.p1(), ctx.p2()
1333 user = ctx.user()
1331 user = ctx.user()
1334
1332
1335 lock = self.lock()
1333 lock = self.lock()
1336 try:
1334 try:
1337 tr = self.transaction("commit")
1335 tr = self.transaction("commit")
1338 trp = weakref.proxy(tr)
1336 trp = weakref.proxy(tr)
1339
1337
1340 if ctx.files():
1338 if ctx.files():
1341 m1 = p1.manifest().copy()
1339 m1 = p1.manifest().copy()
1342 m2 = p2.manifest()
1340 m2 = p2.manifest()
1343
1341
1344 # check in files
1342 # check in files
1345 new = {}
1343 new = {}
1346 changed = []
1344 changed = []
1347 linkrev = len(self)
1345 linkrev = len(self)
1348 for f in sorted(ctx.modified() + ctx.added()):
1346 for f in sorted(ctx.modified() + ctx.added()):
1349 self.ui.note(f + "\n")
1347 self.ui.note(f + "\n")
1350 try:
1348 try:
1351 fctx = ctx[f]
1349 fctx = ctx[f]
1352 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1350 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1353 changed)
1351 changed)
1354 m1.set(f, fctx.flags())
1352 m1.set(f, fctx.flags())
1355 except OSError, inst:
1353 except OSError, inst:
1356 self.ui.warn(_("trouble committing %s!\n") % f)
1354 self.ui.warn(_("trouble committing %s!\n") % f)
1357 raise
1355 raise
1358 except IOError, inst:
1356 except IOError, inst:
1359 errcode = getattr(inst, 'errno', errno.ENOENT)
1357 errcode = getattr(inst, 'errno', errno.ENOENT)
1360 if error or errcode and errcode != errno.ENOENT:
1358 if error or errcode and errcode != errno.ENOENT:
1361 self.ui.warn(_("trouble committing %s!\n") % f)
1359 self.ui.warn(_("trouble committing %s!\n") % f)
1362 raise
1360 raise
1363 else:
1361 else:
1364 removed.append(f)
1362 removed.append(f)
1365
1363
1366 # update manifest
1364 # update manifest
1367 m1.update(new)
1365 m1.update(new)
1368 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1366 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1369 drop = [f for f in removed if f in m1]
1367 drop = [f for f in removed if f in m1]
1370 for f in drop:
1368 for f in drop:
1371 del m1[f]
1369 del m1[f]
1372 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1370 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1373 p2.manifestnode(), (new, drop))
1371 p2.manifestnode(), (new, drop))
1374 files = changed + removed
1372 files = changed + removed
1375 else:
1373 else:
1376 mn = p1.manifestnode()
1374 mn = p1.manifestnode()
1377 files = []
1375 files = []
1378
1376
1379 # update changelog
1377 # update changelog
1380 self.changelog.delayupdate()
1378 self.changelog.delayupdate()
1381 n = self.changelog.add(mn, files, ctx.description(),
1379 n = self.changelog.add(mn, files, ctx.description(),
1382 trp, p1.node(), p2.node(),
1380 trp, p1.node(), p2.node(),
1383 user, ctx.date(), ctx.extra().copy())
1381 user, ctx.date(), ctx.extra().copy())
1384 p = lambda: self.changelog.writepending() and self.root or ""
1382 p = lambda: self.changelog.writepending() and self.root or ""
1385 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1383 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1386 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1384 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1387 parent2=xp2, pending=p)
1385 parent2=xp2, pending=p)
1388 self.changelog.finalize(trp)
1386 self.changelog.finalize(trp)
1389 # set the new commit is proper phase
1387 # set the new commit is proper phase
1390 targetphase = phases.newcommitphase(self.ui)
1388 targetphase = phases.newcommitphase(self.ui)
1391 if targetphase:
1389 if targetphase:
1392 # retract boundary do not alter parent changeset.
1390 # retract boundary do not alter parent changeset.
1393 # if a parent have higher the resulting phase will
1391 # if a parent have higher the resulting phase will
1394 # be compliant anyway
1392 # be compliant anyway
1395 #
1393 #
1396 # if minimal phase was 0 we don't need to retract anything
1394 # if minimal phase was 0 we don't need to retract anything
1397 phases.retractboundary(self, targetphase, [n])
1395 phases.retractboundary(self, targetphase, [n])
1398 tr.close()
1396 tr.close()
1399 branchmap.updatecache(self)
1397 branchmap.updatecache(self)
1400 return n
1398 return n
1401 finally:
1399 finally:
1402 if tr:
1400 if tr:
1403 tr.release()
1401 tr.release()
1404 lock.release()
1402 lock.release()
1405
1403
1406 @unfilteredmethod
1404 @unfilteredmethod
1407 def destroyed(self, newheadnodes=None):
1405 def destroyed(self, newheadnodes=None):
1408 '''Inform the repository that nodes have been destroyed.
1406 '''Inform the repository that nodes have been destroyed.
1409 Intended for use by strip and rollback, so there's a common
1407 Intended for use by strip and rollback, so there's a common
1410 place for anything that has to be done after destroying history.
1408 place for anything that has to be done after destroying history.
1411
1409
1412 If you know the branchheadcache was uptodate before nodes were removed
1410 If you know the branchheadcache was uptodate before nodes were removed
1413 and you also know the set of candidate new heads that may have resulted
1411 and you also know the set of candidate new heads that may have resulted
1414 from the destruction, you can set newheadnodes. This will enable the
1412 from the destruction, you can set newheadnodes. This will enable the
1415 code to update the branchheads cache, rather than having future code
1413 code to update the branchheads cache, rather than having future code
1416 decide it's invalid and regenerating it from scratch.
1414 decide it's invalid and regenerating it from scratch.
1417 '''
1415 '''
1418 # If we have info, newheadnodes, on how to update the branch cache, do
1416 # If we have info, newheadnodes, on how to update the branch cache, do
1419 # it, Otherwise, since nodes were destroyed, the cache is stale and this
1417 # it, Otherwise, since nodes were destroyed, the cache is stale and this
1420 # will be caught the next time it is read.
1418 # will be caught the next time it is read.
1421 if newheadnodes:
1419 if newheadnodes:
1422 ctxgen = (self[node] for node in newheadnodes
1420 ctxgen = (self[node] for node in newheadnodes
1423 if self.changelog.hasnode(node))
1421 if self.changelog.hasnode(node))
1424 cache = self._branchcaches[None]
1422 cache = self._branchcaches[None]
1425 cache.update(self, ctxgen)
1423 cache.update(self, ctxgen)
1426 cache.write(self)
1424 cache.write(self)
1427
1425
1428 # When one tries to:
1426 # When one tries to:
1429 # 1) destroy nodes thus calling this method (e.g. strip)
1427 # 1) destroy nodes thus calling this method (e.g. strip)
1430 # 2) use phasecache somewhere (e.g. commit)
1428 # 2) use phasecache somewhere (e.g. commit)
1431 #
1429 #
1432 # then 2) will fail because the phasecache contains nodes that were
1430 # then 2) will fail because the phasecache contains nodes that were
1433 # removed. We can either remove phasecache from the filecache,
1431 # removed. We can either remove phasecache from the filecache,
1434 # causing it to reload next time it is accessed, or simply filter
1432 # causing it to reload next time it is accessed, or simply filter
1435 # the removed nodes now and write the updated cache.
1433 # the removed nodes now and write the updated cache.
1436 if '_phasecache' in self._filecache:
1434 if '_phasecache' in self._filecache:
1437 self._phasecache.filterunknown(self)
1435 self._phasecache.filterunknown(self)
1438 self._phasecache.write()
1436 self._phasecache.write()
1439
1437
1440 # Ensure the persistent tag cache is updated. Doing it now
1438 # Ensure the persistent tag cache is updated. Doing it now
1441 # means that the tag cache only has to worry about destroyed
1439 # means that the tag cache only has to worry about destroyed
1442 # heads immediately after a strip/rollback. That in turn
1440 # heads immediately after a strip/rollback. That in turn
1443 # guarantees that "cachetip == currenttip" (comparing both rev
1441 # guarantees that "cachetip == currenttip" (comparing both rev
1444 # and node) always means no nodes have been added or destroyed.
1442 # and node) always means no nodes have been added or destroyed.
1445
1443
1446 # XXX this is suboptimal when qrefresh'ing: we strip the current
1444 # XXX this is suboptimal when qrefresh'ing: we strip the current
1447 # head, refresh the tag cache, then immediately add a new head.
1445 # head, refresh the tag cache, then immediately add a new head.
1448 # But I think doing it this way is necessary for the "instant
1446 # But I think doing it this way is necessary for the "instant
1449 # tag cache retrieval" case to work.
1447 # tag cache retrieval" case to work.
1450 self.invalidatecaches()
1448 self.invalidatecaches()
1451
1449
1452 # Discard all cache entries to force reloading everything.
1450 # Discard all cache entries to force reloading everything.
1453 self._filecache.clear()
1451 self._filecache.clear()
1454
1452
1455 def walk(self, match, node=None):
1453 def walk(self, match, node=None):
1456 '''
1454 '''
1457 walk recursively through the directory tree or a given
1455 walk recursively through the directory tree or a given
1458 changeset, finding all files matched by the match
1456 changeset, finding all files matched by the match
1459 function
1457 function
1460 '''
1458 '''
1461 return self[node].walk(match)
1459 return self[node].walk(match)
1462
1460
1463 def status(self, node1='.', node2=None, match=None,
1461 def status(self, node1='.', node2=None, match=None,
1464 ignored=False, clean=False, unknown=False,
1462 ignored=False, clean=False, unknown=False,
1465 listsubrepos=False):
1463 listsubrepos=False):
1466 """return status of files between two nodes or node and working
1464 """return status of files between two nodes or node and working
1467 directory.
1465 directory.
1468
1466
1469 If node1 is None, use the first dirstate parent instead.
1467 If node1 is None, use the first dirstate parent instead.
1470 If node2 is None, compare node1 with working directory.
1468 If node2 is None, compare node1 with working directory.
1471 """
1469 """
1472
1470
1473 def mfmatches(ctx):
1471 def mfmatches(ctx):
1474 mf = ctx.manifest().copy()
1472 mf = ctx.manifest().copy()
1475 if match.always():
1473 if match.always():
1476 return mf
1474 return mf
1477 for fn in mf.keys():
1475 for fn in mf.keys():
1478 if not match(fn):
1476 if not match(fn):
1479 del mf[fn]
1477 del mf[fn]
1480 return mf
1478 return mf
1481
1479
1482 if isinstance(node1, context.changectx):
1480 if isinstance(node1, context.changectx):
1483 ctx1 = node1
1481 ctx1 = node1
1484 else:
1482 else:
1485 ctx1 = self[node1]
1483 ctx1 = self[node1]
1486 if isinstance(node2, context.changectx):
1484 if isinstance(node2, context.changectx):
1487 ctx2 = node2
1485 ctx2 = node2
1488 else:
1486 else:
1489 ctx2 = self[node2]
1487 ctx2 = self[node2]
1490
1488
1491 working = ctx2.rev() is None
1489 working = ctx2.rev() is None
1492 parentworking = working and ctx1 == self['.']
1490 parentworking = working and ctx1 == self['.']
1493 match = match or matchmod.always(self.root, self.getcwd())
1491 match = match or matchmod.always(self.root, self.getcwd())
1494 listignored, listclean, listunknown = ignored, clean, unknown
1492 listignored, listclean, listunknown = ignored, clean, unknown
1495
1493
1496 # load earliest manifest first for caching reasons
1494 # load earliest manifest first for caching reasons
1497 if not working and ctx2.rev() < ctx1.rev():
1495 if not working and ctx2.rev() < ctx1.rev():
1498 ctx2.manifest()
1496 ctx2.manifest()
1499
1497
1500 if not parentworking:
1498 if not parentworking:
1501 def bad(f, msg):
1499 def bad(f, msg):
1502 # 'f' may be a directory pattern from 'match.files()',
1500 # 'f' may be a directory pattern from 'match.files()',
1503 # so 'f not in ctx1' is not enough
1501 # so 'f not in ctx1' is not enough
1504 if f not in ctx1 and f not in ctx1.dirs():
1502 if f not in ctx1 and f not in ctx1.dirs():
1505 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1503 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1506 match.bad = bad
1504 match.bad = bad
1507
1505
1508 if working: # we need to scan the working dir
1506 if working: # we need to scan the working dir
1509 subrepos = []
1507 subrepos = []
1510 if '.hgsub' in self.dirstate:
1508 if '.hgsub' in self.dirstate:
1511 subrepos = ctx2.substate.keys()
1509 subrepos = ctx2.substate.keys()
1512 s = self.dirstate.status(match, subrepos, listignored,
1510 s = self.dirstate.status(match, subrepos, listignored,
1513 listclean, listunknown)
1511 listclean, listunknown)
1514 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1512 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1515
1513
1516 # check for any possibly clean files
1514 # check for any possibly clean files
1517 if parentworking and cmp:
1515 if parentworking and cmp:
1518 fixup = []
1516 fixup = []
1519 # do a full compare of any files that might have changed
1517 # do a full compare of any files that might have changed
1520 for f in sorted(cmp):
1518 for f in sorted(cmp):
1521 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1519 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1522 or ctx1[f].cmp(ctx2[f])):
1520 or ctx1[f].cmp(ctx2[f])):
1523 modified.append(f)
1521 modified.append(f)
1524 else:
1522 else:
1525 fixup.append(f)
1523 fixup.append(f)
1526
1524
1527 # update dirstate for files that are actually clean
1525 # update dirstate for files that are actually clean
1528 if fixup:
1526 if fixup:
1529 if listclean:
1527 if listclean:
1530 clean += fixup
1528 clean += fixup
1531
1529
1532 try:
1530 try:
1533 # updating the dirstate is optional
1531 # updating the dirstate is optional
1534 # so we don't wait on the lock
1532 # so we don't wait on the lock
1535 wlock = self.wlock(False)
1533 wlock = self.wlock(False)
1536 try:
1534 try:
1537 for f in fixup:
1535 for f in fixup:
1538 self.dirstate.normal(f)
1536 self.dirstate.normal(f)
1539 finally:
1537 finally:
1540 wlock.release()
1538 wlock.release()
1541 except error.LockError:
1539 except error.LockError:
1542 pass
1540 pass
1543
1541
1544 if not parentworking:
1542 if not parentworking:
1545 mf1 = mfmatches(ctx1)
1543 mf1 = mfmatches(ctx1)
1546 if working:
1544 if working:
1547 # we are comparing working dir against non-parent
1545 # we are comparing working dir against non-parent
1548 # generate a pseudo-manifest for the working dir
1546 # generate a pseudo-manifest for the working dir
1549 mf2 = mfmatches(self['.'])
1547 mf2 = mfmatches(self['.'])
1550 for f in cmp + modified + added:
1548 for f in cmp + modified + added:
1551 mf2[f] = None
1549 mf2[f] = None
1552 mf2.set(f, ctx2.flags(f))
1550 mf2.set(f, ctx2.flags(f))
1553 for f in removed:
1551 for f in removed:
1554 if f in mf2:
1552 if f in mf2:
1555 del mf2[f]
1553 del mf2[f]
1556 else:
1554 else:
1557 # we are comparing two revisions
1555 # we are comparing two revisions
1558 deleted, unknown, ignored = [], [], []
1556 deleted, unknown, ignored = [], [], []
1559 mf2 = mfmatches(ctx2)
1557 mf2 = mfmatches(ctx2)
1560
1558
1561 modified, added, clean = [], [], []
1559 modified, added, clean = [], [], []
1562 withflags = mf1.withflags() | mf2.withflags()
1560 withflags = mf1.withflags() | mf2.withflags()
1563 for fn in mf2:
1561 for fn in mf2:
1564 if fn in mf1:
1562 if fn in mf1:
1565 if (fn not in deleted and
1563 if (fn not in deleted and
1566 ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or
1564 ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or
1567 (mf1[fn] != mf2[fn] and
1565 (mf1[fn] != mf2[fn] and
1568 (mf2[fn] or ctx1[fn].cmp(ctx2[fn]))))):
1566 (mf2[fn] or ctx1[fn].cmp(ctx2[fn]))))):
1569 modified.append(fn)
1567 modified.append(fn)
1570 elif listclean:
1568 elif listclean:
1571 clean.append(fn)
1569 clean.append(fn)
1572 del mf1[fn]
1570 del mf1[fn]
1573 elif fn not in deleted:
1571 elif fn not in deleted:
1574 added.append(fn)
1572 added.append(fn)
1575 removed = mf1.keys()
1573 removed = mf1.keys()
1576
1574
1577 if working and modified and not self.dirstate._checklink:
1575 if working and modified and not self.dirstate._checklink:
1578 # Symlink placeholders may get non-symlink-like contents
1576 # Symlink placeholders may get non-symlink-like contents
1579 # via user error or dereferencing by NFS or Samba servers,
1577 # via user error or dereferencing by NFS or Samba servers,
1580 # so we filter out any placeholders that don't look like a
1578 # so we filter out any placeholders that don't look like a
1581 # symlink
1579 # symlink
1582 sane = []
1580 sane = []
1583 for f in modified:
1581 for f in modified:
1584 if ctx2.flags(f) == 'l':
1582 if ctx2.flags(f) == 'l':
1585 d = ctx2[f].data()
1583 d = ctx2[f].data()
1586 if len(d) >= 1024 or '\n' in d or util.binary(d):
1584 if len(d) >= 1024 or '\n' in d or util.binary(d):
1587 self.ui.debug('ignoring suspect symlink placeholder'
1585 self.ui.debug('ignoring suspect symlink placeholder'
1588 ' "%s"\n' % f)
1586 ' "%s"\n' % f)
1589 continue
1587 continue
1590 sane.append(f)
1588 sane.append(f)
1591 modified = sane
1589 modified = sane
1592
1590
1593 r = modified, added, removed, deleted, unknown, ignored, clean
1591 r = modified, added, removed, deleted, unknown, ignored, clean
1594
1592
1595 if listsubrepos:
1593 if listsubrepos:
1596 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1594 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1597 if working:
1595 if working:
1598 rev2 = None
1596 rev2 = None
1599 else:
1597 else:
1600 rev2 = ctx2.substate[subpath][1]
1598 rev2 = ctx2.substate[subpath][1]
1601 try:
1599 try:
1602 submatch = matchmod.narrowmatcher(subpath, match)
1600 submatch = matchmod.narrowmatcher(subpath, match)
1603 s = sub.status(rev2, match=submatch, ignored=listignored,
1601 s = sub.status(rev2, match=submatch, ignored=listignored,
1604 clean=listclean, unknown=listunknown,
1602 clean=listclean, unknown=listunknown,
1605 listsubrepos=True)
1603 listsubrepos=True)
1606 for rfiles, sfiles in zip(r, s):
1604 for rfiles, sfiles in zip(r, s):
1607 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1605 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1608 except error.LookupError:
1606 except error.LookupError:
1609 self.ui.status(_("skipping missing subrepository: %s\n")
1607 self.ui.status(_("skipping missing subrepository: %s\n")
1610 % subpath)
1608 % subpath)
1611
1609
1612 for l in r:
1610 for l in r:
1613 l.sort()
1611 l.sort()
1614 return r
1612 return r
1615
1613
1616 def heads(self, start=None):
1614 def heads(self, start=None):
1617 heads = self.changelog.heads(start)
1615 heads = self.changelog.heads(start)
1618 # sort the output in rev descending order
1616 # sort the output in rev descending order
1619 return sorted(heads, key=self.changelog.rev, reverse=True)
1617 return sorted(heads, key=self.changelog.rev, reverse=True)
1620
1618
1621 def branchheads(self, branch=None, start=None, closed=False):
1619 def branchheads(self, branch=None, start=None, closed=False):
1622 '''return a (possibly filtered) list of heads for the given branch
1620 '''return a (possibly filtered) list of heads for the given branch
1623
1621
1624 Heads are returned in topological order, from newest to oldest.
1622 Heads are returned in topological order, from newest to oldest.
1625 If branch is None, use the dirstate branch.
1623 If branch is None, use the dirstate branch.
1626 If start is not None, return only heads reachable from start.
1624 If start is not None, return only heads reachable from start.
1627 If closed is True, return heads that are marked as closed as well.
1625 If closed is True, return heads that are marked as closed as well.
1628 '''
1626 '''
1629 if branch is None:
1627 if branch is None:
1630 branch = self[None].branch()
1628 branch = self[None].branch()
1631 branches = self.branchmap()
1629 branches = self.branchmap()
1632 if branch not in branches:
1630 if branch not in branches:
1633 return []
1631 return []
1634 # the cache returns heads ordered lowest to highest
1632 # the cache returns heads ordered lowest to highest
1635 bheads = list(reversed(branches[branch]))
1633 bheads = list(reversed(branches[branch]))
1636 if start is not None:
1634 if start is not None:
1637 # filter out the heads that cannot be reached from startrev
1635 # filter out the heads that cannot be reached from startrev
1638 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1636 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1639 bheads = [h for h in bheads if h in fbheads]
1637 bheads = [h for h in bheads if h in fbheads]
1640 if not closed:
1638 if not closed:
1641 bheads = [h for h in bheads if not self[h].closesbranch()]
1639 bheads = [h for h in bheads if not self[h].closesbranch()]
1642 return bheads
1640 return bheads
1643
1641
1644 def branches(self, nodes):
1642 def branches(self, nodes):
1645 if not nodes:
1643 if not nodes:
1646 nodes = [self.changelog.tip()]
1644 nodes = [self.changelog.tip()]
1647 b = []
1645 b = []
1648 for n in nodes:
1646 for n in nodes:
1649 t = n
1647 t = n
1650 while True:
1648 while True:
1651 p = self.changelog.parents(n)
1649 p = self.changelog.parents(n)
1652 if p[1] != nullid or p[0] == nullid:
1650 if p[1] != nullid or p[0] == nullid:
1653 b.append((t, n, p[0], p[1]))
1651 b.append((t, n, p[0], p[1]))
1654 break
1652 break
1655 n = p[0]
1653 n = p[0]
1656 return b
1654 return b
1657
1655
1658 def between(self, pairs):
1656 def between(self, pairs):
1659 r = []
1657 r = []
1660
1658
1661 for top, bottom in pairs:
1659 for top, bottom in pairs:
1662 n, l, i = top, [], 0
1660 n, l, i = top, [], 0
1663 f = 1
1661 f = 1
1664
1662
1665 while n != bottom and n != nullid:
1663 while n != bottom and n != nullid:
1666 p = self.changelog.parents(n)[0]
1664 p = self.changelog.parents(n)[0]
1667 if i == f:
1665 if i == f:
1668 l.append(n)
1666 l.append(n)
1669 f = f * 2
1667 f = f * 2
1670 n = p
1668 n = p
1671 i += 1
1669 i += 1
1672
1670
1673 r.append(l)
1671 r.append(l)
1674
1672
1675 return r
1673 return r
1676
1674
1677 def pull(self, remote, heads=None, force=False):
1675 def pull(self, remote, heads=None, force=False):
1678 # don't open transaction for nothing or you break future useful
1676 # don't open transaction for nothing or you break future useful
1679 # rollback call
1677 # rollback call
1680 tr = None
1678 tr = None
1681 trname = 'pull\n' + util.hidepassword(remote.url())
1679 trname = 'pull\n' + util.hidepassword(remote.url())
1682 lock = self.lock()
1680 lock = self.lock()
1683 try:
1681 try:
1684 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1682 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1685 force=force)
1683 force=force)
1686 common, fetch, rheads = tmp
1684 common, fetch, rheads = tmp
1687 if not fetch:
1685 if not fetch:
1688 self.ui.status(_("no changes found\n"))
1686 self.ui.status(_("no changes found\n"))
1689 added = []
1687 added = []
1690 result = 0
1688 result = 0
1691 else:
1689 else:
1692 tr = self.transaction(trname)
1690 tr = self.transaction(trname)
1693 if heads is None and list(common) == [nullid]:
1691 if heads is None and list(common) == [nullid]:
1694 self.ui.status(_("requesting all changes\n"))
1692 self.ui.status(_("requesting all changes\n"))
1695 elif heads is None and remote.capable('changegroupsubset'):
1693 elif heads is None and remote.capable('changegroupsubset'):
1696 # issue1320, avoid a race if remote changed after discovery
1694 # issue1320, avoid a race if remote changed after discovery
1697 heads = rheads
1695 heads = rheads
1698
1696
1699 if remote.capable('getbundle'):
1697 if remote.capable('getbundle'):
1700 cg = remote.getbundle('pull', common=common,
1698 cg = remote.getbundle('pull', common=common,
1701 heads=heads or rheads)
1699 heads=heads or rheads)
1702 elif heads is None:
1700 elif heads is None:
1703 cg = remote.changegroup(fetch, 'pull')
1701 cg = remote.changegroup(fetch, 'pull')
1704 elif not remote.capable('changegroupsubset'):
1702 elif not remote.capable('changegroupsubset'):
1705 raise util.Abort(_("partial pull cannot be done because "
1703 raise util.Abort(_("partial pull cannot be done because "
1706 "other repository doesn't support "
1704 "other repository doesn't support "
1707 "changegroupsubset."))
1705 "changegroupsubset."))
1708 else:
1706 else:
1709 cg = remote.changegroupsubset(fetch, heads, 'pull')
1707 cg = remote.changegroupsubset(fetch, heads, 'pull')
1710 clstart = len(self.changelog)
1708 clstart = len(self.changelog)
1711 result = self.addchangegroup(cg, 'pull', remote.url())
1709 result = self.addchangegroup(cg, 'pull', remote.url())
1712 clend = len(self.changelog)
1710 clend = len(self.changelog)
1713 added = [self.changelog.node(r) for r in xrange(clstart, clend)]
1711 added = [self.changelog.node(r) for r in xrange(clstart, clend)]
1714
1712
1715 # compute target subset
1713 # compute target subset
1716 if heads is None:
1714 if heads is None:
1717 # We pulled every thing possible
1715 # We pulled every thing possible
1718 # sync on everything common
1716 # sync on everything common
1719 subset = common + added
1717 subset = common + added
1720 else:
1718 else:
1721 # We pulled a specific subset
1719 # We pulled a specific subset
1722 # sync on this subset
1720 # sync on this subset
1723 subset = heads
1721 subset = heads
1724
1722
1725 # Get remote phases data from remote
1723 # Get remote phases data from remote
1726 remotephases = remote.listkeys('phases')
1724 remotephases = remote.listkeys('phases')
1727 publishing = bool(remotephases.get('publishing', False))
1725 publishing = bool(remotephases.get('publishing', False))
1728 if remotephases and not publishing:
1726 if remotephases and not publishing:
1729 # remote is new and unpublishing
1727 # remote is new and unpublishing
1730 pheads, _dr = phases.analyzeremotephases(self, subset,
1728 pheads, _dr = phases.analyzeremotephases(self, subset,
1731 remotephases)
1729 remotephases)
1732 phases.advanceboundary(self, phases.public, pheads)
1730 phases.advanceboundary(self, phases.public, pheads)
1733 phases.advanceboundary(self, phases.draft, subset)
1731 phases.advanceboundary(self, phases.draft, subset)
1734 else:
1732 else:
1735 # Remote is old or publishing all common changesets
1733 # Remote is old or publishing all common changesets
1736 # should be seen as public
1734 # should be seen as public
1737 phases.advanceboundary(self, phases.public, subset)
1735 phases.advanceboundary(self, phases.public, subset)
1738
1736
1739 if obsolete._enabled:
1737 if obsolete._enabled:
1740 self.ui.debug('fetching remote obsolete markers\n')
1738 self.ui.debug('fetching remote obsolete markers\n')
1741 remoteobs = remote.listkeys('obsolete')
1739 remoteobs = remote.listkeys('obsolete')
1742 if 'dump0' in remoteobs:
1740 if 'dump0' in remoteobs:
1743 if tr is None:
1741 if tr is None:
1744 tr = self.transaction(trname)
1742 tr = self.transaction(trname)
1745 for key in sorted(remoteobs, reverse=True):
1743 for key in sorted(remoteobs, reverse=True):
1746 if key.startswith('dump'):
1744 if key.startswith('dump'):
1747 data = base85.b85decode(remoteobs[key])
1745 data = base85.b85decode(remoteobs[key])
1748 self.obsstore.mergemarkers(tr, data)
1746 self.obsstore.mergemarkers(tr, data)
1749 self.invalidatevolatilesets()
1747 self.invalidatevolatilesets()
1750 if tr is not None:
1748 if tr is not None:
1751 tr.close()
1749 tr.close()
1752 finally:
1750 finally:
1753 if tr is not None:
1751 if tr is not None:
1754 tr.release()
1752 tr.release()
1755 lock.release()
1753 lock.release()
1756
1754
1757 return result
1755 return result
1758
1756
1759 def checkpush(self, force, revs):
1757 def checkpush(self, force, revs):
1760 """Extensions can override this function if additional checks have
1758 """Extensions can override this function if additional checks have
1761 to be performed before pushing, or call it if they override push
1759 to be performed before pushing, or call it if they override push
1762 command.
1760 command.
1763 """
1761 """
1764 pass
1762 pass
1765
1763
1766 def push(self, remote, force=False, revs=None, newbranch=False):
1764 def push(self, remote, force=False, revs=None, newbranch=False):
1767 '''Push outgoing changesets (limited by revs) from the current
1765 '''Push outgoing changesets (limited by revs) from the current
1768 repository to remote. Return an integer:
1766 repository to remote. Return an integer:
1769 - None means nothing to push
1767 - None means nothing to push
1770 - 0 means HTTP error
1768 - 0 means HTTP error
1771 - 1 means we pushed and remote head count is unchanged *or*
1769 - 1 means we pushed and remote head count is unchanged *or*
1772 we have outgoing changesets but refused to push
1770 we have outgoing changesets but refused to push
1773 - other values as described by addchangegroup()
1771 - other values as described by addchangegroup()
1774 '''
1772 '''
1775 # there are two ways to push to remote repo:
1773 # there are two ways to push to remote repo:
1776 #
1774 #
1777 # addchangegroup assumes local user can lock remote
1775 # addchangegroup assumes local user can lock remote
1778 # repo (local filesystem, old ssh servers).
1776 # repo (local filesystem, old ssh servers).
1779 #
1777 #
1780 # unbundle assumes local user cannot lock remote repo (new ssh
1778 # unbundle assumes local user cannot lock remote repo (new ssh
1781 # servers, http servers).
1779 # servers, http servers).
1782
1780
1783 if not remote.canpush():
1781 if not remote.canpush():
1784 raise util.Abort(_("destination does not support push"))
1782 raise util.Abort(_("destination does not support push"))
1785 unfi = self.unfiltered()
1783 unfi = self.unfiltered()
1786 # get local lock as we might write phase data
1784 # get local lock as we might write phase data
1787 locallock = self.lock()
1785 locallock = self.lock()
1788 try:
1786 try:
1789 self.checkpush(force, revs)
1787 self.checkpush(force, revs)
1790 lock = None
1788 lock = None
1791 unbundle = remote.capable('unbundle')
1789 unbundle = remote.capable('unbundle')
1792 if not unbundle:
1790 if not unbundle:
1793 lock = remote.lock()
1791 lock = remote.lock()
1794 try:
1792 try:
1795 # discovery
1793 # discovery
1796 fci = discovery.findcommonincoming
1794 fci = discovery.findcommonincoming
1797 commoninc = fci(unfi, remote, force=force)
1795 commoninc = fci(unfi, remote, force=force)
1798 common, inc, remoteheads = commoninc
1796 common, inc, remoteheads = commoninc
1799 fco = discovery.findcommonoutgoing
1797 fco = discovery.findcommonoutgoing
1800 outgoing = fco(unfi, remote, onlyheads=revs,
1798 outgoing = fco(unfi, remote, onlyheads=revs,
1801 commoninc=commoninc, force=force)
1799 commoninc=commoninc, force=force)
1802
1800
1803
1801
1804 if not outgoing.missing:
1802 if not outgoing.missing:
1805 # nothing to push
1803 # nothing to push
1806 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
1804 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
1807 ret = None
1805 ret = None
1808 else:
1806 else:
1809 # something to push
1807 # something to push
1810 if not force:
1808 if not force:
1811 # if self.obsstore == False --> no obsolete
1809 # if self.obsstore == False --> no obsolete
1812 # then, save the iteration
1810 # then, save the iteration
1813 if unfi.obsstore:
1811 if unfi.obsstore:
1814 # this message are here for 80 char limit reason
1812 # this message are here for 80 char limit reason
1815 mso = _("push includes obsolete changeset: %s!")
1813 mso = _("push includes obsolete changeset: %s!")
1816 mst = "push includes %s changeset: %s!"
1814 mst = "push includes %s changeset: %s!"
1817 # plain versions for i18n tool to detect them
1815 # plain versions for i18n tool to detect them
1818 _("push includes unstable changeset: %s!")
1816 _("push includes unstable changeset: %s!")
1819 _("push includes bumped changeset: %s!")
1817 _("push includes bumped changeset: %s!")
1820 _("push includes divergent changeset: %s!")
1818 _("push includes divergent changeset: %s!")
1821 # If we are to push if there is at least one
1819 # If we are to push if there is at least one
1822 # obsolete or unstable changeset in missing, at
1820 # obsolete or unstable changeset in missing, at
1823 # least one of the missinghead will be obsolete or
1821 # least one of the missinghead will be obsolete or
1824 # unstable. So checking heads only is ok
1822 # unstable. So checking heads only is ok
1825 for node in outgoing.missingheads:
1823 for node in outgoing.missingheads:
1826 ctx = unfi[node]
1824 ctx = unfi[node]
1827 if ctx.obsolete():
1825 if ctx.obsolete():
1828 raise util.Abort(mso % ctx)
1826 raise util.Abort(mso % ctx)
1829 elif ctx.troubled():
1827 elif ctx.troubled():
1830 raise util.Abort(_(mst)
1828 raise util.Abort(_(mst)
1831 % (ctx.troubles()[0],
1829 % (ctx.troubles()[0],
1832 ctx))
1830 ctx))
1833 discovery.checkheads(unfi, remote, outgoing,
1831 discovery.checkheads(unfi, remote, outgoing,
1834 remoteheads, newbranch,
1832 remoteheads, newbranch,
1835 bool(inc))
1833 bool(inc))
1836
1834
1837 # create a changegroup from local
1835 # create a changegroup from local
1838 if revs is None and not outgoing.excluded:
1836 if revs is None and not outgoing.excluded:
1839 # push everything,
1837 # push everything,
1840 # use the fast path, no race possible on push
1838 # use the fast path, no race possible on push
1841 cg = self._changegroup(outgoing.missing, 'push')
1839 cg = self._changegroup(outgoing.missing, 'push')
1842 else:
1840 else:
1843 cg = self.getlocalbundle('push', outgoing)
1841 cg = self.getlocalbundle('push', outgoing)
1844
1842
1845 # apply changegroup to remote
1843 # apply changegroup to remote
1846 if unbundle:
1844 if unbundle:
1847 # local repo finds heads on server, finds out what
1845 # local repo finds heads on server, finds out what
1848 # revs it must push. once revs transferred, if server
1846 # revs it must push. once revs transferred, if server
1849 # finds it has different heads (someone else won
1847 # finds it has different heads (someone else won
1850 # commit/push race), server aborts.
1848 # commit/push race), server aborts.
1851 if force:
1849 if force:
1852 remoteheads = ['force']
1850 remoteheads = ['force']
1853 # ssh: return remote's addchangegroup()
1851 # ssh: return remote's addchangegroup()
1854 # http: return remote's addchangegroup() or 0 for error
1852 # http: return remote's addchangegroup() or 0 for error
1855 ret = remote.unbundle(cg, remoteheads, 'push')
1853 ret = remote.unbundle(cg, remoteheads, 'push')
1856 else:
1854 else:
1857 # we return an integer indicating remote head count
1855 # we return an integer indicating remote head count
1858 # change
1856 # change
1859 ret = remote.addchangegroup(cg, 'push', self.url())
1857 ret = remote.addchangegroup(cg, 'push', self.url())
1860
1858
1861 if ret:
1859 if ret:
1862 # push succeed, synchronize target of the push
1860 # push succeed, synchronize target of the push
1863 cheads = outgoing.missingheads
1861 cheads = outgoing.missingheads
1864 elif revs is None:
1862 elif revs is None:
1865 # All out push fails. synchronize all common
1863 # All out push fails. synchronize all common
1866 cheads = outgoing.commonheads
1864 cheads = outgoing.commonheads
1867 else:
1865 else:
1868 # I want cheads = heads(::missingheads and ::commonheads)
1866 # I want cheads = heads(::missingheads and ::commonheads)
1869 # (missingheads is revs with secret changeset filtered out)
1867 # (missingheads is revs with secret changeset filtered out)
1870 #
1868 #
1871 # This can be expressed as:
1869 # This can be expressed as:
1872 # cheads = ( (missingheads and ::commonheads)
1870 # cheads = ( (missingheads and ::commonheads)
1873 # + (commonheads and ::missingheads))"
1871 # + (commonheads and ::missingheads))"
1874 # )
1872 # )
1875 #
1873 #
1876 # while trying to push we already computed the following:
1874 # while trying to push we already computed the following:
1877 # common = (::commonheads)
1875 # common = (::commonheads)
1878 # missing = ((commonheads::missingheads) - commonheads)
1876 # missing = ((commonheads::missingheads) - commonheads)
1879 #
1877 #
1880 # We can pick:
1878 # We can pick:
1881 # * missingheads part of common (::commonheads)
1879 # * missingheads part of common (::commonheads)
1882 common = set(outgoing.common)
1880 common = set(outgoing.common)
1883 cheads = [node for node in revs if node in common]
1881 cheads = [node for node in revs if node in common]
1884 # and
1882 # and
1885 # * commonheads parents on missing
1883 # * commonheads parents on missing
1886 revset = unfi.set('%ln and parents(roots(%ln))',
1884 revset = unfi.set('%ln and parents(roots(%ln))',
1887 outgoing.commonheads,
1885 outgoing.commonheads,
1888 outgoing.missing)
1886 outgoing.missing)
1889 cheads.extend(c.node() for c in revset)
1887 cheads.extend(c.node() for c in revset)
1890 # even when we don't push, exchanging phase data is useful
1888 # even when we don't push, exchanging phase data is useful
1891 remotephases = remote.listkeys('phases')
1889 remotephases = remote.listkeys('phases')
1892 if not remotephases: # old server or public only repo
1890 if not remotephases: # old server or public only repo
1893 phases.advanceboundary(self, phases.public, cheads)
1891 phases.advanceboundary(self, phases.public, cheads)
1894 # don't push any phase data as there is nothing to push
1892 # don't push any phase data as there is nothing to push
1895 else:
1893 else:
1896 ana = phases.analyzeremotephases(self, cheads, remotephases)
1894 ana = phases.analyzeremotephases(self, cheads, remotephases)
1897 pheads, droots = ana
1895 pheads, droots = ana
1898 ### Apply remote phase on local
1896 ### Apply remote phase on local
1899 if remotephases.get('publishing', False):
1897 if remotephases.get('publishing', False):
1900 phases.advanceboundary(self, phases.public, cheads)
1898 phases.advanceboundary(self, phases.public, cheads)
1901 else: # publish = False
1899 else: # publish = False
1902 phases.advanceboundary(self, phases.public, pheads)
1900 phases.advanceboundary(self, phases.public, pheads)
1903 phases.advanceboundary(self, phases.draft, cheads)
1901 phases.advanceboundary(self, phases.draft, cheads)
1904 ### Apply local phase on remote
1902 ### Apply local phase on remote
1905
1903
1906 # Get the list of all revs draft on remote by public here.
1904 # Get the list of all revs draft on remote by public here.
1907 # XXX Beware that revset break if droots is not strictly
1905 # XXX Beware that revset break if droots is not strictly
1908 # XXX root we may want to ensure it is but it is costly
1906 # XXX root we may want to ensure it is but it is costly
1909 outdated = unfi.set('heads((%ln::%ln) and public())',
1907 outdated = unfi.set('heads((%ln::%ln) and public())',
1910 droots, cheads)
1908 droots, cheads)
1911 for newremotehead in outdated:
1909 for newremotehead in outdated:
1912 r = remote.pushkey('phases',
1910 r = remote.pushkey('phases',
1913 newremotehead.hex(),
1911 newremotehead.hex(),
1914 str(phases.draft),
1912 str(phases.draft),
1915 str(phases.public))
1913 str(phases.public))
1916 if not r:
1914 if not r:
1917 self.ui.warn(_('updating %s to public failed!\n')
1915 self.ui.warn(_('updating %s to public failed!\n')
1918 % newremotehead)
1916 % newremotehead)
1919 self.ui.debug('try to push obsolete markers to remote\n')
1917 self.ui.debug('try to push obsolete markers to remote\n')
1920 if (obsolete._enabled and self.obsstore and
1918 if (obsolete._enabled and self.obsstore and
1921 'obsolete' in remote.listkeys('namespaces')):
1919 'obsolete' in remote.listkeys('namespaces')):
1922 rslts = []
1920 rslts = []
1923 remotedata = self.listkeys('obsolete')
1921 remotedata = self.listkeys('obsolete')
1924 for key in sorted(remotedata, reverse=True):
1922 for key in sorted(remotedata, reverse=True):
1925 # reverse sort to ensure we end with dump0
1923 # reverse sort to ensure we end with dump0
1926 data = remotedata[key]
1924 data = remotedata[key]
1927 rslts.append(remote.pushkey('obsolete', key, '', data))
1925 rslts.append(remote.pushkey('obsolete', key, '', data))
1928 if [r for r in rslts if not r]:
1926 if [r for r in rslts if not r]:
1929 msg = _('failed to push some obsolete markers!\n')
1927 msg = _('failed to push some obsolete markers!\n')
1930 self.ui.warn(msg)
1928 self.ui.warn(msg)
1931 finally:
1929 finally:
1932 if lock is not None:
1930 if lock is not None:
1933 lock.release()
1931 lock.release()
1934 finally:
1932 finally:
1935 locallock.release()
1933 locallock.release()
1936
1934
1937 self.ui.debug("checking for updated bookmarks\n")
1935 self.ui.debug("checking for updated bookmarks\n")
1938 rb = remote.listkeys('bookmarks')
1936 rb = remote.listkeys('bookmarks')
1939 for k in rb.keys():
1937 for k in rb.keys():
1940 if k in unfi._bookmarks:
1938 if k in unfi._bookmarks:
1941 nr, nl = rb[k], hex(self._bookmarks[k])
1939 nr, nl = rb[k], hex(self._bookmarks[k])
1942 if nr in unfi:
1940 if nr in unfi:
1943 cr = unfi[nr]
1941 cr = unfi[nr]
1944 cl = unfi[nl]
1942 cl = unfi[nl]
1945 if bookmarks.validdest(unfi, cr, cl):
1943 if bookmarks.validdest(unfi, cr, cl):
1946 r = remote.pushkey('bookmarks', k, nr, nl)
1944 r = remote.pushkey('bookmarks', k, nr, nl)
1947 if r:
1945 if r:
1948 self.ui.status(_("updating bookmark %s\n") % k)
1946 self.ui.status(_("updating bookmark %s\n") % k)
1949 else:
1947 else:
1950 self.ui.warn(_('updating bookmark %s'
1948 self.ui.warn(_('updating bookmark %s'
1951 ' failed!\n') % k)
1949 ' failed!\n') % k)
1952
1950
1953 return ret
1951 return ret
1954
1952
1955 def changegroupinfo(self, nodes, source):
1953 def changegroupinfo(self, nodes, source):
1956 if self.ui.verbose or source == 'bundle':
1954 if self.ui.verbose or source == 'bundle':
1957 self.ui.status(_("%d changesets found\n") % len(nodes))
1955 self.ui.status(_("%d changesets found\n") % len(nodes))
1958 if self.ui.debugflag:
1956 if self.ui.debugflag:
1959 self.ui.debug("list of changesets:\n")
1957 self.ui.debug("list of changesets:\n")
1960 for node in nodes:
1958 for node in nodes:
1961 self.ui.debug("%s\n" % hex(node))
1959 self.ui.debug("%s\n" % hex(node))
1962
1960
1963 def changegroupsubset(self, bases, heads, source):
1961 def changegroupsubset(self, bases, heads, source):
1964 """Compute a changegroup consisting of all the nodes that are
1962 """Compute a changegroup consisting of all the nodes that are
1965 descendants of any of the bases and ancestors of any of the heads.
1963 descendants of any of the bases and ancestors of any of the heads.
1966 Return a chunkbuffer object whose read() method will return
1964 Return a chunkbuffer object whose read() method will return
1967 successive changegroup chunks.
1965 successive changegroup chunks.
1968
1966
1969 It is fairly complex as determining which filenodes and which
1967 It is fairly complex as determining which filenodes and which
1970 manifest nodes need to be included for the changeset to be complete
1968 manifest nodes need to be included for the changeset to be complete
1971 is non-trivial.
1969 is non-trivial.
1972
1970
1973 Another wrinkle is doing the reverse, figuring out which changeset in
1971 Another wrinkle is doing the reverse, figuring out which changeset in
1974 the changegroup a particular filenode or manifestnode belongs to.
1972 the changegroup a particular filenode or manifestnode belongs to.
1975 """
1973 """
1976 cl = self.changelog
1974 cl = self.changelog
1977 if not bases:
1975 if not bases:
1978 bases = [nullid]
1976 bases = [nullid]
1979 csets, bases, heads = cl.nodesbetween(bases, heads)
1977 csets, bases, heads = cl.nodesbetween(bases, heads)
1980 # We assume that all ancestors of bases are known
1978 # We assume that all ancestors of bases are known
1981 common = cl.ancestors([cl.rev(n) for n in bases])
1979 common = cl.ancestors([cl.rev(n) for n in bases])
1982 return self._changegroupsubset(common, csets, heads, source)
1980 return self._changegroupsubset(common, csets, heads, source)
1983
1981
1984 def getlocalbundle(self, source, outgoing):
1982 def getlocalbundle(self, source, outgoing):
1985 """Like getbundle, but taking a discovery.outgoing as an argument.
1983 """Like getbundle, but taking a discovery.outgoing as an argument.
1986
1984
1987 This is only implemented for local repos and reuses potentially
1985 This is only implemented for local repos and reuses potentially
1988 precomputed sets in outgoing."""
1986 precomputed sets in outgoing."""
1989 if not outgoing.missing:
1987 if not outgoing.missing:
1990 return None
1988 return None
1991 return self._changegroupsubset(outgoing.common,
1989 return self._changegroupsubset(outgoing.common,
1992 outgoing.missing,
1990 outgoing.missing,
1993 outgoing.missingheads,
1991 outgoing.missingheads,
1994 source)
1992 source)
1995
1993
1996 def getbundle(self, source, heads=None, common=None):
1994 def getbundle(self, source, heads=None, common=None):
1997 """Like changegroupsubset, but returns the set difference between the
1995 """Like changegroupsubset, but returns the set difference between the
1998 ancestors of heads and the ancestors common.
1996 ancestors of heads and the ancestors common.
1999
1997
2000 If heads is None, use the local heads. If common is None, use [nullid].
1998 If heads is None, use the local heads. If common is None, use [nullid].
2001
1999
2002 The nodes in common might not all be known locally due to the way the
2000 The nodes in common might not all be known locally due to the way the
2003 current discovery protocol works.
2001 current discovery protocol works.
2004 """
2002 """
2005 cl = self.changelog
2003 cl = self.changelog
2006 if common:
2004 if common:
2007 hasnode = cl.hasnode
2005 hasnode = cl.hasnode
2008 common = [n for n in common if hasnode(n)]
2006 common = [n for n in common if hasnode(n)]
2009 else:
2007 else:
2010 common = [nullid]
2008 common = [nullid]
2011 if not heads:
2009 if not heads:
2012 heads = cl.heads()
2010 heads = cl.heads()
2013 return self.getlocalbundle(source,
2011 return self.getlocalbundle(source,
2014 discovery.outgoing(cl, common, heads))
2012 discovery.outgoing(cl, common, heads))
2015
2013
2016 @unfilteredmethod
2014 @unfilteredmethod
2017 def _changegroupsubset(self, commonrevs, csets, heads, source):
2015 def _changegroupsubset(self, commonrevs, csets, heads, source):
2018
2016
2019 cl = self.changelog
2017 cl = self.changelog
2020 mf = self.manifest
2018 mf = self.manifest
2021 mfs = {} # needed manifests
2019 mfs = {} # needed manifests
2022 fnodes = {} # needed file nodes
2020 fnodes = {} # needed file nodes
2023 changedfiles = set()
2021 changedfiles = set()
2024 fstate = ['', {}]
2022 fstate = ['', {}]
2025 count = [0, 0]
2023 count = [0, 0]
2026
2024
2027 # can we go through the fast path ?
2025 # can we go through the fast path ?
2028 heads.sort()
2026 heads.sort()
2029 if heads == sorted(self.heads()):
2027 if heads == sorted(self.heads()):
2030 return self._changegroup(csets, source)
2028 return self._changegroup(csets, source)
2031
2029
2032 # slow path
2030 # slow path
2033 self.hook('preoutgoing', throw=True, source=source)
2031 self.hook('preoutgoing', throw=True, source=source)
2034 self.changegroupinfo(csets, source)
2032 self.changegroupinfo(csets, source)
2035
2033
2036 # filter any nodes that claim to be part of the known set
2034 # filter any nodes that claim to be part of the known set
2037 def prune(revlog, missing):
2035 def prune(revlog, missing):
2038 rr, rl = revlog.rev, revlog.linkrev
2036 rr, rl = revlog.rev, revlog.linkrev
2039 return [n for n in missing
2037 return [n for n in missing
2040 if rl(rr(n)) not in commonrevs]
2038 if rl(rr(n)) not in commonrevs]
2041
2039
2042 progress = self.ui.progress
2040 progress = self.ui.progress
2043 _bundling = _('bundling')
2041 _bundling = _('bundling')
2044 _changesets = _('changesets')
2042 _changesets = _('changesets')
2045 _manifests = _('manifests')
2043 _manifests = _('manifests')
2046 _files = _('files')
2044 _files = _('files')
2047
2045
2048 def lookup(revlog, x):
2046 def lookup(revlog, x):
2049 if revlog == cl:
2047 if revlog == cl:
2050 c = cl.read(x)
2048 c = cl.read(x)
2051 changedfiles.update(c[3])
2049 changedfiles.update(c[3])
2052 mfs.setdefault(c[0], x)
2050 mfs.setdefault(c[0], x)
2053 count[0] += 1
2051 count[0] += 1
2054 progress(_bundling, count[0],
2052 progress(_bundling, count[0],
2055 unit=_changesets, total=count[1])
2053 unit=_changesets, total=count[1])
2056 return x
2054 return x
2057 elif revlog == mf:
2055 elif revlog == mf:
2058 clnode = mfs[x]
2056 clnode = mfs[x]
2059 mdata = mf.readfast(x)
2057 mdata = mf.readfast(x)
2060 for f, n in mdata.iteritems():
2058 for f, n in mdata.iteritems():
2061 if f in changedfiles:
2059 if f in changedfiles:
2062 fnodes[f].setdefault(n, clnode)
2060 fnodes[f].setdefault(n, clnode)
2063 count[0] += 1
2061 count[0] += 1
2064 progress(_bundling, count[0],
2062 progress(_bundling, count[0],
2065 unit=_manifests, total=count[1])
2063 unit=_manifests, total=count[1])
2066 return clnode
2064 return clnode
2067 else:
2065 else:
2068 progress(_bundling, count[0], item=fstate[0],
2066 progress(_bundling, count[0], item=fstate[0],
2069 unit=_files, total=count[1])
2067 unit=_files, total=count[1])
2070 return fstate[1][x]
2068 return fstate[1][x]
2071
2069
2072 bundler = changegroup.bundle10(lookup)
2070 bundler = changegroup.bundle10(lookup)
2073 reorder = self.ui.config('bundle', 'reorder', 'auto')
2071 reorder = self.ui.config('bundle', 'reorder', 'auto')
2074 if reorder == 'auto':
2072 if reorder == 'auto':
2075 reorder = None
2073 reorder = None
2076 else:
2074 else:
2077 reorder = util.parsebool(reorder)
2075 reorder = util.parsebool(reorder)
2078
2076
2079 def gengroup():
2077 def gengroup():
2080 # Create a changenode group generator that will call our functions
2078 # Create a changenode group generator that will call our functions
2081 # back to lookup the owning changenode and collect information.
2079 # back to lookup the owning changenode and collect information.
2082 count[:] = [0, len(csets)]
2080 count[:] = [0, len(csets)]
2083 for chunk in cl.group(csets, bundler, reorder=reorder):
2081 for chunk in cl.group(csets, bundler, reorder=reorder):
2084 yield chunk
2082 yield chunk
2085 progress(_bundling, None)
2083 progress(_bundling, None)
2086
2084
2087 # Create a generator for the manifestnodes that calls our lookup
2085 # Create a generator for the manifestnodes that calls our lookup
2088 # and data collection functions back.
2086 # and data collection functions back.
2089 for f in changedfiles:
2087 for f in changedfiles:
2090 fnodes[f] = {}
2088 fnodes[f] = {}
2091 count[:] = [0, len(mfs)]
2089 count[:] = [0, len(mfs)]
2092 for chunk in mf.group(prune(mf, mfs), bundler, reorder=reorder):
2090 for chunk in mf.group(prune(mf, mfs), bundler, reorder=reorder):
2093 yield chunk
2091 yield chunk
2094 progress(_bundling, None)
2092 progress(_bundling, None)
2095
2093
2096 mfs.clear()
2094 mfs.clear()
2097
2095
2098 # Go through all our files in order sorted by name.
2096 # Go through all our files in order sorted by name.
2099 count[:] = [0, len(changedfiles)]
2097 count[:] = [0, len(changedfiles)]
2100 for fname in sorted(changedfiles):
2098 for fname in sorted(changedfiles):
2101 filerevlog = self.file(fname)
2099 filerevlog = self.file(fname)
2102 if not len(filerevlog):
2100 if not len(filerevlog):
2103 raise util.Abort(_("empty or missing revlog for %s")
2101 raise util.Abort(_("empty or missing revlog for %s")
2104 % fname)
2102 % fname)
2105 fstate[0] = fname
2103 fstate[0] = fname
2106 fstate[1] = fnodes.pop(fname, {})
2104 fstate[1] = fnodes.pop(fname, {})
2107
2105
2108 nodelist = prune(filerevlog, fstate[1])
2106 nodelist = prune(filerevlog, fstate[1])
2109 if nodelist:
2107 if nodelist:
2110 count[0] += 1
2108 count[0] += 1
2111 yield bundler.fileheader(fname)
2109 yield bundler.fileheader(fname)
2112 for chunk in filerevlog.group(nodelist, bundler, reorder):
2110 for chunk in filerevlog.group(nodelist, bundler, reorder):
2113 yield chunk
2111 yield chunk
2114
2112
2115 # Signal that no more groups are left.
2113 # Signal that no more groups are left.
2116 yield bundler.close()
2114 yield bundler.close()
2117 progress(_bundling, None)
2115 progress(_bundling, None)
2118
2116
2119 if csets:
2117 if csets:
2120 self.hook('outgoing', node=hex(csets[0]), source=source)
2118 self.hook('outgoing', node=hex(csets[0]), source=source)
2121
2119
2122 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
2120 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
2123
2121
2124 def changegroup(self, basenodes, source):
2122 def changegroup(self, basenodes, source):
2125 # to avoid a race we use changegroupsubset() (issue1320)
2123 # to avoid a race we use changegroupsubset() (issue1320)
2126 return self.changegroupsubset(basenodes, self.heads(), source)
2124 return self.changegroupsubset(basenodes, self.heads(), source)
2127
2125
2128 @unfilteredmethod
2126 @unfilteredmethod
2129 def _changegroup(self, nodes, source):
2127 def _changegroup(self, nodes, source):
2130 """Compute the changegroup of all nodes that we have that a recipient
2128 """Compute the changegroup of all nodes that we have that a recipient
2131 doesn't. Return a chunkbuffer object whose read() method will return
2129 doesn't. Return a chunkbuffer object whose read() method will return
2132 successive changegroup chunks.
2130 successive changegroup chunks.
2133
2131
2134 This is much easier than the previous function as we can assume that
2132 This is much easier than the previous function as we can assume that
2135 the recipient has any changenode we aren't sending them.
2133 the recipient has any changenode we aren't sending them.
2136
2134
2137 nodes is the set of nodes to send"""
2135 nodes is the set of nodes to send"""
2138
2136
2139 cl = self.changelog
2137 cl = self.changelog
2140 mf = self.manifest
2138 mf = self.manifest
2141 mfs = {}
2139 mfs = {}
2142 changedfiles = set()
2140 changedfiles = set()
2143 fstate = ['']
2141 fstate = ['']
2144 count = [0, 0]
2142 count = [0, 0]
2145
2143
2146 self.hook('preoutgoing', throw=True, source=source)
2144 self.hook('preoutgoing', throw=True, source=source)
2147 self.changegroupinfo(nodes, source)
2145 self.changegroupinfo(nodes, source)
2148
2146
2149 revset = set([cl.rev(n) for n in nodes])
2147 revset = set([cl.rev(n) for n in nodes])
2150
2148
2151 def gennodelst(log):
2149 def gennodelst(log):
2152 ln, llr = log.node, log.linkrev
2150 ln, llr = log.node, log.linkrev
2153 return [ln(r) for r in log if llr(r) in revset]
2151 return [ln(r) for r in log if llr(r) in revset]
2154
2152
2155 progress = self.ui.progress
2153 progress = self.ui.progress
2156 _bundling = _('bundling')
2154 _bundling = _('bundling')
2157 _changesets = _('changesets')
2155 _changesets = _('changesets')
2158 _manifests = _('manifests')
2156 _manifests = _('manifests')
2159 _files = _('files')
2157 _files = _('files')
2160
2158
2161 def lookup(revlog, x):
2159 def lookup(revlog, x):
2162 if revlog == cl:
2160 if revlog == cl:
2163 c = cl.read(x)
2161 c = cl.read(x)
2164 changedfiles.update(c[3])
2162 changedfiles.update(c[3])
2165 mfs.setdefault(c[0], x)
2163 mfs.setdefault(c[0], x)
2166 count[0] += 1
2164 count[0] += 1
2167 progress(_bundling, count[0],
2165 progress(_bundling, count[0],
2168 unit=_changesets, total=count[1])
2166 unit=_changesets, total=count[1])
2169 return x
2167 return x
2170 elif revlog == mf:
2168 elif revlog == mf:
2171 count[0] += 1
2169 count[0] += 1
2172 progress(_bundling, count[0],
2170 progress(_bundling, count[0],
2173 unit=_manifests, total=count[1])
2171 unit=_manifests, total=count[1])
2174 return cl.node(revlog.linkrev(revlog.rev(x)))
2172 return cl.node(revlog.linkrev(revlog.rev(x)))
2175 else:
2173 else:
2176 progress(_bundling, count[0], item=fstate[0],
2174 progress(_bundling, count[0], item=fstate[0],
2177 total=count[1], unit=_files)
2175 total=count[1], unit=_files)
2178 return cl.node(revlog.linkrev(revlog.rev(x)))
2176 return cl.node(revlog.linkrev(revlog.rev(x)))
2179
2177
2180 bundler = changegroup.bundle10(lookup)
2178 bundler = changegroup.bundle10(lookup)
2181 reorder = self.ui.config('bundle', 'reorder', 'auto')
2179 reorder = self.ui.config('bundle', 'reorder', 'auto')
2182 if reorder == 'auto':
2180 if reorder == 'auto':
2183 reorder = None
2181 reorder = None
2184 else:
2182 else:
2185 reorder = util.parsebool(reorder)
2183 reorder = util.parsebool(reorder)
2186
2184
2187 def gengroup():
2185 def gengroup():
2188 '''yield a sequence of changegroup chunks (strings)'''
2186 '''yield a sequence of changegroup chunks (strings)'''
2189 # construct a list of all changed files
2187 # construct a list of all changed files
2190
2188
2191 count[:] = [0, len(nodes)]
2189 count[:] = [0, len(nodes)]
2192 for chunk in cl.group(nodes, bundler, reorder=reorder):
2190 for chunk in cl.group(nodes, bundler, reorder=reorder):
2193 yield chunk
2191 yield chunk
2194 progress(_bundling, None)
2192 progress(_bundling, None)
2195
2193
2196 count[:] = [0, len(mfs)]
2194 count[:] = [0, len(mfs)]
2197 for chunk in mf.group(gennodelst(mf), bundler, reorder=reorder):
2195 for chunk in mf.group(gennodelst(mf), bundler, reorder=reorder):
2198 yield chunk
2196 yield chunk
2199 progress(_bundling, None)
2197 progress(_bundling, None)
2200
2198
2201 count[:] = [0, len(changedfiles)]
2199 count[:] = [0, len(changedfiles)]
2202 for fname in sorted(changedfiles):
2200 for fname in sorted(changedfiles):
2203 filerevlog = self.file(fname)
2201 filerevlog = self.file(fname)
2204 if not len(filerevlog):
2202 if not len(filerevlog):
2205 raise util.Abort(_("empty or missing revlog for %s")
2203 raise util.Abort(_("empty or missing revlog for %s")
2206 % fname)
2204 % fname)
2207 fstate[0] = fname
2205 fstate[0] = fname
2208 nodelist = gennodelst(filerevlog)
2206 nodelist = gennodelst(filerevlog)
2209 if nodelist:
2207 if nodelist:
2210 count[0] += 1
2208 count[0] += 1
2211 yield bundler.fileheader(fname)
2209 yield bundler.fileheader(fname)
2212 for chunk in filerevlog.group(nodelist, bundler, reorder):
2210 for chunk in filerevlog.group(nodelist, bundler, reorder):
2213 yield chunk
2211 yield chunk
2214 yield bundler.close()
2212 yield bundler.close()
2215 progress(_bundling, None)
2213 progress(_bundling, None)
2216
2214
2217 if nodes:
2215 if nodes:
2218 self.hook('outgoing', node=hex(nodes[0]), source=source)
2216 self.hook('outgoing', node=hex(nodes[0]), source=source)
2219
2217
2220 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
2218 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
2221
2219
2222 @unfilteredmethod
2220 @unfilteredmethod
2223 def addchangegroup(self, source, srctype, url, emptyok=False):
2221 def addchangegroup(self, source, srctype, url, emptyok=False):
2224 """Add the changegroup returned by source.read() to this repo.
2222 """Add the changegroup returned by source.read() to this repo.
2225 srctype is a string like 'push', 'pull', or 'unbundle'. url is
2223 srctype is a string like 'push', 'pull', or 'unbundle'. url is
2226 the URL of the repo where this changegroup is coming from.
2224 the URL of the repo where this changegroup is coming from.
2227
2225
2228 Return an integer summarizing the change to this repo:
2226 Return an integer summarizing the change to this repo:
2229 - nothing changed or no source: 0
2227 - nothing changed or no source: 0
2230 - more heads than before: 1+added heads (2..n)
2228 - more heads than before: 1+added heads (2..n)
2231 - fewer heads than before: -1-removed heads (-2..-n)
2229 - fewer heads than before: -1-removed heads (-2..-n)
2232 - number of heads stays the same: 1
2230 - number of heads stays the same: 1
2233 """
2231 """
2234 def csmap(x):
2232 def csmap(x):
2235 self.ui.debug("add changeset %s\n" % short(x))
2233 self.ui.debug("add changeset %s\n" % short(x))
2236 return len(cl)
2234 return len(cl)
2237
2235
2238 def revmap(x):
2236 def revmap(x):
2239 return cl.rev(x)
2237 return cl.rev(x)
2240
2238
2241 if not source:
2239 if not source:
2242 return 0
2240 return 0
2243
2241
2244 self.hook('prechangegroup', throw=True, source=srctype, url=url)
2242 self.hook('prechangegroup', throw=True, source=srctype, url=url)
2245
2243
2246 changesets = files = revisions = 0
2244 changesets = files = revisions = 0
2247 efiles = set()
2245 efiles = set()
2248
2246
2249 # write changelog data to temp files so concurrent readers will not see
2247 # write changelog data to temp files so concurrent readers will not see
2250 # inconsistent view
2248 # inconsistent view
2251 cl = self.changelog
2249 cl = self.changelog
2252 cl.delayupdate()
2250 cl.delayupdate()
2253 oldheads = cl.heads()
2251 oldheads = cl.heads()
2254
2252
2255 tr = self.transaction("\n".join([srctype, util.hidepassword(url)]))
2253 tr = self.transaction("\n".join([srctype, util.hidepassword(url)]))
2256 try:
2254 try:
2257 trp = weakref.proxy(tr)
2255 trp = weakref.proxy(tr)
2258 # pull off the changeset group
2256 # pull off the changeset group
2259 self.ui.status(_("adding changesets\n"))
2257 self.ui.status(_("adding changesets\n"))
2260 clstart = len(cl)
2258 clstart = len(cl)
2261 class prog(object):
2259 class prog(object):
2262 step = _('changesets')
2260 step = _('changesets')
2263 count = 1
2261 count = 1
2264 ui = self.ui
2262 ui = self.ui
2265 total = None
2263 total = None
2266 def __call__(self):
2264 def __call__(self):
2267 self.ui.progress(self.step, self.count, unit=_('chunks'),
2265 self.ui.progress(self.step, self.count, unit=_('chunks'),
2268 total=self.total)
2266 total=self.total)
2269 self.count += 1
2267 self.count += 1
2270 pr = prog()
2268 pr = prog()
2271 source.callback = pr
2269 source.callback = pr
2272
2270
2273 source.changelogheader()
2271 source.changelogheader()
2274 srccontent = cl.addgroup(source, csmap, trp)
2272 srccontent = cl.addgroup(source, csmap, trp)
2275 if not (srccontent or emptyok):
2273 if not (srccontent or emptyok):
2276 raise util.Abort(_("received changelog group is empty"))
2274 raise util.Abort(_("received changelog group is empty"))
2277 clend = len(cl)
2275 clend = len(cl)
2278 changesets = clend - clstart
2276 changesets = clend - clstart
2279 for c in xrange(clstart, clend):
2277 for c in xrange(clstart, clend):
2280 efiles.update(self[c].files())
2278 efiles.update(self[c].files())
2281 efiles = len(efiles)
2279 efiles = len(efiles)
2282 self.ui.progress(_('changesets'), None)
2280 self.ui.progress(_('changesets'), None)
2283
2281
2284 # pull off the manifest group
2282 # pull off the manifest group
2285 self.ui.status(_("adding manifests\n"))
2283 self.ui.status(_("adding manifests\n"))
2286 pr.step = _('manifests')
2284 pr.step = _('manifests')
2287 pr.count = 1
2285 pr.count = 1
2288 pr.total = changesets # manifests <= changesets
2286 pr.total = changesets # manifests <= changesets
2289 # no need to check for empty manifest group here:
2287 # no need to check for empty manifest group here:
2290 # if the result of the merge of 1 and 2 is the same in 3 and 4,
2288 # if the result of the merge of 1 and 2 is the same in 3 and 4,
2291 # no new manifest will be created and the manifest group will
2289 # no new manifest will be created and the manifest group will
2292 # be empty during the pull
2290 # be empty during the pull
2293 source.manifestheader()
2291 source.manifestheader()
2294 self.manifest.addgroup(source, revmap, trp)
2292 self.manifest.addgroup(source, revmap, trp)
2295 self.ui.progress(_('manifests'), None)
2293 self.ui.progress(_('manifests'), None)
2296
2294
2297 needfiles = {}
2295 needfiles = {}
2298 if self.ui.configbool('server', 'validate', default=False):
2296 if self.ui.configbool('server', 'validate', default=False):
2299 # validate incoming csets have their manifests
2297 # validate incoming csets have their manifests
2300 for cset in xrange(clstart, clend):
2298 for cset in xrange(clstart, clend):
2301 mfest = self.changelog.read(self.changelog.node(cset))[0]
2299 mfest = self.changelog.read(self.changelog.node(cset))[0]
2302 mfest = self.manifest.readdelta(mfest)
2300 mfest = self.manifest.readdelta(mfest)
2303 # store file nodes we must see
2301 # store file nodes we must see
2304 for f, n in mfest.iteritems():
2302 for f, n in mfest.iteritems():
2305 needfiles.setdefault(f, set()).add(n)
2303 needfiles.setdefault(f, set()).add(n)
2306
2304
2307 # process the files
2305 # process the files
2308 self.ui.status(_("adding file changes\n"))
2306 self.ui.status(_("adding file changes\n"))
2309 pr.step = _('files')
2307 pr.step = _('files')
2310 pr.count = 1
2308 pr.count = 1
2311 pr.total = efiles
2309 pr.total = efiles
2312 source.callback = None
2310 source.callback = None
2313
2311
2314 while True:
2312 while True:
2315 chunkdata = source.filelogheader()
2313 chunkdata = source.filelogheader()
2316 if not chunkdata:
2314 if not chunkdata:
2317 break
2315 break
2318 f = chunkdata["filename"]
2316 f = chunkdata["filename"]
2319 self.ui.debug("adding %s revisions\n" % f)
2317 self.ui.debug("adding %s revisions\n" % f)
2320 pr()
2318 pr()
2321 fl = self.file(f)
2319 fl = self.file(f)
2322 o = len(fl)
2320 o = len(fl)
2323 if not fl.addgroup(source, revmap, trp):
2321 if not fl.addgroup(source, revmap, trp):
2324 raise util.Abort(_("received file revlog group is empty"))
2322 raise util.Abort(_("received file revlog group is empty"))
2325 revisions += len(fl) - o
2323 revisions += len(fl) - o
2326 files += 1
2324 files += 1
2327 if f in needfiles:
2325 if f in needfiles:
2328 needs = needfiles[f]
2326 needs = needfiles[f]
2329 for new in xrange(o, len(fl)):
2327 for new in xrange(o, len(fl)):
2330 n = fl.node(new)
2328 n = fl.node(new)
2331 if n in needs:
2329 if n in needs:
2332 needs.remove(n)
2330 needs.remove(n)
2333 if not needs:
2331 if not needs:
2334 del needfiles[f]
2332 del needfiles[f]
2335 self.ui.progress(_('files'), None)
2333 self.ui.progress(_('files'), None)
2336
2334
2337 for f, needs in needfiles.iteritems():
2335 for f, needs in needfiles.iteritems():
2338 fl = self.file(f)
2336 fl = self.file(f)
2339 for n in needs:
2337 for n in needs:
2340 try:
2338 try:
2341 fl.rev(n)
2339 fl.rev(n)
2342 except error.LookupError:
2340 except error.LookupError:
2343 raise util.Abort(
2341 raise util.Abort(
2344 _('missing file data for %s:%s - run hg verify') %
2342 _('missing file data for %s:%s - run hg verify') %
2345 (f, hex(n)))
2343 (f, hex(n)))
2346
2344
2347 dh = 0
2345 dh = 0
2348 if oldheads:
2346 if oldheads:
2349 heads = cl.heads()
2347 heads = cl.heads()
2350 dh = len(heads) - len(oldheads)
2348 dh = len(heads) - len(oldheads)
2351 for h in heads:
2349 for h in heads:
2352 if h not in oldheads and self[h].closesbranch():
2350 if h not in oldheads and self[h].closesbranch():
2353 dh -= 1
2351 dh -= 1
2354 htext = ""
2352 htext = ""
2355 if dh:
2353 if dh:
2356 htext = _(" (%+d heads)") % dh
2354 htext = _(" (%+d heads)") % dh
2357
2355
2358 self.ui.status(_("added %d changesets"
2356 self.ui.status(_("added %d changesets"
2359 " with %d changes to %d files%s\n")
2357 " with %d changes to %d files%s\n")
2360 % (changesets, revisions, files, htext))
2358 % (changesets, revisions, files, htext))
2361 self.invalidatevolatilesets()
2359 self.invalidatevolatilesets()
2362
2360
2363 if changesets > 0:
2361 if changesets > 0:
2364 p = lambda: cl.writepending() and self.root or ""
2362 p = lambda: cl.writepending() and self.root or ""
2365 self.hook('pretxnchangegroup', throw=True,
2363 self.hook('pretxnchangegroup', throw=True,
2366 node=hex(cl.node(clstart)), source=srctype,
2364 node=hex(cl.node(clstart)), source=srctype,
2367 url=url, pending=p)
2365 url=url, pending=p)
2368
2366
2369 added = [cl.node(r) for r in xrange(clstart, clend)]
2367 added = [cl.node(r) for r in xrange(clstart, clend)]
2370 publishing = self.ui.configbool('phases', 'publish', True)
2368 publishing = self.ui.configbool('phases', 'publish', True)
2371 if srctype == 'push':
2369 if srctype == 'push':
2372 # Old server can not push the boundary themself.
2370 # Old server can not push the boundary themself.
2373 # New server won't push the boundary if changeset already
2371 # New server won't push the boundary if changeset already
2374 # existed locally as secrete
2372 # existed locally as secrete
2375 #
2373 #
2376 # We should not use added here but the list of all change in
2374 # We should not use added here but the list of all change in
2377 # the bundle
2375 # the bundle
2378 if publishing:
2376 if publishing:
2379 phases.advanceboundary(self, phases.public, srccontent)
2377 phases.advanceboundary(self, phases.public, srccontent)
2380 else:
2378 else:
2381 phases.advanceboundary(self, phases.draft, srccontent)
2379 phases.advanceboundary(self, phases.draft, srccontent)
2382 phases.retractboundary(self, phases.draft, added)
2380 phases.retractboundary(self, phases.draft, added)
2383 elif srctype != 'strip':
2381 elif srctype != 'strip':
2384 # publishing only alter behavior during push
2382 # publishing only alter behavior during push
2385 #
2383 #
2386 # strip should not touch boundary at all
2384 # strip should not touch boundary at all
2387 phases.retractboundary(self, phases.draft, added)
2385 phases.retractboundary(self, phases.draft, added)
2388
2386
2389 # make changelog see real files again
2387 # make changelog see real files again
2390 cl.finalize(trp)
2388 cl.finalize(trp)
2391
2389
2392 tr.close()
2390 tr.close()
2393
2391
2394 if changesets > 0:
2392 if changesets > 0:
2395 if srctype != 'strip':
2393 if srctype != 'strip':
2396 # During strip, branchcache is invalid but coming call to
2394 # During strip, branchcache is invalid but coming call to
2397 # `destroyed` will repair it.
2395 # `destroyed` will repair it.
2398 # In other case we can safely update cache on disk.
2396 # In other case we can safely update cache on disk.
2399 branchmap.updatecache(self)
2397 branchmap.updatecache(self)
2400 def runhooks():
2398 def runhooks():
2401 # forcefully update the on-disk branch cache
2399 # forcefully update the on-disk branch cache
2402 self.ui.debug("updating the branch cache\n")
2400 self.ui.debug("updating the branch cache\n")
2403 self.hook("changegroup", node=hex(cl.node(clstart)),
2401 self.hook("changegroup", node=hex(cl.node(clstart)),
2404 source=srctype, url=url)
2402 source=srctype, url=url)
2405
2403
2406 for n in added:
2404 for n in added:
2407 self.hook("incoming", node=hex(n), source=srctype,
2405 self.hook("incoming", node=hex(n), source=srctype,
2408 url=url)
2406 url=url)
2409 self._afterlock(runhooks)
2407 self._afterlock(runhooks)
2410
2408
2411 finally:
2409 finally:
2412 tr.release()
2410 tr.release()
2413 # never return 0 here:
2411 # never return 0 here:
2414 if dh < 0:
2412 if dh < 0:
2415 return dh - 1
2413 return dh - 1
2416 else:
2414 else:
2417 return dh + 1
2415 return dh + 1
2418
2416
2419 def stream_in(self, remote, requirements):
2417 def stream_in(self, remote, requirements):
2420 lock = self.lock()
2418 lock = self.lock()
2421 try:
2419 try:
2422 # Save remote branchmap. We will use it later
2420 # Save remote branchmap. We will use it later
2423 # to speed up branchcache creation
2421 # to speed up branchcache creation
2424 rbranchmap = None
2422 rbranchmap = None
2425 if remote.capable("branchmap"):
2423 if remote.capable("branchmap"):
2426 rbranchmap = remote.branchmap()
2424 rbranchmap = remote.branchmap()
2427
2425
2428 fp = remote.stream_out()
2426 fp = remote.stream_out()
2429 l = fp.readline()
2427 l = fp.readline()
2430 try:
2428 try:
2431 resp = int(l)
2429 resp = int(l)
2432 except ValueError:
2430 except ValueError:
2433 raise error.ResponseError(
2431 raise error.ResponseError(
2434 _('unexpected response from remote server:'), l)
2432 _('unexpected response from remote server:'), l)
2435 if resp == 1:
2433 if resp == 1:
2436 raise util.Abort(_('operation forbidden by server'))
2434 raise util.Abort(_('operation forbidden by server'))
2437 elif resp == 2:
2435 elif resp == 2:
2438 raise util.Abort(_('locking the remote repository failed'))
2436 raise util.Abort(_('locking the remote repository failed'))
2439 elif resp != 0:
2437 elif resp != 0:
2440 raise util.Abort(_('the server sent an unknown error code'))
2438 raise util.Abort(_('the server sent an unknown error code'))
2441 self.ui.status(_('streaming all changes\n'))
2439 self.ui.status(_('streaming all changes\n'))
2442 l = fp.readline()
2440 l = fp.readline()
2443 try:
2441 try:
2444 total_files, total_bytes = map(int, l.split(' ', 1))
2442 total_files, total_bytes = map(int, l.split(' ', 1))
2445 except (ValueError, TypeError):
2443 except (ValueError, TypeError):
2446 raise error.ResponseError(
2444 raise error.ResponseError(
2447 _('unexpected response from remote server:'), l)
2445 _('unexpected response from remote server:'), l)
2448 self.ui.status(_('%d files to transfer, %s of data\n') %
2446 self.ui.status(_('%d files to transfer, %s of data\n') %
2449 (total_files, util.bytecount(total_bytes)))
2447 (total_files, util.bytecount(total_bytes)))
2450 handled_bytes = 0
2448 handled_bytes = 0
2451 self.ui.progress(_('clone'), 0, total=total_bytes)
2449 self.ui.progress(_('clone'), 0, total=total_bytes)
2452 start = time.time()
2450 start = time.time()
2453 for i in xrange(total_files):
2451 for i in xrange(total_files):
2454 # XXX doesn't support '\n' or '\r' in filenames
2452 # XXX doesn't support '\n' or '\r' in filenames
2455 l = fp.readline()
2453 l = fp.readline()
2456 try:
2454 try:
2457 name, size = l.split('\0', 1)
2455 name, size = l.split('\0', 1)
2458 size = int(size)
2456 size = int(size)
2459 except (ValueError, TypeError):
2457 except (ValueError, TypeError):
2460 raise error.ResponseError(
2458 raise error.ResponseError(
2461 _('unexpected response from remote server:'), l)
2459 _('unexpected response from remote server:'), l)
2462 if self.ui.debugflag:
2460 if self.ui.debugflag:
2463 self.ui.debug('adding %s (%s)\n' %
2461 self.ui.debug('adding %s (%s)\n' %
2464 (name, util.bytecount(size)))
2462 (name, util.bytecount(size)))
2465 # for backwards compat, name was partially encoded
2463 # for backwards compat, name was partially encoded
2466 ofp = self.sopener(store.decodedir(name), 'w')
2464 ofp = self.sopener(store.decodedir(name), 'w')
2467 for chunk in util.filechunkiter(fp, limit=size):
2465 for chunk in util.filechunkiter(fp, limit=size):
2468 handled_bytes += len(chunk)
2466 handled_bytes += len(chunk)
2469 self.ui.progress(_('clone'), handled_bytes,
2467 self.ui.progress(_('clone'), handled_bytes,
2470 total=total_bytes)
2468 total=total_bytes)
2471 ofp.write(chunk)
2469 ofp.write(chunk)
2472 ofp.close()
2470 ofp.close()
2473 elapsed = time.time() - start
2471 elapsed = time.time() - start
2474 if elapsed <= 0:
2472 if elapsed <= 0:
2475 elapsed = 0.001
2473 elapsed = 0.001
2476 self.ui.progress(_('clone'), None)
2474 self.ui.progress(_('clone'), None)
2477 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2475 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2478 (util.bytecount(total_bytes), elapsed,
2476 (util.bytecount(total_bytes), elapsed,
2479 util.bytecount(total_bytes / elapsed)))
2477 util.bytecount(total_bytes / elapsed)))
2480
2478
2481 # new requirements = old non-format requirements +
2479 # new requirements = old non-format requirements +
2482 # new format-related
2480 # new format-related
2483 # requirements from the streamed-in repository
2481 # requirements from the streamed-in repository
2484 requirements.update(set(self.requirements) - self.supportedformats)
2482 requirements.update(set(self.requirements) - self.supportedformats)
2485 self._applyrequirements(requirements)
2483 self._applyrequirements(requirements)
2486 self._writerequirements()
2484 self._writerequirements()
2487
2485
2488 if rbranchmap:
2486 if rbranchmap:
2489 rbheads = []
2487 rbheads = []
2490 for bheads in rbranchmap.itervalues():
2488 for bheads in rbranchmap.itervalues():
2491 rbheads.extend(bheads)
2489 rbheads.extend(bheads)
2492
2490
2493 if rbheads:
2491 if rbheads:
2494 rtiprev = max((int(self.changelog.rev(node))
2492 rtiprev = max((int(self.changelog.rev(node))
2495 for node in rbheads))
2493 for node in rbheads))
2496 cache = branchmap.branchcache(rbranchmap,
2494 cache = branchmap.branchcache(rbranchmap,
2497 self[rtiprev].node(),
2495 self[rtiprev].node(),
2498 rtiprev)
2496 rtiprev)
2499 self._branchcaches[None] = cache
2497 self._branchcaches[None] = cache
2500 cache.write(self.unfiltered())
2498 cache.write(self.unfiltered())
2501 self.invalidate()
2499 self.invalidate()
2502 return len(self.heads()) + 1
2500 return len(self.heads()) + 1
2503 finally:
2501 finally:
2504 lock.release()
2502 lock.release()
2505
2503
2506 def clone(self, remote, heads=[], stream=False):
2504 def clone(self, remote, heads=[], stream=False):
2507 '''clone remote repository.
2505 '''clone remote repository.
2508
2506
2509 keyword arguments:
2507 keyword arguments:
2510 heads: list of revs to clone (forces use of pull)
2508 heads: list of revs to clone (forces use of pull)
2511 stream: use streaming clone if possible'''
2509 stream: use streaming clone if possible'''
2512
2510
2513 # now, all clients that can request uncompressed clones can
2511 # now, all clients that can request uncompressed clones can
2514 # read repo formats supported by all servers that can serve
2512 # read repo formats supported by all servers that can serve
2515 # them.
2513 # them.
2516
2514
2517 # if revlog format changes, client will have to check version
2515 # if revlog format changes, client will have to check version
2518 # and format flags on "stream" capability, and use
2516 # and format flags on "stream" capability, and use
2519 # uncompressed only if compatible.
2517 # uncompressed only if compatible.
2520
2518
2521 if not stream:
2519 if not stream:
2522 # if the server explicitly prefers to stream (for fast LANs)
2520 # if the server explicitly prefers to stream (for fast LANs)
2523 stream = remote.capable('stream-preferred')
2521 stream = remote.capable('stream-preferred')
2524
2522
2525 if stream and not heads:
2523 if stream and not heads:
2526 # 'stream' means remote revlog format is revlogv1 only
2524 # 'stream' means remote revlog format is revlogv1 only
2527 if remote.capable('stream'):
2525 if remote.capable('stream'):
2528 return self.stream_in(remote, set(('revlogv1',)))
2526 return self.stream_in(remote, set(('revlogv1',)))
2529 # otherwise, 'streamreqs' contains the remote revlog format
2527 # otherwise, 'streamreqs' contains the remote revlog format
2530 streamreqs = remote.capable('streamreqs')
2528 streamreqs = remote.capable('streamreqs')
2531 if streamreqs:
2529 if streamreqs:
2532 streamreqs = set(streamreqs.split(','))
2530 streamreqs = set(streamreqs.split(','))
2533 # if we support it, stream in and adjust our requirements
2531 # if we support it, stream in and adjust our requirements
2534 if not streamreqs - self.supportedformats:
2532 if not streamreqs - self.supportedformats:
2535 return self.stream_in(remote, streamreqs)
2533 return self.stream_in(remote, streamreqs)
2536 return self.pull(remote, heads)
2534 return self.pull(remote, heads)
2537
2535
2538 def pushkey(self, namespace, key, old, new):
2536 def pushkey(self, namespace, key, old, new):
2539 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
2537 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
2540 old=old, new=new)
2538 old=old, new=new)
2541 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
2539 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
2542 ret = pushkey.push(self, namespace, key, old, new)
2540 ret = pushkey.push(self, namespace, key, old, new)
2543 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
2541 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
2544 ret=ret)
2542 ret=ret)
2545 return ret
2543 return ret
2546
2544
2547 def listkeys(self, namespace):
2545 def listkeys(self, namespace):
2548 self.hook('prelistkeys', throw=True, namespace=namespace)
2546 self.hook('prelistkeys', throw=True, namespace=namespace)
2549 self.ui.debug('listing keys for "%s"\n' % namespace)
2547 self.ui.debug('listing keys for "%s"\n' % namespace)
2550 values = pushkey.list(self, namespace)
2548 values = pushkey.list(self, namespace)
2551 self.hook('listkeys', namespace=namespace, values=values)
2549 self.hook('listkeys', namespace=namespace, values=values)
2552 return values
2550 return values
2553
2551
2554 def debugwireargs(self, one, two, three=None, four=None, five=None):
2552 def debugwireargs(self, one, two, three=None, four=None, five=None):
2555 '''used to test argument passing over the wire'''
2553 '''used to test argument passing over the wire'''
2556 return "%s %s %s %s %s" % (one, two, three, four, five)
2554 return "%s %s %s %s %s" % (one, two, three, four, five)
2557
2555
2558 def savecommitmessage(self, text):
2556 def savecommitmessage(self, text):
2559 fp = self.opener('last-message.txt', 'wb')
2557 fp = self.opener('last-message.txt', 'wb')
2560 try:
2558 try:
2561 fp.write(text)
2559 fp.write(text)
2562 finally:
2560 finally:
2563 fp.close()
2561 fp.close()
2564 return self.pathto(fp.name[len(self.root) + 1:])
2562 return self.pathto(fp.name[len(self.root) + 1:])
2565
2563
2566 # used to avoid circular references so destructors work
2564 # used to avoid circular references so destructors work
2567 def aftertrans(files):
2565 def aftertrans(files):
2568 renamefiles = [tuple(t) for t in files]
2566 renamefiles = [tuple(t) for t in files]
2569 def a():
2567 def a():
2570 for src, dest in renamefiles:
2568 for src, dest in renamefiles:
2571 try:
2569 try:
2572 util.rename(src, dest)
2570 util.rename(src, dest)
2573 except OSError: # journal file does not yet exist
2571 except OSError: # journal file does not yet exist
2574 pass
2572 pass
2575 return a
2573 return a
2576
2574
2577 def undoname(fn):
2575 def undoname(fn):
2578 base, name = os.path.split(fn)
2576 base, name = os.path.split(fn)
2579 assert name.startswith('journal')
2577 assert name.startswith('journal')
2580 return os.path.join(base, name.replace('journal', 'undo', 1))
2578 return os.path.join(base, name.replace('journal', 'undo', 1))
2581
2579
2582 def instance(ui, path, create):
2580 def instance(ui, path, create):
2583 return localrepository(ui, util.urllocalpath(path), create)
2581 return localrepository(ui, util.urllocalpath(path), create)
2584
2582
2585 def islocal(path):
2583 def islocal(path):
2586 return True
2584 return True
@@ -1,2133 +1,2122 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
91 adding changesets
92 bundling: 1/3 changesets (33.33%)
92 bundling: 1/3 changesets (33.33%)
93 bundling: 2/3 changesets (66.67%)
93 bundling: 2/3 changesets (66.67%)
94 bundling: 3/3 changesets (100.00%)
94 bundling: 3/3 changesets (100.00%)
95 bundling: 1/3 manifests (33.33%)
95 bundling: 1/3 manifests (33.33%)
96 bundling: 2/3 manifests (66.67%)
96 bundling: 2/3 manifests (66.67%)
97 bundling: 3/3 manifests (100.00%)
97 bundling: 3/3 manifests (100.00%)
98 bundling: foo/Bar/file.txt 1/3 files (33.33%)
98 bundling: foo/Bar/file.txt 1/3 files (33.33%)
99 bundling: foo/file.txt 2/3 files (66.67%)
99 bundling: foo/file.txt 2/3 files (66.67%)
100 bundling: quux/file.py 3/3 files (100.00%)
100 bundling: quux/file.py 3/3 files (100.00%)
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 listing keys for "phases"
119 listing keys for "phases"
120 try to push obsolete markers to remote
120 try to push obsolete markers to remote
121 updating the branch cache
121 updating the branch cache
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: tip differs
144 listing keys for "bookmarks"
143 listing keys for "bookmarks"
145 3 changesets found
144 3 changesets found
146 list of changesets:
145 list of changesets:
147 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
146 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
148 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
147 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
149 911600dab2ae7a9baff75958b84fe606851ce955
148 911600dab2ae7a9baff75958b84fe606851ce955
150 adding changesets
149 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%)
160 changesets: 1 chunks
159 changesets: 1 chunks
161 add changeset ef1ea85a6374
160 add changeset ef1ea85a6374
162 changesets: 2 chunks
161 changesets: 2 chunks
163 add changeset f9cafe1212c8
162 add changeset f9cafe1212c8
164 changesets: 3 chunks
163 changesets: 3 chunks
165 add changeset 911600dab2ae
164 add changeset 911600dab2ae
166 adding manifests
165 adding manifests
167 manifests: 1/3 chunks (33.33%)
166 manifests: 1/3 chunks (33.33%)
168 manifests: 2/3 chunks (66.67%)
167 manifests: 2/3 chunks (66.67%)
169 manifests: 3/3 chunks (100.00%)
168 manifests: 3/3 chunks (100.00%)
170 adding file changes
169 adding file changes
171 adding foo/Bar/file.txt revisions
170 adding foo/Bar/file.txt revisions
172 files: 1/3 chunks (33.33%)
171 files: 1/3 chunks (33.33%)
173 adding foo/file.txt revisions
172 adding foo/file.txt revisions
174 files: 2/3 chunks (66.67%)
173 files: 2/3 chunks (66.67%)
175 adding quux/file.py revisions
174 adding quux/file.py revisions
176 files: 3/3 chunks (100.00%)
175 files: 3/3 chunks (100.00%)
177 added 3 changesets with 3 changes to 3 files
176 added 3 changesets with 3 changes to 3 files
178 calling hook pretxnchangegroup.acl: hgext.acl.hook
177 calling hook pretxnchangegroup.acl: hgext.acl.hook
179 acl: changes have source "push" - skipping
178 acl: changes have source "push" - skipping
180 listing keys for "phases"
179 listing keys for "phases"
181 try to push obsolete markers to remote
180 try to push obsolete markers to remote
182 updating the branch cache
181 updating the branch cache
183 checking for updated bookmarks
182 checking for updated bookmarks
184 listing keys for "bookmarks"
183 listing keys for "bookmarks"
185 repository tip rolled back to revision 0 (undo push)
184 repository tip rolled back to revision 0 (undo push)
186 0:6675d58eff77
185 0:6675d58eff77
187
186
188
187
189 No [acl.allow]/[acl.deny]
188 No [acl.allow]/[acl.deny]
190
189
191 $ echo '[acl]' >> $config
190 $ echo '[acl]' >> $config
192 $ echo 'sources = push' >> $config
191 $ echo 'sources = push' >> $config
193 $ do_push fred
192 $ do_push fred
194 Pushing as user fred
193 Pushing as user fred
195 hgrc = """
194 hgrc = """
196 [hooks]
195 [hooks]
197 pretxnchangegroup.acl = python:hgext.acl.hook
196 pretxnchangegroup.acl = python:hgext.acl.hook
198 [acl]
197 [acl]
199 sources = push
198 sources = push
200 """
199 """
201 pushing to ../b
200 pushing to ../b
202 query 1; heads
201 query 1; heads
203 searching for changes
202 searching for changes
204 all remote heads known locally
203 all remote heads known locally
205 invalid branchheads cache: tip differs
206 listing keys for "bookmarks"
204 listing keys for "bookmarks"
207 3 changesets found
205 3 changesets found
208 list of changesets:
206 list of changesets:
209 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
207 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
210 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
208 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
211 911600dab2ae7a9baff75958b84fe606851ce955
209 911600dab2ae7a9baff75958b84fe606851ce955
212 adding changesets
210 adding changesets
213 bundling: 1/3 changesets (33.33%)
211 bundling: 1/3 changesets (33.33%)
214 bundling: 2/3 changesets (66.67%)
212 bundling: 2/3 changesets (66.67%)
215 bundling: 3/3 changesets (100.00%)
213 bundling: 3/3 changesets (100.00%)
216 bundling: 1/3 manifests (33.33%)
214 bundling: 1/3 manifests (33.33%)
217 bundling: 2/3 manifests (66.67%)
215 bundling: 2/3 manifests (66.67%)
218 bundling: 3/3 manifests (100.00%)
216 bundling: 3/3 manifests (100.00%)
219 bundling: foo/Bar/file.txt 1/3 files (33.33%)
217 bundling: foo/Bar/file.txt 1/3 files (33.33%)
220 bundling: foo/file.txt 2/3 files (66.67%)
218 bundling: foo/file.txt 2/3 files (66.67%)
221 bundling: quux/file.py 3/3 files (100.00%)
219 bundling: quux/file.py 3/3 files (100.00%)
222 changesets: 1 chunks
220 changesets: 1 chunks
223 add changeset ef1ea85a6374
221 add changeset ef1ea85a6374
224 changesets: 2 chunks
222 changesets: 2 chunks
225 add changeset f9cafe1212c8
223 add changeset f9cafe1212c8
226 changesets: 3 chunks
224 changesets: 3 chunks
227 add changeset 911600dab2ae
225 add changeset 911600dab2ae
228 adding manifests
226 adding manifests
229 manifests: 1/3 chunks (33.33%)
227 manifests: 1/3 chunks (33.33%)
230 manifests: 2/3 chunks (66.67%)
228 manifests: 2/3 chunks (66.67%)
231 manifests: 3/3 chunks (100.00%)
229 manifests: 3/3 chunks (100.00%)
232 adding file changes
230 adding file changes
233 adding foo/Bar/file.txt revisions
231 adding foo/Bar/file.txt revisions
234 files: 1/3 chunks (33.33%)
232 files: 1/3 chunks (33.33%)
235 adding foo/file.txt revisions
233 adding foo/file.txt revisions
236 files: 2/3 chunks (66.67%)
234 files: 2/3 chunks (66.67%)
237 adding quux/file.py revisions
235 adding quux/file.py revisions
238 files: 3/3 chunks (100.00%)
236 files: 3/3 chunks (100.00%)
239 added 3 changesets with 3 changes to 3 files
237 added 3 changesets with 3 changes to 3 files
240 calling hook pretxnchangegroup.acl: hgext.acl.hook
238 calling hook pretxnchangegroup.acl: hgext.acl.hook
241 acl: checking access for user "fred"
239 acl: checking access for user "fred"
242 acl: acl.allow.branches not enabled
240 acl: acl.allow.branches not enabled
243 acl: acl.deny.branches not enabled
241 acl: acl.deny.branches not enabled
244 acl: acl.allow not enabled
242 acl: acl.allow not enabled
245 acl: acl.deny not enabled
243 acl: acl.deny not enabled
246 acl: branch access granted: "ef1ea85a6374" on branch "default"
244 acl: branch access granted: "ef1ea85a6374" on branch "default"
247 acl: path access granted: "ef1ea85a6374"
245 acl: path access granted: "ef1ea85a6374"
248 acl: branch access granted: "f9cafe1212c8" on branch "default"
246 acl: branch access granted: "f9cafe1212c8" on branch "default"
249 acl: path access granted: "f9cafe1212c8"
247 acl: path access granted: "f9cafe1212c8"
250 acl: branch access granted: "911600dab2ae" on branch "default"
248 acl: branch access granted: "911600dab2ae" on branch "default"
251 acl: path access granted: "911600dab2ae"
249 acl: path access granted: "911600dab2ae"
252 listing keys for "phases"
250 listing keys for "phases"
253 try to push obsolete markers to remote
251 try to push obsolete markers to remote
254 updating the branch cache
252 updating the branch cache
255 checking for updated bookmarks
253 checking for updated bookmarks
256 listing keys for "bookmarks"
254 listing keys for "bookmarks"
257 repository tip rolled back to revision 0 (undo push)
255 repository tip rolled back to revision 0 (undo push)
258 0:6675d58eff77
256 0:6675d58eff77
259
257
260
258
261 Empty [acl.allow]
259 Empty [acl.allow]
262
260
263 $ echo '[acl.allow]' >> $config
261 $ echo '[acl.allow]' >> $config
264 $ do_push fred
262 $ do_push fred
265 Pushing as user fred
263 Pushing as user fred
266 hgrc = """
264 hgrc = """
267 [hooks]
265 [hooks]
268 pretxnchangegroup.acl = python:hgext.acl.hook
266 pretxnchangegroup.acl = python:hgext.acl.hook
269 [acl]
267 [acl]
270 sources = push
268 sources = push
271 [acl.allow]
269 [acl.allow]
272 """
270 """
273 pushing to ../b
271 pushing to ../b
274 query 1; heads
272 query 1; heads
275 searching for changes
273 searching for changes
276 all remote heads known locally
274 all remote heads known locally
277 invalid branchheads cache: tip differs
278 listing keys for "bookmarks"
275 listing keys for "bookmarks"
279 3 changesets found
276 3 changesets found
280 list of changesets:
277 list of changesets:
281 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
278 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
282 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
279 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
283 911600dab2ae7a9baff75958b84fe606851ce955
280 911600dab2ae7a9baff75958b84fe606851ce955
284 adding changesets
281 adding changesets
285 bundling: 1/3 changesets (33.33%)
282 bundling: 1/3 changesets (33.33%)
286 bundling: 2/3 changesets (66.67%)
283 bundling: 2/3 changesets (66.67%)
287 bundling: 3/3 changesets (100.00%)
284 bundling: 3/3 changesets (100.00%)
288 bundling: 1/3 manifests (33.33%)
285 bundling: 1/3 manifests (33.33%)
289 bundling: 2/3 manifests (66.67%)
286 bundling: 2/3 manifests (66.67%)
290 bundling: 3/3 manifests (100.00%)
287 bundling: 3/3 manifests (100.00%)
291 bundling: foo/Bar/file.txt 1/3 files (33.33%)
288 bundling: foo/Bar/file.txt 1/3 files (33.33%)
292 bundling: foo/file.txt 2/3 files (66.67%)
289 bundling: foo/file.txt 2/3 files (66.67%)
293 bundling: quux/file.py 3/3 files (100.00%)
290 bundling: quux/file.py 3/3 files (100.00%)
294 changesets: 1 chunks
291 changesets: 1 chunks
295 add changeset ef1ea85a6374
292 add changeset ef1ea85a6374
296 changesets: 2 chunks
293 changesets: 2 chunks
297 add changeset f9cafe1212c8
294 add changeset f9cafe1212c8
298 changesets: 3 chunks
295 changesets: 3 chunks
299 add changeset 911600dab2ae
296 add changeset 911600dab2ae
300 adding manifests
297 adding manifests
301 manifests: 1/3 chunks (33.33%)
298 manifests: 1/3 chunks (33.33%)
302 manifests: 2/3 chunks (66.67%)
299 manifests: 2/3 chunks (66.67%)
303 manifests: 3/3 chunks (100.00%)
300 manifests: 3/3 chunks (100.00%)
304 adding file changes
301 adding file changes
305 adding foo/Bar/file.txt revisions
302 adding foo/Bar/file.txt revisions
306 files: 1/3 chunks (33.33%)
303 files: 1/3 chunks (33.33%)
307 adding foo/file.txt revisions
304 adding foo/file.txt revisions
308 files: 2/3 chunks (66.67%)
305 files: 2/3 chunks (66.67%)
309 adding quux/file.py revisions
306 adding quux/file.py revisions
310 files: 3/3 chunks (100.00%)
307 files: 3/3 chunks (100.00%)
311 added 3 changesets with 3 changes to 3 files
308 added 3 changesets with 3 changes to 3 files
312 calling hook pretxnchangegroup.acl: hgext.acl.hook
309 calling hook pretxnchangegroup.acl: hgext.acl.hook
313 acl: checking access for user "fred"
310 acl: checking access for user "fred"
314 acl: acl.allow.branches not enabled
311 acl: acl.allow.branches not enabled
315 acl: acl.deny.branches not enabled
312 acl: acl.deny.branches not enabled
316 acl: acl.allow enabled, 0 entries for user fred
313 acl: acl.allow enabled, 0 entries for user fred
317 acl: acl.deny not enabled
314 acl: acl.deny not enabled
318 acl: branch access granted: "ef1ea85a6374" on branch "default"
315 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")
316 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
320 transaction abort!
317 transaction abort!
321 rollback completed
318 rollback completed
322 abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
319 abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
323 no rollback information available
320 no rollback information available
324 0:6675d58eff77
321 0:6675d58eff77
325
322
326
323
327 fred is allowed inside foo/
324 fred is allowed inside foo/
328
325
329 $ echo 'foo/** = fred' >> $config
326 $ echo 'foo/** = fred' >> $config
330 $ do_push fred
327 $ do_push fred
331 Pushing as user fred
328 Pushing as user fred
332 hgrc = """
329 hgrc = """
333 [hooks]
330 [hooks]
334 pretxnchangegroup.acl = python:hgext.acl.hook
331 pretxnchangegroup.acl = python:hgext.acl.hook
335 [acl]
332 [acl]
336 sources = push
333 sources = push
337 [acl.allow]
334 [acl.allow]
338 foo/** = fred
335 foo/** = fred
339 """
336 """
340 pushing to ../b
337 pushing to ../b
341 query 1; heads
338 query 1; heads
342 searching for changes
339 searching for changes
343 all remote heads known locally
340 all remote heads known locally
344 listing keys for "bookmarks"
341 listing keys for "bookmarks"
345 3 changesets found
342 3 changesets found
346 list of changesets:
343 list of changesets:
347 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
344 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
348 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
345 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
349 911600dab2ae7a9baff75958b84fe606851ce955
346 911600dab2ae7a9baff75958b84fe606851ce955
350 adding changesets
347 adding changesets
351 bundling: 1/3 changesets (33.33%)
348 bundling: 1/3 changesets (33.33%)
352 bundling: 2/3 changesets (66.67%)
349 bundling: 2/3 changesets (66.67%)
353 bundling: 3/3 changesets (100.00%)
350 bundling: 3/3 changesets (100.00%)
354 bundling: 1/3 manifests (33.33%)
351 bundling: 1/3 manifests (33.33%)
355 bundling: 2/3 manifests (66.67%)
352 bundling: 2/3 manifests (66.67%)
356 bundling: 3/3 manifests (100.00%)
353 bundling: 3/3 manifests (100.00%)
357 bundling: foo/Bar/file.txt 1/3 files (33.33%)
354 bundling: foo/Bar/file.txt 1/3 files (33.33%)
358 bundling: foo/file.txt 2/3 files (66.67%)
355 bundling: foo/file.txt 2/3 files (66.67%)
359 bundling: quux/file.py 3/3 files (100.00%)
356 bundling: quux/file.py 3/3 files (100.00%)
360 changesets: 1 chunks
357 changesets: 1 chunks
361 add changeset ef1ea85a6374
358 add changeset ef1ea85a6374
362 changesets: 2 chunks
359 changesets: 2 chunks
363 add changeset f9cafe1212c8
360 add changeset f9cafe1212c8
364 changesets: 3 chunks
361 changesets: 3 chunks
365 add changeset 911600dab2ae
362 add changeset 911600dab2ae
366 adding manifests
363 adding manifests
367 manifests: 1/3 chunks (33.33%)
364 manifests: 1/3 chunks (33.33%)
368 manifests: 2/3 chunks (66.67%)
365 manifests: 2/3 chunks (66.67%)
369 manifests: 3/3 chunks (100.00%)
366 manifests: 3/3 chunks (100.00%)
370 adding file changes
367 adding file changes
371 adding foo/Bar/file.txt revisions
368 adding foo/Bar/file.txt revisions
372 files: 1/3 chunks (33.33%)
369 files: 1/3 chunks (33.33%)
373 adding foo/file.txt revisions
370 adding foo/file.txt revisions
374 files: 2/3 chunks (66.67%)
371 files: 2/3 chunks (66.67%)
375 adding quux/file.py revisions
372 adding quux/file.py revisions
376 files: 3/3 chunks (100.00%)
373 files: 3/3 chunks (100.00%)
377 added 3 changesets with 3 changes to 3 files
374 added 3 changesets with 3 changes to 3 files
378 calling hook pretxnchangegroup.acl: hgext.acl.hook
375 calling hook pretxnchangegroup.acl: hgext.acl.hook
379 acl: checking access for user "fred"
376 acl: checking access for user "fred"
380 acl: acl.allow.branches not enabled
377 acl: acl.allow.branches not enabled
381 acl: acl.deny.branches not enabled
378 acl: acl.deny.branches not enabled
382 acl: acl.allow enabled, 1 entries for user fred
379 acl: acl.allow enabled, 1 entries for user fred
383 acl: acl.deny not enabled
380 acl: acl.deny not enabled
384 acl: branch access granted: "ef1ea85a6374" on branch "default"
381 acl: branch access granted: "ef1ea85a6374" on branch "default"
385 acl: path access granted: "ef1ea85a6374"
382 acl: path access granted: "ef1ea85a6374"
386 acl: branch access granted: "f9cafe1212c8" on branch "default"
383 acl: branch access granted: "f9cafe1212c8" on branch "default"
387 acl: path access granted: "f9cafe1212c8"
384 acl: path access granted: "f9cafe1212c8"
388 acl: branch access granted: "911600dab2ae" on branch "default"
385 acl: branch access granted: "911600dab2ae" on branch "default"
389 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
386 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
390 transaction abort!
387 transaction abort!
391 rollback completed
388 rollback completed
392 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
389 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
393 no rollback information available
390 no rollback information available
394 0:6675d58eff77
391 0:6675d58eff77
395
392
396
393
397 Empty [acl.deny]
394 Empty [acl.deny]
398
395
399 $ echo '[acl.deny]' >> $config
396 $ echo '[acl.deny]' >> $config
400 $ do_push barney
397 $ do_push barney
401 Pushing as user barney
398 Pushing as user barney
402 hgrc = """
399 hgrc = """
403 [hooks]
400 [hooks]
404 pretxnchangegroup.acl = python:hgext.acl.hook
401 pretxnchangegroup.acl = python:hgext.acl.hook
405 [acl]
402 [acl]
406 sources = push
403 sources = push
407 [acl.allow]
404 [acl.allow]
408 foo/** = fred
405 foo/** = fred
409 [acl.deny]
406 [acl.deny]
410 """
407 """
411 pushing to ../b
408 pushing to ../b
412 query 1; heads
409 query 1; heads
413 searching for changes
410 searching for changes
414 all remote heads known locally
411 all remote heads known locally
415 listing keys for "bookmarks"
412 listing keys for "bookmarks"
416 3 changesets found
413 3 changesets found
417 list of changesets:
414 list of changesets:
418 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
415 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
419 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
416 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
420 911600dab2ae7a9baff75958b84fe606851ce955
417 911600dab2ae7a9baff75958b84fe606851ce955
421 adding changesets
418 adding changesets
422 bundling: 1/3 changesets (33.33%)
419 bundling: 1/3 changesets (33.33%)
423 bundling: 2/3 changesets (66.67%)
420 bundling: 2/3 changesets (66.67%)
424 bundling: 3/3 changesets (100.00%)
421 bundling: 3/3 changesets (100.00%)
425 bundling: 1/3 manifests (33.33%)
422 bundling: 1/3 manifests (33.33%)
426 bundling: 2/3 manifests (66.67%)
423 bundling: 2/3 manifests (66.67%)
427 bundling: 3/3 manifests (100.00%)
424 bundling: 3/3 manifests (100.00%)
428 bundling: foo/Bar/file.txt 1/3 files (33.33%)
425 bundling: foo/Bar/file.txt 1/3 files (33.33%)
429 bundling: foo/file.txt 2/3 files (66.67%)
426 bundling: foo/file.txt 2/3 files (66.67%)
430 bundling: quux/file.py 3/3 files (100.00%)
427 bundling: quux/file.py 3/3 files (100.00%)
431 changesets: 1 chunks
428 changesets: 1 chunks
432 add changeset ef1ea85a6374
429 add changeset ef1ea85a6374
433 changesets: 2 chunks
430 changesets: 2 chunks
434 add changeset f9cafe1212c8
431 add changeset f9cafe1212c8
435 changesets: 3 chunks
432 changesets: 3 chunks
436 add changeset 911600dab2ae
433 add changeset 911600dab2ae
437 adding manifests
434 adding manifests
438 manifests: 1/3 chunks (33.33%)
435 manifests: 1/3 chunks (33.33%)
439 manifests: 2/3 chunks (66.67%)
436 manifests: 2/3 chunks (66.67%)
440 manifests: 3/3 chunks (100.00%)
437 manifests: 3/3 chunks (100.00%)
441 adding file changes
438 adding file changes
442 adding foo/Bar/file.txt revisions
439 adding foo/Bar/file.txt revisions
443 files: 1/3 chunks (33.33%)
440 files: 1/3 chunks (33.33%)
444 adding foo/file.txt revisions
441 adding foo/file.txt revisions
445 files: 2/3 chunks (66.67%)
442 files: 2/3 chunks (66.67%)
446 adding quux/file.py revisions
443 adding quux/file.py revisions
447 files: 3/3 chunks (100.00%)
444 files: 3/3 chunks (100.00%)
448 added 3 changesets with 3 changes to 3 files
445 added 3 changesets with 3 changes to 3 files
449 calling hook pretxnchangegroup.acl: hgext.acl.hook
446 calling hook pretxnchangegroup.acl: hgext.acl.hook
450 acl: checking access for user "barney"
447 acl: checking access for user "barney"
451 acl: acl.allow.branches not enabled
448 acl: acl.allow.branches not enabled
452 acl: acl.deny.branches not enabled
449 acl: acl.deny.branches not enabled
453 acl: acl.allow enabled, 0 entries for user barney
450 acl: acl.allow enabled, 0 entries for user barney
454 acl: acl.deny enabled, 0 entries for user barney
451 acl: acl.deny enabled, 0 entries for user barney
455 acl: branch access granted: "ef1ea85a6374" on branch "default"
452 acl: branch access granted: "ef1ea85a6374" on branch "default"
456 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
453 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
457 transaction abort!
454 transaction abort!
458 rollback completed
455 rollback completed
459 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
456 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
460 no rollback information available
457 no rollback information available
461 0:6675d58eff77
458 0:6675d58eff77
462
459
463
460
464 fred is allowed inside foo/, but not foo/bar/ (case matters)
461 fred is allowed inside foo/, but not foo/bar/ (case matters)
465
462
466 $ echo 'foo/bar/** = fred' >> $config
463 $ echo 'foo/bar/** = fred' >> $config
467 $ do_push fred
464 $ do_push fred
468 Pushing as user fred
465 Pushing as user fred
469 hgrc = """
466 hgrc = """
470 [hooks]
467 [hooks]
471 pretxnchangegroup.acl = python:hgext.acl.hook
468 pretxnchangegroup.acl = python:hgext.acl.hook
472 [acl]
469 [acl]
473 sources = push
470 sources = push
474 [acl.allow]
471 [acl.allow]
475 foo/** = fred
472 foo/** = fred
476 [acl.deny]
473 [acl.deny]
477 foo/bar/** = fred
474 foo/bar/** = fred
478 """
475 """
479 pushing to ../b
476 pushing to ../b
480 query 1; heads
477 query 1; heads
481 searching for changes
478 searching for changes
482 all remote heads known locally
479 all remote heads known locally
483 listing keys for "bookmarks"
480 listing keys for "bookmarks"
484 3 changesets found
481 3 changesets found
485 list of changesets:
482 list of changesets:
486 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
483 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
487 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
484 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
488 911600dab2ae7a9baff75958b84fe606851ce955
485 911600dab2ae7a9baff75958b84fe606851ce955
489 adding changesets
486 adding changesets
490 bundling: 1/3 changesets (33.33%)
487 bundling: 1/3 changesets (33.33%)
491 bundling: 2/3 changesets (66.67%)
488 bundling: 2/3 changesets (66.67%)
492 bundling: 3/3 changesets (100.00%)
489 bundling: 3/3 changesets (100.00%)
493 bundling: 1/3 manifests (33.33%)
490 bundling: 1/3 manifests (33.33%)
494 bundling: 2/3 manifests (66.67%)
491 bundling: 2/3 manifests (66.67%)
495 bundling: 3/3 manifests (100.00%)
492 bundling: 3/3 manifests (100.00%)
496 bundling: foo/Bar/file.txt 1/3 files (33.33%)
493 bundling: foo/Bar/file.txt 1/3 files (33.33%)
497 bundling: foo/file.txt 2/3 files (66.67%)
494 bundling: foo/file.txt 2/3 files (66.67%)
498 bundling: quux/file.py 3/3 files (100.00%)
495 bundling: quux/file.py 3/3 files (100.00%)
499 changesets: 1 chunks
496 changesets: 1 chunks
500 add changeset ef1ea85a6374
497 add changeset ef1ea85a6374
501 changesets: 2 chunks
498 changesets: 2 chunks
502 add changeset f9cafe1212c8
499 add changeset f9cafe1212c8
503 changesets: 3 chunks
500 changesets: 3 chunks
504 add changeset 911600dab2ae
501 add changeset 911600dab2ae
505 adding manifests
502 adding manifests
506 manifests: 1/3 chunks (33.33%)
503 manifests: 1/3 chunks (33.33%)
507 manifests: 2/3 chunks (66.67%)
504 manifests: 2/3 chunks (66.67%)
508 manifests: 3/3 chunks (100.00%)
505 manifests: 3/3 chunks (100.00%)
509 adding file changes
506 adding file changes
510 adding foo/Bar/file.txt revisions
507 adding foo/Bar/file.txt revisions
511 files: 1/3 chunks (33.33%)
508 files: 1/3 chunks (33.33%)
512 adding foo/file.txt revisions
509 adding foo/file.txt revisions
513 files: 2/3 chunks (66.67%)
510 files: 2/3 chunks (66.67%)
514 adding quux/file.py revisions
511 adding quux/file.py revisions
515 files: 3/3 chunks (100.00%)
512 files: 3/3 chunks (100.00%)
516 added 3 changesets with 3 changes to 3 files
513 added 3 changesets with 3 changes to 3 files
517 calling hook pretxnchangegroup.acl: hgext.acl.hook
514 calling hook pretxnchangegroup.acl: hgext.acl.hook
518 acl: checking access for user "fred"
515 acl: checking access for user "fred"
519 acl: acl.allow.branches not enabled
516 acl: acl.allow.branches not enabled
520 acl: acl.deny.branches not enabled
517 acl: acl.deny.branches not enabled
521 acl: acl.allow enabled, 1 entries for user fred
518 acl: acl.allow enabled, 1 entries for user fred
522 acl: acl.deny enabled, 1 entries for user fred
519 acl: acl.deny enabled, 1 entries for user fred
523 acl: branch access granted: "ef1ea85a6374" on branch "default"
520 acl: branch access granted: "ef1ea85a6374" on branch "default"
524 acl: path access granted: "ef1ea85a6374"
521 acl: path access granted: "ef1ea85a6374"
525 acl: branch access granted: "f9cafe1212c8" on branch "default"
522 acl: branch access granted: "f9cafe1212c8" on branch "default"
526 acl: path access granted: "f9cafe1212c8"
523 acl: path access granted: "f9cafe1212c8"
527 acl: branch access granted: "911600dab2ae" on branch "default"
524 acl: branch access granted: "911600dab2ae" on branch "default"
528 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
525 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
529 transaction abort!
526 transaction abort!
530 rollback completed
527 rollback completed
531 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
528 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
532 no rollback information available
529 no rollback information available
533 0:6675d58eff77
530 0:6675d58eff77
534
531
535
532
536 fred is allowed inside foo/, but not foo/Bar/
533 fred is allowed inside foo/, but not foo/Bar/
537
534
538 $ echo 'foo/Bar/** = fred' >> $config
535 $ echo 'foo/Bar/** = fred' >> $config
539 $ do_push fred
536 $ do_push fred
540 Pushing as user fred
537 Pushing as user fred
541 hgrc = """
538 hgrc = """
542 [hooks]
539 [hooks]
543 pretxnchangegroup.acl = python:hgext.acl.hook
540 pretxnchangegroup.acl = python:hgext.acl.hook
544 [acl]
541 [acl]
545 sources = push
542 sources = push
546 [acl.allow]
543 [acl.allow]
547 foo/** = fred
544 foo/** = fred
548 [acl.deny]
545 [acl.deny]
549 foo/bar/** = fred
546 foo/bar/** = fred
550 foo/Bar/** = fred
547 foo/Bar/** = fred
551 """
548 """
552 pushing to ../b
549 pushing to ../b
553 query 1; heads
550 query 1; heads
554 searching for changes
551 searching for changes
555 all remote heads known locally
552 all remote heads known locally
556 listing keys for "bookmarks"
553 listing keys for "bookmarks"
557 3 changesets found
554 3 changesets found
558 list of changesets:
555 list of changesets:
559 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
556 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
560 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
557 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
561 911600dab2ae7a9baff75958b84fe606851ce955
558 911600dab2ae7a9baff75958b84fe606851ce955
562 adding changesets
559 adding changesets
563 bundling: 1/3 changesets (33.33%)
560 bundling: 1/3 changesets (33.33%)
564 bundling: 2/3 changesets (66.67%)
561 bundling: 2/3 changesets (66.67%)
565 bundling: 3/3 changesets (100.00%)
562 bundling: 3/3 changesets (100.00%)
566 bundling: 1/3 manifests (33.33%)
563 bundling: 1/3 manifests (33.33%)
567 bundling: 2/3 manifests (66.67%)
564 bundling: 2/3 manifests (66.67%)
568 bundling: 3/3 manifests (100.00%)
565 bundling: 3/3 manifests (100.00%)
569 bundling: foo/Bar/file.txt 1/3 files (33.33%)
566 bundling: foo/Bar/file.txt 1/3 files (33.33%)
570 bundling: foo/file.txt 2/3 files (66.67%)
567 bundling: foo/file.txt 2/3 files (66.67%)
571 bundling: quux/file.py 3/3 files (100.00%)
568 bundling: quux/file.py 3/3 files (100.00%)
572 changesets: 1 chunks
569 changesets: 1 chunks
573 add changeset ef1ea85a6374
570 add changeset ef1ea85a6374
574 changesets: 2 chunks
571 changesets: 2 chunks
575 add changeset f9cafe1212c8
572 add changeset f9cafe1212c8
576 changesets: 3 chunks
573 changesets: 3 chunks
577 add changeset 911600dab2ae
574 add changeset 911600dab2ae
578 adding manifests
575 adding manifests
579 manifests: 1/3 chunks (33.33%)
576 manifests: 1/3 chunks (33.33%)
580 manifests: 2/3 chunks (66.67%)
577 manifests: 2/3 chunks (66.67%)
581 manifests: 3/3 chunks (100.00%)
578 manifests: 3/3 chunks (100.00%)
582 adding file changes
579 adding file changes
583 adding foo/Bar/file.txt revisions
580 adding foo/Bar/file.txt revisions
584 files: 1/3 chunks (33.33%)
581 files: 1/3 chunks (33.33%)
585 adding foo/file.txt revisions
582 adding foo/file.txt revisions
586 files: 2/3 chunks (66.67%)
583 files: 2/3 chunks (66.67%)
587 adding quux/file.py revisions
584 adding quux/file.py revisions
588 files: 3/3 chunks (100.00%)
585 files: 3/3 chunks (100.00%)
589 added 3 changesets with 3 changes to 3 files
586 added 3 changesets with 3 changes to 3 files
590 calling hook pretxnchangegroup.acl: hgext.acl.hook
587 calling hook pretxnchangegroup.acl: hgext.acl.hook
591 acl: checking access for user "fred"
588 acl: checking access for user "fred"
592 acl: acl.allow.branches not enabled
589 acl: acl.allow.branches not enabled
593 acl: acl.deny.branches not enabled
590 acl: acl.deny.branches not enabled
594 acl: acl.allow enabled, 1 entries for user fred
591 acl: acl.allow enabled, 1 entries for user fred
595 acl: acl.deny enabled, 2 entries for user fred
592 acl: acl.deny enabled, 2 entries for user fred
596 acl: branch access granted: "ef1ea85a6374" on branch "default"
593 acl: branch access granted: "ef1ea85a6374" on branch "default"
597 acl: path access granted: "ef1ea85a6374"
594 acl: path access granted: "ef1ea85a6374"
598 acl: branch access granted: "f9cafe1212c8" on branch "default"
595 acl: branch access granted: "f9cafe1212c8" on branch "default"
599 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
596 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
600 transaction abort!
597 transaction abort!
601 rollback completed
598 rollback completed
602 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
599 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
603 no rollback information available
600 no rollback information available
604 0:6675d58eff77
601 0:6675d58eff77
605
602
606
603
607 $ echo 'barney is not mentioned => not allowed anywhere'
604 $ echo 'barney is not mentioned => not allowed anywhere'
608 barney is not mentioned => not allowed anywhere
605 barney is not mentioned => not allowed anywhere
609 $ do_push barney
606 $ do_push barney
610 Pushing as user barney
607 Pushing as user barney
611 hgrc = """
608 hgrc = """
612 [hooks]
609 [hooks]
613 pretxnchangegroup.acl = python:hgext.acl.hook
610 pretxnchangegroup.acl = python:hgext.acl.hook
614 [acl]
611 [acl]
615 sources = push
612 sources = push
616 [acl.allow]
613 [acl.allow]
617 foo/** = fred
614 foo/** = fred
618 [acl.deny]
615 [acl.deny]
619 foo/bar/** = fred
616 foo/bar/** = fred
620 foo/Bar/** = fred
617 foo/Bar/** = fred
621 """
618 """
622 pushing to ../b
619 pushing to ../b
623 query 1; heads
620 query 1; heads
624 searching for changes
621 searching for changes
625 all remote heads known locally
622 all remote heads known locally
626 listing keys for "bookmarks"
623 listing keys for "bookmarks"
627 3 changesets found
624 3 changesets found
628 list of changesets:
625 list of changesets:
629 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
626 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
630 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
627 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
631 911600dab2ae7a9baff75958b84fe606851ce955
628 911600dab2ae7a9baff75958b84fe606851ce955
632 adding changesets
629 adding changesets
633 bundling: 1/3 changesets (33.33%)
630 bundling: 1/3 changesets (33.33%)
634 bundling: 2/3 changesets (66.67%)
631 bundling: 2/3 changesets (66.67%)
635 bundling: 3/3 changesets (100.00%)
632 bundling: 3/3 changesets (100.00%)
636 bundling: 1/3 manifests (33.33%)
633 bundling: 1/3 manifests (33.33%)
637 bundling: 2/3 manifests (66.67%)
634 bundling: 2/3 manifests (66.67%)
638 bundling: 3/3 manifests (100.00%)
635 bundling: 3/3 manifests (100.00%)
639 bundling: foo/Bar/file.txt 1/3 files (33.33%)
636 bundling: foo/Bar/file.txt 1/3 files (33.33%)
640 bundling: foo/file.txt 2/3 files (66.67%)
637 bundling: foo/file.txt 2/3 files (66.67%)
641 bundling: quux/file.py 3/3 files (100.00%)
638 bundling: quux/file.py 3/3 files (100.00%)
642 changesets: 1 chunks
639 changesets: 1 chunks
643 add changeset ef1ea85a6374
640 add changeset ef1ea85a6374
644 changesets: 2 chunks
641 changesets: 2 chunks
645 add changeset f9cafe1212c8
642 add changeset f9cafe1212c8
646 changesets: 3 chunks
643 changesets: 3 chunks
647 add changeset 911600dab2ae
644 add changeset 911600dab2ae
648 adding manifests
645 adding manifests
649 manifests: 1/3 chunks (33.33%)
646 manifests: 1/3 chunks (33.33%)
650 manifests: 2/3 chunks (66.67%)
647 manifests: 2/3 chunks (66.67%)
651 manifests: 3/3 chunks (100.00%)
648 manifests: 3/3 chunks (100.00%)
652 adding file changes
649 adding file changes
653 adding foo/Bar/file.txt revisions
650 adding foo/Bar/file.txt revisions
654 files: 1/3 chunks (33.33%)
651 files: 1/3 chunks (33.33%)
655 adding foo/file.txt revisions
652 adding foo/file.txt revisions
656 files: 2/3 chunks (66.67%)
653 files: 2/3 chunks (66.67%)
657 adding quux/file.py revisions
654 adding quux/file.py revisions
658 files: 3/3 chunks (100.00%)
655 files: 3/3 chunks (100.00%)
659 added 3 changesets with 3 changes to 3 files
656 added 3 changesets with 3 changes to 3 files
660 calling hook pretxnchangegroup.acl: hgext.acl.hook
657 calling hook pretxnchangegroup.acl: hgext.acl.hook
661 acl: checking access for user "barney"
658 acl: checking access for user "barney"
662 acl: acl.allow.branches not enabled
659 acl: acl.allow.branches not enabled
663 acl: acl.deny.branches not enabled
660 acl: acl.deny.branches not enabled
664 acl: acl.allow enabled, 0 entries for user barney
661 acl: acl.allow enabled, 0 entries for user barney
665 acl: acl.deny enabled, 0 entries for user barney
662 acl: acl.deny enabled, 0 entries for user barney
666 acl: branch access granted: "ef1ea85a6374" on branch "default"
663 acl: branch access granted: "ef1ea85a6374" on branch "default"
667 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
664 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
668 transaction abort!
665 transaction abort!
669 rollback completed
666 rollback completed
670 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
667 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
671 no rollback information available
668 no rollback information available
672 0:6675d58eff77
669 0:6675d58eff77
673
670
674
671
675 barney is allowed everywhere
672 barney is allowed everywhere
676
673
677 $ echo '[acl.allow]' >> $config
674 $ echo '[acl.allow]' >> $config
678 $ echo '** = barney' >> $config
675 $ echo '** = barney' >> $config
679 $ do_push barney
676 $ do_push barney
680 Pushing as user barney
677 Pushing as user barney
681 hgrc = """
678 hgrc = """
682 [hooks]
679 [hooks]
683 pretxnchangegroup.acl = python:hgext.acl.hook
680 pretxnchangegroup.acl = python:hgext.acl.hook
684 [acl]
681 [acl]
685 sources = push
682 sources = push
686 [acl.allow]
683 [acl.allow]
687 foo/** = fred
684 foo/** = fred
688 [acl.deny]
685 [acl.deny]
689 foo/bar/** = fred
686 foo/bar/** = fred
690 foo/Bar/** = fred
687 foo/Bar/** = fred
691 [acl.allow]
688 [acl.allow]
692 ** = barney
689 ** = barney
693 """
690 """
694 pushing to ../b
691 pushing to ../b
695 query 1; heads
692 query 1; heads
696 searching for changes
693 searching for changes
697 all remote heads known locally
694 all remote heads known locally
698 listing keys for "bookmarks"
695 listing keys for "bookmarks"
699 3 changesets found
696 3 changesets found
700 list of changesets:
697 list of changesets:
701 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
698 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
702 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
699 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
703 911600dab2ae7a9baff75958b84fe606851ce955
700 911600dab2ae7a9baff75958b84fe606851ce955
704 adding changesets
701 adding changesets
705 bundling: 1/3 changesets (33.33%)
702 bundling: 1/3 changesets (33.33%)
706 bundling: 2/3 changesets (66.67%)
703 bundling: 2/3 changesets (66.67%)
707 bundling: 3/3 changesets (100.00%)
704 bundling: 3/3 changesets (100.00%)
708 bundling: 1/3 manifests (33.33%)
705 bundling: 1/3 manifests (33.33%)
709 bundling: 2/3 manifests (66.67%)
706 bundling: 2/3 manifests (66.67%)
710 bundling: 3/3 manifests (100.00%)
707 bundling: 3/3 manifests (100.00%)
711 bundling: foo/Bar/file.txt 1/3 files (33.33%)
708 bundling: foo/Bar/file.txt 1/3 files (33.33%)
712 bundling: foo/file.txt 2/3 files (66.67%)
709 bundling: foo/file.txt 2/3 files (66.67%)
713 bundling: quux/file.py 3/3 files (100.00%)
710 bundling: quux/file.py 3/3 files (100.00%)
714 changesets: 1 chunks
711 changesets: 1 chunks
715 add changeset ef1ea85a6374
712 add changeset ef1ea85a6374
716 changesets: 2 chunks
713 changesets: 2 chunks
717 add changeset f9cafe1212c8
714 add changeset f9cafe1212c8
718 changesets: 3 chunks
715 changesets: 3 chunks
719 add changeset 911600dab2ae
716 add changeset 911600dab2ae
720 adding manifests
717 adding manifests
721 manifests: 1/3 chunks (33.33%)
718 manifests: 1/3 chunks (33.33%)
722 manifests: 2/3 chunks (66.67%)
719 manifests: 2/3 chunks (66.67%)
723 manifests: 3/3 chunks (100.00%)
720 manifests: 3/3 chunks (100.00%)
724 adding file changes
721 adding file changes
725 adding foo/Bar/file.txt revisions
722 adding foo/Bar/file.txt revisions
726 files: 1/3 chunks (33.33%)
723 files: 1/3 chunks (33.33%)
727 adding foo/file.txt revisions
724 adding foo/file.txt revisions
728 files: 2/3 chunks (66.67%)
725 files: 2/3 chunks (66.67%)
729 adding quux/file.py revisions
726 adding quux/file.py revisions
730 files: 3/3 chunks (100.00%)
727 files: 3/3 chunks (100.00%)
731 added 3 changesets with 3 changes to 3 files
728 added 3 changesets with 3 changes to 3 files
732 calling hook pretxnchangegroup.acl: hgext.acl.hook
729 calling hook pretxnchangegroup.acl: hgext.acl.hook
733 acl: checking access for user "barney"
730 acl: checking access for user "barney"
734 acl: acl.allow.branches not enabled
731 acl: acl.allow.branches not enabled
735 acl: acl.deny.branches not enabled
732 acl: acl.deny.branches not enabled
736 acl: acl.allow enabled, 1 entries for user barney
733 acl: acl.allow enabled, 1 entries for user barney
737 acl: acl.deny enabled, 0 entries for user barney
734 acl: acl.deny enabled, 0 entries for user barney
738 acl: branch access granted: "ef1ea85a6374" on branch "default"
735 acl: branch access granted: "ef1ea85a6374" on branch "default"
739 acl: path access granted: "ef1ea85a6374"
736 acl: path access granted: "ef1ea85a6374"
740 acl: branch access granted: "f9cafe1212c8" on branch "default"
737 acl: branch access granted: "f9cafe1212c8" on branch "default"
741 acl: path access granted: "f9cafe1212c8"
738 acl: path access granted: "f9cafe1212c8"
742 acl: branch access granted: "911600dab2ae" on branch "default"
739 acl: branch access granted: "911600dab2ae" on branch "default"
743 acl: path access granted: "911600dab2ae"
740 acl: path access granted: "911600dab2ae"
744 listing keys for "phases"
741 listing keys for "phases"
745 try to push obsolete markers to remote
742 try to push obsolete markers to remote
746 updating the branch cache
743 updating the branch cache
747 checking for updated bookmarks
744 checking for updated bookmarks
748 listing keys for "bookmarks"
745 listing keys for "bookmarks"
749 repository tip rolled back to revision 0 (undo push)
746 repository tip rolled back to revision 0 (undo push)
750 0:6675d58eff77
747 0:6675d58eff77
751
748
752
749
753 wilma can change files with a .txt extension
750 wilma can change files with a .txt extension
754
751
755 $ echo '**/*.txt = wilma' >> $config
752 $ echo '**/*.txt = wilma' >> $config
756 $ do_push wilma
753 $ do_push wilma
757 Pushing as user wilma
754 Pushing as user wilma
758 hgrc = """
755 hgrc = """
759 [hooks]
756 [hooks]
760 pretxnchangegroup.acl = python:hgext.acl.hook
757 pretxnchangegroup.acl = python:hgext.acl.hook
761 [acl]
758 [acl]
762 sources = push
759 sources = push
763 [acl.allow]
760 [acl.allow]
764 foo/** = fred
761 foo/** = fred
765 [acl.deny]
762 [acl.deny]
766 foo/bar/** = fred
763 foo/bar/** = fred
767 foo/Bar/** = fred
764 foo/Bar/** = fred
768 [acl.allow]
765 [acl.allow]
769 ** = barney
766 ** = barney
770 **/*.txt = wilma
767 **/*.txt = wilma
771 """
768 """
772 pushing to ../b
769 pushing to ../b
773 query 1; heads
770 query 1; heads
774 searching for changes
771 searching for changes
775 all remote heads known locally
772 all remote heads known locally
776 invalid branchheads cache: tip differs
777 listing keys for "bookmarks"
773 listing keys for "bookmarks"
778 3 changesets found
774 3 changesets found
779 list of changesets:
775 list of changesets:
780 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
776 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
781 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
777 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
782 911600dab2ae7a9baff75958b84fe606851ce955
778 911600dab2ae7a9baff75958b84fe606851ce955
783 adding changesets
779 adding changesets
784 bundling: 1/3 changesets (33.33%)
780 bundling: 1/3 changesets (33.33%)
785 bundling: 2/3 changesets (66.67%)
781 bundling: 2/3 changesets (66.67%)
786 bundling: 3/3 changesets (100.00%)
782 bundling: 3/3 changesets (100.00%)
787 bundling: 1/3 manifests (33.33%)
783 bundling: 1/3 manifests (33.33%)
788 bundling: 2/3 manifests (66.67%)
784 bundling: 2/3 manifests (66.67%)
789 bundling: 3/3 manifests (100.00%)
785 bundling: 3/3 manifests (100.00%)
790 bundling: foo/Bar/file.txt 1/3 files (33.33%)
786 bundling: foo/Bar/file.txt 1/3 files (33.33%)
791 bundling: foo/file.txt 2/3 files (66.67%)
787 bundling: foo/file.txt 2/3 files (66.67%)
792 bundling: quux/file.py 3/3 files (100.00%)
788 bundling: quux/file.py 3/3 files (100.00%)
793 changesets: 1 chunks
789 changesets: 1 chunks
794 add changeset ef1ea85a6374
790 add changeset ef1ea85a6374
795 changesets: 2 chunks
791 changesets: 2 chunks
796 add changeset f9cafe1212c8
792 add changeset f9cafe1212c8
797 changesets: 3 chunks
793 changesets: 3 chunks
798 add changeset 911600dab2ae
794 add changeset 911600dab2ae
799 adding manifests
795 adding manifests
800 manifests: 1/3 chunks (33.33%)
796 manifests: 1/3 chunks (33.33%)
801 manifests: 2/3 chunks (66.67%)
797 manifests: 2/3 chunks (66.67%)
802 manifests: 3/3 chunks (100.00%)
798 manifests: 3/3 chunks (100.00%)
803 adding file changes
799 adding file changes
804 adding foo/Bar/file.txt revisions
800 adding foo/Bar/file.txt revisions
805 files: 1/3 chunks (33.33%)
801 files: 1/3 chunks (33.33%)
806 adding foo/file.txt revisions
802 adding foo/file.txt revisions
807 files: 2/3 chunks (66.67%)
803 files: 2/3 chunks (66.67%)
808 adding quux/file.py revisions
804 adding quux/file.py revisions
809 files: 3/3 chunks (100.00%)
805 files: 3/3 chunks (100.00%)
810 added 3 changesets with 3 changes to 3 files
806 added 3 changesets with 3 changes to 3 files
811 calling hook pretxnchangegroup.acl: hgext.acl.hook
807 calling hook pretxnchangegroup.acl: hgext.acl.hook
812 acl: checking access for user "wilma"
808 acl: checking access for user "wilma"
813 acl: acl.allow.branches not enabled
809 acl: acl.allow.branches not enabled
814 acl: acl.deny.branches not enabled
810 acl: acl.deny.branches not enabled
815 acl: acl.allow enabled, 1 entries for user wilma
811 acl: acl.allow enabled, 1 entries for user wilma
816 acl: acl.deny enabled, 0 entries for user wilma
812 acl: acl.deny enabled, 0 entries for user wilma
817 acl: branch access granted: "ef1ea85a6374" on branch "default"
813 acl: branch access granted: "ef1ea85a6374" on branch "default"
818 acl: path access granted: "ef1ea85a6374"
814 acl: path access granted: "ef1ea85a6374"
819 acl: branch access granted: "f9cafe1212c8" on branch "default"
815 acl: branch access granted: "f9cafe1212c8" on branch "default"
820 acl: path access granted: "f9cafe1212c8"
816 acl: path access granted: "f9cafe1212c8"
821 acl: branch access granted: "911600dab2ae" on branch "default"
817 acl: branch access granted: "911600dab2ae" on branch "default"
822 error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
818 error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
823 transaction abort!
819 transaction abort!
824 rollback completed
820 rollback completed
825 abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
821 abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
826 no rollback information available
822 no rollback information available
827 0:6675d58eff77
823 0:6675d58eff77
828
824
829
825
830 file specified by acl.config does not exist
826 file specified by acl.config does not exist
831
827
832 $ echo '[acl]' >> $config
828 $ echo '[acl]' >> $config
833 $ echo 'config = ../acl.config' >> $config
829 $ echo 'config = ../acl.config' >> $config
834 $ do_push barney
830 $ do_push barney
835 Pushing as user barney
831 Pushing as user barney
836 hgrc = """
832 hgrc = """
837 [hooks]
833 [hooks]
838 pretxnchangegroup.acl = python:hgext.acl.hook
834 pretxnchangegroup.acl = python:hgext.acl.hook
839 [acl]
835 [acl]
840 sources = push
836 sources = push
841 [acl.allow]
837 [acl.allow]
842 foo/** = fred
838 foo/** = fred
843 [acl.deny]
839 [acl.deny]
844 foo/bar/** = fred
840 foo/bar/** = fred
845 foo/Bar/** = fred
841 foo/Bar/** = fred
846 [acl.allow]
842 [acl.allow]
847 ** = barney
843 ** = barney
848 **/*.txt = wilma
844 **/*.txt = wilma
849 [acl]
845 [acl]
850 config = ../acl.config
846 config = ../acl.config
851 """
847 """
852 pushing to ../b
848 pushing to ../b
853 query 1; heads
849 query 1; heads
854 searching for changes
850 searching for changes
855 all remote heads known locally
851 all remote heads known locally
856 listing keys for "bookmarks"
852 listing keys for "bookmarks"
857 3 changesets found
853 3 changesets found
858 list of changesets:
854 list of changesets:
859 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
855 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
860 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
856 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
861 911600dab2ae7a9baff75958b84fe606851ce955
857 911600dab2ae7a9baff75958b84fe606851ce955
862 adding changesets
858 adding changesets
863 bundling: 1/3 changesets (33.33%)
859 bundling: 1/3 changesets (33.33%)
864 bundling: 2/3 changesets (66.67%)
860 bundling: 2/3 changesets (66.67%)
865 bundling: 3/3 changesets (100.00%)
861 bundling: 3/3 changesets (100.00%)
866 bundling: 1/3 manifests (33.33%)
862 bundling: 1/3 manifests (33.33%)
867 bundling: 2/3 manifests (66.67%)
863 bundling: 2/3 manifests (66.67%)
868 bundling: 3/3 manifests (100.00%)
864 bundling: 3/3 manifests (100.00%)
869 bundling: foo/Bar/file.txt 1/3 files (33.33%)
865 bundling: foo/Bar/file.txt 1/3 files (33.33%)
870 bundling: foo/file.txt 2/3 files (66.67%)
866 bundling: foo/file.txt 2/3 files (66.67%)
871 bundling: quux/file.py 3/3 files (100.00%)
867 bundling: quux/file.py 3/3 files (100.00%)
872 changesets: 1 chunks
868 changesets: 1 chunks
873 add changeset ef1ea85a6374
869 add changeset ef1ea85a6374
874 changesets: 2 chunks
870 changesets: 2 chunks
875 add changeset f9cafe1212c8
871 add changeset f9cafe1212c8
876 changesets: 3 chunks
872 changesets: 3 chunks
877 add changeset 911600dab2ae
873 add changeset 911600dab2ae
878 adding manifests
874 adding manifests
879 manifests: 1/3 chunks (33.33%)
875 manifests: 1/3 chunks (33.33%)
880 manifests: 2/3 chunks (66.67%)
876 manifests: 2/3 chunks (66.67%)
881 manifests: 3/3 chunks (100.00%)
877 manifests: 3/3 chunks (100.00%)
882 adding file changes
878 adding file changes
883 adding foo/Bar/file.txt revisions
879 adding foo/Bar/file.txt revisions
884 files: 1/3 chunks (33.33%)
880 files: 1/3 chunks (33.33%)
885 adding foo/file.txt revisions
881 adding foo/file.txt revisions
886 files: 2/3 chunks (66.67%)
882 files: 2/3 chunks (66.67%)
887 adding quux/file.py revisions
883 adding quux/file.py revisions
888 files: 3/3 chunks (100.00%)
884 files: 3/3 chunks (100.00%)
889 added 3 changesets with 3 changes to 3 files
885 added 3 changesets with 3 changes to 3 files
890 calling hook pretxnchangegroup.acl: hgext.acl.hook
886 calling hook pretxnchangegroup.acl: hgext.acl.hook
891 acl: checking access for user "barney"
887 acl: checking access for user "barney"
892 error: pretxnchangegroup.acl hook raised an exception: [Errno *] *: '../acl.config' (glob)
888 error: pretxnchangegroup.acl hook raised an exception: [Errno *] *: '../acl.config' (glob)
893 transaction abort!
889 transaction abort!
894 rollback completed
890 rollback completed
895 abort: *: ../acl.config (glob)
891 abort: *: ../acl.config (glob)
896 no rollback information available
892 no rollback information available
897 0:6675d58eff77
893 0:6675d58eff77
898
894
899
895
900 betty is allowed inside foo/ by a acl.config file
896 betty is allowed inside foo/ by a acl.config file
901
897
902 $ echo '[acl.allow]' >> acl.config
898 $ echo '[acl.allow]' >> acl.config
903 $ echo 'foo/** = betty' >> acl.config
899 $ echo 'foo/** = betty' >> acl.config
904 $ do_push betty
900 $ do_push betty
905 Pushing as user betty
901 Pushing as user betty
906 hgrc = """
902 hgrc = """
907 [hooks]
903 [hooks]
908 pretxnchangegroup.acl = python:hgext.acl.hook
904 pretxnchangegroup.acl = python:hgext.acl.hook
909 [acl]
905 [acl]
910 sources = push
906 sources = push
911 [acl.allow]
907 [acl.allow]
912 foo/** = fred
908 foo/** = fred
913 [acl.deny]
909 [acl.deny]
914 foo/bar/** = fred
910 foo/bar/** = fred
915 foo/Bar/** = fred
911 foo/Bar/** = fred
916 [acl.allow]
912 [acl.allow]
917 ** = barney
913 ** = barney
918 **/*.txt = wilma
914 **/*.txt = wilma
919 [acl]
915 [acl]
920 config = ../acl.config
916 config = ../acl.config
921 """
917 """
922 acl.config = """
918 acl.config = """
923 [acl.allow]
919 [acl.allow]
924 foo/** = betty
920 foo/** = betty
925 """
921 """
926 pushing to ../b
922 pushing to ../b
927 query 1; heads
923 query 1; heads
928 searching for changes
924 searching for changes
929 all remote heads known locally
925 all remote heads known locally
930 listing keys for "bookmarks"
926 listing keys for "bookmarks"
931 3 changesets found
927 3 changesets found
932 list of changesets:
928 list of changesets:
933 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
929 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
934 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
930 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
935 911600dab2ae7a9baff75958b84fe606851ce955
931 911600dab2ae7a9baff75958b84fe606851ce955
936 adding changesets
932 adding changesets
937 bundling: 1/3 changesets (33.33%)
933 bundling: 1/3 changesets (33.33%)
938 bundling: 2/3 changesets (66.67%)
934 bundling: 2/3 changesets (66.67%)
939 bundling: 3/3 changesets (100.00%)
935 bundling: 3/3 changesets (100.00%)
940 bundling: 1/3 manifests (33.33%)
936 bundling: 1/3 manifests (33.33%)
941 bundling: 2/3 manifests (66.67%)
937 bundling: 2/3 manifests (66.67%)
942 bundling: 3/3 manifests (100.00%)
938 bundling: 3/3 manifests (100.00%)
943 bundling: foo/Bar/file.txt 1/3 files (33.33%)
939 bundling: foo/Bar/file.txt 1/3 files (33.33%)
944 bundling: foo/file.txt 2/3 files (66.67%)
940 bundling: foo/file.txt 2/3 files (66.67%)
945 bundling: quux/file.py 3/3 files (100.00%)
941 bundling: quux/file.py 3/3 files (100.00%)
946 changesets: 1 chunks
942 changesets: 1 chunks
947 add changeset ef1ea85a6374
943 add changeset ef1ea85a6374
948 changesets: 2 chunks
944 changesets: 2 chunks
949 add changeset f9cafe1212c8
945 add changeset f9cafe1212c8
950 changesets: 3 chunks
946 changesets: 3 chunks
951 add changeset 911600dab2ae
947 add changeset 911600dab2ae
952 adding manifests
948 adding manifests
953 manifests: 1/3 chunks (33.33%)
949 manifests: 1/3 chunks (33.33%)
954 manifests: 2/3 chunks (66.67%)
950 manifests: 2/3 chunks (66.67%)
955 manifests: 3/3 chunks (100.00%)
951 manifests: 3/3 chunks (100.00%)
956 adding file changes
952 adding file changes
957 adding foo/Bar/file.txt revisions
953 adding foo/Bar/file.txt revisions
958 files: 1/3 chunks (33.33%)
954 files: 1/3 chunks (33.33%)
959 adding foo/file.txt revisions
955 adding foo/file.txt revisions
960 files: 2/3 chunks (66.67%)
956 files: 2/3 chunks (66.67%)
961 adding quux/file.py revisions
957 adding quux/file.py revisions
962 files: 3/3 chunks (100.00%)
958 files: 3/3 chunks (100.00%)
963 added 3 changesets with 3 changes to 3 files
959 added 3 changesets with 3 changes to 3 files
964 calling hook pretxnchangegroup.acl: hgext.acl.hook
960 calling hook pretxnchangegroup.acl: hgext.acl.hook
965 acl: checking access for user "betty"
961 acl: checking access for user "betty"
966 acl: acl.allow.branches not enabled
962 acl: acl.allow.branches not enabled
967 acl: acl.deny.branches not enabled
963 acl: acl.deny.branches not enabled
968 acl: acl.allow enabled, 1 entries for user betty
964 acl: acl.allow enabled, 1 entries for user betty
969 acl: acl.deny enabled, 0 entries for user betty
965 acl: acl.deny enabled, 0 entries for user betty
970 acl: branch access granted: "ef1ea85a6374" on branch "default"
966 acl: branch access granted: "ef1ea85a6374" on branch "default"
971 acl: path access granted: "ef1ea85a6374"
967 acl: path access granted: "ef1ea85a6374"
972 acl: branch access granted: "f9cafe1212c8" on branch "default"
968 acl: branch access granted: "f9cafe1212c8" on branch "default"
973 acl: path access granted: "f9cafe1212c8"
969 acl: path access granted: "f9cafe1212c8"
974 acl: branch access granted: "911600dab2ae" on branch "default"
970 acl: branch access granted: "911600dab2ae" on branch "default"
975 error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
971 error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
976 transaction abort!
972 transaction abort!
977 rollback completed
973 rollback completed
978 abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
974 abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
979 no rollback information available
975 no rollback information available
980 0:6675d58eff77
976 0:6675d58eff77
981
977
982
978
983 acl.config can set only [acl.allow]/[acl.deny]
979 acl.config can set only [acl.allow]/[acl.deny]
984
980
985 $ echo '[hooks]' >> acl.config
981 $ echo '[hooks]' >> acl.config
986 $ echo 'changegroup.acl = false' >> acl.config
982 $ echo 'changegroup.acl = false' >> acl.config
987 $ do_push barney
983 $ do_push barney
988 Pushing as user barney
984 Pushing as user barney
989 hgrc = """
985 hgrc = """
990 [hooks]
986 [hooks]
991 pretxnchangegroup.acl = python:hgext.acl.hook
987 pretxnchangegroup.acl = python:hgext.acl.hook
992 [acl]
988 [acl]
993 sources = push
989 sources = push
994 [acl.allow]
990 [acl.allow]
995 foo/** = fred
991 foo/** = fred
996 [acl.deny]
992 [acl.deny]
997 foo/bar/** = fred
993 foo/bar/** = fred
998 foo/Bar/** = fred
994 foo/Bar/** = fred
999 [acl.allow]
995 [acl.allow]
1000 ** = barney
996 ** = barney
1001 **/*.txt = wilma
997 **/*.txt = wilma
1002 [acl]
998 [acl]
1003 config = ../acl.config
999 config = ../acl.config
1004 """
1000 """
1005 acl.config = """
1001 acl.config = """
1006 [acl.allow]
1002 [acl.allow]
1007 foo/** = betty
1003 foo/** = betty
1008 [hooks]
1004 [hooks]
1009 changegroup.acl = false
1005 changegroup.acl = false
1010 """
1006 """
1011 pushing to ../b
1007 pushing to ../b
1012 query 1; heads
1008 query 1; heads
1013 searching for changes
1009 searching for changes
1014 all remote heads known locally
1010 all remote heads known locally
1015 listing keys for "bookmarks"
1011 listing keys for "bookmarks"
1016 3 changesets found
1012 3 changesets found
1017 list of changesets:
1013 list of changesets:
1018 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1014 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1019 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1015 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1020 911600dab2ae7a9baff75958b84fe606851ce955
1016 911600dab2ae7a9baff75958b84fe606851ce955
1021 adding changesets
1017 adding changesets
1022 bundling: 1/3 changesets (33.33%)
1018 bundling: 1/3 changesets (33.33%)
1023 bundling: 2/3 changesets (66.67%)
1019 bundling: 2/3 changesets (66.67%)
1024 bundling: 3/3 changesets (100.00%)
1020 bundling: 3/3 changesets (100.00%)
1025 bundling: 1/3 manifests (33.33%)
1021 bundling: 1/3 manifests (33.33%)
1026 bundling: 2/3 manifests (66.67%)
1022 bundling: 2/3 manifests (66.67%)
1027 bundling: 3/3 manifests (100.00%)
1023 bundling: 3/3 manifests (100.00%)
1028 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1024 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1029 bundling: foo/file.txt 2/3 files (66.67%)
1025 bundling: foo/file.txt 2/3 files (66.67%)
1030 bundling: quux/file.py 3/3 files (100.00%)
1026 bundling: quux/file.py 3/3 files (100.00%)
1031 changesets: 1 chunks
1027 changesets: 1 chunks
1032 add changeset ef1ea85a6374
1028 add changeset ef1ea85a6374
1033 changesets: 2 chunks
1029 changesets: 2 chunks
1034 add changeset f9cafe1212c8
1030 add changeset f9cafe1212c8
1035 changesets: 3 chunks
1031 changesets: 3 chunks
1036 add changeset 911600dab2ae
1032 add changeset 911600dab2ae
1037 adding manifests
1033 adding manifests
1038 manifests: 1/3 chunks (33.33%)
1034 manifests: 1/3 chunks (33.33%)
1039 manifests: 2/3 chunks (66.67%)
1035 manifests: 2/3 chunks (66.67%)
1040 manifests: 3/3 chunks (100.00%)
1036 manifests: 3/3 chunks (100.00%)
1041 adding file changes
1037 adding file changes
1042 adding foo/Bar/file.txt revisions
1038 adding foo/Bar/file.txt revisions
1043 files: 1/3 chunks (33.33%)
1039 files: 1/3 chunks (33.33%)
1044 adding foo/file.txt revisions
1040 adding foo/file.txt revisions
1045 files: 2/3 chunks (66.67%)
1041 files: 2/3 chunks (66.67%)
1046 adding quux/file.py revisions
1042 adding quux/file.py revisions
1047 files: 3/3 chunks (100.00%)
1043 files: 3/3 chunks (100.00%)
1048 added 3 changesets with 3 changes to 3 files
1044 added 3 changesets with 3 changes to 3 files
1049 calling hook pretxnchangegroup.acl: hgext.acl.hook
1045 calling hook pretxnchangegroup.acl: hgext.acl.hook
1050 acl: checking access for user "barney"
1046 acl: checking access for user "barney"
1051 acl: acl.allow.branches not enabled
1047 acl: acl.allow.branches not enabled
1052 acl: acl.deny.branches not enabled
1048 acl: acl.deny.branches not enabled
1053 acl: acl.allow enabled, 1 entries for user barney
1049 acl: acl.allow enabled, 1 entries for user barney
1054 acl: acl.deny enabled, 0 entries for user barney
1050 acl: acl.deny enabled, 0 entries for user barney
1055 acl: branch access granted: "ef1ea85a6374" on branch "default"
1051 acl: branch access granted: "ef1ea85a6374" on branch "default"
1056 acl: path access granted: "ef1ea85a6374"
1052 acl: path access granted: "ef1ea85a6374"
1057 acl: branch access granted: "f9cafe1212c8" on branch "default"
1053 acl: branch access granted: "f9cafe1212c8" on branch "default"
1058 acl: path access granted: "f9cafe1212c8"
1054 acl: path access granted: "f9cafe1212c8"
1059 acl: branch access granted: "911600dab2ae" on branch "default"
1055 acl: branch access granted: "911600dab2ae" on branch "default"
1060 acl: path access granted: "911600dab2ae"
1056 acl: path access granted: "911600dab2ae"
1061 listing keys for "phases"
1057 listing keys for "phases"
1062 try to push obsolete markers to remote
1058 try to push obsolete markers to remote
1063 updating the branch cache
1059 updating the branch cache
1064 checking for updated bookmarks
1060 checking for updated bookmarks
1065 listing keys for "bookmarks"
1061 listing keys for "bookmarks"
1066 repository tip rolled back to revision 0 (undo push)
1062 repository tip rolled back to revision 0 (undo push)
1067 0:6675d58eff77
1063 0:6675d58eff77
1068
1064
1069
1065
1070 asterisk
1066 asterisk
1071
1067
1072 $ init_config
1068 $ init_config
1073
1069
1074 asterisk test
1070 asterisk test
1075
1071
1076 $ echo '[acl.allow]' >> $config
1072 $ echo '[acl.allow]' >> $config
1077 $ echo "** = fred" >> $config
1073 $ echo "** = fred" >> $config
1078
1074
1079 fred is always allowed
1075 fred is always allowed
1080
1076
1081 $ do_push fred
1077 $ do_push fred
1082 Pushing as user fred
1078 Pushing as user fred
1083 hgrc = """
1079 hgrc = """
1084 [acl]
1080 [acl]
1085 sources = push
1081 sources = push
1086 [extensions]
1082 [extensions]
1087 [acl.allow]
1083 [acl.allow]
1088 ** = fred
1084 ** = fred
1089 """
1085 """
1090 pushing to ../b
1086 pushing to ../b
1091 query 1; heads
1087 query 1; heads
1092 searching for changes
1088 searching for changes
1093 all remote heads known locally
1089 all remote heads known locally
1094 invalid branchheads cache: tip differs
1095 listing keys for "bookmarks"
1090 listing keys for "bookmarks"
1096 3 changesets found
1091 3 changesets found
1097 list of changesets:
1092 list of changesets:
1098 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1093 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1099 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1094 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1100 911600dab2ae7a9baff75958b84fe606851ce955
1095 911600dab2ae7a9baff75958b84fe606851ce955
1101 adding changesets
1096 adding changesets
1102 bundling: 1/3 changesets (33.33%)
1097 bundling: 1/3 changesets (33.33%)
1103 bundling: 2/3 changesets (66.67%)
1098 bundling: 2/3 changesets (66.67%)
1104 bundling: 3/3 changesets (100.00%)
1099 bundling: 3/3 changesets (100.00%)
1105 bundling: 1/3 manifests (33.33%)
1100 bundling: 1/3 manifests (33.33%)
1106 bundling: 2/3 manifests (66.67%)
1101 bundling: 2/3 manifests (66.67%)
1107 bundling: 3/3 manifests (100.00%)
1102 bundling: 3/3 manifests (100.00%)
1108 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1103 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1109 bundling: foo/file.txt 2/3 files (66.67%)
1104 bundling: foo/file.txt 2/3 files (66.67%)
1110 bundling: quux/file.py 3/3 files (100.00%)
1105 bundling: quux/file.py 3/3 files (100.00%)
1111 changesets: 1 chunks
1106 changesets: 1 chunks
1112 add changeset ef1ea85a6374
1107 add changeset ef1ea85a6374
1113 changesets: 2 chunks
1108 changesets: 2 chunks
1114 add changeset f9cafe1212c8
1109 add changeset f9cafe1212c8
1115 changesets: 3 chunks
1110 changesets: 3 chunks
1116 add changeset 911600dab2ae
1111 add changeset 911600dab2ae
1117 adding manifests
1112 adding manifests
1118 manifests: 1/3 chunks (33.33%)
1113 manifests: 1/3 chunks (33.33%)
1119 manifests: 2/3 chunks (66.67%)
1114 manifests: 2/3 chunks (66.67%)
1120 manifests: 3/3 chunks (100.00%)
1115 manifests: 3/3 chunks (100.00%)
1121 adding file changes
1116 adding file changes
1122 adding foo/Bar/file.txt revisions
1117 adding foo/Bar/file.txt revisions
1123 files: 1/3 chunks (33.33%)
1118 files: 1/3 chunks (33.33%)
1124 adding foo/file.txt revisions
1119 adding foo/file.txt revisions
1125 files: 2/3 chunks (66.67%)
1120 files: 2/3 chunks (66.67%)
1126 adding quux/file.py revisions
1121 adding quux/file.py revisions
1127 files: 3/3 chunks (100.00%)
1122 files: 3/3 chunks (100.00%)
1128 added 3 changesets with 3 changes to 3 files
1123 added 3 changesets with 3 changes to 3 files
1129 calling hook pretxnchangegroup.acl: hgext.acl.hook
1124 calling hook pretxnchangegroup.acl: hgext.acl.hook
1130 acl: checking access for user "fred"
1125 acl: checking access for user "fred"
1131 acl: acl.allow.branches not enabled
1126 acl: acl.allow.branches not enabled
1132 acl: acl.deny.branches not enabled
1127 acl: acl.deny.branches not enabled
1133 acl: acl.allow enabled, 1 entries for user fred
1128 acl: acl.allow enabled, 1 entries for user fred
1134 acl: acl.deny not enabled
1129 acl: acl.deny not enabled
1135 acl: branch access granted: "ef1ea85a6374" on branch "default"
1130 acl: branch access granted: "ef1ea85a6374" on branch "default"
1136 acl: path access granted: "ef1ea85a6374"
1131 acl: path access granted: "ef1ea85a6374"
1137 acl: branch access granted: "f9cafe1212c8" on branch "default"
1132 acl: branch access granted: "f9cafe1212c8" on branch "default"
1138 acl: path access granted: "f9cafe1212c8"
1133 acl: path access granted: "f9cafe1212c8"
1139 acl: branch access granted: "911600dab2ae" on branch "default"
1134 acl: branch access granted: "911600dab2ae" on branch "default"
1140 acl: path access granted: "911600dab2ae"
1135 acl: path access granted: "911600dab2ae"
1141 listing keys for "phases"
1136 listing keys for "phases"
1142 try to push obsolete markers to remote
1137 try to push obsolete markers to remote
1143 updating the branch cache
1138 updating the branch cache
1144 checking for updated bookmarks
1139 checking for updated bookmarks
1145 listing keys for "bookmarks"
1140 listing keys for "bookmarks"
1146 repository tip rolled back to revision 0 (undo push)
1141 repository tip rolled back to revision 0 (undo push)
1147 0:6675d58eff77
1142 0:6675d58eff77
1148
1143
1149
1144
1150 $ echo '[acl.deny]' >> $config
1145 $ echo '[acl.deny]' >> $config
1151 $ echo "foo/Bar/** = *" >> $config
1146 $ echo "foo/Bar/** = *" >> $config
1152
1147
1153 no one is allowed inside foo/Bar/
1148 no one is allowed inside foo/Bar/
1154
1149
1155 $ do_push fred
1150 $ do_push fred
1156 Pushing as user fred
1151 Pushing as user fred
1157 hgrc = """
1152 hgrc = """
1158 [acl]
1153 [acl]
1159 sources = push
1154 sources = push
1160 [extensions]
1155 [extensions]
1161 [acl.allow]
1156 [acl.allow]
1162 ** = fred
1157 ** = fred
1163 [acl.deny]
1158 [acl.deny]
1164 foo/Bar/** = *
1159 foo/Bar/** = *
1165 """
1160 """
1166 pushing to ../b
1161 pushing to ../b
1167 query 1; heads
1162 query 1; heads
1168 searching for changes
1163 searching for changes
1169 all remote heads known locally
1164 all remote heads known locally
1170 invalid branchheads cache: tip differs
1171 listing keys for "bookmarks"
1165 listing keys for "bookmarks"
1172 3 changesets found
1166 3 changesets found
1173 list of changesets:
1167 list of changesets:
1174 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1168 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1175 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1169 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1176 911600dab2ae7a9baff75958b84fe606851ce955
1170 911600dab2ae7a9baff75958b84fe606851ce955
1177 adding changesets
1171 adding changesets
1178 bundling: 1/3 changesets (33.33%)
1172 bundling: 1/3 changesets (33.33%)
1179 bundling: 2/3 changesets (66.67%)
1173 bundling: 2/3 changesets (66.67%)
1180 bundling: 3/3 changesets (100.00%)
1174 bundling: 3/3 changesets (100.00%)
1181 bundling: 1/3 manifests (33.33%)
1175 bundling: 1/3 manifests (33.33%)
1182 bundling: 2/3 manifests (66.67%)
1176 bundling: 2/3 manifests (66.67%)
1183 bundling: 3/3 manifests (100.00%)
1177 bundling: 3/3 manifests (100.00%)
1184 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1178 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1185 bundling: foo/file.txt 2/3 files (66.67%)
1179 bundling: foo/file.txt 2/3 files (66.67%)
1186 bundling: quux/file.py 3/3 files (100.00%)
1180 bundling: quux/file.py 3/3 files (100.00%)
1187 changesets: 1 chunks
1181 changesets: 1 chunks
1188 add changeset ef1ea85a6374
1182 add changeset ef1ea85a6374
1189 changesets: 2 chunks
1183 changesets: 2 chunks
1190 add changeset f9cafe1212c8
1184 add changeset f9cafe1212c8
1191 changesets: 3 chunks
1185 changesets: 3 chunks
1192 add changeset 911600dab2ae
1186 add changeset 911600dab2ae
1193 adding manifests
1187 adding manifests
1194 manifests: 1/3 chunks (33.33%)
1188 manifests: 1/3 chunks (33.33%)
1195 manifests: 2/3 chunks (66.67%)
1189 manifests: 2/3 chunks (66.67%)
1196 manifests: 3/3 chunks (100.00%)
1190 manifests: 3/3 chunks (100.00%)
1197 adding file changes
1191 adding file changes
1198 adding foo/Bar/file.txt revisions
1192 adding foo/Bar/file.txt revisions
1199 files: 1/3 chunks (33.33%)
1193 files: 1/3 chunks (33.33%)
1200 adding foo/file.txt revisions
1194 adding foo/file.txt revisions
1201 files: 2/3 chunks (66.67%)
1195 files: 2/3 chunks (66.67%)
1202 adding quux/file.py revisions
1196 adding quux/file.py revisions
1203 files: 3/3 chunks (100.00%)
1197 files: 3/3 chunks (100.00%)
1204 added 3 changesets with 3 changes to 3 files
1198 added 3 changesets with 3 changes to 3 files
1205 calling hook pretxnchangegroup.acl: hgext.acl.hook
1199 calling hook pretxnchangegroup.acl: hgext.acl.hook
1206 acl: checking access for user "fred"
1200 acl: checking access for user "fred"
1207 acl: acl.allow.branches not enabled
1201 acl: acl.allow.branches not enabled
1208 acl: acl.deny.branches not enabled
1202 acl: acl.deny.branches not enabled
1209 acl: acl.allow enabled, 1 entries for user fred
1203 acl: acl.allow enabled, 1 entries for user fred
1210 acl: acl.deny enabled, 1 entries for user fred
1204 acl: acl.deny enabled, 1 entries for user fred
1211 acl: branch access granted: "ef1ea85a6374" on branch "default"
1205 acl: branch access granted: "ef1ea85a6374" on branch "default"
1212 acl: path access granted: "ef1ea85a6374"
1206 acl: path access granted: "ef1ea85a6374"
1213 acl: branch access granted: "f9cafe1212c8" on branch "default"
1207 acl: branch access granted: "f9cafe1212c8" on branch "default"
1214 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1208 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1215 transaction abort!
1209 transaction abort!
1216 rollback completed
1210 rollback completed
1217 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1211 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1218 no rollback information available
1212 no rollback information available
1219 0:6675d58eff77
1213 0:6675d58eff77
1220
1214
1221
1215
1222 Groups
1216 Groups
1223
1217
1224 $ init_config
1218 $ init_config
1225
1219
1226 OS-level groups
1220 OS-level groups
1227
1221
1228 $ echo '[acl.allow]' >> $config
1222 $ echo '[acl.allow]' >> $config
1229 $ echo "** = @group1" >> $config
1223 $ echo "** = @group1" >> $config
1230
1224
1231 @group1 is always allowed
1225 @group1 is always allowed
1232
1226
1233 $ do_push fred
1227 $ do_push fred
1234 Pushing as user fred
1228 Pushing as user fred
1235 hgrc = """
1229 hgrc = """
1236 [acl]
1230 [acl]
1237 sources = push
1231 sources = push
1238 [extensions]
1232 [extensions]
1239 [acl.allow]
1233 [acl.allow]
1240 ** = @group1
1234 ** = @group1
1241 """
1235 """
1242 pushing to ../b
1236 pushing to ../b
1243 query 1; heads
1237 query 1; heads
1244 searching for changes
1238 searching for changes
1245 all remote heads known locally
1239 all remote heads known locally
1246 listing keys for "bookmarks"
1240 listing keys for "bookmarks"
1247 3 changesets found
1241 3 changesets found
1248 list of changesets:
1242 list of changesets:
1249 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1243 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1250 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1244 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1251 911600dab2ae7a9baff75958b84fe606851ce955
1245 911600dab2ae7a9baff75958b84fe606851ce955
1252 adding changesets
1246 adding changesets
1253 bundling: 1/3 changesets (33.33%)
1247 bundling: 1/3 changesets (33.33%)
1254 bundling: 2/3 changesets (66.67%)
1248 bundling: 2/3 changesets (66.67%)
1255 bundling: 3/3 changesets (100.00%)
1249 bundling: 3/3 changesets (100.00%)
1256 bundling: 1/3 manifests (33.33%)
1250 bundling: 1/3 manifests (33.33%)
1257 bundling: 2/3 manifests (66.67%)
1251 bundling: 2/3 manifests (66.67%)
1258 bundling: 3/3 manifests (100.00%)
1252 bundling: 3/3 manifests (100.00%)
1259 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1253 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1260 bundling: foo/file.txt 2/3 files (66.67%)
1254 bundling: foo/file.txt 2/3 files (66.67%)
1261 bundling: quux/file.py 3/3 files (100.00%)
1255 bundling: quux/file.py 3/3 files (100.00%)
1262 changesets: 1 chunks
1256 changesets: 1 chunks
1263 add changeset ef1ea85a6374
1257 add changeset ef1ea85a6374
1264 changesets: 2 chunks
1258 changesets: 2 chunks
1265 add changeset f9cafe1212c8
1259 add changeset f9cafe1212c8
1266 changesets: 3 chunks
1260 changesets: 3 chunks
1267 add changeset 911600dab2ae
1261 add changeset 911600dab2ae
1268 adding manifests
1262 adding manifests
1269 manifests: 1/3 chunks (33.33%)
1263 manifests: 1/3 chunks (33.33%)
1270 manifests: 2/3 chunks (66.67%)
1264 manifests: 2/3 chunks (66.67%)
1271 manifests: 3/3 chunks (100.00%)
1265 manifests: 3/3 chunks (100.00%)
1272 adding file changes
1266 adding file changes
1273 adding foo/Bar/file.txt revisions
1267 adding foo/Bar/file.txt revisions
1274 files: 1/3 chunks (33.33%)
1268 files: 1/3 chunks (33.33%)
1275 adding foo/file.txt revisions
1269 adding foo/file.txt revisions
1276 files: 2/3 chunks (66.67%)
1270 files: 2/3 chunks (66.67%)
1277 adding quux/file.py revisions
1271 adding quux/file.py revisions
1278 files: 3/3 chunks (100.00%)
1272 files: 3/3 chunks (100.00%)
1279 added 3 changesets with 3 changes to 3 files
1273 added 3 changesets with 3 changes to 3 files
1280 calling hook pretxnchangegroup.acl: hgext.acl.hook
1274 calling hook pretxnchangegroup.acl: hgext.acl.hook
1281 acl: checking access for user "fred"
1275 acl: checking access for user "fred"
1282 acl: acl.allow.branches not enabled
1276 acl: acl.allow.branches not enabled
1283 acl: acl.deny.branches not enabled
1277 acl: acl.deny.branches not enabled
1284 acl: "group1" not defined in [acl.groups]
1278 acl: "group1" not defined in [acl.groups]
1285 acl: acl.allow enabled, 1 entries for user fred
1279 acl: acl.allow enabled, 1 entries for user fred
1286 acl: acl.deny not enabled
1280 acl: acl.deny not enabled
1287 acl: branch access granted: "ef1ea85a6374" on branch "default"
1281 acl: branch access granted: "ef1ea85a6374" on branch "default"
1288 acl: path access granted: "ef1ea85a6374"
1282 acl: path access granted: "ef1ea85a6374"
1289 acl: branch access granted: "f9cafe1212c8" on branch "default"
1283 acl: branch access granted: "f9cafe1212c8" on branch "default"
1290 acl: path access granted: "f9cafe1212c8"
1284 acl: path access granted: "f9cafe1212c8"
1291 acl: branch access granted: "911600dab2ae" on branch "default"
1285 acl: branch access granted: "911600dab2ae" on branch "default"
1292 acl: path access granted: "911600dab2ae"
1286 acl: path access granted: "911600dab2ae"
1293 listing keys for "phases"
1287 listing keys for "phases"
1294 try to push obsolete markers to remote
1288 try to push obsolete markers to remote
1295 updating the branch cache
1289 updating the branch cache
1296 checking for updated bookmarks
1290 checking for updated bookmarks
1297 listing keys for "bookmarks"
1291 listing keys for "bookmarks"
1298 repository tip rolled back to revision 0 (undo push)
1292 repository tip rolled back to revision 0 (undo push)
1299 0:6675d58eff77
1293 0:6675d58eff77
1300
1294
1301
1295
1302 $ echo '[acl.deny]' >> $config
1296 $ echo '[acl.deny]' >> $config
1303 $ echo "foo/Bar/** = @group1" >> $config
1297 $ echo "foo/Bar/** = @group1" >> $config
1304
1298
1305 @group is allowed inside anything but foo/Bar/
1299 @group is allowed inside anything but foo/Bar/
1306
1300
1307 $ do_push fred
1301 $ do_push fred
1308 Pushing as user fred
1302 Pushing as user fred
1309 hgrc = """
1303 hgrc = """
1310 [acl]
1304 [acl]
1311 sources = push
1305 sources = push
1312 [extensions]
1306 [extensions]
1313 [acl.allow]
1307 [acl.allow]
1314 ** = @group1
1308 ** = @group1
1315 [acl.deny]
1309 [acl.deny]
1316 foo/Bar/** = @group1
1310 foo/Bar/** = @group1
1317 """
1311 """
1318 pushing to ../b
1312 pushing to ../b
1319 query 1; heads
1313 query 1; heads
1320 searching for changes
1314 searching for changes
1321 all remote heads known locally
1315 all remote heads known locally
1322 invalid branchheads cache: tip differs
1323 listing keys for "bookmarks"
1316 listing keys for "bookmarks"
1324 3 changesets found
1317 3 changesets found
1325 list of changesets:
1318 list of changesets:
1326 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1319 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1327 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1320 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1328 911600dab2ae7a9baff75958b84fe606851ce955
1321 911600dab2ae7a9baff75958b84fe606851ce955
1329 adding changesets
1322 adding changesets
1330 bundling: 1/3 changesets (33.33%)
1323 bundling: 1/3 changesets (33.33%)
1331 bundling: 2/3 changesets (66.67%)
1324 bundling: 2/3 changesets (66.67%)
1332 bundling: 3/3 changesets (100.00%)
1325 bundling: 3/3 changesets (100.00%)
1333 bundling: 1/3 manifests (33.33%)
1326 bundling: 1/3 manifests (33.33%)
1334 bundling: 2/3 manifests (66.67%)
1327 bundling: 2/3 manifests (66.67%)
1335 bundling: 3/3 manifests (100.00%)
1328 bundling: 3/3 manifests (100.00%)
1336 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1329 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1337 bundling: foo/file.txt 2/3 files (66.67%)
1330 bundling: foo/file.txt 2/3 files (66.67%)
1338 bundling: quux/file.py 3/3 files (100.00%)
1331 bundling: quux/file.py 3/3 files (100.00%)
1339 changesets: 1 chunks
1332 changesets: 1 chunks
1340 add changeset ef1ea85a6374
1333 add changeset ef1ea85a6374
1341 changesets: 2 chunks
1334 changesets: 2 chunks
1342 add changeset f9cafe1212c8
1335 add changeset f9cafe1212c8
1343 changesets: 3 chunks
1336 changesets: 3 chunks
1344 add changeset 911600dab2ae
1337 add changeset 911600dab2ae
1345 adding manifests
1338 adding manifests
1346 manifests: 1/3 chunks (33.33%)
1339 manifests: 1/3 chunks (33.33%)
1347 manifests: 2/3 chunks (66.67%)
1340 manifests: 2/3 chunks (66.67%)
1348 manifests: 3/3 chunks (100.00%)
1341 manifests: 3/3 chunks (100.00%)
1349 adding file changes
1342 adding file changes
1350 adding foo/Bar/file.txt revisions
1343 adding foo/Bar/file.txt revisions
1351 files: 1/3 chunks (33.33%)
1344 files: 1/3 chunks (33.33%)
1352 adding foo/file.txt revisions
1345 adding foo/file.txt revisions
1353 files: 2/3 chunks (66.67%)
1346 files: 2/3 chunks (66.67%)
1354 adding quux/file.py revisions
1347 adding quux/file.py revisions
1355 files: 3/3 chunks (100.00%)
1348 files: 3/3 chunks (100.00%)
1356 added 3 changesets with 3 changes to 3 files
1349 added 3 changesets with 3 changes to 3 files
1357 calling hook pretxnchangegroup.acl: hgext.acl.hook
1350 calling hook pretxnchangegroup.acl: hgext.acl.hook
1358 acl: checking access for user "fred"
1351 acl: checking access for user "fred"
1359 acl: acl.allow.branches not enabled
1352 acl: acl.allow.branches not enabled
1360 acl: acl.deny.branches not enabled
1353 acl: acl.deny.branches not enabled
1361 acl: "group1" not defined in [acl.groups]
1354 acl: "group1" not defined in [acl.groups]
1362 acl: acl.allow enabled, 1 entries for user fred
1355 acl: acl.allow enabled, 1 entries for user fred
1363 acl: "group1" not defined in [acl.groups]
1356 acl: "group1" not defined in [acl.groups]
1364 acl: acl.deny enabled, 1 entries for user fred
1357 acl: acl.deny enabled, 1 entries for user fred
1365 acl: branch access granted: "ef1ea85a6374" on branch "default"
1358 acl: branch access granted: "ef1ea85a6374" on branch "default"
1366 acl: path access granted: "ef1ea85a6374"
1359 acl: path access granted: "ef1ea85a6374"
1367 acl: branch access granted: "f9cafe1212c8" on branch "default"
1360 acl: branch access granted: "f9cafe1212c8" on branch "default"
1368 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1361 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1369 transaction abort!
1362 transaction abort!
1370 rollback completed
1363 rollback completed
1371 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1364 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1372 no rollback information available
1365 no rollback information available
1373 0:6675d58eff77
1366 0:6675d58eff77
1374
1367
1375
1368
1376 Invalid group
1369 Invalid group
1377
1370
1378 Disable the fakegroups trick to get real failures
1371 Disable the fakegroups trick to get real failures
1379
1372
1380 $ grep -v fakegroups $config > config.tmp
1373 $ grep -v fakegroups $config > config.tmp
1381 $ mv config.tmp $config
1374 $ mv config.tmp $config
1382 $ echo '[acl.allow]' >> $config
1375 $ echo '[acl.allow]' >> $config
1383 $ echo "** = @unlikelytoexist" >> $config
1376 $ echo "** = @unlikelytoexist" >> $config
1384 $ do_push fred 2>&1 | grep unlikelytoexist
1377 $ do_push fred 2>&1 | grep unlikelytoexist
1385 ** = @unlikelytoexist
1378 ** = @unlikelytoexist
1386 acl: "unlikelytoexist" not defined in [acl.groups]
1379 acl: "unlikelytoexist" not defined in [acl.groups]
1387 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1380 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1388 abort: group 'unlikelytoexist' is undefined
1381 abort: group 'unlikelytoexist' is undefined
1389
1382
1390
1383
1391 Branch acl tests setup
1384 Branch acl tests setup
1392
1385
1393 $ init_config
1386 $ init_config
1394 $ cd b
1387 $ cd b
1395 $ hg up
1388 $ hg up
1396 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1389 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1397 $ hg branch foobar
1390 $ hg branch foobar
1398 marked working directory as branch foobar
1391 marked working directory as branch foobar
1399 (branches are permanent and global, did you want a bookmark?)
1392 (branches are permanent and global, did you want a bookmark?)
1400 $ hg commit -m 'create foobar'
1393 $ hg commit -m 'create foobar'
1401 $ echo 'foo contents' > abc.txt
1394 $ echo 'foo contents' > abc.txt
1402 $ hg add abc.txt
1395 $ hg add abc.txt
1403 $ hg commit -m 'foobar contents'
1396 $ hg commit -m 'foobar contents'
1404 $ cd ..
1397 $ cd ..
1405 $ hg --cwd a pull ../b
1398 $ hg --cwd a pull ../b
1406 pulling from ../b
1399 pulling from ../b
1407 searching for changes
1400 searching for changes
1408 adding changesets
1401 adding changesets
1409 adding manifests
1402 adding manifests
1410 adding file changes
1403 adding file changes
1411 added 2 changesets with 1 changes to 1 files (+1 heads)
1404 added 2 changesets with 1 changes to 1 files (+1 heads)
1412 (run 'hg heads' to see heads)
1405 (run 'hg heads' to see heads)
1413
1406
1414 Create additional changeset on foobar branch
1407 Create additional changeset on foobar branch
1415
1408
1416 $ cd a
1409 $ cd a
1417 $ hg up -C foobar
1410 $ hg up -C foobar
1418 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1411 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1419 $ echo 'foo contents2' > abc.txt
1412 $ echo 'foo contents2' > abc.txt
1420 $ hg commit -m 'foobar contents2'
1413 $ hg commit -m 'foobar contents2'
1421 $ cd ..
1414 $ cd ..
1422
1415
1423
1416
1424 No branch acls specified
1417 No branch acls specified
1425
1418
1426 $ do_push astro
1419 $ do_push astro
1427 Pushing as user astro
1420 Pushing as user astro
1428 hgrc = """
1421 hgrc = """
1429 [acl]
1422 [acl]
1430 sources = push
1423 sources = push
1431 [extensions]
1424 [extensions]
1432 """
1425 """
1433 pushing to ../b
1426 pushing to ../b
1434 query 1; heads
1427 query 1; heads
1435 searching for changes
1428 searching for changes
1436 all remote heads known locally
1429 all remote heads known locally
1437 listing keys for "bookmarks"
1430 listing keys for "bookmarks"
1438 4 changesets found
1431 4 changesets found
1439 list of changesets:
1432 list of changesets:
1440 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1433 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1441 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1434 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1442 911600dab2ae7a9baff75958b84fe606851ce955
1435 911600dab2ae7a9baff75958b84fe606851ce955
1443 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1436 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1444 adding changesets
1437 adding changesets
1445 bundling: 1/4 changesets (25.00%)
1438 bundling: 1/4 changesets (25.00%)
1446 bundling: 2/4 changesets (50.00%)
1439 bundling: 2/4 changesets (50.00%)
1447 bundling: 3/4 changesets (75.00%)
1440 bundling: 3/4 changesets (75.00%)
1448 bundling: 4/4 changesets (100.00%)
1441 bundling: 4/4 changesets (100.00%)
1449 bundling: 1/4 manifests (25.00%)
1442 bundling: 1/4 manifests (25.00%)
1450 bundling: 2/4 manifests (50.00%)
1443 bundling: 2/4 manifests (50.00%)
1451 bundling: 3/4 manifests (75.00%)
1444 bundling: 3/4 manifests (75.00%)
1452 bundling: 4/4 manifests (100.00%)
1445 bundling: 4/4 manifests (100.00%)
1453 bundling: abc.txt 1/4 files (25.00%)
1446 bundling: abc.txt 1/4 files (25.00%)
1454 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1447 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1455 bundling: foo/file.txt 3/4 files (75.00%)
1448 bundling: foo/file.txt 3/4 files (75.00%)
1456 bundling: quux/file.py 4/4 files (100.00%)
1449 bundling: quux/file.py 4/4 files (100.00%)
1457 changesets: 1 chunks
1450 changesets: 1 chunks
1458 add changeset ef1ea85a6374
1451 add changeset ef1ea85a6374
1459 changesets: 2 chunks
1452 changesets: 2 chunks
1460 add changeset f9cafe1212c8
1453 add changeset f9cafe1212c8
1461 changesets: 3 chunks
1454 changesets: 3 chunks
1462 add changeset 911600dab2ae
1455 add changeset 911600dab2ae
1463 changesets: 4 chunks
1456 changesets: 4 chunks
1464 add changeset e8fc755d4d82
1457 add changeset e8fc755d4d82
1465 adding manifests
1458 adding manifests
1466 manifests: 1/4 chunks (25.00%)
1459 manifests: 1/4 chunks (25.00%)
1467 manifests: 2/4 chunks (50.00%)
1460 manifests: 2/4 chunks (50.00%)
1468 manifests: 3/4 chunks (75.00%)
1461 manifests: 3/4 chunks (75.00%)
1469 manifests: 4/4 chunks (100.00%)
1462 manifests: 4/4 chunks (100.00%)
1470 adding file changes
1463 adding file changes
1471 adding abc.txt revisions
1464 adding abc.txt revisions
1472 files: 1/4 chunks (25.00%)
1465 files: 1/4 chunks (25.00%)
1473 adding foo/Bar/file.txt revisions
1466 adding foo/Bar/file.txt revisions
1474 files: 2/4 chunks (50.00%)
1467 files: 2/4 chunks (50.00%)
1475 adding foo/file.txt revisions
1468 adding foo/file.txt revisions
1476 files: 3/4 chunks (75.00%)
1469 files: 3/4 chunks (75.00%)
1477 adding quux/file.py revisions
1470 adding quux/file.py revisions
1478 files: 4/4 chunks (100.00%)
1471 files: 4/4 chunks (100.00%)
1479 added 4 changesets with 4 changes to 4 files (+1 heads)
1472 added 4 changesets with 4 changes to 4 files (+1 heads)
1480 calling hook pretxnchangegroup.acl: hgext.acl.hook
1473 calling hook pretxnchangegroup.acl: hgext.acl.hook
1481 acl: checking access for user "astro"
1474 acl: checking access for user "astro"
1482 acl: acl.allow.branches not enabled
1475 acl: acl.allow.branches not enabled
1483 acl: acl.deny.branches not enabled
1476 acl: acl.deny.branches not enabled
1484 acl: acl.allow not enabled
1477 acl: acl.allow not enabled
1485 acl: acl.deny not enabled
1478 acl: acl.deny not enabled
1486 acl: branch access granted: "ef1ea85a6374" on branch "default"
1479 acl: branch access granted: "ef1ea85a6374" on branch "default"
1487 acl: path access granted: "ef1ea85a6374"
1480 acl: path access granted: "ef1ea85a6374"
1488 acl: branch access granted: "f9cafe1212c8" on branch "default"
1481 acl: branch access granted: "f9cafe1212c8" on branch "default"
1489 acl: path access granted: "f9cafe1212c8"
1482 acl: path access granted: "f9cafe1212c8"
1490 acl: branch access granted: "911600dab2ae" on branch "default"
1483 acl: branch access granted: "911600dab2ae" on branch "default"
1491 acl: path access granted: "911600dab2ae"
1484 acl: path access granted: "911600dab2ae"
1492 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1485 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1493 acl: path access granted: "e8fc755d4d82"
1486 acl: path access granted: "e8fc755d4d82"
1494 listing keys for "phases"
1487 listing keys for "phases"
1495 try to push obsolete markers to remote
1488 try to push obsolete markers to remote
1496 updating the branch cache
1489 updating the branch cache
1497 checking for updated bookmarks
1490 checking for updated bookmarks
1498 listing keys for "bookmarks"
1491 listing keys for "bookmarks"
1499 repository tip rolled back to revision 2 (undo push)
1492 repository tip rolled back to revision 2 (undo push)
1500 2:fb35475503ef
1493 2:fb35475503ef
1501
1494
1502
1495
1503 Branch acl deny test
1496 Branch acl deny test
1504
1497
1505 $ echo "[acl.deny.branches]" >> $config
1498 $ echo "[acl.deny.branches]" >> $config
1506 $ echo "foobar = *" >> $config
1499 $ echo "foobar = *" >> $config
1507 $ do_push astro
1500 $ do_push astro
1508 Pushing as user astro
1501 Pushing as user astro
1509 hgrc = """
1502 hgrc = """
1510 [acl]
1503 [acl]
1511 sources = push
1504 sources = push
1512 [extensions]
1505 [extensions]
1513 [acl.deny.branches]
1506 [acl.deny.branches]
1514 foobar = *
1507 foobar = *
1515 """
1508 """
1516 pushing to ../b
1509 pushing to ../b
1517 query 1; heads
1510 query 1; heads
1518 searching for changes
1511 searching for changes
1519 all remote heads known locally
1512 all remote heads known locally
1520 invalid branchheads cache: tip differs
1521 listing keys for "bookmarks"
1513 listing keys for "bookmarks"
1522 4 changesets found
1514 4 changesets found
1523 list of changesets:
1515 list of changesets:
1524 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1516 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1525 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1517 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1526 911600dab2ae7a9baff75958b84fe606851ce955
1518 911600dab2ae7a9baff75958b84fe606851ce955
1527 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1519 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1528 adding changesets
1520 adding changesets
1529 bundling: 1/4 changesets (25.00%)
1521 bundling: 1/4 changesets (25.00%)
1530 bundling: 2/4 changesets (50.00%)
1522 bundling: 2/4 changesets (50.00%)
1531 bundling: 3/4 changesets (75.00%)
1523 bundling: 3/4 changesets (75.00%)
1532 bundling: 4/4 changesets (100.00%)
1524 bundling: 4/4 changesets (100.00%)
1533 bundling: 1/4 manifests (25.00%)
1525 bundling: 1/4 manifests (25.00%)
1534 bundling: 2/4 manifests (50.00%)
1526 bundling: 2/4 manifests (50.00%)
1535 bundling: 3/4 manifests (75.00%)
1527 bundling: 3/4 manifests (75.00%)
1536 bundling: 4/4 manifests (100.00%)
1528 bundling: 4/4 manifests (100.00%)
1537 bundling: abc.txt 1/4 files (25.00%)
1529 bundling: abc.txt 1/4 files (25.00%)
1538 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1530 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1539 bundling: foo/file.txt 3/4 files (75.00%)
1531 bundling: foo/file.txt 3/4 files (75.00%)
1540 bundling: quux/file.py 4/4 files (100.00%)
1532 bundling: quux/file.py 4/4 files (100.00%)
1541 changesets: 1 chunks
1533 changesets: 1 chunks
1542 add changeset ef1ea85a6374
1534 add changeset ef1ea85a6374
1543 changesets: 2 chunks
1535 changesets: 2 chunks
1544 add changeset f9cafe1212c8
1536 add changeset f9cafe1212c8
1545 changesets: 3 chunks
1537 changesets: 3 chunks
1546 add changeset 911600dab2ae
1538 add changeset 911600dab2ae
1547 changesets: 4 chunks
1539 changesets: 4 chunks
1548 add changeset e8fc755d4d82
1540 add changeset e8fc755d4d82
1549 adding manifests
1541 adding manifests
1550 manifests: 1/4 chunks (25.00%)
1542 manifests: 1/4 chunks (25.00%)
1551 manifests: 2/4 chunks (50.00%)
1543 manifests: 2/4 chunks (50.00%)
1552 manifests: 3/4 chunks (75.00%)
1544 manifests: 3/4 chunks (75.00%)
1553 manifests: 4/4 chunks (100.00%)
1545 manifests: 4/4 chunks (100.00%)
1554 adding file changes
1546 adding file changes
1555 adding abc.txt revisions
1547 adding abc.txt revisions
1556 files: 1/4 chunks (25.00%)
1548 files: 1/4 chunks (25.00%)
1557 adding foo/Bar/file.txt revisions
1549 adding foo/Bar/file.txt revisions
1558 files: 2/4 chunks (50.00%)
1550 files: 2/4 chunks (50.00%)
1559 adding foo/file.txt revisions
1551 adding foo/file.txt revisions
1560 files: 3/4 chunks (75.00%)
1552 files: 3/4 chunks (75.00%)
1561 adding quux/file.py revisions
1553 adding quux/file.py revisions
1562 files: 4/4 chunks (100.00%)
1554 files: 4/4 chunks (100.00%)
1563 added 4 changesets with 4 changes to 4 files (+1 heads)
1555 added 4 changesets with 4 changes to 4 files (+1 heads)
1564 calling hook pretxnchangegroup.acl: hgext.acl.hook
1556 calling hook pretxnchangegroup.acl: hgext.acl.hook
1565 acl: checking access for user "astro"
1557 acl: checking access for user "astro"
1566 acl: acl.allow.branches not enabled
1558 acl: acl.allow.branches not enabled
1567 acl: acl.deny.branches enabled, 1 entries for user astro
1559 acl: acl.deny.branches enabled, 1 entries for user astro
1568 acl: acl.allow not enabled
1560 acl: acl.allow not enabled
1569 acl: acl.deny not enabled
1561 acl: acl.deny not enabled
1570 acl: branch access granted: "ef1ea85a6374" on branch "default"
1562 acl: branch access granted: "ef1ea85a6374" on branch "default"
1571 acl: path access granted: "ef1ea85a6374"
1563 acl: path access granted: "ef1ea85a6374"
1572 acl: branch access granted: "f9cafe1212c8" on branch "default"
1564 acl: branch access granted: "f9cafe1212c8" on branch "default"
1573 acl: path access granted: "f9cafe1212c8"
1565 acl: path access granted: "f9cafe1212c8"
1574 acl: branch access granted: "911600dab2ae" on branch "default"
1566 acl: branch access granted: "911600dab2ae" on branch "default"
1575 acl: path access granted: "911600dab2ae"
1567 acl: path access granted: "911600dab2ae"
1576 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1568 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1577 transaction abort!
1569 transaction abort!
1578 rollback completed
1570 rollback completed
1579 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1571 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1580 no rollback information available
1572 no rollback information available
1581 2:fb35475503ef
1573 2:fb35475503ef
1582
1574
1583
1575
1584 Branch acl empty allow test
1576 Branch acl empty allow test
1585
1577
1586 $ init_config
1578 $ init_config
1587 $ echo "[acl.allow.branches]" >> $config
1579 $ echo "[acl.allow.branches]" >> $config
1588 $ do_push astro
1580 $ do_push astro
1589 Pushing as user astro
1581 Pushing as user astro
1590 hgrc = """
1582 hgrc = """
1591 [acl]
1583 [acl]
1592 sources = push
1584 sources = push
1593 [extensions]
1585 [extensions]
1594 [acl.allow.branches]
1586 [acl.allow.branches]
1595 """
1587 """
1596 pushing to ../b
1588 pushing to ../b
1597 query 1; heads
1589 query 1; heads
1598 searching for changes
1590 searching for changes
1599 all remote heads known locally
1591 all remote heads known locally
1600 listing keys for "bookmarks"
1592 listing keys for "bookmarks"
1601 4 changesets found
1593 4 changesets found
1602 list of changesets:
1594 list of changesets:
1603 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1595 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1604 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1596 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1605 911600dab2ae7a9baff75958b84fe606851ce955
1597 911600dab2ae7a9baff75958b84fe606851ce955
1606 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1598 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1607 adding changesets
1599 adding changesets
1608 bundling: 1/4 changesets (25.00%)
1600 bundling: 1/4 changesets (25.00%)
1609 bundling: 2/4 changesets (50.00%)
1601 bundling: 2/4 changesets (50.00%)
1610 bundling: 3/4 changesets (75.00%)
1602 bundling: 3/4 changesets (75.00%)
1611 bundling: 4/4 changesets (100.00%)
1603 bundling: 4/4 changesets (100.00%)
1612 bundling: 1/4 manifests (25.00%)
1604 bundling: 1/4 manifests (25.00%)
1613 bundling: 2/4 manifests (50.00%)
1605 bundling: 2/4 manifests (50.00%)
1614 bundling: 3/4 manifests (75.00%)
1606 bundling: 3/4 manifests (75.00%)
1615 bundling: 4/4 manifests (100.00%)
1607 bundling: 4/4 manifests (100.00%)
1616 bundling: abc.txt 1/4 files (25.00%)
1608 bundling: abc.txt 1/4 files (25.00%)
1617 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1609 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1618 bundling: foo/file.txt 3/4 files (75.00%)
1610 bundling: foo/file.txt 3/4 files (75.00%)
1619 bundling: quux/file.py 4/4 files (100.00%)
1611 bundling: quux/file.py 4/4 files (100.00%)
1620 changesets: 1 chunks
1612 changesets: 1 chunks
1621 add changeset ef1ea85a6374
1613 add changeset ef1ea85a6374
1622 changesets: 2 chunks
1614 changesets: 2 chunks
1623 add changeset f9cafe1212c8
1615 add changeset f9cafe1212c8
1624 changesets: 3 chunks
1616 changesets: 3 chunks
1625 add changeset 911600dab2ae
1617 add changeset 911600dab2ae
1626 changesets: 4 chunks
1618 changesets: 4 chunks
1627 add changeset e8fc755d4d82
1619 add changeset e8fc755d4d82
1628 adding manifests
1620 adding manifests
1629 manifests: 1/4 chunks (25.00%)
1621 manifests: 1/4 chunks (25.00%)
1630 manifests: 2/4 chunks (50.00%)
1622 manifests: 2/4 chunks (50.00%)
1631 manifests: 3/4 chunks (75.00%)
1623 manifests: 3/4 chunks (75.00%)
1632 manifests: 4/4 chunks (100.00%)
1624 manifests: 4/4 chunks (100.00%)
1633 adding file changes
1625 adding file changes
1634 adding abc.txt revisions
1626 adding abc.txt revisions
1635 files: 1/4 chunks (25.00%)
1627 files: 1/4 chunks (25.00%)
1636 adding foo/Bar/file.txt revisions
1628 adding foo/Bar/file.txt revisions
1637 files: 2/4 chunks (50.00%)
1629 files: 2/4 chunks (50.00%)
1638 adding foo/file.txt revisions
1630 adding foo/file.txt revisions
1639 files: 3/4 chunks (75.00%)
1631 files: 3/4 chunks (75.00%)
1640 adding quux/file.py revisions
1632 adding quux/file.py revisions
1641 files: 4/4 chunks (100.00%)
1633 files: 4/4 chunks (100.00%)
1642 added 4 changesets with 4 changes to 4 files (+1 heads)
1634 added 4 changesets with 4 changes to 4 files (+1 heads)
1643 calling hook pretxnchangegroup.acl: hgext.acl.hook
1635 calling hook pretxnchangegroup.acl: hgext.acl.hook
1644 acl: checking access for user "astro"
1636 acl: checking access for user "astro"
1645 acl: acl.allow.branches enabled, 0 entries for user astro
1637 acl: acl.allow.branches enabled, 0 entries for user astro
1646 acl: acl.deny.branches not enabled
1638 acl: acl.deny.branches not enabled
1647 acl: acl.allow not enabled
1639 acl: acl.allow not enabled
1648 acl: acl.deny not enabled
1640 acl: acl.deny not enabled
1649 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1641 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1650 transaction abort!
1642 transaction abort!
1651 rollback completed
1643 rollback completed
1652 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1644 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1653 no rollback information available
1645 no rollback information available
1654 2:fb35475503ef
1646 2:fb35475503ef
1655
1647
1656
1648
1657 Branch acl allow other
1649 Branch acl allow other
1658
1650
1659 $ init_config
1651 $ init_config
1660 $ echo "[acl.allow.branches]" >> $config
1652 $ echo "[acl.allow.branches]" >> $config
1661 $ echo "* = george" >> $config
1653 $ echo "* = george" >> $config
1662 $ do_push astro
1654 $ do_push astro
1663 Pushing as user astro
1655 Pushing as user astro
1664 hgrc = """
1656 hgrc = """
1665 [acl]
1657 [acl]
1666 sources = push
1658 sources = push
1667 [extensions]
1659 [extensions]
1668 [acl.allow.branches]
1660 [acl.allow.branches]
1669 * = george
1661 * = george
1670 """
1662 """
1671 pushing to ../b
1663 pushing to ../b
1672 query 1; heads
1664 query 1; heads
1673 searching for changes
1665 searching for changes
1674 all remote heads known locally
1666 all remote heads known locally
1675 listing keys for "bookmarks"
1667 listing keys for "bookmarks"
1676 4 changesets found
1668 4 changesets found
1677 list of changesets:
1669 list of changesets:
1678 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1670 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1679 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1671 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1680 911600dab2ae7a9baff75958b84fe606851ce955
1672 911600dab2ae7a9baff75958b84fe606851ce955
1681 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1673 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1682 adding changesets
1674 adding changesets
1683 bundling: 1/4 changesets (25.00%)
1675 bundling: 1/4 changesets (25.00%)
1684 bundling: 2/4 changesets (50.00%)
1676 bundling: 2/4 changesets (50.00%)
1685 bundling: 3/4 changesets (75.00%)
1677 bundling: 3/4 changesets (75.00%)
1686 bundling: 4/4 changesets (100.00%)
1678 bundling: 4/4 changesets (100.00%)
1687 bundling: 1/4 manifests (25.00%)
1679 bundling: 1/4 manifests (25.00%)
1688 bundling: 2/4 manifests (50.00%)
1680 bundling: 2/4 manifests (50.00%)
1689 bundling: 3/4 manifests (75.00%)
1681 bundling: 3/4 manifests (75.00%)
1690 bundling: 4/4 manifests (100.00%)
1682 bundling: 4/4 manifests (100.00%)
1691 bundling: abc.txt 1/4 files (25.00%)
1683 bundling: abc.txt 1/4 files (25.00%)
1692 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1684 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1693 bundling: foo/file.txt 3/4 files (75.00%)
1685 bundling: foo/file.txt 3/4 files (75.00%)
1694 bundling: quux/file.py 4/4 files (100.00%)
1686 bundling: quux/file.py 4/4 files (100.00%)
1695 changesets: 1 chunks
1687 changesets: 1 chunks
1696 add changeset ef1ea85a6374
1688 add changeset ef1ea85a6374
1697 changesets: 2 chunks
1689 changesets: 2 chunks
1698 add changeset f9cafe1212c8
1690 add changeset f9cafe1212c8
1699 changesets: 3 chunks
1691 changesets: 3 chunks
1700 add changeset 911600dab2ae
1692 add changeset 911600dab2ae
1701 changesets: 4 chunks
1693 changesets: 4 chunks
1702 add changeset e8fc755d4d82
1694 add changeset e8fc755d4d82
1703 adding manifests
1695 adding manifests
1704 manifests: 1/4 chunks (25.00%)
1696 manifests: 1/4 chunks (25.00%)
1705 manifests: 2/4 chunks (50.00%)
1697 manifests: 2/4 chunks (50.00%)
1706 manifests: 3/4 chunks (75.00%)
1698 manifests: 3/4 chunks (75.00%)
1707 manifests: 4/4 chunks (100.00%)
1699 manifests: 4/4 chunks (100.00%)
1708 adding file changes
1700 adding file changes
1709 adding abc.txt revisions
1701 adding abc.txt revisions
1710 files: 1/4 chunks (25.00%)
1702 files: 1/4 chunks (25.00%)
1711 adding foo/Bar/file.txt revisions
1703 adding foo/Bar/file.txt revisions
1712 files: 2/4 chunks (50.00%)
1704 files: 2/4 chunks (50.00%)
1713 adding foo/file.txt revisions
1705 adding foo/file.txt revisions
1714 files: 3/4 chunks (75.00%)
1706 files: 3/4 chunks (75.00%)
1715 adding quux/file.py revisions
1707 adding quux/file.py revisions
1716 files: 4/4 chunks (100.00%)
1708 files: 4/4 chunks (100.00%)
1717 added 4 changesets with 4 changes to 4 files (+1 heads)
1709 added 4 changesets with 4 changes to 4 files (+1 heads)
1718 calling hook pretxnchangegroup.acl: hgext.acl.hook
1710 calling hook pretxnchangegroup.acl: hgext.acl.hook
1719 acl: checking access for user "astro"
1711 acl: checking access for user "astro"
1720 acl: acl.allow.branches enabled, 0 entries for user astro
1712 acl: acl.allow.branches enabled, 0 entries for user astro
1721 acl: acl.deny.branches not enabled
1713 acl: acl.deny.branches not enabled
1722 acl: acl.allow not enabled
1714 acl: acl.allow not enabled
1723 acl: acl.deny not enabled
1715 acl: acl.deny not enabled
1724 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1716 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1725 transaction abort!
1717 transaction abort!
1726 rollback completed
1718 rollback completed
1727 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1719 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1728 no rollback information available
1720 no rollback information available
1729 2:fb35475503ef
1721 2:fb35475503ef
1730
1722
1731 $ do_push george
1723 $ do_push george
1732 Pushing as user george
1724 Pushing as user george
1733 hgrc = """
1725 hgrc = """
1734 [acl]
1726 [acl]
1735 sources = push
1727 sources = push
1736 [extensions]
1728 [extensions]
1737 [acl.allow.branches]
1729 [acl.allow.branches]
1738 * = george
1730 * = george
1739 """
1731 """
1740 pushing to ../b
1732 pushing to ../b
1741 query 1; heads
1733 query 1; heads
1742 searching for changes
1734 searching for changes
1743 all remote heads known locally
1735 all remote heads known locally
1744 listing keys for "bookmarks"
1736 listing keys for "bookmarks"
1745 4 changesets found
1737 4 changesets found
1746 list of changesets:
1738 list of changesets:
1747 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1739 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1748 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1740 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1749 911600dab2ae7a9baff75958b84fe606851ce955
1741 911600dab2ae7a9baff75958b84fe606851ce955
1750 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1742 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1751 adding changesets
1743 adding changesets
1752 bundling: 1/4 changesets (25.00%)
1744 bundling: 1/4 changesets (25.00%)
1753 bundling: 2/4 changesets (50.00%)
1745 bundling: 2/4 changesets (50.00%)
1754 bundling: 3/4 changesets (75.00%)
1746 bundling: 3/4 changesets (75.00%)
1755 bundling: 4/4 changesets (100.00%)
1747 bundling: 4/4 changesets (100.00%)
1756 bundling: 1/4 manifests (25.00%)
1748 bundling: 1/4 manifests (25.00%)
1757 bundling: 2/4 manifests (50.00%)
1749 bundling: 2/4 manifests (50.00%)
1758 bundling: 3/4 manifests (75.00%)
1750 bundling: 3/4 manifests (75.00%)
1759 bundling: 4/4 manifests (100.00%)
1751 bundling: 4/4 manifests (100.00%)
1760 bundling: abc.txt 1/4 files (25.00%)
1752 bundling: abc.txt 1/4 files (25.00%)
1761 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1753 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1762 bundling: foo/file.txt 3/4 files (75.00%)
1754 bundling: foo/file.txt 3/4 files (75.00%)
1763 bundling: quux/file.py 4/4 files (100.00%)
1755 bundling: quux/file.py 4/4 files (100.00%)
1764 changesets: 1 chunks
1756 changesets: 1 chunks
1765 add changeset ef1ea85a6374
1757 add changeset ef1ea85a6374
1766 changesets: 2 chunks
1758 changesets: 2 chunks
1767 add changeset f9cafe1212c8
1759 add changeset f9cafe1212c8
1768 changesets: 3 chunks
1760 changesets: 3 chunks
1769 add changeset 911600dab2ae
1761 add changeset 911600dab2ae
1770 changesets: 4 chunks
1762 changesets: 4 chunks
1771 add changeset e8fc755d4d82
1763 add changeset e8fc755d4d82
1772 adding manifests
1764 adding manifests
1773 manifests: 1/4 chunks (25.00%)
1765 manifests: 1/4 chunks (25.00%)
1774 manifests: 2/4 chunks (50.00%)
1766 manifests: 2/4 chunks (50.00%)
1775 manifests: 3/4 chunks (75.00%)
1767 manifests: 3/4 chunks (75.00%)
1776 manifests: 4/4 chunks (100.00%)
1768 manifests: 4/4 chunks (100.00%)
1777 adding file changes
1769 adding file changes
1778 adding abc.txt revisions
1770 adding abc.txt revisions
1779 files: 1/4 chunks (25.00%)
1771 files: 1/4 chunks (25.00%)
1780 adding foo/Bar/file.txt revisions
1772 adding foo/Bar/file.txt revisions
1781 files: 2/4 chunks (50.00%)
1773 files: 2/4 chunks (50.00%)
1782 adding foo/file.txt revisions
1774 adding foo/file.txt revisions
1783 files: 3/4 chunks (75.00%)
1775 files: 3/4 chunks (75.00%)
1784 adding quux/file.py revisions
1776 adding quux/file.py revisions
1785 files: 4/4 chunks (100.00%)
1777 files: 4/4 chunks (100.00%)
1786 added 4 changesets with 4 changes to 4 files (+1 heads)
1778 added 4 changesets with 4 changes to 4 files (+1 heads)
1787 calling hook pretxnchangegroup.acl: hgext.acl.hook
1779 calling hook pretxnchangegroup.acl: hgext.acl.hook
1788 acl: checking access for user "george"
1780 acl: checking access for user "george"
1789 acl: acl.allow.branches enabled, 1 entries for user george
1781 acl: acl.allow.branches enabled, 1 entries for user george
1790 acl: acl.deny.branches not enabled
1782 acl: acl.deny.branches not enabled
1791 acl: acl.allow not enabled
1783 acl: acl.allow not enabled
1792 acl: acl.deny not enabled
1784 acl: acl.deny not enabled
1793 acl: branch access granted: "ef1ea85a6374" on branch "default"
1785 acl: branch access granted: "ef1ea85a6374" on branch "default"
1794 acl: path access granted: "ef1ea85a6374"
1786 acl: path access granted: "ef1ea85a6374"
1795 acl: branch access granted: "f9cafe1212c8" on branch "default"
1787 acl: branch access granted: "f9cafe1212c8" on branch "default"
1796 acl: path access granted: "f9cafe1212c8"
1788 acl: path access granted: "f9cafe1212c8"
1797 acl: branch access granted: "911600dab2ae" on branch "default"
1789 acl: branch access granted: "911600dab2ae" on branch "default"
1798 acl: path access granted: "911600dab2ae"
1790 acl: path access granted: "911600dab2ae"
1799 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1791 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1800 acl: path access granted: "e8fc755d4d82"
1792 acl: path access granted: "e8fc755d4d82"
1801 listing keys for "phases"
1793 listing keys for "phases"
1802 try to push obsolete markers to remote
1794 try to push obsolete markers to remote
1803 updating the branch cache
1795 updating the branch cache
1804 checking for updated bookmarks
1796 checking for updated bookmarks
1805 listing keys for "bookmarks"
1797 listing keys for "bookmarks"
1806 repository tip rolled back to revision 2 (undo push)
1798 repository tip rolled back to revision 2 (undo push)
1807 2:fb35475503ef
1799 2:fb35475503ef
1808
1800
1809
1801
1810 Branch acl conflicting allow
1802 Branch acl conflicting allow
1811 asterisk ends up applying to all branches and allowing george to
1803 asterisk ends up applying to all branches and allowing george to
1812 push foobar into the remote
1804 push foobar into the remote
1813
1805
1814 $ init_config
1806 $ init_config
1815 $ echo "[acl.allow.branches]" >> $config
1807 $ echo "[acl.allow.branches]" >> $config
1816 $ echo "foobar = astro" >> $config
1808 $ echo "foobar = astro" >> $config
1817 $ echo "* = george" >> $config
1809 $ echo "* = george" >> $config
1818 $ do_push george
1810 $ do_push george
1819 Pushing as user george
1811 Pushing as user george
1820 hgrc = """
1812 hgrc = """
1821 [acl]
1813 [acl]
1822 sources = push
1814 sources = push
1823 [extensions]
1815 [extensions]
1824 [acl.allow.branches]
1816 [acl.allow.branches]
1825 foobar = astro
1817 foobar = astro
1826 * = george
1818 * = george
1827 """
1819 """
1828 pushing to ../b
1820 pushing to ../b
1829 query 1; heads
1821 query 1; heads
1830 searching for changes
1822 searching for changes
1831 all remote heads known locally
1823 all remote heads known locally
1832 invalid branchheads cache: tip differs
1833 listing keys for "bookmarks"
1824 listing keys for "bookmarks"
1834 4 changesets found
1825 4 changesets found
1835 list of changesets:
1826 list of changesets:
1836 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1827 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1837 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1828 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1838 911600dab2ae7a9baff75958b84fe606851ce955
1829 911600dab2ae7a9baff75958b84fe606851ce955
1839 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1830 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1840 adding changesets
1831 adding changesets
1841 bundling: 1/4 changesets (25.00%)
1832 bundling: 1/4 changesets (25.00%)
1842 bundling: 2/4 changesets (50.00%)
1833 bundling: 2/4 changesets (50.00%)
1843 bundling: 3/4 changesets (75.00%)
1834 bundling: 3/4 changesets (75.00%)
1844 bundling: 4/4 changesets (100.00%)
1835 bundling: 4/4 changesets (100.00%)
1845 bundling: 1/4 manifests (25.00%)
1836 bundling: 1/4 manifests (25.00%)
1846 bundling: 2/4 manifests (50.00%)
1837 bundling: 2/4 manifests (50.00%)
1847 bundling: 3/4 manifests (75.00%)
1838 bundling: 3/4 manifests (75.00%)
1848 bundling: 4/4 manifests (100.00%)
1839 bundling: 4/4 manifests (100.00%)
1849 bundling: abc.txt 1/4 files (25.00%)
1840 bundling: abc.txt 1/4 files (25.00%)
1850 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1841 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1851 bundling: foo/file.txt 3/4 files (75.00%)
1842 bundling: foo/file.txt 3/4 files (75.00%)
1852 bundling: quux/file.py 4/4 files (100.00%)
1843 bundling: quux/file.py 4/4 files (100.00%)
1853 changesets: 1 chunks
1844 changesets: 1 chunks
1854 add changeset ef1ea85a6374
1845 add changeset ef1ea85a6374
1855 changesets: 2 chunks
1846 changesets: 2 chunks
1856 add changeset f9cafe1212c8
1847 add changeset f9cafe1212c8
1857 changesets: 3 chunks
1848 changesets: 3 chunks
1858 add changeset 911600dab2ae
1849 add changeset 911600dab2ae
1859 changesets: 4 chunks
1850 changesets: 4 chunks
1860 add changeset e8fc755d4d82
1851 add changeset e8fc755d4d82
1861 adding manifests
1852 adding manifests
1862 manifests: 1/4 chunks (25.00%)
1853 manifests: 1/4 chunks (25.00%)
1863 manifests: 2/4 chunks (50.00%)
1854 manifests: 2/4 chunks (50.00%)
1864 manifests: 3/4 chunks (75.00%)
1855 manifests: 3/4 chunks (75.00%)
1865 manifests: 4/4 chunks (100.00%)
1856 manifests: 4/4 chunks (100.00%)
1866 adding file changes
1857 adding file changes
1867 adding abc.txt revisions
1858 adding abc.txt revisions
1868 files: 1/4 chunks (25.00%)
1859 files: 1/4 chunks (25.00%)
1869 adding foo/Bar/file.txt revisions
1860 adding foo/Bar/file.txt revisions
1870 files: 2/4 chunks (50.00%)
1861 files: 2/4 chunks (50.00%)
1871 adding foo/file.txt revisions
1862 adding foo/file.txt revisions
1872 files: 3/4 chunks (75.00%)
1863 files: 3/4 chunks (75.00%)
1873 adding quux/file.py revisions
1864 adding quux/file.py revisions
1874 files: 4/4 chunks (100.00%)
1865 files: 4/4 chunks (100.00%)
1875 added 4 changesets with 4 changes to 4 files (+1 heads)
1866 added 4 changesets with 4 changes to 4 files (+1 heads)
1876 calling hook pretxnchangegroup.acl: hgext.acl.hook
1867 calling hook pretxnchangegroup.acl: hgext.acl.hook
1877 acl: checking access for user "george"
1868 acl: checking access for user "george"
1878 acl: acl.allow.branches enabled, 1 entries for user george
1869 acl: acl.allow.branches enabled, 1 entries for user george
1879 acl: acl.deny.branches not enabled
1870 acl: acl.deny.branches not enabled
1880 acl: acl.allow not enabled
1871 acl: acl.allow not enabled
1881 acl: acl.deny not enabled
1872 acl: acl.deny not enabled
1882 acl: branch access granted: "ef1ea85a6374" on branch "default"
1873 acl: branch access granted: "ef1ea85a6374" on branch "default"
1883 acl: path access granted: "ef1ea85a6374"
1874 acl: path access granted: "ef1ea85a6374"
1884 acl: branch access granted: "f9cafe1212c8" on branch "default"
1875 acl: branch access granted: "f9cafe1212c8" on branch "default"
1885 acl: path access granted: "f9cafe1212c8"
1876 acl: path access granted: "f9cafe1212c8"
1886 acl: branch access granted: "911600dab2ae" on branch "default"
1877 acl: branch access granted: "911600dab2ae" on branch "default"
1887 acl: path access granted: "911600dab2ae"
1878 acl: path access granted: "911600dab2ae"
1888 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1879 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1889 acl: path access granted: "e8fc755d4d82"
1880 acl: path access granted: "e8fc755d4d82"
1890 listing keys for "phases"
1881 listing keys for "phases"
1891 try to push obsolete markers to remote
1882 try to push obsolete markers to remote
1892 updating the branch cache
1883 updating the branch cache
1893 checking for updated bookmarks
1884 checking for updated bookmarks
1894 listing keys for "bookmarks"
1885 listing keys for "bookmarks"
1895 repository tip rolled back to revision 2 (undo push)
1886 repository tip rolled back to revision 2 (undo push)
1896 2:fb35475503ef
1887 2:fb35475503ef
1897
1888
1898 Branch acl conflicting deny
1889 Branch acl conflicting deny
1899
1890
1900 $ init_config
1891 $ init_config
1901 $ echo "[acl.deny.branches]" >> $config
1892 $ echo "[acl.deny.branches]" >> $config
1902 $ echo "foobar = astro" >> $config
1893 $ echo "foobar = astro" >> $config
1903 $ echo "default = astro" >> $config
1894 $ echo "default = astro" >> $config
1904 $ echo "* = george" >> $config
1895 $ echo "* = george" >> $config
1905 $ do_push george
1896 $ do_push george
1906 Pushing as user george
1897 Pushing as user george
1907 hgrc = """
1898 hgrc = """
1908 [acl]
1899 [acl]
1909 sources = push
1900 sources = push
1910 [extensions]
1901 [extensions]
1911 [acl.deny.branches]
1902 [acl.deny.branches]
1912 foobar = astro
1903 foobar = astro
1913 default = astro
1904 default = astro
1914 * = george
1905 * = george
1915 """
1906 """
1916 pushing to ../b
1907 pushing to ../b
1917 query 1; heads
1908 query 1; heads
1918 searching for changes
1909 searching for changes
1919 all remote heads known locally
1910 all remote heads known locally
1920 invalid branchheads cache: tip differs
1921 listing keys for "bookmarks"
1911 listing keys for "bookmarks"
1922 4 changesets found
1912 4 changesets found
1923 list of changesets:
1913 list of changesets:
1924 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1914 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1925 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1915 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1926 911600dab2ae7a9baff75958b84fe606851ce955
1916 911600dab2ae7a9baff75958b84fe606851ce955
1927 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1917 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1928 adding changesets
1918 adding changesets
1929 bundling: 1/4 changesets (25.00%)
1919 bundling: 1/4 changesets (25.00%)
1930 bundling: 2/4 changesets (50.00%)
1920 bundling: 2/4 changesets (50.00%)
1931 bundling: 3/4 changesets (75.00%)
1921 bundling: 3/4 changesets (75.00%)
1932 bundling: 4/4 changesets (100.00%)
1922 bundling: 4/4 changesets (100.00%)
1933 bundling: 1/4 manifests (25.00%)
1923 bundling: 1/4 manifests (25.00%)
1934 bundling: 2/4 manifests (50.00%)
1924 bundling: 2/4 manifests (50.00%)
1935 bundling: 3/4 manifests (75.00%)
1925 bundling: 3/4 manifests (75.00%)
1936 bundling: 4/4 manifests (100.00%)
1926 bundling: 4/4 manifests (100.00%)
1937 bundling: abc.txt 1/4 files (25.00%)
1927 bundling: abc.txt 1/4 files (25.00%)
1938 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1928 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1939 bundling: foo/file.txt 3/4 files (75.00%)
1929 bundling: foo/file.txt 3/4 files (75.00%)
1940 bundling: quux/file.py 4/4 files (100.00%)
1930 bundling: quux/file.py 4/4 files (100.00%)
1941 changesets: 1 chunks
1931 changesets: 1 chunks
1942 add changeset ef1ea85a6374
1932 add changeset ef1ea85a6374
1943 changesets: 2 chunks
1933 changesets: 2 chunks
1944 add changeset f9cafe1212c8
1934 add changeset f9cafe1212c8
1945 changesets: 3 chunks
1935 changesets: 3 chunks
1946 add changeset 911600dab2ae
1936 add changeset 911600dab2ae
1947 changesets: 4 chunks
1937 changesets: 4 chunks
1948 add changeset e8fc755d4d82
1938 add changeset e8fc755d4d82
1949 adding manifests
1939 adding manifests
1950 manifests: 1/4 chunks (25.00%)
1940 manifests: 1/4 chunks (25.00%)
1951 manifests: 2/4 chunks (50.00%)
1941 manifests: 2/4 chunks (50.00%)
1952 manifests: 3/4 chunks (75.00%)
1942 manifests: 3/4 chunks (75.00%)
1953 manifests: 4/4 chunks (100.00%)
1943 manifests: 4/4 chunks (100.00%)
1954 adding file changes
1944 adding file changes
1955 adding abc.txt revisions
1945 adding abc.txt revisions
1956 files: 1/4 chunks (25.00%)
1946 files: 1/4 chunks (25.00%)
1957 adding foo/Bar/file.txt revisions
1947 adding foo/Bar/file.txt revisions
1958 files: 2/4 chunks (50.00%)
1948 files: 2/4 chunks (50.00%)
1959 adding foo/file.txt revisions
1949 adding foo/file.txt revisions
1960 files: 3/4 chunks (75.00%)
1950 files: 3/4 chunks (75.00%)
1961 adding quux/file.py revisions
1951 adding quux/file.py revisions
1962 files: 4/4 chunks (100.00%)
1952 files: 4/4 chunks (100.00%)
1963 added 4 changesets with 4 changes to 4 files (+1 heads)
1953 added 4 changesets with 4 changes to 4 files (+1 heads)
1964 calling hook pretxnchangegroup.acl: hgext.acl.hook
1954 calling hook pretxnchangegroup.acl: hgext.acl.hook
1965 acl: checking access for user "george"
1955 acl: checking access for user "george"
1966 acl: acl.allow.branches not enabled
1956 acl: acl.allow.branches not enabled
1967 acl: acl.deny.branches enabled, 1 entries for user george
1957 acl: acl.deny.branches enabled, 1 entries for user george
1968 acl: acl.allow not enabled
1958 acl: acl.allow not enabled
1969 acl: acl.deny not enabled
1959 acl: acl.deny not enabled
1970 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1960 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1971 transaction abort!
1961 transaction abort!
1972 rollback completed
1962 rollback completed
1973 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1963 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1974 no rollback information available
1964 no rollback information available
1975 2:fb35475503ef
1965 2:fb35475503ef
1976
1966
1977 User 'astro' must not be denied
1967 User 'astro' must not be denied
1978
1968
1979 $ init_config
1969 $ init_config
1980 $ echo "[acl.deny.branches]" >> $config
1970 $ echo "[acl.deny.branches]" >> $config
1981 $ echo "default = !astro" >> $config
1971 $ echo "default = !astro" >> $config
1982 $ do_push astro
1972 $ do_push astro
1983 Pushing as user astro
1973 Pushing as user astro
1984 hgrc = """
1974 hgrc = """
1985 [acl]
1975 [acl]
1986 sources = push
1976 sources = push
1987 [extensions]
1977 [extensions]
1988 [acl.deny.branches]
1978 [acl.deny.branches]
1989 default = !astro
1979 default = !astro
1990 """
1980 """
1991 pushing to ../b
1981 pushing to ../b
1992 query 1; heads
1982 query 1; heads
1993 searching for changes
1983 searching for changes
1994 all remote heads known locally
1984 all remote heads known locally
1995 listing keys for "bookmarks"
1985 listing keys for "bookmarks"
1996 4 changesets found
1986 4 changesets found
1997 list of changesets:
1987 list of changesets:
1998 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1988 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1999 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1989 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2000 911600dab2ae7a9baff75958b84fe606851ce955
1990 911600dab2ae7a9baff75958b84fe606851ce955
2001 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1991 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2002 adding changesets
1992 adding changesets
2003 bundling: 1/4 changesets (25.00%)
1993 bundling: 1/4 changesets (25.00%)
2004 bundling: 2/4 changesets (50.00%)
1994 bundling: 2/4 changesets (50.00%)
2005 bundling: 3/4 changesets (75.00%)
1995 bundling: 3/4 changesets (75.00%)
2006 bundling: 4/4 changesets (100.00%)
1996 bundling: 4/4 changesets (100.00%)
2007 bundling: 1/4 manifests (25.00%)
1997 bundling: 1/4 manifests (25.00%)
2008 bundling: 2/4 manifests (50.00%)
1998 bundling: 2/4 manifests (50.00%)
2009 bundling: 3/4 manifests (75.00%)
1999 bundling: 3/4 manifests (75.00%)
2010 bundling: 4/4 manifests (100.00%)
2000 bundling: 4/4 manifests (100.00%)
2011 bundling: abc.txt 1/4 files (25.00%)
2001 bundling: abc.txt 1/4 files (25.00%)
2012 bundling: foo/Bar/file.txt 2/4 files (50.00%)
2002 bundling: foo/Bar/file.txt 2/4 files (50.00%)
2013 bundling: foo/file.txt 3/4 files (75.00%)
2003 bundling: foo/file.txt 3/4 files (75.00%)
2014 bundling: quux/file.py 4/4 files (100.00%)
2004 bundling: quux/file.py 4/4 files (100.00%)
2015 changesets: 1 chunks
2005 changesets: 1 chunks
2016 add changeset ef1ea85a6374
2006 add changeset ef1ea85a6374
2017 changesets: 2 chunks
2007 changesets: 2 chunks
2018 add changeset f9cafe1212c8
2008 add changeset f9cafe1212c8
2019 changesets: 3 chunks
2009 changesets: 3 chunks
2020 add changeset 911600dab2ae
2010 add changeset 911600dab2ae
2021 changesets: 4 chunks
2011 changesets: 4 chunks
2022 add changeset e8fc755d4d82
2012 add changeset e8fc755d4d82
2023 adding manifests
2013 adding manifests
2024 manifests: 1/4 chunks (25.00%)
2014 manifests: 1/4 chunks (25.00%)
2025 manifests: 2/4 chunks (50.00%)
2015 manifests: 2/4 chunks (50.00%)
2026 manifests: 3/4 chunks (75.00%)
2016 manifests: 3/4 chunks (75.00%)
2027 manifests: 4/4 chunks (100.00%)
2017 manifests: 4/4 chunks (100.00%)
2028 adding file changes
2018 adding file changes
2029 adding abc.txt revisions
2019 adding abc.txt revisions
2030 files: 1/4 chunks (25.00%)
2020 files: 1/4 chunks (25.00%)
2031 adding foo/Bar/file.txt revisions
2021 adding foo/Bar/file.txt revisions
2032 files: 2/4 chunks (50.00%)
2022 files: 2/4 chunks (50.00%)
2033 adding foo/file.txt revisions
2023 adding foo/file.txt revisions
2034 files: 3/4 chunks (75.00%)
2024 files: 3/4 chunks (75.00%)
2035 adding quux/file.py revisions
2025 adding quux/file.py revisions
2036 files: 4/4 chunks (100.00%)
2026 files: 4/4 chunks (100.00%)
2037 added 4 changesets with 4 changes to 4 files (+1 heads)
2027 added 4 changesets with 4 changes to 4 files (+1 heads)
2038 calling hook pretxnchangegroup.acl: hgext.acl.hook
2028 calling hook pretxnchangegroup.acl: hgext.acl.hook
2039 acl: checking access for user "astro"
2029 acl: checking access for user "astro"
2040 acl: acl.allow.branches not enabled
2030 acl: acl.allow.branches not enabled
2041 acl: acl.deny.branches enabled, 0 entries for user astro
2031 acl: acl.deny.branches enabled, 0 entries for user astro
2042 acl: acl.allow not enabled
2032 acl: acl.allow not enabled
2043 acl: acl.deny not enabled
2033 acl: acl.deny not enabled
2044 acl: branch access granted: "ef1ea85a6374" on branch "default"
2034 acl: branch access granted: "ef1ea85a6374" on branch "default"
2045 acl: path access granted: "ef1ea85a6374"
2035 acl: path access granted: "ef1ea85a6374"
2046 acl: branch access granted: "f9cafe1212c8" on branch "default"
2036 acl: branch access granted: "f9cafe1212c8" on branch "default"
2047 acl: path access granted: "f9cafe1212c8"
2037 acl: path access granted: "f9cafe1212c8"
2048 acl: branch access granted: "911600dab2ae" on branch "default"
2038 acl: branch access granted: "911600dab2ae" on branch "default"
2049 acl: path access granted: "911600dab2ae"
2039 acl: path access granted: "911600dab2ae"
2050 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
2040 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
2051 acl: path access granted: "e8fc755d4d82"
2041 acl: path access granted: "e8fc755d4d82"
2052 listing keys for "phases"
2042 listing keys for "phases"
2053 try to push obsolete markers to remote
2043 try to push obsolete markers to remote
2054 updating the branch cache
2044 updating the branch cache
2055 checking for updated bookmarks
2045 checking for updated bookmarks
2056 listing keys for "bookmarks"
2046 listing keys for "bookmarks"
2057 repository tip rolled back to revision 2 (undo push)
2047 repository tip rolled back to revision 2 (undo push)
2058 2:fb35475503ef
2048 2:fb35475503ef
2059
2049
2060
2050
2061 Non-astro users must be denied
2051 Non-astro users must be denied
2062
2052
2063 $ do_push george
2053 $ do_push george
2064 Pushing as user george
2054 Pushing as user george
2065 hgrc = """
2055 hgrc = """
2066 [acl]
2056 [acl]
2067 sources = push
2057 sources = push
2068 [extensions]
2058 [extensions]
2069 [acl.deny.branches]
2059 [acl.deny.branches]
2070 default = !astro
2060 default = !astro
2071 """
2061 """
2072 pushing to ../b
2062 pushing to ../b
2073 query 1; heads
2063 query 1; heads
2074 searching for changes
2064 searching for changes
2075 all remote heads known locally
2065 all remote heads known locally
2076 invalid branchheads cache: tip differs
2077 listing keys for "bookmarks"
2066 listing keys for "bookmarks"
2078 4 changesets found
2067 4 changesets found
2079 list of changesets:
2068 list of changesets:
2080 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2069 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2081 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2070 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2082 911600dab2ae7a9baff75958b84fe606851ce955
2071 911600dab2ae7a9baff75958b84fe606851ce955
2083 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2072 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2084 adding changesets
2073 adding changesets
2085 bundling: 1/4 changesets (25.00%)
2074 bundling: 1/4 changesets (25.00%)
2086 bundling: 2/4 changesets (50.00%)
2075 bundling: 2/4 changesets (50.00%)
2087 bundling: 3/4 changesets (75.00%)
2076 bundling: 3/4 changesets (75.00%)
2088 bundling: 4/4 changesets (100.00%)
2077 bundling: 4/4 changesets (100.00%)
2089 bundling: 1/4 manifests (25.00%)
2078 bundling: 1/4 manifests (25.00%)
2090 bundling: 2/4 manifests (50.00%)
2079 bundling: 2/4 manifests (50.00%)
2091 bundling: 3/4 manifests (75.00%)
2080 bundling: 3/4 manifests (75.00%)
2092 bundling: 4/4 manifests (100.00%)
2081 bundling: 4/4 manifests (100.00%)
2093 bundling: abc.txt 1/4 files (25.00%)
2082 bundling: abc.txt 1/4 files (25.00%)
2094 bundling: foo/Bar/file.txt 2/4 files (50.00%)
2083 bundling: foo/Bar/file.txt 2/4 files (50.00%)
2095 bundling: foo/file.txt 3/4 files (75.00%)
2084 bundling: foo/file.txt 3/4 files (75.00%)
2096 bundling: quux/file.py 4/4 files (100.00%)
2085 bundling: quux/file.py 4/4 files (100.00%)
2097 changesets: 1 chunks
2086 changesets: 1 chunks
2098 add changeset ef1ea85a6374
2087 add changeset ef1ea85a6374
2099 changesets: 2 chunks
2088 changesets: 2 chunks
2100 add changeset f9cafe1212c8
2089 add changeset f9cafe1212c8
2101 changesets: 3 chunks
2090 changesets: 3 chunks
2102 add changeset 911600dab2ae
2091 add changeset 911600dab2ae
2103 changesets: 4 chunks
2092 changesets: 4 chunks
2104 add changeset e8fc755d4d82
2093 add changeset e8fc755d4d82
2105 adding manifests
2094 adding manifests
2106 manifests: 1/4 chunks (25.00%)
2095 manifests: 1/4 chunks (25.00%)
2107 manifests: 2/4 chunks (50.00%)
2096 manifests: 2/4 chunks (50.00%)
2108 manifests: 3/4 chunks (75.00%)
2097 manifests: 3/4 chunks (75.00%)
2109 manifests: 4/4 chunks (100.00%)
2098 manifests: 4/4 chunks (100.00%)
2110 adding file changes
2099 adding file changes
2111 adding abc.txt revisions
2100 adding abc.txt revisions
2112 files: 1/4 chunks (25.00%)
2101 files: 1/4 chunks (25.00%)
2113 adding foo/Bar/file.txt revisions
2102 adding foo/Bar/file.txt revisions
2114 files: 2/4 chunks (50.00%)
2103 files: 2/4 chunks (50.00%)
2115 adding foo/file.txt revisions
2104 adding foo/file.txt revisions
2116 files: 3/4 chunks (75.00%)
2105 files: 3/4 chunks (75.00%)
2117 adding quux/file.py revisions
2106 adding quux/file.py revisions
2118 files: 4/4 chunks (100.00%)
2107 files: 4/4 chunks (100.00%)
2119 added 4 changesets with 4 changes to 4 files (+1 heads)
2108 added 4 changesets with 4 changes to 4 files (+1 heads)
2120 calling hook pretxnchangegroup.acl: hgext.acl.hook
2109 calling hook pretxnchangegroup.acl: hgext.acl.hook
2121 acl: checking access for user "george"
2110 acl: checking access for user "george"
2122 acl: acl.allow.branches not enabled
2111 acl: acl.allow.branches not enabled
2123 acl: acl.deny.branches enabled, 1 entries for user george
2112 acl: acl.deny.branches enabled, 1 entries for user george
2124 acl: acl.allow not enabled
2113 acl: acl.allow not enabled
2125 acl: acl.deny not enabled
2114 acl: acl.deny not enabled
2126 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2115 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2127 transaction abort!
2116 transaction abort!
2128 rollback completed
2117 rollback completed
2129 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2118 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2130 no rollback information available
2119 no rollback information available
2131 2:fb35475503ef
2120 2:fb35475503ef
2132
2121
2133
2122
General Comments 0
You need to be logged in to leave comments. Login now