##// END OF EJS Templates
destroyed: drop complex branchcache rebuilt logic...
Pierre-Yves David -
r18395:904b7109 default
parent child Browse files
Show More
@@ -1,2578 +1,2571 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 from node import hex, nullid, short
7 from node import hex, nullid, short
8 from i18n import _
8 from i18n import _
9 import peer, changegroup, subrepo, discovery, pushkey, obsolete, repoview
9 import peer, changegroup, subrepo, discovery, pushkey, obsolete, repoview
10 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
10 import changelog, dirstate, filelog, manifest, context, bookmarks, phases
11 import lock, transaction, store, encoding, base85
11 import lock, transaction, store, encoding, base85
12 import scmutil, util, extensions, hook, error, revset
12 import scmutil, util, extensions, hook, error, revset
13 import match as matchmod
13 import match as matchmod
14 import merge as mergemod
14 import merge as mergemod
15 import tags as tagsmod
15 import tags as tagsmod
16 from lock import release
16 from lock import release
17 import weakref, errno, os, time, inspect
17 import weakref, errno, os, time, inspect
18 import branchmap
18 import branchmap
19 propertycache = util.propertycache
19 propertycache = util.propertycache
20 filecache = scmutil.filecache
20 filecache = scmutil.filecache
21
21
22 class repofilecache(filecache):
22 class repofilecache(filecache):
23 """All filecache usage on repo are done for logic that should be unfiltered
23 """All filecache usage on repo are done for logic that should be unfiltered
24 """
24 """
25
25
26 def __get__(self, repo, type=None):
26 def __get__(self, repo, type=None):
27 return super(repofilecache, self).__get__(repo.unfiltered(), type)
27 return super(repofilecache, self).__get__(repo.unfiltered(), type)
28 def __set__(self, repo, value):
28 def __set__(self, repo, value):
29 return super(repofilecache, self).__set__(repo.unfiltered(), value)
29 return super(repofilecache, self).__set__(repo.unfiltered(), value)
30 def __delete__(self, repo):
30 def __delete__(self, repo):
31 return super(repofilecache, self).__delete__(repo.unfiltered())
31 return super(repofilecache, self).__delete__(repo.unfiltered())
32
32
33 class storecache(repofilecache):
33 class storecache(repofilecache):
34 """filecache for files in the store"""
34 """filecache for files in the store"""
35 def join(self, obj, fname):
35 def join(self, obj, fname):
36 return obj.sjoin(fname)
36 return obj.sjoin(fname)
37
37
38 class unfilteredpropertycache(propertycache):
38 class unfilteredpropertycache(propertycache):
39 """propertycache that apply to unfiltered repo only"""
39 """propertycache that apply to unfiltered repo only"""
40
40
41 def __get__(self, repo, type=None):
41 def __get__(self, repo, type=None):
42 return super(unfilteredpropertycache, self).__get__(repo.unfiltered())
42 return super(unfilteredpropertycache, self).__get__(repo.unfiltered())
43
43
44 class filteredpropertycache(propertycache):
44 class filteredpropertycache(propertycache):
45 """propertycache that must take filtering in account"""
45 """propertycache that must take filtering in account"""
46
46
47 def cachevalue(self, obj, value):
47 def cachevalue(self, obj, value):
48 object.__setattr__(obj, self.name, value)
48 object.__setattr__(obj, self.name, value)
49
49
50
50
51 def hasunfilteredcache(repo, name):
51 def hasunfilteredcache(repo, name):
52 """check if an repo and a unfilteredproperty cached value for <name>"""
52 """check if an repo and a unfilteredproperty cached value for <name>"""
53 return name in vars(repo.unfiltered())
53 return name in vars(repo.unfiltered())
54
54
55 def unfilteredmethod(orig):
55 def unfilteredmethod(orig):
56 """decorate method that always need to be run on unfiltered version"""
56 """decorate method that always need to be run on unfiltered version"""
57 def wrapper(repo, *args, **kwargs):
57 def wrapper(repo, *args, **kwargs):
58 return orig(repo.unfiltered(), *args, **kwargs)
58 return orig(repo.unfiltered(), *args, **kwargs)
59 return wrapper
59 return wrapper
60
60
61 MODERNCAPS = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle'))
61 MODERNCAPS = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle'))
62 LEGACYCAPS = MODERNCAPS.union(set(['changegroupsubset']))
62 LEGACYCAPS = MODERNCAPS.union(set(['changegroupsubset']))
63
63
64 class localpeer(peer.peerrepository):
64 class localpeer(peer.peerrepository):
65 '''peer for a local repo; reflects only the most recent API'''
65 '''peer for a local repo; reflects only the most recent API'''
66
66
67 def __init__(self, repo, caps=MODERNCAPS):
67 def __init__(self, repo, caps=MODERNCAPS):
68 peer.peerrepository.__init__(self)
68 peer.peerrepository.__init__(self)
69 self._repo = repo.filtered('served')
69 self._repo = repo.filtered('served')
70 self.ui = repo.ui
70 self.ui = repo.ui
71 self._caps = repo._restrictcapabilities(caps)
71 self._caps = repo._restrictcapabilities(caps)
72 self.requirements = repo.requirements
72 self.requirements = repo.requirements
73 self.supportedformats = repo.supportedformats
73 self.supportedformats = repo.supportedformats
74
74
75 def close(self):
75 def close(self):
76 self._repo.close()
76 self._repo.close()
77
77
78 def _capabilities(self):
78 def _capabilities(self):
79 return self._caps
79 return self._caps
80
80
81 def local(self):
81 def local(self):
82 return self._repo
82 return self._repo
83
83
84 def canpush(self):
84 def canpush(self):
85 return True
85 return True
86
86
87 def url(self):
87 def url(self):
88 return self._repo.url()
88 return self._repo.url()
89
89
90 def lookup(self, key):
90 def lookup(self, key):
91 return self._repo.lookup(key)
91 return self._repo.lookup(key)
92
92
93 def branchmap(self):
93 def branchmap(self):
94 return self._repo.branchmap()
94 return self._repo.branchmap()
95
95
96 def heads(self):
96 def heads(self):
97 return self._repo.heads()
97 return self._repo.heads()
98
98
99 def known(self, nodes):
99 def known(self, nodes):
100 return self._repo.known(nodes)
100 return self._repo.known(nodes)
101
101
102 def getbundle(self, source, heads=None, common=None):
102 def getbundle(self, source, heads=None, common=None):
103 return self._repo.getbundle(source, heads=heads, common=common)
103 return self._repo.getbundle(source, heads=heads, common=common)
104
104
105 # TODO We might want to move the next two calls into legacypeer and add
105 # TODO We might want to move the next two calls into legacypeer and add
106 # unbundle instead.
106 # unbundle instead.
107
107
108 def lock(self):
108 def lock(self):
109 return self._repo.lock()
109 return self._repo.lock()
110
110
111 def addchangegroup(self, cg, source, url):
111 def addchangegroup(self, cg, source, url):
112 return self._repo.addchangegroup(cg, source, url)
112 return self._repo.addchangegroup(cg, source, url)
113
113
114 def pushkey(self, namespace, key, old, new):
114 def pushkey(self, namespace, key, old, new):
115 return self._repo.pushkey(namespace, key, old, new)
115 return self._repo.pushkey(namespace, key, old, new)
116
116
117 def listkeys(self, namespace):
117 def listkeys(self, namespace):
118 return self._repo.listkeys(namespace)
118 return self._repo.listkeys(namespace)
119
119
120 def debugwireargs(self, one, two, three=None, four=None, five=None):
120 def debugwireargs(self, one, two, three=None, four=None, five=None):
121 '''used to test argument passing over the wire'''
121 '''used to test argument passing over the wire'''
122 return "%s %s %s %s %s" % (one, two, three, four, five)
122 return "%s %s %s %s %s" % (one, two, three, four, five)
123
123
124 class locallegacypeer(localpeer):
124 class locallegacypeer(localpeer):
125 '''peer extension which implements legacy methods too; used for tests with
125 '''peer extension which implements legacy methods too; used for tests with
126 restricted capabilities'''
126 restricted capabilities'''
127
127
128 def __init__(self, repo):
128 def __init__(self, repo):
129 localpeer.__init__(self, repo, caps=LEGACYCAPS)
129 localpeer.__init__(self, repo, caps=LEGACYCAPS)
130
130
131 def branches(self, nodes):
131 def branches(self, nodes):
132 return self._repo.branches(nodes)
132 return self._repo.branches(nodes)
133
133
134 def between(self, pairs):
134 def between(self, pairs):
135 return self._repo.between(pairs)
135 return self._repo.between(pairs)
136
136
137 def changegroup(self, basenodes, source):
137 def changegroup(self, basenodes, source):
138 return self._repo.changegroup(basenodes, source)
138 return self._repo.changegroup(basenodes, source)
139
139
140 def changegroupsubset(self, bases, heads, source):
140 def changegroupsubset(self, bases, heads, source):
141 return self._repo.changegroupsubset(bases, heads, source)
141 return self._repo.changegroupsubset(bases, heads, source)
142
142
143 class localrepository(object):
143 class localrepository(object):
144
144
145 supportedformats = set(('revlogv1', 'generaldelta'))
145 supportedformats = set(('revlogv1', 'generaldelta'))
146 supported = supportedformats | set(('store', 'fncache', 'shared',
146 supported = supportedformats | set(('store', 'fncache', 'shared',
147 'dotencode'))
147 'dotencode'))
148 openerreqs = set(('revlogv1', 'generaldelta'))
148 openerreqs = set(('revlogv1', 'generaldelta'))
149 requirements = ['revlogv1']
149 requirements = ['revlogv1']
150 filtername = None
150 filtername = None
151
151
152 def _baserequirements(self, create):
152 def _baserequirements(self, create):
153 return self.requirements[:]
153 return self.requirements[:]
154
154
155 def __init__(self, baseui, path=None, create=False):
155 def __init__(self, baseui, path=None, create=False):
156 self.wvfs = scmutil.vfs(path, expand=True)
156 self.wvfs = scmutil.vfs(path, expand=True)
157 self.wopener = self.wvfs
157 self.wopener = self.wvfs
158 self.root = self.wvfs.base
158 self.root = self.wvfs.base
159 self.path = self.wvfs.join(".hg")
159 self.path = self.wvfs.join(".hg")
160 self.origroot = path
160 self.origroot = path
161 self.auditor = scmutil.pathauditor(self.root, self._checknested)
161 self.auditor = scmutil.pathauditor(self.root, self._checknested)
162 self.vfs = scmutil.vfs(self.path)
162 self.vfs = scmutil.vfs(self.path)
163 self.opener = self.vfs
163 self.opener = self.vfs
164 self.baseui = baseui
164 self.baseui = baseui
165 self.ui = baseui.copy()
165 self.ui = baseui.copy()
166 # A list of callback to shape the phase if no data were found.
166 # A list of callback to shape the phase if no data were found.
167 # Callback are in the form: func(repo, roots) --> processed root.
167 # Callback are in the form: func(repo, roots) --> processed root.
168 # This list it to be filled by extension during repo setup
168 # This list it to be filled by extension during repo setup
169 self._phasedefaults = []
169 self._phasedefaults = []
170 try:
170 try:
171 self.ui.readconfig(self.join("hgrc"), self.root)
171 self.ui.readconfig(self.join("hgrc"), self.root)
172 extensions.loadall(self.ui)
172 extensions.loadall(self.ui)
173 except IOError:
173 except IOError:
174 pass
174 pass
175
175
176 if not self.vfs.isdir():
176 if not self.vfs.isdir():
177 if create:
177 if create:
178 if not self.wvfs.exists():
178 if not self.wvfs.exists():
179 self.wvfs.makedirs()
179 self.wvfs.makedirs()
180 self.vfs.makedir(notindexed=True)
180 self.vfs.makedir(notindexed=True)
181 requirements = self._baserequirements(create)
181 requirements = self._baserequirements(create)
182 if self.ui.configbool('format', 'usestore', True):
182 if self.ui.configbool('format', 'usestore', True):
183 self.vfs.mkdir("store")
183 self.vfs.mkdir("store")
184 requirements.append("store")
184 requirements.append("store")
185 if self.ui.configbool('format', 'usefncache', True):
185 if self.ui.configbool('format', 'usefncache', True):
186 requirements.append("fncache")
186 requirements.append("fncache")
187 if self.ui.configbool('format', 'dotencode', True):
187 if self.ui.configbool('format', 'dotencode', True):
188 requirements.append('dotencode')
188 requirements.append('dotencode')
189 # create an invalid changelog
189 # create an invalid changelog
190 self.vfs.append(
190 self.vfs.append(
191 "00changelog.i",
191 "00changelog.i",
192 '\0\0\0\2' # represents revlogv2
192 '\0\0\0\2' # represents revlogv2
193 ' dummy changelog to prevent using the old repo layout'
193 ' dummy changelog to prevent using the old repo layout'
194 )
194 )
195 if self.ui.configbool('format', 'generaldelta', False):
195 if self.ui.configbool('format', 'generaldelta', False):
196 requirements.append("generaldelta")
196 requirements.append("generaldelta")
197 requirements = set(requirements)
197 requirements = set(requirements)
198 else:
198 else:
199 raise error.RepoError(_("repository %s not found") % path)
199 raise error.RepoError(_("repository %s not found") % path)
200 elif create:
200 elif create:
201 raise error.RepoError(_("repository %s already exists") % path)
201 raise error.RepoError(_("repository %s already exists") % path)
202 else:
202 else:
203 try:
203 try:
204 requirements = scmutil.readrequires(self.vfs, self.supported)
204 requirements = scmutil.readrequires(self.vfs, self.supported)
205 except IOError, inst:
205 except IOError, inst:
206 if inst.errno != errno.ENOENT:
206 if inst.errno != errno.ENOENT:
207 raise
207 raise
208 requirements = set()
208 requirements = set()
209
209
210 self.sharedpath = self.path
210 self.sharedpath = self.path
211 try:
211 try:
212 s = os.path.realpath(self.opener.read("sharedpath").rstrip('\n'))
212 s = os.path.realpath(self.opener.read("sharedpath").rstrip('\n'))
213 if not os.path.exists(s):
213 if not os.path.exists(s):
214 raise error.RepoError(
214 raise error.RepoError(
215 _('.hg/sharedpath points to nonexistent directory %s') % s)
215 _('.hg/sharedpath points to nonexistent directory %s') % s)
216 self.sharedpath = s
216 self.sharedpath = s
217 except IOError, inst:
217 except IOError, inst:
218 if inst.errno != errno.ENOENT:
218 if inst.errno != errno.ENOENT:
219 raise
219 raise
220
220
221 self.store = store.store(requirements, self.sharedpath, scmutil.vfs)
221 self.store = store.store(requirements, self.sharedpath, scmutil.vfs)
222 self.spath = self.store.path
222 self.spath = self.store.path
223 self.svfs = self.store.vfs
223 self.svfs = self.store.vfs
224 self.sopener = self.svfs
224 self.sopener = self.svfs
225 self.sjoin = self.store.join
225 self.sjoin = self.store.join
226 self.vfs.createmode = self.store.createmode
226 self.vfs.createmode = self.store.createmode
227 self._applyrequirements(requirements)
227 self._applyrequirements(requirements)
228 if create:
228 if create:
229 self._writerequirements()
229 self._writerequirements()
230
230
231
231
232 self._branchcaches = {}
232 self._branchcaches = {}
233 self.filterpats = {}
233 self.filterpats = {}
234 self._datafilters = {}
234 self._datafilters = {}
235 self._transref = self._lockref = self._wlockref = None
235 self._transref = self._lockref = self._wlockref = None
236
236
237 # A cache for various files under .hg/ that tracks file changes,
237 # A cache for various files under .hg/ that tracks file changes,
238 # (used by the filecache decorator)
238 # (used by the filecache decorator)
239 #
239 #
240 # Maps a property name to its util.filecacheentry
240 # Maps a property name to its util.filecacheentry
241 self._filecache = {}
241 self._filecache = {}
242
242
243 # hold sets of revision to be filtered
243 # hold sets of revision to be filtered
244 # should be cleared when something might have changed the filter value:
244 # should be cleared when something might have changed the filter value:
245 # - new changesets,
245 # - new changesets,
246 # - phase change,
246 # - phase change,
247 # - new obsolescence marker,
247 # - new obsolescence marker,
248 # - working directory parent change,
248 # - working directory parent change,
249 # - bookmark changes
249 # - bookmark changes
250 self.filteredrevcache = {}
250 self.filteredrevcache = {}
251
251
252 def close(self):
252 def close(self):
253 pass
253 pass
254
254
255 def _restrictcapabilities(self, caps):
255 def _restrictcapabilities(self, caps):
256 return caps
256 return caps
257
257
258 def _applyrequirements(self, requirements):
258 def _applyrequirements(self, requirements):
259 self.requirements = requirements
259 self.requirements = requirements
260 self.sopener.options = dict((r, 1) for r in requirements
260 self.sopener.options = dict((r, 1) for r in requirements
261 if r in self.openerreqs)
261 if r in self.openerreqs)
262
262
263 def _writerequirements(self):
263 def _writerequirements(self):
264 reqfile = self.opener("requires", "w")
264 reqfile = self.opener("requires", "w")
265 for r in sorted(self.requirements):
265 for r in sorted(self.requirements):
266 reqfile.write("%s\n" % r)
266 reqfile.write("%s\n" % r)
267 reqfile.close()
267 reqfile.close()
268
268
269 def _checknested(self, path):
269 def _checknested(self, path):
270 """Determine if path is a legal nested repository."""
270 """Determine if path is a legal nested repository."""
271 if not path.startswith(self.root):
271 if not path.startswith(self.root):
272 return False
272 return False
273 subpath = path[len(self.root) + 1:]
273 subpath = path[len(self.root) + 1:]
274 normsubpath = util.pconvert(subpath)
274 normsubpath = util.pconvert(subpath)
275
275
276 # XXX: Checking against the current working copy is wrong in
276 # XXX: Checking against the current working copy is wrong in
277 # the sense that it can reject things like
277 # the sense that it can reject things like
278 #
278 #
279 # $ hg cat -r 10 sub/x.txt
279 # $ hg cat -r 10 sub/x.txt
280 #
280 #
281 # if sub/ is no longer a subrepository in the working copy
281 # if sub/ is no longer a subrepository in the working copy
282 # parent revision.
282 # parent revision.
283 #
283 #
284 # However, it can of course also allow things that would have
284 # However, it can of course also allow things that would have
285 # been rejected before, such as the above cat command if sub/
285 # been rejected before, such as the above cat command if sub/
286 # is a subrepository now, but was a normal directory before.
286 # is a subrepository now, but was a normal directory before.
287 # The old path auditor would have rejected by mistake since it
287 # The old path auditor would have rejected by mistake since it
288 # panics when it sees sub/.hg/.
288 # panics when it sees sub/.hg/.
289 #
289 #
290 # All in all, checking against the working copy seems sensible
290 # All in all, checking against the working copy seems sensible
291 # since we want to prevent access to nested repositories on
291 # since we want to prevent access to nested repositories on
292 # the filesystem *now*.
292 # the filesystem *now*.
293 ctx = self[None]
293 ctx = self[None]
294 parts = util.splitpath(subpath)
294 parts = util.splitpath(subpath)
295 while parts:
295 while parts:
296 prefix = '/'.join(parts)
296 prefix = '/'.join(parts)
297 if prefix in ctx.substate:
297 if prefix in ctx.substate:
298 if prefix == normsubpath:
298 if prefix == normsubpath:
299 return True
299 return True
300 else:
300 else:
301 sub = ctx.sub(prefix)
301 sub = ctx.sub(prefix)
302 return sub.checknested(subpath[len(prefix) + 1:])
302 return sub.checknested(subpath[len(prefix) + 1:])
303 else:
303 else:
304 parts.pop()
304 parts.pop()
305 return False
305 return False
306
306
307 def peer(self):
307 def peer(self):
308 return localpeer(self) # not cached to avoid reference cycle
308 return localpeer(self) # not cached to avoid reference cycle
309
309
310 def unfiltered(self):
310 def unfiltered(self):
311 """Return unfiltered version of the repository
311 """Return unfiltered version of the repository
312
312
313 Intended to be ovewritten by filtered repo."""
313 Intended to be ovewritten by filtered repo."""
314 return self
314 return self
315
315
316 def filtered(self, name):
316 def filtered(self, name):
317 """Return a filtered version of a repository"""
317 """Return a filtered version of a repository"""
318 # build a new class with the mixin and the current class
318 # build a new class with the mixin and the current class
319 # (possibily subclass of the repo)
319 # (possibily subclass of the repo)
320 class proxycls(repoview.repoview, self.unfiltered().__class__):
320 class proxycls(repoview.repoview, self.unfiltered().__class__):
321 pass
321 pass
322 return proxycls(self, name)
322 return proxycls(self, name)
323
323
324 @repofilecache('bookmarks')
324 @repofilecache('bookmarks')
325 def _bookmarks(self):
325 def _bookmarks(self):
326 return bookmarks.bmstore(self)
326 return bookmarks.bmstore(self)
327
327
328 @repofilecache('bookmarks.current')
328 @repofilecache('bookmarks.current')
329 def _bookmarkcurrent(self):
329 def _bookmarkcurrent(self):
330 return bookmarks.readcurrent(self)
330 return bookmarks.readcurrent(self)
331
331
332 def bookmarkheads(self, bookmark):
332 def bookmarkheads(self, bookmark):
333 name = bookmark.split('@', 1)[0]
333 name = bookmark.split('@', 1)[0]
334 heads = []
334 heads = []
335 for mark, n in self._bookmarks.iteritems():
335 for mark, n in self._bookmarks.iteritems():
336 if mark.split('@', 1)[0] == name:
336 if mark.split('@', 1)[0] == name:
337 heads.append(n)
337 heads.append(n)
338 return heads
338 return heads
339
339
340 @storecache('phaseroots')
340 @storecache('phaseroots')
341 def _phasecache(self):
341 def _phasecache(self):
342 return phases.phasecache(self, self._phasedefaults)
342 return phases.phasecache(self, self._phasedefaults)
343
343
344 @storecache('obsstore')
344 @storecache('obsstore')
345 def obsstore(self):
345 def obsstore(self):
346 store = obsolete.obsstore(self.sopener)
346 store = obsolete.obsstore(self.sopener)
347 if store and not obsolete._enabled:
347 if store and not obsolete._enabled:
348 # message is rare enough to not be translated
348 # message is rare enough to not be translated
349 msg = 'obsolete feature not enabled but %i markers found!\n'
349 msg = 'obsolete feature not enabled but %i markers found!\n'
350 self.ui.warn(msg % len(list(store)))
350 self.ui.warn(msg % len(list(store)))
351 return store
351 return store
352
352
353 @storecache('00changelog.i')
353 @storecache('00changelog.i')
354 def changelog(self):
354 def changelog(self):
355 c = changelog.changelog(self.sopener)
355 c = changelog.changelog(self.sopener)
356 if 'HG_PENDING' in os.environ:
356 if 'HG_PENDING' in os.environ:
357 p = os.environ['HG_PENDING']
357 p = os.environ['HG_PENDING']
358 if p.startswith(self.root):
358 if p.startswith(self.root):
359 c.readpending('00changelog.i.a')
359 c.readpending('00changelog.i.a')
360 return c
360 return c
361
361
362 @storecache('00manifest.i')
362 @storecache('00manifest.i')
363 def manifest(self):
363 def manifest(self):
364 return manifest.manifest(self.sopener)
364 return manifest.manifest(self.sopener)
365
365
366 @repofilecache('dirstate')
366 @repofilecache('dirstate')
367 def dirstate(self):
367 def dirstate(self):
368 warned = [0]
368 warned = [0]
369 def validate(node):
369 def validate(node):
370 try:
370 try:
371 self.changelog.rev(node)
371 self.changelog.rev(node)
372 return node
372 return node
373 except error.LookupError:
373 except error.LookupError:
374 if not warned[0]:
374 if not warned[0]:
375 warned[0] = True
375 warned[0] = True
376 self.ui.warn(_("warning: ignoring unknown"
376 self.ui.warn(_("warning: ignoring unknown"
377 " working parent %s!\n") % short(node))
377 " working parent %s!\n") % short(node))
378 return nullid
378 return nullid
379
379
380 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
380 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
381
381
382 def __getitem__(self, changeid):
382 def __getitem__(self, changeid):
383 if changeid is None:
383 if changeid is None:
384 return context.workingctx(self)
384 return context.workingctx(self)
385 return context.changectx(self, changeid)
385 return context.changectx(self, changeid)
386
386
387 def __contains__(self, changeid):
387 def __contains__(self, changeid):
388 try:
388 try:
389 return bool(self.lookup(changeid))
389 return bool(self.lookup(changeid))
390 except error.RepoLookupError:
390 except error.RepoLookupError:
391 return False
391 return False
392
392
393 def __nonzero__(self):
393 def __nonzero__(self):
394 return True
394 return True
395
395
396 def __len__(self):
396 def __len__(self):
397 return len(self.changelog)
397 return len(self.changelog)
398
398
399 def __iter__(self):
399 def __iter__(self):
400 return iter(self.changelog)
400 return iter(self.changelog)
401
401
402 def revs(self, expr, *args):
402 def revs(self, expr, *args):
403 '''Return a list of revisions matching the given revset'''
403 '''Return a list of revisions matching the given revset'''
404 expr = revset.formatspec(expr, *args)
404 expr = revset.formatspec(expr, *args)
405 m = revset.match(None, expr)
405 m = revset.match(None, expr)
406 return [r for r in m(self, list(self))]
406 return [r for r in m(self, list(self))]
407
407
408 def set(self, expr, *args):
408 def set(self, expr, *args):
409 '''
409 '''
410 Yield a context for each matching revision, after doing arg
410 Yield a context for each matching revision, after doing arg
411 replacement via revset.formatspec
411 replacement via revset.formatspec
412 '''
412 '''
413 for r in self.revs(expr, *args):
413 for r in self.revs(expr, *args):
414 yield self[r]
414 yield self[r]
415
415
416 def url(self):
416 def url(self):
417 return 'file:' + self.root
417 return 'file:' + self.root
418
418
419 def hook(self, name, throw=False, **args):
419 def hook(self, name, throw=False, **args):
420 return hook.hook(self.ui, self, name, throw, **args)
420 return hook.hook(self.ui, self, name, throw, **args)
421
421
422 @unfilteredmethod
422 @unfilteredmethod
423 def _tag(self, names, node, message, local, user, date, extra={}):
423 def _tag(self, names, node, message, local, user, date, extra={}):
424 if isinstance(names, str):
424 if isinstance(names, str):
425 names = (names,)
425 names = (names,)
426
426
427 branches = self.branchmap()
427 branches = self.branchmap()
428 for name in names:
428 for name in names:
429 self.hook('pretag', throw=True, node=hex(node), tag=name,
429 self.hook('pretag', throw=True, node=hex(node), tag=name,
430 local=local)
430 local=local)
431 if name in branches:
431 if name in branches:
432 self.ui.warn(_("warning: tag %s conflicts with existing"
432 self.ui.warn(_("warning: tag %s conflicts with existing"
433 " branch name\n") % name)
433 " branch name\n") % name)
434
434
435 def writetags(fp, names, munge, prevtags):
435 def writetags(fp, names, munge, prevtags):
436 fp.seek(0, 2)
436 fp.seek(0, 2)
437 if prevtags and prevtags[-1] != '\n':
437 if prevtags and prevtags[-1] != '\n':
438 fp.write('\n')
438 fp.write('\n')
439 for name in names:
439 for name in names:
440 m = munge and munge(name) or name
440 m = munge and munge(name) or name
441 if (self._tagscache.tagtypes and
441 if (self._tagscache.tagtypes and
442 name in self._tagscache.tagtypes):
442 name in self._tagscache.tagtypes):
443 old = self.tags().get(name, nullid)
443 old = self.tags().get(name, nullid)
444 fp.write('%s %s\n' % (hex(old), m))
444 fp.write('%s %s\n' % (hex(old), m))
445 fp.write('%s %s\n' % (hex(node), m))
445 fp.write('%s %s\n' % (hex(node), m))
446 fp.close()
446 fp.close()
447
447
448 prevtags = ''
448 prevtags = ''
449 if local:
449 if local:
450 try:
450 try:
451 fp = self.opener('localtags', 'r+')
451 fp = self.opener('localtags', 'r+')
452 except IOError:
452 except IOError:
453 fp = self.opener('localtags', 'a')
453 fp = self.opener('localtags', 'a')
454 else:
454 else:
455 prevtags = fp.read()
455 prevtags = fp.read()
456
456
457 # local tags are stored in the current charset
457 # local tags are stored in the current charset
458 writetags(fp, names, None, prevtags)
458 writetags(fp, names, None, prevtags)
459 for name in names:
459 for name in names:
460 self.hook('tag', node=hex(node), tag=name, local=local)
460 self.hook('tag', node=hex(node), tag=name, local=local)
461 return
461 return
462
462
463 try:
463 try:
464 fp = self.wfile('.hgtags', 'rb+')
464 fp = self.wfile('.hgtags', 'rb+')
465 except IOError, e:
465 except IOError, e:
466 if e.errno != errno.ENOENT:
466 if e.errno != errno.ENOENT:
467 raise
467 raise
468 fp = self.wfile('.hgtags', 'ab')
468 fp = self.wfile('.hgtags', 'ab')
469 else:
469 else:
470 prevtags = fp.read()
470 prevtags = fp.read()
471
471
472 # committed tags are stored in UTF-8
472 # committed tags are stored in UTF-8
473 writetags(fp, names, encoding.fromlocal, prevtags)
473 writetags(fp, names, encoding.fromlocal, prevtags)
474
474
475 fp.close()
475 fp.close()
476
476
477 self.invalidatecaches()
477 self.invalidatecaches()
478
478
479 if '.hgtags' not in self.dirstate:
479 if '.hgtags' not in self.dirstate:
480 self[None].add(['.hgtags'])
480 self[None].add(['.hgtags'])
481
481
482 m = matchmod.exact(self.root, '', ['.hgtags'])
482 m = matchmod.exact(self.root, '', ['.hgtags'])
483 tagnode = self.commit(message, user, date, extra=extra, match=m)
483 tagnode = self.commit(message, user, date, extra=extra, match=m)
484
484
485 for name in names:
485 for name in names:
486 self.hook('tag', node=hex(node), tag=name, local=local)
486 self.hook('tag', node=hex(node), tag=name, local=local)
487
487
488 return tagnode
488 return tagnode
489
489
490 def tag(self, names, node, message, local, user, date):
490 def tag(self, names, node, message, local, user, date):
491 '''tag a revision with one or more symbolic names.
491 '''tag a revision with one or more symbolic names.
492
492
493 names is a list of strings or, when adding a single tag, names may be a
493 names is a list of strings or, when adding a single tag, names may be a
494 string.
494 string.
495
495
496 if local is True, the tags are stored in a per-repository file.
496 if local is True, the tags are stored in a per-repository file.
497 otherwise, they are stored in the .hgtags file, and a new
497 otherwise, they are stored in the .hgtags file, and a new
498 changeset is committed with the change.
498 changeset is committed with the change.
499
499
500 keyword arguments:
500 keyword arguments:
501
501
502 local: whether to store tags in non-version-controlled file
502 local: whether to store tags in non-version-controlled file
503 (default False)
503 (default False)
504
504
505 message: commit message to use if committing
505 message: commit message to use if committing
506
506
507 user: name of user to use if committing
507 user: name of user to use if committing
508
508
509 date: date tuple to use if committing'''
509 date: date tuple to use if committing'''
510
510
511 if not local:
511 if not local:
512 for x in self.status()[:5]:
512 for x in self.status()[:5]:
513 if '.hgtags' in x:
513 if '.hgtags' in x:
514 raise util.Abort(_('working copy of .hgtags is changed '
514 raise util.Abort(_('working copy of .hgtags is changed '
515 '(please commit .hgtags manually)'))
515 '(please commit .hgtags manually)'))
516
516
517 self.tags() # instantiate the cache
517 self.tags() # instantiate the cache
518 self._tag(names, node, message, local, user, date)
518 self._tag(names, node, message, local, user, date)
519
519
520 @filteredpropertycache
520 @filteredpropertycache
521 def _tagscache(self):
521 def _tagscache(self):
522 '''Returns a tagscache object that contains various tags related
522 '''Returns a tagscache object that contains various tags related
523 caches.'''
523 caches.'''
524
524
525 # This simplifies its cache management by having one decorated
525 # This simplifies its cache management by having one decorated
526 # function (this one) and the rest simply fetch things from it.
526 # function (this one) and the rest simply fetch things from it.
527 class tagscache(object):
527 class tagscache(object):
528 def __init__(self):
528 def __init__(self):
529 # These two define the set of tags for this repository. tags
529 # These two define the set of tags for this repository. tags
530 # maps tag name to node; tagtypes maps tag name to 'global' or
530 # maps tag name to node; tagtypes maps tag name to 'global' or
531 # 'local'. (Global tags are defined by .hgtags across all
531 # 'local'. (Global tags are defined by .hgtags across all
532 # heads, and local tags are defined in .hg/localtags.)
532 # heads, and local tags are defined in .hg/localtags.)
533 # They constitute the in-memory cache of tags.
533 # They constitute the in-memory cache of tags.
534 self.tags = self.tagtypes = None
534 self.tags = self.tagtypes = None
535
535
536 self.nodetagscache = self.tagslist = None
536 self.nodetagscache = self.tagslist = None
537
537
538 cache = tagscache()
538 cache = tagscache()
539 cache.tags, cache.tagtypes = self._findtags()
539 cache.tags, cache.tagtypes = self._findtags()
540
540
541 return cache
541 return cache
542
542
543 def tags(self):
543 def tags(self):
544 '''return a mapping of tag to node'''
544 '''return a mapping of tag to node'''
545 t = {}
545 t = {}
546 if self.changelog.filteredrevs:
546 if self.changelog.filteredrevs:
547 tags, tt = self._findtags()
547 tags, tt = self._findtags()
548 else:
548 else:
549 tags = self._tagscache.tags
549 tags = self._tagscache.tags
550 for k, v in tags.iteritems():
550 for k, v in tags.iteritems():
551 try:
551 try:
552 # ignore tags to unknown nodes
552 # ignore tags to unknown nodes
553 self.changelog.rev(v)
553 self.changelog.rev(v)
554 t[k] = v
554 t[k] = v
555 except (error.LookupError, ValueError):
555 except (error.LookupError, ValueError):
556 pass
556 pass
557 return t
557 return t
558
558
559 def _findtags(self):
559 def _findtags(self):
560 '''Do the hard work of finding tags. Return a pair of dicts
560 '''Do the hard work of finding tags. Return a pair of dicts
561 (tags, tagtypes) where tags maps tag name to node, and tagtypes
561 (tags, tagtypes) where tags maps tag name to node, and tagtypes
562 maps tag name to a string like \'global\' or \'local\'.
562 maps tag name to a string like \'global\' or \'local\'.
563 Subclasses or extensions are free to add their own tags, but
563 Subclasses or extensions are free to add their own tags, but
564 should be aware that the returned dicts will be retained for the
564 should be aware that the returned dicts will be retained for the
565 duration of the localrepo object.'''
565 duration of the localrepo object.'''
566
566
567 # XXX what tagtype should subclasses/extensions use? Currently
567 # XXX what tagtype should subclasses/extensions use? Currently
568 # mq and bookmarks add tags, but do not set the tagtype at all.
568 # mq and bookmarks add tags, but do not set the tagtype at all.
569 # Should each extension invent its own tag type? Should there
569 # Should each extension invent its own tag type? Should there
570 # be one tagtype for all such "virtual" tags? Or is the status
570 # be one tagtype for all such "virtual" tags? Or is the status
571 # quo fine?
571 # quo fine?
572
572
573 alltags = {} # map tag name to (node, hist)
573 alltags = {} # map tag name to (node, hist)
574 tagtypes = {}
574 tagtypes = {}
575
575
576 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
576 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
577 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
577 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
578
578
579 # Build the return dicts. Have to re-encode tag names because
579 # Build the return dicts. Have to re-encode tag names because
580 # the tags module always uses UTF-8 (in order not to lose info
580 # the tags module always uses UTF-8 (in order not to lose info
581 # writing to the cache), but the rest of Mercurial wants them in
581 # writing to the cache), but the rest of Mercurial wants them in
582 # local encoding.
582 # local encoding.
583 tags = {}
583 tags = {}
584 for (name, (node, hist)) in alltags.iteritems():
584 for (name, (node, hist)) in alltags.iteritems():
585 if node != nullid:
585 if node != nullid:
586 tags[encoding.tolocal(name)] = node
586 tags[encoding.tolocal(name)] = node
587 tags['tip'] = self.changelog.tip()
587 tags['tip'] = self.changelog.tip()
588 tagtypes = dict([(encoding.tolocal(name), value)
588 tagtypes = dict([(encoding.tolocal(name), value)
589 for (name, value) in tagtypes.iteritems()])
589 for (name, value) in tagtypes.iteritems()])
590 return (tags, tagtypes)
590 return (tags, tagtypes)
591
591
592 def tagtype(self, tagname):
592 def tagtype(self, tagname):
593 '''
593 '''
594 return the type of the given tag. result can be:
594 return the type of the given tag. result can be:
595
595
596 'local' : a local tag
596 'local' : a local tag
597 'global' : a global tag
597 'global' : a global tag
598 None : tag does not exist
598 None : tag does not exist
599 '''
599 '''
600
600
601 return self._tagscache.tagtypes.get(tagname)
601 return self._tagscache.tagtypes.get(tagname)
602
602
603 def tagslist(self):
603 def tagslist(self):
604 '''return a list of tags ordered by revision'''
604 '''return a list of tags ordered by revision'''
605 if not self._tagscache.tagslist:
605 if not self._tagscache.tagslist:
606 l = []
606 l = []
607 for t, n in self.tags().iteritems():
607 for t, n in self.tags().iteritems():
608 r = self.changelog.rev(n)
608 r = self.changelog.rev(n)
609 l.append((r, t, n))
609 l.append((r, t, n))
610 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
610 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
611
611
612 return self._tagscache.tagslist
612 return self._tagscache.tagslist
613
613
614 def nodetags(self, node):
614 def nodetags(self, node):
615 '''return the tags associated with a node'''
615 '''return the tags associated with a node'''
616 if not self._tagscache.nodetagscache:
616 if not self._tagscache.nodetagscache:
617 nodetagscache = {}
617 nodetagscache = {}
618 for t, n in self._tagscache.tags.iteritems():
618 for t, n in self._tagscache.tags.iteritems():
619 nodetagscache.setdefault(n, []).append(t)
619 nodetagscache.setdefault(n, []).append(t)
620 for tags in nodetagscache.itervalues():
620 for tags in nodetagscache.itervalues():
621 tags.sort()
621 tags.sort()
622 self._tagscache.nodetagscache = nodetagscache
622 self._tagscache.nodetagscache = nodetagscache
623 return self._tagscache.nodetagscache.get(node, [])
623 return self._tagscache.nodetagscache.get(node, [])
624
624
625 def nodebookmarks(self, node):
625 def nodebookmarks(self, node):
626 marks = []
626 marks = []
627 for bookmark, n in self._bookmarks.iteritems():
627 for bookmark, n in self._bookmarks.iteritems():
628 if n == node:
628 if n == node:
629 marks.append(bookmark)
629 marks.append(bookmark)
630 return sorted(marks)
630 return sorted(marks)
631
631
632 def branchmap(self):
632 def branchmap(self):
633 '''returns a dictionary {branch: [branchheads]}'''
633 '''returns a dictionary {branch: [branchheads]}'''
634 branchmap.updatecache(self)
634 branchmap.updatecache(self)
635 return self._branchcaches[self.filtername]
635 return self._branchcaches[self.filtername]
636
636
637
637
638 def _branchtip(self, heads):
638 def _branchtip(self, heads):
639 '''return the tipmost branch head in heads'''
639 '''return the tipmost branch head in heads'''
640 tip = heads[-1]
640 tip = heads[-1]
641 for h in reversed(heads):
641 for h in reversed(heads):
642 if not self[h].closesbranch():
642 if not self[h].closesbranch():
643 tip = h
643 tip = h
644 break
644 break
645 return tip
645 return tip
646
646
647 def branchtip(self, branch):
647 def branchtip(self, branch):
648 '''return the tip node for a given branch'''
648 '''return the tip node for a given branch'''
649 if branch not in self.branchmap():
649 if branch not in self.branchmap():
650 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
650 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
651 return self._branchtip(self.branchmap()[branch])
651 return self._branchtip(self.branchmap()[branch])
652
652
653 def branchtags(self):
653 def branchtags(self):
654 '''return a dict where branch names map to the tipmost head of
654 '''return a dict where branch names map to the tipmost head of
655 the branch, open heads come before closed'''
655 the branch, open heads come before closed'''
656 bt = {}
656 bt = {}
657 for bn, heads in self.branchmap().iteritems():
657 for bn, heads in self.branchmap().iteritems():
658 bt[bn] = self._branchtip(heads)
658 bt[bn] = self._branchtip(heads)
659 return bt
659 return bt
660
660
661 def lookup(self, key):
661 def lookup(self, key):
662 return self[key].node()
662 return self[key].node()
663
663
664 def lookupbranch(self, key, remote=None):
664 def lookupbranch(self, key, remote=None):
665 repo = remote or self
665 repo = remote or self
666 if key in repo.branchmap():
666 if key in repo.branchmap():
667 return key
667 return key
668
668
669 repo = (remote and remote.local()) and remote or self
669 repo = (remote and remote.local()) and remote or self
670 return repo[key].branch()
670 return repo[key].branch()
671
671
672 def known(self, nodes):
672 def known(self, nodes):
673 nm = self.changelog.nodemap
673 nm = self.changelog.nodemap
674 pc = self._phasecache
674 pc = self._phasecache
675 result = []
675 result = []
676 for n in nodes:
676 for n in nodes:
677 r = nm.get(n)
677 r = nm.get(n)
678 resp = not (r is None or pc.phase(self, r) >= phases.secret)
678 resp = not (r is None or pc.phase(self, r) >= phases.secret)
679 result.append(resp)
679 result.append(resp)
680 return result
680 return result
681
681
682 def local(self):
682 def local(self):
683 return self
683 return self
684
684
685 def cancopy(self):
685 def cancopy(self):
686 return self.local() # so statichttprepo's override of local() works
686 return self.local() # so statichttprepo's override of local() works
687
687
688 def join(self, f):
688 def join(self, f):
689 return os.path.join(self.path, f)
689 return os.path.join(self.path, f)
690
690
691 def wjoin(self, f):
691 def wjoin(self, f):
692 return os.path.join(self.root, f)
692 return os.path.join(self.root, f)
693
693
694 def file(self, f):
694 def file(self, f):
695 if f[0] == '/':
695 if f[0] == '/':
696 f = f[1:]
696 f = f[1:]
697 return filelog.filelog(self.sopener, f)
697 return filelog.filelog(self.sopener, f)
698
698
699 def changectx(self, changeid):
699 def changectx(self, changeid):
700 return self[changeid]
700 return self[changeid]
701
701
702 def parents(self, changeid=None):
702 def parents(self, changeid=None):
703 '''get list of changectxs for parents of changeid'''
703 '''get list of changectxs for parents of changeid'''
704 return self[changeid].parents()
704 return self[changeid].parents()
705
705
706 def setparents(self, p1, p2=nullid):
706 def setparents(self, p1, p2=nullid):
707 copies = self.dirstate.setparents(p1, p2)
707 copies = self.dirstate.setparents(p1, p2)
708 if copies:
708 if copies:
709 # Adjust copy records, the dirstate cannot do it, it
709 # Adjust copy records, the dirstate cannot do it, it
710 # requires access to parents manifests. Preserve them
710 # requires access to parents manifests. Preserve them
711 # only for entries added to first parent.
711 # only for entries added to first parent.
712 pctx = self[p1]
712 pctx = self[p1]
713 for f in copies:
713 for f in copies:
714 if f not in pctx and copies[f] in pctx:
714 if f not in pctx and copies[f] in pctx:
715 self.dirstate.copy(copies[f], f)
715 self.dirstate.copy(copies[f], f)
716
716
717 def filectx(self, path, changeid=None, fileid=None):
717 def filectx(self, path, changeid=None, fileid=None):
718 """changeid can be a changeset revision, node, or tag.
718 """changeid can be a changeset revision, node, or tag.
719 fileid can be a file revision or node."""
719 fileid can be a file revision or node."""
720 return context.filectx(self, path, changeid, fileid)
720 return context.filectx(self, path, changeid, fileid)
721
721
722 def getcwd(self):
722 def getcwd(self):
723 return self.dirstate.getcwd()
723 return self.dirstate.getcwd()
724
724
725 def pathto(self, f, cwd=None):
725 def pathto(self, f, cwd=None):
726 return self.dirstate.pathto(f, cwd)
726 return self.dirstate.pathto(f, cwd)
727
727
728 def wfile(self, f, mode='r'):
728 def wfile(self, f, mode='r'):
729 return self.wopener(f, mode)
729 return self.wopener(f, mode)
730
730
731 def _link(self, f):
731 def _link(self, f):
732 return os.path.islink(self.wjoin(f))
732 return os.path.islink(self.wjoin(f))
733
733
734 def _loadfilter(self, filter):
734 def _loadfilter(self, filter):
735 if filter not in self.filterpats:
735 if filter not in self.filterpats:
736 l = []
736 l = []
737 for pat, cmd in self.ui.configitems(filter):
737 for pat, cmd in self.ui.configitems(filter):
738 if cmd == '!':
738 if cmd == '!':
739 continue
739 continue
740 mf = matchmod.match(self.root, '', [pat])
740 mf = matchmod.match(self.root, '', [pat])
741 fn = None
741 fn = None
742 params = cmd
742 params = cmd
743 for name, filterfn in self._datafilters.iteritems():
743 for name, filterfn in self._datafilters.iteritems():
744 if cmd.startswith(name):
744 if cmd.startswith(name):
745 fn = filterfn
745 fn = filterfn
746 params = cmd[len(name):].lstrip()
746 params = cmd[len(name):].lstrip()
747 break
747 break
748 if not fn:
748 if not fn:
749 fn = lambda s, c, **kwargs: util.filter(s, c)
749 fn = lambda s, c, **kwargs: util.filter(s, c)
750 # Wrap old filters not supporting keyword arguments
750 # Wrap old filters not supporting keyword arguments
751 if not inspect.getargspec(fn)[2]:
751 if not inspect.getargspec(fn)[2]:
752 oldfn = fn
752 oldfn = fn
753 fn = lambda s, c, **kwargs: oldfn(s, c)
753 fn = lambda s, c, **kwargs: oldfn(s, c)
754 l.append((mf, fn, params))
754 l.append((mf, fn, params))
755 self.filterpats[filter] = l
755 self.filterpats[filter] = l
756 return self.filterpats[filter]
756 return self.filterpats[filter]
757
757
758 def _filter(self, filterpats, filename, data):
758 def _filter(self, filterpats, filename, data):
759 for mf, fn, cmd in filterpats:
759 for mf, fn, cmd in filterpats:
760 if mf(filename):
760 if mf(filename):
761 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
761 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
762 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
762 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
763 break
763 break
764
764
765 return data
765 return data
766
766
767 @unfilteredpropertycache
767 @unfilteredpropertycache
768 def _encodefilterpats(self):
768 def _encodefilterpats(self):
769 return self._loadfilter('encode')
769 return self._loadfilter('encode')
770
770
771 @unfilteredpropertycache
771 @unfilteredpropertycache
772 def _decodefilterpats(self):
772 def _decodefilterpats(self):
773 return self._loadfilter('decode')
773 return self._loadfilter('decode')
774
774
775 def adddatafilter(self, name, filter):
775 def adddatafilter(self, name, filter):
776 self._datafilters[name] = filter
776 self._datafilters[name] = filter
777
777
778 def wread(self, filename):
778 def wread(self, filename):
779 if self._link(filename):
779 if self._link(filename):
780 data = os.readlink(self.wjoin(filename))
780 data = os.readlink(self.wjoin(filename))
781 else:
781 else:
782 data = self.wopener.read(filename)
782 data = self.wopener.read(filename)
783 return self._filter(self._encodefilterpats, filename, data)
783 return self._filter(self._encodefilterpats, filename, data)
784
784
785 def wwrite(self, filename, data, flags):
785 def wwrite(self, filename, data, flags):
786 data = self._filter(self._decodefilterpats, filename, data)
786 data = self._filter(self._decodefilterpats, filename, data)
787 if 'l' in flags:
787 if 'l' in flags:
788 self.wopener.symlink(data, filename)
788 self.wopener.symlink(data, filename)
789 else:
789 else:
790 self.wopener.write(filename, data)
790 self.wopener.write(filename, data)
791 if 'x' in flags:
791 if 'x' in flags:
792 util.setflags(self.wjoin(filename), False, True)
792 util.setflags(self.wjoin(filename), False, True)
793
793
794 def wwritedata(self, filename, data):
794 def wwritedata(self, filename, data):
795 return self._filter(self._decodefilterpats, filename, data)
795 return self._filter(self._decodefilterpats, filename, data)
796
796
797 def transaction(self, desc):
797 def transaction(self, desc):
798 tr = self._transref and self._transref() or None
798 tr = self._transref and self._transref() or None
799 if tr and tr.running():
799 if tr and tr.running():
800 return tr.nest()
800 return tr.nest()
801
801
802 # abort here if the journal already exists
802 # abort here if the journal already exists
803 if os.path.exists(self.sjoin("journal")):
803 if os.path.exists(self.sjoin("journal")):
804 raise error.RepoError(
804 raise error.RepoError(
805 _("abandoned transaction found - run hg recover"))
805 _("abandoned transaction found - run hg recover"))
806
806
807 self._writejournal(desc)
807 self._writejournal(desc)
808 renames = [(x, undoname(x)) for x in self._journalfiles()]
808 renames = [(x, undoname(x)) for x in self._journalfiles()]
809
809
810 tr = transaction.transaction(self.ui.warn, self.sopener,
810 tr = transaction.transaction(self.ui.warn, self.sopener,
811 self.sjoin("journal"),
811 self.sjoin("journal"),
812 aftertrans(renames),
812 aftertrans(renames),
813 self.store.createmode)
813 self.store.createmode)
814 self._transref = weakref.ref(tr)
814 self._transref = weakref.ref(tr)
815 return tr
815 return tr
816
816
817 def _journalfiles(self):
817 def _journalfiles(self):
818 return (self.sjoin('journal'), self.join('journal.dirstate'),
818 return (self.sjoin('journal'), self.join('journal.dirstate'),
819 self.join('journal.branch'), self.join('journal.desc'),
819 self.join('journal.branch'), self.join('journal.desc'),
820 self.join('journal.bookmarks'),
820 self.join('journal.bookmarks'),
821 self.sjoin('journal.phaseroots'))
821 self.sjoin('journal.phaseroots'))
822
822
823 def undofiles(self):
823 def undofiles(self):
824 return [undoname(x) for x in self._journalfiles()]
824 return [undoname(x) for x in self._journalfiles()]
825
825
826 def _writejournal(self, desc):
826 def _writejournal(self, desc):
827 self.opener.write("journal.dirstate",
827 self.opener.write("journal.dirstate",
828 self.opener.tryread("dirstate"))
828 self.opener.tryread("dirstate"))
829 self.opener.write("journal.branch",
829 self.opener.write("journal.branch",
830 encoding.fromlocal(self.dirstate.branch()))
830 encoding.fromlocal(self.dirstate.branch()))
831 self.opener.write("journal.desc",
831 self.opener.write("journal.desc",
832 "%d\n%s\n" % (len(self), desc))
832 "%d\n%s\n" % (len(self), desc))
833 self.opener.write("journal.bookmarks",
833 self.opener.write("journal.bookmarks",
834 self.opener.tryread("bookmarks"))
834 self.opener.tryread("bookmarks"))
835 self.sopener.write("journal.phaseroots",
835 self.sopener.write("journal.phaseroots",
836 self.sopener.tryread("phaseroots"))
836 self.sopener.tryread("phaseroots"))
837
837
838 def recover(self):
838 def recover(self):
839 lock = self.lock()
839 lock = self.lock()
840 try:
840 try:
841 if os.path.exists(self.sjoin("journal")):
841 if os.path.exists(self.sjoin("journal")):
842 self.ui.status(_("rolling back interrupted transaction\n"))
842 self.ui.status(_("rolling back interrupted transaction\n"))
843 transaction.rollback(self.sopener, self.sjoin("journal"),
843 transaction.rollback(self.sopener, self.sjoin("journal"),
844 self.ui.warn)
844 self.ui.warn)
845 self.invalidate()
845 self.invalidate()
846 return True
846 return True
847 else:
847 else:
848 self.ui.warn(_("no interrupted transaction available\n"))
848 self.ui.warn(_("no interrupted transaction available\n"))
849 return False
849 return False
850 finally:
850 finally:
851 lock.release()
851 lock.release()
852
852
853 def rollback(self, dryrun=False, force=False):
853 def rollback(self, dryrun=False, force=False):
854 wlock = lock = None
854 wlock = lock = None
855 try:
855 try:
856 wlock = self.wlock()
856 wlock = self.wlock()
857 lock = self.lock()
857 lock = self.lock()
858 if os.path.exists(self.sjoin("undo")):
858 if os.path.exists(self.sjoin("undo")):
859 return self._rollback(dryrun, force)
859 return self._rollback(dryrun, force)
860 else:
860 else:
861 self.ui.warn(_("no rollback information available\n"))
861 self.ui.warn(_("no rollback information available\n"))
862 return 1
862 return 1
863 finally:
863 finally:
864 release(lock, wlock)
864 release(lock, wlock)
865
865
866 @unfilteredmethod # Until we get smarter cache management
866 @unfilteredmethod # Until we get smarter cache management
867 def _rollback(self, dryrun, force):
867 def _rollback(self, dryrun, force):
868 ui = self.ui
868 ui = self.ui
869 try:
869 try:
870 args = self.opener.read('undo.desc').splitlines()
870 args = self.opener.read('undo.desc').splitlines()
871 (oldlen, desc, detail) = (int(args[0]), args[1], None)
871 (oldlen, desc, detail) = (int(args[0]), args[1], None)
872 if len(args) >= 3:
872 if len(args) >= 3:
873 detail = args[2]
873 detail = args[2]
874 oldtip = oldlen - 1
874 oldtip = oldlen - 1
875
875
876 if detail and ui.verbose:
876 if detail and ui.verbose:
877 msg = (_('repository tip rolled back to revision %s'
877 msg = (_('repository tip rolled back to revision %s'
878 ' (undo %s: %s)\n')
878 ' (undo %s: %s)\n')
879 % (oldtip, desc, detail))
879 % (oldtip, desc, detail))
880 else:
880 else:
881 msg = (_('repository tip rolled back to revision %s'
881 msg = (_('repository tip rolled back to revision %s'
882 ' (undo %s)\n')
882 ' (undo %s)\n')
883 % (oldtip, desc))
883 % (oldtip, desc))
884 except IOError:
884 except IOError:
885 msg = _('rolling back unknown transaction\n')
885 msg = _('rolling back unknown transaction\n')
886 desc = None
886 desc = None
887
887
888 if not force and self['.'] != self['tip'] and desc == 'commit':
888 if not force and self['.'] != self['tip'] and desc == 'commit':
889 raise util.Abort(
889 raise util.Abort(
890 _('rollback of last commit while not checked out '
890 _('rollback of last commit while not checked out '
891 'may lose data'), hint=_('use -f to force'))
891 'may lose data'), hint=_('use -f to force'))
892
892
893 ui.status(msg)
893 ui.status(msg)
894 if dryrun:
894 if dryrun:
895 return 0
895 return 0
896
896
897 parents = self.dirstate.parents()
897 parents = self.dirstate.parents()
898 self.destroying()
898 self.destroying()
899 transaction.rollback(self.sopener, self.sjoin('undo'), ui.warn)
899 transaction.rollback(self.sopener, self.sjoin('undo'), ui.warn)
900 if os.path.exists(self.join('undo.bookmarks')):
900 if os.path.exists(self.join('undo.bookmarks')):
901 util.rename(self.join('undo.bookmarks'),
901 util.rename(self.join('undo.bookmarks'),
902 self.join('bookmarks'))
902 self.join('bookmarks'))
903 if os.path.exists(self.sjoin('undo.phaseroots')):
903 if os.path.exists(self.sjoin('undo.phaseroots')):
904 util.rename(self.sjoin('undo.phaseroots'),
904 util.rename(self.sjoin('undo.phaseroots'),
905 self.sjoin('phaseroots'))
905 self.sjoin('phaseroots'))
906 self.invalidate()
906 self.invalidate()
907
907
908 parentgone = (parents[0] not in self.changelog.nodemap or
908 parentgone = (parents[0] not in self.changelog.nodemap or
909 parents[1] not in self.changelog.nodemap)
909 parents[1] not in self.changelog.nodemap)
910 if parentgone:
910 if parentgone:
911 util.rename(self.join('undo.dirstate'), self.join('dirstate'))
911 util.rename(self.join('undo.dirstate'), self.join('dirstate'))
912 try:
912 try:
913 branch = self.opener.read('undo.branch')
913 branch = self.opener.read('undo.branch')
914 self.dirstate.setbranch(encoding.tolocal(branch))
914 self.dirstate.setbranch(encoding.tolocal(branch))
915 except IOError:
915 except IOError:
916 ui.warn(_('named branch could not be reset: '
916 ui.warn(_('named branch could not be reset: '
917 'current branch is still \'%s\'\n')
917 'current branch is still \'%s\'\n')
918 % self.dirstate.branch())
918 % self.dirstate.branch())
919
919
920 self.dirstate.invalidate()
920 self.dirstate.invalidate()
921 parents = tuple([p.rev() for p in self.parents()])
921 parents = tuple([p.rev() for p in self.parents()])
922 if len(parents) > 1:
922 if len(parents) > 1:
923 ui.status(_('working directory now based on '
923 ui.status(_('working directory now based on '
924 'revisions %d and %d\n') % parents)
924 'revisions %d and %d\n') % parents)
925 else:
925 else:
926 ui.status(_('working directory now based on '
926 ui.status(_('working directory now based on '
927 'revision %d\n') % parents)
927 'revision %d\n') % parents)
928 # TODO: if we know which new heads may result from this rollback, pass
928 # TODO: if we know which new heads may result from this rollback, pass
929 # them to destroy(), which will prevent the branchhead cache from being
929 # them to destroy(), which will prevent the branchhead cache from being
930 # invalidated.
930 # invalidated.
931 self.destroyed()
931 self.destroyed()
932 return 0
932 return 0
933
933
934 def invalidatecaches(self):
934 def invalidatecaches(self):
935
935
936 if '_tagscache' in vars(self):
936 if '_tagscache' in vars(self):
937 # can't use delattr on proxy
937 # can't use delattr on proxy
938 del self.__dict__['_tagscache']
938 del self.__dict__['_tagscache']
939
939
940 self.unfiltered()._branchcaches.clear()
940 self.unfiltered()._branchcaches.clear()
941 self.invalidatevolatilesets()
941 self.invalidatevolatilesets()
942
942
943 def invalidatevolatilesets(self):
943 def invalidatevolatilesets(self):
944 self.filteredrevcache.clear()
944 self.filteredrevcache.clear()
945 obsolete.clearobscaches(self)
945 obsolete.clearobscaches(self)
946
946
947 def invalidatedirstate(self):
947 def invalidatedirstate(self):
948 '''Invalidates the dirstate, causing the next call to dirstate
948 '''Invalidates the dirstate, causing the next call to dirstate
949 to check if it was modified since the last time it was read,
949 to check if it was modified since the last time it was read,
950 rereading it if it has.
950 rereading it if it has.
951
951
952 This is different to dirstate.invalidate() that it doesn't always
952 This is different to dirstate.invalidate() that it doesn't always
953 rereads the dirstate. Use dirstate.invalidate() if you want to
953 rereads the dirstate. Use dirstate.invalidate() if you want to
954 explicitly read the dirstate again (i.e. restoring it to a previous
954 explicitly read the dirstate again (i.e. restoring it to a previous
955 known good state).'''
955 known good state).'''
956 if hasunfilteredcache(self, 'dirstate'):
956 if hasunfilteredcache(self, 'dirstate'):
957 for k in self.dirstate._filecache:
957 for k in self.dirstate._filecache:
958 try:
958 try:
959 delattr(self.dirstate, k)
959 delattr(self.dirstate, k)
960 except AttributeError:
960 except AttributeError:
961 pass
961 pass
962 delattr(self.unfiltered(), 'dirstate')
962 delattr(self.unfiltered(), 'dirstate')
963
963
964 def invalidate(self):
964 def invalidate(self):
965 unfiltered = self.unfiltered() # all filecaches are stored on unfiltered
965 unfiltered = self.unfiltered() # all filecaches are stored on unfiltered
966 for k in self._filecache:
966 for k in self._filecache:
967 # dirstate is invalidated separately in invalidatedirstate()
967 # dirstate is invalidated separately in invalidatedirstate()
968 if k == 'dirstate':
968 if k == 'dirstate':
969 continue
969 continue
970
970
971 try:
971 try:
972 delattr(unfiltered, k)
972 delattr(unfiltered, k)
973 except AttributeError:
973 except AttributeError:
974 pass
974 pass
975 self.invalidatecaches()
975 self.invalidatecaches()
976
976
977 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
977 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
978 try:
978 try:
979 l = lock.lock(lockname, 0, releasefn, desc=desc)
979 l = lock.lock(lockname, 0, releasefn, desc=desc)
980 except error.LockHeld, inst:
980 except error.LockHeld, inst:
981 if not wait:
981 if not wait:
982 raise
982 raise
983 self.ui.warn(_("waiting for lock on %s held by %r\n") %
983 self.ui.warn(_("waiting for lock on %s held by %r\n") %
984 (desc, inst.locker))
984 (desc, inst.locker))
985 # default to 600 seconds timeout
985 # default to 600 seconds timeout
986 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
986 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
987 releasefn, desc=desc)
987 releasefn, desc=desc)
988 if acquirefn:
988 if acquirefn:
989 acquirefn()
989 acquirefn()
990 return l
990 return l
991
991
992 def _afterlock(self, callback):
992 def _afterlock(self, callback):
993 """add a callback to the current repository lock.
993 """add a callback to the current repository lock.
994
994
995 The callback will be executed on lock release."""
995 The callback will be executed on lock release."""
996 l = self._lockref and self._lockref()
996 l = self._lockref and self._lockref()
997 if l:
997 if l:
998 l.postrelease.append(callback)
998 l.postrelease.append(callback)
999 else:
999 else:
1000 callback()
1000 callback()
1001
1001
1002 def lock(self, wait=True):
1002 def lock(self, wait=True):
1003 '''Lock the repository store (.hg/store) and return a weak reference
1003 '''Lock the repository store (.hg/store) and return a weak reference
1004 to the lock. Use this before modifying the store (e.g. committing or
1004 to the lock. Use this before modifying the store (e.g. committing or
1005 stripping). If you are opening a transaction, get a lock as well.)'''
1005 stripping). If you are opening a transaction, get a lock as well.)'''
1006 l = self._lockref and self._lockref()
1006 l = self._lockref and self._lockref()
1007 if l is not None and l.held:
1007 if l is not None and l.held:
1008 l.lock()
1008 l.lock()
1009 return l
1009 return l
1010
1010
1011 def unlock():
1011 def unlock():
1012 self.store.write()
1012 self.store.write()
1013 if hasunfilteredcache(self, '_phasecache'):
1013 if hasunfilteredcache(self, '_phasecache'):
1014 self._phasecache.write()
1014 self._phasecache.write()
1015 for k, ce in self._filecache.items():
1015 for k, ce in self._filecache.items():
1016 if k == 'dirstate' or k not in self.__dict__:
1016 if k == 'dirstate' or k not in self.__dict__:
1017 continue
1017 continue
1018 ce.refresh()
1018 ce.refresh()
1019
1019
1020 l = self._lock(self.sjoin("lock"), wait, unlock,
1020 l = self._lock(self.sjoin("lock"), wait, unlock,
1021 self.invalidate, _('repository %s') % self.origroot)
1021 self.invalidate, _('repository %s') % self.origroot)
1022 self._lockref = weakref.ref(l)
1022 self._lockref = weakref.ref(l)
1023 return l
1023 return l
1024
1024
1025 def wlock(self, wait=True):
1025 def wlock(self, wait=True):
1026 '''Lock the non-store parts of the repository (everything under
1026 '''Lock the non-store parts of the repository (everything under
1027 .hg except .hg/store) and return a weak reference to the lock.
1027 .hg except .hg/store) and return a weak reference to the lock.
1028 Use this before modifying files in .hg.'''
1028 Use this before modifying files in .hg.'''
1029 l = self._wlockref and self._wlockref()
1029 l = self._wlockref and self._wlockref()
1030 if l is not None and l.held:
1030 if l is not None and l.held:
1031 l.lock()
1031 l.lock()
1032 return l
1032 return l
1033
1033
1034 def unlock():
1034 def unlock():
1035 self.dirstate.write()
1035 self.dirstate.write()
1036 self._filecache['dirstate'].refresh()
1036 self._filecache['dirstate'].refresh()
1037
1037
1038 l = self._lock(self.join("wlock"), wait, unlock,
1038 l = self._lock(self.join("wlock"), wait, unlock,
1039 self.invalidatedirstate, _('working directory of %s') %
1039 self.invalidatedirstate, _('working directory of %s') %
1040 self.origroot)
1040 self.origroot)
1041 self._wlockref = weakref.ref(l)
1041 self._wlockref = weakref.ref(l)
1042 return l
1042 return l
1043
1043
1044 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1044 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1045 """
1045 """
1046 commit an individual file as part of a larger transaction
1046 commit an individual file as part of a larger transaction
1047 """
1047 """
1048
1048
1049 fname = fctx.path()
1049 fname = fctx.path()
1050 text = fctx.data()
1050 text = fctx.data()
1051 flog = self.file(fname)
1051 flog = self.file(fname)
1052 fparent1 = manifest1.get(fname, nullid)
1052 fparent1 = manifest1.get(fname, nullid)
1053 fparent2 = fparent2o = manifest2.get(fname, nullid)
1053 fparent2 = fparent2o = manifest2.get(fname, nullid)
1054
1054
1055 meta = {}
1055 meta = {}
1056 copy = fctx.renamed()
1056 copy = fctx.renamed()
1057 if copy and copy[0] != fname:
1057 if copy and copy[0] != fname:
1058 # Mark the new revision of this file as a copy of another
1058 # Mark the new revision of this file as a copy of another
1059 # file. This copy data will effectively act as a parent
1059 # file. This copy data will effectively act as a parent
1060 # of this new revision. If this is a merge, the first
1060 # of this new revision. If this is a merge, the first
1061 # parent will be the nullid (meaning "look up the copy data")
1061 # parent will be the nullid (meaning "look up the copy data")
1062 # and the second one will be the other parent. For example:
1062 # and the second one will be the other parent. For example:
1063 #
1063 #
1064 # 0 --- 1 --- 3 rev1 changes file foo
1064 # 0 --- 1 --- 3 rev1 changes file foo
1065 # \ / rev2 renames foo to bar and changes it
1065 # \ / rev2 renames foo to bar and changes it
1066 # \- 2 -/ rev3 should have bar with all changes and
1066 # \- 2 -/ rev3 should have bar with all changes and
1067 # should record that bar descends from
1067 # should record that bar descends from
1068 # bar in rev2 and foo in rev1
1068 # bar in rev2 and foo in rev1
1069 #
1069 #
1070 # this allows this merge to succeed:
1070 # this allows this merge to succeed:
1071 #
1071 #
1072 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1072 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1073 # \ / merging rev3 and rev4 should use bar@rev2
1073 # \ / merging rev3 and rev4 should use bar@rev2
1074 # \- 2 --- 4 as the merge base
1074 # \- 2 --- 4 as the merge base
1075 #
1075 #
1076
1076
1077 cfname = copy[0]
1077 cfname = copy[0]
1078 crev = manifest1.get(cfname)
1078 crev = manifest1.get(cfname)
1079 newfparent = fparent2
1079 newfparent = fparent2
1080
1080
1081 if manifest2: # branch merge
1081 if manifest2: # branch merge
1082 if fparent2 == nullid or crev is None: # copied on remote side
1082 if fparent2 == nullid or crev is None: # copied on remote side
1083 if cfname in manifest2:
1083 if cfname in manifest2:
1084 crev = manifest2[cfname]
1084 crev = manifest2[cfname]
1085 newfparent = fparent1
1085 newfparent = fparent1
1086
1086
1087 # find source in nearest ancestor if we've lost track
1087 # find source in nearest ancestor if we've lost track
1088 if not crev:
1088 if not crev:
1089 self.ui.debug(" %s: searching for copy revision for %s\n" %
1089 self.ui.debug(" %s: searching for copy revision for %s\n" %
1090 (fname, cfname))
1090 (fname, cfname))
1091 for ancestor in self[None].ancestors():
1091 for ancestor in self[None].ancestors():
1092 if cfname in ancestor:
1092 if cfname in ancestor:
1093 crev = ancestor[cfname].filenode()
1093 crev = ancestor[cfname].filenode()
1094 break
1094 break
1095
1095
1096 if crev:
1096 if crev:
1097 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1097 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1098 meta["copy"] = cfname
1098 meta["copy"] = cfname
1099 meta["copyrev"] = hex(crev)
1099 meta["copyrev"] = hex(crev)
1100 fparent1, fparent2 = nullid, newfparent
1100 fparent1, fparent2 = nullid, newfparent
1101 else:
1101 else:
1102 self.ui.warn(_("warning: can't find ancestor for '%s' "
1102 self.ui.warn(_("warning: can't find ancestor for '%s' "
1103 "copied from '%s'!\n") % (fname, cfname))
1103 "copied from '%s'!\n") % (fname, cfname))
1104
1104
1105 elif fparent2 != nullid:
1105 elif fparent2 != nullid:
1106 # is one parent an ancestor of the other?
1106 # is one parent an ancestor of the other?
1107 fparentancestor = flog.ancestor(fparent1, fparent2)
1107 fparentancestor = flog.ancestor(fparent1, fparent2)
1108 if fparentancestor == fparent1:
1108 if fparentancestor == fparent1:
1109 fparent1, fparent2 = fparent2, nullid
1109 fparent1, fparent2 = fparent2, nullid
1110 elif fparentancestor == fparent2:
1110 elif fparentancestor == fparent2:
1111 fparent2 = nullid
1111 fparent2 = nullid
1112
1112
1113 # is the file changed?
1113 # is the file changed?
1114 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1114 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1115 changelist.append(fname)
1115 changelist.append(fname)
1116 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1116 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1117
1117
1118 # are just the flags changed during merge?
1118 # are just the flags changed during merge?
1119 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
1119 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
1120 changelist.append(fname)
1120 changelist.append(fname)
1121
1121
1122 return fparent1
1122 return fparent1
1123
1123
1124 @unfilteredmethod
1124 @unfilteredmethod
1125 def commit(self, text="", user=None, date=None, match=None, force=False,
1125 def commit(self, text="", user=None, date=None, match=None, force=False,
1126 editor=False, extra={}):
1126 editor=False, extra={}):
1127 """Add a new revision to current repository.
1127 """Add a new revision to current repository.
1128
1128
1129 Revision information is gathered from the working directory,
1129 Revision information is gathered from the working directory,
1130 match can be used to filter the committed files. If editor is
1130 match can be used to filter the committed files. If editor is
1131 supplied, it is called to get a commit message.
1131 supplied, it is called to get a commit message.
1132 """
1132 """
1133
1133
1134 def fail(f, msg):
1134 def fail(f, msg):
1135 raise util.Abort('%s: %s' % (f, msg))
1135 raise util.Abort('%s: %s' % (f, msg))
1136
1136
1137 if not match:
1137 if not match:
1138 match = matchmod.always(self.root, '')
1138 match = matchmod.always(self.root, '')
1139
1139
1140 if not force:
1140 if not force:
1141 vdirs = []
1141 vdirs = []
1142 match.dir = vdirs.append
1142 match.dir = vdirs.append
1143 match.bad = fail
1143 match.bad = fail
1144
1144
1145 wlock = self.wlock()
1145 wlock = self.wlock()
1146 try:
1146 try:
1147 wctx = self[None]
1147 wctx = self[None]
1148 merge = len(wctx.parents()) > 1
1148 merge = len(wctx.parents()) > 1
1149
1149
1150 if (not force and merge and match and
1150 if (not force and merge and match and
1151 (match.files() or match.anypats())):
1151 (match.files() or match.anypats())):
1152 raise util.Abort(_('cannot partially commit a merge '
1152 raise util.Abort(_('cannot partially commit a merge '
1153 '(do not specify files or patterns)'))
1153 '(do not specify files or patterns)'))
1154
1154
1155 changes = self.status(match=match, clean=force)
1155 changes = self.status(match=match, clean=force)
1156 if force:
1156 if force:
1157 changes[0].extend(changes[6]) # mq may commit unchanged files
1157 changes[0].extend(changes[6]) # mq may commit unchanged files
1158
1158
1159 # check subrepos
1159 # check subrepos
1160 subs = []
1160 subs = []
1161 commitsubs = set()
1161 commitsubs = set()
1162 newstate = wctx.substate.copy()
1162 newstate = wctx.substate.copy()
1163 # only manage subrepos and .hgsubstate if .hgsub is present
1163 # only manage subrepos and .hgsubstate if .hgsub is present
1164 if '.hgsub' in wctx:
1164 if '.hgsub' in wctx:
1165 # we'll decide whether to track this ourselves, thanks
1165 # we'll decide whether to track this ourselves, thanks
1166 if '.hgsubstate' in changes[0]:
1166 if '.hgsubstate' in changes[0]:
1167 changes[0].remove('.hgsubstate')
1167 changes[0].remove('.hgsubstate')
1168 if '.hgsubstate' in changes[2]:
1168 if '.hgsubstate' in changes[2]:
1169 changes[2].remove('.hgsubstate')
1169 changes[2].remove('.hgsubstate')
1170
1170
1171 # compare current state to last committed state
1171 # compare current state to last committed state
1172 # build new substate based on last committed state
1172 # build new substate based on last committed state
1173 oldstate = wctx.p1().substate
1173 oldstate = wctx.p1().substate
1174 for s in sorted(newstate.keys()):
1174 for s in sorted(newstate.keys()):
1175 if not match(s):
1175 if not match(s):
1176 # ignore working copy, use old state if present
1176 # ignore working copy, use old state if present
1177 if s in oldstate:
1177 if s in oldstate:
1178 newstate[s] = oldstate[s]
1178 newstate[s] = oldstate[s]
1179 continue
1179 continue
1180 if not force:
1180 if not force:
1181 raise util.Abort(
1181 raise util.Abort(
1182 _("commit with new subrepo %s excluded") % s)
1182 _("commit with new subrepo %s excluded") % s)
1183 if wctx.sub(s).dirty(True):
1183 if wctx.sub(s).dirty(True):
1184 if not self.ui.configbool('ui', 'commitsubrepos'):
1184 if not self.ui.configbool('ui', 'commitsubrepos'):
1185 raise util.Abort(
1185 raise util.Abort(
1186 _("uncommitted changes in subrepo %s") % s,
1186 _("uncommitted changes in subrepo %s") % s,
1187 hint=_("use --subrepos for recursive commit"))
1187 hint=_("use --subrepos for recursive commit"))
1188 subs.append(s)
1188 subs.append(s)
1189 commitsubs.add(s)
1189 commitsubs.add(s)
1190 else:
1190 else:
1191 bs = wctx.sub(s).basestate()
1191 bs = wctx.sub(s).basestate()
1192 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1192 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1193 if oldstate.get(s, (None, None, None))[1] != bs:
1193 if oldstate.get(s, (None, None, None))[1] != bs:
1194 subs.append(s)
1194 subs.append(s)
1195
1195
1196 # check for removed subrepos
1196 # check for removed subrepos
1197 for p in wctx.parents():
1197 for p in wctx.parents():
1198 r = [s for s in p.substate if s not in newstate]
1198 r = [s for s in p.substate if s not in newstate]
1199 subs += [s for s in r if match(s)]
1199 subs += [s for s in r if match(s)]
1200 if subs:
1200 if subs:
1201 if (not match('.hgsub') and
1201 if (not match('.hgsub') and
1202 '.hgsub' in (wctx.modified() + wctx.added())):
1202 '.hgsub' in (wctx.modified() + wctx.added())):
1203 raise util.Abort(
1203 raise util.Abort(
1204 _("can't commit subrepos without .hgsub"))
1204 _("can't commit subrepos without .hgsub"))
1205 changes[0].insert(0, '.hgsubstate')
1205 changes[0].insert(0, '.hgsubstate')
1206
1206
1207 elif '.hgsub' in changes[2]:
1207 elif '.hgsub' in changes[2]:
1208 # clean up .hgsubstate when .hgsub is removed
1208 # clean up .hgsubstate when .hgsub is removed
1209 if ('.hgsubstate' in wctx and
1209 if ('.hgsubstate' in wctx and
1210 '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
1210 '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
1211 changes[2].insert(0, '.hgsubstate')
1211 changes[2].insert(0, '.hgsubstate')
1212
1212
1213 # make sure all explicit patterns are matched
1213 # make sure all explicit patterns are matched
1214 if not force and match.files():
1214 if not force and match.files():
1215 matched = set(changes[0] + changes[1] + changes[2])
1215 matched = set(changes[0] + changes[1] + changes[2])
1216
1216
1217 for f in match.files():
1217 for f in match.files():
1218 f = self.dirstate.normalize(f)
1218 f = self.dirstate.normalize(f)
1219 if f == '.' or f in matched or f in wctx.substate:
1219 if f == '.' or f in matched or f in wctx.substate:
1220 continue
1220 continue
1221 if f in changes[3]: # missing
1221 if f in changes[3]: # missing
1222 fail(f, _('file not found!'))
1222 fail(f, _('file not found!'))
1223 if f in vdirs: # visited directory
1223 if f in vdirs: # visited directory
1224 d = f + '/'
1224 d = f + '/'
1225 for mf in matched:
1225 for mf in matched:
1226 if mf.startswith(d):
1226 if mf.startswith(d):
1227 break
1227 break
1228 else:
1228 else:
1229 fail(f, _("no match under directory!"))
1229 fail(f, _("no match under directory!"))
1230 elif f not in self.dirstate:
1230 elif f not in self.dirstate:
1231 fail(f, _("file not tracked!"))
1231 fail(f, _("file not tracked!"))
1232
1232
1233 if (not force and not extra.get("close") and not merge
1233 if (not force and not extra.get("close") and not merge
1234 and not (changes[0] or changes[1] or changes[2])
1234 and not (changes[0] or changes[1] or changes[2])
1235 and wctx.branch() == wctx.p1().branch()):
1235 and wctx.branch() == wctx.p1().branch()):
1236 return None
1236 return None
1237
1237
1238 if merge and changes[3]:
1238 if merge and changes[3]:
1239 raise util.Abort(_("cannot commit merge with missing files"))
1239 raise util.Abort(_("cannot commit merge with missing files"))
1240
1240
1241 ms = mergemod.mergestate(self)
1241 ms = mergemod.mergestate(self)
1242 for f in changes[0]:
1242 for f in changes[0]:
1243 if f in ms and ms[f] == 'u':
1243 if f in ms and ms[f] == 'u':
1244 raise util.Abort(_("unresolved merge conflicts "
1244 raise util.Abort(_("unresolved merge conflicts "
1245 "(see hg help resolve)"))
1245 "(see hg help resolve)"))
1246
1246
1247 cctx = context.workingctx(self, text, user, date, extra, changes)
1247 cctx = context.workingctx(self, text, user, date, extra, changes)
1248 if editor:
1248 if editor:
1249 cctx._text = editor(self, cctx, subs)
1249 cctx._text = editor(self, cctx, subs)
1250 edited = (text != cctx._text)
1250 edited = (text != cctx._text)
1251
1251
1252 # commit subs and write new state
1252 # commit subs and write new state
1253 if subs:
1253 if subs:
1254 for s in sorted(commitsubs):
1254 for s in sorted(commitsubs):
1255 sub = wctx.sub(s)
1255 sub = wctx.sub(s)
1256 self.ui.status(_('committing subrepository %s\n') %
1256 self.ui.status(_('committing subrepository %s\n') %
1257 subrepo.subrelpath(sub))
1257 subrepo.subrelpath(sub))
1258 sr = sub.commit(cctx._text, user, date)
1258 sr = sub.commit(cctx._text, user, date)
1259 newstate[s] = (newstate[s][0], sr)
1259 newstate[s] = (newstate[s][0], sr)
1260 subrepo.writestate(self, newstate)
1260 subrepo.writestate(self, newstate)
1261
1261
1262 # Save commit message in case this transaction gets rolled back
1262 # Save commit message in case this transaction gets rolled back
1263 # (e.g. by a pretxncommit hook). Leave the content alone on
1263 # (e.g. by a pretxncommit hook). Leave the content alone on
1264 # the assumption that the user will use the same editor again.
1264 # the assumption that the user will use the same editor again.
1265 msgfn = self.savecommitmessage(cctx._text)
1265 msgfn = self.savecommitmessage(cctx._text)
1266
1266
1267 p1, p2 = self.dirstate.parents()
1267 p1, p2 = self.dirstate.parents()
1268 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1268 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1269 try:
1269 try:
1270 self.hook("precommit", throw=True, parent1=hookp1,
1270 self.hook("precommit", throw=True, parent1=hookp1,
1271 parent2=hookp2)
1271 parent2=hookp2)
1272 ret = self.commitctx(cctx, True)
1272 ret = self.commitctx(cctx, True)
1273 except: # re-raises
1273 except: # re-raises
1274 if edited:
1274 if edited:
1275 self.ui.write(
1275 self.ui.write(
1276 _('note: commit message saved in %s\n') % msgfn)
1276 _('note: commit message saved in %s\n') % msgfn)
1277 raise
1277 raise
1278
1278
1279 # update bookmarks, dirstate and mergestate
1279 # update bookmarks, dirstate and mergestate
1280 bookmarks.update(self, [p1, p2], ret)
1280 bookmarks.update(self, [p1, p2], ret)
1281 for f in changes[0] + changes[1]:
1281 for f in changes[0] + changes[1]:
1282 self.dirstate.normal(f)
1282 self.dirstate.normal(f)
1283 for f in changes[2]:
1283 for f in changes[2]:
1284 self.dirstate.drop(f)
1284 self.dirstate.drop(f)
1285 self.dirstate.setparents(ret)
1285 self.dirstate.setparents(ret)
1286 ms.reset()
1286 ms.reset()
1287 finally:
1287 finally:
1288 wlock.release()
1288 wlock.release()
1289
1289
1290 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1290 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1291 self.hook("commit", node=node, parent1=parent1, parent2=parent2)
1291 self.hook("commit", node=node, parent1=parent1, parent2=parent2)
1292 self._afterlock(commithook)
1292 self._afterlock(commithook)
1293 return ret
1293 return ret
1294
1294
1295 @unfilteredmethod
1295 @unfilteredmethod
1296 def commitctx(self, ctx, error=False):
1296 def commitctx(self, ctx, error=False):
1297 """Add a new revision to current repository.
1297 """Add a new revision to current repository.
1298 Revision information is passed via the context argument.
1298 Revision information is passed via the context argument.
1299 """
1299 """
1300
1300
1301 tr = lock = None
1301 tr = lock = None
1302 removed = list(ctx.removed())
1302 removed = list(ctx.removed())
1303 p1, p2 = ctx.p1(), ctx.p2()
1303 p1, p2 = ctx.p1(), ctx.p2()
1304 user = ctx.user()
1304 user = ctx.user()
1305
1305
1306 lock = self.lock()
1306 lock = self.lock()
1307 try:
1307 try:
1308 tr = self.transaction("commit")
1308 tr = self.transaction("commit")
1309 trp = weakref.proxy(tr)
1309 trp = weakref.proxy(tr)
1310
1310
1311 if ctx.files():
1311 if ctx.files():
1312 m1 = p1.manifest().copy()
1312 m1 = p1.manifest().copy()
1313 m2 = p2.manifest()
1313 m2 = p2.manifest()
1314
1314
1315 # check in files
1315 # check in files
1316 new = {}
1316 new = {}
1317 changed = []
1317 changed = []
1318 linkrev = len(self)
1318 linkrev = len(self)
1319 for f in sorted(ctx.modified() + ctx.added()):
1319 for f in sorted(ctx.modified() + ctx.added()):
1320 self.ui.note(f + "\n")
1320 self.ui.note(f + "\n")
1321 try:
1321 try:
1322 fctx = ctx[f]
1322 fctx = ctx[f]
1323 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1323 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1324 changed)
1324 changed)
1325 m1.set(f, fctx.flags())
1325 m1.set(f, fctx.flags())
1326 except OSError, inst:
1326 except OSError, inst:
1327 self.ui.warn(_("trouble committing %s!\n") % f)
1327 self.ui.warn(_("trouble committing %s!\n") % f)
1328 raise
1328 raise
1329 except IOError, inst:
1329 except IOError, inst:
1330 errcode = getattr(inst, 'errno', errno.ENOENT)
1330 errcode = getattr(inst, 'errno', errno.ENOENT)
1331 if error or errcode and errcode != errno.ENOENT:
1331 if error or errcode and errcode != errno.ENOENT:
1332 self.ui.warn(_("trouble committing %s!\n") % f)
1332 self.ui.warn(_("trouble committing %s!\n") % f)
1333 raise
1333 raise
1334 else:
1334 else:
1335 removed.append(f)
1335 removed.append(f)
1336
1336
1337 # update manifest
1337 # update manifest
1338 m1.update(new)
1338 m1.update(new)
1339 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1339 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1340 drop = [f for f in removed if f in m1]
1340 drop = [f for f in removed if f in m1]
1341 for f in drop:
1341 for f in drop:
1342 del m1[f]
1342 del m1[f]
1343 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1343 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1344 p2.manifestnode(), (new, drop))
1344 p2.manifestnode(), (new, drop))
1345 files = changed + removed
1345 files = changed + removed
1346 else:
1346 else:
1347 mn = p1.manifestnode()
1347 mn = p1.manifestnode()
1348 files = []
1348 files = []
1349
1349
1350 # update changelog
1350 # update changelog
1351 self.changelog.delayupdate()
1351 self.changelog.delayupdate()
1352 n = self.changelog.add(mn, files, ctx.description(),
1352 n = self.changelog.add(mn, files, ctx.description(),
1353 trp, p1.node(), p2.node(),
1353 trp, p1.node(), p2.node(),
1354 user, ctx.date(), ctx.extra().copy())
1354 user, ctx.date(), ctx.extra().copy())
1355 p = lambda: self.changelog.writepending() and self.root or ""
1355 p = lambda: self.changelog.writepending() and self.root or ""
1356 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1356 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1357 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1357 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1358 parent2=xp2, pending=p)
1358 parent2=xp2, pending=p)
1359 self.changelog.finalize(trp)
1359 self.changelog.finalize(trp)
1360 # set the new commit is proper phase
1360 # set the new commit is proper phase
1361 targetphase = phases.newcommitphase(self.ui)
1361 targetphase = phases.newcommitphase(self.ui)
1362 if targetphase:
1362 if targetphase:
1363 # retract boundary do not alter parent changeset.
1363 # retract boundary do not alter parent changeset.
1364 # if a parent have higher the resulting phase will
1364 # if a parent have higher the resulting phase will
1365 # be compliant anyway
1365 # be compliant anyway
1366 #
1366 #
1367 # if minimal phase was 0 we don't need to retract anything
1367 # if minimal phase was 0 we don't need to retract anything
1368 phases.retractboundary(self, targetphase, [n])
1368 phases.retractboundary(self, targetphase, [n])
1369 tr.close()
1369 tr.close()
1370 branchmap.updatecache(self.filtered('served'))
1370 branchmap.updatecache(self.filtered('served'))
1371 return n
1371 return n
1372 finally:
1372 finally:
1373 if tr:
1373 if tr:
1374 tr.release()
1374 tr.release()
1375 lock.release()
1375 lock.release()
1376
1376
1377 @unfilteredmethod
1377 @unfilteredmethod
1378 def destroying(self):
1378 def destroying(self):
1379 '''Inform the repository that nodes are about to be destroyed.
1379 '''Inform the repository that nodes are about to be destroyed.
1380 Intended for use by strip and rollback, so there's a common
1380 Intended for use by strip and rollback, so there's a common
1381 place for anything that has to be done before destroying history.
1381 place for anything that has to be done before destroying history.
1382
1382
1383 This is mostly useful for saving state that is in memory and waiting
1383 This is mostly useful for saving state that is in memory and waiting
1384 to be flushed when the current lock is released. Because a call to
1384 to be flushed when the current lock is released. Because a call to
1385 destroyed is imminent, the repo will be invalidated causing those
1385 destroyed is imminent, the repo will be invalidated causing those
1386 changes to stay in memory (waiting for the next unlock), or vanish
1386 changes to stay in memory (waiting for the next unlock), or vanish
1387 completely.
1387 completely.
1388 '''
1388 '''
1389 # It simplifies the logic around updating the branchheads cache if we
1389 # It simplifies the logic around updating the branchheads cache if we
1390 # only have to consider the effect of the stripped revisions and not
1390 # only have to consider the effect of the stripped revisions and not
1391 # revisions missing because the cache is out-of-date.
1391 # revisions missing because the cache is out-of-date.
1392 branchmap.updatecache(self)
1392 branchmap.updatecache(self)
1393
1393
1394 # When using the same lock to commit and strip, the phasecache is left
1394 # When using the same lock to commit and strip, the phasecache is left
1395 # dirty after committing. Then when we strip, the repo is invalidated,
1395 # dirty after committing. Then when we strip, the repo is invalidated,
1396 # causing those changes to disappear.
1396 # causing those changes to disappear.
1397 if '_phasecache' in vars(self):
1397 if '_phasecache' in vars(self):
1398 self._phasecache.write()
1398 self._phasecache.write()
1399
1399
1400 @unfilteredmethod
1400 @unfilteredmethod
1401 def destroyed(self, newheadnodes=None):
1401 def destroyed(self):
1402 '''Inform the repository that nodes have been destroyed.
1402 '''Inform the repository that nodes have been destroyed.
1403 Intended for use by strip and rollback, so there's a common
1403 Intended for use by strip and rollback, so there's a common
1404 place for anything that has to be done after destroying history.
1404 place for anything that has to be done after destroying history.
1405
1405
1406 If you know the branchheadcache was uptodate before nodes were removed
1406 If you know the branchheadcache was uptodate before nodes were removed
1407 and you also know the set of candidate new heads that may have resulted
1407 and you also know the set of candidate new heads that may have resulted
1408 from the destruction, you can set newheadnodes. This will enable the
1408 from the destruction, you can set newheadnodes. This will enable the
1409 code to update the branchheads cache, rather than having future code
1409 code to update the branchheads cache, rather than having future code
1410 decide it's invalid and regenerating it from scratch.
1410 decide it's invalid and regenerating it from scratch.
1411 '''
1411 '''
1412 # When one tries to:
1412 # When one tries to:
1413 # 1) destroy nodes thus calling this method (e.g. strip)
1413 # 1) destroy nodes thus calling this method (e.g. strip)
1414 # 2) use phasecache somewhere (e.g. commit)
1414 # 2) use phasecache somewhere (e.g. commit)
1415 #
1415 #
1416 # then 2) will fail because the phasecache contains nodes that were
1416 # then 2) will fail because the phasecache contains nodes that were
1417 # removed. We can either remove phasecache from the filecache,
1417 # removed. We can either remove phasecache from the filecache,
1418 # causing it to reload next time it is accessed, or simply filter
1418 # causing it to reload next time it is accessed, or simply filter
1419 # the removed nodes now and write the updated cache.
1419 # the removed nodes now and write the updated cache.
1420 if '_phasecache' in self._filecache:
1420 if '_phasecache' in self._filecache:
1421 self._phasecache.filterunknown(self)
1421 self._phasecache.filterunknown(self)
1422 self._phasecache.write()
1422 self._phasecache.write()
1423
1423
1424 # If we have info, newheadnodes, on how to update the branch cache, do
1424 # update branchcache information likely invalidated by the strip.
1425 # it, Otherwise, since nodes were destroyed, the cache is stale and this
1425 # We rely on branchcache collaboration for this call to be fast
1426 # will be caught the next time it is read.
1426 branchmap.updatecache(self)
1427 if newheadnodes:
1428 cl = self.changelog
1429 revgen = (cl.rev(node) for node in newheadnodes
1430 if cl.hasnode(node))
1431 cache = self._branchcaches[None]
1432 cache.update(self, revgen)
1433 cache.write(self)
1434
1427
1435 # Ensure the persistent tag cache is updated. Doing it now
1428 # Ensure the persistent tag cache is updated. Doing it now
1436 # means that the tag cache only has to worry about destroyed
1429 # means that the tag cache only has to worry about destroyed
1437 # heads immediately after a strip/rollback. That in turn
1430 # heads immediately after a strip/rollback. That in turn
1438 # guarantees that "cachetip == currenttip" (comparing both rev
1431 # guarantees that "cachetip == currenttip" (comparing both rev
1439 # and node) always means no nodes have been added or destroyed.
1432 # and node) always means no nodes have been added or destroyed.
1440
1433
1441 # XXX this is suboptimal when qrefresh'ing: we strip the current
1434 # XXX this is suboptimal when qrefresh'ing: we strip the current
1442 # head, refresh the tag cache, then immediately add a new head.
1435 # head, refresh the tag cache, then immediately add a new head.
1443 # But I think doing it this way is necessary for the "instant
1436 # But I think doing it this way is necessary for the "instant
1444 # tag cache retrieval" case to work.
1437 # tag cache retrieval" case to work.
1445 self.invalidate()
1438 self.invalidate()
1446
1439
1447 def walk(self, match, node=None):
1440 def walk(self, match, node=None):
1448 '''
1441 '''
1449 walk recursively through the directory tree or a given
1442 walk recursively through the directory tree or a given
1450 changeset, finding all files matched by the match
1443 changeset, finding all files matched by the match
1451 function
1444 function
1452 '''
1445 '''
1453 return self[node].walk(match)
1446 return self[node].walk(match)
1454
1447
1455 def status(self, node1='.', node2=None, match=None,
1448 def status(self, node1='.', node2=None, match=None,
1456 ignored=False, clean=False, unknown=False,
1449 ignored=False, clean=False, unknown=False,
1457 listsubrepos=False):
1450 listsubrepos=False):
1458 """return status of files between two nodes or node and working
1451 """return status of files between two nodes or node and working
1459 directory.
1452 directory.
1460
1453
1461 If node1 is None, use the first dirstate parent instead.
1454 If node1 is None, use the first dirstate parent instead.
1462 If node2 is None, compare node1 with working directory.
1455 If node2 is None, compare node1 with working directory.
1463 """
1456 """
1464
1457
1465 def mfmatches(ctx):
1458 def mfmatches(ctx):
1466 mf = ctx.manifest().copy()
1459 mf = ctx.manifest().copy()
1467 if match.always():
1460 if match.always():
1468 return mf
1461 return mf
1469 for fn in mf.keys():
1462 for fn in mf.keys():
1470 if not match(fn):
1463 if not match(fn):
1471 del mf[fn]
1464 del mf[fn]
1472 return mf
1465 return mf
1473
1466
1474 if isinstance(node1, context.changectx):
1467 if isinstance(node1, context.changectx):
1475 ctx1 = node1
1468 ctx1 = node1
1476 else:
1469 else:
1477 ctx1 = self[node1]
1470 ctx1 = self[node1]
1478 if isinstance(node2, context.changectx):
1471 if isinstance(node2, context.changectx):
1479 ctx2 = node2
1472 ctx2 = node2
1480 else:
1473 else:
1481 ctx2 = self[node2]
1474 ctx2 = self[node2]
1482
1475
1483 working = ctx2.rev() is None
1476 working = ctx2.rev() is None
1484 parentworking = working and ctx1 == self['.']
1477 parentworking = working and ctx1 == self['.']
1485 match = match or matchmod.always(self.root, self.getcwd())
1478 match = match or matchmod.always(self.root, self.getcwd())
1486 listignored, listclean, listunknown = ignored, clean, unknown
1479 listignored, listclean, listunknown = ignored, clean, unknown
1487
1480
1488 # load earliest manifest first for caching reasons
1481 # load earliest manifest first for caching reasons
1489 if not working and ctx2.rev() < ctx1.rev():
1482 if not working and ctx2.rev() < ctx1.rev():
1490 ctx2.manifest()
1483 ctx2.manifest()
1491
1484
1492 if not parentworking:
1485 if not parentworking:
1493 def bad(f, msg):
1486 def bad(f, msg):
1494 # 'f' may be a directory pattern from 'match.files()',
1487 # 'f' may be a directory pattern from 'match.files()',
1495 # so 'f not in ctx1' is not enough
1488 # so 'f not in ctx1' is not enough
1496 if f not in ctx1 and f not in ctx1.dirs():
1489 if f not in ctx1 and f not in ctx1.dirs():
1497 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1490 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1498 match.bad = bad
1491 match.bad = bad
1499
1492
1500 if working: # we need to scan the working dir
1493 if working: # we need to scan the working dir
1501 subrepos = []
1494 subrepos = []
1502 if '.hgsub' in self.dirstate:
1495 if '.hgsub' in self.dirstate:
1503 subrepos = sorted(ctx2.substate)
1496 subrepos = sorted(ctx2.substate)
1504 s = self.dirstate.status(match, subrepos, listignored,
1497 s = self.dirstate.status(match, subrepos, listignored,
1505 listclean, listunknown)
1498 listclean, listunknown)
1506 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1499 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1507
1500
1508 # check for any possibly clean files
1501 # check for any possibly clean files
1509 if parentworking and cmp:
1502 if parentworking and cmp:
1510 fixup = []
1503 fixup = []
1511 # do a full compare of any files that might have changed
1504 # do a full compare of any files that might have changed
1512 for f in sorted(cmp):
1505 for f in sorted(cmp):
1513 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1506 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1514 or ctx1[f].cmp(ctx2[f])):
1507 or ctx1[f].cmp(ctx2[f])):
1515 modified.append(f)
1508 modified.append(f)
1516 else:
1509 else:
1517 fixup.append(f)
1510 fixup.append(f)
1518
1511
1519 # update dirstate for files that are actually clean
1512 # update dirstate for files that are actually clean
1520 if fixup:
1513 if fixup:
1521 if listclean:
1514 if listclean:
1522 clean += fixup
1515 clean += fixup
1523
1516
1524 try:
1517 try:
1525 # updating the dirstate is optional
1518 # updating the dirstate is optional
1526 # so we don't wait on the lock
1519 # so we don't wait on the lock
1527 wlock = self.wlock(False)
1520 wlock = self.wlock(False)
1528 try:
1521 try:
1529 for f in fixup:
1522 for f in fixup:
1530 self.dirstate.normal(f)
1523 self.dirstate.normal(f)
1531 finally:
1524 finally:
1532 wlock.release()
1525 wlock.release()
1533 except error.LockError:
1526 except error.LockError:
1534 pass
1527 pass
1535
1528
1536 if not parentworking:
1529 if not parentworking:
1537 mf1 = mfmatches(ctx1)
1530 mf1 = mfmatches(ctx1)
1538 if working:
1531 if working:
1539 # we are comparing working dir against non-parent
1532 # we are comparing working dir against non-parent
1540 # generate a pseudo-manifest for the working dir
1533 # generate a pseudo-manifest for the working dir
1541 mf2 = mfmatches(self['.'])
1534 mf2 = mfmatches(self['.'])
1542 for f in cmp + modified + added:
1535 for f in cmp + modified + added:
1543 mf2[f] = None
1536 mf2[f] = None
1544 mf2.set(f, ctx2.flags(f))
1537 mf2.set(f, ctx2.flags(f))
1545 for f in removed:
1538 for f in removed:
1546 if f in mf2:
1539 if f in mf2:
1547 del mf2[f]
1540 del mf2[f]
1548 else:
1541 else:
1549 # we are comparing two revisions
1542 # we are comparing two revisions
1550 deleted, unknown, ignored = [], [], []
1543 deleted, unknown, ignored = [], [], []
1551 mf2 = mfmatches(ctx2)
1544 mf2 = mfmatches(ctx2)
1552
1545
1553 modified, added, clean = [], [], []
1546 modified, added, clean = [], [], []
1554 withflags = mf1.withflags() | mf2.withflags()
1547 withflags = mf1.withflags() | mf2.withflags()
1555 for fn in mf2:
1548 for fn in mf2:
1556 if fn in mf1:
1549 if fn in mf1:
1557 if (fn not in deleted and
1550 if (fn not in deleted and
1558 ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or
1551 ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or
1559 (mf1[fn] != mf2[fn] and
1552 (mf1[fn] != mf2[fn] and
1560 (mf2[fn] or ctx1[fn].cmp(ctx2[fn]))))):
1553 (mf2[fn] or ctx1[fn].cmp(ctx2[fn]))))):
1561 modified.append(fn)
1554 modified.append(fn)
1562 elif listclean:
1555 elif listclean:
1563 clean.append(fn)
1556 clean.append(fn)
1564 del mf1[fn]
1557 del mf1[fn]
1565 elif fn not in deleted:
1558 elif fn not in deleted:
1566 added.append(fn)
1559 added.append(fn)
1567 removed = mf1.keys()
1560 removed = mf1.keys()
1568
1561
1569 if working and modified and not self.dirstate._checklink:
1562 if working and modified and not self.dirstate._checklink:
1570 # Symlink placeholders may get non-symlink-like contents
1563 # Symlink placeholders may get non-symlink-like contents
1571 # via user error or dereferencing by NFS or Samba servers,
1564 # via user error or dereferencing by NFS or Samba servers,
1572 # so we filter out any placeholders that don't look like a
1565 # so we filter out any placeholders that don't look like a
1573 # symlink
1566 # symlink
1574 sane = []
1567 sane = []
1575 for f in modified:
1568 for f in modified:
1576 if ctx2.flags(f) == 'l':
1569 if ctx2.flags(f) == 'l':
1577 d = ctx2[f].data()
1570 d = ctx2[f].data()
1578 if len(d) >= 1024 or '\n' in d or util.binary(d):
1571 if len(d) >= 1024 or '\n' in d or util.binary(d):
1579 self.ui.debug('ignoring suspect symlink placeholder'
1572 self.ui.debug('ignoring suspect symlink placeholder'
1580 ' "%s"\n' % f)
1573 ' "%s"\n' % f)
1581 continue
1574 continue
1582 sane.append(f)
1575 sane.append(f)
1583 modified = sane
1576 modified = sane
1584
1577
1585 r = modified, added, removed, deleted, unknown, ignored, clean
1578 r = modified, added, removed, deleted, unknown, ignored, clean
1586
1579
1587 if listsubrepos:
1580 if listsubrepos:
1588 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1581 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1589 if working:
1582 if working:
1590 rev2 = None
1583 rev2 = None
1591 else:
1584 else:
1592 rev2 = ctx2.substate[subpath][1]
1585 rev2 = ctx2.substate[subpath][1]
1593 try:
1586 try:
1594 submatch = matchmod.narrowmatcher(subpath, match)
1587 submatch = matchmod.narrowmatcher(subpath, match)
1595 s = sub.status(rev2, match=submatch, ignored=listignored,
1588 s = sub.status(rev2, match=submatch, ignored=listignored,
1596 clean=listclean, unknown=listunknown,
1589 clean=listclean, unknown=listunknown,
1597 listsubrepos=True)
1590 listsubrepos=True)
1598 for rfiles, sfiles in zip(r, s):
1591 for rfiles, sfiles in zip(r, s):
1599 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1592 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1600 except error.LookupError:
1593 except error.LookupError:
1601 self.ui.status(_("skipping missing subrepository: %s\n")
1594 self.ui.status(_("skipping missing subrepository: %s\n")
1602 % subpath)
1595 % subpath)
1603
1596
1604 for l in r:
1597 for l in r:
1605 l.sort()
1598 l.sort()
1606 return r
1599 return r
1607
1600
1608 def heads(self, start=None):
1601 def heads(self, start=None):
1609 heads = self.changelog.heads(start)
1602 heads = self.changelog.heads(start)
1610 # sort the output in rev descending order
1603 # sort the output in rev descending order
1611 return sorted(heads, key=self.changelog.rev, reverse=True)
1604 return sorted(heads, key=self.changelog.rev, reverse=True)
1612
1605
1613 def branchheads(self, branch=None, start=None, closed=False):
1606 def branchheads(self, branch=None, start=None, closed=False):
1614 '''return a (possibly filtered) list of heads for the given branch
1607 '''return a (possibly filtered) list of heads for the given branch
1615
1608
1616 Heads are returned in topological order, from newest to oldest.
1609 Heads are returned in topological order, from newest to oldest.
1617 If branch is None, use the dirstate branch.
1610 If branch is None, use the dirstate branch.
1618 If start is not None, return only heads reachable from start.
1611 If start is not None, return only heads reachable from start.
1619 If closed is True, return heads that are marked as closed as well.
1612 If closed is True, return heads that are marked as closed as well.
1620 '''
1613 '''
1621 if branch is None:
1614 if branch is None:
1622 branch = self[None].branch()
1615 branch = self[None].branch()
1623 branches = self.branchmap()
1616 branches = self.branchmap()
1624 if branch not in branches:
1617 if branch not in branches:
1625 return []
1618 return []
1626 # the cache returns heads ordered lowest to highest
1619 # the cache returns heads ordered lowest to highest
1627 bheads = list(reversed(branches[branch]))
1620 bheads = list(reversed(branches[branch]))
1628 if start is not None:
1621 if start is not None:
1629 # filter out the heads that cannot be reached from startrev
1622 # filter out the heads that cannot be reached from startrev
1630 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1623 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1631 bheads = [h for h in bheads if h in fbheads]
1624 bheads = [h for h in bheads if h in fbheads]
1632 if not closed:
1625 if not closed:
1633 bheads = [h for h in bheads if not self[h].closesbranch()]
1626 bheads = [h for h in bheads if not self[h].closesbranch()]
1634 return bheads
1627 return bheads
1635
1628
1636 def branches(self, nodes):
1629 def branches(self, nodes):
1637 if not nodes:
1630 if not nodes:
1638 nodes = [self.changelog.tip()]
1631 nodes = [self.changelog.tip()]
1639 b = []
1632 b = []
1640 for n in nodes:
1633 for n in nodes:
1641 t = n
1634 t = n
1642 while True:
1635 while True:
1643 p = self.changelog.parents(n)
1636 p = self.changelog.parents(n)
1644 if p[1] != nullid or p[0] == nullid:
1637 if p[1] != nullid or p[0] == nullid:
1645 b.append((t, n, p[0], p[1]))
1638 b.append((t, n, p[0], p[1]))
1646 break
1639 break
1647 n = p[0]
1640 n = p[0]
1648 return b
1641 return b
1649
1642
1650 def between(self, pairs):
1643 def between(self, pairs):
1651 r = []
1644 r = []
1652
1645
1653 for top, bottom in pairs:
1646 for top, bottom in pairs:
1654 n, l, i = top, [], 0
1647 n, l, i = top, [], 0
1655 f = 1
1648 f = 1
1656
1649
1657 while n != bottom and n != nullid:
1650 while n != bottom and n != nullid:
1658 p = self.changelog.parents(n)[0]
1651 p = self.changelog.parents(n)[0]
1659 if i == f:
1652 if i == f:
1660 l.append(n)
1653 l.append(n)
1661 f = f * 2
1654 f = f * 2
1662 n = p
1655 n = p
1663 i += 1
1656 i += 1
1664
1657
1665 r.append(l)
1658 r.append(l)
1666
1659
1667 return r
1660 return r
1668
1661
1669 def pull(self, remote, heads=None, force=False):
1662 def pull(self, remote, heads=None, force=False):
1670 # don't open transaction for nothing or you break future useful
1663 # don't open transaction for nothing or you break future useful
1671 # rollback call
1664 # rollback call
1672 tr = None
1665 tr = None
1673 trname = 'pull\n' + util.hidepassword(remote.url())
1666 trname = 'pull\n' + util.hidepassword(remote.url())
1674 lock = self.lock()
1667 lock = self.lock()
1675 try:
1668 try:
1676 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1669 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1677 force=force)
1670 force=force)
1678 common, fetch, rheads = tmp
1671 common, fetch, rheads = tmp
1679 if not fetch:
1672 if not fetch:
1680 self.ui.status(_("no changes found\n"))
1673 self.ui.status(_("no changes found\n"))
1681 added = []
1674 added = []
1682 result = 0
1675 result = 0
1683 else:
1676 else:
1684 tr = self.transaction(trname)
1677 tr = self.transaction(trname)
1685 if heads is None and list(common) == [nullid]:
1678 if heads is None and list(common) == [nullid]:
1686 self.ui.status(_("requesting all changes\n"))
1679 self.ui.status(_("requesting all changes\n"))
1687 elif heads is None and remote.capable('changegroupsubset'):
1680 elif heads is None and remote.capable('changegroupsubset'):
1688 # issue1320, avoid a race if remote changed after discovery
1681 # issue1320, avoid a race if remote changed after discovery
1689 heads = rheads
1682 heads = rheads
1690
1683
1691 if remote.capable('getbundle'):
1684 if remote.capable('getbundle'):
1692 cg = remote.getbundle('pull', common=common,
1685 cg = remote.getbundle('pull', common=common,
1693 heads=heads or rheads)
1686 heads=heads or rheads)
1694 elif heads is None:
1687 elif heads is None:
1695 cg = remote.changegroup(fetch, 'pull')
1688 cg = remote.changegroup(fetch, 'pull')
1696 elif not remote.capable('changegroupsubset'):
1689 elif not remote.capable('changegroupsubset'):
1697 raise util.Abort(_("partial pull cannot be done because "
1690 raise util.Abort(_("partial pull cannot be done because "
1698 "other repository doesn't support "
1691 "other repository doesn't support "
1699 "changegroupsubset."))
1692 "changegroupsubset."))
1700 else:
1693 else:
1701 cg = remote.changegroupsubset(fetch, heads, 'pull')
1694 cg = remote.changegroupsubset(fetch, heads, 'pull')
1702 clstart = len(self.changelog)
1695 clstart = len(self.changelog)
1703 result = self.addchangegroup(cg, 'pull', remote.url())
1696 result = self.addchangegroup(cg, 'pull', remote.url())
1704 clend = len(self.changelog)
1697 clend = len(self.changelog)
1705 added = [self.changelog.node(r) for r in xrange(clstart, clend)]
1698 added = [self.changelog.node(r) for r in xrange(clstart, clend)]
1706
1699
1707 # compute target subset
1700 # compute target subset
1708 if heads is None:
1701 if heads is None:
1709 # We pulled every thing possible
1702 # We pulled every thing possible
1710 # sync on everything common
1703 # sync on everything common
1711 subset = common + added
1704 subset = common + added
1712 else:
1705 else:
1713 # We pulled a specific subset
1706 # We pulled a specific subset
1714 # sync on this subset
1707 # sync on this subset
1715 subset = heads
1708 subset = heads
1716
1709
1717 # Get remote phases data from remote
1710 # Get remote phases data from remote
1718 remotephases = remote.listkeys('phases')
1711 remotephases = remote.listkeys('phases')
1719 publishing = bool(remotephases.get('publishing', False))
1712 publishing = bool(remotephases.get('publishing', False))
1720 if remotephases and not publishing:
1713 if remotephases and not publishing:
1721 # remote is new and unpublishing
1714 # remote is new and unpublishing
1722 pheads, _dr = phases.analyzeremotephases(self, subset,
1715 pheads, _dr = phases.analyzeremotephases(self, subset,
1723 remotephases)
1716 remotephases)
1724 phases.advanceboundary(self, phases.public, pheads)
1717 phases.advanceboundary(self, phases.public, pheads)
1725 phases.advanceboundary(self, phases.draft, subset)
1718 phases.advanceboundary(self, phases.draft, subset)
1726 else:
1719 else:
1727 # Remote is old or publishing all common changesets
1720 # Remote is old or publishing all common changesets
1728 # should be seen as public
1721 # should be seen as public
1729 phases.advanceboundary(self, phases.public, subset)
1722 phases.advanceboundary(self, phases.public, subset)
1730
1723
1731 if obsolete._enabled:
1724 if obsolete._enabled:
1732 self.ui.debug('fetching remote obsolete markers\n')
1725 self.ui.debug('fetching remote obsolete markers\n')
1733 remoteobs = remote.listkeys('obsolete')
1726 remoteobs = remote.listkeys('obsolete')
1734 if 'dump0' in remoteobs:
1727 if 'dump0' in remoteobs:
1735 if tr is None:
1728 if tr is None:
1736 tr = self.transaction(trname)
1729 tr = self.transaction(trname)
1737 for key in sorted(remoteobs, reverse=True):
1730 for key in sorted(remoteobs, reverse=True):
1738 if key.startswith('dump'):
1731 if key.startswith('dump'):
1739 data = base85.b85decode(remoteobs[key])
1732 data = base85.b85decode(remoteobs[key])
1740 self.obsstore.mergemarkers(tr, data)
1733 self.obsstore.mergemarkers(tr, data)
1741 self.invalidatevolatilesets()
1734 self.invalidatevolatilesets()
1742 if tr is not None:
1735 if tr is not None:
1743 tr.close()
1736 tr.close()
1744 finally:
1737 finally:
1745 if tr is not None:
1738 if tr is not None:
1746 tr.release()
1739 tr.release()
1747 lock.release()
1740 lock.release()
1748
1741
1749 return result
1742 return result
1750
1743
1751 def checkpush(self, force, revs):
1744 def checkpush(self, force, revs):
1752 """Extensions can override this function if additional checks have
1745 """Extensions can override this function if additional checks have
1753 to be performed before pushing, or call it if they override push
1746 to be performed before pushing, or call it if they override push
1754 command.
1747 command.
1755 """
1748 """
1756 pass
1749 pass
1757
1750
1758 def push(self, remote, force=False, revs=None, newbranch=False):
1751 def push(self, remote, force=False, revs=None, newbranch=False):
1759 '''Push outgoing changesets (limited by revs) from the current
1752 '''Push outgoing changesets (limited by revs) from the current
1760 repository to remote. Return an integer:
1753 repository to remote. Return an integer:
1761 - None means nothing to push
1754 - None means nothing to push
1762 - 0 means HTTP error
1755 - 0 means HTTP error
1763 - 1 means we pushed and remote head count is unchanged *or*
1756 - 1 means we pushed and remote head count is unchanged *or*
1764 we have outgoing changesets but refused to push
1757 we have outgoing changesets but refused to push
1765 - other values as described by addchangegroup()
1758 - other values as described by addchangegroup()
1766 '''
1759 '''
1767 # there are two ways to push to remote repo:
1760 # there are two ways to push to remote repo:
1768 #
1761 #
1769 # addchangegroup assumes local user can lock remote
1762 # addchangegroup assumes local user can lock remote
1770 # repo (local filesystem, old ssh servers).
1763 # repo (local filesystem, old ssh servers).
1771 #
1764 #
1772 # unbundle assumes local user cannot lock remote repo (new ssh
1765 # unbundle assumes local user cannot lock remote repo (new ssh
1773 # servers, http servers).
1766 # servers, http servers).
1774
1767
1775 if not remote.canpush():
1768 if not remote.canpush():
1776 raise util.Abort(_("destination does not support push"))
1769 raise util.Abort(_("destination does not support push"))
1777 unfi = self.unfiltered()
1770 unfi = self.unfiltered()
1778 # get local lock as we might write phase data
1771 # get local lock as we might write phase data
1779 locallock = self.lock()
1772 locallock = self.lock()
1780 try:
1773 try:
1781 self.checkpush(force, revs)
1774 self.checkpush(force, revs)
1782 lock = None
1775 lock = None
1783 unbundle = remote.capable('unbundle')
1776 unbundle = remote.capable('unbundle')
1784 if not unbundle:
1777 if not unbundle:
1785 lock = remote.lock()
1778 lock = remote.lock()
1786 try:
1779 try:
1787 # discovery
1780 # discovery
1788 fci = discovery.findcommonincoming
1781 fci = discovery.findcommonincoming
1789 commoninc = fci(unfi, remote, force=force)
1782 commoninc = fci(unfi, remote, force=force)
1790 common, inc, remoteheads = commoninc
1783 common, inc, remoteheads = commoninc
1791 fco = discovery.findcommonoutgoing
1784 fco = discovery.findcommonoutgoing
1792 outgoing = fco(unfi, remote, onlyheads=revs,
1785 outgoing = fco(unfi, remote, onlyheads=revs,
1793 commoninc=commoninc, force=force)
1786 commoninc=commoninc, force=force)
1794
1787
1795
1788
1796 if not outgoing.missing:
1789 if not outgoing.missing:
1797 # nothing to push
1790 # nothing to push
1798 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
1791 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
1799 ret = None
1792 ret = None
1800 else:
1793 else:
1801 # something to push
1794 # something to push
1802 if not force:
1795 if not force:
1803 # if self.obsstore == False --> no obsolete
1796 # if self.obsstore == False --> no obsolete
1804 # then, save the iteration
1797 # then, save the iteration
1805 if unfi.obsstore:
1798 if unfi.obsstore:
1806 # this message are here for 80 char limit reason
1799 # this message are here for 80 char limit reason
1807 mso = _("push includes obsolete changeset: %s!")
1800 mso = _("push includes obsolete changeset: %s!")
1808 mst = "push includes %s changeset: %s!"
1801 mst = "push includes %s changeset: %s!"
1809 # plain versions for i18n tool to detect them
1802 # plain versions for i18n tool to detect them
1810 _("push includes unstable changeset: %s!")
1803 _("push includes unstable changeset: %s!")
1811 _("push includes bumped changeset: %s!")
1804 _("push includes bumped changeset: %s!")
1812 _("push includes divergent changeset: %s!")
1805 _("push includes divergent changeset: %s!")
1813 # If we are to push if there is at least one
1806 # If we are to push if there is at least one
1814 # obsolete or unstable changeset in missing, at
1807 # obsolete or unstable changeset in missing, at
1815 # least one of the missinghead will be obsolete or
1808 # least one of the missinghead will be obsolete or
1816 # unstable. So checking heads only is ok
1809 # unstable. So checking heads only is ok
1817 for node in outgoing.missingheads:
1810 for node in outgoing.missingheads:
1818 ctx = unfi[node]
1811 ctx = unfi[node]
1819 if ctx.obsolete():
1812 if ctx.obsolete():
1820 raise util.Abort(mso % ctx)
1813 raise util.Abort(mso % ctx)
1821 elif ctx.troubled():
1814 elif ctx.troubled():
1822 raise util.Abort(_(mst)
1815 raise util.Abort(_(mst)
1823 % (ctx.troubles()[0],
1816 % (ctx.troubles()[0],
1824 ctx))
1817 ctx))
1825 discovery.checkheads(unfi, remote, outgoing,
1818 discovery.checkheads(unfi, remote, outgoing,
1826 remoteheads, newbranch,
1819 remoteheads, newbranch,
1827 bool(inc))
1820 bool(inc))
1828
1821
1829 # create a changegroup from local
1822 # create a changegroup from local
1830 if revs is None and not outgoing.excluded:
1823 if revs is None and not outgoing.excluded:
1831 # push everything,
1824 # push everything,
1832 # use the fast path, no race possible on push
1825 # use the fast path, no race possible on push
1833 cg = self._changegroup(outgoing.missing, 'push')
1826 cg = self._changegroup(outgoing.missing, 'push')
1834 else:
1827 else:
1835 cg = self.getlocalbundle('push', outgoing)
1828 cg = self.getlocalbundle('push', outgoing)
1836
1829
1837 # apply changegroup to remote
1830 # apply changegroup to remote
1838 if unbundle:
1831 if unbundle:
1839 # local repo finds heads on server, finds out what
1832 # local repo finds heads on server, finds out what
1840 # revs it must push. once revs transferred, if server
1833 # revs it must push. once revs transferred, if server
1841 # finds it has different heads (someone else won
1834 # finds it has different heads (someone else won
1842 # commit/push race), server aborts.
1835 # commit/push race), server aborts.
1843 if force:
1836 if force:
1844 remoteheads = ['force']
1837 remoteheads = ['force']
1845 # ssh: return remote's addchangegroup()
1838 # ssh: return remote's addchangegroup()
1846 # http: return remote's addchangegroup() or 0 for error
1839 # http: return remote's addchangegroup() or 0 for error
1847 ret = remote.unbundle(cg, remoteheads, 'push')
1840 ret = remote.unbundle(cg, remoteheads, 'push')
1848 else:
1841 else:
1849 # we return an integer indicating remote head count
1842 # we return an integer indicating remote head count
1850 # change
1843 # change
1851 ret = remote.addchangegroup(cg, 'push', self.url())
1844 ret = remote.addchangegroup(cg, 'push', self.url())
1852
1845
1853 if ret:
1846 if ret:
1854 # push succeed, synchronize target of the push
1847 # push succeed, synchronize target of the push
1855 cheads = outgoing.missingheads
1848 cheads = outgoing.missingheads
1856 elif revs is None:
1849 elif revs is None:
1857 # All out push fails. synchronize all common
1850 # All out push fails. synchronize all common
1858 cheads = outgoing.commonheads
1851 cheads = outgoing.commonheads
1859 else:
1852 else:
1860 # I want cheads = heads(::missingheads and ::commonheads)
1853 # I want cheads = heads(::missingheads and ::commonheads)
1861 # (missingheads is revs with secret changeset filtered out)
1854 # (missingheads is revs with secret changeset filtered out)
1862 #
1855 #
1863 # This can be expressed as:
1856 # This can be expressed as:
1864 # cheads = ( (missingheads and ::commonheads)
1857 # cheads = ( (missingheads and ::commonheads)
1865 # + (commonheads and ::missingheads))"
1858 # + (commonheads and ::missingheads))"
1866 # )
1859 # )
1867 #
1860 #
1868 # while trying to push we already computed the following:
1861 # while trying to push we already computed the following:
1869 # common = (::commonheads)
1862 # common = (::commonheads)
1870 # missing = ((commonheads::missingheads) - commonheads)
1863 # missing = ((commonheads::missingheads) - commonheads)
1871 #
1864 #
1872 # We can pick:
1865 # We can pick:
1873 # * missingheads part of common (::commonheads)
1866 # * missingheads part of common (::commonheads)
1874 common = set(outgoing.common)
1867 common = set(outgoing.common)
1875 cheads = [node for node in revs if node in common]
1868 cheads = [node for node in revs if node in common]
1876 # and
1869 # and
1877 # * commonheads parents on missing
1870 # * commonheads parents on missing
1878 revset = unfi.set('%ln and parents(roots(%ln))',
1871 revset = unfi.set('%ln and parents(roots(%ln))',
1879 outgoing.commonheads,
1872 outgoing.commonheads,
1880 outgoing.missing)
1873 outgoing.missing)
1881 cheads.extend(c.node() for c in revset)
1874 cheads.extend(c.node() for c in revset)
1882 # even when we don't push, exchanging phase data is useful
1875 # even when we don't push, exchanging phase data is useful
1883 remotephases = remote.listkeys('phases')
1876 remotephases = remote.listkeys('phases')
1884 if not remotephases: # old server or public only repo
1877 if not remotephases: # old server or public only repo
1885 phases.advanceboundary(self, phases.public, cheads)
1878 phases.advanceboundary(self, phases.public, cheads)
1886 # don't push any phase data as there is nothing to push
1879 # don't push any phase data as there is nothing to push
1887 else:
1880 else:
1888 ana = phases.analyzeremotephases(self, cheads, remotephases)
1881 ana = phases.analyzeremotephases(self, cheads, remotephases)
1889 pheads, droots = ana
1882 pheads, droots = ana
1890 ### Apply remote phase on local
1883 ### Apply remote phase on local
1891 if remotephases.get('publishing', False):
1884 if remotephases.get('publishing', False):
1892 phases.advanceboundary(self, phases.public, cheads)
1885 phases.advanceboundary(self, phases.public, cheads)
1893 else: # publish = False
1886 else: # publish = False
1894 phases.advanceboundary(self, phases.public, pheads)
1887 phases.advanceboundary(self, phases.public, pheads)
1895 phases.advanceboundary(self, phases.draft, cheads)
1888 phases.advanceboundary(self, phases.draft, cheads)
1896 ### Apply local phase on remote
1889 ### Apply local phase on remote
1897
1890
1898 # Get the list of all revs draft on remote by public here.
1891 # Get the list of all revs draft on remote by public here.
1899 # XXX Beware that revset break if droots is not strictly
1892 # XXX Beware that revset break if droots is not strictly
1900 # XXX root we may want to ensure it is but it is costly
1893 # XXX root we may want to ensure it is but it is costly
1901 outdated = unfi.set('heads((%ln::%ln) and public())',
1894 outdated = unfi.set('heads((%ln::%ln) and public())',
1902 droots, cheads)
1895 droots, cheads)
1903 for newremotehead in outdated:
1896 for newremotehead in outdated:
1904 r = remote.pushkey('phases',
1897 r = remote.pushkey('phases',
1905 newremotehead.hex(),
1898 newremotehead.hex(),
1906 str(phases.draft),
1899 str(phases.draft),
1907 str(phases.public))
1900 str(phases.public))
1908 if not r:
1901 if not r:
1909 self.ui.warn(_('updating %s to public failed!\n')
1902 self.ui.warn(_('updating %s to public failed!\n')
1910 % newremotehead)
1903 % newremotehead)
1911 self.ui.debug('try to push obsolete markers to remote\n')
1904 self.ui.debug('try to push obsolete markers to remote\n')
1912 if (obsolete._enabled and self.obsstore and
1905 if (obsolete._enabled and self.obsstore and
1913 'obsolete' in remote.listkeys('namespaces')):
1906 'obsolete' in remote.listkeys('namespaces')):
1914 rslts = []
1907 rslts = []
1915 remotedata = self.listkeys('obsolete')
1908 remotedata = self.listkeys('obsolete')
1916 for key in sorted(remotedata, reverse=True):
1909 for key in sorted(remotedata, reverse=True):
1917 # reverse sort to ensure we end with dump0
1910 # reverse sort to ensure we end with dump0
1918 data = remotedata[key]
1911 data = remotedata[key]
1919 rslts.append(remote.pushkey('obsolete', key, '', data))
1912 rslts.append(remote.pushkey('obsolete', key, '', data))
1920 if [r for r in rslts if not r]:
1913 if [r for r in rslts if not r]:
1921 msg = _('failed to push some obsolete markers!\n')
1914 msg = _('failed to push some obsolete markers!\n')
1922 self.ui.warn(msg)
1915 self.ui.warn(msg)
1923 finally:
1916 finally:
1924 if lock is not None:
1917 if lock is not None:
1925 lock.release()
1918 lock.release()
1926 finally:
1919 finally:
1927 locallock.release()
1920 locallock.release()
1928
1921
1929 self.ui.debug("checking for updated bookmarks\n")
1922 self.ui.debug("checking for updated bookmarks\n")
1930 rb = remote.listkeys('bookmarks')
1923 rb = remote.listkeys('bookmarks')
1931 for k in rb.keys():
1924 for k in rb.keys():
1932 if k in unfi._bookmarks:
1925 if k in unfi._bookmarks:
1933 nr, nl = rb[k], hex(self._bookmarks[k])
1926 nr, nl = rb[k], hex(self._bookmarks[k])
1934 if nr in unfi:
1927 if nr in unfi:
1935 cr = unfi[nr]
1928 cr = unfi[nr]
1936 cl = unfi[nl]
1929 cl = unfi[nl]
1937 if bookmarks.validdest(unfi, cr, cl):
1930 if bookmarks.validdest(unfi, cr, cl):
1938 r = remote.pushkey('bookmarks', k, nr, nl)
1931 r = remote.pushkey('bookmarks', k, nr, nl)
1939 if r:
1932 if r:
1940 self.ui.status(_("updating bookmark %s\n") % k)
1933 self.ui.status(_("updating bookmark %s\n") % k)
1941 else:
1934 else:
1942 self.ui.warn(_('updating bookmark %s'
1935 self.ui.warn(_('updating bookmark %s'
1943 ' failed!\n') % k)
1936 ' failed!\n') % k)
1944
1937
1945 return ret
1938 return ret
1946
1939
1947 def changegroupinfo(self, nodes, source):
1940 def changegroupinfo(self, nodes, source):
1948 if self.ui.verbose or source == 'bundle':
1941 if self.ui.verbose or source == 'bundle':
1949 self.ui.status(_("%d changesets found\n") % len(nodes))
1942 self.ui.status(_("%d changesets found\n") % len(nodes))
1950 if self.ui.debugflag:
1943 if self.ui.debugflag:
1951 self.ui.debug("list of changesets:\n")
1944 self.ui.debug("list of changesets:\n")
1952 for node in nodes:
1945 for node in nodes:
1953 self.ui.debug("%s\n" % hex(node))
1946 self.ui.debug("%s\n" % hex(node))
1954
1947
1955 def changegroupsubset(self, bases, heads, source):
1948 def changegroupsubset(self, bases, heads, source):
1956 """Compute a changegroup consisting of all the nodes that are
1949 """Compute a changegroup consisting of all the nodes that are
1957 descendants of any of the bases and ancestors of any of the heads.
1950 descendants of any of the bases and ancestors of any of the heads.
1958 Return a chunkbuffer object whose read() method will return
1951 Return a chunkbuffer object whose read() method will return
1959 successive changegroup chunks.
1952 successive changegroup chunks.
1960
1953
1961 It is fairly complex as determining which filenodes and which
1954 It is fairly complex as determining which filenodes and which
1962 manifest nodes need to be included for the changeset to be complete
1955 manifest nodes need to be included for the changeset to be complete
1963 is non-trivial.
1956 is non-trivial.
1964
1957
1965 Another wrinkle is doing the reverse, figuring out which changeset in
1958 Another wrinkle is doing the reverse, figuring out which changeset in
1966 the changegroup a particular filenode or manifestnode belongs to.
1959 the changegroup a particular filenode or manifestnode belongs to.
1967 """
1960 """
1968 cl = self.changelog
1961 cl = self.changelog
1969 if not bases:
1962 if not bases:
1970 bases = [nullid]
1963 bases = [nullid]
1971 csets, bases, heads = cl.nodesbetween(bases, heads)
1964 csets, bases, heads = cl.nodesbetween(bases, heads)
1972 # We assume that all ancestors of bases are known
1965 # We assume that all ancestors of bases are known
1973 common = cl.ancestors([cl.rev(n) for n in bases])
1966 common = cl.ancestors([cl.rev(n) for n in bases])
1974 return self._changegroupsubset(common, csets, heads, source)
1967 return self._changegroupsubset(common, csets, heads, source)
1975
1968
1976 def getlocalbundle(self, source, outgoing):
1969 def getlocalbundle(self, source, outgoing):
1977 """Like getbundle, but taking a discovery.outgoing as an argument.
1970 """Like getbundle, but taking a discovery.outgoing as an argument.
1978
1971
1979 This is only implemented for local repos and reuses potentially
1972 This is only implemented for local repos and reuses potentially
1980 precomputed sets in outgoing."""
1973 precomputed sets in outgoing."""
1981 if not outgoing.missing:
1974 if not outgoing.missing:
1982 return None
1975 return None
1983 return self._changegroupsubset(outgoing.common,
1976 return self._changegroupsubset(outgoing.common,
1984 outgoing.missing,
1977 outgoing.missing,
1985 outgoing.missingheads,
1978 outgoing.missingheads,
1986 source)
1979 source)
1987
1980
1988 def getbundle(self, source, heads=None, common=None):
1981 def getbundle(self, source, heads=None, common=None):
1989 """Like changegroupsubset, but returns the set difference between the
1982 """Like changegroupsubset, but returns the set difference between the
1990 ancestors of heads and the ancestors common.
1983 ancestors of heads and the ancestors common.
1991
1984
1992 If heads is None, use the local heads. If common is None, use [nullid].
1985 If heads is None, use the local heads. If common is None, use [nullid].
1993
1986
1994 The nodes in common might not all be known locally due to the way the
1987 The nodes in common might not all be known locally due to the way the
1995 current discovery protocol works.
1988 current discovery protocol works.
1996 """
1989 """
1997 cl = self.changelog
1990 cl = self.changelog
1998 if common:
1991 if common:
1999 hasnode = cl.hasnode
1992 hasnode = cl.hasnode
2000 common = [n for n in common if hasnode(n)]
1993 common = [n for n in common if hasnode(n)]
2001 else:
1994 else:
2002 common = [nullid]
1995 common = [nullid]
2003 if not heads:
1996 if not heads:
2004 heads = cl.heads()
1997 heads = cl.heads()
2005 return self.getlocalbundle(source,
1998 return self.getlocalbundle(source,
2006 discovery.outgoing(cl, common, heads))
1999 discovery.outgoing(cl, common, heads))
2007
2000
2008 @unfilteredmethod
2001 @unfilteredmethod
2009 def _changegroupsubset(self, commonrevs, csets, heads, source):
2002 def _changegroupsubset(self, commonrevs, csets, heads, source):
2010
2003
2011 cl = self.changelog
2004 cl = self.changelog
2012 mf = self.manifest
2005 mf = self.manifest
2013 mfs = {} # needed manifests
2006 mfs = {} # needed manifests
2014 fnodes = {} # needed file nodes
2007 fnodes = {} # needed file nodes
2015 changedfiles = set()
2008 changedfiles = set()
2016 fstate = ['', {}]
2009 fstate = ['', {}]
2017 count = [0, 0]
2010 count = [0, 0]
2018
2011
2019 # can we go through the fast path ?
2012 # can we go through the fast path ?
2020 heads.sort()
2013 heads.sort()
2021 if heads == sorted(self.heads()):
2014 if heads == sorted(self.heads()):
2022 return self._changegroup(csets, source)
2015 return self._changegroup(csets, source)
2023
2016
2024 # slow path
2017 # slow path
2025 self.hook('preoutgoing', throw=True, source=source)
2018 self.hook('preoutgoing', throw=True, source=source)
2026 self.changegroupinfo(csets, source)
2019 self.changegroupinfo(csets, source)
2027
2020
2028 # filter any nodes that claim to be part of the known set
2021 # filter any nodes that claim to be part of the known set
2029 def prune(revlog, missing):
2022 def prune(revlog, missing):
2030 rr, rl = revlog.rev, revlog.linkrev
2023 rr, rl = revlog.rev, revlog.linkrev
2031 return [n for n in missing
2024 return [n for n in missing
2032 if rl(rr(n)) not in commonrevs]
2025 if rl(rr(n)) not in commonrevs]
2033
2026
2034 progress = self.ui.progress
2027 progress = self.ui.progress
2035 _bundling = _('bundling')
2028 _bundling = _('bundling')
2036 _changesets = _('changesets')
2029 _changesets = _('changesets')
2037 _manifests = _('manifests')
2030 _manifests = _('manifests')
2038 _files = _('files')
2031 _files = _('files')
2039
2032
2040 def lookup(revlog, x):
2033 def lookup(revlog, x):
2041 if revlog == cl:
2034 if revlog == cl:
2042 c = cl.read(x)
2035 c = cl.read(x)
2043 changedfiles.update(c[3])
2036 changedfiles.update(c[3])
2044 mfs.setdefault(c[0], x)
2037 mfs.setdefault(c[0], x)
2045 count[0] += 1
2038 count[0] += 1
2046 progress(_bundling, count[0],
2039 progress(_bundling, count[0],
2047 unit=_changesets, total=count[1])
2040 unit=_changesets, total=count[1])
2048 return x
2041 return x
2049 elif revlog == mf:
2042 elif revlog == mf:
2050 clnode = mfs[x]
2043 clnode = mfs[x]
2051 mdata = mf.readfast(x)
2044 mdata = mf.readfast(x)
2052 for f, n in mdata.iteritems():
2045 for f, n in mdata.iteritems():
2053 if f in changedfiles:
2046 if f in changedfiles:
2054 fnodes[f].setdefault(n, clnode)
2047 fnodes[f].setdefault(n, clnode)
2055 count[0] += 1
2048 count[0] += 1
2056 progress(_bundling, count[0],
2049 progress(_bundling, count[0],
2057 unit=_manifests, total=count[1])
2050 unit=_manifests, total=count[1])
2058 return clnode
2051 return clnode
2059 else:
2052 else:
2060 progress(_bundling, count[0], item=fstate[0],
2053 progress(_bundling, count[0], item=fstate[0],
2061 unit=_files, total=count[1])
2054 unit=_files, total=count[1])
2062 return fstate[1][x]
2055 return fstate[1][x]
2063
2056
2064 bundler = changegroup.bundle10(lookup)
2057 bundler = changegroup.bundle10(lookup)
2065 reorder = self.ui.config('bundle', 'reorder', 'auto')
2058 reorder = self.ui.config('bundle', 'reorder', 'auto')
2066 if reorder == 'auto':
2059 if reorder == 'auto':
2067 reorder = None
2060 reorder = None
2068 else:
2061 else:
2069 reorder = util.parsebool(reorder)
2062 reorder = util.parsebool(reorder)
2070
2063
2071 def gengroup():
2064 def gengroup():
2072 # Create a changenode group generator that will call our functions
2065 # Create a changenode group generator that will call our functions
2073 # back to lookup the owning changenode and collect information.
2066 # back to lookup the owning changenode and collect information.
2074 count[:] = [0, len(csets)]
2067 count[:] = [0, len(csets)]
2075 for chunk in cl.group(csets, bundler, reorder=reorder):
2068 for chunk in cl.group(csets, bundler, reorder=reorder):
2076 yield chunk
2069 yield chunk
2077 progress(_bundling, None)
2070 progress(_bundling, None)
2078
2071
2079 # Create a generator for the manifestnodes that calls our lookup
2072 # Create a generator for the manifestnodes that calls our lookup
2080 # and data collection functions back.
2073 # and data collection functions back.
2081 for f in changedfiles:
2074 for f in changedfiles:
2082 fnodes[f] = {}
2075 fnodes[f] = {}
2083 count[:] = [0, len(mfs)]
2076 count[:] = [0, len(mfs)]
2084 for chunk in mf.group(prune(mf, mfs), bundler, reorder=reorder):
2077 for chunk in mf.group(prune(mf, mfs), bundler, reorder=reorder):
2085 yield chunk
2078 yield chunk
2086 progress(_bundling, None)
2079 progress(_bundling, None)
2087
2080
2088 mfs.clear()
2081 mfs.clear()
2089
2082
2090 # Go through all our files in order sorted by name.
2083 # Go through all our files in order sorted by name.
2091 count[:] = [0, len(changedfiles)]
2084 count[:] = [0, len(changedfiles)]
2092 for fname in sorted(changedfiles):
2085 for fname in sorted(changedfiles):
2093 filerevlog = self.file(fname)
2086 filerevlog = self.file(fname)
2094 if not len(filerevlog):
2087 if not len(filerevlog):
2095 raise util.Abort(_("empty or missing revlog for %s")
2088 raise util.Abort(_("empty or missing revlog for %s")
2096 % fname)
2089 % fname)
2097 fstate[0] = fname
2090 fstate[0] = fname
2098 fstate[1] = fnodes.pop(fname, {})
2091 fstate[1] = fnodes.pop(fname, {})
2099
2092
2100 nodelist = prune(filerevlog, fstate[1])
2093 nodelist = prune(filerevlog, fstate[1])
2101 if nodelist:
2094 if nodelist:
2102 count[0] += 1
2095 count[0] += 1
2103 yield bundler.fileheader(fname)
2096 yield bundler.fileheader(fname)
2104 for chunk in filerevlog.group(nodelist, bundler, reorder):
2097 for chunk in filerevlog.group(nodelist, bundler, reorder):
2105 yield chunk
2098 yield chunk
2106
2099
2107 # Signal that no more groups are left.
2100 # Signal that no more groups are left.
2108 yield bundler.close()
2101 yield bundler.close()
2109 progress(_bundling, None)
2102 progress(_bundling, None)
2110
2103
2111 if csets:
2104 if csets:
2112 self.hook('outgoing', node=hex(csets[0]), source=source)
2105 self.hook('outgoing', node=hex(csets[0]), source=source)
2113
2106
2114 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
2107 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
2115
2108
2116 def changegroup(self, basenodes, source):
2109 def changegroup(self, basenodes, source):
2117 # to avoid a race we use changegroupsubset() (issue1320)
2110 # to avoid a race we use changegroupsubset() (issue1320)
2118 return self.changegroupsubset(basenodes, self.heads(), source)
2111 return self.changegroupsubset(basenodes, self.heads(), source)
2119
2112
2120 @unfilteredmethod
2113 @unfilteredmethod
2121 def _changegroup(self, nodes, source):
2114 def _changegroup(self, nodes, source):
2122 """Compute the changegroup of all nodes that we have that a recipient
2115 """Compute the changegroup of all nodes that we have that a recipient
2123 doesn't. Return a chunkbuffer object whose read() method will return
2116 doesn't. Return a chunkbuffer object whose read() method will return
2124 successive changegroup chunks.
2117 successive changegroup chunks.
2125
2118
2126 This is much easier than the previous function as we can assume that
2119 This is much easier than the previous function as we can assume that
2127 the recipient has any changenode we aren't sending them.
2120 the recipient has any changenode we aren't sending them.
2128
2121
2129 nodes is the set of nodes to send"""
2122 nodes is the set of nodes to send"""
2130
2123
2131 cl = self.changelog
2124 cl = self.changelog
2132 mf = self.manifest
2125 mf = self.manifest
2133 mfs = {}
2126 mfs = {}
2134 changedfiles = set()
2127 changedfiles = set()
2135 fstate = ['']
2128 fstate = ['']
2136 count = [0, 0]
2129 count = [0, 0]
2137
2130
2138 self.hook('preoutgoing', throw=True, source=source)
2131 self.hook('preoutgoing', throw=True, source=source)
2139 self.changegroupinfo(nodes, source)
2132 self.changegroupinfo(nodes, source)
2140
2133
2141 revset = set([cl.rev(n) for n in nodes])
2134 revset = set([cl.rev(n) for n in nodes])
2142
2135
2143 def gennodelst(log):
2136 def gennodelst(log):
2144 ln, llr = log.node, log.linkrev
2137 ln, llr = log.node, log.linkrev
2145 return [ln(r) for r in log if llr(r) in revset]
2138 return [ln(r) for r in log if llr(r) in revset]
2146
2139
2147 progress = self.ui.progress
2140 progress = self.ui.progress
2148 _bundling = _('bundling')
2141 _bundling = _('bundling')
2149 _changesets = _('changesets')
2142 _changesets = _('changesets')
2150 _manifests = _('manifests')
2143 _manifests = _('manifests')
2151 _files = _('files')
2144 _files = _('files')
2152
2145
2153 def lookup(revlog, x):
2146 def lookup(revlog, x):
2154 if revlog == cl:
2147 if revlog == cl:
2155 c = cl.read(x)
2148 c = cl.read(x)
2156 changedfiles.update(c[3])
2149 changedfiles.update(c[3])
2157 mfs.setdefault(c[0], x)
2150 mfs.setdefault(c[0], x)
2158 count[0] += 1
2151 count[0] += 1
2159 progress(_bundling, count[0],
2152 progress(_bundling, count[0],
2160 unit=_changesets, total=count[1])
2153 unit=_changesets, total=count[1])
2161 return x
2154 return x
2162 elif revlog == mf:
2155 elif revlog == mf:
2163 count[0] += 1
2156 count[0] += 1
2164 progress(_bundling, count[0],
2157 progress(_bundling, count[0],
2165 unit=_manifests, total=count[1])
2158 unit=_manifests, total=count[1])
2166 return cl.node(revlog.linkrev(revlog.rev(x)))
2159 return cl.node(revlog.linkrev(revlog.rev(x)))
2167 else:
2160 else:
2168 progress(_bundling, count[0], item=fstate[0],
2161 progress(_bundling, count[0], item=fstate[0],
2169 total=count[1], unit=_files)
2162 total=count[1], unit=_files)
2170 return cl.node(revlog.linkrev(revlog.rev(x)))
2163 return cl.node(revlog.linkrev(revlog.rev(x)))
2171
2164
2172 bundler = changegroup.bundle10(lookup)
2165 bundler = changegroup.bundle10(lookup)
2173 reorder = self.ui.config('bundle', 'reorder', 'auto')
2166 reorder = self.ui.config('bundle', 'reorder', 'auto')
2174 if reorder == 'auto':
2167 if reorder == 'auto':
2175 reorder = None
2168 reorder = None
2176 else:
2169 else:
2177 reorder = util.parsebool(reorder)
2170 reorder = util.parsebool(reorder)
2178
2171
2179 def gengroup():
2172 def gengroup():
2180 '''yield a sequence of changegroup chunks (strings)'''
2173 '''yield a sequence of changegroup chunks (strings)'''
2181 # construct a list of all changed files
2174 # construct a list of all changed files
2182
2175
2183 count[:] = [0, len(nodes)]
2176 count[:] = [0, len(nodes)]
2184 for chunk in cl.group(nodes, bundler, reorder=reorder):
2177 for chunk in cl.group(nodes, bundler, reorder=reorder):
2185 yield chunk
2178 yield chunk
2186 progress(_bundling, None)
2179 progress(_bundling, None)
2187
2180
2188 count[:] = [0, len(mfs)]
2181 count[:] = [0, len(mfs)]
2189 for chunk in mf.group(gennodelst(mf), bundler, reorder=reorder):
2182 for chunk in mf.group(gennodelst(mf), bundler, reorder=reorder):
2190 yield chunk
2183 yield chunk
2191 progress(_bundling, None)
2184 progress(_bundling, None)
2192
2185
2193 count[:] = [0, len(changedfiles)]
2186 count[:] = [0, len(changedfiles)]
2194 for fname in sorted(changedfiles):
2187 for fname in sorted(changedfiles):
2195 filerevlog = self.file(fname)
2188 filerevlog = self.file(fname)
2196 if not len(filerevlog):
2189 if not len(filerevlog):
2197 raise util.Abort(_("empty or missing revlog for %s")
2190 raise util.Abort(_("empty or missing revlog for %s")
2198 % fname)
2191 % fname)
2199 fstate[0] = fname
2192 fstate[0] = fname
2200 nodelist = gennodelst(filerevlog)
2193 nodelist = gennodelst(filerevlog)
2201 if nodelist:
2194 if nodelist:
2202 count[0] += 1
2195 count[0] += 1
2203 yield bundler.fileheader(fname)
2196 yield bundler.fileheader(fname)
2204 for chunk in filerevlog.group(nodelist, bundler, reorder):
2197 for chunk in filerevlog.group(nodelist, bundler, reorder):
2205 yield chunk
2198 yield chunk
2206 yield bundler.close()
2199 yield bundler.close()
2207 progress(_bundling, None)
2200 progress(_bundling, None)
2208
2201
2209 if nodes:
2202 if nodes:
2210 self.hook('outgoing', node=hex(nodes[0]), source=source)
2203 self.hook('outgoing', node=hex(nodes[0]), source=source)
2211
2204
2212 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
2205 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
2213
2206
2214 @unfilteredmethod
2207 @unfilteredmethod
2215 def addchangegroup(self, source, srctype, url, emptyok=False):
2208 def addchangegroup(self, source, srctype, url, emptyok=False):
2216 """Add the changegroup returned by source.read() to this repo.
2209 """Add the changegroup returned by source.read() to this repo.
2217 srctype is a string like 'push', 'pull', or 'unbundle'. url is
2210 srctype is a string like 'push', 'pull', or 'unbundle'. url is
2218 the URL of the repo where this changegroup is coming from.
2211 the URL of the repo where this changegroup is coming from.
2219
2212
2220 Return an integer summarizing the change to this repo:
2213 Return an integer summarizing the change to this repo:
2221 - nothing changed or no source: 0
2214 - nothing changed or no source: 0
2222 - more heads than before: 1+added heads (2..n)
2215 - more heads than before: 1+added heads (2..n)
2223 - fewer heads than before: -1-removed heads (-2..-n)
2216 - fewer heads than before: -1-removed heads (-2..-n)
2224 - number of heads stays the same: 1
2217 - number of heads stays the same: 1
2225 """
2218 """
2226 def csmap(x):
2219 def csmap(x):
2227 self.ui.debug("add changeset %s\n" % short(x))
2220 self.ui.debug("add changeset %s\n" % short(x))
2228 return len(cl)
2221 return len(cl)
2229
2222
2230 def revmap(x):
2223 def revmap(x):
2231 return cl.rev(x)
2224 return cl.rev(x)
2232
2225
2233 if not source:
2226 if not source:
2234 return 0
2227 return 0
2235
2228
2236 self.hook('prechangegroup', throw=True, source=srctype, url=url)
2229 self.hook('prechangegroup', throw=True, source=srctype, url=url)
2237
2230
2238 changesets = files = revisions = 0
2231 changesets = files = revisions = 0
2239 efiles = set()
2232 efiles = set()
2240
2233
2241 # write changelog data to temp files so concurrent readers will not see
2234 # write changelog data to temp files so concurrent readers will not see
2242 # inconsistent view
2235 # inconsistent view
2243 cl = self.changelog
2236 cl = self.changelog
2244 cl.delayupdate()
2237 cl.delayupdate()
2245 oldheads = cl.heads()
2238 oldheads = cl.heads()
2246
2239
2247 tr = self.transaction("\n".join([srctype, util.hidepassword(url)]))
2240 tr = self.transaction("\n".join([srctype, util.hidepassword(url)]))
2248 try:
2241 try:
2249 trp = weakref.proxy(tr)
2242 trp = weakref.proxy(tr)
2250 # pull off the changeset group
2243 # pull off the changeset group
2251 self.ui.status(_("adding changesets\n"))
2244 self.ui.status(_("adding changesets\n"))
2252 clstart = len(cl)
2245 clstart = len(cl)
2253 class prog(object):
2246 class prog(object):
2254 step = _('changesets')
2247 step = _('changesets')
2255 count = 1
2248 count = 1
2256 ui = self.ui
2249 ui = self.ui
2257 total = None
2250 total = None
2258 def __call__(self):
2251 def __call__(self):
2259 self.ui.progress(self.step, self.count, unit=_('chunks'),
2252 self.ui.progress(self.step, self.count, unit=_('chunks'),
2260 total=self.total)
2253 total=self.total)
2261 self.count += 1
2254 self.count += 1
2262 pr = prog()
2255 pr = prog()
2263 source.callback = pr
2256 source.callback = pr
2264
2257
2265 source.changelogheader()
2258 source.changelogheader()
2266 srccontent = cl.addgroup(source, csmap, trp)
2259 srccontent = cl.addgroup(source, csmap, trp)
2267 if not (srccontent or emptyok):
2260 if not (srccontent or emptyok):
2268 raise util.Abort(_("received changelog group is empty"))
2261 raise util.Abort(_("received changelog group is empty"))
2269 clend = len(cl)
2262 clend = len(cl)
2270 changesets = clend - clstart
2263 changesets = clend - clstart
2271 for c in xrange(clstart, clend):
2264 for c in xrange(clstart, clend):
2272 efiles.update(self[c].files())
2265 efiles.update(self[c].files())
2273 efiles = len(efiles)
2266 efiles = len(efiles)
2274 self.ui.progress(_('changesets'), None)
2267 self.ui.progress(_('changesets'), None)
2275
2268
2276 # pull off the manifest group
2269 # pull off the manifest group
2277 self.ui.status(_("adding manifests\n"))
2270 self.ui.status(_("adding manifests\n"))
2278 pr.step = _('manifests')
2271 pr.step = _('manifests')
2279 pr.count = 1
2272 pr.count = 1
2280 pr.total = changesets # manifests <= changesets
2273 pr.total = changesets # manifests <= changesets
2281 # no need to check for empty manifest group here:
2274 # no need to check for empty manifest group here:
2282 # if the result of the merge of 1 and 2 is the same in 3 and 4,
2275 # if the result of the merge of 1 and 2 is the same in 3 and 4,
2283 # no new manifest will be created and the manifest group will
2276 # no new manifest will be created and the manifest group will
2284 # be empty during the pull
2277 # be empty during the pull
2285 source.manifestheader()
2278 source.manifestheader()
2286 self.manifest.addgroup(source, revmap, trp)
2279 self.manifest.addgroup(source, revmap, trp)
2287 self.ui.progress(_('manifests'), None)
2280 self.ui.progress(_('manifests'), None)
2288
2281
2289 needfiles = {}
2282 needfiles = {}
2290 if self.ui.configbool('server', 'validate', default=False):
2283 if self.ui.configbool('server', 'validate', default=False):
2291 # validate incoming csets have their manifests
2284 # validate incoming csets have their manifests
2292 for cset in xrange(clstart, clend):
2285 for cset in xrange(clstart, clend):
2293 mfest = self.changelog.read(self.changelog.node(cset))[0]
2286 mfest = self.changelog.read(self.changelog.node(cset))[0]
2294 mfest = self.manifest.readdelta(mfest)
2287 mfest = self.manifest.readdelta(mfest)
2295 # store file nodes we must see
2288 # store file nodes we must see
2296 for f, n in mfest.iteritems():
2289 for f, n in mfest.iteritems():
2297 needfiles.setdefault(f, set()).add(n)
2290 needfiles.setdefault(f, set()).add(n)
2298
2291
2299 # process the files
2292 # process the files
2300 self.ui.status(_("adding file changes\n"))
2293 self.ui.status(_("adding file changes\n"))
2301 pr.step = _('files')
2294 pr.step = _('files')
2302 pr.count = 1
2295 pr.count = 1
2303 pr.total = efiles
2296 pr.total = efiles
2304 source.callback = None
2297 source.callback = None
2305
2298
2306 while True:
2299 while True:
2307 chunkdata = source.filelogheader()
2300 chunkdata = source.filelogheader()
2308 if not chunkdata:
2301 if not chunkdata:
2309 break
2302 break
2310 f = chunkdata["filename"]
2303 f = chunkdata["filename"]
2311 self.ui.debug("adding %s revisions\n" % f)
2304 self.ui.debug("adding %s revisions\n" % f)
2312 pr()
2305 pr()
2313 fl = self.file(f)
2306 fl = self.file(f)
2314 o = len(fl)
2307 o = len(fl)
2315 if not fl.addgroup(source, revmap, trp):
2308 if not fl.addgroup(source, revmap, trp):
2316 raise util.Abort(_("received file revlog group is empty"))
2309 raise util.Abort(_("received file revlog group is empty"))
2317 revisions += len(fl) - o
2310 revisions += len(fl) - o
2318 files += 1
2311 files += 1
2319 if f in needfiles:
2312 if f in needfiles:
2320 needs = needfiles[f]
2313 needs = needfiles[f]
2321 for new in xrange(o, len(fl)):
2314 for new in xrange(o, len(fl)):
2322 n = fl.node(new)
2315 n = fl.node(new)
2323 if n in needs:
2316 if n in needs:
2324 needs.remove(n)
2317 needs.remove(n)
2325 if not needs:
2318 if not needs:
2326 del needfiles[f]
2319 del needfiles[f]
2327 self.ui.progress(_('files'), None)
2320 self.ui.progress(_('files'), None)
2328
2321
2329 for f, needs in needfiles.iteritems():
2322 for f, needs in needfiles.iteritems():
2330 fl = self.file(f)
2323 fl = self.file(f)
2331 for n in needs:
2324 for n in needs:
2332 try:
2325 try:
2333 fl.rev(n)
2326 fl.rev(n)
2334 except error.LookupError:
2327 except error.LookupError:
2335 raise util.Abort(
2328 raise util.Abort(
2336 _('missing file data for %s:%s - run hg verify') %
2329 _('missing file data for %s:%s - run hg verify') %
2337 (f, hex(n)))
2330 (f, hex(n)))
2338
2331
2339 dh = 0
2332 dh = 0
2340 if oldheads:
2333 if oldheads:
2341 heads = cl.heads()
2334 heads = cl.heads()
2342 dh = len(heads) - len(oldheads)
2335 dh = len(heads) - len(oldheads)
2343 for h in heads:
2336 for h in heads:
2344 if h not in oldheads and self[h].closesbranch():
2337 if h not in oldheads and self[h].closesbranch():
2345 dh -= 1
2338 dh -= 1
2346 htext = ""
2339 htext = ""
2347 if dh:
2340 if dh:
2348 htext = _(" (%+d heads)") % dh
2341 htext = _(" (%+d heads)") % dh
2349
2342
2350 self.ui.status(_("added %d changesets"
2343 self.ui.status(_("added %d changesets"
2351 " with %d changes to %d files%s\n")
2344 " with %d changes to %d files%s\n")
2352 % (changesets, revisions, files, htext))
2345 % (changesets, revisions, files, htext))
2353 self.invalidatevolatilesets()
2346 self.invalidatevolatilesets()
2354
2347
2355 if changesets > 0:
2348 if changesets > 0:
2356 p = lambda: cl.writepending() and self.root or ""
2349 p = lambda: cl.writepending() and self.root or ""
2357 self.hook('pretxnchangegroup', throw=True,
2350 self.hook('pretxnchangegroup', throw=True,
2358 node=hex(cl.node(clstart)), source=srctype,
2351 node=hex(cl.node(clstart)), source=srctype,
2359 url=url, pending=p)
2352 url=url, pending=p)
2360
2353
2361 added = [cl.node(r) for r in xrange(clstart, clend)]
2354 added = [cl.node(r) for r in xrange(clstart, clend)]
2362 publishing = self.ui.configbool('phases', 'publish', True)
2355 publishing = self.ui.configbool('phases', 'publish', True)
2363 if srctype == 'push':
2356 if srctype == 'push':
2364 # Old server can not push the boundary themself.
2357 # Old server can not push the boundary themself.
2365 # New server won't push the boundary if changeset already
2358 # New server won't push the boundary if changeset already
2366 # existed locally as secrete
2359 # existed locally as secrete
2367 #
2360 #
2368 # We should not use added here but the list of all change in
2361 # We should not use added here but the list of all change in
2369 # the bundle
2362 # the bundle
2370 if publishing:
2363 if publishing:
2371 phases.advanceboundary(self, phases.public, srccontent)
2364 phases.advanceboundary(self, phases.public, srccontent)
2372 else:
2365 else:
2373 phases.advanceboundary(self, phases.draft, srccontent)
2366 phases.advanceboundary(self, phases.draft, srccontent)
2374 phases.retractboundary(self, phases.draft, added)
2367 phases.retractboundary(self, phases.draft, added)
2375 elif srctype != 'strip':
2368 elif srctype != 'strip':
2376 # publishing only alter behavior during push
2369 # publishing only alter behavior during push
2377 #
2370 #
2378 # strip should not touch boundary at all
2371 # strip should not touch boundary at all
2379 phases.retractboundary(self, phases.draft, added)
2372 phases.retractboundary(self, phases.draft, added)
2380
2373
2381 # make changelog see real files again
2374 # make changelog see real files again
2382 cl.finalize(trp)
2375 cl.finalize(trp)
2383
2376
2384 tr.close()
2377 tr.close()
2385
2378
2386 if changesets > 0:
2379 if changesets > 0:
2387 if srctype != 'strip':
2380 if srctype != 'strip':
2388 # During strip, branchcache is invalid but coming call to
2381 # During strip, branchcache is invalid but coming call to
2389 # `destroyed` will repair it.
2382 # `destroyed` will repair it.
2390 # In other case we can safely update cache on disk.
2383 # In other case we can safely update cache on disk.
2391 branchmap.updatecache(self.filtered('served'))
2384 branchmap.updatecache(self.filtered('served'))
2392 def runhooks():
2385 def runhooks():
2393 # forcefully update the on-disk branch cache
2386 # forcefully update the on-disk branch cache
2394 self.ui.debug("updating the branch cache\n")
2387 self.ui.debug("updating the branch cache\n")
2395 self.hook("changegroup", node=hex(cl.node(clstart)),
2388 self.hook("changegroup", node=hex(cl.node(clstart)),
2396 source=srctype, url=url)
2389 source=srctype, url=url)
2397
2390
2398 for n in added:
2391 for n in added:
2399 self.hook("incoming", node=hex(n), source=srctype,
2392 self.hook("incoming", node=hex(n), source=srctype,
2400 url=url)
2393 url=url)
2401 self._afterlock(runhooks)
2394 self._afterlock(runhooks)
2402
2395
2403 finally:
2396 finally:
2404 tr.release()
2397 tr.release()
2405 # never return 0 here:
2398 # never return 0 here:
2406 if dh < 0:
2399 if dh < 0:
2407 return dh - 1
2400 return dh - 1
2408 else:
2401 else:
2409 return dh + 1
2402 return dh + 1
2410
2403
2411 def stream_in(self, remote, requirements):
2404 def stream_in(self, remote, requirements):
2412 lock = self.lock()
2405 lock = self.lock()
2413 try:
2406 try:
2414 # Save remote branchmap. We will use it later
2407 # Save remote branchmap. We will use it later
2415 # to speed up branchcache creation
2408 # to speed up branchcache creation
2416 rbranchmap = None
2409 rbranchmap = None
2417 if remote.capable("branchmap"):
2410 if remote.capable("branchmap"):
2418 rbranchmap = remote.branchmap()
2411 rbranchmap = remote.branchmap()
2419
2412
2420 fp = remote.stream_out()
2413 fp = remote.stream_out()
2421 l = fp.readline()
2414 l = fp.readline()
2422 try:
2415 try:
2423 resp = int(l)
2416 resp = int(l)
2424 except ValueError:
2417 except ValueError:
2425 raise error.ResponseError(
2418 raise error.ResponseError(
2426 _('unexpected response from remote server:'), l)
2419 _('unexpected response from remote server:'), l)
2427 if resp == 1:
2420 if resp == 1:
2428 raise util.Abort(_('operation forbidden by server'))
2421 raise util.Abort(_('operation forbidden by server'))
2429 elif resp == 2:
2422 elif resp == 2:
2430 raise util.Abort(_('locking the remote repository failed'))
2423 raise util.Abort(_('locking the remote repository failed'))
2431 elif resp != 0:
2424 elif resp != 0:
2432 raise util.Abort(_('the server sent an unknown error code'))
2425 raise util.Abort(_('the server sent an unknown error code'))
2433 self.ui.status(_('streaming all changes\n'))
2426 self.ui.status(_('streaming all changes\n'))
2434 l = fp.readline()
2427 l = fp.readline()
2435 try:
2428 try:
2436 total_files, total_bytes = map(int, l.split(' ', 1))
2429 total_files, total_bytes = map(int, l.split(' ', 1))
2437 except (ValueError, TypeError):
2430 except (ValueError, TypeError):
2438 raise error.ResponseError(
2431 raise error.ResponseError(
2439 _('unexpected response from remote server:'), l)
2432 _('unexpected response from remote server:'), l)
2440 self.ui.status(_('%d files to transfer, %s of data\n') %
2433 self.ui.status(_('%d files to transfer, %s of data\n') %
2441 (total_files, util.bytecount(total_bytes)))
2434 (total_files, util.bytecount(total_bytes)))
2442 handled_bytes = 0
2435 handled_bytes = 0
2443 self.ui.progress(_('clone'), 0, total=total_bytes)
2436 self.ui.progress(_('clone'), 0, total=total_bytes)
2444 start = time.time()
2437 start = time.time()
2445 for i in xrange(total_files):
2438 for i in xrange(total_files):
2446 # XXX doesn't support '\n' or '\r' in filenames
2439 # XXX doesn't support '\n' or '\r' in filenames
2447 l = fp.readline()
2440 l = fp.readline()
2448 try:
2441 try:
2449 name, size = l.split('\0', 1)
2442 name, size = l.split('\0', 1)
2450 size = int(size)
2443 size = int(size)
2451 except (ValueError, TypeError):
2444 except (ValueError, TypeError):
2452 raise error.ResponseError(
2445 raise error.ResponseError(
2453 _('unexpected response from remote server:'), l)
2446 _('unexpected response from remote server:'), l)
2454 if self.ui.debugflag:
2447 if self.ui.debugflag:
2455 self.ui.debug('adding %s (%s)\n' %
2448 self.ui.debug('adding %s (%s)\n' %
2456 (name, util.bytecount(size)))
2449 (name, util.bytecount(size)))
2457 # for backwards compat, name was partially encoded
2450 # for backwards compat, name was partially encoded
2458 ofp = self.sopener(store.decodedir(name), 'w')
2451 ofp = self.sopener(store.decodedir(name), 'w')
2459 for chunk in util.filechunkiter(fp, limit=size):
2452 for chunk in util.filechunkiter(fp, limit=size):
2460 handled_bytes += len(chunk)
2453 handled_bytes += len(chunk)
2461 self.ui.progress(_('clone'), handled_bytes,
2454 self.ui.progress(_('clone'), handled_bytes,
2462 total=total_bytes)
2455 total=total_bytes)
2463 ofp.write(chunk)
2456 ofp.write(chunk)
2464 ofp.close()
2457 ofp.close()
2465 elapsed = time.time() - start
2458 elapsed = time.time() - start
2466 if elapsed <= 0:
2459 if elapsed <= 0:
2467 elapsed = 0.001
2460 elapsed = 0.001
2468 self.ui.progress(_('clone'), None)
2461 self.ui.progress(_('clone'), None)
2469 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2462 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2470 (util.bytecount(total_bytes), elapsed,
2463 (util.bytecount(total_bytes), elapsed,
2471 util.bytecount(total_bytes / elapsed)))
2464 util.bytecount(total_bytes / elapsed)))
2472
2465
2473 # new requirements = old non-format requirements +
2466 # new requirements = old non-format requirements +
2474 # new format-related
2467 # new format-related
2475 # requirements from the streamed-in repository
2468 # requirements from the streamed-in repository
2476 requirements.update(set(self.requirements) - self.supportedformats)
2469 requirements.update(set(self.requirements) - self.supportedformats)
2477 self._applyrequirements(requirements)
2470 self._applyrequirements(requirements)
2478 self._writerequirements()
2471 self._writerequirements()
2479
2472
2480 if rbranchmap:
2473 if rbranchmap:
2481 rbheads = []
2474 rbheads = []
2482 for bheads in rbranchmap.itervalues():
2475 for bheads in rbranchmap.itervalues():
2483 rbheads.extend(bheads)
2476 rbheads.extend(bheads)
2484
2477
2485 if rbheads:
2478 if rbheads:
2486 rtiprev = max((int(self.changelog.rev(node))
2479 rtiprev = max((int(self.changelog.rev(node))
2487 for node in rbheads))
2480 for node in rbheads))
2488 cache = branchmap.branchcache(rbranchmap,
2481 cache = branchmap.branchcache(rbranchmap,
2489 self[rtiprev].node(),
2482 self[rtiprev].node(),
2490 rtiprev)
2483 rtiprev)
2491 self._branchcaches[None] = cache
2484 self._branchcaches[None] = cache
2492 cache.write(self.unfiltered())
2485 cache.write(self.unfiltered())
2493 self.invalidate()
2486 self.invalidate()
2494 return len(self.heads()) + 1
2487 return len(self.heads()) + 1
2495 finally:
2488 finally:
2496 lock.release()
2489 lock.release()
2497
2490
2498 def clone(self, remote, heads=[], stream=False):
2491 def clone(self, remote, heads=[], stream=False):
2499 '''clone remote repository.
2492 '''clone remote repository.
2500
2493
2501 keyword arguments:
2494 keyword arguments:
2502 heads: list of revs to clone (forces use of pull)
2495 heads: list of revs to clone (forces use of pull)
2503 stream: use streaming clone if possible'''
2496 stream: use streaming clone if possible'''
2504
2497
2505 # now, all clients that can request uncompressed clones can
2498 # now, all clients that can request uncompressed clones can
2506 # read repo formats supported by all servers that can serve
2499 # read repo formats supported by all servers that can serve
2507 # them.
2500 # them.
2508
2501
2509 # if revlog format changes, client will have to check version
2502 # if revlog format changes, client will have to check version
2510 # and format flags on "stream" capability, and use
2503 # and format flags on "stream" capability, and use
2511 # uncompressed only if compatible.
2504 # uncompressed only if compatible.
2512
2505
2513 if not stream:
2506 if not stream:
2514 # if the server explicitly prefers to stream (for fast LANs)
2507 # if the server explicitly prefers to stream (for fast LANs)
2515 stream = remote.capable('stream-preferred')
2508 stream = remote.capable('stream-preferred')
2516
2509
2517 if stream and not heads:
2510 if stream and not heads:
2518 # 'stream' means remote revlog format is revlogv1 only
2511 # 'stream' means remote revlog format is revlogv1 only
2519 if remote.capable('stream'):
2512 if remote.capable('stream'):
2520 return self.stream_in(remote, set(('revlogv1',)))
2513 return self.stream_in(remote, set(('revlogv1',)))
2521 # otherwise, 'streamreqs' contains the remote revlog format
2514 # otherwise, 'streamreqs' contains the remote revlog format
2522 streamreqs = remote.capable('streamreqs')
2515 streamreqs = remote.capable('streamreqs')
2523 if streamreqs:
2516 if streamreqs:
2524 streamreqs = set(streamreqs.split(','))
2517 streamreqs = set(streamreqs.split(','))
2525 # if we support it, stream in and adjust our requirements
2518 # if we support it, stream in and adjust our requirements
2526 if not streamreqs - self.supportedformats:
2519 if not streamreqs - self.supportedformats:
2527 return self.stream_in(remote, streamreqs)
2520 return self.stream_in(remote, streamreqs)
2528 return self.pull(remote, heads)
2521 return self.pull(remote, heads)
2529
2522
2530 def pushkey(self, namespace, key, old, new):
2523 def pushkey(self, namespace, key, old, new):
2531 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
2524 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
2532 old=old, new=new)
2525 old=old, new=new)
2533 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
2526 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
2534 ret = pushkey.push(self, namespace, key, old, new)
2527 ret = pushkey.push(self, namespace, key, old, new)
2535 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
2528 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
2536 ret=ret)
2529 ret=ret)
2537 return ret
2530 return ret
2538
2531
2539 def listkeys(self, namespace):
2532 def listkeys(self, namespace):
2540 self.hook('prelistkeys', throw=True, namespace=namespace)
2533 self.hook('prelistkeys', throw=True, namespace=namespace)
2541 self.ui.debug('listing keys for "%s"\n' % namespace)
2534 self.ui.debug('listing keys for "%s"\n' % namespace)
2542 values = pushkey.list(self, namespace)
2535 values = pushkey.list(self, namespace)
2543 self.hook('listkeys', namespace=namespace, values=values)
2536 self.hook('listkeys', namespace=namespace, values=values)
2544 return values
2537 return values
2545
2538
2546 def debugwireargs(self, one, two, three=None, four=None, five=None):
2539 def debugwireargs(self, one, two, three=None, four=None, five=None):
2547 '''used to test argument passing over the wire'''
2540 '''used to test argument passing over the wire'''
2548 return "%s %s %s %s %s" % (one, two, three, four, five)
2541 return "%s %s %s %s %s" % (one, two, three, four, five)
2549
2542
2550 def savecommitmessage(self, text):
2543 def savecommitmessage(self, text):
2551 fp = self.opener('last-message.txt', 'wb')
2544 fp = self.opener('last-message.txt', 'wb')
2552 try:
2545 try:
2553 fp.write(text)
2546 fp.write(text)
2554 finally:
2547 finally:
2555 fp.close()
2548 fp.close()
2556 return self.pathto(fp.name[len(self.root) + 1:])
2549 return self.pathto(fp.name[len(self.root) + 1:])
2557
2550
2558 # used to avoid circular references so destructors work
2551 # used to avoid circular references so destructors work
2559 def aftertrans(files):
2552 def aftertrans(files):
2560 renamefiles = [tuple(t) for t in files]
2553 renamefiles = [tuple(t) for t in files]
2561 def a():
2554 def a():
2562 for src, dest in renamefiles:
2555 for src, dest in renamefiles:
2563 try:
2556 try:
2564 util.rename(src, dest)
2557 util.rename(src, dest)
2565 except OSError: # journal file does not yet exist
2558 except OSError: # journal file does not yet exist
2566 pass
2559 pass
2567 return a
2560 return a
2568
2561
2569 def undoname(fn):
2562 def undoname(fn):
2570 base, name = os.path.split(fn)
2563 base, name = os.path.split(fn)
2571 assert name.startswith('journal')
2564 assert name.startswith('journal')
2572 return os.path.join(base, name.replace('journal', 'undo', 1))
2565 return os.path.join(base, name.replace('journal', 'undo', 1))
2573
2566
2574 def instance(ui, path, create):
2567 def instance(ui, path, create):
2575 return localrepository(ui, util.urllocalpath(path), create)
2568 return localrepository(ui, util.urllocalpath(path), create)
2576
2569
2577 def islocal(path):
2570 def islocal(path):
2578 return True
2571 return True
@@ -1,200 +1,183 b''
1 # repair.py - functions for repository repair for mercurial
1 # repair.py - functions for repository repair for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 # Copyright 2007 Matt Mackall
4 # Copyright 2007 Matt Mackall
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from mercurial import changegroup
9 from mercurial import changegroup
10 from mercurial.node import short
10 from mercurial.node import short
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12 import os
12 import os
13 import errno
13 import errno
14
14
15 def _bundle(repo, bases, heads, node, suffix, compress=True):
15 def _bundle(repo, bases, heads, node, suffix, compress=True):
16 """create a bundle with the specified revisions as a backup"""
16 """create a bundle with the specified revisions as a backup"""
17 cg = repo.changegroupsubset(bases, heads, 'strip')
17 cg = repo.changegroupsubset(bases, heads, 'strip')
18 backupdir = repo.join("strip-backup")
18 backupdir = repo.join("strip-backup")
19 if not os.path.isdir(backupdir):
19 if not os.path.isdir(backupdir):
20 os.mkdir(backupdir)
20 os.mkdir(backupdir)
21 name = os.path.join(backupdir, "%s-%s.hg" % (short(node), suffix))
21 name = os.path.join(backupdir, "%s-%s.hg" % (short(node), suffix))
22 if compress:
22 if compress:
23 bundletype = "HG10BZ"
23 bundletype = "HG10BZ"
24 else:
24 else:
25 bundletype = "HG10UN"
25 bundletype = "HG10UN"
26 return changegroup.writebundle(cg, name, bundletype)
26 return changegroup.writebundle(cg, name, bundletype)
27
27
28 def _collectfiles(repo, striprev):
28 def _collectfiles(repo, striprev):
29 """find out the filelogs affected by the strip"""
29 """find out the filelogs affected by the strip"""
30 files = set()
30 files = set()
31
31
32 for x in xrange(striprev, len(repo)):
32 for x in xrange(striprev, len(repo)):
33 files.update(repo[x].files())
33 files.update(repo[x].files())
34
34
35 return sorted(files)
35 return sorted(files)
36
36
37 def _collectbrokencsets(repo, files, striprev):
37 def _collectbrokencsets(repo, files, striprev):
38 """return the changesets which will be broken by the truncation"""
38 """return the changesets which will be broken by the truncation"""
39 s = set()
39 s = set()
40 def collectone(revlog):
40 def collectone(revlog):
41 linkgen = (revlog.linkrev(i) for i in revlog)
41 linkgen = (revlog.linkrev(i) for i in revlog)
42 # find the truncation point of the revlog
42 # find the truncation point of the revlog
43 for lrev in linkgen:
43 for lrev in linkgen:
44 if lrev >= striprev:
44 if lrev >= striprev:
45 break
45 break
46 # see if any revision after this point has a linkrev
46 # see if any revision after this point has a linkrev
47 # less than striprev (those will be broken by strip)
47 # less than striprev (those will be broken by strip)
48 for lrev in linkgen:
48 for lrev in linkgen:
49 if lrev < striprev:
49 if lrev < striprev:
50 s.add(lrev)
50 s.add(lrev)
51
51
52 collectone(repo.manifest)
52 collectone(repo.manifest)
53 for fname in files:
53 for fname in files:
54 collectone(repo.file(fname))
54 collectone(repo.file(fname))
55
55
56 return s
56 return s
57
57
58 def strip(ui, repo, nodelist, backup="all", topic='backup'):
58 def strip(ui, repo, nodelist, backup="all", topic='backup'):
59 repo = repo.unfiltered()
59 repo = repo.unfiltered()
60 repo.destroying()
60 repo.destroying()
61
61
62 cl = repo.changelog
62 cl = repo.changelog
63 # TODO handle undo of merge sets
63 # TODO handle undo of merge sets
64 if isinstance(nodelist, str):
64 if isinstance(nodelist, str):
65 nodelist = [nodelist]
65 nodelist = [nodelist]
66 striplist = [cl.rev(node) for node in nodelist]
66 striplist = [cl.rev(node) for node in nodelist]
67 striprev = min(striplist)
67 striprev = min(striplist)
68
68
69 # Generate set of branches who will have nodes stripped.
70 striprevs = repo.revs("%ld::", striplist)
71 stripbranches = set([repo[rev].branch() for rev in striprevs])
72
73 # Set of potential new heads resulting from the strip. The parents of any
74 # node removed could be a new head because the node to be removed could have
75 # been the only child of the parent.
76 newheadrevs = repo.revs("parents(%ld::) - %ld::", striprevs, striprevs)
77 newheadnodes = set([cl.node(rev) for rev in newheadrevs])
78 newheadbranches = set([repo[rev].branch() for rev in newheadrevs])
79
80 keeppartialbundle = backup == 'strip'
69 keeppartialbundle = backup == 'strip'
81
70
82 # Some revisions with rev > striprev may not be descendants of striprev.
71 # Some revisions with rev > striprev may not be descendants of striprev.
83 # We have to find these revisions and put them in a bundle, so that
72 # We have to find these revisions and put them in a bundle, so that
84 # we can restore them after the truncations.
73 # we can restore them after the truncations.
85 # To create the bundle we use repo.changegroupsubset which requires
74 # To create the bundle we use repo.changegroupsubset which requires
86 # the list of heads and bases of the set of interesting revisions.
75 # the list of heads and bases of the set of interesting revisions.
87 # (head = revision in the set that has no descendant in the set;
76 # (head = revision in the set that has no descendant in the set;
88 # base = revision in the set that has no ancestor in the set)
77 # base = revision in the set that has no ancestor in the set)
89 tostrip = set(striplist)
78 tostrip = set(striplist)
90 for rev in striplist:
79 for rev in striplist:
91 for desc in cl.descendants([rev]):
80 for desc in cl.descendants([rev]):
92 tostrip.add(desc)
81 tostrip.add(desc)
93
82
94 files = _collectfiles(repo, striprev)
83 files = _collectfiles(repo, striprev)
95 saverevs = _collectbrokencsets(repo, files, striprev)
84 saverevs = _collectbrokencsets(repo, files, striprev)
96
85
97 # compute heads
86 # compute heads
98 saveheads = set(saverevs)
87 saveheads = set(saverevs)
99 for r in xrange(striprev + 1, len(cl)):
88 for r in xrange(striprev + 1, len(cl)):
100 if r not in tostrip:
89 if r not in tostrip:
101 saverevs.add(r)
90 saverevs.add(r)
102 saveheads.difference_update(cl.parentrevs(r))
91 saveheads.difference_update(cl.parentrevs(r))
103 saveheads.add(r)
92 saveheads.add(r)
104 saveheads = [cl.node(r) for r in saveheads]
93 saveheads = [cl.node(r) for r in saveheads]
105
94
106 # compute base nodes
95 # compute base nodes
107 if saverevs:
96 if saverevs:
108 descendants = set(cl.descendants(saverevs))
97 descendants = set(cl.descendants(saverevs))
109 saverevs.difference_update(descendants)
98 saverevs.difference_update(descendants)
110 savebases = [cl.node(r) for r in saverevs]
99 savebases = [cl.node(r) for r in saverevs]
111 stripbases = [cl.node(r) for r in tostrip]
100 stripbases = [cl.node(r) for r in tostrip]
112
101
113 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
102 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
114 # is much faster
103 # is much faster
115 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
104 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
116 if newbmtarget:
105 if newbmtarget:
117 newbmtarget = repo[newbmtarget[0]].node()
106 newbmtarget = repo[newbmtarget[0]].node()
118 else:
107 else:
119 newbmtarget = '.'
108 newbmtarget = '.'
120
109
121 bm = repo._bookmarks
110 bm = repo._bookmarks
122 updatebm = []
111 updatebm = []
123 for m in bm:
112 for m in bm:
124 rev = repo[bm[m]].rev()
113 rev = repo[bm[m]].rev()
125 if rev in tostrip:
114 if rev in tostrip:
126 updatebm.append(m)
115 updatebm.append(m)
127
116
128 # create a changegroup for all the branches we need to keep
117 # create a changegroup for all the branches we need to keep
129 backupfile = None
118 backupfile = None
130 if backup == "all":
119 if backup == "all":
131 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
120 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
132 repo.ui.status(_("saved backup bundle to %s\n") % backupfile)
121 repo.ui.status(_("saved backup bundle to %s\n") % backupfile)
133 if saveheads or savebases:
122 if saveheads or savebases:
134 # do not compress partial bundle if we remove it from disk later
123 # do not compress partial bundle if we remove it from disk later
135 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
124 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
136 compress=keeppartialbundle)
125 compress=keeppartialbundle)
137
126
138 mfst = repo.manifest
127 mfst = repo.manifest
139
128
140 tr = repo.transaction("strip")
129 tr = repo.transaction("strip")
141 offset = len(tr.entries)
130 offset = len(tr.entries)
142
131
143 try:
132 try:
144 tr.startgroup()
133 tr.startgroup()
145 cl.strip(striprev, tr)
134 cl.strip(striprev, tr)
146 mfst.strip(striprev, tr)
135 mfst.strip(striprev, tr)
147 for fn in files:
136 for fn in files:
148 repo.file(fn).strip(striprev, tr)
137 repo.file(fn).strip(striprev, tr)
149 tr.endgroup()
138 tr.endgroup()
150
139
151 try:
140 try:
152 for i in xrange(offset, len(tr.entries)):
141 for i in xrange(offset, len(tr.entries)):
153 file, troffset, ignore = tr.entries[i]
142 file, troffset, ignore = tr.entries[i]
154 repo.sopener(file, 'a').truncate(troffset)
143 repo.sopener(file, 'a').truncate(troffset)
155 tr.close()
144 tr.close()
156 except: # re-raises
145 except: # re-raises
157 tr.abort()
146 tr.abort()
158 raise
147 raise
159
148
160 if saveheads or savebases:
149 if saveheads or savebases:
161 ui.note(_("adding branch\n"))
150 ui.note(_("adding branch\n"))
162 f = open(chgrpfile, "rb")
151 f = open(chgrpfile, "rb")
163 gen = changegroup.readbundle(f, chgrpfile)
152 gen = changegroup.readbundle(f, chgrpfile)
164 if not repo.ui.verbose:
153 if not repo.ui.verbose:
165 # silence internal shuffling chatter
154 # silence internal shuffling chatter
166 repo.ui.pushbuffer()
155 repo.ui.pushbuffer()
167 repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True)
156 repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True)
168 if not repo.ui.verbose:
157 if not repo.ui.verbose:
169 repo.ui.popbuffer()
158 repo.ui.popbuffer()
170 f.close()
159 f.close()
171 if not keeppartialbundle:
160 if not keeppartialbundle:
172 os.unlink(chgrpfile)
161 os.unlink(chgrpfile)
173
162
174 # remove undo files
163 # remove undo files
175 for undofile in repo.undofiles():
164 for undofile in repo.undofiles():
176 try:
165 try:
177 os.unlink(undofile)
166 os.unlink(undofile)
178 except OSError, e:
167 except OSError, e:
179 if e.errno != errno.ENOENT:
168 if e.errno != errno.ENOENT:
180 ui.warn(_('error removing %s: %s\n') % (undofile, str(e)))
169 ui.warn(_('error removing %s: %s\n') % (undofile, str(e)))
181
170
182 for m in updatebm:
171 for m in updatebm:
183 bm[m] = repo[newbmtarget].node()
172 bm[m] = repo[newbmtarget].node()
184 bm.write()
173 bm.write()
185 except: # re-raises
174 except: # re-raises
186 if backupfile:
175 if backupfile:
187 ui.warn(_("strip failed, full bundle stored in '%s'\n")
176 ui.warn(_("strip failed, full bundle stored in '%s'\n")
188 % backupfile)
177 % backupfile)
189 elif saveheads:
178 elif saveheads:
190 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
179 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
191 % chgrpfile)
180 % chgrpfile)
192 raise
181 raise
193
182
194 if len(stripbranches) == 1 and len(newheadbranches) == 1 \
183 repo.destroyed()
195 and stripbranches == newheadbranches:
196 repo.destroyed(newheadnodes)
197 else:
198 # Multiple branches involved in strip. Will allow branchcache to become
199 # invalid and later on rebuilt from scratch
200 repo.destroyed()
@@ -1,2143 +1,2139 b''
1 > do_push()
1 > do_push()
2 > {
2 > {
3 > user=$1
3 > user=$1
4 > shift
4 > shift
5 > echo "Pushing as user $user"
5 > echo "Pushing as user $user"
6 > echo 'hgrc = """'
6 > echo 'hgrc = """'
7 > sed -e 1,2d b/.hg/hgrc | grep -v fakegroups.py
7 > sed -e 1,2d b/.hg/hgrc | grep -v fakegroups.py
8 > echo '"""'
8 > echo '"""'
9 > if test -f acl.config; then
9 > if test -f acl.config; then
10 > echo 'acl.config = """'
10 > echo 'acl.config = """'
11 > cat acl.config
11 > cat acl.config
12 > echo '"""'
12 > echo '"""'
13 > fi
13 > fi
14 > # On AIX /etc/profile sets LOGNAME read-only. So
14 > # On AIX /etc/profile sets LOGNAME read-only. So
15 > # LOGNAME=$user hg --cws a --debug push ../b
15 > # LOGNAME=$user hg --cws a --debug push ../b
16 > # fails with "This variable is read only."
16 > # fails with "This variable is read only."
17 > # Use env to work around this.
17 > # Use env to work around this.
18 > env LOGNAME=$user hg --cwd a --debug push ../b
18 > env LOGNAME=$user hg --cwd a --debug push ../b
19 > hg --cwd b rollback
19 > hg --cwd b rollback
20 > hg --cwd b --quiet tip
20 > hg --cwd b --quiet tip
21 > echo
21 > echo
22 > }
22 > }
23
23
24 > init_config()
24 > init_config()
25 > {
25 > {
26 > cat > fakegroups.py <<EOF
26 > cat > fakegroups.py <<EOF
27 > from hgext import acl
27 > from hgext import acl
28 > def fakegetusers(ui, group):
28 > def fakegetusers(ui, group):
29 > try:
29 > try:
30 > return acl._getusersorig(ui, group)
30 > return acl._getusersorig(ui, group)
31 > except:
31 > except:
32 > return ["fred", "betty"]
32 > return ["fred", "betty"]
33 > acl._getusersorig = acl._getusers
33 > acl._getusersorig = acl._getusers
34 > acl._getusers = fakegetusers
34 > acl._getusers = fakegetusers
35 > EOF
35 > EOF
36 > rm -f acl.config
36 > rm -f acl.config
37 > cat > $config <<EOF
37 > cat > $config <<EOF
38 > [hooks]
38 > [hooks]
39 > pretxnchangegroup.acl = python:hgext.acl.hook
39 > pretxnchangegroup.acl = python:hgext.acl.hook
40 > [acl]
40 > [acl]
41 > sources = push
41 > sources = push
42 > [extensions]
42 > [extensions]
43 > f=`pwd`/fakegroups.py
43 > f=`pwd`/fakegroups.py
44 > EOF
44 > EOF
45 > }
45 > }
46
46
47 $ hg init a
47 $ hg init a
48 $ cd a
48 $ cd a
49 $ mkdir foo foo/Bar quux
49 $ mkdir foo foo/Bar quux
50 $ echo 'in foo' > foo/file.txt
50 $ echo 'in foo' > foo/file.txt
51 $ echo 'in foo/Bar' > foo/Bar/file.txt
51 $ echo 'in foo/Bar' > foo/Bar/file.txt
52 $ echo 'in quux' > quux/file.py
52 $ echo 'in quux' > quux/file.py
53 $ hg add -q
53 $ hg add -q
54 $ hg ci -m 'add files' -d '1000000 0'
54 $ hg ci -m 'add files' -d '1000000 0'
55 $ echo >> foo/file.txt
55 $ echo >> foo/file.txt
56 $ hg ci -m 'change foo/file' -d '1000001 0'
56 $ hg ci -m 'change foo/file' -d '1000001 0'
57 $ echo >> foo/Bar/file.txt
57 $ echo >> foo/Bar/file.txt
58 $ hg ci -m 'change foo/Bar/file' -d '1000002 0'
58 $ hg ci -m 'change foo/Bar/file' -d '1000002 0'
59 $ echo >> quux/file.py
59 $ echo >> quux/file.py
60 $ hg ci -m 'change quux/file' -d '1000003 0'
60 $ hg ci -m 'change quux/file' -d '1000003 0'
61 $ hg tip --quiet
61 $ hg tip --quiet
62 3:911600dab2ae
62 3:911600dab2ae
63
63
64 $ cd ..
64 $ cd ..
65 $ hg clone -r 0 a b
65 $ hg clone -r 0 a b
66 adding changesets
66 adding changesets
67 adding manifests
67 adding manifests
68 adding file changes
68 adding file changes
69 added 1 changesets with 3 changes to 3 files
69 added 1 changesets with 3 changes to 3 files
70 updating to branch default
70 updating to branch default
71 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
72
72
73 $ config=b/.hg/hgrc
73 $ config=b/.hg/hgrc
74
74
75 Extension disabled for lack of a hook
75 Extension disabled for lack of a hook
76
76
77 $ do_push fred
77 $ do_push fred
78 Pushing as user fred
78 Pushing as user fred
79 hgrc = """
79 hgrc = """
80 """
80 """
81 pushing to ../b
81 pushing to ../b
82 query 1; heads
82 query 1; heads
83 searching for changes
83 searching for changes
84 all remote heads known locally
84 all remote heads known locally
85 listing keys for "bookmarks"
85 listing keys for "bookmarks"
86 3 changesets found
86 3 changesets found
87 list of changesets:
87 list of changesets:
88 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
88 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
89 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
89 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
90 911600dab2ae7a9baff75958b84fe606851ce955
90 911600dab2ae7a9baff75958b84fe606851ce955
91 adding changesets
91 adding changesets
92 bundling: 1/3 changesets (33.33%)
92 bundling: 1/3 changesets (33.33%)
93 bundling: 2/3 changesets (66.67%)
93 bundling: 2/3 changesets (66.67%)
94 bundling: 3/3 changesets (100.00%)
94 bundling: 3/3 changesets (100.00%)
95 bundling: 1/3 manifests (33.33%)
95 bundling: 1/3 manifests (33.33%)
96 bundling: 2/3 manifests (66.67%)
96 bundling: 2/3 manifests (66.67%)
97 bundling: 3/3 manifests (100.00%)
97 bundling: 3/3 manifests (100.00%)
98 bundling: foo/Bar/file.txt 1/3 files (33.33%)
98 bundling: foo/Bar/file.txt 1/3 files (33.33%)
99 bundling: foo/file.txt 2/3 files (66.67%)
99 bundling: foo/file.txt 2/3 files (66.67%)
100 bundling: quux/file.py 3/3 files (100.00%)
100 bundling: quux/file.py 3/3 files (100.00%)
101 changesets: 1 chunks
101 changesets: 1 chunks
102 add changeset ef1ea85a6374
102 add changeset ef1ea85a6374
103 changesets: 2 chunks
103 changesets: 2 chunks
104 add changeset f9cafe1212c8
104 add changeset f9cafe1212c8
105 changesets: 3 chunks
105 changesets: 3 chunks
106 add changeset 911600dab2ae
106 add changeset 911600dab2ae
107 adding manifests
107 adding manifests
108 manifests: 1/3 chunks (33.33%)
108 manifests: 1/3 chunks (33.33%)
109 manifests: 2/3 chunks (66.67%)
109 manifests: 2/3 chunks (66.67%)
110 manifests: 3/3 chunks (100.00%)
110 manifests: 3/3 chunks (100.00%)
111 adding file changes
111 adding file changes
112 adding foo/Bar/file.txt revisions
112 adding foo/Bar/file.txt revisions
113 files: 1/3 chunks (33.33%)
113 files: 1/3 chunks (33.33%)
114 adding foo/file.txt revisions
114 adding foo/file.txt revisions
115 files: 2/3 chunks (66.67%)
115 files: 2/3 chunks (66.67%)
116 adding quux/file.py revisions
116 adding quux/file.py revisions
117 files: 3/3 chunks (100.00%)
117 files: 3/3 chunks (100.00%)
118 added 3 changesets with 3 changes to 3 files
118 added 3 changesets with 3 changes to 3 files
119 listing keys for "phases"
119 listing keys for "phases"
120 try to push obsolete markers to remote
120 try to push obsolete markers to remote
121 updating the branch cache
121 updating the branch cache
122 checking for updated bookmarks
122 checking for updated bookmarks
123 listing keys for "bookmarks"
123 listing keys for "bookmarks"
124 repository tip rolled back to revision 0 (undo push)
124 repository tip rolled back to revision 0 (undo push)
125 0:6675d58eff77
125 0:6675d58eff77
126
126
127
127
128 $ echo '[hooks]' >> $config
128 $ echo '[hooks]' >> $config
129 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
129 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
130
130
131 Extension disabled for lack of acl.sources
131 Extension disabled for lack of acl.sources
132
132
133 $ do_push fred
133 $ do_push fred
134 Pushing as user fred
134 Pushing as user fred
135 hgrc = """
135 hgrc = """
136 [hooks]
136 [hooks]
137 pretxnchangegroup.acl = python:hgext.acl.hook
137 pretxnchangegroup.acl = python:hgext.acl.hook
138 """
138 """
139 pushing to ../b
139 pushing to ../b
140 query 1; heads
140 query 1; heads
141 searching for changes
141 searching for changes
142 all remote heads known locally
142 all remote heads known locally
143 invalid branchheads cache (served): tip differs
143 invalid branchheads cache (served): tip differs
144 listing keys for "bookmarks"
144 listing keys for "bookmarks"
145 3 changesets found
145 3 changesets found
146 list of changesets:
146 list of changesets:
147 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
147 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
148 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
148 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
149 911600dab2ae7a9baff75958b84fe606851ce955
149 911600dab2ae7a9baff75958b84fe606851ce955
150 adding changesets
150 adding changesets
151 bundling: 1/3 changesets (33.33%)
151 bundling: 1/3 changesets (33.33%)
152 bundling: 2/3 changesets (66.67%)
152 bundling: 2/3 changesets (66.67%)
153 bundling: 3/3 changesets (100.00%)
153 bundling: 3/3 changesets (100.00%)
154 bundling: 1/3 manifests (33.33%)
154 bundling: 1/3 manifests (33.33%)
155 bundling: 2/3 manifests (66.67%)
155 bundling: 2/3 manifests (66.67%)
156 bundling: 3/3 manifests (100.00%)
156 bundling: 3/3 manifests (100.00%)
157 bundling: foo/Bar/file.txt 1/3 files (33.33%)
157 bundling: foo/Bar/file.txt 1/3 files (33.33%)
158 bundling: foo/file.txt 2/3 files (66.67%)
158 bundling: foo/file.txt 2/3 files (66.67%)
159 bundling: quux/file.py 3/3 files (100.00%)
159 bundling: quux/file.py 3/3 files (100.00%)
160 changesets: 1 chunks
160 changesets: 1 chunks
161 add changeset ef1ea85a6374
161 add changeset ef1ea85a6374
162 changesets: 2 chunks
162 changesets: 2 chunks
163 add changeset f9cafe1212c8
163 add changeset f9cafe1212c8
164 changesets: 3 chunks
164 changesets: 3 chunks
165 add changeset 911600dab2ae
165 add changeset 911600dab2ae
166 adding manifests
166 adding manifests
167 manifests: 1/3 chunks (33.33%)
167 manifests: 1/3 chunks (33.33%)
168 manifests: 2/3 chunks (66.67%)
168 manifests: 2/3 chunks (66.67%)
169 manifests: 3/3 chunks (100.00%)
169 manifests: 3/3 chunks (100.00%)
170 adding file changes
170 adding file changes
171 adding foo/Bar/file.txt revisions
171 adding foo/Bar/file.txt revisions
172 files: 1/3 chunks (33.33%)
172 files: 1/3 chunks (33.33%)
173 adding foo/file.txt revisions
173 adding foo/file.txt revisions
174 files: 2/3 chunks (66.67%)
174 files: 2/3 chunks (66.67%)
175 adding quux/file.py revisions
175 adding quux/file.py revisions
176 files: 3/3 chunks (100.00%)
176 files: 3/3 chunks (100.00%)
177 added 3 changesets with 3 changes to 3 files
177 added 3 changesets with 3 changes to 3 files
178 calling hook pretxnchangegroup.acl: hgext.acl.hook
178 calling hook pretxnchangegroup.acl: hgext.acl.hook
179 acl: changes have source "push" - skipping
179 acl: changes have source "push" - skipping
180 listing keys for "phases"
180 listing keys for "phases"
181 try to push obsolete markers to remote
181 try to push obsolete markers to remote
182 updating the branch cache
182 updating the branch cache
183 checking for updated bookmarks
183 checking for updated bookmarks
184 listing keys for "bookmarks"
184 listing keys for "bookmarks"
185 repository tip rolled back to revision 0 (undo push)
185 repository tip rolled back to revision 0 (undo push)
186 0:6675d58eff77
186 0:6675d58eff77
187
187
188
188
189 No [acl.allow]/[acl.deny]
189 No [acl.allow]/[acl.deny]
190
190
191 $ echo '[acl]' >> $config
191 $ echo '[acl]' >> $config
192 $ echo 'sources = push' >> $config
192 $ echo 'sources = push' >> $config
193 $ do_push fred
193 $ do_push fred
194 Pushing as user fred
194 Pushing as user fred
195 hgrc = """
195 hgrc = """
196 [hooks]
196 [hooks]
197 pretxnchangegroup.acl = python:hgext.acl.hook
197 pretxnchangegroup.acl = python:hgext.acl.hook
198 [acl]
198 [acl]
199 sources = push
199 sources = push
200 """
200 """
201 pushing to ../b
201 pushing to ../b
202 query 1; heads
202 query 1; heads
203 searching for changes
203 searching for changes
204 all remote heads known locally
204 all remote heads known locally
205 invalid branchheads cache (served): tip differs
205 invalid branchheads cache (served): tip differs
206 listing keys for "bookmarks"
206 listing keys for "bookmarks"
207 3 changesets found
207 3 changesets found
208 list of changesets:
208 list of changesets:
209 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
209 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
210 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
210 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
211 911600dab2ae7a9baff75958b84fe606851ce955
211 911600dab2ae7a9baff75958b84fe606851ce955
212 adding changesets
212 adding changesets
213 bundling: 1/3 changesets (33.33%)
213 bundling: 1/3 changesets (33.33%)
214 bundling: 2/3 changesets (66.67%)
214 bundling: 2/3 changesets (66.67%)
215 bundling: 3/3 changesets (100.00%)
215 bundling: 3/3 changesets (100.00%)
216 bundling: 1/3 manifests (33.33%)
216 bundling: 1/3 manifests (33.33%)
217 bundling: 2/3 manifests (66.67%)
217 bundling: 2/3 manifests (66.67%)
218 bundling: 3/3 manifests (100.00%)
218 bundling: 3/3 manifests (100.00%)
219 bundling: foo/Bar/file.txt 1/3 files (33.33%)
219 bundling: foo/Bar/file.txt 1/3 files (33.33%)
220 bundling: foo/file.txt 2/3 files (66.67%)
220 bundling: foo/file.txt 2/3 files (66.67%)
221 bundling: quux/file.py 3/3 files (100.00%)
221 bundling: quux/file.py 3/3 files (100.00%)
222 changesets: 1 chunks
222 changesets: 1 chunks
223 add changeset ef1ea85a6374
223 add changeset ef1ea85a6374
224 changesets: 2 chunks
224 changesets: 2 chunks
225 add changeset f9cafe1212c8
225 add changeset f9cafe1212c8
226 changesets: 3 chunks
226 changesets: 3 chunks
227 add changeset 911600dab2ae
227 add changeset 911600dab2ae
228 adding manifests
228 adding manifests
229 manifests: 1/3 chunks (33.33%)
229 manifests: 1/3 chunks (33.33%)
230 manifests: 2/3 chunks (66.67%)
230 manifests: 2/3 chunks (66.67%)
231 manifests: 3/3 chunks (100.00%)
231 manifests: 3/3 chunks (100.00%)
232 adding file changes
232 adding file changes
233 adding foo/Bar/file.txt revisions
233 adding foo/Bar/file.txt revisions
234 files: 1/3 chunks (33.33%)
234 files: 1/3 chunks (33.33%)
235 adding foo/file.txt revisions
235 adding foo/file.txt revisions
236 files: 2/3 chunks (66.67%)
236 files: 2/3 chunks (66.67%)
237 adding quux/file.py revisions
237 adding quux/file.py revisions
238 files: 3/3 chunks (100.00%)
238 files: 3/3 chunks (100.00%)
239 added 3 changesets with 3 changes to 3 files
239 added 3 changesets with 3 changes to 3 files
240 calling hook pretxnchangegroup.acl: hgext.acl.hook
240 calling hook pretxnchangegroup.acl: hgext.acl.hook
241 acl: checking access for user "fred"
241 acl: checking access for user "fred"
242 acl: acl.allow.branches not enabled
242 acl: acl.allow.branches not enabled
243 acl: acl.deny.branches not enabled
243 acl: acl.deny.branches not enabled
244 acl: acl.allow not enabled
244 acl: acl.allow not enabled
245 acl: acl.deny not enabled
245 acl: acl.deny not enabled
246 acl: branch access granted: "ef1ea85a6374" on branch "default"
246 acl: branch access granted: "ef1ea85a6374" on branch "default"
247 acl: path access granted: "ef1ea85a6374"
247 acl: path access granted: "ef1ea85a6374"
248 acl: branch access granted: "f9cafe1212c8" on branch "default"
248 acl: branch access granted: "f9cafe1212c8" on branch "default"
249 acl: path access granted: "f9cafe1212c8"
249 acl: path access granted: "f9cafe1212c8"
250 acl: branch access granted: "911600dab2ae" on branch "default"
250 acl: branch access granted: "911600dab2ae" on branch "default"
251 acl: path access granted: "911600dab2ae"
251 acl: path access granted: "911600dab2ae"
252 listing keys for "phases"
252 listing keys for "phases"
253 try to push obsolete markers to remote
253 try to push obsolete markers to remote
254 updating the branch cache
254 updating the branch cache
255 checking for updated bookmarks
255 checking for updated bookmarks
256 listing keys for "bookmarks"
256 listing keys for "bookmarks"
257 repository tip rolled back to revision 0 (undo push)
257 repository tip rolled back to revision 0 (undo push)
258 0:6675d58eff77
258 0:6675d58eff77
259
259
260
260
261 Empty [acl.allow]
261 Empty [acl.allow]
262
262
263 $ echo '[acl.allow]' >> $config
263 $ echo '[acl.allow]' >> $config
264 $ do_push fred
264 $ do_push fred
265 Pushing as user fred
265 Pushing as user fred
266 hgrc = """
266 hgrc = """
267 [hooks]
267 [hooks]
268 pretxnchangegroup.acl = python:hgext.acl.hook
268 pretxnchangegroup.acl = python:hgext.acl.hook
269 [acl]
269 [acl]
270 sources = push
270 sources = push
271 [acl.allow]
271 [acl.allow]
272 """
272 """
273 pushing to ../b
273 pushing to ../b
274 query 1; heads
274 query 1; heads
275 searching for changes
275 searching for changes
276 all remote heads known locally
276 all remote heads known locally
277 invalid branchheads cache (served): tip differs
277 invalid branchheads cache (served): tip differs
278 listing keys for "bookmarks"
278 listing keys for "bookmarks"
279 3 changesets found
279 3 changesets found
280 list of changesets:
280 list of changesets:
281 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
281 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
282 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
282 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
283 911600dab2ae7a9baff75958b84fe606851ce955
283 911600dab2ae7a9baff75958b84fe606851ce955
284 adding changesets
284 adding changesets
285 bundling: 1/3 changesets (33.33%)
285 bundling: 1/3 changesets (33.33%)
286 bundling: 2/3 changesets (66.67%)
286 bundling: 2/3 changesets (66.67%)
287 bundling: 3/3 changesets (100.00%)
287 bundling: 3/3 changesets (100.00%)
288 bundling: 1/3 manifests (33.33%)
288 bundling: 1/3 manifests (33.33%)
289 bundling: 2/3 manifests (66.67%)
289 bundling: 2/3 manifests (66.67%)
290 bundling: 3/3 manifests (100.00%)
290 bundling: 3/3 manifests (100.00%)
291 bundling: foo/Bar/file.txt 1/3 files (33.33%)
291 bundling: foo/Bar/file.txt 1/3 files (33.33%)
292 bundling: foo/file.txt 2/3 files (66.67%)
292 bundling: foo/file.txt 2/3 files (66.67%)
293 bundling: quux/file.py 3/3 files (100.00%)
293 bundling: quux/file.py 3/3 files (100.00%)
294 changesets: 1 chunks
294 changesets: 1 chunks
295 add changeset ef1ea85a6374
295 add changeset ef1ea85a6374
296 changesets: 2 chunks
296 changesets: 2 chunks
297 add changeset f9cafe1212c8
297 add changeset f9cafe1212c8
298 changesets: 3 chunks
298 changesets: 3 chunks
299 add changeset 911600dab2ae
299 add changeset 911600dab2ae
300 adding manifests
300 adding manifests
301 manifests: 1/3 chunks (33.33%)
301 manifests: 1/3 chunks (33.33%)
302 manifests: 2/3 chunks (66.67%)
302 manifests: 2/3 chunks (66.67%)
303 manifests: 3/3 chunks (100.00%)
303 manifests: 3/3 chunks (100.00%)
304 adding file changes
304 adding file changes
305 adding foo/Bar/file.txt revisions
305 adding foo/Bar/file.txt revisions
306 files: 1/3 chunks (33.33%)
306 files: 1/3 chunks (33.33%)
307 adding foo/file.txt revisions
307 adding foo/file.txt revisions
308 files: 2/3 chunks (66.67%)
308 files: 2/3 chunks (66.67%)
309 adding quux/file.py revisions
309 adding quux/file.py revisions
310 files: 3/3 chunks (100.00%)
310 files: 3/3 chunks (100.00%)
311 added 3 changesets with 3 changes to 3 files
311 added 3 changesets with 3 changes to 3 files
312 calling hook pretxnchangegroup.acl: hgext.acl.hook
312 calling hook pretxnchangegroup.acl: hgext.acl.hook
313 acl: checking access for user "fred"
313 acl: checking access for user "fred"
314 acl: acl.allow.branches not enabled
314 acl: acl.allow.branches not enabled
315 acl: acl.deny.branches not enabled
315 acl: acl.deny.branches not enabled
316 acl: acl.allow enabled, 0 entries for user fred
316 acl: acl.allow enabled, 0 entries for user fred
317 acl: acl.deny not enabled
317 acl: acl.deny not enabled
318 acl: branch access granted: "ef1ea85a6374" on branch "default"
318 acl: branch access granted: "ef1ea85a6374" on branch "default"
319 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
319 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
320 transaction abort!
320 transaction abort!
321 rollback completed
321 rollback completed
322 abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
322 abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
323 no rollback information available
323 no rollback information available
324 0:6675d58eff77
324 0:6675d58eff77
325
325
326
326
327 fred is allowed inside foo/
327 fred is allowed inside foo/
328
328
329 $ echo 'foo/** = fred' >> $config
329 $ echo 'foo/** = fred' >> $config
330 $ do_push fred
330 $ do_push fred
331 Pushing as user fred
331 Pushing as user fred
332 hgrc = """
332 hgrc = """
333 [hooks]
333 [hooks]
334 pretxnchangegroup.acl = python:hgext.acl.hook
334 pretxnchangegroup.acl = python:hgext.acl.hook
335 [acl]
335 [acl]
336 sources = push
336 sources = push
337 [acl.allow]
337 [acl.allow]
338 foo/** = fred
338 foo/** = fred
339 """
339 """
340 pushing to ../b
340 pushing to ../b
341 query 1; heads
341 query 1; heads
342 searching for changes
342 searching for changes
343 all remote heads known locally
343 all remote heads known locally
344 invalid branchheads cache (served): tip differs
344 invalid branchheads cache (served): tip differs
345 listing keys for "bookmarks"
345 listing keys for "bookmarks"
346 3 changesets found
346 3 changesets found
347 list of changesets:
347 list of changesets:
348 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
348 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
349 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
349 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
350 911600dab2ae7a9baff75958b84fe606851ce955
350 911600dab2ae7a9baff75958b84fe606851ce955
351 adding changesets
351 adding changesets
352 bundling: 1/3 changesets (33.33%)
352 bundling: 1/3 changesets (33.33%)
353 bundling: 2/3 changesets (66.67%)
353 bundling: 2/3 changesets (66.67%)
354 bundling: 3/3 changesets (100.00%)
354 bundling: 3/3 changesets (100.00%)
355 bundling: 1/3 manifests (33.33%)
355 bundling: 1/3 manifests (33.33%)
356 bundling: 2/3 manifests (66.67%)
356 bundling: 2/3 manifests (66.67%)
357 bundling: 3/3 manifests (100.00%)
357 bundling: 3/3 manifests (100.00%)
358 bundling: foo/Bar/file.txt 1/3 files (33.33%)
358 bundling: foo/Bar/file.txt 1/3 files (33.33%)
359 bundling: foo/file.txt 2/3 files (66.67%)
359 bundling: foo/file.txt 2/3 files (66.67%)
360 bundling: quux/file.py 3/3 files (100.00%)
360 bundling: quux/file.py 3/3 files (100.00%)
361 changesets: 1 chunks
361 changesets: 1 chunks
362 add changeset ef1ea85a6374
362 add changeset ef1ea85a6374
363 changesets: 2 chunks
363 changesets: 2 chunks
364 add changeset f9cafe1212c8
364 add changeset f9cafe1212c8
365 changesets: 3 chunks
365 changesets: 3 chunks
366 add changeset 911600dab2ae
366 add changeset 911600dab2ae
367 adding manifests
367 adding manifests
368 manifests: 1/3 chunks (33.33%)
368 manifests: 1/3 chunks (33.33%)
369 manifests: 2/3 chunks (66.67%)
369 manifests: 2/3 chunks (66.67%)
370 manifests: 3/3 chunks (100.00%)
370 manifests: 3/3 chunks (100.00%)
371 adding file changes
371 adding file changes
372 adding foo/Bar/file.txt revisions
372 adding foo/Bar/file.txt revisions
373 files: 1/3 chunks (33.33%)
373 files: 1/3 chunks (33.33%)
374 adding foo/file.txt revisions
374 adding foo/file.txt revisions
375 files: 2/3 chunks (66.67%)
375 files: 2/3 chunks (66.67%)
376 adding quux/file.py revisions
376 adding quux/file.py revisions
377 files: 3/3 chunks (100.00%)
377 files: 3/3 chunks (100.00%)
378 added 3 changesets with 3 changes to 3 files
378 added 3 changesets with 3 changes to 3 files
379 calling hook pretxnchangegroup.acl: hgext.acl.hook
379 calling hook pretxnchangegroup.acl: hgext.acl.hook
380 acl: checking access for user "fred"
380 acl: checking access for user "fred"
381 acl: acl.allow.branches not enabled
381 acl: acl.allow.branches not enabled
382 acl: acl.deny.branches not enabled
382 acl: acl.deny.branches not enabled
383 acl: acl.allow enabled, 1 entries for user fred
383 acl: acl.allow enabled, 1 entries for user fred
384 acl: acl.deny not enabled
384 acl: acl.deny not enabled
385 acl: branch access granted: "ef1ea85a6374" on branch "default"
385 acl: branch access granted: "ef1ea85a6374" on branch "default"
386 acl: path access granted: "ef1ea85a6374"
386 acl: path access granted: "ef1ea85a6374"
387 acl: branch access granted: "f9cafe1212c8" on branch "default"
387 acl: branch access granted: "f9cafe1212c8" on branch "default"
388 acl: path access granted: "f9cafe1212c8"
388 acl: path access granted: "f9cafe1212c8"
389 acl: branch access granted: "911600dab2ae" on branch "default"
389 acl: branch access granted: "911600dab2ae" on branch "default"
390 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
390 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
391 transaction abort!
391 transaction abort!
392 rollback completed
392 rollback completed
393 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
393 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
394 no rollback information available
394 no rollback information available
395 0:6675d58eff77
395 0:6675d58eff77
396
396
397
397
398 Empty [acl.deny]
398 Empty [acl.deny]
399
399
400 $ echo '[acl.deny]' >> $config
400 $ echo '[acl.deny]' >> $config
401 $ do_push barney
401 $ do_push barney
402 Pushing as user barney
402 Pushing as user barney
403 hgrc = """
403 hgrc = """
404 [hooks]
404 [hooks]
405 pretxnchangegroup.acl = python:hgext.acl.hook
405 pretxnchangegroup.acl = python:hgext.acl.hook
406 [acl]
406 [acl]
407 sources = push
407 sources = push
408 [acl.allow]
408 [acl.allow]
409 foo/** = fred
409 foo/** = fred
410 [acl.deny]
410 [acl.deny]
411 """
411 """
412 pushing to ../b
412 pushing to ../b
413 query 1; heads
413 query 1; heads
414 searching for changes
414 searching for changes
415 all remote heads known locally
415 all remote heads known locally
416 invalid branchheads cache (served): tip differs
416 invalid branchheads cache (served): tip differs
417 listing keys for "bookmarks"
417 listing keys for "bookmarks"
418 3 changesets found
418 3 changesets found
419 list of changesets:
419 list of changesets:
420 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
420 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
421 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
421 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
422 911600dab2ae7a9baff75958b84fe606851ce955
422 911600dab2ae7a9baff75958b84fe606851ce955
423 adding changesets
423 adding changesets
424 bundling: 1/3 changesets (33.33%)
424 bundling: 1/3 changesets (33.33%)
425 bundling: 2/3 changesets (66.67%)
425 bundling: 2/3 changesets (66.67%)
426 bundling: 3/3 changesets (100.00%)
426 bundling: 3/3 changesets (100.00%)
427 bundling: 1/3 manifests (33.33%)
427 bundling: 1/3 manifests (33.33%)
428 bundling: 2/3 manifests (66.67%)
428 bundling: 2/3 manifests (66.67%)
429 bundling: 3/3 manifests (100.00%)
429 bundling: 3/3 manifests (100.00%)
430 bundling: foo/Bar/file.txt 1/3 files (33.33%)
430 bundling: foo/Bar/file.txt 1/3 files (33.33%)
431 bundling: foo/file.txt 2/3 files (66.67%)
431 bundling: foo/file.txt 2/3 files (66.67%)
432 bundling: quux/file.py 3/3 files (100.00%)
432 bundling: quux/file.py 3/3 files (100.00%)
433 changesets: 1 chunks
433 changesets: 1 chunks
434 add changeset ef1ea85a6374
434 add changeset ef1ea85a6374
435 changesets: 2 chunks
435 changesets: 2 chunks
436 add changeset f9cafe1212c8
436 add changeset f9cafe1212c8
437 changesets: 3 chunks
437 changesets: 3 chunks
438 add changeset 911600dab2ae
438 add changeset 911600dab2ae
439 adding manifests
439 adding manifests
440 manifests: 1/3 chunks (33.33%)
440 manifests: 1/3 chunks (33.33%)
441 manifests: 2/3 chunks (66.67%)
441 manifests: 2/3 chunks (66.67%)
442 manifests: 3/3 chunks (100.00%)
442 manifests: 3/3 chunks (100.00%)
443 adding file changes
443 adding file changes
444 adding foo/Bar/file.txt revisions
444 adding foo/Bar/file.txt revisions
445 files: 1/3 chunks (33.33%)
445 files: 1/3 chunks (33.33%)
446 adding foo/file.txt revisions
446 adding foo/file.txt revisions
447 files: 2/3 chunks (66.67%)
447 files: 2/3 chunks (66.67%)
448 adding quux/file.py revisions
448 adding quux/file.py revisions
449 files: 3/3 chunks (100.00%)
449 files: 3/3 chunks (100.00%)
450 added 3 changesets with 3 changes to 3 files
450 added 3 changesets with 3 changes to 3 files
451 calling hook pretxnchangegroup.acl: hgext.acl.hook
451 calling hook pretxnchangegroup.acl: hgext.acl.hook
452 acl: checking access for user "barney"
452 acl: checking access for user "barney"
453 acl: acl.allow.branches not enabled
453 acl: acl.allow.branches not enabled
454 acl: acl.deny.branches not enabled
454 acl: acl.deny.branches not enabled
455 acl: acl.allow enabled, 0 entries for user barney
455 acl: acl.allow enabled, 0 entries for user barney
456 acl: acl.deny enabled, 0 entries for user barney
456 acl: acl.deny enabled, 0 entries for user barney
457 acl: branch access granted: "ef1ea85a6374" on branch "default"
457 acl: branch access granted: "ef1ea85a6374" on branch "default"
458 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
458 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
459 transaction abort!
459 transaction abort!
460 rollback completed
460 rollback completed
461 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
461 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
462 no rollback information available
462 no rollback information available
463 0:6675d58eff77
463 0:6675d58eff77
464
464
465
465
466 fred is allowed inside foo/, but not foo/bar/ (case matters)
466 fred is allowed inside foo/, but not foo/bar/ (case matters)
467
467
468 $ echo 'foo/bar/** = fred' >> $config
468 $ echo 'foo/bar/** = fred' >> $config
469 $ do_push fred
469 $ do_push fred
470 Pushing as user fred
470 Pushing as user fred
471 hgrc = """
471 hgrc = """
472 [hooks]
472 [hooks]
473 pretxnchangegroup.acl = python:hgext.acl.hook
473 pretxnchangegroup.acl = python:hgext.acl.hook
474 [acl]
474 [acl]
475 sources = push
475 sources = push
476 [acl.allow]
476 [acl.allow]
477 foo/** = fred
477 foo/** = fred
478 [acl.deny]
478 [acl.deny]
479 foo/bar/** = fred
479 foo/bar/** = fred
480 """
480 """
481 pushing to ../b
481 pushing to ../b
482 query 1; heads
482 query 1; heads
483 searching for changes
483 searching for changes
484 all remote heads known locally
484 all remote heads known locally
485 invalid branchheads cache (served): tip differs
485 invalid branchheads cache (served): tip differs
486 listing keys for "bookmarks"
486 listing keys for "bookmarks"
487 3 changesets found
487 3 changesets found
488 list of changesets:
488 list of changesets:
489 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
489 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
490 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
490 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
491 911600dab2ae7a9baff75958b84fe606851ce955
491 911600dab2ae7a9baff75958b84fe606851ce955
492 adding changesets
492 adding changesets
493 bundling: 1/3 changesets (33.33%)
493 bundling: 1/3 changesets (33.33%)
494 bundling: 2/3 changesets (66.67%)
494 bundling: 2/3 changesets (66.67%)
495 bundling: 3/3 changesets (100.00%)
495 bundling: 3/3 changesets (100.00%)
496 bundling: 1/3 manifests (33.33%)
496 bundling: 1/3 manifests (33.33%)
497 bundling: 2/3 manifests (66.67%)
497 bundling: 2/3 manifests (66.67%)
498 bundling: 3/3 manifests (100.00%)
498 bundling: 3/3 manifests (100.00%)
499 bundling: foo/Bar/file.txt 1/3 files (33.33%)
499 bundling: foo/Bar/file.txt 1/3 files (33.33%)
500 bundling: foo/file.txt 2/3 files (66.67%)
500 bundling: foo/file.txt 2/3 files (66.67%)
501 bundling: quux/file.py 3/3 files (100.00%)
501 bundling: quux/file.py 3/3 files (100.00%)
502 changesets: 1 chunks
502 changesets: 1 chunks
503 add changeset ef1ea85a6374
503 add changeset ef1ea85a6374
504 changesets: 2 chunks
504 changesets: 2 chunks
505 add changeset f9cafe1212c8
505 add changeset f9cafe1212c8
506 changesets: 3 chunks
506 changesets: 3 chunks
507 add changeset 911600dab2ae
507 add changeset 911600dab2ae
508 adding manifests
508 adding manifests
509 manifests: 1/3 chunks (33.33%)
509 manifests: 1/3 chunks (33.33%)
510 manifests: 2/3 chunks (66.67%)
510 manifests: 2/3 chunks (66.67%)
511 manifests: 3/3 chunks (100.00%)
511 manifests: 3/3 chunks (100.00%)
512 adding file changes
512 adding file changes
513 adding foo/Bar/file.txt revisions
513 adding foo/Bar/file.txt revisions
514 files: 1/3 chunks (33.33%)
514 files: 1/3 chunks (33.33%)
515 adding foo/file.txt revisions
515 adding foo/file.txt revisions
516 files: 2/3 chunks (66.67%)
516 files: 2/3 chunks (66.67%)
517 adding quux/file.py revisions
517 adding quux/file.py revisions
518 files: 3/3 chunks (100.00%)
518 files: 3/3 chunks (100.00%)
519 added 3 changesets with 3 changes to 3 files
519 added 3 changesets with 3 changes to 3 files
520 calling hook pretxnchangegroup.acl: hgext.acl.hook
520 calling hook pretxnchangegroup.acl: hgext.acl.hook
521 acl: checking access for user "fred"
521 acl: checking access for user "fred"
522 acl: acl.allow.branches not enabled
522 acl: acl.allow.branches not enabled
523 acl: acl.deny.branches not enabled
523 acl: acl.deny.branches not enabled
524 acl: acl.allow enabled, 1 entries for user fred
524 acl: acl.allow enabled, 1 entries for user fred
525 acl: acl.deny enabled, 1 entries for user fred
525 acl: acl.deny enabled, 1 entries for user fred
526 acl: branch access granted: "ef1ea85a6374" on branch "default"
526 acl: branch access granted: "ef1ea85a6374" on branch "default"
527 acl: path access granted: "ef1ea85a6374"
527 acl: path access granted: "ef1ea85a6374"
528 acl: branch access granted: "f9cafe1212c8" on branch "default"
528 acl: branch access granted: "f9cafe1212c8" on branch "default"
529 acl: path access granted: "f9cafe1212c8"
529 acl: path access granted: "f9cafe1212c8"
530 acl: branch access granted: "911600dab2ae" on branch "default"
530 acl: branch access granted: "911600dab2ae" on branch "default"
531 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
531 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
532 transaction abort!
532 transaction abort!
533 rollback completed
533 rollback completed
534 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
534 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
535 no rollback information available
535 no rollback information available
536 0:6675d58eff77
536 0:6675d58eff77
537
537
538
538
539 fred is allowed inside foo/, but not foo/Bar/
539 fred is allowed inside foo/, but not foo/Bar/
540
540
541 $ echo 'foo/Bar/** = fred' >> $config
541 $ echo 'foo/Bar/** = fred' >> $config
542 $ do_push fred
542 $ do_push fred
543 Pushing as user fred
543 Pushing as user fred
544 hgrc = """
544 hgrc = """
545 [hooks]
545 [hooks]
546 pretxnchangegroup.acl = python:hgext.acl.hook
546 pretxnchangegroup.acl = python:hgext.acl.hook
547 [acl]
547 [acl]
548 sources = push
548 sources = push
549 [acl.allow]
549 [acl.allow]
550 foo/** = fred
550 foo/** = fred
551 [acl.deny]
551 [acl.deny]
552 foo/bar/** = fred
552 foo/bar/** = fred
553 foo/Bar/** = fred
553 foo/Bar/** = fred
554 """
554 """
555 pushing to ../b
555 pushing to ../b
556 query 1; heads
556 query 1; heads
557 searching for changes
557 searching for changes
558 all remote heads known locally
558 all remote heads known locally
559 invalid branchheads cache (served): tip differs
559 invalid branchheads cache (served): tip differs
560 listing keys for "bookmarks"
560 listing keys for "bookmarks"
561 3 changesets found
561 3 changesets found
562 list of changesets:
562 list of changesets:
563 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
563 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
564 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
564 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
565 911600dab2ae7a9baff75958b84fe606851ce955
565 911600dab2ae7a9baff75958b84fe606851ce955
566 adding changesets
566 adding changesets
567 bundling: 1/3 changesets (33.33%)
567 bundling: 1/3 changesets (33.33%)
568 bundling: 2/3 changesets (66.67%)
568 bundling: 2/3 changesets (66.67%)
569 bundling: 3/3 changesets (100.00%)
569 bundling: 3/3 changesets (100.00%)
570 bundling: 1/3 manifests (33.33%)
570 bundling: 1/3 manifests (33.33%)
571 bundling: 2/3 manifests (66.67%)
571 bundling: 2/3 manifests (66.67%)
572 bundling: 3/3 manifests (100.00%)
572 bundling: 3/3 manifests (100.00%)
573 bundling: foo/Bar/file.txt 1/3 files (33.33%)
573 bundling: foo/Bar/file.txt 1/3 files (33.33%)
574 bundling: foo/file.txt 2/3 files (66.67%)
574 bundling: foo/file.txt 2/3 files (66.67%)
575 bundling: quux/file.py 3/3 files (100.00%)
575 bundling: quux/file.py 3/3 files (100.00%)
576 changesets: 1 chunks
576 changesets: 1 chunks
577 add changeset ef1ea85a6374
577 add changeset ef1ea85a6374
578 changesets: 2 chunks
578 changesets: 2 chunks
579 add changeset f9cafe1212c8
579 add changeset f9cafe1212c8
580 changesets: 3 chunks
580 changesets: 3 chunks
581 add changeset 911600dab2ae
581 add changeset 911600dab2ae
582 adding manifests
582 adding manifests
583 manifests: 1/3 chunks (33.33%)
583 manifests: 1/3 chunks (33.33%)
584 manifests: 2/3 chunks (66.67%)
584 manifests: 2/3 chunks (66.67%)
585 manifests: 3/3 chunks (100.00%)
585 manifests: 3/3 chunks (100.00%)
586 adding file changes
586 adding file changes
587 adding foo/Bar/file.txt revisions
587 adding foo/Bar/file.txt revisions
588 files: 1/3 chunks (33.33%)
588 files: 1/3 chunks (33.33%)
589 adding foo/file.txt revisions
589 adding foo/file.txt revisions
590 files: 2/3 chunks (66.67%)
590 files: 2/3 chunks (66.67%)
591 adding quux/file.py revisions
591 adding quux/file.py revisions
592 files: 3/3 chunks (100.00%)
592 files: 3/3 chunks (100.00%)
593 added 3 changesets with 3 changes to 3 files
593 added 3 changesets with 3 changes to 3 files
594 calling hook pretxnchangegroup.acl: hgext.acl.hook
594 calling hook pretxnchangegroup.acl: hgext.acl.hook
595 acl: checking access for user "fred"
595 acl: checking access for user "fred"
596 acl: acl.allow.branches not enabled
596 acl: acl.allow.branches not enabled
597 acl: acl.deny.branches not enabled
597 acl: acl.deny.branches not enabled
598 acl: acl.allow enabled, 1 entries for user fred
598 acl: acl.allow enabled, 1 entries for user fred
599 acl: acl.deny enabled, 2 entries for user fred
599 acl: acl.deny enabled, 2 entries for user fred
600 acl: branch access granted: "ef1ea85a6374" on branch "default"
600 acl: branch access granted: "ef1ea85a6374" on branch "default"
601 acl: path access granted: "ef1ea85a6374"
601 acl: path access granted: "ef1ea85a6374"
602 acl: branch access granted: "f9cafe1212c8" on branch "default"
602 acl: branch access granted: "f9cafe1212c8" on branch "default"
603 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
603 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
604 transaction abort!
604 transaction abort!
605 rollback completed
605 rollback completed
606 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
606 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
607 no rollback information available
607 no rollback information available
608 0:6675d58eff77
608 0:6675d58eff77
609
609
610
610
611 $ echo 'barney is not mentioned => not allowed anywhere'
611 $ echo 'barney is not mentioned => not allowed anywhere'
612 barney is not mentioned => not allowed anywhere
612 barney is not mentioned => not allowed anywhere
613 $ do_push barney
613 $ do_push barney
614 Pushing as user barney
614 Pushing as user barney
615 hgrc = """
615 hgrc = """
616 [hooks]
616 [hooks]
617 pretxnchangegroup.acl = python:hgext.acl.hook
617 pretxnchangegroup.acl = python:hgext.acl.hook
618 [acl]
618 [acl]
619 sources = push
619 sources = push
620 [acl.allow]
620 [acl.allow]
621 foo/** = fred
621 foo/** = fred
622 [acl.deny]
622 [acl.deny]
623 foo/bar/** = fred
623 foo/bar/** = fred
624 foo/Bar/** = fred
624 foo/Bar/** = fred
625 """
625 """
626 pushing to ../b
626 pushing to ../b
627 query 1; heads
627 query 1; heads
628 searching for changes
628 searching for changes
629 all remote heads known locally
629 all remote heads known locally
630 invalid branchheads cache (served): tip differs
630 invalid branchheads cache (served): tip differs
631 listing keys for "bookmarks"
631 listing keys for "bookmarks"
632 3 changesets found
632 3 changesets found
633 list of changesets:
633 list of changesets:
634 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
634 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
635 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
635 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
636 911600dab2ae7a9baff75958b84fe606851ce955
636 911600dab2ae7a9baff75958b84fe606851ce955
637 adding changesets
637 adding changesets
638 bundling: 1/3 changesets (33.33%)
638 bundling: 1/3 changesets (33.33%)
639 bundling: 2/3 changesets (66.67%)
639 bundling: 2/3 changesets (66.67%)
640 bundling: 3/3 changesets (100.00%)
640 bundling: 3/3 changesets (100.00%)
641 bundling: 1/3 manifests (33.33%)
641 bundling: 1/3 manifests (33.33%)
642 bundling: 2/3 manifests (66.67%)
642 bundling: 2/3 manifests (66.67%)
643 bundling: 3/3 manifests (100.00%)
643 bundling: 3/3 manifests (100.00%)
644 bundling: foo/Bar/file.txt 1/3 files (33.33%)
644 bundling: foo/Bar/file.txt 1/3 files (33.33%)
645 bundling: foo/file.txt 2/3 files (66.67%)
645 bundling: foo/file.txt 2/3 files (66.67%)
646 bundling: quux/file.py 3/3 files (100.00%)
646 bundling: quux/file.py 3/3 files (100.00%)
647 changesets: 1 chunks
647 changesets: 1 chunks
648 add changeset ef1ea85a6374
648 add changeset ef1ea85a6374
649 changesets: 2 chunks
649 changesets: 2 chunks
650 add changeset f9cafe1212c8
650 add changeset f9cafe1212c8
651 changesets: 3 chunks
651 changesets: 3 chunks
652 add changeset 911600dab2ae
652 add changeset 911600dab2ae
653 adding manifests
653 adding manifests
654 manifests: 1/3 chunks (33.33%)
654 manifests: 1/3 chunks (33.33%)
655 manifests: 2/3 chunks (66.67%)
655 manifests: 2/3 chunks (66.67%)
656 manifests: 3/3 chunks (100.00%)
656 manifests: 3/3 chunks (100.00%)
657 adding file changes
657 adding file changes
658 adding foo/Bar/file.txt revisions
658 adding foo/Bar/file.txt revisions
659 files: 1/3 chunks (33.33%)
659 files: 1/3 chunks (33.33%)
660 adding foo/file.txt revisions
660 adding foo/file.txt revisions
661 files: 2/3 chunks (66.67%)
661 files: 2/3 chunks (66.67%)
662 adding quux/file.py revisions
662 adding quux/file.py revisions
663 files: 3/3 chunks (100.00%)
663 files: 3/3 chunks (100.00%)
664 added 3 changesets with 3 changes to 3 files
664 added 3 changesets with 3 changes to 3 files
665 calling hook pretxnchangegroup.acl: hgext.acl.hook
665 calling hook pretxnchangegroup.acl: hgext.acl.hook
666 acl: checking access for user "barney"
666 acl: checking access for user "barney"
667 acl: acl.allow.branches not enabled
667 acl: acl.allow.branches not enabled
668 acl: acl.deny.branches not enabled
668 acl: acl.deny.branches not enabled
669 acl: acl.allow enabled, 0 entries for user barney
669 acl: acl.allow enabled, 0 entries for user barney
670 acl: acl.deny enabled, 0 entries for user barney
670 acl: acl.deny enabled, 0 entries for user barney
671 acl: branch access granted: "ef1ea85a6374" on branch "default"
671 acl: branch access granted: "ef1ea85a6374" on branch "default"
672 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
672 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
673 transaction abort!
673 transaction abort!
674 rollback completed
674 rollback completed
675 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
675 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
676 no rollback information available
676 no rollback information available
677 0:6675d58eff77
677 0:6675d58eff77
678
678
679
679
680 barney is allowed everywhere
680 barney is allowed everywhere
681
681
682 $ echo '[acl.allow]' >> $config
682 $ echo '[acl.allow]' >> $config
683 $ echo '** = barney' >> $config
683 $ echo '** = barney' >> $config
684 $ do_push barney
684 $ do_push barney
685 Pushing as user barney
685 Pushing as user barney
686 hgrc = """
686 hgrc = """
687 [hooks]
687 [hooks]
688 pretxnchangegroup.acl = python:hgext.acl.hook
688 pretxnchangegroup.acl = python:hgext.acl.hook
689 [acl]
689 [acl]
690 sources = push
690 sources = push
691 [acl.allow]
691 [acl.allow]
692 foo/** = fred
692 foo/** = fred
693 [acl.deny]
693 [acl.deny]
694 foo/bar/** = fred
694 foo/bar/** = fred
695 foo/Bar/** = fred
695 foo/Bar/** = fred
696 [acl.allow]
696 [acl.allow]
697 ** = barney
697 ** = barney
698 """
698 """
699 pushing to ../b
699 pushing to ../b
700 query 1; heads
700 query 1; heads
701 searching for changes
701 searching for changes
702 all remote heads known locally
702 all remote heads known locally
703 invalid branchheads cache (served): tip differs
703 invalid branchheads cache (served): tip differs
704 listing keys for "bookmarks"
704 listing keys for "bookmarks"
705 3 changesets found
705 3 changesets found
706 list of changesets:
706 list of changesets:
707 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
707 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
708 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
708 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
709 911600dab2ae7a9baff75958b84fe606851ce955
709 911600dab2ae7a9baff75958b84fe606851ce955
710 adding changesets
710 adding changesets
711 bundling: 1/3 changesets (33.33%)
711 bundling: 1/3 changesets (33.33%)
712 bundling: 2/3 changesets (66.67%)
712 bundling: 2/3 changesets (66.67%)
713 bundling: 3/3 changesets (100.00%)
713 bundling: 3/3 changesets (100.00%)
714 bundling: 1/3 manifests (33.33%)
714 bundling: 1/3 manifests (33.33%)
715 bundling: 2/3 manifests (66.67%)
715 bundling: 2/3 manifests (66.67%)
716 bundling: 3/3 manifests (100.00%)
716 bundling: 3/3 manifests (100.00%)
717 bundling: foo/Bar/file.txt 1/3 files (33.33%)
717 bundling: foo/Bar/file.txt 1/3 files (33.33%)
718 bundling: foo/file.txt 2/3 files (66.67%)
718 bundling: foo/file.txt 2/3 files (66.67%)
719 bundling: quux/file.py 3/3 files (100.00%)
719 bundling: quux/file.py 3/3 files (100.00%)
720 changesets: 1 chunks
720 changesets: 1 chunks
721 add changeset ef1ea85a6374
721 add changeset ef1ea85a6374
722 changesets: 2 chunks
722 changesets: 2 chunks
723 add changeset f9cafe1212c8
723 add changeset f9cafe1212c8
724 changesets: 3 chunks
724 changesets: 3 chunks
725 add changeset 911600dab2ae
725 add changeset 911600dab2ae
726 adding manifests
726 adding manifests
727 manifests: 1/3 chunks (33.33%)
727 manifests: 1/3 chunks (33.33%)
728 manifests: 2/3 chunks (66.67%)
728 manifests: 2/3 chunks (66.67%)
729 manifests: 3/3 chunks (100.00%)
729 manifests: 3/3 chunks (100.00%)
730 adding file changes
730 adding file changes
731 adding foo/Bar/file.txt revisions
731 adding foo/Bar/file.txt revisions
732 files: 1/3 chunks (33.33%)
732 files: 1/3 chunks (33.33%)
733 adding foo/file.txt revisions
733 adding foo/file.txt revisions
734 files: 2/3 chunks (66.67%)
734 files: 2/3 chunks (66.67%)
735 adding quux/file.py revisions
735 adding quux/file.py revisions
736 files: 3/3 chunks (100.00%)
736 files: 3/3 chunks (100.00%)
737 added 3 changesets with 3 changes to 3 files
737 added 3 changesets with 3 changes to 3 files
738 calling hook pretxnchangegroup.acl: hgext.acl.hook
738 calling hook pretxnchangegroup.acl: hgext.acl.hook
739 acl: checking access for user "barney"
739 acl: checking access for user "barney"
740 acl: acl.allow.branches not enabled
740 acl: acl.allow.branches not enabled
741 acl: acl.deny.branches not enabled
741 acl: acl.deny.branches not enabled
742 acl: acl.allow enabled, 1 entries for user barney
742 acl: acl.allow enabled, 1 entries for user barney
743 acl: acl.deny enabled, 0 entries for user barney
743 acl: acl.deny enabled, 0 entries for user barney
744 acl: branch access granted: "ef1ea85a6374" on branch "default"
744 acl: branch access granted: "ef1ea85a6374" on branch "default"
745 acl: path access granted: "ef1ea85a6374"
745 acl: path access granted: "ef1ea85a6374"
746 acl: branch access granted: "f9cafe1212c8" on branch "default"
746 acl: branch access granted: "f9cafe1212c8" on branch "default"
747 acl: path access granted: "f9cafe1212c8"
747 acl: path access granted: "f9cafe1212c8"
748 acl: branch access granted: "911600dab2ae" on branch "default"
748 acl: branch access granted: "911600dab2ae" on branch "default"
749 acl: path access granted: "911600dab2ae"
749 acl: path access granted: "911600dab2ae"
750 listing keys for "phases"
750 listing keys for "phases"
751 try to push obsolete markers to remote
751 try to push obsolete markers to remote
752 updating the branch cache
752 updating the branch cache
753 checking for updated bookmarks
753 checking for updated bookmarks
754 listing keys for "bookmarks"
754 listing keys for "bookmarks"
755 repository tip rolled back to revision 0 (undo push)
755 repository tip rolled back to revision 0 (undo push)
756 0:6675d58eff77
756 0:6675d58eff77
757
757
758
758
759 wilma can change files with a .txt extension
759 wilma can change files with a .txt extension
760
760
761 $ echo '**/*.txt = wilma' >> $config
761 $ echo '**/*.txt = wilma' >> $config
762 $ do_push wilma
762 $ do_push wilma
763 Pushing as user wilma
763 Pushing as user wilma
764 hgrc = """
764 hgrc = """
765 [hooks]
765 [hooks]
766 pretxnchangegroup.acl = python:hgext.acl.hook
766 pretxnchangegroup.acl = python:hgext.acl.hook
767 [acl]
767 [acl]
768 sources = push
768 sources = push
769 [acl.allow]
769 [acl.allow]
770 foo/** = fred
770 foo/** = fred
771 [acl.deny]
771 [acl.deny]
772 foo/bar/** = fred
772 foo/bar/** = fred
773 foo/Bar/** = fred
773 foo/Bar/** = fred
774 [acl.allow]
774 [acl.allow]
775 ** = barney
775 ** = barney
776 **/*.txt = wilma
776 **/*.txt = wilma
777 """
777 """
778 pushing to ../b
778 pushing to ../b
779 query 1; heads
779 query 1; heads
780 searching for changes
780 searching for changes
781 all remote heads known locally
781 all remote heads known locally
782 invalid branchheads cache (served): tip differs
782 invalid branchheads cache (served): tip differs
783 listing keys for "bookmarks"
783 listing keys for "bookmarks"
784 3 changesets found
784 3 changesets found
785 list of changesets:
785 list of changesets:
786 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
786 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
787 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
787 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
788 911600dab2ae7a9baff75958b84fe606851ce955
788 911600dab2ae7a9baff75958b84fe606851ce955
789 adding changesets
789 adding changesets
790 bundling: 1/3 changesets (33.33%)
790 bundling: 1/3 changesets (33.33%)
791 bundling: 2/3 changesets (66.67%)
791 bundling: 2/3 changesets (66.67%)
792 bundling: 3/3 changesets (100.00%)
792 bundling: 3/3 changesets (100.00%)
793 bundling: 1/3 manifests (33.33%)
793 bundling: 1/3 manifests (33.33%)
794 bundling: 2/3 manifests (66.67%)
794 bundling: 2/3 manifests (66.67%)
795 bundling: 3/3 manifests (100.00%)
795 bundling: 3/3 manifests (100.00%)
796 bundling: foo/Bar/file.txt 1/3 files (33.33%)
796 bundling: foo/Bar/file.txt 1/3 files (33.33%)
797 bundling: foo/file.txt 2/3 files (66.67%)
797 bundling: foo/file.txt 2/3 files (66.67%)
798 bundling: quux/file.py 3/3 files (100.00%)
798 bundling: quux/file.py 3/3 files (100.00%)
799 changesets: 1 chunks
799 changesets: 1 chunks
800 add changeset ef1ea85a6374
800 add changeset ef1ea85a6374
801 changesets: 2 chunks
801 changesets: 2 chunks
802 add changeset f9cafe1212c8
802 add changeset f9cafe1212c8
803 changesets: 3 chunks
803 changesets: 3 chunks
804 add changeset 911600dab2ae
804 add changeset 911600dab2ae
805 adding manifests
805 adding manifests
806 manifests: 1/3 chunks (33.33%)
806 manifests: 1/3 chunks (33.33%)
807 manifests: 2/3 chunks (66.67%)
807 manifests: 2/3 chunks (66.67%)
808 manifests: 3/3 chunks (100.00%)
808 manifests: 3/3 chunks (100.00%)
809 adding file changes
809 adding file changes
810 adding foo/Bar/file.txt revisions
810 adding foo/Bar/file.txt revisions
811 files: 1/3 chunks (33.33%)
811 files: 1/3 chunks (33.33%)
812 adding foo/file.txt revisions
812 adding foo/file.txt revisions
813 files: 2/3 chunks (66.67%)
813 files: 2/3 chunks (66.67%)
814 adding quux/file.py revisions
814 adding quux/file.py revisions
815 files: 3/3 chunks (100.00%)
815 files: 3/3 chunks (100.00%)
816 added 3 changesets with 3 changes to 3 files
816 added 3 changesets with 3 changes to 3 files
817 calling hook pretxnchangegroup.acl: hgext.acl.hook
817 calling hook pretxnchangegroup.acl: hgext.acl.hook
818 acl: checking access for user "wilma"
818 acl: checking access for user "wilma"
819 acl: acl.allow.branches not enabled
819 acl: acl.allow.branches not enabled
820 acl: acl.deny.branches not enabled
820 acl: acl.deny.branches not enabled
821 acl: acl.allow enabled, 1 entries for user wilma
821 acl: acl.allow enabled, 1 entries for user wilma
822 acl: acl.deny enabled, 0 entries for user wilma
822 acl: acl.deny enabled, 0 entries for user wilma
823 acl: branch access granted: "ef1ea85a6374" on branch "default"
823 acl: branch access granted: "ef1ea85a6374" on branch "default"
824 acl: path access granted: "ef1ea85a6374"
824 acl: path access granted: "ef1ea85a6374"
825 acl: branch access granted: "f9cafe1212c8" on branch "default"
825 acl: branch access granted: "f9cafe1212c8" on branch "default"
826 acl: path access granted: "f9cafe1212c8"
826 acl: path access granted: "f9cafe1212c8"
827 acl: branch access granted: "911600dab2ae" on branch "default"
827 acl: branch access granted: "911600dab2ae" on branch "default"
828 error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
828 error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
829 transaction abort!
829 transaction abort!
830 rollback completed
830 rollback completed
831 abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
831 abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
832 no rollback information available
832 no rollback information available
833 0:6675d58eff77
833 0:6675d58eff77
834
834
835
835
836 file specified by acl.config does not exist
836 file specified by acl.config does not exist
837
837
838 $ echo '[acl]' >> $config
838 $ echo '[acl]' >> $config
839 $ echo 'config = ../acl.config' >> $config
839 $ echo 'config = ../acl.config' >> $config
840 $ do_push barney
840 $ do_push barney
841 Pushing as user barney
841 Pushing as user barney
842 hgrc = """
842 hgrc = """
843 [hooks]
843 [hooks]
844 pretxnchangegroup.acl = python:hgext.acl.hook
844 pretxnchangegroup.acl = python:hgext.acl.hook
845 [acl]
845 [acl]
846 sources = push
846 sources = push
847 [acl.allow]
847 [acl.allow]
848 foo/** = fred
848 foo/** = fred
849 [acl.deny]
849 [acl.deny]
850 foo/bar/** = fred
850 foo/bar/** = fred
851 foo/Bar/** = fred
851 foo/Bar/** = fred
852 [acl.allow]
852 [acl.allow]
853 ** = barney
853 ** = barney
854 **/*.txt = wilma
854 **/*.txt = wilma
855 [acl]
855 [acl]
856 config = ../acl.config
856 config = ../acl.config
857 """
857 """
858 pushing to ../b
858 pushing to ../b
859 query 1; heads
859 query 1; heads
860 searching for changes
860 searching for changes
861 all remote heads known locally
861 all remote heads known locally
862 invalid branchheads cache (served): tip differs
862 invalid branchheads cache (served): tip differs
863 listing keys for "bookmarks"
863 listing keys for "bookmarks"
864 3 changesets found
864 3 changesets found
865 list of changesets:
865 list of changesets:
866 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
866 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
867 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
867 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
868 911600dab2ae7a9baff75958b84fe606851ce955
868 911600dab2ae7a9baff75958b84fe606851ce955
869 adding changesets
869 adding changesets
870 bundling: 1/3 changesets (33.33%)
870 bundling: 1/3 changesets (33.33%)
871 bundling: 2/3 changesets (66.67%)
871 bundling: 2/3 changesets (66.67%)
872 bundling: 3/3 changesets (100.00%)
872 bundling: 3/3 changesets (100.00%)
873 bundling: 1/3 manifests (33.33%)
873 bundling: 1/3 manifests (33.33%)
874 bundling: 2/3 manifests (66.67%)
874 bundling: 2/3 manifests (66.67%)
875 bundling: 3/3 manifests (100.00%)
875 bundling: 3/3 manifests (100.00%)
876 bundling: foo/Bar/file.txt 1/3 files (33.33%)
876 bundling: foo/Bar/file.txt 1/3 files (33.33%)
877 bundling: foo/file.txt 2/3 files (66.67%)
877 bundling: foo/file.txt 2/3 files (66.67%)
878 bundling: quux/file.py 3/3 files (100.00%)
878 bundling: quux/file.py 3/3 files (100.00%)
879 changesets: 1 chunks
879 changesets: 1 chunks
880 add changeset ef1ea85a6374
880 add changeset ef1ea85a6374
881 changesets: 2 chunks
881 changesets: 2 chunks
882 add changeset f9cafe1212c8
882 add changeset f9cafe1212c8
883 changesets: 3 chunks
883 changesets: 3 chunks
884 add changeset 911600dab2ae
884 add changeset 911600dab2ae
885 adding manifests
885 adding manifests
886 manifests: 1/3 chunks (33.33%)
886 manifests: 1/3 chunks (33.33%)
887 manifests: 2/3 chunks (66.67%)
887 manifests: 2/3 chunks (66.67%)
888 manifests: 3/3 chunks (100.00%)
888 manifests: 3/3 chunks (100.00%)
889 adding file changes
889 adding file changes
890 adding foo/Bar/file.txt revisions
890 adding foo/Bar/file.txt revisions
891 files: 1/3 chunks (33.33%)
891 files: 1/3 chunks (33.33%)
892 adding foo/file.txt revisions
892 adding foo/file.txt revisions
893 files: 2/3 chunks (66.67%)
893 files: 2/3 chunks (66.67%)
894 adding quux/file.py revisions
894 adding quux/file.py revisions
895 files: 3/3 chunks (100.00%)
895 files: 3/3 chunks (100.00%)
896 added 3 changesets with 3 changes to 3 files
896 added 3 changesets with 3 changes to 3 files
897 calling hook pretxnchangegroup.acl: hgext.acl.hook
897 calling hook pretxnchangegroup.acl: hgext.acl.hook
898 acl: checking access for user "barney"
898 acl: checking access for user "barney"
899 error: pretxnchangegroup.acl hook raised an exception: [Errno *] *: '../acl.config' (glob)
899 error: pretxnchangegroup.acl hook raised an exception: [Errno *] *: '../acl.config' (glob)
900 transaction abort!
900 transaction abort!
901 rollback completed
901 rollback completed
902 abort: *: ../acl.config (glob)
902 abort: *: ../acl.config (glob)
903 no rollback information available
903 no rollback information available
904 0:6675d58eff77
904 0:6675d58eff77
905
905
906
906
907 betty is allowed inside foo/ by a acl.config file
907 betty is allowed inside foo/ by a acl.config file
908
908
909 $ echo '[acl.allow]' >> acl.config
909 $ echo '[acl.allow]' >> acl.config
910 $ echo 'foo/** = betty' >> acl.config
910 $ echo 'foo/** = betty' >> acl.config
911 $ do_push betty
911 $ do_push betty
912 Pushing as user betty
912 Pushing as user betty
913 hgrc = """
913 hgrc = """
914 [hooks]
914 [hooks]
915 pretxnchangegroup.acl = python:hgext.acl.hook
915 pretxnchangegroup.acl = python:hgext.acl.hook
916 [acl]
916 [acl]
917 sources = push
917 sources = push
918 [acl.allow]
918 [acl.allow]
919 foo/** = fred
919 foo/** = fred
920 [acl.deny]
920 [acl.deny]
921 foo/bar/** = fred
921 foo/bar/** = fred
922 foo/Bar/** = fred
922 foo/Bar/** = fred
923 [acl.allow]
923 [acl.allow]
924 ** = barney
924 ** = barney
925 **/*.txt = wilma
925 **/*.txt = wilma
926 [acl]
926 [acl]
927 config = ../acl.config
927 config = ../acl.config
928 """
928 """
929 acl.config = """
929 acl.config = """
930 [acl.allow]
930 [acl.allow]
931 foo/** = betty
931 foo/** = betty
932 """
932 """
933 pushing to ../b
933 pushing to ../b
934 query 1; heads
934 query 1; heads
935 searching for changes
935 searching for changes
936 all remote heads known locally
936 all remote heads known locally
937 invalid branchheads cache (served): tip differs
937 invalid branchheads cache (served): tip differs
938 listing keys for "bookmarks"
938 listing keys for "bookmarks"
939 3 changesets found
939 3 changesets found
940 list of changesets:
940 list of changesets:
941 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
941 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
942 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
942 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
943 911600dab2ae7a9baff75958b84fe606851ce955
943 911600dab2ae7a9baff75958b84fe606851ce955
944 adding changesets
944 adding changesets
945 bundling: 1/3 changesets (33.33%)
945 bundling: 1/3 changesets (33.33%)
946 bundling: 2/3 changesets (66.67%)
946 bundling: 2/3 changesets (66.67%)
947 bundling: 3/3 changesets (100.00%)
947 bundling: 3/3 changesets (100.00%)
948 bundling: 1/3 manifests (33.33%)
948 bundling: 1/3 manifests (33.33%)
949 bundling: 2/3 manifests (66.67%)
949 bundling: 2/3 manifests (66.67%)
950 bundling: 3/3 manifests (100.00%)
950 bundling: 3/3 manifests (100.00%)
951 bundling: foo/Bar/file.txt 1/3 files (33.33%)
951 bundling: foo/Bar/file.txt 1/3 files (33.33%)
952 bundling: foo/file.txt 2/3 files (66.67%)
952 bundling: foo/file.txt 2/3 files (66.67%)
953 bundling: quux/file.py 3/3 files (100.00%)
953 bundling: quux/file.py 3/3 files (100.00%)
954 changesets: 1 chunks
954 changesets: 1 chunks
955 add changeset ef1ea85a6374
955 add changeset ef1ea85a6374
956 changesets: 2 chunks
956 changesets: 2 chunks
957 add changeset f9cafe1212c8
957 add changeset f9cafe1212c8
958 changesets: 3 chunks
958 changesets: 3 chunks
959 add changeset 911600dab2ae
959 add changeset 911600dab2ae
960 adding manifests
960 adding manifests
961 manifests: 1/3 chunks (33.33%)
961 manifests: 1/3 chunks (33.33%)
962 manifests: 2/3 chunks (66.67%)
962 manifests: 2/3 chunks (66.67%)
963 manifests: 3/3 chunks (100.00%)
963 manifests: 3/3 chunks (100.00%)
964 adding file changes
964 adding file changes
965 adding foo/Bar/file.txt revisions
965 adding foo/Bar/file.txt revisions
966 files: 1/3 chunks (33.33%)
966 files: 1/3 chunks (33.33%)
967 adding foo/file.txt revisions
967 adding foo/file.txt revisions
968 files: 2/3 chunks (66.67%)
968 files: 2/3 chunks (66.67%)
969 adding quux/file.py revisions
969 adding quux/file.py revisions
970 files: 3/3 chunks (100.00%)
970 files: 3/3 chunks (100.00%)
971 added 3 changesets with 3 changes to 3 files
971 added 3 changesets with 3 changes to 3 files
972 calling hook pretxnchangegroup.acl: hgext.acl.hook
972 calling hook pretxnchangegroup.acl: hgext.acl.hook
973 acl: checking access for user "betty"
973 acl: checking access for user "betty"
974 acl: acl.allow.branches not enabled
974 acl: acl.allow.branches not enabled
975 acl: acl.deny.branches not enabled
975 acl: acl.deny.branches not enabled
976 acl: acl.allow enabled, 1 entries for user betty
976 acl: acl.allow enabled, 1 entries for user betty
977 acl: acl.deny enabled, 0 entries for user betty
977 acl: acl.deny enabled, 0 entries for user betty
978 acl: branch access granted: "ef1ea85a6374" on branch "default"
978 acl: branch access granted: "ef1ea85a6374" on branch "default"
979 acl: path access granted: "ef1ea85a6374"
979 acl: path access granted: "ef1ea85a6374"
980 acl: branch access granted: "f9cafe1212c8" on branch "default"
980 acl: branch access granted: "f9cafe1212c8" on branch "default"
981 acl: path access granted: "f9cafe1212c8"
981 acl: path access granted: "f9cafe1212c8"
982 acl: branch access granted: "911600dab2ae" on branch "default"
982 acl: branch access granted: "911600dab2ae" on branch "default"
983 error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
983 error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
984 transaction abort!
984 transaction abort!
985 rollback completed
985 rollback completed
986 abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
986 abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
987 no rollback information available
987 no rollback information available
988 0:6675d58eff77
988 0:6675d58eff77
989
989
990
990
991 acl.config can set only [acl.allow]/[acl.deny]
991 acl.config can set only [acl.allow]/[acl.deny]
992
992
993 $ echo '[hooks]' >> acl.config
993 $ echo '[hooks]' >> acl.config
994 $ echo 'changegroup.acl = false' >> acl.config
994 $ echo 'changegroup.acl = false' >> acl.config
995 $ do_push barney
995 $ do_push barney
996 Pushing as user barney
996 Pushing as user barney
997 hgrc = """
997 hgrc = """
998 [hooks]
998 [hooks]
999 pretxnchangegroup.acl = python:hgext.acl.hook
999 pretxnchangegroup.acl = python:hgext.acl.hook
1000 [acl]
1000 [acl]
1001 sources = push
1001 sources = push
1002 [acl.allow]
1002 [acl.allow]
1003 foo/** = fred
1003 foo/** = fred
1004 [acl.deny]
1004 [acl.deny]
1005 foo/bar/** = fred
1005 foo/bar/** = fred
1006 foo/Bar/** = fred
1006 foo/Bar/** = fred
1007 [acl.allow]
1007 [acl.allow]
1008 ** = barney
1008 ** = barney
1009 **/*.txt = wilma
1009 **/*.txt = wilma
1010 [acl]
1010 [acl]
1011 config = ../acl.config
1011 config = ../acl.config
1012 """
1012 """
1013 acl.config = """
1013 acl.config = """
1014 [acl.allow]
1014 [acl.allow]
1015 foo/** = betty
1015 foo/** = betty
1016 [hooks]
1016 [hooks]
1017 changegroup.acl = false
1017 changegroup.acl = false
1018 """
1018 """
1019 pushing to ../b
1019 pushing to ../b
1020 query 1; heads
1020 query 1; heads
1021 searching for changes
1021 searching for changes
1022 all remote heads known locally
1022 all remote heads known locally
1023 invalid branchheads cache (served): tip differs
1023 invalid branchheads cache (served): tip differs
1024 listing keys for "bookmarks"
1024 listing keys for "bookmarks"
1025 3 changesets found
1025 3 changesets found
1026 list of changesets:
1026 list of changesets:
1027 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1027 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1028 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1028 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1029 911600dab2ae7a9baff75958b84fe606851ce955
1029 911600dab2ae7a9baff75958b84fe606851ce955
1030 adding changesets
1030 adding changesets
1031 bundling: 1/3 changesets (33.33%)
1031 bundling: 1/3 changesets (33.33%)
1032 bundling: 2/3 changesets (66.67%)
1032 bundling: 2/3 changesets (66.67%)
1033 bundling: 3/3 changesets (100.00%)
1033 bundling: 3/3 changesets (100.00%)
1034 bundling: 1/3 manifests (33.33%)
1034 bundling: 1/3 manifests (33.33%)
1035 bundling: 2/3 manifests (66.67%)
1035 bundling: 2/3 manifests (66.67%)
1036 bundling: 3/3 manifests (100.00%)
1036 bundling: 3/3 manifests (100.00%)
1037 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1037 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1038 bundling: foo/file.txt 2/3 files (66.67%)
1038 bundling: foo/file.txt 2/3 files (66.67%)
1039 bundling: quux/file.py 3/3 files (100.00%)
1039 bundling: quux/file.py 3/3 files (100.00%)
1040 changesets: 1 chunks
1040 changesets: 1 chunks
1041 add changeset ef1ea85a6374
1041 add changeset ef1ea85a6374
1042 changesets: 2 chunks
1042 changesets: 2 chunks
1043 add changeset f9cafe1212c8
1043 add changeset f9cafe1212c8
1044 changesets: 3 chunks
1044 changesets: 3 chunks
1045 add changeset 911600dab2ae
1045 add changeset 911600dab2ae
1046 adding manifests
1046 adding manifests
1047 manifests: 1/3 chunks (33.33%)
1047 manifests: 1/3 chunks (33.33%)
1048 manifests: 2/3 chunks (66.67%)
1048 manifests: 2/3 chunks (66.67%)
1049 manifests: 3/3 chunks (100.00%)
1049 manifests: 3/3 chunks (100.00%)
1050 adding file changes
1050 adding file changes
1051 adding foo/Bar/file.txt revisions
1051 adding foo/Bar/file.txt revisions
1052 files: 1/3 chunks (33.33%)
1052 files: 1/3 chunks (33.33%)
1053 adding foo/file.txt revisions
1053 adding foo/file.txt revisions
1054 files: 2/3 chunks (66.67%)
1054 files: 2/3 chunks (66.67%)
1055 adding quux/file.py revisions
1055 adding quux/file.py revisions
1056 files: 3/3 chunks (100.00%)
1056 files: 3/3 chunks (100.00%)
1057 added 3 changesets with 3 changes to 3 files
1057 added 3 changesets with 3 changes to 3 files
1058 calling hook pretxnchangegroup.acl: hgext.acl.hook
1058 calling hook pretxnchangegroup.acl: hgext.acl.hook
1059 acl: checking access for user "barney"
1059 acl: checking access for user "barney"
1060 acl: acl.allow.branches not enabled
1060 acl: acl.allow.branches not enabled
1061 acl: acl.deny.branches not enabled
1061 acl: acl.deny.branches not enabled
1062 acl: acl.allow enabled, 1 entries for user barney
1062 acl: acl.allow enabled, 1 entries for user barney
1063 acl: acl.deny enabled, 0 entries for user barney
1063 acl: acl.deny enabled, 0 entries for user barney
1064 acl: branch access granted: "ef1ea85a6374" on branch "default"
1064 acl: branch access granted: "ef1ea85a6374" on branch "default"
1065 acl: path access granted: "ef1ea85a6374"
1065 acl: path access granted: "ef1ea85a6374"
1066 acl: branch access granted: "f9cafe1212c8" on branch "default"
1066 acl: branch access granted: "f9cafe1212c8" on branch "default"
1067 acl: path access granted: "f9cafe1212c8"
1067 acl: path access granted: "f9cafe1212c8"
1068 acl: branch access granted: "911600dab2ae" on branch "default"
1068 acl: branch access granted: "911600dab2ae" on branch "default"
1069 acl: path access granted: "911600dab2ae"
1069 acl: path access granted: "911600dab2ae"
1070 listing keys for "phases"
1070 listing keys for "phases"
1071 try to push obsolete markers to remote
1071 try to push obsolete markers to remote
1072 updating the branch cache
1072 updating the branch cache
1073 checking for updated bookmarks
1073 checking for updated bookmarks
1074 listing keys for "bookmarks"
1074 listing keys for "bookmarks"
1075 repository tip rolled back to revision 0 (undo push)
1075 repository tip rolled back to revision 0 (undo push)
1076 0:6675d58eff77
1076 0:6675d58eff77
1077
1077
1078
1078
1079 asterisk
1079 asterisk
1080
1080
1081 $ init_config
1081 $ init_config
1082
1082
1083 asterisk test
1083 asterisk test
1084
1084
1085 $ echo '[acl.allow]' >> $config
1085 $ echo '[acl.allow]' >> $config
1086 $ echo "** = fred" >> $config
1086 $ echo "** = fred" >> $config
1087
1087
1088 fred is always allowed
1088 fred is always allowed
1089
1089
1090 $ do_push fred
1090 $ do_push fred
1091 Pushing as user fred
1091 Pushing as user fred
1092 hgrc = """
1092 hgrc = """
1093 [acl]
1093 [acl]
1094 sources = push
1094 sources = push
1095 [extensions]
1095 [extensions]
1096 [acl.allow]
1096 [acl.allow]
1097 ** = fred
1097 ** = fred
1098 """
1098 """
1099 pushing to ../b
1099 pushing to ../b
1100 query 1; heads
1100 query 1; heads
1101 searching for changes
1101 searching for changes
1102 all remote heads known locally
1102 all remote heads known locally
1103 invalid branchheads cache (served): tip differs
1103 invalid branchheads cache (served): tip differs
1104 listing keys for "bookmarks"
1104 listing keys for "bookmarks"
1105 3 changesets found
1105 3 changesets found
1106 list of changesets:
1106 list of changesets:
1107 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1107 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1108 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1108 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1109 911600dab2ae7a9baff75958b84fe606851ce955
1109 911600dab2ae7a9baff75958b84fe606851ce955
1110 adding changesets
1110 adding changesets
1111 bundling: 1/3 changesets (33.33%)
1111 bundling: 1/3 changesets (33.33%)
1112 bundling: 2/3 changesets (66.67%)
1112 bundling: 2/3 changesets (66.67%)
1113 bundling: 3/3 changesets (100.00%)
1113 bundling: 3/3 changesets (100.00%)
1114 bundling: 1/3 manifests (33.33%)
1114 bundling: 1/3 manifests (33.33%)
1115 bundling: 2/3 manifests (66.67%)
1115 bundling: 2/3 manifests (66.67%)
1116 bundling: 3/3 manifests (100.00%)
1116 bundling: 3/3 manifests (100.00%)
1117 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1117 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1118 bundling: foo/file.txt 2/3 files (66.67%)
1118 bundling: foo/file.txt 2/3 files (66.67%)
1119 bundling: quux/file.py 3/3 files (100.00%)
1119 bundling: quux/file.py 3/3 files (100.00%)
1120 changesets: 1 chunks
1120 changesets: 1 chunks
1121 add changeset ef1ea85a6374
1121 add changeset ef1ea85a6374
1122 changesets: 2 chunks
1122 changesets: 2 chunks
1123 add changeset f9cafe1212c8
1123 add changeset f9cafe1212c8
1124 changesets: 3 chunks
1124 changesets: 3 chunks
1125 add changeset 911600dab2ae
1125 add changeset 911600dab2ae
1126 adding manifests
1126 adding manifests
1127 manifests: 1/3 chunks (33.33%)
1127 manifests: 1/3 chunks (33.33%)
1128 manifests: 2/3 chunks (66.67%)
1128 manifests: 2/3 chunks (66.67%)
1129 manifests: 3/3 chunks (100.00%)
1129 manifests: 3/3 chunks (100.00%)
1130 adding file changes
1130 adding file changes
1131 adding foo/Bar/file.txt revisions
1131 adding foo/Bar/file.txt revisions
1132 files: 1/3 chunks (33.33%)
1132 files: 1/3 chunks (33.33%)
1133 adding foo/file.txt revisions
1133 adding foo/file.txt revisions
1134 files: 2/3 chunks (66.67%)
1134 files: 2/3 chunks (66.67%)
1135 adding quux/file.py revisions
1135 adding quux/file.py revisions
1136 files: 3/3 chunks (100.00%)
1136 files: 3/3 chunks (100.00%)
1137 added 3 changesets with 3 changes to 3 files
1137 added 3 changesets with 3 changes to 3 files
1138 calling hook pretxnchangegroup.acl: hgext.acl.hook
1138 calling hook pretxnchangegroup.acl: hgext.acl.hook
1139 acl: checking access for user "fred"
1139 acl: checking access for user "fred"
1140 acl: acl.allow.branches not enabled
1140 acl: acl.allow.branches not enabled
1141 acl: acl.deny.branches not enabled
1141 acl: acl.deny.branches not enabled
1142 acl: acl.allow enabled, 1 entries for user fred
1142 acl: acl.allow enabled, 1 entries for user fred
1143 acl: acl.deny not enabled
1143 acl: acl.deny not enabled
1144 acl: branch access granted: "ef1ea85a6374" on branch "default"
1144 acl: branch access granted: "ef1ea85a6374" on branch "default"
1145 acl: path access granted: "ef1ea85a6374"
1145 acl: path access granted: "ef1ea85a6374"
1146 acl: branch access granted: "f9cafe1212c8" on branch "default"
1146 acl: branch access granted: "f9cafe1212c8" on branch "default"
1147 acl: path access granted: "f9cafe1212c8"
1147 acl: path access granted: "f9cafe1212c8"
1148 acl: branch access granted: "911600dab2ae" on branch "default"
1148 acl: branch access granted: "911600dab2ae" on branch "default"
1149 acl: path access granted: "911600dab2ae"
1149 acl: path access granted: "911600dab2ae"
1150 listing keys for "phases"
1150 listing keys for "phases"
1151 try to push obsolete markers to remote
1151 try to push obsolete markers to remote
1152 updating the branch cache
1152 updating the branch cache
1153 checking for updated bookmarks
1153 checking for updated bookmarks
1154 listing keys for "bookmarks"
1154 listing keys for "bookmarks"
1155 repository tip rolled back to revision 0 (undo push)
1155 repository tip rolled back to revision 0 (undo push)
1156 0:6675d58eff77
1156 0:6675d58eff77
1157
1157
1158
1158
1159 $ echo '[acl.deny]' >> $config
1159 $ echo '[acl.deny]' >> $config
1160 $ echo "foo/Bar/** = *" >> $config
1160 $ echo "foo/Bar/** = *" >> $config
1161
1161
1162 no one is allowed inside foo/Bar/
1162 no one is allowed inside foo/Bar/
1163
1163
1164 $ do_push fred
1164 $ do_push fred
1165 Pushing as user fred
1165 Pushing as user fred
1166 hgrc = """
1166 hgrc = """
1167 [acl]
1167 [acl]
1168 sources = push
1168 sources = push
1169 [extensions]
1169 [extensions]
1170 [acl.allow]
1170 [acl.allow]
1171 ** = fred
1171 ** = fred
1172 [acl.deny]
1172 [acl.deny]
1173 foo/Bar/** = *
1173 foo/Bar/** = *
1174 """
1174 """
1175 pushing to ../b
1175 pushing to ../b
1176 query 1; heads
1176 query 1; heads
1177 searching for changes
1177 searching for changes
1178 all remote heads known locally
1178 all remote heads known locally
1179 invalid branchheads cache (served): tip differs
1179 invalid branchheads cache (served): tip differs
1180 listing keys for "bookmarks"
1180 listing keys for "bookmarks"
1181 3 changesets found
1181 3 changesets found
1182 list of changesets:
1182 list of changesets:
1183 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1183 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1184 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1184 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1185 911600dab2ae7a9baff75958b84fe606851ce955
1185 911600dab2ae7a9baff75958b84fe606851ce955
1186 adding changesets
1186 adding changesets
1187 bundling: 1/3 changesets (33.33%)
1187 bundling: 1/3 changesets (33.33%)
1188 bundling: 2/3 changesets (66.67%)
1188 bundling: 2/3 changesets (66.67%)
1189 bundling: 3/3 changesets (100.00%)
1189 bundling: 3/3 changesets (100.00%)
1190 bundling: 1/3 manifests (33.33%)
1190 bundling: 1/3 manifests (33.33%)
1191 bundling: 2/3 manifests (66.67%)
1191 bundling: 2/3 manifests (66.67%)
1192 bundling: 3/3 manifests (100.00%)
1192 bundling: 3/3 manifests (100.00%)
1193 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1193 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1194 bundling: foo/file.txt 2/3 files (66.67%)
1194 bundling: foo/file.txt 2/3 files (66.67%)
1195 bundling: quux/file.py 3/3 files (100.00%)
1195 bundling: quux/file.py 3/3 files (100.00%)
1196 changesets: 1 chunks
1196 changesets: 1 chunks
1197 add changeset ef1ea85a6374
1197 add changeset ef1ea85a6374
1198 changesets: 2 chunks
1198 changesets: 2 chunks
1199 add changeset f9cafe1212c8
1199 add changeset f9cafe1212c8
1200 changesets: 3 chunks
1200 changesets: 3 chunks
1201 add changeset 911600dab2ae
1201 add changeset 911600dab2ae
1202 adding manifests
1202 adding manifests
1203 manifests: 1/3 chunks (33.33%)
1203 manifests: 1/3 chunks (33.33%)
1204 manifests: 2/3 chunks (66.67%)
1204 manifests: 2/3 chunks (66.67%)
1205 manifests: 3/3 chunks (100.00%)
1205 manifests: 3/3 chunks (100.00%)
1206 adding file changes
1206 adding file changes
1207 adding foo/Bar/file.txt revisions
1207 adding foo/Bar/file.txt revisions
1208 files: 1/3 chunks (33.33%)
1208 files: 1/3 chunks (33.33%)
1209 adding foo/file.txt revisions
1209 adding foo/file.txt revisions
1210 files: 2/3 chunks (66.67%)
1210 files: 2/3 chunks (66.67%)
1211 adding quux/file.py revisions
1211 adding quux/file.py revisions
1212 files: 3/3 chunks (100.00%)
1212 files: 3/3 chunks (100.00%)
1213 added 3 changesets with 3 changes to 3 files
1213 added 3 changesets with 3 changes to 3 files
1214 calling hook pretxnchangegroup.acl: hgext.acl.hook
1214 calling hook pretxnchangegroup.acl: hgext.acl.hook
1215 acl: checking access for user "fred"
1215 acl: checking access for user "fred"
1216 acl: acl.allow.branches not enabled
1216 acl: acl.allow.branches not enabled
1217 acl: acl.deny.branches not enabled
1217 acl: acl.deny.branches not enabled
1218 acl: acl.allow enabled, 1 entries for user fred
1218 acl: acl.allow enabled, 1 entries for user fred
1219 acl: acl.deny enabled, 1 entries for user fred
1219 acl: acl.deny enabled, 1 entries for user fred
1220 acl: branch access granted: "ef1ea85a6374" on branch "default"
1220 acl: branch access granted: "ef1ea85a6374" on branch "default"
1221 acl: path access granted: "ef1ea85a6374"
1221 acl: path access granted: "ef1ea85a6374"
1222 acl: branch access granted: "f9cafe1212c8" on branch "default"
1222 acl: branch access granted: "f9cafe1212c8" on branch "default"
1223 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1223 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1224 transaction abort!
1224 transaction abort!
1225 rollback completed
1225 rollback completed
1226 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1226 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1227 no rollback information available
1227 no rollback information available
1228 0:6675d58eff77
1228 0:6675d58eff77
1229
1229
1230
1230
1231 Groups
1231 Groups
1232
1232
1233 $ init_config
1233 $ init_config
1234
1234
1235 OS-level groups
1235 OS-level groups
1236
1236
1237 $ echo '[acl.allow]' >> $config
1237 $ echo '[acl.allow]' >> $config
1238 $ echo "** = @group1" >> $config
1238 $ echo "** = @group1" >> $config
1239
1239
1240 @group1 is always allowed
1240 @group1 is always allowed
1241
1241
1242 $ do_push fred
1242 $ do_push fred
1243 Pushing as user fred
1243 Pushing as user fred
1244 hgrc = """
1244 hgrc = """
1245 [acl]
1245 [acl]
1246 sources = push
1246 sources = push
1247 [extensions]
1247 [extensions]
1248 [acl.allow]
1248 [acl.allow]
1249 ** = @group1
1249 ** = @group1
1250 """
1250 """
1251 pushing to ../b
1251 pushing to ../b
1252 query 1; heads
1252 query 1; heads
1253 searching for changes
1253 searching for changes
1254 all remote heads known locally
1254 all remote heads known locally
1255 invalid branchheads cache (served): tip differs
1255 invalid branchheads cache (served): tip differs
1256 listing keys for "bookmarks"
1256 listing keys for "bookmarks"
1257 3 changesets found
1257 3 changesets found
1258 list of changesets:
1258 list of changesets:
1259 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1259 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1260 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1260 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1261 911600dab2ae7a9baff75958b84fe606851ce955
1261 911600dab2ae7a9baff75958b84fe606851ce955
1262 adding changesets
1262 adding changesets
1263 bundling: 1/3 changesets (33.33%)
1263 bundling: 1/3 changesets (33.33%)
1264 bundling: 2/3 changesets (66.67%)
1264 bundling: 2/3 changesets (66.67%)
1265 bundling: 3/3 changesets (100.00%)
1265 bundling: 3/3 changesets (100.00%)
1266 bundling: 1/3 manifests (33.33%)
1266 bundling: 1/3 manifests (33.33%)
1267 bundling: 2/3 manifests (66.67%)
1267 bundling: 2/3 manifests (66.67%)
1268 bundling: 3/3 manifests (100.00%)
1268 bundling: 3/3 manifests (100.00%)
1269 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1269 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1270 bundling: foo/file.txt 2/3 files (66.67%)
1270 bundling: foo/file.txt 2/3 files (66.67%)
1271 bundling: quux/file.py 3/3 files (100.00%)
1271 bundling: quux/file.py 3/3 files (100.00%)
1272 changesets: 1 chunks
1272 changesets: 1 chunks
1273 add changeset ef1ea85a6374
1273 add changeset ef1ea85a6374
1274 changesets: 2 chunks
1274 changesets: 2 chunks
1275 add changeset f9cafe1212c8
1275 add changeset f9cafe1212c8
1276 changesets: 3 chunks
1276 changesets: 3 chunks
1277 add changeset 911600dab2ae
1277 add changeset 911600dab2ae
1278 adding manifests
1278 adding manifests
1279 manifests: 1/3 chunks (33.33%)
1279 manifests: 1/3 chunks (33.33%)
1280 manifests: 2/3 chunks (66.67%)
1280 manifests: 2/3 chunks (66.67%)
1281 manifests: 3/3 chunks (100.00%)
1281 manifests: 3/3 chunks (100.00%)
1282 adding file changes
1282 adding file changes
1283 adding foo/Bar/file.txt revisions
1283 adding foo/Bar/file.txt revisions
1284 files: 1/3 chunks (33.33%)
1284 files: 1/3 chunks (33.33%)
1285 adding foo/file.txt revisions
1285 adding foo/file.txt revisions
1286 files: 2/3 chunks (66.67%)
1286 files: 2/3 chunks (66.67%)
1287 adding quux/file.py revisions
1287 adding quux/file.py revisions
1288 files: 3/3 chunks (100.00%)
1288 files: 3/3 chunks (100.00%)
1289 added 3 changesets with 3 changes to 3 files
1289 added 3 changesets with 3 changes to 3 files
1290 calling hook pretxnchangegroup.acl: hgext.acl.hook
1290 calling hook pretxnchangegroup.acl: hgext.acl.hook
1291 acl: checking access for user "fred"
1291 acl: checking access for user "fred"
1292 acl: acl.allow.branches not enabled
1292 acl: acl.allow.branches not enabled
1293 acl: acl.deny.branches not enabled
1293 acl: acl.deny.branches not enabled
1294 acl: "group1" not defined in [acl.groups]
1294 acl: "group1" not defined in [acl.groups]
1295 acl: acl.allow enabled, 1 entries for user fred
1295 acl: acl.allow enabled, 1 entries for user fred
1296 acl: acl.deny not enabled
1296 acl: acl.deny not enabled
1297 acl: branch access granted: "ef1ea85a6374" on branch "default"
1297 acl: branch access granted: "ef1ea85a6374" on branch "default"
1298 acl: path access granted: "ef1ea85a6374"
1298 acl: path access granted: "ef1ea85a6374"
1299 acl: branch access granted: "f9cafe1212c8" on branch "default"
1299 acl: branch access granted: "f9cafe1212c8" on branch "default"
1300 acl: path access granted: "f9cafe1212c8"
1300 acl: path access granted: "f9cafe1212c8"
1301 acl: branch access granted: "911600dab2ae" on branch "default"
1301 acl: branch access granted: "911600dab2ae" on branch "default"
1302 acl: path access granted: "911600dab2ae"
1302 acl: path access granted: "911600dab2ae"
1303 listing keys for "phases"
1303 listing keys for "phases"
1304 try to push obsolete markers to remote
1304 try to push obsolete markers to remote
1305 updating the branch cache
1305 updating the branch cache
1306 checking for updated bookmarks
1306 checking for updated bookmarks
1307 listing keys for "bookmarks"
1307 listing keys for "bookmarks"
1308 repository tip rolled back to revision 0 (undo push)
1308 repository tip rolled back to revision 0 (undo push)
1309 0:6675d58eff77
1309 0:6675d58eff77
1310
1310
1311
1311
1312 $ echo '[acl.deny]' >> $config
1312 $ echo '[acl.deny]' >> $config
1313 $ echo "foo/Bar/** = @group1" >> $config
1313 $ echo "foo/Bar/** = @group1" >> $config
1314
1314
1315 @group is allowed inside anything but foo/Bar/
1315 @group is allowed inside anything but foo/Bar/
1316
1316
1317 $ do_push fred
1317 $ do_push fred
1318 Pushing as user fred
1318 Pushing as user fred
1319 hgrc = """
1319 hgrc = """
1320 [acl]
1320 [acl]
1321 sources = push
1321 sources = push
1322 [extensions]
1322 [extensions]
1323 [acl.allow]
1323 [acl.allow]
1324 ** = @group1
1324 ** = @group1
1325 [acl.deny]
1325 [acl.deny]
1326 foo/Bar/** = @group1
1326 foo/Bar/** = @group1
1327 """
1327 """
1328 pushing to ../b
1328 pushing to ../b
1329 query 1; heads
1329 query 1; heads
1330 searching for changes
1330 searching for changes
1331 all remote heads known locally
1331 all remote heads known locally
1332 invalid branchheads cache (served): tip differs
1332 invalid branchheads cache (served): tip differs
1333 listing keys for "bookmarks"
1333 listing keys for "bookmarks"
1334 3 changesets found
1334 3 changesets found
1335 list of changesets:
1335 list of changesets:
1336 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1336 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1337 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1337 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1338 911600dab2ae7a9baff75958b84fe606851ce955
1338 911600dab2ae7a9baff75958b84fe606851ce955
1339 adding changesets
1339 adding changesets
1340 bundling: 1/3 changesets (33.33%)
1340 bundling: 1/3 changesets (33.33%)
1341 bundling: 2/3 changesets (66.67%)
1341 bundling: 2/3 changesets (66.67%)
1342 bundling: 3/3 changesets (100.00%)
1342 bundling: 3/3 changesets (100.00%)
1343 bundling: 1/3 manifests (33.33%)
1343 bundling: 1/3 manifests (33.33%)
1344 bundling: 2/3 manifests (66.67%)
1344 bundling: 2/3 manifests (66.67%)
1345 bundling: 3/3 manifests (100.00%)
1345 bundling: 3/3 manifests (100.00%)
1346 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1346 bundling: foo/Bar/file.txt 1/3 files (33.33%)
1347 bundling: foo/file.txt 2/3 files (66.67%)
1347 bundling: foo/file.txt 2/3 files (66.67%)
1348 bundling: quux/file.py 3/3 files (100.00%)
1348 bundling: quux/file.py 3/3 files (100.00%)
1349 changesets: 1 chunks
1349 changesets: 1 chunks
1350 add changeset ef1ea85a6374
1350 add changeset ef1ea85a6374
1351 changesets: 2 chunks
1351 changesets: 2 chunks
1352 add changeset f9cafe1212c8
1352 add changeset f9cafe1212c8
1353 changesets: 3 chunks
1353 changesets: 3 chunks
1354 add changeset 911600dab2ae
1354 add changeset 911600dab2ae
1355 adding manifests
1355 adding manifests
1356 manifests: 1/3 chunks (33.33%)
1356 manifests: 1/3 chunks (33.33%)
1357 manifests: 2/3 chunks (66.67%)
1357 manifests: 2/3 chunks (66.67%)
1358 manifests: 3/3 chunks (100.00%)
1358 manifests: 3/3 chunks (100.00%)
1359 adding file changes
1359 adding file changes
1360 adding foo/Bar/file.txt revisions
1360 adding foo/Bar/file.txt revisions
1361 files: 1/3 chunks (33.33%)
1361 files: 1/3 chunks (33.33%)
1362 adding foo/file.txt revisions
1362 adding foo/file.txt revisions
1363 files: 2/3 chunks (66.67%)
1363 files: 2/3 chunks (66.67%)
1364 adding quux/file.py revisions
1364 adding quux/file.py revisions
1365 files: 3/3 chunks (100.00%)
1365 files: 3/3 chunks (100.00%)
1366 added 3 changesets with 3 changes to 3 files
1366 added 3 changesets with 3 changes to 3 files
1367 calling hook pretxnchangegroup.acl: hgext.acl.hook
1367 calling hook pretxnchangegroup.acl: hgext.acl.hook
1368 acl: checking access for user "fred"
1368 acl: checking access for user "fred"
1369 acl: acl.allow.branches not enabled
1369 acl: acl.allow.branches not enabled
1370 acl: acl.deny.branches not enabled
1370 acl: acl.deny.branches not enabled
1371 acl: "group1" not defined in [acl.groups]
1371 acl: "group1" not defined in [acl.groups]
1372 acl: acl.allow enabled, 1 entries for user fred
1372 acl: acl.allow enabled, 1 entries for user fred
1373 acl: "group1" not defined in [acl.groups]
1373 acl: "group1" not defined in [acl.groups]
1374 acl: acl.deny enabled, 1 entries for user fred
1374 acl: acl.deny enabled, 1 entries for user fred
1375 acl: branch access granted: "ef1ea85a6374" on branch "default"
1375 acl: branch access granted: "ef1ea85a6374" on branch "default"
1376 acl: path access granted: "ef1ea85a6374"
1376 acl: path access granted: "ef1ea85a6374"
1377 acl: branch access granted: "f9cafe1212c8" on branch "default"
1377 acl: branch access granted: "f9cafe1212c8" on branch "default"
1378 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1378 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1379 transaction abort!
1379 transaction abort!
1380 rollback completed
1380 rollback completed
1381 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1381 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1382 no rollback information available
1382 no rollback information available
1383 0:6675d58eff77
1383 0:6675d58eff77
1384
1384
1385
1385
1386 Invalid group
1386 Invalid group
1387
1387
1388 Disable the fakegroups trick to get real failures
1388 Disable the fakegroups trick to get real failures
1389
1389
1390 $ grep -v fakegroups $config > config.tmp
1390 $ grep -v fakegroups $config > config.tmp
1391 $ mv config.tmp $config
1391 $ mv config.tmp $config
1392 $ echo '[acl.allow]' >> $config
1392 $ echo '[acl.allow]' >> $config
1393 $ echo "** = @unlikelytoexist" >> $config
1393 $ echo "** = @unlikelytoexist" >> $config
1394 $ do_push fred 2>&1 | grep unlikelytoexist
1394 $ do_push fred 2>&1 | grep unlikelytoexist
1395 ** = @unlikelytoexist
1395 ** = @unlikelytoexist
1396 acl: "unlikelytoexist" not defined in [acl.groups]
1396 acl: "unlikelytoexist" not defined in [acl.groups]
1397 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1397 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1398 abort: group 'unlikelytoexist' is undefined
1398 abort: group 'unlikelytoexist' is undefined
1399
1399
1400
1400
1401 Branch acl tests setup
1401 Branch acl tests setup
1402
1402
1403 $ init_config
1403 $ init_config
1404 $ cd b
1404 $ cd b
1405 $ hg up
1405 $ hg up
1406 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1406 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1407 $ hg branch foobar
1407 $ hg branch foobar
1408 marked working directory as branch foobar
1408 marked working directory as branch foobar
1409 (branches are permanent and global, did you want a bookmark?)
1409 (branches are permanent and global, did you want a bookmark?)
1410 $ hg commit -m 'create foobar'
1410 $ hg commit -m 'create foobar'
1411 $ echo 'foo contents' > abc.txt
1411 $ echo 'foo contents' > abc.txt
1412 $ hg add abc.txt
1412 $ hg add abc.txt
1413 $ hg commit -m 'foobar contents'
1413 $ hg commit -m 'foobar contents'
1414 $ cd ..
1414 $ cd ..
1415 $ hg --cwd a pull ../b
1415 $ hg --cwd a pull ../b
1416 pulling from ../b
1416 pulling from ../b
1417 searching for changes
1417 searching for changes
1418 adding changesets
1418 adding changesets
1419 adding manifests
1419 adding manifests
1420 adding file changes
1420 adding file changes
1421 added 2 changesets with 1 changes to 1 files (+1 heads)
1421 added 2 changesets with 1 changes to 1 files (+1 heads)
1422 (run 'hg heads' to see heads)
1422 (run 'hg heads' to see heads)
1423
1423
1424 Create additional changeset on foobar branch
1424 Create additional changeset on foobar branch
1425
1425
1426 $ cd a
1426 $ cd a
1427 $ hg up -C foobar
1427 $ hg up -C foobar
1428 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1428 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1429 $ echo 'foo contents2' > abc.txt
1429 $ echo 'foo contents2' > abc.txt
1430 $ hg commit -m 'foobar contents2'
1430 $ hg commit -m 'foobar contents2'
1431 $ cd ..
1431 $ cd ..
1432
1432
1433
1433
1434 No branch acls specified
1434 No branch acls specified
1435
1435
1436 $ do_push astro
1436 $ do_push astro
1437 Pushing as user astro
1437 Pushing as user astro
1438 hgrc = """
1438 hgrc = """
1439 [acl]
1439 [acl]
1440 sources = push
1440 sources = push
1441 [extensions]
1441 [extensions]
1442 """
1442 """
1443 pushing to ../b
1443 pushing to ../b
1444 query 1; heads
1444 query 1; heads
1445 searching for changes
1445 searching for changes
1446 all remote heads known locally
1446 all remote heads known locally
1447 listing keys for "bookmarks"
1447 listing keys for "bookmarks"
1448 4 changesets found
1448 4 changesets found
1449 list of changesets:
1449 list of changesets:
1450 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1450 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1451 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1451 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1452 911600dab2ae7a9baff75958b84fe606851ce955
1452 911600dab2ae7a9baff75958b84fe606851ce955
1453 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1453 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1454 adding changesets
1454 adding changesets
1455 bundling: 1/4 changesets (25.00%)
1455 bundling: 1/4 changesets (25.00%)
1456 bundling: 2/4 changesets (50.00%)
1456 bundling: 2/4 changesets (50.00%)
1457 bundling: 3/4 changesets (75.00%)
1457 bundling: 3/4 changesets (75.00%)
1458 bundling: 4/4 changesets (100.00%)
1458 bundling: 4/4 changesets (100.00%)
1459 bundling: 1/4 manifests (25.00%)
1459 bundling: 1/4 manifests (25.00%)
1460 bundling: 2/4 manifests (50.00%)
1460 bundling: 2/4 manifests (50.00%)
1461 bundling: 3/4 manifests (75.00%)
1461 bundling: 3/4 manifests (75.00%)
1462 bundling: 4/4 manifests (100.00%)
1462 bundling: 4/4 manifests (100.00%)
1463 bundling: abc.txt 1/4 files (25.00%)
1463 bundling: abc.txt 1/4 files (25.00%)
1464 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1464 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1465 bundling: foo/file.txt 3/4 files (75.00%)
1465 bundling: foo/file.txt 3/4 files (75.00%)
1466 bundling: quux/file.py 4/4 files (100.00%)
1466 bundling: quux/file.py 4/4 files (100.00%)
1467 changesets: 1 chunks
1467 changesets: 1 chunks
1468 add changeset ef1ea85a6374
1468 add changeset ef1ea85a6374
1469 changesets: 2 chunks
1469 changesets: 2 chunks
1470 add changeset f9cafe1212c8
1470 add changeset f9cafe1212c8
1471 changesets: 3 chunks
1471 changesets: 3 chunks
1472 add changeset 911600dab2ae
1472 add changeset 911600dab2ae
1473 changesets: 4 chunks
1473 changesets: 4 chunks
1474 add changeset e8fc755d4d82
1474 add changeset e8fc755d4d82
1475 adding manifests
1475 adding manifests
1476 manifests: 1/4 chunks (25.00%)
1476 manifests: 1/4 chunks (25.00%)
1477 manifests: 2/4 chunks (50.00%)
1477 manifests: 2/4 chunks (50.00%)
1478 manifests: 3/4 chunks (75.00%)
1478 manifests: 3/4 chunks (75.00%)
1479 manifests: 4/4 chunks (100.00%)
1479 manifests: 4/4 chunks (100.00%)
1480 adding file changes
1480 adding file changes
1481 adding abc.txt revisions
1481 adding abc.txt revisions
1482 files: 1/4 chunks (25.00%)
1482 files: 1/4 chunks (25.00%)
1483 adding foo/Bar/file.txt revisions
1483 adding foo/Bar/file.txt revisions
1484 files: 2/4 chunks (50.00%)
1484 files: 2/4 chunks (50.00%)
1485 adding foo/file.txt revisions
1485 adding foo/file.txt revisions
1486 files: 3/4 chunks (75.00%)
1486 files: 3/4 chunks (75.00%)
1487 adding quux/file.py revisions
1487 adding quux/file.py revisions
1488 files: 4/4 chunks (100.00%)
1488 files: 4/4 chunks (100.00%)
1489 added 4 changesets with 4 changes to 4 files (+1 heads)
1489 added 4 changesets with 4 changes to 4 files (+1 heads)
1490 calling hook pretxnchangegroup.acl: hgext.acl.hook
1490 calling hook pretxnchangegroup.acl: hgext.acl.hook
1491 acl: checking access for user "astro"
1491 acl: checking access for user "astro"
1492 acl: acl.allow.branches not enabled
1492 acl: acl.allow.branches not enabled
1493 acl: acl.deny.branches not enabled
1493 acl: acl.deny.branches not enabled
1494 acl: acl.allow not enabled
1494 acl: acl.allow not enabled
1495 acl: acl.deny not enabled
1495 acl: acl.deny not enabled
1496 acl: branch access granted: "ef1ea85a6374" on branch "default"
1496 acl: branch access granted: "ef1ea85a6374" on branch "default"
1497 acl: path access granted: "ef1ea85a6374"
1497 acl: path access granted: "ef1ea85a6374"
1498 acl: branch access granted: "f9cafe1212c8" on branch "default"
1498 acl: branch access granted: "f9cafe1212c8" on branch "default"
1499 acl: path access granted: "f9cafe1212c8"
1499 acl: path access granted: "f9cafe1212c8"
1500 acl: branch access granted: "911600dab2ae" on branch "default"
1500 acl: branch access granted: "911600dab2ae" on branch "default"
1501 acl: path access granted: "911600dab2ae"
1501 acl: path access granted: "911600dab2ae"
1502 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1502 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1503 acl: path access granted: "e8fc755d4d82"
1503 acl: path access granted: "e8fc755d4d82"
1504 listing keys for "phases"
1504 listing keys for "phases"
1505 try to push obsolete markers to remote
1505 try to push obsolete markers to remote
1506 updating the branch cache
1506 updating the branch cache
1507 checking for updated bookmarks
1507 checking for updated bookmarks
1508 listing keys for "bookmarks"
1508 listing keys for "bookmarks"
1509 repository tip rolled back to revision 2 (undo push)
1509 repository tip rolled back to revision 2 (undo push)
1510 2:fb35475503ef
1510 2:fb35475503ef
1511
1511
1512
1512
1513 Branch acl deny test
1513 Branch acl deny test
1514
1514
1515 $ echo "[acl.deny.branches]" >> $config
1515 $ echo "[acl.deny.branches]" >> $config
1516 $ echo "foobar = *" >> $config
1516 $ echo "foobar = *" >> $config
1517 $ do_push astro
1517 $ do_push astro
1518 Pushing as user astro
1518 Pushing as user astro
1519 hgrc = """
1519 hgrc = """
1520 [acl]
1520 [acl]
1521 sources = push
1521 sources = push
1522 [extensions]
1522 [extensions]
1523 [acl.deny.branches]
1523 [acl.deny.branches]
1524 foobar = *
1524 foobar = *
1525 """
1525 """
1526 pushing to ../b
1526 pushing to ../b
1527 query 1; heads
1527 query 1; heads
1528 searching for changes
1528 searching for changes
1529 all remote heads known locally
1529 all remote heads known locally
1530 invalid branchheads cache (served): tip differs
1531 listing keys for "bookmarks"
1530 listing keys for "bookmarks"
1532 4 changesets found
1531 4 changesets found
1533 list of changesets:
1532 list of changesets:
1534 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1533 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1535 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1534 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1536 911600dab2ae7a9baff75958b84fe606851ce955
1535 911600dab2ae7a9baff75958b84fe606851ce955
1537 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1536 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1538 adding changesets
1537 adding changesets
1539 bundling: 1/4 changesets (25.00%)
1538 bundling: 1/4 changesets (25.00%)
1540 bundling: 2/4 changesets (50.00%)
1539 bundling: 2/4 changesets (50.00%)
1541 bundling: 3/4 changesets (75.00%)
1540 bundling: 3/4 changesets (75.00%)
1542 bundling: 4/4 changesets (100.00%)
1541 bundling: 4/4 changesets (100.00%)
1543 bundling: 1/4 manifests (25.00%)
1542 bundling: 1/4 manifests (25.00%)
1544 bundling: 2/4 manifests (50.00%)
1543 bundling: 2/4 manifests (50.00%)
1545 bundling: 3/4 manifests (75.00%)
1544 bundling: 3/4 manifests (75.00%)
1546 bundling: 4/4 manifests (100.00%)
1545 bundling: 4/4 manifests (100.00%)
1547 bundling: abc.txt 1/4 files (25.00%)
1546 bundling: abc.txt 1/4 files (25.00%)
1548 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1547 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1549 bundling: foo/file.txt 3/4 files (75.00%)
1548 bundling: foo/file.txt 3/4 files (75.00%)
1550 bundling: quux/file.py 4/4 files (100.00%)
1549 bundling: quux/file.py 4/4 files (100.00%)
1551 changesets: 1 chunks
1550 changesets: 1 chunks
1552 add changeset ef1ea85a6374
1551 add changeset ef1ea85a6374
1553 changesets: 2 chunks
1552 changesets: 2 chunks
1554 add changeset f9cafe1212c8
1553 add changeset f9cafe1212c8
1555 changesets: 3 chunks
1554 changesets: 3 chunks
1556 add changeset 911600dab2ae
1555 add changeset 911600dab2ae
1557 changesets: 4 chunks
1556 changesets: 4 chunks
1558 add changeset e8fc755d4d82
1557 add changeset e8fc755d4d82
1559 adding manifests
1558 adding manifests
1560 manifests: 1/4 chunks (25.00%)
1559 manifests: 1/4 chunks (25.00%)
1561 manifests: 2/4 chunks (50.00%)
1560 manifests: 2/4 chunks (50.00%)
1562 manifests: 3/4 chunks (75.00%)
1561 manifests: 3/4 chunks (75.00%)
1563 manifests: 4/4 chunks (100.00%)
1562 manifests: 4/4 chunks (100.00%)
1564 adding file changes
1563 adding file changes
1565 adding abc.txt revisions
1564 adding abc.txt revisions
1566 files: 1/4 chunks (25.00%)
1565 files: 1/4 chunks (25.00%)
1567 adding foo/Bar/file.txt revisions
1566 adding foo/Bar/file.txt revisions
1568 files: 2/4 chunks (50.00%)
1567 files: 2/4 chunks (50.00%)
1569 adding foo/file.txt revisions
1568 adding foo/file.txt revisions
1570 files: 3/4 chunks (75.00%)
1569 files: 3/4 chunks (75.00%)
1571 adding quux/file.py revisions
1570 adding quux/file.py revisions
1572 files: 4/4 chunks (100.00%)
1571 files: 4/4 chunks (100.00%)
1573 added 4 changesets with 4 changes to 4 files (+1 heads)
1572 added 4 changesets with 4 changes to 4 files (+1 heads)
1574 calling hook pretxnchangegroup.acl: hgext.acl.hook
1573 calling hook pretxnchangegroup.acl: hgext.acl.hook
1575 acl: checking access for user "astro"
1574 acl: checking access for user "astro"
1576 acl: acl.allow.branches not enabled
1575 acl: acl.allow.branches not enabled
1577 acl: acl.deny.branches enabled, 1 entries for user astro
1576 acl: acl.deny.branches enabled, 1 entries for user astro
1578 acl: acl.allow not enabled
1577 acl: acl.allow not enabled
1579 acl: acl.deny not enabled
1578 acl: acl.deny not enabled
1580 acl: branch access granted: "ef1ea85a6374" on branch "default"
1579 acl: branch access granted: "ef1ea85a6374" on branch "default"
1581 acl: path access granted: "ef1ea85a6374"
1580 acl: path access granted: "ef1ea85a6374"
1582 acl: branch access granted: "f9cafe1212c8" on branch "default"
1581 acl: branch access granted: "f9cafe1212c8" on branch "default"
1583 acl: path access granted: "f9cafe1212c8"
1582 acl: path access granted: "f9cafe1212c8"
1584 acl: branch access granted: "911600dab2ae" on branch "default"
1583 acl: branch access granted: "911600dab2ae" on branch "default"
1585 acl: path access granted: "911600dab2ae"
1584 acl: path access granted: "911600dab2ae"
1586 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1585 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1587 transaction abort!
1586 transaction abort!
1588 rollback completed
1587 rollback completed
1589 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1588 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1590 no rollback information available
1589 no rollback information available
1591 2:fb35475503ef
1590 2:fb35475503ef
1592
1591
1593
1592
1594 Branch acl empty allow test
1593 Branch acl empty allow test
1595
1594
1596 $ init_config
1595 $ init_config
1597 $ echo "[acl.allow.branches]" >> $config
1596 $ echo "[acl.allow.branches]" >> $config
1598 $ do_push astro
1597 $ do_push astro
1599 Pushing as user astro
1598 Pushing as user astro
1600 hgrc = """
1599 hgrc = """
1601 [acl]
1600 [acl]
1602 sources = push
1601 sources = push
1603 [extensions]
1602 [extensions]
1604 [acl.allow.branches]
1603 [acl.allow.branches]
1605 """
1604 """
1606 pushing to ../b
1605 pushing to ../b
1607 query 1; heads
1606 query 1; heads
1608 searching for changes
1607 searching for changes
1609 all remote heads known locally
1608 all remote heads known locally
1610 listing keys for "bookmarks"
1609 listing keys for "bookmarks"
1611 4 changesets found
1610 4 changesets found
1612 list of changesets:
1611 list of changesets:
1613 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1612 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1614 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1613 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1615 911600dab2ae7a9baff75958b84fe606851ce955
1614 911600dab2ae7a9baff75958b84fe606851ce955
1616 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1615 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1617 adding changesets
1616 adding changesets
1618 bundling: 1/4 changesets (25.00%)
1617 bundling: 1/4 changesets (25.00%)
1619 bundling: 2/4 changesets (50.00%)
1618 bundling: 2/4 changesets (50.00%)
1620 bundling: 3/4 changesets (75.00%)
1619 bundling: 3/4 changesets (75.00%)
1621 bundling: 4/4 changesets (100.00%)
1620 bundling: 4/4 changesets (100.00%)
1622 bundling: 1/4 manifests (25.00%)
1621 bundling: 1/4 manifests (25.00%)
1623 bundling: 2/4 manifests (50.00%)
1622 bundling: 2/4 manifests (50.00%)
1624 bundling: 3/4 manifests (75.00%)
1623 bundling: 3/4 manifests (75.00%)
1625 bundling: 4/4 manifests (100.00%)
1624 bundling: 4/4 manifests (100.00%)
1626 bundling: abc.txt 1/4 files (25.00%)
1625 bundling: abc.txt 1/4 files (25.00%)
1627 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1626 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1628 bundling: foo/file.txt 3/4 files (75.00%)
1627 bundling: foo/file.txt 3/4 files (75.00%)
1629 bundling: quux/file.py 4/4 files (100.00%)
1628 bundling: quux/file.py 4/4 files (100.00%)
1630 changesets: 1 chunks
1629 changesets: 1 chunks
1631 add changeset ef1ea85a6374
1630 add changeset ef1ea85a6374
1632 changesets: 2 chunks
1631 changesets: 2 chunks
1633 add changeset f9cafe1212c8
1632 add changeset f9cafe1212c8
1634 changesets: 3 chunks
1633 changesets: 3 chunks
1635 add changeset 911600dab2ae
1634 add changeset 911600dab2ae
1636 changesets: 4 chunks
1635 changesets: 4 chunks
1637 add changeset e8fc755d4d82
1636 add changeset e8fc755d4d82
1638 adding manifests
1637 adding manifests
1639 manifests: 1/4 chunks (25.00%)
1638 manifests: 1/4 chunks (25.00%)
1640 manifests: 2/4 chunks (50.00%)
1639 manifests: 2/4 chunks (50.00%)
1641 manifests: 3/4 chunks (75.00%)
1640 manifests: 3/4 chunks (75.00%)
1642 manifests: 4/4 chunks (100.00%)
1641 manifests: 4/4 chunks (100.00%)
1643 adding file changes
1642 adding file changes
1644 adding abc.txt revisions
1643 adding abc.txt revisions
1645 files: 1/4 chunks (25.00%)
1644 files: 1/4 chunks (25.00%)
1646 adding foo/Bar/file.txt revisions
1645 adding foo/Bar/file.txt revisions
1647 files: 2/4 chunks (50.00%)
1646 files: 2/4 chunks (50.00%)
1648 adding foo/file.txt revisions
1647 adding foo/file.txt revisions
1649 files: 3/4 chunks (75.00%)
1648 files: 3/4 chunks (75.00%)
1650 adding quux/file.py revisions
1649 adding quux/file.py revisions
1651 files: 4/4 chunks (100.00%)
1650 files: 4/4 chunks (100.00%)
1652 added 4 changesets with 4 changes to 4 files (+1 heads)
1651 added 4 changesets with 4 changes to 4 files (+1 heads)
1653 calling hook pretxnchangegroup.acl: hgext.acl.hook
1652 calling hook pretxnchangegroup.acl: hgext.acl.hook
1654 acl: checking access for user "astro"
1653 acl: checking access for user "astro"
1655 acl: acl.allow.branches enabled, 0 entries for user astro
1654 acl: acl.allow.branches enabled, 0 entries for user astro
1656 acl: acl.deny.branches not enabled
1655 acl: acl.deny.branches not enabled
1657 acl: acl.allow not enabled
1656 acl: acl.allow not enabled
1658 acl: acl.deny not enabled
1657 acl: acl.deny not enabled
1659 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1658 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1660 transaction abort!
1659 transaction abort!
1661 rollback completed
1660 rollback completed
1662 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1661 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1663 no rollback information available
1662 no rollback information available
1664 2:fb35475503ef
1663 2:fb35475503ef
1665
1664
1666
1665
1667 Branch acl allow other
1666 Branch acl allow other
1668
1667
1669 $ init_config
1668 $ init_config
1670 $ echo "[acl.allow.branches]" >> $config
1669 $ echo "[acl.allow.branches]" >> $config
1671 $ echo "* = george" >> $config
1670 $ echo "* = george" >> $config
1672 $ do_push astro
1671 $ do_push astro
1673 Pushing as user astro
1672 Pushing as user astro
1674 hgrc = """
1673 hgrc = """
1675 [acl]
1674 [acl]
1676 sources = push
1675 sources = push
1677 [extensions]
1676 [extensions]
1678 [acl.allow.branches]
1677 [acl.allow.branches]
1679 * = george
1678 * = george
1680 """
1679 """
1681 pushing to ../b
1680 pushing to ../b
1682 query 1; heads
1681 query 1; heads
1683 searching for changes
1682 searching for changes
1684 all remote heads known locally
1683 all remote heads known locally
1685 listing keys for "bookmarks"
1684 listing keys for "bookmarks"
1686 4 changesets found
1685 4 changesets found
1687 list of changesets:
1686 list of changesets:
1688 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1687 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1689 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1688 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1690 911600dab2ae7a9baff75958b84fe606851ce955
1689 911600dab2ae7a9baff75958b84fe606851ce955
1691 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1690 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1692 adding changesets
1691 adding changesets
1693 bundling: 1/4 changesets (25.00%)
1692 bundling: 1/4 changesets (25.00%)
1694 bundling: 2/4 changesets (50.00%)
1693 bundling: 2/4 changesets (50.00%)
1695 bundling: 3/4 changesets (75.00%)
1694 bundling: 3/4 changesets (75.00%)
1696 bundling: 4/4 changesets (100.00%)
1695 bundling: 4/4 changesets (100.00%)
1697 bundling: 1/4 manifests (25.00%)
1696 bundling: 1/4 manifests (25.00%)
1698 bundling: 2/4 manifests (50.00%)
1697 bundling: 2/4 manifests (50.00%)
1699 bundling: 3/4 manifests (75.00%)
1698 bundling: 3/4 manifests (75.00%)
1700 bundling: 4/4 manifests (100.00%)
1699 bundling: 4/4 manifests (100.00%)
1701 bundling: abc.txt 1/4 files (25.00%)
1700 bundling: abc.txt 1/4 files (25.00%)
1702 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1701 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1703 bundling: foo/file.txt 3/4 files (75.00%)
1702 bundling: foo/file.txt 3/4 files (75.00%)
1704 bundling: quux/file.py 4/4 files (100.00%)
1703 bundling: quux/file.py 4/4 files (100.00%)
1705 changesets: 1 chunks
1704 changesets: 1 chunks
1706 add changeset ef1ea85a6374
1705 add changeset ef1ea85a6374
1707 changesets: 2 chunks
1706 changesets: 2 chunks
1708 add changeset f9cafe1212c8
1707 add changeset f9cafe1212c8
1709 changesets: 3 chunks
1708 changesets: 3 chunks
1710 add changeset 911600dab2ae
1709 add changeset 911600dab2ae
1711 changesets: 4 chunks
1710 changesets: 4 chunks
1712 add changeset e8fc755d4d82
1711 add changeset e8fc755d4d82
1713 adding manifests
1712 adding manifests
1714 manifests: 1/4 chunks (25.00%)
1713 manifests: 1/4 chunks (25.00%)
1715 manifests: 2/4 chunks (50.00%)
1714 manifests: 2/4 chunks (50.00%)
1716 manifests: 3/4 chunks (75.00%)
1715 manifests: 3/4 chunks (75.00%)
1717 manifests: 4/4 chunks (100.00%)
1716 manifests: 4/4 chunks (100.00%)
1718 adding file changes
1717 adding file changes
1719 adding abc.txt revisions
1718 adding abc.txt revisions
1720 files: 1/4 chunks (25.00%)
1719 files: 1/4 chunks (25.00%)
1721 adding foo/Bar/file.txt revisions
1720 adding foo/Bar/file.txt revisions
1722 files: 2/4 chunks (50.00%)
1721 files: 2/4 chunks (50.00%)
1723 adding foo/file.txt revisions
1722 adding foo/file.txt revisions
1724 files: 3/4 chunks (75.00%)
1723 files: 3/4 chunks (75.00%)
1725 adding quux/file.py revisions
1724 adding quux/file.py revisions
1726 files: 4/4 chunks (100.00%)
1725 files: 4/4 chunks (100.00%)
1727 added 4 changesets with 4 changes to 4 files (+1 heads)
1726 added 4 changesets with 4 changes to 4 files (+1 heads)
1728 calling hook pretxnchangegroup.acl: hgext.acl.hook
1727 calling hook pretxnchangegroup.acl: hgext.acl.hook
1729 acl: checking access for user "astro"
1728 acl: checking access for user "astro"
1730 acl: acl.allow.branches enabled, 0 entries for user astro
1729 acl: acl.allow.branches enabled, 0 entries for user astro
1731 acl: acl.deny.branches not enabled
1730 acl: acl.deny.branches not enabled
1732 acl: acl.allow not enabled
1731 acl: acl.allow not enabled
1733 acl: acl.deny not enabled
1732 acl: acl.deny not enabled
1734 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1733 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1735 transaction abort!
1734 transaction abort!
1736 rollback completed
1735 rollback completed
1737 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1736 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1738 no rollback information available
1737 no rollback information available
1739 2:fb35475503ef
1738 2:fb35475503ef
1740
1739
1741 $ do_push george
1740 $ do_push george
1742 Pushing as user george
1741 Pushing as user george
1743 hgrc = """
1742 hgrc = """
1744 [acl]
1743 [acl]
1745 sources = push
1744 sources = push
1746 [extensions]
1745 [extensions]
1747 [acl.allow.branches]
1746 [acl.allow.branches]
1748 * = george
1747 * = george
1749 """
1748 """
1750 pushing to ../b
1749 pushing to ../b
1751 query 1; heads
1750 query 1; heads
1752 searching for changes
1751 searching for changes
1753 all remote heads known locally
1752 all remote heads known locally
1754 listing keys for "bookmarks"
1753 listing keys for "bookmarks"
1755 4 changesets found
1754 4 changesets found
1756 list of changesets:
1755 list of changesets:
1757 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1756 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1758 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1757 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1759 911600dab2ae7a9baff75958b84fe606851ce955
1758 911600dab2ae7a9baff75958b84fe606851ce955
1760 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1759 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1761 adding changesets
1760 adding changesets
1762 bundling: 1/4 changesets (25.00%)
1761 bundling: 1/4 changesets (25.00%)
1763 bundling: 2/4 changesets (50.00%)
1762 bundling: 2/4 changesets (50.00%)
1764 bundling: 3/4 changesets (75.00%)
1763 bundling: 3/4 changesets (75.00%)
1765 bundling: 4/4 changesets (100.00%)
1764 bundling: 4/4 changesets (100.00%)
1766 bundling: 1/4 manifests (25.00%)
1765 bundling: 1/4 manifests (25.00%)
1767 bundling: 2/4 manifests (50.00%)
1766 bundling: 2/4 manifests (50.00%)
1768 bundling: 3/4 manifests (75.00%)
1767 bundling: 3/4 manifests (75.00%)
1769 bundling: 4/4 manifests (100.00%)
1768 bundling: 4/4 manifests (100.00%)
1770 bundling: abc.txt 1/4 files (25.00%)
1769 bundling: abc.txt 1/4 files (25.00%)
1771 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1770 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1772 bundling: foo/file.txt 3/4 files (75.00%)
1771 bundling: foo/file.txt 3/4 files (75.00%)
1773 bundling: quux/file.py 4/4 files (100.00%)
1772 bundling: quux/file.py 4/4 files (100.00%)
1774 changesets: 1 chunks
1773 changesets: 1 chunks
1775 add changeset ef1ea85a6374
1774 add changeset ef1ea85a6374
1776 changesets: 2 chunks
1775 changesets: 2 chunks
1777 add changeset f9cafe1212c8
1776 add changeset f9cafe1212c8
1778 changesets: 3 chunks
1777 changesets: 3 chunks
1779 add changeset 911600dab2ae
1778 add changeset 911600dab2ae
1780 changesets: 4 chunks
1779 changesets: 4 chunks
1781 add changeset e8fc755d4d82
1780 add changeset e8fc755d4d82
1782 adding manifests
1781 adding manifests
1783 manifests: 1/4 chunks (25.00%)
1782 manifests: 1/4 chunks (25.00%)
1784 manifests: 2/4 chunks (50.00%)
1783 manifests: 2/4 chunks (50.00%)
1785 manifests: 3/4 chunks (75.00%)
1784 manifests: 3/4 chunks (75.00%)
1786 manifests: 4/4 chunks (100.00%)
1785 manifests: 4/4 chunks (100.00%)
1787 adding file changes
1786 adding file changes
1788 adding abc.txt revisions
1787 adding abc.txt revisions
1789 files: 1/4 chunks (25.00%)
1788 files: 1/4 chunks (25.00%)
1790 adding foo/Bar/file.txt revisions
1789 adding foo/Bar/file.txt revisions
1791 files: 2/4 chunks (50.00%)
1790 files: 2/4 chunks (50.00%)
1792 adding foo/file.txt revisions
1791 adding foo/file.txt revisions
1793 files: 3/4 chunks (75.00%)
1792 files: 3/4 chunks (75.00%)
1794 adding quux/file.py revisions
1793 adding quux/file.py revisions
1795 files: 4/4 chunks (100.00%)
1794 files: 4/4 chunks (100.00%)
1796 added 4 changesets with 4 changes to 4 files (+1 heads)
1795 added 4 changesets with 4 changes to 4 files (+1 heads)
1797 calling hook pretxnchangegroup.acl: hgext.acl.hook
1796 calling hook pretxnchangegroup.acl: hgext.acl.hook
1798 acl: checking access for user "george"
1797 acl: checking access for user "george"
1799 acl: acl.allow.branches enabled, 1 entries for user george
1798 acl: acl.allow.branches enabled, 1 entries for user george
1800 acl: acl.deny.branches not enabled
1799 acl: acl.deny.branches not enabled
1801 acl: acl.allow not enabled
1800 acl: acl.allow not enabled
1802 acl: acl.deny not enabled
1801 acl: acl.deny not enabled
1803 acl: branch access granted: "ef1ea85a6374" on branch "default"
1802 acl: branch access granted: "ef1ea85a6374" on branch "default"
1804 acl: path access granted: "ef1ea85a6374"
1803 acl: path access granted: "ef1ea85a6374"
1805 acl: branch access granted: "f9cafe1212c8" on branch "default"
1804 acl: branch access granted: "f9cafe1212c8" on branch "default"
1806 acl: path access granted: "f9cafe1212c8"
1805 acl: path access granted: "f9cafe1212c8"
1807 acl: branch access granted: "911600dab2ae" on branch "default"
1806 acl: branch access granted: "911600dab2ae" on branch "default"
1808 acl: path access granted: "911600dab2ae"
1807 acl: path access granted: "911600dab2ae"
1809 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1808 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1810 acl: path access granted: "e8fc755d4d82"
1809 acl: path access granted: "e8fc755d4d82"
1811 listing keys for "phases"
1810 listing keys for "phases"
1812 try to push obsolete markers to remote
1811 try to push obsolete markers to remote
1813 updating the branch cache
1812 updating the branch cache
1814 checking for updated bookmarks
1813 checking for updated bookmarks
1815 listing keys for "bookmarks"
1814 listing keys for "bookmarks"
1816 repository tip rolled back to revision 2 (undo push)
1815 repository tip rolled back to revision 2 (undo push)
1817 2:fb35475503ef
1816 2:fb35475503ef
1818
1817
1819
1818
1820 Branch acl conflicting allow
1819 Branch acl conflicting allow
1821 asterisk ends up applying to all branches and allowing george to
1820 asterisk ends up applying to all branches and allowing george to
1822 push foobar into the remote
1821 push foobar into the remote
1823
1822
1824 $ init_config
1823 $ init_config
1825 $ echo "[acl.allow.branches]" >> $config
1824 $ echo "[acl.allow.branches]" >> $config
1826 $ echo "foobar = astro" >> $config
1825 $ echo "foobar = astro" >> $config
1827 $ echo "* = george" >> $config
1826 $ echo "* = george" >> $config
1828 $ do_push george
1827 $ do_push george
1829 Pushing as user george
1828 Pushing as user george
1830 hgrc = """
1829 hgrc = """
1831 [acl]
1830 [acl]
1832 sources = push
1831 sources = push
1833 [extensions]
1832 [extensions]
1834 [acl.allow.branches]
1833 [acl.allow.branches]
1835 foobar = astro
1834 foobar = astro
1836 * = george
1835 * = george
1837 """
1836 """
1838 pushing to ../b
1837 pushing to ../b
1839 query 1; heads
1838 query 1; heads
1840 searching for changes
1839 searching for changes
1841 all remote heads known locally
1840 all remote heads known locally
1842 invalid branchheads cache (served): tip differs
1843 listing keys for "bookmarks"
1841 listing keys for "bookmarks"
1844 4 changesets found
1842 4 changesets found
1845 list of changesets:
1843 list of changesets:
1846 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1844 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1847 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1845 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1848 911600dab2ae7a9baff75958b84fe606851ce955
1846 911600dab2ae7a9baff75958b84fe606851ce955
1849 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1847 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1850 adding changesets
1848 adding changesets
1851 bundling: 1/4 changesets (25.00%)
1849 bundling: 1/4 changesets (25.00%)
1852 bundling: 2/4 changesets (50.00%)
1850 bundling: 2/4 changesets (50.00%)
1853 bundling: 3/4 changesets (75.00%)
1851 bundling: 3/4 changesets (75.00%)
1854 bundling: 4/4 changesets (100.00%)
1852 bundling: 4/4 changesets (100.00%)
1855 bundling: 1/4 manifests (25.00%)
1853 bundling: 1/4 manifests (25.00%)
1856 bundling: 2/4 manifests (50.00%)
1854 bundling: 2/4 manifests (50.00%)
1857 bundling: 3/4 manifests (75.00%)
1855 bundling: 3/4 manifests (75.00%)
1858 bundling: 4/4 manifests (100.00%)
1856 bundling: 4/4 manifests (100.00%)
1859 bundling: abc.txt 1/4 files (25.00%)
1857 bundling: abc.txt 1/4 files (25.00%)
1860 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1858 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1861 bundling: foo/file.txt 3/4 files (75.00%)
1859 bundling: foo/file.txt 3/4 files (75.00%)
1862 bundling: quux/file.py 4/4 files (100.00%)
1860 bundling: quux/file.py 4/4 files (100.00%)
1863 changesets: 1 chunks
1861 changesets: 1 chunks
1864 add changeset ef1ea85a6374
1862 add changeset ef1ea85a6374
1865 changesets: 2 chunks
1863 changesets: 2 chunks
1866 add changeset f9cafe1212c8
1864 add changeset f9cafe1212c8
1867 changesets: 3 chunks
1865 changesets: 3 chunks
1868 add changeset 911600dab2ae
1866 add changeset 911600dab2ae
1869 changesets: 4 chunks
1867 changesets: 4 chunks
1870 add changeset e8fc755d4d82
1868 add changeset e8fc755d4d82
1871 adding manifests
1869 adding manifests
1872 manifests: 1/4 chunks (25.00%)
1870 manifests: 1/4 chunks (25.00%)
1873 manifests: 2/4 chunks (50.00%)
1871 manifests: 2/4 chunks (50.00%)
1874 manifests: 3/4 chunks (75.00%)
1872 manifests: 3/4 chunks (75.00%)
1875 manifests: 4/4 chunks (100.00%)
1873 manifests: 4/4 chunks (100.00%)
1876 adding file changes
1874 adding file changes
1877 adding abc.txt revisions
1875 adding abc.txt revisions
1878 files: 1/4 chunks (25.00%)
1876 files: 1/4 chunks (25.00%)
1879 adding foo/Bar/file.txt revisions
1877 adding foo/Bar/file.txt revisions
1880 files: 2/4 chunks (50.00%)
1878 files: 2/4 chunks (50.00%)
1881 adding foo/file.txt revisions
1879 adding foo/file.txt revisions
1882 files: 3/4 chunks (75.00%)
1880 files: 3/4 chunks (75.00%)
1883 adding quux/file.py revisions
1881 adding quux/file.py revisions
1884 files: 4/4 chunks (100.00%)
1882 files: 4/4 chunks (100.00%)
1885 added 4 changesets with 4 changes to 4 files (+1 heads)
1883 added 4 changesets with 4 changes to 4 files (+1 heads)
1886 calling hook pretxnchangegroup.acl: hgext.acl.hook
1884 calling hook pretxnchangegroup.acl: hgext.acl.hook
1887 acl: checking access for user "george"
1885 acl: checking access for user "george"
1888 acl: acl.allow.branches enabled, 1 entries for user george
1886 acl: acl.allow.branches enabled, 1 entries for user george
1889 acl: acl.deny.branches not enabled
1887 acl: acl.deny.branches not enabled
1890 acl: acl.allow not enabled
1888 acl: acl.allow not enabled
1891 acl: acl.deny not enabled
1889 acl: acl.deny not enabled
1892 acl: branch access granted: "ef1ea85a6374" on branch "default"
1890 acl: branch access granted: "ef1ea85a6374" on branch "default"
1893 acl: path access granted: "ef1ea85a6374"
1891 acl: path access granted: "ef1ea85a6374"
1894 acl: branch access granted: "f9cafe1212c8" on branch "default"
1892 acl: branch access granted: "f9cafe1212c8" on branch "default"
1895 acl: path access granted: "f9cafe1212c8"
1893 acl: path access granted: "f9cafe1212c8"
1896 acl: branch access granted: "911600dab2ae" on branch "default"
1894 acl: branch access granted: "911600dab2ae" on branch "default"
1897 acl: path access granted: "911600dab2ae"
1895 acl: path access granted: "911600dab2ae"
1898 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1896 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1899 acl: path access granted: "e8fc755d4d82"
1897 acl: path access granted: "e8fc755d4d82"
1900 listing keys for "phases"
1898 listing keys for "phases"
1901 try to push obsolete markers to remote
1899 try to push obsolete markers to remote
1902 updating the branch cache
1900 updating the branch cache
1903 checking for updated bookmarks
1901 checking for updated bookmarks
1904 listing keys for "bookmarks"
1902 listing keys for "bookmarks"
1905 repository tip rolled back to revision 2 (undo push)
1903 repository tip rolled back to revision 2 (undo push)
1906 2:fb35475503ef
1904 2:fb35475503ef
1907
1905
1908 Branch acl conflicting deny
1906 Branch acl conflicting deny
1909
1907
1910 $ init_config
1908 $ init_config
1911 $ echo "[acl.deny.branches]" >> $config
1909 $ echo "[acl.deny.branches]" >> $config
1912 $ echo "foobar = astro" >> $config
1910 $ echo "foobar = astro" >> $config
1913 $ echo "default = astro" >> $config
1911 $ echo "default = astro" >> $config
1914 $ echo "* = george" >> $config
1912 $ echo "* = george" >> $config
1915 $ do_push george
1913 $ do_push george
1916 Pushing as user george
1914 Pushing as user george
1917 hgrc = """
1915 hgrc = """
1918 [acl]
1916 [acl]
1919 sources = push
1917 sources = push
1920 [extensions]
1918 [extensions]
1921 [acl.deny.branches]
1919 [acl.deny.branches]
1922 foobar = astro
1920 foobar = astro
1923 default = astro
1921 default = astro
1924 * = george
1922 * = george
1925 """
1923 """
1926 pushing to ../b
1924 pushing to ../b
1927 query 1; heads
1925 query 1; heads
1928 searching for changes
1926 searching for changes
1929 all remote heads known locally
1927 all remote heads known locally
1930 invalid branchheads cache (served): tip differs
1931 listing keys for "bookmarks"
1928 listing keys for "bookmarks"
1932 4 changesets found
1929 4 changesets found
1933 list of changesets:
1930 list of changesets:
1934 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1931 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1935 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1932 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1936 911600dab2ae7a9baff75958b84fe606851ce955
1933 911600dab2ae7a9baff75958b84fe606851ce955
1937 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1934 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1938 adding changesets
1935 adding changesets
1939 bundling: 1/4 changesets (25.00%)
1936 bundling: 1/4 changesets (25.00%)
1940 bundling: 2/4 changesets (50.00%)
1937 bundling: 2/4 changesets (50.00%)
1941 bundling: 3/4 changesets (75.00%)
1938 bundling: 3/4 changesets (75.00%)
1942 bundling: 4/4 changesets (100.00%)
1939 bundling: 4/4 changesets (100.00%)
1943 bundling: 1/4 manifests (25.00%)
1940 bundling: 1/4 manifests (25.00%)
1944 bundling: 2/4 manifests (50.00%)
1941 bundling: 2/4 manifests (50.00%)
1945 bundling: 3/4 manifests (75.00%)
1942 bundling: 3/4 manifests (75.00%)
1946 bundling: 4/4 manifests (100.00%)
1943 bundling: 4/4 manifests (100.00%)
1947 bundling: abc.txt 1/4 files (25.00%)
1944 bundling: abc.txt 1/4 files (25.00%)
1948 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1945 bundling: foo/Bar/file.txt 2/4 files (50.00%)
1949 bundling: foo/file.txt 3/4 files (75.00%)
1946 bundling: foo/file.txt 3/4 files (75.00%)
1950 bundling: quux/file.py 4/4 files (100.00%)
1947 bundling: quux/file.py 4/4 files (100.00%)
1951 changesets: 1 chunks
1948 changesets: 1 chunks
1952 add changeset ef1ea85a6374
1949 add changeset ef1ea85a6374
1953 changesets: 2 chunks
1950 changesets: 2 chunks
1954 add changeset f9cafe1212c8
1951 add changeset f9cafe1212c8
1955 changesets: 3 chunks
1952 changesets: 3 chunks
1956 add changeset 911600dab2ae
1953 add changeset 911600dab2ae
1957 changesets: 4 chunks
1954 changesets: 4 chunks
1958 add changeset e8fc755d4d82
1955 add changeset e8fc755d4d82
1959 adding manifests
1956 adding manifests
1960 manifests: 1/4 chunks (25.00%)
1957 manifests: 1/4 chunks (25.00%)
1961 manifests: 2/4 chunks (50.00%)
1958 manifests: 2/4 chunks (50.00%)
1962 manifests: 3/4 chunks (75.00%)
1959 manifests: 3/4 chunks (75.00%)
1963 manifests: 4/4 chunks (100.00%)
1960 manifests: 4/4 chunks (100.00%)
1964 adding file changes
1961 adding file changes
1965 adding abc.txt revisions
1962 adding abc.txt revisions
1966 files: 1/4 chunks (25.00%)
1963 files: 1/4 chunks (25.00%)
1967 adding foo/Bar/file.txt revisions
1964 adding foo/Bar/file.txt revisions
1968 files: 2/4 chunks (50.00%)
1965 files: 2/4 chunks (50.00%)
1969 adding foo/file.txt revisions
1966 adding foo/file.txt revisions
1970 files: 3/4 chunks (75.00%)
1967 files: 3/4 chunks (75.00%)
1971 adding quux/file.py revisions
1968 adding quux/file.py revisions
1972 files: 4/4 chunks (100.00%)
1969 files: 4/4 chunks (100.00%)
1973 added 4 changesets with 4 changes to 4 files (+1 heads)
1970 added 4 changesets with 4 changes to 4 files (+1 heads)
1974 calling hook pretxnchangegroup.acl: hgext.acl.hook
1971 calling hook pretxnchangegroup.acl: hgext.acl.hook
1975 acl: checking access for user "george"
1972 acl: checking access for user "george"
1976 acl: acl.allow.branches not enabled
1973 acl: acl.allow.branches not enabled
1977 acl: acl.deny.branches enabled, 1 entries for user george
1974 acl: acl.deny.branches enabled, 1 entries for user george
1978 acl: acl.allow not enabled
1975 acl: acl.allow not enabled
1979 acl: acl.deny not enabled
1976 acl: acl.deny not enabled
1980 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1977 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1981 transaction abort!
1978 transaction abort!
1982 rollback completed
1979 rollback completed
1983 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1980 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1984 no rollback information available
1981 no rollback information available
1985 2:fb35475503ef
1982 2:fb35475503ef
1986
1983
1987 User 'astro' must not be denied
1984 User 'astro' must not be denied
1988
1985
1989 $ init_config
1986 $ init_config
1990 $ echo "[acl.deny.branches]" >> $config
1987 $ echo "[acl.deny.branches]" >> $config
1991 $ echo "default = !astro" >> $config
1988 $ echo "default = !astro" >> $config
1992 $ do_push astro
1989 $ do_push astro
1993 Pushing as user astro
1990 Pushing as user astro
1994 hgrc = """
1991 hgrc = """
1995 [acl]
1992 [acl]
1996 sources = push
1993 sources = push
1997 [extensions]
1994 [extensions]
1998 [acl.deny.branches]
1995 [acl.deny.branches]
1999 default = !astro
1996 default = !astro
2000 """
1997 """
2001 pushing to ../b
1998 pushing to ../b
2002 query 1; heads
1999 query 1; heads
2003 searching for changes
2000 searching for changes
2004 all remote heads known locally
2001 all remote heads known locally
2005 listing keys for "bookmarks"
2002 listing keys for "bookmarks"
2006 4 changesets found
2003 4 changesets found
2007 list of changesets:
2004 list of changesets:
2008 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2005 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2009 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2006 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2010 911600dab2ae7a9baff75958b84fe606851ce955
2007 911600dab2ae7a9baff75958b84fe606851ce955
2011 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2008 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2012 adding changesets
2009 adding changesets
2013 bundling: 1/4 changesets (25.00%)
2010 bundling: 1/4 changesets (25.00%)
2014 bundling: 2/4 changesets (50.00%)
2011 bundling: 2/4 changesets (50.00%)
2015 bundling: 3/4 changesets (75.00%)
2012 bundling: 3/4 changesets (75.00%)
2016 bundling: 4/4 changesets (100.00%)
2013 bundling: 4/4 changesets (100.00%)
2017 bundling: 1/4 manifests (25.00%)
2014 bundling: 1/4 manifests (25.00%)
2018 bundling: 2/4 manifests (50.00%)
2015 bundling: 2/4 manifests (50.00%)
2019 bundling: 3/4 manifests (75.00%)
2016 bundling: 3/4 manifests (75.00%)
2020 bundling: 4/4 manifests (100.00%)
2017 bundling: 4/4 manifests (100.00%)
2021 bundling: abc.txt 1/4 files (25.00%)
2018 bundling: abc.txt 1/4 files (25.00%)
2022 bundling: foo/Bar/file.txt 2/4 files (50.00%)
2019 bundling: foo/Bar/file.txt 2/4 files (50.00%)
2023 bundling: foo/file.txt 3/4 files (75.00%)
2020 bundling: foo/file.txt 3/4 files (75.00%)
2024 bundling: quux/file.py 4/4 files (100.00%)
2021 bundling: quux/file.py 4/4 files (100.00%)
2025 changesets: 1 chunks
2022 changesets: 1 chunks
2026 add changeset ef1ea85a6374
2023 add changeset ef1ea85a6374
2027 changesets: 2 chunks
2024 changesets: 2 chunks
2028 add changeset f9cafe1212c8
2025 add changeset f9cafe1212c8
2029 changesets: 3 chunks
2026 changesets: 3 chunks
2030 add changeset 911600dab2ae
2027 add changeset 911600dab2ae
2031 changesets: 4 chunks
2028 changesets: 4 chunks
2032 add changeset e8fc755d4d82
2029 add changeset e8fc755d4d82
2033 adding manifests
2030 adding manifests
2034 manifests: 1/4 chunks (25.00%)
2031 manifests: 1/4 chunks (25.00%)
2035 manifests: 2/4 chunks (50.00%)
2032 manifests: 2/4 chunks (50.00%)
2036 manifests: 3/4 chunks (75.00%)
2033 manifests: 3/4 chunks (75.00%)
2037 manifests: 4/4 chunks (100.00%)
2034 manifests: 4/4 chunks (100.00%)
2038 adding file changes
2035 adding file changes
2039 adding abc.txt revisions
2036 adding abc.txt revisions
2040 files: 1/4 chunks (25.00%)
2037 files: 1/4 chunks (25.00%)
2041 adding foo/Bar/file.txt revisions
2038 adding foo/Bar/file.txt revisions
2042 files: 2/4 chunks (50.00%)
2039 files: 2/4 chunks (50.00%)
2043 adding foo/file.txt revisions
2040 adding foo/file.txt revisions
2044 files: 3/4 chunks (75.00%)
2041 files: 3/4 chunks (75.00%)
2045 adding quux/file.py revisions
2042 adding quux/file.py revisions
2046 files: 4/4 chunks (100.00%)
2043 files: 4/4 chunks (100.00%)
2047 added 4 changesets with 4 changes to 4 files (+1 heads)
2044 added 4 changesets with 4 changes to 4 files (+1 heads)
2048 calling hook pretxnchangegroup.acl: hgext.acl.hook
2045 calling hook pretxnchangegroup.acl: hgext.acl.hook
2049 acl: checking access for user "astro"
2046 acl: checking access for user "astro"
2050 acl: acl.allow.branches not enabled
2047 acl: acl.allow.branches not enabled
2051 acl: acl.deny.branches enabled, 0 entries for user astro
2048 acl: acl.deny.branches enabled, 0 entries for user astro
2052 acl: acl.allow not enabled
2049 acl: acl.allow not enabled
2053 acl: acl.deny not enabled
2050 acl: acl.deny not enabled
2054 acl: branch access granted: "ef1ea85a6374" on branch "default"
2051 acl: branch access granted: "ef1ea85a6374" on branch "default"
2055 acl: path access granted: "ef1ea85a6374"
2052 acl: path access granted: "ef1ea85a6374"
2056 acl: branch access granted: "f9cafe1212c8" on branch "default"
2053 acl: branch access granted: "f9cafe1212c8" on branch "default"
2057 acl: path access granted: "f9cafe1212c8"
2054 acl: path access granted: "f9cafe1212c8"
2058 acl: branch access granted: "911600dab2ae" on branch "default"
2055 acl: branch access granted: "911600dab2ae" on branch "default"
2059 acl: path access granted: "911600dab2ae"
2056 acl: path access granted: "911600dab2ae"
2060 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
2057 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
2061 acl: path access granted: "e8fc755d4d82"
2058 acl: path access granted: "e8fc755d4d82"
2062 listing keys for "phases"
2059 listing keys for "phases"
2063 try to push obsolete markers to remote
2060 try to push obsolete markers to remote
2064 updating the branch cache
2061 updating the branch cache
2065 checking for updated bookmarks
2062 checking for updated bookmarks
2066 listing keys for "bookmarks"
2063 listing keys for "bookmarks"
2067 repository tip rolled back to revision 2 (undo push)
2064 repository tip rolled back to revision 2 (undo push)
2068 2:fb35475503ef
2065 2:fb35475503ef
2069
2066
2070
2067
2071 Non-astro users must be denied
2068 Non-astro users must be denied
2072
2069
2073 $ do_push george
2070 $ do_push george
2074 Pushing as user george
2071 Pushing as user george
2075 hgrc = """
2072 hgrc = """
2076 [acl]
2073 [acl]
2077 sources = push
2074 sources = push
2078 [extensions]
2075 [extensions]
2079 [acl.deny.branches]
2076 [acl.deny.branches]
2080 default = !astro
2077 default = !astro
2081 """
2078 """
2082 pushing to ../b
2079 pushing to ../b
2083 query 1; heads
2080 query 1; heads
2084 searching for changes
2081 searching for changes
2085 all remote heads known locally
2082 all remote heads known locally
2086 invalid branchheads cache (served): tip differs
2087 listing keys for "bookmarks"
2083 listing keys for "bookmarks"
2088 4 changesets found
2084 4 changesets found
2089 list of changesets:
2085 list of changesets:
2090 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2086 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2091 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2087 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2092 911600dab2ae7a9baff75958b84fe606851ce955
2088 911600dab2ae7a9baff75958b84fe606851ce955
2093 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2089 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2094 adding changesets
2090 adding changesets
2095 bundling: 1/4 changesets (25.00%)
2091 bundling: 1/4 changesets (25.00%)
2096 bundling: 2/4 changesets (50.00%)
2092 bundling: 2/4 changesets (50.00%)
2097 bundling: 3/4 changesets (75.00%)
2093 bundling: 3/4 changesets (75.00%)
2098 bundling: 4/4 changesets (100.00%)
2094 bundling: 4/4 changesets (100.00%)
2099 bundling: 1/4 manifests (25.00%)
2095 bundling: 1/4 manifests (25.00%)
2100 bundling: 2/4 manifests (50.00%)
2096 bundling: 2/4 manifests (50.00%)
2101 bundling: 3/4 manifests (75.00%)
2097 bundling: 3/4 manifests (75.00%)
2102 bundling: 4/4 manifests (100.00%)
2098 bundling: 4/4 manifests (100.00%)
2103 bundling: abc.txt 1/4 files (25.00%)
2099 bundling: abc.txt 1/4 files (25.00%)
2104 bundling: foo/Bar/file.txt 2/4 files (50.00%)
2100 bundling: foo/Bar/file.txt 2/4 files (50.00%)
2105 bundling: foo/file.txt 3/4 files (75.00%)
2101 bundling: foo/file.txt 3/4 files (75.00%)
2106 bundling: quux/file.py 4/4 files (100.00%)
2102 bundling: quux/file.py 4/4 files (100.00%)
2107 changesets: 1 chunks
2103 changesets: 1 chunks
2108 add changeset ef1ea85a6374
2104 add changeset ef1ea85a6374
2109 changesets: 2 chunks
2105 changesets: 2 chunks
2110 add changeset f9cafe1212c8
2106 add changeset f9cafe1212c8
2111 changesets: 3 chunks
2107 changesets: 3 chunks
2112 add changeset 911600dab2ae
2108 add changeset 911600dab2ae
2113 changesets: 4 chunks
2109 changesets: 4 chunks
2114 add changeset e8fc755d4d82
2110 add changeset e8fc755d4d82
2115 adding manifests
2111 adding manifests
2116 manifests: 1/4 chunks (25.00%)
2112 manifests: 1/4 chunks (25.00%)
2117 manifests: 2/4 chunks (50.00%)
2113 manifests: 2/4 chunks (50.00%)
2118 manifests: 3/4 chunks (75.00%)
2114 manifests: 3/4 chunks (75.00%)
2119 manifests: 4/4 chunks (100.00%)
2115 manifests: 4/4 chunks (100.00%)
2120 adding file changes
2116 adding file changes
2121 adding abc.txt revisions
2117 adding abc.txt revisions
2122 files: 1/4 chunks (25.00%)
2118 files: 1/4 chunks (25.00%)
2123 adding foo/Bar/file.txt revisions
2119 adding foo/Bar/file.txt revisions
2124 files: 2/4 chunks (50.00%)
2120 files: 2/4 chunks (50.00%)
2125 adding foo/file.txt revisions
2121 adding foo/file.txt revisions
2126 files: 3/4 chunks (75.00%)
2122 files: 3/4 chunks (75.00%)
2127 adding quux/file.py revisions
2123 adding quux/file.py revisions
2128 files: 4/4 chunks (100.00%)
2124 files: 4/4 chunks (100.00%)
2129 added 4 changesets with 4 changes to 4 files (+1 heads)
2125 added 4 changesets with 4 changes to 4 files (+1 heads)
2130 calling hook pretxnchangegroup.acl: hgext.acl.hook
2126 calling hook pretxnchangegroup.acl: hgext.acl.hook
2131 acl: checking access for user "george"
2127 acl: checking access for user "george"
2132 acl: acl.allow.branches not enabled
2128 acl: acl.allow.branches not enabled
2133 acl: acl.deny.branches enabled, 1 entries for user george
2129 acl: acl.deny.branches enabled, 1 entries for user george
2134 acl: acl.allow not enabled
2130 acl: acl.allow not enabled
2135 acl: acl.deny not enabled
2131 acl: acl.deny not enabled
2136 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2132 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2137 transaction abort!
2133 transaction abort!
2138 rollback completed
2134 rollback completed
2139 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2135 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2140 no rollback information available
2136 no rollback information available
2141 2:fb35475503ef
2137 2:fb35475503ef
2142
2138
2143
2139
@@ -1,1153 +1,1154 b''
1 $ cat <<EOF >> $HGRCPATH
1 $ cat <<EOF >> $HGRCPATH
2 > [extensions]
2 > [extensions]
3 > keyword =
3 > keyword =
4 > mq =
4 > mq =
5 > notify =
5 > notify =
6 > record =
6 > record =
7 > transplant =
7 > transplant =
8 > [ui]
8 > [ui]
9 > interactive = true
9 > interactive = true
10 > EOF
10 > EOF
11
11
12 hide outer repo
12 hide outer repo
13 $ hg init
13 $ hg init
14
14
15 Run kwdemo before [keyword] files are set up
15 Run kwdemo before [keyword] files are set up
16 as it would succeed without uisetup otherwise
16 as it would succeed without uisetup otherwise
17
17
18 $ hg --quiet kwdemo
18 $ hg --quiet kwdemo
19 [extensions]
19 [extensions]
20 keyword =
20 keyword =
21 [keyword]
21 [keyword]
22 demo.txt =
22 demo.txt =
23 [keywordset]
23 [keywordset]
24 svn = False
24 svn = False
25 [keywordmaps]
25 [keywordmaps]
26 Author = {author|user}
26 Author = {author|user}
27 Date = {date|utcdate}
27 Date = {date|utcdate}
28 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
28 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
29 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
29 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
30 RCSFile = {file|basename},v
30 RCSFile = {file|basename},v
31 RCSfile = {file|basename},v
31 RCSfile = {file|basename},v
32 Revision = {node|short}
32 Revision = {node|short}
33 Source = {root}/{file},v
33 Source = {root}/{file},v
34 $Author: test $
34 $Author: test $
35 $Date: ????/??/?? ??:??:?? $ (glob)
35 $Date: ????/??/?? ??:??:?? $ (glob)
36 $Header: */demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
36 $Header: */demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
37 $Id: demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
37 $Id: demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
38 $RCSFile: demo.txt,v $
38 $RCSFile: demo.txt,v $
39 $RCSfile: demo.txt,v $
39 $RCSfile: demo.txt,v $
40 $Revision: ???????????? $ (glob)
40 $Revision: ???????????? $ (glob)
41 $Source: */demo.txt,v $ (glob)
41 $Source: */demo.txt,v $ (glob)
42
42
43 $ hg --quiet kwdemo "Branch = {branches}"
43 $ hg --quiet kwdemo "Branch = {branches}"
44 [extensions]
44 [extensions]
45 keyword =
45 keyword =
46 [keyword]
46 [keyword]
47 demo.txt =
47 demo.txt =
48 [keywordset]
48 [keywordset]
49 svn = False
49 svn = False
50 [keywordmaps]
50 [keywordmaps]
51 Branch = {branches}
51 Branch = {branches}
52 $Branch: demobranch $
52 $Branch: demobranch $
53
53
54 $ cat <<EOF >> $HGRCPATH
54 $ cat <<EOF >> $HGRCPATH
55 > [keyword]
55 > [keyword]
56 > ** =
56 > ** =
57 > b = ignore
57 > b = ignore
58 > i = ignore
58 > i = ignore
59 > [hooks]
59 > [hooks]
60 > EOF
60 > EOF
61 $ cp $HGRCPATH $HGRCPATH.nohooks
61 $ cp $HGRCPATH $HGRCPATH.nohooks
62 > cat <<EOF >> $HGRCPATH
62 > cat <<EOF >> $HGRCPATH
63 > commit=
63 > commit=
64 > commit.test=cp a hooktest
64 > commit.test=cp a hooktest
65 > EOF
65 > EOF
66
66
67 $ hg init Test-bndl
67 $ hg init Test-bndl
68 $ cd Test-bndl
68 $ cd Test-bndl
69
69
70 kwshrink should exit silently in empty/invalid repo
70 kwshrink should exit silently in empty/invalid repo
71
71
72 $ hg kwshrink
72 $ hg kwshrink
73
73
74 Symlinks cannot be created on Windows.
74 Symlinks cannot be created on Windows.
75 A bundle to test this was made with:
75 A bundle to test this was made with:
76 hg init t
76 hg init t
77 cd t
77 cd t
78 echo a > a
78 echo a > a
79 ln -s a sym
79 ln -s a sym
80 hg add sym
80 hg add sym
81 hg ci -m addsym -u mercurial
81 hg ci -m addsym -u mercurial
82 hg bundle --base null ../test-keyword.hg
82 hg bundle --base null ../test-keyword.hg
83
83
84 $ hg pull -u "$TESTDIR"/bundles/test-keyword.hg
84 $ hg pull -u "$TESTDIR"/bundles/test-keyword.hg
85 pulling from *test-keyword.hg (glob)
85 pulling from *test-keyword.hg (glob)
86 requesting all changes
86 requesting all changes
87 adding changesets
87 adding changesets
88 adding manifests
88 adding manifests
89 adding file changes
89 adding file changes
90 added 1 changesets with 1 changes to 1 files
90 added 1 changesets with 1 changes to 1 files
91 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
92
92
93 $ echo 'expand $Id$' > a
93 $ echo 'expand $Id$' > a
94 $ echo 'do not process $Id:' >> a
94 $ echo 'do not process $Id:' >> a
95 $ echo 'xxx $' >> a
95 $ echo 'xxx $' >> a
96 $ echo 'ignore $Id$' > b
96 $ echo 'ignore $Id$' > b
97
97
98 Output files as they were created
98 Output files as they were created
99
99
100 $ cat a b
100 $ cat a b
101 expand $Id$
101 expand $Id$
102 do not process $Id:
102 do not process $Id:
103 xxx $
103 xxx $
104 ignore $Id$
104 ignore $Id$
105
105
106 no kwfiles
106 no kwfiles
107
107
108 $ hg kwfiles
108 $ hg kwfiles
109
109
110 untracked candidates
110 untracked candidates
111
111
112 $ hg -v kwfiles --unknown
112 $ hg -v kwfiles --unknown
113 k a
113 k a
114
114
115 Add files and check status
115 Add files and check status
116
116
117 $ hg addremove
117 $ hg addremove
118 adding a
118 adding a
119 adding b
119 adding b
120 $ hg status
120 $ hg status
121 A a
121 A a
122 A b
122 A b
123
123
124
124
125 Default keyword expansion including commit hook
125 Default keyword expansion including commit hook
126 Interrupted commit should not change state or run commit hook
126 Interrupted commit should not change state or run commit hook
127
127
128 $ hg --debug commit
128 $ hg --debug commit
129 abort: empty commit message
129 abort: empty commit message
130 [255]
130 [255]
131 $ hg status
131 $ hg status
132 A a
132 A a
133 A b
133 A b
134
134
135 Commit with several checks
135 Commit with several checks
136
136
137 $ hg --debug commit -mabsym -u 'User Name <user@example.com>'
137 $ hg --debug commit -mabsym -u 'User Name <user@example.com>'
138 a
138 a
139 b
139 b
140 overwriting a expanding keywords
140 overwriting a expanding keywords
141 running hook commit.test: cp a hooktest
141 running hook commit.test: cp a hooktest
142 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
142 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
143 $ hg status
143 $ hg status
144 ? hooktest
144 ? hooktest
145 $ hg debugrebuildstate
145 $ hg debugrebuildstate
146 $ hg --quiet identify
146 $ hg --quiet identify
147 ef63ca68695b
147 ef63ca68695b
148
148
149 cat files in working directory with keywords expanded
149 cat files in working directory with keywords expanded
150
150
151 $ cat a b
151 $ cat a b
152 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
152 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
153 do not process $Id:
153 do not process $Id:
154 xxx $
154 xxx $
155 ignore $Id$
155 ignore $Id$
156
156
157 hg cat files and symlink, no expansion
157 hg cat files and symlink, no expansion
158
158
159 $ hg cat sym a b && echo
159 $ hg cat sym a b && echo
160 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
160 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
161 do not process $Id:
161 do not process $Id:
162 xxx $
162 xxx $
163 ignore $Id$
163 ignore $Id$
164 a
164 a
165
165
166 $ diff a hooktest
166 $ diff a hooktest
167
167
168 $ cp $HGRCPATH.nohooks $HGRCPATH
168 $ cp $HGRCPATH.nohooks $HGRCPATH
169 $ rm hooktest
169 $ rm hooktest
170
170
171 hg status of kw-ignored binary file starting with '\1\n'
171 hg status of kw-ignored binary file starting with '\1\n'
172
172
173 >>> open("i", "wb").write("\1\nfoo")
173 >>> open("i", "wb").write("\1\nfoo")
174 $ hg -q commit -Am metasep i
174 $ hg -q commit -Am metasep i
175 $ hg status
175 $ hg status
176 >>> open("i", "wb").write("\1\nbar")
176 >>> open("i", "wb").write("\1\nbar")
177 $ hg status
177 $ hg status
178 M i
178 M i
179 $ hg -q commit -m "modify metasep" i
179 $ hg -q commit -m "modify metasep" i
180 $ hg status --rev 2:3
180 $ hg status --rev 2:3
181 M i
181 M i
182 $ touch empty
182 $ touch empty
183 $ hg -q commit -A -m "another file"
183 $ hg -q commit -A -m "another file"
184 $ hg status -A --rev 3:4 i
184 $ hg status -A --rev 3:4 i
185 C i
185 C i
186
186
187 $ hg -q strip -n 2
187 $ hg -q strip -n 2
188
188
189 Test hook execution
189 Test hook execution
190
190
191 bundle
191 bundle
192
192
193 $ hg bundle --base null ../kw.hg
193 $ hg bundle --base null ../kw.hg
194 2 changesets found
194 2 changesets found
195 $ cd ..
195 $ cd ..
196 $ hg init Test
196 $ hg init Test
197 $ cd Test
197 $ cd Test
198
198
199 Notify on pull to check whether keywords stay as is in email
199 Notify on pull to check whether keywords stay as is in email
200 ie. if patch.diff wrapper acts as it should
200 ie. if patch.diff wrapper acts as it should
201
201
202 $ cat <<EOF >> $HGRCPATH
202 $ cat <<EOF >> $HGRCPATH
203 > [hooks]
203 > [hooks]
204 > incoming.notify = python:hgext.notify.hook
204 > incoming.notify = python:hgext.notify.hook
205 > [notify]
205 > [notify]
206 > sources = pull
206 > sources = pull
207 > diffstat = False
207 > diffstat = False
208 > maxsubject = 15
208 > maxsubject = 15
209 > [reposubs]
209 > [reposubs]
210 > * = Test
210 > * = Test
211 > EOF
211 > EOF
212
212
213 Pull from bundle and trigger notify
213 Pull from bundle and trigger notify
214
214
215 $ hg pull -u ../kw.hg
215 $ hg pull -u ../kw.hg
216 pulling from ../kw.hg
216 pulling from ../kw.hg
217 requesting all changes
217 requesting all changes
218 adding changesets
218 adding changesets
219 adding manifests
219 adding manifests
220 adding file changes
220 adding file changes
221 added 2 changesets with 3 changes to 3 files
221 added 2 changesets with 3 changes to 3 files
222 Content-Type: text/plain; charset="us-ascii"
222 Content-Type: text/plain; charset="us-ascii"
223 MIME-Version: 1.0
223 MIME-Version: 1.0
224 Content-Transfer-Encoding: 7bit
224 Content-Transfer-Encoding: 7bit
225 Date: * (glob)
225 Date: * (glob)
226 Subject: changeset in...
226 Subject: changeset in...
227 From: mercurial
227 From: mercurial
228 X-Hg-Notification: changeset a2392c293916
228 X-Hg-Notification: changeset a2392c293916
229 Message-Id: <hg.a2392c293916*> (glob)
229 Message-Id: <hg.a2392c293916*> (glob)
230 To: Test
230 To: Test
231
231
232 changeset a2392c293916 in $TESTTMP/Test (glob)
232 changeset a2392c293916 in $TESTTMP/Test (glob)
233 details: $TESTTMP/Test?cmd=changeset;node=a2392c293916
233 details: $TESTTMP/Test?cmd=changeset;node=a2392c293916
234 description:
234 description:
235 addsym
235 addsym
236
236
237 diffs (6 lines):
237 diffs (6 lines):
238
238
239 diff -r 000000000000 -r a2392c293916 sym
239 diff -r 000000000000 -r a2392c293916 sym
240 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
240 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
241 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
241 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
242 @@ -0,0 +1,1 @@
242 @@ -0,0 +1,1 @@
243 +a
243 +a
244 \ No newline at end of file
244 \ No newline at end of file
245 Content-Type: text/plain; charset="us-ascii"
245 Content-Type: text/plain; charset="us-ascii"
246 MIME-Version: 1.0
246 MIME-Version: 1.0
247 Content-Transfer-Encoding: 7bit
247 Content-Transfer-Encoding: 7bit
248 Date:* (glob)
248 Date:* (glob)
249 Subject: changeset in...
249 Subject: changeset in...
250 From: User Name <user@example.com>
250 From: User Name <user@example.com>
251 X-Hg-Notification: changeset ef63ca68695b
251 X-Hg-Notification: changeset ef63ca68695b
252 Message-Id: <hg.ef63ca68695b*> (glob)
252 Message-Id: <hg.ef63ca68695b*> (glob)
253 To: Test
253 To: Test
254
254
255 changeset ef63ca68695b in $TESTTMP/Test (glob)
255 changeset ef63ca68695b in $TESTTMP/Test (glob)
256 details: $TESTTMP/Test?cmd=changeset;node=ef63ca68695b
256 details: $TESTTMP/Test?cmd=changeset;node=ef63ca68695b
257 description:
257 description:
258 absym
258 absym
259
259
260 diffs (12 lines):
260 diffs (12 lines):
261
261
262 diff -r a2392c293916 -r ef63ca68695b a
262 diff -r a2392c293916 -r ef63ca68695b a
263 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
263 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
264 +++ b/a Thu Jan 01 00:00:00 1970 +0000
264 +++ b/a Thu Jan 01 00:00:00 1970 +0000
265 @@ -0,0 +1,3 @@
265 @@ -0,0 +1,3 @@
266 +expand $Id$
266 +expand $Id$
267 +do not process $Id:
267 +do not process $Id:
268 +xxx $
268 +xxx $
269 diff -r a2392c293916 -r ef63ca68695b b
269 diff -r a2392c293916 -r ef63ca68695b b
270 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
270 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
271 +++ b/b Thu Jan 01 00:00:00 1970 +0000
271 +++ b/b Thu Jan 01 00:00:00 1970 +0000
272 @@ -0,0 +1,1 @@
272 @@ -0,0 +1,1 @@
273 +ignore $Id$
273 +ignore $Id$
274 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
274 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
275
275
276 $ cp $HGRCPATH.nohooks $HGRCPATH
276 $ cp $HGRCPATH.nohooks $HGRCPATH
277
277
278 Touch files and check with status
278 Touch files and check with status
279
279
280 $ touch a b
280 $ touch a b
281 $ hg status
281 $ hg status
282
282
283 Update and expand
283 Update and expand
284
284
285 $ rm sym a b
285 $ rm sym a b
286 $ hg update -C
286 $ hg update -C
287 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
287 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
288 $ cat a b
288 $ cat a b
289 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
289 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
290 do not process $Id:
290 do not process $Id:
291 xxx $
291 xxx $
292 ignore $Id$
292 ignore $Id$
293
293
294 Check whether expansion is filewise and file mode is preserved
294 Check whether expansion is filewise and file mode is preserved
295
295
296 $ echo '$Id$' > c
296 $ echo '$Id$' > c
297 $ echo 'tests for different changenodes' >> c
297 $ echo 'tests for different changenodes' >> c
298 #if unix-permissions
298 #if unix-permissions
299 $ chmod 600 c
299 $ chmod 600 c
300 $ ls -l c | cut -b 1-10
300 $ ls -l c | cut -b 1-10
301 -rw-------
301 -rw-------
302 #endif
302 #endif
303
303
304 commit file c
304 commit file c
305
305
306 $ hg commit -A -mcndiff -d '1 0' -u 'User Name <user@example.com>'
306 $ hg commit -A -mcndiff -d '1 0' -u 'User Name <user@example.com>'
307 adding c
307 adding c
308 #if unix-permissions
308 #if unix-permissions
309 $ ls -l c | cut -b 1-10
309 $ ls -l c | cut -b 1-10
310 -rw-------
310 -rw-------
311 #endif
311 #endif
312
312
313 force expansion
313 force expansion
314
314
315 $ hg -v kwexpand
315 $ hg -v kwexpand
316 overwriting a expanding keywords
316 overwriting a expanding keywords
317 overwriting c expanding keywords
317 overwriting c expanding keywords
318
318
319 compare changenodes in a and c
319 compare changenodes in a and c
320
320
321 $ cat a c
321 $ cat a c
322 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
322 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
323 do not process $Id:
323 do not process $Id:
324 xxx $
324 xxx $
325 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
325 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
326 tests for different changenodes
326 tests for different changenodes
327
327
328 record
328 record
329
329
330 $ echo '$Id$' > r
330 $ echo '$Id$' > r
331 $ hg add r
331 $ hg add r
332
332
333 record chunk
333 record chunk
334
334
335 >>> lines = open('a', 'rb').readlines()
335 >>> lines = open('a', 'rb').readlines()
336 >>> lines.insert(1, 'foo\n')
336 >>> lines.insert(1, 'foo\n')
337 >>> lines.append('bar\n')
337 >>> lines.append('bar\n')
338 >>> open('a', 'wb').writelines(lines)
338 >>> open('a', 'wb').writelines(lines)
339 $ hg record -d '10 1' -m rectest a<<EOF
339 $ hg record -d '10 1' -m rectest a<<EOF
340 > y
340 > y
341 > y
341 > y
342 > n
342 > n
343 > EOF
343 > EOF
344 diff --git a/a b/a
344 diff --git a/a b/a
345 2 hunks, 2 lines changed
345 2 hunks, 2 lines changed
346 examine changes to 'a'? [Ynesfdaq?]
346 examine changes to 'a'? [Ynesfdaq?]
347 @@ -1,3 +1,4 @@
347 @@ -1,3 +1,4 @@
348 expand $Id$
348 expand $Id$
349 +foo
349 +foo
350 do not process $Id:
350 do not process $Id:
351 xxx $
351 xxx $
352 record change 1/2 to 'a'? [Ynesfdaq?]
352 record change 1/2 to 'a'? [Ynesfdaq?]
353 @@ -2,2 +3,3 @@
353 @@ -2,2 +3,3 @@
354 do not process $Id:
354 do not process $Id:
355 xxx $
355 xxx $
356 +bar
356 +bar
357 record change 2/2 to 'a'? [Ynesfdaq?]
357 record change 2/2 to 'a'? [Ynesfdaq?]
358
358
359 $ hg identify
359 $ hg identify
360 5f5eb23505c3+ tip
360 5f5eb23505c3+ tip
361 $ hg status
361 $ hg status
362 M a
362 M a
363 A r
363 A r
364
364
365 Cat modified file a
365 Cat modified file a
366
366
367 $ cat a
367 $ cat a
368 expand $Id: a,v 5f5eb23505c3 1970/01/01 00:00:10 test $
368 expand $Id: a,v 5f5eb23505c3 1970/01/01 00:00:10 test $
369 foo
369 foo
370 do not process $Id:
370 do not process $Id:
371 xxx $
371 xxx $
372 bar
372 bar
373
373
374 Diff remaining chunk
374 Diff remaining chunk
375
375
376 $ hg diff a
376 $ hg diff a
377 diff -r 5f5eb23505c3 a
377 diff -r 5f5eb23505c3 a
378 --- a/a Thu Jan 01 00:00:09 1970 -0000
378 --- a/a Thu Jan 01 00:00:09 1970 -0000
379 +++ b/a * (glob)
379 +++ b/a * (glob)
380 @@ -2,3 +2,4 @@
380 @@ -2,3 +2,4 @@
381 foo
381 foo
382 do not process $Id:
382 do not process $Id:
383 xxx $
383 xxx $
384 +bar
384 +bar
385
385
386 $ hg rollback
386 $ hg rollback
387 repository tip rolled back to revision 2 (undo commit)
387 repository tip rolled back to revision 2 (undo commit)
388 working directory now based on revision 2
388 working directory now based on revision 2
389
389
390 Record all chunks in file a
390 Record all chunks in file a
391
391
392 $ echo foo > msg
392 $ echo foo > msg
393
393
394 - do not use "hg record -m" here!
394 - do not use "hg record -m" here!
395
395
396 $ hg record -l msg -d '11 1' a<<EOF
396 $ hg record -l msg -d '11 1' a<<EOF
397 > y
397 > y
398 > y
398 > y
399 > y
399 > y
400 > EOF
400 > EOF
401 diff --git a/a b/a
401 diff --git a/a b/a
402 2 hunks, 2 lines changed
402 2 hunks, 2 lines changed
403 examine changes to 'a'? [Ynesfdaq?]
403 examine changes to 'a'? [Ynesfdaq?]
404 @@ -1,3 +1,4 @@
404 @@ -1,3 +1,4 @@
405 expand $Id$
405 expand $Id$
406 +foo
406 +foo
407 do not process $Id:
407 do not process $Id:
408 xxx $
408 xxx $
409 record change 1/2 to 'a'? [Ynesfdaq?]
409 record change 1/2 to 'a'? [Ynesfdaq?]
410 @@ -2,2 +3,3 @@
410 @@ -2,2 +3,3 @@
411 do not process $Id:
411 do not process $Id:
412 xxx $
412 xxx $
413 +bar
413 +bar
414 record change 2/2 to 'a'? [Ynesfdaq?]
414 record change 2/2 to 'a'? [Ynesfdaq?]
415
415
416 File a should be clean
416 File a should be clean
417
417
418 $ hg status -A a
418 $ hg status -A a
419 C a
419 C a
420
420
421 rollback and revert expansion
421 rollback and revert expansion
422
422
423 $ cat a
423 $ cat a
424 expand $Id: a,v 78e0a02d76aa 1970/01/01 00:00:11 test $
424 expand $Id: a,v 78e0a02d76aa 1970/01/01 00:00:11 test $
425 foo
425 foo
426 do not process $Id:
426 do not process $Id:
427 xxx $
427 xxx $
428 bar
428 bar
429 $ hg --verbose rollback
429 $ hg --verbose rollback
430 repository tip rolled back to revision 2 (undo commit)
430 repository tip rolled back to revision 2 (undo commit)
431 working directory now based on revision 2
431 working directory now based on revision 2
432 overwriting a expanding keywords
432 overwriting a expanding keywords
433 $ hg status a
433 $ hg status a
434 M a
434 M a
435 $ cat a
435 $ cat a
436 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
436 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
437 foo
437 foo
438 do not process $Id:
438 do not process $Id:
439 xxx $
439 xxx $
440 bar
440 bar
441 $ echo '$Id$' > y
441 $ echo '$Id$' > y
442 $ echo '$Id$' > z
442 $ echo '$Id$' > z
443 $ hg add y
443 $ hg add y
444 $ hg commit -Am "rollback only" z
444 $ hg commit -Am "rollback only" z
445 $ cat z
445 $ cat z
446 $Id: z,v 45a5d3adce53 1970/01/01 00:00:00 test $
446 $Id: z,v 45a5d3adce53 1970/01/01 00:00:00 test $
447 $ hg --verbose rollback
447 $ hg --verbose rollback
448 repository tip rolled back to revision 2 (undo commit)
448 repository tip rolled back to revision 2 (undo commit)
449 working directory now based on revision 2
449 working directory now based on revision 2
450 overwriting z shrinking keywords
450 overwriting z shrinking keywords
451
451
452 Only z should be overwritten
452 Only z should be overwritten
453
453
454 $ hg status a y z
454 $ hg status a y z
455 M a
455 M a
456 A y
456 A y
457 A z
457 A z
458 $ cat z
458 $ cat z
459 $Id$
459 $Id$
460 $ hg forget y z
460 $ hg forget y z
461 $ rm y z
461 $ rm y z
462
462
463 record added file alone
463 record added file alone
464
464
465 $ hg -v record -l msg -d '12 2' r<<EOF
465 $ hg -v record -l msg -d '12 2' r<<EOF
466 > y
466 > y
467 > EOF
467 > EOF
468 diff --git a/r b/r
468 diff --git a/r b/r
469 new file mode 100644
469 new file mode 100644
470 examine changes to 'r'? [Ynesfdaq?]
470 examine changes to 'r'? [Ynesfdaq?]
471 r
471 r
472 committed changeset 3:82a2f715724d
472 committed changeset 3:82a2f715724d
473 overwriting r expanding keywords
473 overwriting r expanding keywords
474 - status call required for dirstate.normallookup() check
474 - status call required for dirstate.normallookup() check
475 $ hg status r
475 $ hg status r
476 $ hg --verbose rollback
476 $ hg --verbose rollback
477 repository tip rolled back to revision 2 (undo commit)
477 repository tip rolled back to revision 2 (undo commit)
478 working directory now based on revision 2
478 working directory now based on revision 2
479 overwriting r shrinking keywords
479 overwriting r shrinking keywords
480 $ hg forget r
480 $ hg forget r
481 $ rm msg r
481 $ rm msg r
482 $ hg update -C
482 $ hg update -C
483 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
483 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
484
484
485 record added keyword ignored file
485 record added keyword ignored file
486
486
487 $ echo '$Id$' > i
487 $ echo '$Id$' > i
488 $ hg add i
488 $ hg add i
489 $ hg --verbose record -d '13 1' -m recignored<<EOF
489 $ hg --verbose record -d '13 1' -m recignored<<EOF
490 > y
490 > y
491 > EOF
491 > EOF
492 diff --git a/i b/i
492 diff --git a/i b/i
493 new file mode 100644
493 new file mode 100644
494 examine changes to 'i'? [Ynesfdaq?]
494 examine changes to 'i'? [Ynesfdaq?]
495 i
495 i
496 committed changeset 3:9f40ceb5a072
496 committed changeset 3:9f40ceb5a072
497 $ cat i
497 $ cat i
498 $Id$
498 $Id$
499 $ hg -q rollback
499 $ hg -q rollback
500 $ hg forget i
500 $ hg forget i
501 $ rm i
501 $ rm i
502
502
503 amend
503 amend
504
504
505 $ echo amend >> a
505 $ echo amend >> a
506 $ echo amend >> b
506 $ echo amend >> b
507 $ hg -q commit -d '14 1' -m 'prepare amend'
507 $ hg -q commit -d '14 1' -m 'prepare amend'
508
508
509 $ hg --debug commit --amend -d '15 1' -m 'amend without changes' | grep keywords
509 $ hg --debug commit --amend -d '15 1' -m 'amend without changes' | grep keywords
510 invalid branchheads cache (served): tip differs
510 overwriting a expanding keywords
511 overwriting a expanding keywords
511 $ hg -q id
512 $ hg -q id
512 67d8c481a6be
513 67d8c481a6be
513 $ head -1 a
514 $ head -1 a
514 expand $Id: a,v 67d8c481a6be 1970/01/01 00:00:15 test $
515 expand $Id: a,v 67d8c481a6be 1970/01/01 00:00:15 test $
515
516
516 $ hg -q strip -n tip
517 $ hg -q strip -n tip
517
518
518 Test patch queue repo
519 Test patch queue repo
519
520
520 $ hg init --mq
521 $ hg init --mq
521 $ hg qimport -r tip -n mqtest.diff
522 $ hg qimport -r tip -n mqtest.diff
522 $ hg commit --mq -m mqtest
523 $ hg commit --mq -m mqtest
523
524
524 Keywords should not be expanded in patch
525 Keywords should not be expanded in patch
525
526
526 $ cat .hg/patches/mqtest.diff
527 $ cat .hg/patches/mqtest.diff
527 # HG changeset patch
528 # HG changeset patch
528 # User User Name <user@example.com>
529 # User User Name <user@example.com>
529 # Date 1 0
530 # Date 1 0
530 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
531 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
531 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
532 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
532 cndiff
533 cndiff
533
534
534 diff -r ef63ca68695b -r 40a904bbbe4c c
535 diff -r ef63ca68695b -r 40a904bbbe4c c
535 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
536 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
536 +++ b/c Thu Jan 01 00:00:01 1970 +0000
537 +++ b/c Thu Jan 01 00:00:01 1970 +0000
537 @@ -0,0 +1,2 @@
538 @@ -0,0 +1,2 @@
538 +$Id$
539 +$Id$
539 +tests for different changenodes
540 +tests for different changenodes
540
541
541 $ hg qpop
542 $ hg qpop
542 popping mqtest.diff
543 popping mqtest.diff
543 patch queue now empty
544 patch queue now empty
544
545
545 qgoto, implying qpush, should expand
546 qgoto, implying qpush, should expand
546
547
547 $ hg qgoto mqtest.diff
548 $ hg qgoto mqtest.diff
548 applying mqtest.diff
549 applying mqtest.diff
549 now at: mqtest.diff
550 now at: mqtest.diff
550 $ cat c
551 $ cat c
551 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
552 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
552 tests for different changenodes
553 tests for different changenodes
553 $ hg cat c
554 $ hg cat c
554 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
555 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
555 tests for different changenodes
556 tests for different changenodes
556
557
557 Keywords should not be expanded in filelog
558 Keywords should not be expanded in filelog
558
559
559 $ hg --config 'extensions.keyword=!' cat c
560 $ hg --config 'extensions.keyword=!' cat c
560 $Id$
561 $Id$
561 tests for different changenodes
562 tests for different changenodes
562
563
563 qpop and move on
564 qpop and move on
564
565
565 $ hg qpop
566 $ hg qpop
566 popping mqtest.diff
567 popping mqtest.diff
567 patch queue now empty
568 patch queue now empty
568
569
569 Copy and show added kwfiles
570 Copy and show added kwfiles
570
571
571 $ hg cp a c
572 $ hg cp a c
572 $ hg kwfiles
573 $ hg kwfiles
573 a
574 a
574 c
575 c
575
576
576 Commit and show expansion in original and copy
577 Commit and show expansion in original and copy
577
578
578 $ hg --debug commit -ma2c -d '1 0' -u 'User Name <user@example.com>'
579 $ hg --debug commit -ma2c -d '1 0' -u 'User Name <user@example.com>'
579 invalid branchheads cache (served): tip differs
580 invalid branchheads cache (served): tip differs
580 c
581 c
581 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
582 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
582 invalid branchheads cache (served): tip differs
583 invalid branchheads cache (served): tip differs
583 overwriting c expanding keywords
584 overwriting c expanding keywords
584 committed changeset 2:25736cf2f5cbe41f6be4e6784ef6ecf9f3bbcc7d
585 committed changeset 2:25736cf2f5cbe41f6be4e6784ef6ecf9f3bbcc7d
585 $ cat a c
586 $ cat a c
586 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
587 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
587 do not process $Id:
588 do not process $Id:
588 xxx $
589 xxx $
589 expand $Id: c,v 25736cf2f5cb 1970/01/01 00:00:01 user $
590 expand $Id: c,v 25736cf2f5cb 1970/01/01 00:00:01 user $
590 do not process $Id:
591 do not process $Id:
591 xxx $
592 xxx $
592
593
593 Touch copied c and check its status
594 Touch copied c and check its status
594
595
595 $ touch c
596 $ touch c
596 $ hg status
597 $ hg status
597
598
598 Copy kwfile to keyword ignored file unexpanding keywords
599 Copy kwfile to keyword ignored file unexpanding keywords
599
600
600 $ hg --verbose copy a i
601 $ hg --verbose copy a i
601 copying a to i
602 copying a to i
602 overwriting i shrinking keywords
603 overwriting i shrinking keywords
603 $ head -n 1 i
604 $ head -n 1 i
604 expand $Id$
605 expand $Id$
605 $ hg forget i
606 $ hg forget i
606 $ rm i
607 $ rm i
607
608
608 Copy ignored file to ignored file: no overwriting
609 Copy ignored file to ignored file: no overwriting
609
610
610 $ hg --verbose copy b i
611 $ hg --verbose copy b i
611 copying b to i
612 copying b to i
612 $ hg forget i
613 $ hg forget i
613 $ rm i
614 $ rm i
614
615
615 cp symlink file; hg cp -A symlink file (part1)
616 cp symlink file; hg cp -A symlink file (part1)
616 - copied symlink points to kwfile: overwrite
617 - copied symlink points to kwfile: overwrite
617
618
618 #if symlink
619 #if symlink
619 $ cp sym i
620 $ cp sym i
620 $ ls -l i
621 $ ls -l i
621 -rw-r--r--* (glob)
622 -rw-r--r--* (glob)
622 $ head -1 i
623 $ head -1 i
623 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
624 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
624 $ hg copy --after --verbose sym i
625 $ hg copy --after --verbose sym i
625 copying sym to i
626 copying sym to i
626 overwriting i shrinking keywords
627 overwriting i shrinking keywords
627 $ head -1 i
628 $ head -1 i
628 expand $Id$
629 expand $Id$
629 $ hg forget i
630 $ hg forget i
630 $ rm i
631 $ rm i
631 #endif
632 #endif
632
633
633 Test different options of hg kwfiles
634 Test different options of hg kwfiles
634
635
635 $ hg kwfiles
636 $ hg kwfiles
636 a
637 a
637 c
638 c
638 $ hg -v kwfiles --ignore
639 $ hg -v kwfiles --ignore
639 I b
640 I b
640 I sym
641 I sym
641 $ hg kwfiles --all
642 $ hg kwfiles --all
642 K a
643 K a
643 K c
644 K c
644 I b
645 I b
645 I sym
646 I sym
646
647
647 Diff specific revision
648 Diff specific revision
648
649
649 $ hg diff --rev 1
650 $ hg diff --rev 1
650 diff -r ef63ca68695b c
651 diff -r ef63ca68695b c
651 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
652 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
652 +++ b/c * (glob)
653 +++ b/c * (glob)
653 @@ -0,0 +1,3 @@
654 @@ -0,0 +1,3 @@
654 +expand $Id$
655 +expand $Id$
655 +do not process $Id:
656 +do not process $Id:
656 +xxx $
657 +xxx $
657
658
658 Status after rollback:
659 Status after rollback:
659
660
660 $ hg rollback
661 $ hg rollback
661 repository tip rolled back to revision 1 (undo commit)
662 repository tip rolled back to revision 1 (undo commit)
662 working directory now based on revision 1
663 working directory now based on revision 1
663 $ hg status
664 $ hg status
664 A c
665 A c
665 $ hg update --clean
666 $ hg update --clean
666 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
667 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
667
668
668 #if symlink
669 #if symlink
669
670
670 cp symlink file; hg cp -A symlink file (part2)
671 cp symlink file; hg cp -A symlink file (part2)
671 - copied symlink points to kw ignored file: do not overwrite
672 - copied symlink points to kw ignored file: do not overwrite
672
673
673 $ cat a > i
674 $ cat a > i
674 $ ln -s i symignored
675 $ ln -s i symignored
675 $ hg commit -Am 'fake expansion in ignored and symlink' i symignored
676 $ hg commit -Am 'fake expansion in ignored and symlink' i symignored
676 $ cp symignored x
677 $ cp symignored x
677 $ hg copy --after --verbose symignored x
678 $ hg copy --after --verbose symignored x
678 copying symignored to x
679 copying symignored to x
679 $ head -n 1 x
680 $ head -n 1 x
680 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
681 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
681 $ hg forget x
682 $ hg forget x
682 $ rm x
683 $ rm x
683
684
684 $ hg rollback
685 $ hg rollback
685 repository tip rolled back to revision 1 (undo commit)
686 repository tip rolled back to revision 1 (undo commit)
686 working directory now based on revision 1
687 working directory now based on revision 1
687 $ hg update --clean
688 $ hg update --clean
688 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
689 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
689 $ rm i symignored
690 $ rm i symignored
690
691
691 #endif
692 #endif
692
693
693 Custom keywordmaps as argument to kwdemo
694 Custom keywordmaps as argument to kwdemo
694
695
695 $ hg --quiet kwdemo "Xinfo = {author}: {desc}"
696 $ hg --quiet kwdemo "Xinfo = {author}: {desc}"
696 [extensions]
697 [extensions]
697 keyword =
698 keyword =
698 [keyword]
699 [keyword]
699 ** =
700 ** =
700 b = ignore
701 b = ignore
701 demo.txt =
702 demo.txt =
702 i = ignore
703 i = ignore
703 [keywordset]
704 [keywordset]
704 svn = False
705 svn = False
705 [keywordmaps]
706 [keywordmaps]
706 Xinfo = {author}: {desc}
707 Xinfo = {author}: {desc}
707 $Xinfo: test: hg keyword configuration and expansion example $
708 $Xinfo: test: hg keyword configuration and expansion example $
708
709
709 Configure custom keywordmaps
710 Configure custom keywordmaps
710
711
711 $ cat <<EOF >>$HGRCPATH
712 $ cat <<EOF >>$HGRCPATH
712 > [keywordmaps]
713 > [keywordmaps]
713 > Id = {file} {node|short} {date|rfc822date} {author|user}
714 > Id = {file} {node|short} {date|rfc822date} {author|user}
714 > Xinfo = {author}: {desc}
715 > Xinfo = {author}: {desc}
715 > EOF
716 > EOF
716
717
717 Cat and hg cat files before custom expansion
718 Cat and hg cat files before custom expansion
718
719
719 $ cat a b
720 $ cat a b
720 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
721 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
721 do not process $Id:
722 do not process $Id:
722 xxx $
723 xxx $
723 ignore $Id$
724 ignore $Id$
724 $ hg cat sym a b && echo
725 $ hg cat sym a b && echo
725 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
726 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
726 do not process $Id:
727 do not process $Id:
727 xxx $
728 xxx $
728 ignore $Id$
729 ignore $Id$
729 a
730 a
730
731
731 Write custom keyword and prepare multi-line commit message
732 Write custom keyword and prepare multi-line commit message
732
733
733 $ echo '$Xinfo$' >> a
734 $ echo '$Xinfo$' >> a
734 $ cat <<EOF >> log
735 $ cat <<EOF >> log
735 > firstline
736 > firstline
736 > secondline
737 > secondline
737 > EOF
738 > EOF
738
739
739 Interrupted commit should not change state
740 Interrupted commit should not change state
740
741
741 $ hg commit
742 $ hg commit
742 abort: empty commit message
743 abort: empty commit message
743 [255]
744 [255]
744 $ hg status
745 $ hg status
745 M a
746 M a
746 ? c
747 ? c
747 ? log
748 ? log
748
749
749 Commit with multi-line message and custom expansion
750 Commit with multi-line message and custom expansion
750
751
751 |Note:
752 |Note:
752 |
753 |
753 | After the last rollback, the "unserved" branchheads cache became invalid, but
754 | After the last rollback, the "unserved" branchheads cache became invalid, but
754 | all changesets in the repo were public. For filtering this means:
755 | all changesets in the repo were public. For filtering this means:
755 | "mutable" == "unserved" == ΓΈ.
756 | "mutable" == "unserved" == ΓΈ.
756 |
757 |
757 | As the "unserved" cache is invalid, we fall back to the "mutable" cache. But
758 | As the "unserved" cache is invalid, we fall back to the "mutable" cache. But
758 | no update is needed between "mutable" and "unserved" and the "unserved" cache
759 | no update is needed between "mutable" and "unserved" and the "unserved" cache
759 | is not updated on disk. The on-disk version therefore stays invalid for some
760 | is not updated on disk. The on-disk version therefore stays invalid for some
760 | time. This explains why the "unserved" branchheads cache is detected as
761 | time. This explains why the "unserved" branchheads cache is detected as
761 | invalid here.
762 | invalid here.
762
763
763 $ hg --debug commit -l log -d '2 0' -u 'User Name <user@example.com>'
764 $ hg --debug commit -l log -d '2 0' -u 'User Name <user@example.com>'
764 invalid branchheads cache (served): tip differs
765 invalid branchheads cache (served): tip differs
765 a
766 a
766 invalid branchheads cache (served): tip differs
767 invalid branchheads cache (served): tip differs
767 overwriting a expanding keywords
768 overwriting a expanding keywords
768 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
769 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
769 $ rm log
770 $ rm log
770
771
771 Stat, verify and show custom expansion (firstline)
772 Stat, verify and show custom expansion (firstline)
772
773
773 $ hg status
774 $ hg status
774 ? c
775 ? c
775 $ hg verify
776 $ hg verify
776 checking changesets
777 checking changesets
777 checking manifests
778 checking manifests
778 crosschecking files in changesets and manifests
779 crosschecking files in changesets and manifests
779 checking files
780 checking files
780 3 files, 3 changesets, 4 total revisions
781 3 files, 3 changesets, 4 total revisions
781 $ cat a b
782 $ cat a b
782 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
783 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
783 do not process $Id:
784 do not process $Id:
784 xxx $
785 xxx $
785 $Xinfo: User Name <user@example.com>: firstline $
786 $Xinfo: User Name <user@example.com>: firstline $
786 ignore $Id$
787 ignore $Id$
787 $ hg cat sym a b && echo
788 $ hg cat sym a b && echo
788 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
789 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
789 do not process $Id:
790 do not process $Id:
790 xxx $
791 xxx $
791 $Xinfo: User Name <user@example.com>: firstline $
792 $Xinfo: User Name <user@example.com>: firstline $
792 ignore $Id$
793 ignore $Id$
793 a
794 a
794
795
795 annotate
796 annotate
796
797
797 $ hg annotate a
798 $ hg annotate a
798 1: expand $Id$
799 1: expand $Id$
799 1: do not process $Id:
800 1: do not process $Id:
800 1: xxx $
801 1: xxx $
801 2: $Xinfo$
802 2: $Xinfo$
802
803
803 remove with status checks
804 remove with status checks
804
805
805 $ hg debugrebuildstate
806 $ hg debugrebuildstate
806 $ hg remove a
807 $ hg remove a
807 $ hg --debug commit -m rma
808 $ hg --debug commit -m rma
808 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
809 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
809 $ hg status
810 $ hg status
810 ? c
811 ? c
811
812
812 Rollback, revert, and check expansion
813 Rollback, revert, and check expansion
813
814
814 $ hg rollback
815 $ hg rollback
815 repository tip rolled back to revision 2 (undo commit)
816 repository tip rolled back to revision 2 (undo commit)
816 working directory now based on revision 2
817 working directory now based on revision 2
817 $ hg status
818 $ hg status
818 R a
819 R a
819 ? c
820 ? c
820 $ hg revert --no-backup --rev tip a
821 $ hg revert --no-backup --rev tip a
821 $ cat a
822 $ cat a
822 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
823 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
823 do not process $Id:
824 do not process $Id:
824 xxx $
825 xxx $
825 $Xinfo: User Name <user@example.com>: firstline $
826 $Xinfo: User Name <user@example.com>: firstline $
826
827
827 Clone to test global and local configurations
828 Clone to test global and local configurations
828
829
829 $ cd ..
830 $ cd ..
830
831
831 Expansion in destination with global configuration
832 Expansion in destination with global configuration
832
833
833 $ hg --quiet clone Test globalconf
834 $ hg --quiet clone Test globalconf
834 $ cat globalconf/a
835 $ cat globalconf/a
835 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
836 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
836 do not process $Id:
837 do not process $Id:
837 xxx $
838 xxx $
838 $Xinfo: User Name <user@example.com>: firstline $
839 $Xinfo: User Name <user@example.com>: firstline $
839
840
840 No expansion in destination with local configuration in origin only
841 No expansion in destination with local configuration in origin only
841
842
842 $ hg --quiet --config 'keyword.**=ignore' clone Test localconf
843 $ hg --quiet --config 'keyword.**=ignore' clone Test localconf
843 $ cat localconf/a
844 $ cat localconf/a
844 expand $Id$
845 expand $Id$
845 do not process $Id:
846 do not process $Id:
846 xxx $
847 xxx $
847 $Xinfo$
848 $Xinfo$
848
849
849 Clone to test incoming
850 Clone to test incoming
850
851
851 $ hg clone -r1 Test Test-a
852 $ hg clone -r1 Test Test-a
852 adding changesets
853 adding changesets
853 adding manifests
854 adding manifests
854 adding file changes
855 adding file changes
855 added 2 changesets with 3 changes to 3 files
856 added 2 changesets with 3 changes to 3 files
856 updating to branch default
857 updating to branch default
857 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
858 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
858 $ cd Test-a
859 $ cd Test-a
859 $ cat <<EOF >> .hg/hgrc
860 $ cat <<EOF >> .hg/hgrc
860 > [paths]
861 > [paths]
861 > default = ../Test
862 > default = ../Test
862 > EOF
863 > EOF
863 $ hg incoming
864 $ hg incoming
864 comparing with $TESTTMP/Test (glob)
865 comparing with $TESTTMP/Test (glob)
865 searching for changes
866 searching for changes
866 changeset: 2:bb948857c743
867 changeset: 2:bb948857c743
867 tag: tip
868 tag: tip
868 user: User Name <user@example.com>
869 user: User Name <user@example.com>
869 date: Thu Jan 01 00:00:02 1970 +0000
870 date: Thu Jan 01 00:00:02 1970 +0000
870 summary: firstline
871 summary: firstline
871
872
872 Imported patch should not be rejected
873 Imported patch should not be rejected
873
874
874 >>> import re
875 >>> import re
875 >>> text = re.sub(r'(Id.*)', r'\1 rejecttest', open('a').read())
876 >>> text = re.sub(r'(Id.*)', r'\1 rejecttest', open('a').read())
876 >>> open('a', 'wb').write(text)
877 >>> open('a', 'wb').write(text)
877 $ hg --debug commit -m'rejects?' -d '3 0' -u 'User Name <user@example.com>'
878 $ hg --debug commit -m'rejects?' -d '3 0' -u 'User Name <user@example.com>'
878 a
879 a
879 overwriting a expanding keywords
880 overwriting a expanding keywords
880 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
881 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
881 $ hg export -o ../rejecttest.diff tip
882 $ hg export -o ../rejecttest.diff tip
882 $ cd ../Test
883 $ cd ../Test
883 $ hg import ../rejecttest.diff
884 $ hg import ../rejecttest.diff
884 applying ../rejecttest.diff
885 applying ../rejecttest.diff
885 $ cat a b
886 $ cat a b
886 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
887 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
887 do not process $Id: rejecttest
888 do not process $Id: rejecttest
888 xxx $
889 xxx $
889 $Xinfo: User Name <user@example.com>: rejects? $
890 $Xinfo: User Name <user@example.com>: rejects? $
890 ignore $Id$
891 ignore $Id$
891
892
892 $ hg rollback
893 $ hg rollback
893 repository tip rolled back to revision 2 (undo import)
894 repository tip rolled back to revision 2 (undo import)
894 working directory now based on revision 2
895 working directory now based on revision 2
895 $ hg update --clean
896 $ hg update --clean
896 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
897 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
897
898
898 kwexpand/kwshrink on selected files
899 kwexpand/kwshrink on selected files
899
900
900 $ mkdir x
901 $ mkdir x
901 $ hg copy a x/a
902 $ hg copy a x/a
902 $ hg --verbose kwshrink a
903 $ hg --verbose kwshrink a
903 overwriting a shrinking keywords
904 overwriting a shrinking keywords
904 - sleep required for dirstate.normal() check
905 - sleep required for dirstate.normal() check
905 $ sleep 1
906 $ sleep 1
906 $ hg status a
907 $ hg status a
907 $ hg --verbose kwexpand a
908 $ hg --verbose kwexpand a
908 overwriting a expanding keywords
909 overwriting a expanding keywords
909 $ hg status a
910 $ hg status a
910
911
911 kwexpand x/a should abort
912 kwexpand x/a should abort
912
913
913 $ hg --verbose kwexpand x/a
914 $ hg --verbose kwexpand x/a
914 abort: outstanding uncommitted changes
915 abort: outstanding uncommitted changes
915 [255]
916 [255]
916 $ cd x
917 $ cd x
917 $ hg --debug commit -m xa -d '3 0' -u 'User Name <user@example.com>'
918 $ hg --debug commit -m xa -d '3 0' -u 'User Name <user@example.com>'
918 x/a
919 x/a
919 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
920 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
920 overwriting x/a expanding keywords
921 overwriting x/a expanding keywords
921 committed changeset 3:b4560182a3f9a358179fd2d835c15e9da379c1e4
922 committed changeset 3:b4560182a3f9a358179fd2d835c15e9da379c1e4
922 $ cat a
923 $ cat a
923 expand $Id: x/a b4560182a3f9 Thu, 01 Jan 1970 00:00:03 +0000 user $
924 expand $Id: x/a b4560182a3f9 Thu, 01 Jan 1970 00:00:03 +0000 user $
924 do not process $Id:
925 do not process $Id:
925 xxx $
926 xxx $
926 $Xinfo: User Name <user@example.com>: xa $
927 $Xinfo: User Name <user@example.com>: xa $
927
928
928 kwshrink a inside directory x
929 kwshrink a inside directory x
929
930
930 $ hg --verbose kwshrink a
931 $ hg --verbose kwshrink a
931 overwriting x/a shrinking keywords
932 overwriting x/a shrinking keywords
932 $ cat a
933 $ cat a
933 expand $Id$
934 expand $Id$
934 do not process $Id:
935 do not process $Id:
935 xxx $
936 xxx $
936 $Xinfo$
937 $Xinfo$
937 $ cd ..
938 $ cd ..
938
939
939 kwexpand nonexistent
940 kwexpand nonexistent
940
941
941 $ hg kwexpand nonexistent
942 $ hg kwexpand nonexistent
942 nonexistent:* (glob)
943 nonexistent:* (glob)
943
944
944
945
945 #if serve
946 #if serve
946 hg serve
947 hg serve
947 - expand with hgweb file
948 - expand with hgweb file
948 - no expansion with hgweb annotate/changeset/filediff
949 - no expansion with hgweb annotate/changeset/filediff
949 - check errors
950 - check errors
950
951
951 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
952 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
952 $ cat hg.pid >> $DAEMON_PIDS
953 $ cat hg.pid >> $DAEMON_PIDS
953 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/a/?style=raw'
954 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/a/?style=raw'
954 200 Script output follows
955 200 Script output follows
955
956
956 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
957 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
957 do not process $Id:
958 do not process $Id:
958 xxx $
959 xxx $
959 $Xinfo: User Name <user@example.com>: firstline $
960 $Xinfo: User Name <user@example.com>: firstline $
960 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'annotate/tip/a/?style=raw'
961 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'annotate/tip/a/?style=raw'
961 200 Script output follows
962 200 Script output follows
962
963
963
964
964 user@1: expand $Id$
965 user@1: expand $Id$
965 user@1: do not process $Id:
966 user@1: do not process $Id:
966 user@1: xxx $
967 user@1: xxx $
967 user@2: $Xinfo$
968 user@2: $Xinfo$
968
969
969
970
970
971
971
972
972 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'rev/tip/?style=raw'
973 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'rev/tip/?style=raw'
973 200 Script output follows
974 200 Script output follows
974
975
975
976
976 # HG changeset patch
977 # HG changeset patch
977 # User User Name <user@example.com>
978 # User User Name <user@example.com>
978 # Date 3 0
979 # Date 3 0
979 # Node ID b4560182a3f9a358179fd2d835c15e9da379c1e4
980 # Node ID b4560182a3f9a358179fd2d835c15e9da379c1e4
980 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
981 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
981 xa
982 xa
982
983
983 diff -r bb948857c743 -r b4560182a3f9 x/a
984 diff -r bb948857c743 -r b4560182a3f9 x/a
984 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
985 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
985 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
986 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
986 @@ -0,0 +1,4 @@
987 @@ -0,0 +1,4 @@
987 +expand $Id$
988 +expand $Id$
988 +do not process $Id:
989 +do not process $Id:
989 +xxx $
990 +xxx $
990 +$Xinfo$
991 +$Xinfo$
991
992
992 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'diff/bb948857c743/a?style=raw'
993 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'diff/bb948857c743/a?style=raw'
993 200 Script output follows
994 200 Script output follows
994
995
995
996
996 diff -r ef63ca68695b -r bb948857c743 a
997 diff -r ef63ca68695b -r bb948857c743 a
997 --- a/a Thu Jan 01 00:00:00 1970 +0000
998 --- a/a Thu Jan 01 00:00:00 1970 +0000
998 +++ b/a Thu Jan 01 00:00:02 1970 +0000
999 +++ b/a Thu Jan 01 00:00:02 1970 +0000
999 @@ -1,3 +1,4 @@
1000 @@ -1,3 +1,4 @@
1000 expand $Id$
1001 expand $Id$
1001 do not process $Id:
1002 do not process $Id:
1002 xxx $
1003 xxx $
1003 +$Xinfo$
1004 +$Xinfo$
1004
1005
1005
1006
1006
1007
1007
1008
1008 $ cat errors.log
1009 $ cat errors.log
1009 #endif
1010 #endif
1010
1011
1011 Prepare merge and resolve tests
1012 Prepare merge and resolve tests
1012
1013
1013 $ echo '$Id$' > m
1014 $ echo '$Id$' > m
1014 $ hg add m
1015 $ hg add m
1015 $ hg commit -m 4kw
1016 $ hg commit -m 4kw
1016 $ echo foo >> m
1017 $ echo foo >> m
1017 $ hg commit -m 5foo
1018 $ hg commit -m 5foo
1018
1019
1019 simplemerge
1020 simplemerge
1020
1021
1021 $ hg update 4
1022 $ hg update 4
1022 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1023 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1023 $ echo foo >> m
1024 $ echo foo >> m
1024 $ hg commit -m 6foo
1025 $ hg commit -m 6foo
1025 created new head
1026 created new head
1026 $ hg merge
1027 $ hg merge
1027 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1028 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1028 (branch merge, don't forget to commit)
1029 (branch merge, don't forget to commit)
1029 $ hg commit -m simplemerge
1030 $ hg commit -m simplemerge
1030 $ cat m
1031 $ cat m
1031 $Id: m 27d48ee14f67 Thu, 01 Jan 1970 00:00:00 +0000 test $
1032 $Id: m 27d48ee14f67 Thu, 01 Jan 1970 00:00:00 +0000 test $
1032 foo
1033 foo
1033
1034
1034 conflict: keyword should stay outside conflict zone
1035 conflict: keyword should stay outside conflict zone
1035
1036
1036 $ hg update 4
1037 $ hg update 4
1037 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1038 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1038 $ echo bar >> m
1039 $ echo bar >> m
1039 $ hg commit -m 8bar
1040 $ hg commit -m 8bar
1040 created new head
1041 created new head
1041 $ hg merge
1042 $ hg merge
1042 merging m
1043 merging m
1043 warning: conflicts during merge.
1044 warning: conflicts during merge.
1044 merging m incomplete! (edit conflicts, then use 'hg resolve --mark')
1045 merging m incomplete! (edit conflicts, then use 'hg resolve --mark')
1045 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1046 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1046 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
1047 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
1047 [1]
1048 [1]
1048 $ cat m
1049 $ cat m
1049 $Id$
1050 $Id$
1050 <<<<<<< local
1051 <<<<<<< local
1051 bar
1052 bar
1052 =======
1053 =======
1053 foo
1054 foo
1054 >>>>>>> other
1055 >>>>>>> other
1055
1056
1056 resolve to local
1057 resolve to local
1057
1058
1058 $ HGMERGE=internal:local hg resolve -a
1059 $ HGMERGE=internal:local hg resolve -a
1059 $ hg commit -m localresolve
1060 $ hg commit -m localresolve
1060 $ cat m
1061 $ cat m
1061 $Id: m 800511b3a22d Thu, 01 Jan 1970 00:00:00 +0000 test $
1062 $Id: m 800511b3a22d Thu, 01 Jan 1970 00:00:00 +0000 test $
1062 bar
1063 bar
1063
1064
1064 Test restricted mode with transplant -b
1065 Test restricted mode with transplant -b
1065
1066
1066 $ hg update 6
1067 $ hg update 6
1067 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1068 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1068 $ hg branch foo
1069 $ hg branch foo
1069 marked working directory as branch foo
1070 marked working directory as branch foo
1070 (branches are permanent and global, did you want a bookmark?)
1071 (branches are permanent and global, did you want a bookmark?)
1071 $ mv a a.bak
1072 $ mv a a.bak
1072 $ echo foobranch > a
1073 $ echo foobranch > a
1073 $ cat a.bak >> a
1074 $ cat a.bak >> a
1074 $ rm a.bak
1075 $ rm a.bak
1075 $ hg commit -m 9foobranch
1076 $ hg commit -m 9foobranch
1076 $ hg update default
1077 $ hg update default
1077 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1078 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1078 $ hg -y transplant -b foo tip
1079 $ hg -y transplant -b foo tip
1079 applying 4aa30d025d50
1080 applying 4aa30d025d50
1080 4aa30d025d50 transplanted to e00abbf63521
1081 4aa30d025d50 transplanted to e00abbf63521
1081
1082
1082 Expansion in changeset but not in file
1083 Expansion in changeset but not in file
1083
1084
1084 $ hg tip -p
1085 $ hg tip -p
1085 changeset: 11:e00abbf63521
1086 changeset: 11:e00abbf63521
1086 tag: tip
1087 tag: tip
1087 parent: 9:800511b3a22d
1088 parent: 9:800511b3a22d
1088 user: test
1089 user: test
1089 date: Thu Jan 01 00:00:00 1970 +0000
1090 date: Thu Jan 01 00:00:00 1970 +0000
1090 summary: 9foobranch
1091 summary: 9foobranch
1091
1092
1092 diff -r 800511b3a22d -r e00abbf63521 a
1093 diff -r 800511b3a22d -r e00abbf63521 a
1093 --- a/a Thu Jan 01 00:00:00 1970 +0000
1094 --- a/a Thu Jan 01 00:00:00 1970 +0000
1094 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1095 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1095 @@ -1,3 +1,4 @@
1096 @@ -1,3 +1,4 @@
1096 +foobranch
1097 +foobranch
1097 expand $Id$
1098 expand $Id$
1098 do not process $Id:
1099 do not process $Id:
1099 xxx $
1100 xxx $
1100
1101
1101 $ head -n 2 a
1102 $ head -n 2 a
1102 foobranch
1103 foobranch
1103 expand $Id: a e00abbf63521 Thu, 01 Jan 1970 00:00:00 +0000 test $
1104 expand $Id: a e00abbf63521 Thu, 01 Jan 1970 00:00:00 +0000 test $
1104
1105
1105 Turn off expansion
1106 Turn off expansion
1106
1107
1107 $ hg -q rollback
1108 $ hg -q rollback
1108 $ hg -q update -C
1109 $ hg -q update -C
1109
1110
1110 kwshrink with unknown file u
1111 kwshrink with unknown file u
1111
1112
1112 $ cp a u
1113 $ cp a u
1113 $ hg --verbose kwshrink
1114 $ hg --verbose kwshrink
1114 overwriting a shrinking keywords
1115 overwriting a shrinking keywords
1115 overwriting m shrinking keywords
1116 overwriting m shrinking keywords
1116 overwriting x/a shrinking keywords
1117 overwriting x/a shrinking keywords
1117
1118
1118 Keywords shrunk in working directory, but not yet disabled
1119 Keywords shrunk in working directory, but not yet disabled
1119 - cat shows unexpanded keywords
1120 - cat shows unexpanded keywords
1120 - hg cat shows expanded keywords
1121 - hg cat shows expanded keywords
1121
1122
1122 $ cat a b
1123 $ cat a b
1123 expand $Id$
1124 expand $Id$
1124 do not process $Id:
1125 do not process $Id:
1125 xxx $
1126 xxx $
1126 $Xinfo$
1127 $Xinfo$
1127 ignore $Id$
1128 ignore $Id$
1128 $ hg cat sym a b && echo
1129 $ hg cat sym a b && echo
1129 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
1130 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
1130 do not process $Id:
1131 do not process $Id:
1131 xxx $
1132 xxx $
1132 $Xinfo: User Name <user@example.com>: firstline $
1133 $Xinfo: User Name <user@example.com>: firstline $
1133 ignore $Id$
1134 ignore $Id$
1134 a
1135 a
1135
1136
1136 Now disable keyword expansion
1137 Now disable keyword expansion
1137
1138
1138 $ rm "$HGRCPATH"
1139 $ rm "$HGRCPATH"
1139 $ cat a b
1140 $ cat a b
1140 expand $Id$
1141 expand $Id$
1141 do not process $Id:
1142 do not process $Id:
1142 xxx $
1143 xxx $
1143 $Xinfo$
1144 $Xinfo$
1144 ignore $Id$
1145 ignore $Id$
1145 $ hg cat sym a b && echo
1146 $ hg cat sym a b && echo
1146 expand $Id$
1147 expand $Id$
1147 do not process $Id:
1148 do not process $Id:
1148 xxx $
1149 xxx $
1149 $Xinfo$
1150 $Xinfo$
1150 ignore $Id$
1151 ignore $Id$
1151 a
1152 a
1152
1153
1153 $ cd ..
1154 $ cd ..
@@ -1,109 +1,108 b''
1 $ "$TESTDIR/hghave" symlink || exit 80
1 $ "$TESTDIR/hghave" symlink || exit 80
2
2
3 $ echo "[extensions]" >> $HGRCPATH
3 $ echo "[extensions]" >> $HGRCPATH
4 $ echo "mq=" >> $HGRCPATH
4 $ echo "mq=" >> $HGRCPATH
5
5
6 $ hg init
6 $ hg init
7 $ hg qinit
7 $ hg qinit
8 $ hg qnew base.patch
8 $ hg qnew base.patch
9 $ echo aaa > a
9 $ echo aaa > a
10 $ echo bbb > b
10 $ echo bbb > b
11 $ echo ccc > c
11 $ echo ccc > c
12 $ hg add a b c
12 $ hg add a b c
13 $ hg qrefresh
13 $ hg qrefresh
14 $ "$TESTDIR/readlink.py" a
14 $ "$TESTDIR/readlink.py" a
15 a -> a not a symlink
15 a -> a not a symlink
16
16
17
17
18 test replacing a file with a symlink
18 test replacing a file with a symlink
19
19
20 $ hg qnew symlink.patch
20 $ hg qnew symlink.patch
21 $ rm a
21 $ rm a
22 $ ln -s b a
22 $ ln -s b a
23 $ hg qrefresh --git
23 $ hg qrefresh --git
24 $ "$TESTDIR/readlink.py" a
24 $ "$TESTDIR/readlink.py" a
25 a -> b
25 a -> b
26
26
27 $ hg qpop
27 $ hg qpop
28 popping symlink.patch
28 popping symlink.patch
29 now at: base.patch
29 now at: base.patch
30 $ hg qpush
30 $ hg qpush
31 applying symlink.patch
31 applying symlink.patch
32 now at: symlink.patch
32 now at: symlink.patch
33 $ "$TESTDIR/readlink.py" a
33 $ "$TESTDIR/readlink.py" a
34 a -> b
34 a -> b
35
35
36
36
37 test updating a symlink
37 test updating a symlink
38
38
39 $ rm a
39 $ rm a
40 $ ln -s c a
40 $ ln -s c a
41 $ hg qnew --git -f updatelink
41 $ hg qnew --git -f updatelink
42 $ "$TESTDIR/readlink.py" a
42 $ "$TESTDIR/readlink.py" a
43 a -> c
43 a -> c
44 $ hg qpop
44 $ hg qpop
45 popping updatelink
45 popping updatelink
46 now at: symlink.patch
46 now at: symlink.patch
47 $ hg qpush --debug
47 $ hg qpush --debug
48 invalid branchheads cache (served): tip differs
49 applying updatelink
48 applying updatelink
50 patching file a
49 patching file a
51 a
50 a
52 now at: updatelink
51 now at: updatelink
53 $ "$TESTDIR/readlink.py" a
52 $ "$TESTDIR/readlink.py" a
54 a -> c
53 a -> c
55 $ hg st
54 $ hg st
56
55
57
56
58 test replacing a symlink with a file
57 test replacing a symlink with a file
59
58
60 $ ln -s c s
59 $ ln -s c s
61 $ hg add s
60 $ hg add s
62 $ hg qnew --git -f addlink
61 $ hg qnew --git -f addlink
63 $ rm s
62 $ rm s
64 $ echo sss > s
63 $ echo sss > s
65 $ hg qnew --git -f replacelinkwithfile
64 $ hg qnew --git -f replacelinkwithfile
66 $ hg qpop
65 $ hg qpop
67 popping replacelinkwithfile
66 popping replacelinkwithfile
68 now at: addlink
67 now at: addlink
69 $ hg qpush
68 $ hg qpush
70 applying replacelinkwithfile
69 applying replacelinkwithfile
71 now at: replacelinkwithfile
70 now at: replacelinkwithfile
72 $ cat s
71 $ cat s
73 sss
72 sss
74 $ hg st
73 $ hg st
75
74
76
75
77 test symlink removal
76 test symlink removal
78
77
79 $ hg qnew removesl.patch
78 $ hg qnew removesl.patch
80 $ hg rm a
79 $ hg rm a
81 $ hg qrefresh --git
80 $ hg qrefresh --git
82 $ hg qpop
81 $ hg qpop
83 popping removesl.patch
82 popping removesl.patch
84 now at: replacelinkwithfile
83 now at: replacelinkwithfile
85 $ hg qpush
84 $ hg qpush
86 applying removesl.patch
85 applying removesl.patch
87 now at: removesl.patch
86 now at: removesl.patch
88 $ hg st -c
87 $ hg st -c
89 C b
88 C b
90 C c
89 C c
91 C s
90 C s
92
91
93 replace broken symlink with another broken symlink
92 replace broken symlink with another broken symlink
94
93
95 $ ln -s linka linka
94 $ ln -s linka linka
96 $ hg add linka
95 $ hg add linka
97 $ hg qnew link
96 $ hg qnew link
98 $ hg mv linka linkb
97 $ hg mv linka linkb
99 $ rm linkb
98 $ rm linkb
100 $ ln -s linkb linkb
99 $ ln -s linkb linkb
101 $ hg qnew movelink
100 $ hg qnew movelink
102 $ hg qpop
101 $ hg qpop
103 popping movelink
102 popping movelink
104 now at: link
103 now at: link
105 $ hg qpush
104 $ hg qpush
106 applying movelink
105 applying movelink
107 now at: movelink
106 now at: movelink
108 $ "$TESTDIR/readlink.py" linkb
107 $ "$TESTDIR/readlink.py" linkb
109 linkb -> linkb
108 linkb -> linkb
@@ -1,722 +1,723 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > graphlog=
3 > graphlog=
4 > rebase=
4 > rebase=
5 > mq=
5 > mq=
6 >
6 >
7 > [phases]
7 > [phases]
8 > publish=False
8 > publish=False
9 >
9 >
10 > [alias]
10 > [alias]
11 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
11 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
12 > tglogp = log -G --template "{rev}:{phase} '{desc}' {branches}\n"
12 > tglogp = log -G --template "{rev}:{phase} '{desc}' {branches}\n"
13 > EOF
13 > EOF
14
14
15 Create repo a:
15 Create repo a:
16
16
17 $ hg init a
17 $ hg init a
18 $ cd a
18 $ cd a
19 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
19 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
20 adding changesets
20 adding changesets
21 adding manifests
21 adding manifests
22 adding file changes
22 adding file changes
23 added 8 changesets with 7 changes to 7 files (+2 heads)
23 added 8 changesets with 7 changes to 7 files (+2 heads)
24 (run 'hg heads' to see heads, 'hg merge' to merge)
24 (run 'hg heads' to see heads, 'hg merge' to merge)
25 $ hg up tip
25 $ hg up tip
26 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
26 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
27
27
28 $ hg tglog
28 $ hg tglog
29 @ 7: 'H'
29 @ 7: 'H'
30 |
30 |
31 | o 6: 'G'
31 | o 6: 'G'
32 |/|
32 |/|
33 o | 5: 'F'
33 o | 5: 'F'
34 | |
34 | |
35 | o 4: 'E'
35 | o 4: 'E'
36 |/
36 |/
37 | o 3: 'D'
37 | o 3: 'D'
38 | |
38 | |
39 | o 2: 'C'
39 | o 2: 'C'
40 | |
40 | |
41 | o 1: 'B'
41 | o 1: 'B'
42 |/
42 |/
43 o 0: 'A'
43 o 0: 'A'
44
44
45 $ cd ..
45 $ cd ..
46
46
47
47
48 Rebasing B onto H and collapsing changesets with different phases:
48 Rebasing B onto H and collapsing changesets with different phases:
49
49
50
50
51 $ hg clone -q -u 3 a a1
51 $ hg clone -q -u 3 a a1
52 $ cd a1
52 $ cd a1
53
53
54 $ hg phase --force --secret 3
54 $ hg phase --force --secret 3
55
55
56 $ hg rebase --collapse --keepbranches
56 $ hg rebase --collapse --keepbranches
57 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
57 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
58
58
59 $ hg tglogp
59 $ hg tglogp
60 @ 5:secret 'Collapsed revision
60 @ 5:secret 'Collapsed revision
61 | * B
61 | * B
62 | * C
62 | * C
63 | * D'
63 | * D'
64 o 4:draft 'H'
64 o 4:draft 'H'
65 |
65 |
66 | o 3:draft 'G'
66 | o 3:draft 'G'
67 |/|
67 |/|
68 o | 2:draft 'F'
68 o | 2:draft 'F'
69 | |
69 | |
70 | o 1:draft 'E'
70 | o 1:draft 'E'
71 |/
71 |/
72 o 0:draft 'A'
72 o 0:draft 'A'
73
73
74 $ hg manifest
74 $ hg manifest
75 A
75 A
76 B
76 B
77 C
77 C
78 D
78 D
79 F
79 F
80 H
80 H
81
81
82 $ cd ..
82 $ cd ..
83
83
84
84
85 Rebasing E onto H:
85 Rebasing E onto H:
86
86
87 $ hg clone -q -u . a a2
87 $ hg clone -q -u . a a2
88 $ cd a2
88 $ cd a2
89
89
90 $ hg phase --force --secret 6
90 $ hg phase --force --secret 6
91 $ hg rebase --source 4 --collapse
91 $ hg rebase --source 4 --collapse
92 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
92 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
93
93
94 $ hg tglog
94 $ hg tglog
95 @ 6: 'Collapsed revision
95 @ 6: 'Collapsed revision
96 | * E
96 | * E
97 | * G'
97 | * G'
98 o 5: 'H'
98 o 5: 'H'
99 |
99 |
100 o 4: 'F'
100 o 4: 'F'
101 |
101 |
102 | o 3: 'D'
102 | o 3: 'D'
103 | |
103 | |
104 | o 2: 'C'
104 | o 2: 'C'
105 | |
105 | |
106 | o 1: 'B'
106 | o 1: 'B'
107 |/
107 |/
108 o 0: 'A'
108 o 0: 'A'
109
109
110 $ hg manifest
110 $ hg manifest
111 A
111 A
112 E
112 E
113 F
113 F
114 H
114 H
115
115
116 $ cd ..
116 $ cd ..
117
117
118 Rebasing G onto H with custom message:
118 Rebasing G onto H with custom message:
119
119
120 $ hg clone -q -u . a a3
120 $ hg clone -q -u . a a3
121 $ cd a3
121 $ cd a3
122
122
123 $ hg rebase --base 6 -m 'custom message'
123 $ hg rebase --base 6 -m 'custom message'
124 abort: message can only be specified with collapse
124 abort: message can only be specified with collapse
125 [255]
125 [255]
126
126
127 $ hg rebase --source 4 --collapse -m 'custom message'
127 $ hg rebase --source 4 --collapse -m 'custom message'
128 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
128 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
129
129
130 $ hg tglog
130 $ hg tglog
131 @ 6: 'custom message'
131 @ 6: 'custom message'
132 |
132 |
133 o 5: 'H'
133 o 5: 'H'
134 |
134 |
135 o 4: 'F'
135 o 4: 'F'
136 |
136 |
137 | o 3: 'D'
137 | o 3: 'D'
138 | |
138 | |
139 | o 2: 'C'
139 | o 2: 'C'
140 | |
140 | |
141 | o 1: 'B'
141 | o 1: 'B'
142 |/
142 |/
143 o 0: 'A'
143 o 0: 'A'
144
144
145 $ hg manifest
145 $ hg manifest
146 A
146 A
147 E
147 E
148 F
148 F
149 H
149 H
150
150
151 $ cd ..
151 $ cd ..
152
152
153 Create repo b:
153 Create repo b:
154
154
155 $ hg init b
155 $ hg init b
156 $ cd b
156 $ cd b
157
157
158 $ echo A > A
158 $ echo A > A
159 $ hg ci -Am A
159 $ hg ci -Am A
160 adding A
160 adding A
161 $ echo B > B
161 $ echo B > B
162 $ hg ci -Am B
162 $ hg ci -Am B
163 adding B
163 adding B
164
164
165 $ hg up -q 0
165 $ hg up -q 0
166
166
167 $ echo C > C
167 $ echo C > C
168 $ hg ci -Am C
168 $ hg ci -Am C
169 adding C
169 adding C
170 created new head
170 created new head
171
171
172 $ hg merge
172 $ hg merge
173 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
173 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 (branch merge, don't forget to commit)
174 (branch merge, don't forget to commit)
175
175
176 $ echo D > D
176 $ echo D > D
177 $ hg ci -Am D
177 $ hg ci -Am D
178 adding D
178 adding D
179
179
180 $ hg up -q 1
180 $ hg up -q 1
181
181
182 $ echo E > E
182 $ echo E > E
183 $ hg ci -Am E
183 $ hg ci -Am E
184 adding E
184 adding E
185 created new head
185 created new head
186
186
187 $ echo F > F
187 $ echo F > F
188 $ hg ci -Am F
188 $ hg ci -Am F
189 adding F
189 adding F
190
190
191 $ hg merge
191 $ hg merge
192 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
192 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
193 (branch merge, don't forget to commit)
193 (branch merge, don't forget to commit)
194 $ hg ci -m G
194 $ hg ci -m G
195
195
196 $ hg up -q 0
196 $ hg up -q 0
197
197
198 $ echo H > H
198 $ echo H > H
199 $ hg ci -Am H
199 $ hg ci -Am H
200 adding H
200 adding H
201 created new head
201 created new head
202
202
203 $ hg tglog
203 $ hg tglog
204 @ 7: 'H'
204 @ 7: 'H'
205 |
205 |
206 | o 6: 'G'
206 | o 6: 'G'
207 | |\
207 | |\
208 | | o 5: 'F'
208 | | o 5: 'F'
209 | | |
209 | | |
210 | | o 4: 'E'
210 | | o 4: 'E'
211 | | |
211 | | |
212 | o | 3: 'D'
212 | o | 3: 'D'
213 | |\|
213 | |\|
214 | o | 2: 'C'
214 | o | 2: 'C'
215 |/ /
215 |/ /
216 | o 1: 'B'
216 | o 1: 'B'
217 |/
217 |/
218 o 0: 'A'
218 o 0: 'A'
219
219
220 $ cd ..
220 $ cd ..
221
221
222
222
223 Rebase and collapse - more than one external (fail):
223 Rebase and collapse - more than one external (fail):
224
224
225 $ hg clone -q -u . b b1
225 $ hg clone -q -u . b b1
226 $ cd b1
226 $ cd b1
227
227
228 $ hg rebase -s 2 --collapse
228 $ hg rebase -s 2 --collapse
229 abort: unable to collapse, there is more than one external parent
229 abort: unable to collapse, there is more than one external parent
230 [255]
230 [255]
231
231
232 Rebase and collapse - E onto H:
232 Rebase and collapse - E onto H:
233
233
234 $ hg rebase -s 4 --collapse # root (4) is not a merge
234 $ hg rebase -s 4 --collapse # root (4) is not a merge
235 saved backup bundle to $TESTTMP/b1/.hg/strip-backup/*-backup.hg (glob)
235 saved backup bundle to $TESTTMP/b1/.hg/strip-backup/*-backup.hg (glob)
236
236
237 $ hg tglog
237 $ hg tglog
238 @ 5: 'Collapsed revision
238 @ 5: 'Collapsed revision
239 |\ * E
239 |\ * E
240 | | * F
240 | | * F
241 | | * G'
241 | | * G'
242 | o 4: 'H'
242 | o 4: 'H'
243 | |
243 | |
244 o | 3: 'D'
244 o | 3: 'D'
245 |\ \
245 |\ \
246 | o | 2: 'C'
246 | o | 2: 'C'
247 | |/
247 | |/
248 o / 1: 'B'
248 o / 1: 'B'
249 |/
249 |/
250 o 0: 'A'
250 o 0: 'A'
251
251
252 $ hg manifest
252 $ hg manifest
253 A
253 A
254 C
254 C
255 D
255 D
256 E
256 E
257 F
257 F
258 H
258 H
259
259
260 $ cd ..
260 $ cd ..
261
261
262
262
263
263
264
264
265 Test that branchheads cache is updated correctly when doing a strip in which
265 Test that branchheads cache is updated correctly when doing a strip in which
266 the parent of the ancestor node to be stripped does not become a head and
266 the parent of the ancestor node to be stripped does not become a head and also,
267 also, the parent of a node that is a child of the node stripped becomes a head
267 the parent of a node that is a child of the node stripped becomes a head (node
268 (node 3).
268 3). The code is now much simpler and we could just test a simpler scenario
269 We keep it the test this way in case new complexity is injected.
269
270
270 $ hg clone -q -u . b b2
271 $ hg clone -q -u . b b2
271 $ cd b2
272 $ cd b2
272
273
273 $ hg heads --template="{rev}:{node} {branch}\n"
274 $ hg heads --template="{rev}:{node} {branch}\n"
274 7:c65502d4178782309ce0574c5ae6ee9485a9bafa default
275 7:c65502d4178782309ce0574c5ae6ee9485a9bafa default
275 6:c772a8b2dc17629cec88a19d09c926c4814b12c7 default
276 6:c772a8b2dc17629cec88a19d09c926c4814b12c7 default
276
277
277 $ cat $TESTTMP/b2/.hg/cache/branchheads-served
278 $ cat $TESTTMP/b2/.hg/cache/branchheads-served
278 c65502d4178782309ce0574c5ae6ee9485a9bafa 7
279 c65502d4178782309ce0574c5ae6ee9485a9bafa 7
279 c772a8b2dc17629cec88a19d09c926c4814b12c7 default
280 c772a8b2dc17629cec88a19d09c926c4814b12c7 default
280 c65502d4178782309ce0574c5ae6ee9485a9bafa default
281 c65502d4178782309ce0574c5ae6ee9485a9bafa default
281
282
282 $ hg strip 4
283 $ hg strip 4
283 saved backup bundle to $TESTTMP/b2/.hg/strip-backup/8a5212ebc852-backup.hg (glob)
284 saved backup bundle to $TESTTMP/b2/.hg/strip-backup/8a5212ebc852-backup.hg (glob)
284
285
285 $ cat $TESTTMP/b2/.hg/cache/branchheads
286 $ cat $TESTTMP/b2/.hg/cache/branchheads-served
286 c65502d4178782309ce0574c5ae6ee9485a9bafa 4
287 c65502d4178782309ce0574c5ae6ee9485a9bafa 4
287 2870ad076e541e714f3c2bc32826b5c6a6e5b040 default
288 2870ad076e541e714f3c2bc32826b5c6a6e5b040 default
288 c65502d4178782309ce0574c5ae6ee9485a9bafa default
289 c65502d4178782309ce0574c5ae6ee9485a9bafa default
289
290
290 $ hg heads --template="{rev}:{node} {branch}\n"
291 $ hg heads --template="{rev}:{node} {branch}\n"
291 4:c65502d4178782309ce0574c5ae6ee9485a9bafa default
292 4:c65502d4178782309ce0574c5ae6ee9485a9bafa default
292 3:2870ad076e541e714f3c2bc32826b5c6a6e5b040 default
293 3:2870ad076e541e714f3c2bc32826b5c6a6e5b040 default
293
294
294 $ cd ..
295 $ cd ..
295
296
296
297
297
298
298
299
299
300
300
301
301 Create repo c:
302 Create repo c:
302
303
303 $ hg init c
304 $ hg init c
304 $ cd c
305 $ cd c
305
306
306 $ echo A > A
307 $ echo A > A
307 $ hg ci -Am A
308 $ hg ci -Am A
308 adding A
309 adding A
309 $ echo B > B
310 $ echo B > B
310 $ hg ci -Am B
311 $ hg ci -Am B
311 adding B
312 adding B
312
313
313 $ hg up -q 0
314 $ hg up -q 0
314
315
315 $ echo C > C
316 $ echo C > C
316 $ hg ci -Am C
317 $ hg ci -Am C
317 adding C
318 adding C
318 created new head
319 created new head
319
320
320 $ hg merge
321 $ hg merge
321 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
322 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
322 (branch merge, don't forget to commit)
323 (branch merge, don't forget to commit)
323
324
324 $ echo D > D
325 $ echo D > D
325 $ hg ci -Am D
326 $ hg ci -Am D
326 adding D
327 adding D
327
328
328 $ hg up -q 1
329 $ hg up -q 1
329
330
330 $ echo E > E
331 $ echo E > E
331 $ hg ci -Am E
332 $ hg ci -Am E
332 adding E
333 adding E
333 created new head
334 created new head
334 $ echo F > E
335 $ echo F > E
335 $ hg ci -m 'F'
336 $ hg ci -m 'F'
336
337
337 $ echo G > G
338 $ echo G > G
338 $ hg ci -Am G
339 $ hg ci -Am G
339 adding G
340 adding G
340
341
341 $ hg merge
342 $ hg merge
342 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
343 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
343 (branch merge, don't forget to commit)
344 (branch merge, don't forget to commit)
344
345
345 $ hg ci -m H
346 $ hg ci -m H
346
347
347 $ hg up -q 0
348 $ hg up -q 0
348
349
349 $ echo I > I
350 $ echo I > I
350 $ hg ci -Am I
351 $ hg ci -Am I
351 adding I
352 adding I
352 created new head
353 created new head
353
354
354 $ hg tglog
355 $ hg tglog
355 @ 8: 'I'
356 @ 8: 'I'
356 |
357 |
357 | o 7: 'H'
358 | o 7: 'H'
358 | |\
359 | |\
359 | | o 6: 'G'
360 | | o 6: 'G'
360 | | |
361 | | |
361 | | o 5: 'F'
362 | | o 5: 'F'
362 | | |
363 | | |
363 | | o 4: 'E'
364 | | o 4: 'E'
364 | | |
365 | | |
365 | o | 3: 'D'
366 | o | 3: 'D'
366 | |\|
367 | |\|
367 | o | 2: 'C'
368 | o | 2: 'C'
368 |/ /
369 |/ /
369 | o 1: 'B'
370 | o 1: 'B'
370 |/
371 |/
371 o 0: 'A'
372 o 0: 'A'
372
373
373 $ cd ..
374 $ cd ..
374
375
375
376
376 Rebase and collapse - E onto I:
377 Rebase and collapse - E onto I:
377
378
378 $ hg clone -q -u . c c1
379 $ hg clone -q -u . c c1
379 $ cd c1
380 $ cd c1
380
381
381 $ hg rebase -s 4 --collapse # root (4) is not a merge
382 $ hg rebase -s 4 --collapse # root (4) is not a merge
382 merging E
383 merging E
383 saved backup bundle to $TESTTMP/c1/.hg/strip-backup/*-backup.hg (glob)
384 saved backup bundle to $TESTTMP/c1/.hg/strip-backup/*-backup.hg (glob)
384
385
385 $ hg tglog
386 $ hg tglog
386 @ 5: 'Collapsed revision
387 @ 5: 'Collapsed revision
387 |\ * E
388 |\ * E
388 | | * F
389 | | * F
389 | | * G
390 | | * G
390 | | * H'
391 | | * H'
391 | o 4: 'I'
392 | o 4: 'I'
392 | |
393 | |
393 o | 3: 'D'
394 o | 3: 'D'
394 |\ \
395 |\ \
395 | o | 2: 'C'
396 | o | 2: 'C'
396 | |/
397 | |/
397 o / 1: 'B'
398 o / 1: 'B'
398 |/
399 |/
399 o 0: 'A'
400 o 0: 'A'
400
401
401 $ hg manifest
402 $ hg manifest
402 A
403 A
403 C
404 C
404 D
405 D
405 E
406 E
406 G
407 G
407 I
408 I
408
409
409 $ cat E
410 $ cat E
410 F
411 F
411
412
412 $ cd ..
413 $ cd ..
413
414
414
415
415 Create repo d:
416 Create repo d:
416
417
417 $ hg init d
418 $ hg init d
418 $ cd d
419 $ cd d
419
420
420 $ echo A > A
421 $ echo A > A
421 $ hg ci -Am A
422 $ hg ci -Am A
422 adding A
423 adding A
423 $ echo B > B
424 $ echo B > B
424 $ hg ci -Am B
425 $ hg ci -Am B
425 adding B
426 adding B
426 $ echo C > C
427 $ echo C > C
427 $ hg ci -Am C
428 $ hg ci -Am C
428 adding C
429 adding C
429
430
430 $ hg up -q 1
431 $ hg up -q 1
431
432
432 $ echo D > D
433 $ echo D > D
433 $ hg ci -Am D
434 $ hg ci -Am D
434 adding D
435 adding D
435 created new head
436 created new head
436 $ hg merge
437 $ hg merge
437 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
438 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
438 (branch merge, don't forget to commit)
439 (branch merge, don't forget to commit)
439
440
440 $ hg ci -m E
441 $ hg ci -m E
441
442
442 $ hg up -q 0
443 $ hg up -q 0
443
444
444 $ echo F > F
445 $ echo F > F
445 $ hg ci -Am F
446 $ hg ci -Am F
446 adding F
447 adding F
447 created new head
448 created new head
448
449
449 $ hg tglog
450 $ hg tglog
450 @ 5: 'F'
451 @ 5: 'F'
451 |
452 |
452 | o 4: 'E'
453 | o 4: 'E'
453 | |\
454 | |\
454 | | o 3: 'D'
455 | | o 3: 'D'
455 | | |
456 | | |
456 | o | 2: 'C'
457 | o | 2: 'C'
457 | |/
458 | |/
458 | o 1: 'B'
459 | o 1: 'B'
459 |/
460 |/
460 o 0: 'A'
461 o 0: 'A'
461
462
462 $ cd ..
463 $ cd ..
463
464
464
465
465 Rebase and collapse - B onto F:
466 Rebase and collapse - B onto F:
466
467
467 $ hg clone -q -u . d d1
468 $ hg clone -q -u . d d1
468 $ cd d1
469 $ cd d1
469
470
470 $ hg rebase -s 1 --collapse
471 $ hg rebase -s 1 --collapse
471 saved backup bundle to $TESTTMP/d1/.hg/strip-backup/*-backup.hg (glob)
472 saved backup bundle to $TESTTMP/d1/.hg/strip-backup/*-backup.hg (glob)
472
473
473 $ hg tglog
474 $ hg tglog
474 @ 2: 'Collapsed revision
475 @ 2: 'Collapsed revision
475 | * B
476 | * B
476 | * C
477 | * C
477 | * D
478 | * D
478 | * E'
479 | * E'
479 o 1: 'F'
480 o 1: 'F'
480 |
481 |
481 o 0: 'A'
482 o 0: 'A'
482
483
483 $ hg manifest
484 $ hg manifest
484 A
485 A
485 B
486 B
486 C
487 C
487 D
488 D
488 F
489 F
489
490
490 Interactions between collapse and keepbranches
491 Interactions between collapse and keepbranches
491 $ cd ..
492 $ cd ..
492 $ hg init e
493 $ hg init e
493 $ cd e
494 $ cd e
494 $ echo 'a' > a
495 $ echo 'a' > a
495 $ hg ci -Am 'A'
496 $ hg ci -Am 'A'
496 adding a
497 adding a
497
498
498 $ hg branch '1'
499 $ hg branch '1'
499 marked working directory as branch 1
500 marked working directory as branch 1
500 (branches are permanent and global, did you want a bookmark?)
501 (branches are permanent and global, did you want a bookmark?)
501 $ echo 'b' > b
502 $ echo 'b' > b
502 $ hg ci -Am 'B'
503 $ hg ci -Am 'B'
503 adding b
504 adding b
504
505
505 $ hg branch '2'
506 $ hg branch '2'
506 marked working directory as branch 2
507 marked working directory as branch 2
507 (branches are permanent and global, did you want a bookmark?)
508 (branches are permanent and global, did you want a bookmark?)
508 $ echo 'c' > c
509 $ echo 'c' > c
509 $ hg ci -Am 'C'
510 $ hg ci -Am 'C'
510 adding c
511 adding c
511
512
512 $ hg up -q 0
513 $ hg up -q 0
513 $ echo 'd' > d
514 $ echo 'd' > d
514 $ hg ci -Am 'D'
515 $ hg ci -Am 'D'
515 adding d
516 adding d
516
517
517 $ hg tglog
518 $ hg tglog
518 @ 3: 'D'
519 @ 3: 'D'
519 |
520 |
520 | o 2: 'C' 2
521 | o 2: 'C' 2
521 | |
522 | |
522 | o 1: 'B' 1
523 | o 1: 'B' 1
523 |/
524 |/
524 o 0: 'A'
525 o 0: 'A'
525
526
526 $ hg rebase --keepbranches --collapse -s 1 -d 3
527 $ hg rebase --keepbranches --collapse -s 1 -d 3
527 abort: cannot collapse multiple named branches
528 abort: cannot collapse multiple named branches
528 [255]
529 [255]
529
530
530 $ repeatchange() {
531 $ repeatchange() {
531 > hg checkout $1
532 > hg checkout $1
532 > hg cp d z
533 > hg cp d z
533 > echo blah >> z
534 > echo blah >> z
534 > hg commit -Am "$2" --user "$3"
535 > hg commit -Am "$2" --user "$3"
535 > }
536 > }
536 $ repeatchange 3 "E" "user1"
537 $ repeatchange 3 "E" "user1"
537 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
538 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
538 $ repeatchange 3 "E" "user2"
539 $ repeatchange 3 "E" "user2"
539 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
540 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
540 created new head
541 created new head
541 $ hg tglog
542 $ hg tglog
542 @ 5: 'E'
543 @ 5: 'E'
543 |
544 |
544 | o 4: 'E'
545 | o 4: 'E'
545 |/
546 |/
546 o 3: 'D'
547 o 3: 'D'
547 |
548 |
548 | o 2: 'C' 2
549 | o 2: 'C' 2
549 | |
550 | |
550 | o 1: 'B' 1
551 | o 1: 'B' 1
551 |/
552 |/
552 o 0: 'A'
553 o 0: 'A'
553
554
554 $ hg rebase -s 5 -d 4
555 $ hg rebase -s 5 -d 4
555 saved backup bundle to $TESTTMP/e/.hg/strip-backup/*-backup.hg (glob)
556 saved backup bundle to $TESTTMP/e/.hg/strip-backup/*-backup.hg (glob)
556 $ hg tglog
557 $ hg tglog
557 @ 4: 'E'
558 @ 4: 'E'
558 |
559 |
559 o 3: 'D'
560 o 3: 'D'
560 |
561 |
561 | o 2: 'C' 2
562 | o 2: 'C' 2
562 | |
563 | |
563 | o 1: 'B' 1
564 | o 1: 'B' 1
564 |/
565 |/
565 o 0: 'A'
566 o 0: 'A'
566
567
567 $ hg export tip
568 $ hg export tip
568 # HG changeset patch
569 # HG changeset patch
569 # User user1
570 # User user1
570 # Date 0 0
571 # Date 0 0
571 # Node ID f338eb3c2c7cc5b5915676a2376ba7ac558c5213
572 # Node ID f338eb3c2c7cc5b5915676a2376ba7ac558c5213
572 # Parent 41acb9dca9eb976e84cd21fcb756b4afa5a35c09
573 # Parent 41acb9dca9eb976e84cd21fcb756b4afa5a35c09
573 E
574 E
574
575
575 diff -r 41acb9dca9eb -r f338eb3c2c7c z
576 diff -r 41acb9dca9eb -r f338eb3c2c7c z
576 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
577 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
577 +++ b/z Thu Jan 01 00:00:00 1970 +0000
578 +++ b/z Thu Jan 01 00:00:00 1970 +0000
578 @@ -0,0 +1,2 @@
579 @@ -0,0 +1,2 @@
579 +d
580 +d
580 +blah
581 +blah
581
582
582 $ cd ..
583 $ cd ..
583
584
584 Rebase, collapse and copies
585 Rebase, collapse and copies
585
586
586 $ hg init copies
587 $ hg init copies
587 $ cd copies
588 $ cd copies
588 $ hg unbundle "$TESTDIR/bundles/renames.hg"
589 $ hg unbundle "$TESTDIR/bundles/renames.hg"
589 adding changesets
590 adding changesets
590 adding manifests
591 adding manifests
591 adding file changes
592 adding file changes
592 added 4 changesets with 11 changes to 7 files (+1 heads)
593 added 4 changesets with 11 changes to 7 files (+1 heads)
593 (run 'hg heads' to see heads, 'hg merge' to merge)
594 (run 'hg heads' to see heads, 'hg merge' to merge)
594 $ hg up -q tip
595 $ hg up -q tip
595 $ hg tglog
596 $ hg tglog
596 @ 3: 'move2'
597 @ 3: 'move2'
597 |
598 |
598 o 2: 'move1'
599 o 2: 'move1'
599 |
600 |
600 | o 1: 'change'
601 | o 1: 'change'
601 |/
602 |/
602 o 0: 'add'
603 o 0: 'add'
603
604
604 $ hg rebase --collapse -d 1
605 $ hg rebase --collapse -d 1
605 merging a and d to d
606 merging a and d to d
606 merging b and e to e
607 merging b and e to e
607 merging c and f to f
608 merging c and f to f
608 merging e and g to g
609 merging e and g to g
609 merging f and c to c
610 merging f and c to c
610 saved backup bundle to $TESTTMP/copies/.hg/strip-backup/*-backup.hg (glob)
611 saved backup bundle to $TESTTMP/copies/.hg/strip-backup/*-backup.hg (glob)
611 $ hg st
612 $ hg st
612 $ hg st --copies --change .
613 $ hg st --copies --change .
613 A d
614 A d
614 a
615 a
615 A g
616 A g
616 b
617 b
617 R b
618 R b
618 $ cat c
619 $ cat c
619 c
620 c
620 c
621 c
621 $ cat d
622 $ cat d
622 a
623 a
623 a
624 a
624 $ cat g
625 $ cat g
625 b
626 b
626 b
627 b
627 $ hg log -r . --template "{file_copies}\n"
628 $ hg log -r . --template "{file_copies}\n"
628 d (a)g (b)
629 d (a)g (b)
629
630
630 Test collapsing a middle revision in-place
631 Test collapsing a middle revision in-place
631
632
632 $ hg tglog
633 $ hg tglog
633 @ 2: 'Collapsed revision
634 @ 2: 'Collapsed revision
634 | * move1
635 | * move1
635 | * move2'
636 | * move2'
636 o 1: 'change'
637 o 1: 'change'
637 |
638 |
638 o 0: 'add'
639 o 0: 'add'
639
640
640 $ hg rebase --collapse -r 1 -d 0
641 $ hg rebase --collapse -r 1 -d 0
641 abort: can't remove original changesets with unrebased descendants
642 abort: can't remove original changesets with unrebased descendants
642 (use --keep to keep original changesets)
643 (use --keep to keep original changesets)
643 [255]
644 [255]
644
645
645 Test collapsing in place
646 Test collapsing in place
646
647
647 $ hg rebase --collapse -b . -d 0
648 $ hg rebase --collapse -b . -d 0
648 saved backup bundle to $TESTTMP/copies/.hg/strip-backup/*-backup.hg (glob)
649 saved backup bundle to $TESTTMP/copies/.hg/strip-backup/*-backup.hg (glob)
649 $ hg st --change . --copies
650 $ hg st --change . --copies
650 M a
651 M a
651 M c
652 M c
652 A d
653 A d
653 a
654 a
654 A g
655 A g
655 b
656 b
656 R b
657 R b
657 $ cat a
658 $ cat a
658 a
659 a
659 a
660 a
660 $ cat c
661 $ cat c
661 c
662 c
662 c
663 c
663 $ cat d
664 $ cat d
664 a
665 a
665 a
666 a
666 $ cat g
667 $ cat g
667 b
668 b
668 b
669 b
669 $ cd ..
670 $ cd ..
670
671
671
672
672 Test stripping a revision with another child
673 Test stripping a revision with another child
673
674
674 $ hg init f
675 $ hg init f
675 $ cd f
676 $ cd f
676
677
677 $ echo A > A
678 $ echo A > A
678 $ hg ci -Am A
679 $ hg ci -Am A
679 adding A
680 adding A
680 $ echo B > B
681 $ echo B > B
681 $ hg ci -Am B
682 $ hg ci -Am B
682 adding B
683 adding B
683
684
684 $ hg up -q 0
685 $ hg up -q 0
685
686
686 $ echo C > C
687 $ echo C > C
687 $ hg ci -Am C
688 $ hg ci -Am C
688 adding C
689 adding C
689 created new head
690 created new head
690
691
691 $ hg tglog
692 $ hg tglog
692 @ 2: 'C'
693 @ 2: 'C'
693 |
694 |
694 | o 1: 'B'
695 | o 1: 'B'
695 |/
696 |/
696 o 0: 'A'
697 o 0: 'A'
697
698
698
699
699
700
700 $ hg heads --template="{rev}:{node} {branch}: {desc}\n"
701 $ hg heads --template="{rev}:{node} {branch}: {desc}\n"
701 2:c5cefa58fd557f84b72b87f970135984337acbc5 default: C
702 2:c5cefa58fd557f84b72b87f970135984337acbc5 default: C
702 1:27547f69f25460a52fff66ad004e58da7ad3fb56 default: B
703 1:27547f69f25460a52fff66ad004e58da7ad3fb56 default: B
703
704
704 $ hg strip 2
705 $ hg strip 2
705 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
706 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
706 saved backup bundle to $TESTTMP/f/.hg/strip-backup/*-backup.hg (glob)
707 saved backup bundle to $TESTTMP/f/.hg/strip-backup/*-backup.hg (glob)
707
708
708 $ hg tglog
709 $ hg tglog
709 o 1: 'B'
710 o 1: 'B'
710 |
711 |
711 @ 0: 'A'
712 @ 0: 'A'
712
713
713
714
714
715
715 $ hg heads --template="{rev}:{node} {branch}: {desc}\n"
716 $ hg heads --template="{rev}:{node} {branch}: {desc}\n"
716 1:27547f69f25460a52fff66ad004e58da7ad3fb56 default: B
717 1:27547f69f25460a52fff66ad004e58da7ad3fb56 default: B
717
718
718 $ cd ..
719 $ cd ..
719
720
720
721
721
722
722
723
General Comments 0
You need to be logged in to leave comments. Login now