##// END OF EJS Templates
Add preliminary support for the bundle and unbundle commands
mpm@selenic.com -
r1218:cde6818e default
parent child Browse files
Show More
@@ -1,725 +1,746 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 -R, --repository::
20 -R, --repository::
21 repository root directory
21 repository root directory
22
22
23 --cwd::
23 --cwd::
24 change working directory
24 change working directory
25
25
26 -y, --noninteractive::
26 -y, --noninteractive::
27 do not prompt, assume 'yes' for any required answers
27 do not prompt, assume 'yes' for any required answers
28
28
29 -q, --quiet::
29 -q, --quiet::
30 suppress output
30 suppress output
31
31
32 -v, --verbose::
32 -v, --verbose::
33 enable additional output
33 enable additional output
34
34
35 --debug::
35 --debug::
36 enable debugging output
36 enable debugging output
37
37
38 --traceback::
38 --traceback::
39 print traceback on exception
39 print traceback on exception
40
40
41 --time::
41 --time::
42 time how long the command takes
42 time how long the command takes
43
43
44 --profile::
44 --profile::
45 print command execution profile
45 print command execution profile
46
46
47 --version::
47 --version::
48 output version information and exit
48 output version information and exit
49
49
50 -h, --help::
50 -h, --help::
51 display help and exit
51 display help and exit
52
52
53 COMMAND ELEMENTS
53 COMMAND ELEMENTS
54 ----------------
54 ----------------
55
55
56 files ...::
56 files ...::
57 indicates one or more filename or relative path filenames; see
57 indicates one or more filename or relative path filenames; see
58 "FILE NAME PATTERNS" for information on pattern matching
58 "FILE NAME PATTERNS" for information on pattern matching
59
59
60 path::
60 path::
61 indicates a path on the local machine
61 indicates a path on the local machine
62
62
63 revision::
63 revision::
64 indicates a changeset which can be specified as a changeset revision
64 indicates a changeset which can be specified as a changeset revision
65 number, a tag, or a unique substring of the changeset hash value
65 number, a tag, or a unique substring of the changeset hash value
66
66
67 repository path::
67 repository path::
68 either the pathname of a local repository or the URI of a remote
68 either the pathname of a local repository or the URI of a remote
69 repository. There are two available URI protocols, http:// which is
69 repository. There are two available URI protocols, http:// which is
70 fast and the old-http:// protocol which is much slower but does not
70 fast and the old-http:// protocol which is much slower but does not
71 require a special server on the web host.
71 require a special server on the web host.
72
72
73 COMMANDS
73 COMMANDS
74 --------
74 --------
75
75
76 add [options] [files ...]::
76 add [options] [files ...]::
77 Schedule files to be version controlled and added to the repository.
77 Schedule files to be version controlled and added to the repository.
78
78
79 The files will be added to the repository at the next commit.
79 The files will be added to the repository at the next commit.
80
80
81 If no names are given, add all files in the current directory and
81 If no names are given, add all files in the current directory and
82 its subdirectories.
82 its subdirectories.
83
83
84 addremove [options] [files ...]::
84 addremove [options] [files ...]::
85 Add all new files and remove all missing files from the repository.
85 Add all new files and remove all missing files from the repository.
86
86
87 New files are ignored if they match any of the patterns in .hgignore. As
87 New files are ignored if they match any of the patterns in .hgignore. As
88 with add, these changes take effect at the next commit.
88 with add, these changes take effect at the next commit.
89
89
90 annotate [-r <rev> -u -n -c] [files ...]::
90 annotate [-r <rev> -u -n -c] [files ...]::
91 List changes in files, showing the revision id responsible for each line
91 List changes in files, showing the revision id responsible for each line
92
92
93 This command is useful to discover who did a change or when a change took
93 This command is useful to discover who did a change or when a change took
94 place.
94 place.
95
95
96 Without the -a option, annotate will avoid processing files it
96 Without the -a option, annotate will avoid processing files it
97 detects as binary. With -a, annotate will generate an annotation
97 detects as binary. With -a, annotate will generate an annotation
98 anyway, probably with undesirable results.
98 anyway, probably with undesirable results.
99
99
100 options:
100 options:
101 -a, --text treat all files as text
101 -a, --text treat all files as text
102 -I, --include <pat> include names matching the given patterns
102 -I, --include <pat> include names matching the given patterns
103 -X, --exclude <pat> exclude names matching the given patterns
103 -X, --exclude <pat> exclude names matching the given patterns
104 -r, --revision <rev> annotate the specified revision
104 -r, --revision <rev> annotate the specified revision
105 -u, --user list the author
105 -u, --user list the author
106 -c, --changeset list the changeset
106 -c, --changeset list the changeset
107 -n, --number list the revision number (default)
107 -n, --number list the revision number (default)
108
108
109 bundle <file> <other>::
110 (EXPERIMENTAL)
111
112 Generate a compressed changegroup file collecting all changesets
113 not found in the other repository.
114
115 This file can then be transferred using conventional means and
116 applied to another repository with the unbundle command. This is
117 useful when native push and pull are not available or when
118 exporting an entire repository is undesirable. The standard file
119 extension is ".hg".
120
121 Unlike import/export, this exactly preserves all changeset
122 contents including permissions, rename data, and revision history.
123
109 cat <file> [revision]::
124 cat <file> [revision]::
110 Output to stdout the given revision for the specified file.
125 Output to stdout the given revision for the specified file.
111
126
112 If no revision is given then the tip is used.
127 If no revision is given then the tip is used.
113
128
114 clone [-U] <source> [dest]::
129 clone [-U] <source> [dest]::
115 Create a copy of an existing repository in a new directory.
130 Create a copy of an existing repository in a new directory.
116
131
117 If no destination directory name is specified, it defaults to the
132 If no destination directory name is specified, it defaults to the
118 basename of the source.
133 basename of the source.
119
134
120 The source is added to the new repository's .hg/hgrc file to be used in
135 The source is added to the new repository's .hg/hgrc file to be used in
121 future pulls.
136 future pulls.
122
137
123 For efficiency, hardlinks are used for cloning whenever the
138 For efficiency, hardlinks are used for cloning whenever the
124 source and destination are on the same filesystem.
139 source and destination are on the same filesystem.
125
140
126 options:
141 options:
127 -U, --noupdate do not update the new working directory
142 -U, --noupdate do not update the new working directory
128 -e, --ssh specify ssh command to use
143 -e, --ssh specify ssh command to use
129 --remotecmd specify hg command to run on the remote side
144 --remotecmd specify hg command to run on the remote side
130
145
131 commit [options] [files...]::
146 commit [options] [files...]::
132 Commit changes to the given files into the repository.
147 Commit changes to the given files into the repository.
133
148
134 If a list of files is omitted, all changes reported by "hg status"
149 If a list of files is omitted, all changes reported by "hg status"
135 from the root of the repository will be commited.
150 from the root of the repository will be commited.
136
151
137 The HGEDITOR or EDITOR environment variables are used to start an
152 The HGEDITOR or EDITOR environment variables are used to start an
138 editor to add a commit comment.
153 editor to add a commit comment.
139
154
140 Options:
155 Options:
141
156
142 -A, --addremove run addremove during commit
157 -A, --addremove run addremove during commit
143 -I, --include <pat> include names matching the given patterns
158 -I, --include <pat> include names matching the given patterns
144 -X, --exclude <pat> exclude names matching the given patterns
159 -X, --exclude <pat> exclude names matching the given patterns
145 -m, --message <text> use <text> as commit message
160 -m, --message <text> use <text> as commit message
146 -l, --logfile <file> read the commit message from <file>
161 -l, --logfile <file> read the commit message from <file>
147 -d, --date <datecode> record datecode as commit date
162 -d, --date <datecode> record datecode as commit date
148 -u, --user <user> record user as commiter
163 -u, --user <user> record user as commiter
149
164
150 aliases: ci
165 aliases: ci
151
166
152 copy <source> <dest>::
167 copy <source> <dest>::
153 Mark <dest> file as a copy or rename of a <source> one
168 Mark <dest> file as a copy or rename of a <source> one
154
169
155 This command takes effect for the next commit.
170 This command takes effect for the next commit.
156
171
157 diff [-a] [-r revision] [-r revision] [files ...]::
172 diff [-a] [-r revision] [-r revision] [files ...]::
158 Show differences between revisions for the specified files.
173 Show differences between revisions for the specified files.
159
174
160 Differences between files are shown using the unified diff format.
175 Differences between files are shown using the unified diff format.
161
176
162 When two revision arguments are given, then changes are shown
177 When two revision arguments are given, then changes are shown
163 between those revisions. If only one revision is specified then
178 between those revisions. If only one revision is specified then
164 that revision is compared to the working directory, and, when no
179 that revision is compared to the working directory, and, when no
165 revisions are specified, the working directory files are compared
180 revisions are specified, the working directory files are compared
166 to its parent.
181 to its parent.
167
182
168 Without the -a option, diff will avoid generating diffs of files
183 Without the -a option, diff will avoid generating diffs of files
169 it detects as binary. With -a, diff will generate a diff anyway,
184 it detects as binary. With -a, diff will generate a diff anyway,
170 probably with undesirable results.
185 probably with undesirable results.
171
186
172 options:
187 options:
173 -a, --text treat all files as text
188 -a, --text treat all files as text
174 -I, --include <pat> include names matching the given patterns
189 -I, --include <pat> include names matching the given patterns
175 -X, --exclude <pat> exclude names matching the given patterns
190 -X, --exclude <pat> exclude names matching the given patterns
176
191
177 export [-o filespec] [revision] ...::
192 export [-o filespec] [revision] ...::
178 Print the changeset header and diffs for one or more revisions.
193 Print the changeset header and diffs for one or more revisions.
179
194
180 The information shown in the changeset header is: author,
195 The information shown in the changeset header is: author,
181 changeset hash, parent and commit comment.
196 changeset hash, parent and commit comment.
182
197
183 Output may be to a file, in which case the name of the file is
198 Output may be to a file, in which case the name of the file is
184 given using a format string. The formatting rules are as follows:
199 given using a format string. The formatting rules are as follows:
185
200
186 %% literal "%" character
201 %% literal "%" character
187 %H changeset hash (40 bytes of hexadecimal)
202 %H changeset hash (40 bytes of hexadecimal)
188 %N number of patches being generated
203 %N number of patches being generated
189 %R changeset revision number
204 %R changeset revision number
190 %b basename of the exporting repository
205 %b basename of the exporting repository
191 %h short-form changeset hash (12 bytes of hexadecimal)
206 %h short-form changeset hash (12 bytes of hexadecimal)
192 %n zero-padded sequence number, starting at 1
207 %n zero-padded sequence number, starting at 1
193 %r zero-padded changeset revision number
208 %r zero-padded changeset revision number
194
209
195 Without the -a option, export will avoid generating diffs of files
210 Without the -a option, export will avoid generating diffs of files
196 it detects as binary. With -a, export will generate a diff anyway,
211 it detects as binary. With -a, export will generate a diff anyway,
197 probably with undesirable results.
212 probably with undesirable results.
198
213
199 options:
214 options:
200 -a, --text treat all files as text
215 -a, --text treat all files as text
201 -o, --output <filespec> print output to file with formatted named
216 -o, --output <filespec> print output to file with formatted named
202
217
203 forget [options] [files]::
218 forget [options] [files]::
204 Undo an 'hg add' scheduled for the next commit.
219 Undo an 'hg add' scheduled for the next commit.
205
220
206 options:
221 options:
207 -I, --include <pat> include names matching the given patterns
222 -I, --include <pat> include names matching the given patterns
208 -X, --exclude <pat> exclude names matching the given patterns
223 -X, --exclude <pat> exclude names matching the given patterns
209
224
210 grep [options] pattern [files]::
225 grep [options] pattern [files]::
211 Search revisions of files for a regular expression.
226 Search revisions of files for a regular expression.
212
227
213 This command behaves differently than Unix grep. It only accepts
228 This command behaves differently than Unix grep. It only accepts
214 Python/Perl regexps. It searches repository history, not the
229 Python/Perl regexps. It searches repository history, not the
215 working directory. It always prints the revision number in which
230 working directory. It always prints the revision number in which
216 a match appears.
231 a match appears.
217
232
218 By default, grep only prints output for the first revision of a
233 By default, grep only prints output for the first revision of a
219 file in which it finds a match. To get it to print every revision
234 file in which it finds a match. To get it to print every revision
220 that contains a change in match status ("-" for a match that
235 that contains a change in match status ("-" for a match that
221 becomes a non-match, or "+" for a non-match that becomes a match),
236 becomes a non-match, or "+" for a non-match that becomes a match),
222 use the --all flag.
237 use the --all flag.
223
238
224 options:
239 options:
225 -0, --print0 end fields with NUL
240 -0, --print0 end fields with NUL
226 -I, --include <pat> include names matching the given patterns
241 -I, --include <pat> include names matching the given patterns
227 -X, --exclude <pat> exclude names matching the given patterns
242 -X, --exclude <pat> exclude names matching the given patterns
228 --all print all revisions that match
243 --all print all revisions that match
229 -i, --ignore-case ignore case when matching
244 -i, --ignore-case ignore case when matching
230 -l, --files-with-matches print only file names and revs that match
245 -l, --files-with-matches print only file names and revs that match
231 -n, --line-number print matching line numbers
246 -n, --line-number print matching line numbers
232 -r <rev>, --rev <rev> search in given revision range
247 -r <rev>, --rev <rev> search in given revision range
233 -u, --user print user who committed change
248 -u, --user print user who committed change
234
249
235 heads::
250 heads::
236 Show all repository head changesets.
251 Show all repository head changesets.
237
252
238 Repository "heads" are changesets that don't have children
253 Repository "heads" are changesets that don't have children
239 changesets. They are where development generally takes place and
254 changesets. They are where development generally takes place and
240 are the usual targets for update and merge operations.
255 are the usual targets for update and merge operations.
241
256
242 identify::
257 identify::
243 Print a short summary of the current state of the repo.
258 Print a short summary of the current state of the repo.
244
259
245 This summary identifies the repository state using one or two parent
260 This summary identifies the repository state using one or two parent
246 hash identifiers, followed by a "+" if there are uncommitted changes
261 hash identifiers, followed by a "+" if there are uncommitted changes
247 in the working directory, followed by a list of tags for this revision.
262 in the working directory, followed by a list of tags for this revision.
248
263
249 aliases: id
264 aliases: id
250
265
251 import [-p <n> -b <base> -f] <patches>::
266 import [-p <n> -b <base> -f] <patches>::
252 Import a list of patches and commit them individually.
267 Import a list of patches and commit them individually.
253
268
254 If there are outstanding changes in the working directory, import
269 If there are outstanding changes in the working directory, import
255 will abort unless given the -f flag.
270 will abort unless given the -f flag.
256
271
257 If a patch looks like a mail message (its first line starts with
272 If a patch looks like a mail message (its first line starts with
258 "From " or looks like an RFC822 header), it will not be applied
273 "From " or looks like an RFC822 header), it will not be applied
259 unless the -f option is used. The importer neither parses nor
274 unless the -f option is used. The importer neither parses nor
260 discards mail headers, so use -f only to override the "mailness"
275 discards mail headers, so use -f only to override the "mailness"
261 safety check, not to import a real mail message.
276 safety check, not to import a real mail message.
262
277
263 options:
278 options:
264 -p, --strip <n> directory strip option for patch. This has the same
279 -p, --strip <n> directory strip option for patch. This has the same
265 meaning as the corresponding patch option
280 meaning as the corresponding patch option
266 -b <path> base directory to read patches from
281 -b <path> base directory to read patches from
267 -f, --force skip check for outstanding uncommitted changes
282 -f, --force skip check for outstanding uncommitted changes
268
283
269 aliases: patch
284 aliases: patch
270
285
271 incoming [-p] [source]::
286 incoming [-p] [source]::
272 Show new changesets found in the specified repo or the default
287 Show new changesets found in the specified repo or the default
273 pull repo. These are the changesets that would be pulled if a pull
288 pull repo. These are the changesets that would be pulled if a pull
274 was requested.
289 was requested.
275
290
276 Currently only local repositories are supported.
291 Currently only local repositories are supported.
277
292
278 options:
293 options:
279 -p, --patch show patch
294 -p, --patch show patch
280
295
281 aliases: in
296 aliases: in
282
297
283 init [dest]::
298 init [dest]::
284 Initialize a new repository in the given directory. If the given
299 Initialize a new repository in the given directory. If the given
285 directory does not exist, it is created.
300 directory does not exist, it is created.
286
301
287 If no directory is given, the current directory is used.
302 If no directory is given, the current directory is used.
288
303
289 locate [options] [files]::
304 locate [options] [files]::
290 Print all files under Mercurial control whose names match the
305 Print all files under Mercurial control whose names match the
291 given patterns.
306 given patterns.
292
307
293 This command searches the current directory and its
308 This command searches the current directory and its
294 subdirectories. To search an entire repository, move to the root
309 subdirectories. To search an entire repository, move to the root
295 of the repository.
310 of the repository.
296
311
297 If no patterns are given to match, this command prints all file
312 If no patterns are given to match, this command prints all file
298 names.
313 names.
299
314
300 If you want to feed the output of this command into the "xargs"
315 If you want to feed the output of this command into the "xargs"
301 command, use the "-0" option to both this command and "xargs".
316 command, use the "-0" option to both this command and "xargs".
302 This will avoid the problem of "xargs" treating single filenames
317 This will avoid the problem of "xargs" treating single filenames
303 that contain white space as multiple file names.
318 that contain white space as multiple file names.
304
319
305 options:
320 options:
306
321
307 -0, --print0 end filenames with NUL, for use with xargs
322 -0, --print0 end filenames with NUL, for use with xargs
308 -f, --fullpath print complete paths from the filesystem root
323 -f, --fullpath print complete paths from the filesystem root
309 -I, --include <pat> include names matching the given patterns
324 -I, --include <pat> include names matching the given patterns
310 -r, --rev <rev> search the repository as it stood at rev
325 -r, --rev <rev> search the repository as it stood at rev
311 -X, --exclude <pat> exclude names matching the given patterns
326 -X, --exclude <pat> exclude names matching the given patterns
312
327
313 log [-r revision ...] [-p] [files]::
328 log [-r revision ...] [-p] [files]::
314 Print the revision history of the specified files or the entire project.
329 Print the revision history of the specified files or the entire project.
315
330
316 By default this command outputs: changeset id and hash, tags,
331 By default this command outputs: changeset id and hash, tags,
317 parents, user, date and time, and a summary for each commit. The
332 parents, user, date and time, and a summary for each commit. The
318 -v switch adds some more detail, such as changed files, manifest
333 -v switch adds some more detail, such as changed files, manifest
319 hashes or message signatures.
334 hashes or message signatures.
320
335
321 options:
336 options:
322 -I, --include <pat> include names matching the given patterns
337 -I, --include <pat> include names matching the given patterns
323 -X, --exclude <pat> exclude names matching the given patterns
338 -X, --exclude <pat> exclude names matching the given patterns
324 -r, --rev <A> show the specified revision or range
339 -r, --rev <A> show the specified revision or range
325 -p, --patch show patch
340 -p, --patch show patch
326
341
327 aliases: history
342 aliases: history
328
343
329 manifest [revision]::
344 manifest [revision]::
330 Print a list of version controlled files for the given revision.
345 Print a list of version controlled files for the given revision.
331
346
332 The manifest is the list of files being version controlled. If no revision
347 The manifest is the list of files being version controlled. If no revision
333 is given then the tip is used.
348 is given then the tip is used.
334
349
335 outgoing [-p] [dest]::
350 outgoing [-p] [dest]::
336 Show changesets not found in the specified destination repo or the
351 Show changesets not found in the specified destination repo or the
337 default push repo. These are the changesets that would be pushed
352 default push repo. These are the changesets that would be pushed
338 if a push was requested.
353 if a push was requested.
339
354
340 options:
355 options:
341 -p, --patch show patch
356 -p, --patch show patch
342
357
343 aliases: out
358 aliases: out
344
359
345 parents::
360 parents::
346 Print the working directory's parent revisions.
361 Print the working directory's parent revisions.
347
362
348 paths [NAME]::
363 paths [NAME]::
349 Show definition of symbolic path name NAME. If no name is given, show
364 Show definition of symbolic path name NAME. If no name is given, show
350 definition of available names.
365 definition of available names.
351
366
352 Path names are defined in the [paths] section of /etc/mercurial/hgrc
367 Path names are defined in the [paths] section of /etc/mercurial/hgrc
353 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
368 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
354
369
355 pull <repository path>::
370 pull <repository path>::
356 Pull changes from a remote repository to a local one.
371 Pull changes from a remote repository to a local one.
357
372
358 This finds all changes from the repository at the specified path
373 This finds all changes from the repository at the specified path
359 or URL and adds them to the local repository. By default, this
374 or URL and adds them to the local repository. By default, this
360 does not update the copy of the project in the working directory.
375 does not update the copy of the project in the working directory.
361
376
362 Valid URLs are of the form:
377 Valid URLs are of the form:
363
378
364 local/filesystem/path
379 local/filesystem/path
365 http://[user@]host[:port][/path]
380 http://[user@]host[:port][/path]
366 https://[user@]host[:port][/path]
381 https://[user@]host[:port][/path]
367 ssh://[user@]host[:port][/path]
382 ssh://[user@]host[:port][/path]
368
383
369 SSH requires an accessible shell account on the destination
384 SSH requires an accessible shell account on the destination
370 machine and a copy of hg in the remote path.
385 machine and a copy of hg in the remote path.
371
386
372 options:
387 options:
373 -u, --update update the working directory to tip after pull
388 -u, --update update the working directory to tip after pull
374 -e, --ssh specify ssh command to use
389 -e, --ssh specify ssh command to use
375 --remotecmd specify hg command to run on the remote side
390 --remotecmd specify hg command to run on the remote side
376
391
377 push <destination>::
392 push <destination>::
378 Push changes from the local repository to the given destination.
393 Push changes from the local repository to the given destination.
379
394
380 This is the symmetrical operation for pull. It helps to move
395 This is the symmetrical operation for pull. It helps to move
381 changes from the current repository to a different one. If the
396 changes from the current repository to a different one. If the
382 destination is local this is identical to a pull in that directory
397 destination is local this is identical to a pull in that directory
383 from the current one.
398 from the current one.
384
399
385 By default, push will refuse to run if it detects the result would
400 By default, push will refuse to run if it detects the result would
386 increase the number of remote heads. This generally indicates the
401 increase the number of remote heads. This generally indicates the
387 the client has forgotten to sync and merge before pushing.
402 the client has forgotten to sync and merge before pushing.
388
403
389 Valid URLs are of the form:
404 Valid URLs are of the form:
390
405
391 local/filesystem/path
406 local/filesystem/path
392 ssh://[user@]host[:port][/path]
407 ssh://[user@]host[:port][/path]
393
408
394 SSH requires an accessible shell account on the destination
409 SSH requires an accessible shell account on the destination
395 machine and a copy of hg in the remote path.
410 machine and a copy of hg in the remote path.
396
411
397 options:
412 options:
398
413
399 -f, --force force update
414 -f, --force force update
400 -e, --ssh specify ssh command to use
415 -e, --ssh specify ssh command to use
401 --remotecmd specify hg command to run on the remote side
416 --remotecmd specify hg command to run on the remote side
402
417
403 rawcommit [-p -d -u -F -m -l]::
418 rawcommit [-p -d -u -F -m -l]::
404 Lowlevel commit, for use in helper scripts.
419 Lowlevel commit, for use in helper scripts.
405
420
406 This command is not intended to be used by normal users, as it is
421 This command is not intended to be used by normal users, as it is
407 primarily useful for importing from other SCMs.
422 primarily useful for importing from other SCMs.
408
423
409 recover::
424 recover::
410 Recover from an interrupted commit or pull.
425 Recover from an interrupted commit or pull.
411
426
412 This command tries to fix the repository status after an interrupted
427 This command tries to fix the repository status after an interrupted
413 operation. It should only be necessary when Mercurial suggests it.
428 operation. It should only be necessary when Mercurial suggests it.
414
429
415 remove [options] [files ...]::
430 remove [options] [files ...]::
416 Schedule the indicated files for removal from the repository.
431 Schedule the indicated files for removal from the repository.
417
432
418 This command schedules the files to be removed at the next commit.
433 This command schedules the files to be removed at the next commit.
419 This only removes files from the current branch, not from the
434 This only removes files from the current branch, not from the
420 entire project history.
435 entire project history.
421
436
422 aliases: rm
437 aliases: rm
423
438
424 revert [names ...]::
439 revert [names ...]::
425 Revert any uncommitted modifications made to the named files or
440 Revert any uncommitted modifications made to the named files or
426 directories. This restores the contents of the affected files to
441 directories. This restores the contents of the affected files to
427 an unmodified state.
442 an unmodified state.
428
443
429 If a file has been deleted, it is recreated. If the executable
444 If a file has been deleted, it is recreated. If the executable
430 mode of a file was changed, it is reset.
445 mode of a file was changed, it is reset.
431
446
432 If a directory is given, all files in that directory and its
447 If a directory is given, all files in that directory and its
433 subdirectories are reverted.
448 subdirectories are reverted.
434
449
435 If no arguments are given, all files in the current directory and
450 If no arguments are given, all files in the current directory and
436 its subdirectories are reverted.
451 its subdirectories are reverted.
437
452
438 options:
453 options:
439 -r, --rev <rev> revision to revert to
454 -r, --rev <rev> revision to revert to
440 -n, --nonrecursive do not recurse into subdirectories
455 -n, --nonrecursive do not recurse into subdirectories
441
456
442 root::
457 root::
443 Print the root directory of the current repository.
458 Print the root directory of the current repository.
444
459
445 serve [options]::
460 serve [options]::
446 Start a local HTTP repository browser and pull server.
461 Start a local HTTP repository browser and pull server.
447
462
448 By default, the server logs accesses to stdout and errors to
463 By default, the server logs accesses to stdout and errors to
449 stderr. Use the "-A" and "-E" options to log to files.
464 stderr. Use the "-A" and "-E" options to log to files.
450
465
451 options:
466 options:
452 -A, --accesslog <file> name of access log file to write to
467 -A, --accesslog <file> name of access log file to write to
453 -E, --errorlog <file> name of error log file to write to
468 -E, --errorlog <file> name of error log file to write to
454 -a, --address <addr> address to use
469 -a, --address <addr> address to use
455 -p, --port <n> port to use (default: 8000)
470 -p, --port <n> port to use (default: 8000)
456 -n, --name <name> name to show in web pages (default: working dir)
471 -n, --name <name> name to show in web pages (default: working dir)
457 -t, --templatedir <path> web templates to use
472 -t, --templatedir <path> web templates to use
458 -6, --ipv6 use IPv6 in addition to IPv4
473 -6, --ipv6 use IPv6 in addition to IPv4
459
474
460 status [options] [files]::
475 status [options] [files]::
461 Show changed files in the working directory. If no names are
476 Show changed files in the working directory. If no names are
462 given, all files are shown. Otherwise, only files matching the
477 given, all files are shown. Otherwise, only files matching the
463 given names are shown.
478 given names are shown.
464
479
465 The codes used to show the status of files are:
480 The codes used to show the status of files are:
466
481
467 M = changed
482 M = changed
468 A = added
483 A = added
469 R = removed
484 R = removed
470 ? = not tracked
485 ? = not tracked
471
486
472 options:
487 options:
473
488
474 -m, --modified show only modified files
489 -m, --modified show only modified files
475 -a, --added show only added files
490 -a, --added show only added files
476 -r, --removed show only removed files
491 -r, --removed show only removed files
477 -u, --unknown show only unknown (not tracked) files
492 -u, --unknown show only unknown (not tracked) files
478 -n, --no-status hide status prefix
493 -n, --no-status hide status prefix
479 -0, --print0 end filenames with NUL, for use with xargs
494 -0, --print0 end filenames with NUL, for use with xargs
480 -I, --include <pat> include names matching the given patterns
495 -I, --include <pat> include names matching the given patterns
481 -X, --exclude <pat> exclude names matching the given patterns
496 -X, --exclude <pat> exclude names matching the given patterns
482
497
483 tag [-l -m <text> -d <datecode> -u <user>] <name> [revision]::
498 tag [-l -m <text> -d <datecode> -u <user>] <name> [revision]::
484 Name a particular revision using <name>.
499 Name a particular revision using <name>.
485
500
486 Tags are used to name particular revisions of the repository and are
501 Tags are used to name particular revisions of the repository and are
487 very useful to compare different revision, to go back to significant
502 very useful to compare different revision, to go back to significant
488 earlier versions or to mark branch points as releases, etc.
503 earlier versions or to mark branch points as releases, etc.
489
504
490 If no revision is given, the tip is used.
505 If no revision is given, the tip is used.
491
506
492 To facilitate version control, distribution, and merging of tags,
507 To facilitate version control, distribution, and merging of tags,
493 they are stored as a file named ".hgtags" which is managed
508 they are stored as a file named ".hgtags" which is managed
494 similarly to other project files and can be hand-edited if
509 similarly to other project files and can be hand-edited if
495 necessary.
510 necessary.
496
511
497 options:
512 options:
498 -l, --local make the tag local
513 -l, --local make the tag local
499 -m, --message <text> message for tag commit log entry
514 -m, --message <text> message for tag commit log entry
500 -d, --date <datecode> datecode for commit
515 -d, --date <datecode> datecode for commit
501 -u, --user <user> user for commit
516 -u, --user <user> user for commit
502
517
503 Note: Local tags are not version-controlled or distributed and are
518 Note: Local tags are not version-controlled or distributed and are
504 stored in the .hg/localtags file. If there exists a local tag and
519 stored in the .hg/localtags file. If there exists a local tag and
505 a public tag with the same name, local tag is used.
520 a public tag with the same name, local tag is used.
506
521
507 tags::
522 tags::
508 List the repository tags.
523 List the repository tags.
509
524
510 This lists both regular and local tags.
525 This lists both regular and local tags.
511
526
512 tip::
527 tip::
513 Show the tip revision.
528 Show the tip revision.
514
529
530 unbundle <file>::
531 (EXPERIMENTAL)
532
533 Apply a compressed changegroup file generated by the bundle
534 command.
535
515 undo::
536 undo::
516 Undo the last commit or pull transaction.
537 Undo the last commit or pull transaction.
517
538
518 Roll back the last pull or commit transaction on the
539 Roll back the last pull or commit transaction on the
519 repository, restoring the project to its earlier state.
540 repository, restoring the project to its earlier state.
520
541
521 This command should be used with care. There is only one level of
542 This command should be used with care. There is only one level of
522 undo and there is no redo.
543 undo and there is no redo.
523
544
524 This command is not intended for use on public repositories. Once
545 This command is not intended for use on public repositories. Once
525 a change is visible for pull by other users, undoing it locally is
546 a change is visible for pull by other users, undoing it locally is
526 ineffective.
547 ineffective.
527
548
528 update [-m -C] [revision]::
549 update [-m -C] [revision]::
529 Update the working directory to the specified revision.
550 Update the working directory to the specified revision.
530
551
531 By default, update will refuse to run if doing so would require
552 By default, update will refuse to run if doing so would require
532 merging or discarding local changes.
553 merging or discarding local changes.
533
554
534 With the -m option, a merge will be performed.
555 With the -m option, a merge will be performed.
535
556
536 With the -C option, local changes will be lost.
557 With the -C option, local changes will be lost.
537
558
538 options:
559 options:
539 -m, --merge allow merging of branches
560 -m, --merge allow merging of branches
540 -C, --clean overwrite locally modified files
561 -C, --clean overwrite locally modified files
541
562
542 aliases: up checkout co
563 aliases: up checkout co
543
564
544 verify::
565 verify::
545 Verify the integrity of the current repository.
566 Verify the integrity of the current repository.
546
567
547 This will perform an extensive check of the repository's
568 This will perform an extensive check of the repository's
548 integrity, validating the hashes and checksums of each entry in
569 integrity, validating the hashes and checksums of each entry in
549 the changelog, manifest, and tracked files, as well as the
570 the changelog, manifest, and tracked files, as well as the
550 integrity of their crosslinks and indices.
571 integrity of their crosslinks and indices.
551
572
552 FILE NAME PATTERNS
573 FILE NAME PATTERNS
553 ------------------
574 ------------------
554
575
555 Mercurial accepts several notations for identifying one or more
576 Mercurial accepts several notations for identifying one or more
556 file at a time.
577 file at a time.
557
578
558 By default, Mercurial treats file names as shell-style extended
579 By default, Mercurial treats file names as shell-style extended
559 glob patterns.
580 glob patterns.
560
581
561 Alternate pattern notations must be specified explicitly.
582 Alternate pattern notations must be specified explicitly.
562
583
563 To use a plain path name without any pattern matching, start a
584 To use a plain path name without any pattern matching, start a
564 name with "path:". These path names must match completely, from
585 name with "path:". These path names must match completely, from
565 the root of the current repository.
586 the root of the current repository.
566
587
567 To use an extended glob, start a name with "glob:". Globs are
588 To use an extended glob, start a name with "glob:". Globs are
568 rooted at the current directory; a glob such as "*.c" will match
589 rooted at the current directory; a glob such as "*.c" will match
569 files ending in ".c" in the current directory only.
590 files ending in ".c" in the current directory only.
570
591
571 The supported glob syntax extensions are "**" to match any string
592 The supported glob syntax extensions are "**" to match any string
572 across path separators, and "{a,b}" to mean "a or b".
593 across path separators, and "{a,b}" to mean "a or b".
573
594
574 To use a Perl/Python regular expression, start a name with "re:".
595 To use a Perl/Python regular expression, start a name with "re:".
575 Regexp pattern matching is anchored at the root of the repository.
596 Regexp pattern matching is anchored at the root of the repository.
576
597
577 Plain examples:
598 Plain examples:
578
599
579 path:foo/bar a name bar in a directory named foo in the root of
600 path:foo/bar a name bar in a directory named foo in the root of
580 the repository
601 the repository
581 path:path:name a file or directory named "path:name"
602 path:path:name a file or directory named "path:name"
582
603
583 Glob examples:
604 Glob examples:
584
605
585 glob:*.c any name ending in ".c" in the current directory
606 glob:*.c any name ending in ".c" in the current directory
586 *.c any name ending in ".c" in the current directory
607 *.c any name ending in ".c" in the current directory
587 **.c any name ending in ".c" in the current directory, or
608 **.c any name ending in ".c" in the current directory, or
588 any subdirectory
609 any subdirectory
589 foo/*.c any name ending in ".c" in the directory foo
610 foo/*.c any name ending in ".c" in the directory foo
590 foo/**.c any name ending in ".c" in the directory foo, or any
611 foo/**.c any name ending in ".c" in the directory foo, or any
591 subdirectory
612 subdirectory
592
613
593 Regexp examples:
614 Regexp examples:
594
615
595 re:.*\.c$ any name ending in ".c", anywhere in the repository
616 re:.*\.c$ any name ending in ".c", anywhere in the repository
596
617
597
618
598 SPECIFYING SINGLE REVISIONS
619 SPECIFYING SINGLE REVISIONS
599 ---------------------------
620 ---------------------------
600
621
601 Mercurial accepts several notations for identifying individual
622 Mercurial accepts several notations for identifying individual
602 revisions.
623 revisions.
603
624
604 A plain integer is treated as a revision number. Negative
625 A plain integer is treated as a revision number. Negative
605 integers are treated as offsets from the tip, with -1 denoting the
626 integers are treated as offsets from the tip, with -1 denoting the
606 tip.
627 tip.
607
628
608 A 40-digit hexadecimal string is treated as a unique revision
629 A 40-digit hexadecimal string is treated as a unique revision
609 identifier.
630 identifier.
610
631
611 A hexadecimal string less than 40 characters long is treated as a
632 A hexadecimal string less than 40 characters long is treated as a
612 unique revision identifier, and referred to as a short-form
633 unique revision identifier, and referred to as a short-form
613 identifier. A short-form identifier is only valid if it is the
634 identifier. A short-form identifier is only valid if it is the
614 prefix of one full-length identifier.
635 prefix of one full-length identifier.
615
636
616 Any other string is treated as a tag name, which is a symbolic
637 Any other string is treated as a tag name, which is a symbolic
617 name associated with a revision identifier. Tag names may not
638 name associated with a revision identifier. Tag names may not
618 contain the ":" character.
639 contain the ":" character.
619
640
620 The reserved name "tip" is a special tag that always identifies
641 The reserved name "tip" is a special tag that always identifies
621 the most recent revision.
642 the most recent revision.
622
643
623 SPECIFYING MULTIPLE REVISIONS
644 SPECIFYING MULTIPLE REVISIONS
624 -----------------------------
645 -----------------------------
625
646
626 When Mercurial accepts more than one revision, they may be
647 When Mercurial accepts more than one revision, they may be
627 specified individually, or provided as a continuous range,
648 specified individually, or provided as a continuous range,
628 separated by the ":" character.
649 separated by the ":" character.
629
650
630 The syntax of range notation is [BEGIN]:[END], where BEGIN and END
651 The syntax of range notation is [BEGIN]:[END], where BEGIN and END
631 are revision identifiers. Both BEGIN and END are optional. If
652 are revision identifiers. Both BEGIN and END are optional. If
632 BEGIN is not specified, it defaults to revision number 0. If END
653 BEGIN is not specified, it defaults to revision number 0. If END
633 is not specified, it defaults to the tip. The range ":" thus
654 is not specified, it defaults to the tip. The range ":" thus
634 means "all revisions".
655 means "all revisions".
635
656
636 If BEGIN is greater than END, revisions are treated in reverse
657 If BEGIN is greater than END, revisions are treated in reverse
637 order.
658 order.
638
659
639 A range acts as a closed interval. This means that a range of 3:5
660 A range acts as a closed interval. This means that a range of 3:5
640 gives 3, 4 and 5. Similarly, a range of 4:2 gives 4, 3, and 2.
661 gives 3, 4 and 5. Similarly, a range of 4:2 gives 4, 3, and 2.
641
662
642 ENVIRONMENT VARIABLES
663 ENVIRONMENT VARIABLES
643 ---------------------
664 ---------------------
644
665
645 HGEDITOR::
666 HGEDITOR::
646 This is the name of the editor to use when committing. Defaults to the
667 This is the name of the editor to use when committing. Defaults to the
647 value of EDITOR.
668 value of EDITOR.
648
669
649 (deprecated, use .hgrc)
670 (deprecated, use .hgrc)
650
671
651 HGMERGE::
672 HGMERGE::
652 An executable to use for resolving merge conflicts. The program
673 An executable to use for resolving merge conflicts. The program
653 will be executed with three arguments: local file, remote file,
674 will be executed with three arguments: local file, remote file,
654 ancestor file.
675 ancestor file.
655
676
656 The default program is "hgmerge", which is a shell script provided
677 The default program is "hgmerge", which is a shell script provided
657 by Mercurial with some sensible defaults.
678 by Mercurial with some sensible defaults.
658
679
659 (deprecated, use .hgrc)
680 (deprecated, use .hgrc)
660
681
661 HGUSER::
682 HGUSER::
662 This is the string used for the author of a commit.
683 This is the string used for the author of a commit.
663
684
664 (deprecated, use .hgrc)
685 (deprecated, use .hgrc)
665
686
666 EMAIL::
687 EMAIL::
667 If HGUSER is not set, this will be used as the author for a commit.
688 If HGUSER is not set, this will be used as the author for a commit.
668
689
669 LOGNAME::
690 LOGNAME::
670 If neither HGUSER nor EMAIL is set, LOGNAME will be used (with
691 If neither HGUSER nor EMAIL is set, LOGNAME will be used (with
671 '@hostname' appended) as the author value for a commit.
692 '@hostname' appended) as the author value for a commit.
672
693
673 EDITOR::
694 EDITOR::
674 This is the name of the editor used in the hgmerge script. It will be
695 This is the name of the editor used in the hgmerge script. It will be
675 used for commit messages if HGEDITOR isn't set. Defaults to 'vi'.
696 used for commit messages if HGEDITOR isn't set. Defaults to 'vi'.
676
697
677 PYTHONPATH::
698 PYTHONPATH::
678 This is used by Python to find imported modules and may need to be set
699 This is used by Python to find imported modules and may need to be set
679 appropriately if Mercurial is not installed system-wide.
700 appropriately if Mercurial is not installed system-wide.
680
701
681 FILES
702 FILES
682 -----
703 -----
683 .hgignore::
704 .hgignore::
684 This file contains regular expressions (one per line) that describe file
705 This file contains regular expressions (one per line) that describe file
685 names that should be ignored by hg.
706 names that should be ignored by hg.
686
707
687 .hgtags::
708 .hgtags::
688 This file contains changeset hash values and text tag names (one of each
709 This file contains changeset hash values and text tag names (one of each
689 separated by spaces) that correspond to tagged versions of the repository
710 separated by spaces) that correspond to tagged versions of the repository
690 contents.
711 contents.
691
712
692 /etc/mercurial/hgrc, $HOME/.hgrc, .hg/hgrc::
713 /etc/mercurial/hgrc, $HOME/.hgrc, .hg/hgrc::
693 This file contains defaults and configuration. Values in .hg/hgrc
714 This file contains defaults and configuration. Values in .hg/hgrc
694 override those in $HOME/.hgrc, and these override settings made in the
715 override those in $HOME/.hgrc, and these override settings made in the
695 global /etc/mercurial/hgrc configuration. See hgrc(5) for details of
716 global /etc/mercurial/hgrc configuration. See hgrc(5) for details of
696 the contents and format of these files.
717 the contents and format of these files.
697
718
698 BUGS
719 BUGS
699 ----
720 ----
700 Probably lots, please post them to the mailing list (See Resources below)
721 Probably lots, please post them to the mailing list (See Resources below)
701 when you find them.
722 when you find them.
702
723
703 SEE ALSO
724 SEE ALSO
704 --------
725 --------
705 hgrc(5)
726 hgrc(5)
706
727
707 AUTHOR
728 AUTHOR
708 ------
729 ------
709 Written by Matt Mackall <mpm@selenic.com>
730 Written by Matt Mackall <mpm@selenic.com>
710
731
711 RESOURCES
732 RESOURCES
712 ---------
733 ---------
713 http://selenic.com/mercurial[Main Web Site]
734 http://selenic.com/mercurial[Main Web Site]
714
735
715 http://www.serpentine.com/mercurial[Wiki site]
736 http://www.serpentine.com/mercurial[Wiki site]
716
737
717 http://selenic.com/hg[Source code repository]
738 http://selenic.com/hg[Source code repository]
718
739
719 http://selenic.com/mailman/listinfo/mercurial[Mailing list]
740 http://selenic.com/mailman/listinfo/mercurial[Mailing list]
720
741
721 COPYING
742 COPYING
722 -------
743 -------
723 Copyright (C) 2005 Matt Mackall.
744 Copyright (C) 2005 Matt Mackall.
724 Free use of this software is granted under the terms of the GNU General
745 Free use of this software is granted under the terms of the GNU General
725 Public License (GPL).
746 Public License (GPL).
@@ -1,2014 +1,2066 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 from node import *
9 from node import *
10 demandload(globals(), "os re sys signal shutil imp")
10 demandload(globals(), "os re sys signal shutil imp urllib")
11 demandload(globals(), "fancyopts ui hg util lock revlog")
11 demandload(globals(), "fancyopts ui hg util lock revlog")
12 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
12 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
13 demandload(globals(), "errno socket version struct atexit sets")
13 demandload(globals(), "errno socket version struct atexit sets bz2")
14
14
15 class UnknownCommand(Exception):
15 class UnknownCommand(Exception):
16 """Exception raised if command is not in the command table."""
16 """Exception raised if command is not in the command table."""
17
17
18 def filterfiles(filters, files):
18 def filterfiles(filters, files):
19 l = [x for x in files if x in filters]
19 l = [x for x in files if x in filters]
20
20
21 for t in filters:
21 for t in filters:
22 if t and t[-1] != "/":
22 if t and t[-1] != "/":
23 t += "/"
23 t += "/"
24 l += [x for x in files if x.startswith(t)]
24 l += [x for x in files if x.startswith(t)]
25 return l
25 return l
26
26
27 def relpath(repo, args):
27 def relpath(repo, args):
28 cwd = repo.getcwd()
28 cwd = repo.getcwd()
29 if cwd:
29 if cwd:
30 return [util.normpath(os.path.join(cwd, x)) for x in args]
30 return [util.normpath(os.path.join(cwd, x)) for x in args]
31 return args
31 return args
32
32
33 def matchpats(repo, cwd, pats=[], opts={}, head=''):
33 def matchpats(repo, cwd, pats=[], opts={}, head=''):
34 return util.matcher(repo.root, cwd, pats or ['.'], opts.get('include'),
34 return util.matcher(repo.root, cwd, pats or ['.'], opts.get('include'),
35 opts.get('exclude'), head)
35 opts.get('exclude'), head)
36
36
37 def makewalk(repo, pats, opts, head=''):
37 def makewalk(repo, pats, opts, head=''):
38 cwd = repo.getcwd()
38 cwd = repo.getcwd()
39 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
39 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
40 exact = dict(zip(files, files))
40 exact = dict(zip(files, files))
41 def walk():
41 def walk():
42 for src, fn in repo.walk(files=files, match=matchfn):
42 for src, fn in repo.walk(files=files, match=matchfn):
43 yield src, fn, util.pathto(cwd, fn), fn in exact
43 yield src, fn, util.pathto(cwd, fn), fn in exact
44 return files, matchfn, walk()
44 return files, matchfn, walk()
45
45
46 def walk(repo, pats, opts, head=''):
46 def walk(repo, pats, opts, head=''):
47 files, matchfn, results = makewalk(repo, pats, opts, head)
47 files, matchfn, results = makewalk(repo, pats, opts, head)
48 for r in results:
48 for r in results:
49 yield r
49 yield r
50
50
51 def walkchangerevs(ui, repo, cwd, pats, opts):
51 def walkchangerevs(ui, repo, cwd, pats, opts):
52 '''Iterate over files and the revs they changed in.
52 '''Iterate over files and the revs they changed in.
53
53
54 Callers most commonly need to iterate backwards over the history
54 Callers most commonly need to iterate backwards over the history
55 it is interested in. Doing so has awful (quadratic-looking)
55 it is interested in. Doing so has awful (quadratic-looking)
56 performance, so we use iterators in a "windowed" way.
56 performance, so we use iterators in a "windowed" way.
57
57
58 We walk a window of revisions in the desired order. Within the
58 We walk a window of revisions in the desired order. Within the
59 window, we first walk forwards to gather data, then in the desired
59 window, we first walk forwards to gather data, then in the desired
60 order (usually backwards) to display it.
60 order (usually backwards) to display it.
61
61
62 This function returns an (iterator, getchange) pair. The
62 This function returns an (iterator, getchange) pair. The
63 getchange function returns the changelog entry for a numeric
63 getchange function returns the changelog entry for a numeric
64 revision. The iterator yields 3-tuples. They will be of one of
64 revision. The iterator yields 3-tuples. They will be of one of
65 the following forms:
65 the following forms:
66
66
67 "window", incrementing, lastrev: stepping through a window,
67 "window", incrementing, lastrev: stepping through a window,
68 positive if walking forwards through revs, last rev in the
68 positive if walking forwards through revs, last rev in the
69 sequence iterated over - use to reset state for the current window
69 sequence iterated over - use to reset state for the current window
70
70
71 "add", rev, fns: out-of-order traversal of the given file names
71 "add", rev, fns: out-of-order traversal of the given file names
72 fns, which changed during revision rev - use to gather data for
72 fns, which changed during revision rev - use to gather data for
73 possible display
73 possible display
74
74
75 "iter", rev, None: in-order traversal of the revs earlier iterated
75 "iter", rev, None: in-order traversal of the revs earlier iterated
76 over with "add" - use to display data'''
76 over with "add" - use to display data'''
77 cwd = repo.getcwd()
77 cwd = repo.getcwd()
78 if not pats and cwd:
78 if not pats and cwd:
79 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
79 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
80 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
80 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
81 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
81 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
82 pats, opts)
82 pats, opts)
83 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
83 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
84 wanted = {}
84 wanted = {}
85 slowpath = anypats
85 slowpath = anypats
86 window = 300
86 window = 300
87 fncache = {}
87 fncache = {}
88
88
89 chcache = {}
89 chcache = {}
90 def getchange(rev):
90 def getchange(rev):
91 ch = chcache.get(rev)
91 ch = chcache.get(rev)
92 if ch is None:
92 if ch is None:
93 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
93 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
94 return ch
94 return ch
95
95
96 if not slowpath and not files:
96 if not slowpath and not files:
97 # No files, no patterns. Display all revs.
97 # No files, no patterns. Display all revs.
98 wanted = dict(zip(revs, revs))
98 wanted = dict(zip(revs, revs))
99 if not slowpath:
99 if not slowpath:
100 # Only files, no patterns. Check the history of each file.
100 # Only files, no patterns. Check the history of each file.
101 def filerevgen(filelog):
101 def filerevgen(filelog):
102 for i in xrange(filelog.count() - 1, -1, -window):
102 for i in xrange(filelog.count() - 1, -1, -window):
103 revs = []
103 revs = []
104 for j in xrange(max(0, i - window), i + 1):
104 for j in xrange(max(0, i - window), i + 1):
105 revs.append(filelog.linkrev(filelog.node(j)))
105 revs.append(filelog.linkrev(filelog.node(j)))
106 revs.reverse()
106 revs.reverse()
107 for rev in revs:
107 for rev in revs:
108 yield rev
108 yield rev
109
109
110 minrev, maxrev = min(revs), max(revs)
110 minrev, maxrev = min(revs), max(revs)
111 for file in files:
111 for file in files:
112 filelog = repo.file(file)
112 filelog = repo.file(file)
113 # A zero count may be a directory or deleted file, so
113 # A zero count may be a directory or deleted file, so
114 # try to find matching entries on the slow path.
114 # try to find matching entries on the slow path.
115 if filelog.count() == 0:
115 if filelog.count() == 0:
116 slowpath = True
116 slowpath = True
117 break
117 break
118 for rev in filerevgen(filelog):
118 for rev in filerevgen(filelog):
119 if rev <= maxrev:
119 if rev <= maxrev:
120 if rev < minrev:
120 if rev < minrev:
121 break
121 break
122 fncache.setdefault(rev, [])
122 fncache.setdefault(rev, [])
123 fncache[rev].append(file)
123 fncache[rev].append(file)
124 wanted[rev] = 1
124 wanted[rev] = 1
125 if slowpath:
125 if slowpath:
126 # The slow path checks files modified in every changeset.
126 # The slow path checks files modified in every changeset.
127 def changerevgen():
127 def changerevgen():
128 for i in xrange(repo.changelog.count() - 1, -1, -window):
128 for i in xrange(repo.changelog.count() - 1, -1, -window):
129 for j in xrange(max(0, i - window), i + 1):
129 for j in xrange(max(0, i - window), i + 1):
130 yield j, getchange(j)[3]
130 yield j, getchange(j)[3]
131
131
132 for rev, changefiles in changerevgen():
132 for rev, changefiles in changerevgen():
133 matches = filter(matchfn, changefiles)
133 matches = filter(matchfn, changefiles)
134 if matches:
134 if matches:
135 fncache[rev] = matches
135 fncache[rev] = matches
136 wanted[rev] = 1
136 wanted[rev] = 1
137
137
138 def iterate():
138 def iterate():
139 for i in xrange(0, len(revs), window):
139 for i in xrange(0, len(revs), window):
140 yield 'window', revs[0] < revs[-1], revs[-1]
140 yield 'window', revs[0] < revs[-1], revs[-1]
141 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
141 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
142 if rev in wanted]
142 if rev in wanted]
143 srevs = list(nrevs)
143 srevs = list(nrevs)
144 srevs.sort()
144 srevs.sort()
145 for rev in srevs:
145 for rev in srevs:
146 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
146 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
147 yield 'add', rev, fns
147 yield 'add', rev, fns
148 for rev in nrevs:
148 for rev in nrevs:
149 yield 'iter', rev, None
149 yield 'iter', rev, None
150 return iterate(), getchange
150 return iterate(), getchange
151
151
152 revrangesep = ':'
152 revrangesep = ':'
153
153
154 def revrange(ui, repo, revs, revlog=None):
154 def revrange(ui, repo, revs, revlog=None):
155 """Yield revision as strings from a list of revision specifications."""
155 """Yield revision as strings from a list of revision specifications."""
156 if revlog is None:
156 if revlog is None:
157 revlog = repo.changelog
157 revlog = repo.changelog
158 revcount = revlog.count()
158 revcount = revlog.count()
159 def fix(val, defval):
159 def fix(val, defval):
160 if not val:
160 if not val:
161 return defval
161 return defval
162 try:
162 try:
163 num = int(val)
163 num = int(val)
164 if str(num) != val:
164 if str(num) != val:
165 raise ValueError
165 raise ValueError
166 if num < 0:
166 if num < 0:
167 num += revcount
167 num += revcount
168 if not (0 <= num < revcount):
168 if not (0 <= num < revcount):
169 raise ValueError
169 raise ValueError
170 except ValueError:
170 except ValueError:
171 try:
171 try:
172 num = repo.changelog.rev(repo.lookup(val))
172 num = repo.changelog.rev(repo.lookup(val))
173 except KeyError:
173 except KeyError:
174 try:
174 try:
175 num = revlog.rev(revlog.lookup(val))
175 num = revlog.rev(revlog.lookup(val))
176 except KeyError:
176 except KeyError:
177 raise util.Abort('invalid revision identifier %s', val)
177 raise util.Abort('invalid revision identifier %s', val)
178 return num
178 return num
179 seen = {}
179 seen = {}
180 for spec in revs:
180 for spec in revs:
181 if spec.find(revrangesep) >= 0:
181 if spec.find(revrangesep) >= 0:
182 start, end = spec.split(revrangesep, 1)
182 start, end = spec.split(revrangesep, 1)
183 start = fix(start, 0)
183 start = fix(start, 0)
184 end = fix(end, revcount - 1)
184 end = fix(end, revcount - 1)
185 step = start > end and -1 or 1
185 step = start > end and -1 or 1
186 for rev in xrange(start, end+step, step):
186 for rev in xrange(start, end+step, step):
187 if rev in seen: continue
187 if rev in seen: continue
188 seen[rev] = 1
188 seen[rev] = 1
189 yield str(rev)
189 yield str(rev)
190 else:
190 else:
191 rev = fix(spec, None)
191 rev = fix(spec, None)
192 if rev in seen: continue
192 if rev in seen: continue
193 seen[rev] = 1
193 seen[rev] = 1
194 yield str(rev)
194 yield str(rev)
195
195
196 def make_filename(repo, r, pat, node=None,
196 def make_filename(repo, r, pat, node=None,
197 total=None, seqno=None, revwidth=None):
197 total=None, seqno=None, revwidth=None):
198 node_expander = {
198 node_expander = {
199 'H': lambda: hex(node),
199 'H': lambda: hex(node),
200 'R': lambda: str(r.rev(node)),
200 'R': lambda: str(r.rev(node)),
201 'h': lambda: short(node),
201 'h': lambda: short(node),
202 }
202 }
203 expander = {
203 expander = {
204 '%': lambda: '%',
204 '%': lambda: '%',
205 'b': lambda: os.path.basename(repo.root),
205 'b': lambda: os.path.basename(repo.root),
206 }
206 }
207
207
208 try:
208 try:
209 if node:
209 if node:
210 expander.update(node_expander)
210 expander.update(node_expander)
211 if node and revwidth is not None:
211 if node and revwidth is not None:
212 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
212 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
213 if total is not None:
213 if total is not None:
214 expander['N'] = lambda: str(total)
214 expander['N'] = lambda: str(total)
215 if seqno is not None:
215 if seqno is not None:
216 expander['n'] = lambda: str(seqno)
216 expander['n'] = lambda: str(seqno)
217 if total is not None and seqno is not None:
217 if total is not None and seqno is not None:
218 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
218 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
219
219
220 newname = []
220 newname = []
221 patlen = len(pat)
221 patlen = len(pat)
222 i = 0
222 i = 0
223 while i < patlen:
223 while i < patlen:
224 c = pat[i]
224 c = pat[i]
225 if c == '%':
225 if c == '%':
226 i += 1
226 i += 1
227 c = pat[i]
227 c = pat[i]
228 c = expander[c]()
228 c = expander[c]()
229 newname.append(c)
229 newname.append(c)
230 i += 1
230 i += 1
231 return ''.join(newname)
231 return ''.join(newname)
232 except KeyError, inst:
232 except KeyError, inst:
233 raise util.Abort("invalid format spec '%%%s' in output file name",
233 raise util.Abort("invalid format spec '%%%s' in output file name",
234 inst.args[0])
234 inst.args[0])
235
235
236 def make_file(repo, r, pat, node=None,
236 def make_file(repo, r, pat, node=None,
237 total=None, seqno=None, revwidth=None, mode='wb'):
237 total=None, seqno=None, revwidth=None, mode='wb'):
238 if not pat or pat == '-':
238 if not pat or pat == '-':
239 return 'w' in mode and sys.stdout or sys.stdin
239 return 'w' in mode and sys.stdout or sys.stdin
240 if hasattr(pat, 'write') and 'w' in mode:
240 if hasattr(pat, 'write') and 'w' in mode:
241 return pat
241 return pat
242 if hasattr(pat, 'read') and 'r' in mode:
242 if hasattr(pat, 'read') and 'r' in mode:
243 return pat
243 return pat
244 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
244 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
245 mode)
245 mode)
246
246
247 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
247 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
248 changes=None, text=False):
248 changes=None, text=False):
249 def date(c):
249 def date(c):
250 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
250 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
251
251
252 if not changes:
252 if not changes:
253 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
253 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
254 else:
254 else:
255 (c, a, d, u) = changes
255 (c, a, d, u) = changes
256 if files:
256 if files:
257 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
257 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
258
258
259 if not c and not a and not d:
259 if not c and not a and not d:
260 return
260 return
261
261
262 if node2:
262 if node2:
263 change = repo.changelog.read(node2)
263 change = repo.changelog.read(node2)
264 mmap2 = repo.manifest.read(change[0])
264 mmap2 = repo.manifest.read(change[0])
265 date2 = date(change)
265 date2 = date(change)
266 def read(f):
266 def read(f):
267 return repo.file(f).read(mmap2[f])
267 return repo.file(f).read(mmap2[f])
268 else:
268 else:
269 date2 = time.asctime()
269 date2 = time.asctime()
270 if not node1:
270 if not node1:
271 node1 = repo.dirstate.parents()[0]
271 node1 = repo.dirstate.parents()[0]
272 def read(f):
272 def read(f):
273 return repo.wfile(f).read()
273 return repo.wfile(f).read()
274
274
275 if ui.quiet:
275 if ui.quiet:
276 r = None
276 r = None
277 else:
277 else:
278 hexfunc = ui.verbose and hex or short
278 hexfunc = ui.verbose and hex or short
279 r = [hexfunc(node) for node in [node1, node2] if node]
279 r = [hexfunc(node) for node in [node1, node2] if node]
280
280
281 change = repo.changelog.read(node1)
281 change = repo.changelog.read(node1)
282 mmap = repo.manifest.read(change[0])
282 mmap = repo.manifest.read(change[0])
283 date1 = date(change)
283 date1 = date(change)
284
284
285 for f in c:
285 for f in c:
286 to = None
286 to = None
287 if f in mmap:
287 if f in mmap:
288 to = repo.file(f).read(mmap[f])
288 to = repo.file(f).read(mmap[f])
289 tn = read(f)
289 tn = read(f)
290 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
290 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
291 for f in a:
291 for f in a:
292 to = None
292 to = None
293 tn = read(f)
293 tn = read(f)
294 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
294 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
295 for f in d:
295 for f in d:
296 to = repo.file(f).read(mmap[f])
296 to = repo.file(f).read(mmap[f])
297 tn = None
297 tn = None
298 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
298 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
299
299
300 def trimuser(ui, name, rev, revcache):
300 def trimuser(ui, name, rev, revcache):
301 """trim the name of the user who committed a change"""
301 """trim the name of the user who committed a change"""
302 user = revcache.get(rev)
302 user = revcache.get(rev)
303 if user is None:
303 if user is None:
304 user = revcache[rev] = ui.shortuser(name)
304 user = revcache[rev] = ui.shortuser(name)
305 return user
305 return user
306
306
307 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
307 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
308 """show a single changeset or file revision"""
308 """show a single changeset or file revision"""
309 log = repo.changelog
309 log = repo.changelog
310 if changenode is None:
310 if changenode is None:
311 changenode = log.node(rev)
311 changenode = log.node(rev)
312 elif not rev:
312 elif not rev:
313 rev = log.rev(changenode)
313 rev = log.rev(changenode)
314
314
315 if ui.quiet:
315 if ui.quiet:
316 ui.write("%d:%s\n" % (rev, short(changenode)))
316 ui.write("%d:%s\n" % (rev, short(changenode)))
317 return
317 return
318
318
319 changes = log.read(changenode)
319 changes = log.read(changenode)
320
320
321 t, tz = changes[2].split(' ')
321 t, tz = changes[2].split(' ')
322 # a conversion tool was sticking non-integer offsets into repos
322 # a conversion tool was sticking non-integer offsets into repos
323 try:
323 try:
324 tz = int(tz)
324 tz = int(tz)
325 except ValueError:
325 except ValueError:
326 tz = 0
326 tz = 0
327 date = time.asctime(time.localtime(float(t))) + " %+05d" % (int(tz)/-36)
327 date = time.asctime(time.localtime(float(t))) + " %+05d" % (int(tz)/-36)
328
328
329 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
329 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
330 for p in log.parents(changenode)
330 for p in log.parents(changenode)
331 if ui.debugflag or p != nullid]
331 if ui.debugflag or p != nullid]
332 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
332 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
333 parents = []
333 parents = []
334
334
335 if ui.verbose:
335 if ui.verbose:
336 ui.write("changeset: %d:%s\n" % (rev, hex(changenode)))
336 ui.write("changeset: %d:%s\n" % (rev, hex(changenode)))
337 else:
337 else:
338 ui.write("changeset: %d:%s\n" % (rev, short(changenode)))
338 ui.write("changeset: %d:%s\n" % (rev, short(changenode)))
339
339
340 for tag in repo.nodetags(changenode):
340 for tag in repo.nodetags(changenode):
341 ui.status("tag: %s\n" % tag)
341 ui.status("tag: %s\n" % tag)
342 for parent in parents:
342 for parent in parents:
343 ui.write("parent: %d:%s\n" % parent)
343 ui.write("parent: %d:%s\n" % parent)
344
344
345 if brinfo and changenode in brinfo:
345 if brinfo and changenode in brinfo:
346 br = brinfo[changenode]
346 br = brinfo[changenode]
347 ui.write("branch: %s\n" % " ".join(br))
347 ui.write("branch: %s\n" % " ".join(br))
348
348
349 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
349 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
350 hex(changes[0])))
350 hex(changes[0])))
351 ui.status("user: %s\n" % changes[1])
351 ui.status("user: %s\n" % changes[1])
352 ui.status("date: %s\n" % date)
352 ui.status("date: %s\n" % date)
353
353
354 if ui.debugflag:
354 if ui.debugflag:
355 files = repo.changes(log.parents(changenode)[0], changenode)
355 files = repo.changes(log.parents(changenode)[0], changenode)
356 for key, value in zip(["files:", "files+:", "files-:"], files):
356 for key, value in zip(["files:", "files+:", "files-:"], files):
357 if value:
357 if value:
358 ui.note("%-12s %s\n" % (key, " ".join(value)))
358 ui.note("%-12s %s\n" % (key, " ".join(value)))
359 else:
359 else:
360 ui.note("files: %s\n" % " ".join(changes[3]))
360 ui.note("files: %s\n" % " ".join(changes[3]))
361
361
362 description = changes[4].strip()
362 description = changes[4].strip()
363 if description:
363 if description:
364 if ui.verbose:
364 if ui.verbose:
365 ui.status("description:\n")
365 ui.status("description:\n")
366 ui.status(description)
366 ui.status(description)
367 ui.status("\n\n")
367 ui.status("\n\n")
368 else:
368 else:
369 ui.status("summary: %s\n" % description.splitlines()[0])
369 ui.status("summary: %s\n" % description.splitlines()[0])
370 ui.status("\n")
370 ui.status("\n")
371
371
372 def show_version(ui):
372 def show_version(ui):
373 """output version and copyright information"""
373 """output version and copyright information"""
374 ui.write("Mercurial Distributed SCM (version %s)\n"
374 ui.write("Mercurial Distributed SCM (version %s)\n"
375 % version.get_version())
375 % version.get_version())
376 ui.status(
376 ui.status(
377 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
377 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
378 "This is free software; see the source for copying conditions. "
378 "This is free software; see the source for copying conditions. "
379 "There is NO\nwarranty; "
379 "There is NO\nwarranty; "
380 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
380 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
381 )
381 )
382
382
383 def help_(ui, cmd=None, with_version=False):
383 def help_(ui, cmd=None, with_version=False):
384 """show help for a given command or all commands"""
384 """show help for a given command or all commands"""
385 option_lists = []
385 option_lists = []
386 if cmd and cmd != 'shortlist':
386 if cmd and cmd != 'shortlist':
387 if with_version:
387 if with_version:
388 show_version(ui)
388 show_version(ui)
389 ui.write('\n')
389 ui.write('\n')
390 key, i = find(cmd)
390 key, i = find(cmd)
391 # synopsis
391 # synopsis
392 ui.write("%s\n\n" % i[2])
392 ui.write("%s\n\n" % i[2])
393
393
394 # description
394 # description
395 doc = i[0].__doc__
395 doc = i[0].__doc__
396 if ui.quiet:
396 if ui.quiet:
397 doc = doc.splitlines(0)[0]
397 doc = doc.splitlines(0)[0]
398 ui.write("%s\n" % doc.rstrip())
398 ui.write("%s\n" % doc.rstrip())
399
399
400 if not ui.quiet:
400 if not ui.quiet:
401 # aliases
401 # aliases
402 aliases = ', '.join(key.split('|')[1:])
402 aliases = ', '.join(key.split('|')[1:])
403 if aliases:
403 if aliases:
404 ui.write("\naliases: %s\n" % aliases)
404 ui.write("\naliases: %s\n" % aliases)
405
405
406 # options
406 # options
407 if i[1]:
407 if i[1]:
408 option_lists.append(("options", i[1]))
408 option_lists.append(("options", i[1]))
409
409
410 else:
410 else:
411 # program name
411 # program name
412 if ui.verbose or with_version:
412 if ui.verbose or with_version:
413 show_version(ui)
413 show_version(ui)
414 else:
414 else:
415 ui.status("Mercurial Distributed SCM\n")
415 ui.status("Mercurial Distributed SCM\n")
416 ui.status('\n')
416 ui.status('\n')
417
417
418 # list of commands
418 # list of commands
419 if cmd == "shortlist":
419 if cmd == "shortlist":
420 ui.status('basic commands (use "hg help" '
420 ui.status('basic commands (use "hg help" '
421 'for the full list or option "-v" for details):\n\n')
421 'for the full list or option "-v" for details):\n\n')
422 elif ui.verbose:
422 elif ui.verbose:
423 ui.status('list of commands:\n\n')
423 ui.status('list of commands:\n\n')
424 else:
424 else:
425 ui.status('list of commands (use "hg help -v" '
425 ui.status('list of commands (use "hg help -v" '
426 'to show aliases and global options):\n\n')
426 'to show aliases and global options):\n\n')
427
427
428 h = {}
428 h = {}
429 cmds = {}
429 cmds = {}
430 for c, e in table.items():
430 for c, e in table.items():
431 f = c.split("|")[0]
431 f = c.split("|")[0]
432 if cmd == "shortlist" and not f.startswith("^"):
432 if cmd == "shortlist" and not f.startswith("^"):
433 continue
433 continue
434 f = f.lstrip("^")
434 f = f.lstrip("^")
435 if not ui.debugflag and f.startswith("debug"):
435 if not ui.debugflag and f.startswith("debug"):
436 continue
436 continue
437 d = ""
437 d = ""
438 if e[0].__doc__:
438 if e[0].__doc__:
439 d = e[0].__doc__.splitlines(0)[0].rstrip()
439 d = e[0].__doc__.splitlines(0)[0].rstrip()
440 h[f] = d
440 h[f] = d
441 cmds[f]=c.lstrip("^")
441 cmds[f]=c.lstrip("^")
442
442
443 fns = h.keys()
443 fns = h.keys()
444 fns.sort()
444 fns.sort()
445 m = max(map(len, fns))
445 m = max(map(len, fns))
446 for f in fns:
446 for f in fns:
447 if ui.verbose:
447 if ui.verbose:
448 commands = cmds[f].replace("|",", ")
448 commands = cmds[f].replace("|",", ")
449 ui.write(" %s:\n %s\n"%(commands,h[f]))
449 ui.write(" %s:\n %s\n"%(commands,h[f]))
450 else:
450 else:
451 ui.write(' %-*s %s\n' % (m, f, h[f]))
451 ui.write(' %-*s %s\n' % (m, f, h[f]))
452
452
453 # global options
453 # global options
454 if ui.verbose:
454 if ui.verbose:
455 option_lists.append(("global options", globalopts))
455 option_lists.append(("global options", globalopts))
456
456
457 # list all option lists
457 # list all option lists
458 opt_output = []
458 opt_output = []
459 for title, options in option_lists:
459 for title, options in option_lists:
460 opt_output.append(("\n%s:\n" % title, None))
460 opt_output.append(("\n%s:\n" % title, None))
461 for shortopt, longopt, default, desc in options:
461 for shortopt, longopt, default, desc in options:
462 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
462 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
463 longopt and " --%s" % longopt),
463 longopt and " --%s" % longopt),
464 "%s%s" % (desc,
464 "%s%s" % (desc,
465 default and " (default: %s)" % default
465 default and " (default: %s)" % default
466 or "")))
466 or "")))
467
467
468 if opt_output:
468 if opt_output:
469 opts_len = max([len(line[0]) for line in opt_output if line[1]])
469 opts_len = max([len(line[0]) for line in opt_output if line[1]])
470 for first, second in opt_output:
470 for first, second in opt_output:
471 if second:
471 if second:
472 ui.write(" %-*s %s\n" % (opts_len, first, second))
472 ui.write(" %-*s %s\n" % (opts_len, first, second))
473 else:
473 else:
474 ui.write("%s\n" % first)
474 ui.write("%s\n" % first)
475
475
476 # Commands start here, listed alphabetically
476 # Commands start here, listed alphabetically
477
477
478 def add(ui, repo, *pats, **opts):
478 def add(ui, repo, *pats, **opts):
479 '''add the specified files on the next commit'''
479 '''add the specified files on the next commit'''
480 names = []
480 names = []
481 for src, abs, rel, exact in walk(repo, pats, opts):
481 for src, abs, rel, exact in walk(repo, pats, opts):
482 if exact:
482 if exact:
483 names.append(abs)
483 names.append(abs)
484 elif repo.dirstate.state(abs) == '?':
484 elif repo.dirstate.state(abs) == '?':
485 ui.status('adding %s\n' % rel)
485 ui.status('adding %s\n' % rel)
486 names.append(abs)
486 names.append(abs)
487 repo.add(names)
487 repo.add(names)
488
488
489 def addremove(ui, repo, *pats, **opts):
489 def addremove(ui, repo, *pats, **opts):
490 """add all new files, delete all missing files"""
490 """add all new files, delete all missing files"""
491 add, remove = [], []
491 add, remove = [], []
492 for src, abs, rel, exact in walk(repo, pats, opts):
492 for src, abs, rel, exact in walk(repo, pats, opts):
493 if src == 'f' and repo.dirstate.state(abs) == '?':
493 if src == 'f' and repo.dirstate.state(abs) == '?':
494 add.append(abs)
494 add.append(abs)
495 if not exact:
495 if not exact:
496 ui.status('adding ', rel, '\n')
496 ui.status('adding ', rel, '\n')
497 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
497 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
498 remove.append(abs)
498 remove.append(abs)
499 if not exact:
499 if not exact:
500 ui.status('removing ', rel, '\n')
500 ui.status('removing ', rel, '\n')
501 repo.add(add)
501 repo.add(add)
502 repo.remove(remove)
502 repo.remove(remove)
503
503
504 def annotate(ui, repo, *pats, **opts):
504 def annotate(ui, repo, *pats, **opts):
505 """show changeset information per file line"""
505 """show changeset information per file line"""
506 def getnode(rev):
506 def getnode(rev):
507 return short(repo.changelog.node(rev))
507 return short(repo.changelog.node(rev))
508
508
509 ucache = {}
509 ucache = {}
510 def getname(rev):
510 def getname(rev):
511 cl = repo.changelog.read(repo.changelog.node(rev))
511 cl = repo.changelog.read(repo.changelog.node(rev))
512 return trimuser(ui, cl[1], rev, ucache)
512 return trimuser(ui, cl[1], rev, ucache)
513
513
514 if not pats:
514 if not pats:
515 raise util.Abort('at least one file name or pattern required')
515 raise util.Abort('at least one file name or pattern required')
516
516
517 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
517 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
518 if not opts['user'] and not opts['changeset']:
518 if not opts['user'] and not opts['changeset']:
519 opts['number'] = 1
519 opts['number'] = 1
520
520
521 if opts['rev']:
521 if opts['rev']:
522 node = repo.changelog.lookup(opts['rev'])
522 node = repo.changelog.lookup(opts['rev'])
523 else:
523 else:
524 node = repo.dirstate.parents()[0]
524 node = repo.dirstate.parents()[0]
525 change = repo.changelog.read(node)
525 change = repo.changelog.read(node)
526 mmap = repo.manifest.read(change[0])
526 mmap = repo.manifest.read(change[0])
527
527
528 for src, abs, rel, exact in walk(repo, pats, opts):
528 for src, abs, rel, exact in walk(repo, pats, opts):
529 if abs not in mmap:
529 if abs not in mmap:
530 ui.warn("warning: %s is not in the repository!\n" % rel)
530 ui.warn("warning: %s is not in the repository!\n" % rel)
531 continue
531 continue
532
532
533 f = repo.file(abs)
533 f = repo.file(abs)
534 if not opts['text'] and util.binary(f.read(mmap[abs])):
534 if not opts['text'] and util.binary(f.read(mmap[abs])):
535 ui.write("%s: binary file\n" % rel)
535 ui.write("%s: binary file\n" % rel)
536 continue
536 continue
537
537
538 lines = f.annotate(mmap[abs])
538 lines = f.annotate(mmap[abs])
539 pieces = []
539 pieces = []
540
540
541 for o, f in opmap:
541 for o, f in opmap:
542 if opts[o]:
542 if opts[o]:
543 l = [f(n) for n, dummy in lines]
543 l = [f(n) for n, dummy in lines]
544 if l:
544 if l:
545 m = max(map(len, l))
545 m = max(map(len, l))
546 pieces.append(["%*s" % (m, x) for x in l])
546 pieces.append(["%*s" % (m, x) for x in l])
547
547
548 if pieces:
548 if pieces:
549 for p, l in zip(zip(*pieces), lines):
549 for p, l in zip(zip(*pieces), lines):
550 ui.write("%s: %s" % (" ".join(p), l[1]))
550 ui.write("%s: %s" % (" ".join(p), l[1]))
551
551
552 def bundle(ui, repo, fname, dest="default-push", **opts):
553 """create a changegroup file"""
554 f = open(fname, "wb")
555 dest = ui.expandpath(dest)
556 other = hg.repository(ui, dest)
557 o = repo.findoutgoing(other)
558 cg = repo.changegroup(o)
559
560 try:
561 f.write("HG10")
562 z = bz2.BZ2Compressor(9)
563 while 1:
564 chunk = cg.read(4096)
565 if not chunk:
566 break
567 f.write(z.compress(chunk))
568 f.write(z.flush())
569 except:
570 os.unlink(fname)
571
552 def cat(ui, repo, file1, rev=None, **opts):
572 def cat(ui, repo, file1, rev=None, **opts):
553 """output the latest or given revision of a file"""
573 """output the latest or given revision of a file"""
554 r = repo.file(relpath(repo, [file1])[0])
574 r = repo.file(relpath(repo, [file1])[0])
555 if rev:
575 if rev:
556 try:
576 try:
557 # assume all revision numbers are for changesets
577 # assume all revision numbers are for changesets
558 n = repo.lookup(rev)
578 n = repo.lookup(rev)
559 change = repo.changelog.read(n)
579 change = repo.changelog.read(n)
560 m = repo.manifest.read(change[0])
580 m = repo.manifest.read(change[0])
561 n = m[relpath(repo, [file1])[0]]
581 n = m[relpath(repo, [file1])[0]]
562 except hg.RepoError, KeyError:
582 except hg.RepoError, KeyError:
563 n = r.lookup(rev)
583 n = r.lookup(rev)
564 else:
584 else:
565 n = r.tip()
585 n = r.tip()
566 fp = make_file(repo, r, opts['output'], node=n)
586 fp = make_file(repo, r, opts['output'], node=n)
567 fp.write(r.read(n))
587 fp.write(r.read(n))
568
588
569 def clone(ui, source, dest=None, **opts):
589 def clone(ui, source, dest=None, **opts):
570 """make a copy of an existing repository"""
590 """make a copy of an existing repository"""
571 if dest is None:
591 if dest is None:
572 dest = os.path.basename(os.path.normpath(source))
592 dest = os.path.basename(os.path.normpath(source))
573
593
574 if os.path.exists(dest):
594 if os.path.exists(dest):
575 ui.warn("abort: destination '%s' already exists\n" % dest)
595 ui.warn("abort: destination '%s' already exists\n" % dest)
576 return 1
596 return 1
577
597
578 dest = os.path.realpath(dest)
598 dest = os.path.realpath(dest)
579
599
580 class Dircleanup:
600 class Dircleanup:
581 def __init__(self, dir_):
601 def __init__(self, dir_):
582 self.rmtree = shutil.rmtree
602 self.rmtree = shutil.rmtree
583 self.dir_ = dir_
603 self.dir_ = dir_
584 os.mkdir(dir_)
604 os.mkdir(dir_)
585 def close(self):
605 def close(self):
586 self.dir_ = None
606 self.dir_ = None
587 def __del__(self):
607 def __del__(self):
588 if self.dir_:
608 if self.dir_:
589 self.rmtree(self.dir_, True)
609 self.rmtree(self.dir_, True)
590
610
591 if opts['ssh']:
611 if opts['ssh']:
592 ui.setconfig("ui", "ssh", opts['ssh'])
612 ui.setconfig("ui", "ssh", opts['ssh'])
593 if opts['remotecmd']:
613 if opts['remotecmd']:
594 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
614 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
595
615
596 d = Dircleanup(dest)
616 d = Dircleanup(dest)
597 source = ui.expandpath(source)
617 source = ui.expandpath(source)
598 abspath = source
618 abspath = source
599 other = hg.repository(ui, source)
619 other = hg.repository(ui, source)
600
620
601 if other.dev() != -1:
621 if other.dev() != -1:
602 abspath = os.path.abspath(source)
622 abspath = os.path.abspath(source)
603 copyfile = (os.stat(dest).st_dev == other.dev()
623 copyfile = (os.stat(dest).st_dev == other.dev()
604 and getattr(os, 'link', None) or shutil.copy2)
624 and getattr(os, 'link', None) or shutil.copy2)
605 if copyfile is not shutil.copy2:
625 if copyfile is not shutil.copy2:
606 ui.note("cloning by hardlink\n")
626 ui.note("cloning by hardlink\n")
607
627
608 # we use a lock here because if we race with commit, we can
628 # we use a lock here because if we race with commit, we can
609 # end up with extra data in the cloned revlogs that's not
629 # end up with extra data in the cloned revlogs that's not
610 # pointed to by changesets, thus causing verify to fail
630 # pointed to by changesets, thus causing verify to fail
611 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
631 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
612
632
613 # and here to avoid premature writing to the target
633 # and here to avoid premature writing to the target
614 os.mkdir(os.path.join(dest, ".hg"))
634 os.mkdir(os.path.join(dest, ".hg"))
615 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
635 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
616
636
617 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
637 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
618 for f in files.split():
638 for f in files.split():
619 src = os.path.join(source, ".hg", f)
639 src = os.path.join(source, ".hg", f)
620 dst = os.path.join(dest, ".hg", f)
640 dst = os.path.join(dest, ".hg", f)
621 util.copyfiles(src, dst, copyfile)
641 util.copyfiles(src, dst, copyfile)
622
642
623 repo = hg.repository(ui, dest)
643 repo = hg.repository(ui, dest)
624
644
625 else:
645 else:
626 repo = hg.repository(ui, dest, create=1)
646 repo = hg.repository(ui, dest, create=1)
627 repo.pull(other)
647 repo.pull(other)
628
648
629 f = repo.opener("hgrc", "w")
649 f = repo.opener("hgrc", "w")
630 f.write("[paths]\n")
650 f.write("[paths]\n")
631 f.write("default = %s\n" % abspath)
651 f.write("default = %s\n" % abspath)
632
652
633 if not opts['noupdate']:
653 if not opts['noupdate']:
634 update(ui, repo)
654 update(ui, repo)
635
655
636 d.close()
656 d.close()
637
657
638 def commit(ui, repo, *pats, **opts):
658 def commit(ui, repo, *pats, **opts):
639 """commit the specified files or all outstanding changes"""
659 """commit the specified files or all outstanding changes"""
640 if opts['text']:
660 if opts['text']:
641 ui.warn("Warning: -t and --text is deprecated,"
661 ui.warn("Warning: -t and --text is deprecated,"
642 " please use -m or --message instead.\n")
662 " please use -m or --message instead.\n")
643 message = opts['message'] or opts['text']
663 message = opts['message'] or opts['text']
644 logfile = opts['logfile']
664 logfile = opts['logfile']
645 if not message and logfile:
665 if not message and logfile:
646 try:
666 try:
647 if logfile == '-':
667 if logfile == '-':
648 message = sys.stdin.read()
668 message = sys.stdin.read()
649 else:
669 else:
650 message = open(logfile).read()
670 message = open(logfile).read()
651 except IOError, why:
671 except IOError, why:
652 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
672 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
653
673
654 if opts['addremove']:
674 if opts['addremove']:
655 addremove(ui, repo, *pats, **opts)
675 addremove(ui, repo, *pats, **opts)
656 cwd = repo.getcwd()
676 cwd = repo.getcwd()
657 if not pats and cwd:
677 if not pats and cwd:
658 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
678 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
659 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
679 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
660 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
680 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
661 pats, opts)
681 pats, opts)
662 if pats:
682 if pats:
663 c, a, d, u = repo.changes(files=fns, match=match)
683 c, a, d, u = repo.changes(files=fns, match=match)
664 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
684 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
665 else:
685 else:
666 files = []
686 files = []
667 try:
687 try:
668 repo.commit(files, message, opts['user'], opts['date'], match)
688 repo.commit(files, message, opts['user'], opts['date'], match)
669 except ValueError, inst:
689 except ValueError, inst:
670 raise util.Abort(str(inst))
690 raise util.Abort(str(inst))
671
691
672 def copy(ui, repo, source, dest):
692 def copy(ui, repo, source, dest):
673 """mark a file as copied or renamed for the next commit"""
693 """mark a file as copied or renamed for the next commit"""
674 return repo.copy(*relpath(repo, (source, dest)))
694 return repo.copy(*relpath(repo, (source, dest)))
675
695
676 def debugcheckstate(ui, repo):
696 def debugcheckstate(ui, repo):
677 """validate the correctness of the current dirstate"""
697 """validate the correctness of the current dirstate"""
678 parent1, parent2 = repo.dirstate.parents()
698 parent1, parent2 = repo.dirstate.parents()
679 repo.dirstate.read()
699 repo.dirstate.read()
680 dc = repo.dirstate.map
700 dc = repo.dirstate.map
681 keys = dc.keys()
701 keys = dc.keys()
682 keys.sort()
702 keys.sort()
683 m1n = repo.changelog.read(parent1)[0]
703 m1n = repo.changelog.read(parent1)[0]
684 m2n = repo.changelog.read(parent2)[0]
704 m2n = repo.changelog.read(parent2)[0]
685 m1 = repo.manifest.read(m1n)
705 m1 = repo.manifest.read(m1n)
686 m2 = repo.manifest.read(m2n)
706 m2 = repo.manifest.read(m2n)
687 errors = 0
707 errors = 0
688 for f in dc:
708 for f in dc:
689 state = repo.dirstate.state(f)
709 state = repo.dirstate.state(f)
690 if state in "nr" and f not in m1:
710 if state in "nr" and f not in m1:
691 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
711 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
692 errors += 1
712 errors += 1
693 if state in "a" and f in m1:
713 if state in "a" and f in m1:
694 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
714 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
695 errors += 1
715 errors += 1
696 if state in "m" and f not in m1 and f not in m2:
716 if state in "m" and f not in m1 and f not in m2:
697 ui.warn("%s in state %s, but not in either manifest\n" %
717 ui.warn("%s in state %s, but not in either manifest\n" %
698 (f, state))
718 (f, state))
699 errors += 1
719 errors += 1
700 for f in m1:
720 for f in m1:
701 state = repo.dirstate.state(f)
721 state = repo.dirstate.state(f)
702 if state not in "nrm":
722 if state not in "nrm":
703 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
723 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
704 errors += 1
724 errors += 1
705 if errors:
725 if errors:
706 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
726 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
707
727
708 def debugconfig(ui):
728 def debugconfig(ui):
709 """show combined config settings from all hgrc files"""
729 """show combined config settings from all hgrc files"""
710 try:
730 try:
711 repo = hg.repository(ui)
731 repo = hg.repository(ui)
712 except hg.RepoError:
732 except hg.RepoError:
713 pass
733 pass
714 for section, name, value in ui.walkconfig():
734 for section, name, value in ui.walkconfig():
715 ui.write('%s.%s=%s\n' % (section, name, value))
735 ui.write('%s.%s=%s\n' % (section, name, value))
716
736
717 def debugstate(ui, repo):
737 def debugstate(ui, repo):
718 """show the contents of the current dirstate"""
738 """show the contents of the current dirstate"""
719 repo.dirstate.read()
739 repo.dirstate.read()
720 dc = repo.dirstate.map
740 dc = repo.dirstate.map
721 keys = dc.keys()
741 keys = dc.keys()
722 keys.sort()
742 keys.sort()
723 for file_ in keys:
743 for file_ in keys:
724 ui.write("%c %3o %10d %s %s\n"
744 ui.write("%c %3o %10d %s %s\n"
725 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
745 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
726 time.strftime("%x %X",
746 time.strftime("%x %X",
727 time.localtime(dc[file_][3])), file_))
747 time.localtime(dc[file_][3])), file_))
728 for f in repo.dirstate.copies:
748 for f in repo.dirstate.copies:
729 ui.write("copy: %s -> %s\n" % (repo.dirstate.copies[f], f))
749 ui.write("copy: %s -> %s\n" % (repo.dirstate.copies[f], f))
730
750
731 def debugdata(ui, file_, rev):
751 def debugdata(ui, file_, rev):
732 """dump the contents of an data file revision"""
752 """dump the contents of an data file revision"""
733 r = revlog.revlog(file, file_[:-2] + ".i", file_)
753 r = revlog.revlog(file, file_[:-2] + ".i", file_)
734 ui.write(r.revision(r.lookup(rev)))
754 ui.write(r.revision(r.lookup(rev)))
735
755
736 def debugindex(ui, file_):
756 def debugindex(ui, file_):
737 """dump the contents of an index file"""
757 """dump the contents of an index file"""
738 r = revlog.revlog(file, file_, "")
758 r = revlog.revlog(file, file_, "")
739 ui.write(" rev offset length base linkrev" +
759 ui.write(" rev offset length base linkrev" +
740 " nodeid p1 p2\n")
760 " nodeid p1 p2\n")
741 for i in range(r.count()):
761 for i in range(r.count()):
742 e = r.index[i]
762 e = r.index[i]
743 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
763 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
744 i, e[0], e[1], e[2], e[3],
764 i, e[0], e[1], e[2], e[3],
745 short(e[6]), short(e[4]), short(e[5])))
765 short(e[6]), short(e[4]), short(e[5])))
746
766
747 def debugindexdot(ui, file_):
767 def debugindexdot(ui, file_):
748 """dump an index DAG as a .dot file"""
768 """dump an index DAG as a .dot file"""
749 r = revlog.revlog(file, file_, "")
769 r = revlog.revlog(file, file_, "")
750 ui.write("digraph G {\n")
770 ui.write("digraph G {\n")
751 for i in range(r.count()):
771 for i in range(r.count()):
752 e = r.index[i]
772 e = r.index[i]
753 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
773 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
754 if e[5] != nullid:
774 if e[5] != nullid:
755 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
775 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
756 ui.write("}\n")
776 ui.write("}\n")
757
777
758 def debugrename(ui, repo, file, rev=None):
778 def debugrename(ui, repo, file, rev=None):
759 """dump rename information"""
779 """dump rename information"""
760 r = repo.file(relpath(repo, [file])[0])
780 r = repo.file(relpath(repo, [file])[0])
761 if rev:
781 if rev:
762 try:
782 try:
763 # assume all revision numbers are for changesets
783 # assume all revision numbers are for changesets
764 n = repo.lookup(rev)
784 n = repo.lookup(rev)
765 change = repo.changelog.read(n)
785 change = repo.changelog.read(n)
766 m = repo.manifest.read(change[0])
786 m = repo.manifest.read(change[0])
767 n = m[relpath(repo, [file])[0]]
787 n = m[relpath(repo, [file])[0]]
768 except hg.RepoError, KeyError:
788 except hg.RepoError, KeyError:
769 n = r.lookup(rev)
789 n = r.lookup(rev)
770 else:
790 else:
771 n = r.tip()
791 n = r.tip()
772 m = r.renamed(n)
792 m = r.renamed(n)
773 if m:
793 if m:
774 ui.write("renamed from %s:%s\n" % (m[0], hex(m[1])))
794 ui.write("renamed from %s:%s\n" % (m[0], hex(m[1])))
775 else:
795 else:
776 ui.write("not renamed\n")
796 ui.write("not renamed\n")
777
797
778 def debugwalk(ui, repo, *pats, **opts):
798 def debugwalk(ui, repo, *pats, **opts):
779 """show how files match on given patterns"""
799 """show how files match on given patterns"""
780 items = list(walk(repo, pats, opts))
800 items = list(walk(repo, pats, opts))
781 if not items:
801 if not items:
782 return
802 return
783 fmt = '%%s %%-%ds %%-%ds %%s\n' % (
803 fmt = '%%s %%-%ds %%-%ds %%s\n' % (
784 max([len(abs) for (src, abs, rel, exact) in items]),
804 max([len(abs) for (src, abs, rel, exact) in items]),
785 max([len(rel) for (src, abs, rel, exact) in items]))
805 max([len(rel) for (src, abs, rel, exact) in items]))
786 for src, abs, rel, exact in items:
806 for src, abs, rel, exact in items:
787 ui.write(fmt % (src, abs, rel, exact and 'exact' or ''))
807 ui.write(fmt % (src, abs, rel, exact and 'exact' or ''))
788
808
789 def diff(ui, repo, *pats, **opts):
809 def diff(ui, repo, *pats, **opts):
790 """diff working directory (or selected files)"""
810 """diff working directory (or selected files)"""
791 node1, node2 = None, None
811 node1, node2 = None, None
792 revs = [repo.lookup(x) for x in opts['rev']]
812 revs = [repo.lookup(x) for x in opts['rev']]
793
813
794 if len(revs) > 0:
814 if len(revs) > 0:
795 node1 = revs[0]
815 node1 = revs[0]
796 if len(revs) > 1:
816 if len(revs) > 1:
797 node2 = revs[1]
817 node2 = revs[1]
798 if len(revs) > 2:
818 if len(revs) > 2:
799 raise util.Abort("too many revisions to diff")
819 raise util.Abort("too many revisions to diff")
800
820
801 files = []
821 files = []
802 match = util.always
822 match = util.always
803 if pats:
823 if pats:
804 roots, match, results = makewalk(repo, pats, opts)
824 roots, match, results = makewalk(repo, pats, opts)
805 for src, abs, rel, exact in results:
825 for src, abs, rel, exact in results:
806 files.append(abs)
826 files.append(abs)
807
827
808 dodiff(sys.stdout, ui, repo, node1, node2, files, match=match,
828 dodiff(sys.stdout, ui, repo, node1, node2, files, match=match,
809 text=opts['text'])
829 text=opts['text'])
810
830
811 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
831 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
812 node = repo.lookup(changeset)
832 node = repo.lookup(changeset)
813 prev, other = repo.changelog.parents(node)
833 prev, other = repo.changelog.parents(node)
814 change = repo.changelog.read(node)
834 change = repo.changelog.read(node)
815
835
816 fp = make_file(repo, repo.changelog, opts['output'],
836 fp = make_file(repo, repo.changelog, opts['output'],
817 node=node, total=total, seqno=seqno,
837 node=node, total=total, seqno=seqno,
818 revwidth=revwidth)
838 revwidth=revwidth)
819 if fp != sys.stdout:
839 if fp != sys.stdout:
820 ui.note("%s\n" % fp.name)
840 ui.note("%s\n" % fp.name)
821
841
822 fp.write("# HG changeset patch\n")
842 fp.write("# HG changeset patch\n")
823 fp.write("# User %s\n" % change[1])
843 fp.write("# User %s\n" % change[1])
824 fp.write("# Node ID %s\n" % hex(node))
844 fp.write("# Node ID %s\n" % hex(node))
825 fp.write("# Parent %s\n" % hex(prev))
845 fp.write("# Parent %s\n" % hex(prev))
826 if other != nullid:
846 if other != nullid:
827 fp.write("# Parent %s\n" % hex(other))
847 fp.write("# Parent %s\n" % hex(other))
828 fp.write(change[4].rstrip())
848 fp.write(change[4].rstrip())
829 fp.write("\n\n")
849 fp.write("\n\n")
830
850
831 dodiff(fp, ui, repo, prev, node, text=opts['text'])
851 dodiff(fp, ui, repo, prev, node, text=opts['text'])
832 if fp != sys.stdout:
852 if fp != sys.stdout:
833 fp.close()
853 fp.close()
834
854
835 def export(ui, repo, *changesets, **opts):
855 def export(ui, repo, *changesets, **opts):
836 """dump the header and diffs for one or more changesets"""
856 """dump the header and diffs for one or more changesets"""
837 if not changesets:
857 if not changesets:
838 raise util.Abort("export requires at least one changeset")
858 raise util.Abort("export requires at least one changeset")
839 seqno = 0
859 seqno = 0
840 revs = list(revrange(ui, repo, changesets))
860 revs = list(revrange(ui, repo, changesets))
841 total = len(revs)
861 total = len(revs)
842 revwidth = max(map(len, revs))
862 revwidth = max(map(len, revs))
843 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
863 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
844 for cset in revs:
864 for cset in revs:
845 seqno += 1
865 seqno += 1
846 doexport(ui, repo, cset, seqno, total, revwidth, opts)
866 doexport(ui, repo, cset, seqno, total, revwidth, opts)
847
867
848 def forget(ui, repo, *pats, **opts):
868 def forget(ui, repo, *pats, **opts):
849 """don't add the specified files on the next commit"""
869 """don't add the specified files on the next commit"""
850 forget = []
870 forget = []
851 for src, abs, rel, exact in walk(repo, pats, opts):
871 for src, abs, rel, exact in walk(repo, pats, opts):
852 if repo.dirstate.state(abs) == 'a':
872 if repo.dirstate.state(abs) == 'a':
853 forget.append(abs)
873 forget.append(abs)
854 if not exact:
874 if not exact:
855 ui.status('forgetting ', rel, '\n')
875 ui.status('forgetting ', rel, '\n')
856 repo.forget(forget)
876 repo.forget(forget)
857
877
858 def grep(ui, repo, pattern, *pats, **opts):
878 def grep(ui, repo, pattern, *pats, **opts):
859 """search for a pattern in specified files and revisions"""
879 """search for a pattern in specified files and revisions"""
860 reflags = 0
880 reflags = 0
861 if opts['ignore_case']:
881 if opts['ignore_case']:
862 reflags |= re.I
882 reflags |= re.I
863 regexp = re.compile(pattern, reflags)
883 regexp = re.compile(pattern, reflags)
864 sep, eol = ':', '\n'
884 sep, eol = ':', '\n'
865 if opts['print0']:
885 if opts['print0']:
866 sep = eol = '\0'
886 sep = eol = '\0'
867
887
868 fcache = {}
888 fcache = {}
869 def getfile(fn):
889 def getfile(fn):
870 if fn not in fcache:
890 if fn not in fcache:
871 fcache[fn] = repo.file(fn)
891 fcache[fn] = repo.file(fn)
872 return fcache[fn]
892 return fcache[fn]
873
893
874 def matchlines(body):
894 def matchlines(body):
875 begin = 0
895 begin = 0
876 linenum = 0
896 linenum = 0
877 while True:
897 while True:
878 match = regexp.search(body, begin)
898 match = regexp.search(body, begin)
879 if not match:
899 if not match:
880 break
900 break
881 mstart, mend = match.span()
901 mstart, mend = match.span()
882 linenum += body.count('\n', begin, mstart) + 1
902 linenum += body.count('\n', begin, mstart) + 1
883 lstart = body.rfind('\n', begin, mstart) + 1 or begin
903 lstart = body.rfind('\n', begin, mstart) + 1 or begin
884 lend = body.find('\n', mend)
904 lend = body.find('\n', mend)
885 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
905 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
886 begin = lend + 1
906 begin = lend + 1
887
907
888 class linestate:
908 class linestate:
889 def __init__(self, line, linenum, colstart, colend):
909 def __init__(self, line, linenum, colstart, colend):
890 self.line = line
910 self.line = line
891 self.linenum = linenum
911 self.linenum = linenum
892 self.colstart = colstart
912 self.colstart = colstart
893 self.colend = colend
913 self.colend = colend
894 def __eq__(self, other):
914 def __eq__(self, other):
895 return self.line == other.line
915 return self.line == other.line
896 def __hash__(self):
916 def __hash__(self):
897 return hash(self.line)
917 return hash(self.line)
898
918
899 matches = {}
919 matches = {}
900 def grepbody(fn, rev, body):
920 def grepbody(fn, rev, body):
901 matches[rev].setdefault(fn, {})
921 matches[rev].setdefault(fn, {})
902 m = matches[rev][fn]
922 m = matches[rev][fn]
903 for lnum, cstart, cend, line in matchlines(body):
923 for lnum, cstart, cend, line in matchlines(body):
904 s = linestate(line, lnum, cstart, cend)
924 s = linestate(line, lnum, cstart, cend)
905 m[s] = s
925 m[s] = s
906
926
907 prev = {}
927 prev = {}
908 ucache = {}
928 ucache = {}
909 def display(fn, rev, states, prevstates):
929 def display(fn, rev, states, prevstates):
910 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
930 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
911 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
931 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
912 counts = {'-': 0, '+': 0}
932 counts = {'-': 0, '+': 0}
913 filerevmatches = {}
933 filerevmatches = {}
914 for l in diff:
934 for l in diff:
915 if incrementing or not opts['all']:
935 if incrementing or not opts['all']:
916 change = ((l in prevstates) and '-') or '+'
936 change = ((l in prevstates) and '-') or '+'
917 r = rev
937 r = rev
918 else:
938 else:
919 change = ((l in states) and '-') or '+'
939 change = ((l in states) and '-') or '+'
920 r = prev[fn]
940 r = prev[fn]
921 cols = [fn, str(rev)]
941 cols = [fn, str(rev)]
922 if opts['line_number']: cols.append(str(l.linenum))
942 if opts['line_number']: cols.append(str(l.linenum))
923 if opts['all']: cols.append(change)
943 if opts['all']: cols.append(change)
924 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
944 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
925 ucache))
945 ucache))
926 if opts['files_with_matches']:
946 if opts['files_with_matches']:
927 c = (fn, rev)
947 c = (fn, rev)
928 if c in filerevmatches: continue
948 if c in filerevmatches: continue
929 filerevmatches[c] = 1
949 filerevmatches[c] = 1
930 else:
950 else:
931 cols.append(l.line)
951 cols.append(l.line)
932 ui.write(sep.join(cols), eol)
952 ui.write(sep.join(cols), eol)
933 counts[change] += 1
953 counts[change] += 1
934 return counts['+'], counts['-']
954 return counts['+'], counts['-']
935
955
936 fstate = {}
956 fstate = {}
937 skip = {}
957 skip = {}
938 changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
958 changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
939 count = 0
959 count = 0
940 for st, rev, fns in changeiter:
960 for st, rev, fns in changeiter:
941 if st == 'window':
961 if st == 'window':
942 incrementing = rev
962 incrementing = rev
943 matches.clear()
963 matches.clear()
944 elif st == 'add':
964 elif st == 'add':
945 change = repo.changelog.read(repo.lookup(str(rev)))
965 change = repo.changelog.read(repo.lookup(str(rev)))
946 mf = repo.manifest.read(change[0])
966 mf = repo.manifest.read(change[0])
947 matches[rev] = {}
967 matches[rev] = {}
948 for fn in fns:
968 for fn in fns:
949 if fn in skip: continue
969 if fn in skip: continue
950 fstate.setdefault(fn, {})
970 fstate.setdefault(fn, {})
951 try:
971 try:
952 grepbody(fn, rev, getfile(fn).read(mf[fn]))
972 grepbody(fn, rev, getfile(fn).read(mf[fn]))
953 except KeyError:
973 except KeyError:
954 pass
974 pass
955 elif st == 'iter':
975 elif st == 'iter':
956 states = matches[rev].items()
976 states = matches[rev].items()
957 states.sort()
977 states.sort()
958 for fn, m in states:
978 for fn, m in states:
959 if fn in skip: continue
979 if fn in skip: continue
960 if incrementing or not opts['all'] or fstate[fn]:
980 if incrementing or not opts['all'] or fstate[fn]:
961 pos, neg = display(fn, rev, m, fstate[fn])
981 pos, neg = display(fn, rev, m, fstate[fn])
962 count += pos + neg
982 count += pos + neg
963 if pos and not opts['all']:
983 if pos and not opts['all']:
964 skip[fn] = True
984 skip[fn] = True
965 fstate[fn] = m
985 fstate[fn] = m
966 prev[fn] = rev
986 prev[fn] = rev
967
987
968 if not incrementing:
988 if not incrementing:
969 fstate = fstate.items()
989 fstate = fstate.items()
970 fstate.sort()
990 fstate.sort()
971 for fn, state in fstate:
991 for fn, state in fstate:
972 if fn in skip: continue
992 if fn in skip: continue
973 display(fn, rev, {}, state)
993 display(fn, rev, {}, state)
974 return (count == 0 and 1) or 0
994 return (count == 0 and 1) or 0
975
995
976 def heads(ui, repo, **opts):
996 def heads(ui, repo, **opts):
977 """show current repository heads"""
997 """show current repository heads"""
978 heads = repo.changelog.heads()
998 heads = repo.changelog.heads()
979 br = None
999 br = None
980 if opts['branches']:
1000 if opts['branches']:
981 br = repo.branchlookup(heads)
1001 br = repo.branchlookup(heads)
982 for n in repo.changelog.heads():
1002 for n in repo.changelog.heads():
983 show_changeset(ui, repo, changenode=n, brinfo=br)
1003 show_changeset(ui, repo, changenode=n, brinfo=br)
984
1004
985 def identify(ui, repo):
1005 def identify(ui, repo):
986 """print information about the working copy"""
1006 """print information about the working copy"""
987 parents = [p for p in repo.dirstate.parents() if p != nullid]
1007 parents = [p for p in repo.dirstate.parents() if p != nullid]
988 if not parents:
1008 if not parents:
989 ui.write("unknown\n")
1009 ui.write("unknown\n")
990 return
1010 return
991
1011
992 hexfunc = ui.verbose and hex or short
1012 hexfunc = ui.verbose and hex or short
993 (c, a, d, u) = repo.changes()
1013 (c, a, d, u) = repo.changes()
994 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
1014 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
995 (c or a or d) and "+" or "")]
1015 (c or a or d) and "+" or "")]
996
1016
997 if not ui.quiet:
1017 if not ui.quiet:
998 # multiple tags for a single parent separated by '/'
1018 # multiple tags for a single parent separated by '/'
999 parenttags = ['/'.join(tags)
1019 parenttags = ['/'.join(tags)
1000 for tags in map(repo.nodetags, parents) if tags]
1020 for tags in map(repo.nodetags, parents) if tags]
1001 # tags for multiple parents separated by ' + '
1021 # tags for multiple parents separated by ' + '
1002 if parenttags:
1022 if parenttags:
1003 output.append(' + '.join(parenttags))
1023 output.append(' + '.join(parenttags))
1004
1024
1005 ui.write("%s\n" % ' '.join(output))
1025 ui.write("%s\n" % ' '.join(output))
1006
1026
1007 def import_(ui, repo, patch1, *patches, **opts):
1027 def import_(ui, repo, patch1, *patches, **opts):
1008 """import an ordered set of patches"""
1028 """import an ordered set of patches"""
1009 patches = (patch1,) + patches
1029 patches = (patch1,) + patches
1010
1030
1011 if not opts['force']:
1031 if not opts['force']:
1012 (c, a, d, u) = repo.changes()
1032 (c, a, d, u) = repo.changes()
1013 if c or a or d:
1033 if c or a or d:
1014 ui.warn("abort: outstanding uncommitted changes!\n")
1034 ui.warn("abort: outstanding uncommitted changes!\n")
1015 return 1
1035 return 1
1016
1036
1017 d = opts["base"]
1037 d = opts["base"]
1018 strip = opts["strip"]
1038 strip = opts["strip"]
1019
1039
1020 mailre = re.compile(r'(?:From |[\w-]+:)')
1040 mailre = re.compile(r'(?:From |[\w-]+:)')
1021 diffre = re.compile(r'(?:diff -|--- .*\s+\w+ \w+ +\d+ \d+:\d+:\d+ \d+)')
1041 diffre = re.compile(r'(?:diff -|--- .*\s+\w+ \w+ +\d+ \d+:\d+:\d+ \d+)')
1022
1042
1023 for patch in patches:
1043 for patch in patches:
1024 ui.status("applying %s\n" % patch)
1044 ui.status("applying %s\n" % patch)
1025 pf = os.path.join(d, patch)
1045 pf = os.path.join(d, patch)
1026
1046
1027 message = []
1047 message = []
1028 user = None
1048 user = None
1029 hgpatch = False
1049 hgpatch = False
1030 for line in file(pf):
1050 for line in file(pf):
1031 line = line.rstrip()
1051 line = line.rstrip()
1032 if not message and mailre.match(line) and not opts['force']:
1052 if not message and mailre.match(line) and not opts['force']:
1033 if len(line) > 35: line = line[:32] + '...'
1053 if len(line) > 35: line = line[:32] + '...'
1034 raise util.Abort('first line looks like a '
1054 raise util.Abort('first line looks like a '
1035 'mail header: ' + line)
1055 'mail header: ' + line)
1036 if diffre.match(line):
1056 if diffre.match(line):
1037 break
1057 break
1038 elif hgpatch:
1058 elif hgpatch:
1039 # parse values when importing the result of an hg export
1059 # parse values when importing the result of an hg export
1040 if line.startswith("# User "):
1060 if line.startswith("# User "):
1041 user = line[7:]
1061 user = line[7:]
1042 ui.debug('User: %s\n' % user)
1062 ui.debug('User: %s\n' % user)
1043 elif not line.startswith("# ") and line:
1063 elif not line.startswith("# ") and line:
1044 message.append(line)
1064 message.append(line)
1045 hgpatch = False
1065 hgpatch = False
1046 elif line == '# HG changeset patch':
1066 elif line == '# HG changeset patch':
1047 hgpatch = True
1067 hgpatch = True
1048 message = [] # We may have collected garbage
1068 message = [] # We may have collected garbage
1049 else:
1069 else:
1050 message.append(line)
1070 message.append(line)
1051
1071
1052 # make sure message isn't empty
1072 # make sure message isn't empty
1053 if not message:
1073 if not message:
1054 message = "imported patch %s\n" % patch
1074 message = "imported patch %s\n" % patch
1055 else:
1075 else:
1056 message = "%s\n" % '\n'.join(message)
1076 message = "%s\n" % '\n'.join(message)
1057 ui.debug('message:\n%s\n' % message)
1077 ui.debug('message:\n%s\n' % message)
1058
1078
1059 f = os.popen("patch -p%d < '%s'" % (strip, pf))
1079 f = os.popen("patch -p%d < '%s'" % (strip, pf))
1060 files = []
1080 files = []
1061 for l in f.read().splitlines():
1081 for l in f.read().splitlines():
1062 l.rstrip('\r\n');
1082 l.rstrip('\r\n');
1063 ui.status("%s\n" % l)
1083 ui.status("%s\n" % l)
1064 if l.startswith('patching file '):
1084 if l.startswith('patching file '):
1065 pf = l[14:]
1085 pf = l[14:]
1066 if pf not in files:
1086 if pf not in files:
1067 files.append(pf)
1087 files.append(pf)
1068 patcherr = f.close()
1088 patcherr = f.close()
1069 if patcherr:
1089 if patcherr:
1070 raise util.Abort("patch failed")
1090 raise util.Abort("patch failed")
1071
1091
1072 if len(files) > 0:
1092 if len(files) > 0:
1073 addremove(ui, repo, *files)
1093 addremove(ui, repo, *files)
1074 repo.commit(files, message, user)
1094 repo.commit(files, message, user)
1075
1095
1076 def incoming(ui, repo, source="default", **opts):
1096 def incoming(ui, repo, source="default", **opts):
1077 """show new changesets found in source"""
1097 """show new changesets found in source"""
1078 source = ui.expandpath(source)
1098 source = ui.expandpath(source)
1079 other = hg.repository(ui, source)
1099 other = hg.repository(ui, source)
1080 if not other.local():
1100 if not other.local():
1081 ui.warn("abort: incoming doesn't work for remote"
1101 ui.warn("abort: incoming doesn't work for remote"
1082 + " repositories yet, sorry!\n")
1102 + " repositories yet, sorry!\n")
1083 return 1
1103 return 1
1084 o = repo.findincoming(other)
1104 o = repo.findincoming(other)
1085 if not o:
1105 if not o:
1086 return
1106 return
1087 o = other.newer(o)
1107 o = other.newer(o)
1088 o.reverse()
1108 o.reverse()
1089 for n in o:
1109 for n in o:
1090 show_changeset(ui, other, changenode=n)
1110 show_changeset(ui, other, changenode=n)
1091 if opts['patch']:
1111 if opts['patch']:
1092 prev = other.changelog.parents(n)[0]
1112 prev = other.changelog.parents(n)[0]
1093 dodiff(ui, ui, other, prev, n)
1113 dodiff(ui, ui, other, prev, n)
1094 ui.write("\n")
1114 ui.write("\n")
1095
1115
1096 def init(ui, dest="."):
1116 def init(ui, dest="."):
1097 """create a new repository in the given directory"""
1117 """create a new repository in the given directory"""
1098 if not os.path.exists(dest):
1118 if not os.path.exists(dest):
1099 os.mkdir(dest)
1119 os.mkdir(dest)
1100 hg.repository(ui, dest, create=1)
1120 hg.repository(ui, dest, create=1)
1101
1121
1102 def locate(ui, repo, *pats, **opts):
1122 def locate(ui, repo, *pats, **opts):
1103 """locate files matching specific patterns"""
1123 """locate files matching specific patterns"""
1104 end = opts['print0'] and '\0' or '\n'
1124 end = opts['print0'] and '\0' or '\n'
1105
1125
1106 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1126 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1107 if repo.dirstate.state(abs) == '?':
1127 if repo.dirstate.state(abs) == '?':
1108 continue
1128 continue
1109 if opts['fullpath']:
1129 if opts['fullpath']:
1110 ui.write(os.path.join(repo.root, abs), end)
1130 ui.write(os.path.join(repo.root, abs), end)
1111 else:
1131 else:
1112 ui.write(rel, end)
1132 ui.write(rel, end)
1113
1133
1114 def log(ui, repo, *pats, **opts):
1134 def log(ui, repo, *pats, **opts):
1115 """show revision history of entire repository or files"""
1135 """show revision history of entire repository or files"""
1116 class dui:
1136 class dui:
1117 # Implement and delegate some ui protocol. Save hunks of
1137 # Implement and delegate some ui protocol. Save hunks of
1118 # output for later display in the desired order.
1138 # output for later display in the desired order.
1119 def __init__(self, ui):
1139 def __init__(self, ui):
1120 self.ui = ui
1140 self.ui = ui
1121 self.hunk = {}
1141 self.hunk = {}
1122 def bump(self, rev):
1142 def bump(self, rev):
1123 self.rev = rev
1143 self.rev = rev
1124 self.hunk[rev] = []
1144 self.hunk[rev] = []
1125 def note(self, *args):
1145 def note(self, *args):
1126 if self.verbose:
1146 if self.verbose:
1127 self.write(*args)
1147 self.write(*args)
1128 def status(self, *args):
1148 def status(self, *args):
1129 if not self.quiet:
1149 if not self.quiet:
1130 self.write(*args)
1150 self.write(*args)
1131 def write(self, *args):
1151 def write(self, *args):
1132 self.hunk[self.rev].append(args)
1152 self.hunk[self.rev].append(args)
1133 def __getattr__(self, key):
1153 def __getattr__(self, key):
1134 return getattr(self.ui, key)
1154 return getattr(self.ui, key)
1135 cwd = repo.getcwd()
1155 cwd = repo.getcwd()
1136 if not pats and cwd:
1156 if not pats and cwd:
1137 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1157 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1138 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1158 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1139 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1159 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1140 pats, opts)
1160 pats, opts)
1141 for st, rev, fns in changeiter:
1161 for st, rev, fns in changeiter:
1142 if st == 'window':
1162 if st == 'window':
1143 du = dui(ui)
1163 du = dui(ui)
1144 elif st == 'add':
1164 elif st == 'add':
1145 du.bump(rev)
1165 du.bump(rev)
1146 show_changeset(du, repo, rev)
1166 show_changeset(du, repo, rev)
1147 if opts['patch']:
1167 if opts['patch']:
1148 changenode = repo.changelog.node(rev)
1168 changenode = repo.changelog.node(rev)
1149 prev, other = repo.changelog.parents(changenode)
1169 prev, other = repo.changelog.parents(changenode)
1150 dodiff(du, du, repo, prev, changenode, fns)
1170 dodiff(du, du, repo, prev, changenode, fns)
1151 du.write("\n\n")
1171 du.write("\n\n")
1152 elif st == 'iter':
1172 elif st == 'iter':
1153 for args in du.hunk[rev]:
1173 for args in du.hunk[rev]:
1154 ui.write(*args)
1174 ui.write(*args)
1155
1175
1156 def manifest(ui, repo, rev=None):
1176 def manifest(ui, repo, rev=None):
1157 """output the latest or given revision of the project manifest"""
1177 """output the latest or given revision of the project manifest"""
1158 if rev:
1178 if rev:
1159 try:
1179 try:
1160 # assume all revision numbers are for changesets
1180 # assume all revision numbers are for changesets
1161 n = repo.lookup(rev)
1181 n = repo.lookup(rev)
1162 change = repo.changelog.read(n)
1182 change = repo.changelog.read(n)
1163 n = change[0]
1183 n = change[0]
1164 except hg.RepoError:
1184 except hg.RepoError:
1165 n = repo.manifest.lookup(rev)
1185 n = repo.manifest.lookup(rev)
1166 else:
1186 else:
1167 n = repo.manifest.tip()
1187 n = repo.manifest.tip()
1168 m = repo.manifest.read(n)
1188 m = repo.manifest.read(n)
1169 mf = repo.manifest.readflags(n)
1189 mf = repo.manifest.readflags(n)
1170 files = m.keys()
1190 files = m.keys()
1171 files.sort()
1191 files.sort()
1172
1192
1173 for f in files:
1193 for f in files:
1174 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1194 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1175
1195
1176 def outgoing(ui, repo, dest="default-push", **opts):
1196 def outgoing(ui, repo, dest="default-push", **opts):
1177 """show changesets not found in destination"""
1197 """show changesets not found in destination"""
1178 dest = ui.expandpath(dest)
1198 dest = ui.expandpath(dest)
1179 other = hg.repository(ui, dest)
1199 other = hg.repository(ui, dest)
1180 o = repo.findoutgoing(other)
1200 o = repo.findoutgoing(other)
1181 o = repo.newer(o)
1201 o = repo.newer(o)
1182 o.reverse()
1202 o.reverse()
1183 for n in o:
1203 for n in o:
1184 show_changeset(ui, repo, changenode=n)
1204 show_changeset(ui, repo, changenode=n)
1185 if opts['patch']:
1205 if opts['patch']:
1186 prev = repo.changelog.parents(n)[0]
1206 prev = repo.changelog.parents(n)[0]
1187 dodiff(ui, ui, repo, prev, n)
1207 dodiff(ui, ui, repo, prev, n)
1188 ui.write("\n")
1208 ui.write("\n")
1189
1209
1190 def parents(ui, repo, rev=None):
1210 def parents(ui, repo, rev=None):
1191 """show the parents of the working dir or revision"""
1211 """show the parents of the working dir or revision"""
1192 if rev:
1212 if rev:
1193 p = repo.changelog.parents(repo.lookup(rev))
1213 p = repo.changelog.parents(repo.lookup(rev))
1194 else:
1214 else:
1195 p = repo.dirstate.parents()
1215 p = repo.dirstate.parents()
1196
1216
1197 for n in p:
1217 for n in p:
1198 if n != nullid:
1218 if n != nullid:
1199 show_changeset(ui, repo, changenode=n)
1219 show_changeset(ui, repo, changenode=n)
1200
1220
1201 def paths(ui, search=None):
1221 def paths(ui, search=None):
1202 """show definition of symbolic path names"""
1222 """show definition of symbolic path names"""
1203 try:
1223 try:
1204 repo = hg.repository(ui=ui)
1224 repo = hg.repository(ui=ui)
1205 except hg.RepoError:
1225 except hg.RepoError:
1206 pass
1226 pass
1207
1227
1208 if search:
1228 if search:
1209 for name, path in ui.configitems("paths"):
1229 for name, path in ui.configitems("paths"):
1210 if name == search:
1230 if name == search:
1211 ui.write("%s\n" % path)
1231 ui.write("%s\n" % path)
1212 return
1232 return
1213 ui.warn("not found!\n")
1233 ui.warn("not found!\n")
1214 return 1
1234 return 1
1215 else:
1235 else:
1216 for name, path in ui.configitems("paths"):
1236 for name, path in ui.configitems("paths"):
1217 ui.write("%s = %s\n" % (name, path))
1237 ui.write("%s = %s\n" % (name, path))
1218
1238
1219 def pull(ui, repo, source="default", **opts):
1239 def pull(ui, repo, source="default", **opts):
1220 """pull changes from the specified source"""
1240 """pull changes from the specified source"""
1221 source = ui.expandpath(source)
1241 source = ui.expandpath(source)
1222 ui.status('pulling from %s\n' % (source))
1242 ui.status('pulling from %s\n' % (source))
1223
1243
1224 if opts['ssh']:
1244 if opts['ssh']:
1225 ui.setconfig("ui", "ssh", opts['ssh'])
1245 ui.setconfig("ui", "ssh", opts['ssh'])
1226 if opts['remotecmd']:
1246 if opts['remotecmd']:
1227 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1247 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1228
1248
1229 other = hg.repository(ui, source)
1249 other = hg.repository(ui, source)
1230 r = repo.pull(other)
1250 r = repo.pull(other)
1231 if not r:
1251 if not r:
1232 if opts['update']:
1252 if opts['update']:
1233 return update(ui, repo)
1253 return update(ui, repo)
1234 else:
1254 else:
1235 ui.status("(run 'hg update' to get a working copy)\n")
1255 ui.status("(run 'hg update' to get a working copy)\n")
1236
1256
1237 return r
1257 return r
1238
1258
1239 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1259 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1240 """push changes to the specified destination"""
1260 """push changes to the specified destination"""
1241 dest = ui.expandpath(dest)
1261 dest = ui.expandpath(dest)
1242 ui.status('pushing to %s\n' % (dest))
1262 ui.status('pushing to %s\n' % (dest))
1243
1263
1244 if ssh:
1264 if ssh:
1245 ui.setconfig("ui", "ssh", ssh)
1265 ui.setconfig("ui", "ssh", ssh)
1246 if remotecmd:
1266 if remotecmd:
1247 ui.setconfig("ui", "remotecmd", remotecmd)
1267 ui.setconfig("ui", "remotecmd", remotecmd)
1248
1268
1249 other = hg.repository(ui, dest)
1269 other = hg.repository(ui, dest)
1250 r = repo.push(other, force)
1270 r = repo.push(other, force)
1251 return r
1271 return r
1252
1272
1253 def rawcommit(ui, repo, *flist, **rc):
1273 def rawcommit(ui, repo, *flist, **rc):
1254 "raw commit interface"
1274 "raw commit interface"
1255 if rc['text']:
1275 if rc['text']:
1256 ui.warn("Warning: -t and --text is deprecated,"
1276 ui.warn("Warning: -t and --text is deprecated,"
1257 " please use -m or --message instead.\n")
1277 " please use -m or --message instead.\n")
1258 message = rc['message'] or rc['text']
1278 message = rc['message'] or rc['text']
1259 if not message and rc['logfile']:
1279 if not message and rc['logfile']:
1260 try:
1280 try:
1261 message = open(rc['logfile']).read()
1281 message = open(rc['logfile']).read()
1262 except IOError:
1282 except IOError:
1263 pass
1283 pass
1264 if not message and not rc['logfile']:
1284 if not message and not rc['logfile']:
1265 ui.warn("abort: missing commit message\n")
1285 ui.warn("abort: missing commit message\n")
1266 return 1
1286 return 1
1267
1287
1268 files = relpath(repo, list(flist))
1288 files = relpath(repo, list(flist))
1269 if rc['files']:
1289 if rc['files']:
1270 files += open(rc['files']).read().splitlines()
1290 files += open(rc['files']).read().splitlines()
1271
1291
1272 rc['parent'] = map(repo.lookup, rc['parent'])
1292 rc['parent'] = map(repo.lookup, rc['parent'])
1273
1293
1274 try:
1294 try:
1275 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1295 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1276 except ValueError, inst:
1296 except ValueError, inst:
1277 raise util.Abort(str(inst))
1297 raise util.Abort(str(inst))
1278
1298
1279 def recover(ui, repo):
1299 def recover(ui, repo):
1280 """roll back an interrupted transaction"""
1300 """roll back an interrupted transaction"""
1281 repo.recover()
1301 repo.recover()
1282
1302
1283 def remove(ui, repo, pat, *pats, **opts):
1303 def remove(ui, repo, pat, *pats, **opts):
1284 """remove the specified files on the next commit"""
1304 """remove the specified files on the next commit"""
1285 names = []
1305 names = []
1286 def okaytoremove(abs, rel, exact):
1306 def okaytoremove(abs, rel, exact):
1287 c, a, d, u = repo.changes(files = [abs])
1307 c, a, d, u = repo.changes(files = [abs])
1288 reason = None
1308 reason = None
1289 if c: reason = 'is modified'
1309 if c: reason = 'is modified'
1290 elif a: reason = 'has been marked for add'
1310 elif a: reason = 'has been marked for add'
1291 elif u: reason = 'not managed'
1311 elif u: reason = 'not managed'
1292 if reason and exact:
1312 if reason and exact:
1293 ui.warn('not removing %s: file %s\n' % (rel, reason))
1313 ui.warn('not removing %s: file %s\n' % (rel, reason))
1294 else:
1314 else:
1295 return True
1315 return True
1296 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1316 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1297 if okaytoremove(abs, rel, exact):
1317 if okaytoremove(abs, rel, exact):
1298 if not exact: ui.status('removing %s\n' % rel)
1318 if not exact: ui.status('removing %s\n' % rel)
1299 names.append(abs)
1319 names.append(abs)
1300 repo.remove(names)
1320 repo.remove(names)
1301
1321
1302 def revert(ui, repo, *names, **opts):
1322 def revert(ui, repo, *names, **opts):
1303 """revert modified files or dirs back to their unmodified states"""
1323 """revert modified files or dirs back to their unmodified states"""
1304 node = opts['rev'] and repo.lookup(opts['rev']) or \
1324 node = opts['rev'] and repo.lookup(opts['rev']) or \
1305 repo.dirstate.parents()[0]
1325 repo.dirstate.parents()[0]
1306 root = os.path.realpath(repo.root)
1326 root = os.path.realpath(repo.root)
1307
1327
1308 def trimpath(p):
1328 def trimpath(p):
1309 p = os.path.realpath(p)
1329 p = os.path.realpath(p)
1310 if p.startswith(root):
1330 if p.startswith(root):
1311 rest = p[len(root):]
1331 rest = p[len(root):]
1312 if not rest:
1332 if not rest:
1313 return rest
1333 return rest
1314 if p.startswith(os.sep):
1334 if p.startswith(os.sep):
1315 return rest[1:]
1335 return rest[1:]
1316 return p
1336 return p
1317
1337
1318 relnames = map(trimpath, names or [os.getcwd()])
1338 relnames = map(trimpath, names or [os.getcwd()])
1319 chosen = {}
1339 chosen = {}
1320
1340
1321 def choose(name):
1341 def choose(name):
1322 def body(name):
1342 def body(name):
1323 for r in relnames:
1343 for r in relnames:
1324 if not name.startswith(r):
1344 if not name.startswith(r):
1325 continue
1345 continue
1326 rest = name[len(r):]
1346 rest = name[len(r):]
1327 if not rest:
1347 if not rest:
1328 return r, True
1348 return r, True
1329 depth = rest.count(os.sep)
1349 depth = rest.count(os.sep)
1330 if not r:
1350 if not r:
1331 if depth == 0 or not opts['nonrecursive']:
1351 if depth == 0 or not opts['nonrecursive']:
1332 return r, True
1352 return r, True
1333 elif rest[0] == os.sep:
1353 elif rest[0] == os.sep:
1334 if depth == 1 or not opts['nonrecursive']:
1354 if depth == 1 or not opts['nonrecursive']:
1335 return r, True
1355 return r, True
1336 return None, False
1356 return None, False
1337 relname, ret = body(name)
1357 relname, ret = body(name)
1338 if ret:
1358 if ret:
1339 chosen[relname] = 1
1359 chosen[relname] = 1
1340 return ret
1360 return ret
1341
1361
1342 r = repo.update(node, False, True, choose, False)
1362 r = repo.update(node, False, True, choose, False)
1343 for n in relnames:
1363 for n in relnames:
1344 if n not in chosen:
1364 if n not in chosen:
1345 ui.warn('error: no matches for %s\n' % n)
1365 ui.warn('error: no matches for %s\n' % n)
1346 r = 1
1366 r = 1
1347 sys.stdout.flush()
1367 sys.stdout.flush()
1348 return r
1368 return r
1349
1369
1350 def root(ui, repo):
1370 def root(ui, repo):
1351 """print the root (top) of the current working dir"""
1371 """print the root (top) of the current working dir"""
1352 ui.write(repo.root + "\n")
1372 ui.write(repo.root + "\n")
1353
1373
1354 def serve(ui, repo, **opts):
1374 def serve(ui, repo, **opts):
1355 """export the repository via HTTP"""
1375 """export the repository via HTTP"""
1356
1376
1357 if opts["stdio"]:
1377 if opts["stdio"]:
1358 fin, fout = sys.stdin, sys.stdout
1378 fin, fout = sys.stdin, sys.stdout
1359 sys.stdout = sys.stderr
1379 sys.stdout = sys.stderr
1360
1380
1361 def getarg():
1381 def getarg():
1362 argline = fin.readline()[:-1]
1382 argline = fin.readline()[:-1]
1363 arg, l = argline.split()
1383 arg, l = argline.split()
1364 val = fin.read(int(l))
1384 val = fin.read(int(l))
1365 return arg, val
1385 return arg, val
1366 def respond(v):
1386 def respond(v):
1367 fout.write("%d\n" % len(v))
1387 fout.write("%d\n" % len(v))
1368 fout.write(v)
1388 fout.write(v)
1369 fout.flush()
1389 fout.flush()
1370
1390
1371 lock = None
1391 lock = None
1372
1392
1373 while 1:
1393 while 1:
1374 cmd = fin.readline()[:-1]
1394 cmd = fin.readline()[:-1]
1375 if cmd == '':
1395 if cmd == '':
1376 return
1396 return
1377 if cmd == "heads":
1397 if cmd == "heads":
1378 h = repo.heads()
1398 h = repo.heads()
1379 respond(" ".join(map(hex, h)) + "\n")
1399 respond(" ".join(map(hex, h)) + "\n")
1380 if cmd == "lock":
1400 if cmd == "lock":
1381 lock = repo.lock()
1401 lock = repo.lock()
1382 respond("")
1402 respond("")
1383 if cmd == "unlock":
1403 if cmd == "unlock":
1384 if lock:
1404 if lock:
1385 lock.release()
1405 lock.release()
1386 lock = None
1406 lock = None
1387 respond("")
1407 respond("")
1388 elif cmd == "branches":
1408 elif cmd == "branches":
1389 arg, nodes = getarg()
1409 arg, nodes = getarg()
1390 nodes = map(bin, nodes.split(" "))
1410 nodes = map(bin, nodes.split(" "))
1391 r = []
1411 r = []
1392 for b in repo.branches(nodes):
1412 for b in repo.branches(nodes):
1393 r.append(" ".join(map(hex, b)) + "\n")
1413 r.append(" ".join(map(hex, b)) + "\n")
1394 respond("".join(r))
1414 respond("".join(r))
1395 elif cmd == "between":
1415 elif cmd == "between":
1396 arg, pairs = getarg()
1416 arg, pairs = getarg()
1397 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1417 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1398 r = []
1418 r = []
1399 for b in repo.between(pairs):
1419 for b in repo.between(pairs):
1400 r.append(" ".join(map(hex, b)) + "\n")
1420 r.append(" ".join(map(hex, b)) + "\n")
1401 respond("".join(r))
1421 respond("".join(r))
1402 elif cmd == "changegroup":
1422 elif cmd == "changegroup":
1403 nodes = []
1423 nodes = []
1404 arg, roots = getarg()
1424 arg, roots = getarg()
1405 nodes = map(bin, roots.split(" "))
1425 nodes = map(bin, roots.split(" "))
1406
1426
1407 cg = repo.changegroup(nodes)
1427 cg = repo.changegroup(nodes)
1408 while 1:
1428 while 1:
1409 d = cg.read(4096)
1429 d = cg.read(4096)
1410 if not d:
1430 if not d:
1411 break
1431 break
1412 fout.write(d)
1432 fout.write(d)
1413
1433
1414 fout.flush()
1434 fout.flush()
1415
1435
1416 elif cmd == "addchangegroup":
1436 elif cmd == "addchangegroup":
1417 if not lock:
1437 if not lock:
1418 respond("not locked")
1438 respond("not locked")
1419 continue
1439 continue
1420 respond("")
1440 respond("")
1421
1441
1422 r = repo.addchangegroup(fin)
1442 r = repo.addchangegroup(fin)
1423 respond("")
1443 respond("")
1424
1444
1425 optlist = "name templates style address port ipv6 accesslog errorlog"
1445 optlist = "name templates style address port ipv6 accesslog errorlog"
1426 for o in optlist.split():
1446 for o in optlist.split():
1427 if opts[o]:
1447 if opts[o]:
1428 ui.setconfig("web", o, opts[o])
1448 ui.setconfig("web", o, opts[o])
1429
1449
1430 try:
1450 try:
1431 httpd = hgweb.create_server(repo)
1451 httpd = hgweb.create_server(repo)
1432 except socket.error, inst:
1452 except socket.error, inst:
1433 raise util.Abort('cannot start server: ' + inst.args[1])
1453 raise util.Abort('cannot start server: ' + inst.args[1])
1434
1454
1435 if ui.verbose:
1455 if ui.verbose:
1436 addr, port = httpd.socket.getsockname()
1456 addr, port = httpd.socket.getsockname()
1437 if addr == '0.0.0.0':
1457 if addr == '0.0.0.0':
1438 addr = socket.gethostname()
1458 addr = socket.gethostname()
1439 else:
1459 else:
1440 try:
1460 try:
1441 addr = socket.gethostbyaddr(addr)[0]
1461 addr = socket.gethostbyaddr(addr)[0]
1442 except socket.error:
1462 except socket.error:
1443 pass
1463 pass
1444 if port != 80:
1464 if port != 80:
1445 ui.status('listening at http://%s:%d/\n' % (addr, port))
1465 ui.status('listening at http://%s:%d/\n' % (addr, port))
1446 else:
1466 else:
1447 ui.status('listening at http://%s/\n' % addr)
1467 ui.status('listening at http://%s/\n' % addr)
1448 httpd.serve_forever()
1468 httpd.serve_forever()
1449
1469
1450 def status(ui, repo, *pats, **opts):
1470 def status(ui, repo, *pats, **opts):
1451 '''show changed files in the working directory
1471 '''show changed files in the working directory
1452
1472
1453 M = modified
1473 M = modified
1454 A = added
1474 A = added
1455 R = removed
1475 R = removed
1456 ? = not tracked
1476 ? = not tracked
1457 '''
1477 '''
1458
1478
1459 cwd = repo.getcwd()
1479 cwd = repo.getcwd()
1460 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1480 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1461 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1481 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1462 for n in repo.changes(files=files, match=matchfn)]
1482 for n in repo.changes(files=files, match=matchfn)]
1463
1483
1464 changetypes = [('modified', 'M', c),
1484 changetypes = [('modified', 'M', c),
1465 ('added', 'A', a),
1485 ('added', 'A', a),
1466 ('removed', 'R', d),
1486 ('removed', 'R', d),
1467 ('unknown', '?', u)]
1487 ('unknown', '?', u)]
1468
1488
1469 end = opts['print0'] and '\0' or '\n'
1489 end = opts['print0'] and '\0' or '\n'
1470
1490
1471 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1491 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1472 or changetypes):
1492 or changetypes):
1473 if opts['no_status']:
1493 if opts['no_status']:
1474 format = "%%s%s" % end
1494 format = "%%s%s" % end
1475 else:
1495 else:
1476 format = "%s %%s%s" % (char, end);
1496 format = "%s %%s%s" % (char, end);
1477
1497
1478 for f in changes:
1498 for f in changes:
1479 ui.write(format % f)
1499 ui.write(format % f)
1480
1500
1481 def tag(ui, repo, name, rev=None, **opts):
1501 def tag(ui, repo, name, rev=None, **opts):
1482 """add a tag for the current tip or a given revision"""
1502 """add a tag for the current tip or a given revision"""
1483 if opts['text']:
1503 if opts['text']:
1484 ui.warn("Warning: -t and --text is deprecated,"
1504 ui.warn("Warning: -t and --text is deprecated,"
1485 " please use -m or --message instead.\n")
1505 " please use -m or --message instead.\n")
1486 if name == "tip":
1506 if name == "tip":
1487 ui.warn("abort: 'tip' is a reserved name!\n")
1507 ui.warn("abort: 'tip' is a reserved name!\n")
1488 return -1
1508 return -1
1489 if rev:
1509 if rev:
1490 r = hex(repo.lookup(rev))
1510 r = hex(repo.lookup(rev))
1491 else:
1511 else:
1492 r = hex(repo.changelog.tip())
1512 r = hex(repo.changelog.tip())
1493
1513
1494 if name.find(revrangesep) >= 0:
1514 if name.find(revrangesep) >= 0:
1495 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1515 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1496 return -1
1516 return -1
1497
1517
1498 if opts['local']:
1518 if opts['local']:
1499 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1519 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1500 return
1520 return
1501
1521
1502 (c, a, d, u) = repo.changes()
1522 (c, a, d, u) = repo.changes()
1503 for x in (c, a, d, u):
1523 for x in (c, a, d, u):
1504 if ".hgtags" in x:
1524 if ".hgtags" in x:
1505 ui.warn("abort: working copy of .hgtags is changed!\n")
1525 ui.warn("abort: working copy of .hgtags is changed!\n")
1506 ui.status("(please commit .hgtags manually)\n")
1526 ui.status("(please commit .hgtags manually)\n")
1507 return -1
1527 return -1
1508
1528
1509 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1529 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1510 if repo.dirstate.state(".hgtags") == '?':
1530 if repo.dirstate.state(".hgtags") == '?':
1511 repo.add([".hgtags"])
1531 repo.add([".hgtags"])
1512
1532
1513 message = (opts['message'] or opts['text'] or
1533 message = (opts['message'] or opts['text'] or
1514 "Added tag %s for changeset %s" % (name, r))
1534 "Added tag %s for changeset %s" % (name, r))
1515 try:
1535 try:
1516 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1536 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1517 except ValueError, inst:
1537 except ValueError, inst:
1518 raise util.Abort(str(inst))
1538 raise util.Abort(str(inst))
1519
1539
1520 def tags(ui, repo):
1540 def tags(ui, repo):
1521 """list repository tags"""
1541 """list repository tags"""
1522
1542
1523 l = repo.tagslist()
1543 l = repo.tagslist()
1524 l.reverse()
1544 l.reverse()
1525 for t, n in l:
1545 for t, n in l:
1526 try:
1546 try:
1527 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
1547 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
1528 except KeyError:
1548 except KeyError:
1529 r = " ?:?"
1549 r = " ?:?"
1530 ui.write("%-30s %s\n" % (t, r))
1550 ui.write("%-30s %s\n" % (t, r))
1531
1551
1532 def tip(ui, repo):
1552 def tip(ui, repo):
1533 """show the tip revision"""
1553 """show the tip revision"""
1534 n = repo.changelog.tip()
1554 n = repo.changelog.tip()
1535 show_changeset(ui, repo, changenode=n)
1555 show_changeset(ui, repo, changenode=n)
1536
1556
1557 def unbundle(ui, repo, fname):
1558 f = urllib.urlopen(fname)
1559
1560 if f.read(4) != "HG10":
1561 ui.warn("abort: not a Mercurial bundle file!\n")
1562 return -1
1563
1564 class bzread:
1565 def __init__(self, f):
1566 self.zd = bz2.BZ2Decompressor()
1567 self.f = f
1568 self.buf = ""
1569 def read(self, l):
1570 while l > len(self.buf):
1571 r = self.f.read(4096)
1572 if r:
1573 self.buf += self.zd.decompress(r)
1574 else:
1575 break
1576 d, self.buf = self.buf[:l], self.buf[l:]
1577 return d
1578
1579 repo.addchangegroup(bzread(f))
1580
1537 def undo(ui, repo):
1581 def undo(ui, repo):
1538 """undo the last commit or pull
1582 """undo the last commit or pull
1539
1583
1540 Roll back the last pull or commit transaction on the
1584 Roll back the last pull or commit transaction on the
1541 repository, restoring the project to its earlier state.
1585 repository, restoring the project to its earlier state.
1542
1586
1543 This command should be used with care. There is only one level of
1587 This command should be used with care. There is only one level of
1544 undo and there is no redo.
1588 undo and there is no redo.
1545
1589
1546 This command is not intended for use on public repositories. Once
1590 This command is not intended for use on public repositories. Once
1547 a change is visible for pull by other users, undoing it locally is
1591 a change is visible for pull by other users, undoing it locally is
1548 ineffective.
1592 ineffective.
1549 """
1593 """
1550 repo.undo()
1594 repo.undo()
1551
1595
1552 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1596 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1553 '''update or merge working directory
1597 '''update or merge working directory
1554
1598
1555 If there are no outstanding changes in the working directory and
1599 If there are no outstanding changes in the working directory and
1556 there is a linear relationship between the current version and the
1600 there is a linear relationship between the current version and the
1557 requested version, the result is the requested version.
1601 requested version, the result is the requested version.
1558
1602
1559 Otherwise the result is a merge between the contents of the
1603 Otherwise the result is a merge between the contents of the
1560 current working directory and the requested version. Files that
1604 current working directory and the requested version. Files that
1561 changed between either parent are marked as changed for the next
1605 changed between either parent are marked as changed for the next
1562 commit and a commit must be performed before any further updates
1606 commit and a commit must be performed before any further updates
1563 are allowed.
1607 are allowed.
1564 '''
1608 '''
1565 if branch:
1609 if branch:
1566 br = repo.branchlookup(branch=branch)
1610 br = repo.branchlookup(branch=branch)
1567 found = []
1611 found = []
1568 for x in br:
1612 for x in br:
1569 if branch in br[x]:
1613 if branch in br[x]:
1570 found.append(x)
1614 found.append(x)
1571 if len(found) > 1:
1615 if len(found) > 1:
1572 ui.warn("Found multiple heads for %s\n" % branch)
1616 ui.warn("Found multiple heads for %s\n" % branch)
1573 for x in found:
1617 for x in found:
1574 show_changeset(ui, repo, changenode=x, brinfo=br)
1618 show_changeset(ui, repo, changenode=x, brinfo=br)
1575 return 1
1619 return 1
1576 if len(found) == 1:
1620 if len(found) == 1:
1577 node = found[0]
1621 node = found[0]
1578 ui.warn("Using head %s for branch %s\n" % (short(node), branch))
1622 ui.warn("Using head %s for branch %s\n" % (short(node), branch))
1579 else:
1623 else:
1580 ui.warn("branch %s not found\n" % (branch))
1624 ui.warn("branch %s not found\n" % (branch))
1581 return 1
1625 return 1
1582 else:
1626 else:
1583 node = node and repo.lookup(node) or repo.changelog.tip()
1627 node = node and repo.lookup(node) or repo.changelog.tip()
1584 return repo.update(node, allow=merge, force=clean)
1628 return repo.update(node, allow=merge, force=clean)
1585
1629
1586 def verify(ui, repo):
1630 def verify(ui, repo):
1587 """verify the integrity of the repository"""
1631 """verify the integrity of the repository"""
1588 return repo.verify()
1632 return repo.verify()
1589
1633
1590 # Command options and aliases are listed here, alphabetically
1634 # Command options and aliases are listed here, alphabetically
1591
1635
1592 table = {
1636 table = {
1593 "^add":
1637 "^add":
1594 (add,
1638 (add,
1595 [('I', 'include', [], 'include path in search'),
1639 [('I', 'include', [], 'include path in search'),
1596 ('X', 'exclude', [], 'exclude path from search')],
1640 ('X', 'exclude', [], 'exclude path from search')],
1597 "hg add [OPTION]... [FILE]..."),
1641 "hg add [OPTION]... [FILE]..."),
1598 "addremove":
1642 "addremove":
1599 (addremove,
1643 (addremove,
1600 [('I', 'include', [], 'include path in search'),
1644 [('I', 'include', [], 'include path in search'),
1601 ('X', 'exclude', [], 'exclude path from search')],
1645 ('X', 'exclude', [], 'exclude path from search')],
1602 "hg addremove [OPTION]... [FILE]..."),
1646 "hg addremove [OPTION]... [FILE]..."),
1603 "^annotate":
1647 "^annotate":
1604 (annotate,
1648 (annotate,
1605 [('r', 'rev', '', 'revision'),
1649 [('r', 'rev', '', 'revision'),
1606 ('a', 'text', None, 'treat all files as text'),
1650 ('a', 'text', None, 'treat all files as text'),
1607 ('u', 'user', None, 'show user'),
1651 ('u', 'user', None, 'show user'),
1608 ('n', 'number', None, 'show revision number'),
1652 ('n', 'number', None, 'show revision number'),
1609 ('c', 'changeset', None, 'show changeset'),
1653 ('c', 'changeset', None, 'show changeset'),
1610 ('I', 'include', [], 'include path in search'),
1654 ('I', 'include', [], 'include path in search'),
1611 ('X', 'exclude', [], 'exclude path from search')],
1655 ('X', 'exclude', [], 'exclude path from search')],
1612 'hg annotate [OPTION]... FILE...'),
1656 'hg annotate [OPTION]... FILE...'),
1657 "bundle":
1658 (bundle,
1659 [],
1660 'hg bundle FILE DEST'),
1613 "cat":
1661 "cat":
1614 (cat,
1662 (cat,
1615 [('o', 'output', "", 'output to file')],
1663 [('o', 'output', "", 'output to file')],
1616 'hg cat [-o OUTFILE] FILE [REV]'),
1664 'hg cat [-o OUTFILE] FILE [REV]'),
1617 "^clone":
1665 "^clone":
1618 (clone,
1666 (clone,
1619 [('U', 'noupdate', None, 'skip update after cloning'),
1667 [('U', 'noupdate', None, 'skip update after cloning'),
1620 ('e', 'ssh', "", 'ssh command'),
1668 ('e', 'ssh', "", 'ssh command'),
1621 ('', 'remotecmd', "", 'remote hg command')],
1669 ('', 'remotecmd', "", 'remote hg command')],
1622 'hg clone [OPTION]... SOURCE [DEST]'),
1670 'hg clone [OPTION]... SOURCE [DEST]'),
1623 "^commit|ci":
1671 "^commit|ci":
1624 (commit,
1672 (commit,
1625 [('A', 'addremove', None, 'run add/remove during commit'),
1673 [('A', 'addremove', None, 'run add/remove during commit'),
1626 ('I', 'include', [], 'include path in search'),
1674 ('I', 'include', [], 'include path in search'),
1627 ('X', 'exclude', [], 'exclude path from search'),
1675 ('X', 'exclude', [], 'exclude path from search'),
1628 ('m', 'message', "", 'commit message'),
1676 ('m', 'message', "", 'commit message'),
1629 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1677 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1630 ('l', 'logfile', "", 'commit message file'),
1678 ('l', 'logfile', "", 'commit message file'),
1631 ('d', 'date', "", 'date code'),
1679 ('d', 'date', "", 'date code'),
1632 ('u', 'user', "", 'user')],
1680 ('u', 'user', "", 'user')],
1633 'hg commit [OPTION]... [FILE]...'),
1681 'hg commit [OPTION]... [FILE]...'),
1634 "copy": (copy, [], 'hg copy SOURCE DEST'),
1682 "copy": (copy, [], 'hg copy SOURCE DEST'),
1635 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1683 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1636 "debugconfig": (debugconfig, [], 'debugconfig'),
1684 "debugconfig": (debugconfig, [], 'debugconfig'),
1637 "debugstate": (debugstate, [], 'debugstate'),
1685 "debugstate": (debugstate, [], 'debugstate'),
1638 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1686 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1639 "debugindex": (debugindex, [], 'debugindex FILE'),
1687 "debugindex": (debugindex, [], 'debugindex FILE'),
1640 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1688 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1641 "debugrename": (debugrename, [], 'debugrename FILE [REV]'),
1689 "debugrename": (debugrename, [], 'debugrename FILE [REV]'),
1642 "debugwalk":
1690 "debugwalk":
1643 (debugwalk,
1691 (debugwalk,
1644 [('I', 'include', [], 'include path in search'),
1692 [('I', 'include', [], 'include path in search'),
1645 ('X', 'exclude', [], 'exclude path from search')],
1693 ('X', 'exclude', [], 'exclude path from search')],
1646 'debugwalk [OPTION]... [FILE]...'),
1694 'debugwalk [OPTION]... [FILE]...'),
1647 "^diff":
1695 "^diff":
1648 (diff,
1696 (diff,
1649 [('r', 'rev', [], 'revision'),
1697 [('r', 'rev', [], 'revision'),
1650 ('a', 'text', None, 'treat all files as text'),
1698 ('a', 'text', None, 'treat all files as text'),
1651 ('I', 'include', [], 'include path in search'),
1699 ('I', 'include', [], 'include path in search'),
1652 ('X', 'exclude', [], 'exclude path from search')],
1700 ('X', 'exclude', [], 'exclude path from search')],
1653 'hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1701 'hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1654 "^export":
1702 "^export":
1655 (export,
1703 (export,
1656 [('o', 'output', "", 'output to file'),
1704 [('o', 'output', "", 'output to file'),
1657 ('a', 'text', None, 'treat all files as text')],
1705 ('a', 'text', None, 'treat all files as text')],
1658 "hg export [-a] [-o OUTFILE] REV..."),
1706 "hg export [-a] [-o OUTFILE] REV..."),
1659 "forget":
1707 "forget":
1660 (forget,
1708 (forget,
1661 [('I', 'include', [], 'include path in search'),
1709 [('I', 'include', [], 'include path in search'),
1662 ('X', 'exclude', [], 'exclude path from search')],
1710 ('X', 'exclude', [], 'exclude path from search')],
1663 "hg forget [OPTION]... FILE..."),
1711 "hg forget [OPTION]... FILE..."),
1664 "grep":
1712 "grep":
1665 (grep,
1713 (grep,
1666 [('0', 'print0', None, 'end fields with NUL'),
1714 [('0', 'print0', None, 'end fields with NUL'),
1667 ('I', 'include', [], 'include path in search'),
1715 ('I', 'include', [], 'include path in search'),
1668 ('X', 'exclude', [], 'include path in search'),
1716 ('X', 'exclude', [], 'include path in search'),
1669 ('', 'all', None, 'print all revisions with matches'),
1717 ('', 'all', None, 'print all revisions with matches'),
1670 ('i', 'ignore-case', None, 'ignore case when matching'),
1718 ('i', 'ignore-case', None, 'ignore case when matching'),
1671 ('l', 'files-with-matches', None, 'print names of files and revs with matches'),
1719 ('l', 'files-with-matches', None, 'print names of files and revs with matches'),
1672 ('n', 'line-number', None, 'print line numbers'),
1720 ('n', 'line-number', None, 'print line numbers'),
1673 ('r', 'rev', [], 'search in revision rev'),
1721 ('r', 'rev', [], 'search in revision rev'),
1674 ('u', 'user', None, 'print user who made change')],
1722 ('u', 'user', None, 'print user who made change')],
1675 "hg grep [OPTION]... PATTERN [FILE]..."),
1723 "hg grep [OPTION]... PATTERN [FILE]..."),
1676 "heads":
1724 "heads":
1677 (heads,
1725 (heads,
1678 [('b', 'branches', None, 'find branch info')],
1726 [('b', 'branches', None, 'find branch info')],
1679 'hg heads [-b]'),
1727 'hg heads [-b]'),
1680 "help": (help_, [], 'hg help [COMMAND]'),
1728 "help": (help_, [], 'hg help [COMMAND]'),
1681 "identify|id": (identify, [], 'hg identify'),
1729 "identify|id": (identify, [], 'hg identify'),
1682 "import|patch":
1730 "import|patch":
1683 (import_,
1731 (import_,
1684 [('p', 'strip', 1, 'path strip'),
1732 [('p', 'strip', 1, 'path strip'),
1685 ('f', 'force', None, 'skip check for outstanding changes'),
1733 ('f', 'force', None, 'skip check for outstanding changes'),
1686 ('b', 'base', "", 'base path')],
1734 ('b', 'base', "", 'base path')],
1687 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
1735 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
1688 "incoming|in": (incoming,
1736 "incoming|in": (incoming,
1689 [('p', 'patch', None, 'show patch')],
1737 [('p', 'patch', None, 'show patch')],
1690 'hg incoming [-p] [SOURCE]'),
1738 'hg incoming [-p] [SOURCE]'),
1691 "^init": (init, [], 'hg init [DEST]'),
1739 "^init": (init, [], 'hg init [DEST]'),
1692 "locate":
1740 "locate":
1693 (locate,
1741 (locate,
1694 [('r', 'rev', '', 'revision'),
1742 [('r', 'rev', '', 'revision'),
1695 ('0', 'print0', None, 'end filenames with NUL'),
1743 ('0', 'print0', None, 'end filenames with NUL'),
1696 ('f', 'fullpath', None, 'print complete paths'),
1744 ('f', 'fullpath', None, 'print complete paths'),
1697 ('I', 'include', [], 'include path in search'),
1745 ('I', 'include', [], 'include path in search'),
1698 ('X', 'exclude', [], 'exclude path from search')],
1746 ('X', 'exclude', [], 'exclude path from search')],
1699 'hg locate [OPTION]... [PATTERN]...'),
1747 'hg locate [OPTION]... [PATTERN]...'),
1700 "^log|history":
1748 "^log|history":
1701 (log,
1749 (log,
1702 [('I', 'include', [], 'include path in search'),
1750 [('I', 'include', [], 'include path in search'),
1703 ('X', 'exclude', [], 'exclude path from search'),
1751 ('X', 'exclude', [], 'exclude path from search'),
1704 ('r', 'rev', [], 'revision'),
1752 ('r', 'rev', [], 'revision'),
1705 ('p', 'patch', None, 'show patch')],
1753 ('p', 'patch', None, 'show patch')],
1706 'hg log [-I] [-X] [-r REV]... [-p] [FILE]'),
1754 'hg log [-I] [-X] [-r REV]... [-p] [FILE]'),
1707 "manifest": (manifest, [], 'hg manifest [REV]'),
1755 "manifest": (manifest, [], 'hg manifest [REV]'),
1708 "outgoing|out": (outgoing,
1756 "outgoing|out": (outgoing,
1709 [('p', 'patch', None, 'show patch')],
1757 [('p', 'patch', None, 'show patch')],
1710 'hg outgoing [-p] [DEST]'),
1758 'hg outgoing [-p] [DEST]'),
1711 "parents": (parents, [], 'hg parents [REV]'),
1759 "parents": (parents, [], 'hg parents [REV]'),
1712 "paths": (paths, [], 'hg paths [NAME]'),
1760 "paths": (paths, [], 'hg paths [NAME]'),
1713 "^pull":
1761 "^pull":
1714 (pull,
1762 (pull,
1715 [('u', 'update', None, 'update working directory'),
1763 [('u', 'update', None, 'update working directory'),
1716 ('e', 'ssh', "", 'ssh command'),
1764 ('e', 'ssh', "", 'ssh command'),
1717 ('', 'remotecmd', "", 'remote hg command')],
1765 ('', 'remotecmd', "", 'remote hg command')],
1718 'hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]'),
1766 'hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]'),
1719 "^push":
1767 "^push":
1720 (push,
1768 (push,
1721 [('f', 'force', None, 'force push'),
1769 [('f', 'force', None, 'force push'),
1722 ('e', 'ssh', "", 'ssh command'),
1770 ('e', 'ssh', "", 'ssh command'),
1723 ('', 'remotecmd', "", 'remote hg command')],
1771 ('', 'remotecmd', "", 'remote hg command')],
1724 'hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]'),
1772 'hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]'),
1725 "rawcommit":
1773 "rawcommit":
1726 (rawcommit,
1774 (rawcommit,
1727 [('p', 'parent', [], 'parent'),
1775 [('p', 'parent', [], 'parent'),
1728 ('d', 'date', "", 'date code'),
1776 ('d', 'date', "", 'date code'),
1729 ('u', 'user', "", 'user'),
1777 ('u', 'user', "", 'user'),
1730 ('F', 'files', "", 'file list'),
1778 ('F', 'files', "", 'file list'),
1731 ('m', 'message', "", 'commit message'),
1779 ('m', 'message', "", 'commit message'),
1732 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1780 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1733 ('l', 'logfile', "", 'commit message file')],
1781 ('l', 'logfile', "", 'commit message file')],
1734 'hg rawcommit [OPTION]... [FILE]...'),
1782 'hg rawcommit [OPTION]... [FILE]...'),
1735 "recover": (recover, [], "hg recover"),
1783 "recover": (recover, [], "hg recover"),
1736 "^remove|rm": (remove,
1784 "^remove|rm": (remove,
1737 [('I', 'include', [], 'include path in search'),
1785 [('I', 'include', [], 'include path in search'),
1738 ('X', 'exclude', [], 'exclude path from search')],
1786 ('X', 'exclude', [], 'exclude path from search')],
1739 "hg remove [OPTION]... FILE..."),
1787 "hg remove [OPTION]... FILE..."),
1740 "^revert":
1788 "^revert":
1741 (revert,
1789 (revert,
1742 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1790 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1743 ("r", "rev", "", "revision")],
1791 ("r", "rev", "", "revision")],
1744 "hg revert [-n] [-r REV] [NAME]..."),
1792 "hg revert [-n] [-r REV] [NAME]..."),
1745 "root": (root, [], "hg root"),
1793 "root": (root, [], "hg root"),
1746 "^serve":
1794 "^serve":
1747 (serve,
1795 (serve,
1748 [('A', 'accesslog', '', 'access log file'),
1796 [('A', 'accesslog', '', 'access log file'),
1749 ('E', 'errorlog', '', 'error log file'),
1797 ('E', 'errorlog', '', 'error log file'),
1750 ('p', 'port', 0, 'listen port'),
1798 ('p', 'port', 0, 'listen port'),
1751 ('a', 'address', '', 'interface address'),
1799 ('a', 'address', '', 'interface address'),
1752 ('n', 'name', "", 'repository name'),
1800 ('n', 'name', "", 'repository name'),
1753 ('', 'stdio', None, 'for remote clients'),
1801 ('', 'stdio', None, 'for remote clients'),
1754 ('t', 'templates', "", 'template directory'),
1802 ('t', 'templates', "", 'template directory'),
1755 ('', 'style', "", 'template style'),
1803 ('', 'style', "", 'template style'),
1756 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1804 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1757 "hg serve [OPTION]..."),
1805 "hg serve [OPTION]..."),
1758 "^status":
1806 "^status":
1759 (status,
1807 (status,
1760 [('m', 'modified', None, 'show only modified files'),
1808 [('m', 'modified', None, 'show only modified files'),
1761 ('a', 'added', None, 'show only added files'),
1809 ('a', 'added', None, 'show only added files'),
1762 ('r', 'removed', None, 'show only removed files'),
1810 ('r', 'removed', None, 'show only removed files'),
1763 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1811 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1764 ('n', 'no-status', None, 'hide status prefix'),
1812 ('n', 'no-status', None, 'hide status prefix'),
1765 ('0', 'print0', None, 'end filenames with NUL'),
1813 ('0', 'print0', None, 'end filenames with NUL'),
1766 ('I', 'include', [], 'include path in search'),
1814 ('I', 'include', [], 'include path in search'),
1767 ('X', 'exclude', [], 'exclude path from search')],
1815 ('X', 'exclude', [], 'exclude path from search')],
1768 "hg status [OPTION]... [FILE]..."),
1816 "hg status [OPTION]... [FILE]..."),
1769 "tag":
1817 "tag":
1770 (tag,
1818 (tag,
1771 [('l', 'local', None, 'make the tag local'),
1819 [('l', 'local', None, 'make the tag local'),
1772 ('m', 'message', "", 'commit message'),
1820 ('m', 'message', "", 'commit message'),
1773 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1821 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1774 ('d', 'date', "", 'date code'),
1822 ('d', 'date', "", 'date code'),
1775 ('u', 'user', "", 'user')],
1823 ('u', 'user', "", 'user')],
1776 'hg tag [OPTION]... NAME [REV]'),
1824 'hg tag [OPTION]... NAME [REV]'),
1777 "tags": (tags, [], 'hg tags'),
1825 "tags": (tags, [], 'hg tags'),
1778 "tip": (tip, [], 'hg tip'),
1826 "tip": (tip, [], 'hg tip'),
1827 "unbundle":
1828 (unbundle,
1829 [],
1830 'hg unbundle FILE'),
1779 "undo": (undo, [], 'hg undo'),
1831 "undo": (undo, [], 'hg undo'),
1780 "^update|up|checkout|co":
1832 "^update|up|checkout|co":
1781 (update,
1833 (update,
1782 [('b', 'branch', "", 'checkout the head of a specific branch'),
1834 [('b', 'branch', "", 'checkout the head of a specific branch'),
1783 ('m', 'merge', None, 'allow merging of conflicts'),
1835 ('m', 'merge', None, 'allow merging of conflicts'),
1784 ('C', 'clean', None, 'overwrite locally modified files')],
1836 ('C', 'clean', None, 'overwrite locally modified files')],
1785 'hg update [-b TAG] [-m] [-C] [REV]'),
1837 'hg update [-b TAG] [-m] [-C] [REV]'),
1786 "verify": (verify, [], 'hg verify'),
1838 "verify": (verify, [], 'hg verify'),
1787 "version": (show_version, [], 'hg version'),
1839 "version": (show_version, [], 'hg version'),
1788 }
1840 }
1789
1841
1790 globalopts = [
1842 globalopts = [
1791 ('R', 'repository', "", 'repository root directory'),
1843 ('R', 'repository', "", 'repository root directory'),
1792 ('', 'cwd', '', 'change working directory'),
1844 ('', 'cwd', '', 'change working directory'),
1793 ('y', 'noninteractive', None, 'run non-interactively'),
1845 ('y', 'noninteractive', None, 'run non-interactively'),
1794 ('q', 'quiet', None, 'quiet mode'),
1846 ('q', 'quiet', None, 'quiet mode'),
1795 ('v', 'verbose', None, 'verbose mode'),
1847 ('v', 'verbose', None, 'verbose mode'),
1796 ('', 'debug', None, 'debug mode'),
1848 ('', 'debug', None, 'debug mode'),
1797 ('', 'traceback', None, 'print traceback on exception'),
1849 ('', 'traceback', None, 'print traceback on exception'),
1798 ('', 'time', None, 'time how long the command takes'),
1850 ('', 'time', None, 'time how long the command takes'),
1799 ('', 'profile', None, 'profile'),
1851 ('', 'profile', None, 'profile'),
1800 ('', 'version', None, 'output version information and exit'),
1852 ('', 'version', None, 'output version information and exit'),
1801 ('h', 'help', None, 'display help and exit'),
1853 ('h', 'help', None, 'display help and exit'),
1802 ]
1854 ]
1803
1855
1804 norepo = ("clone init version help debugconfig debugdata"
1856 norepo = ("clone init version help debugconfig debugdata"
1805 " debugindex debugindexdot paths")
1857 " debugindex debugindexdot paths")
1806
1858
1807 def find(cmd):
1859 def find(cmd):
1808 for e in table.keys():
1860 for e in table.keys():
1809 if re.match("(%s)$" % e, cmd):
1861 if re.match("(%s)$" % e, cmd):
1810 return e, table[e]
1862 return e, table[e]
1811
1863
1812 raise UnknownCommand(cmd)
1864 raise UnknownCommand(cmd)
1813
1865
1814 class SignalInterrupt(Exception):
1866 class SignalInterrupt(Exception):
1815 """Exception raised on SIGTERM and SIGHUP."""
1867 """Exception raised on SIGTERM and SIGHUP."""
1816
1868
1817 def catchterm(*args):
1869 def catchterm(*args):
1818 raise SignalInterrupt
1870 raise SignalInterrupt
1819
1871
1820 def run():
1872 def run():
1821 sys.exit(dispatch(sys.argv[1:]))
1873 sys.exit(dispatch(sys.argv[1:]))
1822
1874
1823 class ParseError(Exception):
1875 class ParseError(Exception):
1824 """Exception raised on errors in parsing the command line."""
1876 """Exception raised on errors in parsing the command line."""
1825
1877
1826 def parse(args):
1878 def parse(args):
1827 options = {}
1879 options = {}
1828 cmdoptions = {}
1880 cmdoptions = {}
1829
1881
1830 try:
1882 try:
1831 args = fancyopts.fancyopts(args, globalopts, options)
1883 args = fancyopts.fancyopts(args, globalopts, options)
1832 except fancyopts.getopt.GetoptError, inst:
1884 except fancyopts.getopt.GetoptError, inst:
1833 raise ParseError(None, inst)
1885 raise ParseError(None, inst)
1834
1886
1835 if args:
1887 if args:
1836 cmd, args = args[0], args[1:]
1888 cmd, args = args[0], args[1:]
1837 i = find(cmd)[1]
1889 i = find(cmd)[1]
1838 c = list(i[1])
1890 c = list(i[1])
1839 else:
1891 else:
1840 cmd = None
1892 cmd = None
1841 c = []
1893 c = []
1842
1894
1843 # combine global options into local
1895 # combine global options into local
1844 for o in globalopts:
1896 for o in globalopts:
1845 c.append((o[0], o[1], options[o[1]], o[3]))
1897 c.append((o[0], o[1], options[o[1]], o[3]))
1846
1898
1847 try:
1899 try:
1848 args = fancyopts.fancyopts(args, c, cmdoptions)
1900 args = fancyopts.fancyopts(args, c, cmdoptions)
1849 except fancyopts.getopt.GetoptError, inst:
1901 except fancyopts.getopt.GetoptError, inst:
1850 raise ParseError(cmd, inst)
1902 raise ParseError(cmd, inst)
1851
1903
1852 # separate global options back out
1904 # separate global options back out
1853 for o in globalopts:
1905 for o in globalopts:
1854 n = o[1]
1906 n = o[1]
1855 options[n] = cmdoptions[n]
1907 options[n] = cmdoptions[n]
1856 del cmdoptions[n]
1908 del cmdoptions[n]
1857
1909
1858 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
1910 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
1859
1911
1860 def dispatch(args):
1912 def dispatch(args):
1861 signal.signal(signal.SIGTERM, catchterm)
1913 signal.signal(signal.SIGTERM, catchterm)
1862 try:
1914 try:
1863 signal.signal(signal.SIGHUP, catchterm)
1915 signal.signal(signal.SIGHUP, catchterm)
1864 except AttributeError:
1916 except AttributeError:
1865 pass
1917 pass
1866
1918
1867 u = ui.ui()
1919 u = ui.ui()
1868 external = []
1920 external = []
1869 for x in u.extensions():
1921 for x in u.extensions():
1870 if x[1]:
1922 if x[1]:
1871 mod = imp.load_source(x[0], x[1])
1923 mod = imp.load_source(x[0], x[1])
1872 else:
1924 else:
1873 def importh(name):
1925 def importh(name):
1874 mod = __import__(name)
1926 mod = __import__(name)
1875 components = name.split('.')
1927 components = name.split('.')
1876 for comp in components[1:]:
1928 for comp in components[1:]:
1877 mod = getattr(mod, comp)
1929 mod = getattr(mod, comp)
1878 return mod
1930 return mod
1879 mod = importh(x[0])
1931 mod = importh(x[0])
1880 external.append(mod)
1932 external.append(mod)
1881 for x in external:
1933 for x in external:
1882 for t in x.cmdtable:
1934 for t in x.cmdtable:
1883 if t in table:
1935 if t in table:
1884 u.warn("module %s override %s\n" % (x.__name__, t))
1936 u.warn("module %s override %s\n" % (x.__name__, t))
1885 table.update(x.cmdtable)
1937 table.update(x.cmdtable)
1886
1938
1887 try:
1939 try:
1888 cmd, func, args, options, cmdoptions = parse(args)
1940 cmd, func, args, options, cmdoptions = parse(args)
1889 except ParseError, inst:
1941 except ParseError, inst:
1890 if inst.args[0]:
1942 if inst.args[0]:
1891 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1943 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1892 help_(u, inst.args[0])
1944 help_(u, inst.args[0])
1893 else:
1945 else:
1894 u.warn("hg: %s\n" % inst.args[1])
1946 u.warn("hg: %s\n" % inst.args[1])
1895 help_(u, 'shortlist')
1947 help_(u, 'shortlist')
1896 sys.exit(-1)
1948 sys.exit(-1)
1897 except UnknownCommand, inst:
1949 except UnknownCommand, inst:
1898 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1950 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1899 help_(u, 'shortlist')
1951 help_(u, 'shortlist')
1900 sys.exit(1)
1952 sys.exit(1)
1901
1953
1902 if options["time"]:
1954 if options["time"]:
1903 def get_times():
1955 def get_times():
1904 t = os.times()
1956 t = os.times()
1905 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1957 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1906 t = (t[0], t[1], t[2], t[3], time.clock())
1958 t = (t[0], t[1], t[2], t[3], time.clock())
1907 return t
1959 return t
1908 s = get_times()
1960 s = get_times()
1909 def print_time():
1961 def print_time():
1910 t = get_times()
1962 t = get_times()
1911 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1963 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1912 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1964 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1913 atexit.register(print_time)
1965 atexit.register(print_time)
1914
1966
1915 u.updateopts(options["verbose"], options["debug"], options["quiet"],
1967 u.updateopts(options["verbose"], options["debug"], options["quiet"],
1916 not options["noninteractive"])
1968 not options["noninteractive"])
1917
1969
1918 try:
1970 try:
1919 try:
1971 try:
1920 if options['help']:
1972 if options['help']:
1921 help_(u, cmd, options['version'])
1973 help_(u, cmd, options['version'])
1922 sys.exit(0)
1974 sys.exit(0)
1923 elif options['version']:
1975 elif options['version']:
1924 show_version(u)
1976 show_version(u)
1925 sys.exit(0)
1977 sys.exit(0)
1926 elif not cmd:
1978 elif not cmd:
1927 help_(u, 'shortlist')
1979 help_(u, 'shortlist')
1928 sys.exit(0)
1980 sys.exit(0)
1929
1981
1930 if options['cwd']:
1982 if options['cwd']:
1931 try:
1983 try:
1932 os.chdir(options['cwd'])
1984 os.chdir(options['cwd'])
1933 except OSError, inst:
1985 except OSError, inst:
1934 u.warn('abort: %s: %s\n' % (options['cwd'], inst.strerror))
1986 u.warn('abort: %s: %s\n' % (options['cwd'], inst.strerror))
1935 sys.exit(1)
1987 sys.exit(1)
1936
1988
1937 if cmd not in norepo.split():
1989 if cmd not in norepo.split():
1938 path = options["repository"] or ""
1990 path = options["repository"] or ""
1939 repo = hg.repository(ui=u, path=path)
1991 repo = hg.repository(ui=u, path=path)
1940 for x in external:
1992 for x in external:
1941 x.reposetup(u, repo)
1993 x.reposetup(u, repo)
1942 d = lambda: func(u, repo, *args, **cmdoptions)
1994 d = lambda: func(u, repo, *args, **cmdoptions)
1943 else:
1995 else:
1944 d = lambda: func(u, *args, **cmdoptions)
1996 d = lambda: func(u, *args, **cmdoptions)
1945
1997
1946 if options['profile']:
1998 if options['profile']:
1947 import hotshot, hotshot.stats
1999 import hotshot, hotshot.stats
1948 prof = hotshot.Profile("hg.prof")
2000 prof = hotshot.Profile("hg.prof")
1949 r = prof.runcall(d)
2001 r = prof.runcall(d)
1950 prof.close()
2002 prof.close()
1951 stats = hotshot.stats.load("hg.prof")
2003 stats = hotshot.stats.load("hg.prof")
1952 stats.strip_dirs()
2004 stats.strip_dirs()
1953 stats.sort_stats('time', 'calls')
2005 stats.sort_stats('time', 'calls')
1954 stats.print_stats(40)
2006 stats.print_stats(40)
1955 return r
2007 return r
1956 else:
2008 else:
1957 return d()
2009 return d()
1958 except:
2010 except:
1959 if options['traceback']:
2011 if options['traceback']:
1960 traceback.print_exc()
2012 traceback.print_exc()
1961 raise
2013 raise
1962 except hg.RepoError, inst:
2014 except hg.RepoError, inst:
1963 u.warn("abort: ", inst, "!\n")
2015 u.warn("abort: ", inst, "!\n")
1964 except revlog.RevlogError, inst:
2016 except revlog.RevlogError, inst:
1965 u.warn("abort: ", inst, "!\n")
2017 u.warn("abort: ", inst, "!\n")
1966 except SignalInterrupt:
2018 except SignalInterrupt:
1967 u.warn("killed!\n")
2019 u.warn("killed!\n")
1968 except KeyboardInterrupt:
2020 except KeyboardInterrupt:
1969 try:
2021 try:
1970 u.warn("interrupted!\n")
2022 u.warn("interrupted!\n")
1971 except IOError, inst:
2023 except IOError, inst:
1972 if inst.errno == errno.EPIPE:
2024 if inst.errno == errno.EPIPE:
1973 if u.debugflag:
2025 if u.debugflag:
1974 u.warn("\nbroken pipe\n")
2026 u.warn("\nbroken pipe\n")
1975 else:
2027 else:
1976 raise
2028 raise
1977 except IOError, inst:
2029 except IOError, inst:
1978 if hasattr(inst, "code"):
2030 if hasattr(inst, "code"):
1979 u.warn("abort: %s\n" % inst)
2031 u.warn("abort: %s\n" % inst)
1980 elif hasattr(inst, "reason"):
2032 elif hasattr(inst, "reason"):
1981 u.warn("abort: error: %s\n" % inst.reason[1])
2033 u.warn("abort: error: %s\n" % inst.reason[1])
1982 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2034 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1983 if u.debugflag:
2035 if u.debugflag:
1984 u.warn("broken pipe\n")
2036 u.warn("broken pipe\n")
1985 else:
2037 else:
1986 raise
2038 raise
1987 except OSError, inst:
2039 except OSError, inst:
1988 if hasattr(inst, "filename"):
2040 if hasattr(inst, "filename"):
1989 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
2041 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1990 else:
2042 else:
1991 u.warn("abort: %s\n" % inst.strerror)
2043 u.warn("abort: %s\n" % inst.strerror)
1992 except util.Abort, inst:
2044 except util.Abort, inst:
1993 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
2045 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1994 sys.exit(1)
2046 sys.exit(1)
1995 except TypeError, inst:
2047 except TypeError, inst:
1996 # was this an argument error?
2048 # was this an argument error?
1997 tb = traceback.extract_tb(sys.exc_info()[2])
2049 tb = traceback.extract_tb(sys.exc_info()[2])
1998 if len(tb) > 2: # no
2050 if len(tb) > 2: # no
1999 raise
2051 raise
2000 u.debug(inst, "\n")
2052 u.debug(inst, "\n")
2001 u.warn("%s: invalid arguments\n" % cmd)
2053 u.warn("%s: invalid arguments\n" % cmd)
2002 help_(u, cmd)
2054 help_(u, cmd)
2003 except UnknownCommand, inst:
2055 except UnknownCommand, inst:
2004 u.warn("hg: unknown command '%s'\n" % inst.args[0])
2056 u.warn("hg: unknown command '%s'\n" % inst.args[0])
2005 help_(u, 'shortlist')
2057 help_(u, 'shortlist')
2006 except SystemExit:
2058 except SystemExit:
2007 # don't catch this is the catch-all below
2059 # don't catch this is the catch-all below
2008 raise
2060 raise
2009 except:
2061 except:
2010 u.warn("** unknown exception encountered, details follow\n")
2062 u.warn("** unknown exception encountered, details follow\n")
2011 u.warn("** report bug details to mercurial@selenic.com\n")
2063 u.warn("** report bug details to mercurial@selenic.com\n")
2012 raise
2064 raise
2013
2065
2014 sys.exit(-1)
2066 sys.exit(-1)
@@ -1,651 +1,651 b''
1 """
1 """
2 revlog.py - storage back-end for mercurial
2 revlog.py - storage back-end for mercurial
3
3
4 This provides efficient delta storage with O(1) retrieve and append
4 This provides efficient delta storage with O(1) retrieve and append
5 and O(changes) merge between branches
5 and O(changes) merge between branches
6
6
7 Copyright 2005 Matt Mackall <mpm@selenic.com>
7 Copyright 2005 Matt Mackall <mpm@selenic.com>
8
8
9 This software may be used and distributed according to the terms
9 This software may be used and distributed according to the terms
10 of the GNU General Public License, incorporated herein by reference.
10 of the GNU General Public License, incorporated herein by reference.
11 """
11 """
12
12
13 import zlib, struct, sha, binascii, heapq
13 import zlib, struct, sha, binascii, heapq
14 import mdiff
14 import mdiff
15 from node import *
15 from node import *
16
16
17 def hash(text, p1, p2):
17 def hash(text, p1, p2):
18 """generate a hash from the given text and its parent hashes
18 """generate a hash from the given text and its parent hashes
19
19
20 This hash combines both the current file contents and its history
20 This hash combines both the current file contents and its history
21 in a manner that makes it easy to distinguish nodes with the same
21 in a manner that makes it easy to distinguish nodes with the same
22 content in the revision graph.
22 content in the revision graph.
23 """
23 """
24 l = [p1, p2]
24 l = [p1, p2]
25 l.sort()
25 l.sort()
26 s = sha.new(l[0])
26 s = sha.new(l[0])
27 s.update(l[1])
27 s.update(l[1])
28 s.update(text)
28 s.update(text)
29 return s.digest()
29 return s.digest()
30
30
31 def compress(text):
31 def compress(text):
32 """ generate a possibly-compressed representation of text """
32 """ generate a possibly-compressed representation of text """
33 if not text: return text
33 if not text: return text
34 if len(text) < 44:
34 if len(text) < 44:
35 if text[0] == '\0': return text
35 if text[0] == '\0': return text
36 return 'u' + text
36 return 'u' + text
37 bin = zlib.compress(text)
37 bin = zlib.compress(text)
38 if len(bin) > len(text):
38 if len(bin) > len(text):
39 if text[0] == '\0': return text
39 if text[0] == '\0': return text
40 return 'u' + text
40 return 'u' + text
41 return bin
41 return bin
42
42
43 def decompress(bin):
43 def decompress(bin):
44 """ decompress the given input """
44 """ decompress the given input """
45 if not bin: return bin
45 if not bin: return bin
46 t = bin[0]
46 t = bin[0]
47 if t == '\0': return bin
47 if t == '\0': return bin
48 if t == 'x': return zlib.decompress(bin)
48 if t == 'x': return zlib.decompress(bin)
49 if t == 'u': return bin[1:]
49 if t == 'u': return bin[1:]
50 raise RevlogError("unknown compression type %s" % t)
50 raise RevlogError("unknown compression type %s" % t)
51
51
52 indexformat = ">4l20s20s20s"
52 indexformat = ">4l20s20s20s"
53
53
54 class lazyparser:
54 class lazyparser:
55 """
55 """
56 this class avoids the need to parse the entirety of large indices
56 this class avoids the need to parse the entirety of large indices
57
57
58 By default we parse and load 1000 entries at a time.
58 By default we parse and load 1000 entries at a time.
59
59
60 If no position is specified, we load the whole index, and replace
60 If no position is specified, we load the whole index, and replace
61 the lazy objects in revlog with the underlying objects for
61 the lazy objects in revlog with the underlying objects for
62 efficiency in cases where we look at most of the nodes.
62 efficiency in cases where we look at most of the nodes.
63 """
63 """
64 def __init__(self, data, revlog):
64 def __init__(self, data, revlog):
65 self.data = data
65 self.data = data
66 self.s = struct.calcsize(indexformat)
66 self.s = struct.calcsize(indexformat)
67 self.l = len(data)/self.s
67 self.l = len(data)/self.s
68 self.index = [None] * self.l
68 self.index = [None] * self.l
69 self.map = {nullid: -1}
69 self.map = {nullid: -1}
70 self.all = 0
70 self.all = 0
71 self.revlog = revlog
71 self.revlog = revlog
72
72
73 def load(self, pos=None):
73 def load(self, pos=None):
74 if self.all: return
74 if self.all: return
75 if pos is not None:
75 if pos is not None:
76 block = pos / 1000
76 block = pos / 1000
77 i = block * 1000
77 i = block * 1000
78 end = min(self.l, i + 1000)
78 end = min(self.l, i + 1000)
79 else:
79 else:
80 self.all = 1
80 self.all = 1
81 i = 0
81 i = 0
82 end = self.l
82 end = self.l
83 self.revlog.index = self.index
83 self.revlog.index = self.index
84 self.revlog.nodemap = self.map
84 self.revlog.nodemap = self.map
85
85
86 while i < end:
86 while i < end:
87 d = self.data[i * self.s: (i + 1) * self.s]
87 d = self.data[i * self.s: (i + 1) * self.s]
88 e = struct.unpack(indexformat, d)
88 e = struct.unpack(indexformat, d)
89 self.index[i] = e
89 self.index[i] = e
90 self.map[e[6]] = i
90 self.map[e[6]] = i
91 i += 1
91 i += 1
92
92
93 class lazyindex:
93 class lazyindex:
94 """a lazy version of the index array"""
94 """a lazy version of the index array"""
95 def __init__(self, parser):
95 def __init__(self, parser):
96 self.p = parser
96 self.p = parser
97 def __len__(self):
97 def __len__(self):
98 return len(self.p.index)
98 return len(self.p.index)
99 def load(self, pos):
99 def load(self, pos):
100 self.p.load(pos)
100 self.p.load(pos)
101 return self.p.index[pos]
101 return self.p.index[pos]
102 def __getitem__(self, pos):
102 def __getitem__(self, pos):
103 return self.p.index[pos] or self.load(pos)
103 return self.p.index[pos] or self.load(pos)
104 def append(self, e):
104 def append(self, e):
105 self.p.index.append(e)
105 self.p.index.append(e)
106
106
107 class lazymap:
107 class lazymap:
108 """a lazy version of the node map"""
108 """a lazy version of the node map"""
109 def __init__(self, parser):
109 def __init__(self, parser):
110 self.p = parser
110 self.p = parser
111 def load(self, key):
111 def load(self, key):
112 if self.p.all: return
112 if self.p.all: return
113 n = self.p.data.find(key)
113 n = self.p.data.find(key)
114 if n < 0:
114 if n < 0:
115 raise KeyError(key)
115 raise KeyError(key)
116 pos = n / self.p.s
116 pos = n / self.p.s
117 self.p.load(pos)
117 self.p.load(pos)
118 def __contains__(self, key):
118 def __contains__(self, key):
119 self.p.load()
119 self.p.load()
120 return key in self.p.map
120 return key in self.p.map
121 def __iter__(self):
121 def __iter__(self):
122 yield nullid
122 yield nullid
123 for i in xrange(self.p.l):
123 for i in xrange(self.p.l):
124 try:
124 try:
125 yield self.p.index[i][6]
125 yield self.p.index[i][6]
126 except:
126 except:
127 self.p.load(i)
127 self.p.load(i)
128 yield self.p.index[i][6]
128 yield self.p.index[i][6]
129 def __getitem__(self, key):
129 def __getitem__(self, key):
130 try:
130 try:
131 return self.p.map[key]
131 return self.p.map[key]
132 except KeyError:
132 except KeyError:
133 try:
133 try:
134 self.load(key)
134 self.load(key)
135 return self.p.map[key]
135 return self.p.map[key]
136 except KeyError:
136 except KeyError:
137 raise KeyError("node " + hex(key))
137 raise KeyError("node " + hex(key))
138 def __setitem__(self, key, val):
138 def __setitem__(self, key, val):
139 self.p.map[key] = val
139 self.p.map[key] = val
140
140
141 class RevlogError(Exception): pass
141 class RevlogError(Exception): pass
142
142
143 class revlog:
143 class revlog:
144 """
144 """
145 the underlying revision storage object
145 the underlying revision storage object
146
146
147 A revlog consists of two parts, an index and the revision data.
147 A revlog consists of two parts, an index and the revision data.
148
148
149 The index is a file with a fixed record size containing
149 The index is a file with a fixed record size containing
150 information on each revision, includings its nodeid (hash), the
150 information on each revision, includings its nodeid (hash), the
151 nodeids of its parents, the position and offset of its data within
151 nodeids of its parents, the position and offset of its data within
152 the data file, and the revision it's based on. Finally, each entry
152 the data file, and the revision it's based on. Finally, each entry
153 contains a linkrev entry that can serve as a pointer to external
153 contains a linkrev entry that can serve as a pointer to external
154 data.
154 data.
155
155
156 The revision data itself is a linear collection of data chunks.
156 The revision data itself is a linear collection of data chunks.
157 Each chunk represents a revision and is usually represented as a
157 Each chunk represents a revision and is usually represented as a
158 delta against the previous chunk. To bound lookup time, runs of
158 delta against the previous chunk. To bound lookup time, runs of
159 deltas are limited to about 2 times the length of the original
159 deltas are limited to about 2 times the length of the original
160 version data. This makes retrieval of a version proportional to
160 version data. This makes retrieval of a version proportional to
161 its size, or O(1) relative to the number of revisions.
161 its size, or O(1) relative to the number of revisions.
162
162
163 Both pieces of the revlog are written to in an append-only
163 Both pieces of the revlog are written to in an append-only
164 fashion, which means we never need to rewrite a file to insert or
164 fashion, which means we never need to rewrite a file to insert or
165 remove data, and can use some simple techniques to avoid the need
165 remove data, and can use some simple techniques to avoid the need
166 for locking while reading.
166 for locking while reading.
167 """
167 """
168 def __init__(self, opener, indexfile, datafile):
168 def __init__(self, opener, indexfile, datafile):
169 """
169 """
170 create a revlog object
170 create a revlog object
171
171
172 opener is a function that abstracts the file opening operation
172 opener is a function that abstracts the file opening operation
173 and can be used to implement COW semantics or the like.
173 and can be used to implement COW semantics or the like.
174 """
174 """
175 self.indexfile = indexfile
175 self.indexfile = indexfile
176 self.datafile = datafile
176 self.datafile = datafile
177 self.opener = opener
177 self.opener = opener
178 self.cache = None
178 self.cache = None
179
179
180 try:
180 try:
181 i = self.opener(self.indexfile).read()
181 i = self.opener(self.indexfile).read()
182 except IOError:
182 except IOError:
183 i = ""
183 i = ""
184
184
185 if len(i) > 10000:
185 if len(i) > 10000:
186 # big index, let's parse it on demand
186 # big index, let's parse it on demand
187 parser = lazyparser(i, self)
187 parser = lazyparser(i, self)
188 self.index = lazyindex(parser)
188 self.index = lazyindex(parser)
189 self.nodemap = lazymap(parser)
189 self.nodemap = lazymap(parser)
190 else:
190 else:
191 s = struct.calcsize(indexformat)
191 s = struct.calcsize(indexformat)
192 l = len(i) / s
192 l = len(i) / s
193 self.index = [None] * l
193 self.index = [None] * l
194 m = [None] * l
194 m = [None] * l
195
195
196 n = 0
196 n = 0
197 for f in xrange(0, len(i), s):
197 for f in xrange(0, len(i), s):
198 # offset, size, base, linkrev, p1, p2, nodeid
198 # offset, size, base, linkrev, p1, p2, nodeid
199 e = struct.unpack(indexformat, i[f:f + s])
199 e = struct.unpack(indexformat, i[f:f + s])
200 m[n] = (e[6], n)
200 m[n] = (e[6], n)
201 self.index[n] = e
201 self.index[n] = e
202 n += 1
202 n += 1
203
203
204 self.nodemap = dict(m)
204 self.nodemap = dict(m)
205 self.nodemap[nullid] = -1
205 self.nodemap[nullid] = -1
206
206
207 def tip(self): return self.node(len(self.index) - 1)
207 def tip(self): return self.node(len(self.index) - 1)
208 def count(self): return len(self.index)
208 def count(self): return len(self.index)
209 def node(self, rev): return (rev < 0) and nullid or self.index[rev][6]
209 def node(self, rev): return (rev < 0) and nullid or self.index[rev][6]
210 def rev(self, node):
210 def rev(self, node):
211 try:
211 try:
212 return self.nodemap[node]
212 return self.nodemap[node]
213 except KeyError:
213 except KeyError:
214 raise RevlogError('%s: no node %s' % (self.indexfile, hex(node)))
214 raise RevlogError('%s: no node %s' % (self.indexfile, hex(node)))
215 def linkrev(self, node): return self.index[self.rev(node)][3]
215 def linkrev(self, node): return self.index[self.rev(node)][3]
216 def parents(self, node):
216 def parents(self, node):
217 if node == nullid: return (nullid, nullid)
217 if node == nullid: return (nullid, nullid)
218 return self.index[self.rev(node)][4:6]
218 return self.index[self.rev(node)][4:6]
219
219
220 def start(self, rev): return self.index[rev][0]
220 def start(self, rev): return self.index[rev][0]
221 def length(self, rev): return self.index[rev][1]
221 def length(self, rev): return self.index[rev][1]
222 def end(self, rev): return self.start(rev) + self.length(rev)
222 def end(self, rev): return self.start(rev) + self.length(rev)
223 def base(self, rev): return self.index[rev][2]
223 def base(self, rev): return self.index[rev][2]
224
224
225 def reachable(self, rev, stop=None):
225 def reachable(self, rev, stop=None):
226 reachable = {}
226 reachable = {}
227 visit = [rev]
227 visit = [rev]
228 reachable[rev] = 1
228 reachable[rev] = 1
229 if stop:
229 if stop:
230 stopn = self.rev(stop)
230 stopn = self.rev(stop)
231 else:
231 else:
232 stopn = 0
232 stopn = 0
233 while visit:
233 while visit:
234 n = visit.pop(0)
234 n = visit.pop(0)
235 if n == stop:
235 if n == stop:
236 continue
236 continue
237 if n == nullid:
237 if n == nullid:
238 continue
238 continue
239 for p in self.parents(n):
239 for p in self.parents(n):
240 if self.rev(p) < stopn:
240 if self.rev(p) < stopn:
241 continue
241 continue
242 if p not in reachable:
242 if p not in reachable:
243 reachable[p] = 1
243 reachable[p] = 1
244 visit.append(p)
244 visit.append(p)
245 return reachable
245 return reachable
246
246
247 def heads(self, stop=None):
247 def heads(self, stop=None):
248 """return the list of all nodes that have no children"""
248 """return the list of all nodes that have no children"""
249 p = {}
249 p = {}
250 h = []
250 h = []
251 stoprev = 0
251 stoprev = 0
252 if stop and stop in self.nodemap:
252 if stop and stop in self.nodemap:
253 stoprev = self.rev(stop)
253 stoprev = self.rev(stop)
254
254
255 for r in range(self.count() - 1, -1, -1):
255 for r in range(self.count() - 1, -1, -1):
256 n = self.node(r)
256 n = self.node(r)
257 if n not in p:
257 if n not in p:
258 h.append(n)
258 h.append(n)
259 if n == stop:
259 if n == stop:
260 break
260 break
261 if r < stoprev:
261 if r < stoprev:
262 break
262 break
263 for pn in self.parents(n):
263 for pn in self.parents(n):
264 p[pn] = 1
264 p[pn] = 1
265 return h
265 return h
266
266
267 def children(self, node):
267 def children(self, node):
268 """find the children of a given node"""
268 """find the children of a given node"""
269 c = []
269 c = []
270 p = self.rev(node)
270 p = self.rev(node)
271 for r in range(p + 1, self.count()):
271 for r in range(p + 1, self.count()):
272 n = self.node(r)
272 n = self.node(r)
273 for pn in self.parents(n):
273 for pn in self.parents(n):
274 if pn == node:
274 if pn == node:
275 c.append(n)
275 c.append(n)
276 continue
276 continue
277 elif pn == nullid:
277 elif pn == nullid:
278 continue
278 continue
279 return c
279 return c
280
280
281 def lookup(self, id):
281 def lookup(self, id):
282 """locate a node based on revision number or subset of hex nodeid"""
282 """locate a node based on revision number or subset of hex nodeid"""
283 try:
283 try:
284 rev = int(id)
284 rev = int(id)
285 if str(rev) != id: raise ValueError
285 if str(rev) != id: raise ValueError
286 if rev < 0: rev = self.count() + rev
286 if rev < 0: rev = self.count() + rev
287 if rev < 0 or rev >= self.count(): raise ValueError
287 if rev < 0 or rev >= self.count(): raise ValueError
288 return self.node(rev)
288 return self.node(rev)
289 except (ValueError, OverflowError):
289 except (ValueError, OverflowError):
290 c = []
290 c = []
291 for n in self.nodemap:
291 for n in self.nodemap:
292 if hex(n).startswith(id):
292 if hex(n).startswith(id):
293 c.append(n)
293 c.append(n)
294 if len(c) > 1: raise RevlogError("Ambiguous identifier")
294 if len(c) > 1: raise RevlogError("Ambiguous identifier")
295 if len(c) < 1: raise RevlogError("No match found")
295 if len(c) < 1: raise RevlogError("No match found")
296 return c[0]
296 return c[0]
297
297
298 return None
298 return None
299
299
300 def diff(self, a, b):
300 def diff(self, a, b):
301 """return a delta between two revisions"""
301 """return a delta between two revisions"""
302 return mdiff.textdiff(a, b)
302 return mdiff.textdiff(a, b)
303
303
304 def patches(self, t, pl):
304 def patches(self, t, pl):
305 """apply a list of patches to a string"""
305 """apply a list of patches to a string"""
306 return mdiff.patches(t, pl)
306 return mdiff.patches(t, pl)
307
307
308 def delta(self, node):
308 def delta(self, node):
309 """return or calculate a delta between a node and its predecessor"""
309 """return or calculate a delta between a node and its predecessor"""
310 r = self.rev(node)
310 r = self.rev(node)
311 b = self.base(r)
311 b = self.base(r)
312 if r == b:
312 if r == b:
313 return self.diff(self.revision(self.node(r - 1)),
313 return self.diff(self.revision(self.node(r - 1)),
314 self.revision(node))
314 self.revision(node))
315 else:
315 else:
316 f = self.opener(self.datafile)
316 f = self.opener(self.datafile)
317 f.seek(self.start(r))
317 f.seek(self.start(r))
318 data = f.read(self.length(r))
318 data = f.read(self.length(r))
319 return decompress(data)
319 return decompress(data)
320
320
321 def revision(self, node):
321 def revision(self, node):
322 """return an uncompressed revision of a given"""
322 """return an uncompressed revision of a given"""
323 if node == nullid: return ""
323 if node == nullid: return ""
324 if self.cache and self.cache[0] == node: return self.cache[2]
324 if self.cache and self.cache[0] == node: return self.cache[2]
325
325
326 # look up what we need to read
326 # look up what we need to read
327 text = None
327 text = None
328 rev = self.rev(node)
328 rev = self.rev(node)
329 start, length, base, link, p1, p2, node = self.index[rev]
329 start, length, base, link, p1, p2, node = self.index[rev]
330 end = start + length
330 end = start + length
331 if base != rev: start = self.start(base)
331 if base != rev: start = self.start(base)
332
332
333 # do we have useful data cached?
333 # do we have useful data cached?
334 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
334 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
335 base = self.cache[1]
335 base = self.cache[1]
336 start = self.start(base + 1)
336 start = self.start(base + 1)
337 text = self.cache[2]
337 text = self.cache[2]
338 last = 0
338 last = 0
339
339
340 f = self.opener(self.datafile)
340 f = self.opener(self.datafile)
341 f.seek(start)
341 f.seek(start)
342 data = f.read(end - start)
342 data = f.read(end - start)
343
343
344 if text is None:
344 if text is None:
345 last = self.length(base)
345 last = self.length(base)
346 text = decompress(data[:last])
346 text = decompress(data[:last])
347
347
348 bins = []
348 bins = []
349 for r in xrange(base + 1, rev + 1):
349 for r in xrange(base + 1, rev + 1):
350 s = self.length(r)
350 s = self.length(r)
351 bins.append(decompress(data[last:last + s]))
351 bins.append(decompress(data[last:last + s]))
352 last = last + s
352 last = last + s
353
353
354 text = mdiff.patches(text, bins)
354 text = mdiff.patches(text, bins)
355
355
356 if node != hash(text, p1, p2):
356 if node != hash(text, p1, p2):
357 raise RevlogError("integrity check failed on %s:%d"
357 raise RevlogError("integrity check failed on %s:%d"
358 % (self.datafile, rev))
358 % (self.datafile, rev))
359
359
360 self.cache = (node, rev, text)
360 self.cache = (node, rev, text)
361 return text
361 return text
362
362
363 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
363 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
364 """add a revision to the log
364 """add a revision to the log
365
365
366 text - the revision data to add
366 text - the revision data to add
367 transaction - the transaction object used for rollback
367 transaction - the transaction object used for rollback
368 link - the linkrev data to add
368 link - the linkrev data to add
369 p1, p2 - the parent nodeids of the revision
369 p1, p2 - the parent nodeids of the revision
370 d - an optional precomputed delta
370 d - an optional precomputed delta
371 """
371 """
372 if text is None: text = ""
372 if text is None: text = ""
373 if p1 is None: p1 = self.tip()
373 if p1 is None: p1 = self.tip()
374 if p2 is None: p2 = nullid
374 if p2 is None: p2 = nullid
375
375
376 node = hash(text, p1, p2)
376 node = hash(text, p1, p2)
377
377
378 if node in self.nodemap:
378 if node in self.nodemap:
379 return node
379 return node
380
380
381 n = self.count()
381 n = self.count()
382 t = n - 1
382 t = n - 1
383
383
384 if n:
384 if n:
385 base = self.base(t)
385 base = self.base(t)
386 start = self.start(base)
386 start = self.start(base)
387 end = self.end(t)
387 end = self.end(t)
388 if not d:
388 if not d:
389 prev = self.revision(self.tip())
389 prev = self.revision(self.tip())
390 d = self.diff(prev, text)
390 d = self.diff(prev, text)
391 data = compress(d)
391 data = compress(d)
392 dist = end - start + len(data)
392 dist = end - start + len(data)
393
393
394 # full versions are inserted when the needed deltas
394 # full versions are inserted when the needed deltas
395 # become comparable to the uncompressed text
395 # become comparable to the uncompressed text
396 if not n or dist > len(text) * 2:
396 if not n or dist > len(text) * 2:
397 data = compress(text)
397 data = compress(text)
398 base = n
398 base = n
399 else:
399 else:
400 base = self.base(t)
400 base = self.base(t)
401
401
402 offset = 0
402 offset = 0
403 if t >= 0:
403 if t >= 0:
404 offset = self.end(t)
404 offset = self.end(t)
405
405
406 e = (offset, len(data), base, link, p1, p2, node)
406 e = (offset, len(data), base, link, p1, p2, node)
407
407
408 self.index.append(e)
408 self.index.append(e)
409 self.nodemap[node] = n
409 self.nodemap[node] = n
410 entry = struct.pack(indexformat, *e)
410 entry = struct.pack(indexformat, *e)
411
411
412 transaction.add(self.datafile, e[0])
412 transaction.add(self.datafile, e[0])
413 self.opener(self.datafile, "a").write(data)
413 self.opener(self.datafile, "a").write(data)
414 transaction.add(self.indexfile, n * len(entry))
414 transaction.add(self.indexfile, n * len(entry))
415 self.opener(self.indexfile, "a").write(entry)
415 self.opener(self.indexfile, "a").write(entry)
416
416
417 self.cache = (node, n, text)
417 self.cache = (node, n, text)
418 return node
418 return node
419
419
420 def ancestor(self, a, b):
420 def ancestor(self, a, b):
421 """calculate the least common ancestor of nodes a and b"""
421 """calculate the least common ancestor of nodes a and b"""
422 # calculate the distance of every node from root
422 # calculate the distance of every node from root
423 dist = {nullid: 0}
423 dist = {nullid: 0}
424 for i in xrange(self.count()):
424 for i in xrange(self.count()):
425 n = self.node(i)
425 n = self.node(i)
426 p1, p2 = self.parents(n)
426 p1, p2 = self.parents(n)
427 dist[n] = max(dist[p1], dist[p2]) + 1
427 dist[n] = max(dist[p1], dist[p2]) + 1
428
428
429 # traverse ancestors in order of decreasing distance from root
429 # traverse ancestors in order of decreasing distance from root
430 def ancestors(node):
430 def ancestors(node):
431 # we store negative distances because heap returns smallest member
431 # we store negative distances because heap returns smallest member
432 h = [(-dist[node], node)]
432 h = [(-dist[node], node)]
433 seen = {}
433 seen = {}
434 earliest = self.count()
434 earliest = self.count()
435 while h:
435 while h:
436 d, n = heapq.heappop(h)
436 d, n = heapq.heappop(h)
437 if n not in seen:
437 if n not in seen:
438 seen[n] = 1
438 seen[n] = 1
439 r = self.rev(n)
439 r = self.rev(n)
440 yield (-d, r, n)
440 yield (-d, r, n)
441 for p in self.parents(n):
441 for p in self.parents(n):
442 heapq.heappush(h, (-dist[p], p))
442 heapq.heappush(h, (-dist[p], p))
443
443
444 x = ancestors(a)
444 x = ancestors(a)
445 y = ancestors(b)
445 y = ancestors(b)
446 lx = x.next()
446 lx = x.next()
447 ly = y.next()
447 ly = y.next()
448
448
449 # increment each ancestor list until it is closer to root than
449 # increment each ancestor list until it is closer to root than
450 # the other, or they match
450 # the other, or they match
451 while 1:
451 while 1:
452 if lx == ly:
452 if lx == ly:
453 return lx[2]
453 return lx[2]
454 elif lx < ly:
454 elif lx < ly:
455 ly = y.next()
455 ly = y.next()
456 elif lx > ly:
456 elif lx > ly:
457 lx = x.next()
457 lx = x.next()
458
458
459 def group(self, linkmap):
459 def group(self, linkmap):
460 """calculate a delta group
460 """calculate a delta group
461
461
462 Given a list of changeset revs, return a set of deltas and
462 Given a list of changeset revs, return a set of deltas and
463 metadata corresponding to nodes. the first delta is
463 metadata corresponding to nodes. the first delta is
464 parent(nodes[0]) -> nodes[0] the receiver is guaranteed to
464 parent(nodes[0]) -> nodes[0] the receiver is guaranteed to
465 have this parent as it has all history before these
465 have this parent as it has all history before these
466 changesets. parent is parent[0]
466 changesets. parent is parent[0]
467 """
467 """
468 revs = []
468 revs = []
469 needed = {}
469 needed = {}
470
470
471 # find file nodes/revs that match changeset revs
471 # find file nodes/revs that match changeset revs
472 for i in xrange(0, self.count()):
472 for i in xrange(0, self.count()):
473 if self.index[i][3] in linkmap:
473 if self.index[i][3] in linkmap:
474 revs.append(i)
474 revs.append(i)
475 needed[i] = 1
475 needed[i] = 1
476
476
477 # if we don't have any revisions touched by these changesets, bail
477 # if we don't have any revisions touched by these changesets, bail
478 if not revs:
478 if not revs:
479 yield struct.pack(">l", 0)
479 yield struct.pack(">l", 0)
480 return
480 return
481
481
482 # add the parent of the first rev
482 # add the parent of the first rev
483 p = self.parents(self.node(revs[0]))[0]
483 p = self.parents(self.node(revs[0]))[0]
484 revs.insert(0, self.rev(p))
484 revs.insert(0, self.rev(p))
485
485
486 # for each delta that isn't contiguous in the log, we need to
486 # for each delta that isn't contiguous in the log, we need to
487 # reconstruct the base, reconstruct the result, and then
487 # reconstruct the base, reconstruct the result, and then
488 # calculate the delta. We also need to do this where we've
488 # calculate the delta. We also need to do this where we've
489 # stored a full version and not a delta
489 # stored a full version and not a delta
490 for i in xrange(0, len(revs) - 1):
490 for i in xrange(0, len(revs) - 1):
491 a, b = revs[i], revs[i + 1]
491 a, b = revs[i], revs[i + 1]
492 if a + 1 != b or self.base(b) == b:
492 if a + 1 != b or self.base(b) == b:
493 for j in xrange(self.base(a), a + 1):
493 for j in xrange(self.base(a), a + 1):
494 needed[j] = 1
494 needed[j] = 1
495 for j in xrange(self.base(b), b + 1):
495 for j in xrange(self.base(b), b + 1):
496 needed[j] = 1
496 needed[j] = 1
497
497
498 # calculate spans to retrieve from datafile
498 # calculate spans to retrieve from datafile
499 needed = needed.keys()
499 needed = needed.keys()
500 needed.sort()
500 needed.sort()
501 spans = []
501 spans = []
502 oo = -1
502 oo = -1
503 ol = 0
503 ol = 0
504 for n in needed:
504 for n in needed:
505 if n < 0: continue
505 if n < 0: continue
506 o = self.start(n)
506 o = self.start(n)
507 l = self.length(n)
507 l = self.length(n)
508 if oo + ol == o: # can we merge with the previous?
508 if oo + ol == o: # can we merge with the previous?
509 nl = spans[-1][2]
509 nl = spans[-1][2]
510 nl.append((n, l))
510 nl.append((n, l))
511 ol += l
511 ol += l
512 spans[-1] = (oo, ol, nl)
512 spans[-1] = (oo, ol, nl)
513 else:
513 else:
514 oo = o
514 oo = o
515 ol = l
515 ol = l
516 spans.append((oo, ol, [(n, l)]))
516 spans.append((oo, ol, [(n, l)]))
517
517
518 # read spans in, divide up chunks
518 # read spans in, divide up chunks
519 chunks = {}
519 chunks = {}
520 for span in spans:
520 for span in spans:
521 # we reopen the file for each span to make http happy for now
521 # we reopen the file for each span to make http happy for now
522 f = self.opener(self.datafile)
522 f = self.opener(self.datafile)
523 f.seek(span[0])
523 f.seek(span[0])
524 data = f.read(span[1])
524 data = f.read(span[1])
525
525
526 # divide up the span
526 # divide up the span
527 pos = 0
527 pos = 0
528 for r, l in span[2]:
528 for r, l in span[2]:
529 chunks[r] = decompress(data[pos: pos + l])
529 chunks[r] = decompress(data[pos: pos + l])
530 pos += l
530 pos += l
531
531
532 # helper to reconstruct intermediate versions
532 # helper to reconstruct intermediate versions
533 def construct(text, base, rev):
533 def construct(text, base, rev):
534 bins = [chunks[r] for r in xrange(base + 1, rev + 1)]
534 bins = [chunks[r] for r in xrange(base + 1, rev + 1)]
535 return mdiff.patches(text, bins)
535 return mdiff.patches(text, bins)
536
536
537 # build deltas
537 # build deltas
538 deltas = []
538 deltas = []
539 for d in xrange(0, len(revs) - 1):
539 for d in xrange(0, len(revs) - 1):
540 a, b = revs[d], revs[d + 1]
540 a, b = revs[d], revs[d + 1]
541 n = self.node(b)
541 n = self.node(b)
542
542
543 # do we need to construct a new delta?
543 # do we need to construct a new delta?
544 if a + 1 != b or self.base(b) == b:
544 if a + 1 != b or self.base(b) == b:
545 if a >= 0:
545 if a >= 0:
546 base = self.base(a)
546 base = self.base(a)
547 ta = chunks[self.base(a)]
547 ta = chunks[self.base(a)]
548 ta = construct(ta, base, a)
548 ta = construct(ta, base, a)
549 else:
549 else:
550 ta = ""
550 ta = ""
551
551
552 base = self.base(b)
552 base = self.base(b)
553 if a > base:
553 if a > base:
554 base = a
554 base = a
555 tb = ta
555 tb = ta
556 else:
556 else:
557 tb = chunks[self.base(b)]
557 tb = chunks[self.base(b)]
558 tb = construct(tb, base, b)
558 tb = construct(tb, base, b)
559 d = self.diff(ta, tb)
559 d = self.diff(ta, tb)
560 else:
560 else:
561 d = chunks[b]
561 d = chunks[b]
562
562
563 p = self.parents(n)
563 p = self.parents(n)
564 meta = n + p[0] + p[1] + linkmap[self.linkrev(n)]
564 meta = n + p[0] + p[1] + linkmap[self.linkrev(n)]
565 l = struct.pack(">l", len(meta) + len(d) + 4)
565 l = struct.pack(">l", len(meta) + len(d) + 4)
566 yield l
566 yield l
567 yield meta
567 yield meta
568 yield d
568 yield d
569
569
570 yield struct.pack(">l", 0)
570 yield struct.pack(">l", 0)
571
571
572 def addgroup(self, revs, linkmapper, transaction, unique=0):
572 def addgroup(self, revs, linkmapper, transaction, unique=0):
573 """
573 """
574 add a delta group
574 add a delta group
575
575
576 given a set of deltas, add them to the revision log. the
576 given a set of deltas, add them to the revision log. the
577 first delta is against its parent, which should be in our
577 first delta is against its parent, which should be in our
578 log, the rest are against the previous delta.
578 log, the rest are against the previous delta.
579 """
579 """
580
580
581 #track the base of the current delta log
581 #track the base of the current delta log
582 r = self.count()
582 r = self.count()
583 t = r - 1
583 t = r - 1
584 node = nullid
584 node = nullid
585
585
586 base = prev = -1
586 base = prev = -1
587 start = end = measure = 0
587 start = end = measure = 0
588 if r:
588 if r:
589 start = self.start(self.base(t))
589 start = self.start(self.base(t))
590 end = self.end(t)
590 end = self.end(t)
591 measure = self.length(self.base(t))
591 measure = self.length(self.base(t))
592 base = self.base(t)
592 base = self.base(t)
593 prev = self.tip()
593 prev = self.tip()
594
594
595 transaction.add(self.datafile, end)
595 transaction.add(self.datafile, end)
596 transaction.add(self.indexfile, r * struct.calcsize(indexformat))
596 transaction.add(self.indexfile, r * struct.calcsize(indexformat))
597 dfh = self.opener(self.datafile, "a")
597 dfh = self.opener(self.datafile, "a")
598 ifh = self.opener(self.indexfile, "a")
598 ifh = self.opener(self.indexfile, "a")
599
599
600 # loop through our set of deltas
600 # loop through our set of deltas
601 chain = None
601 chain = None
602 for chunk in revs:
602 for chunk in revs:
603 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
603 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
604 link = linkmapper(cs)
604 link = linkmapper(cs)
605 if node in self.nodemap:
605 if node in self.nodemap:
606 # this can happen if two branches make the same change
606 # this can happen if two branches make the same change
607 if unique:
607 # if unique:
608 raise RevlogError("already have %s" % hex(node[:4]))
608 # raise RevlogError("already have %s" % hex(node[:4]))
609 chain = node
609 chain = node
610 continue
610 continue
611 delta = chunk[80:]
611 delta = chunk[80:]
612
612
613 if not chain:
613 if not chain:
614 # retrieve the parent revision of the delta chain
614 # retrieve the parent revision of the delta chain
615 chain = p1
615 chain = p1
616 if not chain in self.nodemap:
616 if not chain in self.nodemap:
617 raise RevlogError("unknown base %s" % short(chain[:4]))
617 raise RevlogError("unknown base %s" % short(chain[:4]))
618
618
619 # full versions are inserted when the needed deltas become
619 # full versions are inserted when the needed deltas become
620 # comparable to the uncompressed text or when the previous
620 # comparable to the uncompressed text or when the previous
621 # version is not the one we have a delta against. We use
621 # version is not the one we have a delta against. We use
622 # the size of the previous full rev as a proxy for the
622 # the size of the previous full rev as a proxy for the
623 # current size.
623 # current size.
624
624
625 if chain == prev:
625 if chain == prev:
626 cdelta = compress(delta)
626 cdelta = compress(delta)
627
627
628 if chain != prev or (end - start + len(cdelta)) > measure * 2:
628 if chain != prev or (end - start + len(cdelta)) > measure * 2:
629 # flush our writes here so we can read it in revision
629 # flush our writes here so we can read it in revision
630 dfh.flush()
630 dfh.flush()
631 ifh.flush()
631 ifh.flush()
632 text = self.revision(chain)
632 text = self.revision(chain)
633 text = self.patches(text, [delta])
633 text = self.patches(text, [delta])
634 chk = self.addrevision(text, transaction, link, p1, p2)
634 chk = self.addrevision(text, transaction, link, p1, p2)
635 if chk != node:
635 if chk != node:
636 raise RevlogError("consistency error adding group")
636 raise RevlogError("consistency error adding group")
637 measure = len(text)
637 measure = len(text)
638 else:
638 else:
639 e = (end, len(cdelta), self.base(t), link, p1, p2, node)
639 e = (end, len(cdelta), self.base(t), link, p1, p2, node)
640 self.index.append(e)
640 self.index.append(e)
641 self.nodemap[node] = r
641 self.nodemap[node] = r
642 dfh.write(cdelta)
642 dfh.write(cdelta)
643 ifh.write(struct.pack(indexformat, *e))
643 ifh.write(struct.pack(indexformat, *e))
644
644
645 t, r, chain, prev = r, r + 1, node, node
645 t, r, chain, prev = r, r + 1, node, node
646 start = self.start(self.base(t))
646 start = self.start(self.base(t))
647 end = self.end(t)
647 end = self.end(t)
648
648
649 dfh.close()
649 dfh.close()
650 ifh.close()
650 ifh.close()
651 return node
651 return node
General Comments 0
You need to be logged in to leave comments. Login now