##// END OF EJS Templates
On Sat, Jul 02, 2005 at 02:11:34PM -0700, Matt Mackall wrote:...
Alecs King -
r612:9cd74543 default
parent child Browse files
Show More
@@ -1,560 +1,563 b''
1 1 HG(1)
2 2 =====
3 3 Matt Mackall <mpm@selenic.com>
4 4
5 5 NAME
6 6 ----
7 7 hg - Mercurial source code management system
8 8
9 9 SYNOPSIS
10 10 --------
11 11 'hg' [-v -d -q -y] <command> [command options] [files]
12 12
13 13 DESCRIPTION
14 14 -----------
15 15 The hg(1) command provides a command line interface to the Mercurial system.
16 16
17 17 OPTIONS
18 18 -------
19 19
20 20 --debug, -d::
21 21 enable debugging output
22 22
23 23 --quiet, -q::
24 24 suppress output
25 25
26 26 --verbose, -v::
27 27 enable additional output
28 28
29 29 --noninteractive, -y::
30 30 do not prompt, assume 'yes' for any required answers
31 31
32 32 COMMAND ELEMENTS
33 33 ----------------
34 34
35 35 files ...::
36 36 indicates one or more filename or relative path filenames
37 37
38 38 path::
39 39 indicates a path on the local machine
40 40
41 41 revision::
42 42 indicates a changeset which can be specified as a changeset revision
43 43 number, a tag, or a unique substring of the changeset hash value
44 44
45 45 repository path::
46 46 either the pathname of a local repository or the URI of a remote
47 47 repository. There are two available URI protocols, http:// which is
48 48 fast and the old-http:// protocol which is much slower but does not
49 49 require a special server on the web host.
50 50
51 51 COMMANDS
52 52 --------
53 53
54 54 add [files ...]::
55 55 Schedule files to be version controlled and added to the repository.
56 56
57 57 The files will be added to the repository at the next commit.
58 58
59 59 addremove::
60 60 Add all new files and remove all missing files from the repository.
61 61
62 62 New files are ignored if they match any of the patterns in .hgignore. As
63 63 with add, these changes take effect at the next commit.
64 64
65 65 annotate [-r <rev> -u -n -c] [files ...]::
66 66 List changes in files, showing the revision id responsible for each line
67 67
68 68 This command is useful to discover who did a change or when a change took
69 69 place.
70 70
71 71 options:
72 72 -r, --revision <rev> annotate the specified revision
73 73 -u, --user list the author
74 74 -c, --changeset list the changeset
75 75 -n, --number list the revision number (default)
76 76
77 77 cat <file> [revision]::
78 78 Output to stdout the given revision for the specified file.
79 79
80 80 If no revision is given then the tip is used.
81 81
82 82 clone [-U] <source> [dest]::
83 83 Create a copy of an existing repository in a new directory.
84 84
85 85 If no destination directory name is specified, it defaults to the
86 86 basename of the source.
87 87
88 88 The source is added to the new repository's .hg/hgrc file to be used in
89 89 future pulls.
90 90
91 91 For efficiency, hardlinks are used for cloning whenever the
92 92 source and destination are on the same filesystem.
93 93
94 94 options:
95 95 -U, --noupdate do not update the new working directory
96 96
97 97 commit [-A -t -l <file> -t <text> -u <user> -d <datecode>] [files...]::
98 98 Commit changes to the given files into the repository.
99 99
100 100 If a list of files is omitted, all changes reported by "hg status"
101 101 will be commited.
102 102
103 103 The HGEDITOR or EDITOR environment variables are used to start an
104 104 editor to add a commit comment.
105 105
106 106 Options:
107 107
108 108 -A, --addremove run addremove during commit
109 109 -t, --text <text> use <text> as commit message
110 110 -l, --logfile <file> show the commit message for the given file
111 111 -d, --date <datecode> record datecode as commit date
112 112 -u, --user <user> record user as commiter
113 113
114 114 aliases: ci
115 115
116 116 copy <source> <dest>::
117 117 Mark <dest> file as a copy or rename of a <source> one
118 118
119 119 This command takes effect for the next commit.
120 120
121 121 diff [-r revision] [-r revision] [files ...]::
122 122 Show differences between revisions for the specified files.
123 123
124 124 Differences between files are shown using the unified diff format.
125 125
126 126 When two revision arguments are given, then changes are shown
127 127 between those revisions. If only one revision is specified then
128 128 that revision is compared to the working directory, and, when no
129 129 revisions are specified, the working directory files are compared
130 130 to its parent.
131 131
132 132 export [-o filespec] [revision] ...::
133 133 Print the changeset header and diffs for one or more revisions.
134 134
135 135 The information shown in the changeset header is: author,
136 136 changeset hash, parent and commit comment.
137 137
138 138 Output may be to a file, in which case the name of the file is
139 139 given using a format string. The formatting rules are as follows:
140 140
141 141 %% literal "%" character
142 142 %H changeset hash (40 bytes of hexadecimal)
143 143 %N number of patches being generated
144 144 %R changeset revision number
145 145 %b basename of the exporting repository
146 146 %h short-form changeset hash (12 bytes of hexadecimal)
147 147 %n zero-padded sequence number, starting at 1
148 148 %r zero-padded changeset revision number
149 149
150 150 Options:
151 151
152 152 -o, --output <filespec> print output to file with formatted named
153 153
154 154 forget [files]::
155 155 Undo an 'hg add' scheduled for the next commit.
156 156
157 157 heads::
158 158 Show all repository head changesets.
159 159
160 160 Repository "heads" are changesets that don't have children
161 161 changesets. They are where development generally takes place and
162 162 are the usual targets for update and merge operations.
163 163
164 164 identify::
165 165 Print a short summary of the current state of the repo.
166 166
167 167 This summary identifies the repository state using one or two parent
168 168 hash identifiers, followed by a "+" if there are uncommitted changes
169 169 in the working directory, followed by a list of tags for this revision.
170 170
171 171 aliases: id
172 172
173 173 import [-p <n> -b <base> -q] <patches>::
174 174 Import a list of patches and commit them individually.
175 175
176 176 options:
177 177 -p, --strip <n> directory strip option for patch. This has the same
178 178 meaning as the correnponding patch option
179 179 -b <path> base directory to read patches from
180 180
181 181 aliases: patch
182 182
183 183 init::
184 184 Initialize a new repository in the current directory.
185 185
186 log [-r revision ...] [file]::
186 log [-r revision ...] [-p] [file]::
187 187 Print the revision history of the specified file or the entire project.
188 188
189 189 By default this command outputs: changeset id and hash, tags,
190 190 parents, user, date and time, and a summary for each commit. The
191 191 -v switch adds some more detail, such as changed files, manifest
192 192 hashes or message signatures.
193 193
194 When a revision argument is given, only this file or changelog revision
195 is displayed. With two revision arguments all revisions in this range
196 are listed. Additional revision arguments may be given repeating the above
197 cycle.
194 options:
195 -r, --rev <A>, ... When a revision argument is given, only this file or
196 changelog revision is displayed. With two revision
197 arguments all revisions in this range are listed.
198 Additional revision arguments may be given repeating
199 the above cycle.
200 -p, --patch show patch
198 201
199 202 aliases: history
200 203
201 204 manifest [revision]::
202 205 Print a list of version controlled files for the given revision.
203 206
204 207 The manifest is the list of files being version controlled. If no revision
205 208 is given then the tip is used.
206 209
207 210 parents::
208 211 Print the working directory's parent revisions.
209 212
210 213 pull <repository path>::
211 214 Pull changes from a remote repository to a local one.
212 215
213 216 This finds all changes from the repository at the specified path
214 217 or URL and adds them to the local repository. By default, this
215 218 does not update the copy of the project in the working directory.
216 219
217 220 options:
218 221 -u, --update update the working directory to tip after pull
219 222
220 223 push <destination>::
221 224 Push changes from the local repository to the given destination.
222 225
223 226 This is the symmetrical operation for pull. It helps to move
224 227 changes from the current repository to a different one. If the
225 228 destination is local this is identical to a pull in that directory
226 229 from the current one.
227 230
228 231 The other currently available push method is SSH. This requires an
229 232 accessible shell account on the destination machine and a copy of
230 233 hg in the remote path. Destinations are specified in the following
231 234 form:
232 235
233 236 ssh://[user@]host[:port]/path
234 237
235 238 rawcommit [-p -d -u -F -t -l]::
236 239 Lowlevel commit, for use in helper scripts.
237 240
238 241 This command is not intended to be used by normal users, as it is
239 242 primarily useful for importing from other SCMs.
240 243
241 244 recover::
242 245 Recover from an interrupted commit or pull.
243 246
244 247 This command tries to fix the repository status after an interrupted
245 248 operation. It should only be necessary when Mercurial suggests it.
246 249
247 250 remove [files ...]::
248 251 Schedule the indicated files for removal from the repository.
249 252
250 253 This command shedules the files to be removed at the next commit.
251 254 This only removes files from the current branch, not from the
252 255 entire project history.
253 256
254 257 aliases: rm
255 258
256 259 revert [names ...]::
257 260 Revert any uncommitted modifications made to the named files or
258 261 directories. This restores the contents of the affected files to
259 262 an unmodified state.
260 263
261 264 If a file has been deleted, it is recreated. If the executable
262 265 mode of a file was changed, it is reset.
263 266
264 267 If a directory is given, all files in that directory and its
265 268 subdirectories are reverted.
266 269
267 270 If no arguments are given, all files in the current directory and
268 271 its subdirectories are reverted.
269 272
270 273 options:
271 274 -r, --rev <rev> revision to revert to
272 275 -n, --nonrecursive do not recurse into subdirectories
273 276
274 277 root::
275 278 Print the root directory of the current repository.
276 279
277 280 serve [options]::
278 281 Start a local HTTP repository browser and pull server.
279 282
280 283 By default, the server logs accesses to stdout and errors to
281 284 stderr. Use the "-A" and "-E" options to log to files.
282 285
283 286 options:
284 287 -A, --accesslog <file> name of access log file to write to
285 288 -E, --errorlog <file> name of error log file to write to
286 289 -a, --address <addr> address to use
287 290 -p, --port <n> port to use (default: 8000)
288 291 -n, --name <name> name to show in web pages (default: working dir)
289 292 -t, --templatedir <path> web templates to use
290 293
291 294 status::
292 295 Show changed files in the working directory.
293 296
294 297 The codes used to show the status of files are:
295 298
296 299 C = changed
297 300 A = added
298 301 R = removed
299 302 ? = not tracked
300 303
301 304 tag [-t <text> -d <datecode> -u <user>] <name> [revision]::
302 305 Name a particular revision using <name>.
303 306
304 307 Tags are used to name particular revisions of the repository and are
305 308 very useful to compare different revision, to go back to significant
306 309 earlier versions or to mark branch points as releases, etc.
307 310
308 311 If no revision is given, the tip is used.
309 312
310 313 To facilitate version control, distribution, and merging of tags,
311 314 they are stored as a file named ".hgtags" which is managed
312 315 similarly to other project files and can be hand-edited if
313 316 necessary.
314 317
315 318 options:
316 319 -t, --text <text> message for tag commit log entry
317 320 -d, --date <datecode> datecode for commit
318 321 -u, --user <user> user for commit
319 322
320 323 Note: Mercurial also has support for "local tags" that are not
321 324 version-controlled or distributed which are stored in the .hg/hgrc
322 325 file.
323 326
324 327 tags::
325 328 List the repository tags.
326 329
327 330 This lists both regular and local tags.
328 331
329 332 tip::
330 333 Show the tip revision.
331 334
332 335 undo::
333 336 Undo the last commit or pull transaction.
334 337
335 338 Roll back the last pull or commit transaction on the
336 339 repository, restoring the project to its earlier state.
337 340
338 341 This command should be used with care. There is only one level of
339 342 undo and there is no redo.
340 343
341 344 This command is not intended for use on public repositories. Once
342 345 a change is visible for pull by other users, undoing it locally is
343 346 ineffective.
344 347
345 348 update [-m -C] [revision]::
346 349 Update the working directory to the specified revision.
347 350
348 351 By default, update will refuse to run if doing so would require
349 352 merging or discarding local changes.
350 353
351 354 With the -m option, a merge will be performed.
352 355
353 356 With the -C option, local changes will be lost.
354 357
355 358 options:
356 359 -m, --merge allow merging of branches
357 360 -C, --clean overwrite locally modified files
358 361
359 362 aliases: up checkout co
360 363
361 364 verify::
362 365 Verify the integrity of the current repository.
363 366
364 367 This will perform an extensive check of the repository's
365 368 integrity, validating the hashes and checksums of each entry in
366 369 the changelog, manifest, and tracked files, as well as the
367 370 integrity of their crosslinks and indices.
368 371
369 372 SPECIFYING SINGLE REVISIONS
370 373 ---------------------------
371 374
372 375 Mercurial accepts several notations for identifying individual
373 376 revisions.
374 377
375 378 A plain integer is treated as a revision number. Negative
376 379 integers are treated as offsets from the tip, with -1 denoting the
377 380 tip.
378 381
379 382 A 40-digit hexadecimal string is treated as a unique revision
380 383 identifier.
381 384
382 385 A hexadecimal string less than 40 characters long is treated as a
383 386 unique revision identifier, and referred to as a short-form
384 387 identifier. A short-form identifier is only valid if it is the
385 388 prefix of one full-length identifier.
386 389
387 390 Any other string is treated as a tag name, which is a symbolic
388 391 name associated with a revision identifier. Tag names may not
389 392 contain the ":" character.
390 393
391 394 The reserved name "tip" is a special tag that always identifies
392 395 the most recent revision.
393 396
394 397 SPECIFYING MULTIPLE REVISIONS
395 398 -----------------------------
396 399
397 400 When Mercurial accepts more than one revision, they may be
398 401 specified individually, or provided as a continuous range,
399 402 separated by the ":" character.
400 403
401 404 The syntax of range notation is [BEGIN]:[END], where BEGIN and END
402 405 are revision identifiers. Both BEGIN and END are optional. If
403 406 BEGIN is not specified, it defaults to revision number 0. If END
404 407 is not specified, it defaults to the tip. The range ":" thus
405 408 means "all revisions".
406 409
407 410 If BEGIN is greater than END, revisions are treated in reverse
408 411 order.
409 412
410 413 A range acts as an open interval. This means that a range of 3:5
411 414 gives 3, 4 and 5. Similarly, a range of 4:2 gives 4, 3, and 2.
412 415
413 416 ENVIRONMENT VARIABLES
414 417 ---------------------
415 418
416 419 HGEDITOR::
417 420 This is the name of the editor to use when committing. Defaults to the
418 421 value of EDITOR.
419 422
420 423 (deprecated, use .hgrc)
421 424
422 425 HGMERGE::
423 426 An executable to use for resolving merge conflicts. The program
424 427 will be executed with three arguments: local file, remote file,
425 428 ancestor file.
426 429
427 430 The default program is "hgmerge", which is a shell script provided
428 431 by Mercurial with some sensible defaults.
429 432
430 433 (deprecated, use .hgrc)
431 434
432 435 HGUSER::
433 436 This is the string used for the author of a commit.
434 437
435 438 (deprecated, use .hgrc)
436 439
437 440 EMAIL::
438 441 If HGUSER is not set, this will be used as the author for a commit.
439 442
440 443 LOGNAME::
441 444 If neither HGUSER nor EMAIL is set, LOGNAME will be used (with
442 445 '@hostname' appended) as the author value for a commit.
443 446
444 447 EDITOR::
445 448 This is the name of the editor used in the hgmerge script. It will be
446 449 used for commit messages if HGEDITOR isn't set. Defaults to 'vi'.
447 450
448 451 PYTHONPATH::
449 452 This is used by Python to find imported modules and may need to be set
450 453 appropriately if Mercurial is not installed system-wide.
451 454
452 455 FILES
453 456 -----
454 457 .hgignore::
455 458 This file contains regular expressions (one per line) that describe file
456 459 names that should be ignored by hg.
457 460
458 461 .hgtags::
459 462 This file contains changeset hash values and text tag names (one of each
460 463 seperated by spaces) that correspond to tagged versions of the repository
461 464 contents.
462 465
463 466 $HOME/.hgrc, .hg/hgrc::
464 467 This file contains defaults and configuration. Values in .hg/hgrc
465 468 override those in .hgrc.
466 469
467 470
468 471 UI OPTIONS
469 472 ----------
470 473
471 474 Various configuration options can be set in .hgrc:
472 475
473 476 -------------
474 477 [ui]
475 478 verbose = 0
476 479 username = Matt Mackall <mpm@selenic.com>
477 480 editor = hgeditor
478 481 merge = hgmerge
479 482 -------------
480 483
481 484
482 485 NAMED REPOSITORIES
483 486 ------------------
484 487
485 488 To give symbolic names to a repository, create a section in .hgrc
486 489 or .hg/hgrc containing assignments of names to paths. Example:
487 490
488 491 -----------------
489 492 [paths]
490 493 hg = http://selenic.com/hg
491 494 tah = http://hg.intevation.org/mercurial-tah/
492 495 -----------------
493 496
494 497
495 498 LOCAL TAGS
496 499 ----------
497 500
498 501 To create tags that are local to the repository and not distributed or
499 502 version-controlled, create an hgrc section like the following:
500 503
501 504 ----------------
502 505 [tags]
503 506 working = 2dcced388cab3677a8f543c3c47a0ad34ac9d435
504 507 tested = 12e0fdbc57a0be78f0e817fd1d170a3615cd35da
505 508 ----------------
506 509
507 510
508 511 HOOKS
509 512 -----
510 513
511 514 Mercurial supports a set of 'hook', commands that get automatically
512 515 executed by various actions such as starting or finishing a commit. To
513 516 specify a hook, simply create an hgrc section like the following:
514 517
515 518 -----------------
516 519 [hooks]
517 520 precommit = echo "this hook gets executed immediately before a commit"
518 521 commit = hg export $NODE | mail -s "new commit $NODE" commit-list
519 522 -----------------
520 523
521 524
522 525 NON_TRANSPARENT PROXY SUPPORT
523 526 -----------------------------
524 527
525 528 To access a Mercurial repository through a proxy, create a file
526 529 $HOME/.hgrc in the following format:
527 530
528 531 --------------
529 532 [http_proxy]
530 533 host=myproxy:8080
531 534 user=<username>
532 535 passwd=<password>
533 536 no=<localhost1>,<localhost2>,<localhost3>,...
534 537 --------------
535 538
536 539 "user" and "passwd" fields are used for authenticating proxies, "no" is a
537 540 comma-separated list of local host names to not proxy.
538 541
539 542 BUGS
540 543 ----
541 544 Probably lots, please post them to the mailing list (See Resources below)
542 545 when you find them.
543 546
544 547 AUTHOR
545 548 ------
546 549 Written by Matt Mackall <mpm@selenic.com>
547 550
548 551 RESOURCES
549 552 ---------
550 553 http://selenic.com/mercurial[Main Web Site]
551 554
552 555 http://selenic.com/hg[Source code repository]
553 556
554 557 http://selenic.com/mailman/listinfo/mercurial[Mailing list]
555 558
556 559 COPYING
557 560 -------
558 561 Copyright (C) 2005 Matt Mackall.
559 562 Free use of this software is granted under the terms of the GNU General
560 563 Public License (GPL).
@@ -1,1173 +1,1185 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 import os, re, sys, signal
9 9 import fancyopts, ui, hg, util
10 10 from demandload import *
11 11 demandload(globals(), "mdiff time hgweb traceback random signal")
12 12 demandload(globals(), "socket errno version")
13 13
14 14 class UnknownCommand(Exception): pass
15 15
16 16 def filterfiles(filters, files):
17 17 l = [ x for x in files if x in filters ]
18 18
19 19 for t in filters:
20 20 if t and t[-1] != "/": t += "/"
21 21 l += [ x for x in files if x.startswith(t) ]
22 22 return l
23 23
24 24 def relfilter(repo, files):
25 25 if os.getcwd() != repo.root:
26 26 p = os.getcwd()[len(repo.root) + 1: ]
27 27 return filterfiles([util.pconvert(p)], files)
28 28 return files
29 29
30 30 def relpath(repo, args):
31 31 if os.getcwd() != repo.root:
32 32 p = os.getcwd()[len(repo.root) + 1: ]
33 33 return [ util.pconvert(os.path.normpath(os.path.join(p, x)))
34 34 for x in args ]
35 35 return args
36 36
37 37 revrangesep = ':'
38 38
39 39 def revrange(ui, repo, revs = [], revlog = None):
40 40 if revlog is None:
41 41 revlog = repo.changelog
42 42 revcount = revlog.count()
43 43 def fix(val, defval):
44 44 if not val: return defval
45 45 try:
46 46 num = int(val)
47 47 if str(num) != val: raise ValueError
48 48 if num < 0: num += revcount
49 49 if not (0 <= num < revcount):
50 50 raise ValueError
51 51 except ValueError:
52 52 try:
53 53 num = repo.changelog.rev(repo.lookup(val))
54 54 except KeyError:
55 55 try:
56 56 num = revlog.rev(revlog.lookup(val))
57 57 except KeyError:
58 58 ui.warn('abort: invalid revision identifier %s\n' % val)
59 59 sys.exit(1)
60 60 return num
61 61 for spec in revs:
62 62 if spec.find(revrangesep) >= 0:
63 63 start, end = spec.split(revrangesep, 1)
64 64 start = fix(start, 0)
65 65 end = fix(end, revcount - 1)
66 66 if end > start:
67 67 end += 1
68 68 step = 1
69 69 else:
70 70 end -= 1
71 71 step = -1
72 72 for rev in xrange(start, end, step):
73 73 yield str(rev)
74 74 else:
75 75 yield spec
76 76
77 77 def dodiff(fp, ui, repo, files = None, node1 = None, node2 = None):
78 78 def date(c):
79 79 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
80 80
81 81 (c, a, d, u) = repo.changes(node1, node2, files)
82 82 if files:
83 83 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
84 84
85 85 if not c and not a and not d:
86 86 return
87 87
88 88 if node2:
89 89 change = repo.changelog.read(node2)
90 90 mmap2 = repo.manifest.read(change[0])
91 91 def read(f): return repo.file(f).read(mmap2[f])
92 92 date2 = date(change)
93 93 else:
94 94 date2 = time.asctime()
95 95 if not node1:
96 96 node1 = repo.dirstate.parents()[0]
97 97 def read(f): return repo.wfile(f).read()
98 98
99 99 if ui.quiet:
100 100 r = None
101 101 else:
102 102 hexfunc = ui.verbose and hg.hex or hg.short
103 103 r = [hexfunc(node) for node in [node1, node2] if node]
104 104
105 105 change = repo.changelog.read(node1)
106 106 mmap = repo.manifest.read(change[0])
107 107 date1 = date(change)
108 108
109 109 for f in c:
110 110 to = None
111 111 if f in mmap:
112 112 to = repo.file(f).read(mmap[f])
113 113 tn = read(f)
114 114 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
115 115 for f in a:
116 116 to = None
117 117 tn = read(f)
118 118 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
119 119 for f in d:
120 120 to = repo.file(f).read(mmap[f])
121 121 tn = None
122 122 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
123 123
124 124 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
125 125 """show a single changeset or file revision"""
126 126 changelog = repo.changelog
127 127 if filelog:
128 128 log = filelog
129 129 filerev = rev
130 130 node = filenode = filelog.node(filerev)
131 131 changerev = filelog.linkrev(filenode)
132 132 changenode = changenode or changelog.node(changerev)
133 133 else:
134 134 log = changelog
135 135 changerev = rev
136 136 if changenode is None:
137 137 changenode = changelog.node(changerev)
138 138 elif not changerev:
139 139 rev = changerev = changelog.rev(changenode)
140 140 node = changenode
141 141
142 142 if ui.quiet:
143 143 ui.write("%d:%s\n" % (rev, hg.hex(node)))
144 144 return
145 145
146 146 changes = changelog.read(changenode)
147 147
148 148 parents = [(log.rev(parent), hg.hex(parent))
149 149 for parent in log.parents(node)
150 150 if ui.debugflag or parent != hg.nullid]
151 151 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
152 152 parents = []
153 153
154 154 if filelog:
155 155 ui.write("revision: %d:%s\n" % (filerev, hg.hex(filenode)))
156 156 for parent in parents:
157 157 ui.write("parent: %d:%s\n" % parent)
158 158 ui.status("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
159 159 else:
160 160 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
161 161 for tag in repo.nodetags(changenode):
162 162 ui.status("tag: %s\n" % tag)
163 163 for parent in parents:
164 164 ui.write("parent: %d:%s\n" % parent)
165 165 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
166 166 hg.hex(changes[0])))
167 167 ui.status("user: %s\n" % changes[1])
168 168 ui.status("date: %s\n" % time.asctime(
169 169 time.localtime(float(changes[2].split(' ')[0]))))
170 170 if ui.debugflag:
171 171 files = repo.changes(changelog.parents(changenode)[0], changenode)
172 172 for key, value in zip(["files:", "files+:", "files-:"], files):
173 173 if value:
174 174 ui.note("%-12s %s\n" % (key, " ".join(value)))
175 175 else:
176 176 ui.note("files: %s\n" % " ".join(changes[3]))
177 177 description = changes[4].strip()
178 178 if description:
179 179 if ui.verbose:
180 180 ui.status("description:\n")
181 181 ui.status(description)
182 182 ui.status("\n\n")
183 183 else:
184 184 ui.status("summary: %s\n" % description.splitlines()[0])
185 185 ui.status("\n")
186 186
187 187 def show_version(ui):
188 188 """output version and copyright information"""
189 189 ui.write("Mercurial version %s\n" % version.get_version())
190 190 ui.status(
191 191 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
192 192 "This is free software; see the source for copying conditions. "
193 193 "There is NO\nwarranty; "
194 194 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
195 195 )
196 196
197 197 def help(ui, cmd=None):
198 198 '''show help for a given command or all commands'''
199 199 if cmd:
200 200 try:
201 201 i = find(cmd)
202 202 ui.write("%s\n\n" % i[2])
203 203
204 204 if i[1]:
205 205 for s, l, d, c in i[1]:
206 206 opt=' '
207 207 if s: opt = opt + '-' + s + ' '
208 208 if l: opt = opt + '--' + l + ' '
209 209 if d: opt = opt + '(' + str(d) + ')'
210 210 ui.write(opt, "\n")
211 211 if c: ui.write(' %s\n' % c)
212 212 ui.write("\n")
213 213
214 214 ui.write(i[0].__doc__, "\n")
215 215 except UnknownCommand:
216 216 ui.warn("hg: unknown command %s\n" % cmd)
217 217 sys.exit(0)
218 218 else:
219 219 if ui.verbose:
220 220 show_version(ui)
221 221 ui.write('\n')
222 222 if ui.verbose:
223 223 ui.write('hg commands:\n\n')
224 224 else:
225 225 ui.write('basic hg commands (use "hg help -v" for more):\n\n')
226 226
227 227 h = {}
228 228 for c, e in table.items():
229 229 f = c.split("|")[0]
230 230 if not ui.verbose and not f.startswith("^"):
231 231 continue
232 232 if not ui.debugflag and f.startswith("debug"):
233 233 continue
234 234 f = f.lstrip("^")
235 235 d = ""
236 236 if e[0].__doc__:
237 237 d = e[0].__doc__.splitlines(0)[0].rstrip()
238 238 h[f] = d
239 239
240 240 fns = h.keys()
241 241 fns.sort()
242 242 m = max(map(len, fns))
243 243 for f in fns:
244 244 ui.write(' %-*s %s\n' % (m, f, h[f]))
245 245
246 246 # Commands start here, listed alphabetically
247 247
248 248 def add(ui, repo, file, *files):
249 249 '''add the specified files on the next commit'''
250 250 repo.add(relpath(repo, (file,) + files))
251 251
252 252 def addremove(ui, repo, *files):
253 253 """add all new files, delete all missing files"""
254 254 if files:
255 255 files = relpath(repo, files)
256 256 d = []
257 257 u = []
258 258 for f in files:
259 259 p = repo.wjoin(f)
260 260 s = repo.dirstate.state(f)
261 261 isfile = os.path.isfile(p)
262 262 if s != 'r' and not isfile:
263 263 d.append(f)
264 264 elif s not in 'nmai' and isfile:
265 265 u.append(f)
266 266 else:
267 267 (c, a, d, u) = repo.changes(None, None)
268 268 repo.add(u)
269 269 repo.remove(d)
270 270
271 271 def annotate(u, repo, file, *files, **ops):
272 272 """show changeset information per file line"""
273 273 def getnode(rev):
274 274 return hg.short(repo.changelog.node(rev))
275 275
276 276 def getname(rev):
277 277 try:
278 278 return bcache[rev]
279 279 except KeyError:
280 280 cl = repo.changelog.read(repo.changelog.node(rev))
281 281 name = cl[1]
282 282 f = name.find('@')
283 283 if f >= 0:
284 284 name = name[:f]
285 285 f = name.find('<')
286 286 if f >= 0:
287 287 name = name[f+1:]
288 288 bcache[rev] = name
289 289 return name
290 290
291 291 bcache = {}
292 292 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
293 293 if not ops['user'] and not ops['changeset']:
294 294 ops['number'] = 1
295 295
296 296 node = repo.dirstate.parents()[0]
297 297 if ops['revision']:
298 298 node = repo.changelog.lookup(ops['revision'])
299 299 change = repo.changelog.read(node)
300 300 mmap = repo.manifest.read(change[0])
301 301 for f in relpath(repo, (file,) + files):
302 302 lines = repo.file(f).annotate(mmap[f])
303 303 pieces = []
304 304
305 305 for o, f in opmap:
306 306 if ops[o]:
307 307 l = [ f(n) for n,t in lines ]
308 308 m = max(map(len, l))
309 309 pieces.append([ "%*s" % (m, x) for x in l])
310 310
311 311 for p,l in zip(zip(*pieces), lines):
312 312 u.write(" ".join(p) + ": " + l[1])
313 313
314 314 def cat(ui, repo, file, rev = []):
315 315 """output the latest or given revision of a file"""
316 316 r = repo.file(relpath(repo, [file])[0])
317 317 n = r.tip()
318 318 if rev: n = r.lookup(rev)
319 319 sys.stdout.write(r.read(n))
320 320
321 321 def clone(ui, source, dest = None, **opts):
322 322 """make a copy of an existing repository"""
323 323 source = ui.expandpath(source)
324 324
325 325 if dest is None:
326 326 dest = os.path.basename(os.path.normpath(source))
327 327
328 328 if os.path.exists(dest):
329 329 ui.warn("abort: destination '%s' already exists\n" % dest)
330 330 return 1
331 331
332 332 class dircleanup:
333 333 def __init__(self, dir):
334 334 self.dir = dir
335 335 os.mkdir(dir)
336 336 def close(self):
337 337 self.dir = None
338 338 def __del__(self):
339 339 if self.dir:
340 340 import shutil
341 341 shutil.rmtree(self.dir, True)
342 342
343 343 d = dircleanup(dest)
344 344
345 345 link = 0
346 346 abspath = source
347 347 if not (source.startswith("http://") or
348 348 source.startswith("hg://") or
349 349 source.startswith("old-http://")):
350 350 abspath = os.path.abspath(source)
351 351 d1 = os.stat(dest).st_dev
352 352 d2 = os.stat(source).st_dev
353 353 if d1 == d2: link = 1
354 354
355 355 if link:
356 356 ui.note("copying by hardlink\n")
357 357 util.system("cp -al '%s'/.hg '%s'/.hg" % (source, dest))
358 358 try:
359 359 os.remove(os.path.join(dest, ".hg", "dirstate"))
360 360 except: pass
361 361
362 362 repo = hg.repository(ui, dest)
363 363
364 364 else:
365 365 repo = hg.repository(ui, dest, create=1)
366 366 other = hg.repository(ui, source)
367 367 fetch = repo.findincoming(other)
368 368 if fetch:
369 369 cg = other.changegroup(fetch)
370 370 repo.addchangegroup(cg)
371 371
372 372 f = repo.opener("hgrc", "w")
373 373 f.write("[paths]\n")
374 374 f.write("default = %s\n" % abspath)
375 375
376 376 if not opts['noupdate']:
377 377 update(ui, repo)
378 378
379 379 d.close()
380 380
381 381 def commit(ui, repo, *files, **opts):
382 382 """commit the specified files or all outstanding changes"""
383 383 text = opts['text']
384 384 if not text and opts['logfile']:
385 385 try: text = open(opts['logfile']).read()
386 386 except IOError: pass
387 387
388 388 if opts['addremove']:
389 389 addremove(ui, repo, *files)
390 390 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
391 391
392 392 def copy(ui, repo, source, dest):
393 393 """mark a file as copied or renamed for the next commit"""
394 394 return repo.copy(*relpath(repo, (source, dest)))
395 395
396 396 def debugcheckstate(ui, repo):
397 397 """validate the correctness of the current dirstate"""
398 398 parent1, parent2 = repo.dirstate.parents()
399 399 repo.dirstate.read()
400 400 dc = repo.dirstate.map
401 401 keys = dc.keys()
402 402 keys.sort()
403 403 m1n = repo.changelog.read(parent1)[0]
404 404 m2n = repo.changelog.read(parent2)[0]
405 405 m1 = repo.manifest.read(m1n)
406 406 m2 = repo.manifest.read(m2n)
407 407 errors = 0
408 408 for f in dc:
409 409 state = repo.dirstate.state(f)
410 410 if state in "nr" and f not in m1:
411 411 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
412 412 errors += 1
413 413 if state in "a" and f in m1:
414 414 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
415 415 errors += 1
416 416 if state in "m" and f not in m1 and f not in m2:
417 417 ui.warn("%s in state %s, but not in either manifest\n" %
418 418 (f, state))
419 419 errors += 1
420 420 for f in m1:
421 421 state = repo.dirstate.state(f)
422 422 if state not in "nrm":
423 423 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
424 424 errors += 1
425 425 if errors:
426 426 ui.warn(".hg/dirstate inconsistent with current parent's manifest\n")
427 427 sys.exit(1)
428 428
429 429 def debugstate(ui, repo):
430 430 """show the contents of the current dirstate"""
431 431 repo.dirstate.read()
432 432 dc = repo.dirstate.map
433 433 keys = dc.keys()
434 434 keys.sort()
435 435 for file in keys:
436 436 ui.write("%c %s\n" % (dc[file][0], file))
437 437
438 438 def debugindex(ui, file):
439 439 """dump the contents of an index file"""
440 440 r = hg.revlog(hg.opener(""), file, "")
441 441 ui.write(" rev offset length base linkrev" +
442 442 " p1 p2 nodeid\n")
443 443 for i in range(r.count()):
444 444 e = r.index[i]
445 445 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
446 446 i, e[0], e[1], e[2], e[3],
447 447 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
448 448
449 449 def debugindexdot(ui, file):
450 450 """dump an index DAG as a .dot file"""
451 451 r = hg.revlog(hg.opener(""), file, "")
452 452 ui.write("digraph G {\n")
453 453 for i in range(r.count()):
454 454 e = r.index[i]
455 455 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
456 456 if e[5] != hg.nullid:
457 457 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
458 458 ui.write("}\n")
459 459
460 460 def diff(ui, repo, *files, **opts):
461 461 """diff working directory (or selected files)"""
462 462 revs = []
463 463 if opts['rev']:
464 464 revs = map(lambda x: repo.lookup(x), opts['rev'])
465 465
466 466 if len(revs) > 2:
467 467 ui.warn("too many revisions to diff\n")
468 468 sys.exit(1)
469 469
470 470 if files:
471 471 files = relpath(repo, files)
472 472 else:
473 473 files = relpath(repo, [""])
474 474
475 475 dodiff(sys.stdout, ui, repo, files, *revs)
476 476
477 477 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
478 478 node = repo.lookup(changeset)
479 479 prev, other = repo.changelog.parents(node)
480 480 change = repo.changelog.read(node)
481 481
482 482 def expand(name):
483 483 expansions = {
484 484 '%': lambda: '%',
485 485 'H': lambda: hg.hex(node),
486 486 'N': lambda: str(total),
487 487 'R': lambda: str(repo.changelog.rev(node)),
488 488 'b': lambda: os.path.basename(repo.root),
489 489 'h': lambda: hg.short(node),
490 490 'n': lambda: str(seqno).zfill(len(str(total))),
491 491 'r': lambda: str(repo.changelog.rev(node)).zfill(revwidth),
492 492 }
493 493 newname = []
494 494 namelen = len(name)
495 495 i = 0
496 496 while i < namelen:
497 497 c = name[i]
498 498 if c == '%':
499 499 i += 1
500 500 c = name[i]
501 501 c = expansions[c]()
502 502 newname.append(c)
503 503 i += 1
504 504 return ''.join(newname)
505 505
506 506 if opts['output'] and opts['output'] != '-':
507 507 try:
508 508 fp = open(expand(opts['output']), 'w')
509 509 except KeyError, inst:
510 510 ui.warn("error: invalid format spec '%%%s' in output file name\n" %
511 511 inst.args[0])
512 512 sys.exit(1)
513 513 else:
514 514 fp = sys.stdout
515 515
516 516 fp.write("# HG changeset patch\n")
517 517 fp.write("# User %s\n" % change[1])
518 518 fp.write("# Node ID %s\n" % hg.hex(node))
519 519 fp.write("# Parent %s\n" % hg.hex(prev))
520 520 if other != hg.nullid:
521 521 fp.write("# Parent %s\n" % hg.hex(other))
522 522 fp.write(change[4].rstrip())
523 523 fp.write("\n\n")
524 524
525 525 dodiff(fp, ui, repo, None, prev, node)
526 526
527 527 def export(ui, repo, *changesets, **opts):
528 528 """dump the header and diffs for one or more changesets"""
529 529 if not changesets:
530 530 ui.warn("error: export requires at least one changeset\n")
531 531 sys.exit(1)
532 532 seqno = 0
533 533 revs = list(revrange(ui, repo, changesets))
534 534 total = len(revs)
535 535 revwidth = max(len(revs[0]), len(revs[-1]))
536 536 for cset in revs:
537 537 seqno += 1
538 538 doexport(ui, repo, cset, seqno, total, revwidth, opts)
539 539
540 540 def forget(ui, repo, file, *files):
541 541 """don't add the specified files on the next commit"""
542 542 repo.forget(relpath(repo, (file,) + files))
543 543
544 544 def heads(ui, repo):
545 545 """show current repository heads"""
546 546 for n in repo.changelog.heads():
547 547 show_changeset(ui, repo, changenode=n)
548 548
549 549 def identify(ui, repo):
550 550 """print information about the working copy"""
551 551 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
552 552 if not parents:
553 553 ui.write("unknown\n")
554 554 return
555 555
556 556 hexfunc = ui.verbose and hg.hex or hg.short
557 557 (c, a, d, u) = repo.changes(None, None)
558 558 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
559 559 (c or a or d) and "+" or "")]
560 560
561 561 if not ui.quiet:
562 562 # multiple tags for a single parent separated by '/'
563 563 parenttags = ['/'.join(tags)
564 564 for tags in map(repo.nodetags, parents) if tags]
565 565 # tags for multiple parents separated by ' + '
566 566 output.append(' + '.join(parenttags))
567 567
568 568 ui.write("%s\n" % ' '.join(output))
569 569
570 570 def import_(ui, repo, patch1, *patches, **opts):
571 571 """import an ordered set of patches"""
572 572 try:
573 573 import psyco
574 574 psyco.full()
575 575 except:
576 576 pass
577 577
578 578 patches = (patch1,) + patches
579 579
580 580 d = opts["base"]
581 581 strip = opts["strip"]
582 582
583 583 for patch in patches:
584 584 ui.status("applying %s\n" % patch)
585 585 pf = os.path.join(d, patch)
586 586
587 587 text = ""
588 588 for l in file(pf):
589 589 if l[:4] == "--- ": break
590 590 text += l
591 591
592 592 # parse values that exist when importing the result of an hg export
593 593 hgpatch = user = snippet = None
594 594 ui.debug('text:\n')
595 595 for t in text.splitlines():
596 596 ui.debug(t,'\n')
597 597 if t == '# HG changeset patch' or hgpatch == True:
598 598 hgpatch = True
599 599 if t[:7] == "# User ":
600 600 user = t[7:]
601 601 ui.debug('User: %s\n' % user)
602 602 if t[:2] <> "# " and t.strip() and not snippet: snippet = t
603 603 if snippet: text = snippet + '\n' + text
604 604 ui.debug('text:\n%s\n' % text)
605 605
606 606 # make sure text isn't empty
607 607 if not text: text = "imported patch %s\n" % patch
608 608
609 609 f = os.popen("patch -p%d < %s" % (strip, pf))
610 610 files = []
611 611 for l in f.read().splitlines():
612 612 l.rstrip('\r\n');
613 613 ui.status("%s\n" % l)
614 614 if l[:14] == 'patching file ':
615 615 pf = l[14:]
616 616 if pf not in files:
617 617 files.append(pf)
618 618 patcherr = f.close()
619 619 if patcherr:
620 620 sys.stderr.write("patch failed")
621 621 sys.exit(1)
622 622
623 623 if len(files) > 0:
624 624 addremove(ui, repo, *files)
625 625 repo.commit(files, text, user)
626 626
627 627 def init(ui, source=None):
628 628 """create a new repository in the current directory"""
629 629
630 630 if source:
631 631 ui.warn("no longer supported: use \"hg clone\" instead\n")
632 632 sys.exit(1)
633 633 repo = hg.repository(ui, ".", create=1)
634 634
635 635 def log(ui, repo, f=None, **opts):
636 636 """show the revision history of the repository or a single file"""
637 637 if f:
638 filelog = repo.file(relpath(repo, [f])[0])
638 files = relpath(repo, [f])
639 filelog = repo.file(files[0])
639 640 log = filelog
640 641 lookup = filelog.lookup
641 642 else:
643 files = None
642 644 filelog = None
643 645 log = repo.changelog
644 646 lookup = repo.lookup
645 647 revlist = []
646 648 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
647 649 while revs:
648 650 if len(revs) == 1:
649 651 revlist.append(revs.pop(0))
650 652 else:
651 653 a = revs.pop(0)
652 654 b = revs.pop(0)
653 655 off = a > b and -1 or 1
654 656 revlist.extend(range(a, b + off, off))
655 657
656 658 for i in revlist or range(log.count() - 1, -1, -1):
657 659 show_changeset(ui, repo, filelog=filelog, rev=i)
660 if opts['patch']:
661 if filelog:
662 filenode = filelog.node(i)
663 i = filelog.linkrev(filenode)
664 changenode = repo.changelog.node(i)
665 prev, other = repo.changelog.parents(changenode)
666 dodiff(sys.stdout, ui, repo, files, prev, changenode)
667 ui.write("\n")
668 ui.write("\n")
658 669
659 670 def manifest(ui, repo, rev = []):
660 671 """output the latest or given revision of the project manifest"""
661 672 n = repo.manifest.tip()
662 673 if rev:
663 674 n = repo.manifest.lookup(rev)
664 675 m = repo.manifest.read(n)
665 676 mf = repo.manifest.readflags(n)
666 677 files = m.keys()
667 678 files.sort()
668 679
669 680 for f in files:
670 681 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
671 682
672 683 def parents(ui, repo, node = None):
673 684 '''show the parents of the current working dir'''
674 685 if node:
675 686 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
676 687 else:
677 688 p = repo.dirstate.parents()
678 689
679 690 for n in p:
680 691 if n != hg.nullid:
681 692 show_changeset(ui, repo, changenode=n)
682 693
683 694 def pull(ui, repo, source="default", **opts):
684 695 """pull changes from the specified source"""
685 696 source = ui.expandpath(source)
686 697
687 698 ui.status('pulling from %s\n' % (source))
688 699
689 700 other = hg.repository(ui, source)
690 701 fetch = repo.findincoming(other)
691 702 if not fetch:
692 703 ui.status("no changes found\n")
693 704 return
694 705
695 706 cg = other.changegroup(fetch)
696 707 r = repo.addchangegroup(cg)
697 708 if cg and not r:
698 709 if opts['update']:
699 710 return update(ui, repo)
700 711 else:
701 712 ui.status("(run 'hg update' to get a working copy)\n")
702 713
703 714 return r
704 715
705 716 def push(ui, repo, dest="default-push"):
706 717 """push changes to the specified destination"""
707 718 dest = ui.expandpath(dest)
708 719
709 720 if not dest.startswith("ssh://"):
710 721 ui.warn("abort: can only push to ssh:// destinations currently\n")
711 722 return 1
712 723
713 724 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', dest)
714 725 if not m:
715 726 ui.warn("abort: couldn't parse destination %s\n" % dest)
716 727 return 1
717 728
718 729 user, host, port, path = map(m.group, (2, 3, 5, 7))
719 730 uhost = user and ("%s@%s" % (user, host)) or host
720 731 port = port and (" -p %s") % port or ""
721 732 path = path or ""
722 733
723 734 sport = random.randrange(30000, 60000)
724 735 cmd = "ssh %s%s -R %d:localhost:%d 'cd %s; hg pull http://localhost:%d/'"
725 736 cmd = cmd % (uhost, port, sport+1, sport, path, sport+1)
726 737
727 738 child = os.fork()
728 739 if not child:
729 740 sys.stdout = file("/dev/null", "w")
730 741 sys.stderr = sys.stdout
731 742 hgweb.server(repo.root, "pull", "", "localhost", sport)
732 743 else:
733 744 ui.status("connecting to %s\n" % host)
734 745 r = os.system(cmd)
735 746 os.kill(child, signal.SIGTERM)
736 747 return r
737 748
738 749 def rawcommit(ui, repo, *flist, **rc):
739 750 "raw commit interface"
740 751
741 752 text = rc['text']
742 753 if not text and rc['logfile']:
743 754 try: text = open(rc['logfile']).read()
744 755 except IOError: pass
745 756 if not text and not rc['logfile']:
746 757 ui.warn("abort: missing commit text\n")
747 758 return 1
748 759
749 760 files = relpath(repo, list(flist))
750 761 if rc['files']:
751 762 files += open(rc['files']).read().splitlines()
752 763
753 764 rc['parent'] = map(repo.lookup, rc['parent'])
754 765
755 766 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
756 767
757 768 def recover(ui, repo):
758 769 """roll back an interrupted transaction"""
759 770 repo.recover()
760 771
761 772 def remove(ui, repo, file, *files):
762 773 """remove the specified files on the next commit"""
763 774 repo.remove(relpath(repo, (file,) + files))
764 775
765 776 def revert(ui, repo, *names, **opts):
766 777 """revert modified files or dirs back to their unmodified states"""
767 778 node = opts['rev'] and repo.lookup(opts['rev']) or \
768 779 repo.dirstate.parents()[0]
769 780 root = os.path.realpath(repo.root)
770 781
771 782 def trimpath(p):
772 783 p = os.path.realpath(p)
773 784 if p.startswith(root):
774 785 rest = p[len(root):]
775 786 if not rest:
776 787 return rest
777 788 if p.startswith(os.sep):
778 789 return rest[1:]
779 790 return p
780 791
781 792 relnames = map(trimpath, names or [os.getcwd()])
782 793 chosen = {}
783 794
784 795 def choose(name):
785 796 def body(name):
786 797 for r in relnames:
787 798 if not name.startswith(r): continue
788 799 rest = name[len(r):]
789 800 if not rest: return r, True
790 801 depth = rest.count(os.sep)
791 802 if not r:
792 803 if depth == 0 or not opts['nonrecursive']: return r, True
793 804 elif rest[0] == os.sep:
794 805 if depth == 1 or not opts['nonrecursive']: return r, True
795 806 return None, False
796 807 relname, ret = body(name)
797 808 if ret:
798 809 chosen[relname] = 1
799 810 return ret
800 811
801 812 r = repo.update(node, False, True, choose, False)
802 813 for n in relnames:
803 814 if n not in chosen:
804 815 ui.warn('error: no matches for %s\n' % n)
805 816 r = 1
806 817 sys.stdout.flush()
807 818 return r
808 819
809 820 def root(ui, repo):
810 821 """print the root (top) of the current working dir"""
811 822 ui.write(repo.root + "\n")
812 823
813 824 def serve(ui, repo, **opts):
814 825 """export the repository via HTTP"""
815 826 def openlog(opt, default):
816 827 if opts[opt] and opts[opt] != '-': return open(opts[opt], 'w')
817 828 else: return default
818 829 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
819 830 opts["address"], opts["port"],
820 831 openlog('accesslog', sys.stdout),
821 832 openlog('errorlog', sys.stderr))
822 833 if ui.verbose:
823 834 addr, port = httpd.socket.getsockname()
824 835 if addr == '0.0.0.0':
825 836 addr = socket.gethostname()
826 837 else:
827 838 try:
828 839 addr = socket.gethostbyaddr(addr)[0]
829 840 except: pass
830 841 if port != 80:
831 842 ui.status('listening at http://%s:%d/\n' % (addr, port))
832 843 else:
833 844 ui.status('listening at http://%s/\n' % addr)
834 845 httpd.serve_forever()
835 846
836 847 def status(ui, repo):
837 848 '''show changed files in the working directory
838 849
839 850 C = changed
840 851 A = added
841 852 R = removed
842 853 ? = not tracked'''
843 854
844 855 (c, a, d, u) = repo.changes(None, None)
845 856 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
846 857
847 858 for f in c: ui.write("C ", f, "\n")
848 859 for f in a: ui.write("A ", f, "\n")
849 860 for f in d: ui.write("R ", f, "\n")
850 861 for f in u: ui.write("? ", f, "\n")
851 862
852 863 def tag(ui, repo, name, rev = None, **opts):
853 864 """add a tag for the current tip or a given revision"""
854 865
855 866 if name == "tip":
856 867 ui.warn("abort: 'tip' is a reserved name!\n")
857 868 return -1
858 869 if rev:
859 870 r = hg.hex(repo.lookup(rev))
860 871 else:
861 872 r = hg.hex(repo.changelog.tip())
862 873
863 874 if name.find(revrangesep) >= 0:
864 875 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
865 876 return -1
866 877
867 878 if opts['local']:
868 879 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
869 880 return
870 881
871 882 (c, a, d, u) = repo.changes(None, None)
872 883 for x in (c, a, d, u):
873 884 if ".hgtags" in x:
874 885 ui.warn("abort: working copy of .hgtags is changed!\n")
875 886 ui.status("(please commit .hgtags manually)\n")
876 887 return -1
877 888
878 889 add = 0
879 890 if not os.path.exists(repo.wjoin(".hgtags")): add = 1
880 891 repo.wfile(".hgtags", "a").write("%s %s\n" % (r, name))
881 892 if add: repo.add([".hgtags"])
882 893
883 894 if not opts['text']:
884 895 opts['text'] = "Added tag %s for changeset %s" % (name, r)
885 896
886 897 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
887 898
888 899 def tags(ui, repo):
889 900 """list repository tags"""
890 901
891 902 l = repo.tagslist()
892 903 l.reverse()
893 904 for t, n in l:
894 905 try:
895 906 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
896 907 except KeyError:
897 908 r = " ?:?"
898 909 ui.write("%-30s %s\n" % (t, r))
899 910
900 911 def tip(ui, repo):
901 912 """show the tip revision"""
902 913 n = repo.changelog.tip()
903 914 show_changeset(ui, repo, changenode=n)
904 915
905 916 def undo(ui, repo):
906 917 """undo the last commit or pull
907 918
908 919 Roll back the last pull or commit transaction on the
909 920 repository, restoring the project to its earlier state.
910 921
911 922 This command should be used with care. There is only one level of
912 923 undo and there is no redo.
913 924
914 925 This command is not intended for use on public repositories. Once
915 926 a change is visible for pull by other users, undoing it locally is
916 927 ineffective.
917 928 """
918 929 repo.undo()
919 930
920 931 def update(ui, repo, node=None, merge=False, clean=False):
921 932 '''update or merge working directory
922 933
923 934 If there are no outstanding changes in the working directory and
924 935 there is a linear relationship between the current version and the
925 936 requested version, the result is the requested version.
926 937
927 938 Otherwise the result is a merge between the contents of the
928 939 current working directory and the requested version. Files that
929 940 changed between either parent are marked as changed for the next
930 941 commit and a commit must be performed before any further updates
931 942 are allowed.
932 943 '''
933 944 node = node and repo.lookup(node) or repo.changelog.tip()
934 945 return repo.update(node, allow=merge, force=clean)
935 946
936 947 def verify(ui, repo):
937 948 """verify the integrity of the repository"""
938 949 return repo.verify()
939 950
940 951 # Command options and aliases are listed here, alphabetically
941 952
942 953 table = {
943 954 "^add": (add, [], "hg add [files]"),
944 955 "addremove": (addremove, [], "hg addremove [files]"),
945 956 "^annotate": (annotate,
946 957 [('r', 'revision', '', 'revision'),
947 958 ('u', 'user', None, 'show user'),
948 959 ('n', 'number', None, 'show revision number'),
949 960 ('c', 'changeset', None, 'show changeset')],
950 961 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
951 962 "cat": (cat, [], 'hg cat <file> [rev]'),
952 963 "^clone": (clone, [('U', 'noupdate', None, 'skip update after cloning')],
953 964 'hg clone [options] <source> [dest]'),
954 965 "^commit|ci": (commit,
955 966 [('t', 'text', "", 'commit text'),
956 967 ('A', 'addremove', None, 'run add/remove during commit'),
957 968 ('l', 'logfile', "", 'commit text file'),
958 969 ('d', 'date', "", 'date code'),
959 970 ('u', 'user', "", 'user')],
960 971 'hg commit [files]'),
961 972 "copy": (copy, [], 'hg copy <source> <dest>'),
962 973 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
963 974 "debugstate": (debugstate, [], 'debugstate'),
964 975 "debugindex": (debugindex, [], 'debugindex <file>'),
965 976 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
966 977 "^diff": (diff, [('r', 'rev', [], 'revision')],
967 978 'hg diff [-r A] [-r B] [files]'),
968 979 "^export": (export, [('o', 'output', "", 'output to file')],
969 980 "hg export [-o file] <changeset> ..."),
970 981 "forget": (forget, [], "hg forget [files]"),
971 982 "heads": (heads, [], 'hg heads'),
972 983 "help": (help, [], 'hg help [command]'),
973 984 "identify|id": (identify, [], 'hg identify'),
974 985 "import|patch": (import_,
975 986 [('p', 'strip', 1, 'path strip'),
976 987 ('b', 'base', "", 'base path')],
977 988 "hg import [options] <patches>"),
978 989 "^init": (init, [], 'hg init'),
979 990 "^log|history": (log,
980 [('r', 'rev', [], 'revision')],
981 'hg log [-r A] [-r B] [file]'),
991 [('r', 'rev', [], 'revision'),
992 ('p', 'patch', None, 'show patch')],
993 'hg log [-r A] [-r B] [-p] [file]'),
982 994 "manifest": (manifest, [], 'hg manifest [rev]'),
983 995 "parents": (parents, [], 'hg parents [node]'),
984 996 "^pull": (pull,
985 997 [('u', 'update', None, 'update working directory')],
986 998 'hg pull [options] [source]'),
987 999 "^push": (push, [], 'hg push <destination>'),
988 1000 "rawcommit": (rawcommit,
989 1001 [('p', 'parent', [], 'parent'),
990 1002 ('d', 'date', "", 'date code'),
991 1003 ('u', 'user', "", 'user'),
992 1004 ('F', 'files', "", 'file list'),
993 1005 ('t', 'text', "", 'commit text'),
994 1006 ('l', 'logfile', "", 'commit text file')],
995 1007 'hg rawcommit [options] [files]'),
996 1008 "recover": (recover, [], "hg recover"),
997 1009 "^remove|rm": (remove, [], "hg remove [files]"),
998 1010 "^revert": (revert,
999 1011 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1000 1012 ("r", "rev", "", "revision")],
1001 1013 "hg revert [files|dirs]"),
1002 1014 "root": (root, [], "hg root"),
1003 1015 "^serve": (serve, [('A', 'accesslog', '', 'access log file'),
1004 1016 ('E', 'errorlog', '', 'error log file'),
1005 1017 ('p', 'port', 8000, 'listen port'),
1006 1018 ('a', 'address', '', 'interface address'),
1007 1019 ('n', 'name', os.getcwd(), 'repository name'),
1008 1020 ('t', 'templates', "", 'template map')],
1009 1021 "hg serve [options]"),
1010 1022 "^status": (status, [], 'hg status'),
1011 1023 "tag": (tag, [('l', 'local', None, 'make the tag local'),
1012 1024 ('t', 'text', "", 'commit text'),
1013 1025 ('d', 'date', "", 'date code'),
1014 1026 ('u', 'user', "", 'user')],
1015 1027 'hg tag [options] <name> [rev]'),
1016 1028 "tags": (tags, [], 'hg tags'),
1017 1029 "tip": (tip, [], 'hg tip'),
1018 1030 "undo": (undo, [], 'hg undo'),
1019 1031 "^update|up|checkout|co":
1020 1032 (update,
1021 1033 [('m', 'merge', None, 'allow merging of conflicts'),
1022 1034 ('C', 'clean', None, 'overwrite locally modified files')],
1023 1035 'hg update [options] [node]'),
1024 1036 "verify": (verify, [], 'hg verify'),
1025 1037 "version": (show_version, [], 'hg version'),
1026 1038 }
1027 1039
1028 1040 globalopts = [('v', 'verbose', None, 'verbose'),
1029 1041 ('', 'debug', None, 'debug'),
1030 1042 ('q', 'quiet', None, 'quiet'),
1031 1043 ('', 'profile', None, 'profile'),
1032 1044 ('R', 'repository', "", 'repository root directory'),
1033 1045 ('', 'traceback', None, 'print traceback on exception'),
1034 1046 ('y', 'noninteractive', None, 'run non-interactively'),
1035 1047 ('', 'version', None, 'output version information and exit'),
1036 1048 ]
1037 1049
1038 1050 norepo = "clone init version help debugindex debugindexdot"
1039 1051
1040 1052 def find(cmd):
1041 1053 for e in table.keys():
1042 1054 if re.match("(%s)$" % e, cmd):
1043 1055 return table[e]
1044 1056
1045 1057 raise UnknownCommand(cmd)
1046 1058
1047 1059 class SignalInterrupt(Exception): pass
1048 1060
1049 1061 def catchterm(*args):
1050 1062 raise SignalInterrupt
1051 1063
1052 1064 def run():
1053 1065 sys.exit(dispatch(sys.argv[1:]))
1054 1066
1055 1067 class ParseError(Exception): pass
1056 1068
1057 1069 def parse(args):
1058 1070 options = {}
1059 1071 cmdoptions = {}
1060 1072
1061 1073 try:
1062 1074 args = fancyopts.fancyopts(args, globalopts, options)
1063 1075 except fancyopts.getopt.GetoptError, inst:
1064 1076 raise ParseError(cmd, inst)
1065 1077
1066 1078 if options["version"]:
1067 1079 return ("version", show_version, [], options, cmdoptions)
1068 1080 elif not args:
1069 1081 return ("help", help, [], options, cmdoptions)
1070 1082 else:
1071 1083 cmd, args = args[0], args[1:]
1072 1084
1073 1085 i = find(cmd)
1074 1086
1075 1087 # combine global options into local
1076 1088 c = list(i[1])
1077 1089 l = len(c)
1078 1090 for o in globalopts:
1079 1091 c.append((o[0], o[1], options[o[1]], o[3]))
1080 1092
1081 1093 try:
1082 1094 args = fancyopts.fancyopts(args, c, cmdoptions)
1083 1095 except fancyopts.getopt.GetoptError, inst:
1084 1096 raise ParseError(cmd, inst)
1085 1097
1086 1098 # separate global options back out
1087 1099 for o in globalopts:
1088 1100 n = o[1]
1089 1101 options[n] = cmdoptions[n]
1090 1102 del cmdoptions[n]
1091 1103
1092 1104 return (cmd, i[0], args, options, cmdoptions)
1093 1105
1094 1106 def dispatch(args):
1095 1107 signal.signal(signal.SIGTERM, catchterm)
1096 1108
1097 1109 try:
1098 1110 cmd, func, args, options, cmdoptions = parse(args)
1099 1111 except ParseError, inst:
1100 1112 u = ui.ui()
1101 1113 if inst.args[0]:
1102 1114 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1103 1115 help(u, inst.args[0])
1104 1116 else:
1105 1117 u.warn("hg: %s\n" % inst.args[1])
1106 1118 help(u)
1107 1119 sys.exit(-1)
1108 1120 except UnknownCommand, inst:
1109 1121 u = ui.ui()
1110 1122 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1111 1123 help(u)
1112 1124 sys.exit(1)
1113 1125
1114 1126 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1115 1127 not options["noninteractive"])
1116 1128
1117 1129 try:
1118 1130 try:
1119 1131 if cmd not in norepo.split():
1120 1132 path = options["repository"] or ""
1121 1133 repo = hg.repository(ui=u, path=path)
1122 1134 d = lambda: func(u, repo, *args, **cmdoptions)
1123 1135 else:
1124 1136 d = lambda: func(u, *args, **cmdoptions)
1125 1137
1126 1138 if options['profile']:
1127 1139 import hotshot, hotshot.stats
1128 1140 prof = hotshot.Profile("hg.prof")
1129 1141 r = prof.runcall(d)
1130 1142 prof.close()
1131 1143 stats = hotshot.stats.load("hg.prof")
1132 1144 stats.strip_dirs()
1133 1145 stats.sort_stats('time', 'calls')
1134 1146 stats.print_stats(40)
1135 1147 return r
1136 1148 else:
1137 1149 return d()
1138 1150 except:
1139 1151 if options['traceback']:
1140 1152 traceback.print_exc()
1141 1153 raise
1142 1154 except util.CommandError, inst:
1143 1155 u.warn("abort: %s\n" % inst.args)
1144 1156 except hg.RepoError, inst:
1145 1157 u.warn("abort: ", inst, "!\n")
1146 1158 except SignalInterrupt:
1147 1159 u.warn("killed!\n")
1148 1160 except KeyboardInterrupt:
1149 1161 u.warn("interrupted!\n")
1150 1162 except IOError, inst:
1151 1163 if hasattr(inst, "code"):
1152 1164 u.warn("abort: %s\n" % inst)
1153 1165 elif hasattr(inst, "reason"):
1154 1166 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
1155 1167 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1156 1168 u.warn("broken pipe\n")
1157 1169 else:
1158 1170 raise
1159 1171 except OSError, inst:
1160 1172 if hasattr(inst, "filename"):
1161 1173 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1162 1174 else:
1163 1175 u.warn("abort: %s\n" % inst.strerror)
1164 1176 except TypeError, inst:
1165 1177 # was this an argument error?
1166 1178 tb = traceback.extract_tb(sys.exc_info()[2])
1167 1179 if len(tb) > 2: # no
1168 1180 raise
1169 1181 u.debug(inst, "\n")
1170 1182 u.warn("%s: invalid arguments\n" % cmd)
1171 1183 help(u, cmd)
1172 1184
1173 1185 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now