##// END OF EJS Templates
Make import command reject patches that resemble email messages....
bos@serpentine.internal.keyresearch.com -
r1190:737f9b90 default
parent child Browse files
Show More
@@ -1,716 +1,723 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 If a patch looks like a mail message (its first line starts with
255 "From " or looks like an RFC822 header), it will not be applied
256 unless the -m option is used. The importer neither parses nor
257 discards mail headers, so use -m only to override the "mailness"
258 safety check, not to import a real mail message.
259
254 260 options:
255 261 -p, --strip <n> directory strip option for patch. This has the same
256 262 meaning as the corresponding patch option
257 263 -b <path> base directory to read patches from
258 264 -f, --force skip check for outstanding uncommitted changes
265 -m, --mail-like apply a patch that appears to be a mail message
259 266
260 267 aliases: patch
261 268
262 269 incoming [-p] [source]::
263 270 Show new changesets found in the specified repo or the default
264 271 pull repo. These are the changesets that would be pulled if a pull
265 272 was requested.
266 273
267 274 Currently only local repositories are supported.
268 275
269 276 options:
270 277 -p, --patch show patch
271 278
272 279 aliases: in
273 280
274 281 init [dest]::
275 282 Initialize a new repository in the given directory. If the given
276 283 directory does not exist, it is created.
277 284
278 285 If no directory is given, the current directory is used.
279 286
280 287 locate [options] [files]::
281 288 Print all files under Mercurial control whose names match the
282 289 given patterns.
283 290
284 291 This command searches the current directory and its
285 292 subdirectories. To search an entire repository, move to the root
286 293 of the repository.
287 294
288 295 If no patterns are given to match, this command prints all file
289 296 names.
290 297
291 298 If you want to feed the output of this command into the "xargs"
292 299 command, use the "-0" option to both this command and "xargs".
293 300 This will avoid the problem of "xargs" treating single filenames
294 301 that contain white space as multiple file names.
295 302
296 303 options:
297 304
298 305 -0, --print0 end filenames with NUL, for use with xargs
299 306 -f, --fullpath print complete paths from the filesystem root
300 307 -I, --include <pat> include names matching the given patterns
301 308 -r, --rev <rev> search the repository as it stood at rev
302 309 -X, --exclude <pat> exclude names matching the given patterns
303 310
304 311 log [-r revision ...] [-p] [files]::
305 312 Print the revision history of the specified files or the entire project.
306 313
307 314 By default this command outputs: changeset id and hash, tags,
308 315 parents, user, date and time, and a summary for each commit. The
309 316 -v switch adds some more detail, such as changed files, manifest
310 317 hashes or message signatures.
311 318
312 319 options:
313 320 -I, --include <pat> include names matching the given patterns
314 321 -X, --exclude <pat> exclude names matching the given patterns
315 322 -r, --rev <A> show the specified revision or range
316 323 -p, --patch show patch
317 324
318 325 aliases: history
319 326
320 327 manifest [revision]::
321 328 Print a list of version controlled files for the given revision.
322 329
323 330 The manifest is the list of files being version controlled. If no revision
324 331 is given then the tip is used.
325 332
326 333 outgoing [-p] [dest]::
327 334 Show changesets not found in the specified destination repo or the
328 335 default push repo. These are the changesets that would be pushed
329 336 if a push was requested.
330 337
331 338 options:
332 339 -p, --patch show patch
333 340
334 341 aliases: out
335 342
336 343 parents::
337 344 Print the working directory's parent revisions.
338 345
339 346 paths [NAME]::
340 347 Show definition of symbolic path name NAME. If no name is given, show
341 348 definition of available names.
342 349
343 350 Path names are defined in the [paths] section of /etc/mercurial/hgrc
344 351 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
345 352
346 353 pull <repository path>::
347 354 Pull changes from a remote repository to a local one.
348 355
349 356 This finds all changes from the repository at the specified path
350 357 or URL and adds them to the local repository. By default, this
351 358 does not update the copy of the project in the working directory.
352 359
353 360 Valid URLs are of the form:
354 361
355 362 local/filesystem/path
356 363 http://[user@]host[:port][/path]
357 364 https://[user@]host[:port][/path]
358 365 ssh://[user@]host[:port][/path]
359 366
360 367 SSH requires an accessible shell account on the destination
361 368 machine and a copy of hg in the remote path.
362 369
363 370 options:
364 371 -u, --update update the working directory to tip after pull
365 372 -e, --ssh specify ssh command to use
366 373 --remotecmd specify hg command to run on the remote side
367 374
368 375 push <destination>::
369 376 Push changes from the local repository to the given destination.
370 377
371 378 This is the symmetrical operation for pull. It helps to move
372 379 changes from the current repository to a different one. If the
373 380 destination is local this is identical to a pull in that directory
374 381 from the current one.
375 382
376 383 By default, push will refuse to run if it detects the result would
377 384 increase the number of remote heads. This generally indicates the
378 385 the client has forgotten to sync and merge before pushing.
379 386
380 387 Valid URLs are of the form:
381 388
382 389 local/filesystem/path
383 390 ssh://[user@]host[:port][/path]
384 391
385 392 SSH requires an accessible shell account on the destination
386 393 machine and a copy of hg in the remote path.
387 394
388 395 options:
389 396
390 397 -f, --force force update
391 398 -e, --ssh specify ssh command to use
392 399 --remotecmd specify hg command to run on the remote side
393 400
394 401 rawcommit [-p -d -u -F -m -l]::
395 402 Lowlevel commit, for use in helper scripts.
396 403
397 404 This command is not intended to be used by normal users, as it is
398 405 primarily useful for importing from other SCMs.
399 406
400 407 recover::
401 408 Recover from an interrupted commit or pull.
402 409
403 410 This command tries to fix the repository status after an interrupted
404 411 operation. It should only be necessary when Mercurial suggests it.
405 412
406 413 remove [options] [files ...]::
407 414 Schedule the indicated files for removal from the repository.
408 415
409 416 This command schedules the files to be removed at the next commit.
410 417 This only removes files from the current branch, not from the
411 418 entire project history.
412 419
413 420 aliases: rm
414 421
415 422 revert [names ...]::
416 423 Revert any uncommitted modifications made to the named files or
417 424 directories. This restores the contents of the affected files to
418 425 an unmodified state.
419 426
420 427 If a file has been deleted, it is recreated. If the executable
421 428 mode of a file was changed, it is reset.
422 429
423 430 If a directory is given, all files in that directory and its
424 431 subdirectories are reverted.
425 432
426 433 If no arguments are given, all files in the current directory and
427 434 its subdirectories are reverted.
428 435
429 436 options:
430 437 -r, --rev <rev> revision to revert to
431 438 -n, --nonrecursive do not recurse into subdirectories
432 439
433 440 root::
434 441 Print the root directory of the current repository.
435 442
436 443 serve [options]::
437 444 Start a local HTTP repository browser and pull server.
438 445
439 446 By default, the server logs accesses to stdout and errors to
440 447 stderr. Use the "-A" and "-E" options to log to files.
441 448
442 449 options:
443 450 -A, --accesslog <file> name of access log file to write to
444 451 -E, --errorlog <file> name of error log file to write to
445 452 -a, --address <addr> address to use
446 453 -p, --port <n> port to use (default: 8000)
447 454 -n, --name <name> name to show in web pages (default: working dir)
448 455 -t, --templatedir <path> web templates to use
449 456 -6, --ipv6 use IPv6 in addition to IPv4
450 457
451 458 status [options] [files]::
452 459 Show changed files in the working directory. If no names are
453 460 given, all files are shown. Otherwise, only files matching the
454 461 given names are shown.
455 462
456 463 The codes used to show the status of files are:
457 464
458 465 M = changed
459 466 A = added
460 467 R = removed
461 468 ? = not tracked
462 469
463 470 options:
464 471
465 472 -m, --modified show only modified files
466 473 -a, --added show only added files
467 474 -r, --removed show only removed files
468 475 -u, --unknown show only unknown (not tracked) files
469 476 -n, --no-status hide status prefix
470 477 -0, --print0 end filenames with NUL, for use with xargs
471 478 -I, --include <pat> include names matching the given patterns
472 479 -X, --exclude <pat> exclude names matching the given patterns
473 480
474 481 tag [-l -m <text> -d <datecode> -u <user>] <name> [revision]::
475 482 Name a particular revision using <name>.
476 483
477 484 Tags are used to name particular revisions of the repository and are
478 485 very useful to compare different revision, to go back to significant
479 486 earlier versions or to mark branch points as releases, etc.
480 487
481 488 If no revision is given, the tip is used.
482 489
483 490 To facilitate version control, distribution, and merging of tags,
484 491 they are stored as a file named ".hgtags" which is managed
485 492 similarly to other project files and can be hand-edited if
486 493 necessary.
487 494
488 495 options:
489 496 -l, --local make the tag local
490 497 -m, --message <text> message for tag commit log entry
491 498 -d, --date <datecode> datecode for commit
492 499 -u, --user <user> user for commit
493 500
494 501 Note: Local tags are not version-controlled or distributed and are
495 502 stored in the .hg/localtags file. If there exists a local tag and
496 503 a public tag with the same name, local tag is used.
497 504
498 505 tags::
499 506 List the repository tags.
500 507
501 508 This lists both regular and local tags.
502 509
503 510 tip::
504 511 Show the tip revision.
505 512
506 513 undo::
507 514 Undo the last commit or pull transaction.
508 515
509 516 Roll back the last pull or commit transaction on the
510 517 repository, restoring the project to its earlier state.
511 518
512 519 This command should be used with care. There is only one level of
513 520 undo and there is no redo.
514 521
515 522 This command is not intended for use on public repositories. Once
516 523 a change is visible for pull by other users, undoing it locally is
517 524 ineffective.
518 525
519 526 update [-m -C] [revision]::
520 527 Update the working directory to the specified revision.
521 528
522 529 By default, update will refuse to run if doing so would require
523 530 merging or discarding local changes.
524 531
525 532 With the -m option, a merge will be performed.
526 533
527 534 With the -C option, local changes will be lost.
528 535
529 536 options:
530 537 -m, --merge allow merging of branches
531 538 -C, --clean overwrite locally modified files
532 539
533 540 aliases: up checkout co
534 541
535 542 verify::
536 543 Verify the integrity of the current repository.
537 544
538 545 This will perform an extensive check of the repository's
539 546 integrity, validating the hashes and checksums of each entry in
540 547 the changelog, manifest, and tracked files, as well as the
541 548 integrity of their crosslinks and indices.
542 549
543 550 FILE NAME PATTERNS
544 551 ------------------
545 552
546 553 Mercurial accepts several notations for identifying one or more
547 554 file at a time.
548 555
549 556 By default, Mercurial treats file names as shell-style extended
550 557 glob patterns.
551 558
552 559 Alternate pattern notations must be specified explicitly.
553 560
554 561 To use a plain path name without any pattern matching, start a
555 562 name with "path:". These path names must match completely, from
556 563 the root of the current repository.
557 564
558 565 To use an extended glob, start a name with "glob:". Globs are
559 566 rooted at the current directory; a glob such as "*.c" will match
560 567 files ending in ".c" in the current directory only.
561 568
562 569 The supported glob syntax extensions are "**" to match any string
563 570 across path separators, and "{a,b}" to mean "a or b".
564 571
565 572 To use a Perl/Python regular expression, start a name with "re:".
566 573 Regexp pattern matching is anchored at the root of the repository.
567 574
568 575 Plain examples:
569 576
570 577 path:foo/bar a name bar in a directory named foo in the root of
571 578 the repository
572 579 path:path:name a file or directory named "path:name"
573 580
574 581 Glob examples:
575 582
576 583 glob:*.c any name ending in ".c" in the current directory
577 584 *.c any name ending in ".c" in the current directory
578 585 **.c any name ending in ".c" in the current directory, or
579 586 any subdirectory
580 587 foo/*.c any name ending in ".c" in the directory foo
581 588 foo/**.c any name ending in ".c" in the directory foo, or any
582 589 subdirectory
583 590
584 591 Regexp examples:
585 592
586 593 re:.*\.c$ any name ending in ".c", anywhere in the repository
587 594
588 595
589 596 SPECIFYING SINGLE REVISIONS
590 597 ---------------------------
591 598
592 599 Mercurial accepts several notations for identifying individual
593 600 revisions.
594 601
595 602 A plain integer is treated as a revision number. Negative
596 603 integers are treated as offsets from the tip, with -1 denoting the
597 604 tip.
598 605
599 606 A 40-digit hexadecimal string is treated as a unique revision
600 607 identifier.
601 608
602 609 A hexadecimal string less than 40 characters long is treated as a
603 610 unique revision identifier, and referred to as a short-form
604 611 identifier. A short-form identifier is only valid if it is the
605 612 prefix of one full-length identifier.
606 613
607 614 Any other string is treated as a tag name, which is a symbolic
608 615 name associated with a revision identifier. Tag names may not
609 616 contain the ":" character.
610 617
611 618 The reserved name "tip" is a special tag that always identifies
612 619 the most recent revision.
613 620
614 621 SPECIFYING MULTIPLE REVISIONS
615 622 -----------------------------
616 623
617 624 When Mercurial accepts more than one revision, they may be
618 625 specified individually, or provided as a continuous range,
619 626 separated by the ":" character.
620 627
621 628 The syntax of range notation is [BEGIN]:[END], where BEGIN and END
622 629 are revision identifiers. Both BEGIN and END are optional. If
623 630 BEGIN is not specified, it defaults to revision number 0. If END
624 631 is not specified, it defaults to the tip. The range ":" thus
625 632 means "all revisions".
626 633
627 634 If BEGIN is greater than END, revisions are treated in reverse
628 635 order.
629 636
630 637 A range acts as a closed interval. This means that a range of 3:5
631 638 gives 3, 4 and 5. Similarly, a range of 4:2 gives 4, 3, and 2.
632 639
633 640 ENVIRONMENT VARIABLES
634 641 ---------------------
635 642
636 643 HGEDITOR::
637 644 This is the name of the editor to use when committing. Defaults to the
638 645 value of EDITOR.
639 646
640 647 (deprecated, use .hgrc)
641 648
642 649 HGMERGE::
643 650 An executable to use for resolving merge conflicts. The program
644 651 will be executed with three arguments: local file, remote file,
645 652 ancestor file.
646 653
647 654 The default program is "hgmerge", which is a shell script provided
648 655 by Mercurial with some sensible defaults.
649 656
650 657 (deprecated, use .hgrc)
651 658
652 659 HGUSER::
653 660 This is the string used for the author of a commit.
654 661
655 662 (deprecated, use .hgrc)
656 663
657 664 EMAIL::
658 665 If HGUSER is not set, this will be used as the author for a commit.
659 666
660 667 LOGNAME::
661 668 If neither HGUSER nor EMAIL is set, LOGNAME will be used (with
662 669 '@hostname' appended) as the author value for a commit.
663 670
664 671 EDITOR::
665 672 This is the name of the editor used in the hgmerge script. It will be
666 673 used for commit messages if HGEDITOR isn't set. Defaults to 'vi'.
667 674
668 675 PYTHONPATH::
669 676 This is used by Python to find imported modules and may need to be set
670 677 appropriately if Mercurial is not installed system-wide.
671 678
672 679 FILES
673 680 -----
674 681 .hgignore::
675 682 This file contains regular expressions (one per line) that describe file
676 683 names that should be ignored by hg.
677 684
678 685 .hgtags::
679 686 This file contains changeset hash values and text tag names (one of each
680 687 separated by spaces) that correspond to tagged versions of the repository
681 688 contents.
682 689
683 690 /etc/mercurial/hgrc, $HOME/.hgrc, .hg/hgrc::
684 691 This file contains defaults and configuration. Values in .hg/hgrc
685 692 override those in $HOME/.hgrc, and these override settings made in the
686 693 global /etc/mercurial/hgrc configuration. See hgrc(5) for details of
687 694 the contents and format of these files.
688 695
689 696 BUGS
690 697 ----
691 698 Probably lots, please post them to the mailing list (See Resources below)
692 699 when you find them.
693 700
694 701 SEE ALSO
695 702 --------
696 703 hgrc(5)
697 704
698 705 AUTHOR
699 706 ------
700 707 Written by Matt Mackall <mpm@selenic.com>
701 708
702 709 RESOURCES
703 710 ---------
704 711 http://selenic.com/mercurial[Main Web Site]
705 712
706 713 http://www.serpentine.com/mercurial[Wiki site]
707 714
708 715 http://selenic.com/hg[Source code repository]
709 716
710 717 http://selenic.com/mailman/listinfo/mercurial[Mailing list]
711 718
712 719 COPYING
713 720 -------
714 721 Copyright (C) 2005 Matt Mackall.
715 722 Free use of this software is granted under the terms of the GNU General
716 723 Public License (GPL).
@@ -1,1984 +1,1991 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 mailre = re.compile(r'(From |[\w-]+:)')
1013
1012 1014 for patch in patches:
1013 1015 ui.status("applying %s\n" % patch)
1014 1016 pf = os.path.join(d, patch)
1015 1017
1016 1018 message = []
1017 1019 user = None
1018 1020 hgpatch = False
1019 1021 for line in file(pf):
1020 1022 line = line.rstrip()
1023 if not message and mailre.match(line) and not opts['mail_like']:
1024 if len(line) > 35: line = line[:32] + '...'
1025 raise util.Abort('first line looks like a '
1026 'mail header: ' + line)
1021 1027 if line.startswith("--- ") or line.startswith("diff -r"):
1022 1028 break
1023 1029 elif hgpatch:
1024 1030 # parse values when importing the result of an hg export
1025 1031 if line.startswith("# User "):
1026 1032 user = line[7:]
1027 1033 ui.debug('User: %s\n' % user)
1028 1034 elif not line.startswith("# ") and line:
1029 1035 message.append(line)
1030 1036 hgpatch = False
1031 1037 elif line == '# HG changeset patch':
1032 1038 hgpatch = True
1033 1039 message = [] # We may have collected garbage
1034 1040 else:
1035 1041 message.append(line)
1036 1042
1037 1043 # make sure message isn't empty
1038 1044 if not message:
1039 1045 message = "imported patch %s\n" % patch
1040 1046 else:
1041 1047 message = "%s\n" % '\n'.join(message)
1042 1048 ui.debug('message:\n%s\n' % message)
1043 1049
1044 1050 f = os.popen("patch -p%d < '%s'" % (strip, pf))
1045 1051 files = []
1046 1052 for l in f.read().splitlines():
1047 1053 l.rstrip('\r\n');
1048 1054 ui.status("%s\n" % l)
1049 1055 if l.startswith('patching file '):
1050 1056 pf = l[14:]
1051 1057 if pf not in files:
1052 1058 files.append(pf)
1053 1059 patcherr = f.close()
1054 1060 if patcherr:
1055 1061 raise util.Abort("patch failed")
1056 1062
1057 1063 if len(files) > 0:
1058 1064 addremove(ui, repo, *files)
1059 1065 repo.commit(files, message, user)
1060 1066
1061 1067 def incoming(ui, repo, source="default", **opts):
1062 1068 """show new changesets found in source"""
1063 1069 source = ui.expandpath(source)
1064 1070 other = hg.repository(ui, source)
1065 1071 if not other.local():
1066 1072 ui.warn("abort: incoming doesn't work for remote"
1067 1073 + " repositories yet, sorry!\n")
1068 1074 return 1
1069 1075 o = repo.findincoming(other)
1070 1076 if not o:
1071 1077 return
1072 1078 o = other.newer(o)
1073 1079 o.reverse()
1074 1080 for n in o:
1075 1081 show_changeset(ui, other, changenode=n)
1076 1082 if opts['patch']:
1077 1083 prev = other.changelog.parents(n)[0]
1078 1084 dodiff(ui, ui, other, prev, n)
1079 1085 ui.write("\n")
1080 1086
1081 1087 def init(ui, dest="."):
1082 1088 """create a new repository in the given directory"""
1083 1089 if not os.path.exists(dest):
1084 1090 os.mkdir(dest)
1085 1091 hg.repository(ui, dest, create=1)
1086 1092
1087 1093 def locate(ui, repo, *pats, **opts):
1088 1094 """locate files matching specific patterns"""
1089 1095 end = opts['print0'] and '\0' or '\n'
1090 1096
1091 1097 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1092 1098 if repo.dirstate.state(abs) == '?':
1093 1099 continue
1094 1100 if opts['fullpath']:
1095 1101 ui.write(os.path.join(repo.root, abs), end)
1096 1102 else:
1097 1103 ui.write(rel, end)
1098 1104
1099 1105 def log(ui, repo, *pats, **opts):
1100 1106 """show revision history of entire repository or files"""
1101 1107 class dui:
1102 1108 # Implement and delegate some ui protocol. Save hunks of
1103 1109 # output for later display in the desired order.
1104 1110 def __init__(self, ui):
1105 1111 self.ui = ui
1106 1112 self.hunk = {}
1107 1113 def bump(self, rev):
1108 1114 self.rev = rev
1109 1115 self.hunk[rev] = []
1110 1116 def note(self, *args):
1111 1117 if self.verbose:
1112 1118 self.write(*args)
1113 1119 def status(self, *args):
1114 1120 if not self.quiet:
1115 1121 self.write(*args)
1116 1122 def write(self, *args):
1117 1123 self.hunk[self.rev].append(args)
1118 1124 def __getattr__(self, key):
1119 1125 return getattr(self.ui, key)
1120 1126 cwd = repo.getcwd()
1121 1127 if not pats and cwd:
1122 1128 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1123 1129 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1124 1130 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1125 1131 pats, opts)
1126 1132 for st, rev, fns in changeiter:
1127 1133 if st == 'window':
1128 1134 du = dui(ui)
1129 1135 elif st == 'add':
1130 1136 du.bump(rev)
1131 1137 show_changeset(du, repo, rev)
1132 1138 if opts['patch']:
1133 1139 changenode = repo.changelog.node(rev)
1134 1140 prev, other = repo.changelog.parents(changenode)
1135 1141 dodiff(du, du, repo, prev, changenode, fns)
1136 1142 du.write("\n\n")
1137 1143 elif st == 'iter':
1138 1144 for args in du.hunk[rev]:
1139 1145 ui.write(*args)
1140 1146
1141 1147 def manifest(ui, repo, rev=None):
1142 1148 """output the latest or given revision of the project manifest"""
1143 1149 if rev:
1144 1150 try:
1145 1151 # assume all revision numbers are for changesets
1146 1152 n = repo.lookup(rev)
1147 1153 change = repo.changelog.read(n)
1148 1154 n = change[0]
1149 1155 except hg.RepoError:
1150 1156 n = repo.manifest.lookup(rev)
1151 1157 else:
1152 1158 n = repo.manifest.tip()
1153 1159 m = repo.manifest.read(n)
1154 1160 mf = repo.manifest.readflags(n)
1155 1161 files = m.keys()
1156 1162 files.sort()
1157 1163
1158 1164 for f in files:
1159 1165 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1160 1166
1161 1167 def outgoing(ui, repo, dest="default-push", **opts):
1162 1168 """show changesets not found in destination"""
1163 1169 dest = ui.expandpath(dest)
1164 1170 other = hg.repository(ui, dest)
1165 1171 o = repo.findoutgoing(other)
1166 1172 o = repo.newer(o)
1167 1173 o.reverse()
1168 1174 for n in o:
1169 1175 show_changeset(ui, repo, changenode=n)
1170 1176 if opts['patch']:
1171 1177 prev = repo.changelog.parents(n)[0]
1172 1178 dodiff(ui, ui, repo, prev, n)
1173 1179 ui.write("\n")
1174 1180
1175 1181 def parents(ui, repo, rev=None):
1176 1182 """show the parents of the working dir or revision"""
1177 1183 if rev:
1178 1184 p = repo.changelog.parents(repo.lookup(rev))
1179 1185 else:
1180 1186 p = repo.dirstate.parents()
1181 1187
1182 1188 for n in p:
1183 1189 if n != nullid:
1184 1190 show_changeset(ui, repo, changenode=n)
1185 1191
1186 1192 def paths(ui, search=None):
1187 1193 """show definition of symbolic path names"""
1188 1194 try:
1189 1195 repo = hg.repository(ui=ui)
1190 1196 except hg.RepoError:
1191 1197 pass
1192 1198
1193 1199 if search:
1194 1200 for name, path in ui.configitems("paths"):
1195 1201 if name == search:
1196 1202 ui.write("%s\n" % path)
1197 1203 return
1198 1204 ui.warn("not found!\n")
1199 1205 return 1
1200 1206 else:
1201 1207 for name, path in ui.configitems("paths"):
1202 1208 ui.write("%s = %s\n" % (name, path))
1203 1209
1204 1210 def pull(ui, repo, source="default", **opts):
1205 1211 """pull changes from the specified source"""
1206 1212 source = ui.expandpath(source)
1207 1213 ui.status('pulling from %s\n' % (source))
1208 1214
1209 1215 if opts['ssh']:
1210 1216 ui.setconfig("ui", "ssh", opts['ssh'])
1211 1217 if opts['remotecmd']:
1212 1218 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1213 1219
1214 1220 other = hg.repository(ui, source)
1215 1221 r = repo.pull(other)
1216 1222 if not r:
1217 1223 if opts['update']:
1218 1224 return update(ui, repo)
1219 1225 else:
1220 1226 ui.status("(run 'hg update' to get a working copy)\n")
1221 1227
1222 1228 return r
1223 1229
1224 1230 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1225 1231 """push changes to the specified destination"""
1226 1232 dest = ui.expandpath(dest)
1227 1233 ui.status('pushing to %s\n' % (dest))
1228 1234
1229 1235 if ssh:
1230 1236 ui.setconfig("ui", "ssh", ssh)
1231 1237 if remotecmd:
1232 1238 ui.setconfig("ui", "remotecmd", remotecmd)
1233 1239
1234 1240 other = hg.repository(ui, dest)
1235 1241 r = repo.push(other, force)
1236 1242 return r
1237 1243
1238 1244 def rawcommit(ui, repo, *flist, **rc):
1239 1245 "raw commit interface"
1240 1246 if rc['text']:
1241 1247 ui.warn("Warning: -t and --text is deprecated,"
1242 1248 " please use -m or --message instead.\n")
1243 1249 message = rc['message'] or rc['text']
1244 1250 if not message and rc['logfile']:
1245 1251 try:
1246 1252 message = open(rc['logfile']).read()
1247 1253 except IOError:
1248 1254 pass
1249 1255 if not message and not rc['logfile']:
1250 1256 ui.warn("abort: missing commit message\n")
1251 1257 return 1
1252 1258
1253 1259 files = relpath(repo, list(flist))
1254 1260 if rc['files']:
1255 1261 files += open(rc['files']).read().splitlines()
1256 1262
1257 1263 rc['parent'] = map(repo.lookup, rc['parent'])
1258 1264
1259 1265 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1260 1266
1261 1267 def recover(ui, repo):
1262 1268 """roll back an interrupted transaction"""
1263 1269 repo.recover()
1264 1270
1265 1271 def remove(ui, repo, pat, *pats, **opts):
1266 1272 """remove the specified files on the next commit"""
1267 1273 names = []
1268 1274 def okaytoremove(abs, rel, exact):
1269 1275 c, a, d, u = repo.changes(files = [abs])
1270 1276 reason = None
1271 1277 if c: reason = 'is modified'
1272 1278 elif a: reason = 'has been marked for add'
1273 1279 elif u: reason = 'not managed'
1274 1280 if reason and exact:
1275 1281 ui.warn('not removing %s: file %s\n' % (rel, reason))
1276 1282 else:
1277 1283 return True
1278 1284 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1279 1285 if okaytoremove(abs, rel, exact):
1280 1286 if not exact: ui.status('removing %s\n' % rel)
1281 1287 names.append(abs)
1282 1288 repo.remove(names)
1283 1289
1284 1290 def revert(ui, repo, *names, **opts):
1285 1291 """revert modified files or dirs back to their unmodified states"""
1286 1292 node = opts['rev'] and repo.lookup(opts['rev']) or \
1287 1293 repo.dirstate.parents()[0]
1288 1294 root = os.path.realpath(repo.root)
1289 1295
1290 1296 def trimpath(p):
1291 1297 p = os.path.realpath(p)
1292 1298 if p.startswith(root):
1293 1299 rest = p[len(root):]
1294 1300 if not rest:
1295 1301 return rest
1296 1302 if p.startswith(os.sep):
1297 1303 return rest[1:]
1298 1304 return p
1299 1305
1300 1306 relnames = map(trimpath, names or [os.getcwd()])
1301 1307 chosen = {}
1302 1308
1303 1309 def choose(name):
1304 1310 def body(name):
1305 1311 for r in relnames:
1306 1312 if not name.startswith(r):
1307 1313 continue
1308 1314 rest = name[len(r):]
1309 1315 if not rest:
1310 1316 return r, True
1311 1317 depth = rest.count(os.sep)
1312 1318 if not r:
1313 1319 if depth == 0 or not opts['nonrecursive']:
1314 1320 return r, True
1315 1321 elif rest[0] == os.sep:
1316 1322 if depth == 1 or not opts['nonrecursive']:
1317 1323 return r, True
1318 1324 return None, False
1319 1325 relname, ret = body(name)
1320 1326 if ret:
1321 1327 chosen[relname] = 1
1322 1328 return ret
1323 1329
1324 1330 r = repo.update(node, False, True, choose, False)
1325 1331 for n in relnames:
1326 1332 if n not in chosen:
1327 1333 ui.warn('error: no matches for %s\n' % n)
1328 1334 r = 1
1329 1335 sys.stdout.flush()
1330 1336 return r
1331 1337
1332 1338 def root(ui, repo):
1333 1339 """print the root (top) of the current working dir"""
1334 1340 ui.write(repo.root + "\n")
1335 1341
1336 1342 def serve(ui, repo, **opts):
1337 1343 """export the repository via HTTP"""
1338 1344
1339 1345 if opts["stdio"]:
1340 1346 fin, fout = sys.stdin, sys.stdout
1341 1347 sys.stdout = sys.stderr
1342 1348
1343 1349 def getarg():
1344 1350 argline = fin.readline()[:-1]
1345 1351 arg, l = argline.split()
1346 1352 val = fin.read(int(l))
1347 1353 return arg, val
1348 1354 def respond(v):
1349 1355 fout.write("%d\n" % len(v))
1350 1356 fout.write(v)
1351 1357 fout.flush()
1352 1358
1353 1359 lock = None
1354 1360
1355 1361 while 1:
1356 1362 cmd = fin.readline()[:-1]
1357 1363 if cmd == '':
1358 1364 return
1359 1365 if cmd == "heads":
1360 1366 h = repo.heads()
1361 1367 respond(" ".join(map(hex, h)) + "\n")
1362 1368 if cmd == "lock":
1363 1369 lock = repo.lock()
1364 1370 respond("")
1365 1371 if cmd == "unlock":
1366 1372 if lock:
1367 1373 lock.release()
1368 1374 lock = None
1369 1375 respond("")
1370 1376 elif cmd == "branches":
1371 1377 arg, nodes = getarg()
1372 1378 nodes = map(bin, nodes.split(" "))
1373 1379 r = []
1374 1380 for b in repo.branches(nodes):
1375 1381 r.append(" ".join(map(hex, b)) + "\n")
1376 1382 respond("".join(r))
1377 1383 elif cmd == "between":
1378 1384 arg, pairs = getarg()
1379 1385 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1380 1386 r = []
1381 1387 for b in repo.between(pairs):
1382 1388 r.append(" ".join(map(hex, b)) + "\n")
1383 1389 respond("".join(r))
1384 1390 elif cmd == "changegroup":
1385 1391 nodes = []
1386 1392 arg, roots = getarg()
1387 1393 nodes = map(bin, roots.split(" "))
1388 1394
1389 1395 cg = repo.changegroup(nodes)
1390 1396 while 1:
1391 1397 d = cg.read(4096)
1392 1398 if not d:
1393 1399 break
1394 1400 fout.write(d)
1395 1401
1396 1402 fout.flush()
1397 1403
1398 1404 elif cmd == "addchangegroup":
1399 1405 if not lock:
1400 1406 respond("not locked")
1401 1407 continue
1402 1408 respond("")
1403 1409
1404 1410 r = repo.addchangegroup(fin)
1405 1411 respond("")
1406 1412
1407 1413 optlist = "name templates style address port ipv6 accesslog errorlog"
1408 1414 for o in optlist.split():
1409 1415 if opts[o]:
1410 1416 ui.setconfig("web", o, opts[o])
1411 1417
1412 1418 try:
1413 1419 httpd = hgweb.create_server(repo)
1414 1420 except socket.error, inst:
1415 1421 raise util.Abort('cannot start server: ' + inst.args[1])
1416 1422
1417 1423 if ui.verbose:
1418 1424 addr, port = httpd.socket.getsockname()
1419 1425 if addr == '0.0.0.0':
1420 1426 addr = socket.gethostname()
1421 1427 else:
1422 1428 try:
1423 1429 addr = socket.gethostbyaddr(addr)[0]
1424 1430 except socket.error:
1425 1431 pass
1426 1432 if port != 80:
1427 1433 ui.status('listening at http://%s:%d/\n' % (addr, port))
1428 1434 else:
1429 1435 ui.status('listening at http://%s/\n' % addr)
1430 1436 httpd.serve_forever()
1431 1437
1432 1438 def status(ui, repo, *pats, **opts):
1433 1439 '''show changed files in the working directory
1434 1440
1435 1441 M = modified
1436 1442 A = added
1437 1443 R = removed
1438 1444 ? = not tracked
1439 1445 '''
1440 1446
1441 1447 cwd = repo.getcwd()
1442 1448 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1443 1449 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1444 1450 for n in repo.changes(files=files, match=matchfn)]
1445 1451
1446 1452 changetypes = [('modified', 'M', c),
1447 1453 ('added', 'A', a),
1448 1454 ('removed', 'R', d),
1449 1455 ('unknown', '?', u)]
1450 1456
1451 1457 end = opts['print0'] and '\0' or '\n'
1452 1458
1453 1459 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1454 1460 or changetypes):
1455 1461 if opts['no_status']:
1456 1462 format = "%%s%s" % end
1457 1463 else:
1458 1464 format = "%s %%s%s" % (char, end);
1459 1465
1460 1466 for f in changes:
1461 1467 ui.write(format % f)
1462 1468
1463 1469 def tag(ui, repo, name, rev=None, **opts):
1464 1470 """add a tag for the current tip or a given revision"""
1465 1471 if opts['text']:
1466 1472 ui.warn("Warning: -t and --text is deprecated,"
1467 1473 " please use -m or --message instead.\n")
1468 1474 if name == "tip":
1469 1475 ui.warn("abort: 'tip' is a reserved name!\n")
1470 1476 return -1
1471 1477 if rev:
1472 1478 r = hex(repo.lookup(rev))
1473 1479 else:
1474 1480 r = hex(repo.changelog.tip())
1475 1481
1476 1482 if name.find(revrangesep) >= 0:
1477 1483 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1478 1484 return -1
1479 1485
1480 1486 if opts['local']:
1481 1487 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1482 1488 return
1483 1489
1484 1490 (c, a, d, u) = repo.changes()
1485 1491 for x in (c, a, d, u):
1486 1492 if ".hgtags" in x:
1487 1493 ui.warn("abort: working copy of .hgtags is changed!\n")
1488 1494 ui.status("(please commit .hgtags manually)\n")
1489 1495 return -1
1490 1496
1491 1497 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1492 1498 if repo.dirstate.state(".hgtags") == '?':
1493 1499 repo.add([".hgtags"])
1494 1500
1495 1501 message = (opts['message'] or opts['text'] or
1496 1502 "Added tag %s for changeset %s" % (name, r))
1497 1503 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1498 1504
1499 1505 def tags(ui, repo):
1500 1506 """list repository tags"""
1501 1507
1502 1508 l = repo.tagslist()
1503 1509 l.reverse()
1504 1510 for t, n in l:
1505 1511 try:
1506 1512 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
1507 1513 except KeyError:
1508 1514 r = " ?:?"
1509 1515 ui.write("%-30s %s\n" % (t, r))
1510 1516
1511 1517 def tip(ui, repo):
1512 1518 """show the tip revision"""
1513 1519 n = repo.changelog.tip()
1514 1520 show_changeset(ui, repo, changenode=n)
1515 1521
1516 1522 def undo(ui, repo):
1517 1523 """undo the last commit or pull
1518 1524
1519 1525 Roll back the last pull or commit transaction on the
1520 1526 repository, restoring the project to its earlier state.
1521 1527
1522 1528 This command should be used with care. There is only one level of
1523 1529 undo and there is no redo.
1524 1530
1525 1531 This command is not intended for use on public repositories. Once
1526 1532 a change is visible for pull by other users, undoing it locally is
1527 1533 ineffective.
1528 1534 """
1529 1535 repo.undo()
1530 1536
1531 1537 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1532 1538 '''update or merge working directory
1533 1539
1534 1540 If there are no outstanding changes in the working directory and
1535 1541 there is a linear relationship between the current version and the
1536 1542 requested version, the result is the requested version.
1537 1543
1538 1544 Otherwise the result is a merge between the contents of the
1539 1545 current working directory and the requested version. Files that
1540 1546 changed between either parent are marked as changed for the next
1541 1547 commit and a commit must be performed before any further updates
1542 1548 are allowed.
1543 1549 '''
1544 1550 if branch:
1545 1551 br = repo.branchlookup(branch=branch)
1546 1552 found = []
1547 1553 for x in br:
1548 1554 if branch in br[x]:
1549 1555 found.append(x)
1550 1556 if len(found) > 1:
1551 1557 ui.warn("Found multiple heads for %s\n" % branch)
1552 1558 for x in found:
1553 1559 show_changeset(ui, repo, changenode=x, brinfo=br)
1554 1560 return 1
1555 1561 if len(found) == 1:
1556 1562 node = found[0]
1557 1563 ui.warn("Using head %s for branch %s\n" % (short(node), branch))
1558 1564 else:
1559 1565 ui.warn("branch %s not found\n" % (branch))
1560 1566 return 1
1561 1567 else:
1562 1568 node = node and repo.lookup(node) or repo.changelog.tip()
1563 1569 return repo.update(node, allow=merge, force=clean)
1564 1570
1565 1571 def verify(ui, repo):
1566 1572 """verify the integrity of the repository"""
1567 1573 return repo.verify()
1568 1574
1569 1575 # Command options and aliases are listed here, alphabetically
1570 1576
1571 1577 table = {
1572 1578 "^add":
1573 1579 (add,
1574 1580 [('I', 'include', [], 'include path in search'),
1575 1581 ('X', 'exclude', [], 'exclude path from search')],
1576 1582 "hg add [OPTION]... [FILE]..."),
1577 1583 "addremove":
1578 1584 (addremove,
1579 1585 [('I', 'include', [], 'include path in search'),
1580 1586 ('X', 'exclude', [], 'exclude path from search')],
1581 1587 "hg addremove [OPTION]... [FILE]..."),
1582 1588 "^annotate":
1583 1589 (annotate,
1584 1590 [('r', 'rev', '', 'revision'),
1585 1591 ('a', 'text', None, 'treat all files as text'),
1586 1592 ('u', 'user', None, 'show user'),
1587 1593 ('n', 'number', None, 'show revision number'),
1588 1594 ('c', 'changeset', None, 'show changeset'),
1589 1595 ('I', 'include', [], 'include path in search'),
1590 1596 ('X', 'exclude', [], 'exclude path from search')],
1591 1597 'hg annotate [OPTION]... FILE...'),
1592 1598 "cat":
1593 1599 (cat,
1594 1600 [('o', 'output', "", 'output to file')],
1595 1601 'hg cat [-o OUTFILE] FILE [REV]'),
1596 1602 "^clone":
1597 1603 (clone,
1598 1604 [('U', 'noupdate', None, 'skip update after cloning'),
1599 1605 ('e', 'ssh', "", 'ssh command'),
1600 1606 ('', 'remotecmd', "", 'remote hg command')],
1601 1607 'hg clone [OPTION]... SOURCE [DEST]'),
1602 1608 "^commit|ci":
1603 1609 (commit,
1604 1610 [('A', 'addremove', None, 'run add/remove during commit'),
1605 1611 ('I', 'include', [], 'include path in search'),
1606 1612 ('X', 'exclude', [], 'exclude path from search'),
1607 1613 ('m', 'message', "", 'commit message'),
1608 1614 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1609 1615 ('l', 'logfile', "", 'commit message file'),
1610 1616 ('d', 'date', "", 'date code'),
1611 1617 ('u', 'user', "", 'user')],
1612 1618 'hg commit [OPTION]... [FILE]...'),
1613 1619 "copy": (copy, [], 'hg copy SOURCE DEST'),
1614 1620 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1615 1621 "debugconfig": (debugconfig, [], 'debugconfig'),
1616 1622 "debugstate": (debugstate, [], 'debugstate'),
1617 1623 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1618 1624 "debugindex": (debugindex, [], 'debugindex FILE'),
1619 1625 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1620 1626 "debugrename": (debugrename, [], 'debugrename FILE [REV]'),
1621 1627 "debugwalk":
1622 1628 (debugwalk,
1623 1629 [('I', 'include', [], 'include path in search'),
1624 1630 ('X', 'exclude', [], 'exclude path from search')],
1625 1631 'debugwalk [OPTION]... [FILE]...'),
1626 1632 "^diff":
1627 1633 (diff,
1628 1634 [('r', 'rev', [], 'revision'),
1629 1635 ('a', 'text', None, 'treat all files as text'),
1630 1636 ('I', 'include', [], 'include path in search'),
1631 1637 ('X', 'exclude', [], 'exclude path from search')],
1632 1638 'hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1633 1639 "^export":
1634 1640 (export,
1635 1641 [('o', 'output', "", 'output to file'),
1636 1642 ('a', 'text', None, 'treat all files as text')],
1637 1643 "hg export [-a] [-o OUTFILE] REV..."),
1638 1644 "forget":
1639 1645 (forget,
1640 1646 [('I', 'include', [], 'include path in search'),
1641 1647 ('X', 'exclude', [], 'exclude path from search')],
1642 1648 "hg forget [OPTION]... FILE..."),
1643 1649 "grep":
1644 1650 (grep,
1645 1651 [('0', 'print0', None, 'end fields with NUL'),
1646 1652 ('I', 'include', [], 'include path in search'),
1647 1653 ('X', 'exclude', [], 'include path in search'),
1648 1654 ('e', 'every-match', None, 'print every rev with matches'),
1649 1655 ('i', 'ignore-case', None, 'ignore case when matching'),
1650 1656 ('l', 'files-with-matches', None, 'print names of files and revs with matches'),
1651 1657 ('n', 'line-number', None, 'print line numbers'),
1652 1658 ('r', 'rev', [], 'search in revision rev'),
1653 1659 ('u', 'user', None, 'print user who made change')],
1654 1660 "hg grep [OPTION]... PATTERN [FILE]..."),
1655 1661 "heads":
1656 1662 (heads,
1657 1663 [('b', 'branches', None, 'find branch info')],
1658 1664 'hg heads [-b]'),
1659 1665 "help": (help_, [], 'hg help [COMMAND]'),
1660 1666 "identify|id": (identify, [], 'hg identify'),
1661 1667 "import|patch":
1662 1668 (import_,
1663 1669 [('p', 'strip', 1, 'path strip'),
1664 1670 ('f', 'force', None, 'skip check for outstanding changes'),
1665 ('b', 'base', "", 'base path')],
1671 ('b', 'base', "", 'base path'),
1672 ('m', 'mail-like', None, 'apply a patch that looks like email')],
1666 1673 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
1667 1674 "incoming|in": (incoming,
1668 1675 [('p', 'patch', None, 'show patch')],
1669 1676 'hg incoming [-p] [SOURCE]'),
1670 1677 "^init": (init, [], 'hg init [DEST]'),
1671 1678 "locate":
1672 1679 (locate,
1673 1680 [('r', 'rev', '', 'revision'),
1674 1681 ('0', 'print0', None, 'end filenames with NUL'),
1675 1682 ('f', 'fullpath', None, 'print complete paths'),
1676 1683 ('I', 'include', [], 'include path in search'),
1677 1684 ('X', 'exclude', [], 'exclude path from search')],
1678 1685 'hg locate [OPTION]... [PATTERN]...'),
1679 1686 "^log|history":
1680 1687 (log,
1681 1688 [('I', 'include', [], 'include path in search'),
1682 1689 ('X', 'exclude', [], 'exclude path from search'),
1683 1690 ('r', 'rev', [], 'revision'),
1684 1691 ('p', 'patch', None, 'show patch')],
1685 1692 'hg log [-I] [-X] [-r REV]... [-p] [FILE]'),
1686 1693 "manifest": (manifest, [], 'hg manifest [REV]'),
1687 1694 "outgoing|out": (outgoing,
1688 1695 [('p', 'patch', None, 'show patch')],
1689 1696 'hg outgoing [-p] [DEST]'),
1690 1697 "parents": (parents, [], 'hg parents [REV]'),
1691 1698 "paths": (paths, [], 'hg paths [NAME]'),
1692 1699 "^pull":
1693 1700 (pull,
1694 1701 [('u', 'update', None, 'update working directory'),
1695 1702 ('e', 'ssh', "", 'ssh command'),
1696 1703 ('', 'remotecmd', "", 'remote hg command')],
1697 1704 'hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]'),
1698 1705 "^push":
1699 1706 (push,
1700 1707 [('f', 'force', None, 'force push'),
1701 1708 ('e', 'ssh', "", 'ssh command'),
1702 1709 ('', 'remotecmd', "", 'remote hg command')],
1703 1710 'hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]'),
1704 1711 "rawcommit":
1705 1712 (rawcommit,
1706 1713 [('p', 'parent', [], 'parent'),
1707 1714 ('d', 'date', "", 'date code'),
1708 1715 ('u', 'user', "", 'user'),
1709 1716 ('F', 'files', "", 'file list'),
1710 1717 ('m', 'message', "", 'commit message'),
1711 1718 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1712 1719 ('l', 'logfile', "", 'commit message file')],
1713 1720 'hg rawcommit [OPTION]... [FILE]...'),
1714 1721 "recover": (recover, [], "hg recover"),
1715 1722 "^remove|rm": (remove,
1716 1723 [('I', 'include', [], 'include path in search'),
1717 1724 ('X', 'exclude', [], 'exclude path from search')],
1718 1725 "hg remove [OPTION]... FILE..."),
1719 1726 "^revert":
1720 1727 (revert,
1721 1728 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1722 1729 ("r", "rev", "", "revision")],
1723 1730 "hg revert [-n] [-r REV] [NAME]..."),
1724 1731 "root": (root, [], "hg root"),
1725 1732 "^serve":
1726 1733 (serve,
1727 1734 [('A', 'accesslog', '', 'access log file'),
1728 1735 ('E', 'errorlog', '', 'error log file'),
1729 1736 ('p', 'port', 0, 'listen port'),
1730 1737 ('a', 'address', '', 'interface address'),
1731 1738 ('n', 'name', "", 'repository name'),
1732 1739 ('', 'stdio', None, 'for remote clients'),
1733 1740 ('t', 'templates', "", 'template directory'),
1734 1741 ('', 'style', "", 'template style'),
1735 1742 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1736 1743 "hg serve [OPTION]..."),
1737 1744 "^status":
1738 1745 (status,
1739 1746 [('m', 'modified', None, 'show only modified files'),
1740 1747 ('a', 'added', None, 'show only added files'),
1741 1748 ('r', 'removed', None, 'show only removed files'),
1742 1749 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1743 1750 ('n', 'no-status', None, 'hide status prefix'),
1744 1751 ('0', 'print0', None, 'end filenames with NUL'),
1745 1752 ('I', 'include', [], 'include path in search'),
1746 1753 ('X', 'exclude', [], 'exclude path from search')],
1747 1754 "hg status [OPTION]... [FILE]..."),
1748 1755 "tag":
1749 1756 (tag,
1750 1757 [('l', 'local', None, 'make the tag local'),
1751 1758 ('m', 'message', "", 'commit message'),
1752 1759 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1753 1760 ('d', 'date', "", 'date code'),
1754 1761 ('u', 'user', "", 'user')],
1755 1762 'hg tag [OPTION]... NAME [REV]'),
1756 1763 "tags": (tags, [], 'hg tags'),
1757 1764 "tip": (tip, [], 'hg tip'),
1758 1765 "undo": (undo, [], 'hg undo'),
1759 1766 "^update|up|checkout|co":
1760 1767 (update,
1761 1768 [('b', 'branch', "", 'checkout the head of a specific branch'),
1762 1769 ('m', 'merge', None, 'allow merging of conflicts'),
1763 1770 ('C', 'clean', None, 'overwrite locally modified files')],
1764 1771 'hg update [-b TAG] [-m] [-C] [REV]'),
1765 1772 "verify": (verify, [], 'hg verify'),
1766 1773 "version": (show_version, [], 'hg version'),
1767 1774 }
1768 1775
1769 1776 globalopts = [
1770 1777 ('R', 'repository', "", 'repository root directory'),
1771 1778 ('', 'cwd', '', 'change working directory'),
1772 1779 ('y', 'noninteractive', None, 'run non-interactively'),
1773 1780 ('q', 'quiet', None, 'quiet mode'),
1774 1781 ('v', 'verbose', None, 'verbose mode'),
1775 1782 ('', 'debug', None, 'debug mode'),
1776 1783 ('', 'traceback', None, 'print traceback on exception'),
1777 1784 ('', 'time', None, 'time how long the command takes'),
1778 1785 ('', 'profile', None, 'profile'),
1779 1786 ('', 'version', None, 'output version information and exit'),
1780 1787 ('h', 'help', None, 'display help and exit'),
1781 1788 ]
1782 1789
1783 1790 norepo = ("clone init version help debugconfig debugdata"
1784 1791 " debugindex debugindexdot paths")
1785 1792
1786 1793 def find(cmd):
1787 1794 for e in table.keys():
1788 1795 if re.match("(%s)$" % e, cmd):
1789 1796 return e, table[e]
1790 1797
1791 1798 raise UnknownCommand(cmd)
1792 1799
1793 1800 class SignalInterrupt(Exception):
1794 1801 """Exception raised on SIGTERM and SIGHUP."""
1795 1802
1796 1803 def catchterm(*args):
1797 1804 raise SignalInterrupt
1798 1805
1799 1806 def run():
1800 1807 sys.exit(dispatch(sys.argv[1:]))
1801 1808
1802 1809 class ParseError(Exception):
1803 1810 """Exception raised on errors in parsing the command line."""
1804 1811
1805 1812 def parse(args):
1806 1813 options = {}
1807 1814 cmdoptions = {}
1808 1815
1809 1816 try:
1810 1817 args = fancyopts.fancyopts(args, globalopts, options)
1811 1818 except fancyopts.getopt.GetoptError, inst:
1812 1819 raise ParseError(None, inst)
1813 1820
1814 1821 if args:
1815 1822 cmd, args = args[0], args[1:]
1816 1823 i = find(cmd)[1]
1817 1824 c = list(i[1])
1818 1825 else:
1819 1826 cmd = None
1820 1827 c = []
1821 1828
1822 1829 # combine global options into local
1823 1830 for o in globalopts:
1824 1831 c.append((o[0], o[1], options[o[1]], o[3]))
1825 1832
1826 1833 try:
1827 1834 args = fancyopts.fancyopts(args, c, cmdoptions)
1828 1835 except fancyopts.getopt.GetoptError, inst:
1829 1836 raise ParseError(cmd, inst)
1830 1837
1831 1838 # separate global options back out
1832 1839 for o in globalopts:
1833 1840 n = o[1]
1834 1841 options[n] = cmdoptions[n]
1835 1842 del cmdoptions[n]
1836 1843
1837 1844 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
1838 1845
1839 1846 def dispatch(args):
1840 1847 signal.signal(signal.SIGTERM, catchterm)
1841 1848 try:
1842 1849 signal.signal(signal.SIGHUP, catchterm)
1843 1850 except AttributeError:
1844 1851 pass
1845 1852
1846 1853 u = ui.ui()
1847 1854 external = []
1848 1855 for x in u.extensions():
1849 1856 if x[1]:
1850 1857 mod = imp.load_source(x[0], x[1])
1851 1858 else:
1852 1859 def importh(name):
1853 1860 mod = __import__(name)
1854 1861 components = name.split('.')
1855 1862 for comp in components[1:]:
1856 1863 mod = getattr(mod, comp)
1857 1864 return mod
1858 1865 mod = importh(x[0])
1859 1866 external.append(mod)
1860 1867 for x in external:
1861 1868 for t in x.cmdtable:
1862 1869 if t in table:
1863 1870 u.warn("module %s override %s\n" % (x.__name__, t))
1864 1871 table.update(x.cmdtable)
1865 1872
1866 1873 try:
1867 1874 cmd, func, args, options, cmdoptions = parse(args)
1868 1875 except ParseError, inst:
1869 1876 if inst.args[0]:
1870 1877 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1871 1878 help_(u, inst.args[0])
1872 1879 else:
1873 1880 u.warn("hg: %s\n" % inst.args[1])
1874 1881 help_(u, 'shortlist')
1875 1882 sys.exit(-1)
1876 1883 except UnknownCommand, inst:
1877 1884 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1878 1885 help_(u, 'shortlist')
1879 1886 sys.exit(1)
1880 1887
1881 1888 if options["time"]:
1882 1889 def get_times():
1883 1890 t = os.times()
1884 1891 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1885 1892 t = (t[0], t[1], t[2], t[3], time.clock())
1886 1893 return t
1887 1894 s = get_times()
1888 1895 def print_time():
1889 1896 t = get_times()
1890 1897 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1891 1898 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1892 1899 atexit.register(print_time)
1893 1900
1894 1901 u.updateopts(options["verbose"], options["debug"], options["quiet"],
1895 1902 not options["noninteractive"])
1896 1903
1897 1904 try:
1898 1905 try:
1899 1906 if options['help']:
1900 1907 help_(u, cmd, options['version'])
1901 1908 sys.exit(0)
1902 1909 elif options['version']:
1903 1910 show_version(u)
1904 1911 sys.exit(0)
1905 1912 elif not cmd:
1906 1913 help_(u, 'shortlist')
1907 1914 sys.exit(0)
1908 1915
1909 1916 if options['cwd']:
1910 1917 try:
1911 1918 os.chdir(options['cwd'])
1912 1919 except OSError, inst:
1913 1920 u.warn('abort: %s: %s\n' % (options['cwd'], inst.strerror))
1914 1921 sys.exit(1)
1915 1922
1916 1923 if cmd not in norepo.split():
1917 1924 path = options["repository"] or ""
1918 1925 repo = hg.repository(ui=u, path=path)
1919 1926 for x in external:
1920 1927 x.reposetup(u, repo)
1921 1928 d = lambda: func(u, repo, *args, **cmdoptions)
1922 1929 else:
1923 1930 d = lambda: func(u, *args, **cmdoptions)
1924 1931
1925 1932 if options['profile']:
1926 1933 import hotshot, hotshot.stats
1927 1934 prof = hotshot.Profile("hg.prof")
1928 1935 r = prof.runcall(d)
1929 1936 prof.close()
1930 1937 stats = hotshot.stats.load("hg.prof")
1931 1938 stats.strip_dirs()
1932 1939 stats.sort_stats('time', 'calls')
1933 1940 stats.print_stats(40)
1934 1941 return r
1935 1942 else:
1936 1943 return d()
1937 1944 except:
1938 1945 if options['traceback']:
1939 1946 traceback.print_exc()
1940 1947 raise
1941 1948 except hg.RepoError, inst:
1942 1949 u.warn("abort: ", inst, "!\n")
1943 1950 except SignalInterrupt:
1944 1951 u.warn("killed!\n")
1945 1952 except KeyboardInterrupt:
1946 1953 try:
1947 1954 u.warn("interrupted!\n")
1948 1955 except IOError, inst:
1949 1956 if inst.errno == errno.EPIPE:
1950 1957 if u.debugflag:
1951 1958 u.warn("\nbroken pipe\n")
1952 1959 else:
1953 1960 raise
1954 1961 except IOError, inst:
1955 1962 if hasattr(inst, "code"):
1956 1963 u.warn("abort: %s\n" % inst)
1957 1964 elif hasattr(inst, "reason"):
1958 1965 u.warn("abort: error: %s\n" % inst.reason[1])
1959 1966 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1960 1967 if u.debugflag:
1961 1968 u.warn("broken pipe\n")
1962 1969 else:
1963 1970 raise
1964 1971 except OSError, inst:
1965 1972 if hasattr(inst, "filename"):
1966 1973 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1967 1974 else:
1968 1975 u.warn("abort: %s\n" % inst.strerror)
1969 1976 except util.Abort, inst:
1970 1977 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1971 1978 sys.exit(1)
1972 1979 except TypeError, inst:
1973 1980 # was this an argument error?
1974 1981 tb = traceback.extract_tb(sys.exc_info()[2])
1975 1982 if len(tb) > 2: # no
1976 1983 raise
1977 1984 u.debug(inst, "\n")
1978 1985 u.warn("%s: invalid arguments\n" % cmd)
1979 1986 help_(u, cmd)
1980 1987 except UnknownCommand, inst:
1981 1988 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1982 1989 help_(u, 'shortlist')
1983 1990
1984 1991 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now