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