##// END OF EJS Templates
sync with -stable
Thomas Arendsen Hein -
r4016:a195f11e merge default
parent child Browse files
Show More
@@ -1,515 +1,531 b''
1 1 HGRC(5)
2 2 =======
3 3 Bryan O'Sullivan <bos@serpentine.com>
4 4
5 5 NAME
6 6 ----
7 7 hgrc - configuration files for Mercurial
8 8
9 9 SYNOPSIS
10 10 --------
11 11
12 12 The Mercurial system uses a set of configuration files to control
13 13 aspects of its behaviour.
14 14
15 15 FILES
16 16 -----
17 17
18 18 Mercurial reads configuration data from several files, if they exist.
19 19 The names of these files depend on the system on which Mercurial is
20 20 installed.
21 21
22 22 (Unix) <install-root>/etc/mercurial/hgrc.d/*.rc::
23 23 (Unix) <install-root>/etc/mercurial/hgrc::
24 24 Per-installation configuration files, searched for in the
25 25 directory where Mercurial is installed. For example, if installed
26 26 in /shared/tools, Mercurial will look in
27 27 /shared/tools/etc/mercurial/hgrc. Options in these files apply to
28 28 all Mercurial commands executed by any user in any directory.
29 29
30 30 (Unix) /etc/mercurial/hgrc.d/*.rc::
31 31 (Unix) /etc/mercurial/hgrc::
32 32 (Windows) C:\Mercurial\Mercurial.ini::
33 33 Per-system configuration files, for the system on which Mercurial
34 34 is running. Options in these files apply to all Mercurial
35 35 commands executed by any user in any directory. Options in these
36 36 files override per-installation options.
37 37
38 38 (Unix) $HOME/.hgrc::
39 39 (Windows) C:\Documents and Settings\USERNAME\Mercurial.ini::
40 40 (Windows) $HOME\Mercurial.ini::
41 41 Per-user configuration file, for the user running Mercurial.
42 42 Options in this file apply to all Mercurial commands executed by
43 43 any user in any directory. Options in this file override
44 44 per-installation and per-system options.
45 45 On Windows system, one of these is chosen exclusively according
46 46 to definition of HOME environment variable.
47 47
48 48 (Unix, Windows) <repo>/.hg/hgrc::
49 49 Per-repository configuration options that only apply in a
50 50 particular repository. This file is not version-controlled, and
51 51 will not get transferred during a "clone" operation. Options in
52 52 this file override options in all other configuration files.
53 53 On Unix, most of this file will be ignored if it doesn't belong
54 54 to a trusted user or to a trusted group. See the documentation
55 55 for the trusted section below for more details.
56 56
57 57 SYNTAX
58 58 ------
59 59
60 60 A configuration file consists of sections, led by a "[section]" header
61 61 and followed by "name: value" entries; "name=value" is also accepted.
62 62
63 63 [spam]
64 64 eggs=ham
65 65 green=
66 66 eggs
67 67
68 68 Each line contains one entry. If the lines that follow are indented,
69 69 they are treated as continuations of that entry.
70 70
71 71 Leading whitespace is removed from values. Empty lines are skipped.
72 72
73 73 The optional values can contain format strings which refer to other
74 74 values in the same section, or values in a special DEFAULT section.
75 75
76 76 Lines beginning with "#" or ";" are ignored and may be used to provide
77 77 comments.
78 78
79 79 SECTIONS
80 80 --------
81 81
82 82 This section describes the different sections that may appear in a
83 83 Mercurial "hgrc" file, the purpose of each section, its possible
84 84 keys, and their possible values.
85 85
86 86 decode/encode::
87 87 Filters for transforming files on checkout/checkin. This would
88 88 typically be used for newline processing or other
89 89 localization/canonicalization of files.
90 90
91 91 Filters consist of a filter pattern followed by a filter command.
92 92 Filter patterns are globs by default, rooted at the repository
93 93 root. For example, to match any file ending in ".txt" in the root
94 94 directory only, use the pattern "*.txt". To match any file ending
95 95 in ".c" anywhere in the repository, use the pattern "**.c".
96 96
97 97 The filter command can start with a specifier, either "pipe:" or
98 98 "tempfile:". If no specifier is given, "pipe:" is used by default.
99 99
100 100 A "pipe:" command must accept data on stdin and return the
101 101 transformed data on stdout.
102 102
103 103 Pipe example:
104 104
105 105 [encode]
106 106 # uncompress gzip files on checkin to improve delta compression
107 107 # note: not necessarily a good idea, just an example
108 108 *.gz = pipe: gunzip
109 109
110 110 [decode]
111 111 # recompress gzip files when writing them to the working dir (we
112 112 # can safely omit "pipe:", because it's the default)
113 113 *.gz = gzip
114 114
115 115 A "tempfile:" command is a template. The string INFILE is replaced
116 116 with the name of a temporary file that contains the data to be
117 117 filtered by the command. The string OUTFILE is replaced with the
118 118 name of an empty temporary file, where the filtered data must be
119 119 written by the command.
120 120
121 121 NOTE: the tempfile mechanism is recommended for Windows systems,
122 122 where the standard shell I/O redirection operators often have
123 123 strange effects. In particular, if you are doing line ending
124 124 conversion on Windows using the popular dos2unix and unix2dos
125 125 programs, you *must* use the tempfile mechanism, as using pipes will
126 126 corrupt the contents of your files.
127 127
128 128 Tempfile example:
129 129
130 130 [encode]
131 131 # convert files to unix line ending conventions on checkin
132 132 **.txt = tempfile: dos2unix -n INFILE OUTFILE
133 133
134 134 [decode]
135 135 # convert files to windows line ending conventions when writing
136 136 # them to the working dir
137 137 **.txt = tempfile: unix2dos -n INFILE OUTFILE
138 138
139 139 defaults::
140 140 Use the [defaults] section to define command defaults, i.e. the
141 141 default options/arguments to pass to the specified commands.
142 142
143 143 The following example makes 'hg log' run in verbose mode, and
144 144 'hg status' show only the modified files, by default.
145 145
146 146 [defaults]
147 147 log = -v
148 148 status = -m
149 149
150 150 The actual commands, instead of their aliases, must be used when
151 151 defining command defaults. The command defaults will also be
152 152 applied to the aliases of the commands defined.
153 153
154 diff::
155 Settings used when displaying diffs. They are all boolean and
156 defaults to False.
157 git;;
158 Use git extended diff format.
159 nodates;;
160 Don't include dates in diff headers.
161 showfunc;;
162 Show which function each change is in.
163 ignorews;;
164 Ignore white space when comparing lines.
165 ignorewsamount;;
166 Ignore changes in the amount of white space.
167 ignoreblanklines;;
168 Ignore changes whose lines are all blank.
169
154 170 email::
155 171 Settings for extensions that send email messages.
156 172 from;;
157 173 Optional. Email address to use in "From" header and SMTP envelope
158 174 of outgoing messages.
159 175 to;;
160 176 Optional. Comma-separated list of recipients' email addresses.
161 177 cc;;
162 178 Optional. Comma-separated list of carbon copy recipients'
163 179 email addresses.
164 180 bcc;;
165 181 Optional. Comma-separated list of blind carbon copy
166 182 recipients' email addresses. Cannot be set interactively.
167 183 method;;
168 184 Optional. Method to use to send email messages. If value is
169 185 "smtp" (default), use SMTP (see section "[smtp]" for
170 186 configuration). Otherwise, use as name of program to run that
171 187 acts like sendmail (takes "-f" option for sender, list of
172 188 recipients on command line, message on stdin). Normally, setting
173 189 this to "sendmail" or "/usr/sbin/sendmail" is enough to use
174 190 sendmail to send messages.
175 191
176 192 Email example:
177 193
178 194 [email]
179 195 from = Joseph User <joe.user@example.com>
180 196 method = /usr/sbin/sendmail
181 197
182 198 extensions::
183 199 Mercurial has an extension mechanism for adding new features. To
184 200 enable an extension, create an entry for it in this section.
185 201
186 202 If you know that the extension is already in Python's search path,
187 203 you can give the name of the module, followed by "=", with nothing
188 204 after the "=".
189 205
190 206 Otherwise, give a name that you choose, followed by "=", followed by
191 207 the path to the ".py" file (including the file name extension) that
192 208 defines the extension.
193 209
194 210 Example for ~/.hgrc:
195 211
196 212 [extensions]
197 213 # (the mq extension will get loaded from mercurial's path)
198 214 hgext.mq =
199 215 # (this extension will get loaded from the file specified)
200 216 myfeature = ~/.hgext/myfeature.py
201 217
202 218 hooks::
203 219 Commands or Python functions that get automatically executed by
204 220 various actions such as starting or finishing a commit. Multiple
205 221 hooks can be run for the same action by appending a suffix to the
206 222 action. Overriding a site-wide hook can be done by changing its
207 223 value or setting it to an empty string.
208 224
209 225 Example .hg/hgrc:
210 226
211 227 [hooks]
212 228 # do not use the site-wide hook
213 229 incoming =
214 230 incoming.email = /my/email/hook
215 231 incoming.autobuild = /my/build/hook
216 232
217 233 Most hooks are run with environment variables set that give added
218 234 useful information. For each hook below, the environment variables
219 235 it is passed are listed with names of the form "$HG_foo".
220 236
221 237 changegroup;;
222 238 Run after a changegroup has been added via push, pull or
223 239 unbundle. ID of the first new changeset is in $HG_NODE. URL from
224 240 which changes came is in $HG_URL.
225 241 commit;;
226 242 Run after a changeset has been created in the local repository.
227 243 ID of the newly created changeset is in $HG_NODE. Parent
228 244 changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
229 245 incoming;;
230 246 Run after a changeset has been pulled, pushed, or unbundled into
231 247 the local repository. The ID of the newly arrived changeset is in
232 248 $HG_NODE. URL that was source of changes came is in $HG_URL.
233 249 outgoing;;
234 250 Run after sending changes from local repository to another. ID of
235 251 first changeset sent is in $HG_NODE. Source of operation is in
236 252 $HG_SOURCE; see "preoutgoing" hook for description.
237 253 prechangegroup;;
238 254 Run before a changegroup is added via push, pull or unbundle.
239 255 Exit status 0 allows the changegroup to proceed. Non-zero status
240 256 will cause the push, pull or unbundle to fail. URL from which
241 257 changes will come is in $HG_URL.
242 258 precommit;;
243 259 Run before starting a local commit. Exit status 0 allows the
244 260 commit to proceed. Non-zero status will cause the commit to fail.
245 261 Parent changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
246 262 preoutgoing;;
247 263 Run before computing changes to send from the local repository to
248 264 another. Non-zero status will cause failure. This lets you
249 265 prevent pull over http or ssh. Also prevents against local pull,
250 266 push (outbound) or bundle commands, but not effective, since you
251 267 can just copy files instead then. Source of operation is in
252 268 $HG_SOURCE. If "serve", operation is happening on behalf of
253 269 remote ssh or http repository. If "push", "pull" or "bundle",
254 270 operation is happening on behalf of repository on same system.
255 271 pretag;;
256 272 Run before creating a tag. Exit status 0 allows the tag to be
257 273 created. Non-zero status will cause the tag to fail. ID of
258 274 changeset to tag is in $HG_NODE. Name of tag is in $HG_TAG. Tag
259 275 is local if $HG_LOCAL=1, in repo if $HG_LOCAL=0.
260 276 pretxnchangegroup;;
261 277 Run after a changegroup has been added via push, pull or unbundle,
262 278 but before the transaction has been committed. Changegroup is
263 279 visible to hook program. This lets you validate incoming changes
264 280 before accepting them. Passed the ID of the first new changeset
265 281 in $HG_NODE. Exit status 0 allows the transaction to commit.
266 282 Non-zero status will cause the transaction to be rolled back and
267 283 the push, pull or unbundle will fail. URL that was source of
268 284 changes is in $HG_URL.
269 285 pretxncommit;;
270 286 Run after a changeset has been created but the transaction not yet
271 287 committed. Changeset is visible to hook program. This lets you
272 288 validate commit message and changes. Exit status 0 allows the
273 289 commit to proceed. Non-zero status will cause the transaction to
274 290 be rolled back. ID of changeset is in $HG_NODE. Parent changeset
275 291 IDs are in $HG_PARENT1 and $HG_PARENT2.
276 292 preupdate;;
277 293 Run before updating the working directory. Exit status 0 allows
278 294 the update to proceed. Non-zero status will prevent the update.
279 295 Changeset ID of first new parent is in $HG_PARENT1. If merge, ID
280 296 of second new parent is in $HG_PARENT2.
281 297 tag;;
282 298 Run after a tag is created. ID of tagged changeset is in
283 299 $HG_NODE. Name of tag is in $HG_TAG. Tag is local if
284 300 $HG_LOCAL=1, in repo if $HG_LOCAL=0.
285 301 update;;
286 302 Run after updating the working directory. Changeset ID of first
287 303 new parent is in $HG_PARENT1. If merge, ID of second new parent
288 304 is in $HG_PARENT2. If update succeeded, $HG_ERROR=0. If update
289 305 failed (e.g. because conflicts not resolved), $HG_ERROR=1.
290 306
291 307 Note: In earlier releases, the names of hook environment variables
292 308 did not have a "HG_" prefix. The old unprefixed names are no longer
293 309 provided in the environment.
294 310
295 311 The syntax for Python hooks is as follows:
296 312
297 313 hookname = python:modulename.submodule.callable
298 314
299 315 Python hooks are run within the Mercurial process. Each hook is
300 316 called with at least three keyword arguments: a ui object (keyword
301 317 "ui"), a repository object (keyword "repo"), and a "hooktype"
302 318 keyword that tells what kind of hook is used. Arguments listed as
303 319 environment variables above are passed as keyword arguments, with no
304 320 "HG_" prefix, and names in lower case.
305 321
306 322 If a Python hook returns a "true" value or raises an exception, this
307 323 is treated as failure of the hook.
308 324
309 325 http_proxy::
310 326 Used to access web-based Mercurial repositories through a HTTP
311 327 proxy.
312 328 host;;
313 329 Host name and (optional) port of the proxy server, for example
314 330 "myproxy:8000".
315 331 no;;
316 332 Optional. Comma-separated list of host names that should bypass
317 333 the proxy.
318 334 passwd;;
319 335 Optional. Password to authenticate with at the proxy server.
320 336 user;;
321 337 Optional. User name to authenticate with at the proxy server.
322 338
323 339 smtp::
324 340 Configuration for extensions that need to send email messages.
325 341 host;;
326 342 Host name of mail server, e.g. "mail.example.com".
327 343 port;;
328 344 Optional. Port to connect to on mail server. Default: 25.
329 345 tls;;
330 346 Optional. Whether to connect to mail server using TLS. True or
331 347 False. Default: False.
332 348 username;;
333 349 Optional. User name to authenticate to SMTP server with.
334 350 If username is specified, password must also be specified.
335 351 Default: none.
336 352 password;;
337 353 Optional. Password to authenticate to SMTP server with.
338 354 If username is specified, password must also be specified.
339 355 Default: none.
340 356 local_hostname;;
341 357 Optional. It's the hostname that the sender can use to identify itself
342 358 to the MTA.
343 359
344 360 paths::
345 361 Assigns symbolic names to repositories. The left side is the
346 362 symbolic name, and the right gives the directory or URL that is the
347 363 location of the repository. Default paths can be declared by
348 364 setting the following entries.
349 365 default;;
350 366 Directory or URL to use when pulling if no source is specified.
351 367 Default is set to repository from which the current repository
352 368 was cloned.
353 369 default-push;;
354 370 Optional. Directory or URL to use when pushing if no destination
355 371 is specified.
356 372
357 373 server::
358 374 Controls generic server settings.
359 375 uncompressed;;
360 376 Whether to allow clients to clone a repo using the uncompressed
361 377 streaming protocol. This transfers about 40% more data than a
362 378 regular clone, but uses less memory and CPU on both server and
363 379 client. Over a LAN (100Mbps or better) or a very fast WAN, an
364 380 uncompressed streaming clone is a lot faster (~10x) than a regular
365 381 clone. Over most WAN connections (anything slower than about
366 382 6Mbps), uncompressed streaming is slower, because of the extra
367 383 data transfer overhead. Default is False.
368 384
369 385 trusted::
370 386 For security reasons, Mercurial will not use the settings in
371 387 the .hg/hgrc file from a repository if it doesn't belong to a
372 388 trusted user or to a trusted group. The main exception is the
373 389 web interface, which automatically uses some safe settings, since
374 390 it's common to serve repositories from different users.
375 391
376 392 This section specifies what users and groups are trusted. The
377 393 current user is always trusted. To trust everybody, list a user
378 394 or a group with name "*".
379 395
380 396 users;;
381 397 Comma-separated list of trusted users.
382 398 groups;;
383 399 Comma-separated list of trusted groups.
384 400
385 401 ui::
386 402 User interface controls.
387 403 debug;;
388 404 Print debugging information. True or False. Default is False.
389 405 editor;;
390 406 The editor to use during a commit. Default is $EDITOR or "vi".
391 407 fallbackencoding;;
392 408 Encoding to try if it's not possible to decode the changelog using
393 409 UTF-8. Default is ISO-8859-1.
394 410 ignore;;
395 411 A file to read per-user ignore patterns from. This file should be in
396 412 the same format as a repository-wide .hgignore file. This option
397 413 supports hook syntax, so if you want to specify multiple ignore
398 414 files, you can do so by setting something like
399 415 "ignore.other = ~/.hgignore2". For details of the ignore file
400 416 format, see the hgignore(5) man page.
401 417 interactive;;
402 418 Allow to prompt the user. True or False. Default is True.
403 419 logtemplate;;
404 420 Template string for commands that print changesets.
405 421 style;;
406 422 Name of style to use for command output.
407 423 merge;;
408 424 The conflict resolution program to use during a manual merge.
409 425 Default is "hgmerge".
410 426 quiet;;
411 427 Reduce the amount of output printed. True or False. Default is False.
412 428 remotecmd;;
413 429 remote command to use for clone/push/pull operations. Default is 'hg'.
414 430 ssh;;
415 431 command to use for SSH connections. Default is 'ssh'.
416 432 strict;;
417 433 Require exact command names, instead of allowing unambiguous
418 434 abbreviations. True or False. Default is False.
419 435 timeout;;
420 436 The timeout used when a lock is held (in seconds), a negative value
421 437 means no timeout. Default is 600.
422 438 username;;
423 439 The committer of a changeset created when running "commit".
424 440 Typically a person's name and email address, e.g. "Fred Widget
425 441 <fred@example.com>". Default is $EMAIL or username@hostname.
426 442 verbose;;
427 443 Increase the amount of output printed. True or False. Default is False.
428 444
429 445
430 446 web::
431 447 Web interface configuration.
432 448 accesslog;;
433 449 Where to output the access log. Default is stdout.
434 450 address;;
435 451 Interface address to bind to. Default is all.
436 452 allow_archive;;
437 453 List of archive format (bz2, gz, zip) allowed for downloading.
438 454 Default is empty.
439 455 allowbz2;;
440 456 (DEPRECATED) Whether to allow .tar.bz2 downloading of repo revisions.
441 457 Default is false.
442 458 allowgz;;
443 459 (DEPRECATED) Whether to allow .tar.gz downloading of repo revisions.
444 460 Default is false.
445 461 allowpull;;
446 462 Whether to allow pulling from the repository. Default is true.
447 463 allow_push;;
448 464 Whether to allow pushing to the repository. If empty or not set,
449 465 push is not allowed. If the special value "*", any remote user
450 466 can push, including unauthenticated users. Otherwise, the remote
451 467 user must have been authenticated, and the authenticated user name
452 468 must be present in this list (separated by whitespace or ",").
453 469 The contents of the allow_push list are examined after the
454 470 deny_push list.
455 471 allowzip;;
456 472 (DEPRECATED) Whether to allow .zip downloading of repo revisions.
457 473 Default is false. This feature creates temporary files.
458 474 baseurl;;
459 475 Base URL to use when publishing URLs in other locations, so
460 476 third-party tools like email notification hooks can construct URLs.
461 477 Example: "http://hgserver/repos/"
462 478 contact;;
463 479 Name or email address of the person in charge of the repository.
464 480 Default is "unknown".
465 481 deny_push;;
466 482 Whether to deny pushing to the repository. If empty or not set,
467 483 push is not denied. If the special value "*", all remote users
468 484 are denied push. Otherwise, unauthenticated users are all denied,
469 485 and any authenticated user name present in this list (separated by
470 486 whitespace or ",") is also denied. The contents of the deny_push
471 487 list are examined before the allow_push list.
472 488 description;;
473 489 Textual description of the repository's purpose or contents.
474 490 Default is "unknown".
475 491 errorlog;;
476 492 Where to output the error log. Default is stderr.
477 493 ipv6;;
478 494 Whether to use IPv6. Default is false.
479 495 name;;
480 496 Repository name to use in the web interface. Default is current
481 497 working directory.
482 498 maxchanges;;
483 499 Maximum number of changes to list on the changelog. Default is 10.
484 500 maxfiles;;
485 501 Maximum number of files to list per changeset. Default is 10.
486 502 port;;
487 503 Port to listen on. Default is 8000.
488 504 push_ssl;;
489 505 Whether to require that inbound pushes be transported over SSL to
490 506 prevent password sniffing. Default is true.
491 507 stripes;;
492 508 How many lines a "zebra stripe" should span in multiline output.
493 509 Default is 1; set to 0 to disable.
494 510 style;;
495 511 Which template map style to use.
496 512 templates;;
497 513 Where to find the HTML templates. Default is install path.
498 514
499 515
500 516 AUTHOR
501 517 ------
502 518 Bryan O'Sullivan <bos@serpentine.com>.
503 519
504 520 Mercurial was written by Matt Mackall <mpm@selenic.com>.
505 521
506 522 SEE ALSO
507 523 --------
508 524 hg(1), hgignore(5)
509 525
510 526 COPYING
511 527 -------
512 528 This manual page is copyright 2005 Bryan O'Sullivan.
513 529 Mercurial is copyright 2005, 2006 Matt Mackall.
514 530 Free use of this software is granted under the terms of the GNU General
515 531 Public License (GPL).
@@ -1,2181 +1,2181 b''
1 1 # queue.py - patch queues for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 '''patch management and development
9 9
10 10 This extension lets you work with a stack of patches in a Mercurial
11 11 repository. It manages two stacks of patches - all known patches, and
12 12 applied patches (subset of known patches).
13 13
14 14 Known patches are represented as patch files in the .hg/patches
15 15 directory. Applied patches are both patch files and changesets.
16 16
17 17 Common tasks (use "hg help command" for more details):
18 18
19 19 prepare repository to work with patches qinit
20 20 create new patch qnew
21 21 import existing patch qimport
22 22
23 23 print patch series qseries
24 24 print applied patches qapplied
25 25 print name of top applied patch qtop
26 26
27 27 add known patch to applied stack qpush
28 28 remove patch from applied stack qpop
29 29 refresh contents of top applied patch qrefresh
30 30 '''
31 31
32 32 from mercurial.i18n import _
33 33 from mercurial import commands, cmdutil, hg, patch, revlog, util, changegroup
34 34 import os, sys, re, errno
35 35
36 36 commands.norepo += " qclone qversion"
37 37
38 38 class statusentry:
39 39 def __init__(self, rev, name=None):
40 40 if not name:
41 41 fields = rev.split(':', 1)
42 42 if len(fields) == 2:
43 43 self.rev, self.name = fields
44 44 else:
45 45 self.rev, self.name = None, None
46 46 else:
47 47 self.rev, self.name = rev, name
48 48
49 49 def __str__(self):
50 50 return self.rev + ':' + self.name
51 51
52 52 class queue:
53 53 def __init__(self, ui, path, patchdir=None):
54 54 self.basepath = path
55 55 self.path = patchdir or os.path.join(path, "patches")
56 56 self.opener = util.opener(self.path)
57 57 self.ui = ui
58 58 self.applied = []
59 59 self.full_series = []
60 60 self.applied_dirty = 0
61 61 self.series_dirty = 0
62 62 self.series_path = "series"
63 63 self.status_path = "status"
64 64 self.guards_path = "guards"
65 65 self.active_guards = None
66 66 self.guards_dirty = False
67 67 self._diffopts = None
68 68
69 69 if os.path.exists(self.join(self.series_path)):
70 70 self.full_series = self.opener(self.series_path).read().splitlines()
71 71 self.parse_series()
72 72
73 73 if os.path.exists(self.join(self.status_path)):
74 74 lines = self.opener(self.status_path).read().splitlines()
75 75 self.applied = [statusentry(l) for l in lines]
76 76
77 77 def diffopts(self):
78 78 if self._diffopts is None:
79 79 self._diffopts = patch.diffopts(self.ui)
80 80 return self._diffopts
81 81
82 82 def join(self, *p):
83 83 return os.path.join(self.path, *p)
84 84
85 85 def find_series(self, patch):
86 86 pre = re.compile("(\s*)([^#]+)")
87 87 index = 0
88 88 for l in self.full_series:
89 89 m = pre.match(l)
90 90 if m:
91 91 s = m.group(2)
92 92 s = s.rstrip()
93 93 if s == patch:
94 94 return index
95 95 index += 1
96 96 return None
97 97
98 98 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
99 99
100 100 def parse_series(self):
101 101 self.series = []
102 102 self.series_guards = []
103 103 for l in self.full_series:
104 104 h = l.find('#')
105 105 if h == -1:
106 106 patch = l
107 107 comment = ''
108 108 elif h == 0:
109 109 continue
110 110 else:
111 111 patch = l[:h]
112 112 comment = l[h:]
113 113 patch = patch.strip()
114 114 if patch:
115 115 if patch in self.series:
116 116 raise util.Abort(_('%s appears more than once in %s') %
117 117 (patch, self.join(self.series_path)))
118 118 self.series.append(patch)
119 119 self.series_guards.append(self.guard_re.findall(comment))
120 120
121 121 def check_guard(self, guard):
122 122 bad_chars = '# \t\r\n\f'
123 123 first = guard[0]
124 124 for c in '-+':
125 125 if first == c:
126 126 return (_('guard %r starts with invalid character: %r') %
127 127 (guard, c))
128 128 for c in bad_chars:
129 129 if c in guard:
130 130 return _('invalid character in guard %r: %r') % (guard, c)
131 131
132 132 def set_active(self, guards):
133 133 for guard in guards:
134 134 bad = self.check_guard(guard)
135 135 if bad:
136 136 raise util.Abort(bad)
137 137 guards = dict.fromkeys(guards).keys()
138 138 guards.sort()
139 139 self.ui.debug('active guards: %s\n' % ' '.join(guards))
140 140 self.active_guards = guards
141 141 self.guards_dirty = True
142 142
143 143 def active(self):
144 144 if self.active_guards is None:
145 145 self.active_guards = []
146 146 try:
147 147 guards = self.opener(self.guards_path).read().split()
148 148 except IOError, err:
149 149 if err.errno != errno.ENOENT: raise
150 150 guards = []
151 151 for i, guard in enumerate(guards):
152 152 bad = self.check_guard(guard)
153 153 if bad:
154 154 self.ui.warn('%s:%d: %s\n' %
155 155 (self.join(self.guards_path), i + 1, bad))
156 156 else:
157 157 self.active_guards.append(guard)
158 158 return self.active_guards
159 159
160 160 def set_guards(self, idx, guards):
161 161 for g in guards:
162 162 if len(g) < 2:
163 163 raise util.Abort(_('guard %r too short') % g)
164 164 if g[0] not in '-+':
165 165 raise util.Abort(_('guard %r starts with invalid char') % g)
166 166 bad = self.check_guard(g[1:])
167 167 if bad:
168 168 raise util.Abort(bad)
169 169 drop = self.guard_re.sub('', self.full_series[idx])
170 170 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
171 171 self.parse_series()
172 172 self.series_dirty = True
173 173
174 174 def pushable(self, idx):
175 175 if isinstance(idx, str):
176 176 idx = self.series.index(idx)
177 177 patchguards = self.series_guards[idx]
178 178 if not patchguards:
179 179 return True, None
180 180 default = False
181 181 guards = self.active()
182 182 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
183 183 if exactneg:
184 184 return False, exactneg[0]
185 185 pos = [g for g in patchguards if g[0] == '+']
186 186 exactpos = [g for g in pos if g[1:] in guards]
187 187 if pos:
188 188 if exactpos:
189 189 return True, exactpos[0]
190 190 return False, pos
191 191 return True, ''
192 192
193 193 def explain_pushable(self, idx, all_patches=False):
194 194 write = all_patches and self.ui.write or self.ui.warn
195 195 if all_patches or self.ui.verbose:
196 196 if isinstance(idx, str):
197 197 idx = self.series.index(idx)
198 198 pushable, why = self.pushable(idx)
199 199 if all_patches and pushable:
200 200 if why is None:
201 201 write(_('allowing %s - no guards in effect\n') %
202 202 self.series[idx])
203 203 else:
204 204 if not why:
205 205 write(_('allowing %s - no matching negative guards\n') %
206 206 self.series[idx])
207 207 else:
208 208 write(_('allowing %s - guarded by %r\n') %
209 209 (self.series[idx], why))
210 210 if not pushable:
211 211 if why:
212 212 write(_('skipping %s - guarded by %r\n') %
213 213 (self.series[idx], why))
214 214 else:
215 215 write(_('skipping %s - no matching guards\n') %
216 216 self.series[idx])
217 217
218 218 def save_dirty(self):
219 219 def write_list(items, path):
220 220 fp = self.opener(path, 'w')
221 221 for i in items:
222 222 print >> fp, i
223 223 fp.close()
224 224 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
225 225 if self.series_dirty: write_list(self.full_series, self.series_path)
226 226 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
227 227
228 228 def readheaders(self, patch):
229 229 def eatdiff(lines):
230 230 while lines:
231 231 l = lines[-1]
232 232 if (l.startswith("diff -") or
233 233 l.startswith("Index:") or
234 234 l.startswith("===========")):
235 235 del lines[-1]
236 236 else:
237 237 break
238 238 def eatempty(lines):
239 239 while lines:
240 240 l = lines[-1]
241 241 if re.match('\s*$', l):
242 242 del lines[-1]
243 243 else:
244 244 break
245 245
246 246 pf = self.join(patch)
247 247 message = []
248 248 comments = []
249 249 user = None
250 250 date = None
251 251 format = None
252 252 subject = None
253 253 diffstart = 0
254 254
255 255 for line in file(pf):
256 256 line = line.rstrip()
257 257 if line.startswith('diff --git'):
258 258 diffstart = 2
259 259 break
260 260 if diffstart:
261 261 if line.startswith('+++ '):
262 262 diffstart = 2
263 263 break
264 264 if line.startswith("--- "):
265 265 diffstart = 1
266 266 continue
267 267 elif format == "hgpatch":
268 268 # parse values when importing the result of an hg export
269 269 if line.startswith("# User "):
270 270 user = line[7:]
271 271 elif line.startswith("# Date "):
272 272 date = line[7:]
273 273 elif not line.startswith("# ") and line:
274 274 message.append(line)
275 275 format = None
276 276 elif line == '# HG changeset patch':
277 277 format = "hgpatch"
278 278 elif (format != "tagdone" and (line.startswith("Subject: ") or
279 279 line.startswith("subject: "))):
280 280 subject = line[9:]
281 281 format = "tag"
282 282 elif (format != "tagdone" and (line.startswith("From: ") or
283 283 line.startswith("from: "))):
284 284 user = line[6:]
285 285 format = "tag"
286 286 elif format == "tag" and line == "":
287 287 # when looking for tags (subject: from: etc) they
288 288 # end once you find a blank line in the source
289 289 format = "tagdone"
290 290 elif message or line:
291 291 message.append(line)
292 292 comments.append(line)
293 293
294 294 eatdiff(message)
295 295 eatdiff(comments)
296 296 eatempty(message)
297 297 eatempty(comments)
298 298
299 299 # make sure message isn't empty
300 300 if format and format.startswith("tag") and subject:
301 301 message.insert(0, "")
302 302 message.insert(0, subject)
303 303 return (message, comments, user, date, diffstart > 1)
304 304
305 305 def printdiff(self, repo, node1, node2=None, files=None,
306 306 fp=None, changes=None, opts={}):
307 307 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
308 308
309 309 patch.diff(repo, node1, node2, fns, match=matchfn,
310 310 fp=fp, changes=changes, opts=self.diffopts())
311 311
312 312 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
313 313 # first try just applying the patch
314 314 (err, n) = self.apply(repo, [ patch ], update_status=False,
315 315 strict=True, merge=rev, wlock=wlock)
316 316
317 317 if err == 0:
318 318 return (err, n)
319 319
320 320 if n is None:
321 321 raise util.Abort(_("apply failed for patch %s") % patch)
322 322
323 323 self.ui.warn("patch didn't work out, merging %s\n" % patch)
324 324
325 325 # apply failed, strip away that rev and merge.
326 326 hg.clean(repo, head, wlock=wlock)
327 327 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
328 328
329 329 ctx = repo.changectx(rev)
330 330 ret = hg.merge(repo, rev, wlock=wlock)
331 331 if ret:
332 332 raise util.Abort(_("update returned %d") % ret)
333 333 n = repo.commit(None, ctx.description(), ctx.user(),
334 334 force=1, wlock=wlock)
335 335 if n == None:
336 336 raise util.Abort(_("repo commit failed"))
337 337 try:
338 338 message, comments, user, date, patchfound = mergeq.readheaders(patch)
339 339 except:
340 340 raise util.Abort(_("unable to read %s") % patch)
341 341
342 342 patchf = self.opener(patch, "w")
343 343 if comments:
344 344 comments = "\n".join(comments) + '\n\n'
345 345 patchf.write(comments)
346 346 self.printdiff(repo, head, n, fp=patchf)
347 347 patchf.close()
348 348 return (0, n)
349 349
350 350 def qparents(self, repo, rev=None):
351 351 if rev is None:
352 352 (p1, p2) = repo.dirstate.parents()
353 353 if p2 == revlog.nullid:
354 354 return p1
355 355 if len(self.applied) == 0:
356 356 return None
357 357 return revlog.bin(self.applied[-1].rev)
358 358 pp = repo.changelog.parents(rev)
359 359 if pp[1] != revlog.nullid:
360 360 arevs = [ x.rev for x in self.applied ]
361 361 p0 = revlog.hex(pp[0])
362 362 p1 = revlog.hex(pp[1])
363 363 if p0 in arevs:
364 364 return pp[0]
365 365 if p1 in arevs:
366 366 return pp[1]
367 367 return pp[0]
368 368
369 369 def mergepatch(self, repo, mergeq, series, wlock):
370 370 if len(self.applied) == 0:
371 371 # each of the patches merged in will have two parents. This
372 372 # can confuse the qrefresh, qdiff, and strip code because it
373 373 # needs to know which parent is actually in the patch queue.
374 374 # so, we insert a merge marker with only one parent. This way
375 375 # the first patch in the queue is never a merge patch
376 376 #
377 377 pname = ".hg.patches.merge.marker"
378 378 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
379 379 wlock=wlock)
380 380 self.applied.append(statusentry(revlog.hex(n), pname))
381 381 self.applied_dirty = 1
382 382
383 383 head = self.qparents(repo)
384 384
385 385 for patch in series:
386 386 patch = mergeq.lookup(patch, strict=True)
387 387 if not patch:
388 388 self.ui.warn("patch %s does not exist\n" % patch)
389 389 return (1, None)
390 390 pushable, reason = self.pushable(patch)
391 391 if not pushable:
392 392 self.explain_pushable(patch, all_patches=True)
393 393 continue
394 394 info = mergeq.isapplied(patch)
395 395 if not info:
396 396 self.ui.warn("patch %s is not applied\n" % patch)
397 397 return (1, None)
398 398 rev = revlog.bin(info[1])
399 399 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
400 400 if head:
401 401 self.applied.append(statusentry(revlog.hex(head), patch))
402 402 self.applied_dirty = 1
403 403 if err:
404 404 return (err, head)
405 405 return (0, head)
406 406
407 407 def patch(self, repo, patchfile):
408 408 '''Apply patchfile to the working directory.
409 409 patchfile: file name of patch'''
410 410 files = {}
411 411 try:
412 412 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
413 413 files=files)
414 414 except Exception, inst:
415 415 self.ui.note(str(inst) + '\n')
416 416 if not self.ui.verbose:
417 417 self.ui.warn("patch failed, unable to continue (try -v)\n")
418 418 return (False, files, False)
419 419
420 420 return (True, files, fuzz)
421 421
422 422 def apply(self, repo, series, list=False, update_status=True,
423 423 strict=False, patchdir=None, merge=None, wlock=None):
424 424 # TODO unify with commands.py
425 425 if not patchdir:
426 426 patchdir = self.path
427 427 err = 0
428 428 if not wlock:
429 429 wlock = repo.wlock()
430 430 lock = repo.lock()
431 431 tr = repo.transaction()
432 432 n = None
433 433 for patchname in series:
434 434 pushable, reason = self.pushable(patchname)
435 435 if not pushable:
436 436 self.explain_pushable(patchname, all_patches=True)
437 437 continue
438 438 self.ui.warn("applying %s\n" % patchname)
439 439 pf = os.path.join(patchdir, patchname)
440 440
441 441 try:
442 442 message, comments, user, date, patchfound = self.readheaders(patchname)
443 443 except:
444 444 self.ui.warn("Unable to read %s\n" % patchname)
445 445 err = 1
446 446 break
447 447
448 448 if not message:
449 449 message = "imported patch %s\n" % patchname
450 450 else:
451 451 if list:
452 452 message.append("\nimported patch %s" % patchname)
453 453 message = '\n'.join(message)
454 454
455 455 (patcherr, files, fuzz) = self.patch(repo, pf)
456 456 patcherr = not patcherr
457 457
458 458 if merge and files:
459 459 # Mark as merged and update dirstate parent info
460 460 repo.dirstate.update(repo.dirstate.filterfiles(files.keys()), 'm')
461 461 p1, p2 = repo.dirstate.parents()
462 462 repo.dirstate.setparents(p1, merge)
463 463 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
464 464 n = repo.commit(files, message, user, date, force=1, lock=lock,
465 465 wlock=wlock)
466 466
467 467 if n == None:
468 468 raise util.Abort(_("repo commit failed"))
469 469
470 470 if update_status:
471 471 self.applied.append(statusentry(revlog.hex(n), patchname))
472 472
473 473 if patcherr:
474 474 if not patchfound:
475 475 self.ui.warn("patch %s is empty\n" % patchname)
476 476 err = 0
477 477 else:
478 478 self.ui.warn("patch failed, rejects left in working dir\n")
479 479 err = 1
480 480 break
481 481
482 482 if fuzz and strict:
483 483 self.ui.warn("fuzz found when applying patch, stopping\n")
484 484 err = 1
485 485 break
486 486 tr.close()
487 487 return (err, n)
488 488
489 489 def delete(self, repo, patches, opts):
490 490 realpatches = []
491 491 for patch in patches:
492 492 patch = self.lookup(patch, strict=True)
493 493 info = self.isapplied(patch)
494 494 if info:
495 495 raise util.Abort(_("cannot delete applied patch %s") % patch)
496 496 if patch not in self.series:
497 497 raise util.Abort(_("patch %s not in series file") % patch)
498 498 realpatches.append(patch)
499 499
500 500 appliedbase = 0
501 501 if opts.get('rev'):
502 502 if not self.applied:
503 503 raise util.Abort(_('no patches applied'))
504 504 revs = cmdutil.revrange(repo, opts['rev'])
505 505 if len(revs) > 1 and revs[0] > revs[1]:
506 506 revs.reverse()
507 507 for rev in revs:
508 508 if appliedbase >= len(self.applied):
509 509 raise util.Abort(_("revision %d is not managed") % rev)
510 510
511 511 base = revlog.bin(self.applied[appliedbase].rev)
512 512 node = repo.changelog.node(rev)
513 513 if node != base:
514 514 raise util.Abort(_("cannot delete revision %d above "
515 515 "applied patches") % rev)
516 516 realpatches.append(self.applied[appliedbase].name)
517 517 appliedbase += 1
518 518
519 519 if not opts.get('keep'):
520 520 r = self.qrepo()
521 521 if r:
522 522 r.remove(realpatches, True)
523 523 else:
524 524 for p in realpatches:
525 525 os.unlink(self.join(p))
526 526
527 527 if appliedbase:
528 528 del self.applied[:appliedbase]
529 529 self.applied_dirty = 1
530 530 indices = [self.find_series(p) for p in realpatches]
531 531 indices.sort()
532 532 for i in indices[-1::-1]:
533 533 del self.full_series[i]
534 534 self.parse_series()
535 535 self.series_dirty = 1
536 536
537 537 def check_toppatch(self, repo):
538 538 if len(self.applied) > 0:
539 539 top = revlog.bin(self.applied[-1].rev)
540 540 pp = repo.dirstate.parents()
541 541 if top not in pp:
542 542 raise util.Abort(_("queue top not at same revision as working directory"))
543 543 return top
544 544 return None
545 545 def check_localchanges(self, repo, force=False, refresh=True):
546 546 m, a, r, d = repo.status()[:4]
547 547 if m or a or r or d:
548 548 if not force:
549 549 if refresh:
550 550 raise util.Abort(_("local changes found, refresh first"))
551 551 else:
552 552 raise util.Abort(_("local changes found"))
553 553 return m, a, r, d
554 554 def new(self, repo, patch, msg=None, force=None):
555 555 if os.path.exists(self.join(patch)):
556 556 raise util.Abort(_('patch "%s" already exists') % patch)
557 557 m, a, r, d = self.check_localchanges(repo, force)
558 558 commitfiles = m + a + r
559 559 self.check_toppatch(repo)
560 560 wlock = repo.wlock()
561 561 insert = self.full_series_end()
562 562 if msg:
563 563 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
564 564 wlock=wlock)
565 565 else:
566 566 n = repo.commit(commitfiles,
567 567 "New patch: %s" % patch, force=True, wlock=wlock)
568 568 if n == None:
569 569 raise util.Abort(_("repo commit failed"))
570 570 self.full_series[insert:insert] = [patch]
571 571 self.applied.append(statusentry(revlog.hex(n), patch))
572 572 self.parse_series()
573 573 self.series_dirty = 1
574 574 self.applied_dirty = 1
575 575 p = self.opener(patch, "w")
576 576 if msg:
577 577 msg = msg + "\n"
578 578 p.write(msg)
579 579 p.close()
580 580 wlock = None
581 581 r = self.qrepo()
582 582 if r: r.add([patch])
583 583 if commitfiles:
584 584 self.refresh(repo, short=True)
585 585
586 586 def strip(self, repo, rev, update=True, backup="all", wlock=None):
587 587 def limitheads(chlog, stop):
588 588 """return the list of all nodes that have no children"""
589 589 p = {}
590 590 h = []
591 591 stoprev = 0
592 592 if stop in chlog.nodemap:
593 593 stoprev = chlog.rev(stop)
594 594
595 595 for r in xrange(chlog.count() - 1, -1, -1):
596 596 n = chlog.node(r)
597 597 if n not in p:
598 598 h.append(n)
599 599 if n == stop:
600 600 break
601 601 if r < stoprev:
602 602 break
603 603 for pn in chlog.parents(n):
604 604 p[pn] = 1
605 605 return h
606 606
607 607 def bundle(cg):
608 608 backupdir = repo.join("strip-backup")
609 609 if not os.path.isdir(backupdir):
610 610 os.mkdir(backupdir)
611 611 name = os.path.join(backupdir, "%s" % revlog.short(rev))
612 612 name = savename(name)
613 613 self.ui.warn("saving bundle to %s\n" % name)
614 614 return changegroup.writebundle(cg, name, "HG10BZ")
615 615
616 616 def stripall(revnum):
617 617 mm = repo.changectx(rev).manifest()
618 618 seen = {}
619 619
620 620 for x in xrange(revnum, repo.changelog.count()):
621 621 for f in repo.changectx(x).files():
622 622 if f in seen:
623 623 continue
624 624 seen[f] = 1
625 625 if f in mm:
626 626 filerev = mm[f]
627 627 else:
628 628 filerev = 0
629 629 seen[f] = filerev
630 630 # we go in two steps here so the strip loop happens in a
631 631 # sensible order. When stripping many files, this helps keep
632 632 # our disk access patterns under control.
633 633 seen_list = seen.keys()
634 634 seen_list.sort()
635 635 for f in seen_list:
636 636 ff = repo.file(f)
637 637 filerev = seen[f]
638 638 if filerev != 0:
639 639 if filerev in ff.nodemap:
640 640 filerev = ff.rev(filerev)
641 641 else:
642 642 filerev = 0
643 643 ff.strip(filerev, revnum)
644 644
645 645 if not wlock:
646 646 wlock = repo.wlock()
647 647 lock = repo.lock()
648 648 chlog = repo.changelog
649 649 # TODO delete the undo files, and handle undo of merge sets
650 650 pp = chlog.parents(rev)
651 651 revnum = chlog.rev(rev)
652 652
653 653 if update:
654 654 self.check_localchanges(repo, refresh=False)
655 655 urev = self.qparents(repo, rev)
656 656 hg.clean(repo, urev, wlock=wlock)
657 657 repo.dirstate.write()
658 658
659 659 # save is a list of all the branches we are truncating away
660 660 # that we actually want to keep. changegroup will be used
661 661 # to preserve them and add them back after the truncate
662 662 saveheads = []
663 663 savebases = {}
664 664
665 665 heads = limitheads(chlog, rev)
666 666 seen = {}
667 667
668 668 # search through all the heads, finding those where the revision
669 669 # we want to strip away is an ancestor. Also look for merges
670 670 # that might be turned into new heads by the strip.
671 671 while heads:
672 672 h = heads.pop()
673 673 n = h
674 674 while True:
675 675 seen[n] = 1
676 676 pp = chlog.parents(n)
677 677 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
678 678 if pp[1] not in seen:
679 679 heads.append(pp[1])
680 680 if pp[0] == revlog.nullid:
681 681 break
682 682 if chlog.rev(pp[0]) < revnum:
683 683 break
684 684 n = pp[0]
685 685 if n == rev:
686 686 break
687 687 r = chlog.reachable(h, rev)
688 688 if rev not in r:
689 689 saveheads.append(h)
690 690 for x in r:
691 691 if chlog.rev(x) > revnum:
692 692 savebases[x] = 1
693 693
694 694 # create a changegroup for all the branches we need to keep
695 695 if backup == "all":
696 696 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
697 697 bundle(backupch)
698 698 if saveheads:
699 699 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
700 700 chgrpfile = bundle(backupch)
701 chgrpfile = 'file:%s' % chgrpfile
702 701
703 702 stripall(revnum)
704 703
705 704 change = chlog.read(rev)
706 705 chlog.strip(revnum, revnum)
707 706 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
708 707 if saveheads:
709 708 self.ui.status("adding branch\n")
710 commands.unbundle(self.ui, repo, chgrpfile, update=False)
709 commands.unbundle(self.ui, repo, "file:%s" % chgrpfile,
710 update=False)
711 711 if backup != "strip":
712 712 os.unlink(chgrpfile)
713 713
714 714 def isapplied(self, patch):
715 715 """returns (index, rev, patch)"""
716 716 for i in xrange(len(self.applied)):
717 717 a = self.applied[i]
718 718 if a.name == patch:
719 719 return (i, a.rev, a.name)
720 720 return None
721 721
722 722 # if the exact patch name does not exist, we try a few
723 723 # variations. If strict is passed, we try only #1
724 724 #
725 725 # 1) a number to indicate an offset in the series file
726 726 # 2) a unique substring of the patch name was given
727 727 # 3) patchname[-+]num to indicate an offset in the series file
728 728 def lookup(self, patch, strict=False):
729 729 patch = patch and str(patch)
730 730
731 731 def partial_name(s):
732 732 if s in self.series:
733 733 return s
734 734 matches = [x for x in self.series if s in x]
735 735 if len(matches) > 1:
736 736 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
737 737 for m in matches:
738 738 self.ui.warn(' %s\n' % m)
739 739 return None
740 740 if matches:
741 741 return matches[0]
742 742 if len(self.series) > 0 and len(self.applied) > 0:
743 743 if s == 'qtip':
744 744 return self.series[self.series_end(True)-1]
745 745 if s == 'qbase':
746 746 return self.series[0]
747 747 return None
748 748 if patch == None:
749 749 return None
750 750
751 751 # we don't want to return a partial match until we make
752 752 # sure the file name passed in does not exist (checked below)
753 753 res = partial_name(patch)
754 754 if res and res == patch:
755 755 return res
756 756
757 757 if not os.path.isfile(self.join(patch)):
758 758 try:
759 759 sno = int(patch)
760 760 except(ValueError, OverflowError):
761 761 pass
762 762 else:
763 763 if sno < len(self.series):
764 764 return self.series[sno]
765 765 if not strict:
766 766 # return any partial match made above
767 767 if res:
768 768 return res
769 769 minus = patch.rfind('-')
770 770 if minus >= 0:
771 771 res = partial_name(patch[:minus])
772 772 if res:
773 773 i = self.series.index(res)
774 774 try:
775 775 off = int(patch[minus+1:] or 1)
776 776 except(ValueError, OverflowError):
777 777 pass
778 778 else:
779 779 if i - off >= 0:
780 780 return self.series[i - off]
781 781 plus = patch.rfind('+')
782 782 if plus >= 0:
783 783 res = partial_name(patch[:plus])
784 784 if res:
785 785 i = self.series.index(res)
786 786 try:
787 787 off = int(patch[plus+1:] or 1)
788 788 except(ValueError, OverflowError):
789 789 pass
790 790 else:
791 791 if i + off < len(self.series):
792 792 return self.series[i + off]
793 793 raise util.Abort(_("patch %s not in series") % patch)
794 794
795 795 def push(self, repo, patch=None, force=False, list=False,
796 796 mergeq=None, wlock=None):
797 797 if not wlock:
798 798 wlock = repo.wlock()
799 799 patch = self.lookup(patch)
800 800 if patch and self.isapplied(patch):
801 801 raise util.Abort(_("patch %s is already applied") % patch)
802 802 if self.series_end() == len(self.series):
803 803 raise util.Abort(_("patch series fully applied"))
804 804 if not force:
805 805 self.check_localchanges(repo)
806 806
807 807 self.applied_dirty = 1;
808 808 start = self.series_end()
809 809 if start > 0:
810 810 self.check_toppatch(repo)
811 811 if not patch:
812 812 patch = self.series[start]
813 813 end = start + 1
814 814 else:
815 815 end = self.series.index(patch, start) + 1
816 816 s = self.series[start:end]
817 817 if mergeq:
818 818 ret = self.mergepatch(repo, mergeq, s, wlock)
819 819 else:
820 820 ret = self.apply(repo, s, list, wlock=wlock)
821 821 top = self.applied[-1].name
822 822 if ret[0]:
823 823 self.ui.write("Errors during apply, please fix and refresh %s\n" %
824 824 top)
825 825 else:
826 826 self.ui.write("Now at: %s\n" % top)
827 827 return ret[0]
828 828
829 829 def pop(self, repo, patch=None, force=False, update=True, all=False,
830 830 wlock=None):
831 831 def getfile(f, rev):
832 832 t = repo.file(f).read(rev)
833 833 repo.wfile(f, "w").write(t)
834 834
835 835 if not wlock:
836 836 wlock = repo.wlock()
837 837 if patch:
838 838 # index, rev, patch
839 839 info = self.isapplied(patch)
840 840 if not info:
841 841 patch = self.lookup(patch)
842 842 info = self.isapplied(patch)
843 843 if not info:
844 844 raise util.Abort(_("patch %s is not applied") % patch)
845 845 if len(self.applied) == 0:
846 846 raise util.Abort(_("no patches applied"))
847 847
848 848 if not update:
849 849 parents = repo.dirstate.parents()
850 850 rr = [ revlog.bin(x.rev) for x in self.applied ]
851 851 for p in parents:
852 852 if p in rr:
853 853 self.ui.warn("qpop: forcing dirstate update\n")
854 854 update = True
855 855
856 856 if not force and update:
857 857 self.check_localchanges(repo)
858 858
859 859 self.applied_dirty = 1;
860 860 end = len(self.applied)
861 861 if not patch:
862 862 if all:
863 863 popi = 0
864 864 else:
865 865 popi = len(self.applied) - 1
866 866 else:
867 867 popi = info[0] + 1
868 868 if popi >= end:
869 869 self.ui.warn("qpop: %s is already at the top\n" % patch)
870 870 return
871 871 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
872 872
873 873 start = info[0]
874 874 rev = revlog.bin(info[1])
875 875
876 876 # we know there are no local changes, so we can make a simplified
877 877 # form of hg.update.
878 878 if update:
879 879 top = self.check_toppatch(repo)
880 880 qp = self.qparents(repo, rev)
881 881 changes = repo.changelog.read(qp)
882 882 mmap = repo.manifest.read(changes[0])
883 883 m, a, r, d, u = repo.status(qp, top)[:5]
884 884 if d:
885 885 raise util.Abort("deletions found between repo revs")
886 886 for f in m:
887 887 getfile(f, mmap[f])
888 888 for f in r:
889 889 getfile(f, mmap[f])
890 890 util.set_exec(repo.wjoin(f), mmap.execf(f))
891 891 repo.dirstate.update(m + r, 'n')
892 892 for f in a:
893 893 try:
894 894 os.unlink(repo.wjoin(f))
895 895 except OSError, e:
896 896 if e.errno != errno.ENOENT:
897 897 raise
898 898 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
899 899 except: pass
900 900 if a:
901 901 repo.dirstate.forget(a)
902 902 repo.dirstate.setparents(qp, revlog.nullid)
903 903 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
904 904 del self.applied[start:end]
905 905 if len(self.applied):
906 906 self.ui.write("Now at: %s\n" % self.applied[-1].name)
907 907 else:
908 908 self.ui.write("Patch queue now empty\n")
909 909
910 910 def diff(self, repo, pats, opts):
911 911 top = self.check_toppatch(repo)
912 912 if not top:
913 913 self.ui.write("No patches applied\n")
914 914 return
915 915 qp = self.qparents(repo, top)
916 916 if opts.get('git'):
917 917 self.diffopts().git = True
918 918 self.printdiff(repo, qp, files=pats, opts=opts)
919 919
920 920 def refresh(self, repo, pats=None, **opts):
921 921 if len(self.applied) == 0:
922 922 self.ui.write("No patches applied\n")
923 923 return 1
924 924 wlock = repo.wlock()
925 925 self.check_toppatch(repo)
926 926 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
927 927 top = revlog.bin(top)
928 928 cparents = repo.changelog.parents(top)
929 929 patchparent = self.qparents(repo, top)
930 930 message, comments, user, date, patchfound = self.readheaders(patchfn)
931 931
932 932 patchf = self.opener(patchfn, "w")
933 933 msg = opts.get('msg', '').rstrip()
934 934 if msg:
935 935 if comments:
936 936 # Remove existing message.
937 937 ci = 0
938 938 for mi in xrange(len(message)):
939 939 while message[mi] != comments[ci]:
940 940 ci += 1
941 941 del comments[ci]
942 942 comments.append(msg)
943 943 if comments:
944 944 comments = "\n".join(comments) + '\n\n'
945 945 patchf.write(comments)
946 946
947 947 if opts.get('git'):
948 948 self.diffopts().git = True
949 949 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
950 950 tip = repo.changelog.tip()
951 951 if top == tip:
952 952 # if the top of our patch queue is also the tip, there is an
953 953 # optimization here. We update the dirstate in place and strip
954 954 # off the tip commit. Then just commit the current directory
955 955 # tree. We can also send repo.commit the list of files
956 956 # changed to speed up the diff
957 957 #
958 958 # in short mode, we only diff the files included in the
959 959 # patch already
960 960 #
961 961 # this should really read:
962 962 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
963 963 # but we do it backwards to take advantage of manifest/chlog
964 964 # caching against the next repo.status call
965 965 #
966 966 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
967 967 changes = repo.changelog.read(tip)
968 968 man = repo.manifest.read(changes[0])
969 969 aaa = aa[:]
970 970 if opts.get('short'):
971 971 filelist = mm + aa + dd
972 972 else:
973 973 filelist = None
974 974 m, a, r, d, u = repo.status(files=filelist)[:5]
975 975
976 976 # we might end up with files that were added between tip and
977 977 # the dirstate parent, but then changed in the local dirstate.
978 978 # in this case, we want them to only show up in the added section
979 979 for x in m:
980 980 if x not in aa:
981 981 mm.append(x)
982 982 # we might end up with files added by the local dirstate that
983 983 # were deleted by the patch. In this case, they should only
984 984 # show up in the changed section.
985 985 for x in a:
986 986 if x in dd:
987 987 del dd[dd.index(x)]
988 988 mm.append(x)
989 989 else:
990 990 aa.append(x)
991 991 # make sure any files deleted in the local dirstate
992 992 # are not in the add or change column of the patch
993 993 forget = []
994 994 for x in d + r:
995 995 if x in aa:
996 996 del aa[aa.index(x)]
997 997 forget.append(x)
998 998 continue
999 999 elif x in mm:
1000 1000 del mm[mm.index(x)]
1001 1001 dd.append(x)
1002 1002
1003 1003 m = util.unique(mm)
1004 1004 r = util.unique(dd)
1005 1005 a = util.unique(aa)
1006 1006 filelist = filter(matchfn, util.unique(m + r + a))
1007 1007 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1008 1008 fp=patchf, changes=(m, a, r, [], u),
1009 1009 opts=self.diffopts())
1010 1010 patchf.close()
1011 1011
1012 1012 repo.dirstate.setparents(*cparents)
1013 1013 copies = {}
1014 1014 for dst in a:
1015 1015 src = repo.dirstate.copied(dst)
1016 1016 if src is None:
1017 1017 continue
1018 1018 copies.setdefault(src, []).append(dst)
1019 1019 repo.dirstate.update(a, 'a')
1020 1020 # remember the copies between patchparent and tip
1021 1021 # this may be slow, so don't do it if we're not tracking copies
1022 1022 if self.diffopts().git:
1023 1023 for dst in aaa:
1024 1024 f = repo.file(dst)
1025 1025 src = f.renamed(man[dst])
1026 1026 if src:
1027 1027 copies[src[0]] = copies.get(dst, [])
1028 1028 if dst in a:
1029 1029 copies[src[0]].append(dst)
1030 1030 # we can't copy a file created by the patch itself
1031 1031 if dst in copies:
1032 1032 del copies[dst]
1033 1033 for src, dsts in copies.iteritems():
1034 1034 for dst in dsts:
1035 1035 repo.dirstate.copy(src, dst)
1036 1036 repo.dirstate.update(r, 'r')
1037 1037 # if the patch excludes a modified file, mark that file with mtime=0
1038 1038 # so status can see it.
1039 1039 mm = []
1040 1040 for i in xrange(len(m)-1, -1, -1):
1041 1041 if not matchfn(m[i]):
1042 1042 mm.append(m[i])
1043 1043 del m[i]
1044 1044 repo.dirstate.update(m, 'n')
1045 1045 repo.dirstate.update(mm, 'n', st_mtime=0)
1046 1046 repo.dirstate.forget(forget)
1047 1047
1048 1048 if not msg:
1049 1049 if not message:
1050 1050 message = "patch queue: %s\n" % patchfn
1051 1051 else:
1052 1052 message = "\n".join(message)
1053 1053 else:
1054 1054 message = msg
1055 1055
1056 1056 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1057 1057 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
1058 1058 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1059 1059 self.applied_dirty = 1
1060 1060 else:
1061 1061 self.printdiff(repo, patchparent, fp=patchf)
1062 1062 patchf.close()
1063 1063 added = repo.status()[1]
1064 1064 for a in added:
1065 1065 f = repo.wjoin(a)
1066 1066 try:
1067 1067 os.unlink(f)
1068 1068 except OSError, e:
1069 1069 if e.errno != errno.ENOENT:
1070 1070 raise
1071 1071 try: os.removedirs(os.path.dirname(f))
1072 1072 except: pass
1073 1073 # forget the file copies in the dirstate
1074 1074 # push should readd the files later on
1075 1075 repo.dirstate.forget(added)
1076 1076 self.pop(repo, force=True, wlock=wlock)
1077 1077 self.push(repo, force=True, wlock=wlock)
1078 1078
1079 1079 def init(self, repo, create=False):
1080 1080 if os.path.isdir(self.path):
1081 1081 raise util.Abort(_("patch queue directory already exists"))
1082 1082 os.mkdir(self.path)
1083 1083 if create:
1084 1084 return self.qrepo(create=True)
1085 1085
1086 1086 def unapplied(self, repo, patch=None):
1087 1087 if patch and patch not in self.series:
1088 1088 raise util.Abort(_("patch %s is not in series file") % patch)
1089 1089 if not patch:
1090 1090 start = self.series_end()
1091 1091 else:
1092 1092 start = self.series.index(patch) + 1
1093 1093 unapplied = []
1094 1094 for i in xrange(start, len(self.series)):
1095 1095 pushable, reason = self.pushable(i)
1096 1096 if pushable:
1097 1097 unapplied.append((i, self.series[i]))
1098 1098 self.explain_pushable(i)
1099 1099 return unapplied
1100 1100
1101 1101 def qseries(self, repo, missing=None, start=0, length=0, status=None,
1102 1102 summary=False):
1103 1103 def displayname(patchname):
1104 1104 if summary:
1105 1105 msg = self.readheaders(patchname)[0]
1106 1106 msg = msg and ': ' + msg[0] or ': '
1107 1107 else:
1108 1108 msg = ''
1109 1109 return '%s%s' % (patchname, msg)
1110 1110
1111 1111 def pname(i):
1112 1112 if status == 'A':
1113 1113 return self.applied[i].name
1114 1114 else:
1115 1115 return self.series[i]
1116 1116
1117 1117 applied = dict.fromkeys([p.name for p in self.applied])
1118 1118 if not length:
1119 1119 length = len(self.series) - start
1120 1120 if not missing:
1121 1121 for i in xrange(start, start+length):
1122 1122 pfx = ''
1123 1123 patch = pname(i)
1124 1124 if self.ui.verbose:
1125 1125 if patch in applied:
1126 1126 stat = 'A'
1127 1127 elif self.pushable(i)[0]:
1128 1128 stat = 'U'
1129 1129 else:
1130 1130 stat = 'G'
1131 1131 pfx = '%d %s ' % (i, stat)
1132 1132 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1133 1133 else:
1134 1134 msng_list = []
1135 1135 for root, dirs, files in os.walk(self.path):
1136 1136 d = root[len(self.path) + 1:]
1137 1137 for f in files:
1138 1138 fl = os.path.join(d, f)
1139 1139 if (fl not in self.series and
1140 1140 fl not in (self.status_path, self.series_path)
1141 1141 and not fl.startswith('.')):
1142 1142 msng_list.append(fl)
1143 1143 msng_list.sort()
1144 1144 for x in msng_list:
1145 1145 pfx = self.ui.verbose and ('D ') or ''
1146 1146 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1147 1147
1148 1148 def issaveline(self, l):
1149 1149 if l.name == '.hg.patches.save.line':
1150 1150 return True
1151 1151
1152 1152 def qrepo(self, create=False):
1153 1153 if create or os.path.isdir(self.join(".hg")):
1154 1154 return hg.repository(self.ui, path=self.path, create=create)
1155 1155
1156 1156 def restore(self, repo, rev, delete=None, qupdate=None):
1157 1157 c = repo.changelog.read(rev)
1158 1158 desc = c[4].strip()
1159 1159 lines = desc.splitlines()
1160 1160 i = 0
1161 1161 datastart = None
1162 1162 series = []
1163 1163 applied = []
1164 1164 qpp = None
1165 1165 for i in xrange(0, len(lines)):
1166 1166 if lines[i] == 'Patch Data:':
1167 1167 datastart = i + 1
1168 1168 elif lines[i].startswith('Dirstate:'):
1169 1169 l = lines[i].rstrip()
1170 1170 l = l[10:].split(' ')
1171 1171 qpp = [ hg.bin(x) for x in l ]
1172 1172 elif datastart != None:
1173 1173 l = lines[i].rstrip()
1174 1174 se = statusentry(l)
1175 1175 file_ = se.name
1176 1176 if se.rev:
1177 1177 applied.append(se)
1178 1178 else:
1179 1179 series.append(file_)
1180 1180 if datastart == None:
1181 1181 self.ui.warn("No saved patch data found\n")
1182 1182 return 1
1183 1183 self.ui.warn("restoring status: %s\n" % lines[0])
1184 1184 self.full_series = series
1185 1185 self.applied = applied
1186 1186 self.parse_series()
1187 1187 self.series_dirty = 1
1188 1188 self.applied_dirty = 1
1189 1189 heads = repo.changelog.heads()
1190 1190 if delete:
1191 1191 if rev not in heads:
1192 1192 self.ui.warn("save entry has children, leaving it alone\n")
1193 1193 else:
1194 1194 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1195 1195 pp = repo.dirstate.parents()
1196 1196 if rev in pp:
1197 1197 update = True
1198 1198 else:
1199 1199 update = False
1200 1200 self.strip(repo, rev, update=update, backup='strip')
1201 1201 if qpp:
1202 1202 self.ui.warn("saved queue repository parents: %s %s\n" %
1203 1203 (hg.short(qpp[0]), hg.short(qpp[1])))
1204 1204 if qupdate:
1205 1205 print "queue directory updating"
1206 1206 r = self.qrepo()
1207 1207 if not r:
1208 1208 self.ui.warn("Unable to load queue repository\n")
1209 1209 return 1
1210 1210 hg.clean(r, qpp[0])
1211 1211
1212 1212 def save(self, repo, msg=None):
1213 1213 if len(self.applied) == 0:
1214 1214 self.ui.warn("save: no patches applied, exiting\n")
1215 1215 return 1
1216 1216 if self.issaveline(self.applied[-1]):
1217 1217 self.ui.warn("status is already saved\n")
1218 1218 return 1
1219 1219
1220 1220 ar = [ ':' + x for x in self.full_series ]
1221 1221 if not msg:
1222 1222 msg = "hg patches saved state"
1223 1223 else:
1224 1224 msg = "hg patches: " + msg.rstrip('\r\n')
1225 1225 r = self.qrepo()
1226 1226 if r:
1227 1227 pp = r.dirstate.parents()
1228 1228 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1229 1229 msg += "\n\nPatch Data:\n"
1230 1230 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1231 1231 "\n".join(ar) + '\n' or "")
1232 1232 n = repo.commit(None, text, user=None, force=1)
1233 1233 if not n:
1234 1234 self.ui.warn("repo commit failed\n")
1235 1235 return 1
1236 1236 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1237 1237 self.applied_dirty = 1
1238 1238
1239 1239 def full_series_end(self):
1240 1240 if len(self.applied) > 0:
1241 1241 p = self.applied[-1].name
1242 1242 end = self.find_series(p)
1243 1243 if end == None:
1244 1244 return len(self.full_series)
1245 1245 return end + 1
1246 1246 return 0
1247 1247
1248 1248 def series_end(self, all_patches=False):
1249 1249 end = 0
1250 1250 def next(start):
1251 1251 if all_patches:
1252 1252 return start
1253 1253 i = start
1254 1254 while i < len(self.series):
1255 1255 p, reason = self.pushable(i)
1256 1256 if p:
1257 1257 break
1258 1258 self.explain_pushable(i)
1259 1259 i += 1
1260 1260 return i
1261 1261 if len(self.applied) > 0:
1262 1262 p = self.applied[-1].name
1263 1263 try:
1264 1264 end = self.series.index(p)
1265 1265 except ValueError:
1266 1266 return 0
1267 1267 return next(end + 1)
1268 1268 return next(end)
1269 1269
1270 1270 def appliedname(self, index):
1271 1271 pname = self.applied[index].name
1272 1272 if not self.ui.verbose:
1273 1273 p = pname
1274 1274 else:
1275 1275 p = str(self.series.index(pname)) + " " + pname
1276 1276 return p
1277 1277
1278 1278 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1279 1279 force=None, git=False):
1280 1280 def checkseries(patchname):
1281 1281 if patchname in self.series:
1282 1282 raise util.Abort(_('patch %s is already in the series file')
1283 1283 % patchname)
1284 1284 def checkfile(patchname):
1285 1285 if not force and os.path.exists(self.join(patchname)):
1286 1286 raise util.Abort(_('patch "%s" already exists')
1287 1287 % patchname)
1288 1288
1289 1289 if rev:
1290 1290 if files:
1291 1291 raise util.Abort(_('option "-r" not valid when importing '
1292 1292 'files'))
1293 1293 rev = cmdutil.revrange(repo, rev)
1294 1294 rev.sort(lambda x, y: cmp(y, x))
1295 1295 if (len(files) > 1 or len(rev) > 1) and patchname:
1296 1296 raise util.Abort(_('option "-n" not valid when importing multiple '
1297 1297 'patches'))
1298 1298 i = 0
1299 1299 added = []
1300 1300 if rev:
1301 1301 # If mq patches are applied, we can only import revisions
1302 1302 # that form a linear path to qbase.
1303 1303 # Otherwise, they should form a linear path to a head.
1304 1304 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1305 1305 if len(heads) > 1:
1306 1306 raise util.Abort(_('revision %d is the root of more than one '
1307 1307 'branch') % rev[-1])
1308 1308 if self.applied:
1309 1309 base = revlog.hex(repo.changelog.node(rev[0]))
1310 1310 if base in [n.rev for n in self.applied]:
1311 1311 raise util.Abort(_('revision %d is already managed')
1312 1312 % rev[0])
1313 1313 if heads != [revlog.bin(self.applied[-1].rev)]:
1314 1314 raise util.Abort(_('revision %d is not the parent of '
1315 1315 'the queue') % rev[0])
1316 1316 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1317 1317 lastparent = repo.changelog.parentrevs(base)[0]
1318 1318 else:
1319 1319 if heads != [repo.changelog.node(rev[0])]:
1320 1320 raise util.Abort(_('revision %d has unmanaged children')
1321 1321 % rev[0])
1322 1322 lastparent = None
1323 1323
1324 1324 if git:
1325 1325 self.diffopts().git = True
1326 1326
1327 1327 for r in rev:
1328 1328 p1, p2 = repo.changelog.parentrevs(r)
1329 1329 n = repo.changelog.node(r)
1330 1330 if p2 != revlog.nullrev:
1331 1331 raise util.Abort(_('cannot import merge revision %d') % r)
1332 1332 if lastparent and lastparent != r:
1333 1333 raise util.Abort(_('revision %d is not the parent of %d')
1334 1334 % (r, lastparent))
1335 1335 lastparent = p1
1336 1336
1337 1337 if not patchname:
1338 1338 patchname = '%d.diff' % r
1339 1339 checkseries(patchname)
1340 1340 checkfile(patchname)
1341 1341 self.full_series.insert(0, patchname)
1342 1342
1343 1343 patchf = self.opener(patchname, "w")
1344 1344 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1345 1345 patchf.close()
1346 1346
1347 1347 se = statusentry(revlog.hex(n), patchname)
1348 1348 self.applied.insert(0, se)
1349 1349
1350 1350 added.append(patchname)
1351 1351 patchname = None
1352 1352 self.parse_series()
1353 1353 self.applied_dirty = 1
1354 1354
1355 1355 for filename in files:
1356 1356 if existing:
1357 1357 if filename == '-':
1358 1358 raise util.Abort(_('-e is incompatible with import from -'))
1359 1359 if not patchname:
1360 1360 patchname = filename
1361 1361 if not os.path.isfile(self.join(patchname)):
1362 1362 raise util.Abort(_("patch %s does not exist") % patchname)
1363 1363 else:
1364 1364 try:
1365 1365 if filename == '-':
1366 1366 if not patchname:
1367 1367 raise util.Abort(_('need --name to import a patch from -'))
1368 1368 text = sys.stdin.read()
1369 1369 else:
1370 1370 text = file(filename).read()
1371 1371 except IOError:
1372 1372 raise util.Abort(_("unable to read %s") % patchname)
1373 1373 if not patchname:
1374 1374 patchname = os.path.basename(filename)
1375 1375 checkfile(patchname)
1376 1376 patchf = self.opener(patchname, "w")
1377 1377 patchf.write(text)
1378 1378 checkseries(patchname)
1379 1379 index = self.full_series_end() + i
1380 1380 self.full_series[index:index] = [patchname]
1381 1381 self.parse_series()
1382 1382 self.ui.warn("adding %s to series file\n" % patchname)
1383 1383 i += 1
1384 1384 added.append(patchname)
1385 1385 patchname = None
1386 1386 self.series_dirty = 1
1387 1387 qrepo = self.qrepo()
1388 1388 if qrepo:
1389 1389 qrepo.add(added)
1390 1390
1391 1391 def delete(ui, repo, *patches, **opts):
1392 1392 """remove patches from queue
1393 1393
1394 1394 With --rev, mq will stop managing the named revisions. The
1395 1395 patches must be applied and at the base of the stack. This option
1396 1396 is useful when the patches have been applied upstream.
1397 1397
1398 1398 Otherwise, the patches must not be applied.
1399 1399
1400 1400 With --keep, the patch files are preserved in the patch directory."""
1401 1401 q = repo.mq
1402 1402 q.delete(repo, patches, opts)
1403 1403 q.save_dirty()
1404 1404 return 0
1405 1405
1406 1406 def applied(ui, repo, patch=None, **opts):
1407 1407 """print the patches already applied"""
1408 1408 q = repo.mq
1409 1409 if patch:
1410 1410 if patch not in q.series:
1411 1411 raise util.Abort(_("patch %s is not in series file") % patch)
1412 1412 end = q.series.index(patch) + 1
1413 1413 else:
1414 1414 end = len(q.applied)
1415 1415 if not end:
1416 1416 return
1417 1417
1418 1418 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1419 1419
1420 1420 def unapplied(ui, repo, patch=None, **opts):
1421 1421 """print the patches not yet applied"""
1422 1422 q = repo.mq
1423 1423 if patch:
1424 1424 if patch not in q.series:
1425 1425 raise util.Abort(_("patch %s is not in series file") % patch)
1426 1426 start = q.series.index(patch) + 1
1427 1427 else:
1428 1428 start = q.series_end()
1429 1429 q.qseries(repo, start=start, summary=opts.get('summary'))
1430 1430
1431 1431 def qimport(ui, repo, *filename, **opts):
1432 1432 """import a patch
1433 1433
1434 1434 The patch will have the same name as its source file unless you
1435 1435 give it a new one with --name.
1436 1436
1437 1437 You can register an existing patch inside the patch directory
1438 1438 with the --existing flag.
1439 1439
1440 1440 With --force, an existing patch of the same name will be overwritten.
1441 1441
1442 1442 An existing changeset may be placed under mq control with --rev
1443 1443 (e.g. qimport --rev tip -n patch will place tip under mq control).
1444 1444 With --git, patches imported with --rev will use the git diff
1445 1445 format.
1446 1446 """
1447 1447 q = repo.mq
1448 1448 q.qimport(repo, filename, patchname=opts['name'],
1449 1449 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1450 1450 git=opts['git'])
1451 1451 q.save_dirty()
1452 1452 return 0
1453 1453
1454 1454 def init(ui, repo, **opts):
1455 1455 """init a new queue repository
1456 1456
1457 1457 The queue repository is unversioned by default. If -c is
1458 1458 specified, qinit will create a separate nested repository
1459 1459 for patches. Use qcommit to commit changes to this queue
1460 1460 repository."""
1461 1461 q = repo.mq
1462 1462 r = q.init(repo, create=opts['create_repo'])
1463 1463 q.save_dirty()
1464 1464 if r:
1465 1465 fp = r.wopener('.hgignore', 'w')
1466 1466 print >> fp, 'syntax: glob'
1467 1467 print >> fp, 'status'
1468 1468 print >> fp, 'guards'
1469 1469 fp.close()
1470 1470 r.wopener('series', 'w').close()
1471 1471 r.add(['.hgignore', 'series'])
1472 1472 return 0
1473 1473
1474 1474 def clone(ui, source, dest=None, **opts):
1475 1475 '''clone main and patch repository at same time
1476 1476
1477 1477 If source is local, destination will have no patches applied. If
1478 1478 source is remote, this command can not check if patches are
1479 1479 applied in source, so cannot guarantee that patches are not
1480 1480 applied in destination. If you clone remote repository, be sure
1481 1481 before that it has no patches applied.
1482 1482
1483 1483 Source patch repository is looked for in <src>/.hg/patches by
1484 1484 default. Use -p <url> to change.
1485 1485 '''
1486 1486 commands.setremoteconfig(ui, opts)
1487 1487 if dest is None:
1488 1488 dest = hg.defaultdest(source)
1489 1489 sr = hg.repository(ui, ui.expandpath(source))
1490 1490 qbase, destrev = None, None
1491 1491 if sr.local():
1492 1492 reposetup(ui, sr)
1493 1493 if sr.mq.applied:
1494 1494 qbase = revlog.bin(sr.mq.applied[0].rev)
1495 1495 if not hg.islocal(dest):
1496 1496 destrev = sr.parents(qbase)[0]
1497 1497 ui.note(_('cloning main repo\n'))
1498 1498 sr, dr = hg.clone(ui, sr, dest,
1499 1499 pull=opts['pull'],
1500 1500 rev=destrev,
1501 1501 update=False,
1502 1502 stream=opts['uncompressed'])
1503 1503 ui.note(_('cloning patch repo\n'))
1504 1504 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1505 1505 dr.url() + '/.hg/patches',
1506 1506 pull=opts['pull'],
1507 1507 update=not opts['noupdate'],
1508 1508 stream=opts['uncompressed'])
1509 1509 if dr.local():
1510 1510 if qbase:
1511 1511 ui.note(_('stripping applied patches from destination repo\n'))
1512 1512 reposetup(ui, dr)
1513 1513 dr.mq.strip(dr, qbase, update=False, backup=None)
1514 1514 if not opts['noupdate']:
1515 1515 ui.note(_('updating destination repo\n'))
1516 1516 hg.update(dr, dr.changelog.tip())
1517 1517
1518 1518 def commit(ui, repo, *pats, **opts):
1519 1519 """commit changes in the queue repository"""
1520 1520 q = repo.mq
1521 1521 r = q.qrepo()
1522 1522 if not r: raise util.Abort('no queue repository')
1523 1523 commands.commit(r.ui, r, *pats, **opts)
1524 1524
1525 1525 def series(ui, repo, **opts):
1526 1526 """print the entire series file"""
1527 1527 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1528 1528 return 0
1529 1529
1530 1530 def top(ui, repo, **opts):
1531 1531 """print the name of the current patch"""
1532 1532 q = repo.mq
1533 1533 t = len(q.applied)
1534 1534 if t:
1535 1535 return q.qseries(repo, start=t-1, length=1, status='A',
1536 1536 summary=opts.get('summary'))
1537 1537 else:
1538 1538 ui.write("No patches applied\n")
1539 1539 return 1
1540 1540
1541 1541 def next(ui, repo, **opts):
1542 1542 """print the name of the next patch"""
1543 1543 q = repo.mq
1544 1544 end = q.series_end()
1545 1545 if end == len(q.series):
1546 1546 ui.write("All patches applied\n")
1547 1547 return 1
1548 1548 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1549 1549
1550 1550 def prev(ui, repo, **opts):
1551 1551 """print the name of the previous patch"""
1552 1552 q = repo.mq
1553 1553 l = len(q.applied)
1554 1554 if l == 1:
1555 1555 ui.write("Only one patch applied\n")
1556 1556 return 1
1557 1557 if not l:
1558 1558 ui.write("No patches applied\n")
1559 1559 return 1
1560 1560 return q.qseries(repo, start=l-2, length=1, status='A',
1561 1561 summary=opts.get('summary'))
1562 1562
1563 1563 def new(ui, repo, patch, **opts):
1564 1564 """create a new patch
1565 1565
1566 1566 qnew creates a new patch on top of the currently-applied patch
1567 1567 (if any). It will refuse to run if there are any outstanding
1568 1568 changes unless -f is specified, in which case the patch will
1569 1569 be initialised with them.
1570 1570
1571 1571 -e, -m or -l set the patch header as well as the commit message.
1572 1572 If none is specified, the patch header is empty and the
1573 1573 commit message is 'New patch: PATCH'"""
1574 1574 q = repo.mq
1575 1575 message = commands.logmessage(opts)
1576 1576 if opts['edit']:
1577 1577 message = ui.edit(message, ui.username())
1578 1578 q.new(repo, patch, msg=message, force=opts['force'])
1579 1579 q.save_dirty()
1580 1580 return 0
1581 1581
1582 1582 def refresh(ui, repo, *pats, **opts):
1583 1583 """update the current patch
1584 1584
1585 1585 If any file patterns are provided, the refreshed patch will contain only
1586 1586 the modifications that match those patterns; the remaining modifications
1587 1587 will remain in the working directory.
1588 1588 """
1589 1589 q = repo.mq
1590 1590 message = commands.logmessage(opts)
1591 1591 if opts['edit']:
1592 1592 if message:
1593 1593 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1594 1594 patch = q.applied[-1].name
1595 1595 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1596 1596 message = ui.edit('\n'.join(message), user or ui.username())
1597 1597 ret = q.refresh(repo, pats, msg=message, **opts)
1598 1598 q.save_dirty()
1599 1599 return ret
1600 1600
1601 1601 def diff(ui, repo, *pats, **opts):
1602 1602 """diff of the current patch"""
1603 1603 repo.mq.diff(repo, pats, opts)
1604 1604 return 0
1605 1605
1606 1606 def fold(ui, repo, *files, **opts):
1607 1607 """fold the named patches into the current patch
1608 1608
1609 1609 Patches must not yet be applied. Each patch will be successively
1610 1610 applied to the current patch in the order given. If all the
1611 1611 patches apply successfully, the current patch will be refreshed
1612 1612 with the new cumulative patch, and the folded patches will
1613 1613 be deleted. With -k/--keep, the folded patch files will not
1614 1614 be removed afterwards.
1615 1615
1616 1616 The header for each folded patch will be concatenated with
1617 1617 the current patch header, separated by a line of '* * *'."""
1618 1618
1619 1619 q = repo.mq
1620 1620
1621 1621 if not files:
1622 1622 raise util.Abort(_('qfold requires at least one patch name'))
1623 1623 if not q.check_toppatch(repo):
1624 1624 raise util.Abort(_('No patches applied'))
1625 1625
1626 1626 message = commands.logmessage(opts)
1627 1627 if opts['edit']:
1628 1628 if message:
1629 1629 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1630 1630
1631 1631 parent = q.lookup('qtip')
1632 1632 patches = []
1633 1633 messages = []
1634 1634 for f in files:
1635 1635 p = q.lookup(f)
1636 1636 if p in patches or p == parent:
1637 1637 ui.warn(_('Skipping already folded patch %s') % p)
1638 1638 if q.isapplied(p):
1639 1639 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1640 1640 patches.append(p)
1641 1641
1642 1642 for p in patches:
1643 1643 if not message:
1644 1644 messages.append(q.readheaders(p)[0])
1645 1645 pf = q.join(p)
1646 1646 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1647 1647 if not patchsuccess:
1648 1648 raise util.Abort(_('Error folding patch %s') % p)
1649 1649 patch.updatedir(ui, repo, files)
1650 1650
1651 1651 if not message:
1652 1652 message, comments, user = q.readheaders(parent)[0:3]
1653 1653 for msg in messages:
1654 1654 message.append('* * *')
1655 1655 message.extend(msg)
1656 1656 message = '\n'.join(message)
1657 1657
1658 1658 if opts['edit']:
1659 1659 message = ui.edit(message, user or ui.username())
1660 1660
1661 1661 q.refresh(repo, msg=message)
1662 1662 q.delete(repo, patches, opts)
1663 1663 q.save_dirty()
1664 1664
1665 1665 def guard(ui, repo, *args, **opts):
1666 1666 '''set or print guards for a patch
1667 1667
1668 1668 Guards control whether a patch can be pushed. A patch with no
1669 1669 guards is always pushed. A patch with a positive guard ("+foo") is
1670 1670 pushed only if the qselect command has activated it. A patch with
1671 1671 a negative guard ("-foo") is never pushed if the qselect command
1672 1672 has activated it.
1673 1673
1674 1674 With no arguments, print the currently active guards.
1675 1675 With arguments, set guards for the named patch.
1676 1676
1677 1677 To set a negative guard "-foo" on topmost patch ("--" is needed so
1678 1678 hg will not interpret "-foo" as an option):
1679 1679 hg qguard -- -foo
1680 1680
1681 1681 To set guards on another patch:
1682 1682 hg qguard other.patch +2.6.17 -stable
1683 1683 '''
1684 1684 def status(idx):
1685 1685 guards = q.series_guards[idx] or ['unguarded']
1686 1686 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1687 1687 q = repo.mq
1688 1688 patch = None
1689 1689 args = list(args)
1690 1690 if opts['list']:
1691 1691 if args or opts['none']:
1692 1692 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1693 1693 for i in xrange(len(q.series)):
1694 1694 status(i)
1695 1695 return
1696 1696 if not args or args[0][0:1] in '-+':
1697 1697 if not q.applied:
1698 1698 raise util.Abort(_('no patches applied'))
1699 1699 patch = q.applied[-1].name
1700 1700 if patch is None and args[0][0:1] not in '-+':
1701 1701 patch = args.pop(0)
1702 1702 if patch is None:
1703 1703 raise util.Abort(_('no patch to work with'))
1704 1704 if args or opts['none']:
1705 1705 q.set_guards(q.find_series(patch), args)
1706 1706 q.save_dirty()
1707 1707 else:
1708 1708 status(q.series.index(q.lookup(patch)))
1709 1709
1710 1710 def header(ui, repo, patch=None):
1711 1711 """Print the header of the topmost or specified patch"""
1712 1712 q = repo.mq
1713 1713
1714 1714 if patch:
1715 1715 patch = q.lookup(patch)
1716 1716 else:
1717 1717 if not q.applied:
1718 1718 ui.write('No patches applied\n')
1719 1719 return 1
1720 1720 patch = q.lookup('qtip')
1721 1721 message = repo.mq.readheaders(patch)[0]
1722 1722
1723 1723 ui.write('\n'.join(message) + '\n')
1724 1724
1725 1725 def lastsavename(path):
1726 1726 (directory, base) = os.path.split(path)
1727 1727 names = os.listdir(directory)
1728 1728 namere = re.compile("%s.([0-9]+)" % base)
1729 1729 maxindex = None
1730 1730 maxname = None
1731 1731 for f in names:
1732 1732 m = namere.match(f)
1733 1733 if m:
1734 1734 index = int(m.group(1))
1735 1735 if maxindex == None or index > maxindex:
1736 1736 maxindex = index
1737 1737 maxname = f
1738 1738 if maxname:
1739 1739 return (os.path.join(directory, maxname), maxindex)
1740 1740 return (None, None)
1741 1741
1742 1742 def savename(path):
1743 1743 (last, index) = lastsavename(path)
1744 1744 if last is None:
1745 1745 index = 0
1746 1746 newpath = path + ".%d" % (index + 1)
1747 1747 return newpath
1748 1748
1749 1749 def push(ui, repo, patch=None, **opts):
1750 1750 """push the next patch onto the stack"""
1751 1751 q = repo.mq
1752 1752 mergeq = None
1753 1753
1754 1754 if opts['all']:
1755 1755 if not q.series:
1756 1756 raise util.Abort(_('no patches in series'))
1757 1757 patch = q.series[-1]
1758 1758 if opts['merge']:
1759 1759 if opts['name']:
1760 1760 newpath = opts['name']
1761 1761 else:
1762 1762 newpath, i = lastsavename(q.path)
1763 1763 if not newpath:
1764 1764 ui.warn("no saved queues found, please use -n\n")
1765 1765 return 1
1766 1766 mergeq = queue(ui, repo.join(""), newpath)
1767 1767 ui.warn("merging with queue at: %s\n" % mergeq.path)
1768 1768 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1769 1769 mergeq=mergeq)
1770 1770 q.save_dirty()
1771 1771 return ret
1772 1772
1773 1773 def pop(ui, repo, patch=None, **opts):
1774 1774 """pop the current patch off the stack"""
1775 1775 localupdate = True
1776 1776 if opts['name']:
1777 1777 q = queue(ui, repo.join(""), repo.join(opts['name']))
1778 1778 ui.warn('using patch queue: %s\n' % q.path)
1779 1779 localupdate = False
1780 1780 else:
1781 1781 q = repo.mq
1782 1782 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1783 1783 q.save_dirty()
1784 1784 return 0
1785 1785
1786 1786 def rename(ui, repo, patch, name=None, **opts):
1787 1787 """rename a patch
1788 1788
1789 1789 With one argument, renames the current patch to PATCH1.
1790 1790 With two arguments, renames PATCH1 to PATCH2."""
1791 1791
1792 1792 q = repo.mq
1793 1793
1794 1794 if not name:
1795 1795 name = patch
1796 1796 patch = None
1797 1797
1798 1798 if patch:
1799 1799 patch = q.lookup(patch)
1800 1800 else:
1801 1801 if not q.applied:
1802 1802 ui.write(_('No patches applied\n'))
1803 1803 return
1804 1804 patch = q.lookup('qtip')
1805 1805 absdest = q.join(name)
1806 1806 if os.path.isdir(absdest):
1807 1807 name = os.path.join(name, os.path.basename(patch))
1808 1808 absdest = q.join(name)
1809 1809 if os.path.exists(absdest):
1810 1810 raise util.Abort(_('%s already exists') % absdest)
1811 1811
1812 1812 if name in q.series:
1813 1813 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1814 1814
1815 1815 if ui.verbose:
1816 1816 ui.write('Renaming %s to %s\n' % (patch, name))
1817 1817 i = q.find_series(patch)
1818 1818 guards = q.guard_re.findall(q.full_series[i])
1819 1819 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1820 1820 q.parse_series()
1821 1821 q.series_dirty = 1
1822 1822
1823 1823 info = q.isapplied(patch)
1824 1824 if info:
1825 1825 q.applied[info[0]] = statusentry(info[1], name)
1826 1826 q.applied_dirty = 1
1827 1827
1828 1828 util.rename(q.join(patch), absdest)
1829 1829 r = q.qrepo()
1830 1830 if r:
1831 1831 wlock = r.wlock()
1832 1832 if r.dirstate.state(name) == 'r':
1833 1833 r.undelete([name], wlock)
1834 1834 r.copy(patch, name, wlock)
1835 1835 r.remove([patch], False, wlock)
1836 1836
1837 1837 q.save_dirty()
1838 1838
1839 1839 def restore(ui, repo, rev, **opts):
1840 1840 """restore the queue state saved by a rev"""
1841 1841 rev = repo.lookup(rev)
1842 1842 q = repo.mq
1843 1843 q.restore(repo, rev, delete=opts['delete'],
1844 1844 qupdate=opts['update'])
1845 1845 q.save_dirty()
1846 1846 return 0
1847 1847
1848 1848 def save(ui, repo, **opts):
1849 1849 """save current queue state"""
1850 1850 q = repo.mq
1851 1851 message = commands.logmessage(opts)
1852 1852 ret = q.save(repo, msg=message)
1853 1853 if ret:
1854 1854 return ret
1855 1855 q.save_dirty()
1856 1856 if opts['copy']:
1857 1857 path = q.path
1858 1858 if opts['name']:
1859 1859 newpath = os.path.join(q.basepath, opts['name'])
1860 1860 if os.path.exists(newpath):
1861 1861 if not os.path.isdir(newpath):
1862 1862 raise util.Abort(_('destination %s exists and is not '
1863 1863 'a directory') % newpath)
1864 1864 if not opts['force']:
1865 1865 raise util.Abort(_('destination %s exists, '
1866 1866 'use -f to force') % newpath)
1867 1867 else:
1868 1868 newpath = savename(path)
1869 1869 ui.warn("copy %s to %s\n" % (path, newpath))
1870 1870 util.copyfiles(path, newpath)
1871 1871 if opts['empty']:
1872 1872 try:
1873 1873 os.unlink(q.join(q.status_path))
1874 1874 except:
1875 1875 pass
1876 1876 return 0
1877 1877
1878 1878 def strip(ui, repo, rev, **opts):
1879 1879 """strip a revision and all later revs on the same branch"""
1880 1880 rev = repo.lookup(rev)
1881 1881 backup = 'all'
1882 1882 if opts['backup']:
1883 1883 backup = 'strip'
1884 1884 elif opts['nobackup']:
1885 1885 backup = 'none'
1886 1886 update = repo.dirstate.parents()[0] != revlog.nullid
1887 1887 repo.mq.strip(repo, rev, backup=backup, update=update)
1888 1888 return 0
1889 1889
1890 1890 def select(ui, repo, *args, **opts):
1891 1891 '''set or print guarded patches to push
1892 1892
1893 1893 Use the qguard command to set or print guards on patch, then use
1894 1894 qselect to tell mq which guards to use. A patch will be pushed if it
1895 1895 has no guards or any positive guards match the currently selected guard,
1896 1896 but will not be pushed if any negative guards match the current guard.
1897 1897 For example:
1898 1898
1899 1899 qguard foo.patch -stable (negative guard)
1900 1900 qguard bar.patch +stable (positive guard)
1901 1901 qselect stable
1902 1902
1903 1903 This activates the "stable" guard. mq will skip foo.patch (because
1904 1904 it has a negative match) but push bar.patch (because it
1905 1905 has a positive match).
1906 1906
1907 1907 With no arguments, prints the currently active guards.
1908 1908 With one argument, sets the active guard.
1909 1909
1910 1910 Use -n/--none to deactivate guards (no other arguments needed).
1911 1911 When no guards are active, patches with positive guards are skipped
1912 1912 and patches with negative guards are pushed.
1913 1913
1914 1914 qselect can change the guards on applied patches. It does not pop
1915 1915 guarded patches by default. Use --pop to pop back to the last applied
1916 1916 patch that is not guarded. Use --reapply (which implies --pop) to push
1917 1917 back to the current patch afterwards, but skip guarded patches.
1918 1918
1919 1919 Use -s/--series to print a list of all guards in the series file (no
1920 1920 other arguments needed). Use -v for more information.'''
1921 1921
1922 1922 q = repo.mq
1923 1923 guards = q.active()
1924 1924 if args or opts['none']:
1925 1925 old_unapplied = q.unapplied(repo)
1926 1926 old_guarded = [i for i in xrange(len(q.applied)) if
1927 1927 not q.pushable(i)[0]]
1928 1928 q.set_active(args)
1929 1929 q.save_dirty()
1930 1930 if not args:
1931 1931 ui.status(_('guards deactivated\n'))
1932 1932 if not opts['pop'] and not opts['reapply']:
1933 1933 unapplied = q.unapplied(repo)
1934 1934 guarded = [i for i in xrange(len(q.applied))
1935 1935 if not q.pushable(i)[0]]
1936 1936 if len(unapplied) != len(old_unapplied):
1937 1937 ui.status(_('number of unguarded, unapplied patches has '
1938 1938 'changed from %d to %d\n') %
1939 1939 (len(old_unapplied), len(unapplied)))
1940 1940 if len(guarded) != len(old_guarded):
1941 1941 ui.status(_('number of guarded, applied patches has changed '
1942 1942 'from %d to %d\n') %
1943 1943 (len(old_guarded), len(guarded)))
1944 1944 elif opts['series']:
1945 1945 guards = {}
1946 1946 noguards = 0
1947 1947 for gs in q.series_guards:
1948 1948 if not gs:
1949 1949 noguards += 1
1950 1950 for g in gs:
1951 1951 guards.setdefault(g, 0)
1952 1952 guards[g] += 1
1953 1953 if ui.verbose:
1954 1954 guards['NONE'] = noguards
1955 1955 guards = guards.items()
1956 1956 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
1957 1957 if guards:
1958 1958 ui.note(_('guards in series file:\n'))
1959 1959 for guard, count in guards:
1960 1960 ui.note('%2d ' % count)
1961 1961 ui.write(guard, '\n')
1962 1962 else:
1963 1963 ui.note(_('no guards in series file\n'))
1964 1964 else:
1965 1965 if guards:
1966 1966 ui.note(_('active guards:\n'))
1967 1967 for g in guards:
1968 1968 ui.write(g, '\n')
1969 1969 else:
1970 1970 ui.write(_('no active guards\n'))
1971 1971 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
1972 1972 popped = False
1973 1973 if opts['pop'] or opts['reapply']:
1974 1974 for i in xrange(len(q.applied)):
1975 1975 pushable, reason = q.pushable(i)
1976 1976 if not pushable:
1977 1977 ui.status(_('popping guarded patches\n'))
1978 1978 popped = True
1979 1979 if i == 0:
1980 1980 q.pop(repo, all=True)
1981 1981 else:
1982 1982 q.pop(repo, i-1)
1983 1983 break
1984 1984 if popped:
1985 1985 try:
1986 1986 if reapply:
1987 1987 ui.status(_('reapplying unguarded patches\n'))
1988 1988 q.push(repo, reapply)
1989 1989 finally:
1990 1990 q.save_dirty()
1991 1991
1992 1992 def reposetup(ui, repo):
1993 1993 class mqrepo(repo.__class__):
1994 1994 def abort_if_wdir_patched(self, errmsg, force=False):
1995 1995 if self.mq.applied and not force:
1996 1996 parent = revlog.hex(self.dirstate.parents()[0])
1997 1997 if parent in [s.rev for s in self.mq.applied]:
1998 1998 raise util.Abort(errmsg)
1999 1999
2000 2000 def commit(self, *args, **opts):
2001 2001 if len(args) >= 6:
2002 2002 force = args[5]
2003 2003 else:
2004 2004 force = opts.get('force')
2005 2005 self.abort_if_wdir_patched(
2006 2006 _('cannot commit over an applied mq patch'),
2007 2007 force)
2008 2008
2009 2009 return super(mqrepo, self).commit(*args, **opts)
2010 2010
2011 2011 def push(self, remote, force=False, revs=None):
2012 2012 if self.mq.applied and not force:
2013 2013 raise util.Abort(_('source has mq patches applied'))
2014 2014 return super(mqrepo, self).push(remote, force, revs)
2015 2015
2016 2016 def tags(self):
2017 2017 if self.tagscache:
2018 2018 return self.tagscache
2019 2019
2020 2020 tagscache = super(mqrepo, self).tags()
2021 2021
2022 2022 q = self.mq
2023 2023 if not q.applied:
2024 2024 return tagscache
2025 2025
2026 2026 mqtags = [(patch.rev, patch.name) for patch in q.applied]
2027 2027 mqtags.append((mqtags[-1][0], 'qtip'))
2028 2028 mqtags.append((mqtags[0][0], 'qbase'))
2029 2029 for patch in mqtags:
2030 2030 if patch[1] in tagscache:
2031 2031 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2032 2032 else:
2033 2033 tagscache[patch[1]] = revlog.bin(patch[0])
2034 2034
2035 2035 return tagscache
2036 2036
2037 2037 def _branchtags(self):
2038 2038 q = self.mq
2039 2039 if not q.applied:
2040 2040 return super(mqrepo, self)._branchtags()
2041 2041
2042 2042 self.branchcache = {} # avoid recursion in changectx
2043 2043 cl = self.changelog
2044 2044 partial, last, lrev = self._readbranchcache()
2045 2045
2046 2046 qbase = cl.rev(revlog.bin(q.applied[0].rev))
2047 2047 start = lrev + 1
2048 2048 if start < qbase:
2049 2049 # update the cache (excluding the patches) and save it
2050 2050 self._updatebranchcache(partial, lrev+1, qbase)
2051 2051 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2052 2052 start = qbase
2053 2053 # if start = qbase, the cache is as updated as it should be.
2054 2054 # if start > qbase, the cache includes (part of) the patches.
2055 2055 # we might as well use it, but we won't save it.
2056 2056
2057 2057 # update the cache up to the tip
2058 2058 self._updatebranchcache(partial, start, cl.count())
2059 2059
2060 2060 return partial
2061 2061
2062 2062 if repo.local():
2063 2063 repo.__class__ = mqrepo
2064 2064 repo.mq = queue(ui, repo.join(""))
2065 2065
2066 2066 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2067 2067
2068 2068 cmdtable = {
2069 2069 "qapplied": (applied, [] + seriesopts, 'hg qapplied [-s] [PATCH]'),
2070 2070 "qclone": (clone,
2071 2071 [('', 'pull', None, _('use pull protocol to copy metadata')),
2072 2072 ('U', 'noupdate', None, _('do not update the new working directories')),
2073 2073 ('', 'uncompressed', None,
2074 2074 _('use uncompressed transfer (fast over LAN)')),
2075 2075 ('e', 'ssh', '', _('specify ssh command to use')),
2076 2076 ('p', 'patches', '', _('location of source patch repo')),
2077 2077 ('', 'remotecmd', '',
2078 2078 _('specify hg command to run on the remote side'))],
2079 2079 'hg qclone [OPTION]... SOURCE [DEST]'),
2080 2080 "qcommit|qci":
2081 2081 (commit,
2082 2082 commands.table["^commit|ci"][1],
2083 2083 'hg qcommit [OPTION]... [FILE]...'),
2084 2084 "^qdiff": (diff,
2085 2085 [('g', 'git', None, _('use git extended diff format')),
2086 2086 ('I', 'include', [], _('include names matching the given patterns')),
2087 2087 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2088 2088 'hg qdiff [-I] [-X] [FILE]...'),
2089 2089 "qdelete|qremove|qrm":
2090 2090 (delete,
2091 2091 [('k', 'keep', None, _('keep patch file')),
2092 2092 ('r', 'rev', [], _('stop managing a revision'))],
2093 2093 'hg qdelete [-k] [-r REV]... PATCH...'),
2094 2094 'qfold':
2095 2095 (fold,
2096 2096 [('e', 'edit', None, _('edit patch header')),
2097 2097 ('k', 'keep', None, _('keep folded patch files'))
2098 2098 ] + commands.commitopts,
2099 2099 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
2100 2100 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
2101 2101 ('n', 'none', None, _('drop all guards'))],
2102 2102 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
2103 2103 'qheader': (header, [],
2104 2104 _('hg qheader [PATCH]')),
2105 2105 "^qimport":
2106 2106 (qimport,
2107 2107 [('e', 'existing', None, 'import file in patch dir'),
2108 2108 ('n', 'name', '', 'patch file name'),
2109 2109 ('f', 'force', None, 'overwrite existing files'),
2110 2110 ('r', 'rev', [], 'place existing revisions under mq control'),
2111 2111 ('g', 'git', None, _('use git extended diff format'))],
2112 2112 'hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...'),
2113 2113 "^qinit":
2114 2114 (init,
2115 2115 [('c', 'create-repo', None, 'create queue repository')],
2116 2116 'hg qinit [-c]'),
2117 2117 "qnew":
2118 2118 (new,
2119 2119 [('e', 'edit', None, _('edit commit message')),
2120 2120 ('f', 'force', None, _('import uncommitted changes into patch'))
2121 2121 ] + commands.commitopts,
2122 2122 'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
2123 2123 "qnext": (next, [] + seriesopts, 'hg qnext [-s]'),
2124 2124 "qprev": (prev, [] + seriesopts, 'hg qprev [-s]'),
2125 2125 "^qpop":
2126 2126 (pop,
2127 2127 [('a', 'all', None, 'pop all patches'),
2128 2128 ('n', 'name', '', 'queue name to pop'),
2129 2129 ('f', 'force', None, 'forget any local changes')],
2130 2130 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
2131 2131 "^qpush":
2132 2132 (push,
2133 2133 [('f', 'force', None, 'apply if the patch has rejects'),
2134 2134 ('l', 'list', None, 'list patch name in commit text'),
2135 2135 ('a', 'all', None, 'apply all patches'),
2136 2136 ('m', 'merge', None, 'merge from another queue'),
2137 2137 ('n', 'name', '', 'merge queue name')],
2138 2138 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
2139 2139 "^qrefresh":
2140 2140 (refresh,
2141 2141 [('e', 'edit', None, _('edit commit message')),
2142 2142 ('g', 'git', None, _('use git extended diff format')),
2143 2143 ('s', 'short', None, 'refresh only files already in the patch'),
2144 2144 ('I', 'include', [], _('include names matching the given patterns')),
2145 2145 ('X', 'exclude', [], _('exclude names matching the given patterns'))
2146 2146 ] + commands.commitopts,
2147 2147 'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] FILES...'),
2148 2148 'qrename|qmv':
2149 2149 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
2150 2150 "qrestore":
2151 2151 (restore,
2152 2152 [('d', 'delete', None, 'delete save entry'),
2153 2153 ('u', 'update', None, 'update queue working dir')],
2154 2154 'hg qrestore [-d] [-u] REV'),
2155 2155 "qsave":
2156 2156 (save,
2157 2157 [('c', 'copy', None, 'copy patch directory'),
2158 2158 ('n', 'name', '', 'copy directory name'),
2159 2159 ('e', 'empty', None, 'clear queue status file'),
2160 2160 ('f', 'force', None, 'force copy')] + commands.commitopts,
2161 2161 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
2162 2162 "qselect": (select,
2163 2163 [('n', 'none', None, _('disable all guards')),
2164 2164 ('s', 'series', None, _('list all guards in series file')),
2165 2165 ('', 'pop', None,
2166 2166 _('pop to before first guarded applied patch')),
2167 2167 ('', 'reapply', None, _('pop, then reapply patches'))],
2168 2168 'hg qselect [OPTION...] [GUARD...]'),
2169 2169 "qseries":
2170 2170 (series,
2171 2171 [('m', 'missing', None, 'print patches not in series')] + seriesopts,
2172 2172 'hg qseries [-ms]'),
2173 2173 "^strip":
2174 2174 (strip,
2175 2175 [('f', 'force', None, 'force multi-head removal'),
2176 2176 ('b', 'backup', None, 'bundle unrelated changesets'),
2177 2177 ('n', 'nobackup', None, 'no backups')],
2178 2178 'hg strip [-f] [-b] [-n] REV'),
2179 2179 "qtop": (top, [] + seriesopts, 'hg qtop [-s]'),
2180 2180 "qunapplied": (unapplied, [] + seriesopts, 'hg qunapplied [-s] [PATCH]'),
2181 2181 }
@@ -1,3284 +1,3297 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 import demandimport; demandimport.enable()
9 9 from node import *
10 10 from i18n import _
11 11 import bisect, os, re, sys, signal, imp, urllib, pdb, shlex, stat
12 12 import fancyopts, ui, hg, util, lock, revlog, bundlerepo
13 13 import difflib, patch, time, help, mdiff, tempfile
14 import traceback, errno, version, atexit
14 import traceback, errno, version, atexit, socket
15 15 import archival, changegroup, cmdutil, hgweb.server, sshserver
16 16
17 17 class UnknownCommand(Exception):
18 18 """Exception raised if command is not in the command table."""
19 19 class AmbiguousCommand(Exception):
20 20 """Exception raised if command shortcut matches more than one command."""
21 21
22 22 def bail_if_changed(repo):
23 23 modified, added, removed, deleted = repo.status()[:4]
24 24 if modified or added or removed or deleted:
25 25 raise util.Abort(_("outstanding uncommitted changes"))
26 26
27 27 def logmessage(opts):
28 28 """ get the log message according to -m and -l option """
29 29 message = opts['message']
30 30 logfile = opts['logfile']
31 31
32 32 if message and logfile:
33 33 raise util.Abort(_('options --message and --logfile are mutually '
34 34 'exclusive'))
35 35 if not message and logfile:
36 36 try:
37 37 if logfile == '-':
38 38 message = sys.stdin.read()
39 39 else:
40 40 message = open(logfile).read()
41 41 except IOError, inst:
42 42 raise util.Abort(_("can't read commit message '%s': %s") %
43 43 (logfile, inst.strerror))
44 44 return message
45 45
46 46 def setremoteconfig(ui, opts):
47 47 "copy remote options to ui tree"
48 48 if opts.get('ssh'):
49 49 ui.setconfig("ui", "ssh", opts['ssh'])
50 50 if opts.get('remotecmd'):
51 51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
52 52
53 53 # Commands start here, listed alphabetically
54 54
55 55 def add(ui, repo, *pats, **opts):
56 56 """add the specified files on the next commit
57 57
58 58 Schedule files to be version controlled and added to the repository.
59 59
60 60 The files will be added to the repository at the next commit. To
61 61 undo an add before that, see hg revert.
62 62
63 63 If no names are given, add all files in the repository.
64 64 """
65 65
66 66 names = []
67 67 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
68 68 if exact:
69 69 if ui.verbose:
70 70 ui.status(_('adding %s\n') % rel)
71 71 names.append(abs)
72 72 elif repo.dirstate.state(abs) == '?':
73 73 ui.status(_('adding %s\n') % rel)
74 74 names.append(abs)
75 75 if not opts.get('dry_run'):
76 76 repo.add(names)
77 77
78 78 def addremove(ui, repo, *pats, **opts):
79 79 """add all new files, delete all missing files
80 80
81 81 Add all new files and remove all missing files from the repository.
82 82
83 83 New files are ignored if they match any of the patterns in .hgignore. As
84 84 with add, these changes take effect at the next commit.
85 85
86 86 Use the -s option to detect renamed files. With a parameter > 0,
87 87 this compares every removed file with every added file and records
88 88 those similar enough as renames. This option takes a percentage
89 89 between 0 (disabled) and 100 (files must be identical) as its
90 90 parameter. Detecting renamed files this way can be expensive.
91 91 """
92 92 sim = float(opts.get('similarity') or 0)
93 93 if sim < 0 or sim > 100:
94 94 raise util.Abort(_('similarity must be between 0 and 100'))
95 95 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
96 96
97 97 def annotate(ui, repo, *pats, **opts):
98 98 """show changeset information per file line
99 99
100 100 List changes in files, showing the revision id responsible for each line
101 101
102 102 This command is useful to discover who did a change or when a change took
103 103 place.
104 104
105 105 Without the -a option, annotate will avoid processing files it
106 106 detects as binary. With -a, annotate will generate an annotation
107 107 anyway, probably with undesirable results.
108 108 """
109 109 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
110 110
111 111 if not pats:
112 112 raise util.Abort(_('at least one file name or pattern required'))
113 113
114 114 opmap = [['user', lambda x: ui.shortuser(x.user())],
115 115 ['number', lambda x: str(x.rev())],
116 116 ['changeset', lambda x: short(x.node())],
117 117 ['date', getdate], ['follow', lambda x: x.path()]]
118 118 if (not opts['user'] and not opts['changeset'] and not opts['date']
119 119 and not opts['follow']):
120 120 opts['number'] = 1
121 121
122 122 ctx = repo.changectx(opts['rev'])
123 123
124 124 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
125 125 node=ctx.node()):
126 126 fctx = ctx.filectx(abs)
127 127 if not opts['text'] and util.binary(fctx.data()):
128 128 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
129 129 continue
130 130
131 131 lines = fctx.annotate(follow=opts.get('follow'))
132 132 pieces = []
133 133
134 134 for o, f in opmap:
135 135 if opts[o]:
136 136 l = [f(n) for n, dummy in lines]
137 137 if l:
138 138 m = max(map(len, l))
139 139 pieces.append(["%*s" % (m, x) for x in l])
140 140
141 141 if pieces:
142 142 for p, l in zip(zip(*pieces), lines):
143 143 ui.write("%s: %s" % (" ".join(p), l[1]))
144 144
145 145 def archive(ui, repo, dest, **opts):
146 146 '''create unversioned archive of a repository revision
147 147
148 148 By default, the revision used is the parent of the working
149 149 directory; use "-r" to specify a different revision.
150 150
151 151 To specify the type of archive to create, use "-t". Valid
152 152 types are:
153 153
154 154 "files" (default): a directory full of files
155 155 "tar": tar archive, uncompressed
156 156 "tbz2": tar archive, compressed using bzip2
157 157 "tgz": tar archive, compressed using gzip
158 158 "uzip": zip archive, uncompressed
159 159 "zip": zip archive, compressed using deflate
160 160
161 161 The exact name of the destination archive or directory is given
162 162 using a format string; see "hg help export" for details.
163 163
164 164 Each member added to an archive file has a directory prefix
165 165 prepended. Use "-p" to specify a format string for the prefix.
166 166 The default is the basename of the archive, with suffixes removed.
167 167 '''
168 168
169 169 node = repo.changectx(opts['rev']).node()
170 170 dest = cmdutil.make_filename(repo, dest, node)
171 171 if os.path.realpath(dest) == repo.root:
172 172 raise util.Abort(_('repository root cannot be destination'))
173 173 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
174 174 kind = opts.get('type') or 'files'
175 175 prefix = opts['prefix']
176 176 if dest == '-':
177 177 if kind == 'files':
178 178 raise util.Abort(_('cannot archive plain files to stdout'))
179 179 dest = sys.stdout
180 180 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
181 181 prefix = cmdutil.make_filename(repo, prefix, node)
182 182 archival.archive(repo, dest, node, kind, not opts['no_decode'],
183 183 matchfn, prefix)
184 184
185 185 def backout(ui, repo, rev, **opts):
186 186 '''reverse effect of earlier changeset
187 187
188 188 Commit the backed out changes as a new changeset. The new
189 189 changeset is a child of the backed out changeset.
190 190
191 191 If you back out a changeset other than the tip, a new head is
192 192 created. This head is the parent of the working directory. If
193 193 you back out an old changeset, your working directory will appear
194 194 old after the backout. You should merge the backout changeset
195 195 with another head.
196 196
197 197 The --merge option remembers the parent of the working directory
198 198 before starting the backout, then merges the new head with that
199 199 changeset afterwards. This saves you from doing the merge by
200 200 hand. The result of this merge is not committed, as for a normal
201 201 merge.'''
202 202
203 203 bail_if_changed(repo)
204 204 op1, op2 = repo.dirstate.parents()
205 205 if op2 != nullid:
206 206 raise util.Abort(_('outstanding uncommitted merge'))
207 207 node = repo.lookup(rev)
208 208 p1, p2 = repo.changelog.parents(node)
209 209 if p1 == nullid:
210 210 raise util.Abort(_('cannot back out a change with no parents'))
211 211 if p2 != nullid:
212 212 if not opts['parent']:
213 213 raise util.Abort(_('cannot back out a merge changeset without '
214 214 '--parent'))
215 215 p = repo.lookup(opts['parent'])
216 216 if p not in (p1, p2):
217 217 raise util.Abort(_('%s is not a parent of %s') %
218 218 (short(p), short(node)))
219 219 parent = p
220 220 else:
221 221 if opts['parent']:
222 222 raise util.Abort(_('cannot use --parent on non-merge changeset'))
223 223 parent = p1
224 224 hg.clean(repo, node, show_stats=False)
225 225 revert_opts = opts.copy()
226 226 revert_opts['date'] = None
227 227 revert_opts['all'] = True
228 228 revert_opts['rev'] = hex(parent)
229 229 revert(ui, repo, **revert_opts)
230 230 commit_opts = opts.copy()
231 231 commit_opts['addremove'] = False
232 232 if not commit_opts['message'] and not commit_opts['logfile']:
233 233 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
234 234 commit_opts['force_editor'] = True
235 235 commit(ui, repo, **commit_opts)
236 236 def nice(node):
237 237 return '%d:%s' % (repo.changelog.rev(node), short(node))
238 238 ui.status(_('changeset %s backs out changeset %s\n') %
239 239 (nice(repo.changelog.tip()), nice(node)))
240 240 if op1 != node:
241 241 if opts['merge']:
242 242 ui.status(_('merging with changeset %s\n') % nice(op1))
243 243 hg.merge(repo, hex(op1))
244 244 else:
245 245 ui.status(_('the backout changeset is a new head - '
246 246 'do not forget to merge\n'))
247 247 ui.status(_('(use "backout --merge" '
248 248 'if you want to auto-merge)\n'))
249 249
250 250 def branch(ui, repo, label=None):
251 251 """set or show the current branch name
252 252
253 253 With <name>, set the current branch name. Otherwise, show the
254 254 current branch name.
255 255 """
256 256
257 257 if label is not None:
258 258 repo.opener("branch", "w").write(util.fromlocal(label) + '\n')
259 259 else:
260 260 b = util.tolocal(repo.workingctx().branch())
261 261 if b:
262 262 ui.write("%s\n" % b)
263 263
264 264 def branches(ui, repo):
265 265 """list repository named branches
266 266
267 267 List the repository's named branches.
268 268 """
269 269 b = repo.branchtags()
270 270 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
271 271 l.sort()
272 272 for r, n, t in l:
273 273 hexfunc = ui.debugflag and hex or short
274 274 if ui.quiet:
275 275 ui.write("%s\n" % t)
276 276 else:
277 277 t = util.localsub(t, 30)
278 278 t += " " * (30 - util.locallen(t))
279 279 ui.write("%s %s:%s\n" % (t, -r, hexfunc(n)))
280 280
281 281 def bundle(ui, repo, fname, dest=None, **opts):
282 282 """create a changegroup file
283 283
284 284 Generate a compressed changegroup file collecting changesets not
285 285 found in the other repository.
286 286
287 287 If no destination repository is specified the destination is assumed
288 288 to have all the nodes specified by one or more --base parameters.
289 289
290 290 The bundle file can then be transferred using conventional means and
291 291 applied to another repository with the unbundle or pull command.
292 292 This is useful when direct push and pull are not available or when
293 293 exporting an entire repository is undesirable.
294 294
295 295 Applying bundles preserves all changeset contents including
296 296 permissions, copy/rename information, and revision history.
297 297 """
298 298 revs = opts.get('rev') or None
299 299 if revs:
300 300 revs = [repo.lookup(rev) for rev in revs]
301 301 base = opts.get('base')
302 302 if base:
303 303 if dest:
304 304 raise util.Abort(_("--base is incompatible with specifiying "
305 305 "a destination"))
306 306 base = [repo.lookup(rev) for rev in base]
307 307 # create the right base
308 308 # XXX: nodesbetween / changegroup* should be "fixed" instead
309 309 o = []
310 310 has = {nullid: None}
311 311 for n in base:
312 312 has.update(repo.changelog.reachable(n))
313 313 if revs:
314 314 visit = list(revs)
315 315 else:
316 316 visit = repo.changelog.heads()
317 317 seen = {}
318 318 while visit:
319 319 n = visit.pop(0)
320 320 parents = [p for p in repo.changelog.parents(n) if p not in has]
321 321 if len(parents) == 0:
322 322 o.insert(0, n)
323 323 else:
324 324 for p in parents:
325 325 if p not in seen:
326 326 seen[p] = 1
327 327 visit.append(p)
328 328 else:
329 329 setremoteconfig(ui, opts)
330 330 dest = ui.expandpath(dest or 'default-push', dest or 'default')
331 331 other = hg.repository(ui, dest)
332 332 o = repo.findoutgoing(other, force=opts['force'])
333 333
334 334 if revs:
335 335 cg = repo.changegroupsubset(o, revs, 'bundle')
336 336 else:
337 337 cg = repo.changegroup(o, 'bundle')
338 338 changegroup.writebundle(cg, fname, "HG10BZ")
339 339
340 340 def cat(ui, repo, file1, *pats, **opts):
341 341 """output the current or given revision of files
342 342
343 343 Print the specified files as they were at the given revision.
344 344 If no revision is given, the parent of the working directory is used,
345 345 or tip if no revision is checked out.
346 346
347 347 Output may be to a file, in which case the name of the file is
348 348 given using a format string. The formatting rules are the same as
349 349 for the export command, with the following additions:
350 350
351 351 %s basename of file being printed
352 352 %d dirname of file being printed, or '.' if in repo root
353 353 %p root-relative path name of file being printed
354 354 """
355 355 ctx = repo.changectx(opts['rev'])
356 356 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
357 357 ctx.node()):
358 358 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
359 359 fp.write(ctx.filectx(abs).data())
360 360
361 361 def clone(ui, source, dest=None, **opts):
362 362 """make a copy of an existing repository
363 363
364 364 Create a copy of an existing repository in a new directory.
365 365
366 366 If no destination directory name is specified, it defaults to the
367 367 basename of the source.
368 368
369 369 The location of the source is added to the new repository's
370 370 .hg/hgrc file, as the default to be used for future pulls.
371 371
372 372 For efficiency, hardlinks are used for cloning whenever the source
373 373 and destination are on the same filesystem (note this applies only
374 374 to the repository data, not to the checked out files). Some
375 375 filesystems, such as AFS, implement hardlinking incorrectly, but
376 376 do not report errors. In these cases, use the --pull option to
377 377 avoid hardlinking.
378 378
379 379 You can safely clone repositories and checked out files using full
380 380 hardlinks with
381 381
382 382 $ cp -al REPO REPOCLONE
383 383
384 384 which is the fastest way to clone. However, the operation is not
385 385 atomic (making sure REPO is not modified during the operation is
386 386 up to you) and you have to make sure your editor breaks hardlinks
387 387 (Emacs and most Linux Kernel tools do so).
388 388
389 389 If you use the -r option to clone up to a specific revision, no
390 390 subsequent revisions will be present in the cloned repository.
391 391 This option implies --pull, even on local repositories.
392 392
393 393 See pull for valid source format details.
394 394
395 395 It is possible to specify an ssh:// URL as the destination, but no
396 396 .hg/hgrc and working directory will be created on the remote side.
397 397 Look at the help text for the pull command for important details
398 398 about ssh:// URLs.
399 399 """
400 400 setremoteconfig(ui, opts)
401 401 hg.clone(ui, ui.expandpath(source), dest,
402 402 pull=opts['pull'],
403 403 stream=opts['uncompressed'],
404 404 rev=opts['rev'],
405 405 update=not opts['noupdate'])
406 406
407 407 def commit(ui, repo, *pats, **opts):
408 408 """commit the specified files or all outstanding changes
409 409
410 410 Commit changes to the given files into the repository.
411 411
412 412 If a list of files is omitted, all changes reported by "hg status"
413 413 will be committed.
414 414
415 415 If no commit message is specified, the editor configured in your hgrc
416 416 or in the EDITOR environment variable is started to enter a message.
417 417 """
418 418 message = logmessage(opts)
419 419
420 420 if opts['addremove']:
421 421 cmdutil.addremove(repo, pats, opts)
422 422 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
423 423 if pats:
424 424 status = repo.status(files=fns, match=match)
425 425 modified, added, removed, deleted, unknown = status[:5]
426 426 files = modified + added + removed
427 427 slist = None
428 428 for f in fns:
429 429 if f not in files:
430 430 rf = repo.wjoin(f)
431 431 if f in unknown:
432 432 raise util.Abort(_("file %s not tracked!") % rf)
433 433 try:
434 434 mode = os.lstat(rf)[stat.ST_MODE]
435 435 except OSError:
436 436 raise util.Abort(_("file %s not found!") % rf)
437 437 if stat.S_ISDIR(mode):
438 438 name = f + '/'
439 439 if slist is None:
440 440 slist = list(files)
441 441 slist.sort()
442 442 i = bisect.bisect(slist, name)
443 443 if i >= len(slist) or not slist[i].startswith(name):
444 444 raise util.Abort(_("no match under directory %s!")
445 445 % rf)
446 446 elif not stat.S_ISREG(mode):
447 447 raise util.Abort(_("can't commit %s: "
448 448 "unsupported file type!") % rf)
449 449 else:
450 450 files = []
451 451 try:
452 452 repo.commit(files, message, opts['user'], opts['date'], match,
453 453 force_editor=opts.get('force_editor'))
454 454 except ValueError, inst:
455 455 raise util.Abort(str(inst))
456 456
457 457 def docopy(ui, repo, pats, opts, wlock):
458 458 # called with the repo lock held
459 459 #
460 460 # hgsep => pathname that uses "/" to separate directories
461 461 # ossep => pathname that uses os.sep to separate directories
462 462 cwd = repo.getcwd()
463 463 errors = 0
464 464 copied = []
465 465 targets = {}
466 466
467 467 # abs: hgsep
468 468 # rel: ossep
469 469 # return: hgsep
470 470 def okaytocopy(abs, rel, exact):
471 471 reasons = {'?': _('is not managed'),
472 472 'a': _('has been marked for add'),
473 473 'r': _('has been marked for remove')}
474 474 state = repo.dirstate.state(abs)
475 475 reason = reasons.get(state)
476 476 if reason:
477 477 if state == 'a':
478 478 origsrc = repo.dirstate.copied(abs)
479 479 if origsrc is not None:
480 480 return origsrc
481 481 if exact:
482 482 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
483 483 else:
484 484 return abs
485 485
486 486 # origsrc: hgsep
487 487 # abssrc: hgsep
488 488 # relsrc: ossep
489 489 # target: ossep
490 490 def copy(origsrc, abssrc, relsrc, target, exact):
491 491 abstarget = util.canonpath(repo.root, cwd, target)
492 492 reltarget = util.pathto(cwd, abstarget)
493 493 prevsrc = targets.get(abstarget)
494 494 if prevsrc is not None:
495 495 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
496 496 (reltarget, util.localpath(abssrc),
497 497 util.localpath(prevsrc)))
498 498 return
499 499 if (not opts['after'] and os.path.exists(reltarget) or
500 500 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
501 501 if not opts['force']:
502 502 ui.warn(_('%s: not overwriting - file exists\n') %
503 503 reltarget)
504 504 return
505 505 if not opts['after'] and not opts.get('dry_run'):
506 506 os.unlink(reltarget)
507 507 if opts['after']:
508 508 if not os.path.exists(reltarget):
509 509 return
510 510 else:
511 511 targetdir = os.path.dirname(reltarget) or '.'
512 512 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
513 513 os.makedirs(targetdir)
514 514 try:
515 515 restore = repo.dirstate.state(abstarget) == 'r'
516 516 if restore and not opts.get('dry_run'):
517 517 repo.undelete([abstarget], wlock)
518 518 try:
519 519 if not opts.get('dry_run'):
520 520 util.copyfile(relsrc, reltarget)
521 521 restore = False
522 522 finally:
523 523 if restore:
524 524 repo.remove([abstarget], wlock)
525 525 except IOError, inst:
526 526 if inst.errno == errno.ENOENT:
527 527 ui.warn(_('%s: deleted in working copy\n') % relsrc)
528 528 else:
529 529 ui.warn(_('%s: cannot copy - %s\n') %
530 530 (relsrc, inst.strerror))
531 531 errors += 1
532 532 return
533 533 if ui.verbose or not exact:
534 534 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
535 535 targets[abstarget] = abssrc
536 536 if abstarget != origsrc and not opts.get('dry_run'):
537 537 repo.copy(origsrc, abstarget, wlock)
538 538 copied.append((abssrc, relsrc, exact))
539 539
540 540 # pat: ossep
541 541 # dest ossep
542 542 # srcs: list of (hgsep, hgsep, ossep, bool)
543 543 # return: function that takes hgsep and returns ossep
544 544 def targetpathfn(pat, dest, srcs):
545 545 if os.path.isdir(pat):
546 546 abspfx = util.canonpath(repo.root, cwd, pat)
547 547 abspfx = util.localpath(abspfx)
548 548 if destdirexists:
549 549 striplen = len(os.path.split(abspfx)[0])
550 550 else:
551 551 striplen = len(abspfx)
552 552 if striplen:
553 553 striplen += len(os.sep)
554 554 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
555 555 elif destdirexists:
556 556 res = lambda p: os.path.join(dest,
557 557 os.path.basename(util.localpath(p)))
558 558 else:
559 559 res = lambda p: dest
560 560 return res
561 561
562 562 # pat: ossep
563 563 # dest ossep
564 564 # srcs: list of (hgsep, hgsep, ossep, bool)
565 565 # return: function that takes hgsep and returns ossep
566 566 def targetpathafterfn(pat, dest, srcs):
567 567 if util.patkind(pat, None)[0]:
568 568 # a mercurial pattern
569 569 res = lambda p: os.path.join(dest,
570 570 os.path.basename(util.localpath(p)))
571 571 else:
572 572 abspfx = util.canonpath(repo.root, cwd, pat)
573 573 if len(abspfx) < len(srcs[0][0]):
574 574 # A directory. Either the target path contains the last
575 575 # component of the source path or it does not.
576 576 def evalpath(striplen):
577 577 score = 0
578 578 for s in srcs:
579 579 t = os.path.join(dest, util.localpath(s[0])[striplen:])
580 580 if os.path.exists(t):
581 581 score += 1
582 582 return score
583 583
584 584 abspfx = util.localpath(abspfx)
585 585 striplen = len(abspfx)
586 586 if striplen:
587 587 striplen += len(os.sep)
588 588 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
589 589 score = evalpath(striplen)
590 590 striplen1 = len(os.path.split(abspfx)[0])
591 591 if striplen1:
592 592 striplen1 += len(os.sep)
593 593 if evalpath(striplen1) > score:
594 594 striplen = striplen1
595 595 res = lambda p: os.path.join(dest,
596 596 util.localpath(p)[striplen:])
597 597 else:
598 598 # a file
599 599 if destdirexists:
600 600 res = lambda p: os.path.join(dest,
601 601 os.path.basename(util.localpath(p)))
602 602 else:
603 603 res = lambda p: dest
604 604 return res
605 605
606 606
607 607 pats = list(pats)
608 608 if not pats:
609 609 raise util.Abort(_('no source or destination specified'))
610 610 if len(pats) == 1:
611 611 raise util.Abort(_('no destination specified'))
612 612 dest = pats.pop()
613 613 destdirexists = os.path.isdir(dest)
614 614 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
615 615 raise util.Abort(_('with multiple sources, destination must be an '
616 616 'existing directory'))
617 617 if opts['after']:
618 618 tfn = targetpathafterfn
619 619 else:
620 620 tfn = targetpathfn
621 621 copylist = []
622 622 for pat in pats:
623 623 srcs = []
624 624 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
625 625 origsrc = okaytocopy(abssrc, relsrc, exact)
626 626 if origsrc:
627 627 srcs.append((origsrc, abssrc, relsrc, exact))
628 628 if not srcs:
629 629 continue
630 630 copylist.append((tfn(pat, dest, srcs), srcs))
631 631 if not copylist:
632 632 raise util.Abort(_('no files to copy'))
633 633
634 634 for targetpath, srcs in copylist:
635 635 for origsrc, abssrc, relsrc, exact in srcs:
636 636 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
637 637
638 638 if errors:
639 639 ui.warn(_('(consider using --after)\n'))
640 640 return errors, copied
641 641
642 642 def copy(ui, repo, *pats, **opts):
643 643 """mark files as copied for the next commit
644 644
645 645 Mark dest as having copies of source files. If dest is a
646 646 directory, copies are put in that directory. If dest is a file,
647 647 there can only be one source.
648 648
649 649 By default, this command copies the contents of files as they
650 650 stand in the working directory. If invoked with --after, the
651 651 operation is recorded, but no copying is performed.
652 652
653 653 This command takes effect in the next commit. To undo a copy
654 654 before that, see hg revert.
655 655 """
656 656 wlock = repo.wlock(0)
657 657 errs, copied = docopy(ui, repo, pats, opts, wlock)
658 658 return errs
659 659
660 660 def debugancestor(ui, index, rev1, rev2):
661 661 """find the ancestor revision of two revisions in a given index"""
662 662 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
663 663 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
664 664 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
665 665
666 666 def debugcomplete(ui, cmd='', **opts):
667 667 """returns the completion list associated with the given command"""
668 668
669 669 if opts['options']:
670 670 options = []
671 671 otables = [globalopts]
672 672 if cmd:
673 673 aliases, entry = findcmd(ui, cmd)
674 674 otables.append(entry[1])
675 675 for t in otables:
676 676 for o in t:
677 677 if o[0]:
678 678 options.append('-%s' % o[0])
679 679 options.append('--%s' % o[1])
680 680 ui.write("%s\n" % "\n".join(options))
681 681 return
682 682
683 683 clist = findpossible(ui, cmd).keys()
684 684 clist.sort()
685 685 ui.write("%s\n" % "\n".join(clist))
686 686
687 687 def debugrebuildstate(ui, repo, rev=""):
688 688 """rebuild the dirstate as it would look like for the given revision"""
689 689 if rev == "":
690 690 rev = repo.changelog.tip()
691 691 ctx = repo.changectx(rev)
692 692 files = ctx.manifest()
693 693 wlock = repo.wlock()
694 694 repo.dirstate.rebuild(rev, files)
695 695
696 696 def debugcheckstate(ui, repo):
697 697 """validate the correctness of the current dirstate"""
698 698 parent1, parent2 = repo.dirstate.parents()
699 699 repo.dirstate.read()
700 700 dc = repo.dirstate.map
701 701 keys = dc.keys()
702 702 keys.sort()
703 703 m1 = repo.changectx(parent1).manifest()
704 704 m2 = repo.changectx(parent2).manifest()
705 705 errors = 0
706 706 for f in dc:
707 707 state = repo.dirstate.state(f)
708 708 if state in "nr" and f not in m1:
709 709 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
710 710 errors += 1
711 711 if state in "a" and f in m1:
712 712 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
713 713 errors += 1
714 714 if state in "m" and f not in m1 and f not in m2:
715 715 ui.warn(_("%s in state %s, but not in either manifest\n") %
716 716 (f, state))
717 717 errors += 1
718 718 for f in m1:
719 719 state = repo.dirstate.state(f)
720 720 if state not in "nrm":
721 721 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
722 722 errors += 1
723 723 if errors:
724 724 error = _(".hg/dirstate inconsistent with current parent's manifest")
725 725 raise util.Abort(error)
726 726
727 727 def showconfig(ui, repo, *values, **opts):
728 728 """show combined config settings from all hgrc files
729 729
730 730 With no args, print names and values of all config items.
731 731
732 732 With one arg of the form section.name, print just the value of
733 733 that config item.
734 734
735 735 With multiple args, print names and values of all config items
736 736 with matching section names."""
737 737
738 738 untrusted = bool(opts.get('untrusted'))
739 739 if values:
740 740 if len([v for v in values if '.' in v]) > 1:
741 741 raise util.Abort(_('only one config item permitted'))
742 742 for section, name, value in ui.walkconfig(untrusted=untrusted):
743 743 sectname = section + '.' + name
744 744 if values:
745 745 for v in values:
746 746 if v == section:
747 747 ui.write('%s=%s\n' % (sectname, value))
748 748 elif v == sectname:
749 749 ui.write(value, '\n')
750 750 else:
751 751 ui.write('%s=%s\n' % (sectname, value))
752 752
753 753 def debugsetparents(ui, repo, rev1, rev2=None):
754 754 """manually set the parents of the current working directory
755 755
756 756 This is useful for writing repository conversion tools, but should
757 757 be used with care.
758 758 """
759 759
760 760 if not rev2:
761 761 rev2 = hex(nullid)
762 762
763 763 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
764 764
765 765 def debugstate(ui, repo):
766 766 """show the contents of the current dirstate"""
767 767 repo.dirstate.read()
768 768 dc = repo.dirstate.map
769 769 keys = dc.keys()
770 770 keys.sort()
771 771 for file_ in keys:
772 772 ui.write("%c %3o %10d %s %s\n"
773 773 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
774 774 time.strftime("%x %X",
775 775 time.localtime(dc[file_][3])), file_))
776 776 for f in repo.dirstate.copies():
777 777 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
778 778
779 779 def debugdata(ui, file_, rev):
780 780 """dump the contents of an data file revision"""
781 781 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
782 782 file_[:-2] + ".i", file_, 0)
783 783 try:
784 784 ui.write(r.revision(r.lookup(rev)))
785 785 except KeyError:
786 786 raise util.Abort(_('invalid revision identifier %s') % rev)
787 787
788 788 def debugdate(ui, date, range=None, **opts):
789 789 """parse and display a date"""
790 790 if opts["extended"]:
791 791 d = util.parsedate(date, util.extendeddateformats)
792 792 else:
793 793 d = util.parsedate(date)
794 794 ui.write("internal: %s %s\n" % d)
795 795 ui.write("standard: %s\n" % util.datestr(d))
796 796 if range:
797 797 m = util.matchdate(range)
798 798 ui.write("match: %s\n" % m(d[0]))
799 799
800 800 def debugindex(ui, file_):
801 801 """dump the contents of an index file"""
802 802 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
803 803 ui.write(" rev offset length base linkrev" +
804 804 " nodeid p1 p2\n")
805 805 for i in xrange(r.count()):
806 806 node = r.node(i)
807 807 pp = r.parents(node)
808 808 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
809 809 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
810 810 short(node), short(pp[0]), short(pp[1])))
811 811
812 812 def debugindexdot(ui, file_):
813 813 """dump an index DAG as a .dot file"""
814 814 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
815 815 ui.write("digraph G {\n")
816 816 for i in xrange(r.count()):
817 817 node = r.node(i)
818 818 pp = r.parents(node)
819 819 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
820 820 if pp[1] != nullid:
821 821 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
822 822 ui.write("}\n")
823 823
824 824 def debuginstall(ui):
825 825 '''test Mercurial installation'''
826 826
827 827 def writetemp(contents):
828 828 (fd, name) = tempfile.mkstemp()
829 829 f = os.fdopen(fd, "wb")
830 830 f.write(contents)
831 831 f.close()
832 832 return name
833 833
834 834 problems = 0
835 835
836 836 # encoding
837 837 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
838 838 try:
839 839 util.fromlocal("test")
840 840 except util.Abort, inst:
841 841 ui.write(" %s\n" % inst)
842 842 ui.write(_(" (check that your locale is properly set)\n"))
843 843 problems += 1
844 844
845 845 # compiled modules
846 846 ui.status(_("Checking extensions...\n"))
847 847 try:
848 848 import bdiff, mpatch, base85
849 849 except Exception, inst:
850 850 ui.write(" %s\n" % inst)
851 851 ui.write(_(" One or more extensions could not be found"))
852 852 ui.write(_(" (check that you compiled the extensions)\n"))
853 853 problems += 1
854 854
855 855 # templates
856 856 ui.status(_("Checking templates...\n"))
857 857 try:
858 858 import templater
859 859 t = templater.templater(templater.templatepath("map-cmdline.default"))
860 860 except Exception, inst:
861 861 ui.write(" %s\n" % inst)
862 862 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
863 863 problems += 1
864 864
865 865 # patch
866 866 ui.status(_("Checking patch...\n"))
867 867 path = os.environ.get('PATH', '')
868 868 patcher = util.find_in_path('gpatch', path,
869 869 util.find_in_path('patch', path, None))
870 870 if not patcher:
871 871 ui.write(_(" Can't find patch or gpatch in PATH\n"))
872 872 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
873 873 problems += 1
874 874 else:
875 875 # actually attempt a patch here
876 876 a = "1\n2\n3\n4\n"
877 877 b = "1\n2\n3\ninsert\n4\n"
878 878 d = mdiff.unidiff(a, None, b, None, "a")
879 879 fa = writetemp(a)
880 880 fd = writetemp(d)
881 881 fp = os.popen('%s %s %s' % (patcher, fa, fd))
882 882 files = []
883 883 output = ""
884 884 for line in fp:
885 885 output += line
886 886 if line.startswith('patching file '):
887 887 pf = util.parse_patch_output(line.rstrip())
888 888 files.append(pf)
889 889 if files != [fa]:
890 890 ui.write(_(" unexpected patch output!"))
891 891 ui.write(_(" (you may have an incompatible version of patch)\n"))
892 892 ui.write(output)
893 893 problems += 1
894 894 a = file(fa).read()
895 895 if a != b:
896 896 ui.write(_(" patch test failed!"))
897 897 ui.write(_(" (you may have an incompatible version of patch)\n"))
898 898 problems += 1
899 899 os.unlink(fa)
900 900 os.unlink(fd)
901 901
902 902 # merge helper
903 903 ui.status(_("Checking merge helper...\n"))
904 904 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
905 905 or "hgmerge")
906 906 cmdpath = util.find_in_path(cmd, path)
907 907 if not cmdpath:
908 908 cmdpath = util.find_in_path(cmd.split()[0], path)
909 909 if not cmdpath:
910 910 if cmd == 'hgmerge':
911 911 ui.write(_(" No merge helper set and can't find default"
912 912 " hgmerge script in PATH\n"))
913 913 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
914 914 else:
915 915 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
916 916 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
917 917 problems += 1
918 918 else:
919 919 # actually attempt a patch here
920 920 fa = writetemp("1\n2\n3\n4\n")
921 921 fl = writetemp("1\n2\n3\ninsert\n4\n")
922 922 fr = writetemp("begin\n1\n2\n3\n4\n")
923 923 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
924 924 if r:
925 925 ui.write(_(" got unexpected merge error %d!") % r)
926 926 problems += 1
927 927 m = file(fl).read()
928 928 if m != "begin\n1\n2\n3\ninsert\n4\n":
929 929 ui.write(_(" got unexpected merge results!") % r)
930 930 ui.write(_(" (your merge helper may have the"
931 931 " wrong argument order)\n"))
932 932 ui.write(m)
933 933 os.unlink(fa)
934 934 os.unlink(fl)
935 935 os.unlink(fr)
936 936
937 937 # editor
938 938 ui.status(_("Checking commit editor...\n"))
939 939 editor = (os.environ.get("HGEDITOR") or
940 940 ui.config("ui", "editor") or
941 941 os.environ.get("EDITOR", "vi"))
942 942 cmdpath = util.find_in_path(editor, path)
943 943 if not cmdpath:
944 944 cmdpath = util.find_in_path(editor.split()[0], path)
945 945 if not cmdpath:
946 946 if editor == 'vi':
947 947 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
948 948 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
949 949 else:
950 950 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
951 951 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
952 952 problems += 1
953 953
954 954 # check username
955 955 ui.status(_("Checking username...\n"))
956 956 user = os.environ.get("HGUSER")
957 957 if user is None:
958 958 user = ui.config("ui", "username")
959 959 if user is None:
960 960 user = os.environ.get("EMAIL")
961 961 if not user:
962 962 ui.warn(" ")
963 963 ui.username()
964 964 ui.write(_(" (specify a username in your .hgrc file)\n"))
965 965
966 966 if not problems:
967 967 ui.status(_("No problems detected\n"))
968 968 else:
969 969 ui.write(_("%s problems detected,"
970 970 " please check your install!\n") % problems)
971 971
972 972 return problems
973 973
974 974 def debugrename(ui, repo, file1, *pats, **opts):
975 975 """dump rename information"""
976 976
977 977 ctx = repo.changectx(opts.get('rev', 'tip'))
978 978 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
979 979 ctx.node()):
980 980 m = ctx.filectx(abs).renamed()
981 981 if m:
982 982 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
983 983 else:
984 984 ui.write(_("%s not renamed\n") % rel)
985 985
986 986 def debugwalk(ui, repo, *pats, **opts):
987 987 """show how files match on given patterns"""
988 988 items = list(cmdutil.walk(repo, pats, opts))
989 989 if not items:
990 990 return
991 991 fmt = '%%s %%-%ds %%-%ds %%s' % (
992 992 max([len(abs) for (src, abs, rel, exact) in items]),
993 993 max([len(rel) for (src, abs, rel, exact) in items]))
994 994 for src, abs, rel, exact in items:
995 995 line = fmt % (src, abs, rel, exact and 'exact' or '')
996 996 ui.write("%s\n" % line.rstrip())
997 997
998 998 def diff(ui, repo, *pats, **opts):
999 999 """diff repository (or selected files)
1000 1000
1001 1001 Show differences between revisions for the specified files.
1002 1002
1003 1003 Differences between files are shown using the unified diff format.
1004 1004
1005 1005 NOTE: diff may generate unexpected results for merges, as it will
1006 1006 default to comparing against the working directory's first parent
1007 1007 changeset if no revisions are specified.
1008 1008
1009 1009 When two revision arguments are given, then changes are shown
1010 1010 between those revisions. If only one revision is specified then
1011 1011 that revision is compared to the working directory, and, when no
1012 1012 revisions are specified, the working directory files are compared
1013 1013 to its parent.
1014 1014
1015 1015 Without the -a option, diff will avoid generating diffs of files
1016 1016 it detects as binary. With -a, diff will generate a diff anyway,
1017 1017 probably with undesirable results.
1018 1018 """
1019 1019 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1020 1020
1021 1021 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1022 1022
1023 1023 patch.diff(repo, node1, node2, fns, match=matchfn,
1024 1024 opts=patch.diffopts(ui, opts))
1025 1025
1026 1026 def export(ui, repo, *changesets, **opts):
1027 1027 """dump the header and diffs for one or more changesets
1028 1028
1029 1029 Print the changeset header and diffs for one or more revisions.
1030 1030
1031 1031 The information shown in the changeset header is: author,
1032 1032 changeset hash, parent(s) and commit comment.
1033 1033
1034 1034 NOTE: export may generate unexpected diff output for merge changesets,
1035 1035 as it will compare the merge changeset against its first parent only.
1036 1036
1037 1037 Output may be to a file, in which case the name of the file is
1038 1038 given using a format string. The formatting rules are as follows:
1039 1039
1040 1040 %% literal "%" character
1041 1041 %H changeset hash (40 bytes of hexadecimal)
1042 1042 %N number of patches being generated
1043 1043 %R changeset revision number
1044 1044 %b basename of the exporting repository
1045 1045 %h short-form changeset hash (12 bytes of hexadecimal)
1046 1046 %n zero-padded sequence number, starting at 1
1047 1047 %r zero-padded changeset revision number
1048 1048
1049 1049 Without the -a option, export will avoid generating diffs of files
1050 1050 it detects as binary. With -a, export will generate a diff anyway,
1051 1051 probably with undesirable results.
1052 1052
1053 1053 With the --switch-parent option, the diff will be against the second
1054 1054 parent. It can be useful to review a merge.
1055 1055 """
1056 1056 if not changesets:
1057 1057 raise util.Abort(_("export requires at least one changeset"))
1058 1058 revs = cmdutil.revrange(repo, changesets)
1059 1059 if len(revs) > 1:
1060 1060 ui.note(_('exporting patches:\n'))
1061 1061 else:
1062 1062 ui.note(_('exporting patch:\n'))
1063 1063 patch.export(repo, revs, template=opts['output'],
1064 1064 switch_parent=opts['switch_parent'],
1065 1065 opts=patch.diffopts(ui, opts))
1066 1066
1067 1067 def grep(ui, repo, pattern, *pats, **opts):
1068 1068 """search for a pattern in specified files and revisions
1069 1069
1070 1070 Search revisions of files for a regular expression.
1071 1071
1072 1072 This command behaves differently than Unix grep. It only accepts
1073 1073 Python/Perl regexps. It searches repository history, not the
1074 1074 working directory. It always prints the revision number in which
1075 1075 a match appears.
1076 1076
1077 1077 By default, grep only prints output for the first revision of a
1078 1078 file in which it finds a match. To get it to print every revision
1079 1079 that contains a change in match status ("-" for a match that
1080 1080 becomes a non-match, or "+" for a non-match that becomes a match),
1081 1081 use the --all flag.
1082 1082 """
1083 1083 reflags = 0
1084 1084 if opts['ignore_case']:
1085 1085 reflags |= re.I
1086 1086 regexp = re.compile(pattern, reflags)
1087 1087 sep, eol = ':', '\n'
1088 1088 if opts['print0']:
1089 1089 sep = eol = '\0'
1090 1090
1091 1091 fcache = {}
1092 1092 def getfile(fn):
1093 1093 if fn not in fcache:
1094 1094 fcache[fn] = repo.file(fn)
1095 1095 return fcache[fn]
1096 1096
1097 1097 def matchlines(body):
1098 1098 begin = 0
1099 1099 linenum = 0
1100 1100 while True:
1101 1101 match = regexp.search(body, begin)
1102 1102 if not match:
1103 1103 break
1104 1104 mstart, mend = match.span()
1105 1105 linenum += body.count('\n', begin, mstart) + 1
1106 1106 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1107 1107 lend = body.find('\n', mend)
1108 1108 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1109 1109 begin = lend + 1
1110 1110
1111 1111 class linestate(object):
1112 1112 def __init__(self, line, linenum, colstart, colend):
1113 1113 self.line = line
1114 1114 self.linenum = linenum
1115 1115 self.colstart = colstart
1116 1116 self.colend = colend
1117 1117
1118 1118 def __eq__(self, other):
1119 1119 return self.line == other.line
1120 1120
1121 1121 matches = {}
1122 1122 copies = {}
1123 1123 def grepbody(fn, rev, body):
1124 1124 matches[rev].setdefault(fn, [])
1125 1125 m = matches[rev][fn]
1126 1126 for lnum, cstart, cend, line in matchlines(body):
1127 1127 s = linestate(line, lnum, cstart, cend)
1128 1128 m.append(s)
1129 1129
1130 1130 def difflinestates(a, b):
1131 1131 sm = difflib.SequenceMatcher(None, a, b)
1132 1132 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1133 1133 if tag == 'insert':
1134 1134 for i in xrange(blo, bhi):
1135 1135 yield ('+', b[i])
1136 1136 elif tag == 'delete':
1137 1137 for i in xrange(alo, ahi):
1138 1138 yield ('-', a[i])
1139 1139 elif tag == 'replace':
1140 1140 for i in xrange(alo, ahi):
1141 1141 yield ('-', a[i])
1142 1142 for i in xrange(blo, bhi):
1143 1143 yield ('+', b[i])
1144 1144
1145 1145 prev = {}
1146 1146 def display(fn, rev, states, prevstates):
1147 1147 found = False
1148 1148 filerevmatches = {}
1149 1149 r = prev.get(fn, -1)
1150 1150 if opts['all']:
1151 1151 iter = difflinestates(states, prevstates)
1152 1152 else:
1153 1153 iter = [('', l) for l in prevstates]
1154 1154 for change, l in iter:
1155 1155 cols = [fn, str(r)]
1156 1156 if opts['line_number']:
1157 1157 cols.append(str(l.linenum))
1158 1158 if opts['all']:
1159 1159 cols.append(change)
1160 1160 if opts['user']:
1161 1161 cols.append(ui.shortuser(get(r)[1]))
1162 1162 if opts['files_with_matches']:
1163 1163 c = (fn, r)
1164 1164 if c in filerevmatches:
1165 1165 continue
1166 1166 filerevmatches[c] = 1
1167 1167 else:
1168 1168 cols.append(l.line)
1169 1169 ui.write(sep.join(cols), eol)
1170 1170 found = True
1171 1171 return found
1172 1172
1173 1173 fstate = {}
1174 1174 skip = {}
1175 1175 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1176 1176 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1177 1177 found = False
1178 1178 follow = opts.get('follow')
1179 1179 for st, rev, fns in changeiter:
1180 1180 if st == 'window':
1181 1181 matches.clear()
1182 1182 elif st == 'add':
1183 1183 mf = repo.changectx(rev).manifest()
1184 1184 matches[rev] = {}
1185 1185 for fn in fns:
1186 1186 if fn in skip:
1187 1187 continue
1188 1188 fstate.setdefault(fn, {})
1189 1189 try:
1190 1190 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1191 1191 if follow:
1192 1192 copied = getfile(fn).renamed(mf[fn])
1193 1193 if copied:
1194 1194 copies.setdefault(rev, {})[fn] = copied[0]
1195 1195 except KeyError:
1196 1196 pass
1197 1197 elif st == 'iter':
1198 1198 states = matches[rev].items()
1199 1199 states.sort()
1200 1200 for fn, m in states:
1201 1201 copy = copies.get(rev, {}).get(fn)
1202 1202 if fn in skip:
1203 1203 if copy:
1204 1204 skip[copy] = True
1205 1205 continue
1206 1206 if fn in prev or fstate[fn]:
1207 1207 r = display(fn, rev, m, fstate[fn])
1208 1208 found = found or r
1209 1209 if r and not opts['all']:
1210 1210 skip[fn] = True
1211 1211 if copy:
1212 1212 skip[copy] = True
1213 1213 fstate[fn] = m
1214 1214 if copy:
1215 1215 fstate[copy] = m
1216 1216 prev[fn] = rev
1217 1217
1218 1218 fstate = fstate.items()
1219 1219 fstate.sort()
1220 1220 for fn, state in fstate:
1221 1221 if fn in skip:
1222 1222 continue
1223 1223 if fn not in copies.get(prev[fn], {}):
1224 1224 found = display(fn, rev, {}, state) or found
1225 1225 return (not found and 1) or 0
1226 1226
1227 1227 def heads(ui, repo, **opts):
1228 1228 """show current repository heads
1229 1229
1230 1230 Show all repository head changesets.
1231 1231
1232 1232 Repository "heads" are changesets that don't have children
1233 1233 changesets. They are where development generally takes place and
1234 1234 are the usual targets for update and merge operations.
1235 1235 """
1236 1236 if opts['rev']:
1237 1237 heads = repo.heads(repo.lookup(opts['rev']))
1238 1238 else:
1239 1239 heads = repo.heads()
1240 1240 displayer = cmdutil.show_changeset(ui, repo, opts)
1241 1241 for n in heads:
1242 1242 displayer.show(changenode=n)
1243 1243
1244 1244 def help_(ui, name=None, with_version=False):
1245 1245 """show help for a command, extension, or list of commands
1246 1246
1247 1247 With no arguments, print a list of commands and short help.
1248 1248
1249 1249 Given a command name, print help for that command.
1250 1250
1251 1251 Given an extension name, print help for that extension, and the
1252 1252 commands it provides."""
1253 1253 option_lists = []
1254 1254
1255 1255 def helpcmd(name):
1256 1256 if with_version:
1257 1257 version_(ui)
1258 1258 ui.write('\n')
1259 1259 aliases, i = findcmd(ui, name)
1260 1260 # synopsis
1261 1261 ui.write("%s\n\n" % i[2])
1262 1262
1263 1263 # description
1264 1264 doc = i[0].__doc__
1265 1265 if not doc:
1266 1266 doc = _("(No help text available)")
1267 1267 if ui.quiet:
1268 1268 doc = doc.splitlines(0)[0]
1269 1269 ui.write("%s\n" % doc.rstrip())
1270 1270
1271 1271 if not ui.quiet:
1272 1272 # aliases
1273 1273 if len(aliases) > 1:
1274 1274 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1275 1275
1276 1276 # options
1277 1277 if i[1]:
1278 1278 option_lists.append(("options", i[1]))
1279 1279
1280 1280 def helplist(select=None):
1281 1281 h = {}
1282 1282 cmds = {}
1283 1283 for c, e in table.items():
1284 1284 f = c.split("|", 1)[0]
1285 1285 if select and not select(f):
1286 1286 continue
1287 1287 if name == "shortlist" and not f.startswith("^"):
1288 1288 continue
1289 1289 f = f.lstrip("^")
1290 1290 if not ui.debugflag and f.startswith("debug"):
1291 1291 continue
1292 1292 doc = e[0].__doc__
1293 1293 if not doc:
1294 1294 doc = _("(No help text available)")
1295 1295 h[f] = doc.splitlines(0)[0].rstrip()
1296 1296 cmds[f] = c.lstrip("^")
1297 1297
1298 1298 fns = h.keys()
1299 1299 fns.sort()
1300 1300 m = max(map(len, fns))
1301 1301 for f in fns:
1302 1302 if ui.verbose:
1303 1303 commands = cmds[f].replace("|",", ")
1304 1304 ui.write(" %s:\n %s\n"%(commands, h[f]))
1305 1305 else:
1306 1306 ui.write(' %-*s %s\n' % (m, f, h[f]))
1307 1307
1308 1308 def helptopic(name):
1309 1309 v = None
1310 1310 for i in help.helptable:
1311 1311 l = i.split('|')
1312 1312 if name in l:
1313 1313 v = i
1314 1314 header = l[-1]
1315 1315 if not v:
1316 1316 raise UnknownCommand(name)
1317 1317
1318 1318 # description
1319 1319 doc = help.helptable[v]
1320 1320 if not doc:
1321 1321 doc = _("(No help text available)")
1322 1322 if callable(doc):
1323 1323 doc = doc()
1324 1324
1325 1325 ui.write("%s\n" % header)
1326 1326 ui.write("%s\n" % doc.rstrip())
1327 1327
1328 1328 def helpext(name):
1329 1329 try:
1330 1330 mod = findext(name)
1331 1331 except KeyError:
1332 1332 raise UnknownCommand(name)
1333 1333
1334 1334 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1335 1335 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1336 1336 for d in doc[1:]:
1337 1337 ui.write(d, '\n')
1338 1338
1339 1339 ui.status('\n')
1340
1341 try:
1342 ct = mod.cmdtable
1343 except AttributeError:
1344 ui.status(_('no commands defined\n'))
1345 return
1346
1340 1347 if ui.verbose:
1341 1348 ui.status(_('list of commands:\n\n'))
1342 1349 else:
1343 1350 ui.status(_('list of commands (use "hg help -v %s" '
1344 1351 'to show aliases and global options):\n\n') % name)
1345 1352
1346 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
1353 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1347 1354 helplist(modcmds.has_key)
1348 1355
1349 1356 if name and name != 'shortlist':
1350 1357 i = None
1351 1358 for f in (helpcmd, helptopic, helpext):
1352 1359 try:
1353 1360 f(name)
1354 1361 i = None
1355 1362 break
1356 1363 except UnknownCommand, inst:
1357 1364 i = inst
1358 1365 if i:
1359 1366 raise i
1360 1367
1361 1368 else:
1362 1369 # program name
1363 1370 if ui.verbose or with_version:
1364 1371 version_(ui)
1365 1372 else:
1366 1373 ui.status(_("Mercurial Distributed SCM\n"))
1367 1374 ui.status('\n')
1368 1375
1369 1376 # list of commands
1370 1377 if name == "shortlist":
1371 1378 ui.status(_('basic commands (use "hg help" '
1372 1379 'for the full list or option "-v" for details):\n\n'))
1373 1380 elif ui.verbose:
1374 1381 ui.status(_('list of commands:\n\n'))
1375 1382 else:
1376 1383 ui.status(_('list of commands (use "hg help -v" '
1377 1384 'to show aliases and global options):\n\n'))
1378 1385
1379 1386 helplist()
1380 1387
1381 1388 # global options
1382 1389 if ui.verbose:
1383 1390 option_lists.append(("global options", globalopts))
1384 1391
1385 1392 # list all option lists
1386 1393 opt_output = []
1387 1394 for title, options in option_lists:
1388 1395 opt_output.append(("\n%s:\n" % title, None))
1389 1396 for shortopt, longopt, default, desc in options:
1390 1397 if "DEPRECATED" in desc and not ui.verbose: continue
1391 1398 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1392 1399 longopt and " --%s" % longopt),
1393 1400 "%s%s" % (desc,
1394 1401 default
1395 1402 and _(" (default: %s)") % default
1396 1403 or "")))
1397 1404
1398 1405 if opt_output:
1399 1406 opts_len = max([len(line[0]) for line in opt_output if line[1]])
1400 1407 for first, second in opt_output:
1401 1408 if second:
1402 1409 ui.write(" %-*s %s\n" % (opts_len, first, second))
1403 1410 else:
1404 1411 ui.write("%s\n" % first)
1405 1412
1406 1413 def identify(ui, repo):
1407 1414 """print information about the working copy
1408 1415
1409 1416 Print a short summary of the current state of the repo.
1410 1417
1411 1418 This summary identifies the repository state using one or two parent
1412 1419 hash identifiers, followed by a "+" if there are uncommitted changes
1413 1420 in the working directory, followed by a list of tags for this revision.
1414 1421 """
1415 1422 parents = [p for p in repo.dirstate.parents() if p != nullid]
1416 1423 if not parents:
1417 1424 ui.write(_("unknown\n"))
1418 1425 return
1419 1426
1420 1427 hexfunc = ui.debugflag and hex or short
1421 1428 modified, added, removed, deleted = repo.status()[:4]
1422 1429 output = ["%s%s" %
1423 1430 ('+'.join([hexfunc(parent) for parent in parents]),
1424 1431 (modified or added or removed or deleted) and "+" or "")]
1425 1432
1426 1433 if not ui.quiet:
1427 1434
1428 1435 branch = util.tolocal(repo.workingctx().branch())
1429 1436 if branch:
1430 1437 output.append("(%s)" % branch)
1431 1438
1432 1439 # multiple tags for a single parent separated by '/'
1433 1440 parenttags = ['/'.join(tags)
1434 1441 for tags in map(repo.nodetags, parents) if tags]
1435 1442 # tags for multiple parents separated by ' + '
1436 1443 if parenttags:
1437 1444 output.append(' + '.join(parenttags))
1438 1445
1439 1446 ui.write("%s\n" % ' '.join(output))
1440 1447
1441 1448 def import_(ui, repo, patch1, *patches, **opts):
1442 1449 """import an ordered set of patches
1443 1450
1444 1451 Import a list of patches and commit them individually.
1445 1452
1446 1453 If there are outstanding changes in the working directory, import
1447 1454 will abort unless given the -f flag.
1448 1455
1449 1456 You can import a patch straight from a mail message. Even patches
1450 1457 as attachments work (body part must be type text/plain or
1451 1458 text/x-patch to be used). From and Subject headers of email
1452 1459 message are used as default committer and commit message. All
1453 1460 text/plain body parts before first diff are added to commit
1454 1461 message.
1455 1462
1456 1463 If imported patch was generated by hg export, user and description
1457 1464 from patch override values from message headers and body. Values
1458 1465 given on command line with -m and -u override these.
1459 1466
1460 1467 To read a patch from standard input, use patch name "-".
1461 1468 """
1462 1469 patches = (patch1,) + patches
1463 1470
1464 1471 if not opts['force']:
1465 1472 bail_if_changed(repo)
1466 1473
1467 1474 d = opts["base"]
1468 1475 strip = opts["strip"]
1469 1476
1470 1477 wlock = repo.wlock()
1471 1478 lock = repo.lock()
1472 1479
1473 1480 for p in patches:
1474 1481 pf = os.path.join(d, p)
1475 1482
1476 1483 if pf == '-':
1477 1484 ui.status(_("applying patch from stdin\n"))
1478 1485 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1479 1486 else:
1480 1487 ui.status(_("applying %s\n") % p)
1481 1488 tmpname, message, user, date = patch.extract(ui, file(pf))
1482 1489
1483 1490 if tmpname is None:
1484 1491 raise util.Abort(_('no diffs found'))
1485 1492
1486 1493 try:
1487 1494 cmdline_message = logmessage(opts)
1488 1495 if cmdline_message:
1489 1496 # pickup the cmdline msg
1490 1497 message = cmdline_message
1491 1498 elif message:
1492 1499 # pickup the patch msg
1493 1500 message = message.strip()
1494 1501 else:
1495 1502 # launch the editor
1496 1503 message = None
1497 1504 ui.debug(_('message:\n%s\n') % message)
1498 1505
1499 1506 files = {}
1500 1507 try:
1501 1508 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1502 1509 files=files)
1503 1510 finally:
1504 1511 files = patch.updatedir(ui, repo, files, wlock=wlock)
1505 1512 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1506 1513 finally:
1507 1514 os.unlink(tmpname)
1508 1515
1509 1516 def incoming(ui, repo, source="default", **opts):
1510 1517 """show new changesets found in source
1511 1518
1512 1519 Show new changesets found in the specified path/URL or the default
1513 1520 pull location. These are the changesets that would be pulled if a pull
1514 1521 was requested.
1515 1522
1516 1523 For remote repository, using --bundle avoids downloading the changesets
1517 1524 twice if the incoming is followed by a pull.
1518 1525
1519 1526 See pull for valid source format details.
1520 1527 """
1521 1528 source = ui.expandpath(source)
1522 1529 setremoteconfig(ui, opts)
1523 1530
1524 1531 other = hg.repository(ui, source)
1525 1532 incoming = repo.findincoming(other, force=opts["force"])
1526 1533 if not incoming:
1527 1534 try:
1528 1535 os.unlink(opts["bundle"])
1529 1536 except:
1530 1537 pass
1531 1538 ui.status(_("no changes found\n"))
1532 1539 return 1
1533 1540
1534 1541 cleanup = None
1535 1542 try:
1536 1543 fname = opts["bundle"]
1537 1544 if fname or not other.local():
1538 1545 # create a bundle (uncompressed if other repo is not local)
1539 1546 cg = other.changegroup(incoming, "incoming")
1540 1547 bundletype = other.local() and "HG10BZ" or "HG10UN"
1541 1548 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1542 1549 # keep written bundle?
1543 1550 if opts["bundle"]:
1544 1551 cleanup = None
1545 1552 if not other.local():
1546 1553 # use the created uncompressed bundlerepo
1547 1554 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1548 1555
1549 1556 revs = None
1550 1557 if opts['rev']:
1551 1558 revs = [other.lookup(rev) for rev in opts['rev']]
1552 1559 o = other.changelog.nodesbetween(incoming, revs)[0]
1553 1560 if opts['newest_first']:
1554 1561 o.reverse()
1555 1562 displayer = cmdutil.show_changeset(ui, other, opts)
1556 1563 for n in o:
1557 1564 parents = [p for p in other.changelog.parents(n) if p != nullid]
1558 1565 if opts['no_merges'] and len(parents) == 2:
1559 1566 continue
1560 1567 displayer.show(changenode=n)
1561 1568 finally:
1562 1569 if hasattr(other, 'close'):
1563 1570 other.close()
1564 1571 if cleanup:
1565 1572 os.unlink(cleanup)
1566 1573
1567 1574 def init(ui, dest=".", **opts):
1568 1575 """create a new repository in the given directory
1569 1576
1570 1577 Initialize a new repository in the given directory. If the given
1571 1578 directory does not exist, it is created.
1572 1579
1573 1580 If no directory is given, the current directory is used.
1574 1581
1575 1582 It is possible to specify an ssh:// URL as the destination.
1576 1583 Look at the help text for the pull command for important details
1577 1584 about ssh:// URLs.
1578 1585 """
1579 1586 setremoteconfig(ui, opts)
1580 1587 hg.repository(ui, dest, create=1)
1581 1588
1582 1589 def locate(ui, repo, *pats, **opts):
1583 1590 """locate files matching specific patterns
1584 1591
1585 1592 Print all files under Mercurial control whose names match the
1586 1593 given patterns.
1587 1594
1588 1595 This command searches the current directory and its
1589 1596 subdirectories. To search an entire repository, move to the root
1590 1597 of the repository.
1591 1598
1592 1599 If no patterns are given to match, this command prints all file
1593 1600 names.
1594 1601
1595 1602 If you want to feed the output of this command into the "xargs"
1596 1603 command, use the "-0" option to both this command and "xargs".
1597 1604 This will avoid the problem of "xargs" treating single filenames
1598 1605 that contain white space as multiple filenames.
1599 1606 """
1600 1607 end = opts['print0'] and '\0' or '\n'
1601 1608 rev = opts['rev']
1602 1609 if rev:
1603 1610 node = repo.lookup(rev)
1604 1611 else:
1605 1612 node = None
1606 1613
1607 1614 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1608 1615 head='(?:.*/|)'):
1609 1616 if not node and repo.dirstate.state(abs) == '?':
1610 1617 continue
1611 1618 if opts['fullpath']:
1612 1619 ui.write(os.path.join(repo.root, abs), end)
1613 1620 else:
1614 1621 ui.write(((pats and rel) or abs), end)
1615 1622
1616 1623 def log(ui, repo, *pats, **opts):
1617 1624 """show revision history of entire repository or files
1618 1625
1619 1626 Print the revision history of the specified files or the entire
1620 1627 project.
1621 1628
1622 1629 File history is shown without following rename or copy history of
1623 1630 files. Use -f/--follow with a file name to follow history across
1624 1631 renames and copies. --follow without a file name will only show
1625 1632 ancestors or descendants of the starting revision. --follow-first
1626 1633 only follows the first parent of merge revisions.
1627 1634
1628 1635 If no revision range is specified, the default is tip:0 unless
1629 1636 --follow is set, in which case the working directory parent is
1630 1637 used as the starting revision.
1631 1638
1632 1639 By default this command outputs: changeset id and hash, tags,
1633 1640 non-trivial parents, user, date and time, and a summary for each
1634 1641 commit. When the -v/--verbose switch is used, the list of changed
1635 1642 files and full commit message is shown.
1636 1643
1637 1644 NOTE: log -p may generate unexpected diff output for merge
1638 1645 changesets, as it will compare the merge changeset against its
1639 1646 first parent only. Also, the files: list will only reflect files
1640 1647 that are different from BOTH parents.
1641 1648
1642 1649 """
1643 1650
1644 1651 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1645 1652 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1646 1653
1647 1654 if opts['limit']:
1648 1655 try:
1649 1656 limit = int(opts['limit'])
1650 1657 except ValueError:
1651 1658 raise util.Abort(_('limit must be a positive integer'))
1652 1659 if limit <= 0: raise util.Abort(_('limit must be positive'))
1653 1660 else:
1654 1661 limit = sys.maxint
1655 1662 count = 0
1656 1663
1657 1664 if opts['copies'] and opts['rev']:
1658 1665 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1659 1666 else:
1660 1667 endrev = repo.changelog.count()
1661 1668 rcache = {}
1662 1669 ncache = {}
1663 1670 dcache = []
1664 1671 def getrenamed(fn, rev, man):
1665 1672 '''looks up all renames for a file (up to endrev) the first
1666 1673 time the file is given. It indexes on the changerev and only
1667 1674 parses the manifest if linkrev != changerev.
1668 1675 Returns rename info for fn at changerev rev.'''
1669 1676 if fn not in rcache:
1670 1677 rcache[fn] = {}
1671 1678 ncache[fn] = {}
1672 1679 fl = repo.file(fn)
1673 1680 for i in xrange(fl.count()):
1674 1681 node = fl.node(i)
1675 1682 lr = fl.linkrev(node)
1676 1683 renamed = fl.renamed(node)
1677 1684 rcache[fn][lr] = renamed
1678 1685 if renamed:
1679 1686 ncache[fn][node] = renamed
1680 1687 if lr >= endrev:
1681 1688 break
1682 1689 if rev in rcache[fn]:
1683 1690 return rcache[fn][rev]
1684 1691 mr = repo.manifest.rev(man)
1685 1692 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1686 1693 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1687 1694 if not dcache or dcache[0] != man:
1688 1695 dcache[:] = [man, repo.manifest.readdelta(man)]
1689 1696 if fn in dcache[1]:
1690 1697 return ncache[fn].get(dcache[1][fn])
1691 1698 return None
1692 1699
1693 1700 df = False
1694 1701 if opts["date"]:
1695 1702 df = util.matchdate(opts["date"])
1696 1703
1697 1704 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1698 1705 for st, rev, fns in changeiter:
1699 1706 if st == 'add':
1700 1707 changenode = repo.changelog.node(rev)
1701 1708 parents = [p for p in repo.changelog.parentrevs(rev)
1702 1709 if p != nullrev]
1703 1710 if opts['no_merges'] and len(parents) == 2:
1704 1711 continue
1705 1712 if opts['only_merges'] and len(parents) != 2:
1706 1713 continue
1707 1714
1708 1715 if df:
1709 1716 changes = get(rev)
1710 1717 if not df(changes[2][0]):
1711 1718 continue
1712 1719
1713 1720 if opts['keyword']:
1714 1721 changes = get(rev)
1715 1722 miss = 0
1716 1723 for k in [kw.lower() for kw in opts['keyword']]:
1717 1724 if not (k in changes[1].lower() or
1718 1725 k in changes[4].lower() or
1719 1726 k in " ".join(changes[3][:20]).lower()):
1720 1727 miss = 1
1721 1728 break
1722 1729 if miss:
1723 1730 continue
1724 1731
1725 1732 copies = []
1726 1733 if opts.get('copies') and rev:
1727 1734 mf = get(rev)[0]
1728 1735 for fn in get(rev)[3]:
1729 1736 rename = getrenamed(fn, rev, mf)
1730 1737 if rename:
1731 1738 copies.append((fn, rename[0]))
1732 1739 displayer.show(rev, changenode, copies=copies)
1733 1740 elif st == 'iter':
1734 1741 if count == limit: break
1735 1742 if displayer.flush(rev):
1736 1743 count += 1
1737 1744
1738 1745 def manifest(ui, repo, rev=None):
1739 1746 """output the current or given revision of the project manifest
1740 1747
1741 1748 Print a list of version controlled files for the given revision.
1742 1749 If no revision is given, the parent of the working directory is used,
1743 1750 or tip if no revision is checked out.
1744 1751
1745 1752 The manifest is the list of files being version controlled. If no revision
1746 1753 is given then the first parent of the working directory is used.
1747 1754
1748 1755 With -v flag, print file permissions. With --debug flag, print
1749 1756 file revision hashes.
1750 1757 """
1751 1758
1752 1759 m = repo.changectx(rev).manifest()
1753 1760 files = m.keys()
1754 1761 files.sort()
1755 1762
1756 1763 for f in files:
1757 1764 if ui.debugflag:
1758 1765 ui.write("%40s " % hex(m[f]))
1759 1766 if ui.verbose:
1760 1767 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1761 1768 ui.write("%s\n" % f)
1762 1769
1763 1770 def merge(ui, repo, node=None, force=None):
1764 """Merge working directory with another revision
1771 """merge working directory with another revision
1765 1772
1766 1773 Merge the contents of the current working directory and the
1767 1774 requested revision. Files that changed between either parent are
1768 1775 marked as changed for the next commit and a commit must be
1769 1776 performed before any further updates are allowed.
1770 1777
1771 1778 If no revision is specified, the working directory's parent is a
1772 1779 head revision, and the repository contains exactly one other head,
1773 1780 the other head is merged with by default. Otherwise, an explicit
1774 1781 revision to merge with must be provided.
1775 1782 """
1776 1783
1777 1784 if not node:
1778 1785 heads = repo.heads()
1779 1786 if len(heads) > 2:
1780 1787 raise util.Abort(_('repo has %d heads - '
1781 1788 'please merge with an explicit rev') %
1782 1789 len(heads))
1783 1790 if len(heads) == 1:
1784 1791 raise util.Abort(_('there is nothing to merge - '
1785 1792 'use "hg update" instead'))
1786 1793 parent = repo.dirstate.parents()[0]
1787 1794 if parent not in heads:
1788 1795 raise util.Abort(_('working dir not at a head rev - '
1789 1796 'use "hg update" or merge with an explicit rev'))
1790 1797 node = parent == heads[0] and heads[-1] or heads[0]
1791 1798 return hg.merge(repo, node, force=force)
1792 1799
1793 1800 def outgoing(ui, repo, dest=None, **opts):
1794 1801 """show changesets not found in destination
1795 1802
1796 1803 Show changesets not found in the specified destination repository or
1797 1804 the default push location. These are the changesets that would be pushed
1798 1805 if a push was requested.
1799 1806
1800 1807 See pull for valid destination format details.
1801 1808 """
1802 1809 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1803 1810 setremoteconfig(ui, opts)
1804 1811 revs = None
1805 1812 if opts['rev']:
1806 1813 revs = [repo.lookup(rev) for rev in opts['rev']]
1807 1814
1808 1815 other = hg.repository(ui, dest)
1809 1816 o = repo.findoutgoing(other, force=opts['force'])
1810 1817 if not o:
1811 1818 ui.status(_("no changes found\n"))
1812 1819 return 1
1813 1820 o = repo.changelog.nodesbetween(o, revs)[0]
1814 1821 if opts['newest_first']:
1815 1822 o.reverse()
1816 1823 displayer = cmdutil.show_changeset(ui, repo, opts)
1817 1824 for n in o:
1818 1825 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1819 1826 if opts['no_merges'] and len(parents) == 2:
1820 1827 continue
1821 1828 displayer.show(changenode=n)
1822 1829
1823 1830 def parents(ui, repo, file_=None, **opts):
1824 1831 """show the parents of the working dir or revision
1825 1832
1826 1833 Print the working directory's parent revisions.
1827 1834 """
1828 1835 rev = opts.get('rev')
1829 1836 if rev:
1830 1837 if file_:
1831 1838 ctx = repo.filectx(file_, changeid=rev)
1832 1839 else:
1833 1840 ctx = repo.changectx(rev)
1834 1841 p = [cp.node() for cp in ctx.parents()]
1835 1842 else:
1836 1843 p = repo.dirstate.parents()
1837 1844
1838 1845 displayer = cmdutil.show_changeset(ui, repo, opts)
1839 1846 for n in p:
1840 1847 if n != nullid:
1841 1848 displayer.show(changenode=n)
1842 1849
1843 1850 def paths(ui, repo, search=None):
1844 1851 """show definition of symbolic path names
1845 1852
1846 1853 Show definition of symbolic path name NAME. If no name is given, show
1847 1854 definition of available names.
1848 1855
1849 1856 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1850 1857 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1851 1858 """
1852 1859 if search:
1853 1860 for name, path in ui.configitems("paths"):
1854 1861 if name == search:
1855 1862 ui.write("%s\n" % path)
1856 1863 return
1857 1864 ui.warn(_("not found!\n"))
1858 1865 return 1
1859 1866 else:
1860 1867 for name, path in ui.configitems("paths"):
1861 1868 ui.write("%s = %s\n" % (name, path))
1862 1869
1863 1870 def postincoming(ui, repo, modheads, optupdate):
1864 1871 if modheads == 0:
1865 1872 return
1866 1873 if optupdate:
1867 1874 if modheads == 1:
1868 1875 return hg.update(repo, repo.changelog.tip()) # update
1869 1876 else:
1870 1877 ui.status(_("not updating, since new heads added\n"))
1871 1878 if modheads > 1:
1872 1879 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1873 1880 else:
1874 1881 ui.status(_("(run 'hg update' to get a working copy)\n"))
1875 1882
1876 1883 def pull(ui, repo, source="default", **opts):
1877 1884 """pull changes from the specified source
1878 1885
1879 1886 Pull changes from a remote repository to a local one.
1880 1887
1881 1888 This finds all changes from the repository at the specified path
1882 1889 or URL and adds them to the local repository. By default, this
1883 1890 does not update the copy of the project in the working directory.
1884 1891
1885 1892 Valid URLs are of the form:
1886 1893
1887 1894 local/filesystem/path (or file://local/filesystem/path)
1888 1895 http://[user@]host[:port]/[path]
1889 1896 https://[user@]host[:port]/[path]
1890 1897 ssh://[user@]host[:port]/[path]
1891 1898 static-http://host[:port]/[path]
1892 1899
1893 1900 Paths in the local filesystem can either point to Mercurial
1894 1901 repositories or to bundle files (as created by 'hg bundle' or
1895 1902 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1896 1903 allows access to a Mercurial repository where you simply use a web
1897 1904 server to publish the .hg directory as static content.
1898 1905
1899 1906 Some notes about using SSH with Mercurial:
1900 1907 - SSH requires an accessible shell account on the destination machine
1901 1908 and a copy of hg in the remote path or specified with as remotecmd.
1902 1909 - path is relative to the remote user's home directory by default.
1903 1910 Use an extra slash at the start of a path to specify an absolute path:
1904 1911 ssh://example.com//tmp/repository
1905 1912 - Mercurial doesn't use its own compression via SSH; the right thing
1906 1913 to do is to configure it in your ~/.ssh/config, e.g.:
1907 1914 Host *.mylocalnetwork.example.com
1908 1915 Compression no
1909 1916 Host *
1910 1917 Compression yes
1911 1918 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1912 1919 with the --ssh command line option.
1913 1920 """
1914 1921 source = ui.expandpath(source)
1915 1922 setremoteconfig(ui, opts)
1916 1923
1917 1924 other = hg.repository(ui, source)
1918 1925 ui.status(_('pulling from %s\n') % (source))
1919 1926 revs = None
1920 1927 if opts['rev']:
1921 1928 if 'lookup' in other.capabilities:
1922 1929 revs = [other.lookup(rev) for rev in opts['rev']]
1923 1930 else:
1924 1931 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1925 1932 raise util.Abort(error)
1926 1933 modheads = repo.pull(other, heads=revs, force=opts['force'])
1927 1934 return postincoming(ui, repo, modheads, opts['update'])
1928 1935
1929 1936 def push(ui, repo, dest=None, **opts):
1930 1937 """push changes to the specified destination
1931 1938
1932 1939 Push changes from the local repository to the given destination.
1933 1940
1934 1941 This is the symmetrical operation for pull. It helps to move
1935 1942 changes from the current repository to a different one. If the
1936 1943 destination is local this is identical to a pull in that directory
1937 1944 from the current one.
1938 1945
1939 1946 By default, push will refuse to run if it detects the result would
1940 1947 increase the number of remote heads. This generally indicates the
1941 1948 the client has forgotten to sync and merge before pushing.
1942 1949
1943 1950 Valid URLs are of the form:
1944 1951
1945 1952 local/filesystem/path (or file://local/filesystem/path)
1946 1953 ssh://[user@]host[:port]/[path]
1947 1954 http://[user@]host[:port]/[path]
1948 1955 https://[user@]host[:port]/[path]
1949 1956
1950 1957 Look at the help text for the pull command for important details
1951 1958 about ssh:// URLs.
1952 1959
1953 1960 Pushing to http:// and https:// URLs is only possible, if this
1954 1961 feature is explicitly enabled on the remote Mercurial server.
1955 1962 """
1956 1963 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1957 1964 setremoteconfig(ui, opts)
1958 1965
1959 1966 other = hg.repository(ui, dest)
1960 1967 ui.status('pushing to %s\n' % (dest))
1961 1968 revs = None
1962 1969 if opts['rev']:
1963 1970 revs = [repo.lookup(rev) for rev in opts['rev']]
1964 1971 r = repo.push(other, opts['force'], revs=revs)
1965 1972 return r == 0
1966 1973
1967 1974 def rawcommit(ui, repo, *pats, **opts):
1968 1975 """raw commit interface (DEPRECATED)
1969 1976
1970 1977 (DEPRECATED)
1971 1978 Lowlevel commit, for use in helper scripts.
1972 1979
1973 1980 This command is not intended to be used by normal users, as it is
1974 1981 primarily useful for importing from other SCMs.
1975 1982
1976 1983 This command is now deprecated and will be removed in a future
1977 1984 release, please use debugsetparents and commit instead.
1978 1985 """
1979 1986
1980 1987 ui.warn(_("(the rawcommit command is deprecated)\n"))
1981 1988
1982 1989 message = logmessage(opts)
1983 1990
1984 1991 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
1985 1992 if opts['files']:
1986 1993 files += open(opts['files']).read().splitlines()
1987 1994
1988 1995 parents = [repo.lookup(p) for p in opts['parent']]
1989 1996
1990 1997 try:
1991 1998 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
1992 1999 except ValueError, inst:
1993 2000 raise util.Abort(str(inst))
1994 2001
1995 2002 def recover(ui, repo):
1996 2003 """roll back an interrupted transaction
1997 2004
1998 2005 Recover from an interrupted commit or pull.
1999 2006
2000 2007 This command tries to fix the repository status after an interrupted
2001 2008 operation. It should only be necessary when Mercurial suggests it.
2002 2009 """
2003 2010 if repo.recover():
2004 2011 return hg.verify(repo)
2005 2012 return 1
2006 2013
2007 2014 def remove(ui, repo, *pats, **opts):
2008 2015 """remove the specified files on the next commit
2009 2016
2010 2017 Schedule the indicated files for removal from the repository.
2011 2018
2012 2019 This only removes files from the current branch, not from the
2013 2020 entire project history. If the files still exist in the working
2014 2021 directory, they will be deleted from it. If invoked with --after,
2015 2022 files that have been manually deleted are marked as removed.
2016 2023
2017 2024 This command schedules the files to be removed at the next commit.
2018 2025 To undo a remove before that, see hg revert.
2019 2026
2020 2027 Modified files and added files are not removed by default. To
2021 2028 remove them, use the -f/--force option.
2022 2029 """
2023 2030 names = []
2024 2031 if not opts['after'] and not pats:
2025 2032 raise util.Abort(_('no files specified'))
2026 2033 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2027 2034 exact = dict.fromkeys(files)
2028 2035 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2029 2036 modified, added, removed, deleted, unknown = mardu
2030 2037 remove, forget = [], []
2031 2038 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2032 2039 reason = None
2033 2040 if abs not in deleted and opts['after']:
2034 2041 reason = _('is still present')
2035 2042 elif abs in modified and not opts['force']:
2036 2043 reason = _('is modified (use -f to force removal)')
2037 2044 elif abs in added:
2038 2045 if opts['force']:
2039 2046 forget.append(abs)
2040 2047 continue
2041 2048 reason = _('has been marked for add (use -f to force removal)')
2042 2049 elif abs in unknown:
2043 2050 reason = _('is not managed')
2044 2051 elif abs in removed:
2045 2052 continue
2046 2053 if reason:
2047 2054 if exact:
2048 2055 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2049 2056 else:
2050 2057 if ui.verbose or not exact:
2051 2058 ui.status(_('removing %s\n') % rel)
2052 2059 remove.append(abs)
2053 2060 repo.forget(forget)
2054 2061 repo.remove(remove, unlink=not opts['after'])
2055 2062
2056 2063 def rename(ui, repo, *pats, **opts):
2057 2064 """rename files; equivalent of copy + remove
2058 2065
2059 2066 Mark dest as copies of sources; mark sources for deletion. If
2060 2067 dest is a directory, copies are put in that directory. If dest is
2061 2068 a file, there can only be one source.
2062 2069
2063 2070 By default, this command copies the contents of files as they
2064 2071 stand in the working directory. If invoked with --after, the
2065 2072 operation is recorded, but no copying is performed.
2066 2073
2067 2074 This command takes effect in the next commit. To undo a rename
2068 2075 before that, see hg revert.
2069 2076 """
2070 2077 wlock = repo.wlock(0)
2071 2078 errs, copied = docopy(ui, repo, pats, opts, wlock)
2072 2079 names = []
2073 2080 for abs, rel, exact in copied:
2074 2081 if ui.verbose or not exact:
2075 2082 ui.status(_('removing %s\n') % rel)
2076 2083 names.append(abs)
2077 2084 if not opts.get('dry_run'):
2078 2085 repo.remove(names, True, wlock)
2079 2086 return errs
2080 2087
2081 2088 def revert(ui, repo, *pats, **opts):
2082 2089 """revert files or dirs to their states as of some revision
2083 2090
2084 2091 With no revision specified, revert the named files or directories
2085 2092 to the contents they had in the parent of the working directory.
2086 2093 This restores the contents of the affected files to an unmodified
2087 2094 state and unschedules adds, removes, copies, and renames. If the
2088 2095 working directory has two parents, you must explicitly specify the
2089 2096 revision to revert to.
2090 2097
2091 2098 Modified files are saved with a .orig suffix before reverting.
2092 2099 To disable these backups, use --no-backup.
2093 2100
2094 2101 Using the -r option, revert the given files or directories to their
2095 2102 contents as of a specific revision. This can be helpful to "roll
2096 2103 back" some or all of a change that should not have been committed.
2097 2104
2098 2105 Revert modifies the working directory. It does not commit any
2099 2106 changes, or change the parent of the working directory. If you
2100 2107 revert to a revision other than the parent of the working
2101 2108 directory, the reverted files will thus appear modified
2102 2109 afterwards.
2103 2110
2104 2111 If a file has been deleted, it is recreated. If the executable
2105 2112 mode of a file was changed, it is reset.
2106 2113
2107 2114 If names are given, all files matching the names are reverted.
2108 2115
2109 2116 If no arguments are given, no files are reverted.
2110 2117 """
2111 2118
2112 2119 if opts["date"]:
2113 2120 if opts["rev"]:
2114 2121 raise util.Abort(_("you can't specify a revision and a date"))
2115 2122 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2116 2123
2117 2124 if not pats and not opts['all']:
2118 2125 raise util.Abort(_('no files or directories specified; '
2119 2126 'use --all to revert the whole repo'))
2120 2127
2121 2128 parent, p2 = repo.dirstate.parents()
2122 2129 if not opts['rev'] and p2 != nullid:
2123 2130 raise util.Abort(_('uncommitted merge - please provide a '
2124 2131 'specific revision'))
2125 2132 ctx = repo.changectx(opts['rev'])
2126 2133 node = ctx.node()
2127 2134 mf = ctx.manifest()
2128 2135 if node == parent:
2129 2136 pmf = mf
2130 2137 else:
2131 2138 pmf = None
2132 2139
2133 2140 wlock = repo.wlock()
2134 2141
2135 2142 # need all matching names in dirstate and manifest of target rev,
2136 2143 # so have to walk both. do not print errors if files exist in one
2137 2144 # but not other.
2138 2145
2139 2146 names = {}
2140 2147 target_only = {}
2141 2148
2142 2149 # walk dirstate.
2143 2150
2144 2151 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2145 2152 badmatch=mf.has_key):
2146 2153 names[abs] = (rel, exact)
2147 2154 if src == 'b':
2148 2155 target_only[abs] = True
2149 2156
2150 2157 # walk target manifest.
2151 2158
2152 2159 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2153 2160 badmatch=names.has_key):
2154 2161 if abs in names: continue
2155 2162 names[abs] = (rel, exact)
2156 2163 target_only[abs] = True
2157 2164
2158 2165 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2159 2166 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2160 2167
2161 2168 revert = ([], _('reverting %s\n'))
2162 2169 add = ([], _('adding %s\n'))
2163 2170 remove = ([], _('removing %s\n'))
2164 2171 forget = ([], _('forgetting %s\n'))
2165 2172 undelete = ([], _('undeleting %s\n'))
2166 2173 update = {}
2167 2174
2168 2175 disptable = (
2169 2176 # dispatch table:
2170 2177 # file state
2171 2178 # action if in target manifest
2172 2179 # action if not in target manifest
2173 2180 # make backup if in target manifest
2174 2181 # make backup if not in target manifest
2175 2182 (modified, revert, remove, True, True),
2176 2183 (added, revert, forget, True, False),
2177 2184 (removed, undelete, None, False, False),
2178 2185 (deleted, revert, remove, False, False),
2179 2186 (unknown, add, None, True, False),
2180 2187 (target_only, add, None, False, False),
2181 2188 )
2182 2189
2183 2190 entries = names.items()
2184 2191 entries.sort()
2185 2192
2186 2193 for abs, (rel, exact) in entries:
2187 2194 mfentry = mf.get(abs)
2188 2195 def handle(xlist, dobackup):
2189 2196 xlist[0].append(abs)
2190 2197 update[abs] = 1
2191 2198 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2192 2199 bakname = "%s.orig" % rel
2193 2200 ui.note(_('saving current version of %s as %s\n') %
2194 2201 (rel, bakname))
2195 2202 if not opts.get('dry_run'):
2196 2203 util.copyfile(rel, bakname)
2197 2204 if ui.verbose or not exact:
2198 2205 ui.status(xlist[1] % rel)
2199 2206 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2200 2207 if abs not in table: continue
2201 2208 # file has changed in dirstate
2202 2209 if mfentry:
2203 2210 handle(hitlist, backuphit)
2204 2211 elif misslist is not None:
2205 2212 handle(misslist, backupmiss)
2206 2213 else:
2207 2214 if exact: ui.warn(_('file not managed: %s\n') % rel)
2208 2215 break
2209 2216 else:
2210 2217 # file has not changed in dirstate
2211 2218 if node == parent:
2212 2219 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2213 2220 continue
2214 2221 if pmf is None:
2215 2222 # only need parent manifest in this unlikely case,
2216 2223 # so do not read by default
2217 2224 pmf = repo.changectx(parent).manifest()
2218 2225 if abs in pmf:
2219 2226 if mfentry:
2220 2227 # if version of file is same in parent and target
2221 2228 # manifests, do nothing
2222 2229 if pmf[abs] != mfentry:
2223 2230 handle(revert, False)
2224 2231 else:
2225 2232 handle(remove, False)
2226 2233
2227 2234 if not opts.get('dry_run'):
2228 2235 repo.dirstate.forget(forget[0])
2229 2236 r = hg.revert(repo, node, update.has_key, wlock)
2230 2237 repo.dirstate.update(add[0], 'a')
2231 2238 repo.dirstate.update(undelete[0], 'n')
2232 2239 repo.dirstate.update(remove[0], 'r')
2233 2240 return r
2234 2241
2235 2242 def rollback(ui, repo):
2236 2243 """roll back the last transaction in this repository
2237 2244
2238 2245 Roll back the last transaction in this repository, restoring the
2239 2246 project to its state prior to the transaction.
2240 2247
2241 2248 Transactions are used to encapsulate the effects of all commands
2242 2249 that create new changesets or propagate existing changesets into a
2243 2250 repository. For example, the following commands are transactional,
2244 2251 and their effects can be rolled back:
2245 2252
2246 2253 commit
2247 2254 import
2248 2255 pull
2249 2256 push (with this repository as destination)
2250 2257 unbundle
2251 2258
2252 2259 This command should be used with care. There is only one level of
2253 2260 rollback, and there is no way to undo a rollback.
2254 2261
2255 2262 This command is not intended for use on public repositories. Once
2256 2263 changes are visible for pull by other users, rolling a transaction
2257 2264 back locally is ineffective (someone else may already have pulled
2258 2265 the changes). Furthermore, a race is possible with readers of the
2259 2266 repository; for example an in-progress pull from the repository
2260 2267 may fail if a rollback is performed.
2261 2268 """
2262 2269 repo.rollback()
2263 2270
2264 2271 def root(ui, repo):
2265 2272 """print the root (top) of the current working dir
2266 2273
2267 2274 Print the root directory of the current repository.
2268 2275 """
2269 2276 ui.write(repo.root + "\n")
2270 2277
2271 2278 def serve(ui, repo, **opts):
2272 2279 """export the repository via HTTP
2273 2280
2274 2281 Start a local HTTP repository browser and pull server.
2275 2282
2276 2283 By default, the server logs accesses to stdout and errors to
2277 2284 stderr. Use the "-A" and "-E" options to log to files.
2278 2285 """
2279 2286
2280 2287 if opts["stdio"]:
2281 2288 if repo is None:
2282 2289 raise hg.RepoError(_("There is no Mercurial repository here"
2283 2290 " (.hg not found)"))
2284 2291 s = sshserver.sshserver(ui, repo)
2285 2292 s.serve_forever()
2286 2293
2287 2294 optlist = ("name templates style address port ipv6"
2288 2295 " accesslog errorlog webdir_conf")
2289 2296 for o in optlist.split():
2290 2297 if opts[o]:
2291 2298 ui.setconfig("web", o, str(opts[o]))
2292 2299
2293 2300 if repo is None and not ui.config("web", "webdir_conf"):
2294 2301 raise hg.RepoError(_("There is no Mercurial repository here"
2295 2302 " (.hg not found)"))
2296 2303
2297 2304 if opts['daemon'] and not opts['daemon_pipefds']:
2298 2305 rfd, wfd = os.pipe()
2299 2306 args = sys.argv[:]
2300 2307 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2301 2308 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2302 2309 args[0], args)
2303 2310 os.close(wfd)
2304 2311 os.read(rfd, 1)
2305 2312 os._exit(0)
2306 2313
2307 2314 httpd = hgweb.server.create_server(ui, repo)
2308 2315
2309 2316 if ui.verbose:
2310 2317 if httpd.port != 80:
2311 2318 ui.status(_('listening at http://%s:%d/\n') %
2312 2319 (httpd.addr, httpd.port))
2313 2320 else:
2314 2321 ui.status(_('listening at http://%s/\n') % httpd.addr)
2315 2322
2316 2323 if opts['pid_file']:
2317 2324 fp = open(opts['pid_file'], 'w')
2318 2325 fp.write(str(os.getpid()) + '\n')
2319 2326 fp.close()
2320 2327
2321 2328 if opts['daemon_pipefds']:
2322 2329 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2323 2330 os.close(rfd)
2324 2331 os.write(wfd, 'y')
2325 2332 os.close(wfd)
2326 2333 sys.stdout.flush()
2327 2334 sys.stderr.flush()
2328 2335 fd = os.open(util.nulldev, os.O_RDWR)
2329 2336 if fd != 0: os.dup2(fd, 0)
2330 2337 if fd != 1: os.dup2(fd, 1)
2331 2338 if fd != 2: os.dup2(fd, 2)
2332 2339 if fd not in (0, 1, 2): os.close(fd)
2333 2340
2334 2341 httpd.serve_forever()
2335 2342
2336 2343 def status(ui, repo, *pats, **opts):
2337 2344 """show changed files in the working directory
2338 2345
2339 2346 Show status of files in the repository. If names are given, only
2340 2347 files that match are shown. Files that are clean or ignored, are
2341 2348 not listed unless -c (clean), -i (ignored) or -A is given.
2342 2349
2343 2350 NOTE: status may appear to disagree with diff if permissions have
2344 2351 changed or a merge has occurred. The standard diff format does not
2345 2352 report permission changes and diff only reports changes relative
2346 2353 to one merge parent.
2347 2354
2348 2355 If one revision is given, it is used as the base revision.
2349 2356 If two revisions are given, the difference between them is shown.
2350 2357
2351 2358 The codes used to show the status of files are:
2352 2359 M = modified
2353 2360 A = added
2354 2361 R = removed
2355 2362 C = clean
2356 2363 ! = deleted, but still tracked
2357 2364 ? = not tracked
2358 2365 I = ignored (not shown by default)
2359 2366 = the previous added file was copied from here
2360 2367 """
2361 2368
2362 2369 all = opts['all']
2363 2370 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2364 2371
2365 2372 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2366 2373 cwd = (pats and repo.getcwd()) or ''
2367 2374 modified, added, removed, deleted, unknown, ignored, clean = [
2368 2375 [util.pathto(cwd, x) for x in n]
2369 2376 for n in repo.status(node1=node1, node2=node2, files=files,
2370 2377 match=matchfn,
2371 2378 list_ignored=all or opts['ignored'],
2372 2379 list_clean=all or opts['clean'])]
2373 2380
2374 2381 changetypes = (('modified', 'M', modified),
2375 2382 ('added', 'A', added),
2376 2383 ('removed', 'R', removed),
2377 2384 ('deleted', '!', deleted),
2378 2385 ('unknown', '?', unknown),
2379 2386 ('ignored', 'I', ignored))
2380 2387
2381 2388 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2382 2389
2383 2390 end = opts['print0'] and '\0' or '\n'
2384 2391
2385 2392 for opt, char, changes in ([ct for ct in explicit_changetypes
2386 2393 if all or opts[ct[0]]]
2387 2394 or changetypes):
2388 2395 if opts['no_status']:
2389 2396 format = "%%s%s" % end
2390 2397 else:
2391 2398 format = "%s %%s%s" % (char, end)
2392 2399
2393 2400 for f in changes:
2394 2401 ui.write(format % f)
2395 2402 if ((all or opts.get('copies')) and not opts.get('no_status')):
2396 2403 copied = repo.dirstate.copied(f)
2397 2404 if copied:
2398 2405 ui.write(' %s%s' % (copied, end))
2399 2406
2400 2407 def tag(ui, repo, name, rev_=None, **opts):
2401 2408 """add a tag for the current or given revision
2402 2409
2403 2410 Name a particular revision using <name>.
2404 2411
2405 2412 Tags are used to name particular revisions of the repository and are
2406 2413 very useful to compare different revision, to go back to significant
2407 2414 earlier versions or to mark branch points as releases, etc.
2408 2415
2409 2416 If no revision is given, the parent of the working directory is used,
2410 2417 or tip if no revision is checked out.
2411 2418
2412 2419 To facilitate version control, distribution, and merging of tags,
2413 2420 they are stored as a file named ".hgtags" which is managed
2414 2421 similarly to other project files and can be hand-edited if
2415 2422 necessary. The file '.hg/localtags' is used for local tags (not
2416 2423 shared among repositories).
2417 2424 """
2418 2425 if name in ['tip', '.', 'null']:
2419 2426 raise util.Abort(_("the name '%s' is reserved") % name)
2420 2427 if rev_ is not None:
2421 2428 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2422 2429 "please use 'hg tag [-r REV] NAME' instead\n"))
2423 2430 if opts['rev']:
2424 2431 raise util.Abort(_("use only one form to specify the revision"))
2425 2432 if opts['rev']:
2426 2433 rev_ = opts['rev']
2427 2434 if not rev_ and repo.dirstate.parents()[1] != nullid:
2428 2435 raise util.Abort(_('uncommitted merge - please provide a '
2429 2436 'specific revision'))
2430 2437 r = repo.changectx(rev_).node()
2431 2438
2432 2439 message = opts['message']
2433 2440 if not message:
2434 2441 message = _('Added tag %s for changeset %s') % (name, short(r))
2435 2442
2436 2443 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2437 2444
2438 2445 def tags(ui, repo):
2439 2446 """list repository tags
2440 2447
2441 2448 List the repository tags.
2442 2449
2443 2450 This lists both regular and local tags.
2444 2451 """
2445 2452
2446 2453 l = repo.tagslist()
2447 2454 l.reverse()
2448 2455 hexfunc = ui.debugflag and hex or short
2449 2456 for t, n in l:
2450 2457 try:
2451 2458 hn = hexfunc(n)
2452 2459 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2453 2460 except revlog.LookupError:
2454 2461 r = " ?:%s" % hn
2455 2462 if ui.quiet:
2456 2463 ui.write("%s\n" % t)
2457 2464 else:
2458 2465 t = util.localsub(t, 30)
2459 2466 t += " " * (30 - util.locallen(t))
2460 2467 ui.write("%s %s\n" % (t, r))
2461 2468
2462 2469 def tip(ui, repo, **opts):
2463 2470 """show the tip revision
2464 2471
2465 2472 Show the tip revision.
2466 2473 """
2467 2474 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2468 2475
2469 2476 def unbundle(ui, repo, fname, **opts):
2470 2477 """apply a changegroup file
2471 2478
2472 2479 Apply a compressed changegroup file generated by the bundle
2473 2480 command.
2474 2481 """
2475 2482 gen = changegroup.readbundle(urllib.urlopen(fname), fname)
2476 2483 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2477 2484 return postincoming(ui, repo, modheads, opts['update'])
2478 2485
2479 2486 def update(ui, repo, node=None, clean=False, date=None):
2480 """update or merge working directory
2487 """update working directory
2481 2488
2482 2489 Update the working directory to the specified revision.
2483 2490
2484 2491 If there are no outstanding changes in the working directory and
2485 2492 there is a linear relationship between the current version and the
2486 2493 requested version, the result is the requested version.
2487 2494
2488 2495 To merge the working directory with another revision, use the
2489 2496 merge command.
2490 2497
2491 2498 By default, update will refuse to run if doing so would require
2492 merging or discarding local changes.
2499 discarding local changes.
2493 2500 """
2494 2501 if date:
2495 2502 if node:
2496 2503 raise util.Abort(_("you can't specify a revision and a date"))
2497 2504 node = cmdutil.finddate(ui, repo, date)
2498 2505
2499 2506 if clean:
2500 2507 return hg.clean(repo, node)
2501 2508 else:
2502 2509 return hg.update(repo, node)
2503 2510
2504 2511 def verify(ui, repo):
2505 2512 """verify the integrity of the repository
2506 2513
2507 2514 Verify the integrity of the current repository.
2508 2515
2509 2516 This will perform an extensive check of the repository's
2510 2517 integrity, validating the hashes and checksums of each entry in
2511 2518 the changelog, manifest, and tracked files, as well as the
2512 2519 integrity of their crosslinks and indices.
2513 2520 """
2514 2521 return hg.verify(repo)
2515 2522
2516 2523 def version_(ui):
2517 2524 """output version and copyright information"""
2518 2525 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2519 2526 % version.get_version())
2520 2527 ui.status(_(
2521 2528 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2522 2529 "This is free software; see the source for copying conditions. "
2523 2530 "There is NO\nwarranty; "
2524 2531 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2525 2532 ))
2526 2533
2527 2534 # Command options and aliases are listed here, alphabetically
2528 2535
2529 2536 globalopts = [
2530 2537 ('R', 'repository', '',
2531 2538 _('repository root directory or symbolic path name')),
2532 2539 ('', 'cwd', '', _('change working directory')),
2533 2540 ('y', 'noninteractive', None,
2534 2541 _('do not prompt, assume \'yes\' for any required answers')),
2535 2542 ('q', 'quiet', None, _('suppress output')),
2536 2543 ('v', 'verbose', None, _('enable additional output')),
2537 2544 ('', 'config', [], _('set/override config option')),
2538 2545 ('', 'debug', None, _('enable debugging output')),
2539 2546 ('', 'debugger', None, _('start debugger')),
2540 2547 ('', 'encoding', util._encoding, _('set the charset encoding')),
2541 2548 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2542 2549 ('', 'lsprof', None, _('print improved command execution profile')),
2543 2550 ('', 'traceback', None, _('print traceback on exception')),
2544 2551 ('', 'time', None, _('time how long the command takes')),
2545 2552 ('', 'profile', None, _('print command execution profile')),
2546 2553 ('', 'version', None, _('output version information and exit')),
2547 2554 ('h', 'help', None, _('display help and exit')),
2548 2555 ]
2549 2556
2550 2557 dryrunopts = [('n', 'dry-run', None,
2551 2558 _('do not perform actions, just print output'))]
2552 2559
2553 2560 remoteopts = [
2554 2561 ('e', 'ssh', '', _('specify ssh command to use')),
2555 2562 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2556 2563 ]
2557 2564
2558 2565 walkopts = [
2559 2566 ('I', 'include', [], _('include names matching the given patterns')),
2560 2567 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2561 2568 ]
2562 2569
2563 2570 commitopts = [
2564 2571 ('m', 'message', '', _('use <text> as commit message')),
2565 2572 ('l', 'logfile', '', _('read commit message from <file>')),
2566 2573 ]
2567 2574
2568 2575 table = {
2569 2576 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2570 2577 "addremove":
2571 2578 (addremove,
2572 2579 [('s', 'similarity', '',
2573 2580 _('guess renamed files by similarity (0<=s<=100)')),
2574 2581 ] + walkopts + dryrunopts,
2575 2582 _('hg addremove [OPTION]... [FILE]...')),
2576 2583 "^annotate":
2577 2584 (annotate,
2578 2585 [('r', 'rev', '', _('annotate the specified revision')),
2579 2586 ('f', 'follow', None, _('follow file copies and renames')),
2580 2587 ('a', 'text', None, _('treat all files as text')),
2581 2588 ('u', 'user', None, _('list the author')),
2582 2589 ('d', 'date', None, _('list the date')),
2583 2590 ('n', 'number', None, _('list the revision number (default)')),
2584 2591 ('c', 'changeset', None, _('list the changeset')),
2585 2592 ] + walkopts,
2586 2593 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2587 2594 "archive":
2588 2595 (archive,
2589 2596 [('', 'no-decode', None, _('do not pass files through decoders')),
2590 2597 ('p', 'prefix', '', _('directory prefix for files in archive')),
2591 2598 ('r', 'rev', '', _('revision to distribute')),
2592 2599 ('t', 'type', '', _('type of distribution to create')),
2593 2600 ] + walkopts,
2594 2601 _('hg archive [OPTION]... DEST')),
2595 2602 "backout":
2596 2603 (backout,
2597 2604 [('', 'merge', None,
2598 2605 _('merge with old dirstate parent after backout')),
2599 2606 ('d', 'date', '', _('record datecode as commit date')),
2600 2607 ('', 'parent', '', _('parent to choose when backing out merge')),
2601 2608 ('u', 'user', '', _('record user as committer')),
2602 2609 ] + walkopts + commitopts,
2603 2610 _('hg backout [OPTION]... REV')),
2604 2611 "branch": (branch, [], _('hg branch [NAME]')),
2605 2612 "branches": (branches, [], _('hg branches')),
2606 2613 "bundle":
2607 2614 (bundle,
2608 2615 [('f', 'force', None,
2609 2616 _('run even when remote repository is unrelated')),
2610 2617 ('r', 'rev', [],
2611 2618 _('a changeset you would like to bundle')),
2612 2619 ('', 'base', [],
2613 2620 _('a base changeset to specify instead of a destination')),
2614 2621 ] + remoteopts,
2615 2622 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2616 2623 "cat":
2617 2624 (cat,
2618 2625 [('o', 'output', '', _('print output to file with formatted name')),
2619 2626 ('r', 'rev', '', _('print the given revision')),
2620 2627 ] + walkopts,
2621 2628 _('hg cat [OPTION]... FILE...')),
2622 2629 "^clone":
2623 2630 (clone,
2624 2631 [('U', 'noupdate', None, _('do not update the new working directory')),
2625 2632 ('r', 'rev', [],
2626 2633 _('a changeset you would like to have after cloning')),
2627 2634 ('', 'pull', None, _('use pull protocol to copy metadata')),
2628 2635 ('', 'uncompressed', None,
2629 2636 _('use uncompressed transfer (fast over LAN)')),
2630 2637 ] + remoteopts,
2631 2638 _('hg clone [OPTION]... SOURCE [DEST]')),
2632 2639 "^commit|ci":
2633 2640 (commit,
2634 2641 [('A', 'addremove', None,
2635 2642 _('mark new/missing files as added/removed before committing')),
2636 2643 ('d', 'date', '', _('record datecode as commit date')),
2637 2644 ('u', 'user', '', _('record user as commiter')),
2638 2645 ] + walkopts + commitopts,
2639 2646 _('hg commit [OPTION]... [FILE]...')),
2640 2647 "copy|cp":
2641 2648 (copy,
2642 2649 [('A', 'after', None, _('record a copy that has already occurred')),
2643 2650 ('f', 'force', None,
2644 2651 _('forcibly copy over an existing managed file')),
2645 2652 ] + walkopts + dryrunopts,
2646 2653 _('hg copy [OPTION]... [SOURCE]... DEST')),
2647 2654 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2648 2655 "debugcomplete":
2649 2656 (debugcomplete,
2650 2657 [('o', 'options', None, _('show the command options'))],
2651 2658 _('debugcomplete [-o] CMD')),
2652 2659 "debuginstall": (debuginstall, [], _('debuginstall')),
2653 2660 "debugrebuildstate":
2654 2661 (debugrebuildstate,
2655 2662 [('r', 'rev', '', _('revision to rebuild to'))],
2656 2663 _('debugrebuildstate [-r REV] [REV]')),
2657 2664 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2658 2665 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2659 2666 "debugstate": (debugstate, [], _('debugstate')),
2660 2667 "debugdate":
2661 2668 (debugdate,
2662 2669 [('e', 'extended', None, _('try extended date formats'))],
2663 2670 _('debugdate [-e] DATE [RANGE]')),
2664 2671 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2665 2672 "debugindex": (debugindex, [], _('debugindex FILE')),
2666 2673 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2667 2674 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2668 2675 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2669 2676 "^diff":
2670 2677 (diff,
2671 2678 [('r', 'rev', [], _('revision')),
2672 2679 ('a', 'text', None, _('treat all files as text')),
2673 2680 ('p', 'show-function', None,
2674 2681 _('show which function each change is in')),
2675 2682 ('g', 'git', None, _('use git extended diff format')),
2676 2683 ('', 'nodates', None, _("don't include dates in diff headers")),
2677 2684 ('w', 'ignore-all-space', None,
2678 2685 _('ignore white space when comparing lines')),
2679 2686 ('b', 'ignore-space-change', None,
2680 2687 _('ignore changes in the amount of white space')),
2681 2688 ('B', 'ignore-blank-lines', None,
2682 2689 _('ignore changes whose lines are all blank')),
2683 2690 ] + walkopts,
2684 2691 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2685 2692 "^export":
2686 2693 (export,
2687 2694 [('o', 'output', '', _('print output to file with formatted name')),
2688 2695 ('a', 'text', None, _('treat all files as text')),
2689 2696 ('g', 'git', None, _('use git extended diff format')),
2690 2697 ('', 'nodates', None, _("don't include dates in diff headers")),
2691 2698 ('', 'switch-parent', None, _('diff against the second parent'))],
2692 2699 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2693 2700 "grep":
2694 2701 (grep,
2695 2702 [('0', 'print0', None, _('end fields with NUL')),
2696 2703 ('', 'all', None, _('print all revisions that match')),
2697 2704 ('f', 'follow', None,
2698 2705 _('follow changeset history, or file history across copies and renames')),
2699 2706 ('i', 'ignore-case', None, _('ignore case when matching')),
2700 2707 ('l', 'files-with-matches', None,
2701 2708 _('print only filenames and revs that match')),
2702 2709 ('n', 'line-number', None, _('print matching line numbers')),
2703 2710 ('r', 'rev', [], _('search in given revision range')),
2704 2711 ('u', 'user', None, _('print user who committed change')),
2705 2712 ] + walkopts,
2706 2713 _('hg grep [OPTION]... PATTERN [FILE]...')),
2707 2714 "heads":
2708 2715 (heads,
2709 2716 [('', 'style', '', _('display using template map file')),
2710 2717 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2711 2718 ('', 'template', '', _('display with template'))],
2712 2719 _('hg heads [-r REV]')),
2713 2720 "help": (help_, [], _('hg help [COMMAND]')),
2714 2721 "identify|id": (identify, [], _('hg identify')),
2715 2722 "import|patch":
2716 2723 (import_,
2717 2724 [('p', 'strip', 1,
2718 2725 _('directory strip option for patch. This has the same\n'
2719 2726 'meaning as the corresponding patch option')),
2720 2727 ('b', 'base', '', _('base path')),
2721 2728 ('f', 'force', None,
2722 2729 _('skip check for outstanding uncommitted changes'))] + commitopts,
2723 2730 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2724 2731 "incoming|in": (incoming,
2725 2732 [('M', 'no-merges', None, _('do not show merges')),
2726 2733 ('f', 'force', None,
2727 2734 _('run even when remote repository is unrelated')),
2728 2735 ('', 'style', '', _('display using template map file')),
2729 2736 ('n', 'newest-first', None, _('show newest record first')),
2730 2737 ('', 'bundle', '', _('file to store the bundles into')),
2731 2738 ('p', 'patch', None, _('show patch')),
2732 2739 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2733 2740 ('', 'template', '', _('display with template')),
2734 2741 ] + remoteopts,
2735 2742 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2736 2743 ' [--bundle FILENAME] [SOURCE]')),
2737 2744 "^init":
2738 2745 (init,
2739 2746 remoteopts,
2740 2747 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2741 2748 "locate":
2742 2749 (locate,
2743 2750 [('r', 'rev', '', _('search the repository as it stood at rev')),
2744 2751 ('0', 'print0', None,
2745 2752 _('end filenames with NUL, for use with xargs')),
2746 2753 ('f', 'fullpath', None,
2747 2754 _('print complete paths from the filesystem root')),
2748 2755 ] + walkopts,
2749 2756 _('hg locate [OPTION]... [PATTERN]...')),
2750 2757 "^log|history":
2751 2758 (log,
2752 2759 [('f', 'follow', None,
2753 2760 _('follow changeset history, or file history across copies and renames')),
2754 2761 ('', 'follow-first', None,
2755 2762 _('only follow the first parent of merge changesets')),
2756 2763 ('d', 'date', '', _('show revs matching date spec')),
2757 2764 ('C', 'copies', None, _('show copied files')),
2758 2765 ('k', 'keyword', [], _('search for a keyword')),
2759 2766 ('l', 'limit', '', _('limit number of changes displayed')),
2760 2767 ('r', 'rev', [], _('show the specified revision or range')),
2761 2768 ('', 'removed', None, _('include revs where files were removed')),
2762 2769 ('M', 'no-merges', None, _('do not show merges')),
2763 2770 ('', 'style', '', _('display using template map file')),
2764 2771 ('m', 'only-merges', None, _('show only merges')),
2765 2772 ('p', 'patch', None, _('show patch')),
2766 2773 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2767 2774 ('', 'template', '', _('display with template')),
2768 2775 ] + walkopts,
2769 2776 _('hg log [OPTION]... [FILE]')),
2770 2777 "manifest": (manifest, [], _('hg manifest [REV]')),
2771 "merge":
2778 "^merge":
2772 2779 (merge,
2773 2780 [('f', 'force', None, _('force a merge with outstanding changes'))],
2774 2781 _('hg merge [-f] [REV]')),
2775 2782 "outgoing|out": (outgoing,
2776 2783 [('M', 'no-merges', None, _('do not show merges')),
2777 2784 ('f', 'force', None,
2778 2785 _('run even when remote repository is unrelated')),
2779 2786 ('p', 'patch', None, _('show patch')),
2780 2787 ('', 'style', '', _('display using template map file')),
2781 2788 ('r', 'rev', [], _('a specific revision you would like to push')),
2782 2789 ('n', 'newest-first', None, _('show newest record first')),
2783 2790 ('', 'template', '', _('display with template')),
2784 2791 ] + remoteopts,
2785 2792 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2786 2793 "^parents":
2787 2794 (parents,
2788 2795 [('r', 'rev', '', _('show parents from the specified rev')),
2789 2796 ('', 'style', '', _('display using template map file')),
2790 2797 ('', 'template', '', _('display with template'))],
2791 2798 _('hg parents [-r REV] [FILE]')),
2792 2799 "paths": (paths, [], _('hg paths [NAME]')),
2793 2800 "^pull":
2794 2801 (pull,
2795 2802 [('u', 'update', None,
2796 2803 _('update to new tip if changesets were pulled')),
2797 2804 ('f', 'force', None,
2798 2805 _('run even when remote repository is unrelated')),
2799 2806 ('r', 'rev', [],
2800 2807 _('a specific revision up to which you would like to pull')),
2801 2808 ] + remoteopts,
2802 2809 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2803 2810 "^push":
2804 2811 (push,
2805 2812 [('f', 'force', None, _('force push')),
2806 2813 ('r', 'rev', [], _('a specific revision you would like to push')),
2807 2814 ] + remoteopts,
2808 2815 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
2809 2816 "debugrawcommit|rawcommit":
2810 2817 (rawcommit,
2811 2818 [('p', 'parent', [], _('parent')),
2812 2819 ('d', 'date', '', _('date code')),
2813 2820 ('u', 'user', '', _('user')),
2814 2821 ('F', 'files', '', _('file list'))
2815 2822 ] + commitopts,
2816 2823 _('hg debugrawcommit [OPTION]... [FILE]...')),
2817 2824 "recover": (recover, [], _('hg recover')),
2818 2825 "^remove|rm":
2819 2826 (remove,
2820 2827 [('A', 'after', None, _('record remove that has already occurred')),
2821 2828 ('f', 'force', None, _('remove file even if modified')),
2822 2829 ] + walkopts,
2823 2830 _('hg remove [OPTION]... FILE...')),
2824 2831 "rename|mv":
2825 2832 (rename,
2826 2833 [('A', 'after', None, _('record a rename that has already occurred')),
2827 2834 ('f', 'force', None,
2828 2835 _('forcibly copy over an existing managed file')),
2829 2836 ] + walkopts + dryrunopts,
2830 2837 _('hg rename [OPTION]... SOURCE... DEST')),
2831 2838 "^revert":
2832 2839 (revert,
2833 2840 [('a', 'all', None, _('revert all changes when no arguments given')),
2834 2841 ('d', 'date', '', _('tipmost revision matching date')),
2835 2842 ('r', 'rev', '', _('revision to revert to')),
2836 2843 ('', 'no-backup', None, _('do not save backup copies of files')),
2837 2844 ] + walkopts + dryrunopts,
2838 2845 _('hg revert [OPTION]... [-r REV] [NAME]...')),
2839 2846 "rollback": (rollback, [], _('hg rollback')),
2840 2847 "root": (root, [], _('hg root')),
2841 2848 "showconfig|debugconfig":
2842 2849 (showconfig,
2843 2850 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2844 2851 _('showconfig [-u] [NAME]...')),
2845 2852 "^serve":
2846 2853 (serve,
2847 2854 [('A', 'accesslog', '', _('name of access log file to write to')),
2848 2855 ('d', 'daemon', None, _('run server in background')),
2849 2856 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2850 2857 ('E', 'errorlog', '', _('name of error log file to write to')),
2851 2858 ('p', 'port', 0, _('port to use (default: 8000)')),
2852 2859 ('a', 'address', '', _('address to use')),
2853 2860 ('n', 'name', '',
2854 2861 _('name to show in web pages (default: working dir)')),
2855 2862 ('', 'webdir-conf', '', _('name of the webdir config file'
2856 2863 ' (serve more than one repo)')),
2857 2864 ('', 'pid-file', '', _('name of file to write process ID to')),
2858 2865 ('', 'stdio', None, _('for remote clients')),
2859 2866 ('t', 'templates', '', _('web templates to use')),
2860 2867 ('', 'style', '', _('template style to use')),
2861 2868 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2862 2869 _('hg serve [OPTION]...')),
2863 2870 "^status|st":
2864 2871 (status,
2865 2872 [('A', 'all', None, _('show status of all files')),
2866 2873 ('m', 'modified', None, _('show only modified files')),
2867 2874 ('a', 'added', None, _('show only added files')),
2868 2875 ('r', 'removed', None, _('show only removed files')),
2869 2876 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2870 2877 ('c', 'clean', None, _('show only files without changes')),
2871 2878 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2872 2879 ('i', 'ignored', None, _('show ignored files')),
2873 2880 ('n', 'no-status', None, _('hide status prefix')),
2874 2881 ('C', 'copies', None, _('show source of copied files')),
2875 2882 ('0', 'print0', None,
2876 2883 _('end filenames with NUL, for use with xargs')),
2877 2884 ('', 'rev', [], _('show difference from revision')),
2878 2885 ] + walkopts,
2879 2886 _('hg status [OPTION]... [FILE]...')),
2880 2887 "tag":
2881 2888 (tag,
2882 2889 [('l', 'local', None, _('make the tag local')),
2883 2890 ('m', 'message', '', _('message for tag commit log entry')),
2884 2891 ('d', 'date', '', _('record datecode as commit date')),
2885 2892 ('u', 'user', '', _('record user as commiter')),
2886 2893 ('r', 'rev', '', _('revision to tag'))],
2887 2894 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2888 2895 "tags": (tags, [], _('hg tags')),
2889 2896 "tip":
2890 2897 (tip,
2891 2898 [('', 'style', '', _('display using template map file')),
2892 2899 ('p', 'patch', None, _('show patch')),
2893 2900 ('', 'template', '', _('display with template'))],
2894 2901 _('hg tip [-p]')),
2895 2902 "unbundle":
2896 2903 (unbundle,
2897 2904 [('u', 'update', None,
2898 2905 _('update to new tip if changesets were unbundled'))],
2899 2906 _('hg unbundle [-u] FILE')),
2900 2907 "^update|up|checkout|co":
2901 2908 (update,
2902 2909 [('C', 'clean', None, _('overwrite locally modified files')),
2903 2910 ('d', 'date', '', _('tipmost revision matching date'))],
2904 2911 _('hg update [-C] [-d DATE] [REV]')),
2905 2912 "verify": (verify, [], _('hg verify')),
2906 2913 "version": (version_, [], _('hg version')),
2907 2914 }
2908 2915
2909 2916 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2910 2917 " debugindex debugindexdot debugdate debuginstall")
2911 2918 optionalrepo = ("paths serve showconfig")
2912 2919
2913 2920 def findpossible(ui, cmd):
2914 2921 """
2915 2922 Return cmd -> (aliases, command table entry)
2916 2923 for each matching command.
2917 2924 Return debug commands (or their aliases) only if no normal command matches.
2918 2925 """
2919 2926 choice = {}
2920 2927 debugchoice = {}
2921 2928 for e in table.keys():
2922 2929 aliases = e.lstrip("^").split("|")
2923 2930 found = None
2924 2931 if cmd in aliases:
2925 2932 found = cmd
2926 2933 elif not ui.config("ui", "strict"):
2927 2934 for a in aliases:
2928 2935 if a.startswith(cmd):
2929 2936 found = a
2930 2937 break
2931 2938 if found is not None:
2932 2939 if aliases[0].startswith("debug") or found.startswith("debug"):
2933 2940 debugchoice[found] = (aliases, table[e])
2934 2941 else:
2935 2942 choice[found] = (aliases, table[e])
2936 2943
2937 2944 if not choice and debugchoice:
2938 2945 choice = debugchoice
2939 2946
2940 2947 return choice
2941 2948
2942 2949 def findcmd(ui, cmd):
2943 2950 """Return (aliases, command table entry) for command string."""
2944 2951 choice = findpossible(ui, cmd)
2945 2952
2946 2953 if choice.has_key(cmd):
2947 2954 return choice[cmd]
2948 2955
2949 2956 if len(choice) > 1:
2950 2957 clist = choice.keys()
2951 2958 clist.sort()
2952 2959 raise AmbiguousCommand(cmd, clist)
2953 2960
2954 2961 if choice:
2955 2962 return choice.values()[0]
2956 2963
2957 2964 raise UnknownCommand(cmd)
2958 2965
2959 2966 def catchterm(*args):
2960 2967 raise util.SignalInterrupt
2961 2968
2962 2969 def run():
2963 2970 sys.exit(dispatch(sys.argv[1:]))
2964 2971
2965 2972 class ParseError(Exception):
2966 2973 """Exception raised on errors in parsing the command line."""
2967 2974
2968 2975 def parse(ui, args):
2969 2976 options = {}
2970 2977 cmdoptions = {}
2971 2978
2972 2979 try:
2973 2980 args = fancyopts.fancyopts(args, globalopts, options)
2974 2981 except fancyopts.getopt.GetoptError, inst:
2975 2982 raise ParseError(None, inst)
2976 2983
2977 2984 if args:
2978 2985 cmd, args = args[0], args[1:]
2979 2986 aliases, i = findcmd(ui, cmd)
2980 2987 cmd = aliases[0]
2981 2988 defaults = ui.config("defaults", cmd)
2982 2989 if defaults:
2983 2990 args = shlex.split(defaults) + args
2984 2991 c = list(i[1])
2985 2992 else:
2986 2993 cmd = None
2987 2994 c = []
2988 2995
2989 2996 # combine global options into local
2990 2997 for o in globalopts:
2991 2998 c.append((o[0], o[1], options[o[1]], o[3]))
2992 2999
2993 3000 try:
2994 3001 args = fancyopts.fancyopts(args, c, cmdoptions)
2995 3002 except fancyopts.getopt.GetoptError, inst:
2996 3003 raise ParseError(cmd, inst)
2997 3004
2998 3005 # separate global options back out
2999 3006 for o in globalopts:
3000 3007 n = o[1]
3001 3008 options[n] = cmdoptions[n]
3002 3009 del cmdoptions[n]
3003 3010
3004 3011 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3005 3012
3006 3013 external = {}
3007 3014
3008 3015 def findext(name):
3009 3016 '''return module with given extension name'''
3010 3017 try:
3011 3018 return sys.modules[external[name]]
3012 3019 except KeyError:
3013 3020 for k, v in external.iteritems():
3014 3021 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3015 3022 return sys.modules[v]
3016 3023 raise KeyError(name)
3017 3024
3018 3025 def load_extensions(ui):
3019 3026 added = []
3020 3027 for ext_name, load_from_name in ui.extensions():
3021 3028 if ext_name in external:
3022 3029 continue
3023 3030 try:
3024 3031 if load_from_name:
3025 3032 # the module will be loaded in sys.modules
3026 3033 # choose an unique name so that it doesn't
3027 3034 # conflicts with other modules
3028 3035 module_name = "hgext_%s" % ext_name.replace('.', '_')
3029 3036 mod = imp.load_source(module_name, load_from_name)
3030 3037 else:
3031 3038 def importh(name):
3032 3039 mod = __import__(name)
3033 3040 components = name.split('.')
3034 3041 for comp in components[1:]:
3035 3042 mod = getattr(mod, comp)
3036 3043 return mod
3037 3044 try:
3038 3045 mod = importh("hgext.%s" % ext_name)
3039 3046 except ImportError:
3040 3047 mod = importh(ext_name)
3041 3048 external[ext_name] = mod.__name__
3042 3049 added.append((mod, ext_name))
3043 3050 except (util.SignalInterrupt, KeyboardInterrupt):
3044 3051 raise
3045 3052 except Exception, inst:
3046 3053 ui.warn(_("*** failed to import extension %s: %s\n") %
3047 3054 (ext_name, inst))
3048 3055 if ui.print_exc():
3049 3056 return 1
3050 3057
3051 3058 for mod, name in added:
3052 3059 uisetup = getattr(mod, 'uisetup', None)
3053 3060 if uisetup:
3054 3061 uisetup(ui)
3055 3062 cmdtable = getattr(mod, 'cmdtable', {})
3056 3063 overrides = [cmd for cmd in cmdtable if cmd in table]
3057 3064 if overrides:
3058 3065 ui.warn(_("extension '%s' overrides commands: %s\n")
3059 3066 % (name, " ".join(overrides)))
3060 3067 table.update(cmdtable)
3061 3068
3062 3069 def parseconfig(config):
3063 3070 """parse the --config options from the command line"""
3064 3071 parsed = []
3065 3072 for cfg in config:
3066 3073 try:
3067 3074 name, value = cfg.split('=', 1)
3068 3075 section, name = name.split('.', 1)
3069 3076 if not section or not name:
3070 3077 raise IndexError
3071 3078 parsed.append((section, name, value))
3072 3079 except (IndexError, ValueError):
3073 3080 raise util.Abort(_('malformed --config option: %s') % cfg)
3074 3081 return parsed
3075 3082
3076 3083 def dispatch(args):
3077 3084 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3078 3085 num = getattr(signal, name, None)
3079 3086 if num: signal.signal(num, catchterm)
3080 3087
3081 3088 try:
3082 3089 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3083 3090 except util.Abort, inst:
3084 3091 sys.stderr.write(_("abort: %s\n") % inst)
3085 3092 return -1
3086 3093
3087 3094 load_extensions(u)
3088 3095 u.addreadhook(load_extensions)
3089 3096
3090 3097 try:
3091 3098 cmd, func, args, options, cmdoptions = parse(u, args)
3092 3099 if options["encoding"]:
3093 3100 util._encoding = options["encoding"]
3094 3101 if options["encodingmode"]:
3095 3102 util._encodingmode = options["encodingmode"]
3096 3103 if options["time"]:
3097 3104 def get_times():
3098 3105 t = os.times()
3099 3106 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3100 3107 t = (t[0], t[1], t[2], t[3], time.clock())
3101 3108 return t
3102 3109 s = get_times()
3103 3110 def print_time():
3104 3111 t = get_times()
3105 3112 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3106 3113 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3107 3114 atexit.register(print_time)
3108 3115
3109 3116 # enter the debugger before command execution
3110 3117 if options['debugger']:
3111 3118 pdb.set_trace()
3112 3119
3113 3120 try:
3114 3121 if options['cwd']:
3115 3122 os.chdir(options['cwd'])
3116 3123
3117 3124 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3118 3125 not options["noninteractive"], options["traceback"],
3119 3126 parseconfig(options["config"]))
3120 3127
3121 3128 path = u.expandpath(options["repository"]) or ""
3122 3129 repo = path and hg.repository(u, path=path) or None
3123 3130 if repo and not repo.local():
3124 3131 raise util.Abort(_("repository '%s' is not local") % path)
3125 3132
3126 3133 if options['help']:
3127 3134 return help_(u, cmd, options['version'])
3128 3135 elif options['version']:
3129 3136 return version_(u)
3130 3137 elif not cmd:
3131 3138 return help_(u, 'shortlist')
3132 3139
3133 3140 if cmd not in norepo.split():
3134 3141 try:
3135 3142 if not repo:
3136 3143 repo = hg.repository(u, path=path)
3137 3144 u = repo.ui
3138 3145 for name in external.itervalues():
3139 3146 mod = sys.modules[name]
3140 3147 if hasattr(mod, 'reposetup'):
3141 3148 mod.reposetup(u, repo)
3142 3149 hg.repo_setup_hooks.append(mod.reposetup)
3143 3150 except hg.RepoError:
3144 3151 if cmd not in optionalrepo.split():
3145 3152 raise
3146 3153 d = lambda: func(u, repo, *args, **cmdoptions)
3147 3154 else:
3148 3155 d = lambda: func(u, *args, **cmdoptions)
3149 3156
3150 3157 try:
3151 3158 if options['profile']:
3152 3159 import hotshot, hotshot.stats
3153 3160 prof = hotshot.Profile("hg.prof")
3154 3161 try:
3155 3162 try:
3156 3163 return prof.runcall(d)
3157 3164 except:
3158 3165 try:
3159 3166 u.warn(_('exception raised - generating '
3160 3167 'profile anyway\n'))
3161 3168 except:
3162 3169 pass
3163 3170 raise
3164 3171 finally:
3165 3172 prof.close()
3166 3173 stats = hotshot.stats.load("hg.prof")
3167 3174 stats.strip_dirs()
3168 3175 stats.sort_stats('time', 'calls')
3169 3176 stats.print_stats(40)
3170 3177 elif options['lsprof']:
3171 3178 try:
3172 3179 from mercurial import lsprof
3173 3180 except ImportError:
3174 3181 raise util.Abort(_(
3175 3182 'lsprof not available - install from '
3176 3183 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3177 3184 p = lsprof.Profiler()
3178 3185 p.enable(subcalls=True)
3179 3186 try:
3180 3187 return d()
3181 3188 finally:
3182 3189 p.disable()
3183 3190 stats = lsprof.Stats(p.getstats())
3184 3191 stats.sort()
3185 3192 stats.pprint(top=10, file=sys.stderr, climit=5)
3186 3193 else:
3187 3194 return d()
3188 3195 finally:
3189 3196 u.flush()
3190 3197 except:
3191 3198 # enter the debugger when we hit an exception
3192 3199 if options['debugger']:
3193 3200 pdb.post_mortem(sys.exc_info()[2])
3194 3201 u.print_exc()
3195 3202 raise
3196 3203 except ParseError, inst:
3197 3204 if inst.args[0]:
3198 3205 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3199 3206 help_(u, inst.args[0])
3200 3207 else:
3201 3208 u.warn(_("hg: %s\n") % inst.args[1])
3202 3209 help_(u, 'shortlist')
3203 3210 except AmbiguousCommand, inst:
3204 3211 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3205 3212 (inst.args[0], " ".join(inst.args[1])))
3206 3213 except UnknownCommand, inst:
3207 3214 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3208 3215 help_(u, 'shortlist')
3209 3216 except hg.RepoError, inst:
3210 3217 u.warn(_("abort: %s!\n") % inst)
3211 3218 except lock.LockHeld, inst:
3212 3219 if inst.errno == errno.ETIMEDOUT:
3213 3220 reason = _('timed out waiting for lock held by %s') % inst.locker
3214 3221 else:
3215 3222 reason = _('lock held by %s') % inst.locker
3216 3223 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3217 3224 except lock.LockUnavailable, inst:
3218 3225 u.warn(_("abort: could not lock %s: %s\n") %
3219 3226 (inst.desc or inst.filename, inst.strerror))
3220 3227 except revlog.RevlogError, inst:
3221 3228 u.warn(_("abort: %s!\n") % inst)
3222 3229 except util.SignalInterrupt:
3223 3230 u.warn(_("killed!\n"))
3224 3231 except KeyboardInterrupt:
3225 3232 try:
3226 3233 u.warn(_("interrupted!\n"))
3227 3234 except IOError, inst:
3228 3235 if inst.errno == errno.EPIPE:
3229 3236 if u.debugflag:
3230 3237 u.warn(_("\nbroken pipe\n"))
3231 3238 else:
3232 3239 raise
3240 except socket.error, inst:
3241 u.warn(_("abort: %s\n") % inst[1])
3233 3242 except IOError, inst:
3234 3243 if hasattr(inst, "code"):
3235 3244 u.warn(_("abort: %s\n") % inst)
3236 3245 elif hasattr(inst, "reason"):
3237 u.warn(_("abort: error: %s\n") % inst.reason[1])
3246 try: # usually it is in the form (errno, strerror)
3247 reason = inst.reason.args[1]
3248 except: # it might be anything, for example a string
3249 reason = inst.reason
3250 u.warn(_("abort: error: %s\n") % reason)
3238 3251 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3239 3252 if u.debugflag:
3240 3253 u.warn(_("broken pipe\n"))
3241 3254 elif getattr(inst, "strerror", None):
3242 3255 if getattr(inst, "filename", None):
3243 3256 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3244 3257 else:
3245 3258 u.warn(_("abort: %s\n") % inst.strerror)
3246 3259 else:
3247 3260 raise
3248 3261 except OSError, inst:
3249 3262 if getattr(inst, "filename", None):
3250 3263 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3251 3264 else:
3252 3265 u.warn(_("abort: %s\n") % inst.strerror)
3253 3266 except util.UnexpectedOutput, inst:
3254 3267 u.warn(_("abort: %s") % inst[0])
3255 3268 if not isinstance(inst[1], basestring):
3256 3269 u.warn(" %r\n" % (inst[1],))
3257 3270 elif not inst[1]:
3258 3271 u.warn(_(" empty string\n"))
3259 3272 else:
3260 3273 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3261 3274 except util.Abort, inst:
3262 3275 u.warn(_("abort: %s\n") % inst)
3263 3276 except TypeError, inst:
3264 3277 # was this an argument error?
3265 3278 tb = traceback.extract_tb(sys.exc_info()[2])
3266 3279 if len(tb) > 2: # no
3267 3280 raise
3268 3281 u.debug(inst, "\n")
3269 3282 u.warn(_("%s: invalid arguments\n") % cmd)
3270 3283 help_(u, cmd)
3271 3284 except SystemExit, inst:
3272 3285 # Commands shouldn't sys.exit directly, but give a return code.
3273 3286 # Just in case catch this and and pass exit code to caller.
3274 3287 return inst.code
3275 3288 except:
3276 3289 u.warn(_("** unknown exception encountered, details follow\n"))
3277 3290 u.warn(_("** report bug details to "
3278 3291 "http://www.selenic.com/mercurial/bts\n"))
3279 3292 u.warn(_("** or mercurial@selenic.com\n"))
3280 3293 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3281 3294 % version.get_version())
3282 3295 raise
3283 3296
3284 3297 return -1
@@ -1,239 +1,246 b''
1 1 # hgweb/server.py - The standalone hg web server.
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer
9 import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback
10 10 from mercurial import ui, hg, util, templater
11 11 from hgweb_mod import hgweb
12 12 from hgwebdir_mod import hgwebdir
13 13 from request import wsgiapplication
14 14 from mercurial.i18n import gettext as _
15 15
16 16 def _splitURI(uri):
17 17 """ Return path and query splited from uri
18 18
19 19 Just like CGI environment, the path is unquoted, the query is
20 20 not.
21 21 """
22 22 if '?' in uri:
23 23 path, query = uri.split('?', 1)
24 24 else:
25 25 path, query = uri, ''
26 26 return urllib.unquote(path), query
27 27
28 28 class _error_logger(object):
29 29 def __init__(self, handler):
30 30 self.handler = handler
31 31 def flush(self):
32 32 pass
33 33 def write(self, str):
34 34 self.writelines(str.split('\n'))
35 35 def writelines(self, seq):
36 36 for msg in seq:
37 37 self.handler.log_error("HG error: %s", msg)
38 38
39 39 class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler):
40 40 def __init__(self, *args, **kargs):
41 41 self.protocol_version = 'HTTP/1.1'
42 42 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
43 43
44 44 def log_error(self, format, *args):
45 45 errorlog = self.server.errorlog
46 46 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
47 47 self.log_date_time_string(),
48 48 format % args))
49 49
50 50 def log_message(self, format, *args):
51 51 accesslog = self.server.accesslog
52 52 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
53 53 self.log_date_time_string(),
54 54 format % args))
55 55
56 56 def do_POST(self):
57 57 try:
58 self.do_hgweb()
59 except socket.error, inst:
60 if inst[0] != errno.EPIPE:
61 raise
58 try:
59 self.do_hgweb()
60 except socket.error, inst:
61 if inst[0] != errno.EPIPE:
62 raise
63 except StandardError, inst:
64 self._start_response("500 Internal Server Error", [])
65 self._write("Internal Server Error")
66 tb = "".join(traceback.format_exception(*sys.exc_info()))
67 self.log_error("Exception happened during processing request '%s':\n%s",
68 self.path, tb)
62 69
63 70 def do_GET(self):
64 71 self.do_POST()
65 72
66 73 def do_hgweb(self):
67 74 path_info, query = _splitURI(self.path)
68 75
69 76 env = {}
70 77 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
71 78 env['REQUEST_METHOD'] = self.command
72 79 env['SERVER_NAME'] = self.server.server_name
73 80 env['SERVER_PORT'] = str(self.server.server_port)
74 81 env['REQUEST_URI'] = self.path
75 82 env['PATH_INFO'] = path_info
76 83 if query:
77 84 env['QUERY_STRING'] = query
78 85 host = self.address_string()
79 86 if host != self.client_address[0]:
80 87 env['REMOTE_HOST'] = host
81 88 env['REMOTE_ADDR'] = self.client_address[0]
82 89
83 90 if self.headers.typeheader is None:
84 91 env['CONTENT_TYPE'] = self.headers.type
85 92 else:
86 93 env['CONTENT_TYPE'] = self.headers.typeheader
87 94 length = self.headers.getheader('content-length')
88 95 if length:
89 96 env['CONTENT_LENGTH'] = length
90 97 for header in [h for h in self.headers.keys() \
91 98 if h not in ('content-type', 'content-length')]:
92 99 hkey = 'HTTP_' + header.replace('-', '_').upper()
93 100 hval = self.headers.getheader(header)
94 101 hval = hval.replace('\n', '').strip()
95 102 if hval:
96 103 env[hkey] = hval
97 104 env['SERVER_PROTOCOL'] = self.request_version
98 105 env['wsgi.version'] = (1, 0)
99 106 env['wsgi.url_scheme'] = 'http'
100 107 env['wsgi.input'] = self.rfile
101 108 env['wsgi.errors'] = _error_logger(self)
102 109 env['wsgi.multithread'] = isinstance(self.server,
103 110 SocketServer.ThreadingMixIn)
104 111 env['wsgi.multiprocess'] = isinstance(self.server,
105 112 SocketServer.ForkingMixIn)
106 113 env['wsgi.run_once'] = 0
107 114
108 115 self.close_connection = True
109 116 self.saved_status = None
110 117 self.saved_headers = []
111 118 self.sent_headers = False
112 119 self.length = None
113 120 req = self.server.reqmaker(env, self._start_response)
114 121 for data in req:
115 122 if data:
116 123 self._write(data)
117 124
118 125 def send_headers(self):
119 126 if not self.saved_status:
120 127 raise AssertionError("Sending headers before start_response() called")
121 128 saved_status = self.saved_status.split(None, 1)
122 129 saved_status[0] = int(saved_status[0])
123 130 self.send_response(*saved_status)
124 131 should_close = True
125 132 for h in self.saved_headers:
126 133 self.send_header(*h)
127 134 if h[0].lower() == 'content-length':
128 135 should_close = False
129 136 self.length = int(h[1])
130 137 # The value of the Connection header is a list of case-insensitive
131 138 # tokens separated by commas and optional whitespace.
132 139 if 'close' in [token.strip().lower() for token in
133 140 self.headers.get('connection', '').split(',')]:
134 141 should_close = True
135 142 if should_close:
136 143 self.send_header('Connection', 'close')
137 144 self.close_connection = should_close
138 145 self.end_headers()
139 146 self.sent_headers = True
140 147
141 148 def _start_response(self, http_status, headers, exc_info=None):
142 149 code, msg = http_status.split(None, 1)
143 150 code = int(code)
144 151 self.saved_status = http_status
145 152 bad_headers = ('connection', 'transfer-encoding')
146 153 self.saved_headers = [ h for h in headers \
147 154 if h[0].lower() not in bad_headers ]
148 155 return self._write
149 156
150 157 def _write(self, data):
151 158 if not self.saved_status:
152 159 raise AssertionError("data written before start_response() called")
153 160 elif not self.sent_headers:
154 161 self.send_headers()
155 162 if self.length is not None:
156 163 if len(data) > self.length:
157 164 raise AssertionError("Content-length header sent, but more bytes than specified are being written.")
158 165 self.length = self.length - len(data)
159 166 self.wfile.write(data)
160 167 self.wfile.flush()
161 168
162 169 def create_server(ui, repo):
163 170 use_threads = True
164 171
165 172 def openlog(opt, default):
166 173 if opt and opt != '-':
167 174 return open(opt, 'w')
168 175 return default
169 176
170 177 address = ui.config("web", "address", "")
171 178 port = int(ui.config("web", "port", 8000))
172 179 use_ipv6 = ui.configbool("web", "ipv6")
173 180 webdir_conf = ui.config("web", "webdir_conf")
174 181 accesslog = openlog(ui.config("web", "accesslog", "-"), sys.stdout)
175 182 errorlog = openlog(ui.config("web", "errorlog", "-"), sys.stderr)
176 183
177 184 if use_threads:
178 185 try:
179 186 from threading import activeCount
180 187 except ImportError:
181 188 use_threads = False
182 189
183 190 if use_threads:
184 191 _mixin = SocketServer.ThreadingMixIn
185 192 else:
186 193 if hasattr(os, "fork"):
187 194 _mixin = SocketServer.ForkingMixIn
188 195 else:
189 196 class _mixin:
190 197 pass
191 198
192 199 class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
193 200 def __init__(self, *args, **kargs):
194 201 BaseHTTPServer.HTTPServer.__init__(self, *args, **kargs)
195 202 self.accesslog = accesslog
196 203 self.errorlog = errorlog
197 204 self.repo = repo
198 205 self.webdir_conf = webdir_conf
199 206 self.webdirmaker = hgwebdir
200 207 self.repoviewmaker = hgweb
201 208 self.reqmaker = wsgiapplication(self.make_handler)
202 209 self.daemon_threads = True
203 210
204 211 addr, port = self.socket.getsockname()[:2]
205 212 if addr in ('0.0.0.0', '::'):
206 213 addr = socket.gethostname()
207 214 else:
208 215 try:
209 216 addr = socket.gethostbyaddr(addr)[0]
210 217 except socket.error:
211 218 pass
212 219 self.addr, self.port = addr, port
213 220
214 221 def make_handler(self):
215 222 if self.webdir_conf:
216 223 hgwebobj = self.webdirmaker(self.webdir_conf)
217 224 elif self.repo is not None:
218 225 hgwebobj = self.repoviewmaker(repo.__class__(repo.ui,
219 226 repo.origroot))
220 227 else:
221 228 raise hg.RepoError(_("There is no Mercurial repository here"
222 229 " (.hg not found)"))
223 230 return hgwebobj
224 231
225 232 class IPv6HTTPServer(MercurialHTTPServer):
226 233 address_family = getattr(socket, 'AF_INET6', None)
227 234
228 235 def __init__(self, *args, **kwargs):
229 236 if self.address_family is None:
230 237 raise hg.RepoError(_('IPv6 not available on this system'))
231 238 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
232 239
233 240 try:
234 241 if use_ipv6:
235 242 return IPv6HTTPServer((address, port), _hgwebhandler)
236 243 else:
237 244 return MercurialHTTPServer((address, port), _hgwebhandler)
238 245 except socket.error, inst:
239 246 raise util.Abort(_('cannot start server: %s') % inst.args[1])
@@ -1,384 +1,388 b''
1 1 # httprepo.py - HTTP repository proxy classes for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 5 #
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 9 from node import *
10 10 from remoterepo import *
11 11 from i18n import _
12 12 import hg, os, urllib, urllib2, urlparse, zlib, util, httplib
13 13 import errno, keepalive, tempfile, socket, changegroup
14 14
15 15 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
16 16 def __init__(self, ui):
17 17 urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
18 18 self.ui = ui
19 19
20 20 def find_user_password(self, realm, authuri):
21 21 authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
22 22 self, realm, authuri)
23 23 user, passwd = authinfo
24 24 if user and passwd:
25 25 return (user, passwd)
26 26
27 27 if not self.ui.interactive:
28 28 raise util.Abort(_('http authorization required'))
29 29
30 30 self.ui.write(_("http authorization required\n"))
31 31 self.ui.status(_("realm: %s\n") % realm)
32 32 if user:
33 33 self.ui.status(_("user: %s\n") % user)
34 34 else:
35 35 user = self.ui.prompt(_("user:"), default=None)
36 36
37 37 if not passwd:
38 38 passwd = self.ui.getpass()
39 39
40 40 self.add_password(realm, authuri, user, passwd)
41 41 return (user, passwd)
42 42
43 43 def netlocsplit(netloc):
44 44 '''split [user[:passwd]@]host[:port] into 4-tuple.'''
45 45
46 46 a = netloc.find('@')
47 47 if a == -1:
48 48 user, passwd = None, None
49 49 else:
50 50 userpass, netloc = netloc[:a], netloc[a+1:]
51 51 c = userpass.find(':')
52 52 if c == -1:
53 53 user, passwd = urllib.unquote(userpass), None
54 54 else:
55 55 user = urllib.unquote(userpass[:c])
56 56 passwd = urllib.unquote(userpass[c+1:])
57 57 c = netloc.find(':')
58 58 if c == -1:
59 59 host, port = netloc, None
60 60 else:
61 61 host, port = netloc[:c], netloc[c+1:]
62 62 return host, port, user, passwd
63 63
64 64 def netlocunsplit(host, port, user=None, passwd=None):
65 65 '''turn host, port, user, passwd into [user[:passwd]@]host[:port].'''
66 66 if port:
67 67 hostport = host + ':' + port
68 68 else:
69 69 hostport = host
70 70 if user:
71 71 if passwd:
72 72 userpass = urllib.quote(user) + ':' + urllib.quote(passwd)
73 73 else:
74 74 userpass = urllib.quote(user)
75 75 return userpass + '@' + hostport
76 76 return hostport
77 77
78 78 class httpconnection(keepalive.HTTPConnection):
79 79 # must be able to send big bundle as stream.
80 80
81 81 def send(self, data):
82 82 if isinstance(data, str):
83 83 keepalive.HTTPConnection.send(self, data)
84 84 else:
85 85 # if auth required, some data sent twice, so rewind here
86 86 data.seek(0)
87 87 for chunk in util.filechunkiter(data):
88 88 keepalive.HTTPConnection.send(self, chunk)
89 89
90 90 class basehttphandler(keepalive.HTTPHandler):
91 91 def http_open(self, req):
92 92 return self.do_open(httpconnection, req)
93 93
94 94 has_https = hasattr(urllib2, 'HTTPSHandler')
95 95 if has_https:
96 96 class httpsconnection(httplib.HTTPSConnection):
97 97 response_class = keepalive.HTTPResponse
98 98 # must be able to send big bundle as stream.
99 99
100 100 def send(self, data):
101 101 if isinstance(data, str):
102 102 httplib.HTTPSConnection.send(self, data)
103 103 else:
104 104 # if auth required, some data sent twice, so rewind here
105 105 data.seek(0)
106 106 for chunk in util.filechunkiter(data):
107 107 httplib.HTTPSConnection.send(self, chunk)
108 108
109 109 class httphandler(basehttphandler, urllib2.HTTPSHandler):
110 110 def https_open(self, req):
111 111 return self.do_open(httpsconnection, req)
112 112 else:
113 113 class httphandler(basehttphandler):
114 114 pass
115 115
116 116 def zgenerator(f):
117 117 zd = zlib.decompressobj()
118 118 try:
119 119 for chunk in util.filechunkiter(f):
120 120 yield zd.decompress(chunk)
121 121 except httplib.HTTPException, inst:
122 122 raise IOError(None, _('connection ended unexpectedly'))
123 123 yield zd.flush()
124 124
125 125 class httprepository(remoterepository):
126 126 def __init__(self, ui, path):
127 127 self.path = path
128 128 self.caps = None
129 129 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
130 130 if query or frag:
131 131 raise util.Abort(_('unsupported URL component: "%s"') %
132 132 (query or frag))
133 133 if not urlpath: urlpath = '/'
134 134 host, port, user, passwd = netlocsplit(netloc)
135 135
136 136 # urllib cannot handle URLs with embedded user or passwd
137 137 self._url = urlparse.urlunsplit((scheme, netlocunsplit(host, port),
138 138 urlpath, '', ''))
139 139 self.ui = ui
140 140
141 141 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
142 142 # XXX proxyauthinfo = None
143 143 handlers = [httphandler()]
144 144
145 145 if proxyurl:
146 146 # proxy can be proper url or host[:port]
147 147 if not (proxyurl.startswith('http:') or
148 148 proxyurl.startswith('https:')):
149 149 proxyurl = 'http://' + proxyurl + '/'
150 150 snpqf = urlparse.urlsplit(proxyurl)
151 151 proxyscheme, proxynetloc, proxypath, proxyquery, proxyfrag = snpqf
152 152 hpup = netlocsplit(proxynetloc)
153 153
154 154 proxyhost, proxyport, proxyuser, proxypasswd = hpup
155 155 if not proxyuser:
156 156 proxyuser = ui.config("http_proxy", "user")
157 157 proxypasswd = ui.config("http_proxy", "passwd")
158 158
159 159 # see if we should use a proxy for this url
160 160 no_list = [ "localhost", "127.0.0.1" ]
161 161 no_list.extend([p.lower() for
162 162 p in ui.configlist("http_proxy", "no")])
163 163 no_list.extend([p.strip().lower() for
164 164 p in os.getenv("no_proxy", '').split(',')
165 165 if p.strip()])
166 166 # "http_proxy.always" config is for running tests on localhost
167 167 if (not ui.configbool("http_proxy", "always") and
168 168 host.lower() in no_list):
169 169 ui.debug(_('disabling proxy for %s\n') % host)
170 170 else:
171 171 proxyurl = urlparse.urlunsplit((
172 172 proxyscheme, netlocunsplit(proxyhost, proxyport,
173 173 proxyuser, proxypasswd or ''),
174 174 proxypath, proxyquery, proxyfrag))
175 175 handlers.append(urllib2.ProxyHandler({scheme: proxyurl}))
176 176 ui.debug(_('proxying through http://%s:%s\n') %
177 177 (proxyhost, proxyport))
178 178
179 179 # urllib2 takes proxy values from the environment and those
180 180 # will take precedence if found, so drop them
181 181 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
182 182 try:
183 183 if os.environ.has_key(env):
184 184 del os.environ[env]
185 185 except OSError:
186 186 pass
187 187
188 188 passmgr = passwordmgr(ui)
189 189 if user:
190 190 ui.debug(_('http auth: user %s, password %s\n') %
191 191 (user, passwd and '*' * len(passwd) or 'not set'))
192 192 passmgr.add_password(None, host, user, passwd or '')
193 193
194 194 handlers.extend((urllib2.HTTPBasicAuthHandler(passmgr),
195 195 urllib2.HTTPDigestAuthHandler(passmgr)))
196 196 opener = urllib2.build_opener(*handlers)
197 197
198 198 # 1.0 here is the _protocol_ version
199 199 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
200 200 urllib2.install_opener(opener)
201 201
202 202 def url(self):
203 203 return self.path
204 204
205 205 # look up capabilities only when needed
206 206
207 207 def get_caps(self):
208 208 if self.caps is None:
209 209 try:
210 210 self.caps = self.do_read('capabilities').split()
211 211 except hg.RepoError:
212 212 self.caps = ()
213 213 self.ui.debug(_('capabilities: %s\n') %
214 214 (' '.join(self.caps or ['none'])))
215 215 return self.caps
216 216
217 217 capabilities = property(get_caps)
218 218
219 219 def lock(self):
220 220 raise util.Abort(_('operation not supported over http'))
221 221
222 222 def do_cmd(self, cmd, **args):
223 223 data = args.pop('data', None)
224 224 headers = args.pop('headers', {})
225 225 self.ui.debug(_("sending %s command\n") % cmd)
226 226 q = {"cmd": cmd}
227 227 q.update(args)
228 228 qs = '?%s' % urllib.urlencode(q)
229 229 cu = "%s%s" % (self._url, qs)
230 230 try:
231 231 if data:
232 232 self.ui.debug(_("sending %s bytes\n") %
233 233 headers.get('content-length', 'X'))
234 234 resp = urllib2.urlopen(urllib2.Request(cu, data, headers))
235 235 except urllib2.HTTPError, inst:
236 236 if inst.code == 401:
237 237 raise util.Abort(_('authorization failed'))
238 238 raise
239 239 except httplib.HTTPException, inst:
240 240 self.ui.debug(_('http error while sending %s command\n') % cmd)
241 241 self.ui.print_exc()
242 242 raise IOError(None, inst)
243 243 except IndexError:
244 244 # this only happens with Python 2.3, later versions raise URLError
245 245 raise util.Abort(_('http error, possibly caused by proxy setting'))
246 246 # record the url we got redirected to
247 247 resp_url = resp.geturl()
248 248 if resp_url.endswith(qs):
249 249 resp_url = resp_url[:-len(qs)]
250 250 if self._url != resp_url:
251 251 self.ui.status(_('real URL is %s\n') % resp_url)
252 252 self._url = resp_url
253 253 try:
254 254 proto = resp.getheader('content-type')
255 255 except AttributeError:
256 256 proto = resp.headers['content-type']
257 257
258 258 # accept old "text/plain" and "application/hg-changegroup" for now
259 if not proto.startswith('application/mercurial') and \
259 if not proto.startswith('application/mercurial-') and \
260 260 not proto.startswith('text/plain') and \
261 261 not proto.startswith('application/hg-changegroup'):
262 262 raise hg.RepoError(_("'%s' does not appear to be an hg repository") %
263 263 self._url)
264 264
265 if proto.startswith('application/mercurial'):
266 version = proto[22:]
267 if float(version) > 0.1:
265 if proto.startswith('application/mercurial-'):
266 try:
267 version = float(proto[22:])
268 except ValueError:
269 raise hg.RepoError(_("'%s' sent a broken Content-type "
270 "header (%s)") % (self._url, proto))
271 if version > 0.1:
268 272 raise hg.RepoError(_("'%s' uses newer protocol %s") %
269 273 (self._url, version))
270 274
271 275 return resp
272 276
273 277 def do_read(self, cmd, **args):
274 278 fp = self.do_cmd(cmd, **args)
275 279 try:
276 280 return fp.read()
277 281 finally:
278 282 # if using keepalive, allow connection to be reused
279 283 fp.close()
280 284
281 285 def lookup(self, key):
282 286 d = self.do_cmd("lookup", key = key).read()
283 287 success, data = d[:-1].split(' ', 1)
284 288 if int(success):
285 289 return bin(data)
286 290 raise hg.RepoError(data)
287 291
288 292 def heads(self):
289 293 d = self.do_read("heads")
290 294 try:
291 295 return map(bin, d[:-1].split(" "))
292 296 except:
293 297 raise util.UnexpectedOutput(_("unexpected response:"), d)
294 298
295 299 def branches(self, nodes):
296 300 n = " ".join(map(hex, nodes))
297 301 d = self.do_read("branches", nodes=n)
298 302 try:
299 303 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
300 304 return br
301 305 except:
302 306 raise util.UnexpectedOutput(_("unexpected response:"), d)
303 307
304 308 def between(self, pairs):
305 309 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
306 310 d = self.do_read("between", pairs=n)
307 311 try:
308 312 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
309 313 return p
310 314 except:
311 315 raise util.UnexpectedOutput(_("unexpected response:"), d)
312 316
313 317 def changegroup(self, nodes, kind):
314 318 n = " ".join(map(hex, nodes))
315 319 f = self.do_cmd("changegroup", roots=n)
316 320 return util.chunkbuffer(zgenerator(f))
317 321
318 322 def changegroupsubset(self, bases, heads, source):
319 323 baselst = " ".join([hex(n) for n in bases])
320 324 headlst = " ".join([hex(n) for n in heads])
321 325 f = self.do_cmd("changegroupsubset", bases=baselst, heads=headlst)
322 326 return util.chunkbuffer(zgenerator(f))
323 327
324 328 def unbundle(self, cg, heads, source):
325 329 # have to stream bundle to a temp file because we do not have
326 330 # http 1.1 chunked transfer.
327 331
328 332 type = ""
329 333 types = self.capable('unbundle')
330 334 # servers older than d1b16a746db6 will send 'unbundle' as a
331 335 # boolean capability
332 336 try:
333 337 types = types.split(',')
334 338 except AttributeError:
335 339 types = [""]
336 340 if types:
337 341 for x in types:
338 342 if x in changegroup.bundletypes:
339 343 type = x
340 344 break
341 345
342 346 tempname = changegroup.writebundle(cg, None, type)
343 347 fp = file(tempname, "rb")
344 348 try:
345 349 length = os.stat(tempname).st_size
346 350 try:
347 351 rfp = self.do_cmd(
348 352 'unbundle', data=fp,
349 353 headers={'content-length': str(length),
350 354 'content-type': 'application/octet-stream'},
351 355 heads=' '.join(map(hex, heads)))
352 356 try:
353 357 ret = int(rfp.readline())
354 358 self.ui.write(rfp.read())
355 359 return ret
356 360 finally:
357 361 rfp.close()
358 362 except socket.error, err:
359 363 if err[0] in (errno.ECONNRESET, errno.EPIPE):
360 364 raise util.Abort(_('push failed: %s') % err[1])
361 365 raise util.Abort(err[1])
362 366 finally:
363 367 fp.close()
364 368 os.unlink(tempname)
365 369
366 370 def stream_out(self):
367 371 return self.do_cmd('stream_out')
368 372
369 373 class httpsrepository(httprepository):
370 374 def __init__(self, ui, path):
371 375 if not has_https:
372 376 raise util.Abort(_('Python support for SSL and HTTPS '
373 377 'is not installed'))
374 378 httprepository.__init__(self, ui, path)
375 379
376 380 def instance(ui, path, create):
377 381 if create:
378 382 raise util.Abort(_('cannot create new http repository'))
379 383 if path.startswith('hg:'):
380 384 ui.warn(_("hg:// syntax is deprecated, please use http:// instead\n"))
381 385 path = 'http:' + path[3:]
382 386 if path.startswith('https:'):
383 387 return httpsrepository(ui, path)
384 388 return httprepository(ui, path)
@@ -1,216 +1,216 b''
1 1 adding a
2 2 adding b
3 3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 4 pulling from ../b
5 5 searching for changes
6 6 warning: repository is unrelated
7 7 adding changesets
8 8 adding manifests
9 9 adding file changes
10 10 added 1 changesets with 1 changes to 1 files (+1 heads)
11 11 (run 'hg heads' to see heads, 'hg merge' to merge)
12 12 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 13 (branch merge, don't forget to commit)
14 14 %% -R/--repository
15 15 changeset: 0:8580ff50825a
16 16 tag: tip
17 17 user: test
18 18 date: Thu Jan 01 00:00:01 1970 +0000
19 19 summary: a
20 20
21 21 changeset: 0:b6c483daf290
22 22 tag: tip
23 23 user: test
24 24 date: Thu Jan 01 00:00:01 1970 +0000
25 25 summary: b
26 26
27 27 %% abbrev of long option
28 28 changeset: 1:b6c483daf290
29 29 tag: tip
30 30 parent: -1:000000000000
31 31 user: test
32 32 date: Thu Jan 01 00:00:01 1970 +0000
33 33 summary: b
34 34
35 35 %% --cwd
36 36 changeset: 0:8580ff50825a
37 37 tag: tip
38 38 user: test
39 39 date: Thu Jan 01 00:00:01 1970 +0000
40 40 summary: a
41 41
42 42 %% -y/--noninteractive - just be sure it is parsed
43 43 0:8580ff50825a
44 44 0:8580ff50825a
45 45 %% -q/--quiet
46 46 0:8580ff50825a
47 47 0:b6c483daf290
48 48 0:8580ff50825a
49 49 1:b6c483daf290
50 50 %% -v/--verbose
51 51 changeset: 1:b6c483daf290
52 52 tag: tip
53 53 parent: -1:000000000000
54 54 user: test
55 55 date: Thu Jan 01 00:00:01 1970 +0000
56 56 files: b
57 57 description:
58 58 b
59 59
60 60
61 61 changeset: 0:8580ff50825a
62 62 user: test
63 63 date: Thu Jan 01 00:00:01 1970 +0000
64 64 files: a
65 65 description:
66 66 a
67 67
68 68
69 69 changeset: 0:b6c483daf290
70 70 tag: tip
71 71 user: test
72 72 date: Thu Jan 01 00:00:01 1970 +0000
73 73 files: b
74 74 description:
75 75 b
76 76
77 77
78 78 %% --config
79 79 quuxfoo
80 80 abort: malformed --config option:
81 81 abort: malformed --config option: a.b
82 82 abort: malformed --config option: a
83 83 abort: malformed --config option: a.=
84 84 abort: malformed --config option: .b=
85 85 %% --debug
86 86 changeset: 1:b6c483daf2907ce5825c0bb50f5716226281cc1a
87 87 tag: tip
88 88 parent: -1:0000000000000000000000000000000000000000
89 89 parent: -1:0000000000000000000000000000000000000000
90 90 manifest: 1:23226e7a252cacdc2d99e4fbdc3653441056de49
91 91 user: test
92 92 date: Thu Jan 01 00:00:01 1970 +0000
93 93 files+: b
94 94 description:
95 95 b
96 96
97 97
98 98 changeset: 0:8580ff50825a50c8f716709acdf8de0deddcd6ab
99 99 parent: -1:0000000000000000000000000000000000000000
100 100 parent: -1:0000000000000000000000000000000000000000
101 101 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
102 102 user: test
103 103 date: Thu Jan 01 00:00:01 1970 +0000
104 104 files+: a
105 105 description:
106 106 a
107 107
108 108
109 109 %% --traceback
110 110 Traceback (most recent call last):
111 111 %% --time
112 112 Time: real x.x secs (user x.x+x.x sys x.x+x.x)
113 113 %% --version
114 114 Mercurial Distributed SCM (version xxx)
115 115 %% -h/--help
116 116 Mercurial Distributed SCM
117 117
118 118 list of commands (use "hg help -v" to show aliases and global options):
119 119
120 120 add add the specified files on the next commit
121 121 addremove add all new files, delete all missing files
122 122 annotate show changeset information per file line
123 123 archive create unversioned archive of a repository revision
124 124 backout reverse effect of earlier changeset
125 125 branch set or show the current branch name
126 126 branches list repository named branches
127 127 bundle create a changegroup file
128 128 cat output the current or given revision of files
129 129 clone make a copy of an existing repository
130 130 commit commit the specified files or all outstanding changes
131 131 copy mark files as copied for the next commit
132 132 diff diff repository (or selected files)
133 133 export dump the header and diffs for one or more changesets
134 134 grep search for a pattern in specified files and revisions
135 135 heads show current repository heads
136 136 help show help for a command, extension, or list of commands
137 137 identify print information about the working copy
138 138 import import an ordered set of patches
139 139 incoming show new changesets found in source
140 140 init create a new repository in the given directory
141 141 locate locate files matching specific patterns
142 142 log show revision history of entire repository or files
143 143 manifest output the current or given revision of the project manifest
144 merge Merge working directory with another revision
144 merge merge working directory with another revision
145 145 outgoing show changesets not found in destination
146 146 parents show the parents of the working dir or revision
147 147 paths show definition of symbolic path names
148 148 pull pull changes from the specified source
149 149 push push changes to the specified destination
150 150 recover roll back an interrupted transaction
151 151 remove remove the specified files on the next commit
152 152 rename rename files; equivalent of copy + remove
153 153 revert revert files or dirs to their states as of some revision
154 154 rollback roll back the last transaction in this repository
155 155 root print the root (top) of the current working dir
156 156 serve export the repository via HTTP
157 157 showconfig show combined config settings from all hgrc files
158 158 status show changed files in the working directory
159 159 tag add a tag for the current or given revision
160 160 tags list repository tags
161 161 tip show the tip revision
162 162 unbundle apply a changegroup file
163 update update or merge working directory
163 update update working directory
164 164 verify verify the integrity of the repository
165 165 version output version and copyright information
166 166 Mercurial Distributed SCM
167 167
168 168 list of commands (use "hg help -v" to show aliases and global options):
169 169
170 170 add add the specified files on the next commit
171 171 addremove add all new files, delete all missing files
172 172 annotate show changeset information per file line
173 173 archive create unversioned archive of a repository revision
174 174 backout reverse effect of earlier changeset
175 175 branch set or show the current branch name
176 176 branches list repository named branches
177 177 bundle create a changegroup file
178 178 cat output the current or given revision of files
179 179 clone make a copy of an existing repository
180 180 commit commit the specified files or all outstanding changes
181 181 copy mark files as copied for the next commit
182 182 diff diff repository (or selected files)
183 183 export dump the header and diffs for one or more changesets
184 184 grep search for a pattern in specified files and revisions
185 185 heads show current repository heads
186 186 help show help for a command, extension, or list of commands
187 187 identify print information about the working copy
188 188 import import an ordered set of patches
189 189 incoming show new changesets found in source
190 190 init create a new repository in the given directory
191 191 locate locate files matching specific patterns
192 192 log show revision history of entire repository or files
193 193 manifest output the current or given revision of the project manifest
194 merge Merge working directory with another revision
194 merge merge working directory with another revision
195 195 outgoing show changesets not found in destination
196 196 parents show the parents of the working dir or revision
197 197 paths show definition of symbolic path names
198 198 pull pull changes from the specified source
199 199 push push changes to the specified destination
200 200 recover roll back an interrupted transaction
201 201 remove remove the specified files on the next commit
202 202 rename rename files; equivalent of copy + remove
203 203 revert revert files or dirs to their states as of some revision
204 204 rollback roll back the last transaction in this repository
205 205 root print the root (top) of the current working dir
206 206 serve export the repository via HTTP
207 207 showconfig show combined config settings from all hgrc files
208 208 status show changed files in the working directory
209 209 tag add a tag for the current or given revision
210 210 tags list repository tags
211 211 tip show the tip revision
212 212 unbundle apply a changegroup file
213 update update or merge working directory
213 update update working directory
214 214 verify verify the integrity of the repository
215 215 version output version and copyright information
216 216 %% not tested: --debugger
@@ -1,288 +1,292 b''
1 1 Mercurial Distributed SCM
2 2
3 3 basic commands (use "hg help" for the full list or option "-v" for details):
4 4
5 5 add add the specified files on the next commit
6 6 annotate show changeset information per file line
7 7 clone make a copy of an existing repository
8 8 commit commit the specified files or all outstanding changes
9 9 diff diff repository (or selected files)
10 10 export dump the header and diffs for one or more changesets
11 11 init create a new repository in the given directory
12 12 log show revision history of entire repository or files
13 merge merge working directory with another revision
13 14 parents show the parents of the working dir or revision
14 15 pull pull changes from the specified source
15 16 push push changes to the specified destination
16 17 remove remove the specified files on the next commit
17 18 revert revert files or dirs to their states as of some revision
18 19 serve export the repository via HTTP
19 20 status show changed files in the working directory
20 update update or merge working directory
21 update update working directory
21 22 add add the specified files on the next commit
22 23 annotate show changeset information per file line
23 24 clone make a copy of an existing repository
24 25 commit commit the specified files or all outstanding changes
25 26 diff diff repository (or selected files)
26 27 export dump the header and diffs for one or more changesets
27 28 init create a new repository in the given directory
28 29 log show revision history of entire repository or files
30 merge merge working directory with another revision
29 31 parents show the parents of the working dir or revision
30 32 pull pull changes from the specified source
31 33 push push changes to the specified destination
32 34 remove remove the specified files on the next commit
33 35 revert revert files or dirs to their states as of some revision
34 36 serve export the repository via HTTP
35 37 status show changed files in the working directory
36 update update or merge working directory
38 update update working directory
37 39 Mercurial Distributed SCM
38 40
39 41 list of commands (use "hg help -v" to show aliases and global options):
40 42
41 43 add add the specified files on the next commit
42 44 addremove add all new files, delete all missing files
43 45 annotate show changeset information per file line
44 46 archive create unversioned archive of a repository revision
45 47 backout reverse effect of earlier changeset
46 48 branch set or show the current branch name
47 49 branches list repository named branches
48 50 bundle create a changegroup file
49 51 cat output the current or given revision of files
50 52 clone make a copy of an existing repository
51 53 commit commit the specified files or all outstanding changes
52 54 copy mark files as copied for the next commit
53 55 diff diff repository (or selected files)
54 56 export dump the header and diffs for one or more changesets
55 57 grep search for a pattern in specified files and revisions
56 58 heads show current repository heads
57 59 help show help for a command, extension, or list of commands
58 60 identify print information about the working copy
59 61 import import an ordered set of patches
60 62 incoming show new changesets found in source
61 63 init create a new repository in the given directory
62 64 locate locate files matching specific patterns
63 65 log show revision history of entire repository or files
64 66 manifest output the current or given revision of the project manifest
65 merge Merge working directory with another revision
67 merge merge working directory with another revision
66 68 outgoing show changesets not found in destination
67 69 parents show the parents of the working dir or revision
68 70 paths show definition of symbolic path names
69 71 pull pull changes from the specified source
70 72 push push changes to the specified destination
71 73 recover roll back an interrupted transaction
72 74 remove remove the specified files on the next commit
73 75 rename rename files; equivalent of copy + remove
74 76 revert revert files or dirs to their states as of some revision
75 77 rollback roll back the last transaction in this repository
76 78 root print the root (top) of the current working dir
77 79 serve export the repository via HTTP
78 80 showconfig show combined config settings from all hgrc files
79 81 status show changed files in the working directory
80 82 tag add a tag for the current or given revision
81 83 tags list repository tags
82 84 tip show the tip revision
83 85 unbundle apply a changegroup file
84 update update or merge working directory
86 update update working directory
85 87 verify verify the integrity of the repository
86 88 version output version and copyright information
87 89 add add the specified files on the next commit
88 90 addremove add all new files, delete all missing files
89 91 annotate show changeset information per file line
90 92 archive create unversioned archive of a repository revision
91 93 backout reverse effect of earlier changeset
92 94 branch set or show the current branch name
93 95 branches list repository named branches
94 96 bundle create a changegroup file
95 97 cat output the current or given revision of files
96 98 clone make a copy of an existing repository
97 99 commit commit the specified files or all outstanding changes
98 100 copy mark files as copied for the next commit
99 101 diff diff repository (or selected files)
100 102 export dump the header and diffs for one or more changesets
101 103 grep search for a pattern in specified files and revisions
102 104 heads show current repository heads
103 105 help show help for a command, extension, or list of commands
104 106 identify print information about the working copy
105 107 import import an ordered set of patches
106 108 incoming show new changesets found in source
107 109 init create a new repository in the given directory
108 110 locate locate files matching specific patterns
109 111 log show revision history of entire repository or files
110 112 manifest output the current or given revision of the project manifest
111 merge Merge working directory with another revision
113 merge merge working directory with another revision
112 114 outgoing show changesets not found in destination
113 115 parents show the parents of the working dir or revision
114 116 paths show definition of symbolic path names
115 117 pull pull changes from the specified source
116 118 push push changes to the specified destination
117 119 recover roll back an interrupted transaction
118 120 remove remove the specified files on the next commit
119 121 rename rename files; equivalent of copy + remove
120 122 revert revert files or dirs to their states as of some revision
121 123 rollback roll back the last transaction in this repository
122 124 root print the root (top) of the current working dir
123 125 serve export the repository via HTTP
124 126 showconfig show combined config settings from all hgrc files
125 127 status show changed files in the working directory
126 128 tag add a tag for the current or given revision
127 129 tags list repository tags
128 130 tip show the tip revision
129 131 unbundle apply a changegroup file
130 update update or merge working directory
132 update update working directory
131 133 verify verify the integrity of the repository
132 134 version output version and copyright information
133 135 hg add [OPTION]... [FILE]...
134 136
135 137 add the specified files on the next commit
136 138
137 139 Schedule files to be version controlled and added to the repository.
138 140
139 141 The files will be added to the repository at the next commit. To
140 142 undo an add before that, see hg revert.
141 143
142 144 If no names are given, add all files in the repository.
143 145
144 146 options:
145 147
146 148 -I --include include names matching the given patterns
147 149 -X --exclude exclude names matching the given patterns
148 150 -n --dry-run do not perform actions, just print output
149 151 hg add: option --skjdfks not recognized
150 152 hg add [OPTION]... [FILE]...
151 153
152 154 add the specified files on the next commit
153 155
154 156 Schedule files to be version controlled and added to the repository.
155 157
156 158 The files will be added to the repository at the next commit. To
157 159 undo an add before that, see hg revert.
158 160
159 161 If no names are given, add all files in the repository.
160 162
161 163 options:
162 164
163 165 -I --include include names matching the given patterns
164 166 -X --exclude exclude names matching the given patterns
165 167 -n --dry-run do not perform actions, just print output
166 168 hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...
167 169
168 170 diff repository (or selected files)
169 171
170 172 Show differences between revisions for the specified files.
171 173
172 174 Differences between files are shown using the unified diff format.
173 175
174 176 NOTE: diff may generate unexpected results for merges, as it will
175 177 default to comparing against the working directory's first parent
176 178 changeset if no revisions are specified.
177 179
178 180 When two revision arguments are given, then changes are shown
179 181 between those revisions. If only one revision is specified then
180 182 that revision is compared to the working directory, and, when no
181 183 revisions are specified, the working directory files are compared
182 184 to its parent.
183 185
184 186 Without the -a option, diff will avoid generating diffs of files
185 187 it detects as binary. With -a, diff will generate a diff anyway,
186 188 probably with undesirable results.
187 189
188 190 options:
189 191
190 192 -r --rev revision
191 193 -a --text treat all files as text
192 194 -p --show-function show which function each change is in
193 195 -g --git use git extended diff format
194 196 --nodates don't include dates in diff headers
195 197 -w --ignore-all-space ignore white space when comparing lines
196 198 -b --ignore-space-change ignore changes in the amount of white space
197 199 -B --ignore-blank-lines ignore changes whose lines are all blank
198 200 -I --include include names matching the given patterns
199 201 -X --exclude exclude names matching the given patterns
200 202 hg status [OPTION]... [FILE]...
201 203
202 204 show changed files in the working directory
203 205
204 206 Show status of files in the repository. If names are given, only
205 207 files that match are shown. Files that are clean or ignored, are
206 208 not listed unless -c (clean), -i (ignored) or -A is given.
207 209
208 210 NOTE: status may appear to disagree with diff if permissions have
209 211 changed or a merge has occurred. The standard diff format does not
210 212 report permission changes and diff only reports changes relative
211 213 to one merge parent.
212 214
213 215 If one revision is given, it is used as the base revision.
214 216 If two revisions are given, the difference between them is shown.
215 217
216 218 The codes used to show the status of files are:
217 219 M = modified
218 220 A = added
219 221 R = removed
220 222 C = clean
221 223 ! = deleted, but still tracked
222 224 ? = not tracked
223 225 I = ignored (not shown by default)
224 226 = the previous added file was copied from here
225 227
226 228 aliases: st
227 229
228 230 options:
229 231
230 232 -A --all show status of all files
231 233 -m --modified show only modified files
232 234 -a --added show only added files
233 235 -r --removed show only removed files
234 236 -d --deleted show only deleted (but tracked) files
235 237 -c --clean show only files without changes
236 238 -u --unknown show only unknown (not tracked) files
237 239 -i --ignored show ignored files
238 240 -n --no-status hide status prefix
239 241 -C --copies show source of copied files
240 242 -0 --print0 end filenames with NUL, for use with xargs
241 243 --rev show difference from revision
242 244 -I --include include names matching the given patterns
243 245 -X --exclude exclude names matching the given patterns
244 246 hg status [OPTION]... [FILE]...
245 247
246 248 show changed files in the working directory
247 249 hg: unknown command 'foo'
248 250 Mercurial Distributed SCM
249 251
250 252 basic commands (use "hg help" for the full list or option "-v" for details):
251 253
252 254 add add the specified files on the next commit
253 255 annotate show changeset information per file line
254 256 clone make a copy of an existing repository
255 257 commit commit the specified files or all outstanding changes
256 258 diff diff repository (or selected files)
257 259 export dump the header and diffs for one or more changesets
258 260 init create a new repository in the given directory
259 261 log show revision history of entire repository or files
262 merge merge working directory with another revision
260 263 parents show the parents of the working dir or revision
261 264 pull pull changes from the specified source
262 265 push push changes to the specified destination
263 266 remove remove the specified files on the next commit
264 267 revert revert files or dirs to their states as of some revision
265 268 serve export the repository via HTTP
266 269 status show changed files in the working directory
267 update update or merge working directory
270 update update working directory
268 271 hg: unknown command 'skjdfks'
269 272 Mercurial Distributed SCM
270 273
271 274 basic commands (use "hg help" for the full list or option "-v" for details):
272 275
273 276 add add the specified files on the next commit
274 277 annotate show changeset information per file line
275 278 clone make a copy of an existing repository
276 279 commit commit the specified files or all outstanding changes
277 280 diff diff repository (or selected files)
278 281 export dump the header and diffs for one or more changesets
279 282 init create a new repository in the given directory
280 283 log show revision history of entire repository or files
284 merge merge working directory with another revision
281 285 parents show the parents of the working dir or revision
282 286 pull pull changes from the specified source
283 287 push push changes to the specified destination
284 288 remove remove the specified files on the next commit
285 289 revert revert files or dirs to their states as of some revision
286 290 serve export the repository via HTTP
287 291 status show changed files in the working directory
288 update update or merge working directory
292 update update working directory
@@ -1,39 +1,40 b''
1 1 #!/bin/sh
2 2
3 3 cat <<EOF >> $HGRCPATH
4 4 [extensions]
5 5 notify=
6 6
7 7 [hooks]
8 8 incoming.notify = python:hgext.notify.hook
9 9
10 10 [notify]
11 11 config = $HGTMP/.notify.conf
12 12 sources = pull
13 13 domain = test.com
14 14 strip = 3
15 15 template = Subject: {desc|firstline|strip}\nFrom: {author}\n\nchangeset {node|short} in {webroot}\ndescription:\n\t{desc|tabindent|strip}
16 16 diffstat = False
17 17
18 18 [web]
19 19 baseurl = http://test/
20 20
21 21 [usersubs]
22 22 foo@bar = *
23 23 EOF
24 24
25 hg help notify
25 26 hg init a
26 27 echo a > a/a
27 28 echo % commit
28 29 hg --traceback --cwd a commit -Ama -d '0 0'
29 30
30 31 echo % clone
31 32 hg --traceback clone a b
32 33
33 34 echo a >> a/a
34 35 echo % commit
35 36 hg --traceback --cwd a commit -Amb -d '1 0'
36 37
37 38 echo % pull
38 39 hg --traceback --cwd b pull ../a 2>&1 | sed -e 's/\(Message-Id:\).*/\1/' \
39 40 -e 's/changeset \([0-9a-f]*\) in .*/changeset \1/'
@@ -1,30 +1,33 b''
1 notify extension - No help text available
2
3 no commands defined
1 4 % commit
2 5 adding a
3 6 % clone
4 7 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
5 8 % commit
6 9 % pull
7 10 pulling from ../a
8 11 searching for changes
9 12 adding changesets
10 13 adding manifests
11 14 adding file changes
12 15 added 1 changesets with 1 changes to 1 files
13 16 Subject: b
14 17 From: test@test.com
15 18 X-Hg-Notification: changeset 0647d048b600
16 19 Message-Id:
17 20 To: foo@bar
18 21
19 22 changeset 0647d048b600
20 23 description:
21 24 b
22 25 diffs (6 lines):
23 26
24 27 diff -r cb9a9f314b8b -r 0647d048b600 a
25 28 --- a/a Thu Jan 01 00:00:00 1970 +0000
26 29 +++ b/a Thu Jan 01 00:00:01 1970 +0000
27 30 @@ -1,1 +1,2 @@ a
28 31 a
29 32 +a
30 33 (run 'hg update' to get a working copy)
@@ -1,26 +1,27 b''
1 1 adding a
2 2 0: a
3 3 hg: unknown command 'an'
4 4 Mercurial Distributed SCM
5 5
6 6 basic commands (use "hg help" for the full list or option "-v" for details):
7 7
8 8 add add the specified files on the next commit
9 9 annotate show changeset information per file line
10 10 clone make a copy of an existing repository
11 11 commit commit the specified files or all outstanding changes
12 12 diff diff repository (or selected files)
13 13 export dump the header and diffs for one or more changesets
14 14 init create a new repository in the given directory
15 15 log show revision history of entire repository or files
16 merge merge working directory with another revision
16 17 parents show the parents of the working dir or revision
17 18 pull pull changes from the specified source
18 19 push push changes to the specified destination
19 20 remove remove the specified files on the next commit
20 21 revert revert files or dirs to their states as of some revision
21 22 serve export the repository via HTTP
22 23 status show changed files in the working directory
23 update update or merge working directory
24 update update working directory
24 25 0: a
25 26 % should succeed - up is an alias, not an abbreviation
26 27 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
General Comments 0
You need to be logged in to leave comments. Login now