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