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