##// END OF EJS Templates
branches: correctly show inactive multiheaded branches...
the31k -
r34076:abf91c4f default
parent child Browse files
Show More
@@ -1,520 +1,523 b''
1 1 # branchmap.py - logic to computes, maintain and stores branchmap for local repo
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
8 8 from __future__ import absolute_import
9 9
10 10 import struct
11 11
12 12 from .node import (
13 13 bin,
14 14 hex,
15 15 nullid,
16 16 nullrev,
17 17 )
18 18 from . import (
19 19 encoding,
20 20 error,
21 21 scmutil,
22 22 util,
23 23 )
24 24
25 25 calcsize = struct.calcsize
26 26 pack_into = struct.pack_into
27 27 unpack_from = struct.unpack_from
28 28
29 29 def _filename(repo):
30 30 """name of a branchcache file for a given repo or repoview"""
31 31 filename = "branch2"
32 32 if repo.filtername:
33 33 filename = '%s-%s' % (filename, repo.filtername)
34 34 return filename
35 35
36 36 def read(repo):
37 37 try:
38 38 f = repo.cachevfs(_filename(repo))
39 39 lines = f.read().split('\n')
40 40 f.close()
41 41 except (IOError, OSError):
42 42 return None
43 43
44 44 try:
45 45 cachekey = lines.pop(0).split(" ", 2)
46 46 last, lrev = cachekey[:2]
47 47 last, lrev = bin(last), int(lrev)
48 48 filteredhash = None
49 49 if len(cachekey) > 2:
50 50 filteredhash = bin(cachekey[2])
51 51 partial = branchcache(tipnode=last, tiprev=lrev,
52 52 filteredhash=filteredhash)
53 53 if not partial.validfor(repo):
54 54 # invalidate the cache
55 55 raise ValueError('tip differs')
56 56 cl = repo.changelog
57 57 for l in lines:
58 58 if not l:
59 59 continue
60 60 node, state, label = l.split(" ", 2)
61 61 if state not in 'oc':
62 62 raise ValueError('invalid branch state')
63 63 label = encoding.tolocal(label.strip())
64 64 node = bin(node)
65 65 if not cl.hasnode(node):
66 66 raise ValueError('node %s does not exist' % hex(node))
67 67 partial.setdefault(label, []).append(node)
68 68 if state == 'c':
69 69 partial._closednodes.add(node)
70 70 except Exception as inst:
71 71 if repo.ui.debugflag:
72 72 msg = 'invalid branchheads cache'
73 73 if repo.filtername is not None:
74 74 msg += ' (%s)' % repo.filtername
75 75 msg += ': %s\n'
76 76 repo.ui.debug(msg % inst)
77 77 partial = None
78 78 return partial
79 79
80 80 ### Nearest subset relation
81 81 # Nearest subset of filter X is a filter Y so that:
82 82 # * Y is included in X,
83 83 # * X - Y is as small as possible.
84 84 # This create and ordering used for branchmap purpose.
85 85 # the ordering may be partial
86 86 subsettable = {None: 'visible',
87 87 'visible': 'served',
88 88 'served': 'immutable',
89 89 'immutable': 'base'}
90 90
91 91 def updatecache(repo):
92 92 cl = repo.changelog
93 93 filtername = repo.filtername
94 94 partial = repo._branchcaches.get(filtername)
95 95
96 96 revs = []
97 97 if partial is None or not partial.validfor(repo):
98 98 partial = read(repo)
99 99 if partial is None:
100 100 subsetname = subsettable.get(filtername)
101 101 if subsetname is None:
102 102 partial = branchcache()
103 103 else:
104 104 subset = repo.filtered(subsetname)
105 105 partial = subset.branchmap().copy()
106 106 extrarevs = subset.changelog.filteredrevs - cl.filteredrevs
107 107 revs.extend(r for r in extrarevs if r <= partial.tiprev)
108 108 revs.extend(cl.revs(start=partial.tiprev + 1))
109 109 if revs:
110 110 partial.update(repo, revs)
111 111 partial.write(repo)
112 112
113 113 assert partial.validfor(repo), filtername
114 114 repo._branchcaches[repo.filtername] = partial
115 115
116 116 def replacecache(repo, bm):
117 117 """Replace the branchmap cache for a repo with a branch mapping.
118 118
119 119 This is likely only called during clone with a branch map from a remote.
120 120 """
121 121 rbheads = []
122 122 closed = []
123 123 for bheads in bm.itervalues():
124 124 rbheads.extend(bheads)
125 125 for h in bheads:
126 126 r = repo.changelog.rev(h)
127 127 b, c = repo.changelog.branchinfo(r)
128 128 if c:
129 129 closed.append(h)
130 130
131 131 if rbheads:
132 132 rtiprev = max((int(repo.changelog.rev(node))
133 133 for node in rbheads))
134 134 cache = branchcache(bm,
135 135 repo[rtiprev].node(),
136 136 rtiprev,
137 137 closednodes=closed)
138 138
139 139 # Try to stick it as low as possible
140 140 # filter above served are unlikely to be fetch from a clone
141 141 for candidate in ('base', 'immutable', 'served'):
142 142 rview = repo.filtered(candidate)
143 143 if cache.validfor(rview):
144 144 repo._branchcaches[candidate] = cache
145 145 cache.write(rview)
146 146 break
147 147
148 148 class branchcache(dict):
149 149 """A dict like object that hold branches heads cache.
150 150
151 151 This cache is used to avoid costly computations to determine all the
152 152 branch heads of a repo.
153 153
154 154 The cache is serialized on disk in the following format:
155 155
156 156 <tip hex node> <tip rev number> [optional filtered repo hex hash]
157 157 <branch head hex node> <open/closed state> <branch name>
158 158 <branch head hex node> <open/closed state> <branch name>
159 159 ...
160 160
161 161 The first line is used to check if the cache is still valid. If the
162 162 branch cache is for a filtered repo view, an optional third hash is
163 163 included that hashes the hashes of all filtered revisions.
164 164
165 165 The open/closed state is represented by a single letter 'o' or 'c'.
166 166 This field can be used to avoid changelog reads when determining if a
167 167 branch head closes a branch or not.
168 168 """
169 169
170 170 def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev,
171 171 filteredhash=None, closednodes=None):
172 172 super(branchcache, self).__init__(entries)
173 173 self.tipnode = tipnode
174 174 self.tiprev = tiprev
175 175 self.filteredhash = filteredhash
176 176 # closednodes is a set of nodes that close their branch. If the branch
177 177 # cache has been updated, it may contain nodes that are no longer
178 178 # heads.
179 179 if closednodes is None:
180 180 self._closednodes = set()
181 181 else:
182 182 self._closednodes = closednodes
183 183
184 184 def validfor(self, repo):
185 185 """Is the cache content valid regarding a repo
186 186
187 187 - False when cached tipnode is unknown or if we detect a strip.
188 188 - True when cache is up to date or a subset of current repo."""
189 189 try:
190 190 return ((self.tipnode == repo.changelog.node(self.tiprev))
191 191 and (self.filteredhash == \
192 192 scmutil.filteredhash(repo, self.tiprev)))
193 193 except IndexError:
194 194 return False
195 195
196 196 def _branchtip(self, heads):
197 197 '''Return tuple with last open head in heads and false,
198 198 otherwise return last closed head and true.'''
199 199 tip = heads[-1]
200 200 closed = True
201 201 for h in reversed(heads):
202 202 if h not in self._closednodes:
203 203 tip = h
204 204 closed = False
205 205 break
206 206 return tip, closed
207 207
208 208 def branchtip(self, branch):
209 209 '''Return the tipmost open head on branch head, otherwise return the
210 210 tipmost closed head on branch.
211 211 Raise KeyError for unknown branch.'''
212 212 return self._branchtip(self[branch])[0]
213 213
214 def iteropen(self, nodes):
215 return (n for n in nodes if n not in self._closednodes)
216
214 217 def branchheads(self, branch, closed=False):
215 218 heads = self[branch]
216 219 if not closed:
217 heads = [h for h in heads if h not in self._closednodes]
220 heads = list(self.iteropen(heads))
218 221 return heads
219 222
220 223 def iterbranches(self):
221 224 for bn, heads in self.iteritems():
222 225 yield (bn, heads) + self._branchtip(heads)
223 226
224 227 def copy(self):
225 228 """return an deep copy of the branchcache object"""
226 229 return branchcache(self, self.tipnode, self.tiprev, self.filteredhash,
227 230 self._closednodes)
228 231
229 232 def write(self, repo):
230 233 try:
231 234 f = repo.cachevfs(_filename(repo), "w", atomictemp=True)
232 235 cachekey = [hex(self.tipnode), '%d' % self.tiprev]
233 236 if self.filteredhash is not None:
234 237 cachekey.append(hex(self.filteredhash))
235 238 f.write(" ".join(cachekey) + '\n')
236 239 nodecount = 0
237 240 for label, nodes in sorted(self.iteritems()):
238 241 for node in nodes:
239 242 nodecount += 1
240 243 if node in self._closednodes:
241 244 state = 'c'
242 245 else:
243 246 state = 'o'
244 247 f.write("%s %s %s\n" % (hex(node), state,
245 248 encoding.fromlocal(label)))
246 249 f.close()
247 250 repo.ui.log('branchcache',
248 251 'wrote %s branch cache with %d labels and %d nodes\n',
249 252 repo.filtername, len(self), nodecount)
250 253 except (IOError, OSError, error.Abort) as inst:
251 254 repo.ui.debug("couldn't write branch cache: %s\n" % inst)
252 255 # Abort may be raise by read only opener
253 256 pass
254 257
255 258 def update(self, repo, revgen):
256 259 """Given a branchhead cache, self, that may have extra nodes or be
257 260 missing heads, and a generator of nodes that are strictly a superset of
258 261 heads missing, this function updates self to be correct.
259 262 """
260 263 starttime = util.timer()
261 264 cl = repo.changelog
262 265 # collect new branch entries
263 266 newbranches = {}
264 267 getbranchinfo = repo.revbranchcache().branchinfo
265 268 for r in revgen:
266 269 branch, closesbranch = getbranchinfo(r)
267 270 newbranches.setdefault(branch, []).append(r)
268 271 if closesbranch:
269 272 self._closednodes.add(cl.node(r))
270 273
271 274 # fetch current topological heads to speed up filtering
272 275 topoheads = set(cl.headrevs())
273 276
274 277 # if older branchheads are reachable from new ones, they aren't
275 278 # really branchheads. Note checking parents is insufficient:
276 279 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
277 280 for branch, newheadrevs in newbranches.iteritems():
278 281 bheads = self.setdefault(branch, [])
279 282 bheadset = set(cl.rev(node) for node in bheads)
280 283
281 284 # This have been tested True on all internal usage of this function.
282 285 # run it again in case of doubt
283 286 # assert not (set(bheadrevs) & set(newheadrevs))
284 287 newheadrevs.sort()
285 288 bheadset.update(newheadrevs)
286 289
287 290 # This prunes out two kinds of heads - heads that are superseded by
288 291 # a head in newheadrevs, and newheadrevs that are not heads because
289 292 # an existing head is their descendant.
290 293 uncertain = bheadset - topoheads
291 294 if uncertain:
292 295 floorrev = min(uncertain)
293 296 ancestors = set(cl.ancestors(newheadrevs, floorrev))
294 297 bheadset -= ancestors
295 298 bheadrevs = sorted(bheadset)
296 299 self[branch] = [cl.node(rev) for rev in bheadrevs]
297 300 tiprev = bheadrevs[-1]
298 301 if tiprev > self.tiprev:
299 302 self.tipnode = cl.node(tiprev)
300 303 self.tiprev = tiprev
301 304
302 305 if not self.validfor(repo):
303 306 # cache key are not valid anymore
304 307 self.tipnode = nullid
305 308 self.tiprev = nullrev
306 309 for heads in self.values():
307 310 tiprev = max(cl.rev(node) for node in heads)
308 311 if tiprev > self.tiprev:
309 312 self.tipnode = cl.node(tiprev)
310 313 self.tiprev = tiprev
311 314 self.filteredhash = scmutil.filteredhash(repo, self.tiprev)
312 315
313 316 duration = util.timer() - starttime
314 317 repo.ui.log('branchcache', 'updated %s branch cache in %.4f seconds\n',
315 318 repo.filtername, duration)
316 319
317 320 # Revision branch info cache
318 321
319 322 _rbcversion = '-v1'
320 323 _rbcnames = 'rbc-names' + _rbcversion
321 324 _rbcrevs = 'rbc-revs' + _rbcversion
322 325 # [4 byte hash prefix][4 byte branch name number with sign bit indicating open]
323 326 _rbcrecfmt = '>4sI'
324 327 _rbcrecsize = calcsize(_rbcrecfmt)
325 328 _rbcnodelen = 4
326 329 _rbcbranchidxmask = 0x7fffffff
327 330 _rbccloseflag = 0x80000000
328 331
329 332 class revbranchcache(object):
330 333 """Persistent cache, mapping from revision number to branch name and close.
331 334 This is a low level cache, independent of filtering.
332 335
333 336 Branch names are stored in rbc-names in internal encoding separated by 0.
334 337 rbc-names is append-only, and each branch name is only stored once and will
335 338 thus have a unique index.
336 339
337 340 The branch info for each revision is stored in rbc-revs as constant size
338 341 records. The whole file is read into memory, but it is only 'parsed' on
339 342 demand. The file is usually append-only but will be truncated if repo
340 343 modification is detected.
341 344 The record for each revision contains the first 4 bytes of the
342 345 corresponding node hash, and the record is only used if it still matches.
343 346 Even a completely trashed rbc-revs fill thus still give the right result
344 347 while converging towards full recovery ... assuming no incorrectly matching
345 348 node hashes.
346 349 The record also contains 4 bytes where 31 bits contains the index of the
347 350 branch and the last bit indicate that it is a branch close commit.
348 351 The usage pattern for rbc-revs is thus somewhat similar to 00changelog.i
349 352 and will grow with it but be 1/8th of its size.
350 353 """
351 354
352 355 def __init__(self, repo, readonly=True):
353 356 assert repo.filtername is None
354 357 self._repo = repo
355 358 self._names = [] # branch names in local encoding with static index
356 359 self._rbcrevs = bytearray()
357 360 self._rbcsnameslen = 0 # length of names read at _rbcsnameslen
358 361 try:
359 362 bndata = repo.cachevfs.read(_rbcnames)
360 363 self._rbcsnameslen = len(bndata) # for verification before writing
361 364 if bndata:
362 365 self._names = [encoding.tolocal(bn)
363 366 for bn in bndata.split('\0')]
364 367 except (IOError, OSError):
365 368 if readonly:
366 369 # don't try to use cache - fall back to the slow path
367 370 self.branchinfo = self._branchinfo
368 371
369 372 if self._names:
370 373 try:
371 374 data = repo.cachevfs.read(_rbcrevs)
372 375 self._rbcrevs[:] = data
373 376 except (IOError, OSError) as inst:
374 377 repo.ui.debug("couldn't read revision branch cache: %s\n" %
375 378 inst)
376 379 # remember number of good records on disk
377 380 self._rbcrevslen = min(len(self._rbcrevs) // _rbcrecsize,
378 381 len(repo.changelog))
379 382 if self._rbcrevslen == 0:
380 383 self._names = []
381 384 self._rbcnamescount = len(self._names) # number of names read at
382 385 # _rbcsnameslen
383 386 self._namesreverse = dict((b, r) for r, b in enumerate(self._names))
384 387
385 388 def _clear(self):
386 389 self._rbcsnameslen = 0
387 390 del self._names[:]
388 391 self._rbcnamescount = 0
389 392 self._namesreverse.clear()
390 393 self._rbcrevslen = len(self._repo.changelog)
391 394 self._rbcrevs = bytearray(self._rbcrevslen * _rbcrecsize)
392 395
393 396 def branchinfo(self, rev):
394 397 """Return branch name and close flag for rev, using and updating
395 398 persistent cache."""
396 399 changelog = self._repo.changelog
397 400 rbcrevidx = rev * _rbcrecsize
398 401
399 402 # avoid negative index, changelog.read(nullrev) is fast without cache
400 403 if rev == nullrev:
401 404 return changelog.branchinfo(rev)
402 405
403 406 # if requested rev isn't allocated, grow and cache the rev info
404 407 if len(self._rbcrevs) < rbcrevidx + _rbcrecsize:
405 408 return self._branchinfo(rev)
406 409
407 410 # fast path: extract data from cache, use it if node is matching
408 411 reponode = changelog.node(rev)[:_rbcnodelen]
409 412 cachenode, branchidx = unpack_from(
410 413 _rbcrecfmt, util.buffer(self._rbcrevs), rbcrevidx)
411 414 close = bool(branchidx & _rbccloseflag)
412 415 if close:
413 416 branchidx &= _rbcbranchidxmask
414 417 if cachenode == '\0\0\0\0':
415 418 pass
416 419 elif cachenode == reponode:
417 420 try:
418 421 return self._names[branchidx], close
419 422 except IndexError:
420 423 # recover from invalid reference to unknown branch
421 424 self._repo.ui.debug("referenced branch names not found"
422 425 " - rebuilding revision branch cache from scratch\n")
423 426 self._clear()
424 427 else:
425 428 # rev/node map has changed, invalidate the cache from here up
426 429 self._repo.ui.debug("history modification detected - truncating "
427 430 "revision branch cache to revision %d\n" % rev)
428 431 truncate = rbcrevidx + _rbcrecsize
429 432 del self._rbcrevs[truncate:]
430 433 self._rbcrevslen = min(self._rbcrevslen, truncate)
431 434
432 435 # fall back to slow path and make sure it will be written to disk
433 436 return self._branchinfo(rev)
434 437
435 438 def _branchinfo(self, rev):
436 439 """Retrieve branch info from changelog and update _rbcrevs"""
437 440 changelog = self._repo.changelog
438 441 b, close = changelog.branchinfo(rev)
439 442 if b in self._namesreverse:
440 443 branchidx = self._namesreverse[b]
441 444 else:
442 445 branchidx = len(self._names)
443 446 self._names.append(b)
444 447 self._namesreverse[b] = branchidx
445 448 reponode = changelog.node(rev)
446 449 if close:
447 450 branchidx |= _rbccloseflag
448 451 self._setcachedata(rev, reponode, branchidx)
449 452 return b, close
450 453
451 454 def _setcachedata(self, rev, node, branchidx):
452 455 """Writes the node's branch data to the in-memory cache data."""
453 456 if rev == nullrev:
454 457 return
455 458 rbcrevidx = rev * _rbcrecsize
456 459 if len(self._rbcrevs) < rbcrevidx + _rbcrecsize:
457 460 self._rbcrevs.extend('\0' *
458 461 (len(self._repo.changelog) * _rbcrecsize -
459 462 len(self._rbcrevs)))
460 463 pack_into(_rbcrecfmt, self._rbcrevs, rbcrevidx, node, branchidx)
461 464 self._rbcrevslen = min(self._rbcrevslen, rev)
462 465
463 466 tr = self._repo.currenttransaction()
464 467 if tr:
465 468 tr.addfinalize('write-revbranchcache', self.write)
466 469
467 470 def write(self, tr=None):
468 471 """Save branch cache if it is dirty."""
469 472 repo = self._repo
470 473 wlock = None
471 474 step = ''
472 475 try:
473 476 if self._rbcnamescount < len(self._names):
474 477 step = ' names'
475 478 wlock = repo.wlock(wait=False)
476 479 if self._rbcnamescount != 0:
477 480 f = repo.cachevfs.open(_rbcnames, 'ab')
478 481 if f.tell() == self._rbcsnameslen:
479 482 f.write('\0')
480 483 else:
481 484 f.close()
482 485 repo.ui.debug("%s changed - rewriting it\n" % _rbcnames)
483 486 self._rbcnamescount = 0
484 487 self._rbcrevslen = 0
485 488 if self._rbcnamescount == 0:
486 489 # before rewriting names, make sure references are removed
487 490 repo.cachevfs.unlinkpath(_rbcrevs, ignoremissing=True)
488 491 f = repo.cachevfs.open(_rbcnames, 'wb')
489 492 f.write('\0'.join(encoding.fromlocal(b)
490 493 for b in self._names[self._rbcnamescount:]))
491 494 self._rbcsnameslen = f.tell()
492 495 f.close()
493 496 self._rbcnamescount = len(self._names)
494 497
495 498 start = self._rbcrevslen * _rbcrecsize
496 499 if start != len(self._rbcrevs):
497 500 step = ''
498 501 if wlock is None:
499 502 wlock = repo.wlock(wait=False)
500 503 revs = min(len(repo.changelog),
501 504 len(self._rbcrevs) // _rbcrecsize)
502 505 f = repo.cachevfs.open(_rbcrevs, 'ab')
503 506 if f.tell() != start:
504 507 repo.ui.debug("truncating cache/%s to %d\n"
505 508 % (_rbcrevs, start))
506 509 f.seek(start)
507 510 if f.tell() != start:
508 511 start = 0
509 512 f.seek(start)
510 513 f.truncate()
511 514 end = revs * _rbcrecsize
512 515 f.write(self._rbcrevs[start:end])
513 516 f.close()
514 517 self._rbcrevslen = revs
515 518 except (IOError, OSError, error.Abort, error.LockError) as inst:
516 519 repo.ui.debug("couldn't write revision branch cache%s: %s\n"
517 520 % (step, inst))
518 521 finally:
519 522 if wlock is not None:
520 523 wlock.release()
@@ -1,5493 +1,5496 b''
1 1 # commands.py - command processing 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
8 8 from __future__ import absolute_import
9 9
10 10 import difflib
11 11 import errno
12 12 import os
13 13 import re
14 14 import sys
15 15
16 16 from .i18n import _
17 17 from .node import (
18 18 hex,
19 19 nullid,
20 20 nullrev,
21 21 short,
22 22 )
23 23 from . import (
24 24 archival,
25 25 bookmarks,
26 26 bundle2,
27 27 changegroup,
28 28 cmdutil,
29 29 copies,
30 30 debugcommands as debugcommandsmod,
31 31 destutil,
32 32 dirstateguard,
33 33 discovery,
34 34 encoding,
35 35 error,
36 36 exchange,
37 37 extensions,
38 38 formatter,
39 39 graphmod,
40 40 hbisect,
41 41 help,
42 42 hg,
43 43 lock as lockmod,
44 44 merge as mergemod,
45 45 obsolete,
46 46 patch,
47 47 phases,
48 48 pycompat,
49 49 rcutil,
50 50 registrar,
51 51 revsetlang,
52 52 scmutil,
53 53 server,
54 54 sshserver,
55 55 streamclone,
56 56 tags as tagsmod,
57 57 templatekw,
58 58 ui as uimod,
59 59 util,
60 60 )
61 61
62 62 release = lockmod.release
63 63
64 64 table = {}
65 65 table.update(debugcommandsmod.command._table)
66 66
67 67 command = registrar.command(table)
68 68
69 69 # common command options
70 70
71 71 globalopts = [
72 72 ('R', 'repository', '',
73 73 _('repository root directory or name of overlay bundle file'),
74 74 _('REPO')),
75 75 ('', 'cwd', '',
76 76 _('change working directory'), _('DIR')),
77 77 ('y', 'noninteractive', None,
78 78 _('do not prompt, automatically pick the first choice for all prompts')),
79 79 ('q', 'quiet', None, _('suppress output')),
80 80 ('v', 'verbose', None, _('enable additional output')),
81 81 ('', 'color', '',
82 82 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
83 83 # and should not be translated
84 84 _("when to colorize (boolean, always, auto, never, or debug)"),
85 85 _('TYPE')),
86 86 ('', 'config', [],
87 87 _('set/override config option (use \'section.name=value\')'),
88 88 _('CONFIG')),
89 89 ('', 'debug', None, _('enable debugging output')),
90 90 ('', 'debugger', None, _('start debugger')),
91 91 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
92 92 _('ENCODE')),
93 93 ('', 'encodingmode', encoding.encodingmode,
94 94 _('set the charset encoding mode'), _('MODE')),
95 95 ('', 'traceback', None, _('always print a traceback on exception')),
96 96 ('', 'time', None, _('time how long the command takes')),
97 97 ('', 'profile', None, _('print command execution profile')),
98 98 ('', 'version', None, _('output version information and exit')),
99 99 ('h', 'help', None, _('display help and exit')),
100 100 ('', 'hidden', False, _('consider hidden changesets')),
101 101 ('', 'pager', 'auto',
102 102 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
103 103 ]
104 104
105 105 dryrunopts = cmdutil.dryrunopts
106 106 remoteopts = cmdutil.remoteopts
107 107 walkopts = cmdutil.walkopts
108 108 commitopts = cmdutil.commitopts
109 109 commitopts2 = cmdutil.commitopts2
110 110 formatteropts = cmdutil.formatteropts
111 111 templateopts = cmdutil.templateopts
112 112 logopts = cmdutil.logopts
113 113 diffopts = cmdutil.diffopts
114 114 diffwsopts = cmdutil.diffwsopts
115 115 diffopts2 = cmdutil.diffopts2
116 116 mergetoolopts = cmdutil.mergetoolopts
117 117 similarityopts = cmdutil.similarityopts
118 118 subrepoopts = cmdutil.subrepoopts
119 119 debugrevlogopts = cmdutil.debugrevlogopts
120 120
121 121 # Commands start here, listed alphabetically
122 122
123 123 @command('^add',
124 124 walkopts + subrepoopts + dryrunopts,
125 125 _('[OPTION]... [FILE]...'),
126 126 inferrepo=True)
127 127 def add(ui, repo, *pats, **opts):
128 128 """add the specified files on the next commit
129 129
130 130 Schedule files to be version controlled and added to the
131 131 repository.
132 132
133 133 The files will be added to the repository at the next commit. To
134 134 undo an add before that, see :hg:`forget`.
135 135
136 136 If no names are given, add all files to the repository (except
137 137 files matching ``.hgignore``).
138 138
139 139 .. container:: verbose
140 140
141 141 Examples:
142 142
143 143 - New (unknown) files are added
144 144 automatically by :hg:`add`::
145 145
146 146 $ ls
147 147 foo.c
148 148 $ hg status
149 149 ? foo.c
150 150 $ hg add
151 151 adding foo.c
152 152 $ hg status
153 153 A foo.c
154 154
155 155 - Specific files to be added can be specified::
156 156
157 157 $ ls
158 158 bar.c foo.c
159 159 $ hg status
160 160 ? bar.c
161 161 ? foo.c
162 162 $ hg add bar.c
163 163 $ hg status
164 164 A bar.c
165 165 ? foo.c
166 166
167 167 Returns 0 if all files are successfully added.
168 168 """
169 169
170 170 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
171 171 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
172 172 return rejected and 1 or 0
173 173
174 174 @command('addremove',
175 175 similarityopts + subrepoopts + walkopts + dryrunopts,
176 176 _('[OPTION]... [FILE]...'),
177 177 inferrepo=True)
178 178 def addremove(ui, repo, *pats, **opts):
179 179 """add all new files, delete all missing files
180 180
181 181 Add all new files and remove all missing files from the
182 182 repository.
183 183
184 184 Unless names are given, new files are ignored if they match any of
185 185 the patterns in ``.hgignore``. As with add, these changes take
186 186 effect at the next commit.
187 187
188 188 Use the -s/--similarity option to detect renamed files. This
189 189 option takes a percentage between 0 (disabled) and 100 (files must
190 190 be identical) as its parameter. With a parameter greater than 0,
191 191 this compares every removed file with every added file and records
192 192 those similar enough as renames. Detecting renamed files this way
193 193 can be expensive. After using this option, :hg:`status -C` can be
194 194 used to check which files were identified as moved or renamed. If
195 195 not specified, -s/--similarity defaults to 100 and only renames of
196 196 identical files are detected.
197 197
198 198 .. container:: verbose
199 199
200 200 Examples:
201 201
202 202 - A number of files (bar.c and foo.c) are new,
203 203 while foobar.c has been removed (without using :hg:`remove`)
204 204 from the repository::
205 205
206 206 $ ls
207 207 bar.c foo.c
208 208 $ hg status
209 209 ! foobar.c
210 210 ? bar.c
211 211 ? foo.c
212 212 $ hg addremove
213 213 adding bar.c
214 214 adding foo.c
215 215 removing foobar.c
216 216 $ hg status
217 217 A bar.c
218 218 A foo.c
219 219 R foobar.c
220 220
221 221 - A file foobar.c was moved to foo.c without using :hg:`rename`.
222 222 Afterwards, it was edited slightly::
223 223
224 224 $ ls
225 225 foo.c
226 226 $ hg status
227 227 ! foobar.c
228 228 ? foo.c
229 229 $ hg addremove --similarity 90
230 230 removing foobar.c
231 231 adding foo.c
232 232 recording removal of foobar.c as rename to foo.c (94% similar)
233 233 $ hg status -C
234 234 A foo.c
235 235 foobar.c
236 236 R foobar.c
237 237
238 238 Returns 0 if all files are successfully added.
239 239 """
240 240 opts = pycompat.byteskwargs(opts)
241 241 try:
242 242 sim = float(opts.get('similarity') or 100)
243 243 except ValueError:
244 244 raise error.Abort(_('similarity must be a number'))
245 245 if sim < 0 or sim > 100:
246 246 raise error.Abort(_('similarity must be between 0 and 100'))
247 247 matcher = scmutil.match(repo[None], pats, opts)
248 248 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
249 249
250 250 @command('^annotate|blame',
251 251 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
252 252 ('', 'follow', None,
253 253 _('follow copies/renames and list the filename (DEPRECATED)')),
254 254 ('', 'no-follow', None, _("don't follow copies and renames")),
255 255 ('a', 'text', None, _('treat all files as text')),
256 256 ('u', 'user', None, _('list the author (long with -v)')),
257 257 ('f', 'file', None, _('list the filename')),
258 258 ('d', 'date', None, _('list the date (short with -q)')),
259 259 ('n', 'number', None, _('list the revision number (default)')),
260 260 ('c', 'changeset', None, _('list the changeset')),
261 261 ('l', 'line-number', None, _('show line number at the first appearance')),
262 262 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
263 263 ] + diffwsopts + walkopts + formatteropts,
264 264 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
265 265 inferrepo=True)
266 266 def annotate(ui, repo, *pats, **opts):
267 267 """show changeset information by line for each file
268 268
269 269 List changes in files, showing the revision id responsible for
270 270 each line.
271 271
272 272 This command is useful for discovering when a change was made and
273 273 by whom.
274 274
275 275 If you include --file, --user, or --date, the revision number is
276 276 suppressed unless you also include --number.
277 277
278 278 Without the -a/--text option, annotate will avoid processing files
279 279 it detects as binary. With -a, annotate will annotate the file
280 280 anyway, although the results will probably be neither useful
281 281 nor desirable.
282 282
283 283 Returns 0 on success.
284 284 """
285 285 opts = pycompat.byteskwargs(opts)
286 286 if not pats:
287 287 raise error.Abort(_('at least one filename or pattern is required'))
288 288
289 289 if opts.get('follow'):
290 290 # --follow is deprecated and now just an alias for -f/--file
291 291 # to mimic the behavior of Mercurial before version 1.5
292 292 opts['file'] = True
293 293
294 294 ctx = scmutil.revsingle(repo, opts.get('rev'))
295 295
296 296 rootfm = ui.formatter('annotate', opts)
297 297 if ui.quiet:
298 298 datefunc = util.shortdate
299 299 else:
300 300 datefunc = util.datestr
301 301 if ctx.rev() is None:
302 302 def hexfn(node):
303 303 if node is None:
304 304 return None
305 305 else:
306 306 return rootfm.hexfunc(node)
307 307 if opts.get('changeset'):
308 308 # omit "+" suffix which is appended to node hex
309 309 def formatrev(rev):
310 310 if rev is None:
311 311 return '%d' % ctx.p1().rev()
312 312 else:
313 313 return '%d' % rev
314 314 else:
315 315 def formatrev(rev):
316 316 if rev is None:
317 317 return '%d+' % ctx.p1().rev()
318 318 else:
319 319 return '%d ' % rev
320 320 def formathex(hex):
321 321 if hex is None:
322 322 return '%s+' % rootfm.hexfunc(ctx.p1().node())
323 323 else:
324 324 return '%s ' % hex
325 325 else:
326 326 hexfn = rootfm.hexfunc
327 327 formatrev = formathex = pycompat.bytestr
328 328
329 329 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
330 330 ('number', ' ', lambda x: x[0].rev(), formatrev),
331 331 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
332 332 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
333 333 ('file', ' ', lambda x: x[0].path(), str),
334 334 ('line_number', ':', lambda x: x[1], str),
335 335 ]
336 336 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
337 337
338 338 if (not opts.get('user') and not opts.get('changeset')
339 339 and not opts.get('date') and not opts.get('file')):
340 340 opts['number'] = True
341 341
342 342 linenumber = opts.get('line_number') is not None
343 343 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
344 344 raise error.Abort(_('at least one of -n/-c is required for -l'))
345 345
346 346 ui.pager('annotate')
347 347
348 348 if rootfm.isplain():
349 349 def makefunc(get, fmt):
350 350 return lambda x: fmt(get(x))
351 351 else:
352 352 def makefunc(get, fmt):
353 353 return get
354 354 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
355 355 if opts.get(op)]
356 356 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
357 357 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
358 358 if opts.get(op))
359 359
360 360 def bad(x, y):
361 361 raise error.Abort("%s: %s" % (x, y))
362 362
363 363 m = scmutil.match(ctx, pats, opts, badfn=bad)
364 364
365 365 follow = not opts.get('no_follow')
366 366 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
367 367 whitespace=True)
368 368 skiprevs = opts.get('skip')
369 369 if skiprevs:
370 370 skiprevs = scmutil.revrange(repo, skiprevs)
371 371
372 372 for abs in ctx.walk(m):
373 373 fctx = ctx[abs]
374 374 rootfm.startitem()
375 375 rootfm.data(abspath=abs, path=m.rel(abs))
376 376 if not opts.get('text') and fctx.isbinary():
377 377 rootfm.plain(_("%s: binary file\n")
378 378 % ((pats and m.rel(abs)) or abs))
379 379 continue
380 380
381 381 fm = rootfm.nested('lines')
382 382 lines = fctx.annotate(follow=follow, linenumber=linenumber,
383 383 skiprevs=skiprevs, diffopts=diffopts)
384 384 if not lines:
385 385 fm.end()
386 386 continue
387 387 formats = []
388 388 pieces = []
389 389
390 390 for f, sep in funcmap:
391 391 l = [f(n) for n, dummy in lines]
392 392 if fm.isplain():
393 393 sizes = [encoding.colwidth(x) for x in l]
394 394 ml = max(sizes)
395 395 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
396 396 else:
397 397 formats.append(['%s' for x in l])
398 398 pieces.append(l)
399 399
400 400 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
401 401 fm.startitem()
402 402 fm.write(fields, "".join(f), *p)
403 403 fm.write('line', ": %s", l[1])
404 404
405 405 if not lines[-1][1].endswith('\n'):
406 406 fm.plain('\n')
407 407 fm.end()
408 408
409 409 rootfm.end()
410 410
411 411 @command('archive',
412 412 [('', 'no-decode', None, _('do not pass files through decoders')),
413 413 ('p', 'prefix', '', _('directory prefix for files in archive'),
414 414 _('PREFIX')),
415 415 ('r', 'rev', '', _('revision to distribute'), _('REV')),
416 416 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
417 417 ] + subrepoopts + walkopts,
418 418 _('[OPTION]... DEST'))
419 419 def archive(ui, repo, dest, **opts):
420 420 '''create an unversioned archive of a repository revision
421 421
422 422 By default, the revision used is the parent of the working
423 423 directory; use -r/--rev to specify a different revision.
424 424
425 425 The archive type is automatically detected based on file
426 426 extension (to override, use -t/--type).
427 427
428 428 .. container:: verbose
429 429
430 430 Examples:
431 431
432 432 - create a zip file containing the 1.0 release::
433 433
434 434 hg archive -r 1.0 project-1.0.zip
435 435
436 436 - create a tarball excluding .hg files::
437 437
438 438 hg archive project.tar.gz -X ".hg*"
439 439
440 440 Valid types are:
441 441
442 442 :``files``: a directory full of files (default)
443 443 :``tar``: tar archive, uncompressed
444 444 :``tbz2``: tar archive, compressed using bzip2
445 445 :``tgz``: tar archive, compressed using gzip
446 446 :``uzip``: zip archive, uncompressed
447 447 :``zip``: zip archive, compressed using deflate
448 448
449 449 The exact name of the destination archive or directory is given
450 450 using a format string; see :hg:`help export` for details.
451 451
452 452 Each member added to an archive file has a directory prefix
453 453 prepended. Use -p/--prefix to specify a format string for the
454 454 prefix. The default is the basename of the archive, with suffixes
455 455 removed.
456 456
457 457 Returns 0 on success.
458 458 '''
459 459
460 460 opts = pycompat.byteskwargs(opts)
461 461 ctx = scmutil.revsingle(repo, opts.get('rev'))
462 462 if not ctx:
463 463 raise error.Abort(_('no working directory: please specify a revision'))
464 464 node = ctx.node()
465 465 dest = cmdutil.makefilename(repo, dest, node)
466 466 if os.path.realpath(dest) == repo.root:
467 467 raise error.Abort(_('repository root cannot be destination'))
468 468
469 469 kind = opts.get('type') or archival.guesskind(dest) or 'files'
470 470 prefix = opts.get('prefix')
471 471
472 472 if dest == '-':
473 473 if kind == 'files':
474 474 raise error.Abort(_('cannot archive plain files to stdout'))
475 475 dest = cmdutil.makefileobj(repo, dest)
476 476 if not prefix:
477 477 prefix = os.path.basename(repo.root) + '-%h'
478 478
479 479 prefix = cmdutil.makefilename(repo, prefix, node)
480 480 matchfn = scmutil.match(ctx, [], opts)
481 481 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
482 482 matchfn, prefix, subrepos=opts.get('subrepos'))
483 483
484 484 @command('backout',
485 485 [('', 'merge', None, _('merge with old dirstate parent after backout')),
486 486 ('', 'commit', None,
487 487 _('commit if no conflicts were encountered (DEPRECATED)')),
488 488 ('', 'no-commit', None, _('do not commit')),
489 489 ('', 'parent', '',
490 490 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
491 491 ('r', 'rev', '', _('revision to backout'), _('REV')),
492 492 ('e', 'edit', False, _('invoke editor on commit messages')),
493 493 ] + mergetoolopts + walkopts + commitopts + commitopts2,
494 494 _('[OPTION]... [-r] REV'))
495 495 def backout(ui, repo, node=None, rev=None, **opts):
496 496 '''reverse effect of earlier changeset
497 497
498 498 Prepare a new changeset with the effect of REV undone in the
499 499 current working directory. If no conflicts were encountered,
500 500 it will be committed immediately.
501 501
502 502 If REV is the parent of the working directory, then this new changeset
503 503 is committed automatically (unless --no-commit is specified).
504 504
505 505 .. note::
506 506
507 507 :hg:`backout` cannot be used to fix either an unwanted or
508 508 incorrect merge.
509 509
510 510 .. container:: verbose
511 511
512 512 Examples:
513 513
514 514 - Reverse the effect of the parent of the working directory.
515 515 This backout will be committed immediately::
516 516
517 517 hg backout -r .
518 518
519 519 - Reverse the effect of previous bad revision 23::
520 520
521 521 hg backout -r 23
522 522
523 523 - Reverse the effect of previous bad revision 23 and
524 524 leave changes uncommitted::
525 525
526 526 hg backout -r 23 --no-commit
527 527 hg commit -m "Backout revision 23"
528 528
529 529 By default, the pending changeset will have one parent,
530 530 maintaining a linear history. With --merge, the pending
531 531 changeset will instead have two parents: the old parent of the
532 532 working directory and a new child of REV that simply undoes REV.
533 533
534 534 Before version 1.7, the behavior without --merge was equivalent
535 535 to specifying --merge followed by :hg:`update --clean .` to
536 536 cancel the merge and leave the child of REV as a head to be
537 537 merged separately.
538 538
539 539 See :hg:`help dates` for a list of formats valid for -d/--date.
540 540
541 541 See :hg:`help revert` for a way to restore files to the state
542 542 of another revision.
543 543
544 544 Returns 0 on success, 1 if nothing to backout or there are unresolved
545 545 files.
546 546 '''
547 547 wlock = lock = None
548 548 try:
549 549 wlock = repo.wlock()
550 550 lock = repo.lock()
551 551 return _dobackout(ui, repo, node, rev, **opts)
552 552 finally:
553 553 release(lock, wlock)
554 554
555 555 def _dobackout(ui, repo, node=None, rev=None, **opts):
556 556 opts = pycompat.byteskwargs(opts)
557 557 if opts.get('commit') and opts.get('no_commit'):
558 558 raise error.Abort(_("cannot use --commit with --no-commit"))
559 559 if opts.get('merge') and opts.get('no_commit'):
560 560 raise error.Abort(_("cannot use --merge with --no-commit"))
561 561
562 562 if rev and node:
563 563 raise error.Abort(_("please specify just one revision"))
564 564
565 565 if not rev:
566 566 rev = node
567 567
568 568 if not rev:
569 569 raise error.Abort(_("please specify a revision to backout"))
570 570
571 571 date = opts.get('date')
572 572 if date:
573 573 opts['date'] = util.parsedate(date)
574 574
575 575 cmdutil.checkunfinished(repo)
576 576 cmdutil.bailifchanged(repo)
577 577 node = scmutil.revsingle(repo, rev).node()
578 578
579 579 op1, op2 = repo.dirstate.parents()
580 580 if not repo.changelog.isancestor(node, op1):
581 581 raise error.Abort(_('cannot backout change that is not an ancestor'))
582 582
583 583 p1, p2 = repo.changelog.parents(node)
584 584 if p1 == nullid:
585 585 raise error.Abort(_('cannot backout a change with no parents'))
586 586 if p2 != nullid:
587 587 if not opts.get('parent'):
588 588 raise error.Abort(_('cannot backout a merge changeset'))
589 589 p = repo.lookup(opts['parent'])
590 590 if p not in (p1, p2):
591 591 raise error.Abort(_('%s is not a parent of %s') %
592 592 (short(p), short(node)))
593 593 parent = p
594 594 else:
595 595 if opts.get('parent'):
596 596 raise error.Abort(_('cannot use --parent on non-merge changeset'))
597 597 parent = p1
598 598
599 599 # the backout should appear on the same branch
600 600 branch = repo.dirstate.branch()
601 601 bheads = repo.branchheads(branch)
602 602 rctx = scmutil.revsingle(repo, hex(parent))
603 603 if not opts.get('merge') and op1 != node:
604 604 dsguard = dirstateguard.dirstateguard(repo, 'backout')
605 605 try:
606 606 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
607 607 'backout')
608 608 stats = mergemod.update(repo, parent, True, True, node, False)
609 609 repo.setparents(op1, op2)
610 610 dsguard.close()
611 611 hg._showstats(repo, stats)
612 612 if stats[3]:
613 613 repo.ui.status(_("use 'hg resolve' to retry unresolved "
614 614 "file merges\n"))
615 615 return 1
616 616 finally:
617 617 ui.setconfig('ui', 'forcemerge', '', '')
618 618 lockmod.release(dsguard)
619 619 else:
620 620 hg.clean(repo, node, show_stats=False)
621 621 repo.dirstate.setbranch(branch)
622 622 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
623 623
624 624 if opts.get('no_commit'):
625 625 msg = _("changeset %s backed out, "
626 626 "don't forget to commit.\n")
627 627 ui.status(msg % short(node))
628 628 return 0
629 629
630 630 def commitfunc(ui, repo, message, match, opts):
631 631 editform = 'backout'
632 632 e = cmdutil.getcommiteditor(editform=editform,
633 633 **pycompat.strkwargs(opts))
634 634 if not message:
635 635 # we don't translate commit messages
636 636 message = "Backed out changeset %s" % short(node)
637 637 e = cmdutil.getcommiteditor(edit=True, editform=editform)
638 638 return repo.commit(message, opts.get('user'), opts.get('date'),
639 639 match, editor=e)
640 640 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
641 641 if not newnode:
642 642 ui.status(_("nothing changed\n"))
643 643 return 1
644 644 cmdutil.commitstatus(repo, newnode, branch, bheads)
645 645
646 646 def nice(node):
647 647 return '%d:%s' % (repo.changelog.rev(node), short(node))
648 648 ui.status(_('changeset %s backs out changeset %s\n') %
649 649 (nice(repo.changelog.tip()), nice(node)))
650 650 if opts.get('merge') and op1 != node:
651 651 hg.clean(repo, op1, show_stats=False)
652 652 ui.status(_('merging with changeset %s\n')
653 653 % nice(repo.changelog.tip()))
654 654 try:
655 655 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
656 656 'backout')
657 657 return hg.merge(repo, hex(repo.changelog.tip()))
658 658 finally:
659 659 ui.setconfig('ui', 'forcemerge', '', '')
660 660 return 0
661 661
662 662 @command('bisect',
663 663 [('r', 'reset', False, _('reset bisect state')),
664 664 ('g', 'good', False, _('mark changeset good')),
665 665 ('b', 'bad', False, _('mark changeset bad')),
666 666 ('s', 'skip', False, _('skip testing changeset')),
667 667 ('e', 'extend', False, _('extend the bisect range')),
668 668 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
669 669 ('U', 'noupdate', False, _('do not update to target'))],
670 670 _("[-gbsr] [-U] [-c CMD] [REV]"))
671 671 def bisect(ui, repo, rev=None, extra=None, command=None,
672 672 reset=None, good=None, bad=None, skip=None, extend=None,
673 673 noupdate=None):
674 674 """subdivision search of changesets
675 675
676 676 This command helps to find changesets which introduce problems. To
677 677 use, mark the earliest changeset you know exhibits the problem as
678 678 bad, then mark the latest changeset which is free from the problem
679 679 as good. Bisect will update your working directory to a revision
680 680 for testing (unless the -U/--noupdate option is specified). Once
681 681 you have performed tests, mark the working directory as good or
682 682 bad, and bisect will either update to another candidate changeset
683 683 or announce that it has found the bad revision.
684 684
685 685 As a shortcut, you can also use the revision argument to mark a
686 686 revision as good or bad without checking it out first.
687 687
688 688 If you supply a command, it will be used for automatic bisection.
689 689 The environment variable HG_NODE will contain the ID of the
690 690 changeset being tested. The exit status of the command will be
691 691 used to mark revisions as good or bad: status 0 means good, 125
692 692 means to skip the revision, 127 (command not found) will abort the
693 693 bisection, and any other non-zero exit status means the revision
694 694 is bad.
695 695
696 696 .. container:: verbose
697 697
698 698 Some examples:
699 699
700 700 - start a bisection with known bad revision 34, and good revision 12::
701 701
702 702 hg bisect --bad 34
703 703 hg bisect --good 12
704 704
705 705 - advance the current bisection by marking current revision as good or
706 706 bad::
707 707
708 708 hg bisect --good
709 709 hg bisect --bad
710 710
711 711 - mark the current revision, or a known revision, to be skipped (e.g. if
712 712 that revision is not usable because of another issue)::
713 713
714 714 hg bisect --skip
715 715 hg bisect --skip 23
716 716
717 717 - skip all revisions that do not touch directories ``foo`` or ``bar``::
718 718
719 719 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
720 720
721 721 - forget the current bisection::
722 722
723 723 hg bisect --reset
724 724
725 725 - use 'make && make tests' to automatically find the first broken
726 726 revision::
727 727
728 728 hg bisect --reset
729 729 hg bisect --bad 34
730 730 hg bisect --good 12
731 731 hg bisect --command "make && make tests"
732 732
733 733 - see all changesets whose states are already known in the current
734 734 bisection::
735 735
736 736 hg log -r "bisect(pruned)"
737 737
738 738 - see the changeset currently being bisected (especially useful
739 739 if running with -U/--noupdate)::
740 740
741 741 hg log -r "bisect(current)"
742 742
743 743 - see all changesets that took part in the current bisection::
744 744
745 745 hg log -r "bisect(range)"
746 746
747 747 - you can even get a nice graph::
748 748
749 749 hg log --graph -r "bisect(range)"
750 750
751 751 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
752 752
753 753 Returns 0 on success.
754 754 """
755 755 # backward compatibility
756 756 if rev in "good bad reset init".split():
757 757 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
758 758 cmd, rev, extra = rev, extra, None
759 759 if cmd == "good":
760 760 good = True
761 761 elif cmd == "bad":
762 762 bad = True
763 763 else:
764 764 reset = True
765 765 elif extra:
766 766 raise error.Abort(_('incompatible arguments'))
767 767
768 768 incompatibles = {
769 769 '--bad': bad,
770 770 '--command': bool(command),
771 771 '--extend': extend,
772 772 '--good': good,
773 773 '--reset': reset,
774 774 '--skip': skip,
775 775 }
776 776
777 777 enabled = [x for x in incompatibles if incompatibles[x]]
778 778
779 779 if len(enabled) > 1:
780 780 raise error.Abort(_('%s and %s are incompatible') %
781 781 tuple(sorted(enabled)[0:2]))
782 782
783 783 if reset:
784 784 hbisect.resetstate(repo)
785 785 return
786 786
787 787 state = hbisect.load_state(repo)
788 788
789 789 # update state
790 790 if good or bad or skip:
791 791 if rev:
792 792 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
793 793 else:
794 794 nodes = [repo.lookup('.')]
795 795 if good:
796 796 state['good'] += nodes
797 797 elif bad:
798 798 state['bad'] += nodes
799 799 elif skip:
800 800 state['skip'] += nodes
801 801 hbisect.save_state(repo, state)
802 802 if not (state['good'] and state['bad']):
803 803 return
804 804
805 805 def mayupdate(repo, node, show_stats=True):
806 806 """common used update sequence"""
807 807 if noupdate:
808 808 return
809 809 cmdutil.checkunfinished(repo)
810 810 cmdutil.bailifchanged(repo)
811 811 return hg.clean(repo, node, show_stats=show_stats)
812 812
813 813 displayer = cmdutil.show_changeset(ui, repo, {})
814 814
815 815 if command:
816 816 changesets = 1
817 817 if noupdate:
818 818 try:
819 819 node = state['current'][0]
820 820 except LookupError:
821 821 raise error.Abort(_('current bisect revision is unknown - '
822 822 'start a new bisect to fix'))
823 823 else:
824 824 node, p2 = repo.dirstate.parents()
825 825 if p2 != nullid:
826 826 raise error.Abort(_('current bisect revision is a merge'))
827 827 if rev:
828 828 node = repo[scmutil.revsingle(repo, rev, node)].node()
829 829 try:
830 830 while changesets:
831 831 # update state
832 832 state['current'] = [node]
833 833 hbisect.save_state(repo, state)
834 834 status = ui.system(command, environ={'HG_NODE': hex(node)},
835 835 blockedtag='bisect_check')
836 836 if status == 125:
837 837 transition = "skip"
838 838 elif status == 0:
839 839 transition = "good"
840 840 # status < 0 means process was killed
841 841 elif status == 127:
842 842 raise error.Abort(_("failed to execute %s") % command)
843 843 elif status < 0:
844 844 raise error.Abort(_("%s killed") % command)
845 845 else:
846 846 transition = "bad"
847 847 state[transition].append(node)
848 848 ctx = repo[node]
849 849 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
850 850 hbisect.checkstate(state)
851 851 # bisect
852 852 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
853 853 # update to next check
854 854 node = nodes[0]
855 855 mayupdate(repo, node, show_stats=False)
856 856 finally:
857 857 state['current'] = [node]
858 858 hbisect.save_state(repo, state)
859 859 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
860 860 return
861 861
862 862 hbisect.checkstate(state)
863 863
864 864 # actually bisect
865 865 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
866 866 if extend:
867 867 if not changesets:
868 868 extendnode = hbisect.extendrange(repo, state, nodes, good)
869 869 if extendnode is not None:
870 870 ui.write(_("Extending search to changeset %d:%s\n")
871 871 % (extendnode.rev(), extendnode))
872 872 state['current'] = [extendnode.node()]
873 873 hbisect.save_state(repo, state)
874 874 return mayupdate(repo, extendnode.node())
875 875 raise error.Abort(_("nothing to extend"))
876 876
877 877 if changesets == 0:
878 878 hbisect.printresult(ui, repo, state, displayer, nodes, good)
879 879 else:
880 880 assert len(nodes) == 1 # only a single node can be tested next
881 881 node = nodes[0]
882 882 # compute the approximate number of remaining tests
883 883 tests, size = 0, 2
884 884 while size <= changesets:
885 885 tests, size = tests + 1, size * 2
886 886 rev = repo.changelog.rev(node)
887 887 ui.write(_("Testing changeset %d:%s "
888 888 "(%d changesets remaining, ~%d tests)\n")
889 889 % (rev, short(node), changesets, tests))
890 890 state['current'] = [node]
891 891 hbisect.save_state(repo, state)
892 892 return mayupdate(repo, node)
893 893
894 894 @command('bookmarks|bookmark',
895 895 [('f', 'force', False, _('force')),
896 896 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
897 897 ('d', 'delete', False, _('delete a given bookmark')),
898 898 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
899 899 ('i', 'inactive', False, _('mark a bookmark inactive')),
900 900 ] + formatteropts,
901 901 _('hg bookmarks [OPTIONS]... [NAME]...'))
902 902 def bookmark(ui, repo, *names, **opts):
903 903 '''create a new bookmark or list existing bookmarks
904 904
905 905 Bookmarks are labels on changesets to help track lines of development.
906 906 Bookmarks are unversioned and can be moved, renamed and deleted.
907 907 Deleting or moving a bookmark has no effect on the associated changesets.
908 908
909 909 Creating or updating to a bookmark causes it to be marked as 'active'.
910 910 The active bookmark is indicated with a '*'.
911 911 When a commit is made, the active bookmark will advance to the new commit.
912 912 A plain :hg:`update` will also advance an active bookmark, if possible.
913 913 Updating away from a bookmark will cause it to be deactivated.
914 914
915 915 Bookmarks can be pushed and pulled between repositories (see
916 916 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
917 917 diverged, a new 'divergent bookmark' of the form 'name@path' will
918 918 be created. Using :hg:`merge` will resolve the divergence.
919 919
920 920 Specifying bookmark as '.' to -m or -d options is equivalent to specifying
921 921 the active bookmark's name.
922 922
923 923 A bookmark named '@' has the special property that :hg:`clone` will
924 924 check it out by default if it exists.
925 925
926 926 .. container:: verbose
927 927
928 928 Examples:
929 929
930 930 - create an active bookmark for a new line of development::
931 931
932 932 hg book new-feature
933 933
934 934 - create an inactive bookmark as a place marker::
935 935
936 936 hg book -i reviewed
937 937
938 938 - create an inactive bookmark on another changeset::
939 939
940 940 hg book -r .^ tested
941 941
942 942 - rename bookmark turkey to dinner::
943 943
944 944 hg book -m turkey dinner
945 945
946 946 - move the '@' bookmark from another branch::
947 947
948 948 hg book -f @
949 949 '''
950 950 force = opts.get(r'force')
951 951 rev = opts.get(r'rev')
952 952 delete = opts.get(r'delete')
953 953 rename = opts.get(r'rename')
954 954 inactive = opts.get(r'inactive')
955 955
956 956 if delete and rename:
957 957 raise error.Abort(_("--delete and --rename are incompatible"))
958 958 if delete and rev:
959 959 raise error.Abort(_("--rev is incompatible with --delete"))
960 960 if rename and rev:
961 961 raise error.Abort(_("--rev is incompatible with --rename"))
962 962 if not names and (delete or rev):
963 963 raise error.Abort(_("bookmark name required"))
964 964
965 965 if delete or rename or names or inactive:
966 966 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
967 967 if delete:
968 968 names = pycompat.maplist(repo._bookmarks.expandname, names)
969 969 bookmarks.delete(repo, tr, names)
970 970 elif rename:
971 971 if not names:
972 972 raise error.Abort(_("new bookmark name required"))
973 973 elif len(names) > 1:
974 974 raise error.Abort(_("only one new bookmark name allowed"))
975 975 rename = repo._bookmarks.expandname(rename)
976 976 bookmarks.rename(repo, tr, rename, names[0], force, inactive)
977 977 elif names:
978 978 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
979 979 elif inactive:
980 980 if len(repo._bookmarks) == 0:
981 981 ui.status(_("no bookmarks set\n"))
982 982 elif not repo._activebookmark:
983 983 ui.status(_("no active bookmark\n"))
984 984 else:
985 985 bookmarks.deactivate(repo)
986 986 else: # show bookmarks
987 987 bookmarks.printbookmarks(ui, repo, **opts)
988 988
989 989 @command('branch',
990 990 [('f', 'force', None,
991 991 _('set branch name even if it shadows an existing branch')),
992 992 ('C', 'clean', None, _('reset branch name to parent branch name'))],
993 993 _('[-fC] [NAME]'))
994 994 def branch(ui, repo, label=None, **opts):
995 995 """set or show the current branch name
996 996
997 997 .. note::
998 998
999 999 Branch names are permanent and global. Use :hg:`bookmark` to create a
1000 1000 light-weight bookmark instead. See :hg:`help glossary` for more
1001 1001 information about named branches and bookmarks.
1002 1002
1003 1003 With no argument, show the current branch name. With one argument,
1004 1004 set the working directory branch name (the branch will not exist
1005 1005 in the repository until the next commit). Standard practice
1006 1006 recommends that primary development take place on the 'default'
1007 1007 branch.
1008 1008
1009 1009 Unless -f/--force is specified, branch will not let you set a
1010 1010 branch name that already exists.
1011 1011
1012 1012 Use -C/--clean to reset the working directory branch to that of
1013 1013 the parent of the working directory, negating a previous branch
1014 1014 change.
1015 1015
1016 1016 Use the command :hg:`update` to switch to an existing branch. Use
1017 1017 :hg:`commit --close-branch` to mark this branch head as closed.
1018 1018 When all heads of a branch are closed, the branch will be
1019 1019 considered closed.
1020 1020
1021 1021 Returns 0 on success.
1022 1022 """
1023 1023 opts = pycompat.byteskwargs(opts)
1024 1024 if label:
1025 1025 label = label.strip()
1026 1026
1027 1027 if not opts.get('clean') and not label:
1028 1028 ui.write("%s\n" % repo.dirstate.branch())
1029 1029 return
1030 1030
1031 1031 with repo.wlock():
1032 1032 if opts.get('clean'):
1033 1033 label = repo[None].p1().branch()
1034 1034 repo.dirstate.setbranch(label)
1035 1035 ui.status(_('reset working directory to branch %s\n') % label)
1036 1036 elif label:
1037 1037 if not opts.get('force') and label in repo.branchmap():
1038 1038 if label not in [p.branch() for p in repo[None].parents()]:
1039 1039 raise error.Abort(_('a branch of the same name already'
1040 1040 ' exists'),
1041 1041 # i18n: "it" refers to an existing branch
1042 1042 hint=_("use 'hg update' to switch to it"))
1043 1043 scmutil.checknewlabel(repo, label, 'branch')
1044 1044 repo.dirstate.setbranch(label)
1045 1045 ui.status(_('marked working directory as branch %s\n') % label)
1046 1046
1047 1047 # find any open named branches aside from default
1048 1048 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1049 1049 if n != "default" and not c]
1050 1050 if not others:
1051 1051 ui.status(_('(branches are permanent and global, '
1052 1052 'did you want a bookmark?)\n'))
1053 1053
1054 1054 @command('branches',
1055 1055 [('a', 'active', False,
1056 1056 _('show only branches that have unmerged heads (DEPRECATED)')),
1057 1057 ('c', 'closed', False, _('show normal and closed branches')),
1058 1058 ] + formatteropts,
1059 1059 _('[-c]'))
1060 1060 def branches(ui, repo, active=False, closed=False, **opts):
1061 1061 """list repository named branches
1062 1062
1063 1063 List the repository's named branches, indicating which ones are
1064 1064 inactive. If -c/--closed is specified, also list branches which have
1065 1065 been marked closed (see :hg:`commit --close-branch`).
1066 1066
1067 1067 Use the command :hg:`update` to switch to an existing branch.
1068 1068
1069 1069 Returns 0.
1070 1070 """
1071 1071
1072 1072 opts = pycompat.byteskwargs(opts)
1073 1073 ui.pager('branches')
1074 1074 fm = ui.formatter('branches', opts)
1075 1075 hexfunc = fm.hexfunc
1076 1076
1077 1077 allheads = set(repo.heads())
1078 1078 branches = []
1079 1079 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1080 isactive = not isclosed and bool(set(heads) & allheads)
1080 isactive = False
1081 if not isclosed:
1082 openheads = set(repo.branchmap().iteropen(heads))
1083 isactive = bool(openheads & allheads)
1081 1084 branches.append((tag, repo[tip], isactive, not isclosed))
1082 1085 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1083 1086 reverse=True)
1084 1087
1085 1088 for tag, ctx, isactive, isopen in branches:
1086 1089 if active and not isactive:
1087 1090 continue
1088 1091 if isactive:
1089 1092 label = 'branches.active'
1090 1093 notice = ''
1091 1094 elif not isopen:
1092 1095 if not closed:
1093 1096 continue
1094 1097 label = 'branches.closed'
1095 1098 notice = _(' (closed)')
1096 1099 else:
1097 1100 label = 'branches.inactive'
1098 1101 notice = _(' (inactive)')
1099 1102 current = (tag == repo.dirstate.branch())
1100 1103 if current:
1101 1104 label = 'branches.current'
1102 1105
1103 1106 fm.startitem()
1104 1107 fm.write('branch', '%s', tag, label=label)
1105 1108 rev = ctx.rev()
1106 1109 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1107 1110 fmt = ' ' * padsize + ' %d:%s'
1108 1111 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1109 1112 label='log.changeset changeset.%s' % ctx.phasestr())
1110 1113 fm.context(ctx=ctx)
1111 1114 fm.data(active=isactive, closed=not isopen, current=current)
1112 1115 if not ui.quiet:
1113 1116 fm.plain(notice)
1114 1117 fm.plain('\n')
1115 1118 fm.end()
1116 1119
1117 1120 @command('bundle',
1118 1121 [('f', 'force', None, _('run even when the destination is unrelated')),
1119 1122 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1120 1123 _('REV')),
1121 1124 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1122 1125 _('BRANCH')),
1123 1126 ('', 'base', [],
1124 1127 _('a base changeset assumed to be available at the destination'),
1125 1128 _('REV')),
1126 1129 ('a', 'all', None, _('bundle all changesets in the repository')),
1127 1130 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1128 1131 ] + remoteopts,
1129 1132 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1130 1133 def bundle(ui, repo, fname, dest=None, **opts):
1131 1134 """create a bundle file
1132 1135
1133 1136 Generate a bundle file containing data to be added to a repository.
1134 1137
1135 1138 To create a bundle containing all changesets, use -a/--all
1136 1139 (or --base null). Otherwise, hg assumes the destination will have
1137 1140 all the nodes you specify with --base parameters. Otherwise, hg
1138 1141 will assume the repository has all the nodes in destination, or
1139 1142 default-push/default if no destination is specified.
1140 1143
1141 1144 You can change bundle format with the -t/--type option. See
1142 1145 :hg:`help bundlespec` for documentation on this format. By default,
1143 1146 the most appropriate format is used and compression defaults to
1144 1147 bzip2.
1145 1148
1146 1149 The bundle file can then be transferred using conventional means
1147 1150 and applied to another repository with the unbundle or pull
1148 1151 command. This is useful when direct push and pull are not
1149 1152 available or when exporting an entire repository is undesirable.
1150 1153
1151 1154 Applying bundles preserves all changeset contents including
1152 1155 permissions, copy/rename information, and revision history.
1153 1156
1154 1157 Returns 0 on success, 1 if no changes found.
1155 1158 """
1156 1159 opts = pycompat.byteskwargs(opts)
1157 1160 revs = None
1158 1161 if 'rev' in opts:
1159 1162 revstrings = opts['rev']
1160 1163 revs = scmutil.revrange(repo, revstrings)
1161 1164 if revstrings and not revs:
1162 1165 raise error.Abort(_('no commits to bundle'))
1163 1166
1164 1167 bundletype = opts.get('type', 'bzip2').lower()
1165 1168 try:
1166 1169 bcompression, cgversion, params = exchange.parsebundlespec(
1167 1170 repo, bundletype, strict=False)
1168 1171 except error.UnsupportedBundleSpecification as e:
1169 1172 raise error.Abort(str(e),
1170 1173 hint=_("see 'hg help bundlespec' for supported "
1171 1174 "values for --type"))
1172 1175
1173 1176 # Packed bundles are a pseudo bundle format for now.
1174 1177 if cgversion == 's1':
1175 1178 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1176 1179 hint=_("use 'hg debugcreatestreamclonebundle'"))
1177 1180
1178 1181 if opts.get('all'):
1179 1182 if dest:
1180 1183 raise error.Abort(_("--all is incompatible with specifying "
1181 1184 "a destination"))
1182 1185 if opts.get('base'):
1183 1186 ui.warn(_("ignoring --base because --all was specified\n"))
1184 1187 base = ['null']
1185 1188 else:
1186 1189 base = scmutil.revrange(repo, opts.get('base'))
1187 1190 if cgversion not in changegroup.supportedoutgoingversions(repo):
1188 1191 raise error.Abort(_("repository does not support bundle version %s") %
1189 1192 cgversion)
1190 1193
1191 1194 if base:
1192 1195 if dest:
1193 1196 raise error.Abort(_("--base is incompatible with specifying "
1194 1197 "a destination"))
1195 1198 common = [repo.lookup(rev) for rev in base]
1196 1199 heads = revs and map(repo.lookup, revs) or None
1197 1200 outgoing = discovery.outgoing(repo, common, heads)
1198 1201 else:
1199 1202 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1200 1203 dest, branches = hg.parseurl(dest, opts.get('branch'))
1201 1204 other = hg.peer(repo, opts, dest)
1202 1205 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1203 1206 heads = revs and map(repo.lookup, revs) or revs
1204 1207 outgoing = discovery.findcommonoutgoing(repo, other,
1205 1208 onlyheads=heads,
1206 1209 force=opts.get('force'),
1207 1210 portable=True)
1208 1211
1209 1212 if not outgoing.missing:
1210 1213 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1211 1214 return 1
1212 1215
1213 1216 if cgversion == '01': #bundle1
1214 1217 if bcompression is None:
1215 1218 bcompression = 'UN'
1216 1219 bversion = 'HG10' + bcompression
1217 1220 bcompression = None
1218 1221 elif cgversion in ('02', '03'):
1219 1222 bversion = 'HG20'
1220 1223 else:
1221 1224 raise error.ProgrammingError(
1222 1225 'bundle: unexpected changegroup version %s' % cgversion)
1223 1226
1224 1227 # TODO compression options should be derived from bundlespec parsing.
1225 1228 # This is a temporary hack to allow adjusting bundle compression
1226 1229 # level without a) formalizing the bundlespec changes to declare it
1227 1230 # b) introducing a command flag.
1228 1231 compopts = {}
1229 1232 complevel = ui.configint('experimental', 'bundlecomplevel')
1230 1233 if complevel is not None:
1231 1234 compopts['level'] = complevel
1232 1235
1233 1236
1234 1237 contentopts = {'cg.version': cgversion}
1235 1238 if repo.ui.configbool('experimental', 'stabilization.bundle-obsmarker'):
1236 1239 contentopts['obsolescence'] = True
1237 1240 if repo.ui.configbool('experimental', 'bundle-phases'):
1238 1241 contentopts['phases'] = True
1239 1242 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1240 1243 contentopts, compression=bcompression,
1241 1244 compopts=compopts)
1242 1245
1243 1246 @command('cat',
1244 1247 [('o', 'output', '',
1245 1248 _('print output to file with formatted name'), _('FORMAT')),
1246 1249 ('r', 'rev', '', _('print the given revision'), _('REV')),
1247 1250 ('', 'decode', None, _('apply any matching decode filter')),
1248 1251 ] + walkopts + formatteropts,
1249 1252 _('[OPTION]... FILE...'),
1250 1253 inferrepo=True)
1251 1254 def cat(ui, repo, file1, *pats, **opts):
1252 1255 """output the current or given revision of files
1253 1256
1254 1257 Print the specified files as they were at the given revision. If
1255 1258 no revision is given, the parent of the working directory is used.
1256 1259
1257 1260 Output may be to a file, in which case the name of the file is
1258 1261 given using a format string. The formatting rules as follows:
1259 1262
1260 1263 :``%%``: literal "%" character
1261 1264 :``%s``: basename of file being printed
1262 1265 :``%d``: dirname of file being printed, or '.' if in repository root
1263 1266 :``%p``: root-relative path name of file being printed
1264 1267 :``%H``: changeset hash (40 hexadecimal digits)
1265 1268 :``%R``: changeset revision number
1266 1269 :``%h``: short-form changeset hash (12 hexadecimal digits)
1267 1270 :``%r``: zero-padded changeset revision number
1268 1271 :``%b``: basename of the exporting repository
1269 1272
1270 1273 Returns 0 on success.
1271 1274 """
1272 1275 ctx = scmutil.revsingle(repo, opts.get('rev'))
1273 1276 m = scmutil.match(ctx, (file1,) + pats, opts)
1274 1277 fntemplate = opts.pop('output', '')
1275 1278 if cmdutil.isstdiofilename(fntemplate):
1276 1279 fntemplate = ''
1277 1280
1278 1281 if fntemplate:
1279 1282 fm = formatter.nullformatter(ui, 'cat')
1280 1283 else:
1281 1284 ui.pager('cat')
1282 1285 fm = ui.formatter('cat', opts)
1283 1286 with fm:
1284 1287 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '', **opts)
1285 1288
1286 1289 @command('^clone',
1287 1290 [('U', 'noupdate', None, _('the clone will include an empty working '
1288 1291 'directory (only a repository)')),
1289 1292 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1290 1293 _('REV')),
1291 1294 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1292 1295 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1293 1296 ('', 'pull', None, _('use pull protocol to copy metadata')),
1294 1297 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1295 1298 ] + remoteopts,
1296 1299 _('[OPTION]... SOURCE [DEST]'),
1297 1300 norepo=True)
1298 1301 def clone(ui, source, dest=None, **opts):
1299 1302 """make a copy of an existing repository
1300 1303
1301 1304 Create a copy of an existing repository in a new directory.
1302 1305
1303 1306 If no destination directory name is specified, it defaults to the
1304 1307 basename of the source.
1305 1308
1306 1309 The location of the source is added to the new repository's
1307 1310 ``.hg/hgrc`` file, as the default to be used for future pulls.
1308 1311
1309 1312 Only local paths and ``ssh://`` URLs are supported as
1310 1313 destinations. For ``ssh://`` destinations, no working directory or
1311 1314 ``.hg/hgrc`` will be created on the remote side.
1312 1315
1313 1316 If the source repository has a bookmark called '@' set, that
1314 1317 revision will be checked out in the new repository by default.
1315 1318
1316 1319 To check out a particular version, use -u/--update, or
1317 1320 -U/--noupdate to create a clone with no working directory.
1318 1321
1319 1322 To pull only a subset of changesets, specify one or more revisions
1320 1323 identifiers with -r/--rev or branches with -b/--branch. The
1321 1324 resulting clone will contain only the specified changesets and
1322 1325 their ancestors. These options (or 'clone src#rev dest') imply
1323 1326 --pull, even for local source repositories.
1324 1327
1325 1328 .. note::
1326 1329
1327 1330 Specifying a tag will include the tagged changeset but not the
1328 1331 changeset containing the tag.
1329 1332
1330 1333 .. container:: verbose
1331 1334
1332 1335 For efficiency, hardlinks are used for cloning whenever the
1333 1336 source and destination are on the same filesystem (note this
1334 1337 applies only to the repository data, not to the working
1335 1338 directory). Some filesystems, such as AFS, implement hardlinking
1336 1339 incorrectly, but do not report errors. In these cases, use the
1337 1340 --pull option to avoid hardlinking.
1338 1341
1339 1342 In some cases, you can clone repositories and the working
1340 1343 directory using full hardlinks with ::
1341 1344
1342 1345 $ cp -al REPO REPOCLONE
1343 1346
1344 1347 This is the fastest way to clone, but it is not always safe. The
1345 1348 operation is not atomic (making sure REPO is not modified during
1346 1349 the operation is up to you) and you have to make sure your
1347 1350 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1348 1351 so). Also, this is not compatible with certain extensions that
1349 1352 place their metadata under the .hg directory, such as mq.
1350 1353
1351 1354 Mercurial will update the working directory to the first applicable
1352 1355 revision from this list:
1353 1356
1354 1357 a) null if -U or the source repository has no changesets
1355 1358 b) if -u . and the source repository is local, the first parent of
1356 1359 the source repository's working directory
1357 1360 c) the changeset specified with -u (if a branch name, this means the
1358 1361 latest head of that branch)
1359 1362 d) the changeset specified with -r
1360 1363 e) the tipmost head specified with -b
1361 1364 f) the tipmost head specified with the url#branch source syntax
1362 1365 g) the revision marked with the '@' bookmark, if present
1363 1366 h) the tipmost head of the default branch
1364 1367 i) tip
1365 1368
1366 1369 When cloning from servers that support it, Mercurial may fetch
1367 1370 pre-generated data from a server-advertised URL. When this is done,
1368 1371 hooks operating on incoming changesets and changegroups may fire twice,
1369 1372 once for the bundle fetched from the URL and another for any additional
1370 1373 data not fetched from this URL. In addition, if an error occurs, the
1371 1374 repository may be rolled back to a partial clone. This behavior may
1372 1375 change in future releases. See :hg:`help -e clonebundles` for more.
1373 1376
1374 1377 Examples:
1375 1378
1376 1379 - clone a remote repository to a new directory named hg/::
1377 1380
1378 1381 hg clone https://www.mercurial-scm.org/repo/hg/
1379 1382
1380 1383 - create a lightweight local clone::
1381 1384
1382 1385 hg clone project/ project-feature/
1383 1386
1384 1387 - clone from an absolute path on an ssh server (note double-slash)::
1385 1388
1386 1389 hg clone ssh://user@server//home/projects/alpha/
1387 1390
1388 1391 - do a high-speed clone over a LAN while checking out a
1389 1392 specified version::
1390 1393
1391 1394 hg clone --uncompressed http://server/repo -u 1.5
1392 1395
1393 1396 - create a repository without changesets after a particular revision::
1394 1397
1395 1398 hg clone -r 04e544 experimental/ good/
1396 1399
1397 1400 - clone (and track) a particular named branch::
1398 1401
1399 1402 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1400 1403
1401 1404 See :hg:`help urls` for details on specifying URLs.
1402 1405
1403 1406 Returns 0 on success.
1404 1407 """
1405 1408 opts = pycompat.byteskwargs(opts)
1406 1409 if opts.get('noupdate') and opts.get('updaterev'):
1407 1410 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1408 1411
1409 1412 r = hg.clone(ui, opts, source, dest,
1410 1413 pull=opts.get('pull'),
1411 1414 stream=opts.get('uncompressed'),
1412 1415 rev=opts.get('rev'),
1413 1416 update=opts.get('updaterev') or not opts.get('noupdate'),
1414 1417 branch=opts.get('branch'),
1415 1418 shareopts=opts.get('shareopts'))
1416 1419
1417 1420 return r is None
1418 1421
1419 1422 @command('^commit|ci',
1420 1423 [('A', 'addremove', None,
1421 1424 _('mark new/missing files as added/removed before committing')),
1422 1425 ('', 'close-branch', None,
1423 1426 _('mark a branch head as closed')),
1424 1427 ('', 'amend', None, _('amend the parent of the working directory')),
1425 1428 ('s', 'secret', None, _('use the secret phase for committing')),
1426 1429 ('e', 'edit', None, _('invoke editor on commit messages')),
1427 1430 ('i', 'interactive', None, _('use interactive mode')),
1428 1431 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1429 1432 _('[OPTION]... [FILE]...'),
1430 1433 inferrepo=True)
1431 1434 def commit(ui, repo, *pats, **opts):
1432 1435 """commit the specified files or all outstanding changes
1433 1436
1434 1437 Commit changes to the given files into the repository. Unlike a
1435 1438 centralized SCM, this operation is a local operation. See
1436 1439 :hg:`push` for a way to actively distribute your changes.
1437 1440
1438 1441 If a list of files is omitted, all changes reported by :hg:`status`
1439 1442 will be committed.
1440 1443
1441 1444 If you are committing the result of a merge, do not provide any
1442 1445 filenames or -I/-X filters.
1443 1446
1444 1447 If no commit message is specified, Mercurial starts your
1445 1448 configured editor where you can enter a message. In case your
1446 1449 commit fails, you will find a backup of your message in
1447 1450 ``.hg/last-message.txt``.
1448 1451
1449 1452 The --close-branch flag can be used to mark the current branch
1450 1453 head closed. When all heads of a branch are closed, the branch
1451 1454 will be considered closed and no longer listed.
1452 1455
1453 1456 The --amend flag can be used to amend the parent of the
1454 1457 working directory with a new commit that contains the changes
1455 1458 in the parent in addition to those currently reported by :hg:`status`,
1456 1459 if there are any. The old commit is stored in a backup bundle in
1457 1460 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1458 1461 on how to restore it).
1459 1462
1460 1463 Message, user and date are taken from the amended commit unless
1461 1464 specified. When a message isn't specified on the command line,
1462 1465 the editor will open with the message of the amended commit.
1463 1466
1464 1467 It is not possible to amend public changesets (see :hg:`help phases`)
1465 1468 or changesets that have children.
1466 1469
1467 1470 See :hg:`help dates` for a list of formats valid for -d/--date.
1468 1471
1469 1472 Returns 0 on success, 1 if nothing changed.
1470 1473
1471 1474 .. container:: verbose
1472 1475
1473 1476 Examples:
1474 1477
1475 1478 - commit all files ending in .py::
1476 1479
1477 1480 hg commit --include "set:**.py"
1478 1481
1479 1482 - commit all non-binary files::
1480 1483
1481 1484 hg commit --exclude "set:binary()"
1482 1485
1483 1486 - amend the current commit and set the date to now::
1484 1487
1485 1488 hg commit --amend --date now
1486 1489 """
1487 1490 wlock = lock = None
1488 1491 try:
1489 1492 wlock = repo.wlock()
1490 1493 lock = repo.lock()
1491 1494 return _docommit(ui, repo, *pats, **opts)
1492 1495 finally:
1493 1496 release(lock, wlock)
1494 1497
1495 1498 def _docommit(ui, repo, *pats, **opts):
1496 1499 if opts.get(r'interactive'):
1497 1500 opts.pop(r'interactive')
1498 1501 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1499 1502 cmdutil.recordfilter, *pats,
1500 1503 **opts)
1501 1504 # ret can be 0 (no changes to record) or the value returned by
1502 1505 # commit(), 1 if nothing changed or None on success.
1503 1506 return 1 if ret == 0 else ret
1504 1507
1505 1508 opts = pycompat.byteskwargs(opts)
1506 1509 if opts.get('subrepos'):
1507 1510 if opts.get('amend'):
1508 1511 raise error.Abort(_('cannot amend with --subrepos'))
1509 1512 # Let --subrepos on the command line override config setting.
1510 1513 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1511 1514
1512 1515 cmdutil.checkunfinished(repo, commit=True)
1513 1516
1514 1517 branch = repo[None].branch()
1515 1518 bheads = repo.branchheads(branch)
1516 1519
1517 1520 extra = {}
1518 1521 if opts.get('close_branch'):
1519 1522 extra['close'] = 1
1520 1523
1521 1524 if not bheads:
1522 1525 raise error.Abort(_('can only close branch heads'))
1523 1526 elif opts.get('amend'):
1524 1527 if repo[None].parents()[0].p1().branch() != branch and \
1525 1528 repo[None].parents()[0].p2().branch() != branch:
1526 1529 raise error.Abort(_('can only close branch heads'))
1527 1530
1528 1531 if opts.get('amend'):
1529 1532 if ui.configbool('ui', 'commitsubrepos'):
1530 1533 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1531 1534
1532 1535 old = repo['.']
1533 1536 if not old.mutable():
1534 1537 raise error.Abort(_('cannot amend public changesets'))
1535 1538 if len(repo[None].parents()) > 1:
1536 1539 raise error.Abort(_('cannot amend while merging'))
1537 1540 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1538 1541 if not allowunstable and old.children():
1539 1542 raise error.Abort(_('cannot amend changeset with children'))
1540 1543
1541 1544 # Currently histedit gets confused if an amend happens while histedit
1542 1545 # is in progress. Since we have a checkunfinished command, we are
1543 1546 # temporarily honoring it.
1544 1547 #
1545 1548 # Note: eventually this guard will be removed. Please do not expect
1546 1549 # this behavior to remain.
1547 1550 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1548 1551 cmdutil.checkunfinished(repo)
1549 1552
1550 1553 # commitfunc is used only for temporary amend commit by cmdutil.amend
1551 1554 def commitfunc(ui, repo, message, match, opts):
1552 1555 return repo.commit(message,
1553 1556 opts.get('user') or old.user(),
1554 1557 opts.get('date') or old.date(),
1555 1558 match,
1556 1559 extra=extra)
1557 1560
1558 1561 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1559 1562 if node == old.node():
1560 1563 ui.status(_("nothing changed\n"))
1561 1564 return 1
1562 1565 else:
1563 1566 def commitfunc(ui, repo, message, match, opts):
1564 1567 overrides = {}
1565 1568 if opts.get('secret'):
1566 1569 overrides[('phases', 'new-commit')] = 'secret'
1567 1570
1568 1571 baseui = repo.baseui
1569 1572 with baseui.configoverride(overrides, 'commit'):
1570 1573 with ui.configoverride(overrides, 'commit'):
1571 1574 editform = cmdutil.mergeeditform(repo[None],
1572 1575 'commit.normal')
1573 1576 editor = cmdutil.getcommiteditor(
1574 1577 editform=editform, **pycompat.strkwargs(opts))
1575 1578 return repo.commit(message,
1576 1579 opts.get('user'),
1577 1580 opts.get('date'),
1578 1581 match,
1579 1582 editor=editor,
1580 1583 extra=extra)
1581 1584
1582 1585 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1583 1586
1584 1587 if not node:
1585 1588 stat = cmdutil.postcommitstatus(repo, pats, opts)
1586 1589 if stat[3]:
1587 1590 ui.status(_("nothing changed (%d missing files, see "
1588 1591 "'hg status')\n") % len(stat[3]))
1589 1592 else:
1590 1593 ui.status(_("nothing changed\n"))
1591 1594 return 1
1592 1595
1593 1596 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1594 1597
1595 1598 @command('config|showconfig|debugconfig',
1596 1599 [('u', 'untrusted', None, _('show untrusted configuration options')),
1597 1600 ('e', 'edit', None, _('edit user config')),
1598 1601 ('l', 'local', None, _('edit repository config')),
1599 1602 ('g', 'global', None, _('edit global config'))] + formatteropts,
1600 1603 _('[-u] [NAME]...'),
1601 1604 optionalrepo=True)
1602 1605 def config(ui, repo, *values, **opts):
1603 1606 """show combined config settings from all hgrc files
1604 1607
1605 1608 With no arguments, print names and values of all config items.
1606 1609
1607 1610 With one argument of the form section.name, print just the value
1608 1611 of that config item.
1609 1612
1610 1613 With multiple arguments, print names and values of all config
1611 1614 items with matching section names.
1612 1615
1613 1616 With --edit, start an editor on the user-level config file. With
1614 1617 --global, edit the system-wide config file. With --local, edit the
1615 1618 repository-level config file.
1616 1619
1617 1620 With --debug, the source (filename and line number) is printed
1618 1621 for each config item.
1619 1622
1620 1623 See :hg:`help config` for more information about config files.
1621 1624
1622 1625 Returns 0 on success, 1 if NAME does not exist.
1623 1626
1624 1627 """
1625 1628
1626 1629 opts = pycompat.byteskwargs(opts)
1627 1630 if opts.get('edit') or opts.get('local') or opts.get('global'):
1628 1631 if opts.get('local') and opts.get('global'):
1629 1632 raise error.Abort(_("can't use --local and --global together"))
1630 1633
1631 1634 if opts.get('local'):
1632 1635 if not repo:
1633 1636 raise error.Abort(_("can't use --local outside a repository"))
1634 1637 paths = [repo.vfs.join('hgrc')]
1635 1638 elif opts.get('global'):
1636 1639 paths = rcutil.systemrcpath()
1637 1640 else:
1638 1641 paths = rcutil.userrcpath()
1639 1642
1640 1643 for f in paths:
1641 1644 if os.path.exists(f):
1642 1645 break
1643 1646 else:
1644 1647 if opts.get('global'):
1645 1648 samplehgrc = uimod.samplehgrcs['global']
1646 1649 elif opts.get('local'):
1647 1650 samplehgrc = uimod.samplehgrcs['local']
1648 1651 else:
1649 1652 samplehgrc = uimod.samplehgrcs['user']
1650 1653
1651 1654 f = paths[0]
1652 1655 fp = open(f, "wb")
1653 1656 fp.write(util.tonativeeol(samplehgrc))
1654 1657 fp.close()
1655 1658
1656 1659 editor = ui.geteditor()
1657 1660 ui.system("%s \"%s\"" % (editor, f),
1658 1661 onerr=error.Abort, errprefix=_("edit failed"),
1659 1662 blockedtag='config_edit')
1660 1663 return
1661 1664 ui.pager('config')
1662 1665 fm = ui.formatter('config', opts)
1663 1666 for t, f in rcutil.rccomponents():
1664 1667 if t == 'path':
1665 1668 ui.debug('read config from: %s\n' % f)
1666 1669 elif t == 'items':
1667 1670 for section, name, value, source in f:
1668 1671 ui.debug('set config by: %s\n' % source)
1669 1672 else:
1670 1673 raise error.ProgrammingError('unknown rctype: %s' % t)
1671 1674 untrusted = bool(opts.get('untrusted'))
1672 1675 if values:
1673 1676 sections = [v for v in values if '.' not in v]
1674 1677 items = [v for v in values if '.' in v]
1675 1678 if len(items) > 1 or items and sections:
1676 1679 raise error.Abort(_('only one config item permitted'))
1677 1680 matched = False
1678 1681 for section, name, value in ui.walkconfig(untrusted=untrusted):
1679 1682 source = ui.configsource(section, name, untrusted)
1680 1683 value = pycompat.bytestr(value)
1681 1684 if fm.isplain():
1682 1685 source = source or 'none'
1683 1686 value = value.replace('\n', '\\n')
1684 1687 entryname = section + '.' + name
1685 1688 if values:
1686 1689 for v in values:
1687 1690 if v == section:
1688 1691 fm.startitem()
1689 1692 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1690 1693 fm.write('name value', '%s=%s\n', entryname, value)
1691 1694 matched = True
1692 1695 elif v == entryname:
1693 1696 fm.startitem()
1694 1697 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1695 1698 fm.write('value', '%s\n', value)
1696 1699 fm.data(name=entryname)
1697 1700 matched = True
1698 1701 else:
1699 1702 fm.startitem()
1700 1703 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1701 1704 fm.write('name value', '%s=%s\n', entryname, value)
1702 1705 matched = True
1703 1706 fm.end()
1704 1707 if matched:
1705 1708 return 0
1706 1709 return 1
1707 1710
1708 1711 @command('copy|cp',
1709 1712 [('A', 'after', None, _('record a copy that has already occurred')),
1710 1713 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1711 1714 ] + walkopts + dryrunopts,
1712 1715 _('[OPTION]... [SOURCE]... DEST'))
1713 1716 def copy(ui, repo, *pats, **opts):
1714 1717 """mark files as copied for the next commit
1715 1718
1716 1719 Mark dest as having copies of source files. If dest is a
1717 1720 directory, copies are put in that directory. If dest is a file,
1718 1721 the source must be a single file.
1719 1722
1720 1723 By default, this command copies the contents of files as they
1721 1724 exist in the working directory. If invoked with -A/--after, the
1722 1725 operation is recorded, but no copying is performed.
1723 1726
1724 1727 This command takes effect with the next commit. To undo a copy
1725 1728 before that, see :hg:`revert`.
1726 1729
1727 1730 Returns 0 on success, 1 if errors are encountered.
1728 1731 """
1729 1732 opts = pycompat.byteskwargs(opts)
1730 1733 with repo.wlock(False):
1731 1734 return cmdutil.copy(ui, repo, pats, opts)
1732 1735
1733 1736 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1734 1737 def debugcommands(ui, cmd='', *args):
1735 1738 """list all available commands and options"""
1736 1739 for cmd, vals in sorted(table.iteritems()):
1737 1740 cmd = cmd.split('|')[0].strip('^')
1738 1741 opts = ', '.join([i[1] for i in vals[1]])
1739 1742 ui.write('%s: %s\n' % (cmd, opts))
1740 1743
1741 1744 @command('debugcomplete',
1742 1745 [('o', 'options', None, _('show the command options'))],
1743 1746 _('[-o] CMD'),
1744 1747 norepo=True)
1745 1748 def debugcomplete(ui, cmd='', **opts):
1746 1749 """returns the completion list associated with the given command"""
1747 1750
1748 1751 if opts.get('options'):
1749 1752 options = []
1750 1753 otables = [globalopts]
1751 1754 if cmd:
1752 1755 aliases, entry = cmdutil.findcmd(cmd, table, False)
1753 1756 otables.append(entry[1])
1754 1757 for t in otables:
1755 1758 for o in t:
1756 1759 if "(DEPRECATED)" in o[3]:
1757 1760 continue
1758 1761 if o[0]:
1759 1762 options.append('-%s' % o[0])
1760 1763 options.append('--%s' % o[1])
1761 1764 ui.write("%s\n" % "\n".join(options))
1762 1765 return
1763 1766
1764 1767 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1765 1768 if ui.verbose:
1766 1769 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1767 1770 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1768 1771
1769 1772 @command('^diff',
1770 1773 [('r', 'rev', [], _('revision'), _('REV')),
1771 1774 ('c', 'change', '', _('change made by revision'), _('REV'))
1772 1775 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1773 1776 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1774 1777 inferrepo=True)
1775 1778 def diff(ui, repo, *pats, **opts):
1776 1779 """diff repository (or selected files)
1777 1780
1778 1781 Show differences between revisions for the specified files.
1779 1782
1780 1783 Differences between files are shown using the unified diff format.
1781 1784
1782 1785 .. note::
1783 1786
1784 1787 :hg:`diff` may generate unexpected results for merges, as it will
1785 1788 default to comparing against the working directory's first
1786 1789 parent changeset if no revisions are specified.
1787 1790
1788 1791 When two revision arguments are given, then changes are shown
1789 1792 between those revisions. If only one revision is specified then
1790 1793 that revision is compared to the working directory, and, when no
1791 1794 revisions are specified, the working directory files are compared
1792 1795 to its first parent.
1793 1796
1794 1797 Alternatively you can specify -c/--change with a revision to see
1795 1798 the changes in that changeset relative to its first parent.
1796 1799
1797 1800 Without the -a/--text option, diff will avoid generating diffs of
1798 1801 files it detects as binary. With -a, diff will generate a diff
1799 1802 anyway, probably with undesirable results.
1800 1803
1801 1804 Use the -g/--git option to generate diffs in the git extended diff
1802 1805 format. For more information, read :hg:`help diffs`.
1803 1806
1804 1807 .. container:: verbose
1805 1808
1806 1809 Examples:
1807 1810
1808 1811 - compare a file in the current working directory to its parent::
1809 1812
1810 1813 hg diff foo.c
1811 1814
1812 1815 - compare two historical versions of a directory, with rename info::
1813 1816
1814 1817 hg diff --git -r 1.0:1.2 lib/
1815 1818
1816 1819 - get change stats relative to the last change on some date::
1817 1820
1818 1821 hg diff --stat -r "date('may 2')"
1819 1822
1820 1823 - diff all newly-added files that contain a keyword::
1821 1824
1822 1825 hg diff "set:added() and grep(GNU)"
1823 1826
1824 1827 - compare a revision and its parents::
1825 1828
1826 1829 hg diff -c 9353 # compare against first parent
1827 1830 hg diff -r 9353^:9353 # same using revset syntax
1828 1831 hg diff -r 9353^2:9353 # compare against the second parent
1829 1832
1830 1833 Returns 0 on success.
1831 1834 """
1832 1835
1833 1836 opts = pycompat.byteskwargs(opts)
1834 1837 revs = opts.get('rev')
1835 1838 change = opts.get('change')
1836 1839 stat = opts.get('stat')
1837 1840 reverse = opts.get('reverse')
1838 1841
1839 1842 if revs and change:
1840 1843 msg = _('cannot specify --rev and --change at the same time')
1841 1844 raise error.Abort(msg)
1842 1845 elif change:
1843 1846 node2 = scmutil.revsingle(repo, change, None).node()
1844 1847 node1 = repo[node2].p1().node()
1845 1848 else:
1846 1849 node1, node2 = scmutil.revpair(repo, revs)
1847 1850
1848 1851 if reverse:
1849 1852 node1, node2 = node2, node1
1850 1853
1851 1854 diffopts = patch.diffallopts(ui, opts)
1852 1855 m = scmutil.match(repo[node2], pats, opts)
1853 1856 ui.pager('diff')
1854 1857 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1855 1858 listsubrepos=opts.get('subrepos'),
1856 1859 root=opts.get('root'))
1857 1860
1858 1861 @command('^export',
1859 1862 [('o', 'output', '',
1860 1863 _('print output to file with formatted name'), _('FORMAT')),
1861 1864 ('', 'switch-parent', None, _('diff against the second parent')),
1862 1865 ('r', 'rev', [], _('revisions to export'), _('REV')),
1863 1866 ] + diffopts,
1864 1867 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
1865 1868 def export(ui, repo, *changesets, **opts):
1866 1869 """dump the header and diffs for one or more changesets
1867 1870
1868 1871 Print the changeset header and diffs for one or more revisions.
1869 1872 If no revision is given, the parent of the working directory is used.
1870 1873
1871 1874 The information shown in the changeset header is: author, date,
1872 1875 branch name (if non-default), changeset hash, parent(s) and commit
1873 1876 comment.
1874 1877
1875 1878 .. note::
1876 1879
1877 1880 :hg:`export` may generate unexpected diff output for merge
1878 1881 changesets, as it will compare the merge changeset against its
1879 1882 first parent only.
1880 1883
1881 1884 Output may be to a file, in which case the name of the file is
1882 1885 given using a format string. The formatting rules are as follows:
1883 1886
1884 1887 :``%%``: literal "%" character
1885 1888 :``%H``: changeset hash (40 hexadecimal digits)
1886 1889 :``%N``: number of patches being generated
1887 1890 :``%R``: changeset revision number
1888 1891 :``%b``: basename of the exporting repository
1889 1892 :``%h``: short-form changeset hash (12 hexadecimal digits)
1890 1893 :``%m``: first line of the commit message (only alphanumeric characters)
1891 1894 :``%n``: zero-padded sequence number, starting at 1
1892 1895 :``%r``: zero-padded changeset revision number
1893 1896
1894 1897 Without the -a/--text option, export will avoid generating diffs
1895 1898 of files it detects as binary. With -a, export will generate a
1896 1899 diff anyway, probably with undesirable results.
1897 1900
1898 1901 Use the -g/--git option to generate diffs in the git extended diff
1899 1902 format. See :hg:`help diffs` for more information.
1900 1903
1901 1904 With the --switch-parent option, the diff will be against the
1902 1905 second parent. It can be useful to review a merge.
1903 1906
1904 1907 .. container:: verbose
1905 1908
1906 1909 Examples:
1907 1910
1908 1911 - use export and import to transplant a bugfix to the current
1909 1912 branch::
1910 1913
1911 1914 hg export -r 9353 | hg import -
1912 1915
1913 1916 - export all the changesets between two revisions to a file with
1914 1917 rename information::
1915 1918
1916 1919 hg export --git -r 123:150 > changes.txt
1917 1920
1918 1921 - split outgoing changes into a series of patches with
1919 1922 descriptive names::
1920 1923
1921 1924 hg export -r "outgoing()" -o "%n-%m.patch"
1922 1925
1923 1926 Returns 0 on success.
1924 1927 """
1925 1928 opts = pycompat.byteskwargs(opts)
1926 1929 changesets += tuple(opts.get('rev', []))
1927 1930 if not changesets:
1928 1931 changesets = ['.']
1929 1932 revs = scmutil.revrange(repo, changesets)
1930 1933 if not revs:
1931 1934 raise error.Abort(_("export requires at least one changeset"))
1932 1935 if len(revs) > 1:
1933 1936 ui.note(_('exporting patches:\n'))
1934 1937 else:
1935 1938 ui.note(_('exporting patch:\n'))
1936 1939 ui.pager('export')
1937 1940 cmdutil.export(repo, revs, fntemplate=opts.get('output'),
1938 1941 switch_parent=opts.get('switch_parent'),
1939 1942 opts=patch.diffallopts(ui, opts))
1940 1943
1941 1944 @command('files',
1942 1945 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
1943 1946 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
1944 1947 ] + walkopts + formatteropts + subrepoopts,
1945 1948 _('[OPTION]... [FILE]...'))
1946 1949 def files(ui, repo, *pats, **opts):
1947 1950 """list tracked files
1948 1951
1949 1952 Print files under Mercurial control in the working directory or
1950 1953 specified revision for given files (excluding removed files).
1951 1954 Files can be specified as filenames or filesets.
1952 1955
1953 1956 If no files are given to match, this command prints the names
1954 1957 of all files under Mercurial control.
1955 1958
1956 1959 .. container:: verbose
1957 1960
1958 1961 Examples:
1959 1962
1960 1963 - list all files under the current directory::
1961 1964
1962 1965 hg files .
1963 1966
1964 1967 - shows sizes and flags for current revision::
1965 1968
1966 1969 hg files -vr .
1967 1970
1968 1971 - list all files named README::
1969 1972
1970 1973 hg files -I "**/README"
1971 1974
1972 1975 - list all binary files::
1973 1976
1974 1977 hg files "set:binary()"
1975 1978
1976 1979 - find files containing a regular expression::
1977 1980
1978 1981 hg files "set:grep('bob')"
1979 1982
1980 1983 - search tracked file contents with xargs and grep::
1981 1984
1982 1985 hg files -0 | xargs -0 grep foo
1983 1986
1984 1987 See :hg:`help patterns` and :hg:`help filesets` for more information
1985 1988 on specifying file patterns.
1986 1989
1987 1990 Returns 0 if a match is found, 1 otherwise.
1988 1991
1989 1992 """
1990 1993
1991 1994 opts = pycompat.byteskwargs(opts)
1992 1995 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
1993 1996
1994 1997 end = '\n'
1995 1998 if opts.get('print0'):
1996 1999 end = '\0'
1997 2000 fmt = '%s' + end
1998 2001
1999 2002 m = scmutil.match(ctx, pats, opts)
2000 2003 ui.pager('files')
2001 2004 with ui.formatter('files', opts) as fm:
2002 2005 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2003 2006
2004 2007 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2005 2008 def forget(ui, repo, *pats, **opts):
2006 2009 """forget the specified files on the next commit
2007 2010
2008 2011 Mark the specified files so they will no longer be tracked
2009 2012 after the next commit.
2010 2013
2011 2014 This only removes files from the current branch, not from the
2012 2015 entire project history, and it does not delete them from the
2013 2016 working directory.
2014 2017
2015 2018 To delete the file from the working directory, see :hg:`remove`.
2016 2019
2017 2020 To undo a forget before the next commit, see :hg:`add`.
2018 2021
2019 2022 .. container:: verbose
2020 2023
2021 2024 Examples:
2022 2025
2023 2026 - forget newly-added binary files::
2024 2027
2025 2028 hg forget "set:added() and binary()"
2026 2029
2027 2030 - forget files that would be excluded by .hgignore::
2028 2031
2029 2032 hg forget "set:hgignore()"
2030 2033
2031 2034 Returns 0 on success.
2032 2035 """
2033 2036
2034 2037 opts = pycompat.byteskwargs(opts)
2035 2038 if not pats:
2036 2039 raise error.Abort(_('no files specified'))
2037 2040
2038 2041 m = scmutil.match(repo[None], pats, opts)
2039 2042 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2040 2043 return rejected and 1 or 0
2041 2044
2042 2045 @command(
2043 2046 'graft',
2044 2047 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2045 2048 ('c', 'continue', False, _('resume interrupted graft')),
2046 2049 ('e', 'edit', False, _('invoke editor on commit messages')),
2047 2050 ('', 'log', None, _('append graft info to log message')),
2048 2051 ('f', 'force', False, _('force graft')),
2049 2052 ('D', 'currentdate', False,
2050 2053 _('record the current date as commit date')),
2051 2054 ('U', 'currentuser', False,
2052 2055 _('record the current user as committer'), _('DATE'))]
2053 2056 + commitopts2 + mergetoolopts + dryrunopts,
2054 2057 _('[OPTION]... [-r REV]... REV...'))
2055 2058 def graft(ui, repo, *revs, **opts):
2056 2059 '''copy changes from other branches onto the current branch
2057 2060
2058 2061 This command uses Mercurial's merge logic to copy individual
2059 2062 changes from other branches without merging branches in the
2060 2063 history graph. This is sometimes known as 'backporting' or
2061 2064 'cherry-picking'. By default, graft will copy user, date, and
2062 2065 description from the source changesets.
2063 2066
2064 2067 Changesets that are ancestors of the current revision, that have
2065 2068 already been grafted, or that are merges will be skipped.
2066 2069
2067 2070 If --log is specified, log messages will have a comment appended
2068 2071 of the form::
2069 2072
2070 2073 (grafted from CHANGESETHASH)
2071 2074
2072 2075 If --force is specified, revisions will be grafted even if they
2073 2076 are already ancestors of or have been grafted to the destination.
2074 2077 This is useful when the revisions have since been backed out.
2075 2078
2076 2079 If a graft merge results in conflicts, the graft process is
2077 2080 interrupted so that the current merge can be manually resolved.
2078 2081 Once all conflicts are addressed, the graft process can be
2079 2082 continued with the -c/--continue option.
2080 2083
2081 2084 .. note::
2082 2085
2083 2086 The -c/--continue option does not reapply earlier options, except
2084 2087 for --force.
2085 2088
2086 2089 .. container:: verbose
2087 2090
2088 2091 Examples:
2089 2092
2090 2093 - copy a single change to the stable branch and edit its description::
2091 2094
2092 2095 hg update stable
2093 2096 hg graft --edit 9393
2094 2097
2095 2098 - graft a range of changesets with one exception, updating dates::
2096 2099
2097 2100 hg graft -D "2085::2093 and not 2091"
2098 2101
2099 2102 - continue a graft after resolving conflicts::
2100 2103
2101 2104 hg graft -c
2102 2105
2103 2106 - show the source of a grafted changeset::
2104 2107
2105 2108 hg log --debug -r .
2106 2109
2107 2110 - show revisions sorted by date::
2108 2111
2109 2112 hg log -r "sort(all(), date)"
2110 2113
2111 2114 See :hg:`help revisions` for more about specifying revisions.
2112 2115
2113 2116 Returns 0 on successful completion.
2114 2117 '''
2115 2118 with repo.wlock():
2116 2119 return _dograft(ui, repo, *revs, **opts)
2117 2120
2118 2121 def _dograft(ui, repo, *revs, **opts):
2119 2122 opts = pycompat.byteskwargs(opts)
2120 2123 if revs and opts.get('rev'):
2121 2124 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2122 2125 'revision ordering!\n'))
2123 2126
2124 2127 revs = list(revs)
2125 2128 revs.extend(opts.get('rev'))
2126 2129
2127 2130 if not opts.get('user') and opts.get('currentuser'):
2128 2131 opts['user'] = ui.username()
2129 2132 if not opts.get('date') and opts.get('currentdate'):
2130 2133 opts['date'] = "%d %d" % util.makedate()
2131 2134
2132 2135 editor = cmdutil.getcommiteditor(editform='graft',
2133 2136 **pycompat.strkwargs(opts))
2134 2137
2135 2138 cont = False
2136 2139 if opts.get('continue'):
2137 2140 cont = True
2138 2141 if revs:
2139 2142 raise error.Abort(_("can't specify --continue and revisions"))
2140 2143 # read in unfinished revisions
2141 2144 try:
2142 2145 nodes = repo.vfs.read('graftstate').splitlines()
2143 2146 revs = [repo[node].rev() for node in nodes]
2144 2147 except IOError as inst:
2145 2148 if inst.errno != errno.ENOENT:
2146 2149 raise
2147 2150 cmdutil.wrongtooltocontinue(repo, _('graft'))
2148 2151 else:
2149 2152 cmdutil.checkunfinished(repo)
2150 2153 cmdutil.bailifchanged(repo)
2151 2154 if not revs:
2152 2155 raise error.Abort(_('no revisions specified'))
2153 2156 revs = scmutil.revrange(repo, revs)
2154 2157
2155 2158 skipped = set()
2156 2159 # check for merges
2157 2160 for rev in repo.revs('%ld and merge()', revs):
2158 2161 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2159 2162 skipped.add(rev)
2160 2163 revs = [r for r in revs if r not in skipped]
2161 2164 if not revs:
2162 2165 return -1
2163 2166
2164 2167 # Don't check in the --continue case, in effect retaining --force across
2165 2168 # --continues. That's because without --force, any revisions we decided to
2166 2169 # skip would have been filtered out here, so they wouldn't have made their
2167 2170 # way to the graftstate. With --force, any revisions we would have otherwise
2168 2171 # skipped would not have been filtered out, and if they hadn't been applied
2169 2172 # already, they'd have been in the graftstate.
2170 2173 if not (cont or opts.get('force')):
2171 2174 # check for ancestors of dest branch
2172 2175 crev = repo['.'].rev()
2173 2176 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2174 2177 # XXX make this lazy in the future
2175 2178 # don't mutate while iterating, create a copy
2176 2179 for rev in list(revs):
2177 2180 if rev in ancestors:
2178 2181 ui.warn(_('skipping ancestor revision %d:%s\n') %
2179 2182 (rev, repo[rev]))
2180 2183 # XXX remove on list is slow
2181 2184 revs.remove(rev)
2182 2185 if not revs:
2183 2186 return -1
2184 2187
2185 2188 # analyze revs for earlier grafts
2186 2189 ids = {}
2187 2190 for ctx in repo.set("%ld", revs):
2188 2191 ids[ctx.hex()] = ctx.rev()
2189 2192 n = ctx.extra().get('source')
2190 2193 if n:
2191 2194 ids[n] = ctx.rev()
2192 2195
2193 2196 # check ancestors for earlier grafts
2194 2197 ui.debug('scanning for duplicate grafts\n')
2195 2198
2196 2199 # The only changesets we can be sure doesn't contain grafts of any
2197 2200 # revs, are the ones that are common ancestors of *all* revs:
2198 2201 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2199 2202 ctx = repo[rev]
2200 2203 n = ctx.extra().get('source')
2201 2204 if n in ids:
2202 2205 try:
2203 2206 r = repo[n].rev()
2204 2207 except error.RepoLookupError:
2205 2208 r = None
2206 2209 if r in revs:
2207 2210 ui.warn(_('skipping revision %d:%s '
2208 2211 '(already grafted to %d:%s)\n')
2209 2212 % (r, repo[r], rev, ctx))
2210 2213 revs.remove(r)
2211 2214 elif ids[n] in revs:
2212 2215 if r is None:
2213 2216 ui.warn(_('skipping already grafted revision %d:%s '
2214 2217 '(%d:%s also has unknown origin %s)\n')
2215 2218 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2216 2219 else:
2217 2220 ui.warn(_('skipping already grafted revision %d:%s '
2218 2221 '(%d:%s also has origin %d:%s)\n')
2219 2222 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2220 2223 revs.remove(ids[n])
2221 2224 elif ctx.hex() in ids:
2222 2225 r = ids[ctx.hex()]
2223 2226 ui.warn(_('skipping already grafted revision %d:%s '
2224 2227 '(was grafted from %d:%s)\n') %
2225 2228 (r, repo[r], rev, ctx))
2226 2229 revs.remove(r)
2227 2230 if not revs:
2228 2231 return -1
2229 2232
2230 2233 for pos, ctx in enumerate(repo.set("%ld", revs)):
2231 2234 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2232 2235 ctx.description().split('\n', 1)[0])
2233 2236 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2234 2237 if names:
2235 2238 desc += ' (%s)' % ' '.join(names)
2236 2239 ui.status(_('grafting %s\n') % desc)
2237 2240 if opts.get('dry_run'):
2238 2241 continue
2239 2242
2240 2243 source = ctx.extra().get('source')
2241 2244 extra = {}
2242 2245 if source:
2243 2246 extra['source'] = source
2244 2247 extra['intermediate-source'] = ctx.hex()
2245 2248 else:
2246 2249 extra['source'] = ctx.hex()
2247 2250 user = ctx.user()
2248 2251 if opts.get('user'):
2249 2252 user = opts['user']
2250 2253 date = ctx.date()
2251 2254 if opts.get('date'):
2252 2255 date = opts['date']
2253 2256 message = ctx.description()
2254 2257 if opts.get('log'):
2255 2258 message += '\n(grafted from %s)' % ctx.hex()
2256 2259
2257 2260 # we don't merge the first commit when continuing
2258 2261 if not cont:
2259 2262 # perform the graft merge with p1(rev) as 'ancestor'
2260 2263 try:
2261 2264 # ui.forcemerge is an internal variable, do not document
2262 2265 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2263 2266 'graft')
2264 2267 stats = mergemod.graft(repo, ctx, ctx.p1(),
2265 2268 ['local', 'graft'])
2266 2269 finally:
2267 2270 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2268 2271 # report any conflicts
2269 2272 if stats and stats[3] > 0:
2270 2273 # write out state for --continue
2271 2274 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2272 2275 repo.vfs.write('graftstate', ''.join(nodelines))
2273 2276 extra = ''
2274 2277 if opts.get('user'):
2275 2278 extra += ' --user %s' % util.shellquote(opts['user'])
2276 2279 if opts.get('date'):
2277 2280 extra += ' --date %s' % util.shellquote(opts['date'])
2278 2281 if opts.get('log'):
2279 2282 extra += ' --log'
2280 2283 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2281 2284 raise error.Abort(
2282 2285 _("unresolved conflicts, can't continue"),
2283 2286 hint=hint)
2284 2287 else:
2285 2288 cont = False
2286 2289
2287 2290 # commit
2288 2291 node = repo.commit(text=message, user=user,
2289 2292 date=date, extra=extra, editor=editor)
2290 2293 if node is None:
2291 2294 ui.warn(
2292 2295 _('note: graft of %d:%s created no changes to commit\n') %
2293 2296 (ctx.rev(), ctx))
2294 2297
2295 2298 # remove state when we complete successfully
2296 2299 if not opts.get('dry_run'):
2297 2300 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2298 2301
2299 2302 return 0
2300 2303
2301 2304 @command('grep',
2302 2305 [('0', 'print0', None, _('end fields with NUL')),
2303 2306 ('', 'all', None, _('print all revisions that match')),
2304 2307 ('a', 'text', None, _('treat all files as text')),
2305 2308 ('f', 'follow', None,
2306 2309 _('follow changeset history,'
2307 2310 ' or file history across copies and renames')),
2308 2311 ('i', 'ignore-case', None, _('ignore case when matching')),
2309 2312 ('l', 'files-with-matches', None,
2310 2313 _('print only filenames and revisions that match')),
2311 2314 ('n', 'line-number', None, _('print matching line numbers')),
2312 2315 ('r', 'rev', [],
2313 2316 _('only search files changed within revision range'), _('REV')),
2314 2317 ('u', 'user', None, _('list the author (long with -v)')),
2315 2318 ('d', 'date', None, _('list the date (short with -q)')),
2316 2319 ] + formatteropts + walkopts,
2317 2320 _('[OPTION]... PATTERN [FILE]...'),
2318 2321 inferrepo=True)
2319 2322 def grep(ui, repo, pattern, *pats, **opts):
2320 2323 """search revision history for a pattern in specified files
2321 2324
2322 2325 Search revision history for a regular expression in the specified
2323 2326 files or the entire project.
2324 2327
2325 2328 By default, grep prints the most recent revision number for each
2326 2329 file in which it finds a match. To get it to print every revision
2327 2330 that contains a change in match status ("-" for a match that becomes
2328 2331 a non-match, or "+" for a non-match that becomes a match), use the
2329 2332 --all flag.
2330 2333
2331 2334 PATTERN can be any Python (roughly Perl-compatible) regular
2332 2335 expression.
2333 2336
2334 2337 If no FILEs are specified (and -f/--follow isn't set), all files in
2335 2338 the repository are searched, including those that don't exist in the
2336 2339 current branch or have been deleted in a prior changeset.
2337 2340
2338 2341 Returns 0 if a match is found, 1 otherwise.
2339 2342 """
2340 2343 opts = pycompat.byteskwargs(opts)
2341 2344 reflags = re.M
2342 2345 if opts.get('ignore_case'):
2343 2346 reflags |= re.I
2344 2347 try:
2345 2348 regexp = util.re.compile(pattern, reflags)
2346 2349 except re.error as inst:
2347 2350 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2348 2351 return 1
2349 2352 sep, eol = ':', '\n'
2350 2353 if opts.get('print0'):
2351 2354 sep = eol = '\0'
2352 2355
2353 2356 getfile = util.lrucachefunc(repo.file)
2354 2357
2355 2358 def matchlines(body):
2356 2359 begin = 0
2357 2360 linenum = 0
2358 2361 while begin < len(body):
2359 2362 match = regexp.search(body, begin)
2360 2363 if not match:
2361 2364 break
2362 2365 mstart, mend = match.span()
2363 2366 linenum += body.count('\n', begin, mstart) + 1
2364 2367 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2365 2368 begin = body.find('\n', mend) + 1 or len(body) + 1
2366 2369 lend = begin - 1
2367 2370 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2368 2371
2369 2372 class linestate(object):
2370 2373 def __init__(self, line, linenum, colstart, colend):
2371 2374 self.line = line
2372 2375 self.linenum = linenum
2373 2376 self.colstart = colstart
2374 2377 self.colend = colend
2375 2378
2376 2379 def __hash__(self):
2377 2380 return hash((self.linenum, self.line))
2378 2381
2379 2382 def __eq__(self, other):
2380 2383 return self.line == other.line
2381 2384
2382 2385 def findpos(self):
2383 2386 """Iterate all (start, end) indices of matches"""
2384 2387 yield self.colstart, self.colend
2385 2388 p = self.colend
2386 2389 while p < len(self.line):
2387 2390 m = regexp.search(self.line, p)
2388 2391 if not m:
2389 2392 break
2390 2393 yield m.span()
2391 2394 p = m.end()
2392 2395
2393 2396 matches = {}
2394 2397 copies = {}
2395 2398 def grepbody(fn, rev, body):
2396 2399 matches[rev].setdefault(fn, [])
2397 2400 m = matches[rev][fn]
2398 2401 for lnum, cstart, cend, line in matchlines(body):
2399 2402 s = linestate(line, lnum, cstart, cend)
2400 2403 m.append(s)
2401 2404
2402 2405 def difflinestates(a, b):
2403 2406 sm = difflib.SequenceMatcher(None, a, b)
2404 2407 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2405 2408 if tag == 'insert':
2406 2409 for i in xrange(blo, bhi):
2407 2410 yield ('+', b[i])
2408 2411 elif tag == 'delete':
2409 2412 for i in xrange(alo, ahi):
2410 2413 yield ('-', a[i])
2411 2414 elif tag == 'replace':
2412 2415 for i in xrange(alo, ahi):
2413 2416 yield ('-', a[i])
2414 2417 for i in xrange(blo, bhi):
2415 2418 yield ('+', b[i])
2416 2419
2417 2420 def display(fm, fn, ctx, pstates, states):
2418 2421 rev = ctx.rev()
2419 2422 if fm.isplain():
2420 2423 formatuser = ui.shortuser
2421 2424 else:
2422 2425 formatuser = str
2423 2426 if ui.quiet:
2424 2427 datefmt = '%Y-%m-%d'
2425 2428 else:
2426 2429 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2427 2430 found = False
2428 2431 @util.cachefunc
2429 2432 def binary():
2430 2433 flog = getfile(fn)
2431 2434 return util.binary(flog.read(ctx.filenode(fn)))
2432 2435
2433 2436 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2434 2437 if opts.get('all'):
2435 2438 iter = difflinestates(pstates, states)
2436 2439 else:
2437 2440 iter = [('', l) for l in states]
2438 2441 for change, l in iter:
2439 2442 fm.startitem()
2440 2443 fm.data(node=fm.hexfunc(ctx.node()))
2441 2444 cols = [
2442 2445 ('filename', fn, True),
2443 2446 ('rev', rev, True),
2444 2447 ('linenumber', l.linenum, opts.get('line_number')),
2445 2448 ]
2446 2449 if opts.get('all'):
2447 2450 cols.append(('change', change, True))
2448 2451 cols.extend([
2449 2452 ('user', formatuser(ctx.user()), opts.get('user')),
2450 2453 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2451 2454 ])
2452 2455 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2453 2456 for name, data, cond in cols:
2454 2457 field = fieldnamemap.get(name, name)
2455 2458 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2456 2459 if cond and name != lastcol:
2457 2460 fm.plain(sep, label='grep.sep')
2458 2461 if not opts.get('files_with_matches'):
2459 2462 fm.plain(sep, label='grep.sep')
2460 2463 if not opts.get('text') and binary():
2461 2464 fm.plain(_(" Binary file matches"))
2462 2465 else:
2463 2466 displaymatches(fm.nested('texts'), l)
2464 2467 fm.plain(eol)
2465 2468 found = True
2466 2469 if opts.get('files_with_matches'):
2467 2470 break
2468 2471 return found
2469 2472
2470 2473 def displaymatches(fm, l):
2471 2474 p = 0
2472 2475 for s, e in l.findpos():
2473 2476 if p < s:
2474 2477 fm.startitem()
2475 2478 fm.write('text', '%s', l.line[p:s])
2476 2479 fm.data(matched=False)
2477 2480 fm.startitem()
2478 2481 fm.write('text', '%s', l.line[s:e], label='grep.match')
2479 2482 fm.data(matched=True)
2480 2483 p = e
2481 2484 if p < len(l.line):
2482 2485 fm.startitem()
2483 2486 fm.write('text', '%s', l.line[p:])
2484 2487 fm.data(matched=False)
2485 2488 fm.end()
2486 2489
2487 2490 skip = {}
2488 2491 revfiles = {}
2489 2492 matchfn = scmutil.match(repo[None], pats, opts)
2490 2493 found = False
2491 2494 follow = opts.get('follow')
2492 2495
2493 2496 def prep(ctx, fns):
2494 2497 rev = ctx.rev()
2495 2498 pctx = ctx.p1()
2496 2499 parent = pctx.rev()
2497 2500 matches.setdefault(rev, {})
2498 2501 matches.setdefault(parent, {})
2499 2502 files = revfiles.setdefault(rev, [])
2500 2503 for fn in fns:
2501 2504 flog = getfile(fn)
2502 2505 try:
2503 2506 fnode = ctx.filenode(fn)
2504 2507 except error.LookupError:
2505 2508 continue
2506 2509
2507 2510 copied = flog.renamed(fnode)
2508 2511 copy = follow and copied and copied[0]
2509 2512 if copy:
2510 2513 copies.setdefault(rev, {})[fn] = copy
2511 2514 if fn in skip:
2512 2515 if copy:
2513 2516 skip[copy] = True
2514 2517 continue
2515 2518 files.append(fn)
2516 2519
2517 2520 if fn not in matches[rev]:
2518 2521 grepbody(fn, rev, flog.read(fnode))
2519 2522
2520 2523 pfn = copy or fn
2521 2524 if pfn not in matches[parent]:
2522 2525 try:
2523 2526 fnode = pctx.filenode(pfn)
2524 2527 grepbody(pfn, parent, flog.read(fnode))
2525 2528 except error.LookupError:
2526 2529 pass
2527 2530
2528 2531 ui.pager('grep')
2529 2532 fm = ui.formatter('grep', opts)
2530 2533 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2531 2534 rev = ctx.rev()
2532 2535 parent = ctx.p1().rev()
2533 2536 for fn in sorted(revfiles.get(rev, [])):
2534 2537 states = matches[rev][fn]
2535 2538 copy = copies.get(rev, {}).get(fn)
2536 2539 if fn in skip:
2537 2540 if copy:
2538 2541 skip[copy] = True
2539 2542 continue
2540 2543 pstates = matches.get(parent, {}).get(copy or fn, [])
2541 2544 if pstates or states:
2542 2545 r = display(fm, fn, ctx, pstates, states)
2543 2546 found = found or r
2544 2547 if r and not opts.get('all'):
2545 2548 skip[fn] = True
2546 2549 if copy:
2547 2550 skip[copy] = True
2548 2551 del matches[rev]
2549 2552 del revfiles[rev]
2550 2553 fm.end()
2551 2554
2552 2555 return not found
2553 2556
2554 2557 @command('heads',
2555 2558 [('r', 'rev', '',
2556 2559 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2557 2560 ('t', 'topo', False, _('show topological heads only')),
2558 2561 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2559 2562 ('c', 'closed', False, _('show normal and closed branch heads')),
2560 2563 ] + templateopts,
2561 2564 _('[-ct] [-r STARTREV] [REV]...'))
2562 2565 def heads(ui, repo, *branchrevs, **opts):
2563 2566 """show branch heads
2564 2567
2565 2568 With no arguments, show all open branch heads in the repository.
2566 2569 Branch heads are changesets that have no descendants on the
2567 2570 same branch. They are where development generally takes place and
2568 2571 are the usual targets for update and merge operations.
2569 2572
2570 2573 If one or more REVs are given, only open branch heads on the
2571 2574 branches associated with the specified changesets are shown. This
2572 2575 means that you can use :hg:`heads .` to see the heads on the
2573 2576 currently checked-out branch.
2574 2577
2575 2578 If -c/--closed is specified, also show branch heads marked closed
2576 2579 (see :hg:`commit --close-branch`).
2577 2580
2578 2581 If STARTREV is specified, only those heads that are descendants of
2579 2582 STARTREV will be displayed.
2580 2583
2581 2584 If -t/--topo is specified, named branch mechanics will be ignored and only
2582 2585 topological heads (changesets with no children) will be shown.
2583 2586
2584 2587 Returns 0 if matching heads are found, 1 if not.
2585 2588 """
2586 2589
2587 2590 opts = pycompat.byteskwargs(opts)
2588 2591 start = None
2589 2592 if 'rev' in opts:
2590 2593 start = scmutil.revsingle(repo, opts['rev'], None).node()
2591 2594
2592 2595 if opts.get('topo'):
2593 2596 heads = [repo[h] for h in repo.heads(start)]
2594 2597 else:
2595 2598 heads = []
2596 2599 for branch in repo.branchmap():
2597 2600 heads += repo.branchheads(branch, start, opts.get('closed'))
2598 2601 heads = [repo[h] for h in heads]
2599 2602
2600 2603 if branchrevs:
2601 2604 branches = set(repo[br].branch() for br in branchrevs)
2602 2605 heads = [h for h in heads if h.branch() in branches]
2603 2606
2604 2607 if opts.get('active') and branchrevs:
2605 2608 dagheads = repo.heads(start)
2606 2609 heads = [h for h in heads if h.node() in dagheads]
2607 2610
2608 2611 if branchrevs:
2609 2612 haveheads = set(h.branch() for h in heads)
2610 2613 if branches - haveheads:
2611 2614 headless = ', '.join(b for b in branches - haveheads)
2612 2615 msg = _('no open branch heads found on branches %s')
2613 2616 if opts.get('rev'):
2614 2617 msg += _(' (started at %s)') % opts['rev']
2615 2618 ui.warn((msg + '\n') % headless)
2616 2619
2617 2620 if not heads:
2618 2621 return 1
2619 2622
2620 2623 ui.pager('heads')
2621 2624 heads = sorted(heads, key=lambda x: -x.rev())
2622 2625 displayer = cmdutil.show_changeset(ui, repo, opts)
2623 2626 for ctx in heads:
2624 2627 displayer.show(ctx)
2625 2628 displayer.close()
2626 2629
2627 2630 @command('help',
2628 2631 [('e', 'extension', None, _('show only help for extensions')),
2629 2632 ('c', 'command', None, _('show only help for commands')),
2630 2633 ('k', 'keyword', None, _('show topics matching keyword')),
2631 2634 ('s', 'system', [], _('show help for specific platform(s)')),
2632 2635 ],
2633 2636 _('[-ecks] [TOPIC]'),
2634 2637 norepo=True)
2635 2638 def help_(ui, name=None, **opts):
2636 2639 """show help for a given topic or a help overview
2637 2640
2638 2641 With no arguments, print a list of commands with short help messages.
2639 2642
2640 2643 Given a topic, extension, or command name, print help for that
2641 2644 topic.
2642 2645
2643 2646 Returns 0 if successful.
2644 2647 """
2645 2648
2646 2649 keep = opts.get(r'system') or []
2647 2650 if len(keep) == 0:
2648 2651 if pycompat.sysplatform.startswith('win'):
2649 2652 keep.append('windows')
2650 2653 elif pycompat.sysplatform == 'OpenVMS':
2651 2654 keep.append('vms')
2652 2655 elif pycompat.sysplatform == 'plan9':
2653 2656 keep.append('plan9')
2654 2657 else:
2655 2658 keep.append('unix')
2656 2659 keep.append(pycompat.sysplatform.lower())
2657 2660 if ui.verbose:
2658 2661 keep.append('verbose')
2659 2662
2660 2663 commands = sys.modules[__name__]
2661 2664 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
2662 2665 ui.pager('help')
2663 2666 ui.write(formatted)
2664 2667
2665 2668
2666 2669 @command('identify|id',
2667 2670 [('r', 'rev', '',
2668 2671 _('identify the specified revision'), _('REV')),
2669 2672 ('n', 'num', None, _('show local revision number')),
2670 2673 ('i', 'id', None, _('show global revision id')),
2671 2674 ('b', 'branch', None, _('show branch')),
2672 2675 ('t', 'tags', None, _('show tags')),
2673 2676 ('B', 'bookmarks', None, _('show bookmarks')),
2674 2677 ] + remoteopts + formatteropts,
2675 2678 _('[-nibtB] [-r REV] [SOURCE]'),
2676 2679 optionalrepo=True)
2677 2680 def identify(ui, repo, source=None, rev=None,
2678 2681 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2679 2682 """identify the working directory or specified revision
2680 2683
2681 2684 Print a summary identifying the repository state at REV using one or
2682 2685 two parent hash identifiers, followed by a "+" if the working
2683 2686 directory has uncommitted changes, the branch name (if not default),
2684 2687 a list of tags, and a list of bookmarks.
2685 2688
2686 2689 When REV is not given, print a summary of the current state of the
2687 2690 repository.
2688 2691
2689 2692 Specifying a path to a repository root or Mercurial bundle will
2690 2693 cause lookup to operate on that repository/bundle.
2691 2694
2692 2695 .. container:: verbose
2693 2696
2694 2697 Examples:
2695 2698
2696 2699 - generate a build identifier for the working directory::
2697 2700
2698 2701 hg id --id > build-id.dat
2699 2702
2700 2703 - find the revision corresponding to a tag::
2701 2704
2702 2705 hg id -n -r 1.3
2703 2706
2704 2707 - check the most recent revision of a remote repository::
2705 2708
2706 2709 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2707 2710
2708 2711 See :hg:`log` for generating more information about specific revisions,
2709 2712 including full hash identifiers.
2710 2713
2711 2714 Returns 0 if successful.
2712 2715 """
2713 2716
2714 2717 opts = pycompat.byteskwargs(opts)
2715 2718 if not repo and not source:
2716 2719 raise error.Abort(_("there is no Mercurial repository here "
2717 2720 "(.hg not found)"))
2718 2721
2719 2722 if ui.debugflag:
2720 2723 hexfunc = hex
2721 2724 else:
2722 2725 hexfunc = short
2723 2726 default = not (num or id or branch or tags or bookmarks)
2724 2727 output = []
2725 2728 revs = []
2726 2729
2727 2730 if source:
2728 2731 source, branches = hg.parseurl(ui.expandpath(source))
2729 2732 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2730 2733 repo = peer.local()
2731 2734 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2732 2735
2733 2736 fm = ui.formatter('identify', opts)
2734 2737 fm.startitem()
2735 2738
2736 2739 if not repo:
2737 2740 if num or branch or tags:
2738 2741 raise error.Abort(
2739 2742 _("can't query remote revision number, branch, or tags"))
2740 2743 if not rev and revs:
2741 2744 rev = revs[0]
2742 2745 if not rev:
2743 2746 rev = "tip"
2744 2747
2745 2748 remoterev = peer.lookup(rev)
2746 2749 hexrev = hexfunc(remoterev)
2747 2750 if default or id:
2748 2751 output = [hexrev]
2749 2752 fm.data(id=hexrev)
2750 2753
2751 2754 def getbms():
2752 2755 bms = []
2753 2756
2754 2757 if 'bookmarks' in peer.listkeys('namespaces'):
2755 2758 hexremoterev = hex(remoterev)
2756 2759 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2757 2760 if bmr == hexremoterev]
2758 2761
2759 2762 return sorted(bms)
2760 2763
2761 2764 bms = getbms()
2762 2765 if bookmarks:
2763 2766 output.extend(bms)
2764 2767 elif default and not ui.quiet:
2765 2768 # multiple bookmarks for a single parent separated by '/'
2766 2769 bm = '/'.join(bms)
2767 2770 if bm:
2768 2771 output.append(bm)
2769 2772
2770 2773 fm.data(node=hex(remoterev))
2771 2774 fm.data(bookmarks=fm.formatlist(bms, name='bookmark'))
2772 2775 else:
2773 2776 ctx = scmutil.revsingle(repo, rev, None)
2774 2777
2775 2778 if ctx.rev() is None:
2776 2779 ctx = repo[None]
2777 2780 parents = ctx.parents()
2778 2781 taglist = []
2779 2782 for p in parents:
2780 2783 taglist.extend(p.tags())
2781 2784
2782 2785 dirty = ""
2783 2786 if ctx.dirty(missing=True, merge=False, branch=False):
2784 2787 dirty = '+'
2785 2788 fm.data(dirty=dirty)
2786 2789
2787 2790 hexoutput = [hexfunc(p.node()) for p in parents]
2788 2791 if default or id:
2789 2792 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
2790 2793 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
2791 2794
2792 2795 if num:
2793 2796 numoutput = ["%d" % p.rev() for p in parents]
2794 2797 output.append("%s%s" % ('+'.join(numoutput), dirty))
2795 2798
2796 2799 fn = fm.nested('parents')
2797 2800 for p in parents:
2798 2801 fn.startitem()
2799 2802 fn.data(rev=p.rev())
2800 2803 fn.data(node=p.hex())
2801 2804 fn.context(ctx=p)
2802 2805 fn.end()
2803 2806 else:
2804 2807 hexoutput = hexfunc(ctx.node())
2805 2808 if default or id:
2806 2809 output = [hexoutput]
2807 2810 fm.data(id=hexoutput)
2808 2811
2809 2812 if num:
2810 2813 output.append(pycompat.bytestr(ctx.rev()))
2811 2814 taglist = ctx.tags()
2812 2815
2813 2816 if default and not ui.quiet:
2814 2817 b = ctx.branch()
2815 2818 if b != 'default':
2816 2819 output.append("(%s)" % b)
2817 2820
2818 2821 # multiple tags for a single parent separated by '/'
2819 2822 t = '/'.join(taglist)
2820 2823 if t:
2821 2824 output.append(t)
2822 2825
2823 2826 # multiple bookmarks for a single parent separated by '/'
2824 2827 bm = '/'.join(ctx.bookmarks())
2825 2828 if bm:
2826 2829 output.append(bm)
2827 2830 else:
2828 2831 if branch:
2829 2832 output.append(ctx.branch())
2830 2833
2831 2834 if tags:
2832 2835 output.extend(taglist)
2833 2836
2834 2837 if bookmarks:
2835 2838 output.extend(ctx.bookmarks())
2836 2839
2837 2840 fm.data(node=ctx.hex())
2838 2841 fm.data(branch=ctx.branch())
2839 2842 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
2840 2843 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
2841 2844 fm.context(ctx=ctx)
2842 2845
2843 2846 fm.plain("%s\n" % ' '.join(output))
2844 2847 fm.end()
2845 2848
2846 2849 @command('import|patch',
2847 2850 [('p', 'strip', 1,
2848 2851 _('directory strip option for patch. This has the same '
2849 2852 'meaning as the corresponding patch option'), _('NUM')),
2850 2853 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2851 2854 ('e', 'edit', False, _('invoke editor on commit messages')),
2852 2855 ('f', 'force', None,
2853 2856 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2854 2857 ('', 'no-commit', None,
2855 2858 _("don't commit, just update the working directory")),
2856 2859 ('', 'bypass', None,
2857 2860 _("apply patch without touching the working directory")),
2858 2861 ('', 'partial', None,
2859 2862 _('commit even if some hunks fail')),
2860 2863 ('', 'exact', None,
2861 2864 _('abort if patch would apply lossily')),
2862 2865 ('', 'prefix', '',
2863 2866 _('apply patch to subdirectory'), _('DIR')),
2864 2867 ('', 'import-branch', None,
2865 2868 _('use any branch information in patch (implied by --exact)'))] +
2866 2869 commitopts + commitopts2 + similarityopts,
2867 2870 _('[OPTION]... PATCH...'))
2868 2871 def import_(ui, repo, patch1=None, *patches, **opts):
2869 2872 """import an ordered set of patches
2870 2873
2871 2874 Import a list of patches and commit them individually (unless
2872 2875 --no-commit is specified).
2873 2876
2874 2877 To read a patch from standard input (stdin), use "-" as the patch
2875 2878 name. If a URL is specified, the patch will be downloaded from
2876 2879 there.
2877 2880
2878 2881 Import first applies changes to the working directory (unless
2879 2882 --bypass is specified), import will abort if there are outstanding
2880 2883 changes.
2881 2884
2882 2885 Use --bypass to apply and commit patches directly to the
2883 2886 repository, without affecting the working directory. Without
2884 2887 --exact, patches will be applied on top of the working directory
2885 2888 parent revision.
2886 2889
2887 2890 You can import a patch straight from a mail message. Even patches
2888 2891 as attachments work (to use the body part, it must have type
2889 2892 text/plain or text/x-patch). From and Subject headers of email
2890 2893 message are used as default committer and commit message. All
2891 2894 text/plain body parts before first diff are added to the commit
2892 2895 message.
2893 2896
2894 2897 If the imported patch was generated by :hg:`export`, user and
2895 2898 description from patch override values from message headers and
2896 2899 body. Values given on command line with -m/--message and -u/--user
2897 2900 override these.
2898 2901
2899 2902 If --exact is specified, import will set the working directory to
2900 2903 the parent of each patch before applying it, and will abort if the
2901 2904 resulting changeset has a different ID than the one recorded in
2902 2905 the patch. This will guard against various ways that portable
2903 2906 patch formats and mail systems might fail to transfer Mercurial
2904 2907 data or metadata. See :hg:`bundle` for lossless transmission.
2905 2908
2906 2909 Use --partial to ensure a changeset will be created from the patch
2907 2910 even if some hunks fail to apply. Hunks that fail to apply will be
2908 2911 written to a <target-file>.rej file. Conflicts can then be resolved
2909 2912 by hand before :hg:`commit --amend` is run to update the created
2910 2913 changeset. This flag exists to let people import patches that
2911 2914 partially apply without losing the associated metadata (author,
2912 2915 date, description, ...).
2913 2916
2914 2917 .. note::
2915 2918
2916 2919 When no hunks apply cleanly, :hg:`import --partial` will create
2917 2920 an empty changeset, importing only the patch metadata.
2918 2921
2919 2922 With -s/--similarity, hg will attempt to discover renames and
2920 2923 copies in the patch in the same way as :hg:`addremove`.
2921 2924
2922 2925 It is possible to use external patch programs to perform the patch
2923 2926 by setting the ``ui.patch`` configuration option. For the default
2924 2927 internal tool, the fuzz can also be configured via ``patch.fuzz``.
2925 2928 See :hg:`help config` for more information about configuration
2926 2929 files and how to use these options.
2927 2930
2928 2931 See :hg:`help dates` for a list of formats valid for -d/--date.
2929 2932
2930 2933 .. container:: verbose
2931 2934
2932 2935 Examples:
2933 2936
2934 2937 - import a traditional patch from a website and detect renames::
2935 2938
2936 2939 hg import -s 80 http://example.com/bugfix.patch
2937 2940
2938 2941 - import a changeset from an hgweb server::
2939 2942
2940 2943 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
2941 2944
2942 2945 - import all the patches in an Unix-style mbox::
2943 2946
2944 2947 hg import incoming-patches.mbox
2945 2948
2946 2949 - import patches from stdin::
2947 2950
2948 2951 hg import -
2949 2952
2950 2953 - attempt to exactly restore an exported changeset (not always
2951 2954 possible)::
2952 2955
2953 2956 hg import --exact proposed-fix.patch
2954 2957
2955 2958 - use an external tool to apply a patch which is too fuzzy for
2956 2959 the default internal tool.
2957 2960
2958 2961 hg import --config ui.patch="patch --merge" fuzzy.patch
2959 2962
2960 2963 - change the default fuzzing from 2 to a less strict 7
2961 2964
2962 2965 hg import --config ui.fuzz=7 fuzz.patch
2963 2966
2964 2967 Returns 0 on success, 1 on partial success (see --partial).
2965 2968 """
2966 2969
2967 2970 opts = pycompat.byteskwargs(opts)
2968 2971 if not patch1:
2969 2972 raise error.Abort(_('need at least one patch to import'))
2970 2973
2971 2974 patches = (patch1,) + patches
2972 2975
2973 2976 date = opts.get('date')
2974 2977 if date:
2975 2978 opts['date'] = util.parsedate(date)
2976 2979
2977 2980 exact = opts.get('exact')
2978 2981 update = not opts.get('bypass')
2979 2982 if not update and opts.get('no_commit'):
2980 2983 raise error.Abort(_('cannot use --no-commit with --bypass'))
2981 2984 try:
2982 2985 sim = float(opts.get('similarity') or 0)
2983 2986 except ValueError:
2984 2987 raise error.Abort(_('similarity must be a number'))
2985 2988 if sim < 0 or sim > 100:
2986 2989 raise error.Abort(_('similarity must be between 0 and 100'))
2987 2990 if sim and not update:
2988 2991 raise error.Abort(_('cannot use --similarity with --bypass'))
2989 2992 if exact:
2990 2993 if opts.get('edit'):
2991 2994 raise error.Abort(_('cannot use --exact with --edit'))
2992 2995 if opts.get('prefix'):
2993 2996 raise error.Abort(_('cannot use --exact with --prefix'))
2994 2997
2995 2998 base = opts["base"]
2996 2999 wlock = dsguard = lock = tr = None
2997 3000 msgs = []
2998 3001 ret = 0
2999 3002
3000 3003
3001 3004 try:
3002 3005 wlock = repo.wlock()
3003 3006
3004 3007 if update:
3005 3008 cmdutil.checkunfinished(repo)
3006 3009 if (exact or not opts.get('force')):
3007 3010 cmdutil.bailifchanged(repo)
3008 3011
3009 3012 if not opts.get('no_commit'):
3010 3013 lock = repo.lock()
3011 3014 tr = repo.transaction('import')
3012 3015 else:
3013 3016 dsguard = dirstateguard.dirstateguard(repo, 'import')
3014 3017 parents = repo[None].parents()
3015 3018 for patchurl in patches:
3016 3019 if patchurl == '-':
3017 3020 ui.status(_('applying patch from stdin\n'))
3018 3021 patchfile = ui.fin
3019 3022 patchurl = 'stdin' # for error message
3020 3023 else:
3021 3024 patchurl = os.path.join(base, patchurl)
3022 3025 ui.status(_('applying %s\n') % patchurl)
3023 3026 patchfile = hg.openpath(ui, patchurl)
3024 3027
3025 3028 haspatch = False
3026 3029 for hunk in patch.split(patchfile):
3027 3030 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3028 3031 parents, opts,
3029 3032 msgs, hg.clean)
3030 3033 if msg:
3031 3034 haspatch = True
3032 3035 ui.note(msg + '\n')
3033 3036 if update or exact:
3034 3037 parents = repo[None].parents()
3035 3038 else:
3036 3039 parents = [repo[node]]
3037 3040 if rej:
3038 3041 ui.write_err(_("patch applied partially\n"))
3039 3042 ui.write_err(_("(fix the .rej files and run "
3040 3043 "`hg commit --amend`)\n"))
3041 3044 ret = 1
3042 3045 break
3043 3046
3044 3047 if not haspatch:
3045 3048 raise error.Abort(_('%s: no diffs found') % patchurl)
3046 3049
3047 3050 if tr:
3048 3051 tr.close()
3049 3052 if msgs:
3050 3053 repo.savecommitmessage('\n* * *\n'.join(msgs))
3051 3054 if dsguard:
3052 3055 dsguard.close()
3053 3056 return ret
3054 3057 finally:
3055 3058 if tr:
3056 3059 tr.release()
3057 3060 release(lock, dsguard, wlock)
3058 3061
3059 3062 @command('incoming|in',
3060 3063 [('f', 'force', None,
3061 3064 _('run even if remote repository is unrelated')),
3062 3065 ('n', 'newest-first', None, _('show newest record first')),
3063 3066 ('', 'bundle', '',
3064 3067 _('file to store the bundles into'), _('FILE')),
3065 3068 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3066 3069 ('B', 'bookmarks', False, _("compare bookmarks")),
3067 3070 ('b', 'branch', [],
3068 3071 _('a specific branch you would like to pull'), _('BRANCH')),
3069 3072 ] + logopts + remoteopts + subrepoopts,
3070 3073 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3071 3074 def incoming(ui, repo, source="default", **opts):
3072 3075 """show new changesets found in source
3073 3076
3074 3077 Show new changesets found in the specified path/URL or the default
3075 3078 pull location. These are the changesets that would have been pulled
3076 3079 if a pull at the time you issued this command.
3077 3080
3078 3081 See pull for valid source format details.
3079 3082
3080 3083 .. container:: verbose
3081 3084
3082 3085 With -B/--bookmarks, the result of bookmark comparison between
3083 3086 local and remote repositories is displayed. With -v/--verbose,
3084 3087 status is also displayed for each bookmark like below::
3085 3088
3086 3089 BM1 01234567890a added
3087 3090 BM2 1234567890ab advanced
3088 3091 BM3 234567890abc diverged
3089 3092 BM4 34567890abcd changed
3090 3093
3091 3094 The action taken locally when pulling depends on the
3092 3095 status of each bookmark:
3093 3096
3094 3097 :``added``: pull will create it
3095 3098 :``advanced``: pull will update it
3096 3099 :``diverged``: pull will create a divergent bookmark
3097 3100 :``changed``: result depends on remote changesets
3098 3101
3099 3102 From the point of view of pulling behavior, bookmark
3100 3103 existing only in the remote repository are treated as ``added``,
3101 3104 even if it is in fact locally deleted.
3102 3105
3103 3106 .. container:: verbose
3104 3107
3105 3108 For remote repository, using --bundle avoids downloading the
3106 3109 changesets twice if the incoming is followed by a pull.
3107 3110
3108 3111 Examples:
3109 3112
3110 3113 - show incoming changes with patches and full description::
3111 3114
3112 3115 hg incoming -vp
3113 3116
3114 3117 - show incoming changes excluding merges, store a bundle::
3115 3118
3116 3119 hg in -vpM --bundle incoming.hg
3117 3120 hg pull incoming.hg
3118 3121
3119 3122 - briefly list changes inside a bundle::
3120 3123
3121 3124 hg in changes.hg -T "{desc|firstline}\\n"
3122 3125
3123 3126 Returns 0 if there are incoming changes, 1 otherwise.
3124 3127 """
3125 3128 opts = pycompat.byteskwargs(opts)
3126 3129 if opts.get('graph'):
3127 3130 cmdutil.checkunsupportedgraphflags([], opts)
3128 3131 def display(other, chlist, displayer):
3129 3132 revdag = cmdutil.graphrevs(other, chlist, opts)
3130 3133 cmdutil.displaygraph(ui, repo, revdag, displayer,
3131 3134 graphmod.asciiedges)
3132 3135
3133 3136 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3134 3137 return 0
3135 3138
3136 3139 if opts.get('bundle') and opts.get('subrepos'):
3137 3140 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3138 3141
3139 3142 if opts.get('bookmarks'):
3140 3143 source, branches = hg.parseurl(ui.expandpath(source),
3141 3144 opts.get('branch'))
3142 3145 other = hg.peer(repo, opts, source)
3143 3146 if 'bookmarks' not in other.listkeys('namespaces'):
3144 3147 ui.warn(_("remote doesn't support bookmarks\n"))
3145 3148 return 0
3146 3149 ui.pager('incoming')
3147 3150 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3148 3151 return bookmarks.incoming(ui, repo, other)
3149 3152
3150 3153 repo._subtoppath = ui.expandpath(source)
3151 3154 try:
3152 3155 return hg.incoming(ui, repo, source, opts)
3153 3156 finally:
3154 3157 del repo._subtoppath
3155 3158
3156 3159
3157 3160 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3158 3161 norepo=True)
3159 3162 def init(ui, dest=".", **opts):
3160 3163 """create a new repository in the given directory
3161 3164
3162 3165 Initialize a new repository in the given directory. If the given
3163 3166 directory does not exist, it will be created.
3164 3167
3165 3168 If no directory is given, the current directory is used.
3166 3169
3167 3170 It is possible to specify an ``ssh://`` URL as the destination.
3168 3171 See :hg:`help urls` for more information.
3169 3172
3170 3173 Returns 0 on success.
3171 3174 """
3172 3175 opts = pycompat.byteskwargs(opts)
3173 3176 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3174 3177
3175 3178 @command('locate',
3176 3179 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3177 3180 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3178 3181 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3179 3182 ] + walkopts,
3180 3183 _('[OPTION]... [PATTERN]...'))
3181 3184 def locate(ui, repo, *pats, **opts):
3182 3185 """locate files matching specific patterns (DEPRECATED)
3183 3186
3184 3187 Print files under Mercurial control in the working directory whose
3185 3188 names match the given patterns.
3186 3189
3187 3190 By default, this command searches all directories in the working
3188 3191 directory. To search just the current directory and its
3189 3192 subdirectories, use "--include .".
3190 3193
3191 3194 If no patterns are given to match, this command prints the names
3192 3195 of all files under Mercurial control in the working directory.
3193 3196
3194 3197 If you want to feed the output of this command into the "xargs"
3195 3198 command, use the -0 option to both this command and "xargs". This
3196 3199 will avoid the problem of "xargs" treating single filenames that
3197 3200 contain whitespace as multiple filenames.
3198 3201
3199 3202 See :hg:`help files` for a more versatile command.
3200 3203
3201 3204 Returns 0 if a match is found, 1 otherwise.
3202 3205 """
3203 3206 opts = pycompat.byteskwargs(opts)
3204 3207 if opts.get('print0'):
3205 3208 end = '\0'
3206 3209 else:
3207 3210 end = '\n'
3208 3211 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3209 3212
3210 3213 ret = 1
3211 3214 ctx = repo[rev]
3212 3215 m = scmutil.match(ctx, pats, opts, default='relglob',
3213 3216 badfn=lambda x, y: False)
3214 3217
3215 3218 ui.pager('locate')
3216 3219 for abs in ctx.matches(m):
3217 3220 if opts.get('fullpath'):
3218 3221 ui.write(repo.wjoin(abs), end)
3219 3222 else:
3220 3223 ui.write(((pats and m.rel(abs)) or abs), end)
3221 3224 ret = 0
3222 3225
3223 3226 return ret
3224 3227
3225 3228 @command('^log|history',
3226 3229 [('f', 'follow', None,
3227 3230 _('follow changeset history, or file history across copies and renames')),
3228 3231 ('', 'follow-first', None,
3229 3232 _('only follow the first parent of merge changesets (DEPRECATED)')),
3230 3233 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3231 3234 ('C', 'copies', None, _('show copied files')),
3232 3235 ('k', 'keyword', [],
3233 3236 _('do case-insensitive search for a given text'), _('TEXT')),
3234 3237 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3235 3238 ('', 'removed', None, _('include revisions where files were removed')),
3236 3239 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3237 3240 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3238 3241 ('', 'only-branch', [],
3239 3242 _('show only changesets within the given named branch (DEPRECATED)'),
3240 3243 _('BRANCH')),
3241 3244 ('b', 'branch', [],
3242 3245 _('show changesets within the given named branch'), _('BRANCH')),
3243 3246 ('P', 'prune', [],
3244 3247 _('do not display revision or any of its ancestors'), _('REV')),
3245 3248 ] + logopts + walkopts,
3246 3249 _('[OPTION]... [FILE]'),
3247 3250 inferrepo=True)
3248 3251 def log(ui, repo, *pats, **opts):
3249 3252 """show revision history of entire repository or files
3250 3253
3251 3254 Print the revision history of the specified files or the entire
3252 3255 project.
3253 3256
3254 3257 If no revision range is specified, the default is ``tip:0`` unless
3255 3258 --follow is set, in which case the working directory parent is
3256 3259 used as the starting revision.
3257 3260
3258 3261 File history is shown without following rename or copy history of
3259 3262 files. Use -f/--follow with a filename to follow history across
3260 3263 renames and copies. --follow without a filename will only show
3261 3264 ancestors or descendants of the starting revision.
3262 3265
3263 3266 By default this command prints revision number and changeset id,
3264 3267 tags, non-trivial parents, user, date and time, and a summary for
3265 3268 each commit. When the -v/--verbose switch is used, the list of
3266 3269 changed files and full commit message are shown.
3267 3270
3268 3271 With --graph the revisions are shown as an ASCII art DAG with the most
3269 3272 recent changeset at the top.
3270 3273 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3271 3274 and '+' represents a fork where the changeset from the lines below is a
3272 3275 parent of the 'o' merge on the same line.
3273 3276 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3274 3277 of a '|' indicates one or more revisions in a path are omitted.
3275 3278
3276 3279 .. note::
3277 3280
3278 3281 :hg:`log --patch` may generate unexpected diff output for merge
3279 3282 changesets, as it will only compare the merge changeset against
3280 3283 its first parent. Also, only files different from BOTH parents
3281 3284 will appear in files:.
3282 3285
3283 3286 .. note::
3284 3287
3285 3288 For performance reasons, :hg:`log FILE` may omit duplicate changes
3286 3289 made on branches and will not show removals or mode changes. To
3287 3290 see all such changes, use the --removed switch.
3288 3291
3289 3292 .. container:: verbose
3290 3293
3291 3294 Some examples:
3292 3295
3293 3296 - changesets with full descriptions and file lists::
3294 3297
3295 3298 hg log -v
3296 3299
3297 3300 - changesets ancestral to the working directory::
3298 3301
3299 3302 hg log -f
3300 3303
3301 3304 - last 10 commits on the current branch::
3302 3305
3303 3306 hg log -l 10 -b .
3304 3307
3305 3308 - changesets showing all modifications of a file, including removals::
3306 3309
3307 3310 hg log --removed file.c
3308 3311
3309 3312 - all changesets that touch a directory, with diffs, excluding merges::
3310 3313
3311 3314 hg log -Mp lib/
3312 3315
3313 3316 - all revision numbers that match a keyword::
3314 3317
3315 3318 hg log -k bug --template "{rev}\\n"
3316 3319
3317 3320 - the full hash identifier of the working directory parent::
3318 3321
3319 3322 hg log -r . --template "{node}\\n"
3320 3323
3321 3324 - list available log templates::
3322 3325
3323 3326 hg log -T list
3324 3327
3325 3328 - check if a given changeset is included in a tagged release::
3326 3329
3327 3330 hg log -r "a21ccf and ancestor(1.9)"
3328 3331
3329 3332 - find all changesets by some user in a date range::
3330 3333
3331 3334 hg log -k alice -d "may 2008 to jul 2008"
3332 3335
3333 3336 - summary of all changesets after the last tag::
3334 3337
3335 3338 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3336 3339
3337 3340 See :hg:`help dates` for a list of formats valid for -d/--date.
3338 3341
3339 3342 See :hg:`help revisions` for more about specifying and ordering
3340 3343 revisions.
3341 3344
3342 3345 See :hg:`help templates` for more about pre-packaged styles and
3343 3346 specifying custom templates. The default template used by the log
3344 3347 command can be customized via the ``ui.logtemplate`` configuration
3345 3348 setting.
3346 3349
3347 3350 Returns 0 on success.
3348 3351
3349 3352 """
3350 3353 opts = pycompat.byteskwargs(opts)
3351 3354 if opts.get('follow') and opts.get('rev'):
3352 3355 opts['rev'] = [revsetlang.formatspec('reverse(::%lr)', opts.get('rev'))]
3353 3356 del opts['follow']
3354 3357
3355 3358 if opts.get('graph'):
3356 3359 return cmdutil.graphlog(ui, repo, pats, opts)
3357 3360
3358 3361 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3359 3362 limit = cmdutil.loglimit(opts)
3360 3363 count = 0
3361 3364
3362 3365 getrenamed = None
3363 3366 if opts.get('copies'):
3364 3367 endrev = None
3365 3368 if opts.get('rev'):
3366 3369 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3367 3370 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3368 3371
3369 3372 ui.pager('log')
3370 3373 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3371 3374 for rev in revs:
3372 3375 if count == limit:
3373 3376 break
3374 3377 ctx = repo[rev]
3375 3378 copies = None
3376 3379 if getrenamed is not None and rev:
3377 3380 copies = []
3378 3381 for fn in ctx.files():
3379 3382 rename = getrenamed(fn, rev)
3380 3383 if rename:
3381 3384 copies.append((fn, rename[0]))
3382 3385 if filematcher:
3383 3386 revmatchfn = filematcher(ctx.rev())
3384 3387 else:
3385 3388 revmatchfn = None
3386 3389 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3387 3390 if displayer.flush(ctx):
3388 3391 count += 1
3389 3392
3390 3393 displayer.close()
3391 3394
3392 3395 @command('manifest',
3393 3396 [('r', 'rev', '', _('revision to display'), _('REV')),
3394 3397 ('', 'all', False, _("list files from all revisions"))]
3395 3398 + formatteropts,
3396 3399 _('[-r REV]'))
3397 3400 def manifest(ui, repo, node=None, rev=None, **opts):
3398 3401 """output the current or given revision of the project manifest
3399 3402
3400 3403 Print a list of version controlled files for the given revision.
3401 3404 If no revision is given, the first parent of the working directory
3402 3405 is used, or the null revision if no revision is checked out.
3403 3406
3404 3407 With -v, print file permissions, symlink and executable bits.
3405 3408 With --debug, print file revision hashes.
3406 3409
3407 3410 If option --all is specified, the list of all files from all revisions
3408 3411 is printed. This includes deleted and renamed files.
3409 3412
3410 3413 Returns 0 on success.
3411 3414 """
3412 3415 opts = pycompat.byteskwargs(opts)
3413 3416 fm = ui.formatter('manifest', opts)
3414 3417
3415 3418 if opts.get('all'):
3416 3419 if rev or node:
3417 3420 raise error.Abort(_("can't specify a revision with --all"))
3418 3421
3419 3422 res = []
3420 3423 prefix = "data/"
3421 3424 suffix = ".i"
3422 3425 plen = len(prefix)
3423 3426 slen = len(suffix)
3424 3427 with repo.lock():
3425 3428 for fn, b, size in repo.store.datafiles():
3426 3429 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3427 3430 res.append(fn[plen:-slen])
3428 3431 ui.pager('manifest')
3429 3432 for f in res:
3430 3433 fm.startitem()
3431 3434 fm.write("path", '%s\n', f)
3432 3435 fm.end()
3433 3436 return
3434 3437
3435 3438 if rev and node:
3436 3439 raise error.Abort(_("please specify just one revision"))
3437 3440
3438 3441 if not node:
3439 3442 node = rev
3440 3443
3441 3444 char = {'l': '@', 'x': '*', '': ''}
3442 3445 mode = {'l': '644', 'x': '755', '': '644'}
3443 3446 ctx = scmutil.revsingle(repo, node)
3444 3447 mf = ctx.manifest()
3445 3448 ui.pager('manifest')
3446 3449 for f in ctx:
3447 3450 fm.startitem()
3448 3451 fl = ctx[f].flags()
3449 3452 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3450 3453 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3451 3454 fm.write('path', '%s\n', f)
3452 3455 fm.end()
3453 3456
3454 3457 @command('^merge',
3455 3458 [('f', 'force', None,
3456 3459 _('force a merge including outstanding changes (DEPRECATED)')),
3457 3460 ('r', 'rev', '', _('revision to merge'), _('REV')),
3458 3461 ('P', 'preview', None,
3459 3462 _('review revisions to merge (no merge is performed)'))
3460 3463 ] + mergetoolopts,
3461 3464 _('[-P] [[-r] REV]'))
3462 3465 def merge(ui, repo, node=None, **opts):
3463 3466 """merge another revision into working directory
3464 3467
3465 3468 The current working directory is updated with all changes made in
3466 3469 the requested revision since the last common predecessor revision.
3467 3470
3468 3471 Files that changed between either parent are marked as changed for
3469 3472 the next commit and a commit must be performed before any further
3470 3473 updates to the repository are allowed. The next commit will have
3471 3474 two parents.
3472 3475
3473 3476 ``--tool`` can be used to specify the merge tool used for file
3474 3477 merges. It overrides the HGMERGE environment variable and your
3475 3478 configuration files. See :hg:`help merge-tools` for options.
3476 3479
3477 3480 If no revision is specified, the working directory's parent is a
3478 3481 head revision, and the current branch contains exactly one other
3479 3482 head, the other head is merged with by default. Otherwise, an
3480 3483 explicit revision with which to merge with must be provided.
3481 3484
3482 3485 See :hg:`help resolve` for information on handling file conflicts.
3483 3486
3484 3487 To undo an uncommitted merge, use :hg:`update --clean .` which
3485 3488 will check out a clean copy of the original merge parent, losing
3486 3489 all changes.
3487 3490
3488 3491 Returns 0 on success, 1 if there are unresolved files.
3489 3492 """
3490 3493
3491 3494 opts = pycompat.byteskwargs(opts)
3492 3495 if opts.get('rev') and node:
3493 3496 raise error.Abort(_("please specify just one revision"))
3494 3497 if not node:
3495 3498 node = opts.get('rev')
3496 3499
3497 3500 if node:
3498 3501 node = scmutil.revsingle(repo, node).node()
3499 3502
3500 3503 if not node:
3501 3504 node = repo[destutil.destmerge(repo)].node()
3502 3505
3503 3506 if opts.get('preview'):
3504 3507 # find nodes that are ancestors of p2 but not of p1
3505 3508 p1 = repo.lookup('.')
3506 3509 p2 = repo.lookup(node)
3507 3510 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3508 3511
3509 3512 displayer = cmdutil.show_changeset(ui, repo, opts)
3510 3513 for node in nodes:
3511 3514 displayer.show(repo[node])
3512 3515 displayer.close()
3513 3516 return 0
3514 3517
3515 3518 try:
3516 3519 # ui.forcemerge is an internal variable, do not document
3517 3520 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3518 3521 force = opts.get('force')
3519 3522 labels = ['working copy', 'merge rev']
3520 3523 return hg.merge(repo, node, force=force, mergeforce=force,
3521 3524 labels=labels)
3522 3525 finally:
3523 3526 ui.setconfig('ui', 'forcemerge', '', 'merge')
3524 3527
3525 3528 @command('outgoing|out',
3526 3529 [('f', 'force', None, _('run even when the destination is unrelated')),
3527 3530 ('r', 'rev', [],
3528 3531 _('a changeset intended to be included in the destination'), _('REV')),
3529 3532 ('n', 'newest-first', None, _('show newest record first')),
3530 3533 ('B', 'bookmarks', False, _('compare bookmarks')),
3531 3534 ('b', 'branch', [], _('a specific branch you would like to push'),
3532 3535 _('BRANCH')),
3533 3536 ] + logopts + remoteopts + subrepoopts,
3534 3537 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3535 3538 def outgoing(ui, repo, dest=None, **opts):
3536 3539 """show changesets not found in the destination
3537 3540
3538 3541 Show changesets not found in the specified destination repository
3539 3542 or the default push location. These are the changesets that would
3540 3543 be pushed if a push was requested.
3541 3544
3542 3545 See pull for details of valid destination formats.
3543 3546
3544 3547 .. container:: verbose
3545 3548
3546 3549 With -B/--bookmarks, the result of bookmark comparison between
3547 3550 local and remote repositories is displayed. With -v/--verbose,
3548 3551 status is also displayed for each bookmark like below::
3549 3552
3550 3553 BM1 01234567890a added
3551 3554 BM2 deleted
3552 3555 BM3 234567890abc advanced
3553 3556 BM4 34567890abcd diverged
3554 3557 BM5 4567890abcde changed
3555 3558
3556 3559 The action taken when pushing depends on the
3557 3560 status of each bookmark:
3558 3561
3559 3562 :``added``: push with ``-B`` will create it
3560 3563 :``deleted``: push with ``-B`` will delete it
3561 3564 :``advanced``: push will update it
3562 3565 :``diverged``: push with ``-B`` will update it
3563 3566 :``changed``: push with ``-B`` will update it
3564 3567
3565 3568 From the point of view of pushing behavior, bookmarks
3566 3569 existing only in the remote repository are treated as
3567 3570 ``deleted``, even if it is in fact added remotely.
3568 3571
3569 3572 Returns 0 if there are outgoing changes, 1 otherwise.
3570 3573 """
3571 3574 opts = pycompat.byteskwargs(opts)
3572 3575 if opts.get('graph'):
3573 3576 cmdutil.checkunsupportedgraphflags([], opts)
3574 3577 o, other = hg._outgoing(ui, repo, dest, opts)
3575 3578 if not o:
3576 3579 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3577 3580 return
3578 3581
3579 3582 revdag = cmdutil.graphrevs(repo, o, opts)
3580 3583 ui.pager('outgoing')
3581 3584 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3582 3585 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3583 3586 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3584 3587 return 0
3585 3588
3586 3589 if opts.get('bookmarks'):
3587 3590 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3588 3591 dest, branches = hg.parseurl(dest, opts.get('branch'))
3589 3592 other = hg.peer(repo, opts, dest)
3590 3593 if 'bookmarks' not in other.listkeys('namespaces'):
3591 3594 ui.warn(_("remote doesn't support bookmarks\n"))
3592 3595 return 0
3593 3596 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3594 3597 ui.pager('outgoing')
3595 3598 return bookmarks.outgoing(ui, repo, other)
3596 3599
3597 3600 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3598 3601 try:
3599 3602 return hg.outgoing(ui, repo, dest, opts)
3600 3603 finally:
3601 3604 del repo._subtoppath
3602 3605
3603 3606 @command('parents',
3604 3607 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3605 3608 ] + templateopts,
3606 3609 _('[-r REV] [FILE]'),
3607 3610 inferrepo=True)
3608 3611 def parents(ui, repo, file_=None, **opts):
3609 3612 """show the parents of the working directory or revision (DEPRECATED)
3610 3613
3611 3614 Print the working directory's parent revisions. If a revision is
3612 3615 given via -r/--rev, the parent of that revision will be printed.
3613 3616 If a file argument is given, the revision in which the file was
3614 3617 last changed (before the working directory revision or the
3615 3618 argument to --rev if given) is printed.
3616 3619
3617 3620 This command is equivalent to::
3618 3621
3619 3622 hg log -r "p1()+p2()" or
3620 3623 hg log -r "p1(REV)+p2(REV)" or
3621 3624 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3622 3625 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3623 3626
3624 3627 See :hg:`summary` and :hg:`help revsets` for related information.
3625 3628
3626 3629 Returns 0 on success.
3627 3630 """
3628 3631
3629 3632 opts = pycompat.byteskwargs(opts)
3630 3633 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3631 3634
3632 3635 if file_:
3633 3636 m = scmutil.match(ctx, (file_,), opts)
3634 3637 if m.anypats() or len(m.files()) != 1:
3635 3638 raise error.Abort(_('can only specify an explicit filename'))
3636 3639 file_ = m.files()[0]
3637 3640 filenodes = []
3638 3641 for cp in ctx.parents():
3639 3642 if not cp:
3640 3643 continue
3641 3644 try:
3642 3645 filenodes.append(cp.filenode(file_))
3643 3646 except error.LookupError:
3644 3647 pass
3645 3648 if not filenodes:
3646 3649 raise error.Abort(_("'%s' not found in manifest!") % file_)
3647 3650 p = []
3648 3651 for fn in filenodes:
3649 3652 fctx = repo.filectx(file_, fileid=fn)
3650 3653 p.append(fctx.node())
3651 3654 else:
3652 3655 p = [cp.node() for cp in ctx.parents()]
3653 3656
3654 3657 displayer = cmdutil.show_changeset(ui, repo, opts)
3655 3658 for n in p:
3656 3659 if n != nullid:
3657 3660 displayer.show(repo[n])
3658 3661 displayer.close()
3659 3662
3660 3663 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
3661 3664 def paths(ui, repo, search=None, **opts):
3662 3665 """show aliases for remote repositories
3663 3666
3664 3667 Show definition of symbolic path name NAME. If no name is given,
3665 3668 show definition of all available names.
3666 3669
3667 3670 Option -q/--quiet suppresses all output when searching for NAME
3668 3671 and shows only the path names when listing all definitions.
3669 3672
3670 3673 Path names are defined in the [paths] section of your
3671 3674 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3672 3675 repository, ``.hg/hgrc`` is used, too.
3673 3676
3674 3677 The path names ``default`` and ``default-push`` have a special
3675 3678 meaning. When performing a push or pull operation, they are used
3676 3679 as fallbacks if no location is specified on the command-line.
3677 3680 When ``default-push`` is set, it will be used for push and
3678 3681 ``default`` will be used for pull; otherwise ``default`` is used
3679 3682 as the fallback for both. When cloning a repository, the clone
3680 3683 source is written as ``default`` in ``.hg/hgrc``.
3681 3684
3682 3685 .. note::
3683 3686
3684 3687 ``default`` and ``default-push`` apply to all inbound (e.g.
3685 3688 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3686 3689 and :hg:`bundle`) operations.
3687 3690
3688 3691 See :hg:`help urls` for more information.
3689 3692
3690 3693 Returns 0 on success.
3691 3694 """
3692 3695
3693 3696 opts = pycompat.byteskwargs(opts)
3694 3697 ui.pager('paths')
3695 3698 if search:
3696 3699 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3697 3700 if name == search]
3698 3701 else:
3699 3702 pathitems = sorted(ui.paths.iteritems())
3700 3703
3701 3704 fm = ui.formatter('paths', opts)
3702 3705 if fm.isplain():
3703 3706 hidepassword = util.hidepassword
3704 3707 else:
3705 3708 hidepassword = str
3706 3709 if ui.quiet:
3707 3710 namefmt = '%s\n'
3708 3711 else:
3709 3712 namefmt = '%s = '
3710 3713 showsubopts = not search and not ui.quiet
3711 3714
3712 3715 for name, path in pathitems:
3713 3716 fm.startitem()
3714 3717 fm.condwrite(not search, 'name', namefmt, name)
3715 3718 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3716 3719 for subopt, value in sorted(path.suboptions.items()):
3717 3720 assert subopt not in ('name', 'url')
3718 3721 if showsubopts:
3719 3722 fm.plain('%s:%s = ' % (name, subopt))
3720 3723 fm.condwrite(showsubopts, subopt, '%s\n', value)
3721 3724
3722 3725 fm.end()
3723 3726
3724 3727 if search and not pathitems:
3725 3728 if not ui.quiet:
3726 3729 ui.warn(_("not found!\n"))
3727 3730 return 1
3728 3731 else:
3729 3732 return 0
3730 3733
3731 3734 @command('phase',
3732 3735 [('p', 'public', False, _('set changeset phase to public')),
3733 3736 ('d', 'draft', False, _('set changeset phase to draft')),
3734 3737 ('s', 'secret', False, _('set changeset phase to secret')),
3735 3738 ('f', 'force', False, _('allow to move boundary backward')),
3736 3739 ('r', 'rev', [], _('target revision'), _('REV')),
3737 3740 ],
3738 3741 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3739 3742 def phase(ui, repo, *revs, **opts):
3740 3743 """set or show the current phase name
3741 3744
3742 3745 With no argument, show the phase name of the current revision(s).
3743 3746
3744 3747 With one of -p/--public, -d/--draft or -s/--secret, change the
3745 3748 phase value of the specified revisions.
3746 3749
3747 3750 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
3748 3751 lower phase to an higher phase. Phases are ordered as follows::
3749 3752
3750 3753 public < draft < secret
3751 3754
3752 3755 Returns 0 on success, 1 if some phases could not be changed.
3753 3756
3754 3757 (For more information about the phases concept, see :hg:`help phases`.)
3755 3758 """
3756 3759 opts = pycompat.byteskwargs(opts)
3757 3760 # search for a unique phase argument
3758 3761 targetphase = None
3759 3762 for idx, name in enumerate(phases.phasenames):
3760 3763 if opts[name]:
3761 3764 if targetphase is not None:
3762 3765 raise error.Abort(_('only one phase can be specified'))
3763 3766 targetphase = idx
3764 3767
3765 3768 # look for specified revision
3766 3769 revs = list(revs)
3767 3770 revs.extend(opts['rev'])
3768 3771 if not revs:
3769 3772 # display both parents as the second parent phase can influence
3770 3773 # the phase of a merge commit
3771 3774 revs = [c.rev() for c in repo[None].parents()]
3772 3775
3773 3776 revs = scmutil.revrange(repo, revs)
3774 3777
3775 3778 lock = None
3776 3779 ret = 0
3777 3780 if targetphase is None:
3778 3781 # display
3779 3782 for r in revs:
3780 3783 ctx = repo[r]
3781 3784 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3782 3785 else:
3783 3786 tr = None
3784 3787 lock = repo.lock()
3785 3788 try:
3786 3789 tr = repo.transaction("phase")
3787 3790 # set phase
3788 3791 if not revs:
3789 3792 raise error.Abort(_('empty revision set'))
3790 3793 nodes = [repo[r].node() for r in revs]
3791 3794 # moving revision from public to draft may hide them
3792 3795 # We have to check result on an unfiltered repository
3793 3796 unfi = repo.unfiltered()
3794 3797 getphase = unfi._phasecache.phase
3795 3798 olddata = [getphase(unfi, r) for r in unfi]
3796 3799 phases.advanceboundary(repo, tr, targetphase, nodes)
3797 3800 if opts['force']:
3798 3801 phases.retractboundary(repo, tr, targetphase, nodes)
3799 3802 tr.close()
3800 3803 finally:
3801 3804 if tr is not None:
3802 3805 tr.release()
3803 3806 lock.release()
3804 3807 getphase = unfi._phasecache.phase
3805 3808 newdata = [getphase(unfi, r) for r in unfi]
3806 3809 changes = sum(newdata[r] != olddata[r] for r in unfi)
3807 3810 cl = unfi.changelog
3808 3811 rejected = [n for n in nodes
3809 3812 if newdata[cl.rev(n)] < targetphase]
3810 3813 if rejected:
3811 3814 ui.warn(_('cannot move %i changesets to a higher '
3812 3815 'phase, use --force\n') % len(rejected))
3813 3816 ret = 1
3814 3817 if changes:
3815 3818 msg = _('phase changed for %i changesets\n') % changes
3816 3819 if ret:
3817 3820 ui.status(msg)
3818 3821 else:
3819 3822 ui.note(msg)
3820 3823 else:
3821 3824 ui.warn(_('no phases changed\n'))
3822 3825 return ret
3823 3826
3824 3827 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3825 3828 """Run after a changegroup has been added via pull/unbundle
3826 3829
3827 3830 This takes arguments below:
3828 3831
3829 3832 :modheads: change of heads by pull/unbundle
3830 3833 :optupdate: updating working directory is needed or not
3831 3834 :checkout: update destination revision (or None to default destination)
3832 3835 :brev: a name, which might be a bookmark to be activated after updating
3833 3836 """
3834 3837 if modheads == 0:
3835 3838 return
3836 3839 if optupdate:
3837 3840 try:
3838 3841 return hg.updatetotally(ui, repo, checkout, brev)
3839 3842 except error.UpdateAbort as inst:
3840 3843 msg = _("not updating: %s") % str(inst)
3841 3844 hint = inst.hint
3842 3845 raise error.UpdateAbort(msg, hint=hint)
3843 3846 if modheads > 1:
3844 3847 currentbranchheads = len(repo.branchheads())
3845 3848 if currentbranchheads == modheads:
3846 3849 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3847 3850 elif currentbranchheads > 1:
3848 3851 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3849 3852 "merge)\n"))
3850 3853 else:
3851 3854 ui.status(_("(run 'hg heads' to see heads)\n"))
3852 3855 elif not ui.configbool('commands', 'update.requiredest'):
3853 3856 ui.status(_("(run 'hg update' to get a working copy)\n"))
3854 3857
3855 3858 @command('^pull',
3856 3859 [('u', 'update', None,
3857 3860 _('update to new branch head if changesets were pulled')),
3858 3861 ('f', 'force', None, _('run even when remote repository is unrelated')),
3859 3862 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3860 3863 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3861 3864 ('b', 'branch', [], _('a specific branch you would like to pull'),
3862 3865 _('BRANCH')),
3863 3866 ] + remoteopts,
3864 3867 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3865 3868 def pull(ui, repo, source="default", **opts):
3866 3869 """pull changes from the specified source
3867 3870
3868 3871 Pull changes from a remote repository to a local one.
3869 3872
3870 3873 This finds all changes from the repository at the specified path
3871 3874 or URL and adds them to a local repository (the current one unless
3872 3875 -R is specified). By default, this does not update the copy of the
3873 3876 project in the working directory.
3874 3877
3875 3878 Use :hg:`incoming` if you want to see what would have been added
3876 3879 by a pull at the time you issued this command. If you then decide
3877 3880 to add those changes to the repository, you should use :hg:`pull
3878 3881 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3879 3882
3880 3883 If SOURCE is omitted, the 'default' path will be used.
3881 3884 See :hg:`help urls` for more information.
3882 3885
3883 3886 Specifying bookmark as ``.`` is equivalent to specifying the active
3884 3887 bookmark's name.
3885 3888
3886 3889 Returns 0 on success, 1 if an update had unresolved files.
3887 3890 """
3888 3891
3889 3892 opts = pycompat.byteskwargs(opts)
3890 3893 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3891 3894 msg = _('update destination required by configuration')
3892 3895 hint = _('use hg pull followed by hg update DEST')
3893 3896 raise error.Abort(msg, hint=hint)
3894 3897
3895 3898 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3896 3899 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3897 3900 other = hg.peer(repo, opts, source)
3898 3901 try:
3899 3902 revs, checkout = hg.addbranchrevs(repo, other, branches,
3900 3903 opts.get('rev'))
3901 3904
3902 3905
3903 3906 pullopargs = {}
3904 3907 if opts.get('bookmark'):
3905 3908 if not revs:
3906 3909 revs = []
3907 3910 # The list of bookmark used here is not the one used to actually
3908 3911 # update the bookmark name. This can result in the revision pulled
3909 3912 # not ending up with the name of the bookmark because of a race
3910 3913 # condition on the server. (See issue 4689 for details)
3911 3914 remotebookmarks = other.listkeys('bookmarks')
3912 3915 pullopargs['remotebookmarks'] = remotebookmarks
3913 3916 for b in opts['bookmark']:
3914 3917 b = repo._bookmarks.expandname(b)
3915 3918 if b not in remotebookmarks:
3916 3919 raise error.Abort(_('remote bookmark %s not found!') % b)
3917 3920 revs.append(remotebookmarks[b])
3918 3921
3919 3922 if revs:
3920 3923 try:
3921 3924 # When 'rev' is a bookmark name, we cannot guarantee that it
3922 3925 # will be updated with that name because of a race condition
3923 3926 # server side. (See issue 4689 for details)
3924 3927 oldrevs = revs
3925 3928 revs = [] # actually, nodes
3926 3929 for r in oldrevs:
3927 3930 node = other.lookup(r)
3928 3931 revs.append(node)
3929 3932 if r == checkout:
3930 3933 checkout = node
3931 3934 except error.CapabilityError:
3932 3935 err = _("other repository doesn't support revision lookup, "
3933 3936 "so a rev cannot be specified.")
3934 3937 raise error.Abort(err)
3935 3938
3936 3939 pullopargs.update(opts.get('opargs', {}))
3937 3940 modheads = exchange.pull(repo, other, heads=revs,
3938 3941 force=opts.get('force'),
3939 3942 bookmarks=opts.get('bookmark', ()),
3940 3943 opargs=pullopargs).cgresult
3941 3944
3942 3945 # brev is a name, which might be a bookmark to be activated at
3943 3946 # the end of the update. In other words, it is an explicit
3944 3947 # destination of the update
3945 3948 brev = None
3946 3949
3947 3950 if checkout:
3948 3951 checkout = str(repo.changelog.rev(checkout))
3949 3952
3950 3953 # order below depends on implementation of
3951 3954 # hg.addbranchrevs(). opts['bookmark'] is ignored,
3952 3955 # because 'checkout' is determined without it.
3953 3956 if opts.get('rev'):
3954 3957 brev = opts['rev'][0]
3955 3958 elif opts.get('branch'):
3956 3959 brev = opts['branch'][0]
3957 3960 else:
3958 3961 brev = branches[0]
3959 3962 repo._subtoppath = source
3960 3963 try:
3961 3964 ret = postincoming(ui, repo, modheads, opts.get('update'),
3962 3965 checkout, brev)
3963 3966
3964 3967 finally:
3965 3968 del repo._subtoppath
3966 3969
3967 3970 finally:
3968 3971 other.close()
3969 3972 return ret
3970 3973
3971 3974 @command('^push',
3972 3975 [('f', 'force', None, _('force push')),
3973 3976 ('r', 'rev', [],
3974 3977 _('a changeset intended to be included in the destination'),
3975 3978 _('REV')),
3976 3979 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
3977 3980 ('b', 'branch', [],
3978 3981 _('a specific branch you would like to push'), _('BRANCH')),
3979 3982 ('', 'new-branch', False, _('allow pushing a new branch')),
3980 3983 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
3981 3984 ] + remoteopts,
3982 3985 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
3983 3986 def push(ui, repo, dest=None, **opts):
3984 3987 """push changes to the specified destination
3985 3988
3986 3989 Push changesets from the local repository to the specified
3987 3990 destination.
3988 3991
3989 3992 This operation is symmetrical to pull: it is identical to a pull
3990 3993 in the destination repository from the current one.
3991 3994
3992 3995 By default, push will not allow creation of new heads at the
3993 3996 destination, since multiple heads would make it unclear which head
3994 3997 to use. In this situation, it is recommended to pull and merge
3995 3998 before pushing.
3996 3999
3997 4000 Use --new-branch if you want to allow push to create a new named
3998 4001 branch that is not present at the destination. This allows you to
3999 4002 only create a new branch without forcing other changes.
4000 4003
4001 4004 .. note::
4002 4005
4003 4006 Extra care should be taken with the -f/--force option,
4004 4007 which will push all new heads on all branches, an action which will
4005 4008 almost always cause confusion for collaborators.
4006 4009
4007 4010 If -r/--rev is used, the specified revision and all its ancestors
4008 4011 will be pushed to the remote repository.
4009 4012
4010 4013 If -B/--bookmark is used, the specified bookmarked revision, its
4011 4014 ancestors, and the bookmark will be pushed to the remote
4012 4015 repository. Specifying ``.`` is equivalent to specifying the active
4013 4016 bookmark's name.
4014 4017
4015 4018 Please see :hg:`help urls` for important details about ``ssh://``
4016 4019 URLs. If DESTINATION is omitted, a default path will be used.
4017 4020
4018 4021 .. container:: verbose
4019 4022
4020 4023 The --pushvars option sends strings to the server that become
4021 4024 environment variables prepended with ``HG_USERVAR_``. For example,
4022 4025 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4023 4026 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4024 4027
4025 4028 pushvars can provide for user-overridable hooks as well as set debug
4026 4029 levels. One example is having a hook that blocks commits containing
4027 4030 conflict markers, but enables the user to override the hook if the file
4028 4031 is using conflict markers for testing purposes or the file format has
4029 4032 strings that look like conflict markers.
4030 4033
4031 4034 By default, servers will ignore `--pushvars`. To enable it add the
4032 4035 following to your configuration file::
4033 4036
4034 4037 [push]
4035 4038 pushvars.server = true
4036 4039
4037 4040 Returns 0 if push was successful, 1 if nothing to push.
4038 4041 """
4039 4042
4040 4043 opts = pycompat.byteskwargs(opts)
4041 4044 if opts.get('bookmark'):
4042 4045 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4043 4046 for b in opts['bookmark']:
4044 4047 # translate -B options to -r so changesets get pushed
4045 4048 b = repo._bookmarks.expandname(b)
4046 4049 if b in repo._bookmarks:
4047 4050 opts.setdefault('rev', []).append(b)
4048 4051 else:
4049 4052 # if we try to push a deleted bookmark, translate it to null
4050 4053 # this lets simultaneous -r, -b options continue working
4051 4054 opts.setdefault('rev', []).append("null")
4052 4055
4053 4056 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4054 4057 if not path:
4055 4058 raise error.Abort(_('default repository not configured!'),
4056 4059 hint=_("see 'hg help config.paths'"))
4057 4060 dest = path.pushloc or path.loc
4058 4061 branches = (path.branch, opts.get('branch') or [])
4059 4062 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4060 4063 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4061 4064 other = hg.peer(repo, opts, dest)
4062 4065
4063 4066 if revs:
4064 4067 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4065 4068 if not revs:
4066 4069 raise error.Abort(_("specified revisions evaluate to an empty set"),
4067 4070 hint=_("use different revision arguments"))
4068 4071 elif path.pushrev:
4069 4072 # It doesn't make any sense to specify ancestor revisions. So limit
4070 4073 # to DAG heads to make discovery simpler.
4071 4074 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4072 4075 revs = scmutil.revrange(repo, [expr])
4073 4076 revs = [repo[rev].node() for rev in revs]
4074 4077 if not revs:
4075 4078 raise error.Abort(_('default push revset for path evaluates to an '
4076 4079 'empty set'))
4077 4080
4078 4081 repo._subtoppath = dest
4079 4082 try:
4080 4083 # push subrepos depth-first for coherent ordering
4081 4084 c = repo['']
4082 4085 subs = c.substate # only repos that are committed
4083 4086 for s in sorted(subs):
4084 4087 result = c.sub(s).push(opts)
4085 4088 if result == 0:
4086 4089 return not result
4087 4090 finally:
4088 4091 del repo._subtoppath
4089 4092
4090 4093 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4091 4094 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4092 4095
4093 4096 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4094 4097 newbranch=opts.get('new_branch'),
4095 4098 bookmarks=opts.get('bookmark', ()),
4096 4099 opargs=opargs)
4097 4100
4098 4101 result = not pushop.cgresult
4099 4102
4100 4103 if pushop.bkresult is not None:
4101 4104 if pushop.bkresult == 2:
4102 4105 result = 2
4103 4106 elif not result and pushop.bkresult:
4104 4107 result = 2
4105 4108
4106 4109 return result
4107 4110
4108 4111 @command('recover', [])
4109 4112 def recover(ui, repo):
4110 4113 """roll back an interrupted transaction
4111 4114
4112 4115 Recover from an interrupted commit or pull.
4113 4116
4114 4117 This command tries to fix the repository status after an
4115 4118 interrupted operation. It should only be necessary when Mercurial
4116 4119 suggests it.
4117 4120
4118 4121 Returns 0 if successful, 1 if nothing to recover or verify fails.
4119 4122 """
4120 4123 if repo.recover():
4121 4124 return hg.verify(repo)
4122 4125 return 1
4123 4126
4124 4127 @command('^remove|rm',
4125 4128 [('A', 'after', None, _('record delete for missing files')),
4126 4129 ('f', 'force', None,
4127 4130 _('forget added files, delete modified files')),
4128 4131 ] + subrepoopts + walkopts,
4129 4132 _('[OPTION]... FILE...'),
4130 4133 inferrepo=True)
4131 4134 def remove(ui, repo, *pats, **opts):
4132 4135 """remove the specified files on the next commit
4133 4136
4134 4137 Schedule the indicated files for removal from the current branch.
4135 4138
4136 4139 This command schedules the files to be removed at the next commit.
4137 4140 To undo a remove before that, see :hg:`revert`. To undo added
4138 4141 files, see :hg:`forget`.
4139 4142
4140 4143 .. container:: verbose
4141 4144
4142 4145 -A/--after can be used to remove only files that have already
4143 4146 been deleted, -f/--force can be used to force deletion, and -Af
4144 4147 can be used to remove files from the next revision without
4145 4148 deleting them from the working directory.
4146 4149
4147 4150 The following table details the behavior of remove for different
4148 4151 file states (columns) and option combinations (rows). The file
4149 4152 states are Added [A], Clean [C], Modified [M] and Missing [!]
4150 4153 (as reported by :hg:`status`). The actions are Warn, Remove
4151 4154 (from branch) and Delete (from disk):
4152 4155
4153 4156 ========= == == == ==
4154 4157 opt/state A C M !
4155 4158 ========= == == == ==
4156 4159 none W RD W R
4157 4160 -f R RD RD R
4158 4161 -A W W W R
4159 4162 -Af R R R R
4160 4163 ========= == == == ==
4161 4164
4162 4165 .. note::
4163 4166
4164 4167 :hg:`remove` never deletes files in Added [A] state from the
4165 4168 working directory, not even if ``--force`` is specified.
4166 4169
4167 4170 Returns 0 on success, 1 if any warnings encountered.
4168 4171 """
4169 4172
4170 4173 opts = pycompat.byteskwargs(opts)
4171 4174 after, force = opts.get('after'), opts.get('force')
4172 4175 if not pats and not after:
4173 4176 raise error.Abort(_('no files specified'))
4174 4177
4175 4178 m = scmutil.match(repo[None], pats, opts)
4176 4179 subrepos = opts.get('subrepos')
4177 4180 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4178 4181
4179 4182 @command('rename|move|mv',
4180 4183 [('A', 'after', None, _('record a rename that has already occurred')),
4181 4184 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4182 4185 ] + walkopts + dryrunopts,
4183 4186 _('[OPTION]... SOURCE... DEST'))
4184 4187 def rename(ui, repo, *pats, **opts):
4185 4188 """rename files; equivalent of copy + remove
4186 4189
4187 4190 Mark dest as copies of sources; mark sources for deletion. If dest
4188 4191 is a directory, copies are put in that directory. If dest is a
4189 4192 file, there can only be one source.
4190 4193
4191 4194 By default, this command copies the contents of files as they
4192 4195 exist in the working directory. If invoked with -A/--after, the
4193 4196 operation is recorded, but no copying is performed.
4194 4197
4195 4198 This command takes effect at the next commit. To undo a rename
4196 4199 before that, see :hg:`revert`.
4197 4200
4198 4201 Returns 0 on success, 1 if errors are encountered.
4199 4202 """
4200 4203 opts = pycompat.byteskwargs(opts)
4201 4204 with repo.wlock(False):
4202 4205 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4203 4206
4204 4207 @command('resolve',
4205 4208 [('a', 'all', None, _('select all unresolved files')),
4206 4209 ('l', 'list', None, _('list state of files needing merge')),
4207 4210 ('m', 'mark', None, _('mark files as resolved')),
4208 4211 ('u', 'unmark', None, _('mark files as unresolved')),
4209 4212 ('n', 'no-status', None, _('hide status prefix'))]
4210 4213 + mergetoolopts + walkopts + formatteropts,
4211 4214 _('[OPTION]... [FILE]...'),
4212 4215 inferrepo=True)
4213 4216 def resolve(ui, repo, *pats, **opts):
4214 4217 """redo merges or set/view the merge status of files
4215 4218
4216 4219 Merges with unresolved conflicts are often the result of
4217 4220 non-interactive merging using the ``internal:merge`` configuration
4218 4221 setting, or a command-line merge tool like ``diff3``. The resolve
4219 4222 command is used to manage the files involved in a merge, after
4220 4223 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4221 4224 working directory must have two parents). See :hg:`help
4222 4225 merge-tools` for information on configuring merge tools.
4223 4226
4224 4227 The resolve command can be used in the following ways:
4225 4228
4226 4229 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4227 4230 files, discarding any previous merge attempts. Re-merging is not
4228 4231 performed for files already marked as resolved. Use ``--all/-a``
4229 4232 to select all unresolved files. ``--tool`` can be used to specify
4230 4233 the merge tool used for the given files. It overrides the HGMERGE
4231 4234 environment variable and your configuration files. Previous file
4232 4235 contents are saved with a ``.orig`` suffix.
4233 4236
4234 4237 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4235 4238 (e.g. after having manually fixed-up the files). The default is
4236 4239 to mark all unresolved files.
4237 4240
4238 4241 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4239 4242 default is to mark all resolved files.
4240 4243
4241 4244 - :hg:`resolve -l`: list files which had or still have conflicts.
4242 4245 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4243 4246 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4244 4247 the list. See :hg:`help filesets` for details.
4245 4248
4246 4249 .. note::
4247 4250
4248 4251 Mercurial will not let you commit files with unresolved merge
4249 4252 conflicts. You must use :hg:`resolve -m ...` before you can
4250 4253 commit after a conflicting merge.
4251 4254
4252 4255 Returns 0 on success, 1 if any files fail a resolve attempt.
4253 4256 """
4254 4257
4255 4258 opts = pycompat.byteskwargs(opts)
4256 4259 flaglist = 'all mark unmark list no_status'.split()
4257 4260 all, mark, unmark, show, nostatus = \
4258 4261 [opts.get(o) for o in flaglist]
4259 4262
4260 4263 if (show and (mark or unmark)) or (mark and unmark):
4261 4264 raise error.Abort(_("too many options specified"))
4262 4265 if pats and all:
4263 4266 raise error.Abort(_("can't specify --all and patterns"))
4264 4267 if not (all or pats or show or mark or unmark):
4265 4268 raise error.Abort(_('no files or directories specified'),
4266 4269 hint=('use --all to re-merge all unresolved files'))
4267 4270
4268 4271 if show:
4269 4272 ui.pager('resolve')
4270 4273 fm = ui.formatter('resolve', opts)
4271 4274 ms = mergemod.mergestate.read(repo)
4272 4275 m = scmutil.match(repo[None], pats, opts)
4273 4276 for f in ms:
4274 4277 if not m(f):
4275 4278 continue
4276 4279 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
4277 4280 'd': 'driverresolved'}[ms[f]]
4278 4281 fm.startitem()
4279 4282 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
4280 4283 fm.write('path', '%s\n', f, label=l)
4281 4284 fm.end()
4282 4285 return 0
4283 4286
4284 4287 with repo.wlock():
4285 4288 ms = mergemod.mergestate.read(repo)
4286 4289
4287 4290 if not (ms.active() or repo.dirstate.p2() != nullid):
4288 4291 raise error.Abort(
4289 4292 _('resolve command not applicable when not merging'))
4290 4293
4291 4294 wctx = repo[None]
4292 4295
4293 4296 if ms.mergedriver and ms.mdstate() == 'u':
4294 4297 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4295 4298 ms.commit()
4296 4299 # allow mark and unmark to go through
4297 4300 if not mark and not unmark and not proceed:
4298 4301 return 1
4299 4302
4300 4303 m = scmutil.match(wctx, pats, opts)
4301 4304 ret = 0
4302 4305 didwork = False
4303 4306 runconclude = False
4304 4307
4305 4308 tocomplete = []
4306 4309 for f in ms:
4307 4310 if not m(f):
4308 4311 continue
4309 4312
4310 4313 didwork = True
4311 4314
4312 4315 # don't let driver-resolved files be marked, and run the conclude
4313 4316 # step if asked to resolve
4314 4317 if ms[f] == "d":
4315 4318 exact = m.exact(f)
4316 4319 if mark:
4317 4320 if exact:
4318 4321 ui.warn(_('not marking %s as it is driver-resolved\n')
4319 4322 % f)
4320 4323 elif unmark:
4321 4324 if exact:
4322 4325 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4323 4326 % f)
4324 4327 else:
4325 4328 runconclude = True
4326 4329 continue
4327 4330
4328 4331 if mark:
4329 4332 ms.mark(f, "r")
4330 4333 elif unmark:
4331 4334 ms.mark(f, "u")
4332 4335 else:
4333 4336 # backup pre-resolve (merge uses .orig for its own purposes)
4334 4337 a = repo.wjoin(f)
4335 4338 try:
4336 4339 util.copyfile(a, a + ".resolve")
4337 4340 except (IOError, OSError) as inst:
4338 4341 if inst.errno != errno.ENOENT:
4339 4342 raise
4340 4343
4341 4344 try:
4342 4345 # preresolve file
4343 4346 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4344 4347 'resolve')
4345 4348 complete, r = ms.preresolve(f, wctx)
4346 4349 if not complete:
4347 4350 tocomplete.append(f)
4348 4351 elif r:
4349 4352 ret = 1
4350 4353 finally:
4351 4354 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4352 4355 ms.commit()
4353 4356
4354 4357 # replace filemerge's .orig file with our resolve file, but only
4355 4358 # for merges that are complete
4356 4359 if complete:
4357 4360 try:
4358 4361 util.rename(a + ".resolve",
4359 4362 scmutil.origpath(ui, repo, a))
4360 4363 except OSError as inst:
4361 4364 if inst.errno != errno.ENOENT:
4362 4365 raise
4363 4366
4364 4367 for f in tocomplete:
4365 4368 try:
4366 4369 # resolve file
4367 4370 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4368 4371 'resolve')
4369 4372 r = ms.resolve(f, wctx)
4370 4373 if r:
4371 4374 ret = 1
4372 4375 finally:
4373 4376 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4374 4377 ms.commit()
4375 4378
4376 4379 # replace filemerge's .orig file with our resolve file
4377 4380 a = repo.wjoin(f)
4378 4381 try:
4379 4382 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4380 4383 except OSError as inst:
4381 4384 if inst.errno != errno.ENOENT:
4382 4385 raise
4383 4386
4384 4387 ms.commit()
4385 4388 ms.recordactions()
4386 4389
4387 4390 if not didwork and pats:
4388 4391 hint = None
4389 4392 if not any([p for p in pats if p.find(':') >= 0]):
4390 4393 pats = ['path:%s' % p for p in pats]
4391 4394 m = scmutil.match(wctx, pats, opts)
4392 4395 for f in ms:
4393 4396 if not m(f):
4394 4397 continue
4395 4398 flags = ''.join(['-%s ' % o[0] for o in flaglist
4396 4399 if opts.get(o)])
4397 4400 hint = _("(try: hg resolve %s%s)\n") % (
4398 4401 flags,
4399 4402 ' '.join(pats))
4400 4403 break
4401 4404 ui.warn(_("arguments do not match paths that need resolving\n"))
4402 4405 if hint:
4403 4406 ui.warn(hint)
4404 4407 elif ms.mergedriver and ms.mdstate() != 's':
4405 4408 # run conclude step when either a driver-resolved file is requested
4406 4409 # or there are no driver-resolved files
4407 4410 # we can't use 'ret' to determine whether any files are unresolved
4408 4411 # because we might not have tried to resolve some
4409 4412 if ((runconclude or not list(ms.driverresolved()))
4410 4413 and not list(ms.unresolved())):
4411 4414 proceed = mergemod.driverconclude(repo, ms, wctx)
4412 4415 ms.commit()
4413 4416 if not proceed:
4414 4417 return 1
4415 4418
4416 4419 # Nudge users into finishing an unfinished operation
4417 4420 unresolvedf = list(ms.unresolved())
4418 4421 driverresolvedf = list(ms.driverresolved())
4419 4422 if not unresolvedf and not driverresolvedf:
4420 4423 ui.status(_('(no more unresolved files)\n'))
4421 4424 cmdutil.checkafterresolved(repo)
4422 4425 elif not unresolvedf:
4423 4426 ui.status(_('(no more unresolved files -- '
4424 4427 'run "hg resolve --all" to conclude)\n'))
4425 4428
4426 4429 return ret
4427 4430
4428 4431 @command('revert',
4429 4432 [('a', 'all', None, _('revert all changes when no arguments given')),
4430 4433 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4431 4434 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4432 4435 ('C', 'no-backup', None, _('do not save backup copies of files')),
4433 4436 ('i', 'interactive', None,
4434 4437 _('interactively select the changes (EXPERIMENTAL)')),
4435 4438 ] + walkopts + dryrunopts,
4436 4439 _('[OPTION]... [-r REV] [NAME]...'))
4437 4440 def revert(ui, repo, *pats, **opts):
4438 4441 """restore files to their checkout state
4439 4442
4440 4443 .. note::
4441 4444
4442 4445 To check out earlier revisions, you should use :hg:`update REV`.
4443 4446 To cancel an uncommitted merge (and lose your changes),
4444 4447 use :hg:`update --clean .`.
4445 4448
4446 4449 With no revision specified, revert the specified files or directories
4447 4450 to the contents they had in the parent of the working directory.
4448 4451 This restores the contents of files to an unmodified
4449 4452 state and unschedules adds, removes, copies, and renames. If the
4450 4453 working directory has two parents, you must explicitly specify a
4451 4454 revision.
4452 4455
4453 4456 Using the -r/--rev or -d/--date options, revert the given files or
4454 4457 directories to their states as of a specific revision. Because
4455 4458 revert does not change the working directory parents, this will
4456 4459 cause these files to appear modified. This can be helpful to "back
4457 4460 out" some or all of an earlier change. See :hg:`backout` for a
4458 4461 related method.
4459 4462
4460 4463 Modified files are saved with a .orig suffix before reverting.
4461 4464 To disable these backups, use --no-backup. It is possible to store
4462 4465 the backup files in a custom directory relative to the root of the
4463 4466 repository by setting the ``ui.origbackuppath`` configuration
4464 4467 option.
4465 4468
4466 4469 See :hg:`help dates` for a list of formats valid for -d/--date.
4467 4470
4468 4471 See :hg:`help backout` for a way to reverse the effect of an
4469 4472 earlier changeset.
4470 4473
4471 4474 Returns 0 on success.
4472 4475 """
4473 4476
4474 4477 if opts.get("date"):
4475 4478 if opts.get("rev"):
4476 4479 raise error.Abort(_("you can't specify a revision and a date"))
4477 4480 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4478 4481
4479 4482 parent, p2 = repo.dirstate.parents()
4480 4483 if not opts.get('rev') and p2 != nullid:
4481 4484 # revert after merge is a trap for new users (issue2915)
4482 4485 raise error.Abort(_('uncommitted merge with no revision specified'),
4483 4486 hint=_("use 'hg update' or see 'hg help revert'"))
4484 4487
4485 4488 ctx = scmutil.revsingle(repo, opts.get('rev'))
4486 4489
4487 4490 if (not (pats or opts.get('include') or opts.get('exclude') or
4488 4491 opts.get('all') or opts.get('interactive'))):
4489 4492 msg = _("no files or directories specified")
4490 4493 if p2 != nullid:
4491 4494 hint = _("uncommitted merge, use --all to discard all changes,"
4492 4495 " or 'hg update -C .' to abort the merge")
4493 4496 raise error.Abort(msg, hint=hint)
4494 4497 dirty = any(repo.status())
4495 4498 node = ctx.node()
4496 4499 if node != parent:
4497 4500 if dirty:
4498 4501 hint = _("uncommitted changes, use --all to discard all"
4499 4502 " changes, or 'hg update %s' to update") % ctx.rev()
4500 4503 else:
4501 4504 hint = _("use --all to revert all files,"
4502 4505 " or 'hg update %s' to update") % ctx.rev()
4503 4506 elif dirty:
4504 4507 hint = _("uncommitted changes, use --all to discard all changes")
4505 4508 else:
4506 4509 hint = _("use --all to revert all files")
4507 4510 raise error.Abort(msg, hint=hint)
4508 4511
4509 4512 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4510 4513
4511 4514 @command('rollback', dryrunopts +
4512 4515 [('f', 'force', False, _('ignore safety measures'))])
4513 4516 def rollback(ui, repo, **opts):
4514 4517 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4515 4518
4516 4519 Please use :hg:`commit --amend` instead of rollback to correct
4517 4520 mistakes in the last commit.
4518 4521
4519 4522 This command should be used with care. There is only one level of
4520 4523 rollback, and there is no way to undo a rollback. It will also
4521 4524 restore the dirstate at the time of the last transaction, losing
4522 4525 any dirstate changes since that time. This command does not alter
4523 4526 the working directory.
4524 4527
4525 4528 Transactions are used to encapsulate the effects of all commands
4526 4529 that create new changesets or propagate existing changesets into a
4527 4530 repository.
4528 4531
4529 4532 .. container:: verbose
4530 4533
4531 4534 For example, the following commands are transactional, and their
4532 4535 effects can be rolled back:
4533 4536
4534 4537 - commit
4535 4538 - import
4536 4539 - pull
4537 4540 - push (with this repository as the destination)
4538 4541 - unbundle
4539 4542
4540 4543 To avoid permanent data loss, rollback will refuse to rollback a
4541 4544 commit transaction if it isn't checked out. Use --force to
4542 4545 override this protection.
4543 4546
4544 4547 The rollback command can be entirely disabled by setting the
4545 4548 ``ui.rollback`` configuration setting to false. If you're here
4546 4549 because you want to use rollback and it's disabled, you can
4547 4550 re-enable the command by setting ``ui.rollback`` to true.
4548 4551
4549 4552 This command is not intended for use on public repositories. Once
4550 4553 changes are visible for pull by other users, rolling a transaction
4551 4554 back locally is ineffective (someone else may already have pulled
4552 4555 the changes). Furthermore, a race is possible with readers of the
4553 4556 repository; for example an in-progress pull from the repository
4554 4557 may fail if a rollback is performed.
4555 4558
4556 4559 Returns 0 on success, 1 if no rollback data is available.
4557 4560 """
4558 4561 if not ui.configbool('ui', 'rollback'):
4559 4562 raise error.Abort(_('rollback is disabled because it is unsafe'),
4560 4563 hint=('see `hg help -v rollback` for information'))
4561 4564 return repo.rollback(dryrun=opts.get(r'dry_run'),
4562 4565 force=opts.get(r'force'))
4563 4566
4564 4567 @command('root', [])
4565 4568 def root(ui, repo):
4566 4569 """print the root (top) of the current working directory
4567 4570
4568 4571 Print the root directory of the current repository.
4569 4572
4570 4573 Returns 0 on success.
4571 4574 """
4572 4575 ui.write(repo.root + "\n")
4573 4576
4574 4577 @command('^serve',
4575 4578 [('A', 'accesslog', '', _('name of access log file to write to'),
4576 4579 _('FILE')),
4577 4580 ('d', 'daemon', None, _('run server in background')),
4578 4581 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4579 4582 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4580 4583 # use string type, then we can check if something was passed
4581 4584 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4582 4585 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4583 4586 _('ADDR')),
4584 4587 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4585 4588 _('PREFIX')),
4586 4589 ('n', 'name', '',
4587 4590 _('name to show in web pages (default: working directory)'), _('NAME')),
4588 4591 ('', 'web-conf', '',
4589 4592 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4590 4593 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4591 4594 _('FILE')),
4592 4595 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4593 4596 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4594 4597 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4595 4598 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4596 4599 ('', 'style', '', _('template style to use'), _('STYLE')),
4597 4600 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4598 4601 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4599 4602 + subrepoopts,
4600 4603 _('[OPTION]...'),
4601 4604 optionalrepo=True)
4602 4605 def serve(ui, repo, **opts):
4603 4606 """start stand-alone webserver
4604 4607
4605 4608 Start a local HTTP repository browser and pull server. You can use
4606 4609 this for ad-hoc sharing and browsing of repositories. It is
4607 4610 recommended to use a real web server to serve a repository for
4608 4611 longer periods of time.
4609 4612
4610 4613 Please note that the server does not implement access control.
4611 4614 This means that, by default, anybody can read from the server and
4612 4615 nobody can write to it by default. Set the ``web.allow_push``
4613 4616 option to ``*`` to allow everybody to push to the server. You
4614 4617 should use a real web server if you need to authenticate users.
4615 4618
4616 4619 By default, the server logs accesses to stdout and errors to
4617 4620 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4618 4621 files.
4619 4622
4620 4623 To have the server choose a free port number to listen on, specify
4621 4624 a port number of 0; in this case, the server will print the port
4622 4625 number it uses.
4623 4626
4624 4627 Returns 0 on success.
4625 4628 """
4626 4629
4627 4630 opts = pycompat.byteskwargs(opts)
4628 4631 if opts["stdio"] and opts["cmdserver"]:
4629 4632 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4630 4633
4631 4634 if opts["stdio"]:
4632 4635 if repo is None:
4633 4636 raise error.RepoError(_("there is no Mercurial repository here"
4634 4637 " (.hg not found)"))
4635 4638 s = sshserver.sshserver(ui, repo)
4636 4639 s.serve_forever()
4637 4640
4638 4641 service = server.createservice(ui, repo, opts)
4639 4642 return server.runservice(opts, initfn=service.init, runfn=service.run)
4640 4643
4641 4644 @command('^status|st',
4642 4645 [('A', 'all', None, _('show status of all files')),
4643 4646 ('m', 'modified', None, _('show only modified files')),
4644 4647 ('a', 'added', None, _('show only added files')),
4645 4648 ('r', 'removed', None, _('show only removed files')),
4646 4649 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4647 4650 ('c', 'clean', None, _('show only files without changes')),
4648 4651 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4649 4652 ('i', 'ignored', None, _('show only ignored files')),
4650 4653 ('n', 'no-status', None, _('hide status prefix')),
4651 4654 ('t', 'terse', '', _('show the terse output (EXPERIMENTAL)')),
4652 4655 ('C', 'copies', None, _('show source of copied files')),
4653 4656 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4654 4657 ('', 'rev', [], _('show difference from revision'), _('REV')),
4655 4658 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4656 4659 ] + walkopts + subrepoopts + formatteropts,
4657 4660 _('[OPTION]... [FILE]...'),
4658 4661 inferrepo=True)
4659 4662 def status(ui, repo, *pats, **opts):
4660 4663 """show changed files in the working directory
4661 4664
4662 4665 Show status of files in the repository. If names are given, only
4663 4666 files that match are shown. Files that are clean or ignored or
4664 4667 the source of a copy/move operation, are not listed unless
4665 4668 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4666 4669 Unless options described with "show only ..." are given, the
4667 4670 options -mardu are used.
4668 4671
4669 4672 Option -q/--quiet hides untracked (unknown and ignored) files
4670 4673 unless explicitly requested with -u/--unknown or -i/--ignored.
4671 4674
4672 4675 .. note::
4673 4676
4674 4677 :hg:`status` may appear to disagree with diff if permissions have
4675 4678 changed or a merge has occurred. The standard diff format does
4676 4679 not report permission changes and diff only reports changes
4677 4680 relative to one merge parent.
4678 4681
4679 4682 If one revision is given, it is used as the base revision.
4680 4683 If two revisions are given, the differences between them are
4681 4684 shown. The --change option can also be used as a shortcut to list
4682 4685 the changed files of a revision from its first parent.
4683 4686
4684 4687 The codes used to show the status of files are::
4685 4688
4686 4689 M = modified
4687 4690 A = added
4688 4691 R = removed
4689 4692 C = clean
4690 4693 ! = missing (deleted by non-hg command, but still tracked)
4691 4694 ? = not tracked
4692 4695 I = ignored
4693 4696 = origin of the previous file (with --copies)
4694 4697
4695 4698 .. container:: verbose
4696 4699
4697 4700 The -t/--terse option abbreviates the output by showing directory name
4698 4701 if all the files in it share the same status. The option expects a value
4699 4702 which can be a string formed by using 'm', 'a', 'r', 'd', 'u', 'i', 'c'
4700 4703 where, 'm' stands for 'modified', 'a' for 'added', 'r' for 'removed',
4701 4704 'd' for 'deleted', 'u' for 'unknown', 'i' for 'ignored' and 'c' for clean.
4702 4705
4703 4706 It terses the output of only those status which are passed. The ignored
4704 4707 files are not considered while tersing until 'i' is there in --terse value
4705 4708 or the --ignored option is used.
4706 4709
4707 4710 --verbose option shows more context about the state of the repo
4708 4711 like the repository is in unfinised merge, shelve, rebase state etc.
4709 4712 You can have this behaviour turned on by default by following config:
4710 4713
4711 4714 [commands]
4712 4715 status.verbose = true
4713 4716
4714 4717 You can also skip some states like bisect by adding following in
4715 4718 configuration file.
4716 4719
4717 4720 [commands]
4718 4721 status.skipstates = bisect
4719 4722
4720 4723 Examples:
4721 4724
4722 4725 - show changes in the working directory relative to a
4723 4726 changeset::
4724 4727
4725 4728 hg status --rev 9353
4726 4729
4727 4730 - show changes in the working directory relative to the
4728 4731 current directory (see :hg:`help patterns` for more information)::
4729 4732
4730 4733 hg status re:
4731 4734
4732 4735 - show all changes including copies in an existing changeset::
4733 4736
4734 4737 hg status --copies --change 9353
4735 4738
4736 4739 - get a NUL separated list of added files, suitable for xargs::
4737 4740
4738 4741 hg status -an0
4739 4742
4740 4743 Returns 0 on success.
4741 4744 """
4742 4745
4743 4746 opts = pycompat.byteskwargs(opts)
4744 4747 revs = opts.get('rev')
4745 4748 change = opts.get('change')
4746 4749 terse = opts.get('terse')
4747 4750
4748 4751 if revs and change:
4749 4752 msg = _('cannot specify --rev and --change at the same time')
4750 4753 raise error.Abort(msg)
4751 4754 elif revs and terse:
4752 4755 msg = _('cannot use --terse with --rev')
4753 4756 raise error.Abort(msg)
4754 4757 elif change:
4755 4758 node2 = scmutil.revsingle(repo, change, None).node()
4756 4759 node1 = repo[node2].p1().node()
4757 4760 else:
4758 4761 node1, node2 = scmutil.revpair(repo, revs)
4759 4762
4760 4763 if pats or ui.configbool('commands', 'status.relative'):
4761 4764 cwd = repo.getcwd()
4762 4765 else:
4763 4766 cwd = ''
4764 4767
4765 4768 if opts.get('print0'):
4766 4769 end = '\0'
4767 4770 else:
4768 4771 end = '\n'
4769 4772 copy = {}
4770 4773 states = 'modified added removed deleted unknown ignored clean'.split()
4771 4774 show = [k for k in states if opts.get(k)]
4772 4775 if opts.get('all'):
4773 4776 show += ui.quiet and (states[:4] + ['clean']) or states
4774 4777
4775 4778 if not show:
4776 4779 if ui.quiet:
4777 4780 show = states[:4]
4778 4781 else:
4779 4782 show = states[:5]
4780 4783
4781 4784 m = scmutil.match(repo[node2], pats, opts)
4782 4785 stat = repo.status(node1, node2, m,
4783 4786 'ignored' in show, 'clean' in show, 'unknown' in show,
4784 4787 opts.get('subrepos'))
4785 4788 if terse:
4786 4789 stat = cmdutil.tersestatus(repo.root, stat, terse,
4787 4790 repo.dirstate._ignore, opts.get('ignored'))
4788 4791 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4789 4792
4790 4793 if (opts.get('all') or opts.get('copies')
4791 4794 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4792 4795 copy = copies.pathcopies(repo[node1], repo[node2], m)
4793 4796
4794 4797 ui.pager('status')
4795 4798 fm = ui.formatter('status', opts)
4796 4799 fmt = '%s' + end
4797 4800 showchar = not opts.get('no_status')
4798 4801
4799 4802 for state, char, files in changestates:
4800 4803 if state in show:
4801 4804 label = 'status.' + state
4802 4805 for f in files:
4803 4806 fm.startitem()
4804 4807 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4805 4808 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4806 4809 if f in copy:
4807 4810 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4808 4811 label='status.copied')
4809 4812
4810 4813 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
4811 4814 and not ui.plain()):
4812 4815 cmdutil.morestatus(repo, fm)
4813 4816 fm.end()
4814 4817
4815 4818 @command('^summary|sum',
4816 4819 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4817 4820 def summary(ui, repo, **opts):
4818 4821 """summarize working directory state
4819 4822
4820 4823 This generates a brief summary of the working directory state,
4821 4824 including parents, branch, commit status, phase and available updates.
4822 4825
4823 4826 With the --remote option, this will check the default paths for
4824 4827 incoming and outgoing changes. This can be time-consuming.
4825 4828
4826 4829 Returns 0 on success.
4827 4830 """
4828 4831
4829 4832 opts = pycompat.byteskwargs(opts)
4830 4833 ui.pager('summary')
4831 4834 ctx = repo[None]
4832 4835 parents = ctx.parents()
4833 4836 pnode = parents[0].node()
4834 4837 marks = []
4835 4838
4836 4839 ms = None
4837 4840 try:
4838 4841 ms = mergemod.mergestate.read(repo)
4839 4842 except error.UnsupportedMergeRecords as e:
4840 4843 s = ' '.join(e.recordtypes)
4841 4844 ui.warn(
4842 4845 _('warning: merge state has unsupported record types: %s\n') % s)
4843 4846 unresolved = []
4844 4847 else:
4845 4848 unresolved = list(ms.unresolved())
4846 4849
4847 4850 for p in parents:
4848 4851 # label with log.changeset (instead of log.parent) since this
4849 4852 # shows a working directory parent *changeset*:
4850 4853 # i18n: column positioning for "hg summary"
4851 4854 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4852 4855 label=cmdutil._changesetlabels(p))
4853 4856 ui.write(' '.join(p.tags()), label='log.tag')
4854 4857 if p.bookmarks():
4855 4858 marks.extend(p.bookmarks())
4856 4859 if p.rev() == -1:
4857 4860 if not len(repo):
4858 4861 ui.write(_(' (empty repository)'))
4859 4862 else:
4860 4863 ui.write(_(' (no revision checked out)'))
4861 4864 if p.obsolete():
4862 4865 ui.write(_(' (obsolete)'))
4863 4866 if p.isunstable():
4864 4867 instabilities = (ui.label(instability, 'trouble.%s' % instability)
4865 4868 for instability in p.instabilities())
4866 4869 ui.write(' ('
4867 4870 + ', '.join(instabilities)
4868 4871 + ')')
4869 4872 ui.write('\n')
4870 4873 if p.description():
4871 4874 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4872 4875 label='log.summary')
4873 4876
4874 4877 branch = ctx.branch()
4875 4878 bheads = repo.branchheads(branch)
4876 4879 # i18n: column positioning for "hg summary"
4877 4880 m = _('branch: %s\n') % branch
4878 4881 if branch != 'default':
4879 4882 ui.write(m, label='log.branch')
4880 4883 else:
4881 4884 ui.status(m, label='log.branch')
4882 4885
4883 4886 if marks:
4884 4887 active = repo._activebookmark
4885 4888 # i18n: column positioning for "hg summary"
4886 4889 ui.write(_('bookmarks:'), label='log.bookmark')
4887 4890 if active is not None:
4888 4891 if active in marks:
4889 4892 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
4890 4893 marks.remove(active)
4891 4894 else:
4892 4895 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
4893 4896 for m in marks:
4894 4897 ui.write(' ' + m, label='log.bookmark')
4895 4898 ui.write('\n', label='log.bookmark')
4896 4899
4897 4900 status = repo.status(unknown=True)
4898 4901
4899 4902 c = repo.dirstate.copies()
4900 4903 copied, renamed = [], []
4901 4904 for d, s in c.iteritems():
4902 4905 if s in status.removed:
4903 4906 status.removed.remove(s)
4904 4907 renamed.append(d)
4905 4908 else:
4906 4909 copied.append(d)
4907 4910 if d in status.added:
4908 4911 status.added.remove(d)
4909 4912
4910 4913 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4911 4914
4912 4915 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
4913 4916 (ui.label(_('%d added'), 'status.added'), status.added),
4914 4917 (ui.label(_('%d removed'), 'status.removed'), status.removed),
4915 4918 (ui.label(_('%d renamed'), 'status.copied'), renamed),
4916 4919 (ui.label(_('%d copied'), 'status.copied'), copied),
4917 4920 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
4918 4921 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
4919 4922 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
4920 4923 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
4921 4924 t = []
4922 4925 for l, s in labels:
4923 4926 if s:
4924 4927 t.append(l % len(s))
4925 4928
4926 4929 t = ', '.join(t)
4927 4930 cleanworkdir = False
4928 4931
4929 4932 if repo.vfs.exists('graftstate'):
4930 4933 t += _(' (graft in progress)')
4931 4934 if repo.vfs.exists('updatestate'):
4932 4935 t += _(' (interrupted update)')
4933 4936 elif len(parents) > 1:
4934 4937 t += _(' (merge)')
4935 4938 elif branch != parents[0].branch():
4936 4939 t += _(' (new branch)')
4937 4940 elif (parents[0].closesbranch() and
4938 4941 pnode in repo.branchheads(branch, closed=True)):
4939 4942 t += _(' (head closed)')
4940 4943 elif not (status.modified or status.added or status.removed or renamed or
4941 4944 copied or subs):
4942 4945 t += _(' (clean)')
4943 4946 cleanworkdir = True
4944 4947 elif pnode not in bheads:
4945 4948 t += _(' (new branch head)')
4946 4949
4947 4950 if parents:
4948 4951 pendingphase = max(p.phase() for p in parents)
4949 4952 else:
4950 4953 pendingphase = phases.public
4951 4954
4952 4955 if pendingphase > phases.newcommitphase(ui):
4953 4956 t += ' (%s)' % phases.phasenames[pendingphase]
4954 4957
4955 4958 if cleanworkdir:
4956 4959 # i18n: column positioning for "hg summary"
4957 4960 ui.status(_('commit: %s\n') % t.strip())
4958 4961 else:
4959 4962 # i18n: column positioning for "hg summary"
4960 4963 ui.write(_('commit: %s\n') % t.strip())
4961 4964
4962 4965 # all ancestors of branch heads - all ancestors of parent = new csets
4963 4966 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
4964 4967 bheads))
4965 4968
4966 4969 if new == 0:
4967 4970 # i18n: column positioning for "hg summary"
4968 4971 ui.status(_('update: (current)\n'))
4969 4972 elif pnode not in bheads:
4970 4973 # i18n: column positioning for "hg summary"
4971 4974 ui.write(_('update: %d new changesets (update)\n') % new)
4972 4975 else:
4973 4976 # i18n: column positioning for "hg summary"
4974 4977 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4975 4978 (new, len(bheads)))
4976 4979
4977 4980 t = []
4978 4981 draft = len(repo.revs('draft()'))
4979 4982 if draft:
4980 4983 t.append(_('%d draft') % draft)
4981 4984 secret = len(repo.revs('secret()'))
4982 4985 if secret:
4983 4986 t.append(_('%d secret') % secret)
4984 4987
4985 4988 if draft or secret:
4986 4989 ui.status(_('phases: %s\n') % ', '.join(t))
4987 4990
4988 4991 if obsolete.isenabled(repo, obsolete.createmarkersopt):
4989 4992 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
4990 4993 numtrouble = len(repo.revs(trouble + "()"))
4991 4994 # We write all the possibilities to ease translation
4992 4995 troublemsg = {
4993 4996 "orphan": _("orphan: %d changesets"),
4994 4997 "contentdivergent": _("content-divergent: %d changesets"),
4995 4998 "phasedivergent": _("phase-divergent: %d changesets"),
4996 4999 }
4997 5000 if numtrouble > 0:
4998 5001 ui.status(troublemsg[trouble] % numtrouble + "\n")
4999 5002
5000 5003 cmdutil.summaryhooks(ui, repo)
5001 5004
5002 5005 if opts.get('remote'):
5003 5006 needsincoming, needsoutgoing = True, True
5004 5007 else:
5005 5008 needsincoming, needsoutgoing = False, False
5006 5009 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5007 5010 if i:
5008 5011 needsincoming = True
5009 5012 if o:
5010 5013 needsoutgoing = True
5011 5014 if not needsincoming and not needsoutgoing:
5012 5015 return
5013 5016
5014 5017 def getincoming():
5015 5018 source, branches = hg.parseurl(ui.expandpath('default'))
5016 5019 sbranch = branches[0]
5017 5020 try:
5018 5021 other = hg.peer(repo, {}, source)
5019 5022 except error.RepoError:
5020 5023 if opts.get('remote'):
5021 5024 raise
5022 5025 return source, sbranch, None, None, None
5023 5026 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5024 5027 if revs:
5025 5028 revs = [other.lookup(rev) for rev in revs]
5026 5029 ui.debug('comparing with %s\n' % util.hidepassword(source))
5027 5030 repo.ui.pushbuffer()
5028 5031 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5029 5032 repo.ui.popbuffer()
5030 5033 return source, sbranch, other, commoninc, commoninc[1]
5031 5034
5032 5035 if needsincoming:
5033 5036 source, sbranch, sother, commoninc, incoming = getincoming()
5034 5037 else:
5035 5038 source = sbranch = sother = commoninc = incoming = None
5036 5039
5037 5040 def getoutgoing():
5038 5041 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5039 5042 dbranch = branches[0]
5040 5043 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5041 5044 if source != dest:
5042 5045 try:
5043 5046 dother = hg.peer(repo, {}, dest)
5044 5047 except error.RepoError:
5045 5048 if opts.get('remote'):
5046 5049 raise
5047 5050 return dest, dbranch, None, None
5048 5051 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5049 5052 elif sother is None:
5050 5053 # there is no explicit destination peer, but source one is invalid
5051 5054 return dest, dbranch, None, None
5052 5055 else:
5053 5056 dother = sother
5054 5057 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5055 5058 common = None
5056 5059 else:
5057 5060 common = commoninc
5058 5061 if revs:
5059 5062 revs = [repo.lookup(rev) for rev in revs]
5060 5063 repo.ui.pushbuffer()
5061 5064 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5062 5065 commoninc=common)
5063 5066 repo.ui.popbuffer()
5064 5067 return dest, dbranch, dother, outgoing
5065 5068
5066 5069 if needsoutgoing:
5067 5070 dest, dbranch, dother, outgoing = getoutgoing()
5068 5071 else:
5069 5072 dest = dbranch = dother = outgoing = None
5070 5073
5071 5074 if opts.get('remote'):
5072 5075 t = []
5073 5076 if incoming:
5074 5077 t.append(_('1 or more incoming'))
5075 5078 o = outgoing.missing
5076 5079 if o:
5077 5080 t.append(_('%d outgoing') % len(o))
5078 5081 other = dother or sother
5079 5082 if 'bookmarks' in other.listkeys('namespaces'):
5080 5083 counts = bookmarks.summary(repo, other)
5081 5084 if counts[0] > 0:
5082 5085 t.append(_('%d incoming bookmarks') % counts[0])
5083 5086 if counts[1] > 0:
5084 5087 t.append(_('%d outgoing bookmarks') % counts[1])
5085 5088
5086 5089 if t:
5087 5090 # i18n: column positioning for "hg summary"
5088 5091 ui.write(_('remote: %s\n') % (', '.join(t)))
5089 5092 else:
5090 5093 # i18n: column positioning for "hg summary"
5091 5094 ui.status(_('remote: (synced)\n'))
5092 5095
5093 5096 cmdutil.summaryremotehooks(ui, repo, opts,
5094 5097 ((source, sbranch, sother, commoninc),
5095 5098 (dest, dbranch, dother, outgoing)))
5096 5099
5097 5100 @command('tag',
5098 5101 [('f', 'force', None, _('force tag')),
5099 5102 ('l', 'local', None, _('make the tag local')),
5100 5103 ('r', 'rev', '', _('revision to tag'), _('REV')),
5101 5104 ('', 'remove', None, _('remove a tag')),
5102 5105 # -l/--local is already there, commitopts cannot be used
5103 5106 ('e', 'edit', None, _('invoke editor on commit messages')),
5104 5107 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5105 5108 ] + commitopts2,
5106 5109 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5107 5110 def tag(ui, repo, name1, *names, **opts):
5108 5111 """add one or more tags for the current or given revision
5109 5112
5110 5113 Name a particular revision using <name>.
5111 5114
5112 5115 Tags are used to name particular revisions of the repository and are
5113 5116 very useful to compare different revisions, to go back to significant
5114 5117 earlier versions or to mark branch points as releases, etc. Changing
5115 5118 an existing tag is normally disallowed; use -f/--force to override.
5116 5119
5117 5120 If no revision is given, the parent of the working directory is
5118 5121 used.
5119 5122
5120 5123 To facilitate version control, distribution, and merging of tags,
5121 5124 they are stored as a file named ".hgtags" which is managed similarly
5122 5125 to other project files and can be hand-edited if necessary. This
5123 5126 also means that tagging creates a new commit. The file
5124 5127 ".hg/localtags" is used for local tags (not shared among
5125 5128 repositories).
5126 5129
5127 5130 Tag commits are usually made at the head of a branch. If the parent
5128 5131 of the working directory is not a branch head, :hg:`tag` aborts; use
5129 5132 -f/--force to force the tag commit to be based on a non-head
5130 5133 changeset.
5131 5134
5132 5135 See :hg:`help dates` for a list of formats valid for -d/--date.
5133 5136
5134 5137 Since tag names have priority over branch names during revision
5135 5138 lookup, using an existing branch name as a tag name is discouraged.
5136 5139
5137 5140 Returns 0 on success.
5138 5141 """
5139 5142 opts = pycompat.byteskwargs(opts)
5140 5143 wlock = lock = None
5141 5144 try:
5142 5145 wlock = repo.wlock()
5143 5146 lock = repo.lock()
5144 5147 rev_ = "."
5145 5148 names = [t.strip() for t in (name1,) + names]
5146 5149 if len(names) != len(set(names)):
5147 5150 raise error.Abort(_('tag names must be unique'))
5148 5151 for n in names:
5149 5152 scmutil.checknewlabel(repo, n, 'tag')
5150 5153 if not n:
5151 5154 raise error.Abort(_('tag names cannot consist entirely of '
5152 5155 'whitespace'))
5153 5156 if opts.get('rev') and opts.get('remove'):
5154 5157 raise error.Abort(_("--rev and --remove are incompatible"))
5155 5158 if opts.get('rev'):
5156 5159 rev_ = opts['rev']
5157 5160 message = opts.get('message')
5158 5161 if opts.get('remove'):
5159 5162 if opts.get('local'):
5160 5163 expectedtype = 'local'
5161 5164 else:
5162 5165 expectedtype = 'global'
5163 5166
5164 5167 for n in names:
5165 5168 if not repo.tagtype(n):
5166 5169 raise error.Abort(_("tag '%s' does not exist") % n)
5167 5170 if repo.tagtype(n) != expectedtype:
5168 5171 if expectedtype == 'global':
5169 5172 raise error.Abort(_("tag '%s' is not a global tag") % n)
5170 5173 else:
5171 5174 raise error.Abort(_("tag '%s' is not a local tag") % n)
5172 5175 rev_ = 'null'
5173 5176 if not message:
5174 5177 # we don't translate commit messages
5175 5178 message = 'Removed tag %s' % ', '.join(names)
5176 5179 elif not opts.get('force'):
5177 5180 for n in names:
5178 5181 if n in repo.tags():
5179 5182 raise error.Abort(_("tag '%s' already exists "
5180 5183 "(use -f to force)") % n)
5181 5184 if not opts.get('local'):
5182 5185 p1, p2 = repo.dirstate.parents()
5183 5186 if p2 != nullid:
5184 5187 raise error.Abort(_('uncommitted merge'))
5185 5188 bheads = repo.branchheads()
5186 5189 if not opts.get('force') and bheads and p1 not in bheads:
5187 5190 raise error.Abort(_('working directory is not at a branch head '
5188 5191 '(use -f to force)'))
5189 5192 r = scmutil.revsingle(repo, rev_).node()
5190 5193
5191 5194 if not message:
5192 5195 # we don't translate commit messages
5193 5196 message = ('Added tag %s for changeset %s' %
5194 5197 (', '.join(names), short(r)))
5195 5198
5196 5199 date = opts.get('date')
5197 5200 if date:
5198 5201 date = util.parsedate(date)
5199 5202
5200 5203 if opts.get('remove'):
5201 5204 editform = 'tag.remove'
5202 5205 else:
5203 5206 editform = 'tag.add'
5204 5207 editor = cmdutil.getcommiteditor(editform=editform,
5205 5208 **pycompat.strkwargs(opts))
5206 5209
5207 5210 # don't allow tagging the null rev
5208 5211 if (not opts.get('remove') and
5209 5212 scmutil.revsingle(repo, rev_).rev() == nullrev):
5210 5213 raise error.Abort(_("cannot tag null revision"))
5211 5214
5212 5215 tagsmod.tag(repo, names, r, message, opts.get('local'),
5213 5216 opts.get('user'), date, editor=editor)
5214 5217 finally:
5215 5218 release(lock, wlock)
5216 5219
5217 5220 @command('tags', formatteropts, '')
5218 5221 def tags(ui, repo, **opts):
5219 5222 """list repository tags
5220 5223
5221 5224 This lists both regular and local tags. When the -v/--verbose
5222 5225 switch is used, a third column "local" is printed for local tags.
5223 5226 When the -q/--quiet switch is used, only the tag name is printed.
5224 5227
5225 5228 Returns 0 on success.
5226 5229 """
5227 5230
5228 5231 opts = pycompat.byteskwargs(opts)
5229 5232 ui.pager('tags')
5230 5233 fm = ui.formatter('tags', opts)
5231 5234 hexfunc = fm.hexfunc
5232 5235 tagtype = ""
5233 5236
5234 5237 for t, n in reversed(repo.tagslist()):
5235 5238 hn = hexfunc(n)
5236 5239 label = 'tags.normal'
5237 5240 tagtype = ''
5238 5241 if repo.tagtype(t) == 'local':
5239 5242 label = 'tags.local'
5240 5243 tagtype = 'local'
5241 5244
5242 5245 fm.startitem()
5243 5246 fm.write('tag', '%s', t, label=label)
5244 5247 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5245 5248 fm.condwrite(not ui.quiet, 'rev node', fmt,
5246 5249 repo.changelog.rev(n), hn, label=label)
5247 5250 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5248 5251 tagtype, label=label)
5249 5252 fm.plain('\n')
5250 5253 fm.end()
5251 5254
5252 5255 @command('tip',
5253 5256 [('p', 'patch', None, _('show patch')),
5254 5257 ('g', 'git', None, _('use git extended diff format')),
5255 5258 ] + templateopts,
5256 5259 _('[-p] [-g]'))
5257 5260 def tip(ui, repo, **opts):
5258 5261 """show the tip revision (DEPRECATED)
5259 5262
5260 5263 The tip revision (usually just called the tip) is the changeset
5261 5264 most recently added to the repository (and therefore the most
5262 5265 recently changed head).
5263 5266
5264 5267 If you have just made a commit, that commit will be the tip. If
5265 5268 you have just pulled changes from another repository, the tip of
5266 5269 that repository becomes the current tip. The "tip" tag is special
5267 5270 and cannot be renamed or assigned to a different changeset.
5268 5271
5269 5272 This command is deprecated, please use :hg:`heads` instead.
5270 5273
5271 5274 Returns 0 on success.
5272 5275 """
5273 5276 opts = pycompat.byteskwargs(opts)
5274 5277 displayer = cmdutil.show_changeset(ui, repo, opts)
5275 5278 displayer.show(repo['tip'])
5276 5279 displayer.close()
5277 5280
5278 5281 @command('unbundle',
5279 5282 [('u', 'update', None,
5280 5283 _('update to new branch head if changesets were unbundled'))],
5281 5284 _('[-u] FILE...'))
5282 5285 def unbundle(ui, repo, fname1, *fnames, **opts):
5283 5286 """apply one or more bundle files
5284 5287
5285 5288 Apply one or more bundle files generated by :hg:`bundle`.
5286 5289
5287 5290 Returns 0 on success, 1 if an update has unresolved files.
5288 5291 """
5289 5292 fnames = (fname1,) + fnames
5290 5293
5291 5294 with repo.lock():
5292 5295 for fname in fnames:
5293 5296 f = hg.openpath(ui, fname)
5294 5297 gen = exchange.readbundle(ui, f, fname)
5295 5298 if isinstance(gen, streamclone.streamcloneapplier):
5296 5299 raise error.Abort(
5297 5300 _('packed bundles cannot be applied with '
5298 5301 '"hg unbundle"'),
5299 5302 hint=_('use "hg debugapplystreamclonebundle"'))
5300 5303 url = 'bundle:' + fname
5301 5304 try:
5302 5305 txnname = 'unbundle'
5303 5306 if not isinstance(gen, bundle2.unbundle20):
5304 5307 txnname = 'unbundle\n%s' % util.hidepassword(url)
5305 5308 with repo.transaction(txnname) as tr:
5306 5309 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5307 5310 url=url)
5308 5311 except error.BundleUnknownFeatureError as exc:
5309 5312 raise error.Abort(
5310 5313 _('%s: unknown bundle feature, %s') % (fname, exc),
5311 5314 hint=_("see https://mercurial-scm.org/"
5312 5315 "wiki/BundleFeature for more "
5313 5316 "information"))
5314 5317 modheads = bundle2.combinechangegroupresults(op)
5315 5318
5316 5319 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5317 5320
5318 5321 @command('^update|up|checkout|co',
5319 5322 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5320 5323 ('c', 'check', None, _('require clean working directory')),
5321 5324 ('m', 'merge', None, _('merge uncommitted changes')),
5322 5325 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5323 5326 ('r', 'rev', '', _('revision'), _('REV'))
5324 5327 ] + mergetoolopts,
5325 5328 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5326 5329 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5327 5330 merge=None, tool=None):
5328 5331 """update working directory (or switch revisions)
5329 5332
5330 5333 Update the repository's working directory to the specified
5331 5334 changeset. If no changeset is specified, update to the tip of the
5332 5335 current named branch and move the active bookmark (see :hg:`help
5333 5336 bookmarks`).
5334 5337
5335 5338 Update sets the working directory's parent revision to the specified
5336 5339 changeset (see :hg:`help parents`).
5337 5340
5338 5341 If the changeset is not a descendant or ancestor of the working
5339 5342 directory's parent and there are uncommitted changes, the update is
5340 5343 aborted. With the -c/--check option, the working directory is checked
5341 5344 for uncommitted changes; if none are found, the working directory is
5342 5345 updated to the specified changeset.
5343 5346
5344 5347 .. container:: verbose
5345 5348
5346 5349 The -C/--clean, -c/--check, and -m/--merge options control what
5347 5350 happens if the working directory contains uncommitted changes.
5348 5351 At most of one of them can be specified.
5349 5352
5350 5353 1. If no option is specified, and if
5351 5354 the requested changeset is an ancestor or descendant of
5352 5355 the working directory's parent, the uncommitted changes
5353 5356 are merged into the requested changeset and the merged
5354 5357 result is left uncommitted. If the requested changeset is
5355 5358 not an ancestor or descendant (that is, it is on another
5356 5359 branch), the update is aborted and the uncommitted changes
5357 5360 are preserved.
5358 5361
5359 5362 2. With the -m/--merge option, the update is allowed even if the
5360 5363 requested changeset is not an ancestor or descendant of
5361 5364 the working directory's parent.
5362 5365
5363 5366 3. With the -c/--check option, the update is aborted and the
5364 5367 uncommitted changes are preserved.
5365 5368
5366 5369 4. With the -C/--clean option, uncommitted changes are discarded and
5367 5370 the working directory is updated to the requested changeset.
5368 5371
5369 5372 To cancel an uncommitted merge (and lose your changes), use
5370 5373 :hg:`update --clean .`.
5371 5374
5372 5375 Use null as the changeset to remove the working directory (like
5373 5376 :hg:`clone -U`).
5374 5377
5375 5378 If you want to revert just one file to an older revision, use
5376 5379 :hg:`revert [-r REV] NAME`.
5377 5380
5378 5381 See :hg:`help dates` for a list of formats valid for -d/--date.
5379 5382
5380 5383 Returns 0 on success, 1 if there are unresolved files.
5381 5384 """
5382 5385 if rev and node:
5383 5386 raise error.Abort(_("please specify just one revision"))
5384 5387
5385 5388 if ui.configbool('commands', 'update.requiredest'):
5386 5389 if not node and not rev and not date:
5387 5390 raise error.Abort(_('you must specify a destination'),
5388 5391 hint=_('for example: hg update ".::"'))
5389 5392
5390 5393 if rev is None or rev == '':
5391 5394 rev = node
5392 5395
5393 5396 if date and rev is not None:
5394 5397 raise error.Abort(_("you can't specify a revision and a date"))
5395 5398
5396 5399 if len([x for x in (clean, check, merge) if x]) > 1:
5397 5400 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5398 5401 "or -m/merge"))
5399 5402
5400 5403 updatecheck = None
5401 5404 if check:
5402 5405 updatecheck = 'abort'
5403 5406 elif merge:
5404 5407 updatecheck = 'none'
5405 5408
5406 5409 with repo.wlock():
5407 5410 cmdutil.clearunfinished(repo)
5408 5411
5409 5412 if date:
5410 5413 rev = cmdutil.finddate(ui, repo, date)
5411 5414
5412 5415 # if we defined a bookmark, we have to remember the original name
5413 5416 brev = rev
5414 5417 rev = scmutil.revsingle(repo, rev, rev).rev()
5415 5418
5416 5419 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5417 5420
5418 5421 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5419 5422 updatecheck=updatecheck)
5420 5423
5421 5424 @command('verify', [])
5422 5425 def verify(ui, repo):
5423 5426 """verify the integrity of the repository
5424 5427
5425 5428 Verify the integrity of the current repository.
5426 5429
5427 5430 This will perform an extensive check of the repository's
5428 5431 integrity, validating the hashes and checksums of each entry in
5429 5432 the changelog, manifest, and tracked files, as well as the
5430 5433 integrity of their crosslinks and indices.
5431 5434
5432 5435 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5433 5436 for more information about recovery from corruption of the
5434 5437 repository.
5435 5438
5436 5439 Returns 0 on success, 1 if errors are encountered.
5437 5440 """
5438 5441 return hg.verify(repo)
5439 5442
5440 5443 @command('version', [] + formatteropts, norepo=True)
5441 5444 def version_(ui, **opts):
5442 5445 """output version and copyright information"""
5443 5446 opts = pycompat.byteskwargs(opts)
5444 5447 if ui.verbose:
5445 5448 ui.pager('version')
5446 5449 fm = ui.formatter("version", opts)
5447 5450 fm.startitem()
5448 5451 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5449 5452 util.version())
5450 5453 license = _(
5451 5454 "(see https://mercurial-scm.org for more information)\n"
5452 5455 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5453 5456 "This is free software; see the source for copying conditions. "
5454 5457 "There is NO\nwarranty; "
5455 5458 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5456 5459 )
5457 5460 if not ui.quiet:
5458 5461 fm.plain(license)
5459 5462
5460 5463 if ui.verbose:
5461 5464 fm.plain(_("\nEnabled extensions:\n\n"))
5462 5465 # format names and versions into columns
5463 5466 names = []
5464 5467 vers = []
5465 5468 isinternals = []
5466 5469 for name, module in extensions.extensions():
5467 5470 names.append(name)
5468 5471 vers.append(extensions.moduleversion(module) or None)
5469 5472 isinternals.append(extensions.ismoduleinternal(module))
5470 5473 fn = fm.nested("extensions")
5471 5474 if names:
5472 5475 namefmt = " %%-%ds " % max(len(n) for n in names)
5473 5476 places = [_("external"), _("internal")]
5474 5477 for n, v, p in zip(names, vers, isinternals):
5475 5478 fn.startitem()
5476 5479 fn.condwrite(ui.verbose, "name", namefmt, n)
5477 5480 if ui.verbose:
5478 5481 fn.plain("%s " % places[p])
5479 5482 fn.data(bundled=p)
5480 5483 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5481 5484 if ui.verbose:
5482 5485 fn.plain("\n")
5483 5486 fn.end()
5484 5487 fm.end()
5485 5488
5486 5489 def loadcmdtable(ui, name, cmdtable):
5487 5490 """Load command functions from specified cmdtable
5488 5491 """
5489 5492 overrides = [cmd for cmd in cmdtable if cmd in table]
5490 5493 if overrides:
5491 5494 ui.warn(_("extension '%s' overrides commands: %s\n")
5492 5495 % (name, " ".join(overrides)))
5493 5496 table.update(cmdtable)
@@ -1,766 +1,931 b''
1 1 $ hg init a
2 2 $ cd a
3 3
4 4 Verify checking branch of nullrev before the cache is created doesnt crash
5 5 $ hg log -r 'branch(.)' -T '{branch}\n'
6 6
7 7 Basic test
8 8 $ echo 'root' >root
9 9 $ hg add root
10 10 $ hg commit -d '0 0' -m "Adding root node"
11 11
12 12 $ echo 'a' >a
13 13 $ hg add a
14 14 $ hg branch a
15 15 marked working directory as branch a
16 16 (branches are permanent and global, did you want a bookmark?)
17 17 $ hg commit -d '1 0' -m "Adding a branch"
18 18
19 19 $ hg branch q
20 20 marked working directory as branch q
21 21 $ echo 'aa' >a
22 22 $ hg branch -C
23 23 reset working directory to branch a
24 24 $ hg commit -d '2 0' -m "Adding to a branch"
25 25
26 26 $ hg update -C 0
27 27 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
28 28 $ echo 'b' >b
29 29 $ hg add b
30 30 $ hg branch b
31 31 marked working directory as branch b
32 32 $ hg commit -d '2 0' -m "Adding b branch"
33 33
34 34 $ echo 'bh1' >bh1
35 35 $ hg add bh1
36 36 $ hg commit -d '3 0' -m "Adding b branch head 1"
37 37
38 38 $ hg update -C 2
39 39 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
40 40 $ echo 'bh2' >bh2
41 41 $ hg add bh2
42 42 $ hg commit -d '4 0' -m "Adding b branch head 2"
43 43
44 44 $ echo 'c' >c
45 45 $ hg add c
46 46 $ hg branch c
47 47 marked working directory as branch c
48 48 $ hg commit -d '5 0' -m "Adding c branch"
49 49
50 50 reserved names
51 51
52 52 $ hg branch tip
53 53 abort: the name 'tip' is reserved
54 54 [255]
55 55 $ hg branch null
56 56 abort: the name 'null' is reserved
57 57 [255]
58 58 $ hg branch .
59 59 abort: the name '.' is reserved
60 60 [255]
61 61
62 62 invalid characters
63 63
64 64 $ hg branch 'foo:bar'
65 65 abort: ':' cannot be used in a name
66 66 [255]
67 67
68 68 $ hg branch 'foo
69 69 > bar'
70 70 abort: '\n' cannot be used in a name
71 71 [255]
72 72
73 73 trailing or leading spaces should be stripped before testing duplicates
74 74
75 75 $ hg branch 'b '
76 76 abort: a branch of the same name already exists
77 77 (use 'hg update' to switch to it)
78 78 [255]
79 79
80 80 $ hg branch ' b'
81 81 abort: a branch of the same name already exists
82 82 (use 'hg update' to switch to it)
83 83 [255]
84 84
85 85 verify update will accept invalid legacy branch names
86 86
87 87 $ hg init test-invalid-branch-name
88 88 $ cd test-invalid-branch-name
89 89 $ hg pull -u "$TESTDIR"/bundles/test-invalid-branch-name.hg
90 90 pulling from *test-invalid-branch-name.hg (glob)
91 91 requesting all changes
92 92 adding changesets
93 93 adding manifests
94 94 adding file changes
95 95 added 3 changesets with 3 changes to 2 files
96 96 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
97 97
98 98 $ hg update '"colon:test"'
99 99 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
100 100 $ cd ..
101 101
102 102 $ echo 'd' >d
103 103 $ hg add d
104 104 $ hg branch 'a branch name much longer than the default justification used by branches'
105 105 marked working directory as branch a branch name much longer than the default justification used by branches
106 106 $ hg commit -d '6 0' -m "Adding d branch"
107 107
108 108 $ hg branches
109 109 a branch name much longer than the default justification used by branches 7:10ff5895aa57
110 110 b 4:aee39cd168d0
111 111 c 6:589736a22561 (inactive)
112 112 a 5:d8cbc61dbaa6 (inactive)
113 113 default 0:19709c5a4e75 (inactive)
114 114
115 115 -------
116 116
117 117 $ hg branches -a
118 118 a branch name much longer than the default justification used by branches 7:10ff5895aa57
119 119 b 4:aee39cd168d0
120 120
121 121 --- Branch a
122 122
123 123 $ hg log -b a
124 124 changeset: 5:d8cbc61dbaa6
125 125 branch: a
126 126 parent: 2:881fe2b92ad0
127 127 user: test
128 128 date: Thu Jan 01 00:00:04 1970 +0000
129 129 summary: Adding b branch head 2
130 130
131 131 changeset: 2:881fe2b92ad0
132 132 branch: a
133 133 user: test
134 134 date: Thu Jan 01 00:00:02 1970 +0000
135 135 summary: Adding to a branch
136 136
137 137 changeset: 1:dd6b440dd85a
138 138 branch: a
139 139 user: test
140 140 date: Thu Jan 01 00:00:01 1970 +0000
141 141 summary: Adding a branch
142 142
143 143
144 144 ---- Branch b
145 145
146 146 $ hg log -b b
147 147 changeset: 4:aee39cd168d0
148 148 branch: b
149 149 user: test
150 150 date: Thu Jan 01 00:00:03 1970 +0000
151 151 summary: Adding b branch head 1
152 152
153 153 changeset: 3:ac22033332d1
154 154 branch: b
155 155 parent: 0:19709c5a4e75
156 156 user: test
157 157 date: Thu Jan 01 00:00:02 1970 +0000
158 158 summary: Adding b branch
159 159
160 160
161 161 ---- going to test branch closing
162 162
163 163 $ hg branches
164 164 a branch name much longer than the default justification used by branches 7:10ff5895aa57
165 165 b 4:aee39cd168d0
166 166 c 6:589736a22561 (inactive)
167 167 a 5:d8cbc61dbaa6 (inactive)
168 168 default 0:19709c5a4e75 (inactive)
169 169 $ hg up -C b
170 170 2 files updated, 0 files merged, 4 files removed, 0 files unresolved
171 171 $ echo 'xxx1' >> b
172 172 $ hg commit -d '7 0' -m 'adding cset to branch b'
173 173 $ hg up -C aee39cd168d0
174 174 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
175 175 $ echo 'xxx2' >> b
176 176 $ hg commit -d '8 0' -m 'adding head to branch b'
177 177 created new head
178 178 $ echo 'xxx3' >> b
179 179 $ hg commit -d '9 0' -m 'adding another cset to branch b'
180 180 $ hg branches
181 181 b 10:bfbe841b666e
182 182 a branch name much longer than the default justification used by branches 7:10ff5895aa57
183 183 c 6:589736a22561 (inactive)
184 184 a 5:d8cbc61dbaa6 (inactive)
185 185 default 0:19709c5a4e75 (inactive)
186 186 $ hg heads --closed
187 187 changeset: 10:bfbe841b666e
188 188 branch: b
189 189 tag: tip
190 190 user: test
191 191 date: Thu Jan 01 00:00:09 1970 +0000
192 192 summary: adding another cset to branch b
193 193
194 194 changeset: 8:eebb944467c9
195 195 branch: b
196 196 parent: 4:aee39cd168d0
197 197 user: test
198 198 date: Thu Jan 01 00:00:07 1970 +0000
199 199 summary: adding cset to branch b
200 200
201 201 changeset: 7:10ff5895aa57
202 202 branch: a branch name much longer than the default justification used by branches
203 203 user: test
204 204 date: Thu Jan 01 00:00:06 1970 +0000
205 205 summary: Adding d branch
206 206
207 207 changeset: 6:589736a22561
208 208 branch: c
209 209 user: test
210 210 date: Thu Jan 01 00:00:05 1970 +0000
211 211 summary: Adding c branch
212 212
213 213 changeset: 5:d8cbc61dbaa6
214 214 branch: a
215 215 parent: 2:881fe2b92ad0
216 216 user: test
217 217 date: Thu Jan 01 00:00:04 1970 +0000
218 218 summary: Adding b branch head 2
219 219
220 220 changeset: 0:19709c5a4e75
221 221 user: test
222 222 date: Thu Jan 01 00:00:00 1970 +0000
223 223 summary: Adding root node
224 224
225 225 $ hg heads
226 226 changeset: 10:bfbe841b666e
227 227 branch: b
228 228 tag: tip
229 229 user: test
230 230 date: Thu Jan 01 00:00:09 1970 +0000
231 231 summary: adding another cset to branch b
232 232
233 233 changeset: 8:eebb944467c9
234 234 branch: b
235 235 parent: 4:aee39cd168d0
236 236 user: test
237 237 date: Thu Jan 01 00:00:07 1970 +0000
238 238 summary: adding cset to branch b
239 239
240 240 changeset: 7:10ff5895aa57
241 241 branch: a branch name much longer than the default justification used by branches
242 242 user: test
243 243 date: Thu Jan 01 00:00:06 1970 +0000
244 244 summary: Adding d branch
245 245
246 246 changeset: 6:589736a22561
247 247 branch: c
248 248 user: test
249 249 date: Thu Jan 01 00:00:05 1970 +0000
250 250 summary: Adding c branch
251 251
252 252 changeset: 5:d8cbc61dbaa6
253 253 branch: a
254 254 parent: 2:881fe2b92ad0
255 255 user: test
256 256 date: Thu Jan 01 00:00:04 1970 +0000
257 257 summary: Adding b branch head 2
258 258
259 259 changeset: 0:19709c5a4e75
260 260 user: test
261 261 date: Thu Jan 01 00:00:00 1970 +0000
262 262 summary: Adding root node
263 263
264 264 $ hg commit -d '9 0' --close-branch -m 'prune bad branch'
265 265 $ hg branches -a
266 266 b 8:eebb944467c9
267 267 a branch name much longer than the default justification used by branches 7:10ff5895aa57
268 268 $ hg up -C b
269 269 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
270 270 $ hg commit -d '9 0' --close-branch -m 'close this part branch too'
271 271 $ hg commit -d '9 0' --close-branch -m 're-closing this branch'
272 272 abort: can only close branch heads
273 273 [255]
274 274
275 275 $ hg log -r tip --debug
276 276 changeset: 12:e3d49c0575d8fc2cb1cd6859c747c14f5f6d499f
277 277 branch: b
278 278 tag: tip
279 279 phase: draft
280 280 parent: 8:eebb944467c9fb9651ed232aeaf31b3c0a7fc6c1
281 281 parent: -1:0000000000000000000000000000000000000000
282 282 manifest: 8:6f9ed32d2b310e391a4f107d5f0f071df785bfee
283 283 user: test
284 284 date: Thu Jan 01 00:00:09 1970 +0000
285 285 extra: branch=b
286 286 extra: close=1
287 287 description:
288 288 close this part branch too
289 289
290 290
291 291 --- b branch should be inactive
292 292
293 293 $ hg branches
294 294 a branch name much longer than the default justification used by branches 7:10ff5895aa57
295 295 c 6:589736a22561 (inactive)
296 296 a 5:d8cbc61dbaa6 (inactive)
297 297 default 0:19709c5a4e75 (inactive)
298 298 $ hg branches -c
299 299 a branch name much longer than the default justification used by branches 7:10ff5895aa57
300 300 b 12:e3d49c0575d8 (closed)
301 301 c 6:589736a22561 (inactive)
302 302 a 5:d8cbc61dbaa6 (inactive)
303 303 default 0:19709c5a4e75 (inactive)
304 304 $ hg branches -a
305 305 a branch name much longer than the default justification used by branches 7:10ff5895aa57
306 306 $ hg branches -q
307 307 a branch name much longer than the default justification used by branches
308 308 c
309 309 a
310 310 default
311 311 $ hg heads b
312 312 no open branch heads found on branches b
313 313 [1]
314 314 $ hg heads --closed b
315 315 changeset: 12:e3d49c0575d8
316 316 branch: b
317 317 tag: tip
318 318 parent: 8:eebb944467c9
319 319 user: test
320 320 date: Thu Jan 01 00:00:09 1970 +0000
321 321 summary: close this part branch too
322 322
323 323 changeset: 11:d3f163457ebf
324 324 branch: b
325 325 user: test
326 326 date: Thu Jan 01 00:00:09 1970 +0000
327 327 summary: prune bad branch
328 328
329 329 $ echo 'xxx4' >> b
330 330 $ hg commit -d '9 0' -m 'reopen branch with a change'
331 331 reopening closed branch head 12
332 332
333 333 --- branch b is back in action
334 334
335 335 $ hg branches -a
336 336 b 13:e23b5505d1ad
337 337 a branch name much longer than the default justification used by branches 7:10ff5895aa57
338 338
339 339 ---- test heads listings
340 340
341 341 $ hg heads
342 342 changeset: 13:e23b5505d1ad
343 343 branch: b
344 344 tag: tip
345 345 user: test
346 346 date: Thu Jan 01 00:00:09 1970 +0000
347 347 summary: reopen branch with a change
348 348
349 349 changeset: 7:10ff5895aa57
350 350 branch: a branch name much longer than the default justification used by branches
351 351 user: test
352 352 date: Thu Jan 01 00:00:06 1970 +0000
353 353 summary: Adding d branch
354 354
355 355 changeset: 6:589736a22561
356 356 branch: c
357 357 user: test
358 358 date: Thu Jan 01 00:00:05 1970 +0000
359 359 summary: Adding c branch
360 360
361 361 changeset: 5:d8cbc61dbaa6
362 362 branch: a
363 363 parent: 2:881fe2b92ad0
364 364 user: test
365 365 date: Thu Jan 01 00:00:04 1970 +0000
366 366 summary: Adding b branch head 2
367 367
368 368 changeset: 0:19709c5a4e75
369 369 user: test
370 370 date: Thu Jan 01 00:00:00 1970 +0000
371 371 summary: Adding root node
372 372
373 373
374 374 branch default
375 375
376 376 $ hg heads default
377 377 changeset: 0:19709c5a4e75
378 378 user: test
379 379 date: Thu Jan 01 00:00:00 1970 +0000
380 380 summary: Adding root node
381 381
382 382
383 383 branch a
384 384
385 385 $ hg heads a
386 386 changeset: 5:d8cbc61dbaa6
387 387 branch: a
388 388 parent: 2:881fe2b92ad0
389 389 user: test
390 390 date: Thu Jan 01 00:00:04 1970 +0000
391 391 summary: Adding b branch head 2
392 392
393 393 $ hg heads --active a
394 394 no open branch heads found on branches a
395 395 [1]
396 396
397 397 branch b
398 398
399 399 $ hg heads b
400 400 changeset: 13:e23b5505d1ad
401 401 branch: b
402 402 tag: tip
403 403 user: test
404 404 date: Thu Jan 01 00:00:09 1970 +0000
405 405 summary: reopen branch with a change
406 406
407 407 $ hg heads --closed b
408 408 changeset: 13:e23b5505d1ad
409 409 branch: b
410 410 tag: tip
411 411 user: test
412 412 date: Thu Jan 01 00:00:09 1970 +0000
413 413 summary: reopen branch with a change
414 414
415 415 changeset: 11:d3f163457ebf
416 416 branch: b
417 417 user: test
418 418 date: Thu Jan 01 00:00:09 1970 +0000
419 419 summary: prune bad branch
420 420
421
422 reclose branch
423
424 $ hg up -C c
425 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
426 $ hg commit -d '9 0' --close-branch -m 'reclosing this branch'
427 $ hg branches
428 b 13:e23b5505d1ad
429 a branch name much longer than the default justification used by branches 7:10ff5895aa57
430 a 5:d8cbc61dbaa6 (inactive)
431 default 0:19709c5a4e75 (inactive)
432 $ hg branches --closed
433 b 13:e23b5505d1ad
434 a branch name much longer than the default justification used by branches 7:10ff5895aa57
435 c 14:f894c25619d3 (closed)
436 a 5:d8cbc61dbaa6 (inactive)
437 default 0:19709c5a4e75 (inactive)
438
439 multihead branch
440
441 $ hg up -C default
442 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
443 $ hg branch m
444 marked working directory as branch m
445 $ touch m
446 $ hg add m
447 $ hg commit -d '10 0' -m 'multihead base'
448 $ echo "m1" >m
449 $ hg commit -d '10 0' -m 'head 1'
450 $ hg up -C '.^'
451 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
452 $ echo "m2" >m
453 $ hg commit -d '10 0' -m 'head 2'
454 created new head
455 $ hg log -b m
456 changeset: 17:df343b0df04f
457 branch: m
458 tag: tip
459 parent: 15:f3447637f53e
460 user: test
461 date: Thu Jan 01 00:00:10 1970 +0000
462 summary: head 2
463
464 changeset: 16:a58ca5d3bdf3
465 branch: m
466 user: test
467 date: Thu Jan 01 00:00:10 1970 +0000
468 summary: head 1
469
470 changeset: 15:f3447637f53e
471 branch: m
472 parent: 0:19709c5a4e75
473 user: test
474 date: Thu Jan 01 00:00:10 1970 +0000
475 summary: multihead base
476
477 $ hg heads --topo m
478 changeset: 17:df343b0df04f
479 branch: m
480 tag: tip
481 parent: 15:f3447637f53e
482 user: test
483 date: Thu Jan 01 00:00:10 1970 +0000
484 summary: head 2
485
486 changeset: 16:a58ca5d3bdf3
487 branch: m
488 user: test
489 date: Thu Jan 01 00:00:10 1970 +0000
490 summary: head 1
491
492 $ hg branches
493 m 17:df343b0df04f
494 b 13:e23b5505d1ad
495 a branch name much longer than the default justification used by branches 7:10ff5895aa57
496 a 5:d8cbc61dbaa6 (inactive)
497 default 0:19709c5a4e75 (inactive)
498
499 partially merge multihead branch
500
501 $ hg up -C default
502 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
503 $ hg branch md
504 marked working directory as branch md
505 $ hg merge m
506 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
507 (branch merge, don't forget to commit)
508 $ hg commit -d '11 0' -m 'merge head 2'
509 $ hg heads --topo m
510 changeset: 16:a58ca5d3bdf3
511 branch: m
512 user: test
513 date: Thu Jan 01 00:00:10 1970 +0000
514 summary: head 1
515
516 $ hg branches
517 md 18:c914c99f1fbb
518 m 17:df343b0df04f
519 b 13:e23b5505d1ad
520 a branch name much longer than the default justification used by branches 7:10ff5895aa57
521 a 5:d8cbc61dbaa6 (inactive)
522 default 0:19709c5a4e75 (inactive)
523
524 partially close multihead branch
525
526 $ hg up -C a58ca5d3bdf3
527 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
528 $ hg commit -d '12 0' -m 'close head 1' --close-branch
529 $ hg heads --topo m
530 changeset: 19:cd21a80baa3d
531 branch: m
532 tag: tip
533 parent: 16:a58ca5d3bdf3
534 user: test
535 date: Thu Jan 01 00:00:12 1970 +0000
536 summary: close head 1
537
538 $ hg branches
539 md 18:c914c99f1fbb
540 b 13:e23b5505d1ad
541 a branch name much longer than the default justification used by branches 7:10ff5895aa57
542 m 17:df343b0df04f (inactive)
543 a 5:d8cbc61dbaa6 (inactive)
544 default 0:19709c5a4e75 (inactive)
545
421 546 default branch colors:
422 547
423 548 $ cat <<EOF >> $HGRCPATH
424 549 > [extensions]
425 550 > color =
426 551 > [color]
427 552 > mode = ansi
428 553 > EOF
429 554
430 $ hg up -C c
431 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
432 $ hg commit -d '9 0' --close-branch -m 'reclosing this branch'
433 555 $ hg up -C b
434 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
556 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
435 557 $ hg branches --color=always
558 \x1b[0;0mmd\x1b[0m\x1b[0;33m 18:c914c99f1fbb\x1b[0m (esc)
436 559 \x1b[0;32mb\x1b[0m\x1b[0;33m 13:e23b5505d1ad\x1b[0m (esc)
437 560 \x1b[0;0ma branch name much longer than the default justification used by branches\x1b[0m\x1b[0;33m 7:10ff5895aa57\x1b[0m (esc)
561 \x1b[0;0mm\x1b[0m\x1b[0;33m 17:df343b0df04f\x1b[0m (inactive) (esc)
438 562 \x1b[0;0ma\x1b[0m\x1b[0;33m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
439 563 \x1b[0;0mdefault\x1b[0m\x1b[0;33m 0:19709c5a4e75\x1b[0m (inactive) (esc)
440 564
441 565 default closed branch color:
442 566
443 567 $ hg branches --color=always --closed
568 \x1b[0;0mmd\x1b[0m\x1b[0;33m 18:c914c99f1fbb\x1b[0m (esc)
444 569 \x1b[0;32mb\x1b[0m\x1b[0;33m 13:e23b5505d1ad\x1b[0m (esc)
445 570 \x1b[0;0ma branch name much longer than the default justification used by branches\x1b[0m\x1b[0;33m 7:10ff5895aa57\x1b[0m (esc)
571 \x1b[0;0mm\x1b[0m\x1b[0;33m 17:df343b0df04f\x1b[0m (inactive) (esc)
446 572 \x1b[0;30;1mc\x1b[0m\x1b[0;33m 14:f894c25619d3\x1b[0m (closed) (esc)
447 573 \x1b[0;0ma\x1b[0m\x1b[0;33m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
448 574 \x1b[0;0mdefault\x1b[0m\x1b[0;33m 0:19709c5a4e75\x1b[0m (inactive) (esc)
449 575
450 576 $ cat <<EOF >> $HGRCPATH
451 577 > [extensions]
452 578 > color =
453 579 > [color]
454 580 > branches.active = green
455 581 > branches.closed = blue
456 582 > branches.current = red
457 583 > branches.inactive = magenta
458 584 > log.changeset = cyan
459 585 > EOF
460 586
461 587 custom branch colors:
462 588
463 589 $ hg branches --color=always
590 \x1b[0;32mmd\x1b[0m\x1b[0;36m 18:c914c99f1fbb\x1b[0m (esc)
464 591 \x1b[0;31mb\x1b[0m\x1b[0;36m 13:e23b5505d1ad\x1b[0m (esc)
465 592 \x1b[0;32ma branch name much longer than the default justification used by branches\x1b[0m\x1b[0;36m 7:10ff5895aa57\x1b[0m (esc)
593 \x1b[0;35mm\x1b[0m\x1b[0;36m 17:df343b0df04f\x1b[0m (inactive) (esc)
466 594 \x1b[0;35ma\x1b[0m\x1b[0;36m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
467 595 \x1b[0;35mdefault\x1b[0m\x1b[0;36m 0:19709c5a4e75\x1b[0m (inactive) (esc)
468 596
469 597 custom closed branch color:
470 598
471 599 $ hg branches --color=always --closed
600 \x1b[0;32mmd\x1b[0m\x1b[0;36m 18:c914c99f1fbb\x1b[0m (esc)
472 601 \x1b[0;31mb\x1b[0m\x1b[0;36m 13:e23b5505d1ad\x1b[0m (esc)
473 602 \x1b[0;32ma branch name much longer than the default justification used by branches\x1b[0m\x1b[0;36m 7:10ff5895aa57\x1b[0m (esc)
603 \x1b[0;35mm\x1b[0m\x1b[0;36m 17:df343b0df04f\x1b[0m (inactive) (esc)
474 604 \x1b[0;34mc\x1b[0m\x1b[0;36m 14:f894c25619d3\x1b[0m (closed) (esc)
475 605 \x1b[0;35ma\x1b[0m\x1b[0;36m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc)
476 606 \x1b[0;35mdefault\x1b[0m\x1b[0;36m 0:19709c5a4e75\x1b[0m (inactive) (esc)
477 607
478 608 template output:
479 609
480 610 $ hg branches -Tjson --closed
481 611 [
482 612 {
483 613 "active": true,
614 "branch": "md",
615 "closed": false,
616 "current": false,
617 "node": "c914c99f1fbb2b1d785a0a939ed3f67275df18e9",
618 "rev": 18
619 },
620 {
621 "active": true,
484 622 "branch": "b",
485 623 "closed": false,
486 624 "current": true,
487 625 "node": "e23b5505d1ad24aab6f84fd8c7cb8cd8e5e93be0",
488 626 "rev": 13
489 627 },
490 628 {
491 629 "active": true,
492 630 "branch": "a branch name much longer than the default justification used by branches",
493 631 "closed": false,
494 632 "current": false,
495 633 "node": "10ff5895aa5793bd378da574af8cec8ea408d831",
496 634 "rev": 7
497 635 },
498 636 {
499 637 "active": false,
638 "branch": "m",
639 "closed": false,
640 "current": false,
641 "node": "df343b0df04feb2a946cd4b6e9520e552fef14ee",
642 "rev": 17
643 },
644 {
645 "active": false,
500 646 "branch": "c",
501 647 "closed": true,
502 648 "current": false,
503 649 "node": "f894c25619d3f1484639d81be950e0a07bc6f1f6",
504 650 "rev": 14
505 651 },
506 652 {
507 653 "active": false,
508 654 "branch": "a",
509 655 "closed": false,
510 656 "current": false,
511 657 "node": "d8cbc61dbaa6dc817175d1e301eecb863f280832",
512 658 "rev": 5
513 659 },
514 660 {
515 661 "active": false,
516 662 "branch": "default",
517 663 "closed": false,
518 664 "current": false,
519 665 "node": "19709c5a4e75bf938f8e349aff97438539bb729e",
520 666 "rev": 0
521 667 }
522 668 ]
523 669
524 670 $ hg branches --closed -T '{if(closed, "{branch}\n")}'
525 671 c
526 672
527 673 $ hg branches -T '{word(0, branch)}: {desc|firstline}\n'
674 md: merge head 2
528 675 b: reopen branch with a change
529 676 a: Adding d branch
677 m: head 2
530 678 a: Adding b branch head 2
531 679 default: Adding root node
532 680
533 681 $ cat <<'EOF' > "$TESTTMP/map-myjson"
534 682 > docheader = '\{\n'
535 683 > docfooter = '\n}\n'
536 684 > separator = ',\n'
537 685 > branches = ' {dict(branch, node|short)|json}'
538 686 > EOF
539 687 $ hg branches -T "$TESTTMP/map-myjson"
540 688 {
689 {"branch": "md", "node": "c914c99f1fbb"},
541 690 {"branch": "b", "node": "e23b5505d1ad"},
542 691 {"branch": "a branch *", "node": "10ff5895aa57"}, (glob)
692 {"branch": "m", "node": "df343b0df04f"},
543 693 {"branch": "a", "node": "d8cbc61dbaa6"},
544 694 {"branch": "default", "node": "19709c5a4e75"}
545 695 }
546 696
547 697 $ cat <<'EOF' >> .hg/hgrc
548 698 > [templates]
549 699 > myjson = ' {dict(branch, node|short)|json}'
550 700 > myjson:docheader = '\{\n'
551 701 > myjson:docfooter = '\n}\n'
552 702 > myjson:separator = ',\n'
553 703 > EOF
554 704 $ hg branches -T myjson
555 705 {
706 {"branch": "md", "node": "c914c99f1fbb"},
556 707 {"branch": "b", "node": "e23b5505d1ad"},
557 708 {"branch": "a branch *", "node": "10ff5895aa57"}, (glob)
709 {"branch": "m", "node": "df343b0df04f"},
558 710 {"branch": "a", "node": "d8cbc61dbaa6"},
559 711 {"branch": "default", "node": "19709c5a4e75"}
560 712 }
561 713
562 714 $ cat <<'EOF' >> .hg/hgrc
563 715 > [templates]
564 716 > :docheader = 'should not be selected as a docheader for literal templates\n'
565 717 > EOF
566 718 $ hg branches -T '{branch}\n'
719 md
567 720 b
568 721 a branch name much longer than the default justification used by branches
722 m
569 723 a
570 724 default
571 725
572 726 Tests of revision branch name caching
573 727
574 728 We rev branch cache is updated automatically. In these tests we use a trick to
575 729 trigger rebuilds. We remove the branch head cache and run 'hg head' to cause a
576 730 rebuild that also will populate the rev branch cache.
577 731
578 732 revision branch cache is created when building the branch head cache
579 733 $ rm -rf .hg/cache; hg head a -T '{rev}\n'
580 734 5
581 735 $ f --hexdump --size .hg/cache/rbc-*
582 .hg/cache/rbc-names-v1: size=87
736 .hg/cache/rbc-names-v1: size=92
583 737 0000: 64 65 66 61 75 6c 74 00 61 00 62 00 63 00 61 20 |default.a.b.c.a |
584 738 0010: 62 72 61 6e 63 68 20 6e 61 6d 65 20 6d 75 63 68 |branch name much|
585 739 0020: 20 6c 6f 6e 67 65 72 20 74 68 61 6e 20 74 68 65 | longer than the|
586 740 0030: 20 64 65 66 61 75 6c 74 20 6a 75 73 74 69 66 69 | default justifi|
587 741 0040: 63 61 74 69 6f 6e 20 75 73 65 64 20 62 79 20 62 |cation used by b|
588 0050: 72 61 6e 63 68 65 73 |ranches|
589 .hg/cache/rbc-revs-v1: size=120
742 0050: 72 61 6e 63 68 65 73 00 6d 00 6d 64 |ranches.m.md|
743 .hg/cache/rbc-revs-v1: size=160
590 744 0000: 19 70 9c 5a 00 00 00 00 dd 6b 44 0d 00 00 00 01 |.p.Z.....kD.....|
591 745 0010: 88 1f e2 b9 00 00 00 01 ac 22 03 33 00 00 00 02 |.........".3....|
592 746 0020: ae e3 9c d1 00 00 00 02 d8 cb c6 1d 00 00 00 01 |................|
593 747 0030: 58 97 36 a2 00 00 00 03 10 ff 58 95 00 00 00 04 |X.6.......X.....|
594 748 0040: ee bb 94 44 00 00 00 02 5f 40 61 bb 00 00 00 02 |...D...._@a.....|
595 749 0050: bf be 84 1b 00 00 00 02 d3 f1 63 45 80 00 00 02 |..........cE....|
596 750 0060: e3 d4 9c 05 80 00 00 02 e2 3b 55 05 00 00 00 02 |.........;U.....|
597 0070: f8 94 c2 56 80 00 00 03 |...V....|
751 0070: f8 94 c2 56 80 00 00 03 f3 44 76 37 00 00 00 05 |...V.....Dv7....|
752 0080: a5 8c a5 d3 00 00 00 05 df 34 3b 0d 00 00 00 05 |.........4;.....|
753 0090: c9 14 c9 9f 00 00 00 06 cd 21 a8 0b 80 00 00 05 |.........!......|
598 754
599 755 no errors when revbranchcache is not writable
600 756
601 757 $ echo >> .hg/cache/rbc-revs-v1
602 758 $ mv .hg/cache/rbc-revs-v1 .hg/cache/rbc-revs-v1_
603 759 $ mkdir .hg/cache/rbc-revs-v1
604 760 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n'
605 761 5
606 762 $ rmdir .hg/cache/rbc-revs-v1
607 763 $ mv .hg/cache/rbc-revs-v1_ .hg/cache/rbc-revs-v1
608 764
609 765 no errors when wlock cannot be acquired
610 766
611 767 #if unix-permissions
612 768 $ mv .hg/cache/rbc-revs-v1 .hg/cache/rbc-revs-v1_
613 769 $ rm -f .hg/cache/branch*
614 770 $ chmod 555 .hg
615 771 $ hg head a -T '{rev}\n'
616 772 5
617 773 $ chmod 755 .hg
618 774 $ mv .hg/cache/rbc-revs-v1_ .hg/cache/rbc-revs-v1
619 775 #endif
620 776
621 777 recovery from invalid cache revs file with trailing data
622 778 $ echo >> .hg/cache/rbc-revs-v1
623 779 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
624 780 5
625 truncating cache/rbc-revs-v1 to 120
781 truncating cache/rbc-revs-v1 to 160
626 782 $ f --size .hg/cache/rbc-revs*
627 .hg/cache/rbc-revs-v1: size=120
783 .hg/cache/rbc-revs-v1: size=160
628 784 recovery from invalid cache file with partial last record
629 785 $ mv .hg/cache/rbc-revs-v1 .
630 786 $ f -qDB 119 rbc-revs-v1 > .hg/cache/rbc-revs-v1
631 787 $ f --size .hg/cache/rbc-revs*
632 788 .hg/cache/rbc-revs-v1: size=119
633 789 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
634 790 5
635 791 truncating cache/rbc-revs-v1 to 112
636 792 $ f --size .hg/cache/rbc-revs*
637 .hg/cache/rbc-revs-v1: size=120
793 .hg/cache/rbc-revs-v1: size=160
638 794 recovery from invalid cache file with missing record - no truncation
639 795 $ mv .hg/cache/rbc-revs-v1 .
640 796 $ f -qDB 112 rbc-revs-v1 > .hg/cache/rbc-revs-v1
641 797 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
642 798 5
643 799 $ f --size .hg/cache/rbc-revs*
644 .hg/cache/rbc-revs-v1: size=120
800 .hg/cache/rbc-revs-v1: size=160
645 801 recovery from invalid cache file with some bad records
646 802 $ mv .hg/cache/rbc-revs-v1 .
647 803 $ f -qDB 8 rbc-revs-v1 > .hg/cache/rbc-revs-v1
648 804 $ f --size .hg/cache/rbc-revs*
649 805 .hg/cache/rbc-revs-v1: size=8
650 806 $ f -qDB 112 rbc-revs-v1 >> .hg/cache/rbc-revs-v1
651 807 $ f --size .hg/cache/rbc-revs*
652 808 .hg/cache/rbc-revs-v1: size=120
653 809 $ hg log -r 'branch(.)' -T '{rev} ' --debug
654 810 history modification detected - truncating revision branch cache to revision 13
655 811 history modification detected - truncating revision branch cache to revision 1
656 812 3 4 8 9 10 11 12 13 truncating cache/rbc-revs-v1 to 8
657 813 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
658 814 5
659 815 truncating cache/rbc-revs-v1 to 104
660 816 $ f --size --hexdump --bytes=16 .hg/cache/rbc-revs*
661 .hg/cache/rbc-revs-v1: size=120
817 .hg/cache/rbc-revs-v1: size=160
662 818 0000: 19 70 9c 5a 00 00 00 00 dd 6b 44 0d 00 00 00 01 |.p.Z.....kD.....|
663 819 cache is updated when committing
664 820 $ hg branch i-will-regret-this
665 821 marked working directory as branch i-will-regret-this
666 822 $ hg ci -m regrets
667 823 $ f --size .hg/cache/rbc-*
668 .hg/cache/rbc-names-v1: size=106
669 .hg/cache/rbc-revs-v1: size=128
824 .hg/cache/rbc-names-v1: size=111
825 .hg/cache/rbc-revs-v1: size=168
670 826 update after rollback - the cache will be correct but rbc-names will will still
671 827 contain the branch name even though it no longer is used
672 828 $ hg up -qr '.^'
673 829 $ hg rollback -qf
674 830 $ f --size --hexdump .hg/cache/rbc-*
675 .hg/cache/rbc-names-v1: size=106
831 .hg/cache/rbc-names-v1: size=111
676 832 0000: 64 65 66 61 75 6c 74 00 61 00 62 00 63 00 61 20 |default.a.b.c.a |
677 833 0010: 62 72 61 6e 63 68 20 6e 61 6d 65 20 6d 75 63 68 |branch name much|
678 834 0020: 20 6c 6f 6e 67 65 72 20 74 68 61 6e 20 74 68 65 | longer than the|
679 835 0030: 20 64 65 66 61 75 6c 74 20 6a 75 73 74 69 66 69 | default justifi|
680 836 0040: 63 61 74 69 6f 6e 20 75 73 65 64 20 62 79 20 62 |cation used by b|
681 0050: 72 61 6e 63 68 65 73 00 69 2d 77 69 6c 6c 2d 72 |ranches.i-will-r|
682 0060: 65 67 72 65 74 2d 74 68 69 73 |egret-this|
683 .hg/cache/rbc-revs-v1: size=120
837 0050: 72 61 6e 63 68 65 73 00 6d 00 6d 64 00 69 2d 77 |ranches.m.md.i-w|
838 0060: 69 6c 6c 2d 72 65 67 72 65 74 2d 74 68 69 73 |ill-regret-this|
839 .hg/cache/rbc-revs-v1: size=160
684 840 0000: 19 70 9c 5a 00 00 00 00 dd 6b 44 0d 00 00 00 01 |.p.Z.....kD.....|
685 841 0010: 88 1f e2 b9 00 00 00 01 ac 22 03 33 00 00 00 02 |.........".3....|
686 842 0020: ae e3 9c d1 00 00 00 02 d8 cb c6 1d 00 00 00 01 |................|
687 843 0030: 58 97 36 a2 00 00 00 03 10 ff 58 95 00 00 00 04 |X.6.......X.....|
688 844 0040: ee bb 94 44 00 00 00 02 5f 40 61 bb 00 00 00 02 |...D...._@a.....|
689 845 0050: bf be 84 1b 00 00 00 02 d3 f1 63 45 80 00 00 02 |..........cE....|
690 846 0060: e3 d4 9c 05 80 00 00 02 e2 3b 55 05 00 00 00 02 |.........;U.....|
691 0070: f8 94 c2 56 80 00 00 03 |...V....|
847 0070: f8 94 c2 56 80 00 00 03 f3 44 76 37 00 00 00 05 |...V.....Dv7....|
848 0080: a5 8c a5 d3 00 00 00 05 df 34 3b 0d 00 00 00 05 |.........4;.....|
849 0090: c9 14 c9 9f 00 00 00 06 cd 21 a8 0b 80 00 00 05 |.........!......|
692 850 cache is updated/truncated when stripping - it is thus very hard to get in a
693 851 situation where the cache is out of sync and the hash check detects it
694 852 $ hg --config extensions.strip= strip -r tip --nob
695 853 $ f --size .hg/cache/rbc-revs*
696 .hg/cache/rbc-revs-v1: size=112
854 .hg/cache/rbc-revs-v1: size=152
697 855
698 856 cache is rebuilt when corruption is detected
699 857 $ echo > .hg/cache/rbc-names-v1
700 858 $ hg log -r '5:&branch(.)' -T '{rev} ' --debug
701 859 referenced branch names not found - rebuilding revision branch cache from scratch
702 860 8 9 10 11 12 13 truncating cache/rbc-revs-v1 to 40
703 861 $ f --size --hexdump .hg/cache/rbc-*
704 .hg/cache/rbc-names-v1: size=79
862 .hg/cache/rbc-names-v1: size=84
705 863 0000: 62 00 61 00 63 00 61 20 62 72 61 6e 63 68 20 6e |b.a.c.a branch n|
706 864 0010: 61 6d 65 20 6d 75 63 68 20 6c 6f 6e 67 65 72 20 |ame much longer |
707 865 0020: 74 68 61 6e 20 74 68 65 20 64 65 66 61 75 6c 74 |than the default|
708 866 0030: 20 6a 75 73 74 69 66 69 63 61 74 69 6f 6e 20 75 | justification u|
709 0040: 73 65 64 20 62 79 20 62 72 61 6e 63 68 65 73 |sed by branches|
710 .hg/cache/rbc-revs-v1: size=112
867 0040: 73 65 64 20 62 79 20 62 72 61 6e 63 68 65 73 00 |sed by branches.|
868 0050: 6d 00 6d 64 |m.md|
869 .hg/cache/rbc-revs-v1: size=152
711 870 0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
712 871 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
713 872 0020: 00 00 00 00 00 00 00 00 d8 cb c6 1d 00 00 00 01 |................|
714 873 0030: 58 97 36 a2 00 00 00 02 10 ff 58 95 00 00 00 03 |X.6.......X.....|
715 874 0040: ee bb 94 44 00 00 00 00 5f 40 61 bb 00 00 00 00 |...D...._@a.....|
716 875 0050: bf be 84 1b 00 00 00 00 d3 f1 63 45 80 00 00 00 |..........cE....|
717 876 0060: e3 d4 9c 05 80 00 00 00 e2 3b 55 05 00 00 00 00 |.........;U.....|
877 0070: f8 94 c2 56 80 00 00 02 f3 44 76 37 00 00 00 04 |...V.....Dv7....|
878 0080: a5 8c a5 d3 00 00 00 04 df 34 3b 0d 00 00 00 04 |.........4;.....|
879 0090: c9 14 c9 9f 00 00 00 05 |........|
718 880
719 881 Test that cache files are created and grows correctly:
720 882
721 883 $ rm .hg/cache/rbc*
722 884 $ hg log -r "5 & branch(5)" -T "{rev}\n"
723 885 5
724 886 $ f --size --hexdump .hg/cache/rbc-*
725 887 .hg/cache/rbc-names-v1: size=1
726 888 0000: 61 |a|
727 .hg/cache/rbc-revs-v1: size=112
889 .hg/cache/rbc-revs-v1: size=152
728 890 0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
729 891 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
730 892 0020: 00 00 00 00 00 00 00 00 d8 cb c6 1d 00 00 00 00 |................|
731 893 0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
732 894 0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
733 895 0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
734 896 0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
897 0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
898 0080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
899 0090: 00 00 00 00 00 00 00 00 |........|
735 900
736 901 $ cd ..
737 902
738 903 Test for multiple incorrect branch cache entries:
739 904
740 905 $ hg init b
741 906 $ cd b
742 907 $ touch f
743 908 $ hg ci -Aqmf
744 909 $ echo >> f
745 910 $ hg ci -Amf
746 911 $ hg branch -q branch
747 912 $ hg ci -Amf
748 913
749 914 $ f --size --hexdump .hg/cache/rbc-*
750 915 .hg/cache/rbc-names-v1: size=14
751 916 0000: 64 65 66 61 75 6c 74 00 62 72 61 6e 63 68 |default.branch|
752 917 .hg/cache/rbc-revs-v1: size=24
753 918 0000: 66 e5 f5 aa 00 00 00 00 fa 4c 04 e5 00 00 00 00 |f........L......|
754 919 0010: 56 46 78 69 00 00 00 01 |VFxi....|
755 920 $ : > .hg/cache/rbc-revs-v1
756 921
757 922 No superfluous rebuilding of cache:
758 923 $ hg log -r "branch(null)&branch(branch)" --debug
759 924 $ f --size --hexdump .hg/cache/rbc-*
760 925 .hg/cache/rbc-names-v1: size=14
761 926 0000: 64 65 66 61 75 6c 74 00 62 72 61 6e 63 68 |default.branch|
762 927 .hg/cache/rbc-revs-v1: size=24
763 928 0000: 66 e5 f5 aa 00 00 00 00 fa 4c 04 e5 00 00 00 00 |f........L......|
764 929 0010: 56 46 78 69 00 00 00 01 |VFxi....|
765 930
766 931 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now