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