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