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