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