##// END OF EJS Templates
Get add and locate to use new repo and dirstate walk code....
Bryan O'Sullivan -
r724:1c0c413c default
parent child Browse files
Show More
@@ -1,527 +1,573 b''
1 HG(1)
1 HG(1)
2 =====
2 =====
3 Matt Mackall <mpm@selenic.com>
3 Matt Mackall <mpm@selenic.com>
4
4
5 NAME
5 NAME
6 ----
6 ----
7 hg - Mercurial source code management system
7 hg - Mercurial source code management system
8
8
9 SYNOPSIS
9 SYNOPSIS
10 --------
10 --------
11 'hg' [-v -d -q -y] <command> [command options] [files]
11 'hg' [-v -d -q -y] <command> [command options] [files]
12
12
13 DESCRIPTION
13 DESCRIPTION
14 -----------
14 -----------
15 The hg(1) command provides a command line interface to the Mercurial system.
15 The hg(1) command provides a command line interface to the Mercurial system.
16
16
17 OPTIONS
17 OPTIONS
18 -------
18 -------
19
19
20 --debug, -d::
20 --debug, -d::
21 enable debugging output
21 enable debugging output
22
22
23 --quiet, -q::
23 --quiet, -q::
24 suppress output
24 suppress output
25
25
26 --verbose, -v::
26 --verbose, -v::
27 enable additional output
27 enable additional output
28
28
29 --noninteractive, -y::
29 --noninteractive, -y::
30 do not prompt, assume 'yes' for any required answers
30 do not prompt, assume 'yes' for any required answers
31
31
32 COMMAND ELEMENTS
32 COMMAND ELEMENTS
33 ----------------
33 ----------------
34
34
35 files ...::
35 files ...::
36 indicates one or more filename or relative path filenames
36 indicates one or more filename or relative path filenames; see
37 "FILE NAME PATTERNS" for information on pattern matching
37
38
38 path::
39 path::
39 indicates a path on the local machine
40 indicates a path on the local machine
40
41
41 revision::
42 revision::
42 indicates a changeset which can be specified as a changeset revision
43 indicates a changeset which can be specified as a changeset revision
43 number, a tag, or a unique substring of the changeset hash value
44 number, a tag, or a unique substring of the changeset hash value
44
45
45 repository path::
46 repository path::
46 either the pathname of a local repository or the URI of a remote
47 either the pathname of a local repository or the URI of a remote
47 repository. There are two available URI protocols, http:// which is
48 repository. There are two available URI protocols, http:// which is
48 fast and the old-http:// protocol which is much slower but does not
49 fast and the old-http:// protocol which is much slower but does not
49 require a special server on the web host.
50 require a special server on the web host.
50
51
51 COMMANDS
52 COMMANDS
52 --------
53 --------
53
54
54 add [files ...]::
55 add [options] [files ...]::
55 Schedule files to be version controlled and added to the repository.
56 Schedule files to be version controlled and added to the repository.
56
57
57 The files will be added to the repository at the next commit.
58 The files will be added to the repository at the next commit.
58
59
60 If no names are given, add all files in the current directory and
61 its subdirectory.
62
59 addremove::
63 addremove::
60 Add all new files and remove all missing files from the repository.
64 Add all new files and remove all missing files from the repository.
61
65
62 New files are ignored if they match any of the patterns in .hgignore. As
66 New files are ignored if they match any of the patterns in .hgignore. As
63 with add, these changes take effect at the next commit.
67 with add, these changes take effect at the next commit.
64
68
65 annotate [-r <rev> -u -n -c] [files ...]::
69 annotate [-r <rev> -u -n -c] [files ...]::
66 List changes in files, showing the revision id responsible for each line
70 List changes in files, showing the revision id responsible for each line
67
71
68 This command is useful to discover who did a change or when a change took
72 This command is useful to discover who did a change or when a change took
69 place.
73 place.
70
74
71 options:
75 options:
72 -r, --revision <rev> annotate the specified revision
76 -r, --revision <rev> annotate the specified revision
73 -u, --user list the author
77 -u, --user list the author
74 -c, --changeset list the changeset
78 -c, --changeset list the changeset
75 -n, --number list the revision number (default)
79 -n, --number list the revision number (default)
76
80
77 cat <file> [revision]::
81 cat <file> [revision]::
78 Output to stdout the given revision for the specified file.
82 Output to stdout the given revision for the specified file.
79
83
80 If no revision is given then the tip is used.
84 If no revision is given then the tip is used.
81
85
82 clone [-U] <source> [dest]::
86 clone [-U] <source> [dest]::
83 Create a copy of an existing repository in a new directory.
87 Create a copy of an existing repository in a new directory.
84
88
85 If no destination directory name is specified, it defaults to the
89 If no destination directory name is specified, it defaults to the
86 basename of the source.
90 basename of the source.
87
91
88 The source is added to the new repository's .hg/hgrc file to be used in
92 The source is added to the new repository's .hg/hgrc file to be used in
89 future pulls.
93 future pulls.
90
94
91 For efficiency, hardlinks are used for cloning whenever the
95 For efficiency, hardlinks are used for cloning whenever the
92 source and destination are on the same filesystem.
96 source and destination are on the same filesystem.
93
97
94 options:
98 options:
95 -U, --noupdate do not update the new working directory
99 -U, --noupdate do not update the new working directory
96
100
97 commit [-A -t -l <file> -t <text> -u <user> -d <datecode>] [files...]::
101 commit [-A -t -l <file> -t <text> -u <user> -d <datecode>] [files...]::
98 Commit changes to the given files into the repository.
102 Commit changes to the given files into the repository.
99
103
100 If a list of files is omitted, all changes reported by "hg status"
104 If a list of files is omitted, all changes reported by "hg status"
101 will be commited.
105 will be commited.
102
106
103 The HGEDITOR or EDITOR environment variables are used to start an
107 The HGEDITOR or EDITOR environment variables are used to start an
104 editor to add a commit comment.
108 editor to add a commit comment.
105
109
106 Options:
110 Options:
107
111
108 -A, --addremove run addremove during commit
112 -A, --addremove run addremove during commit
109 -t, --text <text> use <text> as commit message
113 -t, --text <text> use <text> as commit message
110 -l, --logfile <file> show the commit message for the given file
114 -l, --logfile <file> show the commit message for the given file
111 -d, --date <datecode> record datecode as commit date
115 -d, --date <datecode> record datecode as commit date
112 -u, --user <user> record user as commiter
116 -u, --user <user> record user as commiter
113
117
114 aliases: ci
118 aliases: ci
115
119
116 copy <source> <dest>::
120 copy <source> <dest>::
117 Mark <dest> file as a copy or rename of a <source> one
121 Mark <dest> file as a copy or rename of a <source> one
118
122
119 This command takes effect for the next commit.
123 This command takes effect for the next commit.
120
124
121 diff [-r revision] [-r revision] [files ...]::
125 diff [-r revision] [-r revision] [files ...]::
122 Show differences between revisions for the specified files.
126 Show differences between revisions for the specified files.
123
127
124 Differences between files are shown using the unified diff format.
128 Differences between files are shown using the unified diff format.
125
129
126 When two revision arguments are given, then changes are shown
130 When two revision arguments are given, then changes are shown
127 between those revisions. If only one revision is specified then
131 between those revisions. If only one revision is specified then
128 that revision is compared to the working directory, and, when no
132 that revision is compared to the working directory, and, when no
129 revisions are specified, the working directory files are compared
133 revisions are specified, the working directory files are compared
130 to its parent.
134 to its parent.
131
135
132 export [-o filespec] [revision] ...::
136 export [-o filespec] [revision] ...::
133 Print the changeset header and diffs for one or more revisions.
137 Print the changeset header and diffs for one or more revisions.
134
138
135 The information shown in the changeset header is: author,
139 The information shown in the changeset header is: author,
136 changeset hash, parent and commit comment.
140 changeset hash, parent and commit comment.
137
141
138 Output may be to a file, in which case the name of the file is
142 Output may be to a file, in which case the name of the file is
139 given using a format string. The formatting rules are as follows:
143 given using a format string. The formatting rules are as follows:
140
144
141 %% literal "%" character
145 %% literal "%" character
142 %H changeset hash (40 bytes of hexadecimal)
146 %H changeset hash (40 bytes of hexadecimal)
143 %N number of patches being generated
147 %N number of patches being generated
144 %R changeset revision number
148 %R changeset revision number
145 %b basename of the exporting repository
149 %b basename of the exporting repository
146 %h short-form changeset hash (12 bytes of hexadecimal)
150 %h short-form changeset hash (12 bytes of hexadecimal)
147 %n zero-padded sequence number, starting at 1
151 %n zero-padded sequence number, starting at 1
148 %r zero-padded changeset revision number
152 %r zero-padded changeset revision number
149
153
150 Options:
154 Options:
151
155
152 -o, --output <filespec> print output to file with formatted named
156 -o, --output <filespec> print output to file with formatted named
153
157
154 forget [files]::
158 forget [files]::
155 Undo an 'hg add' scheduled for the next commit.
159 Undo an 'hg add' scheduled for the next commit.
156
160
157 heads::
161 heads::
158 Show all repository head changesets.
162 Show all repository head changesets.
159
163
160 Repository "heads" are changesets that don't have children
164 Repository "heads" are changesets that don't have children
161 changesets. They are where development generally takes place and
165 changesets. They are where development generally takes place and
162 are the usual targets for update and merge operations.
166 are the usual targets for update and merge operations.
163
167
164 identify::
168 identify::
165 Print a short summary of the current state of the repo.
169 Print a short summary of the current state of the repo.
166
170
167 This summary identifies the repository state using one or two parent
171 This summary identifies the repository state using one or two parent
168 hash identifiers, followed by a "+" if there are uncommitted changes
172 hash identifiers, followed by a "+" if there are uncommitted changes
169 in the working directory, followed by a list of tags for this revision.
173 in the working directory, followed by a list of tags for this revision.
170
174
171 aliases: id
175 aliases: id
172
176
173 import [-p <n> -b <base> -q] <patches>::
177 import [-p <n> -b <base> -q] <patches>::
174 Import a list of patches and commit them individually.
178 Import a list of patches and commit them individually.
175
179
176 options:
180 options:
177 -p, --strip <n> directory strip option for patch. This has the same
181 -p, --strip <n> directory strip option for patch. This has the same
178 meaning as the correnponding patch option
182 meaning as the correnponding patch option
179 -b <path> base directory to read patches from
183 -b <path> base directory to read patches from
180
184
181 aliases: patch
185 aliases: patch
182
186
183 init::
187 init::
184 Initialize a new repository in the current directory.
188 Initialize a new repository in the current directory.
185
189
186 locate [options] [patterns]::
190 locate [options] [files]::
187 Print all files under Mercurial control whose basenames match the
191 Print all files under Mercurial control whose names match the
188 given patterns.
192 given patterns.
189
193
190 Patterns are shell-style globs. To restrict searches to specific
191 directories, use the "-i <pat>" option. To eliminate particular
192 directories from searching, use the "-x <pat>" option.
193
194 This command searches the current directory and its
194 This command searches the current directory and its
195 subdirectories. To search an entire repository, move to the root
195 subdirectories. To search an entire repository, move to the root
196 of the repository.
196 of the repository.
197
197
198 If no patterns are given to match, this command prints all file
198 If no patterns are given to match, this command prints all file
199 names.
199 names.
200
200
201 If you want to feed the output of this command into the "xargs"
201 If you want to feed the output of this command into the "xargs"
202 command, use the "-0" option to both this command and "xargs".
202 command, use the "-0" option to both this command and "xargs".
203 This will avoid the problem of "xargs" treating single filenames
203 This will avoid the problem of "xargs" treating single filenames
204 that contain white space as multiple file names.
204 that contain white space as multiple file names.
205
205
206 options:
206 options:
207
207
208 -0, --print0 end filenames with NUL, for use with xargs
208 -0, --print0 end filenames with NUL, for use with xargs
209 -f, --fullpath print complete paths from the filesystem root
209 -f, --fullpath print complete paths from the filesystem root
210 -i, --include <pat> include directories matching the given globs
210 -I, --include <pat> include directories matching the given patterns
211 -r, --rev <rev> search the repository as it stood at rev
211 -r, --rev <rev> search the repository as it stood at rev
212 -x, --exclude <pat> exclude directories matching the given globs
212 -X, --exclude <pat> exclude directories matching the given patterns
213
213
214 log [-r revision ...] [-p] [file]::
214 log [-r revision ...] [-p] [file]::
215 Print the revision history of the specified file or the entire project.
215 Print the revision history of the specified file or the entire project.
216
216
217 By default this command outputs: changeset id and hash, tags,
217 By default this command outputs: changeset id and hash, tags,
218 parents, user, date and time, and a summary for each commit. The
218 parents, user, date and time, and a summary for each commit. The
219 -v switch adds some more detail, such as changed files, manifest
219 -v switch adds some more detail, such as changed files, manifest
220 hashes or message signatures.
220 hashes or message signatures.
221
221
222 options:
222 options:
223 -r, --rev <A>, ... When a revision argument is given, only this file or
223 -r, --rev <A>, ... When a revision argument is given, only this file or
224 changelog revision is displayed. With two revision
224 changelog revision is displayed. With two revision
225 arguments all revisions in this range are listed.
225 arguments all revisions in this range are listed.
226 Additional revision arguments may be given repeating
226 Additional revision arguments may be given repeating
227 the above cycle.
227 the above cycle.
228 -p, --patch show patch
228 -p, --patch show patch
229
229
230 aliases: history
230 aliases: history
231
231
232 manifest [revision]::
232 manifest [revision]::
233 Print a list of version controlled files for the given revision.
233 Print a list of version controlled files for the given revision.
234
234
235 The manifest is the list of files being version controlled. If no revision
235 The manifest is the list of files being version controlled. If no revision
236 is given then the tip is used.
236 is given then the tip is used.
237
237
238 parents::
238 parents::
239 Print the working directory's parent revisions.
239 Print the working directory's parent revisions.
240
240
241 pull <repository path>::
241 pull <repository path>::
242 Pull changes from a remote repository to a local one.
242 Pull changes from a remote repository to a local one.
243
243
244 This finds all changes from the repository at the specified path
244 This finds all changes from the repository at the specified path
245 or URL and adds them to the local repository. By default, this
245 or URL and adds them to the local repository. By default, this
246 does not update the copy of the project in the working directory.
246 does not update the copy of the project in the working directory.
247
247
248 options:
248 options:
249 -u, --update update the working directory to tip after pull
249 -u, --update update the working directory to tip after pull
250
250
251 push <destination>::
251 push <destination>::
252 Push changes from the local repository to the given destination.
252 Push changes from the local repository to the given destination.
253
253
254 This is the symmetrical operation for pull. It helps to move
254 This is the symmetrical operation for pull. It helps to move
255 changes from the current repository to a different one. If the
255 changes from the current repository to a different one. If the
256 destination is local this is identical to a pull in that directory
256 destination is local this is identical to a pull in that directory
257 from the current one.
257 from the current one.
258
258
259 The other currently available push method is SSH. This requires an
259 The other currently available push method is SSH. This requires an
260 accessible shell account on the destination machine and a copy of
260 accessible shell account on the destination machine and a copy of
261 hg in the remote path. Destinations are specified in the following
261 hg in the remote path. Destinations are specified in the following
262 form:
262 form:
263
263
264 ssh://[user@]host[:port]/path
264 ssh://[user@]host[:port]/path
265
265
266 rawcommit [-p -d -u -F -t -l]::
266 rawcommit [-p -d -u -F -t -l]::
267 Lowlevel commit, for use in helper scripts.
267 Lowlevel commit, for use in helper scripts.
268
268
269 This command is not intended to be used by normal users, as it is
269 This command is not intended to be used by normal users, as it is
270 primarily useful for importing from other SCMs.
270 primarily useful for importing from other SCMs.
271
271
272 recover::
272 recover::
273 Recover from an interrupted commit or pull.
273 Recover from an interrupted commit or pull.
274
274
275 This command tries to fix the repository status after an interrupted
275 This command tries to fix the repository status after an interrupted
276 operation. It should only be necessary when Mercurial suggests it.
276 operation. It should only be necessary when Mercurial suggests it.
277
277
278 remove [files ...]::
278 remove [files ...]::
279 Schedule the indicated files for removal from the repository.
279 Schedule the indicated files for removal from the repository.
280
280
281 This command shedules the files to be removed at the next commit.
281 This command shedules the files to be removed at the next commit.
282 This only removes files from the current branch, not from the
282 This only removes files from the current branch, not from the
283 entire project history.
283 entire project history.
284
284
285 aliases: rm
285 aliases: rm
286
286
287 revert [names ...]::
287 revert [names ...]::
288 Revert any uncommitted modifications made to the named files or
288 Revert any uncommitted modifications made to the named files or
289 directories. This restores the contents of the affected files to
289 directories. This restores the contents of the affected files to
290 an unmodified state.
290 an unmodified state.
291
291
292 If a file has been deleted, it is recreated. If the executable
292 If a file has been deleted, it is recreated. If the executable
293 mode of a file was changed, it is reset.
293 mode of a file was changed, it is reset.
294
294
295 If a directory is given, all files in that directory and its
295 If a directory is given, all files in that directory and its
296 subdirectories are reverted.
296 subdirectories are reverted.
297
297
298 If no arguments are given, all files in the current directory and
298 If no arguments are given, all files in the current directory and
299 its subdirectories are reverted.
299 its subdirectories are reverted.
300
300
301 options:
301 options:
302 -r, --rev <rev> revision to revert to
302 -r, --rev <rev> revision to revert to
303 -n, --nonrecursive do not recurse into subdirectories
303 -n, --nonrecursive do not recurse into subdirectories
304
304
305 root::
305 root::
306 Print the root directory of the current repository.
306 Print the root directory of the current repository.
307
307
308 serve [options]::
308 serve [options]::
309 Start a local HTTP repository browser and pull server.
309 Start a local HTTP repository browser and pull server.
310
310
311 By default, the server logs accesses to stdout and errors to
311 By default, the server logs accesses to stdout and errors to
312 stderr. Use the "-A" and "-E" options to log to files.
312 stderr. Use the "-A" and "-E" options to log to files.
313
313
314 options:
314 options:
315 -A, --accesslog <file> name of access log file to write to
315 -A, --accesslog <file> name of access log file to write to
316 -E, --errorlog <file> name of error log file to write to
316 -E, --errorlog <file> name of error log file to write to
317 -a, --address <addr> address to use
317 -a, --address <addr> address to use
318 -p, --port <n> port to use (default: 8000)
318 -p, --port <n> port to use (default: 8000)
319 -n, --name <name> name to show in web pages (default: working dir)
319 -n, --name <name> name to show in web pages (default: working dir)
320 -t, --templatedir <path> web templates to use
320 -t, --templatedir <path> web templates to use
321
321
322 status::
322 status::
323 Show changed files in the working directory.
323 Show changed files in the working directory.
324
324
325 The codes used to show the status of files are:
325 The codes used to show the status of files are:
326
326
327 C = changed
327 C = changed
328 A = added
328 A = added
329 R = removed
329 R = removed
330 ? = not tracked
330 ? = not tracked
331
331
332 tag [-l -t <text> -d <datecode> -u <user>] <name> [revision]::
332 tag [-l -t <text> -d <datecode> -u <user>] <name> [revision]::
333 Name a particular revision using <name>.
333 Name a particular revision using <name>.
334
334
335 Tags are used to name particular revisions of the repository and are
335 Tags are used to name particular revisions of the repository and are
336 very useful to compare different revision, to go back to significant
336 very useful to compare different revision, to go back to significant
337 earlier versions or to mark branch points as releases, etc.
337 earlier versions or to mark branch points as releases, etc.
338
338
339 If no revision is given, the tip is used.
339 If no revision is given, the tip is used.
340
340
341 To facilitate version control, distribution, and merging of tags,
341 To facilitate version control, distribution, and merging of tags,
342 they are stored as a file named ".hgtags" which is managed
342 they are stored as a file named ".hgtags" which is managed
343 similarly to other project files and can be hand-edited if
343 similarly to other project files and can be hand-edited if
344 necessary.
344 necessary.
345
345
346 options:
346 options:
347 -l, --local make the tag local
347 -l, --local make the tag local
348 -t, --text <text> message for tag commit log entry
348 -t, --text <text> message for tag commit log entry
349 -d, --date <datecode> datecode for commit
349 -d, --date <datecode> datecode for commit
350 -u, --user <user> user for commit
350 -u, --user <user> user for commit
351
351
352 Note: Local tags are not version-controlled or distributed and are
352 Note: Local tags are not version-controlled or distributed and are
353 stored in the .hg/localtags file. If there exists a local tag and
353 stored in the .hg/localtags file. If there exists a local tag and
354 a public tag with the same name, local tag is used.
354 a public tag with the same name, local tag is used.
355
355
356 tags::
356 tags::
357 List the repository tags.
357 List the repository tags.
358
358
359 This lists both regular and local tags.
359 This lists both regular and local tags.
360
360
361 tip::
361 tip::
362 Show the tip revision.
362 Show the tip revision.
363
363
364 undo::
364 undo::
365 Undo the last commit or pull transaction.
365 Undo the last commit or pull transaction.
366
366
367 Roll back the last pull or commit transaction on the
367 Roll back the last pull or commit transaction on the
368 repository, restoring the project to its earlier state.
368 repository, restoring the project to its earlier state.
369
369
370 This command should be used with care. There is only one level of
370 This command should be used with care. There is only one level of
371 undo and there is no redo.
371 undo and there is no redo.
372
372
373 This command is not intended for use on public repositories. Once
373 This command is not intended for use on public repositories. Once
374 a change is visible for pull by other users, undoing it locally is
374 a change is visible for pull by other users, undoing it locally is
375 ineffective.
375 ineffective.
376
376
377 update [-m -C] [revision]::
377 update [-m -C] [revision]::
378 Update the working directory to the specified revision.
378 Update the working directory to the specified revision.
379
379
380 By default, update will refuse to run if doing so would require
380 By default, update will refuse to run if doing so would require
381 merging or discarding local changes.
381 merging or discarding local changes.
382
382
383 With the -m option, a merge will be performed.
383 With the -m option, a merge will be performed.
384
384
385 With the -C option, local changes will be lost.
385 With the -C option, local changes will be lost.
386
386
387 options:
387 options:
388 -m, --merge allow merging of branches
388 -m, --merge allow merging of branches
389 -C, --clean overwrite locally modified files
389 -C, --clean overwrite locally modified files
390
390
391 aliases: up checkout co
391 aliases: up checkout co
392
392
393 verify::
393 verify::
394 Verify the integrity of the current repository.
394 Verify the integrity of the current repository.
395
395
396 This will perform an extensive check of the repository's
396 This will perform an extensive check of the repository's
397 integrity, validating the hashes and checksums of each entry in
397 integrity, validating the hashes and checksums of each entry in
398 the changelog, manifest, and tracked files, as well as the
398 the changelog, manifest, and tracked files, as well as the
399 integrity of their crosslinks and indices.
399 integrity of their crosslinks and indices.
400
400
401 FILE NAME PATTERNS
402 ------------------
403
404 Mercurial accepts several notations for identifying one or more
405 file at a time.
406
407 By default, Mercurial treats file names as shell-style extended
408 glob patterns.
409
410 Alternate pattern notations must be specified explicitly.
411
412 To use a plain path name without any pattern matching, start a
413 name with "path:". These path names must match completely, from
414 the root of the current repository.
415
416 To use an extended glob, start a name with "glob:". Globs are
417 rooted at the current directory; a glob such as "*.c" will match
418 files ending in ".c" in the current directory only.
419
420 The supported glob syntax extensions are "**" to match any string
421 across path separators, and "{a,b}" to mean "a or b".
422
423 To use a Perl/Python regular expression, start a name with "re:".
424 Regexp pattern matching is anchored at the root of the repository.
425
426 Plain examples:
427
428 path:foo/bar a name bar in a directory named foo in the root of
429 the repository
430 path:path:name a file or directory named "path:name"
431
432 Glob examples:
433
434 glob:*.c any name ending in ".c" in the current directory
435 *.c any name ending in ".c" in the current directory
436 **.c any name ending in ".c" in the current directory, or
437 any subdirectory
438 foo/*.c any name ending in ".c" in the directory foo
439 foo/**.c any name ending in ".c" in the directory foo, or any
440 subdirectory
441
442 Regexp examples:
443
444 re:.*\.c$ any name ending in ".c", anywhere in the repsitory
445
446
401 SPECIFYING SINGLE REVISIONS
447 SPECIFYING SINGLE REVISIONS
402 ---------------------------
448 ---------------------------
403
449
404 Mercurial accepts several notations for identifying individual
450 Mercurial accepts several notations for identifying individual
405 revisions.
451 revisions.
406
452
407 A plain integer is treated as a revision number. Negative
453 A plain integer is treated as a revision number. Negative
408 integers are treated as offsets from the tip, with -1 denoting the
454 integers are treated as offsets from the tip, with -1 denoting the
409 tip.
455 tip.
410
456
411 A 40-digit hexadecimal string is treated as a unique revision
457 A 40-digit hexadecimal string is treated as a unique revision
412 identifier.
458 identifier.
413
459
414 A hexadecimal string less than 40 characters long is treated as a
460 A hexadecimal string less than 40 characters long is treated as a
415 unique revision identifier, and referred to as a short-form
461 unique revision identifier, and referred to as a short-form
416 identifier. A short-form identifier is only valid if it is the
462 identifier. A short-form identifier is only valid if it is the
417 prefix of one full-length identifier.
463 prefix of one full-length identifier.
418
464
419 Any other string is treated as a tag name, which is a symbolic
465 Any other string is treated as a tag name, which is a symbolic
420 name associated with a revision identifier. Tag names may not
466 name associated with a revision identifier. Tag names may not
421 contain the ":" character.
467 contain the ":" character.
422
468
423 The reserved name "tip" is a special tag that always identifies
469 The reserved name "tip" is a special tag that always identifies
424 the most recent revision.
470 the most recent revision.
425
471
426 SPECIFYING MULTIPLE REVISIONS
472 SPECIFYING MULTIPLE REVISIONS
427 -----------------------------
473 -----------------------------
428
474
429 When Mercurial accepts more than one revision, they may be
475 When Mercurial accepts more than one revision, they may be
430 specified individually, or provided as a continuous range,
476 specified individually, or provided as a continuous range,
431 separated by the ":" character.
477 separated by the ":" character.
432
478
433 The syntax of range notation is [BEGIN]:[END], where BEGIN and END
479 The syntax of range notation is [BEGIN]:[END], where BEGIN and END
434 are revision identifiers. Both BEGIN and END are optional. If
480 are revision identifiers. Both BEGIN and END are optional. If
435 BEGIN is not specified, it defaults to revision number 0. If END
481 BEGIN is not specified, it defaults to revision number 0. If END
436 is not specified, it defaults to the tip. The range ":" thus
482 is not specified, it defaults to the tip. The range ":" thus
437 means "all revisions".
483 means "all revisions".
438
484
439 If BEGIN is greater than END, revisions are treated in reverse
485 If BEGIN is greater than END, revisions are treated in reverse
440 order.
486 order.
441
487
442 A range acts as an open interval. This means that a range of 3:5
488 A range acts as an open interval. This means that a range of 3:5
443 gives 3, 4 and 5. Similarly, a range of 4:2 gives 4, 3, and 2.
489 gives 3, 4 and 5. Similarly, a range of 4:2 gives 4, 3, and 2.
444
490
445 ENVIRONMENT VARIABLES
491 ENVIRONMENT VARIABLES
446 ---------------------
492 ---------------------
447
493
448 HGEDITOR::
494 HGEDITOR::
449 This is the name of the editor to use when committing. Defaults to the
495 This is the name of the editor to use when committing. Defaults to the
450 value of EDITOR.
496 value of EDITOR.
451
497
452 (deprecated, use .hgrc)
498 (deprecated, use .hgrc)
453
499
454 HGMERGE::
500 HGMERGE::
455 An executable to use for resolving merge conflicts. The program
501 An executable to use for resolving merge conflicts. The program
456 will be executed with three arguments: local file, remote file,
502 will be executed with three arguments: local file, remote file,
457 ancestor file.
503 ancestor file.
458
504
459 The default program is "hgmerge", which is a shell script provided
505 The default program is "hgmerge", which is a shell script provided
460 by Mercurial with some sensible defaults.
506 by Mercurial with some sensible defaults.
461
507
462 (deprecated, use .hgrc)
508 (deprecated, use .hgrc)
463
509
464 HGUSER::
510 HGUSER::
465 This is the string used for the author of a commit.
511 This is the string used for the author of a commit.
466
512
467 (deprecated, use .hgrc)
513 (deprecated, use .hgrc)
468
514
469 EMAIL::
515 EMAIL::
470 If HGUSER is not set, this will be used as the author for a commit.
516 If HGUSER is not set, this will be used as the author for a commit.
471
517
472 LOGNAME::
518 LOGNAME::
473 If neither HGUSER nor EMAIL is set, LOGNAME will be used (with
519 If neither HGUSER nor EMAIL is set, LOGNAME will be used (with
474 '@hostname' appended) as the author value for a commit.
520 '@hostname' appended) as the author value for a commit.
475
521
476 EDITOR::
522 EDITOR::
477 This is the name of the editor used in the hgmerge script. It will be
523 This is the name of the editor used in the hgmerge script. It will be
478 used for commit messages if HGEDITOR isn't set. Defaults to 'vi'.
524 used for commit messages if HGEDITOR isn't set. Defaults to 'vi'.
479
525
480 PYTHONPATH::
526 PYTHONPATH::
481 This is used by Python to find imported modules and may need to be set
527 This is used by Python to find imported modules and may need to be set
482 appropriately if Mercurial is not installed system-wide.
528 appropriately if Mercurial is not installed system-wide.
483
529
484 FILES
530 FILES
485 -----
531 -----
486 .hgignore::
532 .hgignore::
487 This file contains regular expressions (one per line) that describe file
533 This file contains regular expressions (one per line) that describe file
488 names that should be ignored by hg.
534 names that should be ignored by hg.
489
535
490 .hgtags::
536 .hgtags::
491 This file contains changeset hash values and text tag names (one of each
537 This file contains changeset hash values and text tag names (one of each
492 seperated by spaces) that correspond to tagged versions of the repository
538 seperated by spaces) that correspond to tagged versions of the repository
493 contents.
539 contents.
494
540
495 $HOME/.hgrc, .hg/hgrc::
541 $HOME/.hgrc, .hg/hgrc::
496 This file contains defaults and configuration. Values in .hg/hgrc
542 This file contains defaults and configuration. Values in .hg/hgrc
497 override those in .hgrc. See hgrc(5) for details of the contents
543 override those in .hgrc. See hgrc(5) for details of the contents
498 and format of these files.
544 and format of these files.
499
545
500 BUGS
546 BUGS
501 ----
547 ----
502 Probably lots, please post them to the mailing list (See Resources below)
548 Probably lots, please post them to the mailing list (See Resources below)
503 when you find them.
549 when you find them.
504
550
505 SEE ALSO
551 SEE ALSO
506 --------
552 --------
507 hgrc(5)
553 hgrc(5)
508
554
509 AUTHOR
555 AUTHOR
510 ------
556 ------
511 Written by Matt Mackall <mpm@selenic.com>
557 Written by Matt Mackall <mpm@selenic.com>
512
558
513 RESOURCES
559 RESOURCES
514 ---------
560 ---------
515 http://selenic.com/mercurial[Main Web Site]
561 http://selenic.com/mercurial[Main Web Site]
516
562
517 http://www.serpentine.com/mercurial[Wiki site]
563 http://www.serpentine.com/mercurial[Wiki site]
518
564
519 http://selenic.com/hg[Source code repository]
565 http://selenic.com/hg[Source code repository]
520
566
521 http://selenic.com/mailman/listinfo/mercurial[Mailing list]
567 http://selenic.com/mailman/listinfo/mercurial[Mailing list]
522
568
523 COPYING
569 COPYING
524 -------
570 -------
525 Copyright (C) 2005 Matt Mackall.
571 Copyright (C) 2005 Matt Mackall.
526 Free use of this software is granted under the terms of the GNU General
572 Free use of this software is granted under the terms of the GNU General
527 Public License (GPL).
573 Public License (GPL).
@@ -1,1355 +1,1368 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 demandload(globals(), "os re sys signal shutil")
9 demandload(globals(), "os re sys signal shutil")
10 demandload(globals(), "fancyopts ui hg util")
10 demandload(globals(), "fancyopts ui hg util")
11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
12 demandload(globals(), "errno socket version struct")
12 demandload(globals(), "errno socket version struct")
13
13
14 class UnknownCommand(Exception):
14 class UnknownCommand(Exception):
15 """Exception raised if command is not in the command table."""
15 """Exception raised if command is not in the command table."""
16
16
17 def filterfiles(filters, files):
17 def filterfiles(filters, files):
18 l = [x for x in files if x in filters]
18 l = [x for x in files if x in filters]
19
19
20 for t in filters:
20 for t in filters:
21 if t and t[-1] != "/":
21 if t and t[-1] != "/":
22 t += "/"
22 t += "/"
23 l += [x for x in files if x.startswith(t)]
23 l += [x for x in files if x.startswith(t)]
24 return l
24 return l
25
25
26 def relfilter(repo, files):
26 def relfilter(repo, files):
27 cwd = repo.getcwd()
27 cwd = repo.getcwd()
28 if cwd:
28 if cwd:
29 return filterfiles([util.pconvert(cwd)], files)
29 return filterfiles([util.pconvert(cwd)], files)
30 return files
30 return files
31
31
32 def relpath(repo, args):
32 def relpath(repo, args):
33 cwd = repo.getcwd()
33 cwd = repo.getcwd()
34 if cwd:
34 if cwd:
35 return [util.pconvert(os.path.normpath(os.path.join(cwd, x)))
35 return [util.pconvert(os.path.normpath(os.path.join(cwd, x)))
36 for x in args]
36 for x in args]
37 return args
37 return args
38
38
39 def matchpats(ui, cwd, pats = [], opts = {}):
40 head = ''
41 if opts.get('rootless'): head = '(?:.*/|)'
42 def reify(name, tail):
43 if name.startswith('re:'):
44 return name[3:]
45 elif name.startswith('glob:'):
46 return head + util.globre(name[5:], '', tail)
47 elif name.startswith('path:'):
48 return '^' + re.escape(name[5:]) + '$'
49 return head + util.globre(name, '', tail)
50 cwdsep = cwd + os.sep
51 def under(fn):
52 if not cwd or fn.startswith(cwdsep): return True
53 def matchfn(pats, tail, ifempty = util.always):
54 if not pats: return ifempty
55 pat = '(?:%s)' % '|'.join([reify(p, tail) for p in pats])
56 if cwd: pat = re.escape(cwd + os.sep) + pat
57 ui.debug('regexp: %s\n' % pat)
58 return re.compile(pat).match
59 patmatch = matchfn(pats, '$')
60 incmatch = matchfn(opts.get('include'), '(?:/|$)', under)
61 excmatch = matchfn(opts.get('exclude'), '(?:/|$)', util.never)
62 return lambda fn: (incmatch(fn) and not excmatch(fn) and
63 (fn.endswith('/') or patmatch(fn)))
64
65 def walk(repo, pats, opts):
66 cwd = repo.getcwd()
67 if cwd: c = len(cwd) + 1
68 for fn in repo.walk(match = matchpats(repo.ui, cwd, pats, opts)):
69 if cwd: yield fn, fn[c:]
70 else: yield fn, fn
71
39 revrangesep = ':'
72 revrangesep = ':'
40
73
41 def revrange(ui, repo, revs, revlog=None):
74 def revrange(ui, repo, revs, revlog=None):
42 if revlog is None:
75 if revlog is None:
43 revlog = repo.changelog
76 revlog = repo.changelog
44 revcount = revlog.count()
77 revcount = revlog.count()
45 def fix(val, defval):
78 def fix(val, defval):
46 if not val:
79 if not val:
47 return defval
80 return defval
48 try:
81 try:
49 num = int(val)
82 num = int(val)
50 if str(num) != val:
83 if str(num) != val:
51 raise ValueError
84 raise ValueError
52 if num < 0:
85 if num < 0:
53 num += revcount
86 num += revcount
54 if not (0 <= num < revcount):
87 if not (0 <= num < revcount):
55 raise ValueError
88 raise ValueError
56 except ValueError:
89 except ValueError:
57 try:
90 try:
58 num = repo.changelog.rev(repo.lookup(val))
91 num = repo.changelog.rev(repo.lookup(val))
59 except KeyError:
92 except KeyError:
60 try:
93 try:
61 num = revlog.rev(revlog.lookup(val))
94 num = revlog.rev(revlog.lookup(val))
62 except KeyError:
95 except KeyError:
63 ui.warn('abort: invalid revision identifier %s\n' % val)
96 ui.warn('abort: invalid revision identifier %s\n' % val)
64 sys.exit(1)
97 sys.exit(1)
65 return num
98 return num
66 for spec in revs:
99 for spec in revs:
67 if spec.find(revrangesep) >= 0:
100 if spec.find(revrangesep) >= 0:
68 start, end = spec.split(revrangesep, 1)
101 start, end = spec.split(revrangesep, 1)
69 start = fix(start, 0)
102 start = fix(start, 0)
70 end = fix(end, revcount - 1)
103 end = fix(end, revcount - 1)
71 if end > start:
104 if end > start:
72 end += 1
105 end += 1
73 step = 1
106 step = 1
74 else:
107 else:
75 end -= 1
108 end -= 1
76 step = -1
109 step = -1
77 for rev in xrange(start, end, step):
110 for rev in xrange(start, end, step):
78 yield str(rev)
111 yield str(rev)
79 else:
112 else:
80 yield spec
113 yield spec
81
114
82 def make_filename(repo, r, pat, node=None,
115 def make_filename(repo, r, pat, node=None,
83 total=None, seqno=None, revwidth=None):
116 total=None, seqno=None, revwidth=None):
84 node_expander = {
117 node_expander = {
85 'H': lambda: hg.hex(node),
118 'H': lambda: hg.hex(node),
86 'R': lambda: str(r.rev(node)),
119 'R': lambda: str(r.rev(node)),
87 'h': lambda: hg.short(node),
120 'h': lambda: hg.short(node),
88 }
121 }
89 expander = {
122 expander = {
90 '%': lambda: '%',
123 '%': lambda: '%',
91 'b': lambda: os.path.basename(repo.root),
124 'b': lambda: os.path.basename(repo.root),
92 }
125 }
93
126
94 if node:
127 if node:
95 expander.update(node_expander)
128 expander.update(node_expander)
96 if node and revwidth is not None:
129 if node and revwidth is not None:
97 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
130 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
98 if total is not None:
131 if total is not None:
99 expander['N'] = lambda: str(total)
132 expander['N'] = lambda: str(total)
100 if seqno is not None:
133 if seqno is not None:
101 expander['n'] = lambda: str(seqno)
134 expander['n'] = lambda: str(seqno)
102 if total is not None and seqno is not None:
135 if total is not None and seqno is not None:
103 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
136 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
104
137
105 newname = []
138 newname = []
106 patlen = len(pat)
139 patlen = len(pat)
107 i = 0
140 i = 0
108 while i < patlen:
141 while i < patlen:
109 c = pat[i]
142 c = pat[i]
110 if c == '%':
143 if c == '%':
111 i += 1
144 i += 1
112 c = pat[i]
145 c = pat[i]
113 c = expander[c]()
146 c = expander[c]()
114 newname.append(c)
147 newname.append(c)
115 i += 1
148 i += 1
116 return ''.join(newname)
149 return ''.join(newname)
117
150
118 def dodiff(fp, ui, repo, files=None, node1=None, node2=None):
151 def dodiff(fp, ui, repo, files=None, node1=None, node2=None):
119 def date(c):
152 def date(c):
120 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
153 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
121
154
122 (c, a, d, u) = repo.changes(node1, node2, files)
155 (c, a, d, u) = repo.changes(node1, node2, files)
123 if files:
156 if files:
124 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
157 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
125
158
126 if not c and not a and not d:
159 if not c and not a and not d:
127 return
160 return
128
161
129 if node2:
162 if node2:
130 change = repo.changelog.read(node2)
163 change = repo.changelog.read(node2)
131 mmap2 = repo.manifest.read(change[0])
164 mmap2 = repo.manifest.read(change[0])
132 date2 = date(change)
165 date2 = date(change)
133 def read(f):
166 def read(f):
134 return repo.file(f).read(mmap2[f])
167 return repo.file(f).read(mmap2[f])
135 else:
168 else:
136 date2 = time.asctime()
169 date2 = time.asctime()
137 if not node1:
170 if not node1:
138 node1 = repo.dirstate.parents()[0]
171 node1 = repo.dirstate.parents()[0]
139 def read(f):
172 def read(f):
140 return repo.wfile(f).read()
173 return repo.wfile(f).read()
141
174
142 if ui.quiet:
175 if ui.quiet:
143 r = None
176 r = None
144 else:
177 else:
145 hexfunc = ui.verbose and hg.hex or hg.short
178 hexfunc = ui.verbose and hg.hex or hg.short
146 r = [hexfunc(node) for node in [node1, node2] if node]
179 r = [hexfunc(node) for node in [node1, node2] if node]
147
180
148 change = repo.changelog.read(node1)
181 change = repo.changelog.read(node1)
149 mmap = repo.manifest.read(change[0])
182 mmap = repo.manifest.read(change[0])
150 date1 = date(change)
183 date1 = date(change)
151
184
152 for f in c:
185 for f in c:
153 to = None
186 to = None
154 if f in mmap:
187 if f in mmap:
155 to = repo.file(f).read(mmap[f])
188 to = repo.file(f).read(mmap[f])
156 tn = read(f)
189 tn = read(f)
157 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
190 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
158 for f in a:
191 for f in a:
159 to = None
192 to = None
160 tn = read(f)
193 tn = read(f)
161 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
194 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
162 for f in d:
195 for f in d:
163 to = repo.file(f).read(mmap[f])
196 to = repo.file(f).read(mmap[f])
164 tn = None
197 tn = None
165 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
198 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
166
199
167 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
200 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
168 """show a single changeset or file revision"""
201 """show a single changeset or file revision"""
169 changelog = repo.changelog
202 changelog = repo.changelog
170 if filelog:
203 if filelog:
171 log = filelog
204 log = filelog
172 filerev = rev
205 filerev = rev
173 node = filenode = filelog.node(filerev)
206 node = filenode = filelog.node(filerev)
174 changerev = filelog.linkrev(filenode)
207 changerev = filelog.linkrev(filenode)
175 changenode = changenode or changelog.node(changerev)
208 changenode = changenode or changelog.node(changerev)
176 else:
209 else:
177 log = changelog
210 log = changelog
178 changerev = rev
211 changerev = rev
179 if changenode is None:
212 if changenode is None:
180 changenode = changelog.node(changerev)
213 changenode = changelog.node(changerev)
181 elif not changerev:
214 elif not changerev:
182 rev = changerev = changelog.rev(changenode)
215 rev = changerev = changelog.rev(changenode)
183 node = changenode
216 node = changenode
184
217
185 if ui.quiet:
218 if ui.quiet:
186 ui.write("%d:%s\n" % (rev, hg.hex(node)))
219 ui.write("%d:%s\n" % (rev, hg.hex(node)))
187 return
220 return
188
221
189 changes = changelog.read(changenode)
222 changes = changelog.read(changenode)
190
223
191 parents = [(log.rev(parent), hg.hex(parent))
224 parents = [(log.rev(parent), hg.hex(parent))
192 for parent in log.parents(node)
225 for parent in log.parents(node)
193 if ui.debugflag or parent != hg.nullid]
226 if ui.debugflag or parent != hg.nullid]
194 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
227 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
195 parents = []
228 parents = []
196
229
197 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
230 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
198 for tag in repo.nodetags(changenode):
231 for tag in repo.nodetags(changenode):
199 ui.status("tag: %s\n" % tag)
232 ui.status("tag: %s\n" % tag)
200 for parent in parents:
233 for parent in parents:
201 ui.write("parent: %d:%s\n" % parent)
234 ui.write("parent: %d:%s\n" % parent)
202 if filelog:
235 if filelog:
203 ui.debug("file rev: %d:%s\n" % (filerev, hg.hex(filenode)))
236 ui.debug("file rev: %d:%s\n" % (filerev, hg.hex(filenode)))
204 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
237 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
205 hg.hex(changes[0])))
238 hg.hex(changes[0])))
206 ui.status("user: %s\n" % changes[1])
239 ui.status("user: %s\n" % changes[1])
207 ui.status("date: %s\n" % time.asctime(
240 ui.status("date: %s\n" % time.asctime(
208 time.localtime(float(changes[2].split(' ')[0]))))
241 time.localtime(float(changes[2].split(' ')[0]))))
209 if ui.debugflag:
242 if ui.debugflag:
210 files = repo.changes(changelog.parents(changenode)[0], changenode)
243 files = repo.changes(changelog.parents(changenode)[0], changenode)
211 for key, value in zip(["files:", "files+:", "files-:"], files):
244 for key, value in zip(["files:", "files+:", "files-:"], files):
212 if value:
245 if value:
213 ui.note("%-12s %s\n" % (key, " ".join(value)))
246 ui.note("%-12s %s\n" % (key, " ".join(value)))
214 else:
247 else:
215 ui.note("files: %s\n" % " ".join(changes[3]))
248 ui.note("files: %s\n" % " ".join(changes[3]))
216 description = changes[4].strip()
249 description = changes[4].strip()
217 if description:
250 if description:
218 if ui.verbose:
251 if ui.verbose:
219 ui.status("description:\n")
252 ui.status("description:\n")
220 ui.status(description)
253 ui.status(description)
221 ui.status("\n\n")
254 ui.status("\n\n")
222 else:
255 else:
223 ui.status("summary: %s\n" % description.splitlines()[0])
256 ui.status("summary: %s\n" % description.splitlines()[0])
224 ui.status("\n")
257 ui.status("\n")
225
258
226 def show_version(ui):
259 def show_version(ui):
227 """output version and copyright information"""
260 """output version and copyright information"""
228 ui.write("Mercurial version %s\n" % version.get_version())
261 ui.write("Mercurial version %s\n" % version.get_version())
229 ui.status(
262 ui.status(
230 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
263 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
231 "This is free software; see the source for copying conditions. "
264 "This is free software; see the source for copying conditions. "
232 "There is NO\nwarranty; "
265 "There is NO\nwarranty; "
233 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
266 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
234 )
267 )
235
268
236 def help_(ui, cmd=None):
269 def help_(ui, cmd=None):
237 """show help for a given command or all commands"""
270 """show help for a given command or all commands"""
238 if cmd:
271 if cmd:
239 try:
272 try:
240 i = find(cmd)
273 i = find(cmd)
241 ui.write("%s\n\n" % i[2])
274 ui.write("%s\n\n" % i[2])
242
275
243 if i[1]:
276 if i[1]:
244 for s, l, d, c in i[1]:
277 for s, l, d, c in i[1]:
245 opt = ' '
278 opt = ' '
246 if s:
279 if s:
247 opt = opt + '-' + s + ' '
280 opt = opt + '-' + s + ' '
248 if l:
281 if l:
249 opt = opt + '--' + l + ' '
282 opt = opt + '--' + l + ' '
250 if d:
283 if d:
251 opt = opt + '(' + str(d) + ')'
284 opt = opt + '(' + str(d) + ')'
252 ui.write(opt, "\n")
285 ui.write(opt, "\n")
253 if c:
286 if c:
254 ui.write(' %s\n' % c)
287 ui.write(' %s\n' % c)
255 ui.write("\n")
288 ui.write("\n")
256
289
257 ui.write(i[0].__doc__, "\n")
290 ui.write(i[0].__doc__, "\n")
258 except UnknownCommand:
291 except UnknownCommand:
259 ui.warn("hg: unknown command %s\n" % cmd)
292 ui.warn("hg: unknown command %s\n" % cmd)
260 sys.exit(0)
293 sys.exit(0)
261 else:
294 else:
262 if ui.verbose:
295 if ui.verbose:
263 show_version(ui)
296 show_version(ui)
264 ui.write('\n')
297 ui.write('\n')
265 if ui.verbose:
298 if ui.verbose:
266 ui.write('hg commands:\n\n')
299 ui.write('hg commands:\n\n')
267 else:
300 else:
268 ui.write('basic hg commands (use "hg help -v" for more):\n\n')
301 ui.write('basic hg commands (use "hg help -v" for more):\n\n')
269
302
270 h = {}
303 h = {}
271 for c, e in table.items():
304 for c, e in table.items():
272 f = c.split("|")[0]
305 f = c.split("|")[0]
273 if not ui.verbose and not f.startswith("^"):
306 if not ui.verbose and not f.startswith("^"):
274 continue
307 continue
275 if not ui.debugflag and f.startswith("debug"):
308 if not ui.debugflag and f.startswith("debug"):
276 continue
309 continue
277 f = f.lstrip("^")
310 f = f.lstrip("^")
278 d = ""
311 d = ""
279 if e[0].__doc__:
312 if e[0].__doc__:
280 d = e[0].__doc__.splitlines(0)[0].rstrip()
313 d = e[0].__doc__.splitlines(0)[0].rstrip()
281 h[f] = d
314 h[f] = d
282
315
283 fns = h.keys()
316 fns = h.keys()
284 fns.sort()
317 fns.sort()
285 m = max(map(len, fns))
318 m = max(map(len, fns))
286 for f in fns:
319 for f in fns:
287 ui.write(' %-*s %s\n' % (m, f, h[f]))
320 ui.write(' %-*s %s\n' % (m, f, h[f]))
288
321
289 # Commands start here, listed alphabetically
322 # Commands start here, listed alphabetically
290
323
291 def add(ui, repo, file1, *files):
324 def add(ui, repo, *pats, **opts):
292 '''add the specified files on the next commit'''
325 '''add the specified files on the next commit'''
293 repo.add(relpath(repo, (file1,) + files))
326 names = []
327 q = dict(zip(pats, pats))
328 for abs, rel in walk(repo, pats, opts):
329 if rel in q or abs in q:
330 names.append(abs)
331 elif repo.dirstate.state(abs) == '?':
332 ui.status('adding %s\n' % rel)
333 names.append(abs)
334 repo.add(names)
294
335
295 def addremove(ui, repo, *files):
336 def addremove(ui, repo, *files):
296 """add all new files, delete all missing files"""
337 """add all new files, delete all missing files"""
297 if files:
338 if files:
298 files = relpath(repo, files)
339 files = relpath(repo, files)
299 d = []
340 d = []
300 u = []
341 u = []
301 for f in files:
342 for f in files:
302 p = repo.wjoin(f)
343 p = repo.wjoin(f)
303 s = repo.dirstate.state(f)
344 s = repo.dirstate.state(f)
304 isfile = os.path.isfile(p)
345 isfile = os.path.isfile(p)
305 if s != 'r' and not isfile:
346 if s != 'r' and not isfile:
306 d.append(f)
347 d.append(f)
307 elif s not in 'nmai' and isfile:
348 elif s not in 'nmai' and isfile:
308 u.append(f)
349 u.append(f)
309 else:
350 else:
310 (c, a, d, u) = repo.changes()
351 (c, a, d, u) = repo.changes()
311 repo.add(u)
352 repo.add(u)
312 repo.remove(d)
353 repo.remove(d)
313
354
314 def annotate(u, repo, file1, *files, **ops):
355 def annotate(u, repo, file1, *files, **ops):
315 """show changeset information per file line"""
356 """show changeset information per file line"""
316 def getnode(rev):
357 def getnode(rev):
317 return hg.short(repo.changelog.node(rev))
358 return hg.short(repo.changelog.node(rev))
318
359
319 def getname(rev):
360 def getname(rev):
320 try:
361 try:
321 return bcache[rev]
362 return bcache[rev]
322 except KeyError:
363 except KeyError:
323 cl = repo.changelog.read(repo.changelog.node(rev))
364 cl = repo.changelog.read(repo.changelog.node(rev))
324 name = cl[1]
365 name = cl[1]
325 f = name.find('@')
366 f = name.find('@')
326 if f >= 0:
367 if f >= 0:
327 name = name[:f]
368 name = name[:f]
328 f = name.find('<')
369 f = name.find('<')
329 if f >= 0:
370 if f >= 0:
330 name = name[f+1:]
371 name = name[f+1:]
331 bcache[rev] = name
372 bcache[rev] = name
332 return name
373 return name
333
374
334 bcache = {}
375 bcache = {}
335 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
376 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
336 if not ops['user'] and not ops['changeset']:
377 if not ops['user'] and not ops['changeset']:
337 ops['number'] = 1
378 ops['number'] = 1
338
379
339 node = repo.dirstate.parents()[0]
380 node = repo.dirstate.parents()[0]
340 if ops['revision']:
381 if ops['revision']:
341 node = repo.changelog.lookup(ops['revision'])
382 node = repo.changelog.lookup(ops['revision'])
342 change = repo.changelog.read(node)
383 change = repo.changelog.read(node)
343 mmap = repo.manifest.read(change[0])
384 mmap = repo.manifest.read(change[0])
344 for f in relpath(repo, (file1,) + files):
385 for f in relpath(repo, (file1,) + files):
345 lines = repo.file(f).annotate(mmap[f])
386 lines = repo.file(f).annotate(mmap[f])
346 pieces = []
387 pieces = []
347
388
348 for o, f in opmap:
389 for o, f in opmap:
349 if ops[o]:
390 if ops[o]:
350 l = [f(n) for n, dummy in lines]
391 l = [f(n) for n, dummy in lines]
351 m = max(map(len, l))
392 m = max(map(len, l))
352 pieces.append(["%*s" % (m, x) for x in l])
393 pieces.append(["%*s" % (m, x) for x in l])
353
394
354 for p, l in zip(zip(*pieces), lines):
395 for p, l in zip(zip(*pieces), lines):
355 u.write(" ".join(p) + ": " + l[1])
396 u.write(" ".join(p) + ": " + l[1])
356
397
357 def cat(ui, repo, file1, rev=None, **opts):
398 def cat(ui, repo, file1, rev=None, **opts):
358 """output the latest or given revision of a file"""
399 """output the latest or given revision of a file"""
359 r = repo.file(relpath(repo, [file1])[0])
400 r = repo.file(relpath(repo, [file1])[0])
360 if rev:
401 if rev:
361 n = r.lookup(rev)
402 n = r.lookup(rev)
362 else:
403 else:
363 n = r.tip()
404 n = r.tip()
364 if opts['output'] and opts['output'] != '-':
405 if opts['output'] and opts['output'] != '-':
365 try:
406 try:
366 outname = make_filename(repo, r, opts['output'], node=n)
407 outname = make_filename(repo, r, opts['output'], node=n)
367 fp = open(outname, 'wb')
408 fp = open(outname, 'wb')
368 except KeyError, inst:
409 except KeyError, inst:
369 ui.warn("error: invlaid format spec '%%%s' in output file name\n" %
410 ui.warn("error: invlaid format spec '%%%s' in output file name\n" %
370 inst.args[0])
411 inst.args[0])
371 sys.exit(1);
412 sys.exit(1);
372 else:
413 else:
373 fp = sys.stdout
414 fp = sys.stdout
374 fp.write(r.read(n))
415 fp.write(r.read(n))
375
416
376 def clone(ui, source, dest=None, **opts):
417 def clone(ui, source, dest=None, **opts):
377 """make a copy of an existing repository"""
418 """make a copy of an existing repository"""
378 if dest is None:
419 if dest is None:
379 dest = os.path.basename(os.path.normpath(source))
420 dest = os.path.basename(os.path.normpath(source))
380
421
381 if os.path.exists(dest):
422 if os.path.exists(dest):
382 ui.warn("abort: destination '%s' already exists\n" % dest)
423 ui.warn("abort: destination '%s' already exists\n" % dest)
383 return 1
424 return 1
384
425
385 class Dircleanup:
426 class Dircleanup:
386 def __init__(self, dir_):
427 def __init__(self, dir_):
387 self.rmtree = shutil.rmtree
428 self.rmtree = shutil.rmtree
388 self.dir_ = dir_
429 self.dir_ = dir_
389 os.mkdir(dir_)
430 os.mkdir(dir_)
390 def close(self):
431 def close(self):
391 self.dir_ = None
432 self.dir_ = None
392 def __del__(self):
433 def __del__(self):
393 if self.dir_:
434 if self.dir_:
394 self.rmtree(self.dir_, True)
435 self.rmtree(self.dir_, True)
395
436
396 d = Dircleanup(dest)
437 d = Dircleanup(dest)
397 abspath = source
438 abspath = source
398 source = ui.expandpath(source)
439 source = ui.expandpath(source)
399 other = hg.repository(ui, source)
440 other = hg.repository(ui, source)
400
441
401 if other.dev() != -1:
442 if other.dev() != -1:
402 abspath = os.path.abspath(source)
443 abspath = os.path.abspath(source)
403 copyfile = (os.stat(dest).st_dev == other.dev()
444 copyfile = (os.stat(dest).st_dev == other.dev()
404 and getattr(os, 'link', None) or shutil.copy2)
445 and getattr(os, 'link', None) or shutil.copy2)
405 if copyfile is not shutil.copy2:
446 if copyfile is not shutil.copy2:
406 ui.note("cloning by hardlink\n")
447 ui.note("cloning by hardlink\n")
407 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
448 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
408 copyfile)
449 copyfile)
409 try:
450 try:
410 os.unlink(os.path.join(dest, ".hg", "dirstate"))
451 os.unlink(os.path.join(dest, ".hg", "dirstate"))
411 except IOError:
452 except IOError:
412 pass
453 pass
413
454
414 repo = hg.repository(ui, dest)
455 repo = hg.repository(ui, dest)
415
456
416 else:
457 else:
417 repo = hg.repository(ui, dest, create=1)
458 repo = hg.repository(ui, dest, create=1)
418 repo.pull(other)
459 repo.pull(other)
419
460
420 f = repo.opener("hgrc", "w")
461 f = repo.opener("hgrc", "w")
421 f.write("[paths]\n")
462 f.write("[paths]\n")
422 f.write("default = %s\n" % abspath)
463 f.write("default = %s\n" % abspath)
423
464
424 if not opts['noupdate']:
465 if not opts['noupdate']:
425 update(ui, repo)
466 update(ui, repo)
426
467
427 d.close()
468 d.close()
428
469
429 def commit(ui, repo, *files, **opts):
470 def commit(ui, repo, *files, **opts):
430 """commit the specified files or all outstanding changes"""
471 """commit the specified files or all outstanding changes"""
431 text = opts['text']
472 text = opts['text']
432 logfile = opts['logfile']
473 logfile = opts['logfile']
433 if not text and logfile:
474 if not text and logfile:
434 try:
475 try:
435 text = open(logfile).read()
476 text = open(logfile).read()
436 except IOError, why:
477 except IOError, why:
437 ui.warn("Can't read commit text %s: %s\n" % (logfile, why))
478 ui.warn("Can't read commit text %s: %s\n" % (logfile, why))
438
479
439 if opts['addremove']:
480 if opts['addremove']:
440 addremove(ui, repo, *files)
481 addremove(ui, repo, *files)
441 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
482 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
442
483
443 def copy(ui, repo, source, dest):
484 def copy(ui, repo, source, dest):
444 """mark a file as copied or renamed for the next commit"""
485 """mark a file as copied or renamed for the next commit"""
445 return repo.copy(*relpath(repo, (source, dest)))
486 return repo.copy(*relpath(repo, (source, dest)))
446
487
447 def debugcheckstate(ui, repo):
488 def debugcheckstate(ui, repo):
448 """validate the correctness of the current dirstate"""
489 """validate the correctness of the current dirstate"""
449 parent1, parent2 = repo.dirstate.parents()
490 parent1, parent2 = repo.dirstate.parents()
450 repo.dirstate.read()
491 repo.dirstate.read()
451 dc = repo.dirstate.map
492 dc = repo.dirstate.map
452 keys = dc.keys()
493 keys = dc.keys()
453 keys.sort()
494 keys.sort()
454 m1n = repo.changelog.read(parent1)[0]
495 m1n = repo.changelog.read(parent1)[0]
455 m2n = repo.changelog.read(parent2)[0]
496 m2n = repo.changelog.read(parent2)[0]
456 m1 = repo.manifest.read(m1n)
497 m1 = repo.manifest.read(m1n)
457 m2 = repo.manifest.read(m2n)
498 m2 = repo.manifest.read(m2n)
458 errors = 0
499 errors = 0
459 for f in dc:
500 for f in dc:
460 state = repo.dirstate.state(f)
501 state = repo.dirstate.state(f)
461 if state in "nr" and f not in m1:
502 if state in "nr" and f not in m1:
462 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
503 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
463 errors += 1
504 errors += 1
464 if state in "a" and f in m1:
505 if state in "a" and f in m1:
465 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
506 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
466 errors += 1
507 errors += 1
467 if state in "m" and f not in m1 and f not in m2:
508 if state in "m" and f not in m1 and f not in m2:
468 ui.warn("%s in state %s, but not in either manifest\n" %
509 ui.warn("%s in state %s, but not in either manifest\n" %
469 (f, state))
510 (f, state))
470 errors += 1
511 errors += 1
471 for f in m1:
512 for f in m1:
472 state = repo.dirstate.state(f)
513 state = repo.dirstate.state(f)
473 if state not in "nrm":
514 if state not in "nrm":
474 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
515 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
475 errors += 1
516 errors += 1
476 if errors:
517 if errors:
477 ui.warn(".hg/dirstate inconsistent with current parent's manifest\n")
518 ui.warn(".hg/dirstate inconsistent with current parent's manifest\n")
478 sys.exit(1)
519 sys.exit(1)
479
520
480 def debugstate(ui, repo):
521 def debugstate(ui, repo):
481 """show the contents of the current dirstate"""
522 """show the contents of the current dirstate"""
482 repo.dirstate.read()
523 repo.dirstate.read()
483 dc = repo.dirstate.map
524 dc = repo.dirstate.map
484 keys = dc.keys()
525 keys = dc.keys()
485 keys.sort()
526 keys.sort()
486 for file_ in keys:
527 for file_ in keys:
487 ui.write("%c %s\n" % (dc[file_][0], file_))
528 ui.write("%c %s\n" % (dc[file_][0], file_))
488
529
489 def debugindex(ui, file_):
530 def debugindex(ui, file_):
490 """dump the contents of an index file"""
531 """dump the contents of an index file"""
491 r = hg.revlog(hg.opener(""), file_, "")
532 r = hg.revlog(hg.opener(""), file_, "")
492 ui.write(" rev offset length base linkrev" +
533 ui.write(" rev offset length base linkrev" +
493 " p1 p2 nodeid\n")
534 " p1 p2 nodeid\n")
494 for i in range(r.count()):
535 for i in range(r.count()):
495 e = r.index[i]
536 e = r.index[i]
496 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
537 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
497 i, e[0], e[1], e[2], e[3],
538 i, e[0], e[1], e[2], e[3],
498 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
539 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
499
540
500 def debugindexdot(ui, file_):
541 def debugindexdot(ui, file_):
501 """dump an index DAG as a .dot file"""
542 """dump an index DAG as a .dot file"""
502 r = hg.revlog(hg.opener(""), file_, "")
543 r = hg.revlog(hg.opener(""), file_, "")
503 ui.write("digraph G {\n")
544 ui.write("digraph G {\n")
504 for i in range(r.count()):
545 for i in range(r.count()):
505 e = r.index[i]
546 e = r.index[i]
506 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
547 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
507 if e[5] != hg.nullid:
548 if e[5] != hg.nullid:
508 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
549 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
509 ui.write("}\n")
550 ui.write("}\n")
510
551
511 def diff(ui, repo, *files, **opts):
552 def diff(ui, repo, *files, **opts):
512 """diff working directory (or selected files)"""
553 """diff working directory (or selected files)"""
513 revs = []
554 revs = []
514 if opts['rev']:
555 if opts['rev']:
515 revs = map(lambda x: repo.lookup(x), opts['rev'])
556 revs = map(lambda x: repo.lookup(x), opts['rev'])
516
557
517 if len(revs) > 2:
558 if len(revs) > 2:
518 ui.warn("too many revisions to diff\n")
559 ui.warn("too many revisions to diff\n")
519 sys.exit(1)
560 sys.exit(1)
520
561
521 if files:
562 if files:
522 files = relpath(repo, files)
563 files = relpath(repo, files)
523 else:
564 else:
524 files = relpath(repo, [""])
565 files = relpath(repo, [""])
525
566
526 dodiff(sys.stdout, ui, repo, files, *revs)
567 dodiff(sys.stdout, ui, repo, files, *revs)
527
568
528 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
569 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
529 node = repo.lookup(changeset)
570 node = repo.lookup(changeset)
530 prev, other = repo.changelog.parents(node)
571 prev, other = repo.changelog.parents(node)
531 change = repo.changelog.read(node)
572 change = repo.changelog.read(node)
532
573
533 if opts['output'] and opts['output'] != '-':
574 if opts['output'] and opts['output'] != '-':
534 try:
575 try:
535 outname = make_filename(repo, repo.changelog, opts['output'],
576 outname = make_filename(repo, repo.changelog, opts['output'],
536 node=node, total=total, seqno=seqno,
577 node=node, total=total, seqno=seqno,
537 revwidth=revwidth)
578 revwidth=revwidth)
538 fp = open(outname, 'wb')
579 fp = open(outname, 'wb')
539 except KeyError, inst:
580 except KeyError, inst:
540 ui.warn("error: invalid format spec '%%%s' in output file name\n" %
581 ui.warn("error: invalid format spec '%%%s' in output file name\n" %
541 inst.args[0])
582 inst.args[0])
542 sys.exit(1)
583 sys.exit(1)
543 else:
584 else:
544 fp = sys.stdout
585 fp = sys.stdout
545
586
546 fp.write("# HG changeset patch\n")
587 fp.write("# HG changeset patch\n")
547 fp.write("# User %s\n" % change[1])
588 fp.write("# User %s\n" % change[1])
548 fp.write("# Node ID %s\n" % hg.hex(node))
589 fp.write("# Node ID %s\n" % hg.hex(node))
549 fp.write("# Parent %s\n" % hg.hex(prev))
590 fp.write("# Parent %s\n" % hg.hex(prev))
550 if other != hg.nullid:
591 if other != hg.nullid:
551 fp.write("# Parent %s\n" % hg.hex(other))
592 fp.write("# Parent %s\n" % hg.hex(other))
552 fp.write(change[4].rstrip())
593 fp.write(change[4].rstrip())
553 fp.write("\n\n")
594 fp.write("\n\n")
554
595
555 dodiff(fp, ui, repo, None, prev, node)
596 dodiff(fp, ui, repo, None, prev, node)
556
597
557 def export(ui, repo, *changesets, **opts):
598 def export(ui, repo, *changesets, **opts):
558 """dump the header and diffs for one or more changesets"""
599 """dump the header and diffs for one or more changesets"""
559 if not changesets:
600 if not changesets:
560 ui.warn("error: export requires at least one changeset\n")
601 ui.warn("error: export requires at least one changeset\n")
561 sys.exit(1)
602 sys.exit(1)
562 seqno = 0
603 seqno = 0
563 revs = list(revrange(ui, repo, changesets))
604 revs = list(revrange(ui, repo, changesets))
564 total = len(revs)
605 total = len(revs)
565 revwidth = max(len(revs[0]), len(revs[-1]))
606 revwidth = max(len(revs[0]), len(revs[-1]))
566 for cset in revs:
607 for cset in revs:
567 seqno += 1
608 seqno += 1
568 doexport(ui, repo, cset, seqno, total, revwidth, opts)
609 doexport(ui, repo, cset, seqno, total, revwidth, opts)
569
610
570 def forget(ui, repo, file1, *files):
611 def forget(ui, repo, file1, *files):
571 """don't add the specified files on the next commit"""
612 """don't add the specified files on the next commit"""
572 repo.forget(relpath(repo, (file1,) + files))
613 repo.forget(relpath(repo, (file1,) + files))
573
614
574 def heads(ui, repo):
615 def heads(ui, repo):
575 """show current repository heads"""
616 """show current repository heads"""
576 for n in repo.changelog.heads():
617 for n in repo.changelog.heads():
577 show_changeset(ui, repo, changenode=n)
618 show_changeset(ui, repo, changenode=n)
578
619
579 def identify(ui, repo):
620 def identify(ui, repo):
580 """print information about the working copy"""
621 """print information about the working copy"""
581 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
622 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
582 if not parents:
623 if not parents:
583 ui.write("unknown\n")
624 ui.write("unknown\n")
584 return
625 return
585
626
586 hexfunc = ui.verbose and hg.hex or hg.short
627 hexfunc = ui.verbose and hg.hex or hg.short
587 (c, a, d, u) = repo.changes()
628 (c, a, d, u) = repo.changes()
588 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
629 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
589 (c or a or d) and "+" or "")]
630 (c or a or d) and "+" or "")]
590
631
591 if not ui.quiet:
632 if not ui.quiet:
592 # multiple tags for a single parent separated by '/'
633 # multiple tags for a single parent separated by '/'
593 parenttags = ['/'.join(tags)
634 parenttags = ['/'.join(tags)
594 for tags in map(repo.nodetags, parents) if tags]
635 for tags in map(repo.nodetags, parents) if tags]
595 # tags for multiple parents separated by ' + '
636 # tags for multiple parents separated by ' + '
596 output.append(' + '.join(parenttags))
637 output.append(' + '.join(parenttags))
597
638
598 ui.write("%s\n" % ' '.join(output))
639 ui.write("%s\n" % ' '.join(output))
599
640
600 def import_(ui, repo, patch1, *patches, **opts):
641 def import_(ui, repo, patch1, *patches, **opts):
601 """import an ordered set of patches"""
642 """import an ordered set of patches"""
602 try:
643 try:
603 import psyco
644 import psyco
604 psyco.full()
645 psyco.full()
605 except ImportError:
646 except ImportError:
606 pass
647 pass
607
648
608 patches = (patch1,) + patches
649 patches = (patch1,) + patches
609
650
610 d = opts["base"]
651 d = opts["base"]
611 strip = opts["strip"]
652 strip = opts["strip"]
612
653
613 for patch in patches:
654 for patch in patches:
614 ui.status("applying %s\n" % patch)
655 ui.status("applying %s\n" % patch)
615 pf = os.path.join(d, patch)
656 pf = os.path.join(d, patch)
616
657
617 text = []
658 text = []
618 user = None
659 user = None
619 hgpatch = False
660 hgpatch = False
620 for line in file(pf):
661 for line in file(pf):
621 line = line.rstrip()
662 line = line.rstrip()
622 if line.startswith("--- ") or line.startswith("diff -r"):
663 if line.startswith("--- ") or line.startswith("diff -r"):
623 break
664 break
624 elif hgpatch:
665 elif hgpatch:
625 # parse values when importing the result of an hg export
666 # parse values when importing the result of an hg export
626 if line.startswith("# User "):
667 if line.startswith("# User "):
627 user = line[7:]
668 user = line[7:]
628 ui.debug('User: %s\n' % user)
669 ui.debug('User: %s\n' % user)
629 elif not line.startswith("# ") and line:
670 elif not line.startswith("# ") and line:
630 text.append(line)
671 text.append(line)
631 hgpatch = False
672 hgpatch = False
632 elif line == '# HG changeset patch':
673 elif line == '# HG changeset patch':
633 hgpatch = True
674 hgpatch = True
634 else:
675 else:
635 text.append(line)
676 text.append(line)
636
677
637 # make sure text isn't empty
678 # make sure text isn't empty
638 if not text:
679 if not text:
639 text = "imported patch %s\n" % patch
680 text = "imported patch %s\n" % patch
640 else:
681 else:
641 text = "%s\n" % '\n'.join(text)
682 text = "%s\n" % '\n'.join(text)
642 ui.debug('text:\n%s\n' % text)
683 ui.debug('text:\n%s\n' % text)
643
684
644 f = os.popen("patch -p%d < %s" % (strip, pf))
685 f = os.popen("patch -p%d < %s" % (strip, pf))
645 files = []
686 files = []
646 for l in f.read().splitlines():
687 for l in f.read().splitlines():
647 l.rstrip('\r\n');
688 l.rstrip('\r\n');
648 ui.status("%s\n" % l)
689 ui.status("%s\n" % l)
649 if l.startswith('patching file '):
690 if l.startswith('patching file '):
650 pf = l[14:]
691 pf = l[14:]
651 if pf not in files:
692 if pf not in files:
652 files.append(pf)
693 files.append(pf)
653 patcherr = f.close()
694 patcherr = f.close()
654 if patcherr:
695 if patcherr:
655 sys.stderr.write("patch failed")
696 sys.stderr.write("patch failed")
656 sys.exit(1)
697 sys.exit(1)
657
698
658 if len(files) > 0:
699 if len(files) > 0:
659 addremove(ui, repo, *files)
700 addremove(ui, repo, *files)
660 repo.commit(files, text, user)
701 repo.commit(files, text, user)
661
702
662 def init(ui, source=None):
703 def init(ui, source=None):
663 """create a new repository in the current directory"""
704 """create a new repository in the current directory"""
664
705
665 if source:
706 if source:
666 ui.warn("no longer supported: use \"hg clone\" instead\n")
707 ui.warn("no longer supported: use \"hg clone\" instead\n")
667 sys.exit(1)
708 sys.exit(1)
668 hg.repository(ui, ".", create=1)
709 hg.repository(ui, ".", create=1)
669
710
670 def locate(ui, repo, *pats, **opts):
711 def locate(ui, repo, *pats, **opts):
671 """locate files matching specific patterns"""
712 """locate files matching specific patterns"""
672 if [p for p in pats if os.sep in p]:
713 if opts['print0']: end = '\0'
673 ui.warn("error: patterns may not contain '%s'\n" % os.sep)
714 else: end = '\n'
674 ui.warn("use '-i <dir>' instead\n")
715 opts['rootless'] = True
675 sys.exit(1)
716 for abs, rel in walk(repo, pats, opts):
676 def compile(pats, head='^', tail=os.sep, on_empty=True):
717 if repo.dirstate.state(abs) == '?': continue
677 if not pats:
678 class c:
679 def match(self, x):
680 return on_empty
681 return c()
682 fnpats = [fnmatch.translate(os.path.normpath(os.path.normcase(p)))[:-1]
683 for p in pats]
684 regexp = r'%s(?:%s)%s' % (head, '|'.join(fnpats), tail)
685 return re.compile(regexp)
686 exclude = compile(opts['exclude'], on_empty=False)
687 include = compile(opts['include'])
688 pat = compile(pats, head='', tail='$')
689 end = opts['print0'] and '\0' or '\n'
690 if opts['rev']:
691 node = repo.manifest.lookup(opts['rev'])
692 else:
693 node = repo.manifest.tip()
694 manifest = repo.manifest.read(node)
695 cwd = repo.getcwd()
696 cwd_plus = cwd and (cwd + os.sep)
697 found = []
698 for f in manifest:
699 f = os.path.normcase(f)
700 if exclude.match(f) or not(include.match(f) and
701 f.startswith(cwd_plus) and
702 pat.match(os.path.basename(f))):
703 continue
704 if opts['fullpath']:
718 if opts['fullpath']:
705 f = os.path.join(repo.root, f)
719 ui.write(os.path.join(repo.root, abs), end)
706 elif cwd:
720 else:
707 f = f[len(cwd_plus):]
721 ui.write(rel, end)
708 found.append(f)
709 found.sort()
710 for f in found:
711 ui.write(f, end)
712
722
713 def log(ui, repo, f=None, **opts):
723 def log(ui, repo, f=None, **opts):
714 """show the revision history of the repository or a single file"""
724 """show the revision history of the repository or a single file"""
715 if f:
725 if f:
716 files = relpath(repo, [f])
726 files = relpath(repo, [f])
717 filelog = repo.file(files[0])
727 filelog = repo.file(files[0])
718 log = filelog
728 log = filelog
719 lookup = filelog.lookup
729 lookup = filelog.lookup
720 else:
730 else:
721 files = None
731 files = None
722 filelog = None
732 filelog = None
723 log = repo.changelog
733 log = repo.changelog
724 lookup = repo.lookup
734 lookup = repo.lookup
725 revlist = []
735 revlist = []
726 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
736 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
727 while revs:
737 while revs:
728 if len(revs) == 1:
738 if len(revs) == 1:
729 revlist.append(revs.pop(0))
739 revlist.append(revs.pop(0))
730 else:
740 else:
731 a = revs.pop(0)
741 a = revs.pop(0)
732 b = revs.pop(0)
742 b = revs.pop(0)
733 off = a > b and -1 or 1
743 off = a > b and -1 or 1
734 revlist.extend(range(a, b + off, off))
744 revlist.extend(range(a, b + off, off))
735
745
736 for i in revlist or range(log.count() - 1, -1, -1):
746 for i in revlist or range(log.count() - 1, -1, -1):
737 show_changeset(ui, repo, filelog=filelog, rev=i)
747 show_changeset(ui, repo, filelog=filelog, rev=i)
738 if opts['patch']:
748 if opts['patch']:
739 if filelog:
749 if filelog:
740 filenode = filelog.node(i)
750 filenode = filelog.node(i)
741 i = filelog.linkrev(filenode)
751 i = filelog.linkrev(filenode)
742 changenode = repo.changelog.node(i)
752 changenode = repo.changelog.node(i)
743 prev, other = repo.changelog.parents(changenode)
753 prev, other = repo.changelog.parents(changenode)
744 dodiff(sys.stdout, ui, repo, files, prev, changenode)
754 dodiff(sys.stdout, ui, repo, files, prev, changenode)
745 ui.write("\n\n")
755 ui.write("\n\n")
746
756
747 def manifest(ui, repo, rev=None):
757 def manifest(ui, repo, rev=None):
748 """output the latest or given revision of the project manifest"""
758 """output the latest or given revision of the project manifest"""
749 if rev:
759 if rev:
750 try:
760 try:
751 # assume all revision numbers are for changesets
761 # assume all revision numbers are for changesets
752 n = repo.lookup(rev)
762 n = repo.lookup(rev)
753 change = repo.changelog.read(n)
763 change = repo.changelog.read(n)
754 n = change[0]
764 n = change[0]
755 except hg.RepoError:
765 except hg.RepoError:
756 n = repo.manifest.lookup(rev)
766 n = repo.manifest.lookup(rev)
757 else:
767 else:
758 n = repo.manifest.tip()
768 n = repo.manifest.tip()
759 m = repo.manifest.read(n)
769 m = repo.manifest.read(n)
760 mf = repo.manifest.readflags(n)
770 mf = repo.manifest.readflags(n)
761 files = m.keys()
771 files = m.keys()
762 files.sort()
772 files.sort()
763
773
764 for f in files:
774 for f in files:
765 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
775 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
766
776
767 def parents(ui, repo, node=None):
777 def parents(ui, repo, node=None):
768 '''show the parents of the current working dir'''
778 '''show the parents of the current working dir'''
769 if node:
779 if node:
770 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
780 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
771 else:
781 else:
772 p = repo.dirstate.parents()
782 p = repo.dirstate.parents()
773
783
774 for n in p:
784 for n in p:
775 if n != hg.nullid:
785 if n != hg.nullid:
776 show_changeset(ui, repo, changenode=n)
786 show_changeset(ui, repo, changenode=n)
777
787
778 def pull(ui, repo, source="default", **opts):
788 def pull(ui, repo, source="default", **opts):
779 """pull changes from the specified source"""
789 """pull changes from the specified source"""
780 source = ui.expandpath(source)
790 source = ui.expandpath(source)
781 ui.status('pulling from %s\n' % (source))
791 ui.status('pulling from %s\n' % (source))
782
792
783 other = hg.repository(ui, source)
793 other = hg.repository(ui, source)
784 r = repo.pull(other)
794 r = repo.pull(other)
785 if not r:
795 if not r:
786 if opts['update']:
796 if opts['update']:
787 return update(ui, repo)
797 return update(ui, repo)
788 else:
798 else:
789 ui.status("(run 'hg update' to get a working copy)\n")
799 ui.status("(run 'hg update' to get a working copy)\n")
790
800
791 return r
801 return r
792
802
793 def push(ui, repo, dest="default-push"):
803 def push(ui, repo, dest="default-push"):
794 """push changes to the specified destination"""
804 """push changes to the specified destination"""
795 dest = ui.expandpath(dest)
805 dest = ui.expandpath(dest)
796 ui.status('pushing to %s\n' % (dest))
806 ui.status('pushing to %s\n' % (dest))
797
807
798 other = hg.repository(ui, dest)
808 other = hg.repository(ui, dest)
799 r = repo.push(other)
809 r = repo.push(other)
800 return r
810 return r
801
811
802 def rawcommit(ui, repo, *flist, **rc):
812 def rawcommit(ui, repo, *flist, **rc):
803 "raw commit interface"
813 "raw commit interface"
804
814
805 text = rc['text']
815 text = rc['text']
806 if not text and rc['logfile']:
816 if not text and rc['logfile']:
807 try:
817 try:
808 text = open(rc['logfile']).read()
818 text = open(rc['logfile']).read()
809 except IOError:
819 except IOError:
810 pass
820 pass
811 if not text and not rc['logfile']:
821 if not text and not rc['logfile']:
812 ui.warn("abort: missing commit text\n")
822 ui.warn("abort: missing commit text\n")
813 return 1
823 return 1
814
824
815 files = relpath(repo, list(flist))
825 files = relpath(repo, list(flist))
816 if rc['files']:
826 if rc['files']:
817 files += open(rc['files']).read().splitlines()
827 files += open(rc['files']).read().splitlines()
818
828
819 rc['parent'] = map(repo.lookup, rc['parent'])
829 rc['parent'] = map(repo.lookup, rc['parent'])
820
830
821 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
831 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
822
832
823 def recover(ui, repo):
833 def recover(ui, repo):
824 """roll back an interrupted transaction"""
834 """roll back an interrupted transaction"""
825 repo.recover()
835 repo.recover()
826
836
827 def remove(ui, repo, file1, *files):
837 def remove(ui, repo, file1, *files):
828 """remove the specified files on the next commit"""
838 """remove the specified files on the next commit"""
829 repo.remove(relpath(repo, (file1,) + files))
839 repo.remove(relpath(repo, (file1,) + files))
830
840
831 def revert(ui, repo, *names, **opts):
841 def revert(ui, repo, *names, **opts):
832 """revert modified files or dirs back to their unmodified states"""
842 """revert modified files or dirs back to their unmodified states"""
833 node = opts['rev'] and repo.lookup(opts['rev']) or \
843 node = opts['rev'] and repo.lookup(opts['rev']) or \
834 repo.dirstate.parents()[0]
844 repo.dirstate.parents()[0]
835 root = os.path.realpath(repo.root)
845 root = os.path.realpath(repo.root)
836
846
837 def trimpath(p):
847 def trimpath(p):
838 p = os.path.realpath(p)
848 p = os.path.realpath(p)
839 if p.startswith(root):
849 if p.startswith(root):
840 rest = p[len(root):]
850 rest = p[len(root):]
841 if not rest:
851 if not rest:
842 return rest
852 return rest
843 if p.startswith(os.sep):
853 if p.startswith(os.sep):
844 return rest[1:]
854 return rest[1:]
845 return p
855 return p
846
856
847 relnames = map(trimpath, names or [os.getcwd()])
857 relnames = map(trimpath, names or [os.getcwd()])
848 chosen = {}
858 chosen = {}
849
859
850 def choose(name):
860 def choose(name):
851 def body(name):
861 def body(name):
852 for r in relnames:
862 for r in relnames:
853 if not name.startswith(r):
863 if not name.startswith(r):
854 continue
864 continue
855 rest = name[len(r):]
865 rest = name[len(r):]
856 if not rest:
866 if not rest:
857 return r, True
867 return r, True
858 depth = rest.count(os.sep)
868 depth = rest.count(os.sep)
859 if not r:
869 if not r:
860 if depth == 0 or not opts['nonrecursive']:
870 if depth == 0 or not opts['nonrecursive']:
861 return r, True
871 return r, True
862 elif rest[0] == os.sep:
872 elif rest[0] == os.sep:
863 if depth == 1 or not opts['nonrecursive']:
873 if depth == 1 or not opts['nonrecursive']:
864 return r, True
874 return r, True
865 return None, False
875 return None, False
866 relname, ret = body(name)
876 relname, ret = body(name)
867 if ret:
877 if ret:
868 chosen[relname] = 1
878 chosen[relname] = 1
869 return ret
879 return ret
870
880
871 r = repo.update(node, False, True, choose, False)
881 r = repo.update(node, False, True, choose, False)
872 for n in relnames:
882 for n in relnames:
873 if n not in chosen:
883 if n not in chosen:
874 ui.warn('error: no matches for %s\n' % n)
884 ui.warn('error: no matches for %s\n' % n)
875 r = 1
885 r = 1
876 sys.stdout.flush()
886 sys.stdout.flush()
877 return r
887 return r
878
888
879 def root(ui, repo):
889 def root(ui, repo):
880 """print the root (top) of the current working dir"""
890 """print the root (top) of the current working dir"""
881 ui.write(repo.root + "\n")
891 ui.write(repo.root + "\n")
882
892
883 def serve(ui, repo, **opts):
893 def serve(ui, repo, **opts):
884 """export the repository via HTTP"""
894 """export the repository via HTTP"""
885
895
886 if opts["stdio"]:
896 if opts["stdio"]:
887 fin, fout = sys.stdin, sys.stdout
897 fin, fout = sys.stdin, sys.stdout
888 sys.stdout = sys.stderr
898 sys.stdout = sys.stderr
889
899
890 def getarg():
900 def getarg():
891 argline = fin.readline()[:-1]
901 argline = fin.readline()[:-1]
892 arg, l = argline.split()
902 arg, l = argline.split()
893 val = fin.read(int(l))
903 val = fin.read(int(l))
894 return arg, val
904 return arg, val
895 def respond(v):
905 def respond(v):
896 fout.write("%d\n" % len(v))
906 fout.write("%d\n" % len(v))
897 fout.write(v)
907 fout.write(v)
898 fout.flush()
908 fout.flush()
899
909
900 lock = None
910 lock = None
901
911
902 while 1:
912 while 1:
903 cmd = fin.readline()[:-1]
913 cmd = fin.readline()[:-1]
904 if cmd == '':
914 if cmd == '':
905 return
915 return
906 if cmd == "heads":
916 if cmd == "heads":
907 h = repo.heads()
917 h = repo.heads()
908 respond(" ".join(map(hg.hex, h)) + "\n")
918 respond(" ".join(map(hg.hex, h)) + "\n")
909 if cmd == "lock":
919 if cmd == "lock":
910 lock = repo.lock()
920 lock = repo.lock()
911 respond("")
921 respond("")
912 if cmd == "unlock":
922 if cmd == "unlock":
913 if lock:
923 if lock:
914 lock.release()
924 lock.release()
915 lock = None
925 lock = None
916 respond("")
926 respond("")
917 elif cmd == "branches":
927 elif cmd == "branches":
918 arg, nodes = getarg()
928 arg, nodes = getarg()
919 nodes = map(hg.bin, nodes.split(" "))
929 nodes = map(hg.bin, nodes.split(" "))
920 r = []
930 r = []
921 for b in repo.branches(nodes):
931 for b in repo.branches(nodes):
922 r.append(" ".join(map(hg.hex, b)) + "\n")
932 r.append(" ".join(map(hg.hex, b)) + "\n")
923 respond("".join(r))
933 respond("".join(r))
924 elif cmd == "between":
934 elif cmd == "between":
925 arg, pairs = getarg()
935 arg, pairs = getarg()
926 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
936 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
927 r = []
937 r = []
928 for b in repo.between(pairs):
938 for b in repo.between(pairs):
929 r.append(" ".join(map(hg.hex, b)) + "\n")
939 r.append(" ".join(map(hg.hex, b)) + "\n")
930 respond("".join(r))
940 respond("".join(r))
931 elif cmd == "changegroup":
941 elif cmd == "changegroup":
932 nodes = []
942 nodes = []
933 arg, roots = getarg()
943 arg, roots = getarg()
934 nodes = map(hg.bin, roots.split(" "))
944 nodes = map(hg.bin, roots.split(" "))
935
945
936 cg = repo.changegroup(nodes)
946 cg = repo.changegroup(nodes)
937 while 1:
947 while 1:
938 d = cg.read(4096)
948 d = cg.read(4096)
939 if not d:
949 if not d:
940 break
950 break
941 fout.write(d)
951 fout.write(d)
942
952
943 fout.flush()
953 fout.flush()
944
954
945 elif cmd == "addchangegroup":
955 elif cmd == "addchangegroup":
946 if not lock:
956 if not lock:
947 respond("not locked")
957 respond("not locked")
948 continue
958 continue
949 respond("")
959 respond("")
950
960
951 r = repo.addchangegroup(fin)
961 r = repo.addchangegroup(fin)
952 respond("")
962 respond("")
953
963
954 def openlog(opt, default):
964 def openlog(opt, default):
955 if opts[opt] and opts[opt] != '-':
965 if opts[opt] and opts[opt] != '-':
956 return open(opts[opt], 'w')
966 return open(opts[opt], 'w')
957 else:
967 else:
958 return default
968 return default
959
969
960 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
970 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
961 opts["address"], opts["port"],
971 opts["address"], opts["port"],
962 openlog('accesslog', sys.stdout),
972 openlog('accesslog', sys.stdout),
963 openlog('errorlog', sys.stderr))
973 openlog('errorlog', sys.stderr))
964 if ui.verbose:
974 if ui.verbose:
965 addr, port = httpd.socket.getsockname()
975 addr, port = httpd.socket.getsockname()
966 if addr == '0.0.0.0':
976 if addr == '0.0.0.0':
967 addr = socket.gethostname()
977 addr = socket.gethostname()
968 else:
978 else:
969 try:
979 try:
970 addr = socket.gethostbyaddr(addr)[0]
980 addr = socket.gethostbyaddr(addr)[0]
971 except socket.error:
981 except socket.error:
972 pass
982 pass
973 if port != 80:
983 if port != 80:
974 ui.status('listening at http://%s:%d/\n' % (addr, port))
984 ui.status('listening at http://%s:%d/\n' % (addr, port))
975 else:
985 else:
976 ui.status('listening at http://%s/\n' % addr)
986 ui.status('listening at http://%s/\n' % addr)
977 httpd.serve_forever()
987 httpd.serve_forever()
978
988
979 def status(ui, repo):
989 def status(ui, repo):
980 '''show changed files in the working directory
990 '''show changed files in the working directory
981
991
982 C = changed
992 C = changed
983 A = added
993 A = added
984 R = removed
994 R = removed
985 ? = not tracked'''
995 ? = not tracked'''
986
996
987 (c, a, d, u) = repo.changes()
997 (c, a, d, u) = repo.changes()
988 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
998 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
989
999
990 for f in c:
1000 for f in c:
991 ui.write("C ", f, "\n")
1001 ui.write("C ", f, "\n")
992 for f in a:
1002 for f in a:
993 ui.write("A ", f, "\n")
1003 ui.write("A ", f, "\n")
994 for f in d:
1004 for f in d:
995 ui.write("R ", f, "\n")
1005 ui.write("R ", f, "\n")
996 for f in u:
1006 for f in u:
997 ui.write("? ", f, "\n")
1007 ui.write("? ", f, "\n")
998
1008
999 def tag(ui, repo, name, rev=None, **opts):
1009 def tag(ui, repo, name, rev=None, **opts):
1000 """add a tag for the current tip or a given revision"""
1010 """add a tag for the current tip or a given revision"""
1001
1011
1002 if name == "tip":
1012 if name == "tip":
1003 ui.warn("abort: 'tip' is a reserved name!\n")
1013 ui.warn("abort: 'tip' is a reserved name!\n")
1004 return -1
1014 return -1
1005 if rev:
1015 if rev:
1006 r = hg.hex(repo.lookup(rev))
1016 r = hg.hex(repo.lookup(rev))
1007 else:
1017 else:
1008 r = hg.hex(repo.changelog.tip())
1018 r = hg.hex(repo.changelog.tip())
1009
1019
1010 if name.find(revrangesep) >= 0:
1020 if name.find(revrangesep) >= 0:
1011 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1021 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1012 return -1
1022 return -1
1013
1023
1014 if opts['local']:
1024 if opts['local']:
1015 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1025 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1016 return
1026 return
1017
1027
1018 (c, a, d, u) = repo.changes()
1028 (c, a, d, u) = repo.changes()
1019 for x in (c, a, d, u):
1029 for x in (c, a, d, u):
1020 if ".hgtags" in x:
1030 if ".hgtags" in x:
1021 ui.warn("abort: working copy of .hgtags is changed!\n")
1031 ui.warn("abort: working copy of .hgtags is changed!\n")
1022 ui.status("(please commit .hgtags manually)\n")
1032 ui.status("(please commit .hgtags manually)\n")
1023 return -1
1033 return -1
1024
1034
1025 add = not os.path.exists(repo.wjoin(".hgtags"))
1035 add = not os.path.exists(repo.wjoin(".hgtags"))
1026 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1036 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1027 if add:
1037 if add:
1028 repo.add([".hgtags"])
1038 repo.add([".hgtags"])
1029
1039
1030 if not opts['text']:
1040 if not opts['text']:
1031 opts['text'] = "Added tag %s for changeset %s" % (name, r)
1041 opts['text'] = "Added tag %s for changeset %s" % (name, r)
1032
1042
1033 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
1043 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
1034
1044
1035 def tags(ui, repo):
1045 def tags(ui, repo):
1036 """list repository tags"""
1046 """list repository tags"""
1037
1047
1038 l = repo.tagslist()
1048 l = repo.tagslist()
1039 l.reverse()
1049 l.reverse()
1040 for t, n in l:
1050 for t, n in l:
1041 try:
1051 try:
1042 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1052 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1043 except KeyError:
1053 except KeyError:
1044 r = " ?:?"
1054 r = " ?:?"
1045 ui.write("%-30s %s\n" % (t, r))
1055 ui.write("%-30s %s\n" % (t, r))
1046
1056
1047 def tip(ui, repo):
1057 def tip(ui, repo):
1048 """show the tip revision"""
1058 """show the tip revision"""
1049 n = repo.changelog.tip()
1059 n = repo.changelog.tip()
1050 show_changeset(ui, repo, changenode=n)
1060 show_changeset(ui, repo, changenode=n)
1051
1061
1052 def undo(ui, repo):
1062 def undo(ui, repo):
1053 """undo the last commit or pull
1063 """undo the last commit or pull
1054
1064
1055 Roll back the last pull or commit transaction on the
1065 Roll back the last pull or commit transaction on the
1056 repository, restoring the project to its earlier state.
1066 repository, restoring the project to its earlier state.
1057
1067
1058 This command should be used with care. There is only one level of
1068 This command should be used with care. There is only one level of
1059 undo and there is no redo.
1069 undo and there is no redo.
1060
1070
1061 This command is not intended for use on public repositories. Once
1071 This command is not intended for use on public repositories. Once
1062 a change is visible for pull by other users, undoing it locally is
1072 a change is visible for pull by other users, undoing it locally is
1063 ineffective.
1073 ineffective.
1064 """
1074 """
1065 repo.undo()
1075 repo.undo()
1066
1076
1067 def update(ui, repo, node=None, merge=False, clean=False):
1077 def update(ui, repo, node=None, merge=False, clean=False):
1068 '''update or merge working directory
1078 '''update or merge working directory
1069
1079
1070 If there are no outstanding changes in the working directory and
1080 If there are no outstanding changes in the working directory and
1071 there is a linear relationship between the current version and the
1081 there is a linear relationship between the current version and the
1072 requested version, the result is the requested version.
1082 requested version, the result is the requested version.
1073
1083
1074 Otherwise the result is a merge between the contents of the
1084 Otherwise the result is a merge between the contents of the
1075 current working directory and the requested version. Files that
1085 current working directory and the requested version. Files that
1076 changed between either parent are marked as changed for the next
1086 changed between either parent are marked as changed for the next
1077 commit and a commit must be performed before any further updates
1087 commit and a commit must be performed before any further updates
1078 are allowed.
1088 are allowed.
1079 '''
1089 '''
1080 node = node and repo.lookup(node) or repo.changelog.tip()
1090 node = node and repo.lookup(node) or repo.changelog.tip()
1081 return repo.update(node, allow=merge, force=clean)
1091 return repo.update(node, allow=merge, force=clean)
1082
1092
1083 def verify(ui, repo):
1093 def verify(ui, repo):
1084 """verify the integrity of the repository"""
1094 """verify the integrity of the repository"""
1085 return repo.verify()
1095 return repo.verify()
1086
1096
1087 # Command options and aliases are listed here, alphabetically
1097 # Command options and aliases are listed here, alphabetically
1088
1098
1089 table = {
1099 table = {
1090 "^add": (add, [], "hg add [files]"),
1100 "^add": (add,
1101 [('I', 'include', [], 'include path in search'),
1102 ('X', 'exclude', [], 'exclude path from search')],
1103 "hg add [options] [files]"),
1091 "addremove": (addremove, [], "hg addremove [files]"),
1104 "addremove": (addremove, [], "hg addremove [files]"),
1092 "^annotate":
1105 "^annotate":
1093 (annotate,
1106 (annotate,
1094 [('r', 'revision', '', 'revision'),
1107 [('r', 'revision', '', 'revision'),
1095 ('u', 'user', None, 'show user'),
1108 ('u', 'user', None, 'show user'),
1096 ('n', 'number', None, 'show revision number'),
1109 ('n', 'number', None, 'show revision number'),
1097 ('c', 'changeset', None, 'show changeset')],
1110 ('c', 'changeset', None, 'show changeset')],
1098 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
1111 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
1099 "cat":
1112 "cat":
1100 (cat,
1113 (cat,
1101 [('o', 'output', "", 'output to file')],
1114 [('o', 'output', "", 'output to file')],
1102 'hg cat [-o outfile] <file> [rev]'),
1115 'hg cat [-o outfile] <file> [rev]'),
1103 "^clone":
1116 "^clone":
1104 (clone,
1117 (clone,
1105 [('U', 'noupdate', None, 'skip update after cloning')],
1118 [('U', 'noupdate', None, 'skip update after cloning')],
1106 'hg clone [options] <source> [dest]'),
1119 'hg clone [options] <source> [dest]'),
1107 "^commit|ci":
1120 "^commit|ci":
1108 (commit,
1121 (commit,
1109 [('t', 'text', "", 'commit text'),
1122 [('t', 'text', "", 'commit text'),
1110 ('A', 'addremove', None, 'run add/remove during commit'),
1123 ('A', 'addremove', None, 'run add/remove during commit'),
1111 ('l', 'logfile', "", 'commit text file'),
1124 ('l', 'logfile', "", 'commit text file'),
1112 ('d', 'date', "", 'date code'),
1125 ('d', 'date', "", 'date code'),
1113 ('u', 'user', "", 'user')],
1126 ('u', 'user', "", 'user')],
1114 'hg commit [files]'),
1127 'hg commit [files]'),
1115 "copy": (copy, [], 'hg copy <source> <dest>'),
1128 "copy": (copy, [], 'hg copy <source> <dest>'),
1116 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1129 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1117 "debugstate": (debugstate, [], 'debugstate'),
1130 "debugstate": (debugstate, [], 'debugstate'),
1118 "debugindex": (debugindex, [], 'debugindex <file>'),
1131 "debugindex": (debugindex, [], 'debugindex <file>'),
1119 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
1132 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
1120 "^diff":
1133 "^diff":
1121 (diff,
1134 (diff,
1122 [('r', 'rev', [], 'revision')],
1135 [('r', 'rev', [], 'revision')],
1123 'hg diff [-r A] [-r B] [files]'),
1136 'hg diff [-r A] [-r B] [files]'),
1124 "^export":
1137 "^export":
1125 (export,
1138 (export,
1126 [('o', 'output', "", 'output to file')],
1139 [('o', 'output', "", 'output to file')],
1127 "hg export [-o file] <changeset> ..."),
1140 "hg export [-o file] <changeset> ..."),
1128 "forget": (forget, [], "hg forget [files]"),
1141 "forget": (forget, [], "hg forget [files]"),
1129 "heads": (heads, [], 'hg heads'),
1142 "heads": (heads, [], 'hg heads'),
1130 "help": (help_, [], 'hg help [command]'),
1143 "help": (help_, [], 'hg help [command]'),
1131 "identify|id": (identify, [], 'hg identify'),
1144 "identify|id": (identify, [], 'hg identify'),
1132 "import|patch":
1145 "import|patch":
1133 (import_,
1146 (import_,
1134 [('p', 'strip', 1, 'path strip'),
1147 [('p', 'strip', 1, 'path strip'),
1135 ('b', 'base', "", 'base path')],
1148 ('b', 'base', "", 'base path')],
1136 "hg import [options] <patches>"),
1149 "hg import [options] <patches>"),
1137 "^init": (init, [], 'hg init'),
1150 "^init": (init, [], 'hg init'),
1138 "locate":
1151 "locate":
1139 (locate,
1152 (locate,
1140 [('0', 'print0', None, 'end records with NUL'),
1153 [('0', 'print0', None, 'end records with NUL'),
1141 ('f', 'fullpath', None, 'print complete paths'),
1154 ('f', 'fullpath', None, 'print complete paths'),
1142 ('i', 'include', [], 'include path in search'),
1155 ('I', 'include', [], 'include path in search'),
1143 ('r', 'rev', '', 'revision'),
1156 ('r', 'rev', '', 'revision'),
1144 ('x', 'exclude', [], 'exclude path from search')],
1157 ('X', 'exclude', [], 'exclude path from search')],
1145 'hg locate [options] [files]'),
1158 'hg locate [options] [files]'),
1146 "^log|history":
1159 "^log|history":
1147 (log,
1160 (log,
1148 [('r', 'rev', [], 'revision'),
1161 [('r', 'rev', [], 'revision'),
1149 ('p', 'patch', None, 'show patch')],
1162 ('p', 'patch', None, 'show patch')],
1150 'hg log [-r A] [-r B] [-p] [file]'),
1163 'hg log [-r A] [-r B] [-p] [file]'),
1151 "manifest": (manifest, [], 'hg manifest [rev]'),
1164 "manifest": (manifest, [], 'hg manifest [rev]'),
1152 "parents": (parents, [], 'hg parents [node]'),
1165 "parents": (parents, [], 'hg parents [node]'),
1153 "^pull":
1166 "^pull":
1154 (pull,
1167 (pull,
1155 [('u', 'update', None, 'update working directory')],
1168 [('u', 'update', None, 'update working directory')],
1156 'hg pull [options] [source]'),
1169 'hg pull [options] [source]'),
1157 "^push": (push, [], 'hg push <destination>'),
1170 "^push": (push, [], 'hg push <destination>'),
1158 "rawcommit":
1171 "rawcommit":
1159 (rawcommit,
1172 (rawcommit,
1160 [('p', 'parent', [], 'parent'),
1173 [('p', 'parent', [], 'parent'),
1161 ('d', 'date', "", 'date code'),
1174 ('d', 'date', "", 'date code'),
1162 ('u', 'user', "", 'user'),
1175 ('u', 'user', "", 'user'),
1163 ('F', 'files', "", 'file list'),
1176 ('F', 'files', "", 'file list'),
1164 ('t', 'text', "", 'commit text'),
1177 ('t', 'text', "", 'commit text'),
1165 ('l', 'logfile', "", 'commit text file')],
1178 ('l', 'logfile', "", 'commit text file')],
1166 'hg rawcommit [options] [files]'),
1179 'hg rawcommit [options] [files]'),
1167 "recover": (recover, [], "hg recover"),
1180 "recover": (recover, [], "hg recover"),
1168 "^remove|rm": (remove, [], "hg remove [files]"),
1181 "^remove|rm": (remove, [], "hg remove [files]"),
1169 "^revert":
1182 "^revert":
1170 (revert,
1183 (revert,
1171 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1184 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1172 ("r", "rev", "", "revision")],
1185 ("r", "rev", "", "revision")],
1173 "hg revert [files|dirs]"),
1186 "hg revert [files|dirs]"),
1174 "root": (root, [], "hg root"),
1187 "root": (root, [], "hg root"),
1175 "^serve":
1188 "^serve":
1176 (serve,
1189 (serve,
1177 [('A', 'accesslog', '', 'access log file'),
1190 [('A', 'accesslog', '', 'access log file'),
1178 ('E', 'errorlog', '', 'error log file'),
1191 ('E', 'errorlog', '', 'error log file'),
1179 ('p', 'port', 8000, 'listen port'),
1192 ('p', 'port', 8000, 'listen port'),
1180 ('a', 'address', '', 'interface address'),
1193 ('a', 'address', '', 'interface address'),
1181 ('n', 'name', os.getcwd(), 'repository name'),
1194 ('n', 'name', os.getcwd(), 'repository name'),
1182 ('', 'stdio', None, 'for remote clients'),
1195 ('', 'stdio', None, 'for remote clients'),
1183 ('t', 'templates', "", 'template map')],
1196 ('t', 'templates', "", 'template map')],
1184 "hg serve [options]"),
1197 "hg serve [options]"),
1185 "^status": (status, [], 'hg status'),
1198 "^status": (status, [], 'hg status'),
1186 "tag":
1199 "tag":
1187 (tag,
1200 (tag,
1188 [('l', 'local', None, 'make the tag local'),
1201 [('l', 'local', None, 'make the tag local'),
1189 ('t', 'text', "", 'commit text'),
1202 ('t', 'text', "", 'commit text'),
1190 ('d', 'date', "", 'date code'),
1203 ('d', 'date', "", 'date code'),
1191 ('u', 'user', "", 'user')],
1204 ('u', 'user', "", 'user')],
1192 'hg tag [options] <name> [rev]'),
1205 'hg tag [options] <name> [rev]'),
1193 "tags": (tags, [], 'hg tags'),
1206 "tags": (tags, [], 'hg tags'),
1194 "tip": (tip, [], 'hg tip'),
1207 "tip": (tip, [], 'hg tip'),
1195 "undo": (undo, [], 'hg undo'),
1208 "undo": (undo, [], 'hg undo'),
1196 "^update|up|checkout|co":
1209 "^update|up|checkout|co":
1197 (update,
1210 (update,
1198 [('m', 'merge', None, 'allow merging of conflicts'),
1211 [('m', 'merge', None, 'allow merging of conflicts'),
1199 ('C', 'clean', None, 'overwrite locally modified files')],
1212 ('C', 'clean', None, 'overwrite locally modified files')],
1200 'hg update [options] [node]'),
1213 'hg update [options] [node]'),
1201 "verify": (verify, [], 'hg verify'),
1214 "verify": (verify, [], 'hg verify'),
1202 "version": (show_version, [], 'hg version'),
1215 "version": (show_version, [], 'hg version'),
1203 }
1216 }
1204
1217
1205 globalopts = [('v', 'verbose', None, 'verbose'),
1218 globalopts = [('v', 'verbose', None, 'verbose'),
1206 ('', 'debug', None, 'debug'),
1219 ('', 'debug', None, 'debug'),
1207 ('q', 'quiet', None, 'quiet'),
1220 ('q', 'quiet', None, 'quiet'),
1208 ('', 'profile', None, 'profile'),
1221 ('', 'profile', None, 'profile'),
1209 ('R', 'repository', "", 'repository root directory'),
1222 ('R', 'repository', "", 'repository root directory'),
1210 ('', 'traceback', None, 'print traceback on exception'),
1223 ('', 'traceback', None, 'print traceback on exception'),
1211 ('y', 'noninteractive', None, 'run non-interactively'),
1224 ('y', 'noninteractive', None, 'run non-interactively'),
1212 ('', 'version', None, 'output version information and exit'),
1225 ('', 'version', None, 'output version information and exit'),
1213 ]
1226 ]
1214
1227
1215 norepo = "clone init version help debugindex debugindexdot"
1228 norepo = "clone init version help debugindex debugindexdot"
1216
1229
1217 def find(cmd):
1230 def find(cmd):
1218 for e in table.keys():
1231 for e in table.keys():
1219 if re.match("(%s)$" % e, cmd):
1232 if re.match("(%s)$" % e, cmd):
1220 return table[e]
1233 return table[e]
1221
1234
1222 raise UnknownCommand(cmd)
1235 raise UnknownCommand(cmd)
1223
1236
1224 class SignalInterrupt(Exception):
1237 class SignalInterrupt(Exception):
1225 """Exception raised on SIGTERM and SIGHUP."""
1238 """Exception raised on SIGTERM and SIGHUP."""
1226
1239
1227 def catchterm(*args):
1240 def catchterm(*args):
1228 raise SignalInterrupt
1241 raise SignalInterrupt
1229
1242
1230 def run():
1243 def run():
1231 sys.exit(dispatch(sys.argv[1:]))
1244 sys.exit(dispatch(sys.argv[1:]))
1232
1245
1233 class ParseError(Exception):
1246 class ParseError(Exception):
1234 """Exception raised on errors in parsing the command line."""
1247 """Exception raised on errors in parsing the command line."""
1235
1248
1236 def parse(args):
1249 def parse(args):
1237 options = {}
1250 options = {}
1238 cmdoptions = {}
1251 cmdoptions = {}
1239
1252
1240 try:
1253 try:
1241 args = fancyopts.fancyopts(args, globalopts, options)
1254 args = fancyopts.fancyopts(args, globalopts, options)
1242 except fancyopts.getopt.GetoptError, inst:
1255 except fancyopts.getopt.GetoptError, inst:
1243 raise ParseError(None, inst)
1256 raise ParseError(None, inst)
1244
1257
1245 if options["version"]:
1258 if options["version"]:
1246 return ("version", show_version, [], options, cmdoptions)
1259 return ("version", show_version, [], options, cmdoptions)
1247 elif not args:
1260 elif not args:
1248 return ("help", help_, [], options, cmdoptions)
1261 return ("help", help_, [], options, cmdoptions)
1249 else:
1262 else:
1250 cmd, args = args[0], args[1:]
1263 cmd, args = args[0], args[1:]
1251
1264
1252 i = find(cmd)
1265 i = find(cmd)
1253
1266
1254 # combine global options into local
1267 # combine global options into local
1255 c = list(i[1])
1268 c = list(i[1])
1256 for o in globalopts:
1269 for o in globalopts:
1257 c.append((o[0], o[1], options[o[1]], o[3]))
1270 c.append((o[0], o[1], options[o[1]], o[3]))
1258
1271
1259 try:
1272 try:
1260 args = fancyopts.fancyopts(args, c, cmdoptions)
1273 args = fancyopts.fancyopts(args, c, cmdoptions)
1261 except fancyopts.getopt.GetoptError, inst:
1274 except fancyopts.getopt.GetoptError, inst:
1262 raise ParseError(cmd, inst)
1275 raise ParseError(cmd, inst)
1263
1276
1264 # separate global options back out
1277 # separate global options back out
1265 for o in globalopts:
1278 for o in globalopts:
1266 n = o[1]
1279 n = o[1]
1267 options[n] = cmdoptions[n]
1280 options[n] = cmdoptions[n]
1268 del cmdoptions[n]
1281 del cmdoptions[n]
1269
1282
1270 return (cmd, i[0], args, options, cmdoptions)
1283 return (cmd, i[0], args, options, cmdoptions)
1271
1284
1272 def dispatch(args):
1285 def dispatch(args):
1273 signal.signal(signal.SIGTERM, catchterm)
1286 signal.signal(signal.SIGTERM, catchterm)
1274 try:
1287 try:
1275 signal.signal(signal.SIGHUP, catchterm)
1288 signal.signal(signal.SIGHUP, catchterm)
1276 except AttributeError:
1289 except AttributeError:
1277 pass
1290 pass
1278
1291
1279 try:
1292 try:
1280 cmd, func, args, options, cmdoptions = parse(args)
1293 cmd, func, args, options, cmdoptions = parse(args)
1281 except ParseError, inst:
1294 except ParseError, inst:
1282 u = ui.ui()
1295 u = ui.ui()
1283 if inst.args[0]:
1296 if inst.args[0]:
1284 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1297 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1285 help_(u, inst.args[0])
1298 help_(u, inst.args[0])
1286 else:
1299 else:
1287 u.warn("hg: %s\n" % inst.args[1])
1300 u.warn("hg: %s\n" % inst.args[1])
1288 help_(u)
1301 help_(u)
1289 sys.exit(-1)
1302 sys.exit(-1)
1290 except UnknownCommand, inst:
1303 except UnknownCommand, inst:
1291 u = ui.ui()
1304 u = ui.ui()
1292 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1305 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1293 help_(u)
1306 help_(u)
1294 sys.exit(1)
1307 sys.exit(1)
1295
1308
1296 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1309 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1297 not options["noninteractive"])
1310 not options["noninteractive"])
1298
1311
1299 try:
1312 try:
1300 try:
1313 try:
1301 if cmd not in norepo.split():
1314 if cmd not in norepo.split():
1302 path = options["repository"] or ""
1315 path = options["repository"] or ""
1303 repo = hg.repository(ui=u, path=path)
1316 repo = hg.repository(ui=u, path=path)
1304 d = lambda: func(u, repo, *args, **cmdoptions)
1317 d = lambda: func(u, repo, *args, **cmdoptions)
1305 else:
1318 else:
1306 d = lambda: func(u, *args, **cmdoptions)
1319 d = lambda: func(u, *args, **cmdoptions)
1307
1320
1308 if options['profile']:
1321 if options['profile']:
1309 import hotshot, hotshot.stats
1322 import hotshot, hotshot.stats
1310 prof = hotshot.Profile("hg.prof")
1323 prof = hotshot.Profile("hg.prof")
1311 r = prof.runcall(d)
1324 r = prof.runcall(d)
1312 prof.close()
1325 prof.close()
1313 stats = hotshot.stats.load("hg.prof")
1326 stats = hotshot.stats.load("hg.prof")
1314 stats.strip_dirs()
1327 stats.strip_dirs()
1315 stats.sort_stats('time', 'calls')
1328 stats.sort_stats('time', 'calls')
1316 stats.print_stats(40)
1329 stats.print_stats(40)
1317 return r
1330 return r
1318 else:
1331 else:
1319 return d()
1332 return d()
1320 except:
1333 except:
1321 if options['traceback']:
1334 if options['traceback']:
1322 traceback.print_exc()
1335 traceback.print_exc()
1323 raise
1336 raise
1324 except util.CommandError, inst:
1337 except util.CommandError, inst:
1325 u.warn("abort: %s\n" % inst.args)
1338 u.warn("abort: %s\n" % inst.args)
1326 except hg.RepoError, inst:
1339 except hg.RepoError, inst:
1327 u.warn("abort: ", inst, "!\n")
1340 u.warn("abort: ", inst, "!\n")
1328 except SignalInterrupt:
1341 except SignalInterrupt:
1329 u.warn("killed!\n")
1342 u.warn("killed!\n")
1330 except KeyboardInterrupt:
1343 except KeyboardInterrupt:
1331 u.warn("interrupted!\n")
1344 u.warn("interrupted!\n")
1332 except IOError, inst:
1345 except IOError, inst:
1333 if hasattr(inst, "code"):
1346 if hasattr(inst, "code"):
1334 u.warn("abort: %s\n" % inst)
1347 u.warn("abort: %s\n" % inst)
1335 elif hasattr(inst, "reason"):
1348 elif hasattr(inst, "reason"):
1336 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
1349 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
1337 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1350 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1338 if u.debugflag: u.warn("broken pipe\n")
1351 if u.debugflag: u.warn("broken pipe\n")
1339 else:
1352 else:
1340 raise
1353 raise
1341 except OSError, inst:
1354 except OSError, inst:
1342 if hasattr(inst, "filename"):
1355 if hasattr(inst, "filename"):
1343 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1356 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1344 else:
1357 else:
1345 u.warn("abort: %s\n" % inst.strerror)
1358 u.warn("abort: %s\n" % inst.strerror)
1346 except TypeError, inst:
1359 except TypeError, inst:
1347 # was this an argument error?
1360 # was this an argument error?
1348 tb = traceback.extract_tb(sys.exc_info()[2])
1361 tb = traceback.extract_tb(sys.exc_info()[2])
1349 if len(tb) > 2: # no
1362 if len(tb) > 2: # no
1350 raise
1363 raise
1351 u.debug(inst, "\n")
1364 u.debug(inst, "\n")
1352 u.warn("%s: invalid arguments\n" % cmd)
1365 u.warn("%s: invalid arguments\n" % cmd)
1353 help_(u, cmd)
1366 help_(u, cmd)
1354
1367
1355 sys.exit(-1)
1368 sys.exit(-1)
@@ -1,1925 +1,1926 b''
1 # hg.py - repository classes for mercurial
1 # hg.py - repository classes for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import sys, struct, os
8 import sys, struct, os
9 import util
9 import util
10 from revlog import *
10 from revlog import *
11 from demandload import *
11 from demandload import *
12 demandload(globals(), "re lock urllib urllib2 transaction time socket")
12 demandload(globals(), "re lock urllib urllib2 transaction time socket")
13 demandload(globals(), "tempfile httprangereader bdiff")
13 demandload(globals(), "tempfile httprangereader bdiff")
14 demandload(globals(), "bisect select")
14 demandload(globals(), "bisect select")
15
15
16 def always(fn):
17 return True
18
19 class filelog(revlog):
16 class filelog(revlog):
20 def __init__(self, opener, path):
17 def __init__(self, opener, path):
21 revlog.__init__(self, opener,
18 revlog.__init__(self, opener,
22 os.path.join("data", path + ".i"),
19 os.path.join("data", path + ".i"),
23 os.path.join("data", path + ".d"))
20 os.path.join("data", path + ".d"))
24
21
25 def read(self, node):
22 def read(self, node):
26 t = self.revision(node)
23 t = self.revision(node)
27 if not t.startswith('\1\n'):
24 if not t.startswith('\1\n'):
28 return t
25 return t
29 s = t.find('\1\n', 2)
26 s = t.find('\1\n', 2)
30 return t[s+2:]
27 return t[s+2:]
31
28
32 def readmeta(self, node):
29 def readmeta(self, node):
33 t = self.revision(node)
30 t = self.revision(node)
34 if not t.startswith('\1\n'):
31 if not t.startswith('\1\n'):
35 return t
32 return t
36 s = t.find('\1\n', 2)
33 s = t.find('\1\n', 2)
37 mt = t[2:s]
34 mt = t[2:s]
38 for l in mt.splitlines():
35 for l in mt.splitlines():
39 k, v = l.split(": ", 1)
36 k, v = l.split(": ", 1)
40 m[k] = v
37 m[k] = v
41 return m
38 return m
42
39
43 def add(self, text, meta, transaction, link, p1=None, p2=None):
40 def add(self, text, meta, transaction, link, p1=None, p2=None):
44 if meta or text.startswith('\1\n'):
41 if meta or text.startswith('\1\n'):
45 mt = ""
42 mt = ""
46 if meta:
43 if meta:
47 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
44 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
48 text = "\1\n" + "".join(mt) + "\1\n" + text
45 text = "\1\n" + "".join(mt) + "\1\n" + text
49 return self.addrevision(text, transaction, link, p1, p2)
46 return self.addrevision(text, transaction, link, p1, p2)
50
47
51 def annotate(self, node):
48 def annotate(self, node):
52
49
53 def decorate(text, rev):
50 def decorate(text, rev):
54 return ([rev] * len(text.splitlines()), text)
51 return ([rev] * len(text.splitlines()), text)
55
52
56 def pair(parent, child):
53 def pair(parent, child):
57 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
54 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
58 child[0][b1:b2] = parent[0][a1:a2]
55 child[0][b1:b2] = parent[0][a1:a2]
59 return child
56 return child
60
57
61 # find all ancestors
58 # find all ancestors
62 needed = {node:1}
59 needed = {node:1}
63 visit = [node]
60 visit = [node]
64 while visit:
61 while visit:
65 n = visit.pop(0)
62 n = visit.pop(0)
66 for p in self.parents(n):
63 for p in self.parents(n):
67 if p not in needed:
64 if p not in needed:
68 needed[p] = 1
65 needed[p] = 1
69 visit.append(p)
66 visit.append(p)
70 else:
67 else:
71 # count how many times we'll use this
68 # count how many times we'll use this
72 needed[p] += 1
69 needed[p] += 1
73
70
74 # sort by revision which is a topological order
71 # sort by revision which is a topological order
75 visit = [ (self.rev(n), n) for n in needed.keys() ]
72 visit = [ (self.rev(n), n) for n in needed.keys() ]
76 visit.sort()
73 visit.sort()
77 hist = {}
74 hist = {}
78
75
79 for r,n in visit:
76 for r,n in visit:
80 curr = decorate(self.read(n), self.linkrev(n))
77 curr = decorate(self.read(n), self.linkrev(n))
81 for p in self.parents(n):
78 for p in self.parents(n):
82 if p != nullid:
79 if p != nullid:
83 curr = pair(hist[p], curr)
80 curr = pair(hist[p], curr)
84 # trim the history of unneeded revs
81 # trim the history of unneeded revs
85 needed[p] -= 1
82 needed[p] -= 1
86 if not needed[p]:
83 if not needed[p]:
87 del hist[p]
84 del hist[p]
88 hist[n] = curr
85 hist[n] = curr
89
86
90 return zip(hist[n][0], hist[n][1].splitlines(1))
87 return zip(hist[n][0], hist[n][1].splitlines(1))
91
88
92 class manifest(revlog):
89 class manifest(revlog):
93 def __init__(self, opener):
90 def __init__(self, opener):
94 self.mapcache = None
91 self.mapcache = None
95 self.listcache = None
92 self.listcache = None
96 self.addlist = None
93 self.addlist = None
97 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
94 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
98
95
99 def read(self, node):
96 def read(self, node):
100 if node == nullid: return {} # don't upset local cache
97 if node == nullid: return {} # don't upset local cache
101 if self.mapcache and self.mapcache[0] == node:
98 if self.mapcache and self.mapcache[0] == node:
102 return self.mapcache[1]
99 return self.mapcache[1]
103 text = self.revision(node)
100 text = self.revision(node)
104 map = {}
101 map = {}
105 flag = {}
102 flag = {}
106 self.listcache = (text, text.splitlines(1))
103 self.listcache = (text, text.splitlines(1))
107 for l in self.listcache[1]:
104 for l in self.listcache[1]:
108 (f, n) = l.split('\0')
105 (f, n) = l.split('\0')
109 map[f] = bin(n[:40])
106 map[f] = bin(n[:40])
110 flag[f] = (n[40:-1] == "x")
107 flag[f] = (n[40:-1] == "x")
111 self.mapcache = (node, map, flag)
108 self.mapcache = (node, map, flag)
112 return map
109 return map
113
110
114 def readflags(self, node):
111 def readflags(self, node):
115 if node == nullid: return {} # don't upset local cache
112 if node == nullid: return {} # don't upset local cache
116 if not self.mapcache or self.mapcache[0] != node:
113 if not self.mapcache or self.mapcache[0] != node:
117 self.read(node)
114 self.read(node)
118 return self.mapcache[2]
115 return self.mapcache[2]
119
116
120 def diff(self, a, b):
117 def diff(self, a, b):
121 # this is sneaky, as we're not actually using a and b
118 # this is sneaky, as we're not actually using a and b
122 if self.listcache and self.addlist and self.listcache[0] == a:
119 if self.listcache and self.addlist and self.listcache[0] == a:
123 d = mdiff.diff(self.listcache[1], self.addlist, 1)
120 d = mdiff.diff(self.listcache[1], self.addlist, 1)
124 if mdiff.patch(a, d) != b:
121 if mdiff.patch(a, d) != b:
125 sys.stderr.write("*** sortdiff failed, falling back ***\n")
122 sys.stderr.write("*** sortdiff failed, falling back ***\n")
126 return mdiff.textdiff(a, b)
123 return mdiff.textdiff(a, b)
127 return d
124 return d
128 else:
125 else:
129 return mdiff.textdiff(a, b)
126 return mdiff.textdiff(a, b)
130
127
131 def add(self, map, flags, transaction, link, p1=None, p2=None,changed=None):
128 def add(self, map, flags, transaction, link, p1=None, p2=None,changed=None):
132 # directly generate the mdiff delta from the data collected during
129 # directly generate the mdiff delta from the data collected during
133 # the bisect loop below
130 # the bisect loop below
134 def gendelta(delta):
131 def gendelta(delta):
135 i = 0
132 i = 0
136 result = []
133 result = []
137 while i < len(delta):
134 while i < len(delta):
138 start = delta[i][2]
135 start = delta[i][2]
139 end = delta[i][3]
136 end = delta[i][3]
140 l = delta[i][4]
137 l = delta[i][4]
141 if l == None:
138 if l == None:
142 l = ""
139 l = ""
143 while i < len(delta) - 1 and start <= delta[i+1][2] and end >= delta[i+1][2]:
140 while i < len(delta) - 1 and start <= delta[i+1][2] and end >= delta[i+1][2]:
144 if delta[i+1][3] > end:
141 if delta[i+1][3] > end:
145 end = delta[i+1][3]
142 end = delta[i+1][3]
146 if delta[i+1][4]:
143 if delta[i+1][4]:
147 l += delta[i+1][4]
144 l += delta[i+1][4]
148 i += 1
145 i += 1
149 result.append(struct.pack(">lll", start, end, len(l)) + l)
146 result.append(struct.pack(">lll", start, end, len(l)) + l)
150 i += 1
147 i += 1
151 return result
148 return result
152
149
153 # apply the changes collected during the bisect loop to our addlist
150 # apply the changes collected during the bisect loop to our addlist
154 def addlistdelta(addlist, delta):
151 def addlistdelta(addlist, delta):
155 # apply the deltas to the addlist. start from the bottom up
152 # apply the deltas to the addlist. start from the bottom up
156 # so changes to the offsets don't mess things up.
153 # so changes to the offsets don't mess things up.
157 i = len(delta)
154 i = len(delta)
158 while i > 0:
155 while i > 0:
159 i -= 1
156 i -= 1
160 start = delta[i][0]
157 start = delta[i][0]
161 end = delta[i][1]
158 end = delta[i][1]
162 if delta[i][4]:
159 if delta[i][4]:
163 addlist[start:end] = [delta[i][4]]
160 addlist[start:end] = [delta[i][4]]
164 else:
161 else:
165 del addlist[start:end]
162 del addlist[start:end]
166 return addlist
163 return addlist
167
164
168 # calculate the byte offset of the start of each line in the
165 # calculate the byte offset of the start of each line in the
169 # manifest
166 # manifest
170 def calcoffsets(addlist):
167 def calcoffsets(addlist):
171 offsets = [0] * (len(addlist) + 1)
168 offsets = [0] * (len(addlist) + 1)
172 offset = 0
169 offset = 0
173 i = 0
170 i = 0
174 while i < len(addlist):
171 while i < len(addlist):
175 offsets[i] = offset
172 offsets[i] = offset
176 offset += len(addlist[i])
173 offset += len(addlist[i])
177 i += 1
174 i += 1
178 offsets[i] = offset
175 offsets[i] = offset
179 return offsets
176 return offsets
180
177
181 # if we're using the listcache, make sure it is valid and
178 # if we're using the listcache, make sure it is valid and
182 # parented by the same node we're diffing against
179 # parented by the same node we're diffing against
183 if not changed or not self.listcache or not p1 or self.mapcache[0] != p1:
180 if not changed or not self.listcache or not p1 or self.mapcache[0] != p1:
184 files = map.keys()
181 files = map.keys()
185 files.sort()
182 files.sort()
186
183
187 self.addlist = ["%s\000%s%s\n" %
184 self.addlist = ["%s\000%s%s\n" %
188 (f, hex(map[f]), flags[f] and "x" or '')
185 (f, hex(map[f]), flags[f] and "x" or '')
189 for f in files]
186 for f in files]
190 cachedelta = None
187 cachedelta = None
191 else:
188 else:
192 addlist = self.listcache[1]
189 addlist = self.listcache[1]
193
190
194 # find the starting offset for each line in the add list
191 # find the starting offset for each line in the add list
195 offsets = calcoffsets(addlist)
192 offsets = calcoffsets(addlist)
196
193
197 # combine the changed lists into one list for sorting
194 # combine the changed lists into one list for sorting
198 work = [[x, 0] for x in changed[0]]
195 work = [[x, 0] for x in changed[0]]
199 work[len(work):] = [[x, 1] for x in changed[1]]
196 work[len(work):] = [[x, 1] for x in changed[1]]
200 work.sort()
197 work.sort()
201
198
202 delta = []
199 delta = []
203 bs = 0
200 bs = 0
204
201
205 for w in work:
202 for w in work:
206 f = w[0]
203 f = w[0]
207 # bs will either be the index of the item or the insertion point
204 # bs will either be the index of the item or the insertion point
208 bs = bisect.bisect(addlist, f, bs)
205 bs = bisect.bisect(addlist, f, bs)
209 if bs < len(addlist):
206 if bs < len(addlist):
210 fn = addlist[bs][:addlist[bs].index('\0')]
207 fn = addlist[bs][:addlist[bs].index('\0')]
211 else:
208 else:
212 fn = None
209 fn = None
213 if w[1] == 0:
210 if w[1] == 0:
214 l = "%s\000%s%s\n" % (f, hex(map[f]), flags[f] and "x" or '')
211 l = "%s\000%s%s\n" % (f, hex(map[f]), flags[f] and "x" or '')
215 else:
212 else:
216 l = None
213 l = None
217 start = bs
214 start = bs
218 if fn != f:
215 if fn != f:
219 # item not found, insert a new one
216 # item not found, insert a new one
220 end = bs
217 end = bs
221 if w[1] == 1:
218 if w[1] == 1:
222 sys.stderr.write("failed to remove %s from manifest" % f)
219 sys.stderr.write("failed to remove %s from manifest" % f)
223 sys.exit(1)
220 sys.exit(1)
224 else:
221 else:
225 # item is found, replace/delete the existing line
222 # item is found, replace/delete the existing line
226 end = bs + 1
223 end = bs + 1
227 delta.append([start, end, offsets[start], offsets[end], l])
224 delta.append([start, end, offsets[start], offsets[end], l])
228
225
229 self.addlist = addlistdelta(addlist, delta)
226 self.addlist = addlistdelta(addlist, delta)
230 if self.mapcache[0] == self.tip():
227 if self.mapcache[0] == self.tip():
231 cachedelta = "".join(gendelta(delta))
228 cachedelta = "".join(gendelta(delta))
232 else:
229 else:
233 cachedelta = None
230 cachedelta = None
234
231
235 text = "".join(self.addlist)
232 text = "".join(self.addlist)
236 if cachedelta and mdiff.patch(self.listcache[0], cachedelta) != text:
233 if cachedelta and mdiff.patch(self.listcache[0], cachedelta) != text:
237 sys.stderr.write("manifest delta failure")
234 sys.stderr.write("manifest delta failure")
238 sys.exit(1)
235 sys.exit(1)
239 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
236 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
240 self.mapcache = (n, map, flags)
237 self.mapcache = (n, map, flags)
241 self.listcache = (text, self.addlist)
238 self.listcache = (text, self.addlist)
242 self.addlist = None
239 self.addlist = None
243
240
244 return n
241 return n
245
242
246 class changelog(revlog):
243 class changelog(revlog):
247 def __init__(self, opener):
244 def __init__(self, opener):
248 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
245 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
249
246
250 def extract(self, text):
247 def extract(self, text):
251 if not text:
248 if not text:
252 return (nullid, "", "0", [], "")
249 return (nullid, "", "0", [], "")
253 last = text.index("\n\n")
250 last = text.index("\n\n")
254 desc = text[last + 2:]
251 desc = text[last + 2:]
255 l = text[:last].splitlines()
252 l = text[:last].splitlines()
256 manifest = bin(l[0])
253 manifest = bin(l[0])
257 user = l[1]
254 user = l[1]
258 date = l[2]
255 date = l[2]
259 files = l[3:]
256 files = l[3:]
260 return (manifest, user, date, files, desc)
257 return (manifest, user, date, files, desc)
261
258
262 def read(self, node):
259 def read(self, node):
263 return self.extract(self.revision(node))
260 return self.extract(self.revision(node))
264
261
265 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
262 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
266 user=None, date=None):
263 user=None, date=None):
267 date = date or "%d %d" % (time.time(), time.timezone)
264 date = date or "%d %d" % (time.time(), time.timezone)
268 list.sort()
265 list.sort()
269 l = [hex(manifest), user, date] + list + ["", desc]
266 l = [hex(manifest), user, date] + list + ["", desc]
270 text = "\n".join(l)
267 text = "\n".join(l)
271 return self.addrevision(text, transaction, self.count(), p1, p2)
268 return self.addrevision(text, transaction, self.count(), p1, p2)
272
269
273 class dirstate:
270 class dirstate:
274 def __init__(self, opener, ui, root):
271 def __init__(self, opener, ui, root):
275 self.opener = opener
272 self.opener = opener
276 self.root = root
273 self.root = root
277 self.dirty = 0
274 self.dirty = 0
278 self.ui = ui
275 self.ui = ui
279 self.map = None
276 self.map = None
280 self.pl = None
277 self.pl = None
281 self.copies = {}
278 self.copies = {}
282 self.ignorefunc = None
279 self.ignorefunc = None
283
280
284 def wjoin(self, f):
281 def wjoin(self, f):
285 return os.path.join(self.root, f)
282 return os.path.join(self.root, f)
286
283
287 def ignore(self, f):
284 def ignore(self, f):
288 if not self.ignorefunc:
285 if not self.ignorefunc:
289 bigpat = []
286 bigpat = []
290 try:
287 try:
291 l = file(self.wjoin(".hgignore"))
288 l = file(self.wjoin(".hgignore"))
292 for pat in l:
289 for pat in l:
293 if pat != "\n":
290 if pat != "\n":
294 p = util.pconvert(pat[:-1])
291 p = util.pconvert(pat[:-1])
295 try:
292 try:
296 r = re.compile(p)
293 r = re.compile(p)
297 except:
294 except:
298 self.ui.warn("ignoring invalid ignore"
295 self.ui.warn("ignoring invalid ignore"
299 + " regular expression '%s'\n" % p)
296 + " regular expression '%s'\n" % p)
300 else:
297 else:
301 bigpat.append(util.pconvert(pat[:-1]))
298 bigpat.append(util.pconvert(pat[:-1]))
302 except IOError: pass
299 except IOError: pass
303
300
304 s = "(?:%s)" % (")|(?:".join(bigpat))
301 s = "(?:%s)" % (")|(?:".join(bigpat))
305 r = re.compile(s)
302 r = re.compile(s)
306 self.ignorefunc = r.search
303 self.ignorefunc = r.search
307
304
308 return self.ignorefunc(f)
305 return self.ignorefunc(f)
309
306
310 def __del__(self):
307 def __del__(self):
311 if self.dirty:
308 if self.dirty:
312 self.write()
309 self.write()
313
310
314 def __getitem__(self, key):
311 def __getitem__(self, key):
315 try:
312 try:
316 return self.map[key]
313 return self.map[key]
317 except TypeError:
314 except TypeError:
318 self.read()
315 self.read()
319 return self[key]
316 return self[key]
320
317
321 def __contains__(self, key):
318 def __contains__(self, key):
322 if not self.map: self.read()
319 if not self.map: self.read()
323 return key in self.map
320 return key in self.map
324
321
325 def parents(self):
322 def parents(self):
326 if not self.pl:
323 if not self.pl:
327 self.read()
324 self.read()
328 return self.pl
325 return self.pl
329
326
330 def markdirty(self):
327 def markdirty(self):
331 if not self.dirty:
328 if not self.dirty:
332 self.dirty = 1
329 self.dirty = 1
333
330
334 def setparents(self, p1, p2 = nullid):
331 def setparents(self, p1, p2 = nullid):
335 self.markdirty()
332 self.markdirty()
336 self.pl = p1, p2
333 self.pl = p1, p2
337
334
338 def state(self, key):
335 def state(self, key):
339 try:
336 try:
340 return self[key][0]
337 return self[key][0]
341 except KeyError:
338 except KeyError:
342 return "?"
339 return "?"
343
340
344 def read(self):
341 def read(self):
345 if self.map is not None: return self.map
342 if self.map is not None: return self.map
346
343
347 self.map = {}
344 self.map = {}
348 self.pl = [nullid, nullid]
345 self.pl = [nullid, nullid]
349 try:
346 try:
350 st = self.opener("dirstate").read()
347 st = self.opener("dirstate").read()
351 if not st: return
348 if not st: return
352 except: return
349 except: return
353
350
354 self.pl = [st[:20], st[20: 40]]
351 self.pl = [st[:20], st[20: 40]]
355
352
356 pos = 40
353 pos = 40
357 while pos < len(st):
354 while pos < len(st):
358 e = struct.unpack(">cllll", st[pos:pos+17])
355 e = struct.unpack(">cllll", st[pos:pos+17])
359 l = e[4]
356 l = e[4]
360 pos += 17
357 pos += 17
361 f = st[pos:pos + l]
358 f = st[pos:pos + l]
362 if '\0' in f:
359 if '\0' in f:
363 f, c = f.split('\0')
360 f, c = f.split('\0')
364 self.copies[f] = c
361 self.copies[f] = c
365 self.map[f] = e[:4]
362 self.map[f] = e[:4]
366 pos += l
363 pos += l
367
364
368 def copy(self, source, dest):
365 def copy(self, source, dest):
369 self.read()
366 self.read()
370 self.markdirty()
367 self.markdirty()
371 self.copies[dest] = source
368 self.copies[dest] = source
372
369
373 def copied(self, file):
370 def copied(self, file):
374 return self.copies.get(file, None)
371 return self.copies.get(file, None)
375
372
376 def update(self, files, state):
373 def update(self, files, state):
377 ''' current states:
374 ''' current states:
378 n normal
375 n normal
379 m needs merging
376 m needs merging
380 r marked for removal
377 r marked for removal
381 a marked for addition'''
378 a marked for addition'''
382
379
383 if not files: return
380 if not files: return
384 self.read()
381 self.read()
385 self.markdirty()
382 self.markdirty()
386 for f in files:
383 for f in files:
387 if state == "r":
384 if state == "r":
388 self.map[f] = ('r', 0, 0, 0)
385 self.map[f] = ('r', 0, 0, 0)
389 else:
386 else:
390 s = os.stat(os.path.join(self.root, f))
387 s = os.stat(os.path.join(self.root, f))
391 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
388 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
392
389
393 def forget(self, files):
390 def forget(self, files):
394 if not files: return
391 if not files: return
395 self.read()
392 self.read()
396 self.markdirty()
393 self.markdirty()
397 for f in files:
394 for f in files:
398 try:
395 try:
399 del self.map[f]
396 del self.map[f]
400 except KeyError:
397 except KeyError:
401 self.ui.warn("not in dirstate: %s!\n" % f)
398 self.ui.warn("not in dirstate: %s!\n" % f)
402 pass
399 pass
403
400
404 def clear(self):
401 def clear(self):
405 self.map = {}
402 self.map = {}
406 self.markdirty()
403 self.markdirty()
407
404
408 def write(self):
405 def write(self):
409 st = self.opener("dirstate", "w")
406 st = self.opener("dirstate", "w")
410 st.write("".join(self.pl))
407 st.write("".join(self.pl))
411 for f, e in self.map.items():
408 for f, e in self.map.items():
412 c = self.copied(f)
409 c = self.copied(f)
413 if c:
410 if c:
414 f = f + "\0" + c
411 f = f + "\0" + c
415 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
412 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
416 st.write(e + f)
413 st.write(e + f)
417 self.dirty = 0
414 self.dirty = 0
418
415
419 def walk(self, files = None, match = always):
416 def walk(self, files = None, match = util.always):
420 self.read()
417 self.read()
421 dc = self.map.copy()
418 dc = self.map.copy()
422 # walk all files by default
419 # walk all files by default
423 if not files: files = [self.root]
420 if not files: files = [self.root]
424 def traverse():
421 def traverse():
425 for f in util.unique(files):
422 for f in util.unique(files):
426 f = os.path.join(self.root, f)
423 f = os.path.join(self.root, f)
427 if os.path.isdir(f):
424 if os.path.isdir(f):
428 for dir, subdirs, fl in os.walk(f):
425 for dir, subdirs, fl in os.walk(f):
429 d = dir[len(self.root) + 1:]
426 d = dir[len(self.root) + 1:]
430 if d == '.hg':
427 if d == '.hg':
431 subdirs[:] = []
428 subdirs[:] = []
432 continue
429 continue
433 for sd in subdirs:
430 for sd in subdirs:
434 ds = os.path.join(d, sd +'/')
431 ds = os.path.join(d, sd +'/')
435 if self.ignore(ds) or not match(ds):
432 if self.ignore(ds) or not match(ds):
436 subdirs.remove(sd)
433 subdirs.remove(sd)
437 for fn in fl:
434 for fn in fl:
438 fn = util.pconvert(os.path.join(d, fn))
435 fn = util.pconvert(os.path.join(d, fn))
439 yield fn
436 yield fn
440 else:
437 else:
441 yield f[len(self.root) + 1:]
438 yield f[len(self.root) + 1:]
442
439
443 for k in dc.keys():
440 for k in dc.keys():
444 yield k
441 yield k
445
442
446 # yield only files that match: all in dirstate, others only if
443 # yield only files that match: all in dirstate, others only if
447 # not in .hgignore
444 # not in .hgignore
448
445
449 for fn in util.unique(traverse()):
446 for fn in util.unique(traverse()):
450 if fn in dc:
447 if fn in dc:
451 del dc[fn]
448 del dc[fn]
452 elif self.ignore(fn):
449 elif self.ignore(fn):
453 continue
450 continue
454 if match(fn):
451 if match(fn):
455 yield fn
452 yield fn
456
453
457 def changes(self, files = None, match = always):
454 def changes(self, files = None, match = util.always):
458 self.read()
455 self.read()
459 dc = self.map.copy()
456 dc = self.map.copy()
460 lookup, changed, added, unknown = [], [], [], []
457 lookup, changed, added, unknown = [], [], [], []
461
458
462 for fn in self.walk(files, match):
459 for fn in self.walk(files, match):
463 try: s = os.stat(os.path.join(self.root, fn))
460 try: s = os.stat(os.path.join(self.root, fn))
464 except: continue
461 except: continue
465
462
466 if fn in dc:
463 if fn in dc:
467 c = dc[fn]
464 c = dc[fn]
468 del dc[fn]
465 del dc[fn]
469
466
470 if c[0] == 'm':
467 if c[0] == 'm':
471 changed.append(fn)
468 changed.append(fn)
472 elif c[0] == 'a':
469 elif c[0] == 'a':
473 added.append(fn)
470 added.append(fn)
474 elif c[0] == 'r':
471 elif c[0] == 'r':
475 unknown.append(fn)
472 unknown.append(fn)
476 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
473 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
477 changed.append(fn)
474 changed.append(fn)
478 elif c[1] != s.st_mode or c[3] != s.st_mtime:
475 elif c[1] != s.st_mode or c[3] != s.st_mtime:
479 lookup.append(fn)
476 lookup.append(fn)
480 else:
477 else:
481 if match(fn): unknown.append(fn)
478 if match(fn): unknown.append(fn)
482
479
483 return (lookup, changed, added, dc.keys(), unknown)
480 return (lookup, changed, added, dc.keys(), unknown)
484
481
485 # used to avoid circular references so destructors work
482 # used to avoid circular references so destructors work
486 def opener(base):
483 def opener(base):
487 p = base
484 p = base
488 def o(path, mode="r"):
485 def o(path, mode="r"):
489 if p.startswith("http://"):
486 if p.startswith("http://"):
490 f = os.path.join(p, urllib.quote(path))
487 f = os.path.join(p, urllib.quote(path))
491 return httprangereader.httprangereader(f)
488 return httprangereader.httprangereader(f)
492
489
493 f = os.path.join(p, path)
490 f = os.path.join(p, path)
494
491
495 mode += "b" # for that other OS
492 mode += "b" # for that other OS
496
493
497 if mode[0] != "r":
494 if mode[0] != "r":
498 try:
495 try:
499 s = os.stat(f)
496 s = os.stat(f)
500 except OSError:
497 except OSError:
501 d = os.path.dirname(f)
498 d = os.path.dirname(f)
502 if not os.path.isdir(d):
499 if not os.path.isdir(d):
503 os.makedirs(d)
500 os.makedirs(d)
504 else:
501 else:
505 if s.st_nlink > 1:
502 if s.st_nlink > 1:
506 file(f + ".tmp", "wb").write(file(f, "rb").read())
503 file(f + ".tmp", "wb").write(file(f, "rb").read())
507 util.rename(f+".tmp", f)
504 util.rename(f+".tmp", f)
508
505
509 return file(f, mode)
506 return file(f, mode)
510
507
511 return o
508 return o
512
509
513 class RepoError(Exception): pass
510 class RepoError(Exception): pass
514
511
515 class localrepository:
512 class localrepository:
516 def __init__(self, ui, path=None, create=0):
513 def __init__(self, ui, path=None, create=0):
517 self.remote = 0
514 self.remote = 0
518 if path and path.startswith("http://"):
515 if path and path.startswith("http://"):
519 self.remote = 1
516 self.remote = 1
520 self.path = path
517 self.path = path
521 else:
518 else:
522 if not path:
519 if not path:
523 p = os.getcwd()
520 p = os.getcwd()
524 while not os.path.isdir(os.path.join(p, ".hg")):
521 while not os.path.isdir(os.path.join(p, ".hg")):
525 oldp = p
522 oldp = p
526 p = os.path.dirname(p)
523 p = os.path.dirname(p)
527 if p == oldp: raise RepoError("no repo found")
524 if p == oldp: raise RepoError("no repo found")
528 path = p
525 path = p
529 self.path = os.path.join(path, ".hg")
526 self.path = os.path.join(path, ".hg")
530
527
531 if not create and not os.path.isdir(self.path):
528 if not create and not os.path.isdir(self.path):
532 raise RepoError("repository %s not found" % self.path)
529 raise RepoError("repository %s not found" % self.path)
533
530
534 self.root = path
531 self.root = path
535 self.ui = ui
532 self.ui = ui
536
533
537 if create:
534 if create:
538 os.mkdir(self.path)
535 os.mkdir(self.path)
539 os.mkdir(self.join("data"))
536 os.mkdir(self.join("data"))
540
537
541 self.opener = opener(self.path)
538 self.opener = opener(self.path)
542 self.wopener = opener(self.root)
539 self.wopener = opener(self.root)
543 self.manifest = manifest(self.opener)
540 self.manifest = manifest(self.opener)
544 self.changelog = changelog(self.opener)
541 self.changelog = changelog(self.opener)
545 self.tagscache = None
542 self.tagscache = None
546 self.nodetagscache = None
543 self.nodetagscache = None
547
544
548 if not self.remote:
545 if not self.remote:
549 self.dirstate = dirstate(self.opener, ui, self.root)
546 self.dirstate = dirstate(self.opener, ui, self.root)
550 try:
547 try:
551 self.ui.readconfig(self.opener("hgrc"))
548 self.ui.readconfig(self.opener("hgrc"))
552 except IOError: pass
549 except IOError: pass
553
550
554 def hook(self, name, **args):
551 def hook(self, name, **args):
555 s = self.ui.config("hooks", name)
552 s = self.ui.config("hooks", name)
556 if s:
553 if s:
557 self.ui.note("running hook %s: %s\n" % (name, s))
554 self.ui.note("running hook %s: %s\n" % (name, s))
558 old = {}
555 old = {}
559 for k, v in args.items():
556 for k, v in args.items():
560 k = k.upper()
557 k = k.upper()
561 old[k] = os.environ.get(k, None)
558 old[k] = os.environ.get(k, None)
562 os.environ[k] = v
559 os.environ[k] = v
563
560
564 r = os.system(s)
561 r = os.system(s)
565
562
566 for k, v in old.items():
563 for k, v in old.items():
567 if v != None:
564 if v != None:
568 os.environ[k] = v
565 os.environ[k] = v
569 else:
566 else:
570 del os.environ[k]
567 del os.environ[k]
571
568
572 if r:
569 if r:
573 self.ui.warn("abort: %s hook failed with status %d!\n" %
570 self.ui.warn("abort: %s hook failed with status %d!\n" %
574 (name, r))
571 (name, r))
575 return False
572 return False
576 return True
573 return True
577
574
578 def tags(self):
575 def tags(self):
579 '''return a mapping of tag to node'''
576 '''return a mapping of tag to node'''
580 if not self.tagscache:
577 if not self.tagscache:
581 self.tagscache = {}
578 self.tagscache = {}
582 def addtag(self, k, n):
579 def addtag(self, k, n):
583 try:
580 try:
584 bin_n = bin(n)
581 bin_n = bin(n)
585 except TypeError:
582 except TypeError:
586 bin_n = ''
583 bin_n = ''
587 self.tagscache[k.strip()] = bin_n
584 self.tagscache[k.strip()] = bin_n
588
585
589 try:
586 try:
590 # read each head of the tags file, ending with the tip
587 # read each head of the tags file, ending with the tip
591 # and add each tag found to the map, with "newer" ones
588 # and add each tag found to the map, with "newer" ones
592 # taking precedence
589 # taking precedence
593 fl = self.file(".hgtags")
590 fl = self.file(".hgtags")
594 h = fl.heads()
591 h = fl.heads()
595 h.reverse()
592 h.reverse()
596 for r in h:
593 for r in h:
597 for l in fl.revision(r).splitlines():
594 for l in fl.revision(r).splitlines():
598 if l:
595 if l:
599 n, k = l.split(" ", 1)
596 n, k = l.split(" ", 1)
600 addtag(self, k, n)
597 addtag(self, k, n)
601 except KeyError:
598 except KeyError:
602 pass
599 pass
603
600
604 try:
601 try:
605 f = self.opener("localtags")
602 f = self.opener("localtags")
606 for l in f:
603 for l in f:
607 n, k = l.split(" ", 1)
604 n, k = l.split(" ", 1)
608 addtag(self, k, n)
605 addtag(self, k, n)
609 except IOError:
606 except IOError:
610 pass
607 pass
611
608
612 self.tagscache['tip'] = self.changelog.tip()
609 self.tagscache['tip'] = self.changelog.tip()
613
610
614 return self.tagscache
611 return self.tagscache
615
612
616 def tagslist(self):
613 def tagslist(self):
617 '''return a list of tags ordered by revision'''
614 '''return a list of tags ordered by revision'''
618 l = []
615 l = []
619 for t, n in self.tags().items():
616 for t, n in self.tags().items():
620 try:
617 try:
621 r = self.changelog.rev(n)
618 r = self.changelog.rev(n)
622 except:
619 except:
623 r = -2 # sort to the beginning of the list if unknown
620 r = -2 # sort to the beginning of the list if unknown
624 l.append((r,t,n))
621 l.append((r,t,n))
625 l.sort()
622 l.sort()
626 return [(t,n) for r,t,n in l]
623 return [(t,n) for r,t,n in l]
627
624
628 def nodetags(self, node):
625 def nodetags(self, node):
629 '''return the tags associated with a node'''
626 '''return the tags associated with a node'''
630 if not self.nodetagscache:
627 if not self.nodetagscache:
631 self.nodetagscache = {}
628 self.nodetagscache = {}
632 for t,n in self.tags().items():
629 for t,n in self.tags().items():
633 self.nodetagscache.setdefault(n,[]).append(t)
630 self.nodetagscache.setdefault(n,[]).append(t)
634 return self.nodetagscache.get(node, [])
631 return self.nodetagscache.get(node, [])
635
632
636 def lookup(self, key):
633 def lookup(self, key):
637 try:
634 try:
638 return self.tags()[key]
635 return self.tags()[key]
639 except KeyError:
636 except KeyError:
640 try:
637 try:
641 return self.changelog.lookup(key)
638 return self.changelog.lookup(key)
642 except:
639 except:
643 raise RepoError("unknown revision '%s'" % key)
640 raise RepoError("unknown revision '%s'" % key)
644
641
645 def dev(self):
642 def dev(self):
646 if self.remote: return -1
643 if self.remote: return -1
647 return os.stat(self.path).st_dev
644 return os.stat(self.path).st_dev
648
645
649 def join(self, f):
646 def join(self, f):
650 return os.path.join(self.path, f)
647 return os.path.join(self.path, f)
651
648
652 def wjoin(self, f):
649 def wjoin(self, f):
653 return os.path.join(self.root, f)
650 return os.path.join(self.root, f)
654
651
655 def file(self, f):
652 def file(self, f):
656 if f[0] == '/': f = f[1:]
653 if f[0] == '/': f = f[1:]
657 return filelog(self.opener, f)
654 return filelog(self.opener, f)
658
655
659 def getcwd(self):
656 def getcwd(self):
660 cwd = os.getcwd()
657 cwd = os.getcwd()
661 if cwd == self.root: return ''
658 if cwd == self.root: return ''
662 return cwd[len(self.root) + 1:]
659 return cwd[len(self.root) + 1:]
663
660
664 def wfile(self, f, mode='r'):
661 def wfile(self, f, mode='r'):
665 return self.wopener(f, mode)
662 return self.wopener(f, mode)
666
663
667 def transaction(self):
664 def transaction(self):
668 # save dirstate for undo
665 # save dirstate for undo
669 try:
666 try:
670 ds = self.opener("dirstate").read()
667 ds = self.opener("dirstate").read()
671 except IOError:
668 except IOError:
672 ds = ""
669 ds = ""
673 self.opener("undo.dirstate", "w").write(ds)
670 self.opener("undo.dirstate", "w").write(ds)
674
671
675 return transaction.transaction(self.ui.warn,
672 return transaction.transaction(self.ui.warn,
676 self.opener, self.join("journal"),
673 self.opener, self.join("journal"),
677 self.join("undo"))
674 self.join("undo"))
678
675
679 def recover(self):
676 def recover(self):
680 lock = self.lock()
677 lock = self.lock()
681 if os.path.exists(self.join("journal")):
678 if os.path.exists(self.join("journal")):
682 self.ui.status("rolling back interrupted transaction\n")
679 self.ui.status("rolling back interrupted transaction\n")
683 return transaction.rollback(self.opener, self.join("journal"))
680 return transaction.rollback(self.opener, self.join("journal"))
684 else:
681 else:
685 self.ui.warn("no interrupted transaction available\n")
682 self.ui.warn("no interrupted transaction available\n")
686
683
687 def undo(self):
684 def undo(self):
688 lock = self.lock()
685 lock = self.lock()
689 if os.path.exists(self.join("undo")):
686 if os.path.exists(self.join("undo")):
690 self.ui.status("rolling back last transaction\n")
687 self.ui.status("rolling back last transaction\n")
691 transaction.rollback(self.opener, self.join("undo"))
688 transaction.rollback(self.opener, self.join("undo"))
692 self.dirstate = None
689 self.dirstate = None
693 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
690 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
694 self.dirstate = dirstate(self.opener, self.ui, self.root)
691 self.dirstate = dirstate(self.opener, self.ui, self.root)
695 else:
692 else:
696 self.ui.warn("no undo information available\n")
693 self.ui.warn("no undo information available\n")
697
694
698 def lock(self, wait = 1):
695 def lock(self, wait = 1):
699 try:
696 try:
700 return lock.lock(self.join("lock"), 0)
697 return lock.lock(self.join("lock"), 0)
701 except lock.LockHeld, inst:
698 except lock.LockHeld, inst:
702 if wait:
699 if wait:
703 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
700 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
704 return lock.lock(self.join("lock"), wait)
701 return lock.lock(self.join("lock"), wait)
705 raise inst
702 raise inst
706
703
707 def rawcommit(self, files, text, user, date, p1=None, p2=None):
704 def rawcommit(self, files, text, user, date, p1=None, p2=None):
708 orig_parent = self.dirstate.parents()[0] or nullid
705 orig_parent = self.dirstate.parents()[0] or nullid
709 p1 = p1 or self.dirstate.parents()[0] or nullid
706 p1 = p1 or self.dirstate.parents()[0] or nullid
710 p2 = p2 or self.dirstate.parents()[1] or nullid
707 p2 = p2 or self.dirstate.parents()[1] or nullid
711 c1 = self.changelog.read(p1)
708 c1 = self.changelog.read(p1)
712 c2 = self.changelog.read(p2)
709 c2 = self.changelog.read(p2)
713 m1 = self.manifest.read(c1[0])
710 m1 = self.manifest.read(c1[0])
714 mf1 = self.manifest.readflags(c1[0])
711 mf1 = self.manifest.readflags(c1[0])
715 m2 = self.manifest.read(c2[0])
712 m2 = self.manifest.read(c2[0])
716
713
717 if orig_parent == p1:
714 if orig_parent == p1:
718 update_dirstate = 1
715 update_dirstate = 1
719 else:
716 else:
720 update_dirstate = 0
717 update_dirstate = 0
721
718
722 tr = self.transaction()
719 tr = self.transaction()
723 mm = m1.copy()
720 mm = m1.copy()
724 mfm = mf1.copy()
721 mfm = mf1.copy()
725 linkrev = self.changelog.count()
722 linkrev = self.changelog.count()
726 for f in files:
723 for f in files:
727 try:
724 try:
728 t = self.wfile(f).read()
725 t = self.wfile(f).read()
729 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
726 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
730 r = self.file(f)
727 r = self.file(f)
731 mfm[f] = tm
728 mfm[f] = tm
732 mm[f] = r.add(t, {}, tr, linkrev,
729 mm[f] = r.add(t, {}, tr, linkrev,
733 m1.get(f, nullid), m2.get(f, nullid))
730 m1.get(f, nullid), m2.get(f, nullid))
734 if update_dirstate:
731 if update_dirstate:
735 self.dirstate.update([f], "n")
732 self.dirstate.update([f], "n")
736 except IOError:
733 except IOError:
737 try:
734 try:
738 del mm[f]
735 del mm[f]
739 del mfm[f]
736 del mfm[f]
740 if update_dirstate:
737 if update_dirstate:
741 self.dirstate.forget([f])
738 self.dirstate.forget([f])
742 except:
739 except:
743 # deleted from p2?
740 # deleted from p2?
744 pass
741 pass
745
742
746 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
743 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
747 user = user or self.ui.username()
744 user = user or self.ui.username()
748 n = self.changelog.add(mnode, files, text, tr, p1, p2, user, date)
745 n = self.changelog.add(mnode, files, text, tr, p1, p2, user, date)
749 tr.close()
746 tr.close()
750 if update_dirstate:
747 if update_dirstate:
751 self.dirstate.setparents(n, nullid)
748 self.dirstate.setparents(n, nullid)
752
749
753 def commit(self, files = None, text = "", user = None, date = None):
750 def commit(self, files = None, text = "", user = None, date = None):
754 commit = []
751 commit = []
755 remove = []
752 remove = []
756 if files:
753 if files:
757 for f in files:
754 for f in files:
758 s = self.dirstate.state(f)
755 s = self.dirstate.state(f)
759 if s in 'nmai':
756 if s in 'nmai':
760 commit.append(f)
757 commit.append(f)
761 elif s == 'r':
758 elif s == 'r':
762 remove.append(f)
759 remove.append(f)
763 else:
760 else:
764 self.ui.warn("%s not tracked!\n" % f)
761 self.ui.warn("%s not tracked!\n" % f)
765 else:
762 else:
766 (c, a, d, u) = self.changes()
763 (c, a, d, u) = self.changes()
767 commit = c + a
764 commit = c + a
768 remove = d
765 remove = d
769
766
770 if not commit and not remove:
767 if not commit and not remove:
771 self.ui.status("nothing changed\n")
768 self.ui.status("nothing changed\n")
772 return
769 return
773
770
774 if not self.hook("precommit"):
771 if not self.hook("precommit"):
775 return 1
772 return 1
776
773
777 p1, p2 = self.dirstate.parents()
774 p1, p2 = self.dirstate.parents()
778 c1 = self.changelog.read(p1)
775 c1 = self.changelog.read(p1)
779 c2 = self.changelog.read(p2)
776 c2 = self.changelog.read(p2)
780 m1 = self.manifest.read(c1[0])
777 m1 = self.manifest.read(c1[0])
781 mf1 = self.manifest.readflags(c1[0])
778 mf1 = self.manifest.readflags(c1[0])
782 m2 = self.manifest.read(c2[0])
779 m2 = self.manifest.read(c2[0])
783 lock = self.lock()
780 lock = self.lock()
784 tr = self.transaction()
781 tr = self.transaction()
785
782
786 # check in files
783 # check in files
787 new = {}
784 new = {}
788 linkrev = self.changelog.count()
785 linkrev = self.changelog.count()
789 commit.sort()
786 commit.sort()
790 for f in commit:
787 for f in commit:
791 self.ui.note(f + "\n")
788 self.ui.note(f + "\n")
792 try:
789 try:
793 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
790 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
794 t = self.wfile(f).read()
791 t = self.wfile(f).read()
795 except IOError:
792 except IOError:
796 self.ui.warn("trouble committing %s!\n" % f)
793 self.ui.warn("trouble committing %s!\n" % f)
797 raise
794 raise
798
795
799 meta = {}
796 meta = {}
800 cp = self.dirstate.copied(f)
797 cp = self.dirstate.copied(f)
801 if cp:
798 if cp:
802 meta["copy"] = cp
799 meta["copy"] = cp
803 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
800 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
804 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
801 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
805
802
806 r = self.file(f)
803 r = self.file(f)
807 fp1 = m1.get(f, nullid)
804 fp1 = m1.get(f, nullid)
808 fp2 = m2.get(f, nullid)
805 fp2 = m2.get(f, nullid)
809 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
806 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
810
807
811 # update manifest
808 # update manifest
812 m1.update(new)
809 m1.update(new)
813 for f in remove:
810 for f in remove:
814 if f in m1:
811 if f in m1:
815 del m1[f]
812 del m1[f]
816 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0], (new,remove))
813 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0], (new,remove))
817
814
818 # add changeset
815 # add changeset
819 new = new.keys()
816 new = new.keys()
820 new.sort()
817 new.sort()
821
818
822 if not text:
819 if not text:
823 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
820 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
824 edittext += "".join(["HG: changed %s\n" % f for f in new])
821 edittext += "".join(["HG: changed %s\n" % f for f in new])
825 edittext += "".join(["HG: removed %s\n" % f for f in remove])
822 edittext += "".join(["HG: removed %s\n" % f for f in remove])
826 edittext = self.ui.edit(edittext)
823 edittext = self.ui.edit(edittext)
827 if not edittext.rstrip():
824 if not edittext.rstrip():
828 return 1
825 return 1
829 text = edittext
826 text = edittext
830
827
831 user = user or self.ui.username()
828 user = user or self.ui.username()
832 n = self.changelog.add(mn, new, text, tr, p1, p2, user, date)
829 n = self.changelog.add(mn, new, text, tr, p1, p2, user, date)
833
830
834 tr.close()
831 tr.close()
835
832
836 self.dirstate.setparents(n)
833 self.dirstate.setparents(n)
837 self.dirstate.update(new, "n")
834 self.dirstate.update(new, "n")
838 self.dirstate.forget(remove)
835 self.dirstate.forget(remove)
839
836
840 if not self.hook("commit", node=hex(n)):
837 if not self.hook("commit", node=hex(n)):
841 return 1
838 return 1
842
839
843 def walk(self, rev = None, files = [], match = always):
840 def walk(self, node = None, files = [], match = util.always):
844 if rev is None: fns = self.dirstate.walk(files, match)
841 if node:
845 else: fns = filter(match, self.manifest.read(rev))
842 change = self.changelog.read(node)
843 fns = filter(match, self.manifest.read(change[0]))
844 else:
845 fns = self.dirstate.walk(files, match)
846 for fn in fns: yield fn
846 for fn in fns: yield fn
847
847
848 def changes(self, node1 = None, node2 = None, files = [], match = always):
848 def changes(self, node1 = None, node2 = None, files = [],
849 match = util.always):
849 mf2, u = None, []
850 mf2, u = None, []
850
851
851 def fcmp(fn, mf):
852 def fcmp(fn, mf):
852 t1 = self.wfile(fn).read()
853 t1 = self.wfile(fn).read()
853 t2 = self.file(fn).revision(mf[fn])
854 t2 = self.file(fn).revision(mf[fn])
854 return cmp(t1, t2)
855 return cmp(t1, t2)
855
856
856 def mfmatches(node):
857 def mfmatches(node):
857 mf = dict(self.manifest.read(node))
858 mf = dict(self.manifest.read(node))
858 for fn in mf.keys():
859 for fn in mf.keys():
859 if not match(fn):
860 if not match(fn):
860 del mf[fn]
861 del mf[fn]
861 return mf
862 return mf
862
863
863 # are we comparing the working directory?
864 # are we comparing the working directory?
864 if not node2:
865 if not node2:
865 l, c, a, d, u = self.dirstate.changes(files, match)
866 l, c, a, d, u = self.dirstate.changes(files, match)
866
867
867 # are we comparing working dir against its parent?
868 # are we comparing working dir against its parent?
868 if not node1:
869 if not node1:
869 if l:
870 if l:
870 # do a full compare of any files that might have changed
871 # do a full compare of any files that might have changed
871 change = self.changelog.read(self.dirstate.parents()[0])
872 change = self.changelog.read(self.dirstate.parents()[0])
872 mf2 = mfmatches(change[0])
873 mf2 = mfmatches(change[0])
873 for f in l:
874 for f in l:
874 if fcmp(f, mf2):
875 if fcmp(f, mf2):
875 c.append(f)
876 c.append(f)
876
877
877 for l in c, a, d, u:
878 for l in c, a, d, u:
878 l.sort()
879 l.sort()
879
880
880 return (c, a, d, u)
881 return (c, a, d, u)
881
882
882 # are we comparing working dir against non-tip?
883 # are we comparing working dir against non-tip?
883 # generate a pseudo-manifest for the working dir
884 # generate a pseudo-manifest for the working dir
884 if not node2:
885 if not node2:
885 if not mf2:
886 if not mf2:
886 change = self.changelog.read(self.dirstate.parents()[0])
887 change = self.changelog.read(self.dirstate.parents()[0])
887 mf2 = mfmatches(change[0])
888 mf2 = mfmatches(change[0])
888 for f in a + c + l:
889 for f in a + c + l:
889 mf2[f] = ""
890 mf2[f] = ""
890 for f in d:
891 for f in d:
891 if f in mf2: del mf2[f]
892 if f in mf2: del mf2[f]
892 else:
893 else:
893 change = self.changelog.read(node2)
894 change = self.changelog.read(node2)
894 mf2 = mfmatches(change[0])
895 mf2 = mfmatches(change[0])
895
896
896 # flush lists from dirstate before comparing manifests
897 # flush lists from dirstate before comparing manifests
897 c, a = [], []
898 c, a = [], []
898
899
899 change = self.changelog.read(node1)
900 change = self.changelog.read(node1)
900 mf1 = mfmatches(change[0])
901 mf1 = mfmatches(change[0])
901
902
902 for fn in mf2:
903 for fn in mf2:
903 if mf1.has_key(fn):
904 if mf1.has_key(fn):
904 if mf1[fn] != mf2[fn]:
905 if mf1[fn] != mf2[fn]:
905 if mf2[fn] != "" or fcmp(fn, mf1):
906 if mf2[fn] != "" or fcmp(fn, mf1):
906 c.append(fn)
907 c.append(fn)
907 del mf1[fn]
908 del mf1[fn]
908 else:
909 else:
909 a.append(fn)
910 a.append(fn)
910
911
911 d = mf1.keys()
912 d = mf1.keys()
912
913
913 for l in c, a, d, u:
914 for l in c, a, d, u:
914 l.sort()
915 l.sort()
915
916
916 return (c, a, d, u)
917 return (c, a, d, u)
917
918
918 def add(self, list):
919 def add(self, list):
919 for f in list:
920 for f in list:
920 p = self.wjoin(f)
921 p = self.wjoin(f)
921 if not os.path.exists(p):
922 if not os.path.exists(p):
922 self.ui.warn("%s does not exist!\n" % f)
923 self.ui.warn("%s does not exist!\n" % f)
923 elif not os.path.isfile(p):
924 elif not os.path.isfile(p):
924 self.ui.warn("%s not added: mercurial only supports files currently\n" % f)
925 self.ui.warn("%s not added: mercurial only supports files currently\n" % f)
925 elif self.dirstate.state(f) == 'n':
926 elif self.dirstate.state(f) in 'an':
926 self.ui.warn("%s already tracked!\n" % f)
927 self.ui.warn("%s already tracked!\n" % f)
927 else:
928 else:
928 self.dirstate.update([f], "a")
929 self.dirstate.update([f], "a")
929
930
930 def forget(self, list):
931 def forget(self, list):
931 for f in list:
932 for f in list:
932 if self.dirstate.state(f) not in 'ai':
933 if self.dirstate.state(f) not in 'ai':
933 self.ui.warn("%s not added!\n" % f)
934 self.ui.warn("%s not added!\n" % f)
934 else:
935 else:
935 self.dirstate.forget([f])
936 self.dirstate.forget([f])
936
937
937 def remove(self, list):
938 def remove(self, list):
938 for f in list:
939 for f in list:
939 p = self.wjoin(f)
940 p = self.wjoin(f)
940 if os.path.exists(p):
941 if os.path.exists(p):
941 self.ui.warn("%s still exists!\n" % f)
942 self.ui.warn("%s still exists!\n" % f)
942 elif self.dirstate.state(f) == 'a':
943 elif self.dirstate.state(f) == 'a':
943 self.ui.warn("%s never committed!\n" % f)
944 self.ui.warn("%s never committed!\n" % f)
944 self.dirstate.forget([f])
945 self.dirstate.forget([f])
945 elif f not in self.dirstate:
946 elif f not in self.dirstate:
946 self.ui.warn("%s not tracked!\n" % f)
947 self.ui.warn("%s not tracked!\n" % f)
947 else:
948 else:
948 self.dirstate.update([f], "r")
949 self.dirstate.update([f], "r")
949
950
950 def copy(self, source, dest):
951 def copy(self, source, dest):
951 p = self.wjoin(dest)
952 p = self.wjoin(dest)
952 if not os.path.exists(dest):
953 if not os.path.exists(dest):
953 self.ui.warn("%s does not exist!\n" % dest)
954 self.ui.warn("%s does not exist!\n" % dest)
954 elif not os.path.isfile(dest):
955 elif not os.path.isfile(dest):
955 self.ui.warn("copy failed: %s is not a file\n" % dest)
956 self.ui.warn("copy failed: %s is not a file\n" % dest)
956 else:
957 else:
957 if self.dirstate.state(dest) == '?':
958 if self.dirstate.state(dest) == '?':
958 self.dirstate.update([dest], "a")
959 self.dirstate.update([dest], "a")
959 self.dirstate.copy(source, dest)
960 self.dirstate.copy(source, dest)
960
961
961 def heads(self):
962 def heads(self):
962 return self.changelog.heads()
963 return self.changelog.heads()
963
964
964 def branches(self, nodes):
965 def branches(self, nodes):
965 if not nodes: nodes = [self.changelog.tip()]
966 if not nodes: nodes = [self.changelog.tip()]
966 b = []
967 b = []
967 for n in nodes:
968 for n in nodes:
968 t = n
969 t = n
969 while n:
970 while n:
970 p = self.changelog.parents(n)
971 p = self.changelog.parents(n)
971 if p[1] != nullid or p[0] == nullid:
972 if p[1] != nullid or p[0] == nullid:
972 b.append((t, n, p[0], p[1]))
973 b.append((t, n, p[0], p[1]))
973 break
974 break
974 n = p[0]
975 n = p[0]
975 return b
976 return b
976
977
977 def between(self, pairs):
978 def between(self, pairs):
978 r = []
979 r = []
979
980
980 for top, bottom in pairs:
981 for top, bottom in pairs:
981 n, l, i = top, [], 0
982 n, l, i = top, [], 0
982 f = 1
983 f = 1
983
984
984 while n != bottom:
985 while n != bottom:
985 p = self.changelog.parents(n)[0]
986 p = self.changelog.parents(n)[0]
986 if i == f:
987 if i == f:
987 l.append(n)
988 l.append(n)
988 f = f * 2
989 f = f * 2
989 n = p
990 n = p
990 i += 1
991 i += 1
991
992
992 r.append(l)
993 r.append(l)
993
994
994 return r
995 return r
995
996
996 def newer(self, nodes):
997 def newer(self, nodes):
997 m = {}
998 m = {}
998 nl = []
999 nl = []
999 pm = {}
1000 pm = {}
1000 cl = self.changelog
1001 cl = self.changelog
1001 t = l = cl.count()
1002 t = l = cl.count()
1002
1003
1003 # find the lowest numbered node
1004 # find the lowest numbered node
1004 for n in nodes:
1005 for n in nodes:
1005 l = min(l, cl.rev(n))
1006 l = min(l, cl.rev(n))
1006 m[n] = 1
1007 m[n] = 1
1007
1008
1008 for i in xrange(l, t):
1009 for i in xrange(l, t):
1009 n = cl.node(i)
1010 n = cl.node(i)
1010 if n in m: # explicitly listed
1011 if n in m: # explicitly listed
1011 pm[n] = 1
1012 pm[n] = 1
1012 nl.append(n)
1013 nl.append(n)
1013 continue
1014 continue
1014 for p in cl.parents(n):
1015 for p in cl.parents(n):
1015 if p in pm: # parent listed
1016 if p in pm: # parent listed
1016 pm[n] = 1
1017 pm[n] = 1
1017 nl.append(n)
1018 nl.append(n)
1018 break
1019 break
1019
1020
1020 return nl
1021 return nl
1021
1022
1022 def findincoming(self, remote, base={}):
1023 def findincoming(self, remote, base={}):
1023 m = self.changelog.nodemap
1024 m = self.changelog.nodemap
1024 search = []
1025 search = []
1025 fetch = []
1026 fetch = []
1026 seen = {}
1027 seen = {}
1027 seenbranch = {}
1028 seenbranch = {}
1028
1029
1029 # assume we're closer to the tip than the root
1030 # assume we're closer to the tip than the root
1030 # and start by examining the heads
1031 # and start by examining the heads
1031 self.ui.status("searching for changes\n")
1032 self.ui.status("searching for changes\n")
1032 heads = remote.heads()
1033 heads = remote.heads()
1033 unknown = []
1034 unknown = []
1034 for h in heads:
1035 for h in heads:
1035 if h not in m:
1036 if h not in m:
1036 unknown.append(h)
1037 unknown.append(h)
1037 else:
1038 else:
1038 base[h] = 1
1039 base[h] = 1
1039
1040
1040 if not unknown:
1041 if not unknown:
1041 return None
1042 return None
1042
1043
1043 rep = {}
1044 rep = {}
1044 reqcnt = 0
1045 reqcnt = 0
1045
1046
1046 # search through remote branches
1047 # search through remote branches
1047 # a 'branch' here is a linear segment of history, with four parts:
1048 # a 'branch' here is a linear segment of history, with four parts:
1048 # head, root, first parent, second parent
1049 # head, root, first parent, second parent
1049 # (a branch always has two parents (or none) by definition)
1050 # (a branch always has two parents (or none) by definition)
1050 unknown = remote.branches(unknown)
1051 unknown = remote.branches(unknown)
1051 while unknown:
1052 while unknown:
1052 r = []
1053 r = []
1053 while unknown:
1054 while unknown:
1054 n = unknown.pop(0)
1055 n = unknown.pop(0)
1055 if n[0] in seen:
1056 if n[0] in seen:
1056 continue
1057 continue
1057
1058
1058 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
1059 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
1059 if n[0] == nullid:
1060 if n[0] == nullid:
1060 break
1061 break
1061 if n in seenbranch:
1062 if n in seenbranch:
1062 self.ui.debug("branch already found\n")
1063 self.ui.debug("branch already found\n")
1063 continue
1064 continue
1064 if n[1] and n[1] in m: # do we know the base?
1065 if n[1] and n[1] in m: # do we know the base?
1065 self.ui.debug("found incomplete branch %s:%s\n"
1066 self.ui.debug("found incomplete branch %s:%s\n"
1066 % (short(n[0]), short(n[1])))
1067 % (short(n[0]), short(n[1])))
1067 search.append(n) # schedule branch range for scanning
1068 search.append(n) # schedule branch range for scanning
1068 seenbranch[n] = 1
1069 seenbranch[n] = 1
1069 else:
1070 else:
1070 if n[1] not in seen and n[1] not in fetch:
1071 if n[1] not in seen and n[1] not in fetch:
1071 if n[2] in m and n[3] in m:
1072 if n[2] in m and n[3] in m:
1072 self.ui.debug("found new changeset %s\n" %
1073 self.ui.debug("found new changeset %s\n" %
1073 short(n[1]))
1074 short(n[1]))
1074 fetch.append(n[1]) # earliest unknown
1075 fetch.append(n[1]) # earliest unknown
1075 base[n[2]] = 1 # latest known
1076 base[n[2]] = 1 # latest known
1076 continue
1077 continue
1077
1078
1078 for a in n[2:4]:
1079 for a in n[2:4]:
1079 if a not in rep:
1080 if a not in rep:
1080 r.append(a)
1081 r.append(a)
1081 rep[a] = 1
1082 rep[a] = 1
1082
1083
1083 seen[n[0]] = 1
1084 seen[n[0]] = 1
1084
1085
1085 if r:
1086 if r:
1086 reqcnt += 1
1087 reqcnt += 1
1087 self.ui.debug("request %d: %s\n" %
1088 self.ui.debug("request %d: %s\n" %
1088 (reqcnt, " ".join(map(short, r))))
1089 (reqcnt, " ".join(map(short, r))))
1089 for p in range(0, len(r), 10):
1090 for p in range(0, len(r), 10):
1090 for b in remote.branches(r[p:p+10]):
1091 for b in remote.branches(r[p:p+10]):
1091 self.ui.debug("received %s:%s\n" %
1092 self.ui.debug("received %s:%s\n" %
1092 (short(b[0]), short(b[1])))
1093 (short(b[0]), short(b[1])))
1093 if b[0] not in m and b[0] not in seen:
1094 if b[0] not in m and b[0] not in seen:
1094 unknown.append(b)
1095 unknown.append(b)
1095
1096
1096 # do binary search on the branches we found
1097 # do binary search on the branches we found
1097 while search:
1098 while search:
1098 n = search.pop(0)
1099 n = search.pop(0)
1099 reqcnt += 1
1100 reqcnt += 1
1100 l = remote.between([(n[0], n[1])])[0]
1101 l = remote.between([(n[0], n[1])])[0]
1101 l.append(n[1])
1102 l.append(n[1])
1102 p = n[0]
1103 p = n[0]
1103 f = 1
1104 f = 1
1104 for i in l:
1105 for i in l:
1105 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
1106 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
1106 if i in m:
1107 if i in m:
1107 if f <= 2:
1108 if f <= 2:
1108 self.ui.debug("found new branch changeset %s\n" %
1109 self.ui.debug("found new branch changeset %s\n" %
1109 short(p))
1110 short(p))
1110 fetch.append(p)
1111 fetch.append(p)
1111 base[i] = 1
1112 base[i] = 1
1112 else:
1113 else:
1113 self.ui.debug("narrowed branch search to %s:%s\n"
1114 self.ui.debug("narrowed branch search to %s:%s\n"
1114 % (short(p), short(i)))
1115 % (short(p), short(i)))
1115 search.append((p, i))
1116 search.append((p, i))
1116 break
1117 break
1117 p, f = i, f * 2
1118 p, f = i, f * 2
1118
1119
1119 # sanity check our fetch list
1120 # sanity check our fetch list
1120 for f in fetch:
1121 for f in fetch:
1121 if f in m:
1122 if f in m:
1122 raise RepoError("already have changeset " + short(f[:4]))
1123 raise RepoError("already have changeset " + short(f[:4]))
1123
1124
1124 if base.keys() == [nullid]:
1125 if base.keys() == [nullid]:
1125 self.ui.warn("warning: pulling from an unrelated repository!\n")
1126 self.ui.warn("warning: pulling from an unrelated repository!\n")
1126
1127
1127 self.ui.note("adding new changesets starting at " +
1128 self.ui.note("adding new changesets starting at " +
1128 " ".join([short(f) for f in fetch]) + "\n")
1129 " ".join([short(f) for f in fetch]) + "\n")
1129
1130
1130 self.ui.debug("%d total queries\n" % reqcnt)
1131 self.ui.debug("%d total queries\n" % reqcnt)
1131
1132
1132 return fetch
1133 return fetch
1133
1134
1134 def findoutgoing(self, remote):
1135 def findoutgoing(self, remote):
1135 base = {}
1136 base = {}
1136 self.findincoming(remote, base)
1137 self.findincoming(remote, base)
1137 remain = dict.fromkeys(self.changelog.nodemap)
1138 remain = dict.fromkeys(self.changelog.nodemap)
1138
1139
1139 # prune everything remote has from the tree
1140 # prune everything remote has from the tree
1140 del remain[nullid]
1141 del remain[nullid]
1141 remove = base.keys()
1142 remove = base.keys()
1142 while remove:
1143 while remove:
1143 n = remove.pop(0)
1144 n = remove.pop(0)
1144 if n in remain:
1145 if n in remain:
1145 del remain[n]
1146 del remain[n]
1146 for p in self.changelog.parents(n):
1147 for p in self.changelog.parents(n):
1147 remove.append(p)
1148 remove.append(p)
1148
1149
1149 # find every node whose parents have been pruned
1150 # find every node whose parents have been pruned
1150 subset = []
1151 subset = []
1151 for n in remain:
1152 for n in remain:
1152 p1, p2 = self.changelog.parents(n)
1153 p1, p2 = self.changelog.parents(n)
1153 if p1 not in remain and p2 not in remain:
1154 if p1 not in remain and p2 not in remain:
1154 subset.append(n)
1155 subset.append(n)
1155
1156
1156 # this is the set of all roots we have to push
1157 # this is the set of all roots we have to push
1157 return subset
1158 return subset
1158
1159
1159 def pull(self, remote):
1160 def pull(self, remote):
1160 lock = self.lock()
1161 lock = self.lock()
1161
1162
1162 # if we have an empty repo, fetch everything
1163 # if we have an empty repo, fetch everything
1163 if self.changelog.tip() == nullid:
1164 if self.changelog.tip() == nullid:
1164 self.ui.status("requesting all changes\n")
1165 self.ui.status("requesting all changes\n")
1165 fetch = [nullid]
1166 fetch = [nullid]
1166 else:
1167 else:
1167 fetch = self.findincoming(remote)
1168 fetch = self.findincoming(remote)
1168
1169
1169 if not fetch:
1170 if not fetch:
1170 self.ui.status("no changes found\n")
1171 self.ui.status("no changes found\n")
1171 return 1
1172 return 1
1172
1173
1173 cg = remote.changegroup(fetch)
1174 cg = remote.changegroup(fetch)
1174 return self.addchangegroup(cg)
1175 return self.addchangegroup(cg)
1175
1176
1176 def push(self, remote):
1177 def push(self, remote):
1177 lock = remote.lock()
1178 lock = remote.lock()
1178 update = self.findoutgoing(remote)
1179 update = self.findoutgoing(remote)
1179 if not update:
1180 if not update:
1180 self.ui.status("no changes found\n")
1181 self.ui.status("no changes found\n")
1181 return 1
1182 return 1
1182
1183
1183 cg = self.changegroup(update)
1184 cg = self.changegroup(update)
1184 return remote.addchangegroup(cg)
1185 return remote.addchangegroup(cg)
1185
1186
1186 def changegroup(self, basenodes):
1187 def changegroup(self, basenodes):
1187 class genread:
1188 class genread:
1188 def __init__(self, generator):
1189 def __init__(self, generator):
1189 self.g = generator
1190 self.g = generator
1190 self.buf = ""
1191 self.buf = ""
1191 def read(self, l):
1192 def read(self, l):
1192 while l > len(self.buf):
1193 while l > len(self.buf):
1193 try:
1194 try:
1194 self.buf += self.g.next()
1195 self.buf += self.g.next()
1195 except StopIteration:
1196 except StopIteration:
1196 break
1197 break
1197 d, self.buf = self.buf[:l], self.buf[l:]
1198 d, self.buf = self.buf[:l], self.buf[l:]
1198 return d
1199 return d
1199
1200
1200 def gengroup():
1201 def gengroup():
1201 nodes = self.newer(basenodes)
1202 nodes = self.newer(basenodes)
1202
1203
1203 # construct the link map
1204 # construct the link map
1204 linkmap = {}
1205 linkmap = {}
1205 for n in nodes:
1206 for n in nodes:
1206 linkmap[self.changelog.rev(n)] = n
1207 linkmap[self.changelog.rev(n)] = n
1207
1208
1208 # construct a list of all changed files
1209 # construct a list of all changed files
1209 changed = {}
1210 changed = {}
1210 for n in nodes:
1211 for n in nodes:
1211 c = self.changelog.read(n)
1212 c = self.changelog.read(n)
1212 for f in c[3]:
1213 for f in c[3]:
1213 changed[f] = 1
1214 changed[f] = 1
1214 changed = changed.keys()
1215 changed = changed.keys()
1215 changed.sort()
1216 changed.sort()
1216
1217
1217 # the changegroup is changesets + manifests + all file revs
1218 # the changegroup is changesets + manifests + all file revs
1218 revs = [ self.changelog.rev(n) for n in nodes ]
1219 revs = [ self.changelog.rev(n) for n in nodes ]
1219
1220
1220 for y in self.changelog.group(linkmap): yield y
1221 for y in self.changelog.group(linkmap): yield y
1221 for y in self.manifest.group(linkmap): yield y
1222 for y in self.manifest.group(linkmap): yield y
1222 for f in changed:
1223 for f in changed:
1223 yield struct.pack(">l", len(f) + 4) + f
1224 yield struct.pack(">l", len(f) + 4) + f
1224 g = self.file(f).group(linkmap)
1225 g = self.file(f).group(linkmap)
1225 for y in g:
1226 for y in g:
1226 yield y
1227 yield y
1227
1228
1228 yield struct.pack(">l", 0)
1229 yield struct.pack(">l", 0)
1229
1230
1230 return genread(gengroup())
1231 return genread(gengroup())
1231
1232
1232 def addchangegroup(self, source):
1233 def addchangegroup(self, source):
1233
1234
1234 def getchunk():
1235 def getchunk():
1235 d = source.read(4)
1236 d = source.read(4)
1236 if not d: return ""
1237 if not d: return ""
1237 l = struct.unpack(">l", d)[0]
1238 l = struct.unpack(">l", d)[0]
1238 if l <= 4: return ""
1239 if l <= 4: return ""
1239 return source.read(l - 4)
1240 return source.read(l - 4)
1240
1241
1241 def getgroup():
1242 def getgroup():
1242 while 1:
1243 while 1:
1243 c = getchunk()
1244 c = getchunk()
1244 if not c: break
1245 if not c: break
1245 yield c
1246 yield c
1246
1247
1247 def csmap(x):
1248 def csmap(x):
1248 self.ui.debug("add changeset %s\n" % short(x))
1249 self.ui.debug("add changeset %s\n" % short(x))
1249 return self.changelog.count()
1250 return self.changelog.count()
1250
1251
1251 def revmap(x):
1252 def revmap(x):
1252 return self.changelog.rev(x)
1253 return self.changelog.rev(x)
1253
1254
1254 if not source: return
1255 if not source: return
1255 changesets = files = revisions = 0
1256 changesets = files = revisions = 0
1256
1257
1257 tr = self.transaction()
1258 tr = self.transaction()
1258
1259
1259 # pull off the changeset group
1260 # pull off the changeset group
1260 self.ui.status("adding changesets\n")
1261 self.ui.status("adding changesets\n")
1261 co = self.changelog.tip()
1262 co = self.changelog.tip()
1262 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1263 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1263 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
1264 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
1264
1265
1265 # pull off the manifest group
1266 # pull off the manifest group
1266 self.ui.status("adding manifests\n")
1267 self.ui.status("adding manifests\n")
1267 mm = self.manifest.tip()
1268 mm = self.manifest.tip()
1268 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1269 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1269
1270
1270 # process the files
1271 # process the files
1271 self.ui.status("adding file revisions\n")
1272 self.ui.status("adding file revisions\n")
1272 while 1:
1273 while 1:
1273 f = getchunk()
1274 f = getchunk()
1274 if not f: break
1275 if not f: break
1275 self.ui.debug("adding %s revisions\n" % f)
1276 self.ui.debug("adding %s revisions\n" % f)
1276 fl = self.file(f)
1277 fl = self.file(f)
1277 o = fl.count()
1278 o = fl.count()
1278 n = fl.addgroup(getgroup(), revmap, tr)
1279 n = fl.addgroup(getgroup(), revmap, tr)
1279 revisions += fl.count() - o
1280 revisions += fl.count() - o
1280 files += 1
1281 files += 1
1281
1282
1282 self.ui.status(("modified %d files, added %d changesets" +
1283 self.ui.status(("modified %d files, added %d changesets" +
1283 " and %d new revisions\n")
1284 " and %d new revisions\n")
1284 % (files, changesets, revisions))
1285 % (files, changesets, revisions))
1285
1286
1286 tr.close()
1287 tr.close()
1287 return
1288 return
1288
1289
1289 def update(self, node, allow=False, force=False, choose=None,
1290 def update(self, node, allow=False, force=False, choose=None,
1290 moddirstate=True):
1291 moddirstate=True):
1291 pl = self.dirstate.parents()
1292 pl = self.dirstate.parents()
1292 if not force and pl[1] != nullid:
1293 if not force and pl[1] != nullid:
1293 self.ui.warn("aborting: outstanding uncommitted merges\n")
1294 self.ui.warn("aborting: outstanding uncommitted merges\n")
1294 return 1
1295 return 1
1295
1296
1296 p1, p2 = pl[0], node
1297 p1, p2 = pl[0], node
1297 pa = self.changelog.ancestor(p1, p2)
1298 pa = self.changelog.ancestor(p1, p2)
1298 m1n = self.changelog.read(p1)[0]
1299 m1n = self.changelog.read(p1)[0]
1299 m2n = self.changelog.read(p2)[0]
1300 m2n = self.changelog.read(p2)[0]
1300 man = self.manifest.ancestor(m1n, m2n)
1301 man = self.manifest.ancestor(m1n, m2n)
1301 m1 = self.manifest.read(m1n)
1302 m1 = self.manifest.read(m1n)
1302 mf1 = self.manifest.readflags(m1n)
1303 mf1 = self.manifest.readflags(m1n)
1303 m2 = self.manifest.read(m2n)
1304 m2 = self.manifest.read(m2n)
1304 mf2 = self.manifest.readflags(m2n)
1305 mf2 = self.manifest.readflags(m2n)
1305 ma = self.manifest.read(man)
1306 ma = self.manifest.read(man)
1306 mfa = self.manifest.readflags(man)
1307 mfa = self.manifest.readflags(man)
1307
1308
1308 (c, a, d, u) = self.changes()
1309 (c, a, d, u) = self.changes()
1309
1310
1310 # is this a jump, or a merge? i.e. is there a linear path
1311 # is this a jump, or a merge? i.e. is there a linear path
1311 # from p1 to p2?
1312 # from p1 to p2?
1312 linear_path = (pa == p1 or pa == p2)
1313 linear_path = (pa == p1 or pa == p2)
1313
1314
1314 # resolve the manifest to determine which files
1315 # resolve the manifest to determine which files
1315 # we care about merging
1316 # we care about merging
1316 self.ui.note("resolving manifests\n")
1317 self.ui.note("resolving manifests\n")
1317 self.ui.debug(" force %s allow %s moddirstate %s linear %s\n" %
1318 self.ui.debug(" force %s allow %s moddirstate %s linear %s\n" %
1318 (force, allow, moddirstate, linear_path))
1319 (force, allow, moddirstate, linear_path))
1319 self.ui.debug(" ancestor %s local %s remote %s\n" %
1320 self.ui.debug(" ancestor %s local %s remote %s\n" %
1320 (short(man), short(m1n), short(m2n)))
1321 (short(man), short(m1n), short(m2n)))
1321
1322
1322 merge = {}
1323 merge = {}
1323 get = {}
1324 get = {}
1324 remove = []
1325 remove = []
1325 mark = {}
1326 mark = {}
1326
1327
1327 # construct a working dir manifest
1328 # construct a working dir manifest
1328 mw = m1.copy()
1329 mw = m1.copy()
1329 mfw = mf1.copy()
1330 mfw = mf1.copy()
1330 umap = dict.fromkeys(u)
1331 umap = dict.fromkeys(u)
1331
1332
1332 for f in a + c + u:
1333 for f in a + c + u:
1333 mw[f] = ""
1334 mw[f] = ""
1334 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1335 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1335
1336
1336 for f in d:
1337 for f in d:
1337 if f in mw: del mw[f]
1338 if f in mw: del mw[f]
1338
1339
1339 # If we're jumping between revisions (as opposed to merging),
1340 # If we're jumping between revisions (as opposed to merging),
1340 # and if neither the working directory nor the target rev has
1341 # and if neither the working directory nor the target rev has
1341 # the file, then we need to remove it from the dirstate, to
1342 # the file, then we need to remove it from the dirstate, to
1342 # prevent the dirstate from listing the file when it is no
1343 # prevent the dirstate from listing the file when it is no
1343 # longer in the manifest.
1344 # longer in the manifest.
1344 if moddirstate and linear_path and f not in m2:
1345 if moddirstate and linear_path and f not in m2:
1345 self.dirstate.forget((f,))
1346 self.dirstate.forget((f,))
1346
1347
1347 # Compare manifests
1348 # Compare manifests
1348 for f, n in mw.iteritems():
1349 for f, n in mw.iteritems():
1349 if choose and not choose(f): continue
1350 if choose and not choose(f): continue
1350 if f in m2:
1351 if f in m2:
1351 s = 0
1352 s = 0
1352
1353
1353 # is the wfile new since m1, and match m2?
1354 # is the wfile new since m1, and match m2?
1354 if f not in m1:
1355 if f not in m1:
1355 t1 = self.wfile(f).read()
1356 t1 = self.wfile(f).read()
1356 t2 = self.file(f).revision(m2[f])
1357 t2 = self.file(f).revision(m2[f])
1357 if cmp(t1, t2) == 0:
1358 if cmp(t1, t2) == 0:
1358 mark[f] = 1
1359 mark[f] = 1
1359 n = m2[f]
1360 n = m2[f]
1360 del t1, t2
1361 del t1, t2
1361
1362
1362 # are files different?
1363 # are files different?
1363 if n != m2[f]:
1364 if n != m2[f]:
1364 a = ma.get(f, nullid)
1365 a = ma.get(f, nullid)
1365 # are both different from the ancestor?
1366 # are both different from the ancestor?
1366 if n != a and m2[f] != a:
1367 if n != a and m2[f] != a:
1367 self.ui.debug(" %s versions differ, resolve\n" % f)
1368 self.ui.debug(" %s versions differ, resolve\n" % f)
1368 # merge executable bits
1369 # merge executable bits
1369 # "if we changed or they changed, change in merge"
1370 # "if we changed or they changed, change in merge"
1370 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1371 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1371 mode = ((a^b) | (a^c)) ^ a
1372 mode = ((a^b) | (a^c)) ^ a
1372 merge[f] = (m1.get(f, nullid), m2[f], mode)
1373 merge[f] = (m1.get(f, nullid), m2[f], mode)
1373 s = 1
1374 s = 1
1374 # are we clobbering?
1375 # are we clobbering?
1375 # is remote's version newer?
1376 # is remote's version newer?
1376 # or are we going back in time?
1377 # or are we going back in time?
1377 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1378 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1378 self.ui.debug(" remote %s is newer, get\n" % f)
1379 self.ui.debug(" remote %s is newer, get\n" % f)
1379 get[f] = m2[f]
1380 get[f] = m2[f]
1380 s = 1
1381 s = 1
1381 else:
1382 else:
1382 mark[f] = 1
1383 mark[f] = 1
1383 elif f in umap:
1384 elif f in umap:
1384 # this unknown file is the same as the checkout
1385 # this unknown file is the same as the checkout
1385 get[f] = m2[f]
1386 get[f] = m2[f]
1386
1387
1387 if not s and mfw[f] != mf2[f]:
1388 if not s and mfw[f] != mf2[f]:
1388 if force:
1389 if force:
1389 self.ui.debug(" updating permissions for %s\n" % f)
1390 self.ui.debug(" updating permissions for %s\n" % f)
1390 util.set_exec(self.wjoin(f), mf2[f])
1391 util.set_exec(self.wjoin(f), mf2[f])
1391 else:
1392 else:
1392 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1393 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1393 mode = ((a^b) | (a^c)) ^ a
1394 mode = ((a^b) | (a^c)) ^ a
1394 if mode != b:
1395 if mode != b:
1395 self.ui.debug(" updating permissions for %s\n" % f)
1396 self.ui.debug(" updating permissions for %s\n" % f)
1396 util.set_exec(self.wjoin(f), mode)
1397 util.set_exec(self.wjoin(f), mode)
1397 mark[f] = 1
1398 mark[f] = 1
1398 del m2[f]
1399 del m2[f]
1399 elif f in ma:
1400 elif f in ma:
1400 if n != ma[f]:
1401 if n != ma[f]:
1401 r = "d"
1402 r = "d"
1402 if not force and (linear_path or allow):
1403 if not force and (linear_path or allow):
1403 r = self.ui.prompt(
1404 r = self.ui.prompt(
1404 (" local changed %s which remote deleted\n" % f) +
1405 (" local changed %s which remote deleted\n" % f) +
1405 "(k)eep or (d)elete?", "[kd]", "k")
1406 "(k)eep or (d)elete?", "[kd]", "k")
1406 if r == "d":
1407 if r == "d":
1407 remove.append(f)
1408 remove.append(f)
1408 else:
1409 else:
1409 self.ui.debug("other deleted %s\n" % f)
1410 self.ui.debug("other deleted %s\n" % f)
1410 remove.append(f) # other deleted it
1411 remove.append(f) # other deleted it
1411 else:
1412 else:
1412 if n == m1.get(f, nullid): # same as parent
1413 if n == m1.get(f, nullid): # same as parent
1413 if p2 == pa: # going backwards?
1414 if p2 == pa: # going backwards?
1414 self.ui.debug("remote deleted %s\n" % f)
1415 self.ui.debug("remote deleted %s\n" % f)
1415 remove.append(f)
1416 remove.append(f)
1416 else:
1417 else:
1417 self.ui.debug("local created %s, keeping\n" % f)
1418 self.ui.debug("local created %s, keeping\n" % f)
1418 else:
1419 else:
1419 self.ui.debug("working dir created %s, keeping\n" % f)
1420 self.ui.debug("working dir created %s, keeping\n" % f)
1420
1421
1421 for f, n in m2.iteritems():
1422 for f, n in m2.iteritems():
1422 if choose and not choose(f): continue
1423 if choose and not choose(f): continue
1423 if f[0] == "/": continue
1424 if f[0] == "/": continue
1424 if f in ma and n != ma[f]:
1425 if f in ma and n != ma[f]:
1425 r = "k"
1426 r = "k"
1426 if not force and (linear_path or allow):
1427 if not force and (linear_path or allow):
1427 r = self.ui.prompt(
1428 r = self.ui.prompt(
1428 ("remote changed %s which local deleted\n" % f) +
1429 ("remote changed %s which local deleted\n" % f) +
1429 "(k)eep or (d)elete?", "[kd]", "k")
1430 "(k)eep or (d)elete?", "[kd]", "k")
1430 if r == "k": get[f] = n
1431 if r == "k": get[f] = n
1431 elif f not in ma:
1432 elif f not in ma:
1432 self.ui.debug("remote created %s\n" % f)
1433 self.ui.debug("remote created %s\n" % f)
1433 get[f] = n
1434 get[f] = n
1434 else:
1435 else:
1435 if force or p2 == pa: # going backwards?
1436 if force or p2 == pa: # going backwards?
1436 self.ui.debug("local deleted %s, recreating\n" % f)
1437 self.ui.debug("local deleted %s, recreating\n" % f)
1437 get[f] = n
1438 get[f] = n
1438 else:
1439 else:
1439 self.ui.debug("local deleted %s\n" % f)
1440 self.ui.debug("local deleted %s\n" % f)
1440
1441
1441 del mw, m1, m2, ma
1442 del mw, m1, m2, ma
1442
1443
1443 if force:
1444 if force:
1444 for f in merge:
1445 for f in merge:
1445 get[f] = merge[f][1]
1446 get[f] = merge[f][1]
1446 merge = {}
1447 merge = {}
1447
1448
1448 if linear_path or force:
1449 if linear_path or force:
1449 # we don't need to do any magic, just jump to the new rev
1450 # we don't need to do any magic, just jump to the new rev
1450 mode = 'n'
1451 mode = 'n'
1451 p1, p2 = p2, nullid
1452 p1, p2 = p2, nullid
1452 else:
1453 else:
1453 if not allow:
1454 if not allow:
1454 self.ui.status("this update spans a branch" +
1455 self.ui.status("this update spans a branch" +
1455 " affecting the following files:\n")
1456 " affecting the following files:\n")
1456 fl = merge.keys() + get.keys()
1457 fl = merge.keys() + get.keys()
1457 fl.sort()
1458 fl.sort()
1458 for f in fl:
1459 for f in fl:
1459 cf = ""
1460 cf = ""
1460 if f in merge: cf = " (resolve)"
1461 if f in merge: cf = " (resolve)"
1461 self.ui.status(" %s%s\n" % (f, cf))
1462 self.ui.status(" %s%s\n" % (f, cf))
1462 self.ui.warn("aborting update spanning branches!\n")
1463 self.ui.warn("aborting update spanning branches!\n")
1463 self.ui.status("(use update -m to perform a branch merge)\n")
1464 self.ui.status("(use update -m to perform a branch merge)\n")
1464 return 1
1465 return 1
1465 # we have to remember what files we needed to get/change
1466 # we have to remember what files we needed to get/change
1466 # because any file that's different from either one of its
1467 # because any file that's different from either one of its
1467 # parents must be in the changeset
1468 # parents must be in the changeset
1468 mode = 'm'
1469 mode = 'm'
1469 if moddirstate:
1470 if moddirstate:
1470 self.dirstate.update(mark.keys(), "m")
1471 self.dirstate.update(mark.keys(), "m")
1471
1472
1472 if moddirstate:
1473 if moddirstate:
1473 self.dirstate.setparents(p1, p2)
1474 self.dirstate.setparents(p1, p2)
1474
1475
1475 # get the files we don't need to change
1476 # get the files we don't need to change
1476 files = get.keys()
1477 files = get.keys()
1477 files.sort()
1478 files.sort()
1478 for f in files:
1479 for f in files:
1479 if f[0] == "/": continue
1480 if f[0] == "/": continue
1480 self.ui.note("getting %s\n" % f)
1481 self.ui.note("getting %s\n" % f)
1481 t = self.file(f).read(get[f])
1482 t = self.file(f).read(get[f])
1482 try:
1483 try:
1483 self.wfile(f, "w").write(t)
1484 self.wfile(f, "w").write(t)
1484 except IOError:
1485 except IOError:
1485 os.makedirs(os.path.dirname(self.wjoin(f)))
1486 os.makedirs(os.path.dirname(self.wjoin(f)))
1486 self.wfile(f, "w").write(t)
1487 self.wfile(f, "w").write(t)
1487 util.set_exec(self.wjoin(f), mf2[f])
1488 util.set_exec(self.wjoin(f), mf2[f])
1488 if moddirstate:
1489 if moddirstate:
1489 self.dirstate.update([f], mode)
1490 self.dirstate.update([f], mode)
1490
1491
1491 # merge the tricky bits
1492 # merge the tricky bits
1492 files = merge.keys()
1493 files = merge.keys()
1493 files.sort()
1494 files.sort()
1494 for f in files:
1495 for f in files:
1495 self.ui.status("merging %s\n" % f)
1496 self.ui.status("merging %s\n" % f)
1496 m, o, flag = merge[f]
1497 m, o, flag = merge[f]
1497 self.merge3(f, m, o)
1498 self.merge3(f, m, o)
1498 util.set_exec(self.wjoin(f), flag)
1499 util.set_exec(self.wjoin(f), flag)
1499 if moddirstate:
1500 if moddirstate:
1500 self.dirstate.update([f], 'm')
1501 self.dirstate.update([f], 'm')
1501
1502
1502 remove.sort()
1503 remove.sort()
1503 for f in remove:
1504 for f in remove:
1504 self.ui.note("removing %s\n" % f)
1505 self.ui.note("removing %s\n" % f)
1505 try:
1506 try:
1506 os.unlink(f)
1507 os.unlink(f)
1507 except OSError, inst:
1508 except OSError, inst:
1508 self.ui.warn("update failed to remove %s: %s!\n" % (f, inst))
1509 self.ui.warn("update failed to remove %s: %s!\n" % (f, inst))
1509 # try removing directories that might now be empty
1510 # try removing directories that might now be empty
1510 try: os.removedirs(os.path.dirname(f))
1511 try: os.removedirs(os.path.dirname(f))
1511 except: pass
1512 except: pass
1512 if moddirstate:
1513 if moddirstate:
1513 if mode == 'n':
1514 if mode == 'n':
1514 self.dirstate.forget(remove)
1515 self.dirstate.forget(remove)
1515 else:
1516 else:
1516 self.dirstate.update(remove, 'r')
1517 self.dirstate.update(remove, 'r')
1517
1518
1518 def merge3(self, fn, my, other):
1519 def merge3(self, fn, my, other):
1519 """perform a 3-way merge in the working directory"""
1520 """perform a 3-way merge in the working directory"""
1520
1521
1521 def temp(prefix, node):
1522 def temp(prefix, node):
1522 pre = "%s~%s." % (os.path.basename(fn), prefix)
1523 pre = "%s~%s." % (os.path.basename(fn), prefix)
1523 (fd, name) = tempfile.mkstemp("", pre)
1524 (fd, name) = tempfile.mkstemp("", pre)
1524 f = os.fdopen(fd, "wb")
1525 f = os.fdopen(fd, "wb")
1525 f.write(fl.revision(node))
1526 f.write(fl.revision(node))
1526 f.close()
1527 f.close()
1527 return name
1528 return name
1528
1529
1529 fl = self.file(fn)
1530 fl = self.file(fn)
1530 base = fl.ancestor(my, other)
1531 base = fl.ancestor(my, other)
1531 a = self.wjoin(fn)
1532 a = self.wjoin(fn)
1532 b = temp("base", base)
1533 b = temp("base", base)
1533 c = temp("other", other)
1534 c = temp("other", other)
1534
1535
1535 self.ui.note("resolving %s\n" % fn)
1536 self.ui.note("resolving %s\n" % fn)
1536 self.ui.debug("file %s: other %s ancestor %s\n" %
1537 self.ui.debug("file %s: other %s ancestor %s\n" %
1537 (fn, short(other), short(base)))
1538 (fn, short(other), short(base)))
1538
1539
1539 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1540 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1540 or "hgmerge")
1541 or "hgmerge")
1541 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1542 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1542 if r:
1543 if r:
1543 self.ui.warn("merging %s failed!\n" % fn)
1544 self.ui.warn("merging %s failed!\n" % fn)
1544
1545
1545 os.unlink(b)
1546 os.unlink(b)
1546 os.unlink(c)
1547 os.unlink(c)
1547
1548
1548 def verify(self):
1549 def verify(self):
1549 filelinkrevs = {}
1550 filelinkrevs = {}
1550 filenodes = {}
1551 filenodes = {}
1551 changesets = revisions = files = 0
1552 changesets = revisions = files = 0
1552 errors = 0
1553 errors = 0
1553
1554
1554 seen = {}
1555 seen = {}
1555 self.ui.status("checking changesets\n")
1556 self.ui.status("checking changesets\n")
1556 for i in range(self.changelog.count()):
1557 for i in range(self.changelog.count()):
1557 changesets += 1
1558 changesets += 1
1558 n = self.changelog.node(i)
1559 n = self.changelog.node(i)
1559 if n in seen:
1560 if n in seen:
1560 self.ui.warn("duplicate changeset at revision %d\n" % i)
1561 self.ui.warn("duplicate changeset at revision %d\n" % i)
1561 errors += 1
1562 errors += 1
1562 seen[n] = 1
1563 seen[n] = 1
1563
1564
1564 for p in self.changelog.parents(n):
1565 for p in self.changelog.parents(n):
1565 if p not in self.changelog.nodemap:
1566 if p not in self.changelog.nodemap:
1566 self.ui.warn("changeset %s has unknown parent %s\n" %
1567 self.ui.warn("changeset %s has unknown parent %s\n" %
1567 (short(n), short(p)))
1568 (short(n), short(p)))
1568 errors += 1
1569 errors += 1
1569 try:
1570 try:
1570 changes = self.changelog.read(n)
1571 changes = self.changelog.read(n)
1571 except Exception, inst:
1572 except Exception, inst:
1572 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1573 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1573 errors += 1
1574 errors += 1
1574
1575
1575 for f in changes[3]:
1576 for f in changes[3]:
1576 filelinkrevs.setdefault(f, []).append(i)
1577 filelinkrevs.setdefault(f, []).append(i)
1577
1578
1578 seen = {}
1579 seen = {}
1579 self.ui.status("checking manifests\n")
1580 self.ui.status("checking manifests\n")
1580 for i in range(self.manifest.count()):
1581 for i in range(self.manifest.count()):
1581 n = self.manifest.node(i)
1582 n = self.manifest.node(i)
1582 if n in seen:
1583 if n in seen:
1583 self.ui.warn("duplicate manifest at revision %d\n" % i)
1584 self.ui.warn("duplicate manifest at revision %d\n" % i)
1584 errors += 1
1585 errors += 1
1585 seen[n] = 1
1586 seen[n] = 1
1586
1587
1587 for p in self.manifest.parents(n):
1588 for p in self.manifest.parents(n):
1588 if p not in self.manifest.nodemap:
1589 if p not in self.manifest.nodemap:
1589 self.ui.warn("manifest %s has unknown parent %s\n" %
1590 self.ui.warn("manifest %s has unknown parent %s\n" %
1590 (short(n), short(p)))
1591 (short(n), short(p)))
1591 errors += 1
1592 errors += 1
1592
1593
1593 try:
1594 try:
1594 delta = mdiff.patchtext(self.manifest.delta(n))
1595 delta = mdiff.patchtext(self.manifest.delta(n))
1595 except KeyboardInterrupt:
1596 except KeyboardInterrupt:
1596 self.ui.warn("aborted")
1597 self.ui.warn("aborted")
1597 sys.exit(0)
1598 sys.exit(0)
1598 except Exception, inst:
1599 except Exception, inst:
1599 self.ui.warn("unpacking manifest %s: %s\n"
1600 self.ui.warn("unpacking manifest %s: %s\n"
1600 % (short(n), inst))
1601 % (short(n), inst))
1601 errors += 1
1602 errors += 1
1602
1603
1603 ff = [ l.split('\0') for l in delta.splitlines() ]
1604 ff = [ l.split('\0') for l in delta.splitlines() ]
1604 for f, fn in ff:
1605 for f, fn in ff:
1605 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1606 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1606
1607
1607 self.ui.status("crosschecking files in changesets and manifests\n")
1608 self.ui.status("crosschecking files in changesets and manifests\n")
1608 for f in filenodes:
1609 for f in filenodes:
1609 if f not in filelinkrevs:
1610 if f not in filelinkrevs:
1610 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1611 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1611 errors += 1
1612 errors += 1
1612
1613
1613 for f in filelinkrevs:
1614 for f in filelinkrevs:
1614 if f not in filenodes:
1615 if f not in filenodes:
1615 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1616 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1616 errors += 1
1617 errors += 1
1617
1618
1618 self.ui.status("checking files\n")
1619 self.ui.status("checking files\n")
1619 ff = filenodes.keys()
1620 ff = filenodes.keys()
1620 ff.sort()
1621 ff.sort()
1621 for f in ff:
1622 for f in ff:
1622 if f == "/dev/null": continue
1623 if f == "/dev/null": continue
1623 files += 1
1624 files += 1
1624 fl = self.file(f)
1625 fl = self.file(f)
1625 nodes = { nullid: 1 }
1626 nodes = { nullid: 1 }
1626 seen = {}
1627 seen = {}
1627 for i in range(fl.count()):
1628 for i in range(fl.count()):
1628 revisions += 1
1629 revisions += 1
1629 n = fl.node(i)
1630 n = fl.node(i)
1630
1631
1631 if n in seen:
1632 if n in seen:
1632 self.ui.warn("%s: duplicate revision %d\n" % (f, i))
1633 self.ui.warn("%s: duplicate revision %d\n" % (f, i))
1633 errors += 1
1634 errors += 1
1634
1635
1635 if n not in filenodes[f]:
1636 if n not in filenodes[f]:
1636 self.ui.warn("%s: %d:%s not in manifests\n"
1637 self.ui.warn("%s: %d:%s not in manifests\n"
1637 % (f, i, short(n)))
1638 % (f, i, short(n)))
1638 errors += 1
1639 errors += 1
1639 else:
1640 else:
1640 del filenodes[f][n]
1641 del filenodes[f][n]
1641
1642
1642 flr = fl.linkrev(n)
1643 flr = fl.linkrev(n)
1643 if flr not in filelinkrevs[f]:
1644 if flr not in filelinkrevs[f]:
1644 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1645 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1645 % (f, short(n), fl.linkrev(n)))
1646 % (f, short(n), fl.linkrev(n)))
1646 errors += 1
1647 errors += 1
1647 else:
1648 else:
1648 filelinkrevs[f].remove(flr)
1649 filelinkrevs[f].remove(flr)
1649
1650
1650 # verify contents
1651 # verify contents
1651 try:
1652 try:
1652 t = fl.read(n)
1653 t = fl.read(n)
1653 except Exception, inst:
1654 except Exception, inst:
1654 self.ui.warn("unpacking file %s %s: %s\n"
1655 self.ui.warn("unpacking file %s %s: %s\n"
1655 % (f, short(n), inst))
1656 % (f, short(n), inst))
1656 errors += 1
1657 errors += 1
1657
1658
1658 # verify parents
1659 # verify parents
1659 (p1, p2) = fl.parents(n)
1660 (p1, p2) = fl.parents(n)
1660 if p1 not in nodes:
1661 if p1 not in nodes:
1661 self.ui.warn("file %s:%s unknown parent 1 %s" %
1662 self.ui.warn("file %s:%s unknown parent 1 %s" %
1662 (f, short(n), short(p1)))
1663 (f, short(n), short(p1)))
1663 errors += 1
1664 errors += 1
1664 if p2 not in nodes:
1665 if p2 not in nodes:
1665 self.ui.warn("file %s:%s unknown parent 2 %s" %
1666 self.ui.warn("file %s:%s unknown parent 2 %s" %
1666 (f, short(n), short(p1)))
1667 (f, short(n), short(p1)))
1667 errors += 1
1668 errors += 1
1668 nodes[n] = 1
1669 nodes[n] = 1
1669
1670
1670 # cross-check
1671 # cross-check
1671 for node in filenodes[f]:
1672 for node in filenodes[f]:
1672 self.ui.warn("node %s in manifests not in %s\n"
1673 self.ui.warn("node %s in manifests not in %s\n"
1673 % (hex(n), f))
1674 % (hex(n), f))
1674 errors += 1
1675 errors += 1
1675
1676
1676 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1677 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1677 (files, changesets, revisions))
1678 (files, changesets, revisions))
1678
1679
1679 if errors:
1680 if errors:
1680 self.ui.warn("%d integrity errors encountered!\n" % errors)
1681 self.ui.warn("%d integrity errors encountered!\n" % errors)
1681 return 1
1682 return 1
1682
1683
1683 class httprepository:
1684 class httprepository:
1684 def __init__(self, ui, path):
1685 def __init__(self, ui, path):
1685 self.url = path
1686 self.url = path
1686 self.ui = ui
1687 self.ui = ui
1687 no_list = [ "localhost", "127.0.0.1" ]
1688 no_list = [ "localhost", "127.0.0.1" ]
1688 host = ui.config("http_proxy", "host")
1689 host = ui.config("http_proxy", "host")
1689 if host is None:
1690 if host is None:
1690 host = os.environ.get("http_proxy")
1691 host = os.environ.get("http_proxy")
1691 if host and host.startswith('http://'):
1692 if host and host.startswith('http://'):
1692 host = host[7:]
1693 host = host[7:]
1693 user = ui.config("http_proxy", "user")
1694 user = ui.config("http_proxy", "user")
1694 passwd = ui.config("http_proxy", "passwd")
1695 passwd = ui.config("http_proxy", "passwd")
1695 no = ui.config("http_proxy", "no")
1696 no = ui.config("http_proxy", "no")
1696 if no is None:
1697 if no is None:
1697 no = os.environ.get("no_proxy")
1698 no = os.environ.get("no_proxy")
1698 if no:
1699 if no:
1699 no_list = no_list + no.split(",")
1700 no_list = no_list + no.split(",")
1700
1701
1701 no_proxy = 0
1702 no_proxy = 0
1702 for h in no_list:
1703 for h in no_list:
1703 if (path.startswith("http://" + h + "/") or
1704 if (path.startswith("http://" + h + "/") or
1704 path.startswith("http://" + h + ":") or
1705 path.startswith("http://" + h + ":") or
1705 path == "http://" + h):
1706 path == "http://" + h):
1706 no_proxy = 1
1707 no_proxy = 1
1707
1708
1708 # Note: urllib2 takes proxy values from the environment and those will
1709 # Note: urllib2 takes proxy values from the environment and those will
1709 # take precedence
1710 # take precedence
1710 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
1711 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
1711 if os.environ.has_key(env):
1712 if os.environ.has_key(env):
1712 del os.environ[env]
1713 del os.environ[env]
1713
1714
1714 proxy_handler = urllib2.BaseHandler()
1715 proxy_handler = urllib2.BaseHandler()
1715 if host and not no_proxy:
1716 if host and not no_proxy:
1716 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
1717 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
1717
1718
1718 authinfo = None
1719 authinfo = None
1719 if user and passwd:
1720 if user and passwd:
1720 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1721 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1721 passmgr.add_password(None, host, user, passwd)
1722 passmgr.add_password(None, host, user, passwd)
1722 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
1723 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
1723
1724
1724 opener = urllib2.build_opener(proxy_handler, authinfo)
1725 opener = urllib2.build_opener(proxy_handler, authinfo)
1725 urllib2.install_opener(opener)
1726 urllib2.install_opener(opener)
1726
1727
1727 def dev(self):
1728 def dev(self):
1728 return -1
1729 return -1
1729
1730
1730 def do_cmd(self, cmd, **args):
1731 def do_cmd(self, cmd, **args):
1731 self.ui.debug("sending %s command\n" % cmd)
1732 self.ui.debug("sending %s command\n" % cmd)
1732 q = {"cmd": cmd}
1733 q = {"cmd": cmd}
1733 q.update(args)
1734 q.update(args)
1734 qs = urllib.urlencode(q)
1735 qs = urllib.urlencode(q)
1735 cu = "%s?%s" % (self.url, qs)
1736 cu = "%s?%s" % (self.url, qs)
1736 return urllib2.urlopen(cu)
1737 return urllib2.urlopen(cu)
1737
1738
1738 def heads(self):
1739 def heads(self):
1739 d = self.do_cmd("heads").read()
1740 d = self.do_cmd("heads").read()
1740 try:
1741 try:
1741 return map(bin, d[:-1].split(" "))
1742 return map(bin, d[:-1].split(" "))
1742 except:
1743 except:
1743 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1744 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1744 raise
1745 raise
1745
1746
1746 def branches(self, nodes):
1747 def branches(self, nodes):
1747 n = " ".join(map(hex, nodes))
1748 n = " ".join(map(hex, nodes))
1748 d = self.do_cmd("branches", nodes=n).read()
1749 d = self.do_cmd("branches", nodes=n).read()
1749 try:
1750 try:
1750 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1751 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1751 return br
1752 return br
1752 except:
1753 except:
1753 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1754 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1754 raise
1755 raise
1755
1756
1756 def between(self, pairs):
1757 def between(self, pairs):
1757 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1758 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1758 d = self.do_cmd("between", pairs=n).read()
1759 d = self.do_cmd("between", pairs=n).read()
1759 try:
1760 try:
1760 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1761 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1761 return p
1762 return p
1762 except:
1763 except:
1763 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1764 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1764 raise
1765 raise
1765
1766
1766 def changegroup(self, nodes):
1767 def changegroup(self, nodes):
1767 n = " ".join(map(hex, nodes))
1768 n = " ".join(map(hex, nodes))
1768 f = self.do_cmd("changegroup", roots=n)
1769 f = self.do_cmd("changegroup", roots=n)
1769 bytes = 0
1770 bytes = 0
1770
1771
1771 class zread:
1772 class zread:
1772 def __init__(self, f):
1773 def __init__(self, f):
1773 self.zd = zlib.decompressobj()
1774 self.zd = zlib.decompressobj()
1774 self.f = f
1775 self.f = f
1775 self.buf = ""
1776 self.buf = ""
1776 def read(self, l):
1777 def read(self, l):
1777 while l > len(self.buf):
1778 while l > len(self.buf):
1778 r = f.read(4096)
1779 r = f.read(4096)
1779 if r:
1780 if r:
1780 self.buf += self.zd.decompress(r)
1781 self.buf += self.zd.decompress(r)
1781 else:
1782 else:
1782 self.buf += self.zd.flush()
1783 self.buf += self.zd.flush()
1783 break
1784 break
1784 d, self.buf = self.buf[:l], self.buf[l:]
1785 d, self.buf = self.buf[:l], self.buf[l:]
1785 return d
1786 return d
1786
1787
1787 return zread(f)
1788 return zread(f)
1788
1789
1789 class remotelock:
1790 class remotelock:
1790 def __init__(self, repo):
1791 def __init__(self, repo):
1791 self.repo = repo
1792 self.repo = repo
1792 def release(self):
1793 def release(self):
1793 self.repo.unlock()
1794 self.repo.unlock()
1794 self.repo = None
1795 self.repo = None
1795 def __del__(self):
1796 def __del__(self):
1796 if self.repo:
1797 if self.repo:
1797 self.release()
1798 self.release()
1798
1799
1799 class sshrepository:
1800 class sshrepository:
1800 def __init__(self, ui, path):
1801 def __init__(self, ui, path):
1801 self.url = path
1802 self.url = path
1802 self.ui = ui
1803 self.ui = ui
1803
1804
1804 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
1805 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
1805 if not m:
1806 if not m:
1806 raise RepoError("couldn't parse destination %s\n" % path)
1807 raise RepoError("couldn't parse destination %s\n" % path)
1807
1808
1808 self.user = m.group(2)
1809 self.user = m.group(2)
1809 self.host = m.group(3)
1810 self.host = m.group(3)
1810 self.port = m.group(5)
1811 self.port = m.group(5)
1811 self.path = m.group(7)
1812 self.path = m.group(7)
1812
1813
1813 args = self.user and ("%s@%s" % (self.user, self.host)) or self.host
1814 args = self.user and ("%s@%s" % (self.user, self.host)) or self.host
1814 args = self.port and ("%s -p %s") % (args, self.port) or args
1815 args = self.port and ("%s -p %s") % (args, self.port) or args
1815 path = self.path or ""
1816 path = self.path or ""
1816
1817
1817 cmd = "ssh %s 'hg -R %s serve --stdio'"
1818 cmd = "ssh %s 'hg -R %s serve --stdio'"
1818 cmd = cmd % (args, path)
1819 cmd = cmd % (args, path)
1819
1820
1820 self.pipeo, self.pipei, self.pipee = os.popen3(cmd)
1821 self.pipeo, self.pipei, self.pipee = os.popen3(cmd)
1821
1822
1822 def readerr(self):
1823 def readerr(self):
1823 while 1:
1824 while 1:
1824 r,w,x = select.select([self.pipee], [], [], 0)
1825 r,w,x = select.select([self.pipee], [], [], 0)
1825 if not r: break
1826 if not r: break
1826 l = self.pipee.readline()
1827 l = self.pipee.readline()
1827 if not l: break
1828 if not l: break
1828 self.ui.status("remote: ", l)
1829 self.ui.status("remote: ", l)
1829
1830
1830 def __del__(self):
1831 def __del__(self):
1831 self.pipeo.close()
1832 self.pipeo.close()
1832 self.pipei.close()
1833 self.pipei.close()
1833 for l in self.pipee:
1834 for l in self.pipee:
1834 self.ui.status("remote: ", l)
1835 self.ui.status("remote: ", l)
1835 self.pipee.close()
1836 self.pipee.close()
1836
1837
1837 def dev(self):
1838 def dev(self):
1838 return -1
1839 return -1
1839
1840
1840 def do_cmd(self, cmd, **args):
1841 def do_cmd(self, cmd, **args):
1841 self.ui.debug("sending %s command\n" % cmd)
1842 self.ui.debug("sending %s command\n" % cmd)
1842 self.pipeo.write("%s\n" % cmd)
1843 self.pipeo.write("%s\n" % cmd)
1843 for k, v in args.items():
1844 for k, v in args.items():
1844 self.pipeo.write("%s %d\n" % (k, len(v)))
1845 self.pipeo.write("%s %d\n" % (k, len(v)))
1845 self.pipeo.write(v)
1846 self.pipeo.write(v)
1846 self.pipeo.flush()
1847 self.pipeo.flush()
1847
1848
1848 return self.pipei
1849 return self.pipei
1849
1850
1850 def call(self, cmd, **args):
1851 def call(self, cmd, **args):
1851 r = self.do_cmd(cmd, **args)
1852 r = self.do_cmd(cmd, **args)
1852 l = r.readline()
1853 l = r.readline()
1853 self.readerr()
1854 self.readerr()
1854 try:
1855 try:
1855 l = int(l)
1856 l = int(l)
1856 except:
1857 except:
1857 raise RepoError("unexpected response '%s'" % l)
1858 raise RepoError("unexpected response '%s'" % l)
1858 return r.read(l)
1859 return r.read(l)
1859
1860
1860 def lock(self):
1861 def lock(self):
1861 self.call("lock")
1862 self.call("lock")
1862 return remotelock(self)
1863 return remotelock(self)
1863
1864
1864 def unlock(self):
1865 def unlock(self):
1865 self.call("unlock")
1866 self.call("unlock")
1866
1867
1867 def heads(self):
1868 def heads(self):
1868 d = self.call("heads")
1869 d = self.call("heads")
1869 try:
1870 try:
1870 return map(bin, d[:-1].split(" "))
1871 return map(bin, d[:-1].split(" "))
1871 except:
1872 except:
1872 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1873 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1873
1874
1874 def branches(self, nodes):
1875 def branches(self, nodes):
1875 n = " ".join(map(hex, nodes))
1876 n = " ".join(map(hex, nodes))
1876 d = self.call("branches", nodes=n)
1877 d = self.call("branches", nodes=n)
1877 try:
1878 try:
1878 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1879 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1879 return br
1880 return br
1880 except:
1881 except:
1881 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1882 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1882
1883
1883 def between(self, pairs):
1884 def between(self, pairs):
1884 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1885 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1885 d = self.call("between", pairs=n)
1886 d = self.call("between", pairs=n)
1886 try:
1887 try:
1887 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1888 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1888 return p
1889 return p
1889 except:
1890 except:
1890 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1891 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1891
1892
1892 def changegroup(self, nodes):
1893 def changegroup(self, nodes):
1893 n = " ".join(map(hex, nodes))
1894 n = " ".join(map(hex, nodes))
1894 f = self.do_cmd("changegroup", roots=n)
1895 f = self.do_cmd("changegroup", roots=n)
1895 return self.pipei
1896 return self.pipei
1896
1897
1897 def addchangegroup(self, cg):
1898 def addchangegroup(self, cg):
1898 d = self.call("addchangegroup")
1899 d = self.call("addchangegroup")
1899 if d:
1900 if d:
1900 raise RepoError("push refused: %s", d)
1901 raise RepoError("push refused: %s", d)
1901
1902
1902 while 1:
1903 while 1:
1903 d = cg.read(4096)
1904 d = cg.read(4096)
1904 if not d: break
1905 if not d: break
1905 self.pipeo.write(d)
1906 self.pipeo.write(d)
1906 self.readerr()
1907 self.readerr()
1907
1908
1908 self.pipeo.flush()
1909 self.pipeo.flush()
1909
1910
1910 self.readerr()
1911 self.readerr()
1911 l = int(self.pipei.readline())
1912 l = int(self.pipei.readline())
1912 return self.pipei.read(l) != ""
1913 return self.pipei.read(l) != ""
1913
1914
1914 def repository(ui, path=None, create=0):
1915 def repository(ui, path=None, create=0):
1915 if path:
1916 if path:
1916 if path.startswith("http://"):
1917 if path.startswith("http://"):
1917 return httprepository(ui, path)
1918 return httprepository(ui, path)
1918 if path.startswith("hg://"):
1919 if path.startswith("hg://"):
1919 return httprepository(ui, path.replace("hg://", "http://"))
1920 return httprepository(ui, path.replace("hg://", "http://"))
1920 if path.startswith("old-http://"):
1921 if path.startswith("old-http://"):
1921 return localrepository(ui, path.replace("old-http://", "http://"))
1922 return localrepository(ui, path.replace("old-http://", "http://"))
1922 if path.startswith("ssh://"):
1923 if path.startswith("ssh://"):
1923 return sshrepository(ui, path)
1924 return sshrepository(ui, path)
1924
1925
1925 return localrepository(ui, path, create)
1926 return localrepository(ui, path, create)
@@ -1,126 +1,176 b''
1 # util.py - utility functions and platform specfic implementations
1 # util.py - utility functions and platform specfic implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import os, errno
8 import os, errno
9 from demandload import *
10 demandload(globals(), "re")
9
11
10 def unique(g):
12 def unique(g):
11 seen = {}
13 seen = {}
12 for f in g:
14 for f in g:
13 if f not in seen:
15 if f not in seen:
14 seen[f] = 1
16 seen[f] = 1
15 yield f
17 yield f
16
18
17 class CommandError(Exception): pass
19 class CommandError(Exception): pass
18
20
19 def explain_exit(code):
21 def explain_exit(code):
20 """return a 2-tuple (desc, code) describing a process's status"""
22 """return a 2-tuple (desc, code) describing a process's status"""
21 if os.WIFEXITED(code):
23 if os.WIFEXITED(code):
22 val = os.WEXITSTATUS(code)
24 val = os.WEXITSTATUS(code)
23 return "exited with status %d" % val, val
25 return "exited with status %d" % val, val
24 elif os.WIFSIGNALED(code):
26 elif os.WIFSIGNALED(code):
25 val = os.WTERMSIG(code)
27 val = os.WTERMSIG(code)
26 return "killed by signal %d" % val, val
28 return "killed by signal %d" % val, val
27 elif os.WIFSTOPPED(code):
29 elif os.WIFSTOPPED(code):
28 val = os.WSTOPSIG(code)
30 val = os.WSTOPSIG(code)
29 return "stopped by signal %d" % val, val
31 return "stopped by signal %d" % val, val
30 raise ValueError("invalid exit code")
32 raise ValueError("invalid exit code")
31
33
34 def always(fn): return True
35 def never(fn): return False
36
37 def globre(pat, head = '^', tail = '$'):
38 "convert a glob pattern into a regexp"
39 i, n = 0, len(pat)
40 res = ''
41 group = False
42 def peek(): return i < n and pat[i]
43 while i < n:
44 c = pat[i]
45 i = i+1
46 if c == '*':
47 if peek() == '*':
48 i += 1
49 res += '.*'
50 else:
51 res += '[^/]*'
52 elif c == '?':
53 res += '.'
54 elif c == '[':
55 j = i
56 if j < n and pat[j] in '!]':
57 j += 1
58 while j < n and pat[j] != ']':
59 j += 1
60 if j >= n:
61 res += '\\['
62 else:
63 stuff = pat[i:j].replace('\\','\\\\')
64 i = j + 1
65 if stuff[0] == '!':
66 stuff = '^' + stuff[1:]
67 elif stuff[0] == '^':
68 stuff = '\\' + stuff
69 res = '%s[%s]' % (res, stuff)
70 elif c == '{':
71 group = True
72 res += '(?:'
73 elif c == '}' and group:
74 res += ')'
75 group = False
76 elif c == ',' and group:
77 res += '|'
78 else:
79 res += re.escape(c)
80 return head + res + tail
81
32 def system(cmd, errprefix=None):
82 def system(cmd, errprefix=None):
33 """execute a shell command that must succeed"""
83 """execute a shell command that must succeed"""
34 rc = os.system(cmd)
84 rc = os.system(cmd)
35 if rc:
85 if rc:
36 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
86 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
37 explain_exit(rc)[0])
87 explain_exit(rc)[0])
38 if errprefix:
88 if errprefix:
39 errmsg = "%s: %s" % (errprefix, errmsg)
89 errmsg = "%s: %s" % (errprefix, errmsg)
40 raise CommandError(errmsg)
90 raise CommandError(errmsg)
41
91
42 def rename(src, dst):
92 def rename(src, dst):
43 try:
93 try:
44 os.rename(src, dst)
94 os.rename(src, dst)
45 except:
95 except:
46 os.unlink(dst)
96 os.unlink(dst)
47 os.rename(src, dst)
97 os.rename(src, dst)
48
98
49 def copytree(src, dst, copyfile):
99 def copytree(src, dst, copyfile):
50 """Copy a directory tree, files are copied using 'copyfile'."""
100 """Copy a directory tree, files are copied using 'copyfile'."""
51 names = os.listdir(src)
101 names = os.listdir(src)
52 os.mkdir(dst)
102 os.mkdir(dst)
53
103
54 for name in names:
104 for name in names:
55 srcname = os.path.join(src, name)
105 srcname = os.path.join(src, name)
56 dstname = os.path.join(dst, name)
106 dstname = os.path.join(dst, name)
57 if os.path.isdir(srcname):
107 if os.path.isdir(srcname):
58 copytree(srcname, dstname, copyfile)
108 copytree(srcname, dstname, copyfile)
59 elif os.path.isfile(srcname):
109 elif os.path.isfile(srcname):
60 copyfile(srcname, dstname)
110 copyfile(srcname, dstname)
61 else:
111 else:
62 raise IOError("Not a regular file: %r" % srcname)
112 raise IOError("Not a regular file: %r" % srcname)
63
113
64 def _makelock_file(info, pathname):
114 def _makelock_file(info, pathname):
65 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
115 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
66 os.write(ld, info)
116 os.write(ld, info)
67 os.close(ld)
117 os.close(ld)
68
118
69 def _readlock_file(pathname):
119 def _readlock_file(pathname):
70 return file(pathname).read()
120 return file(pathname).read()
71
121
72 # Platfor specific varients
122 # Platfor specific varients
73 if os.name == 'nt':
123 if os.name == 'nt':
74 nulldev = 'NUL:'
124 nulldev = 'NUL:'
75
125
76 def is_exec(f, last):
126 def is_exec(f, last):
77 return last
127 return last
78
128
79 def set_exec(f, mode):
129 def set_exec(f, mode):
80 pass
130 pass
81
131
82 def pconvert(path):
132 def pconvert(path):
83 return path.replace("\\", "/")
133 return path.replace("\\", "/")
84
134
85 makelock = _makelock_file
135 makelock = _makelock_file
86 readlock = _readlock_file
136 readlock = _readlock_file
87
137
88 else:
138 else:
89 nulldev = '/dev/null'
139 nulldev = '/dev/null'
90
140
91 def is_exec(f, last):
141 def is_exec(f, last):
92 return (os.stat(f).st_mode & 0100 != 0)
142 return (os.stat(f).st_mode & 0100 != 0)
93
143
94 def set_exec(f, mode):
144 def set_exec(f, mode):
95 s = os.stat(f).st_mode
145 s = os.stat(f).st_mode
96 if (s & 0100 != 0) == mode:
146 if (s & 0100 != 0) == mode:
97 return
147 return
98 if mode:
148 if mode:
99 # Turn on +x for every +r bit when making a file executable
149 # Turn on +x for every +r bit when making a file executable
100 # and obey umask.
150 # and obey umask.
101 umask = os.umask(0)
151 umask = os.umask(0)
102 os.umask(umask)
152 os.umask(umask)
103 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
153 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
104 else:
154 else:
105 os.chmod(f, s & 0666)
155 os.chmod(f, s & 0666)
106
156
107 def pconvert(path):
157 def pconvert(path):
108 return path
158 return path
109
159
110 def makelock(info, pathname):
160 def makelock(info, pathname):
111 try:
161 try:
112 os.symlink(info, pathname)
162 os.symlink(info, pathname)
113 except OSError, why:
163 except OSError, why:
114 if why.errno == errno.EEXIST:
164 if why.errno == errno.EEXIST:
115 raise
165 raise
116 else:
166 else:
117 _makelock_file(info, pathname)
167 _makelock_file(info, pathname)
118
168
119 def readlock(pathname):
169 def readlock(pathname):
120 try:
170 try:
121 return os.readlink(pathname)
171 return os.readlink(pathname)
122 except OSError, why:
172 except OSError, why:
123 if why.errno == errno.EINVAL:
173 if why.errno == errno.EINVAL:
124 return _readlock_file(pathname)
174 return _readlock_file(pathname)
125 else:
175 else:
126 raise
176 raise
General Comments 0
You need to be logged in to leave comments. Login now