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