##// END OF EJS Templates
debug: add debugwireargs to test argument passing over the wire...
Peter Arrenbrecht -
r13720:9c4e04fe default
parent child Browse files
Show More
@@ -0,0 +1,42 b''
1
2 Test wire protocol argument passing
3
4 Setup repo:
5
6 $ hg init repo
7
8 Local:
9
10 $ hg debugwireargs repo eins zwei
11 eins zwei None None
12
13 HTTP:
14
15 $ hg serve -R repo -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
16 $ cat hg1.pid >> $DAEMON_PIDS
17
18 $ hg debugwireargs http://localhost:$HGPORT/ eins zwei
19 eins zwei None None
20 $ cat access.log
21 * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
22 * - - [*] "GET /?cmd=debugwireargs&one=eins&two=zwei HTTP/1.1" 200 - (glob)
23 * - - [*] "GET /?cmd=debugwireargs&one=eins&two=zwei HTTP/1.1" 200 - (glob)
24
25 SSH (try to exercise the ssh functionality with a dummy script):
26
27 $ cat <<EOF > dummyssh
28 > import sys
29 > import os
30 > os.chdir(os.path.dirname(sys.argv[0]))
31 > if sys.argv[1] != "user@dummy":
32 > sys.exit(-1)
33 > if not os.path.exists("dummyssh"):
34 > sys.exit(-1)
35 > os.environ["SSH_CLIENT"] = "127.0.0.1 1 2"
36 > r = os.system(sys.argv[2])
37 > sys.exit(bool(r))
38 > EOF
39
40 $ hg debugwireargs --ssh "python ./dummyssh" ssh://user@dummy/repo eins zwei
41 eins zwei None None
42
@@ -1,4794 +1,4815 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, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _, gettext
11 11 import os, re, sys, difflib, time, tempfile
12 12 import hg, util, revlog, extensions, copies, error, bookmarks
13 13 import patch, help, mdiff, url, encoding, templatekw, discovery
14 14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 15 import merge as mergemod
16 16 import minirst, revset, templatefilters
17 17 import dagparser
18 18
19 19 # Commands start here, listed alphabetically
20 20
21 21 def add(ui, repo, *pats, **opts):
22 22 """add the specified files on the next commit
23 23
24 24 Schedule files to be version controlled and added to the
25 25 repository.
26 26
27 27 The files will be added to the repository at the next commit. To
28 28 undo an add before that, see :hg:`forget`.
29 29
30 30 If no names are given, add all files to the repository.
31 31
32 32 .. container:: verbose
33 33
34 34 An example showing how new (unknown) files are added
35 35 automatically by :hg:`add`::
36 36
37 37 $ ls
38 38 foo.c
39 39 $ hg status
40 40 ? foo.c
41 41 $ hg add
42 42 adding foo.c
43 43 $ hg status
44 44 A foo.c
45 45
46 46 Returns 0 if all files are successfully added.
47 47 """
48 48
49 49 m = cmdutil.match(repo, pats, opts)
50 50 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
51 51 opts.get('subrepos'), prefix="")
52 52 return rejected and 1 or 0
53 53
54 54 def addremove(ui, repo, *pats, **opts):
55 55 """add all new files, delete all missing files
56 56
57 57 Add all new files and remove all missing files from the
58 58 repository.
59 59
60 60 New files are ignored if they match any of the patterns in
61 61 ``.hgignore``. As with add, these changes take effect at the next
62 62 commit.
63 63
64 64 Use the -s/--similarity option to detect renamed files. With a
65 65 parameter greater than 0, this compares every removed file with
66 66 every added file and records those similar enough as renames. This
67 67 option takes a percentage between 0 (disabled) and 100 (files must
68 68 be identical) as its parameter. Detecting renamed files this way
69 69 can be expensive. After using this option, :hg:`status -C` can be
70 70 used to check which files were identified as moved or renamed.
71 71
72 72 Returns 0 if all files are successfully added.
73 73 """
74 74 try:
75 75 sim = float(opts.get('similarity') or 100)
76 76 except ValueError:
77 77 raise util.Abort(_('similarity must be a number'))
78 78 if sim < 0 or sim > 100:
79 79 raise util.Abort(_('similarity must be between 0 and 100'))
80 80 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
81 81
82 82 def annotate(ui, repo, *pats, **opts):
83 83 """show changeset information by line for each file
84 84
85 85 List changes in files, showing the revision id responsible for
86 86 each line
87 87
88 88 This command is useful for discovering when a change was made and
89 89 by whom.
90 90
91 91 Without the -a/--text option, annotate will avoid processing files
92 92 it detects as binary. With -a, annotate will annotate the file
93 93 anyway, although the results will probably be neither useful
94 94 nor desirable.
95 95
96 96 Returns 0 on success.
97 97 """
98 98 if opts.get('follow'):
99 99 # --follow is deprecated and now just an alias for -f/--file
100 100 # to mimic the behavior of Mercurial before version 1.5
101 101 opts['file'] = 1
102 102
103 103 datefunc = ui.quiet and util.shortdate or util.datestr
104 104 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
105 105
106 106 if not pats:
107 107 raise util.Abort(_('at least one filename or pattern is required'))
108 108
109 109 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
110 110 ('number', lambda x: str(x[0].rev())),
111 111 ('changeset', lambda x: short(x[0].node())),
112 112 ('date', getdate),
113 113 ('file', lambda x: x[0].path()),
114 114 ]
115 115
116 116 if (not opts.get('user') and not opts.get('changeset')
117 117 and not opts.get('date') and not opts.get('file')):
118 118 opts['number'] = 1
119 119
120 120 linenumber = opts.get('line_number') is not None
121 121 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
122 122 raise util.Abort(_('at least one of -n/-c is required for -l'))
123 123
124 124 funcmap = [func for op, func in opmap if opts.get(op)]
125 125 if linenumber:
126 126 lastfunc = funcmap[-1]
127 127 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
128 128
129 129 def bad(x, y):
130 130 raise util.Abort("%s: %s" % (x, y))
131 131
132 132 ctx = cmdutil.revsingle(repo, opts.get('rev'))
133 133 m = cmdutil.match(repo, pats, opts)
134 134 m.bad = bad
135 135 follow = not opts.get('no_follow')
136 136 for abs in ctx.walk(m):
137 137 fctx = ctx[abs]
138 138 if not opts.get('text') and util.binary(fctx.data()):
139 139 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
140 140 continue
141 141
142 142 lines = fctx.annotate(follow=follow, linenumber=linenumber)
143 143 pieces = []
144 144
145 145 for f in funcmap:
146 146 l = [f(n) for n, dummy in lines]
147 147 if l:
148 148 sized = [(x, encoding.colwidth(x)) for x in l]
149 149 ml = max([w for x, w in sized])
150 150 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
151 151
152 152 if pieces:
153 153 for p, l in zip(zip(*pieces), lines):
154 154 ui.write("%s: %s" % (" ".join(p), l[1]))
155 155
156 156 def archive(ui, repo, dest, **opts):
157 157 '''create an unversioned archive of a repository revision
158 158
159 159 By default, the revision used is the parent of the working
160 160 directory; use -r/--rev to specify a different revision.
161 161
162 162 The archive type is automatically detected based on file
163 163 extension (or override using -t/--type).
164 164
165 165 Valid types are:
166 166
167 167 :``files``: a directory full of files (default)
168 168 :``tar``: tar archive, uncompressed
169 169 :``tbz2``: tar archive, compressed using bzip2
170 170 :``tgz``: tar archive, compressed using gzip
171 171 :``uzip``: zip archive, uncompressed
172 172 :``zip``: zip archive, compressed using deflate
173 173
174 174 The exact name of the destination archive or directory is given
175 175 using a format string; see :hg:`help export` for details.
176 176
177 177 Each member added to an archive file has a directory prefix
178 178 prepended. Use -p/--prefix to specify a format string for the
179 179 prefix. The default is the basename of the archive, with suffixes
180 180 removed.
181 181
182 182 Returns 0 on success.
183 183 '''
184 184
185 185 ctx = cmdutil.revsingle(repo, opts.get('rev'))
186 186 if not ctx:
187 187 raise util.Abort(_('no working directory: please specify a revision'))
188 188 node = ctx.node()
189 189 dest = cmdutil.make_filename(repo, dest, node)
190 190 if os.path.realpath(dest) == repo.root:
191 191 raise util.Abort(_('repository root cannot be destination'))
192 192
193 193 kind = opts.get('type') or archival.guesskind(dest) or 'files'
194 194 prefix = opts.get('prefix')
195 195
196 196 if dest == '-':
197 197 if kind == 'files':
198 198 raise util.Abort(_('cannot archive plain files to stdout'))
199 199 dest = sys.stdout
200 200 if not prefix:
201 201 prefix = os.path.basename(repo.root) + '-%h'
202 202
203 203 prefix = cmdutil.make_filename(repo, prefix, node)
204 204 matchfn = cmdutil.match(repo, [], opts)
205 205 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
206 206 matchfn, prefix, subrepos=opts.get('subrepos'))
207 207
208 208 def backout(ui, repo, node=None, rev=None, **opts):
209 209 '''reverse effect of earlier changeset
210 210
211 211 Prepare a new changeset with the effect of REV undone in the
212 212 current working directory.
213 213
214 214 If REV is the parent of the working directory, then this new changeset
215 215 is committed automatically. Otherwise, hg needs to merge the
216 216 changes and the merged result is left uncommitted.
217 217
218 218 By default, the pending changeset will have one parent,
219 219 maintaining a linear history. With --merge, the pending changeset
220 220 will instead have two parents: the old parent of the working
221 221 directory and a new child of REV that simply undoes REV.
222 222
223 223 Before version 1.7, the behavior without --merge was equivalent to
224 224 specifying --merge followed by :hg:`update --clean .` to cancel
225 225 the merge and leave the child of REV as a head to be merged
226 226 separately.
227 227
228 228 See :hg:`help dates` for a list of formats valid for -d/--date.
229 229
230 230 Returns 0 on success.
231 231 '''
232 232 if rev and node:
233 233 raise util.Abort(_("please specify just one revision"))
234 234
235 235 if not rev:
236 236 rev = node
237 237
238 238 if not rev:
239 239 raise util.Abort(_("please specify a revision to backout"))
240 240
241 241 date = opts.get('date')
242 242 if date:
243 243 opts['date'] = util.parsedate(date)
244 244
245 245 cmdutil.bail_if_changed(repo)
246 246 node = cmdutil.revsingle(repo, rev).node()
247 247
248 248 op1, op2 = repo.dirstate.parents()
249 249 a = repo.changelog.ancestor(op1, node)
250 250 if a != node:
251 251 raise util.Abort(_('cannot backout change on a different branch'))
252 252
253 253 p1, p2 = repo.changelog.parents(node)
254 254 if p1 == nullid:
255 255 raise util.Abort(_('cannot backout a change with no parents'))
256 256 if p2 != nullid:
257 257 if not opts.get('parent'):
258 258 raise util.Abort(_('cannot backout a merge changeset without '
259 259 '--parent'))
260 260 p = repo.lookup(opts['parent'])
261 261 if p not in (p1, p2):
262 262 raise util.Abort(_('%s is not a parent of %s') %
263 263 (short(p), short(node)))
264 264 parent = p
265 265 else:
266 266 if opts.get('parent'):
267 267 raise util.Abort(_('cannot use --parent on non-merge changeset'))
268 268 parent = p1
269 269
270 270 # the backout should appear on the same branch
271 271 branch = repo.dirstate.branch()
272 272 hg.clean(repo, node, show_stats=False)
273 273 repo.dirstate.setbranch(branch)
274 274 revert_opts = opts.copy()
275 275 revert_opts['date'] = None
276 276 revert_opts['all'] = True
277 277 revert_opts['rev'] = hex(parent)
278 278 revert_opts['no_backup'] = None
279 279 revert(ui, repo, **revert_opts)
280 280 if not opts.get('merge') and op1 != node:
281 281 try:
282 282 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
283 283 return hg.update(repo, op1)
284 284 finally:
285 285 ui.setconfig('ui', 'forcemerge', '')
286 286
287 287 commit_opts = opts.copy()
288 288 commit_opts['addremove'] = False
289 289 if not commit_opts['message'] and not commit_opts['logfile']:
290 290 # we don't translate commit messages
291 291 commit_opts['message'] = "Backed out changeset %s" % short(node)
292 292 commit_opts['force_editor'] = True
293 293 commit(ui, repo, **commit_opts)
294 294 def nice(node):
295 295 return '%d:%s' % (repo.changelog.rev(node), short(node))
296 296 ui.status(_('changeset %s backs out changeset %s\n') %
297 297 (nice(repo.changelog.tip()), nice(node)))
298 298 if opts.get('merge') and op1 != node:
299 299 hg.clean(repo, op1, show_stats=False)
300 300 ui.status(_('merging with changeset %s\n')
301 301 % nice(repo.changelog.tip()))
302 302 try:
303 303 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
304 304 return hg.merge(repo, hex(repo.changelog.tip()))
305 305 finally:
306 306 ui.setconfig('ui', 'forcemerge', '')
307 307 return 0
308 308
309 309 def bisect(ui, repo, rev=None, extra=None, command=None,
310 310 reset=None, good=None, bad=None, skip=None, extend=None,
311 311 noupdate=None):
312 312 """subdivision search of changesets
313 313
314 314 This command helps to find changesets which introduce problems. To
315 315 use, mark the earliest changeset you know exhibits the problem as
316 316 bad, then mark the latest changeset which is free from the problem
317 317 as good. Bisect will update your working directory to a revision
318 318 for testing (unless the -U/--noupdate option is specified). Once
319 319 you have performed tests, mark the working directory as good or
320 320 bad, and bisect will either update to another candidate changeset
321 321 or announce that it has found the bad revision.
322 322
323 323 As a shortcut, you can also use the revision argument to mark a
324 324 revision as good or bad without checking it out first.
325 325
326 326 If you supply a command, it will be used for automatic bisection.
327 327 Its exit status will be used to mark revisions as good or bad:
328 328 status 0 means good, 125 means to skip the revision, 127
329 329 (command not found) will abort the bisection, and any other
330 330 non-zero exit status means the revision is bad.
331 331
332 332 Returns 0 on success.
333 333 """
334 334 def extendbisectrange(nodes, good):
335 335 # bisect is incomplete when it ends on a merge node and
336 336 # one of the parent was not checked.
337 337 parents = repo[nodes[0]].parents()
338 338 if len(parents) > 1:
339 339 side = good and state['bad'] or state['good']
340 340 num = len(set(i.node() for i in parents) & set(side))
341 341 if num == 1:
342 342 return parents[0].ancestor(parents[1])
343 343 return None
344 344
345 345 def print_result(nodes, good):
346 346 displayer = cmdutil.show_changeset(ui, repo, {})
347 347 if len(nodes) == 1:
348 348 # narrowed it down to a single revision
349 349 if good:
350 350 ui.write(_("The first good revision is:\n"))
351 351 else:
352 352 ui.write(_("The first bad revision is:\n"))
353 353 displayer.show(repo[nodes[0]])
354 354 parents = repo[nodes[0]].parents()
355 355 extendnode = extendbisectrange(nodes, good)
356 356 if extendnode is not None:
357 357 ui.write(_('Not all ancestors of this changeset have been'
358 358 ' checked.\nUse bisect --extend to continue the '
359 359 'bisection from\nthe common ancestor, %s.\n')
360 360 % short(extendnode.node()))
361 361 else:
362 362 # multiple possible revisions
363 363 if good:
364 364 ui.write(_("Due to skipped revisions, the first "
365 365 "good revision could be any of:\n"))
366 366 else:
367 367 ui.write(_("Due to skipped revisions, the first "
368 368 "bad revision could be any of:\n"))
369 369 for n in nodes:
370 370 displayer.show(repo[n])
371 371 displayer.close()
372 372
373 373 def check_state(state, interactive=True):
374 374 if not state['good'] or not state['bad']:
375 375 if (good or bad or skip or reset) and interactive:
376 376 return
377 377 if not state['good']:
378 378 raise util.Abort(_('cannot bisect (no known good revisions)'))
379 379 else:
380 380 raise util.Abort(_('cannot bisect (no known bad revisions)'))
381 381 return True
382 382
383 383 # backward compatibility
384 384 if rev in "good bad reset init".split():
385 385 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
386 386 cmd, rev, extra = rev, extra, None
387 387 if cmd == "good":
388 388 good = True
389 389 elif cmd == "bad":
390 390 bad = True
391 391 else:
392 392 reset = True
393 393 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
394 394 raise util.Abort(_('incompatible arguments'))
395 395
396 396 if reset:
397 397 p = repo.join("bisect.state")
398 398 if os.path.exists(p):
399 399 os.unlink(p)
400 400 return
401 401
402 402 state = hbisect.load_state(repo)
403 403
404 404 if command:
405 405 changesets = 1
406 406 try:
407 407 while changesets:
408 408 # update state
409 409 status = util.system(command)
410 410 if status == 125:
411 411 transition = "skip"
412 412 elif status == 0:
413 413 transition = "good"
414 414 # status < 0 means process was killed
415 415 elif status == 127:
416 416 raise util.Abort(_("failed to execute %s") % command)
417 417 elif status < 0:
418 418 raise util.Abort(_("%s killed") % command)
419 419 else:
420 420 transition = "bad"
421 421 ctx = cmdutil.revsingle(repo, rev)
422 422 rev = None # clear for future iterations
423 423 state[transition].append(ctx.node())
424 424 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
425 425 check_state(state, interactive=False)
426 426 # bisect
427 427 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
428 428 # update to next check
429 429 cmdutil.bail_if_changed(repo)
430 430 hg.clean(repo, nodes[0], show_stats=False)
431 431 finally:
432 432 hbisect.save_state(repo, state)
433 433 print_result(nodes, good)
434 434 return
435 435
436 436 # update state
437 437
438 438 if rev:
439 439 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
440 440 else:
441 441 nodes = [repo.lookup('.')]
442 442
443 443 if good or bad or skip:
444 444 if good:
445 445 state['good'] += nodes
446 446 elif bad:
447 447 state['bad'] += nodes
448 448 elif skip:
449 449 state['skip'] += nodes
450 450 hbisect.save_state(repo, state)
451 451
452 452 if not check_state(state):
453 453 return
454 454
455 455 # actually bisect
456 456 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
457 457 if extend:
458 458 if not changesets:
459 459 extendnode = extendbisectrange(nodes, good)
460 460 if extendnode is not None:
461 461 ui.write(_("Extending search to changeset %d:%s\n"
462 462 % (extendnode.rev(), short(extendnode.node()))))
463 463 if noupdate:
464 464 return
465 465 cmdutil.bail_if_changed(repo)
466 466 return hg.clean(repo, extendnode.node())
467 467 raise util.Abort(_("nothing to extend"))
468 468
469 469 if changesets == 0:
470 470 print_result(nodes, good)
471 471 else:
472 472 assert len(nodes) == 1 # only a single node can be tested next
473 473 node = nodes[0]
474 474 # compute the approximate number of remaining tests
475 475 tests, size = 0, 2
476 476 while size <= changesets:
477 477 tests, size = tests + 1, size * 2
478 478 rev = repo.changelog.rev(node)
479 479 ui.write(_("Testing changeset %d:%s "
480 480 "(%d changesets remaining, ~%d tests)\n")
481 481 % (rev, short(node), changesets, tests))
482 482 if not noupdate:
483 483 cmdutil.bail_if_changed(repo)
484 484 return hg.clean(repo, node)
485 485
486 486 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
487 487 '''track a line of development with movable markers
488 488
489 489 Bookmarks are pointers to certain commits that move when
490 490 committing. Bookmarks are local. They can be renamed, copied and
491 491 deleted. It is possible to use bookmark names in :hg:`merge` and
492 492 :hg:`update` to merge and update respectively to a given bookmark.
493 493
494 494 You can use :hg:`bookmark NAME` to set a bookmark on the working
495 495 directory's parent revision with the given name. If you specify
496 496 a revision using -r REV (where REV may be an existing bookmark),
497 497 the bookmark is assigned to that revision.
498 498
499 499 Bookmarks can be pushed and pulled between repositories (see :hg:`help
500 500 push` and :hg:`help pull`). This requires both the local and remote
501 501 repositories to support bookmarks. For versions prior to 1.8, this means
502 502 the bookmarks extension must be enabled.
503 503 '''
504 504 hexfn = ui.debugflag and hex or short
505 505 marks = repo._bookmarks
506 506 cur = repo.changectx('.').node()
507 507
508 508 if rename:
509 509 if rename not in marks:
510 510 raise util.Abort(_("a bookmark of this name does not exist"))
511 511 if mark in marks and not force:
512 512 raise util.Abort(_("a bookmark of the same name already exists"))
513 513 if mark is None:
514 514 raise util.Abort(_("new bookmark name required"))
515 515 marks[mark] = marks[rename]
516 516 if repo._bookmarkcurrent == rename:
517 517 bookmarks.setcurrent(repo, mark)
518 518 del marks[rename]
519 519 bookmarks.write(repo)
520 520 return
521 521
522 522 if delete:
523 523 if mark is None:
524 524 raise util.Abort(_("bookmark name required"))
525 525 if mark not in marks:
526 526 raise util.Abort(_("a bookmark of this name does not exist"))
527 527 if mark == repo._bookmarkcurrent:
528 528 bookmarks.setcurrent(repo, None)
529 529 del marks[mark]
530 530 bookmarks.write(repo)
531 531 return
532 532
533 533 if mark is not None:
534 534 if "\n" in mark:
535 535 raise util.Abort(_("bookmark name cannot contain newlines"))
536 536 mark = mark.strip()
537 537 if not mark:
538 538 raise util.Abort(_("bookmark names cannot consist entirely of "
539 539 "whitespace"))
540 540 if mark in marks and not force:
541 541 raise util.Abort(_("a bookmark of the same name already exists"))
542 542 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
543 543 and not force):
544 544 raise util.Abort(
545 545 _("a bookmark cannot have the name of an existing branch"))
546 546 if rev:
547 547 marks[mark] = repo.lookup(rev)
548 548 else:
549 549 marks[mark] = repo.changectx('.').node()
550 550 if repo.changectx('.').node() == marks[mark]:
551 551 bookmarks.setcurrent(repo, mark)
552 552 bookmarks.write(repo)
553 553 return
554 554
555 555 if mark is None:
556 556 if rev:
557 557 raise util.Abort(_("bookmark name required"))
558 558 if len(marks) == 0:
559 559 ui.status(_("no bookmarks set\n"))
560 560 else:
561 561 for bmark, n in sorted(marks.iteritems()):
562 562 current = repo._bookmarkcurrent
563 563 if bmark == current and n == cur:
564 564 prefix, label = '*', 'bookmarks.current'
565 565 else:
566 566 prefix, label = ' ', ''
567 567
568 568 if ui.quiet:
569 569 ui.write("%s\n" % bmark, label=label)
570 570 else:
571 571 ui.write(" %s %-25s %d:%s\n" % (
572 572 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
573 573 label=label)
574 574 return
575 575
576 576 def branch(ui, repo, label=None, **opts):
577 577 """set or show the current branch name
578 578
579 579 With no argument, show the current branch name. With one argument,
580 580 set the working directory branch name (the branch will not exist
581 581 in the repository until the next commit). Standard practice
582 582 recommends that primary development take place on the 'default'
583 583 branch.
584 584
585 585 Unless -f/--force is specified, branch will not let you set a
586 586 branch name that already exists, even if it's inactive.
587 587
588 588 Use -C/--clean to reset the working directory branch to that of
589 589 the parent of the working directory, negating a previous branch
590 590 change.
591 591
592 592 Use the command :hg:`update` to switch to an existing branch. Use
593 593 :hg:`commit --close-branch` to mark this branch as closed.
594 594
595 595 Returns 0 on success.
596 596 """
597 597
598 598 if opts.get('clean'):
599 599 label = repo[None].parents()[0].branch()
600 600 repo.dirstate.setbranch(label)
601 601 ui.status(_('reset working directory to branch %s\n') % label)
602 602 elif label:
603 603 if not opts.get('force') and label in repo.branchtags():
604 604 if label not in [p.branch() for p in repo.parents()]:
605 605 raise util.Abort(_('a branch of the same name already exists'
606 606 " (use 'hg update' to switch to it)"))
607 607 repo.dirstate.setbranch(label)
608 608 ui.status(_('marked working directory as branch %s\n') % label)
609 609 else:
610 610 ui.write("%s\n" % repo.dirstate.branch())
611 611
612 612 def branches(ui, repo, active=False, closed=False):
613 613 """list repository named branches
614 614
615 615 List the repository's named branches, indicating which ones are
616 616 inactive. If -c/--closed is specified, also list branches which have
617 617 been marked closed (see :hg:`commit --close-branch`).
618 618
619 619 If -a/--active is specified, only show active branches. A branch
620 620 is considered active if it contains repository heads.
621 621
622 622 Use the command :hg:`update` to switch to an existing branch.
623 623
624 624 Returns 0.
625 625 """
626 626
627 627 hexfunc = ui.debugflag and hex or short
628 628 activebranches = [repo[n].branch() for n in repo.heads()]
629 629 def testactive(tag, node):
630 630 realhead = tag in activebranches
631 631 open = node in repo.branchheads(tag, closed=False)
632 632 return realhead and open
633 633 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
634 634 for tag, node in repo.branchtags().items()],
635 635 reverse=True)
636 636
637 637 for isactive, node, tag in branches:
638 638 if (not active) or isactive:
639 639 if ui.quiet:
640 640 ui.write("%s\n" % tag)
641 641 else:
642 642 hn = repo.lookup(node)
643 643 if isactive:
644 644 label = 'branches.active'
645 645 notice = ''
646 646 elif hn not in repo.branchheads(tag, closed=False):
647 647 if not closed:
648 648 continue
649 649 label = 'branches.closed'
650 650 notice = _(' (closed)')
651 651 else:
652 652 label = 'branches.inactive'
653 653 notice = _(' (inactive)')
654 654 if tag == repo.dirstate.branch():
655 655 label = 'branches.current'
656 656 rev = str(node).rjust(31 - encoding.colwidth(tag))
657 657 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
658 658 tag = ui.label(tag, label)
659 659 ui.write("%s %s%s\n" % (tag, rev, notice))
660 660
661 661 def bundle(ui, repo, fname, dest=None, **opts):
662 662 """create a changegroup file
663 663
664 664 Generate a compressed changegroup file collecting changesets not
665 665 known to be in another repository.
666 666
667 667 If you omit the destination repository, then hg assumes the
668 668 destination will have all the nodes you specify with --base
669 669 parameters. To create a bundle containing all changesets, use
670 670 -a/--all (or --base null).
671 671
672 672 You can change compression method with the -t/--type option.
673 673 The available compression methods are: none, bzip2, and
674 674 gzip (by default, bundles are compressed using bzip2).
675 675
676 676 The bundle file can then be transferred using conventional means
677 677 and applied to another repository with the unbundle or pull
678 678 command. This is useful when direct push and pull are not
679 679 available or when exporting an entire repository is undesirable.
680 680
681 681 Applying bundles preserves all changeset contents including
682 682 permissions, copy/rename information, and revision history.
683 683
684 684 Returns 0 on success, 1 if no changes found.
685 685 """
686 686 revs = None
687 687 if 'rev' in opts:
688 688 revs = cmdutil.revrange(repo, opts['rev'])
689 689
690 690 if opts.get('all'):
691 691 base = ['null']
692 692 else:
693 693 base = cmdutil.revrange(repo, opts.get('base'))
694 694 if base:
695 695 if dest:
696 696 raise util.Abort(_("--base is incompatible with specifying "
697 697 "a destination"))
698 698 base = [repo.lookup(rev) for rev in base]
699 699 # create the right base
700 700 # XXX: nodesbetween / changegroup* should be "fixed" instead
701 701 o = []
702 702 has = set((nullid,))
703 703 for n in base:
704 704 has.update(repo.changelog.reachable(n))
705 705 if revs:
706 706 revs = [repo.lookup(rev) for rev in revs]
707 707 visit = revs[:]
708 708 has.difference_update(visit)
709 709 else:
710 710 visit = repo.changelog.heads()
711 711 seen = {}
712 712 while visit:
713 713 n = visit.pop(0)
714 714 parents = [p for p in repo.changelog.parents(n) if p not in has]
715 715 if len(parents) == 0:
716 716 if n not in has:
717 717 o.append(n)
718 718 else:
719 719 for p in parents:
720 720 if p not in seen:
721 721 seen[p] = 1
722 722 visit.append(p)
723 723 else:
724 724 dest = ui.expandpath(dest or 'default-push', dest or 'default')
725 725 dest, branches = hg.parseurl(dest, opts.get('branch'))
726 726 other = hg.repository(hg.remoteui(repo, opts), dest)
727 727 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
728 728 if revs:
729 729 revs = [repo.lookup(rev) for rev in revs]
730 730 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
731 731
732 732 if not o:
733 733 ui.status(_("no changes found\n"))
734 734 return 1
735 735
736 736 if revs:
737 737 cg = repo.changegroupsubset(o, revs, 'bundle')
738 738 else:
739 739 cg = repo.changegroup(o, 'bundle')
740 740
741 741 bundletype = opts.get('type', 'bzip2').lower()
742 742 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
743 743 bundletype = btypes.get(bundletype)
744 744 if bundletype not in changegroup.bundletypes:
745 745 raise util.Abort(_('unknown bundle type specified with --type'))
746 746
747 747 changegroup.writebundle(cg, fname, bundletype)
748 748
749 749 def cat(ui, repo, file1, *pats, **opts):
750 750 """output the current or given revision of files
751 751
752 752 Print the specified files as they were at the given revision. If
753 753 no revision is given, the parent of the working directory is used,
754 754 or tip if no revision is checked out.
755 755
756 756 Output may be to a file, in which case the name of the file is
757 757 given using a format string. The formatting rules are the same as
758 758 for the export command, with the following additions:
759 759
760 760 :``%s``: basename of file being printed
761 761 :``%d``: dirname of file being printed, or '.' if in repository root
762 762 :``%p``: root-relative path name of file being printed
763 763
764 764 Returns 0 on success.
765 765 """
766 766 ctx = cmdutil.revsingle(repo, opts.get('rev'))
767 767 err = 1
768 768 m = cmdutil.match(repo, (file1,) + pats, opts)
769 769 for abs in ctx.walk(m):
770 770 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
771 771 data = ctx[abs].data()
772 772 if opts.get('decode'):
773 773 data = repo.wwritedata(abs, data)
774 774 fp.write(data)
775 775 fp.close()
776 776 err = 0
777 777 return err
778 778
779 779 def clone(ui, source, dest=None, **opts):
780 780 """make a copy of an existing repository
781 781
782 782 Create a copy of an existing repository in a new directory.
783 783
784 784 If no destination directory name is specified, it defaults to the
785 785 basename of the source.
786 786
787 787 The location of the source is added to the new repository's
788 788 ``.hg/hgrc`` file, as the default to be used for future pulls.
789 789
790 790 See :hg:`help urls` for valid source format details.
791 791
792 792 It is possible to specify an ``ssh://`` URL as the destination, but no
793 793 ``.hg/hgrc`` and working directory will be created on the remote side.
794 794 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
795 795
796 796 A set of changesets (tags, or branch names) to pull may be specified
797 797 by listing each changeset (tag, or branch name) with -r/--rev.
798 798 If -r/--rev is used, the cloned repository will contain only a subset
799 799 of the changesets of the source repository. Only the set of changesets
800 800 defined by all -r/--rev options (including all their ancestors)
801 801 will be pulled into the destination repository.
802 802 No subsequent changesets (including subsequent tags) will be present
803 803 in the destination.
804 804
805 805 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
806 806 local source repositories.
807 807
808 808 For efficiency, hardlinks are used for cloning whenever the source
809 809 and destination are on the same filesystem (note this applies only
810 810 to the repository data, not to the working directory). Some
811 811 filesystems, such as AFS, implement hardlinking incorrectly, but
812 812 do not report errors. In these cases, use the --pull option to
813 813 avoid hardlinking.
814 814
815 815 In some cases, you can clone repositories and the working directory
816 816 using full hardlinks with ::
817 817
818 818 $ cp -al REPO REPOCLONE
819 819
820 820 This is the fastest way to clone, but it is not always safe. The
821 821 operation is not atomic (making sure REPO is not modified during
822 822 the operation is up to you) and you have to make sure your editor
823 823 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
824 824 this is not compatible with certain extensions that place their
825 825 metadata under the .hg directory, such as mq.
826 826
827 827 Mercurial will update the working directory to the first applicable
828 828 revision from this list:
829 829
830 830 a) null if -U or the source repository has no changesets
831 831 b) if -u . and the source repository is local, the first parent of
832 832 the source repository's working directory
833 833 c) the changeset specified with -u (if a branch name, this means the
834 834 latest head of that branch)
835 835 d) the changeset specified with -r
836 836 e) the tipmost head specified with -b
837 837 f) the tipmost head specified with the url#branch source syntax
838 838 g) the tipmost head of the default branch
839 839 h) tip
840 840
841 841 Returns 0 on success.
842 842 """
843 843 if opts.get('noupdate') and opts.get('updaterev'):
844 844 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
845 845
846 846 r = hg.clone(hg.remoteui(ui, opts), source, dest,
847 847 pull=opts.get('pull'),
848 848 stream=opts.get('uncompressed'),
849 849 rev=opts.get('rev'),
850 850 update=opts.get('updaterev') or not opts.get('noupdate'),
851 851 branch=opts.get('branch'))
852 852
853 853 return r is None
854 854
855 855 def commit(ui, repo, *pats, **opts):
856 856 """commit the specified files or all outstanding changes
857 857
858 858 Commit changes to the given files into the repository. Unlike a
859 859 centralized SCM, this operation is a local operation. See
860 860 :hg:`push` for a way to actively distribute your changes.
861 861
862 862 If a list of files is omitted, all changes reported by :hg:`status`
863 863 will be committed.
864 864
865 865 If you are committing the result of a merge, do not provide any
866 866 filenames or -I/-X filters.
867 867
868 868 If no commit message is specified, Mercurial starts your
869 869 configured editor where you can enter a message. In case your
870 870 commit fails, you will find a backup of your message in
871 871 ``.hg/last-message.txt``.
872 872
873 873 See :hg:`help dates` for a list of formats valid for -d/--date.
874 874
875 875 Returns 0 on success, 1 if nothing changed.
876 876 """
877 877 extra = {}
878 878 if opts.get('close_branch'):
879 879 if repo['.'].node() not in repo.branchheads():
880 880 # The topo heads set is included in the branch heads set of the
881 881 # current branch, so it's sufficient to test branchheads
882 882 raise util.Abort(_('can only close branch heads'))
883 883 extra['close'] = 1
884 884 e = cmdutil.commiteditor
885 885 if opts.get('force_editor'):
886 886 e = cmdutil.commitforceeditor
887 887
888 888 def commitfunc(ui, repo, message, match, opts):
889 889 return repo.commit(message, opts.get('user'), opts.get('date'), match,
890 890 editor=e, extra=extra)
891 891
892 892 branch = repo[None].branch()
893 893 bheads = repo.branchheads(branch)
894 894
895 895 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
896 896 if not node:
897 897 ui.status(_("nothing changed\n"))
898 898 return 1
899 899
900 900 ctx = repo[node]
901 901 parents = ctx.parents()
902 902
903 903 if bheads and not [x for x in parents
904 904 if x.node() in bheads and x.branch() == branch]:
905 905 ui.status(_('created new head\n'))
906 906 # The message is not printed for initial roots. For the other
907 907 # changesets, it is printed in the following situations:
908 908 #
909 909 # Par column: for the 2 parents with ...
910 910 # N: null or no parent
911 911 # B: parent is on another named branch
912 912 # C: parent is a regular non head changeset
913 913 # H: parent was a branch head of the current branch
914 914 # Msg column: whether we print "created new head" message
915 915 # In the following, it is assumed that there already exists some
916 916 # initial branch heads of the current branch, otherwise nothing is
917 917 # printed anyway.
918 918 #
919 919 # Par Msg Comment
920 920 # NN y additional topo root
921 921 #
922 922 # BN y additional branch root
923 923 # CN y additional topo head
924 924 # HN n usual case
925 925 #
926 926 # BB y weird additional branch root
927 927 # CB y branch merge
928 928 # HB n merge with named branch
929 929 #
930 930 # CC y additional head from merge
931 931 # CH n merge with a head
932 932 #
933 933 # HH n head merge: head count decreases
934 934
935 935 if not opts.get('close_branch'):
936 936 for r in parents:
937 937 if r.extra().get('close') and r.branch() == branch:
938 938 ui.status(_('reopening closed branch head %d\n') % r)
939 939
940 940 if ui.debugflag:
941 941 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
942 942 elif ui.verbose:
943 943 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
944 944
945 945 def copy(ui, repo, *pats, **opts):
946 946 """mark files as copied for the next commit
947 947
948 948 Mark dest as having copies of source files. If dest is a
949 949 directory, copies are put in that directory. If dest is a file,
950 950 the source must be a single file.
951 951
952 952 By default, this command copies the contents of files as they
953 953 exist in the working directory. If invoked with -A/--after, the
954 954 operation is recorded, but no copying is performed.
955 955
956 956 This command takes effect with the next commit. To undo a copy
957 957 before that, see :hg:`revert`.
958 958
959 959 Returns 0 on success, 1 if errors are encountered.
960 960 """
961 961 wlock = repo.wlock(False)
962 962 try:
963 963 return cmdutil.copy(ui, repo, pats, opts)
964 964 finally:
965 965 wlock.release()
966 966
967 967 def debugancestor(ui, repo, *args):
968 968 """find the ancestor revision of two revisions in a given index"""
969 969 if len(args) == 3:
970 970 index, rev1, rev2 = args
971 971 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
972 972 lookup = r.lookup
973 973 elif len(args) == 2:
974 974 if not repo:
975 975 raise util.Abort(_("there is no Mercurial repository here "
976 976 "(.hg not found)"))
977 977 rev1, rev2 = args
978 978 r = repo.changelog
979 979 lookup = repo.lookup
980 980 else:
981 981 raise util.Abort(_('either two or three arguments required'))
982 982 a = r.ancestor(lookup(rev1), lookup(rev2))
983 983 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
984 984
985 985 def debugbuilddag(ui, repo, text,
986 986 mergeable_file=False,
987 987 appended_file=False,
988 988 overwritten_file=False,
989 989 new_file=False):
990 990 """builds a repo with a given dag from scratch in the current empty repo
991 991
992 992 Elements:
993 993
994 994 - "+n" is a linear run of n nodes based on the current default parent
995 995 - "." is a single node based on the current default parent
996 996 - "$" resets the default parent to null (implied at the start);
997 997 otherwise the default parent is always the last node created
998 998 - "<p" sets the default parent to the backref p
999 999 - "*p" is a fork at parent p, which is a backref
1000 1000 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1001 1001 - "/p2" is a merge of the preceding node and p2
1002 1002 - ":tag" defines a local tag for the preceding node
1003 1003 - "@branch" sets the named branch for subsequent nodes
1004 1004 - "!command" runs the command using your shell
1005 1005 - "!!my command\\n" is like "!", but to the end of the line
1006 1006 - "#...\\n" is a comment up to the end of the line
1007 1007
1008 1008 Whitespace between the above elements is ignored.
1009 1009
1010 1010 A backref is either
1011 1011
1012 1012 - a number n, which references the node curr-n, where curr is the current
1013 1013 node, or
1014 1014 - the name of a local tag you placed earlier using ":tag", or
1015 1015 - empty to denote the default parent.
1016 1016
1017 1017 All string valued-elements are either strictly alphanumeric, or must
1018 1018 be enclosed in double quotes ("..."), with "\\" as escape character.
1019 1019
1020 1020 Note that the --overwritten-file and --appended-file options imply the
1021 1021 use of "HGMERGE=internal:local" during DAG buildup.
1022 1022 """
1023 1023
1024 1024 if not (mergeable_file or appended_file or overwritten_file or new_file):
1025 1025 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
1026 1026
1027 1027 if len(repo.changelog) > 0:
1028 1028 raise util.Abort(_('repository is not empty'))
1029 1029
1030 1030 if overwritten_file or appended_file:
1031 1031 # we don't want to fail in merges during buildup
1032 1032 os.environ['HGMERGE'] = 'internal:local'
1033 1033
1034 1034 def writefile(fname, text, fmode="wb"):
1035 1035 f = open(fname, fmode)
1036 1036 try:
1037 1037 f.write(text)
1038 1038 finally:
1039 1039 f.close()
1040 1040
1041 1041 if mergeable_file:
1042 1042 linesperrev = 2
1043 1043 # determine number of revs in DAG
1044 1044 n = 0
1045 1045 for type, data in dagparser.parsedag(text):
1046 1046 if type == 'n':
1047 1047 n += 1
1048 1048 # make a file with k lines per rev
1049 1049 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
1050 1050 + "\n")
1051 1051
1052 1052 at = -1
1053 1053 atbranch = 'default'
1054 1054 for type, data in dagparser.parsedag(text):
1055 1055 if type == 'n':
1056 1056 ui.status('node %s\n' % str(data))
1057 1057 id, ps = data
1058 1058 p1 = ps[0]
1059 1059 if p1 != at:
1060 1060 update(ui, repo, node=str(p1), clean=True)
1061 1061 at = p1
1062 1062 if repo.dirstate.branch() != atbranch:
1063 1063 branch(ui, repo, atbranch, force=True)
1064 1064 if len(ps) > 1:
1065 1065 p2 = ps[1]
1066 1066 merge(ui, repo, node=p2)
1067 1067
1068 1068 if mergeable_file:
1069 1069 f = open("mf", "rb+")
1070 1070 try:
1071 1071 lines = f.read().split("\n")
1072 1072 lines[id * linesperrev] += " r%i" % id
1073 1073 f.seek(0)
1074 1074 f.write("\n".join(lines))
1075 1075 finally:
1076 1076 f.close()
1077 1077
1078 1078 if appended_file:
1079 1079 writefile("af", "r%i\n" % id, "ab")
1080 1080
1081 1081 if overwritten_file:
1082 1082 writefile("of", "r%i\n" % id)
1083 1083
1084 1084 if new_file:
1085 1085 writefile("nf%i" % id, "r%i\n" % id)
1086 1086
1087 1087 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
1088 1088 at = id
1089 1089 elif type == 'l':
1090 1090 id, name = data
1091 1091 ui.status('tag %s\n' % name)
1092 1092 tag(ui, repo, name, local=True)
1093 1093 elif type == 'a':
1094 1094 ui.status('branch %s\n' % data)
1095 1095 atbranch = data
1096 1096 elif type in 'cC':
1097 1097 r = util.system(data, cwd=repo.root)
1098 1098 if r:
1099 1099 desc, r = util.explain_exit(r)
1100 1100 raise util.Abort(_('%s command %s') % (data, desc))
1101 1101
1102 1102 def debugcommands(ui, cmd='', *args):
1103 1103 """list all available commands and options"""
1104 1104 for cmd, vals in sorted(table.iteritems()):
1105 1105 cmd = cmd.split('|')[0].strip('^')
1106 1106 opts = ', '.join([i[1] for i in vals[1]])
1107 1107 ui.write('%s: %s\n' % (cmd, opts))
1108 1108
1109 1109 def debugcomplete(ui, cmd='', **opts):
1110 1110 """returns the completion list associated with the given command"""
1111 1111
1112 1112 if opts.get('options'):
1113 1113 options = []
1114 1114 otables = [globalopts]
1115 1115 if cmd:
1116 1116 aliases, entry = cmdutil.findcmd(cmd, table, False)
1117 1117 otables.append(entry[1])
1118 1118 for t in otables:
1119 1119 for o in t:
1120 1120 if "(DEPRECATED)" in o[3]:
1121 1121 continue
1122 1122 if o[0]:
1123 1123 options.append('-%s' % o[0])
1124 1124 options.append('--%s' % o[1])
1125 1125 ui.write("%s\n" % "\n".join(options))
1126 1126 return
1127 1127
1128 1128 cmdlist = cmdutil.findpossible(cmd, table)
1129 1129 if ui.verbose:
1130 1130 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1131 1131 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1132 1132
1133 1133 def debugfsinfo(ui, path = "."):
1134 1134 """show information detected about current filesystem"""
1135 1135 open('.debugfsinfo', 'w').write('')
1136 1136 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1137 1137 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1138 1138 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1139 1139 and 'yes' or 'no'))
1140 1140 os.unlink('.debugfsinfo')
1141 1141
1142 1142 def debugrebuildstate(ui, repo, rev="tip"):
1143 1143 """rebuild the dirstate as it would look like for the given revision"""
1144 1144 ctx = cmdutil.revsingle(repo, rev)
1145 1145 wlock = repo.wlock()
1146 1146 try:
1147 1147 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1148 1148 finally:
1149 1149 wlock.release()
1150 1150
1151 1151 def debugcheckstate(ui, repo):
1152 1152 """validate the correctness of the current dirstate"""
1153 1153 parent1, parent2 = repo.dirstate.parents()
1154 1154 m1 = repo[parent1].manifest()
1155 1155 m2 = repo[parent2].manifest()
1156 1156 errors = 0
1157 1157 for f in repo.dirstate:
1158 1158 state = repo.dirstate[f]
1159 1159 if state in "nr" and f not in m1:
1160 1160 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1161 1161 errors += 1
1162 1162 if state in "a" and f in m1:
1163 1163 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1164 1164 errors += 1
1165 1165 if state in "m" and f not in m1 and f not in m2:
1166 1166 ui.warn(_("%s in state %s, but not in either manifest\n") %
1167 1167 (f, state))
1168 1168 errors += 1
1169 1169 for f in m1:
1170 1170 state = repo.dirstate[f]
1171 1171 if state not in "nrm":
1172 1172 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1173 1173 errors += 1
1174 1174 if errors:
1175 1175 error = _(".hg/dirstate inconsistent with current parent's manifest")
1176 1176 raise util.Abort(error)
1177 1177
1178 1178 def showconfig(ui, repo, *values, **opts):
1179 1179 """show combined config settings from all hgrc files
1180 1180
1181 1181 With no arguments, print names and values of all config items.
1182 1182
1183 1183 With one argument of the form section.name, print just the value
1184 1184 of that config item.
1185 1185
1186 1186 With multiple arguments, print names and values of all config
1187 1187 items with matching section names.
1188 1188
1189 1189 With --debug, the source (filename and line number) is printed
1190 1190 for each config item.
1191 1191
1192 1192 Returns 0 on success.
1193 1193 """
1194 1194
1195 1195 for f in util.rcpath():
1196 1196 ui.debug(_('read config from: %s\n') % f)
1197 1197 untrusted = bool(opts.get('untrusted'))
1198 1198 if values:
1199 1199 sections = [v for v in values if '.' not in v]
1200 1200 items = [v for v in values if '.' in v]
1201 1201 if len(items) > 1 or items and sections:
1202 1202 raise util.Abort(_('only one config item permitted'))
1203 1203 for section, name, value in ui.walkconfig(untrusted=untrusted):
1204 1204 value = str(value).replace('\n', '\\n')
1205 1205 sectname = section + '.' + name
1206 1206 if values:
1207 1207 for v in values:
1208 1208 if v == section:
1209 1209 ui.debug('%s: ' %
1210 1210 ui.configsource(section, name, untrusted))
1211 1211 ui.write('%s=%s\n' % (sectname, value))
1212 1212 elif v == sectname:
1213 1213 ui.debug('%s: ' %
1214 1214 ui.configsource(section, name, untrusted))
1215 1215 ui.write(value, '\n')
1216 1216 else:
1217 1217 ui.debug('%s: ' %
1218 1218 ui.configsource(section, name, untrusted))
1219 1219 ui.write('%s=%s\n' % (sectname, value))
1220 1220
1221 1221 def debugpushkey(ui, repopath, namespace, *keyinfo):
1222 1222 '''access the pushkey key/value protocol
1223 1223
1224 1224 With two args, list the keys in the given namespace.
1225 1225
1226 1226 With five args, set a key to new if it currently is set to old.
1227 1227 Reports success or failure.
1228 1228 '''
1229 1229
1230 1230 target = hg.repository(ui, repopath)
1231 1231 if keyinfo:
1232 1232 key, old, new = keyinfo
1233 1233 r = target.pushkey(namespace, key, old, new)
1234 1234 ui.status(str(r) + '\n')
1235 1235 return not r
1236 1236 else:
1237 1237 for k, v in target.listkeys(namespace).iteritems():
1238 1238 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1239 1239 v.encode('string-escape')))
1240 1240
1241 1241 def debugrevspec(ui, repo, expr):
1242 1242 '''parse and apply a revision specification'''
1243 1243 if ui.verbose:
1244 1244 tree = revset.parse(expr)[0]
1245 1245 ui.note(tree, "\n")
1246 1246 func = revset.match(expr)
1247 1247 for c in func(repo, range(len(repo))):
1248 1248 ui.write("%s\n" % c)
1249 1249
1250 1250 def debugsetparents(ui, repo, rev1, rev2=None):
1251 1251 """manually set the parents of the current working directory
1252 1252
1253 1253 This is useful for writing repository conversion tools, but should
1254 1254 be used with care.
1255 1255
1256 1256 Returns 0 on success.
1257 1257 """
1258 1258
1259 1259 r1 = cmdutil.revsingle(repo, rev1).node()
1260 1260 r2 = cmdutil.revsingle(repo, rev2, 'null').node()
1261 1261
1262 1262 wlock = repo.wlock()
1263 1263 try:
1264 1264 repo.dirstate.setparents(r1, r2)
1265 1265 finally:
1266 1266 wlock.release()
1267 1267
1268 1268 def debugstate(ui, repo, nodates=None):
1269 1269 """show the contents of the current dirstate"""
1270 1270 timestr = ""
1271 1271 showdate = not nodates
1272 1272 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1273 1273 if showdate:
1274 1274 if ent[3] == -1:
1275 1275 # Pad or slice to locale representation
1276 1276 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1277 1277 time.localtime(0)))
1278 1278 timestr = 'unset'
1279 1279 timestr = (timestr[:locale_len] +
1280 1280 ' ' * (locale_len - len(timestr)))
1281 1281 else:
1282 1282 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1283 1283 time.localtime(ent[3]))
1284 1284 if ent[1] & 020000:
1285 1285 mode = 'lnk'
1286 1286 else:
1287 1287 mode = '%3o' % (ent[1] & 0777)
1288 1288 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1289 1289 for f in repo.dirstate.copies():
1290 1290 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1291 1291
1292 1292 def debugsub(ui, repo, rev=None):
1293 1293 ctx = cmdutil.revsingle(repo, rev, None)
1294 1294 for k, v in sorted(ctx.substate.items()):
1295 1295 ui.write('path %s\n' % k)
1296 1296 ui.write(' source %s\n' % v[0])
1297 1297 ui.write(' revision %s\n' % v[1])
1298 1298
1299 1299 def debugdag(ui, repo, file_=None, *revs, **opts):
1300 1300 """format the changelog or an index DAG as a concise textual description
1301 1301
1302 1302 If you pass a revlog index, the revlog's DAG is emitted. If you list
1303 1303 revision numbers, they get labelled in the output as rN.
1304 1304
1305 1305 Otherwise, the changelog DAG of the current repo is emitted.
1306 1306 """
1307 1307 spaces = opts.get('spaces')
1308 1308 dots = opts.get('dots')
1309 1309 if file_:
1310 1310 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1311 1311 revs = set((int(r) for r in revs))
1312 1312 def events():
1313 1313 for r in rlog:
1314 1314 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1315 1315 if r in revs:
1316 1316 yield 'l', (r, "r%i" % r)
1317 1317 elif repo:
1318 1318 cl = repo.changelog
1319 1319 tags = opts.get('tags')
1320 1320 branches = opts.get('branches')
1321 1321 if tags:
1322 1322 labels = {}
1323 1323 for l, n in repo.tags().items():
1324 1324 labels.setdefault(cl.rev(n), []).append(l)
1325 1325 def events():
1326 1326 b = "default"
1327 1327 for r in cl:
1328 1328 if branches:
1329 1329 newb = cl.read(cl.node(r))[5]['branch']
1330 1330 if newb != b:
1331 1331 yield 'a', newb
1332 1332 b = newb
1333 1333 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1334 1334 if tags:
1335 1335 ls = labels.get(r)
1336 1336 if ls:
1337 1337 for l in ls:
1338 1338 yield 'l', (r, l)
1339 1339 else:
1340 1340 raise util.Abort(_('need repo for changelog dag'))
1341 1341
1342 1342 for line in dagparser.dagtextlines(events(),
1343 1343 addspaces=spaces,
1344 1344 wraplabels=True,
1345 1345 wrapannotations=True,
1346 1346 wrapnonlinear=dots,
1347 1347 usedots=dots,
1348 1348 maxlinewidth=70):
1349 1349 ui.write(line)
1350 1350 ui.write("\n")
1351 1351
1352 1352 def debugdata(ui, repo, file_, rev):
1353 1353 """dump the contents of a data file revision"""
1354 1354 r = None
1355 1355 if repo:
1356 1356 filelog = repo.file(file_)
1357 1357 if len(filelog):
1358 1358 r = filelog
1359 1359 if not r:
1360 1360 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1361 1361 try:
1362 1362 ui.write(r.revision(r.lookup(rev)))
1363 1363 except KeyError:
1364 1364 raise util.Abort(_('invalid revision identifier %s') % rev)
1365 1365
1366 1366 def debugdate(ui, date, range=None, **opts):
1367 1367 """parse and display a date"""
1368 1368 if opts["extended"]:
1369 1369 d = util.parsedate(date, util.extendeddateformats)
1370 1370 else:
1371 1371 d = util.parsedate(date)
1372 1372 ui.write("internal: %s %s\n" % d)
1373 1373 ui.write("standard: %s\n" % util.datestr(d))
1374 1374 if range:
1375 1375 m = util.matchdate(range)
1376 1376 ui.write("match: %s\n" % m(d[0]))
1377 1377
1378 1378 def debugignore(ui, repo, *values, **opts):
1379 1379 """display the combined ignore pattern"""
1380 1380 ignore = repo.dirstate._ignore
1381 1381 if hasattr(ignore, 'includepat'):
1382 1382 ui.write("%s\n" % ignore.includepat)
1383 1383 else:
1384 1384 raise util.Abort(_("no ignore patterns found"))
1385 1385
1386 1386 def debugindex(ui, repo, file_, **opts):
1387 1387 """dump the contents of an index file"""
1388 1388 r = None
1389 1389 if repo:
1390 1390 filelog = repo.file(file_)
1391 1391 if len(filelog):
1392 1392 r = filelog
1393 1393
1394 1394 format = opts.get('format', 0)
1395 1395 if format not in (0, 1):
1396 1396 raise util.Abort(_("unknown format %d") % format)
1397 1397
1398 1398 if not r:
1399 1399 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1400 1400
1401 1401 if format == 0:
1402 1402 ui.write(" rev offset length base linkrev"
1403 1403 " nodeid p1 p2\n")
1404 1404 elif format == 1:
1405 1405 ui.write(" rev flag offset length"
1406 1406 " size base link p1 p2 nodeid\n")
1407 1407
1408 1408 for i in r:
1409 1409 node = r.node(i)
1410 1410 if format == 0:
1411 1411 try:
1412 1412 pp = r.parents(node)
1413 1413 except:
1414 1414 pp = [nullid, nullid]
1415 1415 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1416 1416 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1417 1417 short(node), short(pp[0]), short(pp[1])))
1418 1418 elif format == 1:
1419 1419 pr = r.parentrevs(i)
1420 1420 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1421 1421 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1422 1422 r.base(i), r.linkrev(i), pr[0], pr[1], short(node)))
1423 1423
1424 1424 def debugindexdot(ui, repo, file_):
1425 1425 """dump an index DAG as a graphviz dot file"""
1426 1426 r = None
1427 1427 if repo:
1428 1428 filelog = repo.file(file_)
1429 1429 if len(filelog):
1430 1430 r = filelog
1431 1431 if not r:
1432 1432 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1433 1433 ui.write("digraph G {\n")
1434 1434 for i in r:
1435 1435 node = r.node(i)
1436 1436 pp = r.parents(node)
1437 1437 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1438 1438 if pp[1] != nullid:
1439 1439 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1440 1440 ui.write("}\n")
1441 1441
1442 1442 def debuginstall(ui):
1443 1443 '''test Mercurial installation
1444 1444
1445 1445 Returns 0 on success.
1446 1446 '''
1447 1447
1448 1448 def writetemp(contents):
1449 1449 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1450 1450 f = os.fdopen(fd, "wb")
1451 1451 f.write(contents)
1452 1452 f.close()
1453 1453 return name
1454 1454
1455 1455 problems = 0
1456 1456
1457 1457 # encoding
1458 1458 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1459 1459 try:
1460 1460 encoding.fromlocal("test")
1461 1461 except util.Abort, inst:
1462 1462 ui.write(" %s\n" % inst)
1463 1463 ui.write(_(" (check that your locale is properly set)\n"))
1464 1464 problems += 1
1465 1465
1466 1466 # compiled modules
1467 1467 ui.status(_("Checking installed modules (%s)...\n")
1468 1468 % os.path.dirname(__file__))
1469 1469 try:
1470 1470 import bdiff, mpatch, base85, osutil
1471 1471 except Exception, inst:
1472 1472 ui.write(" %s\n" % inst)
1473 1473 ui.write(_(" One or more extensions could not be found"))
1474 1474 ui.write(_(" (check that you compiled the extensions)\n"))
1475 1475 problems += 1
1476 1476
1477 1477 # templates
1478 1478 ui.status(_("Checking templates...\n"))
1479 1479 try:
1480 1480 import templater
1481 1481 templater.templater(templater.templatepath("map-cmdline.default"))
1482 1482 except Exception, inst:
1483 1483 ui.write(" %s\n" % inst)
1484 1484 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1485 1485 problems += 1
1486 1486
1487 1487 # patch
1488 1488 ui.status(_("Checking patch...\n"))
1489 1489 patchproblems = 0
1490 1490 a = "1\n2\n3\n4\n"
1491 1491 b = "1\n2\n3\ninsert\n4\n"
1492 1492 fa = writetemp(a)
1493 1493 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1494 1494 os.path.basename(fa))
1495 1495 fd = writetemp(d)
1496 1496
1497 1497 files = {}
1498 1498 try:
1499 1499 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1500 1500 except util.Abort, e:
1501 1501 ui.write(_(" patch call failed:\n"))
1502 1502 ui.write(" " + str(e) + "\n")
1503 1503 patchproblems += 1
1504 1504 else:
1505 1505 if list(files) != [os.path.basename(fa)]:
1506 1506 ui.write(_(" unexpected patch output!\n"))
1507 1507 patchproblems += 1
1508 1508 a = open(fa).read()
1509 1509 if a != b:
1510 1510 ui.write(_(" patch test failed!\n"))
1511 1511 patchproblems += 1
1512 1512
1513 1513 if patchproblems:
1514 1514 if ui.config('ui', 'patch'):
1515 1515 ui.write(_(" (Current patch tool may be incompatible with patch,"
1516 1516 " or misconfigured. Please check your configuration"
1517 1517 " file)\n"))
1518 1518 else:
1519 1519 ui.write(_(" Internal patcher failure, please report this error"
1520 1520 " to http://mercurial.selenic.com/wiki/BugTracker\n"))
1521 1521 problems += patchproblems
1522 1522
1523 1523 os.unlink(fa)
1524 1524 os.unlink(fd)
1525 1525
1526 1526 # editor
1527 1527 ui.status(_("Checking commit editor...\n"))
1528 1528 editor = ui.geteditor()
1529 1529 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1530 1530 if not cmdpath:
1531 1531 if editor == 'vi':
1532 1532 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1533 1533 ui.write(_(" (specify a commit editor in your configuration"
1534 1534 " file)\n"))
1535 1535 else:
1536 1536 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1537 1537 ui.write(_(" (specify a commit editor in your configuration"
1538 1538 " file)\n"))
1539 1539 problems += 1
1540 1540
1541 1541 # check username
1542 1542 ui.status(_("Checking username...\n"))
1543 1543 try:
1544 1544 ui.username()
1545 1545 except util.Abort, e:
1546 1546 ui.write(" %s\n" % e)
1547 1547 ui.write(_(" (specify a username in your configuration file)\n"))
1548 1548 problems += 1
1549 1549
1550 1550 if not problems:
1551 1551 ui.status(_("No problems detected\n"))
1552 1552 else:
1553 1553 ui.write(_("%s problems detected,"
1554 1554 " please check your install!\n") % problems)
1555 1555
1556 1556 return problems
1557 1557
1558 1558 def debugrename(ui, repo, file1, *pats, **opts):
1559 1559 """dump rename information"""
1560 1560
1561 1561 ctx = cmdutil.revsingle(repo, opts.get('rev'))
1562 1562 m = cmdutil.match(repo, (file1,) + pats, opts)
1563 1563 for abs in ctx.walk(m):
1564 1564 fctx = ctx[abs]
1565 1565 o = fctx.filelog().renamed(fctx.filenode())
1566 1566 rel = m.rel(abs)
1567 1567 if o:
1568 1568 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1569 1569 else:
1570 1570 ui.write(_("%s not renamed\n") % rel)
1571 1571
1572 1572 def debugwalk(ui, repo, *pats, **opts):
1573 1573 """show how files match on given patterns"""
1574 1574 m = cmdutil.match(repo, pats, opts)
1575 1575 items = list(repo.walk(m))
1576 1576 if not items:
1577 1577 return
1578 1578 fmt = 'f %%-%ds %%-%ds %%s' % (
1579 1579 max([len(abs) for abs in items]),
1580 1580 max([len(m.rel(abs)) for abs in items]))
1581 1581 for abs in items:
1582 1582 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1583 1583 ui.write("%s\n" % line.rstrip())
1584 1584
1585 def debugwireargs(ui, repopath, *vals, **opts):
1586 repo = hg.repository(hg.remoteui(ui, opts), repopath)
1587 for opt in remoteopts:
1588 del opts[opt[1]]
1589 args = {}
1590 for k, v in opts.iteritems():
1591 if v:
1592 args[k] = v
1593 # run twice to check that we don't mess up the stream for the next command
1594 res1 = repo.debugwireargs(*vals, **args)
1595 res2 = repo.debugwireargs(*vals, **args)
1596 ui.write("%s\n" % res1)
1597 if res1 != res2:
1598 ui.warn("%s\n" % res2)
1599
1585 1600 def diff(ui, repo, *pats, **opts):
1586 1601 """diff repository (or selected files)
1587 1602
1588 1603 Show differences between revisions for the specified files.
1589 1604
1590 1605 Differences between files are shown using the unified diff format.
1591 1606
1592 1607 .. note::
1593 1608 diff may generate unexpected results for merges, as it will
1594 1609 default to comparing against the working directory's first
1595 1610 parent changeset if no revisions are specified.
1596 1611
1597 1612 When two revision arguments are given, then changes are shown
1598 1613 between those revisions. If only one revision is specified then
1599 1614 that revision is compared to the working directory, and, when no
1600 1615 revisions are specified, the working directory files are compared
1601 1616 to its parent.
1602 1617
1603 1618 Alternatively you can specify -c/--change with a revision to see
1604 1619 the changes in that changeset relative to its first parent.
1605 1620
1606 1621 Without the -a/--text option, diff will avoid generating diffs of
1607 1622 files it detects as binary. With -a, diff will generate a diff
1608 1623 anyway, probably with undesirable results.
1609 1624
1610 1625 Use the -g/--git option to generate diffs in the git extended diff
1611 1626 format. For more information, read :hg:`help diffs`.
1612 1627
1613 1628 Returns 0 on success.
1614 1629 """
1615 1630
1616 1631 revs = opts.get('rev')
1617 1632 change = opts.get('change')
1618 1633 stat = opts.get('stat')
1619 1634 reverse = opts.get('reverse')
1620 1635
1621 1636 if revs and change:
1622 1637 msg = _('cannot specify --rev and --change at the same time')
1623 1638 raise util.Abort(msg)
1624 1639 elif change:
1625 1640 node2 = cmdutil.revsingle(repo, change, None).node()
1626 1641 node1 = repo[node2].parents()[0].node()
1627 1642 else:
1628 1643 node1, node2 = cmdutil.revpair(repo, revs)
1629 1644
1630 1645 if reverse:
1631 1646 node1, node2 = node2, node1
1632 1647
1633 1648 diffopts = patch.diffopts(ui, opts)
1634 1649 m = cmdutil.match(repo, pats, opts)
1635 1650 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1636 1651 listsubrepos=opts.get('subrepos'))
1637 1652
1638 1653 def export(ui, repo, *changesets, **opts):
1639 1654 """dump the header and diffs for one or more changesets
1640 1655
1641 1656 Print the changeset header and diffs for one or more revisions.
1642 1657
1643 1658 The information shown in the changeset header is: author, date,
1644 1659 branch name (if non-default), changeset hash, parent(s) and commit
1645 1660 comment.
1646 1661
1647 1662 .. note::
1648 1663 export may generate unexpected diff output for merge
1649 1664 changesets, as it will compare the merge changeset against its
1650 1665 first parent only.
1651 1666
1652 1667 Output may be to a file, in which case the name of the file is
1653 1668 given using a format string. The formatting rules are as follows:
1654 1669
1655 1670 :``%%``: literal "%" character
1656 1671 :``%H``: changeset hash (40 hexadecimal digits)
1657 1672 :``%N``: number of patches being generated
1658 1673 :``%R``: changeset revision number
1659 1674 :``%b``: basename of the exporting repository
1660 1675 :``%h``: short-form changeset hash (12 hexadecimal digits)
1661 1676 :``%n``: zero-padded sequence number, starting at 1
1662 1677 :``%r``: zero-padded changeset revision number
1663 1678
1664 1679 Without the -a/--text option, export will avoid generating diffs
1665 1680 of files it detects as binary. With -a, export will generate a
1666 1681 diff anyway, probably with undesirable results.
1667 1682
1668 1683 Use the -g/--git option to generate diffs in the git extended diff
1669 1684 format. See :hg:`help diffs` for more information.
1670 1685
1671 1686 With the --switch-parent option, the diff will be against the
1672 1687 second parent. It can be useful to review a merge.
1673 1688
1674 1689 Returns 0 on success.
1675 1690 """
1676 1691 changesets += tuple(opts.get('rev', []))
1677 1692 if not changesets:
1678 1693 raise util.Abort(_("export requires at least one changeset"))
1679 1694 revs = cmdutil.revrange(repo, changesets)
1680 1695 if len(revs) > 1:
1681 1696 ui.note(_('exporting patches:\n'))
1682 1697 else:
1683 1698 ui.note(_('exporting patch:\n'))
1684 1699 cmdutil.export(repo, revs, template=opts.get('output'),
1685 1700 switch_parent=opts.get('switch_parent'),
1686 1701 opts=patch.diffopts(ui, opts))
1687 1702
1688 1703 def forget(ui, repo, *pats, **opts):
1689 1704 """forget the specified files on the next commit
1690 1705
1691 1706 Mark the specified files so they will no longer be tracked
1692 1707 after the next commit.
1693 1708
1694 1709 This only removes files from the current branch, not from the
1695 1710 entire project history, and it does not delete them from the
1696 1711 working directory.
1697 1712
1698 1713 To undo a forget before the next commit, see :hg:`add`.
1699 1714
1700 1715 Returns 0 on success.
1701 1716 """
1702 1717
1703 1718 if not pats:
1704 1719 raise util.Abort(_('no files specified'))
1705 1720
1706 1721 m = cmdutil.match(repo, pats, opts)
1707 1722 s = repo.status(match=m, clean=True)
1708 1723 forget = sorted(s[0] + s[1] + s[3] + s[6])
1709 1724 errs = 0
1710 1725
1711 1726 for f in m.files():
1712 1727 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1713 1728 ui.warn(_('not removing %s: file is already untracked\n')
1714 1729 % m.rel(f))
1715 1730 errs = 1
1716 1731
1717 1732 for f in forget:
1718 1733 if ui.verbose or not m.exact(f):
1719 1734 ui.status(_('removing %s\n') % m.rel(f))
1720 1735
1721 1736 repo[None].remove(forget, unlink=False)
1722 1737 return errs
1723 1738
1724 1739 def grep(ui, repo, pattern, *pats, **opts):
1725 1740 """search for a pattern in specified files and revisions
1726 1741
1727 1742 Search revisions of files for a regular expression.
1728 1743
1729 1744 This command behaves differently than Unix grep. It only accepts
1730 1745 Python/Perl regexps. It searches repository history, not the
1731 1746 working directory. It always prints the revision number in which a
1732 1747 match appears.
1733 1748
1734 1749 By default, grep only prints output for the first revision of a
1735 1750 file in which it finds a match. To get it to print every revision
1736 1751 that contains a change in match status ("-" for a match that
1737 1752 becomes a non-match, or "+" for a non-match that becomes a match),
1738 1753 use the --all flag.
1739 1754
1740 1755 Returns 0 if a match is found, 1 otherwise.
1741 1756 """
1742 1757 reflags = 0
1743 1758 if opts.get('ignore_case'):
1744 1759 reflags |= re.I
1745 1760 try:
1746 1761 regexp = re.compile(pattern, reflags)
1747 1762 except re.error, inst:
1748 1763 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1749 1764 return 1
1750 1765 sep, eol = ':', '\n'
1751 1766 if opts.get('print0'):
1752 1767 sep = eol = '\0'
1753 1768
1754 1769 getfile = util.lrucachefunc(repo.file)
1755 1770
1756 1771 def matchlines(body):
1757 1772 begin = 0
1758 1773 linenum = 0
1759 1774 while True:
1760 1775 match = regexp.search(body, begin)
1761 1776 if not match:
1762 1777 break
1763 1778 mstart, mend = match.span()
1764 1779 linenum += body.count('\n', begin, mstart) + 1
1765 1780 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1766 1781 begin = body.find('\n', mend) + 1 or len(body)
1767 1782 lend = begin - 1
1768 1783 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1769 1784
1770 1785 class linestate(object):
1771 1786 def __init__(self, line, linenum, colstart, colend):
1772 1787 self.line = line
1773 1788 self.linenum = linenum
1774 1789 self.colstart = colstart
1775 1790 self.colend = colend
1776 1791
1777 1792 def __hash__(self):
1778 1793 return hash((self.linenum, self.line))
1779 1794
1780 1795 def __eq__(self, other):
1781 1796 return self.line == other.line
1782 1797
1783 1798 matches = {}
1784 1799 copies = {}
1785 1800 def grepbody(fn, rev, body):
1786 1801 matches[rev].setdefault(fn, [])
1787 1802 m = matches[rev][fn]
1788 1803 for lnum, cstart, cend, line in matchlines(body):
1789 1804 s = linestate(line, lnum, cstart, cend)
1790 1805 m.append(s)
1791 1806
1792 1807 def difflinestates(a, b):
1793 1808 sm = difflib.SequenceMatcher(None, a, b)
1794 1809 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1795 1810 if tag == 'insert':
1796 1811 for i in xrange(blo, bhi):
1797 1812 yield ('+', b[i])
1798 1813 elif tag == 'delete':
1799 1814 for i in xrange(alo, ahi):
1800 1815 yield ('-', a[i])
1801 1816 elif tag == 'replace':
1802 1817 for i in xrange(alo, ahi):
1803 1818 yield ('-', a[i])
1804 1819 for i in xrange(blo, bhi):
1805 1820 yield ('+', b[i])
1806 1821
1807 1822 def display(fn, ctx, pstates, states):
1808 1823 rev = ctx.rev()
1809 1824 datefunc = ui.quiet and util.shortdate or util.datestr
1810 1825 found = False
1811 1826 filerevmatches = {}
1812 1827 if opts.get('all'):
1813 1828 iter = difflinestates(pstates, states)
1814 1829 else:
1815 1830 iter = [('', l) for l in states]
1816 1831 for change, l in iter:
1817 1832 cols = [fn, str(rev)]
1818 1833 before, match, after = None, None, None
1819 1834 if opts.get('line_number'):
1820 1835 cols.append(str(l.linenum))
1821 1836 if opts.get('all'):
1822 1837 cols.append(change)
1823 1838 if opts.get('user'):
1824 1839 cols.append(ui.shortuser(ctx.user()))
1825 1840 if opts.get('date'):
1826 1841 cols.append(datefunc(ctx.date()))
1827 1842 if opts.get('files_with_matches'):
1828 1843 c = (fn, rev)
1829 1844 if c in filerevmatches:
1830 1845 continue
1831 1846 filerevmatches[c] = 1
1832 1847 else:
1833 1848 before = l.line[:l.colstart]
1834 1849 match = l.line[l.colstart:l.colend]
1835 1850 after = l.line[l.colend:]
1836 1851 ui.write(sep.join(cols))
1837 1852 if before is not None:
1838 1853 ui.write(sep + before)
1839 1854 ui.write(match, label='grep.match')
1840 1855 ui.write(after)
1841 1856 ui.write(eol)
1842 1857 found = True
1843 1858 return found
1844 1859
1845 1860 skip = {}
1846 1861 revfiles = {}
1847 1862 matchfn = cmdutil.match(repo, pats, opts)
1848 1863 found = False
1849 1864 follow = opts.get('follow')
1850 1865
1851 1866 def prep(ctx, fns):
1852 1867 rev = ctx.rev()
1853 1868 pctx = ctx.parents()[0]
1854 1869 parent = pctx.rev()
1855 1870 matches.setdefault(rev, {})
1856 1871 matches.setdefault(parent, {})
1857 1872 files = revfiles.setdefault(rev, [])
1858 1873 for fn in fns:
1859 1874 flog = getfile(fn)
1860 1875 try:
1861 1876 fnode = ctx.filenode(fn)
1862 1877 except error.LookupError:
1863 1878 continue
1864 1879
1865 1880 copied = flog.renamed(fnode)
1866 1881 copy = follow and copied and copied[0]
1867 1882 if copy:
1868 1883 copies.setdefault(rev, {})[fn] = copy
1869 1884 if fn in skip:
1870 1885 if copy:
1871 1886 skip[copy] = True
1872 1887 continue
1873 1888 files.append(fn)
1874 1889
1875 1890 if fn not in matches[rev]:
1876 1891 grepbody(fn, rev, flog.read(fnode))
1877 1892
1878 1893 pfn = copy or fn
1879 1894 if pfn not in matches[parent]:
1880 1895 try:
1881 1896 fnode = pctx.filenode(pfn)
1882 1897 grepbody(pfn, parent, flog.read(fnode))
1883 1898 except error.LookupError:
1884 1899 pass
1885 1900
1886 1901 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1887 1902 rev = ctx.rev()
1888 1903 parent = ctx.parents()[0].rev()
1889 1904 for fn in sorted(revfiles.get(rev, [])):
1890 1905 states = matches[rev][fn]
1891 1906 copy = copies.get(rev, {}).get(fn)
1892 1907 if fn in skip:
1893 1908 if copy:
1894 1909 skip[copy] = True
1895 1910 continue
1896 1911 pstates = matches.get(parent, {}).get(copy or fn, [])
1897 1912 if pstates or states:
1898 1913 r = display(fn, ctx, pstates, states)
1899 1914 found = found or r
1900 1915 if r and not opts.get('all'):
1901 1916 skip[fn] = True
1902 1917 if copy:
1903 1918 skip[copy] = True
1904 1919 del matches[rev]
1905 1920 del revfiles[rev]
1906 1921
1907 1922 return not found
1908 1923
1909 1924 def heads(ui, repo, *branchrevs, **opts):
1910 1925 """show current repository heads or show branch heads
1911 1926
1912 1927 With no arguments, show all repository branch heads.
1913 1928
1914 1929 Repository "heads" are changesets with no child changesets. They are
1915 1930 where development generally takes place and are the usual targets
1916 1931 for update and merge operations. Branch heads are changesets that have
1917 1932 no child changeset on the same branch.
1918 1933
1919 1934 If one or more REVs are given, only branch heads on the branches
1920 1935 associated with the specified changesets are shown.
1921 1936
1922 1937 If -c/--closed is specified, also show branch heads marked closed
1923 1938 (see :hg:`commit --close-branch`).
1924 1939
1925 1940 If STARTREV is specified, only those heads that are descendants of
1926 1941 STARTREV will be displayed.
1927 1942
1928 1943 If -t/--topo is specified, named branch mechanics will be ignored and only
1929 1944 changesets without children will be shown.
1930 1945
1931 1946 Returns 0 if matching heads are found, 1 if not.
1932 1947 """
1933 1948
1934 1949 start = None
1935 1950 if 'rev' in opts:
1936 1951 start = cmdutil.revsingle(repo, opts['rev'], None).node()
1937 1952
1938 1953 if opts.get('topo'):
1939 1954 heads = [repo[h] for h in repo.heads(start)]
1940 1955 else:
1941 1956 heads = []
1942 1957 for b, ls in repo.branchmap().iteritems():
1943 1958 if start is None:
1944 1959 heads += [repo[h] for h in ls]
1945 1960 continue
1946 1961 startrev = repo.changelog.rev(start)
1947 1962 descendants = set(repo.changelog.descendants(startrev))
1948 1963 descendants.add(startrev)
1949 1964 rev = repo.changelog.rev
1950 1965 heads += [repo[h] for h in ls if rev(h) in descendants]
1951 1966
1952 1967 if branchrevs:
1953 1968 branches = set(repo[br].branch() for br in branchrevs)
1954 1969 heads = [h for h in heads if h.branch() in branches]
1955 1970
1956 1971 if not opts.get('closed'):
1957 1972 heads = [h for h in heads if not h.extra().get('close')]
1958 1973
1959 1974 if opts.get('active') and branchrevs:
1960 1975 dagheads = repo.heads(start)
1961 1976 heads = [h for h in heads if h.node() in dagheads]
1962 1977
1963 1978 if branchrevs:
1964 1979 haveheads = set(h.branch() for h in heads)
1965 1980 if branches - haveheads:
1966 1981 headless = ', '.join(b for b in branches - haveheads)
1967 1982 msg = _('no open branch heads found on branches %s')
1968 1983 if opts.get('rev'):
1969 1984 msg += _(' (started at %s)' % opts['rev'])
1970 1985 ui.warn((msg + '\n') % headless)
1971 1986
1972 1987 if not heads:
1973 1988 return 1
1974 1989
1975 1990 heads = sorted(heads, key=lambda x: -x.rev())
1976 1991 displayer = cmdutil.show_changeset(ui, repo, opts)
1977 1992 for ctx in heads:
1978 1993 displayer.show(ctx)
1979 1994 displayer.close()
1980 1995
1981 1996 def help_(ui, name=None, with_version=False, unknowncmd=False):
1982 1997 """show help for a given topic or a help overview
1983 1998
1984 1999 With no arguments, print a list of commands with short help messages.
1985 2000
1986 2001 Given a topic, extension, or command name, print help for that
1987 2002 topic.
1988 2003
1989 2004 Returns 0 if successful.
1990 2005 """
1991 2006 option_lists = []
1992 2007 textwidth = min(ui.termwidth(), 80) - 2
1993 2008
1994 2009 def addglobalopts(aliases):
1995 2010 if ui.verbose:
1996 2011 option_lists.append((_("global options:"), globalopts))
1997 2012 if name == 'shortlist':
1998 2013 option_lists.append((_('use "hg help" for the full list '
1999 2014 'of commands'), ()))
2000 2015 else:
2001 2016 if name == 'shortlist':
2002 2017 msg = _('use "hg help" for the full list of commands '
2003 2018 'or "hg -v" for details')
2004 2019 elif aliases:
2005 2020 msg = _('use "hg -v help%s" to show builtin aliases and '
2006 2021 'global options') % (name and " " + name or "")
2007 2022 else:
2008 2023 msg = _('use "hg -v help %s" to show global options') % name
2009 2024 option_lists.append((msg, ()))
2010 2025
2011 2026 def helpcmd(name):
2012 2027 if with_version:
2013 2028 version_(ui)
2014 2029 ui.write('\n')
2015 2030
2016 2031 try:
2017 2032 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2018 2033 except error.AmbiguousCommand, inst:
2019 2034 # py3k fix: except vars can't be used outside the scope of the
2020 2035 # except block, nor can be used inside a lambda. python issue4617
2021 2036 prefix = inst.args[0]
2022 2037 select = lambda c: c.lstrip('^').startswith(prefix)
2023 2038 helplist(_('list of commands:\n\n'), select)
2024 2039 return
2025 2040
2026 2041 # check if it's an invalid alias and display its error if it is
2027 2042 if getattr(entry[0], 'badalias', False):
2028 2043 if not unknowncmd:
2029 2044 entry[0](ui)
2030 2045 return
2031 2046
2032 2047 # synopsis
2033 2048 if len(entry) > 2:
2034 2049 if entry[2].startswith('hg'):
2035 2050 ui.write("%s\n" % entry[2])
2036 2051 else:
2037 2052 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2038 2053 else:
2039 2054 ui.write('hg %s\n' % aliases[0])
2040 2055
2041 2056 # aliases
2042 2057 if not ui.quiet and len(aliases) > 1:
2043 2058 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2044 2059
2045 2060 # description
2046 2061 doc = gettext(entry[0].__doc__)
2047 2062 if not doc:
2048 2063 doc = _("(no help text available)")
2049 2064 if hasattr(entry[0], 'definition'): # aliased command
2050 2065 if entry[0].definition.startswith('!'): # shell alias
2051 2066 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2052 2067 else:
2053 2068 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2054 2069 if ui.quiet:
2055 2070 doc = doc.splitlines()[0]
2056 2071 keep = ui.verbose and ['verbose'] or []
2057 2072 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2058 2073 ui.write("\n%s\n" % formatted)
2059 2074 if pruned:
2060 2075 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2061 2076
2062 2077 if not ui.quiet:
2063 2078 # options
2064 2079 if entry[1]:
2065 2080 option_lists.append((_("options:\n"), entry[1]))
2066 2081
2067 2082 addglobalopts(False)
2068 2083
2069 2084 def helplist(header, select=None):
2070 2085 h = {}
2071 2086 cmds = {}
2072 2087 for c, e in table.iteritems():
2073 2088 f = c.split("|", 1)[0]
2074 2089 if select and not select(f):
2075 2090 continue
2076 2091 if (not select and name != 'shortlist' and
2077 2092 e[0].__module__ != __name__):
2078 2093 continue
2079 2094 if name == "shortlist" and not f.startswith("^"):
2080 2095 continue
2081 2096 f = f.lstrip("^")
2082 2097 if not ui.debugflag and f.startswith("debug"):
2083 2098 continue
2084 2099 doc = e[0].__doc__
2085 2100 if doc and 'DEPRECATED' in doc and not ui.verbose:
2086 2101 continue
2087 2102 doc = gettext(doc)
2088 2103 if not doc:
2089 2104 doc = _("(no help text available)")
2090 2105 h[f] = doc.splitlines()[0].rstrip()
2091 2106 cmds[f] = c.lstrip("^")
2092 2107
2093 2108 if not h:
2094 2109 ui.status(_('no commands defined\n'))
2095 2110 return
2096 2111
2097 2112 ui.status(header)
2098 2113 fns = sorted(h)
2099 2114 m = max(map(len, fns))
2100 2115 for f in fns:
2101 2116 if ui.verbose:
2102 2117 commands = cmds[f].replace("|",", ")
2103 2118 ui.write(" %s:\n %s\n"%(commands, h[f]))
2104 2119 else:
2105 2120 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2106 2121 initindent=' %-*s ' % (m, f),
2107 2122 hangindent=' ' * (m + 4))))
2108 2123
2109 2124 if not ui.quiet:
2110 2125 addglobalopts(True)
2111 2126
2112 2127 def helptopic(name):
2113 2128 for names, header, doc in help.helptable:
2114 2129 if name in names:
2115 2130 break
2116 2131 else:
2117 2132 raise error.UnknownCommand(name)
2118 2133
2119 2134 # description
2120 2135 if not doc:
2121 2136 doc = _("(no help text available)")
2122 2137 if hasattr(doc, '__call__'):
2123 2138 doc = doc()
2124 2139
2125 2140 ui.write("%s\n\n" % header)
2126 2141 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2127 2142
2128 2143 def helpext(name):
2129 2144 try:
2130 2145 mod = extensions.find(name)
2131 2146 doc = gettext(mod.__doc__) or _('no help text available')
2132 2147 except KeyError:
2133 2148 mod = None
2134 2149 doc = extensions.disabledext(name)
2135 2150 if not doc:
2136 2151 raise error.UnknownCommand(name)
2137 2152
2138 2153 if '\n' not in doc:
2139 2154 head, tail = doc, ""
2140 2155 else:
2141 2156 head, tail = doc.split('\n', 1)
2142 2157 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2143 2158 if tail:
2144 2159 ui.write(minirst.format(tail, textwidth))
2145 2160 ui.status('\n\n')
2146 2161
2147 2162 if mod:
2148 2163 try:
2149 2164 ct = mod.cmdtable
2150 2165 except AttributeError:
2151 2166 ct = {}
2152 2167 modcmds = set([c.split('|', 1)[0] for c in ct])
2153 2168 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2154 2169 else:
2155 2170 ui.write(_('use "hg help extensions" for information on enabling '
2156 2171 'extensions\n'))
2157 2172
2158 2173 def helpextcmd(name):
2159 2174 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2160 2175 doc = gettext(mod.__doc__).splitlines()[0]
2161 2176
2162 2177 msg = help.listexts(_("'%s' is provided by the following "
2163 2178 "extension:") % cmd, {ext: doc}, len(ext),
2164 2179 indent=4)
2165 2180 ui.write(minirst.format(msg, textwidth))
2166 2181 ui.write('\n\n')
2167 2182 ui.write(_('use "hg help extensions" for information on enabling '
2168 2183 'extensions\n'))
2169 2184
2170 2185 help.addtopichook('revsets', revset.makedoc)
2171 2186 help.addtopichook('templates', templatekw.makedoc)
2172 2187 help.addtopichook('templates', templatefilters.makedoc)
2173 2188
2174 2189 if name and name != 'shortlist':
2175 2190 i = None
2176 2191 if unknowncmd:
2177 2192 queries = (helpextcmd,)
2178 2193 else:
2179 2194 queries = (helptopic, helpcmd, helpext, helpextcmd)
2180 2195 for f in queries:
2181 2196 try:
2182 2197 f(name)
2183 2198 i = None
2184 2199 break
2185 2200 except error.UnknownCommand, inst:
2186 2201 i = inst
2187 2202 if i:
2188 2203 raise i
2189 2204
2190 2205 else:
2191 2206 # program name
2192 2207 if ui.verbose or with_version:
2193 2208 version_(ui)
2194 2209 else:
2195 2210 ui.status(_("Mercurial Distributed SCM\n"))
2196 2211 ui.status('\n')
2197 2212
2198 2213 # list of commands
2199 2214 if name == "shortlist":
2200 2215 header = _('basic commands:\n\n')
2201 2216 else:
2202 2217 header = _('list of commands:\n\n')
2203 2218
2204 2219 helplist(header)
2205 2220 if name != 'shortlist':
2206 2221 exts, maxlength = extensions.enabled()
2207 2222 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2208 2223 if text:
2209 2224 ui.write("\n%s\n" % minirst.format(text, textwidth))
2210 2225
2211 2226 # list all option lists
2212 2227 opt_output = []
2213 2228 multioccur = False
2214 2229 for title, options in option_lists:
2215 2230 opt_output.append(("\n%s" % title, None))
2216 2231 for option in options:
2217 2232 if len(option) == 5:
2218 2233 shortopt, longopt, default, desc, optlabel = option
2219 2234 else:
2220 2235 shortopt, longopt, default, desc = option
2221 2236 optlabel = _("VALUE") # default label
2222 2237
2223 2238 if _("DEPRECATED") in desc and not ui.verbose:
2224 2239 continue
2225 2240 if isinstance(default, list):
2226 2241 numqualifier = " %s [+]" % optlabel
2227 2242 multioccur = True
2228 2243 elif (default is not None) and not isinstance(default, bool):
2229 2244 numqualifier = " %s" % optlabel
2230 2245 else:
2231 2246 numqualifier = ""
2232 2247 opt_output.append(("%2s%s" %
2233 2248 (shortopt and "-%s" % shortopt,
2234 2249 longopt and " --%s%s" %
2235 2250 (longopt, numqualifier)),
2236 2251 "%s%s" % (desc,
2237 2252 default
2238 2253 and _(" (default: %s)") % default
2239 2254 or "")))
2240 2255 if multioccur:
2241 2256 msg = _("\n[+] marked option can be specified multiple times")
2242 2257 if ui.verbose and name != 'shortlist':
2243 2258 opt_output.append((msg, None))
2244 2259 else:
2245 2260 opt_output.insert(-1, (msg, None))
2246 2261
2247 2262 if not name:
2248 2263 ui.write(_("\nadditional help topics:\n\n"))
2249 2264 topics = []
2250 2265 for names, header, doc in help.helptable:
2251 2266 topics.append((sorted(names, key=len, reverse=True)[0], header))
2252 2267 topics_len = max([len(s[0]) for s in topics])
2253 2268 for t, desc in topics:
2254 2269 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2255 2270
2256 2271 if opt_output:
2257 2272 colwidth = encoding.colwidth
2258 2273 # normalize: (opt or message, desc or None, width of opt)
2259 2274 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2260 2275 for opt, desc in opt_output]
2261 2276 hanging = max([e[2] for e in entries])
2262 2277 for opt, desc, width in entries:
2263 2278 if desc:
2264 2279 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2265 2280 hangindent = ' ' * (hanging + 3)
2266 2281 ui.write('%s\n' % (util.wrap(desc, textwidth,
2267 2282 initindent=initindent,
2268 2283 hangindent=hangindent)))
2269 2284 else:
2270 2285 ui.write("%s\n" % opt)
2271 2286
2272 2287 def identify(ui, repo, source=None, rev=None,
2273 2288 num=None, id=None, branch=None, tags=None, bookmarks=None):
2274 2289 """identify the working copy or specified revision
2275 2290
2276 2291 With no revision, print a summary of the current state of the
2277 2292 repository.
2278 2293
2279 2294 Specifying a path to a repository root or Mercurial bundle will
2280 2295 cause lookup to operate on that repository/bundle.
2281 2296
2282 2297 This summary identifies the repository state using one or two
2283 2298 parent hash identifiers, followed by a "+" if there are
2284 2299 uncommitted changes in the working directory, a list of tags for
2285 2300 this revision and a branch name for non-default branches.
2286 2301
2287 2302 Returns 0 if successful.
2288 2303 """
2289 2304
2290 2305 if not repo and not source:
2291 2306 raise util.Abort(_("there is no Mercurial repository here "
2292 2307 "(.hg not found)"))
2293 2308
2294 2309 hexfunc = ui.debugflag and hex or short
2295 2310 default = not (num or id or branch or tags or bookmarks)
2296 2311 output = []
2297 2312
2298 2313 revs = []
2299 2314 bms = []
2300 2315 if source:
2301 2316 source, branches = hg.parseurl(ui.expandpath(source))
2302 2317 repo = hg.repository(ui, source)
2303 2318 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2304 2319
2305 2320 if not repo.local():
2306 2321 if not rev and revs:
2307 2322 rev = revs[0]
2308 2323 if not rev:
2309 2324 rev = "tip"
2310 2325 if num or branch or tags:
2311 2326 raise util.Abort(
2312 2327 _("can't query remote revision number, branch, or tags"))
2313 2328
2314 2329 remoterev = repo.lookup(rev)
2315 2330 if default or id:
2316 2331 output = [hexfunc(remoterev)]
2317 2332
2318 2333 if 'bookmarks' in repo.listkeys('namespaces'):
2319 2334 hexremoterev = hex(remoterev)
2320 2335 bms = [bm for bm, bmrev in repo.listkeys('bookmarks').iteritems()
2321 2336 if bmrev == hexremoterev]
2322 2337
2323 2338 elif not rev:
2324 2339 ctx = repo[None]
2325 2340 parents = ctx.parents()
2326 2341 changed = False
2327 2342 if default or id or num:
2328 2343 changed = util.any(repo.status())
2329 2344 if default or id:
2330 2345 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2331 2346 (changed) and "+" or "")]
2332 2347 if num:
2333 2348 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2334 2349 (changed) and "+" or ""))
2335 2350 else:
2336 2351 ctx = cmdutil.revsingle(repo, rev)
2337 2352 if default or id:
2338 2353 output = [hexfunc(ctx.node())]
2339 2354 if num:
2340 2355 output.append(str(ctx.rev()))
2341 2356
2342 2357 if repo.local():
2343 2358 bms = ctx.bookmarks()
2344 2359
2345 2360 if repo.local() and default and not ui.quiet:
2346 2361 b = ctx.branch()
2347 2362 if b != 'default':
2348 2363 output.append("(%s)" % b)
2349 2364
2350 2365 # multiple tags for a single parent separated by '/'
2351 2366 t = "/".join(ctx.tags())
2352 2367 if t:
2353 2368 output.append(t)
2354 2369
2355 2370 if default and not ui.quiet:
2356 2371 # multiple bookmarks for a single parent separated by '/'
2357 2372 bm = '/'.join(bms)
2358 2373 if bm:
2359 2374 output.append(bm)
2360 2375
2361 2376 if branch:
2362 2377 output.append(ctx.branch())
2363 2378
2364 2379 if tags:
2365 2380 output.extend(ctx.tags())
2366 2381
2367 2382 if bookmarks:
2368 2383 output.extend(bms)
2369 2384
2370 2385 ui.write("%s\n" % ' '.join(output))
2371 2386
2372 2387 def import_(ui, repo, patch1, *patches, **opts):
2373 2388 """import an ordered set of patches
2374 2389
2375 2390 Import a list of patches and commit them individually (unless
2376 2391 --no-commit is specified).
2377 2392
2378 2393 If there are outstanding changes in the working directory, import
2379 2394 will abort unless given the -f/--force flag.
2380 2395
2381 2396 You can import a patch straight from a mail message. Even patches
2382 2397 as attachments work (to use the body part, it must have type
2383 2398 text/plain or text/x-patch). From and Subject headers of email
2384 2399 message are used as default committer and commit message. All
2385 2400 text/plain body parts before first diff are added to commit
2386 2401 message.
2387 2402
2388 2403 If the imported patch was generated by :hg:`export`, user and
2389 2404 description from patch override values from message headers and
2390 2405 body. Values given on command line with -m/--message and -u/--user
2391 2406 override these.
2392 2407
2393 2408 If --exact is specified, import will set the working directory to
2394 2409 the parent of each patch before applying it, and will abort if the
2395 2410 resulting changeset has a different ID than the one recorded in
2396 2411 the patch. This may happen due to character set problems or other
2397 2412 deficiencies in the text patch format.
2398 2413
2399 2414 With -s/--similarity, hg will attempt to discover renames and
2400 2415 copies in the patch in the same way as 'addremove'.
2401 2416
2402 2417 To read a patch from standard input, use "-" as the patch name. If
2403 2418 a URL is specified, the patch will be downloaded from it.
2404 2419 See :hg:`help dates` for a list of formats valid for -d/--date.
2405 2420
2406 2421 Returns 0 on success.
2407 2422 """
2408 2423 patches = (patch1,) + patches
2409 2424
2410 2425 date = opts.get('date')
2411 2426 if date:
2412 2427 opts['date'] = util.parsedate(date)
2413 2428
2414 2429 try:
2415 2430 sim = float(opts.get('similarity') or 0)
2416 2431 except ValueError:
2417 2432 raise util.Abort(_('similarity must be a number'))
2418 2433 if sim < 0 or sim > 100:
2419 2434 raise util.Abort(_('similarity must be between 0 and 100'))
2420 2435
2421 2436 if opts.get('exact') or not opts.get('force'):
2422 2437 cmdutil.bail_if_changed(repo)
2423 2438
2424 2439 d = opts["base"]
2425 2440 strip = opts["strip"]
2426 2441 wlock = lock = None
2427 2442 msgs = []
2428 2443
2429 2444 def tryone(ui, hunk):
2430 2445 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2431 2446 patch.extract(ui, hunk)
2432 2447
2433 2448 if not tmpname:
2434 2449 return None
2435 2450 commitid = _('to working directory')
2436 2451
2437 2452 try:
2438 2453 cmdline_message = cmdutil.logmessage(opts)
2439 2454 if cmdline_message:
2440 2455 # pickup the cmdline msg
2441 2456 message = cmdline_message
2442 2457 elif message:
2443 2458 # pickup the patch msg
2444 2459 message = message.strip()
2445 2460 else:
2446 2461 # launch the editor
2447 2462 message = None
2448 2463 ui.debug('message:\n%s\n' % message)
2449 2464
2450 2465 wp = repo.parents()
2451 2466 if opts.get('exact'):
2452 2467 if not nodeid or not p1:
2453 2468 raise util.Abort(_('not a Mercurial patch'))
2454 2469 p1 = repo.lookup(p1)
2455 2470 p2 = repo.lookup(p2 or hex(nullid))
2456 2471
2457 2472 if p1 != wp[0].node():
2458 2473 hg.clean(repo, p1)
2459 2474 repo.dirstate.setparents(p1, p2)
2460 2475 elif p2:
2461 2476 try:
2462 2477 p1 = repo.lookup(p1)
2463 2478 p2 = repo.lookup(p2)
2464 2479 if p1 == wp[0].node():
2465 2480 repo.dirstate.setparents(p1, p2)
2466 2481 except error.RepoError:
2467 2482 pass
2468 2483 if opts.get('exact') or opts.get('import_branch'):
2469 2484 repo.dirstate.setbranch(branch or 'default')
2470 2485
2471 2486 files = {}
2472 2487 try:
2473 2488 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2474 2489 files=files, eolmode=None)
2475 2490 finally:
2476 2491 files = cmdutil.updatedir(ui, repo, files,
2477 2492 similarity=sim / 100.0)
2478 2493 if opts.get('no_commit'):
2479 2494 if message:
2480 2495 msgs.append(message)
2481 2496 else:
2482 2497 if opts.get('exact'):
2483 2498 m = None
2484 2499 else:
2485 2500 m = cmdutil.matchfiles(repo, files or [])
2486 2501 n = repo.commit(message, opts.get('user') or user,
2487 2502 opts.get('date') or date, match=m,
2488 2503 editor=cmdutil.commiteditor)
2489 2504 if opts.get('exact'):
2490 2505 if hex(n) != nodeid:
2491 2506 repo.rollback()
2492 2507 raise util.Abort(_('patch is damaged'
2493 2508 ' or loses information'))
2494 2509 # Force a dirstate write so that the next transaction
2495 2510 # backups an up-do-date file.
2496 2511 repo.dirstate.write()
2497 2512 if n:
2498 2513 commitid = short(n)
2499 2514
2500 2515 return commitid
2501 2516 finally:
2502 2517 os.unlink(tmpname)
2503 2518
2504 2519 try:
2505 2520 wlock = repo.wlock()
2506 2521 lock = repo.lock()
2507 2522 lastcommit = None
2508 2523 for p in patches:
2509 2524 pf = os.path.join(d, p)
2510 2525
2511 2526 if pf == '-':
2512 2527 ui.status(_("applying patch from stdin\n"))
2513 2528 pf = sys.stdin
2514 2529 else:
2515 2530 ui.status(_("applying %s\n") % p)
2516 2531 pf = url.open(ui, pf)
2517 2532
2518 2533 haspatch = False
2519 2534 for hunk in patch.split(pf):
2520 2535 commitid = tryone(ui, hunk)
2521 2536 if commitid:
2522 2537 haspatch = True
2523 2538 if lastcommit:
2524 2539 ui.status(_('applied %s\n') % lastcommit)
2525 2540 lastcommit = commitid
2526 2541
2527 2542 if not haspatch:
2528 2543 raise util.Abort(_('no diffs found'))
2529 2544
2530 2545 if msgs:
2531 2546 repo.opener('last-message.txt', 'wb').write('\n* * *\n'.join(msgs))
2532 2547 finally:
2533 2548 release(lock, wlock)
2534 2549
2535 2550 def incoming(ui, repo, source="default", **opts):
2536 2551 """show new changesets found in source
2537 2552
2538 2553 Show new changesets found in the specified path/URL or the default
2539 2554 pull location. These are the changesets that would have been pulled
2540 2555 if a pull at the time you issued this command.
2541 2556
2542 2557 For remote repository, using --bundle avoids downloading the
2543 2558 changesets twice if the incoming is followed by a pull.
2544 2559
2545 2560 See pull for valid source format details.
2546 2561
2547 2562 Returns 0 if there are incoming changes, 1 otherwise.
2548 2563 """
2549 2564 if opts.get('bundle') and opts.get('subrepos'):
2550 2565 raise util.Abort(_('cannot combine --bundle and --subrepos'))
2551 2566
2552 2567 if opts.get('bookmarks'):
2553 2568 source, branches = hg.parseurl(ui.expandpath(source),
2554 2569 opts.get('branch'))
2555 2570 other = hg.repository(hg.remoteui(repo, opts), source)
2556 2571 if 'bookmarks' not in other.listkeys('namespaces'):
2557 2572 ui.warn(_("remote doesn't support bookmarks\n"))
2558 2573 return 0
2559 2574 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2560 2575 return bookmarks.diff(ui, repo, other)
2561 2576
2562 2577 ret = hg.incoming(ui, repo, source, opts)
2563 2578 return ret
2564 2579
2565 2580 def init(ui, dest=".", **opts):
2566 2581 """create a new repository in the given directory
2567 2582
2568 2583 Initialize a new repository in the given directory. If the given
2569 2584 directory does not exist, it will be created.
2570 2585
2571 2586 If no directory is given, the current directory is used.
2572 2587
2573 2588 It is possible to specify an ``ssh://`` URL as the destination.
2574 2589 See :hg:`help urls` for more information.
2575 2590
2576 2591 Returns 0 on success.
2577 2592 """
2578 2593 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
2579 2594
2580 2595 def locate(ui, repo, *pats, **opts):
2581 2596 """locate files matching specific patterns
2582 2597
2583 2598 Print files under Mercurial control in the working directory whose
2584 2599 names match the given patterns.
2585 2600
2586 2601 By default, this command searches all directories in the working
2587 2602 directory. To search just the current directory and its
2588 2603 subdirectories, use "--include .".
2589 2604
2590 2605 If no patterns are given to match, this command prints the names
2591 2606 of all files under Mercurial control in the working directory.
2592 2607
2593 2608 If you want to feed the output of this command into the "xargs"
2594 2609 command, use the -0 option to both this command and "xargs". This
2595 2610 will avoid the problem of "xargs" treating single filenames that
2596 2611 contain whitespace as multiple filenames.
2597 2612
2598 2613 Returns 0 if a match is found, 1 otherwise.
2599 2614 """
2600 2615 end = opts.get('print0') and '\0' or '\n'
2601 2616 rev = cmdutil.revsingle(repo, opts.get('rev'), None).node()
2602 2617
2603 2618 ret = 1
2604 2619 m = cmdutil.match(repo, pats, opts, default='relglob')
2605 2620 m.bad = lambda x, y: False
2606 2621 for abs in repo[rev].walk(m):
2607 2622 if not rev and abs not in repo.dirstate:
2608 2623 continue
2609 2624 if opts.get('fullpath'):
2610 2625 ui.write(repo.wjoin(abs), end)
2611 2626 else:
2612 2627 ui.write(((pats and m.rel(abs)) or abs), end)
2613 2628 ret = 0
2614 2629
2615 2630 return ret
2616 2631
2617 2632 def log(ui, repo, *pats, **opts):
2618 2633 """show revision history of entire repository or files
2619 2634
2620 2635 Print the revision history of the specified files or the entire
2621 2636 project.
2622 2637
2623 2638 File history is shown without following rename or copy history of
2624 2639 files. Use -f/--follow with a filename to follow history across
2625 2640 renames and copies. --follow without a filename will only show
2626 2641 ancestors or descendants of the starting revision. --follow-first
2627 2642 only follows the first parent of merge revisions.
2628 2643
2629 2644 If no revision range is specified, the default is ``tip:0`` unless
2630 2645 --follow is set, in which case the working directory parent is
2631 2646 used as the starting revision. You can specify a revision set for
2632 2647 log, see :hg:`help revsets` for more information.
2633 2648
2634 2649 See :hg:`help dates` for a list of formats valid for -d/--date.
2635 2650
2636 2651 By default this command prints revision number and changeset id,
2637 2652 tags, non-trivial parents, user, date and time, and a summary for
2638 2653 each commit. When the -v/--verbose switch is used, the list of
2639 2654 changed files and full commit message are shown.
2640 2655
2641 2656 .. note::
2642 2657 log -p/--patch may generate unexpected diff output for merge
2643 2658 changesets, as it will only compare the merge changeset against
2644 2659 its first parent. Also, only files different from BOTH parents
2645 2660 will appear in files:.
2646 2661
2647 2662 Returns 0 on success.
2648 2663 """
2649 2664
2650 2665 matchfn = cmdutil.match(repo, pats, opts)
2651 2666 limit = cmdutil.loglimit(opts)
2652 2667 count = 0
2653 2668
2654 2669 endrev = None
2655 2670 if opts.get('copies') and opts.get('rev'):
2656 2671 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2657 2672
2658 2673 df = False
2659 2674 if opts["date"]:
2660 2675 df = util.matchdate(opts["date"])
2661 2676
2662 2677 branches = opts.get('branch', []) + opts.get('only_branch', [])
2663 2678 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2664 2679
2665 2680 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2666 2681 def prep(ctx, fns):
2667 2682 rev = ctx.rev()
2668 2683 parents = [p for p in repo.changelog.parentrevs(rev)
2669 2684 if p != nullrev]
2670 2685 if opts.get('no_merges') and len(parents) == 2:
2671 2686 return
2672 2687 if opts.get('only_merges') and len(parents) != 2:
2673 2688 return
2674 2689 if opts.get('branch') and ctx.branch() not in opts['branch']:
2675 2690 return
2676 2691 if df and not df(ctx.date()[0]):
2677 2692 return
2678 2693 if opts['user'] and not [k for k in opts['user']
2679 2694 if k.lower() in ctx.user().lower()]:
2680 2695 return
2681 2696 if opts.get('keyword'):
2682 2697 for k in [kw.lower() for kw in opts['keyword']]:
2683 2698 if (k in ctx.user().lower() or
2684 2699 k in ctx.description().lower() or
2685 2700 k in " ".join(ctx.files()).lower()):
2686 2701 break
2687 2702 else:
2688 2703 return
2689 2704
2690 2705 copies = None
2691 2706 if opts.get('copies') and rev:
2692 2707 copies = []
2693 2708 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2694 2709 for fn in ctx.files():
2695 2710 rename = getrenamed(fn, rev)
2696 2711 if rename:
2697 2712 copies.append((fn, rename[0]))
2698 2713
2699 2714 revmatchfn = None
2700 2715 if opts.get('patch') or opts.get('stat'):
2701 2716 if opts.get('follow') or opts.get('follow_first'):
2702 2717 # note: this might be wrong when following through merges
2703 2718 revmatchfn = cmdutil.match(repo, fns, default='path')
2704 2719 else:
2705 2720 revmatchfn = matchfn
2706 2721
2707 2722 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2708 2723
2709 2724 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2710 2725 if count == limit:
2711 2726 break
2712 2727 if displayer.flush(ctx.rev()):
2713 2728 count += 1
2714 2729 displayer.close()
2715 2730
2716 2731 def manifest(ui, repo, node=None, rev=None):
2717 2732 """output the current or given revision of the project manifest
2718 2733
2719 2734 Print a list of version controlled files for the given revision.
2720 2735 If no revision is given, the first parent of the working directory
2721 2736 is used, or the null revision if no revision is checked out.
2722 2737
2723 2738 With -v, print file permissions, symlink and executable bits.
2724 2739 With --debug, print file revision hashes.
2725 2740
2726 2741 Returns 0 on success.
2727 2742 """
2728 2743
2729 2744 if rev and node:
2730 2745 raise util.Abort(_("please specify just one revision"))
2731 2746
2732 2747 if not node:
2733 2748 node = rev
2734 2749
2735 2750 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2736 2751 ctx = cmdutil.revsingle(repo, node)
2737 2752 for f in ctx:
2738 2753 if ui.debugflag:
2739 2754 ui.write("%40s " % hex(ctx.manifest()[f]))
2740 2755 if ui.verbose:
2741 2756 ui.write(decor[ctx.flags(f)])
2742 2757 ui.write("%s\n" % f)
2743 2758
2744 2759 def merge(ui, repo, node=None, **opts):
2745 2760 """merge working directory with another revision
2746 2761
2747 2762 The current working directory is updated with all changes made in
2748 2763 the requested revision since the last common predecessor revision.
2749 2764
2750 2765 Files that changed between either parent are marked as changed for
2751 2766 the next commit and a commit must be performed before any further
2752 2767 updates to the repository are allowed. The next commit will have
2753 2768 two parents.
2754 2769
2755 2770 ``--tool`` can be used to specify the merge tool used for file
2756 2771 merges. It overrides the HGMERGE environment variable and your
2757 2772 configuration files.
2758 2773
2759 2774 If no revision is specified, the working directory's parent is a
2760 2775 head revision, and the current branch contains exactly one other
2761 2776 head, the other head is merged with by default. Otherwise, an
2762 2777 explicit revision with which to merge with must be provided.
2763 2778
2764 2779 :hg:`resolve` must be used to resolve unresolved files.
2765 2780
2766 2781 To undo an uncommitted merge, use :hg:`update --clean .` which
2767 2782 will check out a clean copy of the original merge parent, losing
2768 2783 all changes.
2769 2784
2770 2785 Returns 0 on success, 1 if there are unresolved files.
2771 2786 """
2772 2787
2773 2788 if opts.get('rev') and node:
2774 2789 raise util.Abort(_("please specify just one revision"))
2775 2790 if not node:
2776 2791 node = opts.get('rev')
2777 2792
2778 2793 if not node:
2779 2794 branch = repo[None].branch()
2780 2795 bheads = repo.branchheads(branch)
2781 2796 if len(bheads) > 2:
2782 2797 raise util.Abort(_(
2783 2798 'branch \'%s\' has %d heads - '
2784 2799 'please merge with an explicit rev\n'
2785 2800 '(run \'hg heads .\' to see heads)')
2786 2801 % (branch, len(bheads)))
2787 2802
2788 2803 parent = repo.dirstate.parents()[0]
2789 2804 if len(bheads) == 1:
2790 2805 if len(repo.heads()) > 1:
2791 2806 raise util.Abort(_(
2792 2807 'branch \'%s\' has one head - '
2793 2808 'please merge with an explicit rev\n'
2794 2809 '(run \'hg heads\' to see all heads)')
2795 2810 % branch)
2796 2811 msg = _('there is nothing to merge')
2797 2812 if parent != repo.lookup(repo[None].branch()):
2798 2813 msg = _('%s - use "hg update" instead') % msg
2799 2814 raise util.Abort(msg)
2800 2815
2801 2816 if parent not in bheads:
2802 2817 raise util.Abort(_('working dir not at a head rev - '
2803 2818 'use "hg update" or merge with an explicit rev'))
2804 2819 node = parent == bheads[0] and bheads[-1] or bheads[0]
2805 2820 else:
2806 2821 node = cmdutil.revsingle(repo, node).node()
2807 2822
2808 2823 if opts.get('preview'):
2809 2824 # find nodes that are ancestors of p2 but not of p1
2810 2825 p1 = repo.lookup('.')
2811 2826 p2 = repo.lookup(node)
2812 2827 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2813 2828
2814 2829 displayer = cmdutil.show_changeset(ui, repo, opts)
2815 2830 for node in nodes:
2816 2831 displayer.show(repo[node])
2817 2832 displayer.close()
2818 2833 return 0
2819 2834
2820 2835 try:
2821 2836 # ui.forcemerge is an internal variable, do not document
2822 2837 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2823 2838 return hg.merge(repo, node, force=opts.get('force'))
2824 2839 finally:
2825 2840 ui.setconfig('ui', 'forcemerge', '')
2826 2841
2827 2842 def outgoing(ui, repo, dest=None, **opts):
2828 2843 """show changesets not found in the destination
2829 2844
2830 2845 Show changesets not found in the specified destination repository
2831 2846 or the default push location. These are the changesets that would
2832 2847 be pushed if a push was requested.
2833 2848
2834 2849 See pull for details of valid destination formats.
2835 2850
2836 2851 Returns 0 if there are outgoing changes, 1 otherwise.
2837 2852 """
2838 2853
2839 2854 if opts.get('bookmarks'):
2840 2855 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2841 2856 dest, branches = hg.parseurl(dest, opts.get('branch'))
2842 2857 other = hg.repository(hg.remoteui(repo, opts), dest)
2843 2858 if 'bookmarks' not in other.listkeys('namespaces'):
2844 2859 ui.warn(_("remote doesn't support bookmarks\n"))
2845 2860 return 0
2846 2861 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2847 2862 return bookmarks.diff(ui, other, repo)
2848 2863
2849 2864 ret = hg.outgoing(ui, repo, dest, opts)
2850 2865 return ret
2851 2866
2852 2867 def parents(ui, repo, file_=None, **opts):
2853 2868 """show the parents of the working directory or revision
2854 2869
2855 2870 Print the working directory's parent revisions. If a revision is
2856 2871 given via -r/--rev, the parent of that revision will be printed.
2857 2872 If a file argument is given, the revision in which the file was
2858 2873 last changed (before the working directory revision or the
2859 2874 argument to --rev if given) is printed.
2860 2875
2861 2876 Returns 0 on success.
2862 2877 """
2863 2878
2864 2879 ctx = cmdutil.revsingle(repo, opts.get('rev'), None)
2865 2880
2866 2881 if file_:
2867 2882 m = cmdutil.match(repo, (file_,), opts)
2868 2883 if m.anypats() or len(m.files()) != 1:
2869 2884 raise util.Abort(_('can only specify an explicit filename'))
2870 2885 file_ = m.files()[0]
2871 2886 filenodes = []
2872 2887 for cp in ctx.parents():
2873 2888 if not cp:
2874 2889 continue
2875 2890 try:
2876 2891 filenodes.append(cp.filenode(file_))
2877 2892 except error.LookupError:
2878 2893 pass
2879 2894 if not filenodes:
2880 2895 raise util.Abort(_("'%s' not found in manifest!") % file_)
2881 2896 fl = repo.file(file_)
2882 2897 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2883 2898 else:
2884 2899 p = [cp.node() for cp in ctx.parents()]
2885 2900
2886 2901 displayer = cmdutil.show_changeset(ui, repo, opts)
2887 2902 for n in p:
2888 2903 if n != nullid:
2889 2904 displayer.show(repo[n])
2890 2905 displayer.close()
2891 2906
2892 2907 def paths(ui, repo, search=None):
2893 2908 """show aliases for remote repositories
2894 2909
2895 2910 Show definition of symbolic path name NAME. If no name is given,
2896 2911 show definition of all available names.
2897 2912
2898 2913 Path names are defined in the [paths] section of your
2899 2914 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
2900 2915 repository, ``.hg/hgrc`` is used, too.
2901 2916
2902 2917 The path names ``default`` and ``default-push`` have a special
2903 2918 meaning. When performing a push or pull operation, they are used
2904 2919 as fallbacks if no location is specified on the command-line.
2905 2920 When ``default-push`` is set, it will be used for push and
2906 2921 ``default`` will be used for pull; otherwise ``default`` is used
2907 2922 as the fallback for both. When cloning a repository, the clone
2908 2923 source is written as ``default`` in ``.hg/hgrc``. Note that
2909 2924 ``default`` and ``default-push`` apply to all inbound (e.g.
2910 2925 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2911 2926 :hg:`bundle`) operations.
2912 2927
2913 2928 See :hg:`help urls` for more information.
2914 2929
2915 2930 Returns 0 on success.
2916 2931 """
2917 2932 if search:
2918 2933 for name, path in ui.configitems("paths"):
2919 2934 if name == search:
2920 2935 ui.write("%s\n" % url.hidepassword(path))
2921 2936 return
2922 2937 ui.warn(_("not found!\n"))
2923 2938 return 1
2924 2939 else:
2925 2940 for name, path in ui.configitems("paths"):
2926 2941 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2927 2942
2928 2943 def postincoming(ui, repo, modheads, optupdate, checkout):
2929 2944 if modheads == 0:
2930 2945 return
2931 2946 if optupdate:
2932 2947 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2933 2948 return hg.update(repo, checkout)
2934 2949 else:
2935 2950 ui.status(_("not updating, since new heads added\n"))
2936 2951 if modheads > 1:
2937 2952 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2938 2953 else:
2939 2954 ui.status(_("(run 'hg update' to get a working copy)\n"))
2940 2955
2941 2956 def pull(ui, repo, source="default", **opts):
2942 2957 """pull changes from the specified source
2943 2958
2944 2959 Pull changes from a remote repository to a local one.
2945 2960
2946 2961 This finds all changes from the repository at the specified path
2947 2962 or URL and adds them to a local repository (the current one unless
2948 2963 -R is specified). By default, this does not update the copy of the
2949 2964 project in the working directory.
2950 2965
2951 2966 Use :hg:`incoming` if you want to see what would have been added
2952 2967 by a pull at the time you issued this command. If you then decide
2953 2968 to add those changes to the repository, you should use :hg:`pull
2954 2969 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2955 2970
2956 2971 If SOURCE is omitted, the 'default' path will be used.
2957 2972 See :hg:`help urls` for more information.
2958 2973
2959 2974 Returns 0 on success, 1 if an update had unresolved files.
2960 2975 """
2961 2976 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2962 2977 other = hg.repository(hg.remoteui(repo, opts), source)
2963 2978 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2964 2979 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2965 2980
2966 2981 if opts.get('bookmark'):
2967 2982 if not revs:
2968 2983 revs = []
2969 2984 rb = other.listkeys('bookmarks')
2970 2985 for b in opts['bookmark']:
2971 2986 if b not in rb:
2972 2987 raise util.Abort(_('remote bookmark %s not found!') % b)
2973 2988 revs.append(rb[b])
2974 2989
2975 2990 if revs:
2976 2991 try:
2977 2992 revs = [other.lookup(rev) for rev in revs]
2978 2993 except error.CapabilityError:
2979 2994 err = _("other repository doesn't support revision lookup, "
2980 2995 "so a rev cannot be specified.")
2981 2996 raise util.Abort(err)
2982 2997
2983 2998 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2984 2999 bookmarks.updatefromremote(ui, repo, other)
2985 3000 if checkout:
2986 3001 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2987 3002 repo._subtoppath = source
2988 3003 try:
2989 3004 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
2990 3005
2991 3006 finally:
2992 3007 del repo._subtoppath
2993 3008
2994 3009 # update specified bookmarks
2995 3010 if opts.get('bookmark'):
2996 3011 for b in opts['bookmark']:
2997 3012 # explicit pull overrides local bookmark if any
2998 3013 ui.status(_("importing bookmark %s\n") % b)
2999 3014 repo._bookmarks[b] = repo[rb[b]].node()
3000 3015 bookmarks.write(repo)
3001 3016
3002 3017 return ret
3003 3018
3004 3019 def push(ui, repo, dest=None, **opts):
3005 3020 """push changes to the specified destination
3006 3021
3007 3022 Push changesets from the local repository to the specified
3008 3023 destination.
3009 3024
3010 3025 This operation is symmetrical to pull: it is identical to a pull
3011 3026 in the destination repository from the current one.
3012 3027
3013 3028 By default, push will not allow creation of new heads at the
3014 3029 destination, since multiple heads would make it unclear which head
3015 3030 to use. In this situation, it is recommended to pull and merge
3016 3031 before pushing.
3017 3032
3018 3033 Use --new-branch if you want to allow push to create a new named
3019 3034 branch that is not present at the destination. This allows you to
3020 3035 only create a new branch without forcing other changes.
3021 3036
3022 3037 Use -f/--force to override the default behavior and push all
3023 3038 changesets on all branches.
3024 3039
3025 3040 If -r/--rev is used, the specified revision and all its ancestors
3026 3041 will be pushed to the remote repository.
3027 3042
3028 3043 Please see :hg:`help urls` for important details about ``ssh://``
3029 3044 URLs. If DESTINATION is omitted, a default path will be used.
3030 3045
3031 3046 Returns 0 if push was successful, 1 if nothing to push.
3032 3047 """
3033 3048
3034 3049 if opts.get('bookmark'):
3035 3050 for b in opts['bookmark']:
3036 3051 # translate -B options to -r so changesets get pushed
3037 3052 if b in repo._bookmarks:
3038 3053 opts.setdefault('rev', []).append(b)
3039 3054 else:
3040 3055 # if we try to push a deleted bookmark, translate it to null
3041 3056 # this lets simultaneous -r, -b options continue working
3042 3057 opts.setdefault('rev', []).append("null")
3043 3058
3044 3059 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3045 3060 dest, branches = hg.parseurl(dest, opts.get('branch'))
3046 3061 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
3047 3062 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3048 3063 other = hg.repository(hg.remoteui(repo, opts), dest)
3049 3064 if revs:
3050 3065 revs = [repo.lookup(rev) for rev in revs]
3051 3066
3052 3067 repo._subtoppath = dest
3053 3068 try:
3054 3069 # push subrepos depth-first for coherent ordering
3055 3070 c = repo['']
3056 3071 subs = c.substate # only repos that are committed
3057 3072 for s in sorted(subs):
3058 3073 if not c.sub(s).push(opts.get('force')):
3059 3074 return False
3060 3075 finally:
3061 3076 del repo._subtoppath
3062 3077 result = repo.push(other, opts.get('force'), revs=revs,
3063 3078 newbranch=opts.get('new_branch'))
3064 3079
3065 3080 result = (result == 0)
3066 3081
3067 3082 if opts.get('bookmark'):
3068 3083 rb = other.listkeys('bookmarks')
3069 3084 for b in opts['bookmark']:
3070 3085 # explicit push overrides remote bookmark if any
3071 3086 if b in repo._bookmarks:
3072 3087 ui.status(_("exporting bookmark %s\n") % b)
3073 3088 new = repo[b].hex()
3074 3089 elif b in rb:
3075 3090 ui.status(_("deleting remote bookmark %s\n") % b)
3076 3091 new = '' # delete
3077 3092 else:
3078 3093 ui.warn(_('bookmark %s does not exist on the local '
3079 3094 'or remote repository!\n') % b)
3080 3095 return 2
3081 3096 old = rb.get(b, '')
3082 3097 r = other.pushkey('bookmarks', b, old, new)
3083 3098 if not r:
3084 3099 ui.warn(_('updating bookmark %s failed!\n') % b)
3085 3100 if not result:
3086 3101 result = 2
3087 3102
3088 3103 return result
3089 3104
3090 3105 def recover(ui, repo):
3091 3106 """roll back an interrupted transaction
3092 3107
3093 3108 Recover from an interrupted commit or pull.
3094 3109
3095 3110 This command tries to fix the repository status after an
3096 3111 interrupted operation. It should only be necessary when Mercurial
3097 3112 suggests it.
3098 3113
3099 3114 Returns 0 if successful, 1 if nothing to recover or verify fails.
3100 3115 """
3101 3116 if repo.recover():
3102 3117 return hg.verify(repo)
3103 3118 return 1
3104 3119
3105 3120 def remove(ui, repo, *pats, **opts):
3106 3121 """remove the specified files on the next commit
3107 3122
3108 3123 Schedule the indicated files for removal from the repository.
3109 3124
3110 3125 This only removes files from the current branch, not from the
3111 3126 entire project history. -A/--after can be used to remove only
3112 3127 files that have already been deleted, -f/--force can be used to
3113 3128 force deletion, and -Af can be used to remove files from the next
3114 3129 revision without deleting them from the working directory.
3115 3130
3116 3131 The following table details the behavior of remove for different
3117 3132 file states (columns) and option combinations (rows). The file
3118 3133 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3119 3134 reported by :hg:`status`). The actions are Warn, Remove (from
3120 3135 branch) and Delete (from disk)::
3121 3136
3122 3137 A C M !
3123 3138 none W RD W R
3124 3139 -f R RD RD R
3125 3140 -A W W W R
3126 3141 -Af R R R R
3127 3142
3128 3143 This command schedules the files to be removed at the next commit.
3129 3144 To undo a remove before that, see :hg:`revert`.
3130 3145
3131 3146 Returns 0 on success, 1 if any warnings encountered.
3132 3147 """
3133 3148
3134 3149 ret = 0
3135 3150 after, force = opts.get('after'), opts.get('force')
3136 3151 if not pats and not after:
3137 3152 raise util.Abort(_('no files specified'))
3138 3153
3139 3154 m = cmdutil.match(repo, pats, opts)
3140 3155 s = repo.status(match=m, clean=True)
3141 3156 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3142 3157
3143 3158 for f in m.files():
3144 3159 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3145 3160 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3146 3161 ret = 1
3147 3162
3148 3163 if force:
3149 3164 remove, forget = modified + deleted + clean, added
3150 3165 elif after:
3151 3166 remove, forget = deleted, []
3152 3167 for f in modified + added + clean:
3153 3168 ui.warn(_('not removing %s: file still exists (use -f'
3154 3169 ' to force removal)\n') % m.rel(f))
3155 3170 ret = 1
3156 3171 else:
3157 3172 remove, forget = deleted + clean, []
3158 3173 for f in modified:
3159 3174 ui.warn(_('not removing %s: file is modified (use -f'
3160 3175 ' to force removal)\n') % m.rel(f))
3161 3176 ret = 1
3162 3177 for f in added:
3163 3178 ui.warn(_('not removing %s: file has been marked for add (use -f'
3164 3179 ' to force removal)\n') % m.rel(f))
3165 3180 ret = 1
3166 3181
3167 3182 for f in sorted(remove + forget):
3168 3183 if ui.verbose or not m.exact(f):
3169 3184 ui.status(_('removing %s\n') % m.rel(f))
3170 3185
3171 3186 repo[None].forget(forget)
3172 3187 repo[None].remove(remove, unlink=not after)
3173 3188 return ret
3174 3189
3175 3190 def rename(ui, repo, *pats, **opts):
3176 3191 """rename files; equivalent of copy + remove
3177 3192
3178 3193 Mark dest as copies of sources; mark sources for deletion. If dest
3179 3194 is a directory, copies are put in that directory. If dest is a
3180 3195 file, there can only be one source.
3181 3196
3182 3197 By default, this command copies the contents of files as they
3183 3198 exist in the working directory. If invoked with -A/--after, the
3184 3199 operation is recorded, but no copying is performed.
3185 3200
3186 3201 This command takes effect at the next commit. To undo a rename
3187 3202 before that, see :hg:`revert`.
3188 3203
3189 3204 Returns 0 on success, 1 if errors are encountered.
3190 3205 """
3191 3206 wlock = repo.wlock(False)
3192 3207 try:
3193 3208 return cmdutil.copy(ui, repo, pats, opts, rename=True)
3194 3209 finally:
3195 3210 wlock.release()
3196 3211
3197 3212 def resolve(ui, repo, *pats, **opts):
3198 3213 """redo merges or set/view the merge status of files
3199 3214
3200 3215 Merges with unresolved conflicts are often the result of
3201 3216 non-interactive merging using the ``internal:merge`` configuration
3202 3217 setting, or a command-line merge tool like ``diff3``. The resolve
3203 3218 command is used to manage the files involved in a merge, after
3204 3219 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
3205 3220 working directory must have two parents).
3206 3221
3207 3222 The resolve command can be used in the following ways:
3208 3223
3209 3224 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
3210 3225 files, discarding any previous merge attempts. Re-merging is not
3211 3226 performed for files already marked as resolved. Use ``--all/-a``
3212 3227 to selects all unresolved files. ``--tool`` can be used to specify
3213 3228 the merge tool used for the given files. It overrides the HGMERGE
3214 3229 environment variable and your configuration files.
3215 3230
3216 3231 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
3217 3232 (e.g. after having manually fixed-up the files). The default is
3218 3233 to mark all unresolved files.
3219 3234
3220 3235 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
3221 3236 default is to mark all resolved files.
3222 3237
3223 3238 - :hg:`resolve -l`: list files which had or still have conflicts.
3224 3239 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3225 3240
3226 3241 Note that Mercurial will not let you commit files with unresolved
3227 3242 merge conflicts. You must use :hg:`resolve -m ...` before you can
3228 3243 commit after a conflicting merge.
3229 3244
3230 3245 Returns 0 on success, 1 if any files fail a resolve attempt.
3231 3246 """
3232 3247
3233 3248 all, mark, unmark, show, nostatus = \
3234 3249 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3235 3250
3236 3251 if (show and (mark or unmark)) or (mark and unmark):
3237 3252 raise util.Abort(_("too many options specified"))
3238 3253 if pats and all:
3239 3254 raise util.Abort(_("can't specify --all and patterns"))
3240 3255 if not (all or pats or show or mark or unmark):
3241 3256 raise util.Abort(_('no files or directories specified; '
3242 3257 'use --all to remerge all files'))
3243 3258
3244 3259 ms = mergemod.mergestate(repo)
3245 3260 m = cmdutil.match(repo, pats, opts)
3246 3261 ret = 0
3247 3262
3248 3263 for f in ms:
3249 3264 if m(f):
3250 3265 if show:
3251 3266 if nostatus:
3252 3267 ui.write("%s\n" % f)
3253 3268 else:
3254 3269 ui.write("%s %s\n" % (ms[f].upper(), f),
3255 3270 label='resolve.' +
3256 3271 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3257 3272 elif mark:
3258 3273 ms.mark(f, "r")
3259 3274 elif unmark:
3260 3275 ms.mark(f, "u")
3261 3276 else:
3262 3277 wctx = repo[None]
3263 3278 mctx = wctx.parents()[-1]
3264 3279
3265 3280 # backup pre-resolve (merge uses .orig for its own purposes)
3266 3281 a = repo.wjoin(f)
3267 3282 util.copyfile(a, a + ".resolve")
3268 3283
3269 3284 try:
3270 3285 # resolve file
3271 3286 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3272 3287 if ms.resolve(f, wctx, mctx):
3273 3288 ret = 1
3274 3289 finally:
3275 3290 ui.setconfig('ui', 'forcemerge', '')
3276 3291
3277 3292 # replace filemerge's .orig file with our resolve file
3278 3293 util.rename(a + ".resolve", a + ".orig")
3279 3294
3280 3295 ms.commit()
3281 3296 return ret
3282 3297
3283 3298 def revert(ui, repo, *pats, **opts):
3284 3299 """restore individual files or directories to an earlier state
3285 3300
3286 3301 .. note::
3287 3302 This command is most likely not what you are looking for.
3288 3303 Revert will partially overwrite content in the working
3289 3304 directory without changing the working directory parents. Use
3290 3305 :hg:`update -r rev` to check out earlier revisions, or
3291 3306 :hg:`update --clean .` to undo a merge which has added another
3292 3307 parent.
3293 3308
3294 3309 With no revision specified, revert the named files or directories
3295 3310 to the contents they had in the parent of the working directory.
3296 3311 This restores the contents of the affected files to an unmodified
3297 3312 state and unschedules adds, removes, copies, and renames. If the
3298 3313 working directory has two parents, you must explicitly specify a
3299 3314 revision.
3300 3315
3301 3316 Using the -r/--rev option, revert the given files or directories
3302 3317 to their contents as of a specific revision. This can be helpful
3303 3318 to "roll back" some or all of an earlier change. See :hg:`help
3304 3319 dates` for a list of formats valid for -d/--date.
3305 3320
3306 3321 Revert modifies the working directory. It does not commit any
3307 3322 changes, or change the parent of the working directory. If you
3308 3323 revert to a revision other than the parent of the working
3309 3324 directory, the reverted files will thus appear modified
3310 3325 afterwards.
3311 3326
3312 3327 If a file has been deleted, it is restored. If the executable mode
3313 3328 of a file was changed, it is reset.
3314 3329
3315 3330 If names are given, all files matching the names are reverted.
3316 3331 If no arguments are given, no files are reverted.
3317 3332
3318 3333 Modified files are saved with a .orig suffix before reverting.
3319 3334 To disable these backups, use --no-backup.
3320 3335
3321 3336 Returns 0 on success.
3322 3337 """
3323 3338
3324 3339 if opts.get("date"):
3325 3340 if opts.get("rev"):
3326 3341 raise util.Abort(_("you can't specify a revision and a date"))
3327 3342 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3328 3343
3329 3344 parent, p2 = repo.dirstate.parents()
3330 3345 if not opts.get('rev') and p2 != nullid:
3331 3346 raise util.Abort(_('uncommitted merge - '
3332 3347 'use "hg update", see "hg help revert"'))
3333 3348
3334 3349 if not pats and not opts.get('all'):
3335 3350 raise util.Abort(_('no files or directories specified; '
3336 3351 'use --all to revert the whole repo'))
3337 3352
3338 3353 ctx = cmdutil.revsingle(repo, opts.get('rev'))
3339 3354 node = ctx.node()
3340 3355 mf = ctx.manifest()
3341 3356 if node == parent:
3342 3357 pmf = mf
3343 3358 else:
3344 3359 pmf = None
3345 3360
3346 3361 # need all matching names in dirstate and manifest of target rev,
3347 3362 # so have to walk both. do not print errors if files exist in one
3348 3363 # but not other.
3349 3364
3350 3365 names = {}
3351 3366
3352 3367 wlock = repo.wlock()
3353 3368 try:
3354 3369 # walk dirstate.
3355 3370
3356 3371 m = cmdutil.match(repo, pats, opts)
3357 3372 m.bad = lambda x, y: False
3358 3373 for abs in repo.walk(m):
3359 3374 names[abs] = m.rel(abs), m.exact(abs)
3360 3375
3361 3376 # walk target manifest.
3362 3377
3363 3378 def badfn(path, msg):
3364 3379 if path in names:
3365 3380 return
3366 3381 path_ = path + '/'
3367 3382 for f in names:
3368 3383 if f.startswith(path_):
3369 3384 return
3370 3385 ui.warn("%s: %s\n" % (m.rel(path), msg))
3371 3386
3372 3387 m = cmdutil.match(repo, pats, opts)
3373 3388 m.bad = badfn
3374 3389 for abs in repo[node].walk(m):
3375 3390 if abs not in names:
3376 3391 names[abs] = m.rel(abs), m.exact(abs)
3377 3392
3378 3393 m = cmdutil.matchfiles(repo, names)
3379 3394 changes = repo.status(match=m)[:4]
3380 3395 modified, added, removed, deleted = map(set, changes)
3381 3396
3382 3397 # if f is a rename, also revert the source
3383 3398 cwd = repo.getcwd()
3384 3399 for f in added:
3385 3400 src = repo.dirstate.copied(f)
3386 3401 if src and src not in names and repo.dirstate[src] == 'r':
3387 3402 removed.add(src)
3388 3403 names[src] = (repo.pathto(src, cwd), True)
3389 3404
3390 3405 def removeforget(abs):
3391 3406 if repo.dirstate[abs] == 'a':
3392 3407 return _('forgetting %s\n')
3393 3408 return _('removing %s\n')
3394 3409
3395 3410 revert = ([], _('reverting %s\n'))
3396 3411 add = ([], _('adding %s\n'))
3397 3412 remove = ([], removeforget)
3398 3413 undelete = ([], _('undeleting %s\n'))
3399 3414
3400 3415 disptable = (
3401 3416 # dispatch table:
3402 3417 # file state
3403 3418 # action if in target manifest
3404 3419 # action if not in target manifest
3405 3420 # make backup if in target manifest
3406 3421 # make backup if not in target manifest
3407 3422 (modified, revert, remove, True, True),
3408 3423 (added, revert, remove, True, False),
3409 3424 (removed, undelete, None, False, False),
3410 3425 (deleted, revert, remove, False, False),
3411 3426 )
3412 3427
3413 3428 for abs, (rel, exact) in sorted(names.items()):
3414 3429 mfentry = mf.get(abs)
3415 3430 target = repo.wjoin(abs)
3416 3431 def handle(xlist, dobackup):
3417 3432 xlist[0].append(abs)
3418 3433 if (dobackup and not opts.get('no_backup') and
3419 3434 os.path.lexists(target)):
3420 3435 bakname = "%s.orig" % rel
3421 3436 ui.note(_('saving current version of %s as %s\n') %
3422 3437 (rel, bakname))
3423 3438 if not opts.get('dry_run'):
3424 3439 util.rename(target, bakname)
3425 3440 if ui.verbose or not exact:
3426 3441 msg = xlist[1]
3427 3442 if not isinstance(msg, basestring):
3428 3443 msg = msg(abs)
3429 3444 ui.status(msg % rel)
3430 3445 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3431 3446 if abs not in table:
3432 3447 continue
3433 3448 # file has changed in dirstate
3434 3449 if mfentry:
3435 3450 handle(hitlist, backuphit)
3436 3451 elif misslist is not None:
3437 3452 handle(misslist, backupmiss)
3438 3453 break
3439 3454 else:
3440 3455 if abs not in repo.dirstate:
3441 3456 if mfentry:
3442 3457 handle(add, True)
3443 3458 elif exact:
3444 3459 ui.warn(_('file not managed: %s\n') % rel)
3445 3460 continue
3446 3461 # file has not changed in dirstate
3447 3462 if node == parent:
3448 3463 if exact:
3449 3464 ui.warn(_('no changes needed to %s\n') % rel)
3450 3465 continue
3451 3466 if pmf is None:
3452 3467 # only need parent manifest in this unlikely case,
3453 3468 # so do not read by default
3454 3469 pmf = repo[parent].manifest()
3455 3470 if abs in pmf:
3456 3471 if mfentry:
3457 3472 # if version of file is same in parent and target
3458 3473 # manifests, do nothing
3459 3474 if (pmf[abs] != mfentry or
3460 3475 pmf.flags(abs) != mf.flags(abs)):
3461 3476 handle(revert, False)
3462 3477 else:
3463 3478 handle(remove, False)
3464 3479
3465 3480 if not opts.get('dry_run'):
3466 3481 def checkout(f):
3467 3482 fc = ctx[f]
3468 3483 repo.wwrite(f, fc.data(), fc.flags())
3469 3484
3470 3485 audit_path = util.path_auditor(repo.root)
3471 3486 for f in remove[0]:
3472 3487 if repo.dirstate[f] == 'a':
3473 3488 repo.dirstate.forget(f)
3474 3489 continue
3475 3490 audit_path(f)
3476 3491 try:
3477 3492 util.unlinkpath(repo.wjoin(f))
3478 3493 except OSError:
3479 3494 pass
3480 3495 repo.dirstate.remove(f)
3481 3496
3482 3497 normal = None
3483 3498 if node == parent:
3484 3499 # We're reverting to our parent. If possible, we'd like status
3485 3500 # to report the file as clean. We have to use normallookup for
3486 3501 # merges to avoid losing information about merged/dirty files.
3487 3502 if p2 != nullid:
3488 3503 normal = repo.dirstate.normallookup
3489 3504 else:
3490 3505 normal = repo.dirstate.normal
3491 3506 for f in revert[0]:
3492 3507 checkout(f)
3493 3508 if normal:
3494 3509 normal(f)
3495 3510
3496 3511 for f in add[0]:
3497 3512 checkout(f)
3498 3513 repo.dirstate.add(f)
3499 3514
3500 3515 normal = repo.dirstate.normallookup
3501 3516 if node == parent and p2 == nullid:
3502 3517 normal = repo.dirstate.normal
3503 3518 for f in undelete[0]:
3504 3519 checkout(f)
3505 3520 normal(f)
3506 3521
3507 3522 finally:
3508 3523 wlock.release()
3509 3524
3510 3525 def rollback(ui, repo, **opts):
3511 3526 """roll back the last transaction (dangerous)
3512 3527
3513 3528 This command should be used with care. There is only one level of
3514 3529 rollback, and there is no way to undo a rollback. It will also
3515 3530 restore the dirstate at the time of the last transaction, losing
3516 3531 any dirstate changes since that time. This command does not alter
3517 3532 the working directory.
3518 3533
3519 3534 Transactions are used to encapsulate the effects of all commands
3520 3535 that create new changesets or propagate existing changesets into a
3521 3536 repository. For example, the following commands are transactional,
3522 3537 and their effects can be rolled back:
3523 3538
3524 3539 - commit
3525 3540 - import
3526 3541 - pull
3527 3542 - push (with this repository as the destination)
3528 3543 - unbundle
3529 3544
3530 3545 This command is not intended for use on public repositories. Once
3531 3546 changes are visible for pull by other users, rolling a transaction
3532 3547 back locally is ineffective (someone else may already have pulled
3533 3548 the changes). Furthermore, a race is possible with readers of the
3534 3549 repository; for example an in-progress pull from the repository
3535 3550 may fail if a rollback is performed.
3536 3551
3537 3552 Returns 0 on success, 1 if no rollback data is available.
3538 3553 """
3539 3554 return repo.rollback(opts.get('dry_run'))
3540 3555
3541 3556 def root(ui, repo):
3542 3557 """print the root (top) of the current working directory
3543 3558
3544 3559 Print the root directory of the current repository.
3545 3560
3546 3561 Returns 0 on success.
3547 3562 """
3548 3563 ui.write(repo.root + "\n")
3549 3564
3550 3565 def serve(ui, repo, **opts):
3551 3566 """start stand-alone webserver
3552 3567
3553 3568 Start a local HTTP repository browser and pull server. You can use
3554 3569 this for ad-hoc sharing and browsing of repositories. It is
3555 3570 recommended to use a real web server to serve a repository for
3556 3571 longer periods of time.
3557 3572
3558 3573 Please note that the server does not implement access control.
3559 3574 This means that, by default, anybody can read from the server and
3560 3575 nobody can write to it by default. Set the ``web.allow_push``
3561 3576 option to ``*`` to allow everybody to push to the server. You
3562 3577 should use a real web server if you need to authenticate users.
3563 3578
3564 3579 By default, the server logs accesses to stdout and errors to
3565 3580 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3566 3581 files.
3567 3582
3568 3583 To have the server choose a free port number to listen on, specify
3569 3584 a port number of 0; in this case, the server will print the port
3570 3585 number it uses.
3571 3586
3572 3587 Returns 0 on success.
3573 3588 """
3574 3589
3575 3590 if opts["stdio"]:
3576 3591 if repo is None:
3577 3592 raise error.RepoError(_("There is no Mercurial repository here"
3578 3593 " (.hg not found)"))
3579 3594 s = sshserver.sshserver(ui, repo)
3580 3595 s.serve_forever()
3581 3596
3582 3597 # this way we can check if something was given in the command-line
3583 3598 if opts.get('port'):
3584 3599 opts['port'] = util.getport(opts.get('port'))
3585 3600
3586 3601 baseui = repo and repo.baseui or ui
3587 3602 optlist = ("name templates style address port prefix ipv6"
3588 3603 " accesslog errorlog certificate encoding")
3589 3604 for o in optlist.split():
3590 3605 val = opts.get(o, '')
3591 3606 if val in (None, ''): # should check against default options instead
3592 3607 continue
3593 3608 baseui.setconfig("web", o, val)
3594 3609 if repo and repo.ui != baseui:
3595 3610 repo.ui.setconfig("web", o, val)
3596 3611
3597 3612 o = opts.get('web_conf') or opts.get('webdir_conf')
3598 3613 if not o:
3599 3614 if not repo:
3600 3615 raise error.RepoError(_("There is no Mercurial repository"
3601 3616 " here (.hg not found)"))
3602 3617 o = repo.root
3603 3618
3604 3619 app = hgweb.hgweb(o, baseui=ui)
3605 3620
3606 3621 class service(object):
3607 3622 def init(self):
3608 3623 util.set_signal_handler()
3609 3624 self.httpd = hgweb.server.create_server(ui, app)
3610 3625
3611 3626 if opts['port'] and not ui.verbose:
3612 3627 return
3613 3628
3614 3629 if self.httpd.prefix:
3615 3630 prefix = self.httpd.prefix.strip('/') + '/'
3616 3631 else:
3617 3632 prefix = ''
3618 3633
3619 3634 port = ':%d' % self.httpd.port
3620 3635 if port == ':80':
3621 3636 port = ''
3622 3637
3623 3638 bindaddr = self.httpd.addr
3624 3639 if bindaddr == '0.0.0.0':
3625 3640 bindaddr = '*'
3626 3641 elif ':' in bindaddr: # IPv6
3627 3642 bindaddr = '[%s]' % bindaddr
3628 3643
3629 3644 fqaddr = self.httpd.fqaddr
3630 3645 if ':' in fqaddr:
3631 3646 fqaddr = '[%s]' % fqaddr
3632 3647 if opts['port']:
3633 3648 write = ui.status
3634 3649 else:
3635 3650 write = ui.write
3636 3651 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3637 3652 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3638 3653
3639 3654 def run(self):
3640 3655 self.httpd.serve_forever()
3641 3656
3642 3657 service = service()
3643 3658
3644 3659 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3645 3660
3646 3661 def status(ui, repo, *pats, **opts):
3647 3662 """show changed files in the working directory
3648 3663
3649 3664 Show status of files in the repository. If names are given, only
3650 3665 files that match are shown. Files that are clean or ignored or
3651 3666 the source of a copy/move operation, are not listed unless
3652 3667 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3653 3668 Unless options described with "show only ..." are given, the
3654 3669 options -mardu are used.
3655 3670
3656 3671 Option -q/--quiet hides untracked (unknown and ignored) files
3657 3672 unless explicitly requested with -u/--unknown or -i/--ignored.
3658 3673
3659 3674 .. note::
3660 3675 status may appear to disagree with diff if permissions have
3661 3676 changed or a merge has occurred. The standard diff format does
3662 3677 not report permission changes and diff only reports changes
3663 3678 relative to one merge parent.
3664 3679
3665 3680 If one revision is given, it is used as the base revision.
3666 3681 If two revisions are given, the differences between them are
3667 3682 shown. The --change option can also be used as a shortcut to list
3668 3683 the changed files of a revision from its first parent.
3669 3684
3670 3685 The codes used to show the status of files are::
3671 3686
3672 3687 M = modified
3673 3688 A = added
3674 3689 R = removed
3675 3690 C = clean
3676 3691 ! = missing (deleted by non-hg command, but still tracked)
3677 3692 ? = not tracked
3678 3693 I = ignored
3679 3694 = origin of the previous file listed as A (added)
3680 3695
3681 3696 Returns 0 on success.
3682 3697 """
3683 3698
3684 3699 revs = opts.get('rev')
3685 3700 change = opts.get('change')
3686 3701
3687 3702 if revs and change:
3688 3703 msg = _('cannot specify --rev and --change at the same time')
3689 3704 raise util.Abort(msg)
3690 3705 elif change:
3691 3706 node2 = repo.lookup(change)
3692 3707 node1 = repo[node2].parents()[0].node()
3693 3708 else:
3694 3709 node1, node2 = cmdutil.revpair(repo, revs)
3695 3710
3696 3711 cwd = (pats and repo.getcwd()) or ''
3697 3712 end = opts.get('print0') and '\0' or '\n'
3698 3713 copy = {}
3699 3714 states = 'modified added removed deleted unknown ignored clean'.split()
3700 3715 show = [k for k in states if opts.get(k)]
3701 3716 if opts.get('all'):
3702 3717 show += ui.quiet and (states[:4] + ['clean']) or states
3703 3718 if not show:
3704 3719 show = ui.quiet and states[:4] or states[:5]
3705 3720
3706 3721 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3707 3722 'ignored' in show, 'clean' in show, 'unknown' in show,
3708 3723 opts.get('subrepos'))
3709 3724 changestates = zip(states, 'MAR!?IC', stat)
3710 3725
3711 3726 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3712 3727 ctxn = repo[nullid]
3713 3728 ctx1 = repo[node1]
3714 3729 ctx2 = repo[node2]
3715 3730 added = stat[1]
3716 3731 if node2 is None:
3717 3732 added = stat[0] + stat[1] # merged?
3718 3733
3719 3734 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3720 3735 if k in added:
3721 3736 copy[k] = v
3722 3737 elif v in added:
3723 3738 copy[v] = k
3724 3739
3725 3740 for state, char, files in changestates:
3726 3741 if state in show:
3727 3742 format = "%s %%s%s" % (char, end)
3728 3743 if opts.get('no_status'):
3729 3744 format = "%%s%s" % end
3730 3745
3731 3746 for f in files:
3732 3747 ui.write(format % repo.pathto(f, cwd),
3733 3748 label='status.' + state)
3734 3749 if f in copy:
3735 3750 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3736 3751 label='status.copied')
3737 3752
3738 3753 def summary(ui, repo, **opts):
3739 3754 """summarize working directory state
3740 3755
3741 3756 This generates a brief summary of the working directory state,
3742 3757 including parents, branch, commit status, and available updates.
3743 3758
3744 3759 With the --remote option, this will check the default paths for
3745 3760 incoming and outgoing changes. This can be time-consuming.
3746 3761
3747 3762 Returns 0 on success.
3748 3763 """
3749 3764
3750 3765 ctx = repo[None]
3751 3766 parents = ctx.parents()
3752 3767 pnode = parents[0].node()
3753 3768
3754 3769 for p in parents:
3755 3770 # label with log.changeset (instead of log.parent) since this
3756 3771 # shows a working directory parent *changeset*:
3757 3772 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3758 3773 label='log.changeset')
3759 3774 ui.write(' '.join(p.tags()), label='log.tag')
3760 3775 if p.bookmarks():
3761 3776 ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
3762 3777 if p.rev() == -1:
3763 3778 if not len(repo):
3764 3779 ui.write(_(' (empty repository)'))
3765 3780 else:
3766 3781 ui.write(_(' (no revision checked out)'))
3767 3782 ui.write('\n')
3768 3783 if p.description():
3769 3784 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3770 3785 label='log.summary')
3771 3786
3772 3787 branch = ctx.branch()
3773 3788 bheads = repo.branchheads(branch)
3774 3789 m = _('branch: %s\n') % branch
3775 3790 if branch != 'default':
3776 3791 ui.write(m, label='log.branch')
3777 3792 else:
3778 3793 ui.status(m, label='log.branch')
3779 3794
3780 3795 st = list(repo.status(unknown=True))[:6]
3781 3796
3782 3797 c = repo.dirstate.copies()
3783 3798 copied, renamed = [], []
3784 3799 for d, s in c.iteritems():
3785 3800 if s in st[2]:
3786 3801 st[2].remove(s)
3787 3802 renamed.append(d)
3788 3803 else:
3789 3804 copied.append(d)
3790 3805 if d in st[1]:
3791 3806 st[1].remove(d)
3792 3807 st.insert(3, renamed)
3793 3808 st.insert(4, copied)
3794 3809
3795 3810 ms = mergemod.mergestate(repo)
3796 3811 st.append([f for f in ms if ms[f] == 'u'])
3797 3812
3798 3813 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3799 3814 st.append(subs)
3800 3815
3801 3816 labels = [ui.label(_('%d modified'), 'status.modified'),
3802 3817 ui.label(_('%d added'), 'status.added'),
3803 3818 ui.label(_('%d removed'), 'status.removed'),
3804 3819 ui.label(_('%d renamed'), 'status.copied'),
3805 3820 ui.label(_('%d copied'), 'status.copied'),
3806 3821 ui.label(_('%d deleted'), 'status.deleted'),
3807 3822 ui.label(_('%d unknown'), 'status.unknown'),
3808 3823 ui.label(_('%d ignored'), 'status.ignored'),
3809 3824 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3810 3825 ui.label(_('%d subrepos'), 'status.modified')]
3811 3826 t = []
3812 3827 for s, l in zip(st, labels):
3813 3828 if s:
3814 3829 t.append(l % len(s))
3815 3830
3816 3831 t = ', '.join(t)
3817 3832 cleanworkdir = False
3818 3833
3819 3834 if len(parents) > 1:
3820 3835 t += _(' (merge)')
3821 3836 elif branch != parents[0].branch():
3822 3837 t += _(' (new branch)')
3823 3838 elif (parents[0].extra().get('close') and
3824 3839 pnode in repo.branchheads(branch, closed=True)):
3825 3840 t += _(' (head closed)')
3826 3841 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3827 3842 t += _(' (clean)')
3828 3843 cleanworkdir = True
3829 3844 elif pnode not in bheads:
3830 3845 t += _(' (new branch head)')
3831 3846
3832 3847 if cleanworkdir:
3833 3848 ui.status(_('commit: %s\n') % t.strip())
3834 3849 else:
3835 3850 ui.write(_('commit: %s\n') % t.strip())
3836 3851
3837 3852 # all ancestors of branch heads - all ancestors of parent = new csets
3838 3853 new = [0] * len(repo)
3839 3854 cl = repo.changelog
3840 3855 for a in [cl.rev(n) for n in bheads]:
3841 3856 new[a] = 1
3842 3857 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3843 3858 new[a] = 1
3844 3859 for a in [p.rev() for p in parents]:
3845 3860 if a >= 0:
3846 3861 new[a] = 0
3847 3862 for a in cl.ancestors(*[p.rev() for p in parents]):
3848 3863 new[a] = 0
3849 3864 new = sum(new)
3850 3865
3851 3866 if new == 0:
3852 3867 ui.status(_('update: (current)\n'))
3853 3868 elif pnode not in bheads:
3854 3869 ui.write(_('update: %d new changesets (update)\n') % new)
3855 3870 else:
3856 3871 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3857 3872 (new, len(bheads)))
3858 3873
3859 3874 if opts.get('remote'):
3860 3875 t = []
3861 3876 source, branches = hg.parseurl(ui.expandpath('default'))
3862 3877 other = hg.repository(hg.remoteui(repo, {}), source)
3863 3878 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3864 3879 ui.debug('comparing with %s\n' % url.hidepassword(source))
3865 3880 repo.ui.pushbuffer()
3866 3881 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3867 3882 repo.ui.popbuffer()
3868 3883 if incoming:
3869 3884 t.append(_('1 or more incoming'))
3870 3885
3871 3886 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3872 3887 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3873 3888 other = hg.repository(hg.remoteui(repo, {}), dest)
3874 3889 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3875 3890 repo.ui.pushbuffer()
3876 3891 o = discovery.findoutgoing(repo, other)
3877 3892 repo.ui.popbuffer()
3878 3893 o = repo.changelog.nodesbetween(o, None)[0]
3879 3894 if o:
3880 3895 t.append(_('%d outgoing') % len(o))
3881 3896 if 'bookmarks' in other.listkeys('namespaces'):
3882 3897 lmarks = repo.listkeys('bookmarks')
3883 3898 rmarks = other.listkeys('bookmarks')
3884 3899 diff = set(rmarks) - set(lmarks)
3885 3900 if len(diff) > 0:
3886 3901 t.append(_('%d incoming bookmarks') % len(diff))
3887 3902 diff = set(lmarks) - set(rmarks)
3888 3903 if len(diff) > 0:
3889 3904 t.append(_('%d outgoing bookmarks') % len(diff))
3890 3905
3891 3906 if t:
3892 3907 ui.write(_('remote: %s\n') % (', '.join(t)))
3893 3908 else:
3894 3909 ui.status(_('remote: (synced)\n'))
3895 3910
3896 3911 def tag(ui, repo, name1, *names, **opts):
3897 3912 """add one or more tags for the current or given revision
3898 3913
3899 3914 Name a particular revision using <name>.
3900 3915
3901 3916 Tags are used to name particular revisions of the repository and are
3902 3917 very useful to compare different revisions, to go back to significant
3903 3918 earlier versions or to mark branch points as releases, etc. Changing
3904 3919 an existing tag is normally disallowed; use -f/--force to override.
3905 3920
3906 3921 If no revision is given, the parent of the working directory is
3907 3922 used, or tip if no revision is checked out.
3908 3923
3909 3924 To facilitate version control, distribution, and merging of tags,
3910 3925 they are stored as a file named ".hgtags" which is managed similarly
3911 3926 to other project files and can be hand-edited if necessary. This
3912 3927 also means that tagging creates a new commit. The file
3913 3928 ".hg/localtags" is used for local tags (not shared among
3914 3929 repositories).
3915 3930
3916 3931 Tag commits are usually made at the head of a branch. If the parent
3917 3932 of the working directory is not a branch head, :hg:`tag` aborts; use
3918 3933 -f/--force to force the tag commit to be based on a non-head
3919 3934 changeset.
3920 3935
3921 3936 See :hg:`help dates` for a list of formats valid for -d/--date.
3922 3937
3923 3938 Since tag names have priority over branch names during revision
3924 3939 lookup, using an existing branch name as a tag name is discouraged.
3925 3940
3926 3941 Returns 0 on success.
3927 3942 """
3928 3943
3929 3944 rev_ = "."
3930 3945 names = [t.strip() for t in (name1,) + names]
3931 3946 if len(names) != len(set(names)):
3932 3947 raise util.Abort(_('tag names must be unique'))
3933 3948 for n in names:
3934 3949 if n in ['tip', '.', 'null']:
3935 3950 raise util.Abort(_('the name \'%s\' is reserved') % n)
3936 3951 if not n:
3937 3952 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3938 3953 if opts.get('rev') and opts.get('remove'):
3939 3954 raise util.Abort(_("--rev and --remove are incompatible"))
3940 3955 if opts.get('rev'):
3941 3956 rev_ = opts['rev']
3942 3957 message = opts.get('message')
3943 3958 if opts.get('remove'):
3944 3959 expectedtype = opts.get('local') and 'local' or 'global'
3945 3960 for n in names:
3946 3961 if not repo.tagtype(n):
3947 3962 raise util.Abort(_('tag \'%s\' does not exist') % n)
3948 3963 if repo.tagtype(n) != expectedtype:
3949 3964 if expectedtype == 'global':
3950 3965 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3951 3966 else:
3952 3967 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3953 3968 rev_ = nullid
3954 3969 if not message:
3955 3970 # we don't translate commit messages
3956 3971 message = 'Removed tag %s' % ', '.join(names)
3957 3972 elif not opts.get('force'):
3958 3973 for n in names:
3959 3974 if n in repo.tags():
3960 3975 raise util.Abort(_('tag \'%s\' already exists '
3961 3976 '(use -f to force)') % n)
3962 3977 if not opts.get('local'):
3963 3978 p1, p2 = repo.dirstate.parents()
3964 3979 if p2 != nullid:
3965 3980 raise util.Abort(_('uncommitted merge'))
3966 3981 bheads = repo.branchheads()
3967 3982 if not opts.get('force') and bheads and p1 not in bheads:
3968 3983 raise util.Abort(_('not at a branch head (use -f to force)'))
3969 3984 r = cmdutil.revsingle(repo, rev_).node()
3970 3985
3971 3986 if not message:
3972 3987 # we don't translate commit messages
3973 3988 message = ('Added tag %s for changeset %s' %
3974 3989 (', '.join(names), short(r)))
3975 3990
3976 3991 date = opts.get('date')
3977 3992 if date:
3978 3993 date = util.parsedate(date)
3979 3994
3980 3995 if opts.get('edit'):
3981 3996 message = ui.edit(message, ui.username())
3982 3997
3983 3998 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3984 3999
3985 4000 def tags(ui, repo):
3986 4001 """list repository tags
3987 4002
3988 4003 This lists both regular and local tags. When the -v/--verbose
3989 4004 switch is used, a third column "local" is printed for local tags.
3990 4005
3991 4006 Returns 0 on success.
3992 4007 """
3993 4008
3994 4009 hexfunc = ui.debugflag and hex or short
3995 4010 tagtype = ""
3996 4011
3997 4012 for t, n in reversed(repo.tagslist()):
3998 4013 if ui.quiet:
3999 4014 ui.write("%s\n" % t)
4000 4015 continue
4001 4016
4002 4017 try:
4003 4018 hn = hexfunc(n)
4004 4019 r = "%5d:%s" % (repo.changelog.rev(n), hn)
4005 4020 except error.LookupError:
4006 4021 r = " ?:%s" % hn
4007 4022 else:
4008 4023 spaces = " " * (30 - encoding.colwidth(t))
4009 4024 if ui.verbose:
4010 4025 if repo.tagtype(t) == 'local':
4011 4026 tagtype = " local"
4012 4027 else:
4013 4028 tagtype = ""
4014 4029 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
4015 4030
4016 4031 def tip(ui, repo, **opts):
4017 4032 """show the tip revision
4018 4033
4019 4034 The tip revision (usually just called the tip) is the changeset
4020 4035 most recently added to the repository (and therefore the most
4021 4036 recently changed head).
4022 4037
4023 4038 If you have just made a commit, that commit will be the tip. If
4024 4039 you have just pulled changes from another repository, the tip of
4025 4040 that repository becomes the current tip. The "tip" tag is special
4026 4041 and cannot be renamed or assigned to a different changeset.
4027 4042
4028 4043 Returns 0 on success.
4029 4044 """
4030 4045 displayer = cmdutil.show_changeset(ui, repo, opts)
4031 4046 displayer.show(repo[len(repo) - 1])
4032 4047 displayer.close()
4033 4048
4034 4049 def unbundle(ui, repo, fname1, *fnames, **opts):
4035 4050 """apply one or more changegroup files
4036 4051
4037 4052 Apply one or more compressed changegroup files generated by the
4038 4053 bundle command.
4039 4054
4040 4055 Returns 0 on success, 1 if an update has unresolved files.
4041 4056 """
4042 4057 fnames = (fname1,) + fnames
4043 4058
4044 4059 lock = repo.lock()
4045 4060 wc = repo['.']
4046 4061 try:
4047 4062 for fname in fnames:
4048 4063 f = url.open(ui, fname)
4049 4064 gen = changegroup.readbundle(f, fname)
4050 4065 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
4051 4066 lock=lock)
4052 4067 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
4053 4068 finally:
4054 4069 lock.release()
4055 4070 return postincoming(ui, repo, modheads, opts.get('update'), None)
4056 4071
4057 4072 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
4058 4073 """update working directory (or switch revisions)
4059 4074
4060 4075 Update the repository's working directory to the specified
4061 4076 changeset. If no changeset is specified, update to the tip of the
4062 4077 current named branch.
4063 4078
4064 4079 If the changeset is not a descendant of the working directory's
4065 4080 parent, the update is aborted. With the -c/--check option, the
4066 4081 working directory is checked for uncommitted changes; if none are
4067 4082 found, the working directory is updated to the specified
4068 4083 changeset.
4069 4084
4070 4085 The following rules apply when the working directory contains
4071 4086 uncommitted changes:
4072 4087
4073 4088 1. If neither -c/--check nor -C/--clean is specified, and if
4074 4089 the requested changeset is an ancestor or descendant of
4075 4090 the working directory's parent, the uncommitted changes
4076 4091 are merged into the requested changeset and the merged
4077 4092 result is left uncommitted. If the requested changeset is
4078 4093 not an ancestor or descendant (that is, it is on another
4079 4094 branch), the update is aborted and the uncommitted changes
4080 4095 are preserved.
4081 4096
4082 4097 2. With the -c/--check option, the update is aborted and the
4083 4098 uncommitted changes are preserved.
4084 4099
4085 4100 3. With the -C/--clean option, uncommitted changes are discarded and
4086 4101 the working directory is updated to the requested changeset.
4087 4102
4088 4103 Use null as the changeset to remove the working directory (like
4089 4104 :hg:`clone -U`).
4090 4105
4091 4106 If you want to update just one file to an older changeset, use
4092 4107 :hg:`revert`.
4093 4108
4094 4109 See :hg:`help dates` for a list of formats valid for -d/--date.
4095 4110
4096 4111 Returns 0 on success, 1 if there are unresolved files.
4097 4112 """
4098 4113 if rev and node:
4099 4114 raise util.Abort(_("please specify just one revision"))
4100 4115
4101 4116 if rev is None or rev == '':
4102 4117 rev = node
4103 4118
4104 4119 # if we defined a bookmark, we have to remember the original bookmark name
4105 4120 brev = rev
4106 4121 rev = cmdutil.revsingle(repo, rev, rev).rev()
4107 4122
4108 4123 if check and clean:
4109 4124 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
4110 4125
4111 4126 if check:
4112 4127 # we could use dirty() but we can ignore merge and branch trivia
4113 4128 c = repo[None]
4114 4129 if c.modified() or c.added() or c.removed():
4115 4130 raise util.Abort(_("uncommitted local changes"))
4116 4131
4117 4132 if date:
4118 4133 if rev:
4119 4134 raise util.Abort(_("you can't specify a revision and a date"))
4120 4135 rev = cmdutil.finddate(ui, repo, date)
4121 4136
4122 4137 if clean or check:
4123 4138 ret = hg.clean(repo, rev)
4124 4139 else:
4125 4140 ret = hg.update(repo, rev)
4126 4141
4127 4142 if brev in repo._bookmarks:
4128 4143 bookmarks.setcurrent(repo, brev)
4129 4144
4130 4145 return ret
4131 4146
4132 4147 def verify(ui, repo):
4133 4148 """verify the integrity of the repository
4134 4149
4135 4150 Verify the integrity of the current repository.
4136 4151
4137 4152 This will perform an extensive check of the repository's
4138 4153 integrity, validating the hashes and checksums of each entry in
4139 4154 the changelog, manifest, and tracked files, as well as the
4140 4155 integrity of their crosslinks and indices.
4141 4156
4142 4157 Returns 0 on success, 1 if errors are encountered.
4143 4158 """
4144 4159 return hg.verify(repo)
4145 4160
4146 4161 def version_(ui):
4147 4162 """output version and copyright information"""
4148 4163 ui.write(_("Mercurial Distributed SCM (version %s)\n")
4149 4164 % util.version())
4150 4165 ui.status(_(
4151 4166 "(see http://mercurial.selenic.com for more information)\n"
4152 4167 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
4153 4168 "This is free software; see the source for copying conditions. "
4154 4169 "There is NO\nwarranty; "
4155 4170 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
4156 4171 ))
4157 4172
4158 4173 # Command options and aliases are listed here, alphabetically
4159 4174
4160 4175 globalopts = [
4161 4176 ('R', 'repository', '',
4162 4177 _('repository root directory or name of overlay bundle file'),
4163 4178 _('REPO')),
4164 4179 ('', 'cwd', '',
4165 4180 _('change working directory'), _('DIR')),
4166 4181 ('y', 'noninteractive', None,
4167 4182 _('do not prompt, assume \'yes\' for any required answers')),
4168 4183 ('q', 'quiet', None, _('suppress output')),
4169 4184 ('v', 'verbose', None, _('enable additional output')),
4170 4185 ('', 'config', [],
4171 4186 _('set/override config option (use \'section.name=value\')'),
4172 4187 _('CONFIG')),
4173 4188 ('', 'debug', None, _('enable debugging output')),
4174 4189 ('', 'debugger', None, _('start debugger')),
4175 4190 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
4176 4191 _('ENCODE')),
4177 4192 ('', 'encodingmode', encoding.encodingmode,
4178 4193 _('set the charset encoding mode'), _('MODE')),
4179 4194 ('', 'traceback', None, _('always print a traceback on exception')),
4180 4195 ('', 'time', None, _('time how long the command takes')),
4181 4196 ('', 'profile', None, _('print command execution profile')),
4182 4197 ('', 'version', None, _('output version information and exit')),
4183 4198 ('h', 'help', None, _('display help and exit')),
4184 4199 ]
4185 4200
4186 4201 dryrunopts = [('n', 'dry-run', None,
4187 4202 _('do not perform actions, just print output'))]
4188 4203
4189 4204 remoteopts = [
4190 4205 ('e', 'ssh', '',
4191 4206 _('specify ssh command to use'), _('CMD')),
4192 4207 ('', 'remotecmd', '',
4193 4208 _('specify hg command to run on the remote side'), _('CMD')),
4194 4209 ('', 'insecure', None,
4195 4210 _('do not verify server certificate (ignoring web.cacerts config)')),
4196 4211 ]
4197 4212
4198 4213 walkopts = [
4199 4214 ('I', 'include', [],
4200 4215 _('include names matching the given patterns'), _('PATTERN')),
4201 4216 ('X', 'exclude', [],
4202 4217 _('exclude names matching the given patterns'), _('PATTERN')),
4203 4218 ]
4204 4219
4205 4220 commitopts = [
4206 4221 ('m', 'message', '',
4207 4222 _('use text as commit message'), _('TEXT')),
4208 4223 ('l', 'logfile', '',
4209 4224 _('read commit message from file'), _('FILE')),
4210 4225 ]
4211 4226
4212 4227 commitopts2 = [
4213 4228 ('d', 'date', '',
4214 4229 _('record datecode as commit date'), _('DATE')),
4215 4230 ('u', 'user', '',
4216 4231 _('record the specified user as committer'), _('USER')),
4217 4232 ]
4218 4233
4219 4234 templateopts = [
4220 4235 ('', 'style', '',
4221 4236 _('display using template map file'), _('STYLE')),
4222 4237 ('', 'template', '',
4223 4238 _('display with template'), _('TEMPLATE')),
4224 4239 ]
4225 4240
4226 4241 logopts = [
4227 4242 ('p', 'patch', None, _('show patch')),
4228 4243 ('g', 'git', None, _('use git extended diff format')),
4229 4244 ('l', 'limit', '',
4230 4245 _('limit number of changes displayed'), _('NUM')),
4231 4246 ('M', 'no-merges', None, _('do not show merges')),
4232 4247 ('', 'stat', None, _('output diffstat-style summary of changes')),
4233 4248 ] + templateopts
4234 4249
4235 4250 diffopts = [
4236 4251 ('a', 'text', None, _('treat all files as text')),
4237 4252 ('g', 'git', None, _('use git extended diff format')),
4238 4253 ('', 'nodates', None, _('omit dates from diff headers'))
4239 4254 ]
4240 4255
4241 4256 diffopts2 = [
4242 4257 ('p', 'show-function', None, _('show which function each change is in')),
4243 4258 ('', 'reverse', None, _('produce a diff that undoes the changes')),
4244 4259 ('w', 'ignore-all-space', None,
4245 4260 _('ignore white space when comparing lines')),
4246 4261 ('b', 'ignore-space-change', None,
4247 4262 _('ignore changes in the amount of white space')),
4248 4263 ('B', 'ignore-blank-lines', None,
4249 4264 _('ignore changes whose lines are all blank')),
4250 4265 ('U', 'unified', '',
4251 4266 _('number of lines of context to show'), _('NUM')),
4252 4267 ('', 'stat', None, _('output diffstat-style summary of changes')),
4253 4268 ]
4254 4269
4255 4270 similarityopts = [
4256 4271 ('s', 'similarity', '',
4257 4272 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
4258 4273 ]
4259 4274
4260 4275 subrepoopts = [
4261 4276 ('S', 'subrepos', None,
4262 4277 _('recurse into subrepositories'))
4263 4278 ]
4264 4279
4265 4280 table = {
4266 4281 "^add": (add, walkopts + subrepoopts + dryrunopts,
4267 4282 _('[OPTION]... [FILE]...')),
4268 4283 "addremove":
4269 4284 (addremove, similarityopts + walkopts + dryrunopts,
4270 4285 _('[OPTION]... [FILE]...')),
4271 4286 "^annotate|blame":
4272 4287 (annotate,
4273 4288 [('r', 'rev', '',
4274 4289 _('annotate the specified revision'), _('REV')),
4275 4290 ('', 'follow', None,
4276 4291 _('follow copies/renames and list the filename (DEPRECATED)')),
4277 4292 ('', 'no-follow', None, _("don't follow copies and renames")),
4278 4293 ('a', 'text', None, _('treat all files as text')),
4279 4294 ('u', 'user', None, _('list the author (long with -v)')),
4280 4295 ('f', 'file', None, _('list the filename')),
4281 4296 ('d', 'date', None, _('list the date (short with -q)')),
4282 4297 ('n', 'number', None, _('list the revision number (default)')),
4283 4298 ('c', 'changeset', None, _('list the changeset')),
4284 4299 ('l', 'line-number', None,
4285 4300 _('show line number at the first appearance'))
4286 4301 ] + walkopts,
4287 4302 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
4288 4303 "archive":
4289 4304 (archive,
4290 4305 [('', 'no-decode', None, _('do not pass files through decoders')),
4291 4306 ('p', 'prefix', '',
4292 4307 _('directory prefix for files in archive'), _('PREFIX')),
4293 4308 ('r', 'rev', '',
4294 4309 _('revision to distribute'), _('REV')),
4295 4310 ('t', 'type', '',
4296 4311 _('type of distribution to create'), _('TYPE')),
4297 4312 ] + subrepoopts + walkopts,
4298 4313 _('[OPTION]... DEST')),
4299 4314 "backout":
4300 4315 (backout,
4301 4316 [('', 'merge', None,
4302 4317 _('merge with old dirstate parent after backout')),
4303 4318 ('', 'parent', '',
4304 4319 _('parent to choose when backing out merge'), _('REV')),
4305 4320 ('t', 'tool', '',
4306 4321 _('specify merge tool')),
4307 4322 ('r', 'rev', '',
4308 4323 _('revision to backout'), _('REV')),
4309 4324 ] + walkopts + commitopts + commitopts2,
4310 4325 _('[OPTION]... [-r] REV')),
4311 4326 "bisect":
4312 4327 (bisect,
4313 4328 [('r', 'reset', False, _('reset bisect state')),
4314 4329 ('g', 'good', False, _('mark changeset good')),
4315 4330 ('b', 'bad', False, _('mark changeset bad')),
4316 4331 ('s', 'skip', False, _('skip testing changeset')),
4317 4332 ('e', 'extend', False, _('extend the bisect range')),
4318 4333 ('c', 'command', '',
4319 4334 _('use command to check changeset state'), _('CMD')),
4320 4335 ('U', 'noupdate', False, _('do not update to target'))],
4321 4336 _("[-gbsr] [-U] [-c CMD] [REV]")),
4322 4337 "bookmarks":
4323 4338 (bookmark,
4324 4339 [('f', 'force', False, _('force')),
4325 4340 ('r', 'rev', '', _('revision'), _('REV')),
4326 4341 ('d', 'delete', False, _('delete a given bookmark')),
4327 4342 ('m', 'rename', '', _('rename a given bookmark'), _('NAME'))],
4328 4343 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
4329 4344 "branch":
4330 4345 (branch,
4331 4346 [('f', 'force', None,
4332 4347 _('set branch name even if it shadows an existing branch')),
4333 4348 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4334 4349 _('[-fC] [NAME]')),
4335 4350 "branches":
4336 4351 (branches,
4337 4352 [('a', 'active', False,
4338 4353 _('show only branches that have unmerged heads')),
4339 4354 ('c', 'closed', False,
4340 4355 _('show normal and closed branches'))],
4341 4356 _('[-ac]')),
4342 4357 "bundle":
4343 4358 (bundle,
4344 4359 [('f', 'force', None,
4345 4360 _('run even when the destination is unrelated')),
4346 4361 ('r', 'rev', [],
4347 4362 _('a changeset intended to be added to the destination'),
4348 4363 _('REV')),
4349 4364 ('b', 'branch', [],
4350 4365 _('a specific branch you would like to bundle'),
4351 4366 _('BRANCH')),
4352 4367 ('', 'base', [],
4353 4368 _('a base changeset assumed to be available at the destination'),
4354 4369 _('REV')),
4355 4370 ('a', 'all', None, _('bundle all changesets in the repository')),
4356 4371 ('t', 'type', 'bzip2',
4357 4372 _('bundle compression type to use'), _('TYPE')),
4358 4373 ] + remoteopts,
4359 4374 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4360 4375 "cat":
4361 4376 (cat,
4362 4377 [('o', 'output', '',
4363 4378 _('print output to file with formatted name'), _('FORMAT')),
4364 4379 ('r', 'rev', '',
4365 4380 _('print the given revision'), _('REV')),
4366 4381 ('', 'decode', None, _('apply any matching decode filter')),
4367 4382 ] + walkopts,
4368 4383 _('[OPTION]... FILE...')),
4369 4384 "^clone":
4370 4385 (clone,
4371 4386 [('U', 'noupdate', None,
4372 4387 _('the clone will include an empty working copy (only a repository)')),
4373 4388 ('u', 'updaterev', '',
4374 4389 _('revision, tag or branch to check out'), _('REV')),
4375 4390 ('r', 'rev', [],
4376 4391 _('include the specified changeset'), _('REV')),
4377 4392 ('b', 'branch', [],
4378 4393 _('clone only the specified branch'), _('BRANCH')),
4379 4394 ('', 'pull', None, _('use pull protocol to copy metadata')),
4380 4395 ('', 'uncompressed', None,
4381 4396 _('use uncompressed transfer (fast over LAN)')),
4382 4397 ] + remoteopts,
4383 4398 _('[OPTION]... SOURCE [DEST]')),
4384 4399 "^commit|ci":
4385 4400 (commit,
4386 4401 [('A', 'addremove', None,
4387 4402 _('mark new/missing files as added/removed before committing')),
4388 4403 ('', 'close-branch', None,
4389 4404 _('mark a branch as closed, hiding it from the branch list')),
4390 4405 ] + walkopts + commitopts + commitopts2,
4391 4406 _('[OPTION]... [FILE]...')),
4392 4407 "copy|cp":
4393 4408 (copy,
4394 4409 [('A', 'after', None, _('record a copy that has already occurred')),
4395 4410 ('f', 'force', None,
4396 4411 _('forcibly copy over an existing managed file')),
4397 4412 ] + walkopts + dryrunopts,
4398 4413 _('[OPTION]... [SOURCE]... DEST')),
4399 4414 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4400 4415 "debugbuilddag":
4401 4416 (debugbuilddag,
4402 4417 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4403 4418 ('a', 'appended-file', None, _('add single file all revs append to')),
4404 4419 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4405 4420 ('n', 'new-file', None, _('add new file at each rev')),
4406 4421 ],
4407 4422 _('[OPTION]... TEXT')),
4408 4423 "debugcheckstate": (debugcheckstate, [], ''),
4409 4424 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4410 4425 "debugcomplete":
4411 4426 (debugcomplete,
4412 4427 [('o', 'options', None, _('show the command options'))],
4413 4428 _('[-o] CMD')),
4414 4429 "debugdag":
4415 4430 (debugdag,
4416 4431 [('t', 'tags', None, _('use tags as labels')),
4417 4432 ('b', 'branches', None, _('annotate with branch names')),
4418 4433 ('', 'dots', None, _('use dots for runs')),
4419 4434 ('s', 'spaces', None, _('separate elements by spaces')),
4420 4435 ],
4421 4436 _('[OPTION]... [FILE [REV]...]')),
4422 4437 "debugdate":
4423 4438 (debugdate,
4424 4439 [('e', 'extended', None, _('try extended date formats'))],
4425 4440 _('[-e] DATE [RANGE]')),
4426 4441 "debugdata": (debugdata, [], _('FILE REV')),
4427 4442 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4428 4443 "debugignore": (debugignore, [], ''),
4429 4444 "debugindex": (debugindex,
4430 4445 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
4431 4446 _('FILE')),
4432 4447 "debugindexdot": (debugindexdot, [], _('FILE')),
4433 4448 "debuginstall": (debuginstall, [], ''),
4434 4449 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4435 4450 "debugrebuildstate":
4436 4451 (debugrebuildstate,
4437 4452 [('r', 'rev', '',
4438 4453 _('revision to rebuild to'), _('REV'))],
4439 4454 _('[-r REV] [REV]')),
4440 4455 "debugrename":
4441 4456 (debugrename,
4442 4457 [('r', 'rev', '',
4443 4458 _('revision to debug'), _('REV'))],
4444 4459 _('[-r REV] FILE')),
4445 4460 "debugrevspec":
4446 4461 (debugrevspec, [], ('REVSPEC')),
4447 4462 "debugsetparents":
4448 4463 (debugsetparents, [], _('REV1 [REV2]')),
4449 4464 "debugstate":
4450 4465 (debugstate,
4451 4466 [('', 'nodates', None, _('do not display the saved mtime'))],
4452 4467 _('[OPTION]...')),
4453 4468 "debugsub":
4454 4469 (debugsub,
4455 4470 [('r', 'rev', '',
4456 4471 _('revision to check'), _('REV'))],
4457 4472 _('[-r REV] [REV]')),
4458 4473 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4474 "debugwireargs":
4475 (debugwireargs,
4476 [('', 'three', '', 'three'),
4477 ('', 'four', '', 'four'),
4478 ] + remoteopts,
4479 _('REPO [OPTIONS]... [ONE [TWO]]')),
4459 4480 "^diff":
4460 4481 (diff,
4461 4482 [('r', 'rev', [],
4462 4483 _('revision'), _('REV')),
4463 4484 ('c', 'change', '',
4464 4485 _('change made by revision'), _('REV'))
4465 4486 ] + diffopts + diffopts2 + walkopts + subrepoopts,
4466 4487 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4467 4488 "^export":
4468 4489 (export,
4469 4490 [('o', 'output', '',
4470 4491 _('print output to file with formatted name'), _('FORMAT')),
4471 4492 ('', 'switch-parent', None, _('diff against the second parent')),
4472 4493 ('r', 'rev', [],
4473 4494 _('revisions to export'), _('REV')),
4474 4495 ] + diffopts,
4475 4496 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4476 4497 "^forget":
4477 4498 (forget,
4478 4499 [] + walkopts,
4479 4500 _('[OPTION]... FILE...')),
4480 4501 "grep":
4481 4502 (grep,
4482 4503 [('0', 'print0', None, _('end fields with NUL')),
4483 4504 ('', 'all', None, _('print all revisions that match')),
4484 4505 ('f', 'follow', None,
4485 4506 _('follow changeset history,'
4486 4507 ' or file history across copies and renames')),
4487 4508 ('i', 'ignore-case', None, _('ignore case when matching')),
4488 4509 ('l', 'files-with-matches', None,
4489 4510 _('print only filenames and revisions that match')),
4490 4511 ('n', 'line-number', None, _('print matching line numbers')),
4491 4512 ('r', 'rev', [],
4492 4513 _('only search files changed within revision range'), _('REV')),
4493 4514 ('u', 'user', None, _('list the author (long with -v)')),
4494 4515 ('d', 'date', None, _('list the date (short with -q)')),
4495 4516 ] + walkopts,
4496 4517 _('[OPTION]... PATTERN [FILE]...')),
4497 4518 "heads":
4498 4519 (heads,
4499 4520 [('r', 'rev', '',
4500 4521 _('show only heads which are descendants of STARTREV'),
4501 4522 _('STARTREV')),
4502 4523 ('t', 'topo', False, _('show topological heads only')),
4503 4524 ('a', 'active', False,
4504 4525 _('show active branchheads only (DEPRECATED)')),
4505 4526 ('c', 'closed', False,
4506 4527 _('show normal and closed branch heads')),
4507 4528 ] + templateopts,
4508 4529 _('[-ac] [-r STARTREV] [REV]...')),
4509 4530 "help": (help_, [], _('[TOPIC]')),
4510 4531 "identify|id":
4511 4532 (identify,
4512 4533 [('r', 'rev', '',
4513 4534 _('identify the specified revision'), _('REV')),
4514 4535 ('n', 'num', None, _('show local revision number')),
4515 4536 ('i', 'id', None, _('show global revision id')),
4516 4537 ('b', 'branch', None, _('show branch')),
4517 4538 ('t', 'tags', None, _('show tags')),
4518 4539 ('B', 'bookmarks', None, _('show bookmarks'))],
4519 4540 _('[-nibtB] [-r REV] [SOURCE]')),
4520 4541 "import|patch":
4521 4542 (import_,
4522 4543 [('p', 'strip', 1,
4523 4544 _('directory strip option for patch. This has the same '
4524 4545 'meaning as the corresponding patch option'),
4525 4546 _('NUM')),
4526 4547 ('b', 'base', '',
4527 4548 _('base path'), _('PATH')),
4528 4549 ('f', 'force', None,
4529 4550 _('skip check for outstanding uncommitted changes')),
4530 4551 ('', 'no-commit', None,
4531 4552 _("don't commit, just update the working directory")),
4532 4553 ('', 'exact', None,
4533 4554 _('apply patch to the nodes from which it was generated')),
4534 4555 ('', 'import-branch', None,
4535 4556 _('use any branch information in patch (implied by --exact)'))] +
4536 4557 commitopts + commitopts2 + similarityopts,
4537 4558 _('[OPTION]... PATCH...')),
4538 4559 "incoming|in":
4539 4560 (incoming,
4540 4561 [('f', 'force', None,
4541 4562 _('run even if remote repository is unrelated')),
4542 4563 ('n', 'newest-first', None, _('show newest record first')),
4543 4564 ('', 'bundle', '',
4544 4565 _('file to store the bundles into'), _('FILE')),
4545 4566 ('r', 'rev', [],
4546 4567 _('a remote changeset intended to be added'), _('REV')),
4547 4568 ('B', 'bookmarks', False, _("compare bookmarks")),
4548 4569 ('b', 'branch', [],
4549 4570 _('a specific branch you would like to pull'), _('BRANCH')),
4550 4571 ] + logopts + remoteopts + subrepoopts,
4551 4572 _('[-p] [-n] [-M] [-f] [-r REV]...'
4552 4573 ' [--bundle FILENAME] [SOURCE]')),
4553 4574 "^init":
4554 4575 (init,
4555 4576 remoteopts,
4556 4577 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4557 4578 "locate":
4558 4579 (locate,
4559 4580 [('r', 'rev', '',
4560 4581 _('search the repository as it is in REV'), _('REV')),
4561 4582 ('0', 'print0', None,
4562 4583 _('end filenames with NUL, for use with xargs')),
4563 4584 ('f', 'fullpath', None,
4564 4585 _('print complete paths from the filesystem root')),
4565 4586 ] + walkopts,
4566 4587 _('[OPTION]... [PATTERN]...')),
4567 4588 "^log|history":
4568 4589 (log,
4569 4590 [('f', 'follow', None,
4570 4591 _('follow changeset history,'
4571 4592 ' or file history across copies and renames')),
4572 4593 ('', 'follow-first', None,
4573 4594 _('only follow the first parent of merge changesets')),
4574 4595 ('d', 'date', '',
4575 4596 _('show revisions matching date spec'), _('DATE')),
4576 4597 ('C', 'copies', None, _('show copied files')),
4577 4598 ('k', 'keyword', [],
4578 4599 _('do case-insensitive search for a given text'), _('TEXT')),
4579 4600 ('r', 'rev', [],
4580 4601 _('show the specified revision or range'), _('REV')),
4581 4602 ('', 'removed', None, _('include revisions where files were removed')),
4582 4603 ('m', 'only-merges', None, _('show only merges')),
4583 4604 ('u', 'user', [],
4584 4605 _('revisions committed by user'), _('USER')),
4585 4606 ('', 'only-branch', [],
4586 4607 _('show only changesets within the given named branch (DEPRECATED)'),
4587 4608 _('BRANCH')),
4588 4609 ('b', 'branch', [],
4589 4610 _('show changesets within the given named branch'), _('BRANCH')),
4590 4611 ('P', 'prune', [],
4591 4612 _('do not display revision or any of its ancestors'), _('REV')),
4592 4613 ] + logopts + walkopts,
4593 4614 _('[OPTION]... [FILE]')),
4594 4615 "manifest":
4595 4616 (manifest,
4596 4617 [('r', 'rev', '',
4597 4618 _('revision to display'), _('REV'))],
4598 4619 _('[-r REV]')),
4599 4620 "^merge":
4600 4621 (merge,
4601 4622 [('f', 'force', None, _('force a merge with outstanding changes')),
4602 4623 ('t', 'tool', '', _('specify merge tool')),
4603 4624 ('r', 'rev', '',
4604 4625 _('revision to merge'), _('REV')),
4605 4626 ('P', 'preview', None,
4606 4627 _('review revisions to merge (no merge is performed)'))],
4607 4628 _('[-P] [-f] [[-r] REV]')),
4608 4629 "outgoing|out":
4609 4630 (outgoing,
4610 4631 [('f', 'force', None,
4611 4632 _('run even when the destination is unrelated')),
4612 4633 ('r', 'rev', [],
4613 4634 _('a changeset intended to be included in the destination'),
4614 4635 _('REV')),
4615 4636 ('n', 'newest-first', None, _('show newest record first')),
4616 4637 ('B', 'bookmarks', False, _("compare bookmarks")),
4617 4638 ('b', 'branch', [],
4618 4639 _('a specific branch you would like to push'), _('BRANCH')),
4619 4640 ] + logopts + remoteopts + subrepoopts,
4620 4641 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4621 4642 "parents":
4622 4643 (parents,
4623 4644 [('r', 'rev', '',
4624 4645 _('show parents of the specified revision'), _('REV')),
4625 4646 ] + templateopts,
4626 4647 _('[-r REV] [FILE]')),
4627 4648 "paths": (paths, [], _('[NAME]')),
4628 4649 "^pull":
4629 4650 (pull,
4630 4651 [('u', 'update', None,
4631 4652 _('update to new branch head if changesets were pulled')),
4632 4653 ('f', 'force', None,
4633 4654 _('run even when remote repository is unrelated')),
4634 4655 ('r', 'rev', [],
4635 4656 _('a remote changeset intended to be added'), _('REV')),
4636 4657 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4637 4658 ('b', 'branch', [],
4638 4659 _('a specific branch you would like to pull'), _('BRANCH')),
4639 4660 ] + remoteopts,
4640 4661 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4641 4662 "^push":
4642 4663 (push,
4643 4664 [('f', 'force', None, _('force push')),
4644 4665 ('r', 'rev', [],
4645 4666 _('a changeset intended to be included in the destination'),
4646 4667 _('REV')),
4647 4668 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4648 4669 ('b', 'branch', [],
4649 4670 _('a specific branch you would like to push'), _('BRANCH')),
4650 4671 ('', 'new-branch', False, _('allow pushing a new branch')),
4651 4672 ] + remoteopts,
4652 4673 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4653 4674 "recover": (recover, []),
4654 4675 "^remove|rm":
4655 4676 (remove,
4656 4677 [('A', 'after', None, _('record delete for missing files')),
4657 4678 ('f', 'force', None,
4658 4679 _('remove (and delete) file even if added or modified')),
4659 4680 ] + walkopts,
4660 4681 _('[OPTION]... FILE...')),
4661 4682 "rename|move|mv":
4662 4683 (rename,
4663 4684 [('A', 'after', None, _('record a rename that has already occurred')),
4664 4685 ('f', 'force', None,
4665 4686 _('forcibly copy over an existing managed file')),
4666 4687 ] + walkopts + dryrunopts,
4667 4688 _('[OPTION]... SOURCE... DEST')),
4668 4689 "resolve":
4669 4690 (resolve,
4670 4691 [('a', 'all', None, _('select all unresolved files')),
4671 4692 ('l', 'list', None, _('list state of files needing merge')),
4672 4693 ('m', 'mark', None, _('mark files as resolved')),
4673 4694 ('u', 'unmark', None, _('mark files as unresolved')),
4674 4695 ('t', 'tool', '', _('specify merge tool')),
4675 4696 ('n', 'no-status', None, _('hide status prefix'))]
4676 4697 + walkopts,
4677 4698 _('[OPTION]... [FILE]...')),
4678 4699 "revert":
4679 4700 (revert,
4680 4701 [('a', 'all', None, _('revert all changes when no arguments given')),
4681 4702 ('d', 'date', '',
4682 4703 _('tipmost revision matching date'), _('DATE')),
4683 4704 ('r', 'rev', '',
4684 4705 _('revert to the specified revision'), _('REV')),
4685 4706 ('', 'no-backup', None, _('do not save backup copies of files')),
4686 4707 ] + walkopts + dryrunopts,
4687 4708 _('[OPTION]... [-r REV] [NAME]...')),
4688 4709 "rollback": (rollback, dryrunopts),
4689 4710 "root": (root, []),
4690 4711 "^serve":
4691 4712 (serve,
4692 4713 [('A', 'accesslog', '',
4693 4714 _('name of access log file to write to'), _('FILE')),
4694 4715 ('d', 'daemon', None, _('run server in background')),
4695 4716 ('', 'daemon-pipefds', '',
4696 4717 _('used internally by daemon mode'), _('NUM')),
4697 4718 ('E', 'errorlog', '',
4698 4719 _('name of error log file to write to'), _('FILE')),
4699 4720 # use string type, then we can check if something was passed
4700 4721 ('p', 'port', '',
4701 4722 _('port to listen on (default: 8000)'), _('PORT')),
4702 4723 ('a', 'address', '',
4703 4724 _('address to listen on (default: all interfaces)'), _('ADDR')),
4704 4725 ('', 'prefix', '',
4705 4726 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4706 4727 ('n', 'name', '',
4707 4728 _('name to show in web pages (default: working directory)'),
4708 4729 _('NAME')),
4709 4730 ('', 'web-conf', '',
4710 4731 _('name of the hgweb config file (see "hg help hgweb")'),
4711 4732 _('FILE')),
4712 4733 ('', 'webdir-conf', '',
4713 4734 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4714 4735 ('', 'pid-file', '',
4715 4736 _('name of file to write process ID to'), _('FILE')),
4716 4737 ('', 'stdio', None, _('for remote clients')),
4717 4738 ('t', 'templates', '',
4718 4739 _('web templates to use'), _('TEMPLATE')),
4719 4740 ('', 'style', '',
4720 4741 _('template style to use'), _('STYLE')),
4721 4742 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4722 4743 ('', 'certificate', '',
4723 4744 _('SSL certificate file'), _('FILE'))],
4724 4745 _('[OPTION]...')),
4725 4746 "showconfig|debugconfig":
4726 4747 (showconfig,
4727 4748 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4728 4749 _('[-u] [NAME]...')),
4729 4750 "^summary|sum":
4730 4751 (summary,
4731 4752 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4732 4753 "^status|st":
4733 4754 (status,
4734 4755 [('A', 'all', None, _('show status of all files')),
4735 4756 ('m', 'modified', None, _('show only modified files')),
4736 4757 ('a', 'added', None, _('show only added files')),
4737 4758 ('r', 'removed', None, _('show only removed files')),
4738 4759 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4739 4760 ('c', 'clean', None, _('show only files without changes')),
4740 4761 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4741 4762 ('i', 'ignored', None, _('show only ignored files')),
4742 4763 ('n', 'no-status', None, _('hide status prefix')),
4743 4764 ('C', 'copies', None, _('show source of copied files')),
4744 4765 ('0', 'print0', None,
4745 4766 _('end filenames with NUL, for use with xargs')),
4746 4767 ('', 'rev', [],
4747 4768 _('show difference from revision'), _('REV')),
4748 4769 ('', 'change', '',
4749 4770 _('list the changed files of a revision'), _('REV')),
4750 4771 ] + walkopts + subrepoopts,
4751 4772 _('[OPTION]... [FILE]...')),
4752 4773 "tag":
4753 4774 (tag,
4754 4775 [('f', 'force', None, _('force tag')),
4755 4776 ('l', 'local', None, _('make the tag local')),
4756 4777 ('r', 'rev', '',
4757 4778 _('revision to tag'), _('REV')),
4758 4779 ('', 'remove', None, _('remove a tag')),
4759 4780 # -l/--local is already there, commitopts cannot be used
4760 4781 ('e', 'edit', None, _('edit commit message')),
4761 4782 ('m', 'message', '',
4762 4783 _('use <text> as commit message'), _('TEXT')),
4763 4784 ] + commitopts2,
4764 4785 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4765 4786 "tags": (tags, [], ''),
4766 4787 "tip":
4767 4788 (tip,
4768 4789 [('p', 'patch', None, _('show patch')),
4769 4790 ('g', 'git', None, _('use git extended diff format')),
4770 4791 ] + templateopts,
4771 4792 _('[-p] [-g]')),
4772 4793 "unbundle":
4773 4794 (unbundle,
4774 4795 [('u', 'update', None,
4775 4796 _('update to new branch head if changesets were unbundled'))],
4776 4797 _('[-u] FILE...')),
4777 4798 "^update|up|checkout|co":
4778 4799 (update,
4779 4800 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4780 4801 ('c', 'check', None,
4781 4802 _('update across branches if no uncommitted changes')),
4782 4803 ('d', 'date', '',
4783 4804 _('tipmost revision matching date'), _('DATE')),
4784 4805 ('r', 'rev', '',
4785 4806 _('revision'), _('REV'))],
4786 4807 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4787 4808 "verify": (verify, []),
4788 4809 "version": (version_, []),
4789 4810 }
4790 4811
4791 4812 norepo = ("clone init version help debugcommands debugcomplete"
4792 " debugdate debuginstall debugfsinfo debugpushkey")
4813 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs")
4793 4814 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4794 4815 " debugdata debugindex debugindexdot")
@@ -1,1920 +1,1924 b''
1 1 # localrepo.py - read/write repository class 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 bin, hex, nullid, nullrev, short
9 9 from i18n import _
10 10 import repo, changegroup, subrepo, discovery, pushkey
11 11 import changelog, dirstate, filelog, manifest, context, bookmarks
12 12 import lock, transaction, store, encoding
13 13 import util, extensions, hook, error
14 14 import match as matchmod
15 15 import merge as mergemod
16 16 import tags as tagsmod
17 17 import url as urlmod
18 18 from lock import release
19 19 import weakref, errno, os, time, inspect
20 20 propertycache = util.propertycache
21 21
22 22 class localrepository(repo.repository):
23 23 capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey'))
24 24 supportedformats = set(('revlogv1', 'parentdelta'))
25 25 supported = supportedformats | set(('store', 'fncache', 'shared',
26 26 'dotencode'))
27 27
28 28 def __init__(self, baseui, path=None, create=0):
29 29 repo.repository.__init__(self)
30 30 self.root = os.path.realpath(util.expandpath(path))
31 31 self.path = os.path.join(self.root, ".hg")
32 32 self.origroot = path
33 33 self.auditor = util.path_auditor(self.root, self._checknested)
34 34 self.opener = util.opener(self.path)
35 35 self.wopener = util.opener(self.root)
36 36 self.baseui = baseui
37 37 self.ui = baseui.copy()
38 38
39 39 try:
40 40 self.ui.readconfig(self.join("hgrc"), self.root)
41 41 extensions.loadall(self.ui)
42 42 except IOError:
43 43 pass
44 44
45 45 if not os.path.isdir(self.path):
46 46 if create:
47 47 if not os.path.exists(path):
48 48 util.makedirs(path)
49 49 os.mkdir(self.path)
50 50 requirements = ["revlogv1"]
51 51 if self.ui.configbool('format', 'usestore', True):
52 52 os.mkdir(os.path.join(self.path, "store"))
53 53 requirements.append("store")
54 54 if self.ui.configbool('format', 'usefncache', True):
55 55 requirements.append("fncache")
56 56 if self.ui.configbool('format', 'dotencode', True):
57 57 requirements.append('dotencode')
58 58 # create an invalid changelog
59 59 self.opener("00changelog.i", "a").write(
60 60 '\0\0\0\2' # represents revlogv2
61 61 ' dummy changelog to prevent using the old repo layout'
62 62 )
63 63 if self.ui.configbool('format', 'parentdelta', False):
64 64 requirements.append("parentdelta")
65 65 else:
66 66 raise error.RepoError(_("repository %s not found") % path)
67 67 elif create:
68 68 raise error.RepoError(_("repository %s already exists") % path)
69 69 else:
70 70 # find requirements
71 71 requirements = set()
72 72 try:
73 73 requirements = set(self.opener("requires").read().splitlines())
74 74 except IOError, inst:
75 75 if inst.errno != errno.ENOENT:
76 76 raise
77 77 for r in requirements - self.supported:
78 78 raise error.RequirementError(
79 79 _("requirement '%s' not supported") % r)
80 80
81 81 self.sharedpath = self.path
82 82 try:
83 83 s = os.path.realpath(self.opener("sharedpath").read())
84 84 if not os.path.exists(s):
85 85 raise error.RepoError(
86 86 _('.hg/sharedpath points to nonexistent directory %s') % s)
87 87 self.sharedpath = s
88 88 except IOError, inst:
89 89 if inst.errno != errno.ENOENT:
90 90 raise
91 91
92 92 self.store = store.store(requirements, self.sharedpath, util.opener)
93 93 self.spath = self.store.path
94 94 self.sopener = self.store.opener
95 95 self.sjoin = self.store.join
96 96 self.opener.createmode = self.store.createmode
97 97 self._applyrequirements(requirements)
98 98 if create:
99 99 self._writerequirements()
100 100
101 101 # These two define the set of tags for this repository. _tags
102 102 # maps tag name to node; _tagtypes maps tag name to 'global' or
103 103 # 'local'. (Global tags are defined by .hgtags across all
104 104 # heads, and local tags are defined in .hg/localtags.) They
105 105 # constitute the in-memory cache of tags.
106 106 self._tags = None
107 107 self._tagtypes = None
108 108
109 109 self._branchcache = None
110 110 self._branchcachetip = None
111 111 self.nodetagscache = None
112 112 self.filterpats = {}
113 113 self._datafilters = {}
114 114 self._transref = self._lockref = self._wlockref = None
115 115
116 116 def _applyrequirements(self, requirements):
117 117 self.requirements = requirements
118 118 self.sopener.options = {}
119 119 if 'parentdelta' in requirements:
120 120 self.sopener.options['parentdelta'] = 1
121 121
122 122 def _writerequirements(self):
123 123 reqfile = self.opener("requires", "w")
124 124 for r in self.requirements:
125 125 reqfile.write("%s\n" % r)
126 126 reqfile.close()
127 127
128 128 def _checknested(self, path):
129 129 """Determine if path is a legal nested repository."""
130 130 if not path.startswith(self.root):
131 131 return False
132 132 subpath = path[len(self.root) + 1:]
133 133
134 134 # XXX: Checking against the current working copy is wrong in
135 135 # the sense that it can reject things like
136 136 #
137 137 # $ hg cat -r 10 sub/x.txt
138 138 #
139 139 # if sub/ is no longer a subrepository in the working copy
140 140 # parent revision.
141 141 #
142 142 # However, it can of course also allow things that would have
143 143 # been rejected before, such as the above cat command if sub/
144 144 # is a subrepository now, but was a normal directory before.
145 145 # The old path auditor would have rejected by mistake since it
146 146 # panics when it sees sub/.hg/.
147 147 #
148 148 # All in all, checking against the working copy seems sensible
149 149 # since we want to prevent access to nested repositories on
150 150 # the filesystem *now*.
151 151 ctx = self[None]
152 152 parts = util.splitpath(subpath)
153 153 while parts:
154 154 prefix = os.sep.join(parts)
155 155 if prefix in ctx.substate:
156 156 if prefix == subpath:
157 157 return True
158 158 else:
159 159 sub = ctx.sub(prefix)
160 160 return sub.checknested(subpath[len(prefix) + 1:])
161 161 else:
162 162 parts.pop()
163 163 return False
164 164
165 165 @util.propertycache
166 166 def _bookmarks(self):
167 167 return bookmarks.read(self)
168 168
169 169 @util.propertycache
170 170 def _bookmarkcurrent(self):
171 171 return bookmarks.readcurrent(self)
172 172
173 173 @propertycache
174 174 def changelog(self):
175 175 c = changelog.changelog(self.sopener)
176 176 if 'HG_PENDING' in os.environ:
177 177 p = os.environ['HG_PENDING']
178 178 if p.startswith(self.root):
179 179 c.readpending('00changelog.i.a')
180 180 self.sopener.options['defversion'] = c.version
181 181 return c
182 182
183 183 @propertycache
184 184 def manifest(self):
185 185 return manifest.manifest(self.sopener)
186 186
187 187 @propertycache
188 188 def dirstate(self):
189 189 warned = [0]
190 190 def validate(node):
191 191 try:
192 192 r = self.changelog.rev(node)
193 193 return node
194 194 except error.LookupError:
195 195 if not warned[0]:
196 196 warned[0] = True
197 197 self.ui.warn(_("warning: ignoring unknown"
198 198 " working parent %s!\n") % short(node))
199 199 return nullid
200 200
201 201 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
202 202
203 203 def __getitem__(self, changeid):
204 204 if changeid is None:
205 205 return context.workingctx(self)
206 206 return context.changectx(self, changeid)
207 207
208 208 def __contains__(self, changeid):
209 209 try:
210 210 return bool(self.lookup(changeid))
211 211 except error.RepoLookupError:
212 212 return False
213 213
214 214 def __nonzero__(self):
215 215 return True
216 216
217 217 def __len__(self):
218 218 return len(self.changelog)
219 219
220 220 def __iter__(self):
221 221 for i in xrange(len(self)):
222 222 yield i
223 223
224 224 def url(self):
225 225 return 'file:' + self.root
226 226
227 227 def hook(self, name, throw=False, **args):
228 228 return hook.hook(self.ui, self, name, throw, **args)
229 229
230 230 tag_disallowed = ':\r\n'
231 231
232 232 def _tag(self, names, node, message, local, user, date, extra={}):
233 233 if isinstance(names, str):
234 234 allchars = names
235 235 names = (names,)
236 236 else:
237 237 allchars = ''.join(names)
238 238 for c in self.tag_disallowed:
239 239 if c in allchars:
240 240 raise util.Abort(_('%r cannot be used in a tag name') % c)
241 241
242 242 branches = self.branchmap()
243 243 for name in names:
244 244 self.hook('pretag', throw=True, node=hex(node), tag=name,
245 245 local=local)
246 246 if name in branches:
247 247 self.ui.warn(_("warning: tag %s conflicts with existing"
248 248 " branch name\n") % name)
249 249
250 250 def writetags(fp, names, munge, prevtags):
251 251 fp.seek(0, 2)
252 252 if prevtags and prevtags[-1] != '\n':
253 253 fp.write('\n')
254 254 for name in names:
255 255 m = munge and munge(name) or name
256 256 if self._tagtypes and name in self._tagtypes:
257 257 old = self._tags.get(name, nullid)
258 258 fp.write('%s %s\n' % (hex(old), m))
259 259 fp.write('%s %s\n' % (hex(node), m))
260 260 fp.close()
261 261
262 262 prevtags = ''
263 263 if local:
264 264 try:
265 265 fp = self.opener('localtags', 'r+')
266 266 except IOError:
267 267 fp = self.opener('localtags', 'a')
268 268 else:
269 269 prevtags = fp.read()
270 270
271 271 # local tags are stored in the current charset
272 272 writetags(fp, names, None, prevtags)
273 273 for name in names:
274 274 self.hook('tag', node=hex(node), tag=name, local=local)
275 275 return
276 276
277 277 try:
278 278 fp = self.wfile('.hgtags', 'rb+')
279 279 except IOError:
280 280 fp = self.wfile('.hgtags', 'ab')
281 281 else:
282 282 prevtags = fp.read()
283 283
284 284 # committed tags are stored in UTF-8
285 285 writetags(fp, names, encoding.fromlocal, prevtags)
286 286
287 287 fp.close()
288 288
289 289 if '.hgtags' not in self.dirstate:
290 290 self[None].add(['.hgtags'])
291 291
292 292 m = matchmod.exact(self.root, '', ['.hgtags'])
293 293 tagnode = self.commit(message, user, date, extra=extra, match=m)
294 294
295 295 for name in names:
296 296 self.hook('tag', node=hex(node), tag=name, local=local)
297 297
298 298 return tagnode
299 299
300 300 def tag(self, names, node, message, local, user, date):
301 301 '''tag a revision with one or more symbolic names.
302 302
303 303 names is a list of strings or, when adding a single tag, names may be a
304 304 string.
305 305
306 306 if local is True, the tags are stored in a per-repository file.
307 307 otherwise, they are stored in the .hgtags file, and a new
308 308 changeset is committed with the change.
309 309
310 310 keyword arguments:
311 311
312 312 local: whether to store tags in non-version-controlled file
313 313 (default False)
314 314
315 315 message: commit message to use if committing
316 316
317 317 user: name of user to use if committing
318 318
319 319 date: date tuple to use if committing'''
320 320
321 321 if not local:
322 322 for x in self.status()[:5]:
323 323 if '.hgtags' in x:
324 324 raise util.Abort(_('working copy of .hgtags is changed '
325 325 '(please commit .hgtags manually)'))
326 326
327 327 self.tags() # instantiate the cache
328 328 self._tag(names, node, message, local, user, date)
329 329
330 330 def tags(self):
331 331 '''return a mapping of tag to node'''
332 332 if self._tags is None:
333 333 (self._tags, self._tagtypes) = self._findtags()
334 334
335 335 return self._tags
336 336
337 337 def _findtags(self):
338 338 '''Do the hard work of finding tags. Return a pair of dicts
339 339 (tags, tagtypes) where tags maps tag name to node, and tagtypes
340 340 maps tag name to a string like \'global\' or \'local\'.
341 341 Subclasses or extensions are free to add their own tags, but
342 342 should be aware that the returned dicts will be retained for the
343 343 duration of the localrepo object.'''
344 344
345 345 # XXX what tagtype should subclasses/extensions use? Currently
346 346 # mq and bookmarks add tags, but do not set the tagtype at all.
347 347 # Should each extension invent its own tag type? Should there
348 348 # be one tagtype for all such "virtual" tags? Or is the status
349 349 # quo fine?
350 350
351 351 alltags = {} # map tag name to (node, hist)
352 352 tagtypes = {}
353 353
354 354 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
355 355 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
356 356
357 357 # Build the return dicts. Have to re-encode tag names because
358 358 # the tags module always uses UTF-8 (in order not to lose info
359 359 # writing to the cache), but the rest of Mercurial wants them in
360 360 # local encoding.
361 361 tags = {}
362 362 for (name, (node, hist)) in alltags.iteritems():
363 363 if node != nullid:
364 364 tags[encoding.tolocal(name)] = node
365 365 tags['tip'] = self.changelog.tip()
366 366 tagtypes = dict([(encoding.tolocal(name), value)
367 367 for (name, value) in tagtypes.iteritems()])
368 368 return (tags, tagtypes)
369 369
370 370 def tagtype(self, tagname):
371 371 '''
372 372 return the type of the given tag. result can be:
373 373
374 374 'local' : a local tag
375 375 'global' : a global tag
376 376 None : tag does not exist
377 377 '''
378 378
379 379 self.tags()
380 380
381 381 return self._tagtypes.get(tagname)
382 382
383 383 def tagslist(self):
384 384 '''return a list of tags ordered by revision'''
385 385 l = []
386 386 for t, n in self.tags().iteritems():
387 387 try:
388 388 r = self.changelog.rev(n)
389 389 except:
390 390 r = -2 # sort to the beginning of the list if unknown
391 391 l.append((r, t, n))
392 392 return [(t, n) for r, t, n in sorted(l)]
393 393
394 394 def nodetags(self, node):
395 395 '''return the tags associated with a node'''
396 396 if not self.nodetagscache:
397 397 self.nodetagscache = {}
398 398 for t, n in self.tags().iteritems():
399 399 self.nodetagscache.setdefault(n, []).append(t)
400 400 for tags in self.nodetagscache.itervalues():
401 401 tags.sort()
402 402 return self.nodetagscache.get(node, [])
403 403
404 404 def nodebookmarks(self, node):
405 405 marks = []
406 406 for bookmark, n in self._bookmarks.iteritems():
407 407 if n == node:
408 408 marks.append(bookmark)
409 409 return sorted(marks)
410 410
411 411 def _branchtags(self, partial, lrev):
412 412 # TODO: rename this function?
413 413 tiprev = len(self) - 1
414 414 if lrev != tiprev:
415 415 ctxgen = (self[r] for r in xrange(lrev + 1, tiprev + 1))
416 416 self._updatebranchcache(partial, ctxgen)
417 417 self._writebranchcache(partial, self.changelog.tip(), tiprev)
418 418
419 419 return partial
420 420
421 421 def updatebranchcache(self):
422 422 tip = self.changelog.tip()
423 423 if self._branchcache is not None and self._branchcachetip == tip:
424 424 return self._branchcache
425 425
426 426 oldtip = self._branchcachetip
427 427 self._branchcachetip = tip
428 428 if oldtip is None or oldtip not in self.changelog.nodemap:
429 429 partial, last, lrev = self._readbranchcache()
430 430 else:
431 431 lrev = self.changelog.rev(oldtip)
432 432 partial = self._branchcache
433 433
434 434 self._branchtags(partial, lrev)
435 435 # this private cache holds all heads (not just tips)
436 436 self._branchcache = partial
437 437
438 438 def branchmap(self):
439 439 '''returns a dictionary {branch: [branchheads]}'''
440 440 self.updatebranchcache()
441 441 return self._branchcache
442 442
443 443 def branchtags(self):
444 444 '''return a dict where branch names map to the tipmost head of
445 445 the branch, open heads come before closed'''
446 446 bt = {}
447 447 for bn, heads in self.branchmap().iteritems():
448 448 tip = heads[-1]
449 449 for h in reversed(heads):
450 450 if 'close' not in self.changelog.read(h)[5]:
451 451 tip = h
452 452 break
453 453 bt[bn] = tip
454 454 return bt
455 455
456 456 def _readbranchcache(self):
457 457 partial = {}
458 458 try:
459 459 f = self.opener("cache/branchheads")
460 460 lines = f.read().split('\n')
461 461 f.close()
462 462 except (IOError, OSError):
463 463 return {}, nullid, nullrev
464 464
465 465 try:
466 466 last, lrev = lines.pop(0).split(" ", 1)
467 467 last, lrev = bin(last), int(lrev)
468 468 if lrev >= len(self) or self[lrev].node() != last:
469 469 # invalidate the cache
470 470 raise ValueError('invalidating branch cache (tip differs)')
471 471 for l in lines:
472 472 if not l:
473 473 continue
474 474 node, label = l.split(" ", 1)
475 475 label = encoding.tolocal(label.strip())
476 476 partial.setdefault(label, []).append(bin(node))
477 477 except KeyboardInterrupt:
478 478 raise
479 479 except Exception, inst:
480 480 if self.ui.debugflag:
481 481 self.ui.warn(str(inst), '\n')
482 482 partial, last, lrev = {}, nullid, nullrev
483 483 return partial, last, lrev
484 484
485 485 def _writebranchcache(self, branches, tip, tiprev):
486 486 try:
487 487 f = self.opener("cache/branchheads", "w", atomictemp=True)
488 488 f.write("%s %s\n" % (hex(tip), tiprev))
489 489 for label, nodes in branches.iteritems():
490 490 for node in nodes:
491 491 f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
492 492 f.rename()
493 493 except (IOError, OSError):
494 494 pass
495 495
496 496 def _updatebranchcache(self, partial, ctxgen):
497 497 # collect new branch entries
498 498 newbranches = {}
499 499 for c in ctxgen:
500 500 newbranches.setdefault(c.branch(), []).append(c.node())
501 501 # if older branchheads are reachable from new ones, they aren't
502 502 # really branchheads. Note checking parents is insufficient:
503 503 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
504 504 for branch, newnodes in newbranches.iteritems():
505 505 bheads = partial.setdefault(branch, [])
506 506 bheads.extend(newnodes)
507 507 if len(bheads) <= 1:
508 508 continue
509 509 # starting from tip means fewer passes over reachable
510 510 while newnodes:
511 511 latest = newnodes.pop()
512 512 if latest not in bheads:
513 513 continue
514 514 minbhrev = self[min([self[bh].rev() for bh in bheads])].node()
515 515 reachable = self.changelog.reachable(latest, minbhrev)
516 516 reachable.remove(latest)
517 517 bheads = [b for b in bheads if b not in reachable]
518 518 partial[branch] = bheads
519 519
520 520 def lookup(self, key):
521 521 if isinstance(key, int):
522 522 return self.changelog.node(key)
523 523 elif key == '.':
524 524 return self.dirstate.parents()[0]
525 525 elif key == 'null':
526 526 return nullid
527 527 elif key == 'tip':
528 528 return self.changelog.tip()
529 529 n = self.changelog._match(key)
530 530 if n:
531 531 return n
532 532 if key in self._bookmarks:
533 533 return self._bookmarks[key]
534 534 if key in self.tags():
535 535 return self.tags()[key]
536 536 if key in self.branchtags():
537 537 return self.branchtags()[key]
538 538 n = self.changelog._partialmatch(key)
539 539 if n:
540 540 return n
541 541
542 542 # can't find key, check if it might have come from damaged dirstate
543 543 if key in self.dirstate.parents():
544 544 raise error.Abort(_("working directory has unknown parent '%s'!")
545 545 % short(key))
546 546 try:
547 547 if len(key) == 20:
548 548 key = hex(key)
549 549 except:
550 550 pass
551 551 raise error.RepoLookupError(_("unknown revision '%s'") % key)
552 552
553 553 def lookupbranch(self, key, remote=None):
554 554 repo = remote or self
555 555 if key in repo.branchmap():
556 556 return key
557 557
558 558 repo = (remote and remote.local()) and remote or self
559 559 return repo[key].branch()
560 560
561 561 def local(self):
562 562 return True
563 563
564 564 def join(self, f):
565 565 return os.path.join(self.path, f)
566 566
567 567 def wjoin(self, f):
568 568 return os.path.join(self.root, f)
569 569
570 570 def file(self, f):
571 571 if f[0] == '/':
572 572 f = f[1:]
573 573 return filelog.filelog(self.sopener, f)
574 574
575 575 def changectx(self, changeid):
576 576 return self[changeid]
577 577
578 578 def parents(self, changeid=None):
579 579 '''get list of changectxs for parents of changeid'''
580 580 return self[changeid].parents()
581 581
582 582 def filectx(self, path, changeid=None, fileid=None):
583 583 """changeid can be a changeset revision, node, or tag.
584 584 fileid can be a file revision or node."""
585 585 return context.filectx(self, path, changeid, fileid)
586 586
587 587 def getcwd(self):
588 588 return self.dirstate.getcwd()
589 589
590 590 def pathto(self, f, cwd=None):
591 591 return self.dirstate.pathto(f, cwd)
592 592
593 593 def wfile(self, f, mode='r'):
594 594 return self.wopener(f, mode)
595 595
596 596 def _link(self, f):
597 597 return os.path.islink(self.wjoin(f))
598 598
599 599 def _loadfilter(self, filter):
600 600 if filter not in self.filterpats:
601 601 l = []
602 602 for pat, cmd in self.ui.configitems(filter):
603 603 if cmd == '!':
604 604 continue
605 605 mf = matchmod.match(self.root, '', [pat])
606 606 fn = None
607 607 params = cmd
608 608 for name, filterfn in self._datafilters.iteritems():
609 609 if cmd.startswith(name):
610 610 fn = filterfn
611 611 params = cmd[len(name):].lstrip()
612 612 break
613 613 if not fn:
614 614 fn = lambda s, c, **kwargs: util.filter(s, c)
615 615 # Wrap old filters not supporting keyword arguments
616 616 if not inspect.getargspec(fn)[2]:
617 617 oldfn = fn
618 618 fn = lambda s, c, **kwargs: oldfn(s, c)
619 619 l.append((mf, fn, params))
620 620 self.filterpats[filter] = l
621 621 return self.filterpats[filter]
622 622
623 623 def _filter(self, filterpats, filename, data):
624 624 for mf, fn, cmd in filterpats:
625 625 if mf(filename):
626 626 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
627 627 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
628 628 break
629 629
630 630 return data
631 631
632 632 @propertycache
633 633 def _encodefilterpats(self):
634 634 return self._loadfilter('encode')
635 635
636 636 @propertycache
637 637 def _decodefilterpats(self):
638 638 return self._loadfilter('decode')
639 639
640 640 def adddatafilter(self, name, filter):
641 641 self._datafilters[name] = filter
642 642
643 643 def wread(self, filename):
644 644 if self._link(filename):
645 645 data = os.readlink(self.wjoin(filename))
646 646 else:
647 647 data = self.wopener(filename, 'r').read()
648 648 return self._filter(self._encodefilterpats, filename, data)
649 649
650 650 def wwrite(self, filename, data, flags):
651 651 data = self._filter(self._decodefilterpats, filename, data)
652 652 if 'l' in flags:
653 653 self.wopener.symlink(data, filename)
654 654 else:
655 655 self.wopener(filename, 'w').write(data)
656 656 if 'x' in flags:
657 657 util.set_flags(self.wjoin(filename), False, True)
658 658
659 659 def wwritedata(self, filename, data):
660 660 return self._filter(self._decodefilterpats, filename, data)
661 661
662 662 def transaction(self, desc):
663 663 tr = self._transref and self._transref() or None
664 664 if tr and tr.running():
665 665 return tr.nest()
666 666
667 667 # abort here if the journal already exists
668 668 if os.path.exists(self.sjoin("journal")):
669 669 raise error.RepoError(
670 670 _("abandoned transaction found - run hg recover"))
671 671
672 672 # save dirstate for rollback
673 673 try:
674 674 ds = self.opener("dirstate").read()
675 675 except IOError:
676 676 ds = ""
677 677 self.opener("journal.dirstate", "w").write(ds)
678 678 self.opener("journal.branch", "w").write(
679 679 encoding.fromlocal(self.dirstate.branch()))
680 680 self.opener("journal.desc", "w").write("%d\n%s\n" % (len(self), desc))
681 681
682 682 renames = [(self.sjoin("journal"), self.sjoin("undo")),
683 683 (self.join("journal.dirstate"), self.join("undo.dirstate")),
684 684 (self.join("journal.branch"), self.join("undo.branch")),
685 685 (self.join("journal.desc"), self.join("undo.desc"))]
686 686 tr = transaction.transaction(self.ui.warn, self.sopener,
687 687 self.sjoin("journal"),
688 688 aftertrans(renames),
689 689 self.store.createmode)
690 690 self._transref = weakref.ref(tr)
691 691 return tr
692 692
693 693 def recover(self):
694 694 lock = self.lock()
695 695 try:
696 696 if os.path.exists(self.sjoin("journal")):
697 697 self.ui.status(_("rolling back interrupted transaction\n"))
698 698 transaction.rollback(self.sopener, self.sjoin("journal"),
699 699 self.ui.warn)
700 700 self.invalidate()
701 701 return True
702 702 else:
703 703 self.ui.warn(_("no interrupted transaction available\n"))
704 704 return False
705 705 finally:
706 706 lock.release()
707 707
708 708 def rollback(self, dryrun=False):
709 709 wlock = lock = None
710 710 try:
711 711 wlock = self.wlock()
712 712 lock = self.lock()
713 713 if os.path.exists(self.sjoin("undo")):
714 714 try:
715 715 args = self.opener("undo.desc", "r").read().splitlines()
716 716 if len(args) >= 3 and self.ui.verbose:
717 717 desc = _("repository tip rolled back to revision %s"
718 718 " (undo %s: %s)\n") % (
719 719 int(args[0]) - 1, args[1], args[2])
720 720 elif len(args) >= 2:
721 721 desc = _("repository tip rolled back to revision %s"
722 722 " (undo %s)\n") % (
723 723 int(args[0]) - 1, args[1])
724 724 except IOError:
725 725 desc = _("rolling back unknown transaction\n")
726 726 self.ui.status(desc)
727 727 if dryrun:
728 728 return
729 729 transaction.rollback(self.sopener, self.sjoin("undo"),
730 730 self.ui.warn)
731 731 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
732 732 if os.path.exists(self.join('undo.bookmarks')):
733 733 util.rename(self.join('undo.bookmarks'),
734 734 self.join('bookmarks'))
735 735 try:
736 736 branch = self.opener("undo.branch").read()
737 737 self.dirstate.setbranch(branch)
738 738 except IOError:
739 739 self.ui.warn(_("Named branch could not be reset, "
740 740 "current branch still is: %s\n")
741 741 % self.dirstate.branch())
742 742 self.invalidate()
743 743 self.dirstate.invalidate()
744 744 self.destroyed()
745 745 parents = tuple([p.rev() for p in self.parents()])
746 746 if len(parents) > 1:
747 747 self.ui.status(_("working directory now based on "
748 748 "revisions %d and %d\n") % parents)
749 749 else:
750 750 self.ui.status(_("working directory now based on "
751 751 "revision %d\n") % parents)
752 752 else:
753 753 self.ui.warn(_("no rollback information available\n"))
754 754 return 1
755 755 finally:
756 756 release(lock, wlock)
757 757
758 758 def invalidatecaches(self):
759 759 self._tags = None
760 760 self._tagtypes = None
761 761 self.nodetagscache = None
762 762 self._branchcache = None # in UTF-8
763 763 self._branchcachetip = None
764 764
765 765 def invalidate(self):
766 766 for a in ("changelog", "manifest", "_bookmarks", "_bookmarkcurrent"):
767 767 if a in self.__dict__:
768 768 delattr(self, a)
769 769 self.invalidatecaches()
770 770
771 771 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
772 772 try:
773 773 l = lock.lock(lockname, 0, releasefn, desc=desc)
774 774 except error.LockHeld, inst:
775 775 if not wait:
776 776 raise
777 777 self.ui.warn(_("waiting for lock on %s held by %r\n") %
778 778 (desc, inst.locker))
779 779 # default to 600 seconds timeout
780 780 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
781 781 releasefn, desc=desc)
782 782 if acquirefn:
783 783 acquirefn()
784 784 return l
785 785
786 786 def lock(self, wait=True):
787 787 '''Lock the repository store (.hg/store) and return a weak reference
788 788 to the lock. Use this before modifying the store (e.g. committing or
789 789 stripping). If you are opening a transaction, get a lock as well.)'''
790 790 l = self._lockref and self._lockref()
791 791 if l is not None and l.held:
792 792 l.lock()
793 793 return l
794 794
795 795 l = self._lock(self.sjoin("lock"), wait, self.store.write,
796 796 self.invalidate, _('repository %s') % self.origroot)
797 797 self._lockref = weakref.ref(l)
798 798 return l
799 799
800 800 def wlock(self, wait=True):
801 801 '''Lock the non-store parts of the repository (everything under
802 802 .hg except .hg/store) and return a weak reference to the lock.
803 803 Use this before modifying files in .hg.'''
804 804 l = self._wlockref and self._wlockref()
805 805 if l is not None and l.held:
806 806 l.lock()
807 807 return l
808 808
809 809 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
810 810 self.dirstate.invalidate, _('working directory of %s') %
811 811 self.origroot)
812 812 self._wlockref = weakref.ref(l)
813 813 return l
814 814
815 815 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
816 816 """
817 817 commit an individual file as part of a larger transaction
818 818 """
819 819
820 820 fname = fctx.path()
821 821 text = fctx.data()
822 822 flog = self.file(fname)
823 823 fparent1 = manifest1.get(fname, nullid)
824 824 fparent2 = fparent2o = manifest2.get(fname, nullid)
825 825
826 826 meta = {}
827 827 copy = fctx.renamed()
828 828 if copy and copy[0] != fname:
829 829 # Mark the new revision of this file as a copy of another
830 830 # file. This copy data will effectively act as a parent
831 831 # of this new revision. If this is a merge, the first
832 832 # parent will be the nullid (meaning "look up the copy data")
833 833 # and the second one will be the other parent. For example:
834 834 #
835 835 # 0 --- 1 --- 3 rev1 changes file foo
836 836 # \ / rev2 renames foo to bar and changes it
837 837 # \- 2 -/ rev3 should have bar with all changes and
838 838 # should record that bar descends from
839 839 # bar in rev2 and foo in rev1
840 840 #
841 841 # this allows this merge to succeed:
842 842 #
843 843 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
844 844 # \ / merging rev3 and rev4 should use bar@rev2
845 845 # \- 2 --- 4 as the merge base
846 846 #
847 847
848 848 cfname = copy[0]
849 849 crev = manifest1.get(cfname)
850 850 newfparent = fparent2
851 851
852 852 if manifest2: # branch merge
853 853 if fparent2 == nullid or crev is None: # copied on remote side
854 854 if cfname in manifest2:
855 855 crev = manifest2[cfname]
856 856 newfparent = fparent1
857 857
858 858 # find source in nearest ancestor if we've lost track
859 859 if not crev:
860 860 self.ui.debug(" %s: searching for copy revision for %s\n" %
861 861 (fname, cfname))
862 862 for ancestor in self[None].ancestors():
863 863 if cfname in ancestor:
864 864 crev = ancestor[cfname].filenode()
865 865 break
866 866
867 867 if crev:
868 868 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
869 869 meta["copy"] = cfname
870 870 meta["copyrev"] = hex(crev)
871 871 fparent1, fparent2 = nullid, newfparent
872 872 else:
873 873 self.ui.warn(_("warning: can't find ancestor for '%s' "
874 874 "copied from '%s'!\n") % (fname, cfname))
875 875
876 876 elif fparent2 != nullid:
877 877 # is one parent an ancestor of the other?
878 878 fparentancestor = flog.ancestor(fparent1, fparent2)
879 879 if fparentancestor == fparent1:
880 880 fparent1, fparent2 = fparent2, nullid
881 881 elif fparentancestor == fparent2:
882 882 fparent2 = nullid
883 883
884 884 # is the file changed?
885 885 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
886 886 changelist.append(fname)
887 887 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
888 888
889 889 # are just the flags changed during merge?
890 890 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
891 891 changelist.append(fname)
892 892
893 893 return fparent1
894 894
895 895 def commit(self, text="", user=None, date=None, match=None, force=False,
896 896 editor=False, extra={}):
897 897 """Add a new revision to current repository.
898 898
899 899 Revision information is gathered from the working directory,
900 900 match can be used to filter the committed files. If editor is
901 901 supplied, it is called to get a commit message.
902 902 """
903 903
904 904 def fail(f, msg):
905 905 raise util.Abort('%s: %s' % (f, msg))
906 906
907 907 if not match:
908 908 match = matchmod.always(self.root, '')
909 909
910 910 if not force:
911 911 vdirs = []
912 912 match.dir = vdirs.append
913 913 match.bad = fail
914 914
915 915 wlock = self.wlock()
916 916 try:
917 917 wctx = self[None]
918 918 merge = len(wctx.parents()) > 1
919 919
920 920 if (not force and merge and match and
921 921 (match.files() or match.anypats())):
922 922 raise util.Abort(_('cannot partially commit a merge '
923 923 '(do not specify files or patterns)'))
924 924
925 925 changes = self.status(match=match, clean=force)
926 926 if force:
927 927 changes[0].extend(changes[6]) # mq may commit unchanged files
928 928
929 929 # check subrepos
930 930 subs = []
931 931 removedsubs = set()
932 932 for p in wctx.parents():
933 933 removedsubs.update(s for s in p.substate if match(s))
934 934 for s in wctx.substate:
935 935 removedsubs.discard(s)
936 936 if match(s) and wctx.sub(s).dirty():
937 937 subs.append(s)
938 938 if (subs or removedsubs):
939 939 if (not match('.hgsub') and
940 940 '.hgsub' in (wctx.modified() + wctx.added())):
941 941 raise util.Abort(_("can't commit subrepos without .hgsub"))
942 942 if '.hgsubstate' not in changes[0]:
943 943 changes[0].insert(0, '.hgsubstate')
944 944
945 945 if subs and not self.ui.configbool('ui', 'commitsubrepos', True):
946 946 changedsubs = [s for s in subs if wctx.sub(s).dirty(True)]
947 947 if changedsubs:
948 948 raise util.Abort(_("uncommitted changes in subrepo %s")
949 949 % changedsubs[0])
950 950
951 951 # make sure all explicit patterns are matched
952 952 if not force and match.files():
953 953 matched = set(changes[0] + changes[1] + changes[2])
954 954
955 955 for f in match.files():
956 956 if f == '.' or f in matched or f in wctx.substate:
957 957 continue
958 958 if f in changes[3]: # missing
959 959 fail(f, _('file not found!'))
960 960 if f in vdirs: # visited directory
961 961 d = f + '/'
962 962 for mf in matched:
963 963 if mf.startswith(d):
964 964 break
965 965 else:
966 966 fail(f, _("no match under directory!"))
967 967 elif f not in self.dirstate:
968 968 fail(f, _("file not tracked!"))
969 969
970 970 if (not force and not extra.get("close") and not merge
971 971 and not (changes[0] or changes[1] or changes[2])
972 972 and wctx.branch() == wctx.p1().branch()):
973 973 return None
974 974
975 975 ms = mergemod.mergestate(self)
976 976 for f in changes[0]:
977 977 if f in ms and ms[f] == 'u':
978 978 raise util.Abort(_("unresolved merge conflicts "
979 979 "(see hg help resolve)"))
980 980
981 981 cctx = context.workingctx(self, text, user, date, extra, changes)
982 982 if editor:
983 983 cctx._text = editor(self, cctx, subs)
984 984 edited = (text != cctx._text)
985 985
986 986 # commit subs
987 987 if subs or removedsubs:
988 988 state = wctx.substate.copy()
989 989 for s in sorted(subs):
990 990 sub = wctx.sub(s)
991 991 self.ui.status(_('committing subrepository %s\n') %
992 992 subrepo.subrelpath(sub))
993 993 sr = sub.commit(cctx._text, user, date)
994 994 state[s] = (state[s][0], sr)
995 995 subrepo.writestate(self, state)
996 996
997 997 # Save commit message in case this transaction gets rolled back
998 998 # (e.g. by a pretxncommit hook). Leave the content alone on
999 999 # the assumption that the user will use the same editor again.
1000 1000 msgfile = self.opener('last-message.txt', 'wb')
1001 1001 msgfile.write(cctx._text)
1002 1002 msgfile.close()
1003 1003
1004 1004 p1, p2 = self.dirstate.parents()
1005 1005 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1006 1006 try:
1007 1007 self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
1008 1008 ret = self.commitctx(cctx, True)
1009 1009 except:
1010 1010 if edited:
1011 1011 msgfn = self.pathto(msgfile.name[len(self.root)+1:])
1012 1012 self.ui.write(
1013 1013 _('note: commit message saved in %s\n') % msgfn)
1014 1014 raise
1015 1015
1016 1016 # update bookmarks, dirstate and mergestate
1017 1017 parents = (p1, p2)
1018 1018 if p2 == nullid:
1019 1019 parents = (p1,)
1020 1020 bookmarks.update(self, parents, ret)
1021 1021 for f in changes[0] + changes[1]:
1022 1022 self.dirstate.normal(f)
1023 1023 for f in changes[2]:
1024 1024 self.dirstate.forget(f)
1025 1025 self.dirstate.setparents(ret)
1026 1026 ms.reset()
1027 1027 finally:
1028 1028 wlock.release()
1029 1029
1030 1030 self.hook("commit", node=hex(ret), parent1=hookp1, parent2=hookp2)
1031 1031 return ret
1032 1032
1033 1033 def commitctx(self, ctx, error=False):
1034 1034 """Add a new revision to current repository.
1035 1035 Revision information is passed via the context argument.
1036 1036 """
1037 1037
1038 1038 tr = lock = None
1039 1039 removed = list(ctx.removed())
1040 1040 p1, p2 = ctx.p1(), ctx.p2()
1041 1041 m1 = p1.manifest().copy()
1042 1042 m2 = p2.manifest()
1043 1043 user = ctx.user()
1044 1044
1045 1045 lock = self.lock()
1046 1046 try:
1047 1047 tr = self.transaction("commit")
1048 1048 trp = weakref.proxy(tr)
1049 1049
1050 1050 # check in files
1051 1051 new = {}
1052 1052 changed = []
1053 1053 linkrev = len(self)
1054 1054 for f in sorted(ctx.modified() + ctx.added()):
1055 1055 self.ui.note(f + "\n")
1056 1056 try:
1057 1057 fctx = ctx[f]
1058 1058 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1059 1059 changed)
1060 1060 m1.set(f, fctx.flags())
1061 1061 except OSError, inst:
1062 1062 self.ui.warn(_("trouble committing %s!\n") % f)
1063 1063 raise
1064 1064 except IOError, inst:
1065 1065 errcode = getattr(inst, 'errno', errno.ENOENT)
1066 1066 if error or errcode and errcode != errno.ENOENT:
1067 1067 self.ui.warn(_("trouble committing %s!\n") % f)
1068 1068 raise
1069 1069 else:
1070 1070 removed.append(f)
1071 1071
1072 1072 # update manifest
1073 1073 m1.update(new)
1074 1074 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1075 1075 drop = [f for f in removed if f in m1]
1076 1076 for f in drop:
1077 1077 del m1[f]
1078 1078 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1079 1079 p2.manifestnode(), (new, drop))
1080 1080
1081 1081 # update changelog
1082 1082 self.changelog.delayupdate()
1083 1083 n = self.changelog.add(mn, changed + removed, ctx.description(),
1084 1084 trp, p1.node(), p2.node(),
1085 1085 user, ctx.date(), ctx.extra().copy())
1086 1086 p = lambda: self.changelog.writepending() and self.root or ""
1087 1087 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1088 1088 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1089 1089 parent2=xp2, pending=p)
1090 1090 self.changelog.finalize(trp)
1091 1091 tr.close()
1092 1092
1093 1093 if self._branchcache:
1094 1094 self.updatebranchcache()
1095 1095 return n
1096 1096 finally:
1097 1097 if tr:
1098 1098 tr.release()
1099 1099 lock.release()
1100 1100
1101 1101 def destroyed(self):
1102 1102 '''Inform the repository that nodes have been destroyed.
1103 1103 Intended for use by strip and rollback, so there's a common
1104 1104 place for anything that has to be done after destroying history.'''
1105 1105 # XXX it might be nice if we could take the list of destroyed
1106 1106 # nodes, but I don't see an easy way for rollback() to do that
1107 1107
1108 1108 # Ensure the persistent tag cache is updated. Doing it now
1109 1109 # means that the tag cache only has to worry about destroyed
1110 1110 # heads immediately after a strip/rollback. That in turn
1111 1111 # guarantees that "cachetip == currenttip" (comparing both rev
1112 1112 # and node) always means no nodes have been added or destroyed.
1113 1113
1114 1114 # XXX this is suboptimal when qrefresh'ing: we strip the current
1115 1115 # head, refresh the tag cache, then immediately add a new head.
1116 1116 # But I think doing it this way is necessary for the "instant
1117 1117 # tag cache retrieval" case to work.
1118 1118 self.invalidatecaches()
1119 1119
1120 1120 def walk(self, match, node=None):
1121 1121 '''
1122 1122 walk recursively through the directory tree or a given
1123 1123 changeset, finding all files matched by the match
1124 1124 function
1125 1125 '''
1126 1126 return self[node].walk(match)
1127 1127
1128 1128 def status(self, node1='.', node2=None, match=None,
1129 1129 ignored=False, clean=False, unknown=False,
1130 1130 listsubrepos=False):
1131 1131 """return status of files between two nodes or node and working directory
1132 1132
1133 1133 If node1 is None, use the first dirstate parent instead.
1134 1134 If node2 is None, compare node1 with working directory.
1135 1135 """
1136 1136
1137 1137 def mfmatches(ctx):
1138 1138 mf = ctx.manifest().copy()
1139 1139 for fn in mf.keys():
1140 1140 if not match(fn):
1141 1141 del mf[fn]
1142 1142 return mf
1143 1143
1144 1144 if isinstance(node1, context.changectx):
1145 1145 ctx1 = node1
1146 1146 else:
1147 1147 ctx1 = self[node1]
1148 1148 if isinstance(node2, context.changectx):
1149 1149 ctx2 = node2
1150 1150 else:
1151 1151 ctx2 = self[node2]
1152 1152
1153 1153 working = ctx2.rev() is None
1154 1154 parentworking = working and ctx1 == self['.']
1155 1155 match = match or matchmod.always(self.root, self.getcwd())
1156 1156 listignored, listclean, listunknown = ignored, clean, unknown
1157 1157
1158 1158 # load earliest manifest first for caching reasons
1159 1159 if not working and ctx2.rev() < ctx1.rev():
1160 1160 ctx2.manifest()
1161 1161
1162 1162 if not parentworking:
1163 1163 def bad(f, msg):
1164 1164 if f not in ctx1:
1165 1165 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1166 1166 match.bad = bad
1167 1167
1168 1168 if working: # we need to scan the working dir
1169 1169 subrepos = []
1170 1170 if '.hgsub' in self.dirstate:
1171 1171 subrepos = ctx1.substate.keys()
1172 1172 s = self.dirstate.status(match, subrepos, listignored,
1173 1173 listclean, listunknown)
1174 1174 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1175 1175
1176 1176 # check for any possibly clean files
1177 1177 if parentworking and cmp:
1178 1178 fixup = []
1179 1179 # do a full compare of any files that might have changed
1180 1180 for f in sorted(cmp):
1181 1181 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1182 1182 or ctx1[f].cmp(ctx2[f])):
1183 1183 modified.append(f)
1184 1184 else:
1185 1185 fixup.append(f)
1186 1186
1187 1187 # update dirstate for files that are actually clean
1188 1188 if fixup:
1189 1189 if listclean:
1190 1190 clean += fixup
1191 1191
1192 1192 try:
1193 1193 # updating the dirstate is optional
1194 1194 # so we don't wait on the lock
1195 1195 wlock = self.wlock(False)
1196 1196 try:
1197 1197 for f in fixup:
1198 1198 self.dirstate.normal(f)
1199 1199 finally:
1200 1200 wlock.release()
1201 1201 except error.LockError:
1202 1202 pass
1203 1203
1204 1204 if not parentworking:
1205 1205 mf1 = mfmatches(ctx1)
1206 1206 if working:
1207 1207 # we are comparing working dir against non-parent
1208 1208 # generate a pseudo-manifest for the working dir
1209 1209 mf2 = mfmatches(self['.'])
1210 1210 for f in cmp + modified + added:
1211 1211 mf2[f] = None
1212 1212 mf2.set(f, ctx2.flags(f))
1213 1213 for f in removed:
1214 1214 if f in mf2:
1215 1215 del mf2[f]
1216 1216 else:
1217 1217 # we are comparing two revisions
1218 1218 deleted, unknown, ignored = [], [], []
1219 1219 mf2 = mfmatches(ctx2)
1220 1220
1221 1221 modified, added, clean = [], [], []
1222 1222 for fn in mf2:
1223 1223 if fn in mf1:
1224 1224 if (mf1.flags(fn) != mf2.flags(fn) or
1225 1225 (mf1[fn] != mf2[fn] and
1226 1226 (mf2[fn] or ctx1[fn].cmp(ctx2[fn])))):
1227 1227 modified.append(fn)
1228 1228 elif listclean:
1229 1229 clean.append(fn)
1230 1230 del mf1[fn]
1231 1231 else:
1232 1232 added.append(fn)
1233 1233 removed = mf1.keys()
1234 1234
1235 1235 r = modified, added, removed, deleted, unknown, ignored, clean
1236 1236
1237 1237 if listsubrepos:
1238 1238 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1239 1239 if working:
1240 1240 rev2 = None
1241 1241 else:
1242 1242 rev2 = ctx2.substate[subpath][1]
1243 1243 try:
1244 1244 submatch = matchmod.narrowmatcher(subpath, match)
1245 1245 s = sub.status(rev2, match=submatch, ignored=listignored,
1246 1246 clean=listclean, unknown=listunknown,
1247 1247 listsubrepos=True)
1248 1248 for rfiles, sfiles in zip(r, s):
1249 1249 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1250 1250 except error.LookupError:
1251 1251 self.ui.status(_("skipping missing subrepository: %s\n")
1252 1252 % subpath)
1253 1253
1254 1254 for l in r:
1255 1255 l.sort()
1256 1256 return r
1257 1257
1258 1258 def heads(self, start=None):
1259 1259 heads = self.changelog.heads(start)
1260 1260 # sort the output in rev descending order
1261 1261 return sorted(heads, key=self.changelog.rev, reverse=True)
1262 1262
1263 1263 def branchheads(self, branch=None, start=None, closed=False):
1264 1264 '''return a (possibly filtered) list of heads for the given branch
1265 1265
1266 1266 Heads are returned in topological order, from newest to oldest.
1267 1267 If branch is None, use the dirstate branch.
1268 1268 If start is not None, return only heads reachable from start.
1269 1269 If closed is True, return heads that are marked as closed as well.
1270 1270 '''
1271 1271 if branch is None:
1272 1272 branch = self[None].branch()
1273 1273 branches = self.branchmap()
1274 1274 if branch not in branches:
1275 1275 return []
1276 1276 # the cache returns heads ordered lowest to highest
1277 1277 bheads = list(reversed(branches[branch]))
1278 1278 if start is not None:
1279 1279 # filter out the heads that cannot be reached from startrev
1280 1280 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1281 1281 bheads = [h for h in bheads if h in fbheads]
1282 1282 if not closed:
1283 1283 bheads = [h for h in bheads if
1284 1284 ('close' not in self.changelog.read(h)[5])]
1285 1285 return bheads
1286 1286
1287 1287 def branches(self, nodes):
1288 1288 if not nodes:
1289 1289 nodes = [self.changelog.tip()]
1290 1290 b = []
1291 1291 for n in nodes:
1292 1292 t = n
1293 1293 while 1:
1294 1294 p = self.changelog.parents(n)
1295 1295 if p[1] != nullid or p[0] == nullid:
1296 1296 b.append((t, n, p[0], p[1]))
1297 1297 break
1298 1298 n = p[0]
1299 1299 return b
1300 1300
1301 1301 def between(self, pairs):
1302 1302 r = []
1303 1303
1304 1304 for top, bottom in pairs:
1305 1305 n, l, i = top, [], 0
1306 1306 f = 1
1307 1307
1308 1308 while n != bottom and n != nullid:
1309 1309 p = self.changelog.parents(n)[0]
1310 1310 if i == f:
1311 1311 l.append(n)
1312 1312 f = f * 2
1313 1313 n = p
1314 1314 i += 1
1315 1315
1316 1316 r.append(l)
1317 1317
1318 1318 return r
1319 1319
1320 1320 def pull(self, remote, heads=None, force=False):
1321 1321 lock = self.lock()
1322 1322 try:
1323 1323 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1324 1324 force=force)
1325 1325 common, fetch, rheads = tmp
1326 1326 if not fetch:
1327 1327 self.ui.status(_("no changes found\n"))
1328 1328 result = 0
1329 1329 else:
1330 1330 if heads is None and fetch == [nullid]:
1331 1331 self.ui.status(_("requesting all changes\n"))
1332 1332 elif heads is None and remote.capable('changegroupsubset'):
1333 1333 # issue1320, avoid a race if remote changed after discovery
1334 1334 heads = rheads
1335 1335
1336 1336 if heads is None:
1337 1337 cg = remote.changegroup(fetch, 'pull')
1338 1338 elif not remote.capable('changegroupsubset'):
1339 1339 raise util.Abort(_("partial pull cannot be done because "
1340 1340 "other repository doesn't support "
1341 1341 "changegroupsubset."))
1342 1342 else:
1343 1343 cg = remote.changegroupsubset(fetch, heads, 'pull')
1344 1344 result = self.addchangegroup(cg, 'pull', remote.url(),
1345 1345 lock=lock)
1346 1346 finally:
1347 1347 lock.release()
1348 1348
1349 1349 return result
1350 1350
1351 1351 def checkpush(self, force, revs):
1352 1352 """Extensions can override this function if additional checks have
1353 1353 to be performed before pushing, or call it if they override push
1354 1354 command.
1355 1355 """
1356 1356 pass
1357 1357
1358 1358 def push(self, remote, force=False, revs=None, newbranch=False):
1359 1359 '''Push outgoing changesets (limited by revs) from the current
1360 1360 repository to remote. Return an integer:
1361 1361 - 0 means HTTP error *or* nothing to push
1362 1362 - 1 means we pushed and remote head count is unchanged *or*
1363 1363 we have outgoing changesets but refused to push
1364 1364 - other values as described by addchangegroup()
1365 1365 '''
1366 1366 # there are two ways to push to remote repo:
1367 1367 #
1368 1368 # addchangegroup assumes local user can lock remote
1369 1369 # repo (local filesystem, old ssh servers).
1370 1370 #
1371 1371 # unbundle assumes local user cannot lock remote repo (new ssh
1372 1372 # servers, http servers).
1373 1373
1374 1374 self.checkpush(force, revs)
1375 1375 lock = None
1376 1376 unbundle = remote.capable('unbundle')
1377 1377 if not unbundle:
1378 1378 lock = remote.lock()
1379 1379 try:
1380 1380 cg, remote_heads = discovery.prepush(self, remote, force, revs,
1381 1381 newbranch)
1382 1382 ret = remote_heads
1383 1383 if cg is not None:
1384 1384 if unbundle:
1385 1385 # local repo finds heads on server, finds out what
1386 1386 # revs it must push. once revs transferred, if server
1387 1387 # finds it has different heads (someone else won
1388 1388 # commit/push race), server aborts.
1389 1389 if force:
1390 1390 remote_heads = ['force']
1391 1391 # ssh: return remote's addchangegroup()
1392 1392 # http: return remote's addchangegroup() or 0 for error
1393 1393 ret = remote.unbundle(cg, remote_heads, 'push')
1394 1394 else:
1395 1395 # we return an integer indicating remote head count change
1396 1396 ret = remote.addchangegroup(cg, 'push', self.url(),
1397 1397 lock=lock)
1398 1398 finally:
1399 1399 if lock is not None:
1400 1400 lock.release()
1401 1401
1402 1402 self.ui.debug("checking for updated bookmarks\n")
1403 1403 rb = remote.listkeys('bookmarks')
1404 1404 for k in rb.keys():
1405 1405 if k in self._bookmarks:
1406 1406 nr, nl = rb[k], hex(self._bookmarks[k])
1407 1407 if nr in self:
1408 1408 cr = self[nr]
1409 1409 cl = self[nl]
1410 1410 if cl in cr.descendants():
1411 1411 r = remote.pushkey('bookmarks', k, nr, nl)
1412 1412 if r:
1413 1413 self.ui.status(_("updating bookmark %s\n") % k)
1414 1414 else:
1415 1415 self.ui.warn(_('updating bookmark %s'
1416 1416 ' failed!\n') % k)
1417 1417
1418 1418 return ret
1419 1419
1420 1420 def changegroupinfo(self, nodes, source):
1421 1421 if self.ui.verbose or source == 'bundle':
1422 1422 self.ui.status(_("%d changesets found\n") % len(nodes))
1423 1423 if self.ui.debugflag:
1424 1424 self.ui.debug("list of changesets:\n")
1425 1425 for node in nodes:
1426 1426 self.ui.debug("%s\n" % hex(node))
1427 1427
1428 1428 def changegroupsubset(self, bases, heads, source):
1429 1429 """Compute a changegroup consisting of all the nodes that are
1430 1430 descendents of any of the bases and ancestors of any of the heads.
1431 1431 Return a chunkbuffer object whose read() method will return
1432 1432 successive changegroup chunks.
1433 1433
1434 1434 It is fairly complex as determining which filenodes and which
1435 1435 manifest nodes need to be included for the changeset to be complete
1436 1436 is non-trivial.
1437 1437
1438 1438 Another wrinkle is doing the reverse, figuring out which changeset in
1439 1439 the changegroup a particular filenode or manifestnode belongs to.
1440 1440 """
1441 1441
1442 1442 cl = self.changelog
1443 1443 mf = self.manifest
1444 1444 mfs = {} # needed manifests
1445 1445 fnodes = {} # needed file nodes
1446 1446
1447 1447 if not bases:
1448 1448 bases = [nullid]
1449 1449 csets, bases, heads = cl.nodesbetween(bases, heads)
1450 1450
1451 1451 # can we go through the fast path ?
1452 1452 heads.sort()
1453 1453 if heads == sorted(self.heads()):
1454 1454 return self._changegroup(csets, source)
1455 1455
1456 1456 # slow path
1457 1457 self.hook('preoutgoing', throw=True, source=source)
1458 1458 self.changegroupinfo(csets, source)
1459 1459
1460 1460 # We assume that all ancestors of bases are known
1461 1461 commonrevs = set(cl.ancestors(*[cl.rev(n) for n in bases]))
1462 1462
1463 1463 # A function generating function that sets up the initial environment
1464 1464 # the inner function.
1465 1465 def filenode_collector(changedfiles):
1466 1466 # This gathers information from each manifestnode included in the
1467 1467 # changegroup about which filenodes the manifest node references
1468 1468 # so we can include those in the changegroup too.
1469 1469 #
1470 1470 # It also remembers which changenode each filenode belongs to. It
1471 1471 # does this by assuming the a filenode belongs to the changenode
1472 1472 # the first manifest that references it belongs to.
1473 1473 def collect(mnode):
1474 1474 r = mf.rev(mnode)
1475 1475 clnode = mfs[mnode]
1476 1476 mdata = mf.readfast(mnode)
1477 1477 for f in changedfiles:
1478 1478 if f in mdata:
1479 1479 fnodes.setdefault(f, {}).setdefault(mdata[f], clnode)
1480 1480
1481 1481 return collect
1482 1482
1483 1483 # If we determine that a particular file or manifest node must be a
1484 1484 # node that the recipient of the changegroup will already have, we can
1485 1485 # also assume the recipient will have all the parents. This function
1486 1486 # prunes them from the set of missing nodes.
1487 1487 def prune(revlog, missingnodes):
1488 1488 # drop any nodes that claim to be part of a cset in commonrevs
1489 1489 drop = set()
1490 1490 for n in missingnodes:
1491 1491 if revlog.linkrev(revlog.rev(n)) in commonrevs:
1492 1492 drop.add(n)
1493 1493 for n in drop:
1494 1494 missingnodes.pop(n, None)
1495 1495
1496 1496 # Now that we have all theses utility functions to help out and
1497 1497 # logically divide up the task, generate the group.
1498 1498 def gengroup():
1499 1499 # The set of changed files starts empty.
1500 1500 changedfiles = set()
1501 1501 collect = changegroup.collector(cl, mfs, changedfiles)
1502 1502
1503 1503 # Create a changenode group generator that will call our functions
1504 1504 # back to lookup the owning changenode and collect information.
1505 1505 group = cl.group(csets, lambda x: x, collect)
1506 1506 for count, chunk in enumerate(group):
1507 1507 yield chunk
1508 1508 # revlog.group yields three entries per node, so
1509 1509 # dividing by 3 gives an approximation of how many
1510 1510 # nodes have been processed.
1511 1511 self.ui.progress(_('bundling'), count / 3,
1512 1512 unit=_('changesets'))
1513 1513 changecount = count / 3
1514 1514 efiles = len(changedfiles)
1515 1515 self.ui.progress(_('bundling'), None)
1516 1516
1517 1517 prune(mf, mfs)
1518 1518 # Create a generator for the manifestnodes that calls our lookup
1519 1519 # and data collection functions back.
1520 1520 group = mf.group(sorted(mfs, key=mf.rev),
1521 1521 lambda mnode: mfs[mnode],
1522 1522 filenode_collector(changedfiles))
1523 1523 for count, chunk in enumerate(group):
1524 1524 yield chunk
1525 1525 # see above comment for why we divide by 3
1526 1526 self.ui.progress(_('bundling'), count / 3,
1527 1527 unit=_('manifests'), total=changecount)
1528 1528 self.ui.progress(_('bundling'), None)
1529 1529
1530 1530 mfs.clear()
1531 1531
1532 1532 # Go through all our files in order sorted by name.
1533 1533 for idx, fname in enumerate(sorted(changedfiles)):
1534 1534 filerevlog = self.file(fname)
1535 1535 if not len(filerevlog):
1536 1536 raise util.Abort(_("empty or missing revlog for %s") % fname)
1537 1537 # Toss out the filenodes that the recipient isn't really
1538 1538 # missing.
1539 1539 missingfnodes = fnodes.pop(fname, {})
1540 1540 prune(filerevlog, missingfnodes)
1541 1541 # If any filenodes are left, generate the group for them,
1542 1542 # otherwise don't bother.
1543 1543 if missingfnodes:
1544 1544 yield changegroup.chunkheader(len(fname))
1545 1545 yield fname
1546 1546 # Create a group generator and only pass in a changenode
1547 1547 # lookup function as we need to collect no information
1548 1548 # from filenodes.
1549 1549 group = filerevlog.group(
1550 1550 sorted(missingfnodes, key=filerevlog.rev),
1551 1551 lambda fnode: missingfnodes[fnode])
1552 1552 for chunk in group:
1553 1553 # even though we print the same progress on
1554 1554 # most loop iterations, put the progress call
1555 1555 # here so that time estimates (if any) can be updated
1556 1556 self.ui.progress(
1557 1557 _('bundling'), idx, item=fname,
1558 1558 unit=_('files'), total=efiles)
1559 1559 yield chunk
1560 1560 # Signal that no more groups are left.
1561 1561 yield changegroup.closechunk()
1562 1562 self.ui.progress(_('bundling'), None)
1563 1563
1564 1564 if csets:
1565 1565 self.hook('outgoing', node=hex(csets[0]), source=source)
1566 1566
1567 1567 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1568 1568
1569 1569 def changegroup(self, basenodes, source):
1570 1570 # to avoid a race we use changegroupsubset() (issue1320)
1571 1571 return self.changegroupsubset(basenodes, self.heads(), source)
1572 1572
1573 1573 def _changegroup(self, nodes, source):
1574 1574 """Compute the changegroup of all nodes that we have that a recipient
1575 1575 doesn't. Return a chunkbuffer object whose read() method will return
1576 1576 successive changegroup chunks.
1577 1577
1578 1578 This is much easier than the previous function as we can assume that
1579 1579 the recipient has any changenode we aren't sending them.
1580 1580
1581 1581 nodes is the set of nodes to send"""
1582 1582
1583 1583 self.hook('preoutgoing', throw=True, source=source)
1584 1584
1585 1585 cl = self.changelog
1586 1586 revset = set([cl.rev(n) for n in nodes])
1587 1587 self.changegroupinfo(nodes, source)
1588 1588
1589 1589 def gennodelst(log):
1590 1590 for r in log:
1591 1591 if log.linkrev(r) in revset:
1592 1592 yield log.node(r)
1593 1593
1594 1594 def lookuplinkrev_func(revlog):
1595 1595 def lookuplinkrev(n):
1596 1596 return cl.node(revlog.linkrev(revlog.rev(n)))
1597 1597 return lookuplinkrev
1598 1598
1599 1599 def gengroup():
1600 1600 '''yield a sequence of changegroup chunks (strings)'''
1601 1601 # construct a list of all changed files
1602 1602 changedfiles = set()
1603 1603 mmfs = {}
1604 1604 collect = changegroup.collector(cl, mmfs, changedfiles)
1605 1605
1606 1606 for count, chunk in enumerate(cl.group(nodes, lambda x: x, collect)):
1607 1607 # revlog.group yields three entries per node, so
1608 1608 # dividing by 3 gives an approximation of how many
1609 1609 # nodes have been processed.
1610 1610 self.ui.progress(_('bundling'), count / 3, unit=_('changesets'))
1611 1611 yield chunk
1612 1612 efiles = len(changedfiles)
1613 1613 changecount = count / 3
1614 1614 self.ui.progress(_('bundling'), None)
1615 1615
1616 1616 mnfst = self.manifest
1617 1617 nodeiter = gennodelst(mnfst)
1618 1618 for count, chunk in enumerate(mnfst.group(nodeiter,
1619 1619 lookuplinkrev_func(mnfst))):
1620 1620 # see above comment for why we divide by 3
1621 1621 self.ui.progress(_('bundling'), count / 3,
1622 1622 unit=_('manifests'), total=changecount)
1623 1623 yield chunk
1624 1624 self.ui.progress(_('bundling'), None)
1625 1625
1626 1626 for idx, fname in enumerate(sorted(changedfiles)):
1627 1627 filerevlog = self.file(fname)
1628 1628 if not len(filerevlog):
1629 1629 raise util.Abort(_("empty or missing revlog for %s") % fname)
1630 1630 nodeiter = gennodelst(filerevlog)
1631 1631 nodeiter = list(nodeiter)
1632 1632 if nodeiter:
1633 1633 yield changegroup.chunkheader(len(fname))
1634 1634 yield fname
1635 1635 lookup = lookuplinkrev_func(filerevlog)
1636 1636 for chunk in filerevlog.group(nodeiter, lookup):
1637 1637 self.ui.progress(
1638 1638 _('bundling'), idx, item=fname,
1639 1639 total=efiles, unit=_('files'))
1640 1640 yield chunk
1641 1641 self.ui.progress(_('bundling'), None)
1642 1642
1643 1643 yield changegroup.closechunk()
1644 1644
1645 1645 if nodes:
1646 1646 self.hook('outgoing', node=hex(nodes[0]), source=source)
1647 1647
1648 1648 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1649 1649
1650 1650 def addchangegroup(self, source, srctype, url, emptyok=False, lock=None):
1651 1651 """Add the changegroup returned by source.read() to this repo.
1652 1652 srctype is a string like 'push', 'pull', or 'unbundle'. url is
1653 1653 the URL of the repo where this changegroup is coming from.
1654 1654 If lock is not None, the function takes ownership of the lock
1655 1655 and releases it after the changegroup is added.
1656 1656
1657 1657 Return an integer summarizing the change to this repo:
1658 1658 - nothing changed or no source: 0
1659 1659 - more heads than before: 1+added heads (2..n)
1660 1660 - fewer heads than before: -1-removed heads (-2..-n)
1661 1661 - number of heads stays the same: 1
1662 1662 """
1663 1663 def csmap(x):
1664 1664 self.ui.debug("add changeset %s\n" % short(x))
1665 1665 return len(cl)
1666 1666
1667 1667 def revmap(x):
1668 1668 return cl.rev(x)
1669 1669
1670 1670 if not source:
1671 1671 return 0
1672 1672
1673 1673 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1674 1674
1675 1675 changesets = files = revisions = 0
1676 1676 efiles = set()
1677 1677
1678 1678 # write changelog data to temp files so concurrent readers will not see
1679 1679 # inconsistent view
1680 1680 cl = self.changelog
1681 1681 cl.delayupdate()
1682 1682 oldheads = len(cl.heads())
1683 1683
1684 1684 tr = self.transaction("\n".join([srctype, urlmod.hidepassword(url)]))
1685 1685 try:
1686 1686 trp = weakref.proxy(tr)
1687 1687 # pull off the changeset group
1688 1688 self.ui.status(_("adding changesets\n"))
1689 1689 clstart = len(cl)
1690 1690 class prog(object):
1691 1691 step = _('changesets')
1692 1692 count = 1
1693 1693 ui = self.ui
1694 1694 total = None
1695 1695 def __call__(self):
1696 1696 self.ui.progress(self.step, self.count, unit=_('chunks'),
1697 1697 total=self.total)
1698 1698 self.count += 1
1699 1699 pr = prog()
1700 1700 source.callback = pr
1701 1701
1702 1702 if (cl.addgroup(source, csmap, trp) is None
1703 1703 and not emptyok):
1704 1704 raise util.Abort(_("received changelog group is empty"))
1705 1705 clend = len(cl)
1706 1706 changesets = clend - clstart
1707 1707 for c in xrange(clstart, clend):
1708 1708 efiles.update(self[c].files())
1709 1709 efiles = len(efiles)
1710 1710 self.ui.progress(_('changesets'), None)
1711 1711
1712 1712 # pull off the manifest group
1713 1713 self.ui.status(_("adding manifests\n"))
1714 1714 pr.step = _('manifests')
1715 1715 pr.count = 1
1716 1716 pr.total = changesets # manifests <= changesets
1717 1717 # no need to check for empty manifest group here:
1718 1718 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1719 1719 # no new manifest will be created and the manifest group will
1720 1720 # be empty during the pull
1721 1721 self.manifest.addgroup(source, revmap, trp)
1722 1722 self.ui.progress(_('manifests'), None)
1723 1723
1724 1724 needfiles = {}
1725 1725 if self.ui.configbool('server', 'validate', default=False):
1726 1726 # validate incoming csets have their manifests
1727 1727 for cset in xrange(clstart, clend):
1728 1728 mfest = self.changelog.read(self.changelog.node(cset))[0]
1729 1729 mfest = self.manifest.readdelta(mfest)
1730 1730 # store file nodes we must see
1731 1731 for f, n in mfest.iteritems():
1732 1732 needfiles.setdefault(f, set()).add(n)
1733 1733
1734 1734 # process the files
1735 1735 self.ui.status(_("adding file changes\n"))
1736 1736 pr.step = 'files'
1737 1737 pr.count = 1
1738 1738 pr.total = efiles
1739 1739 source.callback = None
1740 1740
1741 1741 while 1:
1742 1742 f = source.chunk()
1743 1743 if not f:
1744 1744 break
1745 1745 self.ui.debug("adding %s revisions\n" % f)
1746 1746 pr()
1747 1747 fl = self.file(f)
1748 1748 o = len(fl)
1749 1749 if fl.addgroup(source, revmap, trp) is None:
1750 1750 raise util.Abort(_("received file revlog group is empty"))
1751 1751 revisions += len(fl) - o
1752 1752 files += 1
1753 1753 if f in needfiles:
1754 1754 needs = needfiles[f]
1755 1755 for new in xrange(o, len(fl)):
1756 1756 n = fl.node(new)
1757 1757 if n in needs:
1758 1758 needs.remove(n)
1759 1759 if not needs:
1760 1760 del needfiles[f]
1761 1761 self.ui.progress(_('files'), None)
1762 1762
1763 1763 for f, needs in needfiles.iteritems():
1764 1764 fl = self.file(f)
1765 1765 for n in needs:
1766 1766 try:
1767 1767 fl.rev(n)
1768 1768 except error.LookupError:
1769 1769 raise util.Abort(
1770 1770 _('missing file data for %s:%s - run hg verify') %
1771 1771 (f, hex(n)))
1772 1772
1773 1773 newheads = len(cl.heads())
1774 1774 heads = ""
1775 1775 if oldheads and newheads != oldheads:
1776 1776 heads = _(" (%+d heads)") % (newheads - oldheads)
1777 1777
1778 1778 self.ui.status(_("added %d changesets"
1779 1779 " with %d changes to %d files%s\n")
1780 1780 % (changesets, revisions, files, heads))
1781 1781
1782 1782 if changesets > 0:
1783 1783 p = lambda: cl.writepending() and self.root or ""
1784 1784 self.hook('pretxnchangegroup', throw=True,
1785 1785 node=hex(cl.node(clstart)), source=srctype,
1786 1786 url=url, pending=p)
1787 1787
1788 1788 # make changelog see real files again
1789 1789 cl.finalize(trp)
1790 1790
1791 1791 tr.close()
1792 1792 finally:
1793 1793 tr.release()
1794 1794 if lock:
1795 1795 lock.release()
1796 1796
1797 1797 if changesets > 0:
1798 1798 # forcefully update the on-disk branch cache
1799 1799 self.ui.debug("updating the branch cache\n")
1800 1800 self.updatebranchcache()
1801 1801 self.hook("changegroup", node=hex(cl.node(clstart)),
1802 1802 source=srctype, url=url)
1803 1803
1804 1804 for i in xrange(clstart, clend):
1805 1805 self.hook("incoming", node=hex(cl.node(i)),
1806 1806 source=srctype, url=url)
1807 1807
1808 1808 # never return 0 here:
1809 1809 if newheads < oldheads:
1810 1810 return newheads - oldheads - 1
1811 1811 else:
1812 1812 return newheads - oldheads + 1
1813 1813
1814 1814
1815 1815 def stream_in(self, remote, requirements):
1816 1816 lock = self.lock()
1817 1817 try:
1818 1818 fp = remote.stream_out()
1819 1819 l = fp.readline()
1820 1820 try:
1821 1821 resp = int(l)
1822 1822 except ValueError:
1823 1823 raise error.ResponseError(
1824 1824 _('Unexpected response from remote server:'), l)
1825 1825 if resp == 1:
1826 1826 raise util.Abort(_('operation forbidden by server'))
1827 1827 elif resp == 2:
1828 1828 raise util.Abort(_('locking the remote repository failed'))
1829 1829 elif resp != 0:
1830 1830 raise util.Abort(_('the server sent an unknown error code'))
1831 1831 self.ui.status(_('streaming all changes\n'))
1832 1832 l = fp.readline()
1833 1833 try:
1834 1834 total_files, total_bytes = map(int, l.split(' ', 1))
1835 1835 except (ValueError, TypeError):
1836 1836 raise error.ResponseError(
1837 1837 _('Unexpected response from remote server:'), l)
1838 1838 self.ui.status(_('%d files to transfer, %s of data\n') %
1839 1839 (total_files, util.bytecount(total_bytes)))
1840 1840 start = time.time()
1841 1841 for i in xrange(total_files):
1842 1842 # XXX doesn't support '\n' or '\r' in filenames
1843 1843 l = fp.readline()
1844 1844 try:
1845 1845 name, size = l.split('\0', 1)
1846 1846 size = int(size)
1847 1847 except (ValueError, TypeError):
1848 1848 raise error.ResponseError(
1849 1849 _('Unexpected response from remote server:'), l)
1850 1850 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
1851 1851 # for backwards compat, name was partially encoded
1852 1852 ofp = self.sopener(store.decodedir(name), 'w')
1853 1853 for chunk in util.filechunkiter(fp, limit=size):
1854 1854 ofp.write(chunk)
1855 1855 ofp.close()
1856 1856 elapsed = time.time() - start
1857 1857 if elapsed <= 0:
1858 1858 elapsed = 0.001
1859 1859 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1860 1860 (util.bytecount(total_bytes), elapsed,
1861 1861 util.bytecount(total_bytes / elapsed)))
1862 1862
1863 1863 # new requirements = old non-format requirements + new format-related
1864 1864 # requirements from the streamed-in repository
1865 1865 requirements.update(set(self.requirements) - self.supportedformats)
1866 1866 self._applyrequirements(requirements)
1867 1867 self._writerequirements()
1868 1868
1869 1869 self.invalidate()
1870 1870 return len(self.heads()) + 1
1871 1871 finally:
1872 1872 lock.release()
1873 1873
1874 1874 def clone(self, remote, heads=[], stream=False):
1875 1875 '''clone remote repository.
1876 1876
1877 1877 keyword arguments:
1878 1878 heads: list of revs to clone (forces use of pull)
1879 1879 stream: use streaming clone if possible'''
1880 1880
1881 1881 # now, all clients that can request uncompressed clones can
1882 1882 # read repo formats supported by all servers that can serve
1883 1883 # them.
1884 1884
1885 1885 # if revlog format changes, client will have to check version
1886 1886 # and format flags on "stream" capability, and use
1887 1887 # uncompressed only if compatible.
1888 1888
1889 1889 if stream and not heads:
1890 1890 # 'stream' means remote revlog format is revlogv1 only
1891 1891 if remote.capable('stream'):
1892 1892 return self.stream_in(remote, set(('revlogv1',)))
1893 1893 # otherwise, 'streamreqs' contains the remote revlog format
1894 1894 streamreqs = remote.capable('streamreqs')
1895 1895 if streamreqs:
1896 1896 streamreqs = set(streamreqs.split(','))
1897 1897 # if we support it, stream in and adjust our requirements
1898 1898 if not streamreqs - self.supportedformats:
1899 1899 return self.stream_in(remote, streamreqs)
1900 1900 return self.pull(remote, heads)
1901 1901
1902 1902 def pushkey(self, namespace, key, old, new):
1903 1903 return pushkey.push(self, namespace, key, old, new)
1904 1904
1905 1905 def listkeys(self, namespace):
1906 1906 return pushkey.list(self, namespace)
1907 1907
1908 def debugwireargs(self, one, two, three=None, four=None):
1909 '''used to test argument passing over the wire'''
1910 return "%s %s %s %s" % (one, two, three, four)
1911
1908 1912 # used to avoid circular references so destructors work
1909 1913 def aftertrans(files):
1910 1914 renamefiles = [tuple(t) for t in files]
1911 1915 def a():
1912 1916 for src, dest in renamefiles:
1913 1917 util.rename(src, dest)
1914 1918 return a
1915 1919
1916 1920 def instance(ui, path, create):
1917 1921 return localrepository(ui, util.drop_scheme('file', path), create)
1918 1922
1919 1923 def islocal(path):
1920 1924 return True
@@ -1,353 +1,366 b''
1 1 # wireproto.py - generic wire protocol support functions
2 2 #
3 3 # Copyright 2005-2010 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 import urllib, tempfile, os, sys
9 9 from i18n import _
10 10 from node import bin, hex
11 11 import changegroup as changegroupmod
12 12 import repo, error, encoding, util, store
13 13 import pushkey as pushkeymod
14 14
15 15 # list of nodes encoding / decoding
16 16
17 17 def decodelist(l, sep=' '):
18 18 return map(bin, l.split(sep))
19 19
20 20 def encodelist(l, sep=' '):
21 21 return sep.join(map(hex, l))
22 22
23 23 # client side
24 24
25 25 class wirerepository(repo.repository):
26 26 def lookup(self, key):
27 27 self.requirecap('lookup', _('look up remote revision'))
28 28 d = self._call("lookup", key=encoding.fromlocal(key))
29 29 success, data = d[:-1].split(" ", 1)
30 30 if int(success):
31 31 return bin(data)
32 32 self._abort(error.RepoError(data))
33 33
34 34 def heads(self):
35 35 d = self._call("heads")
36 36 try:
37 37 return decodelist(d[:-1])
38 38 except:
39 39 self._abort(error.ResponseError(_("unexpected response:"), d))
40 40
41 41 def branchmap(self):
42 42 d = self._call("branchmap")
43 43 try:
44 44 branchmap = {}
45 45 for branchpart in d.splitlines():
46 46 branchname, branchheads = branchpart.split(' ', 1)
47 47 branchname = encoding.tolocal(urllib.unquote(branchname))
48 48 branchheads = decodelist(branchheads)
49 49 branchmap[branchname] = branchheads
50 50 return branchmap
51 51 except TypeError:
52 52 self._abort(error.ResponseError(_("unexpected response:"), d))
53 53
54 54 def branches(self, nodes):
55 55 n = encodelist(nodes)
56 56 d = self._call("branches", nodes=n)
57 57 try:
58 58 br = [tuple(decodelist(b)) for b in d.splitlines()]
59 59 return br
60 60 except:
61 61 self._abort(error.ResponseError(_("unexpected response:"), d))
62 62
63 63 def between(self, pairs):
64 64 batch = 8 # avoid giant requests
65 65 r = []
66 66 for i in xrange(0, len(pairs), batch):
67 67 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
68 68 d = self._call("between", pairs=n)
69 69 try:
70 70 r.extend(l and decodelist(l) or [] for l in d.splitlines())
71 71 except:
72 72 self._abort(error.ResponseError(_("unexpected response:"), d))
73 73 return r
74 74
75 75 def pushkey(self, namespace, key, old, new):
76 76 if not self.capable('pushkey'):
77 77 return False
78 78 d = self._call("pushkey",
79 79 namespace=encoding.fromlocal(namespace),
80 80 key=encoding.fromlocal(key),
81 81 old=encoding.fromlocal(old),
82 82 new=encoding.fromlocal(new))
83 83 try:
84 84 d = bool(int(d))
85 85 except ValueError:
86 86 raise error.ResponseError(
87 87 _('push failed (unexpected response):'), d)
88 88 return d
89 89
90 90 def listkeys(self, namespace):
91 91 if not self.capable('pushkey'):
92 92 return {}
93 93 d = self._call("listkeys", namespace=encoding.fromlocal(namespace))
94 94 r = {}
95 95 for l in d.splitlines():
96 96 k, v = l.split('\t')
97 97 r[encoding.tolocal(k)] = encoding.tolocal(v)
98 98 return r
99 99
100 100 def stream_out(self):
101 101 return self._callstream('stream_out')
102 102
103 103 def changegroup(self, nodes, kind):
104 104 n = encodelist(nodes)
105 105 f = self._callstream("changegroup", roots=n)
106 106 return changegroupmod.unbundle10(self._decompress(f), 'UN')
107 107
108 108 def changegroupsubset(self, bases, heads, kind):
109 109 self.requirecap('changegroupsubset', _('look up remote changes'))
110 110 bases = encodelist(bases)
111 111 heads = encodelist(heads)
112 112 f = self._callstream("changegroupsubset",
113 113 bases=bases, heads=heads)
114 114 return changegroupmod.unbundle10(self._decompress(f), 'UN')
115 115
116 116 def unbundle(self, cg, heads, source):
117 117 '''Send cg (a readable file-like object representing the
118 118 changegroup to push, typically a chunkbuffer object) to the
119 119 remote server as a bundle. Return an integer indicating the
120 120 result of the push (see localrepository.addchangegroup()).'''
121 121
122 122 ret, output = self._callpush("unbundle", cg, heads=encodelist(heads))
123 123 if ret == "":
124 124 raise error.ResponseError(
125 125 _('push failed:'), output)
126 126 try:
127 127 ret = int(ret)
128 128 except ValueError:
129 129 raise error.ResponseError(
130 130 _('push failed (unexpected response):'), ret)
131 131
132 132 for l in output.splitlines(True):
133 133 self.ui.status(_('remote: '), l)
134 134 return ret
135 135
136 def debugwireargs(self, one, two, three=None, four=None):
137 # don't pass optional arguments left at their default value
138 opts = {}
139 if three is not None:
140 opts['three'] = three
141 if four is not None:
142 opts['four'] = four
143 return self._call('debugwireargs', one=one, two=two, **opts)
144
136 145 # server side
137 146
138 147 class streamres(object):
139 148 def __init__(self, gen):
140 149 self.gen = gen
141 150
142 151 class pushres(object):
143 152 def __init__(self, res):
144 153 self.res = res
145 154
146 155 class pusherr(object):
147 156 def __init__(self, res):
148 157 self.res = res
149 158
150 159 def dispatch(repo, proto, command):
151 160 func, spec = commands[command]
152 161 args = proto.getargs(spec)
153 162 return func(repo, proto, *args)
154 163
155 164 def between(repo, proto, pairs):
156 165 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
157 166 r = []
158 167 for b in repo.between(pairs):
159 168 r.append(encodelist(b) + "\n")
160 169 return "".join(r)
161 170
162 171 def branchmap(repo, proto):
163 172 branchmap = repo.branchmap()
164 173 heads = []
165 174 for branch, nodes in branchmap.iteritems():
166 175 branchname = urllib.quote(encoding.fromlocal(branch))
167 176 branchnodes = encodelist(nodes)
168 177 heads.append('%s %s' % (branchname, branchnodes))
169 178 return '\n'.join(heads)
170 179
171 180 def branches(repo, proto, nodes):
172 181 nodes = decodelist(nodes)
173 182 r = []
174 183 for b in repo.branches(nodes):
175 184 r.append(encodelist(b) + "\n")
176 185 return "".join(r)
177 186
178 187 def capabilities(repo, proto):
179 188 caps = 'lookup changegroupsubset branchmap pushkey'.split()
180 189 if _allowstream(repo.ui):
181 190 requiredformats = repo.requirements & repo.supportedformats
182 191 # if our local revlogs are just revlogv1, add 'stream' cap
183 192 if not requiredformats - set(('revlogv1',)):
184 193 caps.append('stream')
185 194 # otherwise, add 'streamreqs' detailing our local revlog format
186 195 else:
187 196 caps.append('streamreqs=%s' % ','.join(requiredformats))
188 197 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
189 198 return ' '.join(caps)
190 199
191 200 def changegroup(repo, proto, roots):
192 201 nodes = decodelist(roots)
193 202 cg = repo.changegroup(nodes, 'serve')
194 203 return streamres(proto.groupchunks(cg))
195 204
196 205 def changegroupsubset(repo, proto, bases, heads):
197 206 bases = decodelist(bases)
198 207 heads = decodelist(heads)
199 208 cg = repo.changegroupsubset(bases, heads, 'serve')
200 209 return streamres(proto.groupchunks(cg))
201 210
211 def debugwireargs(repo, proto, one, two):
212 return repo.debugwireargs(one, two)
213
202 214 def heads(repo, proto):
203 215 h = repo.heads()
204 216 return encodelist(h) + "\n"
205 217
206 218 def hello(repo, proto):
207 219 '''the hello command returns a set of lines describing various
208 220 interesting things about the server, in an RFC822-like format.
209 221 Currently the only one defined is "capabilities", which
210 222 consists of a line in the form:
211 223
212 224 capabilities: space separated list of tokens
213 225 '''
214 226 return "capabilities: %s\n" % (capabilities(repo, proto))
215 227
216 228 def listkeys(repo, proto, namespace):
217 229 d = pushkeymod.list(repo, encoding.tolocal(namespace)).items()
218 230 t = '\n'.join(['%s\t%s' % (encoding.fromlocal(k), encoding.fromlocal(v))
219 231 for k, v in d])
220 232 return t
221 233
222 234 def lookup(repo, proto, key):
223 235 try:
224 236 r = hex(repo.lookup(encoding.tolocal(key)))
225 237 success = 1
226 238 except Exception, inst:
227 239 r = str(inst)
228 240 success = 0
229 241 return "%s %s\n" % (success, r)
230 242
231 243 def pushkey(repo, proto, namespace, key, old, new):
232 244 # compatibility with pre-1.8 clients which were accidentally
233 245 # sending raw binary nodes rather than utf-8-encoded hex
234 246 if len(new) == 20 and new.encode('string-escape') != new:
235 247 # looks like it could be a binary node
236 248 try:
237 249 u = new.decode('utf-8')
238 250 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
239 251 except UnicodeDecodeError:
240 252 pass # binary, leave unmodified
241 253 else:
242 254 new = encoding.tolocal(new) # normal path
243 255
244 256 r = pushkeymod.push(repo,
245 257 encoding.tolocal(namespace), encoding.tolocal(key),
246 258 encoding.tolocal(old), new)
247 259 return '%s\n' % int(r)
248 260
249 261 def _allowstream(ui):
250 262 return ui.configbool('server', 'uncompressed', True, untrusted=True)
251 263
252 264 def stream(repo, proto):
253 265 '''If the server supports streaming clone, it advertises the "stream"
254 266 capability with a value representing the version and flags of the repo
255 267 it is serving. Client checks to see if it understands the format.
256 268
257 269 The format is simple: the server writes out a line with the amount
258 270 of files, then the total amount of bytes to be transfered (separated
259 271 by a space). Then, for each file, the server first writes the filename
260 272 and filesize (separated by the null character), then the file contents.
261 273 '''
262 274
263 275 if not _allowstream(repo.ui):
264 276 return '1\n'
265 277
266 278 entries = []
267 279 total_bytes = 0
268 280 try:
269 281 # get consistent snapshot of repo, lock during scan
270 282 lock = repo.lock()
271 283 try:
272 284 repo.ui.debug('scanning\n')
273 285 for name, ename, size in repo.store.walk():
274 286 entries.append((name, size))
275 287 total_bytes += size
276 288 finally:
277 289 lock.release()
278 290 except error.LockError:
279 291 return '2\n' # error: 2
280 292
281 293 def streamer(repo, entries, total):
282 294 '''stream out all metadata files in repository.'''
283 295 yield '0\n' # success
284 296 repo.ui.debug('%d files, %d bytes to transfer\n' %
285 297 (len(entries), total_bytes))
286 298 yield '%d %d\n' % (len(entries), total_bytes)
287 299 for name, size in entries:
288 300 repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
289 301 # partially encode name over the wire for backwards compat
290 302 yield '%s\0%d\n' % (store.encodedir(name), size)
291 303 for chunk in util.filechunkiter(repo.sopener(name), limit=size):
292 304 yield chunk
293 305
294 306 return streamres(streamer(repo, entries, total_bytes))
295 307
296 308 def unbundle(repo, proto, heads):
297 309 their_heads = decodelist(heads)
298 310
299 311 def check_heads():
300 312 heads = repo.heads()
301 313 return their_heads == ['force'] or their_heads == heads
302 314
303 315 proto.redirect()
304 316
305 317 # fail early if possible
306 318 if not check_heads():
307 319 return pusherr('unsynced changes')
308 320
309 321 # write bundle data to temporary file because it can be big
310 322 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
311 323 fp = os.fdopen(fd, 'wb+')
312 324 r = 0
313 325 try:
314 326 proto.getfile(fp)
315 327 lock = repo.lock()
316 328 try:
317 329 if not check_heads():
318 330 # someone else committed/pushed/unbundled while we
319 331 # were transferring data
320 332 return pusherr('unsynced changes')
321 333
322 334 # push can proceed
323 335 fp.seek(0)
324 336 gen = changegroupmod.readbundle(fp, None)
325 337
326 338 try:
327 339 r = repo.addchangegroup(gen, 'serve', proto._client(),
328 340 lock=lock)
329 341 except util.Abort, inst:
330 342 sys.stderr.write("abort: %s\n" % inst)
331 343 finally:
332 344 lock.release()
333 345 return pushres(r)
334 346
335 347 finally:
336 348 fp.close()
337 349 os.unlink(tempname)
338 350
339 351 commands = {
340 352 'between': (between, 'pairs'),
341 353 'branchmap': (branchmap, ''),
342 354 'branches': (branches, 'nodes'),
343 355 'capabilities': (capabilities, ''),
344 356 'changegroup': (changegroup, 'roots'),
345 357 'changegroupsubset': (changegroupsubset, 'bases heads'),
358 'debugwireargs': (debugwireargs, 'one two'),
346 359 'heads': (heads, ''),
347 360 'hello': (hello, ''),
348 361 'listkeys': (listkeys, 'namespace'),
349 362 'lookup': (lookup, 'key'),
350 363 'pushkey': (pushkey, 'namespace key old new'),
351 364 'stream_out': (stream, ''),
352 365 'unbundle': (unbundle, 'heads'),
353 366 }
@@ -1,253 +1,255 b''
1 1 Show all commands except debug commands
2 2 $ hg debugcomplete
3 3 add
4 4 addremove
5 5 annotate
6 6 archive
7 7 backout
8 8 bisect
9 9 bookmarks
10 10 branch
11 11 branches
12 12 bundle
13 13 cat
14 14 clone
15 15 commit
16 16 copy
17 17 diff
18 18 export
19 19 forget
20 20 grep
21 21 heads
22 22 help
23 23 identify
24 24 import
25 25 incoming
26 26 init
27 27 locate
28 28 log
29 29 manifest
30 30 merge
31 31 outgoing
32 32 parents
33 33 paths
34 34 pull
35 35 push
36 36 recover
37 37 remove
38 38 rename
39 39 resolve
40 40 revert
41 41 rollback
42 42 root
43 43 serve
44 44 showconfig
45 45 status
46 46 summary
47 47 tag
48 48 tags
49 49 tip
50 50 unbundle
51 51 update
52 52 verify
53 53 version
54 54
55 55 Show all commands that start with "a"
56 56 $ hg debugcomplete a
57 57 add
58 58 addremove
59 59 annotate
60 60 archive
61 61
62 62 Do not show debug commands if there are other candidates
63 63 $ hg debugcomplete d
64 64 diff
65 65
66 66 Show debug commands if there are no other candidates
67 67 $ hg debugcomplete debug
68 68 debugancestor
69 69 debugbuilddag
70 70 debugcheckstate
71 71 debugcommands
72 72 debugcomplete
73 73 debugconfig
74 74 debugdag
75 75 debugdata
76 76 debugdate
77 77 debugfsinfo
78 78 debugignore
79 79 debugindex
80 80 debugindexdot
81 81 debuginstall
82 82 debugpushkey
83 83 debugrebuildstate
84 84 debugrename
85 85 debugrevspec
86 86 debugsetparents
87 87 debugstate
88 88 debugsub
89 89 debugwalk
90 debugwireargs
90 91
91 92 Do not show the alias of a debug command if there are other candidates
92 93 (this should hide rawcommit)
93 94 $ hg debugcomplete r
94 95 recover
95 96 remove
96 97 rename
97 98 resolve
98 99 revert
99 100 rollback
100 101 root
101 102 Show the alias of a debug command if there are no other candidates
102 103 $ hg debugcomplete rawc
103 104
104 105
105 106 Show the global options
106 107 $ hg debugcomplete --options | sort
107 108 --config
108 109 --cwd
109 110 --debug
110 111 --debugger
111 112 --encoding
112 113 --encodingmode
113 114 --help
114 115 --noninteractive
115 116 --profile
116 117 --quiet
117 118 --repository
118 119 --time
119 120 --traceback
120 121 --verbose
121 122 --version
122 123 -R
123 124 -h
124 125 -q
125 126 -v
126 127 -y
127 128
128 129 Show the options for the "serve" command
129 130 $ hg debugcomplete --options serve | sort
130 131 --accesslog
131 132 --address
132 133 --certificate
133 134 --config
134 135 --cwd
135 136 --daemon
136 137 --daemon-pipefds
137 138 --debug
138 139 --debugger
139 140 --encoding
140 141 --encodingmode
141 142 --errorlog
142 143 --help
143 144 --ipv6
144 145 --name
145 146 --noninteractive
146 147 --pid-file
147 148 --port
148 149 --prefix
149 150 --profile
150 151 --quiet
151 152 --repository
152 153 --stdio
153 154 --style
154 155 --templates
155 156 --time
156 157 --traceback
157 158 --verbose
158 159 --version
159 160 --web-conf
160 161 -6
161 162 -A
162 163 -E
163 164 -R
164 165 -a
165 166 -d
166 167 -h
167 168 -n
168 169 -p
169 170 -q
170 171 -t
171 172 -v
172 173 -y
173 174
174 175 Show an error if we use --options with an ambiguous abbreviation
175 176 $ hg debugcomplete --options s
176 177 hg: command 's' is ambiguous:
177 178 serve showconfig status summary
178 179 [255]
179 180
180 181 Show all commands + options
181 182 $ hg debugcommands
182 183 add: include, exclude, subrepos, dry-run
183 184 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, include, exclude
184 185 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
185 186 commit: addremove, close-branch, include, exclude, message, logfile, date, user
186 187 diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude, subrepos
187 188 export: output, switch-parent, rev, text, git, nodates
188 189 forget: include, exclude
189 190 init: ssh, remotecmd, insecure
190 191 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, style, template, include, exclude
191 192 merge: force, tool, rev, preview
192 193 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
193 194 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
194 195 remove: after, force, include, exclude
195 196 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate
196 197 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos
197 198 summary: remote
198 199 update: clean, check, date, rev
199 200 addremove: similarity, include, exclude, dry-run
200 201 archive: no-decode, prefix, rev, type, subrepos, include, exclude
201 202 backout: merge, parent, tool, rev, include, exclude, message, logfile, date, user
202 203 bisect: reset, good, bad, skip, extend, command, noupdate
203 204 bookmarks: force, rev, delete, rename
204 205 branch: force, clean
205 206 branches: active, closed
206 207 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
207 208 cat: output, rev, decode, include, exclude
208 209 copy: after, force, include, exclude, dry-run
209 210 debugancestor:
210 211 debugbuilddag: mergeable-file, appended-file, overwritten-file, new-file
211 212 debugcheckstate:
212 213 debugcommands:
213 214 debugcomplete: options
214 215 debugdag: tags, branches, dots, spaces
215 216 debugdata:
216 217 debugdate: extended
217 218 debugfsinfo:
218 219 debugignore:
219 220 debugindex: format
220 221 debugindexdot:
221 222 debuginstall:
222 223 debugpushkey:
223 224 debugrebuildstate: rev
224 225 debugrename: rev
225 226 debugrevspec:
226 227 debugsetparents:
227 228 debugstate: nodates
228 229 debugsub: rev
229 230 debugwalk: include, exclude
231 debugwireargs: three, four, ssh, remotecmd, insecure
230 232 grep: print0, all, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
231 233 heads: rev, topo, active, closed, style, template
232 234 help:
233 235 identify: rev, num, id, branch, tags, bookmarks
234 236 import: strip, base, force, no-commit, exact, import-branch, message, logfile, date, user, similarity
235 237 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
236 238 locate: rev, print0, fullpath, include, exclude
237 239 manifest: rev
238 240 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
239 241 parents: rev, style, template
240 242 paths:
241 243 recover:
242 244 rename: after, force, include, exclude, dry-run
243 245 resolve: all, list, mark, unmark, tool, no-status, include, exclude
244 246 revert: all, date, rev, no-backup, include, exclude, dry-run
245 247 rollback: dry-run
246 248 root:
247 249 showconfig: untrusted
248 250 tag: force, local, rev, remove, edit, message, date, user
249 251 tags:
250 252 tip: patch, git, style, template
251 253 unbundle: update
252 254 verify:
253 255 version:
General Comments 0
You need to be logged in to leave comments. Login now