##// END OF EJS Templates
Revert changeset 120aa5fc7ced1bf765b4f025f5a3a138cd87f49e....
bos@serpentine.internal.keyresearch.com -
r1191:77a0c752 default
parent child Browse files
Show More
@@ -1,723 +1,717 b''
1 1 HG(1)
2 2 =====
3 3 Matt Mackall <mpm@selenic.com>
4 4
5 5 NAME
6 6 ----
7 7 hg - Mercurial source code management system
8 8
9 9 SYNOPSIS
10 10 --------
11 11 'hg' [-v -d -q -y] <command> [command options] [files]
12 12
13 13 DESCRIPTION
14 14 -----------
15 15 The hg(1) command provides a command line interface to the Mercurial system.
16 16
17 17 OPTIONS
18 18 -------
19 19
20 20 -R, --repository::
21 21 repository root directory
22 22
23 23 --cwd::
24 24 change working directory
25 25
26 26 -y, --noninteractive::
27 27 do not prompt, assume 'yes' for any required answers
28 28
29 29 -q, --quiet::
30 30 suppress output
31 31
32 32 -v, --verbose::
33 33 enable additional output
34 34
35 35 --debug::
36 36 enable debugging output
37 37
38 38 --traceback::
39 39 print traceback on exception
40 40
41 41 --time::
42 42 time how long the command takes
43 43
44 44 --profile::
45 45 print command execution profile
46 46
47 47 --version::
48 48 output version information and exit
49 49
50 50 -h, --help::
51 51 display help and exit
52 52
53 53 COMMAND ELEMENTS
54 54 ----------------
55 55
56 56 files ...::
57 57 indicates one or more filename or relative path filenames; see
58 58 "FILE NAME PATTERNS" for information on pattern matching
59 59
60 60 path::
61 61 indicates a path on the local machine
62 62
63 63 revision::
64 64 indicates a changeset which can be specified as a changeset revision
65 65 number, a tag, or a unique substring of the changeset hash value
66 66
67 67 repository path::
68 68 either the pathname of a local repository or the URI of a remote
69 69 repository. There are two available URI protocols, http:// which is
70 70 fast and the old-http:// protocol which is much slower but does not
71 71 require a special server on the web host.
72 72
73 73 COMMANDS
74 74 --------
75 75
76 76 add [options] [files ...]::
77 77 Schedule files to be version controlled and added to the repository.
78 78
79 79 The files will be added to the repository at the next commit.
80 80
81 81 If no names are given, add all files in the current directory and
82 82 its subdirectories.
83 83
84 84 addremove [options] [files ...]::
85 85 Add all new files and remove all missing files from the repository.
86 86
87 87 New files are ignored if they match any of the patterns in .hgignore. As
88 88 with add, these changes take effect at the next commit.
89 89
90 90 annotate [-r <rev> -u -n -c] [files ...]::
91 91 List changes in files, showing the revision id responsible for each line
92 92
93 93 This command is useful to discover who did a change or when a change took
94 94 place.
95 95
96 96 Without the -a option, annotate will avoid processing files it
97 97 detects as binary. With -a, annotate will generate an annotation
98 98 anyway, probably with undesirable results.
99 99
100 100 options:
101 101 -a, --text treat all files as text
102 102 -I, --include <pat> include names matching the given patterns
103 103 -X, --exclude <pat> exclude names matching the given patterns
104 104 -r, --revision <rev> annotate the specified revision
105 105 -u, --user list the author
106 106 -c, --changeset list the changeset
107 107 -n, --number list the revision number (default)
108 108
109 109 cat <file> [revision]::
110 110 Output to stdout the given revision for the specified file.
111 111
112 112 If no revision is given then the tip is used.
113 113
114 114 clone [-U] <source> [dest]::
115 115 Create a copy of an existing repository in a new directory.
116 116
117 117 If no destination directory name is specified, it defaults to the
118 118 basename of the source.
119 119
120 120 The source is added to the new repository's .hg/hgrc file to be used in
121 121 future pulls.
122 122
123 123 For efficiency, hardlinks are used for cloning whenever the
124 124 source and destination are on the same filesystem.
125 125
126 126 options:
127 127 -U, --noupdate do not update the new working directory
128 128 -e, --ssh specify ssh command to use
129 129 --remotecmd specify hg command to run on the remote side
130 130
131 131 commit [options] [files...]::
132 132 Commit changes to the given files into the repository.
133 133
134 134 If a list of files is omitted, all changes reported by "hg status"
135 135 from the root of the repository will be commited.
136 136
137 137 The HGEDITOR or EDITOR environment variables are used to start an
138 138 editor to add a commit comment.
139 139
140 140 Options:
141 141
142 142 -A, --addremove run addremove during commit
143 143 -I, --include <pat> include names matching the given patterns
144 144 -X, --exclude <pat> exclude names matching the given patterns
145 145 -m, --message <text> use <text> as commit message
146 146 -l, --logfile <file> read the commit message from <file>
147 147 -d, --date <datecode> record datecode as commit date
148 148 -u, --user <user> record user as commiter
149 149
150 150 aliases: ci
151 151
152 152 copy <source> <dest>::
153 153 Mark <dest> file as a copy or rename of a <source> one
154 154
155 155 This command takes effect for the next commit.
156 156
157 157 diff [-a] [-r revision] [-r revision] [files ...]::
158 158 Show differences between revisions for the specified files.
159 159
160 160 Differences between files are shown using the unified diff format.
161 161
162 162 When two revision arguments are given, then changes are shown
163 163 between those revisions. If only one revision is specified then
164 164 that revision is compared to the working directory, and, when no
165 165 revisions are specified, the working directory files are compared
166 166 to its parent.
167 167
168 168 Without the -a option, diff will avoid generating diffs of files
169 169 it detects as binary. With -a, diff will generate a diff anyway,
170 170 probably with undesirable results.
171 171
172 172 options:
173 173 -a, --text treat all files as text
174 174 -I, --include <pat> include names matching the given patterns
175 175 -X, --exclude <pat> exclude names matching the given patterns
176 176
177 177 export [-o filespec] [revision] ...::
178 178 Print the changeset header and diffs for one or more revisions.
179 179
180 180 The information shown in the changeset header is: author,
181 181 changeset hash, parent and commit comment.
182 182
183 183 Output may be to a file, in which case the name of the file is
184 184 given using a format string. The formatting rules are as follows:
185 185
186 186 %% literal "%" character
187 187 %H changeset hash (40 bytes of hexadecimal)
188 188 %N number of patches being generated
189 189 %R changeset revision number
190 190 %b basename of the exporting repository
191 191 %h short-form changeset hash (12 bytes of hexadecimal)
192 192 %n zero-padded sequence number, starting at 1
193 193 %r zero-padded changeset revision number
194 194
195 195 Without the -a option, export will avoid generating diffs of files
196 196 it detects as binary. With -a, export will generate a diff anyway,
197 197 probably with undesirable results.
198 198
199 199 options:
200 200 -a, --text treat all files as text
201 201 -o, --output <filespec> print output to file with formatted named
202 202
203 203 forget [options] [files]::
204 204 Undo an 'hg add' scheduled for the next commit.
205 205
206 206 options:
207 207 -I, --include <pat> include names matching the given patterns
208 208 -X, --exclude <pat> exclude names matching the given patterns
209 209
210 210 grep [options] pattern [files]::
211 211 Search revisions of files for a regular expression.
212 212
213 213 This command behaves differently than Unix grep. It only accepts
214 214 Python/Perl regexps. It searches repository history, not the
215 215 working directory. It always prints the revision number in which
216 216 a match appears.
217 217
218 218 By default, grep only prints output for the first revision of a
219 219 file in which it finds a match. To get it to print every revision
220 220 that contains a change in match status ("-" for a match that
221 221 becomes a non-match, or "+" for a non-match that becomes a match),
222 222 use the --every-match flag.
223 223
224 224 options:
225 225 -0, --print0 end fields with NUL
226 226 -I, --include <pat> include names matching the given patterns
227 227 -X, --exclude <pat> exclude names matching the given patterns
228 228 -e, --every-match print every revision that matches
229 229 -i, --ignore-case ignore case when matching
230 230 -l, --files-with-matches print only file names and revs that match
231 231 -n, --line-number print matching line numbers
232 232 -r <rev>, --rev <rev> search in given revision range
233 233 -u, --user print user who committed change
234 234
235 235 heads::
236 236 Show all repository head changesets.
237 237
238 238 Repository "heads" are changesets that don't have children
239 239 changesets. They are where development generally takes place and
240 240 are the usual targets for update and merge operations.
241 241
242 242 identify::
243 243 Print a short summary of the current state of the repo.
244 244
245 245 This summary identifies the repository state using one or two parent
246 246 hash identifiers, followed by a "+" if there are uncommitted changes
247 247 in the working directory, followed by a list of tags for this revision.
248 248
249 249 aliases: id
250 250
251 251 import [-p <n> -b <base> -f] <patches>::
252 252 Import a list of patches and commit them individually.
253 253
254 254 If a patch looks like a mail message (its first line starts with
255 255 "From " or looks like an RFC822 header), it will not be applied
256 256 unless the -m option is used. The importer neither parses nor
257 257 discards mail headers, so use -m only to override the "mailness"
258 258 safety check, not to import a real mail message.
259 259
260 260 options:
261 261 -p, --strip <n> directory strip option for patch. This has the same
262 262 meaning as the corresponding patch option
263 263 -b <path> base directory to read patches from
264 264 -f, --force skip check for outstanding uncommitted changes
265 265 -m, --mail-like apply a patch that appears to be a mail message
266 266
267 267 aliases: patch
268 268
269 incoming [-p] [source]::
269 incoming [source]::
270 270 Show new changesets found in the specified repo or the default
271 271 pull repo. These are the changesets that would be pulled if a pull
272 272 was requested.
273 273
274 274 Currently only local repositories are supported.
275 275
276 options:
277 -p, --patch show patch
278
279 276 aliases: in
280 277
281 278 init [dest]::
282 279 Initialize a new repository in the given directory. If the given
283 280 directory does not exist, it is created.
284 281
285 282 If no directory is given, the current directory is used.
286 283
287 284 locate [options] [files]::
288 285 Print all files under Mercurial control whose names match the
289 286 given patterns.
290 287
291 288 This command searches the current directory and its
292 289 subdirectories. To search an entire repository, move to the root
293 290 of the repository.
294 291
295 292 If no patterns are given to match, this command prints all file
296 293 names.
297 294
298 295 If you want to feed the output of this command into the "xargs"
299 296 command, use the "-0" option to both this command and "xargs".
300 297 This will avoid the problem of "xargs" treating single filenames
301 298 that contain white space as multiple file names.
302 299
303 300 options:
304 301
305 302 -0, --print0 end filenames with NUL, for use with xargs
306 303 -f, --fullpath print complete paths from the filesystem root
307 304 -I, --include <pat> include names matching the given patterns
308 305 -r, --rev <rev> search the repository as it stood at rev
309 306 -X, --exclude <pat> exclude names matching the given patterns
310 307
311 308 log [-r revision ...] [-p] [files]::
312 309 Print the revision history of the specified files or the entire project.
313 310
314 311 By default this command outputs: changeset id and hash, tags,
315 312 parents, user, date and time, and a summary for each commit. The
316 313 -v switch adds some more detail, such as changed files, manifest
317 314 hashes or message signatures.
318 315
319 316 options:
320 317 -I, --include <pat> include names matching the given patterns
321 318 -X, --exclude <pat> exclude names matching the given patterns
322 319 -r, --rev <A> show the specified revision or range
323 320 -p, --patch show patch
324 321
325 322 aliases: history
326 323
327 324 manifest [revision]::
328 325 Print a list of version controlled files for the given revision.
329 326
330 327 The manifest is the list of files being version controlled. If no revision
331 328 is given then the tip is used.
332 329
333 outgoing [-p] [dest]::
330 outgoing [dest]::
334 331 Show changesets not found in the specified destination repo or the
335 332 default push repo. These are the changesets that would be pushed
336 333 if a push was requested.
337 334
338 options:
339 -p, --patch show patch
340
341 335 aliases: out
342 336
343 337 parents::
344 338 Print the working directory's parent revisions.
345 339
346 340 paths [NAME]::
347 341 Show definition of symbolic path name NAME. If no name is given, show
348 342 definition of available names.
349 343
350 344 Path names are defined in the [paths] section of /etc/mercurial/hgrc
351 345 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
352 346
353 347 pull <repository path>::
354 348 Pull changes from a remote repository to a local one.
355 349
356 350 This finds all changes from the repository at the specified path
357 351 or URL and adds them to the local repository. By default, this
358 352 does not update the copy of the project in the working directory.
359 353
360 354 Valid URLs are of the form:
361 355
362 356 local/filesystem/path
363 357 http://[user@]host[:port][/path]
364 358 https://[user@]host[:port][/path]
365 359 ssh://[user@]host[:port][/path]
366 360
367 361 SSH requires an accessible shell account on the destination
368 362 machine and a copy of hg in the remote path.
369 363
370 364 options:
371 365 -u, --update update the working directory to tip after pull
372 366 -e, --ssh specify ssh command to use
373 367 --remotecmd specify hg command to run on the remote side
374 368
375 369 push <destination>::
376 370 Push changes from the local repository to the given destination.
377 371
378 372 This is the symmetrical operation for pull. It helps to move
379 373 changes from the current repository to a different one. If the
380 374 destination is local this is identical to a pull in that directory
381 375 from the current one.
382 376
383 377 By default, push will refuse to run if it detects the result would
384 378 increase the number of remote heads. This generally indicates the
385 379 the client has forgotten to sync and merge before pushing.
386 380
387 381 Valid URLs are of the form:
388 382
389 383 local/filesystem/path
390 384 ssh://[user@]host[:port][/path]
391 385
392 386 SSH requires an accessible shell account on the destination
393 387 machine and a copy of hg in the remote path.
394 388
395 389 options:
396 390
397 391 -f, --force force update
398 392 -e, --ssh specify ssh command to use
399 393 --remotecmd specify hg command to run on the remote side
400 394
401 395 rawcommit [-p -d -u -F -m -l]::
402 396 Lowlevel commit, for use in helper scripts.
403 397
404 398 This command is not intended to be used by normal users, as it is
405 399 primarily useful for importing from other SCMs.
406 400
407 401 recover::
408 402 Recover from an interrupted commit or pull.
409 403
410 404 This command tries to fix the repository status after an interrupted
411 405 operation. It should only be necessary when Mercurial suggests it.
412 406
413 407 remove [options] [files ...]::
414 408 Schedule the indicated files for removal from the repository.
415 409
416 410 This command schedules the files to be removed at the next commit.
417 411 This only removes files from the current branch, not from the
418 412 entire project history.
419 413
420 414 aliases: rm
421 415
422 416 revert [names ...]::
423 417 Revert any uncommitted modifications made to the named files or
424 418 directories. This restores the contents of the affected files to
425 419 an unmodified state.
426 420
427 421 If a file has been deleted, it is recreated. If the executable
428 422 mode of a file was changed, it is reset.
429 423
430 424 If a directory is given, all files in that directory and its
431 425 subdirectories are reverted.
432 426
433 427 If no arguments are given, all files in the current directory and
434 428 its subdirectories are reverted.
435 429
436 430 options:
437 431 -r, --rev <rev> revision to revert to
438 432 -n, --nonrecursive do not recurse into subdirectories
439 433
440 434 root::
441 435 Print the root directory of the current repository.
442 436
443 437 serve [options]::
444 438 Start a local HTTP repository browser and pull server.
445 439
446 440 By default, the server logs accesses to stdout and errors to
447 441 stderr. Use the "-A" and "-E" options to log to files.
448 442
449 443 options:
450 444 -A, --accesslog <file> name of access log file to write to
451 445 -E, --errorlog <file> name of error log file to write to
452 446 -a, --address <addr> address to use
453 447 -p, --port <n> port to use (default: 8000)
454 448 -n, --name <name> name to show in web pages (default: working dir)
455 449 -t, --templatedir <path> web templates to use
456 450 -6, --ipv6 use IPv6 in addition to IPv4
457 451
458 452 status [options] [files]::
459 453 Show changed files in the working directory. If no names are
460 454 given, all files are shown. Otherwise, only files matching the
461 455 given names are shown.
462 456
463 457 The codes used to show the status of files are:
464 458
465 459 M = changed
466 460 A = added
467 461 R = removed
468 462 ? = not tracked
469 463
470 464 options:
471 465
472 466 -m, --modified show only modified files
473 467 -a, --added show only added files
474 468 -r, --removed show only removed files
475 469 -u, --unknown show only unknown (not tracked) files
476 470 -n, --no-status hide status prefix
477 471 -0, --print0 end filenames with NUL, for use with xargs
478 472 -I, --include <pat> include names matching the given patterns
479 473 -X, --exclude <pat> exclude names matching the given patterns
480 474
481 475 tag [-l -m <text> -d <datecode> -u <user>] <name> [revision]::
482 476 Name a particular revision using <name>.
483 477
484 478 Tags are used to name particular revisions of the repository and are
485 479 very useful to compare different revision, to go back to significant
486 480 earlier versions or to mark branch points as releases, etc.
487 481
488 482 If no revision is given, the tip is used.
489 483
490 484 To facilitate version control, distribution, and merging of tags,
491 485 they are stored as a file named ".hgtags" which is managed
492 486 similarly to other project files and can be hand-edited if
493 487 necessary.
494 488
495 489 options:
496 490 -l, --local make the tag local
497 491 -m, --message <text> message for tag commit log entry
498 492 -d, --date <datecode> datecode for commit
499 493 -u, --user <user> user for commit
500 494
501 495 Note: Local tags are not version-controlled or distributed and are
502 496 stored in the .hg/localtags file. If there exists a local tag and
503 497 a public tag with the same name, local tag is used.
504 498
505 499 tags::
506 500 List the repository tags.
507 501
508 502 This lists both regular and local tags.
509 503
510 504 tip::
511 505 Show the tip revision.
512 506
513 507 undo::
514 508 Undo the last commit or pull transaction.
515 509
516 510 Roll back the last pull or commit transaction on the
517 511 repository, restoring the project to its earlier state.
518 512
519 513 This command should be used with care. There is only one level of
520 514 undo and there is no redo.
521 515
522 516 This command is not intended for use on public repositories. Once
523 517 a change is visible for pull by other users, undoing it locally is
524 518 ineffective.
525 519
526 520 update [-m -C] [revision]::
527 521 Update the working directory to the specified revision.
528 522
529 523 By default, update will refuse to run if doing so would require
530 524 merging or discarding local changes.
531 525
532 526 With the -m option, a merge will be performed.
533 527
534 528 With the -C option, local changes will be lost.
535 529
536 530 options:
537 531 -m, --merge allow merging of branches
538 532 -C, --clean overwrite locally modified files
539 533
540 534 aliases: up checkout co
541 535
542 536 verify::
543 537 Verify the integrity of the current repository.
544 538
545 539 This will perform an extensive check of the repository's
546 540 integrity, validating the hashes and checksums of each entry in
547 541 the changelog, manifest, and tracked files, as well as the
548 542 integrity of their crosslinks and indices.
549 543
550 544 FILE NAME PATTERNS
551 545 ------------------
552 546
553 547 Mercurial accepts several notations for identifying one or more
554 548 file at a time.
555 549
556 550 By default, Mercurial treats file names as shell-style extended
557 551 glob patterns.
558 552
559 553 Alternate pattern notations must be specified explicitly.
560 554
561 555 To use a plain path name without any pattern matching, start a
562 556 name with "path:". These path names must match completely, from
563 557 the root of the current repository.
564 558
565 559 To use an extended glob, start a name with "glob:". Globs are
566 560 rooted at the current directory; a glob such as "*.c" will match
567 561 files ending in ".c" in the current directory only.
568 562
569 563 The supported glob syntax extensions are "**" to match any string
570 564 across path separators, and "{a,b}" to mean "a or b".
571 565
572 566 To use a Perl/Python regular expression, start a name with "re:".
573 567 Regexp pattern matching is anchored at the root of the repository.
574 568
575 569 Plain examples:
576 570
577 571 path:foo/bar a name bar in a directory named foo in the root of
578 572 the repository
579 573 path:path:name a file or directory named "path:name"
580 574
581 575 Glob examples:
582 576
583 577 glob:*.c any name ending in ".c" in the current directory
584 578 *.c any name ending in ".c" in the current directory
585 579 **.c any name ending in ".c" in the current directory, or
586 580 any subdirectory
587 581 foo/*.c any name ending in ".c" in the directory foo
588 582 foo/**.c any name ending in ".c" in the directory foo, or any
589 583 subdirectory
590 584
591 585 Regexp examples:
592 586
593 587 re:.*\.c$ any name ending in ".c", anywhere in the repository
594 588
595 589
596 590 SPECIFYING SINGLE REVISIONS
597 591 ---------------------------
598 592
599 593 Mercurial accepts several notations for identifying individual
600 594 revisions.
601 595
602 596 A plain integer is treated as a revision number. Negative
603 597 integers are treated as offsets from the tip, with -1 denoting the
604 598 tip.
605 599
606 600 A 40-digit hexadecimal string is treated as a unique revision
607 601 identifier.
608 602
609 603 A hexadecimal string less than 40 characters long is treated as a
610 604 unique revision identifier, and referred to as a short-form
611 605 identifier. A short-form identifier is only valid if it is the
612 606 prefix of one full-length identifier.
613 607
614 608 Any other string is treated as a tag name, which is a symbolic
615 609 name associated with a revision identifier. Tag names may not
616 610 contain the ":" character.
617 611
618 612 The reserved name "tip" is a special tag that always identifies
619 613 the most recent revision.
620 614
621 615 SPECIFYING MULTIPLE REVISIONS
622 616 -----------------------------
623 617
624 618 When Mercurial accepts more than one revision, they may be
625 619 specified individually, or provided as a continuous range,
626 620 separated by the ":" character.
627 621
628 622 The syntax of range notation is [BEGIN]:[END], where BEGIN and END
629 623 are revision identifiers. Both BEGIN and END are optional. If
630 624 BEGIN is not specified, it defaults to revision number 0. If END
631 625 is not specified, it defaults to the tip. The range ":" thus
632 626 means "all revisions".
633 627
634 628 If BEGIN is greater than END, revisions are treated in reverse
635 629 order.
636 630
637 631 A range acts as a closed interval. This means that a range of 3:5
638 632 gives 3, 4 and 5. Similarly, a range of 4:2 gives 4, 3, and 2.
639 633
640 634 ENVIRONMENT VARIABLES
641 635 ---------------------
642 636
643 637 HGEDITOR::
644 638 This is the name of the editor to use when committing. Defaults to the
645 639 value of EDITOR.
646 640
647 641 (deprecated, use .hgrc)
648 642
649 643 HGMERGE::
650 644 An executable to use for resolving merge conflicts. The program
651 645 will be executed with three arguments: local file, remote file,
652 646 ancestor file.
653 647
654 648 The default program is "hgmerge", which is a shell script provided
655 649 by Mercurial with some sensible defaults.
656 650
657 651 (deprecated, use .hgrc)
658 652
659 653 HGUSER::
660 654 This is the string used for the author of a commit.
661 655
662 656 (deprecated, use .hgrc)
663 657
664 658 EMAIL::
665 659 If HGUSER is not set, this will be used as the author for a commit.
666 660
667 661 LOGNAME::
668 662 If neither HGUSER nor EMAIL is set, LOGNAME will be used (with
669 663 '@hostname' appended) as the author value for a commit.
670 664
671 665 EDITOR::
672 666 This is the name of the editor used in the hgmerge script. It will be
673 667 used for commit messages if HGEDITOR isn't set. Defaults to 'vi'.
674 668
675 669 PYTHONPATH::
676 670 This is used by Python to find imported modules and may need to be set
677 671 appropriately if Mercurial is not installed system-wide.
678 672
679 673 FILES
680 674 -----
681 675 .hgignore::
682 676 This file contains regular expressions (one per line) that describe file
683 677 names that should be ignored by hg.
684 678
685 679 .hgtags::
686 680 This file contains changeset hash values and text tag names (one of each
687 681 separated by spaces) that correspond to tagged versions of the repository
688 682 contents.
689 683
690 684 /etc/mercurial/hgrc, $HOME/.hgrc, .hg/hgrc::
691 685 This file contains defaults and configuration. Values in .hg/hgrc
692 686 override those in $HOME/.hgrc, and these override settings made in the
693 687 global /etc/mercurial/hgrc configuration. See hgrc(5) for details of
694 688 the contents and format of these files.
695 689
696 690 BUGS
697 691 ----
698 692 Probably lots, please post them to the mailing list (See Resources below)
699 693 when you find them.
700 694
701 695 SEE ALSO
702 696 --------
703 697 hgrc(5)
704 698
705 699 AUTHOR
706 700 ------
707 701 Written by Matt Mackall <mpm@selenic.com>
708 702
709 703 RESOURCES
710 704 ---------
711 705 http://selenic.com/mercurial[Main Web Site]
712 706
713 707 http://www.serpentine.com/mercurial[Wiki site]
714 708
715 709 http://selenic.com/hg[Source code repository]
716 710
717 711 http://selenic.com/mailman/listinfo/mercurial[Mailing list]
718 712
719 713 COPYING
720 714 -------
721 715 Copyright (C) 2005 Matt Mackall.
722 716 Free use of this software is granted under the terms of the GNU General
723 717 Public License (GPL).
@@ -1,1991 +1,1979 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import demandload
9 9 from node import *
10 10 demandload(globals(), "os re sys signal shutil imp")
11 11 demandload(globals(), "fancyopts ui hg util lock revlog")
12 12 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
13 13 demandload(globals(), "errno socket version struct atexit sets")
14 14
15 15 class UnknownCommand(Exception):
16 16 """Exception raised if command is not in the command table."""
17 17
18 18 def filterfiles(filters, files):
19 19 l = [x for x in files if x in filters]
20 20
21 21 for t in filters:
22 22 if t and t[-1] != "/":
23 23 t += "/"
24 24 l += [x for x in files if x.startswith(t)]
25 25 return l
26 26
27 27 def relpath(repo, args):
28 28 cwd = repo.getcwd()
29 29 if cwd:
30 30 return [util.normpath(os.path.join(cwd, x)) for x in args]
31 31 return args
32 32
33 33 def matchpats(repo, cwd, pats=[], opts={}, head=''):
34 34 return util.matcher(repo.root, cwd, pats or ['.'], opts.get('include'),
35 35 opts.get('exclude'), head)
36 36
37 37 def makewalk(repo, pats, opts, head=''):
38 38 cwd = repo.getcwd()
39 39 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
40 40 exact = dict(zip(files, files))
41 41 def walk():
42 42 for src, fn in repo.walk(files=files, match=matchfn):
43 43 yield src, fn, util.pathto(cwd, fn), fn in exact
44 44 return files, matchfn, walk()
45 45
46 46 def walk(repo, pats, opts, head=''):
47 47 files, matchfn, results = makewalk(repo, pats, opts, head)
48 48 for r in results:
49 49 yield r
50 50
51 51 def walkchangerevs(ui, repo, cwd, pats, opts):
52 52 '''Iterate over files and the revs they changed in.
53 53
54 54 Callers most commonly need to iterate backwards over the history
55 55 it is interested in. Doing so has awful (quadratic-looking)
56 56 performance, so we use iterators in a "windowed" way.
57 57
58 58 We walk a window of revisions in the desired order. Within the
59 59 window, we first walk forwards to gather data, then in the desired
60 60 order (usually backwards) to display it.
61 61
62 62 This function returns an (iterator, getchange) pair. The
63 63 getchange function returns the changelog entry for a numeric
64 64 revision. The iterator yields 3-tuples. They will be of one of
65 65 the following forms:
66 66
67 67 "window", incrementing, lastrev: stepping through a window,
68 68 positive if walking forwards through revs, last rev in the
69 69 sequence iterated over - use to reset state for the current window
70 70
71 71 "add", rev, fns: out-of-order traversal of the given file names
72 72 fns, which changed during revision rev - use to gather data for
73 73 possible display
74 74
75 75 "iter", rev, None: in-order traversal of the revs earlier iterated
76 76 over with "add" - use to display data'''
77 77 cwd = repo.getcwd()
78 78 if not pats and cwd:
79 79 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
80 80 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
81 81 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
82 82 pats, opts)
83 83 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
84 84 wanted = {}
85 85 slowpath = anypats
86 86 window = 300
87 87 fncache = {}
88 88
89 89 chcache = {}
90 90 def getchange(rev):
91 91 ch = chcache.get(rev)
92 92 if ch is None:
93 93 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
94 94 return ch
95 95
96 96 if not slowpath and not files:
97 97 # No files, no patterns. Display all revs.
98 98 wanted = dict(zip(revs, revs))
99 99 if not slowpath:
100 100 # Only files, no patterns. Check the history of each file.
101 101 def filerevgen(filelog):
102 102 for i in xrange(filelog.count() - 1, -1, -window):
103 103 revs = []
104 104 for j in xrange(max(0, i - window), i + 1):
105 105 revs.append(filelog.linkrev(filelog.node(j)))
106 106 revs.reverse()
107 107 for rev in revs:
108 108 yield rev
109 109
110 110 minrev, maxrev = min(revs), max(revs)
111 111 for file in files:
112 112 filelog = repo.file(file)
113 113 # A zero count may be a directory or deleted file, so
114 114 # try to find matching entries on the slow path.
115 115 if filelog.count() == 0:
116 116 slowpath = True
117 117 break
118 118 for rev in filerevgen(filelog):
119 119 if rev <= maxrev:
120 120 if rev < minrev:
121 121 break
122 122 fncache.setdefault(rev, [])
123 123 fncache[rev].append(file)
124 124 wanted[rev] = 1
125 125 if slowpath:
126 126 # The slow path checks files modified in every changeset.
127 127 def changerevgen():
128 128 for i in xrange(repo.changelog.count() - 1, -1, -window):
129 129 for j in xrange(max(0, i - window), i + 1):
130 130 yield j, getchange(j)[3]
131 131
132 132 for rev, changefiles in changerevgen():
133 133 matches = filter(matchfn, changefiles)
134 134 if matches:
135 135 fncache[rev] = matches
136 136 wanted[rev] = 1
137 137
138 138 def iterate():
139 139 for i in xrange(0, len(revs), window):
140 140 yield 'window', revs[0] < revs[-1], revs[-1]
141 141 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
142 142 if rev in wanted]
143 143 srevs = list(nrevs)
144 144 srevs.sort()
145 145 for rev in srevs:
146 146 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
147 147 yield 'add', rev, fns
148 148 for rev in nrevs:
149 149 yield 'iter', rev, None
150 150 return iterate(), getchange
151 151
152 152 revrangesep = ':'
153 153
154 154 def revrange(ui, repo, revs, revlog=None):
155 155 """Yield revision as strings from a list of revision specifications."""
156 156 if revlog is None:
157 157 revlog = repo.changelog
158 158 revcount = revlog.count()
159 159 def fix(val, defval):
160 160 if not val:
161 161 return defval
162 162 try:
163 163 num = int(val)
164 164 if str(num) != val:
165 165 raise ValueError
166 166 if num < 0:
167 167 num += revcount
168 168 if not (0 <= num < revcount):
169 169 raise ValueError
170 170 except ValueError:
171 171 try:
172 172 num = repo.changelog.rev(repo.lookup(val))
173 173 except KeyError:
174 174 try:
175 175 num = revlog.rev(revlog.lookup(val))
176 176 except KeyError:
177 177 raise util.Abort('invalid revision identifier %s', val)
178 178 return num
179 179 seen = {}
180 180 for spec in revs:
181 181 if spec.find(revrangesep) >= 0:
182 182 start, end = spec.split(revrangesep, 1)
183 183 start = fix(start, 0)
184 184 end = fix(end, revcount - 1)
185 185 step = start > end and -1 or 1
186 186 for rev in xrange(start, end+step, step):
187 187 if rev in seen: continue
188 188 seen[rev] = 1
189 189 yield str(rev)
190 190 else:
191 191 rev = fix(spec, None)
192 192 if rev in seen: continue
193 193 seen[rev] = 1
194 194 yield str(rev)
195 195
196 196 def make_filename(repo, r, pat, node=None,
197 197 total=None, seqno=None, revwidth=None):
198 198 node_expander = {
199 199 'H': lambda: hex(node),
200 200 'R': lambda: str(r.rev(node)),
201 201 'h': lambda: short(node),
202 202 }
203 203 expander = {
204 204 '%': lambda: '%',
205 205 'b': lambda: os.path.basename(repo.root),
206 206 }
207 207
208 208 try:
209 209 if node:
210 210 expander.update(node_expander)
211 211 if node and revwidth is not None:
212 212 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
213 213 if total is not None:
214 214 expander['N'] = lambda: str(total)
215 215 if seqno is not None:
216 216 expander['n'] = lambda: str(seqno)
217 217 if total is not None and seqno is not None:
218 218 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
219 219
220 220 newname = []
221 221 patlen = len(pat)
222 222 i = 0
223 223 while i < patlen:
224 224 c = pat[i]
225 225 if c == '%':
226 226 i += 1
227 227 c = pat[i]
228 228 c = expander[c]()
229 229 newname.append(c)
230 230 i += 1
231 231 return ''.join(newname)
232 232 except KeyError, inst:
233 233 raise util.Abort("invalid format spec '%%%s' in output file name",
234 234 inst.args[0])
235 235
236 236 def make_file(repo, r, pat, node=None,
237 237 total=None, seqno=None, revwidth=None, mode='wb'):
238 238 if not pat or pat == '-':
239 239 return 'w' in mode and sys.stdout or sys.stdin
240 240 if hasattr(pat, 'write') and 'w' in mode:
241 241 return pat
242 242 if hasattr(pat, 'read') and 'r' in mode:
243 243 return pat
244 244 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
245 245 mode)
246 246
247 247 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
248 248 changes=None, text=False):
249 249 def date(c):
250 250 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
251 251
252 252 if not changes:
253 253 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
254 254 else:
255 255 (c, a, d, u) = changes
256 256 if files:
257 257 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
258 258
259 259 if not c and not a and not d:
260 260 return
261 261
262 262 if node2:
263 263 change = repo.changelog.read(node2)
264 264 mmap2 = repo.manifest.read(change[0])
265 265 date2 = date(change)
266 266 def read(f):
267 267 return repo.file(f).read(mmap2[f])
268 268 else:
269 269 date2 = time.asctime()
270 270 if not node1:
271 271 node1 = repo.dirstate.parents()[0]
272 272 def read(f):
273 273 return repo.wfile(f).read()
274 274
275 275 if ui.quiet:
276 276 r = None
277 277 else:
278 278 hexfunc = ui.verbose and hex or short
279 279 r = [hexfunc(node) for node in [node1, node2] if node]
280 280
281 281 change = repo.changelog.read(node1)
282 282 mmap = repo.manifest.read(change[0])
283 283 date1 = date(change)
284 284
285 285 for f in c:
286 286 to = None
287 287 if f in mmap:
288 288 to = repo.file(f).read(mmap[f])
289 289 tn = read(f)
290 290 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
291 291 for f in a:
292 292 to = None
293 293 tn = read(f)
294 294 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
295 295 for f in d:
296 296 to = repo.file(f).read(mmap[f])
297 297 tn = None
298 298 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
299 299
300 300 def trimuser(ui, name, rev, revcache):
301 301 """trim the name of the user who committed a change"""
302 302 user = revcache.get(rev)
303 303 if user is None:
304 304 user = revcache[rev] = ui.shortuser(name)
305 305 return user
306 306
307 307 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
308 308 """show a single changeset or file revision"""
309 309 log = repo.changelog
310 310 if changenode is None:
311 311 changenode = log.node(rev)
312 312 elif not rev:
313 313 rev = log.rev(changenode)
314 314
315 315 if ui.quiet:
316 316 ui.write("%d:%s\n" % (rev, short(changenode)))
317 317 return
318 318
319 319 changes = log.read(changenode)
320 320
321 321 t, tz = changes[2].split(' ')
322 322 # a conversion tool was sticking non-integer offsets into repos
323 323 try:
324 324 tz = int(tz)
325 325 except ValueError:
326 326 tz = 0
327 327 date = time.asctime(time.localtime(float(t))) + " %+05d" % (int(tz)/-36)
328 328
329 329 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
330 330 for p in log.parents(changenode)
331 331 if ui.debugflag or p != nullid]
332 332 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
333 333 parents = []
334 334
335 335 if ui.verbose:
336 336 ui.write("changeset: %d:%s\n" % (rev, hex(changenode)))
337 337 else:
338 338 ui.write("changeset: %d:%s\n" % (rev, short(changenode)))
339 339
340 340 for tag in repo.nodetags(changenode):
341 341 ui.status("tag: %s\n" % tag)
342 342 for parent in parents:
343 343 ui.write("parent: %d:%s\n" % parent)
344 344
345 345 if brinfo and changenode in brinfo:
346 346 br = brinfo[changenode]
347 347 ui.write("branch: %s\n" % " ".join(br))
348 348
349 349 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
350 350 hex(changes[0])))
351 351 ui.status("user: %s\n" % changes[1])
352 352 ui.status("date: %s\n" % date)
353 353
354 354 if ui.debugflag:
355 355 files = repo.changes(log.parents(changenode)[0], changenode)
356 356 for key, value in zip(["files:", "files+:", "files-:"], files):
357 357 if value:
358 358 ui.note("%-12s %s\n" % (key, " ".join(value)))
359 359 else:
360 360 ui.note("files: %s\n" % " ".join(changes[3]))
361 361
362 362 description = changes[4].strip()
363 363 if description:
364 364 if ui.verbose:
365 365 ui.status("description:\n")
366 366 ui.status(description)
367 367 ui.status("\n\n")
368 368 else:
369 369 ui.status("summary: %s\n" % description.splitlines()[0])
370 370 ui.status("\n")
371 371
372 372 def show_version(ui):
373 373 """output version and copyright information"""
374 374 ui.write("Mercurial Distributed SCM (version %s)\n"
375 375 % version.get_version())
376 376 ui.status(
377 377 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
378 378 "This is free software; see the source for copying conditions. "
379 379 "There is NO\nwarranty; "
380 380 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
381 381 )
382 382
383 383 def help_(ui, cmd=None, with_version=False):
384 384 """show help for a given command or all commands"""
385 385 option_lists = []
386 386 if cmd and cmd != 'shortlist':
387 387 if with_version:
388 388 show_version(ui)
389 389 ui.write('\n')
390 390 key, i = find(cmd)
391 391 # synopsis
392 392 ui.write("%s\n\n" % i[2])
393 393
394 394 # description
395 395 doc = i[0].__doc__
396 396 if ui.quiet:
397 397 doc = doc.splitlines(0)[0]
398 398 ui.write("%s\n" % doc.rstrip())
399 399
400 400 if not ui.quiet:
401 401 # aliases
402 402 aliases = ', '.join(key.split('|')[1:])
403 403 if aliases:
404 404 ui.write("\naliases: %s\n" % aliases)
405 405
406 406 # options
407 407 if i[1]:
408 408 option_lists.append(("options", i[1]))
409 409
410 410 else:
411 411 # program name
412 412 if ui.verbose or with_version:
413 413 show_version(ui)
414 414 else:
415 415 ui.status("Mercurial Distributed SCM\n")
416 416 ui.status('\n')
417 417
418 418 # list of commands
419 419 if cmd == "shortlist":
420 420 ui.status('basic commands (use "hg help" '
421 421 'for the full list or option "-v" for details):\n\n')
422 422 elif ui.verbose:
423 423 ui.status('list of commands:\n\n')
424 424 else:
425 425 ui.status('list of commands (use "hg help -v" '
426 426 'to show aliases and global options):\n\n')
427 427
428 428 h = {}
429 429 cmds = {}
430 430 for c, e in table.items():
431 431 f = c.split("|")[0]
432 432 if cmd == "shortlist" and not f.startswith("^"):
433 433 continue
434 434 f = f.lstrip("^")
435 435 if not ui.debugflag and f.startswith("debug"):
436 436 continue
437 437 d = ""
438 438 if e[0].__doc__:
439 439 d = e[0].__doc__.splitlines(0)[0].rstrip()
440 440 h[f] = d
441 441 cmds[f]=c.lstrip("^")
442 442
443 443 fns = h.keys()
444 444 fns.sort()
445 445 m = max(map(len, fns))
446 446 for f in fns:
447 447 if ui.verbose:
448 448 commands = cmds[f].replace("|",", ")
449 449 ui.write(" %s:\n %s\n"%(commands,h[f]))
450 450 else:
451 451 ui.write(' %-*s %s\n' % (m, f, h[f]))
452 452
453 453 # global options
454 454 if ui.verbose:
455 455 option_lists.append(("global options", globalopts))
456 456
457 457 # list all option lists
458 458 opt_output = []
459 459 for title, options in option_lists:
460 460 opt_output.append(("\n%s:\n" % title, None))
461 461 for shortopt, longopt, default, desc in options:
462 462 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
463 463 longopt and " --%s" % longopt),
464 464 "%s%s" % (desc,
465 465 default and " (default: %s)" % default
466 466 or "")))
467 467
468 468 if opt_output:
469 469 opts_len = max([len(line[0]) for line in opt_output if line[1]])
470 470 for first, second in opt_output:
471 471 if second:
472 472 ui.write(" %-*s %s\n" % (opts_len, first, second))
473 473 else:
474 474 ui.write("%s\n" % first)
475 475
476 476 # Commands start here, listed alphabetically
477 477
478 478 def add(ui, repo, *pats, **opts):
479 479 '''add the specified files on the next commit'''
480 480 names = []
481 481 for src, abs, rel, exact in walk(repo, pats, opts):
482 482 if exact:
483 483 names.append(abs)
484 484 elif repo.dirstate.state(abs) == '?':
485 485 ui.status('adding %s\n' % rel)
486 486 names.append(abs)
487 487 repo.add(names)
488 488
489 489 def addremove(ui, repo, *pats, **opts):
490 490 """add all new files, delete all missing files"""
491 491 add, remove = [], []
492 492 for src, abs, rel, exact in walk(repo, pats, opts):
493 493 if src == 'f' and repo.dirstate.state(abs) == '?':
494 494 add.append(abs)
495 495 if not exact:
496 496 ui.status('adding ', rel, '\n')
497 497 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
498 498 remove.append(abs)
499 499 if not exact:
500 500 ui.status('removing ', rel, '\n')
501 501 repo.add(add)
502 502 repo.remove(remove)
503 503
504 504 def annotate(ui, repo, *pats, **opts):
505 505 """show changeset information per file line"""
506 506 def getnode(rev):
507 507 return short(repo.changelog.node(rev))
508 508
509 509 ucache = {}
510 510 def getname(rev):
511 511 cl = repo.changelog.read(repo.changelog.node(rev))
512 512 return trimuser(ui, cl[1], rev, ucache)
513 513
514 514 if not pats:
515 515 raise util.Abort('at least one file name or pattern required')
516 516
517 517 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
518 518 if not opts['user'] and not opts['changeset']:
519 519 opts['number'] = 1
520 520
521 521 if opts['rev']:
522 522 node = repo.changelog.lookup(opts['rev'])
523 523 else:
524 524 node = repo.dirstate.parents()[0]
525 525 change = repo.changelog.read(node)
526 526 mmap = repo.manifest.read(change[0])
527 527
528 528 for src, abs, rel, exact in walk(repo, pats, opts):
529 529 if abs not in mmap:
530 530 ui.warn("warning: %s is not in the repository!\n" % rel)
531 531 continue
532 532
533 533 f = repo.file(abs)
534 534 if not opts['text'] and util.binary(f.read(mmap[abs])):
535 535 ui.write("%s: binary file\n" % rel)
536 536 continue
537 537
538 538 lines = f.annotate(mmap[abs])
539 539 pieces = []
540 540
541 541 for o, f in opmap:
542 542 if opts[o]:
543 543 l = [f(n) for n, dummy in lines]
544 544 if l:
545 545 m = max(map(len, l))
546 546 pieces.append(["%*s" % (m, x) for x in l])
547 547
548 548 if pieces:
549 549 for p, l in zip(zip(*pieces), lines):
550 550 ui.write("%s: %s" % (" ".join(p), l[1]))
551 551
552 552 def cat(ui, repo, file1, rev=None, **opts):
553 553 """output the latest or given revision of a file"""
554 554 r = repo.file(relpath(repo, [file1])[0])
555 555 if rev:
556 556 try:
557 557 # assume all revision numbers are for changesets
558 558 n = repo.lookup(rev)
559 559 change = repo.changelog.read(n)
560 560 m = repo.manifest.read(change[0])
561 561 n = m[relpath(repo, [file1])[0]]
562 562 except hg.RepoError, KeyError:
563 563 n = r.lookup(rev)
564 564 else:
565 565 n = r.tip()
566 566 fp = make_file(repo, r, opts['output'], node=n)
567 567 fp.write(r.read(n))
568 568
569 569 def clone(ui, source, dest=None, **opts):
570 570 """make a copy of an existing repository"""
571 571 if dest is None:
572 572 dest = os.path.basename(os.path.normpath(source))
573 573
574 574 if os.path.exists(dest):
575 575 ui.warn("abort: destination '%s' already exists\n" % dest)
576 576 return 1
577 577
578 578 dest = os.path.realpath(dest)
579 579
580 580 class Dircleanup:
581 581 def __init__(self, dir_):
582 582 self.rmtree = shutil.rmtree
583 583 self.dir_ = dir_
584 584 os.mkdir(dir_)
585 585 def close(self):
586 586 self.dir_ = None
587 587 def __del__(self):
588 588 if self.dir_:
589 589 self.rmtree(self.dir_, True)
590 590
591 591 if opts['ssh']:
592 592 ui.setconfig("ui", "ssh", opts['ssh'])
593 593 if opts['remotecmd']:
594 594 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
595 595
596 596 d = Dircleanup(dest)
597 597 source = ui.expandpath(source)
598 598 abspath = source
599 599 other = hg.repository(ui, source)
600 600
601 601 if other.dev() != -1:
602 602 abspath = os.path.abspath(source)
603 603 copyfile = (os.stat(dest).st_dev == other.dev()
604 604 and getattr(os, 'link', None) or shutil.copy2)
605 605 if copyfile is not shutil.copy2:
606 606 ui.note("cloning by hardlink\n")
607 607 # we use a lock here because because we're not nicely ordered
608 608 l = lock.lock(os.path.join(source, ".hg", "lock"))
609 609
610 610 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
611 611 copyfile)
612 612
613 613 for fn in "dirstate", "lock":
614 614 try:
615 615 os.unlink(os.path.join(dest, ".hg", fn))
616 616 except OSError:
617 617 pass
618 618
619 619 repo = hg.repository(ui, dest)
620 620
621 621 else:
622 622 repo = hg.repository(ui, dest, create=1)
623 623 repo.pull(other)
624 624
625 625 f = repo.opener("hgrc", "a")
626 626 f.write("\n[paths]\n")
627 627 f.write("default = %s\n" % abspath)
628 628
629 629 if not opts['noupdate']:
630 630 update(ui, repo)
631 631
632 632 d.close()
633 633
634 634 def commit(ui, repo, *pats, **opts):
635 635 """commit the specified files or all outstanding changes"""
636 636 if opts['text']:
637 637 ui.warn("Warning: -t and --text is deprecated,"
638 638 " please use -m or --message instead.\n")
639 639 message = opts['message'] or opts['text']
640 640 logfile = opts['logfile']
641 641 if not message and logfile:
642 642 try:
643 643 if logfile == '-':
644 644 message = sys.stdin.read()
645 645 else:
646 646 message = open(logfile).read()
647 647 except IOError, why:
648 648 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
649 649
650 650 if opts['addremove']:
651 651 addremove(ui, repo, *pats, **opts)
652 652 cwd = repo.getcwd()
653 653 if not pats and cwd:
654 654 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
655 655 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
656 656 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
657 657 pats, opts)
658 658 if pats:
659 659 c, a, d, u = repo.changes(files=fns, match=match)
660 660 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
661 661 else:
662 662 files = []
663 663 repo.commit(files, message, opts['user'], opts['date'], match)
664 664
665 665 def copy(ui, repo, source, dest):
666 666 """mark a file as copied or renamed for the next commit"""
667 667 return repo.copy(*relpath(repo, (source, dest)))
668 668
669 669 def debugcheckstate(ui, repo):
670 670 """validate the correctness of the current dirstate"""
671 671 parent1, parent2 = repo.dirstate.parents()
672 672 repo.dirstate.read()
673 673 dc = repo.dirstate.map
674 674 keys = dc.keys()
675 675 keys.sort()
676 676 m1n = repo.changelog.read(parent1)[0]
677 677 m2n = repo.changelog.read(parent2)[0]
678 678 m1 = repo.manifest.read(m1n)
679 679 m2 = repo.manifest.read(m2n)
680 680 errors = 0
681 681 for f in dc:
682 682 state = repo.dirstate.state(f)
683 683 if state in "nr" and f not in m1:
684 684 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
685 685 errors += 1
686 686 if state in "a" and f in m1:
687 687 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
688 688 errors += 1
689 689 if state in "m" and f not in m1 and f not in m2:
690 690 ui.warn("%s in state %s, but not in either manifest\n" %
691 691 (f, state))
692 692 errors += 1
693 693 for f in m1:
694 694 state = repo.dirstate.state(f)
695 695 if state not in "nrm":
696 696 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
697 697 errors += 1
698 698 if errors:
699 699 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
700 700
701 701 def debugconfig(ui):
702 702 """show combined config settings from all hgrc files"""
703 703 try:
704 704 repo = hg.repository(ui)
705 705 except hg.RepoError:
706 706 pass
707 707 for section, name, value in ui.walkconfig():
708 708 ui.write('%s.%s=%s\n' % (section, name, value))
709 709
710 710 def debugstate(ui, repo):
711 711 """show the contents of the current dirstate"""
712 712 repo.dirstate.read()
713 713 dc = repo.dirstate.map
714 714 keys = dc.keys()
715 715 keys.sort()
716 716 for file_ in keys:
717 717 ui.write("%c %3o %10d %s %s\n"
718 718 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
719 719 time.strftime("%x %X",
720 720 time.localtime(dc[file_][3])), file_))
721 721 for f in repo.dirstate.copies:
722 722 ui.write("copy: %s -> %s\n" % (repo.dirstate.copies[f], f))
723 723
724 724 def debugdata(ui, file_, rev):
725 725 """dump the contents of an data file revision"""
726 726 r = revlog.revlog(file, file_[:-2] + ".i", file_)
727 727 ui.write(r.revision(r.lookup(rev)))
728 728
729 729 def debugindex(ui, file_):
730 730 """dump the contents of an index file"""
731 731 r = revlog.revlog(file, file_, "")
732 732 ui.write(" rev offset length base linkrev" +
733 733 " nodeid p1 p2\n")
734 734 for i in range(r.count()):
735 735 e = r.index[i]
736 736 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
737 737 i, e[0], e[1], e[2], e[3],
738 738 short(e[6]), short(e[4]), short(e[5])))
739 739
740 740 def debugindexdot(ui, file_):
741 741 """dump an index DAG as a .dot file"""
742 742 r = revlog.revlog(file, file_, "")
743 743 ui.write("digraph G {\n")
744 744 for i in range(r.count()):
745 745 e = r.index[i]
746 746 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
747 747 if e[5] != nullid:
748 748 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
749 749 ui.write("}\n")
750 750
751 751 def debugrename(ui, repo, file, rev=None):
752 752 r = repo.file(relpath(repo, [file])[0])
753 753 if rev:
754 754 try:
755 755 # assume all revision numbers are for changesets
756 756 n = repo.lookup(rev)
757 757 change = repo.changelog.read(n)
758 758 m = repo.manifest.read(change[0])
759 759 n = m[relpath(repo, [file])[0]]
760 760 except hg.RepoError, KeyError:
761 761 n = r.lookup(rev)
762 762 else:
763 763 n = r.tip()
764 764 m = r.renamed(n)
765 765 if m:
766 766 ui.write("renamed from %s:%s\n" % (m[0], hex(m[1])))
767 767 else:
768 768 ui.write("not renamed\n")
769 769
770 770 def debugwalk(ui, repo, *pats, **opts):
771 771 """show how files match on given patterns"""
772 772 items = list(walk(repo, pats, opts))
773 773 if not items:
774 774 return
775 775 fmt = '%%s %%-%ds %%-%ds %%s\n' % (
776 776 max([len(abs) for (src, abs, rel, exact) in items]),
777 777 max([len(rel) for (src, abs, rel, exact) in items]))
778 778 for src, abs, rel, exact in items:
779 779 ui.write(fmt % (src, abs, rel, exact and 'exact' or ''))
780 780
781 781 def diff(ui, repo, *pats, **opts):
782 782 """diff working directory (or selected files)"""
783 783 node1, node2 = None, None
784 784 revs = [repo.lookup(x) for x in opts['rev']]
785 785
786 786 if len(revs) > 0:
787 787 node1 = revs[0]
788 788 if len(revs) > 1:
789 789 node2 = revs[1]
790 790 if len(revs) > 2:
791 791 raise util.Abort("too many revisions to diff")
792 792
793 793 files = []
794 794 match = util.always
795 795 if pats:
796 796 roots, match, results = makewalk(repo, pats, opts)
797 797 for src, abs, rel, exact in results:
798 798 files.append(abs)
799 799
800 800 dodiff(sys.stdout, ui, repo, node1, node2, files, match=match,
801 801 text=opts['text'])
802 802
803 803 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
804 804 node = repo.lookup(changeset)
805 805 prev, other = repo.changelog.parents(node)
806 806 change = repo.changelog.read(node)
807 807
808 808 fp = make_file(repo, repo.changelog, opts['output'],
809 809 node=node, total=total, seqno=seqno,
810 810 revwidth=revwidth)
811 811 if fp != sys.stdout:
812 812 ui.note("%s\n" % fp.name)
813 813
814 814 fp.write("# HG changeset patch\n")
815 815 fp.write("# User %s\n" % change[1])
816 816 fp.write("# Node ID %s\n" % hex(node))
817 817 fp.write("# Parent %s\n" % hex(prev))
818 818 if other != nullid:
819 819 fp.write("# Parent %s\n" % hex(other))
820 820 fp.write(change[4].rstrip())
821 821 fp.write("\n\n")
822 822
823 823 dodiff(fp, ui, repo, prev, node, text=opts['text'])
824 824 if fp != sys.stdout:
825 825 fp.close()
826 826
827 827 def export(ui, repo, *changesets, **opts):
828 828 """dump the header and diffs for one or more changesets"""
829 829 if not changesets:
830 830 raise util.Abort("export requires at least one changeset")
831 831 seqno = 0
832 832 revs = list(revrange(ui, repo, changesets))
833 833 total = len(revs)
834 834 revwidth = max(map(len, revs))
835 835 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
836 836 for cset in revs:
837 837 seqno += 1
838 838 doexport(ui, repo, cset, seqno, total, revwidth, opts)
839 839
840 840 def forget(ui, repo, *pats, **opts):
841 841 """don't add the specified files on the next commit"""
842 842 forget = []
843 843 for src, abs, rel, exact in walk(repo, pats, opts):
844 844 if repo.dirstate.state(abs) == 'a':
845 845 forget.append(abs)
846 846 if not exact:
847 847 ui.status('forgetting ', rel, '\n')
848 848 repo.forget(forget)
849 849
850 850 def grep(ui, repo, pattern, *pats, **opts):
851 851 """search for a pattern in specified files and revisions"""
852 852 reflags = 0
853 853 if opts['ignore_case']:
854 854 reflags |= re.I
855 855 regexp = re.compile(pattern, reflags)
856 856 sep, eol = ':', '\n'
857 857 if opts['print0']:
858 858 sep = eol = '\0'
859 859
860 860 fcache = {}
861 861 def getfile(fn):
862 862 if fn not in fcache:
863 863 fcache[fn] = repo.file(fn)
864 864 return fcache[fn]
865 865
866 866 def matchlines(body):
867 867 begin = 0
868 868 linenum = 0
869 869 while True:
870 870 match = regexp.search(body, begin)
871 871 if not match:
872 872 break
873 873 mstart, mend = match.span()
874 874 linenum += body.count('\n', begin, mstart) + 1
875 875 lstart = body.rfind('\n', begin, mstart) + 1 or begin
876 876 lend = body.find('\n', mend)
877 877 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
878 878 begin = lend + 1
879 879
880 880 class linestate:
881 881 def __init__(self, line, linenum, colstart, colend):
882 882 self.line = line
883 883 self.linenum = linenum
884 884 self.colstart = colstart
885 885 self.colend = colend
886 886 def __eq__(self, other):
887 887 return self.line == other.line
888 888 def __hash__(self):
889 889 return hash(self.line)
890 890
891 891 matches = {}
892 892 def grepbody(fn, rev, body):
893 893 matches[rev].setdefault(fn, {})
894 894 m = matches[rev][fn]
895 895 for lnum, cstart, cend, line in matchlines(body):
896 896 s = linestate(line, lnum, cstart, cend)
897 897 m[s] = s
898 898
899 899 prev = {}
900 900 ucache = {}
901 901 def display(fn, rev, states, prevstates):
902 902 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
903 903 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
904 904 counts = {'-': 0, '+': 0}
905 905 filerevmatches = {}
906 906 for l in diff:
907 907 if incrementing or not opts['every_match']:
908 908 change = ((l in prevstates) and '-') or '+'
909 909 r = rev
910 910 else:
911 911 change = ((l in states) and '-') or '+'
912 912 r = prev[fn]
913 913 cols = [fn, str(rev)]
914 914 if opts['line_number']: cols.append(str(l.linenum))
915 915 if opts['every_match']: cols.append(change)
916 916 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
917 917 ucache))
918 918 if opts['files_with_matches']:
919 919 c = (fn, rev)
920 920 if c in filerevmatches: continue
921 921 filerevmatches[c] = 1
922 922 else:
923 923 cols.append(l.line)
924 924 ui.write(sep.join(cols), eol)
925 925 counts[change] += 1
926 926 return counts['+'], counts['-']
927 927
928 928 fstate = {}
929 929 skip = {}
930 930 changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
931 931 count = 0
932 932 for st, rev, fns in changeiter:
933 933 if st == 'window':
934 934 incrementing = rev
935 935 matches.clear()
936 936 elif st == 'add':
937 937 change = repo.changelog.read(repo.lookup(str(rev)))
938 938 mf = repo.manifest.read(change[0])
939 939 matches[rev] = {}
940 940 for fn in fns:
941 941 if fn in skip: continue
942 942 fstate.setdefault(fn, {})
943 943 try:
944 944 grepbody(fn, rev, getfile(fn).read(mf[fn]))
945 945 except KeyError:
946 946 pass
947 947 elif st == 'iter':
948 948 states = matches[rev].items()
949 949 states.sort()
950 950 for fn, m in states:
951 951 if fn in skip: continue
952 952 if incrementing or not opts['every_match'] or fstate[fn]:
953 953 pos, neg = display(fn, rev, m, fstate[fn])
954 954 count += pos + neg
955 955 if pos and not opts['every_match']:
956 956 skip[fn] = True
957 957 fstate[fn] = m
958 958 prev[fn] = rev
959 959
960 960 if not incrementing:
961 961 fstate = fstate.items()
962 962 fstate.sort()
963 963 for fn, state in fstate:
964 964 if fn in skip: continue
965 965 display(fn, rev, {}, state)
966 966 return (count == 0 and 1) or 0
967 967
968 968 def heads(ui, repo, **opts):
969 969 """show current repository heads"""
970 970 heads = repo.changelog.heads()
971 971 br = None
972 972 if opts['branches']:
973 973 br = repo.branchlookup(heads)
974 974 for n in repo.changelog.heads():
975 975 show_changeset(ui, repo, changenode=n, brinfo=br)
976 976
977 977 def identify(ui, repo):
978 978 """print information about the working copy"""
979 979 parents = [p for p in repo.dirstate.parents() if p != nullid]
980 980 if not parents:
981 981 ui.write("unknown\n")
982 982 return
983 983
984 984 hexfunc = ui.verbose and hex or short
985 985 (c, a, d, u) = repo.changes()
986 986 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
987 987 (c or a or d) and "+" or "")]
988 988
989 989 if not ui.quiet:
990 990 # multiple tags for a single parent separated by '/'
991 991 parenttags = ['/'.join(tags)
992 992 for tags in map(repo.nodetags, parents) if tags]
993 993 # tags for multiple parents separated by ' + '
994 994 if parenttags:
995 995 output.append(' + '.join(parenttags))
996 996
997 997 ui.write("%s\n" % ' '.join(output))
998 998
999 999 def import_(ui, repo, patch1, *patches, **opts):
1000 1000 """import an ordered set of patches"""
1001 1001 patches = (patch1,) + patches
1002 1002
1003 1003 if not opts['force']:
1004 1004 (c, a, d, u) = repo.changes()
1005 1005 if c or a or d:
1006 1006 ui.warn("abort: outstanding uncommitted changes!\n")
1007 1007 return 1
1008 1008
1009 1009 d = opts["base"]
1010 1010 strip = opts["strip"]
1011 1011
1012 1012 mailre = re.compile(r'(From |[\w-]+:)')
1013 1013
1014 1014 for patch in patches:
1015 1015 ui.status("applying %s\n" % patch)
1016 1016 pf = os.path.join(d, patch)
1017 1017
1018 1018 message = []
1019 1019 user = None
1020 1020 hgpatch = False
1021 1021 for line in file(pf):
1022 1022 line = line.rstrip()
1023 1023 if not message and mailre.match(line) and not opts['mail_like']:
1024 1024 if len(line) > 35: line = line[:32] + '...'
1025 1025 raise util.Abort('first line looks like a '
1026 1026 'mail header: ' + line)
1027 1027 if line.startswith("--- ") or line.startswith("diff -r"):
1028 1028 break
1029 1029 elif hgpatch:
1030 1030 # parse values when importing the result of an hg export
1031 1031 if line.startswith("# User "):
1032 1032 user = line[7:]
1033 1033 ui.debug('User: %s\n' % user)
1034 1034 elif not line.startswith("# ") and line:
1035 1035 message.append(line)
1036 1036 hgpatch = False
1037 1037 elif line == '# HG changeset patch':
1038 1038 hgpatch = True
1039 1039 message = [] # We may have collected garbage
1040 1040 else:
1041 1041 message.append(line)
1042 1042
1043 1043 # make sure message isn't empty
1044 1044 if not message:
1045 1045 message = "imported patch %s\n" % patch
1046 1046 else:
1047 1047 message = "%s\n" % '\n'.join(message)
1048 1048 ui.debug('message:\n%s\n' % message)
1049 1049
1050 1050 f = os.popen("patch -p%d < '%s'" % (strip, pf))
1051 1051 files = []
1052 1052 for l in f.read().splitlines():
1053 1053 l.rstrip('\r\n');
1054 1054 ui.status("%s\n" % l)
1055 1055 if l.startswith('patching file '):
1056 1056 pf = l[14:]
1057 1057 if pf not in files:
1058 1058 files.append(pf)
1059 1059 patcherr = f.close()
1060 1060 if patcherr:
1061 1061 raise util.Abort("patch failed")
1062 1062
1063 1063 if len(files) > 0:
1064 1064 addremove(ui, repo, *files)
1065 1065 repo.commit(files, message, user)
1066 1066
1067 def incoming(ui, repo, source="default", **opts):
1067 def incoming(ui, repo, source="default"):
1068 1068 """show new changesets found in source"""
1069 1069 source = ui.expandpath(source)
1070 1070 other = hg.repository(ui, source)
1071 1071 if not other.local():
1072 1072 ui.warn("abort: incoming doesn't work for remote"
1073 1073 + " repositories yet, sorry!\n")
1074 1074 return 1
1075 1075 o = repo.findincoming(other)
1076 1076 if not o:
1077 1077 return
1078 1078 o = other.newer(o)
1079 1079 o.reverse()
1080 1080 for n in o:
1081 1081 show_changeset(ui, other, changenode=n)
1082 if opts['patch']:
1083 prev = other.changelog.parents(n)[0]
1084 dodiff(ui, ui, other, prev, n)
1085 ui.write("\n")
1086 1082
1087 1083 def init(ui, dest="."):
1088 1084 """create a new repository in the given directory"""
1089 1085 if not os.path.exists(dest):
1090 1086 os.mkdir(dest)
1091 1087 hg.repository(ui, dest, create=1)
1092 1088
1093 1089 def locate(ui, repo, *pats, **opts):
1094 1090 """locate files matching specific patterns"""
1095 1091 end = opts['print0'] and '\0' or '\n'
1096 1092
1097 1093 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1098 1094 if repo.dirstate.state(abs) == '?':
1099 1095 continue
1100 1096 if opts['fullpath']:
1101 1097 ui.write(os.path.join(repo.root, abs), end)
1102 1098 else:
1103 1099 ui.write(rel, end)
1104 1100
1105 1101 def log(ui, repo, *pats, **opts):
1106 1102 """show revision history of entire repository or files"""
1107 1103 class dui:
1108 1104 # Implement and delegate some ui protocol. Save hunks of
1109 1105 # output for later display in the desired order.
1110 1106 def __init__(self, ui):
1111 1107 self.ui = ui
1112 1108 self.hunk = {}
1113 1109 def bump(self, rev):
1114 1110 self.rev = rev
1115 1111 self.hunk[rev] = []
1116 1112 def note(self, *args):
1117 1113 if self.verbose:
1118 1114 self.write(*args)
1119 1115 def status(self, *args):
1120 1116 if not self.quiet:
1121 1117 self.write(*args)
1122 1118 def write(self, *args):
1123 1119 self.hunk[self.rev].append(args)
1124 1120 def __getattr__(self, key):
1125 1121 return getattr(self.ui, key)
1126 1122 cwd = repo.getcwd()
1127 1123 if not pats and cwd:
1128 1124 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1129 1125 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1130 1126 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1131 1127 pats, opts)
1132 1128 for st, rev, fns in changeiter:
1133 1129 if st == 'window':
1134 1130 du = dui(ui)
1135 1131 elif st == 'add':
1136 1132 du.bump(rev)
1137 1133 show_changeset(du, repo, rev)
1138 1134 if opts['patch']:
1139 1135 changenode = repo.changelog.node(rev)
1140 1136 prev, other = repo.changelog.parents(changenode)
1141 1137 dodiff(du, du, repo, prev, changenode, fns)
1142 1138 du.write("\n\n")
1143 1139 elif st == 'iter':
1144 1140 for args in du.hunk[rev]:
1145 1141 ui.write(*args)
1146 1142
1147 1143 def manifest(ui, repo, rev=None):
1148 1144 """output the latest or given revision of the project manifest"""
1149 1145 if rev:
1150 1146 try:
1151 1147 # assume all revision numbers are for changesets
1152 1148 n = repo.lookup(rev)
1153 1149 change = repo.changelog.read(n)
1154 1150 n = change[0]
1155 1151 except hg.RepoError:
1156 1152 n = repo.manifest.lookup(rev)
1157 1153 else:
1158 1154 n = repo.manifest.tip()
1159 1155 m = repo.manifest.read(n)
1160 1156 mf = repo.manifest.readflags(n)
1161 1157 files = m.keys()
1162 1158 files.sort()
1163 1159
1164 1160 for f in files:
1165 1161 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1166 1162
1167 def outgoing(ui, repo, dest="default-push", **opts):
1163 def outgoing(ui, repo, dest="default-push"):
1168 1164 """show changesets not found in destination"""
1169 1165 dest = ui.expandpath(dest)
1170 1166 other = hg.repository(ui, dest)
1171 1167 o = repo.findoutgoing(other)
1172 1168 o = repo.newer(o)
1173 1169 o.reverse()
1174 1170 for n in o:
1175 1171 show_changeset(ui, repo, changenode=n)
1176 if opts['patch']:
1177 prev = repo.changelog.parents(n)[0]
1178 dodiff(ui, ui, repo, prev, n)
1179 ui.write("\n")
1180 1172
1181 1173 def parents(ui, repo, rev=None):
1182 1174 """show the parents of the working dir or revision"""
1183 1175 if rev:
1184 1176 p = repo.changelog.parents(repo.lookup(rev))
1185 1177 else:
1186 1178 p = repo.dirstate.parents()
1187 1179
1188 1180 for n in p:
1189 1181 if n != nullid:
1190 1182 show_changeset(ui, repo, changenode=n)
1191 1183
1192 1184 def paths(ui, search=None):
1193 1185 """show definition of symbolic path names"""
1194 1186 try:
1195 1187 repo = hg.repository(ui=ui)
1196 1188 except hg.RepoError:
1197 1189 pass
1198 1190
1199 1191 if search:
1200 1192 for name, path in ui.configitems("paths"):
1201 1193 if name == search:
1202 1194 ui.write("%s\n" % path)
1203 1195 return
1204 1196 ui.warn("not found!\n")
1205 1197 return 1
1206 1198 else:
1207 1199 for name, path in ui.configitems("paths"):
1208 1200 ui.write("%s = %s\n" % (name, path))
1209 1201
1210 1202 def pull(ui, repo, source="default", **opts):
1211 1203 """pull changes from the specified source"""
1212 1204 source = ui.expandpath(source)
1213 1205 ui.status('pulling from %s\n' % (source))
1214 1206
1215 1207 if opts['ssh']:
1216 1208 ui.setconfig("ui", "ssh", opts['ssh'])
1217 1209 if opts['remotecmd']:
1218 1210 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1219 1211
1220 1212 other = hg.repository(ui, source)
1221 1213 r = repo.pull(other)
1222 1214 if not r:
1223 1215 if opts['update']:
1224 1216 return update(ui, repo)
1225 1217 else:
1226 1218 ui.status("(run 'hg update' to get a working copy)\n")
1227 1219
1228 1220 return r
1229 1221
1230 1222 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1231 1223 """push changes to the specified destination"""
1232 1224 dest = ui.expandpath(dest)
1233 1225 ui.status('pushing to %s\n' % (dest))
1234 1226
1235 1227 if ssh:
1236 1228 ui.setconfig("ui", "ssh", ssh)
1237 1229 if remotecmd:
1238 1230 ui.setconfig("ui", "remotecmd", remotecmd)
1239 1231
1240 1232 other = hg.repository(ui, dest)
1241 1233 r = repo.push(other, force)
1242 1234 return r
1243 1235
1244 1236 def rawcommit(ui, repo, *flist, **rc):
1245 1237 "raw commit interface"
1246 1238 if rc['text']:
1247 1239 ui.warn("Warning: -t and --text is deprecated,"
1248 1240 " please use -m or --message instead.\n")
1249 1241 message = rc['message'] or rc['text']
1250 1242 if not message and rc['logfile']:
1251 1243 try:
1252 1244 message = open(rc['logfile']).read()
1253 1245 except IOError:
1254 1246 pass
1255 1247 if not message and not rc['logfile']:
1256 1248 ui.warn("abort: missing commit message\n")
1257 1249 return 1
1258 1250
1259 1251 files = relpath(repo, list(flist))
1260 1252 if rc['files']:
1261 1253 files += open(rc['files']).read().splitlines()
1262 1254
1263 1255 rc['parent'] = map(repo.lookup, rc['parent'])
1264 1256
1265 1257 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1266 1258
1267 1259 def recover(ui, repo):
1268 1260 """roll back an interrupted transaction"""
1269 1261 repo.recover()
1270 1262
1271 1263 def remove(ui, repo, pat, *pats, **opts):
1272 1264 """remove the specified files on the next commit"""
1273 1265 names = []
1274 1266 def okaytoremove(abs, rel, exact):
1275 1267 c, a, d, u = repo.changes(files = [abs])
1276 1268 reason = None
1277 1269 if c: reason = 'is modified'
1278 1270 elif a: reason = 'has been marked for add'
1279 1271 elif u: reason = 'not managed'
1280 1272 if reason and exact:
1281 1273 ui.warn('not removing %s: file %s\n' % (rel, reason))
1282 1274 else:
1283 1275 return True
1284 1276 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1285 1277 if okaytoremove(abs, rel, exact):
1286 1278 if not exact: ui.status('removing %s\n' % rel)
1287 1279 names.append(abs)
1288 1280 repo.remove(names)
1289 1281
1290 1282 def revert(ui, repo, *names, **opts):
1291 1283 """revert modified files or dirs back to their unmodified states"""
1292 1284 node = opts['rev'] and repo.lookup(opts['rev']) or \
1293 1285 repo.dirstate.parents()[0]
1294 1286 root = os.path.realpath(repo.root)
1295 1287
1296 1288 def trimpath(p):
1297 1289 p = os.path.realpath(p)
1298 1290 if p.startswith(root):
1299 1291 rest = p[len(root):]
1300 1292 if not rest:
1301 1293 return rest
1302 1294 if p.startswith(os.sep):
1303 1295 return rest[1:]
1304 1296 return p
1305 1297
1306 1298 relnames = map(trimpath, names or [os.getcwd()])
1307 1299 chosen = {}
1308 1300
1309 1301 def choose(name):
1310 1302 def body(name):
1311 1303 for r in relnames:
1312 1304 if not name.startswith(r):
1313 1305 continue
1314 1306 rest = name[len(r):]
1315 1307 if not rest:
1316 1308 return r, True
1317 1309 depth = rest.count(os.sep)
1318 1310 if not r:
1319 1311 if depth == 0 or not opts['nonrecursive']:
1320 1312 return r, True
1321 1313 elif rest[0] == os.sep:
1322 1314 if depth == 1 or not opts['nonrecursive']:
1323 1315 return r, True
1324 1316 return None, False
1325 1317 relname, ret = body(name)
1326 1318 if ret:
1327 1319 chosen[relname] = 1
1328 1320 return ret
1329 1321
1330 1322 r = repo.update(node, False, True, choose, False)
1331 1323 for n in relnames:
1332 1324 if n not in chosen:
1333 1325 ui.warn('error: no matches for %s\n' % n)
1334 1326 r = 1
1335 1327 sys.stdout.flush()
1336 1328 return r
1337 1329
1338 1330 def root(ui, repo):
1339 1331 """print the root (top) of the current working dir"""
1340 1332 ui.write(repo.root + "\n")
1341 1333
1342 1334 def serve(ui, repo, **opts):
1343 1335 """export the repository via HTTP"""
1344 1336
1345 1337 if opts["stdio"]:
1346 1338 fin, fout = sys.stdin, sys.stdout
1347 1339 sys.stdout = sys.stderr
1348 1340
1349 1341 def getarg():
1350 1342 argline = fin.readline()[:-1]
1351 1343 arg, l = argline.split()
1352 1344 val = fin.read(int(l))
1353 1345 return arg, val
1354 1346 def respond(v):
1355 1347 fout.write("%d\n" % len(v))
1356 1348 fout.write(v)
1357 1349 fout.flush()
1358 1350
1359 1351 lock = None
1360 1352
1361 1353 while 1:
1362 1354 cmd = fin.readline()[:-1]
1363 1355 if cmd == '':
1364 1356 return
1365 1357 if cmd == "heads":
1366 1358 h = repo.heads()
1367 1359 respond(" ".join(map(hex, h)) + "\n")
1368 1360 if cmd == "lock":
1369 1361 lock = repo.lock()
1370 1362 respond("")
1371 1363 if cmd == "unlock":
1372 1364 if lock:
1373 1365 lock.release()
1374 1366 lock = None
1375 1367 respond("")
1376 1368 elif cmd == "branches":
1377 1369 arg, nodes = getarg()
1378 1370 nodes = map(bin, nodes.split(" "))
1379 1371 r = []
1380 1372 for b in repo.branches(nodes):
1381 1373 r.append(" ".join(map(hex, b)) + "\n")
1382 1374 respond("".join(r))
1383 1375 elif cmd == "between":
1384 1376 arg, pairs = getarg()
1385 1377 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1386 1378 r = []
1387 1379 for b in repo.between(pairs):
1388 1380 r.append(" ".join(map(hex, b)) + "\n")
1389 1381 respond("".join(r))
1390 1382 elif cmd == "changegroup":
1391 1383 nodes = []
1392 1384 arg, roots = getarg()
1393 1385 nodes = map(bin, roots.split(" "))
1394 1386
1395 1387 cg = repo.changegroup(nodes)
1396 1388 while 1:
1397 1389 d = cg.read(4096)
1398 1390 if not d:
1399 1391 break
1400 1392 fout.write(d)
1401 1393
1402 1394 fout.flush()
1403 1395
1404 1396 elif cmd == "addchangegroup":
1405 1397 if not lock:
1406 1398 respond("not locked")
1407 1399 continue
1408 1400 respond("")
1409 1401
1410 1402 r = repo.addchangegroup(fin)
1411 1403 respond("")
1412 1404
1413 1405 optlist = "name templates style address port ipv6 accesslog errorlog"
1414 1406 for o in optlist.split():
1415 1407 if opts[o]:
1416 1408 ui.setconfig("web", o, opts[o])
1417 1409
1418 1410 try:
1419 1411 httpd = hgweb.create_server(repo)
1420 1412 except socket.error, inst:
1421 1413 raise util.Abort('cannot start server: ' + inst.args[1])
1422 1414
1423 1415 if ui.verbose:
1424 1416 addr, port = httpd.socket.getsockname()
1425 1417 if addr == '0.0.0.0':
1426 1418 addr = socket.gethostname()
1427 1419 else:
1428 1420 try:
1429 1421 addr = socket.gethostbyaddr(addr)[0]
1430 1422 except socket.error:
1431 1423 pass
1432 1424 if port != 80:
1433 1425 ui.status('listening at http://%s:%d/\n' % (addr, port))
1434 1426 else:
1435 1427 ui.status('listening at http://%s/\n' % addr)
1436 1428 httpd.serve_forever()
1437 1429
1438 1430 def status(ui, repo, *pats, **opts):
1439 1431 '''show changed files in the working directory
1440 1432
1441 1433 M = modified
1442 1434 A = added
1443 1435 R = removed
1444 1436 ? = not tracked
1445 1437 '''
1446 1438
1447 1439 cwd = repo.getcwd()
1448 1440 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1449 1441 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1450 1442 for n in repo.changes(files=files, match=matchfn)]
1451 1443
1452 1444 changetypes = [('modified', 'M', c),
1453 1445 ('added', 'A', a),
1454 1446 ('removed', 'R', d),
1455 1447 ('unknown', '?', u)]
1456 1448
1457 1449 end = opts['print0'] and '\0' or '\n'
1458 1450
1459 1451 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1460 1452 or changetypes):
1461 1453 if opts['no_status']:
1462 1454 format = "%%s%s" % end
1463 1455 else:
1464 1456 format = "%s %%s%s" % (char, end);
1465 1457
1466 1458 for f in changes:
1467 1459 ui.write(format % f)
1468 1460
1469 1461 def tag(ui, repo, name, rev=None, **opts):
1470 1462 """add a tag for the current tip or a given revision"""
1471 1463 if opts['text']:
1472 1464 ui.warn("Warning: -t and --text is deprecated,"
1473 1465 " please use -m or --message instead.\n")
1474 1466 if name == "tip":
1475 1467 ui.warn("abort: 'tip' is a reserved name!\n")
1476 1468 return -1
1477 1469 if rev:
1478 1470 r = hex(repo.lookup(rev))
1479 1471 else:
1480 1472 r = hex(repo.changelog.tip())
1481 1473
1482 1474 if name.find(revrangesep) >= 0:
1483 1475 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1484 1476 return -1
1485 1477
1486 1478 if opts['local']:
1487 1479 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1488 1480 return
1489 1481
1490 1482 (c, a, d, u) = repo.changes()
1491 1483 for x in (c, a, d, u):
1492 1484 if ".hgtags" in x:
1493 1485 ui.warn("abort: working copy of .hgtags is changed!\n")
1494 1486 ui.status("(please commit .hgtags manually)\n")
1495 1487 return -1
1496 1488
1497 1489 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1498 1490 if repo.dirstate.state(".hgtags") == '?':
1499 1491 repo.add([".hgtags"])
1500 1492
1501 1493 message = (opts['message'] or opts['text'] or
1502 1494 "Added tag %s for changeset %s" % (name, r))
1503 1495 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1504 1496
1505 1497 def tags(ui, repo):
1506 1498 """list repository tags"""
1507 1499
1508 1500 l = repo.tagslist()
1509 1501 l.reverse()
1510 1502 for t, n in l:
1511 1503 try:
1512 1504 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
1513 1505 except KeyError:
1514 1506 r = " ?:?"
1515 1507 ui.write("%-30s %s\n" % (t, r))
1516 1508
1517 1509 def tip(ui, repo):
1518 1510 """show the tip revision"""
1519 1511 n = repo.changelog.tip()
1520 1512 show_changeset(ui, repo, changenode=n)
1521 1513
1522 1514 def undo(ui, repo):
1523 1515 """undo the last commit or pull
1524 1516
1525 1517 Roll back the last pull or commit transaction on the
1526 1518 repository, restoring the project to its earlier state.
1527 1519
1528 1520 This command should be used with care. There is only one level of
1529 1521 undo and there is no redo.
1530 1522
1531 1523 This command is not intended for use on public repositories. Once
1532 1524 a change is visible for pull by other users, undoing it locally is
1533 1525 ineffective.
1534 1526 """
1535 1527 repo.undo()
1536 1528
1537 1529 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1538 1530 '''update or merge working directory
1539 1531
1540 1532 If there are no outstanding changes in the working directory and
1541 1533 there is a linear relationship between the current version and the
1542 1534 requested version, the result is the requested version.
1543 1535
1544 1536 Otherwise the result is a merge between the contents of the
1545 1537 current working directory and the requested version. Files that
1546 1538 changed between either parent are marked as changed for the next
1547 1539 commit and a commit must be performed before any further updates
1548 1540 are allowed.
1549 1541 '''
1550 1542 if branch:
1551 1543 br = repo.branchlookup(branch=branch)
1552 1544 found = []
1553 1545 for x in br:
1554 1546 if branch in br[x]:
1555 1547 found.append(x)
1556 1548 if len(found) > 1:
1557 1549 ui.warn("Found multiple heads for %s\n" % branch)
1558 1550 for x in found:
1559 1551 show_changeset(ui, repo, changenode=x, brinfo=br)
1560 1552 return 1
1561 1553 if len(found) == 1:
1562 1554 node = found[0]
1563 1555 ui.warn("Using head %s for branch %s\n" % (short(node), branch))
1564 1556 else:
1565 1557 ui.warn("branch %s not found\n" % (branch))
1566 1558 return 1
1567 1559 else:
1568 1560 node = node and repo.lookup(node) or repo.changelog.tip()
1569 1561 return repo.update(node, allow=merge, force=clean)
1570 1562
1571 1563 def verify(ui, repo):
1572 1564 """verify the integrity of the repository"""
1573 1565 return repo.verify()
1574 1566
1575 1567 # Command options and aliases are listed here, alphabetically
1576 1568
1577 1569 table = {
1578 1570 "^add":
1579 1571 (add,
1580 1572 [('I', 'include', [], 'include path in search'),
1581 1573 ('X', 'exclude', [], 'exclude path from search')],
1582 1574 "hg add [OPTION]... [FILE]..."),
1583 1575 "addremove":
1584 1576 (addremove,
1585 1577 [('I', 'include', [], 'include path in search'),
1586 1578 ('X', 'exclude', [], 'exclude path from search')],
1587 1579 "hg addremove [OPTION]... [FILE]..."),
1588 1580 "^annotate":
1589 1581 (annotate,
1590 1582 [('r', 'rev', '', 'revision'),
1591 1583 ('a', 'text', None, 'treat all files as text'),
1592 1584 ('u', 'user', None, 'show user'),
1593 1585 ('n', 'number', None, 'show revision number'),
1594 1586 ('c', 'changeset', None, 'show changeset'),
1595 1587 ('I', 'include', [], 'include path in search'),
1596 1588 ('X', 'exclude', [], 'exclude path from search')],
1597 1589 'hg annotate [OPTION]... FILE...'),
1598 1590 "cat":
1599 1591 (cat,
1600 1592 [('o', 'output', "", 'output to file')],
1601 1593 'hg cat [-o OUTFILE] FILE [REV]'),
1602 1594 "^clone":
1603 1595 (clone,
1604 1596 [('U', 'noupdate', None, 'skip update after cloning'),
1605 1597 ('e', 'ssh', "", 'ssh command'),
1606 1598 ('', 'remotecmd', "", 'remote hg command')],
1607 1599 'hg clone [OPTION]... SOURCE [DEST]'),
1608 1600 "^commit|ci":
1609 1601 (commit,
1610 1602 [('A', 'addremove', None, 'run add/remove during commit'),
1611 1603 ('I', 'include', [], 'include path in search'),
1612 1604 ('X', 'exclude', [], 'exclude path from search'),
1613 1605 ('m', 'message', "", 'commit message'),
1614 1606 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1615 1607 ('l', 'logfile', "", 'commit message file'),
1616 1608 ('d', 'date', "", 'date code'),
1617 1609 ('u', 'user', "", 'user')],
1618 1610 'hg commit [OPTION]... [FILE]...'),
1619 1611 "copy": (copy, [], 'hg copy SOURCE DEST'),
1620 1612 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1621 1613 "debugconfig": (debugconfig, [], 'debugconfig'),
1622 1614 "debugstate": (debugstate, [], 'debugstate'),
1623 1615 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1624 1616 "debugindex": (debugindex, [], 'debugindex FILE'),
1625 1617 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1626 1618 "debugrename": (debugrename, [], 'debugrename FILE [REV]'),
1627 1619 "debugwalk":
1628 1620 (debugwalk,
1629 1621 [('I', 'include', [], 'include path in search'),
1630 1622 ('X', 'exclude', [], 'exclude path from search')],
1631 1623 'debugwalk [OPTION]... [FILE]...'),
1632 1624 "^diff":
1633 1625 (diff,
1634 1626 [('r', 'rev', [], 'revision'),
1635 1627 ('a', 'text', None, 'treat all files as text'),
1636 1628 ('I', 'include', [], 'include path in search'),
1637 1629 ('X', 'exclude', [], 'exclude path from search')],
1638 1630 'hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1639 1631 "^export":
1640 1632 (export,
1641 1633 [('o', 'output', "", 'output to file'),
1642 1634 ('a', 'text', None, 'treat all files as text')],
1643 1635 "hg export [-a] [-o OUTFILE] REV..."),
1644 1636 "forget":
1645 1637 (forget,
1646 1638 [('I', 'include', [], 'include path in search'),
1647 1639 ('X', 'exclude', [], 'exclude path from search')],
1648 1640 "hg forget [OPTION]... FILE..."),
1649 1641 "grep":
1650 1642 (grep,
1651 1643 [('0', 'print0', None, 'end fields with NUL'),
1652 1644 ('I', 'include', [], 'include path in search'),
1653 1645 ('X', 'exclude', [], 'include path in search'),
1654 1646 ('e', 'every-match', None, 'print every rev with matches'),
1655 1647 ('i', 'ignore-case', None, 'ignore case when matching'),
1656 1648 ('l', 'files-with-matches', None, 'print names of files and revs with matches'),
1657 1649 ('n', 'line-number', None, 'print line numbers'),
1658 1650 ('r', 'rev', [], 'search in revision rev'),
1659 1651 ('u', 'user', None, 'print user who made change')],
1660 1652 "hg grep [OPTION]... PATTERN [FILE]..."),
1661 1653 "heads":
1662 1654 (heads,
1663 1655 [('b', 'branches', None, 'find branch info')],
1664 1656 'hg heads [-b]'),
1665 1657 "help": (help_, [], 'hg help [COMMAND]'),
1666 1658 "identify|id": (identify, [], 'hg identify'),
1667 1659 "import|patch":
1668 1660 (import_,
1669 1661 [('p', 'strip', 1, 'path strip'),
1670 1662 ('f', 'force', None, 'skip check for outstanding changes'),
1671 1663 ('b', 'base', "", 'base path'),
1672 1664 ('m', 'mail-like', None, 'apply a patch that looks like email')],
1673 1665 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
1674 "incoming|in": (incoming,
1675 [('p', 'patch', None, 'show patch')],
1676 'hg incoming [-p] [SOURCE]'),
1666 "incoming|in": (incoming, [], 'hg incoming [SOURCE]'),
1677 1667 "^init": (init, [], 'hg init [DEST]'),
1678 1668 "locate":
1679 1669 (locate,
1680 1670 [('r', 'rev', '', 'revision'),
1681 1671 ('0', 'print0', None, 'end filenames with NUL'),
1682 1672 ('f', 'fullpath', None, 'print complete paths'),
1683 1673 ('I', 'include', [], 'include path in search'),
1684 1674 ('X', 'exclude', [], 'exclude path from search')],
1685 1675 'hg locate [OPTION]... [PATTERN]...'),
1686 1676 "^log|history":
1687 1677 (log,
1688 1678 [('I', 'include', [], 'include path in search'),
1689 1679 ('X', 'exclude', [], 'exclude path from search'),
1690 1680 ('r', 'rev', [], 'revision'),
1691 1681 ('p', 'patch', None, 'show patch')],
1692 1682 'hg log [-I] [-X] [-r REV]... [-p] [FILE]'),
1693 1683 "manifest": (manifest, [], 'hg manifest [REV]'),
1694 "outgoing|out": (outgoing,
1695 [('p', 'patch', None, 'show patch')],
1696 'hg outgoing [-p] [DEST]'),
1684 "outgoing|out": (outgoing, [], 'hg outgoing [DEST]'),
1697 1685 "parents": (parents, [], 'hg parents [REV]'),
1698 1686 "paths": (paths, [], 'hg paths [NAME]'),
1699 1687 "^pull":
1700 1688 (pull,
1701 1689 [('u', 'update', None, 'update working directory'),
1702 1690 ('e', 'ssh', "", 'ssh command'),
1703 1691 ('', 'remotecmd', "", 'remote hg command')],
1704 1692 'hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]'),
1705 1693 "^push":
1706 1694 (push,
1707 1695 [('f', 'force', None, 'force push'),
1708 1696 ('e', 'ssh', "", 'ssh command'),
1709 1697 ('', 'remotecmd', "", 'remote hg command')],
1710 1698 'hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]'),
1711 1699 "rawcommit":
1712 1700 (rawcommit,
1713 1701 [('p', 'parent', [], 'parent'),
1714 1702 ('d', 'date', "", 'date code'),
1715 1703 ('u', 'user', "", 'user'),
1716 1704 ('F', 'files', "", 'file list'),
1717 1705 ('m', 'message', "", 'commit message'),
1718 1706 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1719 1707 ('l', 'logfile', "", 'commit message file')],
1720 1708 'hg rawcommit [OPTION]... [FILE]...'),
1721 1709 "recover": (recover, [], "hg recover"),
1722 1710 "^remove|rm": (remove,
1723 1711 [('I', 'include', [], 'include path in search'),
1724 1712 ('X', 'exclude', [], 'exclude path from search')],
1725 1713 "hg remove [OPTION]... FILE..."),
1726 1714 "^revert":
1727 1715 (revert,
1728 1716 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1729 1717 ("r", "rev", "", "revision")],
1730 1718 "hg revert [-n] [-r REV] [NAME]..."),
1731 1719 "root": (root, [], "hg root"),
1732 1720 "^serve":
1733 1721 (serve,
1734 1722 [('A', 'accesslog', '', 'access log file'),
1735 1723 ('E', 'errorlog', '', 'error log file'),
1736 1724 ('p', 'port', 0, 'listen port'),
1737 1725 ('a', 'address', '', 'interface address'),
1738 1726 ('n', 'name', "", 'repository name'),
1739 1727 ('', 'stdio', None, 'for remote clients'),
1740 1728 ('t', 'templates', "", 'template directory'),
1741 1729 ('', 'style', "", 'template style'),
1742 1730 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1743 1731 "hg serve [OPTION]..."),
1744 1732 "^status":
1745 1733 (status,
1746 1734 [('m', 'modified', None, 'show only modified files'),
1747 1735 ('a', 'added', None, 'show only added files'),
1748 1736 ('r', 'removed', None, 'show only removed files'),
1749 1737 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1750 1738 ('n', 'no-status', None, 'hide status prefix'),
1751 1739 ('0', 'print0', None, 'end filenames with NUL'),
1752 1740 ('I', 'include', [], 'include path in search'),
1753 1741 ('X', 'exclude', [], 'exclude path from search')],
1754 1742 "hg status [OPTION]... [FILE]..."),
1755 1743 "tag":
1756 1744 (tag,
1757 1745 [('l', 'local', None, 'make the tag local'),
1758 1746 ('m', 'message', "", 'commit message'),
1759 1747 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1760 1748 ('d', 'date', "", 'date code'),
1761 1749 ('u', 'user', "", 'user')],
1762 1750 'hg tag [OPTION]... NAME [REV]'),
1763 1751 "tags": (tags, [], 'hg tags'),
1764 1752 "tip": (tip, [], 'hg tip'),
1765 1753 "undo": (undo, [], 'hg undo'),
1766 1754 "^update|up|checkout|co":
1767 1755 (update,
1768 1756 [('b', 'branch', "", 'checkout the head of a specific branch'),
1769 1757 ('m', 'merge', None, 'allow merging of conflicts'),
1770 1758 ('C', 'clean', None, 'overwrite locally modified files')],
1771 1759 'hg update [-b TAG] [-m] [-C] [REV]'),
1772 1760 "verify": (verify, [], 'hg verify'),
1773 1761 "version": (show_version, [], 'hg version'),
1774 1762 }
1775 1763
1776 1764 globalopts = [
1777 1765 ('R', 'repository', "", 'repository root directory'),
1778 1766 ('', 'cwd', '', 'change working directory'),
1779 1767 ('y', 'noninteractive', None, 'run non-interactively'),
1780 1768 ('q', 'quiet', None, 'quiet mode'),
1781 1769 ('v', 'verbose', None, 'verbose mode'),
1782 1770 ('', 'debug', None, 'debug mode'),
1783 1771 ('', 'traceback', None, 'print traceback on exception'),
1784 1772 ('', 'time', None, 'time how long the command takes'),
1785 1773 ('', 'profile', None, 'profile'),
1786 1774 ('', 'version', None, 'output version information and exit'),
1787 1775 ('h', 'help', None, 'display help and exit'),
1788 1776 ]
1789 1777
1790 1778 norepo = ("clone init version help debugconfig debugdata"
1791 1779 " debugindex debugindexdot paths")
1792 1780
1793 1781 def find(cmd):
1794 1782 for e in table.keys():
1795 1783 if re.match("(%s)$" % e, cmd):
1796 1784 return e, table[e]
1797 1785
1798 1786 raise UnknownCommand(cmd)
1799 1787
1800 1788 class SignalInterrupt(Exception):
1801 1789 """Exception raised on SIGTERM and SIGHUP."""
1802 1790
1803 1791 def catchterm(*args):
1804 1792 raise SignalInterrupt
1805 1793
1806 1794 def run():
1807 1795 sys.exit(dispatch(sys.argv[1:]))
1808 1796
1809 1797 class ParseError(Exception):
1810 1798 """Exception raised on errors in parsing the command line."""
1811 1799
1812 1800 def parse(args):
1813 1801 options = {}
1814 1802 cmdoptions = {}
1815 1803
1816 1804 try:
1817 1805 args = fancyopts.fancyopts(args, globalopts, options)
1818 1806 except fancyopts.getopt.GetoptError, inst:
1819 1807 raise ParseError(None, inst)
1820 1808
1821 1809 if args:
1822 1810 cmd, args = args[0], args[1:]
1823 1811 i = find(cmd)[1]
1824 1812 c = list(i[1])
1825 1813 else:
1826 1814 cmd = None
1827 1815 c = []
1828 1816
1829 1817 # combine global options into local
1830 1818 for o in globalopts:
1831 1819 c.append((o[0], o[1], options[o[1]], o[3]))
1832 1820
1833 1821 try:
1834 1822 args = fancyopts.fancyopts(args, c, cmdoptions)
1835 1823 except fancyopts.getopt.GetoptError, inst:
1836 1824 raise ParseError(cmd, inst)
1837 1825
1838 1826 # separate global options back out
1839 1827 for o in globalopts:
1840 1828 n = o[1]
1841 1829 options[n] = cmdoptions[n]
1842 1830 del cmdoptions[n]
1843 1831
1844 1832 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
1845 1833
1846 1834 def dispatch(args):
1847 1835 signal.signal(signal.SIGTERM, catchterm)
1848 1836 try:
1849 1837 signal.signal(signal.SIGHUP, catchterm)
1850 1838 except AttributeError:
1851 1839 pass
1852 1840
1853 1841 u = ui.ui()
1854 1842 external = []
1855 1843 for x in u.extensions():
1856 1844 if x[1]:
1857 1845 mod = imp.load_source(x[0], x[1])
1858 1846 else:
1859 1847 def importh(name):
1860 1848 mod = __import__(name)
1861 1849 components = name.split('.')
1862 1850 for comp in components[1:]:
1863 1851 mod = getattr(mod, comp)
1864 1852 return mod
1865 1853 mod = importh(x[0])
1866 1854 external.append(mod)
1867 1855 for x in external:
1868 1856 for t in x.cmdtable:
1869 1857 if t in table:
1870 1858 u.warn("module %s override %s\n" % (x.__name__, t))
1871 1859 table.update(x.cmdtable)
1872 1860
1873 1861 try:
1874 1862 cmd, func, args, options, cmdoptions = parse(args)
1875 1863 except ParseError, inst:
1876 1864 if inst.args[0]:
1877 1865 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1878 1866 help_(u, inst.args[0])
1879 1867 else:
1880 1868 u.warn("hg: %s\n" % inst.args[1])
1881 1869 help_(u, 'shortlist')
1882 1870 sys.exit(-1)
1883 1871 except UnknownCommand, inst:
1884 1872 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1885 1873 help_(u, 'shortlist')
1886 1874 sys.exit(1)
1887 1875
1888 1876 if options["time"]:
1889 1877 def get_times():
1890 1878 t = os.times()
1891 1879 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1892 1880 t = (t[0], t[1], t[2], t[3], time.clock())
1893 1881 return t
1894 1882 s = get_times()
1895 1883 def print_time():
1896 1884 t = get_times()
1897 1885 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1898 1886 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1899 1887 atexit.register(print_time)
1900 1888
1901 1889 u.updateopts(options["verbose"], options["debug"], options["quiet"],
1902 1890 not options["noninteractive"])
1903 1891
1904 1892 try:
1905 1893 try:
1906 1894 if options['help']:
1907 1895 help_(u, cmd, options['version'])
1908 1896 sys.exit(0)
1909 1897 elif options['version']:
1910 1898 show_version(u)
1911 1899 sys.exit(0)
1912 1900 elif not cmd:
1913 1901 help_(u, 'shortlist')
1914 1902 sys.exit(0)
1915 1903
1916 1904 if options['cwd']:
1917 1905 try:
1918 1906 os.chdir(options['cwd'])
1919 1907 except OSError, inst:
1920 1908 u.warn('abort: %s: %s\n' % (options['cwd'], inst.strerror))
1921 1909 sys.exit(1)
1922 1910
1923 1911 if cmd not in norepo.split():
1924 1912 path = options["repository"] or ""
1925 1913 repo = hg.repository(ui=u, path=path)
1926 1914 for x in external:
1927 1915 x.reposetup(u, repo)
1928 1916 d = lambda: func(u, repo, *args, **cmdoptions)
1929 1917 else:
1930 1918 d = lambda: func(u, *args, **cmdoptions)
1931 1919
1932 1920 if options['profile']:
1933 1921 import hotshot, hotshot.stats
1934 1922 prof = hotshot.Profile("hg.prof")
1935 1923 r = prof.runcall(d)
1936 1924 prof.close()
1937 1925 stats = hotshot.stats.load("hg.prof")
1938 1926 stats.strip_dirs()
1939 1927 stats.sort_stats('time', 'calls')
1940 1928 stats.print_stats(40)
1941 1929 return r
1942 1930 else:
1943 1931 return d()
1944 1932 except:
1945 1933 if options['traceback']:
1946 1934 traceback.print_exc()
1947 1935 raise
1948 1936 except hg.RepoError, inst:
1949 1937 u.warn("abort: ", inst, "!\n")
1950 1938 except SignalInterrupt:
1951 1939 u.warn("killed!\n")
1952 1940 except KeyboardInterrupt:
1953 1941 try:
1954 1942 u.warn("interrupted!\n")
1955 1943 except IOError, inst:
1956 1944 if inst.errno == errno.EPIPE:
1957 1945 if u.debugflag:
1958 1946 u.warn("\nbroken pipe\n")
1959 1947 else:
1960 1948 raise
1961 1949 except IOError, inst:
1962 1950 if hasattr(inst, "code"):
1963 1951 u.warn("abort: %s\n" % inst)
1964 1952 elif hasattr(inst, "reason"):
1965 1953 u.warn("abort: error: %s\n" % inst.reason[1])
1966 1954 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1967 1955 if u.debugflag:
1968 1956 u.warn("broken pipe\n")
1969 1957 else:
1970 1958 raise
1971 1959 except OSError, inst:
1972 1960 if hasattr(inst, "filename"):
1973 1961 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1974 1962 else:
1975 1963 u.warn("abort: %s\n" % inst.strerror)
1976 1964 except util.Abort, inst:
1977 1965 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1978 1966 sys.exit(1)
1979 1967 except TypeError, inst:
1980 1968 # was this an argument error?
1981 1969 tb = traceback.extract_tb(sys.exc_info()[2])
1982 1970 if len(tb) > 2: # no
1983 1971 raise
1984 1972 u.debug(inst, "\n")
1985 1973 u.warn("%s: invalid arguments\n" % cmd)
1986 1974 help_(u, cmd)
1987 1975 except UnknownCommand, inst:
1988 1976 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1989 1977 help_(u, 'shortlist')
1990 1978
1991 1979 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now