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