##// END OF EJS Templates
merge with crew.
Vadim Gelfer -
r1732:d3e6da33 merge default
parent child Browse files
Show More
@@ -1,822 +1,827
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 -d] [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 -d, --date list the commit date
107 107 -c, --changeset list the changeset
108 108 -n, --number list the revision number (default)
109 109
110 110 bundle <file> <other>::
111 111 (EXPERIMENTAL)
112 112
113 113 Generate a compressed changegroup file collecting all changesets
114 114 not found in the other repository.
115 115
116 116 This file can then be transferred using conventional means and
117 117 applied to another repository with the unbundle command. This is
118 118 useful when native push and pull are not available or when
119 119 exporting an entire repository is undesirable. The standard file
120 120 extension is ".hg".
121 121
122 122 Unlike import/export, this exactly preserves all changeset
123 123 contents including permissions, rename data, and revision history.
124 124
125 125 cat [options] <file ...>::
126 126 Print the specified files as they were at the given revision.
127 127 If no revision is given then the tip is used.
128 128
129 129 Output may be to a file, in which case the name of the file is
130 130 given using a format string. The formatting rules are the same as
131 131 for the export command, with the following additions:
132 132
133 133 %s basename of file being printed
134 134 %d dirname of file being printed, or '.' if in repo root
135 135 %p root-relative path name of file being printed
136 136
137 137 options:
138 138 -I, --include <pat> include names matching the given patterns
139 139 -X, --exclude <pat> exclude names matching the given patterns
140 140 -o, --output <filespec> print output to file with formatted name
141 141 -r, --rev <rev> print the given revision
142 142
143 143 clone [options] <source> [dest]::
144 144 Create a copy of an existing repository in a new directory.
145 145
146 146 If no destination directory name is specified, it defaults to the
147 147 basename of the source.
148 148
149 149 The location of the source is added to the new repository's
150 150 .hg/hgrc file, as the default to be used for future pulls.
151 151
152 152 For efficiency, hardlinks are used for cloning whenever the source
153 153 and destination are on the same filesystem. Some filesystems,
154 154 such as AFS, implement hardlinking incorrectly, but do not report
155 155 errors. In these cases, use the --pull option to avoid
156 156 hardlinking.
157 157
158 158 See pull for valid source format details.
159 159
160 160 options:
161 161 -U, --noupdate do not update the new working directory
162 162 --pull use pull protocol to copy metadata
163 163 -e, --ssh specify ssh command to use
164 164 --remotecmd specify hg command to run on the remote side
165 165
166 166 commit [options] [files...]::
167 167 Commit changes to the given files into the repository.
168 168
169 169 If a list of files is omitted, all changes reported by "hg status"
170 170 from the root of the repository will be commited.
171 171
172 172 The HGEDITOR or EDITOR environment variables are used to start an
173 173 editor to add a commit comment.
174 174
175 175 Options:
176 176
177 177 -A, --addremove run addremove during commit
178 178 -I, --include <pat> include names matching the given patterns
179 179 -X, --exclude <pat> exclude names matching the given patterns
180 180 -m, --message <text> use <text> as commit message
181 181 -l, --logfile <file> read the commit message from <file>
182 182 -d, --date <datecode> record datecode as commit date
183 183 -u, --user <user> record user as commiter
184 184
185 185 aliases: ci
186 186
187 187 copy <source ...> <dest>::
188 188 Mark dest as having copies of source files. If dest is a
189 189 directory, copies are put in that directory. If dest is a file,
190 190 there can only be one source.
191 191
192 192 By default, this command copies the contents of files as they
193 193 stand in the working directory. If invoked with --after, the
194 194 operation is recorded, but no copying is performed.
195 195
196 196 This command takes effect in the next commit.
197 197
198 198 NOTE: This command should be treated as experimental. While it
199 199 should properly record copied files, this information is not yet
200 200 fully used by merge, nor fully reported by log.
201 201
202 202 Options:
203 203 -A, --after record a copy that has already occurred
204 204 -I, --include <pat> include names matching the given patterns
205 205 -X, --exclude <pat> exclude names matching the given patterns
206 206 -f, --force forcibly copy over an existing managed file
207 207
208 208 aliases: cp
209 209
210 210 diff [-a] [-r revision] [-r revision] [files ...]::
211 211 Show differences between revisions for the specified files.
212 212
213 213 Differences between files are shown using the unified diff format.
214 214
215 215 When two revision arguments are given, then changes are shown
216 216 between those revisions. If only one revision is specified then
217 217 that revision is compared to the working directory, and, when no
218 218 revisions are specified, the working directory files are compared
219 219 to its parent.
220 220
221 221 Without the -a option, diff will avoid generating diffs of files
222 222 it detects as binary. With -a, diff will generate a diff anyway,
223 223 probably with undesirable results.
224 224
225 225 options:
226 -a, --text treat all files as text
227 -I, --include <pat> include names matching the given patterns
228 -X, --exclude <pat> exclude names matching the given patterns
226 -a, --text treat all files as text
227 -I, --include <pat> include names matching the given patterns
228 -p, --show-function show which function each change is in
229 -X, --exclude <pat> exclude names matching the given patterns
230 -w, --ignore-all-space ignore white space when comparing lines
229 231
230 232 export [-o filespec] [revision] ...::
231 233 Print the changeset header and diffs for one or more revisions.
232 234
233 235 The information shown in the changeset header is: author,
234 236 changeset hash, parent and commit comment.
235 237
236 238 Output may be to a file, in which case the name of the file is
237 239 given using a format string. The formatting rules are as follows:
238 240
239 241 %% literal "%" character
240 242 %H changeset hash (40 bytes of hexadecimal)
241 243 %N number of patches being generated
242 244 %R changeset revision number
243 245 %b basename of the exporting repository
244 246 %h short-form changeset hash (12 bytes of hexadecimal)
245 247 %n zero-padded sequence number, starting at 1
246 248 %r zero-padded changeset revision number
247 249
248 250 Without the -a option, export will avoid generating diffs of files
249 251 it detects as binary. With -a, export will generate a diff anyway,
250 252 probably with undesirable results.
251 253
252 254 options:
253 255 -a, --text treat all files as text
254 256 -o, --output <filespec> print output to file with formatted name
255 257
256 258 forget [options] [files]::
257 259 Undo an 'hg add' scheduled for the next commit.
258 260
259 261 options:
260 262 -I, --include <pat> include names matching the given patterns
261 263 -X, --exclude <pat> exclude names matching the given patterns
262 264
263 265 grep [options] pattern [files]::
264 266 Search revisions of files for a regular expression.
265 267
266 268 This command behaves differently than Unix grep. It only accepts
267 269 Python/Perl regexps. It searches repository history, not the
268 270 working directory. It always prints the revision number in which
269 271 a match appears.
270 272
271 273 By default, grep only prints output for the first revision of a
272 274 file in which it finds a match. To get it to print every revision
273 275 that contains a change in match status ("-" for a match that
274 276 becomes a non-match, or "+" for a non-match that becomes a match),
275 277 use the --all flag.
276 278
277 279 options:
278 280 -0, --print0 end fields with NUL
279 281 -I, --include <pat> include names matching the given patterns
280 282 -X, --exclude <pat> exclude names matching the given patterns
281 283 --all print all revisions that match
282 284 -i, --ignore-case ignore case when matching
283 285 -l, --files-with-matches print only filenames and revs that match
284 286 -n, --line-number print matching line numbers
285 287 -r <rev>, --rev <rev> search in given revision range
286 288 -u, --user print user who committed change
287 289
288 290 heads::
289 291 Show all repository head changesets.
290 292
291 293 Repository "heads" are changesets that don't have children
292 294 changesets. They are where development generally takes place and
293 295 are the usual targets for update and merge operations.
294 296
295 297 identify::
296 298 Print a short summary of the current state of the repo.
297 299
298 300 This summary identifies the repository state using one or two parent
299 301 hash identifiers, followed by a "+" if there are uncommitted changes
300 302 in the working directory, followed by a list of tags for this revision.
301 303
302 304 aliases: id
303 305
304 306 import [-p <n> -b <base> -f] <patches>::
305 307 Import a list of patches and commit them individually.
306 308
307 309 If there are outstanding changes in the working directory, import
308 310 will abort unless given the -f flag.
309 311
310 312 If a patch looks like a mail message (its first line starts with
311 313 "From " or looks like an RFC822 header), it will not be applied
312 314 unless the -f option is used. The importer neither parses nor
313 315 discards mail headers, so use -f only to override the "mailness"
314 316 safety check, not to import a real mail message.
315 317
316 318 options:
317 319 -p, --strip <n> directory strip option for patch. This has the same
318 320 meaning as the corresponding patch option
319 321 -b <path> base directory to read patches from
320 322 -f, --force skip check for outstanding uncommitted changes
321 323
322 324 aliases: patch
323 325
324 326 incoming [-p] [source]::
325 327 Show new changesets found in the specified repo or the default
326 328 pull repo. These are the changesets that would be pulled if a pull
327 329 was requested.
328 330
329 331 Currently only local repositories are supported.
330 332
331 333 options:
332 334 -p, --patch show patch
333 335
334 336 aliases: in
335 337
336 338 init [dest]::
337 339 Initialize a new repository in the given directory. If the given
338 340 directory does not exist, it is created.
339 341
340 342 If no directory is given, the current directory is used.
341 343
342 344 locate [options] [files]::
343 345 Print all files under Mercurial control whose names match the
344 346 given patterns.
345 347
346 348 This command searches the current directory and its
347 349 subdirectories. To search an entire repository, move to the root
348 350 of the repository.
349 351
350 352 If no patterns are given to match, this command prints all file
351 353 names.
352 354
353 355 If you want to feed the output of this command into the "xargs"
354 356 command, use the "-0" option to both this command and "xargs".
355 357 This will avoid the problem of "xargs" treating single filenames
356 358 that contain white space as multiple filenames.
357 359
358 360 options:
359 361
360 362 -0, --print0 end filenames with NUL, for use with xargs
361 363 -f, --fullpath print complete paths from the filesystem root
362 364 -I, --include <pat> include names matching the given patterns
363 365 -r, --rev <rev> search the repository as it stood at rev
364 366 -X, --exclude <pat> exclude names matching the given patterns
365 367
366 368 log [-r revision ...] [-p] [files]::
367 369 Print the revision history of the specified files or the entire project.
368 370
369 371 By default this command outputs: changeset id and hash, tags,
370 372 parents, user, date and time, and a summary for each commit. The
371 373 -v switch adds some more detail, such as changed files, manifest
372 374 hashes or message signatures.
373 375
374 376 options:
375 377 -I, --include <pat> include names matching the given patterns
376 378 -X, --exclude <pat> exclude names matching the given patterns
377 379 -r, --rev <A> show the specified revision or range
378 380 -p, --patch show patch
379 381
380 382 aliases: history
381 383
382 384 manifest [revision]::
383 385 Print a list of version controlled files for the given revision.
384 386
385 387 The manifest is the list of files being version controlled. If no revision
386 388 is given then the tip is used.
387 389
388 390 outgoing [-p] [dest]::
389 391 Show changesets not found in the specified destination repo or the
390 392 default push repo. These are the changesets that would be pushed
391 393 if a push was requested.
392 394
393 395 See pull for valid source format details.
394 396
395 397 options:
396 398 -p, --patch show patch
397 399
398 400 aliases: out
399 401
400 402 parents::
401 403 Print the working directory's parent revisions.
402 404
403 405 paths [NAME]::
404 406 Show definition of symbolic path name NAME. If no name is given, show
405 407 definition of available names.
406 408
407 409 Path names are defined in the [paths] section of /etc/mercurial/hgrc
408 410 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
409 411
410 412 pull <repository path>::
411 413 Pull changes from a remote repository to a local one.
412 414
413 415 This finds all changes from the repository at the specified path
414 416 or URL and adds them to the local repository. By default, this
415 417 does not update the copy of the project in the working directory.
416 418
417 419 Valid URLs are of the form:
418 420
419 421 local/filesystem/path
420 422 http://[user@]host[:port][/path]
421 423 https://[user@]host[:port][/path]
422 424 ssh://[user@]host[:port][/path]
423 425
424 426 SSH requires an accessible shell account on the destination machine
425 427 and a copy of hg in the remote path. With SSH, paths are relative
426 428 to the remote user's home directory by default; use two slashes at
427 429 the start of a path to specify it as relative to the filesystem root.
428 430
429 431 options:
430 432 -u, --update update the working directory to tip after pull
431 433 -e, --ssh specify ssh command to use
432 434 --remotecmd specify hg command to run on the remote side
433 435
434 436 push <destination>::
435 437 Push changes from the local repository to the given destination.
436 438
437 439 This is the symmetrical operation for pull. It helps to move
438 440 changes from the current repository to a different one. If the
439 441 destination is local this is identical to a pull in that directory
440 442 from the current one.
441 443
442 444 By default, push will refuse to run if it detects the result would
443 445 increase the number of remote heads. This generally indicates the
444 446 the client has forgotten to sync and merge before pushing.
445 447
446 448 Valid URLs are of the form:
447 449
448 450 local/filesystem/path
449 451 ssh://[user@]host[:port][/path]
450 452
451 453 SSH requires an accessible shell account on the destination
452 454 machine and a copy of hg in the remote path.
453 455
454 456 options:
455 457
456 458 -f, --force force update
457 459 -e, --ssh specify ssh command to use
458 460 --remotecmd specify hg command to run on the remote side
459 461
460 462 rawcommit [-p -d -u -F -m -l]::
461 463 Lowlevel commit, for use in helper scripts. (DEPRECATED)
462 464
463 465 This command is not intended to be used by normal users, as it is
464 466 primarily useful for importing from other SCMs.
465 467
466 468 This command is now deprecated and will be removed in a future
467 469 release, please use debugsetparents and commit instead.
468 470
469 471 recover::
470 472 Recover from an interrupted commit or pull.
471 473
472 474 This command tries to fix the repository status after an interrupted
473 475 operation. It should only be necessary when Mercurial suggests it.
474 476
475 477 remove [options] [files ...]::
476 478 Schedule the indicated files for removal from the repository.
477 479
478 480 This command schedules the files to be removed at the next commit.
479 481 This only removes files from the current branch, not from the
480 482 entire project history. If the files still exist in the working
481 483 directory, they will be deleted from it.
482 484
483 485 aliases: rm
484 486
485 487 rename <source ...> <dest>::
486 488 Mark dest as copies of sources; mark sources for deletion. If
487 489 dest is a directory, copies are put in that directory. If dest is
488 490 a file, there can only be one source.
489 491
490 492 By default, this command copies the contents of files as they
491 493 stand in the working directory. If invoked with --after, the
492 494 operation is recorded, but no copying is performed.
493 495
494 496 This command takes effect in the next commit.
495 497
496 498 NOTE: This command should be treated as experimental. While it
497 499 should properly record rename files, this information is not yet
498 500 fully used by merge, nor fully reported by log.
499 501
500 502 Options:
501 503 -A, --after record a rename that has already occurred
502 504 -f, --force forcibly copy over an existing managed file
503 505
504 506 aliases: mv
505 507
506 508 revert [names ...]::
507 509 The revert command has two modes of operation.
508 510
509 511 In its default mode, it reverts any uncommitted modifications made
510 512 to the named files or directories. This restores the contents of
511 513 the affected files to an unmodified state.
512 514
513 515 Using the -r option, it reverts the given files or directories to
514 516 their state as of an earlier revision. This can be helpful to "roll
515 517 back" some or all of a change that should not have been committed.
516 518
517 519 Revert modifies the working directory. It does not commit any
518 520 changes, or change the parent of the current working directory.
519 521
520 522 If a file has been deleted, it is recreated. If the executable
521 523 mode of a file was changed, it is reset.
522 524
523 525 If a directory is given, all files in that directory and its
524 526 subdirectories are reverted.
525 527
526 528 If no arguments are given, all files in the current directory and
527 529 its subdirectories are reverted.
528 530
529 531 options:
530 532 -r, --rev <rev> revision to revert to
531 533 -n, --nonrecursive do not recurse into subdirectories
532 534
533 535 root::
534 536 Print the root directory of the current repository.
535 537
536 538 serve [options]::
537 539 Start a local HTTP repository browser and pull server.
538 540
539 541 By default, the server logs accesses to stdout and errors to
540 542 stderr. Use the "-A" and "-E" options to log to files.
541 543
542 544 options:
543 545 -A, --accesslog <file> name of access log file to write to
544 546 -E, --errorlog <file> name of error log file to write to
545 547 -a, --address <addr> address to use
546 548 -p, --port <n> port to use (default: 8000)
547 549 -n, --name <name> name to show in web pages (default: working dir)
548 550 -t, --templatedir <path> web templates to use
549 551 -6, --ipv6 use IPv6 in addition to IPv4
550 552
551 553 status [options] [files]::
552 554 Show changed files in the working directory. If no names are
553 555 given, all files are shown. Otherwise, only files matching the
554 556 given names are shown.
555 557
556 558 The codes used to show the status of files are:
557 559
558 560 M = changed
559 561 A = added
560 562 R = removed
561 563 ? = not tracked
562 564
563 565 options:
564 566
565 567 -m, --modified show only modified files
566 568 -a, --added show only added files
567 569 -r, --removed show only removed files
568 570 -u, --unknown show only unknown (not tracked) files
569 571 -n, --no-status hide status prefix
570 572 -0, --print0 end filenames with NUL, for use with xargs
571 573 -I, --include <pat> include names matching the given patterns
572 574 -X, --exclude <pat> exclude names matching the given patterns
573 575
574 576 tag [-l -m <text> -d <datecode> -u <user>] <name> [revision]::
575 577 Name a particular revision using <name>.
576 578
577 579 Tags are used to name particular revisions of the repository and are
578 580 very useful to compare different revision, to go back to significant
579 581 earlier versions or to mark branch points as releases, etc.
580 582
581 583 If no revision is given, the tip is used.
582 584
583 585 To facilitate version control, distribution, and merging of tags,
584 586 they are stored as a file named ".hgtags" which is managed
585 587 similarly to other project files and can be hand-edited if
586 588 necessary.
587 589
588 590 options:
589 591 -l, --local make the tag local
590 592 -m, --message <text> message for tag commit log entry
591 593 -d, --date <datecode> datecode for commit
592 594 -u, --user <user> user for commit
593 595
594 596 Note: Local tags are not version-controlled or distributed and are
595 597 stored in the .hg/localtags file. If there exists a local tag and
596 598 a public tag with the same name, local tag is used.
597 599
598 600 tags::
599 601 List the repository tags.
600 602
601 603 This lists both regular and local tags.
602 604
603 tip::
605 tip [-p]::
604 606 Show the tip revision.
605 607
608 options:
609 -p, --patch show patch
610
606 611 unbundle <file>::
607 612 (EXPERIMENTAL)
608 613
609 614 Apply a compressed changegroup file generated by the bundle
610 615 command.
611 616
612 617 undo::
613 618 Undo the last commit or pull transaction.
614 619
615 620 Roll back the last pull or commit transaction on the
616 621 repository, restoring the project to its earlier state.
617 622
618 623 This command should be used with care. There is only one level of
619 624 undo and there is no redo.
620 625
621 626 This command is not intended for use on public repositories. Once
622 627 a change is visible for pull by other users, undoing it locally is
623 628 ineffective.
624 629
625 630 update [-m -C] [revision]::
626 631 Update the working directory to the specified revision.
627 632
628 633 By default, update will refuse to run if doing so would require
629 634 merging or discarding local changes.
630 635
631 636 With the -m option, a merge will be performed.
632 637
633 638 With the -C option, local changes will be lost.
634 639
635 640 options:
636 641 -m, --merge allow merging of branches
637 642 -C, --clean overwrite locally modified files
638 643
639 644 aliases: up checkout co
640 645
641 646 verify::
642 647 Verify the integrity of the current repository.
643 648
644 649 This will perform an extensive check of the repository's
645 650 integrity, validating the hashes and checksums of each entry in
646 651 the changelog, manifest, and tracked files, as well as the
647 652 integrity of their crosslinks and indices.
648 653
649 654 FILE NAME PATTERNS
650 655 ------------------
651 656
652 657 Mercurial accepts several notations for identifying one or more
653 658 files at a time.
654 659
655 660 By default, Mercurial treats filenames as shell-style extended
656 661 glob patterns.
657 662
658 663 Alternate pattern notations must be specified explicitly.
659 664
660 665 To use a plain path name without any pattern matching, start a
661 666 name with "path:". These path names must match completely, from
662 667 the root of the current repository.
663 668
664 669 To use an extended glob, start a name with "glob:". Globs are
665 670 rooted at the current directory; a glob such as "*.c" will match
666 671 files ending in ".c" in the current directory only.
667 672
668 673 The supported glob syntax extensions are "**" to match any string
669 674 across path separators, and "{a,b}" to mean "a or b".
670 675
671 676 To use a Perl/Python regular expression, start a name with "re:".
672 677 Regexp pattern matching is anchored at the root of the repository.
673 678
674 679 Plain examples:
675 680
676 681 path:foo/bar a name bar in a directory named foo in the root of
677 682 the repository
678 683 path:path:name a file or directory named "path:name"
679 684
680 685 Glob examples:
681 686
682 687 glob:*.c any name ending in ".c" in the current directory
683 688 *.c any name ending in ".c" in the current directory
684 689 **.c any name ending in ".c" in the current directory, or
685 690 any subdirectory
686 691 foo/*.c any name ending in ".c" in the directory foo
687 692 foo/**.c any name ending in ".c" in the directory foo, or any
688 693 subdirectory
689 694
690 695 Regexp examples:
691 696
692 697 re:.*\.c$ any name ending in ".c", anywhere in the repository
693 698
694 699
695 700 SPECIFYING SINGLE REVISIONS
696 701 ---------------------------
697 702
698 703 Mercurial accepts several notations for identifying individual
699 704 revisions.
700 705
701 706 A plain integer is treated as a revision number. Negative
702 707 integers are treated as offsets from the tip, with -1 denoting the
703 708 tip.
704 709
705 710 A 40-digit hexadecimal string is treated as a unique revision
706 711 identifier.
707 712
708 713 A hexadecimal string less than 40 characters long is treated as a
709 714 unique revision identifier, and referred to as a short-form
710 715 identifier. A short-form identifier is only valid if it is the
711 716 prefix of one full-length identifier.
712 717
713 718 Any other string is treated as a tag name, which is a symbolic
714 719 name associated with a revision identifier. Tag names may not
715 720 contain the ":" character.
716 721
717 722 The reserved name "tip" is a special tag that always identifies
718 723 the most recent revision.
719 724
720 725 SPECIFYING MULTIPLE REVISIONS
721 726 -----------------------------
722 727
723 728 When Mercurial accepts more than one revision, they may be
724 729 specified individually, or provided as a continuous range,
725 730 separated by the ":" character.
726 731
727 732 The syntax of range notation is [BEGIN]:[END], where BEGIN and END
728 733 are revision identifiers. Both BEGIN and END are optional. If
729 734 BEGIN is not specified, it defaults to revision number 0. If END
730 735 is not specified, it defaults to the tip. The range ":" thus
731 736 means "all revisions".
732 737
733 738 If BEGIN is greater than END, revisions are treated in reverse
734 739 order.
735 740
736 741 A range acts as a closed interval. This means that a range of 3:5
737 742 gives 3, 4 and 5. Similarly, a range of 4:2 gives 4, 3, and 2.
738 743
739 744 ENVIRONMENT VARIABLES
740 745 ---------------------
741 746
742 747 HGEDITOR::
743 748 This is the name of the editor to use when committing. Defaults to the
744 749 value of EDITOR.
745 750
746 751 (deprecated, use .hgrc)
747 752
748 753 HGMERGE::
749 754 An executable to use for resolving merge conflicts. The program
750 755 will be executed with three arguments: local file, remote file,
751 756 ancestor file.
752 757
753 758 The default program is "hgmerge", which is a shell script provided
754 759 by Mercurial with some sensible defaults.
755 760
756 761 (deprecated, use .hgrc)
757 762
758 763 HGUSER::
759 764 This is the string used for the author of a commit.
760 765
761 766 (deprecated, use .hgrc)
762 767
763 768 EMAIL::
764 769 If HGUSER is not set, this will be used as the author for a commit.
765 770
766 771 LOGNAME::
767 772 If neither HGUSER nor EMAIL is set, LOGNAME will be used (with
768 773 '@hostname' appended) as the author value for a commit.
769 774
770 775 EDITOR::
771 776 This is the name of the editor used in the hgmerge script. It will be
772 777 used for commit messages if HGEDITOR isn't set. Defaults to 'vi'.
773 778
774 779 PYTHONPATH::
775 780 This is used by Python to find imported modules and may need to be set
776 781 appropriately if Mercurial is not installed system-wide.
777 782
778 783 FILES
779 784 -----
780 785 .hgignore::
781 786 This file contains regular expressions (one per line) that describe file
782 787 names that should be ignored by hg.
783 788
784 789 .hgtags::
785 790 This file contains changeset hash values and text tag names (one of each
786 791 separated by spaces) that correspond to tagged versions of the repository
787 792 contents.
788 793
789 794 /etc/mercurial/hgrc, $HOME/.hgrc, .hg/hgrc::
790 795 This file contains defaults and configuration. Values in .hg/hgrc
791 796 override those in $HOME/.hgrc, and these override settings made in the
792 797 global /etc/mercurial/hgrc configuration. See hgrc(5) for details of
793 798 the contents and format of these files.
794 799
795 800 BUGS
796 801 ----
797 802 Probably lots, please post them to the mailing list (See Resources below)
798 803 when you find them.
799 804
800 805 SEE ALSO
801 806 --------
802 807 hgrc(5)
803 808
804 809 AUTHOR
805 810 ------
806 811 Written by Matt Mackall <mpm@selenic.com>
807 812
808 813 RESOURCES
809 814 ---------
810 815 http://selenic.com/mercurial[Main Web Site]
811 816
812 817 http://www.serpentine.com/mercurial[Wiki site]
813 818
814 819 http://selenic.com/hg[Source code repository]
815 820
816 821 http://selenic.com/mailman/listinfo/mercurial[Mailing list]
817 822
818 823 COPYING
819 824 -------
820 825 Copyright \(C) 2005 Matt Mackall.
821 826 Free use of this software is granted under the terms of the GNU General
822 827 Public License (GPL).
@@ -1,285 +1,297
1 1 HGRC(5)
2 2 =======
3 3 Bryan O'Sullivan <bos@serpentine.com>
4 4
5 5 NAME
6 6 ----
7 7 hgrc - configuration files for Mercurial
8 8
9 9 SYNOPSIS
10 10 --------
11 11
12 12 The Mercurial system uses a set of configuration files to control
13 13 aspects of its behaviour.
14 14
15 15 FILES
16 16 -----
17 17
18 18 Mercurial reads configuration data from several files, if they exist.
19 19 The names of these files depend on the system on which Mercurial is
20 20 installed.
21 21
22 22 (Unix) <install-root>/etc/mercurial/hgrc.d/*.rc::
23 23 (Unix) <install-root>/etc/mercurial/hgrc::
24 24 Per-installation configuration files, searched for in the
25 25 directory where Mercurial is installed. For example, if installed
26 26 in /shared/tools, Mercurial will look in
27 27 /shared/tools/etc/mercurial/hgrc. Options in these files apply to
28 28 all Mercurial commands executed by any user in any directory.
29 29
30 30 (Unix) /etc/mercurial/hgrc.d/*.rc::
31 31 (Unix) /etc/mercurial/hgrc::
32 32 (Windows) C:\Mercurial\Mercurial.ini::
33 33 Per-system configuration files, for the system on which Mercurial
34 34 is running. Options in these files apply to all Mercurial
35 35 commands executed by any user in any directory. Options in these
36 36 files override per-installation options.
37 37
38 38 (Unix) $HOME/.hgrc::
39 39 (Windows) C:\Documents and Settings\USERNAME\Mercurial.ini
40 40 Per-user configuration file, for the user running Mercurial.
41 41 Options in this file apply to all Mercurial commands executed by
42 42 any user in any directory. Options in this file override
43 43 per-installation and per-system options.
44 44
45 45 (Unix, Windows) <repo>/.hg/hgrc::
46 46 Per-repository configuration options that only apply in a
47 47 particular repository. This file is not version-controlled, and
48 48 will not get transferred during a "clone" operation. Options in
49 49 this file override options in all other configuration files.
50 50
51 51 SYNTAX
52 52 ------
53 53
54 54 A configuration file consists of sections, led by a "[section]" header
55 55 and followed by "name: value" entries; "name=value" is also accepted.
56 56
57 57 [spam]
58 58 eggs=ham
59 59 green=
60 60 eggs
61 61
62 62 Each line contains one entry. If the lines that follow are indented,
63 63 they are treated as continuations of that entry.
64 64
65 65 Leading whitespace is removed from values. Empty lines are skipped.
66 66
67 67 The optional values can contain format strings which refer to other
68 68 values in the same section, or values in a special DEFAULT section.
69 69
70 70 Lines beginning with "#" or ";" are ignored and may be used to provide
71 71 comments.
72 72
73 73 SECTIONS
74 74 --------
75 75
76 76 This section describes the different sections that may appear in a
77 77 Mercurial "hgrc" file, the purpose of each section, its possible
78 78 keys, and their possible values.
79 79
80 80 decode/encode::
81 81 Filters for transforming files on checkout/checkin. This would
82 82 typically be used for newline processing or other
83 83 localization/canonicalization of files.
84 84
85 85 Filters consist of a filter pattern followed by a filter command.
86 86 Filter patterns are globs by default, rooted at the repository
87 87 root. For example, to match any file ending in ".txt" in the root
88 88 directory only, use the pattern "*.txt". To match any file ending
89 89 in ".c" anywhere in the repository, use the pattern "**.c".
90 90
91 91 The filter command can start with a specifier, either "pipe:" or
92 92 "tempfile:". If no specifier is given, "pipe:" is used by default.
93 93
94 94 A "pipe:" command must accept data on stdin and return the
95 95 transformed data on stdout.
96 96
97 97 Pipe example:
98 98
99 99 [encode]
100 100 # uncompress gzip files on checkin to improve delta compression
101 101 # note: not necessarily a good idea, just an example
102 102 *.gz = pipe: gunzip
103 103
104 104 [decode]
105 105 # recompress gzip files when writing them to the working dir (we
106 106 # can safely omit "pipe:", because it's the default)
107 107 *.gz = gzip
108 108
109 109 A "tempfile:" command is a template. The string INFILE is replaced
110 110 with the name of a temporary file that contains the data to be
111 111 filtered by the command. The string OUTFILE is replaced with the
112 112 name of an empty temporary file, where the filtered data must be
113 113 written by the command.
114 114
115 115 NOTE: the tempfile mechanism is recommended for Windows systems,
116 116 where the standard shell I/O redirection operators often have
117 117 strange effects. In particular, if you are doing line ending
118 118 conversion on Windows using the popular dos2unix and unix2dos
119 119 programs, you *must* use the tempfile mechanism, as using pipes will
120 120 corrupt the contents of your files.
121 121
122 122 Tempfile example:
123 123
124 124 [encode]
125 125 # convert files to unix line ending conventions on checkin
126 126 **.txt = tempfile: dos2unix -n INFILE OUTFILE
127 127
128 128 [decode]
129 129 # convert files to windows line ending conventions when writing
130 130 # them to the working dir
131 131 **.txt = tempfile: unix2dos -n INFILE OUTFILE
132 132
133 133 hooks::
134 134 Commands that get automatically executed by various actions such as
135 135 starting or finishing a commit. Multiple commands can be run for
136 136 the same action by appending a suffix to the action. Overriding a
137 137 site-wide hook can be done by changing its value or setting it to
138 138 an empty string.
139 139
140 140 Example .hg/hgrc:
141 141
142 142 [hooks]
143 143 # do not use the site-wide hook
144 144 incoming =
145 145 incoming.email = /my/email/hook
146 146 incoming.autobuild = /my/build/hook
147 147
148 148 Most hooks are run with environment variables set that give added
149 149 useful information. For each hook below, the environment variables
150 150 it is passed are listed with names of the form "$HG_foo".
151 151
152 152 changegroup;;
153 Run after a changegroup has been added via push or pull. ID of the
154 first new changeset is in $HG_NODE.
153 Run after a changegroup has been added via push, pull or
154 unbundle. ID of the first new changeset is in $HG_NODE.
155 155 commit;;
156 156 Run after a changeset has been created in the local repository.
157 157 ID of the newly created changeset is in $HG_NODE. Parent
158 158 changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
159 159 incoming;;
160 160 Run after a changeset has been pulled, pushed, or unbundled into
161 161 the local repository. The ID of the newly arrived changeset is in
162 162 $HG_NODE.
163 prechangegroup;;
164 Run before a changegroup is added via push, pull or unbundle.
165 Exit status 0 allows the changegroup to proceed. Non-zero status
166 will cause the push, pull or unbundle to fail.
163 167 precommit;;
164 168 Run before starting a local commit. Exit status 0 allows the
165 169 commit to proceed. Non-zero status will cause the commit to fail.
166 170 Parent changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
167 171 pretag;;
168 172 Run before creating a tag. Exit status 0 allows the tag to be
169 173 created. Non-zero status will cause the tag to fail. ID of
170 174 changeset to tag is in $HG_NODE. Name of tag is in $HG_TAG. Tag
171 175 is local if $HG_LOCAL=1, in repo if $HG_LOCAL=0.
176 pretxnchangegroup;;
177 Run after a changegroup has been added via push, pull or unbundle,
178 but before the transaction has been committed. Changegroup is
179 visible to hook program. This lets you validate incoming changes
180 before accepting them. Passed the ID of the first new changeset
181 in $NODE. Exit status 0 allows the transaction to commit.
182 Non-zero status will cause the transaction to be rolled back and
183 the push, pull or unbundle will fail.
172 184 pretxncommit;;
173 185 Run after a changeset has been created but the transaction not yet
174 186 committed. Changeset is visible to hook program. This lets you
175 187 validate commit message and changes. Exit status 0 allows the
176 188 commit to proceed. Non-zero status will cause the transaction to
177 189 be rolled back. ID of changeset is in $HG_NODE. Parent changeset
178 190 IDs are in $HG_PARENT1 and $HG_PARENT2.
179 191 tag;;
180 192 Run after a tag is created. ID of tagged changeset is in
181 193 $HG_NODE. Name of tag is in $HG_TAG. Tag is local if
182 194 $HG_LOCAL=1, in repo if $HG_LOCAL=0.
183 195
184 196 In earlier releases, the names of hook environment variables did not
185 197 have a "HG_" prefix. These unprefixed names are still provided in
186 198 the environment for backwards compatibility, but their use is
187 199 deprecated, and they will be removed in a future release.
188 200
189 201 http_proxy::
190 202 Used to access web-based Mercurial repositories through a HTTP
191 203 proxy.
192 204 host;;
193 205 Host name and (optional) port of the proxy server, for example
194 206 "myproxy:8000".
195 207 no;;
196 208 Optional. Comma-separated list of host names that should bypass
197 209 the proxy.
198 210 passwd;;
199 211 Optional. Password to authenticate with at the proxy server.
200 212 user;;
201 213 Optional. User name to authenticate with at the proxy server.
202 214
203 215 paths::
204 216 Assigns symbolic names to repositories. The left side is the
205 217 symbolic name, and the right gives the directory or URL that is the
206 218 location of the repository.
207 219
208 220 ui::
209 221 User interface controls.
210 222 debug;;
211 223 Print debugging information. True or False. Default is False.
212 224 editor;;
213 225 The editor to use during a commit. Default is $EDITOR or "vi".
214 226 interactive;;
215 227 Allow to prompt the user. True or False. Default is True.
216 228 merge;;
217 229 The conflict resolution program to use during a manual merge.
218 230 Default is "hgmerge".
219 231 quiet;;
220 232 Reduce the amount of output printed. True or False. Default is False.
221 233 remotecmd;;
222 234 remote command to use for clone/push/pull operations. Default is 'hg'.
223 235 ssh;;
224 236 command to use for SSH connections. Default is 'ssh'.
225 237 username;;
226 238 The committer of a changeset created when running "commit".
227 239 Typically a person's name and email address, e.g. "Fred Widget
228 240 <fred@example.com>". Default is $EMAIL or username@hostname.
229 241 verbose;;
230 242 Increase the amount of output printed. True or False. Default is False.
231 243
232 244
233 245 web::
234 246 Web interface configuration.
235 247 accesslog;;
236 248 Where to output the access log. Default is stdout.
237 249 address;;
238 250 Interface address to bind to. Default is all.
239 251 allowbz2;;
240 252 Whether to allow .tar.bz2 downloading of repo revisions. Default is false.
241 253 allowgz;;
242 254 Whether to allow .tar.gz downloading of repo revisions. Default is false.
243 255 allowpull;;
244 256 Whether to allow pulling from the repository. Default is true.
245 257 allowzip;;
246 258 Whether to allow .zip downloading of repo revisions. Default is false.
247 259 This feature creates temporary files.
248 260 description;;
249 261 Textual description of the repository's purpose or contents.
250 262 Default is "unknown".
251 263 errorlog;;
252 264 Where to output the error log. Default is stderr.
253 265 ipv6;;
254 266 Whether to use IPv6. Default is false.
255 267 name;;
256 268 Repository name to use in the web interface. Default is current
257 269 working directory.
258 270 maxchanges;;
259 271 Maximum number of changes to list on the changelog. Default is 10.
260 272 maxfiles;;
261 273 Maximum number of files to list per changeset. Default is 10.
262 274 port;;
263 275 Port to listen on. Default is 8000.
264 276 style;;
265 277 Which template map style to use.
266 278 templates;;
267 279 Where to find the HTML templates. Default is install path.
268 280
269 281
270 282 AUTHOR
271 283 ------
272 284 Bryan O'Sullivan <bos@serpentine.com>.
273 285
274 286 Mercurial was written by Matt Mackall <mpm@selenic.com>.
275 287
276 288 SEE ALSO
277 289 --------
278 290 hg(1)
279 291
280 292 COPYING
281 293 -------
282 294 This manual page is copyright 2005 Bryan O'Sullivan.
283 295 Mercurial is copyright 2005 Matt Mackall.
284 296 Free use of this software is granted under the terms of the GNU General
285 297 Public License (GPL).
@@ -1,2805 +1,2812
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import demandload
9 9 from node import *
10 10 from i18n import gettext as _
11 11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 12 demandload(globals(), "fancyopts ui hg util lock revlog")
13 13 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
14 14 demandload(globals(), "errno socket version struct atexit sets bz2")
15 15
16 16 class UnknownCommand(Exception):
17 17 """Exception raised if command is not in the command table."""
18 18 class AmbiguousCommand(Exception):
19 19 """Exception raised if command shortcut matches more than one command."""
20 20
21 21 def filterfiles(filters, files):
22 22 l = [x for x in files if x in filters]
23 23
24 24 for t in filters:
25 25 if t and t[-1] != "/":
26 26 t += "/"
27 27 l += [x for x in files if x.startswith(t)]
28 28 return l
29 29
30 30 def relpath(repo, args):
31 31 cwd = repo.getcwd()
32 32 if cwd:
33 33 return [util.normpath(os.path.join(cwd, x)) for x in args]
34 34 return args
35 35
36 36 def matchpats(repo, pats=[], opts={}, head=''):
37 37 cwd = repo.getcwd()
38 38 if not pats and cwd:
39 39 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
40 40 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
41 41 cwd = ''
42 42 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
43 43 opts.get('exclude'), head)
44 44
45 45 def makewalk(repo, pats, opts, node=None, head=''):
46 46 files, matchfn, anypats = matchpats(repo, pats, opts, head)
47 47 exact = dict(zip(files, files))
48 48 def walk():
49 49 for src, fn in repo.walk(node=node, files=files, match=matchfn):
50 50 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
51 51 return files, matchfn, walk()
52 52
53 53 def walk(repo, pats, opts, node=None, head=''):
54 54 files, matchfn, results = makewalk(repo, pats, opts, node, head)
55 55 for r in results:
56 56 yield r
57 57
58 58 def walkchangerevs(ui, repo, pats, opts):
59 59 '''Iterate over files and the revs they changed in.
60 60
61 61 Callers most commonly need to iterate backwards over the history
62 62 it is interested in. Doing so has awful (quadratic-looking)
63 63 performance, so we use iterators in a "windowed" way.
64 64
65 65 We walk a window of revisions in the desired order. Within the
66 66 window, we first walk forwards to gather data, then in the desired
67 67 order (usually backwards) to display it.
68 68
69 69 This function returns an (iterator, getchange, matchfn) tuple. The
70 70 getchange function returns the changelog entry for a numeric
71 71 revision. The iterator yields 3-tuples. They will be of one of
72 72 the following forms:
73 73
74 74 "window", incrementing, lastrev: stepping through a window,
75 75 positive if walking forwards through revs, last rev in the
76 76 sequence iterated over - use to reset state for the current window
77 77
78 78 "add", rev, fns: out-of-order traversal of the given file names
79 79 fns, which changed during revision rev - use to gather data for
80 80 possible display
81 81
82 82 "iter", rev, None: in-order traversal of the revs earlier iterated
83 83 over with "add" - use to display data'''
84 84
85 85 files, matchfn, anypats = matchpats(repo, pats, opts)
86 86
87 87 if repo.changelog.count() == 0:
88 88 return [], False, matchfn
89 89
90 90 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
91 91 wanted = {}
92 92 slowpath = anypats
93 93 window = 300
94 94 fncache = {}
95 95
96 96 chcache = {}
97 97 def getchange(rev):
98 98 ch = chcache.get(rev)
99 99 if ch is None:
100 100 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
101 101 return ch
102 102
103 103 if not slowpath and not files:
104 104 # No files, no patterns. Display all revs.
105 105 wanted = dict(zip(revs, revs))
106 106 if not slowpath:
107 107 # Only files, no patterns. Check the history of each file.
108 108 def filerevgen(filelog):
109 109 for i in xrange(filelog.count() - 1, -1, -window):
110 110 revs = []
111 111 for j in xrange(max(0, i - window), i + 1):
112 112 revs.append(filelog.linkrev(filelog.node(j)))
113 113 revs.reverse()
114 114 for rev in revs:
115 115 yield rev
116 116
117 117 minrev, maxrev = min(revs), max(revs)
118 118 for file in files:
119 119 filelog = repo.file(file)
120 120 # A zero count may be a directory or deleted file, so
121 121 # try to find matching entries on the slow path.
122 122 if filelog.count() == 0:
123 123 slowpath = True
124 124 break
125 125 for rev in filerevgen(filelog):
126 126 if rev <= maxrev:
127 127 if rev < minrev:
128 128 break
129 129 fncache.setdefault(rev, [])
130 130 fncache[rev].append(file)
131 131 wanted[rev] = 1
132 132 if slowpath:
133 133 # The slow path checks files modified in every changeset.
134 134 def changerevgen():
135 135 for i in xrange(repo.changelog.count() - 1, -1, -window):
136 136 for j in xrange(max(0, i - window), i + 1):
137 137 yield j, getchange(j)[3]
138 138
139 139 for rev, changefiles in changerevgen():
140 140 matches = filter(matchfn, changefiles)
141 141 if matches:
142 142 fncache[rev] = matches
143 143 wanted[rev] = 1
144 144
145 145 def iterate():
146 146 for i in xrange(0, len(revs), window):
147 147 yield 'window', revs[0] < revs[-1], revs[-1]
148 148 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
149 149 if rev in wanted]
150 150 srevs = list(nrevs)
151 151 srevs.sort()
152 152 for rev in srevs:
153 153 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
154 154 yield 'add', rev, fns
155 155 for rev in nrevs:
156 156 yield 'iter', rev, None
157 157 return iterate(), getchange, matchfn
158 158
159 159 revrangesep = ':'
160 160
161 161 def revrange(ui, repo, revs, revlog=None):
162 162 """Yield revision as strings from a list of revision specifications."""
163 163 if revlog is None:
164 164 revlog = repo.changelog
165 165 revcount = revlog.count()
166 166 def fix(val, defval):
167 167 if not val:
168 168 return defval
169 169 try:
170 170 num = int(val)
171 171 if str(num) != val:
172 172 raise ValueError
173 173 if num < 0:
174 174 num += revcount
175 175 if num < 0:
176 176 num = 0
177 177 elif num >= revcount:
178 178 raise ValueError
179 179 except ValueError:
180 180 try:
181 181 num = repo.changelog.rev(repo.lookup(val))
182 182 except KeyError:
183 183 try:
184 184 num = revlog.rev(revlog.lookup(val))
185 185 except KeyError:
186 186 raise util.Abort(_('invalid revision identifier %s'), val)
187 187 return num
188 188 seen = {}
189 189 for spec in revs:
190 190 if spec.find(revrangesep) >= 0:
191 191 start, end = spec.split(revrangesep, 1)
192 192 start = fix(start, 0)
193 193 end = fix(end, revcount - 1)
194 194 step = start > end and -1 or 1
195 195 for rev in xrange(start, end+step, step):
196 196 if rev in seen:
197 197 continue
198 198 seen[rev] = 1
199 199 yield str(rev)
200 200 else:
201 201 rev = fix(spec, None)
202 202 if rev in seen:
203 203 continue
204 204 seen[rev] = 1
205 205 yield str(rev)
206 206
207 207 def make_filename(repo, r, pat, node=None,
208 208 total=None, seqno=None, revwidth=None, pathname=None):
209 209 node_expander = {
210 210 'H': lambda: hex(node),
211 211 'R': lambda: str(r.rev(node)),
212 212 'h': lambda: short(node),
213 213 }
214 214 expander = {
215 215 '%': lambda: '%',
216 216 'b': lambda: os.path.basename(repo.root),
217 217 }
218 218
219 219 try:
220 220 if node:
221 221 expander.update(node_expander)
222 222 if node and revwidth is not None:
223 223 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
224 224 if total is not None:
225 225 expander['N'] = lambda: str(total)
226 226 if seqno is not None:
227 227 expander['n'] = lambda: str(seqno)
228 228 if total is not None and seqno is not None:
229 229 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
230 230 if pathname is not None:
231 231 expander['s'] = lambda: os.path.basename(pathname)
232 232 expander['d'] = lambda: os.path.dirname(pathname) or '.'
233 233 expander['p'] = lambda: pathname
234 234
235 235 newname = []
236 236 patlen = len(pat)
237 237 i = 0
238 238 while i < patlen:
239 239 c = pat[i]
240 240 if c == '%':
241 241 i += 1
242 242 c = pat[i]
243 243 c = expander[c]()
244 244 newname.append(c)
245 245 i += 1
246 246 return ''.join(newname)
247 247 except KeyError, inst:
248 248 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
249 249 inst.args[0])
250 250
251 251 def make_file(repo, r, pat, node=None,
252 252 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
253 253 if not pat or pat == '-':
254 254 return 'w' in mode and sys.stdout or sys.stdin
255 255 if hasattr(pat, 'write') and 'w' in mode:
256 256 return pat
257 257 if hasattr(pat, 'read') and 'r' in mode:
258 258 return pat
259 259 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
260 260 pathname),
261 261 mode)
262 262
263 263 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
264 changes=None, text=False):
264 changes=None, text=False, opts={}):
265 265 if not changes:
266 266 changes = repo.changes(node1, node2, files, match=match)
267 267 modified, added, removed, deleted, unknown = changes
268 268 if files:
269 269 modified, added, removed = map(lambda x: filterfiles(files, x),
270 270 (modified, added, removed))
271 271
272 272 if not modified and not added and not removed:
273 273 return
274 274
275 275 if node2:
276 276 change = repo.changelog.read(node2)
277 277 mmap2 = repo.manifest.read(change[0])
278 278 date2 = util.datestr(change[2])
279 279 def read(f):
280 280 return repo.file(f).read(mmap2[f])
281 281 else:
282 282 date2 = util.datestr()
283 283 if not node1:
284 284 node1 = repo.dirstate.parents()[0]
285 285 def read(f):
286 286 return repo.wread(f)
287 287
288 288 if ui.quiet:
289 289 r = None
290 290 else:
291 291 hexfunc = ui.verbose and hex or short
292 292 r = [hexfunc(node) for node in [node1, node2] if node]
293 293
294 294 change = repo.changelog.read(node1)
295 295 mmap = repo.manifest.read(change[0])
296 296 date1 = util.datestr(change[2])
297 297
298 298 diffopts = ui.diffopts()
299 showfunc = diffopts['showfunc']
300 ignorews = diffopts['ignorews']
299 showfunc = opts.get('show_function') or diffopts['showfunc']
300 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
301 301 for f in modified:
302 302 to = None
303 303 if f in mmap:
304 304 to = repo.file(f).read(mmap[f])
305 305 tn = read(f)
306 306 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
307 307 showfunc=showfunc, ignorews=ignorews))
308 308 for f in added:
309 309 to = None
310 310 tn = read(f)
311 311 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
312 312 showfunc=showfunc, ignorews=ignorews))
313 313 for f in removed:
314 314 to = repo.file(f).read(mmap[f])
315 315 tn = None
316 316 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
317 317 showfunc=showfunc, ignorews=ignorews))
318 318
319 319 def trimuser(ui, name, rev, revcache):
320 320 """trim the name of the user who committed a change"""
321 321 user = revcache.get(rev)
322 322 if user is None:
323 323 user = revcache[rev] = ui.shortuser(name)
324 324 return user
325 325
326 326 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
327 327 """show a single changeset or file revision"""
328 328 log = repo.changelog
329 329 if changenode is None:
330 330 changenode = log.node(rev)
331 331 elif not rev:
332 332 rev = log.rev(changenode)
333 333
334 334 if ui.quiet:
335 335 ui.write("%d:%s\n" % (rev, short(changenode)))
336 336 return
337 337
338 338 changes = log.read(changenode)
339 339 date = util.datestr(changes[2])
340 340
341 341 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
342 342 for p in log.parents(changenode)
343 343 if ui.debugflag or p != nullid]
344 344 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
345 345 parents = []
346 346
347 347 if ui.verbose:
348 348 ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
349 349 else:
350 350 ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
351 351
352 352 for tag in repo.nodetags(changenode):
353 353 ui.status(_("tag: %s\n") % tag)
354 354 for parent in parents:
355 355 ui.write(_("parent: %d:%s\n") % parent)
356 356
357 357 if brinfo and changenode in brinfo:
358 358 br = brinfo[changenode]
359 359 ui.write(_("branch: %s\n") % " ".join(br))
360 360
361 361 ui.debug(_("manifest: %d:%s\n") % (repo.manifest.rev(changes[0]),
362 362 hex(changes[0])))
363 363 ui.status(_("user: %s\n") % changes[1])
364 364 ui.status(_("date: %s\n") % date)
365 365
366 366 if ui.debugflag:
367 367 files = repo.changes(log.parents(changenode)[0], changenode)
368 368 for key, value in zip([_("files:"), _("files+:"), _("files-:")], files):
369 369 if value:
370 370 ui.note("%-12s %s\n" % (key, " ".join(value)))
371 371 else:
372 372 ui.note(_("files: %s\n") % " ".join(changes[3]))
373 373
374 374 description = changes[4].strip()
375 375 if description:
376 376 if ui.verbose:
377 377 ui.status(_("description:\n"))
378 378 ui.status(description)
379 379 ui.status("\n\n")
380 380 else:
381 381 ui.status(_("summary: %s\n") % description.splitlines()[0])
382 382 ui.status("\n")
383 383
384 384 def show_version(ui):
385 385 """output version and copyright information"""
386 386 ui.write(_("Mercurial Distributed SCM (version %s)\n")
387 387 % version.get_version())
388 388 ui.status(_(
389 389 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
390 390 "This is free software; see the source for copying conditions. "
391 391 "There is NO\nwarranty; "
392 392 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
393 393 ))
394 394
395 395 def help_(ui, cmd=None, with_version=False):
396 396 """show help for a given command or all commands"""
397 397 option_lists = []
398 398 if cmd and cmd != 'shortlist':
399 399 if with_version:
400 400 show_version(ui)
401 401 ui.write('\n')
402 402 aliases, i = find(cmd)
403 403 # synopsis
404 404 ui.write("%s\n\n" % i[2])
405 405
406 406 # description
407 407 doc = i[0].__doc__
408 408 if not doc:
409 409 doc = _("(No help text available)")
410 410 if ui.quiet:
411 411 doc = doc.splitlines(0)[0]
412 412 ui.write("%s\n" % doc.rstrip())
413 413
414 414 if not ui.quiet:
415 415 # aliases
416 416 if len(aliases) > 1:
417 417 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
418 418
419 419 # options
420 420 if i[1]:
421 421 option_lists.append(("options", i[1]))
422 422
423 423 else:
424 424 # program name
425 425 if ui.verbose or with_version:
426 426 show_version(ui)
427 427 else:
428 428 ui.status(_("Mercurial Distributed SCM\n"))
429 429 ui.status('\n')
430 430
431 431 # list of commands
432 432 if cmd == "shortlist":
433 433 ui.status(_('basic commands (use "hg help" '
434 434 'for the full list or option "-v" for details):\n\n'))
435 435 elif ui.verbose:
436 436 ui.status(_('list of commands:\n\n'))
437 437 else:
438 438 ui.status(_('list of commands (use "hg help -v" '
439 439 'to show aliases and global options):\n\n'))
440 440
441 441 h = {}
442 442 cmds = {}
443 443 for c, e in table.items():
444 444 f = c.split("|")[0]
445 445 if cmd == "shortlist" and not f.startswith("^"):
446 446 continue
447 447 f = f.lstrip("^")
448 448 if not ui.debugflag and f.startswith("debug"):
449 449 continue
450 450 d = ""
451 451 doc = e[0].__doc__
452 452 if not doc:
453 453 doc = _("(No help text available)")
454 454 h[f] = doc.splitlines(0)[0].rstrip()
455 455 cmds[f] = c.lstrip("^")
456 456
457 457 fns = h.keys()
458 458 fns.sort()
459 459 m = max(map(len, fns))
460 460 for f in fns:
461 461 if ui.verbose:
462 462 commands = cmds[f].replace("|",", ")
463 463 ui.write(" %s:\n %s\n"%(commands, h[f]))
464 464 else:
465 465 ui.write(' %-*s %s\n' % (m, f, h[f]))
466 466
467 467 # global options
468 468 if ui.verbose:
469 469 option_lists.append(("global options", globalopts))
470 470
471 471 # list all option lists
472 472 opt_output = []
473 473 for title, options in option_lists:
474 474 opt_output.append(("\n%s:\n" % title, None))
475 475 for shortopt, longopt, default, desc in options:
476 476 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
477 477 longopt and " --%s" % longopt),
478 478 "%s%s" % (desc,
479 479 default
480 480 and _(" (default: %s)") % default
481 481 or "")))
482 482
483 483 if opt_output:
484 484 opts_len = max([len(line[0]) for line in opt_output if line[1]])
485 485 for first, second in opt_output:
486 486 if second:
487 487 ui.write(" %-*s %s\n" % (opts_len, first, second))
488 488 else:
489 489 ui.write("%s\n" % first)
490 490
491 491 # Commands start here, listed alphabetically
492 492
493 493 def add(ui, repo, *pats, **opts):
494 494 """add the specified files on the next commit
495 495
496 496 Schedule files to be version controlled and added to the repository.
497 497
498 498 The files will be added to the repository at the next commit.
499 499
500 500 If no names are given, add all files in the repository.
501 501 """
502 502
503 503 names = []
504 504 for src, abs, rel, exact in walk(repo, pats, opts):
505 505 if exact:
506 506 if ui.verbose:
507 507 ui.status(_('adding %s\n') % rel)
508 508 names.append(abs)
509 509 elif repo.dirstate.state(abs) == '?':
510 510 ui.status(_('adding %s\n') % rel)
511 511 names.append(abs)
512 512 repo.add(names)
513 513
514 514 def addremove(ui, repo, *pats, **opts):
515 515 """add all new files, delete all missing files
516 516
517 517 Add all new files and remove all missing files from the repository.
518 518
519 519 New files are ignored if they match any of the patterns in .hgignore. As
520 520 with add, these changes take effect at the next commit.
521 521 """
522 522 return addremove_lock(ui, repo, pats, opts)
523 523
524 524 def addremove_lock(ui, repo, pats, opts, wlock=None):
525 525 add, remove = [], []
526 526 for src, abs, rel, exact in walk(repo, pats, opts):
527 527 if src == 'f' and repo.dirstate.state(abs) == '?':
528 528 add.append(abs)
529 529 if ui.verbose or not exact:
530 530 ui.status(_('adding %s\n') % ((pats and rel) or abs))
531 531 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
532 532 remove.append(abs)
533 533 if ui.verbose or not exact:
534 534 ui.status(_('removing %s\n') % ((pats and rel) or abs))
535 535 repo.add(add, wlock=wlock)
536 536 repo.remove(remove, wlock=wlock)
537 537
538 538 def annotate(ui, repo, *pats, **opts):
539 539 """show changeset information per file line
540 540
541 541 List changes in files, showing the revision id responsible for each line
542 542
543 543 This command is useful to discover who did a change or when a change took
544 544 place.
545 545
546 546 Without the -a option, annotate will avoid processing files it
547 547 detects as binary. With -a, annotate will generate an annotation
548 548 anyway, probably with undesirable results.
549 549 """
550 550 def getnode(rev):
551 551 return short(repo.changelog.node(rev))
552 552
553 553 ucache = {}
554 554 def getname(rev):
555 555 cl = repo.changelog.read(repo.changelog.node(rev))
556 556 return trimuser(ui, cl[1], rev, ucache)
557 557
558 558 dcache = {}
559 559 def getdate(rev):
560 560 datestr = dcache.get(rev)
561 561 if datestr is None:
562 562 cl = repo.changelog.read(repo.changelog.node(rev))
563 563 datestr = dcache[rev] = util.datestr(cl[2])
564 564 return datestr
565 565
566 566 if not pats:
567 567 raise util.Abort(_('at least one file name or pattern required'))
568 568
569 569 opmap = [['user', getname], ['number', str], ['changeset', getnode],
570 570 ['date', getdate]]
571 571 if not opts['user'] and not opts['changeset'] and not opts['date']:
572 572 opts['number'] = 1
573 573
574 574 if opts['rev']:
575 575 node = repo.changelog.lookup(opts['rev'])
576 576 else:
577 577 node = repo.dirstate.parents()[0]
578 578 change = repo.changelog.read(node)
579 579 mmap = repo.manifest.read(change[0])
580 580
581 581 for src, abs, rel, exact in walk(repo, pats, opts):
582 582 if abs not in mmap:
583 583 ui.warn(_("warning: %s is not in the repository!\n") %
584 584 ((pats and rel) or abs))
585 585 continue
586 586
587 587 f = repo.file(abs)
588 588 if not opts['text'] and util.binary(f.read(mmap[abs])):
589 589 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
590 590 continue
591 591
592 592 lines = f.annotate(mmap[abs])
593 593 pieces = []
594 594
595 595 for o, f in opmap:
596 596 if opts[o]:
597 597 l = [f(n) for n, dummy in lines]
598 598 if l:
599 599 m = max(map(len, l))
600 600 pieces.append(["%*s" % (m, x) for x in l])
601 601
602 602 if pieces:
603 603 for p, l in zip(zip(*pieces), lines):
604 604 ui.write("%s: %s" % (" ".join(p), l[1]))
605 605
606 606 def bundle(ui, repo, fname, dest="default-push", **opts):
607 607 """create a changegroup file
608 608
609 609 Generate a compressed changegroup file collecting all changesets
610 610 not found in the other repository.
611 611
612 612 This file can then be transferred using conventional means and
613 613 applied to another repository with the unbundle command. This is
614 614 useful when native push and pull are not available or when
615 615 exporting an entire repository is undesirable. The standard file
616 616 extension is ".hg".
617 617
618 618 Unlike import/export, this exactly preserves all changeset
619 619 contents including permissions, rename data, and revision history.
620 620 """
621 621 f = open(fname, "wb")
622 622 dest = ui.expandpath(dest, repo.root)
623 623 other = hg.repository(ui, dest)
624 624 o = repo.findoutgoing(other)
625 625 cg = repo.changegroup(o)
626 626
627 627 try:
628 628 f.write("HG10")
629 629 z = bz2.BZ2Compressor(9)
630 630 while 1:
631 631 chunk = cg.read(4096)
632 632 if not chunk:
633 633 break
634 634 f.write(z.compress(chunk))
635 635 f.write(z.flush())
636 636 except:
637 637 os.unlink(fname)
638 638 raise
639 639
640 640 def cat(ui, repo, file1, *pats, **opts):
641 641 """output the latest or given revisions of files
642 642
643 643 Print the specified files as they were at the given revision.
644 644 If no revision is given then the tip is used.
645 645
646 646 Output may be to a file, in which case the name of the file is
647 647 given using a format string. The formatting rules are the same as
648 648 for the export command, with the following additions:
649 649
650 650 %s basename of file being printed
651 651 %d dirname of file being printed, or '.' if in repo root
652 652 %p root-relative path name of file being printed
653 653 """
654 654 mf = {}
655 655 rev = opts['rev']
656 656 if rev:
657 657 node = repo.lookup(rev)
658 658 else:
659 659 node = repo.changelog.tip()
660 660 change = repo.changelog.read(node)
661 661 mf = repo.manifest.read(change[0])
662 662 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
663 663 r = repo.file(abs)
664 664 n = mf[abs]
665 665 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
666 666 fp.write(r.read(n))
667 667
668 668 def clone(ui, source, dest=None, **opts):
669 669 """make a copy of an existing repository
670 670
671 671 Create a copy of an existing repository in a new directory.
672 672
673 673 If no destination directory name is specified, it defaults to the
674 674 basename of the source.
675 675
676 676 The location of the source is added to the new repository's
677 677 .hg/hgrc file, as the default to be used for future pulls.
678 678
679 679 For efficiency, hardlinks are used for cloning whenever the source
680 680 and destination are on the same filesystem. Some filesystems,
681 681 such as AFS, implement hardlinking incorrectly, but do not report
682 682 errors. In these cases, use the --pull option to avoid
683 683 hardlinking.
684 684 """
685 685 if dest is None:
686 686 dest = os.path.basename(os.path.normpath(source))
687 687
688 688 if os.path.exists(dest):
689 689 raise util.Abort(_("destination '%s' already exists"), dest)
690 690
691 691 dest = os.path.realpath(dest)
692 692
693 693 class Dircleanup(object):
694 694 def __init__(self, dir_):
695 695 self.rmtree = shutil.rmtree
696 696 self.dir_ = dir_
697 697 os.mkdir(dir_)
698 698 def close(self):
699 699 self.dir_ = None
700 700 def __del__(self):
701 701 if self.dir_:
702 702 self.rmtree(self.dir_, True)
703 703
704 704 if opts['ssh']:
705 705 ui.setconfig("ui", "ssh", opts['ssh'])
706 706 if opts['remotecmd']:
707 707 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
708 708
709 709 if not os.path.exists(source):
710 710 source = ui.expandpath(source)
711 711
712 712 d = Dircleanup(dest)
713 713 abspath = source
714 714 other = hg.repository(ui, source)
715 715
716 716 copy = False
717 717 if other.dev() != -1:
718 718 abspath = os.path.abspath(source)
719 719 if not opts['pull'] and not opts['rev']:
720 720 copy = True
721 721
722 722 if copy:
723 723 try:
724 724 # we use a lock here because if we race with commit, we
725 725 # can end up with extra data in the cloned revlogs that's
726 726 # not pointed to by changesets, thus causing verify to
727 727 # fail
728 728 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
729 729 except OSError:
730 730 copy = False
731 731
732 732 if copy:
733 733 # we lock here to avoid premature writing to the target
734 734 os.mkdir(os.path.join(dest, ".hg"))
735 735 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
736 736
737 737 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
738 738 for f in files.split():
739 739 src = os.path.join(source, ".hg", f)
740 740 dst = os.path.join(dest, ".hg", f)
741 741 try:
742 742 util.copyfiles(src, dst)
743 743 except OSError, inst:
744 744 if inst.errno != errno.ENOENT:
745 745 raise
746 746
747 747 repo = hg.repository(ui, dest)
748 748
749 749 else:
750 750 revs = None
751 751 if opts['rev']:
752 752 if not other.local():
753 753 error = _("clone -r not supported yet for remote repositories.")
754 754 raise util.Abort(error)
755 755 else:
756 756 revs = [other.lookup(rev) for rev in opts['rev']]
757 757 repo = hg.repository(ui, dest, create=1)
758 758 repo.pull(other, heads = revs)
759 759
760 760 f = repo.opener("hgrc", "w", text=True)
761 761 f.write("[paths]\n")
762 762 f.write("default = %s\n" % abspath)
763 763 f.close()
764 764
765 765 if not opts['noupdate']:
766 766 update(ui, repo)
767 767
768 768 d.close()
769 769
770 770 def commit(ui, repo, *pats, **opts):
771 771 """commit the specified files or all outstanding changes
772 772
773 773 Commit changes to the given files into the repository.
774 774
775 775 If a list of files is omitted, all changes reported by "hg status"
776 776 will be commited.
777 777
778 778 The HGEDITOR or EDITOR environment variables are used to start an
779 779 editor to add a commit comment.
780 780 """
781 781 message = opts['message']
782 782 logfile = opts['logfile']
783 783
784 784 if message and logfile:
785 785 raise util.Abort(_('options --message and --logfile are mutually '
786 786 'exclusive'))
787 787 if not message and logfile:
788 788 try:
789 789 if logfile == '-':
790 790 message = sys.stdin.read()
791 791 else:
792 792 message = open(logfile).read()
793 793 except IOError, inst:
794 794 raise util.Abort(_("can't read commit message '%s': %s") %
795 795 (logfile, inst.strerror))
796 796
797 797 if opts['addremove']:
798 798 addremove(ui, repo, *pats, **opts)
799 799 fns, match, anypats = matchpats(repo, pats, opts)
800 800 if pats:
801 801 modified, added, removed, deleted, unknown = (
802 802 repo.changes(files=fns, match=match))
803 803 files = modified + added + removed
804 804 else:
805 805 files = []
806 806 try:
807 807 repo.commit(files, message, opts['user'], opts['date'], match)
808 808 except ValueError, inst:
809 809 raise util.Abort(str(inst))
810 810
811 811 def docopy(ui, repo, pats, opts):
812 812 cwd = repo.getcwd()
813 813 errors = 0
814 814 copied = []
815 815 targets = {}
816 816
817 817 def okaytocopy(abs, rel, exact):
818 818 reasons = {'?': _('is not managed'),
819 819 'a': _('has been marked for add'),
820 820 'r': _('has been marked for remove')}
821 821 reason = reasons.get(repo.dirstate.state(abs))
822 822 if reason:
823 823 if exact:
824 824 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
825 825 else:
826 826 return True
827 827
828 828 def copy(abssrc, relsrc, target, exact):
829 829 abstarget = util.canonpath(repo.root, cwd, target)
830 830 reltarget = util.pathto(cwd, abstarget)
831 831 prevsrc = targets.get(abstarget)
832 832 if prevsrc is not None:
833 833 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
834 834 (reltarget, abssrc, prevsrc))
835 835 return
836 836 if (not opts['after'] and os.path.exists(reltarget) or
837 837 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
838 838 if not opts['force']:
839 839 ui.warn(_('%s: not overwriting - file exists\n') %
840 840 reltarget)
841 841 return
842 842 if not opts['after']:
843 843 os.unlink(reltarget)
844 844 if opts['after']:
845 845 if not os.path.exists(reltarget):
846 846 return
847 847 else:
848 848 targetdir = os.path.dirname(reltarget) or '.'
849 849 if not os.path.isdir(targetdir):
850 850 os.makedirs(targetdir)
851 851 try:
852 852 shutil.copyfile(relsrc, reltarget)
853 853 shutil.copymode(relsrc, reltarget)
854 854 except shutil.Error, inst:
855 855 raise util.Abort(str(inst))
856 856 except IOError, inst:
857 857 if inst.errno == errno.ENOENT:
858 858 ui.warn(_('%s: deleted in working copy\n') % relsrc)
859 859 else:
860 860 ui.warn(_('%s: cannot copy - %s\n') %
861 861 (relsrc, inst.strerror))
862 862 errors += 1
863 863 return
864 864 if ui.verbose or not exact:
865 865 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
866 866 targets[abstarget] = abssrc
867 867 repo.copy(abssrc, abstarget)
868 868 copied.append((abssrc, relsrc, exact))
869 869
870 870 def targetpathfn(pat, dest, srcs):
871 871 if os.path.isdir(pat):
872 872 abspfx = util.canonpath(repo.root, cwd, pat)
873 873 if destdirexists:
874 874 striplen = len(os.path.split(abspfx)[0])
875 875 else:
876 876 striplen = len(abspfx)
877 877 if striplen:
878 878 striplen += len(os.sep)
879 879 res = lambda p: os.path.join(dest, p[striplen:])
880 880 elif destdirexists:
881 881 res = lambda p: os.path.join(dest, os.path.basename(p))
882 882 else:
883 883 res = lambda p: dest
884 884 return res
885 885
886 886 def targetpathafterfn(pat, dest, srcs):
887 887 if util.patkind(pat, None)[0]:
888 888 # a mercurial pattern
889 889 res = lambda p: os.path.join(dest, os.path.basename(p))
890 890 else:
891 891 abspfx = util.canonpath(repo.root, cwd, pat)
892 892 if len(abspfx) < len(srcs[0][0]):
893 893 # A directory. Either the target path contains the last
894 894 # component of the source path or it does not.
895 895 def evalpath(striplen):
896 896 score = 0
897 897 for s in srcs:
898 898 t = os.path.join(dest, s[0][striplen:])
899 899 if os.path.exists(t):
900 900 score += 1
901 901 return score
902 902
903 903 striplen = len(abspfx)
904 904 if striplen:
905 905 striplen += len(os.sep)
906 906 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
907 907 score = evalpath(striplen)
908 908 striplen1 = len(os.path.split(abspfx)[0])
909 909 if striplen1:
910 910 striplen1 += len(os.sep)
911 911 if evalpath(striplen1) > score:
912 912 striplen = striplen1
913 913 res = lambda p: os.path.join(dest, p[striplen:])
914 914 else:
915 915 # a file
916 916 if destdirexists:
917 917 res = lambda p: os.path.join(dest, os.path.basename(p))
918 918 else:
919 919 res = lambda p: dest
920 920 return res
921 921
922 922
923 923 pats = list(pats)
924 924 if not pats:
925 925 raise util.Abort(_('no source or destination specified'))
926 926 if len(pats) == 1:
927 927 raise util.Abort(_('no destination specified'))
928 928 dest = pats.pop()
929 929 destdirexists = os.path.isdir(dest)
930 930 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
931 931 raise util.Abort(_('with multiple sources, destination must be an '
932 932 'existing directory'))
933 933 if opts['after']:
934 934 tfn = targetpathafterfn
935 935 else:
936 936 tfn = targetpathfn
937 937 copylist = []
938 938 for pat in pats:
939 939 srcs = []
940 940 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
941 941 if okaytocopy(abssrc, relsrc, exact):
942 942 srcs.append((abssrc, relsrc, exact))
943 943 if not srcs:
944 944 continue
945 945 copylist.append((tfn(pat, dest, srcs), srcs))
946 946 if not copylist:
947 947 raise util.Abort(_('no files to copy'))
948 948
949 949 for targetpath, srcs in copylist:
950 950 for abssrc, relsrc, exact in srcs:
951 951 copy(abssrc, relsrc, targetpath(abssrc), exact)
952 952
953 953 if errors:
954 954 ui.warn(_('(consider using --after)\n'))
955 955 return errors, copied
956 956
957 957 def copy(ui, repo, *pats, **opts):
958 958 """mark files as copied for the next commit
959 959
960 960 Mark dest as having copies of source files. If dest is a
961 961 directory, copies are put in that directory. If dest is a file,
962 962 there can only be one source.
963 963
964 964 By default, this command copies the contents of files as they
965 965 stand in the working directory. If invoked with --after, the
966 966 operation is recorded, but no copying is performed.
967 967
968 968 This command takes effect in the next commit.
969 969
970 970 NOTE: This command should be treated as experimental. While it
971 971 should properly record copied files, this information is not yet
972 972 fully used by merge, nor fully reported by log.
973 973 """
974 974 errs, copied = docopy(ui, repo, pats, opts)
975 975 return errs
976 976
977 977 def debugancestor(ui, index, rev1, rev2):
978 978 """find the ancestor revision of two revisions in a given index"""
979 979 r = revlog.revlog(util.opener(os.getcwd()), index, "")
980 980 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
981 981 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
982 982
983 983 def debugcheckstate(ui, repo):
984 984 """validate the correctness of the current dirstate"""
985 985 parent1, parent2 = repo.dirstate.parents()
986 986 repo.dirstate.read()
987 987 dc = repo.dirstate.map
988 988 keys = dc.keys()
989 989 keys.sort()
990 990 m1n = repo.changelog.read(parent1)[0]
991 991 m2n = repo.changelog.read(parent2)[0]
992 992 m1 = repo.manifest.read(m1n)
993 993 m2 = repo.manifest.read(m2n)
994 994 errors = 0
995 995 for f in dc:
996 996 state = repo.dirstate.state(f)
997 997 if state in "nr" and f not in m1:
998 998 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
999 999 errors += 1
1000 1000 if state in "a" and f in m1:
1001 1001 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1002 1002 errors += 1
1003 1003 if state in "m" and f not in m1 and f not in m2:
1004 1004 ui.warn(_("%s in state %s, but not in either manifest\n") %
1005 1005 (f, state))
1006 1006 errors += 1
1007 1007 for f in m1:
1008 1008 state = repo.dirstate.state(f)
1009 1009 if state not in "nrm":
1010 1010 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1011 1011 errors += 1
1012 1012 if errors:
1013 1013 error = _(".hg/dirstate inconsistent with current parent's manifest")
1014 1014 raise util.Abort(error)
1015 1015
1016 1016 def debugconfig(ui):
1017 1017 """show combined config settings from all hgrc files"""
1018 1018 try:
1019 1019 repo = hg.repository(ui)
1020 1020 except hg.RepoError:
1021 1021 pass
1022 1022 for section, name, value in ui.walkconfig():
1023 1023 ui.write('%s.%s=%s\n' % (section, name, value))
1024 1024
1025 1025 def debugsetparents(ui, repo, rev1, rev2=None):
1026 1026 """manually set the parents of the current working directory
1027 1027
1028 1028 This is useful for writing repository conversion tools, but should
1029 1029 be used with care.
1030 1030 """
1031 1031
1032 1032 if not rev2:
1033 1033 rev2 = hex(nullid)
1034 1034
1035 1035 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1036 1036
1037 1037 def debugstate(ui, repo):
1038 1038 """show the contents of the current dirstate"""
1039 1039 repo.dirstate.read()
1040 1040 dc = repo.dirstate.map
1041 1041 keys = dc.keys()
1042 1042 keys.sort()
1043 1043 for file_ in keys:
1044 1044 ui.write("%c %3o %10d %s %s\n"
1045 1045 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1046 1046 time.strftime("%x %X",
1047 1047 time.localtime(dc[file_][3])), file_))
1048 1048 for f in repo.dirstate.copies:
1049 1049 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1050 1050
1051 1051 def debugdata(ui, file_, rev):
1052 1052 """dump the contents of an data file revision"""
1053 1053 r = revlog.revlog(util.opener(os.getcwd()), file_[:-2] + ".i", file_)
1054 1054 try:
1055 1055 ui.write(r.revision(r.lookup(rev)))
1056 1056 except KeyError:
1057 1057 raise util.Abort(_('invalid revision identifier %s'), rev)
1058 1058
1059 1059 def debugindex(ui, file_):
1060 1060 """dump the contents of an index file"""
1061 1061 r = revlog.revlog(util.opener(os.getcwd()), file_, "")
1062 1062 ui.write(" rev offset length base linkrev" +
1063 1063 " nodeid p1 p2\n")
1064 1064 for i in range(r.count()):
1065 1065 e = r.index[i]
1066 1066 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1067 1067 i, e[0], e[1], e[2], e[3],
1068 1068 short(e[6]), short(e[4]), short(e[5])))
1069 1069
1070 1070 def debugindexdot(ui, file_):
1071 1071 """dump an index DAG as a .dot file"""
1072 1072 r = revlog.revlog(util.opener(os.getcwd()), file_, "")
1073 1073 ui.write("digraph G {\n")
1074 1074 for i in range(r.count()):
1075 1075 e = r.index[i]
1076 1076 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
1077 1077 if e[5] != nullid:
1078 1078 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
1079 1079 ui.write("}\n")
1080 1080
1081 1081 def debugrename(ui, repo, file, rev=None):
1082 1082 """dump rename information"""
1083 1083 r = repo.file(relpath(repo, [file])[0])
1084 1084 if rev:
1085 1085 try:
1086 1086 # assume all revision numbers are for changesets
1087 1087 n = repo.lookup(rev)
1088 1088 change = repo.changelog.read(n)
1089 1089 m = repo.manifest.read(change[0])
1090 1090 n = m[relpath(repo, [file])[0]]
1091 1091 except (hg.RepoError, KeyError):
1092 1092 n = r.lookup(rev)
1093 1093 else:
1094 1094 n = r.tip()
1095 1095 m = r.renamed(n)
1096 1096 if m:
1097 1097 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1098 1098 else:
1099 1099 ui.write(_("not renamed\n"))
1100 1100
1101 1101 def debugwalk(ui, repo, *pats, **opts):
1102 1102 """show how files match on given patterns"""
1103 1103 items = list(walk(repo, pats, opts))
1104 1104 if not items:
1105 1105 return
1106 1106 fmt = '%%s %%-%ds %%-%ds %%s' % (
1107 1107 max([len(abs) for (src, abs, rel, exact) in items]),
1108 1108 max([len(rel) for (src, abs, rel, exact) in items]))
1109 1109 for src, abs, rel, exact in items:
1110 1110 line = fmt % (src, abs, rel, exact and 'exact' or '')
1111 1111 ui.write("%s\n" % line.rstrip())
1112 1112
1113 1113 def diff(ui, repo, *pats, **opts):
1114 1114 """diff repository (or selected files)
1115 1115
1116 1116 Show differences between revisions for the specified files.
1117 1117
1118 1118 Differences between files are shown using the unified diff format.
1119 1119
1120 1120 When two revision arguments are given, then changes are shown
1121 1121 between those revisions. If only one revision is specified then
1122 1122 that revision is compared to the working directory, and, when no
1123 1123 revisions are specified, the working directory files are compared
1124 1124 to its parent.
1125 1125
1126 1126 Without the -a option, diff will avoid generating diffs of files
1127 1127 it detects as binary. With -a, diff will generate a diff anyway,
1128 1128 probably with undesirable results.
1129 1129 """
1130 1130 node1, node2 = None, None
1131 1131 revs = [repo.lookup(x) for x in opts['rev']]
1132 1132
1133 1133 if len(revs) > 0:
1134 1134 node1 = revs[0]
1135 1135 if len(revs) > 1:
1136 1136 node2 = revs[1]
1137 1137 if len(revs) > 2:
1138 1138 raise util.Abort(_("too many revisions to diff"))
1139 1139
1140 1140 fns, matchfn, anypats = matchpats(repo, pats, opts)
1141 1141
1142 1142 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1143 text=opts['text'])
1143 text=opts['text'], opts=opts)
1144 1144
1145 1145 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1146 1146 node = repo.lookup(changeset)
1147 1147 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1148 1148 if opts['switch_parent']:
1149 1149 parents.reverse()
1150 1150 prev = (parents and parents[0]) or nullid
1151 1151 change = repo.changelog.read(node)
1152 1152
1153 1153 fp = make_file(repo, repo.changelog, opts['output'],
1154 1154 node=node, total=total, seqno=seqno,
1155 1155 revwidth=revwidth)
1156 1156 if fp != sys.stdout:
1157 1157 ui.note("%s\n" % fp.name)
1158 1158
1159 1159 fp.write("# HG changeset patch\n")
1160 1160 fp.write("# User %s\n" % change[1])
1161 1161 fp.write("# Node ID %s\n" % hex(node))
1162 1162 fp.write("# Parent %s\n" % hex(prev))
1163 1163 if len(parents) > 1:
1164 1164 fp.write("# Parent %s\n" % hex(parents[1]))
1165 1165 fp.write(change[4].rstrip())
1166 1166 fp.write("\n\n")
1167 1167
1168 1168 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1169 1169 if fp != sys.stdout:
1170 1170 fp.close()
1171 1171
1172 1172 def export(ui, repo, *changesets, **opts):
1173 1173 """dump the header and diffs for one or more changesets
1174 1174
1175 1175 Print the changeset header and diffs for one or more revisions.
1176 1176
1177 1177 The information shown in the changeset header is: author,
1178 1178 changeset hash, parent and commit comment.
1179 1179
1180 1180 Output may be to a file, in which case the name of the file is
1181 1181 given using a format string. The formatting rules are as follows:
1182 1182
1183 1183 %% literal "%" character
1184 1184 %H changeset hash (40 bytes of hexadecimal)
1185 1185 %N number of patches being generated
1186 1186 %R changeset revision number
1187 1187 %b basename of the exporting repository
1188 1188 %h short-form changeset hash (12 bytes of hexadecimal)
1189 1189 %n zero-padded sequence number, starting at 1
1190 1190 %r zero-padded changeset revision number
1191 1191
1192 1192 Without the -a option, export will avoid generating diffs of files
1193 1193 it detects as binary. With -a, export will generate a diff anyway,
1194 1194 probably with undesirable results.
1195 1195
1196 1196 With the --switch-parent option, the diff will be against the second
1197 1197 parent. It can be useful to review a merge.
1198 1198 """
1199 1199 if not changesets:
1200 1200 raise util.Abort(_("export requires at least one changeset"))
1201 1201 seqno = 0
1202 1202 revs = list(revrange(ui, repo, changesets))
1203 1203 total = len(revs)
1204 1204 revwidth = max(map(len, revs))
1205 1205 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1206 1206 ui.note(msg)
1207 1207 for cset in revs:
1208 1208 seqno += 1
1209 1209 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1210 1210
1211 1211 def forget(ui, repo, *pats, **opts):
1212 1212 """don't add the specified files on the next commit
1213 1213
1214 1214 Undo an 'hg add' scheduled for the next commit.
1215 1215 """
1216 1216 forget = []
1217 1217 for src, abs, rel, exact in walk(repo, pats, opts):
1218 1218 if repo.dirstate.state(abs) == 'a':
1219 1219 forget.append(abs)
1220 1220 if ui.verbose or not exact:
1221 1221 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1222 1222 repo.forget(forget)
1223 1223
1224 1224 def grep(ui, repo, pattern, *pats, **opts):
1225 1225 """search for a pattern in specified files and revisions
1226 1226
1227 1227 Search revisions of files for a regular expression.
1228 1228
1229 1229 This command behaves differently than Unix grep. It only accepts
1230 1230 Python/Perl regexps. It searches repository history, not the
1231 1231 working directory. It always prints the revision number in which
1232 1232 a match appears.
1233 1233
1234 1234 By default, grep only prints output for the first revision of a
1235 1235 file in which it finds a match. To get it to print every revision
1236 1236 that contains a change in match status ("-" for a match that
1237 1237 becomes a non-match, or "+" for a non-match that becomes a match),
1238 1238 use the --all flag.
1239 1239 """
1240 1240 reflags = 0
1241 1241 if opts['ignore_case']:
1242 1242 reflags |= re.I
1243 1243 regexp = re.compile(pattern, reflags)
1244 1244 sep, eol = ':', '\n'
1245 1245 if opts['print0']:
1246 1246 sep = eol = '\0'
1247 1247
1248 1248 fcache = {}
1249 1249 def getfile(fn):
1250 1250 if fn not in fcache:
1251 1251 fcache[fn] = repo.file(fn)
1252 1252 return fcache[fn]
1253 1253
1254 1254 def matchlines(body):
1255 1255 begin = 0
1256 1256 linenum = 0
1257 1257 while True:
1258 1258 match = regexp.search(body, begin)
1259 1259 if not match:
1260 1260 break
1261 1261 mstart, mend = match.span()
1262 1262 linenum += body.count('\n', begin, mstart) + 1
1263 1263 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1264 1264 lend = body.find('\n', mend)
1265 1265 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1266 1266 begin = lend + 1
1267 1267
1268 1268 class linestate(object):
1269 1269 def __init__(self, line, linenum, colstart, colend):
1270 1270 self.line = line
1271 1271 self.linenum = linenum
1272 1272 self.colstart = colstart
1273 1273 self.colend = colend
1274 1274 def __eq__(self, other):
1275 1275 return self.line == other.line
1276 1276 def __hash__(self):
1277 1277 return hash(self.line)
1278 1278
1279 1279 matches = {}
1280 1280 def grepbody(fn, rev, body):
1281 1281 matches[rev].setdefault(fn, {})
1282 1282 m = matches[rev][fn]
1283 1283 for lnum, cstart, cend, line in matchlines(body):
1284 1284 s = linestate(line, lnum, cstart, cend)
1285 1285 m[s] = s
1286 1286
1287 1287 prev = {}
1288 1288 ucache = {}
1289 1289 def display(fn, rev, states, prevstates):
1290 1290 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1291 1291 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1292 1292 counts = {'-': 0, '+': 0}
1293 1293 filerevmatches = {}
1294 1294 for l in diff:
1295 1295 if incrementing or not opts['all']:
1296 1296 change = ((l in prevstates) and '-') or '+'
1297 1297 r = rev
1298 1298 else:
1299 1299 change = ((l in states) and '-') or '+'
1300 1300 r = prev[fn]
1301 1301 cols = [fn, str(rev)]
1302 1302 if opts['line_number']:
1303 1303 cols.append(str(l.linenum))
1304 1304 if opts['all']:
1305 1305 cols.append(change)
1306 1306 if opts['user']:
1307 1307 cols.append(trimuser(ui, getchange(rev)[1], rev,
1308 1308 ucache))
1309 1309 if opts['files_with_matches']:
1310 1310 c = (fn, rev)
1311 1311 if c in filerevmatches:
1312 1312 continue
1313 1313 filerevmatches[c] = 1
1314 1314 else:
1315 1315 cols.append(l.line)
1316 1316 ui.write(sep.join(cols), eol)
1317 1317 counts[change] += 1
1318 1318 return counts['+'], counts['-']
1319 1319
1320 1320 fstate = {}
1321 1321 skip = {}
1322 1322 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1323 1323 count = 0
1324 1324 incrementing = False
1325 1325 for st, rev, fns in changeiter:
1326 1326 if st == 'window':
1327 1327 incrementing = rev
1328 1328 matches.clear()
1329 1329 elif st == 'add':
1330 1330 change = repo.changelog.read(repo.lookup(str(rev)))
1331 1331 mf = repo.manifest.read(change[0])
1332 1332 matches[rev] = {}
1333 1333 for fn in fns:
1334 1334 if fn in skip:
1335 1335 continue
1336 1336 fstate.setdefault(fn, {})
1337 1337 try:
1338 1338 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1339 1339 except KeyError:
1340 1340 pass
1341 1341 elif st == 'iter':
1342 1342 states = matches[rev].items()
1343 1343 states.sort()
1344 1344 for fn, m in states:
1345 1345 if fn in skip:
1346 1346 continue
1347 1347 if incrementing or not opts['all'] or fstate[fn]:
1348 1348 pos, neg = display(fn, rev, m, fstate[fn])
1349 1349 count += pos + neg
1350 1350 if pos and not opts['all']:
1351 1351 skip[fn] = True
1352 1352 fstate[fn] = m
1353 1353 prev[fn] = rev
1354 1354
1355 1355 if not incrementing:
1356 1356 fstate = fstate.items()
1357 1357 fstate.sort()
1358 1358 for fn, state in fstate:
1359 1359 if fn in skip:
1360 1360 continue
1361 1361 display(fn, rev, {}, state)
1362 1362 return (count == 0 and 1) or 0
1363 1363
1364 1364 def heads(ui, repo, **opts):
1365 1365 """show current repository heads
1366 1366
1367 1367 Show all repository head changesets.
1368 1368
1369 1369 Repository "heads" are changesets that don't have children
1370 1370 changesets. They are where development generally takes place and
1371 1371 are the usual targets for update and merge operations.
1372 1372 """
1373 1373 if opts['rev']:
1374 1374 heads = repo.heads(repo.lookup(opts['rev']))
1375 1375 else:
1376 1376 heads = repo.heads()
1377 1377 br = None
1378 1378 if opts['branches']:
1379 1379 br = repo.branchlookup(heads)
1380 1380 for n in heads:
1381 1381 show_changeset(ui, repo, changenode=n, brinfo=br)
1382 1382
1383 1383 def identify(ui, repo):
1384 1384 """print information about the working copy
1385 1385
1386 1386 Print a short summary of the current state of the repo.
1387 1387
1388 1388 This summary identifies the repository state using one or two parent
1389 1389 hash identifiers, followed by a "+" if there are uncommitted changes
1390 1390 in the working directory, followed by a list of tags for this revision.
1391 1391 """
1392 1392 parents = [p for p in repo.dirstate.parents() if p != nullid]
1393 1393 if not parents:
1394 1394 ui.write(_("unknown\n"))
1395 1395 return
1396 1396
1397 1397 hexfunc = ui.verbose and hex or short
1398 1398 modified, added, removed, deleted, unknown = repo.changes()
1399 1399 output = ["%s%s" %
1400 1400 ('+'.join([hexfunc(parent) for parent in parents]),
1401 1401 (modified or added or removed or deleted) and "+" or "")]
1402 1402
1403 1403 if not ui.quiet:
1404 1404 # multiple tags for a single parent separated by '/'
1405 1405 parenttags = ['/'.join(tags)
1406 1406 for tags in map(repo.nodetags, parents) if tags]
1407 1407 # tags for multiple parents separated by ' + '
1408 1408 if parenttags:
1409 1409 output.append(' + '.join(parenttags))
1410 1410
1411 1411 ui.write("%s\n" % ' '.join(output))
1412 1412
1413 1413 def import_(ui, repo, patch1, *patches, **opts):
1414 1414 """import an ordered set of patches
1415 1415
1416 1416 Import a list of patches and commit them individually.
1417 1417
1418 1418 If there are outstanding changes in the working directory, import
1419 1419 will abort unless given the -f flag.
1420 1420
1421 1421 If a patch looks like a mail message (its first line starts with
1422 1422 "From " or looks like an RFC822 header), it will not be applied
1423 1423 unless the -f option is used. The importer neither parses nor
1424 1424 discards mail headers, so use -f only to override the "mailness"
1425 1425 safety check, not to import a real mail message.
1426 1426 """
1427 1427 patches = (patch1,) + patches
1428 1428
1429 1429 if not opts['force']:
1430 1430 modified, added, removed, deleted, unknown = repo.changes()
1431 1431 if modified or added or removed or deleted:
1432 1432 raise util.Abort(_("outstanding uncommitted changes"))
1433 1433
1434 1434 d = opts["base"]
1435 1435 strip = opts["strip"]
1436 1436
1437 1437 mailre = re.compile(r'(?:From |[\w-]+:)')
1438 1438
1439 1439 # attempt to detect the start of a patch
1440 1440 # (this heuristic is borrowed from quilt)
1441 1441 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1442 1442 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1443 1443 '(---|\*\*\*)[ \t])')
1444 1444
1445 1445 for patch in patches:
1446 1446 ui.status(_("applying %s\n") % patch)
1447 1447 pf = os.path.join(d, patch)
1448 1448
1449 1449 message = []
1450 1450 user = None
1451 1451 hgpatch = False
1452 1452 for line in file(pf):
1453 1453 line = line.rstrip()
1454 1454 if (not message and not hgpatch and
1455 1455 mailre.match(line) and not opts['force']):
1456 1456 if len(line) > 35:
1457 1457 line = line[:32] + '...'
1458 1458 raise util.Abort(_('first line looks like a '
1459 1459 'mail header: ') + line)
1460 1460 if diffre.match(line):
1461 1461 break
1462 1462 elif hgpatch:
1463 1463 # parse values when importing the result of an hg export
1464 1464 if line.startswith("# User "):
1465 1465 user = line[7:]
1466 1466 ui.debug(_('User: %s\n') % user)
1467 1467 elif not line.startswith("# ") and line:
1468 1468 message.append(line)
1469 1469 hgpatch = False
1470 1470 elif line == '# HG changeset patch':
1471 1471 hgpatch = True
1472 1472 message = [] # We may have collected garbage
1473 1473 else:
1474 1474 message.append(line)
1475 1475
1476 1476 # make sure message isn't empty
1477 1477 if not message:
1478 1478 message = _("imported patch %s\n") % patch
1479 1479 else:
1480 1480 message = "%s\n" % '\n'.join(message)
1481 1481 ui.debug(_('message:\n%s\n') % message)
1482 1482
1483 1483 files = util.patch(strip, pf, ui)
1484 1484
1485 1485 if len(files) > 0:
1486 1486 addremove(ui, repo, *files)
1487 1487 repo.commit(files, message, user)
1488 1488
1489 1489 def incoming(ui, repo, source="default", **opts):
1490 1490 """show new changesets found in source
1491 1491
1492 1492 Show new changesets found in the specified repo or the default
1493 1493 pull repo. These are the changesets that would be pulled if a pull
1494 1494 was requested.
1495 1495
1496 1496 Currently only local repositories are supported.
1497 1497 """
1498 1498 source = ui.expandpath(source, repo.root)
1499 1499 other = hg.repository(ui, source)
1500 1500 if not other.local():
1501 1501 raise util.Abort(_("incoming doesn't work for remote repositories yet"))
1502 1502 o = repo.findincoming(other)
1503 1503 if not o:
1504 1504 return
1505 1505 o = other.changelog.nodesbetween(o)[0]
1506 1506 if opts['newest_first']:
1507 1507 o.reverse()
1508 1508 for n in o:
1509 1509 parents = [p for p in other.changelog.parents(n) if p != nullid]
1510 1510 if opts['no_merges'] and len(parents) == 2:
1511 1511 continue
1512 1512 show_changeset(ui, other, changenode=n)
1513 1513 if opts['patch']:
1514 1514 prev = (parents and parents[0]) or nullid
1515 1515 dodiff(ui, ui, other, prev, n)
1516 1516 ui.write("\n")
1517 1517
1518 1518 def init(ui, dest="."):
1519 1519 """create a new repository in the given directory
1520 1520
1521 1521 Initialize a new repository in the given directory. If the given
1522 1522 directory does not exist, it is created.
1523 1523
1524 1524 If no directory is given, the current directory is used.
1525 1525 """
1526 1526 if not os.path.exists(dest):
1527 1527 os.mkdir(dest)
1528 1528 hg.repository(ui, dest, create=1)
1529 1529
1530 1530 def locate(ui, repo, *pats, **opts):
1531 1531 """locate files matching specific patterns
1532 1532
1533 1533 Print all files under Mercurial control whose names match the
1534 1534 given patterns.
1535 1535
1536 1536 This command searches the current directory and its
1537 1537 subdirectories. To search an entire repository, move to the root
1538 1538 of the repository.
1539 1539
1540 1540 If no patterns are given to match, this command prints all file
1541 1541 names.
1542 1542
1543 1543 If you want to feed the output of this command into the "xargs"
1544 1544 command, use the "-0" option to both this command and "xargs".
1545 1545 This will avoid the problem of "xargs" treating single filenames
1546 1546 that contain white space as multiple filenames.
1547 1547 """
1548 1548 end = opts['print0'] and '\0' or '\n'
1549 1549 rev = opts['rev']
1550 1550 if rev:
1551 1551 node = repo.lookup(rev)
1552 1552 else:
1553 1553 node = None
1554 1554
1555 1555 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1556 1556 head='(?:.*/|)'):
1557 1557 if not node and repo.dirstate.state(abs) == '?':
1558 1558 continue
1559 1559 if opts['fullpath']:
1560 1560 ui.write(os.path.join(repo.root, abs), end)
1561 1561 else:
1562 1562 ui.write(((pats and rel) or abs), end)
1563 1563
1564 1564 def log(ui, repo, *pats, **opts):
1565 1565 """show revision history of entire repository or files
1566 1566
1567 1567 Print the revision history of the specified files or the entire project.
1568 1568
1569 1569 By default this command outputs: changeset id and hash, tags,
1570 1570 non-trivial parents, user, date and time, and a summary for each
1571 1571 commit. When the -v/--verbose switch is used, the list of changed
1572 1572 files and full commit message is shown.
1573 1573 """
1574 1574 class dui(object):
1575 1575 # Implement and delegate some ui protocol. Save hunks of
1576 1576 # output for later display in the desired order.
1577 1577 def __init__(self, ui):
1578 1578 self.ui = ui
1579 1579 self.hunk = {}
1580 1580 def bump(self, rev):
1581 1581 self.rev = rev
1582 1582 self.hunk[rev] = []
1583 1583 def note(self, *args):
1584 1584 if self.verbose:
1585 1585 self.write(*args)
1586 1586 def status(self, *args):
1587 1587 if not self.quiet:
1588 1588 self.write(*args)
1589 1589 def write(self, *args):
1590 1590 self.hunk[self.rev].append(args)
1591 1591 def debug(self, *args):
1592 1592 if self.debugflag:
1593 1593 self.write(*args)
1594 1594 def __getattr__(self, key):
1595 1595 return getattr(self.ui, key)
1596 1596 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1597 1597 for st, rev, fns in changeiter:
1598 1598 if st == 'window':
1599 1599 du = dui(ui)
1600 1600 elif st == 'add':
1601 1601 du.bump(rev)
1602 1602 changenode = repo.changelog.node(rev)
1603 1603 parents = [p for p in repo.changelog.parents(changenode)
1604 1604 if p != nullid]
1605 1605 if opts['no_merges'] and len(parents) == 2:
1606 1606 continue
1607 1607 if opts['only_merges'] and len(parents) != 2:
1608 1608 continue
1609 1609
1610 1610 br = None
1611 1611 if opts['keyword']:
1612 1612 changes = getchange(rev)
1613 1613 miss = 0
1614 1614 for k in [kw.lower() for kw in opts['keyword']]:
1615 1615 if not (k in changes[1].lower() or
1616 1616 k in changes[4].lower() or
1617 1617 k in " ".join(changes[3][:20]).lower()):
1618 1618 miss = 1
1619 1619 break
1620 1620 if miss:
1621 1621 continue
1622 1622
1623 1623 if opts['branch']:
1624 1624 br = repo.branchlookup([repo.changelog.node(rev)])
1625 1625
1626 1626 show_changeset(du, repo, rev, brinfo=br)
1627 1627 if opts['patch']:
1628 1628 prev = (parents and parents[0]) or nullid
1629 1629 dodiff(du, du, repo, prev, changenode, match=matchfn)
1630 1630 du.write("\n\n")
1631 1631 elif st == 'iter':
1632 1632 for args in du.hunk[rev]:
1633 1633 ui.write(*args)
1634 1634
1635 1635 def manifest(ui, repo, rev=None):
1636 1636 """output the latest or given revision of the project manifest
1637 1637
1638 1638 Print a list of version controlled files for the given revision.
1639 1639
1640 1640 The manifest is the list of files being version controlled. If no revision
1641 1641 is given then the tip is used.
1642 1642 """
1643 1643 if rev:
1644 1644 try:
1645 1645 # assume all revision numbers are for changesets
1646 1646 n = repo.lookup(rev)
1647 1647 change = repo.changelog.read(n)
1648 1648 n = change[0]
1649 1649 except hg.RepoError:
1650 1650 n = repo.manifest.lookup(rev)
1651 1651 else:
1652 1652 n = repo.manifest.tip()
1653 1653 m = repo.manifest.read(n)
1654 1654 mf = repo.manifest.readflags(n)
1655 1655 files = m.keys()
1656 1656 files.sort()
1657 1657
1658 1658 for f in files:
1659 1659 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1660 1660
1661 1661 def outgoing(ui, repo, dest="default-push", **opts):
1662 1662 """show changesets not found in destination
1663 1663
1664 1664 Show changesets not found in the specified destination repo or the
1665 1665 default push repo. These are the changesets that would be pushed
1666 1666 if a push was requested.
1667 1667 """
1668 1668 dest = ui.expandpath(dest, repo.root)
1669 1669 other = hg.repository(ui, dest)
1670 1670 o = repo.findoutgoing(other)
1671 1671 o = repo.changelog.nodesbetween(o)[0]
1672 1672 if opts['newest_first']:
1673 1673 o.reverse()
1674 1674 for n in o:
1675 1675 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1676 1676 if opts['no_merges'] and len(parents) == 2:
1677 1677 continue
1678 1678 show_changeset(ui, repo, changenode=n)
1679 1679 if opts['patch']:
1680 1680 prev = (parents and parents[0]) or nullid
1681 1681 dodiff(ui, ui, repo, prev, n)
1682 1682 ui.write("\n")
1683 1683
1684 1684 def parents(ui, repo, rev=None, branch=None):
1685 1685 """show the parents of the working dir or revision
1686 1686
1687 1687 Print the working directory's parent revisions.
1688 1688 """
1689 1689 if rev:
1690 1690 p = repo.changelog.parents(repo.lookup(rev))
1691 1691 else:
1692 1692 p = repo.dirstate.parents()
1693 1693
1694 1694 br = None
1695 1695 if branch is not None:
1696 1696 br = repo.branchlookup(p)
1697 1697 for n in p:
1698 1698 if n != nullid:
1699 1699 show_changeset(ui, repo, changenode=n, brinfo=br)
1700 1700
1701 1701 def paths(ui, search=None):
1702 1702 """show definition of symbolic path names
1703 1703
1704 1704 Show definition of symbolic path name NAME. If no name is given, show
1705 1705 definition of available names.
1706 1706
1707 1707 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1708 1708 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1709 1709 """
1710 1710 try:
1711 1711 repo = hg.repository(ui=ui)
1712 1712 except hg.RepoError:
1713 1713 pass
1714 1714
1715 1715 if search:
1716 1716 for name, path in ui.configitems("paths"):
1717 1717 if name == search:
1718 1718 ui.write("%s\n" % path)
1719 1719 return
1720 1720 ui.warn(_("not found!\n"))
1721 1721 return 1
1722 1722 else:
1723 1723 for name, path in ui.configitems("paths"):
1724 1724 ui.write("%s = %s\n" % (name, path))
1725 1725
1726 1726 def pull(ui, repo, source="default", **opts):
1727 1727 """pull changes from the specified source
1728 1728
1729 1729 Pull changes from a remote repository to a local one.
1730 1730
1731 1731 This finds all changes from the repository at the specified path
1732 1732 or URL and adds them to the local repository. By default, this
1733 1733 does not update the copy of the project in the working directory.
1734 1734
1735 1735 Valid URLs are of the form:
1736 1736
1737 1737 local/filesystem/path
1738 1738 http://[user@]host[:port][/path]
1739 1739 https://[user@]host[:port][/path]
1740 1740 ssh://[user@]host[:port][/path]
1741 1741
1742 1742 SSH requires an accessible shell account on the destination machine
1743 1743 and a copy of hg in the remote path. With SSH, paths are relative
1744 1744 to the remote user's home directory by default; use two slashes at
1745 1745 the start of a path to specify it as relative to the filesystem root.
1746 1746 """
1747 1747 source = ui.expandpath(source, repo.root)
1748 1748 ui.status(_('pulling from %s\n') % (source))
1749 1749
1750 1750 if opts['ssh']:
1751 1751 ui.setconfig("ui", "ssh", opts['ssh'])
1752 1752 if opts['remotecmd']:
1753 1753 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1754 1754
1755 1755 other = hg.repository(ui, source)
1756 1756 revs = None
1757 1757 if opts['rev'] and not other.local():
1758 1758 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
1759 1759 elif opts['rev']:
1760 1760 revs = [other.lookup(rev) for rev in opts['rev']]
1761 1761 r = repo.pull(other, heads=revs)
1762 1762 if not r:
1763 1763 if opts['update']:
1764 1764 return update(ui, repo)
1765 1765 else:
1766 1766 ui.status(_("(run 'hg update' to get a working copy)\n"))
1767 1767
1768 1768 return r
1769 1769
1770 1770 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1771 1771 """push changes to the specified destination
1772 1772
1773 1773 Push changes from the local repository to the given destination.
1774 1774
1775 1775 This is the symmetrical operation for pull. It helps to move
1776 1776 changes from the current repository to a different one. If the
1777 1777 destination is local this is identical to a pull in that directory
1778 1778 from the current one.
1779 1779
1780 1780 By default, push will refuse to run if it detects the result would
1781 1781 increase the number of remote heads. This generally indicates the
1782 1782 the client has forgotten to sync and merge before pushing.
1783 1783
1784 1784 Valid URLs are of the form:
1785 1785
1786 1786 local/filesystem/path
1787 1787 ssh://[user@]host[:port][/path]
1788 1788
1789 1789 SSH requires an accessible shell account on the destination
1790 1790 machine and a copy of hg in the remote path.
1791 1791 """
1792 1792 dest = ui.expandpath(dest, repo.root)
1793 1793 ui.status('pushing to %s\n' % (dest))
1794 1794
1795 1795 if ssh:
1796 1796 ui.setconfig("ui", "ssh", ssh)
1797 1797 if remotecmd:
1798 1798 ui.setconfig("ui", "remotecmd", remotecmd)
1799 1799
1800 1800 other = hg.repository(ui, dest)
1801 1801 r = repo.push(other, force)
1802 1802 return r
1803 1803
1804 1804 def rawcommit(ui, repo, *flist, **rc):
1805 1805 """raw commit interface (DEPRECATED)
1806 1806
1807 1807 Lowlevel commit, for use in helper scripts.
1808 1808
1809 1809 This command is not intended to be used by normal users, as it is
1810 1810 primarily useful for importing from other SCMs.
1811 1811
1812 1812 This command is now deprecated and will be removed in a future
1813 1813 release, please use debugsetparents and commit instead.
1814 1814 """
1815 1815
1816 1816 ui.warn(_("(the rawcommit command is deprecated)\n"))
1817 1817
1818 1818 message = rc['message']
1819 1819 if not message and rc['logfile']:
1820 1820 try:
1821 1821 message = open(rc['logfile']).read()
1822 1822 except IOError:
1823 1823 pass
1824 1824 if not message and not rc['logfile']:
1825 1825 raise util.Abort(_("missing commit message"))
1826 1826
1827 1827 files = relpath(repo, list(flist))
1828 1828 if rc['files']:
1829 1829 files += open(rc['files']).read().splitlines()
1830 1830
1831 1831 rc['parent'] = map(repo.lookup, rc['parent'])
1832 1832
1833 1833 try:
1834 1834 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1835 1835 except ValueError, inst:
1836 1836 raise util.Abort(str(inst))
1837 1837
1838 1838 def recover(ui, repo):
1839 1839 """roll back an interrupted transaction
1840 1840
1841 1841 Recover from an interrupted commit or pull.
1842 1842
1843 1843 This command tries to fix the repository status after an interrupted
1844 1844 operation. It should only be necessary when Mercurial suggests it.
1845 1845 """
1846 1846 if repo.recover():
1847 1847 return repo.verify()
1848 1848 return False
1849 1849
1850 1850 def remove(ui, repo, pat, *pats, **opts):
1851 1851 """remove the specified files on the next commit
1852 1852
1853 1853 Schedule the indicated files for removal from the repository.
1854 1854
1855 1855 This command schedules the files to be removed at the next commit.
1856 1856 This only removes files from the current branch, not from the
1857 1857 entire project history. If the files still exist in the working
1858 1858 directory, they will be deleted from it.
1859 1859 """
1860 1860 names = []
1861 1861 def okaytoremove(abs, rel, exact):
1862 1862 modified, added, removed, deleted, unknown = repo.changes(files=[abs])
1863 1863 reason = None
1864 1864 if modified:
1865 1865 reason = _('is modified')
1866 1866 elif added:
1867 1867 reason = _('has been marked for add')
1868 1868 elif unknown:
1869 1869 reason = _('is not managed')
1870 1870 if reason:
1871 1871 if exact:
1872 1872 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
1873 1873 else:
1874 1874 return True
1875 1875 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1876 1876 if okaytoremove(abs, rel, exact):
1877 1877 if ui.verbose or not exact:
1878 1878 ui.status(_('removing %s\n') % rel)
1879 1879 names.append(abs)
1880 1880 repo.remove(names, unlink=True)
1881 1881
1882 1882 def rename(ui, repo, *pats, **opts):
1883 1883 """rename files; equivalent of copy + remove
1884 1884
1885 1885 Mark dest as copies of sources; mark sources for deletion. If
1886 1886 dest is a directory, copies are put in that directory. If dest is
1887 1887 a file, there can only be one source.
1888 1888
1889 1889 By default, this command copies the contents of files as they
1890 1890 stand in the working directory. If invoked with --after, the
1891 1891 operation is recorded, but no copying is performed.
1892 1892
1893 1893 This command takes effect in the next commit.
1894 1894
1895 1895 NOTE: This command should be treated as experimental. While it
1896 1896 should properly record rename files, this information is not yet
1897 1897 fully used by merge, nor fully reported by log.
1898 1898 """
1899 1899 errs, copied = docopy(ui, repo, pats, opts)
1900 1900 names = []
1901 1901 for abs, rel, exact in copied:
1902 1902 if ui.verbose or not exact:
1903 1903 ui.status(_('removing %s\n') % rel)
1904 1904 names.append(abs)
1905 1905 repo.remove(names, unlink=True)
1906 1906 return errs
1907 1907
1908 1908 def revert(ui, repo, *pats, **opts):
1909 1909 """revert modified files or dirs back to their unmodified states
1910 1910
1911 1911 Revert any uncommitted modifications made to the named files or
1912 1912 directories. This restores the contents of the affected files to
1913 1913 an unmodified state.
1914 1914
1915 1915 If a file has been deleted, it is recreated. If the executable
1916 1916 mode of a file was changed, it is reset.
1917 1917
1918 1918 If names are given, all files matching the names are reverted.
1919 1919
1920 1920 If no arguments are given, all files in the repository are reverted.
1921 1921 """
1922 1922 node = opts['rev'] and repo.lookup(opts['rev']) or \
1923 1923 repo.dirstate.parents()[0]
1924 1924
1925 1925 files, choose, anypats = matchpats(repo, pats, opts)
1926 1926 modified, added, removed, deleted, unknown = repo.changes(match=choose)
1927 1927 repo.forget(added)
1928 1928 repo.undelete(removed + deleted)
1929 1929
1930 1930 return repo.update(node, False, True, choose, False)
1931 1931
1932 1932 def root(ui, repo):
1933 1933 """print the root (top) of the current working dir
1934 1934
1935 1935 Print the root directory of the current repository.
1936 1936 """
1937 1937 ui.write(repo.root + "\n")
1938 1938
1939 1939 def serve(ui, repo, **opts):
1940 1940 """export the repository via HTTP
1941 1941
1942 1942 Start a local HTTP repository browser and pull server.
1943 1943
1944 1944 By default, the server logs accesses to stdout and errors to
1945 1945 stderr. Use the "-A" and "-E" options to log to files.
1946 1946 """
1947 1947
1948 1948 if opts["stdio"]:
1949 1949 fin, fout = sys.stdin, sys.stdout
1950 1950 sys.stdout = sys.stderr
1951 1951
1952 1952 # Prevent insertion/deletion of CRs
1953 1953 util.set_binary(fin)
1954 1954 util.set_binary(fout)
1955 1955
1956 1956 def getarg():
1957 1957 argline = fin.readline()[:-1]
1958 1958 arg, l = argline.split()
1959 1959 val = fin.read(int(l))
1960 1960 return arg, val
1961 1961 def respond(v):
1962 1962 fout.write("%d\n" % len(v))
1963 1963 fout.write(v)
1964 1964 fout.flush()
1965 1965
1966 1966 lock = None
1967 1967
1968 1968 while 1:
1969 1969 cmd = fin.readline()[:-1]
1970 1970 if cmd == '':
1971 1971 return
1972 1972 if cmd == "heads":
1973 1973 h = repo.heads()
1974 1974 respond(" ".join(map(hex, h)) + "\n")
1975 1975 if cmd == "lock":
1976 1976 lock = repo.lock()
1977 1977 respond("")
1978 1978 if cmd == "unlock":
1979 1979 if lock:
1980 1980 lock.release()
1981 1981 lock = None
1982 1982 respond("")
1983 1983 elif cmd == "branches":
1984 1984 arg, nodes = getarg()
1985 1985 nodes = map(bin, nodes.split(" "))
1986 1986 r = []
1987 1987 for b in repo.branches(nodes):
1988 1988 r.append(" ".join(map(hex, b)) + "\n")
1989 1989 respond("".join(r))
1990 1990 elif cmd == "between":
1991 1991 arg, pairs = getarg()
1992 1992 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1993 1993 r = []
1994 1994 for b in repo.between(pairs):
1995 1995 r.append(" ".join(map(hex, b)) + "\n")
1996 1996 respond("".join(r))
1997 1997 elif cmd == "changegroup":
1998 1998 nodes = []
1999 1999 arg, roots = getarg()
2000 2000 nodes = map(bin, roots.split(" "))
2001 2001
2002 2002 cg = repo.changegroup(nodes)
2003 2003 while 1:
2004 2004 d = cg.read(4096)
2005 2005 if not d:
2006 2006 break
2007 2007 fout.write(d)
2008 2008
2009 2009 fout.flush()
2010 2010
2011 2011 elif cmd == "addchangegroup":
2012 2012 if not lock:
2013 2013 respond("not locked")
2014 2014 continue
2015 2015 respond("")
2016 2016
2017 2017 r = repo.addchangegroup(fin)
2018 2018 respond("")
2019 2019
2020 2020 optlist = "name templates style address port ipv6 accesslog errorlog"
2021 2021 for o in optlist.split():
2022 2022 if opts[o]:
2023 2023 ui.setconfig("web", o, opts[o])
2024 2024
2025 2025 try:
2026 2026 httpd = hgweb.create_server(repo)
2027 2027 except socket.error, inst:
2028 2028 raise util.Abort(_('cannot start server: ') + inst.args[1])
2029 2029
2030 2030 if ui.verbose:
2031 2031 addr, port = httpd.socket.getsockname()
2032 2032 if addr == '0.0.0.0':
2033 2033 addr = socket.gethostname()
2034 2034 else:
2035 2035 try:
2036 2036 addr = socket.gethostbyaddr(addr)[0]
2037 2037 except socket.error:
2038 2038 pass
2039 2039 if port != 80:
2040 2040 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2041 2041 else:
2042 2042 ui.status(_('listening at http://%s/\n') % addr)
2043 2043 httpd.serve_forever()
2044 2044
2045 2045 def status(ui, repo, *pats, **opts):
2046 2046 """show changed files in the working directory
2047 2047
2048 2048 Show changed files in the repository. If names are
2049 2049 given, only files that match are shown.
2050 2050
2051 2051 The codes used to show the status of files are:
2052 2052 M = modified
2053 2053 A = added
2054 2054 R = removed
2055 2055 ! = deleted, but still tracked
2056 2056 ? = not tracked
2057 2057 """
2058 2058
2059 2059 files, matchfn, anypats = matchpats(repo, pats, opts)
2060 2060 cwd = (pats and repo.getcwd()) or ''
2061 2061 modified, added, removed, deleted, unknown = [
2062 2062 [util.pathto(cwd, x) for x in n]
2063 2063 for n in repo.changes(files=files, match=matchfn)]
2064 2064
2065 2065 changetypes = [(_('modified'), 'M', modified),
2066 2066 (_('added'), 'A', added),
2067 2067 (_('removed'), 'R', removed),
2068 2068 (_('deleted'), '!', deleted),
2069 2069 (_('unknown'), '?', unknown)]
2070 2070
2071 2071 end = opts['print0'] and '\0' or '\n'
2072 2072
2073 2073 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2074 2074 or changetypes):
2075 2075 if opts['no_status']:
2076 2076 format = "%%s%s" % end
2077 2077 else:
2078 2078 format = "%s %%s%s" % (char, end);
2079 2079
2080 2080 for f in changes:
2081 2081 ui.write(format % f)
2082 2082
2083 2083 def tag(ui, repo, name, rev_=None, **opts):
2084 2084 """add a tag for the current tip or a given revision
2085 2085
2086 2086 Name a particular revision using <name>.
2087 2087
2088 2088 Tags are used to name particular revisions of the repository and are
2089 2089 very useful to compare different revision, to go back to significant
2090 2090 earlier versions or to mark branch points as releases, etc.
2091 2091
2092 2092 If no revision is given, the tip is used.
2093 2093
2094 2094 To facilitate version control, distribution, and merging of tags,
2095 2095 they are stored as a file named ".hgtags" which is managed
2096 2096 similarly to other project files and can be hand-edited if
2097 2097 necessary. The file '.hg/localtags' is used for local tags (not
2098 2098 shared among repositories).
2099 2099 """
2100 2100 if name == "tip":
2101 2101 raise util.Abort(_("the name 'tip' is reserved"))
2102 2102 if rev_ is not None:
2103 2103 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2104 2104 "please use 'hg tag [-r REV] NAME' instead\n"))
2105 2105 if opts['rev']:
2106 2106 raise util.Abort(_("use only one form to specify the revision"))
2107 2107 if opts['rev']:
2108 2108 rev_ = opts['rev']
2109 2109 if rev_:
2110 2110 r = hex(repo.lookup(rev_))
2111 2111 else:
2112 2112 r = hex(repo.changelog.tip())
2113 2113
2114 2114 disallowed = (revrangesep, '\r', '\n')
2115 2115 for c in disallowed:
2116 2116 if name.find(c) >= 0:
2117 2117 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2118 2118
2119 2119 repo.hook('pretag', throw=True, node=r, tag=name,
2120 2120 local=not not opts['local'])
2121 2121
2122 2122 if opts['local']:
2123 2123 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2124 2124 repo.hook('tag', node=r, tag=name, local=1)
2125 2125 return
2126 2126
2127 2127 for x in repo.changes():
2128 2128 if ".hgtags" in x:
2129 2129 raise util.Abort(_("working copy of .hgtags is changed "
2130 2130 "(please commit .hgtags manually)"))
2131 2131
2132 2132 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2133 2133 if repo.dirstate.state(".hgtags") == '?':
2134 2134 repo.add([".hgtags"])
2135 2135
2136 2136 message = (opts['message'] or
2137 2137 _("Added tag %s for changeset %s") % (name, r))
2138 2138 try:
2139 2139 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2140 2140 repo.hook('tag', node=r, tag=name, local=0)
2141 2141 except ValueError, inst:
2142 2142 raise util.Abort(str(inst))
2143 2143
2144 2144 def tags(ui, repo):
2145 2145 """list repository tags
2146 2146
2147 2147 List the repository tags.
2148 2148
2149 2149 This lists both regular and local tags.
2150 2150 """
2151 2151
2152 2152 l = repo.tagslist()
2153 2153 l.reverse()
2154 2154 for t, n in l:
2155 2155 try:
2156 2156 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2157 2157 except KeyError:
2158 2158 r = " ?:?"
2159 2159 ui.write("%-30s %s\n" % (t, r))
2160 2160
2161 def tip(ui, repo):
2161 def tip(ui, repo, **opts):
2162 2162 """show the tip revision
2163 2163
2164 2164 Show the tip revision.
2165 2165 """
2166 2166 n = repo.changelog.tip()
2167 2167 show_changeset(ui, repo, changenode=n)
2168 if opts['patch']:
2169 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2168 2170
2169 2171 def unbundle(ui, repo, fname, **opts):
2170 2172 """apply a changegroup file
2171 2173
2172 2174 Apply a compressed changegroup file generated by the bundle
2173 2175 command.
2174 2176 """
2175 2177 f = urllib.urlopen(fname)
2176 2178
2177 2179 if f.read(4) != "HG10":
2178 2180 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2179 2181
2180 2182 def bzgenerator(f):
2181 2183 zd = bz2.BZ2Decompressor()
2182 2184 for chunk in f:
2183 2185 yield zd.decompress(chunk)
2184 2186
2185 2187 bzgen = bzgenerator(util.filechunkiter(f, 4096))
2186 2188 if repo.addchangegroup(util.chunkbuffer(bzgen)):
2187 2189 return 1
2188 2190
2189 2191 if opts['update']:
2190 2192 return update(ui, repo)
2191 2193 else:
2192 2194 ui.status(_("(run 'hg update' to get a working copy)\n"))
2193 2195
2194 2196 def undo(ui, repo):
2195 2197 """undo the last commit or pull
2196 2198
2197 2199 Roll back the last pull or commit transaction on the
2198 2200 repository, restoring the project to its earlier state.
2199 2201
2200 2202 This command should be used with care. There is only one level of
2201 2203 undo and there is no redo.
2202 2204
2203 2205 This command is not intended for use on public repositories. Once
2204 2206 a change is visible for pull by other users, undoing it locally is
2205 2207 ineffective.
2206 2208 """
2207 2209 repo.undo()
2208 2210
2209 2211 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2210 2212 branch=None):
2211 2213 """update or merge working directory
2212 2214
2213 2215 Update the working directory to the specified revision.
2214 2216
2215 2217 If there are no outstanding changes in the working directory and
2216 2218 there is a linear relationship between the current version and the
2217 2219 requested version, the result is the requested version.
2218 2220
2219 2221 Otherwise the result is a merge between the contents of the
2220 2222 current working directory and the requested version. Files that
2221 2223 changed between either parent are marked as changed for the next
2222 2224 commit and a commit must be performed before any further updates
2223 2225 are allowed.
2224 2226
2225 2227 By default, update will refuse to run if doing so would require
2226 2228 merging or discarding local changes.
2227 2229 """
2228 2230 if branch:
2229 2231 br = repo.branchlookup(branch=branch)
2230 2232 found = []
2231 2233 for x in br:
2232 2234 if branch in br[x]:
2233 2235 found.append(x)
2234 2236 if len(found) > 1:
2235 2237 ui.warn(_("Found multiple heads for %s\n") % branch)
2236 2238 for x in found:
2237 2239 show_changeset(ui, repo, changenode=x, brinfo=br)
2238 2240 return 1
2239 2241 if len(found) == 1:
2240 2242 node = found[0]
2241 2243 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2242 2244 else:
2243 2245 ui.warn(_("branch %s not found\n") % (branch))
2244 2246 return 1
2245 2247 else:
2246 2248 node = node and repo.lookup(node) or repo.changelog.tip()
2247 2249 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2248 2250
2249 2251 def verify(ui, repo):
2250 2252 """verify the integrity of the repository
2251 2253
2252 2254 Verify the integrity of the current repository.
2253 2255
2254 2256 This will perform an extensive check of the repository's
2255 2257 integrity, validating the hashes and checksums of each entry in
2256 2258 the changelog, manifest, and tracked files, as well as the
2257 2259 integrity of their crosslinks and indices.
2258 2260 """
2259 2261 return repo.verify()
2260 2262
2261 2263 # Command options and aliases are listed here, alphabetically
2262 2264
2263 2265 table = {
2264 2266 "^add":
2265 2267 (add,
2266 2268 [('I', 'include', [], _('include names matching the given patterns')),
2267 2269 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2268 2270 _('hg add [OPTION]... [FILE]...')),
2269 2271 "addremove":
2270 2272 (addremove,
2271 2273 [('I', 'include', [], _('include names matching the given patterns')),
2272 2274 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2273 2275 _('hg addremove [OPTION]... [FILE]...')),
2274 2276 "^annotate":
2275 2277 (annotate,
2276 2278 [('r', 'rev', '', _('annotate the specified revision')),
2277 2279 ('a', 'text', None, _('treat all files as text')),
2278 2280 ('u', 'user', None, _('list the author')),
2279 2281 ('d', 'date', None, _('list the date')),
2280 2282 ('n', 'number', None, _('list the revision number (default)')),
2281 2283 ('c', 'changeset', None, _('list the changeset')),
2282 2284 ('I', 'include', [], _('include names matching the given patterns')),
2283 2285 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2284 2286 _('hg annotate [OPTION]... FILE...')),
2285 2287 "bundle":
2286 2288 (bundle,
2287 2289 [],
2288 2290 _('hg bundle FILE DEST')),
2289 2291 "cat":
2290 2292 (cat,
2291 2293 [('I', 'include', [], _('include names matching the given patterns')),
2292 2294 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2293 2295 ('o', 'output', '', _('print output to file with formatted name')),
2294 2296 ('r', 'rev', '', _('print the given revision'))],
2295 2297 _('hg cat [OPTION]... FILE...')),
2296 2298 "^clone":
2297 2299 (clone,
2298 2300 [('U', 'noupdate', None, _('do not update the new working directory')),
2299 2301 ('e', 'ssh', '', _('specify ssh command to use')),
2300 2302 ('', 'pull', None, _('use pull protocol to copy metadata')),
2301 2303 ('r', 'rev', [],
2302 2304 _('a changeset you would like to have after cloning')),
2303 2305 ('', 'remotecmd', '',
2304 2306 _('specify hg command to run on the remote side'))],
2305 2307 _('hg clone [OPTION]... SOURCE [DEST]')),
2306 2308 "^commit|ci":
2307 2309 (commit,
2308 2310 [('A', 'addremove', None, _('run addremove during commit')),
2309 2311 ('I', 'include', [], _('include names matching the given patterns')),
2310 2312 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2311 2313 ('m', 'message', '', _('use <text> as commit message')),
2312 2314 ('l', 'logfile', '', _('read the commit message from <file>')),
2313 2315 ('d', 'date', '', _('record datecode as commit date')),
2314 2316 ('u', 'user', '', _('record user as commiter'))],
2315 2317 _('hg commit [OPTION]... [FILE]...')),
2316 2318 "copy|cp":
2317 2319 (copy,
2318 2320 [('I', 'include', [], _('include names matching the given patterns')),
2319 2321 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2320 2322 ('A', 'after', None, _('record a copy that has already occurred')),
2321 2323 ('f', 'force', None,
2322 2324 _('forcibly copy over an existing managed file'))],
2323 2325 _('hg copy [OPTION]... [SOURCE]... DEST')),
2324 2326 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2325 2327 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2326 2328 "debugconfig": (debugconfig, [], _('debugconfig')),
2327 2329 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2328 2330 "debugstate": (debugstate, [], _('debugstate')),
2329 2331 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2330 2332 "debugindex": (debugindex, [], _('debugindex FILE')),
2331 2333 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2332 2334 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2333 2335 "debugwalk":
2334 2336 (debugwalk,
2335 2337 [('I', 'include', [], _('include names matching the given patterns')),
2336 2338 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2337 2339 _('debugwalk [OPTION]... [FILE]...')),
2338 2340 "^diff":
2339 2341 (diff,
2340 2342 [('r', 'rev', [], _('revision')),
2341 2343 ('a', 'text', None, _('treat all files as text')),
2342 2344 ('I', 'include', [], _('include names matching the given patterns')),
2343 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2345 ('p', 'show-function', None,
2346 _('show which function each change is in')),
2347 ('w', 'ignore-all-space', None,
2348 _('ignore white space when comparing lines')),
2349 ('X', 'exclude', [],
2350 _('exclude names matching the given patterns'))],
2344 2351 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2345 2352 "^export":
2346 2353 (export,
2347 2354 [('o', 'output', '', _('print output to file with formatted name')),
2348 2355 ('a', 'text', None, _('treat all files as text')),
2349 2356 ('', 'switch-parent', None, _('diff against the second parent'))],
2350 2357 _('hg export [-a] [-o OUTFILE] REV...')),
2351 2358 "forget":
2352 2359 (forget,
2353 2360 [('I', 'include', [], _('include names matching the given patterns')),
2354 2361 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2355 2362 _('hg forget [OPTION]... FILE...')),
2356 2363 "grep":
2357 2364 (grep,
2358 2365 [('0', 'print0', None, _('end fields with NUL')),
2359 2366 ('I', 'include', [], _('include names matching the given patterns')),
2360 2367 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2361 2368 ('', 'all', None, _('print all revisions that match')),
2362 2369 ('i', 'ignore-case', None, _('ignore case when matching')),
2363 2370 ('l', 'files-with-matches', None,
2364 2371 _('print only filenames and revs that match')),
2365 2372 ('n', 'line-number', None, _('print matching line numbers')),
2366 2373 ('r', 'rev', [], _('search in given revision range')),
2367 2374 ('u', 'user', None, _('print user who committed change'))],
2368 2375 _('hg grep [OPTION]... PATTERN [FILE]...')),
2369 2376 "heads":
2370 2377 (heads,
2371 2378 [('b', 'branches', None, _('find branch info')),
2372 2379 ('r', 'rev', '', _('show only heads which are descendants of rev'))],
2373 2380 _('hg heads [-b] [-r <rev>]')),
2374 2381 "help": (help_, [], _('hg help [COMMAND]')),
2375 2382 "identify|id": (identify, [], _('hg identify')),
2376 2383 "import|patch":
2377 2384 (import_,
2378 2385 [('p', 'strip', 1,
2379 2386 _('directory strip option for patch. This has the same\n') +
2380 2387 _('meaning as the corresponding patch option')),
2381 2388 ('f', 'force', None,
2382 2389 _('skip check for outstanding uncommitted changes')),
2383 2390 ('b', 'base', '', _('base path'))],
2384 2391 _('hg import [-f] [-p NUM] [-b BASE] PATCH...')),
2385 2392 "incoming|in": (incoming,
2386 2393 [('M', 'no-merges', None, _('do not show merges')),
2387 2394 ('p', 'patch', None, _('show patch')),
2388 2395 ('n', 'newest-first', None, _('show newest record first'))],
2389 2396 _('hg incoming [-p] [-n] [-M] [SOURCE]')),
2390 2397 "^init": (init, [], _('hg init [DEST]')),
2391 2398 "locate":
2392 2399 (locate,
2393 2400 [('r', 'rev', '', _('search the repository as it stood at rev')),
2394 2401 ('0', 'print0', None,
2395 2402 _('end filenames with NUL, for use with xargs')),
2396 2403 ('f', 'fullpath', None,
2397 2404 _('print complete paths from the filesystem root')),
2398 2405 ('I', 'include', [], _('include names matching the given patterns')),
2399 2406 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2400 2407 _('hg locate [OPTION]... [PATTERN]...')),
2401 2408 "^log|history":
2402 2409 (log,
2403 2410 [('I', 'include', [], _('include names matching the given patterns')),
2404 2411 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2405 2412 ('b', 'branch', None, _('show branches')),
2406 2413 ('k', 'keyword', [], _('search for a keyword')),
2407 2414 ('r', 'rev', [], _('show the specified revision or range')),
2408 2415 ('M', 'no-merges', None, _('do not show merges')),
2409 2416 ('m', 'only-merges', None, _('show only merges')),
2410 2417 ('p', 'patch', None, _('show patch'))],
2411 2418 _('hg log [-I] [-X] [-r REV]... [-p] [FILE]')),
2412 2419 "manifest": (manifest, [], _('hg manifest [REV]')),
2413 2420 "outgoing|out": (outgoing,
2414 2421 [('M', 'no-merges', None, _('do not show merges')),
2415 2422 ('p', 'patch', None, _('show patch')),
2416 2423 ('n', 'newest-first', None, _('show newest record first'))],
2417 2424 _('hg outgoing [-p] [-n] [-M] [DEST]')),
2418 2425 "^parents":
2419 2426 (parents,
2420 2427 [('b', 'branch', None, _('show branches'))],
2421 2428 _('hg parents [-b] [REV]')),
2422 2429 "paths": (paths, [], _('hg paths [NAME]')),
2423 2430 "^pull":
2424 2431 (pull,
2425 2432 [('u', 'update', None,
2426 2433 _('update the working directory to tip after pull')),
2427 2434 ('e', 'ssh', '', _('specify ssh command to use')),
2428 2435 ('r', 'rev', [], _('a specific revision you would like to pull')),
2429 2436 ('', 'remotecmd', '',
2430 2437 _('specify hg command to run on the remote side'))],
2431 2438 _('hg pull [-u] [-e FILE] [-r rev] [--remotecmd FILE] [SOURCE]')),
2432 2439 "^push":
2433 2440 (push,
2434 2441 [('f', 'force', None, _('force push')),
2435 2442 ('e', 'ssh', '', _('specify ssh command to use')),
2436 2443 ('', 'remotecmd', '',
2437 2444 _('specify hg command to run on the remote side'))],
2438 2445 _('hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]')),
2439 2446 "rawcommit":
2440 2447 (rawcommit,
2441 2448 [('p', 'parent', [], _('parent')),
2442 2449 ('d', 'date', '', _('date code')),
2443 2450 ('u', 'user', '', _('user')),
2444 2451 ('F', 'files', '', _('file list')),
2445 2452 ('m', 'message', '', _('commit message')),
2446 2453 ('l', 'logfile', '', _('commit message file'))],
2447 2454 _('hg rawcommit [OPTION]... [FILE]...')),
2448 2455 "recover": (recover, [], _('hg recover')),
2449 2456 "^remove|rm":
2450 2457 (remove,
2451 2458 [('I', 'include', [], _('include names matching the given patterns')),
2452 2459 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2453 2460 _('hg remove [OPTION]... FILE...')),
2454 2461 "rename|mv":
2455 2462 (rename,
2456 2463 [('I', 'include', [], _('include names matching the given patterns')),
2457 2464 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2458 2465 ('A', 'after', None, _('record a rename that has already occurred')),
2459 2466 ('f', 'force', None,
2460 2467 _('forcibly copy over an existing managed file'))],
2461 2468 _('hg rename [OPTION]... [SOURCE]... DEST')),
2462 2469 "^revert":
2463 2470 (revert,
2464 2471 [('I', 'include', [], _('include names matching the given patterns')),
2465 2472 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2466 2473 ('r', 'rev', '', _('revision to revert to'))],
2467 2474 _('hg revert [-n] [-r REV] [NAME]...')),
2468 2475 "root": (root, [], _('hg root')),
2469 2476 "^serve":
2470 2477 (serve,
2471 2478 [('A', 'accesslog', '', _('name of access log file to write to')),
2472 2479 ('E', 'errorlog', '', _('name of error log file to write to')),
2473 2480 ('p', 'port', 0, _('port to use (default: 8000)')),
2474 2481 ('a', 'address', '', _('address to use')),
2475 2482 ('n', 'name', '',
2476 2483 _('name to show in web pages (default: working dir)')),
2477 2484 ('', 'stdio', None, _('for remote clients')),
2478 2485 ('t', 'templates', '', _('web templates to use')),
2479 2486 ('', 'style', '', _('template style to use')),
2480 2487 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2481 2488 _('hg serve [OPTION]...')),
2482 2489 "^status|st":
2483 2490 (status,
2484 2491 [('m', 'modified', None, _('show only modified files')),
2485 2492 ('a', 'added', None, _('show only added files')),
2486 2493 ('r', 'removed', None, _('show only removed files')),
2487 2494 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2488 2495 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2489 2496 ('n', 'no-status', None, _('hide status prefix')),
2490 2497 ('0', 'print0', None,
2491 2498 _('end filenames with NUL, for use with xargs')),
2492 2499 ('I', 'include', [], _('include names matching the given patterns')),
2493 2500 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2494 2501 _('hg status [OPTION]... [FILE]...')),
2495 2502 "tag":
2496 2503 (tag,
2497 2504 [('l', 'local', None, _('make the tag local')),
2498 2505 ('m', 'message', '', _('message for tag commit log entry')),
2499 2506 ('d', 'date', '', _('record datecode as commit date')),
2500 2507 ('u', 'user', '', _('record user as commiter')),
2501 2508 ('r', 'rev', '', _('revision to tag'))],
2502 2509 _('hg tag [-r REV] [OPTION]... NAME')),
2503 2510 "tags": (tags, [], _('hg tags')),
2504 "tip": (tip, [], _('hg tip')),
2511 "tip": (tip, [('p', 'patch', None, _('show patch'))], _('hg tip')),
2505 2512 "unbundle":
2506 2513 (unbundle,
2507 2514 [('u', 'update', None,
2508 2515 _('update the working directory to tip after unbundle'))],
2509 2516 _('hg unbundle [-u] FILE')),
2510 2517 "undo": (undo, [], _('hg undo')),
2511 2518 "^update|up|checkout|co":
2512 2519 (update,
2513 2520 [('b', 'branch', '', _('checkout the head of a specific branch')),
2514 2521 ('m', 'merge', None, _('allow merging of branches')),
2515 2522 ('C', 'clean', None, _('overwrite locally modified files')),
2516 2523 ('f', 'force', None, _('force a merge with outstanding changes'))],
2517 2524 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
2518 2525 "verify": (verify, [], _('hg verify')),
2519 2526 "version": (show_version, [], _('hg version')),
2520 2527 }
2521 2528
2522 2529 globalopts = [
2523 2530 ('R', 'repository', '', _('repository root directory')),
2524 2531 ('', 'cwd', '', _('change working directory')),
2525 2532 ('y', 'noninteractive', None,
2526 2533 _('do not prompt, assume \'yes\' for any required answers')),
2527 2534 ('q', 'quiet', None, _('suppress output')),
2528 2535 ('v', 'verbose', None, _('enable additional output')),
2529 2536 ('', 'debug', None, _('enable debugging output')),
2530 2537 ('', 'debugger', None, _('start debugger')),
2531 2538 ('', 'traceback', None, _('print traceback on exception')),
2532 2539 ('', 'time', None, _('time how long the command takes')),
2533 2540 ('', 'profile', None, _('print command execution profile')),
2534 2541 ('', 'version', None, _('output version information and exit')),
2535 2542 ('h', 'help', None, _('display help and exit')),
2536 2543 ]
2537 2544
2538 2545 norepo = ("clone init version help debugancestor debugconfig debugdata"
2539 2546 " debugindex debugindexdot paths")
2540 2547
2541 2548 def find(cmd):
2542 2549 """Return (aliases, command table entry) for command string."""
2543 2550 choice = None
2544 2551 for e in table.keys():
2545 2552 aliases = e.lstrip("^").split("|")
2546 2553 if cmd in aliases:
2547 2554 return aliases, table[e]
2548 2555 for a in aliases:
2549 2556 if a.startswith(cmd):
2550 2557 if choice:
2551 2558 raise AmbiguousCommand(cmd)
2552 2559 else:
2553 2560 choice = aliases, table[e]
2554 2561 break
2555 2562 if choice:
2556 2563 return choice
2557 2564
2558 2565 raise UnknownCommand(cmd)
2559 2566
2560 2567 class SignalInterrupt(Exception):
2561 2568 """Exception raised on SIGTERM and SIGHUP."""
2562 2569
2563 2570 def catchterm(*args):
2564 2571 raise SignalInterrupt
2565 2572
2566 2573 def run():
2567 2574 sys.exit(dispatch(sys.argv[1:]))
2568 2575
2569 2576 class ParseError(Exception):
2570 2577 """Exception raised on errors in parsing the command line."""
2571 2578
2572 2579 def parse(ui, args):
2573 2580 options = {}
2574 2581 cmdoptions = {}
2575 2582
2576 2583 try:
2577 2584 args = fancyopts.fancyopts(args, globalopts, options)
2578 2585 except fancyopts.getopt.GetoptError, inst:
2579 2586 raise ParseError(None, inst)
2580 2587
2581 2588 if args:
2582 2589 cmd, args = args[0], args[1:]
2583 2590 aliases, i = find(cmd)
2584 2591 cmd = aliases[0]
2585 2592 defaults = ui.config("defaults", cmd)
2586 2593 if defaults:
2587 2594 args = defaults.split() + args
2588 2595 c = list(i[1])
2589 2596 else:
2590 2597 cmd = None
2591 2598 c = []
2592 2599
2593 2600 # combine global options into local
2594 2601 for o in globalopts:
2595 2602 c.append((o[0], o[1], options[o[1]], o[3]))
2596 2603
2597 2604 try:
2598 2605 args = fancyopts.fancyopts(args, c, cmdoptions)
2599 2606 except fancyopts.getopt.GetoptError, inst:
2600 2607 raise ParseError(cmd, inst)
2601 2608
2602 2609 # separate global options back out
2603 2610 for o in globalopts:
2604 2611 n = o[1]
2605 2612 options[n] = cmdoptions[n]
2606 2613 del cmdoptions[n]
2607 2614
2608 2615 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2609 2616
2610 2617 def dispatch(args):
2611 2618 signal.signal(signal.SIGTERM, catchterm)
2612 2619 try:
2613 2620 signal.signal(signal.SIGHUP, catchterm)
2614 2621 except AttributeError:
2615 2622 pass
2616 2623
2617 2624 try:
2618 2625 u = ui.ui()
2619 2626 except util.Abort, inst:
2620 2627 sys.stderr.write(_("abort: %s\n") % inst)
2621 2628 sys.exit(1)
2622 2629
2623 2630 external = []
2624 2631 for x in u.extensions():
2625 2632 def on_exception(exc, inst):
2626 2633 u.warn(_("*** failed to import extension %s\n") % x[1])
2627 2634 u.warn("%s\n" % inst)
2628 2635 if "--traceback" in sys.argv[1:]:
2629 2636 traceback.print_exc()
2630 2637 if x[1]:
2631 2638 try:
2632 2639 mod = imp.load_source(x[0], x[1])
2633 2640 except Exception, inst:
2634 2641 on_exception(Exception, inst)
2635 2642 continue
2636 2643 else:
2637 2644 def importh(name):
2638 2645 mod = __import__(name)
2639 2646 components = name.split('.')
2640 2647 for comp in components[1:]:
2641 2648 mod = getattr(mod, comp)
2642 2649 return mod
2643 2650 try:
2644 2651 mod = importh(x[0])
2645 2652 except Exception, inst:
2646 2653 on_exception(Exception, inst)
2647 2654 continue
2648 2655
2649 2656 external.append(mod)
2650 2657 for x in external:
2651 2658 cmdtable = getattr(x, 'cmdtable', {})
2652 2659 for t in cmdtable:
2653 2660 if t in table:
2654 2661 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
2655 2662 table.update(cmdtable)
2656 2663
2657 2664 try:
2658 2665 cmd, func, args, options, cmdoptions = parse(u, args)
2659 2666 except ParseError, inst:
2660 2667 if inst.args[0]:
2661 2668 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
2662 2669 help_(u, inst.args[0])
2663 2670 else:
2664 2671 u.warn(_("hg: %s\n") % inst.args[1])
2665 2672 help_(u, 'shortlist')
2666 2673 sys.exit(-1)
2667 2674 except AmbiguousCommand, inst:
2668 2675 u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0])
2669 2676 sys.exit(1)
2670 2677 except UnknownCommand, inst:
2671 2678 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2672 2679 help_(u, 'shortlist')
2673 2680 sys.exit(1)
2674 2681
2675 2682 if options["time"]:
2676 2683 def get_times():
2677 2684 t = os.times()
2678 2685 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2679 2686 t = (t[0], t[1], t[2], t[3], time.clock())
2680 2687 return t
2681 2688 s = get_times()
2682 2689 def print_time():
2683 2690 t = get_times()
2684 2691 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
2685 2692 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2686 2693 atexit.register(print_time)
2687 2694
2688 2695 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2689 2696 not options["noninteractive"])
2690 2697
2691 2698 # enter the debugger before command execution
2692 2699 if options['debugger']:
2693 2700 pdb.set_trace()
2694 2701
2695 2702 try:
2696 2703 try:
2697 2704 if options['help']:
2698 2705 help_(u, cmd, options['version'])
2699 2706 sys.exit(0)
2700 2707 elif options['version']:
2701 2708 show_version(u)
2702 2709 sys.exit(0)
2703 2710 elif not cmd:
2704 2711 help_(u, 'shortlist')
2705 2712 sys.exit(0)
2706 2713
2707 2714 if options['cwd']:
2708 2715 try:
2709 2716 os.chdir(options['cwd'])
2710 2717 except OSError, inst:
2711 2718 raise util.Abort('%s: %s' %
2712 2719 (options['cwd'], inst.strerror))
2713 2720
2714 2721 if cmd not in norepo.split():
2715 2722 path = options["repository"] or ""
2716 2723 repo = hg.repository(ui=u, path=path)
2717 2724 for x in external:
2718 2725 if hasattr(x, 'reposetup'):
2719 2726 x.reposetup(u, repo)
2720 2727 d = lambda: func(u, repo, *args, **cmdoptions)
2721 2728 else:
2722 2729 d = lambda: func(u, *args, **cmdoptions)
2723 2730
2724 2731 if options['profile']:
2725 2732 import hotshot, hotshot.stats
2726 2733 prof = hotshot.Profile("hg.prof")
2727 2734 r = prof.runcall(d)
2728 2735 prof.close()
2729 2736 stats = hotshot.stats.load("hg.prof")
2730 2737 stats.strip_dirs()
2731 2738 stats.sort_stats('time', 'calls')
2732 2739 stats.print_stats(40)
2733 2740 return r
2734 2741 else:
2735 2742 return d()
2736 2743 except:
2737 2744 # enter the debugger when we hit an exception
2738 2745 if options['debugger']:
2739 2746 pdb.post_mortem(sys.exc_info()[2])
2740 2747 if options['traceback']:
2741 2748 traceback.print_exc()
2742 2749 raise
2743 2750 except hg.RepoError, inst:
2744 2751 u.warn(_("abort: "), inst, "!\n")
2745 2752 except revlog.RevlogError, inst:
2746 2753 u.warn(_("abort: "), inst, "!\n")
2747 2754 except SignalInterrupt:
2748 2755 u.warn(_("killed!\n"))
2749 2756 except KeyboardInterrupt:
2750 2757 try:
2751 2758 u.warn(_("interrupted!\n"))
2752 2759 except IOError, inst:
2753 2760 if inst.errno == errno.EPIPE:
2754 2761 if u.debugflag:
2755 2762 u.warn(_("\nbroken pipe\n"))
2756 2763 else:
2757 2764 raise
2758 2765 except IOError, inst:
2759 2766 if hasattr(inst, "code"):
2760 2767 u.warn(_("abort: %s\n") % inst)
2761 2768 elif hasattr(inst, "reason"):
2762 2769 u.warn(_("abort: error: %s\n") % inst.reason[1])
2763 2770 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2764 2771 if u.debugflag:
2765 2772 u.warn(_("broken pipe\n"))
2766 2773 elif getattr(inst, "strerror", None):
2767 2774 if getattr(inst, "filename", None):
2768 2775 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
2769 2776 else:
2770 2777 u.warn(_("abort: %s\n") % inst.strerror)
2771 2778 else:
2772 2779 raise
2773 2780 except OSError, inst:
2774 2781 if hasattr(inst, "filename"):
2775 2782 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
2776 2783 else:
2777 2784 u.warn(_("abort: %s\n") % inst.strerror)
2778 2785 except util.Abort, inst:
2779 2786 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
2780 2787 sys.exit(1)
2781 2788 except TypeError, inst:
2782 2789 # was this an argument error?
2783 2790 tb = traceback.extract_tb(sys.exc_info()[2])
2784 2791 if len(tb) > 2: # no
2785 2792 raise
2786 2793 u.debug(inst, "\n")
2787 2794 u.warn(_("%s: invalid arguments\n") % cmd)
2788 2795 help_(u, cmd)
2789 2796 except AmbiguousCommand, inst:
2790 2797 u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0])
2791 2798 help_(u, 'shortlist')
2792 2799 except UnknownCommand, inst:
2793 2800 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2794 2801 help_(u, 'shortlist')
2795 2802 except SystemExit:
2796 2803 # don't catch this in the catch-all below
2797 2804 raise
2798 2805 except:
2799 2806 u.warn(_("** unknown exception encountered, details follow\n"))
2800 2807 u.warn(_("** report bug details to mercurial@selenic.com\n"))
2801 2808 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
2802 2809 % version.get_version())
2803 2810 raise
2804 2811
2805 2812 sys.exit(-1)
@@ -1,1851 +1,1857
1 1 # localrepo.py - read/write repository class 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 import struct, os, util
9 9 import filelog, manifest, changelog, dirstate, repo
10 10 from node import *
11 11 from i18n import gettext as _
12 12 from demandload import *
13 13 demandload(globals(), "re lock transaction tempfile stat mdiff errno")
14 14
15 15 class localrepository(object):
16 16 def __init__(self, ui, path=None, create=0):
17 17 if not path:
18 18 p = os.getcwd()
19 19 while not os.path.isdir(os.path.join(p, ".hg")):
20 20 oldp = p
21 21 p = os.path.dirname(p)
22 22 if p == oldp:
23 23 raise repo.RepoError(_("no repo found"))
24 24 path = p
25 25 self.path = os.path.join(path, ".hg")
26 26
27 27 if not create and not os.path.isdir(self.path):
28 28 raise repo.RepoError(_("repository %s not found") % path)
29 29
30 30 self.root = os.path.abspath(path)
31 31 self.ui = ui
32 32 self.opener = util.opener(self.path)
33 33 self.wopener = util.opener(self.root)
34 34 self.manifest = manifest.manifest(self.opener)
35 35 self.changelog = changelog.changelog(self.opener)
36 36 self.tagscache = None
37 37 self.nodetagscache = None
38 38 self.encodepats = None
39 39 self.decodepats = None
40 40
41 41 if create:
42 42 os.mkdir(self.path)
43 43 os.mkdir(self.join("data"))
44 44
45 45 self.dirstate = dirstate.dirstate(self.opener, ui, self.root)
46 46 try:
47 47 self.ui.readconfig(self.join("hgrc"))
48 48 except IOError:
49 49 pass
50 50
51 51 def hook(self, name, throw=False, **args):
52 52 def runhook(name, cmd):
53 53 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
54 54 old = {}
55 55 for k, v in args.items():
56 56 k = k.upper()
57 57 old['HG_' + k] = os.environ.get(k, None)
58 58 old[k] = os.environ.get(k, None)
59 59 os.environ['HG_' + k] = str(v)
60 60 os.environ[k] = str(v)
61 61
62 62 try:
63 63 # Hooks run in the repository root
64 64 olddir = os.getcwd()
65 65 os.chdir(self.root)
66 66 r = os.system(cmd)
67 67 finally:
68 68 for k, v in old.items():
69 69 if v is not None:
70 70 os.environ[k] = v
71 71 else:
72 72 del os.environ[k]
73 73
74 74 os.chdir(olddir)
75 75
76 76 if r:
77 77 desc, r = util.explain_exit(r)
78 78 if throw:
79 79 raise util.Abort(_('%s hook %s') % (name, desc))
80 80 self.ui.warn(_('error: %s hook %s\n') % (name, desc))
81 81 return False
82 82 return True
83 83
84 84 r = True
85 85 for hname, cmd in self.ui.configitems("hooks"):
86 86 s = hname.split(".")
87 87 if s[0] == name and cmd:
88 88 r = runhook(hname, cmd) and r
89 89 return r
90 90
91 91 def tags(self):
92 92 '''return a mapping of tag to node'''
93 93 if not self.tagscache:
94 94 self.tagscache = {}
95 95 def addtag(self, k, n):
96 96 try:
97 97 bin_n = bin(n)
98 98 except TypeError:
99 99 bin_n = ''
100 100 self.tagscache[k.strip()] = bin_n
101 101
102 102 try:
103 103 # read each head of the tags file, ending with the tip
104 104 # and add each tag found to the map, with "newer" ones
105 105 # taking precedence
106 106 fl = self.file(".hgtags")
107 107 h = fl.heads()
108 108 h.reverse()
109 109 for r in h:
110 110 for l in fl.read(r).splitlines():
111 111 if l:
112 112 n, k = l.split(" ", 1)
113 113 addtag(self, k, n)
114 114 except KeyError:
115 115 pass
116 116
117 117 try:
118 118 f = self.opener("localtags")
119 119 for l in f:
120 120 n, k = l.split(" ", 1)
121 121 addtag(self, k, n)
122 122 except IOError:
123 123 pass
124 124
125 125 self.tagscache['tip'] = self.changelog.tip()
126 126
127 127 return self.tagscache
128 128
129 129 def tagslist(self):
130 130 '''return a list of tags ordered by revision'''
131 131 l = []
132 132 for t, n in self.tags().items():
133 133 try:
134 134 r = self.changelog.rev(n)
135 135 except:
136 136 r = -2 # sort to the beginning of the list if unknown
137 137 l.append((r, t, n))
138 138 l.sort()
139 139 return [(t, n) for r, t, n in l]
140 140
141 141 def nodetags(self, node):
142 142 '''return the tags associated with a node'''
143 143 if not self.nodetagscache:
144 144 self.nodetagscache = {}
145 145 for t, n in self.tags().items():
146 146 self.nodetagscache.setdefault(n, []).append(t)
147 147 return self.nodetagscache.get(node, [])
148 148
149 149 def lookup(self, key):
150 150 try:
151 151 return self.tags()[key]
152 152 except KeyError:
153 153 try:
154 154 return self.changelog.lookup(key)
155 155 except:
156 156 raise repo.RepoError(_("unknown revision '%s'") % key)
157 157
158 158 def dev(self):
159 159 return os.stat(self.path).st_dev
160 160
161 161 def local(self):
162 162 return True
163 163
164 164 def join(self, f):
165 165 return os.path.join(self.path, f)
166 166
167 167 def wjoin(self, f):
168 168 return os.path.join(self.root, f)
169 169
170 170 def file(self, f):
171 171 if f[0] == '/':
172 172 f = f[1:]
173 173 return filelog.filelog(self.opener, f)
174 174
175 175 def getcwd(self):
176 176 return self.dirstate.getcwd()
177 177
178 178 def wfile(self, f, mode='r'):
179 179 return self.wopener(f, mode)
180 180
181 181 def wread(self, filename):
182 182 if self.encodepats == None:
183 183 l = []
184 184 for pat, cmd in self.ui.configitems("encode"):
185 185 mf = util.matcher("", "/", [pat], [], [])[1]
186 186 l.append((mf, cmd))
187 187 self.encodepats = l
188 188
189 189 data = self.wopener(filename, 'r').read()
190 190
191 191 for mf, cmd in self.encodepats:
192 192 if mf(filename):
193 193 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
194 194 data = util.filter(data, cmd)
195 195 break
196 196
197 197 return data
198 198
199 199 def wwrite(self, filename, data, fd=None):
200 200 if self.decodepats == None:
201 201 l = []
202 202 for pat, cmd in self.ui.configitems("decode"):
203 203 mf = util.matcher("", "/", [pat], [], [])[1]
204 204 l.append((mf, cmd))
205 205 self.decodepats = l
206 206
207 207 for mf, cmd in self.decodepats:
208 208 if mf(filename):
209 209 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
210 210 data = util.filter(data, cmd)
211 211 break
212 212
213 213 if fd:
214 214 return fd.write(data)
215 215 return self.wopener(filename, 'w').write(data)
216 216
217 217 def transaction(self):
218 218 # save dirstate for undo
219 219 try:
220 220 ds = self.opener("dirstate").read()
221 221 except IOError:
222 222 ds = ""
223 223 self.opener("journal.dirstate", "w").write(ds)
224 224
225 225 def after():
226 226 util.rename(self.join("journal"), self.join("undo"))
227 227 util.rename(self.join("journal.dirstate"),
228 228 self.join("undo.dirstate"))
229 229
230 230 return transaction.transaction(self.ui.warn, self.opener,
231 231 self.join("journal"), after)
232 232
233 233 def recover(self):
234 234 lock = self.lock()
235 235 if os.path.exists(self.join("journal")):
236 236 self.ui.status(_("rolling back interrupted transaction\n"))
237 237 transaction.rollback(self.opener, self.join("journal"))
238 238 self.manifest = manifest.manifest(self.opener)
239 239 self.changelog = changelog.changelog(self.opener)
240 240 return True
241 241 else:
242 242 self.ui.warn(_("no interrupted transaction available\n"))
243 243 return False
244 244
245 245 def undo(self, wlock=None):
246 246 if not wlock:
247 247 wlock = self.wlock()
248 248 lock = self.lock()
249 249 if os.path.exists(self.join("undo")):
250 250 self.ui.status(_("rolling back last transaction\n"))
251 251 transaction.rollback(self.opener, self.join("undo"))
252 252 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
253 253 self.dirstate.read()
254 254 else:
255 255 self.ui.warn(_("no undo information available\n"))
256 256
257 257 def lock(self, wait=1):
258 258 try:
259 259 return lock.lock(self.join("lock"), 0)
260 260 except lock.LockHeld, inst:
261 261 if wait:
262 262 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
263 263 return lock.lock(self.join("lock"), wait)
264 264 raise inst
265 265
266 266 def wlock(self, wait=1):
267 267 try:
268 268 wlock = lock.lock(self.join("wlock"), 0, self.dirstate.write)
269 269 except lock.LockHeld, inst:
270 270 if not wait:
271 271 raise inst
272 272 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
273 273 wlock = lock.lock(self.join("wlock"), wait, self.dirstate.write)
274 274 self.dirstate.read()
275 275 return wlock
276 276
277 277 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None):
278 278 orig_parent = self.dirstate.parents()[0] or nullid
279 279 p1 = p1 or self.dirstate.parents()[0] or nullid
280 280 p2 = p2 or self.dirstate.parents()[1] or nullid
281 281 c1 = self.changelog.read(p1)
282 282 c2 = self.changelog.read(p2)
283 283 m1 = self.manifest.read(c1[0])
284 284 mf1 = self.manifest.readflags(c1[0])
285 285 m2 = self.manifest.read(c2[0])
286 286 changed = []
287 287
288 288 if orig_parent == p1:
289 289 update_dirstate = 1
290 290 else:
291 291 update_dirstate = 0
292 292
293 293 if not wlock:
294 294 wlock = self.wlock()
295 295 lock = self.lock()
296 296 tr = self.transaction()
297 297 mm = m1.copy()
298 298 mfm = mf1.copy()
299 299 linkrev = self.changelog.count()
300 300 for f in files:
301 301 try:
302 302 t = self.wread(f)
303 303 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
304 304 r = self.file(f)
305 305 mfm[f] = tm
306 306
307 307 fp1 = m1.get(f, nullid)
308 308 fp2 = m2.get(f, nullid)
309 309
310 310 # is the same revision on two branches of a merge?
311 311 if fp2 == fp1:
312 312 fp2 = nullid
313 313
314 314 if fp2 != nullid:
315 315 # is one parent an ancestor of the other?
316 316 fpa = r.ancestor(fp1, fp2)
317 317 if fpa == fp1:
318 318 fp1, fp2 = fp2, nullid
319 319 elif fpa == fp2:
320 320 fp2 = nullid
321 321
322 322 # is the file unmodified from the parent?
323 323 if t == r.read(fp1):
324 324 # record the proper existing parent in manifest
325 325 # no need to add a revision
326 326 mm[f] = fp1
327 327 continue
328 328
329 329 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
330 330 changed.append(f)
331 331 if update_dirstate:
332 332 self.dirstate.update([f], "n")
333 333 except IOError:
334 334 try:
335 335 del mm[f]
336 336 del mfm[f]
337 337 if update_dirstate:
338 338 self.dirstate.forget([f])
339 339 except:
340 340 # deleted from p2?
341 341 pass
342 342
343 343 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
344 344 user = user or self.ui.username()
345 345 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
346 346 tr.close()
347 347 if update_dirstate:
348 348 self.dirstate.setparents(n, nullid)
349 349
350 350 def commit(self, files=None, text="", user=None, date=None,
351 351 match=util.always, force=False, wlock=None):
352 352 commit = []
353 353 remove = []
354 354 changed = []
355 355
356 356 if files:
357 357 for f in files:
358 358 s = self.dirstate.state(f)
359 359 if s in 'nmai':
360 360 commit.append(f)
361 361 elif s == 'r':
362 362 remove.append(f)
363 363 else:
364 364 self.ui.warn(_("%s not tracked!\n") % f)
365 365 else:
366 366 modified, added, removed, deleted, unknown = self.changes(match=match)
367 367 commit = modified + added
368 368 remove = removed
369 369
370 370 p1, p2 = self.dirstate.parents()
371 371 c1 = self.changelog.read(p1)
372 372 c2 = self.changelog.read(p2)
373 373 m1 = self.manifest.read(c1[0])
374 374 mf1 = self.manifest.readflags(c1[0])
375 375 m2 = self.manifest.read(c2[0])
376 376
377 377 if not commit and not remove and not force and p2 == nullid:
378 378 self.ui.status(_("nothing changed\n"))
379 379 return None
380 380
381 381 xp1 = hex(p1)
382 382 if p2 == nullid: xp2 = ''
383 383 else: xp2 = hex(p2)
384 384
385 385 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
386 386
387 387 if not wlock:
388 388 wlock = self.wlock()
389 389 lock = self.lock()
390 390 tr = self.transaction()
391 391
392 392 # check in files
393 393 new = {}
394 394 linkrev = self.changelog.count()
395 395 commit.sort()
396 396 for f in commit:
397 397 self.ui.note(f + "\n")
398 398 try:
399 399 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
400 400 t = self.wread(f)
401 401 except IOError:
402 402 self.ui.warn(_("trouble committing %s!\n") % f)
403 403 raise
404 404
405 405 r = self.file(f)
406 406
407 407 meta = {}
408 408 cp = self.dirstate.copied(f)
409 409 if cp:
410 410 meta["copy"] = cp
411 411 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
412 412 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"]))
413 413 fp1, fp2 = nullid, nullid
414 414 else:
415 415 fp1 = m1.get(f, nullid)
416 416 fp2 = m2.get(f, nullid)
417 417
418 418 if fp2 != nullid:
419 419 # is one parent an ancestor of the other?
420 420 fpa = r.ancestor(fp1, fp2)
421 421 if fpa == fp1:
422 422 fp1, fp2 = fp2, nullid
423 423 elif fpa == fp2:
424 424 fp2 = nullid
425 425
426 426 # is the file unmodified from the parent?
427 427 if not meta and t == r.read(fp1) and fp2 == nullid:
428 428 # record the proper existing parent in manifest
429 429 # no need to add a revision
430 430 new[f] = fp1
431 431 continue
432 432
433 433 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
434 434 # remember what we've added so that we can later calculate
435 435 # the files to pull from a set of changesets
436 436 changed.append(f)
437 437
438 438 # update manifest
439 439 m1 = m1.copy()
440 440 m1.update(new)
441 441 for f in remove:
442 442 if f in m1:
443 443 del m1[f]
444 444 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
445 445 (new, remove))
446 446
447 447 # add changeset
448 448 new = new.keys()
449 449 new.sort()
450 450
451 451 if not text:
452 452 edittext = [""]
453 453 if p2 != nullid:
454 454 edittext.append("HG: branch merge")
455 455 edittext.extend(["HG: changed %s" % f for f in changed])
456 456 edittext.extend(["HG: removed %s" % f for f in remove])
457 457 if not changed and not remove:
458 458 edittext.append("HG: no files changed")
459 459 edittext.append("")
460 460 # run editor in the repository root
461 461 olddir = os.getcwd()
462 462 os.chdir(self.root)
463 463 edittext = self.ui.edit("\n".join(edittext))
464 464 os.chdir(olddir)
465 465 if not edittext.rstrip():
466 466 return None
467 467 text = edittext
468 468
469 469 user = user or self.ui.username()
470 470 n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
471 471 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
472 472 parent2=xp2)
473 473 tr.close()
474 474
475 475 self.dirstate.setparents(n)
476 476 self.dirstate.update(new, "n")
477 477 self.dirstate.forget(remove)
478 478
479 479 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
480 480 return n
481 481
482 482 def walk(self, node=None, files=[], match=util.always):
483 483 if node:
484 484 fdict = dict.fromkeys(files)
485 485 for fn in self.manifest.read(self.changelog.read(node)[0]):
486 486 fdict.pop(fn, None)
487 487 if match(fn):
488 488 yield 'm', fn
489 489 for fn in fdict:
490 490 self.ui.warn(_('%s: No such file in rev %s\n') % (
491 491 util.pathto(self.getcwd(), fn), short(node)))
492 492 else:
493 493 for src, fn in self.dirstate.walk(files, match):
494 494 yield src, fn
495 495
496 496 def changes(self, node1=None, node2=None, files=[], match=util.always,
497 497 wlock=None):
498 498 """return changes between two nodes or node and working directory
499 499
500 500 If node1 is None, use the first dirstate parent instead.
501 501 If node2 is None, compare node1 with working directory.
502 502 """
503 503
504 504 def fcmp(fn, mf):
505 505 t1 = self.wread(fn)
506 506 t2 = self.file(fn).read(mf.get(fn, nullid))
507 507 return cmp(t1, t2)
508 508
509 509 def mfmatches(node):
510 510 change = self.changelog.read(node)
511 511 mf = dict(self.manifest.read(change[0]))
512 512 for fn in mf.keys():
513 513 if not match(fn):
514 514 del mf[fn]
515 515 return mf
516 516
517 517 # are we comparing the working directory?
518 518 if not node2:
519 519 if not wlock:
520 520 try:
521 521 wlock = self.wlock(wait=0)
522 522 except lock.LockHeld:
523 523 wlock = None
524 524 lookup, modified, added, removed, deleted, unknown = (
525 525 self.dirstate.changes(files, match))
526 526
527 527 # are we comparing working dir against its parent?
528 528 if not node1:
529 529 if lookup:
530 530 # do a full compare of any files that might have changed
531 531 mf2 = mfmatches(self.dirstate.parents()[0])
532 532 for f in lookup:
533 533 if fcmp(f, mf2):
534 534 modified.append(f)
535 535 elif wlock is not None:
536 536 self.dirstate.update([f], "n")
537 537 else:
538 538 # we are comparing working dir against non-parent
539 539 # generate a pseudo-manifest for the working dir
540 540 mf2 = mfmatches(self.dirstate.parents()[0])
541 541 for f in lookup + modified + added:
542 542 mf2[f] = ""
543 543 for f in removed:
544 544 if f in mf2:
545 545 del mf2[f]
546 546 else:
547 547 # we are comparing two revisions
548 548 deleted, unknown = [], []
549 549 mf2 = mfmatches(node2)
550 550
551 551 if node1:
552 552 # flush lists from dirstate before comparing manifests
553 553 modified, added = [], []
554 554
555 555 mf1 = mfmatches(node1)
556 556
557 557 for fn in mf2:
558 558 if mf1.has_key(fn):
559 559 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
560 560 modified.append(fn)
561 561 del mf1[fn]
562 562 else:
563 563 added.append(fn)
564 564
565 565 removed = mf1.keys()
566 566
567 567 # sort and return results:
568 568 for l in modified, added, removed, deleted, unknown:
569 569 l.sort()
570 570 return (modified, added, removed, deleted, unknown)
571 571
572 572 def add(self, list, wlock=None):
573 573 if not wlock:
574 574 wlock = self.wlock()
575 575 for f in list:
576 576 p = self.wjoin(f)
577 577 if not os.path.exists(p):
578 578 self.ui.warn(_("%s does not exist!\n") % f)
579 579 elif not os.path.isfile(p):
580 580 self.ui.warn(_("%s not added: only files supported currently\n")
581 581 % f)
582 582 elif self.dirstate.state(f) in 'an':
583 583 self.ui.warn(_("%s already tracked!\n") % f)
584 584 else:
585 585 self.dirstate.update([f], "a")
586 586
587 587 def forget(self, list, wlock=None):
588 588 if not wlock:
589 589 wlock = self.wlock()
590 590 for f in list:
591 591 if self.dirstate.state(f) not in 'ai':
592 592 self.ui.warn(_("%s not added!\n") % f)
593 593 else:
594 594 self.dirstate.forget([f])
595 595
596 596 def remove(self, list, unlink=False, wlock=None):
597 597 if unlink:
598 598 for f in list:
599 599 try:
600 600 util.unlink(self.wjoin(f))
601 601 except OSError, inst:
602 602 if inst.errno != errno.ENOENT:
603 603 raise
604 604 if not wlock:
605 605 wlock = self.wlock()
606 606 for f in list:
607 607 p = self.wjoin(f)
608 608 if os.path.exists(p):
609 609 self.ui.warn(_("%s still exists!\n") % f)
610 610 elif self.dirstate.state(f) == 'a':
611 611 self.ui.warn(_("%s never committed!\n") % f)
612 612 self.dirstate.forget([f])
613 613 elif f not in self.dirstate:
614 614 self.ui.warn(_("%s not tracked!\n") % f)
615 615 else:
616 616 self.dirstate.update([f], "r")
617 617
618 618 def undelete(self, list, wlock=None):
619 619 p = self.dirstate.parents()[0]
620 620 mn = self.changelog.read(p)[0]
621 621 mf = self.manifest.readflags(mn)
622 622 m = self.manifest.read(mn)
623 623 if not wlock:
624 624 wlock = self.wlock()
625 625 for f in list:
626 626 if self.dirstate.state(f) not in "r":
627 627 self.ui.warn("%s not removed!\n" % f)
628 628 else:
629 629 t = self.file(f).read(m[f])
630 630 self.wwrite(f, t)
631 631 util.set_exec(self.wjoin(f), mf[f])
632 632 self.dirstate.update([f], "n")
633 633
634 634 def copy(self, source, dest, wlock=None):
635 635 p = self.wjoin(dest)
636 636 if not os.path.exists(p):
637 637 self.ui.warn(_("%s does not exist!\n") % dest)
638 638 elif not os.path.isfile(p):
639 639 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
640 640 else:
641 641 if not wlock:
642 642 wlock = self.wlock()
643 643 if self.dirstate.state(dest) == '?':
644 644 self.dirstate.update([dest], "a")
645 645 self.dirstate.copy(source, dest)
646 646
647 647 def heads(self, start=None):
648 648 heads = self.changelog.heads(start)
649 649 # sort the output in rev descending order
650 650 heads = [(-self.changelog.rev(h), h) for h in heads]
651 651 heads.sort()
652 652 return [n for (r, n) in heads]
653 653
654 654 # branchlookup returns a dict giving a list of branches for
655 655 # each head. A branch is defined as the tag of a node or
656 656 # the branch of the node's parents. If a node has multiple
657 657 # branch tags, tags are eliminated if they are visible from other
658 658 # branch tags.
659 659 #
660 660 # So, for this graph: a->b->c->d->e
661 661 # \ /
662 662 # aa -----/
663 663 # a has tag 2.6.12
664 664 # d has tag 2.6.13
665 665 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
666 666 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
667 667 # from the list.
668 668 #
669 669 # It is possible that more than one head will have the same branch tag.
670 670 # callers need to check the result for multiple heads under the same
671 671 # branch tag if that is a problem for them (ie checkout of a specific
672 672 # branch).
673 673 #
674 674 # passing in a specific branch will limit the depth of the search
675 675 # through the parents. It won't limit the branches returned in the
676 676 # result though.
677 677 def branchlookup(self, heads=None, branch=None):
678 678 if not heads:
679 679 heads = self.heads()
680 680 headt = [ h for h in heads ]
681 681 chlog = self.changelog
682 682 branches = {}
683 683 merges = []
684 684 seenmerge = {}
685 685
686 686 # traverse the tree once for each head, recording in the branches
687 687 # dict which tags are visible from this head. The branches
688 688 # dict also records which tags are visible from each tag
689 689 # while we traverse.
690 690 while headt or merges:
691 691 if merges:
692 692 n, found = merges.pop()
693 693 visit = [n]
694 694 else:
695 695 h = headt.pop()
696 696 visit = [h]
697 697 found = [h]
698 698 seen = {}
699 699 while visit:
700 700 n = visit.pop()
701 701 if n in seen:
702 702 continue
703 703 pp = chlog.parents(n)
704 704 tags = self.nodetags(n)
705 705 if tags:
706 706 for x in tags:
707 707 if x == 'tip':
708 708 continue
709 709 for f in found:
710 710 branches.setdefault(f, {})[n] = 1
711 711 branches.setdefault(n, {})[n] = 1
712 712 break
713 713 if n not in found:
714 714 found.append(n)
715 715 if branch in tags:
716 716 continue
717 717 seen[n] = 1
718 718 if pp[1] != nullid and n not in seenmerge:
719 719 merges.append((pp[1], [x for x in found]))
720 720 seenmerge[n] = 1
721 721 if pp[0] != nullid:
722 722 visit.append(pp[0])
723 723 # traverse the branches dict, eliminating branch tags from each
724 724 # head that are visible from another branch tag for that head.
725 725 out = {}
726 726 viscache = {}
727 727 for h in heads:
728 728 def visible(node):
729 729 if node in viscache:
730 730 return viscache[node]
731 731 ret = {}
732 732 visit = [node]
733 733 while visit:
734 734 x = visit.pop()
735 735 if x in viscache:
736 736 ret.update(viscache[x])
737 737 elif x not in ret:
738 738 ret[x] = 1
739 739 if x in branches:
740 740 visit[len(visit):] = branches[x].keys()
741 741 viscache[node] = ret
742 742 return ret
743 743 if h not in branches:
744 744 continue
745 745 # O(n^2), but somewhat limited. This only searches the
746 746 # tags visible from a specific head, not all the tags in the
747 747 # whole repo.
748 748 for b in branches[h]:
749 749 vis = False
750 750 for bb in branches[h].keys():
751 751 if b != bb:
752 752 if b in visible(bb):
753 753 vis = True
754 754 break
755 755 if not vis:
756 756 l = out.setdefault(h, [])
757 757 l[len(l):] = self.nodetags(b)
758 758 return out
759 759
760 760 def branches(self, nodes):
761 761 if not nodes:
762 762 nodes = [self.changelog.tip()]
763 763 b = []
764 764 for n in nodes:
765 765 t = n
766 766 while n:
767 767 p = self.changelog.parents(n)
768 768 if p[1] != nullid or p[0] == nullid:
769 769 b.append((t, n, p[0], p[1]))
770 770 break
771 771 n = p[0]
772 772 return b
773 773
774 774 def between(self, pairs):
775 775 r = []
776 776
777 777 for top, bottom in pairs:
778 778 n, l, i = top, [], 0
779 779 f = 1
780 780
781 781 while n != bottom:
782 782 p = self.changelog.parents(n)[0]
783 783 if i == f:
784 784 l.append(n)
785 785 f = f * 2
786 786 n = p
787 787 i += 1
788 788
789 789 r.append(l)
790 790
791 791 return r
792 792
793 793 def findincoming(self, remote, base=None, heads=None):
794 794 m = self.changelog.nodemap
795 795 search = []
796 796 fetch = {}
797 797 seen = {}
798 798 seenbranch = {}
799 799 if base == None:
800 800 base = {}
801 801
802 802 # assume we're closer to the tip than the root
803 803 # and start by examining the heads
804 804 self.ui.status(_("searching for changes\n"))
805 805
806 806 if not heads:
807 807 heads = remote.heads()
808 808
809 809 unknown = []
810 810 for h in heads:
811 811 if h not in m:
812 812 unknown.append(h)
813 813 else:
814 814 base[h] = 1
815 815
816 816 if not unknown:
817 817 return None
818 818
819 819 rep = {}
820 820 reqcnt = 0
821 821
822 822 # search through remote branches
823 823 # a 'branch' here is a linear segment of history, with four parts:
824 824 # head, root, first parent, second parent
825 825 # (a branch always has two parents (or none) by definition)
826 826 unknown = remote.branches(unknown)
827 827 while unknown:
828 828 r = []
829 829 while unknown:
830 830 n = unknown.pop(0)
831 831 if n[0] in seen:
832 832 continue
833 833
834 834 self.ui.debug(_("examining %s:%s\n")
835 835 % (short(n[0]), short(n[1])))
836 836 if n[0] == nullid:
837 837 break
838 838 if n in seenbranch:
839 839 self.ui.debug(_("branch already found\n"))
840 840 continue
841 841 if n[1] and n[1] in m: # do we know the base?
842 842 self.ui.debug(_("found incomplete branch %s:%s\n")
843 843 % (short(n[0]), short(n[1])))
844 844 search.append(n) # schedule branch range for scanning
845 845 seenbranch[n] = 1
846 846 else:
847 847 if n[1] not in seen and n[1] not in fetch:
848 848 if n[2] in m and n[3] in m:
849 849 self.ui.debug(_("found new changeset %s\n") %
850 850 short(n[1]))
851 851 fetch[n[1]] = 1 # earliest unknown
852 852 base[n[2]] = 1 # latest known
853 853 continue
854 854
855 855 for a in n[2:4]:
856 856 if a not in rep:
857 857 r.append(a)
858 858 rep[a] = 1
859 859
860 860 seen[n[0]] = 1
861 861
862 862 if r:
863 863 reqcnt += 1
864 864 self.ui.debug(_("request %d: %s\n") %
865 865 (reqcnt, " ".join(map(short, r))))
866 866 for p in range(0, len(r), 10):
867 867 for b in remote.branches(r[p:p+10]):
868 868 self.ui.debug(_("received %s:%s\n") %
869 869 (short(b[0]), short(b[1])))
870 870 if b[0] in m:
871 871 self.ui.debug(_("found base node %s\n")
872 872 % short(b[0]))
873 873 base[b[0]] = 1
874 874 elif b[0] not in seen:
875 875 unknown.append(b)
876 876
877 877 # do binary search on the branches we found
878 878 while search:
879 879 n = search.pop(0)
880 880 reqcnt += 1
881 881 l = remote.between([(n[0], n[1])])[0]
882 882 l.append(n[1])
883 883 p = n[0]
884 884 f = 1
885 885 for i in l:
886 886 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
887 887 if i in m:
888 888 if f <= 2:
889 889 self.ui.debug(_("found new branch changeset %s\n") %
890 890 short(p))
891 891 fetch[p] = 1
892 892 base[i] = 1
893 893 else:
894 894 self.ui.debug(_("narrowed branch search to %s:%s\n")
895 895 % (short(p), short(i)))
896 896 search.append((p, i))
897 897 break
898 898 p, f = i, f * 2
899 899
900 900 # sanity check our fetch list
901 901 for f in fetch.keys():
902 902 if f in m:
903 903 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
904 904
905 905 if base.keys() == [nullid]:
906 906 self.ui.warn(_("warning: pulling from an unrelated repository!\n"))
907 907
908 908 self.ui.note(_("found new changesets starting at ") +
909 909 " ".join([short(f) for f in fetch]) + "\n")
910 910
911 911 self.ui.debug(_("%d total queries\n") % reqcnt)
912 912
913 913 return fetch.keys()
914 914
915 915 def findoutgoing(self, remote, base=None, heads=None):
916 916 if base == None:
917 917 base = {}
918 918 self.findincoming(remote, base, heads)
919 919
920 920 self.ui.debug(_("common changesets up to ")
921 921 + " ".join(map(short, base.keys())) + "\n")
922 922
923 923 remain = dict.fromkeys(self.changelog.nodemap)
924 924
925 925 # prune everything remote has from the tree
926 926 del remain[nullid]
927 927 remove = base.keys()
928 928 while remove:
929 929 n = remove.pop(0)
930 930 if n in remain:
931 931 del remain[n]
932 932 for p in self.changelog.parents(n):
933 933 remove.append(p)
934 934
935 935 # find every node whose parents have been pruned
936 936 subset = []
937 937 for n in remain:
938 938 p1, p2 = self.changelog.parents(n)
939 939 if p1 not in remain and p2 not in remain:
940 940 subset.append(n)
941 941
942 942 # this is the set of all roots we have to push
943 943 return subset
944 944
945 945 def pull(self, remote, heads=None):
946 946 lock = self.lock()
947 947
948 948 # if we have an empty repo, fetch everything
949 949 if self.changelog.tip() == nullid:
950 950 self.ui.status(_("requesting all changes\n"))
951 951 fetch = [nullid]
952 952 else:
953 953 fetch = self.findincoming(remote)
954 954
955 955 if not fetch:
956 956 self.ui.status(_("no changes found\n"))
957 957 return 1
958 958
959 959 if heads is None:
960 960 cg = remote.changegroup(fetch)
961 961 else:
962 962 cg = remote.changegroupsubset(fetch, heads)
963 963 return self.addchangegroup(cg)
964 964
965 965 def push(self, remote, force=False):
966 966 lock = remote.lock()
967 967
968 968 base = {}
969 969 heads = remote.heads()
970 970 inc = self.findincoming(remote, base, heads)
971 971 if not force and inc:
972 972 self.ui.warn(_("abort: unsynced remote changes!\n"))
973 973 self.ui.status(_("(did you forget to sync? use push -f to force)\n"))
974 974 return 1
975 975
976 976 update = self.findoutgoing(remote, base)
977 977 if not update:
978 978 self.ui.status(_("no changes found\n"))
979 979 return 1
980 980 elif not force:
981 981 if len(heads) < len(self.changelog.heads()):
982 982 self.ui.warn(_("abort: push creates new remote branches!\n"))
983 983 self.ui.status(_("(did you forget to merge?"
984 984 " use push -f to force)\n"))
985 985 return 1
986 986
987 987 cg = self.changegroup(update)
988 988 return remote.addchangegroup(cg)
989 989
990 990 def changegroupsubset(self, bases, heads):
991 991 """This function generates a changegroup consisting of all the nodes
992 992 that are descendents of any of the bases, and ancestors of any of
993 993 the heads.
994 994
995 995 It is fairly complex as determining which filenodes and which
996 996 manifest nodes need to be included for the changeset to be complete
997 997 is non-trivial.
998 998
999 999 Another wrinkle is doing the reverse, figuring out which changeset in
1000 1000 the changegroup a particular filenode or manifestnode belongs to."""
1001 1001
1002 1002 # Set up some initial variables
1003 1003 # Make it easy to refer to self.changelog
1004 1004 cl = self.changelog
1005 1005 # msng is short for missing - compute the list of changesets in this
1006 1006 # changegroup.
1007 1007 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1008 1008 # Some bases may turn out to be superfluous, and some heads may be
1009 1009 # too. nodesbetween will return the minimal set of bases and heads
1010 1010 # necessary to re-create the changegroup.
1011 1011
1012 1012 # Known heads are the list of heads that it is assumed the recipient
1013 1013 # of this changegroup will know about.
1014 1014 knownheads = {}
1015 1015 # We assume that all parents of bases are known heads.
1016 1016 for n in bases:
1017 1017 for p in cl.parents(n):
1018 1018 if p != nullid:
1019 1019 knownheads[p] = 1
1020 1020 knownheads = knownheads.keys()
1021 1021 if knownheads:
1022 1022 # Now that we know what heads are known, we can compute which
1023 1023 # changesets are known. The recipient must know about all
1024 1024 # changesets required to reach the known heads from the null
1025 1025 # changeset.
1026 1026 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1027 1027 junk = None
1028 1028 # Transform the list into an ersatz set.
1029 1029 has_cl_set = dict.fromkeys(has_cl_set)
1030 1030 else:
1031 1031 # If there were no known heads, the recipient cannot be assumed to
1032 1032 # know about any changesets.
1033 1033 has_cl_set = {}
1034 1034
1035 1035 # Make it easy to refer to self.manifest
1036 1036 mnfst = self.manifest
1037 1037 # We don't know which manifests are missing yet
1038 1038 msng_mnfst_set = {}
1039 1039 # Nor do we know which filenodes are missing.
1040 1040 msng_filenode_set = {}
1041 1041
1042 1042 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1043 1043 junk = None
1044 1044
1045 1045 # A changeset always belongs to itself, so the changenode lookup
1046 1046 # function for a changenode is identity.
1047 1047 def identity(x):
1048 1048 return x
1049 1049
1050 1050 # A function generating function. Sets up an environment for the
1051 1051 # inner function.
1052 1052 def cmp_by_rev_func(revlog):
1053 1053 # Compare two nodes by their revision number in the environment's
1054 1054 # revision history. Since the revision number both represents the
1055 1055 # most efficient order to read the nodes in, and represents a
1056 1056 # topological sorting of the nodes, this function is often useful.
1057 1057 def cmp_by_rev(a, b):
1058 1058 return cmp(revlog.rev(a), revlog.rev(b))
1059 1059 return cmp_by_rev
1060 1060
1061 1061 # If we determine that a particular file or manifest node must be a
1062 1062 # node that the recipient of the changegroup will already have, we can
1063 1063 # also assume the recipient will have all the parents. This function
1064 1064 # prunes them from the set of missing nodes.
1065 1065 def prune_parents(revlog, hasset, msngset):
1066 1066 haslst = hasset.keys()
1067 1067 haslst.sort(cmp_by_rev_func(revlog))
1068 1068 for node in haslst:
1069 1069 parentlst = [p for p in revlog.parents(node) if p != nullid]
1070 1070 while parentlst:
1071 1071 n = parentlst.pop()
1072 1072 if n not in hasset:
1073 1073 hasset[n] = 1
1074 1074 p = [p for p in revlog.parents(n) if p != nullid]
1075 1075 parentlst.extend(p)
1076 1076 for n in hasset:
1077 1077 msngset.pop(n, None)
1078 1078
1079 1079 # This is a function generating function used to set up an environment
1080 1080 # for the inner function to execute in.
1081 1081 def manifest_and_file_collector(changedfileset):
1082 1082 # This is an information gathering function that gathers
1083 1083 # information from each changeset node that goes out as part of
1084 1084 # the changegroup. The information gathered is a list of which
1085 1085 # manifest nodes are potentially required (the recipient may
1086 1086 # already have them) and total list of all files which were
1087 1087 # changed in any changeset in the changegroup.
1088 1088 #
1089 1089 # We also remember the first changenode we saw any manifest
1090 1090 # referenced by so we can later determine which changenode 'owns'
1091 1091 # the manifest.
1092 1092 def collect_manifests_and_files(clnode):
1093 1093 c = cl.read(clnode)
1094 1094 for f in c[3]:
1095 1095 # This is to make sure we only have one instance of each
1096 1096 # filename string for each filename.
1097 1097 changedfileset.setdefault(f, f)
1098 1098 msng_mnfst_set.setdefault(c[0], clnode)
1099 1099 return collect_manifests_and_files
1100 1100
1101 1101 # Figure out which manifest nodes (of the ones we think might be part
1102 1102 # of the changegroup) the recipient must know about and remove them
1103 1103 # from the changegroup.
1104 1104 def prune_manifests():
1105 1105 has_mnfst_set = {}
1106 1106 for n in msng_mnfst_set:
1107 1107 # If a 'missing' manifest thinks it belongs to a changenode
1108 1108 # the recipient is assumed to have, obviously the recipient
1109 1109 # must have that manifest.
1110 1110 linknode = cl.node(mnfst.linkrev(n))
1111 1111 if linknode in has_cl_set:
1112 1112 has_mnfst_set[n] = 1
1113 1113 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1114 1114
1115 1115 # Use the information collected in collect_manifests_and_files to say
1116 1116 # which changenode any manifestnode belongs to.
1117 1117 def lookup_manifest_link(mnfstnode):
1118 1118 return msng_mnfst_set[mnfstnode]
1119 1119
1120 1120 # A function generating function that sets up the initial environment
1121 1121 # the inner function.
1122 1122 def filenode_collector(changedfiles):
1123 1123 next_rev = [0]
1124 1124 # This gathers information from each manifestnode included in the
1125 1125 # changegroup about which filenodes the manifest node references
1126 1126 # so we can include those in the changegroup too.
1127 1127 #
1128 1128 # It also remembers which changenode each filenode belongs to. It
1129 1129 # does this by assuming the a filenode belongs to the changenode
1130 1130 # the first manifest that references it belongs to.
1131 1131 def collect_msng_filenodes(mnfstnode):
1132 1132 r = mnfst.rev(mnfstnode)
1133 1133 if r == next_rev[0]:
1134 1134 # If the last rev we looked at was the one just previous,
1135 1135 # we only need to see a diff.
1136 1136 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1137 1137 # For each line in the delta
1138 1138 for dline in delta.splitlines():
1139 1139 # get the filename and filenode for that line
1140 1140 f, fnode = dline.split('\0')
1141 1141 fnode = bin(fnode[:40])
1142 1142 f = changedfiles.get(f, None)
1143 1143 # And if the file is in the list of files we care
1144 1144 # about.
1145 1145 if f is not None:
1146 1146 # Get the changenode this manifest belongs to
1147 1147 clnode = msng_mnfst_set[mnfstnode]
1148 1148 # Create the set of filenodes for the file if
1149 1149 # there isn't one already.
1150 1150 ndset = msng_filenode_set.setdefault(f, {})
1151 1151 # And set the filenode's changelog node to the
1152 1152 # manifest's if it hasn't been set already.
1153 1153 ndset.setdefault(fnode, clnode)
1154 1154 else:
1155 1155 # Otherwise we need a full manifest.
1156 1156 m = mnfst.read(mnfstnode)
1157 1157 # For every file in we care about.
1158 1158 for f in changedfiles:
1159 1159 fnode = m.get(f, None)
1160 1160 # If it's in the manifest
1161 1161 if fnode is not None:
1162 1162 # See comments above.
1163 1163 clnode = msng_mnfst_set[mnfstnode]
1164 1164 ndset = msng_filenode_set.setdefault(f, {})
1165 1165 ndset.setdefault(fnode, clnode)
1166 1166 # Remember the revision we hope to see next.
1167 1167 next_rev[0] = r + 1
1168 1168 return collect_msng_filenodes
1169 1169
1170 1170 # We have a list of filenodes we think we need for a file, lets remove
1171 1171 # all those we now the recipient must have.
1172 1172 def prune_filenodes(f, filerevlog):
1173 1173 msngset = msng_filenode_set[f]
1174 1174 hasset = {}
1175 1175 # If a 'missing' filenode thinks it belongs to a changenode we
1176 1176 # assume the recipient must have, then the recipient must have
1177 1177 # that filenode.
1178 1178 for n in msngset:
1179 1179 clnode = cl.node(filerevlog.linkrev(n))
1180 1180 if clnode in has_cl_set:
1181 1181 hasset[n] = 1
1182 1182 prune_parents(filerevlog, hasset, msngset)
1183 1183
1184 1184 # A function generator function that sets up the a context for the
1185 1185 # inner function.
1186 1186 def lookup_filenode_link_func(fname):
1187 1187 msngset = msng_filenode_set[fname]
1188 1188 # Lookup the changenode the filenode belongs to.
1189 1189 def lookup_filenode_link(fnode):
1190 1190 return msngset[fnode]
1191 1191 return lookup_filenode_link
1192 1192
1193 1193 # Now that we have all theses utility functions to help out and
1194 1194 # logically divide up the task, generate the group.
1195 1195 def gengroup():
1196 1196 # The set of changed files starts empty.
1197 1197 changedfiles = {}
1198 1198 # Create a changenode group generator that will call our functions
1199 1199 # back to lookup the owning changenode and collect information.
1200 1200 group = cl.group(msng_cl_lst, identity,
1201 1201 manifest_and_file_collector(changedfiles))
1202 1202 for chnk in group:
1203 1203 yield chnk
1204 1204
1205 1205 # The list of manifests has been collected by the generator
1206 1206 # calling our functions back.
1207 1207 prune_manifests()
1208 1208 msng_mnfst_lst = msng_mnfst_set.keys()
1209 1209 # Sort the manifestnodes by revision number.
1210 1210 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1211 1211 # Create a generator for the manifestnodes that calls our lookup
1212 1212 # and data collection functions back.
1213 1213 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1214 1214 filenode_collector(changedfiles))
1215 1215 for chnk in group:
1216 1216 yield chnk
1217 1217
1218 1218 # These are no longer needed, dereference and toss the memory for
1219 1219 # them.
1220 1220 msng_mnfst_lst = None
1221 1221 msng_mnfst_set.clear()
1222 1222
1223 1223 changedfiles = changedfiles.keys()
1224 1224 changedfiles.sort()
1225 1225 # Go through all our files in order sorted by name.
1226 1226 for fname in changedfiles:
1227 1227 filerevlog = self.file(fname)
1228 1228 # Toss out the filenodes that the recipient isn't really
1229 1229 # missing.
1230 1230 if msng_filenode_set.has_key(fname):
1231 1231 prune_filenodes(fname, filerevlog)
1232 1232 msng_filenode_lst = msng_filenode_set[fname].keys()
1233 1233 else:
1234 1234 msng_filenode_lst = []
1235 1235 # If any filenodes are left, generate the group for them,
1236 1236 # otherwise don't bother.
1237 1237 if len(msng_filenode_lst) > 0:
1238 1238 yield struct.pack(">l", len(fname) + 4) + fname
1239 1239 # Sort the filenodes by their revision #
1240 1240 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1241 1241 # Create a group generator and only pass in a changenode
1242 1242 # lookup function as we need to collect no information
1243 1243 # from filenodes.
1244 1244 group = filerevlog.group(msng_filenode_lst,
1245 1245 lookup_filenode_link_func(fname))
1246 1246 for chnk in group:
1247 1247 yield chnk
1248 1248 if msng_filenode_set.has_key(fname):
1249 1249 # Don't need this anymore, toss it to free memory.
1250 1250 del msng_filenode_set[fname]
1251 1251 # Signal that no more groups are left.
1252 1252 yield struct.pack(">l", 0)
1253 1253
1254 1254 return util.chunkbuffer(gengroup())
1255 1255
1256 1256 def changegroup(self, basenodes):
1257 1257 """Generate a changegroup of all nodes that we have that a recipient
1258 1258 doesn't.
1259 1259
1260 1260 This is much easier than the previous function as we can assume that
1261 1261 the recipient has any changenode we aren't sending them."""
1262 1262 cl = self.changelog
1263 1263 nodes = cl.nodesbetween(basenodes, None)[0]
1264 1264 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1265 1265
1266 1266 def identity(x):
1267 1267 return x
1268 1268
1269 1269 def gennodelst(revlog):
1270 1270 for r in xrange(0, revlog.count()):
1271 1271 n = revlog.node(r)
1272 1272 if revlog.linkrev(n) in revset:
1273 1273 yield n
1274 1274
1275 1275 def changed_file_collector(changedfileset):
1276 1276 def collect_changed_files(clnode):
1277 1277 c = cl.read(clnode)
1278 1278 for fname in c[3]:
1279 1279 changedfileset[fname] = 1
1280 1280 return collect_changed_files
1281 1281
1282 1282 def lookuprevlink_func(revlog):
1283 1283 def lookuprevlink(n):
1284 1284 return cl.node(revlog.linkrev(n))
1285 1285 return lookuprevlink
1286 1286
1287 1287 def gengroup():
1288 1288 # construct a list of all changed files
1289 1289 changedfiles = {}
1290 1290
1291 1291 for chnk in cl.group(nodes, identity,
1292 1292 changed_file_collector(changedfiles)):
1293 1293 yield chnk
1294 1294 changedfiles = changedfiles.keys()
1295 1295 changedfiles.sort()
1296 1296
1297 1297 mnfst = self.manifest
1298 1298 nodeiter = gennodelst(mnfst)
1299 1299 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1300 1300 yield chnk
1301 1301
1302 1302 for fname in changedfiles:
1303 1303 filerevlog = self.file(fname)
1304 1304 nodeiter = gennodelst(filerevlog)
1305 1305 nodeiter = list(nodeiter)
1306 1306 if nodeiter:
1307 1307 yield struct.pack(">l", len(fname) + 4) + fname
1308 1308 lookup = lookuprevlink_func(filerevlog)
1309 1309 for chnk in filerevlog.group(nodeiter, lookup):
1310 1310 yield chnk
1311 1311
1312 1312 yield struct.pack(">l", 0)
1313 1313
1314 1314 return util.chunkbuffer(gengroup())
1315 1315
1316 1316 def addchangegroup(self, source):
1317 1317
1318 1318 def getchunk():
1319 1319 d = source.read(4)
1320 1320 if not d:
1321 1321 return ""
1322 1322 l = struct.unpack(">l", d)[0]
1323 1323 if l <= 4:
1324 1324 return ""
1325 1325 d = source.read(l - 4)
1326 1326 if len(d) < l - 4:
1327 1327 raise repo.RepoError(_("premature EOF reading chunk"
1328 1328 " (got %d bytes, expected %d)")
1329 1329 % (len(d), l - 4))
1330 1330 return d
1331 1331
1332 1332 def getgroup():
1333 1333 while 1:
1334 1334 c = getchunk()
1335 1335 if not c:
1336 1336 break
1337 1337 yield c
1338 1338
1339 1339 def csmap(x):
1340 1340 self.ui.debug(_("add changeset %s\n") % short(x))
1341 1341 return self.changelog.count()
1342 1342
1343 1343 def revmap(x):
1344 1344 return self.changelog.rev(x)
1345 1345
1346 1346 if not source:
1347 1347 return
1348
1349 self.hook('prechangegroup', throw=True)
1350
1348 1351 changesets = files = revisions = 0
1349 1352
1350 1353 tr = self.transaction()
1351 1354
1352 1355 oldheads = len(self.changelog.heads())
1353 1356
1354 1357 # pull off the changeset group
1355 1358 self.ui.status(_("adding changesets\n"))
1356 1359 co = self.changelog.tip()
1357 1360 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1358 1361 cnr, cor = map(self.changelog.rev, (cn, co))
1359 1362 if cn == nullid:
1360 1363 cnr = cor
1361 1364 changesets = cnr - cor
1362 1365
1363 1366 # pull off the manifest group
1364 1367 self.ui.status(_("adding manifests\n"))
1365 1368 mm = self.manifest.tip()
1366 1369 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1367 1370
1368 1371 # process the files
1369 1372 self.ui.status(_("adding file changes\n"))
1370 1373 while 1:
1371 1374 f = getchunk()
1372 1375 if not f:
1373 1376 break
1374 1377 self.ui.debug(_("adding %s revisions\n") % f)
1375 1378 fl = self.file(f)
1376 1379 o = fl.count()
1377 1380 n = fl.addgroup(getgroup(), revmap, tr)
1378 1381 revisions += fl.count() - o
1379 1382 files += 1
1380 1383
1381 1384 newheads = len(self.changelog.heads())
1382 1385 heads = ""
1383 1386 if oldheads and newheads > oldheads:
1384 1387 heads = _(" (+%d heads)") % (newheads - oldheads)
1385 1388
1386 1389 self.ui.status(_("added %d changesets"
1387 1390 " with %d changes to %d files%s\n")
1388 1391 % (changesets, revisions, files, heads))
1389 1392
1393 self.hook('pretxnchangegroup', throw=True,
1394 node=hex(self.changelog.node(cor+1)))
1395
1390 1396 tr.close()
1391 1397
1392 1398 if changesets > 0:
1393 1399 self.hook("changegroup", node=hex(self.changelog.node(cor+1)))
1394 1400
1395 1401 for i in range(cor + 1, cnr + 1):
1396 1402 self.hook("incoming", node=hex(self.changelog.node(i)))
1397 1403
1398 1404 def update(self, node, allow=False, force=False, choose=None,
1399 1405 moddirstate=True, forcemerge=False, wlock=None):
1400 1406 pl = self.dirstate.parents()
1401 1407 if not force and pl[1] != nullid:
1402 1408 self.ui.warn(_("aborting: outstanding uncommitted merges\n"))
1403 1409 return 1
1404 1410
1405 1411 err = False
1406 1412
1407 1413 p1, p2 = pl[0], node
1408 1414 pa = self.changelog.ancestor(p1, p2)
1409 1415 m1n = self.changelog.read(p1)[0]
1410 1416 m2n = self.changelog.read(p2)[0]
1411 1417 man = self.manifest.ancestor(m1n, m2n)
1412 1418 m1 = self.manifest.read(m1n)
1413 1419 mf1 = self.manifest.readflags(m1n)
1414 1420 m2 = self.manifest.read(m2n).copy()
1415 1421 mf2 = self.manifest.readflags(m2n)
1416 1422 ma = self.manifest.read(man)
1417 1423 mfa = self.manifest.readflags(man)
1418 1424
1419 1425 modified, added, removed, deleted, unknown = self.changes()
1420 1426
1421 1427 # is this a jump, or a merge? i.e. is there a linear path
1422 1428 # from p1 to p2?
1423 1429 linear_path = (pa == p1 or pa == p2)
1424 1430
1425 1431 if allow and linear_path:
1426 1432 raise util.Abort(_("there is nothing to merge, "
1427 1433 "just use 'hg update'"))
1428 1434 if allow and not forcemerge:
1429 1435 if modified or added or removed:
1430 1436 raise util.Abort(_("outstanding uncommited changes"))
1431 1437 if not forcemerge and not force:
1432 1438 for f in unknown:
1433 1439 if f in m2:
1434 1440 t1 = self.wread(f)
1435 1441 t2 = self.file(f).read(m2[f])
1436 1442 if cmp(t1, t2) != 0:
1437 1443 raise util.Abort(_("'%s' already exists in the working"
1438 1444 " dir and differs from remote") % f)
1439 1445
1440 1446 # resolve the manifest to determine which files
1441 1447 # we care about merging
1442 1448 self.ui.note(_("resolving manifests\n"))
1443 1449 self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
1444 1450 (force, allow, moddirstate, linear_path))
1445 1451 self.ui.debug(_(" ancestor %s local %s remote %s\n") %
1446 1452 (short(man), short(m1n), short(m2n)))
1447 1453
1448 1454 merge = {}
1449 1455 get = {}
1450 1456 remove = []
1451 1457
1452 1458 # construct a working dir manifest
1453 1459 mw = m1.copy()
1454 1460 mfw = mf1.copy()
1455 1461 umap = dict.fromkeys(unknown)
1456 1462
1457 1463 for f in added + modified + unknown:
1458 1464 mw[f] = ""
1459 1465 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1460 1466
1461 1467 if moddirstate and not wlock:
1462 1468 wlock = self.wlock()
1463 1469
1464 1470 for f in deleted + removed:
1465 1471 if f in mw:
1466 1472 del mw[f]
1467 1473
1468 1474 # If we're jumping between revisions (as opposed to merging),
1469 1475 # and if neither the working directory nor the target rev has
1470 1476 # the file, then we need to remove it from the dirstate, to
1471 1477 # prevent the dirstate from listing the file when it is no
1472 1478 # longer in the manifest.
1473 1479 if moddirstate and linear_path and f not in m2:
1474 1480 self.dirstate.forget((f,))
1475 1481
1476 1482 # Compare manifests
1477 1483 for f, n in mw.iteritems():
1478 1484 if choose and not choose(f):
1479 1485 continue
1480 1486 if f in m2:
1481 1487 s = 0
1482 1488
1483 1489 # is the wfile new since m1, and match m2?
1484 1490 if f not in m1:
1485 1491 t1 = self.wread(f)
1486 1492 t2 = self.file(f).read(m2[f])
1487 1493 if cmp(t1, t2) == 0:
1488 1494 n = m2[f]
1489 1495 del t1, t2
1490 1496
1491 1497 # are files different?
1492 1498 if n != m2[f]:
1493 1499 a = ma.get(f, nullid)
1494 1500 # are both different from the ancestor?
1495 1501 if n != a and m2[f] != a:
1496 1502 self.ui.debug(_(" %s versions differ, resolve\n") % f)
1497 1503 # merge executable bits
1498 1504 # "if we changed or they changed, change in merge"
1499 1505 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1500 1506 mode = ((a^b) | (a^c)) ^ a
1501 1507 merge[f] = (m1.get(f, nullid), m2[f], mode)
1502 1508 s = 1
1503 1509 # are we clobbering?
1504 1510 # is remote's version newer?
1505 1511 # or are we going back in time?
1506 1512 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1507 1513 self.ui.debug(_(" remote %s is newer, get\n") % f)
1508 1514 get[f] = m2[f]
1509 1515 s = 1
1510 1516 elif f in umap:
1511 1517 # this unknown file is the same as the checkout
1512 1518 get[f] = m2[f]
1513 1519
1514 1520 if not s and mfw[f] != mf2[f]:
1515 1521 if force:
1516 1522 self.ui.debug(_(" updating permissions for %s\n") % f)
1517 1523 util.set_exec(self.wjoin(f), mf2[f])
1518 1524 else:
1519 1525 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1520 1526 mode = ((a^b) | (a^c)) ^ a
1521 1527 if mode != b:
1522 1528 self.ui.debug(_(" updating permissions for %s\n")
1523 1529 % f)
1524 1530 util.set_exec(self.wjoin(f), mode)
1525 1531 del m2[f]
1526 1532 elif f in ma:
1527 1533 if n != ma[f]:
1528 1534 r = _("d")
1529 1535 if not force and (linear_path or allow):
1530 1536 r = self.ui.prompt(
1531 1537 (_(" local changed %s which remote deleted\n") % f) +
1532 1538 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1533 1539 if r == _("d"):
1534 1540 remove.append(f)
1535 1541 else:
1536 1542 self.ui.debug(_("other deleted %s\n") % f)
1537 1543 remove.append(f) # other deleted it
1538 1544 else:
1539 1545 # file is created on branch or in working directory
1540 1546 if force and f not in umap:
1541 1547 self.ui.debug(_("remote deleted %s, clobbering\n") % f)
1542 1548 remove.append(f)
1543 1549 elif n == m1.get(f, nullid): # same as parent
1544 1550 if p2 == pa: # going backwards?
1545 1551 self.ui.debug(_("remote deleted %s\n") % f)
1546 1552 remove.append(f)
1547 1553 else:
1548 1554 self.ui.debug(_("local modified %s, keeping\n") % f)
1549 1555 else:
1550 1556 self.ui.debug(_("working dir created %s, keeping\n") % f)
1551 1557
1552 1558 for f, n in m2.iteritems():
1553 1559 if choose and not choose(f):
1554 1560 continue
1555 1561 if f[0] == "/":
1556 1562 continue
1557 1563 if f in ma and n != ma[f]:
1558 1564 r = _("k")
1559 1565 if not force and (linear_path or allow):
1560 1566 r = self.ui.prompt(
1561 1567 (_("remote changed %s which local deleted\n") % f) +
1562 1568 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1563 1569 if r == _("k"):
1564 1570 get[f] = n
1565 1571 elif f not in ma:
1566 1572 self.ui.debug(_("remote created %s\n") % f)
1567 1573 get[f] = n
1568 1574 else:
1569 1575 if force or p2 == pa: # going backwards?
1570 1576 self.ui.debug(_("local deleted %s, recreating\n") % f)
1571 1577 get[f] = n
1572 1578 else:
1573 1579 self.ui.debug(_("local deleted %s\n") % f)
1574 1580
1575 1581 del mw, m1, m2, ma
1576 1582
1577 1583 if force:
1578 1584 for f in merge:
1579 1585 get[f] = merge[f][1]
1580 1586 merge = {}
1581 1587
1582 1588 if linear_path or force:
1583 1589 # we don't need to do any magic, just jump to the new rev
1584 1590 branch_merge = False
1585 1591 p1, p2 = p2, nullid
1586 1592 else:
1587 1593 if not allow:
1588 1594 self.ui.status(_("this update spans a branch"
1589 1595 " affecting the following files:\n"))
1590 1596 fl = merge.keys() + get.keys()
1591 1597 fl.sort()
1592 1598 for f in fl:
1593 1599 cf = ""
1594 1600 if f in merge:
1595 1601 cf = _(" (resolve)")
1596 1602 self.ui.status(" %s%s\n" % (f, cf))
1597 1603 self.ui.warn(_("aborting update spanning branches!\n"))
1598 1604 self.ui.status(_("(use update -m to merge across branches"
1599 1605 " or -C to lose changes)\n"))
1600 1606 return 1
1601 1607 branch_merge = True
1602 1608
1603 1609 # get the files we don't need to change
1604 1610 files = get.keys()
1605 1611 files.sort()
1606 1612 for f in files:
1607 1613 if f[0] == "/":
1608 1614 continue
1609 1615 self.ui.note(_("getting %s\n") % f)
1610 1616 t = self.file(f).read(get[f])
1611 1617 self.wwrite(f, t)
1612 1618 util.set_exec(self.wjoin(f), mf2[f])
1613 1619 if moddirstate:
1614 1620 if branch_merge:
1615 1621 self.dirstate.update([f], 'n', st_mtime=-1)
1616 1622 else:
1617 1623 self.dirstate.update([f], 'n')
1618 1624
1619 1625 # merge the tricky bits
1620 1626 files = merge.keys()
1621 1627 files.sort()
1622 1628 for f in files:
1623 1629 self.ui.status(_("merging %s\n") % f)
1624 1630 my, other, flag = merge[f]
1625 1631 ret = self.merge3(f, my, other)
1626 1632 if ret:
1627 1633 err = True
1628 1634 util.set_exec(self.wjoin(f), flag)
1629 1635 if moddirstate:
1630 1636 if branch_merge:
1631 1637 # We've done a branch merge, mark this file as merged
1632 1638 # so that we properly record the merger later
1633 1639 self.dirstate.update([f], 'm')
1634 1640 else:
1635 1641 # We've update-merged a locally modified file, so
1636 1642 # we set the dirstate to emulate a normal checkout
1637 1643 # of that file some time in the past. Thus our
1638 1644 # merge will appear as a normal local file
1639 1645 # modification.
1640 1646 f_len = len(self.file(f).read(other))
1641 1647 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1642 1648
1643 1649 remove.sort()
1644 1650 for f in remove:
1645 1651 self.ui.note(_("removing %s\n") % f)
1646 1652 try:
1647 1653 util.unlink(self.wjoin(f))
1648 1654 except OSError, inst:
1649 1655 if inst.errno != errno.ENOENT:
1650 1656 self.ui.warn(_("update failed to remove %s: %s!\n") %
1651 1657 (f, inst.strerror))
1652 1658 if moddirstate:
1653 1659 if branch_merge:
1654 1660 self.dirstate.update(remove, 'r')
1655 1661 else:
1656 1662 self.dirstate.forget(remove)
1657 1663
1658 1664 if moddirstate:
1659 1665 self.dirstate.setparents(p1, p2)
1660 1666 return err
1661 1667
1662 1668 def merge3(self, fn, my, other):
1663 1669 """perform a 3-way merge in the working directory"""
1664 1670
1665 1671 def temp(prefix, node):
1666 1672 pre = "%s~%s." % (os.path.basename(fn), prefix)
1667 1673 (fd, name) = tempfile.mkstemp("", pre)
1668 1674 f = os.fdopen(fd, "wb")
1669 1675 self.wwrite(fn, fl.read(node), f)
1670 1676 f.close()
1671 1677 return name
1672 1678
1673 1679 fl = self.file(fn)
1674 1680 base = fl.ancestor(my, other)
1675 1681 a = self.wjoin(fn)
1676 1682 b = temp("base", base)
1677 1683 c = temp("other", other)
1678 1684
1679 1685 self.ui.note(_("resolving %s\n") % fn)
1680 1686 self.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
1681 1687 (fn, short(my), short(other), short(base)))
1682 1688
1683 1689 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1684 1690 or "hgmerge")
1685 1691 r = os.system('%s "%s" "%s" "%s"' % (cmd, a, b, c))
1686 1692 if r:
1687 1693 self.ui.warn(_("merging %s failed!\n") % fn)
1688 1694
1689 1695 os.unlink(b)
1690 1696 os.unlink(c)
1691 1697 return r
1692 1698
1693 1699 def verify(self):
1694 1700 filelinkrevs = {}
1695 1701 filenodes = {}
1696 1702 changesets = revisions = files = 0
1697 1703 errors = [0]
1698 1704 neededmanifests = {}
1699 1705
1700 1706 def err(msg):
1701 1707 self.ui.warn(msg + "\n")
1702 1708 errors[0] += 1
1703 1709
1704 1710 def checksize(obj, name):
1705 1711 d = obj.checksize()
1706 1712 if d[0]:
1707 1713 err(_("%s data length off by %d bytes") % (name, d[0]))
1708 1714 if d[1]:
1709 1715 err(_("%s index contains %d extra bytes") % (name, d[1]))
1710 1716
1711 1717 seen = {}
1712 1718 self.ui.status(_("checking changesets\n"))
1713 1719 checksize(self.changelog, "changelog")
1714 1720
1715 1721 for i in range(self.changelog.count()):
1716 1722 changesets += 1
1717 1723 n = self.changelog.node(i)
1718 1724 l = self.changelog.linkrev(n)
1719 1725 if l != i:
1720 1726 err(_("incorrect link (%d) for changeset revision %d") %(l, i))
1721 1727 if n in seen:
1722 1728 err(_("duplicate changeset at revision %d") % i)
1723 1729 seen[n] = 1
1724 1730
1725 1731 for p in self.changelog.parents(n):
1726 1732 if p not in self.changelog.nodemap:
1727 1733 err(_("changeset %s has unknown parent %s") %
1728 1734 (short(n), short(p)))
1729 1735 try:
1730 1736 changes = self.changelog.read(n)
1731 1737 except KeyboardInterrupt:
1732 1738 self.ui.warn(_("interrupted"))
1733 1739 raise
1734 1740 except Exception, inst:
1735 1741 err(_("unpacking changeset %s: %s") % (short(n), inst))
1736 1742
1737 1743 neededmanifests[changes[0]] = n
1738 1744
1739 1745 for f in changes[3]:
1740 1746 filelinkrevs.setdefault(f, []).append(i)
1741 1747
1742 1748 seen = {}
1743 1749 self.ui.status(_("checking manifests\n"))
1744 1750 checksize(self.manifest, "manifest")
1745 1751
1746 1752 for i in range(self.manifest.count()):
1747 1753 n = self.manifest.node(i)
1748 1754 l = self.manifest.linkrev(n)
1749 1755
1750 1756 if l < 0 or l >= self.changelog.count():
1751 1757 err(_("bad manifest link (%d) at revision %d") % (l, i))
1752 1758
1753 1759 if n in neededmanifests:
1754 1760 del neededmanifests[n]
1755 1761
1756 1762 if n in seen:
1757 1763 err(_("duplicate manifest at revision %d") % i)
1758 1764
1759 1765 seen[n] = 1
1760 1766
1761 1767 for p in self.manifest.parents(n):
1762 1768 if p not in self.manifest.nodemap:
1763 1769 err(_("manifest %s has unknown parent %s") %
1764 1770 (short(n), short(p)))
1765 1771
1766 1772 try:
1767 1773 delta = mdiff.patchtext(self.manifest.delta(n))
1768 1774 except KeyboardInterrupt:
1769 1775 self.ui.warn(_("interrupted"))
1770 1776 raise
1771 1777 except Exception, inst:
1772 1778 err(_("unpacking manifest %s: %s") % (short(n), inst))
1773 1779
1774 1780 ff = [ l.split('\0') for l in delta.splitlines() ]
1775 1781 for f, fn in ff:
1776 1782 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1777 1783
1778 1784 self.ui.status(_("crosschecking files in changesets and manifests\n"))
1779 1785
1780 1786 for m, c in neededmanifests.items():
1781 1787 err(_("Changeset %s refers to unknown manifest %s") %
1782 1788 (short(m), short(c)))
1783 1789 del neededmanifests
1784 1790
1785 1791 for f in filenodes:
1786 1792 if f not in filelinkrevs:
1787 1793 err(_("file %s in manifest but not in changesets") % f)
1788 1794
1789 1795 for f in filelinkrevs:
1790 1796 if f not in filenodes:
1791 1797 err(_("file %s in changeset but not in manifest") % f)
1792 1798
1793 1799 self.ui.status(_("checking files\n"))
1794 1800 ff = filenodes.keys()
1795 1801 ff.sort()
1796 1802 for f in ff:
1797 1803 if f == "/dev/null":
1798 1804 continue
1799 1805 files += 1
1800 1806 fl = self.file(f)
1801 1807 checksize(fl, f)
1802 1808
1803 1809 nodes = {nullid: 1}
1804 1810 seen = {}
1805 1811 for i in range(fl.count()):
1806 1812 revisions += 1
1807 1813 n = fl.node(i)
1808 1814
1809 1815 if n in seen:
1810 1816 err(_("%s: duplicate revision %d") % (f, i))
1811 1817 if n not in filenodes[f]:
1812 1818 err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
1813 1819 else:
1814 1820 del filenodes[f][n]
1815 1821
1816 1822 flr = fl.linkrev(n)
1817 1823 if flr not in filelinkrevs[f]:
1818 1824 err(_("%s:%s points to unexpected changeset %d")
1819 1825 % (f, short(n), flr))
1820 1826 else:
1821 1827 filelinkrevs[f].remove(flr)
1822 1828
1823 1829 # verify contents
1824 1830 try:
1825 1831 t = fl.read(n)
1826 1832 except KeyboardInterrupt:
1827 1833 self.ui.warn(_("interrupted"))
1828 1834 raise
1829 1835 except Exception, inst:
1830 1836 err(_("unpacking file %s %s: %s") % (f, short(n), inst))
1831 1837
1832 1838 # verify parents
1833 1839 (p1, p2) = fl.parents(n)
1834 1840 if p1 not in nodes:
1835 1841 err(_("file %s:%s unknown parent 1 %s") %
1836 1842 (f, short(n), short(p1)))
1837 1843 if p2 not in nodes:
1838 1844 err(_("file %s:%s unknown parent 2 %s") %
1839 1845 (f, short(n), short(p1)))
1840 1846 nodes[n] = 1
1841 1847
1842 1848 # cross-check
1843 1849 for node in filenodes[f]:
1844 1850 err(_("node %s in manifests not in %s") % (hex(node), f))
1845 1851
1846 1852 self.ui.status(_("%d files, %d changesets, %d total revisions\n") %
1847 1853 (files, changesets, revisions))
1848 1854
1849 1855 if errors[0]:
1850 1856 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
1851 1857 return 1
General Comments 0
You need to be logged in to leave comments. Login now