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