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