##// END OF EJS Templates
pushkey: add more verbose debug output regarding pushkey...
Pierre-Yves David -
r17293:d3f84ccc stable
parent child Browse files
Show More
@@ -1,2586 +1,2588 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 bin, hex, nullid, nullrev, short
7 from node import bin, hex, nullid, nullrev, short
8 from i18n import _
8 from i18n import _
9 import peer, changegroup, subrepo, discovery, pushkey, obsolete
9 import peer, changegroup, subrepo, discovery, pushkey, obsolete
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 propertycache = util.propertycache
18 propertycache = util.propertycache
19 filecache = scmutil.filecache
19 filecache = scmutil.filecache
20
20
21 class storecache(filecache):
21 class storecache(filecache):
22 """filecache for files in the store"""
22 """filecache for files in the store"""
23 def join(self, obj, fname):
23 def join(self, obj, fname):
24 return obj.sjoin(fname)
24 return obj.sjoin(fname)
25
25
26 MODERNCAPS = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle'))
26 MODERNCAPS = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle'))
27 LEGACYCAPS = MODERNCAPS.union(set(['changegroupsubset']))
27 LEGACYCAPS = MODERNCAPS.union(set(['changegroupsubset']))
28
28
29 class localpeer(peer.peerrepository):
29 class localpeer(peer.peerrepository):
30 '''peer for a local repo; reflects only the most recent API'''
30 '''peer for a local repo; reflects only the most recent API'''
31
31
32 def __init__(self, repo, caps=MODERNCAPS):
32 def __init__(self, repo, caps=MODERNCAPS):
33 peer.peerrepository.__init__(self)
33 peer.peerrepository.__init__(self)
34 self._repo = repo
34 self._repo = repo
35 self.ui = repo.ui
35 self.ui = repo.ui
36 self._caps = repo._restrictcapabilities(caps)
36 self._caps = repo._restrictcapabilities(caps)
37 self.requirements = repo.requirements
37 self.requirements = repo.requirements
38 self.supportedformats = repo.supportedformats
38 self.supportedformats = repo.supportedformats
39
39
40 def close(self):
40 def close(self):
41 self._repo.close()
41 self._repo.close()
42
42
43 def _capabilities(self):
43 def _capabilities(self):
44 return self._caps
44 return self._caps
45
45
46 def local(self):
46 def local(self):
47 return self._repo
47 return self._repo
48
48
49 def canpush(self):
49 def canpush(self):
50 return True
50 return True
51
51
52 def url(self):
52 def url(self):
53 return self._repo.url()
53 return self._repo.url()
54
54
55 def lookup(self, key):
55 def lookup(self, key):
56 return self._repo.lookup(key)
56 return self._repo.lookup(key)
57
57
58 def branchmap(self):
58 def branchmap(self):
59 return discovery.visiblebranchmap(self._repo)
59 return discovery.visiblebranchmap(self._repo)
60
60
61 def heads(self):
61 def heads(self):
62 return discovery.visibleheads(self._repo)
62 return discovery.visibleheads(self._repo)
63
63
64 def known(self, nodes):
64 def known(self, nodes):
65 return self._repo.known(nodes)
65 return self._repo.known(nodes)
66
66
67 def getbundle(self, source, heads=None, common=None):
67 def getbundle(self, source, heads=None, common=None):
68 return self._repo.getbundle(source, heads=heads, common=common)
68 return self._repo.getbundle(source, heads=heads, common=common)
69
69
70 # TODO We might want to move the next two calls into legacypeer and add
70 # TODO We might want to move the next two calls into legacypeer and add
71 # unbundle instead.
71 # unbundle instead.
72
72
73 def lock(self):
73 def lock(self):
74 return self._repo.lock()
74 return self._repo.lock()
75
75
76 def addchangegroup(self, cg, source, url):
76 def addchangegroup(self, cg, source, url):
77 return self._repo.addchangegroup(cg, source, url)
77 return self._repo.addchangegroup(cg, source, url)
78
78
79 def pushkey(self, namespace, key, old, new):
79 def pushkey(self, namespace, key, old, new):
80 return self._repo.pushkey(namespace, key, old, new)
80 return self._repo.pushkey(namespace, key, old, new)
81
81
82 def listkeys(self, namespace):
82 def listkeys(self, namespace):
83 return self._repo.listkeys(namespace)
83 return self._repo.listkeys(namespace)
84
84
85 def debugwireargs(self, one, two, three=None, four=None, five=None):
85 def debugwireargs(self, one, two, three=None, four=None, five=None):
86 '''used to test argument passing over the wire'''
86 '''used to test argument passing over the wire'''
87 return "%s %s %s %s %s" % (one, two, three, four, five)
87 return "%s %s %s %s %s" % (one, two, three, four, five)
88
88
89 class locallegacypeer(localpeer):
89 class locallegacypeer(localpeer):
90 '''peer extension which implements legacy methods too; used for tests with
90 '''peer extension which implements legacy methods too; used for tests with
91 restricted capabilities'''
91 restricted capabilities'''
92
92
93 def __init__(self, repo):
93 def __init__(self, repo):
94 localpeer.__init__(self, repo, caps=LEGACYCAPS)
94 localpeer.__init__(self, repo, caps=LEGACYCAPS)
95
95
96 def branches(self, nodes):
96 def branches(self, nodes):
97 return self._repo.branches(nodes)
97 return self._repo.branches(nodes)
98
98
99 def between(self, pairs):
99 def between(self, pairs):
100 return self._repo.between(pairs)
100 return self._repo.between(pairs)
101
101
102 def changegroup(self, basenodes, source):
102 def changegroup(self, basenodes, source):
103 return self._repo.changegroup(basenodes, source)
103 return self._repo.changegroup(basenodes, source)
104
104
105 def changegroupsubset(self, bases, heads, source):
105 def changegroupsubset(self, bases, heads, source):
106 return self._repo.changegroupsubset(bases, heads, source)
106 return self._repo.changegroupsubset(bases, heads, source)
107
107
108 class localrepository(object):
108 class localrepository(object):
109
109
110 supportedformats = set(('revlogv1', 'generaldelta'))
110 supportedformats = set(('revlogv1', 'generaldelta'))
111 supported = supportedformats | set(('store', 'fncache', 'shared',
111 supported = supportedformats | set(('store', 'fncache', 'shared',
112 'dotencode'))
112 'dotencode'))
113 openerreqs = set(('revlogv1', 'generaldelta'))
113 openerreqs = set(('revlogv1', 'generaldelta'))
114 requirements = ['revlogv1']
114 requirements = ['revlogv1']
115
115
116 def _baserequirements(self, create):
116 def _baserequirements(self, create):
117 return self.requirements[:]
117 return self.requirements[:]
118
118
119 def __init__(self, baseui, path=None, create=False):
119 def __init__(self, baseui, path=None, create=False):
120 self.wopener = scmutil.opener(path, expand=True)
120 self.wopener = scmutil.opener(path, expand=True)
121 self.wvfs = self.wopener
121 self.wvfs = self.wopener
122 self.root = self.wvfs.base
122 self.root = self.wvfs.base
123 self.path = self.wvfs.join(".hg")
123 self.path = self.wvfs.join(".hg")
124 self.origroot = path
124 self.origroot = path
125 self.auditor = scmutil.pathauditor(self.root, self._checknested)
125 self.auditor = scmutil.pathauditor(self.root, self._checknested)
126 self.opener = scmutil.opener(self.path)
126 self.opener = scmutil.opener(self.path)
127 self.vfs = self.opener
127 self.vfs = self.opener
128 self.baseui = baseui
128 self.baseui = baseui
129 self.ui = baseui.copy()
129 self.ui = baseui.copy()
130 # A list of callback to shape the phase if no data were found.
130 # A list of callback to shape the phase if no data were found.
131 # Callback are in the form: func(repo, roots) --> processed root.
131 # Callback are in the form: func(repo, roots) --> processed root.
132 # This list it to be filled by extension during repo setup
132 # This list it to be filled by extension during repo setup
133 self._phasedefaults = []
133 self._phasedefaults = []
134 try:
134 try:
135 self.ui.readconfig(self.join("hgrc"), self.root)
135 self.ui.readconfig(self.join("hgrc"), self.root)
136 extensions.loadall(self.ui)
136 extensions.loadall(self.ui)
137 except IOError:
137 except IOError:
138 pass
138 pass
139
139
140 if not self.vfs.isdir():
140 if not self.vfs.isdir():
141 if create:
141 if create:
142 if not self.wvfs.exists():
142 if not self.wvfs.exists():
143 self.wvfs.makedirs()
143 self.wvfs.makedirs()
144 self.vfs.makedir(notindexed=True)
144 self.vfs.makedir(notindexed=True)
145 requirements = self._baserequirements(create)
145 requirements = self._baserequirements(create)
146 if self.ui.configbool('format', 'usestore', True):
146 if self.ui.configbool('format', 'usestore', True):
147 self.vfs.mkdir("store")
147 self.vfs.mkdir("store")
148 requirements.append("store")
148 requirements.append("store")
149 if self.ui.configbool('format', 'usefncache', True):
149 if self.ui.configbool('format', 'usefncache', True):
150 requirements.append("fncache")
150 requirements.append("fncache")
151 if self.ui.configbool('format', 'dotencode', True):
151 if self.ui.configbool('format', 'dotencode', True):
152 requirements.append('dotencode')
152 requirements.append('dotencode')
153 # create an invalid changelog
153 # create an invalid changelog
154 self.vfs.append(
154 self.vfs.append(
155 "00changelog.i",
155 "00changelog.i",
156 '\0\0\0\2' # represents revlogv2
156 '\0\0\0\2' # represents revlogv2
157 ' dummy changelog to prevent using the old repo layout'
157 ' dummy changelog to prevent using the old repo layout'
158 )
158 )
159 if self.ui.configbool('format', 'generaldelta', False):
159 if self.ui.configbool('format', 'generaldelta', False):
160 requirements.append("generaldelta")
160 requirements.append("generaldelta")
161 requirements = set(requirements)
161 requirements = set(requirements)
162 else:
162 else:
163 raise error.RepoError(_("repository %s not found") % path)
163 raise error.RepoError(_("repository %s not found") % path)
164 elif create:
164 elif create:
165 raise error.RepoError(_("repository %s already exists") % path)
165 raise error.RepoError(_("repository %s already exists") % path)
166 else:
166 else:
167 try:
167 try:
168 requirements = scmutil.readrequires(self.vfs, self.supported)
168 requirements = scmutil.readrequires(self.vfs, self.supported)
169 except IOError, inst:
169 except IOError, inst:
170 if inst.errno != errno.ENOENT:
170 if inst.errno != errno.ENOENT:
171 raise
171 raise
172 requirements = set()
172 requirements = set()
173
173
174 self.sharedpath = self.path
174 self.sharedpath = self.path
175 try:
175 try:
176 s = os.path.realpath(self.opener.read("sharedpath").rstrip('\n'))
176 s = os.path.realpath(self.opener.read("sharedpath").rstrip('\n'))
177 if not os.path.exists(s):
177 if not os.path.exists(s):
178 raise error.RepoError(
178 raise error.RepoError(
179 _('.hg/sharedpath points to nonexistent directory %s') % s)
179 _('.hg/sharedpath points to nonexistent directory %s') % s)
180 self.sharedpath = s
180 self.sharedpath = s
181 except IOError, inst:
181 except IOError, inst:
182 if inst.errno != errno.ENOENT:
182 if inst.errno != errno.ENOENT:
183 raise
183 raise
184
184
185 self.store = store.store(requirements, self.sharedpath, scmutil.opener)
185 self.store = store.store(requirements, self.sharedpath, scmutil.opener)
186 self.spath = self.store.path
186 self.spath = self.store.path
187 self.sopener = self.store.opener
187 self.sopener = self.store.opener
188 self.svfs = self.sopener
188 self.svfs = self.sopener
189 self.sjoin = self.store.join
189 self.sjoin = self.store.join
190 self.opener.createmode = self.store.createmode
190 self.opener.createmode = self.store.createmode
191 self._applyrequirements(requirements)
191 self._applyrequirements(requirements)
192 if create:
192 if create:
193 self._writerequirements()
193 self._writerequirements()
194
194
195
195
196 self._branchcache = None
196 self._branchcache = None
197 self._branchcachetip = None
197 self._branchcachetip = None
198 self.filterpats = {}
198 self.filterpats = {}
199 self._datafilters = {}
199 self._datafilters = {}
200 self._transref = self._lockref = self._wlockref = None
200 self._transref = self._lockref = self._wlockref = None
201
201
202 # A cache for various files under .hg/ that tracks file changes,
202 # A cache for various files under .hg/ that tracks file changes,
203 # (used by the filecache decorator)
203 # (used by the filecache decorator)
204 #
204 #
205 # Maps a property name to its util.filecacheentry
205 # Maps a property name to its util.filecacheentry
206 self._filecache = {}
206 self._filecache = {}
207
207
208 def close(self):
208 def close(self):
209 pass
209 pass
210
210
211 def _restrictcapabilities(self, caps):
211 def _restrictcapabilities(self, caps):
212 return caps
212 return caps
213
213
214 def _applyrequirements(self, requirements):
214 def _applyrequirements(self, requirements):
215 self.requirements = requirements
215 self.requirements = requirements
216 self.sopener.options = dict((r, 1) for r in requirements
216 self.sopener.options = dict((r, 1) for r in requirements
217 if r in self.openerreqs)
217 if r in self.openerreqs)
218
218
219 def _writerequirements(self):
219 def _writerequirements(self):
220 reqfile = self.opener("requires", "w")
220 reqfile = self.opener("requires", "w")
221 for r in self.requirements:
221 for r in self.requirements:
222 reqfile.write("%s\n" % r)
222 reqfile.write("%s\n" % r)
223 reqfile.close()
223 reqfile.close()
224
224
225 def _checknested(self, path):
225 def _checknested(self, path):
226 """Determine if path is a legal nested repository."""
226 """Determine if path is a legal nested repository."""
227 if not path.startswith(self.root):
227 if not path.startswith(self.root):
228 return False
228 return False
229 subpath = path[len(self.root) + 1:]
229 subpath = path[len(self.root) + 1:]
230 normsubpath = util.pconvert(subpath)
230 normsubpath = util.pconvert(subpath)
231
231
232 # XXX: Checking against the current working copy is wrong in
232 # XXX: Checking against the current working copy is wrong in
233 # the sense that it can reject things like
233 # the sense that it can reject things like
234 #
234 #
235 # $ hg cat -r 10 sub/x.txt
235 # $ hg cat -r 10 sub/x.txt
236 #
236 #
237 # if sub/ is no longer a subrepository in the working copy
237 # if sub/ is no longer a subrepository in the working copy
238 # parent revision.
238 # parent revision.
239 #
239 #
240 # However, it can of course also allow things that would have
240 # However, it can of course also allow things that would have
241 # been rejected before, such as the above cat command if sub/
241 # been rejected before, such as the above cat command if sub/
242 # is a subrepository now, but was a normal directory before.
242 # is a subrepository now, but was a normal directory before.
243 # The old path auditor would have rejected by mistake since it
243 # The old path auditor would have rejected by mistake since it
244 # panics when it sees sub/.hg/.
244 # panics when it sees sub/.hg/.
245 #
245 #
246 # All in all, checking against the working copy seems sensible
246 # All in all, checking against the working copy seems sensible
247 # since we want to prevent access to nested repositories on
247 # since we want to prevent access to nested repositories on
248 # the filesystem *now*.
248 # the filesystem *now*.
249 ctx = self[None]
249 ctx = self[None]
250 parts = util.splitpath(subpath)
250 parts = util.splitpath(subpath)
251 while parts:
251 while parts:
252 prefix = '/'.join(parts)
252 prefix = '/'.join(parts)
253 if prefix in ctx.substate:
253 if prefix in ctx.substate:
254 if prefix == normsubpath:
254 if prefix == normsubpath:
255 return True
255 return True
256 else:
256 else:
257 sub = ctx.sub(prefix)
257 sub = ctx.sub(prefix)
258 return sub.checknested(subpath[len(prefix) + 1:])
258 return sub.checknested(subpath[len(prefix) + 1:])
259 else:
259 else:
260 parts.pop()
260 parts.pop()
261 return False
261 return False
262
262
263 def peer(self):
263 def peer(self):
264 return localpeer(self) # not cached to avoid reference cycle
264 return localpeer(self) # not cached to avoid reference cycle
265
265
266 @filecache('bookmarks')
266 @filecache('bookmarks')
267 def _bookmarks(self):
267 def _bookmarks(self):
268 return bookmarks.read(self)
268 return bookmarks.read(self)
269
269
270 @filecache('bookmarks.current')
270 @filecache('bookmarks.current')
271 def _bookmarkcurrent(self):
271 def _bookmarkcurrent(self):
272 return bookmarks.readcurrent(self)
272 return bookmarks.readcurrent(self)
273
273
274 def _writebookmarks(self, marks):
274 def _writebookmarks(self, marks):
275 bookmarks.write(self)
275 bookmarks.write(self)
276
276
277 def bookmarkheads(self, bookmark):
277 def bookmarkheads(self, bookmark):
278 name = bookmark.split('@', 1)[0]
278 name = bookmark.split('@', 1)[0]
279 heads = []
279 heads = []
280 for mark, n in self._bookmarks.iteritems():
280 for mark, n in self._bookmarks.iteritems():
281 if mark.split('@', 1)[0] == name:
281 if mark.split('@', 1)[0] == name:
282 heads.append(n)
282 heads.append(n)
283 return heads
283 return heads
284
284
285 @storecache('phaseroots')
285 @storecache('phaseroots')
286 def _phasecache(self):
286 def _phasecache(self):
287 return phases.phasecache(self, self._phasedefaults)
287 return phases.phasecache(self, self._phasedefaults)
288
288
289 @storecache('obsstore')
289 @storecache('obsstore')
290 def obsstore(self):
290 def obsstore(self):
291 store = obsolete.obsstore(self.sopener)
291 store = obsolete.obsstore(self.sopener)
292 return store
292 return store
293
293
294 @propertycache
294 @propertycache
295 def hiddenrevs(self):
295 def hiddenrevs(self):
296 """hiddenrevs: revs that should be hidden by command and tools
296 """hiddenrevs: revs that should be hidden by command and tools
297
297
298 This set is carried on the repo to ease initialisation and lazy
298 This set is carried on the repo to ease initialisation and lazy
299 loading it'll probably move back to changelog for efficienty and
299 loading it'll probably move back to changelog for efficienty and
300 consistency reason
300 consistency reason
301
301
302 Note that the hiddenrevs will needs invalidations when
302 Note that the hiddenrevs will needs invalidations when
303 - a new changesets is added (possible unstable above extinct)
303 - a new changesets is added (possible unstable above extinct)
304 - a new obsolete marker is added (possible new extinct changeset)
304 - a new obsolete marker is added (possible new extinct changeset)
305 """
305 """
306 hidden = set()
306 hidden = set()
307 if self.obsstore:
307 if self.obsstore:
308 ### hide extinct changeset that are not accessible by any mean
308 ### hide extinct changeset that are not accessible by any mean
309 hiddenquery = 'extinct() - ::(. + bookmark() + tagged())'
309 hiddenquery = 'extinct() - ::(. + bookmark() + tagged())'
310 hidden.update(self.revs(hiddenquery))
310 hidden.update(self.revs(hiddenquery))
311 return hidden
311 return hidden
312
312
313 @storecache('00changelog.i')
313 @storecache('00changelog.i')
314 def changelog(self):
314 def changelog(self):
315 c = changelog.changelog(self.sopener)
315 c = changelog.changelog(self.sopener)
316 if 'HG_PENDING' in os.environ:
316 if 'HG_PENDING' in os.environ:
317 p = os.environ['HG_PENDING']
317 p = os.environ['HG_PENDING']
318 if p.startswith(self.root):
318 if p.startswith(self.root):
319 c.readpending('00changelog.i.a')
319 c.readpending('00changelog.i.a')
320 return c
320 return c
321
321
322 @storecache('00manifest.i')
322 @storecache('00manifest.i')
323 def manifest(self):
323 def manifest(self):
324 return manifest.manifest(self.sopener)
324 return manifest.manifest(self.sopener)
325
325
326 @filecache('dirstate')
326 @filecache('dirstate')
327 def dirstate(self):
327 def dirstate(self):
328 warned = [0]
328 warned = [0]
329 def validate(node):
329 def validate(node):
330 try:
330 try:
331 self.changelog.rev(node)
331 self.changelog.rev(node)
332 return node
332 return node
333 except error.LookupError:
333 except error.LookupError:
334 if not warned[0]:
334 if not warned[0]:
335 warned[0] = True
335 warned[0] = True
336 self.ui.warn(_("warning: ignoring unknown"
336 self.ui.warn(_("warning: ignoring unknown"
337 " working parent %s!\n") % short(node))
337 " working parent %s!\n") % short(node))
338 return nullid
338 return nullid
339
339
340 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
340 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
341
341
342 def __getitem__(self, changeid):
342 def __getitem__(self, changeid):
343 if changeid is None:
343 if changeid is None:
344 return context.workingctx(self)
344 return context.workingctx(self)
345 return context.changectx(self, changeid)
345 return context.changectx(self, changeid)
346
346
347 def __contains__(self, changeid):
347 def __contains__(self, changeid):
348 try:
348 try:
349 return bool(self.lookup(changeid))
349 return bool(self.lookup(changeid))
350 except error.RepoLookupError:
350 except error.RepoLookupError:
351 return False
351 return False
352
352
353 def __nonzero__(self):
353 def __nonzero__(self):
354 return True
354 return True
355
355
356 def __len__(self):
356 def __len__(self):
357 return len(self.changelog)
357 return len(self.changelog)
358
358
359 def __iter__(self):
359 def __iter__(self):
360 for i in xrange(len(self)):
360 for i in xrange(len(self)):
361 yield i
361 yield i
362
362
363 def revs(self, expr, *args):
363 def revs(self, expr, *args):
364 '''Return a list of revisions matching the given revset'''
364 '''Return a list of revisions matching the given revset'''
365 expr = revset.formatspec(expr, *args)
365 expr = revset.formatspec(expr, *args)
366 m = revset.match(None, expr)
366 m = revset.match(None, expr)
367 return [r for r in m(self, range(len(self)))]
367 return [r for r in m(self, range(len(self)))]
368
368
369 def set(self, expr, *args):
369 def set(self, expr, *args):
370 '''
370 '''
371 Yield a context for each matching revision, after doing arg
371 Yield a context for each matching revision, after doing arg
372 replacement via revset.formatspec
372 replacement via revset.formatspec
373 '''
373 '''
374 for r in self.revs(expr, *args):
374 for r in self.revs(expr, *args):
375 yield self[r]
375 yield self[r]
376
376
377 def url(self):
377 def url(self):
378 return 'file:' + self.root
378 return 'file:' + self.root
379
379
380 def hook(self, name, throw=False, **args):
380 def hook(self, name, throw=False, **args):
381 return hook.hook(self.ui, self, name, throw, **args)
381 return hook.hook(self.ui, self, name, throw, **args)
382
382
383 tag_disallowed = ':\r\n'
383 tag_disallowed = ':\r\n'
384
384
385 def _tag(self, names, node, message, local, user, date, extra={}):
385 def _tag(self, names, node, message, local, user, date, extra={}):
386 if isinstance(names, str):
386 if isinstance(names, str):
387 allchars = names
387 allchars = names
388 names = (names,)
388 names = (names,)
389 else:
389 else:
390 allchars = ''.join(names)
390 allchars = ''.join(names)
391 for c in self.tag_disallowed:
391 for c in self.tag_disallowed:
392 if c in allchars:
392 if c in allchars:
393 raise util.Abort(_('%r cannot be used in a tag name') % c)
393 raise util.Abort(_('%r cannot be used in a tag name') % c)
394
394
395 branches = self.branchmap()
395 branches = self.branchmap()
396 for name in names:
396 for name in names:
397 self.hook('pretag', throw=True, node=hex(node), tag=name,
397 self.hook('pretag', throw=True, node=hex(node), tag=name,
398 local=local)
398 local=local)
399 if name in branches:
399 if name in branches:
400 self.ui.warn(_("warning: tag %s conflicts with existing"
400 self.ui.warn(_("warning: tag %s conflicts with existing"
401 " branch name\n") % name)
401 " branch name\n") % name)
402
402
403 def writetags(fp, names, munge, prevtags):
403 def writetags(fp, names, munge, prevtags):
404 fp.seek(0, 2)
404 fp.seek(0, 2)
405 if prevtags and prevtags[-1] != '\n':
405 if prevtags and prevtags[-1] != '\n':
406 fp.write('\n')
406 fp.write('\n')
407 for name in names:
407 for name in names:
408 m = munge and munge(name) or name
408 m = munge and munge(name) or name
409 if (self._tagscache.tagtypes and
409 if (self._tagscache.tagtypes and
410 name in self._tagscache.tagtypes):
410 name in self._tagscache.tagtypes):
411 old = self.tags().get(name, nullid)
411 old = self.tags().get(name, nullid)
412 fp.write('%s %s\n' % (hex(old), m))
412 fp.write('%s %s\n' % (hex(old), m))
413 fp.write('%s %s\n' % (hex(node), m))
413 fp.write('%s %s\n' % (hex(node), m))
414 fp.close()
414 fp.close()
415
415
416 prevtags = ''
416 prevtags = ''
417 if local:
417 if local:
418 try:
418 try:
419 fp = self.opener('localtags', 'r+')
419 fp = self.opener('localtags', 'r+')
420 except IOError:
420 except IOError:
421 fp = self.opener('localtags', 'a')
421 fp = self.opener('localtags', 'a')
422 else:
422 else:
423 prevtags = fp.read()
423 prevtags = fp.read()
424
424
425 # local tags are stored in the current charset
425 # local tags are stored in the current charset
426 writetags(fp, names, None, prevtags)
426 writetags(fp, names, None, prevtags)
427 for name in names:
427 for name in names:
428 self.hook('tag', node=hex(node), tag=name, local=local)
428 self.hook('tag', node=hex(node), tag=name, local=local)
429 return
429 return
430
430
431 try:
431 try:
432 fp = self.wfile('.hgtags', 'rb+')
432 fp = self.wfile('.hgtags', 'rb+')
433 except IOError, e:
433 except IOError, e:
434 if e.errno != errno.ENOENT:
434 if e.errno != errno.ENOENT:
435 raise
435 raise
436 fp = self.wfile('.hgtags', 'ab')
436 fp = self.wfile('.hgtags', 'ab')
437 else:
437 else:
438 prevtags = fp.read()
438 prevtags = fp.read()
439
439
440 # committed tags are stored in UTF-8
440 # committed tags are stored in UTF-8
441 writetags(fp, names, encoding.fromlocal, prevtags)
441 writetags(fp, names, encoding.fromlocal, prevtags)
442
442
443 fp.close()
443 fp.close()
444
444
445 self.invalidatecaches()
445 self.invalidatecaches()
446
446
447 if '.hgtags' not in self.dirstate:
447 if '.hgtags' not in self.dirstate:
448 self[None].add(['.hgtags'])
448 self[None].add(['.hgtags'])
449
449
450 m = matchmod.exact(self.root, '', ['.hgtags'])
450 m = matchmod.exact(self.root, '', ['.hgtags'])
451 tagnode = self.commit(message, user, date, extra=extra, match=m)
451 tagnode = self.commit(message, user, date, extra=extra, match=m)
452
452
453 for name in names:
453 for name in names:
454 self.hook('tag', node=hex(node), tag=name, local=local)
454 self.hook('tag', node=hex(node), tag=name, local=local)
455
455
456 return tagnode
456 return tagnode
457
457
458 def tag(self, names, node, message, local, user, date):
458 def tag(self, names, node, message, local, user, date):
459 '''tag a revision with one or more symbolic names.
459 '''tag a revision with one or more symbolic names.
460
460
461 names is a list of strings or, when adding a single tag, names may be a
461 names is a list of strings or, when adding a single tag, names may be a
462 string.
462 string.
463
463
464 if local is True, the tags are stored in a per-repository file.
464 if local is True, the tags are stored in a per-repository file.
465 otherwise, they are stored in the .hgtags file, and a new
465 otherwise, they are stored in the .hgtags file, and a new
466 changeset is committed with the change.
466 changeset is committed with the change.
467
467
468 keyword arguments:
468 keyword arguments:
469
469
470 local: whether to store tags in non-version-controlled file
470 local: whether to store tags in non-version-controlled file
471 (default False)
471 (default False)
472
472
473 message: commit message to use if committing
473 message: commit message to use if committing
474
474
475 user: name of user to use if committing
475 user: name of user to use if committing
476
476
477 date: date tuple to use if committing'''
477 date: date tuple to use if committing'''
478
478
479 if not local:
479 if not local:
480 for x in self.status()[:5]:
480 for x in self.status()[:5]:
481 if '.hgtags' in x:
481 if '.hgtags' in x:
482 raise util.Abort(_('working copy of .hgtags is changed '
482 raise util.Abort(_('working copy of .hgtags is changed '
483 '(please commit .hgtags manually)'))
483 '(please commit .hgtags manually)'))
484
484
485 self.tags() # instantiate the cache
485 self.tags() # instantiate the cache
486 self._tag(names, node, message, local, user, date)
486 self._tag(names, node, message, local, user, date)
487
487
488 @propertycache
488 @propertycache
489 def _tagscache(self):
489 def _tagscache(self):
490 '''Returns a tagscache object that contains various tags related
490 '''Returns a tagscache object that contains various tags related
491 caches.'''
491 caches.'''
492
492
493 # This simplifies its cache management by having one decorated
493 # This simplifies its cache management by having one decorated
494 # function (this one) and the rest simply fetch things from it.
494 # function (this one) and the rest simply fetch things from it.
495 class tagscache(object):
495 class tagscache(object):
496 def __init__(self):
496 def __init__(self):
497 # These two define the set of tags for this repository. tags
497 # These two define the set of tags for this repository. tags
498 # maps tag name to node; tagtypes maps tag name to 'global' or
498 # maps tag name to node; tagtypes maps tag name to 'global' or
499 # 'local'. (Global tags are defined by .hgtags across all
499 # 'local'. (Global tags are defined by .hgtags across all
500 # heads, and local tags are defined in .hg/localtags.)
500 # heads, and local tags are defined in .hg/localtags.)
501 # They constitute the in-memory cache of tags.
501 # They constitute the in-memory cache of tags.
502 self.tags = self.tagtypes = None
502 self.tags = self.tagtypes = None
503
503
504 self.nodetagscache = self.tagslist = None
504 self.nodetagscache = self.tagslist = None
505
505
506 cache = tagscache()
506 cache = tagscache()
507 cache.tags, cache.tagtypes = self._findtags()
507 cache.tags, cache.tagtypes = self._findtags()
508
508
509 return cache
509 return cache
510
510
511 def tags(self):
511 def tags(self):
512 '''return a mapping of tag to node'''
512 '''return a mapping of tag to node'''
513 t = {}
513 t = {}
514 for k, v in self._tagscache.tags.iteritems():
514 for k, v in self._tagscache.tags.iteritems():
515 try:
515 try:
516 # ignore tags to unknown nodes
516 # ignore tags to unknown nodes
517 self.changelog.rev(v)
517 self.changelog.rev(v)
518 t[k] = v
518 t[k] = v
519 except (error.LookupError, ValueError):
519 except (error.LookupError, ValueError):
520 pass
520 pass
521 return t
521 return t
522
522
523 def _findtags(self):
523 def _findtags(self):
524 '''Do the hard work of finding tags. Return a pair of dicts
524 '''Do the hard work of finding tags. Return a pair of dicts
525 (tags, tagtypes) where tags maps tag name to node, and tagtypes
525 (tags, tagtypes) where tags maps tag name to node, and tagtypes
526 maps tag name to a string like \'global\' or \'local\'.
526 maps tag name to a string like \'global\' or \'local\'.
527 Subclasses or extensions are free to add their own tags, but
527 Subclasses or extensions are free to add their own tags, but
528 should be aware that the returned dicts will be retained for the
528 should be aware that the returned dicts will be retained for the
529 duration of the localrepo object.'''
529 duration of the localrepo object.'''
530
530
531 # XXX what tagtype should subclasses/extensions use? Currently
531 # XXX what tagtype should subclasses/extensions use? Currently
532 # mq and bookmarks add tags, but do not set the tagtype at all.
532 # mq and bookmarks add tags, but do not set the tagtype at all.
533 # Should each extension invent its own tag type? Should there
533 # Should each extension invent its own tag type? Should there
534 # be one tagtype for all such "virtual" tags? Or is the status
534 # be one tagtype for all such "virtual" tags? Or is the status
535 # quo fine?
535 # quo fine?
536
536
537 alltags = {} # map tag name to (node, hist)
537 alltags = {} # map tag name to (node, hist)
538 tagtypes = {}
538 tagtypes = {}
539
539
540 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
540 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
541 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
541 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
542
542
543 # Build the return dicts. Have to re-encode tag names because
543 # Build the return dicts. Have to re-encode tag names because
544 # the tags module always uses UTF-8 (in order not to lose info
544 # the tags module always uses UTF-8 (in order not to lose info
545 # writing to the cache), but the rest of Mercurial wants them in
545 # writing to the cache), but the rest of Mercurial wants them in
546 # local encoding.
546 # local encoding.
547 tags = {}
547 tags = {}
548 for (name, (node, hist)) in alltags.iteritems():
548 for (name, (node, hist)) in alltags.iteritems():
549 if node != nullid:
549 if node != nullid:
550 tags[encoding.tolocal(name)] = node
550 tags[encoding.tolocal(name)] = node
551 tags['tip'] = self.changelog.tip()
551 tags['tip'] = self.changelog.tip()
552 tagtypes = dict([(encoding.tolocal(name), value)
552 tagtypes = dict([(encoding.tolocal(name), value)
553 for (name, value) in tagtypes.iteritems()])
553 for (name, value) in tagtypes.iteritems()])
554 return (tags, tagtypes)
554 return (tags, tagtypes)
555
555
556 def tagtype(self, tagname):
556 def tagtype(self, tagname):
557 '''
557 '''
558 return the type of the given tag. result can be:
558 return the type of the given tag. result can be:
559
559
560 'local' : a local tag
560 'local' : a local tag
561 'global' : a global tag
561 'global' : a global tag
562 None : tag does not exist
562 None : tag does not exist
563 '''
563 '''
564
564
565 return self._tagscache.tagtypes.get(tagname)
565 return self._tagscache.tagtypes.get(tagname)
566
566
567 def tagslist(self):
567 def tagslist(self):
568 '''return a list of tags ordered by revision'''
568 '''return a list of tags ordered by revision'''
569 if not self._tagscache.tagslist:
569 if not self._tagscache.tagslist:
570 l = []
570 l = []
571 for t, n in self.tags().iteritems():
571 for t, n in self.tags().iteritems():
572 r = self.changelog.rev(n)
572 r = self.changelog.rev(n)
573 l.append((r, t, n))
573 l.append((r, t, n))
574 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
574 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
575
575
576 return self._tagscache.tagslist
576 return self._tagscache.tagslist
577
577
578 def nodetags(self, node):
578 def nodetags(self, node):
579 '''return the tags associated with a node'''
579 '''return the tags associated with a node'''
580 if not self._tagscache.nodetagscache:
580 if not self._tagscache.nodetagscache:
581 nodetagscache = {}
581 nodetagscache = {}
582 for t, n in self._tagscache.tags.iteritems():
582 for t, n in self._tagscache.tags.iteritems():
583 nodetagscache.setdefault(n, []).append(t)
583 nodetagscache.setdefault(n, []).append(t)
584 for tags in nodetagscache.itervalues():
584 for tags in nodetagscache.itervalues():
585 tags.sort()
585 tags.sort()
586 self._tagscache.nodetagscache = nodetagscache
586 self._tagscache.nodetagscache = nodetagscache
587 return self._tagscache.nodetagscache.get(node, [])
587 return self._tagscache.nodetagscache.get(node, [])
588
588
589 def nodebookmarks(self, node):
589 def nodebookmarks(self, node):
590 marks = []
590 marks = []
591 for bookmark, n in self._bookmarks.iteritems():
591 for bookmark, n in self._bookmarks.iteritems():
592 if n == node:
592 if n == node:
593 marks.append(bookmark)
593 marks.append(bookmark)
594 return sorted(marks)
594 return sorted(marks)
595
595
596 def _branchtags(self, partial, lrev):
596 def _branchtags(self, partial, lrev):
597 # TODO: rename this function?
597 # TODO: rename this function?
598 tiprev = len(self) - 1
598 tiprev = len(self) - 1
599 if lrev != tiprev:
599 if lrev != tiprev:
600 ctxgen = (self[r] for r in xrange(lrev + 1, tiprev + 1))
600 ctxgen = (self[r] for r in xrange(lrev + 1, tiprev + 1))
601 self._updatebranchcache(partial, ctxgen)
601 self._updatebranchcache(partial, ctxgen)
602 self._writebranchcache(partial, self.changelog.tip(), tiprev)
602 self._writebranchcache(partial, self.changelog.tip(), tiprev)
603
603
604 return partial
604 return partial
605
605
606 def updatebranchcache(self):
606 def updatebranchcache(self):
607 tip = self.changelog.tip()
607 tip = self.changelog.tip()
608 if self._branchcache is not None and self._branchcachetip == tip:
608 if self._branchcache is not None and self._branchcachetip == tip:
609 return
609 return
610
610
611 oldtip = self._branchcachetip
611 oldtip = self._branchcachetip
612 self._branchcachetip = tip
612 self._branchcachetip = tip
613 if oldtip is None or oldtip not in self.changelog.nodemap:
613 if oldtip is None or oldtip not in self.changelog.nodemap:
614 partial, last, lrev = self._readbranchcache()
614 partial, last, lrev = self._readbranchcache()
615 else:
615 else:
616 lrev = self.changelog.rev(oldtip)
616 lrev = self.changelog.rev(oldtip)
617 partial = self._branchcache
617 partial = self._branchcache
618
618
619 self._branchtags(partial, lrev)
619 self._branchtags(partial, lrev)
620 # this private cache holds all heads (not just the branch tips)
620 # this private cache holds all heads (not just the branch tips)
621 self._branchcache = partial
621 self._branchcache = partial
622
622
623 def branchmap(self):
623 def branchmap(self):
624 '''returns a dictionary {branch: [branchheads]}'''
624 '''returns a dictionary {branch: [branchheads]}'''
625 self.updatebranchcache()
625 self.updatebranchcache()
626 return self._branchcache
626 return self._branchcache
627
627
628 def _branchtip(self, heads):
628 def _branchtip(self, heads):
629 '''return the tipmost branch head in heads'''
629 '''return the tipmost branch head in heads'''
630 tip = heads[-1]
630 tip = heads[-1]
631 for h in reversed(heads):
631 for h in reversed(heads):
632 if not self[h].closesbranch():
632 if not self[h].closesbranch():
633 tip = h
633 tip = h
634 break
634 break
635 return tip
635 return tip
636
636
637 def branchtip(self, branch):
637 def branchtip(self, branch):
638 '''return the tip node for a given branch'''
638 '''return the tip node for a given branch'''
639 if branch not in self.branchmap():
639 if branch not in self.branchmap():
640 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
640 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
641 return self._branchtip(self.branchmap()[branch])
641 return self._branchtip(self.branchmap()[branch])
642
642
643 def branchtags(self):
643 def branchtags(self):
644 '''return a dict where branch names map to the tipmost head of
644 '''return a dict where branch names map to the tipmost head of
645 the branch, open heads come before closed'''
645 the branch, open heads come before closed'''
646 bt = {}
646 bt = {}
647 for bn, heads in self.branchmap().iteritems():
647 for bn, heads in self.branchmap().iteritems():
648 bt[bn] = self._branchtip(heads)
648 bt[bn] = self._branchtip(heads)
649 return bt
649 return bt
650
650
651 def _readbranchcache(self):
651 def _readbranchcache(self):
652 partial = {}
652 partial = {}
653 try:
653 try:
654 f = self.opener("cache/branchheads")
654 f = self.opener("cache/branchheads")
655 lines = f.read().split('\n')
655 lines = f.read().split('\n')
656 f.close()
656 f.close()
657 except (IOError, OSError):
657 except (IOError, OSError):
658 return {}, nullid, nullrev
658 return {}, nullid, nullrev
659
659
660 try:
660 try:
661 last, lrev = lines.pop(0).split(" ", 1)
661 last, lrev = lines.pop(0).split(" ", 1)
662 last, lrev = bin(last), int(lrev)
662 last, lrev = bin(last), int(lrev)
663 if lrev >= len(self) or self[lrev].node() != last:
663 if lrev >= len(self) or self[lrev].node() != last:
664 # invalidate the cache
664 # invalidate the cache
665 raise ValueError('invalidating branch cache (tip differs)')
665 raise ValueError('invalidating branch cache (tip differs)')
666 for l in lines:
666 for l in lines:
667 if not l:
667 if not l:
668 continue
668 continue
669 node, label = l.split(" ", 1)
669 node, label = l.split(" ", 1)
670 label = encoding.tolocal(label.strip())
670 label = encoding.tolocal(label.strip())
671 if not node in self:
671 if not node in self:
672 raise ValueError('invalidating branch cache because node '+
672 raise ValueError('invalidating branch cache because node '+
673 '%s does not exist' % node)
673 '%s does not exist' % node)
674 partial.setdefault(label, []).append(bin(node))
674 partial.setdefault(label, []).append(bin(node))
675 except KeyboardInterrupt:
675 except KeyboardInterrupt:
676 raise
676 raise
677 except Exception, inst:
677 except Exception, inst:
678 if self.ui.debugflag:
678 if self.ui.debugflag:
679 self.ui.warn(str(inst), '\n')
679 self.ui.warn(str(inst), '\n')
680 partial, last, lrev = {}, nullid, nullrev
680 partial, last, lrev = {}, nullid, nullrev
681 return partial, last, lrev
681 return partial, last, lrev
682
682
683 def _writebranchcache(self, branches, tip, tiprev):
683 def _writebranchcache(self, branches, tip, tiprev):
684 try:
684 try:
685 f = self.opener("cache/branchheads", "w", atomictemp=True)
685 f = self.opener("cache/branchheads", "w", atomictemp=True)
686 f.write("%s %s\n" % (hex(tip), tiprev))
686 f.write("%s %s\n" % (hex(tip), tiprev))
687 for label, nodes in branches.iteritems():
687 for label, nodes in branches.iteritems():
688 for node in nodes:
688 for node in nodes:
689 f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
689 f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
690 f.close()
690 f.close()
691 except (IOError, OSError):
691 except (IOError, OSError):
692 pass
692 pass
693
693
694 def _updatebranchcache(self, partial, ctxgen):
694 def _updatebranchcache(self, partial, ctxgen):
695 """Given a branchhead cache, partial, that may have extra nodes or be
695 """Given a branchhead cache, partial, that may have extra nodes or be
696 missing heads, and a generator of nodes that are at least a superset of
696 missing heads, and a generator of nodes that are at least a superset of
697 heads missing, this function updates partial to be correct.
697 heads missing, this function updates partial to be correct.
698 """
698 """
699 # collect new branch entries
699 # collect new branch entries
700 newbranches = {}
700 newbranches = {}
701 for c in ctxgen:
701 for c in ctxgen:
702 newbranches.setdefault(c.branch(), []).append(c.node())
702 newbranches.setdefault(c.branch(), []).append(c.node())
703 # if older branchheads are reachable from new ones, they aren't
703 # if older branchheads are reachable from new ones, they aren't
704 # really branchheads. Note checking parents is insufficient:
704 # really branchheads. Note checking parents is insufficient:
705 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
705 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
706 for branch, newnodes in newbranches.iteritems():
706 for branch, newnodes in newbranches.iteritems():
707 bheads = partial.setdefault(branch, [])
707 bheads = partial.setdefault(branch, [])
708 # Remove candidate heads that no longer are in the repo (e.g., as
708 # Remove candidate heads that no longer are in the repo (e.g., as
709 # the result of a strip that just happened). Avoid using 'node in
709 # the result of a strip that just happened). Avoid using 'node in
710 # self' here because that dives down into branchcache code somewhat
710 # self' here because that dives down into branchcache code somewhat
711 # recrusively.
711 # recrusively.
712 bheadrevs = [self.changelog.rev(node) for node in bheads
712 bheadrevs = [self.changelog.rev(node) for node in bheads
713 if self.changelog.hasnode(node)]
713 if self.changelog.hasnode(node)]
714 newheadrevs = [self.changelog.rev(node) for node in newnodes
714 newheadrevs = [self.changelog.rev(node) for node in newnodes
715 if self.changelog.hasnode(node)]
715 if self.changelog.hasnode(node)]
716 ctxisnew = bheadrevs and min(newheadrevs) > max(bheadrevs)
716 ctxisnew = bheadrevs and min(newheadrevs) > max(bheadrevs)
717 # Remove duplicates - nodes that are in newheadrevs and are already
717 # Remove duplicates - nodes that are in newheadrevs and are already
718 # in bheadrevs. This can happen if you strip a node whose parent
718 # in bheadrevs. This can happen if you strip a node whose parent
719 # was already a head (because they're on different branches).
719 # was already a head (because they're on different branches).
720 bheadrevs = sorted(set(bheadrevs).union(newheadrevs))
720 bheadrevs = sorted(set(bheadrevs).union(newheadrevs))
721
721
722 # Starting from tip means fewer passes over reachable. If we know
722 # Starting from tip means fewer passes over reachable. If we know
723 # the new candidates are not ancestors of existing heads, we don't
723 # the new candidates are not ancestors of existing heads, we don't
724 # have to examine ancestors of existing heads
724 # have to examine ancestors of existing heads
725 if ctxisnew:
725 if ctxisnew:
726 iterrevs = sorted(newheadrevs)
726 iterrevs = sorted(newheadrevs)
727 else:
727 else:
728 iterrevs = list(bheadrevs)
728 iterrevs = list(bheadrevs)
729
729
730 # This loop prunes out two kinds of heads - heads that are
730 # This loop prunes out two kinds of heads - heads that are
731 # superceded by a head in newheadrevs, and newheadrevs that are not
731 # superceded by a head in newheadrevs, and newheadrevs that are not
732 # heads because an existing head is their descendant.
732 # heads because an existing head is their descendant.
733 while iterrevs:
733 while iterrevs:
734 latest = iterrevs.pop()
734 latest = iterrevs.pop()
735 if latest not in bheadrevs:
735 if latest not in bheadrevs:
736 continue
736 continue
737 ancestors = set(self.changelog.ancestors([latest],
737 ancestors = set(self.changelog.ancestors([latest],
738 bheadrevs[0]))
738 bheadrevs[0]))
739 if ancestors:
739 if ancestors:
740 bheadrevs = [b for b in bheadrevs if b not in ancestors]
740 bheadrevs = [b for b in bheadrevs if b not in ancestors]
741 partial[branch] = [self.changelog.node(rev) for rev in bheadrevs]
741 partial[branch] = [self.changelog.node(rev) for rev in bheadrevs]
742
742
743 # There may be branches that cease to exist when the last commit in the
743 # There may be branches that cease to exist when the last commit in the
744 # branch was stripped. This code filters them out. Note that the
744 # branch was stripped. This code filters them out. Note that the
745 # branch that ceased to exist may not be in newbranches because
745 # branch that ceased to exist may not be in newbranches because
746 # newbranches is the set of candidate heads, which when you strip the
746 # newbranches is the set of candidate heads, which when you strip the
747 # last commit in a branch will be the parent branch.
747 # last commit in a branch will be the parent branch.
748 for branch in partial.keys():
748 for branch in partial.keys():
749 nodes = [head for head in partial[branch]
749 nodes = [head for head in partial[branch]
750 if self.changelog.hasnode(head)]
750 if self.changelog.hasnode(head)]
751 if not nodes:
751 if not nodes:
752 del partial[branch]
752 del partial[branch]
753
753
754 def lookup(self, key):
754 def lookup(self, key):
755 return self[key].node()
755 return self[key].node()
756
756
757 def lookupbranch(self, key, remote=None):
757 def lookupbranch(self, key, remote=None):
758 repo = remote or self
758 repo = remote or self
759 if key in repo.branchmap():
759 if key in repo.branchmap():
760 return key
760 return key
761
761
762 repo = (remote and remote.local()) and remote or self
762 repo = (remote and remote.local()) and remote or self
763 return repo[key].branch()
763 return repo[key].branch()
764
764
765 def known(self, nodes):
765 def known(self, nodes):
766 nm = self.changelog.nodemap
766 nm = self.changelog.nodemap
767 pc = self._phasecache
767 pc = self._phasecache
768 result = []
768 result = []
769 for n in nodes:
769 for n in nodes:
770 r = nm.get(n)
770 r = nm.get(n)
771 resp = not (r is None or pc.phase(self, r) >= phases.secret)
771 resp = not (r is None or pc.phase(self, r) >= phases.secret)
772 result.append(resp)
772 result.append(resp)
773 return result
773 return result
774
774
775 def local(self):
775 def local(self):
776 return self
776 return self
777
777
778 def cancopy(self):
778 def cancopy(self):
779 return self.local() # so statichttprepo's override of local() works
779 return self.local() # so statichttprepo's override of local() works
780
780
781 def join(self, f):
781 def join(self, f):
782 return os.path.join(self.path, f)
782 return os.path.join(self.path, f)
783
783
784 def wjoin(self, f):
784 def wjoin(self, f):
785 return os.path.join(self.root, f)
785 return os.path.join(self.root, f)
786
786
787 def file(self, f):
787 def file(self, f):
788 if f[0] == '/':
788 if f[0] == '/':
789 f = f[1:]
789 f = f[1:]
790 return filelog.filelog(self.sopener, f)
790 return filelog.filelog(self.sopener, f)
791
791
792 def changectx(self, changeid):
792 def changectx(self, changeid):
793 return self[changeid]
793 return self[changeid]
794
794
795 def parents(self, changeid=None):
795 def parents(self, changeid=None):
796 '''get list of changectxs for parents of changeid'''
796 '''get list of changectxs for parents of changeid'''
797 return self[changeid].parents()
797 return self[changeid].parents()
798
798
799 def setparents(self, p1, p2=nullid):
799 def setparents(self, p1, p2=nullid):
800 copies = self.dirstate.setparents(p1, p2)
800 copies = self.dirstate.setparents(p1, p2)
801 if copies:
801 if copies:
802 # Adjust copy records, the dirstate cannot do it, it
802 # Adjust copy records, the dirstate cannot do it, it
803 # requires access to parents manifests. Preserve them
803 # requires access to parents manifests. Preserve them
804 # only for entries added to first parent.
804 # only for entries added to first parent.
805 pctx = self[p1]
805 pctx = self[p1]
806 for f in copies:
806 for f in copies:
807 if f not in pctx and copies[f] in pctx:
807 if f not in pctx and copies[f] in pctx:
808 self.dirstate.copy(copies[f], f)
808 self.dirstate.copy(copies[f], f)
809
809
810 def filectx(self, path, changeid=None, fileid=None):
810 def filectx(self, path, changeid=None, fileid=None):
811 """changeid can be a changeset revision, node, or tag.
811 """changeid can be a changeset revision, node, or tag.
812 fileid can be a file revision or node."""
812 fileid can be a file revision or node."""
813 return context.filectx(self, path, changeid, fileid)
813 return context.filectx(self, path, changeid, fileid)
814
814
815 def getcwd(self):
815 def getcwd(self):
816 return self.dirstate.getcwd()
816 return self.dirstate.getcwd()
817
817
818 def pathto(self, f, cwd=None):
818 def pathto(self, f, cwd=None):
819 return self.dirstate.pathto(f, cwd)
819 return self.dirstate.pathto(f, cwd)
820
820
821 def wfile(self, f, mode='r'):
821 def wfile(self, f, mode='r'):
822 return self.wopener(f, mode)
822 return self.wopener(f, mode)
823
823
824 def _link(self, f):
824 def _link(self, f):
825 return os.path.islink(self.wjoin(f))
825 return os.path.islink(self.wjoin(f))
826
826
827 def _loadfilter(self, filter):
827 def _loadfilter(self, filter):
828 if filter not in self.filterpats:
828 if filter not in self.filterpats:
829 l = []
829 l = []
830 for pat, cmd in self.ui.configitems(filter):
830 for pat, cmd in self.ui.configitems(filter):
831 if cmd == '!':
831 if cmd == '!':
832 continue
832 continue
833 mf = matchmod.match(self.root, '', [pat])
833 mf = matchmod.match(self.root, '', [pat])
834 fn = None
834 fn = None
835 params = cmd
835 params = cmd
836 for name, filterfn in self._datafilters.iteritems():
836 for name, filterfn in self._datafilters.iteritems():
837 if cmd.startswith(name):
837 if cmd.startswith(name):
838 fn = filterfn
838 fn = filterfn
839 params = cmd[len(name):].lstrip()
839 params = cmd[len(name):].lstrip()
840 break
840 break
841 if not fn:
841 if not fn:
842 fn = lambda s, c, **kwargs: util.filter(s, c)
842 fn = lambda s, c, **kwargs: util.filter(s, c)
843 # Wrap old filters not supporting keyword arguments
843 # Wrap old filters not supporting keyword arguments
844 if not inspect.getargspec(fn)[2]:
844 if not inspect.getargspec(fn)[2]:
845 oldfn = fn
845 oldfn = fn
846 fn = lambda s, c, **kwargs: oldfn(s, c)
846 fn = lambda s, c, **kwargs: oldfn(s, c)
847 l.append((mf, fn, params))
847 l.append((mf, fn, params))
848 self.filterpats[filter] = l
848 self.filterpats[filter] = l
849 return self.filterpats[filter]
849 return self.filterpats[filter]
850
850
851 def _filter(self, filterpats, filename, data):
851 def _filter(self, filterpats, filename, data):
852 for mf, fn, cmd in filterpats:
852 for mf, fn, cmd in filterpats:
853 if mf(filename):
853 if mf(filename):
854 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
854 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
855 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
855 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
856 break
856 break
857
857
858 return data
858 return data
859
859
860 @propertycache
860 @propertycache
861 def _encodefilterpats(self):
861 def _encodefilterpats(self):
862 return self._loadfilter('encode')
862 return self._loadfilter('encode')
863
863
864 @propertycache
864 @propertycache
865 def _decodefilterpats(self):
865 def _decodefilterpats(self):
866 return self._loadfilter('decode')
866 return self._loadfilter('decode')
867
867
868 def adddatafilter(self, name, filter):
868 def adddatafilter(self, name, filter):
869 self._datafilters[name] = filter
869 self._datafilters[name] = filter
870
870
871 def wread(self, filename):
871 def wread(self, filename):
872 if self._link(filename):
872 if self._link(filename):
873 data = os.readlink(self.wjoin(filename))
873 data = os.readlink(self.wjoin(filename))
874 else:
874 else:
875 data = self.wopener.read(filename)
875 data = self.wopener.read(filename)
876 return self._filter(self._encodefilterpats, filename, data)
876 return self._filter(self._encodefilterpats, filename, data)
877
877
878 def wwrite(self, filename, data, flags):
878 def wwrite(self, filename, data, flags):
879 data = self._filter(self._decodefilterpats, filename, data)
879 data = self._filter(self._decodefilterpats, filename, data)
880 if 'l' in flags:
880 if 'l' in flags:
881 self.wopener.symlink(data, filename)
881 self.wopener.symlink(data, filename)
882 else:
882 else:
883 self.wopener.write(filename, data)
883 self.wopener.write(filename, data)
884 if 'x' in flags:
884 if 'x' in flags:
885 util.setflags(self.wjoin(filename), False, True)
885 util.setflags(self.wjoin(filename), False, True)
886
886
887 def wwritedata(self, filename, data):
887 def wwritedata(self, filename, data):
888 return self._filter(self._decodefilterpats, filename, data)
888 return self._filter(self._decodefilterpats, filename, data)
889
889
890 def transaction(self, desc):
890 def transaction(self, desc):
891 tr = self._transref and self._transref() or None
891 tr = self._transref and self._transref() or None
892 if tr and tr.running():
892 if tr and tr.running():
893 return tr.nest()
893 return tr.nest()
894
894
895 # abort here if the journal already exists
895 # abort here if the journal already exists
896 if os.path.exists(self.sjoin("journal")):
896 if os.path.exists(self.sjoin("journal")):
897 raise error.RepoError(
897 raise error.RepoError(
898 _("abandoned transaction found - run hg recover"))
898 _("abandoned transaction found - run hg recover"))
899
899
900 self._writejournal(desc)
900 self._writejournal(desc)
901 renames = [(x, undoname(x)) for x in self._journalfiles()]
901 renames = [(x, undoname(x)) for x in self._journalfiles()]
902
902
903 tr = transaction.transaction(self.ui.warn, self.sopener,
903 tr = transaction.transaction(self.ui.warn, self.sopener,
904 self.sjoin("journal"),
904 self.sjoin("journal"),
905 aftertrans(renames),
905 aftertrans(renames),
906 self.store.createmode)
906 self.store.createmode)
907 self._transref = weakref.ref(tr)
907 self._transref = weakref.ref(tr)
908 return tr
908 return tr
909
909
910 def _journalfiles(self):
910 def _journalfiles(self):
911 return (self.sjoin('journal'), self.join('journal.dirstate'),
911 return (self.sjoin('journal'), self.join('journal.dirstate'),
912 self.join('journal.branch'), self.join('journal.desc'),
912 self.join('journal.branch'), self.join('journal.desc'),
913 self.join('journal.bookmarks'),
913 self.join('journal.bookmarks'),
914 self.sjoin('journal.phaseroots'))
914 self.sjoin('journal.phaseroots'))
915
915
916 def undofiles(self):
916 def undofiles(self):
917 return [undoname(x) for x in self._journalfiles()]
917 return [undoname(x) for x in self._journalfiles()]
918
918
919 def _writejournal(self, desc):
919 def _writejournal(self, desc):
920 self.opener.write("journal.dirstate",
920 self.opener.write("journal.dirstate",
921 self.opener.tryread("dirstate"))
921 self.opener.tryread("dirstate"))
922 self.opener.write("journal.branch",
922 self.opener.write("journal.branch",
923 encoding.fromlocal(self.dirstate.branch()))
923 encoding.fromlocal(self.dirstate.branch()))
924 self.opener.write("journal.desc",
924 self.opener.write("journal.desc",
925 "%d\n%s\n" % (len(self), desc))
925 "%d\n%s\n" % (len(self), desc))
926 self.opener.write("journal.bookmarks",
926 self.opener.write("journal.bookmarks",
927 self.opener.tryread("bookmarks"))
927 self.opener.tryread("bookmarks"))
928 self.sopener.write("journal.phaseroots",
928 self.sopener.write("journal.phaseroots",
929 self.sopener.tryread("phaseroots"))
929 self.sopener.tryread("phaseroots"))
930
930
931 def recover(self):
931 def recover(self):
932 lock = self.lock()
932 lock = self.lock()
933 try:
933 try:
934 if os.path.exists(self.sjoin("journal")):
934 if os.path.exists(self.sjoin("journal")):
935 self.ui.status(_("rolling back interrupted transaction\n"))
935 self.ui.status(_("rolling back interrupted transaction\n"))
936 transaction.rollback(self.sopener, self.sjoin("journal"),
936 transaction.rollback(self.sopener, self.sjoin("journal"),
937 self.ui.warn)
937 self.ui.warn)
938 self.invalidate()
938 self.invalidate()
939 return True
939 return True
940 else:
940 else:
941 self.ui.warn(_("no interrupted transaction available\n"))
941 self.ui.warn(_("no interrupted transaction available\n"))
942 return False
942 return False
943 finally:
943 finally:
944 lock.release()
944 lock.release()
945
945
946 def rollback(self, dryrun=False, force=False):
946 def rollback(self, dryrun=False, force=False):
947 wlock = lock = None
947 wlock = lock = None
948 try:
948 try:
949 wlock = self.wlock()
949 wlock = self.wlock()
950 lock = self.lock()
950 lock = self.lock()
951 if os.path.exists(self.sjoin("undo")):
951 if os.path.exists(self.sjoin("undo")):
952 return self._rollback(dryrun, force)
952 return self._rollback(dryrun, force)
953 else:
953 else:
954 self.ui.warn(_("no rollback information available\n"))
954 self.ui.warn(_("no rollback information available\n"))
955 return 1
955 return 1
956 finally:
956 finally:
957 release(lock, wlock)
957 release(lock, wlock)
958
958
959 def _rollback(self, dryrun, force):
959 def _rollback(self, dryrun, force):
960 ui = self.ui
960 ui = self.ui
961 try:
961 try:
962 args = self.opener.read('undo.desc').splitlines()
962 args = self.opener.read('undo.desc').splitlines()
963 (oldlen, desc, detail) = (int(args[0]), args[1], None)
963 (oldlen, desc, detail) = (int(args[0]), args[1], None)
964 if len(args) >= 3:
964 if len(args) >= 3:
965 detail = args[2]
965 detail = args[2]
966 oldtip = oldlen - 1
966 oldtip = oldlen - 1
967
967
968 if detail and ui.verbose:
968 if detail and ui.verbose:
969 msg = (_('repository tip rolled back to revision %s'
969 msg = (_('repository tip rolled back to revision %s'
970 ' (undo %s: %s)\n')
970 ' (undo %s: %s)\n')
971 % (oldtip, desc, detail))
971 % (oldtip, desc, detail))
972 else:
972 else:
973 msg = (_('repository tip rolled back to revision %s'
973 msg = (_('repository tip rolled back to revision %s'
974 ' (undo %s)\n')
974 ' (undo %s)\n')
975 % (oldtip, desc))
975 % (oldtip, desc))
976 except IOError:
976 except IOError:
977 msg = _('rolling back unknown transaction\n')
977 msg = _('rolling back unknown transaction\n')
978 desc = None
978 desc = None
979
979
980 if not force and self['.'] != self['tip'] and desc == 'commit':
980 if not force and self['.'] != self['tip'] and desc == 'commit':
981 raise util.Abort(
981 raise util.Abort(
982 _('rollback of last commit while not checked out '
982 _('rollback of last commit while not checked out '
983 'may lose data'), hint=_('use -f to force'))
983 'may lose data'), hint=_('use -f to force'))
984
984
985 ui.status(msg)
985 ui.status(msg)
986 if dryrun:
986 if dryrun:
987 return 0
987 return 0
988
988
989 parents = self.dirstate.parents()
989 parents = self.dirstate.parents()
990 transaction.rollback(self.sopener, self.sjoin('undo'), ui.warn)
990 transaction.rollback(self.sopener, self.sjoin('undo'), ui.warn)
991 if os.path.exists(self.join('undo.bookmarks')):
991 if os.path.exists(self.join('undo.bookmarks')):
992 util.rename(self.join('undo.bookmarks'),
992 util.rename(self.join('undo.bookmarks'),
993 self.join('bookmarks'))
993 self.join('bookmarks'))
994 if os.path.exists(self.sjoin('undo.phaseroots')):
994 if os.path.exists(self.sjoin('undo.phaseroots')):
995 util.rename(self.sjoin('undo.phaseroots'),
995 util.rename(self.sjoin('undo.phaseroots'),
996 self.sjoin('phaseroots'))
996 self.sjoin('phaseroots'))
997 self.invalidate()
997 self.invalidate()
998
998
999 parentgone = (parents[0] not in self.changelog.nodemap or
999 parentgone = (parents[0] not in self.changelog.nodemap or
1000 parents[1] not in self.changelog.nodemap)
1000 parents[1] not in self.changelog.nodemap)
1001 if parentgone:
1001 if parentgone:
1002 util.rename(self.join('undo.dirstate'), self.join('dirstate'))
1002 util.rename(self.join('undo.dirstate'), self.join('dirstate'))
1003 try:
1003 try:
1004 branch = self.opener.read('undo.branch')
1004 branch = self.opener.read('undo.branch')
1005 self.dirstate.setbranch(branch)
1005 self.dirstate.setbranch(branch)
1006 except IOError:
1006 except IOError:
1007 ui.warn(_('named branch could not be reset: '
1007 ui.warn(_('named branch could not be reset: '
1008 'current branch is still \'%s\'\n')
1008 'current branch is still \'%s\'\n')
1009 % self.dirstate.branch())
1009 % self.dirstate.branch())
1010
1010
1011 self.dirstate.invalidate()
1011 self.dirstate.invalidate()
1012 parents = tuple([p.rev() for p in self.parents()])
1012 parents = tuple([p.rev() for p in self.parents()])
1013 if len(parents) > 1:
1013 if len(parents) > 1:
1014 ui.status(_('working directory now based on '
1014 ui.status(_('working directory now based on '
1015 'revisions %d and %d\n') % parents)
1015 'revisions %d and %d\n') % parents)
1016 else:
1016 else:
1017 ui.status(_('working directory now based on '
1017 ui.status(_('working directory now based on '
1018 'revision %d\n') % parents)
1018 'revision %d\n') % parents)
1019 # TODO: if we know which new heads may result from this rollback, pass
1019 # TODO: if we know which new heads may result from this rollback, pass
1020 # them to destroy(), which will prevent the branchhead cache from being
1020 # them to destroy(), which will prevent the branchhead cache from being
1021 # invalidated.
1021 # invalidated.
1022 self.destroyed()
1022 self.destroyed()
1023 return 0
1023 return 0
1024
1024
1025 def invalidatecaches(self):
1025 def invalidatecaches(self):
1026 def delcache(name):
1026 def delcache(name):
1027 try:
1027 try:
1028 delattr(self, name)
1028 delattr(self, name)
1029 except AttributeError:
1029 except AttributeError:
1030 pass
1030 pass
1031
1031
1032 delcache('_tagscache')
1032 delcache('_tagscache')
1033
1033
1034 self._branchcache = None # in UTF-8
1034 self._branchcache = None # in UTF-8
1035 self._branchcachetip = None
1035 self._branchcachetip = None
1036
1036
1037 def invalidatedirstate(self):
1037 def invalidatedirstate(self):
1038 '''Invalidates the dirstate, causing the next call to dirstate
1038 '''Invalidates the dirstate, causing the next call to dirstate
1039 to check if it was modified since the last time it was read,
1039 to check if it was modified since the last time it was read,
1040 rereading it if it has.
1040 rereading it if it has.
1041
1041
1042 This is different to dirstate.invalidate() that it doesn't always
1042 This is different to dirstate.invalidate() that it doesn't always
1043 rereads the dirstate. Use dirstate.invalidate() if you want to
1043 rereads the dirstate. Use dirstate.invalidate() if you want to
1044 explicitly read the dirstate again (i.e. restoring it to a previous
1044 explicitly read the dirstate again (i.e. restoring it to a previous
1045 known good state).'''
1045 known good state).'''
1046 if 'dirstate' in self.__dict__:
1046 if 'dirstate' in self.__dict__:
1047 for k in self.dirstate._filecache:
1047 for k in self.dirstate._filecache:
1048 try:
1048 try:
1049 delattr(self.dirstate, k)
1049 delattr(self.dirstate, k)
1050 except AttributeError:
1050 except AttributeError:
1051 pass
1051 pass
1052 delattr(self, 'dirstate')
1052 delattr(self, 'dirstate')
1053
1053
1054 def invalidate(self):
1054 def invalidate(self):
1055 for k in self._filecache:
1055 for k in self._filecache:
1056 # dirstate is invalidated separately in invalidatedirstate()
1056 # dirstate is invalidated separately in invalidatedirstate()
1057 if k == 'dirstate':
1057 if k == 'dirstate':
1058 continue
1058 continue
1059
1059
1060 try:
1060 try:
1061 delattr(self, k)
1061 delattr(self, k)
1062 except AttributeError:
1062 except AttributeError:
1063 pass
1063 pass
1064 self.invalidatecaches()
1064 self.invalidatecaches()
1065
1065
1066 # Discard all cache entries to force reloading everything.
1066 # Discard all cache entries to force reloading everything.
1067 self._filecache.clear()
1067 self._filecache.clear()
1068
1068
1069 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
1069 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
1070 try:
1070 try:
1071 l = lock.lock(lockname, 0, releasefn, desc=desc)
1071 l = lock.lock(lockname, 0, releasefn, desc=desc)
1072 except error.LockHeld, inst:
1072 except error.LockHeld, inst:
1073 if not wait:
1073 if not wait:
1074 raise
1074 raise
1075 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1075 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1076 (desc, inst.locker))
1076 (desc, inst.locker))
1077 # default to 600 seconds timeout
1077 # default to 600 seconds timeout
1078 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
1078 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
1079 releasefn, desc=desc)
1079 releasefn, desc=desc)
1080 if acquirefn:
1080 if acquirefn:
1081 acquirefn()
1081 acquirefn()
1082 return l
1082 return l
1083
1083
1084 def _afterlock(self, callback):
1084 def _afterlock(self, callback):
1085 """add a callback to the current repository lock.
1085 """add a callback to the current repository lock.
1086
1086
1087 The callback will be executed on lock release."""
1087 The callback will be executed on lock release."""
1088 l = self._lockref and self._lockref()
1088 l = self._lockref and self._lockref()
1089 if l:
1089 if l:
1090 l.postrelease.append(callback)
1090 l.postrelease.append(callback)
1091 else:
1091 else:
1092 callback()
1092 callback()
1093
1093
1094 def lock(self, wait=True):
1094 def lock(self, wait=True):
1095 '''Lock the repository store (.hg/store) and return a weak reference
1095 '''Lock the repository store (.hg/store) and return a weak reference
1096 to the lock. Use this before modifying the store (e.g. committing or
1096 to the lock. Use this before modifying the store (e.g. committing or
1097 stripping). If you are opening a transaction, get a lock as well.)'''
1097 stripping). If you are opening a transaction, get a lock as well.)'''
1098 l = self._lockref and self._lockref()
1098 l = self._lockref and self._lockref()
1099 if l is not None and l.held:
1099 if l is not None and l.held:
1100 l.lock()
1100 l.lock()
1101 return l
1101 return l
1102
1102
1103 def unlock():
1103 def unlock():
1104 self.store.write()
1104 self.store.write()
1105 if '_phasecache' in vars(self):
1105 if '_phasecache' in vars(self):
1106 self._phasecache.write()
1106 self._phasecache.write()
1107 for k, ce in self._filecache.items():
1107 for k, ce in self._filecache.items():
1108 if k == 'dirstate':
1108 if k == 'dirstate':
1109 continue
1109 continue
1110 ce.refresh()
1110 ce.refresh()
1111
1111
1112 l = self._lock(self.sjoin("lock"), wait, unlock,
1112 l = self._lock(self.sjoin("lock"), wait, unlock,
1113 self.invalidate, _('repository %s') % self.origroot)
1113 self.invalidate, _('repository %s') % self.origroot)
1114 self._lockref = weakref.ref(l)
1114 self._lockref = weakref.ref(l)
1115 return l
1115 return l
1116
1116
1117 def wlock(self, wait=True):
1117 def wlock(self, wait=True):
1118 '''Lock the non-store parts of the repository (everything under
1118 '''Lock the non-store parts of the repository (everything under
1119 .hg except .hg/store) and return a weak reference to the lock.
1119 .hg except .hg/store) and return a weak reference to the lock.
1120 Use this before modifying files in .hg.'''
1120 Use this before modifying files in .hg.'''
1121 l = self._wlockref and self._wlockref()
1121 l = self._wlockref and self._wlockref()
1122 if l is not None and l.held:
1122 if l is not None and l.held:
1123 l.lock()
1123 l.lock()
1124 return l
1124 return l
1125
1125
1126 def unlock():
1126 def unlock():
1127 self.dirstate.write()
1127 self.dirstate.write()
1128 ce = self._filecache.get('dirstate')
1128 ce = self._filecache.get('dirstate')
1129 if ce:
1129 if ce:
1130 ce.refresh()
1130 ce.refresh()
1131
1131
1132 l = self._lock(self.join("wlock"), wait, unlock,
1132 l = self._lock(self.join("wlock"), wait, unlock,
1133 self.invalidatedirstate, _('working directory of %s') %
1133 self.invalidatedirstate, _('working directory of %s') %
1134 self.origroot)
1134 self.origroot)
1135 self._wlockref = weakref.ref(l)
1135 self._wlockref = weakref.ref(l)
1136 return l
1136 return l
1137
1137
1138 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1138 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1139 """
1139 """
1140 commit an individual file as part of a larger transaction
1140 commit an individual file as part of a larger transaction
1141 """
1141 """
1142
1142
1143 fname = fctx.path()
1143 fname = fctx.path()
1144 text = fctx.data()
1144 text = fctx.data()
1145 flog = self.file(fname)
1145 flog = self.file(fname)
1146 fparent1 = manifest1.get(fname, nullid)
1146 fparent1 = manifest1.get(fname, nullid)
1147 fparent2 = fparent2o = manifest2.get(fname, nullid)
1147 fparent2 = fparent2o = manifest2.get(fname, nullid)
1148
1148
1149 meta = {}
1149 meta = {}
1150 copy = fctx.renamed()
1150 copy = fctx.renamed()
1151 if copy and copy[0] != fname:
1151 if copy and copy[0] != fname:
1152 # Mark the new revision of this file as a copy of another
1152 # Mark the new revision of this file as a copy of another
1153 # file. This copy data will effectively act as a parent
1153 # file. This copy data will effectively act as a parent
1154 # of this new revision. If this is a merge, the first
1154 # of this new revision. If this is a merge, the first
1155 # parent will be the nullid (meaning "look up the copy data")
1155 # parent will be the nullid (meaning "look up the copy data")
1156 # and the second one will be the other parent. For example:
1156 # and the second one will be the other parent. For example:
1157 #
1157 #
1158 # 0 --- 1 --- 3 rev1 changes file foo
1158 # 0 --- 1 --- 3 rev1 changes file foo
1159 # \ / rev2 renames foo to bar and changes it
1159 # \ / rev2 renames foo to bar and changes it
1160 # \- 2 -/ rev3 should have bar with all changes and
1160 # \- 2 -/ rev3 should have bar with all changes and
1161 # should record that bar descends from
1161 # should record that bar descends from
1162 # bar in rev2 and foo in rev1
1162 # bar in rev2 and foo in rev1
1163 #
1163 #
1164 # this allows this merge to succeed:
1164 # this allows this merge to succeed:
1165 #
1165 #
1166 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1166 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1167 # \ / merging rev3 and rev4 should use bar@rev2
1167 # \ / merging rev3 and rev4 should use bar@rev2
1168 # \- 2 --- 4 as the merge base
1168 # \- 2 --- 4 as the merge base
1169 #
1169 #
1170
1170
1171 cfname = copy[0]
1171 cfname = copy[0]
1172 crev = manifest1.get(cfname)
1172 crev = manifest1.get(cfname)
1173 newfparent = fparent2
1173 newfparent = fparent2
1174
1174
1175 if manifest2: # branch merge
1175 if manifest2: # branch merge
1176 if fparent2 == nullid or crev is None: # copied on remote side
1176 if fparent2 == nullid or crev is None: # copied on remote side
1177 if cfname in manifest2:
1177 if cfname in manifest2:
1178 crev = manifest2[cfname]
1178 crev = manifest2[cfname]
1179 newfparent = fparent1
1179 newfparent = fparent1
1180
1180
1181 # find source in nearest ancestor if we've lost track
1181 # find source in nearest ancestor if we've lost track
1182 if not crev:
1182 if not crev:
1183 self.ui.debug(" %s: searching for copy revision for %s\n" %
1183 self.ui.debug(" %s: searching for copy revision for %s\n" %
1184 (fname, cfname))
1184 (fname, cfname))
1185 for ancestor in self[None].ancestors():
1185 for ancestor in self[None].ancestors():
1186 if cfname in ancestor:
1186 if cfname in ancestor:
1187 crev = ancestor[cfname].filenode()
1187 crev = ancestor[cfname].filenode()
1188 break
1188 break
1189
1189
1190 if crev:
1190 if crev:
1191 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1191 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1192 meta["copy"] = cfname
1192 meta["copy"] = cfname
1193 meta["copyrev"] = hex(crev)
1193 meta["copyrev"] = hex(crev)
1194 fparent1, fparent2 = nullid, newfparent
1194 fparent1, fparent2 = nullid, newfparent
1195 else:
1195 else:
1196 self.ui.warn(_("warning: can't find ancestor for '%s' "
1196 self.ui.warn(_("warning: can't find ancestor for '%s' "
1197 "copied from '%s'!\n") % (fname, cfname))
1197 "copied from '%s'!\n") % (fname, cfname))
1198
1198
1199 elif fparent2 != nullid:
1199 elif fparent2 != nullid:
1200 # is one parent an ancestor of the other?
1200 # is one parent an ancestor of the other?
1201 fparentancestor = flog.ancestor(fparent1, fparent2)
1201 fparentancestor = flog.ancestor(fparent1, fparent2)
1202 if fparentancestor == fparent1:
1202 if fparentancestor == fparent1:
1203 fparent1, fparent2 = fparent2, nullid
1203 fparent1, fparent2 = fparent2, nullid
1204 elif fparentancestor == fparent2:
1204 elif fparentancestor == fparent2:
1205 fparent2 = nullid
1205 fparent2 = nullid
1206
1206
1207 # is the file changed?
1207 # is the file changed?
1208 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1208 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1209 changelist.append(fname)
1209 changelist.append(fname)
1210 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1210 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1211
1211
1212 # are just the flags changed during merge?
1212 # are just the flags changed during merge?
1213 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
1213 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
1214 changelist.append(fname)
1214 changelist.append(fname)
1215
1215
1216 return fparent1
1216 return fparent1
1217
1217
1218 def commit(self, text="", user=None, date=None, match=None, force=False,
1218 def commit(self, text="", user=None, date=None, match=None, force=False,
1219 editor=False, extra={}):
1219 editor=False, extra={}):
1220 """Add a new revision to current repository.
1220 """Add a new revision to current repository.
1221
1221
1222 Revision information is gathered from the working directory,
1222 Revision information is gathered from the working directory,
1223 match can be used to filter the committed files. If editor is
1223 match can be used to filter the committed files. If editor is
1224 supplied, it is called to get a commit message.
1224 supplied, it is called to get a commit message.
1225 """
1225 """
1226
1226
1227 def fail(f, msg):
1227 def fail(f, msg):
1228 raise util.Abort('%s: %s' % (f, msg))
1228 raise util.Abort('%s: %s' % (f, msg))
1229
1229
1230 if not match:
1230 if not match:
1231 match = matchmod.always(self.root, '')
1231 match = matchmod.always(self.root, '')
1232
1232
1233 if not force:
1233 if not force:
1234 vdirs = []
1234 vdirs = []
1235 match.dir = vdirs.append
1235 match.dir = vdirs.append
1236 match.bad = fail
1236 match.bad = fail
1237
1237
1238 wlock = self.wlock()
1238 wlock = self.wlock()
1239 try:
1239 try:
1240 wctx = self[None]
1240 wctx = self[None]
1241 merge = len(wctx.parents()) > 1
1241 merge = len(wctx.parents()) > 1
1242
1242
1243 if (not force and merge and match and
1243 if (not force and merge and match and
1244 (match.files() or match.anypats())):
1244 (match.files() or match.anypats())):
1245 raise util.Abort(_('cannot partially commit a merge '
1245 raise util.Abort(_('cannot partially commit a merge '
1246 '(do not specify files or patterns)'))
1246 '(do not specify files or patterns)'))
1247
1247
1248 changes = self.status(match=match, clean=force)
1248 changes = self.status(match=match, clean=force)
1249 if force:
1249 if force:
1250 changes[0].extend(changes[6]) # mq may commit unchanged files
1250 changes[0].extend(changes[6]) # mq may commit unchanged files
1251
1251
1252 # check subrepos
1252 # check subrepos
1253 subs = []
1253 subs = []
1254 commitsubs = set()
1254 commitsubs = set()
1255 newstate = wctx.substate.copy()
1255 newstate = wctx.substate.copy()
1256 # only manage subrepos and .hgsubstate if .hgsub is present
1256 # only manage subrepos and .hgsubstate if .hgsub is present
1257 if '.hgsub' in wctx:
1257 if '.hgsub' in wctx:
1258 # we'll decide whether to track this ourselves, thanks
1258 # we'll decide whether to track this ourselves, thanks
1259 if '.hgsubstate' in changes[0]:
1259 if '.hgsubstate' in changes[0]:
1260 changes[0].remove('.hgsubstate')
1260 changes[0].remove('.hgsubstate')
1261 if '.hgsubstate' in changes[2]:
1261 if '.hgsubstate' in changes[2]:
1262 changes[2].remove('.hgsubstate')
1262 changes[2].remove('.hgsubstate')
1263
1263
1264 # compare current state to last committed state
1264 # compare current state to last committed state
1265 # build new substate based on last committed state
1265 # build new substate based on last committed state
1266 oldstate = wctx.p1().substate
1266 oldstate = wctx.p1().substate
1267 for s in sorted(newstate.keys()):
1267 for s in sorted(newstate.keys()):
1268 if not match(s):
1268 if not match(s):
1269 # ignore working copy, use old state if present
1269 # ignore working copy, use old state if present
1270 if s in oldstate:
1270 if s in oldstate:
1271 newstate[s] = oldstate[s]
1271 newstate[s] = oldstate[s]
1272 continue
1272 continue
1273 if not force:
1273 if not force:
1274 raise util.Abort(
1274 raise util.Abort(
1275 _("commit with new subrepo %s excluded") % s)
1275 _("commit with new subrepo %s excluded") % s)
1276 if wctx.sub(s).dirty(True):
1276 if wctx.sub(s).dirty(True):
1277 if not self.ui.configbool('ui', 'commitsubrepos'):
1277 if not self.ui.configbool('ui', 'commitsubrepos'):
1278 raise util.Abort(
1278 raise util.Abort(
1279 _("uncommitted changes in subrepo %s") % s,
1279 _("uncommitted changes in subrepo %s") % s,
1280 hint=_("use --subrepos for recursive commit"))
1280 hint=_("use --subrepos for recursive commit"))
1281 subs.append(s)
1281 subs.append(s)
1282 commitsubs.add(s)
1282 commitsubs.add(s)
1283 else:
1283 else:
1284 bs = wctx.sub(s).basestate()
1284 bs = wctx.sub(s).basestate()
1285 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1285 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1286 if oldstate.get(s, (None, None, None))[1] != bs:
1286 if oldstate.get(s, (None, None, None))[1] != bs:
1287 subs.append(s)
1287 subs.append(s)
1288
1288
1289 # check for removed subrepos
1289 # check for removed subrepos
1290 for p in wctx.parents():
1290 for p in wctx.parents():
1291 r = [s for s in p.substate if s not in newstate]
1291 r = [s for s in p.substate if s not in newstate]
1292 subs += [s for s in r if match(s)]
1292 subs += [s for s in r if match(s)]
1293 if subs:
1293 if subs:
1294 if (not match('.hgsub') and
1294 if (not match('.hgsub') and
1295 '.hgsub' in (wctx.modified() + wctx.added())):
1295 '.hgsub' in (wctx.modified() + wctx.added())):
1296 raise util.Abort(
1296 raise util.Abort(
1297 _("can't commit subrepos without .hgsub"))
1297 _("can't commit subrepos without .hgsub"))
1298 changes[0].insert(0, '.hgsubstate')
1298 changes[0].insert(0, '.hgsubstate')
1299
1299
1300 elif '.hgsub' in changes[2]:
1300 elif '.hgsub' in changes[2]:
1301 # clean up .hgsubstate when .hgsub is removed
1301 # clean up .hgsubstate when .hgsub is removed
1302 if ('.hgsubstate' in wctx and
1302 if ('.hgsubstate' in wctx and
1303 '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
1303 '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
1304 changes[2].insert(0, '.hgsubstate')
1304 changes[2].insert(0, '.hgsubstate')
1305
1305
1306 # make sure all explicit patterns are matched
1306 # make sure all explicit patterns are matched
1307 if not force and match.files():
1307 if not force and match.files():
1308 matched = set(changes[0] + changes[1] + changes[2])
1308 matched = set(changes[0] + changes[1] + changes[2])
1309
1309
1310 for f in match.files():
1310 for f in match.files():
1311 if f == '.' or f in matched or f in wctx.substate:
1311 if f == '.' or f in matched or f in wctx.substate:
1312 continue
1312 continue
1313 if f in changes[3]: # missing
1313 if f in changes[3]: # missing
1314 fail(f, _('file not found!'))
1314 fail(f, _('file not found!'))
1315 if f in vdirs: # visited directory
1315 if f in vdirs: # visited directory
1316 d = f + '/'
1316 d = f + '/'
1317 for mf in matched:
1317 for mf in matched:
1318 if mf.startswith(d):
1318 if mf.startswith(d):
1319 break
1319 break
1320 else:
1320 else:
1321 fail(f, _("no match under directory!"))
1321 fail(f, _("no match under directory!"))
1322 elif f not in self.dirstate:
1322 elif f not in self.dirstate:
1323 fail(f, _("file not tracked!"))
1323 fail(f, _("file not tracked!"))
1324
1324
1325 if (not force and not extra.get("close") and not merge
1325 if (not force and not extra.get("close") and not merge
1326 and not (changes[0] or changes[1] or changes[2])
1326 and not (changes[0] or changes[1] or changes[2])
1327 and wctx.branch() == wctx.p1().branch()):
1327 and wctx.branch() == wctx.p1().branch()):
1328 return None
1328 return None
1329
1329
1330 if merge and changes[3]:
1330 if merge and changes[3]:
1331 raise util.Abort(_("cannot commit merge with missing files"))
1331 raise util.Abort(_("cannot commit merge with missing files"))
1332
1332
1333 ms = mergemod.mergestate(self)
1333 ms = mergemod.mergestate(self)
1334 for f in changes[0]:
1334 for f in changes[0]:
1335 if f in ms and ms[f] == 'u':
1335 if f in ms and ms[f] == 'u':
1336 raise util.Abort(_("unresolved merge conflicts "
1336 raise util.Abort(_("unresolved merge conflicts "
1337 "(see hg help resolve)"))
1337 "(see hg help resolve)"))
1338
1338
1339 cctx = context.workingctx(self, text, user, date, extra, changes)
1339 cctx = context.workingctx(self, text, user, date, extra, changes)
1340 if editor:
1340 if editor:
1341 cctx._text = editor(self, cctx, subs)
1341 cctx._text = editor(self, cctx, subs)
1342 edited = (text != cctx._text)
1342 edited = (text != cctx._text)
1343
1343
1344 # commit subs and write new state
1344 # commit subs and write new state
1345 if subs:
1345 if subs:
1346 for s in sorted(commitsubs):
1346 for s in sorted(commitsubs):
1347 sub = wctx.sub(s)
1347 sub = wctx.sub(s)
1348 self.ui.status(_('committing subrepository %s\n') %
1348 self.ui.status(_('committing subrepository %s\n') %
1349 subrepo.subrelpath(sub))
1349 subrepo.subrelpath(sub))
1350 sr = sub.commit(cctx._text, user, date)
1350 sr = sub.commit(cctx._text, user, date)
1351 newstate[s] = (newstate[s][0], sr)
1351 newstate[s] = (newstate[s][0], sr)
1352 subrepo.writestate(self, newstate)
1352 subrepo.writestate(self, newstate)
1353
1353
1354 # Save commit message in case this transaction gets rolled back
1354 # Save commit message in case this transaction gets rolled back
1355 # (e.g. by a pretxncommit hook). Leave the content alone on
1355 # (e.g. by a pretxncommit hook). Leave the content alone on
1356 # the assumption that the user will use the same editor again.
1356 # the assumption that the user will use the same editor again.
1357 msgfn = self.savecommitmessage(cctx._text)
1357 msgfn = self.savecommitmessage(cctx._text)
1358
1358
1359 p1, p2 = self.dirstate.parents()
1359 p1, p2 = self.dirstate.parents()
1360 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1360 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1361 try:
1361 try:
1362 self.hook("precommit", throw=True, parent1=hookp1,
1362 self.hook("precommit", throw=True, parent1=hookp1,
1363 parent2=hookp2)
1363 parent2=hookp2)
1364 ret = self.commitctx(cctx, True)
1364 ret = self.commitctx(cctx, True)
1365 except: # re-raises
1365 except: # re-raises
1366 if edited:
1366 if edited:
1367 self.ui.write(
1367 self.ui.write(
1368 _('note: commit message saved in %s\n') % msgfn)
1368 _('note: commit message saved in %s\n') % msgfn)
1369 raise
1369 raise
1370
1370
1371 # update bookmarks, dirstate and mergestate
1371 # update bookmarks, dirstate and mergestate
1372 bookmarks.update(self, [p1, p2], ret)
1372 bookmarks.update(self, [p1, p2], ret)
1373 for f in changes[0] + changes[1]:
1373 for f in changes[0] + changes[1]:
1374 self.dirstate.normal(f)
1374 self.dirstate.normal(f)
1375 for f in changes[2]:
1375 for f in changes[2]:
1376 self.dirstate.drop(f)
1376 self.dirstate.drop(f)
1377 self.dirstate.setparents(ret)
1377 self.dirstate.setparents(ret)
1378 ms.reset()
1378 ms.reset()
1379 finally:
1379 finally:
1380 wlock.release()
1380 wlock.release()
1381
1381
1382 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1382 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1383 self.hook("commit", node=node, parent1=parent1, parent2=parent2)
1383 self.hook("commit", node=node, parent1=parent1, parent2=parent2)
1384 self._afterlock(commithook)
1384 self._afterlock(commithook)
1385 return ret
1385 return ret
1386
1386
1387 def commitctx(self, ctx, error=False):
1387 def commitctx(self, ctx, error=False):
1388 """Add a new revision to current repository.
1388 """Add a new revision to current repository.
1389 Revision information is passed via the context argument.
1389 Revision information is passed via the context argument.
1390 """
1390 """
1391
1391
1392 tr = lock = None
1392 tr = lock = None
1393 removed = list(ctx.removed())
1393 removed = list(ctx.removed())
1394 p1, p2 = ctx.p1(), ctx.p2()
1394 p1, p2 = ctx.p1(), ctx.p2()
1395 user = ctx.user()
1395 user = ctx.user()
1396
1396
1397 lock = self.lock()
1397 lock = self.lock()
1398 try:
1398 try:
1399 tr = self.transaction("commit")
1399 tr = self.transaction("commit")
1400 trp = weakref.proxy(tr)
1400 trp = weakref.proxy(tr)
1401
1401
1402 if ctx.files():
1402 if ctx.files():
1403 m1 = p1.manifest().copy()
1403 m1 = p1.manifest().copy()
1404 m2 = p2.manifest()
1404 m2 = p2.manifest()
1405
1405
1406 # check in files
1406 # check in files
1407 new = {}
1407 new = {}
1408 changed = []
1408 changed = []
1409 linkrev = len(self)
1409 linkrev = len(self)
1410 for f in sorted(ctx.modified() + ctx.added()):
1410 for f in sorted(ctx.modified() + ctx.added()):
1411 self.ui.note(f + "\n")
1411 self.ui.note(f + "\n")
1412 try:
1412 try:
1413 fctx = ctx[f]
1413 fctx = ctx[f]
1414 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1414 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1415 changed)
1415 changed)
1416 m1.set(f, fctx.flags())
1416 m1.set(f, fctx.flags())
1417 except OSError, inst:
1417 except OSError, inst:
1418 self.ui.warn(_("trouble committing %s!\n") % f)
1418 self.ui.warn(_("trouble committing %s!\n") % f)
1419 raise
1419 raise
1420 except IOError, inst:
1420 except IOError, inst:
1421 errcode = getattr(inst, 'errno', errno.ENOENT)
1421 errcode = getattr(inst, 'errno', errno.ENOENT)
1422 if error or errcode and errcode != errno.ENOENT:
1422 if error or errcode and errcode != errno.ENOENT:
1423 self.ui.warn(_("trouble committing %s!\n") % f)
1423 self.ui.warn(_("trouble committing %s!\n") % f)
1424 raise
1424 raise
1425 else:
1425 else:
1426 removed.append(f)
1426 removed.append(f)
1427
1427
1428 # update manifest
1428 # update manifest
1429 m1.update(new)
1429 m1.update(new)
1430 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1430 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1431 drop = [f for f in removed if f in m1]
1431 drop = [f for f in removed if f in m1]
1432 for f in drop:
1432 for f in drop:
1433 del m1[f]
1433 del m1[f]
1434 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1434 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1435 p2.manifestnode(), (new, drop))
1435 p2.manifestnode(), (new, drop))
1436 files = changed + removed
1436 files = changed + removed
1437 else:
1437 else:
1438 mn = p1.manifestnode()
1438 mn = p1.manifestnode()
1439 files = []
1439 files = []
1440
1440
1441 # update changelog
1441 # update changelog
1442 self.changelog.delayupdate()
1442 self.changelog.delayupdate()
1443 n = self.changelog.add(mn, files, ctx.description(),
1443 n = self.changelog.add(mn, files, ctx.description(),
1444 trp, p1.node(), p2.node(),
1444 trp, p1.node(), p2.node(),
1445 user, ctx.date(), ctx.extra().copy())
1445 user, ctx.date(), ctx.extra().copy())
1446 p = lambda: self.changelog.writepending() and self.root or ""
1446 p = lambda: self.changelog.writepending() and self.root or ""
1447 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1447 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1448 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1448 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1449 parent2=xp2, pending=p)
1449 parent2=xp2, pending=p)
1450 self.changelog.finalize(trp)
1450 self.changelog.finalize(trp)
1451 # set the new commit is proper phase
1451 # set the new commit is proper phase
1452 targetphase = phases.newcommitphase(self.ui)
1452 targetphase = phases.newcommitphase(self.ui)
1453 if targetphase:
1453 if targetphase:
1454 # retract boundary do not alter parent changeset.
1454 # retract boundary do not alter parent changeset.
1455 # if a parent have higher the resulting phase will
1455 # if a parent have higher the resulting phase will
1456 # be compliant anyway
1456 # be compliant anyway
1457 #
1457 #
1458 # if minimal phase was 0 we don't need to retract anything
1458 # if minimal phase was 0 we don't need to retract anything
1459 phases.retractboundary(self, targetphase, [n])
1459 phases.retractboundary(self, targetphase, [n])
1460 tr.close()
1460 tr.close()
1461 self.updatebranchcache()
1461 self.updatebranchcache()
1462 return n
1462 return n
1463 finally:
1463 finally:
1464 if tr:
1464 if tr:
1465 tr.release()
1465 tr.release()
1466 lock.release()
1466 lock.release()
1467
1467
1468 def destroyed(self, newheadnodes=None):
1468 def destroyed(self, newheadnodes=None):
1469 '''Inform the repository that nodes have been destroyed.
1469 '''Inform the repository that nodes have been destroyed.
1470 Intended for use by strip and rollback, so there's a common
1470 Intended for use by strip and rollback, so there's a common
1471 place for anything that has to be done after destroying history.
1471 place for anything that has to be done after destroying history.
1472
1472
1473 If you know the branchheadcache was uptodate before nodes were removed
1473 If you know the branchheadcache was uptodate before nodes were removed
1474 and you also know the set of candidate new heads that may have resulted
1474 and you also know the set of candidate new heads that may have resulted
1475 from the destruction, you can set newheadnodes. This will enable the
1475 from the destruction, you can set newheadnodes. This will enable the
1476 code to update the branchheads cache, rather than having future code
1476 code to update the branchheads cache, rather than having future code
1477 decide it's invalid and regenrating it from scratch.
1477 decide it's invalid and regenrating it from scratch.
1478 '''
1478 '''
1479 # If we have info, newheadnodes, on how to update the branch cache, do
1479 # If we have info, newheadnodes, on how to update the branch cache, do
1480 # it, Otherwise, since nodes were destroyed, the cache is stale and this
1480 # it, Otherwise, since nodes were destroyed, the cache is stale and this
1481 # will be caught the next time it is read.
1481 # will be caught the next time it is read.
1482 if newheadnodes:
1482 if newheadnodes:
1483 tiprev = len(self) - 1
1483 tiprev = len(self) - 1
1484 ctxgen = (self[node] for node in newheadnodes
1484 ctxgen = (self[node] for node in newheadnodes
1485 if self.changelog.hasnode(node))
1485 if self.changelog.hasnode(node))
1486 self._updatebranchcache(self._branchcache, ctxgen)
1486 self._updatebranchcache(self._branchcache, ctxgen)
1487 self._writebranchcache(self._branchcache, self.changelog.tip(),
1487 self._writebranchcache(self._branchcache, self.changelog.tip(),
1488 tiprev)
1488 tiprev)
1489
1489
1490 # Ensure the persistent tag cache is updated. Doing it now
1490 # Ensure the persistent tag cache is updated. Doing it now
1491 # means that the tag cache only has to worry about destroyed
1491 # means that the tag cache only has to worry about destroyed
1492 # heads immediately after a strip/rollback. That in turn
1492 # heads immediately after a strip/rollback. That in turn
1493 # guarantees that "cachetip == currenttip" (comparing both rev
1493 # guarantees that "cachetip == currenttip" (comparing both rev
1494 # and node) always means no nodes have been added or destroyed.
1494 # and node) always means no nodes have been added or destroyed.
1495
1495
1496 # XXX this is suboptimal when qrefresh'ing: we strip the current
1496 # XXX this is suboptimal when qrefresh'ing: we strip the current
1497 # head, refresh the tag cache, then immediately add a new head.
1497 # head, refresh the tag cache, then immediately add a new head.
1498 # But I think doing it this way is necessary for the "instant
1498 # But I think doing it this way is necessary for the "instant
1499 # tag cache retrieval" case to work.
1499 # tag cache retrieval" case to work.
1500 self.invalidatecaches()
1500 self.invalidatecaches()
1501
1501
1502 def walk(self, match, node=None):
1502 def walk(self, match, node=None):
1503 '''
1503 '''
1504 walk recursively through the directory tree or a given
1504 walk recursively through the directory tree or a given
1505 changeset, finding all files matched by the match
1505 changeset, finding all files matched by the match
1506 function
1506 function
1507 '''
1507 '''
1508 return self[node].walk(match)
1508 return self[node].walk(match)
1509
1509
1510 def status(self, node1='.', node2=None, match=None,
1510 def status(self, node1='.', node2=None, match=None,
1511 ignored=False, clean=False, unknown=False,
1511 ignored=False, clean=False, unknown=False,
1512 listsubrepos=False):
1512 listsubrepos=False):
1513 """return status of files between two nodes or node and working
1513 """return status of files between two nodes or node and working
1514 directory.
1514 directory.
1515
1515
1516 If node1 is None, use the first dirstate parent instead.
1516 If node1 is None, use the first dirstate parent instead.
1517 If node2 is None, compare node1 with working directory.
1517 If node2 is None, compare node1 with working directory.
1518 """
1518 """
1519
1519
1520 def mfmatches(ctx):
1520 def mfmatches(ctx):
1521 mf = ctx.manifest().copy()
1521 mf = ctx.manifest().copy()
1522 if match.always():
1522 if match.always():
1523 return mf
1523 return mf
1524 for fn in mf.keys():
1524 for fn in mf.keys():
1525 if not match(fn):
1525 if not match(fn):
1526 del mf[fn]
1526 del mf[fn]
1527 return mf
1527 return mf
1528
1528
1529 if isinstance(node1, context.changectx):
1529 if isinstance(node1, context.changectx):
1530 ctx1 = node1
1530 ctx1 = node1
1531 else:
1531 else:
1532 ctx1 = self[node1]
1532 ctx1 = self[node1]
1533 if isinstance(node2, context.changectx):
1533 if isinstance(node2, context.changectx):
1534 ctx2 = node2
1534 ctx2 = node2
1535 else:
1535 else:
1536 ctx2 = self[node2]
1536 ctx2 = self[node2]
1537
1537
1538 working = ctx2.rev() is None
1538 working = ctx2.rev() is None
1539 parentworking = working and ctx1 == self['.']
1539 parentworking = working and ctx1 == self['.']
1540 match = match or matchmod.always(self.root, self.getcwd())
1540 match = match or matchmod.always(self.root, self.getcwd())
1541 listignored, listclean, listunknown = ignored, clean, unknown
1541 listignored, listclean, listunknown = ignored, clean, unknown
1542
1542
1543 # load earliest manifest first for caching reasons
1543 # load earliest manifest first for caching reasons
1544 if not working and ctx2.rev() < ctx1.rev():
1544 if not working and ctx2.rev() < ctx1.rev():
1545 ctx2.manifest()
1545 ctx2.manifest()
1546
1546
1547 if not parentworking:
1547 if not parentworking:
1548 def bad(f, msg):
1548 def bad(f, msg):
1549 # 'f' may be a directory pattern from 'match.files()',
1549 # 'f' may be a directory pattern from 'match.files()',
1550 # so 'f not in ctx1' is not enough
1550 # so 'f not in ctx1' is not enough
1551 if f not in ctx1 and f not in ctx1.dirs():
1551 if f not in ctx1 and f not in ctx1.dirs():
1552 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1552 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1553 match.bad = bad
1553 match.bad = bad
1554
1554
1555 if working: # we need to scan the working dir
1555 if working: # we need to scan the working dir
1556 subrepos = []
1556 subrepos = []
1557 if '.hgsub' in self.dirstate:
1557 if '.hgsub' in self.dirstate:
1558 subrepos = ctx2.substate.keys()
1558 subrepos = ctx2.substate.keys()
1559 s = self.dirstate.status(match, subrepos, listignored,
1559 s = self.dirstate.status(match, subrepos, listignored,
1560 listclean, listunknown)
1560 listclean, listunknown)
1561 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1561 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1562
1562
1563 # check for any possibly clean files
1563 # check for any possibly clean files
1564 if parentworking and cmp:
1564 if parentworking and cmp:
1565 fixup = []
1565 fixup = []
1566 # do a full compare of any files that might have changed
1566 # do a full compare of any files that might have changed
1567 for f in sorted(cmp):
1567 for f in sorted(cmp):
1568 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1568 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1569 or ctx1[f].cmp(ctx2[f])):
1569 or ctx1[f].cmp(ctx2[f])):
1570 modified.append(f)
1570 modified.append(f)
1571 else:
1571 else:
1572 fixup.append(f)
1572 fixup.append(f)
1573
1573
1574 # update dirstate for files that are actually clean
1574 # update dirstate for files that are actually clean
1575 if fixup:
1575 if fixup:
1576 if listclean:
1576 if listclean:
1577 clean += fixup
1577 clean += fixup
1578
1578
1579 try:
1579 try:
1580 # updating the dirstate is optional
1580 # updating the dirstate is optional
1581 # so we don't wait on the lock
1581 # so we don't wait on the lock
1582 wlock = self.wlock(False)
1582 wlock = self.wlock(False)
1583 try:
1583 try:
1584 for f in fixup:
1584 for f in fixup:
1585 self.dirstate.normal(f)
1585 self.dirstate.normal(f)
1586 finally:
1586 finally:
1587 wlock.release()
1587 wlock.release()
1588 except error.LockError:
1588 except error.LockError:
1589 pass
1589 pass
1590
1590
1591 if not parentworking:
1591 if not parentworking:
1592 mf1 = mfmatches(ctx1)
1592 mf1 = mfmatches(ctx1)
1593 if working:
1593 if working:
1594 # we are comparing working dir against non-parent
1594 # we are comparing working dir against non-parent
1595 # generate a pseudo-manifest for the working dir
1595 # generate a pseudo-manifest for the working dir
1596 mf2 = mfmatches(self['.'])
1596 mf2 = mfmatches(self['.'])
1597 for f in cmp + modified + added:
1597 for f in cmp + modified + added:
1598 mf2[f] = None
1598 mf2[f] = None
1599 mf2.set(f, ctx2.flags(f))
1599 mf2.set(f, ctx2.flags(f))
1600 for f in removed:
1600 for f in removed:
1601 if f in mf2:
1601 if f in mf2:
1602 del mf2[f]
1602 del mf2[f]
1603 else:
1603 else:
1604 # we are comparing two revisions
1604 # we are comparing two revisions
1605 deleted, unknown, ignored = [], [], []
1605 deleted, unknown, ignored = [], [], []
1606 mf2 = mfmatches(ctx2)
1606 mf2 = mfmatches(ctx2)
1607
1607
1608 modified, added, clean = [], [], []
1608 modified, added, clean = [], [], []
1609 withflags = mf1.withflags() | mf2.withflags()
1609 withflags = mf1.withflags() | mf2.withflags()
1610 for fn in mf2:
1610 for fn in mf2:
1611 if fn in mf1:
1611 if fn in mf1:
1612 if (fn not in deleted and
1612 if (fn not in deleted and
1613 ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or
1613 ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or
1614 (mf1[fn] != mf2[fn] and
1614 (mf1[fn] != mf2[fn] and
1615 (mf2[fn] or ctx1[fn].cmp(ctx2[fn]))))):
1615 (mf2[fn] or ctx1[fn].cmp(ctx2[fn]))))):
1616 modified.append(fn)
1616 modified.append(fn)
1617 elif listclean:
1617 elif listclean:
1618 clean.append(fn)
1618 clean.append(fn)
1619 del mf1[fn]
1619 del mf1[fn]
1620 elif fn not in deleted:
1620 elif fn not in deleted:
1621 added.append(fn)
1621 added.append(fn)
1622 removed = mf1.keys()
1622 removed = mf1.keys()
1623
1623
1624 if working and modified and not self.dirstate._checklink:
1624 if working and modified and not self.dirstate._checklink:
1625 # Symlink placeholders may get non-symlink-like contents
1625 # Symlink placeholders may get non-symlink-like contents
1626 # via user error or dereferencing by NFS or Samba servers,
1626 # via user error or dereferencing by NFS or Samba servers,
1627 # so we filter out any placeholders that don't look like a
1627 # so we filter out any placeholders that don't look like a
1628 # symlink
1628 # symlink
1629 sane = []
1629 sane = []
1630 for f in modified:
1630 for f in modified:
1631 if ctx2.flags(f) == 'l':
1631 if ctx2.flags(f) == 'l':
1632 d = ctx2[f].data()
1632 d = ctx2[f].data()
1633 if len(d) >= 1024 or '\n' in d or util.binary(d):
1633 if len(d) >= 1024 or '\n' in d or util.binary(d):
1634 self.ui.debug('ignoring suspect symlink placeholder'
1634 self.ui.debug('ignoring suspect symlink placeholder'
1635 ' "%s"\n' % f)
1635 ' "%s"\n' % f)
1636 continue
1636 continue
1637 sane.append(f)
1637 sane.append(f)
1638 modified = sane
1638 modified = sane
1639
1639
1640 r = modified, added, removed, deleted, unknown, ignored, clean
1640 r = modified, added, removed, deleted, unknown, ignored, clean
1641
1641
1642 if listsubrepos:
1642 if listsubrepos:
1643 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1643 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1644 if working:
1644 if working:
1645 rev2 = None
1645 rev2 = None
1646 else:
1646 else:
1647 rev2 = ctx2.substate[subpath][1]
1647 rev2 = ctx2.substate[subpath][1]
1648 try:
1648 try:
1649 submatch = matchmod.narrowmatcher(subpath, match)
1649 submatch = matchmod.narrowmatcher(subpath, match)
1650 s = sub.status(rev2, match=submatch, ignored=listignored,
1650 s = sub.status(rev2, match=submatch, ignored=listignored,
1651 clean=listclean, unknown=listunknown,
1651 clean=listclean, unknown=listunknown,
1652 listsubrepos=True)
1652 listsubrepos=True)
1653 for rfiles, sfiles in zip(r, s):
1653 for rfiles, sfiles in zip(r, s):
1654 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1654 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1655 except error.LookupError:
1655 except error.LookupError:
1656 self.ui.status(_("skipping missing subrepository: %s\n")
1656 self.ui.status(_("skipping missing subrepository: %s\n")
1657 % subpath)
1657 % subpath)
1658
1658
1659 for l in r:
1659 for l in r:
1660 l.sort()
1660 l.sort()
1661 return r
1661 return r
1662
1662
1663 def heads(self, start=None):
1663 def heads(self, start=None):
1664 heads = self.changelog.heads(start)
1664 heads = self.changelog.heads(start)
1665 # sort the output in rev descending order
1665 # sort the output in rev descending order
1666 return sorted(heads, key=self.changelog.rev, reverse=True)
1666 return sorted(heads, key=self.changelog.rev, reverse=True)
1667
1667
1668 def branchheads(self, branch=None, start=None, closed=False):
1668 def branchheads(self, branch=None, start=None, closed=False):
1669 '''return a (possibly filtered) list of heads for the given branch
1669 '''return a (possibly filtered) list of heads for the given branch
1670
1670
1671 Heads are returned in topological order, from newest to oldest.
1671 Heads are returned in topological order, from newest to oldest.
1672 If branch is None, use the dirstate branch.
1672 If branch is None, use the dirstate branch.
1673 If start is not None, return only heads reachable from start.
1673 If start is not None, return only heads reachable from start.
1674 If closed is True, return heads that are marked as closed as well.
1674 If closed is True, return heads that are marked as closed as well.
1675 '''
1675 '''
1676 if branch is None:
1676 if branch is None:
1677 branch = self[None].branch()
1677 branch = self[None].branch()
1678 branches = self.branchmap()
1678 branches = self.branchmap()
1679 if branch not in branches:
1679 if branch not in branches:
1680 return []
1680 return []
1681 # the cache returns heads ordered lowest to highest
1681 # the cache returns heads ordered lowest to highest
1682 bheads = list(reversed(branches[branch]))
1682 bheads = list(reversed(branches[branch]))
1683 if start is not None:
1683 if start is not None:
1684 # filter out the heads that cannot be reached from startrev
1684 # filter out the heads that cannot be reached from startrev
1685 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1685 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1686 bheads = [h for h in bheads if h in fbheads]
1686 bheads = [h for h in bheads if h in fbheads]
1687 if not closed:
1687 if not closed:
1688 bheads = [h for h in bheads if not self[h].closesbranch()]
1688 bheads = [h for h in bheads if not self[h].closesbranch()]
1689 return bheads
1689 return bheads
1690
1690
1691 def branches(self, nodes):
1691 def branches(self, nodes):
1692 if not nodes:
1692 if not nodes:
1693 nodes = [self.changelog.tip()]
1693 nodes = [self.changelog.tip()]
1694 b = []
1694 b = []
1695 for n in nodes:
1695 for n in nodes:
1696 t = n
1696 t = n
1697 while True:
1697 while True:
1698 p = self.changelog.parents(n)
1698 p = self.changelog.parents(n)
1699 if p[1] != nullid or p[0] == nullid:
1699 if p[1] != nullid or p[0] == nullid:
1700 b.append((t, n, p[0], p[1]))
1700 b.append((t, n, p[0], p[1]))
1701 break
1701 break
1702 n = p[0]
1702 n = p[0]
1703 return b
1703 return b
1704
1704
1705 def between(self, pairs):
1705 def between(self, pairs):
1706 r = []
1706 r = []
1707
1707
1708 for top, bottom in pairs:
1708 for top, bottom in pairs:
1709 n, l, i = top, [], 0
1709 n, l, i = top, [], 0
1710 f = 1
1710 f = 1
1711
1711
1712 while n != bottom and n != nullid:
1712 while n != bottom and n != nullid:
1713 p = self.changelog.parents(n)[0]
1713 p = self.changelog.parents(n)[0]
1714 if i == f:
1714 if i == f:
1715 l.append(n)
1715 l.append(n)
1716 f = f * 2
1716 f = f * 2
1717 n = p
1717 n = p
1718 i += 1
1718 i += 1
1719
1719
1720 r.append(l)
1720 r.append(l)
1721
1721
1722 return r
1722 return r
1723
1723
1724 def pull(self, remote, heads=None, force=False):
1724 def pull(self, remote, heads=None, force=False):
1725 # don't open transaction for nothing or you break future useful
1725 # don't open transaction for nothing or you break future useful
1726 # rollback call
1726 # rollback call
1727 tr = None
1727 tr = None
1728 trname = 'pull\n' + util.hidepassword(remote.url())
1728 trname = 'pull\n' + util.hidepassword(remote.url())
1729 lock = self.lock()
1729 lock = self.lock()
1730 try:
1730 try:
1731 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1731 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1732 force=force)
1732 force=force)
1733 common, fetch, rheads = tmp
1733 common, fetch, rheads = tmp
1734 if not fetch:
1734 if not fetch:
1735 self.ui.status(_("no changes found\n"))
1735 self.ui.status(_("no changes found\n"))
1736 added = []
1736 added = []
1737 result = 0
1737 result = 0
1738 else:
1738 else:
1739 tr = self.transaction(trname)
1739 tr = self.transaction(trname)
1740 if heads is None and list(common) == [nullid]:
1740 if heads is None and list(common) == [nullid]:
1741 self.ui.status(_("requesting all changes\n"))
1741 self.ui.status(_("requesting all changes\n"))
1742 elif heads is None and remote.capable('changegroupsubset'):
1742 elif heads is None and remote.capable('changegroupsubset'):
1743 # issue1320, avoid a race if remote changed after discovery
1743 # issue1320, avoid a race if remote changed after discovery
1744 heads = rheads
1744 heads = rheads
1745
1745
1746 if remote.capable('getbundle'):
1746 if remote.capable('getbundle'):
1747 cg = remote.getbundle('pull', common=common,
1747 cg = remote.getbundle('pull', common=common,
1748 heads=heads or rheads)
1748 heads=heads or rheads)
1749 elif heads is None:
1749 elif heads is None:
1750 cg = remote.changegroup(fetch, 'pull')
1750 cg = remote.changegroup(fetch, 'pull')
1751 elif not remote.capable('changegroupsubset'):
1751 elif not remote.capable('changegroupsubset'):
1752 raise util.Abort(_("partial pull cannot be done because "
1752 raise util.Abort(_("partial pull cannot be done because "
1753 "other repository doesn't support "
1753 "other repository doesn't support "
1754 "changegroupsubset."))
1754 "changegroupsubset."))
1755 else:
1755 else:
1756 cg = remote.changegroupsubset(fetch, heads, 'pull')
1756 cg = remote.changegroupsubset(fetch, heads, 'pull')
1757 clstart = len(self.changelog)
1757 clstart = len(self.changelog)
1758 result = self.addchangegroup(cg, 'pull', remote.url())
1758 result = self.addchangegroup(cg, 'pull', remote.url())
1759 clend = len(self.changelog)
1759 clend = len(self.changelog)
1760 added = [self.changelog.node(r) for r in xrange(clstart, clend)]
1760 added = [self.changelog.node(r) for r in xrange(clstart, clend)]
1761
1761
1762 # compute target subset
1762 # compute target subset
1763 if heads is None:
1763 if heads is None:
1764 # We pulled every thing possible
1764 # We pulled every thing possible
1765 # sync on everything common
1765 # sync on everything common
1766 subset = common + added
1766 subset = common + added
1767 else:
1767 else:
1768 # We pulled a specific subset
1768 # We pulled a specific subset
1769 # sync on this subset
1769 # sync on this subset
1770 subset = heads
1770 subset = heads
1771
1771
1772 # Get remote phases data from remote
1772 # Get remote phases data from remote
1773 remotephases = remote.listkeys('phases')
1773 remotephases = remote.listkeys('phases')
1774 publishing = bool(remotephases.get('publishing', False))
1774 publishing = bool(remotephases.get('publishing', False))
1775 if remotephases and not publishing:
1775 if remotephases and not publishing:
1776 # remote is new and unpublishing
1776 # remote is new and unpublishing
1777 pheads, _dr = phases.analyzeremotephases(self, subset,
1777 pheads, _dr = phases.analyzeremotephases(self, subset,
1778 remotephases)
1778 remotephases)
1779 phases.advanceboundary(self, phases.public, pheads)
1779 phases.advanceboundary(self, phases.public, pheads)
1780 phases.advanceboundary(self, phases.draft, subset)
1780 phases.advanceboundary(self, phases.draft, subset)
1781 else:
1781 else:
1782 # Remote is old or publishing all common changesets
1782 # Remote is old or publishing all common changesets
1783 # should be seen as public
1783 # should be seen as public
1784 phases.advanceboundary(self, phases.public, subset)
1784 phases.advanceboundary(self, phases.public, subset)
1785
1785
1786 remoteobs = remote.listkeys('obsolete')
1786 remoteobs = remote.listkeys('obsolete')
1787 if 'dump' in remoteobs:
1787 if 'dump' in remoteobs:
1788 if tr is None:
1788 if tr is None:
1789 tr = self.transaction(trname)
1789 tr = self.transaction(trname)
1790 data = base85.b85decode(remoteobs['dump'])
1790 data = base85.b85decode(remoteobs['dump'])
1791 self.obsstore.mergemarkers(tr, data)
1791 self.obsstore.mergemarkers(tr, data)
1792 if tr is not None:
1792 if tr is not None:
1793 tr.close()
1793 tr.close()
1794 finally:
1794 finally:
1795 if tr is not None:
1795 if tr is not None:
1796 tr.release()
1796 tr.release()
1797 lock.release()
1797 lock.release()
1798
1798
1799 return result
1799 return result
1800
1800
1801 def checkpush(self, force, revs):
1801 def checkpush(self, force, revs):
1802 """Extensions can override this function if additional checks have
1802 """Extensions can override this function if additional checks have
1803 to be performed before pushing, or call it if they override push
1803 to be performed before pushing, or call it if they override push
1804 command.
1804 command.
1805 """
1805 """
1806 pass
1806 pass
1807
1807
1808 def push(self, remote, force=False, revs=None, newbranch=False):
1808 def push(self, remote, force=False, revs=None, newbranch=False):
1809 '''Push outgoing changesets (limited by revs) from the current
1809 '''Push outgoing changesets (limited by revs) from the current
1810 repository to remote. Return an integer:
1810 repository to remote. Return an integer:
1811 - None means nothing to push
1811 - None means nothing to push
1812 - 0 means HTTP error
1812 - 0 means HTTP error
1813 - 1 means we pushed and remote head count is unchanged *or*
1813 - 1 means we pushed and remote head count is unchanged *or*
1814 we have outgoing changesets but refused to push
1814 we have outgoing changesets but refused to push
1815 - other values as described by addchangegroup()
1815 - other values as described by addchangegroup()
1816 '''
1816 '''
1817 # there are two ways to push to remote repo:
1817 # there are two ways to push to remote repo:
1818 #
1818 #
1819 # addchangegroup assumes local user can lock remote
1819 # addchangegroup assumes local user can lock remote
1820 # repo (local filesystem, old ssh servers).
1820 # repo (local filesystem, old ssh servers).
1821 #
1821 #
1822 # unbundle assumes local user cannot lock remote repo (new ssh
1822 # unbundle assumes local user cannot lock remote repo (new ssh
1823 # servers, http servers).
1823 # servers, http servers).
1824
1824
1825 if not remote.canpush():
1825 if not remote.canpush():
1826 raise util.Abort(_("destination does not support push"))
1826 raise util.Abort(_("destination does not support push"))
1827 # get local lock as we might write phase data
1827 # get local lock as we might write phase data
1828 locallock = self.lock()
1828 locallock = self.lock()
1829 try:
1829 try:
1830 self.checkpush(force, revs)
1830 self.checkpush(force, revs)
1831 lock = None
1831 lock = None
1832 unbundle = remote.capable('unbundle')
1832 unbundle = remote.capable('unbundle')
1833 if not unbundle:
1833 if not unbundle:
1834 lock = remote.lock()
1834 lock = remote.lock()
1835 try:
1835 try:
1836 # discovery
1836 # discovery
1837 fci = discovery.findcommonincoming
1837 fci = discovery.findcommonincoming
1838 commoninc = fci(self, remote, force=force)
1838 commoninc = fci(self, remote, force=force)
1839 common, inc, remoteheads = commoninc
1839 common, inc, remoteheads = commoninc
1840 fco = discovery.findcommonoutgoing
1840 fco = discovery.findcommonoutgoing
1841 outgoing = fco(self, remote, onlyheads=revs,
1841 outgoing = fco(self, remote, onlyheads=revs,
1842 commoninc=commoninc, force=force)
1842 commoninc=commoninc, force=force)
1843
1843
1844
1844
1845 if not outgoing.missing:
1845 if not outgoing.missing:
1846 # nothing to push
1846 # nothing to push
1847 scmutil.nochangesfound(self.ui, self, outgoing.excluded)
1847 scmutil.nochangesfound(self.ui, self, outgoing.excluded)
1848 ret = None
1848 ret = None
1849 else:
1849 else:
1850 # something to push
1850 # something to push
1851 if not force:
1851 if not force:
1852 # if self.obsstore == False --> no obsolete
1852 # if self.obsstore == False --> no obsolete
1853 # then, save the iteration
1853 # then, save the iteration
1854 if self.obsstore:
1854 if self.obsstore:
1855 # this message are here for 80 char limit reason
1855 # this message are here for 80 char limit reason
1856 mso = _("push includes an obsolete changeset: %s!")
1856 mso = _("push includes an obsolete changeset: %s!")
1857 msu = _("push includes an unstable changeset: %s!")
1857 msu = _("push includes an unstable changeset: %s!")
1858 # If we are to push if there is at least one
1858 # If we are to push if there is at least one
1859 # obsolete or unstable changeset in missing, at
1859 # obsolete or unstable changeset in missing, at
1860 # least one of the missinghead will be obsolete or
1860 # least one of the missinghead will be obsolete or
1861 # unstable. So checking heads only is ok
1861 # unstable. So checking heads only is ok
1862 for node in outgoing.missingheads:
1862 for node in outgoing.missingheads:
1863 ctx = self[node]
1863 ctx = self[node]
1864 if ctx.obsolete():
1864 if ctx.obsolete():
1865 raise util.Abort(_(mso) % ctx)
1865 raise util.Abort(_(mso) % ctx)
1866 elif ctx.unstable():
1866 elif ctx.unstable():
1867 raise util.Abort(_(msu) % ctx)
1867 raise util.Abort(_(msu) % ctx)
1868 discovery.checkheads(self, remote, outgoing,
1868 discovery.checkheads(self, remote, outgoing,
1869 remoteheads, newbranch,
1869 remoteheads, newbranch,
1870 bool(inc))
1870 bool(inc))
1871
1871
1872 # create a changegroup from local
1872 # create a changegroup from local
1873 if revs is None and not outgoing.excluded:
1873 if revs is None and not outgoing.excluded:
1874 # push everything,
1874 # push everything,
1875 # use the fast path, no race possible on push
1875 # use the fast path, no race possible on push
1876 cg = self._changegroup(outgoing.missing, 'push')
1876 cg = self._changegroup(outgoing.missing, 'push')
1877 else:
1877 else:
1878 cg = self.getlocalbundle('push', outgoing)
1878 cg = self.getlocalbundle('push', outgoing)
1879
1879
1880 # apply changegroup to remote
1880 # apply changegroup to remote
1881 if unbundle:
1881 if unbundle:
1882 # local repo finds heads on server, finds out what
1882 # local repo finds heads on server, finds out what
1883 # revs it must push. once revs transferred, if server
1883 # revs it must push. once revs transferred, if server
1884 # finds it has different heads (someone else won
1884 # finds it has different heads (someone else won
1885 # commit/push race), server aborts.
1885 # commit/push race), server aborts.
1886 if force:
1886 if force:
1887 remoteheads = ['force']
1887 remoteheads = ['force']
1888 # ssh: return remote's addchangegroup()
1888 # ssh: return remote's addchangegroup()
1889 # http: return remote's addchangegroup() or 0 for error
1889 # http: return remote's addchangegroup() or 0 for error
1890 ret = remote.unbundle(cg, remoteheads, 'push')
1890 ret = remote.unbundle(cg, remoteheads, 'push')
1891 else:
1891 else:
1892 # we return an integer indicating remote head count
1892 # we return an integer indicating remote head count
1893 # change
1893 # change
1894 ret = remote.addchangegroup(cg, 'push', self.url())
1894 ret = remote.addchangegroup(cg, 'push', self.url())
1895
1895
1896 if ret:
1896 if ret:
1897 # push succeed, synchonize target of the push
1897 # push succeed, synchonize target of the push
1898 cheads = outgoing.missingheads
1898 cheads = outgoing.missingheads
1899 elif revs is None:
1899 elif revs is None:
1900 # All out push fails. synchronize all common
1900 # All out push fails. synchronize all common
1901 cheads = outgoing.commonheads
1901 cheads = outgoing.commonheads
1902 else:
1902 else:
1903 # I want cheads = heads(::missingheads and ::commonheads)
1903 # I want cheads = heads(::missingheads and ::commonheads)
1904 # (missingheads is revs with secret changeset filtered out)
1904 # (missingheads is revs with secret changeset filtered out)
1905 #
1905 #
1906 # This can be expressed as:
1906 # This can be expressed as:
1907 # cheads = ( (missingheads and ::commonheads)
1907 # cheads = ( (missingheads and ::commonheads)
1908 # + (commonheads and ::missingheads))"
1908 # + (commonheads and ::missingheads))"
1909 # )
1909 # )
1910 #
1910 #
1911 # while trying to push we already computed the following:
1911 # while trying to push we already computed the following:
1912 # common = (::commonheads)
1912 # common = (::commonheads)
1913 # missing = ((commonheads::missingheads) - commonheads)
1913 # missing = ((commonheads::missingheads) - commonheads)
1914 #
1914 #
1915 # We can pick:
1915 # We can pick:
1916 # * missingheads part of comon (::commonheads)
1916 # * missingheads part of comon (::commonheads)
1917 common = set(outgoing.common)
1917 common = set(outgoing.common)
1918 cheads = [node for node in revs if node in common]
1918 cheads = [node for node in revs if node in common]
1919 # and
1919 # and
1920 # * commonheads parents on missing
1920 # * commonheads parents on missing
1921 revset = self.set('%ln and parents(roots(%ln))',
1921 revset = self.set('%ln and parents(roots(%ln))',
1922 outgoing.commonheads,
1922 outgoing.commonheads,
1923 outgoing.missing)
1923 outgoing.missing)
1924 cheads.extend(c.node() for c in revset)
1924 cheads.extend(c.node() for c in revset)
1925 # even when we don't push, exchanging phase data is useful
1925 # even when we don't push, exchanging phase data is useful
1926 remotephases = remote.listkeys('phases')
1926 remotephases = remote.listkeys('phases')
1927 if not remotephases: # old server or public only repo
1927 if not remotephases: # old server or public only repo
1928 phases.advanceboundary(self, phases.public, cheads)
1928 phases.advanceboundary(self, phases.public, cheads)
1929 # don't push any phase data as there is nothing to push
1929 # don't push any phase data as there is nothing to push
1930 else:
1930 else:
1931 ana = phases.analyzeremotephases(self, cheads, remotephases)
1931 ana = phases.analyzeremotephases(self, cheads, remotephases)
1932 pheads, droots = ana
1932 pheads, droots = ana
1933 ### Apply remote phase on local
1933 ### Apply remote phase on local
1934 if remotephases.get('publishing', False):
1934 if remotephases.get('publishing', False):
1935 phases.advanceboundary(self, phases.public, cheads)
1935 phases.advanceboundary(self, phases.public, cheads)
1936 else: # publish = False
1936 else: # publish = False
1937 phases.advanceboundary(self, phases.public, pheads)
1937 phases.advanceboundary(self, phases.public, pheads)
1938 phases.advanceboundary(self, phases.draft, cheads)
1938 phases.advanceboundary(self, phases.draft, cheads)
1939 ### Apply local phase on remote
1939 ### Apply local phase on remote
1940
1940
1941 # Get the list of all revs draft on remote by public here.
1941 # Get the list of all revs draft on remote by public here.
1942 # XXX Beware that revset break if droots is not strictly
1942 # XXX Beware that revset break if droots is not strictly
1943 # XXX root we may want to ensure it is but it is costly
1943 # XXX root we may want to ensure it is but it is costly
1944 outdated = self.set('heads((%ln::%ln) and public())',
1944 outdated = self.set('heads((%ln::%ln) and public())',
1945 droots, cheads)
1945 droots, cheads)
1946 for newremotehead in outdated:
1946 for newremotehead in outdated:
1947 r = remote.pushkey('phases',
1947 r = remote.pushkey('phases',
1948 newremotehead.hex(),
1948 newremotehead.hex(),
1949 str(phases.draft),
1949 str(phases.draft),
1950 str(phases.public))
1950 str(phases.public))
1951 if not r:
1951 if not r:
1952 self.ui.warn(_('updating %s to public failed!\n')
1952 self.ui.warn(_('updating %s to public failed!\n')
1953 % newremotehead)
1953 % newremotehead)
1954 if (self.obsstore and
1954 if (self.obsstore and
1955 'obsolete' in remote.listkeys('namespaces')):
1955 'obsolete' in remote.listkeys('namespaces')):
1956 data = self.listkeys('obsolete')['dump']
1956 data = self.listkeys('obsolete')['dump']
1957 r = remote.pushkey('obsolete', 'dump', '', data)
1957 r = remote.pushkey('obsolete', 'dump', '', data)
1958 if not r:
1958 if not r:
1959 self.ui.warn(_('failed to push obsolete markers!\n'))
1959 self.ui.warn(_('failed to push obsolete markers!\n'))
1960 finally:
1960 finally:
1961 if lock is not None:
1961 if lock is not None:
1962 lock.release()
1962 lock.release()
1963 finally:
1963 finally:
1964 locallock.release()
1964 locallock.release()
1965
1965
1966 self.ui.debug("checking for updated bookmarks\n")
1966 self.ui.debug("checking for updated bookmarks\n")
1967 rb = remote.listkeys('bookmarks')
1967 rb = remote.listkeys('bookmarks')
1968 for k in rb.keys():
1968 for k in rb.keys():
1969 if k in self._bookmarks:
1969 if k in self._bookmarks:
1970 nr, nl = rb[k], hex(self._bookmarks[k])
1970 nr, nl = rb[k], hex(self._bookmarks[k])
1971 if nr in self:
1971 if nr in self:
1972 cr = self[nr]
1972 cr = self[nr]
1973 cl = self[nl]
1973 cl = self[nl]
1974 if cl in cr.descendants():
1974 if cl in cr.descendants():
1975 r = remote.pushkey('bookmarks', k, nr, nl)
1975 r = remote.pushkey('bookmarks', k, nr, nl)
1976 if r:
1976 if r:
1977 self.ui.status(_("updating bookmark %s\n") % k)
1977 self.ui.status(_("updating bookmark %s\n") % k)
1978 else:
1978 else:
1979 self.ui.warn(_('updating bookmark %s'
1979 self.ui.warn(_('updating bookmark %s'
1980 ' failed!\n') % k)
1980 ' failed!\n') % k)
1981
1981
1982 return ret
1982 return ret
1983
1983
1984 def changegroupinfo(self, nodes, source):
1984 def changegroupinfo(self, nodes, source):
1985 if self.ui.verbose or source == 'bundle':
1985 if self.ui.verbose or source == 'bundle':
1986 self.ui.status(_("%d changesets found\n") % len(nodes))
1986 self.ui.status(_("%d changesets found\n") % len(nodes))
1987 if self.ui.debugflag:
1987 if self.ui.debugflag:
1988 self.ui.debug("list of changesets:\n")
1988 self.ui.debug("list of changesets:\n")
1989 for node in nodes:
1989 for node in nodes:
1990 self.ui.debug("%s\n" % hex(node))
1990 self.ui.debug("%s\n" % hex(node))
1991
1991
1992 def changegroupsubset(self, bases, heads, source):
1992 def changegroupsubset(self, bases, heads, source):
1993 """Compute a changegroup consisting of all the nodes that are
1993 """Compute a changegroup consisting of all the nodes that are
1994 descendants of any of the bases and ancestors of any of the heads.
1994 descendants of any of the bases and ancestors of any of the heads.
1995 Return a chunkbuffer object whose read() method will return
1995 Return a chunkbuffer object whose read() method will return
1996 successive changegroup chunks.
1996 successive changegroup chunks.
1997
1997
1998 It is fairly complex as determining which filenodes and which
1998 It is fairly complex as determining which filenodes and which
1999 manifest nodes need to be included for the changeset to be complete
1999 manifest nodes need to be included for the changeset to be complete
2000 is non-trivial.
2000 is non-trivial.
2001
2001
2002 Another wrinkle is doing the reverse, figuring out which changeset in
2002 Another wrinkle is doing the reverse, figuring out which changeset in
2003 the changegroup a particular filenode or manifestnode belongs to.
2003 the changegroup a particular filenode or manifestnode belongs to.
2004 """
2004 """
2005 cl = self.changelog
2005 cl = self.changelog
2006 if not bases:
2006 if not bases:
2007 bases = [nullid]
2007 bases = [nullid]
2008 csets, bases, heads = cl.nodesbetween(bases, heads)
2008 csets, bases, heads = cl.nodesbetween(bases, heads)
2009 # We assume that all ancestors of bases are known
2009 # We assume that all ancestors of bases are known
2010 common = set(cl.ancestors([cl.rev(n) for n in bases]))
2010 common = set(cl.ancestors([cl.rev(n) for n in bases]))
2011 return self._changegroupsubset(common, csets, heads, source)
2011 return self._changegroupsubset(common, csets, heads, source)
2012
2012
2013 def getlocalbundle(self, source, outgoing):
2013 def getlocalbundle(self, source, outgoing):
2014 """Like getbundle, but taking a discovery.outgoing as an argument.
2014 """Like getbundle, but taking a discovery.outgoing as an argument.
2015
2015
2016 This is only implemented for local repos and reuses potentially
2016 This is only implemented for local repos and reuses potentially
2017 precomputed sets in outgoing."""
2017 precomputed sets in outgoing."""
2018 if not outgoing.missing:
2018 if not outgoing.missing:
2019 return None
2019 return None
2020 return self._changegroupsubset(outgoing.common,
2020 return self._changegroupsubset(outgoing.common,
2021 outgoing.missing,
2021 outgoing.missing,
2022 outgoing.missingheads,
2022 outgoing.missingheads,
2023 source)
2023 source)
2024
2024
2025 def getbundle(self, source, heads=None, common=None):
2025 def getbundle(self, source, heads=None, common=None):
2026 """Like changegroupsubset, but returns the set difference between the
2026 """Like changegroupsubset, but returns the set difference between the
2027 ancestors of heads and the ancestors common.
2027 ancestors of heads and the ancestors common.
2028
2028
2029 If heads is None, use the local heads. If common is None, use [nullid].
2029 If heads is None, use the local heads. If common is None, use [nullid].
2030
2030
2031 The nodes in common might not all be known locally due to the way the
2031 The nodes in common might not all be known locally due to the way the
2032 current discovery protocol works.
2032 current discovery protocol works.
2033 """
2033 """
2034 cl = self.changelog
2034 cl = self.changelog
2035 if common:
2035 if common:
2036 nm = cl.nodemap
2036 nm = cl.nodemap
2037 common = [n for n in common if n in nm]
2037 common = [n for n in common if n in nm]
2038 else:
2038 else:
2039 common = [nullid]
2039 common = [nullid]
2040 if not heads:
2040 if not heads:
2041 heads = cl.heads()
2041 heads = cl.heads()
2042 return self.getlocalbundle(source,
2042 return self.getlocalbundle(source,
2043 discovery.outgoing(cl, common, heads))
2043 discovery.outgoing(cl, common, heads))
2044
2044
2045 def _changegroupsubset(self, commonrevs, csets, heads, source):
2045 def _changegroupsubset(self, commonrevs, csets, heads, source):
2046
2046
2047 cl = self.changelog
2047 cl = self.changelog
2048 mf = self.manifest
2048 mf = self.manifest
2049 mfs = {} # needed manifests
2049 mfs = {} # needed manifests
2050 fnodes = {} # needed file nodes
2050 fnodes = {} # needed file nodes
2051 changedfiles = set()
2051 changedfiles = set()
2052 fstate = ['', {}]
2052 fstate = ['', {}]
2053 count = [0, 0]
2053 count = [0, 0]
2054
2054
2055 # can we go through the fast path ?
2055 # can we go through the fast path ?
2056 heads.sort()
2056 heads.sort()
2057 if heads == sorted(self.heads()):
2057 if heads == sorted(self.heads()):
2058 return self._changegroup(csets, source)
2058 return self._changegroup(csets, source)
2059
2059
2060 # slow path
2060 # slow path
2061 self.hook('preoutgoing', throw=True, source=source)
2061 self.hook('preoutgoing', throw=True, source=source)
2062 self.changegroupinfo(csets, source)
2062 self.changegroupinfo(csets, source)
2063
2063
2064 # filter any nodes that claim to be part of the known set
2064 # filter any nodes that claim to be part of the known set
2065 def prune(revlog, missing):
2065 def prune(revlog, missing):
2066 rr, rl = revlog.rev, revlog.linkrev
2066 rr, rl = revlog.rev, revlog.linkrev
2067 return [n for n in missing
2067 return [n for n in missing
2068 if rl(rr(n)) not in commonrevs]
2068 if rl(rr(n)) not in commonrevs]
2069
2069
2070 progress = self.ui.progress
2070 progress = self.ui.progress
2071 _bundling = _('bundling')
2071 _bundling = _('bundling')
2072 _changesets = _('changesets')
2072 _changesets = _('changesets')
2073 _manifests = _('manifests')
2073 _manifests = _('manifests')
2074 _files = _('files')
2074 _files = _('files')
2075
2075
2076 def lookup(revlog, x):
2076 def lookup(revlog, x):
2077 if revlog == cl:
2077 if revlog == cl:
2078 c = cl.read(x)
2078 c = cl.read(x)
2079 changedfiles.update(c[3])
2079 changedfiles.update(c[3])
2080 mfs.setdefault(c[0], x)
2080 mfs.setdefault(c[0], x)
2081 count[0] += 1
2081 count[0] += 1
2082 progress(_bundling, count[0],
2082 progress(_bundling, count[0],
2083 unit=_changesets, total=count[1])
2083 unit=_changesets, total=count[1])
2084 return x
2084 return x
2085 elif revlog == mf:
2085 elif revlog == mf:
2086 clnode = mfs[x]
2086 clnode = mfs[x]
2087 mdata = mf.readfast(x)
2087 mdata = mf.readfast(x)
2088 for f, n in mdata.iteritems():
2088 for f, n in mdata.iteritems():
2089 if f in changedfiles:
2089 if f in changedfiles:
2090 fnodes[f].setdefault(n, clnode)
2090 fnodes[f].setdefault(n, clnode)
2091 count[0] += 1
2091 count[0] += 1
2092 progress(_bundling, count[0],
2092 progress(_bundling, count[0],
2093 unit=_manifests, total=count[1])
2093 unit=_manifests, total=count[1])
2094 return clnode
2094 return clnode
2095 else:
2095 else:
2096 progress(_bundling, count[0], item=fstate[0],
2096 progress(_bundling, count[0], item=fstate[0],
2097 unit=_files, total=count[1])
2097 unit=_files, total=count[1])
2098 return fstate[1][x]
2098 return fstate[1][x]
2099
2099
2100 bundler = changegroup.bundle10(lookup)
2100 bundler = changegroup.bundle10(lookup)
2101 reorder = self.ui.config('bundle', 'reorder', 'auto')
2101 reorder = self.ui.config('bundle', 'reorder', 'auto')
2102 if reorder == 'auto':
2102 if reorder == 'auto':
2103 reorder = None
2103 reorder = None
2104 else:
2104 else:
2105 reorder = util.parsebool(reorder)
2105 reorder = util.parsebool(reorder)
2106
2106
2107 def gengroup():
2107 def gengroup():
2108 # Create a changenode group generator that will call our functions
2108 # Create a changenode group generator that will call our functions
2109 # back to lookup the owning changenode and collect information.
2109 # back to lookup the owning changenode and collect information.
2110 count[:] = [0, len(csets)]
2110 count[:] = [0, len(csets)]
2111 for chunk in cl.group(csets, bundler, reorder=reorder):
2111 for chunk in cl.group(csets, bundler, reorder=reorder):
2112 yield chunk
2112 yield chunk
2113 progress(_bundling, None)
2113 progress(_bundling, None)
2114
2114
2115 # Create a generator for the manifestnodes that calls our lookup
2115 # Create a generator for the manifestnodes that calls our lookup
2116 # and data collection functions back.
2116 # and data collection functions back.
2117 for f in changedfiles:
2117 for f in changedfiles:
2118 fnodes[f] = {}
2118 fnodes[f] = {}
2119 count[:] = [0, len(mfs)]
2119 count[:] = [0, len(mfs)]
2120 for chunk in mf.group(prune(mf, mfs), bundler, reorder=reorder):
2120 for chunk in mf.group(prune(mf, mfs), bundler, reorder=reorder):
2121 yield chunk
2121 yield chunk
2122 progress(_bundling, None)
2122 progress(_bundling, None)
2123
2123
2124 mfs.clear()
2124 mfs.clear()
2125
2125
2126 # Go through all our files in order sorted by name.
2126 # Go through all our files in order sorted by name.
2127 count[:] = [0, len(changedfiles)]
2127 count[:] = [0, len(changedfiles)]
2128 for fname in sorted(changedfiles):
2128 for fname in sorted(changedfiles):
2129 filerevlog = self.file(fname)
2129 filerevlog = self.file(fname)
2130 if not len(filerevlog):
2130 if not len(filerevlog):
2131 raise util.Abort(_("empty or missing revlog for %s")
2131 raise util.Abort(_("empty or missing revlog for %s")
2132 % fname)
2132 % fname)
2133 fstate[0] = fname
2133 fstate[0] = fname
2134 fstate[1] = fnodes.pop(fname, {})
2134 fstate[1] = fnodes.pop(fname, {})
2135
2135
2136 nodelist = prune(filerevlog, fstate[1])
2136 nodelist = prune(filerevlog, fstate[1])
2137 if nodelist:
2137 if nodelist:
2138 count[0] += 1
2138 count[0] += 1
2139 yield bundler.fileheader(fname)
2139 yield bundler.fileheader(fname)
2140 for chunk in filerevlog.group(nodelist, bundler, reorder):
2140 for chunk in filerevlog.group(nodelist, bundler, reorder):
2141 yield chunk
2141 yield chunk
2142
2142
2143 # Signal that no more groups are left.
2143 # Signal that no more groups are left.
2144 yield bundler.close()
2144 yield bundler.close()
2145 progress(_bundling, None)
2145 progress(_bundling, None)
2146
2146
2147 if csets:
2147 if csets:
2148 self.hook('outgoing', node=hex(csets[0]), source=source)
2148 self.hook('outgoing', node=hex(csets[0]), source=source)
2149
2149
2150 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
2150 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
2151
2151
2152 def changegroup(self, basenodes, source):
2152 def changegroup(self, basenodes, source):
2153 # to avoid a race we use changegroupsubset() (issue1320)
2153 # to avoid a race we use changegroupsubset() (issue1320)
2154 return self.changegroupsubset(basenodes, self.heads(), source)
2154 return self.changegroupsubset(basenodes, self.heads(), source)
2155
2155
2156 def _changegroup(self, nodes, source):
2156 def _changegroup(self, nodes, source):
2157 """Compute the changegroup of all nodes that we have that a recipient
2157 """Compute the changegroup of all nodes that we have that a recipient
2158 doesn't. Return a chunkbuffer object whose read() method will return
2158 doesn't. Return a chunkbuffer object whose read() method will return
2159 successive changegroup chunks.
2159 successive changegroup chunks.
2160
2160
2161 This is much easier than the previous function as we can assume that
2161 This is much easier than the previous function as we can assume that
2162 the recipient has any changenode we aren't sending them.
2162 the recipient has any changenode we aren't sending them.
2163
2163
2164 nodes is the set of nodes to send"""
2164 nodes is the set of nodes to send"""
2165
2165
2166 cl = self.changelog
2166 cl = self.changelog
2167 mf = self.manifest
2167 mf = self.manifest
2168 mfs = {}
2168 mfs = {}
2169 changedfiles = set()
2169 changedfiles = set()
2170 fstate = ['']
2170 fstate = ['']
2171 count = [0, 0]
2171 count = [0, 0]
2172
2172
2173 self.hook('preoutgoing', throw=True, source=source)
2173 self.hook('preoutgoing', throw=True, source=source)
2174 self.changegroupinfo(nodes, source)
2174 self.changegroupinfo(nodes, source)
2175
2175
2176 revset = set([cl.rev(n) for n in nodes])
2176 revset = set([cl.rev(n) for n in nodes])
2177
2177
2178 def gennodelst(log):
2178 def gennodelst(log):
2179 ln, llr = log.node, log.linkrev
2179 ln, llr = log.node, log.linkrev
2180 return [ln(r) for r in log if llr(r) in revset]
2180 return [ln(r) for r in log if llr(r) in revset]
2181
2181
2182 progress = self.ui.progress
2182 progress = self.ui.progress
2183 _bundling = _('bundling')
2183 _bundling = _('bundling')
2184 _changesets = _('changesets')
2184 _changesets = _('changesets')
2185 _manifests = _('manifests')
2185 _manifests = _('manifests')
2186 _files = _('files')
2186 _files = _('files')
2187
2187
2188 def lookup(revlog, x):
2188 def lookup(revlog, x):
2189 if revlog == cl:
2189 if revlog == cl:
2190 c = cl.read(x)
2190 c = cl.read(x)
2191 changedfiles.update(c[3])
2191 changedfiles.update(c[3])
2192 mfs.setdefault(c[0], x)
2192 mfs.setdefault(c[0], x)
2193 count[0] += 1
2193 count[0] += 1
2194 progress(_bundling, count[0],
2194 progress(_bundling, count[0],
2195 unit=_changesets, total=count[1])
2195 unit=_changesets, total=count[1])
2196 return x
2196 return x
2197 elif revlog == mf:
2197 elif revlog == mf:
2198 count[0] += 1
2198 count[0] += 1
2199 progress(_bundling, count[0],
2199 progress(_bundling, count[0],
2200 unit=_manifests, total=count[1])
2200 unit=_manifests, total=count[1])
2201 return cl.node(revlog.linkrev(revlog.rev(x)))
2201 return cl.node(revlog.linkrev(revlog.rev(x)))
2202 else:
2202 else:
2203 progress(_bundling, count[0], item=fstate[0],
2203 progress(_bundling, count[0], item=fstate[0],
2204 total=count[1], unit=_files)
2204 total=count[1], unit=_files)
2205 return cl.node(revlog.linkrev(revlog.rev(x)))
2205 return cl.node(revlog.linkrev(revlog.rev(x)))
2206
2206
2207 bundler = changegroup.bundle10(lookup)
2207 bundler = changegroup.bundle10(lookup)
2208 reorder = self.ui.config('bundle', 'reorder', 'auto')
2208 reorder = self.ui.config('bundle', 'reorder', 'auto')
2209 if reorder == 'auto':
2209 if reorder == 'auto':
2210 reorder = None
2210 reorder = None
2211 else:
2211 else:
2212 reorder = util.parsebool(reorder)
2212 reorder = util.parsebool(reorder)
2213
2213
2214 def gengroup():
2214 def gengroup():
2215 '''yield a sequence of changegroup chunks (strings)'''
2215 '''yield a sequence of changegroup chunks (strings)'''
2216 # construct a list of all changed files
2216 # construct a list of all changed files
2217
2217
2218 count[:] = [0, len(nodes)]
2218 count[:] = [0, len(nodes)]
2219 for chunk in cl.group(nodes, bundler, reorder=reorder):
2219 for chunk in cl.group(nodes, bundler, reorder=reorder):
2220 yield chunk
2220 yield chunk
2221 progress(_bundling, None)
2221 progress(_bundling, None)
2222
2222
2223 count[:] = [0, len(mfs)]
2223 count[:] = [0, len(mfs)]
2224 for chunk in mf.group(gennodelst(mf), bundler, reorder=reorder):
2224 for chunk in mf.group(gennodelst(mf), bundler, reorder=reorder):
2225 yield chunk
2225 yield chunk
2226 progress(_bundling, None)
2226 progress(_bundling, None)
2227
2227
2228 count[:] = [0, len(changedfiles)]
2228 count[:] = [0, len(changedfiles)]
2229 for fname in sorted(changedfiles):
2229 for fname in sorted(changedfiles):
2230 filerevlog = self.file(fname)
2230 filerevlog = self.file(fname)
2231 if not len(filerevlog):
2231 if not len(filerevlog):
2232 raise util.Abort(_("empty or missing revlog for %s")
2232 raise util.Abort(_("empty or missing revlog for %s")
2233 % fname)
2233 % fname)
2234 fstate[0] = fname
2234 fstate[0] = fname
2235 nodelist = gennodelst(filerevlog)
2235 nodelist = gennodelst(filerevlog)
2236 if nodelist:
2236 if nodelist:
2237 count[0] += 1
2237 count[0] += 1
2238 yield bundler.fileheader(fname)
2238 yield bundler.fileheader(fname)
2239 for chunk in filerevlog.group(nodelist, bundler, reorder):
2239 for chunk in filerevlog.group(nodelist, bundler, reorder):
2240 yield chunk
2240 yield chunk
2241 yield bundler.close()
2241 yield bundler.close()
2242 progress(_bundling, None)
2242 progress(_bundling, None)
2243
2243
2244 if nodes:
2244 if nodes:
2245 self.hook('outgoing', node=hex(nodes[0]), source=source)
2245 self.hook('outgoing', node=hex(nodes[0]), source=source)
2246
2246
2247 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
2247 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
2248
2248
2249 def addchangegroup(self, source, srctype, url, emptyok=False):
2249 def addchangegroup(self, source, srctype, url, emptyok=False):
2250 """Add the changegroup returned by source.read() to this repo.
2250 """Add the changegroup returned by source.read() to this repo.
2251 srctype is a string like 'push', 'pull', or 'unbundle'. url is
2251 srctype is a string like 'push', 'pull', or 'unbundle'. url is
2252 the URL of the repo where this changegroup is coming from.
2252 the URL of the repo where this changegroup is coming from.
2253
2253
2254 Return an integer summarizing the change to this repo:
2254 Return an integer summarizing the change to this repo:
2255 - nothing changed or no source: 0
2255 - nothing changed or no source: 0
2256 - more heads than before: 1+added heads (2..n)
2256 - more heads than before: 1+added heads (2..n)
2257 - fewer heads than before: -1-removed heads (-2..-n)
2257 - fewer heads than before: -1-removed heads (-2..-n)
2258 - number of heads stays the same: 1
2258 - number of heads stays the same: 1
2259 """
2259 """
2260 def csmap(x):
2260 def csmap(x):
2261 self.ui.debug("add changeset %s\n" % short(x))
2261 self.ui.debug("add changeset %s\n" % short(x))
2262 return len(cl)
2262 return len(cl)
2263
2263
2264 def revmap(x):
2264 def revmap(x):
2265 return cl.rev(x)
2265 return cl.rev(x)
2266
2266
2267 if not source:
2267 if not source:
2268 return 0
2268 return 0
2269
2269
2270 self.hook('prechangegroup', throw=True, source=srctype, url=url)
2270 self.hook('prechangegroup', throw=True, source=srctype, url=url)
2271
2271
2272 changesets = files = revisions = 0
2272 changesets = files = revisions = 0
2273 efiles = set()
2273 efiles = set()
2274
2274
2275 # write changelog data to temp files so concurrent readers will not see
2275 # write changelog data to temp files so concurrent readers will not see
2276 # inconsistent view
2276 # inconsistent view
2277 cl = self.changelog
2277 cl = self.changelog
2278 cl.delayupdate()
2278 cl.delayupdate()
2279 oldheads = cl.heads()
2279 oldheads = cl.heads()
2280
2280
2281 tr = self.transaction("\n".join([srctype, util.hidepassword(url)]))
2281 tr = self.transaction("\n".join([srctype, util.hidepassword(url)]))
2282 try:
2282 try:
2283 trp = weakref.proxy(tr)
2283 trp = weakref.proxy(tr)
2284 # pull off the changeset group
2284 # pull off the changeset group
2285 self.ui.status(_("adding changesets\n"))
2285 self.ui.status(_("adding changesets\n"))
2286 clstart = len(cl)
2286 clstart = len(cl)
2287 class prog(object):
2287 class prog(object):
2288 step = _('changesets')
2288 step = _('changesets')
2289 count = 1
2289 count = 1
2290 ui = self.ui
2290 ui = self.ui
2291 total = None
2291 total = None
2292 def __call__(self):
2292 def __call__(self):
2293 self.ui.progress(self.step, self.count, unit=_('chunks'),
2293 self.ui.progress(self.step, self.count, unit=_('chunks'),
2294 total=self.total)
2294 total=self.total)
2295 self.count += 1
2295 self.count += 1
2296 pr = prog()
2296 pr = prog()
2297 source.callback = pr
2297 source.callback = pr
2298
2298
2299 source.changelogheader()
2299 source.changelogheader()
2300 srccontent = cl.addgroup(source, csmap, trp)
2300 srccontent = cl.addgroup(source, csmap, trp)
2301 if not (srccontent or emptyok):
2301 if not (srccontent or emptyok):
2302 raise util.Abort(_("received changelog group is empty"))
2302 raise util.Abort(_("received changelog group is empty"))
2303 clend = len(cl)
2303 clend = len(cl)
2304 changesets = clend - clstart
2304 changesets = clend - clstart
2305 for c in xrange(clstart, clend):
2305 for c in xrange(clstart, clend):
2306 efiles.update(self[c].files())
2306 efiles.update(self[c].files())
2307 efiles = len(efiles)
2307 efiles = len(efiles)
2308 self.ui.progress(_('changesets'), None)
2308 self.ui.progress(_('changesets'), None)
2309
2309
2310 # pull off the manifest group
2310 # pull off the manifest group
2311 self.ui.status(_("adding manifests\n"))
2311 self.ui.status(_("adding manifests\n"))
2312 pr.step = _('manifests')
2312 pr.step = _('manifests')
2313 pr.count = 1
2313 pr.count = 1
2314 pr.total = changesets # manifests <= changesets
2314 pr.total = changesets # manifests <= changesets
2315 # no need to check for empty manifest group here:
2315 # no need to check for empty manifest group here:
2316 # if the result of the merge of 1 and 2 is the same in 3 and 4,
2316 # if the result of the merge of 1 and 2 is the same in 3 and 4,
2317 # no new manifest will be created and the manifest group will
2317 # no new manifest will be created and the manifest group will
2318 # be empty during the pull
2318 # be empty during the pull
2319 source.manifestheader()
2319 source.manifestheader()
2320 self.manifest.addgroup(source, revmap, trp)
2320 self.manifest.addgroup(source, revmap, trp)
2321 self.ui.progress(_('manifests'), None)
2321 self.ui.progress(_('manifests'), None)
2322
2322
2323 needfiles = {}
2323 needfiles = {}
2324 if self.ui.configbool('server', 'validate', default=False):
2324 if self.ui.configbool('server', 'validate', default=False):
2325 # validate incoming csets have their manifests
2325 # validate incoming csets have their manifests
2326 for cset in xrange(clstart, clend):
2326 for cset in xrange(clstart, clend):
2327 mfest = self.changelog.read(self.changelog.node(cset))[0]
2327 mfest = self.changelog.read(self.changelog.node(cset))[0]
2328 mfest = self.manifest.readdelta(mfest)
2328 mfest = self.manifest.readdelta(mfest)
2329 # store file nodes we must see
2329 # store file nodes we must see
2330 for f, n in mfest.iteritems():
2330 for f, n in mfest.iteritems():
2331 needfiles.setdefault(f, set()).add(n)
2331 needfiles.setdefault(f, set()).add(n)
2332
2332
2333 # process the files
2333 # process the files
2334 self.ui.status(_("adding file changes\n"))
2334 self.ui.status(_("adding file changes\n"))
2335 pr.step = _('files')
2335 pr.step = _('files')
2336 pr.count = 1
2336 pr.count = 1
2337 pr.total = efiles
2337 pr.total = efiles
2338 source.callback = None
2338 source.callback = None
2339
2339
2340 while True:
2340 while True:
2341 chunkdata = source.filelogheader()
2341 chunkdata = source.filelogheader()
2342 if not chunkdata:
2342 if not chunkdata:
2343 break
2343 break
2344 f = chunkdata["filename"]
2344 f = chunkdata["filename"]
2345 self.ui.debug("adding %s revisions\n" % f)
2345 self.ui.debug("adding %s revisions\n" % f)
2346 pr()
2346 pr()
2347 fl = self.file(f)
2347 fl = self.file(f)
2348 o = len(fl)
2348 o = len(fl)
2349 if not fl.addgroup(source, revmap, trp):
2349 if not fl.addgroup(source, revmap, trp):
2350 raise util.Abort(_("received file revlog group is empty"))
2350 raise util.Abort(_("received file revlog group is empty"))
2351 revisions += len(fl) - o
2351 revisions += len(fl) - o
2352 files += 1
2352 files += 1
2353 if f in needfiles:
2353 if f in needfiles:
2354 needs = needfiles[f]
2354 needs = needfiles[f]
2355 for new in xrange(o, len(fl)):
2355 for new in xrange(o, len(fl)):
2356 n = fl.node(new)
2356 n = fl.node(new)
2357 if n in needs:
2357 if n in needs:
2358 needs.remove(n)
2358 needs.remove(n)
2359 if not needs:
2359 if not needs:
2360 del needfiles[f]
2360 del needfiles[f]
2361 self.ui.progress(_('files'), None)
2361 self.ui.progress(_('files'), None)
2362
2362
2363 for f, needs in needfiles.iteritems():
2363 for f, needs in needfiles.iteritems():
2364 fl = self.file(f)
2364 fl = self.file(f)
2365 for n in needs:
2365 for n in needs:
2366 try:
2366 try:
2367 fl.rev(n)
2367 fl.rev(n)
2368 except error.LookupError:
2368 except error.LookupError:
2369 raise util.Abort(
2369 raise util.Abort(
2370 _('missing file data for %s:%s - run hg verify') %
2370 _('missing file data for %s:%s - run hg verify') %
2371 (f, hex(n)))
2371 (f, hex(n)))
2372
2372
2373 dh = 0
2373 dh = 0
2374 if oldheads:
2374 if oldheads:
2375 heads = cl.heads()
2375 heads = cl.heads()
2376 dh = len(heads) - len(oldheads)
2376 dh = len(heads) - len(oldheads)
2377 for h in heads:
2377 for h in heads:
2378 if h not in oldheads and self[h].closesbranch():
2378 if h not in oldheads and self[h].closesbranch():
2379 dh -= 1
2379 dh -= 1
2380 htext = ""
2380 htext = ""
2381 if dh:
2381 if dh:
2382 htext = _(" (%+d heads)") % dh
2382 htext = _(" (%+d heads)") % dh
2383
2383
2384 self.ui.status(_("added %d changesets"
2384 self.ui.status(_("added %d changesets"
2385 " with %d changes to %d files%s\n")
2385 " with %d changes to %d files%s\n")
2386 % (changesets, revisions, files, htext))
2386 % (changesets, revisions, files, htext))
2387
2387
2388 if changesets > 0:
2388 if changesets > 0:
2389 p = lambda: cl.writepending() and self.root or ""
2389 p = lambda: cl.writepending() and self.root or ""
2390 self.hook('pretxnchangegroup', throw=True,
2390 self.hook('pretxnchangegroup', throw=True,
2391 node=hex(cl.node(clstart)), source=srctype,
2391 node=hex(cl.node(clstart)), source=srctype,
2392 url=url, pending=p)
2392 url=url, pending=p)
2393
2393
2394 added = [cl.node(r) for r in xrange(clstart, clend)]
2394 added = [cl.node(r) for r in xrange(clstart, clend)]
2395 publishing = self.ui.configbool('phases', 'publish', True)
2395 publishing = self.ui.configbool('phases', 'publish', True)
2396 if srctype == 'push':
2396 if srctype == 'push':
2397 # Old server can not push the boundary themself.
2397 # Old server can not push the boundary themself.
2398 # New server won't push the boundary if changeset already
2398 # New server won't push the boundary if changeset already
2399 # existed locally as secrete
2399 # existed locally as secrete
2400 #
2400 #
2401 # We should not use added here but the list of all change in
2401 # We should not use added here but the list of all change in
2402 # the bundle
2402 # the bundle
2403 if publishing:
2403 if publishing:
2404 phases.advanceboundary(self, phases.public, srccontent)
2404 phases.advanceboundary(self, phases.public, srccontent)
2405 else:
2405 else:
2406 phases.advanceboundary(self, phases.draft, srccontent)
2406 phases.advanceboundary(self, phases.draft, srccontent)
2407 phases.retractboundary(self, phases.draft, added)
2407 phases.retractboundary(self, phases.draft, added)
2408 elif srctype != 'strip':
2408 elif srctype != 'strip':
2409 # publishing only alter behavior during push
2409 # publishing only alter behavior during push
2410 #
2410 #
2411 # strip should not touch boundary at all
2411 # strip should not touch boundary at all
2412 phases.retractboundary(self, phases.draft, added)
2412 phases.retractboundary(self, phases.draft, added)
2413
2413
2414 # make changelog see real files again
2414 # make changelog see real files again
2415 cl.finalize(trp)
2415 cl.finalize(trp)
2416
2416
2417 tr.close()
2417 tr.close()
2418
2418
2419 if changesets > 0:
2419 if changesets > 0:
2420 def runhooks():
2420 def runhooks():
2421 # forcefully update the on-disk branch cache
2421 # forcefully update the on-disk branch cache
2422 self.ui.debug("updating the branch cache\n")
2422 self.ui.debug("updating the branch cache\n")
2423 self.updatebranchcache()
2423 self.updatebranchcache()
2424 self.hook("changegroup", node=hex(cl.node(clstart)),
2424 self.hook("changegroup", node=hex(cl.node(clstart)),
2425 source=srctype, url=url)
2425 source=srctype, url=url)
2426
2426
2427 for n in added:
2427 for n in added:
2428 self.hook("incoming", node=hex(n), source=srctype,
2428 self.hook("incoming", node=hex(n), source=srctype,
2429 url=url)
2429 url=url)
2430 self._afterlock(runhooks)
2430 self._afterlock(runhooks)
2431
2431
2432 finally:
2432 finally:
2433 tr.release()
2433 tr.release()
2434 # never return 0 here:
2434 # never return 0 here:
2435 if dh < 0:
2435 if dh < 0:
2436 return dh - 1
2436 return dh - 1
2437 else:
2437 else:
2438 return dh + 1
2438 return dh + 1
2439
2439
2440 def stream_in(self, remote, requirements):
2440 def stream_in(self, remote, requirements):
2441 lock = self.lock()
2441 lock = self.lock()
2442 try:
2442 try:
2443 fp = remote.stream_out()
2443 fp = remote.stream_out()
2444 l = fp.readline()
2444 l = fp.readline()
2445 try:
2445 try:
2446 resp = int(l)
2446 resp = int(l)
2447 except ValueError:
2447 except ValueError:
2448 raise error.ResponseError(
2448 raise error.ResponseError(
2449 _('unexpected response from remote server:'), l)
2449 _('unexpected response from remote server:'), l)
2450 if resp == 1:
2450 if resp == 1:
2451 raise util.Abort(_('operation forbidden by server'))
2451 raise util.Abort(_('operation forbidden by server'))
2452 elif resp == 2:
2452 elif resp == 2:
2453 raise util.Abort(_('locking the remote repository failed'))
2453 raise util.Abort(_('locking the remote repository failed'))
2454 elif resp != 0:
2454 elif resp != 0:
2455 raise util.Abort(_('the server sent an unknown error code'))
2455 raise util.Abort(_('the server sent an unknown error code'))
2456 self.ui.status(_('streaming all changes\n'))
2456 self.ui.status(_('streaming all changes\n'))
2457 l = fp.readline()
2457 l = fp.readline()
2458 try:
2458 try:
2459 total_files, total_bytes = map(int, l.split(' ', 1))
2459 total_files, total_bytes = map(int, l.split(' ', 1))
2460 except (ValueError, TypeError):
2460 except (ValueError, TypeError):
2461 raise error.ResponseError(
2461 raise error.ResponseError(
2462 _('unexpected response from remote server:'), l)
2462 _('unexpected response from remote server:'), l)
2463 self.ui.status(_('%d files to transfer, %s of data\n') %
2463 self.ui.status(_('%d files to transfer, %s of data\n') %
2464 (total_files, util.bytecount(total_bytes)))
2464 (total_files, util.bytecount(total_bytes)))
2465 handled_bytes = 0
2465 handled_bytes = 0
2466 self.ui.progress(_('clone'), 0, total=total_bytes)
2466 self.ui.progress(_('clone'), 0, total=total_bytes)
2467 start = time.time()
2467 start = time.time()
2468 for i in xrange(total_files):
2468 for i in xrange(total_files):
2469 # XXX doesn't support '\n' or '\r' in filenames
2469 # XXX doesn't support '\n' or '\r' in filenames
2470 l = fp.readline()
2470 l = fp.readline()
2471 try:
2471 try:
2472 name, size = l.split('\0', 1)
2472 name, size = l.split('\0', 1)
2473 size = int(size)
2473 size = int(size)
2474 except (ValueError, TypeError):
2474 except (ValueError, TypeError):
2475 raise error.ResponseError(
2475 raise error.ResponseError(
2476 _('unexpected response from remote server:'), l)
2476 _('unexpected response from remote server:'), l)
2477 if self.ui.debugflag:
2477 if self.ui.debugflag:
2478 self.ui.debug('adding %s (%s)\n' %
2478 self.ui.debug('adding %s (%s)\n' %
2479 (name, util.bytecount(size)))
2479 (name, util.bytecount(size)))
2480 # for backwards compat, name was partially encoded
2480 # for backwards compat, name was partially encoded
2481 ofp = self.sopener(store.decodedir(name), 'w')
2481 ofp = self.sopener(store.decodedir(name), 'w')
2482 for chunk in util.filechunkiter(fp, limit=size):
2482 for chunk in util.filechunkiter(fp, limit=size):
2483 handled_bytes += len(chunk)
2483 handled_bytes += len(chunk)
2484 self.ui.progress(_('clone'), handled_bytes,
2484 self.ui.progress(_('clone'), handled_bytes,
2485 total=total_bytes)
2485 total=total_bytes)
2486 ofp.write(chunk)
2486 ofp.write(chunk)
2487 ofp.close()
2487 ofp.close()
2488 elapsed = time.time() - start
2488 elapsed = time.time() - start
2489 if elapsed <= 0:
2489 if elapsed <= 0:
2490 elapsed = 0.001
2490 elapsed = 0.001
2491 self.ui.progress(_('clone'), None)
2491 self.ui.progress(_('clone'), None)
2492 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2492 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2493 (util.bytecount(total_bytes), elapsed,
2493 (util.bytecount(total_bytes), elapsed,
2494 util.bytecount(total_bytes / elapsed)))
2494 util.bytecount(total_bytes / elapsed)))
2495
2495
2496 # new requirements = old non-format requirements +
2496 # new requirements = old non-format requirements +
2497 # new format-related
2497 # new format-related
2498 # requirements from the streamed-in repository
2498 # requirements from the streamed-in repository
2499 requirements.update(set(self.requirements) - self.supportedformats)
2499 requirements.update(set(self.requirements) - self.supportedformats)
2500 self._applyrequirements(requirements)
2500 self._applyrequirements(requirements)
2501 self._writerequirements()
2501 self._writerequirements()
2502
2502
2503 self.invalidate()
2503 self.invalidate()
2504 return len(self.heads()) + 1
2504 return len(self.heads()) + 1
2505 finally:
2505 finally:
2506 lock.release()
2506 lock.release()
2507
2507
2508 def clone(self, remote, heads=[], stream=False):
2508 def clone(self, remote, heads=[], stream=False):
2509 '''clone remote repository.
2509 '''clone remote repository.
2510
2510
2511 keyword arguments:
2511 keyword arguments:
2512 heads: list of revs to clone (forces use of pull)
2512 heads: list of revs to clone (forces use of pull)
2513 stream: use streaming clone if possible'''
2513 stream: use streaming clone if possible'''
2514
2514
2515 # now, all clients that can request uncompressed clones can
2515 # now, all clients that can request uncompressed clones can
2516 # read repo formats supported by all servers that can serve
2516 # read repo formats supported by all servers that can serve
2517 # them.
2517 # them.
2518
2518
2519 # if revlog format changes, client will have to check version
2519 # if revlog format changes, client will have to check version
2520 # and format flags on "stream" capability, and use
2520 # and format flags on "stream" capability, and use
2521 # uncompressed only if compatible.
2521 # uncompressed only if compatible.
2522
2522
2523 if not stream:
2523 if not stream:
2524 # if the server explicitely prefer to stream (for fast LANs)
2524 # if the server explicitely prefer to stream (for fast LANs)
2525 stream = remote.capable('stream-preferred')
2525 stream = remote.capable('stream-preferred')
2526
2526
2527 if stream and not heads:
2527 if stream and not heads:
2528 # 'stream' means remote revlog format is revlogv1 only
2528 # 'stream' means remote revlog format is revlogv1 only
2529 if remote.capable('stream'):
2529 if remote.capable('stream'):
2530 return self.stream_in(remote, set(('revlogv1',)))
2530 return self.stream_in(remote, set(('revlogv1',)))
2531 # otherwise, 'streamreqs' contains the remote revlog format
2531 # otherwise, 'streamreqs' contains the remote revlog format
2532 streamreqs = remote.capable('streamreqs')
2532 streamreqs = remote.capable('streamreqs')
2533 if streamreqs:
2533 if streamreqs:
2534 streamreqs = set(streamreqs.split(','))
2534 streamreqs = set(streamreqs.split(','))
2535 # if we support it, stream in and adjust our requirements
2535 # if we support it, stream in and adjust our requirements
2536 if not streamreqs - self.supportedformats:
2536 if not streamreqs - self.supportedformats:
2537 return self.stream_in(remote, streamreqs)
2537 return self.stream_in(remote, streamreqs)
2538 return self.pull(remote, heads)
2538 return self.pull(remote, heads)
2539
2539
2540 def pushkey(self, namespace, key, old, new):
2540 def pushkey(self, namespace, key, old, new):
2541 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
2541 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
2542 old=old, new=new)
2542 old=old, new=new)
2543 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
2543 ret = pushkey.push(self, namespace, key, old, new)
2544 ret = pushkey.push(self, namespace, key, old, new)
2544 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
2545 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
2545 ret=ret)
2546 ret=ret)
2546 return ret
2547 return ret
2547
2548
2548 def listkeys(self, namespace):
2549 def listkeys(self, namespace):
2549 self.hook('prelistkeys', throw=True, namespace=namespace)
2550 self.hook('prelistkeys', throw=True, namespace=namespace)
2551 self.ui.debug('listing keys for "%s"\n' % namespace)
2550 values = pushkey.list(self, namespace)
2552 values = pushkey.list(self, namespace)
2551 self.hook('listkeys', namespace=namespace, values=values)
2553 self.hook('listkeys', namespace=namespace, values=values)
2552 return values
2554 return values
2553
2555
2554 def debugwireargs(self, one, two, three=None, four=None, five=None):
2556 def debugwireargs(self, one, two, three=None, four=None, five=None):
2555 '''used to test argument passing over the wire'''
2557 '''used to test argument passing over the wire'''
2556 return "%s %s %s %s %s" % (one, two, three, four, five)
2558 return "%s %s %s %s %s" % (one, two, three, four, five)
2557
2559
2558 def savecommitmessage(self, text):
2560 def savecommitmessage(self, text):
2559 fp = self.opener('last-message.txt', 'wb')
2561 fp = self.opener('last-message.txt', 'wb')
2560 try:
2562 try:
2561 fp.write(text)
2563 fp.write(text)
2562 finally:
2564 finally:
2563 fp.close()
2565 fp.close()
2564 return self.pathto(fp.name[len(self.root)+1:])
2566 return self.pathto(fp.name[len(self.root)+1:])
2565
2567
2566 # used to avoid circular references so destructors work
2568 # used to avoid circular references so destructors work
2567 def aftertrans(files):
2569 def aftertrans(files):
2568 renamefiles = [tuple(t) for t in files]
2570 renamefiles = [tuple(t) for t in files]
2569 def a():
2571 def a():
2570 for src, dest in renamefiles:
2572 for src, dest in renamefiles:
2571 try:
2573 try:
2572 util.rename(src, dest)
2574 util.rename(src, dest)
2573 except OSError: # journal file does not yet exist
2575 except OSError: # journal file does not yet exist
2574 pass
2576 pass
2575 return a
2577 return a
2576
2578
2577 def undoname(fn):
2579 def undoname(fn):
2578 base, name = os.path.split(fn)
2580 base, name = os.path.split(fn)
2579 assert name.startswith('journal')
2581 assert name.startswith('journal')
2580 return os.path.join(base, name.replace('journal', 'undo', 1))
2582 return os.path.join(base, name.replace('journal', 'undo', 1))
2581
2583
2582 def instance(ui, path, create):
2584 def instance(ui, path, create):
2583 return localrepository(ui, util.urllocalpath(path), create)
2585 return localrepository(ui, util.urllocalpath(path), create)
2584
2586
2585 def islocal(path):
2587 def islocal(path):
2586 return True
2588 return True
@@ -1,617 +1,619 b''
1 # wireproto.py - generic wire protocol support functions
1 # wireproto.py - generic wire protocol support functions
2 #
2 #
3 # Copyright 2005-2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import urllib, tempfile, os, sys
8 import urllib, tempfile, os, sys
9 from i18n import _
9 from i18n import _
10 from node import bin, hex
10 from node import bin, hex
11 import changegroup as changegroupmod
11 import changegroup as changegroupmod
12 import peer, error, encoding, util, store
12 import peer, error, encoding, util, store
13 import discovery, phases
13 import discovery, phases
14
14
15 # abstract batching support
15 # abstract batching support
16
16
17 class future(object):
17 class future(object):
18 '''placeholder for a value to be set later'''
18 '''placeholder for a value to be set later'''
19 def set(self, value):
19 def set(self, value):
20 if util.safehasattr(self, 'value'):
20 if util.safehasattr(self, 'value'):
21 raise error.RepoError("future is already set")
21 raise error.RepoError("future is already set")
22 self.value = value
22 self.value = value
23
23
24 class batcher(object):
24 class batcher(object):
25 '''base class for batches of commands submittable in a single request
25 '''base class for batches of commands submittable in a single request
26
26
27 All methods invoked on instances of this class are simply queued and
27 All methods invoked on instances of this class are simply queued and
28 return a a future for the result. Once you call submit(), all the queued
28 return a a future for the result. Once you call submit(), all the queued
29 calls are performed and the results set in their respective futures.
29 calls are performed and the results set in their respective futures.
30 '''
30 '''
31 def __init__(self):
31 def __init__(self):
32 self.calls = []
32 self.calls = []
33 def __getattr__(self, name):
33 def __getattr__(self, name):
34 def call(*args, **opts):
34 def call(*args, **opts):
35 resref = future()
35 resref = future()
36 self.calls.append((name, args, opts, resref,))
36 self.calls.append((name, args, opts, resref,))
37 return resref
37 return resref
38 return call
38 return call
39 def submit(self):
39 def submit(self):
40 pass
40 pass
41
41
42 class localbatch(batcher):
42 class localbatch(batcher):
43 '''performs the queued calls directly'''
43 '''performs the queued calls directly'''
44 def __init__(self, local):
44 def __init__(self, local):
45 batcher.__init__(self)
45 batcher.__init__(self)
46 self.local = local
46 self.local = local
47 def submit(self):
47 def submit(self):
48 for name, args, opts, resref in self.calls:
48 for name, args, opts, resref in self.calls:
49 resref.set(getattr(self.local, name)(*args, **opts))
49 resref.set(getattr(self.local, name)(*args, **opts))
50
50
51 class remotebatch(batcher):
51 class remotebatch(batcher):
52 '''batches the queued calls; uses as few roundtrips as possible'''
52 '''batches the queued calls; uses as few roundtrips as possible'''
53 def __init__(self, remote):
53 def __init__(self, remote):
54 '''remote must support _submitbatch(encbatch) and
54 '''remote must support _submitbatch(encbatch) and
55 _submitone(op, encargs)'''
55 _submitone(op, encargs)'''
56 batcher.__init__(self)
56 batcher.__init__(self)
57 self.remote = remote
57 self.remote = remote
58 def submit(self):
58 def submit(self):
59 req, rsp = [], []
59 req, rsp = [], []
60 for name, args, opts, resref in self.calls:
60 for name, args, opts, resref in self.calls:
61 mtd = getattr(self.remote, name)
61 mtd = getattr(self.remote, name)
62 batchablefn = getattr(mtd, 'batchable', None)
62 batchablefn = getattr(mtd, 'batchable', None)
63 if batchablefn is not None:
63 if batchablefn is not None:
64 batchable = batchablefn(mtd.im_self, *args, **opts)
64 batchable = batchablefn(mtd.im_self, *args, **opts)
65 encargsorres, encresref = batchable.next()
65 encargsorres, encresref = batchable.next()
66 if encresref:
66 if encresref:
67 req.append((name, encargsorres,))
67 req.append((name, encargsorres,))
68 rsp.append((batchable, encresref, resref,))
68 rsp.append((batchable, encresref, resref,))
69 else:
69 else:
70 resref.set(encargsorres)
70 resref.set(encargsorres)
71 else:
71 else:
72 if req:
72 if req:
73 self._submitreq(req, rsp)
73 self._submitreq(req, rsp)
74 req, rsp = [], []
74 req, rsp = [], []
75 resref.set(mtd(*args, **opts))
75 resref.set(mtd(*args, **opts))
76 if req:
76 if req:
77 self._submitreq(req, rsp)
77 self._submitreq(req, rsp)
78 def _submitreq(self, req, rsp):
78 def _submitreq(self, req, rsp):
79 encresults = self.remote._submitbatch(req)
79 encresults = self.remote._submitbatch(req)
80 for encres, r in zip(encresults, rsp):
80 for encres, r in zip(encresults, rsp):
81 batchable, encresref, resref = r
81 batchable, encresref, resref = r
82 encresref.set(encres)
82 encresref.set(encres)
83 resref.set(batchable.next())
83 resref.set(batchable.next())
84
84
85 def batchable(f):
85 def batchable(f):
86 '''annotation for batchable methods
86 '''annotation for batchable methods
87
87
88 Such methods must implement a coroutine as follows:
88 Such methods must implement a coroutine as follows:
89
89
90 @batchable
90 @batchable
91 def sample(self, one, two=None):
91 def sample(self, one, two=None):
92 # Handle locally computable results first:
92 # Handle locally computable results first:
93 if not one:
93 if not one:
94 yield "a local result", None
94 yield "a local result", None
95 # Build list of encoded arguments suitable for your wire protocol:
95 # Build list of encoded arguments suitable for your wire protocol:
96 encargs = [('one', encode(one),), ('two', encode(two),)]
96 encargs = [('one', encode(one),), ('two', encode(two),)]
97 # Create future for injection of encoded result:
97 # Create future for injection of encoded result:
98 encresref = future()
98 encresref = future()
99 # Return encoded arguments and future:
99 # Return encoded arguments and future:
100 yield encargs, encresref
100 yield encargs, encresref
101 # Assuming the future to be filled with the result from the batched
101 # Assuming the future to be filled with the result from the batched
102 # request now. Decode it:
102 # request now. Decode it:
103 yield decode(encresref.value)
103 yield decode(encresref.value)
104
104
105 The decorator returns a function which wraps this coroutine as a plain
105 The decorator returns a function which wraps this coroutine as a plain
106 method, but adds the original method as an attribute called "batchable",
106 method, but adds the original method as an attribute called "batchable",
107 which is used by remotebatch to split the call into separate encoding and
107 which is used by remotebatch to split the call into separate encoding and
108 decoding phases.
108 decoding phases.
109 '''
109 '''
110 def plain(*args, **opts):
110 def plain(*args, **opts):
111 batchable = f(*args, **opts)
111 batchable = f(*args, **opts)
112 encargsorres, encresref = batchable.next()
112 encargsorres, encresref = batchable.next()
113 if not encresref:
113 if not encresref:
114 return encargsorres # a local result in this case
114 return encargsorres # a local result in this case
115 self = args[0]
115 self = args[0]
116 encresref.set(self._submitone(f.func_name, encargsorres))
116 encresref.set(self._submitone(f.func_name, encargsorres))
117 return batchable.next()
117 return batchable.next()
118 setattr(plain, 'batchable', f)
118 setattr(plain, 'batchable', f)
119 return plain
119 return plain
120
120
121 # list of nodes encoding / decoding
121 # list of nodes encoding / decoding
122
122
123 def decodelist(l, sep=' '):
123 def decodelist(l, sep=' '):
124 if l:
124 if l:
125 return map(bin, l.split(sep))
125 return map(bin, l.split(sep))
126 return []
126 return []
127
127
128 def encodelist(l, sep=' '):
128 def encodelist(l, sep=' '):
129 return sep.join(map(hex, l))
129 return sep.join(map(hex, l))
130
130
131 # batched call argument encoding
131 # batched call argument encoding
132
132
133 def escapearg(plain):
133 def escapearg(plain):
134 return (plain
134 return (plain
135 .replace(':', '::')
135 .replace(':', '::')
136 .replace(',', ':,')
136 .replace(',', ':,')
137 .replace(';', ':;')
137 .replace(';', ':;')
138 .replace('=', ':='))
138 .replace('=', ':='))
139
139
140 def unescapearg(escaped):
140 def unescapearg(escaped):
141 return (escaped
141 return (escaped
142 .replace(':=', '=')
142 .replace(':=', '=')
143 .replace(':;', ';')
143 .replace(':;', ';')
144 .replace(':,', ',')
144 .replace(':,', ',')
145 .replace('::', ':'))
145 .replace('::', ':'))
146
146
147 # client side
147 # client side
148
148
149 def todict(**args):
149 def todict(**args):
150 return args
150 return args
151
151
152 class wirepeer(peer.peerrepository):
152 class wirepeer(peer.peerrepository):
153
153
154 def batch(self):
154 def batch(self):
155 return remotebatch(self)
155 return remotebatch(self)
156 def _submitbatch(self, req):
156 def _submitbatch(self, req):
157 cmds = []
157 cmds = []
158 for op, argsdict in req:
158 for op, argsdict in req:
159 args = ','.join('%s=%s' % p for p in argsdict.iteritems())
159 args = ','.join('%s=%s' % p for p in argsdict.iteritems())
160 cmds.append('%s %s' % (op, args))
160 cmds.append('%s %s' % (op, args))
161 rsp = self._call("batch", cmds=';'.join(cmds))
161 rsp = self._call("batch", cmds=';'.join(cmds))
162 return rsp.split(';')
162 return rsp.split(';')
163 def _submitone(self, op, args):
163 def _submitone(self, op, args):
164 return self._call(op, **args)
164 return self._call(op, **args)
165
165
166 @batchable
166 @batchable
167 def lookup(self, key):
167 def lookup(self, key):
168 self.requirecap('lookup', _('look up remote revision'))
168 self.requirecap('lookup', _('look up remote revision'))
169 f = future()
169 f = future()
170 yield todict(key=encoding.fromlocal(key)), f
170 yield todict(key=encoding.fromlocal(key)), f
171 d = f.value
171 d = f.value
172 success, data = d[:-1].split(" ", 1)
172 success, data = d[:-1].split(" ", 1)
173 if int(success):
173 if int(success):
174 yield bin(data)
174 yield bin(data)
175 self._abort(error.RepoError(data))
175 self._abort(error.RepoError(data))
176
176
177 @batchable
177 @batchable
178 def heads(self):
178 def heads(self):
179 f = future()
179 f = future()
180 yield {}, f
180 yield {}, f
181 d = f.value
181 d = f.value
182 try:
182 try:
183 yield decodelist(d[:-1])
183 yield decodelist(d[:-1])
184 except ValueError:
184 except ValueError:
185 self._abort(error.ResponseError(_("unexpected response:"), d))
185 self._abort(error.ResponseError(_("unexpected response:"), d))
186
186
187 @batchable
187 @batchable
188 def known(self, nodes):
188 def known(self, nodes):
189 f = future()
189 f = future()
190 yield todict(nodes=encodelist(nodes)), f
190 yield todict(nodes=encodelist(nodes)), f
191 d = f.value
191 d = f.value
192 try:
192 try:
193 yield [bool(int(f)) for f in d]
193 yield [bool(int(f)) for f in d]
194 except ValueError:
194 except ValueError:
195 self._abort(error.ResponseError(_("unexpected response:"), d))
195 self._abort(error.ResponseError(_("unexpected response:"), d))
196
196
197 @batchable
197 @batchable
198 def branchmap(self):
198 def branchmap(self):
199 f = future()
199 f = future()
200 yield {}, f
200 yield {}, f
201 d = f.value
201 d = f.value
202 try:
202 try:
203 branchmap = {}
203 branchmap = {}
204 for branchpart in d.splitlines():
204 for branchpart in d.splitlines():
205 branchname, branchheads = branchpart.split(' ', 1)
205 branchname, branchheads = branchpart.split(' ', 1)
206 branchname = encoding.tolocal(urllib.unquote(branchname))
206 branchname = encoding.tolocal(urllib.unquote(branchname))
207 branchheads = decodelist(branchheads)
207 branchheads = decodelist(branchheads)
208 branchmap[branchname] = branchheads
208 branchmap[branchname] = branchheads
209 yield branchmap
209 yield branchmap
210 except TypeError:
210 except TypeError:
211 self._abort(error.ResponseError(_("unexpected response:"), d))
211 self._abort(error.ResponseError(_("unexpected response:"), d))
212
212
213 def branches(self, nodes):
213 def branches(self, nodes):
214 n = encodelist(nodes)
214 n = encodelist(nodes)
215 d = self._call("branches", nodes=n)
215 d = self._call("branches", nodes=n)
216 try:
216 try:
217 br = [tuple(decodelist(b)) for b in d.splitlines()]
217 br = [tuple(decodelist(b)) for b in d.splitlines()]
218 return br
218 return br
219 except ValueError:
219 except ValueError:
220 self._abort(error.ResponseError(_("unexpected response:"), d))
220 self._abort(error.ResponseError(_("unexpected response:"), d))
221
221
222 def between(self, pairs):
222 def between(self, pairs):
223 batch = 8 # avoid giant requests
223 batch = 8 # avoid giant requests
224 r = []
224 r = []
225 for i in xrange(0, len(pairs), batch):
225 for i in xrange(0, len(pairs), batch):
226 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
226 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
227 d = self._call("between", pairs=n)
227 d = self._call("between", pairs=n)
228 try:
228 try:
229 r.extend(l and decodelist(l) or [] for l in d.splitlines())
229 r.extend(l and decodelist(l) or [] for l in d.splitlines())
230 except ValueError:
230 except ValueError:
231 self._abort(error.ResponseError(_("unexpected response:"), d))
231 self._abort(error.ResponseError(_("unexpected response:"), d))
232 return r
232 return r
233
233
234 @batchable
234 @batchable
235 def pushkey(self, namespace, key, old, new):
235 def pushkey(self, namespace, key, old, new):
236 if not self.capable('pushkey'):
236 if not self.capable('pushkey'):
237 yield False, None
237 yield False, None
238 f = future()
238 f = future()
239 self.ui.debug('preparing pushkey for "%s:%s"\n' % (namespace, key))
239 yield todict(namespace=encoding.fromlocal(namespace),
240 yield todict(namespace=encoding.fromlocal(namespace),
240 key=encoding.fromlocal(key),
241 key=encoding.fromlocal(key),
241 old=encoding.fromlocal(old),
242 old=encoding.fromlocal(old),
242 new=encoding.fromlocal(new)), f
243 new=encoding.fromlocal(new)), f
243 d = f.value
244 d = f.value
244 d, output = d.split('\n', 1)
245 d, output = d.split('\n', 1)
245 try:
246 try:
246 d = bool(int(d))
247 d = bool(int(d))
247 except ValueError:
248 except ValueError:
248 raise error.ResponseError(
249 raise error.ResponseError(
249 _('push failed (unexpected response):'), d)
250 _('push failed (unexpected response):'), d)
250 for l in output.splitlines(True):
251 for l in output.splitlines(True):
251 self.ui.status(_('remote: '), l)
252 self.ui.status(_('remote: '), l)
252 yield d
253 yield d
253
254
254 @batchable
255 @batchable
255 def listkeys(self, namespace):
256 def listkeys(self, namespace):
256 if not self.capable('pushkey'):
257 if not self.capable('pushkey'):
257 yield {}, None
258 yield {}, None
258 f = future()
259 f = future()
260 self.ui.debug('preparing listkeys for "%s"\n' % namespace)
259 yield todict(namespace=encoding.fromlocal(namespace)), f
261 yield todict(namespace=encoding.fromlocal(namespace)), f
260 d = f.value
262 d = f.value
261 r = {}
263 r = {}
262 for l in d.splitlines():
264 for l in d.splitlines():
263 k, v = l.split('\t')
265 k, v = l.split('\t')
264 r[encoding.tolocal(k)] = encoding.tolocal(v)
266 r[encoding.tolocal(k)] = encoding.tolocal(v)
265 yield r
267 yield r
266
268
267 def stream_out(self):
269 def stream_out(self):
268 return self._callstream('stream_out')
270 return self._callstream('stream_out')
269
271
270 def changegroup(self, nodes, kind):
272 def changegroup(self, nodes, kind):
271 n = encodelist(nodes)
273 n = encodelist(nodes)
272 f = self._callstream("changegroup", roots=n)
274 f = self._callstream("changegroup", roots=n)
273 return changegroupmod.unbundle10(self._decompress(f), 'UN')
275 return changegroupmod.unbundle10(self._decompress(f), 'UN')
274
276
275 def changegroupsubset(self, bases, heads, kind):
277 def changegroupsubset(self, bases, heads, kind):
276 self.requirecap('changegroupsubset', _('look up remote changes'))
278 self.requirecap('changegroupsubset', _('look up remote changes'))
277 bases = encodelist(bases)
279 bases = encodelist(bases)
278 heads = encodelist(heads)
280 heads = encodelist(heads)
279 f = self._callstream("changegroupsubset",
281 f = self._callstream("changegroupsubset",
280 bases=bases, heads=heads)
282 bases=bases, heads=heads)
281 return changegroupmod.unbundle10(self._decompress(f), 'UN')
283 return changegroupmod.unbundle10(self._decompress(f), 'UN')
282
284
283 def getbundle(self, source, heads=None, common=None):
285 def getbundle(self, source, heads=None, common=None):
284 self.requirecap('getbundle', _('look up remote changes'))
286 self.requirecap('getbundle', _('look up remote changes'))
285 opts = {}
287 opts = {}
286 if heads is not None:
288 if heads is not None:
287 opts['heads'] = encodelist(heads)
289 opts['heads'] = encodelist(heads)
288 if common is not None:
290 if common is not None:
289 opts['common'] = encodelist(common)
291 opts['common'] = encodelist(common)
290 f = self._callstream("getbundle", **opts)
292 f = self._callstream("getbundle", **opts)
291 return changegroupmod.unbundle10(self._decompress(f), 'UN')
293 return changegroupmod.unbundle10(self._decompress(f), 'UN')
292
294
293 def unbundle(self, cg, heads, source):
295 def unbundle(self, cg, heads, source):
294 '''Send cg (a readable file-like object representing the
296 '''Send cg (a readable file-like object representing the
295 changegroup to push, typically a chunkbuffer object) to the
297 changegroup to push, typically a chunkbuffer object) to the
296 remote server as a bundle. Return an integer indicating the
298 remote server as a bundle. Return an integer indicating the
297 result of the push (see localrepository.addchangegroup()).'''
299 result of the push (see localrepository.addchangegroup()).'''
298
300
299 if heads != ['force'] and self.capable('unbundlehash'):
301 if heads != ['force'] and self.capable('unbundlehash'):
300 heads = encodelist(['hashed',
302 heads = encodelist(['hashed',
301 util.sha1(''.join(sorted(heads))).digest()])
303 util.sha1(''.join(sorted(heads))).digest()])
302 else:
304 else:
303 heads = encodelist(heads)
305 heads = encodelist(heads)
304
306
305 ret, output = self._callpush("unbundle", cg, heads=heads)
307 ret, output = self._callpush("unbundle", cg, heads=heads)
306 if ret == "":
308 if ret == "":
307 raise error.ResponseError(
309 raise error.ResponseError(
308 _('push failed:'), output)
310 _('push failed:'), output)
309 try:
311 try:
310 ret = int(ret)
312 ret = int(ret)
311 except ValueError:
313 except ValueError:
312 raise error.ResponseError(
314 raise error.ResponseError(
313 _('push failed (unexpected response):'), ret)
315 _('push failed (unexpected response):'), ret)
314
316
315 for l in output.splitlines(True):
317 for l in output.splitlines(True):
316 self.ui.status(_('remote: '), l)
318 self.ui.status(_('remote: '), l)
317 return ret
319 return ret
318
320
319 def debugwireargs(self, one, two, three=None, four=None, five=None):
321 def debugwireargs(self, one, two, three=None, four=None, five=None):
320 # don't pass optional arguments left at their default value
322 # don't pass optional arguments left at their default value
321 opts = {}
323 opts = {}
322 if three is not None:
324 if three is not None:
323 opts['three'] = three
325 opts['three'] = three
324 if four is not None:
326 if four is not None:
325 opts['four'] = four
327 opts['four'] = four
326 return self._call('debugwireargs', one=one, two=two, **opts)
328 return self._call('debugwireargs', one=one, two=two, **opts)
327
329
328 # server side
330 # server side
329
331
330 class streamres(object):
332 class streamres(object):
331 def __init__(self, gen):
333 def __init__(self, gen):
332 self.gen = gen
334 self.gen = gen
333
335
334 class pushres(object):
336 class pushres(object):
335 def __init__(self, res):
337 def __init__(self, res):
336 self.res = res
338 self.res = res
337
339
338 class pusherr(object):
340 class pusherr(object):
339 def __init__(self, res):
341 def __init__(self, res):
340 self.res = res
342 self.res = res
341
343
342 class ooberror(object):
344 class ooberror(object):
343 def __init__(self, message):
345 def __init__(self, message):
344 self.message = message
346 self.message = message
345
347
346 def dispatch(repo, proto, command):
348 def dispatch(repo, proto, command):
347 func, spec = commands[command]
349 func, spec = commands[command]
348 args = proto.getargs(spec)
350 args = proto.getargs(spec)
349 return func(repo, proto, *args)
351 return func(repo, proto, *args)
350
352
351 def options(cmd, keys, others):
353 def options(cmd, keys, others):
352 opts = {}
354 opts = {}
353 for k in keys:
355 for k in keys:
354 if k in others:
356 if k in others:
355 opts[k] = others[k]
357 opts[k] = others[k]
356 del others[k]
358 del others[k]
357 if others:
359 if others:
358 sys.stderr.write("abort: %s got unexpected arguments %s\n"
360 sys.stderr.write("abort: %s got unexpected arguments %s\n"
359 % (cmd, ",".join(others)))
361 % (cmd, ",".join(others)))
360 return opts
362 return opts
361
363
362 def batch(repo, proto, cmds, others):
364 def batch(repo, proto, cmds, others):
363 res = []
365 res = []
364 for pair in cmds.split(';'):
366 for pair in cmds.split(';'):
365 op, args = pair.split(' ', 1)
367 op, args = pair.split(' ', 1)
366 vals = {}
368 vals = {}
367 for a in args.split(','):
369 for a in args.split(','):
368 if a:
370 if a:
369 n, v = a.split('=')
371 n, v = a.split('=')
370 vals[n] = unescapearg(v)
372 vals[n] = unescapearg(v)
371 func, spec = commands[op]
373 func, spec = commands[op]
372 if spec:
374 if spec:
373 keys = spec.split()
375 keys = spec.split()
374 data = {}
376 data = {}
375 for k in keys:
377 for k in keys:
376 if k == '*':
378 if k == '*':
377 star = {}
379 star = {}
378 for key in vals.keys():
380 for key in vals.keys():
379 if key not in keys:
381 if key not in keys:
380 star[key] = vals[key]
382 star[key] = vals[key]
381 data['*'] = star
383 data['*'] = star
382 else:
384 else:
383 data[k] = vals[k]
385 data[k] = vals[k]
384 result = func(repo, proto, *[data[k] for k in keys])
386 result = func(repo, proto, *[data[k] for k in keys])
385 else:
387 else:
386 result = func(repo, proto)
388 result = func(repo, proto)
387 if isinstance(result, ooberror):
389 if isinstance(result, ooberror):
388 return result
390 return result
389 res.append(escapearg(result))
391 res.append(escapearg(result))
390 return ';'.join(res)
392 return ';'.join(res)
391
393
392 def between(repo, proto, pairs):
394 def between(repo, proto, pairs):
393 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
395 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
394 r = []
396 r = []
395 for b in repo.between(pairs):
397 for b in repo.between(pairs):
396 r.append(encodelist(b) + "\n")
398 r.append(encodelist(b) + "\n")
397 return "".join(r)
399 return "".join(r)
398
400
399 def branchmap(repo, proto):
401 def branchmap(repo, proto):
400 branchmap = discovery.visiblebranchmap(repo)
402 branchmap = discovery.visiblebranchmap(repo)
401 heads = []
403 heads = []
402 for branch, nodes in branchmap.iteritems():
404 for branch, nodes in branchmap.iteritems():
403 branchname = urllib.quote(encoding.fromlocal(branch))
405 branchname = urllib.quote(encoding.fromlocal(branch))
404 branchnodes = encodelist(nodes)
406 branchnodes = encodelist(nodes)
405 heads.append('%s %s' % (branchname, branchnodes))
407 heads.append('%s %s' % (branchname, branchnodes))
406 return '\n'.join(heads)
408 return '\n'.join(heads)
407
409
408 def branches(repo, proto, nodes):
410 def branches(repo, proto, nodes):
409 nodes = decodelist(nodes)
411 nodes = decodelist(nodes)
410 r = []
412 r = []
411 for b in repo.branches(nodes):
413 for b in repo.branches(nodes):
412 r.append(encodelist(b) + "\n")
414 r.append(encodelist(b) + "\n")
413 return "".join(r)
415 return "".join(r)
414
416
415 def capabilities(repo, proto):
417 def capabilities(repo, proto):
416 caps = ('lookup changegroupsubset branchmap pushkey known getbundle '
418 caps = ('lookup changegroupsubset branchmap pushkey known getbundle '
417 'unbundlehash batch').split()
419 'unbundlehash batch').split()
418 if _allowstream(repo.ui):
420 if _allowstream(repo.ui):
419 if repo.ui.configbool('server', 'preferuncompressed', False):
421 if repo.ui.configbool('server', 'preferuncompressed', False):
420 caps.append('stream-preferred')
422 caps.append('stream-preferred')
421 requiredformats = repo.requirements & repo.supportedformats
423 requiredformats = repo.requirements & repo.supportedformats
422 # if our local revlogs are just revlogv1, add 'stream' cap
424 # if our local revlogs are just revlogv1, add 'stream' cap
423 if not requiredformats - set(('revlogv1',)):
425 if not requiredformats - set(('revlogv1',)):
424 caps.append('stream')
426 caps.append('stream')
425 # otherwise, add 'streamreqs' detailing our local revlog format
427 # otherwise, add 'streamreqs' detailing our local revlog format
426 else:
428 else:
427 caps.append('streamreqs=%s' % ','.join(requiredformats))
429 caps.append('streamreqs=%s' % ','.join(requiredformats))
428 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
430 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
429 caps.append('httpheader=1024')
431 caps.append('httpheader=1024')
430 return ' '.join(caps)
432 return ' '.join(caps)
431
433
432 def changegroup(repo, proto, roots):
434 def changegroup(repo, proto, roots):
433 nodes = decodelist(roots)
435 nodes = decodelist(roots)
434 cg = repo.changegroup(nodes, 'serve')
436 cg = repo.changegroup(nodes, 'serve')
435 return streamres(proto.groupchunks(cg))
437 return streamres(proto.groupchunks(cg))
436
438
437 def changegroupsubset(repo, proto, bases, heads):
439 def changegroupsubset(repo, proto, bases, heads):
438 bases = decodelist(bases)
440 bases = decodelist(bases)
439 heads = decodelist(heads)
441 heads = decodelist(heads)
440 cg = repo.changegroupsubset(bases, heads, 'serve')
442 cg = repo.changegroupsubset(bases, heads, 'serve')
441 return streamres(proto.groupchunks(cg))
443 return streamres(proto.groupchunks(cg))
442
444
443 def debugwireargs(repo, proto, one, two, others):
445 def debugwireargs(repo, proto, one, two, others):
444 # only accept optional args from the known set
446 # only accept optional args from the known set
445 opts = options('debugwireargs', ['three', 'four'], others)
447 opts = options('debugwireargs', ['three', 'four'], others)
446 return repo.debugwireargs(one, two, **opts)
448 return repo.debugwireargs(one, two, **opts)
447
449
448 def getbundle(repo, proto, others):
450 def getbundle(repo, proto, others):
449 opts = options('getbundle', ['heads', 'common'], others)
451 opts = options('getbundle', ['heads', 'common'], others)
450 for k, v in opts.iteritems():
452 for k, v in opts.iteritems():
451 opts[k] = decodelist(v)
453 opts[k] = decodelist(v)
452 cg = repo.getbundle('serve', **opts)
454 cg = repo.getbundle('serve', **opts)
453 return streamres(proto.groupchunks(cg))
455 return streamres(proto.groupchunks(cg))
454
456
455 def heads(repo, proto):
457 def heads(repo, proto):
456 h = discovery.visibleheads(repo)
458 h = discovery.visibleheads(repo)
457 return encodelist(h) + "\n"
459 return encodelist(h) + "\n"
458
460
459 def hello(repo, proto):
461 def hello(repo, proto):
460 '''the hello command returns a set of lines describing various
462 '''the hello command returns a set of lines describing various
461 interesting things about the server, in an RFC822-like format.
463 interesting things about the server, in an RFC822-like format.
462 Currently the only one defined is "capabilities", which
464 Currently the only one defined is "capabilities", which
463 consists of a line in the form:
465 consists of a line in the form:
464
466
465 capabilities: space separated list of tokens
467 capabilities: space separated list of tokens
466 '''
468 '''
467 return "capabilities: %s\n" % (capabilities(repo, proto))
469 return "capabilities: %s\n" % (capabilities(repo, proto))
468
470
469 def listkeys(repo, proto, namespace):
471 def listkeys(repo, proto, namespace):
470 d = repo.listkeys(encoding.tolocal(namespace)).items()
472 d = repo.listkeys(encoding.tolocal(namespace)).items()
471 t = '\n'.join(['%s\t%s' % (encoding.fromlocal(k), encoding.fromlocal(v))
473 t = '\n'.join(['%s\t%s' % (encoding.fromlocal(k), encoding.fromlocal(v))
472 for k, v in d])
474 for k, v in d])
473 return t
475 return t
474
476
475 def lookup(repo, proto, key):
477 def lookup(repo, proto, key):
476 try:
478 try:
477 k = encoding.tolocal(key)
479 k = encoding.tolocal(key)
478 c = repo[k]
480 c = repo[k]
479 if c.phase() == phases.secret:
481 if c.phase() == phases.secret:
480 raise error.RepoLookupError(_("unknown revision '%s'") % k)
482 raise error.RepoLookupError(_("unknown revision '%s'") % k)
481 r = c.hex()
483 r = c.hex()
482 success = 1
484 success = 1
483 except Exception, inst:
485 except Exception, inst:
484 r = str(inst)
486 r = str(inst)
485 success = 0
487 success = 0
486 return "%s %s\n" % (success, r)
488 return "%s %s\n" % (success, r)
487
489
488 def known(repo, proto, nodes, others):
490 def known(repo, proto, nodes, others):
489 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
491 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
490
492
491 def pushkey(repo, proto, namespace, key, old, new):
493 def pushkey(repo, proto, namespace, key, old, new):
492 # compatibility with pre-1.8 clients which were accidentally
494 # compatibility with pre-1.8 clients which were accidentally
493 # sending raw binary nodes rather than utf-8-encoded hex
495 # sending raw binary nodes rather than utf-8-encoded hex
494 if len(new) == 20 and new.encode('string-escape') != new:
496 if len(new) == 20 and new.encode('string-escape') != new:
495 # looks like it could be a binary node
497 # looks like it could be a binary node
496 try:
498 try:
497 new.decode('utf-8')
499 new.decode('utf-8')
498 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
500 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
499 except UnicodeDecodeError:
501 except UnicodeDecodeError:
500 pass # binary, leave unmodified
502 pass # binary, leave unmodified
501 else:
503 else:
502 new = encoding.tolocal(new) # normal path
504 new = encoding.tolocal(new) # normal path
503
505
504 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
506 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
505 encoding.tolocal(old), new)
507 encoding.tolocal(old), new)
506 return '%s\n' % int(r)
508 return '%s\n' % int(r)
507
509
508 def _allowstream(ui):
510 def _allowstream(ui):
509 return ui.configbool('server', 'uncompressed', True, untrusted=True)
511 return ui.configbool('server', 'uncompressed', True, untrusted=True)
510
512
511 def stream(repo, proto):
513 def stream(repo, proto):
512 '''If the server supports streaming clone, it advertises the "stream"
514 '''If the server supports streaming clone, it advertises the "stream"
513 capability with a value representing the version and flags of the repo
515 capability with a value representing the version and flags of the repo
514 it is serving. Client checks to see if it understands the format.
516 it is serving. Client checks to see if it understands the format.
515
517
516 The format is simple: the server writes out a line with the amount
518 The format is simple: the server writes out a line with the amount
517 of files, then the total amount of bytes to be transfered (separated
519 of files, then the total amount of bytes to be transfered (separated
518 by a space). Then, for each file, the server first writes the filename
520 by a space). Then, for each file, the server first writes the filename
519 and filesize (separated by the null character), then the file contents.
521 and filesize (separated by the null character), then the file contents.
520 '''
522 '''
521
523
522 if not _allowstream(repo.ui):
524 if not _allowstream(repo.ui):
523 return '1\n'
525 return '1\n'
524
526
525 entries = []
527 entries = []
526 total_bytes = 0
528 total_bytes = 0
527 try:
529 try:
528 # get consistent snapshot of repo, lock during scan
530 # get consistent snapshot of repo, lock during scan
529 lock = repo.lock()
531 lock = repo.lock()
530 try:
532 try:
531 repo.ui.debug('scanning\n')
533 repo.ui.debug('scanning\n')
532 for name, ename, size in repo.store.walk():
534 for name, ename, size in repo.store.walk():
533 entries.append((name, size))
535 entries.append((name, size))
534 total_bytes += size
536 total_bytes += size
535 finally:
537 finally:
536 lock.release()
538 lock.release()
537 except error.LockError:
539 except error.LockError:
538 return '2\n' # error: 2
540 return '2\n' # error: 2
539
541
540 def streamer(repo, entries, total):
542 def streamer(repo, entries, total):
541 '''stream out all metadata files in repository.'''
543 '''stream out all metadata files in repository.'''
542 yield '0\n' # success
544 yield '0\n' # success
543 repo.ui.debug('%d files, %d bytes to transfer\n' %
545 repo.ui.debug('%d files, %d bytes to transfer\n' %
544 (len(entries), total_bytes))
546 (len(entries), total_bytes))
545 yield '%d %d\n' % (len(entries), total_bytes)
547 yield '%d %d\n' % (len(entries), total_bytes)
546 for name, size in entries:
548 for name, size in entries:
547 repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
549 repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
548 # partially encode name over the wire for backwards compat
550 # partially encode name over the wire for backwards compat
549 yield '%s\0%d\n' % (store.encodedir(name), size)
551 yield '%s\0%d\n' % (store.encodedir(name), size)
550 for chunk in util.filechunkiter(repo.sopener(name), limit=size):
552 for chunk in util.filechunkiter(repo.sopener(name), limit=size):
551 yield chunk
553 yield chunk
552
554
553 return streamres(streamer(repo, entries, total_bytes))
555 return streamres(streamer(repo, entries, total_bytes))
554
556
555 def unbundle(repo, proto, heads):
557 def unbundle(repo, proto, heads):
556 their_heads = decodelist(heads)
558 their_heads = decodelist(heads)
557
559
558 def check_heads():
560 def check_heads():
559 heads = discovery.visibleheads(repo)
561 heads = discovery.visibleheads(repo)
560 heads_hash = util.sha1(''.join(sorted(heads))).digest()
562 heads_hash = util.sha1(''.join(sorted(heads))).digest()
561 return (their_heads == ['force'] or their_heads == heads or
563 return (their_heads == ['force'] or their_heads == heads or
562 their_heads == ['hashed', heads_hash])
564 their_heads == ['hashed', heads_hash])
563
565
564 proto.redirect()
566 proto.redirect()
565
567
566 # fail early if possible
568 # fail early if possible
567 if not check_heads():
569 if not check_heads():
568 return pusherr('unsynced changes')
570 return pusherr('unsynced changes')
569
571
570 # write bundle data to temporary file because it can be big
572 # write bundle data to temporary file because it can be big
571 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
573 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
572 fp = os.fdopen(fd, 'wb+')
574 fp = os.fdopen(fd, 'wb+')
573 r = 0
575 r = 0
574 try:
576 try:
575 proto.getfile(fp)
577 proto.getfile(fp)
576 lock = repo.lock()
578 lock = repo.lock()
577 try:
579 try:
578 if not check_heads():
580 if not check_heads():
579 # someone else committed/pushed/unbundled while we
581 # someone else committed/pushed/unbundled while we
580 # were transferring data
582 # were transferring data
581 return pusherr('unsynced changes')
583 return pusherr('unsynced changes')
582
584
583 # push can proceed
585 # push can proceed
584 fp.seek(0)
586 fp.seek(0)
585 gen = changegroupmod.readbundle(fp, None)
587 gen = changegroupmod.readbundle(fp, None)
586
588
587 try:
589 try:
588 r = repo.addchangegroup(gen, 'serve', proto._client())
590 r = repo.addchangegroup(gen, 'serve', proto._client())
589 except util.Abort, inst:
591 except util.Abort, inst:
590 sys.stderr.write("abort: %s\n" % inst)
592 sys.stderr.write("abort: %s\n" % inst)
591 finally:
593 finally:
592 lock.release()
594 lock.release()
593 return pushres(r)
595 return pushres(r)
594
596
595 finally:
597 finally:
596 fp.close()
598 fp.close()
597 os.unlink(tempname)
599 os.unlink(tempname)
598
600
599 commands = {
601 commands = {
600 'batch': (batch, 'cmds *'),
602 'batch': (batch, 'cmds *'),
601 'between': (between, 'pairs'),
603 'between': (between, 'pairs'),
602 'branchmap': (branchmap, ''),
604 'branchmap': (branchmap, ''),
603 'branches': (branches, 'nodes'),
605 'branches': (branches, 'nodes'),
604 'capabilities': (capabilities, ''),
606 'capabilities': (capabilities, ''),
605 'changegroup': (changegroup, 'roots'),
607 'changegroup': (changegroup, 'roots'),
606 'changegroupsubset': (changegroupsubset, 'bases heads'),
608 'changegroupsubset': (changegroupsubset, 'bases heads'),
607 'debugwireargs': (debugwireargs, 'one two *'),
609 'debugwireargs': (debugwireargs, 'one two *'),
608 'getbundle': (getbundle, '*'),
610 'getbundle': (getbundle, '*'),
609 'heads': (heads, ''),
611 'heads': (heads, ''),
610 'hello': (hello, ''),
612 'hello': (hello, ''),
611 'known': (known, 'nodes *'),
613 'known': (known, 'nodes *'),
612 'listkeys': (listkeys, 'namespace'),
614 'listkeys': (listkeys, 'namespace'),
613 'lookup': (lookup, 'key'),
615 'lookup': (lookup, 'key'),
614 'pushkey': (pushkey, 'namespace key old new'),
616 'pushkey': (pushkey, 'namespace key old new'),
615 'stream_out': (stream, ''),
617 'stream_out': (stream, ''),
616 'unbundle': (unbundle, 'heads'),
618 'unbundle': (unbundle, 'heads'),
617 }
619 }
@@ -1,2073 +1,2133 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 3 changesets found
86 3 changesets found
86 list of changesets:
87 list of changesets:
87 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
88 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
88 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
89 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
89 911600dab2ae7a9baff75958b84fe606851ce955
90 911600dab2ae7a9baff75958b84fe606851ce955
90 adding changesets
91 adding changesets
91 bundling: 1/3 changesets (33.33%)
92 bundling: 1/3 changesets (33.33%)
92 bundling: 2/3 changesets (66.67%)
93 bundling: 2/3 changesets (66.67%)
93 bundling: 3/3 changesets (100.00%)
94 bundling: 3/3 changesets (100.00%)
94 bundling: 1/3 manifests (33.33%)
95 bundling: 1/3 manifests (33.33%)
95 bundling: 2/3 manifests (66.67%)
96 bundling: 2/3 manifests (66.67%)
96 bundling: 3/3 manifests (100.00%)
97 bundling: 3/3 manifests (100.00%)
97 bundling: foo/Bar/file.txt 1/3 files (33.33%)
98 bundling: foo/Bar/file.txt 1/3 files (33.33%)
98 bundling: foo/file.txt 2/3 files (66.67%)
99 bundling: foo/file.txt 2/3 files (66.67%)
99 bundling: quux/file.py 3/3 files (100.00%)
100 bundling: quux/file.py 3/3 files (100.00%)
100 changesets: 1 chunks
101 changesets: 1 chunks
101 add changeset ef1ea85a6374
102 add changeset ef1ea85a6374
102 changesets: 2 chunks
103 changesets: 2 chunks
103 add changeset f9cafe1212c8
104 add changeset f9cafe1212c8
104 changesets: 3 chunks
105 changesets: 3 chunks
105 add changeset 911600dab2ae
106 add changeset 911600dab2ae
106 adding manifests
107 adding manifests
107 manifests: 1/3 chunks (33.33%)
108 manifests: 1/3 chunks (33.33%)
108 manifests: 2/3 chunks (66.67%)
109 manifests: 2/3 chunks (66.67%)
109 manifests: 3/3 chunks (100.00%)
110 manifests: 3/3 chunks (100.00%)
110 adding file changes
111 adding file changes
111 adding foo/Bar/file.txt revisions
112 adding foo/Bar/file.txt revisions
112 files: 1/3 chunks (33.33%)
113 files: 1/3 chunks (33.33%)
113 adding foo/file.txt revisions
114 adding foo/file.txt revisions
114 files: 2/3 chunks (66.67%)
115 files: 2/3 chunks (66.67%)
115 adding quux/file.py revisions
116 adding quux/file.py revisions
116 files: 3/3 chunks (100.00%)
117 files: 3/3 chunks (100.00%)
117 added 3 changesets with 3 changes to 3 files
118 added 3 changesets with 3 changes to 3 files
119 listing keys for "phases"
120 listing keys for "namespaces"
118 updating the branch cache
121 updating the branch cache
119 checking for updated bookmarks
122 checking for updated bookmarks
123 listing keys for "bookmarks"
120 repository tip rolled back to revision 0 (undo push)
124 repository tip rolled back to revision 0 (undo push)
121 0:6675d58eff77
125 0:6675d58eff77
122
126
123
127
124 $ echo '[hooks]' >> $config
128 $ echo '[hooks]' >> $config
125 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
129 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
126
130
127 Extension disabled for lack of acl.sources
131 Extension disabled for lack of acl.sources
128
132
129 $ do_push fred
133 $ do_push fred
130 Pushing as user fred
134 Pushing as user fred
131 hgrc = """
135 hgrc = """
132 [hooks]
136 [hooks]
133 pretxnchangegroup.acl = python:hgext.acl.hook
137 pretxnchangegroup.acl = python:hgext.acl.hook
134 """
138 """
135 pushing to ../b
139 pushing to ../b
136 query 1; heads
140 query 1; heads
137 searching for changes
141 searching for changes
138 all remote heads known locally
142 all remote heads known locally
139 invalidating branch cache (tip differs)
143 invalidating branch cache (tip differs)
144 listing keys for "bookmarks"
140 3 changesets found
145 3 changesets found
141 list of changesets:
146 list of changesets:
142 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
147 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
143 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
148 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
144 911600dab2ae7a9baff75958b84fe606851ce955
149 911600dab2ae7a9baff75958b84fe606851ce955
145 adding changesets
150 adding changesets
146 bundling: 1/3 changesets (33.33%)
151 bundling: 1/3 changesets (33.33%)
147 bundling: 2/3 changesets (66.67%)
152 bundling: 2/3 changesets (66.67%)
148 bundling: 3/3 changesets (100.00%)
153 bundling: 3/3 changesets (100.00%)
149 bundling: 1/3 manifests (33.33%)
154 bundling: 1/3 manifests (33.33%)
150 bundling: 2/3 manifests (66.67%)
155 bundling: 2/3 manifests (66.67%)
151 bundling: 3/3 manifests (100.00%)
156 bundling: 3/3 manifests (100.00%)
152 bundling: foo/Bar/file.txt 1/3 files (33.33%)
157 bundling: foo/Bar/file.txt 1/3 files (33.33%)
153 bundling: foo/file.txt 2/3 files (66.67%)
158 bundling: foo/file.txt 2/3 files (66.67%)
154 bundling: quux/file.py 3/3 files (100.00%)
159 bundling: quux/file.py 3/3 files (100.00%)
155 changesets: 1 chunks
160 changesets: 1 chunks
156 add changeset ef1ea85a6374
161 add changeset ef1ea85a6374
157 changesets: 2 chunks
162 changesets: 2 chunks
158 add changeset f9cafe1212c8
163 add changeset f9cafe1212c8
159 changesets: 3 chunks
164 changesets: 3 chunks
160 add changeset 911600dab2ae
165 add changeset 911600dab2ae
161 adding manifests
166 adding manifests
162 manifests: 1/3 chunks (33.33%)
167 manifests: 1/3 chunks (33.33%)
163 manifests: 2/3 chunks (66.67%)
168 manifests: 2/3 chunks (66.67%)
164 manifests: 3/3 chunks (100.00%)
169 manifests: 3/3 chunks (100.00%)
165 adding file changes
170 adding file changes
166 adding foo/Bar/file.txt revisions
171 adding foo/Bar/file.txt revisions
167 files: 1/3 chunks (33.33%)
172 files: 1/3 chunks (33.33%)
168 adding foo/file.txt revisions
173 adding foo/file.txt revisions
169 files: 2/3 chunks (66.67%)
174 files: 2/3 chunks (66.67%)
170 adding quux/file.py revisions
175 adding quux/file.py revisions
171 files: 3/3 chunks (100.00%)
176 files: 3/3 chunks (100.00%)
172 added 3 changesets with 3 changes to 3 files
177 added 3 changesets with 3 changes to 3 files
173 calling hook pretxnchangegroup.acl: hgext.acl.hook
178 calling hook pretxnchangegroup.acl: hgext.acl.hook
174 acl: changes have source "push" - skipping
179 acl: changes have source "push" - skipping
180 listing keys for "phases"
181 listing keys for "namespaces"
175 updating the branch cache
182 updating the branch cache
176 checking for updated bookmarks
183 checking for updated bookmarks
184 listing keys for "bookmarks"
177 repository tip rolled back to revision 0 (undo push)
185 repository tip rolled back to revision 0 (undo push)
178 0:6675d58eff77
186 0:6675d58eff77
179
187
180
188
181 No [acl.allow]/[acl.deny]
189 No [acl.allow]/[acl.deny]
182
190
183 $ echo '[acl]' >> $config
191 $ echo '[acl]' >> $config
184 $ echo 'sources = push' >> $config
192 $ echo 'sources = push' >> $config
185 $ do_push fred
193 $ do_push fred
186 Pushing as user fred
194 Pushing as user fred
187 hgrc = """
195 hgrc = """
188 [hooks]
196 [hooks]
189 pretxnchangegroup.acl = python:hgext.acl.hook
197 pretxnchangegroup.acl = python:hgext.acl.hook
190 [acl]
198 [acl]
191 sources = push
199 sources = push
192 """
200 """
193 pushing to ../b
201 pushing to ../b
194 query 1; heads
202 query 1; heads
195 searching for changes
203 searching for changes
196 all remote heads known locally
204 all remote heads known locally
197 invalidating branch cache (tip differs)
205 invalidating branch cache (tip differs)
206 listing keys for "bookmarks"
198 3 changesets found
207 3 changesets found
199 list of changesets:
208 list of changesets:
200 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
209 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
201 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
210 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
202 911600dab2ae7a9baff75958b84fe606851ce955
211 911600dab2ae7a9baff75958b84fe606851ce955
203 adding changesets
212 adding changesets
204 bundling: 1/3 changesets (33.33%)
213 bundling: 1/3 changesets (33.33%)
205 bundling: 2/3 changesets (66.67%)
214 bundling: 2/3 changesets (66.67%)
206 bundling: 3/3 changesets (100.00%)
215 bundling: 3/3 changesets (100.00%)
207 bundling: 1/3 manifests (33.33%)
216 bundling: 1/3 manifests (33.33%)
208 bundling: 2/3 manifests (66.67%)
217 bundling: 2/3 manifests (66.67%)
209 bundling: 3/3 manifests (100.00%)
218 bundling: 3/3 manifests (100.00%)
210 bundling: foo/Bar/file.txt 1/3 files (33.33%)
219 bundling: foo/Bar/file.txt 1/3 files (33.33%)
211 bundling: foo/file.txt 2/3 files (66.67%)
220 bundling: foo/file.txt 2/3 files (66.67%)
212 bundling: quux/file.py 3/3 files (100.00%)
221 bundling: quux/file.py 3/3 files (100.00%)
213 changesets: 1 chunks
222 changesets: 1 chunks
214 add changeset ef1ea85a6374
223 add changeset ef1ea85a6374
215 changesets: 2 chunks
224 changesets: 2 chunks
216 add changeset f9cafe1212c8
225 add changeset f9cafe1212c8
217 changesets: 3 chunks
226 changesets: 3 chunks
218 add changeset 911600dab2ae
227 add changeset 911600dab2ae
219 adding manifests
228 adding manifests
220 manifests: 1/3 chunks (33.33%)
229 manifests: 1/3 chunks (33.33%)
221 manifests: 2/3 chunks (66.67%)
230 manifests: 2/3 chunks (66.67%)
222 manifests: 3/3 chunks (100.00%)
231 manifests: 3/3 chunks (100.00%)
223 adding file changes
232 adding file changes
224 adding foo/Bar/file.txt revisions
233 adding foo/Bar/file.txt revisions
225 files: 1/3 chunks (33.33%)
234 files: 1/3 chunks (33.33%)
226 adding foo/file.txt revisions
235 adding foo/file.txt revisions
227 files: 2/3 chunks (66.67%)
236 files: 2/3 chunks (66.67%)
228 adding quux/file.py revisions
237 adding quux/file.py revisions
229 files: 3/3 chunks (100.00%)
238 files: 3/3 chunks (100.00%)
230 added 3 changesets with 3 changes to 3 files
239 added 3 changesets with 3 changes to 3 files
231 calling hook pretxnchangegroup.acl: hgext.acl.hook
240 calling hook pretxnchangegroup.acl: hgext.acl.hook
232 acl: checking access for user "fred"
241 acl: checking access for user "fred"
233 acl: acl.allow.branches not enabled
242 acl: acl.allow.branches not enabled
234 acl: acl.deny.branches not enabled
243 acl: acl.deny.branches not enabled
235 acl: acl.allow not enabled
244 acl: acl.allow not enabled
236 acl: acl.deny not enabled
245 acl: acl.deny not enabled
237 acl: branch access granted: "ef1ea85a6374" on branch "default"
246 acl: branch access granted: "ef1ea85a6374" on branch "default"
238 acl: path access granted: "ef1ea85a6374"
247 acl: path access granted: "ef1ea85a6374"
239 acl: branch access granted: "f9cafe1212c8" on branch "default"
248 acl: branch access granted: "f9cafe1212c8" on branch "default"
240 acl: path access granted: "f9cafe1212c8"
249 acl: path access granted: "f9cafe1212c8"
241 acl: branch access granted: "911600dab2ae" on branch "default"
250 acl: branch access granted: "911600dab2ae" on branch "default"
242 acl: path access granted: "911600dab2ae"
251 acl: path access granted: "911600dab2ae"
252 listing keys for "phases"
253 listing keys for "namespaces"
243 updating the branch cache
254 updating the branch cache
244 checking for updated bookmarks
255 checking for updated bookmarks
256 listing keys for "bookmarks"
245 repository tip rolled back to revision 0 (undo push)
257 repository tip rolled back to revision 0 (undo push)
246 0:6675d58eff77
258 0:6675d58eff77
247
259
248
260
249 Empty [acl.allow]
261 Empty [acl.allow]
250
262
251 $ echo '[acl.allow]' >> $config
263 $ echo '[acl.allow]' >> $config
252 $ do_push fred
264 $ do_push fred
253 Pushing as user fred
265 Pushing as user fred
254 hgrc = """
266 hgrc = """
255 [hooks]
267 [hooks]
256 pretxnchangegroup.acl = python:hgext.acl.hook
268 pretxnchangegroup.acl = python:hgext.acl.hook
257 [acl]
269 [acl]
258 sources = push
270 sources = push
259 [acl.allow]
271 [acl.allow]
260 """
272 """
261 pushing to ../b
273 pushing to ../b
262 query 1; heads
274 query 1; heads
263 searching for changes
275 searching for changes
264 all remote heads known locally
276 all remote heads known locally
265 invalidating branch cache (tip differs)
277 invalidating branch cache (tip differs)
278 listing keys for "bookmarks"
266 3 changesets found
279 3 changesets found
267 list of changesets:
280 list of changesets:
268 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
281 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
269 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
282 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
270 911600dab2ae7a9baff75958b84fe606851ce955
283 911600dab2ae7a9baff75958b84fe606851ce955
271 adding changesets
284 adding changesets
272 bundling: 1/3 changesets (33.33%)
285 bundling: 1/3 changesets (33.33%)
273 bundling: 2/3 changesets (66.67%)
286 bundling: 2/3 changesets (66.67%)
274 bundling: 3/3 changesets (100.00%)
287 bundling: 3/3 changesets (100.00%)
275 bundling: 1/3 manifests (33.33%)
288 bundling: 1/3 manifests (33.33%)
276 bundling: 2/3 manifests (66.67%)
289 bundling: 2/3 manifests (66.67%)
277 bundling: 3/3 manifests (100.00%)
290 bundling: 3/3 manifests (100.00%)
278 bundling: foo/Bar/file.txt 1/3 files (33.33%)
291 bundling: foo/Bar/file.txt 1/3 files (33.33%)
279 bundling: foo/file.txt 2/3 files (66.67%)
292 bundling: foo/file.txt 2/3 files (66.67%)
280 bundling: quux/file.py 3/3 files (100.00%)
293 bundling: quux/file.py 3/3 files (100.00%)
281 changesets: 1 chunks
294 changesets: 1 chunks
282 add changeset ef1ea85a6374
295 add changeset ef1ea85a6374
283 changesets: 2 chunks
296 changesets: 2 chunks
284 add changeset f9cafe1212c8
297 add changeset f9cafe1212c8
285 changesets: 3 chunks
298 changesets: 3 chunks
286 add changeset 911600dab2ae
299 add changeset 911600dab2ae
287 adding manifests
300 adding manifests
288 manifests: 1/3 chunks (33.33%)
301 manifests: 1/3 chunks (33.33%)
289 manifests: 2/3 chunks (66.67%)
302 manifests: 2/3 chunks (66.67%)
290 manifests: 3/3 chunks (100.00%)
303 manifests: 3/3 chunks (100.00%)
291 adding file changes
304 adding file changes
292 adding foo/Bar/file.txt revisions
305 adding foo/Bar/file.txt revisions
293 files: 1/3 chunks (33.33%)
306 files: 1/3 chunks (33.33%)
294 adding foo/file.txt revisions
307 adding foo/file.txt revisions
295 files: 2/3 chunks (66.67%)
308 files: 2/3 chunks (66.67%)
296 adding quux/file.py revisions
309 adding quux/file.py revisions
297 files: 3/3 chunks (100.00%)
310 files: 3/3 chunks (100.00%)
298 added 3 changesets with 3 changes to 3 files
311 added 3 changesets with 3 changes to 3 files
299 calling hook pretxnchangegroup.acl: hgext.acl.hook
312 calling hook pretxnchangegroup.acl: hgext.acl.hook
300 acl: checking access for user "fred"
313 acl: checking access for user "fred"
301 acl: acl.allow.branches not enabled
314 acl: acl.allow.branches not enabled
302 acl: acl.deny.branches not enabled
315 acl: acl.deny.branches not enabled
303 acl: acl.allow enabled, 0 entries for user fred
316 acl: acl.allow enabled, 0 entries for user fred
304 acl: acl.deny not enabled
317 acl: acl.deny not enabled
305 acl: branch access granted: "ef1ea85a6374" on branch "default"
318 acl: branch access granted: "ef1ea85a6374" on branch "default"
306 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
319 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
307 transaction abort!
320 transaction abort!
308 rollback completed
321 rollback completed
309 abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
322 abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
310 no rollback information available
323 no rollback information available
311 0:6675d58eff77
324 0:6675d58eff77
312
325
313
326
314 fred is allowed inside foo/
327 fred is allowed inside foo/
315
328
316 $ echo 'foo/** = fred' >> $config
329 $ echo 'foo/** = fred' >> $config
317 $ do_push fred
330 $ do_push fred
318 Pushing as user fred
331 Pushing as user fred
319 hgrc = """
332 hgrc = """
320 [hooks]
333 [hooks]
321 pretxnchangegroup.acl = python:hgext.acl.hook
334 pretxnchangegroup.acl = python:hgext.acl.hook
322 [acl]
335 [acl]
323 sources = push
336 sources = push
324 [acl.allow]
337 [acl.allow]
325 foo/** = fred
338 foo/** = fred
326 """
339 """
327 pushing to ../b
340 pushing to ../b
328 query 1; heads
341 query 1; heads
329 searching for changes
342 searching for changes
330 all remote heads known locally
343 all remote heads known locally
344 listing keys for "bookmarks"
331 3 changesets found
345 3 changesets found
332 list of changesets:
346 list of changesets:
333 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
347 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
334 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
348 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
335 911600dab2ae7a9baff75958b84fe606851ce955
349 911600dab2ae7a9baff75958b84fe606851ce955
336 adding changesets
350 adding changesets
337 bundling: 1/3 changesets (33.33%)
351 bundling: 1/3 changesets (33.33%)
338 bundling: 2/3 changesets (66.67%)
352 bundling: 2/3 changesets (66.67%)
339 bundling: 3/3 changesets (100.00%)
353 bundling: 3/3 changesets (100.00%)
340 bundling: 1/3 manifests (33.33%)
354 bundling: 1/3 manifests (33.33%)
341 bundling: 2/3 manifests (66.67%)
355 bundling: 2/3 manifests (66.67%)
342 bundling: 3/3 manifests (100.00%)
356 bundling: 3/3 manifests (100.00%)
343 bundling: foo/Bar/file.txt 1/3 files (33.33%)
357 bundling: foo/Bar/file.txt 1/3 files (33.33%)
344 bundling: foo/file.txt 2/3 files (66.67%)
358 bundling: foo/file.txt 2/3 files (66.67%)
345 bundling: quux/file.py 3/3 files (100.00%)
359 bundling: quux/file.py 3/3 files (100.00%)
346 changesets: 1 chunks
360 changesets: 1 chunks
347 add changeset ef1ea85a6374
361 add changeset ef1ea85a6374
348 changesets: 2 chunks
362 changesets: 2 chunks
349 add changeset f9cafe1212c8
363 add changeset f9cafe1212c8
350 changesets: 3 chunks
364 changesets: 3 chunks
351 add changeset 911600dab2ae
365 add changeset 911600dab2ae
352 adding manifests
366 adding manifests
353 manifests: 1/3 chunks (33.33%)
367 manifests: 1/3 chunks (33.33%)
354 manifests: 2/3 chunks (66.67%)
368 manifests: 2/3 chunks (66.67%)
355 manifests: 3/3 chunks (100.00%)
369 manifests: 3/3 chunks (100.00%)
356 adding file changes
370 adding file changes
357 adding foo/Bar/file.txt revisions
371 adding foo/Bar/file.txt revisions
358 files: 1/3 chunks (33.33%)
372 files: 1/3 chunks (33.33%)
359 adding foo/file.txt revisions
373 adding foo/file.txt revisions
360 files: 2/3 chunks (66.67%)
374 files: 2/3 chunks (66.67%)
361 adding quux/file.py revisions
375 adding quux/file.py revisions
362 files: 3/3 chunks (100.00%)
376 files: 3/3 chunks (100.00%)
363 added 3 changesets with 3 changes to 3 files
377 added 3 changesets with 3 changes to 3 files
364 calling hook pretxnchangegroup.acl: hgext.acl.hook
378 calling hook pretxnchangegroup.acl: hgext.acl.hook
365 acl: checking access for user "fred"
379 acl: checking access for user "fred"
366 acl: acl.allow.branches not enabled
380 acl: acl.allow.branches not enabled
367 acl: acl.deny.branches not enabled
381 acl: acl.deny.branches not enabled
368 acl: acl.allow enabled, 1 entries for user fred
382 acl: acl.allow enabled, 1 entries for user fred
369 acl: acl.deny not enabled
383 acl: acl.deny not enabled
370 acl: branch access granted: "ef1ea85a6374" on branch "default"
384 acl: branch access granted: "ef1ea85a6374" on branch "default"
371 acl: path access granted: "ef1ea85a6374"
385 acl: path access granted: "ef1ea85a6374"
372 acl: branch access granted: "f9cafe1212c8" on branch "default"
386 acl: branch access granted: "f9cafe1212c8" on branch "default"
373 acl: path access granted: "f9cafe1212c8"
387 acl: path access granted: "f9cafe1212c8"
374 acl: branch access granted: "911600dab2ae" on branch "default"
388 acl: branch access granted: "911600dab2ae" on branch "default"
375 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
389 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
376 transaction abort!
390 transaction abort!
377 rollback completed
391 rollback completed
378 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
392 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
379 no rollback information available
393 no rollback information available
380 0:6675d58eff77
394 0:6675d58eff77
381
395
382
396
383 Empty [acl.deny]
397 Empty [acl.deny]
384
398
385 $ echo '[acl.deny]' >> $config
399 $ echo '[acl.deny]' >> $config
386 $ do_push barney
400 $ do_push barney
387 Pushing as user barney
401 Pushing as user barney
388 hgrc = """
402 hgrc = """
389 [hooks]
403 [hooks]
390 pretxnchangegroup.acl = python:hgext.acl.hook
404 pretxnchangegroup.acl = python:hgext.acl.hook
391 [acl]
405 [acl]
392 sources = push
406 sources = push
393 [acl.allow]
407 [acl.allow]
394 foo/** = fred
408 foo/** = fred
395 [acl.deny]
409 [acl.deny]
396 """
410 """
397 pushing to ../b
411 pushing to ../b
398 query 1; heads
412 query 1; heads
399 searching for changes
413 searching for changes
400 all remote heads known locally
414 all remote heads known locally
415 listing keys for "bookmarks"
401 3 changesets found
416 3 changesets found
402 list of changesets:
417 list of changesets:
403 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
418 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
404 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
419 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
405 911600dab2ae7a9baff75958b84fe606851ce955
420 911600dab2ae7a9baff75958b84fe606851ce955
406 adding changesets
421 adding changesets
407 bundling: 1/3 changesets (33.33%)
422 bundling: 1/3 changesets (33.33%)
408 bundling: 2/3 changesets (66.67%)
423 bundling: 2/3 changesets (66.67%)
409 bundling: 3/3 changesets (100.00%)
424 bundling: 3/3 changesets (100.00%)
410 bundling: 1/3 manifests (33.33%)
425 bundling: 1/3 manifests (33.33%)
411 bundling: 2/3 manifests (66.67%)
426 bundling: 2/3 manifests (66.67%)
412 bundling: 3/3 manifests (100.00%)
427 bundling: 3/3 manifests (100.00%)
413 bundling: foo/Bar/file.txt 1/3 files (33.33%)
428 bundling: foo/Bar/file.txt 1/3 files (33.33%)
414 bundling: foo/file.txt 2/3 files (66.67%)
429 bundling: foo/file.txt 2/3 files (66.67%)
415 bundling: quux/file.py 3/3 files (100.00%)
430 bundling: quux/file.py 3/3 files (100.00%)
416 changesets: 1 chunks
431 changesets: 1 chunks
417 add changeset ef1ea85a6374
432 add changeset ef1ea85a6374
418 changesets: 2 chunks
433 changesets: 2 chunks
419 add changeset f9cafe1212c8
434 add changeset f9cafe1212c8
420 changesets: 3 chunks
435 changesets: 3 chunks
421 add changeset 911600dab2ae
436 add changeset 911600dab2ae
422 adding manifests
437 adding manifests
423 manifests: 1/3 chunks (33.33%)
438 manifests: 1/3 chunks (33.33%)
424 manifests: 2/3 chunks (66.67%)
439 manifests: 2/3 chunks (66.67%)
425 manifests: 3/3 chunks (100.00%)
440 manifests: 3/3 chunks (100.00%)
426 adding file changes
441 adding file changes
427 adding foo/Bar/file.txt revisions
442 adding foo/Bar/file.txt revisions
428 files: 1/3 chunks (33.33%)
443 files: 1/3 chunks (33.33%)
429 adding foo/file.txt revisions
444 adding foo/file.txt revisions
430 files: 2/3 chunks (66.67%)
445 files: 2/3 chunks (66.67%)
431 adding quux/file.py revisions
446 adding quux/file.py revisions
432 files: 3/3 chunks (100.00%)
447 files: 3/3 chunks (100.00%)
433 added 3 changesets with 3 changes to 3 files
448 added 3 changesets with 3 changes to 3 files
434 calling hook pretxnchangegroup.acl: hgext.acl.hook
449 calling hook pretxnchangegroup.acl: hgext.acl.hook
435 acl: checking access for user "barney"
450 acl: checking access for user "barney"
436 acl: acl.allow.branches not enabled
451 acl: acl.allow.branches not enabled
437 acl: acl.deny.branches not enabled
452 acl: acl.deny.branches not enabled
438 acl: acl.allow enabled, 0 entries for user barney
453 acl: acl.allow enabled, 0 entries for user barney
439 acl: acl.deny enabled, 0 entries for user barney
454 acl: acl.deny enabled, 0 entries for user barney
440 acl: branch access granted: "ef1ea85a6374" on branch "default"
455 acl: branch access granted: "ef1ea85a6374" on branch "default"
441 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
456 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
442 transaction abort!
457 transaction abort!
443 rollback completed
458 rollback completed
444 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
459 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
445 no rollback information available
460 no rollback information available
446 0:6675d58eff77
461 0:6675d58eff77
447
462
448
463
449 fred is allowed inside foo/, but not foo/bar/ (case matters)
464 fred is allowed inside foo/, but not foo/bar/ (case matters)
450
465
451 $ echo 'foo/bar/** = fred' >> $config
466 $ echo 'foo/bar/** = fred' >> $config
452 $ do_push fred
467 $ do_push fred
453 Pushing as user fred
468 Pushing as user fred
454 hgrc = """
469 hgrc = """
455 [hooks]
470 [hooks]
456 pretxnchangegroup.acl = python:hgext.acl.hook
471 pretxnchangegroup.acl = python:hgext.acl.hook
457 [acl]
472 [acl]
458 sources = push
473 sources = push
459 [acl.allow]
474 [acl.allow]
460 foo/** = fred
475 foo/** = fred
461 [acl.deny]
476 [acl.deny]
462 foo/bar/** = fred
477 foo/bar/** = fred
463 """
478 """
464 pushing to ../b
479 pushing to ../b
465 query 1; heads
480 query 1; heads
466 searching for changes
481 searching for changes
467 all remote heads known locally
482 all remote heads known locally
483 listing keys for "bookmarks"
468 3 changesets found
484 3 changesets found
469 list of changesets:
485 list of changesets:
470 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
486 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
471 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
487 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
472 911600dab2ae7a9baff75958b84fe606851ce955
488 911600dab2ae7a9baff75958b84fe606851ce955
473 adding changesets
489 adding changesets
474 bundling: 1/3 changesets (33.33%)
490 bundling: 1/3 changesets (33.33%)
475 bundling: 2/3 changesets (66.67%)
491 bundling: 2/3 changesets (66.67%)
476 bundling: 3/3 changesets (100.00%)
492 bundling: 3/3 changesets (100.00%)
477 bundling: 1/3 manifests (33.33%)
493 bundling: 1/3 manifests (33.33%)
478 bundling: 2/3 manifests (66.67%)
494 bundling: 2/3 manifests (66.67%)
479 bundling: 3/3 manifests (100.00%)
495 bundling: 3/3 manifests (100.00%)
480 bundling: foo/Bar/file.txt 1/3 files (33.33%)
496 bundling: foo/Bar/file.txt 1/3 files (33.33%)
481 bundling: foo/file.txt 2/3 files (66.67%)
497 bundling: foo/file.txt 2/3 files (66.67%)
482 bundling: quux/file.py 3/3 files (100.00%)
498 bundling: quux/file.py 3/3 files (100.00%)
483 changesets: 1 chunks
499 changesets: 1 chunks
484 add changeset ef1ea85a6374
500 add changeset ef1ea85a6374
485 changesets: 2 chunks
501 changesets: 2 chunks
486 add changeset f9cafe1212c8
502 add changeset f9cafe1212c8
487 changesets: 3 chunks
503 changesets: 3 chunks
488 add changeset 911600dab2ae
504 add changeset 911600dab2ae
489 adding manifests
505 adding manifests
490 manifests: 1/3 chunks (33.33%)
506 manifests: 1/3 chunks (33.33%)
491 manifests: 2/3 chunks (66.67%)
507 manifests: 2/3 chunks (66.67%)
492 manifests: 3/3 chunks (100.00%)
508 manifests: 3/3 chunks (100.00%)
493 adding file changes
509 adding file changes
494 adding foo/Bar/file.txt revisions
510 adding foo/Bar/file.txt revisions
495 files: 1/3 chunks (33.33%)
511 files: 1/3 chunks (33.33%)
496 adding foo/file.txt revisions
512 adding foo/file.txt revisions
497 files: 2/3 chunks (66.67%)
513 files: 2/3 chunks (66.67%)
498 adding quux/file.py revisions
514 adding quux/file.py revisions
499 files: 3/3 chunks (100.00%)
515 files: 3/3 chunks (100.00%)
500 added 3 changesets with 3 changes to 3 files
516 added 3 changesets with 3 changes to 3 files
501 calling hook pretxnchangegroup.acl: hgext.acl.hook
517 calling hook pretxnchangegroup.acl: hgext.acl.hook
502 acl: checking access for user "fred"
518 acl: checking access for user "fred"
503 acl: acl.allow.branches not enabled
519 acl: acl.allow.branches not enabled
504 acl: acl.deny.branches not enabled
520 acl: acl.deny.branches not enabled
505 acl: acl.allow enabled, 1 entries for user fred
521 acl: acl.allow enabled, 1 entries for user fred
506 acl: acl.deny enabled, 1 entries for user fred
522 acl: acl.deny enabled, 1 entries for user fred
507 acl: branch access granted: "ef1ea85a6374" on branch "default"
523 acl: branch access granted: "ef1ea85a6374" on branch "default"
508 acl: path access granted: "ef1ea85a6374"
524 acl: path access granted: "ef1ea85a6374"
509 acl: branch access granted: "f9cafe1212c8" on branch "default"
525 acl: branch access granted: "f9cafe1212c8" on branch "default"
510 acl: path access granted: "f9cafe1212c8"
526 acl: path access granted: "f9cafe1212c8"
511 acl: branch access granted: "911600dab2ae" on branch "default"
527 acl: branch access granted: "911600dab2ae" on branch "default"
512 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
528 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
513 transaction abort!
529 transaction abort!
514 rollback completed
530 rollback completed
515 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
531 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
516 no rollback information available
532 no rollback information available
517 0:6675d58eff77
533 0:6675d58eff77
518
534
519
535
520 fred is allowed inside foo/, but not foo/Bar/
536 fred is allowed inside foo/, but not foo/Bar/
521
537
522 $ echo 'foo/Bar/** = fred' >> $config
538 $ echo 'foo/Bar/** = fred' >> $config
523 $ do_push fred
539 $ do_push fred
524 Pushing as user fred
540 Pushing as user fred
525 hgrc = """
541 hgrc = """
526 [hooks]
542 [hooks]
527 pretxnchangegroup.acl = python:hgext.acl.hook
543 pretxnchangegroup.acl = python:hgext.acl.hook
528 [acl]
544 [acl]
529 sources = push
545 sources = push
530 [acl.allow]
546 [acl.allow]
531 foo/** = fred
547 foo/** = fred
532 [acl.deny]
548 [acl.deny]
533 foo/bar/** = fred
549 foo/bar/** = fred
534 foo/Bar/** = fred
550 foo/Bar/** = fred
535 """
551 """
536 pushing to ../b
552 pushing to ../b
537 query 1; heads
553 query 1; heads
538 searching for changes
554 searching for changes
539 all remote heads known locally
555 all remote heads known locally
556 listing keys for "bookmarks"
540 3 changesets found
557 3 changesets found
541 list of changesets:
558 list of changesets:
542 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
559 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
543 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
560 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
544 911600dab2ae7a9baff75958b84fe606851ce955
561 911600dab2ae7a9baff75958b84fe606851ce955
545 adding changesets
562 adding changesets
546 bundling: 1/3 changesets (33.33%)
563 bundling: 1/3 changesets (33.33%)
547 bundling: 2/3 changesets (66.67%)
564 bundling: 2/3 changesets (66.67%)
548 bundling: 3/3 changesets (100.00%)
565 bundling: 3/3 changesets (100.00%)
549 bundling: 1/3 manifests (33.33%)
566 bundling: 1/3 manifests (33.33%)
550 bundling: 2/3 manifests (66.67%)
567 bundling: 2/3 manifests (66.67%)
551 bundling: 3/3 manifests (100.00%)
568 bundling: 3/3 manifests (100.00%)
552 bundling: foo/Bar/file.txt 1/3 files (33.33%)
569 bundling: foo/Bar/file.txt 1/3 files (33.33%)
553 bundling: foo/file.txt 2/3 files (66.67%)
570 bundling: foo/file.txt 2/3 files (66.67%)
554 bundling: quux/file.py 3/3 files (100.00%)
571 bundling: quux/file.py 3/3 files (100.00%)
555 changesets: 1 chunks
572 changesets: 1 chunks
556 add changeset ef1ea85a6374
573 add changeset ef1ea85a6374
557 changesets: 2 chunks
574 changesets: 2 chunks
558 add changeset f9cafe1212c8
575 add changeset f9cafe1212c8
559 changesets: 3 chunks
576 changesets: 3 chunks
560 add changeset 911600dab2ae
577 add changeset 911600dab2ae
561 adding manifests
578 adding manifests
562 manifests: 1/3 chunks (33.33%)
579 manifests: 1/3 chunks (33.33%)
563 manifests: 2/3 chunks (66.67%)
580 manifests: 2/3 chunks (66.67%)
564 manifests: 3/3 chunks (100.00%)
581 manifests: 3/3 chunks (100.00%)
565 adding file changes
582 adding file changes
566 adding foo/Bar/file.txt revisions
583 adding foo/Bar/file.txt revisions
567 files: 1/3 chunks (33.33%)
584 files: 1/3 chunks (33.33%)
568 adding foo/file.txt revisions
585 adding foo/file.txt revisions
569 files: 2/3 chunks (66.67%)
586 files: 2/3 chunks (66.67%)
570 adding quux/file.py revisions
587 adding quux/file.py revisions
571 files: 3/3 chunks (100.00%)
588 files: 3/3 chunks (100.00%)
572 added 3 changesets with 3 changes to 3 files
589 added 3 changesets with 3 changes to 3 files
573 calling hook pretxnchangegroup.acl: hgext.acl.hook
590 calling hook pretxnchangegroup.acl: hgext.acl.hook
574 acl: checking access for user "fred"
591 acl: checking access for user "fred"
575 acl: acl.allow.branches not enabled
592 acl: acl.allow.branches not enabled
576 acl: acl.deny.branches not enabled
593 acl: acl.deny.branches not enabled
577 acl: acl.allow enabled, 1 entries for user fred
594 acl: acl.allow enabled, 1 entries for user fred
578 acl: acl.deny enabled, 2 entries for user fred
595 acl: acl.deny enabled, 2 entries for user fred
579 acl: branch access granted: "ef1ea85a6374" on branch "default"
596 acl: branch access granted: "ef1ea85a6374" on branch "default"
580 acl: path access granted: "ef1ea85a6374"
597 acl: path access granted: "ef1ea85a6374"
581 acl: branch access granted: "f9cafe1212c8" on branch "default"
598 acl: branch access granted: "f9cafe1212c8" on branch "default"
582 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
599 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
583 transaction abort!
600 transaction abort!
584 rollback completed
601 rollback completed
585 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
602 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
586 no rollback information available
603 no rollback information available
587 0:6675d58eff77
604 0:6675d58eff77
588
605
589
606
590 $ echo 'barney is not mentioned => not allowed anywhere'
607 $ echo 'barney is not mentioned => not allowed anywhere'
591 barney is not mentioned => not allowed anywhere
608 barney is not mentioned => not allowed anywhere
592 $ do_push barney
609 $ do_push barney
593 Pushing as user barney
610 Pushing as user barney
594 hgrc = """
611 hgrc = """
595 [hooks]
612 [hooks]
596 pretxnchangegroup.acl = python:hgext.acl.hook
613 pretxnchangegroup.acl = python:hgext.acl.hook
597 [acl]
614 [acl]
598 sources = push
615 sources = push
599 [acl.allow]
616 [acl.allow]
600 foo/** = fred
617 foo/** = fred
601 [acl.deny]
618 [acl.deny]
602 foo/bar/** = fred
619 foo/bar/** = fred
603 foo/Bar/** = fred
620 foo/Bar/** = fred
604 """
621 """
605 pushing to ../b
622 pushing to ../b
606 query 1; heads
623 query 1; heads
607 searching for changes
624 searching for changes
608 all remote heads known locally
625 all remote heads known locally
626 listing keys for "bookmarks"
609 3 changesets found
627 3 changesets found
610 list of changesets:
628 list of changesets:
611 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
629 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
612 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
630 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
613 911600dab2ae7a9baff75958b84fe606851ce955
631 911600dab2ae7a9baff75958b84fe606851ce955
614 adding changesets
632 adding changesets
615 bundling: 1/3 changesets (33.33%)
633 bundling: 1/3 changesets (33.33%)
616 bundling: 2/3 changesets (66.67%)
634 bundling: 2/3 changesets (66.67%)
617 bundling: 3/3 changesets (100.00%)
635 bundling: 3/3 changesets (100.00%)
618 bundling: 1/3 manifests (33.33%)
636 bundling: 1/3 manifests (33.33%)
619 bundling: 2/3 manifests (66.67%)
637 bundling: 2/3 manifests (66.67%)
620 bundling: 3/3 manifests (100.00%)
638 bundling: 3/3 manifests (100.00%)
621 bundling: foo/Bar/file.txt 1/3 files (33.33%)
639 bundling: foo/Bar/file.txt 1/3 files (33.33%)
622 bundling: foo/file.txt 2/3 files (66.67%)
640 bundling: foo/file.txt 2/3 files (66.67%)
623 bundling: quux/file.py 3/3 files (100.00%)
641 bundling: quux/file.py 3/3 files (100.00%)
624 changesets: 1 chunks
642 changesets: 1 chunks
625 add changeset ef1ea85a6374
643 add changeset ef1ea85a6374
626 changesets: 2 chunks
644 changesets: 2 chunks
627 add changeset f9cafe1212c8
645 add changeset f9cafe1212c8
628 changesets: 3 chunks
646 changesets: 3 chunks
629 add changeset 911600dab2ae
647 add changeset 911600dab2ae
630 adding manifests
648 adding manifests
631 manifests: 1/3 chunks (33.33%)
649 manifests: 1/3 chunks (33.33%)
632 manifests: 2/3 chunks (66.67%)
650 manifests: 2/3 chunks (66.67%)
633 manifests: 3/3 chunks (100.00%)
651 manifests: 3/3 chunks (100.00%)
634 adding file changes
652 adding file changes
635 adding foo/Bar/file.txt revisions
653 adding foo/Bar/file.txt revisions
636 files: 1/3 chunks (33.33%)
654 files: 1/3 chunks (33.33%)
637 adding foo/file.txt revisions
655 adding foo/file.txt revisions
638 files: 2/3 chunks (66.67%)
656 files: 2/3 chunks (66.67%)
639 adding quux/file.py revisions
657 adding quux/file.py revisions
640 files: 3/3 chunks (100.00%)
658 files: 3/3 chunks (100.00%)
641 added 3 changesets with 3 changes to 3 files
659 added 3 changesets with 3 changes to 3 files
642 calling hook pretxnchangegroup.acl: hgext.acl.hook
660 calling hook pretxnchangegroup.acl: hgext.acl.hook
643 acl: checking access for user "barney"
661 acl: checking access for user "barney"
644 acl: acl.allow.branches not enabled
662 acl: acl.allow.branches not enabled
645 acl: acl.deny.branches not enabled
663 acl: acl.deny.branches not enabled
646 acl: acl.allow enabled, 0 entries for user barney
664 acl: acl.allow enabled, 0 entries for user barney
647 acl: acl.deny enabled, 0 entries for user barney
665 acl: acl.deny enabled, 0 entries for user barney
648 acl: branch access granted: "ef1ea85a6374" on branch "default"
666 acl: branch access granted: "ef1ea85a6374" on branch "default"
649 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
667 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
650 transaction abort!
668 transaction abort!
651 rollback completed
669 rollback completed
652 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
670 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
653 no rollback information available
671 no rollback information available
654 0:6675d58eff77
672 0:6675d58eff77
655
673
656
674
657 barney is allowed everywhere
675 barney is allowed everywhere
658
676
659 $ echo '[acl.allow]' >> $config
677 $ echo '[acl.allow]' >> $config
660 $ echo '** = barney' >> $config
678 $ echo '** = barney' >> $config
661 $ do_push barney
679 $ do_push barney
662 Pushing as user barney
680 Pushing as user barney
663 hgrc = """
681 hgrc = """
664 [hooks]
682 [hooks]
665 pretxnchangegroup.acl = python:hgext.acl.hook
683 pretxnchangegroup.acl = python:hgext.acl.hook
666 [acl]
684 [acl]
667 sources = push
685 sources = push
668 [acl.allow]
686 [acl.allow]
669 foo/** = fred
687 foo/** = fred
670 [acl.deny]
688 [acl.deny]
671 foo/bar/** = fred
689 foo/bar/** = fred
672 foo/Bar/** = fred
690 foo/Bar/** = fred
673 [acl.allow]
691 [acl.allow]
674 ** = barney
692 ** = barney
675 """
693 """
676 pushing to ../b
694 pushing to ../b
677 query 1; heads
695 query 1; heads
678 searching for changes
696 searching for changes
679 all remote heads known locally
697 all remote heads known locally
698 listing keys for "bookmarks"
680 3 changesets found
699 3 changesets found
681 list of changesets:
700 list of changesets:
682 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
701 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
683 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
702 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
684 911600dab2ae7a9baff75958b84fe606851ce955
703 911600dab2ae7a9baff75958b84fe606851ce955
685 adding changesets
704 adding changesets
686 bundling: 1/3 changesets (33.33%)
705 bundling: 1/3 changesets (33.33%)
687 bundling: 2/3 changesets (66.67%)
706 bundling: 2/3 changesets (66.67%)
688 bundling: 3/3 changesets (100.00%)
707 bundling: 3/3 changesets (100.00%)
689 bundling: 1/3 manifests (33.33%)
708 bundling: 1/3 manifests (33.33%)
690 bundling: 2/3 manifests (66.67%)
709 bundling: 2/3 manifests (66.67%)
691 bundling: 3/3 manifests (100.00%)
710 bundling: 3/3 manifests (100.00%)
692 bundling: foo/Bar/file.txt 1/3 files (33.33%)
711 bundling: foo/Bar/file.txt 1/3 files (33.33%)
693 bundling: foo/file.txt 2/3 files (66.67%)
712 bundling: foo/file.txt 2/3 files (66.67%)
694 bundling: quux/file.py 3/3 files (100.00%)
713 bundling: quux/file.py 3/3 files (100.00%)
695 changesets: 1 chunks
714 changesets: 1 chunks
696 add changeset ef1ea85a6374
715 add changeset ef1ea85a6374
697 changesets: 2 chunks
716 changesets: 2 chunks
698 add changeset f9cafe1212c8
717 add changeset f9cafe1212c8
699 changesets: 3 chunks
718 changesets: 3 chunks
700 add changeset 911600dab2ae
719 add changeset 911600dab2ae
701 adding manifests
720 adding manifests
702 manifests: 1/3 chunks (33.33%)
721 manifests: 1/3 chunks (33.33%)
703 manifests: 2/3 chunks (66.67%)
722 manifests: 2/3 chunks (66.67%)
704 manifests: 3/3 chunks (100.00%)
723 manifests: 3/3 chunks (100.00%)
705 adding file changes
724 adding file changes
706 adding foo/Bar/file.txt revisions
725 adding foo/Bar/file.txt revisions
707 files: 1/3 chunks (33.33%)
726 files: 1/3 chunks (33.33%)
708 adding foo/file.txt revisions
727 adding foo/file.txt revisions
709 files: 2/3 chunks (66.67%)
728 files: 2/3 chunks (66.67%)
710 adding quux/file.py revisions
729 adding quux/file.py revisions
711 files: 3/3 chunks (100.00%)
730 files: 3/3 chunks (100.00%)
712 added 3 changesets with 3 changes to 3 files
731 added 3 changesets with 3 changes to 3 files
713 calling hook pretxnchangegroup.acl: hgext.acl.hook
732 calling hook pretxnchangegroup.acl: hgext.acl.hook
714 acl: checking access for user "barney"
733 acl: checking access for user "barney"
715 acl: acl.allow.branches not enabled
734 acl: acl.allow.branches not enabled
716 acl: acl.deny.branches not enabled
735 acl: acl.deny.branches not enabled
717 acl: acl.allow enabled, 1 entries for user barney
736 acl: acl.allow enabled, 1 entries for user barney
718 acl: acl.deny enabled, 0 entries for user barney
737 acl: acl.deny enabled, 0 entries for user barney
719 acl: branch access granted: "ef1ea85a6374" on branch "default"
738 acl: branch access granted: "ef1ea85a6374" on branch "default"
720 acl: path access granted: "ef1ea85a6374"
739 acl: path access granted: "ef1ea85a6374"
721 acl: branch access granted: "f9cafe1212c8" on branch "default"
740 acl: branch access granted: "f9cafe1212c8" on branch "default"
722 acl: path access granted: "f9cafe1212c8"
741 acl: path access granted: "f9cafe1212c8"
723 acl: branch access granted: "911600dab2ae" on branch "default"
742 acl: branch access granted: "911600dab2ae" on branch "default"
724 acl: path access granted: "911600dab2ae"
743 acl: path access granted: "911600dab2ae"
744 listing keys for "phases"
745 listing keys for "namespaces"
725 updating the branch cache
746 updating the branch cache
726 checking for updated bookmarks
747 checking for updated bookmarks
748 listing keys for "bookmarks"
727 repository tip rolled back to revision 0 (undo push)
749 repository tip rolled back to revision 0 (undo push)
728 0:6675d58eff77
750 0:6675d58eff77
729
751
730
752
731 wilma can change files with a .txt extension
753 wilma can change files with a .txt extension
732
754
733 $ echo '**/*.txt = wilma' >> $config
755 $ echo '**/*.txt = wilma' >> $config
734 $ do_push wilma
756 $ do_push wilma
735 Pushing as user wilma
757 Pushing as user wilma
736 hgrc = """
758 hgrc = """
737 [hooks]
759 [hooks]
738 pretxnchangegroup.acl = python:hgext.acl.hook
760 pretxnchangegroup.acl = python:hgext.acl.hook
739 [acl]
761 [acl]
740 sources = push
762 sources = push
741 [acl.allow]
763 [acl.allow]
742 foo/** = fred
764 foo/** = fred
743 [acl.deny]
765 [acl.deny]
744 foo/bar/** = fred
766 foo/bar/** = fred
745 foo/Bar/** = fred
767 foo/Bar/** = fred
746 [acl.allow]
768 [acl.allow]
747 ** = barney
769 ** = barney
748 **/*.txt = wilma
770 **/*.txt = wilma
749 """
771 """
750 pushing to ../b
772 pushing to ../b
751 query 1; heads
773 query 1; heads
752 searching for changes
774 searching for changes
753 all remote heads known locally
775 all remote heads known locally
754 invalidating branch cache (tip differs)
776 invalidating branch cache (tip differs)
777 listing keys for "bookmarks"
755 3 changesets found
778 3 changesets found
756 list of changesets:
779 list of changesets:
757 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
780 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
758 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
781 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
759 911600dab2ae7a9baff75958b84fe606851ce955
782 911600dab2ae7a9baff75958b84fe606851ce955
760 adding changesets
783 adding changesets
761 bundling: 1/3 changesets (33.33%)
784 bundling: 1/3 changesets (33.33%)
762 bundling: 2/3 changesets (66.67%)
785 bundling: 2/3 changesets (66.67%)
763 bundling: 3/3 changesets (100.00%)
786 bundling: 3/3 changesets (100.00%)
764 bundling: 1/3 manifests (33.33%)
787 bundling: 1/3 manifests (33.33%)
765 bundling: 2/3 manifests (66.67%)
788 bundling: 2/3 manifests (66.67%)
766 bundling: 3/3 manifests (100.00%)
789 bundling: 3/3 manifests (100.00%)
767 bundling: foo/Bar/file.txt 1/3 files (33.33%)
790 bundling: foo/Bar/file.txt 1/3 files (33.33%)
768 bundling: foo/file.txt 2/3 files (66.67%)
791 bundling: foo/file.txt 2/3 files (66.67%)
769 bundling: quux/file.py 3/3 files (100.00%)
792 bundling: quux/file.py 3/3 files (100.00%)
770 changesets: 1 chunks
793 changesets: 1 chunks
771 add changeset ef1ea85a6374
794 add changeset ef1ea85a6374
772 changesets: 2 chunks
795 changesets: 2 chunks
773 add changeset f9cafe1212c8
796 add changeset f9cafe1212c8
774 changesets: 3 chunks
797 changesets: 3 chunks
775 add changeset 911600dab2ae
798 add changeset 911600dab2ae
776 adding manifests
799 adding manifests
777 manifests: 1/3 chunks (33.33%)
800 manifests: 1/3 chunks (33.33%)
778 manifests: 2/3 chunks (66.67%)
801 manifests: 2/3 chunks (66.67%)
779 manifests: 3/3 chunks (100.00%)
802 manifests: 3/3 chunks (100.00%)
780 adding file changes
803 adding file changes
781 adding foo/Bar/file.txt revisions
804 adding foo/Bar/file.txt revisions
782 files: 1/3 chunks (33.33%)
805 files: 1/3 chunks (33.33%)
783 adding foo/file.txt revisions
806 adding foo/file.txt revisions
784 files: 2/3 chunks (66.67%)
807 files: 2/3 chunks (66.67%)
785 adding quux/file.py revisions
808 adding quux/file.py revisions
786 files: 3/3 chunks (100.00%)
809 files: 3/3 chunks (100.00%)
787 added 3 changesets with 3 changes to 3 files
810 added 3 changesets with 3 changes to 3 files
788 calling hook pretxnchangegroup.acl: hgext.acl.hook
811 calling hook pretxnchangegroup.acl: hgext.acl.hook
789 acl: checking access for user "wilma"
812 acl: checking access for user "wilma"
790 acl: acl.allow.branches not enabled
813 acl: acl.allow.branches not enabled
791 acl: acl.deny.branches not enabled
814 acl: acl.deny.branches not enabled
792 acl: acl.allow enabled, 1 entries for user wilma
815 acl: acl.allow enabled, 1 entries for user wilma
793 acl: acl.deny enabled, 0 entries for user wilma
816 acl: acl.deny enabled, 0 entries for user wilma
794 acl: branch access granted: "ef1ea85a6374" on branch "default"
817 acl: branch access granted: "ef1ea85a6374" on branch "default"
795 acl: path access granted: "ef1ea85a6374"
818 acl: path access granted: "ef1ea85a6374"
796 acl: branch access granted: "f9cafe1212c8" on branch "default"
819 acl: branch access granted: "f9cafe1212c8" on branch "default"
797 acl: path access granted: "f9cafe1212c8"
820 acl: path access granted: "f9cafe1212c8"
798 acl: branch access granted: "911600dab2ae" on branch "default"
821 acl: branch access granted: "911600dab2ae" on branch "default"
799 error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
822 error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
800 transaction abort!
823 transaction abort!
801 rollback completed
824 rollback completed
802 abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
825 abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
803 no rollback information available
826 no rollback information available
804 0:6675d58eff77
827 0:6675d58eff77
805
828
806
829
807 file specified by acl.config does not exist
830 file specified by acl.config does not exist
808
831
809 $ echo '[acl]' >> $config
832 $ echo '[acl]' >> $config
810 $ echo 'config = ../acl.config' >> $config
833 $ echo 'config = ../acl.config' >> $config
811 $ do_push barney
834 $ do_push barney
812 Pushing as user barney
835 Pushing as user barney
813 hgrc = """
836 hgrc = """
814 [hooks]
837 [hooks]
815 pretxnchangegroup.acl = python:hgext.acl.hook
838 pretxnchangegroup.acl = python:hgext.acl.hook
816 [acl]
839 [acl]
817 sources = push
840 sources = push
818 [acl.allow]
841 [acl.allow]
819 foo/** = fred
842 foo/** = fred
820 [acl.deny]
843 [acl.deny]
821 foo/bar/** = fred
844 foo/bar/** = fred
822 foo/Bar/** = fred
845 foo/Bar/** = fred
823 [acl.allow]
846 [acl.allow]
824 ** = barney
847 ** = barney
825 **/*.txt = wilma
848 **/*.txt = wilma
826 [acl]
849 [acl]
827 config = ../acl.config
850 config = ../acl.config
828 """
851 """
829 pushing to ../b
852 pushing to ../b
830 query 1; heads
853 query 1; heads
831 searching for changes
854 searching for changes
832 all remote heads known locally
855 all remote heads known locally
856 listing keys for "bookmarks"
833 3 changesets found
857 3 changesets found
834 list of changesets:
858 list of changesets:
835 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
859 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
836 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
860 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
837 911600dab2ae7a9baff75958b84fe606851ce955
861 911600dab2ae7a9baff75958b84fe606851ce955
838 adding changesets
862 adding changesets
839 bundling: 1/3 changesets (33.33%)
863 bundling: 1/3 changesets (33.33%)
840 bundling: 2/3 changesets (66.67%)
864 bundling: 2/3 changesets (66.67%)
841 bundling: 3/3 changesets (100.00%)
865 bundling: 3/3 changesets (100.00%)
842 bundling: 1/3 manifests (33.33%)
866 bundling: 1/3 manifests (33.33%)
843 bundling: 2/3 manifests (66.67%)
867 bundling: 2/3 manifests (66.67%)
844 bundling: 3/3 manifests (100.00%)
868 bundling: 3/3 manifests (100.00%)
845 bundling: foo/Bar/file.txt 1/3 files (33.33%)
869 bundling: foo/Bar/file.txt 1/3 files (33.33%)
846 bundling: foo/file.txt 2/3 files (66.67%)
870 bundling: foo/file.txt 2/3 files (66.67%)
847 bundling: quux/file.py 3/3 files (100.00%)
871 bundling: quux/file.py 3/3 files (100.00%)
848 changesets: 1 chunks
872 changesets: 1 chunks
849 add changeset ef1ea85a6374
873 add changeset ef1ea85a6374
850 changesets: 2 chunks
874 changesets: 2 chunks
851 add changeset f9cafe1212c8
875 add changeset f9cafe1212c8
852 changesets: 3 chunks
876 changesets: 3 chunks
853 add changeset 911600dab2ae
877 add changeset 911600dab2ae
854 adding manifests
878 adding manifests
855 manifests: 1/3 chunks (33.33%)
879 manifests: 1/3 chunks (33.33%)
856 manifests: 2/3 chunks (66.67%)
880 manifests: 2/3 chunks (66.67%)
857 manifests: 3/3 chunks (100.00%)
881 manifests: 3/3 chunks (100.00%)
858 adding file changes
882 adding file changes
859 adding foo/Bar/file.txt revisions
883 adding foo/Bar/file.txt revisions
860 files: 1/3 chunks (33.33%)
884 files: 1/3 chunks (33.33%)
861 adding foo/file.txt revisions
885 adding foo/file.txt revisions
862 files: 2/3 chunks (66.67%)
886 files: 2/3 chunks (66.67%)
863 adding quux/file.py revisions
887 adding quux/file.py revisions
864 files: 3/3 chunks (100.00%)
888 files: 3/3 chunks (100.00%)
865 added 3 changesets with 3 changes to 3 files
889 added 3 changesets with 3 changes to 3 files
866 calling hook pretxnchangegroup.acl: hgext.acl.hook
890 calling hook pretxnchangegroup.acl: hgext.acl.hook
867 acl: checking access for user "barney"
891 acl: checking access for user "barney"
868 error: pretxnchangegroup.acl hook raised an exception: [Errno 2] *: '../acl.config' (glob)
892 error: pretxnchangegroup.acl hook raised an exception: [Errno 2] *: '../acl.config' (glob)
869 transaction abort!
893 transaction abort!
870 rollback completed
894 rollback completed
871 abort: *: ../acl.config (glob)
895 abort: *: ../acl.config (glob)
872 no rollback information available
896 no rollback information available
873 0:6675d58eff77
897 0:6675d58eff77
874
898
875
899
876 betty is allowed inside foo/ by a acl.config file
900 betty is allowed inside foo/ by a acl.config file
877
901
878 $ echo '[acl.allow]' >> acl.config
902 $ echo '[acl.allow]' >> acl.config
879 $ echo 'foo/** = betty' >> acl.config
903 $ echo 'foo/** = betty' >> acl.config
880 $ do_push betty
904 $ do_push betty
881 Pushing as user betty
905 Pushing as user betty
882 hgrc = """
906 hgrc = """
883 [hooks]
907 [hooks]
884 pretxnchangegroup.acl = python:hgext.acl.hook
908 pretxnchangegroup.acl = python:hgext.acl.hook
885 [acl]
909 [acl]
886 sources = push
910 sources = push
887 [acl.allow]
911 [acl.allow]
888 foo/** = fred
912 foo/** = fred
889 [acl.deny]
913 [acl.deny]
890 foo/bar/** = fred
914 foo/bar/** = fred
891 foo/Bar/** = fred
915 foo/Bar/** = fred
892 [acl.allow]
916 [acl.allow]
893 ** = barney
917 ** = barney
894 **/*.txt = wilma
918 **/*.txt = wilma
895 [acl]
919 [acl]
896 config = ../acl.config
920 config = ../acl.config
897 """
921 """
898 acl.config = """
922 acl.config = """
899 [acl.allow]
923 [acl.allow]
900 foo/** = betty
924 foo/** = betty
901 """
925 """
902 pushing to ../b
926 pushing to ../b
903 query 1; heads
927 query 1; heads
904 searching for changes
928 searching for changes
905 all remote heads known locally
929 all remote heads known locally
930 listing keys for "bookmarks"
906 3 changesets found
931 3 changesets found
907 list of changesets:
932 list of changesets:
908 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
933 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
909 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
934 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
910 911600dab2ae7a9baff75958b84fe606851ce955
935 911600dab2ae7a9baff75958b84fe606851ce955
911 adding changesets
936 adding changesets
912 bundling: 1/3 changesets (33.33%)
937 bundling: 1/3 changesets (33.33%)
913 bundling: 2/3 changesets (66.67%)
938 bundling: 2/3 changesets (66.67%)
914 bundling: 3/3 changesets (100.00%)
939 bundling: 3/3 changesets (100.00%)
915 bundling: 1/3 manifests (33.33%)
940 bundling: 1/3 manifests (33.33%)
916 bundling: 2/3 manifests (66.67%)
941 bundling: 2/3 manifests (66.67%)
917 bundling: 3/3 manifests (100.00%)
942 bundling: 3/3 manifests (100.00%)
918 bundling: foo/Bar/file.txt 1/3 files (33.33%)
943 bundling: foo/Bar/file.txt 1/3 files (33.33%)
919 bundling: foo/file.txt 2/3 files (66.67%)
944 bundling: foo/file.txt 2/3 files (66.67%)
920 bundling: quux/file.py 3/3 files (100.00%)
945 bundling: quux/file.py 3/3 files (100.00%)
921 changesets: 1 chunks
946 changesets: 1 chunks
922 add changeset ef1ea85a6374
947 add changeset ef1ea85a6374
923 changesets: 2 chunks
948 changesets: 2 chunks
924 add changeset f9cafe1212c8
949 add changeset f9cafe1212c8
925 changesets: 3 chunks
950 changesets: 3 chunks
926 add changeset 911600dab2ae
951 add changeset 911600dab2ae
927 adding manifests
952 adding manifests
928 manifests: 1/3 chunks (33.33%)
953 manifests: 1/3 chunks (33.33%)
929 manifests: 2/3 chunks (66.67%)
954 manifests: 2/3 chunks (66.67%)
930 manifests: 3/3 chunks (100.00%)
955 manifests: 3/3 chunks (100.00%)
931 adding file changes
956 adding file changes
932 adding foo/Bar/file.txt revisions
957 adding foo/Bar/file.txt revisions
933 files: 1/3 chunks (33.33%)
958 files: 1/3 chunks (33.33%)
934 adding foo/file.txt revisions
959 adding foo/file.txt revisions
935 files: 2/3 chunks (66.67%)
960 files: 2/3 chunks (66.67%)
936 adding quux/file.py revisions
961 adding quux/file.py revisions
937 files: 3/3 chunks (100.00%)
962 files: 3/3 chunks (100.00%)
938 added 3 changesets with 3 changes to 3 files
963 added 3 changesets with 3 changes to 3 files
939 calling hook pretxnchangegroup.acl: hgext.acl.hook
964 calling hook pretxnchangegroup.acl: hgext.acl.hook
940 acl: checking access for user "betty"
965 acl: checking access for user "betty"
941 acl: acl.allow.branches not enabled
966 acl: acl.allow.branches not enabled
942 acl: acl.deny.branches not enabled
967 acl: acl.deny.branches not enabled
943 acl: acl.allow enabled, 1 entries for user betty
968 acl: acl.allow enabled, 1 entries for user betty
944 acl: acl.deny enabled, 0 entries for user betty
969 acl: acl.deny enabled, 0 entries for user betty
945 acl: branch access granted: "ef1ea85a6374" on branch "default"
970 acl: branch access granted: "ef1ea85a6374" on branch "default"
946 acl: path access granted: "ef1ea85a6374"
971 acl: path access granted: "ef1ea85a6374"
947 acl: branch access granted: "f9cafe1212c8" on branch "default"
972 acl: branch access granted: "f9cafe1212c8" on branch "default"
948 acl: path access granted: "f9cafe1212c8"
973 acl: path access granted: "f9cafe1212c8"
949 acl: branch access granted: "911600dab2ae" on branch "default"
974 acl: branch access granted: "911600dab2ae" on branch "default"
950 error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
975 error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
951 transaction abort!
976 transaction abort!
952 rollback completed
977 rollback completed
953 abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
978 abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
954 no rollback information available
979 no rollback information available
955 0:6675d58eff77
980 0:6675d58eff77
956
981
957
982
958 acl.config can set only [acl.allow]/[acl.deny]
983 acl.config can set only [acl.allow]/[acl.deny]
959
984
960 $ echo '[hooks]' >> acl.config
985 $ echo '[hooks]' >> acl.config
961 $ echo 'changegroup.acl = false' >> acl.config
986 $ echo 'changegroup.acl = false' >> acl.config
962 $ do_push barney
987 $ do_push barney
963 Pushing as user barney
988 Pushing as user barney
964 hgrc = """
989 hgrc = """
965 [hooks]
990 [hooks]
966 pretxnchangegroup.acl = python:hgext.acl.hook
991 pretxnchangegroup.acl = python:hgext.acl.hook
967 [acl]
992 [acl]
968 sources = push
993 sources = push
969 [acl.allow]
994 [acl.allow]
970 foo/** = fred
995 foo/** = fred
971 [acl.deny]
996 [acl.deny]
972 foo/bar/** = fred
997 foo/bar/** = fred
973 foo/Bar/** = fred
998 foo/Bar/** = fred
974 [acl.allow]
999 [acl.allow]
975 ** = barney
1000 ** = barney
976 **/*.txt = wilma
1001 **/*.txt = wilma
977 [acl]
1002 [acl]
978 config = ../acl.config
1003 config = ../acl.config
979 """
1004 """
980 acl.config = """
1005 acl.config = """
981 [acl.allow]
1006 [acl.allow]
982 foo/** = betty
1007 foo/** = betty
983 [hooks]
1008 [hooks]
984 changegroup.acl = false
1009 changegroup.acl = false
985 """
1010 """
986 pushing to ../b
1011 pushing to ../b
987 query 1; heads
1012 query 1; heads
988 searching for changes
1013 searching for changes
989 all remote heads known locally
1014 all remote heads known locally
1015 listing keys for "bookmarks"
990 3 changesets found
1016 3 changesets found
991 list of changesets:
1017 list of changesets:
992 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1018 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
993 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1019 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
994 911600dab2ae7a9baff75958b84fe606851ce955
1020 911600dab2ae7a9baff75958b84fe606851ce955
995 adding changesets
1021 adding changesets
996 bundling: 1/3 changesets (33.33%)
1022 bundling: 1/3 changesets (33.33%)
997 bundling: 2/3 changesets (66.67%)
1023 bundling: 2/3 changesets (66.67%)
998 bundling: 3/3 changesets (100.00%)
1024 bundling: 3/3 changesets (100.00%)
999 bundling: 1/3 manifests (33.33%)
1025 bundling: 1/3 manifests (33.33%)
1000 bundling: 2/3 manifests (66.67%)
1026 bundling: 2/3 manifests (66.67%)
1001 bundling: 3/3 manifests (100.00%)
1027 bundling: 3/3 manifests (100.00%)
1002 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1028 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1003 bundling: foo/file.txt 2/3 files (66.67%)
1029 bundling: foo/file.txt 2/3 files (66.67%)
1004 bundling: quux/file.py 3/3 files (100.00%)
1030 bundling: quux/file.py 3/3 files (100.00%)
1005 changesets: 1 chunks
1031 changesets: 1 chunks
1006 add changeset ef1ea85a6374
1032 add changeset ef1ea85a6374
1007 changesets: 2 chunks
1033 changesets: 2 chunks
1008 add changeset f9cafe1212c8
1034 add changeset f9cafe1212c8
1009 changesets: 3 chunks
1035 changesets: 3 chunks
1010 add changeset 911600dab2ae
1036 add changeset 911600dab2ae
1011 adding manifests
1037 adding manifests
1012 manifests: 1/3 chunks (33.33%)
1038 manifests: 1/3 chunks (33.33%)
1013 manifests: 2/3 chunks (66.67%)
1039 manifests: 2/3 chunks (66.67%)
1014 manifests: 3/3 chunks (100.00%)
1040 manifests: 3/3 chunks (100.00%)
1015 adding file changes
1041 adding file changes
1016 adding foo/Bar/file.txt revisions
1042 adding foo/Bar/file.txt revisions
1017 files: 1/3 chunks (33.33%)
1043 files: 1/3 chunks (33.33%)
1018 adding foo/file.txt revisions
1044 adding foo/file.txt revisions
1019 files: 2/3 chunks (66.67%)
1045 files: 2/3 chunks (66.67%)
1020 adding quux/file.py revisions
1046 adding quux/file.py revisions
1021 files: 3/3 chunks (100.00%)
1047 files: 3/3 chunks (100.00%)
1022 added 3 changesets with 3 changes to 3 files
1048 added 3 changesets with 3 changes to 3 files
1023 calling hook pretxnchangegroup.acl: hgext.acl.hook
1049 calling hook pretxnchangegroup.acl: hgext.acl.hook
1024 acl: checking access for user "barney"
1050 acl: checking access for user "barney"
1025 acl: acl.allow.branches not enabled
1051 acl: acl.allow.branches not enabled
1026 acl: acl.deny.branches not enabled
1052 acl: acl.deny.branches not enabled
1027 acl: acl.allow enabled, 1 entries for user barney
1053 acl: acl.allow enabled, 1 entries for user barney
1028 acl: acl.deny enabled, 0 entries for user barney
1054 acl: acl.deny enabled, 0 entries for user barney
1029 acl: branch access granted: "ef1ea85a6374" on branch "default"
1055 acl: branch access granted: "ef1ea85a6374" on branch "default"
1030 acl: path access granted: "ef1ea85a6374"
1056 acl: path access granted: "ef1ea85a6374"
1031 acl: branch access granted: "f9cafe1212c8" on branch "default"
1057 acl: branch access granted: "f9cafe1212c8" on branch "default"
1032 acl: path access granted: "f9cafe1212c8"
1058 acl: path access granted: "f9cafe1212c8"
1033 acl: branch access granted: "911600dab2ae" on branch "default"
1059 acl: branch access granted: "911600dab2ae" on branch "default"
1034 acl: path access granted: "911600dab2ae"
1060 acl: path access granted: "911600dab2ae"
1061 listing keys for "phases"
1062 listing keys for "namespaces"
1035 updating the branch cache
1063 updating the branch cache
1036 checking for updated bookmarks
1064 checking for updated bookmarks
1065 listing keys for "bookmarks"
1037 repository tip rolled back to revision 0 (undo push)
1066 repository tip rolled back to revision 0 (undo push)
1038 0:6675d58eff77
1067 0:6675d58eff77
1039
1068
1040
1069
1041 asterisk
1070 asterisk
1042
1071
1043 $ init_config
1072 $ init_config
1044
1073
1045 asterisk test
1074 asterisk test
1046
1075
1047 $ echo '[acl.allow]' >> $config
1076 $ echo '[acl.allow]' >> $config
1048 $ echo "** = fred" >> $config
1077 $ echo "** = fred" >> $config
1049
1078
1050 fred is always allowed
1079 fred is always allowed
1051
1080
1052 $ do_push fred
1081 $ do_push fred
1053 Pushing as user fred
1082 Pushing as user fred
1054 hgrc = """
1083 hgrc = """
1055 [acl]
1084 [acl]
1056 sources = push
1085 sources = push
1057 [extensions]
1086 [extensions]
1058 [acl.allow]
1087 [acl.allow]
1059 ** = fred
1088 ** = fred
1060 """
1089 """
1061 pushing to ../b
1090 pushing to ../b
1062 query 1; heads
1091 query 1; heads
1063 searching for changes
1092 searching for changes
1064 all remote heads known locally
1093 all remote heads known locally
1065 invalidating branch cache (tip differs)
1094 invalidating branch cache (tip differs)
1095 listing keys for "bookmarks"
1066 3 changesets found
1096 3 changesets found
1067 list of changesets:
1097 list of changesets:
1068 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1098 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1069 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1099 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1070 911600dab2ae7a9baff75958b84fe606851ce955
1100 911600dab2ae7a9baff75958b84fe606851ce955
1071 adding changesets
1101 adding changesets
1072 bundling: 1/3 changesets (33.33%)
1102 bundling: 1/3 changesets (33.33%)
1073 bundling: 2/3 changesets (66.67%)
1103 bundling: 2/3 changesets (66.67%)
1074 bundling: 3/3 changesets (100.00%)
1104 bundling: 3/3 changesets (100.00%)
1075 bundling: 1/3 manifests (33.33%)
1105 bundling: 1/3 manifests (33.33%)
1076 bundling: 2/3 manifests (66.67%)
1106 bundling: 2/3 manifests (66.67%)
1077 bundling: 3/3 manifests (100.00%)
1107 bundling: 3/3 manifests (100.00%)
1078 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1108 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1079 bundling: foo/file.txt 2/3 files (66.67%)
1109 bundling: foo/file.txt 2/3 files (66.67%)
1080 bundling: quux/file.py 3/3 files (100.00%)
1110 bundling: quux/file.py 3/3 files (100.00%)
1081 changesets: 1 chunks
1111 changesets: 1 chunks
1082 add changeset ef1ea85a6374
1112 add changeset ef1ea85a6374
1083 changesets: 2 chunks
1113 changesets: 2 chunks
1084 add changeset f9cafe1212c8
1114 add changeset f9cafe1212c8
1085 changesets: 3 chunks
1115 changesets: 3 chunks
1086 add changeset 911600dab2ae
1116 add changeset 911600dab2ae
1087 adding manifests
1117 adding manifests
1088 manifests: 1/3 chunks (33.33%)
1118 manifests: 1/3 chunks (33.33%)
1089 manifests: 2/3 chunks (66.67%)
1119 manifests: 2/3 chunks (66.67%)
1090 manifests: 3/3 chunks (100.00%)
1120 manifests: 3/3 chunks (100.00%)
1091 adding file changes
1121 adding file changes
1092 adding foo/Bar/file.txt revisions
1122 adding foo/Bar/file.txt revisions
1093 files: 1/3 chunks (33.33%)
1123 files: 1/3 chunks (33.33%)
1094 adding foo/file.txt revisions
1124 adding foo/file.txt revisions
1095 files: 2/3 chunks (66.67%)
1125 files: 2/3 chunks (66.67%)
1096 adding quux/file.py revisions
1126 adding quux/file.py revisions
1097 files: 3/3 chunks (100.00%)
1127 files: 3/3 chunks (100.00%)
1098 added 3 changesets with 3 changes to 3 files
1128 added 3 changesets with 3 changes to 3 files
1099 calling hook pretxnchangegroup.acl: hgext.acl.hook
1129 calling hook pretxnchangegroup.acl: hgext.acl.hook
1100 acl: checking access for user "fred"
1130 acl: checking access for user "fred"
1101 acl: acl.allow.branches not enabled
1131 acl: acl.allow.branches not enabled
1102 acl: acl.deny.branches not enabled
1132 acl: acl.deny.branches not enabled
1103 acl: acl.allow enabled, 1 entries for user fred
1133 acl: acl.allow enabled, 1 entries for user fred
1104 acl: acl.deny not enabled
1134 acl: acl.deny not enabled
1105 acl: branch access granted: "ef1ea85a6374" on branch "default"
1135 acl: branch access granted: "ef1ea85a6374" on branch "default"
1106 acl: path access granted: "ef1ea85a6374"
1136 acl: path access granted: "ef1ea85a6374"
1107 acl: branch access granted: "f9cafe1212c8" on branch "default"
1137 acl: branch access granted: "f9cafe1212c8" on branch "default"
1108 acl: path access granted: "f9cafe1212c8"
1138 acl: path access granted: "f9cafe1212c8"
1109 acl: branch access granted: "911600dab2ae" on branch "default"
1139 acl: branch access granted: "911600dab2ae" on branch "default"
1110 acl: path access granted: "911600dab2ae"
1140 acl: path access granted: "911600dab2ae"
1141 listing keys for "phases"
1142 listing keys for "namespaces"
1111 updating the branch cache
1143 updating the branch cache
1112 checking for updated bookmarks
1144 checking for updated bookmarks
1145 listing keys for "bookmarks"
1113 repository tip rolled back to revision 0 (undo push)
1146 repository tip rolled back to revision 0 (undo push)
1114 0:6675d58eff77
1147 0:6675d58eff77
1115
1148
1116
1149
1117 $ echo '[acl.deny]' >> $config
1150 $ echo '[acl.deny]' >> $config
1118 $ echo "foo/Bar/** = *" >> $config
1151 $ echo "foo/Bar/** = *" >> $config
1119
1152
1120 no one is allowed inside foo/Bar/
1153 no one is allowed inside foo/Bar/
1121
1154
1122 $ do_push fred
1155 $ do_push fred
1123 Pushing as user fred
1156 Pushing as user fred
1124 hgrc = """
1157 hgrc = """
1125 [acl]
1158 [acl]
1126 sources = push
1159 sources = push
1127 [extensions]
1160 [extensions]
1128 [acl.allow]
1161 [acl.allow]
1129 ** = fred
1162 ** = fred
1130 [acl.deny]
1163 [acl.deny]
1131 foo/Bar/** = *
1164 foo/Bar/** = *
1132 """
1165 """
1133 pushing to ../b
1166 pushing to ../b
1134 query 1; heads
1167 query 1; heads
1135 searching for changes
1168 searching for changes
1136 all remote heads known locally
1169 all remote heads known locally
1137 invalidating branch cache (tip differs)
1170 invalidating branch cache (tip differs)
1171 listing keys for "bookmarks"
1138 3 changesets found
1172 3 changesets found
1139 list of changesets:
1173 list of changesets:
1140 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1174 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1141 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1175 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1142 911600dab2ae7a9baff75958b84fe606851ce955
1176 911600dab2ae7a9baff75958b84fe606851ce955
1143 adding changesets
1177 adding changesets
1144 bundling: 1/3 changesets (33.33%)
1178 bundling: 1/3 changesets (33.33%)
1145 bundling: 2/3 changesets (66.67%)
1179 bundling: 2/3 changesets (66.67%)
1146 bundling: 3/3 changesets (100.00%)
1180 bundling: 3/3 changesets (100.00%)
1147 bundling: 1/3 manifests (33.33%)
1181 bundling: 1/3 manifests (33.33%)
1148 bundling: 2/3 manifests (66.67%)
1182 bundling: 2/3 manifests (66.67%)
1149 bundling: 3/3 manifests (100.00%)
1183 bundling: 3/3 manifests (100.00%)
1150 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1184 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1151 bundling: foo/file.txt 2/3 files (66.67%)
1185 bundling: foo/file.txt 2/3 files (66.67%)
1152 bundling: quux/file.py 3/3 files (100.00%)
1186 bundling: quux/file.py 3/3 files (100.00%)
1153 changesets: 1 chunks
1187 changesets: 1 chunks
1154 add changeset ef1ea85a6374
1188 add changeset ef1ea85a6374
1155 changesets: 2 chunks
1189 changesets: 2 chunks
1156 add changeset f9cafe1212c8
1190 add changeset f9cafe1212c8
1157 changesets: 3 chunks
1191 changesets: 3 chunks
1158 add changeset 911600dab2ae
1192 add changeset 911600dab2ae
1159 adding manifests
1193 adding manifests
1160 manifests: 1/3 chunks (33.33%)
1194 manifests: 1/3 chunks (33.33%)
1161 manifests: 2/3 chunks (66.67%)
1195 manifests: 2/3 chunks (66.67%)
1162 manifests: 3/3 chunks (100.00%)
1196 manifests: 3/3 chunks (100.00%)
1163 adding file changes
1197 adding file changes
1164 adding foo/Bar/file.txt revisions
1198 adding foo/Bar/file.txt revisions
1165 files: 1/3 chunks (33.33%)
1199 files: 1/3 chunks (33.33%)
1166 adding foo/file.txt revisions
1200 adding foo/file.txt revisions
1167 files: 2/3 chunks (66.67%)
1201 files: 2/3 chunks (66.67%)
1168 adding quux/file.py revisions
1202 adding quux/file.py revisions
1169 files: 3/3 chunks (100.00%)
1203 files: 3/3 chunks (100.00%)
1170 added 3 changesets with 3 changes to 3 files
1204 added 3 changesets with 3 changes to 3 files
1171 calling hook pretxnchangegroup.acl: hgext.acl.hook
1205 calling hook pretxnchangegroup.acl: hgext.acl.hook
1172 acl: checking access for user "fred"
1206 acl: checking access for user "fred"
1173 acl: acl.allow.branches not enabled
1207 acl: acl.allow.branches not enabled
1174 acl: acl.deny.branches not enabled
1208 acl: acl.deny.branches not enabled
1175 acl: acl.allow enabled, 1 entries for user fred
1209 acl: acl.allow enabled, 1 entries for user fred
1176 acl: acl.deny enabled, 1 entries for user fred
1210 acl: acl.deny enabled, 1 entries for user fred
1177 acl: branch access granted: "ef1ea85a6374" on branch "default"
1211 acl: branch access granted: "ef1ea85a6374" on branch "default"
1178 acl: path access granted: "ef1ea85a6374"
1212 acl: path access granted: "ef1ea85a6374"
1179 acl: branch access granted: "f9cafe1212c8" on branch "default"
1213 acl: branch access granted: "f9cafe1212c8" on branch "default"
1180 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1214 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1181 transaction abort!
1215 transaction abort!
1182 rollback completed
1216 rollback completed
1183 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1217 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1184 no rollback information available
1218 no rollback information available
1185 0:6675d58eff77
1219 0:6675d58eff77
1186
1220
1187
1221
1188 Groups
1222 Groups
1189
1223
1190 $ init_config
1224 $ init_config
1191
1225
1192 OS-level groups
1226 OS-level groups
1193
1227
1194 $ echo '[acl.allow]' >> $config
1228 $ echo '[acl.allow]' >> $config
1195 $ echo "** = @group1" >> $config
1229 $ echo "** = @group1" >> $config
1196
1230
1197 @group1 is always allowed
1231 @group1 is always allowed
1198
1232
1199 $ do_push fred
1233 $ do_push fred
1200 Pushing as user fred
1234 Pushing as user fred
1201 hgrc = """
1235 hgrc = """
1202 [acl]
1236 [acl]
1203 sources = push
1237 sources = push
1204 [extensions]
1238 [extensions]
1205 [acl.allow]
1239 [acl.allow]
1206 ** = @group1
1240 ** = @group1
1207 """
1241 """
1208 pushing to ../b
1242 pushing to ../b
1209 query 1; heads
1243 query 1; heads
1210 searching for changes
1244 searching for changes
1211 all remote heads known locally
1245 all remote heads known locally
1246 listing keys for "bookmarks"
1212 3 changesets found
1247 3 changesets found
1213 list of changesets:
1248 list of changesets:
1214 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1249 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1215 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1250 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1216 911600dab2ae7a9baff75958b84fe606851ce955
1251 911600dab2ae7a9baff75958b84fe606851ce955
1217 adding changesets
1252 adding changesets
1218 bundling: 1/3 changesets (33.33%)
1253 bundling: 1/3 changesets (33.33%)
1219 bundling: 2/3 changesets (66.67%)
1254 bundling: 2/3 changesets (66.67%)
1220 bundling: 3/3 changesets (100.00%)
1255 bundling: 3/3 changesets (100.00%)
1221 bundling: 1/3 manifests (33.33%)
1256 bundling: 1/3 manifests (33.33%)
1222 bundling: 2/3 manifests (66.67%)
1257 bundling: 2/3 manifests (66.67%)
1223 bundling: 3/3 manifests (100.00%)
1258 bundling: 3/3 manifests (100.00%)
1224 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1259 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1225 bundling: foo/file.txt 2/3 files (66.67%)
1260 bundling: foo/file.txt 2/3 files (66.67%)
1226 bundling: quux/file.py 3/3 files (100.00%)
1261 bundling: quux/file.py 3/3 files (100.00%)
1227 changesets: 1 chunks
1262 changesets: 1 chunks
1228 add changeset ef1ea85a6374
1263 add changeset ef1ea85a6374
1229 changesets: 2 chunks
1264 changesets: 2 chunks
1230 add changeset f9cafe1212c8
1265 add changeset f9cafe1212c8
1231 changesets: 3 chunks
1266 changesets: 3 chunks
1232 add changeset 911600dab2ae
1267 add changeset 911600dab2ae
1233 adding manifests
1268 adding manifests
1234 manifests: 1/3 chunks (33.33%)
1269 manifests: 1/3 chunks (33.33%)
1235 manifests: 2/3 chunks (66.67%)
1270 manifests: 2/3 chunks (66.67%)
1236 manifests: 3/3 chunks (100.00%)
1271 manifests: 3/3 chunks (100.00%)
1237 adding file changes
1272 adding file changes
1238 adding foo/Bar/file.txt revisions
1273 adding foo/Bar/file.txt revisions
1239 files: 1/3 chunks (33.33%)
1274 files: 1/3 chunks (33.33%)
1240 adding foo/file.txt revisions
1275 adding foo/file.txt revisions
1241 files: 2/3 chunks (66.67%)
1276 files: 2/3 chunks (66.67%)
1242 adding quux/file.py revisions
1277 adding quux/file.py revisions
1243 files: 3/3 chunks (100.00%)
1278 files: 3/3 chunks (100.00%)
1244 added 3 changesets with 3 changes to 3 files
1279 added 3 changesets with 3 changes to 3 files
1245 calling hook pretxnchangegroup.acl: hgext.acl.hook
1280 calling hook pretxnchangegroup.acl: hgext.acl.hook
1246 acl: checking access for user "fred"
1281 acl: checking access for user "fred"
1247 acl: acl.allow.branches not enabled
1282 acl: acl.allow.branches not enabled
1248 acl: acl.deny.branches not enabled
1283 acl: acl.deny.branches not enabled
1249 acl: "group1" not defined in [acl.groups]
1284 acl: "group1" not defined in [acl.groups]
1250 acl: acl.allow enabled, 1 entries for user fred
1285 acl: acl.allow enabled, 1 entries for user fred
1251 acl: acl.deny not enabled
1286 acl: acl.deny not enabled
1252 acl: branch access granted: "ef1ea85a6374" on branch "default"
1287 acl: branch access granted: "ef1ea85a6374" on branch "default"
1253 acl: path access granted: "ef1ea85a6374"
1288 acl: path access granted: "ef1ea85a6374"
1254 acl: branch access granted: "f9cafe1212c8" on branch "default"
1289 acl: branch access granted: "f9cafe1212c8" on branch "default"
1255 acl: path access granted: "f9cafe1212c8"
1290 acl: path access granted: "f9cafe1212c8"
1256 acl: branch access granted: "911600dab2ae" on branch "default"
1291 acl: branch access granted: "911600dab2ae" on branch "default"
1257 acl: path access granted: "911600dab2ae"
1292 acl: path access granted: "911600dab2ae"
1293 listing keys for "phases"
1294 listing keys for "namespaces"
1258 updating the branch cache
1295 updating the branch cache
1259 checking for updated bookmarks
1296 checking for updated bookmarks
1297 listing keys for "bookmarks"
1260 repository tip rolled back to revision 0 (undo push)
1298 repository tip rolled back to revision 0 (undo push)
1261 0:6675d58eff77
1299 0:6675d58eff77
1262
1300
1263
1301
1264 $ echo '[acl.deny]' >> $config
1302 $ echo '[acl.deny]' >> $config
1265 $ echo "foo/Bar/** = @group1" >> $config
1303 $ echo "foo/Bar/** = @group1" >> $config
1266
1304
1267 @group is allowed inside anything but foo/Bar/
1305 @group is allowed inside anything but foo/Bar/
1268
1306
1269 $ do_push fred
1307 $ do_push fred
1270 Pushing as user fred
1308 Pushing as user fred
1271 hgrc = """
1309 hgrc = """
1272 [acl]
1310 [acl]
1273 sources = push
1311 sources = push
1274 [extensions]
1312 [extensions]
1275 [acl.allow]
1313 [acl.allow]
1276 ** = @group1
1314 ** = @group1
1277 [acl.deny]
1315 [acl.deny]
1278 foo/Bar/** = @group1
1316 foo/Bar/** = @group1
1279 """
1317 """
1280 pushing to ../b
1318 pushing to ../b
1281 query 1; heads
1319 query 1; heads
1282 searching for changes
1320 searching for changes
1283 all remote heads known locally
1321 all remote heads known locally
1284 invalidating branch cache (tip differs)
1322 invalidating branch cache (tip differs)
1323 listing keys for "bookmarks"
1285 3 changesets found
1324 3 changesets found
1286 list of changesets:
1325 list of changesets:
1287 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1326 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1288 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1327 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1289 911600dab2ae7a9baff75958b84fe606851ce955
1328 911600dab2ae7a9baff75958b84fe606851ce955
1290 adding changesets
1329 adding changesets
1291 bundling: 1/3 changesets (33.33%)
1330 bundling: 1/3 changesets (33.33%)
1292 bundling: 2/3 changesets (66.67%)
1331 bundling: 2/3 changesets (66.67%)
1293 bundling: 3/3 changesets (100.00%)
1332 bundling: 3/3 changesets (100.00%)
1294 bundling: 1/3 manifests (33.33%)
1333 bundling: 1/3 manifests (33.33%)
1295 bundling: 2/3 manifests (66.67%)
1334 bundling: 2/3 manifests (66.67%)
1296 bundling: 3/3 manifests (100.00%)
1335 bundling: 3/3 manifests (100.00%)
1297 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1336 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1298 bundling: foo/file.txt 2/3 files (66.67%)
1337 bundling: foo/file.txt 2/3 files (66.67%)
1299 bundling: quux/file.py 3/3 files (100.00%)
1338 bundling: quux/file.py 3/3 files (100.00%)
1300 changesets: 1 chunks
1339 changesets: 1 chunks
1301 add changeset ef1ea85a6374
1340 add changeset ef1ea85a6374
1302 changesets: 2 chunks
1341 changesets: 2 chunks
1303 add changeset f9cafe1212c8
1342 add changeset f9cafe1212c8
1304 changesets: 3 chunks
1343 changesets: 3 chunks
1305 add changeset 911600dab2ae
1344 add changeset 911600dab2ae
1306 adding manifests
1345 adding manifests
1307 manifests: 1/3 chunks (33.33%)
1346 manifests: 1/3 chunks (33.33%)
1308 manifests: 2/3 chunks (66.67%)
1347 manifests: 2/3 chunks (66.67%)
1309 manifests: 3/3 chunks (100.00%)
1348 manifests: 3/3 chunks (100.00%)
1310 adding file changes
1349 adding file changes
1311 adding foo/Bar/file.txt revisions
1350 adding foo/Bar/file.txt revisions
1312 files: 1/3 chunks (33.33%)
1351 files: 1/3 chunks (33.33%)
1313 adding foo/file.txt revisions
1352 adding foo/file.txt revisions
1314 files: 2/3 chunks (66.67%)
1353 files: 2/3 chunks (66.67%)
1315 adding quux/file.py revisions
1354 adding quux/file.py revisions
1316 files: 3/3 chunks (100.00%)
1355 files: 3/3 chunks (100.00%)
1317 added 3 changesets with 3 changes to 3 files
1356 added 3 changesets with 3 changes to 3 files
1318 calling hook pretxnchangegroup.acl: hgext.acl.hook
1357 calling hook pretxnchangegroup.acl: hgext.acl.hook
1319 acl: checking access for user "fred"
1358 acl: checking access for user "fred"
1320 acl: acl.allow.branches not enabled
1359 acl: acl.allow.branches not enabled
1321 acl: acl.deny.branches not enabled
1360 acl: acl.deny.branches not enabled
1322 acl: "group1" not defined in [acl.groups]
1361 acl: "group1" not defined in [acl.groups]
1323 acl: acl.allow enabled, 1 entries for user fred
1362 acl: acl.allow enabled, 1 entries for user fred
1324 acl: "group1" not defined in [acl.groups]
1363 acl: "group1" not defined in [acl.groups]
1325 acl: acl.deny enabled, 1 entries for user fred
1364 acl: acl.deny enabled, 1 entries for user fred
1326 acl: branch access granted: "ef1ea85a6374" on branch "default"
1365 acl: branch access granted: "ef1ea85a6374" on branch "default"
1327 acl: path access granted: "ef1ea85a6374"
1366 acl: path access granted: "ef1ea85a6374"
1328 acl: branch access granted: "f9cafe1212c8" on branch "default"
1367 acl: branch access granted: "f9cafe1212c8" on branch "default"
1329 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1368 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1330 transaction abort!
1369 transaction abort!
1331 rollback completed
1370 rollback completed
1332 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1371 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1333 no rollback information available
1372 no rollback information available
1334 0:6675d58eff77
1373 0:6675d58eff77
1335
1374
1336
1375
1337 Invalid group
1376 Invalid group
1338
1377
1339 Disable the fakegroups trick to get real failures
1378 Disable the fakegroups trick to get real failures
1340
1379
1341 $ grep -v fakegroups $config > config.tmp
1380 $ grep -v fakegroups $config > config.tmp
1342 $ mv config.tmp $config
1381 $ mv config.tmp $config
1343 $ echo '[acl.allow]' >> $config
1382 $ echo '[acl.allow]' >> $config
1344 $ echo "** = @unlikelytoexist" >> $config
1383 $ echo "** = @unlikelytoexist" >> $config
1345 $ do_push fred 2>&1 | grep unlikelytoexist
1384 $ do_push fred 2>&1 | grep unlikelytoexist
1346 ** = @unlikelytoexist
1385 ** = @unlikelytoexist
1347 acl: "unlikelytoexist" not defined in [acl.groups]
1386 acl: "unlikelytoexist" not defined in [acl.groups]
1348 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1387 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1349 abort: group 'unlikelytoexist' is undefined
1388 abort: group 'unlikelytoexist' is undefined
1350
1389
1351
1390
1352 Branch acl tests setup
1391 Branch acl tests setup
1353
1392
1354 $ init_config
1393 $ init_config
1355 $ cd b
1394 $ cd b
1356 $ hg up
1395 $ hg up
1357 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1396 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1358 $ hg branch foobar
1397 $ hg branch foobar
1359 marked working directory as branch foobar
1398 marked working directory as branch foobar
1360 (branches are permanent and global, did you want a bookmark?)
1399 (branches are permanent and global, did you want a bookmark?)
1361 $ hg commit -m 'create foobar'
1400 $ hg commit -m 'create foobar'
1362 $ echo 'foo contents' > abc.txt
1401 $ echo 'foo contents' > abc.txt
1363 $ hg add abc.txt
1402 $ hg add abc.txt
1364 $ hg commit -m 'foobar contents'
1403 $ hg commit -m 'foobar contents'
1365 $ cd ..
1404 $ cd ..
1366 $ hg --cwd a pull ../b
1405 $ hg --cwd a pull ../b
1367 pulling from ../b
1406 pulling from ../b
1368 searching for changes
1407 searching for changes
1369 adding changesets
1408 adding changesets
1370 adding manifests
1409 adding manifests
1371 adding file changes
1410 adding file changes
1372 added 2 changesets with 1 changes to 1 files (+1 heads)
1411 added 2 changesets with 1 changes to 1 files (+1 heads)
1373 (run 'hg heads' to see heads)
1412 (run 'hg heads' to see heads)
1374
1413
1375 Create additional changeset on foobar branch
1414 Create additional changeset on foobar branch
1376
1415
1377 $ cd a
1416 $ cd a
1378 $ hg up -C foobar
1417 $ hg up -C foobar
1379 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1418 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1380 $ echo 'foo contents2' > abc.txt
1419 $ echo 'foo contents2' > abc.txt
1381 $ hg commit -m 'foobar contents2'
1420 $ hg commit -m 'foobar contents2'
1382 $ cd ..
1421 $ cd ..
1383
1422
1384
1423
1385 No branch acls specified
1424 No branch acls specified
1386
1425
1387 $ do_push astro
1426 $ do_push astro
1388 Pushing as user astro
1427 Pushing as user astro
1389 hgrc = """
1428 hgrc = """
1390 [acl]
1429 [acl]
1391 sources = push
1430 sources = push
1392 [extensions]
1431 [extensions]
1393 """
1432 """
1394 pushing to ../b
1433 pushing to ../b
1395 query 1; heads
1434 query 1; heads
1396 searching for changes
1435 searching for changes
1397 all remote heads known locally
1436 all remote heads known locally
1437 listing keys for "bookmarks"
1398 4 changesets found
1438 4 changesets found
1399 list of changesets:
1439 list of changesets:
1400 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1440 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1401 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1441 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1402 911600dab2ae7a9baff75958b84fe606851ce955
1442 911600dab2ae7a9baff75958b84fe606851ce955
1403 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1443 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1404 adding changesets
1444 adding changesets
1405 bundling: 1/4 changesets (25.00%)
1445 bundling: 1/4 changesets (25.00%)
1406 bundling: 2/4 changesets (50.00%)
1446 bundling: 2/4 changesets (50.00%)
1407 bundling: 3/4 changesets (75.00%)
1447 bundling: 3/4 changesets (75.00%)
1408 bundling: 4/4 changesets (100.00%)
1448 bundling: 4/4 changesets (100.00%)
1409 bundling: 1/4 manifests (25.00%)
1449 bundling: 1/4 manifests (25.00%)
1410 bundling: 2/4 manifests (50.00%)
1450 bundling: 2/4 manifests (50.00%)
1411 bundling: 3/4 manifests (75.00%)
1451 bundling: 3/4 manifests (75.00%)
1412 bundling: 4/4 manifests (100.00%)
1452 bundling: 4/4 manifests (100.00%)
1413 bundling: abc.txt 1/4 files (25.00%)
1453 bundling: abc.txt 1/4 files (25.00%)
1414 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1454 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1415 bundling: foo/file.txt 3/4 files (75.00%)
1455 bundling: foo/file.txt 3/4 files (75.00%)
1416 bundling: quux/file.py 4/4 files (100.00%)
1456 bundling: quux/file.py 4/4 files (100.00%)
1417 changesets: 1 chunks
1457 changesets: 1 chunks
1418 add changeset ef1ea85a6374
1458 add changeset ef1ea85a6374
1419 changesets: 2 chunks
1459 changesets: 2 chunks
1420 add changeset f9cafe1212c8
1460 add changeset f9cafe1212c8
1421 changesets: 3 chunks
1461 changesets: 3 chunks
1422 add changeset 911600dab2ae
1462 add changeset 911600dab2ae
1423 changesets: 4 chunks
1463 changesets: 4 chunks
1424 add changeset e8fc755d4d82
1464 add changeset e8fc755d4d82
1425 adding manifests
1465 adding manifests
1426 manifests: 1/4 chunks (25.00%)
1466 manifests: 1/4 chunks (25.00%)
1427 manifests: 2/4 chunks (50.00%)
1467 manifests: 2/4 chunks (50.00%)
1428 manifests: 3/4 chunks (75.00%)
1468 manifests: 3/4 chunks (75.00%)
1429 manifests: 4/4 chunks (100.00%)
1469 manifests: 4/4 chunks (100.00%)
1430 adding file changes
1470 adding file changes
1431 adding abc.txt revisions
1471 adding abc.txt revisions
1432 files: 1/4 chunks (25.00%)
1472 files: 1/4 chunks (25.00%)
1433 adding foo/Bar/file.txt revisions
1473 adding foo/Bar/file.txt revisions
1434 files: 2/4 chunks (50.00%)
1474 files: 2/4 chunks (50.00%)
1435 adding foo/file.txt revisions
1475 adding foo/file.txt revisions
1436 files: 3/4 chunks (75.00%)
1476 files: 3/4 chunks (75.00%)
1437 adding quux/file.py revisions
1477 adding quux/file.py revisions
1438 files: 4/4 chunks (100.00%)
1478 files: 4/4 chunks (100.00%)
1439 added 4 changesets with 4 changes to 4 files (+1 heads)
1479 added 4 changesets with 4 changes to 4 files (+1 heads)
1440 calling hook pretxnchangegroup.acl: hgext.acl.hook
1480 calling hook pretxnchangegroup.acl: hgext.acl.hook
1441 acl: checking access for user "astro"
1481 acl: checking access for user "astro"
1442 acl: acl.allow.branches not enabled
1482 acl: acl.allow.branches not enabled
1443 acl: acl.deny.branches not enabled
1483 acl: acl.deny.branches not enabled
1444 acl: acl.allow not enabled
1484 acl: acl.allow not enabled
1445 acl: acl.deny not enabled
1485 acl: acl.deny not enabled
1446 acl: branch access granted: "ef1ea85a6374" on branch "default"
1486 acl: branch access granted: "ef1ea85a6374" on branch "default"
1447 acl: path access granted: "ef1ea85a6374"
1487 acl: path access granted: "ef1ea85a6374"
1448 acl: branch access granted: "f9cafe1212c8" on branch "default"
1488 acl: branch access granted: "f9cafe1212c8" on branch "default"
1449 acl: path access granted: "f9cafe1212c8"
1489 acl: path access granted: "f9cafe1212c8"
1450 acl: branch access granted: "911600dab2ae" on branch "default"
1490 acl: branch access granted: "911600dab2ae" on branch "default"
1451 acl: path access granted: "911600dab2ae"
1491 acl: path access granted: "911600dab2ae"
1452 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1492 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1453 acl: path access granted: "e8fc755d4d82"
1493 acl: path access granted: "e8fc755d4d82"
1494 listing keys for "phases"
1495 listing keys for "namespaces"
1454 updating the branch cache
1496 updating the branch cache
1455 checking for updated bookmarks
1497 checking for updated bookmarks
1498 listing keys for "bookmarks"
1456 repository tip rolled back to revision 2 (undo push)
1499 repository tip rolled back to revision 2 (undo push)
1457 2:fb35475503ef
1500 2:fb35475503ef
1458
1501
1459
1502
1460 Branch acl deny test
1503 Branch acl deny test
1461
1504
1462 $ echo "[acl.deny.branches]" >> $config
1505 $ echo "[acl.deny.branches]" >> $config
1463 $ echo "foobar = *" >> $config
1506 $ echo "foobar = *" >> $config
1464 $ do_push astro
1507 $ do_push astro
1465 Pushing as user astro
1508 Pushing as user astro
1466 hgrc = """
1509 hgrc = """
1467 [acl]
1510 [acl]
1468 sources = push
1511 sources = push
1469 [extensions]
1512 [extensions]
1470 [acl.deny.branches]
1513 [acl.deny.branches]
1471 foobar = *
1514 foobar = *
1472 """
1515 """
1473 pushing to ../b
1516 pushing to ../b
1474 query 1; heads
1517 query 1; heads
1475 searching for changes
1518 searching for changes
1476 all remote heads known locally
1519 all remote heads known locally
1477 invalidating branch cache (tip differs)
1520 invalidating branch cache (tip differs)
1521 listing keys for "bookmarks"
1478 4 changesets found
1522 4 changesets found
1479 list of changesets:
1523 list of changesets:
1480 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1524 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1481 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1525 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1482 911600dab2ae7a9baff75958b84fe606851ce955
1526 911600dab2ae7a9baff75958b84fe606851ce955
1483 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1527 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1484 adding changesets
1528 adding changesets
1485 bundling: 1/4 changesets (25.00%)
1529 bundling: 1/4 changesets (25.00%)
1486 bundling: 2/4 changesets (50.00%)
1530 bundling: 2/4 changesets (50.00%)
1487 bundling: 3/4 changesets (75.00%)
1531 bundling: 3/4 changesets (75.00%)
1488 bundling: 4/4 changesets (100.00%)
1532 bundling: 4/4 changesets (100.00%)
1489 bundling: 1/4 manifests (25.00%)
1533 bundling: 1/4 manifests (25.00%)
1490 bundling: 2/4 manifests (50.00%)
1534 bundling: 2/4 manifests (50.00%)
1491 bundling: 3/4 manifests (75.00%)
1535 bundling: 3/4 manifests (75.00%)
1492 bundling: 4/4 manifests (100.00%)
1536 bundling: 4/4 manifests (100.00%)
1493 bundling: abc.txt 1/4 files (25.00%)
1537 bundling: abc.txt 1/4 files (25.00%)
1494 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1538 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1495 bundling: foo/file.txt 3/4 files (75.00%)
1539 bundling: foo/file.txt 3/4 files (75.00%)
1496 bundling: quux/file.py 4/4 files (100.00%)
1540 bundling: quux/file.py 4/4 files (100.00%)
1497 changesets: 1 chunks
1541 changesets: 1 chunks
1498 add changeset ef1ea85a6374
1542 add changeset ef1ea85a6374
1499 changesets: 2 chunks
1543 changesets: 2 chunks
1500 add changeset f9cafe1212c8
1544 add changeset f9cafe1212c8
1501 changesets: 3 chunks
1545 changesets: 3 chunks
1502 add changeset 911600dab2ae
1546 add changeset 911600dab2ae
1503 changesets: 4 chunks
1547 changesets: 4 chunks
1504 add changeset e8fc755d4d82
1548 add changeset e8fc755d4d82
1505 adding manifests
1549 adding manifests
1506 manifests: 1/4 chunks (25.00%)
1550 manifests: 1/4 chunks (25.00%)
1507 manifests: 2/4 chunks (50.00%)
1551 manifests: 2/4 chunks (50.00%)
1508 manifests: 3/4 chunks (75.00%)
1552 manifests: 3/4 chunks (75.00%)
1509 manifests: 4/4 chunks (100.00%)
1553 manifests: 4/4 chunks (100.00%)
1510 adding file changes
1554 adding file changes
1511 adding abc.txt revisions
1555 adding abc.txt revisions
1512 files: 1/4 chunks (25.00%)
1556 files: 1/4 chunks (25.00%)
1513 adding foo/Bar/file.txt revisions
1557 adding foo/Bar/file.txt revisions
1514 files: 2/4 chunks (50.00%)
1558 files: 2/4 chunks (50.00%)
1515 adding foo/file.txt revisions
1559 adding foo/file.txt revisions
1516 files: 3/4 chunks (75.00%)
1560 files: 3/4 chunks (75.00%)
1517 adding quux/file.py revisions
1561 adding quux/file.py revisions
1518 files: 4/4 chunks (100.00%)
1562 files: 4/4 chunks (100.00%)
1519 added 4 changesets with 4 changes to 4 files (+1 heads)
1563 added 4 changesets with 4 changes to 4 files (+1 heads)
1520 calling hook pretxnchangegroup.acl: hgext.acl.hook
1564 calling hook pretxnchangegroup.acl: hgext.acl.hook
1521 acl: checking access for user "astro"
1565 acl: checking access for user "astro"
1522 acl: acl.allow.branches not enabled
1566 acl: acl.allow.branches not enabled
1523 acl: acl.deny.branches enabled, 1 entries for user astro
1567 acl: acl.deny.branches enabled, 1 entries for user astro
1524 acl: acl.allow not enabled
1568 acl: acl.allow not enabled
1525 acl: acl.deny not enabled
1569 acl: acl.deny not enabled
1526 acl: branch access granted: "ef1ea85a6374" on branch "default"
1570 acl: branch access granted: "ef1ea85a6374" on branch "default"
1527 acl: path access granted: "ef1ea85a6374"
1571 acl: path access granted: "ef1ea85a6374"
1528 acl: branch access granted: "f9cafe1212c8" on branch "default"
1572 acl: branch access granted: "f9cafe1212c8" on branch "default"
1529 acl: path access granted: "f9cafe1212c8"
1573 acl: path access granted: "f9cafe1212c8"
1530 acl: branch access granted: "911600dab2ae" on branch "default"
1574 acl: branch access granted: "911600dab2ae" on branch "default"
1531 acl: path access granted: "911600dab2ae"
1575 acl: path access granted: "911600dab2ae"
1532 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1576 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1533 transaction abort!
1577 transaction abort!
1534 rollback completed
1578 rollback completed
1535 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1579 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1536 no rollback information available
1580 no rollback information available
1537 2:fb35475503ef
1581 2:fb35475503ef
1538
1582
1539
1583
1540 Branch acl empty allow test
1584 Branch acl empty allow test
1541
1585
1542 $ init_config
1586 $ init_config
1543 $ echo "[acl.allow.branches]" >> $config
1587 $ echo "[acl.allow.branches]" >> $config
1544 $ do_push astro
1588 $ do_push astro
1545 Pushing as user astro
1589 Pushing as user astro
1546 hgrc = """
1590 hgrc = """
1547 [acl]
1591 [acl]
1548 sources = push
1592 sources = push
1549 [extensions]
1593 [extensions]
1550 [acl.allow.branches]
1594 [acl.allow.branches]
1551 """
1595 """
1552 pushing to ../b
1596 pushing to ../b
1553 query 1; heads
1597 query 1; heads
1554 searching for changes
1598 searching for changes
1555 all remote heads known locally
1599 all remote heads known locally
1600 listing keys for "bookmarks"
1556 4 changesets found
1601 4 changesets found
1557 list of changesets:
1602 list of changesets:
1558 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1603 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1559 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1604 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1560 911600dab2ae7a9baff75958b84fe606851ce955
1605 911600dab2ae7a9baff75958b84fe606851ce955
1561 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1606 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1562 adding changesets
1607 adding changesets
1563 bundling: 1/4 changesets (25.00%)
1608 bundling: 1/4 changesets (25.00%)
1564 bundling: 2/4 changesets (50.00%)
1609 bundling: 2/4 changesets (50.00%)
1565 bundling: 3/4 changesets (75.00%)
1610 bundling: 3/4 changesets (75.00%)
1566 bundling: 4/4 changesets (100.00%)
1611 bundling: 4/4 changesets (100.00%)
1567 bundling: 1/4 manifests (25.00%)
1612 bundling: 1/4 manifests (25.00%)
1568 bundling: 2/4 manifests (50.00%)
1613 bundling: 2/4 manifests (50.00%)
1569 bundling: 3/4 manifests (75.00%)
1614 bundling: 3/4 manifests (75.00%)
1570 bundling: 4/4 manifests (100.00%)
1615 bundling: 4/4 manifests (100.00%)
1571 bundling: abc.txt 1/4 files (25.00%)
1616 bundling: abc.txt 1/4 files (25.00%)
1572 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1617 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1573 bundling: foo/file.txt 3/4 files (75.00%)
1618 bundling: foo/file.txt 3/4 files (75.00%)
1574 bundling: quux/file.py 4/4 files (100.00%)
1619 bundling: quux/file.py 4/4 files (100.00%)
1575 changesets: 1 chunks
1620 changesets: 1 chunks
1576 add changeset ef1ea85a6374
1621 add changeset ef1ea85a6374
1577 changesets: 2 chunks
1622 changesets: 2 chunks
1578 add changeset f9cafe1212c8
1623 add changeset f9cafe1212c8
1579 changesets: 3 chunks
1624 changesets: 3 chunks
1580 add changeset 911600dab2ae
1625 add changeset 911600dab2ae
1581 changesets: 4 chunks
1626 changesets: 4 chunks
1582 add changeset e8fc755d4d82
1627 add changeset e8fc755d4d82
1583 adding manifests
1628 adding manifests
1584 manifests: 1/4 chunks (25.00%)
1629 manifests: 1/4 chunks (25.00%)
1585 manifests: 2/4 chunks (50.00%)
1630 manifests: 2/4 chunks (50.00%)
1586 manifests: 3/4 chunks (75.00%)
1631 manifests: 3/4 chunks (75.00%)
1587 manifests: 4/4 chunks (100.00%)
1632 manifests: 4/4 chunks (100.00%)
1588 adding file changes
1633 adding file changes
1589 adding abc.txt revisions
1634 adding abc.txt revisions
1590 files: 1/4 chunks (25.00%)
1635 files: 1/4 chunks (25.00%)
1591 adding foo/Bar/file.txt revisions
1636 adding foo/Bar/file.txt revisions
1592 files: 2/4 chunks (50.00%)
1637 files: 2/4 chunks (50.00%)
1593 adding foo/file.txt revisions
1638 adding foo/file.txt revisions
1594 files: 3/4 chunks (75.00%)
1639 files: 3/4 chunks (75.00%)
1595 adding quux/file.py revisions
1640 adding quux/file.py revisions
1596 files: 4/4 chunks (100.00%)
1641 files: 4/4 chunks (100.00%)
1597 added 4 changesets with 4 changes to 4 files (+1 heads)
1642 added 4 changesets with 4 changes to 4 files (+1 heads)
1598 calling hook pretxnchangegroup.acl: hgext.acl.hook
1643 calling hook pretxnchangegroup.acl: hgext.acl.hook
1599 acl: checking access for user "astro"
1644 acl: checking access for user "astro"
1600 acl: acl.allow.branches enabled, 0 entries for user astro
1645 acl: acl.allow.branches enabled, 0 entries for user astro
1601 acl: acl.deny.branches not enabled
1646 acl: acl.deny.branches not enabled
1602 acl: acl.allow not enabled
1647 acl: acl.allow not enabled
1603 acl: acl.deny not enabled
1648 acl: acl.deny not enabled
1604 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1649 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1605 transaction abort!
1650 transaction abort!
1606 rollback completed
1651 rollback completed
1607 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1652 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1608 no rollback information available
1653 no rollback information available
1609 2:fb35475503ef
1654 2:fb35475503ef
1610
1655
1611
1656
1612 Branch acl allow other
1657 Branch acl allow other
1613
1658
1614 $ init_config
1659 $ init_config
1615 $ echo "[acl.allow.branches]" >> $config
1660 $ echo "[acl.allow.branches]" >> $config
1616 $ echo "* = george" >> $config
1661 $ echo "* = george" >> $config
1617 $ do_push astro
1662 $ do_push astro
1618 Pushing as user astro
1663 Pushing as user astro
1619 hgrc = """
1664 hgrc = """
1620 [acl]
1665 [acl]
1621 sources = push
1666 sources = push
1622 [extensions]
1667 [extensions]
1623 [acl.allow.branches]
1668 [acl.allow.branches]
1624 * = george
1669 * = george
1625 """
1670 """
1626 pushing to ../b
1671 pushing to ../b
1627 query 1; heads
1672 query 1; heads
1628 searching for changes
1673 searching for changes
1629 all remote heads known locally
1674 all remote heads known locally
1675 listing keys for "bookmarks"
1630 4 changesets found
1676 4 changesets found
1631 list of changesets:
1677 list of changesets:
1632 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1678 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1633 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1679 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1634 911600dab2ae7a9baff75958b84fe606851ce955
1680 911600dab2ae7a9baff75958b84fe606851ce955
1635 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1681 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1636 adding changesets
1682 adding changesets
1637 bundling: 1/4 changesets (25.00%)
1683 bundling: 1/4 changesets (25.00%)
1638 bundling: 2/4 changesets (50.00%)
1684 bundling: 2/4 changesets (50.00%)
1639 bundling: 3/4 changesets (75.00%)
1685 bundling: 3/4 changesets (75.00%)
1640 bundling: 4/4 changesets (100.00%)
1686 bundling: 4/4 changesets (100.00%)
1641 bundling: 1/4 manifests (25.00%)
1687 bundling: 1/4 manifests (25.00%)
1642 bundling: 2/4 manifests (50.00%)
1688 bundling: 2/4 manifests (50.00%)
1643 bundling: 3/4 manifests (75.00%)
1689 bundling: 3/4 manifests (75.00%)
1644 bundling: 4/4 manifests (100.00%)
1690 bundling: 4/4 manifests (100.00%)
1645 bundling: abc.txt 1/4 files (25.00%)
1691 bundling: abc.txt 1/4 files (25.00%)
1646 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1692 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1647 bundling: foo/file.txt 3/4 files (75.00%)
1693 bundling: foo/file.txt 3/4 files (75.00%)
1648 bundling: quux/file.py 4/4 files (100.00%)
1694 bundling: quux/file.py 4/4 files (100.00%)
1649 changesets: 1 chunks
1695 changesets: 1 chunks
1650 add changeset ef1ea85a6374
1696 add changeset ef1ea85a6374
1651 changesets: 2 chunks
1697 changesets: 2 chunks
1652 add changeset f9cafe1212c8
1698 add changeset f9cafe1212c8
1653 changesets: 3 chunks
1699 changesets: 3 chunks
1654 add changeset 911600dab2ae
1700 add changeset 911600dab2ae
1655 changesets: 4 chunks
1701 changesets: 4 chunks
1656 add changeset e8fc755d4d82
1702 add changeset e8fc755d4d82
1657 adding manifests
1703 adding manifests
1658 manifests: 1/4 chunks (25.00%)
1704 manifests: 1/4 chunks (25.00%)
1659 manifests: 2/4 chunks (50.00%)
1705 manifests: 2/4 chunks (50.00%)
1660 manifests: 3/4 chunks (75.00%)
1706 manifests: 3/4 chunks (75.00%)
1661 manifests: 4/4 chunks (100.00%)
1707 manifests: 4/4 chunks (100.00%)
1662 adding file changes
1708 adding file changes
1663 adding abc.txt revisions
1709 adding abc.txt revisions
1664 files: 1/4 chunks (25.00%)
1710 files: 1/4 chunks (25.00%)
1665 adding foo/Bar/file.txt revisions
1711 adding foo/Bar/file.txt revisions
1666 files: 2/4 chunks (50.00%)
1712 files: 2/4 chunks (50.00%)
1667 adding foo/file.txt revisions
1713 adding foo/file.txt revisions
1668 files: 3/4 chunks (75.00%)
1714 files: 3/4 chunks (75.00%)
1669 adding quux/file.py revisions
1715 adding quux/file.py revisions
1670 files: 4/4 chunks (100.00%)
1716 files: 4/4 chunks (100.00%)
1671 added 4 changesets with 4 changes to 4 files (+1 heads)
1717 added 4 changesets with 4 changes to 4 files (+1 heads)
1672 calling hook pretxnchangegroup.acl: hgext.acl.hook
1718 calling hook pretxnchangegroup.acl: hgext.acl.hook
1673 acl: checking access for user "astro"
1719 acl: checking access for user "astro"
1674 acl: acl.allow.branches enabled, 0 entries for user astro
1720 acl: acl.allow.branches enabled, 0 entries for user astro
1675 acl: acl.deny.branches not enabled
1721 acl: acl.deny.branches not enabled
1676 acl: acl.allow not enabled
1722 acl: acl.allow not enabled
1677 acl: acl.deny not enabled
1723 acl: acl.deny not enabled
1678 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1724 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1679 transaction abort!
1725 transaction abort!
1680 rollback completed
1726 rollback completed
1681 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1727 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1682 no rollback information available
1728 no rollback information available
1683 2:fb35475503ef
1729 2:fb35475503ef
1684
1730
1685 $ do_push george
1731 $ do_push george
1686 Pushing as user george
1732 Pushing as user george
1687 hgrc = """
1733 hgrc = """
1688 [acl]
1734 [acl]
1689 sources = push
1735 sources = push
1690 [extensions]
1736 [extensions]
1691 [acl.allow.branches]
1737 [acl.allow.branches]
1692 * = george
1738 * = george
1693 """
1739 """
1694 pushing to ../b
1740 pushing to ../b
1695 query 1; heads
1741 query 1; heads
1696 searching for changes
1742 searching for changes
1697 all remote heads known locally
1743 all remote heads known locally
1744 listing keys for "bookmarks"
1698 4 changesets found
1745 4 changesets found
1699 list of changesets:
1746 list of changesets:
1700 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1747 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1701 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1748 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1702 911600dab2ae7a9baff75958b84fe606851ce955
1749 911600dab2ae7a9baff75958b84fe606851ce955
1703 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1750 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1704 adding changesets
1751 adding changesets
1705 bundling: 1/4 changesets (25.00%)
1752 bundling: 1/4 changesets (25.00%)
1706 bundling: 2/4 changesets (50.00%)
1753 bundling: 2/4 changesets (50.00%)
1707 bundling: 3/4 changesets (75.00%)
1754 bundling: 3/4 changesets (75.00%)
1708 bundling: 4/4 changesets (100.00%)
1755 bundling: 4/4 changesets (100.00%)
1709 bundling: 1/4 manifests (25.00%)
1756 bundling: 1/4 manifests (25.00%)
1710 bundling: 2/4 manifests (50.00%)
1757 bundling: 2/4 manifests (50.00%)
1711 bundling: 3/4 manifests (75.00%)
1758 bundling: 3/4 manifests (75.00%)
1712 bundling: 4/4 manifests (100.00%)
1759 bundling: 4/4 manifests (100.00%)
1713 bundling: abc.txt 1/4 files (25.00%)
1760 bundling: abc.txt 1/4 files (25.00%)
1714 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1761 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1715 bundling: foo/file.txt 3/4 files (75.00%)
1762 bundling: foo/file.txt 3/4 files (75.00%)
1716 bundling: quux/file.py 4/4 files (100.00%)
1763 bundling: quux/file.py 4/4 files (100.00%)
1717 changesets: 1 chunks
1764 changesets: 1 chunks
1718 add changeset ef1ea85a6374
1765 add changeset ef1ea85a6374
1719 changesets: 2 chunks
1766 changesets: 2 chunks
1720 add changeset f9cafe1212c8
1767 add changeset f9cafe1212c8
1721 changesets: 3 chunks
1768 changesets: 3 chunks
1722 add changeset 911600dab2ae
1769 add changeset 911600dab2ae
1723 changesets: 4 chunks
1770 changesets: 4 chunks
1724 add changeset e8fc755d4d82
1771 add changeset e8fc755d4d82
1725 adding manifests
1772 adding manifests
1726 manifests: 1/4 chunks (25.00%)
1773 manifests: 1/4 chunks (25.00%)
1727 manifests: 2/4 chunks (50.00%)
1774 manifests: 2/4 chunks (50.00%)
1728 manifests: 3/4 chunks (75.00%)
1775 manifests: 3/4 chunks (75.00%)
1729 manifests: 4/4 chunks (100.00%)
1776 manifests: 4/4 chunks (100.00%)
1730 adding file changes
1777 adding file changes
1731 adding abc.txt revisions
1778 adding abc.txt revisions
1732 files: 1/4 chunks (25.00%)
1779 files: 1/4 chunks (25.00%)
1733 adding foo/Bar/file.txt revisions
1780 adding foo/Bar/file.txt revisions
1734 files: 2/4 chunks (50.00%)
1781 files: 2/4 chunks (50.00%)
1735 adding foo/file.txt revisions
1782 adding foo/file.txt revisions
1736 files: 3/4 chunks (75.00%)
1783 files: 3/4 chunks (75.00%)
1737 adding quux/file.py revisions
1784 adding quux/file.py revisions
1738 files: 4/4 chunks (100.00%)
1785 files: 4/4 chunks (100.00%)
1739 added 4 changesets with 4 changes to 4 files (+1 heads)
1786 added 4 changesets with 4 changes to 4 files (+1 heads)
1740 calling hook pretxnchangegroup.acl: hgext.acl.hook
1787 calling hook pretxnchangegroup.acl: hgext.acl.hook
1741 acl: checking access for user "george"
1788 acl: checking access for user "george"
1742 acl: acl.allow.branches enabled, 1 entries for user george
1789 acl: acl.allow.branches enabled, 1 entries for user george
1743 acl: acl.deny.branches not enabled
1790 acl: acl.deny.branches not enabled
1744 acl: acl.allow not enabled
1791 acl: acl.allow not enabled
1745 acl: acl.deny not enabled
1792 acl: acl.deny not enabled
1746 acl: branch access granted: "ef1ea85a6374" on branch "default"
1793 acl: branch access granted: "ef1ea85a6374" on branch "default"
1747 acl: path access granted: "ef1ea85a6374"
1794 acl: path access granted: "ef1ea85a6374"
1748 acl: branch access granted: "f9cafe1212c8" on branch "default"
1795 acl: branch access granted: "f9cafe1212c8" on branch "default"
1749 acl: path access granted: "f9cafe1212c8"
1796 acl: path access granted: "f9cafe1212c8"
1750 acl: branch access granted: "911600dab2ae" on branch "default"
1797 acl: branch access granted: "911600dab2ae" on branch "default"
1751 acl: path access granted: "911600dab2ae"
1798 acl: path access granted: "911600dab2ae"
1752 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1799 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1753 acl: path access granted: "e8fc755d4d82"
1800 acl: path access granted: "e8fc755d4d82"
1801 listing keys for "phases"
1802 listing keys for "namespaces"
1754 updating the branch cache
1803 updating the branch cache
1755 checking for updated bookmarks
1804 checking for updated bookmarks
1805 listing keys for "bookmarks"
1756 repository tip rolled back to revision 2 (undo push)
1806 repository tip rolled back to revision 2 (undo push)
1757 2:fb35475503ef
1807 2:fb35475503ef
1758
1808
1759
1809
1760 Branch acl conflicting allow
1810 Branch acl conflicting allow
1761 asterisk ends up applying to all branches and allowing george to
1811 asterisk ends up applying to all branches and allowing george to
1762 push foobar into the remote
1812 push foobar into the remote
1763
1813
1764 $ init_config
1814 $ init_config
1765 $ echo "[acl.allow.branches]" >> $config
1815 $ echo "[acl.allow.branches]" >> $config
1766 $ echo "foobar = astro" >> $config
1816 $ echo "foobar = astro" >> $config
1767 $ echo "* = george" >> $config
1817 $ echo "* = george" >> $config
1768 $ do_push george
1818 $ do_push george
1769 Pushing as user george
1819 Pushing as user george
1770 hgrc = """
1820 hgrc = """
1771 [acl]
1821 [acl]
1772 sources = push
1822 sources = push
1773 [extensions]
1823 [extensions]
1774 [acl.allow.branches]
1824 [acl.allow.branches]
1775 foobar = astro
1825 foobar = astro
1776 * = george
1826 * = george
1777 """
1827 """
1778 pushing to ../b
1828 pushing to ../b
1779 query 1; heads
1829 query 1; heads
1780 searching for changes
1830 searching for changes
1781 all remote heads known locally
1831 all remote heads known locally
1782 invalidating branch cache (tip differs)
1832 invalidating branch cache (tip differs)
1833 listing keys for "bookmarks"
1783 4 changesets found
1834 4 changesets found
1784 list of changesets:
1835 list of changesets:
1785 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1836 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1786 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1837 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1787 911600dab2ae7a9baff75958b84fe606851ce955
1838 911600dab2ae7a9baff75958b84fe606851ce955
1788 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1839 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1789 adding changesets
1840 adding changesets
1790 bundling: 1/4 changesets (25.00%)
1841 bundling: 1/4 changesets (25.00%)
1791 bundling: 2/4 changesets (50.00%)
1842 bundling: 2/4 changesets (50.00%)
1792 bundling: 3/4 changesets (75.00%)
1843 bundling: 3/4 changesets (75.00%)
1793 bundling: 4/4 changesets (100.00%)
1844 bundling: 4/4 changesets (100.00%)
1794 bundling: 1/4 manifests (25.00%)
1845 bundling: 1/4 manifests (25.00%)
1795 bundling: 2/4 manifests (50.00%)
1846 bundling: 2/4 manifests (50.00%)
1796 bundling: 3/4 manifests (75.00%)
1847 bundling: 3/4 manifests (75.00%)
1797 bundling: 4/4 manifests (100.00%)
1848 bundling: 4/4 manifests (100.00%)
1798 bundling: abc.txt 1/4 files (25.00%)
1849 bundling: abc.txt 1/4 files (25.00%)
1799 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1850 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1800 bundling: foo/file.txt 3/4 files (75.00%)
1851 bundling: foo/file.txt 3/4 files (75.00%)
1801 bundling: quux/file.py 4/4 files (100.00%)
1852 bundling: quux/file.py 4/4 files (100.00%)
1802 changesets: 1 chunks
1853 changesets: 1 chunks
1803 add changeset ef1ea85a6374
1854 add changeset ef1ea85a6374
1804 changesets: 2 chunks
1855 changesets: 2 chunks
1805 add changeset f9cafe1212c8
1856 add changeset f9cafe1212c8
1806 changesets: 3 chunks
1857 changesets: 3 chunks
1807 add changeset 911600dab2ae
1858 add changeset 911600dab2ae
1808 changesets: 4 chunks
1859 changesets: 4 chunks
1809 add changeset e8fc755d4d82
1860 add changeset e8fc755d4d82
1810 adding manifests
1861 adding manifests
1811 manifests: 1/4 chunks (25.00%)
1862 manifests: 1/4 chunks (25.00%)
1812 manifests: 2/4 chunks (50.00%)
1863 manifests: 2/4 chunks (50.00%)
1813 manifests: 3/4 chunks (75.00%)
1864 manifests: 3/4 chunks (75.00%)
1814 manifests: 4/4 chunks (100.00%)
1865 manifests: 4/4 chunks (100.00%)
1815 adding file changes
1866 adding file changes
1816 adding abc.txt revisions
1867 adding abc.txt revisions
1817 files: 1/4 chunks (25.00%)
1868 files: 1/4 chunks (25.00%)
1818 adding foo/Bar/file.txt revisions
1869 adding foo/Bar/file.txt revisions
1819 files: 2/4 chunks (50.00%)
1870 files: 2/4 chunks (50.00%)
1820 adding foo/file.txt revisions
1871 adding foo/file.txt revisions
1821 files: 3/4 chunks (75.00%)
1872 files: 3/4 chunks (75.00%)
1822 adding quux/file.py revisions
1873 adding quux/file.py revisions
1823 files: 4/4 chunks (100.00%)
1874 files: 4/4 chunks (100.00%)
1824 added 4 changesets with 4 changes to 4 files (+1 heads)
1875 added 4 changesets with 4 changes to 4 files (+1 heads)
1825 calling hook pretxnchangegroup.acl: hgext.acl.hook
1876 calling hook pretxnchangegroup.acl: hgext.acl.hook
1826 acl: checking access for user "george"
1877 acl: checking access for user "george"
1827 acl: acl.allow.branches enabled, 1 entries for user george
1878 acl: acl.allow.branches enabled, 1 entries for user george
1828 acl: acl.deny.branches not enabled
1879 acl: acl.deny.branches not enabled
1829 acl: acl.allow not enabled
1880 acl: acl.allow not enabled
1830 acl: acl.deny not enabled
1881 acl: acl.deny not enabled
1831 acl: branch access granted: "ef1ea85a6374" on branch "default"
1882 acl: branch access granted: "ef1ea85a6374" on branch "default"
1832 acl: path access granted: "ef1ea85a6374"
1883 acl: path access granted: "ef1ea85a6374"
1833 acl: branch access granted: "f9cafe1212c8" on branch "default"
1884 acl: branch access granted: "f9cafe1212c8" on branch "default"
1834 acl: path access granted: "f9cafe1212c8"
1885 acl: path access granted: "f9cafe1212c8"
1835 acl: branch access granted: "911600dab2ae" on branch "default"
1886 acl: branch access granted: "911600dab2ae" on branch "default"
1836 acl: path access granted: "911600dab2ae"
1887 acl: path access granted: "911600dab2ae"
1837 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1888 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1838 acl: path access granted: "e8fc755d4d82"
1889 acl: path access granted: "e8fc755d4d82"
1890 listing keys for "phases"
1891 listing keys for "namespaces"
1839 updating the branch cache
1892 updating the branch cache
1840 checking for updated bookmarks
1893 checking for updated bookmarks
1894 listing keys for "bookmarks"
1841 repository tip rolled back to revision 2 (undo push)
1895 repository tip rolled back to revision 2 (undo push)
1842 2:fb35475503ef
1896 2:fb35475503ef
1843
1897
1844 Branch acl conflicting deny
1898 Branch acl conflicting deny
1845
1899
1846 $ init_config
1900 $ init_config
1847 $ echo "[acl.deny.branches]" >> $config
1901 $ echo "[acl.deny.branches]" >> $config
1848 $ echo "foobar = astro" >> $config
1902 $ echo "foobar = astro" >> $config
1849 $ echo "default = astro" >> $config
1903 $ echo "default = astro" >> $config
1850 $ echo "* = george" >> $config
1904 $ echo "* = george" >> $config
1851 $ do_push george
1905 $ do_push george
1852 Pushing as user george
1906 Pushing as user george
1853 hgrc = """
1907 hgrc = """
1854 [acl]
1908 [acl]
1855 sources = push
1909 sources = push
1856 [extensions]
1910 [extensions]
1857 [acl.deny.branches]
1911 [acl.deny.branches]
1858 foobar = astro
1912 foobar = astro
1859 default = astro
1913 default = astro
1860 * = george
1914 * = george
1861 """
1915 """
1862 pushing to ../b
1916 pushing to ../b
1863 query 1; heads
1917 query 1; heads
1864 searching for changes
1918 searching for changes
1865 all remote heads known locally
1919 all remote heads known locally
1866 invalidating branch cache (tip differs)
1920 invalidating branch cache (tip differs)
1921 listing keys for "bookmarks"
1867 4 changesets found
1922 4 changesets found
1868 list of changesets:
1923 list of changesets:
1869 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1924 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1870 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1925 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1871 911600dab2ae7a9baff75958b84fe606851ce955
1926 911600dab2ae7a9baff75958b84fe606851ce955
1872 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1927 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1873 adding changesets
1928 adding changesets
1874 bundling: 1/4 changesets (25.00%)
1929 bundling: 1/4 changesets (25.00%)
1875 bundling: 2/4 changesets (50.00%)
1930 bundling: 2/4 changesets (50.00%)
1876 bundling: 3/4 changesets (75.00%)
1931 bundling: 3/4 changesets (75.00%)
1877 bundling: 4/4 changesets (100.00%)
1932 bundling: 4/4 changesets (100.00%)
1878 bundling: 1/4 manifests (25.00%)
1933 bundling: 1/4 manifests (25.00%)
1879 bundling: 2/4 manifests (50.00%)
1934 bundling: 2/4 manifests (50.00%)
1880 bundling: 3/4 manifests (75.00%)
1935 bundling: 3/4 manifests (75.00%)
1881 bundling: 4/4 manifests (100.00%)
1936 bundling: 4/4 manifests (100.00%)
1882 bundling: abc.txt 1/4 files (25.00%)
1937 bundling: abc.txt 1/4 files (25.00%)
1883 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1938 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1884 bundling: foo/file.txt 3/4 files (75.00%)
1939 bundling: foo/file.txt 3/4 files (75.00%)
1885 bundling: quux/file.py 4/4 files (100.00%)
1940 bundling: quux/file.py 4/4 files (100.00%)
1886 changesets: 1 chunks
1941 changesets: 1 chunks
1887 add changeset ef1ea85a6374
1942 add changeset ef1ea85a6374
1888 changesets: 2 chunks
1943 changesets: 2 chunks
1889 add changeset f9cafe1212c8
1944 add changeset f9cafe1212c8
1890 changesets: 3 chunks
1945 changesets: 3 chunks
1891 add changeset 911600dab2ae
1946 add changeset 911600dab2ae
1892 changesets: 4 chunks
1947 changesets: 4 chunks
1893 add changeset e8fc755d4d82
1948 add changeset e8fc755d4d82
1894 adding manifests
1949 adding manifests
1895 manifests: 1/4 chunks (25.00%)
1950 manifests: 1/4 chunks (25.00%)
1896 manifests: 2/4 chunks (50.00%)
1951 manifests: 2/4 chunks (50.00%)
1897 manifests: 3/4 chunks (75.00%)
1952 manifests: 3/4 chunks (75.00%)
1898 manifests: 4/4 chunks (100.00%)
1953 manifests: 4/4 chunks (100.00%)
1899 adding file changes
1954 adding file changes
1900 adding abc.txt revisions
1955 adding abc.txt revisions
1901 files: 1/4 chunks (25.00%)
1956 files: 1/4 chunks (25.00%)
1902 adding foo/Bar/file.txt revisions
1957 adding foo/Bar/file.txt revisions
1903 files: 2/4 chunks (50.00%)
1958 files: 2/4 chunks (50.00%)
1904 adding foo/file.txt revisions
1959 adding foo/file.txt revisions
1905 files: 3/4 chunks (75.00%)
1960 files: 3/4 chunks (75.00%)
1906 adding quux/file.py revisions
1961 adding quux/file.py revisions
1907 files: 4/4 chunks (100.00%)
1962 files: 4/4 chunks (100.00%)
1908 added 4 changesets with 4 changes to 4 files (+1 heads)
1963 added 4 changesets with 4 changes to 4 files (+1 heads)
1909 calling hook pretxnchangegroup.acl: hgext.acl.hook
1964 calling hook pretxnchangegroup.acl: hgext.acl.hook
1910 acl: checking access for user "george"
1965 acl: checking access for user "george"
1911 acl: acl.allow.branches not enabled
1966 acl: acl.allow.branches not enabled
1912 acl: acl.deny.branches enabled, 1 entries for user george
1967 acl: acl.deny.branches enabled, 1 entries for user george
1913 acl: acl.allow not enabled
1968 acl: acl.allow not enabled
1914 acl: acl.deny not enabled
1969 acl: acl.deny not enabled
1915 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1970 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1916 transaction abort!
1971 transaction abort!
1917 rollback completed
1972 rollback completed
1918 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1973 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1919 no rollback information available
1974 no rollback information available
1920 2:fb35475503ef
1975 2:fb35475503ef
1921
1976
1922 User 'astro' must not be denied
1977 User 'astro' must not be denied
1923
1978
1924 $ init_config
1979 $ init_config
1925 $ echo "[acl.deny.branches]" >> $config
1980 $ echo "[acl.deny.branches]" >> $config
1926 $ echo "default = !astro" >> $config
1981 $ echo "default = !astro" >> $config
1927 $ do_push astro
1982 $ do_push astro
1928 Pushing as user astro
1983 Pushing as user astro
1929 hgrc = """
1984 hgrc = """
1930 [acl]
1985 [acl]
1931 sources = push
1986 sources = push
1932 [extensions]
1987 [extensions]
1933 [acl.deny.branches]
1988 [acl.deny.branches]
1934 default = !astro
1989 default = !astro
1935 """
1990 """
1936 pushing to ../b
1991 pushing to ../b
1937 query 1; heads
1992 query 1; heads
1938 searching for changes
1993 searching for changes
1939 all remote heads known locally
1994 all remote heads known locally
1995 listing keys for "bookmarks"
1940 4 changesets found
1996 4 changesets found
1941 list of changesets:
1997 list of changesets:
1942 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1998 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1943 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1999 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1944 911600dab2ae7a9baff75958b84fe606851ce955
2000 911600dab2ae7a9baff75958b84fe606851ce955
1945 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2001 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1946 adding changesets
2002 adding changesets
1947 bundling: 1/4 changesets (25.00%)
2003 bundling: 1/4 changesets (25.00%)
1948 bundling: 2/4 changesets (50.00%)
2004 bundling: 2/4 changesets (50.00%)
1949 bundling: 3/4 changesets (75.00%)
2005 bundling: 3/4 changesets (75.00%)
1950 bundling: 4/4 changesets (100.00%)
2006 bundling: 4/4 changesets (100.00%)
1951 bundling: 1/4 manifests (25.00%)
2007 bundling: 1/4 manifests (25.00%)
1952 bundling: 2/4 manifests (50.00%)
2008 bundling: 2/4 manifests (50.00%)
1953 bundling: 3/4 manifests (75.00%)
2009 bundling: 3/4 manifests (75.00%)
1954 bundling: 4/4 manifests (100.00%)
2010 bundling: 4/4 manifests (100.00%)
1955 bundling: abc.txt 1/4 files (25.00%)
2011 bundling: abc.txt 1/4 files (25.00%)
1956 bundling: foo/Bar/file.txt 2/4 files (50.00%)
2012 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1957 bundling: foo/file.txt 3/4 files (75.00%)
2013 bundling: foo/file.txt 3/4 files (75.00%)
1958 bundling: quux/file.py 4/4 files (100.00%)
2014 bundling: quux/file.py 4/4 files (100.00%)
1959 changesets: 1 chunks
2015 changesets: 1 chunks
1960 add changeset ef1ea85a6374
2016 add changeset ef1ea85a6374
1961 changesets: 2 chunks
2017 changesets: 2 chunks
1962 add changeset f9cafe1212c8
2018 add changeset f9cafe1212c8
1963 changesets: 3 chunks
2019 changesets: 3 chunks
1964 add changeset 911600dab2ae
2020 add changeset 911600dab2ae
1965 changesets: 4 chunks
2021 changesets: 4 chunks
1966 add changeset e8fc755d4d82
2022 add changeset e8fc755d4d82
1967 adding manifests
2023 adding manifests
1968 manifests: 1/4 chunks (25.00%)
2024 manifests: 1/4 chunks (25.00%)
1969 manifests: 2/4 chunks (50.00%)
2025 manifests: 2/4 chunks (50.00%)
1970 manifests: 3/4 chunks (75.00%)
2026 manifests: 3/4 chunks (75.00%)
1971 manifests: 4/4 chunks (100.00%)
2027 manifests: 4/4 chunks (100.00%)
1972 adding file changes
2028 adding file changes
1973 adding abc.txt revisions
2029 adding abc.txt revisions
1974 files: 1/4 chunks (25.00%)
2030 files: 1/4 chunks (25.00%)
1975 adding foo/Bar/file.txt revisions
2031 adding foo/Bar/file.txt revisions
1976 files: 2/4 chunks (50.00%)
2032 files: 2/4 chunks (50.00%)
1977 adding foo/file.txt revisions
2033 adding foo/file.txt revisions
1978 files: 3/4 chunks (75.00%)
2034 files: 3/4 chunks (75.00%)
1979 adding quux/file.py revisions
2035 adding quux/file.py revisions
1980 files: 4/4 chunks (100.00%)
2036 files: 4/4 chunks (100.00%)
1981 added 4 changesets with 4 changes to 4 files (+1 heads)
2037 added 4 changesets with 4 changes to 4 files (+1 heads)
1982 calling hook pretxnchangegroup.acl: hgext.acl.hook
2038 calling hook pretxnchangegroup.acl: hgext.acl.hook
1983 acl: checking access for user "astro"
2039 acl: checking access for user "astro"
1984 acl: acl.allow.branches not enabled
2040 acl: acl.allow.branches not enabled
1985 acl: acl.deny.branches enabled, 0 entries for user astro
2041 acl: acl.deny.branches enabled, 0 entries for user astro
1986 acl: acl.allow not enabled
2042 acl: acl.allow not enabled
1987 acl: acl.deny not enabled
2043 acl: acl.deny not enabled
1988 acl: branch access granted: "ef1ea85a6374" on branch "default"
2044 acl: branch access granted: "ef1ea85a6374" on branch "default"
1989 acl: path access granted: "ef1ea85a6374"
2045 acl: path access granted: "ef1ea85a6374"
1990 acl: branch access granted: "f9cafe1212c8" on branch "default"
2046 acl: branch access granted: "f9cafe1212c8" on branch "default"
1991 acl: path access granted: "f9cafe1212c8"
2047 acl: path access granted: "f9cafe1212c8"
1992 acl: branch access granted: "911600dab2ae" on branch "default"
2048 acl: branch access granted: "911600dab2ae" on branch "default"
1993 acl: path access granted: "911600dab2ae"
2049 acl: path access granted: "911600dab2ae"
1994 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
2050 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1995 acl: path access granted: "e8fc755d4d82"
2051 acl: path access granted: "e8fc755d4d82"
2052 listing keys for "phases"
2053 listing keys for "namespaces"
1996 updating the branch cache
2054 updating the branch cache
1997 checking for updated bookmarks
2055 checking for updated bookmarks
2056 listing keys for "bookmarks"
1998 repository tip rolled back to revision 2 (undo push)
2057 repository tip rolled back to revision 2 (undo push)
1999 2:fb35475503ef
2058 2:fb35475503ef
2000
2059
2001
2060
2002 Non-astro users must be denied
2061 Non-astro users must be denied
2003
2062
2004 $ do_push george
2063 $ do_push george
2005 Pushing as user george
2064 Pushing as user george
2006 hgrc = """
2065 hgrc = """
2007 [acl]
2066 [acl]
2008 sources = push
2067 sources = push
2009 [extensions]
2068 [extensions]
2010 [acl.deny.branches]
2069 [acl.deny.branches]
2011 default = !astro
2070 default = !astro
2012 """
2071 """
2013 pushing to ../b
2072 pushing to ../b
2014 query 1; heads
2073 query 1; heads
2015 searching for changes
2074 searching for changes
2016 all remote heads known locally
2075 all remote heads known locally
2017 invalidating branch cache (tip differs)
2076 invalidating branch cache (tip differs)
2077 listing keys for "bookmarks"
2018 4 changesets found
2078 4 changesets found
2019 list of changesets:
2079 list of changesets:
2020 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2080 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2021 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2081 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2022 911600dab2ae7a9baff75958b84fe606851ce955
2082 911600dab2ae7a9baff75958b84fe606851ce955
2023 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2083 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2024 adding changesets
2084 adding changesets
2025 bundling: 1/4 changesets (25.00%)
2085 bundling: 1/4 changesets (25.00%)
2026 bundling: 2/4 changesets (50.00%)
2086 bundling: 2/4 changesets (50.00%)
2027 bundling: 3/4 changesets (75.00%)
2087 bundling: 3/4 changesets (75.00%)
2028 bundling: 4/4 changesets (100.00%)
2088 bundling: 4/4 changesets (100.00%)
2029 bundling: 1/4 manifests (25.00%)
2089 bundling: 1/4 manifests (25.00%)
2030 bundling: 2/4 manifests (50.00%)
2090 bundling: 2/4 manifests (50.00%)
2031 bundling: 3/4 manifests (75.00%)
2091 bundling: 3/4 manifests (75.00%)
2032 bundling: 4/4 manifests (100.00%)
2092 bundling: 4/4 manifests (100.00%)
2033 bundling: abc.txt 1/4 files (25.00%)
2093 bundling: abc.txt 1/4 files (25.00%)
2034 bundling: foo/Bar/file.txt 2/4 files (50.00%)
2094 bundling: foo/Bar/file.txt 2/4 files (50.00%)
2035 bundling: foo/file.txt 3/4 files (75.00%)
2095 bundling: foo/file.txt 3/4 files (75.00%)
2036 bundling: quux/file.py 4/4 files (100.00%)
2096 bundling: quux/file.py 4/4 files (100.00%)
2037 changesets: 1 chunks
2097 changesets: 1 chunks
2038 add changeset ef1ea85a6374
2098 add changeset ef1ea85a6374
2039 changesets: 2 chunks
2099 changesets: 2 chunks
2040 add changeset f9cafe1212c8
2100 add changeset f9cafe1212c8
2041 changesets: 3 chunks
2101 changesets: 3 chunks
2042 add changeset 911600dab2ae
2102 add changeset 911600dab2ae
2043 changesets: 4 chunks
2103 changesets: 4 chunks
2044 add changeset e8fc755d4d82
2104 add changeset e8fc755d4d82
2045 adding manifests
2105 adding manifests
2046 manifests: 1/4 chunks (25.00%)
2106 manifests: 1/4 chunks (25.00%)
2047 manifests: 2/4 chunks (50.00%)
2107 manifests: 2/4 chunks (50.00%)
2048 manifests: 3/4 chunks (75.00%)
2108 manifests: 3/4 chunks (75.00%)
2049 manifests: 4/4 chunks (100.00%)
2109 manifests: 4/4 chunks (100.00%)
2050 adding file changes
2110 adding file changes
2051 adding abc.txt revisions
2111 adding abc.txt revisions
2052 files: 1/4 chunks (25.00%)
2112 files: 1/4 chunks (25.00%)
2053 adding foo/Bar/file.txt revisions
2113 adding foo/Bar/file.txt revisions
2054 files: 2/4 chunks (50.00%)
2114 files: 2/4 chunks (50.00%)
2055 adding foo/file.txt revisions
2115 adding foo/file.txt revisions
2056 files: 3/4 chunks (75.00%)
2116 files: 3/4 chunks (75.00%)
2057 adding quux/file.py revisions
2117 adding quux/file.py revisions
2058 files: 4/4 chunks (100.00%)
2118 files: 4/4 chunks (100.00%)
2059 added 4 changesets with 4 changes to 4 files (+1 heads)
2119 added 4 changesets with 4 changes to 4 files (+1 heads)
2060 calling hook pretxnchangegroup.acl: hgext.acl.hook
2120 calling hook pretxnchangegroup.acl: hgext.acl.hook
2061 acl: checking access for user "george"
2121 acl: checking access for user "george"
2062 acl: acl.allow.branches not enabled
2122 acl: acl.allow.branches not enabled
2063 acl: acl.deny.branches enabled, 1 entries for user george
2123 acl: acl.deny.branches enabled, 1 entries for user george
2064 acl: acl.allow not enabled
2124 acl: acl.allow not enabled
2065 acl: acl.deny not enabled
2125 acl: acl.deny not enabled
2066 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2126 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2067 transaction abort!
2127 transaction abort!
2068 rollback completed
2128 rollback completed
2069 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2129 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2070 no rollback information available
2130 no rollback information available
2071 2:fb35475503ef
2131 2:fb35475503ef
2072
2132
2073
2133
@@ -1,565 +1,566 b''
1 Prepare repo a:
1 Prepare repo a:
2
2
3 $ hg init a
3 $ hg init a
4 $ cd a
4 $ cd a
5 $ echo a > a
5 $ echo a > a
6 $ hg add a
6 $ hg add a
7 $ hg commit -m test
7 $ hg commit -m test
8 $ echo first line > b
8 $ echo first line > b
9 $ hg add b
9 $ hg add b
10
10
11 Create a non-inlined filelog:
11 Create a non-inlined filelog:
12
12
13 $ python -c 'file("data1", "wb").write("".join("%s\n" % x for x in range(10000)))'
13 $ python -c 'file("data1", "wb").write("".join("%s\n" % x for x in range(10000)))'
14 $ for j in 0 1 2 3 4 5 6 7 8 9; do
14 $ for j in 0 1 2 3 4 5 6 7 8 9; do
15 > cat data1 >> b
15 > cat data1 >> b
16 > hg commit -m test
16 > hg commit -m test
17 > done
17 > done
18
18
19 List files in store/data (should show a 'b.d'):
19 List files in store/data (should show a 'b.d'):
20
20
21 $ for i in .hg/store/data/*; do
21 $ for i in .hg/store/data/*; do
22 > echo $i
22 > echo $i
23 > done
23 > done
24 .hg/store/data/a.i
24 .hg/store/data/a.i
25 .hg/store/data/b.d
25 .hg/store/data/b.d
26 .hg/store/data/b.i
26 .hg/store/data/b.i
27
27
28 Default operation:
28 Default operation:
29
29
30 $ hg clone . ../b
30 $ hg clone . ../b
31 updating to branch default
31 updating to branch default
32 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
32 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 $ cd ../b
33 $ cd ../b
34 $ cat a
34 $ cat a
35 a
35 a
36 $ hg verify
36 $ hg verify
37 checking changesets
37 checking changesets
38 checking manifests
38 checking manifests
39 crosschecking files in changesets and manifests
39 crosschecking files in changesets and manifests
40 checking files
40 checking files
41 2 files, 11 changesets, 11 total revisions
41 2 files, 11 changesets, 11 total revisions
42
42
43 Invalid dest '' must abort:
43 Invalid dest '' must abort:
44
44
45 $ hg clone . ''
45 $ hg clone . ''
46 abort: empty destination path is not valid
46 abort: empty destination path is not valid
47 [255]
47 [255]
48
48
49 No update, with debug option:
49 No update, with debug option:
50
50
51 #if hardlink
51 #if hardlink
52 $ hg --debug clone -U . ../c
52 $ hg --debug clone -U . ../c
53 linked 8 files
53 linked 8 files
54 listing keys for "bookmarks"
54 #else
55 #else
55 $ hg --debug clone -U . ../c
56 $ hg --debug clone -U . ../c
56 copied 8 files
57 copied 8 files
57 #endif
58 #endif
58 $ cd ../c
59 $ cd ../c
59 $ cat a 2>/dev/null || echo "a not present"
60 $ cat a 2>/dev/null || echo "a not present"
60 a not present
61 a not present
61 $ hg verify
62 $ hg verify
62 checking changesets
63 checking changesets
63 checking manifests
64 checking manifests
64 crosschecking files in changesets and manifests
65 crosschecking files in changesets and manifests
65 checking files
66 checking files
66 2 files, 11 changesets, 11 total revisions
67 2 files, 11 changesets, 11 total revisions
67
68
68 Default destination:
69 Default destination:
69
70
70 $ mkdir ../d
71 $ mkdir ../d
71 $ cd ../d
72 $ cd ../d
72 $ hg clone ../a
73 $ hg clone ../a
73 destination directory: a
74 destination directory: a
74 updating to branch default
75 updating to branch default
75 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 $ cd a
77 $ cd a
77 $ hg cat a
78 $ hg cat a
78 a
79 a
79 $ cd ../..
80 $ cd ../..
80
81
81 Check that we drop the 'file:' from the path before writing the .hgrc:
82 Check that we drop the 'file:' from the path before writing the .hgrc:
82
83
83 $ hg clone file:a e
84 $ hg clone file:a e
84 updating to branch default
85 updating to branch default
85 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
86 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
86 $ grep 'file:' e/.hg/hgrc
87 $ grep 'file:' e/.hg/hgrc
87 [1]
88 [1]
88
89
89 Check that path aliases are expanded:
90 Check that path aliases are expanded:
90
91
91 $ hg clone -q -U --config 'paths.foobar=a#0' foobar f
92 $ hg clone -q -U --config 'paths.foobar=a#0' foobar f
92 $ hg -R f showconfig paths.default
93 $ hg -R f showconfig paths.default
93 $TESTTMP/a#0 (glob)
94 $TESTTMP/a#0 (glob)
94
95
95 Use --pull:
96 Use --pull:
96
97
97 $ hg clone --pull a g
98 $ hg clone --pull a g
98 requesting all changes
99 requesting all changes
99 adding changesets
100 adding changesets
100 adding manifests
101 adding manifests
101 adding file changes
102 adding file changes
102 added 11 changesets with 11 changes to 2 files
103 added 11 changesets with 11 changes to 2 files
103 updating to branch default
104 updating to branch default
104 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
105 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
105 $ hg -R g verify
106 $ hg -R g verify
106 checking changesets
107 checking changesets
107 checking manifests
108 checking manifests
108 crosschecking files in changesets and manifests
109 crosschecking files in changesets and manifests
109 checking files
110 checking files
110 2 files, 11 changesets, 11 total revisions
111 2 files, 11 changesets, 11 total revisions
111
112
112 Invalid dest '' with --pull must abort (issue2528):
113 Invalid dest '' with --pull must abort (issue2528):
113
114
114 $ hg clone --pull a ''
115 $ hg clone --pull a ''
115 abort: empty destination path is not valid
116 abort: empty destination path is not valid
116 [255]
117 [255]
117
118
118 Clone to '.':
119 Clone to '.':
119
120
120 $ mkdir h
121 $ mkdir h
121 $ cd h
122 $ cd h
122 $ hg clone ../a .
123 $ hg clone ../a .
123 updating to branch default
124 updating to branch default
124 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
125 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
125 $ cd ..
126 $ cd ..
126
127
127
128
128 *** Tests for option -u ***
129 *** Tests for option -u ***
129
130
130 Adding some more history to repo a:
131 Adding some more history to repo a:
131
132
132 $ cd a
133 $ cd a
133 $ hg tag ref1
134 $ hg tag ref1
134 $ echo the quick brown fox >a
135 $ echo the quick brown fox >a
135 $ hg ci -m "hacked default"
136 $ hg ci -m "hacked default"
136 $ hg up ref1
137 $ hg up ref1
137 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
138 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
138 $ hg branch stable
139 $ hg branch stable
139 marked working directory as branch stable
140 marked working directory as branch stable
140 (branches are permanent and global, did you want a bookmark?)
141 (branches are permanent and global, did you want a bookmark?)
141 $ echo some text >a
142 $ echo some text >a
142 $ hg ci -m "starting branch stable"
143 $ hg ci -m "starting branch stable"
143 $ hg tag ref2
144 $ hg tag ref2
144 $ echo some more text >a
145 $ echo some more text >a
145 $ hg ci -m "another change for branch stable"
146 $ hg ci -m "another change for branch stable"
146 $ hg up ref2
147 $ hg up ref2
147 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
148 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
148 $ hg parents
149 $ hg parents
149 changeset: 13:e8ece76546a6
150 changeset: 13:e8ece76546a6
150 branch: stable
151 branch: stable
151 tag: ref2
152 tag: ref2
152 parent: 10:a7949464abda
153 parent: 10:a7949464abda
153 user: test
154 user: test
154 date: Thu Jan 01 00:00:00 1970 +0000
155 date: Thu Jan 01 00:00:00 1970 +0000
155 summary: starting branch stable
156 summary: starting branch stable
156
157
157
158
158 Repo a has two heads:
159 Repo a has two heads:
159
160
160 $ hg heads
161 $ hg heads
161 changeset: 15:0aae7cf88f0d
162 changeset: 15:0aae7cf88f0d
162 branch: stable
163 branch: stable
163 tag: tip
164 tag: tip
164 user: test
165 user: test
165 date: Thu Jan 01 00:00:00 1970 +0000
166 date: Thu Jan 01 00:00:00 1970 +0000
166 summary: another change for branch stable
167 summary: another change for branch stable
167
168
168 changeset: 12:f21241060d6a
169 changeset: 12:f21241060d6a
169 user: test
170 user: test
170 date: Thu Jan 01 00:00:00 1970 +0000
171 date: Thu Jan 01 00:00:00 1970 +0000
171 summary: hacked default
172 summary: hacked default
172
173
173
174
174 $ cd ..
175 $ cd ..
175
176
176
177
177 Testing --noupdate with --updaterev (must abort):
178 Testing --noupdate with --updaterev (must abort):
178
179
179 $ hg clone --noupdate --updaterev 1 a ua
180 $ hg clone --noupdate --updaterev 1 a ua
180 abort: cannot specify both --noupdate and --updaterev
181 abort: cannot specify both --noupdate and --updaterev
181 [255]
182 [255]
182
183
183
184
184 Testing clone -u:
185 Testing clone -u:
185
186
186 $ hg clone -u . a ua
187 $ hg clone -u . a ua
187 updating to branch stable
188 updating to branch stable
188 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
189 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
189
190
190 Repo ua has both heads:
191 Repo ua has both heads:
191
192
192 $ hg -R ua heads
193 $ hg -R ua heads
193 changeset: 15:0aae7cf88f0d
194 changeset: 15:0aae7cf88f0d
194 branch: stable
195 branch: stable
195 tag: tip
196 tag: tip
196 user: test
197 user: test
197 date: Thu Jan 01 00:00:00 1970 +0000
198 date: Thu Jan 01 00:00:00 1970 +0000
198 summary: another change for branch stable
199 summary: another change for branch stable
199
200
200 changeset: 12:f21241060d6a
201 changeset: 12:f21241060d6a
201 user: test
202 user: test
202 date: Thu Jan 01 00:00:00 1970 +0000
203 date: Thu Jan 01 00:00:00 1970 +0000
203 summary: hacked default
204 summary: hacked default
204
205
205
206
206 Same revision checked out in repo a and ua:
207 Same revision checked out in repo a and ua:
207
208
208 $ hg -R a parents --template "{node|short}\n"
209 $ hg -R a parents --template "{node|short}\n"
209 e8ece76546a6
210 e8ece76546a6
210 $ hg -R ua parents --template "{node|short}\n"
211 $ hg -R ua parents --template "{node|short}\n"
211 e8ece76546a6
212 e8ece76546a6
212
213
213 $ rm -r ua
214 $ rm -r ua
214
215
215
216
216 Testing clone --pull -u:
217 Testing clone --pull -u:
217
218
218 $ hg clone --pull -u . a ua
219 $ hg clone --pull -u . a ua
219 requesting all changes
220 requesting all changes
220 adding changesets
221 adding changesets
221 adding manifests
222 adding manifests
222 adding file changes
223 adding file changes
223 added 16 changesets with 16 changes to 3 files (+1 heads)
224 added 16 changesets with 16 changes to 3 files (+1 heads)
224 updating to branch stable
225 updating to branch stable
225 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
226 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
226
227
227 Repo ua has both heads:
228 Repo ua has both heads:
228
229
229 $ hg -R ua heads
230 $ hg -R ua heads
230 changeset: 15:0aae7cf88f0d
231 changeset: 15:0aae7cf88f0d
231 branch: stable
232 branch: stable
232 tag: tip
233 tag: tip
233 user: test
234 user: test
234 date: Thu Jan 01 00:00:00 1970 +0000
235 date: Thu Jan 01 00:00:00 1970 +0000
235 summary: another change for branch stable
236 summary: another change for branch stable
236
237
237 changeset: 12:f21241060d6a
238 changeset: 12:f21241060d6a
238 user: test
239 user: test
239 date: Thu Jan 01 00:00:00 1970 +0000
240 date: Thu Jan 01 00:00:00 1970 +0000
240 summary: hacked default
241 summary: hacked default
241
242
242
243
243 Same revision checked out in repo a and ua:
244 Same revision checked out in repo a and ua:
244
245
245 $ hg -R a parents --template "{node|short}\n"
246 $ hg -R a parents --template "{node|short}\n"
246 e8ece76546a6
247 e8ece76546a6
247 $ hg -R ua parents --template "{node|short}\n"
248 $ hg -R ua parents --template "{node|short}\n"
248 e8ece76546a6
249 e8ece76546a6
249
250
250 $ rm -r ua
251 $ rm -r ua
251
252
252
253
253 Testing clone -u <branch>:
254 Testing clone -u <branch>:
254
255
255 $ hg clone -u stable a ua
256 $ hg clone -u stable a ua
256 updating to branch stable
257 updating to branch stable
257 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
258 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
258
259
259 Repo ua has both heads:
260 Repo ua has both heads:
260
261
261 $ hg -R ua heads
262 $ hg -R ua heads
262 changeset: 15:0aae7cf88f0d
263 changeset: 15:0aae7cf88f0d
263 branch: stable
264 branch: stable
264 tag: tip
265 tag: tip
265 user: test
266 user: test
266 date: Thu Jan 01 00:00:00 1970 +0000
267 date: Thu Jan 01 00:00:00 1970 +0000
267 summary: another change for branch stable
268 summary: another change for branch stable
268
269
269 changeset: 12:f21241060d6a
270 changeset: 12:f21241060d6a
270 user: test
271 user: test
271 date: Thu Jan 01 00:00:00 1970 +0000
272 date: Thu Jan 01 00:00:00 1970 +0000
272 summary: hacked default
273 summary: hacked default
273
274
274
275
275 Branch 'stable' is checked out:
276 Branch 'stable' is checked out:
276
277
277 $ hg -R ua parents
278 $ hg -R ua parents
278 changeset: 15:0aae7cf88f0d
279 changeset: 15:0aae7cf88f0d
279 branch: stable
280 branch: stable
280 tag: tip
281 tag: tip
281 user: test
282 user: test
282 date: Thu Jan 01 00:00:00 1970 +0000
283 date: Thu Jan 01 00:00:00 1970 +0000
283 summary: another change for branch stable
284 summary: another change for branch stable
284
285
285
286
286 $ rm -r ua
287 $ rm -r ua
287
288
288
289
289 Testing default checkout:
290 Testing default checkout:
290
291
291 $ hg clone a ua
292 $ hg clone a ua
292 updating to branch default
293 updating to branch default
293 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
294 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
294
295
295 Repo ua has both heads:
296 Repo ua has both heads:
296
297
297 $ hg -R ua heads
298 $ hg -R ua heads
298 changeset: 15:0aae7cf88f0d
299 changeset: 15:0aae7cf88f0d
299 branch: stable
300 branch: stable
300 tag: tip
301 tag: tip
301 user: test
302 user: test
302 date: Thu Jan 01 00:00:00 1970 +0000
303 date: Thu Jan 01 00:00:00 1970 +0000
303 summary: another change for branch stable
304 summary: another change for branch stable
304
305
305 changeset: 12:f21241060d6a
306 changeset: 12:f21241060d6a
306 user: test
307 user: test
307 date: Thu Jan 01 00:00:00 1970 +0000
308 date: Thu Jan 01 00:00:00 1970 +0000
308 summary: hacked default
309 summary: hacked default
309
310
310
311
311 Branch 'default' is checked out:
312 Branch 'default' is checked out:
312
313
313 $ hg -R ua parents
314 $ hg -R ua parents
314 changeset: 12:f21241060d6a
315 changeset: 12:f21241060d6a
315 user: test
316 user: test
316 date: Thu Jan 01 00:00:00 1970 +0000
317 date: Thu Jan 01 00:00:00 1970 +0000
317 summary: hacked default
318 summary: hacked default
318
319
319
320
320 $ rm -r ua
321 $ rm -r ua
321
322
322
323
323 Testing #<branch>:
324 Testing #<branch>:
324
325
325 $ hg clone -u . a#stable ua
326 $ hg clone -u . a#stable ua
326 adding changesets
327 adding changesets
327 adding manifests
328 adding manifests
328 adding file changes
329 adding file changes
329 added 14 changesets with 14 changes to 3 files
330 added 14 changesets with 14 changes to 3 files
330 updating to branch stable
331 updating to branch stable
331 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
332 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
332
333
333 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
334 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
334
335
335 $ hg -R ua heads
336 $ hg -R ua heads
336 changeset: 13:0aae7cf88f0d
337 changeset: 13:0aae7cf88f0d
337 branch: stable
338 branch: stable
338 tag: tip
339 tag: tip
339 user: test
340 user: test
340 date: Thu Jan 01 00:00:00 1970 +0000
341 date: Thu Jan 01 00:00:00 1970 +0000
341 summary: another change for branch stable
342 summary: another change for branch stable
342
343
343 changeset: 10:a7949464abda
344 changeset: 10:a7949464abda
344 user: test
345 user: test
345 date: Thu Jan 01 00:00:00 1970 +0000
346 date: Thu Jan 01 00:00:00 1970 +0000
346 summary: test
347 summary: test
347
348
348
349
349 Same revision checked out in repo a and ua:
350 Same revision checked out in repo a and ua:
350
351
351 $ hg -R a parents --template "{node|short}\n"
352 $ hg -R a parents --template "{node|short}\n"
352 e8ece76546a6
353 e8ece76546a6
353 $ hg -R ua parents --template "{node|short}\n"
354 $ hg -R ua parents --template "{node|short}\n"
354 e8ece76546a6
355 e8ece76546a6
355
356
356 $ rm -r ua
357 $ rm -r ua
357
358
358
359
359 Testing -u -r <branch>:
360 Testing -u -r <branch>:
360
361
361 $ hg clone -u . -r stable a ua
362 $ hg clone -u . -r stable a ua
362 adding changesets
363 adding changesets
363 adding manifests
364 adding manifests
364 adding file changes
365 adding file changes
365 added 14 changesets with 14 changes to 3 files
366 added 14 changesets with 14 changes to 3 files
366 updating to branch stable
367 updating to branch stable
367 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
368 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
368
369
369 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
370 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
370
371
371 $ hg -R ua heads
372 $ hg -R ua heads
372 changeset: 13:0aae7cf88f0d
373 changeset: 13:0aae7cf88f0d
373 branch: stable
374 branch: stable
374 tag: tip
375 tag: tip
375 user: test
376 user: test
376 date: Thu Jan 01 00:00:00 1970 +0000
377 date: Thu Jan 01 00:00:00 1970 +0000
377 summary: another change for branch stable
378 summary: another change for branch stable
378
379
379 changeset: 10:a7949464abda
380 changeset: 10:a7949464abda
380 user: test
381 user: test
381 date: Thu Jan 01 00:00:00 1970 +0000
382 date: Thu Jan 01 00:00:00 1970 +0000
382 summary: test
383 summary: test
383
384
384
385
385 Same revision checked out in repo a and ua:
386 Same revision checked out in repo a and ua:
386
387
387 $ hg -R a parents --template "{node|short}\n"
388 $ hg -R a parents --template "{node|short}\n"
388 e8ece76546a6
389 e8ece76546a6
389 $ hg -R ua parents --template "{node|short}\n"
390 $ hg -R ua parents --template "{node|short}\n"
390 e8ece76546a6
391 e8ece76546a6
391
392
392 $ rm -r ua
393 $ rm -r ua
393
394
394
395
395 Testing -r <branch>:
396 Testing -r <branch>:
396
397
397 $ hg clone -r stable a ua
398 $ hg clone -r stable a ua
398 adding changesets
399 adding changesets
399 adding manifests
400 adding manifests
400 adding file changes
401 adding file changes
401 added 14 changesets with 14 changes to 3 files
402 added 14 changesets with 14 changes to 3 files
402 updating to branch stable
403 updating to branch stable
403 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
404 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
404
405
405 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
406 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
406
407
407 $ hg -R ua heads
408 $ hg -R ua heads
408 changeset: 13:0aae7cf88f0d
409 changeset: 13:0aae7cf88f0d
409 branch: stable
410 branch: stable
410 tag: tip
411 tag: tip
411 user: test
412 user: test
412 date: Thu Jan 01 00:00:00 1970 +0000
413 date: Thu Jan 01 00:00:00 1970 +0000
413 summary: another change for branch stable
414 summary: another change for branch stable
414
415
415 changeset: 10:a7949464abda
416 changeset: 10:a7949464abda
416 user: test
417 user: test
417 date: Thu Jan 01 00:00:00 1970 +0000
418 date: Thu Jan 01 00:00:00 1970 +0000
418 summary: test
419 summary: test
419
420
420
421
421 Branch 'stable' is checked out:
422 Branch 'stable' is checked out:
422
423
423 $ hg -R ua parents
424 $ hg -R ua parents
424 changeset: 13:0aae7cf88f0d
425 changeset: 13:0aae7cf88f0d
425 branch: stable
426 branch: stable
426 tag: tip
427 tag: tip
427 user: test
428 user: test
428 date: Thu Jan 01 00:00:00 1970 +0000
429 date: Thu Jan 01 00:00:00 1970 +0000
429 summary: another change for branch stable
430 summary: another change for branch stable
430
431
431
432
432 $ rm -r ua
433 $ rm -r ua
433
434
434
435
435 Issue2267: Error in 1.6 hg.py: TypeError: 'NoneType' object is not
436 Issue2267: Error in 1.6 hg.py: TypeError: 'NoneType' object is not
436 iterable in addbranchrevs()
437 iterable in addbranchrevs()
437
438
438 $ cat <<EOF > simpleclone.py
439 $ cat <<EOF > simpleclone.py
439 > from mercurial import ui, hg
440 > from mercurial import ui, hg
440 > myui = ui.ui()
441 > myui = ui.ui()
441 > repo = hg.repository(myui, 'a')
442 > repo = hg.repository(myui, 'a')
442 > hg.clone(myui, {}, repo, dest="ua")
443 > hg.clone(myui, {}, repo, dest="ua")
443 > EOF
444 > EOF
444
445
445 $ python simpleclone.py
446 $ python simpleclone.py
446 updating to branch default
447 updating to branch default
447 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
448 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
448
449
449 $ rm -r ua
450 $ rm -r ua
450
451
451 $ cat <<EOF > branchclone.py
452 $ cat <<EOF > branchclone.py
452 > from mercurial import ui, hg, extensions
453 > from mercurial import ui, hg, extensions
453 > myui = ui.ui()
454 > myui = ui.ui()
454 > extensions.loadall(myui)
455 > extensions.loadall(myui)
455 > repo = hg.repository(myui, 'a')
456 > repo = hg.repository(myui, 'a')
456 > hg.clone(myui, {}, repo, dest="ua", branch=["stable",])
457 > hg.clone(myui, {}, repo, dest="ua", branch=["stable",])
457 > EOF
458 > EOF
458
459
459 $ python branchclone.py
460 $ python branchclone.py
460 adding changesets
461 adding changesets
461 adding manifests
462 adding manifests
462 adding file changes
463 adding file changes
463 added 14 changesets with 14 changes to 3 files
464 added 14 changesets with 14 changes to 3 files
464 updating to branch stable
465 updating to branch stable
465 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
466 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
466 $ rm -r ua
467 $ rm -r ua
467
468
468
469
469 Testing failures:
470 Testing failures:
470
471
471 $ mkdir fail
472 $ mkdir fail
472 $ cd fail
473 $ cd fail
473
474
474 No local source
475 No local source
475
476
476 $ hg clone a b
477 $ hg clone a b
477 abort: repository a not found!
478 abort: repository a not found!
478 [255]
479 [255]
479
480
480 No remote source
481 No remote source
481
482
482 $ hg clone http://127.0.0.1:3121/a b
483 $ hg clone http://127.0.0.1:3121/a b
483 abort: error: *refused* (glob)
484 abort: error: *refused* (glob)
484 [255]
485 [255]
485 $ rm -rf b # work around bug with http clone
486 $ rm -rf b # work around bug with http clone
486
487
487
488
488 #if unix-permissions
489 #if unix-permissions
489
490
490 Inaccessible source
491 Inaccessible source
491
492
492 $ mkdir a
493 $ mkdir a
493 $ chmod 000 a
494 $ chmod 000 a
494 $ hg clone a b
495 $ hg clone a b
495 abort: repository a not found!
496 abort: repository a not found!
496 [255]
497 [255]
497
498
498 Inaccessible destination
499 Inaccessible destination
499
500
500 $ hg init b
501 $ hg init b
501 $ cd b
502 $ cd b
502 $ hg clone . ../a
503 $ hg clone . ../a
503 abort: Permission denied: ../a
504 abort: Permission denied: ../a
504 [255]
505 [255]
505 $ cd ..
506 $ cd ..
506 $ chmod 700 a
507 $ chmod 700 a
507 $ rm -r a b
508 $ rm -r a b
508
509
509 #endif
510 #endif
510
511
511
512
512 #if fifo
513 #if fifo
513
514
514 Source of wrong type
515 Source of wrong type
515
516
516 $ mkfifo a
517 $ mkfifo a
517 $ hg clone a b
518 $ hg clone a b
518 abort: repository a not found!
519 abort: repository a not found!
519 [255]
520 [255]
520 $ rm a
521 $ rm a
521
522
522 #endif
523 #endif
523
524
524 Default destination, same directory
525 Default destination, same directory
525
526
526 $ hg init q
527 $ hg init q
527 $ hg clone q
528 $ hg clone q
528 destination directory: q
529 destination directory: q
529 abort: destination 'q' is not empty
530 abort: destination 'q' is not empty
530 [255]
531 [255]
531
532
532 destination directory not empty
533 destination directory not empty
533
534
534 $ mkdir a
535 $ mkdir a
535 $ echo stuff > a/a
536 $ echo stuff > a/a
536 $ hg clone q a
537 $ hg clone q a
537 abort: destination 'a' is not empty
538 abort: destination 'a' is not empty
538 [255]
539 [255]
539
540
540
541
541 #if unix-permissions
542 #if unix-permissions
542
543
543 leave existing directory in place after clone failure
544 leave existing directory in place after clone failure
544
545
545 $ hg init c
546 $ hg init c
546 $ cd c
547 $ cd c
547 $ echo c > c
548 $ echo c > c
548 $ hg commit -A -m test
549 $ hg commit -A -m test
549 adding c
550 adding c
550 $ chmod -rx .hg/store/data
551 $ chmod -rx .hg/store/data
551 $ cd ..
552 $ cd ..
552 $ mkdir d
553 $ mkdir d
553 $ hg clone c d 2> err
554 $ hg clone c d 2> err
554 [255]
555 [255]
555 $ test -d d
556 $ test -d d
556 $ test -d d/.hg
557 $ test -d d/.hg
557 [1]
558 [1]
558
559
559 reenable perm to allow deletion
560 reenable perm to allow deletion
560
561
561 $ chmod +rx c/.hg/store/data
562 $ chmod +rx c/.hg/store/data
562
563
563 #endif
564 #endif
564
565
565 $ cd ..
566 $ cd ..
@@ -1,351 +1,352 b''
1 $ "$TESTDIR/hghave" hardlink || exit 80
1 $ "$TESTDIR/hghave" hardlink || exit 80
2
2
3 $ cat > nlinks.py <<EOF
3 $ cat > nlinks.py <<EOF
4 > import sys
4 > import sys
5 > from mercurial import util
5 > from mercurial import util
6 > for f in sorted(sys.stdin.readlines()):
6 > for f in sorted(sys.stdin.readlines()):
7 > f = f[:-1]
7 > f = f[:-1]
8 > print util.nlinks(f), f
8 > print util.nlinks(f), f
9 > EOF
9 > EOF
10
10
11 $ nlinksdir()
11 $ nlinksdir()
12 > {
12 > {
13 > find $1 -type f | python $TESTTMP/nlinks.py
13 > find $1 -type f | python $TESTTMP/nlinks.py
14 > }
14 > }
15
15
16 Some implementations of cp can't create hardlinks (replaces 'cp -al' on Linux):
16 Some implementations of cp can't create hardlinks (replaces 'cp -al' on Linux):
17
17
18 $ cat > linkcp.py <<EOF
18 $ cat > linkcp.py <<EOF
19 > from mercurial import util
19 > from mercurial import util
20 > import sys
20 > import sys
21 > util.copyfiles(sys.argv[1], sys.argv[2], hardlink=True)
21 > util.copyfiles(sys.argv[1], sys.argv[2], hardlink=True)
22 > EOF
22 > EOF
23
23
24 $ linkcp()
24 $ linkcp()
25 > {
25 > {
26 > python $TESTTMP/linkcp.py $1 $2
26 > python $TESTTMP/linkcp.py $1 $2
27 > }
27 > }
28
28
29 Prepare repo r1:
29 Prepare repo r1:
30
30
31 $ hg init r1
31 $ hg init r1
32 $ cd r1
32 $ cd r1
33
33
34 $ echo c1 > f1
34 $ echo c1 > f1
35 $ hg add f1
35 $ hg add f1
36 $ hg ci -m0
36 $ hg ci -m0
37
37
38 $ mkdir d1
38 $ mkdir d1
39 $ cd d1
39 $ cd d1
40 $ echo c2 > f2
40 $ echo c2 > f2
41 $ hg add f2
41 $ hg add f2
42 $ hg ci -m1
42 $ hg ci -m1
43 $ cd ../..
43 $ cd ../..
44
44
45 $ nlinksdir r1/.hg/store
45 $ nlinksdir r1/.hg/store
46 1 r1/.hg/store/00changelog.i
46 1 r1/.hg/store/00changelog.i
47 1 r1/.hg/store/00manifest.i
47 1 r1/.hg/store/00manifest.i
48 1 r1/.hg/store/data/d1/f2.i
48 1 r1/.hg/store/data/d1/f2.i
49 1 r1/.hg/store/data/f1.i
49 1 r1/.hg/store/data/f1.i
50 1 r1/.hg/store/fncache
50 1 r1/.hg/store/fncache
51 1 r1/.hg/store/phaseroots
51 1 r1/.hg/store/phaseroots
52 1 r1/.hg/store/undo
52 1 r1/.hg/store/undo
53 1 r1/.hg/store/undo.phaseroots
53 1 r1/.hg/store/undo.phaseroots
54
54
55
55
56 Create hardlinked clone r2:
56 Create hardlinked clone r2:
57
57
58 $ hg clone -U --debug r1 r2
58 $ hg clone -U --debug r1 r2
59 linked 7 files
59 linked 7 files
60 listing keys for "bookmarks"
60
61
61 Create non-hardlinked clone r3:
62 Create non-hardlinked clone r3:
62
63
63 $ hg clone --pull r1 r3
64 $ hg clone --pull r1 r3
64 requesting all changes
65 requesting all changes
65 adding changesets
66 adding changesets
66 adding manifests
67 adding manifests
67 adding file changes
68 adding file changes
68 added 2 changesets with 2 changes to 2 files
69 added 2 changesets with 2 changes to 2 files
69 updating to branch default
70 updating to branch default
70 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
71
72
72
73
73 Repos r1 and r2 should now contain hardlinked files:
74 Repos r1 and r2 should now contain hardlinked files:
74
75
75 $ nlinksdir r1/.hg/store
76 $ nlinksdir r1/.hg/store
76 2 r1/.hg/store/00changelog.i
77 2 r1/.hg/store/00changelog.i
77 2 r1/.hg/store/00manifest.i
78 2 r1/.hg/store/00manifest.i
78 2 r1/.hg/store/data/d1/f2.i
79 2 r1/.hg/store/data/d1/f2.i
79 2 r1/.hg/store/data/f1.i
80 2 r1/.hg/store/data/f1.i
80 2 r1/.hg/store/fncache
81 2 r1/.hg/store/fncache
81 1 r1/.hg/store/phaseroots
82 1 r1/.hg/store/phaseroots
82 1 r1/.hg/store/undo
83 1 r1/.hg/store/undo
83 1 r1/.hg/store/undo.phaseroots
84 1 r1/.hg/store/undo.phaseroots
84
85
85 $ nlinksdir r2/.hg/store
86 $ nlinksdir r2/.hg/store
86 2 r2/.hg/store/00changelog.i
87 2 r2/.hg/store/00changelog.i
87 2 r2/.hg/store/00manifest.i
88 2 r2/.hg/store/00manifest.i
88 2 r2/.hg/store/data/d1/f2.i
89 2 r2/.hg/store/data/d1/f2.i
89 2 r2/.hg/store/data/f1.i
90 2 r2/.hg/store/data/f1.i
90 2 r2/.hg/store/fncache
91 2 r2/.hg/store/fncache
91
92
92 Repo r3 should not be hardlinked:
93 Repo r3 should not be hardlinked:
93
94
94 $ nlinksdir r3/.hg/store
95 $ nlinksdir r3/.hg/store
95 1 r3/.hg/store/00changelog.i
96 1 r3/.hg/store/00changelog.i
96 1 r3/.hg/store/00manifest.i
97 1 r3/.hg/store/00manifest.i
97 1 r3/.hg/store/data/d1/f2.i
98 1 r3/.hg/store/data/d1/f2.i
98 1 r3/.hg/store/data/f1.i
99 1 r3/.hg/store/data/f1.i
99 1 r3/.hg/store/fncache
100 1 r3/.hg/store/fncache
100 1 r3/.hg/store/phaseroots
101 1 r3/.hg/store/phaseroots
101 1 r3/.hg/store/undo
102 1 r3/.hg/store/undo
102 1 r3/.hg/store/undo.phaseroots
103 1 r3/.hg/store/undo.phaseroots
103
104
104
105
105 Create a non-inlined filelog in r3:
106 Create a non-inlined filelog in r3:
106
107
107 $ cd r3/d1
108 $ cd r3/d1
108 >>> f = open('data1', 'wb')
109 >>> f = open('data1', 'wb')
109 >>> for x in range(10000):
110 >>> for x in range(10000):
110 ... f.write("%s\n" % str(x))
111 ... f.write("%s\n" % str(x))
111 >>> f.close()
112 >>> f.close()
112 $ for j in 0 1 2 3 4 5 6 7 8 9; do
113 $ for j in 0 1 2 3 4 5 6 7 8 9; do
113 > cat data1 >> f2
114 > cat data1 >> f2
114 > hg commit -m$j
115 > hg commit -m$j
115 > done
116 > done
116 $ cd ../..
117 $ cd ../..
117
118
118 $ nlinksdir r3/.hg/store
119 $ nlinksdir r3/.hg/store
119 1 r3/.hg/store/00changelog.i
120 1 r3/.hg/store/00changelog.i
120 1 r3/.hg/store/00manifest.i
121 1 r3/.hg/store/00manifest.i
121 1 r3/.hg/store/data/d1/f2.d
122 1 r3/.hg/store/data/d1/f2.d
122 1 r3/.hg/store/data/d1/f2.i
123 1 r3/.hg/store/data/d1/f2.i
123 1 r3/.hg/store/data/f1.i
124 1 r3/.hg/store/data/f1.i
124 1 r3/.hg/store/fncache
125 1 r3/.hg/store/fncache
125 1 r3/.hg/store/phaseroots
126 1 r3/.hg/store/phaseroots
126 1 r3/.hg/store/undo
127 1 r3/.hg/store/undo
127 1 r3/.hg/store/undo.phaseroots
128 1 r3/.hg/store/undo.phaseroots
128
129
129 Push to repo r1 should break up most hardlinks in r2:
130 Push to repo r1 should break up most hardlinks in r2:
130
131
131 $ hg -R r2 verify
132 $ hg -R r2 verify
132 checking changesets
133 checking changesets
133 checking manifests
134 checking manifests
134 crosschecking files in changesets and manifests
135 crosschecking files in changesets and manifests
135 checking files
136 checking files
136 2 files, 2 changesets, 2 total revisions
137 2 files, 2 changesets, 2 total revisions
137
138
138 $ cd r3
139 $ cd r3
139 $ hg push
140 $ hg push
140 pushing to $TESTTMP/r1 (glob)
141 pushing to $TESTTMP/r1 (glob)
141 searching for changes
142 searching for changes
142 adding changesets
143 adding changesets
143 adding manifests
144 adding manifests
144 adding file changes
145 adding file changes
145 added 10 changesets with 10 changes to 1 files
146 added 10 changesets with 10 changes to 1 files
146
147
147 $ cd ..
148 $ cd ..
148
149
149 $ nlinksdir r2/.hg/store
150 $ nlinksdir r2/.hg/store
150 1 r2/.hg/store/00changelog.i
151 1 r2/.hg/store/00changelog.i
151 1 r2/.hg/store/00manifest.i
152 1 r2/.hg/store/00manifest.i
152 1 r2/.hg/store/data/d1/f2.i
153 1 r2/.hg/store/data/d1/f2.i
153 2 r2/.hg/store/data/f1.i
154 2 r2/.hg/store/data/f1.i
154 1 r2/.hg/store/fncache
155 1 r2/.hg/store/fncache
155
156
156 $ hg -R r2 verify
157 $ hg -R r2 verify
157 checking changesets
158 checking changesets
158 checking manifests
159 checking manifests
159 crosschecking files in changesets and manifests
160 crosschecking files in changesets and manifests
160 checking files
161 checking files
161 2 files, 2 changesets, 2 total revisions
162 2 files, 2 changesets, 2 total revisions
162
163
163
164
164 $ cd r1
165 $ cd r1
165 $ hg up
166 $ hg up
166 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
167 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
167
168
168 Committing a change to f1 in r1 must break up hardlink f1.i in r2:
169 Committing a change to f1 in r1 must break up hardlink f1.i in r2:
169
170
170 $ echo c1c1 >> f1
171 $ echo c1c1 >> f1
171 $ hg ci -m00
172 $ hg ci -m00
172 $ cd ..
173 $ cd ..
173
174
174 $ nlinksdir r2/.hg/store
175 $ nlinksdir r2/.hg/store
175 1 r2/.hg/store/00changelog.i
176 1 r2/.hg/store/00changelog.i
176 1 r2/.hg/store/00manifest.i
177 1 r2/.hg/store/00manifest.i
177 1 r2/.hg/store/data/d1/f2.i
178 1 r2/.hg/store/data/d1/f2.i
178 1 r2/.hg/store/data/f1.i
179 1 r2/.hg/store/data/f1.i
179 1 r2/.hg/store/fncache
180 1 r2/.hg/store/fncache
180
181
181
182
182 $ cd r3
183 $ cd r3
183 $ hg tip --template '{rev}:{node|short}\n'
184 $ hg tip --template '{rev}:{node|short}\n'
184 11:a6451b6bc41f
185 11:a6451b6bc41f
185 $ echo bla > f1
186 $ echo bla > f1
186 $ hg ci -m1
187 $ hg ci -m1
187 $ cd ..
188 $ cd ..
188
189
189 Create hardlinked copy r4 of r3 (on Linux, we would call 'cp -al'):
190 Create hardlinked copy r4 of r3 (on Linux, we would call 'cp -al'):
190
191
191 $ linkcp r3 r4
192 $ linkcp r3 r4
192
193
193 r4 has hardlinks in the working dir (not just inside .hg):
194 r4 has hardlinks in the working dir (not just inside .hg):
194
195
195 $ nlinksdir r4
196 $ nlinksdir r4
196 2 r4/.hg/00changelog.i
197 2 r4/.hg/00changelog.i
197 2 r4/.hg/branch
198 2 r4/.hg/branch
198 2 r4/.hg/cache/branchheads
199 2 r4/.hg/cache/branchheads
199 2 r4/.hg/cache/tags
200 2 r4/.hg/cache/tags
200 2 r4/.hg/dirstate
201 2 r4/.hg/dirstate
201 2 r4/.hg/hgrc
202 2 r4/.hg/hgrc
202 2 r4/.hg/last-message.txt
203 2 r4/.hg/last-message.txt
203 2 r4/.hg/requires
204 2 r4/.hg/requires
204 2 r4/.hg/store/00changelog.i
205 2 r4/.hg/store/00changelog.i
205 2 r4/.hg/store/00manifest.i
206 2 r4/.hg/store/00manifest.i
206 2 r4/.hg/store/data/d1/f2.d
207 2 r4/.hg/store/data/d1/f2.d
207 2 r4/.hg/store/data/d1/f2.i
208 2 r4/.hg/store/data/d1/f2.i
208 2 r4/.hg/store/data/f1.i
209 2 r4/.hg/store/data/f1.i
209 2 r4/.hg/store/fncache
210 2 r4/.hg/store/fncache
210 2 r4/.hg/store/phaseroots
211 2 r4/.hg/store/phaseroots
211 2 r4/.hg/store/undo
212 2 r4/.hg/store/undo
212 2 r4/.hg/store/undo.phaseroots
213 2 r4/.hg/store/undo.phaseroots
213 2 r4/.hg/undo.bookmarks
214 2 r4/.hg/undo.bookmarks
214 2 r4/.hg/undo.branch
215 2 r4/.hg/undo.branch
215 2 r4/.hg/undo.desc
216 2 r4/.hg/undo.desc
216 2 r4/.hg/undo.dirstate
217 2 r4/.hg/undo.dirstate
217 2 r4/d1/data1
218 2 r4/d1/data1
218 2 r4/d1/f2
219 2 r4/d1/f2
219 2 r4/f1
220 2 r4/f1
220
221
221 Update back to revision 11 in r4 should break hardlink of file f1:
222 Update back to revision 11 in r4 should break hardlink of file f1:
222
223
223 $ hg -R r4 up 11
224 $ hg -R r4 up 11
224 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
225 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
225
226
226 $ nlinksdir r4
227 $ nlinksdir r4
227 2 r4/.hg/00changelog.i
228 2 r4/.hg/00changelog.i
228 1 r4/.hg/branch
229 1 r4/.hg/branch
229 2 r4/.hg/cache/branchheads
230 2 r4/.hg/cache/branchheads
230 2 r4/.hg/cache/tags
231 2 r4/.hg/cache/tags
231 1 r4/.hg/dirstate
232 1 r4/.hg/dirstate
232 2 r4/.hg/hgrc
233 2 r4/.hg/hgrc
233 2 r4/.hg/last-message.txt
234 2 r4/.hg/last-message.txt
234 2 r4/.hg/requires
235 2 r4/.hg/requires
235 2 r4/.hg/store/00changelog.i
236 2 r4/.hg/store/00changelog.i
236 2 r4/.hg/store/00manifest.i
237 2 r4/.hg/store/00manifest.i
237 2 r4/.hg/store/data/d1/f2.d
238 2 r4/.hg/store/data/d1/f2.d
238 2 r4/.hg/store/data/d1/f2.i
239 2 r4/.hg/store/data/d1/f2.i
239 2 r4/.hg/store/data/f1.i
240 2 r4/.hg/store/data/f1.i
240 2 r4/.hg/store/fncache
241 2 r4/.hg/store/fncache
241 2 r4/.hg/store/phaseroots
242 2 r4/.hg/store/phaseroots
242 2 r4/.hg/store/undo
243 2 r4/.hg/store/undo
243 2 r4/.hg/store/undo.phaseroots
244 2 r4/.hg/store/undo.phaseroots
244 2 r4/.hg/undo.bookmarks
245 2 r4/.hg/undo.bookmarks
245 2 r4/.hg/undo.branch
246 2 r4/.hg/undo.branch
246 2 r4/.hg/undo.desc
247 2 r4/.hg/undo.desc
247 2 r4/.hg/undo.dirstate
248 2 r4/.hg/undo.dirstate
248 2 r4/d1/data1
249 2 r4/d1/data1
249 2 r4/d1/f2
250 2 r4/d1/f2
250 1 r4/f1
251 1 r4/f1
251
252
252
253
253 Test hardlinking outside hg:
254 Test hardlinking outside hg:
254
255
255 $ mkdir x
256 $ mkdir x
256 $ echo foo > x/a
257 $ echo foo > x/a
257
258
258 $ linkcp x y
259 $ linkcp x y
259 $ echo bar >> y/a
260 $ echo bar >> y/a
260
261
261 No diff if hardlink:
262 No diff if hardlink:
262
263
263 $ diff x/a y/a
264 $ diff x/a y/a
264
265
265 Test mq hardlinking:
266 Test mq hardlinking:
266
267
267 $ echo "[extensions]" >> $HGRCPATH
268 $ echo "[extensions]" >> $HGRCPATH
268 $ echo "mq=" >> $HGRCPATH
269 $ echo "mq=" >> $HGRCPATH
269
270
270 $ hg init a
271 $ hg init a
271 $ cd a
272 $ cd a
272
273
273 $ hg qimport -n foo - << EOF
274 $ hg qimport -n foo - << EOF
274 > # HG changeset patch
275 > # HG changeset patch
275 > # Date 1 0
276 > # Date 1 0
276 > diff -r 2588a8b53d66 a
277 > diff -r 2588a8b53d66 a
277 > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
278 > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
278 > +++ b/a Wed Jul 23 15:54:29 2008 +0200
279 > +++ b/a Wed Jul 23 15:54:29 2008 +0200
279 > @@ -0,0 +1,1 @@
280 > @@ -0,0 +1,1 @@
280 > +a
281 > +a
281 > EOF
282 > EOF
282 adding foo to series file
283 adding foo to series file
283
284
284 $ hg qpush
285 $ hg qpush
285 applying foo
286 applying foo
286 now at: foo
287 now at: foo
287
288
288 $ cd ..
289 $ cd ..
289 $ linkcp a b
290 $ linkcp a b
290 $ cd b
291 $ cd b
291
292
292 $ hg qimport -n bar - << EOF
293 $ hg qimport -n bar - << EOF
293 > # HG changeset patch
294 > # HG changeset patch
294 > # Date 2 0
295 > # Date 2 0
295 > diff -r 2588a8b53d66 a
296 > diff -r 2588a8b53d66 a
296 > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
297 > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
297 > +++ b/b Wed Jul 23 15:54:29 2008 +0200
298 > +++ b/b Wed Jul 23 15:54:29 2008 +0200
298 > @@ -0,0 +1,1 @@
299 > @@ -0,0 +1,1 @@
299 > +b
300 > +b
300 > EOF
301 > EOF
301 adding bar to series file
302 adding bar to series file
302
303
303 $ hg qpush
304 $ hg qpush
304 applying bar
305 applying bar
305 now at: bar
306 now at: bar
306
307
307 $ cat .hg/patches/status
308 $ cat .hg/patches/status
308 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo
309 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo
309 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c:bar
310 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c:bar
310
311
311 $ cat .hg/patches/series
312 $ cat .hg/patches/series
312 foo
313 foo
313 bar
314 bar
314
315
315 $ cat ../a/.hg/patches/status
316 $ cat ../a/.hg/patches/status
316 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo
317 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo
317
318
318 $ cat ../a/.hg/patches/series
319 $ cat ../a/.hg/patches/series
319 foo
320 foo
320
321
321 Test tags hardlinking:
322 Test tags hardlinking:
322
323
323 $ hg qdel -r qbase:qtip
324 $ hg qdel -r qbase:qtip
324 patch foo finalized without changeset message
325 patch foo finalized without changeset message
325 patch bar finalized without changeset message
326 patch bar finalized without changeset message
326
327
327 $ hg tag -l lfoo
328 $ hg tag -l lfoo
328 $ hg tag foo
329 $ hg tag foo
329
330
330 $ cd ..
331 $ cd ..
331 $ linkcp b c
332 $ linkcp b c
332 $ cd c
333 $ cd c
333
334
334 $ hg tag -l -r 0 lbar
335 $ hg tag -l -r 0 lbar
335 $ hg tag -r 0 bar
336 $ hg tag -r 0 bar
336
337
337 $ cat .hgtags
338 $ cat .hgtags
338 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo
339 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo
339 430ed4828a74fa4047bc816a25500f7472ab4bfe bar
340 430ed4828a74fa4047bc816a25500f7472ab4bfe bar
340
341
341 $ cat .hg/localtags
342 $ cat .hg/localtags
342 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo
343 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo
343 430ed4828a74fa4047bc816a25500f7472ab4bfe lbar
344 430ed4828a74fa4047bc816a25500f7472ab4bfe lbar
344
345
345 $ cat ../b/.hgtags
346 $ cat ../b/.hgtags
346 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo
347 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo
347
348
348 $ cat ../b/.hg/localtags
349 $ cat ../b/.hg/localtags
349 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo
350 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo
350
351
351 $ cd ..
352 $ cd ..
@@ -1,731 +1,732 b''
1 $ echo "[extensions]" >> $HGRCPATH
1 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "graphlog=" >> $HGRCPATH
2 $ echo "graphlog=" >> $HGRCPATH
3
3
4 $ hg init a
4 $ hg init a
5 $ cd a
5 $ cd a
6 $ echo foo > t1
6 $ echo foo > t1
7 $ hg add t1
7 $ hg add t1
8 $ hg commit -m "1"
8 $ hg commit -m "1"
9
9
10 $ cd ..
10 $ cd ..
11 $ hg clone a b
11 $ hg clone a b
12 updating to branch default
12 updating to branch default
13 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
14
14
15 $ cd a
15 $ cd a
16 $ echo foo > t2
16 $ echo foo > t2
17 $ hg add t2
17 $ hg add t2
18 $ hg commit -m "2"
18 $ hg commit -m "2"
19
19
20 $ cd ../b
20 $ cd ../b
21 $ echo foo > t3
21 $ echo foo > t3
22 $ hg add t3
22 $ hg add t3
23 $ hg commit -m "3"
23 $ hg commit -m "3"
24
24
25 $ hg push ../a
25 $ hg push ../a
26 pushing to ../a
26 pushing to ../a
27 searching for changes
27 searching for changes
28 abort: push creates new remote head 1e108cc5548c!
28 abort: push creates new remote head 1e108cc5548c!
29 (you should pull and merge or use push -f to force)
29 (you should pull and merge or use push -f to force)
30 [255]
30 [255]
31
31
32 $ hg push --debug ../a
32 $ hg push --debug ../a
33 pushing to ../a
33 pushing to ../a
34 query 1; heads
34 query 1; heads
35 searching for changes
35 searching for changes
36 taking quick initial sample
36 taking quick initial sample
37 searching: 2 queries
37 searching: 2 queries
38 query 2; still undecided: 1, sample size is: 1
38 query 2; still undecided: 1, sample size is: 1
39 2 total queries
39 2 total queries
40 listing keys for "bookmarks"
40 new remote heads on branch 'default'
41 new remote heads on branch 'default'
41 new remote head 1e108cc5548c
42 new remote head 1e108cc5548c
42 abort: push creates new remote head 1e108cc5548c!
43 abort: push creates new remote head 1e108cc5548c!
43 (you should pull and merge or use push -f to force)
44 (you should pull and merge or use push -f to force)
44 [255]
45 [255]
45
46
46 $ hg pull ../a
47 $ hg pull ../a
47 pulling from ../a
48 pulling from ../a
48 searching for changes
49 searching for changes
49 adding changesets
50 adding changesets
50 adding manifests
51 adding manifests
51 adding file changes
52 adding file changes
52 added 1 changesets with 1 changes to 1 files (+1 heads)
53 added 1 changesets with 1 changes to 1 files (+1 heads)
53 (run 'hg heads' to see heads, 'hg merge' to merge)
54 (run 'hg heads' to see heads, 'hg merge' to merge)
54
55
55 $ hg push ../a
56 $ hg push ../a
56 pushing to ../a
57 pushing to ../a
57 searching for changes
58 searching for changes
58 abort: push creates new remote head 1e108cc5548c!
59 abort: push creates new remote head 1e108cc5548c!
59 (did you forget to merge? use push -f to force)
60 (did you forget to merge? use push -f to force)
60 [255]
61 [255]
61
62
62 $ hg merge
63 $ hg merge
63 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 (branch merge, don't forget to commit)
65 (branch merge, don't forget to commit)
65
66
66 $ hg commit -m "4"
67 $ hg commit -m "4"
67 $ hg push ../a
68 $ hg push ../a
68 pushing to ../a
69 pushing to ../a
69 searching for changes
70 searching for changes
70 adding changesets
71 adding changesets
71 adding manifests
72 adding manifests
72 adding file changes
73 adding file changes
73 added 2 changesets with 1 changes to 1 files
74 added 2 changesets with 1 changes to 1 files
74
75
75 $ cd ..
76 $ cd ..
76
77
77 $ hg init c
78 $ hg init c
78 $ cd c
79 $ cd c
79 $ for i in 0 1 2; do
80 $ for i in 0 1 2; do
80 > echo $i >> foo
81 > echo $i >> foo
81 > hg ci -Am $i
82 > hg ci -Am $i
82 > done
83 > done
83 adding foo
84 adding foo
84 $ cd ..
85 $ cd ..
85
86
86 $ hg clone c d
87 $ hg clone c d
87 updating to branch default
88 updating to branch default
88 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
89
90
90 $ cd d
91 $ cd d
91 $ for i in 0 1; do
92 $ for i in 0 1; do
92 > hg co -C $i
93 > hg co -C $i
93 > echo d-$i >> foo
94 > echo d-$i >> foo
94 > hg ci -m d-$i
95 > hg ci -m d-$i
95 > done
96 > done
96 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
97 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
97 created new head
98 created new head
98 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
99 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
99 created new head
100 created new head
100
101
101 $ HGMERGE=true hg merge 3
102 $ HGMERGE=true hg merge 3
102 merging foo
103 merging foo
103 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
104 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
104 (branch merge, don't forget to commit)
105 (branch merge, don't forget to commit)
105
106
106 $ hg ci -m c-d
107 $ hg ci -m c-d
107
108
108 $ hg push ../c
109 $ hg push ../c
109 pushing to ../c
110 pushing to ../c
110 searching for changes
111 searching for changes
111 abort: push creates new remote head 6346d66eb9f5!
112 abort: push creates new remote head 6346d66eb9f5!
112 (did you forget to merge? use push -f to force)
113 (did you forget to merge? use push -f to force)
113 [255]
114 [255]
114
115
115 $ hg push -r 2 ../c
116 $ hg push -r 2 ../c
116 pushing to ../c
117 pushing to ../c
117 searching for changes
118 searching for changes
118 no changes found
119 no changes found
119 [1]
120 [1]
120
121
121 $ hg push -r 3 ../c
122 $ hg push -r 3 ../c
122 pushing to ../c
123 pushing to ../c
123 searching for changes
124 searching for changes
124 abort: push creates new remote head a5dda829a167!
125 abort: push creates new remote head a5dda829a167!
125 (did you forget to merge? use push -f to force)
126 (did you forget to merge? use push -f to force)
126 [255]
127 [255]
127
128
128 $ hg push -v -r 3 -r 4 ../c
129 $ hg push -v -r 3 -r 4 ../c
129 pushing to ../c
130 pushing to ../c
130 searching for changes
131 searching for changes
131 new remote heads on branch 'default'
132 new remote heads on branch 'default'
132 new remote head a5dda829a167
133 new remote head a5dda829a167
133 new remote head ee8fbc7a0295
134 new remote head ee8fbc7a0295
134 abort: push creates new remote head a5dda829a167!
135 abort: push creates new remote head a5dda829a167!
135 (did you forget to merge? use push -f to force)
136 (did you forget to merge? use push -f to force)
136 [255]
137 [255]
137
138
138 $ hg push -v -f -r 3 -r 4 ../c
139 $ hg push -v -f -r 3 -r 4 ../c
139 pushing to ../c
140 pushing to ../c
140 searching for changes
141 searching for changes
141 2 changesets found
142 2 changesets found
142 adding changesets
143 adding changesets
143 adding manifests
144 adding manifests
144 adding file changes
145 adding file changes
145 added 2 changesets with 2 changes to 1 files (+2 heads)
146 added 2 changesets with 2 changes to 1 files (+2 heads)
146
147
147 $ hg push -r 5 ../c
148 $ hg push -r 5 ../c
148 pushing to ../c
149 pushing to ../c
149 searching for changes
150 searching for changes
150 adding changesets
151 adding changesets
151 adding manifests
152 adding manifests
152 adding file changes
153 adding file changes
153 added 1 changesets with 1 changes to 1 files (-1 heads)
154 added 1 changesets with 1 changes to 1 files (-1 heads)
154
155
155 $ hg in ../c
156 $ hg in ../c
156 comparing with ../c
157 comparing with ../c
157 searching for changes
158 searching for changes
158 no changes found
159 no changes found
159 [1]
160 [1]
160
161
161
162
162 Issue450: push -r warns about remote head creation even if no heads
163 Issue450: push -r warns about remote head creation even if no heads
163 will be created
164 will be created
164
165
165 $ hg init ../e
166 $ hg init ../e
166 $ hg push -r 0 ../e
167 $ hg push -r 0 ../e
167 pushing to ../e
168 pushing to ../e
168 searching for changes
169 searching for changes
169 adding changesets
170 adding changesets
170 adding manifests
171 adding manifests
171 adding file changes
172 adding file changes
172 added 1 changesets with 1 changes to 1 files
173 added 1 changesets with 1 changes to 1 files
173
174
174 $ hg push -r 1 ../e
175 $ hg push -r 1 ../e
175 pushing to ../e
176 pushing to ../e
176 searching for changes
177 searching for changes
177 adding changesets
178 adding changesets
178 adding manifests
179 adding manifests
179 adding file changes
180 adding file changes
180 added 1 changesets with 1 changes to 1 files
181 added 1 changesets with 1 changes to 1 files
181
182
182 $ cd ..
183 $ cd ..
183
184
184
185
185 Issue736: named branches are not considered for detection of
186 Issue736: named branches are not considered for detection of
186 unmerged heads in "hg push"
187 unmerged heads in "hg push"
187
188
188 $ hg init f
189 $ hg init f
189 $ cd f
190 $ cd f
190 $ hg -q branch a
191 $ hg -q branch a
191 $ echo 0 > foo
192 $ echo 0 > foo
192 $ hg -q ci -Am 0
193 $ hg -q ci -Am 0
193 $ echo 1 > foo
194 $ echo 1 > foo
194 $ hg -q ci -m 1
195 $ hg -q ci -m 1
195 $ hg -q up 0
196 $ hg -q up 0
196 $ echo 2 > foo
197 $ echo 2 > foo
197 $ hg -q ci -m 2
198 $ hg -q ci -m 2
198 $ hg -q up 0
199 $ hg -q up 0
199 $ hg -q branch b
200 $ hg -q branch b
200 $ echo 3 > foo
201 $ echo 3 > foo
201 $ hg -q ci -m 3
202 $ hg -q ci -m 3
202 $ cd ..
203 $ cd ..
203
204
204 $ hg -q clone f g
205 $ hg -q clone f g
205 $ cd g
206 $ cd g
206
207
207 Push on existing branch and new branch:
208 Push on existing branch and new branch:
208
209
209 $ hg -q up 1
210 $ hg -q up 1
210 $ echo 4 > foo
211 $ echo 4 > foo
211 $ hg -q ci -m 4
212 $ hg -q ci -m 4
212 $ hg -q up 0
213 $ hg -q up 0
213 $ echo 5 > foo
214 $ echo 5 > foo
214 $ hg -q branch c
215 $ hg -q branch c
215 $ hg -q ci -m 5
216 $ hg -q ci -m 5
216
217
217 $ hg push ../f
218 $ hg push ../f
218 pushing to ../f
219 pushing to ../f
219 searching for changes
220 searching for changes
220 abort: push creates new remote branches: c!
221 abort: push creates new remote branches: c!
221 (use 'hg push --new-branch' to create new remote branches)
222 (use 'hg push --new-branch' to create new remote branches)
222 [255]
223 [255]
223
224
224 $ hg push -r 4 -r 5 ../f
225 $ hg push -r 4 -r 5 ../f
225 pushing to ../f
226 pushing to ../f
226 searching for changes
227 searching for changes
227 abort: push creates new remote branches: c!
228 abort: push creates new remote branches: c!
228 (use 'hg push --new-branch' to create new remote branches)
229 (use 'hg push --new-branch' to create new remote branches)
229 [255]
230 [255]
230
231
231
232
232 Multiple new branches:
233 Multiple new branches:
233
234
234 $ hg -q branch d
235 $ hg -q branch d
235 $ echo 6 > foo
236 $ echo 6 > foo
236 $ hg -q ci -m 6
237 $ hg -q ci -m 6
237
238
238 $ hg push ../f
239 $ hg push ../f
239 pushing to ../f
240 pushing to ../f
240 searching for changes
241 searching for changes
241 abort: push creates new remote branches: c, d!
242 abort: push creates new remote branches: c, d!
242 (use 'hg push --new-branch' to create new remote branches)
243 (use 'hg push --new-branch' to create new remote branches)
243 [255]
244 [255]
244
245
245 $ hg push -r 4 -r 6 ../f
246 $ hg push -r 4 -r 6 ../f
246 pushing to ../f
247 pushing to ../f
247 searching for changes
248 searching for changes
248 abort: push creates new remote branches: c, d!
249 abort: push creates new remote branches: c, d!
249 (use 'hg push --new-branch' to create new remote branches)
250 (use 'hg push --new-branch' to create new remote branches)
250 [255]
251 [255]
251
252
252 $ cd ../g
253 $ cd ../g
253
254
254
255
255 Fail on multiple head push:
256 Fail on multiple head push:
256
257
257 $ hg -q up 1
258 $ hg -q up 1
258 $ echo 7 > foo
259 $ echo 7 > foo
259 $ hg -q ci -m 7
260 $ hg -q ci -m 7
260
261
261 $ hg push -r 4 -r 7 ../f
262 $ hg push -r 4 -r 7 ../f
262 pushing to ../f
263 pushing to ../f
263 searching for changes
264 searching for changes
264 abort: push creates new remote head 0b715ef6ff8f on branch 'a'!
265 abort: push creates new remote head 0b715ef6ff8f on branch 'a'!
265 (did you forget to merge? use push -f to force)
266 (did you forget to merge? use push -f to force)
266 [255]
267 [255]
267
268
268 Push replacement head on existing branches:
269 Push replacement head on existing branches:
269
270
270 $ hg -q up 3
271 $ hg -q up 3
271 $ echo 8 > foo
272 $ echo 8 > foo
272 $ hg -q ci -m 8
273 $ hg -q ci -m 8
273
274
274 $ hg push -r 7 -r 8 ../f
275 $ hg push -r 7 -r 8 ../f
275 pushing to ../f
276 pushing to ../f
276 searching for changes
277 searching for changes
277 adding changesets
278 adding changesets
278 adding manifests
279 adding manifests
279 adding file changes
280 adding file changes
280 added 2 changesets with 2 changes to 1 files
281 added 2 changesets with 2 changes to 1 files
281
282
282
283
283 Merge of branch a to other branch b followed by unrelated push
284 Merge of branch a to other branch b followed by unrelated push
284 on branch a:
285 on branch a:
285
286
286 $ hg -q up 7
287 $ hg -q up 7
287 $ HGMERGE=true hg -q merge 8
288 $ HGMERGE=true hg -q merge 8
288 $ hg -q ci -m 9
289 $ hg -q ci -m 9
289 $ hg -q up 8
290 $ hg -q up 8
290 $ echo 10 > foo
291 $ echo 10 > foo
291 $ hg -q ci -m 10
292 $ hg -q ci -m 10
292
293
293 $ hg push -r 9 ../f
294 $ hg push -r 9 ../f
294 pushing to ../f
295 pushing to ../f
295 searching for changes
296 searching for changes
296 adding changesets
297 adding changesets
297 adding manifests
298 adding manifests
298 adding file changes
299 adding file changes
299 added 1 changesets with 1 changes to 1 files (-1 heads)
300 added 1 changesets with 1 changes to 1 files (-1 heads)
300
301
301 $ hg push -r 10 ../f
302 $ hg push -r 10 ../f
302 pushing to ../f
303 pushing to ../f
303 searching for changes
304 searching for changes
304 adding changesets
305 adding changesets
305 adding manifests
306 adding manifests
306 adding file changes
307 adding file changes
307 added 1 changesets with 1 changes to 1 files (+1 heads)
308 added 1 changesets with 1 changes to 1 files (+1 heads)
308
309
309
310
310 Cheating the counting algorithm:
311 Cheating the counting algorithm:
311
312
312 $ hg -q up 9
313 $ hg -q up 9
313 $ HGMERGE=true hg -q merge 2
314 $ HGMERGE=true hg -q merge 2
314 $ hg -q ci -m 11
315 $ hg -q ci -m 11
315 $ hg -q up 1
316 $ hg -q up 1
316 $ echo 12 > foo
317 $ echo 12 > foo
317 $ hg -q ci -m 12
318 $ hg -q ci -m 12
318
319
319 $ hg push -r 11 -r 12 ../f
320 $ hg push -r 11 -r 12 ../f
320 pushing to ../f
321 pushing to ../f
321 searching for changes
322 searching for changes
322 adding changesets
323 adding changesets
323 adding manifests
324 adding manifests
324 adding file changes
325 adding file changes
325 added 2 changesets with 2 changes to 1 files
326 added 2 changesets with 2 changes to 1 files
326
327
327
328
328 Failed push of new named branch:
329 Failed push of new named branch:
329
330
330 $ echo 12 > foo
331 $ echo 12 > foo
331 $ hg -q ci -m 12a
332 $ hg -q ci -m 12a
332 [1]
333 [1]
333 $ hg -q up 11
334 $ hg -q up 11
334 $ echo 13 > foo
335 $ echo 13 > foo
335 $ hg -q branch e
336 $ hg -q branch e
336 $ hg -q ci -m 13d
337 $ hg -q ci -m 13d
337
338
338 $ hg push -r 12 -r 13 ../f
339 $ hg push -r 12 -r 13 ../f
339 pushing to ../f
340 pushing to ../f
340 searching for changes
341 searching for changes
341 abort: push creates new remote branches: e!
342 abort: push creates new remote branches: e!
342 (use 'hg push --new-branch' to create new remote branches)
343 (use 'hg push --new-branch' to create new remote branches)
343 [255]
344 [255]
344
345
345
346
346 Using --new-branch to push new named branch:
347 Using --new-branch to push new named branch:
347
348
348 $ hg push --new-branch -r 12 -r 13 ../f
349 $ hg push --new-branch -r 12 -r 13 ../f
349 pushing to ../f
350 pushing to ../f
350 searching for changes
351 searching for changes
351 adding changesets
352 adding changesets
352 adding manifests
353 adding manifests
353 adding file changes
354 adding file changes
354 added 1 changesets with 1 changes to 1 files
355 added 1 changesets with 1 changes to 1 files
355
356
356
357
357 Checking prepush logic does not allow silently pushing
358 Checking prepush logic does not allow silently pushing
358 multiple new heads:
359 multiple new heads:
359
360
360 $ cd ..
361 $ cd ..
361 $ hg init h
362 $ hg init h
362 $ echo init > h/init
363 $ echo init > h/init
363 $ hg -R h ci -Am init
364 $ hg -R h ci -Am init
364 adding init
365 adding init
365 $ echo a > h/a
366 $ echo a > h/a
366 $ hg -R h ci -Am a
367 $ hg -R h ci -Am a
367 adding a
368 adding a
368 $ hg clone h i
369 $ hg clone h i
369 updating to branch default
370 updating to branch default
370 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
371 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
371 $ hg -R h up 0
372 $ hg -R h up 0
372 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
373 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
373 $ echo b > h/b
374 $ echo b > h/b
374 $ hg -R h ci -Am b
375 $ hg -R h ci -Am b
375 adding b
376 adding b
376 created new head
377 created new head
377 $ hg -R i up 0
378 $ hg -R i up 0
378 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
379 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
379 $ echo c > i/c
380 $ echo c > i/c
380 $ hg -R i ci -Am c
381 $ hg -R i ci -Am c
381 adding c
382 adding c
382 created new head
383 created new head
383
384
384 $ hg -R i push h
385 $ hg -R i push h
385 pushing to h
386 pushing to h
386 searching for changes
387 searching for changes
387 abort: push creates new remote head 97bd0c84d346!
388 abort: push creates new remote head 97bd0c84d346!
388 (you should pull and merge or use push -f to force)
389 (you should pull and merge or use push -f to force)
389 [255]
390 [255]
390
391
391
392
392 Check prepush logic with merged branches:
393 Check prepush logic with merged branches:
393
394
394 $ hg init j
395 $ hg init j
395 $ hg -R j branch a
396 $ hg -R j branch a
396 marked working directory as branch a
397 marked working directory as branch a
397 (branches are permanent and global, did you want a bookmark?)
398 (branches are permanent and global, did you want a bookmark?)
398 $ echo init > j/foo
399 $ echo init > j/foo
399 $ hg -R j ci -Am init
400 $ hg -R j ci -Am init
400 adding foo
401 adding foo
401 $ hg clone j k
402 $ hg clone j k
402 updating to branch a
403 updating to branch a
403 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
404 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
404 $ echo a1 > j/foo
405 $ echo a1 > j/foo
405 $ hg -R j ci -m a1
406 $ hg -R j ci -m a1
406 $ hg -R k branch b
407 $ hg -R k branch b
407 marked working directory as branch b
408 marked working directory as branch b
408 (branches are permanent and global, did you want a bookmark?)
409 (branches are permanent and global, did you want a bookmark?)
409 $ echo b > k/foo
410 $ echo b > k/foo
410 $ hg -R k ci -m b
411 $ hg -R k ci -m b
411 $ hg -R k up 0
412 $ hg -R k up 0
412 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
413 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
413
414
414 $ hg -R k merge b
415 $ hg -R k merge b
415 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
416 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
416 (branch merge, don't forget to commit)
417 (branch merge, don't forget to commit)
417
418
418 $ hg -R k ci -m merge
419 $ hg -R k ci -m merge
419
420
420 $ hg -R k push -r a j
421 $ hg -R k push -r a j
421 pushing to j
422 pushing to j
422 searching for changes
423 searching for changes
423 abort: push creates new remote branches: b!
424 abort: push creates new remote branches: b!
424 (use 'hg push --new-branch' to create new remote branches)
425 (use 'hg push --new-branch' to create new remote branches)
425 [255]
426 [255]
426
427
427
428
428 Prepush -r should not allow you to sneak in new heads:
429 Prepush -r should not allow you to sneak in new heads:
429
430
430 $ hg init l
431 $ hg init l
431 $ cd l
432 $ cd l
432 $ echo a >> foo
433 $ echo a >> foo
433 $ hg -q add foo
434 $ hg -q add foo
434 $ hg -q branch a
435 $ hg -q branch a
435 $ hg -q ci -ma
436 $ hg -q ci -ma
436 $ hg -q up null
437 $ hg -q up null
437 $ echo a >> foo
438 $ echo a >> foo
438 $ hg -q add foo
439 $ hg -q add foo
439 $ hg -q branch b
440 $ hg -q branch b
440 $ hg -q ci -mb
441 $ hg -q ci -mb
441 $ cd ..
442 $ cd ..
442 $ hg -q clone l m -u a
443 $ hg -q clone l m -u a
443 $ cd m
444 $ cd m
444 $ hg -q merge b
445 $ hg -q merge b
445 $ hg -q ci -mmb
446 $ hg -q ci -mmb
446 $ hg -q up 0
447 $ hg -q up 0
447 $ echo a >> foo
448 $ echo a >> foo
448 $ hg -q ci -ma2
449 $ hg -q ci -ma2
449 $ hg -q up 2
450 $ hg -q up 2
450 $ echo a >> foo
451 $ echo a >> foo
451 $ hg -q branch -f b
452 $ hg -q branch -f b
452 $ hg -q ci -mb2
453 $ hg -q ci -mb2
453 $ hg -q merge 3
454 $ hg -q merge 3
454 $ hg -q ci -mma
455 $ hg -q ci -mma
455
456
456 $ hg push ../l -b b
457 $ hg push ../l -b b
457 pushing to ../l
458 pushing to ../l
458 searching for changes
459 searching for changes
459 abort: push creates new remote head e7e31d71180f on branch 'a'!
460 abort: push creates new remote head e7e31d71180f on branch 'a'!
460 (did you forget to merge? use push -f to force)
461 (did you forget to merge? use push -f to force)
461 [255]
462 [255]
462
463
463 $ cd ..
464 $ cd ..
464
465
465
466
466 Check prepush with new branch head on former topo non-head:
467 Check prepush with new branch head on former topo non-head:
467
468
468 $ hg init n
469 $ hg init n
469 $ cd n
470 $ cd n
470 $ hg branch A
471 $ hg branch A
471 marked working directory as branch A
472 marked working directory as branch A
472 (branches are permanent and global, did you want a bookmark?)
473 (branches are permanent and global, did you want a bookmark?)
473 $ echo a >a
474 $ echo a >a
474 $ hg ci -Ama
475 $ hg ci -Ama
475 adding a
476 adding a
476 $ hg branch B
477 $ hg branch B
477 marked working directory as branch B
478 marked working directory as branch B
478 (branches are permanent and global, did you want a bookmark?)
479 (branches are permanent and global, did you want a bookmark?)
479 $ echo b >b
480 $ echo b >b
480 $ hg ci -Amb
481 $ hg ci -Amb
481 adding b
482 adding b
482
483
483 b is now branch head of B, and a topological head
484 b is now branch head of B, and a topological head
484 a is now branch head of A, but not a topological head
485 a is now branch head of A, but not a topological head
485
486
486 $ hg clone . inner
487 $ hg clone . inner
487 updating to branch B
488 updating to branch B
488 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
489 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
489 $ cd inner
490 $ cd inner
490 $ hg up B
491 $ hg up B
491 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
492 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
492 $ echo b1 >b1
493 $ echo b1 >b1
493 $ hg ci -Amb1
494 $ hg ci -Amb1
494 adding b1
495 adding b1
495
496
496 in the clone b1 is now the head of B
497 in the clone b1 is now the head of B
497
498
498 $ cd ..
499 $ cd ..
499 $ hg up 0
500 $ hg up 0
500 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
501 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
501 $ echo a2 >a2
502 $ echo a2 >a2
502 $ hg ci -Ama2
503 $ hg ci -Ama2
503 adding a2
504 adding a2
504
505
505 a2 is now the new branch head of A, and a new topological head
506 a2 is now the new branch head of A, and a new topological head
506 it replaces a former inner branch head, so it should at most warn about
507 it replaces a former inner branch head, so it should at most warn about
507 A, not B
508 A, not B
508
509
509 glog of local:
510 glog of local:
510
511
511 $ hg glog --template "{rev}: {branches} {desc}\n"
512 $ hg glog --template "{rev}: {branches} {desc}\n"
512 @ 2: A a2
513 @ 2: A a2
513 |
514 |
514 | o 1: B b
515 | o 1: B b
515 |/
516 |/
516 o 0: A a
517 o 0: A a
517
518
518 glog of remote:
519 glog of remote:
519
520
520 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
521 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
521 @ 2: B b1
522 @ 2: B b1
522 |
523 |
523 o 1: B b
524 o 1: B b
524 |
525 |
525 o 0: A a
526 o 0: A a
526
527
527 outgoing:
528 outgoing:
528
529
529 $ hg out inner --template "{rev}: {branches} {desc}\n"
530 $ hg out inner --template "{rev}: {branches} {desc}\n"
530 comparing with inner
531 comparing with inner
531 searching for changes
532 searching for changes
532 2: A a2
533 2: A a2
533
534
534 $ hg push inner
535 $ hg push inner
535 pushing to inner
536 pushing to inner
536 searching for changes
537 searching for changes
537 adding changesets
538 adding changesets
538 adding manifests
539 adding manifests
539 adding file changes
540 adding file changes
540 added 1 changesets with 1 changes to 1 files (+1 heads)
541 added 1 changesets with 1 changes to 1 files (+1 heads)
541
542
542 $ cd ..
543 $ cd ..
543
544
544
545
545 Check prepush with new branch head on former topo head:
546 Check prepush with new branch head on former topo head:
546
547
547 $ hg init o
548 $ hg init o
548 $ cd o
549 $ cd o
549 $ hg branch A
550 $ hg branch A
550 marked working directory as branch A
551 marked working directory as branch A
551 (branches are permanent and global, did you want a bookmark?)
552 (branches are permanent and global, did you want a bookmark?)
552 $ echo a >a
553 $ echo a >a
553 $ hg ci -Ama
554 $ hg ci -Ama
554 adding a
555 adding a
555 $ hg branch B
556 $ hg branch B
556 marked working directory as branch B
557 marked working directory as branch B
557 (branches are permanent and global, did you want a bookmark?)
558 (branches are permanent and global, did you want a bookmark?)
558 $ echo b >b
559 $ echo b >b
559 $ hg ci -Amb
560 $ hg ci -Amb
560 adding b
561 adding b
561
562
562 b is now branch head of B, and a topological head
563 b is now branch head of B, and a topological head
563
564
564 $ hg up 0
565 $ hg up 0
565 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
566 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
566 $ echo a1 >a1
567 $ echo a1 >a1
567 $ hg ci -Ama1
568 $ hg ci -Ama1
568 adding a1
569 adding a1
569
570
570 a1 is now branch head of A, and a topological head
571 a1 is now branch head of A, and a topological head
571
572
572 $ hg clone . inner
573 $ hg clone . inner
573 updating to branch A
574 updating to branch A
574 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
575 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
575 $ cd inner
576 $ cd inner
576 $ hg up B
577 $ hg up B
577 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
578 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
578 $ echo b1 >b1
579 $ echo b1 >b1
579 $ hg ci -Amb1
580 $ hg ci -Amb1
580 adding b1
581 adding b1
581
582
582 in the clone b1 is now the head of B
583 in the clone b1 is now the head of B
583
584
584 $ cd ..
585 $ cd ..
585 $ echo a2 >a2
586 $ echo a2 >a2
586 $ hg ci -Ama2
587 $ hg ci -Ama2
587 adding a2
588 adding a2
588
589
589 a2 is now the new branch head of A, and a topological head
590 a2 is now the new branch head of A, and a topological head
590 it replaces a former topological and branch head, so this should not warn
591 it replaces a former topological and branch head, so this should not warn
591
592
592 glog of local:
593 glog of local:
593
594
594 $ hg glog --template "{rev}: {branches} {desc}\n"
595 $ hg glog --template "{rev}: {branches} {desc}\n"
595 @ 3: A a2
596 @ 3: A a2
596 |
597 |
597 o 2: A a1
598 o 2: A a1
598 |
599 |
599 | o 1: B b
600 | o 1: B b
600 |/
601 |/
601 o 0: A a
602 o 0: A a
602
603
603 glog of remote:
604 glog of remote:
604
605
605 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
606 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
606 @ 3: B b1
607 @ 3: B b1
607 |
608 |
608 | o 2: A a1
609 | o 2: A a1
609 | |
610 | |
610 o | 1: B b
611 o | 1: B b
611 |/
612 |/
612 o 0: A a
613 o 0: A a
613
614
614 outgoing:
615 outgoing:
615
616
616 $ hg out inner --template "{rev}: {branches} {desc}\n"
617 $ hg out inner --template "{rev}: {branches} {desc}\n"
617 comparing with inner
618 comparing with inner
618 searching for changes
619 searching for changes
619 3: A a2
620 3: A a2
620
621
621 $ hg push inner
622 $ hg push inner
622 pushing to inner
623 pushing to inner
623 searching for changes
624 searching for changes
624 adding changesets
625 adding changesets
625 adding manifests
626 adding manifests
626 adding file changes
627 adding file changes
627 added 1 changesets with 1 changes to 1 files
628 added 1 changesets with 1 changes to 1 files
628
629
629 $ cd ..
630 $ cd ..
630
631
631
632
632 Check prepush with new branch head and new child of former branch head
633 Check prepush with new branch head and new child of former branch head
633 but child is on different branch:
634 but child is on different branch:
634
635
635 $ hg init p
636 $ hg init p
636 $ cd p
637 $ cd p
637 $ hg branch A
638 $ hg branch A
638 marked working directory as branch A
639 marked working directory as branch A
639 (branches are permanent and global, did you want a bookmark?)
640 (branches are permanent and global, did you want a bookmark?)
640 $ echo a0 >a
641 $ echo a0 >a
641 $ hg ci -Ama0
642 $ hg ci -Ama0
642 adding a
643 adding a
643 $ echo a1 >a
644 $ echo a1 >a
644 $ hg ci -ma1
645 $ hg ci -ma1
645 $ hg up null
646 $ hg up null
646 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
647 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
647 $ hg branch B
648 $ hg branch B
648 marked working directory as branch B
649 marked working directory as branch B
649 (branches are permanent and global, did you want a bookmark?)
650 (branches are permanent and global, did you want a bookmark?)
650 $ echo b0 >b
651 $ echo b0 >b
651 $ hg ci -Amb0
652 $ hg ci -Amb0
652 adding b
653 adding b
653 $ echo b1 >b
654 $ echo b1 >b
654 $ hg ci -mb1
655 $ hg ci -mb1
655
656
656 $ hg clone . inner
657 $ hg clone . inner
657 updating to branch B
658 updating to branch B
658 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
659 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
659
660
660 $ hg up A
661 $ hg up A
661 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
662 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
662 $ hg branch -f B
663 $ hg branch -f B
663 marked working directory as branch B
664 marked working directory as branch B
664 (branches are permanent and global, did you want a bookmark?)
665 (branches are permanent and global, did you want a bookmark?)
665 $ echo a3 >a
666 $ echo a3 >a
666 $ hg ci -ma3
667 $ hg ci -ma3
667 created new head
668 created new head
668 $ hg up 3
669 $ hg up 3
669 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
670 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
670 $ hg branch -f A
671 $ hg branch -f A
671 marked working directory as branch A
672 marked working directory as branch A
672 (branches are permanent and global, did you want a bookmark?)
673 (branches are permanent and global, did you want a bookmark?)
673 $ echo b3 >b
674 $ echo b3 >b
674 $ hg ci -mb3
675 $ hg ci -mb3
675 created new head
676 created new head
676
677
677 glog of local:
678 glog of local:
678
679
679 $ hg glog --template "{rev}: {branches} {desc}\n"
680 $ hg glog --template "{rev}: {branches} {desc}\n"
680 @ 5: A b3
681 @ 5: A b3
681 |
682 |
682 | o 4: B a3
683 | o 4: B a3
683 | |
684 | |
684 o | 3: B b1
685 o | 3: B b1
685 | |
686 | |
686 o | 2: B b0
687 o | 2: B b0
687 /
688 /
688 o 1: A a1
689 o 1: A a1
689 |
690 |
690 o 0: A a0
691 o 0: A a0
691
692
692 glog of remote:
693 glog of remote:
693
694
694 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
695 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
695 @ 3: B b1
696 @ 3: B b1
696 |
697 |
697 o 2: B b0
698 o 2: B b0
698
699
699 o 1: A a1
700 o 1: A a1
700 |
701 |
701 o 0: A a0
702 o 0: A a0
702
703
703 outgoing:
704 outgoing:
704
705
705 $ hg out inner --template "{rev}: {branches} {desc}\n"
706 $ hg out inner --template "{rev}: {branches} {desc}\n"
706 comparing with inner
707 comparing with inner
707 searching for changes
708 searching for changes
708 4: B a3
709 4: B a3
709 5: A b3
710 5: A b3
710
711
711 $ hg push inner
712 $ hg push inner
712 pushing to inner
713 pushing to inner
713 searching for changes
714 searching for changes
714 abort: push creates new remote head 7d0f4fb6cf04 on branch 'A'!
715 abort: push creates new remote head 7d0f4fb6cf04 on branch 'A'!
715 (did you forget to merge? use push -f to force)
716 (did you forget to merge? use push -f to force)
716 [255]
717 [255]
717
718
718 $ hg push inner -r4 -r5
719 $ hg push inner -r4 -r5
719 pushing to inner
720 pushing to inner
720 searching for changes
721 searching for changes
721 abort: push creates new remote head 7d0f4fb6cf04 on branch 'A'!
722 abort: push creates new remote head 7d0f4fb6cf04 on branch 'A'!
722 (did you forget to merge? use push -f to force)
723 (did you forget to merge? use push -f to force)
723 [255]
724 [255]
724
725
725 $ hg in inner
726 $ hg in inner
726 comparing with inner
727 comparing with inner
727 searching for changes
728 searching for changes
728 no changes found
729 no changes found
729 [1]
730 [1]
730
731
731 $ cd ..
732 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now