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