mercurial-2.8.2/0000755000175000017500000000000012261160455015316 5ustar oxymoronoxymoron00000000000000mercurial-2.8.2/COPYING0000644000175000017500000004325412261160437016361 0ustar oxymoronoxymoron00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. mercurial-2.8.2/doc/0000755000175000017500000000000012261160455016063 5ustar oxymoronoxymoron00000000000000mercurial-2.8.2/doc/hg.1.html0000644000175000017500000156607012261160452017522 0ustar oxymoronoxymoron00000000000000 hg

hg

Mercurial source code management system

Author: Matt Mackall <mpm@selenic.com>
Organization: Mercurial
Manual section:1
Manual group:Mercurial Manual

Synopsis

hg command [option]... [argument]...

Description

The hg command provides a command line interface to the Mercurial system.

Command Elements

files...
indicates one or more filename or relative path filenames; see File Name Patterns for information on pattern matching
path
indicates a path on the local machine
revision
indicates a changeset which can be specified as a changeset revision number, a tag, or a unique substring of the changeset hash value
repository path
either the pathname of a local repository or the URI of a remote repository.

Options

-R, --repository
 repository root directory or name of overlay bundle file
--cwd change working directory
-y, --noninteractive
 do not prompt, automatically pick the first choice for all prompts
-q, --quiet suppress output
-v, --verbose enable additional output
--config set/override config option (use 'section.name=value')
--debug enable debugging output
--debugger start debugger
--encoding set the charset encoding (default: UTF-8)
--encodingmode set the charset encoding mode (default: strict)
--traceback always print a traceback on exception
--time time how long the command takes
--profile print command execution profile
--version output version information and exit
-h, --help display help and exit
--hidden consider hidden changesets

Commands

add

hg add [OPTION]... [FILE]...

Schedule files to be version controlled and added to the repository.

The files will be added to the repository at the next commit. To undo an add before that, see hg forget.

If no names are given, add all files to the repository.

An example showing how new (unknown) files are added automatically by hg add:

$ ls
foo.c
$ hg status
? foo.c
$ hg add
adding foo.c
$ hg status
A foo.c

Returns 0 if all files are successfully added.

Options:

-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns
-S, --subrepos recurse into subrepositories
-n, --dry-run do not perform actions, just print output

addremove

hg addremove [OPTION]... [FILE]...

Add all new files and remove all missing files from the repository.

New files are ignored if they match any of the patterns in .hgignore. As with add, these changes take effect at the next commit.

Use the -s/--similarity option to detect renamed files. This option takes a percentage between 0 (disabled) and 100 (files must be identical) as its parameter. With a parameter greater than 0, this compares every removed file with every added file and records those similar enough as renames. Detecting renamed files this way can be expensive. After using this option, hg status -C can be used to check which files were identified as moved or renamed. If not specified, -s/--similarity defaults to 100 and only renames of identical files are detected.

Returns 0 if all files are successfully added.

Options:

-s, --similarity
 guess renamed files by similarity (0<=s<=100)
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns
-n, --dry-run do not perform actions, just print output

annotate

hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...

List changes in files, showing the revision id responsible for each line

This command is useful for discovering when a change was made and by whom.

Without the -a/--text option, annotate will avoid processing files it detects as binary. With -a, annotate will annotate the file anyway, although the results will probably be neither useful nor desirable.

Returns 0 on success.

Options:

-r, --rev annotate the specified revision
--follow follow copies/renames and list the filename (DEPRECATED)
--no-follow don't follow copies and renames
-a, --text treat all files as text
-u, --user list the author (long with -v)
-f, --file list the filename
-d, --date list the date (short with -q)
-n, --number list the revision number (default)
-c, --changeset
 list the changeset
-l, --line-number
 show line number at the first appearance
-w, --ignore-all-space
 ignore white space when comparing lines
-b, --ignore-space-change
 ignore changes in the amount of white space
-B, --ignore-blank-lines
 ignore changes whose lines are all blank
-I, --include include names matching the given patterns
-X, --exclude

exclude names matching the given patterns

aliases: blame

archive

hg archive [OPTION]... DEST

By default, the revision used is the parent of the working directory; use -r/--rev to specify a different revision.

The archive type is automatically detected based on file extension (or override using -t/--type).

Examples:

  • create a zip file containing the 1.0 release:

    hg archive -r 1.0 project-1.0.zip
    
  • create a tarball excluding .hg files:

    hg archive project.tar.gz -X ".hg*"
    

Valid types are:

files:a directory full of files (default)
tar:tar archive, uncompressed
tbz2:tar archive, compressed using bzip2
tgz:tar archive, compressed using gzip
uzip:zip archive, uncompressed
zip:zip archive, compressed using deflate

The exact name of the destination archive or directory is given using a format string; see hg help export for details.

Each member added to an archive file has a directory prefix prepended. Use -p/--prefix to specify a format string for the prefix. The default is the basename of the archive, with suffixes removed.

Returns 0 on success.

Options:

--no-decode do not pass files through decoders
-p, --prefix directory prefix for files in archive
-r, --rev revision to distribute
-t, --type type of distribution to create
-S, --subrepos recurse into subrepositories
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns

backout

hg backout [OPTION]... [-r] REV

Prepare a new changeset with the effect of REV undone in the current working directory.

If REV is the parent of the working directory, then this new changeset is committed automatically. Otherwise, hg needs to merge the changes and the merged result is left uncommitted.

Note

backout cannot be used to fix either an unwanted or incorrect merge.

By default, the pending changeset will have one parent, maintaining a linear history. With --merge, the pending changeset will instead have two parents: the old parent of the working directory and a new child of REV that simply undoes REV.

Before version 1.7, the behavior without --merge was equivalent to specifying --merge followed by hg update --clean . to cancel the merge and leave the child of REV as a head to be merged separately.

See hg help dates for a list of formats valid for -d/--date.

Returns 0 on success.

Options:

--merge merge with old dirstate parent after backout
--parent parent to choose when backing out merge (DEPRECATED)
-r, --rev revision to backout
-t, --tool specify merge tool
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns
-m, --message use text as commit message
-l, --logfile read commit message from file
-d, --date record the specified date as commit date
-u, --user record the specified user as committer

bisect

hg bisect [-gbsr] [-U] [-c CMD] [REV]

This command helps to find changesets which introduce problems. To use, mark the earliest changeset you know exhibits the problem as bad, then mark the latest changeset which is free from the problem as good. Bisect will update your working directory to a revision for testing (unless the -U/--noupdate option is specified). Once you have performed tests, mark the working directory as good or bad, and bisect will either update to another candidate changeset or announce that it has found the bad revision.

As a shortcut, you can also use the revision argument to mark a revision as good or bad without checking it out first.

If you supply a command, it will be used for automatic bisection. The environment variable HG_NODE will contain the ID of the changeset being tested. The exit status of the command will be used to mark revisions as good or bad: status 0 means good, 125 means to skip the revision, 127 (command not found) will abort the bisection, and any other non-zero exit status means the revision is bad.

Some examples:

  • start a bisection with known bad revision 34, and good revision 12:

    hg bisect --bad 34
    hg bisect --good 12
    
  • advance the current bisection by marking current revision as good or bad:

    hg bisect --good
    hg bisect --bad
    
  • mark the current revision, or a known revision, to be skipped (e.g. if that revision is not usable because of another issue):

    hg bisect --skip
    hg bisect --skip 23
    
  • skip all revisions that do not touch directories foo or bar:

    hg bisect --skip "!( file('path:foo') & file('path:bar') )"
    
  • forget the current bisection:

    hg bisect --reset
    
  • use 'make && make tests' to automatically find the first broken revision:

    hg bisect --reset
    hg bisect --bad 34
    hg bisect --good 12
    hg bisect --command "make && make tests"
    
  • see all changesets whose states are already known in the current bisection:

    hg log -r "bisect(pruned)"
    
  • see the changeset currently being bisected (especially useful if running with -U/--noupdate):

    hg log -r "bisect(current)"
    
  • see all changesets that took part in the current bisection:

    hg log -r "bisect(range)"
    
  • you can even get a nice graph:

    hg log --graph -r "bisect(range)"
    

See hg help revsets for more about the bisect() keyword.

Returns 0 on success.

Options:

-r, --reset reset bisect state
-g, --good mark changeset good
-b, --bad mark changeset bad
-s, --skip skip testing changeset
-e, --extend extend the bisect range
-c, --command use command to check changeset state
-U, --noupdate do not update to target

bookmarks

hg bookmarks [OPTIONS]... [NAME]...

Bookmarks are pointers to certain commits that move when committing. Bookmarks are local. They can be renamed, copied and deleted. It is possible to use hg merge NAME to merge from a given bookmark, and hg update NAME to update to a given bookmark.

You can use hg bookmark NAME to set a bookmark on the working directory's parent revision with the given name. If you specify a revision using -r REV (where REV may be an existing bookmark), the bookmark is assigned to that revision.

Bookmarks can be pushed and pulled between repositories (see hg help push and hg help pull). This requires both the local and remote repositories to support bookmarks. For versions prior to 1.8, this means the bookmarks extension must be enabled.

If you set a bookmark called '@', new clones of the repository will have that revision checked out (and the bookmark made active) by default.

With -i/--inactive, the new bookmark will not be made the active bookmark. If -r/--rev is given, the new bookmark will not be made active even if -i/--inactive is not given. If no NAME is given, the current active bookmark will be marked inactive.

Options:

-f, --force force
-r, --rev revision
-d, --delete delete a given bookmark
-m, --rename rename a given bookmark
-i, --inactive

mark a bookmark inactive

aliases: bookmark

branch

hg branch [-fC] [NAME]

Note

Branch names are permanent and global. Use hg bookmark to create a light-weight bookmark instead. See hg help glossary for more information about named branches and bookmarks.

With no argument, show the current branch name. With one argument, set the working directory branch name (the branch will not exist in the repository until the next commit). Standard practice recommends that primary development take place on the 'default' branch.

Unless -f/--force is specified, branch will not let you set a branch name that already exists, even if it's inactive.

Use -C/--clean to reset the working directory branch to that of the parent of the working directory, negating a previous branch change.

Use the command hg update to switch to an existing branch. Use hg commit --close-branch to mark this branch as closed.

Returns 0 on success.

Options:

-f, --force set branch name even if it shadows an existing branch
-C, --clean reset branch name to parent branch name

branches

hg branches [-ac]

List the repository's named branches, indicating which ones are inactive. If -c/--closed is specified, also list branches which have been marked closed (see hg commit --close-branch).

If -a/--active is specified, only show active branches. A branch is considered active if it contains repository heads.

Use the command hg update to switch to an existing branch.

Returns 0.

Options:

-a, --active show only branches that have unmerged heads
-c, --closed show normal and closed branches

bundle

hg bundle [-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]

Generate a compressed changegroup file collecting changesets not known to be in another repository.

If you omit the destination repository, then hg assumes the destination will have all the nodes you specify with --base parameters. To create a bundle containing all changesets, use -a/--all (or --base null).

You can change compression method with the -t/--type option. The available compression methods are: none, bzip2, and gzip (by default, bundles are compressed using bzip2).

The bundle file can then be transferred using conventional means and applied to another repository with the unbundle or pull command. This is useful when direct push and pull are not available or when exporting an entire repository is undesirable.

Applying bundles preserves all changeset contents including permissions, copy/rename information, and revision history.

Returns 0 on success, 1 if no changes found.

Options:

-f, --force run even when the destination is unrelated
-r, --rev a changeset intended to be added to the destination
-b, --branch a specific branch you would like to bundle
--base a base changeset assumed to be available at the destination
-a, --all bundle all changesets in the repository
-t, --type bundle compression type to use (default: bzip2)
-e, --ssh specify ssh command to use
--remotecmd specify hg command to run on the remote side
--insecure do not verify server certificate (ignoring web.cacerts config)

cat

hg cat [OPTION]... FILE...

Print the specified files as they were at the given revision. If no revision is given, the parent of the working directory is used.

Output may be to a file, in which case the name of the file is given using a format string. The formatting rules are the same as for the export command, with the following additions:

%s:basename of file being printed
%d:dirname of file being printed, or '.' if in repository root
%p:root-relative path name of file being printed

Returns 0 on success.

Options:

-o, --output print output to file with formatted name
-r, --rev print the given revision
--decode apply any matching decode filter
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns

clone

hg clone [OPTION]... SOURCE [DEST]

Create a copy of an existing repository in a new directory.

If no destination directory name is specified, it defaults to the basename of the source.

The location of the source is added to the new repository's .hg/hgrc file, as the default to be used for future pulls.

Only local paths and ssh:// URLs are supported as destinations. For ssh:// destinations, no working directory or .hg/hgrc will be created on the remote side.

To pull only a subset of changesets, specify one or more revisions identifiers with -r/--rev or branches with -b/--branch. The resulting clone will contain only the specified changesets and their ancestors. These options (or 'clone src#rev dest') imply --pull, even for local source repositories. Note that specifying a tag will include the tagged changeset but not the changeset containing the tag.

If the source repository has a bookmark called '@' set, that revision will be checked out in the new repository by default.

To check out a particular version, use -u/--update, or -U/--noupdate to create a clone with no working directory.

For efficiency, hardlinks are used for cloning whenever the source and destination are on the same filesystem (note this applies only to the repository data, not to the working directory). Some filesystems, such as AFS, implement hardlinking incorrectly, but do not report errors. In these cases, use the --pull option to avoid hardlinking.

In some cases, you can clone repositories and the working directory using full hardlinks with

$ cp -al REPO REPOCLONE

This is the fastest way to clone, but it is not always safe. The operation is not atomic (making sure REPO is not modified during the operation is up to you) and you have to make sure your editor breaks hardlinks (Emacs and most Linux Kernel tools do so). Also, this is not compatible with certain extensions that place their metadata under the .hg directory, such as mq.

Mercurial will update the working directory to the first applicable revision from this list:

  1. null if -U or the source repository has no changesets
  2. if -u . and the source repository is local, the first parent of the source repository's working directory
  3. the changeset specified with -u (if a branch name, this means the latest head of that branch)
  4. the changeset specified with -r
  5. the tipmost head specified with -b
  6. the tipmost head specified with the url#branch source syntax
  7. the revision marked with the '@' bookmark, if present
  8. the tipmost head of the default branch
  9. tip

Examples:

  • clone a remote repository to a new directory named hg/:

    hg clone http://selenic.com/hg
    
  • create a lightweight local clone:

    hg clone project/ project-feature/
    
  • clone from an absolute path on an ssh server (note double-slash):

    hg clone ssh://user@server//home/projects/alpha/
    
  • do a high-speed clone over a LAN while checking out a specified version:

    hg clone --uncompressed http://server/repo -u 1.5
    
  • create a repository without changesets after a particular revision:

    hg clone -r 04e544 experimental/ good/
    
  • clone (and track) a particular named branch:

    hg clone http://selenic.com/hg#stable
    

See hg help urls for details on specifying URLs.

Returns 0 on success.

Options:

-U, --noupdate the clone will include an empty working copy (only a repository)
-u, --updaterev
 revision, tag or branch to check out
-r, --rev include the specified changeset
-b, --branch clone only the specified branch
--pull use pull protocol to copy metadata
--uncompressed use uncompressed transfer (fast over LAN)
-e, --ssh specify ssh command to use
--remotecmd specify hg command to run on the remote side
--insecure do not verify server certificate (ignoring web.cacerts config)

commit

hg commit [OPTION]... [FILE]...

Commit changes to the given files into the repository. Unlike a centralized SCM, this operation is a local operation. See hg push for a way to actively distribute your changes.

If a list of files is omitted, all changes reported by hg status will be committed.

If you are committing the result of a merge, do not provide any filenames or -I/-X filters.

If no commit message is specified, Mercurial starts your configured editor where you can enter a message. In case your commit fails, you will find a backup of your message in .hg/last-message.txt.

The --amend flag can be used to amend the parent of the working directory with a new commit that contains the changes in the parent in addition to those currently reported by hg status, if there are any. The old commit is stored in a backup bundle in .hg/strip-backup (see hg help bundle and hg help unbundle on how to restore it).

Message, user and date are taken from the amended commit unless specified. When a message isn't specified on the command line, the editor will open with the message of the amended commit.

It is not possible to amend public changesets (see hg help phases) or changesets that have children.

See hg help dates for a list of formats valid for -d/--date.

Returns 0 on success, 1 if nothing changed.

Options:

-A, --addremove
 mark new/missing files as added/removed before committing
--close-branch mark a branch as closed, hiding it from the branch list
--amend amend the parent of the working dir
-s, --secret use the secret phase for committing
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns
-m, --message use text as commit message
-l, --logfile read commit message from file
-d, --date record the specified date as commit date
-u, --user record the specified user as committer
-S, --subrepos

recurse into subrepositories

aliases: ci

copy

hg copy [OPTION]... [SOURCE]... DEST

Mark dest as having copies of source files. If dest is a directory, copies are put in that directory. If dest is a file, the source must be a single file.

By default, this command copies the contents of files as they exist in the working directory. If invoked with -A/--after, the operation is recorded, but no copying is performed.

This command takes effect with the next commit. To undo a copy before that, see hg revert.

Returns 0 on success, 1 if errors are encountered.

Options:

-A, --after record a copy that has already occurred
-f, --force forcibly copy over an existing managed file
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns
-n, --dry-run

do not perform actions, just print output

aliases: cp

diff

hg diff [OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...

Show differences between revisions for the specified files.

Differences between files are shown using the unified diff format.

Note

diff may generate unexpected results for merges, as it will default to comparing against the working directory's first parent changeset if no revisions are specified.

When two revision arguments are given, then changes are shown between those revisions. If only one revision is specified then that revision is compared to the working directory, and, when no revisions are specified, the working directory files are compared to its parent.

Alternatively you can specify -c/--change with a revision to see the changes in that changeset relative to its first parent.

Without the -a/--text option, diff will avoid generating diffs of files it detects as binary. With -a, diff will generate a diff anyway, probably with undesirable results.

Use the -g/--git option to generate diffs in the git extended diff format. For more information, read hg help diffs.

Examples:

  • compare a file in the current working directory to its parent:

    hg diff foo.c
    
  • compare two historical versions of a directory, with rename info:

    hg diff --git -r 1.0:1.2 lib/
    
  • get change stats relative to the last change on some date:

    hg diff --stat -r "date('may 2')"
    
  • diff all newly-added files that contain a keyword:

    hg diff "set:added() and grep(GNU)"
    
  • compare a revision and its parents:

    hg diff -c 9353         # compare against first parent
    hg diff -r 9353^:9353   # same using revset syntax
    hg diff -r 9353^2:9353  # compare against the second parent
    

Returns 0 on success.

Options:

-r, --rev revision
-c, --change change made by revision
-a, --text treat all files as text
-g, --git use git extended diff format
--nodates omit dates from diff headers
-p, --show-function
 show which function each change is in
--reverse produce a diff that undoes the changes
-w, --ignore-all-space
 ignore white space when comparing lines
-b, --ignore-space-change
 ignore changes in the amount of white space
-B, --ignore-blank-lines
 ignore changes whose lines are all blank
-U, --unified number of lines of context to show
--stat output diffstat-style summary of changes
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns
-S, --subrepos recurse into subrepositories

export

hg export [OPTION]... [-o OUTFILESPEC] [-r] [REV]...

Print the changeset header and diffs for one or more revisions. If no revision is given, the parent of the working directory is used.

The information shown in the changeset header is: author, date, branch name (if non-default), changeset hash, parent(s) and commit comment.

Note

export may generate unexpected diff output for merge changesets, as it will compare the merge changeset against its first parent only.

Output may be to a file, in which case the name of the file is given using a format string. The formatting rules are as follows:

%%:literal "%" character
%H:changeset hash (40 hexadecimal digits)
%N:number of patches being generated
%R:changeset revision number
%b:basename of the exporting repository
%h:short-form changeset hash (12 hexadecimal digits)
%m:first line of the commit message (only alphanumeric characters)
%n:zero-padded sequence number, starting at 1
%r:zero-padded changeset revision number

Without the -a/--text option, export will avoid generating diffs of files it detects as binary. With -a, export will generate a diff anyway, probably with undesirable results.

Use the -g/--git option to generate diffs in the git extended diff format. See hg help diffs for more information.

With the --switch-parent option, the diff will be against the second parent. It can be useful to review a merge.

Examples:

  • use export and import to transplant a bugfix to the current branch:

    hg export -r 9353 | hg import -
    
  • export all the changesets between two revisions to a file with rename information:

    hg export --git -r 123:150 > changes.txt
    
  • split outgoing changes into a series of patches with descriptive names:

    hg export -r "outgoing()" -o "%n-%m.patch"
    

Returns 0 on success.

Options:

-o, --output print output to file with formatted name
--switch-parent
 diff against the second parent
-r, --rev revisions to export
-a, --text treat all files as text
-g, --git use git extended diff format
--nodates omit dates from diff headers

forget

hg forget [OPTION]... FILE...

Mark the specified files so they will no longer be tracked after the next commit.

This only removes files from the current branch, not from the entire project history, and it does not delete them from the working directory.

To undo a forget before the next commit, see hg add.

Examples:

  • forget newly-added binary files:

    hg forget "set:added() and binary()"
    
  • forget files that would be excluded by .hgignore:

    hg forget "set:hgignore()"
    

Returns 0 on success.

Options:

-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns

graft

hg graft [OPTION]... [-r] REV...

This command uses Mercurial's merge logic to copy individual changes from other branches without merging branches in the history graph. This is sometimes known as 'backporting' or 'cherry-picking'. By default, graft will copy user, date, and description from the source changesets.

Changesets that are ancestors of the current revision, that have already been grafted, or that are merges will be skipped.

If --log is specified, log messages will have a comment appended of the form:

(grafted from CHANGESETHASH)

If a graft merge results in conflicts, the graft process is interrupted so that the current merge can be manually resolved. Once all conflicts are addressed, the graft process can be continued with the -c/--continue option.

Note

The -c/--continue option does not reapply earlier options.

Examples:

  • copy a single change to the stable branch and edit its description:

    hg update stable
    hg graft --edit 9393
    
  • graft a range of changesets with one exception, updating dates:

    hg graft -D "2085::2093 and not 2091"
    
  • continue a graft after resolving conflicts:

    hg graft -c
    
  • show the source of a grafted changeset:

    hg log --debug -r .
    

Returns 0 on successful completion.

Options:

-r, --rev revisions to graft
-c, --continue resume interrupted graft
-e, --edit invoke editor on commit messages
--log append graft info to log message
-D, --currentdate
 record the current date as commit date
-U, --currentuser
 record the current user as committer
-d, --date record the specified date as commit date
-u, --user record the specified user as committer
-t, --tool specify merge tool
-n, --dry-run do not perform actions, just print output

grep

hg grep [OPTION]... PATTERN [FILE]...

Search revisions of files for a regular expression.

This command behaves differently than Unix grep. It only accepts Python/Perl regexps. It searches repository history, not the working directory. It always prints the revision number in which a match appears.

By default, grep only prints output for the first revision of a file in which it finds a match. To get it to print every revision that contains a change in match status ("-" for a match that becomes a non-match, or "+" for a non-match that becomes a match), use the --all flag.

Returns 0 if a match is found, 1 otherwise.

Options:

-0, --print0 end fields with NUL
--all print all revisions that match
-a, --text treat all files as text
-f, --follow follow changeset history, or file history across copies and renames
-i, --ignore-case
 ignore case when matching
-l, --files-with-matches
 print only filenames and revisions that match
-n, --line-number
 print matching line numbers
-r, --rev only search files changed within revision range
-u, --user list the author (long with -v)
-d, --date list the date (short with -q)
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns

heads

hg heads [-ct] [-r STARTREV] [REV]...

With no arguments, show all open branch heads in the repository. Branch heads are changesets that have no descendants on the same branch. They are where development generally takes place and are the usual targets for update and merge operations.

If one or more REVs are given, only open branch heads on the branches associated with the specified changesets are shown. This means that you can use hg heads . to see the heads on the currently checked-out branch.

If -c/--closed is specified, also show branch heads marked closed (see hg commit --close-branch).

If STARTREV is specified, only those heads that are descendants of STARTREV will be displayed.

If -t/--topo is specified, named branch mechanics will be ignored and only topological heads (changesets with no children) will be shown.

Returns 0 if matching heads are found, 1 if not.

Options:

-r, --rev show only heads which are descendants of STARTREV
-t, --topo show topological heads only
-a, --active show active branchheads only (DEPRECATED)
-c, --closed show normal and closed branch heads
--style display using template map file
--template display with template

help

hg help [-ec] [TOPIC]

With no arguments, print a list of commands with short help messages.

Given a topic, extension, or command name, print help for that topic.

Returns 0 if successful.

Options:

-e, --extension
 show only help for extensions
-c, --command show only help for commands
-k, --keyword show topics matching keyword

identify

hg identify [-nibtB] [-r REV] [SOURCE]

Print a summary identifying the repository state at REV using one or two parent hash identifiers, followed by a "+" if the working directory has uncommitted changes, the branch name (if not default), a list of tags, and a list of bookmarks.

When REV is not given, print a summary of the current state of the repository.

Specifying a path to a repository root or Mercurial bundle will cause lookup to operate on that repository/bundle.

Examples:

  • generate a build identifier for the working directory:

    hg id --id > build-id.dat
    
  • find the revision corresponding to a tag:

    hg id -n -r 1.3
    
  • check the most recent revision of a remote repository:

    hg id -r tip http://selenic.com/hg/
    

Returns 0 if successful.

Options:

-r, --rev identify the specified revision
-n, --num show local revision number
-i, --id show global revision id
-b, --branch show branch
-t, --tags show tags
-B, --bookmarks
 show bookmarks
-e, --ssh specify ssh command to use
--remotecmd specify hg command to run on the remote side
--insecure

do not verify server certificate (ignoring web.cacerts config)

aliases: id

import

hg import [OPTION]... PATCH...

Import a list of patches and commit them individually (unless --no-commit is specified).

Because import first applies changes to the working directory, import will abort if there are outstanding changes.

You can import a patch straight from a mail message. Even patches as attachments work (to use the body part, it must have type text/plain or text/x-patch). From and Subject headers of email message are used as default committer and commit message. All text/plain body parts before first diff are added to commit message.

If the imported patch was generated by hg export, user and description from patch override values from message headers and body. Values given on command line with -m/--message and -u/--user override these.

If --exact is specified, import will set the working directory to the parent of each patch before applying it, and will abort if the resulting changeset has a different ID than the one recorded in the patch. This may happen due to character set problems or other deficiencies in the text patch format.

Use --bypass to apply and commit patches directly to the repository, not touching the working directory. Without --exact, patches will be applied on top of the working directory parent revision.

With -s/--similarity, hg will attempt to discover renames and copies in the patch in the same way as hg addremove.

To read a patch from standard input, use "-" as the patch name. If a URL is specified, the patch will be downloaded from it. See hg help dates for a list of formats valid for -d/--date.

Examples:

  • import a traditional patch from a website and detect renames:

    hg import -s 80 http://example.com/bugfix.patch
    
  • import a changeset from an hgweb server:

    hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
    
  • import all the patches in an Unix-style mbox:

    hg import incoming-patches.mbox
    
  • attempt to exactly restore an exported changeset (not always possible):

    hg import --exact proposed-fix.patch
    

Returns 0 on success.

Options:

-p, --strip directory strip option for patch. This has the same meaning as the corresponding patch option (default: 1)
-b, --base base path (DEPRECATED)
-e, --edit invoke editor on commit messages
-f, --force skip check for outstanding uncommitted changes (DEPRECATED)
--no-commit don't commit, just update the working directory
--bypass apply patch without touching the working directory
--exact apply patch to the nodes from which it was generated
--import-branch
 use any branch information in patch (implied by --exact)
-m, --message use text as commit message
-l, --logfile read commit message from file
-d, --date record the specified date as commit date
-u, --user record the specified user as committer
-s, --similarity
 

guess renamed files by similarity (0<=s<=100)

aliases: patch

incoming

hg incoming [-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]

Show new changesets found in the specified path/URL or the default pull location. These are the changesets that would have been pulled if a pull at the time you issued this command.

For remote repository, using --bundle avoids downloading the changesets twice if the incoming is followed by a pull.

See pull for valid source format details.

Returns 0 if there are incoming changes, 1 otherwise.

Options:

-f, --force run even if remote repository is unrelated
-n, --newest-first
 show newest record first
--bundle file to store the bundles into
-r, --rev a remote changeset intended to be added
-B, --bookmarks
 compare bookmarks
-b, --branch a specific branch you would like to pull
-p, --patch show patch
-g, --git use git extended diff format
-l, --limit limit number of changes displayed
-M, --no-merges
 do not show merges
--stat output diffstat-style summary of changes
-G, --graph show the revision DAG
--style display using template map file
--template display with template
-e, --ssh specify ssh command to use
--remotecmd specify hg command to run on the remote side
--insecure do not verify server certificate (ignoring web.cacerts config)
-S, --subrepos

recurse into subrepositories

aliases: in

init

hg init [-e CMD] [--remotecmd CMD] [DEST]

Initialize a new repository in the given directory. If the given directory does not exist, it will be created.

If no directory is given, the current directory is used.

It is possible to specify an ssh:// URL as the destination. See hg help urls for more information.

Returns 0 on success.

Options:

-e, --ssh specify ssh command to use
--remotecmd specify hg command to run on the remote side
--insecure do not verify server certificate (ignoring web.cacerts config)

locate

hg locate [OPTION]... [PATTERN]...

Print files under Mercurial control in the working directory whose names match the given patterns.

By default, this command searches all directories in the working directory. To search just the current directory and its subdirectories, use "--include .".

If no patterns are given to match, this command prints the names of all files under Mercurial control in the working directory.

If you want to feed the output of this command into the "xargs" command, use the -0 option to both this command and "xargs". This will avoid the problem of "xargs" treating single filenames that contain whitespace as multiple filenames.

Returns 0 if a match is found, 1 otherwise.

Options:

-r, --rev search the repository as it is in REV
-0, --print0 end filenames with NUL, for use with xargs
-f, --fullpath print complete paths from the filesystem root
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns

log

hg log [OPTION]... [FILE]

Print the revision history of the specified files or the entire project.

If no revision range is specified, the default is tip:0 unless --follow is set, in which case the working directory parent is used as the starting revision.

File history is shown without following rename or copy history of files. Use -f/--follow with a filename to follow history across renames and copies. --follow without a filename will only show ancestors or descendants of the starting revision.

By default this command prints revision number and changeset id, tags, non-trivial parents, user, date and time, and a summary for each commit. When the -v/--verbose switch is used, the list of changed files and full commit message are shown.

Note

log -p/--patch may generate unexpected diff output for merge changesets, as it will only compare the merge changeset against its first parent. Also, only files different from BOTH parents will appear in files:.

Note

for performance reasons, log FILE may omit duplicate changes made on branches and will not show deletions. To see all changes including duplicates and deletions, use the --removed switch.

Some examples:

  • changesets with full descriptions and file lists:

    hg log -v
    
  • changesets ancestral to the working directory:

    hg log -f
    
  • last 10 commits on the current branch:

    hg log -l 10 -b .
    
  • changesets showing all modifications of a file, including removals:

    hg log --removed file.c
    
  • all changesets that touch a directory, with diffs, excluding merges:

    hg log -Mp lib/
    
  • all revision numbers that match a keyword:

    hg log -k bug --template "{rev}\n"
    
  • check if a given changeset is included is a tagged release:

    hg log -r "a21ccf and ancestor(1.9)"
    
  • find all changesets by some user in a date range:

    hg log -k alice -d "may 2008 to jul 2008"
    
  • summary of all changesets after the last tag:

    hg log -r "last(tagged())::" --template "{desc|firstline}\n"
    

See hg help dates for a list of formats valid for -d/--date.

See hg help revisions and hg help revsets for more about specifying revisions.

See hg help templates for more about pre-packaged styles and specifying custom templates.

Returns 0 on success.

Options:

-f, --follow follow changeset history, or file history across copies and renames
--follow-first only follow the first parent of merge changesets (DEPRECATED)
-d, --date show revisions matching date spec
-C, --copies show copied files
-k, --keyword do case-insensitive search for a given text
-r, --rev show the specified revision or range
--removed include revisions where files were removed
-m, --only-merges
 show only merges (DEPRECATED)
-u, --user revisions committed by user
--only-branch show only changesets within the given named branch (DEPRECATED)
-b, --branch show changesets within the given named branch
-P, --prune do not display revision or any of its ancestors
-p, --patch show patch
-g, --git use git extended diff format
-l, --limit limit number of changes displayed
-M, --no-merges
 do not show merges
--stat output diffstat-style summary of changes
-G, --graph show the revision DAG
--style display using template map file
--template display with template
-I, --include include names matching the given patterns
-X, --exclude

exclude names matching the given patterns

aliases: history

manifest

hg manifest [-r REV]

Print a list of version controlled files for the given revision. If no revision is given, the first parent of the working directory is used, or the null revision if no revision is checked out.

With -v, print file permissions, symlink and executable bits. With --debug, print file revision hashes.

If option --all is specified, the list of all files from all revisions is printed. This includes deleted and renamed files.

Returns 0 on success.

Options:

-r, --rev revision to display
--all list files from all revisions

merge

hg merge [-P] [-f] [[-r] REV]

The current working directory is updated with all changes made in the requested revision since the last common predecessor revision.

Files that changed between either parent are marked as changed for the next commit and a commit must be performed before any further updates to the repository are allowed. The next commit will have two parents.

--tool can be used to specify the merge tool used for file merges. It overrides the HGMERGE environment variable and your configuration files. See hg help merge-tools for options.

If no revision is specified, the working directory's parent is a head revision, and the current branch contains exactly one other head, the other head is merged with by default. Otherwise, an explicit revision with which to merge with must be provided.

hg resolve must be used to resolve unresolved files.

To undo an uncommitted merge, use hg update --clean . which will check out a clean copy of the original merge parent, losing all changes.

Returns 0 on success, 1 if there are unresolved files.

Options:

-f, --force force a merge including outstanding changes (DEPRECATED)
-r, --rev revision to merge
-P, --preview review revisions to merge (no merge is performed)
-t, --tool specify merge tool

outgoing

hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]

Show changesets not found in the specified destination repository or the default push location. These are the changesets that would be pushed if a push was requested.

See pull for details of valid destination formats.

Returns 0 if there are outgoing changes, 1 otherwise.

Options:

-f, --force run even when the destination is unrelated
-r, --rev a changeset intended to be included in the destination
-n, --newest-first
 show newest record first
-B, --bookmarks
 compare bookmarks
-b, --branch a specific branch you would like to push
-p, --patch show patch
-g, --git use git extended diff format
-l, --limit limit number of changes displayed
-M, --no-merges
 do not show merges
--stat output diffstat-style summary of changes
-G, --graph show the revision DAG
--style display using template map file
--template display with template
-e, --ssh specify ssh command to use
--remotecmd specify hg command to run on the remote side
--insecure do not verify server certificate (ignoring web.cacerts config)
-S, --subrepos

recurse into subrepositories

aliases: out

parents

hg parents [-r REV] [FILE]

Print the working directory's parent revisions. If a revision is given via -r/--rev, the parent of that revision will be printed. If a file argument is given, the revision in which the file was last changed (before the working directory revision or the argument to --rev if given) is printed.

Returns 0 on success.

Options:

-r, --rev show parents of the specified revision
--style display using template map file
--template display with template

paths

hg paths [NAME]

Show definition of symbolic path name NAME. If no name is given, show definition of all available names.

Option -q/--quiet suppresses all output when searching for NAME and shows only the path names when listing all definitions.

Path names are defined in the [paths] section of your configuration file and in /etc/mercurial/hgrc. If run inside a repository, .hg/hgrc is used, too.

The path names default and default-push have a special meaning. When performing a push or pull operation, they are used as fallbacks if no location is specified on the command-line. When default-push is set, it will be used for push and default will be used for pull; otherwise default is used as the fallback for both. When cloning a repository, the clone source is written as default in .hg/hgrc. Note that default and default-push apply to all inbound (e.g. hg incoming) and outbound (e.g. hg outgoing, hg email and hg bundle) operations.

See hg help urls for more information.

Returns 0 on success.

phase

hg phase [-p|-d|-s] [-f] [-r] REV...

With no argument, show the phase name of specified revisions.

With one of -p/--public, -d/--draft or -s/--secret, change the phase value of the specified revisions.

Unless -f/--force is specified, hg phase won't move changeset from a lower phase to an higher phase. Phases are ordered as follows:

public < draft < secret

Return 0 on success, 1 if no phases were changed or some could not be changed.

Options:

-p, --public set changeset phase to public
-d, --draft set changeset phase to draft
-s, --secret set changeset phase to secret
-f, --force allow to move boundary backward
-r, --rev target revision

pull

hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]

Pull changes from a remote repository to a local one.

This finds all changes from the repository at the specified path or URL and adds them to a local repository (the current one unless -R is specified). By default, this does not update the copy of the project in the working directory.

Use hg incoming if you want to see what would have been added by a pull at the time you issued this command. If you then decide to add those changes to the repository, you should use hg pull -r X where X is the last changeset listed by hg incoming.

If SOURCE is omitted, the 'default' path will be used. See hg help urls for more information.

Returns 0 on success, 1 if an update had unresolved files.

Options:

-u, --update update to new branch head if changesets were pulled
-f, --force run even when remote repository is unrelated
-r, --rev a remote changeset intended to be added
-B, --bookmark bookmark to pull
-b, --branch a specific branch you would like to pull
-e, --ssh specify ssh command to use
--remotecmd specify hg command to run on the remote side
--insecure do not verify server certificate (ignoring web.cacerts config)

push

hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]

Push changesets from the local repository to the specified destination.

This operation is symmetrical to pull: it is identical to a pull in the destination repository from the current one.

By default, push will not allow creation of new heads at the destination, since multiple heads would make it unclear which head to use. In this situation, it is recommended to pull and merge before pushing.

Use --new-branch if you want to allow push to create a new named branch that is not present at the destination. This allows you to only create a new branch without forcing other changes.

Note

Extra care should be taken with the -f/--force option, which will push all new heads on all branches, an action which will almost always cause confusion for collaborators.

If -r/--rev is used, the specified revision and all its ancestors will be pushed to the remote repository.

If -B/--bookmark is used, the specified bookmarked revision, its ancestors, and the bookmark will be pushed to the remote repository.

Please see hg help urls for important details about ssh:// URLs. If DESTINATION is omitted, a default path will be used.

Returns 0 if push was successful, 1 if nothing to push.

Options:

-f, --force force push
-r, --rev a changeset intended to be included in the destination
-B, --bookmark bookmark to push
-b, --branch a specific branch you would like to push
--new-branch allow pushing a new branch
-e, --ssh specify ssh command to use
--remotecmd specify hg command to run on the remote side
--insecure do not verify server certificate (ignoring web.cacerts config)

recover

hg recover

Recover from an interrupted commit or pull.

This command tries to fix the repository status after an interrupted operation. It should only be necessary when Mercurial suggests it.

Returns 0 if successful, 1 if nothing to recover or verify fails.

remove

hg remove [OPTION]... FILE...

Schedule the indicated files for removal from the current branch.

This command schedules the files to be removed at the next commit. To undo a remove before that, see hg revert. To undo added files, see hg forget.

-A/--after can be used to remove only files that have already been deleted, -f/--force can be used to force deletion, and -Af can be used to remove files from the next revision without deleting them from the working directory.

The following table details the behavior of remove for different file states (columns) and option combinations (rows). The file states are Added [A], Clean [C], Modified [M] and Missing [!] (as reported by hg status). The actions are Warn, Remove (from branch) and Delete (from disk):

opt/state A C M !
none W RD W R
-f R RD RD R
-A W W W R
-Af R R R R

Note that remove never deletes files in Added [A] state from the working directory, not even if option --force is specified.

Returns 0 on success, 1 if any warnings encountered.

Options:

-A, --after record delete for missing files
-f, --force remove (and delete) file even if added or modified
-I, --include include names matching the given patterns
-X, --exclude

exclude names matching the given patterns

aliases: rm

rename

hg rename [OPTION]... SOURCE... DEST

Mark dest as copies of sources; mark sources for deletion. If dest is a directory, copies are put in that directory. If dest is a file, there can only be one source.

By default, this command copies the contents of files as they exist in the working directory. If invoked with -A/--after, the operation is recorded, but no copying is performed.

This command takes effect at the next commit. To undo a rename before that, see hg revert.

Returns 0 on success, 1 if errors are encountered.

Options:

-A, --after record a rename that has already occurred
-f, --force forcibly copy over an existing managed file
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns
-n, --dry-run

do not perform actions, just print output

aliases: move mv

resolve

hg resolve [OPTION]... [FILE]...

Merges with unresolved conflicts are often the result of non-interactive merging using the internal:merge configuration setting, or a command-line merge tool like diff3. The resolve command is used to manage the files involved in a merge, after hg merge has been run, and before hg commit is run (i.e. the working directory must have two parents). See hg help merge-tools for information on configuring merge tools.

The resolve command can be used in the following ways:

  • hg resolve [--tool TOOL] FILE...: attempt to re-merge the specified files, discarding any previous merge attempts. Re-merging is not performed for files already marked as resolved. Use --all/-a to select all unresolved files. --tool can be used to specify the merge tool used for the given files. It overrides the HGMERGE environment variable and your configuration files. Previous file contents are saved with a .orig suffix.
  • hg resolve -m [FILE]: mark a file as having been resolved (e.g. after having manually fixed-up the files). The default is to mark all unresolved files.
  • hg resolve -u [FILE]...: mark a file as unresolved. The default is to mark all resolved files.
  • hg resolve -l: list files which had or still have conflicts. In the printed list, U = unresolved and R = resolved.

Note that Mercurial will not let you commit files with unresolved merge conflicts. You must use hg resolve -m ... before you can commit after a conflicting merge.

Returns 0 on success, 1 if any files fail a resolve attempt.

Options:

-a, --all select all unresolved files
-l, --list list state of files needing merge
-m, --mark mark files as resolved
-u, --unmark mark files as unresolved
-n, --no-status
 hide status prefix
-t, --tool specify merge tool
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns

revert

hg revert [OPTION]... [-r REV] [NAME]...

Note

To check out earlier revisions, you should use hg update REV. To cancel an uncommitted merge (and lose your changes), use hg update --clean ..

With no revision specified, revert the specified files or directories to the contents they had in the parent of the working directory. This restores the contents of files to an unmodified state and unschedules adds, removes, copies, and renames. If the working directory has two parents, you must explicitly specify a revision.

Using the -r/--rev or -d/--date options, revert the given files or directories to their states as of a specific revision. Because revert does not change the working directory parents, this will cause these files to appear modified. This can be helpful to "back out" some or all of an earlier change. See hg backout for a related method.

Modified files are saved with a .orig suffix before reverting. To disable these backups, use --no-backup.

See hg help dates for a list of formats valid for -d/--date.

Returns 0 on success.

Options:

-a, --all revert all changes when no arguments given
-d, --date tipmost revision matching date
-r, --rev revert to the specified revision
-C, --no-backup
 do not save backup copies of files
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns
-n, --dry-run do not perform actions, just print output

rollback

hg rollback

Please use hg commit --amend instead of rollback to correct mistakes in the last commit.

This command should be used with care. There is only one level of rollback, and there is no way to undo a rollback. It will also restore the dirstate at the time of the last transaction, losing any dirstate changes since that time. This command does not alter the working directory.

Transactions are used to encapsulate the effects of all commands that create new changesets or propagate existing changesets into a repository.

For example, the following commands are transactional, and their effects can be rolled back:

  • commit
  • import
  • pull
  • push (with this repository as the destination)
  • unbundle

To avoid permanent data loss, rollback will refuse to rollback a commit transaction if it isn't checked out. Use --force to override this protection.

This command is not intended for use on public repositories. Once changes are visible for pull by other users, rolling a transaction back locally is ineffective (someone else may already have pulled the changes). Furthermore, a race is possible with readers of the repository; for example an in-progress pull from the repository may fail if a rollback is performed.

Returns 0 on success, 1 if no rollback data is available.

Options:

-n, --dry-run do not perform actions, just print output
-f, --force ignore safety measures

root

hg root

Print the root directory of the current repository.

Returns 0 on success.

serve

hg serve [OPTION]...

Start a local HTTP repository browser and pull server. You can use this for ad-hoc sharing and browsing of repositories. It is recommended to use a real web server to serve a repository for longer periods of time.

Please note that the server does not implement access control. This means that, by default, anybody can read from the server and nobody can write to it by default. Set the web.allow_push option to * to allow everybody to push to the server. You should use a real web server if you need to authenticate users.

By default, the server logs accesses to stdout and errors to stderr. Use the -A/--accesslog and -E/--errorlog options to log to files.

To have the server choose a free port number to listen on, specify a port number of 0; in this case, the server will print the port number it uses.

Returns 0 on success.

Options:

-A, --accesslog
 name of access log file to write to
-d, --daemon run server in background
--daemon-pipefds
 used internally by daemon mode
-E, --errorlog name of error log file to write to
-p, --port port to listen on (default: 8000)
-a, --address address to listen on (default: all interfaces)
--prefix prefix path to serve from (default: server root)
-n, --name name to show in web pages (default: working directory)
--web-conf name of the hgweb config file (see "hg help hgweb")
--webdir-conf name of the hgweb config file (DEPRECATED)
--pid-file name of file to write process ID to
--stdio for remote clients
--cmdserver for remote clients
-t, --templates
 web templates to use
--style template style to use
-6, --ipv6 use IPv6 in addition to IPv4
--certificate SSL certificate file

showconfig

hg showconfig [-u] [NAME]...

With no arguments, print names and values of all config items.

With one argument of the form section.name, print just the value of that config item.

With multiple arguments, print names and values of all config items with matching section names.

With --debug, the source (filename and line number) is printed for each config item.

Returns 0 on success.

Options:

-u, --untrusted
 

show untrusted configuration options

aliases: debugconfig

status

hg status [OPTION]... [FILE]...

Show status of files in the repository. If names are given, only files that match are shown. Files that are clean or ignored or the source of a copy/move operation, are not listed unless -c/--clean, -i/--ignored, -C/--copies or -A/--all are given. Unless options described with "show only ..." are given, the options -mardu are used.

Option -q/--quiet hides untracked (unknown and ignored) files unless explicitly requested with -u/--unknown or -i/--ignored.

Note

status may appear to disagree with diff if permissions have changed or a merge has occurred. The standard diff format does not report permission changes and diff only reports changes relative to one merge parent.

If one revision is given, it is used as the base revision. If two revisions are given, the differences between them are shown. The --change option can also be used as a shortcut to list the changed files of a revision from its first parent.

The codes used to show the status of files are:

M = modified
A = added
R = removed
C = clean
! = missing (deleted by non-hg command, but still tracked)
? = not tracked
I = ignored
  = origin of the previous file listed as A (added)

Examples:

  • show changes in the working directory relative to a changeset:

    hg status --rev 9353
    
  • show all changes including copies in an existing changeset:

    hg status --copies --change 9353
    
  • get a NUL separated list of added files, suitable for xargs:

    hg status -an0
    

Returns 0 on success.

Options:

-A, --all show status of all files
-m, --modified show only modified files
-a, --added show only added files
-r, --removed show only removed files
-d, --deleted show only deleted (but tracked) files
-c, --clean show only files without changes
-u, --unknown show only unknown (not tracked) files
-i, --ignored show only ignored files
-n, --no-status
 hide status prefix
-C, --copies show source of copied files
-0, --print0 end filenames with NUL, for use with xargs
--rev show difference from revision
--change list the changed files of a revision
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns
-S, --subrepos

recurse into subrepositories

aliases: st

summary

hg summary [--remote]

This generates a brief summary of the working directory state, including parents, branch, commit status, and available updates.

With the --remote option, this will check the default paths for incoming and outgoing changes. This can be time-consuming.

Returns 0 on success.

Options:

--remote

check for push and pull

aliases: sum

tag

hg tag [-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...

Name a particular revision using <name>.

Tags are used to name particular revisions of the repository and are very useful to compare different revisions, to go back to significant earlier versions or to mark branch points as releases, etc. Changing an existing tag is normally disallowed; use -f/--force to override.

If no revision is given, the parent of the working directory is used.

To facilitate version control, distribution, and merging of tags, they are stored as a file named ".hgtags" which is managed similarly to other project files and can be hand-edited if necessary. This also means that tagging creates a new commit. The file ".hg/localtags" is used for local tags (not shared among repositories).

Tag commits are usually made at the head of a branch. If the parent of the working directory is not a branch head, hg tag aborts; use -f/--force to force the tag commit to be based on a non-head changeset.

See hg help dates for a list of formats valid for -d/--date.

Since tag names have priority over branch names during revision lookup, using an existing branch name as a tag name is discouraged.

Returns 0 on success.

Options:

-f, --force force tag
-l, --local make the tag local
-r, --rev revision to tag
--remove remove a tag
-e, --edit edit commit message
-m, --message use <text> as commit message
-d, --date record the specified date as commit date
-u, --user record the specified user as committer

tags

hg tags

This lists both regular and local tags. When the -v/--verbose switch is used, a third column "local" is printed for local tags.

Returns 0 on success.

tip

hg tip [-p] [-g]

The tip revision (usually just called the tip) is the changeset most recently added to the repository (and therefore the most recently changed head).

If you have just made a commit, that commit will be the tip. If you have just pulled changes from another repository, the tip of that repository becomes the current tip. The "tip" tag is special and cannot be renamed or assigned to a different changeset.

This command is deprecated, please use hg heads instead.

Returns 0 on success.

Options:

-p, --patch show patch
-g, --git use git extended diff format
--style display using template map file
--template display with template

unbundle

hg unbundle [-u] FILE...

Apply one or more compressed changegroup files generated by the bundle command.

Returns 0 on success, 1 if an update has unresolved files.

Options:

-u, --update update to new branch head if changesets were unbundled

update

hg update [-c] [-C] [-d DATE] [[-r] REV]

Update the repository's working directory to the specified changeset. If no changeset is specified, update to the tip of the current named branch and move the current bookmark (see hg help bookmarks).

Update sets the working directory's parent revision to the specified changeset (see hg help parents).

If the changeset is not a descendant or ancestor of the working directory's parent, the update is aborted. With the -c/--check option, the working directory is checked for uncommitted changes; if none are found, the working directory is updated to the specified changeset.

The following rules apply when the working directory contains uncommitted changes:

  1. If neither -c/--check nor -C/--clean is specified, and if the requested changeset is an ancestor or descendant of the working directory's parent, the uncommitted changes are merged into the requested changeset and the merged result is left uncommitted. If the requested changeset is not an ancestor or descendant (that is, it is on another branch), the update is aborted and the uncommitted changes are preserved.
  2. With the -c/--check option, the update is aborted and the uncommitted changes are preserved.
  3. With the -C/--clean option, uncommitted changes are discarded and the working directory is updated to the requested changeset.

To cancel an uncommitted merge (and lose your changes), use hg update --clean ..

Use null as the changeset to remove the working directory (like hg clone -U).

If you want to revert just one file to an older revision, use hg revert [-r REV] NAME.

See hg help dates for a list of formats valid for -d/--date.

Returns 0 on success, 1 if there are unresolved files.

Options:

-C, --clean discard uncommitted changes (no backup)
-c, --check update across branches if no uncommitted changes
-d, --date tipmost revision matching date
-r, --rev

revision

aliases: up checkout co

verify

hg verify

Verify the integrity of the current repository.

This will perform an extensive check of the repository's integrity, validating the hashes and checksums of each entry in the changelog, manifest, and tracked files, as well as the integrity of their crosslinks and indices.

Please see http://mercurial.selenic.com/wiki/RepositoryCorruption for more information about recovery from corruption of the repository.

Returns 0 on success, 1 if errors are encountered.

version

hg version

output version and copyright information

Date Formats

Some commands allow the user to specify a date, e.g.:

  • backout, commit, import, tag: Specify the commit date.
  • log, revert, update: Select revision(s) by date.

Many date formats are valid. Here are some examples:

  • Wed Dec 6 13:18:29 2006 (local timezone assumed)
  • Dec 6 13:18 -0600 (year assumed, time offset provided)
  • Dec 6 13:18 UTC (UTC and GMT are aliases for +0000)
  • Dec 6 (midnight)
  • 13:18 (today assumed)
  • 3:39 (3:39AM assumed)
  • 3:39pm (15:39)
  • 2006-12-06 13:18:29 (ISO 8601 format)
  • 2006-12-6 13:18
  • 2006-12-6
  • 12-6
  • 12/6
  • 12/6/6 (Dec 6 2006)
  • today (midnight)
  • yesterday (midnight)
  • now - right now

Lastly, there is Mercurial's internal format:

  • 1165411109 0 (Wed Dec 6 13:18:29 2006 UTC)

This is the internal representation format for dates. The first number is the number of seconds since the epoch (1970-01-01 00:00 UTC). The second is the offset of the local timezone, in seconds west of UTC (negative if the timezone is east of UTC).

The log command also accepts date ranges:

  • <DATE - at or before a given date/time
  • >DATE - on or after a given date/time
  • DATE to DATE - a date range, inclusive
  • -DAYS - within a given number of days of today

Diff Formats

Mercurial's default format for showing changes between two versions of a file is compatible with the unified format of GNU diff, which can be used by GNU patch and many other standard tools.

While this standard format is often enough, it does not encode the following information:

  • executable status and other permission bits
  • copy or rename information
  • changes in binary files
  • creation or deletion of empty files

Mercurial also supports the extended diff format from the git VCS which addresses these limitations. The git diff format is not produced by default because a few widespread tools still do not understand this format.

This means that when generating diffs from a Mercurial repository (e.g. with hg export), you should be careful about things like file copies and renames or other things mentioned above, because when applying a standard diff to a different repository, this extra information is lost. Mercurial's internal operations (like push and pull) are not affected by this, because they use an internal binary format for communicating changes.

To make Mercurial produce the git extended diff format, use the --git option available for many commands, or set 'git = True' in the [diff] section of your configuration file. You do not need to set this option when importing diffs in this format or using them in the mq extension.

Environment Variables

HG
Path to the 'hg' executable, automatically passed when running hooks, extensions or external tools. If unset or empty, this is the hg executable's name if it's frozen, or an executable named 'hg' (with %PATHEXT% [defaulting to COM/EXE/BAT/CMD] extensions on Windows) is searched.
HGEDITOR

This is the name of the editor to run when committing. See EDITOR.

(deprecated, use configuration file)

HGENCODING
This overrides the default locale setting detected by Mercurial. This setting is used to convert data including usernames, changeset descriptions, tag names, and branches. This setting can be overridden with the --encoding command-line option.
HGENCODINGMODE
This sets Mercurial's behavior for handling unknown characters while transcoding user input. The default is "strict", which causes Mercurial to abort if it can't map a character. Other settings include "replace", which replaces unknown characters, and "ignore", which drops them. This setting can be overridden with the --encodingmode command-line option.
HGENCODINGAMBIGUOUS
This sets Mercurial's behavior for handling characters with "ambiguous" widths like accented Latin characters with East Asian fonts. By default, Mercurial assumes ambiguous characters are narrow, set this variable to "wide" if such characters cause formatting problems.
HGMERGE

An executable to use for resolving merge conflicts. The program will be executed with three arguments: local file, remote file, ancestor file.

(deprecated, use configuration file)

HGRCPATH

A list of files or directories to search for configuration files. Item separator is ":" on Unix, ";" on Windows. If HGRCPATH is not set, platform default search path is used. If empty, only the .hg/hgrc from the current repository is read.

For each element in HGRCPATH:

  • if it's a directory, all files ending with .rc are added
  • otherwise, the file itself will be added
HGPLAIN

When set, this disables any configuration settings that might change Mercurial's default output. This includes encoding, defaults, verbose mode, debug mode, quiet mode, tracebacks, and localization. This can be useful when scripting against Mercurial in the face of existing user configuration.

Equivalent options set via command line flags or environment variables are not overridden.

HGPLAINEXCEPT

This is a comma-separated list of features to preserve when HGPLAIN is enabled. Currently the only value supported is "i18n", which preserves internationalization in plain mode.

Setting HGPLAINEXCEPT to anything (even an empty string) will enable plain mode.

HGUSER

This is the string used as the author of a commit. If not set, available values will be considered in this order:

  • HGUSER (deprecated)
  • configuration files from the HGRCPATH
  • EMAIL
  • interactive prompt
  • LOGNAME (with @hostname appended)

(deprecated, use configuration file)

EMAIL
May be used as the author of a commit; see HGUSER.
LOGNAME
May be used as the author of a commit; see HGUSER.
VISUAL
This is the name of the editor to use when committing. See EDITOR.
EDITOR
Sometimes Mercurial needs to open a text file in an editor for a user to modify, for example when writing commit messages. The editor it uses is determined by looking at the environment variables HGEDITOR, VISUAL and EDITOR, in that order. The first non-empty one is chosen. If all of them are empty, the editor defaults to 'vi'.
PYTHONPATH
This is used by Python to find imported modules and may need to be set appropriately if this Mercurial is not installed system-wide.

Using Additional Features

Mercurial has the ability to add new features through the use of extensions. Extensions may add new commands, add options to existing commands, change the default behavior of commands, or implement hooks.

To enable the "foo" extension, either shipped with Mercurial or in the Python search path, create an entry for it in your configuration file, like this:

[extensions]
foo =

You may also specify the full path to an extension:

[extensions]
myfeature = ~/.hgext/myfeature.py

See hg help config for more information on configuration files.

Extensions are not loaded by default for a variety of reasons: they can increase startup overhead; they may be meant for advanced usage only; they may provide potentially dangerous abilities (such as letting you destroy or modify history); they might not be ready for prime time; or they may alter some usual behaviors of stock Mercurial. It is thus up to the user to activate extensions as needed.

To explicitly disable an extension enabled in a configuration file of broader scope, prepend its path with !:

[extensions]
# disabling extension bar residing in /path/to/extension/bar.py
bar = !/path/to/extension/bar.py
# ditto, but no path was supplied for extension baz
baz = !

disabled extensions:

acl:hooks for controlling repository access
blackbox:log repository events to a blackbox for debugging
bugzilla:hooks for integrating with the Bugzilla bug tracker
children:command to display child changesets (DEPRECATED)
churn:command to display statistics about repository history
color:colorize output from some commands
convert:import revisions from foreign VCS repositories into Mercurial
eol:automatically manage newlines in repository files
extdiff:command to allow external programs to compare revisions
factotum:http authentication with factotum
fetch:pull, update and merge in one command (DEPRECATED)
gpg:commands to sign and verify changesets
graphlog:command to view revision graphs from a shell
hgcia:hooks for integrating with the CIA.vc notification service
hgk:browse the repository in a graphical way
highlight:syntax highlighting for hgweb (requires Pygments)
histedit:interactive history editing
inotify:accelerate status report using Linux's inotify service
interhg:expand expressions into changelog and summaries
keyword:expand keywords in tracked files
largefiles:track large binary files
mq:manage a stack of patches
notify:hooks for sending email push notifications
pager:browse command output with an external pager
patchbomb:command to send changesets as (a series of) patch emails
progress:show progress bars for some actions
purge:command to delete untracked files from the working directory
rebase:command to move sets of revisions to a different ancestor
record:commands to interactively select changes for commit/qrefresh
relink:recreates hardlinks between repository clones
schemes:extend schemes with shortcuts to repository swarms
share:share a common history between several working directories
shelve:save and restore changes to the working directory
strip:strip changesets and their descendents from history
transplant:command to transplant changesets from another branch
win32mbcs:allow the use of MBCS paths with problematic encodings
win32text:perform automatic newline conversion
zeroconf:discover and advertise repositories on the local network

Specifying File Sets

Mercurial supports a functional language for selecting a set of files.

Like other file patterns, this pattern type is indicated by a prefix, 'set:'. The language supports a number of predicates which are joined by infix operators. Parenthesis can be used for grouping.

Identifiers such as filenames or patterns must be quoted with single or double quotes if they contain characters outside of [.*{}[]?/\_a-zA-Z0-9\x80-\xff] or if they match one of the predefined predicates. This generally applies to file patterns other than globs and arguments for predicates.

Special characters can be used in quoted identifiers by escaping them, e.g., \n is interpreted as a newline. To prevent them from being interpreted, strings can be prefixed with r, e.g. r'...'.

There is a single prefix operator:

not x
Files not in x. Short form is ! x.

These are the supported infix operators:

x and y
The intersection of files in x and y. Short form is x & y.
x or y
The union of files in x and y. There are two alternative short forms: x | y and x + y.
x - y
Files in x but not in y.

The following predicates are supported:

added()
File that is added according to status.
binary()
File that appears to be binary (contains NUL bytes).
clean()
File that is clean according to status.
copied()
File that is recorded as being copied.
deleted()
File that is deleted according to status.
encoding(name)
File can be successfully decoded with the given character encoding. May not be useful for encodings other than ASCII and UTF-8.
eol(style)
File contains newlines of the given style (dos, unix, mac). Binary files are excluded, files with mixed line endings match multiple styles.
exec()
File that is marked as executable.
grep(regex)
File contains the given regular expression.
hgignore()
File that matches the active .hgignore pattern.
ignored()
File that is ignored according to status. These files will only be considered if this predicate is used.
modified()
File that is modified according to status.
removed()
File that is removed according to status.
resolved()
File that is marked resolved according to the resolve state.
size(expression)

File size matches the given expression. Examples:

  • 1k (files from 1024 to 2047 bytes)
  • < 20k (files less than 20480 bytes)
  • >= .5MB (files at least 524288 bytes)
  • 4k - 1MB (files from 4096 bytes to 1048576 bytes)
subrepo([pattern])
Subrepositories whose paths match the given pattern.
symlink()
File that is marked as a symlink.
unknown()
File that is unknown according to status. These files will only be considered if this predicate is used.
unresolved()
File that is marked unresolved according to the resolve state.

Some sample queries:

  • Show status of files that appear to be binary in the working directory:

    hg status -A "set:binary()"
    
  • Forget files that are in .hgignore but are already tracked:

    hg forget "set:hgignore() and not ignored()"
    
  • Find text files that contain a string:

    hg locate "set:grep(magic) and not binary()"
    
  • Find C files in a non-standard encoding:

    hg locate "set:**.c and not encoding('UTF-8')"
    
  • Revert copies of large binary files:

    hg revert "set:copied() and binary() and size('>1M')"
    
  • Remove files listed in foo.lst that contain the letter a or b:

    hg remove "set: 'listfile:foo.lst' and (**a* or **b*)"
    

See also hg help patterns.

Glossary

Ancestor
Any changeset that can be reached by an unbroken chain of parent changesets from a given changeset. More precisely, the ancestors of a changeset can be defined by two properties: a parent of a changeset is an ancestor, and a parent of an ancestor is an ancestor. See also: 'Descendant'.
Bookmark

Bookmarks are pointers to certain commits that move when committing. They are similar to tags in that it is possible to use bookmark names in all places where Mercurial expects a changeset ID, e.g., with hg update. Unlike tags, bookmarks move along when you make a commit.

Bookmarks can be renamed, copied and deleted. Bookmarks are local, unless they are explicitly pushed or pulled between repositories. Pushing and pulling bookmarks allow you to collaborate with others on a branch without creating a named branch.

Branch

(Noun) A child changeset that has been created from a parent that is not a head. These are known as topological branches, see 'Branch, topological'. If a topological branch is named, it becomes a named branch. If a topological branch is not named, it becomes an anonymous branch. See 'Branch, anonymous' and 'Branch, named'.

Branches may be created when changes are pulled from or pushed to a remote repository, since new heads may be created by these operations. Note that the term branch can also be used informally to describe a development process in which certain development is done independently of other development. This is sometimes done explicitly with a named branch, but it can also be done locally, using bookmarks or clones and anonymous branches.

Example: "The experimental branch".

(Verb) The action of creating a child changeset which results in its parent having more than one child.

Example: "I'm going to branch at X".

Branch, anonymous
Every time a new child changeset is created from a parent that is not a head and the name of the branch is not changed, a new anonymous branch is created.
Branch, closed
A named branch whose branch heads have all been closed.
Branch, default
The branch assigned to a changeset when no name has previously been assigned.
Branch head
See 'Head, branch'.
Branch, inactive

If a named branch has no topological heads, it is considered to be inactive. As an example, a feature branch becomes inactive when it is merged into the default branch. The hg branches command shows inactive branches by default, though they can be hidden with hg branches --active.

NOTE: this concept is deprecated because it is too implicit. Branches should now be explicitly closed using hg commit --close-branch when they are no longer needed.

Branch, named

A collection of changesets which have the same branch name. By default, children of a changeset in a named branch belong to the same named branch. A child can be explicitly assigned to a different branch. See hg help branch, hg help branches and hg commit --close-branch for more information on managing branches.

Named branches can be thought of as a kind of namespace, dividing the collection of changesets that comprise the repository into a collection of disjoint subsets. A named branch is not necessarily a topological branch. If a new named branch is created from the head of another named branch, or the default branch, but no further changesets are added to that previous branch, then that previous branch will be a branch in name only.

Branch tip
See 'Tip, branch'.
Branch, topological
Every time a new child changeset is created from a parent that is not a head, a new topological branch is created. If a topological branch is named, it becomes a named branch. If a topological branch is not named, it becomes an anonymous branch of the current, possibly default, branch.
Changelog
A record of the changesets in the order in which they were added to the repository. This includes details such as changeset id, author, commit message, date, and list of changed files.
Changeset
A snapshot of the state of the repository used to record a change.
Changeset, child
The converse of parent changeset: if P is a parent of C, then C is a child of P. There is no limit to the number of children that a changeset may have.
Changeset id
A SHA-1 hash that uniquely identifies a changeset. It may be represented as either a "long" 40 hexadecimal digit string, or a "short" 12 hexadecimal digit string.
Changeset, merge
A changeset with two parents. This occurs when a merge is committed.
Changeset, parent
A revision upon which a child changeset is based. Specifically, a parent changeset of a changeset C is a changeset whose node immediately precedes C in the DAG. Changesets have at most two parents.
Checkout

(Noun) The working directory being updated to a specific revision. This use should probably be avoided where possible, as changeset is much more appropriate than checkout in this context.

Example: "I'm using checkout X."

(Verb) Updating the working directory to a specific changeset. See hg help update.

Example: "I'm going to check out changeset X."

Child changeset
See 'Changeset, child'.
Close changeset
See 'Head, closed branch'
Closed branch
See 'Branch, closed'.
Clone

(Noun) An entire or partial copy of a repository. The partial clone must be in the form of a revision and its ancestors.

Example: "Is your clone up to date?".

(Verb) The process of creating a clone, using hg clone.

Example: "I'm going to clone the repository".

Closed branch head
See 'Head, closed branch'.
Commit

(Noun) A synonym for changeset.

Example: "Is the bug fixed in your recent commit?"

(Verb) The act of recording changes to a repository. When files are committed in a working directory, Mercurial finds the differences between the committed files and their parent changeset, creating a new changeset in the repository.

Example: "You should commit those changes now."

Cset
A common abbreviation of the term changeset.
DAG
The repository of changesets of a distributed version control system (DVCS) can be described as a directed acyclic graph (DAG), consisting of nodes and edges, where nodes correspond to changesets and edges imply a parent -> child relation. This graph can be visualized by graphical tools such as hg log --graph. In Mercurial, the DAG is limited by the requirement for children to have at most two parents.
Default branch
See 'Branch, default'.
Descendant
Any changeset that can be reached by a chain of child changesets from a given changeset. More precisely, the descendants of a changeset can be defined by two properties: the child of a changeset is a descendant, and the child of a descendant is a descendant. See also: 'Ancestor'.
Diff

(Noun) The difference between the contents and attributes of files in two changesets or a changeset and the current working directory. The difference is usually represented in a standard form called a "diff" or "patch". The "git diff" format is used when the changes include copies, renames, or changes to file attributes, none of which can be represented/handled by classic "diff" and "patch".

Example: "Did you see my correction in the diff?"

(Verb) Diffing two changesets is the action of creating a diff or patch.

Example: "If you diff with changeset X, you will see what I mean."

Directory, working
The working directory represents the state of the files tracked by Mercurial, that will be recorded in the next commit. The working directory initially corresponds to the snapshot at an existing changeset, known as the parent of the working directory. See 'Parent, working directory'. The state may be modified by changes to the files introduced manually or by a merge. The repository metadata exists in the .hg directory inside the working directory.
Draft
Changesets in the draft phase have not been shared with publishing repositories and may thus be safely changed by history-modifying extensions. See hg help phases.
Graph
See DAG and hg log --graph.
Head

The term 'head' may be used to refer to both a branch head or a repository head, depending on the context. See 'Head, branch' and 'Head, repository' for specific definitions.

Heads are where development generally takes place and are the usual targets for update and merge operations.

Head, branch
A changeset with no descendants on the same named branch.
Head, closed branch

A changeset that marks a head as no longer interesting. The closed head is no longer listed by hg heads. A branch is considered closed when all its heads are closed and consequently is not listed by hg branches.

Closed heads can be re-opened by committing new changeset as the child of the changeset that marks a head as closed.

Head, repository
A topological head which has not been closed.
Head, topological
A changeset with no children in the repository.
History, immutable
Once committed, changesets cannot be altered. Extensions which appear to change history actually create new changesets that replace existing ones, and then destroy the old changesets. Doing so in public repositories can result in old changesets being reintroduced to the repository.
History, rewriting
The changesets in a repository are immutable. However, extensions to Mercurial can be used to alter the repository, usually in such a way as to preserve changeset contents.
Immutable history
See 'History, immutable'.
Merge changeset
See 'Changeset, merge'.
Manifest
Each changeset has a manifest, which is the list of files that are tracked by the changeset.
Merge
Used to bring together divergent branches of work. When you update to a changeset and then merge another changeset, you bring the history of the latter changeset into your working directory. Once conflicts are resolved (and marked), this merge may be committed as a merge changeset, bringing two branches together in the DAG.
Named branch
See 'Branch, named'.
Null changeset
The empty changeset. It is the parent state of newly-initialized repositories and repositories with no checked out revision. It is thus the parent of root changesets and the effective ancestor when merging unrelated changesets. Can be specified by the alias 'null' or by the changeset ID '000000000000'.
Parent
See 'Changeset, parent'.
Parent changeset
See 'Changeset, parent'.
Parent, working directory
The working directory parent reflects a virtual revision which is the child of the changeset (or two changesets with an uncommitted merge) shown by hg parents. This is changed with hg update. Other commands to see the working directory parent are hg summary and hg id. Can be specified by the alias ".".
Patch

(Noun) The product of a diff operation.

Example: "I've sent you my patch."

(Verb) The process of using a patch file to transform one changeset into another.

Example: "You will need to patch that revision."

Phase
A per-changeset state tracking how the changeset has been or should be shared. See hg help phases.
Public
Changesets in the public phase have been shared with publishing repositories and are therefore considered immutable. See hg help phases.
Pull
An operation in which changesets in a remote repository which are not in the local repository are brought into the local repository. Note that this operation without special arguments only updates the repository, it does not update the files in the working directory. See hg help pull.
Push
An operation in which changesets in a local repository which are not in a remote repository are sent to the remote repository. Note that this operation only adds changesets which have been committed locally to the remote repository. Uncommitted changes are not sent. See hg help push.
Repository
The metadata describing all recorded states of a collection of files. Each recorded state is represented by a changeset. A repository is usually (but not always) found in the .hg subdirectory of a working directory. Any recorded state can be recreated by "updating" a working directory to a specific changeset.
Repository head
See 'Head, repository'.
Revision
A state of the repository at some point in time. Earlier revisions can be updated to by using hg update. See also 'Revision number'; See also 'Changeset'.
Revision number
This integer uniquely identifies a changeset in a specific repository. It represents the order in which changesets were added to a repository, starting with revision number 0. Note that the revision number may be different in each clone of a repository. To identify changesets uniquely between different clones, see 'Changeset id'.
Revlog
History storage mechanism used by Mercurial. It is a form of delta encoding, with occasional full revision of data followed by delta of each successive revision. It includes data and an index pointing to the data.
Rewriting history
See 'History, rewriting'.
Root
A changeset that has only the null changeset as its parent. Most repositories have only a single root changeset.
Secret
Changesets in the secret phase may not be shared via push, pull, or clone. See hg help phases.
Tag
An alternative name given to a changeset. Tags can be used in all places where Mercurial expects a changeset ID, e.g., with hg update. The creation of a tag is stored in the history and will thus automatically be shared with other using push and pull.
Tip
The changeset with the highest revision number. It is the changeset most recently added in a repository.
Tip, branch
The head of a given branch with the highest revision number. When a branch name is used as a revision identifier, it refers to the branch tip. See also 'Branch, head'. Note that because revision numbers may be different in different repository clones, the branch tip may be different in different cloned repositories.
Update

(Noun) Another synonym of changeset.

Example: "I've pushed an update".

(Verb) This term is usually used to describe updating the state of the working directory to that of a specific changeset. See hg help update.

Example: "You should update".

Working directory
See 'Directory, working'.
Working directory parent
See 'Parent, working directory'.

Syntax for Mercurial Ignore Files

Synopsis

The Mercurial system uses a file called .hgignore in the root directory of a repository to control its behavior when it searches for files that it is not currently tracking.

Description

The working directory of a Mercurial repository will often contain files that should not be tracked by Mercurial. These include backup files created by editors and build products created by compilers. These files can be ignored by listing them in a .hgignore file in the root of the working directory. The .hgignore file must be created manually. It is typically put under version control, so that the settings will propagate to other repositories with push and pull.

An untracked file is ignored if its path relative to the repository root directory, or any prefix path of that path, is matched against any pattern in .hgignore.

For example, say we have an untracked file, file.c, at a/b/file.c inside our repository. Mercurial will ignore file.c if any pattern in .hgignore matches a/b/file.c, a/b or a.

In addition, a Mercurial configuration file can reference a set of per-user or global ignore files. See the ignore configuration key on the [ui] section of hg help config for details of how to configure these files.

To control Mercurial's handling of files that it manages, many commands support the -I and -X options; see hg help <command> and hg help patterns for details.

Files that are already tracked are not affected by .hgignore, even if they appear in .hgignore. An untracked file X can be explicitly added with hg add X, even if X would be excluded by a pattern in .hgignore.

Syntax

An ignore file is a plain text file consisting of a list of patterns, with one pattern per line. Empty lines are skipped. The # character is treated as a comment character, and the \ character is treated as an escape character.

Mercurial supports several pattern syntaxes. The default syntax used is Python/Perl-style regular expressions.

To change the syntax used, use a line of the following form:

syntax: NAME

where NAME is one of the following:

regexp
Regular expression, Python/Perl syntax.
glob
Shell-style glob.

The chosen syntax stays in effect when parsing all patterns that follow, until another syntax is selected.

Neither glob nor regexp patterns are rooted. A glob-syntax pattern of the form *.c will match a file ending in .c in any directory, and a regexp pattern of the form \.c$ will do the same. To root a regexp pattern, start it with ^.

Note

Patterns specified in other than .hgignore are always rooted. Please see hg help patterns for details.

Example

Here is an example ignore file.

# use glob syntax.
syntax: glob

*.elc
*.pyc
*~

# switch to regexp syntax.
syntax: regexp
^\.pc/

Configuring hgweb

Mercurial's internal web server, hgweb, can serve either a single repository, or a tree of repositories. In the second case, repository paths and global options can be defined using a dedicated configuration file common to hg serve, hgweb.wsgi, hgweb.cgi and hgweb.fcgi.

This file uses the same syntax as other Mercurial configuration files but recognizes only the following sections:

  • web
  • paths
  • collections

The web options are thoroughly described in hg help config.

The paths section maps URL paths to paths of repositories in the filesystem. hgweb will not expose the filesystem directly - only Mercurial repositories can be published and only according to the configuration.

The left hand side is the path in the URL. Note that hgweb reserves subpaths like rev or file, try using different names for nested repositories to avoid confusing effects.

The right hand side is the path in the filesystem. If the specified path ends with * or ** the filesystem will be searched recursively for repositories below that point. With * it will not recurse into the repositories it finds (except for .hg/patches). With ** it will also search inside repository working directories and possibly find subrepositories.

In this example:

[paths]
/projects/a = /srv/tmprepos/a
/projects/b = c:/repos/b
/ = /srv/repos/*
/user/bob = /home/bob/repos/**
  • The first two entries make two repositories in different directories appear under the same directory in the web interface
  • The third entry will publish every Mercurial repository found in /srv/repos/, for instance the repository /srv/repos/quux/ will appear as http://server/quux/
  • The fourth entry will publish both http://server/user/bob/quux/ and http://server/user/bob/quux/testsubrepo/

The collections section is deprecated and has been superseded by paths.

Merge Tools

To merge files Mercurial uses merge tools.

A merge tool combines two different versions of a file into a merged file. Merge tools are given the two files and the greatest common ancestor of the two file versions, so they can determine the changes made on both branches.

Merge tools are used both for hg resolve, hg merge, hg update, hg backout and in several extensions.

Usually, the merge tool tries to automatically reconcile the files by combining all non-overlapping changes that occurred separately in the two different evolutions of the same initial base file. Furthermore, some interactive merge programs make it easier to manually resolve conflicting merges, either in a graphical way, or by inserting some conflict markers. Mercurial does not include any interactive merge programs but relies on external tools for that.

Available merge tools

External merge tools and their properties are configured in the merge-tools configuration section - see hgrc(5) - but they can often just be named by their executable.

A merge tool is generally usable if its executable can be found on the system and if it can handle the merge. The executable is found if it is an absolute or relative executable path or the name of an application in the executable search path. The tool is assumed to be able to handle the merge if it can handle symlinks if the file is a symlink, if it can handle binary files if the file is binary, and if a GUI is available if the tool requires a GUI.

There are some internal merge tools which can be used. The internal merge tools are:

internal:dump
Creates three versions of the files to merge, containing the contents of local, other and base. These files can then be used to perform a merge manually. If the file to be merged is named a.txt, these files will accordingly be named a.txt.local, a.txt.other and a.txt.base and they will be placed in the same directory as a.txt.
internal:fail
Rather than attempting to merge files that were modified on both branches, it marks them as unresolved. The resolve command must be used to resolve these conflicts.
internal:local
Uses the local version of files as the merged version.
internal:merge
Uses the internal non-interactive simple merge algorithm for merging files. It will fail if there are any conflicts and leave markers in the partially merged file.
internal:other
Uses the other version of files as the merged version.
internal:prompt
Asks the user which of the local or the other version to keep as the merged version.

Internal tools are always available and do not require a GUI but will by default not handle symlinks or binary files.

Choosing a merge tool

Mercurial uses these rules when deciding which merge tool to use:

  1. If a tool has been specified with the --tool option to merge or resolve, it is used. If it is the name of a tool in the merge-tools configuration, its configuration is used. Otherwise the specified tool must be executable by the shell.
  2. If the HGMERGE environment variable is present, its value is used and must be executable by the shell.
  3. If the filename of the file to be merged matches any of the patterns in the merge-patterns configuration section, the first usable merge tool corresponding to a matching pattern is used. Here, binary capabilities of the merge tool are not considered.
  4. If ui.merge is set it will be considered next. If the value is not the name of a configured tool, the specified value is used and must be executable by the shell. Otherwise the named tool is used if it is usable.
  5. If any usable merge tools are present in the merge-tools configuration section, the one with the highest priority is used.
  6. If a program named hgmerge can be found on the system, it is used - but it will by default not be used for symlinks and binary files.
  7. If the file to be merged is not binary and is not a symlink, then internal:merge is used.
  8. The merge of the file fails and must be resolved before commit.

Note

After selecting a merge program, Mercurial will by default attempt to merge the files using a simple merge algorithm first. Only if it doesn't succeed because of conflicting changes Mercurial will actually execute the merge program. Whether to use the simple merge algorithm first can be controlled by the premerge setting of the merge tool. Premerge is enabled by default unless the file is binary or a symlink.

See the merge-tools and ui sections of hgrc(5) for details on the configuration of merge tools.

Specifying Multiple Revisions

When Mercurial accepts more than one revision, they may be specified individually, or provided as a topologically continuous range, separated by the ":" character.

The syntax of range notation is [BEGIN]:[END], where BEGIN and END are revision identifiers. Both BEGIN and END are optional. If BEGIN is not specified, it defaults to revision number 0. If END is not specified, it defaults to the tip. The range ":" thus means "all revisions".

If BEGIN is greater than END, revisions are treated in reverse order.

A range acts as a closed interval. This means that a range of 3:5 gives 3, 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.

File Name Patterns

Mercurial accepts several notations for identifying one or more files at a time.

By default, Mercurial treats filenames as shell-style extended glob patterns.

Alternate pattern notations must be specified explicitly.

Note

Patterns specified in .hgignore are not rooted. Please see hg help hgignore for details.

To use a plain path name without any pattern matching, start it with path:. These path names must completely match starting at the current repository root.

To use an extended glob, start a name with glob:. Globs are rooted at the current directory; a glob such as *.c will only match files in the current directory ending with .c.

The supported glob syntax extensions are ** to match any string across path separators and {a,b} to mean "a or b".

To use a Perl/Python regular expression, start a name with re:. Regexp pattern matching is anchored at the root of the repository.

To read name patterns from a file, use listfile: or listfile0:. The latter expects null delimited patterns while the former expects line feeds. Each string read from the file is itself treated as a file pattern.

Plain examples:

path:foo/bar   a name bar in a directory named foo in the root
               of the repository
path:path:name a file or directory named "path:name"

Glob examples:

glob:*.c       any name ending in ".c" in the current directory
*.c            any name ending in ".c" in the current directory
**.c           any name ending in ".c" in any subdirectory of the
               current directory including itself.
foo/*.c        any name ending in ".c" in the directory foo
foo/**.c       any name ending in ".c" in any subdirectory of foo
               including itself.

Regexp examples:

re:.*\.c$      any name ending in ".c", anywhere in the repository

File examples:

listfile:list.txt  read list from list.txt with one file pattern per line
listfile0:list.txt read list from list.txt with null byte delimiters

See also hg help filesets.

Working with Phases

What are phases?

Phases are a system for tracking which changesets have been or should be shared. This helps prevent common mistakes when modifying history (for instance, with the mq or rebase extensions).

Each changeset in a repository is in one of the following phases:

  • public : changeset is visible on a public server
  • draft : changeset is not yet published
  • secret : changeset should not be pushed, pulled, or cloned

These phases are ordered (public < draft < secret) and no changeset can be in a lower phase than its ancestors. For instance, if a changeset is public, all its ancestors are also public. Lastly, changeset phases should only be changed towards the public phase.

How are phases managed?

For the most part, phases should work transparently. By default, a changeset is created in the draft phase and is moved into the public phase when it is pushed to another repository.

Once changesets become public, extensions like mq and rebase will refuse to operate on them to prevent creating duplicate changesets. Phases can also be manually manipulated with the hg phase command if needed. See hg help -v phase for examples.

Phases and servers

Normally, all servers are publishing by default. This means:

- all draft changesets that are pulled or cloned appear in phase
public on the client

- all draft changesets that are pushed appear as public on both
client and server

- secret changesets are neither pushed, pulled, or cloned

Note

Pulling a draft changeset from a publishing server does not mark it as public on the server side due to the read-only nature of pull.

Sometimes it may be desirable to push and pull changesets in the draft phase to share unfinished work. This can be done by setting a repository to disable publishing in its configuration file:

[phases]
publish = False

See hg help config for more information on configuration files.

Note

Servers running older versions of Mercurial are treated as publishing.

Examples

  • list changesets in draft or secret phase:

    hg log -r "not public()"
    
  • change all secret changesets to draft:

    hg phase --draft "secret()"
    
  • forcibly move the current changeset and descendants from public to draft:

    hg phase --force --draft .
    
  • show a list of changeset revision and phase:

    hg log --template "{rev} {phase}\n"
    
  • resynchronize draft changesets relative to a remote repository:

    hg phase -fd "outgoing(URL)"
    

See hg help phase for more information on manually manipulating phases.

Specifying Single Revisions

Mercurial supports several ways to specify individual revisions.

A plain integer is treated as a revision number. Negative integers are treated as sequential offsets from the tip, with -1 denoting the tip, -2 denoting the revision prior to the tip, and so forth.

A 40-digit hexadecimal string is treated as a unique revision identifier.

A hexadecimal string less than 40 characters long is treated as a unique revision identifier and is referred to as a short-form identifier. A short-form identifier is only valid if it is the prefix of exactly one full-length identifier.

Any other string is treated as a bookmark, tag, or branch name. A bookmark is a movable pointer to a revision. A tag is a permanent name associated with a revision. A branch name denotes the tipmost revision of that branch. Bookmark, tag, and branch names must not contain the ":" character.

The reserved name "tip" always identifies the most recent revision.

The reserved name "null" indicates the null revision. This is the revision of an empty repository, and the parent of revision 0.

The reserved name "." indicates the working directory parent. If no working directory is checked out, it is equivalent to null. If an uncommitted merge is in progress, "." is the revision of the first parent.

Specifying Revision Sets

Mercurial supports a functional language for selecting a set of revisions.

The language supports a number of predicates which are joined by infix operators. Parenthesis can be used for grouping.

Identifiers such as branch names may need quoting with single or double quotes if they contain characters like - or if they match one of the predefined predicates.

Special characters can be used in quoted identifiers by escaping them, e.g., \n is interpreted as a newline. To prevent them from being interpreted, strings can be prefixed with r, e.g. r'...'.

There is a single prefix operator:

not x
Changesets not in x. Short form is ! x.

These are the supported infix operators:

x::y

A DAG range, meaning all changesets that are descendants of x and ancestors of y, including x and y themselves. If the first endpoint is left out, this is equivalent to ancestors(y), if the second is left out it is equivalent to descendants(x).

An alternative syntax is x..y.

x:y
All changesets with revision numbers between x and y, both inclusive. Either endpoint can be left out, they default to 0 and tip.
x and y
The intersection of changesets in x and y. Short form is x & y.
x or y
The union of changesets in x and y. There are two alternative short forms: x | y and x + y.
x - y
Changesets in x but not in y.
x^n
The nth parent of x, n == 0, 1, or 2. For n == 0, x; for n == 1, the first parent of each changeset in x; for n == 2, the second parent of changeset in x.
x~n
The nth first ancestor of x; x~0 is x; x~3 is x^^^.

There is a single postfix operator:

x^
Equivalent to x^1, the first parent of each changeset in x.

The following predicates are supported:

adds(pattern)
Changesets that add a file matching pattern.
all()
All changesets, the same as 0:tip.
ancestor(*changeset)

Greatest common ancestor of the changesets.

Accepts 0 or more changesets. Will return empty list when passed no args. Greatest common ancestor of a single changeset is that changeset.

ancestors(set)
Changesets that are ancestors of a changeset in set.
author(string)
Alias for user(string).
bisect(string)

Changesets marked in the specified bisect status:

  • good, bad, skip: csets explicitly marked as good/bad/skip
  • goods, bads : csets topologically good/bad
  • range : csets taking part in the bisection
  • pruned : csets that are goods, bads or skipped
  • untested : csets whose fate is yet unknown
  • ignored : csets ignored due to DAG topology
  • current : the cset currently being bisected
bookmark([name])

The named bookmark or all bookmarks.

If name starts with re:, the remainder of the name is treated as a regular expression. To match a bookmark that actually starts with re:, use the prefix literal:.

branch(string or set)

All changesets belonging to the given branch or the branches of the given changesets.

If string starts with re:, the remainder of the name is treated as a regular expression. To match a branch that actually starts with re:, use the prefix literal:.

branchpoint()
Changesets with more than one child.
bumped()

Mutable changesets marked as successors of public changesets.

Only non-public and non-obsolete changesets can be bumped.

bundle()

Changesets in the bundle.

Bundle must be specified by the -R option.

children(set)
Child changesets of changesets in set.
closed()
Changeset is closed.
contains(pattern)
Revision contains a file matching pattern. See hg help patterns for information about file patterns.
converted([id])
Changesets converted from the given identifier in the old repository if present, or all converted changesets if no identifier is specified.
date(interval)
Changesets within the interval, see hg help dates.
desc(string)
Search commit message for string. The match is case-insensitive.
descendants(set)
Changesets which are descendants of changesets in set.
destination([set])
Changesets that were created by a graft, transplant or rebase operation, with the given revisions specified as the source. Omitting the optional set is the same as passing all().
divergent()
Final successors of changesets with an alternative set of final successors.
draft()
Changeset in draft phase.
extinct()
Obsolete changesets with obsolete descendants only.
extra(label, [value])

Changesets with the given label in the extra metadata, with the given optional value.

If value starts with re:, the remainder of the value is treated as a regular expression. To match a value that actually starts with re:, use the prefix literal:.

file(pattern)

Changesets affecting files matched by pattern.

For a faster but less accurate result, consider using filelog() instead.

filelog(pattern)

Changesets connected to the specified filelog.

For performance reasons, filelog() does not show every changeset that affects the requested file(s). See hg help log for details. For a slower, more accurate result, use file().

first(set, [n])
An alias for limit().
follow([file])
An alias for ::. (ancestors of the working copy's first parent). If a filename is specified, the history of the given file is followed, including copies.
grep(regex)
Like keyword(string) but accepts a regex. Use grep(r'...') to ensure special escape characters are handled correctly. Unlike keyword(string), the match is case-sensitive.
head()
Changeset is a named branch head.
heads(set)
Members of set with no children in set.
hidden()
Hidden changesets.
id(string)
Revision non-ambiguously specified by the given hex string prefix.
keyword(string)
Search commit message, user name, and names of changed files for string. The match is case-insensitive.
last(set, [n])
Last n members of set, defaulting to 1.
limit(set, [n])
First n members of set, defaulting to 1.
matching(revision [, field])

Changesets in which a given set of fields match the set of fields in the selected revision or set.

To match more than one field pass the list of fields to match separated by spaces (e.g. author description).

Valid fields are most regular revision fields and some special fields.

Regular revision fields are description, author, branch, date, files, phase, parents, substate, user and diff. Note that author and user are synonyms. diff refers to the contents of the revision. Two revisions matching their diff will also match their files.

Special fields are summary and metadata: summary matches the first line of the description. metadata is equivalent to matching description user date (i.e. it matches the main metadata fields).

metadata is the default field which is used when no fields are specified. You can match more than one field at a time.

max(set)
Changeset with highest revision number in set.
merge()
Changeset is a merge changeset.
min(set)
Changeset with lowest revision number in set.
modifies(pattern)
Changesets modifying files matched by pattern.
obsolete()
Mutable changeset with a newer version.
origin([set])
Changesets that were specified as a source for the grafts, transplants or rebases that created the given revisions. Omitting the optional set is the same as passing all(). If a changeset created by these operations is itself specified as a source for one of these operations, only the source changeset for the first operation is selected.
outgoing([path])
Changesets not found in the specified destination repository, or the default push location.
p1([set])
First parent of changesets in set, or the working directory.
p2([set])
Second parent of changesets in set, or the working directory.
parents([set])
The set of all parents for all changesets in set, or the working directory.
present(set)

An empty set, if any revision in set isn't found; otherwise, all revisions in set.

If any of specified revisions is not present in the local repository, the query is normally aborted. But this predicate allows the query to continue even in such cases.

public()
Changeset in public phase.
remote([id [,path]])
Local revision that corresponds to the given identifier in a remote repository, if present. Here, the '.' identifier is a synonym for the current local branch.
removes(pattern)
Changesets which remove files matching pattern.
rev(number)
Revision with the given numeric identifier.
reverse(set)
Reverse order of set.
roots(set)
Changesets in set with no parent changeset in set.
secret()
Changeset in secret phase.
sort(set[, [-]key...])

Sort set by keys. The default sort order is ascending, specify a key as -key to sort in descending order.

The keys can be:

  • rev for the revision number,
  • branch for the branch name,
  • desc for the commit message (description),
  • user for user name (author can be used as an alias),
  • date for the commit date
tag([name])
The specified tag by name, or all tagged revisions if no name is given.
unstable()
Non-obsolete changesets with obsolete ancestors.
user(string)

User name contains string. The match is case-insensitive.

If string starts with re:, the remainder of the string is treated as a regular expression. To match a user that actually contains re:, use the prefix literal:.

New predicates (known as "aliases") can be defined, using any combination of existing predicates or other aliases. An alias definition looks like:

<alias> = <definition>

in the revsetalias section of a Mercurial configuration file. Arguments of the form $1, $2, etc. are substituted from the alias into the definition.

For example,

[revsetalias]
h = heads()
d($1) = sort($1, date)
rs($1, $2) = reverse(sort($1, $2))

defines three aliases, h, d, and rs. rs(0:tip, author) is exactly equivalent to reverse(sort(0:tip, author)).

Command line equivalents for hg log:

-f    ->  ::.
-d x  ->  date(x)
-k x  ->  keyword(x)
-m    ->  merge()
-u x  ->  user(x)
-b x  ->  branch(x)
-P x  ->  !::x
-l x  ->  limit(expr, x)

Some sample queries:

  • Changesets on the default branch:

    hg log -r "branch(default)"
    
  • Changesets on the default branch since tag 1.5 (excluding merges):

    hg log -r "branch(default) and 1.5:: and not merge()"
    
  • Open branch heads:

    hg log -r "head() and not closed()"
    
  • Changesets between tags 1.3 and 1.5 mentioning "bug" that affect hgext/*:

    hg log -r "1.3::1.5 and keyword(bug) and file('hgext/*')"
    
  • Changesets committed in May 2008, sorted by user:

    hg log -r "sort(date('May 2008'), user)"
    
  • Changesets mentioning "bug" or "issue" that are not in a tagged release:

    hg log -r "(keyword(bug) or keyword(issue)) and not ancestors(tag())"
    

Subrepositories

Subrepositories let you nest external repositories or projects into a parent Mercurial repository, and make commands operate on them as a group.

Mercurial currently supports Mercurial, Git, and Subversion subrepositories.

Subrepositories are made of three components:

  1. Nested repository checkouts. They can appear anywhere in the parent working directory.

  2. Nested repository references. They are defined in .hgsub, which should be placed in the root of working directory, and tell where the subrepository checkouts come from. Mercurial subrepositories are referenced like:

    path/to/nested = https://example.com/nested/repo/path
    

    Git and Subversion subrepos are also supported:

    path/to/nested = [git]git://example.com/nested/repo/path
    path/to/nested = [svn]https://example.com/nested/trunk/path
    

    where path/to/nested is the checkout location relatively to the parent Mercurial root, and https://example.com/nested/repo/path is the source repository path. The source can also reference a filesystem path.

    Note that .hgsub does not exist by default in Mercurial repositories, you have to create and add it to the parent repository before using subrepositories.

  3. Nested repository states. They are defined in .hgsubstate, which is placed in the root of working directory, and capture whatever information is required to restore the subrepositories to the state they were committed in a parent repository changeset. Mercurial automatically record the nested repositories states when committing in the parent repository.

    Note

    The .hgsubstate file should not be edited manually.

Adding a Subrepository

If .hgsub does not exist, create it and add it to the parent repository. Clone or checkout the external projects where you want it to live in the parent repository. Edit .hgsub and add the subrepository entry as described above. At this point, the subrepository is tracked and the next commit will record its state in .hgsubstate and bind it to the committed changeset.

Synchronizing a Subrepository

Subrepos do not automatically track the latest changeset of their sources. Instead, they are updated to the changeset that corresponds with the changeset checked out in the top-level changeset. This is so developers always get a consistent set of compatible code and libraries when they update.

Thus, updating subrepos is a manual process. Simply check out target subrepo at the desired revision, test in the top-level repo, then commit in the parent repository to record the new combination.

Deleting a Subrepository

To remove a subrepository from the parent repository, delete its reference from .hgsub, then remove its files.

Interaction with Mercurial Commands

add:add does not recurse in subrepos unless -S/--subrepos is specified. However, if you specify the full path of a file in a subrepo, it will be added even without -S/--subrepos specified. Git and Subversion subrepositories are currently silently ignored.
archive:archive does not recurse in subrepositories unless -S/--subrepos is specified.
commit:commit creates a consistent snapshot of the state of the entire project and its subrepositories. If any subrepositories have been modified, Mercurial will abort. Mercurial can be made to instead commit all modified subrepositories by specifying -S/--subrepos, or setting "ui.commitsubrepos=True" in a configuration file (see hg help config). After there are no longer any modified subrepositories, it records their state and finally commits it in the parent repository.
diff:diff does not recurse in subrepos unless -S/--subrepos is specified. Changes are displayed as usual, on the subrepositories elements. Git and Subversion subrepositories are currently silently ignored.
forget:forget currently only handles exact file matches in subrepos. Git and Subversion subrepositories are currently silently ignored.
incoming:incoming does not recurse in subrepos unless -S/--subrepos is specified. Git and Subversion subrepositories are currently silently ignored.
outgoing:outgoing does not recurse in subrepos unless -S/--subrepos is specified. Git and Subversion subrepositories are currently silently ignored.
pull:pull is not recursive since it is not clear what to pull prior to running hg update. Listing and retrieving all subrepositories changes referenced by the parent repository pulled changesets is expensive at best, impossible in the Subversion case.
push:Mercurial will automatically push all subrepositories first when the parent repository is being pushed. This ensures new subrepository changes are available when referenced by top-level repositories. Push is a no-op for Subversion subrepositories.
status:status does not recurse into subrepositories unless -S/--subrepos is specified. Subrepository changes are displayed as regular Mercurial changes on the subrepository elements. Subversion subrepositories are currently silently ignored.
update:update restores the subrepos in the state they were originally committed in target changeset. If the recorded changeset is not available in the current subrepository, Mercurial will pull it in first before updating. This means that updating can require network access when using subrepositories.

Remapping Subrepositories Sources

A subrepository source location may change during a project life, invalidating references stored in the parent repository history. To fix this, rewriting rules can be defined in parent repository hgrc file or in Mercurial configuration. See the [subpaths] section in hgrc(5) for more details.

Template Usage

Mercurial allows you to customize output of commands through templates. You can either pass in a template from the command line, via the --template option, or select an existing template-style (--style).

You can customize output for any "log-like" command: log, outgoing, incoming, tip, parents, heads and glog.

Five styles are packaged with Mercurial: default (the style used when no explicit preference is passed), compact, changelog, phases and xml. Usage:

$ hg log -r1 --style changelog

A template is a piece of text, with markup to invoke variable expansion:

$ hg log -r1 --template "{node}\n"
b56ce7b07c52de7d5fd79fb89701ea538af65746

Strings in curly braces are called keywords. The availability of keywords depends on the exact context of the templater. These keywords are usually available for templating a log-like command:

author:String. The unmodified author of the changeset.
bisect:String. The changeset bisection status.
bookmarks:List of strings. Any bookmarks associated with the changeset.
branch:String. The name of the branch on which the changeset was committed.
branches:List of strings. The name of the branch on which the changeset was committed. Will be empty if the branch name was default.
children:List of strings. The children of the changeset.
date:Date information. The date when the changeset was committed.
desc:String. The text of the changeset description.
diffstat:String. Statistics of changes with the following format: "modified files: +added/-removed lines"
file_adds:List of strings. Files added by this changeset.
file_copies:List of strings. Files copied in this changeset with their sources.
file_copies_switch:
 List of strings. Like "file_copies" but displayed only if the --copied switch is set.
file_dels:List of strings. Files removed by this changeset.
file_mods:List of strings. Files modified by this changeset.
files:List of strings. All files modified, added, or removed by this changeset.
latesttag:String. Most recent global tag in the ancestors of this changeset.
latesttagdistance:
 Integer. Longest path to the latest tag.
node:String. The changeset identification hash, as a 40 hexadecimal digit string.
p1node:String. The identification hash of the changeset's first parent, as a 40 digit hexadecimal string. If the changeset has no parents, all digits are 0.
p1rev:Integer. The repository-local revision number of the changeset's first parent, or -1 if the changeset has no parents.
p2node:String. The identification hash of the changeset's second parent, as a 40 digit hexadecimal string. If the changeset has no second parent, all digits are 0.
p2rev:Integer. The repository-local revision number of the changeset's second parent, or -1 if the changeset has no second parent.
parents:List of strings. The parents of the changeset in "rev:node" format. If the changeset has only one "natural" parent (the predecessor revision) nothing is shown.
phase:String. The changeset phase name.
phaseidx:Integer. The changeset phase index.
rev:Integer. The repository-local changeset revision number.
tags:List of strings. Any tags associated with the changeset.

The "date" keyword does not produce human-readable output. If you want to use a date in your output, you can use a filter to process it. Filters are functions which return a string based on the input variable. Be sure to use the stringify filter first when you're applying a string-input filter to a list-like input variable. You can also use a chain of filters to get the desired output:

$ hg tip --template "{date|isodate}\n"
2008-08-21 18:22 +0000

List of filters:

addbreaks:Any text. Add an XHTML "<br />" tag before the end of every line except the last.
age:Date. Returns a human-readable date/time difference between the given date/time and the current date/time.
basename:Any text. Treats the text as a path, and returns the last component of the path after splitting by the path separator (ignoring trailing separators). For example, "foo/bar/baz" becomes "baz" and "foo/bar//" becomes "bar".
date:Date. Returns a date in a Unix date format, including the timezone: "Mon Sep 04 15:13:13 2006 0700".
domain:Any text. Finds the first string that looks like an email address, and extracts just the domain component. Example: User <user@example.com> becomes example.com.
email:Any text. Extracts the first string that looks like an email address. Example: User <user@example.com> becomes user@example.com.
emailuser:Any text. Returns the user portion of an email address.
escape:Any text. Replaces the special XML/XHTML characters "&", "<" and ">" with XML entities, and filters out NUL characters.
fill68:Any text. Wraps the text to fit in 68 columns.
fill76:Any text. Wraps the text to fit in 76 columns.
firstline:Any text. Returns the first line of text.
hex:Any text. Convert a binary Mercurial node identifier into its long hexadecimal representation.
hgdate:Date. Returns the date as a pair of numbers: "1157407993 25200" (Unix timestamp, timezone offset).
isodate:Date. Returns the date in ISO 8601 format: "2009-08-18 13:00 +0200".
isodatesec:Date. Returns the date in ISO 8601 format, including seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date filter.
localdate:Date. Converts a date to local date.
nonempty:Any text. Returns '(none)' if the string is empty.
obfuscate:Any text. Returns the input text rendered as a sequence of XML entities.
person:Any text. Returns the name before an email address, interpreting it as per RFC 5322.
rfc3339date:Date. Returns a date using the Internet date format specified in RFC 3339: "2009-08-18T13:00:13+02:00".
rfc822date:Date. Returns a date using the same format used in email headers: "Tue, 18 Aug 2009 13:00:13 +0200".
short:Changeset hash. Returns the short form of a changeset hash, i.e. a 12 hexadecimal digit string.
shortbisect:Any text. Treats text as a bisection status, and returns a single-character representing the status (G: good, B: bad, S: skipped, U: untested, I: ignored). Returns single space if text is not a valid bisection status.
shortdate:Date. Returns a date like "2006-09-18".
stringify:Any type. Turns the value into text by converting values into text and concatenating them.
strip:Any text. Strips all leading and trailing whitespace.
stripdir:Treat the text as path and strip a directory level, if possible. For example, "foo" and "foo/bar" becomes "foo".
tabindent:Any text. Returns the text, with every non-empty line except the first starting with a tab character.
urlescape:Any text. Escapes all "special" characters. For example, "foo bar" becomes "foo%20bar".
user:Any text. Returns a short representation of a user name or email address.

Note that a filter is nothing more than a function call, i.e. expr|filter is equivalent to filter(expr).

In addition to filters, there are some basic built-in functions:

  • date(date[, fmt])
  • fill(text[, width])
  • get(dict, key)
  • if(expr, then[, else])
  • ifeq(expr, expr, then[, else])
  • join(list, sep)
  • label(label, expr)
  • rstdoc(text, style)
  • strip(text[, chars])
  • sub(pat, repl, expr)

Also, for any expression that returns a list, there is a list operator:

  • expr % "{template}"

Some sample command line templates:

  • Format lists, e.g. files:

    $ hg log -r 0 --template "files:\n{files % '  {file}\n'}"
    
  • Join the list of files with a ", ":

    $ hg log -r 0 --template "files: {join(files, ', ')}\n"
    
  • Format date:

    $ hg log -r 0 --template "{date(date, '%Y')}\n"
    
  • Output the description set to a fill-width of 30:

    $ hg log -r 0 --template "{fill(desc, '30')}"
    
  • Use a conditional to test for the default branch:

    $ hg log -r 0 --template "{ifeq(branch, 'default', 'on the main branch',
    'on branch {branch}')}\n"
    
  • Append a newline if not empty:

    $ hg tip --template "{if(author, '{author}\n')}"
    
  • Label the output for use with the color extension:

    $ hg log -r 0 --template "{label('changeset.{phase}', node|short)}\n"
    
  • Invert the firstline filter, i.e. everything but the first line:

    $ hg log -r 0 --template "{sub(r'^.*\n?\n?', '', desc)}\n"
    

URL Paths

Valid URLs are of the form:

local/filesystem/path[#revision]
file://local/filesystem/path[#revision]
http://[user[:pass]@]host[:port]/[path][#revision]
https://[user[:pass]@]host[:port]/[path][#revision]
ssh://[user@]host[:port]/[path][#revision]

Paths in the local filesystem can either point to Mercurial repositories or to bundle files (as created by hg bundle or hg incoming --bundle). See also hg help paths.

An optional identifier after # indicates a particular branch, tag, or changeset to use from the remote repository. See also hg help revisions.

Some features, such as pushing to http:// and https:// URLs are only possible if the feature is explicitly enabled on the remote Mercurial server.

Note that the security of HTTPS URLs depends on proper configuration of web.cacerts.

Some notes about using SSH with Mercurial:

  • SSH requires an accessible shell account on the destination machine and a copy of hg in the remote path or specified with as remotecmd.

  • path is relative to the remote user's home directory by default. Use an extra slash at the start of a path to specify an absolute path:

    ssh://example.com//tmp/repository
    
  • Mercurial doesn't use its own compression via SSH; the right thing to do is to configure it in your ~/.ssh/config, e.g.:

    Host *.mylocalnetwork.example.com
      Compression no
    Host *
      Compression yes
    

    Alternatively specify "ssh -C" as your ssh command in your configuration file or with the --ssh command line option.

These URLs can all be stored in your configuration file with path aliases under the [paths] section like so:

[paths]
alias1 = URL1
alias2 = URL2
...

You can then use the alias for any command that uses a URL (for example hg pull alias1 will be treated as hg pull URL1).

Two path aliases are special because they are used as defaults when you do not provide the URL to a command:

default:
When you create a repository with hg clone, the clone command saves the location of the source repository as the new repository's 'default' path. This is then used when you omit path from push- and pull-like commands (including incoming and outgoing).
default-push:
The push command will look for a path named 'default-push', and prefer it over 'default' if both are defined.

Extensions

This section contains help for extensions that are distributed together with Mercurial. Help for other extensions is available in the help system.

acl

hooks for controlling repository access

This hook makes it possible to allow or deny write access to given branches and paths of a repository when receiving incoming changesets via pretxnchangegroup and pretxncommit.

The authorization is matched based on the local user name on the system where the hook runs, and not the committer of the original changeset (since the latter is merely informative).

The acl hook is best used along with a restricted shell like hgsh, preventing authenticating users from doing anything other than pushing or pulling. The hook is not safe to use if users have interactive shell access, as they can then disable the hook. Nor is it safe if remote users share an account, because then there is no way to distinguish them.

The order in which access checks are performed is:

  1. Deny list for branches (section acl.deny.branches)
  2. Allow list for branches (section acl.allow.branches)
  3. Deny list for paths (section acl.deny)
  4. Allow list for paths (section acl.allow)

The allow and deny sections take key-value pairs.

Branch-based Access Control

Use the acl.deny.branches and acl.allow.branches sections to have branch-based access control. Keys in these sections can be either:

  • a branch name, or
  • an asterisk, to match any branch;

The corresponding values can be either:

  • a comma-separated list containing users and groups, or
  • an asterisk, to match anyone;

You can add the "!" prefix to a user or group name to invert the sense of the match.

Path-based Access Control

Use the acl.deny and acl.allow sections to have path-based access control. Keys in these sections accept a subtree pattern (with a glob syntax by default). The corresponding values follow the same syntax as the other sections above.

Groups

Group names must be prefixed with an @ symbol. Specifying a group name has the same effect as specifying all the users in that group.

You can define group members in the acl.groups section. If a group name is not defined there, and Mercurial is running under a Unix-like system, the list of users will be taken from the OS. Otherwise, an exception will be raised.

Example Configuration

[hooks]

# Use this if you want to check access restrictions at commit time
pretxncommit.acl = python:hgext.acl.hook

# Use this if you want to check access restrictions for pull, push,
# bundle and serve.
pretxnchangegroup.acl = python:hgext.acl.hook

[acl]
# Allow or deny access for incoming changes only if their source is
# listed here, let them pass otherwise. Source is "serve" for all
# remote access (http or ssh), "push", "pull" or "bundle" when the
# related commands are run locally.
# Default: serve
sources = serve

[acl.deny.branches]

# Everyone is denied to the frozen branch:
frozen-branch = *

# A bad user is denied on all branches:
* = bad-user

[acl.allow.branches]

# A few users are allowed on branch-a:
branch-a = user-1, user-2, user-3

# Only one user is allowed on branch-b:
branch-b = user-1

# The super user is allowed on any branch:
* = super-user

# Everyone is allowed on branch-for-tests:
branch-for-tests = *

[acl.deny]
# This list is checked first. If a match is found, acl.allow is not
# checked. All users are granted access if acl.deny is not present.
# Format for both lists: glob pattern = user, ..., @group, ...

# To match everyone, use an asterisk for the user:
# my/glob/pattern = *

# user6 will not have write access to any file:
** = user6

# Group "hg-denied" will not have write access to any file:
** = @hg-denied

# Nobody will be able to change "DONT-TOUCH-THIS.txt", despite
# everyone being able to change all other files. See below.
src/main/resources/DONT-TOUCH-THIS.txt = *

[acl.allow]
# if acl.allow is not present, all users are allowed by default
# empty acl.allow = no users allowed

# User "doc_writer" has write access to any file under the "docs"
# folder:
docs/** = doc_writer

# User "jack" and group "designers" have write access to any file
# under the "images" folder:
images/** = jack, @designers

# Everyone (except for "user6" and "@hg-denied" - see acl.deny above)
# will have write access to any file under the "resources" folder
# (except for 1 file. See acl.deny):
src/main/resources/** = *

.hgtags = release_engineer

Examples using the "!" prefix

Suppose there's a branch that only a given user (or group) should be able to push to, and you don't want to restrict access to any other branch that may be created.

The "!" prefix allows you to prevent anyone except a given user or group to push changesets in a given branch or path.

In the examples below, we will: 1) Deny access to branch "ring" to anyone but user "gollum" 2) Deny access to branch "lake" to anyone but members of the group "hobbit" 3) Deny access to a file to anyone but user "gollum"

[acl.allow.branches]
# Empty

[acl.deny.branches]

# 1) only 'gollum' can commit to branch 'ring';
# 'gollum' and anyone else can still commit to any other branch.
ring = !gollum

# 2) only members of the group 'hobbit' can commit to branch 'lake';
# 'hobbit' members and anyone else can still commit to any other branch.
lake = !@hobbit

# You can also deny access based on file paths:

[acl.allow]
# Empty

[acl.deny]
# 3) only 'gollum' can change the file below;
# 'gollum' and anyone else can still change any other file.
/misty/mountains/cave/ring = !gollum

blackbox

log repository events to a blackbox for debugging

Logs event information to .hg/blackbox.log to help debug and diagnose problems. The events that get logged can be configured via the blackbox.track config key. Examples:

[blackbox]
track = *

[blackbox]
track = command, commandfinish, commandexception, exthook, pythonhook

[blackbox]
track = incoming

[blackbox]
# limit the size of a log file
maxsize = 1.5 MB
# rotate up to N log files when the current one gets too big
maxfiles = 3

Commands

blackbox

hg blackbox [OPTION]...

view the recent repository events

Options:

-l, --limit the number of events to show (default: 10)

bugzilla

hooks for integrating with the Bugzilla bug tracker

This hook extension adds comments on bugs in Bugzilla when changesets that refer to bugs by Bugzilla ID are seen. The comment is formatted using the Mercurial template mechanism.

The bug references can optionally include an update for Bugzilla of the hours spent working on the bug. Bugs can also be marked fixed.

Three basic modes of access to Bugzilla are provided:

  1. Access via the Bugzilla XMLRPC interface. Requires Bugzilla 3.4 or later.
  2. Check data via the Bugzilla XMLRPC interface and submit bug change via email to Bugzilla email interface. Requires Bugzilla 3.4 or later.
  3. Writing directly to the Bugzilla database. Only Bugzilla installations using MySQL are supported. Requires Python MySQLdb.

Writing directly to the database is susceptible to schema changes, and relies on a Bugzilla contrib script to send out bug change notification emails. This script runs as the user running Mercurial, must be run on the host with the Bugzilla install, and requires permission to read Bugzilla configuration details and the necessary MySQL user and password to have full access rights to the Bugzilla database. For these reasons this access mode is now considered deprecated, and will not be updated for new Bugzilla versions going forward. Only adding comments is supported in this access mode.

Access via XMLRPC needs a Bugzilla username and password to be specified in the configuration. Comments are added under that username. Since the configuration must be readable by all Mercurial users, it is recommended that the rights of that user are restricted in Bugzilla to the minimum necessary to add comments. Marking bugs fixed requires Bugzilla 4.0 and later.

Access via XMLRPC/email uses XMLRPC to query Bugzilla, but sends email to the Bugzilla email interface to submit comments to bugs. The From: address in the email is set to the email address of the Mercurial user, so the comment appears to come from the Mercurial user. In the event that the Mercurial user email is not recognized by Bugzilla as a Bugzilla user, the email associated with the Bugzilla username used to log into Bugzilla is used instead as the source of the comment. Marking bugs fixed works on all supported Bugzilla versions.

Configuration items common to all access modes:

bugzilla.version

The access type to use. Values recognized are:

xmlrpc:Bugzilla XMLRPC interface.
xmlrpc+email:Bugzilla XMLRPC and email interfaces.
3.0:MySQL access, Bugzilla 3.0 and later.
2.18:MySQL access, Bugzilla 2.18 and up to but not including 3.0.
2.16:MySQL access, Bugzilla 2.16 and up to but not including 2.18.
bugzilla.regexp
Regular expression to match bug IDs for update in changeset commit message. It must contain one "()" named group <ids> containing the bug IDs separated by non-digit characters. It may also contain a named group <hours> with a floating-point number giving the hours worked on the bug. If no named groups are present, the first "()" group is assumed to contain the bug IDs, and work time is not updated. The default expression matches Bug 1234, Bug no. 1234, Bug number 1234, Bugs 1234,5678, Bug 1234 and 5678 and variations thereof, followed by an hours number prefixed by h or hours, e.g. hours 1.5. Matching is case insensitive.
bugzilla.fixregexp
Regular expression to match bug IDs for marking fixed in changeset commit message. This must contain a "()" named group <ids>` containing the bug IDs separated by non-digit characters. It may also contain a named group ``<hours> with a floating-point number giving the hours worked on the bug. If no named groups are present, the first "()" group is assumed to contain the bug IDs, and work time is not updated. The default expression matches Fixes 1234, Fixes bug 1234, Fixes bugs 1234,5678, Fixes 1234 and 5678 and variations thereof, followed by an hours number prefixed by h or hours, e.g. hours 1.5. Matching is case insensitive.
bugzilla.fixstatus
The status to set a bug to when marking fixed. Default RESOLVED.
bugzilla.fixresolution
The resolution to set a bug to when marking fixed. Default FIXED.
bugzilla.style
The style file to use when formatting comments.
bugzilla.template

Template to use when formatting comments. Overrides style if specified. In addition to the usual Mercurial keywords, the extension specifies:

{bug}:The Bugzilla bug ID.
{root}:The full pathname of the Mercurial repository.
{webroot}:Stripped pathname of the Mercurial repository.
{hgweb}:Base URL for browsing Mercurial repositories.

Default changeset {node|short} in repo {root} refers to bug {bug}.\ndetails:\n\t{desc|tabindent}

bugzilla.strip
The number of path separator characters to strip from the front of the Mercurial repository path ({root} in templates) to produce {webroot}. For example, a repository with {root} /var/local/my-project with a strip of 2 gives a value for {webroot} of my-project. Default 0.
web.baseurl
Base URL for browsing Mercurial repositories. Referenced from templates as {hgweb}.

Configuration items common to XMLRPC+email and MySQL access modes:

bugzilla.usermap

Path of file containing Mercurial committer email to Bugzilla user email mappings. If specified, the file should contain one mapping per line:

committer = Bugzilla user

See also the [usermap] section.

The [usermap] section is used to specify mappings of Mercurial committer email to Bugzilla user email. See also bugzilla.usermap. Contains entries of the form committer = Bugzilla user.

XMLRPC access mode configuration:

bugzilla.bzurl
The base URL for the Bugzilla installation. Default http://localhost/bugzilla.
bugzilla.user
The username to use to log into Bugzilla via XMLRPC. Default bugs.
bugzilla.password
The password for Bugzilla login.

XMLRPC+email access mode uses the XMLRPC access mode configuration items, and also:

bugzilla.bzemail
The Bugzilla email address.

In addition, the Mercurial email settings must be configured. See the documentation in hgrc(5), sections [email] and [smtp].

MySQL access mode configuration:

bugzilla.host
Hostname of the MySQL server holding the Bugzilla database. Default localhost.
bugzilla.db
Name of the Bugzilla database in MySQL. Default bugs.
bugzilla.user
Username to use to access MySQL server. Default bugs.
bugzilla.password
Password to use to access MySQL server.
bugzilla.timeout
Database connection timeout (seconds). Default 5.
bugzilla.bzuser
Fallback Bugzilla user name to record comments with, if changeset committer cannot be found as a Bugzilla user.
bugzilla.bzdir
Bugzilla install directory. Used by default notify. Default /var/www/html/bugzilla.
bugzilla.notify
The command to run to get Bugzilla to send bug change notification emails. Substitutes from a map with 3 keys, bzdir, id (bug id) and user (committer bugzilla email). Default depends on version; from 2.18 it is "cd %(bzdir)s && perl -T contrib/sendbugmail.pl %(id)s %(user)s".

Activating the extension:

[extensions]
bugzilla =

[hooks]
# run bugzilla hook on every change pulled or pushed in here
incoming.bugzilla = python:hgext.bugzilla.hook

Example configurations:

XMLRPC example configuration. This uses the Bugzilla at http://my-project.org/bugzilla, logging in as user bugmail@my-project.org with password plugh. It is used with a collection of Mercurial repositories in /var/local/hg/repos/, with a web interface at http://my-project.org/hg.

[bugzilla]
bzurl=http://my-project.org/bugzilla
user=bugmail@my-project.org
password=plugh
version=xmlrpc
template=Changeset {node|short} in {root|basename}.
         {hgweb}/{webroot}/rev/{node|short}\n
         {desc}\n
strip=5

[web]
baseurl=http://my-project.org/hg

XMLRPC+email example configuration. This uses the Bugzilla at http://my-project.org/bugzilla, logging in as user bugmail@my-project.org with password plugh. It is used with a collection of Mercurial repositories in /var/local/hg/repos/, with a web interface at http://my-project.org/hg. Bug comments are sent to the Bugzilla email address bugzilla@my-project.org.

[bugzilla]
bzurl=http://my-project.org/bugzilla
user=bugmail@my-project.org
password=plugh
version=xmlrpc
bzemail=bugzilla@my-project.org
template=Changeset {node|short} in {root|basename}.
         {hgweb}/{webroot}/rev/{node|short}\n
         {desc}\n
strip=5

[web]
baseurl=http://my-project.org/hg

[usermap]
user@emaildomain.com=user.name@bugzilladomain.com

MySQL example configuration. This has a local Bugzilla 3.2 installation in /opt/bugzilla-3.2. The MySQL database is on localhost, the Bugzilla database name is bugs and MySQL is accessed with MySQL username bugs password XYZZY. It is used with a collection of Mercurial repositories in /var/local/hg/repos/, with a web interface at http://my-project.org/hg.

[bugzilla]
host=localhost
password=XYZZY
version=3.0
bzuser=unknown@domain.com
bzdir=/opt/bugzilla-3.2
template=Changeset {node|short} in {root|basename}.
         {hgweb}/{webroot}/rev/{node|short}\n
         {desc}\n
strip=5

[web]
baseurl=http://my-project.org/hg

[usermap]
user@emaildomain.com=user.name@bugzilladomain.com

All the above add a comment to the Bugzilla bug record of the form:

Changeset 3b16791d6642 in repository-name.
http://my-project.org/hg/repository-name/rev/3b16791d6642

Changeset commit comment. Bug 1234.

children

command to display child changesets (DEPRECATED)

This extension is deprecated. You should use hg log -r "children(REV)" instead.

Commands

children

hg children [-r REV] [FILE]

Print the children of the working directory's revisions. If a revision is given via -r/--rev, the children of that revision will be printed. If a file argument is given, revision in which the file was last changed (after the working directory revision or the argument to --rev if given) is printed.

Options:

-r, --rev show children of the specified revision
--style display using template map file
--template display with template

churn

command to display statistics about repository history

Commands

churn

hg churn [-d DATE] [-r REV] [--aliases FILE] [FILE]

This command will display a histogram representing the number of changed lines or revisions, grouped according to the given template. The default template will group changes by author. The --dateformat option may be used to group the results by date instead.

Statistics are based on the number of changed lines, or alternatively the number of matching revisions if the --changesets option is specified.

Examples:

# display count of changed lines for every committer
hg churn -t "{author|email}"

# display daily activity graph
hg churn -f "%H" -s -c

# display activity of developers by month
hg churn -f "%Y-%m" -s -c

# display count of lines changed in every year
hg churn -f "%Y" -s

It is possible to map alternate email addresses to a main address by providing a file using the following format:

<alias email> = <actual email>

Such a file may be specified with the --aliases option, otherwise a .hgchurn file will be looked for in the working directory root. Aliases will be split from the rightmost "=".

Options:

-r, --rev count rate for the specified revision or range
-d, --date count rate for revisions matching date spec
-t, --template template to group changesets (default: {author|email})
-f, --dateformat
 strftime-compatible format for grouping by date
-c, --changesets
 count rate by number of changesets
-s, --sort sort by key (default: sort by count)
--diffstat display added/removed lines separately
--aliases file with email aliases
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns

color

colorize output from some commands

This extension modifies the status and resolve commands to add color to their output to reflect file status, the qseries command to add color to reflect patch status (applied, unapplied, missing), and to diff-related commands to highlight additions, removals, diff headers, and trailing whitespace.

Other effects in addition to color, like bold and underlined text, are also available. By default, the terminfo database is used to find the terminal codes used to change color and effect. If terminfo is not available, then effects are rendered with the ECMA-48 SGR control function (aka ANSI escape codes).

Default effects may be overridden from your configuration file:

[color]
status.modified = blue bold underline red_background
status.added = green bold
status.removed = red bold blue_background
status.deleted = cyan bold underline
status.unknown = magenta bold underline
status.ignored = black bold

# 'none' turns off all effects
status.clean = none
status.copied = none

qseries.applied = blue bold underline
qseries.unapplied = black bold
qseries.missing = red bold

diff.diffline = bold
diff.extended = cyan bold
diff.file_a = red bold
diff.file_b = green bold
diff.hunk = magenta
diff.deleted = red
diff.inserted = green
diff.changed = white
diff.trailingwhitespace = bold red_background

resolve.unresolved = red bold
resolve.resolved = green bold

bookmarks.current = green

branches.active = none
branches.closed = black bold
branches.current = green
branches.inactive = none

tags.normal = green
tags.local = black bold

rebase.rebased = blue
rebase.remaining = red bold

shelve.age = cyan
shelve.newest = green bold
shelve.name = blue bold

histedit.remaining = red bold

The available effects in terminfo mode are 'blink', 'bold', 'dim', 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and 'underline'. How each is rendered depends on the terminal emulator. Some may not be available for a given terminal type, and will be silently ignored.

Note that on some systems, terminfo mode may cause problems when using color with the pager extension and less -R. less with the -R option will only display ECMA-48 color codes, and terminfo mode may sometimes emit codes that less doesn't understand. You can work around this by either using ansi mode (or auto mode), or by using less -r (which will pass through all terminal control codes, not just color control codes).

Because there are only eight standard colors, this module allows you to define color names for other color slots which might be available for your terminal type, assuming terminfo mode. For instance:

color.brightblue = 12
color.pink = 207
color.orange = 202

to set 'brightblue' to color slot 12 (useful for 16 color terminals that have brighter colors defined in the upper eight) and, 'pink' and 'orange' to colors in 256-color xterm's default color cube. These defined colors may then be used as any of the pre-defined eight, including appending '_background' to set the background to that color.

By default, the color extension will use ANSI mode (or win32 mode on Windows) if it detects a terminal. To override auto mode (to enable terminfo mode, for example), set the following configuration option:

[color]
mode = terminfo

Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will disable color.

convert

import revisions from foreign VCS repositories into Mercurial

Commands

convert

hg convert [OPTION]... SOURCE [DEST [REVMAP]]

Accepted source formats [identifiers]:

  • Mercurial [hg]
  • CVS [cvs]
  • Darcs [darcs]
  • git [git]
  • Subversion [svn]
  • Monotone [mtn]
  • GNU Arch [gnuarch]
  • Bazaar [bzr]
  • Perforce [p4]

Accepted destination formats [identifiers]:

  • Mercurial [hg]
  • Subversion [svn] (history on branches is not preserved)

If no revision is given, all revisions will be converted. Otherwise, convert will only import up to the named revision (given in a format understood by the source).

If no destination directory name is specified, it defaults to the basename of the source with -hg appended. If the destination repository doesn't exist, it will be created.

By default, all sources except Mercurial will use --branchsort. Mercurial uses --sourcesort to preserve original revision numbers order. Sort modes have the following effects:

--branchsort convert from parent to child revision when possible, which means branches are usually converted one after the other. It generates more compact repositories.
--datesort sort revisions by date. Converted repositories have good-looking changelogs but are often an order of magnitude larger than the same ones generated by --branchsort.
--sourcesort try to preserve source revisions order, only supported by Mercurial sources.
--closesort try to move closed revisions as close as possible to parent branches, only supported by Mercurial sources.

If REVMAP isn't given, it will be put in a default location (<dest>/.hg/shamap by default). The REVMAP is a simple text file that maps each source commit ID to the destination ID for that revision, like so:

<source ID> <destination ID>

If the file doesn't exist, it's automatically created. It's updated on each commit copied, so hg convert can be interrupted and can be run repeatedly to copy new commits.

The authormap is a simple text file that maps each source commit author to a destination commit author. It is handy for source SCMs that use unix logins to identify authors (e.g.: CVS). One line per author mapping and the line format is:

source author = destination author

Empty lines and lines starting with a # are ignored.

The filemap is a file that allows filtering and remapping of files and directories. Each line can contain one of the following directives:

include path/to/file-or-dir

exclude path/to/file-or-dir

rename path/to/source path/to/destination

Comment lines start with #. A specified path matches if it equals the full relative name of a file or one of its parent directories. The include or exclude directive with the longest matching path applies, so line order does not matter.

The include directive causes a file, or all files under a directory, to be included in the destination repository, and the exclusion of all other files and directories not explicitly included. The exclude directive causes files or directories to be omitted. The rename directive renames a file or directory if it is converted. To rename from a subdirectory into the root of the repository, use . as the path to rename to.

The splicemap is a file that allows insertion of synthetic history, letting you specify the parents of a revision. This is useful if you want to e.g. give a Subversion merge two parents, or graft two disconnected series of history together. Each entry contains a key, followed by a space, followed by one or two comma-separated values:

key parent1, parent2

The key is the revision ID in the source revision control system whose parents should be modified (same format as a key in .hg/shamap). The values are the revision IDs (in either the source or destination revision control system) that should be used as the new parents for that node. For example, if you have merged "release-1.0" into "trunk", then you should specify the revision on "trunk" as the first parent and the one on the "release-1.0" branch as the second.

The branchmap is a file that allows you to rename a branch when it is being brought in from whatever external repository. When used in conjunction with a splicemap, it allows for a powerful combination to help fix even the most badly mismanaged repositories and turn them into nicely structured Mercurial repositories. The branchmap contains lines of the form:

original_branch_name new_branch_name

where "original_branch_name" is the name of the branch in the source repository, and "new_branch_name" is the name of the branch is the destination repository. No whitespace is allowed in the branch names. This can be used to (for instance) move code in one repository from "default" to a named branch.

Mercurial Source

The Mercurial source recognizes the following configuration options, which you can set on the command line with --config:

convert.hg.ignoreerrors:
 ignore integrity errors when reading. Use it to fix Mercurial repositories with missing revlogs, by converting from and to Mercurial. Default is False.
convert.hg.saverev:
 store original revision ID in changeset (forces target IDs to change). It takes a boolean argument and defaults to False.
convert.hg.revs:
 revset specifying the source revisions to convert.
CVS Source

CVS source will use a sandbox (i.e. a checked-out copy) from CVS to indicate the starting point of what will be converted. Direct access to the repository files is not needed, unless of course the repository is :local:. The conversion uses the top level directory in the sandbox to find the CVS repository, and then uses CVS rlog commands to find files to convert. This means that unless a filemap is given, all files under the starting directory will be converted, and that any directory reorganization in the CVS sandbox is ignored.

The following options can be used with --config:

convert.cvsps.cache:
 Set to False to disable remote log caching, for testing and debugging purposes. Default is True.
convert.cvsps.fuzz:
 Specify the maximum time (in seconds) that is allowed between commits with identical user and log message in a single changeset. When very large files were checked in as part of a changeset then the default may not be long enough. The default is 60.
convert.cvsps.mergeto:
 Specify a regular expression to which commit log messages are matched. If a match occurs, then the conversion process will insert a dummy revision merging the branch on which this log message occurs to the branch indicated in the regex. Default is {{mergetobranch ([-\w]+)}}
convert.cvsps.mergefrom:
 Specify a regular expression to which commit log messages are matched. If a match occurs, then the conversion process will add the most recent revision on the branch indicated in the regex as the second parent of the changeset. Default is {{mergefrombranch ([-\w]+)}}
convert.localtimezone:
 use local time (as determined by the TZ environment variable) for changeset date/times. The default is False (use UTC).
hooks.cvslog:Specify a Python function to be called at the end of gathering the CVS log. The function is passed a list with the log entries, and can modify the entries in-place, or add or delete them.
hooks.cvschangesets:
 Specify a Python function to be called after the changesets are calculated from the CVS log. The function is passed a list with the changeset entries, and can modify the changesets in-place, or add or delete them.

An additional "debugcvsps" Mercurial command allows the builtin changeset merging code to be run without doing a conversion. Its parameters and output are similar to that of cvsps 2.1. Please see the command help for more details.

Subversion Source

Subversion source detects classical trunk/branches/tags layouts. By default, the supplied svn://repo/path/ source URL is converted as a single branch. If svn://repo/path/trunk exists it replaces the default branch. If svn://repo/path/branches exists, its subdirectories are listed as possible branches. If svn://repo/path/tags exists, it is looked for tags referencing converted branches. Default trunk, branches and tags values can be overridden with following options. Set them to paths relative to the source URL, or leave them blank to disable auto detection.

The following options can be set with --config:

convert.svn.branches:
 specify the directory containing branches. The default is branches.
convert.svn.tags:
 specify the directory containing tags. The default is tags.
convert.svn.trunk:
 specify the name of the trunk branch. The default is trunk.
convert.localtimezone:
 use local time (as determined by the TZ environment variable) for changeset date/times. The default is False (use UTC).

Source history can be retrieved starting at a specific revision, instead of being integrally converted. Only single branch conversions are supported.

convert.svn.startrev:
 specify start Subversion revision number. The default is 0.
Perforce Source

The Perforce (P4) importer can be given a p4 depot path or a client specification as source. It will convert all files in the source to a flat Mercurial repository, ignoring labels, branches and integrations. Note that when a depot path is given you then usually should specify a target directory, because otherwise the target may be named ...-hg.

It is possible to limit the amount of source history to be converted by specifying an initial Perforce revision:

convert.p4.startrev:
 specify initial Perforce revision (a Perforce changelist number).
Mercurial Destination

The following options are supported:

convert.hg.clonebranches:
 dispatch source branches in separate clones. The default is False.
convert.hg.tagsbranch:
 branch name for tag revisions, defaults to default.
convert.hg.usebranchnames:
 preserve branch names. The default is True.

Options:

--authors username mapping filename (DEPRECATED, use --authormap instead)
-s, --source-type
 source repository type
-d, --dest-type
 destination repository type
-r, --rev import up to source revision REV
-A, --authormap
 remap usernames using this file
--filemap remap file names using contents of file
--splicemap splice synthesized history into place
--branchmap change branch names while converting
--branchsort try to sort changesets by branches
--datesort try to sort changesets by date
--sourcesort preserve source changesets order
--closesort try to reorder closed revisions

eol

automatically manage newlines in repository files

This extension allows you to manage the type of line endings (CRLF or LF) that are used in the repository and in the local working directory. That way you can get CRLF line endings on Windows and LF on Unix/Mac, thereby letting everybody use their OS native line endings.

The extension reads its configuration from a versioned .hgeol configuration file found in the root of the working copy. The .hgeol file use the same syntax as all other Mercurial configuration files. It uses two sections, [patterns] and [repository].

The [patterns] section specifies how line endings should be converted between the working copy and the repository. The format is specified by a file pattern. The first match is used, so put more specific patterns first. The available line endings are LF, CRLF, and BIN.

Files with the declared format of CRLF or LF are always checked out and stored in the repository in that format and files declared to be binary (BIN) are left unchanged. Additionally, native is an alias for checking out in the platform's default line ending: LF on Unix (including Mac OS X) and CRLF on Windows. Note that BIN (do nothing to line endings) is Mercurial's default behaviour; it is only needed if you need to override a later, more general pattern.

The optional [repository] section specifies the line endings to use for files stored in the repository. It has a single setting, native, which determines the storage line endings for files declared as native in the [patterns] section. It can be set to LF or CRLF. The default is LF. For example, this means that on Windows, files configured as native (CRLF by default) will be converted to LF when stored in the repository. Files declared as LF, CRLF, or BIN in the [patterns] section are always stored as-is in the repository.

Example versioned .hgeol file:

[patterns]
**.py = native
**.vcproj = CRLF
**.txt = native
Makefile = LF
**.jpg = BIN

[repository]
native = LF

Note

The rules will first apply when files are touched in the working copy, e.g. by updating to null and back to tip to touch all files.

The extension uses an optional [eol] section read from both the normal Mercurial configuration files and the .hgeol file, with the latter overriding the former. You can use that section to control the overall behavior. There are three settings:

  • eol.native (default os.linesep) can be set to LF or CRLF to override the default interpretation of native for checkout. This can be used with hg archive on Unix, say, to generate an archive where files have line endings for Windows.
  • eol.only-consistent (default True) can be set to False to make the extension convert files with inconsistent EOLs. Inconsistent means that there is both CRLF and LF present in the file. Such files are normally not touched under the assumption that they have mixed EOLs on purpose.
  • eol.fix-trailing-newline (default False) can be set to True to ensure that converted files end with a EOL character (either \n or \r\n as per the configured patterns).

The extension provides cleverencode: and cleverdecode: filters like the deprecated win32text extension does. This means that you can disable win32text and enable eol and your filters will still work. You only need to these filters until you have prepared a .hgeol file.

The win32text.forbid* hooks provided by the win32text extension have been unified into a single hook named eol.checkheadshook. The hook will lookup the expected line endings from the .hgeol file, which means you must migrate to a .hgeol file first before using the hook. eol.checkheadshook only checks heads, intermediate invalid revisions will be pushed. To forbid them completely, use the eol.checkallhook hook. These hooks are best used as pretxnchangegroup hooks.

See hg help patterns for more information about the glob patterns used.

extdiff

command to allow external programs to compare revisions

The extdiff Mercurial extension allows you to use external programs to compare revisions, or revision with working directory. The external diff programs are called with a configurable set of options and two non-option arguments: paths to directories containing snapshots of files to compare.

The extdiff extension also allows you to configure new diff commands, so you do not need to type hg extdiff -p kdiff3 always.

[extdiff]
# add new command that runs GNU diff(1) in 'context diff' mode
cdiff = gdiff -Nprc5
## or the old way:
#cmd.cdiff = gdiff
#opts.cdiff = -Nprc5

# add new command called vdiff, runs kdiff3
vdiff = kdiff3

# add new command called meld, runs meld (no need to name twice)
meld =

# add new command called vimdiff, runs gvimdiff with DirDiff plugin
# (see http://www.vim.org/scripts/script.php?script_id=102) Non
# English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
# your .vimrc
vimdiff = gvim -f "+next" \
          "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"

Tool arguments can include variables that are expanded at runtime:

$parent1, $plabel1 - filename, descriptive label of first parent
$child,   $clabel  - filename, descriptive label of child revision
$parent2, $plabel2 - filename, descriptive label of second parent
$root              - repository root
$parent is an alias for $parent1.

The extdiff extension will look in your [diff-tools] and [merge-tools] sections for diff tool arguments, when none are specified in [extdiff].

[extdiff]
kdiff3 =

[diff-tools]
kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child

You can use -I/-X and list of file or directory names like normal hg diff command. The extdiff extension makes snapshots of only needed files, so running the external diff program will actually be pretty fast (at least faster than having to compare the entire tree).

Commands

extdiff

hg extdiff [OPT]... [FILE]...

Show differences between revisions for the specified files, using an external program. The default program used is diff, with default options "-Npru".

To select a different program, use the -p/--program option. The program will be passed the names of two directories to compare. To pass additional options to the program, use -o/--option. These will be passed before the names of the directories to compare.

When two revision arguments are given, then changes are shown between those revisions. If only one revision is specified then that revision is compared to the working directory, and, when no revisions are specified, the working directory files are compared to its parent.

Options:

-p, --program comparison program to run
-o, --option pass option to comparison program
-r, --rev revision
-c, --change change made by revision
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns

factotum

http authentication with factotum

This extension allows the factotum(4) facility on Plan 9 from Bell Labs platforms to provide authentication information for HTTP access. Configuration entries specified in the auth section as well as authentication information provided in the repository URL are fully supported. If no prefix is specified, a value of "*" will be assumed.

By default, keys are specified as:

proto=pass service=hg prefix=<prefix> user=<username> !password=<password>

If the factotum extension is unable to read the required key, one will be requested interactively.

A configuration section is available to customize runtime behavior. By default, these entries are:

[factotum]
executable = /bin/auth/factotum
mountpoint = /mnt/factotum
service = hg

The executable entry defines the full path to the factotum binary. The mountpoint entry defines the path to the factotum file service. Lastly, the service entry controls the service name used when reading keys.

fetch

pull, update and merge in one command (DEPRECATED)

Commands

fetch

hg fetch [SOURCE]

This finds all changes from the repository at the specified path or URL and adds them to the local repository.

If the pulled changes add a new branch head, the head is automatically merged, and the result of the merge is committed. Otherwise, the working directory is updated to include the new changes.

When a merge is needed, the working directory is first updated to the newly pulled changes. Local changes are then merged into the pulled changes. To switch the merge order, use --switch-parent.

See hg help dates for a list of formats valid for -d/--date.

Returns 0 on success.

Options:

-r, --rev a specific revision you would like to pull
-e, --edit edit commit message
--force-editor edit commit message (DEPRECATED)
--switch-parent
 switch parents when merging
-m, --message use text as commit message
-l, --logfile read commit message from file
-d, --date record the specified date as commit date
-u, --user record the specified user as committer
-e, --ssh specify ssh command to use
--remotecmd specify hg command to run on the remote side
--insecure do not verify server certificate (ignoring web.cacerts config)

gpg

commands to sign and verify changesets

Commands

sigcheck

hg sigcheck REV

verify all the signatures there may be for a particular revision

sign

hg sign [OPTION]... [REV]...

If no revision is given, the parent of the working directory is used, or tip if no revision is checked out.

See hg help dates for a list of formats valid for -d/--date.

Options:

-l, --local make the signature local
-f, --force sign even if the sigfile is modified
--no-commit do not commit the sigfile after signing
-k, --key the key id to sign with
-m, --message commit message
-d, --date record the specified date as commit date
-u, --user record the specified user as committer

sigs

hg sigs

list signed changesets

graphlog

command to view revision graphs from a shell

This extension adds a --graph option to the incoming, outgoing and log commands. When this options is given, an ASCII representation of the revision graph is also shown.

Commands

glog

hg glog [OPTION]... [FILE]

Print a revision history alongside a revision graph drawn with ASCII characters.

Nodes printed as an @ character are parents of the working directory.

Options:

-f, --follow follow changeset history, or file history across copies and renames
--follow-first only follow the first parent of merge changesets (DEPRECATED)
-d, --date show revisions matching date spec
-C, --copies show copied files
-k, --keyword do case-insensitive search for a given text
-r, --rev show the specified revision or range
--removed include revisions where files were removed
-m, --only-merges
 show only merges (DEPRECATED)
-u, --user revisions committed by user
--only-branch show only changesets within the given named branch (DEPRECATED)
-b, --branch show changesets within the given named branch
-P, --prune do not display revision or any of its ancestors
-p, --patch show patch
-g, --git use git extended diff format
-l, --limit limit number of changes displayed
-M, --no-merges
 do not show merges
--stat output diffstat-style summary of changes
-G, --graph show the revision DAG
--style display using template map file
--template display with template
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns

hgcia

hooks for integrating with the CIA.vc notification service

This is meant to be run as a changegroup or incoming hook. To configure it, set the following options in your hgrc:

[cia]
# your registered CIA user name
user = foo
# the name of the project in CIA
project = foo
# the module (subproject) (optional)
#module = foo
# Append a diffstat to the log message (optional)
#diffstat = False
# Template to use for log messages (optional)
#template = {desc}\n{baseurl}{webroot}/rev/{node}-- {diffstat}
# Style to use (optional)
#style = foo
# The URL of the CIA notification service (optional)
# You can use mailto: URLs to send by email, e.g.
# mailto:cia@cia.vc
# Make sure to set email.from if you do this.
#url = http://cia.vc/
# print message instead of sending it (optional)
#test = False
# number of slashes to strip for url paths
#strip = 0

[hooks]
# one of these:
changegroup.cia = python:hgcia.hook
#incoming.cia = python:hgcia.hook

[web]
# If you want hyperlinks (optional)
baseurl = http://server/path/to/repo

hgk

browse the repository in a graphical way

The hgk extension allows browsing the history of a repository in a graphical way. It requires Tcl/Tk version 8.4 or later. (Tcl/Tk is not distributed with Mercurial.)

hgk consists of two parts: a Tcl script that does the displaying and querying of information, and an extension to Mercurial named hgk.py, which provides hooks for hgk to get information. hgk can be found in the contrib directory, and the extension is shipped in the hgext repository, and needs to be enabled.

The hg view command will launch the hgk Tcl script. For this command to work, hgk must be in your search path. Alternately, you can specify the path to hgk in your configuration file:

[hgk]
path=/location/of/hgk

hgk can make use of the extdiff extension to visualize revisions. Assuming you had already configured extdiff vdiff command, just add:

[hgk]
vdiff=vdiff

Revisions context menu will now display additional entries to fire vdiff on hovered and selected revisions.

Commands

view

hg view [-l LIMIT] [REVRANGE]

start interactive history viewer

Options:

-l, --limit limit number of changes displayed

highlight

syntax highlighting for hgweb (requires Pygments)

It depends on the Pygments syntax highlighting library: http://pygments.org/

There is a single configuration option:

[web]
pygments_style = <style>

The default is 'colorful'.

histedit

interactive history editing

With this extension installed, Mercurial gains one new command: histedit. Usage is as follows, assuming the following history:

@  3[tip]   7c2fd3b9020c   2009-04-27 18:04 -0500   durin42
|    Add delta
|
o  2   030b686bedc4   2009-04-27 18:04 -0500   durin42
|    Add gamma
|
o  1   c561b4e977df   2009-04-27 18:04 -0500   durin42
|    Add beta
|
o  0   d8d2fcd0e319   2009-04-27 18:04 -0500   durin42
     Add alpha

If you were to run hg histedit c561b4e977df, you would see the following file open in your editor:

pick c561b4e977df Add beta
pick 030b686bedc4 Add gamma
pick 7c2fd3b9020c Add delta

# Edit history between c561b4e977df and 7c2fd3b9020c
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  f, fold = use commit, but fold into previous commit (combines N and N-1)
#  d, drop = remove commit from history
#  m, mess = edit message without changing commit content
#

In this file, lines beginning with # are ignored. You must specify a rule for each revision in your history. For example, if you had meant to add gamma before beta, and then wanted to add delta in the same revision as beta, you would reorganize the file to look like this:

pick 030b686bedc4 Add gamma
pick c561b4e977df Add beta
fold 7c2fd3b9020c Add delta

# Edit history between c561b4e977df and 7c2fd3b9020c
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  f, fold = use commit, but fold into previous commit (combines N and N-1)
#  d, drop = remove commit from history
#  m, mess = edit message without changing commit content
#

At which point you close the editor and histedit starts working. When you specify a fold operation, histedit will open an editor when it folds those revisions together, offering you a chance to clean up the commit message:

Add beta
***
Add delta

Edit the commit message to your liking, then close the editor. For this example, let's assume that the commit message was changed to Add beta and delta. After histedit has run and had a chance to remove any old or temporary revisions it needed, the history looks like this:

@  2[tip]   989b4d060121   2009-04-27 18:04 -0500   durin42
|    Add beta and delta.
|
o  1   081603921c3f   2009-04-27 18:04 -0500   durin42
|    Add gamma
|
o  0   d8d2fcd0e319   2009-04-27 18:04 -0500   durin42
     Add alpha

Note that histedit does not remove any revisions (even its own temporary ones) until after it has completed all the editing operations, so it will probably perform several strip operations when it's done. For the above example, it had to run strip twice. Strip can be slow depending on a variety of factors, so you might need to be a little patient. You can choose to keep the original revisions by passing the --keep flag.

The edit operation will drop you back to a command prompt, allowing you to edit files freely, or even use hg record to commit some changes as a separate commit. When you're done, any remaining uncommitted changes will be committed as well. When done, run hg histedit --continue to finish this step. You'll be prompted for a new commit message, but the default commit message will be the original message for the edit ed revision.

The message operation will give you a chance to revise a commit message without changing the contents. It's a shortcut for doing edit immediately followed by hg histedit --continue`.

If histedit encounters a conflict when moving a revision (while handling pick or fold), it'll stop in a similar manner to edit with the difference that it won't prompt you for a commit message when done. If you decide at this point that you don't like how much work it will be to rearrange history, or that you made a mistake, you can use hg histedit --abort to abandon the new changes you have made and return to the state before you attempted to edit your history.

If we clone the histedit-ed example repository above and add four more changes, such that we have the following history:

@  6[tip]   038383181893   2009-04-27 18:04 -0500   stefan
|    Add theta
|
o  5   140988835471   2009-04-27 18:04 -0500   stefan
|    Add eta
|
o  4   122930637314   2009-04-27 18:04 -0500   stefan
|    Add zeta
|
o  3   836302820282   2009-04-27 18:04 -0500   stefan
|    Add epsilon
|
o  2   989b4d060121   2009-04-27 18:04 -0500   durin42
|    Add beta and delta.
|
o  1   081603921c3f   2009-04-27 18:04 -0500   durin42
|    Add gamma
|
o  0   d8d2fcd0e319   2009-04-27 18:04 -0500   durin42
     Add alpha

If you run hg histedit --outgoing on the clone then it is the same as running hg histedit 836302820282. If you need plan to push to a repository that Mercurial does not detect to be related to the source repo, you can add a --force option.

Commands

histedit

hg histedit ANCESTOR | --outgoing [URL]

This command edits changesets between ANCESTOR and the parent of the working directory.

With --outgoing, this edits changesets not found in the destination repository. If URL of the destination is omitted, the 'default-push' (or 'default') path will be used.

For safety, this command is aborted, also if there are ambiguous outgoing revisions which may confuse users: for example, there are multiple branches containing outgoing revisions.

Use "min(outgoing() and ::.)" or similar revset specification instead of --outgoing to specify edit target revision exactly in such ambiguous situation. See hg help revsets for detail about selecting revisions.

Returns 0 on success, 1 if user intervention is required (not only for intentional "edit" command, but also for resolving unexpected conflicts).

Options:

--commands Read history edits from the specified file.
-c, --continue continue an edit already in progress
-k, --keep don't strip old nodes after edit is complete
--abort abort an edit in progress
-o, --outgoing changesets not found in destination
-f, --force force outgoing even for unrelated repositories
-r, --rev first revision to be edited

inotify

accelerate status report using Linux's inotify service

Commands

inserve

hg inserve [OPTION]...

start an inotify server for this repository

Options:

-d, --daemon run server in background
--daemon-pipefds
 used internally by daemon mode
-t, --idle-timeout
 minutes to sit idle before exiting
--pid-file name of file to write process ID to

interhg

None

keyword

expand keywords in tracked files

This extension expands RCS/CVS-like or self-customized $Keywords$ in tracked text files selected by your configuration.

Keywords are only expanded in local repositories and not stored in the change history. The mechanism can be regarded as a convenience for the current user or for archive distribution.

Keywords expand to the changeset data pertaining to the latest change relative to the working directory parent of each file.

Configuration is done in the [keyword], [keywordset] and [keywordmaps] sections of hgrc files.

Example:

[keyword]
# expand keywords in every python file except those matching "x*"
**.py =
x*    = ignore

[keywordset]
# prefer svn- over cvs-like default keywordmaps
svn = True

Note

The more specific you are in your filename patterns the less you lose speed in huge repositories.

For [keywordmaps] template mapping and expansion demonstration and control run hg kwdemo. See hg help templates for a list of available templates and filters.

Three additional date template filters are provided:

utcdate:"2006/09/18 15:13:13"
svnutcdate:"2006-09-18 15:13:13Z"
svnisodate:"2006-09-18 08:13:13 -700 (Mon, 18 Sep 2006)"

The default template mappings (view with hg kwdemo -d) can be replaced with customized keywords and templates. Again, run hg kwdemo to control the results of your configuration changes.

Before changing/disabling active keywords, you must run hg kwshrink to avoid storing expanded keywords in the change history.

To force expansion after enabling it, or a configuration change, run hg kwexpand.

Expansions spanning more than one line and incremental expansions, like CVS' $Log$, are not supported. A keyword template map "Log = {desc}" expands to the first line of the changeset description.

Commands

kwdemo

hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...

Show current, custom, or default keyword template maps and their expansions.

Extend the current configuration by specifying maps as arguments and using -f/--rcfile to source an external hgrc file.

Use -d/--default to disable current configuration.

See hg help templates for information on templates and filters.

Options:

-d, --default show default keyword template maps
-f, --rcfile read maps from rcfile

kwexpand

hg kwexpand [OPTION]... [FILE]...

Run after (re)enabling keyword expansion.

kwexpand refuses to run if given files contain local changes.

Options:

-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns

kwfiles

hg kwfiles [OPTION]... [FILE]...

List which files in the working directory are matched by the [keyword] configuration patterns.

Useful to prevent inadvertent keyword expansion and to speed up execution by including only files that are actual candidates for expansion.

See hg help keyword on how to construct patterns both for inclusion and exclusion of files.

With -A/--all and -v/--verbose the codes used to show the status of files are:

K = keyword expansion candidate
k = keyword expansion candidate (not tracked)
I = ignored
i = ignored (not tracked)

Options:

-A, --all show keyword status flags of all files
-i, --ignore show files excluded from expansion
-u, --unknown only show unknown (not tracked) files
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns

kwshrink

hg kwshrink [OPTION]... [FILE]...

Must be run before changing/disabling active keywords.

kwshrink refuses to run if given files contain local changes.

Options:

-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns

largefiles

track large binary files

Large binary files tend to be not very compressible, not very diffable, and not at all mergeable. Such files are not handled efficiently by Mercurial's storage format (revlog), which is based on compressed binary deltas; storing large binary files as regular Mercurial files wastes bandwidth and disk space and increases Mercurial's memory usage. The largefiles extension addresses these problems by adding a centralized client-server layer on top of Mercurial: largefiles live in a central store out on the network somewhere, and you only fetch the revisions that you need when you need them.

largefiles works by maintaining a "standin file" in .hglf/ for each largefile. The standins are small (41 bytes: an SHA-1 hash plus newline) and are tracked by Mercurial. Largefile revisions are identified by the SHA-1 hash of their contents, which is written to the standin. largefiles uses that revision ID to get/put largefile revisions from/to the central store. This saves both disk space and bandwidth, since you don't need to retrieve all historical revisions of large files when you clone or pull.

To start a new repository or add new large binary files, just add --large to your hg add command. For example:

$ dd if=/dev/urandom of=randomdata count=2000
$ hg add --large randomdata
$ hg commit -m 'add randomdata as a largefile'

When you push a changeset that adds/modifies largefiles to a remote repository, its largefile revisions will be uploaded along with it. Note that the remote Mercurial must also have the largefiles extension enabled for this to work.

When you pull a changeset that affects largefiles from a remote repository, the largefiles for the changeset will by default not be pulled down. However, when you update to such a revision, any largefiles needed by that revision are downloaded and cached (if they have never been downloaded before). One way to pull largefiles when pulling is thus to use --update, which will update your working copy to the latest pulled revision (and thereby downloading any new largefiles).

If you want to pull largefiles you don't need for update yet, then you can use pull with the --lfrev option or the hg lfpull command.

If you know you are pulling from a non-default location and want to download all the largefiles that correspond to the new changesets at the same time, then you can pull with --lfrev "pulled()".

If you just want to ensure that you will have the largefiles needed to merge or rebase with new heads that you are pulling, then you can pull with --lfrev "head(pulled())" flag to pre-emptively download any largefiles that are new in the heads you are pulling.

Keep in mind that network access may now be required to update to changesets that you have not previously updated to. The nature of the largefiles extension means that updating is no longer guaranteed to be a local-only operation.

If you already have large files tracked by Mercurial without the largefiles extension, you will need to convert your repository in order to benefit from largefiles. This is done with the hg lfconvert command:

$ hg lfconvert --size 10 oldrepo newrepo

In repositories that already have largefiles in them, any new file over 10MB will automatically be added as a largefile. To change this threshold, set largefiles.minsize in your Mercurial config file to the minimum size in megabytes to track as a largefile, or use the --lfsize option to the add command (also in megabytes):

[largefiles]
minsize = 2

$ hg add --lfsize 2

The largefiles.patterns config option allows you to specify a list of filename patterns (see hg help patterns) that should always be tracked as largefiles:

[largefiles]
patterns =
  *.jpg
  re:.*\.(png|bmp)$
  library.zip
  content/audio/*

Files that match one of these patterns will be added as largefiles regardless of their size.

The largefiles.minsize and largefiles.patterns config options will be ignored for any repositories not already containing a largefile. To add the first largefile to a repository, you must explicitly do so with the --large flag passed to the hg add command.

Commands

lfconvert

hg lfconvert SOURCE DEST [FILE ...]

Convert repository SOURCE to a new repository DEST, identical to SOURCE except that certain files will be converted as largefiles: specifically, any file that matches any PATTERN or whose size is above the minimum size threshold is converted as a largefile. The size used to determine whether or not to track a file as a largefile is the size of the first version of the file. The minimum size can be specified either with --size or in configuration as largefiles.size.

After running this command you will need to make sure that largefiles is enabled anywhere you intend to push the new repository.

Use --to-normal to convert largefiles back to normal files; after this, the DEST repository can be used without largefiles at all.

Options:

-s, --size minimum size (MB) for files to be converted as largefiles
--to-normal convert from a largefiles repo to a normal repo

lfpull

hg lfpull -r REV... [-e CMD] [--remotecmd CMD] [SOURCE]

Pull largefiles that are referenced from local changesets but missing locally, pulling from a remote repository to the local cache.

If SOURCE is omitted, the 'default' path will be used. See hg help urls for more information.

Some examples:

  • pull largefiles for all branch heads:

    hg lfpull -r "head() and not closed()"
    
  • pull largefiles on the default branch:

    hg lfpull -r "branch(default)"
    

Options:

-r, --rev pull largefiles for these revisions
-e, --ssh specify ssh command to use
--remotecmd specify hg command to run on the remote side
--insecure do not verify server certificate (ignoring web.cacerts config)

mq

manage a stack of patches

This extension lets you work with a stack of patches in a Mercurial repository. It manages two stacks of patches - all known patches, and applied patches (subset of known patches).

Known patches are represented as patch files in the .hg/patches directory. Applied patches are both patch files and changesets.

Common tasks (use hg help command for more details):

create new patch                          qnew
import existing patch                     qimport

print patch series                        qseries
print applied patches                     qapplied

add known patch to applied stack          qpush
remove patch from applied stack           qpop
refresh contents of top applied patch     qrefresh

By default, mq will automatically use git patches when required to avoid losing file mode changes, copy records, binary files or empty files creations or deletions. This behaviour can be configured with:

[mq]
git = auto/keep/yes/no

If set to 'keep', mq will obey the [diff] section configuration while preserving existing git patches upon qrefresh. If set to 'yes' or 'no', mq will override the [diff] section and always generate git or regular patches, possibly losing data in the second case.

It may be desirable for mq changesets to be kept in the secret phase (see hg help phases), which can be enabled with the following setting:

[mq]
secret = True

You will by default be managing a patch queue named "patches". You can create other, independent patch queues with the hg qqueue command.

If the working directory contains uncommitted files, qpush, qpop and qgoto abort immediately. If -f/--force is used, the changes are discarded. Setting:

[mq]
keepchanges = True

make them behave as if --keep-changes were passed, and non-conflicting local changes will be tolerated and preserved. If incompatible options such as -f/--force or --exact are passed, this setting is ignored.

This extension used to provide a strip command. This command now lives in the strip extension.

Commands

qapplied

hg qapplied [-1] [-s] [PATCH]

Returns 0 on success.

Options:

-1, --last show only the preceding applied patch
-s, --summary print first line of patch header

qclone

hg qclone [OPTION]... SOURCE [DEST]

If source is local, destination will have no patches applied. If source is remote, this command can not check if patches are applied in source, so cannot guarantee that patches are not applied in destination. If you clone remote repository, be sure before that it has no patches applied.

Source patch repository is looked for in <src>/.hg/patches by default. Use -p <url> to change.

The patch directory must be a nested Mercurial repository, as would be created by hg init --mq.

Return 0 on success.

Options:

--pull use pull protocol to copy metadata
-U, --noupdate do not update the new working directories
--uncompressed use uncompressed transfer (fast over LAN)
-p, --patches location of source patch repository
-e, --ssh specify ssh command to use
--remotecmd specify hg command to run on the remote side
--insecure do not verify server certificate (ignoring web.cacerts config)

qcommit

hg qcommit [OPTION]... [FILE]...

This command is deprecated; use hg commit --mq instead.

Options:

-A, --addremove
 mark new/missing files as added/removed before committing
--close-branch mark a branch as closed, hiding it from the branch list
--amend amend the parent of the working dir
-s, --secret use the secret phase for committing
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns
-m, --message use text as commit message
-l, --logfile read commit message from file
-d, --date record the specified date as commit date
-u, --user record the specified user as committer
-S, --subrepos

recurse into subrepositories

aliases: qci

qdelete

hg qdelete [-k] [PATCH]...

The patches must not be applied, and at least one patch is required. Exact patch identifiers must be given. With -k/--keep, the patch files are preserved in the patch directory.

To stop managing a patch and move it into permanent history, use the hg qfinish command.

Options:

-k, --keep keep patch file
-r, --rev

stop managing a revision (DEPRECATED)

aliases: qremove qrm

qdiff

hg qdiff [OPTION]... [FILE]...

Shows a diff which includes the current patch as well as any changes which have been made in the working directory since the last refresh (thus showing what the current patch would become after a qrefresh).

Use hg diff if you only want to see the changes made since the last qrefresh, or hg export qtip if you want to see changes made by the current patch without including changes made since the qrefresh.

Returns 0 on success.

Options:

-a, --text treat all files as text
-g, --git use git extended diff format
--nodates omit dates from diff headers
-p, --show-function
 show which function each change is in
--reverse produce a diff that undoes the changes
-w, --ignore-all-space
 ignore white space when comparing lines
-b, --ignore-space-change
 ignore changes in the amount of white space
-B, --ignore-blank-lines
 ignore changes whose lines are all blank
-U, --unified number of lines of context to show
--stat output diffstat-style summary of changes
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns

qfinish

hg qfinish [-a] [REV]...

Finishes the specified revisions (corresponding to applied patches) by moving them out of mq control into regular repository history.

Accepts a revision range or the -a/--applied option. If --applied is specified, all applied mq revisions are removed from mq control. Otherwise, the given revisions must be at the base of the stack of applied patches.

This can be especially useful if your changes have been applied to an upstream repository, or if you are about to push your changes to upstream.

Returns 0 on success.

Options:

-a, --applied finish all applied changesets

qfold

hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...

Patches must not yet be applied. Each patch will be successively applied to the current patch in the order given. If all the patches apply successfully, the current patch will be refreshed with the new cumulative patch, and the folded patches will be deleted. With -k/--keep, the folded patch files will not be removed afterwards.

The header for each folded patch will be concatenated with the current patch header, separated by a line of * * *.

Returns 0 on success.

Options:

-e, --edit edit patch header
-k, --keep keep folded patch files
-m, --message use text as commit message
-l, --logfile read commit message from file

qgoto

hg qgoto [OPTION]... PATCH

Returns 0 on success.

Options:

--keep-changes tolerate non-conflicting local changes
-f, --force overwrite any local changes
--no-backup do not save backup copies of files

qguard

hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]

Guards control whether a patch can be pushed. A patch with no guards is always pushed. A patch with a positive guard ("+foo") is pushed only if the hg qselect command has activated it. A patch with a negative guard ("-foo") is never pushed if the hg qselect command has activated it.

With no arguments, print the currently active guards. With arguments, set guards for the named patch.

Note

Specifying negative guards now requires '--'.

To set guards on another patch:

hg qguard other.patch -- +2.6.17 -stable

Returns 0 on success.

Options:

-l, --list list all patches and guards
-n, --none drop all guards

qheader

hg qheader [PATCH]

Returns 0 on success.

qimport

hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...

The patch is inserted into the series after the last applied patch. If no patches have been applied, qimport prepends the patch to the series.

The patch will have the same name as its source file unless you give it a new one with -n/--name.

You can register an existing patch inside the patch directory with the -e/--existing flag.

With -f/--force, an existing patch of the same name will be overwritten.

An existing changeset may be placed under mq control with -r/--rev (e.g. qimport --rev . -n patch will place the current revision under mq control). With -g/--git, patches imported with --rev will use the git diff format. See the diffs help topic for information on why this is important for preserving rename/copy information and permission changes. Use hg qfinish to remove changesets from mq control.

To import a patch from standard input, pass - as the patch file. When importing from standard input, a patch name must be specified using the --name flag.

To import an existing patch while renaming it:

hg qimport -e existing-patch -n new-name

Returns 0 if import succeeded.

Options:

-e, --existing import file in patch directory
-n, --name name of patch file
-f, --force overwrite existing files
-r, --rev place existing revisions under mq control
-g, --git use git extended diff format
-P, --push qpush after importing

qinit

hg qinit [-c]

The queue repository is unversioned by default. If -c/--create-repo is specified, qinit will create a separate nested repository for patches (qinit -c may also be run later to convert an unversioned patch repository into a versioned one). You can use qcommit to commit changes to this queue repository.

This command is deprecated. Without -c, it's implied by other relevant commands. With -c, use hg init --mq instead.

Options:

-c, --create-repo
 create queue repository

qnew

hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...

qnew creates a new patch on top of the currently-applied patch (if any). The patch will be initialized with any outstanding changes in the working directory. You may also use -I/--include, -X/--exclude, and/or a list of files after the patch name to add only changes to matching files to the new patch, leaving the rest as uncommitted modifications.

-u/--user and -d/--date can be used to set the (given) user and date, respectively. -U/--currentuser and -D/--currentdate set user to current user and date to current date.

-e/--edit, -m/--message or -l/--logfile set the patch header as well as the commit message. If none is specified, the header is empty and the commit message is '[mq]: PATCH'.

Use the -g/--git option to keep the patch in the git extended diff format. Read the diffs help topic for more information on why this is important for preserving permission changes and copy/rename information.

Returns 0 on successful creation of a new patch.

Options:

-e, --edit edit commit message
-f, --force import uncommitted changes (DEPRECATED)
-g, --git use git extended diff format
-U, --currentuser
 add "From: <current user>" to patch
-u, --user add "From: <USER>" to patch
-D, --currentdate
 add "Date: <current date>" to patch
-d, --date add "Date: <DATE>" to patch
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns
-m, --message use text as commit message
-l, --logfile read commit message from file

qnext

hg qnext [-s]

Returns 0 on success.

Options:

-s, --summary print first line of patch header

qpop

hg qpop [-a] [-f] [PATCH | INDEX]

Without argument, pops off the top of the patch stack. If given a patch name, keeps popping off patches until the named patch is at the top of the stack.

By default, abort if the working directory contains uncommitted changes. With --keep-changes, abort only if the uncommitted files overlap with patched files. With -f/--force, backup and discard changes made to such files.

Return 0 on success.

Options:

-a, --all pop all patches
-n, --name queue name to pop (DEPRECATED)
--keep-changes tolerate non-conflicting local changes
-f, --force forget any local changes to patched files
--no-backup do not save backup copies of files

qprev

hg qprev [-s]

Returns 0 on success.

Options:

-s, --summary print first line of patch header

qpush

hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]

By default, abort if the working directory contains uncommitted changes. With --keep-changes, abort only if the uncommitted files overlap with patched files. With -f/--force, backup and patch over uncommitted changes.

Return 0 on success.

Options:

--keep-changes tolerate non-conflicting local changes
-f, --force apply on top of local changes
-e, --exact apply the target patch to its recorded parent
-l, --list list patch name in commit text
-a, --all apply all patches
-m, --merge merge from another queue (DEPRECATED)
-n, --name merge queue name (DEPRECATED)
--move reorder patch series and apply only the patch
--no-backup do not save backup copies of files

qqueue

hg qqueue [OPTION] [QUEUE]

Supports switching between different patch queues, as well as creating new patch queues and deleting existing ones.

Omitting a queue name or specifying -l/--list will show you the registered queues - by default the "normal" patches queue is registered. The currently active queue will be marked with "(active)". Specifying --active will print only the name of the active queue.

To create a new queue, use -c/--create. The queue is automatically made active, except in the case where there are applied patches from the currently active queue in the repository. Then the queue will only be created and switching will fail.

To delete an existing queue, use --delete. You cannot delete the currently active queue.

Returns 0 on success.

Options:

-l, --list list all available queues
--active print name of active queue
-c, --create create new queue
--rename rename active queue
--delete delete reference to queue
--purge delete queue, and remove patch dir

qrefresh

hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...

If any file patterns are provided, the refreshed patch will contain only the modifications that match those patterns; the remaining modifications will remain in the working directory.

If -s/--short is specified, files currently included in the patch will be refreshed just like matched files and remain in the patch.

If -e/--edit is specified, Mercurial will start your configured editor for you to enter a message. In case qrefresh fails, you will find a backup of your message in .hg/last-message.txt.

hg add/remove/copy/rename work as usual, though you might want to use git-style patches (-g/--git or [diff] git=1) to track copies and renames. See the diffs help topic for more information on the git diff format.

Returns 0 on success.

Options:

-e, --edit edit commit message
-g, --git use git extended diff format
-s, --short refresh only files already in the patch and specified files
-U, --currentuser
 add/update author field in patch with current user
-u, --user add/update author field in patch with given user
-D, --currentdate
 add/update date field in patch with current date
-d, --date add/update date field in patch with given date
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns
-m, --message use text as commit message
-l, --logfile read commit message from file

qrename

hg qrename PATCH1 [PATCH2]

With one argument, renames the current patch to PATCH1. With two arguments, renames PATCH1 to PATCH2.

Returns 0 on success.

aliases: qmv

qrestore

hg qrestore [-d] [-u] REV

This command is deprecated, use hg rebase instead.

Options:

-d, --delete delete save entry
-u, --update update queue working directory

qsave

hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]

This command is deprecated, use hg rebase instead.

Options:

-c, --copy copy patch directory
-n, --name copy directory name
-e, --empty clear queue status file
-f, --force force copy
-m, --message use text as commit message
-l, --logfile read commit message from file

qselect

hg qselect [OPTION]... [GUARD]...

Use the hg qguard command to set or print guards on patch, then use qselect to tell mq which guards to use. A patch will be pushed if it has no guards or any positive guards match the currently selected guard, but will not be pushed if any negative guards match the current guard. For example:

qguard foo.patch -- -stable    (negative guard)
qguard bar.patch    +stable    (positive guard)
qselect stable

This activates the "stable" guard. mq will skip foo.patch (because it has a negative match) but push bar.patch (because it has a positive match).

With no arguments, prints the currently active guards. With one argument, sets the active guard.

Use -n/--none to deactivate guards (no other arguments needed). When no guards are active, patches with positive guards are skipped and patches with negative guards are pushed.

qselect can change the guards on applied patches. It does not pop guarded patches by default. Use --pop to pop back to the last applied patch that is not guarded. Use --reapply (which implies --pop) to push back to the current patch afterwards, but skip guarded patches.

Use -s/--series to print a list of all guards in the series file (no other arguments needed). Use -v for more information.

Returns 0 on success.

Options:

-n, --none disable all guards
-s, --series list all guards in series file
--pop pop to before first guarded applied patch
--reapply pop, then reapply patches

qseries

hg qseries [-ms]

Returns 0 on success.

Options:

-m, --missing print patches not in series
-s, --summary print first line of patch header

qtop

hg qtop [-s]

Returns 0 on success.

Options:

-s, --summary print first line of patch header

qunapplied

hg qunapplied [-1] [-s] [PATCH]

Returns 0 on success.

Options:

-1, --first show only the first patch
-s, --summary print first line of patch header

notify

hooks for sending email push notifications

This extension implements hooks to send email notifications when changesets are sent from or received by the local repository.

First, enable the extension as explained in hg help extensions, and register the hook you want to run. incoming and changegroup hooks are run when changesets are received, while outgoing hooks are for changesets sent to another repository:

[hooks]
# one email for each incoming changeset
incoming.notify = python:hgext.notify.hook
# one email for all incoming changesets
changegroup.notify = python:hgext.notify.hook

# one email for all outgoing changesets
outgoing.notify = python:hgext.notify.hook

This registers the hooks. To enable notification, subscribers must be assigned to repositories. The [usersubs] section maps multiple repositories to a given recipient. The [reposubs] section maps multiple recipients to a single repository:

[usersubs]
# key is subscriber email, value is a comma-separated list of repo patterns
user@host = pattern

[reposubs]
# key is repo pattern, value is a comma-separated list of subscriber emails
pattern = user@host

A pattern is a glob matching the absolute path to a repository, optionally combined with a revset expression. A revset expression, if present, is separated from the glob by a hash. Example:

[reposubs]
*/widgets#branch(release) = qa-team@example.com

This sends to qa-team@example.com whenever a changeset on the release branch triggers a notification in any repository ending in widgets.

In order to place them under direct user management, [usersubs] and [reposubs] sections may be placed in a separate hgrc file and incorporated by reference:

[notify]
config = /path/to/subscriptionsfile

Notifications will not be sent until the notify.test value is set to False; see below.

Notifications content can be tweaked with the following configuration entries:

notify.test
If True, print messages to stdout instead of sending them. Default: True.
notify.sources

Space-separated list of change sources. Notifications are activated only when a changeset's source is in this list. Sources may be:

serve:changesets received via http or ssh
pull:changesets received via hg pull
unbundle:changesets received via hg unbundle
push:changesets sent or received via hg push
bundle:changesets sent via hg unbundle

Default: serve.

notify.strip
Number of leading slashes to strip from url paths. By default, notifications reference repositories with their absolute path. notify.strip lets you turn them into relative paths. For example, notify.strip=3 will change /long/path/repository into repository. Default: 0.
notify.domain
Default email domain for sender or recipients with no explicit domain.
notify.style
Style file to use when formatting emails.
notify.template
Template to use when formatting emails.
notify.incoming
Template to use when run as an incoming hook, overriding notify.template.
notify.outgoing
Template to use when run as an outgoing hook, overriding notify.template.
notify.changegroup
Template to use when running as a changegroup hook, overriding notify.template.
notify.maxdiff
Maximum number of diff lines to include in notification email. Set to 0 to disable the diff, or -1 to include all of it. Default: 300.
notify.maxsubject
Maximum number of characters in email's subject line. Default: 67.
notify.diffstat
Set to True to include a diffstat before diff content. Default: True.
notify.merge
If True, send notifications for merge changesets. Default: True.
notify.mbox
If set, append mails to this mbox file instead of sending. Default: None.
notify.fromauthor
If set, use the committer of the first changeset in a changegroup for the "From" field of the notification mail. If not set, take the user from the pushing repo. Default: False.

If set, the following entries will also be used to customize the notifications:

email.from
Email From address to use if none can be found in the generated email content.
web.baseurl
Root repository URL to combine with repository paths when making references. See also notify.strip.

pager

browse command output with an external pager

To set the pager that should be used, set the application variable:

[pager]
pager = less -FRX

If no pager is set, the pager extensions uses the environment variable $PAGER. If neither pager.pager, nor $PAGER is set, no pager is used.

You can disable the pager for certain commands by adding them to the pager.ignore list:

[pager]
ignore = version, help, update

You can also enable the pager only for certain commands using pager.attend. Below is the default list of commands to be paged:

[pager]
attend = annotate, cat, diff, export, glog, log, qdiff

Setting pager.attend to an empty value will cause all commands to be paged.

If pager.attend is present, pager.ignore will be ignored.

To ignore global commands like hg version or hg help, you have to specify them in your user configuration file.

The --pager=... option can also be used to control when the pager is used. Use a boolean value like yes, no, on, off, or use auto for normal behavior.

patchbomb

command to send changesets as (a series of) patch emails

The series is started off with a "[PATCH 0 of N]" introduction, which describes the series as a whole.

Each patch email has a Subject line of "[PATCH M of N] ...", using the first line of the changeset description as the subject text. The message contains two or three body parts:

  • The changeset description.
  • [Optional] The result of running diffstat on the patch.
  • The patch itself, as generated by hg export.

Each message refers to the first in the series using the In-Reply-To and References headers, so they will show up as a sequence in threaded mail and news readers, and in mail archives.

To configure other defaults, add a section like this to your configuration file:

[email]
from = My Name <my@email>
to = recipient1, recipient2, ...
cc = cc1, cc2, ...
bcc = bcc1, bcc2, ...
reply-to = address1, address2, ...

Use [patchbomb] as configuration section name if you need to override global [email] address settings.

Then you can use the hg email command to mail a series of changesets as a patchbomb.

You can also either configure the method option in the email section to be a sendmail compatible mailer or fill out the [smtp] section so that the patchbomb extension can automatically send patchbombs directly from the commandline. See the [email] and [smtp] sections in hgrc(5) for details.

Commands

email

hg email [OPTION]... [DEST]...

By default, diffs are sent in the format generated by hg export, one per message. The series starts with a "[PATCH 0 of N]" introduction, which describes the series as a whole.

Each patch email has a Subject line of "[PATCH M of N] ...", using the first line of the changeset description as the subject text. The message contains two or three parts. First, the changeset description.

With the -d/--diffstat option, if the diffstat program is installed, the result of running diffstat on the patch is inserted.

Finally, the patch itself, as generated by hg export.

With the -d/--diffstat or --confirm options, you will be presented with a final summary of all messages and asked for confirmation before the messages are sent.

By default the patch is included as text in the email body for easy reviewing. Using the -a/--attach option will instead create an attachment for the patch. With -i/--inline an inline attachment will be created. You can include a patch both as text in the email body and as a regular or an inline attachment by combining the -a/--attach or -i/--inline with the --body option.

With -o/--outgoing, emails will be generated for patches not found in the destination repository (or only those which are ancestors of the specified revisions if any are provided)

With -b/--bundle, changesets are selected as for --outgoing, but a single email containing a binary Mercurial bundle as an attachment will be sent.

With -m/--mbox, instead of previewing each patchbomb message in a pager or sending the messages directly, it will create a UNIX mailbox file with the patch emails. This mailbox file can be previewed with any mail user agent which supports UNIX mbox files.

With -n/--test, all steps will run, but mail will not be sent. You will be prompted for an email recipient address, a subject and an introductory message describing the patches of your patchbomb. Then when all is done, patchbomb messages are displayed. If the PAGER environment variable is set, your pager will be fired up once for each patchbomb message, so you can verify everything is alright.

In case email sending fails, you will find a backup of your series introductory message in .hg/last-email.txt.

Examples:

hg email -r 3000          # send patch 3000 only
hg email -r 3000 -r 3001  # send patches 3000 and 3001
hg email -r 3000:3005     # send patches 3000 through 3005
hg email 3000             # send patch 3000 (deprecated)

hg email -o               # send all patches not in default
hg email -o DEST          # send all patches not in DEST
hg email -o -r 3000       # send all ancestors of 3000 not in default
hg email -o -r 3000 DEST  # send all ancestors of 3000 not in DEST

hg email -b               # send bundle of all patches not in default
hg email -b DEST          # send bundle of all patches not in DEST
hg email -b -r 3000       # bundle of all ancestors of 3000 not in default
hg email -b -r 3000 DEST  # bundle of all ancestors of 3000 not in DEST

hg email -o -m mbox &&    # generate an mbox file...
  mutt -R -f mbox         # ... and view it with mutt
hg email -o -m mbox &&    # generate an mbox file ...
  formail -s sendmail \   # ... and use formail to send from the mbox
    -bm -t < mbox         # ... using sendmail

Before using this command, you will need to enable email in your hgrc. See the [email] section in hgrc(5) for details.

Options:

-g, --git use git extended diff format
--plain omit hg patch header
-o, --outgoing send changes not found in the target repository
-b, --bundle send changes not in target as a binary bundle
--bundlename name of the bundle attachment file (default: bundle)
-r, --rev a revision to send
--force run even when remote repository is unrelated (with -b/--bundle)
--base a base changeset to specify instead of a destination (with -b/--bundle)
--intro send an introduction email for a single patch
--body send patches as inline message text (default)
-a, --attach send patches as attachments
-i, --inline send patches as inline attachments
--bcc email addresses of blind carbon copy recipients
-c, --cc email addresses of copy recipients
--confirm ask for confirmation before sending
-d, --diffstat add diffstat output to messages
--date use the given date as the sending date
--desc use the given file as the series description
-f, --from email address of sender
-n, --test print messages that would be sent
-m, --mbox write messages to mbox file instead of sending them
--reply-to email addresses replies should be sent to
-s, --subject subject of first message (intro or single patch)
--in-reply-to message identifier to reply to
--flag flags to add in subject prefixes
-t, --to email addresses of recipients
-e, --ssh specify ssh command to use
--remotecmd specify hg command to run on the remote side
--insecure do not verify server certificate (ignoring web.cacerts config)

progress

show progress bars for some actions

This extension uses the progress information logged by hg commands to draw progress bars that are as informative as possible. Some progress bars only offer indeterminate information, while others have a definite end point.

The following settings are available:

[progress]
delay = 3 # number of seconds (float) before showing the progress bar
changedelay = 1 # changedelay: minimum delay before showing a new topic.
                # If set to less than 3 * refresh, that value will
                # be used instead.
refresh = 0.1 # time in seconds between refreshes of the progress bar
format = topic bar number estimate # format of the progress bar
width = <none> # if set, the maximum width of the progress information
               # (that is, min(width, term width) will be used)
clear-complete = True # clear the progress bar after it's done
disable = False # if true, don't show a progress bar
assume-tty = False # if true, ALWAYS show a progress bar, unless
                   # disable is given

Valid entries for the format field are topic, bar, number, unit, estimate, speed, and item. item defaults to the last 20 characters of the item, but this can be changed by adding either -<num> which would take the last num characters, or +<num> for the first num characters.

purge

command to delete untracked files from the working directory

Commands

purge

hg purge [OPTION]... [DIR]...

Delete files not known to Mercurial. This is useful to test local and uncommitted changes in an otherwise-clean source tree.

This means that purge will delete:

  • Unknown files: files marked with "?" by hg status
  • Empty directories: in fact Mercurial ignores directories unless they contain files under source control management

But it will leave untouched:

  • Modified and unmodified tracked files
  • Ignored files (unless --all is specified)
  • New files added to the repository (with hg add)

If directories are given on the command line, only files in these directories are considered.

Be careful with purge, as you could irreversibly delete some files you forgot to add to the repository. If you only want to print the list of files that this program would delete, use the --print option.

Options:

-a, --abort-on-err
 abort if an error occurs
--all purge ignored files too
-p, --print print filenames instead of deleting them
-0, --print0 end filenames with NUL, for use with xargs (implies -p/--print)
-I, --include include names matching the given patterns
-X, --exclude

exclude names matching the given patterns

aliases: clean

rebase

command to move sets of revisions to a different ancestor

This extension lets you rebase changesets in an existing Mercurial repository.

For more information: http://mercurial.selenic.com/wiki/RebaseExtension

Commands

rebase

hg rebase [-s REV | -b REV] [-d REV] [OPTION]

Rebase uses repeated merging to graft changesets from one part of history (the source) onto another (the destination). This can be useful for linearizing local changes relative to a master development tree.

You should not rebase changesets that have already been shared with others. Doing so will force everybody else to perform the same rebase or they will end up with duplicated changesets after pulling in your rebased changesets.

In its default configuration, Mercurial will prevent you from rebasing published changes. See hg help phases for details.

If you don't specify a destination changeset (-d/--dest), rebase uses the current branch tip as the destination. (The destination changeset is not modified by rebasing, but new changesets are added as its descendants.)

You can specify which changesets to rebase in two ways: as a "source" changeset or as a "base" changeset. Both are shorthand for a topologically related set of changesets (the "source branch"). If you specify source (-s/--source), rebase will rebase that changeset and all of its descendants onto dest. If you specify base (-b/--base), rebase will select ancestors of base back to but not including the common ancestor with dest. Thus, -b is less precise but more convenient than -s: you can specify any changeset in the source branch, and rebase will select the whole branch. If you specify neither -s nor -b, rebase uses the parent of the working directory as the base.

For advanced usage, a third way is available through the --rev option. It allows you to specify an arbitrary set of changesets to rebase. Descendants of revs you specify with this option are not automatically included in the rebase.

By default, rebase recreates the changesets in the source branch as descendants of dest and then destroys the originals. Use --keep to preserve the original source changesets. Some changesets in the source branch (e.g. merges from the destination branch) may be dropped if they no longer contribute any change.

One result of the rules for selecting the destination changeset and source branch is that, unlike merge, rebase will do nothing if you are at the branch tip of a named branch with two heads. You need to explicitly specify source and/or destination (or update to the other head, if it's the head of the intended source branch).

If a rebase is interrupted to manually resolve a merge, it can be continued with --continue/-c or aborted with --abort/-a.

Returns 0 on success, 1 if nothing to rebase or there are unresolved conflicts.

Options:

-s, --source rebase from the specified changeset
-b, --base rebase from the base of the specified changeset (up to greatest common ancestor of base and dest)
-r, --rev rebase these revisions
-d, --dest rebase onto the specified changeset
--collapse collapse the rebased changesets
-m, --message use text as collapse commit message
-e, --edit invoke editor on commit messages
-l, --logfile read collapse commit message from file
--keep keep original changesets
--keepbranches keep original branch names
-D, --detach (DEPRECATED)
-t, --tool specify merge tool
-c, --continue continue an interrupted rebase
-a, --abort abort an interrupted rebase
--style display using template map file
--template display with template

record

commands to interactively select changes for commit/qrefresh

Commands

qrecord

hg qrecord [OPTION]... PATCH [FILE]...

See hg help qnew & hg help record for more information and usage.

record

hg record [OPTION]... [FILE]...

If a list of files is omitted, all changes reported by hg status will be candidates for recording.

See hg help dates for a list of formats valid for -d/--date.

You will be prompted for whether to record changes to each modified file, and for files with multiple changes, for each change to use. For each query, the following responses are possible:

y - record this change
n - skip this change
e - edit this change manually

s - skip remaining changes to this file
f - record remaining changes to this file

d - done, skip remaining changes and files
a - record all changes to all remaining files
q - quit, recording no changes

? - display help

This command is not available when committing a merge.

Options:

-A, --addremove
 mark new/missing files as added/removed before committing
--close-branch mark a branch as closed, hiding it from the branch list
--amend amend the parent of the working dir
-s, --secret use the secret phase for committing
-I, --include include names matching the given patterns
-X, --exclude exclude names matching the given patterns
-m, --message use text as commit message
-l, --logfile read commit message from file
-d, --date record the specified date as commit date
-u, --user record the specified user as committer
-S, --subrepos recurse into subrepositories
-w, --ignore-all-space
 ignore white space when comparing lines
-b, --ignore-space-change
 ignore changes in the amount of white space
-B, --ignore-blank-lines
 ignore changes whose lines are all blank

schemes

extend schemes with shortcuts to repository swarms

This extension allows you to specify shortcuts for parent URLs with a lot of repositories to act like a scheme, for example:

[schemes]
py = http://code.python.org/hg/

After that you can use it like:

hg clone py://trunk/

Additionally there is support for some more complex schemas, for example used by Google Code:

[schemes]
gcode = http://{1}.googlecode.com/hg/

The syntax is taken from Mercurial templates, and you have unlimited number of variables, starting with {1} and continuing with {2}, {3} and so on. This variables will receive parts of URL supplied, split by /. Anything not specified as {part} will be just appended to an URL.

For convenience, the extension adds these schemes by default:

[schemes]
py = http://hg.python.org/
bb = https://bitbucket.org/
bb+ssh = ssh://hg@bitbucket.org/
gcode = https://{1}.googlecode.com/hg/
kiln = https://{1}.kilnhg.com/Repo/

You can override a predefined scheme by defining a new scheme with the same name.

share

share a common history between several working directories

Commands

share

hg share [-U] SOURCE [DEST]

Initialize a new repository and working directory that shares its history with another repository.

Note

using rollback or extensions that destroy/modify history (mq, rebase, etc.) can cause considerable confusion with shared clones. In particular, if two shared clones are both updated to the same changeset, and one of them destroys that changeset with rollback, the other clone will suddenly stop working: all operations will fail with "abort: working directory has unknown parent". The only known workaround is to use debugsetparents on the broken clone to reset it to a changeset that still exists.

Options:

-U, --noupdate do not create a working copy

unshare

hg unshare

Copy the store data to the repo and remove the sharedpath data.

shelve

save and restore changes to the working directory

The "hg shelve" command saves changes made to the working directory and reverts those changes, resetting the working directory to a clean state.

Later on, the "hg unshelve" command restores the changes saved by "hg shelve". Changes can be restored even after updating to a different parent, in which case Mercurial's merge machinery will resolve any conflicts if necessary.

You can have more than one shelved change outstanding at a time; each shelved change has a distinct name. For details, see the help for "hg shelve".

Commands

shelve

hg shelve

Shelving takes files that "hg status" reports as not clean, saves the modifications to a bundle (a shelved change), and reverts the files so that their state in the working directory becomes clean.

To restore these changes to the working directory, using "hg unshelve"; this will work even if you switch to a different commit.

When no files are specified, "hg shelve" saves all not-clean files. If specific files or directories are named, only changes to those files are shelved.

Each shelved change has a name that makes it easier to find later. The name of a shelved change defaults to being based on the active bookmark, or if there is no active bookmark, the current named branch. To specify a different name, use --name.

To see a list of existing shelved changes, use the --list option. For each shelved change, this will print its name, age, and description; use --patch or --stat for more details.

To delete specific shelved changes, use --delete. To delete all shelved changes, use --cleanup.

Options:

-A, --addremove
 mark new/missing files as added/removed before shelving
--cleanup delete all shelved changes
--date shelve with the specified commit date
-d, --delete delete the named shelved change(s)
-l, --list list current shelves
-m, --message use text as shelve message
-n, --name use the given name for the shelved commit
-p, --patch show patch
--stat output diffstat-style summary of changes

unshelve

hg unshelve [SHELVED]

This command accepts an optional name of a shelved change to restore. If none is given, the most recent shelved change is used.

If a shelved change is applied successfully, the bundle that contains the shelved changes is deleted afterwards.

Since you can restore a shelved change on top of an arbitrary commit, it is possible that unshelving will result in a conflict between your changes and the commits you are unshelving onto. If this occurs, you must resolve the conflict, then use --continue to complete the unshelve operation. (The bundle will not be deleted until you successfully complete the unshelve.)

(Alternatively, you can use --abort to abandon an unshelve that causes a conflict. This reverts the unshelved changes, and does not delete the bundle.)

Options:

-a, --abort abort an incomplete unshelve operation
-c, --continue continue an incomplete unshelve operation
--keep keep shelve after unshelving

strip

strip changesets and their descendents from history

This extension allows you to strip changesets and all their descendants from the repository. See the command help for details.

Commands

strip

hg strip [-k] [-f] [-n] [-B bookmark] [-r] REV...

The strip command removes the specified changesets and all their descendants. If the working directory has uncommitted changes, the operation is aborted unless the --force flag is supplied, in which case changes will be discarded.

If a parent of the working directory is stripped, then the working directory will automatically be updated to the most recent available ancestor of the stripped parent after the operation completes.

Any stripped changesets are stored in .hg/strip-backup as a bundle (see hg help bundle and hg help unbundle). They can be restored by running hg unbundle .hg/strip-backup/BUNDLE, where BUNDLE is the bundle file created by the strip. Note that the local revision numbers will in general be different after the restore.

Use the --no-backup option to discard the backup bundle once the operation completes.

Strip is not a history-rewriting operation and can be used on changesets in the public phase. But if the stripped changesets have been pushed to a remote repository you will likely pull them again.

Return 0 on success.

Options:

-r, --rev strip specified revision (optional, can specify revisions without this option)
-f, --force force removal of changesets, discard uncommitted changes (no backup)
-b, --backup bundle only changesets with local revision number greater than REV which are not descendants of REV (DEPRECATED)
--no-backup no backups
--nobackup no backups (DEPRECATED)
-n ignored (DEPRECATED)
-k, --keep do not modify working copy during strip
-B, --bookmark remove revs only reachable from given bookmark

transplant

command to transplant changesets from another branch

This extension allows you to transplant changes to another parent revision, possibly in another repository. The transplant is done using 'diff' patches.

Transplanted patches are recorded in .hg/transplant/transplants, as a map from a changeset hash to its hash in the source repository.

Commands

transplant

hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] [-m REV] [REV]...

Selected changesets will be applied on top of the current working directory with the log of the original changeset. The changesets are copied and will thus appear twice in the history with different identities.

Consider using the graft command if everything is inside the same repository - it will use merges and will usually give a better result. Use the rebase extension if the changesets are unpublished and you want to move them instead of copying them.

If --log is specified, log messages will have a comment appended of the form:

(transplanted from CHANGESETHASH)

You can rewrite the changelog message with the --filter option. Its argument will be invoked with the current changelog message as $1 and the patch as $2.

--source/-s specifies another repository to use for selecting changesets, just as if it temporarily had been pulled. If --branch/-b is specified, these revisions will be used as heads when deciding which changesets to transplant, just as if only these revisions had been pulled. If --all/-a is specified, all the revisions up to the heads specified with --branch will be transplanted.

Example:

  • transplant all changes up to REV on top of your current revision:

    hg transplant --branch REV --all
    

You can optionally mark selected transplanted changesets as merge changesets. You will not be prompted to transplant any ancestors of a merged transplant, and you can merge descendants of them normally instead of transplanting them.

Merge changesets may be transplanted directly by specifying the proper parent changeset by calling hg transplant --parent.

If no merges or revisions are provided, hg transplant will start an interactive changeset browser.

If a changeset application fails, you can fix the merge by hand and then resume where you left off by calling hg transplant --continue/-c.

Options:

-s, --source transplant changesets from REPO
-b, --branch use this source changeset as head
-a, --all pull all changesets up to the --branch revisions
-p, --prune skip over REV
-m, --merge merge at REV
--parent parent to choose when transplanting merge
-e, --edit invoke editor on commit messages
--log append transplant info to log message
-c, --continue continue last transplant session after fixing conflicts
--filter filter changesets through command

win32mbcs

allow the use of MBCS paths with problematic encodings

Some MBCS encodings are not good for some path operations (i.e. splitting path, case conversion, etc.) with its encoded bytes. We call such a encoding (i.e. shift_jis and big5) as "problematic encoding". This extension can be used to fix the issue with those encodings by wrapping some functions to convert to Unicode string before path operation.

This extension is useful for:

  • Japanese Windows users using shift_jis encoding.
  • Chinese Windows users using big5 encoding.
  • All users who use a repository with one of problematic encodings on case-insensitive file system.

This extension is not needed for:

  • Any user who use only ASCII chars in path.
  • Any user who do not use any of problematic encodings.

Note that there are some limitations on using this extension:

  • You should use single encoding in one repository.
  • If the repository path ends with 0x5c, .hg/hgrc cannot be read.
  • win32mbcs is not compatible with fixutf8 extension.

By default, win32mbcs uses encoding.encoding decided by Mercurial. You can specify the encoding by config option:

[win32mbcs]
encoding = sjis

It is useful for the users who want to commit with UTF-8 log message.

win32text

perform automatic newline conversion

Deprecation: The win32text extension requires each user to configure the extension again and again for each clone since the configuration is not copied when cloning.

We have therefore made the eol as an alternative. The eol uses a version controlled file for its configuration and each clone will therefore use the right settings from the start.

To perform automatic newline conversion, use:

[extensions]
win32text =
[encode]
** = cleverencode:
# or ** = macencode:

[decode]
** = cleverdecode:
# or ** = macdecode:

If not doing conversion, to make sure you do not commit CRLF/CR by accident:

[hooks]
pretxncommit.crlf = python:hgext.win32text.forbidcrlf
# or pretxncommit.cr = python:hgext.win32text.forbidcr

To do the same check on a server to prevent CRLF/CR from being pushed or pulled:

[hooks]
pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf
# or pretxnchangegroup.cr = python:hgext.win32text.forbidcr

zeroconf

discover and advertise repositories on the local network

Zeroconf-enabled repositories will be announced in a network without the need to configure a server or a service. They can be discovered without knowing their actual IP address.

To allow other people to discover your repository using run hg serve in your repository:

$ cd test
$ hg serve

You can discover Zeroconf-enabled repositories by running hg paths:

$ hg paths
zc-test = http://example.com:8000/test

Files

/etc/mercurial/hgrc, $HOME/.hgrc, .hg/hgrc
This file contains defaults and configuration. Values in .hg/hgrc override those in $HOME/.hgrc, and these override settings made in the global /etc/mercurial/hgrc configuration. See hgrc(5) for details of the contents and format of these files.
.hgignore
This file contains regular expressions (one per line) that describe file names that should be ignored by hg. For details, see hgignore(5).
.hgsub
This file defines the locations of all subrepositories, and tells where the subrepository checkouts came from. For details, see hg help subrepos.
.hgsubstate
This file is where Mercurial stores all nested repository states. NB: This file should not be edited manually.
.hgtags
This file contains changeset hash values and text tag names (one of each separated by spaces) that correspond to tagged versions of the repository contents. The file content is encoded using UTF-8.
.hg/last-message.txt
This file is used by hg commit to store a backup of the commit message in case the commit fails.
.hg/localtags
This file can be used to define local tags which are not shared among repositories. The file format is the same as for .hgtags, but it is encoded using the local system encoding.

Some commands (e.g. revert) produce backup files ending in .orig, if the .orig file already exists and is not tracked by Mercurial, it will be overwritten.

Bugs

Probably lots, please post them to the mailing list (see Resources below) when you find them.

Author

Written by Matt Mackall <mpm@selenic.com>

Copying

Copyright (C) 2005-2013 Matt Mackall. Free use of this software is granted under the terms of the GNU General Public License version 2 or any later version.

mercurial-2.8.2/doc/README0000644000175000017500000000064012261160437016743 0ustar oxymoronoxymoron00000000000000Mercurial's documentation is kept in reStructuredText format, which is a simple plain text format that's easy to read and edit: http://docutils.sourceforge.net/rst.html It's also convertible to a variety of other formats including standard UNIX man page format and HTML. You'll need to install Docutils: http://docutils.sourceforge.net/ Use the Makefile in this directory to generate the man and HTML pages. mercurial-2.8.2/doc/check-seclevel.py0000644000175000017500000001323612261160437021317 0ustar oxymoronoxymoron00000000000000#!/usr/bin/env python # # checkseclevel - checking section title levels in each online help documents import sys, os import optparse # import from the live mercurial repo sys.path.insert(0, "..") # fall back to pure modules if required C extensions are not available sys.path.append(os.path.join('..', 'mercurial', 'pure')) from mercurial import demandimport; demandimport.enable() from mercurial.commands import table from mercurial.help import helptable from mercurial import extensions from mercurial import minirst from mercurial import util _verbose = False def verbose(msg): if _verbose: print msg def error(msg): sys.stderr.write('%s\n' % msg) level2mark = ['"', '=', '-', '.', '#'] reservedmarks = ['"'] mark2level = {} for m, l in zip(level2mark, xrange(len(level2mark))): if m not in reservedmarks: mark2level[m] = l initlevel_topic = 0 initlevel_cmd = 1 initlevel_ext = 1 initlevel_ext_cmd = 3 def showavailables(initlevel): error(' available marks and order of them in this help: %s' % (', '.join(['%r' % (m * 4) for m in level2mark[initlevel + 1:]]))) def checkseclevel(doc, name, initlevel): verbose('checking "%s"' % name) blocks, pruned = minirst.parse(doc, 0, ['verbose']) errorcnt = 0 curlevel = initlevel for block in blocks: if block['type'] != 'section': continue mark = block['underline'] title = block['lines'][0] if (mark not in mark2level) or (mark2level[mark] <= initlevel): error('invalid section mark %r for "%s" of %s' % (mark * 4, title, name)) showavailables(initlevel) errorcnt += 1 continue nextlevel = mark2level[mark] if curlevel < nextlevel and curlevel + 1 != nextlevel: error('gap of section level at "%s" of %s' % (title, name)) showavailables(initlevel) errorcnt += 1 continue verbose('appropriate section level for "%s %s"' % (mark * (nextlevel * 2), title)) curlevel = nextlevel return errorcnt def checkcmdtable(cmdtable, namefmt, initlevel): errorcnt = 0 for k, entry in cmdtable.items(): name = k.split("|")[0].lstrip("^") if not entry[0].__doc__: verbose('skip checking %s: no help document' % (namefmt % name)) continue errorcnt += checkseclevel(entry[0].__doc__, namefmt % name, initlevel) return errorcnt def checkhghelps(): errorcnt = 0 for names, sec, doc in helptable: if util.safehasattr(doc, '__call__'): doc = doc() errorcnt += checkseclevel(doc, '%s help topic' % names[0], initlevel_topic) errorcnt += checkcmdtable(table, '%s command', initlevel_cmd) for name in sorted(extensions.enabled().keys() + extensions.disabled().keys()): mod = extensions.load(None, name, None) if not mod.__doc__: verbose('skip checking %s extension: no help document' % name) continue errorcnt += checkseclevel(mod.__doc__, '%s extension' % name, initlevel_ext) cmdtable = getattr(mod, 'cmdtable', None) if cmdtable: errorcnt += checkcmdtable(cmdtable, '%s command of ' + name + ' extension', initlevel_ext_cmd) return errorcnt def checkfile(filename, initlevel): if filename == '-': filename = 'stdin' doc = sys.stdin.read() else: fp = open(filename) try: doc = fp.read() finally: fp.close() verbose('checking input from %s with initlevel %d' % (filename, initlevel)) return checkseclevel(doc, 'input from %s' % filename, initlevel) if __name__ == "__main__": optparser = optparse.OptionParser("""%prog [options] This checks all help documents of Mercurial (topics, commands, extensions and commands of them), if no file is specified by --file option. """) optparser.add_option("-v", "--verbose", help="enable additional output", action="store_true") optparser.add_option("-f", "--file", help="filename to read in (or '-' for stdin)", action="store", default="") optparser.add_option("-t", "--topic", help="parse file as help topic", action="store_const", dest="initlevel", const=0) optparser.add_option("-c", "--command", help="parse file as help of core command", action="store_const", dest="initlevel", const=1) optparser.add_option("-e", "--extension", help="parse file as help of extension", action="store_const", dest="initlevel", const=1) optparser.add_option("-C", "--extension-command", help="parse file as help of extension command", action="store_const", dest="initlevel", const=3) optparser.add_option("-l", "--initlevel", help="set initial section level manually", action="store", type="int", default=0) (options, args) = optparser.parse_args() _verbose = options.verbose if options.file: if checkfile(options.file, options.initlevel): sys.exit(1) else: if checkhghelps(): sys.exit(1) mercurial-2.8.2/doc/hgrc.50000644000175000017500000016146112261160450017100 0ustar oxymoronoxymoron00000000000000.\" Man page generated from reStructuredText. . .TH HGRC 5 "" "" "Mercurial Manual" .SH NAME hgrc \- configuration files for Mercurial . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH DESCRIPTION .sp The Mercurial system uses a set of configuration files to control aspects of its behavior. .sp The configuration files use a simple ini\-file format. A configuration file consists of sections, led by a \fB[section]\fP header and followed by \fBname = value\fP entries: .sp .nf .ft C [ui] username = Firstname Lastname verbose = True .ft P .fi .sp The above entries will be referred to as \fBui.username\fP and \fBui.verbose\fP, respectively. See the Syntax section below. .SH FILES .sp Mercurial reads configuration data from several files, if they exist. These files do not exist by default and you will have to create the appropriate configuration files yourself: global configuration like the username setting is typically put into \fB%USERPROFILE%\emercurial.ini\fP or \fB$HOME/.hgrc\fP and local configuration is put into the per\-repository \fB/.hg/hgrc\fP file. .sp The names of these files depend on the system on which Mercurial is installed. \fB*.rc\fP files from a single directory are read in alphabetical order, later ones overriding earlier ones. Where multiple paths are given below, settings from earlier paths override later ones. .sp .nf (All) \fB/.hg/hgrc\fP .fi .sp .INDENT 0.0 .INDENT 3.5 .sp Per\-repository configuration options that only apply in a particular repository. This file is not version\-controlled, and will not get transferred during a "clone" operation. Options in this file override options in all other configuration files. On Plan 9 and Unix, most of this file will be ignored if it doesn\(aqt belong to a trusted user or to a trusted group. See the documentation for the \fB[trusted]\fP section below for more details. .UNINDENT .UNINDENT .sp .nf (Plan 9) \fB$home/lib/hgrc\fP (Unix) \fB$HOME/.hgrc\fP (Windows) \fB%USERPROFILE%\e.hgrc\fP (Windows) \fB%USERPROFILE%\eMercurial.ini\fP (Windows) \fB%HOME%\e.hgrc\fP (Windows) \fB%HOME%\eMercurial.ini\fP .fi .sp .INDENT 0.0 .INDENT 3.5 .sp Per\-user configuration file(s), for the user running Mercurial. On Windows 9x, \fB%HOME%\fP is replaced by \fB%APPDATA%\fP. Options in these files apply to all Mercurial commands executed by this user in any directory. Options in these files override per\-system and per\-installation options. .UNINDENT .UNINDENT .sp .nf (Plan 9) \fB/lib/mercurial/hgrc\fP (Plan 9) \fB/lib/mercurial/hgrc.d/*.rc\fP (Unix) \fB/etc/mercurial/hgrc\fP (Unix) \fB/etc/mercurial/hgrc.d/*.rc\fP .fi .sp .INDENT 0.0 .INDENT 3.5 .sp Per\-system configuration files, for the system on which Mercurial is running. Options in these files apply to all Mercurial commands executed by any user in any directory. Options in these files override per\-installation options. .UNINDENT .UNINDENT .sp .nf (Plan 9) \fB/lib/mercurial/hgrc\fP (Plan 9) \fB/lib/mercurial/hgrc.d/*.rc\fP (Unix) \fB/etc/mercurial/hgrc\fP (Unix) \fB/etc/mercurial/hgrc.d/*.rc\fP .fi .sp .INDENT 0.0 .INDENT 3.5 .sp Per\-installation configuration files, searched for in the directory where Mercurial is installed. \fB\fP is the parent directory of the \fBhg\fP executable (or symlink) being run. For example, if installed in \fB/shared/tools/bin/hg\fP, Mercurial will look in \fB/shared/tools/etc/mercurial/hgrc\fP. Options in these files apply to all Mercurial commands executed by any user in any directory. .UNINDENT .UNINDENT .sp .nf (Windows) \fB\eMercurial.ini\fP \fBor\fP (Windows) \fB\ehgrc.d\e*.rc\fP \fBor\fP (Windows) \fBHKEY_LOCAL_MACHINE\eSOFTWARE\eMercurial\fP .fi .sp .INDENT 0.0 .INDENT 3.5 .sp Per\-installation/system configuration files, for the system on which Mercurial is running. Options in these files apply to all Mercurial commands executed by any user in any directory. Registry keys contain PATH\-like strings, every part of which must reference a \fBMercurial.ini\fP file or be a directory where \fB*.rc\fP files will be read. Mercurial checks each of these locations in the specified order until one or more configuration files are detected. .UNINDENT .UNINDENT .IP Note . The registry key \fBHKEY_LOCAL_MACHINE\eSOFTWARE\eWow6432Node\eMercurial\fP is used when running 32\-bit Python on 64\-bit Windows. .RE .SH SYNTAX .sp A configuration file consists of sections, led by a \fB[section]\fP header and followed by \fBname = value\fP entries (sometimes called \fBconfiguration keys\fP): .sp .nf .ft C [spam] eggs=ham green= eggs .ft P .fi .sp Each line contains one entry. If the lines that follow are indented, they are treated as continuations of that entry. Leading whitespace is removed from values. Empty lines are skipped. Lines beginning with \fB#\fP or \fB;\fP are ignored and may be used to provide comments. .sp Configuration keys can be set multiple times, in which case Mercurial will use the value that was configured last. As an example: .sp .nf .ft C [spam] eggs=large ham=serrano eggs=small .ft P .fi .sp This would set the configuration key named \fBeggs\fP to \fBsmall\fP. .sp It is also possible to define a section multiple times. A section can be redefined on the same and/or on different configuration files. For example: .sp .nf .ft C [foo] eggs=large ham=serrano eggs=small [bar] eggs=ham green= eggs [foo] ham=prosciutto eggs=medium bread=toasted .ft P .fi .sp This would set the \fBeggs\fP, \fBham\fP, and \fBbread\fP configuration keys of the \fBfoo\fP section to \fBmedium\fP, \fBprosciutto\fP, and \fBtoasted\fP, respectively. As you can see there only thing that matters is the last value that was set for each of the configuration keys. .sp If a configuration key is set multiple times in different configuration files the final value will depend on the order in which the different configuration files are read, with settings from earlier paths overriding later ones as described on the \fBFiles\fP section above. .sp A line of the form \fB%include file\fP will include \fBfile\fP into the current configuration file. The inclusion is recursive, which means that included files can include other files. Filenames are relative to the configuration file in which the \fB%include\fP directive is found. Environment variables and \fB~user\fP constructs are expanded in \fBfile\fP. This lets you do something like: .sp .nf .ft C %include ~/.hgrc.d/$HOST.rc .ft P .fi .sp to include a different configuration file on each computer you use. .sp A line with \fB%unset name\fP will remove \fBname\fP from the current section, if it has been set previously. .sp The values are either free\-form text strings, lists of text strings, or Boolean values. Boolean values can be set to true using any of "1", "yes", "true", or "on" and to false using "0", "no", "false", or "off" (all case insensitive). .sp List values are separated by whitespace or comma, except when values are placed in double quotation marks: .sp .nf .ft C allow_read = "John Doe, PhD", brian, betty .ft P .fi .sp Quotation marks can be escaped by prefixing them with a backslash. Only quotation marks at the beginning of a word is counted as a quotation (e.g., \fBfoo"bar baz\fP is the list of \fBfoo"bar\fP and \fBbaz\fP). .SH SECTIONS .sp This section describes the different sections that may appear in a Mercurial configuration file, the purpose of each section, its possible keys, and their possible values. .SS \fBalias\fP .sp Defines command aliases. Aliases allow you to define your own commands in terms of other commands (or aliases), optionally including arguments. Positional arguments in the form of \fB$1\fP, \fB$2\fP, etc in the alias definition are expanded by Mercurial before execution. Positional arguments not already used by \fB$N\fP in the definition are put at the end of the command to be executed. .sp Alias definitions consist of lines of the form: .sp .nf .ft C = []... .ft P .fi .sp For example, this definition: .sp .nf .ft C latest = log \-\-limit 5 .ft P .fi .sp creates a new command \fBlatest\fP that shows only the five most recent changesets. You can define subsequent aliases using earlier ones: .sp .nf .ft C stable5 = latest \-b stable .ft P .fi .IP Note . It is possible to create aliases with the same names as existing commands, which will then override the original definitions. This is almost always a bad idea! .RE .sp An alias can start with an exclamation point (\fB!\fP) to make it a shell alias. A shell alias is executed with the shell and will let you run arbitrary commands. As an example, .sp .nf .ft C echo = !echo $@ .ft P .fi .sp will let you do \fBhg echo foo\fP to have \fBfoo\fP printed in your terminal. A better example might be: .sp .nf .ft C purge = !$HG status \-\-no\-status \-\-unknown \-0 | xargs \-0 rm .ft P .fi .sp which will make \fBhg purge\fP delete all unknown files in the repository in the same manner as the purge extension. .sp Positional arguments like \fB$1\fP, \fB$2\fP, etc. in the alias definition expand to the command arguments. Unmatched arguments are removed. \fB$0\fP expands to the alias name and \fB$@\fP expands to all arguments separated by a space. These expansions happen before the command is passed to the shell. .sp Shell aliases are executed in an environment where \fB$HG\fP expands to the path of the Mercurial that was used to execute the alias. This is useful when you want to call further Mercurial commands in a shell alias, as was done above for the purge alias. In addition, \fB$HG_ARGS\fP expands to the arguments given to Mercurial. In the \fBhg echo foo\fP call above, \fB$HG_ARGS\fP would expand to \fBecho foo\fP. .IP Note . Some global configuration options such as \fB\-R\fP are processed before shell aliases and will thus not be passed to aliases. .RE .SS \fBannotate\fP .sp Settings used when displaying file annotations. All values are Booleans and default to False. See \fBdiff\fP section for related options for the diff command. .INDENT 0.0 .TP .B \fBignorews\fP .sp Ignore white space when comparing lines. .TP .B \fBignorewsamount\fP .sp Ignore changes in the amount of white space. .TP .B \fBignoreblanklines\fP .sp Ignore changes whose lines are all blank. .UNINDENT .SS \fBauth\fP .sp Authentication credentials for HTTP authentication. This section allows you to store usernames and passwords for use when logging \fIinto\fP HTTP servers. See the \fB[web]\fP configuration section if you want to configure \fIwho\fP can login to your HTTP server. .sp Each line has the following format: .sp .nf .ft C . = .ft P .fi .sp where \fB\fP is used to group arguments into authentication entries. Example: .sp .nf .ft C foo.prefix = hg.intevation.org/mercurial foo.username = foo foo.password = bar foo.schemes = http https bar.prefix = secure.example.org bar.key = path/to/file.key bar.cert = path/to/file.cert bar.schemes = https .ft P .fi .sp Supported arguments: .INDENT 0.0 .TP .B \fBprefix\fP .sp Either \fB*\fP or a URI prefix with or without the scheme part. The authentication entry with the longest matching prefix is used (where \fB*\fP matches everything and counts as a match of length 1). If the prefix doesn\(aqt include a scheme, the match is performed against the URI with its scheme stripped as well, and the schemes argument, q.v., is then subsequently consulted. .TP .B \fBusername\fP .sp Optional. Username to authenticate with. If not given, and the remote site requires basic or digest authentication, the user will be prompted for it. Environment variables are expanded in the username letting you do \fBfoo.username = $USER\fP. If the URI includes a username, only \fB[auth]\fP entries with a matching username or without a username will be considered. .TP .B \fBpassword\fP .sp Optional. Password to authenticate with. If not given, and the remote site requires basic or digest authentication, the user will be prompted for it. .TP .B \fBkey\fP .sp Optional. PEM encoded client certificate key file. Environment variables are expanded in the filename. .TP .B \fBcert\fP .sp Optional. PEM encoded client certificate chain file. Environment variables are expanded in the filename. .TP .B \fBschemes\fP .sp Optional. Space separated list of URI schemes to use this authentication entry with. Only used if the prefix doesn\(aqt include a scheme. Supported schemes are http and https. They will match static\-http and static\-https respectively, as well. Default: https. .UNINDENT .sp If no suitable authentication entry is found, the user is prompted for credentials as usual if required by the remote. .SS \fBdecode/encode\fP .sp Filters for transforming files on checkout/checkin. This would typically be used for newline processing or other localization/canonicalization of files. .sp Filters consist of a filter pattern followed by a filter command. Filter patterns are globs by default, rooted at the repository root. For example, to match any file ending in \fB.txt\fP in the root directory only, use the pattern \fB*.txt\fP. To match any file ending in \fB.c\fP anywhere in the repository, use the pattern \fB**.c\fP. For each file only the first matching filter applies. .sp The filter command can start with a specifier, either \fBpipe:\fP or \fBtempfile:\fP. If no specifier is given, \fBpipe:\fP is used by default. .sp A \fBpipe:\fP command must accept data on stdin and return the transformed data on stdout. .sp Pipe example: .sp .nf .ft C [encode] # uncompress gzip files on checkin to improve delta compression # note: not necessarily a good idea, just an example *.gz = pipe: gunzip [decode] # recompress gzip files when writing them to the working dir (we # can safely omit "pipe:", because it\(aqs the default) *.gz = gzip .ft P .fi .sp A \fBtempfile:\fP command is a template. The string \fBINFILE\fP is replaced with the name of a temporary file that contains the data to be filtered by the command. The string \fBOUTFILE\fP is replaced with the name of an empty temporary file, where the filtered data must be written by the command. .IP Note . The tempfile mechanism is recommended for Windows systems, where the standard shell I/O redirection operators often have strange effects and may corrupt the contents of your files. .RE .sp This filter mechanism is used internally by the \fBeol\fP extension to translate line ending characters between Windows (CRLF) and Unix (LF) format. We suggest you use the \fBeol\fP extension for convenience. .SS \fBdefaults\fP .sp (defaults are deprecated. Don\(aqt use them. Use aliases instead) .sp Use the \fB[defaults]\fP section to define command defaults, i.e. the default options/arguments to pass to the specified commands. .sp The following example makes \%\fBhg log\fP\: run in verbose mode, and \%\fBhg status\fP\: show only the modified files, by default: .sp .nf .ft C [defaults] log = \-v status = \-m .ft P .fi .sp The actual commands, instead of their aliases, must be used when defining command defaults. The command defaults will also be applied to the aliases of the commands defined. .SS \fBdiff\fP .sp Settings used when displaying diffs. Everything except for \fBunified\fP is a Boolean and defaults to False. See \fBannotate\fP section for related options for the annotate command. .INDENT 0.0 .TP .B \fBgit\fP .sp Use git extended diff format. .TP .B \fBnodates\fP .sp Don\(aqt include dates in diff headers. .TP .B \fBshowfunc\fP .sp Show which function each change is in. .TP .B \fBignorews\fP .sp Ignore white space when comparing lines. .TP .B \fBignorewsamount\fP .sp Ignore changes in the amount of white space. .TP .B \fBignoreblanklines\fP .sp Ignore changes whose lines are all blank. .TP .B \fBunified\fP .sp Number of lines of context to show. .UNINDENT .SS \fBemail\fP .sp Settings for extensions that send email messages. .INDENT 0.0 .TP .B \fBfrom\fP .sp Optional. Email address to use in "From" header and SMTP envelope of outgoing messages. .TP .B \fBto\fP .sp Optional. Comma\-separated list of recipients\(aq email addresses. .TP .B \fBcc\fP .sp Optional. Comma\-separated list of carbon copy recipients\(aq email addresses. .TP .B \fBbcc\fP .sp Optional. Comma\-separated list of blind carbon copy recipients\(aq email addresses. .TP .B \fBmethod\fP .sp Optional. Method to use to send email messages. If value is \fBsmtp\fP (default), use SMTP (see the \fB[smtp]\fP section for configuration). Otherwise, use as name of program to run that acts like sendmail (takes \fB\-f\fP option for sender, list of recipients on command line, message on stdin). Normally, setting this to \fBsendmail\fP or \fB/usr/sbin/sendmail\fP is enough to use sendmail to send messages. .TP .B \fBcharsets\fP .sp Optional. Comma\-separated list of character sets considered convenient for recipients. Addresses, headers, and parts not containing patches of outgoing messages will be encoded in the first character set to which conversion from local encoding (\fB$HGENCODING\fP, \fBui.fallbackencoding\fP) succeeds. If correct conversion fails, the text in question is sent as is. Defaults to empty (explicit) list. .sp Order of outgoing email character sets: .INDENT 7.0 .IP 1. 3 . \fBus\-ascii\fP: always first, regardless of settings .IP 2. 3 . \fBemail.charsets\fP: in order given by user .IP 3. 3 . \fBui.fallbackencoding\fP: if not in email.charsets .IP 4. 3 . \fB$HGENCODING\fP: if not in email.charsets .IP 5. 3 . \fButf\-8\fP: always last, regardless of settings .UNINDENT .UNINDENT .sp Email example: .sp .nf .ft C [email] from = Joseph User method = /usr/sbin/sendmail # charsets for western Europeans # us\-ascii, utf\-8 omitted, as they are tried first and last charsets = iso\-8859\-1, iso\-8859\-15, windows\-1252 .ft P .fi .SS \fBextensions\fP .sp Mercurial has an extension mechanism for adding new features. To enable an extension, create an entry for it in this section. .sp If you know that the extension is already in Python\(aqs search path, you can give the name of the module, followed by \fB=\fP, with nothing after the \fB=\fP. .sp Otherwise, give a name that you choose, followed by \fB=\fP, followed by the path to the \fB.py\fP file (including the file name extension) that defines the extension. .sp To explicitly disable an extension that is enabled in an hgrc of broader scope, prepend its path with \fB!\fP, as in \fBfoo = !/ext/path\fP or \fBfoo = !\fP when path is not supplied. .sp Example for \fB~/.hgrc\fP: .sp .nf .ft C [extensions] # (the progress extension will get loaded from Mercurial\(aqs path) progress = # (this extension will get loaded from the file specified) myfeature = ~/.hgext/myfeature.py .ft P .fi .SS \fBformat\fP .INDENT 0.0 .TP .B \fBusestore\fP .sp Enable or disable the "store" repository format which improves compatibility with systems that fold case or otherwise mangle filenames. Enabled by default. Disabling this option will allow you to store longer filenames in some situations at the expense of compatibility and ensures that the on\-disk format of newly created repositories will be compatible with Mercurial before version 0.9.4. .TP .B \fBusefncache\fP .sp Enable or disable the "fncache" repository format which enhances the "store" repository format (which has to be enabled to use fncache) to allow longer filenames and avoids using Windows reserved names, e.g. "nul". Enabled by default. Disabling this option ensures that the on\-disk format of newly created repositories will be compatible with Mercurial before version 1.1. .TP .B \fBdotencode\fP .sp Enable or disable the "dotencode" repository format which enhances the "fncache" repository format (which has to be enabled to use dotencode) to avoid issues with filenames starting with ._ on Mac OS X and spaces on Windows. Enabled by default. Disabling this option ensures that the on\-disk format of newly created repositories will be compatible with Mercurial before version 1.7. .UNINDENT .SS \fBgraph\fP .sp Web graph view configuration. This section let you change graph elements display properties by branches, for instance to make the \fBdefault\fP branch stand out. .sp Each line has the following format: .sp .nf .ft C . = .ft P .fi .sp where \fB\fP is the name of the branch being customized. Example: .sp .nf .ft C [graph] # 2px width default.width = 2 # red color default.color = FF0000 .ft P .fi .sp Supported arguments: .INDENT 0.0 .TP .B \fBwidth\fP .sp Set branch edges width in pixels. .TP .B \fBcolor\fP .sp Set branch edges color in hexadecimal RGB notation. .UNINDENT .SS \fBhooks\fP .sp Commands or Python functions that get automatically executed by various actions such as starting or finishing a commit. Multiple hooks can be run for the same action by appending a suffix to the action. Overriding a site\-wide hook can be done by changing its value or setting it to an empty string. Hooks can be prioritized by adding a prefix of \fBpriority\fP to the hook name on a new line and setting the priority. The default priority is 0 if not specified. .sp Example \fB.hg/hgrc\fP: .sp .nf .ft C [hooks] # update working directory after adding changesets changegroup.update = hg update # do not use the site\-wide hook incoming = incoming.email = /my/email/hook incoming.autobuild = /my/build/hook # force autobuild hook to run before other incoming hooks priority.incoming.autobuild = 1 .ft P .fi .sp Most hooks are run with environment variables set that give useful additional information. For each hook below, the environment variables it is passed are listed with names of the form \fB$HG_foo\fP. .INDENT 0.0 .TP .B \fBchangegroup\fP .sp Run after a changegroup has been added via push, pull or unbundle. ID of the first new changeset is in \fB$HG_NODE\fP. URL from which changes came is in \fB$HG_URL\fP. .TP .B \fBcommit\fP .sp Run after a changeset has been created in the local repository. ID of the newly created changeset is in \fB$HG_NODE\fP. Parent changeset IDs are in \fB$HG_PARENT1\fP and \fB$HG_PARENT2\fP. .TP .B \fBincoming\fP .sp Run after a changeset has been pulled, pushed, or unbundled into the local repository. The ID of the newly arrived changeset is in \fB$HG_NODE\fP. URL that was source of changes came is in \fB$HG_URL\fP. .TP .B \fBoutgoing\fP .sp Run after sending changes from local repository to another. ID of first changeset sent is in \fB$HG_NODE\fP. Source of operation is in \fB$HG_SOURCE\fP; see "preoutgoing" hook for description. .TP .B \fBpost\-\fP .sp Run after successful invocations of the associated command. The contents of the command line are passed as \fB$HG_ARGS\fP and the result code in \fB$HG_RESULT\fP. Parsed command line arguments are passed as \fB$HG_PATS\fP and \fB$HG_OPTS\fP. These contain string representations of the python data internally passed to . \fB$HG_OPTS\fP is a dictionary of options (with unspecified options set to their defaults). \fB$HG_PATS\fP is a list of arguments. Hook failure is ignored. .TP .B \fBpre\-\fP .sp Run before executing the associated command. The contents of the command line are passed as \fB$HG_ARGS\fP. Parsed command line arguments are passed as \fB$HG_PATS\fP and \fB$HG_OPTS\fP. These contain string representations of the data internally passed to . \fB$HG_OPTS\fP is a dictionary of options (with unspecified options set to their defaults). \fB$HG_PATS\fP is a list of arguments. If the hook returns failure, the command doesn\(aqt execute and Mercurial returns the failure code. .TP .B \fBprechangegroup\fP .sp Run before a changegroup is added via push, pull or unbundle. Exit status 0 allows the changegroup to proceed. Non\-zero status will cause the push, pull or unbundle to fail. URL from which changes will come is in \fB$HG_URL\fP. .TP .B \fBprecommit\fP .sp Run before starting a local commit. Exit status 0 allows the commit to proceed. Non\-zero status will cause the commit to fail. Parent changeset IDs are in \fB$HG_PARENT1\fP and \fB$HG_PARENT2\fP. .TP .B \fBprelistkeys\fP .sp Run before listing pushkeys (like bookmarks) in the repository. Non\-zero status will cause failure. The key namespace is in \fB$HG_NAMESPACE\fP. .TP .B \fBpreoutgoing\fP .sp Run before collecting changes to send from the local repository to another. Non\-zero status will cause failure. This lets you prevent pull over HTTP or SSH. Also prevents against local pull, push (outbound) or bundle commands, but not effective, since you can just copy files instead then. Source of operation is in \fB$HG_SOURCE\fP. If "serve", operation is happening on behalf of remote SSH or HTTP repository. If "push", "pull" or "bundle", operation is happening on behalf of repository on same system. .TP .B \fBprepushkey\fP .sp Run before a pushkey (like a bookmark) is added to the repository. Non\-zero status will cause the key to be rejected. The key namespace is in \fB$HG_NAMESPACE\fP, the key is in \fB$HG_KEY\fP, the old value (if any) is in \fB$HG_OLD\fP, and the new value is in \fB$HG_NEW\fP. .TP .B \fBpretag\fP .sp Run before creating a tag. Exit status 0 allows the tag to be created. Non\-zero status will cause the tag to fail. ID of changeset to tag is in \fB$HG_NODE\fP. Name of tag is in \fB$HG_TAG\fP. Tag is local if \fB$HG_LOCAL=1\fP, in repository if \fB$HG_LOCAL=0\fP. .TP .B \fBpretxnchangegroup\fP .sp Run after a changegroup has been added via push, pull or unbundle, but before the transaction has been committed. Changegroup is visible to hook program. This lets you validate incoming changes before accepting them. Passed the ID of the first new changeset in \fB$HG_NODE\fP. Exit status 0 allows the transaction to commit. Non\-zero status will cause the transaction to be rolled back and the push, pull or unbundle will fail. URL that was source of changes is in \fB$HG_URL\fP. .TP .B \fBpretxncommit\fP .sp Run after a changeset has been created but the transaction not yet committed. Changeset is visible to hook program. This lets you validate commit message and changes. Exit status 0 allows the commit to proceed. Non\-zero status will cause the transaction to be rolled back. ID of changeset is in \fB$HG_NODE\fP. Parent changeset IDs are in \fB$HG_PARENT1\fP and \fB$HG_PARENT2\fP. .TP .B \fBpreupdate\fP .sp Run before updating the working directory. Exit status 0 allows the update to proceed. Non\-zero status will prevent the update. Changeset ID of first new parent is in \fB$HG_PARENT1\fP. If merge, ID of second new parent is in \fB$HG_PARENT2\fP. .TP .B \fBlistkeys\fP .sp Run after listing pushkeys (like bookmarks) in the repository. The key namespace is in \fB$HG_NAMESPACE\fP. \fB$HG_VALUES\fP is a dictionary containing the keys and values. .TP .B \fBpushkey\fP .sp Run after a pushkey (like a bookmark) is added to the repository. The key namespace is in \fB$HG_NAMESPACE\fP, the key is in \fB$HG_KEY\fP, the old value (if any) is in \fB$HG_OLD\fP, and the new value is in \fB$HG_NEW\fP. .TP .B \fBtag\fP .sp Run after a tag is created. ID of tagged changeset is in \fB$HG_NODE\fP. Name of tag is in \fB$HG_TAG\fP. Tag is local if \fB$HG_LOCAL=1\fP, in repository if \fB$HG_LOCAL=0\fP. .TP .B \fBupdate\fP .sp Run after updating the working directory. Changeset ID of first new parent is in \fB$HG_PARENT1\fP. If merge, ID of second new parent is in \fB$HG_PARENT2\fP. If the update succeeded, \fB$HG_ERROR=0\fP. If the update failed (e.g. because conflicts not resolved), \fB$HG_ERROR=1\fP. .UNINDENT .IP Note . It is generally better to use standard hooks rather than the generic pre\- and post\- command hooks as they are guaranteed to be called in the appropriate contexts for influencing transactions. Also, hooks like "commit" will be called in all contexts that generate a commit (e.g. tag) and not just the commit command. .RE .IP Note . Environment variables with empty values may not be passed to hooks on platforms such as Windows. As an example, \fB$HG_PARENT2\fP will have an empty value under Unix\-like platforms for non\-merge changesets, while it will not be available at all under Windows. .RE .sp The syntax for Python hooks is as follows: .sp .nf .ft C hookname = python:modulename.submodule.callable hookname = python:/path/to/python/module.py:callable .ft P .fi .sp Python hooks are run within the Mercurial process. Each hook is called with at least three keyword arguments: a ui object (keyword \fBui\fP), a repository object (keyword \fBrepo\fP), and a \fBhooktype\fP keyword that tells what kind of hook is used. Arguments listed as environment variables above are passed as keyword arguments, with no \fBHG_\fP prefix, and names in lower case. .sp If a Python hook returns a "true" value or raises an exception, this is treated as a failure. .SS \fBhostfingerprints\fP .sp Fingerprints of the certificates of known HTTPS servers. A HTTPS connection to a server with a fingerprint configured here will only succeed if the servers certificate matches the fingerprint. This is very similar to how ssh known hosts works. The fingerprint is the SHA\-1 hash value of the DER encoded certificate. The CA chain and web.cacerts is not used for servers with a fingerprint. .sp For example: .sp .nf .ft C [hostfingerprints] hg.intevation.org = 44:ed:af:1f:97:11:b6:01:7a:48:45:fc:10:3c:b7:f9:d4:89:2a:9d .ft P .fi .sp This feature is only supported when using Python 2.6 or later. .SS \fBhttp_proxy\fP .sp Used to access web\-based Mercurial repositories through a HTTP proxy. .INDENT 0.0 .TP .B \fBhost\fP .sp Host name and (optional) port of the proxy server, for example "myproxy:8000". .TP .B \fBno\fP .sp Optional. Comma\-separated list of host names that should bypass the proxy. .TP .B \fBpasswd\fP .sp Optional. Password to authenticate with at the proxy server. .TP .B \fBuser\fP .sp Optional. User name to authenticate with at the proxy server. .TP .B \fBalways\fP .sp Optional. Always use the proxy, even for localhost and any entries in \fBhttp_proxy.no\fP. True or False. Default: False. .UNINDENT .SS \fBmerge\-patterns\fP .sp This section specifies merge tools to associate with particular file patterns. Tools matched here will take precedence over the default merge tool. Patterns are globs by default, rooted at the repository root. .sp Example: .sp .nf .ft C [merge\-patterns] **.c = kdiff3 **.jpg = myimgmerge .ft P .fi .SS \fBmerge\-tools\fP .sp This section configures external merge tools to use for file\-level merges. .sp Example \fB~/.hgrc\fP: .sp .nf .ft C [merge\-tools] # Override stock tool location kdiff3.executable = ~/bin/kdiff3 # Specify command line kdiff3.args = $base $local $other \-o $output # Give higher priority kdiff3.priority = 1 # Define new tool myHtmlTool.args = \-m $local $other $base $output myHtmlTool.regkey = Software\eFooSoftware\eHtmlMerge myHtmlTool.priority = 1 .ft P .fi .sp Supported arguments: .INDENT 0.0 .TP .B \fBpriority\fP .sp The priority in which to evaluate this tool. Default: 0. .TP .B \fBexecutable\fP .sp Either just the name of the executable or its pathname. On Windows, the path can use environment variables with ${ProgramFiles} syntax. Default: the tool name. .TP .B \fBargs\fP .sp The arguments to pass to the tool executable. You can refer to the files being merged as well as the output file through these variables: \fB$base\fP, \fB$local\fP, \fB$other\fP, \fB$output\fP. Default: \fB$local $base $other\fP .TP .B \fBpremerge\fP .sp Attempt to run internal non\-interactive 3\-way merge tool before launching external tool. Options are \fBtrue\fP, \fBfalse\fP, or \fBkeep\fP to leave markers in the file if the premerge fails. Default: True .TP .B \fBbinary\fP .sp This tool can merge binary files. Defaults to False, unless tool was selected by file pattern match. .TP .B \fBsymlink\fP .sp This tool can merge symlinks. Defaults to False, even if tool was selected by file pattern match. .TP .B \fBcheck\fP .sp A list of merge success\-checking options: .INDENT 7.0 .TP .B \fBchanged\fP .sp Ask whether merge was successful when the merged file shows no changes. .TP .B \fBconflicts\fP .sp Check whether there are conflicts even though the tool reported success. .TP .B \fBprompt\fP .sp Always prompt for merge success, regardless of success reported by tool. .UNINDENT .TP .B \fBfixeol\fP .sp Attempt to fix up EOL changes caused by the merge tool. Default: False .TP .B \fBgui\fP .sp This tool requires a graphical interface to run. Default: False .TP .B \fBregkey\fP .sp Windows registry key which describes install location of this tool. Mercurial will search for this key first under \fBHKEY_CURRENT_USER\fP and then under \fBHKEY_LOCAL_MACHINE\fP. Default: None .TP .B \fBregkeyalt\fP .sp An alternate Windows registry key to try if the first key is not found. The alternate key uses the same \fBregname\fP and \fBregappend\fP semantics of the primary key. The most common use for this key is to search for 32bit applications on 64bit operating systems. Default: None .TP .B \fBregname\fP .sp Name of value to read from specified registry key. Defaults to the unnamed (default) value. .TP .B \fBregappend\fP .sp String to append to the value read from the registry, typically the executable name of the tool. Default: None .UNINDENT .SS \fBpatch\fP .sp Settings used when applying patches, for instance through the \(aqimport\(aq command or with Mercurial Queues extension. .INDENT 0.0 .TP .B \fBeol\fP .sp When set to \(aqstrict\(aq patch content and patched files end of lines are preserved. When set to \fBlf\fP or \fBcrlf\fP, both files end of lines are ignored when patching and the result line endings are normalized to either LF (Unix) or CRLF (Windows). When set to \fBauto\fP, end of lines are again ignored while patching but line endings in patched files are normalized to their original setting on a per\-file basis. If target file does not exist or has no end of line, patch line endings are preserved. Default: strict. .UNINDENT .SS \fBpaths\fP .sp Assigns symbolic names to repositories. The left side is the symbolic name, and the right gives the directory or URL that is the location of the repository. Default paths can be declared by setting the following entries. .INDENT 0.0 .TP .B \fBdefault\fP .sp Directory or URL to use when pulling if no source is specified. Default is set to repository from which the current repository was cloned. .TP .B \fBdefault\-push\fP .sp Optional. Directory or URL to use when pushing if no destination is specified. .UNINDENT .sp Custom paths can be defined by assigning the path to a name that later can be used from the command line. Example: .sp .nf .ft C [paths] my_path = http://example.com/path .ft P .fi .sp To push to the path defined in \fBmy_path\fP run the command: .sp .nf .ft C hg push my_path .ft P .fi .SS \fBphases\fP .sp Specifies default handling of phases. See \%\fBhg help phases\fP\: for more information about working with phases. .INDENT 0.0 .TP .B \fBpublish\fP .sp Controls draft phase behavior when working as a server. When true, pushed changesets are set to public in both client and server and pulled or cloned changesets are set to public in the client. Default: True .TP .B \fBnew\-commit\fP .sp Phase of newly\-created commits. Default: draft .UNINDENT .SS \fBprofiling\fP .sp Specifies profiling type, format, and file output. Two profilers are supported: an instrumenting profiler (named \fBls\fP), and a sampling profiler (named \fBstat\fP). .sp In this section description, \(aqprofiling data\(aq stands for the raw data collected during profiling, while \(aqprofiling report\(aq stands for a statistical text report generated from the profiling data. The profiling is done using lsprof. .INDENT 0.0 .TP .B \fBtype\fP .sp The type of profiler to use. Default: ls. .INDENT 7.0 .TP .B \fBls\fP .sp Use Python\(aqs built\-in instrumenting profiler. This profiler works on all platforms, but each line number it reports is the first line of a function. This restriction makes it difficult to identify the expensive parts of a non\-trivial function. .TP .B \fBstat\fP .sp Use a third\-party statistical profiler, statprof. This profiler currently runs only on Unix systems, and is most useful for profiling commands that run for longer than about 0.1 seconds. .UNINDENT .TP .B \fBformat\fP .sp Profiling format. Specific to the \fBls\fP instrumenting profiler. Default: text. .INDENT 7.0 .TP .B \fBtext\fP .sp Generate a profiling report. When saving to a file, it should be noted that only the report is saved, and the profiling data is not kept. .TP .B \fBkcachegrind\fP .sp Format profiling data for kcachegrind use: when saving to a file, the generated file can directly be loaded into kcachegrind. .UNINDENT .TP .B \fBfrequency\fP .sp Sampling frequency. Specific to the \fBstat\fP sampling profiler. Default: 1000. .TP .B \fBoutput\fP .sp File path where profiling data or report should be saved. If the file exists, it is replaced. Default: None, data is printed on stderr .TP .B \fBsort\fP .sp Sort field. Specific to the \fBls\fP instrumenting profiler. One of \fBcallcount\fP, \fBreccallcount\fP, \fBtotaltime\fP and \fBinlinetime\fP. Default: inlinetime. .TP .B \fBlimit\fP .sp Number of lines to show. Specific to the \fBls\fP instrumenting profiler. Default: 30. .TP .B \fBnested\fP .sp Show at most this number of lines of drill\-down info after each main entry. This can help explain the difference between Total and Inline. Specific to the \fBls\fP instrumenting profiler. Default: 5. .UNINDENT .SS \fBrevsetalias\fP .sp Alias definitions for revsets. See \%\fBhg help revsets\fP\: for details. .SS \fBserver\fP .sp Controls generic server settings. .INDENT 0.0 .TP .B \fBuncompressed\fP .sp Whether to allow clients to clone a repository using the uncompressed streaming protocol. This transfers about 40% more data than a regular clone, but uses less memory and CPU on both server and client. Over a LAN (100 Mbps or better) or a very fast WAN, an uncompressed streaming clone is a lot faster (~10x) than a regular clone. Over most WAN connections (anything slower than about 6 Mbps), uncompressed streaming is slower, because of the extra data transfer overhead. This mode will also temporarily hold the write lock while determining what data to transfer. Default is True. .TP .B \fBpreferuncompressed\fP .sp When set, clients will try to use the uncompressed streaming protocol. Default is False. .TP .B \fBvalidate\fP .sp Whether to validate the completeness of pushed changesets by checking that all new file revisions specified in manifests are present. Default is False. .UNINDENT .SS \fBsmtp\fP .sp Configuration for extensions that need to send email messages. .INDENT 0.0 .TP .B \fBhost\fP .sp Host name of mail server, e.g. "mail.example.com". .TP .B \fBport\fP .sp Optional. Port to connect to on mail server. Default: 465 (if \fBtls\fP is smtps) or 25 (otherwise). .TP .B \fBtls\fP .sp Optional. Method to enable TLS when connecting to mail server: starttls, smtps or none. Default: none. .TP .B \fBverifycert\fP .sp Optional. Verification for the certificate of mail server, when \fBtls\fP is starttls or smtps. "strict", "loose" or False. For "strict" or "loose", the certificate is verified as same as the verification for HTTPS connections (see \fB[hostfingerprints]\fP and \fB[web] cacerts\fP also). For "strict", sending email is also aborted, if there is no configuration for mail server in \fB[hostfingerprints]\fP and \fB[web] cacerts\fP. \-\-insecure for \%\fBhg email\fP\: overwrites this as "loose". Default: "strict". .TP .B \fBusername\fP .sp Optional. User name for authenticating with the SMTP server. Default: none. .TP .B \fBpassword\fP .sp Optional. Password for authenticating with the SMTP server. If not specified, interactive sessions will prompt the user for a password; non\-interactive sessions will fail. Default: none. .TP .B \fBlocal_hostname\fP .sp Optional. It\(aqs the hostname that the sender can use to identify itself to the MTA. .UNINDENT .SS \fBsubpaths\fP .sp Subrepository source URLs can go stale if a remote server changes name or becomes temporarily unavailable. This section lets you define rewrite rules of the form: .sp .nf .ft C = .ft P .fi .sp where \fBpattern\fP is a regular expression matching a subrepository source URL and \fBreplacement\fP is the replacement string used to rewrite it. Groups can be matched in \fBpattern\fP and referenced in \fBreplacements\fP. For instance: .sp .nf .ft C http://server/(.*)\-hg/ = http://hg.server/\e1/ .ft P .fi .sp rewrites \fBhttp://server/foo\-hg/\fP into \fBhttp://hg.server/foo/\fP. .sp Relative subrepository paths are first made absolute, and the rewrite rules are then applied on the full (absolute) path. The rules are applied in definition order. .SS \fBtrusted\fP .sp Mercurial will not use the settings in the \fB.hg/hgrc\fP file from a repository if it doesn\(aqt belong to a trusted user or to a trusted group, as various hgrc features allow arbitrary commands to be run. This issue is often encountered when configuring hooks or extensions for shared repositories or servers. However, the web interface will use some safe settings from the \fB[web]\fP section. .sp This section specifies what users and groups are trusted. The current user is always trusted. To trust everybody, list a user or a group with name \fB*\fP. These settings must be placed in an \fIalready\-trusted file\fP to take effect, such as \fB$HOME/.hgrc\fP of the user or service running Mercurial. .INDENT 0.0 .TP .B \fBusers\fP .sp Comma\-separated list of trusted users. .TP .B \fBgroups\fP .sp Comma\-separated list of trusted groups. .UNINDENT .SS \fBui\fP .sp User interface controls. .INDENT 0.0 .TP .B \fBarchivemeta\fP .sp Whether to include the .hg_archival.txt file containing meta data (hashes for the repository base and for tip) in archives created by the \%\fBhg archive\fP\: command or downloaded via hgweb. Default is True. .TP .B \fBaskusername\fP .sp Whether to prompt for a username when committing. If True, and neither \fB$HGUSER\fP nor \fB$EMAIL\fP has been specified, then the user will be prompted to enter a username. If no username is entered, the default \fBUSER@HOST\fP is used instead. Default is False. .TP .B \fBcommitsubrepos\fP .sp Whether to commit modified subrepositories when committing the parent repository. If False and one subrepository has uncommitted changes, abort the commit. Default is False. .TP .B \fBdebug\fP .sp Print debugging information. True or False. Default is False. .TP .B \fBeditor\fP .sp The editor to use during a commit. Default is \fB$EDITOR\fP or \fBvi\fP. .TP .B \fBfallbackencoding\fP .sp Encoding to try if it\(aqs not possible to decode the changelog using UTF\-8. Default is ISO\-8859\-1. .TP .B \fBignore\fP .sp A file to read per\-user ignore patterns from. This file should be in the same format as a repository\-wide .hgignore file. This option supports hook syntax, so if you want to specify multiple ignore files, you can do so by setting something like \fBignore.other = ~/.hgignore2\fP. For details of the ignore file format, see the \fBhgignore(5)\fP man page. .TP .B \fBinteractive\fP .sp Allow to prompt the user. True or False. Default is True. .TP .B \fBlogtemplate\fP .sp Template string for commands that print changesets. .TP .B \fBmerge\fP .sp The conflict resolution program to use during a manual merge. For more information on merge tools see \%\fBhg help merge\-tools\fP\:. For configuring merge tools see the \fB[merge\-tools]\fP section. .TP .B \fBportablefilenames\fP .sp Check for portable filenames. Can be \fBwarn\fP, \fBignore\fP or \fBabort\fP. Default is \fBwarn\fP. If set to \fBwarn\fP (or \fBtrue\fP), a warning message is printed on POSIX platforms, if a file with a non\-portable filename is added (e.g. a file with a name that can\(aqt be created on Windows because it contains reserved parts like \fBAUX\fP, reserved characters like \fB:\fP, or would cause a case collision with an existing file). If set to \fBignore\fP (or \fBfalse\fP), no warning is printed. If set to \fBabort\fP, the command is aborted. On Windows, this configuration option is ignored and the command aborted. .TP .B \fBquiet\fP .sp Reduce the amount of output printed. True or False. Default is False. .TP .B \fBremotecmd\fP .sp remote command to use for clone/push/pull operations. Default is \fBhg\fP. .TP .B \fBreportoldssl\fP .sp Warn if an SSL certificate is unable to be due to using Python 2.5 or earlier. True or False. Default is True. .TP .B \fBreport_untrusted\fP .sp Warn if a \fB.hg/hgrc\fP file is ignored due to not being owned by a trusted user or group. True or False. Default is True. .TP .B \fBslash\fP .sp Display paths using a slash (\fB/\fP) as the path separator. This only makes a difference on systems where the default path separator is not the slash character (e.g. Windows uses the backslash character (\fB\e\fP)). Default is False. .TP .B \fBssh\fP .sp command to use for SSH connections. Default is \fBssh\fP. .TP .B \fBstrict\fP .sp Require exact command names, instead of allowing unambiguous abbreviations. True or False. Default is False. .TP .B \fBstyle\fP .sp Name of style to use for command output. .TP .B \fBtimeout\fP .sp The timeout used when a lock is held (in seconds), a negative value means no timeout. Default is 600. .TP .B \fBtraceback\fP .sp Mercurial always prints a traceback when an unknown exception occurs. Setting this to True will make Mercurial print a traceback on all exceptions, even those recognized by Mercurial (such as IOError or MemoryError). Default is False. .TP .B \fBusername\fP .sp The committer of a changeset created when running "commit". Typically a person\(aqs name and email address, e.g. \fBFred Widget \fP. Default is \fB$EMAIL\fP or \fBusername@hostname\fP. If the username in hgrc is empty, it has to be specified manually or in a different hgrc file (e.g. \fB$HOME/.hgrc\fP, if the admin set \fBusername =\fP in the system hgrc). Environment variables in the username are expanded. .TP .B \fBverbose\fP .sp Increase the amount of output printed. True or False. Default is False. .UNINDENT .SS \fBweb\fP .sp Web interface configuration. The settings in this section apply to both the builtin webserver (started by \%\fBhg serve\fP\:) and the script you run through a webserver (\fBhgweb.cgi\fP and the derivatives for FastCGI and WSGI). .sp The Mercurial webserver does no authentication (it does not prompt for usernames and passwords to validate \fIwho\fP users are), but it does do authorization (it grants or denies access for \fIauthenticated users\fP based on settings in this section). You must either configure your webserver to do authentication for you, or disable the authorization checks. .sp For a quick setup in a trusted environment, e.g., a private LAN, where you want it to accept pushes from anybody, you can use the following command line: .sp .nf .ft C $ hg \-\-config web.allow_push=* \-\-config web.push_ssl=False serve .ft P .fi .sp Note that this will allow anybody to push anything to the server and that this should not be used for public servers. .sp The full set of options is: .INDENT 0.0 .TP .B \fBaccesslog\fP .sp Where to output the access log. Default is stdout. .TP .B \fBaddress\fP .sp Interface address to bind to. Default is all. .TP .B \fBallow_archive\fP .sp List of archive format (bz2, gz, zip) allowed for downloading. Default is empty. .TP .B \fBallowbz2\fP .sp (DEPRECATED) Whether to allow .tar.bz2 downloading of repository revisions. Default is False. .TP .B \fBallowgz\fP .sp (DEPRECATED) Whether to allow .tar.gz downloading of repository revisions. Default is False. .TP .B \fBallowpull\fP .sp Whether to allow pulling from the repository. Default is True. .TP .B \fBallow_push\fP .sp Whether to allow pushing to the repository. If empty or not set, push is not allowed. If the special value \fB*\fP, any remote user can push, including unauthenticated users. Otherwise, the remote user must have been authenticated, and the authenticated user name must be present in this list. The contents of the allow_push list are examined after the deny_push list. .TP .B \fBallow_read\fP .sp If the user has not already been denied repository access due to the contents of deny_read, this list determines whether to grant repository access to the user. If this list is not empty, and the user is unauthenticated or not present in the list, then access is denied for the user. If the list is empty or not set, then access is permitted to all users by default. Setting allow_read to the special value \fB*\fP is equivalent to it not being set (i.e. access is permitted to all users). The contents of the allow_read list are examined after the deny_read list. .TP .B \fBallowzip\fP .sp (DEPRECATED) Whether to allow .zip downloading of repository revisions. Default is False. This feature creates temporary files. .TP .B \fBarchivesubrepos\fP .sp Whether to recurse into subrepositories when archiving. Default is False. .TP .B \fBbaseurl\fP .sp Base URL to use when publishing URLs in other locations, so third\-party tools like email notification hooks can construct URLs. Example: \fBhttp://hgserver/repos/\fP. .TP .B \fBcacerts\fP .sp Path to file containing a list of PEM encoded certificate authority certificates. Environment variables and \fB~user\fP constructs are expanded in the filename. If specified on the client, then it will verify the identity of remote HTTPS servers with these certificates. .sp This feature is only supported when using Python 2.6 or later. If you wish to use it with earlier versions of Python, install the backported version of the ssl library that is available from \fBhttp://pypi.python.org\fP. .sp To disable SSL verification temporarily, specify \fB\-\-insecure\fP from command line. .sp You can use OpenSSL\(aqs CA certificate file if your platform has one. On most Linux systems this will be \fB/etc/ssl/certs/ca\-certificates.crt\fP. Otherwise you will have to generate this file manually. The form must be as follows: .sp .nf .ft C \-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\- \&... (certificate in base64 PEM encoding) ... \-\-\-\-\-END CERTIFICATE\-\-\-\-\- \-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\- \&... (certificate in base64 PEM encoding) ... \-\-\-\-\-END CERTIFICATE\-\-\-\-\- .ft P .fi .TP .B \fBcache\fP .sp Whether to support caching in hgweb. Defaults to True. .TP .B \fBcollapse\fP .sp With \fBdescend\fP enabled, repositories in subdirectories are shown at a single level alongside repositories in the current path. With \fBcollapse\fP also enabled, repositories residing at a deeper level than the current path are grouped behind navigable directory entries that lead to the locations of these repositories. In effect, this setting collapses each collection of repositories found within a subdirectory into a single entry for that subdirectory. Default is False. .TP .B \fBcomparisoncontext\fP .sp Number of lines of context to show in side\-by\-side file comparison. If negative or the value \fBfull\fP, whole files are shown. Default is 5. This setting can be overridden by a \fBcontext\fP request parameter to the \fBcomparison\fP command, taking the same values. .TP .B \fBcontact\fP .sp Name or email address of the person in charge of the repository. Defaults to ui.username or \fB$EMAIL\fP or "unknown" if unset or empty. .TP .B \fBdeny_push\fP .sp Whether to deny pushing to the repository. If empty or not set, push is not denied. If the special value \fB*\fP, all remote users are denied push. Otherwise, unauthenticated users are all denied, and any authenticated user name present in this list is also denied. The contents of the deny_push list are examined before the allow_push list. .TP .B \fBdeny_read\fP .sp Whether to deny reading/viewing of the repository. If this list is not empty, unauthenticated users are all denied, and any authenticated user name present in this list is also denied access to the repository. If set to the special value \fB*\fP, all remote users are denied access (rarely needed ;). If deny_read is empty or not set, the determination of repository access depends on the presence and content of the allow_read list (see description). If both deny_read and allow_read are empty or not set, then access is permitted to all users by default. If the repository is being served via hgwebdir, denied users will not be able to see it in the list of repositories. The contents of the deny_read list have priority over (are examined before) the contents of the allow_read list. .TP .B \fBdescend\fP .sp hgwebdir indexes will not descend into subdirectories. Only repositories directly in the current path will be shown (other repositories are still available from the index corresponding to their containing path). .TP .B \fBdescription\fP .sp Textual description of the repository\(aqs purpose or contents. Default is "unknown". .TP .B \fBencoding\fP .sp Character encoding name. Default is the current locale charset. Example: "UTF\-8" .TP .B \fBerrorlog\fP .sp Where to output the error log. Default is stderr. .TP .B \fBguessmime\fP .sp Control MIME types for raw download of file content. Set to True to let hgweb guess the content type from the file extension. This will serve HTML files as \fBtext/html\fP and might allow cross\-site scripting attacks when serving untrusted repositories. Default is False. .TP .B \fBhidden\fP .sp Whether to hide the repository in the hgwebdir index. Default is False. .TP .B \fBipv6\fP .sp Whether to use IPv6. Default is False. .TP .B \fBlogoimg\fP .sp File name of the logo image that some templates display on each page. The file name is relative to \fBstaticurl\fP. That is, the full path to the logo image is "staticurl/logoimg". If unset, \fBhglogo.png\fP will be used. .TP .B \fBlogourl\fP .sp Base URL to use for logos. If unset, \fBhttp://mercurial.selenic.com/\fP will be used. .TP .B \fBmaxchanges\fP .sp Maximum number of changes to list on the changelog. Default is 10. .TP .B \fBmaxfiles\fP .sp Maximum number of files to list per changeset. Default is 10. .TP .B \fBmaxshortchanges\fP .sp Maximum number of changes to list on the shortlog, graph or filelog pages. Default is 60. .TP .B \fBname\fP .sp Repository name to use in the web interface. Default is current working directory. .TP .B \fBport\fP .sp Port to listen on. Default is 8000. .TP .B \fBprefix\fP .sp Prefix path to serve from. Default is \(aq\(aq (server root). .TP .B \fBpush_ssl\fP .sp Whether to require that inbound pushes be transported over SSL to prevent password sniffing. Default is True. .TP .B \fBstaticurl\fP .sp Base URL to use for static files. If unset, static files (e.g. the hgicon.png favicon) will be served by the CGI script itself. Use this setting to serve them directly with the HTTP server. Example: \fBhttp://hgserver/static/\fP. .TP .B \fBstripes\fP .sp How many lines a "zebra stripe" should span in multi\-line output. Default is 1; set to 0 to disable. .TP .B \fBstyle\fP .sp Which template map style to use. .TP .B \fBtemplates\fP .sp Where to find the HTML templates. Default is install path. .UNINDENT .SS \fBwebsub\fP .sp Web substitution filter definition. You can use this section to define a set of regular expression substitution patterns which let you automatically modify the hgweb server output. .sp The default hgweb templates only apply these substitution patterns on the revision description fields. You can apply them anywhere you want when you create your own templates by adding calls to the "websub" filter (usually after calling the "escape" filter). .sp This can be used, for example, to convert issue references to links to your issue tracker, or to convert "markdown\-like" syntax into HTML (see the examples below). .sp Each entry in this section names a substitution filter. The value of each entry defines the substitution expression itself. The websub expressions follow the old interhg extension syntax, which in turn imitates the Unix sed replacement syntax: .sp .nf .ft C patternname = s/SEARCH_REGEX/REPLACE_EXPRESSION/[i] .ft P .fi .sp You can use any separator other than "/". The final "i" is optional and indicates that the search must be case insensitive. .sp Examples: .sp .nf .ft C [websub] issues = s|issue(\ed+)|issue\e1|i italic = s/\eb_(\eS+)_\eb/\e1<\e/i>/ bold = s/\e*\eb(\eS+)\eb\e*/\e1<\e/b>/ .ft P .fi .SS \fBworker\fP .sp Parallel master/worker configuration. We currently perform working directory updates in parallel on Unix\-like systems, which greatly helps performance. .INDENT 0.0 .TP .B \fBnumcpus\fP .sp Number of CPUs to use for parallel operations. Default is 4 or the number of CPUs on the system, whichever is larger. A zero or negative value is treated as \fBuse the default\fP. .UNINDENT .SH AUTHOR .sp Bryan O\(aqSullivan <\%bos@serpentine.com\:>. .sp Mercurial was written by Matt Mackall <\%mpm@selenic.com\:>. .SH SEE ALSO .sp \%\fBhg\fP(1)\:, \%\fBhgignore\fP(5)\: .SH COPYING .sp This manual page is copyright 2005 Bryan O\(aqSullivan. Mercurial is copyright 2005\-2013 Matt Mackall. Free use of this software is granted under the terms of the GNU General Public License version 2 or any later version. .\" Common link and substitution definitions. . .SH AUTHOR Bryan O'Sullivan Organization: Mercurial .\" Generated by docutils manpage writer. .\" . mercurial-2.8.2/doc/style.css0000644000175000017500000001245512261160437017744 0ustar oxymoronoxymoron00000000000000/* * Styles for man pages, which match with http://mercurial.selenic.com/ * * Color scheme & layout are borrowed from * http://mercurial.selenic.com/css/styles.css * * Some styles are from html4css1.css from Docutils, which is in the * public domain. */ body { margin: 0; padding: 0; font-family: sans-serif; } .document { position: relative; /* be a top of absolute positioning */ margin: 1.5em 1.8em; padding: 0; line-height: 1.3; } /* layout: toc to right */ #contents { position: absolute; right: 0; top: 0; width: 26%; } /* layout: others to left */ h1.title, h2.subtitle, .section { width: 72%; } .section .section { width: auto; } table.docinfo { max-width: 72%; } /* headings */ h1, h2, .topic-title, .admonition-title { font-family: "MgOpen Cosmetica", "Lucida Sans Unicode", sans-serif; font-weight: normal; } h1, h2, .topic-title, .admonition-title { margin: 1em 0 0.5em; } h1.title { font-size: 300%; } h2.subtitle, h1 { font-size: 200%; } h2, .topic-title, .admonition-title { font-size: 140%; } /* subtitle starts with lowercase in man pages, but not in HTML */ h2.subtitle:first-letter { text-transform: uppercase; } /* override first/last margin */ .first, h1.title, h2.subtitle { margin-top: 0 !important; } .last, .with-subtitle { margin-bottom: 0 !important; } blockquote, pre, dd .option-list, .field-list { margin: 0.2em 0 1em 2em; } kbd, tt, pre { font-family: monospace; } dt { font-weight: bold; } dd { margin-bottom: 0.5em; } th, td { padding: 0.1em 0.2em; border: 0 none; } th { font-weight: bold; text-align: left; } a:link, a:visited { text-decoration: underline; } a:hover, a:focus { text-decoration: none; } a:link { color: #00b5f1; } a:visited { color: #5c9caf; } a:link.toc-backref, a:visited.toc-backref { text-decoration: none; color: inherit; /* NOTE: `inherit' is not supported by IE6 */ } div.admonition, div.attention, div.caution, div.danger, div.error, div.hint, div.important, div.note, div.tip, div.warning { border-top: 1px #ccc solid; border-bottom: 1px #ccc solid; padding: 0.3em 1em; margin: 1em; } div.note { border-color: #fcc200; } /* * The following styles are from Docutils. * Please refine if necessary. */ table.borderless td, table.borderless th { /* Override padding for "table.docutils td" with "! important". The right padding separates the table cells. */ padding: 0 0.5em 0 0 ! important; } .hidden { display: none; } blockquote.epigraph { margin: 2em 5em; } div.abstract { margin: 2em 5em; } div.dedication { margin: 2em 5em; text-align: center; font-style: italic; } div.figure { margin-left: 2em; margin-right: 2em; } div.footer, div.header { clear: both; font-size: smaller; } div.line-block { display: block; margin-top: 1em; margin-bottom: 1em; } div.line-block div.line-block { margin-top: 0; margin-bottom: 0; margin-left: 1.5em; } div.sidebar { margin: 0 0 0.5em 1em; border: medium outset; padding: 1em; background-color: #ffffee; width: 40%; float: right; clear: right; } div.sidebar p.rubric { font-family: sans-serif; font-size: medium; } div.system-messages { margin: 5em; } div.system-messages h1 { color: red; } div.system-message { border: medium outset; padding: 1em; } div.system-message p.system-message-title { color: red; font-weight: bold; } h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { margin-top: 0.4em; } hr.docutils { width: 75%; } img.align-left { clear: left; } img.align-right { clear: right; } ol.simple, ul.simple { margin-bottom: 1em; } ol.arabic { list-style: decimal; } ol.loweralpha { list-style: lower-alpha; } ol.upperalpha { list-style: upper-alpha; } ol.lowerroman { list-style: lower-roman; } ol.upperroman { list-style: upper-roman; } p.attribution { text-align: right; margin-left: 50%; } p.caption { font-style: italic; } p.credits { font-style: italic; font-size: smaller; } p.label { white-space: nowrap; } p.rubric { font-weight: bold; font-size: larger; color: maroon; text-align: center; } pre.address { margin-bottom: 0; margin-top: 0; font-family: serif; font-size: 100%; } pre.literal-block, pre.doctest-block { margin-left: 2em; margin-right: 2em; } span.classifier { font-family: sans-serif; font-style: oblique; } span.classifier-delimiter { font-family: sans-serif; font-weight: bold; } span.interpreted { font-family: sans-serif; } span.option { white-space: nowrap; } span.pre { white-space: pre; } span.problematic { color: red; } span.section-subtitle { /* font-size relative to parent (h1..h6 element) */ font-size: 80%; } table.citation { border-left: solid 1px gray; margin-left: 1px; } table.footnote { border-left: solid 1px black; margin-left: 1px; } h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { font-size: 100%; } ul.auto-toc { list-style-type: none; } div.contents.local { -moz-column-width: 10em; -moz-column-gap: 1em; -webkit-column-width: 10em; -webkit-column-gap: 1em; } mercurial-2.8.2/doc/hgignore.50000644000175000017500000001052112261160450017745 0ustar oxymoronoxymoron00000000000000.\" Man page generated from reStructuredText. . .TH HGIGNORE 5 "" "" "Mercurial Manual" .SH NAME hgignore \- syntax for Mercurial ignore files . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp The Mercurial system uses a file called \fB.hgignore\fP in the root directory of a repository to control its behavior when it searches for files that it is not currently tracking. .SH DESCRIPTION .sp The working directory of a Mercurial repository will often contain files that should not be tracked by Mercurial. These include backup files created by editors and build products created by compilers. These files can be ignored by listing them in a \fB.hgignore\fP file in the root of the working directory. The \fB.hgignore\fP file must be created manually. It is typically put under version control, so that the settings will propagate to other repositories with push and pull. .sp An untracked file is ignored if its path relative to the repository root directory, or any prefix path of that path, is matched against any pattern in \fB.hgignore\fP. .sp For example, say we have an untracked file, \fBfile.c\fP, at \fBa/b/file.c\fP inside our repository. Mercurial will ignore \fBfile.c\fP if any pattern in \fB.hgignore\fP matches \fBa/b/file.c\fP, \fBa/b\fP or \fBa\fP. .sp In addition, a Mercurial configuration file can reference a set of per\-user or global ignore files. See the \fBignore\fP configuration key on the \fB[ui]\fP section of \%\fBhg help config\fP\: for details of how to configure these files. .sp To control Mercurial\(aqs handling of files that it manages, many commands support the \fB\-I\fP and \fB\-X\fP options; see \%\fBhg help \fP\: and \%\fBhg help patterns\fP\: for details. .sp Files that are already tracked are not affected by .hgignore, even if they appear in .hgignore. An untracked file X can be explicitly added with \%\fBhg add X\fP\:, even if X would be excluded by a pattern in .hgignore. .SH SYNTAX .sp An ignore file is a plain text file consisting of a list of patterns, with one pattern per line. Empty lines are skipped. The \fB#\fP character is treated as a comment character, and the \fB\e\fP character is treated as an escape character. .sp Mercurial supports several pattern syntaxes. The default syntax used is Python/Perl\-style regular expressions. .sp To change the syntax used, use a line of the following form: .sp .nf .ft C syntax: NAME .ft P .fi .sp where \fBNAME\fP is one of the following: .INDENT 0.0 .TP .B \fBregexp\fP .sp Regular expression, Python/Perl syntax. .TP .B \fBglob\fP .sp Shell\-style glob. .UNINDENT .sp The chosen syntax stays in effect when parsing all patterns that follow, until another syntax is selected. .sp Neither glob nor regexp patterns are rooted. A glob\-syntax pattern of the form \fB*.c\fP will match a file ending in \fB.c\fP in any directory, and a regexp pattern of the form \fB\e.c$\fP will do the same. To root a regexp pattern, start it with \fB^\fP. .IP Note . Patterns specified in other than \fB.hgignore\fP are always rooted. Please see \%\fBhg help patterns\fP\: for details. .RE .SH EXAMPLE .sp Here is an example ignore file. .sp .nf .ft C # use glob syntax. syntax: glob *.elc *.pyc *~ # switch to regexp syntax. syntax: regexp ^\e.pc/ .ft P .fi .SH AUTHOR .sp Vadim Gelfer <\%vadim.gelfer@gmail.com\:> .sp Mercurial was written by Matt Mackall <\%mpm@selenic.com\:>. .SH SEE ALSO .sp \%\fBhg\fP(1)\:, \%\fBhgrc\fP(5)\: .SH COPYING .sp This manual page is copyright 2006 Vadim Gelfer. Mercurial is copyright 2005\-2013 Matt Mackall. Free use of this software is granted under the terms of the GNU General Public License version 2 or any later version. .\" Common link and substitution definitions. . .SH AUTHOR Vadim Gelfer Organization: Mercurial .\" Generated by docutils manpage writer. .\" . mercurial-2.8.2/doc/gendoc.py0000644000175000017500000001271012261160437017675 0ustar oxymoronoxymoron00000000000000"""usage: %s DOC ... where DOC is the name of a document """ import os, sys, textwrap # import from the live mercurial repo sys.path.insert(0, "..") # fall back to pure modules if required C extensions are not available sys.path.append(os.path.join('..', 'mercurial', 'pure')) from mercurial import demandimport; demandimport.enable() from mercurial import minirst from mercurial.commands import table, globalopts from mercurial.i18n import gettext, _ from mercurial.help import helptable, loaddoc from mercurial import extensions from mercurial import util def get_desc(docstr): if not docstr: return "", "" # sanitize docstr = docstr.strip("\n") docstr = docstr.rstrip() shortdesc = docstr.splitlines()[0].strip() i = docstr.find("\n") if i != -1: desc = docstr[i + 2:] else: desc = shortdesc desc = textwrap.dedent(desc) return (shortdesc, desc) def get_opts(opts): for opt in opts: if len(opt) == 5: shortopt, longopt, default, desc, optlabel = opt else: shortopt, longopt, default, desc = opt allopts = [] if shortopt: allopts.append("-%s" % shortopt) if longopt: allopts.append("--%s" % longopt) desc += default and _(" (default: %s)") % default or "" yield (", ".join(allopts), desc) def get_cmd(cmd, cmdtable): d = {} attr = cmdtable[cmd] cmds = cmd.lstrip("^").split("|") d['cmd'] = cmds[0] d['aliases'] = cmd.split("|")[1:] d['desc'] = get_desc(gettext(attr[0].__doc__)) d['opts'] = list(get_opts(attr[1])) s = 'hg ' + cmds[0] if len(attr) > 2: if not attr[2].startswith('hg'): s += ' ' + attr[2] else: s = attr[2] d['synopsis'] = s.strip() return d def showdoc(ui): # print options ui.write(minirst.section(_("Options"))) for optstr, desc in get_opts(globalopts): ui.write("%s\n %s\n\n" % (optstr, desc)) # print cmds ui.write(minirst.section(_("Commands"))) commandprinter(ui, table, minirst.subsection) # print help topics # The config help topic is included in the hgrc.5 man page. helpprinter(ui, helptable, minirst.section, exclude=['config']) ui.write(minirst.section(_("Extensions"))) ui.write(_("This section contains help for extensions that are " "distributed together with Mercurial. Help for other " "extensions is available in the help system.")) ui.write("\n\n" ".. contents::\n" " :class: htmlonly\n" " :local:\n" " :depth: 1\n\n") for extensionname in sorted(allextensionnames()): mod = extensions.load(None, extensionname, None) ui.write(minirst.subsection(extensionname)) ui.write("%s\n\n" % gettext(mod.__doc__)) cmdtable = getattr(mod, 'cmdtable', None) if cmdtable: ui.write(minirst.subsubsection(_('Commands'))) commandprinter(ui, cmdtable, minirst.subsubsubsection) def showtopic(ui, topic): extrahelptable = [ (["common"], '', loaddoc('common')), (["hg.1"], '', loaddoc('hg.1')), (["hgignore.5"], '', loaddoc('hgignore.5')), (["hgrc.5"], '', loaddoc('hgrc.5')), (["hgignore.5.gendoc"], '', loaddoc('hgignore')), (["hgrc.5.gendoc"], '', loaddoc('config')), ] helpprinter(ui, helptable + extrahelptable, None, include=[topic]) def helpprinter(ui, helptable, sectionfunc, include=[], exclude=[]): for names, sec, doc in helptable: if exclude and names[0] in exclude: continue if include and names[0] not in include: continue for name in names: ui.write(".. _%s:\n" % name) ui.write("\n") if sectionfunc: ui.write(sectionfunc(sec)) if util.safehasattr(doc, '__call__'): doc = doc() ui.write(doc) ui.write("\n") def commandprinter(ui, cmdtable, sectionfunc): h = {} for c, attr in cmdtable.items(): f = c.split("|")[0] f = f.lstrip("^") h[f] = c cmds = h.keys() cmds.sort() for f in cmds: if f.startswith("debug"): continue d = get_cmd(h[f], cmdtable) ui.write(sectionfunc(d['cmd'])) # synopsis ui.write("::\n\n") synopsislines = d['synopsis'].splitlines() for line in synopsislines: # some commands (such as rebase) have a multi-line # synopsis ui.write(" %s\n" % line) ui.write('\n') # description ui.write("%s\n\n" % d['desc'][1]) # options opt_output = list(d['opts']) if opt_output: opts_len = max([len(line[0]) for line in opt_output]) ui.write(_("Options:\n\n")) for optstr, desc in opt_output: if desc: s = "%-*s %s" % (opts_len, optstr, desc) else: s = optstr ui.write("%s\n" % s) ui.write("\n") # aliases if d['aliases']: ui.write(_(" aliases: %s\n\n") % " ".join(d['aliases'])) def allextensionnames(): return extensions.enabled().keys() + extensions.disabled().keys() if __name__ == "__main__": doc = 'hg.1.gendoc' if len(sys.argv) > 1: doc = sys.argv[1] if doc == 'hg.1.gendoc': showdoc(sys.stdout) else: showtopic(sys.stdout, sys.argv[1]) mercurial-2.8.2/doc/hgmanpage.py0000644000175000017500000010231712261160437020370 0ustar oxymoronoxymoron00000000000000# -*- coding: utf-8 -*- # $Id: manpage.py 6110 2009-08-31 14:40:33Z grubert $ # Author: Engelbert Gruber # Copyright: This module is put into the public domain. """ Simple man page writer for reStructuredText. Man pages (short for "manual pages") contain system documentation on unix-like systems. The pages are grouped in numbered sections: 1 executable programs and shell commands 2 system calls 3 library functions 4 special files 5 file formats 6 games 7 miscellaneous 8 system administration Man pages are written *troff*, a text file formatting system. See http://www.tldp.org/HOWTO/Man-Page for a start. Man pages have no subsection only parts. Standard parts NAME , SYNOPSIS , DESCRIPTION , OPTIONS , FILES , SEE ALSO , BUGS , and AUTHOR . A unix-like system keeps an index of the DESCRIPTIONs, which is accesable by the command whatis or apropos. """ __docformat__ = 'reStructuredText' import re from docutils import nodes, writers, languages try: import roman except ImportError: from docutils.utils import roman import inspect FIELD_LIST_INDENT = 7 DEFINITION_LIST_INDENT = 7 OPTION_LIST_INDENT = 7 BLOCKQOUTE_INDENT = 3.5 # Define two macros so man/roff can calculate the # indent/unindent margins by itself MACRO_DEF = (r""". .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. """) class Writer(writers.Writer): supported = ('manpage') """Formats this writer supports.""" output = None """Final translated form of `document`.""" def __init__(self): writers.Writer.__init__(self) self.translator_class = Translator def translate(self): visitor = self.translator_class(self.document) self.document.walkabout(visitor) self.output = visitor.astext() class Table(object): def __init__(self): self._rows = [] self._options = ['center'] self._tab_char = '\t' self._coldefs = [] def new_row(self): self._rows.append([]) def append_separator(self, separator): """Append the separator for table head.""" self._rows.append([separator]) def append_cell(self, cell_lines): """cell_lines is an array of lines""" start = 0 if len(cell_lines) > 0 and cell_lines[0] == '.sp\n': start = 1 self._rows[-1].append(cell_lines[start:]) if len(self._coldefs) < len(self._rows[-1]): self._coldefs.append('l') def _minimize_cell(self, cell_lines): """Remove leading and trailing blank and ``.sp`` lines""" while (cell_lines and cell_lines[0] in ('\n', '.sp\n')): del cell_lines[0] while (cell_lines and cell_lines[-1] in ('\n', '.sp\n')): del cell_lines[-1] def as_list(self): text = ['.TS\n'] text.append(' '.join(self._options) + ';\n') text.append('|%s|.\n' % ('|'.join(self._coldefs))) for row in self._rows: # row = array of cells. cell = array of lines. text.append('_\n') # line above text.append('T{\n') for i in range(len(row)): cell = row[i] self._minimize_cell(cell) text.extend(cell) if not text[-1].endswith('\n'): text[-1] += '\n' if i < len(row) - 1: text.append('T}'+self._tab_char+'T{\n') else: text.append('T}\n') text.append('_\n') text.append('.TE\n') return text class Translator(nodes.NodeVisitor): """""" words_and_spaces = re.compile(r'\S+| +|\n') document_start = """Man page generated from reStructuredText.""" def __init__(self, document): nodes.NodeVisitor.__init__(self, document) self.settings = settings = document.settings lcode = settings.language_code arglen = len(inspect.getargspec(languages.get_language)[0]) if arglen == 2: self.language = languages.get_language(lcode, self.document.reporter) else: self.language = languages.get_language(lcode) self.head = [] self.body = [] self.foot = [] self.section_level = 0 self.context = [] self.topic_class = '' self.colspecs = [] self.compact_p = 1 self.compact_simple = None # the list style "*" bullet or "#" numbered self._list_char = [] # writing the header .TH and .SH NAME is postboned after # docinfo. self._docinfo = { "title" : "", "title_upper": "", "subtitle" : "", "manual_section" : "", "manual_group" : "", "author" : [], "date" : "", "copyright" : "", "version" : "", } self._docinfo_keys = [] # a list to keep the sequence as in source. self._docinfo_names = {} # to get name from text not normalized. self._in_docinfo = None self._active_table = None self._in_literal = False self.header_written = 0 self._line_block = 0 self.authors = [] self.section_level = 0 self._indent = [0] # central definition of simple processing rules # what to output on : visit, depart # Do not use paragraph requests ``.PP`` because these set indentation. # use ``.sp``. Remove superfluous ``.sp`` in ``astext``. # # Fonts are put on a stack, the top one is used. # ``.ft P`` or ``\\fP`` pop from stack. # ``B`` bold, ``I`` italic, ``R`` roman should be available. # Hopefully ``C`` courier too. self.defs = { 'indent' : ('.INDENT %.1f\n', '.UNINDENT\n'), 'definition_list_item' : ('.TP', ''), 'field_name' : ('.TP\n.B ', '\n'), 'literal' : ('\\fB', '\\fP'), 'literal_block' : ('.sp\n.nf\n.ft C\n', '\n.ft P\n.fi\n'), 'option_list_item' : ('.TP\n', ''), 'reference' : (r'\%', r'\:'), 'emphasis': ('\\fI', '\\fP'), 'strong' : ('\\fB', '\\fP'), 'term' : ('\n.B ', '\n'), 'title_reference' : ('\\fI', '\\fP'), 'topic-title' : ('.SS ',), 'sidebar-title' : ('.SS ',), 'problematic' : ('\n.nf\n', '\n.fi\n'), } # NOTE don't specify the newline before a dot-command, but ensure # it is there. def comment_begin(self, text): """Return commented version of the passed text WITHOUT end of line/comment.""" prefix = '.\\" ' out_text = ''.join( [(prefix + in_line + '\n') for in_line in text.split('\n')]) return out_text def comment(self, text): """Return commented version of the passed text.""" return self.comment_begin(text)+'.\n' def ensure_eol(self): """Ensure the last line in body is terminated by new line.""" if self.body[-1][-1] != '\n': self.body.append('\n') def astext(self): """Return the final formatted document as a string.""" if not self.header_written: # ensure we get a ".TH" as viewers require it. self.head.append(self.header()) # filter body for i in xrange(len(self.body) - 1, 0, -1): # remove superfluous vertical gaps. if self.body[i] == '.sp\n': if self.body[i - 1][:4] in ('.BI ','.IP '): self.body[i] = '.\n' elif (self.body[i - 1][:3] == '.B ' and self.body[i - 2][:4] == '.TP\n'): self.body[i] = '.\n' elif (self.body[i - 1] == '\n' and self.body[i - 2][0] != '.' and (self.body[i - 3][:7] == '.TP\n.B ' or self.body[i - 3][:4] == '\n.B ') ): self.body[i] = '.\n' return ''.join(self.head + self.body + self.foot) def deunicode(self, text): text = text.replace(u'\xa0', '\\ ') text = text.replace(u'\u2020', '\\(dg') return text def visit_Text(self, node): text = node.astext() text = text.replace('\\','\\e') replace_pairs = [ (u'-', ur'\-'), (u'\'', ur'\(aq'), (u'´', ur'\''), (u'`', ur'\(ga'), ] for (in_char, out_markup) in replace_pairs: text = text.replace(in_char, out_markup) # unicode text = self.deunicode(text) if self._in_literal: # prevent interpretation of "." at line start if text[0] == '.': text = '\\&' + text text = text.replace('\n.', '\n\\&.') self.body.append(text) def depart_Text(self, node): pass def list_start(self, node): class enum_char(object): enum_style = { 'bullet' : '\\(bu', 'emdash' : '\\(em', } def __init__(self, style): self._style = style if 'start' in node: self._cnt = node['start'] - 1 else: self._cnt = 0 self._indent = 2 if style == 'arabic': # indentation depends on number of childrens # and start value. self._indent = len(str(len(node.children))) self._indent += len(str(self._cnt)) + 1 elif style == 'loweralpha': self._cnt += ord('a') - 1 self._indent = 3 elif style == 'upperalpha': self._cnt += ord('A') - 1 self._indent = 3 elif style.endswith('roman'): self._indent = 5 def next(self): if self._style == 'bullet': return self.enum_style[self._style] elif self._style == 'emdash': return self.enum_style[self._style] self._cnt += 1 # TODO add prefix postfix if self._style == 'arabic': return "%d." % self._cnt elif self._style in ('loweralpha', 'upperalpha'): return "%c." % self._cnt elif self._style.endswith('roman'): res = roman.toRoman(self._cnt) + '.' if self._style.startswith('upper'): return res.upper() return res.lower() else: return "%d." % self._cnt def get_width(self): return self._indent def __repr__(self): return 'enum_style-%s' % list(self._style) if 'enumtype' in node: self._list_char.append(enum_char(node['enumtype'])) else: self._list_char.append(enum_char('bullet')) if len(self._list_char) > 1: # indent nested lists self.indent(self._list_char[-2].get_width()) else: self.indent(self._list_char[-1].get_width()) def list_end(self): self.dedent() self._list_char.pop() def header(self): tmpl = (".TH %(title_upper)s %(manual_section)s" " \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n" ".SH NAME\n" "%(title)s \- %(subtitle)s\n") return tmpl % self._docinfo def append_header(self): """append header with .TH and .SH NAME""" # NOTE before everything # .TH title_upper section date source manual if self.header_written: return self.body.append(self.header()) self.body.append(MACRO_DEF) self.header_written = 1 def visit_address(self, node): self.visit_docinfo_item(node, 'address') def depart_address(self, node): pass def visit_admonition(self, node, name=None): if name: self.body.append('.IP %s\n' % self.language.labels.get(name, name)) def depart_admonition(self, node): self.body.append('.RE\n') def visit_attention(self, node): self.visit_admonition(node, 'attention') depart_attention = depart_admonition def visit_docinfo_item(self, node, name): if name == 'author': self._docinfo[name].append(node.astext()) else: self._docinfo[name] = node.astext() self._docinfo_keys.append(name) raise nodes.SkipNode def depart_docinfo_item(self, node): pass def visit_author(self, node): self.visit_docinfo_item(node, 'author') depart_author = depart_docinfo_item def visit_authors(self, node): # _author is called anyway. pass def depart_authors(self, node): pass def visit_block_quote(self, node): # BUG/HACK: indent alway uses the _last_ indention, # thus we need two of them. self.indent(BLOCKQOUTE_INDENT) self.indent(0) def depart_block_quote(self, node): self.dedent() self.dedent() def visit_bullet_list(self, node): self.list_start(node) def depart_bullet_list(self, node): self.list_end() def visit_caption(self, node): pass def depart_caption(self, node): pass def visit_caution(self, node): self.visit_admonition(node, 'caution') depart_caution = depart_admonition def visit_citation(self, node): num, text = node.astext().split(None, 1) num = num.strip() self.body.append('.IP [%s] 5\n' % num) def depart_citation(self, node): pass def visit_citation_reference(self, node): self.body.append('['+node.astext()+']') raise nodes.SkipNode def visit_classifier(self, node): pass def depart_classifier(self, node): pass def visit_colspec(self, node): self.colspecs.append(node) def depart_colspec(self, node): pass def write_colspecs(self): self.body.append("%s.\n" % ('L '*len(self.colspecs))) def visit_comment(self, node, sub=re.compile('-(?=-)').sub): self.body.append(self.comment(node.astext())) raise nodes.SkipNode def visit_contact(self, node): self.visit_docinfo_item(node, 'contact') depart_contact = depart_docinfo_item def visit_container(self, node): pass def depart_container(self, node): pass def visit_compound(self, node): pass def depart_compound(self, node): pass def visit_copyright(self, node): self.visit_docinfo_item(node, 'copyright') def visit_danger(self, node): self.visit_admonition(node, 'danger') depart_danger = depart_admonition def visit_date(self, node): self.visit_docinfo_item(node, 'date') def visit_decoration(self, node): pass def depart_decoration(self, node): pass def visit_definition(self, node): pass def depart_definition(self, node): pass def visit_definition_list(self, node): self.indent(DEFINITION_LIST_INDENT) def depart_definition_list(self, node): self.dedent() def visit_definition_list_item(self, node): self.body.append(self.defs['definition_list_item'][0]) def depart_definition_list_item(self, node): self.body.append(self.defs['definition_list_item'][1]) def visit_description(self, node): pass def depart_description(self, node): pass def visit_docinfo(self, node): self._in_docinfo = 1 def depart_docinfo(self, node): self._in_docinfo = None # NOTE nothing should be written before this self.append_header() def visit_doctest_block(self, node): self.body.append(self.defs['literal_block'][0]) self._in_literal = True def depart_doctest_block(self, node): self._in_literal = False self.body.append(self.defs['literal_block'][1]) def visit_document(self, node): # no blank line between comment and header. self.body.append(self.comment(self.document_start).rstrip()+'\n') # writing header is postboned self.header_written = 0 def depart_document(self, node): if self._docinfo['author']: self.body.append('.SH AUTHOR\n%s\n' % ', '.join(self._docinfo['author'])) skip = ('author', 'copyright', 'date', 'manual_group', 'manual_section', 'subtitle', 'title', 'title_upper', 'version') for name in self._docinfo_keys: if name == 'address': self.body.append("\n%s:\n%s%s.nf\n%s\n.fi\n%s%s" % ( self.language.labels.get(name, name), self.defs['indent'][0] % 0, self.defs['indent'][0] % BLOCKQOUTE_INDENT, self._docinfo[name], self.defs['indent'][1], self.defs['indent'][1])) elif name not in skip: if name in self._docinfo_names: label = self._docinfo_names[name] else: label = self.language.labels.get(name, name) self.body.append("\n%s: %s\n" % (label, self._docinfo[name])) if self._docinfo['copyright']: self.body.append('.SH COPYRIGHT\n%s\n' % self._docinfo['copyright']) self.body.append(self.comment( 'Generated by docutils manpage writer.\n')) def visit_emphasis(self, node): self.body.append(self.defs['emphasis'][0]) def depart_emphasis(self, node): self.body.append(self.defs['emphasis'][1]) def visit_entry(self, node): # a cell in a table row if 'morerows' in node: self.document.reporter.warning('"table row spanning" not supported', base_node=node) if 'morecols' in node: self.document.reporter.warning( '"table cell spanning" not supported', base_node=node) self.context.append(len(self.body)) def depart_entry(self, node): start = self.context.pop() self._active_table.append_cell(self.body[start:]) del self.body[start:] def visit_enumerated_list(self, node): self.list_start(node) def depart_enumerated_list(self, node): self.list_end() def visit_error(self, node): self.visit_admonition(node, 'error') depart_error = depart_admonition def visit_field(self, node): pass def depart_field(self, node): pass def visit_field_body(self, node): if self._in_docinfo: name_normalized = self._field_name.lower().replace(" ","_") self._docinfo_names[name_normalized] = self._field_name self.visit_docinfo_item(node, name_normalized) raise nodes.SkipNode def depart_field_body(self, node): pass def visit_field_list(self, node): self.indent(FIELD_LIST_INDENT) def depart_field_list(self, node): self.dedent() def visit_field_name(self, node): if self._in_docinfo: self._field_name = node.astext() raise nodes.SkipNode else: self.body.append(self.defs['field_name'][0]) def depart_field_name(self, node): self.body.append(self.defs['field_name'][1]) def visit_figure(self, node): self.indent(2.5) self.indent(0) def depart_figure(self, node): self.dedent() self.dedent() def visit_footer(self, node): self.document.reporter.warning('"footer" not supported', base_node=node) def depart_footer(self, node): pass def visit_footnote(self, node): num, text = node.astext().split(None, 1) num = num.strip() self.body.append('.IP [%s] 5\n' % self.deunicode(num)) def depart_footnote(self, node): pass def footnote_backrefs(self, node): self.document.reporter.warning('"footnote_backrefs" not supported', base_node=node) def visit_footnote_reference(self, node): self.body.append('['+self.deunicode(node.astext())+']') raise nodes.SkipNode def depart_footnote_reference(self, node): pass def visit_generated(self, node): pass def depart_generated(self, node): pass def visit_header(self, node): raise NotImplementedError, node.astext() def depart_header(self, node): pass def visit_hint(self, node): self.visit_admonition(node, 'hint') depart_hint = depart_admonition def visit_subscript(self, node): self.body.append('\\s-2\\d') def depart_subscript(self, node): self.body.append('\\u\\s0') def visit_superscript(self, node): self.body.append('\\s-2\\u') def depart_superscript(self, node): self.body.append('\\d\\s0') def visit_attribution(self, node): self.body.append('\\(em ') def depart_attribution(self, node): self.body.append('\n') def visit_image(self, node): self.document.reporter.warning('"image" not supported', base_node=node) text = [] if 'alt' in node.attributes: text.append(node.attributes['alt']) if 'uri' in node.attributes: text.append(node.attributes['uri']) self.body.append('[image: %s]\n' % ('/'.join(text))) raise nodes.SkipNode def visit_important(self, node): self.visit_admonition(node, 'important') depart_important = depart_admonition def visit_label(self, node): # footnote and citation if (isinstance(node.parent, nodes.footnote) or isinstance(node.parent, nodes.citation)): raise nodes.SkipNode self.document.reporter.warning('"unsupported "label"', base_node=node) self.body.append('[') def depart_label(self, node): self.body.append(']\n') def visit_legend(self, node): pass def depart_legend(self, node): pass # WHAT should we use .INDENT, .UNINDENT ? def visit_line_block(self, node): self._line_block += 1 if self._line_block == 1: self.body.append('.sp\n') self.body.append('.nf\n') else: self.body.append('.in +2\n') def depart_line_block(self, node): self._line_block -= 1 if self._line_block == 0: self.body.append('.fi\n') self.body.append('.sp\n') else: self.body.append('.in -2\n') def visit_line(self, node): pass def depart_line(self, node): self.body.append('\n') def visit_list_item(self, node): # man 7 man argues to use ".IP" instead of ".TP" self.body.append('.IP %s %d\n' % ( self._list_char[-1].next(), self._list_char[-1].get_width(),)) def depart_list_item(self, node): pass def visit_literal(self, node): self.body.append(self.defs['literal'][0]) def depart_literal(self, node): self.body.append(self.defs['literal'][1]) def visit_literal_block(self, node): self.body.append(self.defs['literal_block'][0]) self._in_literal = True def depart_literal_block(self, node): self._in_literal = False self.body.append(self.defs['literal_block'][1]) def visit_meta(self, node): raise NotImplementedError, node.astext() def depart_meta(self, node): pass def visit_note(self, node): self.visit_admonition(node, 'note') depart_note = depart_admonition def indent(self, by=0.5): # if we are in a section ".SH" there already is a .RS step = self._indent[-1] self._indent.append(by) self.body.append(self.defs['indent'][0] % step) def dedent(self): self._indent.pop() self.body.append(self.defs['indent'][1]) def visit_option_list(self, node): self.indent(OPTION_LIST_INDENT) def depart_option_list(self, node): self.dedent() def visit_option_list_item(self, node): # one item of the list self.body.append(self.defs['option_list_item'][0]) def depart_option_list_item(self, node): self.body.append(self.defs['option_list_item'][1]) def visit_option_group(self, node): # as one option could have several forms it is a group # options without parameter bold only, .B, -v # options with parameter bold italic, .BI, -f file # # we do not know if .B or .BI self.context.append('.B') # blind guess self.context.append(len(self.body)) # to be able to insert later self.context.append(0) # option counter def depart_option_group(self, node): self.context.pop() # the counter start_position = self.context.pop() text = self.body[start_position:] del self.body[start_position:] self.body.append('%s%s\n' % (self.context.pop(), ''.join(text))) def visit_option(self, node): # each form of the option will be presented separately if self.context[-1] > 0: self.body.append(', ') if self.context[-3] == '.BI': self.body.append('\\') self.body.append(' ') def depart_option(self, node): self.context[-1] += 1 def visit_option_string(self, node): # do not know if .B or .BI pass def depart_option_string(self, node): pass def visit_option_argument(self, node): self.context[-3] = '.BI' # bold/italic alternate if node['delimiter'] != ' ': self.body.append('\\fB%s ' % node['delimiter']) elif self.body[len(self.body) - 1].endswith('='): # a blank only means no blank in output, just changing font self.body.append(' ') else: # blank backslash blank, switch font then a blank self.body.append(' \\ ') def depart_option_argument(self, node): pass def visit_organization(self, node): self.visit_docinfo_item(node, 'organization') def depart_organization(self, node): pass def visit_paragraph(self, node): # ``.PP`` : Start standard indented paragraph. # ``.LP`` : Start block paragraph, all except the first. # ``.P [type]`` : Start paragraph type. # NOTE don't use paragraph starts because they reset indentation. # ``.sp`` is only vertical space self.ensure_eol() self.body.append('.sp\n') def depart_paragraph(self, node): self.body.append('\n') def visit_problematic(self, node): self.body.append(self.defs['problematic'][0]) def depart_problematic(self, node): self.body.append(self.defs['problematic'][1]) def visit_raw(self, node): if node.get('format') == 'manpage': self.body.append(node.astext() + "\n") # Keep non-manpage raw text out of output: raise nodes.SkipNode def visit_reference(self, node): """E.g. link or email address.""" self.body.append(self.defs['reference'][0]) def depart_reference(self, node): self.body.append(self.defs['reference'][1]) def visit_revision(self, node): self.visit_docinfo_item(node, 'revision') depart_revision = depart_docinfo_item def visit_row(self, node): self._active_table.new_row() def depart_row(self, node): pass def visit_section(self, node): self.section_level += 1 def depart_section(self, node): self.section_level -= 1 def visit_status(self, node): self.visit_docinfo_item(node, 'status') depart_status = depart_docinfo_item def visit_strong(self, node): self.body.append(self.defs['strong'][0]) def depart_strong(self, node): self.body.append(self.defs['strong'][1]) def visit_substitution_definition(self, node): """Internal only.""" raise nodes.SkipNode def visit_substitution_reference(self, node): self.document.reporter.warning('"substitution_reference" not supported', base_node=node) def visit_subtitle(self, node): if isinstance(node.parent, nodes.sidebar): self.body.append(self.defs['strong'][0]) elif isinstance(node.parent, nodes.document): self.visit_docinfo_item(node, 'subtitle') elif isinstance(node.parent, nodes.section): self.body.append(self.defs['strong'][0]) def depart_subtitle(self, node): # document subtitle calls SkipNode self.body.append(self.defs['strong'][1]+'\n.PP\n') def visit_system_message(self, node): # TODO add report_level #if node['level'] < self.document.reporter['writer'].report_level: # Level is too low to display: # raise nodes.SkipNode attr = {} if node.hasattr('id'): attr['name'] = node['id'] if node.hasattr('line'): line = ', line %s' % node['line'] else: line = '' self.body.append('.IP "System Message: %s/%s (%s:%s)"\n' % (node['type'], node['level'], node['source'], line)) def depart_system_message(self, node): pass def visit_table(self, node): self._active_table = Table() def depart_table(self, node): self.ensure_eol() self.body.extend(self._active_table.as_list()) self._active_table = None def visit_target(self, node): # targets are in-document hyper targets, without any use for man-pages. raise nodes.SkipNode def visit_tbody(self, node): pass def depart_tbody(self, node): pass def visit_term(self, node): self.body.append(self.defs['term'][0]) def depart_term(self, node): self.body.append(self.defs['term'][1]) def visit_tgroup(self, node): pass def depart_tgroup(self, node): pass def visit_thead(self, node): # MAYBE double line '=' pass def depart_thead(self, node): # MAYBE double line '=' pass def visit_tip(self, node): self.visit_admonition(node, 'tip') depart_tip = depart_admonition def visit_title(self, node): if isinstance(node.parent, nodes.topic): self.body.append(self.defs['topic-title'][0]) elif isinstance(node.parent, nodes.sidebar): self.body.append(self.defs['sidebar-title'][0]) elif isinstance(node.parent, nodes.admonition): self.body.append('.IP "') elif self.section_level == 0: self._docinfo['title'] = node.astext() # document title for .TH self._docinfo['title_upper'] = node.astext().upper() raise nodes.SkipNode elif self.section_level == 1: self.body.append('.SH ') for n in node.traverse(nodes.Text): n.parent.replace(n, nodes.Text(n.astext().upper())) else: self.body.append('.SS ') def depart_title(self, node): if isinstance(node.parent, nodes.admonition): self.body.append('"') self.body.append('\n') def visit_title_reference(self, node): """inline citation reference""" self.body.append(self.defs['title_reference'][0]) def depart_title_reference(self, node): self.body.append(self.defs['title_reference'][1]) def visit_topic(self, node): pass def depart_topic(self, node): pass def visit_sidebar(self, node): pass def depart_sidebar(self, node): pass def visit_rubric(self, node): pass def depart_rubric(self, node): pass def visit_transition(self, node): # .PP Begin a new paragraph and reset prevailing indent. # .sp N leaves N lines of blank space. # .ce centers the next line self.body.append('\n.sp\n.ce\n----\n') def depart_transition(self, node): self.body.append('\n.ce 0\n.sp\n') def visit_version(self, node): self.visit_docinfo_item(node, 'version') def visit_warning(self, node): self.visit_admonition(node, 'warning') depart_warning = depart_admonition def unimplemented_visit(self, node): raise NotImplementedError('visiting unimplemented node type: %s' % node.__class__.__name__) # vim: set fileencoding=utf-8 et ts=4 ai : mercurial-2.8.2/doc/runrst0000755000175000017500000000274412261160437017355 0ustar oxymoronoxymoron00000000000000#!/usr/bin/env python # # runrst - register custom roles and run correct writer # # Copyright 2010 Matt Mackall and others # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. """usage: %s WRITER args... where WRITER is the name of a Docutils writer such as 'html' or 'manpage' """ import sys try: from docutils.parsers.rst import roles from docutils.core import publish_cmdline from docutils import nodes, utils except ImportError: sys.stderr.write("abort: couldn't generate documentation: docutils " "module is missing\n") sys.stderr.write("please install python-docutils or see " "http://docutils.sourceforge.net/\n") sys.exit(-1) def role_hg(name, rawtext, text, lineno, inliner, options={}, content=[]): text = "hg " + utils.unescape(text) linktext = nodes.literal(rawtext, text) parts = text.split() cmd, args = parts[1], parts[2:] if cmd == 'help' and args: cmd = args[0] # link to 'dates' for 'hg help dates' node = nodes.reference(rawtext, '', linktext, refuri="hg.1.html#%s" % cmd) return [node], [] roles.register_local_role("hg", role_hg) if __name__ == "__main__": if len(sys.argv) < 2: sys.stderr.write(__doc__ % sys.argv[0]) sys.exit(1) writer = sys.argv[1] del sys.argv[1] publish_cmdline(writer_name=writer) mercurial-2.8.2/doc/Makefile0000644000175000017500000000235012261160437017523 0ustar oxymoronoxymoron00000000000000SOURCES=$(notdir $(wildcard ../mercurial/help/*.[0-9].txt)) MAN=$(SOURCES:%.txt=%) HTML=$(SOURCES:%.txt=%.html) GENDOC=gendoc.py ../mercurial/commands.py ../mercurial/help.py \ ../mercurial/help/*.txt ../hgext/*.py ../hgext/*/__init__.py PREFIX=/usr/local MANDIR=$(PREFIX)/share/man INSTALL=install -c -m 644 PYTHON=python RSTARGS= export HGENCODING=UTF-8 all: man html man: $(MAN) html: $(HTML) common.txt $(SOURCES) $(SOURCES:%.txt=%.gendoc.txt): $(GENDOC) ${PYTHON} gendoc.py $(basename $@) > $@.tmp mv $@.tmp $@ %: %.txt %.gendoc.txt common.txt $(PYTHON) runrst hgmanpage $(RSTARGS) --halt warning \ --strip-elements-with-class htmlonly $*.txt $* %.html: %.txt %.gendoc.txt common.txt $(PYTHON) runrst html $(RSTARGS) --halt warning \ --link-stylesheet --stylesheet-path style.css $*.txt $*.html MANIFEST: man html # tracked files are already in the main MANIFEST $(RM) $@ for i in $(MAN) $(HTML); do \ echo "doc/$$i" >> $@ ; \ done install: man for i in $(MAN) ; do \ subdir=`echo $$i | sed -n 's/^.*\.\([0-9]\)$$/man\1/p'` ; \ mkdir -p $(DESTDIR)$(MANDIR)/$$subdir ; \ $(INSTALL) $$i $(DESTDIR)$(MANDIR)/$$subdir ; \ done clean: $(RM) $(MAN) $(HTML) common.txt $(SOURCES) $(SOURCES:%.txt=%.gendoc.txt) MANIFEST mercurial-2.8.2/doc/hgignore.5.html0000644000175000017500000001553612261160452020725 0ustar oxymoronoxymoron00000000000000 hgignore

hgignore

syntax for Mercurial ignore files

Author: Vadim Gelfer <vadim.gelfer@gmail.com>
Organization: Mercurial
Manual section:5
Manual group:Mercurial Manual

Synopsis

The Mercurial system uses a file called .hgignore in the root directory of a repository to control its behavior when it searches for files that it is not currently tracking.

Description

The working directory of a Mercurial repository will often contain files that should not be tracked by Mercurial. These include backup files created by editors and build products created by compilers. These files can be ignored by listing them in a .hgignore file in the root of the working directory. The .hgignore file must be created manually. It is typically put under version control, so that the settings will propagate to other repositories with push and pull.

An untracked file is ignored if its path relative to the repository root directory, or any prefix path of that path, is matched against any pattern in .hgignore.

For example, say we have an untracked file, file.c, at a/b/file.c inside our repository. Mercurial will ignore file.c if any pattern in .hgignore matches a/b/file.c, a/b or a.

In addition, a Mercurial configuration file can reference a set of per-user or global ignore files. See the ignore configuration key on the [ui] section of hg help config for details of how to configure these files.

To control Mercurial's handling of files that it manages, many commands support the -I and -X options; see hg help <command> and hg help patterns for details.

Files that are already tracked are not affected by .hgignore, even if they appear in .hgignore. An untracked file X can be explicitly added with hg add X, even if X would be excluded by a pattern in .hgignore.

Syntax

An ignore file is a plain text file consisting of a list of patterns, with one pattern per line. Empty lines are skipped. The # character is treated as a comment character, and the \ character is treated as an escape character.

Mercurial supports several pattern syntaxes. The default syntax used is Python/Perl-style regular expressions.

To change the syntax used, use a line of the following form:

syntax: NAME

where NAME is one of the following:

regexp
Regular expression, Python/Perl syntax.
glob
Shell-style glob.

The chosen syntax stays in effect when parsing all patterns that follow, until another syntax is selected.

Neither glob nor regexp patterns are rooted. A glob-syntax pattern of the form *.c will match a file ending in .c in any directory, and a regexp pattern of the form \.c$ will do the same. To root a regexp pattern, start it with ^.

Note

Patterns specified in other than .hgignore are always rooted. Please see hg help patterns for details.

Example

Here is an example ignore file.

# use glob syntax.
syntax: glob

*.elc
*.pyc
*~

# switch to regexp syntax.
syntax: regexp
^\.pc/

Author

Vadim Gelfer <vadim.gelfer@gmail.com>

Mercurial was written by Matt Mackall <mpm@selenic.com>.

See Also

hg(1), hgrc(5)

Copying

This manual page is copyright 2006 Vadim Gelfer. Mercurial is copyright 2005-2013 Matt Mackall. Free use of this software is granted under the terms of the GNU General Public License version 2 or any later version.

mercurial-2.8.2/doc/hg.10000644000175000017500000105704612261160447016561 0ustar oxymoronoxymoron00000000000000.\" Man page generated from reStructuredText. . .TH HG 1 "" "" "Mercurial Manual" .SH NAME hg \- Mercurial source code management system . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp \fBhg\fP \fIcommand\fP [\fIoption\fP]... [\fIargument\fP]... .SH DESCRIPTION .sp The \fBhg\fP command provides a command line interface to the Mercurial system. .SH COMMAND ELEMENTS .INDENT 0.0 .TP .B files... . indicates one or more filename or relative path filenames; see \%File Name Patterns\: for information on pattern matching .TP .B path . indicates a path on the local machine .TP .B revision . indicates a changeset which can be specified as a changeset revision number, a tag, or a unique substring of the changeset hash value .TP .B repository path . either the pathname of a local repository or the URI of a remote repository. .UNINDENT .SH OPTIONS .INDENT 0.0 .TP .B \-R, \-\-repository . repository root directory or name of overlay bundle file .TP .B \-\-cwd . change working directory .TP .B \-y, \-\-noninteractive . do not prompt, automatically pick the first choice for all prompts .TP .B \-q, \-\-quiet . suppress output .TP .B \-v, \-\-verbose . enable additional output .TP .B \-\-config . set/override config option (use \(aqsection.name=value\(aq) .TP .B \-\-debug . enable debugging output .TP .B \-\-debugger . start debugger .TP .B \-\-encoding . set the charset encoding (default: UTF\-8) .TP .B \-\-encodingmode . set the charset encoding mode (default: strict) .TP .B \-\-traceback . always print a traceback on exception .TP .B \-\-time . time how long the command takes .TP .B \-\-profile . print command execution profile .TP .B \-\-version . output version information and exit .TP .B \-h, \-\-help . display help and exit .TP .B \-\-hidden . consider hidden changesets .UNINDENT .SH COMMANDS .SS add .sp .nf .ft C hg add [OPTION]... [FILE]... .ft P .fi .sp Schedule files to be version controlled and added to the repository. .sp The files will be added to the repository at the next commit. To undo an add before that, see \%\fBhg forget\fP\:. .sp If no names are given, add all files to the repository. .sp An example showing how new (unknown) files are added automatically by \%\fBhg add\fP\:: .sp .nf .ft C $ ls foo.c $ hg status ? foo.c $ hg add adding foo.c $ hg status A foo.c .ft P .fi .sp Returns 0 if all files are successfully added. .sp Options: .INDENT 0.0 .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .TP .B \-S, \-\-subrepos . recurse into subrepositories .TP .B \-n, \-\-dry\-run . do not perform actions, just print output .UNINDENT .SS addremove .sp .nf .ft C hg addremove [OPTION]... [FILE]... .ft P .fi .sp Add all new files and remove all missing files from the repository. .sp New files are ignored if they match any of the patterns in \fB.hgignore\fP. As with add, these changes take effect at the next commit. .sp Use the \-s/\-\-similarity option to detect renamed files. This option takes a percentage between 0 (disabled) and 100 (files must be identical) as its parameter. With a parameter greater than 0, this compares every removed file with every added file and records those similar enough as renames. Detecting renamed files this way can be expensive. After using this option, \%\fBhg status \-C\fP\: can be used to check which files were identified as moved or renamed. If not specified, \-s/\-\-similarity defaults to 100 and only renames of identical files are detected. .sp Returns 0 if all files are successfully added. .sp Options: .INDENT 0.0 .TP .B \-s, \-\-similarity . guess renamed files by similarity (0<=s<=100) .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .TP .B \-n, \-\-dry\-run . do not perform actions, just print output .UNINDENT .SS annotate .sp .nf .ft C hg annotate [\-r REV] [\-f] [\-a] [\-u] [\-d] [\-n] [\-c] [\-l] FILE... .ft P .fi .sp List changes in files, showing the revision id responsible for each line .sp This command is useful for discovering when a change was made and by whom. .sp Without the \-a/\-\-text option, annotate will avoid processing files it detects as binary. With \-a, annotate will annotate the file anyway, although the results will probably be neither useful nor desirable. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-r, \-\-rev . annotate the specified revision .TP .B \-\-follow . follow copies/renames and list the filename (DEPRECATED) .TP .B \-\-no\-follow . don\(aqt follow copies and renames .TP .B \-a, \-\-text . treat all files as text .TP .B \-u, \-\-user . list the author (long with \-v) .TP .B \-f, \-\-file . list the filename .TP .B \-d, \-\-date . list the date (short with \-q) .TP .B \-n, \-\-number . list the revision number (default) .TP .B \-c, \-\-changeset . list the changeset .TP .B \-l, \-\-line\-number . show line number at the first appearance .TP .B \-w, \-\-ignore\-all\-space . ignore white space when comparing lines .TP .B \-b, \-\-ignore\-space\-change . ignore changes in the amount of white space .TP .B \-B, \-\-ignore\-blank\-lines . ignore changes whose lines are all blank .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .sp aliases: blame .UNINDENT .SS archive .sp .nf .ft C hg archive [OPTION]... DEST .ft P .fi .sp By default, the revision used is the parent of the working directory; use \-r/\-\-rev to specify a different revision. .sp The archive type is automatically detected based on file extension (or override using \-t/\-\-type). .sp Examples: .INDENT 0.0 .IP \(bu 2 . create a zip file containing the 1.0 release: .sp .nf .ft C hg archive \-r 1.0 project\-1.0.zip .ft P .fi .IP \(bu 2 . create a tarball excluding .hg files: .sp .nf .ft C hg archive project.tar.gz \-X ".hg*" .ft P .fi .UNINDENT .sp Valid types are: .INDENT 0.0 .TP .B \fBfiles\fP .sp a directory full of files (default) .TP .B \fBtar\fP .sp tar archive, uncompressed .TP .B \fBtbz2\fP .sp tar archive, compressed using bzip2 .TP .B \fBtgz\fP .sp tar archive, compressed using gzip .TP .B \fBuzip\fP .sp zip archive, uncompressed .TP .B \fBzip\fP .sp zip archive, compressed using deflate .UNINDENT .sp The exact name of the destination archive or directory is given using a format string; see \%\fBhg help export\fP\: for details. .sp Each member added to an archive file has a directory prefix prepended. Use \-p/\-\-prefix to specify a format string for the prefix. The default is the basename of the archive, with suffixes removed. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-\-no\-decode . do not pass files through decoders .TP .B \-p, \-\-prefix . directory prefix for files in archive .TP .B \-r, \-\-rev . revision to distribute .TP .B \-t, \-\-type . type of distribution to create .TP .B \-S, \-\-subrepos . recurse into subrepositories .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .UNINDENT .SS backout .sp .nf .ft C hg backout [OPTION]... [\-r] REV .ft P .fi .sp Prepare a new changeset with the effect of REV undone in the current working directory. .sp If REV is the parent of the working directory, then this new changeset is committed automatically. Otherwise, hg needs to merge the changes and the merged result is left uncommitted. .IP Note . backout cannot be used to fix either an unwanted or incorrect merge. .RE .sp By default, the pending changeset will have one parent, maintaining a linear history. With \-\-merge, the pending changeset will instead have two parents: the old parent of the working directory and a new child of REV that simply undoes REV. .sp Before version 1.7, the behavior without \-\-merge was equivalent to specifying \-\-merge followed by \%\fBhg update \-\-clean .\fP\: to cancel the merge and leave the child of REV as a head to be merged separately. .sp See \%\fBhg help dates\fP\: for a list of formats valid for \-d/\-\-date. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-\-merge . merge with old dirstate parent after backout .TP .B \-\-parent . parent to choose when backing out merge (DEPRECATED) .TP .B \-r, \-\-rev . revision to backout .TP .B \-t, \-\-tool . specify merge tool .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .TP .B \-m, \-\-message . use text as commit message .TP .B \-l, \-\-logfile . read commit message from file .TP .B \-d, \-\-date . record the specified date as commit date .TP .B \-u, \-\-user . record the specified user as committer .UNINDENT .SS bisect .sp .nf .ft C hg bisect [\-gbsr] [\-U] [\-c CMD] [REV] .ft P .fi .sp This command helps to find changesets which introduce problems. To use, mark the earliest changeset you know exhibits the problem as bad, then mark the latest changeset which is free from the problem as good. Bisect will update your working directory to a revision for testing (unless the \-U/\-\-noupdate option is specified). Once you have performed tests, mark the working directory as good or bad, and bisect will either update to another candidate changeset or announce that it has found the bad revision. .sp As a shortcut, you can also use the revision argument to mark a revision as good or bad without checking it out first. .sp If you supply a command, it will be used for automatic bisection. The environment variable HG_NODE will contain the ID of the changeset being tested. The exit status of the command will be used to mark revisions as good or bad: status 0 means good, 125 means to skip the revision, 127 (command not found) will abort the bisection, and any other non\-zero exit status means the revision is bad. .sp Some examples: .INDENT 0.0 .IP \(bu 2 . start a bisection with known bad revision 34, and good revision 12: .sp .nf .ft C hg bisect \-\-bad 34 hg bisect \-\-good 12 .ft P .fi .IP \(bu 2 . advance the current bisection by marking current revision as good or bad: .sp .nf .ft C hg bisect \-\-good hg bisect \-\-bad .ft P .fi .IP \(bu 2 . mark the current revision, or a known revision, to be skipped (e.g. if that revision is not usable because of another issue): .sp .nf .ft C hg bisect \-\-skip hg bisect \-\-skip 23 .ft P .fi .IP \(bu 2 . skip all revisions that do not touch directories \fBfoo\fP or \fBbar\fP: .sp .nf .ft C hg bisect \-\-skip "!( file(\(aqpath:foo\(aq) & file(\(aqpath:bar\(aq) )" .ft P .fi .IP \(bu 2 . forget the current bisection: .sp .nf .ft C hg bisect \-\-reset .ft P .fi .IP \(bu 2 . use \(aqmake && make tests\(aq to automatically find the first broken revision: .sp .nf .ft C hg bisect \-\-reset hg bisect \-\-bad 34 hg bisect \-\-good 12 hg bisect \-\-command "make && make tests" .ft P .fi .IP \(bu 2 . see all changesets whose states are already known in the current bisection: .sp .nf .ft C hg log \-r "bisect(pruned)" .ft P .fi .IP \(bu 2 . see the changeset currently being bisected (especially useful if running with \-U/\-\-noupdate): .sp .nf .ft C hg log \-r "bisect(current)" .ft P .fi .IP \(bu 2 . see all changesets that took part in the current bisection: .sp .nf .ft C hg log \-r "bisect(range)" .ft P .fi .IP \(bu 2 . you can even get a nice graph: .sp .nf .ft C hg log \-\-graph \-r "bisect(range)" .ft P .fi .UNINDENT .sp See \%\fBhg help revsets\fP\: for more about the \fIbisect()\fP keyword. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-r, \-\-reset . reset bisect state .TP .B \-g, \-\-good . mark changeset good .TP .B \-b, \-\-bad . mark changeset bad .TP .B \-s, \-\-skip . skip testing changeset .TP .B \-e, \-\-extend . extend the bisect range .TP .B \-c, \-\-command . use command to check changeset state .TP .B \-U, \-\-noupdate . do not update to target .UNINDENT .SS bookmarks .sp .nf .ft C hg bookmarks [OPTIONS]... [NAME]... .ft P .fi .sp Bookmarks are pointers to certain commits that move when committing. Bookmarks are local. They can be renamed, copied and deleted. It is possible to use \%\fBhg merge NAME\fP\: to merge from a given bookmark, and \%\fBhg update NAME\fP\: to update to a given bookmark. .sp You can use \%\fBhg bookmark NAME\fP\: to set a bookmark on the working directory\(aqs parent revision with the given name. If you specify a revision using \-r REV (where REV may be an existing bookmark), the bookmark is assigned to that revision. .sp Bookmarks can be pushed and pulled between repositories (see \%\fBhg help push\fP\: and \%\fBhg help pull\fP\:). This requires both the local and remote repositories to support bookmarks. For versions prior to 1.8, this means the bookmarks extension must be enabled. .sp If you set a bookmark called \(aq@\(aq, new clones of the repository will have that revision checked out (and the bookmark made active) by default. .sp With \-i/\-\-inactive, the new bookmark will not be made the active bookmark. If \-r/\-\-rev is given, the new bookmark will not be made active even if \-i/\-\-inactive is not given. If no NAME is given, the current active bookmark will be marked inactive. .sp Options: .INDENT 0.0 .TP .B \-f, \-\-force . force .TP .B \-r, \-\-rev . revision .TP .B \-d, \-\-delete . delete a given bookmark .TP .B \-m, \-\-rename . rename a given bookmark .TP .B \-i, \-\-inactive . mark a bookmark inactive .sp aliases: bookmark .UNINDENT .SS branch .sp .nf .ft C hg branch [\-fC] [NAME] .ft P .fi .IP Note . Branch names are permanent and global. Use \%\fBhg bookmark\fP\: to create a light\-weight bookmark instead. See \%\fBhg help glossary\fP\: for more information about named branches and bookmarks. .RE .sp With no argument, show the current branch name. With one argument, set the working directory branch name (the branch will not exist in the repository until the next commit). Standard practice recommends that primary development take place on the \(aqdefault\(aq branch. .sp Unless \-f/\-\-force is specified, branch will not let you set a branch name that already exists, even if it\(aqs inactive. .sp Use \-C/\-\-clean to reset the working directory branch to that of the parent of the working directory, negating a previous branch change. .sp Use the command \%\fBhg update\fP\: to switch to an existing branch. Use \%\fBhg commit \-\-close\-branch\fP\: to mark this branch as closed. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-f, \-\-force . set branch name even if it shadows an existing branch .TP .B \-C, \-\-clean . reset branch name to parent branch name .UNINDENT .SS branches .sp .nf .ft C hg branches [\-ac] .ft P .fi .sp List the repository\(aqs named branches, indicating which ones are inactive. If \-c/\-\-closed is specified, also list branches which have been marked closed (see \%\fBhg commit \-\-close\-branch\fP\:). .sp If \-a/\-\-active is specified, only show active branches. A branch is considered active if it contains repository heads. .sp Use the command \%\fBhg update\fP\: to switch to an existing branch. .sp Returns 0. .sp Options: .INDENT 0.0 .TP .B \-a, \-\-active . show only branches that have unmerged heads .TP .B \-c, \-\-closed . show normal and closed branches .UNINDENT .SS bundle .sp .nf .ft C hg bundle [\-f] [\-t TYPE] [\-a] [\-r REV]... [\-\-base REV]... FILE [DEST] .ft P .fi .sp Generate a compressed changegroup file collecting changesets not known to be in another repository. .sp If you omit the destination repository, then hg assumes the destination will have all the nodes you specify with \-\-base parameters. To create a bundle containing all changesets, use \-a/\-\-all (or \-\-base null). .sp You can change compression method with the \-t/\-\-type option. The available compression methods are: none, bzip2, and gzip (by default, bundles are compressed using bzip2). .sp The bundle file can then be transferred using conventional means and applied to another repository with the unbundle or pull command. This is useful when direct push and pull are not available or when exporting an entire repository is undesirable. .sp Applying bundles preserves all changeset contents including permissions, copy/rename information, and revision history. .sp Returns 0 on success, 1 if no changes found. .sp Options: .INDENT 0.0 .TP .B \-f, \-\-force . run even when the destination is unrelated .TP .B \-r, \-\-rev . a changeset intended to be added to the destination .TP .B \-b, \-\-branch . a specific branch you would like to bundle .TP .B \-\-base . a base changeset assumed to be available at the destination .TP .B \-a, \-\-all . bundle all changesets in the repository .TP .B \-t, \-\-type . bundle compression type to use (default: bzip2) .TP .B \-e, \-\-ssh . specify ssh command to use .TP .B \-\-remotecmd . specify hg command to run on the remote side .TP .B \-\-insecure . do not verify server certificate (ignoring web.cacerts config) .UNINDENT .SS cat .sp .nf .ft C hg cat [OPTION]... FILE... .ft P .fi .sp Print the specified files as they were at the given revision. If no revision is given, the parent of the working directory is used. .sp Output may be to a file, in which case the name of the file is given using a format string. The formatting rules are the same as for the export command, with the following additions: .INDENT 0.0 .TP .B \fB%s\fP .sp basename of file being printed .TP .B \fB%d\fP .sp dirname of file being printed, or \(aq.\(aq if in repository root .TP .B \fB%p\fP .sp root\-relative path name of file being printed .UNINDENT .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-o, \-\-output . print output to file with formatted name .TP .B \-r, \-\-rev . print the given revision .TP .B \-\-decode . apply any matching decode filter .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .UNINDENT .SS clone .sp .nf .ft C hg clone [OPTION]... SOURCE [DEST] .ft P .fi .sp Create a copy of an existing repository in a new directory. .sp If no destination directory name is specified, it defaults to the basename of the source. .sp The location of the source is added to the new repository\(aqs \fB.hg/hgrc\fP file, as the default to be used for future pulls. .sp Only local paths and \fBssh://\fP URLs are supported as destinations. For \fBssh://\fP destinations, no working directory or \fB.hg/hgrc\fP will be created on the remote side. .sp To pull only a subset of changesets, specify one or more revisions identifiers with \-r/\-\-rev or branches with \-b/\-\-branch. The resulting clone will contain only the specified changesets and their ancestors. These options (or \(aqclone src#rev dest\(aq) imply \-\-pull, even for local source repositories. Note that specifying a tag will include the tagged changeset but not the changeset containing the tag. .sp If the source repository has a bookmark called \(aq@\(aq set, that revision will be checked out in the new repository by default. .sp To check out a particular version, use \-u/\-\-update, or \-U/\-\-noupdate to create a clone with no working directory. .sp For efficiency, hardlinks are used for cloning whenever the source and destination are on the same filesystem (note this applies only to the repository data, not to the working directory). Some filesystems, such as AFS, implement hardlinking incorrectly, but do not report errors. In these cases, use the \-\-pull option to avoid hardlinking. .sp In some cases, you can clone repositories and the working directory using full hardlinks with .sp .nf .ft C $ cp \-al REPO REPOCLONE .ft P .fi .sp This is the fastest way to clone, but it is not always safe. The operation is not atomic (making sure REPO is not modified during the operation is up to you) and you have to make sure your editor breaks hardlinks (Emacs and most Linux Kernel tools do so). Also, this is not compatible with certain extensions that place their metadata under the .hg directory, such as mq. .sp Mercurial will update the working directory to the first applicable revision from this list: .INDENT 0.0 .IP a. 3 . null if \-U or the source repository has no changesets .IP b. 3 . if \-u . and the source repository is local, the first parent of the source repository\(aqs working directory .IP c. 3 . the changeset specified with \-u (if a branch name, this means the latest head of that branch) .IP d. 3 . the changeset specified with \-r .IP e. 3 . the tipmost head specified with \-b .IP f. 3 . the tipmost head specified with the url#branch source syntax .IP g. 3 . the revision marked with the \(aq@\(aq bookmark, if present .IP h. 3 . the tipmost head of the default branch .IP i. 3 . tip .UNINDENT .sp Examples: .INDENT 0.0 .IP \(bu 2 . clone a remote repository to a new directory named hg/: .sp .nf .ft C hg clone http://selenic.com/hg .ft P .fi .IP \(bu 2 . create a lightweight local clone: .sp .nf .ft C hg clone project/ project\-feature/ .ft P .fi .IP \(bu 2 . clone from an absolute path on an ssh server (note double\-slash): .sp .nf .ft C hg clone ssh://user@server//home/projects/alpha/ .ft P .fi .IP \(bu 2 . do a high\-speed clone over a LAN while checking out a specified version: .sp .nf .ft C hg clone \-\-uncompressed http://server/repo \-u 1.5 .ft P .fi .IP \(bu 2 . create a repository without changesets after a particular revision: .sp .nf .ft C hg clone \-r 04e544 experimental/ good/ .ft P .fi .IP \(bu 2 . clone (and track) a particular named branch: .sp .nf .ft C hg clone http://selenic.com/hg#stable .ft P .fi .UNINDENT .sp See \%\fBhg help urls\fP\: for details on specifying URLs. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-U, \-\-noupdate . the clone will include an empty working copy (only a repository) .TP .B \-u, \-\-updaterev . revision, tag or branch to check out .TP .B \-r, \-\-rev . include the specified changeset .TP .B \-b, \-\-branch . clone only the specified branch .TP .B \-\-pull . use pull protocol to copy metadata .TP .B \-\-uncompressed . use uncompressed transfer (fast over LAN) .TP .B \-e, \-\-ssh . specify ssh command to use .TP .B \-\-remotecmd . specify hg command to run on the remote side .TP .B \-\-insecure . do not verify server certificate (ignoring web.cacerts config) .UNINDENT .SS commit .sp .nf .ft C hg commit [OPTION]... [FILE]... .ft P .fi .sp Commit changes to the given files into the repository. Unlike a centralized SCM, this operation is a local operation. See \%\fBhg push\fP\: for a way to actively distribute your changes. .sp If a list of files is omitted, all changes reported by \%\fBhg status\fP\: will be committed. .sp If you are committing the result of a merge, do not provide any filenames or \-I/\-X filters. .sp If no commit message is specified, Mercurial starts your configured editor where you can enter a message. In case your commit fails, you will find a backup of your message in \fB.hg/last\-message.txt\fP. .sp The \-\-amend flag can be used to amend the parent of the working directory with a new commit that contains the changes in the parent in addition to those currently reported by \%\fBhg status\fP\:, if there are any. The old commit is stored in a backup bundle in \fB.hg/strip\-backup\fP (see \%\fBhg help bundle\fP\: and \%\fBhg help unbundle\fP\: on how to restore it). .sp Message, user and date are taken from the amended commit unless specified. When a message isn\(aqt specified on the command line, the editor will open with the message of the amended commit. .sp It is not possible to amend public changesets (see \%\fBhg help phases\fP\:) or changesets that have children. .sp See \%\fBhg help dates\fP\: for a list of formats valid for \-d/\-\-date. .sp Returns 0 on success, 1 if nothing changed. .sp Options: .INDENT 0.0 .TP .B \-A, \-\-addremove . mark new/missing files as added/removed before committing .TP .B \-\-close\-branch . mark a branch as closed, hiding it from the branch list .TP .B \-\-amend . amend the parent of the working dir .TP .B \-s, \-\-secret . use the secret phase for committing .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .TP .B \-m, \-\-message . use text as commit message .TP .B \-l, \-\-logfile . read commit message from file .TP .B \-d, \-\-date . record the specified date as commit date .TP .B \-u, \-\-user . record the specified user as committer .TP .B \-S, \-\-subrepos . recurse into subrepositories .sp aliases: ci .UNINDENT .SS copy .sp .nf .ft C hg copy [OPTION]... [SOURCE]... DEST .ft P .fi .sp Mark dest as having copies of source files. If dest is a directory, copies are put in that directory. If dest is a file, the source must be a single file. .sp By default, this command copies the contents of files as they exist in the working directory. If invoked with \-A/\-\-after, the operation is recorded, but no copying is performed. .sp This command takes effect with the next commit. To undo a copy before that, see \%\fBhg revert\fP\:. .sp Returns 0 on success, 1 if errors are encountered. .sp Options: .INDENT 0.0 .TP .B \-A, \-\-after . record a copy that has already occurred .TP .B \-f, \-\-force . forcibly copy over an existing managed file .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .TP .B \-n, \-\-dry\-run . do not perform actions, just print output .sp aliases: cp .UNINDENT .SS diff .sp .nf .ft C hg diff [OPTION]... ([\-c REV] | [\-r REV1 [\-r REV2]]) [FILE]... .ft P .fi .sp Show differences between revisions for the specified files. .sp Differences between files are shown using the unified diff format. .IP Note . diff may generate unexpected results for merges, as it will default to comparing against the working directory\(aqs first parent changeset if no revisions are specified. .RE .sp When two revision arguments are given, then changes are shown between those revisions. If only one revision is specified then that revision is compared to the working directory, and, when no revisions are specified, the working directory files are compared to its parent. .sp Alternatively you can specify \-c/\-\-change with a revision to see the changes in that changeset relative to its first parent. .sp Without the \-a/\-\-text option, diff will avoid generating diffs of files it detects as binary. With \-a, diff will generate a diff anyway, probably with undesirable results. .sp Use the \-g/\-\-git option to generate diffs in the git extended diff format. For more information, read \%\fBhg help diffs\fP\:. .sp Examples: .INDENT 0.0 .IP \(bu 2 . compare a file in the current working directory to its parent: .sp .nf .ft C hg diff foo.c .ft P .fi .IP \(bu 2 . compare two historical versions of a directory, with rename info: .sp .nf .ft C hg diff \-\-git \-r 1.0:1.2 lib/ .ft P .fi .IP \(bu 2 . get change stats relative to the last change on some date: .sp .nf .ft C hg diff \-\-stat \-r "date(\(aqmay 2\(aq)" .ft P .fi .IP \(bu 2 . diff all newly\-added files that contain a keyword: .sp .nf .ft C hg diff "set:added() and grep(GNU)" .ft P .fi .IP \(bu 2 . compare a revision and its parents: .sp .nf .ft C hg diff \-c 9353 # compare against first parent hg diff \-r 9353^:9353 # same using revset syntax hg diff \-r 9353^2:9353 # compare against the second parent .ft P .fi .UNINDENT .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-r, \-\-rev . revision .TP .B \-c, \-\-change . change made by revision .TP .B \-a, \-\-text . treat all files as text .TP .B \-g, \-\-git . use git extended diff format .TP .B \-\-nodates . omit dates from diff headers .TP .B \-p, \-\-show\-function . show which function each change is in .TP .B \-\-reverse . produce a diff that undoes the changes .TP .B \-w, \-\-ignore\-all\-space . ignore white space when comparing lines .TP .B \-b, \-\-ignore\-space\-change . ignore changes in the amount of white space .TP .B \-B, \-\-ignore\-blank\-lines . ignore changes whose lines are all blank .TP .B \-U, \-\-unified . number of lines of context to show .TP .B \-\-stat . output diffstat\-style summary of changes .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .TP .B \-S, \-\-subrepos . recurse into subrepositories .UNINDENT .SS export .sp .nf .ft C hg export [OPTION]... [\-o OUTFILESPEC] [\-r] [REV]... .ft P .fi .sp Print the changeset header and diffs for one or more revisions. If no revision is given, the parent of the working directory is used. .sp The information shown in the changeset header is: author, date, branch name (if non\-default), changeset hash, parent(s) and commit comment. .IP Note . export may generate unexpected diff output for merge changesets, as it will compare the merge changeset against its first parent only. .RE .sp Output may be to a file, in which case the name of the file is given using a format string. The formatting rules are as follows: .INDENT 0.0 .TP .B \fB%%\fP .sp literal "%" character .TP .B \fB%H\fP .sp changeset hash (40 hexadecimal digits) .TP .B \fB%N\fP .sp number of patches being generated .TP .B \fB%R\fP .sp changeset revision number .TP .B \fB%b\fP .sp basename of the exporting repository .TP .B \fB%h\fP .sp short\-form changeset hash (12 hexadecimal digits) .TP .B \fB%m\fP .sp first line of the commit message (only alphanumeric characters) .TP .B \fB%n\fP .sp zero\-padded sequence number, starting at 1 .TP .B \fB%r\fP .sp zero\-padded changeset revision number .UNINDENT .sp Without the \-a/\-\-text option, export will avoid generating diffs of files it detects as binary. With \-a, export will generate a diff anyway, probably with undesirable results. .sp Use the \-g/\-\-git option to generate diffs in the git extended diff format. See \%\fBhg help diffs\fP\: for more information. .sp With the \-\-switch\-parent option, the diff will be against the second parent. It can be useful to review a merge. .sp Examples: .INDENT 0.0 .IP \(bu 2 . use export and import to transplant a bugfix to the current branch: .sp .nf .ft C hg export \-r 9353 | hg import \- .ft P .fi .IP \(bu 2 . export all the changesets between two revisions to a file with rename information: .sp .nf .ft C hg export \-\-git \-r 123:150 > changes.txt .ft P .fi .IP \(bu 2 . split outgoing changes into a series of patches with descriptive names: .sp .nf .ft C hg export \-r "outgoing()" \-o "%n\-%m.patch" .ft P .fi .UNINDENT .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-o, \-\-output . print output to file with formatted name .TP .B \-\-switch\-parent . diff against the second parent .TP .B \-r, \-\-rev . revisions to export .TP .B \-a, \-\-text . treat all files as text .TP .B \-g, \-\-git . use git extended diff format .TP .B \-\-nodates . omit dates from diff headers .UNINDENT .SS forget .sp .nf .ft C hg forget [OPTION]... FILE... .ft P .fi .sp Mark the specified files so they will no longer be tracked after the next commit. .sp This only removes files from the current branch, not from the entire project history, and it does not delete them from the working directory. .sp To undo a forget before the next commit, see \%\fBhg add\fP\:. .sp Examples: .INDENT 0.0 .IP \(bu 2 . forget newly\-added binary files: .sp .nf .ft C hg forget "set:added() and binary()" .ft P .fi .IP \(bu 2 . forget files that would be excluded by .hgignore: .sp .nf .ft C hg forget "set:hgignore()" .ft P .fi .UNINDENT .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .UNINDENT .SS graft .sp .nf .ft C hg graft [OPTION]... [\-r] REV... .ft P .fi .sp This command uses Mercurial\(aqs merge logic to copy individual changes from other branches without merging branches in the history graph. This is sometimes known as \(aqbackporting\(aq or \(aqcherry\-picking\(aq. By default, graft will copy user, date, and description from the source changesets. .sp Changesets that are ancestors of the current revision, that have already been grafted, or that are merges will be skipped. .sp If \-\-log is specified, log messages will have a comment appended of the form: .sp .nf .ft C (grafted from CHANGESETHASH) .ft P .fi .sp If a graft merge results in conflicts, the graft process is interrupted so that the current merge can be manually resolved. Once all conflicts are addressed, the graft process can be continued with the \-c/\-\-continue option. .IP Note . The \-c/\-\-continue option does not reapply earlier options. .RE .sp Examples: .INDENT 0.0 .IP \(bu 2 . copy a single change to the stable branch and edit its description: .sp .nf .ft C hg update stable hg graft \-\-edit 9393 .ft P .fi .IP \(bu 2 . graft a range of changesets with one exception, updating dates: .sp .nf .ft C hg graft \-D "2085::2093 and not 2091" .ft P .fi .IP \(bu 2 . continue a graft after resolving conflicts: .sp .nf .ft C hg graft \-c .ft P .fi .IP \(bu 2 . show the source of a grafted changeset: .sp .nf .ft C hg log \-\-debug \-r . .ft P .fi .UNINDENT .sp Returns 0 on successful completion. .sp Options: .INDENT 0.0 .TP .B \-r, \-\-rev . revisions to graft .TP .B \-c, \-\-continue . resume interrupted graft .TP .B \-e, \-\-edit . invoke editor on commit messages .TP .B \-\-log . append graft info to log message .TP .B \-D, \-\-currentdate . record the current date as commit date .TP .B \-U, \-\-currentuser . record the current user as committer .TP .B \-d, \-\-date . record the specified date as commit date .TP .B \-u, \-\-user . record the specified user as committer .TP .B \-t, \-\-tool . specify merge tool .TP .B \-n, \-\-dry\-run . do not perform actions, just print output .UNINDENT .SS grep .sp .nf .ft C hg grep [OPTION]... PATTERN [FILE]... .ft P .fi .sp Search revisions of files for a regular expression. .sp This command behaves differently than Unix grep. It only accepts Python/Perl regexps. It searches repository history, not the working directory. It always prints the revision number in which a match appears. .sp By default, grep only prints output for the first revision of a file in which it finds a match. To get it to print every revision that contains a change in match status ("\-" for a match that becomes a non\-match, or "+" for a non\-match that becomes a match), use the \-\-all flag. .sp Returns 0 if a match is found, 1 otherwise. .sp Options: .INDENT 0.0 .TP .B \-0, \-\-print0 . end fields with NUL .TP .B \-\-all . print all revisions that match .TP .B \-a, \-\-text . treat all files as text .TP .B \-f, \-\-follow . follow changeset history, or file history across copies and renames .TP .B \-i, \-\-ignore\-case . ignore case when matching .TP .B \-l, \-\-files\-with\-matches . print only filenames and revisions that match .TP .B \-n, \-\-line\-number . print matching line numbers .TP .B \-r, \-\-rev . only search files changed within revision range .TP .B \-u, \-\-user . list the author (long with \-v) .TP .B \-d, \-\-date . list the date (short with \-q) .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .UNINDENT .SS heads .sp .nf .ft C hg heads [\-ct] [\-r STARTREV] [REV]... .ft P .fi .sp With no arguments, show all open branch heads in the repository. Branch heads are changesets that have no descendants on the same branch. They are where development generally takes place and are the usual targets for update and merge operations. .sp If one or more REVs are given, only open branch heads on the branches associated with the specified changesets are shown. This means that you can use \%\fBhg heads .\fP\: to see the heads on the currently checked\-out branch. .sp If \-c/\-\-closed is specified, also show branch heads marked closed (see \%\fBhg commit \-\-close\-branch\fP\:). .sp If STARTREV is specified, only those heads that are descendants of STARTREV will be displayed. .sp If \-t/\-\-topo is specified, named branch mechanics will be ignored and only topological heads (changesets with no children) will be shown. .sp Returns 0 if matching heads are found, 1 if not. .sp Options: .INDENT 0.0 .TP .B \-r, \-\-rev . show only heads which are descendants of STARTREV .TP .B \-t, \-\-topo . show topological heads only .TP .B \-a, \-\-active . show active branchheads only (DEPRECATED) .TP .B \-c, \-\-closed . show normal and closed branch heads .TP .B \-\-style . display using template map file .TP .B \-\-template . display with template .UNINDENT .SS help .sp .nf .ft C hg help [\-ec] [TOPIC] .ft P .fi .sp With no arguments, print a list of commands with short help messages. .sp Given a topic, extension, or command name, print help for that topic. .sp Returns 0 if successful. .sp Options: .INDENT 0.0 .TP .B \-e, \-\-extension . show only help for extensions .TP .B \-c, \-\-command . show only help for commands .TP .B \-k, \-\-keyword . show topics matching keyword .UNINDENT .SS identify .sp .nf .ft C hg identify [\-nibtB] [\-r REV] [SOURCE] .ft P .fi .sp Print a summary identifying the repository state at REV using one or two parent hash identifiers, followed by a "+" if the working directory has uncommitted changes, the branch name (if not default), a list of tags, and a list of bookmarks. .sp When REV is not given, print a summary of the current state of the repository. .sp Specifying a path to a repository root or Mercurial bundle will cause lookup to operate on that repository/bundle. .sp Examples: .INDENT 0.0 .IP \(bu 2 . generate a build identifier for the working directory: .sp .nf .ft C hg id \-\-id > build\-id.dat .ft P .fi .IP \(bu 2 . find the revision corresponding to a tag: .sp .nf .ft C hg id \-n \-r 1.3 .ft P .fi .IP \(bu 2 . check the most recent revision of a remote repository: .sp .nf .ft C hg id \-r tip http://selenic.com/hg/ .ft P .fi .UNINDENT .sp Returns 0 if successful. .sp Options: .INDENT 0.0 .TP .B \-r, \-\-rev . identify the specified revision .TP .B \-n, \-\-num . show local revision number .TP .B \-i, \-\-id . show global revision id .TP .B \-b, \-\-branch . show branch .TP .B \-t, \-\-tags . show tags .TP .B \-B, \-\-bookmarks . show bookmarks .TP .B \-e, \-\-ssh . specify ssh command to use .TP .B \-\-remotecmd . specify hg command to run on the remote side .TP .B \-\-insecure . do not verify server certificate (ignoring web.cacerts config) .sp aliases: id .UNINDENT .SS import .sp .nf .ft C hg import [OPTION]... PATCH... .ft P .fi .sp Import a list of patches and commit them individually (unless \-\-no\-commit is specified). .sp Because import first applies changes to the working directory, import will abort if there are outstanding changes. .sp You can import a patch straight from a mail message. Even patches as attachments work (to use the body part, it must have type text/plain or text/x\-patch). From and Subject headers of email message are used as default committer and commit message. All text/plain body parts before first diff are added to commit message. .sp If the imported patch was generated by \%\fBhg export\fP\:, user and description from patch override values from message headers and body. Values given on command line with \-m/\-\-message and \-u/\-\-user override these. .sp If \-\-exact is specified, import will set the working directory to the parent of each patch before applying it, and will abort if the resulting changeset has a different ID than the one recorded in the patch. This may happen due to character set problems or other deficiencies in the text patch format. .sp Use \-\-bypass to apply and commit patches directly to the repository, not touching the working directory. Without \-\-exact, patches will be applied on top of the working directory parent revision. .sp With \-s/\-\-similarity, hg will attempt to discover renames and copies in the patch in the same way as \%\fBhg addremove\fP\:. .sp To read a patch from standard input, use "\-" as the patch name. If a URL is specified, the patch will be downloaded from it. See \%\fBhg help dates\fP\: for a list of formats valid for \-d/\-\-date. .sp Examples: .INDENT 0.0 .IP \(bu 2 . import a traditional patch from a website and detect renames: .sp .nf .ft C hg import \-s 80 http://example.com/bugfix.patch .ft P .fi .IP \(bu 2 . import a changeset from an hgweb server: .sp .nf .ft C hg import http://www.selenic.com/hg/rev/5ca8c111e9aa .ft P .fi .IP \(bu 2 . import all the patches in an Unix\-style mbox: .sp .nf .ft C hg import incoming\-patches.mbox .ft P .fi .IP \(bu 2 . attempt to exactly restore an exported changeset (not always possible): .sp .nf .ft C hg import \-\-exact proposed\-fix.patch .ft P .fi .UNINDENT .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-p, \-\-strip . directory strip option for patch. This has the same meaning as the corresponding patch option (default: 1) .TP .B \-b, \-\-base . base path (DEPRECATED) .TP .B \-e, \-\-edit . invoke editor on commit messages .TP .B \-f, \-\-force . skip check for outstanding uncommitted changes (DEPRECATED) .TP .B \-\-no\-commit . don\(aqt commit, just update the working directory .TP .B \-\-bypass . apply patch without touching the working directory .TP .B \-\-exact . apply patch to the nodes from which it was generated .TP .B \-\-import\-branch . use any branch information in patch (implied by \-\-exact) .TP .B \-m, \-\-message . use text as commit message .TP .B \-l, \-\-logfile . read commit message from file .TP .B \-d, \-\-date . record the specified date as commit date .TP .B \-u, \-\-user . record the specified user as committer .TP .B \-s, \-\-similarity . guess renamed files by similarity (0<=s<=100) .sp aliases: patch .UNINDENT .SS incoming .sp .nf .ft C hg incoming [\-p] [\-n] [\-M] [\-f] [\-r REV]... [\-\-bundle FILENAME] [SOURCE] .ft P .fi .sp Show new changesets found in the specified path/URL or the default pull location. These are the changesets that would have been pulled if a pull at the time you issued this command. .sp For remote repository, using \-\-bundle avoids downloading the changesets twice if the incoming is followed by a pull. .sp See pull for valid source format details. .sp Returns 0 if there are incoming changes, 1 otherwise. .sp Options: .INDENT 0.0 .TP .B \-f, \-\-force . run even if remote repository is unrelated .TP .B \-n, \-\-newest\-first . show newest record first .TP .B \-\-bundle . file to store the bundles into .TP .B \-r, \-\-rev . a remote changeset intended to be added .TP .B \-B, \-\-bookmarks . compare bookmarks .TP .B \-b, \-\-branch . a specific branch you would like to pull .TP .B \-p, \-\-patch . show patch .TP .B \-g, \-\-git . use git extended diff format .TP .B \-l, \-\-limit . limit number of changes displayed .TP .B \-M, \-\-no\-merges . do not show merges .TP .B \-\-stat . output diffstat\-style summary of changes .TP .B \-G, \-\-graph . show the revision DAG .TP .B \-\-style . display using template map file .TP .B \-\-template . display with template .TP .B \-e, \-\-ssh . specify ssh command to use .TP .B \-\-remotecmd . specify hg command to run on the remote side .TP .B \-\-insecure . do not verify server certificate (ignoring web.cacerts config) .TP .B \-S, \-\-subrepos . recurse into subrepositories .sp aliases: in .UNINDENT .SS init .sp .nf .ft C hg init [\-e CMD] [\-\-remotecmd CMD] [DEST] .ft P .fi .sp Initialize a new repository in the given directory. If the given directory does not exist, it will be created. .sp If no directory is given, the current directory is used. .sp It is possible to specify an \fBssh://\fP URL as the destination. See \%\fBhg help urls\fP\: for more information. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-e, \-\-ssh . specify ssh command to use .TP .B \-\-remotecmd . specify hg command to run on the remote side .TP .B \-\-insecure . do not verify server certificate (ignoring web.cacerts config) .UNINDENT .SS locate .sp .nf .ft C hg locate [OPTION]... [PATTERN]... .ft P .fi .sp Print files under Mercurial control in the working directory whose names match the given patterns. .sp By default, this command searches all directories in the working directory. To search just the current directory and its subdirectories, use "\-\-include .". .sp If no patterns are given to match, this command prints the names of all files under Mercurial control in the working directory. .sp If you want to feed the output of this command into the "xargs" command, use the \-0 option to both this command and "xargs". This will avoid the problem of "xargs" treating single filenames that contain whitespace as multiple filenames. .sp Returns 0 if a match is found, 1 otherwise. .sp Options: .INDENT 0.0 .TP .B \-r, \-\-rev . search the repository as it is in REV .TP .B \-0, \-\-print0 . end filenames with NUL, for use with xargs .TP .B \-f, \-\-fullpath . print complete paths from the filesystem root .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .UNINDENT .SS log .sp .nf .ft C hg log [OPTION]... [FILE] .ft P .fi .sp Print the revision history of the specified files or the entire project. .sp If no revision range is specified, the default is \fBtip:0\fP unless \-\-follow is set, in which case the working directory parent is used as the starting revision. .sp File history is shown without following rename or copy history of files. Use \-f/\-\-follow with a filename to follow history across renames and copies. \-\-follow without a filename will only show ancestors or descendants of the starting revision. .sp By default this command prints revision number and changeset id, tags, non\-trivial parents, user, date and time, and a summary for each commit. When the \-v/\-\-verbose switch is used, the list of changed files and full commit message are shown. .IP Note . log \-p/\-\-patch may generate unexpected diff output for merge changesets, as it will only compare the merge changeset against its first parent. Also, only files different from BOTH parents will appear in files:. .RE .IP Note . for performance reasons, log FILE may omit duplicate changes made on branches and will not show deletions. To see all changes including duplicates and deletions, use the \-\-removed switch. .RE .sp Some examples: .INDENT 0.0 .IP \(bu 2 . changesets with full descriptions and file lists: .sp .nf .ft C hg log \-v .ft P .fi .IP \(bu 2 . changesets ancestral to the working directory: .sp .nf .ft C hg log \-f .ft P .fi .IP \(bu 2 . last 10 commits on the current branch: .sp .nf .ft C hg log \-l 10 \-b . .ft P .fi .IP \(bu 2 . changesets showing all modifications of a file, including removals: .sp .nf .ft C hg log \-\-removed file.c .ft P .fi .IP \(bu 2 . all changesets that touch a directory, with diffs, excluding merges: .sp .nf .ft C hg log \-Mp lib/ .ft P .fi .IP \(bu 2 . all revision numbers that match a keyword: .sp .nf .ft C hg log \-k bug \-\-template "{rev}\en" .ft P .fi .IP \(bu 2 . check if a given changeset is included is a tagged release: .sp .nf .ft C hg log \-r "a21ccf and ancestor(1.9)" .ft P .fi .IP \(bu 2 . find all changesets by some user in a date range: .sp .nf .ft C hg log \-k alice \-d "may 2008 to jul 2008" .ft P .fi .IP \(bu 2 . summary of all changesets after the last tag: .sp .nf .ft C hg log \-r "last(tagged())::" \-\-template "{desc|firstline}\en" .ft P .fi .UNINDENT .sp See \%\fBhg help dates\fP\: for a list of formats valid for \-d/\-\-date. .sp See \%\fBhg help revisions\fP\: and \%\fBhg help revsets\fP\: for more about specifying revisions. .sp See \%\fBhg help templates\fP\: for more about pre\-packaged styles and specifying custom templates. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-f, \-\-follow . follow changeset history, or file history across copies and renames .TP .B \-\-follow\-first . only follow the first parent of merge changesets (DEPRECATED) .TP .B \-d, \-\-date . show revisions matching date spec .TP .B \-C, \-\-copies . show copied files .TP .B \-k, \-\-keyword . do case\-insensitive search for a given text .TP .B \-r, \-\-rev . show the specified revision or range .TP .B \-\-removed . include revisions where files were removed .TP .B \-m, \-\-only\-merges . show only merges (DEPRECATED) .TP .B \-u, \-\-user . revisions committed by user .TP .B \-\-only\-branch . show only changesets within the given named branch (DEPRECATED) .TP .B \-b, \-\-branch . show changesets within the given named branch .TP .B \-P, \-\-prune . do not display revision or any of its ancestors .TP .B \-p, \-\-patch . show patch .TP .B \-g, \-\-git . use git extended diff format .TP .B \-l, \-\-limit . limit number of changes displayed .TP .B \-M, \-\-no\-merges . do not show merges .TP .B \-\-stat . output diffstat\-style summary of changes .TP .B \-G, \-\-graph . show the revision DAG .TP .B \-\-style . display using template map file .TP .B \-\-template . display with template .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .sp aliases: history .UNINDENT .SS manifest .sp .nf .ft C hg manifest [\-r REV] .ft P .fi .sp Print a list of version controlled files for the given revision. If no revision is given, the first parent of the working directory is used, or the null revision if no revision is checked out. .sp With \-v, print file permissions, symlink and executable bits. With \-\-debug, print file revision hashes. .sp If option \-\-all is specified, the list of all files from all revisions is printed. This includes deleted and renamed files. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-r, \-\-rev . revision to display .TP .B \-\-all . list files from all revisions .UNINDENT .SS merge .sp .nf .ft C hg merge [\-P] [\-f] [[\-r] REV] .ft P .fi .sp The current working directory is updated with all changes made in the requested revision since the last common predecessor revision. .sp Files that changed between either parent are marked as changed for the next commit and a commit must be performed before any further updates to the repository are allowed. The next commit will have two parents. .sp \fB\-\-tool\fP can be used to specify the merge tool used for file merges. It overrides the HGMERGE environment variable and your configuration files. See \%\fBhg help merge\-tools\fP\: for options. .sp If no revision is specified, the working directory\(aqs parent is a head revision, and the current branch contains exactly one other head, the other head is merged with by default. Otherwise, an explicit revision with which to merge with must be provided. .sp \%\fBhg resolve\fP\: must be used to resolve unresolved files. .sp To undo an uncommitted merge, use \%\fBhg update \-\-clean .\fP\: which will check out a clean copy of the original merge parent, losing all changes. .sp Returns 0 on success, 1 if there are unresolved files. .sp Options: .INDENT 0.0 .TP .B \-f, \-\-force . force a merge including outstanding changes (DEPRECATED) .TP .B \-r, \-\-rev . revision to merge .TP .B \-P, \-\-preview . review revisions to merge (no merge is performed) .TP .B \-t, \-\-tool . specify merge tool .UNINDENT .SS outgoing .sp .nf .ft C hg outgoing [\-M] [\-p] [\-n] [\-f] [\-r REV]... [DEST] .ft P .fi .sp Show changesets not found in the specified destination repository or the default push location. These are the changesets that would be pushed if a push was requested. .sp See pull for details of valid destination formats. .sp Returns 0 if there are outgoing changes, 1 otherwise. .sp Options: .INDENT 0.0 .TP .B \-f, \-\-force . run even when the destination is unrelated .TP .B \-r, \-\-rev . a changeset intended to be included in the destination .TP .B \-n, \-\-newest\-first . show newest record first .TP .B \-B, \-\-bookmarks . compare bookmarks .TP .B \-b, \-\-branch . a specific branch you would like to push .TP .B \-p, \-\-patch . show patch .TP .B \-g, \-\-git . use git extended diff format .TP .B \-l, \-\-limit . limit number of changes displayed .TP .B \-M, \-\-no\-merges . do not show merges .TP .B \-\-stat . output diffstat\-style summary of changes .TP .B \-G, \-\-graph . show the revision DAG .TP .B \-\-style . display using template map file .TP .B \-\-template . display with template .TP .B \-e, \-\-ssh . specify ssh command to use .TP .B \-\-remotecmd . specify hg command to run on the remote side .TP .B \-\-insecure . do not verify server certificate (ignoring web.cacerts config) .TP .B \-S, \-\-subrepos . recurse into subrepositories .sp aliases: out .UNINDENT .SS parents .sp .nf .ft C hg parents [\-r REV] [FILE] .ft P .fi .sp Print the working directory\(aqs parent revisions. If a revision is given via \-r/\-\-rev, the parent of that revision will be printed. If a file argument is given, the revision in which the file was last changed (before the working directory revision or the argument to \-\-rev if given) is printed. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-r, \-\-rev . show parents of the specified revision .TP .B \-\-style . display using template map file .TP .B \-\-template . display with template .UNINDENT .SS paths .sp .nf .ft C hg paths [NAME] .ft P .fi .sp Show definition of symbolic path name NAME. If no name is given, show definition of all available names. .sp Option \-q/\-\-quiet suppresses all output when searching for NAME and shows only the path names when listing all definitions. .sp Path names are defined in the [paths] section of your configuration file and in \fB/etc/mercurial/hgrc\fP. If run inside a repository, \fB.hg/hgrc\fP is used, too. .sp The path names \fBdefault\fP and \fBdefault\-push\fP have a special meaning. When performing a push or pull operation, they are used as fallbacks if no location is specified on the command\-line. When \fBdefault\-push\fP is set, it will be used for push and \fBdefault\fP will be used for pull; otherwise \fBdefault\fP is used as the fallback for both. When cloning a repository, the clone source is written as \fBdefault\fP in \fB.hg/hgrc\fP. Note that \fBdefault\fP and \fBdefault\-push\fP apply to all inbound (e.g. \%\fBhg incoming\fP\:) and outbound (e.g. \%\fBhg outgoing\fP\:, \%\fBhg email\fP\: and \%\fBhg bundle\fP\:) operations. .sp See \%\fBhg help urls\fP\: for more information. .sp Returns 0 on success. .SS phase .sp .nf .ft C hg phase [\-p|\-d|\-s] [\-f] [\-r] REV... .ft P .fi .sp With no argument, show the phase name of specified revisions. .sp With one of \-p/\-\-public, \-d/\-\-draft or \-s/\-\-secret, change the phase value of the specified revisions. .sp Unless \-f/\-\-force is specified, \%\fBhg phase\fP\: won\(aqt move changeset from a lower phase to an higher phase. Phases are ordered as follows: .sp .nf .ft C public < draft < secret .ft P .fi .sp Return 0 on success, 1 if no phases were changed or some could not be changed. .sp Options: .INDENT 0.0 .TP .B \-p, \-\-public . set changeset phase to public .TP .B \-d, \-\-draft . set changeset phase to draft .TP .B \-s, \-\-secret . set changeset phase to secret .TP .B \-f, \-\-force . allow to move boundary backward .TP .B \-r, \-\-rev . target revision .UNINDENT .SS pull .sp .nf .ft C hg pull [\-u] [\-f] [\-r REV]... [\-e CMD] [\-\-remotecmd CMD] [SOURCE] .ft P .fi .sp Pull changes from a remote repository to a local one. .sp This finds all changes from the repository at the specified path or URL and adds them to a local repository (the current one unless \-R is specified). By default, this does not update the copy of the project in the working directory. .sp Use \%\fBhg incoming\fP\: if you want to see what would have been added by a pull at the time you issued this command. If you then decide to add those changes to the repository, you should use \%\fBhg pull \-r X\fP\: where \fBX\fP is the last changeset listed by \%\fBhg incoming\fP\:. .sp If SOURCE is omitted, the \(aqdefault\(aq path will be used. See \%\fBhg help urls\fP\: for more information. .sp Returns 0 on success, 1 if an update had unresolved files. .sp Options: .INDENT 0.0 .TP .B \-u, \-\-update . update to new branch head if changesets were pulled .TP .B \-f, \-\-force . run even when remote repository is unrelated .TP .B \-r, \-\-rev . a remote changeset intended to be added .TP .B \-B, \-\-bookmark . bookmark to pull .TP .B \-b, \-\-branch . a specific branch you would like to pull .TP .B \-e, \-\-ssh . specify ssh command to use .TP .B \-\-remotecmd . specify hg command to run on the remote side .TP .B \-\-insecure . do not verify server certificate (ignoring web.cacerts config) .UNINDENT .SS push .sp .nf .ft C hg push [\-f] [\-r REV]... [\-e CMD] [\-\-remotecmd CMD] [DEST] .ft P .fi .sp Push changesets from the local repository to the specified destination. .sp This operation is symmetrical to pull: it is identical to a pull in the destination repository from the current one. .sp By default, push will not allow creation of new heads at the destination, since multiple heads would make it unclear which head to use. In this situation, it is recommended to pull and merge before pushing. .sp Use \-\-new\-branch if you want to allow push to create a new named branch that is not present at the destination. This allows you to only create a new branch without forcing other changes. .IP Note . Extra care should be taken with the \-f/\-\-force option, which will push all new heads on all branches, an action which will almost always cause confusion for collaborators. .RE .sp If \-r/\-\-rev is used, the specified revision and all its ancestors will be pushed to the remote repository. .sp If \-B/\-\-bookmark is used, the specified bookmarked revision, its ancestors, and the bookmark will be pushed to the remote repository. .sp Please see \%\fBhg help urls\fP\: for important details about \fBssh://\fP URLs. If DESTINATION is omitted, a default path will be used. .sp Returns 0 if push was successful, 1 if nothing to push. .sp Options: .INDENT 0.0 .TP .B \-f, \-\-force . force push .TP .B \-r, \-\-rev . a changeset intended to be included in the destination .TP .B \-B, \-\-bookmark . bookmark to push .TP .B \-b, \-\-branch . a specific branch you would like to push .TP .B \-\-new\-branch . allow pushing a new branch .TP .B \-e, \-\-ssh . specify ssh command to use .TP .B \-\-remotecmd . specify hg command to run on the remote side .TP .B \-\-insecure . do not verify server certificate (ignoring web.cacerts config) .UNINDENT .SS recover .sp .nf .ft C hg recover .ft P .fi .sp Recover from an interrupted commit or pull. .sp This command tries to fix the repository status after an interrupted operation. It should only be necessary when Mercurial suggests it. .sp Returns 0 if successful, 1 if nothing to recover or verify fails. .SS remove .sp .nf .ft C hg remove [OPTION]... FILE... .ft P .fi .sp Schedule the indicated files for removal from the current branch. .sp This command schedules the files to be removed at the next commit. To undo a remove before that, see \%\fBhg revert\fP\:. To undo added files, see \%\fBhg forget\fP\:. .sp \-A/\-\-after can be used to remove only files that have already been deleted, \-f/\-\-force can be used to force deletion, and \-Af can be used to remove files from the next revision without deleting them from the working directory. .sp The following table details the behavior of remove for different file states (columns) and option combinations (rows). The file states are Added [A], Clean [C], Modified [M] and Missing [!] (as reported by \%\fBhg status\fP\:). The actions are Warn, Remove (from branch) and Delete (from disk): .TS center; |l|l|l|l|l|. _ T{ opt/state T} T{ A T} T{ C T} T{ M T} T{ ! T} _ T{ none T} T{ W T} T{ RD T} T{ W T} T{ R T} _ T{ \-f T} T{ R T} T{ RD T} T{ RD T} T{ R T} _ T{ \-A T} T{ W T} T{ W T} T{ W T} T{ R T} _ T{ \-Af T} T{ R T} T{ R T} T{ R T} T{ R T} _ .TE .sp Note that remove never deletes files in Added [A] state from the working directory, not even if option \-\-force is specified. .sp Returns 0 on success, 1 if any warnings encountered. .sp Options: .INDENT 0.0 .TP .B \-A, \-\-after . record delete for missing files .TP .B \-f, \-\-force . remove (and delete) file even if added or modified .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .sp aliases: rm .UNINDENT .SS rename .sp .nf .ft C hg rename [OPTION]... SOURCE... DEST .ft P .fi .sp Mark dest as copies of sources; mark sources for deletion. If dest is a directory, copies are put in that directory. If dest is a file, there can only be one source. .sp By default, this command copies the contents of files as they exist in the working directory. If invoked with \-A/\-\-after, the operation is recorded, but no copying is performed. .sp This command takes effect at the next commit. To undo a rename before that, see \%\fBhg revert\fP\:. .sp Returns 0 on success, 1 if errors are encountered. .sp Options: .INDENT 0.0 .TP .B \-A, \-\-after . record a rename that has already occurred .TP .B \-f, \-\-force . forcibly copy over an existing managed file .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .TP .B \-n, \-\-dry\-run . do not perform actions, just print output .sp aliases: move mv .UNINDENT .SS resolve .sp .nf .ft C hg resolve [OPTION]... [FILE]... .ft P .fi .sp Merges with unresolved conflicts are often the result of non\-interactive merging using the \fBinternal:merge\fP configuration setting, or a command\-line merge tool like \fBdiff3\fP. The resolve command is used to manage the files involved in a merge, after \%\fBhg merge\fP\: has been run, and before \%\fBhg commit\fP\: is run (i.e. the working directory must have two parents). See \%\fBhg help merge\-tools\fP\: for information on configuring merge tools. .sp The resolve command can be used in the following ways: .INDENT 0.0 .IP \(bu 2 . \%\fBhg resolve [\-\-tool TOOL] FILE...\fP\:: attempt to re\-merge the specified files, discarding any previous merge attempts. Re\-merging is not performed for files already marked as resolved. Use \fB\-\-all/\-a\fP to select all unresolved files. \fB\-\-tool\fP can be used to specify the merge tool used for the given files. It overrides the HGMERGE environment variable and your configuration files. Previous file contents are saved with a \fB.orig\fP suffix. .IP \(bu 2 . \%\fBhg resolve \-m [FILE]\fP\:: mark a file as having been resolved (e.g. after having manually fixed\-up the files). The default is to mark all unresolved files. .IP \(bu 2 . \%\fBhg resolve \-u [FILE]...\fP\:: mark a file as unresolved. The default is to mark all resolved files. .IP \(bu 2 . \%\fBhg resolve \-l\fP\:: list files which had or still have conflicts. In the printed list, \fBU\fP = unresolved and \fBR\fP = resolved. .UNINDENT .sp Note that Mercurial will not let you commit files with unresolved merge conflicts. You must use \%\fBhg resolve \-m ...\fP\: before you can commit after a conflicting merge. .sp Returns 0 on success, 1 if any files fail a resolve attempt. .sp Options: .INDENT 0.0 .TP .B \-a, \-\-all . select all unresolved files .TP .B \-l, \-\-list . list state of files needing merge .TP .B \-m, \-\-mark . mark files as resolved .TP .B \-u, \-\-unmark . mark files as unresolved .TP .B \-n, \-\-no\-status . hide status prefix .TP .B \-t, \-\-tool . specify merge tool .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .UNINDENT .SS revert .sp .nf .ft C hg revert [OPTION]... [\-r REV] [NAME]... .ft P .fi .IP Note . To check out earlier revisions, you should use \%\fBhg update REV\fP\:. To cancel an uncommitted merge (and lose your changes), use \%\fBhg update \-\-clean .\fP\:. .RE .sp With no revision specified, revert the specified files or directories to the contents they had in the parent of the working directory. This restores the contents of files to an unmodified state and unschedules adds, removes, copies, and renames. If the working directory has two parents, you must explicitly specify a revision. .sp Using the \-r/\-\-rev or \-d/\-\-date options, revert the given files or directories to their states as of a specific revision. Because revert does not change the working directory parents, this will cause these files to appear modified. This can be helpful to "back out" some or all of an earlier change. See \%\fBhg backout\fP\: for a related method. .sp Modified files are saved with a .orig suffix before reverting. To disable these backups, use \-\-no\-backup. .sp See \%\fBhg help dates\fP\: for a list of formats valid for \-d/\-\-date. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-a, \-\-all . revert all changes when no arguments given .TP .B \-d, \-\-date . tipmost revision matching date .TP .B \-r, \-\-rev . revert to the specified revision .TP .B \-C, \-\-no\-backup . do not save backup copies of files .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .TP .B \-n, \-\-dry\-run . do not perform actions, just print output .UNINDENT .SS rollback .sp .nf .ft C hg rollback .ft P .fi .sp Please use \%\fBhg commit \-\-amend\fP\: instead of rollback to correct mistakes in the last commit. .sp This command should be used with care. There is only one level of rollback, and there is no way to undo a rollback. It will also restore the dirstate at the time of the last transaction, losing any dirstate changes since that time. This command does not alter the working directory. .sp Transactions are used to encapsulate the effects of all commands that create new changesets or propagate existing changesets into a repository. .sp For example, the following commands are transactional, and their effects can be rolled back: .INDENT 0.0 .IP \(bu 2 . commit .IP \(bu 2 . import .IP \(bu 2 . pull .IP \(bu 2 . push (with this repository as the destination) .IP \(bu 2 . unbundle .UNINDENT .sp To avoid permanent data loss, rollback will refuse to rollback a commit transaction if it isn\(aqt checked out. Use \-\-force to override this protection. .sp This command is not intended for use on public repositories. Once changes are visible for pull by other users, rolling a transaction back locally is ineffective (someone else may already have pulled the changes). Furthermore, a race is possible with readers of the repository; for example an in\-progress pull from the repository may fail if a rollback is performed. .sp Returns 0 on success, 1 if no rollback data is available. .sp Options: .INDENT 0.0 .TP .B \-n, \-\-dry\-run . do not perform actions, just print output .TP .B \-f, \-\-force . ignore safety measures .UNINDENT .SS root .sp .nf .ft C hg root .ft P .fi .sp Print the root directory of the current repository. .sp Returns 0 on success. .SS serve .sp .nf .ft C hg serve [OPTION]... .ft P .fi .sp Start a local HTTP repository browser and pull server. You can use this for ad\-hoc sharing and browsing of repositories. It is recommended to use a real web server to serve a repository for longer periods of time. .sp Please note that the server does not implement access control. This means that, by default, anybody can read from the server and nobody can write to it by default. Set the \fBweb.allow_push\fP option to \fB*\fP to allow everybody to push to the server. You should use a real web server if you need to authenticate users. .sp By default, the server logs accesses to stdout and errors to stderr. Use the \-A/\-\-accesslog and \-E/\-\-errorlog options to log to files. .sp To have the server choose a free port number to listen on, specify a port number of 0; in this case, the server will print the port number it uses. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-A, \-\-accesslog . name of access log file to write to .TP .B \-d, \-\-daemon . run server in background .TP .B \-\-daemon\-pipefds . used internally by daemon mode .TP .B \-E, \-\-errorlog . name of error log file to write to .TP .B \-p, \-\-port . port to listen on (default: 8000) .TP .B \-a, \-\-address . address to listen on (default: all interfaces) .TP .B \-\-prefix . prefix path to serve from (default: server root) .TP .B \-n, \-\-name . name to show in web pages (default: working directory) .TP .B \-\-web\-conf . name of the hgweb config file (see "hg help hgweb") .TP .B \-\-webdir\-conf . name of the hgweb config file (DEPRECATED) .TP .B \-\-pid\-file . name of file to write process ID to .TP .B \-\-stdio . for remote clients .TP .B \-\-cmdserver . for remote clients .TP .B \-t, \-\-templates . web templates to use .TP .B \-\-style . template style to use .TP .B \-6, \-\-ipv6 . use IPv6 in addition to IPv4 .TP .B \-\-certificate . SSL certificate file .UNINDENT .SS showconfig .sp .nf .ft C hg showconfig [\-u] [NAME]... .ft P .fi .sp With no arguments, print names and values of all config items. .sp With one argument of the form section.name, print just the value of that config item. .sp With multiple arguments, print names and values of all config items with matching section names. .sp With \-\-debug, the source (filename and line number) is printed for each config item. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-u, \-\-untrusted . show untrusted configuration options .sp aliases: debugconfig .UNINDENT .SS status .sp .nf .ft C hg status [OPTION]... [FILE]... .ft P .fi .sp Show status of files in the repository. If names are given, only files that match are shown. Files that are clean or ignored or the source of a copy/move operation, are not listed unless \-c/\-\-clean, \-i/\-\-ignored, \-C/\-\-copies or \-A/\-\-all are given. Unless options described with "show only ..." are given, the options \-mardu are used. .sp Option \-q/\-\-quiet hides untracked (unknown and ignored) files unless explicitly requested with \-u/\-\-unknown or \-i/\-\-ignored. .IP Note . status may appear to disagree with diff if permissions have changed or a merge has occurred. The standard diff format does not report permission changes and diff only reports changes relative to one merge parent. .RE .sp If one revision is given, it is used as the base revision. If two revisions are given, the differences between them are shown. The \-\-change option can also be used as a shortcut to list the changed files of a revision from its first parent. .sp The codes used to show the status of files are: .sp .nf .ft C M = modified A = added R = removed C = clean ! = missing (deleted by non\-hg command, but still tracked) ? = not tracked I = ignored = origin of the previous file listed as A (added) .ft P .fi .sp Examples: .INDENT 0.0 .IP \(bu 2 . show changes in the working directory relative to a changeset: .sp .nf .ft C hg status \-\-rev 9353 .ft P .fi .IP \(bu 2 . show all changes including copies in an existing changeset: .sp .nf .ft C hg status \-\-copies \-\-change 9353 .ft P .fi .IP \(bu 2 . get a NUL separated list of added files, suitable for xargs: .sp .nf .ft C hg status \-an0 .ft P .fi .UNINDENT .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-A, \-\-all . show status of all files .TP .B \-m, \-\-modified . show only modified files .TP .B \-a, \-\-added . show only added files .TP .B \-r, \-\-removed . show only removed files .TP .B \-d, \-\-deleted . show only deleted (but tracked) files .TP .B \-c, \-\-clean . show only files without changes .TP .B \-u, \-\-unknown . show only unknown (not tracked) files .TP .B \-i, \-\-ignored . show only ignored files .TP .B \-n, \-\-no\-status . hide status prefix .TP .B \-C, \-\-copies . show source of copied files .TP .B \-0, \-\-print0 . end filenames with NUL, for use with xargs .TP .B \-\-rev . show difference from revision .TP .B \-\-change . list the changed files of a revision .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .TP .B \-S, \-\-subrepos . recurse into subrepositories .sp aliases: st .UNINDENT .SS summary .sp .nf .ft C hg summary [\-\-remote] .ft P .fi .sp This generates a brief summary of the working directory state, including parents, branch, commit status, and available updates. .sp With the \-\-remote option, this will check the default paths for incoming and outgoing changes. This can be time\-consuming. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-\-remote . check for push and pull .sp aliases: sum .UNINDENT .SS tag .sp .nf .ft C hg tag [\-f] [\-l] [\-m TEXT] [\-d DATE] [\-u USER] [\-r REV] NAME... .ft P .fi .sp Name a particular revision using . .sp Tags are used to name particular revisions of the repository and are very useful to compare different revisions, to go back to significant earlier versions or to mark branch points as releases, etc. Changing an existing tag is normally disallowed; use \-f/\-\-force to override. .sp If no revision is given, the parent of the working directory is used. .sp To facilitate version control, distribution, and merging of tags, they are stored as a file named ".hgtags" which is managed similarly to other project files and can be hand\-edited if necessary. This also means that tagging creates a new commit. The file ".hg/localtags" is used for local tags (not shared among repositories). .sp Tag commits are usually made at the head of a branch. If the parent of the working directory is not a branch head, \%\fBhg tag\fP\: aborts; use \-f/\-\-force to force the tag commit to be based on a non\-head changeset. .sp See \%\fBhg help dates\fP\: for a list of formats valid for \-d/\-\-date. .sp Since tag names have priority over branch names during revision lookup, using an existing branch name as a tag name is discouraged. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-f, \-\-force . force tag .TP .B \-l, \-\-local . make the tag local .TP .B \-r, \-\-rev . revision to tag .TP .B \-\-remove . remove a tag .TP .B \-e, \-\-edit . edit commit message .TP .B \-m, \-\-message . use as commit message .TP .B \-d, \-\-date . record the specified date as commit date .TP .B \-u, \-\-user . record the specified user as committer .UNINDENT .SS tags .sp .nf .ft C hg tags .ft P .fi .sp This lists both regular and local tags. When the \-v/\-\-verbose switch is used, a third column "local" is printed for local tags. .sp Returns 0 on success. .SS tip .sp .nf .ft C hg tip [\-p] [\-g] .ft P .fi .sp The tip revision (usually just called the tip) is the changeset most recently added to the repository (and therefore the most recently changed head). .sp If you have just made a commit, that commit will be the tip. If you have just pulled changes from another repository, the tip of that repository becomes the current tip. The "tip" tag is special and cannot be renamed or assigned to a different changeset. .sp This command is deprecated, please use \%\fBhg heads\fP\: instead. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-p, \-\-patch . show patch .TP .B \-g, \-\-git . use git extended diff format .TP .B \-\-style . display using template map file .TP .B \-\-template . display with template .UNINDENT .SS unbundle .sp .nf .ft C hg unbundle [\-u] FILE... .ft P .fi .sp Apply one or more compressed changegroup files generated by the bundle command. .sp Returns 0 on success, 1 if an update has unresolved files. .sp Options: .INDENT 0.0 .TP .B \-u, \-\-update . update to new branch head if changesets were unbundled .UNINDENT .SS update .sp .nf .ft C hg update [\-c] [\-C] [\-d DATE] [[\-r] REV] .ft P .fi .sp Update the repository\(aqs working directory to the specified changeset. If no changeset is specified, update to the tip of the current named branch and move the current bookmark (see \%\fBhg help bookmarks\fP\:). .sp Update sets the working directory\(aqs parent revision to the specified changeset (see \%\fBhg help parents\fP\:). .sp If the changeset is not a descendant or ancestor of the working directory\(aqs parent, the update is aborted. With the \-c/\-\-check option, the working directory is checked for uncommitted changes; if none are found, the working directory is updated to the specified changeset. .sp The following rules apply when the working directory contains uncommitted changes: .INDENT 0.0 .IP 1. 3 . If neither \-c/\-\-check nor \-C/\-\-clean is specified, and if the requested changeset is an ancestor or descendant of the working directory\(aqs parent, the uncommitted changes are merged into the requested changeset and the merged result is left uncommitted. If the requested changeset is not an ancestor or descendant (that is, it is on another branch), the update is aborted and the uncommitted changes are preserved. .IP 2. 3 . With the \-c/\-\-check option, the update is aborted and the uncommitted changes are preserved. .IP 3. 3 . With the \-C/\-\-clean option, uncommitted changes are discarded and the working directory is updated to the requested changeset. .UNINDENT .sp To cancel an uncommitted merge (and lose your changes), use \%\fBhg update \-\-clean .\fP\:. .sp Use null as the changeset to remove the working directory (like \%\fBhg clone \-U\fP\:). .sp If you want to revert just one file to an older revision, use \%\fBhg revert [\-r REV] NAME\fP\:. .sp See \%\fBhg help dates\fP\: for a list of formats valid for \-d/\-\-date. .sp Returns 0 on success, 1 if there are unresolved files. .sp Options: .INDENT 0.0 .TP .B \-C, \-\-clean . discard uncommitted changes (no backup) .TP .B \-c, \-\-check . update across branches if no uncommitted changes .TP .B \-d, \-\-date . tipmost revision matching date .TP .B \-r, \-\-rev . revision .sp aliases: up checkout co .UNINDENT .SS verify .sp .nf .ft C hg verify .ft P .fi .sp Verify the integrity of the current repository. .sp This will perform an extensive check of the repository\(aqs integrity, validating the hashes and checksums of each entry in the changelog, manifest, and tracked files, as well as the integrity of their crosslinks and indices. .sp Please see \%http://mercurial.selenic.com/wiki/RepositoryCorruption\: for more information about recovery from corruption of the repository. .sp Returns 0 on success, 1 if errors are encountered. .SS version .sp .nf .ft C hg version .ft P .fi .sp output version and copyright information .SH DATE FORMATS .sp Some commands allow the user to specify a date, e.g.: .INDENT 0.0 .IP \(bu 2 . backout, commit, import, tag: Specify the commit date. .IP \(bu 2 . log, revert, update: Select revision(s) by date. .UNINDENT .sp Many date formats are valid. Here are some examples: .INDENT 0.0 .IP \(bu 2 . \fBWed Dec 6 13:18:29 2006\fP (local timezone assumed) .IP \(bu 2 . \fBDec 6 13:18 \-0600\fP (year assumed, time offset provided) .IP \(bu 2 . \fBDec 6 13:18 UTC\fP (UTC and GMT are aliases for +0000) .IP \(bu 2 . \fBDec 6\fP (midnight) .IP \(bu 2 . \fB13:18\fP (today assumed) .IP \(bu 2 . \fB3:39\fP (3:39AM assumed) .IP \(bu 2 . \fB3:39pm\fP (15:39) .IP \(bu 2 . \fB2006\-12\-06 13:18:29\fP (ISO 8601 format) .IP \(bu 2 . \fB2006\-12\-6 13:18\fP .IP \(bu 2 . \fB2006\-12\-6\fP .IP \(bu 2 . \fB12\-6\fP .IP \(bu 2 . \fB12/6\fP .IP \(bu 2 . \fB12/6/6\fP (Dec 6 2006) .IP \(bu 2 . \fBtoday\fP (midnight) .IP \(bu 2 . \fByesterday\fP (midnight) .IP \(bu 2 . \fBnow\fP \- right now .UNINDENT .sp Lastly, there is Mercurial\(aqs internal format: .INDENT 0.0 .IP \(bu 2 . \fB1165411109 0\fP (Wed Dec 6 13:18:29 2006 UTC) .UNINDENT .sp This is the internal representation format for dates. The first number is the number of seconds since the epoch (1970\-01\-01 00:00 UTC). The second is the offset of the local timezone, in seconds west of UTC (negative if the timezone is east of UTC). .sp The log command also accepts date ranges: .INDENT 0.0 .IP \(bu 2 . \fBDATE\fP \- on or after a given date/time .IP \(bu 2 . \fBDATE to DATE\fP \- a date range, inclusive .IP \(bu 2 . \fB\-DAYS\fP \- within a given number of days of today .UNINDENT .SH DIFF FORMATS .sp Mercurial\(aqs default format for showing changes between two versions of a file is compatible with the unified format of GNU diff, which can be used by GNU patch and many other standard tools. .sp While this standard format is often enough, it does not encode the following information: .INDENT 0.0 .IP \(bu 2 . executable status and other permission bits .IP \(bu 2 . copy or rename information .IP \(bu 2 . changes in binary files .IP \(bu 2 . creation or deletion of empty files .UNINDENT .sp Mercurial also supports the extended diff format from the git VCS which addresses these limitations. The git diff format is not produced by default because a few widespread tools still do not understand this format. .sp This means that when generating diffs from a Mercurial repository (e.g. with \%\fBhg export\fP\:), you should be careful about things like file copies and renames or other things mentioned above, because when applying a standard diff to a different repository, this extra information is lost. Mercurial\(aqs internal operations (like push and pull) are not affected by this, because they use an internal binary format for communicating changes. .sp To make Mercurial produce the git extended diff format, use the \-\-git option available for many commands, or set \(aqgit = True\(aq in the [diff] section of your configuration file. You do not need to set this option when importing diffs in this format or using them in the mq extension. .SH ENVIRONMENT VARIABLES .INDENT 0.0 .TP .B HG . Path to the \(aqhg\(aq executable, automatically passed when running hooks, extensions or external tools. If unset or empty, this is the hg executable\(aqs name if it\(aqs frozen, or an executable named \(aqhg\(aq (with %PATHEXT% [defaulting to COM/EXE/BAT/CMD] extensions on Windows) is searched. .TP .B HGEDITOR . This is the name of the editor to run when committing. See EDITOR. .sp (deprecated, use configuration file) .TP .B HGENCODING . This overrides the default locale setting detected by Mercurial. This setting is used to convert data including usernames, changeset descriptions, tag names, and branches. This setting can be overridden with the \-\-encoding command\-line option. .TP .B HGENCODINGMODE . This sets Mercurial\(aqs behavior for handling unknown characters while transcoding user input. The default is "strict", which causes Mercurial to abort if it can\(aqt map a character. Other settings include "replace", which replaces unknown characters, and "ignore", which drops them. This setting can be overridden with the \-\-encodingmode command\-line option. .TP .B HGENCODINGAMBIGUOUS . This sets Mercurial\(aqs behavior for handling characters with "ambiguous" widths like accented Latin characters with East Asian fonts. By default, Mercurial assumes ambiguous characters are narrow, set this variable to "wide" if such characters cause formatting problems. .TP .B HGMERGE . An executable to use for resolving merge conflicts. The program will be executed with three arguments: local file, remote file, ancestor file. .sp (deprecated, use configuration file) .TP .B HGRCPATH . A list of files or directories to search for configuration files. Item separator is ":" on Unix, ";" on Windows. If HGRCPATH is not set, platform default search path is used. If empty, only the .hg/hgrc from the current repository is read. .sp For each element in HGRCPATH: .INDENT 7.0 .IP \(bu 2 . if it\(aqs a directory, all files ending with .rc are added .IP \(bu 2 . otherwise, the file itself will be added .UNINDENT .TP .B HGPLAIN . When set, this disables any configuration settings that might change Mercurial\(aqs default output. This includes encoding, defaults, verbose mode, debug mode, quiet mode, tracebacks, and localization. This can be useful when scripting against Mercurial in the face of existing user configuration. .sp Equivalent options set via command line flags or environment variables are not overridden. .TP .B HGPLAINEXCEPT . This is a comma\-separated list of features to preserve when HGPLAIN is enabled. Currently the only value supported is "i18n", which preserves internationalization in plain mode. .sp Setting HGPLAINEXCEPT to anything (even an empty string) will enable plain mode. .TP .B HGUSER . This is the string used as the author of a commit. If not set, available values will be considered in this order: .INDENT 7.0 .IP \(bu 2 . HGUSER (deprecated) .IP \(bu 2 . configuration files from the HGRCPATH .IP \(bu 2 . EMAIL .IP \(bu 2 . interactive prompt .IP \(bu 2 . LOGNAME (with \fB@hostname\fP appended) .UNINDENT .sp (deprecated, use configuration file) .TP .B EMAIL . May be used as the author of a commit; see HGUSER. .TP .B LOGNAME . May be used as the author of a commit; see HGUSER. .TP .B VISUAL . This is the name of the editor to use when committing. See EDITOR. .TP .B EDITOR . Sometimes Mercurial needs to open a text file in an editor for a user to modify, for example when writing commit messages. The editor it uses is determined by looking at the environment variables HGEDITOR, VISUAL and EDITOR, in that order. The first non\-empty one is chosen. If all of them are empty, the editor defaults to \(aqvi\(aq. .TP .B PYTHONPATH . This is used by Python to find imported modules and may need to be set appropriately if this Mercurial is not installed system\-wide. .UNINDENT .SH USING ADDITIONAL FEATURES .sp Mercurial has the ability to add new features through the use of extensions. Extensions may add new commands, add options to existing commands, change the default behavior of commands, or implement hooks. .sp To enable the "foo" extension, either shipped with Mercurial or in the Python search path, create an entry for it in your configuration file, like this: .sp .nf .ft C [extensions] foo = .ft P .fi .sp You may also specify the full path to an extension: .sp .nf .ft C [extensions] myfeature = ~/.hgext/myfeature.py .ft P .fi .sp See \%\fBhg help config\fP\: for more information on configuration files. .sp Extensions are not loaded by default for a variety of reasons: they can increase startup overhead; they may be meant for advanced usage only; they may provide potentially dangerous abilities (such as letting you destroy or modify history); they might not be ready for prime time; or they may alter some usual behaviors of stock Mercurial. It is thus up to the user to activate extensions as needed. .sp To explicitly disable an extension enabled in a configuration file of broader scope, prepend its path with !: .sp .nf .ft C [extensions] # disabling extension bar residing in /path/to/extension/bar.py bar = !/path/to/extension/bar.py # ditto, but no path was supplied for extension baz baz = ! .ft P .fi .sp disabled extensions: .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B acl . hooks for controlling repository access .TP .B blackbox . log repository events to a blackbox for debugging .TP .B bugzilla . hooks for integrating with the Bugzilla bug tracker .TP .B children . command to display child changesets (DEPRECATED) .TP .B churn . command to display statistics about repository history .TP .B color . colorize output from some commands .TP .B convert . import revisions from foreign VCS repositories into Mercurial .TP .B eol . automatically manage newlines in repository files .TP .B extdiff . command to allow external programs to compare revisions .TP .B factotum . http authentication with factotum .TP .B fetch . pull, update and merge in one command (DEPRECATED) .TP .B gpg . commands to sign and verify changesets .TP .B graphlog . command to view revision graphs from a shell .TP .B hgcia . hooks for integrating with the CIA.vc notification service .TP .B hgk . browse the repository in a graphical way .TP .B highlight . syntax highlighting for hgweb (requires Pygments) .TP .B histedit . interactive history editing .TP .B inotify . accelerate status report using Linux\(aqs inotify service .TP .B interhg . expand expressions into changelog and summaries .TP .B keyword . expand keywords in tracked files .TP .B largefiles . track large binary files .TP .B mq . manage a stack of patches .TP .B notify . hooks for sending email push notifications .TP .B pager . browse command output with an external pager .TP .B patchbomb . command to send changesets as (a series of) patch emails .TP .B progress . show progress bars for some actions .TP .B purge . command to delete untracked files from the working directory .TP .B rebase . command to move sets of revisions to a different ancestor .TP .B record . commands to interactively select changes for commit/qrefresh .TP .B relink . recreates hardlinks between repository clones .TP .B schemes . extend schemes with shortcuts to repository swarms .TP .B share . share a common history between several working directories .TP .B shelve . save and restore changes to the working directory .TP .B strip . strip changesets and their descendents from history .TP .B transplant . command to transplant changesets from another branch .TP .B win32mbcs . allow the use of MBCS paths with problematic encodings .TP .B win32text . perform automatic newline conversion .TP .B zeroconf . discover and advertise repositories on the local network .UNINDENT .UNINDENT .UNINDENT .SH SPECIFYING FILE SETS .sp Mercurial supports a functional language for selecting a set of files. .sp Like other file patterns, this pattern type is indicated by a prefix, \(aqset:\(aq. The language supports a number of predicates which are joined by infix operators. Parenthesis can be used for grouping. .sp Identifiers such as filenames or patterns must be quoted with single or double quotes if they contain characters outside of \fB[.*{}[]?/\e_a\-zA\-Z0\-9\ex80\-\exff]\fP or if they match one of the predefined predicates. This generally applies to file patterns other than globs and arguments for predicates. .sp Special characters can be used in quoted identifiers by escaping them, e.g., \fB\en\fP is interpreted as a newline. To prevent them from being interpreted, strings can be prefixed with \fBr\fP, e.g. \fBr\(aq...\(aq\fP. .sp There is a single prefix operator: .INDENT 0.0 .TP .B \fBnot x\fP .sp Files not in x. Short form is \fB! x\fP. .UNINDENT .sp These are the supported infix operators: .INDENT 0.0 .TP .B \fBx and y\fP .sp The intersection of files in x and y. Short form is \fBx & y\fP. .TP .B \fBx or y\fP .sp The union of files in x and y. There are two alternative short forms: \fBx | y\fP and \fBx + y\fP. .TP .B \fBx \- y\fP .sp Files in x but not in y. .UNINDENT .sp The following predicates are supported: .INDENT 0.0 .TP .B \fBadded()\fP .sp File that is added according to status. .TP .B \fBbinary()\fP .sp File that appears to be binary (contains NUL bytes). .TP .B \fBclean()\fP .sp File that is clean according to status. .TP .B \fBcopied()\fP .sp File that is recorded as being copied. .TP .B \fBdeleted()\fP .sp File that is deleted according to status. .TP .B \fBencoding(name)\fP .sp File can be successfully decoded with the given character encoding. May not be useful for encodings other than ASCII and UTF\-8. .TP .B \fBeol(style)\fP .sp File contains newlines of the given style (dos, unix, mac). Binary files are excluded, files with mixed line endings match multiple styles. .TP .B \fBexec()\fP .sp File that is marked as executable. .TP .B \fBgrep(regex)\fP .sp File contains the given regular expression. .TP .B \fBhgignore()\fP .sp File that matches the active .hgignore pattern. .TP .B \fBignored()\fP .sp File that is ignored according to status. These files will only be considered if this predicate is used. .TP .B \fBmodified()\fP .sp File that is modified according to status. .TP .B \fBremoved()\fP .sp File that is removed according to status. .TP .B \fBresolved()\fP .sp File that is marked resolved according to the resolve state. .TP .B \fBsize(expression)\fP .sp File size matches the given expression. Examples: .INDENT 7.0 .IP \(bu 2 . 1k (files from 1024 to 2047 bytes) .IP \(bu 2 . < 20k (files less than 20480 bytes) .IP \(bu 2 . >= .5MB (files at least 524288 bytes) .IP \(bu 2 . 4k \- 1MB (files from 4096 bytes to 1048576 bytes) .UNINDENT .TP .B \fBsubrepo([pattern])\fP .sp Subrepositories whose paths match the given pattern. .TP .B \fBsymlink()\fP .sp File that is marked as a symlink. .TP .B \fBunknown()\fP .sp File that is unknown according to status. These files will only be considered if this predicate is used. .TP .B \fBunresolved()\fP .sp File that is marked unresolved according to the resolve state. .UNINDENT .sp Some sample queries: .INDENT 0.0 .IP \(bu 2 . Show status of files that appear to be binary in the working directory: .sp .nf .ft C hg status \-A "set:binary()" .ft P .fi .IP \(bu 2 . Forget files that are in .hgignore but are already tracked: .sp .nf .ft C hg forget "set:hgignore() and not ignored()" .ft P .fi .IP \(bu 2 . Find text files that contain a string: .sp .nf .ft C hg locate "set:grep(magic) and not binary()" .ft P .fi .IP \(bu 2 . Find C files in a non\-standard encoding: .sp .nf .ft C hg locate "set:**.c and not encoding(\(aqUTF\-8\(aq)" .ft P .fi .IP \(bu 2 . Revert copies of large binary files: .sp .nf .ft C hg revert "set:copied() and binary() and size(\(aq>1M\(aq)" .ft P .fi .IP \(bu 2 . Remove files listed in foo.lst that contain the letter a or b: .sp .nf .ft C hg remove "set: \(aqlistfile:foo.lst\(aq and (**a* or **b*)" .ft P .fi .UNINDENT .sp See also \%\fBhg help patterns\fP\:. .SH GLOSSARY .INDENT 0.0 .TP .B Ancestor . Any changeset that can be reached by an unbroken chain of parent changesets from a given changeset. More precisely, the ancestors of a changeset can be defined by two properties: a parent of a changeset is an ancestor, and a parent of an ancestor is an ancestor. See also: \(aqDescendant\(aq. .TP .B Bookmark . Bookmarks are pointers to certain commits that move when committing. They are similar to tags in that it is possible to use bookmark names in all places where Mercurial expects a changeset ID, e.g., with \%\fBhg update\fP\:. Unlike tags, bookmarks move along when you make a commit. .sp Bookmarks can be renamed, copied and deleted. Bookmarks are local, unless they are explicitly pushed or pulled between repositories. Pushing and pulling bookmarks allow you to collaborate with others on a branch without creating a named branch. .TP .B Branch . (Noun) A child changeset that has been created from a parent that is not a head. These are known as topological branches, see \(aqBranch, topological\(aq. If a topological branch is named, it becomes a named branch. If a topological branch is not named, it becomes an anonymous branch. See \(aqBranch, anonymous\(aq and \(aqBranch, named\(aq. .sp Branches may be created when changes are pulled from or pushed to a remote repository, since new heads may be created by these operations. Note that the term branch can also be used informally to describe a development process in which certain development is done independently of other development. This is sometimes done explicitly with a named branch, but it can also be done locally, using bookmarks or clones and anonymous branches. .sp Example: "The experimental branch". .sp (Verb) The action of creating a child changeset which results in its parent having more than one child. .sp Example: "I\(aqm going to branch at X". .TP .B Branch, anonymous . Every time a new child changeset is created from a parent that is not a head and the name of the branch is not changed, a new anonymous branch is created. .TP .B Branch, closed . A named branch whose branch heads have all been closed. .TP .B Branch, default . The branch assigned to a changeset when no name has previously been assigned. .TP .B Branch head . See \(aqHead, branch\(aq. .TP .B Branch, inactive . If a named branch has no topological heads, it is considered to be inactive. As an example, a feature branch becomes inactive when it is merged into the default branch. The \%\fBhg branches\fP\: command shows inactive branches by default, though they can be hidden with \%\fBhg branches \-\-active\fP\:. .sp NOTE: this concept is deprecated because it is too implicit. Branches should now be explicitly closed using \%\fBhg commit \-\-close\-branch\fP\: when they are no longer needed. .TP .B Branch, named . A collection of changesets which have the same branch name. By default, children of a changeset in a named branch belong to the same named branch. A child can be explicitly assigned to a different branch. See \%\fBhg help branch\fP\:, \%\fBhg help branches\fP\: and \%\fBhg commit \-\-close\-branch\fP\: for more information on managing branches. .sp Named branches can be thought of as a kind of namespace, dividing the collection of changesets that comprise the repository into a collection of disjoint subsets. A named branch is not necessarily a topological branch. If a new named branch is created from the head of another named branch, or the default branch, but no further changesets are added to that previous branch, then that previous branch will be a branch in name only. .TP .B Branch tip . See \(aqTip, branch\(aq. .TP .B Branch, topological . Every time a new child changeset is created from a parent that is not a head, a new topological branch is created. If a topological branch is named, it becomes a named branch. If a topological branch is not named, it becomes an anonymous branch of the current, possibly default, branch. .TP .B Changelog . A record of the changesets in the order in which they were added to the repository. This includes details such as changeset id, author, commit message, date, and list of changed files. .TP .B Changeset . A snapshot of the state of the repository used to record a change. .TP .B Changeset, child . The converse of parent changeset: if P is a parent of C, then C is a child of P. There is no limit to the number of children that a changeset may have. .TP .B Changeset id . A SHA\-1 hash that uniquely identifies a changeset. It may be represented as either a "long" 40 hexadecimal digit string, or a "short" 12 hexadecimal digit string. .TP .B Changeset, merge . A changeset with two parents. This occurs when a merge is committed. .TP .B Changeset, parent . A revision upon which a child changeset is based. Specifically, a parent changeset of a changeset C is a changeset whose node immediately precedes C in the DAG. Changesets have at most two parents. .TP .B Checkout . (Noun) The working directory being updated to a specific revision. This use should probably be avoided where possible, as changeset is much more appropriate than checkout in this context. .sp Example: "I\(aqm using checkout X." .sp (Verb) Updating the working directory to a specific changeset. See \%\fBhg help update\fP\:. .sp Example: "I\(aqm going to check out changeset X." .TP .B Child changeset . See \(aqChangeset, child\(aq. .TP .B Close changeset . See \(aqHead, closed branch\(aq .TP .B Closed branch . See \(aqBranch, closed\(aq. .TP .B Clone . (Noun) An entire or partial copy of a repository. The partial clone must be in the form of a revision and its ancestors. .sp Example: "Is your clone up to date?". .sp (Verb) The process of creating a clone, using \%\fBhg clone\fP\:. .sp Example: "I\(aqm going to clone the repository". .TP .B Closed branch head . See \(aqHead, closed branch\(aq. .TP .B Commit . (Noun) A synonym for changeset. .sp Example: "Is the bug fixed in your recent commit?" .sp (Verb) The act of recording changes to a repository. When files are committed in a working directory, Mercurial finds the differences between the committed files and their parent changeset, creating a new changeset in the repository. .sp Example: "You should commit those changes now." .TP .B Cset . A common abbreviation of the term changeset. .TP .B DAG . The repository of changesets of a distributed version control system (DVCS) can be described as a directed acyclic graph (DAG), consisting of nodes and edges, where nodes correspond to changesets and edges imply a parent \-> child relation. This graph can be visualized by graphical tools such as \%\fBhg log \-\-graph\fP\:. In Mercurial, the DAG is limited by the requirement for children to have at most two parents. .TP .B Default branch . See \(aqBranch, default\(aq. .TP .B Descendant . Any changeset that can be reached by a chain of child changesets from a given changeset. More precisely, the descendants of a changeset can be defined by two properties: the child of a changeset is a descendant, and the child of a descendant is a descendant. See also: \(aqAncestor\(aq. .TP .B Diff . (Noun) The difference between the contents and attributes of files in two changesets or a changeset and the current working directory. The difference is usually represented in a standard form called a "diff" or "patch". The "git diff" format is used when the changes include copies, renames, or changes to file attributes, none of which can be represented/handled by classic "diff" and "patch". .sp Example: "Did you see my correction in the diff?" .sp (Verb) Diffing two changesets is the action of creating a diff or patch. .sp Example: "If you diff with changeset X, you will see what I mean." .TP .B Directory, working . The working directory represents the state of the files tracked by Mercurial, that will be recorded in the next commit. The working directory initially corresponds to the snapshot at an existing changeset, known as the parent of the working directory. See \(aqParent, working directory\(aq. The state may be modified by changes to the files introduced manually or by a merge. The repository metadata exists in the .hg directory inside the working directory. .TP .B Draft . Changesets in the draft phase have not been shared with publishing repositories and may thus be safely changed by history\-modifying extensions. See \%\fBhg help phases\fP\:. .TP .B Graph . See DAG and \%\fBhg log \-\-graph\fP\:. .TP .B Head . The term \(aqhead\(aq may be used to refer to both a branch head or a repository head, depending on the context. See \(aqHead, branch\(aq and \(aqHead, repository\(aq for specific definitions. .sp Heads are where development generally takes place and are the usual targets for update and merge operations. .TP .B Head, branch . A changeset with no descendants on the same named branch. .TP .B Head, closed branch . A changeset that marks a head as no longer interesting. The closed head is no longer listed by \%\fBhg heads\fP\:. A branch is considered closed when all its heads are closed and consequently is not listed by \%\fBhg branches\fP\:. .sp Closed heads can be re\-opened by committing new changeset as the child of the changeset that marks a head as closed. .TP .B Head, repository . A topological head which has not been closed. .TP .B Head, topological . A changeset with no children in the repository. .TP .B History, immutable . Once committed, changesets cannot be altered. Extensions which appear to change history actually create new changesets that replace existing ones, and then destroy the old changesets. Doing so in public repositories can result in old changesets being reintroduced to the repository. .TP .B History, rewriting . The changesets in a repository are immutable. However, extensions to Mercurial can be used to alter the repository, usually in such a way as to preserve changeset contents. .TP .B Immutable history . See \(aqHistory, immutable\(aq. .TP .B Merge changeset . See \(aqChangeset, merge\(aq. .TP .B Manifest . Each changeset has a manifest, which is the list of files that are tracked by the changeset. .TP .B Merge . Used to bring together divergent branches of work. When you update to a changeset and then merge another changeset, you bring the history of the latter changeset into your working directory. Once conflicts are resolved (and marked), this merge may be committed as a merge changeset, bringing two branches together in the DAG. .TP .B Named branch . See \(aqBranch, named\(aq. .TP .B Null changeset . The empty changeset. It is the parent state of newly\-initialized repositories and repositories with no checked out revision. It is thus the parent of root changesets and the effective ancestor when merging unrelated changesets. Can be specified by the alias \(aqnull\(aq or by the changeset ID \(aq000000000000\(aq. .TP .B Parent . See \(aqChangeset, parent\(aq. .TP .B Parent changeset . See \(aqChangeset, parent\(aq. .TP .B Parent, working directory . The working directory parent reflects a virtual revision which is the child of the changeset (or two changesets with an uncommitted merge) shown by \%\fBhg parents\fP\:. This is changed with \%\fBhg update\fP\:. Other commands to see the working directory parent are \%\fBhg summary\fP\: and \%\fBhg id\fP\:. Can be specified by the alias ".". .TP .B Patch . (Noun) The product of a diff operation. .sp Example: "I\(aqve sent you my patch." .sp (Verb) The process of using a patch file to transform one changeset into another. .sp Example: "You will need to patch that revision." .TP .B Phase . A per\-changeset state tracking how the changeset has been or should be shared. See \%\fBhg help phases\fP\:. .TP .B Public . Changesets in the public phase have been shared with publishing repositories and are therefore considered immutable. See \%\fBhg help phases\fP\:. .TP .B Pull . An operation in which changesets in a remote repository which are not in the local repository are brought into the local repository. Note that this operation without special arguments only updates the repository, it does not update the files in the working directory. See \%\fBhg help pull\fP\:. .TP .B Push . An operation in which changesets in a local repository which are not in a remote repository are sent to the remote repository. Note that this operation only adds changesets which have been committed locally to the remote repository. Uncommitted changes are not sent. See \%\fBhg help push\fP\:. .TP .B Repository . The metadata describing all recorded states of a collection of files. Each recorded state is represented by a changeset. A repository is usually (but not always) found in the \fB.hg\fP subdirectory of a working directory. Any recorded state can be recreated by "updating" a working directory to a specific changeset. .TP .B Repository head . See \(aqHead, repository\(aq. .TP .B Revision . A state of the repository at some point in time. Earlier revisions can be updated to by using \%\fBhg update\fP\:. See also \(aqRevision number\(aq; See also \(aqChangeset\(aq. .TP .B Revision number . This integer uniquely identifies a changeset in a specific repository. It represents the order in which changesets were added to a repository, starting with revision number 0. Note that the revision number may be different in each clone of a repository. To identify changesets uniquely between different clones, see \(aqChangeset id\(aq. .TP .B Revlog . History storage mechanism used by Mercurial. It is a form of delta encoding, with occasional full revision of data followed by delta of each successive revision. It includes data and an index pointing to the data. .TP .B Rewriting history . See \(aqHistory, rewriting\(aq. .TP .B Root . A changeset that has only the null changeset as its parent. Most repositories have only a single root changeset. .TP .B Secret . Changesets in the secret phase may not be shared via push, pull, or clone. See \%\fBhg help phases\fP\:. .TP .B Tag . An alternative name given to a changeset. Tags can be used in all places where Mercurial expects a changeset ID, e.g., with \%\fBhg update\fP\:. The creation of a tag is stored in the history and will thus automatically be shared with other using push and pull. .TP .B Tip . The changeset with the highest revision number. It is the changeset most recently added in a repository. .TP .B Tip, branch . The head of a given branch with the highest revision number. When a branch name is used as a revision identifier, it refers to the branch tip. See also \(aqBranch, head\(aq. Note that because revision numbers may be different in different repository clones, the branch tip may be different in different cloned repositories. .TP .B Update . (Noun) Another synonym of changeset. .sp Example: "I\(aqve pushed an update". .sp (Verb) This term is usually used to describe updating the state of the working directory to that of a specific changeset. See \%\fBhg help update\fP\:. .sp Example: "You should update". .TP .B Working directory . See \(aqDirectory, working\(aq. .TP .B Working directory parent . See \(aqParent, working directory\(aq. .UNINDENT .SH SYNTAX FOR MERCURIAL IGNORE FILES .SS Synopsis .sp The Mercurial system uses a file called \fB.hgignore\fP in the root directory of a repository to control its behavior when it searches for files that it is not currently tracking. .SS Description .sp The working directory of a Mercurial repository will often contain files that should not be tracked by Mercurial. These include backup files created by editors and build products created by compilers. These files can be ignored by listing them in a \fB.hgignore\fP file in the root of the working directory. The \fB.hgignore\fP file must be created manually. It is typically put under version control, so that the settings will propagate to other repositories with push and pull. .sp An untracked file is ignored if its path relative to the repository root directory, or any prefix path of that path, is matched against any pattern in \fB.hgignore\fP. .sp For example, say we have an untracked file, \fBfile.c\fP, at \fBa/b/file.c\fP inside our repository. Mercurial will ignore \fBfile.c\fP if any pattern in \fB.hgignore\fP matches \fBa/b/file.c\fP, \fBa/b\fP or \fBa\fP. .sp In addition, a Mercurial configuration file can reference a set of per\-user or global ignore files. See the \fBignore\fP configuration key on the \fB[ui]\fP section of \%\fBhg help config\fP\: for details of how to configure these files. .sp To control Mercurial\(aqs handling of files that it manages, many commands support the \fB\-I\fP and \fB\-X\fP options; see \%\fBhg help \fP\: and \%\fBhg help patterns\fP\: for details. .sp Files that are already tracked are not affected by .hgignore, even if they appear in .hgignore. An untracked file X can be explicitly added with \%\fBhg add X\fP\:, even if X would be excluded by a pattern in .hgignore. .SS Syntax .sp An ignore file is a plain text file consisting of a list of patterns, with one pattern per line. Empty lines are skipped. The \fB#\fP character is treated as a comment character, and the \fB\e\fP character is treated as an escape character. .sp Mercurial supports several pattern syntaxes. The default syntax used is Python/Perl\-style regular expressions. .sp To change the syntax used, use a line of the following form: .sp .nf .ft C syntax: NAME .ft P .fi .sp where \fBNAME\fP is one of the following: .INDENT 0.0 .TP .B \fBregexp\fP .sp Regular expression, Python/Perl syntax. .TP .B \fBglob\fP .sp Shell\-style glob. .UNINDENT .sp The chosen syntax stays in effect when parsing all patterns that follow, until another syntax is selected. .sp Neither glob nor regexp patterns are rooted. A glob\-syntax pattern of the form \fB*.c\fP will match a file ending in \fB.c\fP in any directory, and a regexp pattern of the form \fB\e.c$\fP will do the same. To root a regexp pattern, start it with \fB^\fP. .IP Note . Patterns specified in other than \fB.hgignore\fP are always rooted. Please see \%\fBhg help patterns\fP\: for details. .RE .SS Example .sp Here is an example ignore file. .sp .nf .ft C # use glob syntax. syntax: glob *.elc *.pyc *~ # switch to regexp syntax. syntax: regexp ^\e.pc/ .ft P .fi .SH CONFIGURING HGWEB .sp Mercurial\(aqs internal web server, hgweb, can serve either a single repository, or a tree of repositories. In the second case, repository paths and global options can be defined using a dedicated configuration file common to \%\fBhg serve\fP\:, \fBhgweb.wsgi\fP, \fBhgweb.cgi\fP and \fBhgweb.fcgi\fP. .sp This file uses the same syntax as other Mercurial configuration files but recognizes only the following sections: .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 . web .IP \(bu 2 . paths .IP \(bu 2 . collections .UNINDENT .UNINDENT .UNINDENT .sp The \fBweb\fP options are thoroughly described in \%\fBhg help config\fP\:. .sp The \fBpaths\fP section maps URL paths to paths of repositories in the filesystem. hgweb will not expose the filesystem directly \- only Mercurial repositories can be published and only according to the configuration. .sp The left hand side is the path in the URL. Note that hgweb reserves subpaths like \fBrev\fP or \fBfile\fP, try using different names for nested repositories to avoid confusing effects. .sp The right hand side is the path in the filesystem. If the specified path ends with \fB*\fP or \fB**\fP the filesystem will be searched recursively for repositories below that point. With \fB*\fP it will not recurse into the repositories it finds (except for \fB.hg/patches\fP). With \fB**\fP it will also search inside repository working directories and possibly find subrepositories. .sp In this example: .sp .nf .ft C [paths] /projects/a = /srv/tmprepos/a /projects/b = c:/repos/b / = /srv/repos/* /user/bob = /home/bob/repos/** .ft P .fi .INDENT 0.0 .IP \(bu 2 . The first two entries make two repositories in different directories appear under the same directory in the web interface .IP \(bu 2 . The third entry will publish every Mercurial repository found in \fB/srv/repos/\fP, for instance the repository \fB/srv/repos/quux/\fP will appear as \fBhttp://server/quux/\fP .IP \(bu 2 . The fourth entry will publish both \fBhttp://server/user/bob/quux/\fP and \fBhttp://server/user/bob/quux/testsubrepo/\fP .UNINDENT .sp The \fBcollections\fP section is deprecated and has been superseded by \fBpaths\fP. .SH MERGE TOOLS .sp To merge files Mercurial uses merge tools. .sp A merge tool combines two different versions of a file into a merged file. Merge tools are given the two files and the greatest common ancestor of the two file versions, so they can determine the changes made on both branches. .sp Merge tools are used both for \%\fBhg resolve\fP\:, \%\fBhg merge\fP\:, \%\fBhg update\fP\:, \%\fBhg backout\fP\: and in several extensions. .sp Usually, the merge tool tries to automatically reconcile the files by combining all non\-overlapping changes that occurred separately in the two different evolutions of the same initial base file. Furthermore, some interactive merge programs make it easier to manually resolve conflicting merges, either in a graphical way, or by inserting some conflict markers. Mercurial does not include any interactive merge programs but relies on external tools for that. .SS Available merge tools .sp External merge tools and their properties are configured in the merge\-tools configuration section \- see hgrc(5) \- but they can often just be named by their executable. .sp A merge tool is generally usable if its executable can be found on the system and if it can handle the merge. The executable is found if it is an absolute or relative executable path or the name of an application in the executable search path. The tool is assumed to be able to handle the merge if it can handle symlinks if the file is a symlink, if it can handle binary files if the file is binary, and if a GUI is available if the tool requires a GUI. .sp There are some internal merge tools which can be used. The internal merge tools are: .INDENT 0.0 .TP .B \fBinternal:dump\fP .sp Creates three versions of the files to merge, containing the contents of local, other and base. These files can then be used to perform a merge manually. If the file to be merged is named \fBa.txt\fP, these files will accordingly be named \fBa.txt.local\fP, \fBa.txt.other\fP and \fBa.txt.base\fP and they will be placed in the same directory as \fBa.txt\fP. .TP .B \fBinternal:fail\fP .sp Rather than attempting to merge files that were modified on both branches, it marks them as unresolved. The resolve command must be used to resolve these conflicts. .TP .B \fBinternal:local\fP .sp Uses the local version of files as the merged version. .TP .B \fBinternal:merge\fP .sp Uses the internal non\-interactive simple merge algorithm for merging files. It will fail if there are any conflicts and leave markers in the partially merged file. .TP .B \fBinternal:other\fP .sp Uses the other version of files as the merged version. .TP .B \fBinternal:prompt\fP .sp Asks the user which of the local or the other version to keep as the merged version. .UNINDENT .sp Internal tools are always available and do not require a GUI but will by default not handle symlinks or binary files. .SS Choosing a merge tool .sp Mercurial uses these rules when deciding which merge tool to use: .INDENT 0.0 .IP 1. 3 . If a tool has been specified with the \-\-tool option to merge or resolve, it is used. If it is the name of a tool in the merge\-tools configuration, its configuration is used. Otherwise the specified tool must be executable by the shell. .IP 2. 3 . If the \fBHGMERGE\fP environment variable is present, its value is used and must be executable by the shell. .IP 3. 3 . If the filename of the file to be merged matches any of the patterns in the merge\-patterns configuration section, the first usable merge tool corresponding to a matching pattern is used. Here, binary capabilities of the merge tool are not considered. .IP 4. 3 . If ui.merge is set it will be considered next. If the value is not the name of a configured tool, the specified value is used and must be executable by the shell. Otherwise the named tool is used if it is usable. .IP 5. 3 . If any usable merge tools are present in the merge\-tools configuration section, the one with the highest priority is used. .IP 6. 3 . If a program named \fBhgmerge\fP can be found on the system, it is used \- but it will by default not be used for symlinks and binary files. .IP 7. 3 . If the file to be merged is not binary and is not a symlink, then \fBinternal:merge\fP is used. .IP 8. 3 . The merge of the file fails and must be resolved before commit. .UNINDENT .IP Note . After selecting a merge program, Mercurial will by default attempt to merge the files using a simple merge algorithm first. Only if it doesn\(aqt succeed because of conflicting changes Mercurial will actually execute the merge program. Whether to use the simple merge algorithm first can be controlled by the premerge setting of the merge tool. Premerge is enabled by default unless the file is binary or a symlink. .RE .sp See the merge\-tools and ui sections of hgrc(5) for details on the configuration of merge tools. .SH SPECIFYING MULTIPLE REVISIONS .sp When Mercurial accepts more than one revision, they may be specified individually, or provided as a topologically continuous range, separated by the ":" character. .sp The syntax of range notation is [BEGIN]:[END], where BEGIN and END are revision identifiers. Both BEGIN and END are optional. If BEGIN is not specified, it defaults to revision number 0. If END is not specified, it defaults to the tip. The range ":" thus means "all revisions". .sp If BEGIN is greater than END, revisions are treated in reverse order. .sp A range acts as a closed interval. This means that a range of 3:5 gives 3, 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6. .SH FILE NAME PATTERNS .sp Mercurial accepts several notations for identifying one or more files at a time. .sp By default, Mercurial treats filenames as shell\-style extended glob patterns. .sp Alternate pattern notations must be specified explicitly. .IP Note . Patterns specified in \fB.hgignore\fP are not rooted. Please see \%\fBhg help hgignore\fP\: for details. .RE .sp To use a plain path name without any pattern matching, start it with \fBpath:\fP. These path names must completely match starting at the current repository root. .sp To use an extended glob, start a name with \fBglob:\fP. Globs are rooted at the current directory; a glob such as \fB*.c\fP will only match files in the current directory ending with \fB.c\fP. .sp The supported glob syntax extensions are \fB**\fP to match any string across path separators and \fB{a,b}\fP to mean "a or b". .sp To use a Perl/Python regular expression, start a name with \fBre:\fP. Regexp pattern matching is anchored at the root of the repository. .sp To read name patterns from a file, use \fBlistfile:\fP or \fBlistfile0:\fP. The latter expects null delimited patterns while the former expects line feeds. Each string read from the file is itself treated as a file pattern. .sp Plain examples: .sp .nf .ft C path:foo/bar a name bar in a directory named foo in the root of the repository path:path:name a file or directory named "path:name" .ft P .fi .sp Glob examples: .sp .nf .ft C glob:*.c any name ending in ".c" in the current directory *.c any name ending in ".c" in the current directory **.c any name ending in ".c" in any subdirectory of the current directory including itself. foo/*.c any name ending in ".c" in the directory foo foo/**.c any name ending in ".c" in any subdirectory of foo including itself. .ft P .fi .sp Regexp examples: .sp .nf .ft C re:.*\e.c$ any name ending in ".c", anywhere in the repository .ft P .fi .sp File examples: .sp .nf .ft C listfile:list.txt read list from list.txt with one file pattern per line listfile0:list.txt read list from list.txt with null byte delimiters .ft P .fi .sp See also \%\fBhg help filesets\fP\:. .SH WORKING WITH PHASES .SS What are phases? .sp Phases are a system for tracking which changesets have been or should be shared. This helps prevent common mistakes when modifying history (for instance, with the mq or rebase extensions). .sp Each changeset in a repository is in one of the following phases: .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 . public : changeset is visible on a public server .IP \(bu 2 . draft : changeset is not yet published .IP \(bu 2 . secret : changeset should not be pushed, pulled, or cloned .UNINDENT .UNINDENT .UNINDENT .sp These phases are ordered (public < draft < secret) and no changeset can be in a lower phase than its ancestors. For instance, if a changeset is public, all its ancestors are also public. Lastly, changeset phases should only be changed towards the public phase. .SS How are phases managed? .sp For the most part, phases should work transparently. By default, a changeset is created in the draft phase and is moved into the public phase when it is pushed to another repository. .sp Once changesets become public, extensions like mq and rebase will refuse to operate on them to prevent creating duplicate changesets. Phases can also be manually manipulated with the \%\fBhg phase\fP\: command if needed. See \%\fBhg help \-v phase\fP\: for examples. .SS Phases and servers .sp Normally, all servers are \fBpublishing\fP by default. This means: .sp .nf .ft C \- all draft changesets that are pulled or cloned appear in phase public on the client \- all draft changesets that are pushed appear as public on both client and server \- secret changesets are neither pushed, pulled, or cloned .ft P .fi .IP Note . Pulling a draft changeset from a publishing server does not mark it as public on the server side due to the read\-only nature of pull. .RE .sp Sometimes it may be desirable to push and pull changesets in the draft phase to share unfinished work. This can be done by setting a repository to disable publishing in its configuration file: .sp .nf .ft C [phases] publish = False .ft P .fi .sp See \%\fBhg help config\fP\: for more information on configuration files. .IP Note . Servers running older versions of Mercurial are treated as publishing. .RE .SS Examples .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 . list changesets in draft or secret phase: .sp .nf .ft C hg log \-r "not public()" .ft P .fi .IP \(bu 2 . change all secret changesets to draft: .sp .nf .ft C hg phase \-\-draft "secret()" .ft P .fi .IP \(bu 2 . forcibly move the current changeset and descendants from public to draft: .sp .nf .ft C hg phase \-\-force \-\-draft . .ft P .fi .IP \(bu 2 . show a list of changeset revision and phase: .sp .nf .ft C hg log \-\-template "{rev} {phase}\en" .ft P .fi .IP \(bu 2 . resynchronize draft changesets relative to a remote repository: .sp .nf .ft C hg phase \-fd "outgoing(URL)" .ft P .fi .UNINDENT .UNINDENT .UNINDENT .sp See \%\fBhg help phase\fP\: for more information on manually manipulating phases. .SH SPECIFYING SINGLE REVISIONS .sp Mercurial supports several ways to specify individual revisions. .sp A plain integer is treated as a revision number. Negative integers are treated as sequential offsets from the tip, with \-1 denoting the tip, \-2 denoting the revision prior to the tip, and so forth. .sp A 40\-digit hexadecimal string is treated as a unique revision identifier. .sp A hexadecimal string less than 40 characters long is treated as a unique revision identifier and is referred to as a short\-form identifier. A short\-form identifier is only valid if it is the prefix of exactly one full\-length identifier. .sp Any other string is treated as a bookmark, tag, or branch name. A bookmark is a movable pointer to a revision. A tag is a permanent name associated with a revision. A branch name denotes the tipmost revision of that branch. Bookmark, tag, and branch names must not contain the ":" character. .sp The reserved name "tip" always identifies the most recent revision. .sp The reserved name "null" indicates the null revision. This is the revision of an empty repository, and the parent of revision 0. .sp The reserved name "." indicates the working directory parent. If no working directory is checked out, it is equivalent to null. If an uncommitted merge is in progress, "." is the revision of the first parent. .SH SPECIFYING REVISION SETS .sp Mercurial supports a functional language for selecting a set of revisions. .sp The language supports a number of predicates which are joined by infix operators. Parenthesis can be used for grouping. .sp Identifiers such as branch names may need quoting with single or double quotes if they contain characters like \fB\-\fP or if they match one of the predefined predicates. .sp Special characters can be used in quoted identifiers by escaping them, e.g., \fB\en\fP is interpreted as a newline. To prevent them from being interpreted, strings can be prefixed with \fBr\fP, e.g. \fBr\(aq...\(aq\fP. .sp There is a single prefix operator: .INDENT 0.0 .TP .B \fBnot x\fP .sp Changesets not in x. Short form is \fB! x\fP. .UNINDENT .sp These are the supported infix operators: .INDENT 0.0 .TP .B \fBx::y\fP .sp A DAG range, meaning all changesets that are descendants of x and ancestors of y, including x and y themselves. If the first endpoint is left out, this is equivalent to \fBancestors(y)\fP, if the second is left out it is equivalent to \fBdescendants(x)\fP. .sp An alternative syntax is \fBx..y\fP. .TP .B \fBx:y\fP .sp All changesets with revision numbers between x and y, both inclusive. Either endpoint can be left out, they default to 0 and tip. .TP .B \fBx and y\fP .sp The intersection of changesets in x and y. Short form is \fBx & y\fP. .TP .B \fBx or y\fP .sp The union of changesets in x and y. There are two alternative short forms: \fBx | y\fP and \fBx + y\fP. .TP .B \fBx \- y\fP .sp Changesets in x but not in y. .TP .B \fBx^n\fP .sp The nth parent of x, n == 0, 1, or 2. For n == 0, x; for n == 1, the first parent of each changeset in x; for n == 2, the second parent of changeset in x. .TP .B \fBx~n\fP .sp The nth first ancestor of x; \fBx~0\fP is x; \fBx~3\fP is \fBx^^^\fP. .UNINDENT .sp There is a single postfix operator: .INDENT 0.0 .TP .B \fBx^\fP .sp Equivalent to \fBx^1\fP, the first parent of each changeset in x. .UNINDENT .sp The following predicates are supported: .INDENT 0.0 .TP .B \fBadds(pattern)\fP .sp Changesets that add a file matching pattern. .TP .B \fBall()\fP .sp All changesets, the same as \fB0:tip\fP. .TP .B \fBancestor(*changeset)\fP .sp Greatest common ancestor of the changesets. .sp Accepts 0 or more changesets. Will return empty list when passed no args. Greatest common ancestor of a single changeset is that changeset. .TP .B \fBancestors(set)\fP .sp Changesets that are ancestors of a changeset in set. .TP .B \fBauthor(string)\fP .sp Alias for \fBuser(string)\fP. .TP .B \fBbisect(string)\fP .sp Changesets marked in the specified bisect status: .INDENT 7.0 .IP \(bu 2 . \fBgood\fP, \fBbad\fP, \fBskip\fP: csets explicitly marked as good/bad/skip .IP \(bu 2 . \fBgoods\fP, \fBbads\fP : csets topologically good/bad .IP \(bu 2 . \fBrange\fP : csets taking part in the bisection .IP \(bu 2 . \fBpruned\fP : csets that are goods, bads or skipped .IP \(bu 2 . \fBuntested\fP : csets whose fate is yet unknown .IP \(bu 2 . \fBignored\fP : csets ignored due to DAG topology .IP \(bu 2 . \fBcurrent\fP : the cset currently being bisected .UNINDENT .TP .B \fBbookmark([name])\fP .sp The named bookmark or all bookmarks. .sp If \fIname\fP starts with \fIre:\fP, the remainder of the name is treated as a regular expression. To match a bookmark that actually starts with \fIre:\fP, use the prefix \fIliteral:\fP. .TP .B \fBbranch(string or set)\fP .sp All changesets belonging to the given branch or the branches of the given changesets. .sp If \fIstring\fP starts with \fIre:\fP, the remainder of the name is treated as a regular expression. To match a branch that actually starts with \fIre:\fP, use the prefix \fIliteral:\fP. .TP .B \fBbranchpoint()\fP .sp Changesets with more than one child. .TP .B \fBbumped()\fP .sp Mutable changesets marked as successors of public changesets. .sp Only non\-public and non\-obsolete changesets can be \fIbumped\fP. .TP .B \fBbundle()\fP .sp Changesets in the bundle. .sp Bundle must be specified by the \-R option. .TP .B \fBchildren(set)\fP .sp Child changesets of changesets in set. .TP .B \fBclosed()\fP .sp Changeset is closed. .TP .B \fBcontains(pattern)\fP .sp Revision contains a file matching pattern. See \%\fBhg help patterns\fP\: for information about file patterns. .TP .B \fBconverted([id])\fP .sp Changesets converted from the given identifier in the old repository if present, or all converted changesets if no identifier is specified. .TP .B \fBdate(interval)\fP .sp Changesets within the interval, see \%\fBhg help dates\fP\:. .TP .B \fBdesc(string)\fP .sp Search commit message for string. The match is case\-insensitive. .TP .B \fBdescendants(set)\fP .sp Changesets which are descendants of changesets in set. .TP .B \fBdestination([set])\fP .sp Changesets that were created by a graft, transplant or rebase operation, with the given revisions specified as the source. Omitting the optional set is the same as passing all(). .TP .B \fBdivergent()\fP .sp Final successors of changesets with an alternative set of final successors. .TP .B \fBdraft()\fP .sp Changeset in draft phase. .TP .B \fBextinct()\fP .sp Obsolete changesets with obsolete descendants only. .TP .B \fBextra(label, [value])\fP .sp Changesets with the given label in the extra metadata, with the given optional value. .sp If \fIvalue\fP starts with \fIre:\fP, the remainder of the value is treated as a regular expression. To match a value that actually starts with \fIre:\fP, use the prefix \fIliteral:\fP. .TP .B \fBfile(pattern)\fP .sp Changesets affecting files matched by pattern. .sp For a faster but less accurate result, consider using \fBfilelog()\fP instead. .TP .B \fBfilelog(pattern)\fP .sp Changesets connected to the specified filelog. .sp For performance reasons, \fBfilelog()\fP does not show every changeset that affects the requested file(s). See \%\fBhg help log\fP\: for details. For a slower, more accurate result, use \fBfile()\fP. .TP .B \fBfirst(set, [n])\fP .sp An alias for limit(). .TP .B \fBfollow([file])\fP .sp An alias for \fB::.\fP (ancestors of the working copy\(aqs first parent). If a filename is specified, the history of the given file is followed, including copies. .TP .B \fBgrep(regex)\fP .sp Like \fBkeyword(string)\fP but accepts a regex. Use \fBgrep(r\(aq...\(aq)\fP to ensure special escape characters are handled correctly. Unlike \fBkeyword(string)\fP, the match is case\-sensitive. .TP .B \fBhead()\fP .sp Changeset is a named branch head. .TP .B \fBheads(set)\fP .sp Members of set with no children in set. .TP .B \fBhidden()\fP .sp Hidden changesets. .TP .B \fBid(string)\fP .sp Revision non\-ambiguously specified by the given hex string prefix. .TP .B \fBkeyword(string)\fP .sp Search commit message, user name, and names of changed files for string. The match is case\-insensitive. .TP .B \fBlast(set, [n])\fP .sp Last n members of set, defaulting to 1. .TP .B \fBlimit(set, [n])\fP .sp First n members of set, defaulting to 1. .TP .B \fBmatching(revision [, field])\fP .sp Changesets in which a given set of fields match the set of fields in the selected revision or set. .sp To match more than one field pass the list of fields to match separated by spaces (e.g. \fBauthor description\fP). .sp Valid fields are most regular revision fields and some special fields. .sp Regular revision fields are \fBdescription\fP, \fBauthor\fP, \fBbranch\fP, \fBdate\fP, \fBfiles\fP, \fBphase\fP, \fBparents\fP, \fBsubstate\fP, \fBuser\fP and \fBdiff\fP. Note that \fBauthor\fP and \fBuser\fP are synonyms. \fBdiff\fP refers to the contents of the revision. Two revisions matching their \fBdiff\fP will also match their \fBfiles\fP. .sp Special fields are \fBsummary\fP and \fBmetadata\fP: \fBsummary\fP matches the first line of the description. \fBmetadata\fP is equivalent to matching \fBdescription user date\fP (i.e. it matches the main metadata fields). .sp \fBmetadata\fP is the default field which is used when no fields are specified. You can match more than one field at a time. .TP .B \fBmax(set)\fP .sp Changeset with highest revision number in set. .TP .B \fBmerge()\fP .sp Changeset is a merge changeset. .TP .B \fBmin(set)\fP .sp Changeset with lowest revision number in set. .TP .B \fBmodifies(pattern)\fP .sp Changesets modifying files matched by pattern. .TP .B \fBobsolete()\fP .sp Mutable changeset with a newer version. .TP .B \fBorigin([set])\fP .sp Changesets that were specified as a source for the grafts, transplants or rebases that created the given revisions. Omitting the optional set is the same as passing all(). If a changeset created by these operations is itself specified as a source for one of these operations, only the source changeset for the first operation is selected. .TP .B \fBoutgoing([path])\fP .sp Changesets not found in the specified destination repository, or the default push location. .TP .B \fBp1([set])\fP .sp First parent of changesets in set, or the working directory. .TP .B \fBp2([set])\fP .sp Second parent of changesets in set, or the working directory. .TP .B \fBparents([set])\fP .sp The set of all parents for all changesets in set, or the working directory. .TP .B \fBpresent(set)\fP .sp An empty set, if any revision in set isn\(aqt found; otherwise, all revisions in set. .sp If any of specified revisions is not present in the local repository, the query is normally aborted. But this predicate allows the query to continue even in such cases. .TP .B \fBpublic()\fP .sp Changeset in public phase. .TP .B \fBremote([id [,path]])\fP .sp Local revision that corresponds to the given identifier in a remote repository, if present. Here, the \(aq.\(aq identifier is a synonym for the current local branch. .TP .B \fBremoves(pattern)\fP .sp Changesets which remove files matching pattern. .TP .B \fBrev(number)\fP .sp Revision with the given numeric identifier. .TP .B \fBreverse(set)\fP .sp Reverse order of set. .TP .B \fBroots(set)\fP .sp Changesets in set with no parent changeset in set. .TP .B \fBsecret()\fP .sp Changeset in secret phase. .TP .B \fBsort(set[, [\-]key...])\fP .sp Sort set by keys. The default sort order is ascending, specify a key as \fB\-key\fP to sort in descending order. .sp The keys can be: .INDENT 7.0 .IP \(bu 2 . \fBrev\fP for the revision number, .IP \(bu 2 . \fBbranch\fP for the branch name, .IP \(bu 2 . \fBdesc\fP for the commit message (description), .IP \(bu 2 . \fBuser\fP for user name (\fBauthor\fP can be used as an alias), .IP \(bu 2 . \fBdate\fP for the commit date .UNINDENT .TP .B \fBtag([name])\fP .sp The specified tag by name, or all tagged revisions if no name is given. .TP .B \fBunstable()\fP .sp Non\-obsolete changesets with obsolete ancestors. .TP .B \fBuser(string)\fP .sp User name contains string. The match is case\-insensitive. .sp If \fIstring\fP starts with \fIre:\fP, the remainder of the string is treated as a regular expression. To match a user that actually contains \fIre:\fP, use the prefix \fIliteral:\fP. .UNINDENT .sp New predicates (known as "aliases") can be defined, using any combination of existing predicates or other aliases. An alias definition looks like: .sp .nf .ft C = .ft P .fi .sp in the \fBrevsetalias\fP section of a Mercurial configuration file. Arguments of the form \fI$1\fP, \fI$2\fP, etc. are substituted from the alias into the definition. .sp For example, .sp .nf .ft C [revsetalias] h = heads() d($1) = sort($1, date) rs($1, $2) = reverse(sort($1, $2)) .ft P .fi .sp defines three aliases, \fBh\fP, \fBd\fP, and \fBrs\fP. \fBrs(0:tip, author)\fP is exactly equivalent to \fBreverse(sort(0:tip, author))\fP. .sp Command line equivalents for \%\fBhg log\fP\:: .sp .nf .ft C \-f \-> ::. \-d x \-> date(x) \-k x \-> keyword(x) \-m \-> merge() \-u x \-> user(x) \-b x \-> branch(x) \-P x \-> !::x \-l x \-> limit(expr, x) .ft P .fi .sp Some sample queries: .INDENT 0.0 .IP \(bu 2 . Changesets on the default branch: .sp .nf .ft C hg log \-r "branch(default)" .ft P .fi .IP \(bu 2 . Changesets on the default branch since tag 1.5 (excluding merges): .sp .nf .ft C hg log \-r "branch(default) and 1.5:: and not merge()" .ft P .fi .IP \(bu 2 . Open branch heads: .sp .nf .ft C hg log \-r "head() and not closed()" .ft P .fi .IP \(bu 2 . Changesets between tags 1.3 and 1.5 mentioning "bug" that affect \fBhgext/*\fP: .sp .nf .ft C hg log \-r "1.3::1.5 and keyword(bug) and file(\(aqhgext/*\(aq)" .ft P .fi .IP \(bu 2 . Changesets committed in May 2008, sorted by user: .sp .nf .ft C hg log \-r "sort(date(\(aqMay 2008\(aq), user)" .ft P .fi .IP \(bu 2 . Changesets mentioning "bug" or "issue" that are not in a tagged release: .sp .nf .ft C hg log \-r "(keyword(bug) or keyword(issue)) and not ancestors(tag())" .ft P .fi .UNINDENT .SH SUBREPOSITORIES .sp Subrepositories let you nest external repositories or projects into a parent Mercurial repository, and make commands operate on them as a group. .sp Mercurial currently supports Mercurial, Git, and Subversion subrepositories. .sp Subrepositories are made of three components: .INDENT 0.0 .IP 1. 3 . Nested repository checkouts. They can appear anywhere in the parent working directory. .IP 2. 3 . Nested repository references. They are defined in \fB.hgsub\fP, which should be placed in the root of working directory, and tell where the subrepository checkouts come from. Mercurial subrepositories are referenced like: .sp .nf .ft C path/to/nested = https://example.com/nested/repo/path .ft P .fi .sp Git and Subversion subrepos are also supported: .sp .nf .ft C path/to/nested = [git]git://example.com/nested/repo/path path/to/nested = [svn]https://example.com/nested/trunk/path .ft P .fi .sp where \fBpath/to/nested\fP is the checkout location relatively to the parent Mercurial root, and \fBhttps://example.com/nested/repo/path\fP is the source repository path. The source can also reference a filesystem path. .sp Note that \fB.hgsub\fP does not exist by default in Mercurial repositories, you have to create and add it to the parent repository before using subrepositories. .IP 3. 3 . Nested repository states. They are defined in \fB.hgsubstate\fP, which is placed in the root of working directory, and capture whatever information is required to restore the subrepositories to the state they were committed in a parent repository changeset. Mercurial automatically record the nested repositories states when committing in the parent repository. .IP Note . The \fB.hgsubstate\fP file should not be edited manually. .RE .UNINDENT .SS Adding a Subrepository .sp If \fB.hgsub\fP does not exist, create it and add it to the parent repository. Clone or checkout the external projects where you want it to live in the parent repository. Edit \fB.hgsub\fP and add the subrepository entry as described above. At this point, the subrepository is tracked and the next commit will record its state in \fB.hgsubstate\fP and bind it to the committed changeset. .SS Synchronizing a Subrepository .sp Subrepos do not automatically track the latest changeset of their sources. Instead, they are updated to the changeset that corresponds with the changeset checked out in the top\-level changeset. This is so developers always get a consistent set of compatible code and libraries when they update. .sp Thus, updating subrepos is a manual process. Simply check out target subrepo at the desired revision, test in the top\-level repo, then commit in the parent repository to record the new combination. .SS Deleting a Subrepository .sp To remove a subrepository from the parent repository, delete its reference from \fB.hgsub\fP, then remove its files. .SS Interaction with Mercurial Commands .INDENT 0.0 .TP .B add . add does not recurse in subrepos unless \-S/\-\-subrepos is specified. However, if you specify the full path of a file in a subrepo, it will be added even without \-S/\-\-subrepos specified. Git and Subversion subrepositories are currently silently ignored. .TP .B archive . archive does not recurse in subrepositories unless \-S/\-\-subrepos is specified. .TP .B commit . commit creates a consistent snapshot of the state of the entire project and its subrepositories. If any subrepositories have been modified, Mercurial will abort. Mercurial can be made to instead commit all modified subrepositories by specifying \-S/\-\-subrepos, or setting "ui.commitsubrepos=True" in a configuration file (see \%\fBhg help config\fP\:). After there are no longer any modified subrepositories, it records their state and finally commits it in the parent repository. .TP .B diff . diff does not recurse in subrepos unless \-S/\-\-subrepos is specified. Changes are displayed as usual, on the subrepositories elements. Git and Subversion subrepositories are currently silently ignored. .TP .B forget . forget currently only handles exact file matches in subrepos. Git and Subversion subrepositories are currently silently ignored. .TP .B incoming . incoming does not recurse in subrepos unless \-S/\-\-subrepos is specified. Git and Subversion subrepositories are currently silently ignored. .TP .B outgoing . outgoing does not recurse in subrepos unless \-S/\-\-subrepos is specified. Git and Subversion subrepositories are currently silently ignored. .TP .B pull . pull is not recursive since it is not clear what to pull prior to running \%\fBhg update\fP\:. Listing and retrieving all subrepositories changes referenced by the parent repository pulled changesets is expensive at best, impossible in the Subversion case. .TP .B push . Mercurial will automatically push all subrepositories first when the parent repository is being pushed. This ensures new subrepository changes are available when referenced by top\-level repositories. Push is a no\-op for Subversion subrepositories. .TP .B status . status does not recurse into subrepositories unless \-S/\-\-subrepos is specified. Subrepository changes are displayed as regular Mercurial changes on the subrepository elements. Subversion subrepositories are currently silently ignored. .TP .B update . update restores the subrepos in the state they were originally committed in target changeset. If the recorded changeset is not available in the current subrepository, Mercurial will pull it in first before updating. This means that updating can require network access when using subrepositories. .UNINDENT .SS Remapping Subrepositories Sources .sp A subrepository source location may change during a project life, invalidating references stored in the parent repository history. To fix this, rewriting rules can be defined in parent repository \fBhgrc\fP file or in Mercurial configuration. See the \fB[subpaths]\fP section in hgrc(5) for more details. .SH TEMPLATE USAGE .sp Mercurial allows you to customize output of commands through templates. You can either pass in a template from the command line, via the \-\-template option, or select an existing template\-style (\-\-style). .sp You can customize output for any "log\-like" command: log, outgoing, incoming, tip, parents, heads and glog. .sp Five styles are packaged with Mercurial: default (the style used when no explicit preference is passed), compact, changelog, phases and xml. Usage: .sp .nf .ft C $ hg log \-r1 \-\-style changelog .ft P .fi .sp A template is a piece of text, with markup to invoke variable expansion: .sp .nf .ft C $ hg log \-r1 \-\-template "{node}\en" b56ce7b07c52de7d5fd79fb89701ea538af65746 .ft P .fi .sp Strings in curly braces are called keywords. The availability of keywords depends on the exact context of the templater. These keywords are usually available for templating a log\-like command: .INDENT 0.0 .TP .B author . String. The unmodified author of the changeset. .TP .B bisect . String. The changeset bisection status. .TP .B bookmarks . List of strings. Any bookmarks associated with the changeset. .TP .B branch . String. The name of the branch on which the changeset was committed. .TP .B branches . List of strings. The name of the branch on which the changeset was committed. Will be empty if the branch name was default. .TP .B children . List of strings. The children of the changeset. .TP .B date . Date information. The date when the changeset was committed. .TP .B desc . String. The text of the changeset description. .TP .B diffstat . String. Statistics of changes with the following format: "modified files: +added/\-removed lines" .TP .B file_adds . List of strings. Files added by this changeset. .TP .B file_copies . List of strings. Files copied in this changeset with their sources. .TP .B file_copies_switch . List of strings. Like "file_copies" but displayed only if the \-\-copied switch is set. .TP .B file_dels . List of strings. Files removed by this changeset. .TP .B file_mods . List of strings. Files modified by this changeset. .TP .B files . List of strings. All files modified, added, or removed by this changeset. .TP .B latesttag . String. Most recent global tag in the ancestors of this changeset. .TP .B latesttagdistance . Integer. Longest path to the latest tag. .TP .B node . String. The changeset identification hash, as a 40 hexadecimal digit string. .TP .B p1node . String. The identification hash of the changeset\(aqs first parent, as a 40 digit hexadecimal string. If the changeset has no parents, all digits are 0. .TP .B p1rev . Integer. The repository\-local revision number of the changeset\(aqs first parent, or \-1 if the changeset has no parents. .TP .B p2node . String. The identification hash of the changeset\(aqs second parent, as a 40 digit hexadecimal string. If the changeset has no second parent, all digits are 0. .TP .B p2rev . Integer. The repository\-local revision number of the changeset\(aqs second parent, or \-1 if the changeset has no second parent. .TP .B parents . List of strings. The parents of the changeset in "rev:node" format. If the changeset has only one "natural" parent (the predecessor revision) nothing is shown. .TP .B phase . String. The changeset phase name. .TP .B phaseidx . Integer. The changeset phase index. .TP .B rev . Integer. The repository\-local changeset revision number. .TP .B tags . List of strings. Any tags associated with the changeset. .UNINDENT .sp The "date" keyword does not produce human\-readable output. If you want to use a date in your output, you can use a filter to process it. Filters are functions which return a string based on the input variable. Be sure to use the stringify filter first when you\(aqre applying a string\-input filter to a list\-like input variable. You can also use a chain of filters to get the desired output: .sp .nf .ft C $ hg tip \-\-template "{date|isodate}\en" 2008\-08\-21 18:22 +0000 .ft P .fi .sp List of filters: .INDENT 0.0 .TP .B addbreaks . Any text. Add an XHTML "
" tag before the end of every line except the last. .TP .B age . Date. Returns a human\-readable date/time difference between the given date/time and the current date/time. .TP .B basename . Any text. Treats the text as a path, and returns the last component of the path after splitting by the path separator (ignoring trailing separators). For example, "foo/bar/baz" becomes "baz" and "foo/bar//" becomes "bar". .TP .B date . Date. Returns a date in a Unix date format, including the timezone: "Mon Sep 04 15:13:13 2006 0700". .TP .B domain . Any text. Finds the first string that looks like an email address, and extracts just the domain component. Example: \fBUser \fP becomes \fBexample.com\fP. .TP .B email . Any text. Extracts the first string that looks like an email address. Example: \fBUser \fP becomes \fBuser@example.com\fP. .TP .B emailuser . Any text. Returns the user portion of an email address. .TP .B escape . Any text. Replaces the special XML/XHTML characters "&", "<" and ">" with XML entities, and filters out NUL characters. .TP .B fill68 . Any text. Wraps the text to fit in 68 columns. .TP .B fill76 . Any text. Wraps the text to fit in 76 columns. .TP .B firstline . Any text. Returns the first line of text. .TP .B hex . Any text. Convert a binary Mercurial node identifier into its long hexadecimal representation. .TP .B hgdate . Date. Returns the date as a pair of numbers: "1157407993 25200" (Unix timestamp, timezone offset). .TP .B isodate . Date. Returns the date in ISO 8601 format: "2009\-08\-18 13:00 +0200". .TP .B isodatesec . Date. Returns the date in ISO 8601 format, including seconds: "2009\-08\-18 13:00:13 +0200". See also the rfc3339date filter. .TP .B localdate . Date. Converts a date to local date. .TP .B nonempty . Any text. Returns \(aq(none)\(aq if the string is empty. .TP .B obfuscate . Any text. Returns the input text rendered as a sequence of XML entities. .TP .B person . Any text. Returns the name before an email address, interpreting it as per RFC 5322. .TP .B rfc3339date . Date. Returns a date using the Internet date format specified in RFC 3339: "2009\-08\-18T13:00:13+02:00". .TP .B rfc822date . Date. Returns a date using the same format used in email headers: "Tue, 18 Aug 2009 13:00:13 +0200". .TP .B short . Changeset hash. Returns the short form of a changeset hash, i.e. a 12 hexadecimal digit string. .TP .B shortbisect . Any text. Treats \fItext\fP as a bisection status, and returns a single\-character representing the status (G: good, B: bad, S: skipped, U: untested, I: ignored). Returns single space if \fItext\fP is not a valid bisection status. .TP .B shortdate . Date. Returns a date like "2006\-09\-18". .TP .B stringify . Any type. Turns the value into text by converting values into text and concatenating them. .TP .B strip . Any text. Strips all leading and trailing whitespace. .TP .B stripdir . Treat the text as path and strip a directory level, if possible. For example, "foo" and "foo/bar" becomes "foo". .TP .B tabindent . Any text. Returns the text, with every non\-empty line except the first starting with a tab character. .TP .B urlescape . Any text. Escapes all "special" characters. For example, "foo bar" becomes "foo%20bar". .TP .B user . Any text. Returns a short representation of a user name or email address. .UNINDENT .sp Note that a filter is nothing more than a function call, i.e. \fBexpr|filter\fP is equivalent to \fBfilter(expr)\fP. .sp In addition to filters, there are some basic built\-in functions: .INDENT 0.0 .IP \(bu 2 . date(date[, fmt]) .IP \(bu 2 . fill(text[, width]) .IP \(bu 2 . get(dict, key) .IP \(bu 2 . if(expr, then[, else]) .IP \(bu 2 . ifeq(expr, expr, then[, else]) .IP \(bu 2 . join(list, sep) .IP \(bu 2 . label(label, expr) .IP \(bu 2 . rstdoc(text, style) .IP \(bu 2 . strip(text[, chars]) .IP \(bu 2 . sub(pat, repl, expr) .UNINDENT .sp Also, for any expression that returns a list, there is a list operator: .INDENT 0.0 .IP \(bu 2 . expr % "{template}" .UNINDENT .sp Some sample command line templates: .INDENT 0.0 .IP \(bu 2 . Format lists, e.g. files: .sp .nf .ft C $ hg log \-r 0 \-\-template "files:\en{files % \(aq {file}\en\(aq}" .ft P .fi .IP \(bu 2 . Join the list of files with a ", ": .sp .nf .ft C $ hg log \-r 0 \-\-template "files: {join(files, \(aq, \(aq)}\en" .ft P .fi .IP \(bu 2 . Format date: .sp .nf .ft C $ hg log \-r 0 \-\-template "{date(date, \(aq%Y\(aq)}\en" .ft P .fi .IP \(bu 2 . Output the description set to a fill\-width of 30: .sp .nf .ft C $ hg log \-r 0 \-\-template "{fill(desc, \(aq30\(aq)}" .ft P .fi .IP \(bu 2 . Use a conditional to test for the default branch: .sp .nf .ft C $ hg log \-r 0 \-\-template "{ifeq(branch, \(aqdefault\(aq, \(aqon the main branch\(aq, \(aqon branch {branch}\(aq)}\en" .ft P .fi .IP \(bu 2 . Append a newline if not empty: .sp .nf .ft C $ hg tip \-\-template "{if(author, \(aq{author}\en\(aq)}" .ft P .fi .IP \(bu 2 . Label the output for use with the color extension: .sp .nf .ft C $ hg log \-r 0 \-\-template "{label(\(aqchangeset.{phase}\(aq, node|short)}\en" .ft P .fi .IP \(bu 2 . Invert the firstline filter, i.e. everything but the first line: .sp .nf .ft C $ hg log \-r 0 \-\-template "{sub(r\(aq^.*\en?\en?\(aq, \(aq\(aq, desc)}\en" .ft P .fi .UNINDENT .SH URL PATHS .sp Valid URLs are of the form: .sp .nf .ft C local/filesystem/path[#revision] file://local/filesystem/path[#revision] http://[user[:pass]@]host[:port]/[path][#revision] https://[user[:pass]@]host[:port]/[path][#revision] ssh://[user@]host[:port]/[path][#revision] .ft P .fi .sp Paths in the local filesystem can either point to Mercurial repositories or to bundle files (as created by \%\fBhg bundle\fP\: or \%\fBhg incoming \-\-bundle\fP\:). See also \%\fBhg help paths\fP\:. .sp An optional identifier after # indicates a particular branch, tag, or changeset to use from the remote repository. See also \%\fBhg help revisions\fP\:. .sp Some features, such as pushing to \%http://\: and \%https://\: URLs are only possible if the feature is explicitly enabled on the remote Mercurial server. .sp Note that the security of HTTPS URLs depends on proper configuration of web.cacerts. .sp Some notes about using SSH with Mercurial: .INDENT 0.0 .IP \(bu 2 . SSH requires an accessible shell account on the destination machine and a copy of hg in the remote path or specified with as remotecmd. .IP \(bu 2 . path is relative to the remote user\(aqs home directory by default. Use an extra slash at the start of a path to specify an absolute path: .sp .nf .ft C ssh://example.com//tmp/repository .ft P .fi .IP \(bu 2 . Mercurial doesn\(aqt use its own compression via SSH; the right thing to do is to configure it in your ~/.ssh/config, e.g.: .sp .nf .ft C Host *.mylocalnetwork.example.com Compression no Host * Compression yes .ft P .fi .sp Alternatively specify "ssh \-C" as your ssh command in your configuration file or with the \-\-ssh command line option. .UNINDENT .sp These URLs can all be stored in your configuration file with path aliases under the [paths] section like so: .sp .nf .ft C [paths] alias1 = URL1 alias2 = URL2 \&... .ft P .fi .sp You can then use the alias for any command that uses a URL (for example \%\fBhg pull alias1\fP\: will be treated as \%\fBhg pull URL1\fP\:). .sp Two path aliases are special because they are used as defaults when you do not provide the URL to a command: .INDENT 0.0 .TP .B default: . When you create a repository with hg clone, the clone command saves the location of the source repository as the new repository\(aqs \(aqdefault\(aq path. This is then used when you omit path from push\- and pull\-like commands (including incoming and outgoing). .TP .B default\-push: . The push command will look for a path named \(aqdefault\-push\(aq, and prefer it over \(aqdefault\(aq if both are defined. .UNINDENT .SH EXTENSIONS .sp This section contains help for extensions that are distributed together with Mercurial. Help for other extensions is available in the help system. .SS acl .sp hooks for controlling repository access .sp This hook makes it possible to allow or deny write access to given branches and paths of a repository when receiving incoming changesets via pretxnchangegroup and pretxncommit. .sp The authorization is matched based on the local user name on the system where the hook runs, and not the committer of the original changeset (since the latter is merely informative). .sp The acl hook is best used along with a restricted shell like hgsh, preventing authenticating users from doing anything other than pushing or pulling. The hook is not safe to use if users have interactive shell access, as they can then disable the hook. Nor is it safe if remote users share an account, because then there is no way to distinguish them. .sp The order in which access checks are performed is: .INDENT 0.0 .IP 1. 3 . Deny list for branches (section \fBacl.deny.branches\fP) .IP 2. 3 . Allow list for branches (section \fBacl.allow.branches\fP) .IP 3. 3 . Deny list for paths (section \fBacl.deny\fP) .IP 4. 3 . Allow list for paths (section \fBacl.allow\fP) .UNINDENT .sp The allow and deny sections take key\-value pairs. .SS Branch\-based Access Control .sp Use the \fBacl.deny.branches\fP and \fBacl.allow.branches\fP sections to have branch\-based access control. Keys in these sections can be either: .INDENT 0.0 .IP \(bu 2 . a branch name, or .IP \(bu 2 . an asterisk, to match any branch; .UNINDENT .sp The corresponding values can be either: .INDENT 0.0 .IP \(bu 2 . a comma\-separated list containing users and groups, or .IP \(bu 2 . an asterisk, to match anyone; .UNINDENT .sp You can add the "!" prefix to a user or group name to invert the sense of the match. .SS Path\-based Access Control .sp Use the \fBacl.deny\fP and \fBacl.allow\fP sections to have path\-based access control. Keys in these sections accept a subtree pattern (with a glob syntax by default). The corresponding values follow the same syntax as the other sections above. .SS Groups .sp Group names must be prefixed with an \fB@\fP symbol. Specifying a group name has the same effect as specifying all the users in that group. .sp You can define group members in the \fBacl.groups\fP section. If a group name is not defined there, and Mercurial is running under a Unix\-like system, the list of users will be taken from the OS. Otherwise, an exception will be raised. .SS Example Configuration .sp .nf .ft C [hooks] # Use this if you want to check access restrictions at commit time pretxncommit.acl = python:hgext.acl.hook # Use this if you want to check access restrictions for pull, push, # bundle and serve. pretxnchangegroup.acl = python:hgext.acl.hook [acl] # Allow or deny access for incoming changes only if their source is # listed here, let them pass otherwise. Source is "serve" for all # remote access (http or ssh), "push", "pull" or "bundle" when the # related commands are run locally. # Default: serve sources = serve [acl.deny.branches] # Everyone is denied to the frozen branch: frozen\-branch = * # A bad user is denied on all branches: * = bad\-user [acl.allow.branches] # A few users are allowed on branch\-a: branch\-a = user\-1, user\-2, user\-3 # Only one user is allowed on branch\-b: branch\-b = user\-1 # The super user is allowed on any branch: * = super\-user # Everyone is allowed on branch\-for\-tests: branch\-for\-tests = * [acl.deny] # This list is checked first. If a match is found, acl.allow is not # checked. All users are granted access if acl.deny is not present. # Format for both lists: glob pattern = user, ..., @group, ... # To match everyone, use an asterisk for the user: # my/glob/pattern = * # user6 will not have write access to any file: ** = user6 # Group "hg\-denied" will not have write access to any file: ** = @hg\-denied # Nobody will be able to change "DONT\-TOUCH\-THIS.txt", despite # everyone being able to change all other files. See below. src/main/resources/DONT\-TOUCH\-THIS.txt = * [acl.allow] # if acl.allow is not present, all users are allowed by default # empty acl.allow = no users allowed # User "doc_writer" has write access to any file under the "docs" # folder: docs/** = doc_writer # User "jack" and group "designers" have write access to any file # under the "images" folder: images/** = jack, @designers # Everyone (except for "user6" and "@hg\-denied" \- see acl.deny above) # will have write access to any file under the "resources" folder # (except for 1 file. See acl.deny): src/main/resources/** = * \&.hgtags = release_engineer .ft P .fi .SS Examples using the "!" prefix .sp Suppose there\(aqs a branch that only a given user (or group) should be able to push to, and you don\(aqt want to restrict access to any other branch that may be created. .sp The "!" prefix allows you to prevent anyone except a given user or group to push changesets in a given branch or path. .sp In the examples below, we will: 1) Deny access to branch "ring" to anyone but user "gollum" 2) Deny access to branch "lake" to anyone but members of the group "hobbit" 3) Deny access to a file to anyone but user "gollum" .sp .nf .ft C [acl.allow.branches] # Empty [acl.deny.branches] # 1) only \(aqgollum\(aq can commit to branch \(aqring\(aq; # \(aqgollum\(aq and anyone else can still commit to any other branch. ring = !gollum # 2) only members of the group \(aqhobbit\(aq can commit to branch \(aqlake\(aq; # \(aqhobbit\(aq members and anyone else can still commit to any other branch. lake = !@hobbit # You can also deny access based on file paths: [acl.allow] # Empty [acl.deny] # 3) only \(aqgollum\(aq can change the file below; # \(aqgollum\(aq and anyone else can still change any other file. /misty/mountains/cave/ring = !gollum .ft P .fi .SS blackbox .sp log repository events to a blackbox for debugging .sp Logs event information to .hg/blackbox.log to help debug and diagnose problems. The events that get logged can be configured via the blackbox.track config key. Examples: .sp .nf .ft C [blackbox] track = * [blackbox] track = command, commandfinish, commandexception, exthook, pythonhook [blackbox] track = incoming [blackbox] # limit the size of a log file maxsize = 1.5 MB # rotate up to N log files when the current one gets too big maxfiles = 3 .ft P .fi .SS Commands .SS blackbox .sp .nf .ft C hg blackbox [OPTION]... .ft P .fi .sp view the recent repository events .sp Options: .INDENT 0.0 .TP .B \-l, \-\-limit . the number of events to show (default: 10) .UNINDENT .SS bugzilla .sp hooks for integrating with the Bugzilla bug tracker .sp This hook extension adds comments on bugs in Bugzilla when changesets that refer to bugs by Bugzilla ID are seen. The comment is formatted using the Mercurial template mechanism. .sp The bug references can optionally include an update for Bugzilla of the hours spent working on the bug. Bugs can also be marked fixed. .sp Three basic modes of access to Bugzilla are provided: .INDENT 0.0 .IP 1. 3 . Access via the Bugzilla XMLRPC interface. Requires Bugzilla 3.4 or later. .IP 2. 3 . Check data via the Bugzilla XMLRPC interface and submit bug change via email to Bugzilla email interface. Requires Bugzilla 3.4 or later. .IP 3. 3 . Writing directly to the Bugzilla database. Only Bugzilla installations using MySQL are supported. Requires Python MySQLdb. .UNINDENT .sp Writing directly to the database is susceptible to schema changes, and relies on a Bugzilla contrib script to send out bug change notification emails. This script runs as the user running Mercurial, must be run on the host with the Bugzilla install, and requires permission to read Bugzilla configuration details and the necessary MySQL user and password to have full access rights to the Bugzilla database. For these reasons this access mode is now considered deprecated, and will not be updated for new Bugzilla versions going forward. Only adding comments is supported in this access mode. .sp Access via XMLRPC needs a Bugzilla username and password to be specified in the configuration. Comments are added under that username. Since the configuration must be readable by all Mercurial users, it is recommended that the rights of that user are restricted in Bugzilla to the minimum necessary to add comments. Marking bugs fixed requires Bugzilla 4.0 and later. .sp Access via XMLRPC/email uses XMLRPC to query Bugzilla, but sends email to the Bugzilla email interface to submit comments to bugs. The From: address in the email is set to the email address of the Mercurial user, so the comment appears to come from the Mercurial user. In the event that the Mercurial user email is not recognized by Bugzilla as a Bugzilla user, the email associated with the Bugzilla username used to log into Bugzilla is used instead as the source of the comment. Marking bugs fixed works on all supported Bugzilla versions. .sp Configuration items common to all access modes: .INDENT 0.0 .TP .B bugzilla.version . The access type to use. Values recognized are: .INDENT 7.0 .TP .B \fBxmlrpc\fP .sp Bugzilla XMLRPC interface. .TP .B \fBxmlrpc+email\fP .sp Bugzilla XMLRPC and email interfaces. .TP .B \fB3.0\fP .sp MySQL access, Bugzilla 3.0 and later. .TP .B \fB2.18\fP .sp MySQL access, Bugzilla 2.18 and up to but not including 3.0. .TP .B \fB2.16\fP .sp MySQL access, Bugzilla 2.16 and up to but not including 2.18. .UNINDENT .TP .B bugzilla.regexp . Regular expression to match bug IDs for update in changeset commit message. It must contain one "()" named group \fB\fP containing the bug IDs separated by non\-digit characters. It may also contain a named group \fB\fP with a floating\-point number giving the hours worked on the bug. If no named groups are present, the first "()" group is assumed to contain the bug IDs, and work time is not updated. The default expression matches \fBBug 1234\fP, \fBBug no. 1234\fP, \fBBug number 1234\fP, \fBBugs 1234,5678\fP, \fBBug 1234 and 5678\fP and variations thereof, followed by an hours number prefixed by \fBh\fP or \fBhours\fP, e.g. \fBhours 1.5\fP. Matching is case insensitive. .TP .B bugzilla.fixregexp . Regular expression to match bug IDs for marking fixed in changeset commit message. This must contain a "()" named group \fB\(ga containing the bug IDs separated by non\-digit characters. It may also contain a named group \(ga\(ga\fP with a floating\-point number giving the hours worked on the bug. If no named groups are present, the first "()" group is assumed to contain the bug IDs, and work time is not updated. The default expression matches \fBFixes 1234\fP, \fBFixes bug 1234\fP, \fBFixes bugs 1234,5678\fP, \fBFixes 1234 and 5678\fP and variations thereof, followed by an hours number prefixed by \fBh\fP or \fBhours\fP, e.g. \fBhours 1.5\fP. Matching is case insensitive. .TP .B bugzilla.fixstatus . The status to set a bug to when marking fixed. Default \fBRESOLVED\fP. .TP .B bugzilla.fixresolution . The resolution to set a bug to when marking fixed. Default \fBFIXED\fP. .TP .B bugzilla.style . The style file to use when formatting comments. .TP .B bugzilla.template . Template to use when formatting comments. Overrides style if specified. In addition to the usual Mercurial keywords, the extension specifies: .INDENT 7.0 .TP .B \fB{bug}\fP .sp The Bugzilla bug ID. .TP .B \fB{root}\fP .sp The full pathname of the Mercurial repository. .TP .B \fB{webroot}\fP .sp Stripped pathname of the Mercurial repository. .TP .B \fB{hgweb}\fP .sp Base URL for browsing Mercurial repositories. .UNINDENT .sp Default \fBchangeset {node|short} in repo {root} refers to bug {bug}.\endetails:\en\et{desc|tabindent}\fP .TP .B bugzilla.strip . The number of path separator characters to strip from the front of the Mercurial repository path (\fB{root}\fP in templates) to produce \fB{webroot}\fP. For example, a repository with \fB{root}\fP \fB/var/local/my\-project\fP with a strip of 2 gives a value for \fB{webroot}\fP of \fBmy\-project\fP. Default 0. .TP .B web.baseurl . Base URL for browsing Mercurial repositories. Referenced from templates as \fB{hgweb}\fP. .UNINDENT .sp Configuration items common to XMLRPC+email and MySQL access modes: .INDENT 0.0 .TP .B bugzilla.usermap . Path of file containing Mercurial committer email to Bugzilla user email mappings. If specified, the file should contain one mapping per line: .sp .nf .ft C committer = Bugzilla user .ft P .fi .sp See also the \fB[usermap]\fP section. .UNINDENT .sp The \fB[usermap]\fP section is used to specify mappings of Mercurial committer email to Bugzilla user email. See also \fBbugzilla.usermap\fP. Contains entries of the form \fBcommitter = Bugzilla user\fP. .sp XMLRPC access mode configuration: .INDENT 0.0 .TP .B bugzilla.bzurl . The base URL for the Bugzilla installation. Default \fBhttp://localhost/bugzilla\fP. .TP .B bugzilla.user . The username to use to log into Bugzilla via XMLRPC. Default \fBbugs\fP. .TP .B bugzilla.password . The password for Bugzilla login. .UNINDENT .sp XMLRPC+email access mode uses the XMLRPC access mode configuration items, and also: .INDENT 0.0 .TP .B bugzilla.bzemail . The Bugzilla email address. .UNINDENT .sp In addition, the Mercurial email settings must be configured. See the documentation in hgrc(5), sections \fB[email]\fP and \fB[smtp]\fP. .sp MySQL access mode configuration: .INDENT 0.0 .TP .B bugzilla.host . Hostname of the MySQL server holding the Bugzilla database. Default \fBlocalhost\fP. .TP .B bugzilla.db . Name of the Bugzilla database in MySQL. Default \fBbugs\fP. .TP .B bugzilla.user . Username to use to access MySQL server. Default \fBbugs\fP. .TP .B bugzilla.password . Password to use to access MySQL server. .TP .B bugzilla.timeout . Database connection timeout (seconds). Default 5. .TP .B bugzilla.bzuser . Fallback Bugzilla user name to record comments with, if changeset committer cannot be found as a Bugzilla user. .TP .B bugzilla.bzdir . Bugzilla install directory. Used by default notify. Default \fB/var/www/html/bugzilla\fP. .TP .B bugzilla.notify . The command to run to get Bugzilla to send bug change notification emails. Substitutes from a map with 3 keys, \fBbzdir\fP, \fBid\fP (bug id) and \fBuser\fP (committer bugzilla email). Default depends on version; from 2.18 it is "cd %(bzdir)s && perl \-T contrib/sendbugmail.pl %(id)s %(user)s". .UNINDENT .sp Activating the extension: .sp .nf .ft C [extensions] bugzilla = [hooks] # run bugzilla hook on every change pulled or pushed in here incoming.bugzilla = python:hgext.bugzilla.hook .ft P .fi .sp Example configurations: .sp XMLRPC example configuration. This uses the Bugzilla at \fBhttp://my\-project.org/bugzilla\fP, logging in as user \fBbugmail@my\-project.org\fP with password \fBplugh\fP. It is used with a collection of Mercurial repositories in \fB/var/local/hg/repos/\fP, with a web interface at \fBhttp://my\-project.org/hg\fP. .sp .nf .ft C [bugzilla] bzurl=http://my\-project.org/bugzilla user=bugmail@my\-project.org password=plugh version=xmlrpc template=Changeset {node|short} in {root|basename}. {hgweb}/{webroot}/rev/{node|short}\en {desc}\en strip=5 [web] baseurl=http://my\-project.org/hg .ft P .fi .sp XMLRPC+email example configuration. This uses the Bugzilla at \fBhttp://my\-project.org/bugzilla\fP, logging in as user \fBbugmail@my\-project.org\fP with password \fBplugh\fP. It is used with a collection of Mercurial repositories in \fB/var/local/hg/repos/\fP, with a web interface at \fBhttp://my\-project.org/hg\fP. Bug comments are sent to the Bugzilla email address \fBbugzilla@my\-project.org\fP. .sp .nf .ft C [bugzilla] bzurl=http://my\-project.org/bugzilla user=bugmail@my\-project.org password=plugh version=xmlrpc bzemail=bugzilla@my\-project.org template=Changeset {node|short} in {root|basename}. {hgweb}/{webroot}/rev/{node|short}\en {desc}\en strip=5 [web] baseurl=http://my\-project.org/hg [usermap] user@emaildomain.com=user.name@bugzilladomain.com .ft P .fi .sp MySQL example configuration. This has a local Bugzilla 3.2 installation in \fB/opt/bugzilla\-3.2\fP. The MySQL database is on \fBlocalhost\fP, the Bugzilla database name is \fBbugs\fP and MySQL is accessed with MySQL username \fBbugs\fP password \fBXYZZY\fP. It is used with a collection of Mercurial repositories in \fB/var/local/hg/repos/\fP, with a web interface at \fBhttp://my\-project.org/hg\fP. .sp .nf .ft C [bugzilla] host=localhost password=XYZZY version=3.0 bzuser=unknown@domain.com bzdir=/opt/bugzilla\-3.2 template=Changeset {node|short} in {root|basename}. {hgweb}/{webroot}/rev/{node|short}\en {desc}\en strip=5 [web] baseurl=http://my\-project.org/hg [usermap] user@emaildomain.com=user.name@bugzilladomain.com .ft P .fi .sp All the above add a comment to the Bugzilla bug record of the form: .sp .nf .ft C Changeset 3b16791d6642 in repository\-name. http://my\-project.org/hg/repository\-name/rev/3b16791d6642 Changeset commit comment. Bug 1234. .ft P .fi .SS children .sp command to display child changesets (DEPRECATED) .sp This extension is deprecated. You should use \%\fBhg log \-r "children(REV)"\fP\: instead. .SS Commands .SS children .sp .nf .ft C hg children [\-r REV] [FILE] .ft P .fi .sp Print the children of the working directory\(aqs revisions. If a revision is given via \-r/\-\-rev, the children of that revision will be printed. If a file argument is given, revision in which the file was last changed (after the working directory revision or the argument to \-\-rev if given) is printed. .sp Options: .INDENT 0.0 .TP .B \-r, \-\-rev . show children of the specified revision .TP .B \-\-style . display using template map file .TP .B \-\-template . display with template .UNINDENT .SS churn .sp command to display statistics about repository history .SS Commands .SS churn .sp .nf .ft C hg churn [\-d DATE] [\-r REV] [\-\-aliases FILE] [FILE] .ft P .fi .sp This command will display a histogram representing the number of changed lines or revisions, grouped according to the given template. The default template will group changes by author. The \-\-dateformat option may be used to group the results by date instead. .sp Statistics are based on the number of changed lines, or alternatively the number of matching revisions if the \-\-changesets option is specified. .sp Examples: .sp .nf .ft C # display count of changed lines for every committer hg churn \-t "{author|email}" # display daily activity graph hg churn \-f "%H" \-s \-c # display activity of developers by month hg churn \-f "%Y\-%m" \-s \-c # display count of lines changed in every year hg churn \-f "%Y" \-s .ft P .fi .sp It is possible to map alternate email addresses to a main address by providing a file using the following format: .sp .nf .ft C = .ft P .fi .sp Such a file may be specified with the \-\-aliases option, otherwise a .hgchurn file will be looked for in the working directory root. Aliases will be split from the rightmost "=". .sp Options: .INDENT 0.0 .TP .B \-r, \-\-rev . count rate for the specified revision or range .TP .B \-d, \-\-date . count rate for revisions matching date spec .TP .B \-t, \-\-template . template to group changesets (default: {author|email}) .TP .B \-f, \-\-dateformat . strftime\-compatible format for grouping by date .TP .B \-c, \-\-changesets . count rate by number of changesets .TP .B \-s, \-\-sort . sort by key (default: sort by count) .TP .B \-\-diffstat . display added/removed lines separately .TP .B \-\-aliases . file with email aliases .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .UNINDENT .SS color .sp colorize output from some commands .sp This extension modifies the status and resolve commands to add color to their output to reflect file status, the qseries command to add color to reflect patch status (applied, unapplied, missing), and to diff\-related commands to highlight additions, removals, diff headers, and trailing whitespace. .sp Other effects in addition to color, like bold and underlined text, are also available. By default, the terminfo database is used to find the terminal codes used to change color and effect. If terminfo is not available, then effects are rendered with the ECMA\-48 SGR control function (aka ANSI escape codes). .sp Default effects may be overridden from your configuration file: .sp .nf .ft C [color] status.modified = blue bold underline red_background status.added = green bold status.removed = red bold blue_background status.deleted = cyan bold underline status.unknown = magenta bold underline status.ignored = black bold # \(aqnone\(aq turns off all effects status.clean = none status.copied = none qseries.applied = blue bold underline qseries.unapplied = black bold qseries.missing = red bold diff.diffline = bold diff.extended = cyan bold diff.file_a = red bold diff.file_b = green bold diff.hunk = magenta diff.deleted = red diff.inserted = green diff.changed = white diff.trailingwhitespace = bold red_background resolve.unresolved = red bold resolve.resolved = green bold bookmarks.current = green branches.active = none branches.closed = black bold branches.current = green branches.inactive = none tags.normal = green tags.local = black bold rebase.rebased = blue rebase.remaining = red bold shelve.age = cyan shelve.newest = green bold shelve.name = blue bold histedit.remaining = red bold .ft P .fi .sp The available effects in terminfo mode are \(aqblink\(aq, \(aqbold\(aq, \(aqdim\(aq, \(aqinverse\(aq, \(aqinvisible\(aq, \(aqitalic\(aq, \(aqstandout\(aq, and \(aqunderline\(aq; in ECMA\-48 mode, the options are \(aqbold\(aq, \(aqinverse\(aq, \(aqitalic\(aq, and \(aqunderline\(aq. How each is rendered depends on the terminal emulator. Some may not be available for a given terminal type, and will be silently ignored. .sp Note that on some systems, terminfo mode may cause problems when using color with the pager extension and less \-R. less with the \-R option will only display ECMA\-48 color codes, and terminfo mode may sometimes emit codes that less doesn\(aqt understand. You can work around this by either using ansi mode (or auto mode), or by using less \-r (which will pass through all terminal control codes, not just color control codes). .sp Because there are only eight standard colors, this module allows you to define color names for other color slots which might be available for your terminal type, assuming terminfo mode. For instance: .sp .nf .ft C color.brightblue = 12 color.pink = 207 color.orange = 202 .ft P .fi .sp to set \(aqbrightblue\(aq to color slot 12 (useful for 16 color terminals that have brighter colors defined in the upper eight) and, \(aqpink\(aq and \(aqorange\(aq to colors in 256\-color xterm\(aqs default color cube. These defined colors may then be used as any of the pre\-defined eight, including appending \(aq_background\(aq to set the background to that color. .sp By default, the color extension will use ANSI mode (or win32 mode on Windows) if it detects a terminal. To override auto mode (to enable terminfo mode, for example), set the following configuration option: .sp .nf .ft C [color] mode = terminfo .ft P .fi .sp Any value other than \(aqansi\(aq, \(aqwin32\(aq, \(aqterminfo\(aq, or \(aqauto\(aq will disable color. .SS convert .sp import revisions from foreign VCS repositories into Mercurial .SS Commands .SS convert .sp .nf .ft C hg convert [OPTION]... SOURCE [DEST [REVMAP]] .ft P .fi .sp Accepted source formats [identifiers]: .INDENT 0.0 .IP \(bu 2 . Mercurial [hg] .IP \(bu 2 . CVS [cvs] .IP \(bu 2 . Darcs [darcs] .IP \(bu 2 . git [git] .IP \(bu 2 . Subversion [svn] .IP \(bu 2 . Monotone [mtn] .IP \(bu 2 . GNU Arch [gnuarch] .IP \(bu 2 . Bazaar [bzr] .IP \(bu 2 . Perforce [p4] .UNINDENT .sp Accepted destination formats [identifiers]: .INDENT 0.0 .IP \(bu 2 . Mercurial [hg] .IP \(bu 2 . Subversion [svn] (history on branches is not preserved) .UNINDENT .sp If no revision is given, all revisions will be converted. Otherwise, convert will only import up to the named revision (given in a format understood by the source). .sp If no destination directory name is specified, it defaults to the basename of the source with \fB\-hg\fP appended. If the destination repository doesn\(aqt exist, it will be created. .sp By default, all sources except Mercurial will use \-\-branchsort. Mercurial uses \-\-sourcesort to preserve original revision numbers order. Sort modes have the following effects: .INDENT 0.0 .TP .B \-\-branchsort . convert from parent to child revision when possible, which means branches are usually converted one after the other. It generates more compact repositories. .TP .B \-\-datesort . sort revisions by date. Converted repositories have good\-looking changelogs but are often an order of magnitude larger than the same ones generated by \-\-branchsort. .TP .B \-\-sourcesort . try to preserve source revisions order, only supported by Mercurial sources. .TP .B \-\-closesort . try to move closed revisions as close as possible to parent branches, only supported by Mercurial sources. .UNINDENT .sp If \fBREVMAP\fP isn\(aqt given, it will be put in a default location (\fB/.hg/shamap\fP by default). The \fBREVMAP\fP is a simple text file that maps each source commit ID to the destination ID for that revision, like so: .sp .nf .ft C .ft P .fi .sp If the file doesn\(aqt exist, it\(aqs automatically created. It\(aqs updated on each commit copied, so \%\fBhg convert\fP\: can be interrupted and can be run repeatedly to copy new commits. .sp The authormap is a simple text file that maps each source commit author to a destination commit author. It is handy for source SCMs that use unix logins to identify authors (e.g.: CVS). One line per author mapping and the line format is: .sp .nf .ft C source author = destination author .ft P .fi .sp Empty lines and lines starting with a \fB#\fP are ignored. .sp The filemap is a file that allows filtering and remapping of files and directories. Each line can contain one of the following directives: .sp .nf .ft C include path/to/file\-or\-dir exclude path/to/file\-or\-dir rename path/to/source path/to/destination .ft P .fi .sp Comment lines start with \fB#\fP. A specified path matches if it equals the full relative name of a file or one of its parent directories. The \fBinclude\fP or \fBexclude\fP directive with the longest matching path applies, so line order does not matter. .sp The \fBinclude\fP directive causes a file, or all files under a directory, to be included in the destination repository, and the exclusion of all other files and directories not explicitly included. The \fBexclude\fP directive causes files or directories to be omitted. The \fBrename\fP directive renames a file or directory if it is converted. To rename from a subdirectory into the root of the repository, use \fB.\fP as the path to rename to. .sp The splicemap is a file that allows insertion of synthetic history, letting you specify the parents of a revision. This is useful if you want to e.g. give a Subversion merge two parents, or graft two disconnected series of history together. Each entry contains a key, followed by a space, followed by one or two comma\-separated values: .sp .nf .ft C key parent1, parent2 .ft P .fi .sp The key is the revision ID in the source revision control system whose parents should be modified (same format as a key in .hg/shamap). The values are the revision IDs (in either the source or destination revision control system) that should be used as the new parents for that node. For example, if you have merged "release\-1.0" into "trunk", then you should specify the revision on "trunk" as the first parent and the one on the "release\-1.0" branch as the second. .sp The branchmap is a file that allows you to rename a branch when it is being brought in from whatever external repository. When used in conjunction with a splicemap, it allows for a powerful combination to help fix even the most badly mismanaged repositories and turn them into nicely structured Mercurial repositories. The branchmap contains lines of the form: .sp .nf .ft C original_branch_name new_branch_name .ft P .fi .sp where "original_branch_name" is the name of the branch in the source repository, and "new_branch_name" is the name of the branch is the destination repository. No whitespace is allowed in the branch names. This can be used to (for instance) move code in one repository from "default" to a named branch. .SS Mercurial Source .sp The Mercurial source recognizes the following configuration options, which you can set on the command line with \fB\-\-config\fP: .INDENT 0.0 .TP .B convert.hg.ignoreerrors . ignore integrity errors when reading. Use it to fix Mercurial repositories with missing revlogs, by converting from and to Mercurial. Default is False. .TP .B convert.hg.saverev . store original revision ID in changeset (forces target IDs to change). It takes a boolean argument and defaults to False. .TP .B convert.hg.revs . revset specifying the source revisions to convert. .UNINDENT .SS CVS Source .sp CVS source will use a sandbox (i.e. a checked\-out copy) from CVS to indicate the starting point of what will be converted. Direct access to the repository files is not needed, unless of course the repository is \fB:local:\fP. The conversion uses the top level directory in the sandbox to find the CVS repository, and then uses CVS rlog commands to find files to convert. This means that unless a filemap is given, all files under the starting directory will be converted, and that any directory reorganization in the CVS sandbox is ignored. .sp The following options can be used with \fB\-\-config\fP: .INDENT 0.0 .TP .B convert.cvsps.cache . Set to False to disable remote log caching, for testing and debugging purposes. Default is True. .TP .B convert.cvsps.fuzz . Specify the maximum time (in seconds) that is allowed between commits with identical user and log message in a single changeset. When very large files were checked in as part of a changeset then the default may not be long enough. The default is 60. .TP .B convert.cvsps.mergeto . Specify a regular expression to which commit log messages are matched. If a match occurs, then the conversion process will insert a dummy revision merging the branch on which this log message occurs to the branch indicated in the regex. Default is \fB{{mergetobranch ([\-\ew]+)}}\fP .TP .B convert.cvsps.mergefrom . Specify a regular expression to which commit log messages are matched. If a match occurs, then the conversion process will add the most recent revision on the branch indicated in the regex as the second parent of the changeset. Default is \fB{{mergefrombranch ([\-\ew]+)}}\fP .TP .B convert.localtimezone . use local time (as determined by the TZ environment variable) for changeset date/times. The default is False (use UTC). .TP .B hooks.cvslog . Specify a Python function to be called at the end of gathering the CVS log. The function is passed a list with the log entries, and can modify the entries in\-place, or add or delete them. .TP .B hooks.cvschangesets . Specify a Python function to be called after the changesets are calculated from the CVS log. The function is passed a list with the changeset entries, and can modify the changesets in\-place, or add or delete them. .UNINDENT .sp An additional "debugcvsps" Mercurial command allows the builtin changeset merging code to be run without doing a conversion. Its parameters and output are similar to that of cvsps 2.1. Please see the command help for more details. .SS Subversion Source .sp Subversion source detects classical trunk/branches/tags layouts. By default, the supplied \fBsvn://repo/path/\fP source URL is converted as a single branch. If \fBsvn://repo/path/trunk\fP exists it replaces the default branch. If \fBsvn://repo/path/branches\fP exists, its subdirectories are listed as possible branches. If \fBsvn://repo/path/tags\fP exists, it is looked for tags referencing converted branches. Default \fBtrunk\fP, \fBbranches\fP and \fBtags\fP values can be overridden with following options. Set them to paths relative to the source URL, or leave them blank to disable auto detection. .sp The following options can be set with \fB\-\-config\fP: .INDENT 0.0 .TP .B convert.svn.branches . specify the directory containing branches. The default is \fBbranches\fP. .TP .B convert.svn.tags . specify the directory containing tags. The default is \fBtags\fP. .TP .B convert.svn.trunk . specify the name of the trunk branch. The default is \fBtrunk\fP. .TP .B convert.localtimezone . use local time (as determined by the TZ environment variable) for changeset date/times. The default is False (use UTC). .UNINDENT .sp Source history can be retrieved starting at a specific revision, instead of being integrally converted. Only single branch conversions are supported. .INDENT 0.0 .TP .B convert.svn.startrev . specify start Subversion revision number. The default is 0. .UNINDENT .SS Perforce Source .sp The Perforce (P4) importer can be given a p4 depot path or a client specification as source. It will convert all files in the source to a flat Mercurial repository, ignoring labels, branches and integrations. Note that when a depot path is given you then usually should specify a target directory, because otherwise the target may be named \fB...\-hg\fP. .sp It is possible to limit the amount of source history to be converted by specifying an initial Perforce revision: .INDENT 0.0 .TP .B convert.p4.startrev . specify initial Perforce revision (a Perforce changelist number). .UNINDENT .SS Mercurial Destination .sp The following options are supported: .INDENT 0.0 .TP .B convert.hg.clonebranches . dispatch source branches in separate clones. The default is False. .TP .B convert.hg.tagsbranch . branch name for tag revisions, defaults to \fBdefault\fP. .TP .B convert.hg.usebranchnames . preserve branch names. The default is True. .UNINDENT .sp Options: .INDENT 0.0 .TP .B \-\-authors . username mapping filename (DEPRECATED, use \-\-authormap instead) .TP .B \-s, \-\-source\-type . source repository type .TP .B \-d, \-\-dest\-type . destination repository type .TP .B \-r, \-\-rev . import up to source revision REV .TP .B \-A, \-\-authormap . remap usernames using this file .TP .B \-\-filemap . remap file names using contents of file .TP .B \-\-splicemap . splice synthesized history into place .TP .B \-\-branchmap . change branch names while converting .TP .B \-\-branchsort . try to sort changesets by branches .TP .B \-\-datesort . try to sort changesets by date .TP .B \-\-sourcesort . preserve source changesets order .TP .B \-\-closesort . try to reorder closed revisions .UNINDENT .SS eol .sp automatically manage newlines in repository files .sp This extension allows you to manage the type of line endings (CRLF or LF) that are used in the repository and in the local working directory. That way you can get CRLF line endings on Windows and LF on Unix/Mac, thereby letting everybody use their OS native line endings. .sp The extension reads its configuration from a versioned \fB.hgeol\fP configuration file found in the root of the working copy. The \fB.hgeol\fP file use the same syntax as all other Mercurial configuration files. It uses two sections, \fB[patterns]\fP and \fB[repository]\fP. .sp The \fB[patterns]\fP section specifies how line endings should be converted between the working copy and the repository. The format is specified by a file pattern. The first match is used, so put more specific patterns first. The available line endings are \fBLF\fP, \fBCRLF\fP, and \fBBIN\fP. .sp Files with the declared format of \fBCRLF\fP or \fBLF\fP are always checked out and stored in the repository in that format and files declared to be binary (\fBBIN\fP) are left unchanged. Additionally, \fBnative\fP is an alias for checking out in the platform\(aqs default line ending: \fBLF\fP on Unix (including Mac OS X) and \fBCRLF\fP on Windows. Note that \fBBIN\fP (do nothing to line endings) is Mercurial\(aqs default behaviour; it is only needed if you need to override a later, more general pattern. .sp The optional \fB[repository]\fP section specifies the line endings to use for files stored in the repository. It has a single setting, \fBnative\fP, which determines the storage line endings for files declared as \fBnative\fP in the \fB[patterns]\fP section. It can be set to \fBLF\fP or \fBCRLF\fP. The default is \fBLF\fP. For example, this means that on Windows, files configured as \fBnative\fP (\fBCRLF\fP by default) will be converted to \fBLF\fP when stored in the repository. Files declared as \fBLF\fP, \fBCRLF\fP, or \fBBIN\fP in the \fB[patterns]\fP section are always stored as\-is in the repository. .sp Example versioned \fB.hgeol\fP file: .sp .nf .ft C [patterns] **.py = native **.vcproj = CRLF **.txt = native Makefile = LF **.jpg = BIN [repository] native = LF .ft P .fi .IP Note . The rules will first apply when files are touched in the working copy, e.g. by updating to null and back to tip to touch all files. .RE .sp The extension uses an optional \fB[eol]\fP section read from both the normal Mercurial configuration files and the \fB.hgeol\fP file, with the latter overriding the former. You can use that section to control the overall behavior. There are three settings: .INDENT 0.0 .IP \(bu 2 . \fBeol.native\fP (default \fBos.linesep\fP) can be set to \fBLF\fP or \fBCRLF\fP to override the default interpretation of \fBnative\fP for checkout. This can be used with \%\fBhg archive\fP\: on Unix, say, to generate an archive where files have line endings for Windows. .IP \(bu 2 . \fBeol.only\-consistent\fP (default True) can be set to False to make the extension convert files with inconsistent EOLs. Inconsistent means that there is both \fBCRLF\fP and \fBLF\fP present in the file. Such files are normally not touched under the assumption that they have mixed EOLs on purpose. .IP \(bu 2 . \fBeol.fix\-trailing\-newline\fP (default False) can be set to True to ensure that converted files end with a EOL character (either \fB\en\fP or \fB\er\en\fP as per the configured patterns). .UNINDENT .sp The extension provides \fBcleverencode:\fP and \fBcleverdecode:\fP filters like the deprecated win32text extension does. This means that you can disable win32text and enable eol and your filters will still work. You only need to these filters until you have prepared a \fB.hgeol\fP file. .sp The \fBwin32text.forbid*\fP hooks provided by the win32text extension have been unified into a single hook named \fBeol.checkheadshook\fP. The hook will lookup the expected line endings from the \fB.hgeol\fP file, which means you must migrate to a \fB.hgeol\fP file first before using the hook. \fBeol.checkheadshook\fP only checks heads, intermediate invalid revisions will be pushed. To forbid them completely, use the \fBeol.checkallhook\fP hook. These hooks are best used as \fBpretxnchangegroup\fP hooks. .sp See \%\fBhg help patterns\fP\: for more information about the glob patterns used. .SS extdiff .sp command to allow external programs to compare revisions .sp The extdiff Mercurial extension allows you to use external programs to compare revisions, or revision with working directory. The external diff programs are called with a configurable set of options and two non\-option arguments: paths to directories containing snapshots of files to compare. .sp The extdiff extension also allows you to configure new diff commands, so you do not need to type \%\fBhg extdiff \-p kdiff3\fP\: always. .sp .nf .ft C [extdiff] # add new command that runs GNU diff(1) in \(aqcontext diff\(aq mode cdiff = gdiff \-Nprc5 ## or the old way: #cmd.cdiff = gdiff #opts.cdiff = \-Nprc5 # add new command called vdiff, runs kdiff3 vdiff = kdiff3 # add new command called meld, runs meld (no need to name twice) meld = # add new command called vimdiff, runs gvimdiff with DirDiff plugin # (see http://www.vim.org/scripts/script.php?script_id=102) Non # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in # your .vimrc vimdiff = gvim \-f "+next" \e "+execute \(aqDirDiff\(aq fnameescape(argv(0)) fnameescape(argv(1))" .ft P .fi .sp Tool arguments can include variables that are expanded at runtime: .sp .nf .ft C $parent1, $plabel1 \- filename, descriptive label of first parent $child, $clabel \- filename, descriptive label of child revision $parent2, $plabel2 \- filename, descriptive label of second parent $root \- repository root $parent is an alias for $parent1. .ft P .fi .sp The extdiff extension will look in your [diff\-tools] and [merge\-tools] sections for diff tool arguments, when none are specified in [extdiff]. .sp .nf .ft C [extdiff] kdiff3 = [diff\-tools] kdiff3.diffargs=\-\-L1 \(aq$plabel1\(aq \-\-L2 \(aq$clabel\(aq $parent $child .ft P .fi .sp You can use \-I/\-X and list of file or directory names like normal \%\fBhg diff\fP\: command. The extdiff extension makes snapshots of only needed files, so running the external diff program will actually be pretty fast (at least faster than having to compare the entire tree). .SS Commands .SS extdiff .sp .nf .ft C hg extdiff [OPT]... [FILE]... .ft P .fi .sp Show differences between revisions for the specified files, using an external program. The default program used is diff, with default options "\-Npru". .sp To select a different program, use the \-p/\-\-program option. The program will be passed the names of two directories to compare. To pass additional options to the program, use \-o/\-\-option. These will be passed before the names of the directories to compare. .sp When two revision arguments are given, then changes are shown between those revisions. If only one revision is specified then that revision is compared to the working directory, and, when no revisions are specified, the working directory files are compared to its parent. .sp Options: .INDENT 0.0 .TP .B \-p, \-\-program . comparison program to run .TP .B \-o, \-\-option . pass option to comparison program .TP .B \-r, \-\-rev . revision .TP .B \-c, \-\-change . change made by revision .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .UNINDENT .SS factotum .sp http authentication with factotum .sp This extension allows the factotum(4) facility on Plan 9 from Bell Labs platforms to provide authentication information for HTTP access. Configuration entries specified in the auth section as well as authentication information provided in the repository URL are fully supported. If no prefix is specified, a value of "*" will be assumed. .sp By default, keys are specified as: .sp .nf .ft C proto=pass service=hg prefix= user= !password= .ft P .fi .sp If the factotum extension is unable to read the required key, one will be requested interactively. .sp A configuration section is available to customize runtime behavior. By default, these entries are: .sp .nf .ft C [factotum] executable = /bin/auth/factotum mountpoint = /mnt/factotum service = hg .ft P .fi .sp The executable entry defines the full path to the factotum binary. The mountpoint entry defines the path to the factotum file service. Lastly, the service entry controls the service name used when reading keys. .SS fetch .sp pull, update and merge in one command (DEPRECATED) .SS Commands .SS fetch .sp .nf .ft C hg fetch [SOURCE] .ft P .fi .sp This finds all changes from the repository at the specified path or URL and adds them to the local repository. .sp If the pulled changes add a new branch head, the head is automatically merged, and the result of the merge is committed. Otherwise, the working directory is updated to include the new changes. .sp When a merge is needed, the working directory is first updated to the newly pulled changes. Local changes are then merged into the pulled changes. To switch the merge order, use \-\-switch\-parent. .sp See \%\fBhg help dates\fP\: for a list of formats valid for \-d/\-\-date. .sp Returns 0 on success. .sp Options: .INDENT 0.0 .TP .B \-r, \-\-rev . a specific revision you would like to pull .TP .B \-e, \-\-edit . edit commit message .TP .B \-\-force\-editor . edit commit message (DEPRECATED) .TP .B \-\-switch\-parent . switch parents when merging .TP .B \-m, \-\-message . use text as commit message .TP .B \-l, \-\-logfile . read commit message from file .TP .B \-d, \-\-date . record the specified date as commit date .TP .B \-u, \-\-user . record the specified user as committer .TP .B \-e, \-\-ssh . specify ssh command to use .TP .B \-\-remotecmd . specify hg command to run on the remote side .TP .B \-\-insecure . do not verify server certificate (ignoring web.cacerts config) .UNINDENT .SS gpg .sp commands to sign and verify changesets .SS Commands .SS sigcheck .sp .nf .ft C hg sigcheck REV .ft P .fi .sp verify all the signatures there may be for a particular revision .SS sign .sp .nf .ft C hg sign [OPTION]... [REV]... .ft P .fi .sp If no revision is given, the parent of the working directory is used, or tip if no revision is checked out. .sp See \%\fBhg help dates\fP\: for a list of formats valid for \-d/\-\-date. .sp Options: .INDENT 0.0 .TP .B \-l, \-\-local . make the signature local .TP .B \-f, \-\-force . sign even if the sigfile is modified .TP .B \-\-no\-commit . do not commit the sigfile after signing .TP .B \-k, \-\-key . the key id to sign with .TP .B \-m, \-\-message . commit message .TP .B \-d, \-\-date . record the specified date as commit date .TP .B \-u, \-\-user . record the specified user as committer .UNINDENT .SS sigs .sp .nf .ft C hg sigs .ft P .fi .sp list signed changesets .SS graphlog .sp command to view revision graphs from a shell .sp This extension adds a \-\-graph option to the incoming, outgoing and log commands. When this options is given, an ASCII representation of the revision graph is also shown. .SS Commands .SS glog .sp .nf .ft C hg glog [OPTION]... [FILE] .ft P .fi .sp Print a revision history alongside a revision graph drawn with ASCII characters. .sp Nodes printed as an @ character are parents of the working directory. .sp Options: .INDENT 0.0 .TP .B \-f, \-\-follow . follow changeset history, or file history across copies and renames .TP .B \-\-follow\-first . only follow the first parent of merge changesets (DEPRECATED) .TP .B \-d, \-\-date . show revisions matching date spec .TP .B \-C, \-\-copies . show copied files .TP .B \-k, \-\-keyword . do case\-insensitive search for a given text .TP .B \-r, \-\-rev . show the specified revision or range .TP .B \-\-removed . include revisions where files were removed .TP .B \-m, \-\-only\-merges . show only merges (DEPRECATED) .TP .B \-u, \-\-user . revisions committed by user .TP .B \-\-only\-branch . show only changesets within the given named branch (DEPRECATED) .TP .B \-b, \-\-branch . show changesets within the given named branch .TP .B \-P, \-\-prune . do not display revision or any of its ancestors .TP .B \-p, \-\-patch . show patch .TP .B \-g, \-\-git . use git extended diff format .TP .B \-l, \-\-limit . limit number of changes displayed .TP .B \-M, \-\-no\-merges . do not show merges .TP .B \-\-stat . output diffstat\-style summary of changes .TP .B \-G, \-\-graph . show the revision DAG .TP .B \-\-style . display using template map file .TP .B \-\-template . display with template .TP .B \-I, \-\-include . include names matching the given patterns .TP .B \-X, \-\-exclude . exclude names matching the given patterns .UNINDENT .SS hgcia .sp hooks for integrating with the CIA.vc notification service .sp This is meant to be run as a changegroup or incoming hook. To configure it, set the following options in your hgrc: .sp .nf .ft C [cia] # your registered CIA user name user = foo # the name of the project in CIA project = foo # the module (subproject) (optional) #module = foo # Append a diffstat to the log message (optional) #diffstat = False # Template to use for log messages (optional) #template = {desc}\en{baseurl}{webroot}/rev/{node}\-\- {diffstat} # Style to use (optional) #style = foo # The URL of the CIA notification service (optional) # You can use mailto: URLs to send by email, e.g. # mailto:cia@cia.vc # Make sure to set email.from if you do this. #url = http://cia.vc/ # print message instead of sending it (optional) #test = False # number of slashes to strip for url paths #strip = 0 [hooks] # one of these: changegroup.cia = python:hgcia.hook #incoming.cia = python:hgcia.hook [web] # If you want hyperlinks (optional) baseurl = http://server/path/to/repo .ft P .fi .SS hgk .sp browse the repository in a graphical way .sp The hgk extension allows browsing the history of a repository in a graphical way. It requires Tcl/Tk version 8.4 or later. (Tcl/Tk is not distributed with Mercurial.) .sp hgk consists of two parts: a Tcl script that does the displaying and querying of information, and an extension to Mercurial named hgk.py, which provides hooks for hgk to get information. hgk can be found in the contrib directory, and the extension is shipped in the hgext repository, and needs to be enabled. .sp The \%\fBhg view\fP\: command will launch the hgk Tcl script. For this command to work, hgk must be in your search path. Alternately, you can specify the path to hgk in your configuration file: .sp .nf .ft C [hgk] path=/location/of/hgk .ft P .fi .sp hgk can make use of the extdiff extension to visualize revisions. Assuming you had already configured extdiff vdiff command, just add: .sp .nf .ft C [hgk] vdiff=vdiff .ft P .fi .sp Revisions context menu will now display additional entries to fire vdiff on hovered and selected revisions. .SS Commands .SS view .sp .nf .ft C hg view [\-l LIMIT] [REVRANGE] .ft P .fi .sp start interactive history viewer .sp Options: .INDENT 0.0 .TP .B \-l, \-\-limit . limit number of changes displayed .UNINDENT .SS highlight .sp syntax highlighting for hgweb (requires Pygments) .sp It depends on the Pygments syntax highlighting library: \%http://pygments.org/\: .sp There is a single configuration option: .sp .nf .ft C [web] pygments_style =

This is a prepackaged release of Mercurial for Mac OS X.



Please be sure to read the latest release notes.

mercurial-2.8.2/contrib/macosx/Readme.html0000644000175000017500000000350112261160437022332 0ustar oxymoronoxymoron00000000000000

Before you install


This is an OS X 10.6 version of Mercurial that depends on the default Python 2.6 installation.


After you install


This package installs the hg executable in /usr/local/bin and the Mercurial files in /Library/Python/2.6/site-packages/mercurial.


Documentation


Visit the Mercurial web site and wiki


There's also a free book, Distributed revision control with Mercurial


Reporting problems


If you run into any problems, please file a bug online:

http://mercurial.selenic.com/bts/

mercurial-2.8.2/contrib/macosx/macosx-build.txt0000644000175000017500000000042012261160437023374 0ustar oxymoronoxymoron00000000000000to build a new macosx binary package: install macpython from http://www.python.org/download/mac install py2app from http://pythonmac.org/packages/ make sure /usr/local/bin is in your path run bdist_mpkg in top-level hg directory find packaged stuff in dist directory mercurial-2.8.2/contrib/mergetools.hgrc0000644000175000017500000001157612261160437022015 0ustar oxymoronoxymoron00000000000000# Some default global settings for common merge tools [merge-tools] kdiff3.args=--auto --L1 base --L2 local --L3 other $base $local $other -o $output kdiff3.regkey=Software\KDiff3 kdiff3.regkeyalt=Software\Wow6432Node\KDiff3 kdiff3.regappend=\kdiff3.exe kdiff3.fixeol=True kdiff3.gui=True kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child gvimdiff.args=--nofork -d -g -O $local $other $base gvimdiff.regkey=Software\Vim\GVim gvimdiff.regkeyalt=Software\Wow6432Node\Vim\GVim gvimdiff.regname=path gvimdiff.priority=-9 vimdiff.args=$local $other $base -c 'redraw | echomsg "hg merge conflict, type \":cq\" to abort vimdiff"' vimdiff.check=changed vimdiff.priority=-10 merge.check=conflicts merge.priority=-100 gpyfm.gui=True meld.gui=True meld.args=--label='local' $local --label='merged' $base --label='other' $other -o $output meld.check=changed meld.diffargs=-a --label='$plabel1' $parent --label='$clabel' $child tkdiff.args=$local $other -a $base -o $output tkdiff.gui=True tkdiff.priority=-8 tkdiff.diffargs=-L '$plabel1' $parent -L '$clabel' $child xxdiff.args=--show-merged-pane --exit-with-merge-status --title1 local --title2 base --title3 other --merged-filename $output --merge $local $base $other xxdiff.gui=True xxdiff.priority=-8 xxdiff.diffargs=--title1 '$plabel1' $parent --title2 '$clabel' $child diffmerge.regkey=Software\SourceGear\SourceGear DiffMerge\ diffmerge.regkeyalt=Software\Wow6432Node\SourceGear\SourceGear DiffMerge\ diffmerge.regname=Location diffmerge.priority=-7 diffmerge.args=-nosplash -merge -title1=local -title2=merged -title3=other $local $base $other -result=$output diffmerge.check=changed diffmerge.gui=True diffmerge.diffargs=--nosplash --title1='$plabel1' --title2='$clabel' $parent $child p4merge.args=$base $local $other $output p4merge.regkey=Software\Perforce\Environment p4merge.regkeyalt=Software\Wow6432Node\Perforce\Environment p4merge.regname=P4INSTROOT p4merge.regappend=\p4merge.exe p4merge.gui=True p4merge.priority=-8 p4merge.diffargs=$parent $child tortoisemerge.args=/base:$base /mine:$local /theirs:$other /merged:$output tortoisemerge.regkey=Software\TortoiseSVN tortoisemerge.regkeyalt=Software\Wow6432Node\TortoiseSVN tortoisemerge.check=changed tortoisemerge.gui=True tortoisemerge.priority=-8 tortoisemerge.diffargs=/base:$parent /mine:$child /basename:'$plabel1' /minename:'$clabel' ecmerge.args=$base $local $other --mode=merge3 --title0=base --title1=local --title2=other --to=$output ecmerge.regkey=Software\Elli\xc3\xa9 Computing\Merge ecmerge.regkeyalt=Software\Wow6432Node\Elli\xc3\xa9 Computing\Merge ecmerge.gui=True ecmerge.diffargs=$parent $child --mode=diff2 --title1='$plabel1' --title2='$clabel' filemerge.executable=/Developer/Applications/Utilities/FileMerge.app/Contents/MacOS/FileMerge filemerge.args=-left $other -right $local -ancestor $base -merge $output filemerge.gui=True ; Windows version of Beyond Compare beyondcompare3.args=$local $other $base $output /ro /lefttitle=local /centertitle=base /righttitle=other /automerge /reviewconflicts /solo beyondcompare3.regkey=Software\Scooter Software\Beyond Compare 3 beyondcompare3.regname=ExePath beyondcompare3.gui=True beyondcompare3.priority=-2 beyondcompare3.diffargs=/lro /lefttitle='$plabel1' /righttitle='$clabel' /solo /expandall $parent $child ; Linux version of Beyond Compare bcompare.args=$local $other $base -mergeoutput=$output -ro -lefttitle=parent1 -centertitle=base -righttitle=parent2 -outputtitle=merged -automerge -reviewconflicts -solo bcompare.premerge=False bcompare.gui=True bcompare.priority=-1 bcompare.diffargs=-lro -lefttitle='$plabel1' -righttitle='$clabel' -solo -expandall $parent $child winmerge.args=/e /x /wl /ub /dl other /dr local $other $local $output winmerge.regkey=Software\Thingamahoochie\WinMerge winmerge.regkeyalt=Software\Wow6432Node\Thingamahoochie\WinMerge\ winmerge.regname=Executable winmerge.check=changed winmerge.gui=True winmerge.priority=-10 winmerge.diffargs=/r /e /x /ub /wl /dl '$plabel1' /dr '$clabel' $parent $child araxis.regkey=SOFTWARE\Classes\TypeLib\{46799e0a-7bd1-4330-911c-9660bb964ea2}\7.0\HELPDIR araxis.regappend=\ConsoleCompare.exe araxis.priority=-2 araxis.args=/3 /a2 /wait /merge /title1:"Other" /title2:"Base" /title3:"Local :"$local $other $base $local $output araxis.premerge=False araxis.checkconflict=True araxis.binary=True araxis.gui=True araxis.diffargs=/2 /wait /title1:"$plabel1" /title2:"$clabel" $parent $child diffuse.priority=-3 diffuse.args=$local $base $other diffuse.gui=True diffuse.diffargs=$parent $child UltraCompare.regkey=Software\Microsoft\Windows\CurrentVersion\App Paths\UC.exe UltraCompare.regkeyalt=Software\Wow6432Node\Microsoft\Windows\CurrentVersion\App Paths\UC.exe UltraCompare.args = $base $local $other -title1 base -title3 other UltraCompare.priority = -2 UltraCompare.gui = True UltraCompare.binary = True UltraCompare.check = conflicts,changed UltraCompare.diffargs=$child $parent -title1 $clabel -title2 $plabel1 mercurial-2.8.2/contrib/python-hook-examples.py0000644000175000017500000000110212261160437023415 0ustar oxymoronoxymoron00000000000000''' Examples of useful python hooks for Mercurial. ''' from mercurial import patch, util def diffstat(ui, repo, **kwargs): '''Example usage: [hooks] commit.diffstat = python:/path/to/this/file.py:diffstat changegroup.diffstat = python:/path/to/this/file.py:diffstat ''' if kwargs.get('parent2'): return node = kwargs['node'] first = repo[node].p1().node() if 'url' in kwargs: last = repo['tip'].node() else: last = node diff = patch.diff(repo, first, last) ui.write(patch.diffstat(util.iterlines(diff))) mercurial-2.8.2/contrib/win32/0000755000175000017500000000000012261160455017720 5ustar oxymoronoxymoron00000000000000mercurial-2.8.2/contrib/win32/mercurial.ini0000644000175000017500000000453712261160437022415 0ustar oxymoronoxymoron00000000000000; System-wide Mercurial config file. ; ; !!! Do Not Edit This File !!! ; ; This file will be replaced by the installer on every upgrade. ; Editing this file can cause strange side effects on Vista. ; ; http://bitbucket.org/tortoisehg/stable/issue/135 ; ; To change settings you see in this file, override (or enable) them in ; your user Mercurial.ini file, where USERNAME is your Windows user name: ; ; XP or older - C:\Documents and Settings\USERNAME\Mercurial.ini ; Vista or later - C:\Users\USERNAME\Mercurial.ini [ui] ; editor used to enter commit logs, etc. Most text editors will work. editor = notepad ; show changed files and be a bit more verbose if True ; verbose = True ; username data to appear in commits ; it usually takes the form: Joe User ; username = Joe User ; In order to push/pull over ssh you must specify an ssh tool ;ssh = "C:\Progra~1\TortoiseSVN\bin\TortoisePlink.exe" -ssh -2 ;ssh = C:\cygwin\bin\ssh ; ; For more information about mercurial extensions, start here ; http://www.selenic.com/mercurial/wiki/index.cgi/UsingExtensions ; ; Extensions shipped with Mercurial ; [extensions] ;acl = ;blackbox = ;bugzilla = ;children = ;churn = ;color = ;convert = ;eol = ;extdiff = ;fetch = ;gpg = ;hgcia = ;hgk = ;highlight = ;histedit = ;interhg = ;largefiles = ;keyword = ;mq = ;notify = ;pager = ;patchbomb = ;progress = ;purge = ;rebase = ;record = ;relink = ;schemes = ;share = ;shelve = ;transplant = ;win32mbcs = ;zeroconf = ; ; Define external diff commands ; [extdiff] ;cmd.bc3diff = C:\Program Files\Beyond Compare 3\BCompare.exe ;cmd.vdiff = C:\Progra~1\TortoiseSVN\bin\TortoiseMerge.exe ;cmd.vimdiff = gvim.exe ;opts.vimdiff = -f "+next" "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))" [hgk] ; Replace the following with your path to hgk, uncomment it and ; install ActiveTcl (or another win32 port like tclkit) ; path="C:\Program Files\Mercurial\Contrib\hgk.tcl" ; vdiff=vdiff ; ; The git extended diff format can represent binary files, file ; permission changes, and rename information that the normal patch format ; cannot describe. However it is also not compatible with tools which ; expect normal patches. so enable git patches at your own risk. ; [diff] ;git = false ;nodates = false mercurial-2.8.2/contrib/win32/hgwebdir_wsgi.py0000644000175000017500000000634312261160437023124 0ustar oxymoronoxymoron00000000000000# An example WSGI script for IIS/isapi-wsgi to export multiple hgweb repos # Copyright 2010 Sune Foldager # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. # # Requirements: # - Python 2.6 # - PyWin32 build 214 or newer # - Mercurial installed from source (python setup.py install) # - IIS 7 # # Earlier versions will in general work as well, but the PyWin32 version is # necessary for win32traceutil to work correctly. # # # Installation and use: # # - Download the isapi-wsgi source and run python setup.py install: # http://code.google.com/p/isapi-wsgi/ # # - Run this script (i.e. python hgwebdir_wsgi.py) to get a shim dll. The # shim is identical for all scripts, so you can just copy and rename one # from an earlier run, if you wish. # # - Setup an IIS application where your hgwebdir is to be served from. # On 64-bit systems, make sure it's assigned a 32-bit app pool. # # - In the application, setup a wildcard script handler mapping of type # IsapiModule with the shim dll as its executable. This file MUST reside # in the same directory as the shim. Remove all other handlers, if you wish. # # - Make sure the ISAPI and CGI restrictions (configured globally on the # web server) includes the shim dll, to allow it to run. # # - Adjust the configuration variables below to match your needs. # # Configuration file location hgweb_config = r'c:\src\iis\hg\hgweb.config' # Global settings for IIS path translation path_strip = 0 # Strip this many path elements off (when using url rewrite) path_prefix = 1 # This many path elements are prefixes (depends on the # virtual path of the IIS application). import sys # Adjust python path if this is not a system-wide install #sys.path.insert(0, r'c:\path\to\python\lib') # Enable tracing. Run 'python -m win32traceutil' to debug if getattr(sys, 'isapidllhandle', None) is not None: import win32traceutil # To serve pages in local charset instead of UTF-8, remove the two lines below import os os.environ['HGENCODING'] = 'UTF-8' import isapi_wsgi from mercurial import demandimport; demandimport.enable() from mercurial.hgweb.hgwebdir_mod import hgwebdir # Example tweak: Replace isapi_wsgi's handler to provide better error message # Other stuff could also be done here, like logging errors etc. class WsgiHandler(isapi_wsgi.IsapiWsgiHandler): error_status = '500 Internal Server Error' # less silly error message isapi_wsgi.IsapiWsgiHandler = WsgiHandler # Only create the hgwebdir instance once application = hgwebdir(hgweb_config) def handler(environ, start_response): # Translate IIS's weird URLs url = environ['SCRIPT_NAME'] + environ['PATH_INFO'] paths = url[1:].split('/')[path_strip:] script_name = '/' + '/'.join(paths[:path_prefix]) path_info = '/'.join(paths[path_prefix:]) if path_info: path_info = '/' + path_info environ['SCRIPT_NAME'] = script_name environ['PATH_INFO'] = path_info return application(environ, start_response) def __ExtensionFactory__(): return isapi_wsgi.ISAPISimpleHandler(handler) if __name__=='__main__': from isapi.install import * params = ISAPIParameters() HandleCommandLine(params) mercurial-2.8.2/contrib/win32/mercurial.iss0000644000175000017500000001240412261160437022424 0ustar oxymoronoxymoron00000000000000; Script generated by the Inno Setup Script Wizard. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #ifndef VERSION #define FileHandle #define FileLine #define VERSION = "unknown" #if FileHandle = FileOpen(SourcePath + "\..\..\mercurial\__version__.py") #expr FileLine = FileRead(FileHandle) #expr FileLine = FileRead(FileHandle) #define VERSION = Copy(FileLine, Pos('"', FileLine)+1, Len(FileLine)-Pos('"', FileLine)-1) #endif #if FileHandle #expr FileClose(FileHandle) #endif #pragma message "Detected Version: " + VERSION #endif #ifndef ARCH #define ARCH = "x86" #endif [Setup] AppCopyright=Copyright 2005-2010 Matt Mackall and others AppName=Mercurial #if ARCH == "x64" AppVerName=Mercurial {#VERSION} (64-bit) OutputBaseFilename=Mercurial-{#VERSION}-x64 ArchitecturesAllowed=x64 ArchitecturesInstallIn64BitMode=x64 #else AppVerName=Mercurial {#VERSION} OutputBaseFilename=Mercurial-{#VERSION} #endif InfoAfterFile=contrib/win32/postinstall.txt LicenseFile=COPYING ShowLanguageDialog=yes AppPublisher=Matt Mackall and others AppPublisherURL=http://mercurial.selenic.com/ AppSupportURL=http://mercurial.selenic.com/ AppUpdatesURL=http://mercurial.selenic.com/ AppID={{4B95A5F1-EF59-4B08-BED8-C891C46121B3} AppContact=mercurial@selenic.com DefaultDirName={pf}\Mercurial SourceDir=..\.. VersionInfoDescription=Mercurial distributed SCM (version {#VERSION}) VersionInfoCopyright=Copyright 2005-2010 Matt Mackall and others VersionInfoCompany=Matt Mackall and others InternalCompressLevel=max SolidCompression=true SetupIconFile=contrib\win32\mercurial.ico AllowNoIcons=true DefaultGroupName=Mercurial PrivilegesRequired=none [Files] Source: contrib\mercurial.el; DestDir: {app}/Contrib Source: contrib\vim\*.*; DestDir: {app}/Contrib/Vim Source: contrib\zsh_completion; DestDir: {app}/Contrib Source: contrib\bash_completion; DestDir: {app}/Contrib Source: contrib\tcsh_completion; DestDir: {app}/Contrib Source: contrib\tcsh_completion_build.sh; DestDir: {app}/Contrib Source: contrib\hgk; DestDir: {app}/Contrib; DestName: hgk.tcl Source: contrib\xml.rnc; DestDir: {app}/Contrib Source: contrib\mercurial.el; DestDir: {app}/Contrib Source: contrib\mq.el; DestDir: {app}/Contrib Source: contrib\hgweb.fcgi; DestDir: {app}/Contrib Source: contrib\hgweb.wsgi; DestDir: {app}/Contrib Source: contrib\win32\ReadMe.html; DestDir: {app}; Flags: isreadme Source: contrib\mergetools.hgrc; DestDir: {tmp}; Source: contrib\win32\mercurial.ini; DestDir: {app}; DestName: Mercurial.ini; Check: CheckFile; AfterInstall: ConcatenateFiles; Source: contrib\win32\postinstall.txt; DestDir: {app}; DestName: ReleaseNotes.txt Source: dist\hg.exe; DestDir: {app}; AfterInstall: Touch('{app}\hg.exe.local') #if ARCH == "x64" Source: dist\*.dll; Destdir: {app} Source: dist\*.pyd; Destdir: {app} #else Source: dist\python*.dll; Destdir: {app}; Flags: skipifsourcedoesntexist Source: dist\msvc*.dll; DestDir: {app}; Flags: skipifsourcedoesntexist Source: dist\w9xpopen.exe; DestDir: {app} #endif Source: dist\Microsoft.VC*.CRT.manifest; DestDir: {app}; Flags: skipifsourcedoesntexist Source: dist\library.zip; DestDir: {app} Source: dist\add_path.exe; DestDir: {app} Source: dist\cacert.pem; Destdir: {app} Source: doc\*.html; DestDir: {app}\Docs Source: doc\style.css; DestDir: {app}\Docs Source: mercurial\help\*.txt; DestDir: {app}\help Source: mercurial\locale\*.*; DestDir: {app}\locale; Flags: recursesubdirs createallsubdirs skipifsourcedoesntexist Source: mercurial\templates\*.*; DestDir: {app}\Templates; Flags: recursesubdirs createallsubdirs Source: CONTRIBUTORS; DestDir: {app}; DestName: Contributors.txt Source: COPYING; DestDir: {app}; DestName: Copying.txt [INI] Filename: {app}\Mercurial.url; Section: InternetShortcut; Key: URL; String: http://mercurial.selenic.com/ Filename: {app}\Mercurial.ini; Section: web; Key: cacerts; String: {app}\cacert.pem [UninstallDelete] Type: files; Name: {app}\Mercurial.url [Icons] Name: {group}\Uninstall Mercurial; Filename: {uninstallexe} Name: {group}\Mercurial Command Reference; Filename: {app}\Docs\hg.1.html Name: {group}\Mercurial Configuration Files; Filename: {app}\Docs\hgrc.5.html Name: {group}\Mercurial Ignore Files; Filename: {app}\Docs\hgignore.5.html Name: {group}\Mercurial Web Site; Filename: {app}\Mercurial.url [Run] Filename: "{app}\add_path.exe"; Parameters: "{app}"; Flags: postinstall; Description: "Add the installation path to the search path" [UninstallRun] Filename: "{app}\add_path.exe"; Parameters: "/del {app}" [UninstallDelete] Type: files; Name: "{app}\hg.exe.local" [Code] var WriteFile: Boolean; CheckDone: Boolean; function CheckFile(): Boolean; begin if not CheckDone then begin WriteFile := True; if FileExists(ExpandConstant(CurrentFileName)) then begin WriteFile := MsgBox('' + ExpandConstant(CurrentFileName) + '' #13#13 'The file already exists.' #13#13 'Would you like Setup to overwrite it?', mbConfirmation, MB_YESNO) = idYes; end; CheckDone := True; end; Result := WriteFile; end; procedure ConcatenateFiles(); var MergeConfigs: TArrayOfString; begin if LoadStringsFromFile(ExpandConstant('{tmp}\mergetools.hgrc'),MergeConfigs) then begin SaveStringsToFile(ExpandConstant(CurrentFileName),MergeConfigs,True); end; end; procedure Touch(fn: String); begin SaveStringToFile(ExpandConstant(fn), '', False); end; mercurial-2.8.2/contrib/win32/mercurial.ico0000644000175000017500000000427612261160437022410 0ustar oxymoronoxymoron00000000000000 ¨( @!!!%%%'''(((***,,,///000111222777999===>>>???AAAGGGJJJNNNOOOQQQSSSTTTWWWXXXZZZ[[[]]]___aaabbbcccdddeeehhhiiilllppprrrsssuuuvuvvvvxxxzzz{{{|||}}}~~~~€€€ƒƒƒ‰ˆ‰‰‰‰Š‰ŠŠŠŠ‹‹‹ŒŒŒŽŽŽ’‘’’’’“““”””•••———˜˜˜™™™ššš›››œœœžžžŸžŸŸŸŸ   ¡¡¡£££¤¤¤¥¥¥¦¦¦§¦§§§§¨¨¨©©©ªªª«««¬¬¬­­­®®®¯¯¯°°°±±±²²²³²³³³³´´´µµµ¶¶¶···¸¸¸¹¸¹¹¹¹ººº¼¼¼½½½¾¾¾¿¿¿ÀÀÀÁÀÁÁÁÁÃÃÃÄÄÄÅÅÅÆÆÆÇÇÇÊÊÊËËËÌÌÌÍÍÍÎÎÎÏÏÏÐÐÐÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÛÛÛÝÝÝÞÞÞàààáááäääæææçççèèèéééêêêëëëìììîîîïïïðððñññòñòòòòóóóôôôõõõööö÷ö÷ùùùúúúûûûüüüýýýþþþ®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®œI5„«®®®®®®®®®®®®®®¥m5>{ª®®®®£@~‚f3Œ®®®®®®®®®®®®ˆ(47ž®®®€v ]`U­®®®®®®®®®šNOaxyR02«®®‹xhphS0qL­®®®®®®®®ZaQQ£™i)u®® uf¤£šZ nW®®®®®®®®HRQQQj™’B ®®®sY¤¤¤“]V…®®®®®®®UQQQQQbž^ ®®®˜`q£¤¤¤‘Q';¨®®®®®®lTQQQQQ}] ®®®­iQv ¤¤¤:DP®®®®®®›dQQQQQQL®®®®ŠZQ\wš¡h < ®®®®®®Š\QQQQU|­®®®®hQQQQY•”N'`®®®®®®®“ka^d‡¬®®®®®ŸrQQQQQZ›w @©®®®®®®®¬¥¤ª®®®®®®®‰xXQQQQQp—M ™®®®®®®®®®®®®®®®®®¥6x[QQQQQQ“~y®¥[C‡®®®®®®®®®®¬|($wXQQQQQQn’/Oš,/*B®®®®®®­Q!Xp+oQQQQQQQR˜Q2K\S~(®®®®ƒ&<89`TQQQQQQQQ†r.AQT £rV®®«="?`ZQQQQQQQQQQqt4eQQpŽd`®¬1MbTQQQQQQQQQQQQQYgL£ZQQQb¥®_%jSQQQQQQQQQQQQQQQQ[z®Ÿqcu¤®¢#kQQQQQQQQQQQQQQQQQQ`›®®®­®®®yjSQQQQQQQQQQQQQQQQQQf¬®®®®®®®G?dQQQQQQQQQQQQQQQQQQQME®®®®®®®®JwYQQQQQQQQQQQQQQQQQQY%Ÿ®®®®®®®®hxTQQQQQQQQQQQQQQQQQQU?®®®®®®®®®‡yXQQQQQQQQQQQQQQQQQY-¥®®®®®®®®®¦wbQQQQQQQQQQQQQQQQRFƒ®®®®®®®®®®®uRQQQQQQQQQQQQQQQSd®®®®®®®®®®®®­‚kQQQQQQQQQQQQQSm€®®®®®®®®®®®®®®­‡oVQQQQQQQQQQ[r—®®®®®®®®®®®®®®®®®ž~j\TQQQQVaoª®®®®®®®®®®®®®®®®®®®®§–‰yrr}Žœ«®®®®®®®®®mercurial-2.8.2/contrib/win32/hg.bat0000644000175000017500000000070312261160437021006 0ustar oxymoronoxymoron00000000000000@echo off rem Windows Driver script for Mercurial setlocal set HG=%~f0 rem Use a full path to Python (relative to this script) if it exists, rem as the standard Python install does not put python.exe on the PATH... rem Otherwise, expect that python.exe can be found on the PATH. rem %~dp0 is the directory of this script if exist "%~dp0..\python.exe" ( "%~dp0..\python" "%~dp0hg" %* ) else ( python "%~dp0hg" %* ) endlocal mercurial-2.8.2/contrib/win32/buildlocal.bat0000644000175000017500000000041312261160437022520 0ustar oxymoronoxymoron00000000000000@echo off rem Double-click this file to (re)build Mercurial for Windows in place. rem Useful for testing and development. cd ..\.. del /Q mercurial\*.pyd del /Q mercurial\*.pyc rmdir /Q /S mercurial\locale python setup.py build_py -c -d . build_ext -i build_mo pause mercurial-2.8.2/contrib/win32/postinstall.txt0000644000175000017500000000037312261160437023040 0ustar oxymoronoxymoron00000000000000Welcome to Mercurial for Windows! --------------------------------- For configuration and usage directions, please read the ReadMe.html file that comes with this package. Also check the release notes at: http://mercurial.selenic.com/wiki/WhatsNew mercurial-2.8.2/contrib/win32/win32-build.txt0000644000175000017500000001155212261160437022524 0ustar oxymoronoxymoron00000000000000The standalone Windows installer for Mercurial is built in a somewhat jury-rigged fashion. It has the following prerequisites. Ensure to take the packages matching the mercurial version you want to build (32-bit or 64-bit). Python 2.6 for Windows http://www.python.org/download/releases/ A compiler: either MinGW http://www.mingw.org/ or Microsoft Visual C++ 2008 SP1 Express Edition http://www.microsoft.com/express/Downloads/Download-2008.aspx Python for Windows Extensions http://sourceforge.net/projects/pywin32/ mfc71.dll (just download, don't install; not needed for Python 2.6) http://starship.python.net/crew/mhammond/win32/ Visual C++ 2008 redistributable package (needed for >= Python 2.6 or if you compile with MSVC) for 32-bit: http://www.microsoft.com/downloads/details.aspx?FamilyID=9b2da534-3e03-4391-8a4d-074b9f2bc1bf for 64-bit: http://www.microsoft.com/downloads/details.aspx?familyid=bd2a6171-e2d6-4230-b809-9a8d7548c1b6 The py2exe distutils extension http://sourceforge.net/projects/py2exe/ GnuWin32 gettext utility (if you want to build translations) http://gnuwin32.sourceforge.net/packages/gettext.htm Inno Setup http://www.jrsoftware.org/isdl.php#qsp Get and install ispack-5.3.10.exe or later (includes Inno Setup Processor), which is necessary to package Mercurial. ISTool - optional http://www.istool.org/default.aspx/ add_path (you need only add_path.exe in the zip file) http://www.barisione.org/apps.html#add_path Docutils http://docutils.sourceforge.net/ CA Certs file http://curl.haxx.se/ca/cacert.pem And, of course, Mercurial itself. Once you have all this installed and built, clone a copy of the Mercurial repository you want to package, and name the repo C:\hg\hg-release. In a shell, build a standalone copy of the hg.exe program. Building instructions for MinGW: python setup.py build -c mingw32 python setup.py py2exe -b 2 Note: the previously suggested combined command of "python setup.py build -c mingw32 py2exe -b 2" doesn't work correctly anymore as it doesn't include the extensions in the mercurial subdirectory. If you want to create a file named setup.cfg with the contents: [build] compiler=mingw32 you can skip the first build step. Building instructions with MSVC 2008 Express Edition: for 32-bit: "C:\Program Files\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" x86 python setup.py py2exe -b 2 for 64-bit: "C:\Program Files\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" x86_amd64 python setup.py py2exe -b 3 Copy add_path.exe and cacert.pem files into the dist directory that just got created. If you are using Python up to version 2.5.4, copy mfc71.dll into the dist directory that just got created. If you are using Python 2.6 or later, or if you are using MSVC 2008 to compile mercurial, you must include the C runtime libraries in the installer. To do so, install the Visual C++ 2008 redistributable package. Then in your windows\winsxs folder, locate the folder containing the dlls version 9.0.21022.8. For x86, it should be named like x86_Microsoft.VC90.CRT_(...)_9.0.21022.8(...). For x64, it should be named like amd64_Microsoft.VC90.CRT_(...)_9.0.21022.8(...). Copy the files named msvcm90.dll, msvcp90.dll and msvcr90.dll into the dist directory. Then in the windows\winsxs\manifests folder, locate the corresponding manifest file (x86_Microsoft.VC90.CRT_(...)_9.0.21022.8(...).manifest for x86, amd64_Microsoft.VC90.CRT_(...)_9.0.21022.8(...).manifest for x64), copy it in the dist directory and rename it to Microsoft.VC90.CRT.manifest. Before building the installer, you have to build Mercurial HTML documentation (or fix mercurial.iss to not reference the doc directory): cd doc mingw32-make html cd .. If you use ISTool, you open the C:\hg\hg-release\contrib\win32\mercurial.iss file and type Ctrl-F9 to compile the installer file. Otherwise you run the Inno Setup compiler. Assuming it's in the path you should execute: iscc contrib\win32\mercurial.iss /dVERSION=foo Where 'foo' is the version number you would like to see in the 'Add/Remove Applications' tool. The installer will be placed into a directory named Output/ at the root of your repository. If the /dVERSION=foo parameter is not given in the command line, the installer will retrieve the version information from the __version__.py file. If you want to build an installer for a 64-bit mercurial, add /dARCH=x64 to your command line: iscc contrib\win32\mercurial.iss /dARCH=x64 To automate the steps above you may want to create a batchfile based on the following (MinGW build chain): echo [build] > setup.cfg echo compiler=mingw32 >> setup.cfg python setup.py py2exe -b 2 cd doc mingw32-make html cd .. iscc contrib\win32\mercurial.iss /dVERSION=snapshot and run it from the root of the hg repository (c:\hg\hg-release). mercurial-2.8.2/contrib/win32/ReadMe.html0000644000175000017500000001120112261160437021736 0ustar oxymoronoxymoron00000000000000 Mercurial for Windows

Mercurial for Windows

Welcome to Mercurial for Windows!

Mercurial is a command-line application. You must run it from the Windows command prompt (or if you're hard core, a MinGW shell).

Note: the standard MinGW msys startup script uses rxvt which has problems setting up standard input and output. Running bash directly works correctly.

For documentation, please visit the Mercurial web site. You can also download a free book, Mercurial: The Definitive Guide.

By default, Mercurial installs to C:\Program Files\Mercurial. The Mercurial command is called hg.exe.

Testing Mercurial after you've installed it

The easiest way to check that Mercurial is installed properly is to just type the following at the command prompt:

hg

This command should print a useful help message. If it does, other Mercurial commands should work fine for you.

Configuration notes

Default editor

The default editor for commit messages is 'notepad'. You can set the EDITOR (or HGEDITOR) environment variable to specify your preference or set it in mercurial.ini:

[ui]
editor = whatever

Configuring a Merge program

It should be emphasized that Mercurial by itself doesn't attempt to do a Merge at the file level, neither does it make any attempt to Resolve the conflicts.

By default, Mercurial will use the merge program defined by the HGMERGE environment variable, or uses the one defined in the mercurial.ini file. (see MergeProgram on the Mercurial Wiki for more information)

Reporting problems

Before you report any problems, please consult the Mercurial web site and see if your question is already in our list of Frequently Answered Questions (the "FAQ").

If you cannot find an answer to your question, please feel free to send mail to the Mercurial mailing list, at mercurial@selenic.com. Remember, the more useful information you include in your report, the easier it will be for us to help you!

If you are IRC-savvy, that's usually the fastest way to get help. Go to #mercurial on irc.freenode.net.

Author and copyright information

Mercurial was written by Matt Mackall, and is maintained by Matt and a team of volunteers.

The Windows installer was written by Bryan O'Sullivan.

Mercurial is Copyright 2005-2013 Matt Mackall and others. See the Contributors.txt file for a list of contributors.

Mercurial is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or any later version.

Mercurial is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. See the GNU General Public License for more details.

mercurial-2.8.2/contrib/simplemerge0000755000175000017500000000413412261160437021217 0ustar oxymoronoxymoron00000000000000#!/usr/bin/env python from mercurial import demandimport demandimport.enable() import sys from mercurial.i18n import _ from mercurial import simplemerge, fancyopts, util, ui options = [('L', 'label', [], _('labels to use on conflict markers')), ('a', 'text', None, _('treat all files as text')), ('p', 'print', None, _('print results instead of overwriting LOCAL')), ('', 'no-minimal', None, _('do not try to minimize conflict regions')), ('h', 'help', None, _('display help and exit')), ('q', 'quiet', None, _('suppress output'))] usage = _('''simplemerge [OPTS] LOCAL BASE OTHER Simple three-way file merge utility with a minimal feature set. Apply to LOCAL the changes necessary to go from BASE to OTHER. By default, LOCAL is overwritten with the results of this operation. ''') class ParseError(Exception): """Exception raised on errors in parsing the command line.""" def showhelp(): sys.stdout.write(usage) sys.stdout.write('\noptions:\n') out_opts = [] for shortopt, longopt, default, desc in options: out_opts.append(('%2s%s' % (shortopt and '-%s' % shortopt, longopt and ' --%s' % longopt), '%s' % desc)) opts_len = max([len(opt[0]) for opt in out_opts]) for first, second in out_opts: sys.stdout.write(' %-*s %s\n' % (opts_len, first, second)) try: for fp in (sys.stdin, sys.stdout, sys.stderr): util.setbinary(fp) opts = {} try: args = fancyopts.fancyopts(sys.argv[1:], options, opts) except fancyopts.getopt.GetoptError, e: raise ParseError(e) if opts['help']: showhelp() sys.exit(0) if len(args) != 3: raise ParseError(_('wrong number of arguments')) sys.exit(simplemerge.simplemerge(ui.ui(), *args, **opts)) except ParseError, e: sys.stdout.write("%s: %s\n" % (sys.argv[0], e)) showhelp() sys.exit(1) except util.Abort, e: sys.stderr.write("abort: %s\n" % e) sys.exit(255) except KeyboardInterrupt: sys.exit(255) mercurial-2.8.2/contrib/hgk0000755000175000017500000033372212261160437017467 0ustar oxymoronoxymoron00000000000000#!/usr/bin/env wish # Copyright (C) 2005 Paul Mackerras. All rights reserved. # This program is free software; it may be used, copied, modified # and distributed under the terms of the GNU General Public Licence, # either version 2, or (at your option) any later version. # # See hgk.py for extension usage and configuration. # Modified version of Tip 171: # http://www.tcl.tk/cgi-bin/tct/tip/171.html # # The in_mousewheel global was added to fix strange reentrancy issues. # The whole snipped is activated only under windows, mouse wheel # bindings working already under MacOSX and Linux. if {[catch {package require Ttk}]} { # use a shim namespace eval ttk { proc style args {} proc entry args { eval [linsert $args 0 ::entry] -relief flat } } interp alias {} ttk::button {} button interp alias {} ttk::frame {} frame interp alias {} ttk::label {} label interp alias {} ttk::scrollbar {} scrollbar interp alias {} ttk::optionMenu {} tk_optionMenu proc updatepalette {} {} } else { proc ::ttk::optionMenu {w varName firstValue args} { upvar #0 $varName var if {![info exists var]} { set var $firstValue } ttk::menubutton $w -textvariable $varName -menu $w.menu \ -direction flush menu $w.menu -tearoff 0 $w.menu add radiobutton -label $firstValue -variable $varName foreach i $args { $w.menu add radiobutton -label $i -variable $varName } return $w.menu } proc updatepalette {} { catch { tk_setPalette background [ttk::style lookup client -background] } } } if {[tk windowingsystem] eq "win32"} { ttk::style theme use xpnative set mw_classes [list Text Listbox Table TreeCtrl] foreach class $mw_classes { bind $class {} } set in_mousewheel 0 proc ::tk::MouseWheel {wFired X Y D {shifted 0}} { global in_mousewheel if { $in_mousewheel != 0 } { return } # Set event to check based on call set evt "<[expr {$shifted?{Shift-}:{}}]MouseWheel>" # do not double-fire in case the class already has a binding if {[bind [winfo class $wFired] $evt] ne ""} { return } # obtain the window the mouse is over set w [winfo containing $X $Y] # if we are outside the app, try and scroll the focus widget if {![winfo exists $w]} { catch {set w [focus]} } if {[winfo exists $w]} { if {[bind $w $evt] ne ""} { # Awkward ... this widget has a MouseWheel binding, but to # trigger successfully in it, we must give it focus. catch {focus} old if {$w ne $old} { focus $w } set in_mousewheel 1 event generate $w $evt -rootx $X -rooty $Y -delta $D set in_mousewheel 0 if {$w ne $old} { focus $old } return } # aqua and x11/win32 have different delta handling if {[tk windowingsystem] ne "aqua"} { set delta [expr {- ($D / 30)}] } else { set delta [expr {- ($D)}] } # scrollbars have different call conventions if {[string match "*Scrollbar" [winfo class $w]]} { catch {tk::ScrollByUnits $w \ [string index [$w cget -orient] 0] $delta} } else { set cmd [list $w [expr {$shifted ? "xview" : "yview"}] \ scroll $delta units] # Walking up to find the proper widget (handles cases like # embedded widgets in a canvas) while {[catch $cmd] && [winfo toplevel $w] ne $w} { set w [winfo parent $w] } } } } bind all [list ::tk::MouseWheel %W %X %Y %D 0] # end of win32 section } else { if {[catch { set theme [ttk::style theme use] }]} { set theme $::ttk::currentTheme } if {$theme eq "default"} { ttk::style theme use clam } } updatepalette # Unify right mouse button handling. # See "mouse buttons on macintosh" thread on comp.lang.tcl if {[tk windowingsystem] eq "aqua"} { event add <> event add <> } else { event add <> } proc gitdir {} { global env if {[info exists env(GIT_DIR)]} { return $env(GIT_DIR) } else { return ".hg" } } proc popupify {w} { wm resizable $w 0 0 wm withdraw $w update set x [expr {([winfo screenwidth .]-[winfo reqwidth $w])/2}] set y [expr {([winfo screenheight .]-[winfo reqheight $w])/2}] wm geometry $w +$x+$y wm transient $w . wm deiconify $w wm resizable $w 1 1 } proc getcommits {rargs} { global commits commfd phase canv mainfont env global startmsecs nextupdate ncmupdate global ctext maincursor textcursor leftover # check that we can find a .git directory somewhere... set gitdir [gitdir] if {![file isdirectory $gitdir]} { error_popup "Cannot find the git directory \"$gitdir\"." exit 1 } set commits {} set phase getcommits set startmsecs [clock clicks -milliseconds] set nextupdate [expr $startmsecs + 100] set ncmupdate 1 set limit 0 set revargs {} for {set i 0} {$i < [llength $rargs]} {incr i} { set opt [lindex $rargs $i] if {$opt == "--limit"} { incr i set limit [lindex $rargs $i] } else { lappend revargs $opt } } if [catch { set parse_args [concat --default HEAD $revargs] set parse_temp [eval exec {$env(HG)} --config ui.report_untrusted=false debug-rev-parse $parse_args] regsub -all "\r\n" $parse_temp "\n" parse_temp set parsed_args [split $parse_temp "\n"] } err] { # if git-rev-parse failed for some reason... if {$rargs == {}} { set revargs HEAD } set parsed_args $revargs } if {$limit > 0} { set parsed_args [concat -n $limit $parsed_args] } if [catch { set commfd [open "|{$env(HG)} --config ui.report_untrusted=false debug-rev-list --header --topo-order --parents $parsed_args" r] } err] { puts stderr "Error executing hg debug-rev-list: $err" exit 1 } set leftover {} fconfigure $commfd -blocking 0 -translation lf fileevent $commfd readable [list getcommitlines $commfd] $canv delete all $canv create text 3 3 -anchor nw -text "Reading commits..." \ -font $mainfont -tags textitems . config -cursor watch settextcursor watch } proc getcommitlines {commfd} { global commits parents cdate children global commitlisted phase commitinfo nextupdate global stopped redisplaying leftover set stuff [read $commfd] if {$stuff == {}} { if {![eof $commfd]} return # set it blocking so we wait for the process to terminate fconfigure $commfd -blocking 1 if {![catch {close $commfd} err]} { after idle finishcommits return } if {[string range $err 0 4] == "usage"} { set err \ {Gitk: error reading commits: bad arguments to git-rev-list. (Note: arguments to gitk are passed to git-rev-list to allow selection of commits to be displayed.)} } else { set err "Error reading commits: $err" } error_popup $err exit 1 } set start 0 while 1 { set i [string first "\0" $stuff $start] if {$i < 0} { append leftover [string range $stuff $start end] return } set cmit [string range $stuff $start [expr {$i - 1}]] if {$start == 0} { set cmit "$leftover$cmit" set leftover {} } set start [expr {$i + 1}] regsub -all "\r\n" $cmit "\n" cmit set j [string first "\n" $cmit] set ok 0 if {$j >= 0} { set ids [string range $cmit 0 [expr {$j - 1}]] set ok 1 foreach id $ids { if {![regexp {^[0-9a-f]{12}$} $id]} { set ok 0 break } } } if {!$ok} { set shortcmit $cmit if {[string length $shortcmit] > 80} { set shortcmit "[string range $shortcmit 0 80]..." } error_popup "Can't parse hg debug-rev-list output: {$shortcmit}" exit 1 } set id [lindex $ids 0] set olds [lrange $ids 1 end] set cmit [string range $cmit [expr {$j + 1}] end] lappend commits $id set commitlisted($id) 1 parsecommit $id $cmit 1 [lrange $ids 1 end] drawcommit $id if {[clock clicks -milliseconds] >= $nextupdate} { doupdate 1 } while {$redisplaying} { set redisplaying 0 if {$stopped == 1} { set stopped 0 set phase "getcommits" foreach id $commits { drawcommit $id if {$stopped} break if {[clock clicks -milliseconds] >= $nextupdate} { doupdate 1 } } } } } } proc doupdate {reading} { global commfd nextupdate numcommits ncmupdate if {$reading} { fileevent $commfd readable {} } update set nextupdate [expr {[clock clicks -milliseconds] + 100}] if {$numcommits < 100} { set ncmupdate [expr {$numcommits + 1}] } elseif {$numcommits < 10000} { set ncmupdate [expr {$numcommits + 10}] } else { set ncmupdate [expr {$numcommits + 100}] } if {$reading} { fileevent $commfd readable [list getcommitlines $commfd] } } proc readcommit {id} { global env if [catch {set contents [exec $env(HG) --config ui.report_untrusted=false debug-cat-file commit $id]}] return parsecommit $id $contents 0 {} } proc parsecommit {id contents listed olds} { global commitinfo children nchildren parents nparents cdate ncleft global firstparents set inhdr 1 set comment {} set headline {} set auname {} set audate {} set comname {} set comdate {} set rev {} set branch {} set bookmark {} if {![info exists nchildren($id)]} { set children($id) {} set nchildren($id) 0 set ncleft($id) 0 } set parents($id) $olds set nparents($id) [llength $olds] foreach p $olds { if {![info exists nchildren($p)]} { set children($p) [list $id] set nchildren($p) 1 set ncleft($p) 1 } elseif {[lsearch -exact $children($p) $id] < 0} { lappend children($p) $id incr nchildren($p) incr ncleft($p) } } regsub -all "\r\n" $contents "\n" contents foreach line [split $contents "\n"] { if {$inhdr} { set line [split $line] if {$line == {}} { set inhdr 0 } else { set tag [lindex $line 0] if {$tag == "author"} { set x [expr {[llength $line] - 2}] set audate [lindex $line $x] set auname [join [lrange $line 1 [expr {$x - 1}]]] } elseif {$tag == "committer"} { set x [expr {[llength $line] - 2}] set comdate [lindex $line $x] set comname [join [lrange $line 1 [expr {$x - 1}]]] } elseif {$tag == "revision"} { set rev [lindex $line 1] } elseif {$tag == "branch"} { set branch [join [lrange $line 1 end]] } elseif {$tag == "bookmark"} { set bookmark [join [lrange $line 1 end]] } } } else { if {$comment == {}} { set headline [string trim $line] } else { append comment "\n" } if {!$listed} { # git-rev-list indents the comment by 4 spaces; # if we got this via git-cat-file, add the indentation append comment " " } append comment $line } } if {$audate != {}} { set audate [clock format $audate] } if {$comdate != {}} { set cdate($id) $comdate set comdate [clock format $comdate] } set commitinfo($id) [list $headline $auname $audate \ $comname $comdate $comment $rev $branch $bookmark] if {[info exists firstparents]} { set i [lsearch $firstparents $id] if {$i != -1} { # remove the parent from firstparents, possible building # an empty list set firstparents [concat \ [lrange $firstparents 0 [expr $i - 1]] \ [lrange $firstparents [expr $i + 1] end]] if {$firstparents eq {}} { # we have found all parents of the first changeset # which means that we can safely select the first line after idle { selectline 0 0 } } } } else { # this is the first changeset, save the parents set firstparents $olds if {$firstparents eq {}} { # a repository with a single changeset after idle { selectline 0 0 } } } } proc readrefs {} { global bookmarkcurrent bookmarkids tagids idtags idbookmarks headids idheads tagcontents env curid set status [catch {exec $env(HG) --config ui.report_untrusted=false id} curid] if { $status != 0 } { puts $::errorInfo if { ![string equal $::errorCode NONE] } { exit 2 } } regexp -- {[[:xdigit:]]+} $curid curid set status [catch {exec $env(HG) --config ui.report_untrusted=false tags} tags] if { $status != 0 } { puts $::errorInfo if { ![string equal $::errorCode NONE] } { exit 2 } } foreach {- tag rev id} [regexp -inline -all -line {^(.+\S)\s+(\d+):(\S+)} $tags] { # we use foreach as Tcl8.4 doesn't support lassign lappend tagids($tag) $id lappend idtags($id) $tag } set status [catch {exec $env(HG) --config ui.report_untrusted=false heads} heads] if { $status != 0 } { puts $::errorInfo if { ![string equal $::errorCode NONE] } { exit 2 } } set lines [split $heads \r\n] foreach f $lines { set match "" regexp {changeset:\s+(\S+):(\S+)$} $f match id sha if {$match != ""} { lappend idheads($sha) $id } } set status [catch {exec $env(HG) --config ui.report_untrusted=false bookmarks} bookmarks] if { $status != 0 } { puts $::errorInfo if { ![string equal $::errorCode NONE] } { exit 2 } } set lines [split $bookmarks "\n"] set bookmarkcurrent 0 foreach f $lines { regexp {(\S+)$} $f full regsub {\s+(\S+)$} $f "" direct set sha [split $full ':'] set bookmark [lindex $sha 1] set current [string first " * " $direct)] regsub {^\s(\*|\s)\s} $direct "" direct lappend bookmarkids($direct) $bookmark lappend idbookmarks($bookmark) $direct if {$current >= 0} { set bookmarkcurrent $direct } } } proc readotherrefs {base dname excl} { global otherrefids idotherrefs set git [gitdir] set files [glob -nocomplain -types f [file join $git $base *]] foreach f $files { catch { set fd [open $f r] set line [read $fd 40] if {[regexp {^[0-9a-f]{12}} $line id]} { set name "$dname[file tail $f]" set otherrefids($name) $id lappend idotherrefs($id) $name } close $fd } } set dirs [glob -nocomplain -types d [file join $git $base *]] foreach d $dirs { set dir [file tail $d] if {[lsearch -exact $excl $dir] >= 0} continue readotherrefs [file join $base $dir] "$dname$dir/" {} } } proc allcansmousewheel {delta} { set delta [expr -5*(int($delta)/abs($delta))] allcanvs yview scroll $delta units } proc error_popup msg { set w .error toplevel $w wm transient $w . message $w.m -text $msg -justify center -aspect 400 pack $w.m -side top -fill x -padx 20 -pady 20 ttk::button $w.ok -text OK -command "destroy $w" pack $w.ok -side bottom -fill x bind $w "grab $w; focus $w" popupify $w tkwait window $w } proc makewindow {} { global canv canv2 canv3 linespc charspc ctext cflist textfont global findtype findtypemenu findloc findstring fstring geometry global entries sha1entry sha1string sha1but global maincursor textcursor curtextcursor global rowctxmenu gaudydiff mergemax global hgvdiff bgcolor fgcolor diffremcolor diffaddcolor diffmerge1color global diffmerge2color hunksepcolor global posx posy if {[info exists posx]} { wm geometry . +$posx+$posy } menu .bar .bar add cascade -label "File" -menu .bar.file menu .bar.file .bar.file add command -label "Reread references" -command rereadrefs .bar.file add command -label "Quit" -command doquit menu .bar.help .bar add cascade -label "Help" -menu .bar.help .bar.help add command -label "About hgk" -command about . configure -menu .bar if {![info exists geometry(canv1)]} { set geometry(canv1) [expr 45 * $charspc] set geometry(canv2) [expr 30 * $charspc] set geometry(canv3) [expr 15 * $charspc] set geometry(canvh) [expr 25 * $linespc + 4] set geometry(ctextw) 80 set geometry(ctexth) 30 set geometry(cflistw) 30 } panedwindow .ctop -orient vertical if {[info exists geometry(width)]} { .ctop conf -width $geometry(width) -height $geometry(height) set texth [expr {$geometry(height) - $geometry(canvh) - 56}] set geometry(ctexth) [expr {($texth - 8) / [font metrics $textfont -linespace]}] } ttk::frame .ctop.top ttk::frame .ctop.top.bar pack .ctop.top.bar -side bottom -fill x set cscroll .ctop.top.csb ttk::scrollbar $cscroll -command {allcanvs yview} pack $cscroll -side right -fill y panedwindow .ctop.top.clist -orient horizontal -sashpad 0 -handlesize 4 pack .ctop.top.clist -side top -fill both -expand 1 .ctop add .ctop.top set canv .ctop.top.clist.canv canvas $canv -height $geometry(canvh) -width $geometry(canv1) \ -bg $bgcolor -bd 0 \ -yscrollincr $linespc -yscrollcommand "$cscroll set" -selectbackground "#c0c0c0" .ctop.top.clist add $canv set canv2 .ctop.top.clist.canv2 canvas $canv2 -height $geometry(canvh) -width $geometry(canv2) \ -bg $bgcolor -bd 0 -yscrollincr $linespc -selectbackground "#c0c0c0" .ctop.top.clist add $canv2 set canv3 .ctop.top.clist.canv3 canvas $canv3 -height $geometry(canvh) -width $geometry(canv3) \ -bg $bgcolor -bd 0 -yscrollincr $linespc -selectbackground "#c0c0c0" .ctop.top.clist add $canv3 bind .ctop.top.clist {resizeclistpanes %W %w} set sha1entry .ctop.top.bar.sha1 set entries $sha1entry set sha1but .ctop.top.bar.sha1label button $sha1but -text "SHA1 ID: " -state disabled -relief flat \ -command gotocommit -width 8 $sha1but conf -disabledforeground [$sha1but cget -foreground] pack .ctop.top.bar.sha1label -side left ttk::entry $sha1entry -width 40 -font $textfont -textvariable sha1string trace add variable sha1string write sha1change pack $sha1entry -side left -pady 2 image create bitmap bm-left -data { #define left_width 16 #define left_height 16 static unsigned char left_bits[] = { 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00, 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01}; } image create bitmap bm-right -data { #define right_width 16 #define right_height 16 static unsigned char right_bits[] = { 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c, 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01}; } ttk::button .ctop.top.bar.leftbut -image bm-left -command goback \ -state disabled -width 26 pack .ctop.top.bar.leftbut -side left -fill y ttk::button .ctop.top.bar.rightbut -image bm-right -command goforw \ -state disabled -width 26 pack .ctop.top.bar.rightbut -side left -fill y ttk::button .ctop.top.bar.findbut -text "Find" -command dofind pack .ctop.top.bar.findbut -side left set findstring {} set fstring .ctop.top.bar.findstring lappend entries $fstring ttk::entry $fstring -width 30 -font $textfont -textvariable findstring pack $fstring -side left -expand 1 -fill x set findtype Exact set findtypemenu [ttk::optionMenu .ctop.top.bar.findtype \ findtype Exact IgnCase Regexp] set findloc "All fields" ttk::optionMenu .ctop.top.bar.findloc findloc "All fields" Headline \ Comments Author Files Pickaxe pack .ctop.top.bar.findloc -side right pack .ctop.top.bar.findtype -side right # for making sure type==Exact whenever loc==Pickaxe trace add variable findloc write findlocchange panedwindow .ctop.cdet -orient horizontal .ctop add .ctop.cdet ttk::frame .ctop.cdet.left set ctext .ctop.cdet.left.ctext text $ctext -fg $fgcolor -bg $bgcolor -state disabled -font $textfont \ -width $geometry(ctextw) -height $geometry(ctexth) \ -yscrollcommand ".ctop.cdet.left.sb set" \ -xscrollcommand ".ctop.cdet.left.hb set" -wrap none ttk::scrollbar .ctop.cdet.left.sb -command "$ctext yview" ttk::scrollbar .ctop.cdet.left.hb -orient horizontal -command "$ctext xview" pack .ctop.cdet.left.sb -side right -fill y pack .ctop.cdet.left.hb -side bottom -fill x pack $ctext -side left -fill both -expand 1 .ctop.cdet add .ctop.cdet.left $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa" if {$gaudydiff} { $ctext tag conf hunksep -back blue -fore white $ctext tag conf d0 -back "#ff8080" $ctext tag conf d1 -back green } else { $ctext tag conf hunksep -fore $hunksepcolor $ctext tag conf d0 -fore $diffremcolor $ctext tag conf d1 -fore $diffaddcolor # The mX colours seem to be used in merge changesets, where m0 # is first parent, m1 is second parent and so on. Git can have # several parents, Hg cannot, so I think the m2..mmax would be # unused. $ctext tag conf m0 -fore $diffmerge1color $ctext tag conf m1 -fore $diffmerge2color $ctext tag conf m2 -fore green $ctext tag conf m3 -fore purple $ctext tag conf m4 -fore brown $ctext tag conf mmax -fore darkgrey set mergemax 5 $ctext tag conf mresult -font [concat $textfont bold] $ctext tag conf msep -font [concat $textfont bold] $ctext tag conf found -back yellow } ttk::frame .ctop.cdet.right set cflist .ctop.cdet.right.cfiles listbox $cflist -fg $fgcolor -bg $bgcolor \ -selectmode extended -width $geometry(cflistw) \ -yscrollcommand ".ctop.cdet.right.sb set" ttk::scrollbar .ctop.cdet.right.sb -command "$cflist yview" pack .ctop.cdet.right.sb -side right -fill y pack $cflist -side left -fill both -expand 1 .ctop.cdet add .ctop.cdet.right bind .ctop.cdet {resizecdetpanes %W %w} pack .ctop -side top -fill both -expand 1 bindall <1> {selcanvline %W %x %y} #bindall {selcanvline %W %x %y} bindall "allcansmousewheel %D" bindall "allcanvs yview scroll -5 units" bindall "allcanvs yview scroll 5 units" bindall <2> "allcanvs scan mark 0 %y" bindall "allcanvs scan dragto 0 %y" bind . "selnextline -1" bind . "selnextline 1" bind . "allcanvs yview scroll -1 pages" bind . "allcanvs yview scroll 1 pages" bindkey "$ctext yview scroll -1 pages" bindkey "$ctext yview scroll -1 pages" bindkey "$ctext yview scroll 1 pages" bindkey p "selnextline -1" bindkey n "selnextline 1" bindkey b "$ctext yview scroll -1 pages" bindkey d "$ctext yview scroll 18 units" bindkey u "$ctext yview scroll -18 units" bindkey / {findnext 1} bindkey {findnext 0} bindkey ? findprev bindkey f nextfile bind . doquit bind . doquit bind . dofind bind . {findnext 0} bind . findprev bind . {incrfont 1} bind . {incrfont 1} bind . {incrfont -1} bind . {incrfont -1} bind $cflist <> listboxsel bind . {savestuff %W} bind . "click %W" bind $fstring dofind bind $sha1entry gotocommit bind $sha1entry <> clearsha1 set maincursor [. cget -cursor] set textcursor [$ctext cget -cursor] set curtextcursor $textcursor set rowctxmenu .rowctxmenu menu $rowctxmenu -tearoff 0 $rowctxmenu add command -label "Diff this -> selected" \ -command {diffvssel 0} $rowctxmenu add command -label "Diff selected -> this" \ -command {diffvssel 1} $rowctxmenu add command -label "Make patch" -command mkpatch $rowctxmenu add command -label "Create tag" -command mktag $rowctxmenu add command -label "Write commit to file" -command writecommit if { $hgvdiff ne "" } { $rowctxmenu add command -label "Visual diff with parent" \ -command {vdiff 1} $rowctxmenu add command -label "Visual diff with selected" \ -command {vdiff 0} } } # when we make a key binding for the toplevel, make sure # it doesn't get triggered when that key is pressed in the # find string entry widget. proc bindkey {ev script} { global entries bind . $ev $script set escript [bind Entry $ev] if {$escript == {}} { set escript [bind Entry ] } foreach e $entries { bind $e $ev "$escript; break" } } # set the focus back to the toplevel for any click outside # the entry widgets proc click {w} { global entries foreach e $entries { if {$w == $e} return } focus . } proc savestuff {w} { global canv canv2 canv3 ctext cflist mainfont textfont global stuffsaved findmergefiles gaudydiff maxgraphpct global maxwidth authorcolors curidfont bgcolor fgcolor global diffremcolor diffaddcolor hunksepcolor global diffmerge1color diffmerge2color if {$stuffsaved} return if {![winfo viewable .]} return catch { set f [open "~/.hgk-new" w] puts $f [list set mainfont $mainfont] puts $f [list set curidfont $curidfont] puts $f [list set textfont $textfont] puts $f [list set findmergefiles $findmergefiles] puts $f [list set gaudydiff $gaudydiff] puts $f [list set maxgraphpct $maxgraphpct] puts $f [list set maxwidth $maxwidth] puts $f "set geometry(width) [winfo width .ctop]" puts $f "set geometry(height) [winfo height .ctop]" puts $f "set geometry(canv1) [expr [winfo width $canv]-2]" puts $f "set geometry(canv2) [expr [winfo width $canv2]-2]" puts $f "set geometry(canv3) [expr [winfo width $canv3]-2]" puts $f "set geometry(canvh) [expr [winfo height $canv]-2]" set wid [expr {([winfo width $ctext] - 8) \ / [font measure $textfont "0"]}] puts $f "set geometry(ctextw) $wid" set wid [expr {([winfo width $cflist] - 11) \ / [font measure [$cflist cget -font] "0"]}] puts $f "set geometry(cflistw) $wid" puts $f "#" puts $f "# main window position:" puts $f "set posx [winfo x .]" puts $f "set posy [winfo y .]" puts $f "#" puts $f "# authorcolors format:" puts $f "#" puts $f "# zero or more sublists of" puts $f "#" puts $f "# { regex color }" puts $f "#" puts $f "# followed by a list of colors" puts $f "#" puts $f "# If the commit author matches a regex in a sublist," puts $f "# the commit will be colored by that color" puts $f "# otherwise the next unused entry from the list of colors" puts $f "# will be assigned to this commit and also all other commits" puts $f "# of the same author. When the list of colors is exhausted," puts $f "# the last entry will be reused." puts $f "#" puts $f "set authorcolors {$authorcolors}" puts $f "#" puts $f "# The background color in the text windows" puts $f "set bgcolor $bgcolor" puts $f "#" puts $f "# The text color used in the diff and file list view" puts $f "set fgcolor $fgcolor" puts $f "#" puts $f "# Color to display + lines in diffs" puts $f "set diffaddcolor $diffaddcolor" puts $f "#" puts $f "# Color to display - lines in diffs" puts $f "set diffremcolor $diffremcolor" puts $f "#" puts $f "# Merge diffs: Color to signal lines from first parent" puts $f "set diffmerge1color $diffmerge1color" puts $f "#" puts $f "# Merge diffs: Color to signal lines from second parent" puts $f "set diffmerge2color $diffmerge2color" puts $f "#" puts $f "# Hunkseparator (@@ -lineno,lines +lineno,lines @@) color" puts $f "set hunksepcolor $hunksepcolor" close $f file rename -force "~/.hgk-new" "~/.hgk" } set stuffsaved 1 } proc resizeclistpanes {win w} { global oldwidth if [info exists oldwidth($win)] { set s0 [$win sash coord 0] set s1 [$win sash coord 1] if {$w < 60} { set sash0 [expr {int($w/2 - 2)}] set sash1 [expr {int($w*5/6 - 2)}] } else { set factor [expr {1.0 * $w / $oldwidth($win)}] set sash0 [expr {int($factor * [lindex $s0 0])}] set sash1 [expr {int($factor * [lindex $s1 0])}] if {$sash0 < 30} { set sash0 30 } if {$sash1 < $sash0 + 20} { set sash1 [expr $sash0 + 20] } if {$sash1 > $w - 10} { set sash1 [expr $w - 10] if {$sash0 > $sash1 - 20} { set sash0 [expr $sash1 - 20] } } } $win sash place 0 $sash0 [lindex $s0 1] $win sash place 1 $sash1 [lindex $s1 1] } set oldwidth($win) $w } proc resizecdetpanes {win w} { global oldwidth if [info exists oldwidth($win)] { set s0 [$win sash coord 0] if {$w < 60} { set sash0 [expr {int($w*3/4 - 2)}] } else { set factor [expr {1.0 * $w / $oldwidth($win)}] set sash0 [expr {int($factor * [lindex $s0 0])}] if {$sash0 < 45} { set sash0 45 } if {$sash0 > $w - 15} { set sash0 [expr $w - 15] } } $win sash place 0 $sash0 [lindex $s0 1] } set oldwidth($win) $w } proc allcanvs args { global canv canv2 canv3 eval $canv $args eval $canv2 $args eval $canv3 $args } proc bindall {event action} { global canv canv2 canv3 bind $canv $event $action bind $canv2 $event $action bind $canv3 $event $action } proc about {} { set w .about if {[winfo exists $w]} { raise $w return } toplevel $w wm title $w "About hgk" message $w.m -text { Hgk version 1.2 Copyright © 2005 Paul Mackerras Use and redistribute under the terms of the GNU General Public License} \ -justify center -aspect 400 pack $w.m -side top -fill x -padx 20 -pady 20 ttk::button $w.ok -text Close -command "destroy $w" pack $w.ok -side bottom popupify $w } set aunextcolor 0 proc assignauthorcolor {name} { global authorcolors aucolormap aunextcolor if [info exists aucolormap($name)] return set randomcolors {black} for {set i 0} {$i < [llength $authorcolors]} {incr i} { set col [lindex $authorcolors $i] if {[llength $col] > 1} { set re [lindex $col 0] set c [lindex $col 1] if {[regexp -- $re $name]} { set aucolormap($name) $c return } } else { set randomcolors [lrange $authorcolors $i end] break } } set ncolors [llength $randomcolors] set c [lindex $randomcolors $aunextcolor] if {[incr aunextcolor] >= $ncolors} { incr aunextcolor -1 } set aucolormap($name) $c } proc assigncolor {id} { global commitinfo colormap commcolors colors nextcolor global parents nparents children nchildren global cornercrossings crossings if [info exists colormap($id)] return set ncolors [llength $colors] if {$nparents($id) <= 1 && $nchildren($id) == 1} { set child [lindex $children($id) 0] if {[info exists colormap($child)] && $nparents($child) == 1} { set colormap($id) $colormap($child) return } } set badcolors {} if {[info exists cornercrossings($id)]} { foreach x $cornercrossings($id) { if {[info exists colormap($x)] && [lsearch -exact $badcolors $colormap($x)] < 0} { lappend badcolors $colormap($x) } } if {[llength $badcolors] >= $ncolors} { set badcolors {} } } set origbad $badcolors if {[llength $badcolors] < $ncolors - 1} { if {[info exists crossings($id)]} { foreach x $crossings($id) { if {[info exists colormap($x)] && [lsearch -exact $badcolors $colormap($x)] < 0} { lappend badcolors $colormap($x) } } if {[llength $badcolors] >= $ncolors} { set badcolors $origbad } } set origbad $badcolors } if {[llength $badcolors] < $ncolors - 1} { foreach child $children($id) { if {[info exists colormap($child)] && [lsearch -exact $badcolors $colormap($child)] < 0} { lappend badcolors $colormap($child) } if {[info exists parents($child)]} { foreach p $parents($child) { if {[info exists colormap($p)] && [lsearch -exact $badcolors $colormap($p)] < 0} { lappend badcolors $colormap($p) } } } } if {[llength $badcolors] >= $ncolors} { set badcolors $origbad } } for {set i 0} {$i <= $ncolors} {incr i} { set c [lindex $colors $nextcolor] if {[incr nextcolor] >= $ncolors} { set nextcolor 0 } if {[lsearch -exact $badcolors $c]} break } set colormap($id) $c } proc initgraph {} { global canvy canvy0 lineno numcommits nextcolor linespc global mainline mainlinearrow sidelines global nchildren ncleft global displist nhyperspace allcanvs delete all set nextcolor 0 set canvy $canvy0 set lineno -1 set numcommits 0 catch {unset mainline} catch {unset mainlinearrow} catch {unset sidelines} foreach id [array names nchildren] { set ncleft($id) $nchildren($id) } set displist {} set nhyperspace 0 } proc bindline {t id} { global canv $canv bind $t "lineenter %x %y $id" $canv bind $t "linemotion %x %y $id" $canv bind $t "lineleave $id" $canv bind $t "lineclick %x %y $id 1" } proc drawlines {id xtra} { global mainline mainlinearrow sidelines lthickness colormap canv $canv delete lines.$id if {[info exists mainline($id)]} { set t [$canv create line $mainline($id) \ -width [expr {($xtra + 1) * $lthickness}] \ -fill $colormap($id) -tags lines.$id \ -arrow $mainlinearrow($id)] $canv lower $t bindline $t $id } if {[info exists sidelines($id)]} { foreach ls $sidelines($id) { set coords [lindex $ls 0] set thick [lindex $ls 1] set arrow [lindex $ls 2] set t [$canv create line $coords -fill $colormap($id) \ -width [expr {($thick + $xtra) * $lthickness}] \ -arrow $arrow -tags lines.$id] $canv lower $t bindline $t $id } } } # level here is an index in displist proc drawcommitline {level} { global parents children nparents displist global canv canv2 canv3 mainfont namefont canvy linespc global lineid linehtag linentag linedtag commitinfo global colormap numcommits currentparents dupparents global idtags idline idheads idotherrefs idbookmarks global lineno lthickness mainline mainlinearrow sidelines global commitlisted rowtextx idpos lastuse displist global oldnlines olddlevel olddisplist global aucolormap curid curidfont incr numcommits incr lineno set id [lindex $displist $level] set lastuse($id) $lineno set lineid($lineno) $id set idline($id) $lineno set ofill [expr {[info exists commitlisted($id)]? "blue": "white"}] if {![info exists commitinfo($id)]} { readcommit $id if {![info exists commitinfo($id)]} { set commitinfo($id) {"No commit information available"} set nparents($id) 0 } } assigncolor $id set currentparents {} set dupparents {} if {[info exists commitlisted($id)] && [info exists parents($id)]} { foreach p $parents($id) { if {[lsearch -exact $currentparents $p] < 0} { lappend currentparents $p } else { # remember that this parent was listed twice lappend dupparents $p } } } set x [xcoord $level $level $lineno] set y1 $canvy set canvy [expr $canvy + $linespc] allcanvs conf -scrollregion \ [list 0 0 0 [expr $y1 + 0.5 * $linespc + 2]] if {[info exists mainline($id)]} { lappend mainline($id) $x $y1 if {$mainlinearrow($id) ne "none"} { set mainline($id) [trimdiagstart $mainline($id)] } } drawlines $id 0 set orad [expr {$linespc / 3}] set t [$canv create oval [expr $x - $orad] [expr $y1 - $orad] \ [expr $x + $orad - 1] [expr $y1 + $orad - 1] \ -fill $ofill -outline black -width 1] $canv raise $t $canv bind $t <1> {selcanvline {} %x %y} set xt [xcoord [llength $displist] $level $lineno] if {[llength $currentparents] > 2} { set xt [expr {$xt + ([llength $currentparents] - 2) * $linespc}] } set rowtextx($lineno) $xt set idpos($id) [list $x $xt $y1] if {[info exists idtags($id)] || [info exists idheads($id)] || [info exists idotherrefs($id)] || [info exists idbookmarks($id)]} { set xt [drawtags $id $x $xt $y1] } set headline [lindex $commitinfo($id) 0] set name [lindex $commitinfo($id) 1] assignauthorcolor $name set fg $aucolormap($name) if {$id == $curid} { set fn $curidfont } else { set fn $mainfont } set date [lindex $commitinfo($id) 2] set linehtag($lineno) [$canv create text $xt $y1 -anchor w \ -text $headline -font $fn \ -fill $fg] $canv bind $linehtag($lineno) <> "rowmenu %X %Y $id" set linentag($lineno) [$canv2 create text 3 $y1 -anchor w \ -text $name -font $namefont \ -fill $fg] set linedtag($lineno) [$canv3 create text 3 $y1 -anchor w \ -text $date -font $mainfont \ -fill $fg] set olddlevel $level set olddisplist $displist set oldnlines [llength $displist] } proc drawtags {id x xt y1} { global bookmarkcurrent idtags idbookmarks idheads idotherrefs commitinfo global linespc lthickness global canv mainfont idline rowtextx set marks {} set nbookmarks 0 set ntags 0 set nheads 0 if {[info exists idtags($id)]} { set marks $idtags($id) set ntags [llength $marks] } if {[info exists idbookmarks($id)]} { set marks [concat $marks $idbookmarks($id)] set nbookmarks [llength $idbookmarks($id)] } if {[info exists idheads($id)]} { set headmark [lindex $commitinfo($id) 7] if {$headmark ne "default"} { lappend marks $headmark set nheads 1 } } if {$marks eq {}} { return $xt } set delta [expr {int(0.5 * ($linespc - $lthickness))}] set yt [expr $y1 - 0.5 * $linespc] set yb [expr $yt + $linespc - 1] set xvals {} set wvals {} foreach tag $marks { set wid [font measure $mainfont $tag] lappend xvals $xt lappend wvals $wid set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}] } set t [$canv create line $x $y1 [lindex $xvals end] $y1 \ -width $lthickness -fill black -tags tag.$id] $canv lower $t foreach tag $marks x $xvals wid $wvals { set xl [expr $x + $delta] set xr [expr $x + $delta + $wid + $lthickness] if {[incr ntags -1] >= 0} { # draw a tag set t [$canv create polygon $x [expr $yt + $delta] $xl $yt \ $xr $yt $xr $yb $xl $yb $x [expr $yb - $delta] \ -width 1 -outline black -fill yellow -tags tag.$id] $canv bind $t <1> [list showtag $tag 1] set rowtextx($idline($id)) [expr {$xr + $linespc}] } elseif {[incr nbookmarks -1] >= 0} { # draw a tag set col gray50 if {[string compare $bookmarkcurrent $tag] == 0} { set col gray } set xl [expr $xl - $delta/2] $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \ -width 1 -outline black -fill $col -tags tag.$id } else { # draw a head or other ref if {[incr nheads -1] >= 0} { set col "#00ff00" } else { set col "#ddddff" } set xl [expr $xl - $delta/2] $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \ -width 1 -outline black -fill $col -tags tag.$id } set t [$canv create text $xl $y1 -anchor w -text $tag \ -font $mainfont -tags tag.$id] if {$ntags >= 0} { $canv bind $t <1> [list showtag $tag 1] } } return $xt } proc notecrossings {id lo hi corner} { global olddisplist crossings cornercrossings for {set i $lo} {[incr i] < $hi} {} { set p [lindex $olddisplist $i] if {$p == {}} continue if {$i == $corner} { if {![info exists cornercrossings($id)] || [lsearch -exact $cornercrossings($id) $p] < 0} { lappend cornercrossings($id) $p } if {![info exists cornercrossings($p)] || [lsearch -exact $cornercrossings($p) $id] < 0} { lappend cornercrossings($p) $id } } else { if {![info exists crossings($id)] || [lsearch -exact $crossings($id) $p] < 0} { lappend crossings($id) $p } if {![info exists crossings($p)] || [lsearch -exact $crossings($p) $id] < 0} { lappend crossings($p) $id } } } } proc xcoord {i level ln} { global canvx0 xspc1 xspc2 set x [expr {$canvx0 + $i * $xspc1($ln)}] if {$i > 0 && $i == $level} { set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}] } elseif {$i > $level} { set x [expr {$x + $xspc2 - $xspc1($ln)}] } return $x } # it seems Tk can't draw arrows on the end of diagonal line segments... proc trimdiagend {line} { while {[llength $line] > 4} { set x1 [lindex $line end-3] set y1 [lindex $line end-2] set x2 [lindex $line end-1] set y2 [lindex $line end] if {($x1 == $x2) != ($y1 == $y2)} break set line [lreplace $line end-1 end] } return $line } proc trimdiagstart {line} { while {[llength $line] > 4} { set x1 [lindex $line 0] set y1 [lindex $line 1] set x2 [lindex $line 2] set y2 [lindex $line 3] if {($x1 == $x2) != ($y1 == $y2)} break set line [lreplace $line 0 1] } return $line } proc drawslants {id needonscreen nohs} { global canv mainline mainlinearrow sidelines global canvx0 canvy xspc1 xspc2 lthickness global currentparents dupparents global lthickness linespc canvy colormap lineno geometry global maxgraphpct maxwidth global displist onscreen lastuse global parents commitlisted global oldnlines olddlevel olddisplist global nhyperspace numcommits nnewparents if {$lineno < 0} { lappend displist $id set onscreen($id) 1 return 0 } set y1 [expr {$canvy - $linespc}] set y2 $canvy # work out what we need to get back on screen set reins {} if {$onscreen($id) < 0} { # next to do isn't displayed, better get it on screen... lappend reins [list $id 0] } # make sure all the previous commits's parents are on the screen foreach p $currentparents { if {$onscreen($p) < 0} { lappend reins [list $p 0] } } # bring back anything requested by caller if {$needonscreen ne {}} { lappend reins $needonscreen } # try the shortcut if {$currentparents == $id && $onscreen($id) == 0 && $reins eq {}} { set dlevel $olddlevel set x [xcoord $dlevel $dlevel $lineno] set mainline($id) [list $x $y1] set mainlinearrow($id) none set lastuse($id) $lineno set displist [lreplace $displist $dlevel $dlevel $id] set onscreen($id) 1 set xspc1([expr {$lineno + 1}]) $xspc1($lineno) return $dlevel } # update displist set displist [lreplace $displist $olddlevel $olddlevel] set j $olddlevel foreach p $currentparents { set lastuse($p) $lineno if {$onscreen($p) == 0} { set displist [linsert $displist $j $p] set onscreen($p) 1 incr j } } if {$onscreen($id) == 0} { lappend displist $id set onscreen($id) 1 } # remove the null entry if present set nullentry [lsearch -exact $displist {}] if {$nullentry >= 0} { set displist [lreplace $displist $nullentry $nullentry] } # bring back the ones we need now (if we did it earlier # it would change displist and invalidate olddlevel) foreach pi $reins { # test again in case of duplicates in reins set p [lindex $pi 0] if {$onscreen($p) < 0} { set onscreen($p) 1 set lastuse($p) $lineno set displist [linsert $displist [lindex $pi 1] $p] incr nhyperspace -1 } } set lastuse($id) $lineno # see if we need to make any lines jump off into hyperspace set displ [llength $displist] if {$displ > $maxwidth} { set ages {} foreach x $displist { lappend ages [list $lastuse($x) $x] } set ages [lsort -integer -index 0 $ages] set k 0 while {$displ > $maxwidth} { set use [lindex $ages $k 0] set victim [lindex $ages $k 1] if {$use >= $lineno - 5} break incr k if {[lsearch -exact $nohs $victim] >= 0} continue set i [lsearch -exact $displist $victim] set displist [lreplace $displist $i $i] set onscreen($victim) -1 incr nhyperspace incr displ -1 if {$i < $nullentry} { incr nullentry -1 } set x [lindex $mainline($victim) end-1] lappend mainline($victim) $x $y1 set line [trimdiagend $mainline($victim)] set arrow "last" if {$mainlinearrow($victim) ne "none"} { set line [trimdiagstart $line] set arrow "both" } lappend sidelines($victim) [list $line 1 $arrow] unset mainline($victim) } } set dlevel [lsearch -exact $displist $id] # If we are reducing, put in a null entry if {$displ < $oldnlines} { # does the next line look like a merge? # i.e. does it have > 1 new parent? if {$nnewparents($id) > 1} { set i [expr {$dlevel + 1}] } elseif {$nnewparents([lindex $olddisplist $olddlevel]) == 0} { set i $olddlevel if {$nullentry >= 0 && $nullentry < $i} { incr i -1 } } elseif {$nullentry >= 0} { set i $nullentry while {$i < $displ && [lindex $olddisplist $i] == [lindex $displist $i]} { incr i } } else { set i $olddlevel if {$dlevel >= $i} { incr i } } if {$i < $displ} { set displist [linsert $displist $i {}] incr displ if {$dlevel >= $i} { incr dlevel } } } # decide on the line spacing for the next line set lj [expr {$lineno + 1}] set maxw [expr {$maxgraphpct * $geometry(canv1) / 100}] if {$displ <= 1 || $canvx0 + $displ * $xspc2 <= $maxw} { set xspc1($lj) $xspc2 } else { set xspc1($lj) [expr {($maxw - $canvx0 - $xspc2) / ($displ - 1)}] if {$xspc1($lj) < $lthickness} { set xspc1($lj) $lthickness } } foreach idi $reins { set id [lindex $idi 0] set j [lsearch -exact $displist $id] set xj [xcoord $j $dlevel $lj] set mainline($id) [list $xj $y2] set mainlinearrow($id) first } set i -1 foreach id $olddisplist { incr i if {$id == {}} continue if {$onscreen($id) <= 0} continue set xi [xcoord $i $olddlevel $lineno] if {$i == $olddlevel} { foreach p $currentparents { set j [lsearch -exact $displist $p] set coords [list $xi $y1] set xj [xcoord $j $dlevel $lj] if {$xj < $xi - $linespc} { lappend coords [expr {$xj + $linespc}] $y1 notecrossings $p $j $i [expr {$j + 1}] } elseif {$xj > $xi + $linespc} { lappend coords [expr {$xj - $linespc}] $y1 notecrossings $p $i $j [expr {$j - 1}] } if {[lsearch -exact $dupparents $p] >= 0} { # draw a double-width line to indicate the doubled parent lappend coords $xj $y2 lappend sidelines($p) [list $coords 2 none] if {![info exists mainline($p)]} { set mainline($p) [list $xj $y2] set mainlinearrow($p) none } } else { # normal case, no parent duplicated set yb $y2 set dx [expr {abs($xi - $xj)}] if {0 && $dx < $linespc} { set yb [expr {$y1 + $dx}] } if {![info exists mainline($p)]} { if {$xi != $xj} { lappend coords $xj $yb } set mainline($p) $coords set mainlinearrow($p) none } else { lappend coords $xj $yb if {$yb < $y2} { lappend coords $xj $y2 } lappend sidelines($p) [list $coords 1 none] } } } } else { set j $i if {[lindex $displist $i] != $id} { set j [lsearch -exact $displist $id] } if {$j != $i || $xspc1($lineno) != $xspc1($lj) || ($olddlevel < $i && $i < $dlevel) || ($dlevel < $i && $i < $olddlevel)} { set xj [xcoord $j $dlevel $lj] lappend mainline($id) $xi $y1 $xj $y2 } } } return $dlevel } # search for x in a list of lists proc llsearch {llist x} { set i 0 foreach l $llist { if {$l == $x || [lsearch -exact $l $x] >= 0} { return $i } incr i } return -1 } proc drawmore {reading} { global displayorder numcommits ncmupdate nextupdate global stopped nhyperspace parents commitlisted global maxwidth onscreen displist currentparents olddlevel set n [llength $displayorder] while {$numcommits < $n} { set id [lindex $displayorder $numcommits] set ctxend [expr {$numcommits + 10}] if {!$reading && $ctxend > $n} { set ctxend $n } set dlist {} if {$numcommits > 0} { set dlist [lreplace $displist $olddlevel $olddlevel] set i $olddlevel foreach p $currentparents { if {$onscreen($p) == 0} { set dlist [linsert $dlist $i $p] incr i } } } set nohs {} set reins {} set isfat [expr {[llength $dlist] > $maxwidth}] if {$nhyperspace > 0 || $isfat} { if {$ctxend > $n} break # work out what to bring back and # what we want to don't want to send into hyperspace set room 1 for {set k $numcommits} {$k < $ctxend} {incr k} { set x [lindex $displayorder $k] set i [llsearch $dlist $x] if {$i < 0} { set i [llength $dlist] lappend dlist $x } if {[lsearch -exact $nohs $x] < 0} { lappend nohs $x } if {$reins eq {} && $onscreen($x) < 0 && $room} { set reins [list $x $i] } set newp {} if {[info exists commitlisted($x)]} { set right 0 foreach p $parents($x) { if {[llsearch $dlist $p] < 0} { lappend newp $p if {[lsearch -exact $nohs $p] < 0} { lappend nohs $p } if {$reins eq {} && $onscreen($p) < 0 && $room} { set reins [list $p [expr {$i + $right}]] } } set right 1 } } set l [lindex $dlist $i] if {[llength $l] == 1} { set l $newp } else { set j [lsearch -exact $l $x] set l [concat [lreplace $l $j $j] $newp] } set dlist [lreplace $dlist $i $i $l] if {$room && $isfat && [llength $newp] <= 1} { set room 0 } } } set dlevel [drawslants $id $reins $nohs] drawcommitline $dlevel if {[clock clicks -milliseconds] >= $nextupdate && $numcommits >= $ncmupdate} { doupdate $reading if {$stopped} break } } } # level here is an index in todo proc updatetodo {level noshortcut} { global ncleft todo nnewparents global commitlisted parents onscreen set id [lindex $todo $level] set olds {} if {[info exists commitlisted($id)]} { foreach p $parents($id) { if {[lsearch -exact $olds $p] < 0} { lappend olds $p } } } if {!$noshortcut && [llength $olds] == 1} { set p [lindex $olds 0] if {$ncleft($p) == 1 && [lsearch -exact $todo $p] < 0} { set ncleft($p) 0 set todo [lreplace $todo $level $level $p] set onscreen($p) 0 set nnewparents($id) 1 return 0 } } set todo [lreplace $todo $level $level] set i $level set n 0 foreach p $olds { incr ncleft($p) -1 set k [lsearch -exact $todo $p] if {$k < 0} { set todo [linsert $todo $i $p] set onscreen($p) 0 incr i incr n } } set nnewparents($id) $n return 1 } proc decidenext {{noread 0}} { global ncleft todo global datemode cdate global commitinfo # choose which one to do next time around set todol [llength $todo] set level -1 set latest {} for {set k $todol} {[incr k -1] >= 0} {} { set p [lindex $todo $k] if {$ncleft($p) == 0} { if {$datemode} { if {![info exists commitinfo($p)]} { if {$noread} { return {} } readcommit $p } if {$latest == {} || $cdate($p) > $latest} { set level $k set latest $cdate($p) } } else { set level $k break } } } if {$level < 0} { if {$todo != {}} { puts "ERROR: none of the pending commits can be done yet:" foreach p $todo { puts " $p ($ncleft($p))" } } return -1 } return $level } proc drawcommit {id} { global phase todo nchildren datemode nextupdate global numcommits ncmupdate displayorder todo onscreen if {$phase != "incrdraw"} { set phase incrdraw set displayorder {} set todo {} initgraph } if {$nchildren($id) == 0} { lappend todo $id set onscreen($id) 0 } set level [decidenext 1] if {$level == {} || $id != [lindex $todo $level]} { return } while 1 { lappend displayorder [lindex $todo $level] if {[updatetodo $level $datemode]} { set level [decidenext 1] if {$level == {}} break } set id [lindex $todo $level] if {![info exists commitlisted($id)]} { break } } drawmore 1 } proc finishcommits {} { global phase global canv mainfont ctext maincursor textcursor if {$phase != "incrdraw"} { $canv delete all $canv create text 3 3 -anchor nw -text "No commits selected" \ -font $mainfont -tags textitems set phase {} } else { drawrest } . config -cursor $maincursor settextcursor $textcursor } # Don't change the text pane cursor if it is currently the hand cursor, # showing that we are over a sha1 ID link. proc settextcursor {c} { global ctext curtextcursor if {[$ctext cget -cursor] == $curtextcursor} { $ctext config -cursor $c } set curtextcursor $c } proc drawgraph {} { global nextupdate startmsecs ncmupdate global displayorder onscreen if {$displayorder == {}} return set startmsecs [clock clicks -milliseconds] set nextupdate [expr $startmsecs + 100] set ncmupdate 1 initgraph foreach id $displayorder { set onscreen($id) 0 } drawmore 0 } proc drawrest {} { global phase stopped redisplaying selectedline global datemode todo displayorder global numcommits ncmupdate global nextupdate startmsecs set level [decidenext] if {$level >= 0} { set phase drawgraph while 1 { lappend displayorder [lindex $todo $level] set hard [updatetodo $level $datemode] if {$hard} { set level [decidenext] if {$level < 0} break } } drawmore 0 } set phase {} set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs] #puts "overall $drawmsecs ms for $numcommits commits" if {$redisplaying} { if {$stopped == 0 && [info exists selectedline]} { selectline $selectedline 0 } if {$stopped == 1} { set stopped 0 after idle drawgraph } else { set redisplaying 0 } } } proc findmatches {f} { global findtype foundstring foundstrlen if {$findtype == "Regexp"} { set matches [regexp -indices -all -inline $foundstring $f] } else { if {$findtype == "IgnCase"} { set str [string tolower $f] } else { set str $f } set matches {} set i 0 while {[set j [string first $foundstring $str $i]] >= 0} { lappend matches [list $j [expr $j+$foundstrlen-1]] set i [expr $j + $foundstrlen] } } return $matches } proc dofind {} { global findtype findloc findstring markedmatches commitinfo global numcommits lineid linehtag linentag linedtag global mainfont namefont canv canv2 canv3 selectedline global matchinglines foundstring foundstrlen stopfindproc unmarkmatches focus . set matchinglines {} if {$findloc == "Pickaxe"} { findpatches return } if {$findtype == "IgnCase"} { set foundstring [string tolower $findstring] } else { set foundstring $findstring } set foundstrlen [string length $findstring] if {$foundstrlen == 0} return if {$findloc == "Files"} { findfiles return } if {![info exists selectedline]} { set oldsel -1 } else { set oldsel $selectedline } set didsel 0 set fldtypes {Headline Author Date CDate Comment} for {set l 0} {$l < $numcommits} {incr l} { set id $lineid($l) set info $commitinfo($id) set doesmatch 0 foreach f $info ty $fldtypes { if {$findloc != "All fields" && $findloc != $ty} { continue } set matches [findmatches $f] if {$matches == {}} continue set doesmatch 1 if {$ty == "Headline"} { markmatches $canv $l $f $linehtag($l) $matches $mainfont } elseif {$ty == "Author"} { markmatches $canv2 $l $f $linentag($l) $matches $namefont } elseif {$ty == "Date"} { markmatches $canv3 $l $f $linedtag($l) $matches $mainfont } } if {$doesmatch} { lappend matchinglines $l if {!$didsel && $l > $oldsel} { findselectline $l set didsel 1 } } } if {$matchinglines == {}} { bell } elseif {!$didsel} { findselectline [lindex $matchinglines 0] } } proc findselectline {l} { global findloc commentend ctext selectline $l 1 if {$findloc == "All fields" || $findloc == "Comments"} { # highlight the matches in the comments set f [$ctext get 1.0 $commentend] set matches [findmatches $f] foreach match $matches { set start [lindex $match 0] set end [expr [lindex $match 1] + 1] $ctext tag add found "1.0 + $start c" "1.0 + $end c" } } } proc findnext {restart} { global matchinglines selectedline if {![info exists matchinglines]} { if {$restart} { dofind } return } if {![info exists selectedline]} return foreach l $matchinglines { if {$l > $selectedline} { findselectline $l return } } bell } proc findprev {} { global matchinglines selectedline if {![info exists matchinglines]} { dofind return } if {![info exists selectedline]} return set prev {} foreach l $matchinglines { if {$l >= $selectedline} break set prev $l } if {$prev != {}} { findselectline $prev } else { bell } } proc findlocchange {name ix op} { global findloc findtype findtypemenu if {$findloc == "Pickaxe"} { set findtype Exact set state disabled } else { set state normal } $findtypemenu entryconf 1 -state $state $findtypemenu entryconf 2 -state $state } proc stopfindproc {{done 0}} { global findprocpid findprocfile findids global ctext findoldcursor phase maincursor textcursor global findinprogress catch {unset findids} if {[info exists findprocpid]} { if {!$done} { catch {exec kill $findprocpid} } catch {close $findprocfile} unset findprocpid } if {[info exists findinprogress]} { unset findinprogress if {$phase != "incrdraw"} { . config -cursor $maincursor settextcursor $textcursor } } } proc findpatches {} { global findstring selectedline numcommits global findprocpid findprocfile global finddidsel ctext lineid findinprogress global findinsertpos global env if {$numcommits == 0} return # make a list of all the ids to search, starting at the one # after the selected line (if any) if {[info exists selectedline]} { set l $selectedline } else { set l -1 } set inputids {} for {set i 0} {$i < $numcommits} {incr i} { if {[incr l] >= $numcommits} { set l 0 } append inputids $lineid($l) "\n" } if {[catch { set f [open [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree --stdin -s -r -S$findstring << $inputids] r] } err]} { error_popup "Error starting search process: $err" return } set findinsertpos end set findprocfile $f set findprocpid [pid $f] fconfigure $f -blocking 0 fileevent $f readable readfindproc set finddidsel 0 . config -cursor watch settextcursor watch set findinprogress 1 } proc readfindproc {} { global findprocfile finddidsel global idline matchinglines findinsertpos set n [gets $findprocfile line] if {$n < 0} { if {[eof $findprocfile]} { stopfindproc 1 if {!$finddidsel} { bell } } return } if {![regexp {^[0-9a-f]{12}} $line id]} { error_popup "Can't parse git-diff-tree output: $line" stopfindproc return } if {![info exists idline($id)]} { puts stderr "spurious id: $id" return } set l $idline($id) insertmatch $l $id } proc insertmatch {l id} { global matchinglines findinsertpos finddidsel if {$findinsertpos == "end"} { if {$matchinglines != {} && $l < [lindex $matchinglines 0]} { set matchinglines [linsert $matchinglines 0 $l] set findinsertpos 1 } else { lappend matchinglines $l } } else { set matchinglines [linsert $matchinglines $findinsertpos $l] incr findinsertpos } markheadline $l $id if {!$finddidsel} { findselectline $l set finddidsel 1 } } proc findfiles {} { global selectedline numcommits lineid ctext global ffileline finddidsel parents nparents global findinprogress findstartline findinsertpos global treediffs fdiffids fdiffsneeded fdiffpos global findmergefiles global env if {$numcommits == 0} return if {[info exists selectedline]} { set l [expr {$selectedline + 1}] } else { set l 0 } set ffileline $l set findstartline $l set diffsneeded {} set fdiffsneeded {} while 1 { set id $lineid($l) if {$findmergefiles || $nparents($id) == 1} { foreach p $parents($id) { if {![info exists treediffs([list $id $p])]} { append diffsneeded "$id $p\n" lappend fdiffsneeded [list $id $p] } } } if {[incr l] >= $numcommits} { set l 0 } if {$l == $findstartline} break } # start off a git-diff-tree process if needed if {$diffsneeded ne {}} { if {[catch { set df [open [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree -r --stdin << $diffsneeded] r] } err ]} { error_popup "Error starting search process: $err" return } catch {unset fdiffids} set fdiffpos 0 fconfigure $df -blocking 0 fileevent $df readable [list readfilediffs $df] } set finddidsel 0 set findinsertpos end set id $lineid($l) set p [lindex $parents($id) 0] . config -cursor watch settextcursor watch set findinprogress 1 findcont [list $id $p] update } proc readfilediffs {df} { global findids fdiffids fdiffs set n [gets $df line] if {$n < 0} { if {[eof $df]} { donefilediff if {[catch {close $df} err]} { stopfindproc bell error_popup "Error in hg debug-diff-tree: $err" } elseif {[info exists findids]} { set ids $findids stopfindproc bell error_popup "Couldn't find diffs for {$ids}" } } return } if {[regexp {^([0-9a-f]{12}) \(from ([0-9a-f]{12})\)} $line match id p]} { # start of a new string of diffs donefilediff set fdiffids [list $id $p] set fdiffs {} } elseif {[string match ":*" $line]} { lappend fdiffs [lindex $line 5] } } proc donefilediff {} { global fdiffids fdiffs treediffs findids global fdiffsneeded fdiffpos if {[info exists fdiffids]} { while {[lindex $fdiffsneeded $fdiffpos] ne $fdiffids && $fdiffpos < [llength $fdiffsneeded]} { # git-diff-tree doesn't output anything for a commit # which doesn't change anything set nullids [lindex $fdiffsneeded $fdiffpos] set treediffs($nullids) {} if {[info exists findids] && $nullids eq $findids} { unset findids findcont $nullids } incr fdiffpos } incr fdiffpos if {![info exists treediffs($fdiffids)]} { set treediffs($fdiffids) $fdiffs } if {[info exists findids] && $fdiffids eq $findids} { unset findids findcont $fdiffids } } } proc findcont {ids} { global findids treediffs parents nparents global ffileline findstartline finddidsel global lineid numcommits matchinglines findinprogress global findmergefiles set id [lindex $ids 0] set p [lindex $ids 1] set pi [lsearch -exact $parents($id) $p] set l $ffileline while 1 { if {$findmergefiles || $nparents($id) == 1} { if {![info exists treediffs($ids)]} { set findids $ids set ffileline $l return } set doesmatch 0 foreach f $treediffs($ids) { set x [findmatches $f] if {$x != {}} { set doesmatch 1 break } } if {$doesmatch} { insertmatch $l $id set pi $nparents($id) } } else { set pi $nparents($id) } if {[incr pi] >= $nparents($id)} { set pi 0 if {[incr l] >= $numcommits} { set l 0 } if {$l == $findstartline} break set id $lineid($l) } set p [lindex $parents($id) $pi] set ids [list $id $p] } stopfindproc if {!$finddidsel} { bell } } # mark a commit as matching by putting a yellow background # behind the headline proc markheadline {l id} { global canv mainfont linehtag commitinfo set bbox [$canv bbox $linehtag($l)] set t [$canv create rect $bbox -outline {} -tags matches -fill yellow] $canv lower $t } # mark the bits of a headline, author or date that match a find string proc markmatches {canv l str tag matches font} { set bbox [$canv bbox $tag] set x0 [lindex $bbox 0] set y0 [lindex $bbox 1] set y1 [lindex $bbox 3] foreach match $matches { set start [lindex $match 0] set end [lindex $match 1] if {$start > $end} continue set xoff [font measure $font [string range $str 0 [expr $start-1]]] set xlen [font measure $font [string range $str 0 [expr $end]]] set t [$canv create rect [expr $x0+$xoff] $y0 [expr $x0+$xlen+2] $y1 \ -outline {} -tags matches -fill yellow] $canv lower $t } } proc unmarkmatches {} { global matchinglines findids allcanvs delete matches catch {unset matchinglines} catch {unset findids} } proc selcanvline {w x y} { global canv canvy0 ctext linespc global lineid linehtag linentag linedtag rowtextx set ymax [lindex [$canv cget -scrollregion] 3] if {$ymax == {}} return set yfrac [lindex [$canv yview] 0] set y [expr {$y + $yfrac * $ymax}] set l [expr {int(($y - $canvy0) / $linespc + 0.5)}] if {$l < 0} { set l 0 } if {$w eq $canv} { if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return } unmarkmatches selectline $l 1 } proc commit_descriptor {p} { global commitinfo set l "..." if {[info exists commitinfo($p)]} { set l [lindex $commitinfo($p) 0] set r [lindex $commitinfo($p) 6] } return "$r:$p ($l)" } # append some text to the ctext widget, and make any SHA1 ID # that we know about be a clickable link. proc appendwithlinks {text} { global ctext idline linknum set start [$ctext index "end - 1c"] $ctext insert end $text $ctext insert end "\n" set links [regexp -indices -all -inline {[0-9a-f]{12}} $text] foreach l $links { set s [lindex $l 0] set e [lindex $l 1] set linkid [string range $text $s $e] if {![info exists idline($linkid)]} continue incr e $ctext tag add link "$start + $s c" "$start + $e c" $ctext tag add link$linknum "$start + $s c" "$start + $e c" $ctext tag bind link$linknum <1> [list selectline $idline($linkid) 1] incr linknum } $ctext tag conf link -foreground blue -underline 1 $ctext tag bind link { %W configure -cursor hand2 } $ctext tag bind link { %W configure -cursor $curtextcursor } } proc selectline {l isnew} { global canv canv2 canv3 ctext commitinfo selectedline global lineid linehtag linentag linedtag global canvy0 linespc parents nparents children global cflist currentid sha1entry global commentend idtags idbookmarks idline linknum $canv delete hover normalline if {![info exists lineid($l)] || ![info exists linehtag($l)]} return $canv delete secsel set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \ -tags secsel -fill [$canv cget -selectbackground]] $canv lower $t $canv2 delete secsel set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \ -tags secsel -fill [$canv2 cget -selectbackground]] $canv2 lower $t $canv3 delete secsel set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \ -tags secsel -fill [$canv3 cget -selectbackground]] $canv3 lower $t set y [expr {$canvy0 + $l * $linespc}] set ymax [lindex [$canv cget -scrollregion] 3] set ytop [expr {$y - $linespc - 1}] set ybot [expr {$y + $linespc + 1}] set wnow [$canv yview] set wtop [expr [lindex $wnow 0] * $ymax] set wbot [expr [lindex $wnow 1] * $ymax] set wh [expr {$wbot - $wtop}] set newtop $wtop if {$ytop < $wtop} { if {$ybot < $wtop} { set newtop [expr {$y - $wh / 2.0}] } else { set newtop $ytop if {$newtop > $wtop - $linespc} { set newtop [expr {$wtop - $linespc}] } } } elseif {$ybot > $wbot} { if {$ytop > $wbot} { set newtop [expr {$y - $wh / 2.0}] } else { set newtop [expr {$ybot - $wh}] if {$newtop < $wtop + $linespc} { set newtop [expr {$wtop + $linespc}] } } } if {$newtop != $wtop} { if {$newtop < 0} { set newtop 0 } allcanvs yview moveto [expr $newtop * 1.0 / $ymax] } if {$isnew} { addtohistory [list selectline $l 0] } set selectedline $l set id $lineid($l) set currentid $id $sha1entry delete 0 end $sha1entry insert 0 $id $sha1entry selection range 0 end $ctext conf -state normal $ctext delete 0.0 end set linknum 0 $ctext mark set fmark.0 0.0 $ctext mark gravity fmark.0 left set info $commitinfo($id) $ctext insert end "Changeset: [lindex $info 6]\n" if {[llength [lindex $info 7]] > 0} { $ctext insert end "Branch: [lindex $info 7]\n" } $ctext insert end "User: [lindex $info 1]\n" $ctext insert end "Date: [lindex $info 2]\n" if {[info exists idbookmarks($id)]} { $ctext insert end "Bookmarks:" foreach bookmark $idbookmarks($id) { $ctext insert end " $bookmark" } $ctext insert end "\n" } if {[info exists idtags($id)]} { $ctext insert end "Tags:" foreach tag $idtags($id) { $ctext insert end " $tag" } $ctext insert end "\n" } set comment {} if {[info exists parents($id)]} { foreach p $parents($id) { append comment "Parent: [commit_descriptor $p]\n" } } if {[info exists children($id)]} { foreach c $children($id) { append comment "Child: [commit_descriptor $c]\n" } } append comment "\n" append comment [lindex $info 5] # make anything that looks like a SHA1 ID be a clickable link appendwithlinks $comment $ctext tag delete Comments $ctext tag remove found 1.0 end $ctext conf -state disabled set commentend [$ctext index "end - 1c"] $cflist delete 0 end $cflist insert end "Comments" if {$nparents($id) <= 1} { set parent "null" if {$nparents($id) == 1} { set parent $parents($id) } startdiff [concat $id $parent] } elseif {$nparents($id) > 1} { mergediff $id } } proc selnextline {dir} { global selectedline if {![info exists selectedline]} return set l [expr $selectedline + $dir] unmarkmatches selectline $l 1 } proc unselectline {} { global selectedline catch {unset selectedline} allcanvs delete secsel } proc addtohistory {cmd} { global history historyindex if {$historyindex > 0 && [lindex $history [expr {$historyindex - 1}]] == $cmd} { return } if {$historyindex < [llength $history]} { set history [lreplace $history $historyindex end $cmd] } else { lappend history $cmd } incr historyindex if {$historyindex > 1} { .ctop.top.bar.leftbut conf -state normal } else { .ctop.top.bar.leftbut conf -state disabled } .ctop.top.bar.rightbut conf -state disabled } proc goback {} { global history historyindex if {$historyindex > 1} { incr historyindex -1 set cmd [lindex $history [expr {$historyindex - 1}]] eval $cmd .ctop.top.bar.rightbut conf -state normal } if {$historyindex <= 1} { .ctop.top.bar.leftbut conf -state disabled } } proc goforw {} { global history historyindex if {$historyindex < [llength $history]} { set cmd [lindex $history $historyindex] incr historyindex eval $cmd .ctop.top.bar.leftbut conf -state normal } if {$historyindex >= [llength $history]} { .ctop.top.bar.rightbut conf -state disabled } } proc mergediff {id} { global parents diffmergeid diffmergegca mergefilelist diffpindex set diffmergeid $id set diffpindex -1 set diffmergegca [findgca $parents($id)] if {[info exists mergefilelist($id)]} { if {$mergefilelist($id) ne {}} { showmergediff } } else { contmergediff {} } } proc findgca {ids} { global env set gca {} foreach id $ids { if {$gca eq {}} { set gca $id } else { if {[catch { set gca [exec $env(HG) --config ui.report_untrusted=false debug-merge-base $gca $id] } err]} { return {} } } } return $gca } proc contmergediff {ids} { global diffmergeid diffpindex parents nparents diffmergegca global treediffs mergefilelist diffids treepending # diff the child against each of the parents, and diff # each of the parents against the GCA. while 1 { if {[lindex $ids 0] == $diffmergeid && $diffmergegca ne {}} { set ids [list [lindex $ids 1] $diffmergegca] } else { if {[incr diffpindex] >= $nparents($diffmergeid)} break set p [lindex $parents($diffmergeid) $diffpindex] set ids [list $diffmergeid $p] } if {![info exists treediffs($ids)]} { set diffids $ids if {![info exists treepending]} { gettreediffs $ids } return } } # If a file in some parent is different from the child and also # different from the GCA, then it's interesting. # If we don't have a GCA, then a file is interesting if it is # different from the child in all the parents. if {$diffmergegca ne {}} { set files {} foreach p $parents($diffmergeid) { set gcadiffs $treediffs([list $p $diffmergegca]) foreach f $treediffs([list $diffmergeid $p]) { if {[lsearch -exact $files $f] < 0 && [lsearch -exact $gcadiffs $f] >= 0} { lappend files $f } } } set files [lsort $files] } else { set p [lindex $parents($diffmergeid) 0] set files $treediffs([list $diffmergeid $p]) for {set i 1} {$i < $nparents($diffmergeid) && $files ne {}} {incr i} { set p [lindex $parents($diffmergeid) $i] set df $treediffs([list $diffmergeid $p]) set nf {} foreach f $files { if {[lsearch -exact $df $f] >= 0} { lappend nf $f } } set files $nf } } set mergefilelist($diffmergeid) $files if {$files ne {}} { showmergediff } } proc showmergediff {} { global cflist diffmergeid mergefilelist parents global diffopts diffinhunk currentfile currenthunk filelines global diffblocked groupfilelast mergefds groupfilenum grouphunks global env set files $mergefilelist($diffmergeid) foreach f $files { $cflist insert end $f } set env(GIT_DIFF_OPTS) $diffopts set flist {} catch {unset currentfile} catch {unset currenthunk} catch {unset filelines} catch {unset groupfilenum} catch {unset grouphunks} set groupfilelast -1 foreach p $parents($diffmergeid) { set cmd [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree -p $p $diffmergeid] set cmd [concat $cmd $mergefilelist($diffmergeid)] if {[catch {set f [open $cmd r]} err]} { error_popup "Error getting diffs: $err" foreach f $flist { catch {close $f} } return } lappend flist $f set ids [list $diffmergeid $p] set mergefds($ids) $f set diffinhunk($ids) 0 set diffblocked($ids) 0 fconfigure $f -blocking 0 fileevent $f readable [list getmergediffline $f $ids $diffmergeid] } } proc getmergediffline {f ids id} { global diffmergeid diffinhunk diffoldlines diffnewlines global currentfile currenthunk global diffoldstart diffnewstart diffoldlno diffnewlno global diffblocked mergefilelist global noldlines nnewlines difflcounts filelines set n [gets $f line] if {$n < 0} { if {![eof $f]} return } if {!([info exists diffmergeid] && $diffmergeid == $id)} { if {$n < 0} { close $f } return } if {$diffinhunk($ids) != 0} { set fi $currentfile($ids) if {$n > 0 && [regexp {^[-+ \\]} $line match]} { # continuing an existing hunk set line [string range $line 1 end] set p [lindex $ids 1] if {$match eq "-" || $match eq " "} { set filelines($p,$fi,$diffoldlno($ids)) $line incr diffoldlno($ids) } if {$match eq "+" || $match eq " "} { set filelines($id,$fi,$diffnewlno($ids)) $line incr diffnewlno($ids) } if {$match eq " "} { if {$diffinhunk($ids) == 2} { lappend difflcounts($ids) \ [list $noldlines($ids) $nnewlines($ids)] set noldlines($ids) 0 set diffinhunk($ids) 1 } incr noldlines($ids) } elseif {$match eq "-" || $match eq "+"} { if {$diffinhunk($ids) == 1} { lappend difflcounts($ids) [list $noldlines($ids)] set noldlines($ids) 0 set nnewlines($ids) 0 set diffinhunk($ids) 2 } if {$match eq "-"} { incr noldlines($ids) } else { incr nnewlines($ids) } } # and if it's \ No newline at end of line, then what? return } # end of a hunk if {$diffinhunk($ids) == 1 && $noldlines($ids) != 0} { lappend difflcounts($ids) [list $noldlines($ids)] } elseif {$diffinhunk($ids) == 2 && ($noldlines($ids) != 0 || $nnewlines($ids) != 0)} { lappend difflcounts($ids) [list $noldlines($ids) $nnewlines($ids)] } set currenthunk($ids) [list $currentfile($ids) \ $diffoldstart($ids) $diffnewstart($ids) \ $diffoldlno($ids) $diffnewlno($ids) \ $difflcounts($ids)] set diffinhunk($ids) 0 # -1 = need to block, 0 = unblocked, 1 = is blocked set diffblocked($ids) -1 processhunks if {$diffblocked($ids) == -1} { fileevent $f readable {} set diffblocked($ids) 1 } } if {$n < 0} { # eof if {!$diffblocked($ids)} { close $f set currentfile($ids) [llength $mergefilelist($diffmergeid)] set currenthunk($ids) [list $currentfile($ids) 0 0 0 0 {}] processhunks } } elseif {[regexp {^diff --git a/(.*) b/} $line match fname]} { # start of a new file set currentfile($ids) \ [lsearch -exact $mergefilelist($diffmergeid) $fname] } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \ $line match f1l f1c f2l f2c rest]} { if {[info exists currentfile($ids)] && $currentfile($ids) >= 0} { # start of a new hunk if {$f1l == 0 && $f1c == 0} { set f1l 1 } if {$f2l == 0 && $f2c == 0} { set f2l 1 } set diffinhunk($ids) 1 set diffoldstart($ids) $f1l set diffnewstart($ids) $f2l set diffoldlno($ids) $f1l set diffnewlno($ids) $f2l set difflcounts($ids) {} set noldlines($ids) 0 set nnewlines($ids) 0 } } } proc processhunks {} { global diffmergeid parents nparents currenthunk global mergefilelist diffblocked mergefds global grouphunks grouplinestart grouplineend groupfilenum set nfiles [llength $mergefilelist($diffmergeid)] while 1 { set fi $nfiles set lno 0 # look for the earliest hunk foreach p $parents($diffmergeid) { set ids [list $diffmergeid $p] if {![info exists currenthunk($ids)]} return set i [lindex $currenthunk($ids) 0] set l [lindex $currenthunk($ids) 2] if {$i < $fi || ($i == $fi && $l < $lno)} { set fi $i set lno $l set pi $p } } if {$fi < $nfiles} { set ids [list $diffmergeid $pi] set hunk $currenthunk($ids) unset currenthunk($ids) if {$diffblocked($ids) > 0} { fileevent $mergefds($ids) readable \ [list getmergediffline $mergefds($ids) $ids $diffmergeid] } set diffblocked($ids) 0 if {[info exists groupfilenum] && $groupfilenum == $fi && $lno <= $grouplineend} { # add this hunk to the pending group lappend grouphunks($pi) $hunk set endln [lindex $hunk 4] if {$endln > $grouplineend} { set grouplineend $endln } continue } } # succeeding stuff doesn't belong in this group, so # process the group now if {[info exists groupfilenum]} { processgroup unset groupfilenum unset grouphunks } if {$fi >= $nfiles} break # start a new group set groupfilenum $fi set grouphunks($pi) [list $hunk] set grouplinestart $lno set grouplineend [lindex $hunk 4] } } proc processgroup {} { global groupfilelast groupfilenum difffilestart global mergefilelist diffmergeid ctext filelines global parents diffmergeid diffoffset global grouphunks grouplinestart grouplineend nparents global mergemax $ctext conf -state normal set id $diffmergeid set f $groupfilenum if {$groupfilelast != $f} { $ctext insert end "\n" set here [$ctext index "end - 1c"] set difffilestart($f) $here set mark fmark.[expr {$f + 1}] $ctext mark set $mark $here $ctext mark gravity $mark left set header [lindex $mergefilelist($id) $f] set l [expr {(78 - [string length $header]) / 2}] set pad [string range "----------------------------------------" 1 $l] $ctext insert end "$pad $header $pad\n" filesep set groupfilelast $f foreach p $parents($id) { set diffoffset($p) 0 } } $ctext insert end "@@" msep set nlines [expr {$grouplineend - $grouplinestart}] set events {} set pnum 0 foreach p $parents($id) { set startline [expr {$grouplinestart + $diffoffset($p)}] set ol $startline set nl $grouplinestart if {[info exists grouphunks($p)]} { foreach h $grouphunks($p) { set l [lindex $h 2] if {$nl < $l} { for {} {$nl < $l} {incr nl} { set filelines($p,$f,$ol) $filelines($id,$f,$nl) incr ol } } foreach chunk [lindex $h 5] { if {[llength $chunk] == 2} { set olc [lindex $chunk 0] set nlc [lindex $chunk 1] set nnl [expr {$nl + $nlc}] lappend events [list $nl $nnl $pnum $olc $nlc] incr ol $olc set nl $nnl } else { incr ol [lindex $chunk 0] incr nl [lindex $chunk 0] } } } } if {$nl < $grouplineend} { for {} {$nl < $grouplineend} {incr nl} { set filelines($p,$f,$ol) $filelines($id,$f,$nl) incr ol } } set nlines [expr {$ol - $startline}] $ctext insert end " -$startline,$nlines" msep incr pnum } set nlines [expr {$grouplineend - $grouplinestart}] $ctext insert end " +$grouplinestart,$nlines @@\n" msep set events [lsort -integer -index 0 $events] set nevents [llength $events] set nmerge $nparents($diffmergeid) set l $grouplinestart for {set i 0} {$i < $nevents} {set i $j} { set nl [lindex $events $i 0] while {$l < $nl} { $ctext insert end " $filelines($id,$f,$l)\n" incr l } set e [lindex $events $i] set enl [lindex $e 1] set j $i set active {} while 1 { set pnum [lindex $e 2] set olc [lindex $e 3] set nlc [lindex $e 4] if {![info exists delta($pnum)]} { set delta($pnum) [expr {$olc - $nlc}] lappend active $pnum } else { incr delta($pnum) [expr {$olc - $nlc}] } if {[incr j] >= $nevents} break set e [lindex $events $j] if {[lindex $e 0] >= $enl} break if {[lindex $e 1] > $enl} { set enl [lindex $e 1] } } set nlc [expr {$enl - $l}] set ncol mresult set bestpn -1 if {[llength $active] == $nmerge - 1} { # no diff for one of the parents, i.e. it's identical for {set pnum 0} {$pnum < $nmerge} {incr pnum} { if {![info exists delta($pnum)]} { if {$pnum < $mergemax} { lappend ncol m$pnum } else { lappend ncol mmax } break } } } elseif {[llength $active] == $nmerge} { # all parents are different, see if one is very similar set bestsim 30 for {set pnum 0} {$pnum < $nmerge} {incr pnum} { set sim [similarity $pnum $l $nlc $f \ [lrange $events $i [expr {$j-1}]]] if {$sim > $bestsim} { set bestsim $sim set bestpn $pnum } } if {$bestpn >= 0} { lappend ncol m$bestpn } } set pnum -1 foreach p $parents($id) { incr pnum if {![info exists delta($pnum)] || $pnum == $bestpn} continue set olc [expr {$nlc + $delta($pnum)}] set ol [expr {$l + $diffoffset($p)}] incr diffoffset($p) $delta($pnum) unset delta($pnum) for {} {$olc > 0} {incr olc -1} { $ctext insert end "-$filelines($p,$f,$ol)\n" m$pnum incr ol } } set endl [expr {$l + $nlc}] if {$bestpn >= 0} { # show this pretty much as a normal diff set p [lindex $parents($id) $bestpn] set ol [expr {$l + $diffoffset($p)}] incr diffoffset($p) $delta($bestpn) unset delta($bestpn) for {set k $i} {$k < $j} {incr k} { set e [lindex $events $k] if {[lindex $e 2] != $bestpn} continue set nl [lindex $e 0] set ol [expr {$ol + $nl - $l}] for {} {$l < $nl} {incr l} { $ctext insert end "+$filelines($id,$f,$l)\n" $ncol } set c [lindex $e 3] for {} {$c > 0} {incr c -1} { $ctext insert end "-$filelines($p,$f,$ol)\n" m$bestpn incr ol } set nl [lindex $e 1] for {} {$l < $nl} {incr l} { $ctext insert end "+$filelines($id,$f,$l)\n" mresult } } } for {} {$l < $endl} {incr l} { $ctext insert end "+$filelines($id,$f,$l)\n" $ncol } } while {$l < $grouplineend} { $ctext insert end " $filelines($id,$f,$l)\n" incr l } $ctext conf -state disabled } proc similarity {pnum l nlc f events} { global diffmergeid parents diffoffset filelines set id $diffmergeid set p [lindex $parents($id) $pnum] set ol [expr {$l + $diffoffset($p)}] set endl [expr {$l + $nlc}] set same 0 set diff 0 foreach e $events { if {[lindex $e 2] != $pnum} continue set nl [lindex $e 0] set ol [expr {$ol + $nl - $l}] for {} {$l < $nl} {incr l} { incr same [string length $filelines($id,$f,$l)] incr same } set oc [lindex $e 3] for {} {$oc > 0} {incr oc -1} { incr diff [string length $filelines($p,$f,$ol)] incr diff incr ol } set nl [lindex $e 1] for {} {$l < $nl} {incr l} { incr diff [string length $filelines($id,$f,$l)] incr diff } } for {} {$l < $endl} {incr l} { incr same [string length $filelines($id,$f,$l)] incr same } if {$same == 0} { return 0 } return [expr {200 * $same / (2 * $same + $diff)}] } proc startdiff {ids} { global treediffs diffids treepending diffmergeid set diffids $ids catch {unset diffmergeid} if {![info exists treediffs($ids)]} { if {![info exists treepending]} { gettreediffs $ids } } else { addtocflist $ids } } proc addtocflist {ids} { global treediffs cflist foreach f $treediffs($ids) { $cflist insert end $f } getblobdiffs $ids } proc gettreediffs {ids} { global treediff parents treepending env set treepending $ids set treediff {} set id [lindex $ids 0] set p [lindex $ids 1] if [catch {set gdtf [open "|{$env(HG)} --config ui.report_untrusted=false debug-diff-tree -r $p $id" r]}] return fconfigure $gdtf -blocking 0 fileevent $gdtf readable [list gettreediffline $gdtf $ids] } proc gettreediffline {gdtf ids} { global treediff treediffs treepending diffids diffmergeid set n [gets $gdtf line] if {$n < 0} { if {![eof $gdtf]} return close $gdtf set treediffs($ids) $treediff unset treepending if {$ids != $diffids} { gettreediffs $diffids } else { if {[info exists diffmergeid]} { contmergediff $ids } else { addtocflist $ids } } return } set tab1 [expr [string first "\t" $line] + 1] set tab2 [expr [string first "\t" $line $tab1] - 1] set file [string range $line $tab1 $tab2] lappend treediff $file } proc getblobdiffs {ids} { global diffopts blobdifffd diffids env curdifftag curtagstart global difffilestart nextupdate diffinhdr treediffs set id [lindex $ids 0] set p [lindex $ids 1] set env(GIT_DIFF_OPTS) $diffopts set cmd [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree -r -p -C $p $id] if {[catch {set bdf [open $cmd r]} err]} { puts "error getting diffs: $err" return } set diffinhdr 0 fconfigure $bdf -blocking 0 set blobdifffd($ids) $bdf set curdifftag Comments set curtagstart 0.0 catch {unset difffilestart} fileevent $bdf readable [list getblobdiffline $bdf $diffids] set nextupdate [expr {[clock clicks -milliseconds] + 100}] } proc getblobdiffline {bdf ids} { global diffids blobdifffd ctext curdifftag curtagstart global diffnexthead diffnextnote difffilestart global nextupdate diffinhdr treediffs global gaudydiff set n [gets $bdf line] if {$n < 0} { if {[eof $bdf]} { close $bdf if {$ids == $diffids && $bdf == $blobdifffd($ids)} { $ctext tag add $curdifftag $curtagstart end } } return } if {$ids != $diffids || $bdf != $blobdifffd($ids)} { return } regsub -all "\r" $line "" line $ctext conf -state normal if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} { # start of a new file $ctext insert end "\n" $ctext tag add $curdifftag $curtagstart end set curtagstart [$ctext index "end - 1c"] set header $newname set here [$ctext index "end - 1c"] set i [lsearch -exact $treediffs($diffids) $fname] if {$i >= 0} { set difffilestart($i) $here incr i $ctext mark set fmark.$i $here $ctext mark gravity fmark.$i left } if {$newname != $fname} { set i [lsearch -exact $treediffs($diffids) $newname] if {$i >= 0} { set difffilestart($i) $here incr i $ctext mark set fmark.$i $here $ctext mark gravity fmark.$i left } } set curdifftag "f:$fname" $ctext tag delete $curdifftag set l [expr {(78 - [string length $header]) / 2}] set pad [string range "----------------------------------------" 1 $l] $ctext insert end "$pad $header $pad\n" filesep set diffinhdr 1 } elseif {[regexp {^(---|\+\+\+) } $line] && $diffinhdr} { set diffinhdr 1 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \ $line match f1l f1c f2l f2c rest]} { if {$gaudydiff} { $ctext insert end "\t" hunksep $ctext insert end " $f1l " d0 " $f2l " d1 $ctext insert end " $rest \n" hunksep } else { $ctext insert end "$line\n" hunksep } set diffinhdr 0 } else { set x [string range $line 0 0] if {$x == "-" || $x == "+"} { set tag [expr {$x == "+"}] if {$gaudydiff} { set line [string range $line 1 end] } $ctext insert end "$line\n" d$tag } elseif {$x == " "} { if {$gaudydiff} { set line [string range $line 1 end] } $ctext insert end "$line\n" } elseif {$diffinhdr || $x == "\\"} { # e.g. "\ No newline at end of file" $ctext insert end "$line\n" filesep } elseif {$line != ""} { # Something else we don't recognize if {$curdifftag != "Comments"} { $ctext insert end "\n" $ctext tag add $curdifftag $curtagstart end set curtagstart [$ctext index "end - 1c"] set curdifftag Comments } $ctext insert end "$line\n" filesep } } $ctext conf -state disabled if {[clock clicks -milliseconds] >= $nextupdate} { incr nextupdate 100 fileevent $bdf readable {} update fileevent $bdf readable "getblobdiffline $bdf {$ids}" } } proc nextfile {} { global difffilestart ctext set here [$ctext index @0,0] for {set i 0} {[info exists difffilestart($i)]} {incr i} { if {[$ctext compare $difffilestart($i) > $here]} { if {![info exists pos] || [$ctext compare $difffilestart($i) < $pos]} { set pos $difffilestart($i) } } } if {[info exists pos]} { $ctext yview $pos } } proc listboxsel {} { global ctext cflist currentid if {![info exists currentid]} return set sel [lsort [$cflist curselection]] if {$sel eq {}} return set first [lindex $sel 0] catch {$ctext yview fmark.$first} } proc setcoords {} { global linespc charspc canvx0 canvy0 mainfont global xspc1 xspc2 lthickness set linespc [font metrics $mainfont -linespace] set charspc [font measure $mainfont "m"] set canvy0 [expr 3 + 0.5 * $linespc] set canvx0 [expr 3 + 0.5 * $linespc] set lthickness [expr {int($linespc / 9) + 1}] set xspc1(0) $linespc set xspc2 $linespc } proc redisplay {} { global stopped redisplaying phase if {$stopped > 1} return if {$phase == "getcommits"} return set redisplaying 1 if {$phase == "drawgraph" || $phase == "incrdraw"} { set stopped 1 } else { drawgraph } } proc incrfont {inc} { global mainfont namefont textfont ctext canv phase global stopped entries curidfont unmarkmatches set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]] set curidfont [lreplace $curidfont 1 1 [expr {[lindex $curidfont 1] + $inc}]] set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]] set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]] setcoords $ctext conf -font $textfont $ctext tag conf filesep -font [concat $textfont bold] foreach e $entries { $e conf -font $mainfont } if {$phase == "getcommits"} { $canv itemconf textitems -font $mainfont } redisplay } proc clearsha1 {} { global sha1entry sha1string if {[string length $sha1string] == 40} { $sha1entry delete 0 end } } proc sha1change {n1 n2 op} { global sha1string currentid sha1but if {$sha1string == {} || ([info exists currentid] && $sha1string == $currentid)} { set state disabled } else { set state normal } if {[$sha1but cget -state] == $state} return if {$state == "normal"} { $sha1but conf -state normal -relief raised -text "Goto: " } else { $sha1but conf -state disabled -relief flat -text "SHA1 ID: " } } proc gotocommit {} { global sha1string currentid idline tagids global lineid numcommits if {$sha1string == {} || ([info exists currentid] && $sha1string == $currentid)} return if {[info exists tagids($sha1string)]} { set id $tagids($sha1string) } else { set id [string tolower $sha1string] if {[regexp {^[0-9a-f]{4,39}$} $id]} { set matches {} for {set l 0} {$l < $numcommits} {incr l} { if {[string match $id* $lineid($l)]} { lappend matches $lineid($l) } } if {$matches ne {}} { if {[llength $matches] > 1} { error_popup "Short SHA1 id $id is ambiguous" return } set id [lindex $matches 0] } } } if {[info exists idline($id)]} { selectline $idline($id) 1 return } if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} { set type "SHA1 id" } else { set type "Tag" } error_popup "$type $sha1string is not known" } proc lineenter {x y id} { global hoverx hovery hoverid hovertimer global commitinfo canv if {![info exists commitinfo($id)]} return set hoverx $x set hovery $y set hoverid $id if {[info exists hovertimer]} { after cancel $hovertimer } set hovertimer [after 500 linehover] $canv delete hover } proc linemotion {x y id} { global hoverx hovery hoverid hovertimer if {[info exists hoverid] && $id == $hoverid} { set hoverx $x set hovery $y if {[info exists hovertimer]} { after cancel $hovertimer } set hovertimer [after 500 linehover] } } proc lineleave {id} { global hoverid hovertimer canv if {[info exists hoverid] && $id == $hoverid} { $canv delete hover if {[info exists hovertimer]} { after cancel $hovertimer unset hovertimer } unset hoverid } } proc linehover {} { global hoverx hovery hoverid hovertimer global canv linespc lthickness global commitinfo mainfont set text [lindex $commitinfo($hoverid) 0] set ymax [lindex [$canv cget -scrollregion] 3] if {$ymax == {}} return set yfrac [lindex [$canv yview] 0] set x [expr {$hoverx + 2 * $linespc}] set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}] set x0 [expr {$x - 2 * $lthickness}] set y0 [expr {$y - 2 * $lthickness}] set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}] set y1 [expr {$y + $linespc + 2 * $lthickness}] set t [$canv create rectangle $x0 $y0 $x1 $y1 \ -fill \#ffff80 -outline black -width 1 -tags hover] $canv raise $t set t [$canv create text $x $y -anchor nw -text $text -tags hover] $canv raise $t } proc clickisonarrow {id y} { global mainline mainlinearrow sidelines lthickness set thresh [expr {2 * $lthickness + 6}] if {[info exists mainline($id)]} { if {$mainlinearrow($id) ne "none"} { if {abs([lindex $mainline($id) 1] - $y) < $thresh} { return "up" } } } if {[info exists sidelines($id)]} { foreach ls $sidelines($id) { set coords [lindex $ls 0] set arrow [lindex $ls 2] if {$arrow eq "first" || $arrow eq "both"} { if {abs([lindex $coords 1] - $y) < $thresh} { return "up" } } if {$arrow eq "last" || $arrow eq "both"} { if {abs([lindex $coords end] - $y) < $thresh} { return "down" } } } } return {} } proc arrowjump {id dirn y} { global mainline sidelines canv set yt {} if {$dirn eq "down"} { if {[info exists mainline($id)]} { set y1 [lindex $mainline($id) 1] if {$y1 > $y} { set yt $y1 } } if {[info exists sidelines($id)]} { foreach ls $sidelines($id) { set y1 [lindex $ls 0 1] if {$y1 > $y && ($yt eq {} || $y1 < $yt)} { set yt $y1 } } } } else { if {[info exists sidelines($id)]} { foreach ls $sidelines($id) { set y1 [lindex $ls 0 end] if {$y1 < $y && ($yt eq {} || $y1 > $yt)} { set yt $y1 } } } } if {$yt eq {}} return set ymax [lindex [$canv cget -scrollregion] 3] if {$ymax eq {} || $ymax <= 0} return set view [$canv yview] set yspan [expr {[lindex $view 1] - [lindex $view 0]}] set yfrac [expr {$yt / $ymax - $yspan / 2}] if {$yfrac < 0} { set yfrac 0 } $canv yview moveto $yfrac } proc lineclick {x y id isnew} { global ctext commitinfo children cflist canv thickerline unmarkmatches unselectline normalline $canv delete hover # draw this line thicker than normal drawlines $id 1 set thickerline $id if {$isnew} { set ymax [lindex [$canv cget -scrollregion] 3] if {$ymax eq {}} return set yfrac [lindex [$canv yview] 0] set y [expr {$y + $yfrac * $ymax}] } set dirn [clickisonarrow $id $y] if {$dirn ne {}} { arrowjump $id $dirn $y return } if {$isnew} { addtohistory [list lineclick $x $y $id 0] } # fill the details pane with info about this line $ctext conf -state normal $ctext delete 0.0 end $ctext tag conf link -foreground blue -underline 1 $ctext tag bind link { %W configure -cursor hand2 } $ctext tag bind link { %W configure -cursor $curtextcursor } $ctext insert end "Parent:\t" $ctext insert end $id [list link link0] $ctext tag bind link0 <1> [list selbyid $id] set info $commitinfo($id) $ctext insert end "\n\t[lindex $info 0]\n" $ctext insert end "\tUser:\t[lindex $info 1]\n" $ctext insert end "\tDate:\t[lindex $info 2]\n" if {[info exists children($id)]} { $ctext insert end "\nChildren:" set i 0 foreach child $children($id) { incr i set info $commitinfo($child) $ctext insert end "\n\t" $ctext insert end $child [list link link$i] $ctext tag bind link$i <1> [list selbyid $child] $ctext insert end "\n\t[lindex $info 0]" $ctext insert end "\n\tUser:\t[lindex $info 1]" $ctext insert end "\n\tDate:\t[lindex $info 2]\n" } } $ctext conf -state disabled $cflist delete 0 end } proc normalline {} { global thickerline if {[info exists thickerline]} { drawlines $thickerline 0 unset thickerline } } proc selbyid {id} { global idline if {[info exists idline($id)]} { selectline $idline($id) 1 } } proc mstime {} { global startmstime if {![info exists startmstime]} { set startmstime [clock clicks -milliseconds] } return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]] } proc rowmenu {x y id} { global rowctxmenu idline selectedline rowmenuid hgvdiff if {![info exists selectedline] || $idline($id) eq $selectedline} { set state disabled } else { set state normal } $rowctxmenu entryconfigure 0 -state $state $rowctxmenu entryconfigure 1 -state $state $rowctxmenu entryconfigure 2 -state $state if { $hgvdiff ne "" } { $rowctxmenu entryconfigure 6 -state $state } set rowmenuid $id tk_popup $rowctxmenu $x $y } proc diffvssel {dirn} { global rowmenuid selectedline lineid if {![info exists selectedline]} return if {$dirn} { set oldid $lineid($selectedline) set newid $rowmenuid } else { set oldid $rowmenuid set newid $lineid($selectedline) } addtohistory [list doseldiff $oldid $newid] doseldiff $oldid $newid } proc doseldiff {oldid newid} { global ctext cflist global commitinfo $ctext conf -state normal $ctext delete 0.0 end $ctext mark set fmark.0 0.0 $ctext mark gravity fmark.0 left $cflist delete 0 end $cflist insert end "Top" $ctext insert end "From " $ctext tag conf link -foreground blue -underline 1 $ctext tag bind link { %W configure -cursor hand2 } $ctext tag bind link { %W configure -cursor $curtextcursor } $ctext tag bind link0 <1> [list selbyid $oldid] $ctext insert end $oldid [list link link0] $ctext insert end "\n " $ctext insert end [lindex $commitinfo($oldid) 0] $ctext insert end "\n\nTo " $ctext tag bind link1 <1> [list selbyid $newid] $ctext insert end $newid [list link link1] $ctext insert end "\n " $ctext insert end [lindex $commitinfo($newid) 0] $ctext insert end "\n" $ctext conf -state disabled $ctext tag delete Comments $ctext tag remove found 1.0 end startdiff [list $newid $oldid] } proc mkpatch {} { global rowmenuid currentid commitinfo patchtop patchnum if {![info exists currentid]} return set oldid $currentid set oldhead [lindex $commitinfo($oldid) 0] set newid $rowmenuid set newhead [lindex $commitinfo($newid) 0] set top .patch set patchtop $top catch {destroy $top} toplevel $top ttk::label $top.from -text "From:" ttk::entry $top.fromsha1 -width 40 $top.fromsha1 insert 0 $oldid $top.fromsha1 conf -state readonly grid $top.from $top.fromsha1 -sticky w -pady {10 0} ttk::entry $top.fromhead -width 60 $top.fromhead insert 0 $oldhead $top.fromhead conf -state readonly grid x $top.fromhead -sticky w ttk::label $top.to -text "To:" ttk::entry $top.tosha1 -width 40 $top.tosha1 insert 0 $newid $top.tosha1 conf -state readonly grid $top.to $top.tosha1 -sticky w ttk::entry $top.tohead -width 60 $top.tohead insert 0 $newhead $top.tohead conf -state readonly grid x $top.tohead -sticky w ttk::button $top.rev -text "Reverse" -command mkpatchrev grid $top.rev x -pady 10 ttk::label $top.flab -text "Output file:" ttk::entry $top.fname -width 60 $top.fname insert 0 [file normalize "patch$patchnum.patch"] incr patchnum grid $top.flab $top.fname -sticky w ttk::frame $top.buts ttk::button $top.buts.gen -text "Generate" -command mkpatchgo ttk::button $top.buts.can -text "Cancel" -command mkpatchcan grid $top.buts.gen $top.buts.can grid columnconfigure $top.buts 0 -weight 1 -uniform a grid columnconfigure $top.buts 1 -weight 1 -uniform a grid $top.buts - -pady 10 -sticky ew focus $top.fname popupify $top wm title $top "Generate a patch" } proc mkpatchrev {} { global patchtop set oldid [$patchtop.fromsha1 get] set oldhead [$patchtop.fromhead get] set newid [$patchtop.tosha1 get] set newhead [$patchtop.tohead get] foreach e [list fromsha1 fromhead tosha1 tohead] \ v [list $newid $newhead $oldid $oldhead] { $patchtop.$e conf -state normal $patchtop.$e delete 0 end $patchtop.$e insert 0 $v $patchtop.$e conf -state readonly } } proc mkpatchgo {} { global patchtop env set oldid [$patchtop.fromsha1 get] set newid [$patchtop.tosha1 get] set fname [$patchtop.fname get] if {[catch {exec $env(HG) --config ui.report_untrusted=false debug-diff-tree -p $oldid $newid >$fname &} err]} { error_popup "Error creating patch: $err" } catch {destroy $patchtop} unset patchtop } proc mkpatchcan {} { global patchtop catch {destroy $patchtop} unset patchtop } proc mktag {} { global rowmenuid mktagtop commitinfo set top .maketag set mktagtop $top catch {destroy $top} toplevel $top ttk::label $top.id -text "ID:" ttk::entry $top.sha1 -width 40 $top.sha1 insert 0 $rowmenuid $top.sha1 conf -state readonly grid $top.id $top.sha1 -sticky w -pady {10 0} ttk::entry $top.head -width 60 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0] $top.head conf -state readonly grid x $top.head -sticky w ttk::label $top.tlab -text "Tag name:" ttk::entry $top.tag -width 60 grid $top.tlab $top.tag -sticky w ttk::frame $top.buts ttk::button $top.buts.gen -text "Create" -command mktaggo ttk::button $top.buts.can -text "Cancel" -command mktagcan grid $top.buts.gen $top.buts.can grid columnconfigure $top.buts 0 -weight 1 -uniform a grid columnconfigure $top.buts 1 -weight 1 -uniform a grid $top.buts - -pady 10 -sticky ew focus $top.tag popupify $top wm title $top "Create a tag" } proc domktag {} { global mktagtop env tagids idtags set id [$mktagtop.sha1 get] set tag [$mktagtop.tag get] if {$tag == {}} { error_popup "No tag name specified" return } if {[info exists tagids($tag)]} { error_popup "Tag \"$tag\" already exists" return } if {[catch { set out [exec $env(HG) --config ui.report_untrusted=false tag -r $id $tag] } err]} { error_popup "Error creating tag: $err" return } set tagids($tag) $id lappend idtags($id) $tag redrawtags $id } proc redrawtags {id} { global canv linehtag idline idpos selectedline if {![info exists idline($id)]} return $canv delete tag.$id set xt [eval drawtags $id $idpos($id)] $canv coords $linehtag($idline($id)) $xt [lindex $idpos($id) 2] if {[info exists selectedline] && $selectedline == $idline($id)} { selectline $selectedline 0 } } proc mktagcan {} { global mktagtop catch {destroy $mktagtop} unset mktagtop } proc mktaggo {} { domktag mktagcan } proc writecommit {} { global rowmenuid wrcomtop commitinfo wrcomcmd set top .writecommit set wrcomtop $top catch {destroy $top} toplevel $top ttk::label $top.id -text "ID:" ttk::entry $top.sha1 -width 40 $top.sha1 insert 0 $rowmenuid $top.sha1 conf -state readonly grid $top.id $top.sha1 -sticky w -pady {10 0} ttk::entry $top.head -width 60 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0] $top.head conf -state readonly grid x $top.head -sticky w ttk::label $top.clab -text "Command:" ttk::entry $top.cmd -width 60 -textvariable wrcomcmd grid $top.clab $top.cmd -sticky w -pady 10 ttk::label $top.flab -text "Output file:" ttk::entry $top.fname -width 60 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"] grid $top.flab $top.fname -sticky w ttk::frame $top.buts ttk::button $top.buts.gen -text "Write" -command wrcomgo ttk::button $top.buts.can -text "Cancel" -command wrcomcan grid $top.buts.gen $top.buts.can grid columnconfigure $top.buts 0 -weight 1 -uniform a grid columnconfigure $top.buts 1 -weight 1 -uniform a grid $top.buts - -pady 10 -sticky ew focus $top.fname popupify $top wm title $top "Write commit to a file" } proc wrcomgo {} { global wrcomtop set id [$wrcomtop.sha1 get] set cmd "echo $id | [$wrcomtop.cmd get]" set fname [$wrcomtop.fname get] if {[catch {exec sh -c $cmd > $fname &} err]} { error_popup "Error writing commit: $err" } catch {destroy $wrcomtop} unset wrcomtop } proc wrcomcan {} { global wrcomtop catch {destroy $wrcomtop} unset wrcomtop } proc listrefs {id} { global idtags idheads idotherrefs idbookmarks set w {} if {[info exists idbookmarks($id)]} { set w $idbookmarks($id) } set x {} if {[info exists idtags($id)]} { set x $idtags($id) } set y {} if {[info exists idheads($id)]} { set y $idheads($id) } set z {} if {[info exists idotherrefs($id)]} { set z $idotherrefs($id) } return [list $w $x $y $z] } proc rereadrefs {} { global idbookmarks idtags idheads idotherrefs global bookmarkids tagids headids otherrefids set refids [concat [array names idtags] \ [array names idheads] [array names idotherrefs] \ [array names idbookmarks]] foreach id $refids { if {![info exists ref($id)]} { set ref($id) [listrefs $id] } } foreach v {tagids idtags headids idheads otherrefids idotherrefs \ bookmarkids idbookmarks} { catch {unset $v} } readrefs set refids [lsort -unique [concat $refids [array names idtags] \ [array names idheads] [array names idotherrefs] \ [array names idbookmarks]]] foreach id $refids { set v [listrefs $id] if {![info exists ref($id)] || $ref($id) != $v} { redrawtags $id } } } proc vdiff {withparent} { global env rowmenuid selectedline lineid hgvdiff if {![info exists rowmenuid]} return set curid $rowmenuid if {$withparent} { set parents [exec $env(HG) --config ui.report_untrusted=false parents --rev $curid --template "{node}\n"] set firstparent [lindex [split $parents "\n"] 0] set otherid $firstparent } else { if {![info exists selectedline]} return set otherid $lineid($selectedline) } set range "$otherid:$curid" if {[catch {exec $env(HG) --config ui.report_untrusted=false $hgvdiff -r $range} err]} { # Ignore errors, this is just visualization } } proc showtag {tag isnew} { global ctext cflist tagcontents tagids linknum if {$isnew} { addtohistory [list showtag $tag 0] } $ctext conf -state normal $ctext delete 0.0 end set linknum 0 if {[info exists tagcontents($tag)]} { set text $tagcontents($tag) } else { set text "Tag: $tag\nId: $tagids($tag)" } appendwithlinks $text $ctext conf -state disabled $cflist delete 0 end } proc doquit {} { global stopped set stopped 100 destroy . } proc getconfig {} { global env set lines [exec $env(HG) debug-config] regsub -all "\r\n" $lines "\n" config set config {} foreach line [split $lines "\n"] { regsub "^(k|v)=" $line "" line lappend config $line } return $config } # defaults... set datemode 0 set boldnames 0 set diffopts "-U 5 -p" set wrcomcmd "\"\$HG\" --config ui.report_untrusted=false debug-diff-tree --stdin -p --pretty" set mainfont {Helvetica 9} set curidfont {} set textfont {Courier 9} set findmergefiles 0 set gaudydiff 0 set maxgraphpct 50 set maxwidth 16 set colors {green red blue magenta darkgrey brown orange} set authorcolors { black blue deeppink mediumorchid blue burlywood4 goldenrod slateblue red2 navy dimgrey } set bgcolor white # This color should probably be some system color (provided by tk), # but as the bgcolor has always been set to white, I choose to ignore set fgcolor black set diffaddcolor "#00a000" set diffremcolor red set diffmerge1color red set diffmerge2color blue set hunksepcolor blue catch {source ~/.hgk} if {$curidfont == ""} { # initialize late based on current mainfont set curidfont "$mainfont bold italic underline" } set namefont $mainfont if {$boldnames} { lappend namefont bold } set revtreeargs {} foreach arg $argv { switch -regexp -- $arg { "^$" { } "^-b" { set boldnames 1 } "^-d" { set datemode 1 } default { lappend revtreeargs $arg } } } set history {} set historyindex 0 set stopped 0 set redisplaying 0 set stuffsaved 0 set patchnum 0 array set config [getconfig] set hgvdiff $config(vdiff) setcoords makewindow readrefs set hgroot [exec $env(HG) root] wm title . "hgk $hgroot" getcommits $revtreeargs mercurial-2.8.2/contrib/perf.py0000644000175000017500000002707012261160437020272 0ustar oxymoronoxymoron00000000000000# perf.py - performance test routines '''helper extension to measure performance''' from mercurial import cmdutil, scmutil, util, commands, obsolete from mercurial import repoview, branchmap, merge, copies import time, os, sys cmdtable = {} command = cmdutil.command(cmdtable) def timer(func, title=None): results = [] begin = time.time() count = 0 while True: ostart = os.times() cstart = time.time() r = func() cstop = time.time() ostop = os.times() count += 1 a, b = ostart, ostop results.append((cstop - cstart, b[0] - a[0], b[1]-a[1])) if cstop - begin > 3 and count >= 100: break if cstop - begin > 10 and count >= 3: break if title: sys.stderr.write("! %s\n" % title) if r: sys.stderr.write("! result: %s\n" % r) m = min(results) sys.stderr.write("! wall %f comb %f user %f sys %f (best of %d)\n" % (m[0], m[1] + m[2], m[1], m[2], count)) @command('perfwalk') def perfwalk(ui, repo, *pats): try: m = scmutil.match(repo[None], pats, {}) timer(lambda: len(list(repo.dirstate.walk(m, [], True, False)))) except Exception: try: m = scmutil.match(repo[None], pats, {}) timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)])) except Exception: timer(lambda: len(list(cmdutil.walk(repo, pats, {})))) @command('perfannotate') def perfannotate(ui, repo, f): fc = repo['.'][f] timer(lambda: len(fc.annotate(True))) @command('perfstatus', [('u', 'unknown', False, 'ask status to look for unknown files')]) def perfstatus(ui, repo, **opts): #m = match.always(repo.root, repo.getcwd()) #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False, # False)))) timer(lambda: sum(map(len, repo.status(**opts)))) @command('perfaddremove') def perfaddremove(ui, repo): try: oldquiet = repo.ui.quiet repo.ui.quiet = True timer(lambda: scmutil.addremove(repo, dry_run=True)) finally: repo.ui.quiet = oldquiet def clearcaches(cl): # behave somewhat consistently across internal API changes if util.safehasattr(cl, 'clearcaches'): cl.clearcaches() elif util.safehasattr(cl, '_nodecache'): from mercurial.node import nullid, nullrev cl._nodecache = {nullid: nullrev} cl._nodepos = None @command('perfheads') def perfheads(ui, repo): cl = repo.changelog def d(): len(cl.headrevs()) clearcaches(cl) timer(d) @command('perftags') def perftags(ui, repo): import mercurial.changelog import mercurial.manifest def t(): repo.changelog = mercurial.changelog.changelog(repo.sopener) repo.manifest = mercurial.manifest.manifest(repo.sopener) repo._tags = None return len(repo.tags()) timer(t) @command('perfancestors') def perfancestors(ui, repo): heads = repo.changelog.headrevs() def d(): for a in repo.changelog.ancestors(heads): pass timer(d) @command('perfancestorset') def perfancestorset(ui, repo, revset): revs = repo.revs(revset) heads = repo.changelog.headrevs() def d(): s = repo.changelog.ancestors(heads) for rev in revs: rev in s timer(d) @command('perfdirs') def perfdirs(ui, repo): dirstate = repo.dirstate 'a' in dirstate def d(): dirstate.dirs() del dirstate._dirs timer(d) @command('perfdirstate') def perfdirstate(ui, repo): "a" in repo.dirstate def d(): repo.dirstate.invalidate() "a" in repo.dirstate timer(d) @command('perfdirstatedirs') def perfdirstatedirs(ui, repo): "a" in repo.dirstate def d(): "a" in repo.dirstate._dirs del repo.dirstate._dirs timer(d) @command('perfdirstatewrite') def perfdirstatewrite(ui, repo): ds = repo.dirstate "a" in ds def d(): ds._dirty = True ds.write() timer(d) @command('perfmergecalculate', [('r', 'rev', '.', 'rev to merge against')]) def perfmergecalculate(ui, repo, rev): wctx = repo[None] rctx = scmutil.revsingle(repo, rev, rev) ancestor = wctx.ancestor(rctx) # we don't want working dir files to be stat'd in the benchmark, so prime # that cache wctx.dirty() def d(): # acceptremote is True because we don't want prompts in the middle of # our benchmark merge.calculateupdates(repo, wctx, rctx, ancestor, False, False, False, acceptremote=True) timer(d) @command('perfpathcopies', [], "REV REV") def perfpathcopies(ui, repo, rev1, rev2): ctx1 = scmutil.revsingle(repo, rev1, rev1) ctx2 = scmutil.revsingle(repo, rev2, rev2) def d(): copies.pathcopies(ctx1, ctx2) timer(d) @command('perfmanifest', [], 'REV') def perfmanifest(ui, repo, rev): ctx = scmutil.revsingle(repo, rev, rev) t = ctx.manifestnode() def d(): repo.manifest._mancache.clear() repo.manifest._cache = None repo.manifest.read(t) timer(d) @command('perfchangeset') def perfchangeset(ui, repo, rev): n = repo[rev].node() def d(): repo.changelog.read(n) #repo.changelog._cache = None timer(d) @command('perfindex') def perfindex(ui, repo): import mercurial.revlog mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg n = repo["tip"].node() def d(): cl = mercurial.revlog.revlog(repo.sopener, "00changelog.i") cl.rev(n) timer(d) @command('perfstartup') def perfstartup(ui, repo): cmd = sys.argv[0] def d(): os.system("HGRCPATH= %s version -q > /dev/null" % cmd) timer(d) @command('perfparents') def perfparents(ui, repo): nl = [repo.changelog.node(i) for i in xrange(1000)] def d(): for n in nl: repo.changelog.parents(n) timer(d) @command('perflookup') def perflookup(ui, repo, rev): timer(lambda: len(repo.lookup(rev))) @command('perfrevrange') def perfrevrange(ui, repo, *specs): revrange = scmutil.revrange timer(lambda: len(revrange(repo, specs))) @command('perfnodelookup') def perfnodelookup(ui, repo, rev): import mercurial.revlog mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg n = repo[rev].node() cl = mercurial.revlog.revlog(repo.sopener, "00changelog.i") def d(): cl.rev(n) clearcaches(cl) timer(d) @command('perflog', [('', 'rename', False, 'ask log to follow renames')]) def perflog(ui, repo, **opts): ui.pushbuffer() timer(lambda: commands.log(ui, repo, rev=[], date='', user='', copies=opts.get('rename'))) ui.popbuffer() @command('perftemplating') def perftemplating(ui, repo): ui.pushbuffer() timer(lambda: commands.log(ui, repo, rev=[], date='', user='', template='{date|shortdate} [{rev}:{node|short}]' ' {author|person}: {desc|firstline}\n')) ui.popbuffer() @command('perfcca') def perfcca(ui, repo): timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate)) @command('perffncacheload') def perffncacheload(ui, repo): s = repo.store def d(): s.fncache._load() timer(d) @command('perffncachewrite') def perffncachewrite(ui, repo): s = repo.store s.fncache._load() def d(): s.fncache._dirty = True s.fncache.write() timer(d) @command('perffncacheencode') def perffncacheencode(ui, repo): s = repo.store s.fncache._load() def d(): for p in s.fncache.entries: s.encode(p) timer(d) @command('perfdiffwd') def perfdiffwd(ui, repo): """Profile diff of working directory changes""" options = { 'w': 'ignore_all_space', 'b': 'ignore_space_change', 'B': 'ignore_blank_lines', } for diffopt in ('', 'w', 'b', 'B', 'wB'): opts = dict((options[c], '1') for c in diffopt) def d(): ui.pushbuffer() commands.diff(ui, repo, **opts) ui.popbuffer() title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none') timer(d, title) @command('perfrevlog', [('d', 'dist', 100, 'distance between the revisions')], "[INDEXFILE]") def perfrevlog(ui, repo, file_, **opts): from mercurial import revlog dist = opts['dist'] def d(): r = revlog.revlog(lambda fn: open(fn, 'rb'), file_) for x in xrange(0, len(r), dist): r.revision(r.node(x)) timer(d) @command('perfrevset', [('C', 'clear', False, 'clear volatile cache between each call.')], "REVSET") def perfrevset(ui, repo, expr, clear=False): """benchmark the execution time of a revset Use the --clean option if need to evaluate the impact of build volatile revisions set cache on the revset execution. Volatile cache hold filtered and obsolete related cache.""" def d(): if clear: repo.invalidatevolatilesets() repo.revs(expr) timer(d) @command('perfvolatilesets') def perfvolatilesets(ui, repo, *names): """benchmark the computation of various volatile set Volatile set computes element related to filtering and obsolescence.""" repo = repo.unfiltered() def getobs(name): def d(): repo.invalidatevolatilesets() obsolete.getrevs(repo, name) return d allobs = sorted(obsolete.cachefuncs) if names: allobs = [n for n in allobs if n in names] for name in allobs: timer(getobs(name), title=name) def getfiltered(name): def d(): repo.invalidatevolatilesets() repoview.filteredrevs(repo, name) return d allfilter = sorted(repoview.filtertable) if names: allfilter = [n for n in allfilter if n in names] for name in allfilter: timer(getfiltered(name), title=name) @command('perfbranchmap', [('f', 'full', False, 'Includes build time of subset'), ]) def perfbranchmap(ui, repo, full=False): """benchmark the update of a branchmap This benchmarks the full repo.branchmap() call with read and write disabled """ def getbranchmap(filtername): """generate a benchmark function for the filtername""" if filtername is None: view = repo else: view = repo.filtered(filtername) def d(): if full: view._branchcaches.clear() else: view._branchcaches.pop(filtername, None) view.branchmap() return d # add filter in smaller subset to bigger subset possiblefilters = set(repoview.filtertable) allfilters = [] while possiblefilters: for name in possiblefilters: subset = repoview.subsettable.get(name) if subset not in possiblefilters: break else: assert False, 'subset cycle %s!' % possiblefilters allfilters.append(name) possiblefilters.remove(name) # warm the cache if not full: for name in allfilters: repo.filtered(name).branchmap() # add unfiltered allfilters.append(None) oldread = branchmap.read oldwrite = branchmap.branchcache.write try: branchmap.read = lambda repo: None branchmap.write = lambda repo: None for name in allfilters: timer(getbranchmap(name), title=str(name)) finally: branchmap.read = oldread branchmap.branchcache.write = oldwrite mercurial-2.8.2/contrib/hgweb.wsgi0000644000175000017500000000125012261160437020743 0ustar oxymoronoxymoron00000000000000# An example WSGI for use with mod_wsgi, edit as necessary # See http://mercurial.selenic.com/wiki/modwsgi for more information # Path to repo or hgweb config to serve (see 'hg help hgweb') config = "/path/to/repo/or/config" # Uncomment and adjust if Mercurial is not installed system-wide # (consult "installed modules" path from 'hg debuginstall'): #import sys; sys.path.insert(0, "/path/to/python/lib") # Uncomment to send python tracebacks to the browser if an error occurs: #import cgitb; cgitb.enable() # enable demandloading to reduce startup time from mercurial import demandimport; demandimport.enable() from mercurial.hgweb import hgweb application = hgweb(config) mercurial-2.8.2/contrib/casesmash.py0000644000175000017500000000156412261160437021305 0ustar oxymoronoxymoron00000000000000import os, __builtin__ from mercurial import util def lowerwrap(scope, funcname): f = getattr(scope, funcname) def wrap(fname, *args, **kwargs): d, base = os.path.split(fname) try: files = os.listdir(d or '.') except OSError: files = [] if base in files: return f(fname, *args, **kwargs) for fn in files: if fn.lower() == base.lower(): return f(os.path.join(d, fn), *args, **kwargs) return f(fname, *args, **kwargs) scope.__dict__[funcname] = wrap def normcase(path): return path.lower() os.path.normcase = normcase for f in 'file open'.split(): lowerwrap(__builtin__, f) for f in "chmod chown open lstat stat remove unlink".split(): lowerwrap(os, f) for f in "exists lexists".split(): lowerwrap(os.path, f) lowerwrap(util, 'posixfile') mercurial-2.8.2/contrib/bash_completion0000644000175000017500000002673012261160437022057 0ustar oxymoronoxymoron00000000000000# bash completion for the Mercurial distributed SCM -*- sh -*- # Docs: # # If you source this file from your .bashrc, bash should be able to # complete a command line that uses hg with all the available commands # and options and sometimes even arguments. # # Mercurial allows you to define additional commands through extensions. # Bash should be able to automatically figure out the name of these new # commands and their options. See below for how to define _hg_opt_foo # and _hg_cmd_foo functions to fine-tune the completion for option and # non-option arguments, respectively. # # # Notes about completion for specific commands: # # - the completion function for the email command from the patchbomb # extension will try to call _hg_emails to get a list of e-mail # addresses. It's up to the user to define this function. For # example, put the addresses of the lists that you usually patchbomb # in ~/.patchbomb-to and the addresses that you usually use to send # the patchbombs in ~/.patchbomb-from and use something like this: # # _hg_emails() # { # if [ -r ~/.patchbomb-$1 ]; then # cat ~/.patchbomb-$1 # fi # } # # # Writing completion functions for additional commands: # # If it exists, the function _hg_cmd_foo will be called without # arguments to generate the completion candidates for the hg command # "foo". If the command receives some arguments that aren't options # even though they start with a "-", you can define a function called # _hg_opt_foo to generate the completion candidates. If _hg_opt_foo # doesn't return 0, regular completion for options is attempted. # # In addition to the regular completion variables provided by bash, # the following variables are also set: # - $hg - the hg program being used (e.g. /usr/bin/hg) # - $cmd - the name of the hg command being completed # - $cmd_index - the index of $cmd in $COMP_WORDS # - $cur - the current argument being completed # - $prev - the argument before $cur # - $global_args - "|"-separated list of global options that accept # an argument (e.g. '--cwd|-R|--repository') # - $canonical - 1 if we canonicalized $cmd before calling the function # 0 otherwise # shopt -s extglob _hg_cmd() { HGPLAIN=1 "$hg" "$@" 2>/dev/null } _hg_commands() { local commands commands="$(HGPLAINEXCEPT=alias _hg_cmd debugcomplete "$cur")" || commands="" COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$commands' -- "$cur")) } _hg_paths() { local paths="$(_hg_cmd paths -q)" COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$paths' -- "$cur")) } _hg_repos() { local i for i in $(compgen -d -- "$cur"); do test ! -d "$i"/.hg || COMPREPLY=(${COMPREPLY[@]:-} "$i") done } _hg_debugpathcomplete() { local files="$(_hg_cmd debugpathcomplete $1 "$cur")" local IFS=$'\n' compopt -o filenames 2>/dev/null COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur")) } _hg_status() { local files="$(_hg_cmd status -n$1 "glob:$cur**")" local IFS=$'\n' compopt -o filenames 2>/dev/null COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur")) } _hg_bookmarks() { local bookmarks="$(_hg_cmd bookmarks -q)" local IFS=$'\n' COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$bookmarks' -- "$cur")) } _hg_labels() { local labels="$(_hg_cmd debuglabelcomplete "$cur")" local IFS=$'\n' COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$labels' -- "$cur")) } # this is "kind of" ugly... _hg_count_non_option() { local i count=0 local filters="$1" for ((i=1; $i<=$COMP_CWORD; i++)); do if [[ "${COMP_WORDS[i]}" != -* ]]; then if [[ ${COMP_WORDS[i-1]} == @($filters|$global_args) ]]; then continue fi count=$(($count + 1)) fi done echo $(($count - 1)) } _hg() { local cur prev cmd cmd_index opts i # global options that receive an argument local global_args='--cwd|-R|--repository' local hg="$1" local canonical=0 COMPREPLY=() cur="$2" prev="$3" # searching for the command # (first non-option argument that doesn't follow a global option that # receives an argument) for ((i=1; $i<=$COMP_CWORD; i++)); do if [[ ${COMP_WORDS[i]} != -* ]]; then if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then cmd="${COMP_WORDS[i]}" cmd_index=$i break fi fi done if [[ "$cur" == -* ]]; then if [ "$(type -t "_hg_opt_$cmd")" = function ] && "_hg_opt_$cmd"; then return fi opts=$(_hg_cmd debugcomplete --options "$cmd") COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$opts' -- "$cur")) return fi # global options case "$prev" in -R|--repository) _hg_paths _hg_repos return ;; --cwd) # Stick with default bash completion return ;; esac if [ -z "$cmd" ] || [ $COMP_CWORD -eq $i ]; then _hg_commands return fi # try to generate completion candidates for whatever command the user typed local help if _hg_command_specific; then return fi # canonicalize the command name and try again help=$(_hg_cmd help "$cmd") if [ $? -ne 0 ]; then # Probably either the command doesn't exist or it's ambiguous return fi cmd=${help#hg } cmd=${cmd%%[$' \n']*} canonical=1 _hg_command_specific } _hg_command_specific() { if [ "$(type -t "_hg_cmd_$cmd")" = function ]; then "_hg_cmd_$cmd" return 0 fi if [ "$cmd" != status ] && [ "$prev" = -r ] || [ "$prev" == --rev ]; then if [ $canonical = 1 ]; then _hg_labels return 0 elif [[ status != "$cmd"* ]]; then _hg_labels return 0 else return 1 fi fi case "$cmd" in help) _hg_commands ;; export) if _hg_ext_mq_patchlist qapplied && [ "${COMPREPLY[*]}" ]; then return 0 fi _hg_labels ;; manifest|update|up|checkout|co) _hg_labels ;; pull|push|outgoing|incoming) _hg_paths _hg_repos ;; paths) _hg_paths ;; add) _hg_status "u" ;; merge) _hg_labels ;; commit|ci|record) _hg_status "mar" ;; remove|rm) _hg_debugpathcomplete -n ;; forget) _hg_debugpathcomplete -fa ;; diff) _hg_status "mar" ;; revert) _hg_debugpathcomplete ;; clone) local count=$(_hg_count_non_option) if [ $count = 1 ]; then _hg_paths fi _hg_repos ;; debugindex|debugindexdot) COMPREPLY=(${COMPREPLY[@]:-} $(compgen -f -X "!*.i" -- "$cur")) ;; debugdata) COMPREPLY=(${COMPREPLY[@]:-} $(compgen -f -X "!*.d" -- "$cur")) ;; *) return 1 ;; esac return 0 } complete -o bashdefault -o default -F _hg hg \ || complete -o default -F _hg hg # Completion for commands provided by extensions # bookmarks _hg_cmd_bookmarks() { if [[ "$prev" = @(-d|--delete|-m|--rename) ]]; then _hg_bookmarks return fi } # mq _hg_ext_mq_patchlist() { local patches patches=$(_hg_cmd $1) if [ $? -eq 0 ] && [ "$patches" ]; then COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$patches' -- "$cur")) return 0 fi return 1 } _hg_ext_mq_queues() { local root=$(_hg_cmd root) local n for n in $(cd "$root"/.hg && compgen -d -- "$cur"); do # I think we're usually not interested in the regular "patches" queue # so just filter it. if [ "$n" != patches ] && [ -e "$root/.hg/$n/series" ]; then COMPREPLY=(${COMPREPLY[@]:-} "$n") fi done } _hg_cmd_qpop() { if [[ "$prev" = @(-n|--name) ]]; then _hg_ext_mq_queues return fi _hg_ext_mq_patchlist qapplied } _hg_cmd_qpush() { if [[ "$prev" = @(-n|--name) ]]; then _hg_ext_mq_queues return fi _hg_ext_mq_patchlist qunapplied } _hg_cmd_qgoto() { if [[ "$prev" = @(-n|--name) ]]; then _hg_ext_mq_queues return fi _hg_ext_mq_patchlist qseries } _hg_cmd_qdelete() { local qcmd=qunapplied if [[ "$prev" = @(-r|--rev) ]]; then qcmd=qapplied fi _hg_ext_mq_patchlist $qcmd } _hg_cmd_qfinish() { if [[ "$prev" = @(-a|--applied) ]]; then return fi _hg_ext_mq_patchlist qapplied } _hg_cmd_qsave() { if [[ "$prev" = @(-n|--name) ]]; then _hg_ext_mq_queues return fi } _hg_cmd_rebase() { if [[ "$prev" = @(-s|--source|-d|--dest|-b|--base|-r|--rev) ]]; then _hg_labels return fi } _hg_cmd_strip() { _hg_labels } _hg_cmd_qcommit() { local root=$(_hg_cmd root) # this is run in a sub-shell, so we can't use _hg_status local files=$(cd "$root/.hg/patches" && _hg_cmd status -nmar) COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur")) } _hg_cmd_qfold() { _hg_ext_mq_patchlist qunapplied } _hg_cmd_qrename() { _hg_ext_mq_patchlist qseries } _hg_cmd_qheader() { _hg_ext_mq_patchlist qseries } _hg_cmd_qclone() { local count=$(_hg_count_non_option) if [ $count = 1 ]; then _hg_paths fi _hg_repos } _hg_ext_mq_guards() { _hg_cmd qselect --series | sed -e 's/^.//' } _hg_cmd_qselect() { local guards=$(_hg_ext_mq_guards) COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$guards' -- "$cur")) } _hg_cmd_qguard() { local prefix='' if [[ "$cur" == +* ]]; then prefix=+ elif [[ "$cur" == -* ]]; then prefix=- fi local ncur=${cur#[-+]} if ! [ "$prefix" ]; then _hg_ext_mq_patchlist qseries return fi local guards=$(_hg_ext_mq_guards) COMPREPLY=(${COMPREPLY[@]:-} $(compgen -P $prefix -W '$guards' -- "$ncur")) } _hg_opt_qguard() { local i for ((i=cmd_index+1; i<=COMP_CWORD; i++)); do if [[ ${COMP_WORDS[i]} != -* ]]; then if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then _hg_cmd_qguard return 0 fi elif [ "${COMP_WORDS[i]}" = -- ]; then _hg_cmd_qguard return 0 fi done return 1 } _hg_cmd_qqueue() { local q local queues local opts="--list --create --rename --delete --purge" queues=$( _hg_cmd qqueue --quiet ) COMPREPLY=( $( compgen -W "${opts} ${queues}" "${cur}" ) ) } # hbisect _hg_cmd_bisect() { local i subcmd # find the sub-command for ((i=cmd_index+1; i<=COMP_CWORD; i++)); do if [[ ${COMP_WORDS[i]} != -* ]]; then if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then subcmd="${COMP_WORDS[i]}" break fi fi done if [ -z "$subcmd" ] || [ $COMP_CWORD -eq $i ] || [ "$subcmd" = help ]; then COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W 'bad good help init next reset' -- "$cur")) return fi case "$subcmd" in good|bad) _hg_labels ;; esac return } # patchbomb _hg_cmd_email() { case "$prev" in -c|--cc|-t|--to|-f|--from|--bcc) # we need an e-mail address. let the user provide a function # to get them if [ "$(type -t _hg_emails)" = function ]; then local arg=to if [[ "$prev" == @(-f|--from) ]]; then arg=from fi local addresses=$(_hg_emails $arg) COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$addresses' -- "$cur")) fi return ;; -m|--mbox) # fallback to standard filename completion return ;; -s|--subject) # free form string return ;; esac _hg_labels return } # gpg _hg_cmd_sign() { _hg_labels } # transplant _hg_cmd_transplant() { case "$prev" in -s|--source) _hg_paths _hg_repos return ;; --filter) # standard filename completion return ;; esac # all other transplant options values and command parameters are revisions _hg_labels return } # shelve _hg_shelves() { local shelves="$(_hg_cmd unshelve -l .)" local IFS=$'\n' COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$shelves' -- "$cur")) } _hg_cmd_shelve() { _hg_status "mard" } _hg_cmd_unshelve() { _hg_shelves } mercurial-2.8.2/contrib/tcsh_completion0000644000175000017500000000305512261160437022076 0ustar oxymoronoxymoron00000000000000# # tcsh completion for Mercurial # # This file has been auto-generated by tcsh_completion_build.sh for # Mercurial Distributed SCM (version 1.7.5+157-8a220ae0b2ba) # # Copyright (C) 2005 TK Soh. # # This is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free # Software Foundation; either version 2 of the License, or (at your # option) any later version. # complete hg \ 'n/--cwd/d/' 'n/-R/d/' 'n/--repository/d/' \ 'C/-/( -R --repository \ --cwd \ -y --noninteractive \ -q --quiet \ -v --verbose \ --config \ --debug \ --debugger \ --encoding \ --encodingmode \ --traceback \ --time \ --profile \ --version \ -h --help)/' \ 'p/1/(add addremove annotate blame archive \ backout bisect bookmarks branch branches \ bundle cat clone commit ci \ copy cp debugancestor debugbuilddag debugcheckstate \ debugcommands debugcomplete debugdag debugdata debugdate \ debugfsinfo debugignore debugindex debugindexdot debuginstall \ debugpushkey debugrebuildstate debugrename debugrevspec debugsetparents \ debugstate debugsub debugwalk diff export \ forget grep heads help identify \ id import patch incoming in \ init locate log history manifest \ merge outgoing out parents paths \ pull push recover remove rm \ rename move mv resolve revert \ rollback root serve showconfig debugconfig \ status st summary sum tag \ tags tip unbundle update up \ checkout co verify version)/' mercurial-2.8.2/contrib/hgsh/0000755000175000017500000000000012261160455017707 5ustar oxymoronoxymoron00000000000000mercurial-2.8.2/contrib/hgsh/hgsh.c0000644000175000017500000002115712261160437021012 0ustar oxymoronoxymoron00000000000000/* * hgsh.c - restricted login shell for mercurial * * Copyright 2006 Vadim Gelfer * * This software may be used and distributed according to the terms of the * GNU General Public License, incorporated herein by reference. * * this program is login shell for dedicated mercurial user account. it * only allows few actions: * * 1. run hg in server mode on specific repository. no other hg commands * are allowed. we try to verify that repo to be accessed exists under * given top-level directory. * * 2. (optional) forward ssh connection from firewall/gateway machine to * "real" mercurial host, to let users outside intranet pull and push * changes through firewall. * * 3. (optional) run normal shell, to allow to "su" to mercurial user, use * "sudo" to run programs as that user, or run cron jobs as that user. * * only tested on linux yet. patches for non-linux systems welcome. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE /* for asprintf */ #endif #include #include #include #include #include #include #include /* * user config. * * if you see a hostname below, just use first part of hostname. example, * if you have host named foo.bar.com, use "foo". */ /* * HG_GATEWAY: hostname of gateway/firewall machine that people outside your * intranet ssh into if they need to ssh to other machines. if you do not * have such machine, set to NULL. */ #ifndef HG_GATEWAY #define HG_GATEWAY "gateway" #endif /* * HG_HOST: hostname of mercurial server. if any machine is allowed, set to * NULL. */ #ifndef HG_HOST #define HG_HOST "mercurial" #endif /* * HG_USER: username to log in from HG_GATEWAY to HG_HOST. if gateway and * host username are same, set to NULL. */ #ifndef HG_USER #define HG_USER "hg" #endif /* * HG_ROOT: root of tree full of mercurial repos. if you do not want to * validate location of repo when someone is try to access, set to NULL. */ #ifndef HG_ROOT #define HG_ROOT "/home/hg/repos" #endif /* * HG: path to the mercurial executable to run. */ #ifndef HG #define HG "/home/hg/bin/hg" #endif /* * HG_SHELL: shell to use for actions like "sudo" and "su" access to * mercurial user, and cron jobs. if you want to make these things * impossible, set to NULL. */ #ifndef HG_SHELL #define HG_SHELL NULL /* #define HG_SHELL "/bin/bash" */ #endif /* * HG_HELP: some way for users to get support if they have problem. if they * should not get helpful message, set to NULL. */ #ifndef HG_HELP #define HG_HELP "please contact support@example.com for help." #endif /* * SSH: path to ssh executable to run, if forwarding from HG_GATEWAY to * HG_HOST. if you want to use rsh instead (why?), you need to modify * arguments it is called with. see forward_through_gateway. */ #ifndef SSH #define SSH "/usr/bin/ssh" #endif /* * tell whether to print command that is to be executed. useful for * debugging. should not interfere with mercurial operation, since * mercurial only cares about stdin and stdout, and this prints to stderr. */ static const int debug = 0; static void print_cmdline(int argc, char **argv) { FILE *fp = stderr; int i; fputs("command: ", fp); for (i = 0; i < argc; i++) { char *spc = strpbrk(argv[i], " \t\r\n"); if (spc) { fputc('\'', fp); } fputs(argv[i], fp); if (spc) { fputc('\'', fp); } if (i < argc - 1) { fputc(' ', fp); } } fputc('\n', fp); fflush(fp); } static void usage(const char *reason, int exitcode) { char *hg_help = HG_HELP; if (reason) { fprintf(stderr, "*** Error: %s.\n", reason); } fprintf(stderr, "*** This program has been invoked incorrectly.\n"); if (hg_help) { fprintf(stderr, "*** %s\n", hg_help); } exit(exitcode ? exitcode : EX_USAGE); } /* * run on gateway host to make another ssh connection, to "real" mercurial * server. it sends its command line unmodified to far end. * * never called if HG_GATEWAY is NULL. */ static void forward_through_gateway(int argc, char **argv) { char *ssh = SSH; char *hg_host = HG_HOST; char *hg_user = HG_USER; char **nargv = alloca((10 + argc) * sizeof(char *)); int i = 0, j; nargv[i++] = ssh; nargv[i++] = "-q"; nargv[i++] = "-T"; nargv[i++] = "-x"; if (hg_user) { nargv[i++] = "-l"; nargv[i++] = hg_user; } nargv[i++] = hg_host; /* * sshd called us with added "-c", because it thinks we are a shell. * drop it if we find it. */ j = 1; if (j < argc && strcmp(argv[j], "-c") == 0) { j++; } for (; j < argc; i++, j++) { nargv[i] = argv[j]; } nargv[i] = NULL; if (debug) { print_cmdline(i, nargv); } execv(ssh, nargv); perror(ssh); exit(EX_UNAVAILABLE); } /* * run shell. let administrator "su" to mercurial user's account to do * administrative works. * * never called if HG_SHELL is NULL. */ static void run_shell(int argc, char **argv) { char *hg_shell = HG_SHELL; char **nargv; char *c; int i; nargv = alloca((argc + 3) * sizeof(char *)); c = strrchr(hg_shell, '/'); /* tell "real" shell it is login shell, if needed. */ if (argv[0][0] == '-' && c) { nargv[0] = strdup(c); if (nargv[0] == NULL) { perror("malloc"); exit(EX_OSERR); } nargv[0][0] = '-'; } else { nargv[0] = hg_shell; } for (i = 1; i < argc; i++) { nargv[i] = argv[i]; } nargv[i] = NULL; if (debug) { print_cmdline(i, nargv); } execv(hg_shell, nargv); perror(hg_shell); exit(EX_OSFILE); } enum cmdline { hg_init, hg_serve, }; /* * attempt to verify that a directory is really a hg repo, by testing * for the existence of a subdirectory. */ static int validate_repo(const char *repo_root, const char *subdir) { char *abs_path; struct stat st; int ret; if (asprintf(&abs_path, "%s.hg/%s", repo_root, subdir) == -1) { ret = -1; goto bail; } /* verify that we really are looking at valid repo. */ if (stat(abs_path, &st) == -1) { ret = 0; } else { ret = 1; } bail: return ret; } /* * paranoid wrapper, runs hg executable in server mode. */ static void serve_data(int argc, char **argv) { char *hg_root = HG_ROOT; char *repo, *repo_root; enum cmdline cmd; char *nargv[6]; size_t repolen; int i; /* * check argv for looking okay. we should be invoked with argv * resembling like this: * * hgsh * -c * hg -R some/path serve --stdio * * the "-c" is added by sshd, because it thinks we are login shell. */ if (argc != 3) { goto badargs; } if (strcmp(argv[1], "-c") != 0) { goto badargs; } if (sscanf(argv[2], "hg init %as", &repo) == 1) { cmd = hg_init; } else if (sscanf(argv[2], "hg -R %as serve --stdio", &repo) == 1) { cmd = hg_serve; } else { goto badargs; } repolen = repo ? strlen(repo) : 0; if (repolen == 0) { goto badargs; } if (hg_root) { if (asprintf(&repo_root, "%s/%s/", hg_root, repo) == -1) { goto badargs; } /* * attempt to stop break out from inside the * repository tree. could do something more clever * here, because e.g. we could traverse a symlink that * looks safe, but really breaks us out of tree. */ if (strstr(repo_root, "/../") != NULL) { goto badargs; } /* only hg init expects no repo. */ if (cmd != hg_init) { int valid; valid = validate_repo(repo_root, "data"); if (valid == -1) { goto badargs; } if (valid == 0) { valid = validate_repo(repo_root, "store"); if (valid == -1) { goto badargs; } } if (valid == 0) { perror(repo); exit(EX_DATAERR); } } if (chdir(hg_root) == -1) { perror(hg_root); exit(EX_SOFTWARE); } } i = 0; switch (cmd) { case hg_serve: nargv[i++] = HG; nargv[i++] = "-R"; nargv[i++] = repo; nargv[i++] = "serve"; nargv[i++] = "--stdio"; break; case hg_init: nargv[i++] = HG; nargv[i++] = "init"; nargv[i++] = repo; break; } nargv[i] = NULL; if (debug) { print_cmdline(i, nargv); } execv(HG, nargv); perror(HG); exit(EX_UNAVAILABLE); badargs: /* print useless error message. */ usage("invalid arguments", EX_DATAERR); } int main(int argc, char **argv) { char host[1024]; char *c; if (gethostname(host, sizeof(host)) == -1) { perror("gethostname"); exit(EX_OSERR); } if ((c = strchr(host, '.')) != NULL) { *c = '\0'; } if (getenv("SSH_CLIENT")) { char *hg_gateway = HG_GATEWAY; char *hg_host = HG_HOST; if (hg_gateway && strcmp(host, hg_gateway) == 0) { forward_through_gateway(argc, argv); } if (hg_host && strcmp(host, hg_host) != 0) { usage("invoked on unexpected host", EX_USAGE); } serve_data(argc, argv); } else if (HG_SHELL) { run_shell(argc, argv); } else { usage("invalid arguments", EX_DATAERR); } return 0; } mercurial-2.8.2/contrib/hgsh/Makefile0000644000175000017500000000024012261160437021343 0ustar oxymoronoxymoron00000000000000CC := gcc CFLAGS := -g -O2 -Wall -Werror prefix ?= /usr/bin hgsh: hgsh.o $(CC) -o $@ $< install: hgsh install -m755 hgsh $(prefix) clean: rm -f *.o hgsh mercurial-2.8.2/contrib/check-code.py0000755000175000017500000004642312261160437021331 0ustar oxymoronoxymoron00000000000000#!/usr/bin/env python # # check-code - a style and portability checker for Mercurial # # Copyright 2010 Matt Mackall # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. import re, glob, os, sys import keyword import optparse try: import re2 except ImportError: re2 = None def compilere(pat, multiline=False): if multiline: pat = '(?m)' + pat if re2: try: return re2.compile(pat) except re2.error: pass return re.compile(pat) def repquote(m): fromc = '.:' tochr = 'pq' def encodechr(i): if i > 255: return 'u' c = chr(i) if c in ' \n': return c if c.isalpha(): return 'x' if c.isdigit(): return 'n' try: return tochr[fromc.find(c)] except (ValueError, IndexError): return 'o' t = m.group('text') tt = ''.join(encodechr(i) for i in xrange(256)) t = t.translate(tt) return m.group('quote') + t + m.group('quote') def reppython(m): comment = m.group('comment') if comment: l = len(comment.rstrip()) return "#" * l + comment[l:] return repquote(m) def repcomment(m): return m.group(1) + "#" * len(m.group(2)) def repccomment(m): t = re.sub(r"((?<=\n) )|\S", "x", m.group(2)) return m.group(1) + t + "*/" def repcallspaces(m): t = re.sub(r"\n\s+", "\n", m.group(2)) return m.group(1) + t def repinclude(m): return m.group(1) + "" def rephere(m): t = re.sub(r"\S", "x", m.group(2)) return m.group(1) + t testpats = [ [ (r'pushd|popd', "don't use 'pushd' or 'popd', use 'cd'"), (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"), (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"), (r'(?'"), (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"), (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"), (r'printf.*[^\\]\\([1-9]|0\d)', "don't use 'printf \NNN', use Python"), (r'printf.*[^\\]\\x', "don't use printf \\x, use Python"), (r'\$\(.*\)', "don't use $(expr), use `expr`"), (r'rm -rf \*', "don't use naked rm -rf, target a directory"), (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w', "use egrep for extended grep syntax"), (r'/bin/', "don't use explicit paths for tools"), (r'[^\n]\Z', "no trailing newline"), (r'export.*=', "don't export and assign at once"), (r'^source\b', "don't use 'source', use '.'"), (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"), (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"), (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"), (r'^stop\(\)', "don't use 'stop' as a shell function name"), (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"), (r'^alias\b.*=', "don't use alias, use a function"), (r'if\s*!', "don't use '!' to negate exit status"), (r'/dev/u?random', "don't use entropy, use /dev/zero"), (r'do\s*true;\s*done', "don't use true as loop body, use sleep 0"), (r'^( *)\t', "don't use tabs to indent"), (r'sed (-e )?\'(\d+|/[^/]*/)i(?!\\\n)', "put a backslash-escaped newline after sed 'i' command"), ], # warnings [ (r'^function', "don't use 'function', use old style"), (r'^diff.*-\w*N', "don't use 'diff -N'"), (r'\$PWD|\${PWD}', "don't use $PWD, use `pwd`"), (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"), (r'kill (`|\$\()', "don't use kill, use killdaemons.py") ] ] testfilters = [ (r"( *)(#([^\n]*\S)?)", repcomment), (r"<<(\S+)((.|\n)*?\n\1)", rephere), ] winglobmsg = "use (glob) to match Windows paths too" uprefix = r"^ \$ " utestpats = [ [ (r'^(\S.*|| [$>] .*)[ \t]\n', "trailing whitespace on non-output"), (uprefix + r'.*\|\s*sed[^|>\n]*\n', "use regex test output patterns instead of sed"), (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"), (uprefix + r'.*(? for continued lines"), (r'^ saved backup bundle to \$TESTTMP.*\.hg$', winglobmsg), (r'^ changeset .* references (corrupted|missing) \$TESTTMP/.*[^)]$', winglobmsg), (r'^ pulling from \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'), (r'^ reverting .*/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'), (r'^ cloning subrepo \S+/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'), (r'^ pushing to \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'), (r'^ pushing subrepo \S+/\S+ to.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'), (r'^ moving \S+/.*[^)]$', winglobmsg), (r'^ no changes made to subrepo since.*/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'), (r'^ .*: largefile \S+ not available from file:.*/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'), ], # warnings [ (r'^ [^*?/\n]* \(glob\)$', "glob match with no glob character (?*/)"), ] ] for i in [0, 1]: for p, m in testpats[i]: if p.startswith(r'^'): p = r"^ [$>] (%s)" % p[1:] else: p = r"^ [$>] .*(%s)" % p utestpats[i].append((p, m)) utestfilters = [ (r"<<(\S+)((.|\n)*?\n > \1)", rephere), (r"( *)(#([^\n]*\S)?)", repcomment), ] pypats = [ [ (r'^\s*def\s*\w+\s*\(.*,\s*\(', "tuple parameter unpacking not available in Python 3+"), (r'lambda\s*\(.*,.*\)', "tuple parameter unpacking not available in Python 3+"), (r'import (.+,[^.]+\.[^.]+|[^.]+\.[^.]+,)', '2to3 can\'t always rewrite "import qux, foo.bar", ' 'use "import foo.bar" on its own line instead.'), (r'(?\s', '<> operator is not available in Python 3+, use !='), (r'^\s*\t', "don't use tabs"), (r'\S;\s*\n', "semicolon"), (r'[^_]_\("[^"]+"\s*%', "don't use % inside _()"), (r"[^_]_\('[^']+'\s*%", "don't use % inside _()"), (r'(\w|\)),\w', "missing whitespace after ,"), (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"), (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"), (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n' r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Python 2.4'), (r'(?< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S', "missing whitespace around operator"), (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\s', "missing whitespace around operator"), (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S', "missing whitespace around operator"), (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]', "wrong whitespace around ="), (r'\([^()]*( =[^=]|[^<>!=]= )', "no whitespace around = for named parameters"), (r'raise Exception', "don't raise generic exceptions"), (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$', "don't use old-style two-argument raise, use Exception(message)"), (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"), (r' [=!]=\s+(True|False|None)', "comparison with singleton, use 'is' or 'is not' instead"), (r'^\s*(while|if) [01]:', "use True/False for constant Boolean expression"), (r'(?:(?\#.*?$)| ((?P('''|\"\"\"|(?(([^\\]|\\.)*?)) (?P=quote))""", reppython), ] txtfilters = [] txtpats = [ [ ('\s$', 'trailing whitespace'), ], [] ] cpats = [ [ (r'//', "don't use //-style comments"), (r'^ ', "don't use spaces to indent"), (r'\S\t', "don't use tabs except for indent"), (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"), (r'.{81}', "line too long"), (r'(while|if|do|for)\(', "use space after while/if/do/for"), (r'return\(', "return is not a function"), (r' ;', "no space before ;"), (r'[)][{]', "space between ) and {"), (r'\w+\* \w+', "use int *foo, not int* foo"), (r'\W\([^\)]+\) \w+', "use (int)foo, not (int) foo"), (r'\w+ (\+\+|--)', "use foo++, not foo ++"), (r'\w,\w', "missing whitespace after ,"), (r'^[^#]\w[+/*]\w', "missing whitespace in expression"), (r'^#\s+\w', "use #foo, not # foo"), (r'[^\n]\Z', "no trailing newline"), (r'^\s*#import\b', "use only #include in standard C code"), ], # warnings [] ] cfilters = [ (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment), (r'''(?P(?([^"]|\\")+)"(?!")''', repquote), (r'''(#\s*include\s+<)([^>]+)>''', repinclude), (r'(\()([^)]+\))', repcallspaces), ] inutilpats = [ [ (r'\bui\.', "don't use ui in util"), ], # warnings [] ] inrevlogpats = [ [ (r'\brepo\.', "don't use repo in revlog"), ], # warnings [] ] checks = [ ('python', r'.*\.(py|cgi)$', pyfilters, pypats), ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats), ('c', r'.*\.[ch]$', cfilters, cpats), ('unified test', r'.*\.t$', utestfilters, utestpats), ('layering violation repo in revlog', r'mercurial/revlog\.py', pyfilters, inrevlogpats), ('layering violation ui in util', r'mercurial/util\.py', pyfilters, inutilpats), ('txt', r'.*\.txt$', txtfilters, txtpats), ] def _preparepats(): for c in checks: failandwarn = c[-1] for pats in failandwarn: for i, pseq in enumerate(pats): # fix-up regexes for multi-line searches p = pseq[0] # \s doesn't match \n p = re.sub(r'(? %s" % line self._lastseen = msgid print " " + msg _defaultlogger = norepeatlogger() def getblame(f): lines = [] for l in os.popen('hg annotate -un %s' % f): start, line = l.split(':', 1) user, rev = start.split() lines.append((line[1:-1], user, rev)) return lines def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False, blame=False, debug=False, lineno=True): """checks style and portability of a given file :f: filepath :logfunc: function used to report error logfunc(filename, linenumber, linecontent, errormessage) :maxerr: number of error to display before aborting. Set to false (default) to report all errors return True if no error is found, False otherwise. """ blamecache = None result = True for name, match, filters, pats in checks: if debug: print name, f fc = 0 if not re.match(match, f): if debug: print "Skipping %s for %s it doesn't match %s" % ( name, match, f) continue try: fp = open(f) except IOError, e: print "Skipping %s, %s" % (f, str(e).split(':', 1)[0]) continue pre = post = fp.read() fp.close() if "no-" "check-code" in pre: if debug: print "Skipping %s for %s it has no-" "check-code" % ( name, f) break for p, r in filters: post = re.sub(p, r, post) nerrs = len(pats[0]) # nerr elements are errors if warnings: pats = pats[0] + pats[1] else: pats = pats[0] # print post # uncomment to show filtered version if debug: print "Checking %s for %s" % (name, f) prelines = None errors = [] for i, pat in enumerate(pats): if len(pat) == 3: p, msg, ignore = pat else: p, msg = pat ignore = None if i >= nerrs: msg = "warning: " + msg pos = 0 n = 0 for m in p.finditer(post): if prelines is None: prelines = pre.splitlines() postlines = post.splitlines(True) start = m.start() while n < len(postlines): step = len(postlines[n]) if pos + step > start: break pos += step n += 1 l = prelines[n] if "check-code" "-ignore" in l: if debug: print "Skipping %s for %s:%s (check-code" "-ignore)" % ( name, f, n) continue elif ignore and re.search(ignore, l, re.MULTILINE): continue bd = "" if blame: bd = 'working directory' if not blamecache: blamecache = getblame(f) if n < len(blamecache): bl, bu, br = blamecache[n] if bl == l: bd = '%s@%s' % (bu, br) errors.append((f, lineno and n + 1, l, msg, bd)) result = False errors.sort() for e in errors: logfunc(*e) fc += 1 if maxerr and fc >= maxerr: print " (too many errors, giving up)" break return result if __name__ == "__main__": parser = optparse.OptionParser("%prog [options] [files]") parser.add_option("-w", "--warnings", action="store_true", help="include warning-level checks") parser.add_option("-p", "--per-file", type="int", help="max warnings per file") parser.add_option("-b", "--blame", action="store_true", help="use annotate to generate blame info") parser.add_option("", "--debug", action="store_true", help="show debug information") parser.add_option("", "--nolineno", action="store_false", dest='lineno', help="don't show line numbers") parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False, lineno=True) (options, args) = parser.parse_args() if len(args) == 0: check = glob.glob("*") else: check = args ret = 0 for f in check: if not checkfile(f, maxerr=options.per_file, warnings=options.warnings, blame=options.blame, debug=options.debug, lineno=options.lineno): ret = 1 sys.exit(ret) mercurial-2.8.2/contrib/buildrpm0000755000175000017500000000546512261160437020534 0ustar oxymoronoxymoron00000000000000#!/bin/sh # # Build a Mercurial RPM in place. # # Tested on # - Fedora 8 (with docutils 0.5) # - Fedora 11 # - OpenSuse 11.2 cd "`dirname $0`/.." HG="$PWD/hg" PYTHONPATH="$PWD/mercurial/pure" export PYTHONPATH specfile=contrib/mercurial.spec if [ ! -f $specfile ]; then echo "Cannot find $specfile!" 1>&2 exit 1 fi if [ ! -d .hg ]; then echo 'You are not inside a Mercurial repository!' 1>&2 exit 1 fi if $HG id -i | grep '+$' > /dev/null 2>&1; then echo -n "Your local changes will NOT be in the RPM. Continue [y/n] ? " read answer if echo $answer | grep -iv '^y'; then exit fi fi rpmdir="$PWD/rpmbuild" rm -rf $rpmdir mkdir -p $rpmdir/SOURCES $rpmdir/SPECS $rpmdir/RPMS $rpmdir/SRPMS $rpmdir/BUILD # make setup.py build the version string python setup.py build_py -c -d . hgversion=`$HG version | sed -ne 's/.*(version \(.*\))$/\1/p'` if echo $hgversion | grep -- '-' > /dev/null 2>&1; then # nightly build case, version is like 1.3.1+250-20b91f91f9ca version=`echo $hgversion | cut -d- -f1` release=`echo $hgversion | cut -d- -f2 | sed -e 's/+.*//'` else # official tag, version is like 1.3.1 version=`echo $hgversion | sed -e 's/+.*//'` release='0' fi $HG archive -t tgz $rpmdir/SOURCES/mercurial-$version.tar.gz rpmspec=$rpmdir/SPECS/mercurial-$version.spec sed -e "s,^Version:.*,Version: $version," \ -e "s,^Release:.*,Release: $release," \ $specfile > $rpmspec echo >> $rpmspec echo "%changelog" >> $rpmspec if echo $version | grep '+' > /dev/null 2>&1; then latesttag="`echo $version | sed -e 's/+.*//'`" $HG log -r .:"$latesttag" -fM \ --template '{date|hgdate}\t{author}\t{desc|firstline}\n' | python -c ' import sys, time def datestr(date, format): return time.strftime(format, time.gmtime(float(date[0]) - date[1])) changelog = [] for l in sys.stdin.readlines(): tok = l.split("\t") hgdate = tuple(int(v) for v in tok[0].split()) changelog.append((datestr(hgdate, "%F"), tok[1], hgdate, tok[2])) prevtitle = "" for l in sorted(changelog, reverse=True): title = "* %s %s" % (datestr(l[2], "%a %b %d %Y"), l[1]) if prevtitle != title: prevtitle = title print print title print "- %s" % l[3].strip() ' >> $rpmspec else $HG log \ --template '{date|hgdate}\t{author}\t{desc|firstline}\n' \ .hgtags | python -c ' import sys, time def datestr(date, format): return time.strftime(format, time.gmtime(float(date[0]) - date[1])) for l in sys.stdin.readlines(): tok = l.split("\t") hgdate = tuple(int(v) for v in tok[0].split()) print "* %s %s\n- %s" % (datestr(hgdate, "%a %b %d %Y"), tok[1], tok[2]) ' >> $rpmspec fi rpmbuild --define "_topdir $rpmdir" -ba $rpmspec --clean if [ $? = 0 ]; then echo echo "Packages are in $rpmdir:" ls -l $rpmdir/*RPMS/* fi mercurial-2.8.2/contrib/tcsh_completion_build.sh0000755000175000017500000000364312261160437023674 0ustar oxymoronoxymoron00000000000000#!/bin/sh # # tcsh_completion_build.sh - script to generate tcsh completion # # # Copyright (C) 2005 TK Soh. # # This is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free # Software Foundation; either version 2 of the License, or (at your # option) any later version. # # # Description # ----------- # This script generates a tcsh source file to support completion # of Mercurial commands and options. # # Instruction: # ----------- # Run this script to generate the tcsh source file, and source # the file to add command completion support for Mercurial. # # tcsh% tcsh_completion.sh FILE # tcsh% source FILE # # If FILE is not specified, tcsh_completion will be generated. # # Bugs: # ---- # 1. command specific options are not supported # 2. hg commands must be specified immediately after 'hg'. # tcsh_file=${1-tcsh_completion} hg_commands=`hg --debug help | \ sed -e '1,/^list of commands:/d' \ -e '/^enabled extensions:/,$d' \ -e '/^additional help topics:/,$d' \ -e '/^ [^ ]/!d; s/[,:]//g;' | \ xargs -n5 | \ sed -e '$!s/$/ \\\\/g; 2,$s/^ */ /g'` hg_global_options=`hg -v help | \ sed -e '1,/global/d;/^ *-/!d; s/ [^- ].*//' | \ sed -e 's/ *$//; $!s/$/ \\\\/g; 2,$s/^ */ /g'` hg_version=`hg version | sed -e '1q'` script_name=`basename $0` cat > $tcsh_file < Thomas Arendsen Hein Goffredo Baroncelli Muli Ben-Yehuda Mikael Berthe Benoit Boissinot Brendan Cully Vincent Danjean Jake Edge Michael Fetterman Edouard Gomez Eric Hopper Alecs King Volker Kleinfeld Vadim Lebedev Christopher Li Chris Mason Colin McMillen Wojciech Milkowski Chad Netzer Bryan O'Sullivan Vicent Seguí Pascual Sean Perry Nguyen Anh Quynh Ollivier Robert Alexander Schremmer Arun Sharma Josef "Jeff" Sipek Kevin Smith TK Soh Radoslaw Szkodzinski Samuel Tardieu K Thananchayan Andrew Thompson Michael S. Tsirkin Rafael Villar Burke Tristan Wibberley Mark Williamson mercurial-2.8.2/hgweb.cgi0000755000175000017500000000125312261160437017102 0ustar oxymoronoxymoron00000000000000#!/usr/bin/env python # # An example hgweb CGI script, edit as necessary # See also http://mercurial.selenic.com/wiki/PublishingRepositories # Path to repo or hgweb config to serve (see 'hg help hgweb') config = "/path/to/repo/or/config" # Uncomment and adjust if Mercurial is not installed system-wide # (consult "installed modules" path from 'hg debuginstall'): #import sys; sys.path.insert(0, "/path/to/python/lib") # Uncomment to send python tracebacks to the browser if an error occurs: #import cgitb; cgitb.enable() from mercurial import demandimport; demandimport.enable() from mercurial.hgweb import hgweb, wsgicgi application = hgweb(config) wsgicgi.launch(application) mercurial-2.8.2/hgeditor0000755000175000017500000000231112261160437017046 0ustar oxymoronoxymoron00000000000000#!/bin/sh # # This is an example of using HGEDITOR to create of diff to review the # changes while commiting. # If you want to pass your favourite editor some other parameters # only for Mercurial, modify this: case "${EDITOR}" in "") EDITOR="vi" ;; emacs) EDITOR="$EDITOR -nw" ;; gvim|vim) EDITOR="$EDITOR -f -o" ;; esac HGTMP="" cleanup_exit() { rm -rf "$HGTMP" } # Remove temporary files even if we get interrupted trap "cleanup_exit" 0 # normal exit trap "exit 255" HUP INT QUIT ABRT TERM HGTMP=$(mktemp -d ${TMPDIR-/tmp}/hgeditor.XXXXXX) [ x$HGTMP != x -a -d $HGTMP ] || { echo "Could not create temporary directory! Exiting." 1>&2 exit 1 } ( grep '^HG: changed' "$1" | cut -b 13- | while read changed; do "$HG" diff "$changed" >> "$HGTMP/diff" done ) cat "$1" > "$HGTMP/msg" MD5=$(which md5sum 2>/dev/null) || \ MD5=$(which md5 2>/dev/null) [ -x "${MD5}" ] && CHECKSUM=`${MD5} "$HGTMP/msg"` if [ -s "$HGTMP/diff" ]; then $EDITOR "$HGTMP/msg" "$HGTMP/diff" || exit $? else $EDITOR "$HGTMP/msg" || exit $? fi [ -x "${MD5}" ] && (echo "$CHECKSUM" | ${MD5} -c >/dev/null 2>&1 && exit 13) mv "$HGTMP/msg" "$1" exit $? mercurial-2.8.2/.hgignore0000644000175000017500000000136612261160437017127 0ustar oxymoronoxymoron00000000000000syntax: glob *.elc *.orig *.rej *~ *.mergebackup *.o *.so *.dll *.exe *.pyd *.pyc *.pyo *$py.class *.swp *.prof *.zip \#*\# .\#* tests/.coverage* tests/annotated tests/*.err tests/htmlcov build contrib/hgsh/hgsh dist doc/common.txt doc/*.[0-9] doc/*.[0-9].txt doc/*.[0-9].gendoc.txt doc/*.[0-9].{x,ht}ml MANIFEST MANIFEST.in patches mercurial/__version__.py mercurial/hgpythonlib.h mercurial.egg-info .DS_Store tags cscope.* i18n/hg.pot locale/*/LC_MESSAGES/hg.mo hgext/__index__.py # files installed with a local --pure build mercurial/base85.py mercurial/bdiff.py mercurial/diffhelpers.py mercurial/mpatch.py mercurial/osutil.py mercurial/parsers.py syntax: regexp ^\.pc/ ^\.(pydev)?project # hackable windows distribution additions ^hg-python ^hg.py$ mercurial-2.8.2/hg0000755000175000017500000000204012261160437015636 0ustar oxymoronoxymoron00000000000000#!/usr/bin/env python # # mercurial - scalable distributed SCM # # Copyright 2005-2007 Matt Mackall # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. import os import sys libdir = '@LIBDIR@' if libdir != '@' 'LIBDIR' '@': if not os.path.isabs(libdir): libdir = os.path.join(os.path.dirname(os.path.realpath(__file__)), libdir) libdir = os.path.abspath(libdir) sys.path.insert(0, libdir) # enable importing on demand to reduce startup time try: from mercurial import demandimport; demandimport.enable() except ImportError: import sys sys.stderr.write("abort: couldn't find mercurial libraries in [%s]\n" % ' '.join(sys.path)) sys.stderr.write("(check your install and PYTHONPATH)\n") sys.exit(-1) import mercurial.util import mercurial.dispatch for fp in (sys.stdin, sys.stdout, sys.stderr): mercurial.util.setbinary(fp) mercurial.dispatch.run() mercurial-2.8.2/.hgtags0000644000175000017500000001056212261160445016577 0ustar oxymoronoxymoron00000000000000d40cc5aacc31ed673d9b5b24f98bee78c283062c 0.4f 1c590d34bf61e2ea12c71738e5a746cd74586157 0.4e 7eca4cfa8aad5fce9a04f7d8acadcd0452e2f34e 0.4d b4d0c3786ad3e47beacf8412157326a32b6d25a4 0.4c f40273b0ad7b3a6d3012fd37736d0611f41ecf54 0.5 0a28dfe59f8fab54a5118c5be4f40da34a53cdb7 0.5b 12e0fdbc57a0be78f0e817fd1d170a3615cd35da 0.6 4ccf3de52989b14c3d84e1097f59e39a992e00bd 0.6b eac9c8efcd9bd8244e72fb6821f769f450457a32 0.6c 979c049974485125e1f9357f6bbe9c1b548a64c3 0.7 3a56574f329a368d645853e0f9e09472aee62349 0.8 6a03cff2b0f5d30281e6addefe96b993582f2eac 0.8.1 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0.9 2be3001847cb18a23c403439d9e7d0ace30804e9 0.9.1 36a957364b1b89c150f2d0e60a99befe0ee08bd3 0.9.2 27230c29bfec36d5540fbe1c976810aefecfd1d2 0.9.3 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0.9.4 23889160905a1b09fffe1c07378e9fc1827606eb 0.9.5 bae2e9c838e90a393bae3973a7850280413e091a 1.0 d5cbbe2c49cee22a9fbeb9ea41daa0ac4e26b846 1.0.1 d2375bbee6d47e62ba8e415c86e83a465dc4dce9 1.0.2 2a67430f92f15ea5159c26b09ec4839a0c549a26 1.1 3773e510d433969e277b1863c317b674cbee2065 1.1.1 11a4eb81fb4f4742451591489e2797dc47903277 1.1.2 11efa41037e280d08cfb07c09ad485df30fb0ea8 1.2 02981000012e3adf40c4849bd7b3d5618f9ce82d 1.2.1 196d40e7c885fa6e95f89134809b3ec7bdbca34b 1.3 3ef6c14a1e8e83a31226f5881b7fe6095bbfa6f6 1.3.1 31ec469f9b556f11819937cf68ee53f2be927ebf 1.4 439d7ea6fe3aa4ab9ec274a68846779153789de9 1.4.1 296a0b14a68621f6990c54fdba0083f6f20935bf 1.4.2 4aa619c4c2c09907034d9824ebb1dd0e878206eb 1.4.3 ff2704a8ded37fbebd8b6eb5ec733731d725da8a 1.5 2b01dab594167bc0dd33331dbaa6dca3dca1b3aa 1.5.1 39f725929f0c48c5fb3b90c071fc3066012456ca 1.5.2 fdcf80f26604f233dc4d8f0a5ef9d7470e317e8a 1.5.3 24fe2629c6fd0c74c90bd066e77387c2b02e8437 1.5.4 f786fc4b8764cd2a5526d259cf2f94d8a66924d9 1.6 bf1774d95bde614af3956d92b20e2a0c68c5fec7 1.6.1 c00f03a4982e467fb6b6bd45908767db6df4771d 1.6.2 ff5cec76b1c5b6be9c3bb923aae8c3c6d079d6b9 1.6.3 93d8bff78c96fe7e33237b257558ee97290048a4 1.6.4 333421b9e0f96c7bc788e5667c146a58a9440a55 1.7 4438875ec01bd0fc32be92b0872eb6daeed4d44f 1.7.1 6aff4f144ad356311318b0011df0bb21f2c97429 1.7.2 e3bf16703e2601de99e563cdb3a5d50b64e6d320 1.7.3 a6c855c32ea081da3c3b8ff628f1847ff271482f 1.7.4 2b2155623ee2559caf288fd333f30475966c4525 1.7.5 2616325766e3504c8ae7c84bd15ee610901fe91d 1.8 aa1f3be38ab127280761889d2dca906ca465b5f4 1.8.1 b032bec2c0a651ca0ddecb65714bfe6770f67d70 1.8.2 3cb1e95676ad089596bd81d0937cad37d6e3b7fb 1.8.3 733af5d9f6b22387913e1d11350fb8cb7c1487dd 1.8.4 de9eb6b1da4fc522b1cab16d86ca166204c24f25 1.9 4a43e23b8c55b4566b8200bf69fe2158485a2634 1.9.1 d629f1e89021103f1753addcef6b310e4435b184 1.9.2 351a9292e430e35766c552066ed3e87c557b803b 1.9.3 384082750f2c51dc917d85a7145748330fa6ef4d 2.0-rc 41453d55b481ddfcc1dacb445179649e24ca861d 2.0 195dbd1cef0c2f9f8bcf4ea303238105f716bda3 2.0.1 6344043924497cd06d781d9014c66802285072e4 2.0.2 db33555eafeaf9df1e18950e29439eaa706d399b 2.1-rc 2aa5b51f310fb3befd26bed99c02267f5c12c734 2.1 53e2cd303ecf8ca7c7eeebd785c34e5ed6b0f4a4 2.1.1 b9bd95e61b49c221c4cca24e6da7c946fc02f992 2.1.2 d9e2f09d5488c395ae9ddbb320ceacd24757e055 2.2-rc 00182b3d087909e3c3ae44761efecdde8f319ef3 2.2 5983de86462c5a9f42a3ad0f5e90ce5b1d221d25 2.2.1 85a358df5bbbe404ca25730c9c459b34263441dc 2.2.2 b013baa3898e117959984fc64c29d8c784d2f28b 2.2.3 a06e2681dd1786e2354d84a5fa9c1c88dd4fa3e0 2.3-rc 7f5094bb3f423fc799e471aac2aee81a7ce57a0b 2.3 072209ae4ddb654eb2d5fd35bff358c738414432 2.3.1 b3f0f9a39c4e1d0250048cd803ab03542d6f140a 2.3.2 d118a4f4fd16d9b558ec3f3e87bfee772861d2b7 2.4-rc 195ad823b5d58c68903a6153a25e3fb4ed25239d 2.4 0c10cf8191469e7c3c8844922e17e71a176cb7cb 2.4.1 a4765077b65e6ae29ba42bab7834717b5072d5ba 2.4.2 f5fbe15ca7449f2c9a3cf817c86d0ae68b307214 2.5-rc a6088c05e43a8aee0472ca3a4f6f8d7dd914ebbf 2.5 7511d4df752e61fe7ae4f3682e0a0008573b0402 2.5.1 5b7175377babacce80a6c1e12366d8032a6d4340 2.5.2 50c922c1b5145dab8baefefb0437d363b6a6c21c 2.5.3 8a7bd2dccd44ed571afe7424cd7f95594f27c092 2.5.4 292cd385856d98bacb2c3086f8897bc660c2beea 2.6-rc 23f785b38af38d2fca6b8f3db56b8007a84cd73a 2.6 ddc7a6be20212d18f3e27d9d7e6f079a66d96f21 2.6.1 cceaf7af4c9e9e6fa2dbfdcfe9856c5da69c4ffd 2.6.2 009794acc6e37a650f0fae37872e733382ac1c0c 2.6.3 f0d7721d7322dcfb5af33599c2543f27335334bb 2.7-rc f37b5a17e6a0ee17afde2cdde5393dd74715fb58 2.7 335a558f81dc73afeab4d7be63617392b130117f 2.7.1 e7fa36d2ad3a7944a52dca126458d6f482db3524 2.7.2 1596f2d8f2421314b1ddead8f7d0c91009358994 2.8-rc d825e4025e39d1c39db943cdc89818abd0a87c27 2.8 209e04a06467e2969c0cc6501335be0406d46ef0 2.8.1 mercurial-2.8.2/hgext/0000755000175000017500000000000012261160455016435 5ustar oxymoronoxymoron00000000000000mercurial-2.8.2/hgext/churn.py0000644000175000017500000001534712261160437020140 0ustar oxymoronoxymoron00000000000000# churn.py - create a graph of revisions count grouped by template # # Copyright 2006 Josef "Jeff" Sipek # Copyright 2008 Alexander Solovyov # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''command to display statistics about repository history''' from mercurial.i18n import _ from mercurial import patch, cmdutil, scmutil, util, templater, commands import os import time, datetime testedwith = 'internal' def maketemplater(ui, repo, tmpl): tmpl = templater.parsestring(tmpl, quoted=False) try: t = cmdutil.changeset_templater(ui, repo, False, None, None, False) except SyntaxError, inst: raise util.Abort(inst.args[0]) t.use_template(tmpl) return t def changedlines(ui, repo, ctx1, ctx2, fns): added, removed = 0, 0 fmatch = scmutil.matchfiles(repo, fns) diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch)) for l in diff.split('\n'): if l.startswith("+") and not l.startswith("+++ "): added += 1 elif l.startswith("-") and not l.startswith("--- "): removed += 1 return (added, removed) def countrate(ui, repo, amap, *pats, **opts): """Calculate stats""" if opts.get('dateformat'): def getkey(ctx): t, tz = ctx.date() date = datetime.datetime(*time.gmtime(float(t) - tz)[:6]) return date.strftime(opts['dateformat']) else: tmpl = opts.get('template', '{author|email}') tmpl = maketemplater(ui, repo, tmpl) def getkey(ctx): ui.pushbuffer() tmpl.show(ctx) return ui.popbuffer() state = {'count': 0} rate = {} df = False if opts.get('date'): df = util.matchdate(opts['date']) m = scmutil.match(repo[None], pats, opts) def prep(ctx, fns): rev = ctx.rev() if df and not df(ctx.date()[0]): # doesn't match date format return key = getkey(ctx).strip() key = amap.get(key, key) # alias remap if opts.get('changesets'): rate[key] = (rate.get(key, (0,))[0] + 1, 0) else: parents = ctx.parents() if len(parents) > 1: ui.note(_('revision %d is a merge, ignoring...\n') % (rev,)) return ctx1 = parents[0] lines = changedlines(ui, repo, ctx1, ctx, fns) rate[key] = [r + l for r, l in zip(rate.get(key, (0, 0)), lines)] state['count'] += 1 ui.progress(_('analyzing'), state['count'], total=len(repo)) for ctx in cmdutil.walkchangerevs(repo, m, opts, prep): continue ui.progress(_('analyzing'), None) return rate def churn(ui, repo, *pats, **opts): '''histogram of changes to the repository This command will display a histogram representing the number of changed lines or revisions, grouped according to the given template. The default template will group changes by author. The --dateformat option may be used to group the results by date instead. Statistics are based on the number of changed lines, or alternatively the number of matching revisions if the --changesets option is specified. Examples:: # display count of changed lines for every committer hg churn -t "{author|email}" # display daily activity graph hg churn -f "%H" -s -c # display activity of developers by month hg churn -f "%Y-%m" -s -c # display count of lines changed in every year hg churn -f "%Y" -s It is possible to map alternate email addresses to a main address by providing a file using the following format:: = Such a file may be specified with the --aliases option, otherwise a .hgchurn file will be looked for in the working directory root. Aliases will be split from the rightmost "=". ''' def pad(s, l): return (s + " " * l)[:l] amap = {} aliases = opts.get('aliases') if not aliases and os.path.exists(repo.wjoin('.hgchurn')): aliases = repo.wjoin('.hgchurn') if aliases: for l in open(aliases, "r"): try: alias, actual = l.rsplit('=' in l and '=' or None, 1) amap[alias.strip()] = actual.strip() except ValueError: l = l.strip() if l: ui.warn(_("skipping malformed alias: %s\n") % l) continue rate = countrate(ui, repo, amap, *pats, **opts).items() if not rate: return if opts.get('sort'): rate.sort() else: rate.sort(key=lambda x: (-sum(x[1]), x)) # Be careful not to have a zero maxcount (issue833) maxcount = float(max(sum(v) for k, v in rate)) or 1.0 maxname = max(len(k) for k, v in rate) ttywidth = ui.termwidth() ui.debug("assuming %i character terminal\n" % ttywidth) width = ttywidth - maxname - 2 - 2 - 2 if opts.get('diffstat'): width -= 15 def format(name, diffstat): added, removed = diffstat return "%s %15s %s%s\n" % (pad(name, maxname), '+%d/-%d' % (added, removed), ui.label('+' * charnum(added), 'diffstat.inserted'), ui.label('-' * charnum(removed), 'diffstat.deleted')) else: width -= 6 def format(name, count): return "%s %6d %s\n" % (pad(name, maxname), sum(count), '*' * charnum(sum(count))) def charnum(count): return int(round(count * width / maxcount)) for name, count in rate: ui.write(format(name, count)) cmdtable = { "churn": (churn, [('r', 'rev', [], _('count rate for the specified revision or range'), _('REV')), ('d', 'date', '', _('count rate for revisions matching date spec'), _('DATE')), ('t', 'template', '{author|email}', _('template to group changesets'), _('TEMPLATE')), ('f', 'dateformat', '', _('strftime-compatible format for grouping by date'), _('FORMAT')), ('c', 'changesets', False, _('count rate by number of changesets')), ('s', 'sort', False, _('sort by key (default: sort by count)')), ('', 'diffstat', False, _('display added/removed lines separately')), ('', 'aliases', '', _('file with email aliases'), _('FILE')), ] + commands.walkopts, _("hg churn [-d DATE] [-r REV] [--aliases FILE] [FILE]")), } commands.inferrepo += " churn" mercurial-2.8.2/hgext/hgk.py0000644000175000017500000002723212261160437017566 0ustar oxymoronoxymoron00000000000000# Minimal support for git commands on an hg repository # # Copyright 2005, 2006 Chris Mason # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''browse the repository in a graphical way The hgk extension allows browsing the history of a repository in a graphical way. It requires Tcl/Tk version 8.4 or later. (Tcl/Tk is not distributed with Mercurial.) hgk consists of two parts: a Tcl script that does the displaying and querying of information, and an extension to Mercurial named hgk.py, which provides hooks for hgk to get information. hgk can be found in the contrib directory, and the extension is shipped in the hgext repository, and needs to be enabled. The :hg:`view` command will launch the hgk Tcl script. For this command to work, hgk must be in your search path. Alternately, you can specify the path to hgk in your configuration file:: [hgk] path=/location/of/hgk hgk can make use of the extdiff extension to visualize revisions. Assuming you had already configured extdiff vdiff command, just add:: [hgk] vdiff=vdiff Revisions context menu will now display additional entries to fire vdiff on hovered and selected revisions. ''' import os from mercurial import commands, util, patch, revlog, scmutil from mercurial.node import nullid, nullrev, short from mercurial.i18n import _ testedwith = 'internal' def difftree(ui, repo, node1=None, node2=None, *files, **opts): """diff trees from two commits""" def __difftree(repo, node1, node2, files=[]): assert node2 is not None mmap = repo[node1].manifest() mmap2 = repo[node2].manifest() m = scmutil.match(repo[node1], files) modified, added, removed = repo.status(node1, node2, m)[:3] empty = short(nullid) for f in modified: # TODO get file permissions ui.write(":100664 100664 %s %s M\t%s\t%s\n" % (short(mmap[f]), short(mmap2[f]), f, f)) for f in added: ui.write(":000000 100664 %s %s N\t%s\t%s\n" % (empty, short(mmap2[f]), f, f)) for f in removed: ui.write(":100664 000000 %s %s D\t%s\t%s\n" % (short(mmap[f]), empty, f, f)) ## while True: if opts['stdin']: try: line = raw_input().split(' ') node1 = line[0] if len(line) > 1: node2 = line[1] else: node2 = None except EOFError: break node1 = repo.lookup(node1) if node2: node2 = repo.lookup(node2) else: node2 = node1 node1 = repo.changelog.parents(node1)[0] if opts['patch']: if opts['pretty']: catcommit(ui, repo, node2, "") m = scmutil.match(repo[node1], files) chunks = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, {'git': True})) for chunk in chunks: ui.write(chunk) else: __difftree(repo, node1, node2, files=files) if not opts['stdin']: break def catcommit(ui, repo, n, prefix, ctx=None): nlprefix = '\n' + prefix if ctx is None: ctx = repo[n] # use ctx.node() instead ?? ui.write(("tree %s\n" % short(ctx.changeset()[0]))) for p in ctx.parents(): ui.write(("parent %s\n" % p)) date = ctx.date() description = ctx.description().replace("\0", "") lines = description.splitlines() if lines and lines[-1].startswith('committer:'): committer = lines[-1].split(': ')[1].rstrip() else: committer = "" ui.write(("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))) if committer != '': ui.write(("committer %s %s %s\n" % (committer, int(date[0]), date[1]))) ui.write(("revision %d\n" % ctx.rev())) ui.write(("branch %s\n" % ctx.branch())) ui.write(("phase %s\n\n" % ctx.phasestr())) if prefix != "": ui.write("%s%s\n" % (prefix, description.replace('\n', nlprefix).strip())) else: ui.write(description + "\n") if prefix: ui.write('\0') def base(ui, repo, node1, node2): """output common ancestor information""" node1 = repo.lookup(node1) node2 = repo.lookup(node2) n = repo.changelog.ancestor(node1, node2) ui.write(short(n) + "\n") def catfile(ui, repo, type=None, r=None, **opts): """cat a specific revision""" # in stdin mode, every line except the commit is prefixed with two # spaces. This way the our caller can find the commit without magic # strings # prefix = "" if opts['stdin']: try: (type, r) = raw_input().split(' ') prefix = " " except EOFError: return else: if not type or not r: ui.warn(_("cat-file: type or revision not supplied\n")) commands.help_(ui, 'cat-file') while r: if type != "commit": ui.warn(_("aborting hg cat-file only understands commits\n")) return 1 n = repo.lookup(r) catcommit(ui, repo, n, prefix) if opts['stdin']: try: (type, r) = raw_input().split(' ') except EOFError: break else: break # git rev-tree is a confusing thing. You can supply a number of # commit sha1s on the command line, and it walks the commit history # telling you which commits are reachable from the supplied ones via # a bitmask based on arg position. # you can specify a commit to stop at by starting the sha1 with ^ def revtree(ui, args, repo, full="tree", maxnr=0, parents=False): def chlogwalk(): count = len(repo) i = count l = [0] * 100 chunk = 100 while True: if chunk > i: chunk = i i = 0 else: i -= chunk for x in xrange(chunk): if i + x >= count: l[chunk - x:] = [0] * (chunk - x) break if full is not None: l[x] = repo[i + x] l[x].changeset() # force reading else: l[x] = 1 for x in xrange(chunk - 1, -1, -1): if l[x] != 0: yield (i + x, full is not None and l[x] or None) if i == 0: break # calculate and return the reachability bitmask for sha def is_reachable(ar, reachable, sha): if len(ar) == 0: return 1 mask = 0 for i in xrange(len(ar)): if sha in reachable[i]: mask |= 1 << i return mask reachable = [] stop_sha1 = [] want_sha1 = [] count = 0 # figure out which commits they are asking for and which ones they # want us to stop on for i, arg in enumerate(args): if arg.startswith('^'): s = repo.lookup(arg[1:]) stop_sha1.append(s) want_sha1.append(s) elif arg != 'HEAD': want_sha1.append(repo.lookup(arg)) # calculate the graph for the supplied commits for i, n in enumerate(want_sha1): reachable.append(set()) visit = [n] reachable[i].add(n) while visit: n = visit.pop(0) if n in stop_sha1: continue for p in repo.changelog.parents(n): if p not in reachable[i]: reachable[i].add(p) visit.append(p) if p in stop_sha1: continue # walk the repository looking for commits that are in our # reachability graph for i, ctx in chlogwalk(): n = repo.changelog.node(i) mask = is_reachable(want_sha1, reachable, n) if mask: parentstr = "" if parents: pp = repo.changelog.parents(n) if pp[0] != nullid: parentstr += " " + short(pp[0]) if pp[1] != nullid: parentstr += " " + short(pp[1]) if not full: ui.write("%s%s\n" % (short(n), parentstr)) elif full == "commit": ui.write("%s%s\n" % (short(n), parentstr)) catcommit(ui, repo, n, ' ', ctx) else: (p1, p2) = repo.changelog.parents(n) (h, h1, h2) = map(short, (n, p1, p2)) (i1, i2) = map(repo.changelog.rev, (p1, p2)) date = ctx.date()[0] ui.write("%s %s:%s" % (date, h, mask)) mask = is_reachable(want_sha1, reachable, p1) if i1 != nullrev and mask > 0: ui.write("%s:%s " % (h1, mask)), mask = is_reachable(want_sha1, reachable, p2) if i2 != nullrev and mask > 0: ui.write("%s:%s " % (h2, mask)) ui.write("\n") if maxnr and count >= maxnr: break count += 1 def revparse(ui, repo, *revs, **opts): """parse given revisions""" def revstr(rev): if rev == 'HEAD': rev = 'tip' return revlog.hex(repo.lookup(rev)) for r in revs: revrange = r.split(':', 1) ui.write('%s\n' % revstr(revrange[0])) if len(revrange) == 2: ui.write('^%s\n' % revstr(revrange[1])) # git rev-list tries to order things by date, and has the ability to stop # at a given commit without walking the whole repo. TODO add the stop # parameter def revlist(ui, repo, *revs, **opts): """print revisions""" if opts['header']: full = "commit" else: full = None copy = [x for x in revs] revtree(ui, copy, repo, full, opts['max_count'], opts['parents']) def config(ui, repo, **opts): """print extension options""" def writeopt(name, value): ui.write(('k=%s\nv=%s\n' % (name, value))) writeopt('vdiff', ui.config('hgk', 'vdiff', '')) def view(ui, repo, *etc, **opts): "start interactive history viewer" os.chdir(repo.root) optstr = ' '.join(['--%s %s' % (k, v) for k, v in opts.iteritems() if v]) cmd = ui.config("hgk", "path", "hgk") + " %s %s" % (optstr, " ".join(etc)) ui.debug("running %s\n" % cmd) util.system(cmd) cmdtable = { "^view": (view, [('l', 'limit', '', _('limit number of changes displayed'), _('NUM'))], _('hg view [-l LIMIT] [REVRANGE]')), "debug-diff-tree": (difftree, [('p', 'patch', None, _('generate patch')), ('r', 'recursive', None, _('recursive')), ('P', 'pretty', None, _('pretty')), ('s', 'stdin', None, _('stdin')), ('C', 'copy', None, _('detect copies')), ('S', 'search', "", _('search'))], _('hg git-diff-tree [OPTION]... NODE1 NODE2 [FILE]...')), "debug-cat-file": (catfile, [('s', 'stdin', None, _('stdin'))], _('hg debug-cat-file [OPTION]... TYPE FILE')), "debug-config": (config, [], _('hg debug-config')), "debug-merge-base": (base, [], _('hg debug-merge-base REV REV')), "debug-rev-parse": (revparse, [('', 'default', '', _('ignored'))], _('hg debug-rev-parse REV')), "debug-rev-list": (revlist, [('H', 'header', None, _('header')), ('t', 'topo-order', None, _('topo-order')), ('p', 'parents', None, _('parents')), ('n', 'max-count', 0, _('max-count'))], _('hg debug-rev-list [OPTION]... REV...')), } commands.inferrepo += " debug-diff-tree debug-cat-file" mercurial-2.8.2/hgext/fetch.py0000644000175000017500000001354612261160437020111 0ustar oxymoronoxymoron00000000000000# fetch.py - pull and merge remote changes # # Copyright 2006 Vadim Gelfer # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''pull, update and merge in one command (DEPRECATED)''' from mercurial.i18n import _ from mercurial.node import nullid, short from mercurial import commands, cmdutil, hg, util, error from mercurial.lock import release testedwith = 'internal' def fetch(ui, repo, source='default', **opts): '''pull changes from a remote repository, merge new changes if needed. This finds all changes from the repository at the specified path or URL and adds them to the local repository. If the pulled changes add a new branch head, the head is automatically merged, and the result of the merge is committed. Otherwise, the working directory is updated to include the new changes. When a merge is needed, the working directory is first updated to the newly pulled changes. Local changes are then merged into the pulled changes. To switch the merge order, use --switch-parent. See :hg:`help dates` for a list of formats valid for -d/--date. Returns 0 on success. ''' date = opts.get('date') if date: opts['date'] = util.parsedate(date) parent, p2 = repo.dirstate.parents() branch = repo.dirstate.branch() try: branchnode = repo.branchtip(branch) except error.RepoLookupError: branchnode = None if parent != branchnode: raise util.Abort(_('working dir not at branch tip ' '(use "hg update" to check out branch tip)')) if p2 != nullid: raise util.Abort(_('outstanding uncommitted merge')) wlock = lock = None try: wlock = repo.wlock() lock = repo.lock() mod, add, rem, del_ = repo.status()[:4] if mod or add or rem: raise util.Abort(_('outstanding uncommitted changes')) if del_: raise util.Abort(_('working directory is missing some files')) bheads = repo.branchheads(branch) bheads = [head for head in bheads if len(repo[head].children()) == 0] if len(bheads) > 1: raise util.Abort(_('multiple heads in this branch ' '(use "hg heads ." and "hg merge" to merge)')) other = hg.peer(repo, opts, ui.expandpath(source)) ui.status(_('pulling from %s\n') % util.hidepassword(ui.expandpath(source))) revs = None if opts['rev']: try: revs = [other.lookup(rev) for rev in opts['rev']] except error.CapabilityError: err = _("other repository doesn't support revision lookup, " "so a rev cannot be specified.") raise util.Abort(err) # Are there any changes at all? modheads = repo.pull(other, heads=revs) if modheads == 0: return 0 # Is this a simple fast-forward along the current branch? newheads = repo.branchheads(branch) newchildren = repo.changelog.nodesbetween([parent], newheads)[2] if len(newheads) == 1 and len(newchildren): if newchildren[0] != parent: return hg.update(repo, newchildren[0]) else: return 0 # Are there more than one additional branch heads? newchildren = [n for n in newchildren if n != parent] newparent = parent if newchildren: newparent = newchildren[0] hg.clean(repo, newparent) newheads = [n for n in newheads if n != newparent] if len(newheads) > 1: ui.status(_('not merging with %d other new branch heads ' '(use "hg heads ." and "hg merge" to merge them)\n') % (len(newheads) - 1)) return 1 if not newheads: return 0 # Otherwise, let's merge. err = False if newheads: # By default, we consider the repository we're pulling # *from* as authoritative, so we merge our changes into # theirs. if opts['switch_parent']: firstparent, secondparent = newparent, newheads[0] else: firstparent, secondparent = newheads[0], newparent ui.status(_('updating to %d:%s\n') % (repo.changelog.rev(firstparent), short(firstparent))) hg.clean(repo, firstparent) ui.status(_('merging with %d:%s\n') % (repo.changelog.rev(secondparent), short(secondparent))) err = hg.merge(repo, secondparent, remind=False) if not err: # we don't translate commit messages message = (cmdutil.logmessage(ui, opts) or ('Automated merge with %s' % util.removeauth(other.url()))) editor = cmdutil.commiteditor if opts.get('force_editor') or opts.get('edit'): editor = cmdutil.commitforceeditor n = repo.commit(message, opts['user'], opts['date'], editor=editor) ui.status(_('new changeset %d:%s merges remote changes ' 'with local\n') % (repo.changelog.rev(n), short(n))) return err finally: release(lock, wlock) cmdtable = { 'fetch': (fetch, [('r', 'rev', [], _('a specific revision you would like to pull'), _('REV')), ('e', 'edit', None, _('edit commit message')), ('', 'force-editor', None, _('edit commit message (DEPRECATED)')), ('', 'switch-parent', None, _('switch parents when merging')), ] + commands.commitopts + commands.commitopts2 + commands.remoteopts, _('hg fetch [SOURCE]')), } mercurial-2.8.2/hgext/factotum.py0000644000175000017500000001022512261160437020631 0ustar oxymoronoxymoron00000000000000# factotum.py - Plan 9 factotum integration for Mercurial # # Copyright (C) 2012 Steven Stallion # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. '''http authentication with factotum This extension allows the factotum(4) facility on Plan 9 from Bell Labs platforms to provide authentication information for HTTP access. Configuration entries specified in the auth section as well as authentication information provided in the repository URL are fully supported. If no prefix is specified, a value of "*" will be assumed. By default, keys are specified as:: proto=pass service=hg prefix= user= !password= If the factotum extension is unable to read the required key, one will be requested interactively. A configuration section is available to customize runtime behavior. By default, these entries are:: [factotum] executable = /bin/auth/factotum mountpoint = /mnt/factotum service = hg The executable entry defines the full path to the factotum binary. The mountpoint entry defines the path to the factotum file service. Lastly, the service entry controls the service name used when reading keys. ''' from mercurial.i18n import _ from mercurial.url import passwordmgr from mercurial import httpconnection, util import os, urllib2 ERRMAX = 128 def auth_getkey(self, params): if not self.ui.interactive(): raise util.Abort(_('factotum not interactive')) if 'user=' not in params: params = '%s user?' % params params = '%s !password?' % params os.system("%s -g '%s'" % (_executable, params)) def auth_getuserpasswd(self, getkey, params): params = 'proto=pass %s' % params while True: fd = os.open('%s/rpc' % _mountpoint, os.O_RDWR) try: try: os.write(fd, 'start %s' % params) l = os.read(fd, ERRMAX).split() if l[0] == 'ok': os.write(fd, 'read') l = os.read(fd, ERRMAX).split() if l[0] == 'ok': return l[1:] except (OSError, IOError): raise util.Abort(_('factotum not responding')) finally: os.close(fd) getkey(self, params) def monkeypatch_method(cls): def decorator(func): setattr(cls, func.__name__, func) return func return decorator @monkeypatch_method(passwordmgr) def find_user_password(self, realm, authuri): user, passwd = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password( self, realm, authuri) if user and passwd: self._writedebug(user, passwd) return (user, passwd) prefix = '' res = httpconnection.readauthforuri(self.ui, authuri, user) if res: _, auth = res prefix = auth.get('prefix') user, passwd = auth.get('username'), auth.get('password') if not user or not passwd: if not prefix: prefix = realm.split(' ')[0].lower() params = 'service=%s prefix=%s' % (_service, prefix) if user: params = '%s user=%s' % (params, user) user, passwd = auth_getuserpasswd(self, auth_getkey, params) self.add_password(realm, authuri, user, passwd) self._writedebug(user, passwd) return (user, passwd) def uisetup(ui): global _executable _executable = ui.config('factotum', 'executable', '/bin/auth/factotum') global _mountpoint _mountpoint = ui.config('factotum', 'mountpoint', '/mnt/factotum') global _service _service = ui.config('factotum', 'service', 'hg') mercurial-2.8.2/hgext/win32mbcs.py0000644000175000017500000001440712261160437020624 0ustar oxymoronoxymoron00000000000000# win32mbcs.py -- MBCS filename support for Mercurial # # Copyright (c) 2008 Shun-ichi Goto # # Version: 0.3 # Author: Shun-ichi Goto # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. # '''allow the use of MBCS paths with problematic encodings Some MBCS encodings are not good for some path operations (i.e. splitting path, case conversion, etc.) with its encoded bytes. We call such a encoding (i.e. shift_jis and big5) as "problematic encoding". This extension can be used to fix the issue with those encodings by wrapping some functions to convert to Unicode string before path operation. This extension is useful for: - Japanese Windows users using shift_jis encoding. - Chinese Windows users using big5 encoding. - All users who use a repository with one of problematic encodings on case-insensitive file system. This extension is not needed for: - Any user who use only ASCII chars in path. - Any user who do not use any of problematic encodings. Note that there are some limitations on using this extension: - You should use single encoding in one repository. - If the repository path ends with 0x5c, .hg/hgrc cannot be read. - win32mbcs is not compatible with fixutf8 extension. By default, win32mbcs uses encoding.encoding decided by Mercurial. You can specify the encoding by config option:: [win32mbcs] encoding = sjis It is useful for the users who want to commit with UTF-8 log message. ''' import os, sys from mercurial.i18n import _ from mercurial import util, encoding testedwith = 'internal' _encoding = None # see extsetup def decode(arg): if isinstance(arg, str): uarg = arg.decode(_encoding) if arg == uarg.encode(_encoding): return uarg raise UnicodeError("Not local encoding") elif isinstance(arg, tuple): return tuple(map(decode, arg)) elif isinstance(arg, list): return map(decode, arg) elif isinstance(arg, dict): for k, v in arg.items(): arg[k] = decode(v) return arg def encode(arg): if isinstance(arg, unicode): return arg.encode(_encoding) elif isinstance(arg, tuple): return tuple(map(encode, arg)) elif isinstance(arg, list): return map(encode, arg) elif isinstance(arg, dict): for k, v in arg.items(): arg[k] = encode(v) return arg def appendsep(s): # ensure the path ends with os.sep, appending it if necessary. try: us = decode(s) except UnicodeError: us = s if us and us[-1] not in ':/\\': s += os.sep return s def basewrapper(func, argtype, enc, dec, args, kwds): # check check already converted, then call original for arg in args: if isinstance(arg, argtype): return func(*args, **kwds) try: # convert string arguments, call func, then convert back the # return value. return enc(func(*dec(args), **dec(kwds))) except UnicodeError: raise util.Abort(_("[win32mbcs] filename conversion failed with" " %s encoding\n") % (_encoding)) def wrapper(func, args, kwds): return basewrapper(func, unicode, encode, decode, args, kwds) def reversewrapper(func, args, kwds): return basewrapper(func, str, decode, encode, args, kwds) def wrapperforlistdir(func, args, kwds): # Ensure 'path' argument ends with os.sep to avoids # misinterpreting last 0x5c of MBCS 2nd byte as path separator. if args: args = list(args) args[0] = appendsep(args[0]) if 'path' in kwds: kwds['path'] = appendsep(kwds['path']) return func(*args, **kwds) def wrapname(name, wrapper): module, name = name.rsplit('.', 1) module = sys.modules[module] func = getattr(module, name) def f(*args, **kwds): return wrapper(func, args, kwds) try: f.__name__ = func.__name__ # fails with Python 2.3 except Exception: pass setattr(module, name, f) # List of functions to be wrapped. # NOTE: os.path.dirname() and os.path.basename() are safe because # they use result of os.path.split() funcs = '''os.path.join os.path.split os.path.splitext os.path.normpath os.makedirs mercurial.util.endswithsep mercurial.util.splitpath mercurial.util.checkcase mercurial.util.fspath mercurial.util.pconvert mercurial.util.normpath mercurial.util.checkwinfilename mercurial.util.checkosfilename mercurial.util.split''' # These functions are required to be called with local encoded string # because they expects argument is local encoded string and cause # problem with unicode string. rfuncs = '''mercurial.encoding.upper mercurial.encoding.lower''' # List of Windows specific functions to be wrapped. winfuncs = '''os.path.splitunc''' # codec and alias names of sjis and big5 to be faked. problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs hkscs cp932 932 ms932 mskanji ms-kanji shift_jis csshiftjis shiftjis sjis s_jis shift_jis_2004 shiftjis2004 sjis_2004 sjis2004 shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213 950 cp950 ms950 ''' def extsetup(ui): # TODO: decide use of config section for this extension if ((not os.path.supports_unicode_filenames) and (sys.platform != 'cygwin')): ui.warn(_("[win32mbcs] cannot activate on this platform.\n")) return # determine encoding for filename global _encoding _encoding = ui.config('win32mbcs', 'encoding', encoding.encoding) # fake is only for relevant environment. if _encoding.lower() in problematic_encodings.split(): for f in funcs.split(): wrapname(f, wrapper) if os.name == 'nt': for f in winfuncs.split(): wrapname(f, wrapper) wrapname("mercurial.osutil.listdir", wrapperforlistdir) # wrap functions to be called with local byte string arguments for f in rfuncs.split(): wrapname(f, reversewrapper) # Check sys.args manually instead of using ui.debug() because # command line options is not yet applied when # extensions.loadall() is called. if '--debug' in sys.argv: ui.write("[win32mbcs] activated with encoding: %s\n" % _encoding) mercurial-2.8.2/hgext/gpg.py0000644000175000017500000002251612261160437017572 0ustar oxymoronoxymoron00000000000000# Copyright 2005, 2006 Benoit Boissinot # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''commands to sign and verify changesets''' import os, tempfile, binascii from mercurial import util, commands, match, cmdutil from mercurial import node as hgnode from mercurial.i18n import _ cmdtable = {} command = cmdutil.command(cmdtable) testedwith = 'internal' class gpg(object): def __init__(self, path, key=None): self.path = path self.key = (key and " --local-user \"%s\"" % key) or "" def sign(self, data): gpgcmd = "%s --sign --detach-sign%s" % (self.path, self.key) return util.filter(data, gpgcmd) def verify(self, data, sig): """ returns of the good and bad signatures""" sigfile = datafile = None try: # create temporary files fd, sigfile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".sig") fp = os.fdopen(fd, 'wb') fp.write(sig) fp.close() fd, datafile = tempfile.mkstemp(prefix="hg-gpg-", suffix=".txt") fp = os.fdopen(fd, 'wb') fp.write(data) fp.close() gpgcmd = ("%s --logger-fd 1 --status-fd 1 --verify " "\"%s\" \"%s\"" % (self.path, sigfile, datafile)) ret = util.filter("", gpgcmd) finally: for f in (sigfile, datafile): try: if f: os.unlink(f) except OSError: pass keys = [] key, fingerprint = None, None for l in ret.splitlines(): # see DETAILS in the gnupg documentation # filter the logger output if not l.startswith("[GNUPG:]"): continue l = l[9:] if l.startswith("VALIDSIG"): # fingerprint of the primary key fingerprint = l.split()[10] elif l.startswith("ERRSIG"): key = l.split(" ", 3)[:2] key.append("") fingerprint = None elif (l.startswith("GOODSIG") or l.startswith("EXPSIG") or l.startswith("EXPKEYSIG") or l.startswith("BADSIG")): if key is not None: keys.append(key + [fingerprint]) key = l.split(" ", 2) fingerprint = None if key is not None: keys.append(key + [fingerprint]) return keys def newgpg(ui, **opts): """create a new gpg instance""" gpgpath = ui.config("gpg", "cmd", "gpg") gpgkey = opts.get('key') if not gpgkey: gpgkey = ui.config("gpg", "key", None) return gpg(gpgpath, gpgkey) def sigwalk(repo): """ walk over every sigs, yields a couple ((node, version, sig), (filename, linenumber)) """ def parsefile(fileiter, context): ln = 1 for l in fileiter: if not l: continue yield (l.split(" ", 2), (context, ln)) ln += 1 # read the heads fl = repo.file(".hgsigs") for r in reversed(fl.heads()): fn = ".hgsigs|%s" % hgnode.short(r) for item in parsefile(fl.read(r).splitlines(), fn): yield item try: # read local signatures fn = "localsigs" for item in parsefile(repo.opener(fn), fn): yield item except IOError: pass def getkeys(ui, repo, mygpg, sigdata, context): """get the keys who signed a data""" fn, ln = context node, version, sig = sigdata prefix = "%s:%d" % (fn, ln) node = hgnode.bin(node) data = node2txt(repo, node, version) sig = binascii.a2b_base64(sig) keys = mygpg.verify(data, sig) validkeys = [] # warn for expired key and/or sigs for key in keys: if key[0] == "ERRSIG": ui.write(_("%s Unknown key ID \"%s\"\n") % (prefix, shortkey(ui, key[1][:15]))) continue if key[0] == "BADSIG": ui.write(_("%s Bad signature from \"%s\"\n") % (prefix, key[2])) continue if key[0] == "EXPSIG": ui.write(_("%s Note: Signature has expired" " (signed by: \"%s\")\n") % (prefix, key[2])) elif key[0] == "EXPKEYSIG": ui.write(_("%s Note: This key has expired" " (signed by: \"%s\")\n") % (prefix, key[2])) validkeys.append((key[1], key[2], key[3])) return validkeys @command("sigs", [], _('hg sigs')) def sigs(ui, repo): """list signed changesets""" mygpg = newgpg(ui) revs = {} for data, context in sigwalk(repo): node, version, sig = data fn, ln = context try: n = repo.lookup(node) except KeyError: ui.warn(_("%s:%d node does not exist\n") % (fn, ln)) continue r = repo.changelog.rev(n) keys = getkeys(ui, repo, mygpg, data, context) if not keys: continue revs.setdefault(r, []) revs[r].extend(keys) for rev in sorted(revs, reverse=True): for k in revs[rev]: r = "%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev))) ui.write("%-30s %s\n" % (keystr(ui, k), r)) @command("sigcheck", [], _('hg sigcheck REV')) def check(ui, repo, rev): """verify all the signatures there may be for a particular revision""" mygpg = newgpg(ui) rev = repo.lookup(rev) hexrev = hgnode.hex(rev) keys = [] for data, context in sigwalk(repo): node, version, sig = data if node == hexrev: k = getkeys(ui, repo, mygpg, data, context) if k: keys.extend(k) if not keys: ui.write(_("no valid signature for %s\n") % hgnode.short(rev)) return # print summary ui.write("%s is signed by:\n" % hgnode.short(rev)) for key in keys: ui.write(" %s\n" % keystr(ui, key)) def keystr(ui, key): """associate a string to a key (username, comment)""" keyid, user, fingerprint = key comment = ui.config("gpg", fingerprint, None) if comment: return "%s (%s)" % (user, comment) else: return user @command("sign", [('l', 'local', None, _('make the signature local')), ('f', 'force', None, _('sign even if the sigfile is modified')), ('', 'no-commit', None, _('do not commit the sigfile after signing')), ('k', 'key', '', _('the key id to sign with'), _('ID')), ('m', 'message', '', _('commit message'), _('TEXT')), ] + commands.commitopts2, _('hg sign [OPTION]... [REV]...')) def sign(ui, repo, *revs, **opts): """add a signature for the current or given revision If no revision is given, the parent of the working directory is used, or tip if no revision is checked out. See :hg:`help dates` for a list of formats valid for -d/--date. """ mygpg = newgpg(ui, **opts) sigver = "0" sigmessage = "" date = opts.get('date') if date: opts['date'] = util.parsedate(date) if revs: nodes = [repo.lookup(n) for n in revs] else: nodes = [node for node in repo.dirstate.parents() if node != hgnode.nullid] if len(nodes) > 1: raise util.Abort(_('uncommitted merge - please provide a ' 'specific revision')) if not nodes: nodes = [repo.changelog.tip()] for n in nodes: hexnode = hgnode.hex(n) ui.write(_("signing %d:%s\n") % (repo.changelog.rev(n), hgnode.short(n))) # build data data = node2txt(repo, n, sigver) sig = mygpg.sign(data) if not sig: raise util.Abort(_("error while signing")) sig = binascii.b2a_base64(sig) sig = sig.replace("\n", "") sigmessage += "%s %s %s\n" % (hexnode, sigver, sig) # write it if opts['local']: repo.opener.append("localsigs", sigmessage) return msigs = match.exact(repo.root, '', ['.hgsigs']) s = repo.status(match=msigs, unknown=True, ignored=True)[:6] if util.any(s) and not opts["force"]: raise util.Abort(_("working copy of .hgsigs is changed " "(please commit .hgsigs manually " "or use --force)")) sigsfile = repo.wfile(".hgsigs", "ab") sigsfile.write(sigmessage) sigsfile.close() if '.hgsigs' not in repo.dirstate: repo[None].add([".hgsigs"]) if opts["no_commit"]: return message = opts['message'] if not message: # we don't translate commit messages message = "\n".join(["Added signature for changeset %s" % hgnode.short(n) for n in nodes]) try: repo.commit(message, opts['user'], opts['date'], match=msigs) except ValueError, inst: raise util.Abort(str(inst)) def shortkey(ui, key): if len(key) != 16: ui.debug("key ID \"%s\" format error\n" % key) return key return key[-8:] def node2txt(repo, node, ver): """map a manifest into some text""" if ver == "0": return "%s\n" % hgnode.hex(node) else: raise util.Abort(_("unknown signature version")) mercurial-2.8.2/hgext/share.py0000644000175000017500000000444112261160437020114 0ustar oxymoronoxymoron00000000000000# Copyright 2006, 2007 Matt Mackall # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''share a common history between several working directories''' from mercurial.i18n import _ from mercurial import hg, commands, util testedwith = 'internal' def share(ui, source, dest=None, noupdate=False): """create a new shared repository Initialize a new repository and working directory that shares its history with another repository. .. note:: using rollback or extensions that destroy/modify history (mq, rebase, etc.) can cause considerable confusion with shared clones. In particular, if two shared clones are both updated to the same changeset, and one of them destroys that changeset with rollback, the other clone will suddenly stop working: all operations will fail with "abort: working directory has unknown parent". The only known workaround is to use debugsetparents on the broken clone to reset it to a changeset that still exists. """ return hg.share(ui, source, dest, not noupdate) def unshare(ui, repo): """convert a shared repository to a normal one Copy the store data to the repo and remove the sharedpath data. """ if repo.sharedpath == repo.path: raise util.Abort(_("this is not a shared repo")) destlock = lock = None lock = repo.lock() try: # we use locks here because if we race with commit, we # can end up with extra data in the cloned revlogs that's # not pointed to by changesets, thus causing verify to # fail destlock = hg.copystore(ui, repo, repo.path) sharefile = repo.join('sharedpath') util.rename(sharefile, sharefile + '.old') repo.requirements.discard('sharedpath') repo._writerequirements() finally: destlock and destlock.release() lock and lock.release() # update store, spath, sopener and sjoin of repo repo.unfiltered().__init__(repo.baseui, repo.root) cmdtable = { "share": (share, [('U', 'noupdate', None, _('do not create a working copy'))], _('[-U] SOURCE [DEST]')), "unshare": (unshare, [], ''), } commands.norepo += " share" mercurial-2.8.2/hgext/largefiles/0000755000175000017500000000000012261160455020552 5ustar oxymoronoxymoron00000000000000mercurial-2.8.2/hgext/largefiles/wirestore.py0000644000175000017500000000247012261160437023152 0ustar oxymoronoxymoron00000000000000# Copyright 2010-2011 Fog Creek Software # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''largefile store working over Mercurial's wire protocol''' import lfutil import remotestore class wirestore(remotestore.remotestore): def __init__(self, ui, repo, remote): cap = remote.capable('largefiles') if not cap: raise lfutil.storeprotonotcapable([]) storetypes = cap.split(',') if 'serve' not in storetypes: raise lfutil.storeprotonotcapable(storetypes) self.remote = remote super(wirestore, self).__init__(ui, repo, remote.url()) def _put(self, hash, fd): return self.remote.putlfile(hash, fd) def _get(self, hash): return self.remote.getlfile(hash) def _stat(self, hashes): '''For each hash, return 0 if it is available, other values if not. It is usually 2 if the largefile is missing, but might be 1 the server has a corrupted copy.''' batch = self.remote.batch() futures = {} for hash in hashes: futures[hash] = batch.statlfile(hash) batch.submit() retval = {} for hash in hashes: retval[hash] = futures[hash].value return retval mercurial-2.8.2/hgext/largefiles/proto.py0000644000175000017500000001534512261160437022277 0ustar oxymoronoxymoron00000000000000# Copyright 2011 Fog Creek Software # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. import os import urllib2 import re from mercurial import error, httppeer, util, wireproto from mercurial.wireproto import batchable, future from mercurial.i18n import _ import lfutil LARGEFILES_REQUIRED_MSG = ('\nThis repository uses the largefiles extension.' '\n\nPlease enable it in your Mercurial config ' 'file.\n') # these will all be replaced by largefiles.uisetup capabilitiesorig = None ssholdcallstream = None httpoldcallstream = None def putlfile(repo, proto, sha): '''Put a largefile into a repository's local store and into the user cache.''' proto.redirect() path = lfutil.storepath(repo, sha) util.makedirs(os.path.dirname(path)) tmpfp = util.atomictempfile(path, createmode=repo.store.createmode) try: try: proto.getfile(tmpfp) tmpfp._fp.seek(0) if sha != lfutil.hexsha1(tmpfp._fp): raise IOError(0, _('largefile contents do not match hash')) tmpfp.close() lfutil.linktousercache(repo, sha) except IOError, e: repo.ui.warn(_('largefiles: failed to put %s into store: %s\n') % (sha, e.strerror)) return wireproto.pushres(1) finally: tmpfp.discard() return wireproto.pushres(0) def getlfile(repo, proto, sha): '''Retrieve a largefile from the repository-local cache or system cache.''' filename = lfutil.findfile(repo, sha) if not filename: raise util.Abort(_('requested largefile %s not present in cache') % sha) f = open(filename, 'rb') length = os.fstat(f.fileno())[6] # Since we can't set an HTTP content-length header here, and # Mercurial core provides no way to give the length of a streamres # (and reading the entire file into RAM would be ill-advised), we # just send the length on the first line of the response, like the # ssh proto does for string responses. def generator(): yield '%d\n' % length for chunk in util.filechunkiter(f): yield chunk return wireproto.streamres(generator()) def statlfile(repo, proto, sha): '''Return '2\n' if the largefile is missing, '0\n' if it seems to be in good condition. The value 1 is reserved for mismatched checksum, but that is too expensive to be verified on every stat and must be caught be running 'hg verify' server side.''' filename = lfutil.findfile(repo, sha) if not filename: return '2\n' return '0\n' def wirereposetup(ui, repo): class lfileswirerepository(repo.__class__): def putlfile(self, sha, fd): # unfortunately, httprepository._callpush tries to convert its # input file-like into a bundle before sending it, so we can't use # it ... if issubclass(self.__class__, httppeer.httppeer): res = None try: res = self._call('putlfile', data=fd, sha=sha, headers={'content-type':'application/mercurial-0.1'}) d, output = res.split('\n', 1) for l in output.splitlines(True): self.ui.warn(_('remote: '), l) # assume l ends with \n return int(d) except (ValueError, urllib2.HTTPError): self.ui.warn(_('unexpected putlfile response: %r\n') % res) return 1 # ... but we can't use sshrepository._call because the data= # argument won't get sent, and _callpush does exactly what we want # in this case: send the data straight through else: try: ret, output = self._callpush("putlfile", fd, sha=sha) if ret == "": raise error.ResponseError(_('putlfile failed:'), output) return int(ret) except IOError: return 1 except ValueError: raise error.ResponseError( _('putlfile failed (unexpected response):'), ret) def getlfile(self, sha): """returns an iterable with the chunks of the file with sha sha""" stream = self._callstream("getlfile", sha=sha) length = stream.readline() try: length = int(length) except ValueError: self._abort(error.ResponseError(_("unexpected response:"), length)) # SSH streams will block if reading more than length for chunk in util.filechunkiter(stream, 128 * 1024, length): yield chunk # HTTP streams must hit the end to process the last empty # chunk of Chunked-Encoding so the connection can be reused. if issubclass(self.__class__, httppeer.httppeer): chunk = stream.read(1) if chunk: self._abort(error.ResponseError(_("unexpected response:"), chunk)) @batchable def statlfile(self, sha): f = future() result = {'sha': sha} yield result, f try: yield int(f.value) except (ValueError, urllib2.HTTPError): # If the server returns anything but an integer followed by a # newline, newline, it's not speaking our language; if we get # an HTTP error, we can't be sure the largefile is present; # either way, consider it missing. yield 2 repo.__class__ = lfileswirerepository # advertise the largefiles=serve capability def capabilities(repo, proto): return capabilitiesorig(repo, proto) + ' largefiles=serve' def heads(repo, proto): if lfutil.islfilesrepo(repo): return wireproto.ooberror(LARGEFILES_REQUIRED_MSG) return wireproto.heads(repo, proto) def sshrepocallstream(self, cmd, **args): if cmd == 'heads' and self.capable('largefiles'): cmd = 'lheads' if cmd == 'batch' and self.capable('largefiles'): args['cmds'] = args['cmds'].replace('heads ', 'lheads ') return ssholdcallstream(self, cmd, **args) headsre = re.compile(r'(^|;)heads\b') def httprepocallstream(self, cmd, **args): if cmd == 'heads' and self.capable('largefiles'): cmd = 'lheads' if cmd == 'batch' and self.capable('largefiles'): args['cmds'] = headsre.sub('lheads', args['cmds']) return httpoldcallstream(self, cmd, **args) mercurial-2.8.2/hgext/largefiles/uisetup.py0000644000175000017500000002012312261160437022620 0ustar oxymoronoxymoron00000000000000# Copyright 2009-2010 Gregory P. Ward # Copyright 2009-2010 Intelerad Medical Systems Incorporated # Copyright 2010-2011 Fog Creek Software # Copyright 2010-2011 Unity Technologies # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''setup for largefiles extension: uisetup''' from mercurial import archival, cmdutil, commands, extensions, filemerge, hg, \ httppeer, merge, scmutil, sshpeer, wireproto, revset from mercurial.i18n import _ from mercurial.hgweb import hgweb_mod, webcommands from mercurial.subrepo import hgsubrepo import overrides import proto def uisetup(ui): # Disable auto-status for some commands which assume that all # files in the result are under Mercurial's control entry = extensions.wrapcommand(commands.table, 'add', overrides.overrideadd) addopt = [('', 'large', None, _('add as largefile')), ('', 'normal', None, _('add as normal file')), ('', 'lfsize', '', _('add all files above this size ' '(in megabytes) as largefiles ' '(default: 10)'))] entry[1].extend(addopt) # The scmutil function is called both by the (trivial) addremove command, # and in the process of handling commit -A (issue3542) entry = extensions.wrapfunction(scmutil, 'addremove', overrides.scmutiladdremove) entry = extensions.wrapcommand(commands.table, 'remove', overrides.overrideremove) entry = extensions.wrapcommand(commands.table, 'forget', overrides.overrideforget) # Subrepos call status function entry = extensions.wrapcommand(commands.table, 'status', overrides.overridestatus) entry = extensions.wrapfunction(hgsubrepo, 'status', overrides.overridestatusfn) entry = extensions.wrapcommand(commands.table, 'log', overrides.overridelog) entry = extensions.wrapcommand(commands.table, 'rollback', overrides.overriderollback) entry = extensions.wrapcommand(commands.table, 'verify', overrides.overrideverify) verifyopt = [('', 'large', None, _('verify that all largefiles in current revision exists')), ('', 'lfa', None, _('verify largefiles in all revisions, not just current')), ('', 'lfc', None, _('verify local largefile contents, not just existence'))] entry[1].extend(verifyopt) entry = extensions.wrapcommand(commands.table, 'debugstate', overrides.overridedebugstate) debugstateopt = [('', 'large', None, _('display largefiles dirstate'))] entry[1].extend(debugstateopt) entry = extensions.wrapcommand(commands.table, 'outgoing', overrides.overrideoutgoing) outgoingopt = [('', 'large', None, _('display outgoing largefiles'))] entry[1].extend(outgoingopt) entry = extensions.wrapcommand(commands.table, 'summary', overrides.overridesummary) summaryopt = [('', 'large', None, _('display outgoing largefiles'))] entry[1].extend(summaryopt) entry = extensions.wrapcommand(commands.table, 'update', overrides.overrideupdate) entry = extensions.wrapcommand(commands.table, 'pull', overrides.overridepull) pullopt = [('', 'all-largefiles', None, _('download all pulled versions of largefiles (DEPRECATED)')), ('', 'lfrev', [], _('download largefiles for these revisions'), _('REV'))] entry[1].extend(pullopt) revset.symbols['pulled'] = overrides.pulledrevsetsymbol entry = extensions.wrapcommand(commands.table, 'clone', overrides.overrideclone) cloneopt = [('', 'all-largefiles', None, _('download all versions of all largefiles'))] entry[1].extend(cloneopt) entry = extensions.wrapfunction(hg, 'clone', overrides.hgclone) entry = extensions.wrapcommand(commands.table, 'cat', overrides.overridecat) entry = extensions.wrapfunction(merge, '_checkunknownfile', overrides.overridecheckunknownfile) entry = extensions.wrapfunction(merge, 'manifestmerge', overrides.overridemanifestmerge) entry = extensions.wrapfunction(filemerge, 'filemerge', overrides.overridefilemerge) entry = extensions.wrapfunction(cmdutil, 'copy', overrides.overridecopy) # Summary calls dirty on the subrepos entry = extensions.wrapfunction(hgsubrepo, 'dirty', overrides.overridedirty) # Backout calls revert so we need to override both the command and the # function entry = extensions.wrapcommand(commands.table, 'revert', overrides.overriderevert) entry = extensions.wrapfunction(commands, 'revert', overrides.overriderevert) extensions.wrapfunction(hg, 'updaterepo', overrides.hgupdaterepo) extensions.wrapfunction(hg, 'merge', overrides.hgmerge) extensions.wrapfunction(archival, 'archive', overrides.overridearchive) extensions.wrapfunction(hgsubrepo, 'archive', overrides.hgsubrepoarchive) extensions.wrapfunction(cmdutil, 'bailifchanged', overrides.overridebailifchanged) # create the new wireproto commands ... wireproto.commands['putlfile'] = (proto.putlfile, 'sha') wireproto.commands['getlfile'] = (proto.getlfile, 'sha') wireproto.commands['statlfile'] = (proto.statlfile, 'sha') # ... and wrap some existing ones wireproto.commands['capabilities'] = (proto.capabilities, '') wireproto.commands['heads'] = (proto.heads, '') wireproto.commands['lheads'] = (wireproto.heads, '') # make putlfile behave the same as push and {get,stat}lfile behave # the same as pull w.r.t. permissions checks hgweb_mod.perms['putlfile'] = 'push' hgweb_mod.perms['getlfile'] = 'pull' hgweb_mod.perms['statlfile'] = 'pull' extensions.wrapfunction(webcommands, 'decodepath', overrides.decodepath) # the hello wireproto command uses wireproto.capabilities, so it won't see # our largefiles capability unless we replace the actual function as well. proto.capabilitiesorig = wireproto.capabilities wireproto.capabilities = proto.capabilities # can't do this in reposetup because it needs to have happened before # wirerepo.__init__ is called proto.ssholdcallstream = sshpeer.sshpeer._callstream proto.httpoldcallstream = httppeer.httppeer._callstream sshpeer.sshpeer._callstream = proto.sshrepocallstream httppeer.httppeer._callstream = proto.httprepocallstream # override some extensions' stuff as well for name, module in extensions.extensions(): if name == 'fetch': extensions.wrapcommand(getattr(module, 'cmdtable'), 'fetch', overrides.overridefetch) if name == 'purge': extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge', overrides.overridepurge) if name == 'rebase': extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase', overrides.overriderebase) if name == 'transplant': extensions.wrapcommand(getattr(module, 'cmdtable'), 'transplant', overrides.overridetransplant) if name == 'convert': convcmd = getattr(module, 'convcmd') hgsink = getattr(convcmd, 'mercurial_sink') extensions.wrapfunction(hgsink, 'before', overrides.mercurialsinkbefore) extensions.wrapfunction(hgsink, 'after', overrides.mercurialsinkafter) mercurial-2.8.2/hgext/largefiles/overrides.py0000644000175000017500000013335112261160437023134 0ustar oxymoronoxymoron00000000000000# Copyright 2009-2010 Gregory P. Ward # Copyright 2009-2010 Intelerad Medical Systems Incorporated # Copyright 2010-2011 Fog Creek Software # Copyright 2010-2011 Unity Technologies # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''Overridden Mercurial commands and functions for the largefiles extension''' import os import copy from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \ node, archival, error, merge, discovery from mercurial.i18n import _ from mercurial.node import hex from hgext import rebase import lfutil import lfcommands import basestore # -- Utility functions: commonly/repeatedly needed functionality --------------- def installnormalfilesmatchfn(manifest): '''overrides scmutil.match so that the matcher it returns will ignore all largefiles''' oldmatch = None # for the closure def overridematch(ctx, pats=[], opts={}, globbed=False, default='relpath'): match = oldmatch(ctx, pats, opts, globbed, default) m = copy.copy(match) notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in manifest) m._files = filter(notlfile, m._files) m._fmap = set(m._files) m._always = False origmatchfn = m.matchfn m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None return m oldmatch = installmatchfn(overridematch) def installmatchfn(f): oldmatch = scmutil.match setattr(f, 'oldmatch', oldmatch) scmutil.match = f return oldmatch def restorematchfn(): '''restores scmutil.match to what it was before installnormalfilesmatchfn was called. no-op if scmutil.match is its original function. Note that n calls to installnormalfilesmatchfn will require n calls to restore matchfn to reverse''' scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match) def addlargefiles(ui, repo, *pats, **opts): large = opts.pop('large', None) lfsize = lfutil.getminsize( ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None)) lfmatcher = None if lfutil.islfilesrepo(repo): lfpats = ui.configlist(lfutil.longname, 'patterns', default=[]) if lfpats: lfmatcher = match_.match(repo.root, '', list(lfpats)) lfnames = [] m = scmutil.match(repo[None], pats, opts) m.bad = lambda x, y: None wctx = repo[None] for f in repo.walk(m): exact = m.exact(f) lfile = lfutil.standin(f) in wctx nfile = f in wctx exists = lfile or nfile # Don't warn the user when they attempt to add a normal tracked file. # The normal add code will do that for us. if exact and exists: if lfile: ui.warn(_('%s already a largefile\n') % f) continue if (exact or not exists) and not lfutil.isstandin(f): wfile = repo.wjoin(f) # In case the file was removed previously, but not committed # (issue3507) if not os.path.exists(wfile): continue abovemin = (lfsize and os.lstat(wfile).st_size >= lfsize * 1024 * 1024) if large or abovemin or (lfmatcher and lfmatcher(f)): lfnames.append(f) if ui.verbose or not exact: ui.status(_('adding %s as a largefile\n') % m.rel(f)) bad = [] standins = [] # Need to lock, otherwise there could be a race condition between # when standins are created and added to the repo. wlock = repo.wlock() try: if not opts.get('dry_run'): lfdirstate = lfutil.openlfdirstate(ui, repo) for f in lfnames: standinname = lfutil.standin(f) lfutil.writestandin(repo, standinname, hash='', executable=lfutil.getexecutable(repo.wjoin(f))) standins.append(standinname) if lfdirstate[f] == 'r': lfdirstate.normallookup(f) else: lfdirstate.add(f) lfdirstate.write() bad += [lfutil.splitstandin(f) for f in repo[None].add(standins) if f in m.files()] finally: wlock.release() return bad def removelargefiles(ui, repo, *pats, **opts): after = opts.get('after') if not pats and not after: raise util.Abort(_('no files specified')) m = scmutil.match(repo[None], pats, opts) try: repo.lfstatus = True s = repo.status(match=m, clean=True) finally: repo.lfstatus = False manifest = repo[None].manifest() modified, added, deleted, clean = [[f for f in list if lfutil.standin(f) in manifest] for list in [s[0], s[1], s[3], s[6]]] def warn(files, msg): for f in files: ui.warn(msg % m.rel(f)) return int(len(files) > 0) result = 0 if after: remove, forget = deleted, [] result = warn(modified + added + clean, _('not removing %s: file still exists\n')) else: remove, forget = deleted + clean, [] result = warn(modified, _('not removing %s: file is modified (use -f' ' to force removal)\n')) result = warn(added, _('not removing %s: file has been marked for add' ' (use forget to undo)\n')) or result for f in sorted(remove + forget): if ui.verbose or not m.exact(f): ui.status(_('removing %s\n') % m.rel(f)) # Need to lock because standin files are deleted then removed from the # repository and we could race in-between. wlock = repo.wlock() try: lfdirstate = lfutil.openlfdirstate(ui, repo) for f in remove: if not after: # If this is being called by addremove, notify the user that we # are removing the file. if getattr(repo, "_isaddremove", False): ui.status(_('removing %s\n') % f) util.unlinkpath(repo.wjoin(f), ignoremissing=True) lfdirstate.remove(f) lfdirstate.write() forget = [lfutil.standin(f) for f in forget] remove = [lfutil.standin(f) for f in remove] repo[None].forget(forget) # If this is being called by addremove, let the original addremove # function handle this. if not getattr(repo, "_isaddremove", False): for f in remove: util.unlinkpath(repo.wjoin(f), ignoremissing=True) repo[None].forget(remove) finally: wlock.release() return result # For overriding mercurial.hgweb.webcommands so that largefiles will # appear at their right place in the manifests. def decodepath(orig, path): return lfutil.splitstandin(path) or path # -- Wrappers: modify existing commands -------------------------------- # Add works by going through the files that the user wanted to add and # checking if they should be added as largefiles. Then it makes a new # matcher which matches only the normal files and runs the original # version of add. def overrideadd(orig, ui, repo, *pats, **opts): normal = opts.pop('normal') if normal: if opts.get('large'): raise util.Abort(_('--normal cannot be used with --large')) return orig(ui, repo, *pats, **opts) bad = addlargefiles(ui, repo, *pats, **opts) installnormalfilesmatchfn(repo[None].manifest()) result = orig(ui, repo, *pats, **opts) restorematchfn() return (result == 1 or bad) and 1 or 0 def overrideremove(orig, ui, repo, *pats, **opts): installnormalfilesmatchfn(repo[None].manifest()) result = orig(ui, repo, *pats, **opts) restorematchfn() return removelargefiles(ui, repo, *pats, **opts) or result def overridestatusfn(orig, repo, rev2, **opts): try: repo._repo.lfstatus = True return orig(repo, rev2, **opts) finally: repo._repo.lfstatus = False def overridestatus(orig, ui, repo, *pats, **opts): try: repo.lfstatus = True return orig(ui, repo, *pats, **opts) finally: repo.lfstatus = False def overridedirty(orig, repo, ignoreupdate=False): try: repo._repo.lfstatus = True return orig(repo, ignoreupdate) finally: repo._repo.lfstatus = False def overridelog(orig, ui, repo, *pats, **opts): def overridematch(ctx, pats=[], opts={}, globbed=False, default='relpath'): """Matcher that merges root directory with .hglf, suitable for log. It is still possible to match .hglf directly. For any listed files run log on the standin too. matchfn tries both the given filename and with .hglf stripped. """ match = oldmatch(ctx, pats, opts, globbed, default) m = copy.copy(match) for i in range(0, len(m._files)): standin = lfutil.standin(m._files[i]) if standin in repo[ctx.node()]: m._files[i] = standin m._fmap = set(m._files) m._always = False origmatchfn = m.matchfn def lfmatchfn(f): lf = lfutil.splitstandin(f) if lf is not None and origmatchfn(lf): return True r = origmatchfn(f) return r m.matchfn = lfmatchfn return m oldmatch = installmatchfn(overridematch) try: repo.lfstatus = True return orig(ui, repo, *pats, **opts) finally: repo.lfstatus = False restorematchfn() def overrideverify(orig, ui, repo, *pats, **opts): large = opts.pop('large', False) all = opts.pop('lfa', False) contents = opts.pop('lfc', False) result = orig(ui, repo, *pats, **opts) if large or all or contents: result = result or lfcommands.verifylfiles(ui, repo, all, contents) return result def overridedebugstate(orig, ui, repo, *pats, **opts): large = opts.pop('large', False) if large: lfcommands.debugdirstate(ui, repo) else: orig(ui, repo, *pats, **opts) # Override needs to refresh standins so that update's normal merge # will go through properly. Then the other update hook (overriding repo.update) # will get the new files. Filemerge is also overridden so that the merge # will merge standins correctly. def overrideupdate(orig, ui, repo, *pats, **opts): lfdirstate = lfutil.openlfdirstate(ui, repo) s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False, False, False) (unsure, modified, added, removed, missing, unknown, ignored, clean) = s # Need to lock between the standins getting updated and their # largefiles getting updated wlock = repo.wlock() try: if opts['check']: mod = len(modified) > 0 for lfile in unsure: standin = lfutil.standin(lfile) if repo['.'][standin].data().strip() != \ lfutil.hashfile(repo.wjoin(lfile)): mod = True else: lfdirstate.normal(lfile) lfdirstate.write() if mod: raise util.Abort(_('uncommitted changes')) # XXX handle removed differently if not opts['clean']: for lfile in unsure + modified + added: lfutil.updatestandin(repo, lfutil.standin(lfile)) finally: wlock.release() return orig(ui, repo, *pats, **opts) # Before starting the manifest merge, merge.updates will call # _checkunknown to check if there are any files in the merged-in # changeset that collide with unknown files in the working copy. # # The largefiles are seen as unknown, so this prevents us from merging # in a file 'foo' if we already have a largefile with the same name. # # The overridden function filters the unknown files by removing any # largefiles. This makes the merge proceed and we can then handle this # case further in the overridden manifestmerge function below. def overridecheckunknownfile(origfn, repo, wctx, mctx, f): if lfutil.standin(repo.dirstate.normalize(f)) in wctx: return False return origfn(repo, wctx, mctx, f) # The manifest merge handles conflicts on the manifest level. We want # to handle changes in largefile-ness of files at this level too. # # The strategy is to run the original manifestmerge and then process # the action list it outputs. There are two cases we need to deal with: # # 1. Normal file in p1, largefile in p2. Here the largefile is # detected via its standin file, which will enter the working copy # with a "get" action. It is not "merge" since the standin is all # Mercurial is concerned with at this level -- the link to the # existing normal file is not relevant here. # # 2. Largefile in p1, normal file in p2. Here we get a "merge" action # since the largefile will be present in the working copy and # different from the normal file in p2. Mercurial therefore # triggers a merge action. # # In both cases, we prompt the user and emit new actions to either # remove the standin (if the normal file was kept) or to remove the # normal file and get the standin (if the largefile was kept). The # default prompt answer is to use the largefile version since it was # presumably changed on purpose. # # Finally, the merge.applyupdates function will then take care of # writing the files into the working copy and lfcommands.updatelfiles # will update the largefiles. def overridemanifestmerge(origfn, repo, p1, p2, pa, branchmerge, force, partial, acceptremote=False): overwrite = force and not branchmerge actions = origfn(repo, p1, p2, pa, branchmerge, force, partial, acceptremote) if overwrite: return actions removes = set(a[0] for a in actions if a[1] == 'r') processed = [] for action in actions: f, m, args, msg = action splitstandin = f and lfutil.splitstandin(f) if (m == "g" and splitstandin is not None and splitstandin in p1 and splitstandin not in removes): # Case 1: normal file in the working copy, largefile in # the second parent lfile = splitstandin standin = f msg = _('remote turned local normal file %s into a largefile\n' 'use (l)argefile or keep (n)ormal file?' '$$ &Largefile $$ &Normal file') % lfile if repo.ui.promptchoice(msg, 0) == 0: processed.append((lfile, "r", None, msg)) processed.append((standin, "g", (p2.flags(standin),), msg)) else: processed.append((standin, "r", None, msg)) elif (m == "g" and lfutil.standin(f) in p1 and lfutil.standin(f) not in removes): # Case 2: largefile in the working copy, normal file in # the second parent standin = lfutil.standin(f) lfile = f msg = _('remote turned local largefile %s into a normal file\n' 'keep (l)argefile or use (n)ormal file?' '$$ &Largefile $$ &Normal file') % lfile if repo.ui.promptchoice(msg, 0) == 0: processed.append((lfile, "r", None, msg)) else: processed.append((standin, "r", None, msg)) processed.append((lfile, "g", (p2.flags(lfile),), msg)) else: processed.append(action) return processed # Override filemerge to prompt the user about how they wish to merge # largefiles. This will handle identical edits, and copy/rename + # edit without prompting the user. def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca): # Use better variable names here. Because this is a wrapper we cannot # change the variable names in the function declaration. fcdest, fcother, fcancestor = fcd, fco, fca if not lfutil.isstandin(orig): return origfn(repo, mynode, orig, fcdest, fcother, fcancestor) else: if not fcother.cmp(fcdest): # files identical? return None # backwards, use working dir parent as ancestor if fcancestor == fcother: fcancestor = fcdest.parents()[0] if orig != fcother.path(): repo.ui.status(_('merging %s and %s to %s\n') % (lfutil.splitstandin(orig), lfutil.splitstandin(fcother.path()), lfutil.splitstandin(fcdest.path()))) else: repo.ui.status(_('merging %s\n') % lfutil.splitstandin(fcdest.path())) if fcancestor.path() != fcother.path() and fcother.data() == \ fcancestor.data(): return 0 if fcancestor.path() != fcdest.path() and fcdest.data() == \ fcancestor.data(): repo.wwrite(fcdest.path(), fcother.data(), fcother.flags()) return 0 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n' 'keep (l)ocal or take (o)ther?' '$$ &Local $$ &Other') % lfutil.splitstandin(orig), 0) == 0: return 0 else: repo.wwrite(fcdest.path(), fcother.data(), fcother.flags()) return 0 # Copy first changes the matchers to match standins instead of # largefiles. Then it overrides util.copyfile in that function it # checks if the destination largefile already exists. It also keeps a # list of copied files so that the largefiles can be copied and the # dirstate updated. def overridecopy(orig, ui, repo, pats, opts, rename=False): # doesn't remove largefile on rename if len(pats) < 2: # this isn't legal, let the original function deal with it return orig(ui, repo, pats, opts, rename) def makestandin(relpath): path = scmutil.canonpath(repo.root, repo.getcwd(), relpath) return os.path.join(repo.wjoin(lfutil.standin(path))) fullpats = scmutil.expandpats(pats) dest = fullpats[-1] if os.path.isdir(dest): if not os.path.isdir(makestandin(dest)): os.makedirs(makestandin(dest)) # This could copy both lfiles and normal files in one command, # but we don't want to do that. First replace their matcher to # only match normal files and run it, then replace it to just # match largefiles and run it again. nonormalfiles = False nolfiles = False try: try: installnormalfilesmatchfn(repo[None].manifest()) result = orig(ui, repo, pats, opts, rename) except util.Abort, e: if str(e) != _('no files to copy'): raise e else: nonormalfiles = True result = 0 finally: restorematchfn() # The first rename can cause our current working directory to be removed. # In that case there is nothing left to copy/rename so just quit. try: repo.getcwd() except OSError: return result try: try: # When we call orig below it creates the standins but we don't add # them to the dir state until later so lock during that time. wlock = repo.wlock() manifest = repo[None].manifest() oldmatch = None # for the closure def overridematch(ctx, pats=[], opts={}, globbed=False, default='relpath'): newpats = [] # The patterns were previously mangled to add the standin # directory; we need to remove that now for pat in pats: if match_.patkind(pat) is None and lfutil.shortname in pat: newpats.append(pat.replace(lfutil.shortname, '')) else: newpats.append(pat) match = oldmatch(ctx, newpats, opts, globbed, default) m = copy.copy(match) lfile = lambda f: lfutil.standin(f) in manifest m._files = [lfutil.standin(f) for f in m._files if lfile(f)] m._fmap = set(m._files) m._always = False origmatchfn = m.matchfn m.matchfn = lambda f: (lfutil.isstandin(f) and (f in manifest) and origmatchfn(lfutil.splitstandin(f)) or None) return m oldmatch = installmatchfn(overridematch) listpats = [] for pat in pats: if match_.patkind(pat) is not None: listpats.append(pat) else: listpats.append(makestandin(pat)) try: origcopyfile = util.copyfile copiedfiles = [] def overridecopyfile(src, dest): if (lfutil.shortname in src and dest.startswith(repo.wjoin(lfutil.shortname))): destlfile = dest.replace(lfutil.shortname, '') if not opts['force'] and os.path.exists(destlfile): raise IOError('', _('destination largefile already exists')) copiedfiles.append((src, dest)) origcopyfile(src, dest) util.copyfile = overridecopyfile result += orig(ui, repo, listpats, opts, rename) finally: util.copyfile = origcopyfile lfdirstate = lfutil.openlfdirstate(ui, repo) for (src, dest) in copiedfiles: if (lfutil.shortname in src and dest.startswith(repo.wjoin(lfutil.shortname))): srclfile = src.replace(repo.wjoin(lfutil.standin('')), '') destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '') destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.' if not os.path.isdir(destlfiledir): os.makedirs(destlfiledir) if rename: os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile)) lfdirstate.remove(srclfile) else: util.copyfile(repo.wjoin(srclfile), repo.wjoin(destlfile)) lfdirstate.add(destlfile) lfdirstate.write() except util.Abort, e: if str(e) != _('no files to copy'): raise e else: nolfiles = True finally: restorematchfn() wlock.release() if nolfiles and nonormalfiles: raise util.Abort(_('no files to copy')) return result # When the user calls revert, we have to be careful to not revert any # changes to other largefiles accidentally. This means we have to keep # track of the largefiles that are being reverted so we only pull down # the necessary largefiles. # # Standins are only updated (to match the hash of largefiles) before # commits. Update the standins then run the original revert, changing # the matcher to hit standins instead of largefiles. Based on the # resulting standins update the largefiles. Then return the standins # to their proper state def overriderevert(orig, ui, repo, *pats, **opts): # Because we put the standins in a bad state (by updating them) # and then return them to a correct state we need to lock to # prevent others from changing them in their incorrect state. wlock = repo.wlock() try: lfdirstate = lfutil.openlfdirstate(ui, repo) (modified, added, removed, missing, unknown, ignored, clean) = \ lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev()) lfdirstate.write() for lfile in modified: lfutil.updatestandin(repo, lfutil.standin(lfile)) for lfile in missing: if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))): os.unlink(repo.wjoin(lfutil.standin(lfile))) try: ctx = scmutil.revsingle(repo, opts.get('rev')) oldmatch = None # for the closure def overridematch(ctx, pats=[], opts={}, globbed=False, default='relpath'): match = oldmatch(ctx, pats, opts, globbed, default) m = copy.copy(match) def tostandin(f): if lfutil.standin(f) in ctx: return lfutil.standin(f) elif lfutil.standin(f) in repo[None]: return None return f m._files = [tostandin(f) for f in m._files] m._files = [f for f in m._files if f is not None] m._fmap = set(m._files) m._always = False origmatchfn = m.matchfn def matchfn(f): if lfutil.isstandin(f): # We need to keep track of what largefiles are being # matched so we know which ones to update later -- # otherwise we accidentally revert changes to other # largefiles. This is repo-specific, so duckpunch the # repo object to keep the list of largefiles for us # later. if origmatchfn(lfutil.splitstandin(f)) and \ (f in repo[None] or f in ctx): lfileslist = getattr(repo, '_lfilestoupdate', []) lfileslist.append(lfutil.splitstandin(f)) repo._lfilestoupdate = lfileslist return True else: return False return origmatchfn(f) m.matchfn = matchfn return m oldmatch = installmatchfn(overridematch) scmutil.match matches = overridematch(repo[None], pats, opts) orig(ui, repo, *pats, **opts) finally: restorematchfn() lfileslist = getattr(repo, '_lfilestoupdate', []) lfcommands.updatelfiles(ui, repo, filelist=lfileslist, printmessage=False) # empty out the largefiles list so we start fresh next time repo._lfilestoupdate = [] for lfile in modified: if lfile in lfileslist: if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\ in repo['.']: lfutil.writestandin(repo, lfutil.standin(lfile), repo['.'][lfile].data().strip(), 'x' in repo['.'][lfile].flags()) lfdirstate = lfutil.openlfdirstate(ui, repo) for lfile in added: standin = lfutil.standin(lfile) if standin not in ctx and (standin in matches or opts.get('all')): if lfile in lfdirstate: lfdirstate.drop(lfile) util.unlinkpath(repo.wjoin(standin)) lfdirstate.write() finally: wlock.release() def hgupdaterepo(orig, repo, node, overwrite): if not overwrite: # Only call updatelfiles on the standins that have changed to save time oldstandins = lfutil.getstandinsstate(repo) result = orig(repo, node, overwrite) filelist = None if not overwrite: newstandins = lfutil.getstandinsstate(repo) filelist = lfutil.getlfilestoupdate(oldstandins, newstandins) lfcommands.updatelfiles(repo.ui, repo, filelist=filelist) return result def hgmerge(orig, repo, node, force=None, remind=True): result = orig(repo, node, force, remind) lfcommands.updatelfiles(repo.ui, repo) return result # When we rebase a repository with remotely changed largefiles, we need to # take some extra care so that the largefiles are correctly updated in the # working copy def overridepull(orig, ui, repo, source=None, **opts): revsprepull = len(repo) if not source: source = 'default' repo.lfpullsource = source if opts.get('rebase', False): repo._isrebasing = True try: if opts.get('update'): del opts['update'] ui.debug('--update and --rebase are not compatible, ignoring ' 'the update flag\n') del opts['rebase'] origpostincoming = commands.postincoming def _dummy(*args, **kwargs): pass commands.postincoming = _dummy try: result = commands.pull(ui, repo, source, **opts) finally: commands.postincoming = origpostincoming revspostpull = len(repo) if revspostpull > revsprepull: result = result or rebase.rebase(ui, repo) finally: repo._isrebasing = False else: result = orig(ui, repo, source, **opts) revspostpull = len(repo) lfrevs = opts.get('lfrev', []) if opts.get('all_largefiles'): lfrevs.append('pulled()') if lfrevs and revspostpull > revsprepull: numcached = 0 repo.firstpulled = revsprepull # for pulled() revset expression try: for rev in scmutil.revrange(repo, lfrevs): ui.note(_('pulling largefiles for revision %s\n') % rev) (cached, missing) = lfcommands.cachelfiles(ui, repo, rev) numcached += len(cached) finally: del repo.firstpulled ui.status(_("%d largefiles cached\n") % numcached) return result def pulledrevsetsymbol(repo, subset, x): """``pulled()`` Changesets that just has been pulled. Only available with largefiles from pull --lfrev expressions. .. container:: verbose Some examples: - pull largefiles for all new changesets:: hg pull -lfrev "pulled()" - pull largefiles for all new branch heads:: hg pull -lfrev "head(pulled()) and not closed()" """ try: firstpulled = repo.firstpulled except AttributeError: raise util.Abort(_("pulled() only available in --lfrev")) return [r for r in subset if r >= firstpulled] def overrideclone(orig, ui, source, dest=None, **opts): d = dest if d is None: d = hg.defaultdest(source) if opts.get('all_largefiles') and not hg.islocal(d): raise util.Abort(_( '--all-largefiles is incompatible with non-local destination %s' % d)) return orig(ui, source, dest, **opts) def hgclone(orig, ui, opts, *args, **kwargs): result = orig(ui, opts, *args, **kwargs) if result is not None: sourcerepo, destrepo = result repo = destrepo.local() # Caching is implicitly limited to 'rev' option, since the dest repo was # truncated at that point. The user may expect a download count with # this option, so attempt whether or not this is a largefile repo. if opts.get('all_largefiles'): success, missing = lfcommands.downloadlfiles(ui, repo, None) if missing != 0: return None return result def overriderebase(orig, ui, repo, **opts): repo._isrebasing = True try: return orig(ui, repo, **opts) finally: repo._isrebasing = False def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None, prefix=None, mtime=None, subrepos=None): # No need to lock because we are only reading history and # largefile caches, neither of which are modified. lfcommands.cachelfiles(repo.ui, repo, node) if kind not in archival.archivers: raise util.Abort(_("unknown archive type '%s'") % kind) ctx = repo[node] if kind == 'files': if prefix: raise util.Abort( _('cannot give prefix when archiving to files')) else: prefix = archival.tidyprefix(dest, kind, prefix) def write(name, mode, islink, getdata): if matchfn and not matchfn(name): return data = getdata() if decode: data = repo.wwritedata(name, data) archiver.addfile(prefix + name, mode, islink, data) archiver = archival.archivers[kind](dest, mtime or ctx.date()[0]) if repo.ui.configbool("ui", "archivemeta", True): def metadata(): base = 'repo: %s\nnode: %s\nbranch: %s\n' % ( hex(repo.changelog.node(0)), hex(node), ctx.branch()) tags = ''.join('tag: %s\n' % t for t in ctx.tags() if repo.tagtype(t) == 'global') if not tags: repo.ui.pushbuffer() opts = {'template': '{latesttag}\n{latesttagdistance}', 'style': '', 'patch': None, 'git': None} cmdutil.show_changeset(repo.ui, repo, opts).show(ctx) ltags, dist = repo.ui.popbuffer().split('\n') tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':')) tags += 'latesttagdistance: %s\n' % dist return base + tags write('.hg_archival.txt', 0644, False, metadata) for f in ctx: ff = ctx.flags(f) getdata = ctx[f].data if lfutil.isstandin(f): path = lfutil.findfile(repo, getdata().strip()) if path is None: raise util.Abort( _('largefile %s not found in repo store or system cache') % lfutil.splitstandin(f)) f = lfutil.splitstandin(f) def getdatafn(): fd = None try: fd = open(path, 'rb') return fd.read() finally: if fd: fd.close() getdata = getdatafn write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata) if subrepos: for subpath in sorted(ctx.substate): sub = ctx.sub(subpath) submatch = match_.narrowmatcher(subpath, matchfn) sub.archive(repo.ui, archiver, prefix, submatch) archiver.done() def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None): repo._get(repo._state + ('hg',)) rev = repo._state[1] ctx = repo._repo[rev] lfcommands.cachelfiles(ui, repo._repo, ctx.node()) def write(name, mode, islink, getdata): # At this point, the standin has been replaced with the largefile name, # so the normal matcher works here without the lfutil variants. if match and not match(f): return data = getdata() archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data) for f in ctx: ff = ctx.flags(f) getdata = ctx[f].data if lfutil.isstandin(f): path = lfutil.findfile(repo._repo, getdata().strip()) if path is None: raise util.Abort( _('largefile %s not found in repo store or system cache') % lfutil.splitstandin(f)) f = lfutil.splitstandin(f) def getdatafn(): fd = None try: fd = open(os.path.join(prefix, path), 'rb') return fd.read() finally: if fd: fd.close() getdata = getdatafn write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata) for subpath in sorted(ctx.substate): sub = ctx.sub(subpath) submatch = match_.narrowmatcher(subpath, match) sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/', submatch) # If a largefile is modified, the change is not reflected in its # standin until a commit. cmdutil.bailifchanged() raises an exception # if the repo has uncommitted changes. Wrap it to also check if # largefiles were changed. This is used by bisect and backout. def overridebailifchanged(orig, repo): orig(repo) repo.lfstatus = True modified, added, removed, deleted = repo.status()[:4] repo.lfstatus = False if modified or added or removed or deleted: raise util.Abort(_('uncommitted changes')) # Fetch doesn't use cmdutil.bailifchanged so override it to add the check def overridefetch(orig, ui, repo, *pats, **opts): repo.lfstatus = True modified, added, removed, deleted = repo.status()[:4] repo.lfstatus = False if modified or added or removed or deleted: raise util.Abort(_('uncommitted changes')) return orig(ui, repo, *pats, **opts) def overrideforget(orig, ui, repo, *pats, **opts): installnormalfilesmatchfn(repo[None].manifest()) result = orig(ui, repo, *pats, **opts) restorematchfn() m = scmutil.match(repo[None], pats, opts) try: repo.lfstatus = True s = repo.status(match=m, clean=True) finally: repo.lfstatus = False forget = sorted(s[0] + s[1] + s[3] + s[6]) forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()] for f in forget: if lfutil.standin(f) not in repo.dirstate and not \ os.path.isdir(m.rel(lfutil.standin(f))): ui.warn(_('not removing %s: file is already untracked\n') % m.rel(f)) result = 1 for f in forget: if ui.verbose or not m.exact(f): ui.status(_('removing %s\n') % m.rel(f)) # Need to lock because standin files are deleted then removed from the # repository and we could race in-between. wlock = repo.wlock() try: lfdirstate = lfutil.openlfdirstate(ui, repo) for f in forget: if lfdirstate[f] == 'a': lfdirstate.drop(f) else: lfdirstate.remove(f) lfdirstate.write() standins = [lfutil.standin(f) for f in forget] for f in standins: util.unlinkpath(repo.wjoin(f), ignoremissing=True) repo[None].forget(standins) finally: wlock.release() return result def getoutgoinglfiles(ui, repo, dest=None, **opts): dest = ui.expandpath(dest or 'default-push', dest or 'default') dest, branches = hg.parseurl(dest, opts.get('branch')) revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev')) if revs: revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)] try: remote = hg.peer(repo, opts, dest) except error.RepoError: return None outgoing = discovery.findcommonoutgoing(repo, remote.peer(), force=False) if not outgoing.missing: return outgoing.missing o = repo.changelog.nodesbetween(outgoing.missing, revs)[0] if opts.get('newest_first'): o.reverse() toupload = set() for n in o: parents = [p for p in repo.changelog.parents(n) if p != node.nullid] ctx = repo[n] files = set(ctx.files()) if len(parents) == 2: mc = ctx.manifest() mp1 = ctx.parents()[0].manifest() mp2 = ctx.parents()[1].manifest() for f in mp1: if f not in mc: files.add(f) for f in mp2: if f not in mc: files.add(f) for f in mc: if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None): files.add(f) toupload = toupload.union( set([f for f in files if lfutil.isstandin(f) and f in ctx])) return sorted(toupload) def overrideoutgoing(orig, ui, repo, dest=None, **opts): result = orig(ui, repo, dest, **opts) if opts.pop('large', None): toupload = getoutgoinglfiles(ui, repo, dest, **opts) if toupload is None: ui.status(_('largefiles: No remote repo\n')) elif not toupload: ui.status(_('largefiles: no files to upload\n')) else: ui.status(_('largefiles to upload:\n')) for file in toupload: ui.status(lfutil.splitstandin(file) + '\n') ui.status('\n') return result def overridesummary(orig, ui, repo, *pats, **opts): try: repo.lfstatus = True orig(ui, repo, *pats, **opts) finally: repo.lfstatus = False if opts.pop('large', None): toupload = getoutgoinglfiles(ui, repo, None, **opts) if toupload is None: # i18n: column positioning for "hg summary" ui.status(_('largefiles: (no remote repo)\n')) elif not toupload: # i18n: column positioning for "hg summary" ui.status(_('largefiles: (no files to upload)\n')) else: # i18n: column positioning for "hg summary" ui.status(_('largefiles: %d to upload\n') % len(toupload)) def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None, similarity=None): if not lfutil.islfilesrepo(repo): return orig(repo, pats, opts, dry_run, similarity) # Get the list of missing largefiles so we can remove them lfdirstate = lfutil.openlfdirstate(repo.ui, repo) s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False, False, False) (unsure, modified, added, removed, missing, unknown, ignored, clean) = s # Call into the normal remove code, but the removing of the standin, we want # to have handled by original addremove. Monkey patching here makes sure # we don't remove the standin in the largefiles code, preventing a very # confused state later. if missing: m = [repo.wjoin(f) for f in missing] repo._isaddremove = True removelargefiles(repo.ui, repo, *m, **opts) repo._isaddremove = False # Call into the normal add code, and any files that *should* be added as # largefiles will be addlargefiles(repo.ui, repo, *pats, **opts) # Now that we've handled largefiles, hand off to the original addremove # function to take care of the rest. Make sure it doesn't do anything with # largefiles by installing a matcher that will ignore them. installnormalfilesmatchfn(repo[None].manifest()) result = orig(repo, pats, opts, dry_run, similarity) restorematchfn() return result # Calling purge with --all will cause the largefiles to be deleted. # Override repo.status to prevent this from happening. def overridepurge(orig, ui, repo, *dirs, **opts): # XXX large file status is buggy when used on repo proxy. # XXX this needs to be investigate. repo = repo.unfiltered() oldstatus = repo.status def overridestatus(node1='.', node2=None, match=None, ignored=False, clean=False, unknown=False, listsubrepos=False): r = oldstatus(node1, node2, match, ignored, clean, unknown, listsubrepos) lfdirstate = lfutil.openlfdirstate(ui, repo) modified, added, removed, deleted, unknown, ignored, clean = r unknown = [f for f in unknown if lfdirstate[f] == '?'] ignored = [f for f in ignored if lfdirstate[f] == '?'] return modified, added, removed, deleted, unknown, ignored, clean repo.status = overridestatus orig(ui, repo, *dirs, **opts) repo.status = oldstatus def overriderollback(orig, ui, repo, **opts): result = orig(ui, repo, **opts) merge.update(repo, node=None, branchmerge=False, force=True, partial=lfutil.isstandin) wlock = repo.wlock() try: lfdirstate = lfutil.openlfdirstate(ui, repo) lfiles = lfutil.listlfiles(repo) oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev()) for file in lfiles: if file in oldlfiles: lfdirstate.normallookup(file) else: lfdirstate.add(file) lfdirstate.write() finally: wlock.release() return result def overridetransplant(orig, ui, repo, *revs, **opts): try: oldstandins = lfutil.getstandinsstate(repo) repo._istransplanting = True result = orig(ui, repo, *revs, **opts) newstandins = lfutil.getstandinsstate(repo) filelist = lfutil.getlfilestoupdate(oldstandins, newstandins) lfcommands.updatelfiles(repo.ui, repo, filelist=filelist, printmessage=True) finally: repo._istransplanting = False return result def overridecat(orig, ui, repo, file1, *pats, **opts): ctx = scmutil.revsingle(repo, opts.get('rev')) err = 1 notbad = set() m = scmutil.match(ctx, (file1,) + pats, opts) origmatchfn = m.matchfn def lfmatchfn(f): lf = lfutil.splitstandin(f) if lf is None: return origmatchfn(f) notbad.add(lf) return origmatchfn(lf) m.matchfn = lfmatchfn origbadfn = m.bad def lfbadfn(f, msg): if not f in notbad: return origbadfn(f, msg) m.bad = lfbadfn for f in ctx.walk(m): fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(), pathname=f) lf = lfutil.splitstandin(f) if lf is None: # duplicating unreachable code from commands.cat data = ctx[f].data() if opts.get('decode'): data = repo.wwritedata(f, data) fp.write(data) else: hash = lfutil.readstandin(repo, lf, ctx.rev()) if not lfutil.inusercache(repo.ui, hash): store = basestore._openstore(repo) success, missing = store.get([(lf, hash)]) if len(success) != 1: raise util.Abort( _('largefile %s is not in cache and could not be ' 'downloaded') % lf) path = lfutil.usercachepath(repo.ui, hash) fpin = open(path, "rb") for chunk in util.filechunkiter(fpin, 128 * 1024): fp.write(chunk) fpin.close() fp.close() err = 0 return err def mercurialsinkbefore(orig, sink): sink.repo._isconverting = True orig(sink) def mercurialsinkafter(orig, sink): sink.repo._isconverting = False orig(sink) mercurial-2.8.2/hgext/largefiles/lfutil.py0000644000175000017500000003124312261160437022426 0ustar oxymoronoxymoron00000000000000# Copyright 2009-2010 Gregory P. Ward # Copyright 2009-2010 Intelerad Medical Systems Incorporated # Copyright 2010-2011 Fog Creek Software # Copyright 2010-2011 Unity Technologies # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''largefiles utility code: must not import other modules in this package.''' import os import platform import shutil import stat from mercurial import dirstate, httpconnection, match as match_, util, scmutil from mercurial.i18n import _ shortname = '.hglf' shortnameslash = shortname + '/' longname = 'largefiles' # -- Private worker functions ------------------------------------------ def getminsize(ui, assumelfiles, opt, default=10): lfsize = opt if not lfsize and assumelfiles: lfsize = ui.config(longname, 'minsize', default=default) if lfsize: try: lfsize = float(lfsize) except ValueError: raise util.Abort(_('largefiles: size must be number (not %s)\n') % lfsize) if lfsize is None: raise util.Abort(_('minimum size for largefiles must be specified')) return lfsize def link(src, dest): util.makedirs(os.path.dirname(dest)) try: util.oslink(src, dest) except OSError: # if hardlinks fail, fallback on atomic copy dst = util.atomictempfile(dest) for chunk in util.filechunkiter(open(src, 'rb')): dst.write(chunk) dst.close() os.chmod(dest, os.stat(src).st_mode) def usercachepath(ui, hash): path = ui.configpath(longname, 'usercache', None) if path: path = os.path.join(path, hash) else: if os.name == 'nt': appdata = os.getenv('LOCALAPPDATA', os.getenv('APPDATA')) if appdata: path = os.path.join(appdata, longname, hash) elif platform.system() == 'Darwin': home = os.getenv('HOME') if home: path = os.path.join(home, 'Library', 'Caches', longname, hash) elif os.name == 'posix': path = os.getenv('XDG_CACHE_HOME') if path: path = os.path.join(path, longname, hash) else: home = os.getenv('HOME') if home: path = os.path.join(home, '.cache', longname, hash) else: raise util.Abort(_('unknown operating system: %s\n') % os.name) return path def inusercache(ui, hash): path = usercachepath(ui, hash) return path and os.path.exists(path) def findfile(repo, hash): if instore(repo, hash): repo.ui.note(_('found %s in store\n') % hash) return storepath(repo, hash) elif inusercache(repo.ui, hash): repo.ui.note(_('found %s in system cache\n') % hash) path = storepath(repo, hash) link(usercachepath(repo.ui, hash), path) return path return None class largefilesdirstate(dirstate.dirstate): def __getitem__(self, key): return super(largefilesdirstate, self).__getitem__(unixpath(key)) def normal(self, f): return super(largefilesdirstate, self).normal(unixpath(f)) def remove(self, f): return super(largefilesdirstate, self).remove(unixpath(f)) def add(self, f): return super(largefilesdirstate, self).add(unixpath(f)) def drop(self, f): return super(largefilesdirstate, self).drop(unixpath(f)) def forget(self, f): return super(largefilesdirstate, self).forget(unixpath(f)) def normallookup(self, f): return super(largefilesdirstate, self).normallookup(unixpath(f)) def _ignore(self): return False def openlfdirstate(ui, repo, create=True): ''' Return a dirstate object that tracks largefiles: i.e. its root is the repo root, but it is saved in .hg/largefiles/dirstate. ''' lfstoredir = repo.join(longname) opener = scmutil.opener(lfstoredir) lfdirstate = largefilesdirstate(opener, ui, repo.root, repo.dirstate._validate) # If the largefiles dirstate does not exist, populate and create # it. This ensures that we create it on the first meaningful # largefiles operation in a new clone. if create and not os.path.exists(os.path.join(lfstoredir, 'dirstate')): util.makedirs(lfstoredir) matcher = getstandinmatcher(repo) for standin in repo.dirstate.walk(matcher, [], False, False): lfile = splitstandin(standin) lfdirstate.normallookup(lfile) return lfdirstate def lfdirstatestatus(lfdirstate, repo, rev): match = match_.always(repo.root, repo.getcwd()) s = lfdirstate.status(match, [], False, False, False) unsure, modified, added, removed, missing, unknown, ignored, clean = s for lfile in unsure: try: fctx = repo[rev][standin(lfile)] except LookupError: fctx = None if not fctx or fctx.data().strip() != hashfile(repo.wjoin(lfile)): modified.append(lfile) else: clean.append(lfile) lfdirstate.normal(lfile) return (modified, added, removed, missing, unknown, ignored, clean) def listlfiles(repo, rev=None, matcher=None): '''return a list of largefiles in the working copy or the specified changeset''' if matcher is None: matcher = getstandinmatcher(repo) # ignore unknown files in working directory return [splitstandin(f) for f in repo[rev].walk(matcher) if rev is not None or repo.dirstate[f] != '?'] def instore(repo, hash): return os.path.exists(storepath(repo, hash)) def storepath(repo, hash): return repo.join(os.path.join(longname, hash)) def copyfromcache(repo, hash, filename): '''Copy the specified largefile from the repo or system cache to filename in the repository. Return true on success or false if the file was not found in either cache (which should not happened: this is meant to be called only after ensuring that the needed largefile exists in the cache).''' path = findfile(repo, hash) if path is None: return False util.makedirs(os.path.dirname(repo.wjoin(filename))) # The write may fail before the file is fully written, but we # don't use atomic writes in the working copy. shutil.copy(path, repo.wjoin(filename)) return True def copytostore(repo, rev, file, uploaded=False): hash = readstandin(repo, file, rev) if instore(repo, hash): return copytostoreabsolute(repo, repo.wjoin(file), hash) def copyalltostore(repo, node): '''Copy all largefiles in a given revision to the store''' ctx = repo[node] for filename in ctx.files(): if isstandin(filename) and filename in ctx.manifest(): realfile = splitstandin(filename) copytostore(repo, ctx.node(), realfile) def copytostoreabsolute(repo, file, hash): if inusercache(repo.ui, hash): link(usercachepath(repo.ui, hash), storepath(repo, hash)) elif not getattr(repo, "_isconverting", False): util.makedirs(os.path.dirname(storepath(repo, hash))) dst = util.atomictempfile(storepath(repo, hash), createmode=repo.store.createmode) for chunk in util.filechunkiter(open(file, 'rb')): dst.write(chunk) dst.close() linktousercache(repo, hash) def linktousercache(repo, hash): path = usercachepath(repo.ui, hash) if path: link(storepath(repo, hash), path) def getstandinmatcher(repo, pats=[], opts={}): '''Return a match object that applies pats to the standin directory''' standindir = repo.wjoin(shortname) if pats: pats = [os.path.join(standindir, pat) for pat in pats] else: # no patterns: relative to repo root pats = [standindir] # no warnings about missing files or directories match = scmutil.match(repo[None], pats, opts) match.bad = lambda f, msg: None return match def composestandinmatcher(repo, rmatcher): '''Return a matcher that accepts standins corresponding to the files accepted by rmatcher. Pass the list of files in the matcher as the paths specified by the user.''' smatcher = getstandinmatcher(repo, rmatcher.files()) isstandin = smatcher.matchfn def composedmatchfn(f): return isstandin(f) and rmatcher.matchfn(splitstandin(f)) smatcher.matchfn = composedmatchfn return smatcher def standin(filename): '''Return the repo-relative path to the standin for the specified big file.''' # Notes: # 1) Some callers want an absolute path, but for instance addlargefiles # needs it repo-relative so it can be passed to repo[None].add(). So # leave it up to the caller to use repo.wjoin() to get an absolute path. # 2) Join with '/' because that's what dirstate always uses, even on # Windows. Change existing separator to '/' first in case we are # passed filenames from an external source (like the command line). return shortnameslash + util.pconvert(filename) def isstandin(filename): '''Return true if filename is a big file standin. filename must be in Mercurial's internal form (slash-separated).''' return filename.startswith(shortnameslash) def splitstandin(filename): # Split on / because that's what dirstate always uses, even on Windows. # Change local separator to / first just in case we are passed filenames # from an external source (like the command line). bits = util.pconvert(filename).split('/', 1) if len(bits) == 2 and bits[0] == shortname: return bits[1] else: return None def updatestandin(repo, standin): file = repo.wjoin(splitstandin(standin)) if os.path.exists(file): hash = hashfile(file) executable = getexecutable(file) writestandin(repo, standin, hash, executable) def readstandin(repo, filename, node=None): '''read hex hash from standin for filename at given node, or working directory if no node is given''' return repo[node][standin(filename)].data().strip() def writestandin(repo, standin, hash, executable): '''write hash to /''' repo.wwrite(standin, hash + '\n', executable and 'x' or '') def copyandhash(instream, outfile): '''Read bytes from instream (iterable) and write them to outfile, computing the SHA-1 hash of the data along the way. Return the hash.''' hasher = util.sha1('') for data in instream: hasher.update(data) outfile.write(data) return hasher.hexdigest() def hashrepofile(repo, file): return hashfile(repo.wjoin(file)) def hashfile(file): if not os.path.exists(file): return '' hasher = util.sha1('') fd = open(file, 'rb') for data in util.filechunkiter(fd, 128 * 1024): hasher.update(data) fd.close() return hasher.hexdigest() def getexecutable(filename): mode = os.stat(filename).st_mode return ((mode & stat.S_IXUSR) and (mode & stat.S_IXGRP) and (mode & stat.S_IXOTH)) def urljoin(first, second, *arg): def join(left, right): if not left.endswith('/'): left += '/' if right.startswith('/'): right = right[1:] return left + right url = join(first, second) for a in arg: url = join(url, a) return url def hexsha1(data): """hexsha1 returns the hex-encoded sha1 sum of the data in the file-like object data""" h = util.sha1() for chunk in util.filechunkiter(data): h.update(chunk) return h.hexdigest() def httpsendfile(ui, filename): return httpconnection.httpsendfile(ui, filename, 'rb') def unixpath(path): '''Return a version of path normalized for use with the lfdirstate.''' return util.pconvert(os.path.normpath(path)) def islfilesrepo(repo): if ('largefiles' in repo.requirements and util.any(shortnameslash in f[0] for f in repo.store.datafiles())): return True return util.any(openlfdirstate(repo.ui, repo, False)) class storeprotonotcapable(Exception): def __init__(self, storetypes): self.storetypes = storetypes def getstandinsstate(repo): standins = [] matcher = getstandinmatcher(repo) for standin in repo.dirstate.walk(matcher, [], False, False): lfile = splitstandin(standin) try: hash = readstandin(repo, lfile) except IOError: hash = None standins.append((lfile, hash)) return standins def getlfilestoupdate(oldstandins, newstandins): changedstandins = set(oldstandins).symmetric_difference(set(newstandins)) filelist = [] for f in changedstandins: if f[0] not in filelist: filelist.append(f[0]) return filelist mercurial-2.8.2/hgext/largefiles/CONTRIBUTORS0000644000175000017500000000025412261160437022433 0ustar oxymoronoxymoron00000000000000Greg Ward, author of the original bfiles extension Na'Tosha Bard of Unity Technologies Fog Creek Software Special thanks to the University of Toronto and the UCOSP program mercurial-2.8.2/hgext/largefiles/basestore.py0000644000175000017500000001754412261160437023126 0ustar oxymoronoxymoron00000000000000# Copyright 2009-2010 Gregory P. Ward # Copyright 2009-2010 Intelerad Medical Systems Incorporated # Copyright 2010-2011 Fog Creek Software # Copyright 2010-2011 Unity Technologies # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''base class for store implementations and store-related utility code''' import re from mercurial import util, node, hg from mercurial.i18n import _ import lfutil class StoreError(Exception): '''Raised when there is a problem getting files from or putting files to a central store.''' def __init__(self, filename, hash, url, detail): self.filename = filename self.hash = hash self.url = url self.detail = detail def longmessage(self): return (_("error getting id %s from url %s for file %s: %s\n") % (self.hash, util.hidepassword(self.url), self.filename, self.detail)) def __str__(self): return "%s: %s" % (util.hidepassword(self.url), self.detail) class basestore(object): def __init__(self, ui, repo, url): self.ui = ui self.repo = repo self.url = url def put(self, source, hash): '''Put source file into the store so it can be retrieved by hash.''' raise NotImplementedError('abstract method') def exists(self, hashes): '''Check to see if the store contains the given hashes. Given an iterable of hashes it returns a mapping from hash to bool.''' raise NotImplementedError('abstract method') def get(self, files): '''Get the specified largefiles from the store and write to local files under repo.root. files is a list of (filename, hash) tuples. Return (success, missing), lists of files successfully downloaded and those not found in the store. success is a list of (filename, hash) tuples; missing is a list of filenames that we could not get. (The detailed error message will already have been presented to the user, so missing is just supplied as a summary.)''' success = [] missing = [] ui = self.ui at = 0 available = self.exists(set(hash for (_filename, hash) in files)) for filename, hash in files: ui.progress(_('getting largefiles'), at, unit='lfile', total=len(files)) at += 1 ui.note(_('getting %s:%s\n') % (filename, hash)) if not available.get(hash): ui.warn(_('%s: largefile %s not available from %s\n') % (filename, hash, util.hidepassword(self.url))) missing.append(filename) continue if self._gethash(filename, hash): success.append((filename, hash)) else: missing.append(filename) ui.progress(_('getting largefiles'), None) return (success, missing) def _gethash(self, filename, hash): """Get file with the provided hash and store it in the local repo's store and in the usercache. filename is for informational messages only. """ util.makedirs(lfutil.storepath(self.repo, '')) storefilename = lfutil.storepath(self.repo, hash) tmpname = storefilename + '.tmp' tmpfile = util.atomictempfile(tmpname, createmode=self.repo.store.createmode) try: gothash = self._getfile(tmpfile, filename, hash) except StoreError, err: self.ui.warn(err.longmessage()) gothash = "" tmpfile.close() if gothash != hash: if gothash != "": self.ui.warn(_('%s: data corruption (expected %s, got %s)\n') % (filename, hash, gothash)) util.unlink(tmpname) return False util.rename(tmpname, storefilename) lfutil.linktousercache(self.repo, hash) return True def verify(self, revs, contents=False): '''Verify the existence (and, optionally, contents) of every big file revision referenced by every changeset in revs. Return 0 if all is well, non-zero on any errors.''' failed = False self.ui.status(_('searching %d changesets for largefiles\n') % len(revs)) verified = set() # set of (filename, filenode) tuples for rev in revs: cctx = self.repo[rev] cset = "%d:%s" % (cctx.rev(), node.short(cctx.node())) for standin in cctx: if self._verifyfile(cctx, cset, contents, standin, verified): failed = True numrevs = len(verified) numlfiles = len(set([fname for (fname, fnode) in verified])) if contents: self.ui.status( _('verified contents of %d revisions of %d largefiles\n') % (numrevs, numlfiles)) else: self.ui.status( _('verified existence of %d revisions of %d largefiles\n') % (numrevs, numlfiles)) return int(failed) def _getfile(self, tmpfile, filename, hash): '''Fetch one revision of one file from the store and write it to tmpfile. Compute the hash of the file on-the-fly as it downloads and return the hash. Close tmpfile. Raise StoreError if unable to download the file (e.g. it does not exist in the store).''' raise NotImplementedError('abstract method') def _verifyfile(self, cctx, cset, contents, standin, verified): '''Perform the actual verification of a file in the store. 'cset' is only used in warnings. 'contents' controls verification of content hash. 'standin' is the standin path of the largefile to verify. 'verified' is maintained as a set of already verified files. Returns _true_ if it is a standin and any problems are found! ''' raise NotImplementedError('abstract method') import localstore, wirestore _storeprovider = { 'file': [localstore.localstore], 'http': [wirestore.wirestore], 'https': [wirestore.wirestore], 'ssh': [wirestore.wirestore], } _scheme_re = re.compile(r'^([a-zA-Z0-9+-.]+)://') # During clone this function is passed the src's ui object # but it needs the dest's ui object so it can read out of # the config file. Use repo.ui instead. def _openstore(repo, remote=None, put=False): ui = repo.ui if not remote: lfpullsource = getattr(repo, 'lfpullsource', None) if lfpullsource: path = ui.expandpath(lfpullsource) else: path = ui.expandpath('default-push', 'default') # ui.expandpath() leaves 'default-push' and 'default' alone if # they cannot be expanded: fallback to the empty string, # meaning the current directory. if path == 'default-push' or path == 'default': path = '' remote = repo else: path, _branches = hg.parseurl(path) remote = hg.peer(repo, {}, path) # The path could be a scheme so use Mercurial's normal functionality # to resolve the scheme to a repository and use its path path = util.safehasattr(remote, 'url') and remote.url() or remote.path match = _scheme_re.match(path) if not match: # regular filesystem path scheme = 'file' else: scheme = match.group(1) try: storeproviders = _storeprovider[scheme] except KeyError: raise util.Abort(_('unsupported URL scheme %r') % scheme) for classobj in storeproviders: try: return classobj(ui, repo, remote) except lfutil.storeprotonotcapable: pass raise util.Abort(_('%s does not appear to be a largefile store') % util.hidepassword(path)) mercurial-2.8.2/hgext/largefiles/remotestore.py0000644000175000017500000000653712261160437023507 0ustar oxymoronoxymoron00000000000000# Copyright 2010-2011 Fog Creek Software # Copyright 2010-2011 Unity Technologies # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''remote largefile store; the base class for wirestore''' import urllib2 from mercurial import util from mercurial.i18n import _ from mercurial.wireproto import remotebatch import lfutil import basestore class remotestore(basestore.basestore): '''a largefile store accessed over a network''' def __init__(self, ui, repo, url): super(remotestore, self).__init__(ui, repo, url) def put(self, source, hash): if self.sendfile(source, hash): raise util.Abort( _('remotestore: could not put %s to remote store %s') % (source, util.hidepassword(self.url))) self.ui.debug( _('remotestore: put %s to remote store %s\n') % (source, util.hidepassword(self.url))) def exists(self, hashes): return dict((h, s == 0) for (h, s) in self._stat(hashes).iteritems()) def sendfile(self, filename, hash): self.ui.debug('remotestore: sendfile(%s, %s)\n' % (filename, hash)) fd = None try: try: fd = lfutil.httpsendfile(self.ui, filename) except IOError, e: raise util.Abort( _('remotestore: could not open file %s: %s') % (filename, str(e))) return self._put(hash, fd) finally: if fd: fd.close() def _getfile(self, tmpfile, filename, hash): try: chunks = self._get(hash) except urllib2.HTTPError, e: # 401s get converted to util.Aborts; everything else is fine being # turned into a StoreError raise basestore.StoreError(filename, hash, self.url, str(e)) except urllib2.URLError, e: # This usually indicates a connection problem, so don't # keep trying with the other files... they will probably # all fail too. raise util.Abort('%s: %s' % (util.hidepassword(self.url), e.reason)) except IOError, e: raise basestore.StoreError(filename, hash, self.url, str(e)) return lfutil.copyandhash(chunks, tmpfile) def _verifyfile(self, cctx, cset, contents, standin, verified): filename = lfutil.splitstandin(standin) if not filename: return False fctx = cctx[standin] key = (filename, fctx.filenode()) if key in verified: return False verified.add(key) expecthash = fctx.data()[0:40] stat = self._stat([expecthash])[expecthash] if not stat: return False elif stat == 1: self.ui.warn( _('changeset %s: %s: contents differ\n') % (cset, filename)) return True # failed elif stat == 2: self.ui.warn( _('changeset %s: %s missing\n') % (cset, filename)) return True # failed else: raise RuntimeError('verify failed: unexpected response from ' 'statlfile (%r)' % stat) def batch(self): '''Support for remote batching.''' return remotebatch(self) mercurial-2.8.2/hgext/largefiles/localstore.py0000644000175000017500000000465012261160437023300 0ustar oxymoronoxymoron00000000000000# Copyright 2009-2010 Gregory P. Ward # Copyright 2009-2010 Intelerad Medical Systems Incorporated # Copyright 2010-2011 Fog Creek Software # Copyright 2010-2011 Unity Technologies # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''store class for local filesystem''' from mercurial.i18n import _ import lfutil import basestore class localstore(basestore.basestore): '''localstore first attempts to grab files out of the store in the remote Mercurial repository. Failing that, it attempts to grab the files from the user cache.''' def __init__(self, ui, repo, remote): self.remote = remote.local() super(localstore, self).__init__(ui, repo, self.remote.url()) def put(self, source, hash): if lfutil.instore(self.remote, hash): return lfutil.link(source, lfutil.storepath(self.remote, hash)) def exists(self, hashes): retval = {} for hash in hashes: retval[hash] = lfutil.instore(self.remote, hash) return retval def _getfile(self, tmpfile, filename, hash): path = lfutil.findfile(self.remote, hash) if not path: raise basestore.StoreError(filename, hash, self.url, _("can't get file locally")) fd = open(path, 'rb') try: return lfutil.copyandhash(fd, tmpfile) finally: fd.close() def _verifyfile(self, cctx, cset, contents, standin, verified): filename = lfutil.splitstandin(standin) if not filename: return False fctx = cctx[standin] key = (filename, fctx.filenode()) if key in verified: return False expecthash = fctx.data()[0:40] storepath = lfutil.storepath(self.remote, expecthash) verified.add(key) if not lfutil.instore(self.remote, expecthash): self.ui.warn( _('changeset %s: %s references missing %s\n') % (cset, filename, storepath)) return True # failed if contents: actualhash = lfutil.hashfile(storepath) if actualhash != expecthash: self.ui.warn( _('changeset %s: %s references corrupted %s\n') % (cset, filename, storepath)) return True # failed return False mercurial-2.8.2/hgext/largefiles/__init__.py0000644000175000017500000001157612261160437022675 0ustar oxymoronoxymoron00000000000000# Copyright 2009-2010 Gregory P. Ward # Copyright 2009-2010 Intelerad Medical Systems Incorporated # Copyright 2010-2011 Fog Creek Software # Copyright 2010-2011 Unity Technologies # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''track large binary files Large binary files tend to be not very compressible, not very diffable, and not at all mergeable. Such files are not handled efficiently by Mercurial's storage format (revlog), which is based on compressed binary deltas; storing large binary files as regular Mercurial files wastes bandwidth and disk space and increases Mercurial's memory usage. The largefiles extension addresses these problems by adding a centralized client-server layer on top of Mercurial: largefiles live in a *central store* out on the network somewhere, and you only fetch the revisions that you need when you need them. largefiles works by maintaining a "standin file" in .hglf/ for each largefile. The standins are small (41 bytes: an SHA-1 hash plus newline) and are tracked by Mercurial. Largefile revisions are identified by the SHA-1 hash of their contents, which is written to the standin. largefiles uses that revision ID to get/put largefile revisions from/to the central store. This saves both disk space and bandwidth, since you don't need to retrieve all historical revisions of large files when you clone or pull. To start a new repository or add new large binary files, just add --large to your :hg:`add` command. For example:: $ dd if=/dev/urandom of=randomdata count=2000 $ hg add --large randomdata $ hg commit -m 'add randomdata as a largefile' When you push a changeset that adds/modifies largefiles to a remote repository, its largefile revisions will be uploaded along with it. Note that the remote Mercurial must also have the largefiles extension enabled for this to work. When you pull a changeset that affects largefiles from a remote repository, the largefiles for the changeset will by default not be pulled down. However, when you update to such a revision, any largefiles needed by that revision are downloaded and cached (if they have never been downloaded before). One way to pull largefiles when pulling is thus to use --update, which will update your working copy to the latest pulled revision (and thereby downloading any new largefiles). If you want to pull largefiles you don't need for update yet, then you can use pull with the `--lfrev` option or the :hg:`lfpull` command. If you know you are pulling from a non-default location and want to download all the largefiles that correspond to the new changesets at the same time, then you can pull with `--lfrev "pulled()"`. If you just want to ensure that you will have the largefiles needed to merge or rebase with new heads that you are pulling, then you can pull with `--lfrev "head(pulled())"` flag to pre-emptively download any largefiles that are new in the heads you are pulling. Keep in mind that network access may now be required to update to changesets that you have not previously updated to. The nature of the largefiles extension means that updating is no longer guaranteed to be a local-only operation. If you already have large files tracked by Mercurial without the largefiles extension, you will need to convert your repository in order to benefit from largefiles. This is done with the :hg:`lfconvert` command:: $ hg lfconvert --size 10 oldrepo newrepo In repositories that already have largefiles in them, any new file over 10MB will automatically be added as a largefile. To change this threshold, set ``largefiles.minsize`` in your Mercurial config file to the minimum size in megabytes to track as a largefile, or use the --lfsize option to the add command (also in megabytes):: [largefiles] minsize = 2 $ hg add --lfsize 2 The ``largefiles.patterns`` config option allows you to specify a list of filename patterns (see :hg:`help patterns`) that should always be tracked as largefiles:: [largefiles] patterns = *.jpg re:.*\.(png|bmp)$ library.zip content/audio/* Files that match one of these patterns will be added as largefiles regardless of their size. The ``largefiles.minsize`` and ``largefiles.patterns`` config options will be ignored for any repositories not already containing a largefile. To add the first largefile to a repository, you must explicitly do so with the --large flag passed to the :hg:`add` command. ''' from mercurial import commands, localrepo import lfcommands import reposetup import uisetup as uisetupmod testedwith = 'internal' reposetup = reposetup.reposetup def featuresetup(ui, supported): # don't die on seeing a repo with the largefiles requirement supported |= set(['largefiles']) def uisetup(ui): localrepo.localrepository.featuresetupfuncs.add(featuresetup) uisetupmod.uisetup(ui) commands.norepo += " lfconvert" cmdtable = lfcommands.cmdtable mercurial-2.8.2/hgext/largefiles/reposetup.py0000644000175000017500000005706012261160437023162 0ustar oxymoronoxymoron00000000000000# Copyright 2009-2010 Gregory P. Ward # Copyright 2009-2010 Intelerad Medical Systems Incorporated # Copyright 2010-2011 Fog Creek Software # Copyright 2010-2011 Unity Technologies # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''setup for largefiles repositories: reposetup''' import copy import os from mercurial import error, manifest, match as match_, util, discovery from mercurial import node as node_ from mercurial.i18n import _ from mercurial import localrepo import lfcommands import proto import lfutil def reposetup(ui, repo): # wire repositories should be given new wireproto functions but not the # other largefiles modifications if not repo.local(): return proto.wirereposetup(ui, repo) class lfilesrepo(repo.__class__): lfstatus = False def status_nolfiles(self, *args, **kwargs): return super(lfilesrepo, self).status(*args, **kwargs) # When lfstatus is set, return a context that gives the names # of largefiles instead of their corresponding standins and # identifies the largefiles as always binary, regardless of # their actual contents. def __getitem__(self, changeid): ctx = super(lfilesrepo, self).__getitem__(changeid) if self.lfstatus: class lfilesmanifestdict(manifest.manifestdict): def __contains__(self, filename): if super(lfilesmanifestdict, self).__contains__(filename): return True return super(lfilesmanifestdict, self).__contains__(lfutil.standin(filename)) class lfilesctx(ctx.__class__): def files(self): filenames = super(lfilesctx, self).files() return [lfutil.splitstandin(f) or f for f in filenames] def manifest(self): man1 = super(lfilesctx, self).manifest() man1.__class__ = lfilesmanifestdict return man1 def filectx(self, path, fileid=None, filelog=None): try: if filelog is not None: result = super(lfilesctx, self).filectx( path, fileid, filelog) else: result = super(lfilesctx, self).filectx( path, fileid) except error.LookupError: # Adding a null character will cause Mercurial to # identify this as a binary file. if filelog is not None: result = super(lfilesctx, self).filectx( lfutil.standin(path), fileid, filelog) else: result = super(lfilesctx, self).filectx( lfutil.standin(path), fileid) olddata = result.data result.data = lambda: olddata() + '\0' return result ctx.__class__ = lfilesctx return ctx # Figure out the status of big files and insert them into the # appropriate list in the result. Also removes standin files # from the listing. Revert to the original status if # self.lfstatus is False. # XXX large file status is buggy when used on repo proxy. # XXX this needs to be investigated. @localrepo.unfilteredmethod def status(self, node1='.', node2=None, match=None, ignored=False, clean=False, unknown=False, listsubrepos=False): listignored, listclean, listunknown = ignored, clean, unknown if not self.lfstatus: return super(lfilesrepo, self).status(node1, node2, match, listignored, listclean, listunknown, listsubrepos) else: # some calls in this function rely on the old version of status self.lfstatus = False ctx1 = self[node1] ctx2 = self[node2] working = ctx2.rev() is None parentworking = working and ctx1 == self['.'] def inctx(file, ctx): try: if ctx.rev() is None: return file in ctx.manifest() ctx[file] return True except KeyError: return False if match is None: match = match_.always(self.root, self.getcwd()) wlock = None try: try: # updating the dirstate is optional # so we don't wait on the lock wlock = self.wlock(False) except error.LockError: pass # First check if there were files specified on the # command line. If there were, and none of them were # largefiles, we should just bail here and let super # handle it -- thus gaining a big performance boost. lfdirstate = lfutil.openlfdirstate(ui, self) if match.files() and not match.anypats(): for f in lfdirstate: if match(f): break else: return super(lfilesrepo, self).status(node1, node2, match, listignored, listclean, listunknown, listsubrepos) # Create a copy of match that matches standins instead # of largefiles. def tostandins(files): if not working: return files newfiles = [] dirstate = self.dirstate for f in files: sf = lfutil.standin(f) if sf in dirstate: newfiles.append(sf) elif sf in dirstate.dirs(): # Directory entries could be regular or # standin, check both newfiles.extend((f, sf)) else: newfiles.append(f) return newfiles m = copy.copy(match) m._files = tostandins(m._files) result = super(lfilesrepo, self).status(node1, node2, m, ignored, clean, unknown, listsubrepos) if working: def sfindirstate(f): sf = lfutil.standin(f) dirstate = self.dirstate return sf in dirstate or sf in dirstate.dirs() match._files = [f for f in match._files if sfindirstate(f)] # Don't waste time getting the ignored and unknown # files from lfdirstate s = lfdirstate.status(match, [], False, listclean, False) (unsure, modified, added, removed, missing, _unknown, _ignored, clean) = s if parentworking: for lfile in unsure: standin = lfutil.standin(lfile) if standin not in ctx1: # from second parent modified.append(lfile) elif ctx1[standin].data().strip() \ != lfutil.hashfile(self.wjoin(lfile)): modified.append(lfile) else: clean.append(lfile) lfdirstate.normal(lfile) else: tocheck = unsure + modified + added + clean modified, added, clean = [], [], [] for lfile in tocheck: standin = lfutil.standin(lfile) if inctx(standin, ctx1): if ctx1[standin].data().strip() != \ lfutil.hashfile(self.wjoin(lfile)): modified.append(lfile) else: clean.append(lfile) else: added.append(lfile) # Standins no longer found in lfdirstate has been # removed for standin in ctx1.manifest(): if not lfutil.isstandin(standin): continue lfile = lfutil.splitstandin(standin) if not match(lfile): continue if lfile not in lfdirstate: removed.append(lfile) # Filter result lists result = list(result) # Largefiles are not really removed when they're # still in the normal dirstate. Likewise, normal # files are not really removed if they are still in # lfdirstate. This happens in merges where files # change type. removed = [f for f in removed if f not in self.dirstate] result[2] = [f for f in result[2] if f not in lfdirstate] lfiles = set(lfdirstate._map) # Unknown files result[4] = set(result[4]).difference(lfiles) # Ignored files result[5] = set(result[5]).difference(lfiles) # combine normal files and largefiles normals = [[fn for fn in filelist if not lfutil.isstandin(fn)] for filelist in result] lfiles = (modified, added, removed, missing, [], [], clean) result = [sorted(list1 + list2) for (list1, list2) in zip(normals, lfiles)] else: def toname(f): if lfutil.isstandin(f): return lfutil.splitstandin(f) return f result = [[toname(f) for f in items] for items in result] if wlock: lfdirstate.write() finally: if wlock: wlock.release() if not listunknown: result[4] = [] if not listignored: result[5] = [] if not listclean: result[6] = [] self.lfstatus = True return result # As part of committing, copy all of the largefiles into the # cache. def commitctx(self, *args, **kwargs): node = super(lfilesrepo, self).commitctx(*args, **kwargs) lfutil.copyalltostore(self, node) return node # Before commit, largefile standins have not had their # contents updated to reflect the hash of their largefile. # Do that here. def commit(self, text="", user=None, date=None, match=None, force=False, editor=False, extra={}): orig = super(lfilesrepo, self).commit wlock = self.wlock() try: # Case 0: Rebase or Transplant # We have to take the time to pull down the new largefiles now. # Otherwise, any largefiles that were modified in the # destination changesets get overwritten, either by the rebase # or in the first commit after the rebase or transplant. # updatelfiles will update the dirstate to mark any pulled # largefiles as modified if getattr(self, "_isrebasing", False) or \ getattr(self, "_istransplanting", False): lfcommands.updatelfiles(self.ui, self, filelist=None, printmessage=False) result = orig(text=text, user=user, date=date, match=match, force=force, editor=editor, extra=extra) return result # Case 1: user calls commit with no specific files or # include/exclude patterns: refresh and commit all files that # are "dirty". if ((match is None) or (not match.anypats() and not match.files())): # Spend a bit of time here to get a list of files we know # are modified so we can compare only against those. # It can cost a lot of time (several seconds) # otherwise to update all standins if the largefiles are # large. lfdirstate = lfutil.openlfdirstate(ui, self) dirtymatch = match_.always(self.root, self.getcwd()) s = lfdirstate.status(dirtymatch, [], False, False, False) (unsure, modified, added, removed, _missing, _unknown, _ignored, _clean) = s modifiedfiles = unsure + modified + added + removed lfiles = lfutil.listlfiles(self) # this only loops through largefiles that exist (not # removed/renamed) for lfile in lfiles: if lfile in modifiedfiles: if os.path.exists( self.wjoin(lfutil.standin(lfile))): # this handles the case where a rebase is being # performed and the working copy is not updated # yet. if os.path.exists(self.wjoin(lfile)): lfutil.updatestandin(self, lfutil.standin(lfile)) lfdirstate.normal(lfile) result = orig(text=text, user=user, date=date, match=match, force=force, editor=editor, extra=extra) if result is not None: for lfile in lfdirstate: if lfile in modifiedfiles: if (not os.path.exists(self.wjoin( lfutil.standin(lfile)))) or \ (not os.path.exists(self.wjoin(lfile))): lfdirstate.drop(lfile) # This needs to be after commit; otherwise precommit hooks # get the wrong status lfdirstate.write() return result lfiles = lfutil.listlfiles(self) match._files = self._subdirlfs(match.files(), lfiles) # Case 2: user calls commit with specified patterns: refresh # any matching big files. smatcher = lfutil.composestandinmatcher(self, match) standins = self.dirstate.walk(smatcher, [], False, False) # No matching big files: get out of the way and pass control to # the usual commit() method. if not standins: return orig(text=text, user=user, date=date, match=match, force=force, editor=editor, extra=extra) # Refresh all matching big files. It's possible that the # commit will end up failing, in which case the big files will # stay refreshed. No harm done: the user modified them and # asked to commit them, so sooner or later we're going to # refresh the standins. Might as well leave them refreshed. lfdirstate = lfutil.openlfdirstate(ui, self) for standin in standins: lfile = lfutil.splitstandin(standin) if lfdirstate[lfile] != 'r': lfutil.updatestandin(self, standin) lfdirstate.normal(lfile) else: lfdirstate.drop(lfile) # Cook up a new matcher that only matches regular files or # standins corresponding to the big files requested by the # user. Have to modify _files to prevent commit() from # complaining "not tracked" for big files. match = copy.copy(match) origmatchfn = match.matchfn # Check both the list of largefiles and the list of # standins because if a largefile was removed, it # won't be in the list of largefiles at this point match._files += sorted(standins) actualfiles = [] for f in match._files: fstandin = lfutil.standin(f) # ignore known largefiles and standins if f in lfiles or fstandin in standins: continue # append directory separator to avoid collisions if not fstandin.endswith(os.sep): fstandin += os.sep actualfiles.append(f) match._files = actualfiles def matchfn(f): if origmatchfn(f): return f not in lfiles else: return f in standins match.matchfn = matchfn result = orig(text=text, user=user, date=date, match=match, force=force, editor=editor, extra=extra) # This needs to be after commit; otherwise precommit hooks # get the wrong status lfdirstate.write() return result finally: wlock.release() def push(self, remote, force=False, revs=None, newbranch=False): if remote.local(): missing = set(self.requirements) - remote.local().supported if missing: msg = _("required features are not" " supported in the destination:" " %s") % (', '.join(sorted(missing))) raise util.Abort(msg) outgoing = discovery.findcommonoutgoing(repo, remote.peer(), force=force) if outgoing.missing: toupload = set() o = self.changelog.nodesbetween(outgoing.missing, revs)[0] for n in o: parents = [p for p in self.changelog.parents(n) if p != node_.nullid] ctx = self[n] files = set(ctx.files()) if len(parents) == 2: mc = ctx.manifest() mp1 = ctx.parents()[0].manifest() mp2 = ctx.parents()[1].manifest() for f in mp1: if f not in mc: files.add(f) for f in mp2: if f not in mc: files.add(f) for f in mc: if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None): files.add(f) toupload = toupload.union( set([ctx[f].data().strip() for f in files if lfutil.isstandin(f) and f in ctx])) lfcommands.uploadlfiles(ui, self, remote, toupload) return super(lfilesrepo, self).push(remote, force, revs, newbranch) def _subdirlfs(self, files, lfiles): ''' Adjust matched file list If we pass a directory to commit whose only commitable files are largefiles, the core commit code aborts before finding the largefiles. So we do the following: For directories that only have largefiles as matches, we explicitly add the largefiles to the match list and remove the directory. In other cases, we leave the match list unmodified. ''' actualfiles = [] dirs = [] regulars = [] for f in files: if lfutil.isstandin(f + '/'): raise util.Abort( _('file "%s" is a largefile standin') % f, hint=('commit the largefile itself instead')) # Scan directories if os.path.isdir(self.wjoin(f)): dirs.append(f) else: regulars.append(f) for f in dirs: matcheddir = False d = self.dirstate.normalize(f) + '/' # Check for matched normal files for mf in regulars: if self.dirstate.normalize(mf).startswith(d): actualfiles.append(f) matcheddir = True break if not matcheddir: # If no normal match, manually append # any matching largefiles for lf in lfiles: if self.dirstate.normalize(lf).startswith(d): actualfiles.append(lf) if not matcheddir: actualfiles.append(lfutil.standin(f)) matcheddir = True # Nothing in dir, so readd it # and let commit reject it if not matcheddir: actualfiles.append(f) # Always add normal files actualfiles += regulars return actualfiles repo.__class__ = lfilesrepo def checkrequireslfiles(ui, repo, **kwargs): if 'largefiles' not in repo.requirements and util.any( lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()): repo.requirements.add('largefiles') repo._writerequirements() ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles) ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles) mercurial-2.8.2/hgext/largefiles/lfcommands.py0000644000175000017500000005235112261160437023255 0ustar oxymoronoxymoron00000000000000# Copyright 2009-2010 Gregory P. Ward # Copyright 2009-2010 Intelerad Medical Systems Incorporated # Copyright 2010-2011 Fog Creek Software # Copyright 2010-2011 Unity Technologies # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''High-level command function for lfconvert, plus the cmdtable.''' import os, errno import shutil from mercurial import util, match as match_, hg, node, context, error, \ cmdutil, scmutil, commands from mercurial.i18n import _ from mercurial.lock import release import lfutil import basestore # -- Commands ---------------------------------------------------------- def lfconvert(ui, src, dest, *pats, **opts): '''convert a normal repository to a largefiles repository Convert repository SOURCE to a new repository DEST, identical to SOURCE except that certain files will be converted as largefiles: specifically, any file that matches any PATTERN *or* whose size is above the minimum size threshold is converted as a largefile. The size used to determine whether or not to track a file as a largefile is the size of the first version of the file. The minimum size can be specified either with --size or in configuration as ``largefiles.size``. After running this command you will need to make sure that largefiles is enabled anywhere you intend to push the new repository. Use --to-normal to convert largefiles back to normal files; after this, the DEST repository can be used without largefiles at all.''' if opts['to_normal']: tolfile = False else: tolfile = True size = lfutil.getminsize(ui, True, opts.get('size'), default=None) if not hg.islocal(src): raise util.Abort(_('%s is not a local Mercurial repo') % src) if not hg.islocal(dest): raise util.Abort(_('%s is not a local Mercurial repo') % dest) rsrc = hg.repository(ui, src) ui.status(_('initializing destination %s\n') % dest) rdst = hg.repository(ui, dest, create=True) success = False dstwlock = dstlock = None try: # Lock destination to prevent modification while it is converted to. # Don't need to lock src because we are just reading from its history # which can't change. dstwlock = rdst.wlock() dstlock = rdst.lock() # Get a list of all changesets in the source. The easy way to do this # is to simply walk the changelog, using changelog.nodesbetween(). # Take a look at mercurial/revlog.py:639 for more details. # Use a generator instead of a list to decrease memory usage ctxs = (rsrc[ctx] for ctx in rsrc.changelog.nodesbetween(None, rsrc.heads())[0]) revmap = {node.nullid: node.nullid} if tolfile: lfiles = set() normalfiles = set() if not pats: pats = ui.configlist(lfutil.longname, 'patterns', default=[]) if pats: matcher = match_.match(rsrc.root, '', list(pats)) else: matcher = None lfiletohash = {} for ctx in ctxs: ui.progress(_('converting revisions'), ctx.rev(), unit=_('revision'), total=rsrc['tip'].rev()) _lfconvert_addchangeset(rsrc, rdst, ctx, revmap, lfiles, normalfiles, matcher, size, lfiletohash) ui.progress(_('converting revisions'), None) if os.path.exists(rdst.wjoin(lfutil.shortname)): shutil.rmtree(rdst.wjoin(lfutil.shortname)) for f in lfiletohash.keys(): if os.path.isfile(rdst.wjoin(f)): os.unlink(rdst.wjoin(f)) try: os.removedirs(os.path.dirname(rdst.wjoin(f))) except OSError: pass # If there were any files converted to largefiles, add largefiles # to the destination repository's requirements. if lfiles: rdst.requirements.add('largefiles') rdst._writerequirements() else: for ctx in ctxs: ui.progress(_('converting revisions'), ctx.rev(), unit=_('revision'), total=rsrc['tip'].rev()) _addchangeset(ui, rsrc, rdst, ctx, revmap) ui.progress(_('converting revisions'), None) success = True finally: rdst.dirstate.clear() release(dstlock, dstwlock) if not success: # we failed, remove the new directory shutil.rmtree(rdst.root) def _addchangeset(ui, rsrc, rdst, ctx, revmap): # Convert src parents to dst parents parents = _convertparents(ctx, revmap) # Generate list of changed files files = _getchangedfiles(ctx, parents) def getfilectx(repo, memctx, f): if lfutil.standin(f) in files: # if the file isn't in the manifest then it was removed # or renamed, raise IOError to indicate this try: fctx = ctx.filectx(lfutil.standin(f)) except error.LookupError: raise IOError renamed = fctx.renamed() if renamed: renamed = lfutil.splitstandin(renamed[0]) hash = fctx.data().strip() path = lfutil.findfile(rsrc, hash) # If one file is missing, likely all files from this rev are if path is None: cachelfiles(ui, rsrc, ctx.node()) path = lfutil.findfile(rsrc, hash) if path is None: raise util.Abort( _("missing largefile \'%s\' from revision %s") % (f, node.hex(ctx.node()))) data = '' fd = None try: fd = open(path, 'rb') data = fd.read() finally: if fd: fd.close() return context.memfilectx(f, data, 'l' in fctx.flags(), 'x' in fctx.flags(), renamed) else: return _getnormalcontext(repo.ui, ctx, f, revmap) dstfiles = [] for file in files: if lfutil.isstandin(file): dstfiles.append(lfutil.splitstandin(file)) else: dstfiles.append(file) # Commit _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap) def _lfconvert_addchangeset(rsrc, rdst, ctx, revmap, lfiles, normalfiles, matcher, size, lfiletohash): # Convert src parents to dst parents parents = _convertparents(ctx, revmap) # Generate list of changed files files = _getchangedfiles(ctx, parents) dstfiles = [] for f in files: if f not in lfiles and f not in normalfiles: islfile = _islfile(f, ctx, matcher, size) # If this file was renamed or copied then copy # the largefile-ness of its predecessor if f in ctx.manifest(): fctx = ctx.filectx(f) renamed = fctx.renamed() renamedlfile = renamed and renamed[0] in lfiles islfile |= renamedlfile if 'l' in fctx.flags(): if renamedlfile: raise util.Abort( _('renamed/copied largefile %s becomes symlink') % f) islfile = False if islfile: lfiles.add(f) else: normalfiles.add(f) if f in lfiles: dstfiles.append(lfutil.standin(f)) # largefile in manifest if it has not been removed/renamed if f in ctx.manifest(): fctx = ctx.filectx(f) if 'l' in fctx.flags(): renamed = fctx.renamed() if renamed and renamed[0] in lfiles: raise util.Abort(_('largefile %s becomes symlink') % f) # largefile was modified, update standins m = util.sha1('') m.update(ctx[f].data()) hash = m.hexdigest() if f not in lfiletohash or lfiletohash[f] != hash: rdst.wwrite(f, ctx[f].data(), ctx[f].flags()) executable = 'x' in ctx[f].flags() lfutil.writestandin(rdst, lfutil.standin(f), hash, executable) lfiletohash[f] = hash else: # normal file dstfiles.append(f) def getfilectx(repo, memctx, f): if lfutil.isstandin(f): # if the file isn't in the manifest then it was removed # or renamed, raise IOError to indicate this srcfname = lfutil.splitstandin(f) try: fctx = ctx.filectx(srcfname) except error.LookupError: raise IOError renamed = fctx.renamed() if renamed: # standin is always a largefile because largefile-ness # doesn't change after rename or copy renamed = lfutil.standin(renamed[0]) return context.memfilectx(f, lfiletohash[srcfname] + '\n', 'l' in fctx.flags(), 'x' in fctx.flags(), renamed) else: return _getnormalcontext(repo.ui, ctx, f, revmap) # Commit _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap) def _commitcontext(rdst, parents, ctx, dstfiles, getfilectx, revmap): mctx = context.memctx(rdst, parents, ctx.description(), dstfiles, getfilectx, ctx.user(), ctx.date(), ctx.extra()) ret = rdst.commitctx(mctx) rdst.setparents(ret) revmap[ctx.node()] = rdst.changelog.tip() # Generate list of changed files def _getchangedfiles(ctx, parents): files = set(ctx.files()) if node.nullid not in parents: mc = ctx.manifest() mp1 = ctx.parents()[0].manifest() mp2 = ctx.parents()[1].manifest() files |= (set(mp1) | set(mp2)) - set(mc) for f in mc: if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None): files.add(f) return files # Convert src parents to dst parents def _convertparents(ctx, revmap): parents = [] for p in ctx.parents(): parents.append(revmap[p.node()]) while len(parents) < 2: parents.append(node.nullid) return parents # Get memfilectx for a normal file def _getnormalcontext(ui, ctx, f, revmap): try: fctx = ctx.filectx(f) except error.LookupError: raise IOError renamed = fctx.renamed() if renamed: renamed = renamed[0] data = fctx.data() if f == '.hgtags': data = _converttags (ui, revmap, data) return context.memfilectx(f, data, 'l' in fctx.flags(), 'x' in fctx.flags(), renamed) # Remap tag data using a revision map def _converttags(ui, revmap, data): newdata = [] for line in data.splitlines(): try: id, name = line.split(' ', 1) except ValueError: ui.warn(_('skipping incorrectly formatted tag %s\n' % line)) continue try: newid = node.bin(id) except TypeError: ui.warn(_('skipping incorrectly formatted id %s\n' % id)) continue try: newdata.append('%s %s\n' % (node.hex(revmap[newid]), name)) except KeyError: ui.warn(_('no mapping for id %s\n') % id) continue return ''.join(newdata) def _islfile(file, ctx, matcher, size): '''Return true if file should be considered a largefile, i.e. matcher matches it or it is larger than size.''' # never store special .hg* files as largefiles if file == '.hgtags' or file == '.hgignore' or file == '.hgsigs': return False if matcher and matcher(file): return True try: return ctx.filectx(file).size() >= size * 1024 * 1024 except error.LookupError: return False def uploadlfiles(ui, rsrc, rdst, files): '''upload largefiles to the central store''' if not files: return store = basestore._openstore(rsrc, rdst, put=True) at = 0 ui.debug("sending statlfile command for %d largefiles\n" % len(files)) retval = store.exists(files) files = filter(lambda h: not retval[h], files) ui.debug("%d largefiles need to be uploaded\n" % len(files)) for hash in files: ui.progress(_('uploading largefiles'), at, unit='largefile', total=len(files)) source = lfutil.findfile(rsrc, hash) if not source: raise util.Abort(_('largefile %s missing from store' ' (needs to be uploaded)') % hash) # XXX check for errors here store.put(source, hash) at += 1 ui.progress(_('uploading largefiles'), None) def verifylfiles(ui, repo, all=False, contents=False): '''Verify that every largefile revision in the current changeset exists in the central store. With --contents, also verify that the contents of each local largefile file revision are correct (SHA-1 hash matches the revision ID). With --all, check every changeset in this repository.''' if all: # Pass a list to the function rather than an iterator because we know a # list will work. revs = range(len(repo)) else: revs = ['.'] store = basestore._openstore(repo) return store.verify(revs, contents=contents) def debugdirstate(ui, repo): '''Show basic information for the largefiles dirstate''' lfdirstate = lfutil.openlfdirstate(ui, repo) for file_, ent in sorted(lfdirstate._map.iteritems()): mode = '%3o' % (ent[1] & 0777 & ~util.umask) ui.write("%c %s %10d %s\n" % (ent[0], mode, ent[2], file_)) def cachelfiles(ui, repo, node, filelist=None): '''cachelfiles ensures that all largefiles needed by the specified revision are present in the repository's largefile cache. returns a tuple (cached, missing). cached is the list of files downloaded by this operation; missing is the list of files that were needed but could not be found.''' lfiles = lfutil.listlfiles(repo, node) if filelist: lfiles = set(lfiles) & set(filelist) toget = [] for lfile in lfiles: try: expectedhash = repo[node][lfutil.standin(lfile)].data().strip() except IOError, err: if err.errno == errno.ENOENT: continue # node must be None and standin wasn't found in wctx raise if not lfutil.findfile(repo, expectedhash): toget.append((lfile, expectedhash)) if toget: store = basestore._openstore(repo) ret = store.get(toget) return ret return ([], []) def downloadlfiles(ui, repo, rev=None): matchfn = scmutil.match(repo[None], [repo.wjoin(lfutil.shortname)], {}) def prepare(ctx, fns): pass totalsuccess = 0 totalmissing = 0 if rev != []: # walkchangerevs on empty list would return all revs for ctx in cmdutil.walkchangerevs(repo, matchfn, {'rev' : rev}, prepare): success, missing = cachelfiles(ui, repo, ctx.node()) totalsuccess += len(success) totalmissing += len(missing) ui.status(_("%d additional largefiles cached\n") % totalsuccess) if totalmissing > 0: ui.status(_("%d largefiles failed to download\n") % totalmissing) return totalsuccess, totalmissing def updatelfiles(ui, repo, filelist=None, printmessage=True): wlock = repo.wlock() try: lfdirstate = lfutil.openlfdirstate(ui, repo) lfiles = set(lfutil.listlfiles(repo)) | set(lfdirstate) if filelist is not None: lfiles = [f for f in lfiles if f in filelist] update = {} updated, removed = 0, 0 for lfile in lfiles: abslfile = repo.wjoin(lfile) absstandin = repo.wjoin(lfutil.standin(lfile)) if os.path.exists(absstandin): if (os.path.exists(absstandin + '.orig') and os.path.exists(abslfile)): shutil.copyfile(abslfile, abslfile + '.orig') expecthash = lfutil.readstandin(repo, lfile) if (expecthash != '' and (not os.path.exists(abslfile) or expecthash != lfutil.hashfile(abslfile))): if lfile not in repo[None]: # not switched to normal file util.unlinkpath(abslfile, ignoremissing=True) # use normallookup() to allocate entry in largefiles # dirstate, because lack of it misleads # lfilesrepo.status() into recognition that such cache # missing files are REMOVED. lfdirstate.normallookup(lfile) update[lfile] = expecthash else: # Remove lfiles for which the standin is deleted, unless the # lfile is added to the repository again. This happens when a # largefile is converted back to a normal file: the standin # disappears, but a new (normal) file appears as the lfile. if (os.path.exists(abslfile) and repo.dirstate.normalize(lfile) not in repo[None]): util.unlinkpath(abslfile) removed += 1 # largefile processing might be slow and be interrupted - be prepared lfdirstate.write() if lfiles: if printmessage: ui.status(_('getting changed largefiles\n')) cachelfiles(ui, repo, None, lfiles) for lfile in lfiles: update1 = 0 expecthash = update.get(lfile) if expecthash: if not lfutil.copyfromcache(repo, expecthash, lfile): # failed ... but already removed and set to normallookup continue # Synchronize largefile dirstate to the last modified # time of the file lfdirstate.normal(lfile) update1 = 1 # copy the state of largefile standin from the repository's # dirstate to its state in the lfdirstate. abslfile = repo.wjoin(lfile) absstandin = repo.wjoin(lfutil.standin(lfile)) if os.path.exists(absstandin): mode = os.stat(absstandin).st_mode if mode != os.stat(abslfile).st_mode: os.chmod(abslfile, mode) update1 = 1 updated += update1 state = repo.dirstate[lfutil.standin(lfile)] if state == 'n': # When rebasing, we need to synchronize the standin and the # largefile, because otherwise the largefile will get reverted. # But for commit's sake, we have to mark the file as unclean. if getattr(repo, "_isrebasing", False): lfdirstate.normallookup(lfile) else: lfdirstate.normal(lfile) elif state == 'r': lfdirstate.remove(lfile) elif state == 'a': lfdirstate.add(lfile) elif state == '?': lfdirstate.drop(lfile) lfdirstate.write() if printmessage and lfiles: ui.status(_('%d largefiles updated, %d removed\n') % (updated, removed)) finally: wlock.release() def lfpull(ui, repo, source="default", **opts): """pull largefiles for the specified revisions from the specified source Pull largefiles that are referenced from local changesets but missing locally, pulling from a remote repository to the local cache. If SOURCE is omitted, the 'default' path will be used. See :hg:`help urls` for more information. .. container:: verbose Some examples: - pull largefiles for all branch heads:: hg lfpull -r "head() and not closed()" - pull largefiles on the default branch:: hg lfpull -r "branch(default)" """ repo.lfpullsource = source revs = opts.get('rev', []) if not revs: raise util.Abort(_('no revisions specified')) revs = scmutil.revrange(repo, revs) numcached = 0 for rev in revs: ui.note(_('pulling largefiles for revision %s\n') % rev) (cached, missing) = cachelfiles(ui, repo, rev) numcached += len(cached) ui.status(_("%d largefiles cached\n") % numcached) # -- hg commands declarations ------------------------------------------------ cmdtable = { 'lfconvert': (lfconvert, [('s', 'size', '', _('minimum size (MB) for files to be converted ' 'as largefiles'), 'SIZE'), ('', 'to-normal', False, _('convert from a largefiles repo to a normal repo')), ], _('hg lfconvert SOURCE DEST [FILE ...]')), 'lfpull': (lfpull, [('r', 'rev', [], _('pull largefiles for these revisions')) ] + commands.remoteopts, _('-r REV... [-e CMD] [--remotecmd CMD] [SOURCE]') ), } commands.inferrepo += " lfconvert" mercurial-2.8.2/hgext/rebase.py0000644000175000017500000011046512261160437020257 0ustar oxymoronoxymoron00000000000000# rebase.py - rebasing feature for mercurial # # Copyright 2008 Stefano Tortarolo # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''command to move sets of revisions to a different ancestor This extension lets you rebase changesets in an existing Mercurial repository. For more information: http://mercurial.selenic.com/wiki/RebaseExtension ''' from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks from mercurial import extensions, patch, scmutil, phases, obsolete, error from mercurial.commands import templateopts from mercurial.node import nullrev from mercurial.lock import release from mercurial.i18n import _ import os, errno nullmerge = -2 revignored = -3 cmdtable = {} command = cmdutil.command(cmdtable) testedwith = 'internal' def _savegraft(ctx, extra): s = ctx.extra().get('source', None) if s is not None: extra['source'] = s def _savebranch(ctx, extra): extra['branch'] = ctx.branch() def _makeextrafn(copiers): """make an extrafn out of the given copy-functions. A copy function takes a context and an extra dict, and mutates the extra dict as needed based on the given context. """ def extrafn(ctx, extra): for c in copiers: c(ctx, extra) return extrafn @command('rebase', [('s', 'source', '', _('rebase from the specified changeset'), _('REV')), ('b', 'base', '', _('rebase from the base of the specified changeset ' '(up to greatest common ancestor of base and dest)'), _('REV')), ('r', 'rev', [], _('rebase these revisions'), _('REV')), ('d', 'dest', '', _('rebase onto the specified changeset'), _('REV')), ('', 'collapse', False, _('collapse the rebased changesets')), ('m', 'message', '', _('use text as collapse commit message'), _('TEXT')), ('e', 'edit', False, _('invoke editor on commit messages')), ('l', 'logfile', '', _('read collapse commit message from file'), _('FILE')), ('', 'keep', False, _('keep original changesets')), ('', 'keepbranches', False, _('keep original branch names')), ('D', 'detach', False, _('(DEPRECATED)')), ('t', 'tool', '', _('specify merge tool')), ('c', 'continue', False, _('continue an interrupted rebase')), ('a', 'abort', False, _('abort an interrupted rebase'))] + templateopts, _('[-s REV | -b REV] [-d REV] [OPTION]')) def rebase(ui, repo, **opts): """move changeset (and descendants) to a different branch Rebase uses repeated merging to graft changesets from one part of history (the source) onto another (the destination). This can be useful for linearizing *local* changes relative to a master development tree. You should not rebase changesets that have already been shared with others. Doing so will force everybody else to perform the same rebase or they will end up with duplicated changesets after pulling in your rebased changesets. In its default configuration, Mercurial will prevent you from rebasing published changes. See :hg:`help phases` for details. If you don't specify a destination changeset (``-d/--dest``), rebase uses the current branch tip as the destination. (The destination changeset is not modified by rebasing, but new changesets are added as its descendants.) You can specify which changesets to rebase in two ways: as a "source" changeset or as a "base" changeset. Both are shorthand for a topologically related set of changesets (the "source branch"). If you specify source (``-s/--source``), rebase will rebase that changeset and all of its descendants onto dest. If you specify base (``-b/--base``), rebase will select ancestors of base back to but not including the common ancestor with dest. Thus, ``-b`` is less precise but more convenient than ``-s``: you can specify any changeset in the source branch, and rebase will select the whole branch. If you specify neither ``-s`` nor ``-b``, rebase uses the parent of the working directory as the base. For advanced usage, a third way is available through the ``--rev`` option. It allows you to specify an arbitrary set of changesets to rebase. Descendants of revs you specify with this option are not automatically included in the rebase. By default, rebase recreates the changesets in the source branch as descendants of dest and then destroys the originals. Use ``--keep`` to preserve the original source changesets. Some changesets in the source branch (e.g. merges from the destination branch) may be dropped if they no longer contribute any change. One result of the rules for selecting the destination changeset and source branch is that, unlike ``merge``, rebase will do nothing if you are at the branch tip of a named branch with two heads. You need to explicitly specify source and/or destination (or ``update`` to the other head, if it's the head of the intended source branch). If a rebase is interrupted to manually resolve a merge, it can be continued with --continue/-c or aborted with --abort/-a. Returns 0 on success, 1 if nothing to rebase or there are unresolved conflicts. """ originalwd = target = None activebookmark = None external = nullrev state = {} skipped = set() targetancestors = set() editor = None if opts.get('edit'): editor = cmdutil.commitforceeditor lock = wlock = None try: wlock = repo.wlock() lock = repo.lock() # Validate input and define rebasing points destf = opts.get('dest', None) srcf = opts.get('source', None) basef = opts.get('base', None) revf = opts.get('rev', []) contf = opts.get('continue') abortf = opts.get('abort') collapsef = opts.get('collapse', False) collapsemsg = cmdutil.logmessage(ui, opts) e = opts.get('extrafn') # internal, used by e.g. hgsubversion extrafns = [_savegraft] if e: extrafns = [e] keepf = opts.get('keep', False) keepbranchesf = opts.get('keepbranches', False) # keepopen is not meant for use on the command line, but by # other extensions keepopen = opts.get('keepopen', False) if collapsemsg and not collapsef: raise util.Abort( _('message can only be specified with collapse')) if contf or abortf: if contf and abortf: raise util.Abort(_('cannot use both abort and continue')) if collapsef: raise util.Abort( _('cannot use collapse with continue or abort')) if srcf or basef or destf: raise util.Abort( _('abort and continue do not allow specifying revisions')) if opts.get('tool', False): ui.warn(_('tool option will be ignored\n')) try: (originalwd, target, state, skipped, collapsef, keepf, keepbranchesf, external, activebookmark) = restorestatus(repo) except error.RepoLookupError: if abortf: clearstatus(repo) repo.ui.warn(_('rebase aborted (no revision is removed,' ' only broken state is cleared)\n')) return 0 else: msg = _('cannot continue inconsistent rebase') hint = _('use "hg rebase --abort" to clear borken state') raise util.Abort(msg, hint=hint) if abortf: return abort(repo, originalwd, target, state) else: if srcf and basef: raise util.Abort(_('cannot specify both a ' 'source and a base')) if revf and basef: raise util.Abort(_('cannot specify both a ' 'revision and a base')) if revf and srcf: raise util.Abort(_('cannot specify both a ' 'revision and a source')) cmdutil.checkunfinished(repo) cmdutil.bailifchanged(repo) if not destf: # Destination defaults to the latest revision in the # current branch branch = repo[None].branch() dest = repo[branch] else: dest = scmutil.revsingle(repo, destf) if revf: rebaseset = scmutil.revrange(repo, revf) elif srcf: src = scmutil.revrange(repo, [srcf]) rebaseset = repo.revs('(%ld)::', src) else: base = scmutil.revrange(repo, [basef or '.']) rebaseset = repo.revs( '(children(ancestor(%ld, %d)) and ::(%ld))::', base, dest, base) if rebaseset: root = min(rebaseset) else: root = None if not rebaseset: repo.ui.debug('base is ancestor of destination\n') result = None elif (not (keepf or obsolete._enabled) and repo.revs('first(children(%ld) - %ld)', rebaseset, rebaseset)): raise util.Abort( _("can't remove original changesets with" " unrebased descendants"), hint=_('use --keep to keep original changesets')) else: result = buildstate(repo, dest, rebaseset, collapsef) if not result: # Empty state built, nothing to rebase ui.status(_('nothing to rebase\n')) return 1 elif not keepf and not repo[root].mutable(): raise util.Abort(_("can't rebase immutable changeset %s") % repo[root], hint=_('see hg help phases for details')) else: originalwd, target, state = result if collapsef: targetancestors = repo.changelog.ancestors([target], inclusive=True) external = externalparent(repo, state, targetancestors) if keepbranchesf: # insert _savebranch at the start of extrafns so if # there's a user-provided extrafn it can clobber branch if # desired extrafns.insert(0, _savebranch) if collapsef: branches = set() for rev in state: branches.add(repo[rev].branch()) if len(branches) > 1: raise util.Abort(_('cannot collapse multiple named ' 'branches')) # Rebase if not targetancestors: targetancestors = repo.changelog.ancestors([target], inclusive=True) # Keep track of the current bookmarks in order to reset them later currentbookmarks = repo._bookmarks.copy() activebookmark = activebookmark or repo._bookmarkcurrent if activebookmark: bookmarks.unsetcurrent(repo) extrafn = _makeextrafn(extrafns) sortedstate = sorted(state) total = len(sortedstate) pos = 0 for rev in sortedstate: pos += 1 if state[rev] == -1: ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])), _('changesets'), total) p1, p2 = defineparents(repo, rev, target, state, targetancestors) storestatus(repo, originalwd, target, state, collapsef, keepf, keepbranchesf, external, activebookmark) if len(repo.parents()) == 2: repo.ui.debug('resuming interrupted rebase\n') else: try: ui.setconfig('ui', 'forcemerge', opts.get('tool', '')) stats = rebasenode(repo, rev, p1, state, collapsef) if stats and stats[3] > 0: raise error.InterventionRequired( _('unresolved conflicts (see hg ' 'resolve, then hg rebase --continue)')) finally: ui.setconfig('ui', 'forcemerge', '') cmdutil.duplicatecopies(repo, rev, target) if not collapsef: newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn, editor=editor) else: # Skip commit if we are collapsing repo.setparents(repo[p1].node()) newrev = None # Update the state if newrev is not None: state[rev] = repo[newrev].rev() else: if not collapsef: ui.note(_('no changes, revision %d skipped\n') % rev) ui.debug('next revision set to %s\n' % p1) skipped.add(rev) state[rev] = p1 ui.progress(_('rebasing'), None) ui.note(_('rebase merging completed\n')) if collapsef and not keepopen: p1, p2 = defineparents(repo, min(state), target, state, targetancestors) if collapsemsg: commitmsg = collapsemsg else: commitmsg = 'Collapsed revision' for rebased in state: if rebased not in skipped and state[rebased] > nullmerge: commitmsg += '\n* %s' % repo[rebased].description() commitmsg = ui.edit(commitmsg, repo.ui.username()) newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg, extrafn=extrafn, editor=editor) for oldrev in state.iterkeys(): if state[oldrev] > nullmerge: state[oldrev] = newrev if 'qtip' in repo.tags(): updatemq(repo, state, skipped, **opts) if currentbookmarks: # Nodeids are needed to reset bookmarks nstate = {} for k, v in state.iteritems(): if v > nullmerge: nstate[repo[k].node()] = repo[v].node() # XXX this is the same as dest.node() for the non-continue path -- # this should probably be cleaned up targetnode = repo[target].node() # restore original working directory # (we do this before stripping) newwd = state.get(originalwd, originalwd) if newwd not in [c.rev() for c in repo[None].parents()]: ui.note(_("update back to initial working directory parent\n")) hg.updaterepo(repo, newwd, False) if not keepf: collapsedas = None if collapsef: collapsedas = newrev clearrebased(ui, repo, state, skipped, collapsedas) if currentbookmarks: updatebookmarks(repo, targetnode, nstate, currentbookmarks) clearstatus(repo) ui.note(_("rebase completed\n")) util.unlinkpath(repo.sjoin('undo'), ignoremissing=True) if skipped: ui.note(_("%d revisions have been skipped\n") % len(skipped)) if (activebookmark and repo['.'].node() == repo._bookmarks[activebookmark]): bookmarks.setcurrent(repo, activebookmark) finally: release(lock, wlock) def externalparent(repo, state, targetancestors): """Return the revision that should be used as the second parent when the revisions in state is collapsed on top of targetancestors. Abort if there is more than one parent. """ parents = set() source = min(state) for rev in state: if rev == source: continue for p in repo[rev].parents(): if (p.rev() not in state and p.rev() not in targetancestors): parents.add(p.rev()) if not parents: return nullrev if len(parents) == 1: return parents.pop() raise util.Abort(_('unable to collapse on top of %s, there is more ' 'than one external parent: %s') % (max(targetancestors), ', '.join(str(p) for p in sorted(parents)))) def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None): 'Commit the changes and store useful information in extra' try: repo.setparents(repo[p1].node(), repo[p2].node()) ctx = repo[rev] if commitmsg is None: commitmsg = ctx.description() extra = {'rebase_source': ctx.hex()} if extrafn: extrafn(ctx, extra) # Commit might fail if unresolved files exist newrev = repo.commit(text=commitmsg, user=ctx.user(), date=ctx.date(), extra=extra, editor=editor) repo.dirstate.setbranch(repo[newrev].branch()) targetphase = max(ctx.phase(), phases.draft) # retractboundary doesn't overwrite upper phase inherited from parent newnode = repo[newrev].node() if newnode: phases.retractboundary(repo, targetphase, [newnode]) return newrev except util.Abort: # Invalidate the previous setparents repo.dirstate.invalidate() raise def rebasenode(repo, rev, p1, state, collapse): 'Rebase a single revision' # Merge phase # Update to target and merge it with local if repo['.'].rev() != repo[p1].rev(): repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1])) merge.update(repo, p1, False, True, False) else: repo.ui.debug(" already in target\n") repo.dirstate.write() repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev])) if repo[rev].rev() == repo[min(state)].rev(): # Case (1) initial changeset of a non-detaching rebase. # Let the merge mechanism find the base itself. base = None elif not repo[rev].p2(): # Case (2) detaching the node with a single parent, use this parent base = repo[rev].p1().node() else: # In case of merge, we need to pick the right parent as merge base. # # Imagine we have: # - M: currently rebase revision in this step # - A: one parent of M # - B: second parent of M # - D: destination of this merge step (p1 var) # # If we are rebasing on D, D is the successors of A or B. The right # merge base is the one D succeed to. We pretend it is B for the rest # of this comment # # If we pick B as the base, the merge involves: # - changes from B to M (actual changeset payload) # - changes from B to D (induced by rebase) as D is a rebased # version of B) # Which exactly represent the rebase operation. # # If we pick the A as the base, the merge involves # - changes from A to M (actual changeset payload) # - changes from A to D (with include changes between unrelated A and B # plus changes induced by rebase) # Which does not represent anything sensible and creates a lot of # conflicts. for p in repo[rev].parents(): if state.get(p.rev()) == repo[p1].rev(): base = p.node() break if base is not None: repo.ui.debug(" detach base %d:%s\n" % (repo[base].rev(), repo[base])) # When collapsing in-place, the parent is the common ancestor, we # have to allow merging with it. return merge.update(repo, rev, True, True, False, base, collapse) def nearestrebased(repo, rev, state): """return the nearest ancestors of rev in the rebase result""" rebased = [r for r in state if state[r] > nullmerge] candidates = repo.revs('max(%ld and (::%d))', rebased, rev) if candidates: return state[candidates[0]] else: return None def defineparents(repo, rev, target, state, targetancestors): 'Return the new parent relationship of the revision that will be rebased' parents = repo[rev].parents() p1 = p2 = nullrev P1n = parents[0].rev() if P1n in targetancestors: p1 = target elif P1n in state: if state[P1n] == nullmerge: p1 = target elif state[P1n] == revignored: p1 = nearestrebased(repo, P1n, state) if p1 is None: p1 = target else: p1 = state[P1n] else: # P1n external p1 = target p2 = P1n if len(parents) == 2 and parents[1].rev() not in targetancestors: P2n = parents[1].rev() # interesting second parent if P2n in state: if p1 == target: # P1n in targetancestors or external p1 = state[P2n] elif state[P2n] == revignored: p2 = nearestrebased(repo, P2n, state) if p2 is None: # no ancestors rebased yet, detach p2 = target else: p2 = state[P2n] else: # P2n external if p2 != nullrev: # P1n external too => rev is a merged revision raise util.Abort(_('cannot use revision %d as base, result ' 'would have 3 parents') % rev) p2 = P2n repo.ui.debug(" future parents are %d and %d\n" % (repo[p1].rev(), repo[p2].rev())) return p1, p2 def isagitpatch(repo, patchname): 'Return true if the given patch is in git format' mqpatch = os.path.join(repo.mq.path, patchname) for line in patch.linereader(file(mqpatch, 'rb')): if line.startswith('diff --git'): return True return False def updatemq(repo, state, skipped, **opts): 'Update rebased mq patches - finalize and then import them' mqrebase = {} mq = repo.mq original_series = mq.fullseries[:] skippedpatches = set() for p in mq.applied: rev = repo[p.node].rev() if rev in state: repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' % (rev, p.name)) mqrebase[rev] = (p.name, isagitpatch(repo, p.name)) else: # Applied but not rebased, not sure this should happen skippedpatches.add(p.name) if mqrebase: mq.finish(repo, mqrebase.keys()) # We must start import from the newest revision for rev in sorted(mqrebase, reverse=True): if rev not in skipped: name, isgit = mqrebase[rev] repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name)) mq.qimport(repo, (), patchname=name, git=isgit, rev=[str(state[rev])]) else: # Rebased and skipped skippedpatches.add(mqrebase[rev][0]) # Patches were either applied and rebased and imported in # order, applied and removed or unapplied. Discard the removed # ones while preserving the original series order and guards. newseries = [s for s in original_series if mq.guard_re.split(s, 1)[0] not in skippedpatches] mq.fullseries[:] = newseries mq.seriesdirty = True mq.savedirty() def updatebookmarks(repo, targetnode, nstate, originalbookmarks): 'Move bookmarks to their correct changesets, and delete divergent ones' marks = repo._bookmarks for k, v in originalbookmarks.iteritems(): if v in nstate: # update the bookmarks for revs that have moved marks[k] = nstate[v] bookmarks.deletedivergent(repo, [targetnode], k) marks.write() def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches, external, activebookmark): 'Store the current status to allow recovery' f = repo.opener("rebasestate", "w") f.write(repo[originalwd].hex() + '\n') f.write(repo[target].hex() + '\n') f.write(repo[external].hex() + '\n') f.write('%d\n' % int(collapse)) f.write('%d\n' % int(keep)) f.write('%d\n' % int(keepbranches)) f.write('%s\n' % (activebookmark or '')) for d, v in state.iteritems(): oldrev = repo[d].hex() if v > nullmerge: newrev = repo[v].hex() else: newrev = v f.write("%s:%s\n" % (oldrev, newrev)) f.close() repo.ui.debug('rebase status stored\n') def clearstatus(repo): 'Remove the status files' util.unlinkpath(repo.join("rebasestate"), ignoremissing=True) def restorestatus(repo): 'Restore a previously stored status' try: target = None collapse = False external = nullrev activebookmark = None state = {} f = repo.opener("rebasestate") for i, l in enumerate(f.read().splitlines()): if i == 0: originalwd = repo[l].rev() elif i == 1: target = repo[l].rev() elif i == 2: external = repo[l].rev() elif i == 3: collapse = bool(int(l)) elif i == 4: keep = bool(int(l)) elif i == 5: keepbranches = bool(int(l)) elif i == 6 and not (len(l) == 81 and ':' in l): # line 6 is a recent addition, so for backwards compatibility # check that the line doesn't look like the oldrev:newrev lines activebookmark = l else: oldrev, newrev = l.split(':') if newrev in (str(nullmerge), str(revignored)): state[repo[oldrev].rev()] = int(newrev) else: state[repo[oldrev].rev()] = repo[newrev].rev() skipped = set() # recompute the set of skipped revs if not collapse: seen = set([target]) for old, new in sorted(state.items()): if new != nullrev and new in seen: skipped.add(old) seen.add(new) repo.ui.debug('computed skipped revs: %s\n' % skipped) repo.ui.debug('rebase status resumed\n') return (originalwd, target, state, skipped, collapse, keep, keepbranches, external, activebookmark) except IOError, err: if err.errno != errno.ENOENT: raise raise util.Abort(_('no rebase in progress')) def inrebase(repo, originalwd, state): '''check whether the working dir is in an interrupted rebase''' parents = [p.rev() for p in repo.parents()] if originalwd in parents: return True for newrev in state.itervalues(): if newrev in parents: return True return False def abort(repo, originalwd, target, state): 'Restore the repository to its original state' dstates = [s for s in state.values() if s > nullrev] immutable = [d for d in dstates if not repo[d].mutable()] cleanup = True if immutable: repo.ui.warn(_("warning: can't clean up immutable changesets %s\n") % ', '.join(str(repo[r]) for r in immutable), hint=_('see hg help phases for details')) cleanup = False descendants = set() if dstates: descendants = set(repo.changelog.descendants(dstates)) if descendants - set(dstates): repo.ui.warn(_("warning: new changesets detected on target branch, " "can't strip\n")) cleanup = False if cleanup: # Update away from the rebase if necessary if inrebase(repo, originalwd, state): merge.update(repo, repo[originalwd].rev(), False, True, False) # Strip from the first rebased revision rebased = filter(lambda x: x > -1 and x != target, state.values()) if rebased: strippoints = [c.node() for c in repo.set('roots(%ld)', rebased)] # no backup of rebased cset versions needed repair.strip(repo.ui, repo, strippoints) clearstatus(repo) repo.ui.warn(_('rebase aborted\n')) return 0 def buildstate(repo, dest, rebaseset, collapse): '''Define which revisions are going to be rebased and where repo: repo dest: context rebaseset: set of rev ''' # This check isn't strictly necessary, since mq detects commits over an # applied patch. But it prevents messing up the working directory when # a partially completed rebase is blocked by mq. if 'qtip' in repo.tags() and (dest.node() in [s.node for s in repo.mq.applied]): raise util.Abort(_('cannot rebase onto an applied mq patch')) roots = list(repo.set('roots(%ld)', rebaseset)) if not roots: raise util.Abort(_('no matching revisions')) roots.sort() state = {} detachset = set() for root in roots: commonbase = root.ancestor(dest) if commonbase == root: raise util.Abort(_('source is ancestor of destination')) if commonbase == dest: samebranch = root.branch() == dest.branch() if not collapse and samebranch and root in dest.children(): repo.ui.debug('source is a child of destination\n') return None repo.ui.debug('rebase onto %d starting from %s\n' % (dest, roots)) state.update(dict.fromkeys(rebaseset, nullrev)) # Rebase tries to turn into a parent of while # preserving the number of parents of rebased changesets: # # - A changeset with a single parent will always be rebased as a # changeset with a single parent. # # - A merge will be rebased as merge unless its parents are both # ancestors of or are themselves in the rebased set and # pruned while rebased. # # If one parent of is an ancestor of , the rebased # version of this parent will be . This is always true with # --base option. # # Otherwise, we need to *replace* the original parents with # . This "detaches" the rebased set from its former location # and rebases it onto . Changes introduced by ancestors of # not common with (the detachset, marked as # nullmerge) are "removed" from the rebased changesets. # # - If has a single parent, set it to . # # - If is a merge, we cannot decide which parent to # replace, the rebase operation is not clearly defined. # # The table below sums up this behavior: # # +------------------+----------------------+-------------------------+ # | | one parent | merge | # +------------------+----------------------+-------------------------+ # | parent in | new parent is | parents in :: are | # | :: | | remapped to | # +------------------+----------------------+-------------------------+ # | unrelated source | new parent is | ambiguous, abort | # +------------------+----------------------+-------------------------+ # # The actual abort is handled by `defineparents` if len(root.parents()) <= 1: # ancestors of not ancestors of detachset.update(repo.changelog.findmissingrevs([commonbase.rev()], [root.rev()])) for r in detachset: if r not in state: state[r] = nullmerge if len(roots) > 1: # If we have multiple roots, we may have "hole" in the rebase set. # Rebase roots that descend from those "hole" should not be detached as # other root are. We use the special `revignored` to inform rebase that # the revision should be ignored but that `defineparents` should search # a rebase destination that make sense regarding rebased topology. rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset)) for ignored in set(rebasedomain) - set(rebaseset): state[ignored] = revignored return repo['.'].rev(), dest.rev(), state def clearrebased(ui, repo, state, skipped, collapsedas=None): """dispose of rebased revision at the end of the rebase If `collapsedas` is not None, the rebase was a collapse whose result if the `collapsedas` node.""" if obsolete._enabled: markers = [] for rev, newrev in sorted(state.items()): if newrev >= 0: if rev in skipped: succs = () elif collapsedas is not None: succs = (repo[collapsedas],) else: succs = (repo[newrev],) markers.append((repo[rev], succs)) if markers: obsolete.createmarkers(repo, markers) else: rebased = [rev for rev in state if state[rev] > nullmerge] if rebased: stripped = [] for root in repo.set('roots(%ld)', rebased): if set(repo.changelog.descendants([root.rev()])) - set(state): ui.warn(_("warning: new changesets detected " "on source branch, not stripping\n")) else: stripped.append(root.node()) if stripped: # backup the old csets by default repair.strip(ui, repo, stripped, "all") def pullrebase(orig, ui, repo, *args, **opts): 'Call rebase after pull if the latter has been invoked with --rebase' if opts.get('rebase'): if opts.get('update'): del opts['update'] ui.debug('--update and --rebase are not compatible, ignoring ' 'the update flag\n') movemarkfrom = repo['.'].node() revsprepull = len(repo) origpostincoming = commands.postincoming def _dummy(*args, **kwargs): pass commands.postincoming = _dummy try: orig(ui, repo, *args, **opts) finally: commands.postincoming = origpostincoming revspostpull = len(repo) if revspostpull > revsprepull: # --rev option from pull conflict with rebase own --rev # dropping it if 'rev' in opts: del opts['rev'] rebase(ui, repo, **opts) branch = repo[None].branch() dest = repo[branch].rev() if dest != repo['.'].rev(): # there was nothing to rebase we force an update hg.update(repo, dest) if bookmarks.update(repo, [movemarkfrom], repo['.'].node()): ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent) else: if opts.get('tool'): raise util.Abort(_('--tool can only be used with --rebase')) orig(ui, repo, *args, **opts) def summaryhook(ui, repo): if not os.path.exists(repo.join('rebasestate')): return try: state = restorestatus(repo)[2] except error.RepoLookupError: # i18n: column positioning for "hg summary" msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n') ui.write(msg) return numrebased = len([i for i in state.itervalues() if i != -1]) # i18n: column positioning for "hg summary" ui.write(_('rebase: %s, %s (rebase --continue)\n') % (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased, ui.label(_('%d remaining'), 'rebase.remaining') % (len(state) - numrebased))) def uisetup(ui): 'Replace pull with a decorator to provide --rebase option' entry = extensions.wrapcommand(commands.table, 'pull', pullrebase) entry[1].append(('', 'rebase', None, _("rebase working directory to branch head"))) entry[1].append(('t', 'tool', '', _("specify merge tool for rebase"))) cmdutil.summaryhooks.add('rebase', summaryhook) cmdutil.unfinishedstates.append( ['rebasestate', False, False, _('rebase in progress'), _("use 'hg rebase --continue' or 'hg rebase --abort'")]) mercurial-2.8.2/hgext/color.py0000644000175000017500000004656412261160437020144 0ustar oxymoronoxymoron00000000000000# color.py color output for the status and qseries commands # # Copyright (C) 2007 Kevin Christen # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''colorize output from some commands This extension modifies the status and resolve commands to add color to their output to reflect file status, the qseries command to add color to reflect patch status (applied, unapplied, missing), and to diff-related commands to highlight additions, removals, diff headers, and trailing whitespace. Other effects in addition to color, like bold and underlined text, are also available. By default, the terminfo database is used to find the terminal codes used to change color and effect. If terminfo is not available, then effects are rendered with the ECMA-48 SGR control function (aka ANSI escape codes). Default effects may be overridden from your configuration file:: [color] status.modified = blue bold underline red_background status.added = green bold status.removed = red bold blue_background status.deleted = cyan bold underline status.unknown = magenta bold underline status.ignored = black bold # 'none' turns off all effects status.clean = none status.copied = none qseries.applied = blue bold underline qseries.unapplied = black bold qseries.missing = red bold diff.diffline = bold diff.extended = cyan bold diff.file_a = red bold diff.file_b = green bold diff.hunk = magenta diff.deleted = red diff.inserted = green diff.changed = white diff.trailingwhitespace = bold red_background resolve.unresolved = red bold resolve.resolved = green bold bookmarks.current = green branches.active = none branches.closed = black bold branches.current = green branches.inactive = none tags.normal = green tags.local = black bold rebase.rebased = blue rebase.remaining = red bold shelve.age = cyan shelve.newest = green bold shelve.name = blue bold histedit.remaining = red bold The available effects in terminfo mode are 'blink', 'bold', 'dim', 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and 'underline'. How each is rendered depends on the terminal emulator. Some may not be available for a given terminal type, and will be silently ignored. Note that on some systems, terminfo mode may cause problems when using color with the pager extension and less -R. less with the -R option will only display ECMA-48 color codes, and terminfo mode may sometimes emit codes that less doesn't understand. You can work around this by either using ansi mode (or auto mode), or by using less -r (which will pass through all terminal control codes, not just color control codes). Because there are only eight standard colors, this module allows you to define color names for other color slots which might be available for your terminal type, assuming terminfo mode. For instance:: color.brightblue = 12 color.pink = 207 color.orange = 202 to set 'brightblue' to color slot 12 (useful for 16 color terminals that have brighter colors defined in the upper eight) and, 'pink' and 'orange' to colors in 256-color xterm's default color cube. These defined colors may then be used as any of the pre-defined eight, including appending '_background' to set the background to that color. By default, the color extension will use ANSI mode (or win32 mode on Windows) if it detects a terminal. To override auto mode (to enable terminfo mode, for example), set the following configuration option:: [color] mode = terminfo Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will disable color. ''' import os from mercurial import commands, dispatch, extensions, ui as uimod, util from mercurial import templater, error from mercurial.i18n import _ testedwith = 'internal' # start and stop parameters for effects _effects = {'none': 0, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'bold': 1, 'italic': 3, 'underline': 4, 'inverse': 7, 'black_background': 40, 'red_background': 41, 'green_background': 42, 'yellow_background': 43, 'blue_background': 44, 'purple_background': 45, 'cyan_background': 46, 'white_background': 47} def _terminfosetup(ui, mode): '''Initialize terminfo data and the terminal if we're in terminfo mode.''' global _terminfo_params # If we failed to load curses, we go ahead and return. if not _terminfo_params: return # Otherwise, see what the config file says. if mode not in ('auto', 'terminfo'): return _terminfo_params.update((key[6:], (False, int(val))) for key, val in ui.configitems('color') if key.startswith('color.')) try: curses.setupterm() except curses.error, e: _terminfo_params = {} return for key, (b, e) in _terminfo_params.items(): if not b: continue if not curses.tigetstr(e): # Most terminals don't support dim, invis, etc, so don't be # noisy and use ui.debug(). ui.debug("no terminfo entry for %s\n" % e) del _terminfo_params[key] if not curses.tigetstr('setaf') or not curses.tigetstr('setab'): # Only warn about missing terminfo entries if we explicitly asked for # terminfo mode. if mode == "terminfo": ui.warn(_("no terminfo entry for setab/setaf: reverting to " "ECMA-48 color\n")) _terminfo_params = {} def _modesetup(ui, coloropt): global _terminfo_params auto = coloropt == 'auto' always = not auto and util.parsebool(coloropt) if not always and not auto: return None formatted = always or (os.environ.get('TERM') != 'dumb' and ui.formatted()) mode = ui.config('color', 'mode', 'auto') realmode = mode if mode == 'auto': if os.name == 'nt' and 'TERM' not in os.environ: # looks line a cmd.exe console, use win32 API or nothing realmode = 'win32' else: realmode = 'ansi' if realmode == 'win32': _terminfo_params = {} if not w32effects: if mode == 'win32': # only warn if color.mode is explicitly set to win32 ui.warn(_('warning: failed to set color mode to %s\n') % mode) return None _effects.update(w32effects) elif realmode == 'ansi': _terminfo_params = {} elif realmode == 'terminfo': _terminfosetup(ui, mode) if not _terminfo_params: if mode == 'terminfo': ## FIXME Shouldn't we return None in this case too? # only warn if color.mode is explicitly set to win32 ui.warn(_('warning: failed to set color mode to %s\n') % mode) realmode = 'ansi' else: return None if always or (auto and formatted): return realmode return None try: import curses # Mapping from effect name to terminfo attribute name or color number. # This will also force-load the curses module. _terminfo_params = {'none': (True, 'sgr0'), 'standout': (True, 'smso'), 'underline': (True, 'smul'), 'reverse': (True, 'rev'), 'inverse': (True, 'rev'), 'blink': (True, 'blink'), 'dim': (True, 'dim'), 'bold': (True, 'bold'), 'invisible': (True, 'invis'), 'italic': (True, 'sitm'), 'black': (False, curses.COLOR_BLACK), 'red': (False, curses.COLOR_RED), 'green': (False, curses.COLOR_GREEN), 'yellow': (False, curses.COLOR_YELLOW), 'blue': (False, curses.COLOR_BLUE), 'magenta': (False, curses.COLOR_MAGENTA), 'cyan': (False, curses.COLOR_CYAN), 'white': (False, curses.COLOR_WHITE)} except ImportError: _terminfo_params = False _styles = {'grep.match': 'red bold', 'grep.linenumber': 'green', 'grep.rev': 'green', 'grep.change': 'green', 'grep.sep': 'cyan', 'grep.filename': 'magenta', 'grep.user': 'magenta', 'grep.date': 'magenta', 'bookmarks.current': 'green', 'branches.active': 'none', 'branches.closed': 'black bold', 'branches.current': 'green', 'branches.inactive': 'none', 'diff.changed': 'white', 'diff.deleted': 'red', 'diff.diffline': 'bold', 'diff.extended': 'cyan bold', 'diff.file_a': 'red bold', 'diff.file_b': 'green bold', 'diff.hunk': 'magenta', 'diff.inserted': 'green', 'diff.trailingwhitespace': 'bold red_background', 'diffstat.deleted': 'red', 'diffstat.inserted': 'green', 'histedit.remaining': 'red bold', 'ui.prompt': 'yellow', 'log.changeset': 'yellow', 'rebase.rebased': 'blue', 'rebase.remaining': 'red bold', 'resolve.resolved': 'green bold', 'resolve.unresolved': 'red bold', 'shelve.age': 'cyan', 'shelve.newest': 'green bold', 'shelve.name': 'blue bold', 'status.added': 'green bold', 'status.clean': 'none', 'status.copied': 'none', 'status.deleted': 'cyan bold underline', 'status.ignored': 'black bold', 'status.modified': 'blue bold', 'status.removed': 'red bold', 'status.unknown': 'magenta bold underline', 'tags.normal': 'green', 'tags.local': 'black bold'} def _effect_str(effect): '''Helper function for render_effects().''' bg = False if effect.endswith('_background'): bg = True effect = effect[:-11] attr, val = _terminfo_params[effect] if attr: return curses.tigetstr(val) elif bg: return curses.tparm(curses.tigetstr('setab'), val) else: return curses.tparm(curses.tigetstr('setaf'), val) def render_effects(text, effects): 'Wrap text in commands to turn on each effect.' if not text: return text if not _terminfo_params: start = [str(_effects[e]) for e in ['none'] + effects.split()] start = '\033[' + ';'.join(start) + 'm' stop = '\033[' + str(_effects['none']) + 'm' else: start = ''.join(_effect_str(effect) for effect in ['none'] + effects.split()) stop = _effect_str('none') return ''.join([start, text, stop]) def extstyles(): for name, ext in extensions.extensions(): _styles.update(getattr(ext, 'colortable', {})) def configstyles(ui): for status, cfgeffects in ui.configitems('color'): if '.' not in status or status.startswith('color.'): continue cfgeffects = ui.configlist('color', status) if cfgeffects: good = [] for e in cfgeffects: if not _terminfo_params and e in _effects: good.append(e) elif e in _terminfo_params or e[:-11] in _terminfo_params: good.append(e) else: ui.warn(_("ignoring unknown color/effect %r " "(configured in color.%s)\n") % (e, status)) _styles[status] = ' '.join(good) class colorui(uimod.ui): def popbuffer(self, labeled=False): if self._colormode is None: return super(colorui, self).popbuffer(labeled) if labeled: return ''.join(self.label(a, label) for a, label in self._buffers.pop()) return ''.join(a for a, label in self._buffers.pop()) _colormode = 'ansi' def write(self, *args, **opts): if self._colormode is None: return super(colorui, self).write(*args, **opts) label = opts.get('label', '') if self._buffers: self._buffers[-1].extend([(str(a), label) for a in args]) elif self._colormode == 'win32': for a in args: win32print(a, super(colorui, self).write, **opts) else: return super(colorui, self).write( *[self.label(str(a), label) for a in args], **opts) def write_err(self, *args, **opts): if self._colormode is None: return super(colorui, self).write_err(*args, **opts) label = opts.get('label', '') if self._colormode == 'win32': for a in args: win32print(a, super(colorui, self).write_err, **opts) else: return super(colorui, self).write_err( *[self.label(str(a), label) for a in args], **opts) def label(self, msg, label): if self._colormode is None: return super(colorui, self).label(msg, label) effects = [] for l in label.split(): s = _styles.get(l, '') if s: effects.append(s) effects = ' '.join(effects) if effects: return '\n'.join([render_effects(s, effects) for s in msg.split('\n')]) return msg def templatelabel(context, mapping, args): if len(args) != 2: # i18n: "label" is a keyword raise error.ParseError(_("label expects two arguments")) thing = templater._evalifliteral(args[1], context, mapping) # apparently, repo could be a string that is the favicon? repo = mapping.get('repo', '') if isinstance(repo, str): return thing label = templater.stringify(args[0][0](context, mapping, args[0][1])) label = templater.runtemplate(context, mapping, templater.compiletemplate(label, context)) thing = templater.stringify(thing) label = templater.stringify(label) return repo.ui.label(thing, label) def uisetup(ui): if ui.plain(): return if not isinstance(ui, colorui): colorui.__bases__ = (ui.__class__,) ui.__class__ = colorui def colorcmd(orig, ui_, opts, cmd, cmdfunc): mode = _modesetup(ui_, opts['color']) colorui._colormode = mode if mode: extstyles() configstyles(ui_) return orig(ui_, opts, cmd, cmdfunc) extensions.wrapfunction(dispatch, '_runcommand', colorcmd) templater.funcs['label'] = templatelabel def extsetup(ui): commands.globalopts.append( ('', 'color', 'auto', # i18n: 'always', 'auto', and 'never' are keywords and should # not be translated _("when to colorize (boolean, always, auto, or never)"), _('TYPE'))) if os.name != 'nt': w32effects = None else: import re, ctypes _kernel32 = ctypes.windll.kernel32 _WORD = ctypes.c_ushort _INVALID_HANDLE_VALUE = -1 class _COORD(ctypes.Structure): _fields_ = [('X', ctypes.c_short), ('Y', ctypes.c_short)] class _SMALL_RECT(ctypes.Structure): _fields_ = [('Left', ctypes.c_short), ('Top', ctypes.c_short), ('Right', ctypes.c_short), ('Bottom', ctypes.c_short)] class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure): _fields_ = [('dwSize', _COORD), ('dwCursorPosition', _COORD), ('wAttributes', _WORD), ('srWindow', _SMALL_RECT), ('dwMaximumWindowSize', _COORD)] _STD_OUTPUT_HANDLE = 0xfffffff5L # (DWORD)-11 _STD_ERROR_HANDLE = 0xfffffff4L # (DWORD)-12 _FOREGROUND_BLUE = 0x0001 _FOREGROUND_GREEN = 0x0002 _FOREGROUND_RED = 0x0004 _FOREGROUND_INTENSITY = 0x0008 _BACKGROUND_BLUE = 0x0010 _BACKGROUND_GREEN = 0x0020 _BACKGROUND_RED = 0x0040 _BACKGROUND_INTENSITY = 0x0080 _COMMON_LVB_REVERSE_VIDEO = 0x4000 _COMMON_LVB_UNDERSCORE = 0x8000 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx w32effects = { 'none': -1, 'black': 0, 'red': _FOREGROUND_RED, 'green': _FOREGROUND_GREEN, 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN, 'blue': _FOREGROUND_BLUE, 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED, 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN, 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE, 'bold': _FOREGROUND_INTENSITY, 'black_background': 0x100, # unused value > 0x0f 'red_background': _BACKGROUND_RED, 'green_background': _BACKGROUND_GREEN, 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN, 'blue_background': _BACKGROUND_BLUE, 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED, 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN, 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN | _BACKGROUND_BLUE), 'bold_background': _BACKGROUND_INTENSITY, 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only } passthrough = set([_FOREGROUND_INTENSITY, _BACKGROUND_INTENSITY, _COMMON_LVB_UNDERSCORE, _COMMON_LVB_REVERSE_VIDEO]) stdout = _kernel32.GetStdHandle( _STD_OUTPUT_HANDLE) # don't close the handle returned if stdout is None or stdout == _INVALID_HANDLE_VALUE: w32effects = None else: csbi = _CONSOLE_SCREEN_BUFFER_INFO() if not _kernel32.GetConsoleScreenBufferInfo( stdout, ctypes.byref(csbi)): # stdout may not support GetConsoleScreenBufferInfo() # when called from subprocess or redirected w32effects = None else: origattr = csbi.wAttributes ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)', re.MULTILINE | re.DOTALL) def win32print(text, orig, **opts): label = opts.get('label', '') attr = origattr def mapcolor(val, attr): if val == -1: return origattr elif val in passthrough: return attr | val elif val > 0x0f: return (val & 0x70) | (attr & 0x8f) else: return (val & 0x07) | (attr & 0xf8) # determine console attributes based on labels for l in label.split(): style = _styles.get(l, '') for effect in style.split(): attr = mapcolor(w32effects[effect], attr) # hack to ensure regexp finds data if not text.startswith('\033['): text = '\033[m' + text # Look for ANSI-like codes embedded in text m = re.match(ansire, text) try: while m: for sattr in m.group(1).split(';'): if sattr: attr = mapcolor(int(sattr), attr) _kernel32.SetConsoleTextAttribute(stdout, attr) orig(m.group(2), **opts) m = re.match(ansire, m.group(3)) finally: # Explicitly reset original attributes _kernel32.SetConsoleTextAttribute(stdout, origattr) mercurial-2.8.2/hgext/patchbomb.py0000644000175000017500000005175412261160437020762 0ustar oxymoronoxymoron00000000000000# patchbomb.py - sending Mercurial changesets as patch emails # # Copyright 2005-2009 Matt Mackall and others # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''command to send changesets as (a series of) patch emails The series is started off with a "[PATCH 0 of N]" introduction, which describes the series as a whole. Each patch email has a Subject line of "[PATCH M of N] ...", using the first line of the changeset description as the subject text. The message contains two or three body parts: - The changeset description. - [Optional] The result of running diffstat on the patch. - The patch itself, as generated by :hg:`export`. Each message refers to the first in the series using the In-Reply-To and References headers, so they will show up as a sequence in threaded mail and news readers, and in mail archives. To configure other defaults, add a section like this to your configuration file:: [email] from = My Name to = recipient1, recipient2, ... cc = cc1, cc2, ... bcc = bcc1, bcc2, ... reply-to = address1, address2, ... Use ``[patchbomb]`` as configuration section name if you need to override global ``[email]`` address settings. Then you can use the :hg:`email` command to mail a series of changesets as a patchbomb. You can also either configure the method option in the email section to be a sendmail compatible mailer or fill out the [smtp] section so that the patchbomb extension can automatically send patchbombs directly from the commandline. See the [email] and [smtp] sections in hgrc(5) for details. ''' import os, errno, socket, tempfile, cStringIO import email # On python2.4 you have to import these by name or they fail to # load. This was not a problem on Python 2.7. import email.Generator import email.MIMEMultipart from mercurial import cmdutil, commands, hg, mail, patch, util from mercurial import scmutil from mercurial.i18n import _ from mercurial.node import bin cmdtable = {} command = cmdutil.command(cmdtable) testedwith = 'internal' def prompt(ui, prompt, default=None, rest=':'): if default: prompt += ' [%s]' % default return ui.prompt(prompt + rest, default) def introwanted(opts, number): '''is an introductory message apparently wanted?''' return number > 1 or opts.get('intro') or opts.get('desc') def makepatch(ui, repo, patchlines, opts, _charsets, idx, total, numbered, patchname=None): desc = [] node = None body = '' for line in patchlines: if line.startswith('#'): if line.startswith('# Node ID'): node = line.split()[-1] continue if line.startswith('diff -r') or line.startswith('diff --git'): break desc.append(line) if not patchname and not node: raise ValueError if opts.get('attach') and not opts.get('body'): body = ('\n'.join(desc[1:]).strip() or 'Patch subject is complete summary.') body += '\n\n\n' if opts.get('plain'): while patchlines and patchlines[0].startswith('# '): patchlines.pop(0) if patchlines: patchlines.pop(0) while patchlines and not patchlines[0].strip(): patchlines.pop(0) ds = patch.diffstat(patchlines, git=opts.get('git')) if opts.get('diffstat'): body += ds + '\n\n' addattachment = opts.get('attach') or opts.get('inline') if not addattachment or opts.get('body'): body += '\n'.join(patchlines) if addattachment: msg = email.MIMEMultipart.MIMEMultipart() if body: msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test'))) p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch', opts.get('test')) binnode = bin(node) # if node is mq patch, it will have the patch file's name as a tag if not patchname: patchtags = [t for t in repo.nodetags(binnode) if t.endswith('.patch') or t.endswith('.diff')] if patchtags: patchname = patchtags[0] elif total > 1: patchname = cmdutil.makefilename(repo, '%b-%n.patch', binnode, seqno=idx, total=total) else: patchname = cmdutil.makefilename(repo, '%b.patch', binnode) disposition = 'inline' if opts.get('attach'): disposition = 'attachment' p['Content-Disposition'] = disposition + '; filename=' + patchname msg.attach(p) else: msg = mail.mimetextpatch(body, display=opts.get('test')) flag = ' '.join(opts.get('flag')) if flag: flag = ' ' + flag subj = desc[0].strip().rstrip('. ') if not numbered: subj = '[PATCH%s] %s' % (flag, opts.get('subject') or subj) else: tlen = len(str(total)) subj = '[PATCH %0*d of %d%s] %s' % (tlen, idx, total, flag, subj) msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test')) msg['X-Mercurial-Node'] = node return msg, subj, ds emailopts = [ ('', 'body', None, _('send patches as inline message text (default)')), ('a', 'attach', None, _('send patches as attachments')), ('i', 'inline', None, _('send patches as inline attachments')), ('', 'bcc', [], _('email addresses of blind carbon copy recipients')), ('c', 'cc', [], _('email addresses of copy recipients')), ('', 'confirm', None, _('ask for confirmation before sending')), ('d', 'diffstat', None, _('add diffstat output to messages')), ('', 'date', '', _('use the given date as the sending date')), ('', 'desc', '', _('use the given file as the series description')), ('f', 'from', '', _('email address of sender')), ('n', 'test', None, _('print messages that would be sent')), ('m', 'mbox', '', _('write messages to mbox file instead of sending them')), ('', 'reply-to', [], _('email addresses replies should be sent to')), ('s', 'subject', '', _('subject of first message (intro or single patch)')), ('', 'in-reply-to', '', _('message identifier to reply to')), ('', 'flag', [], _('flags to add in subject prefixes')), ('t', 'to', [], _('email addresses of recipients'))] @command('email', [('g', 'git', None, _('use git extended diff format')), ('', 'plain', None, _('omit hg patch header')), ('o', 'outgoing', None, _('send changes not found in the target repository')), ('b', 'bundle', None, _('send changes not in target as a binary bundle')), ('', 'bundlename', 'bundle', _('name of the bundle attachment file'), _('NAME')), ('r', 'rev', [], _('a revision to send'), _('REV')), ('', 'force', None, _('run even when remote repository is unrelated ' '(with -b/--bundle)')), ('', 'base', [], _('a base changeset to specify instead of a destination ' '(with -b/--bundle)'), _('REV')), ('', 'intro', None, _('send an introduction email for a single patch')), ] + emailopts + commands.remoteopts, _('hg email [OPTION]... [DEST]...')) def patchbomb(ui, repo, *revs, **opts): '''send changesets by email By default, diffs are sent in the format generated by :hg:`export`, one per message. The series starts with a "[PATCH 0 of N]" introduction, which describes the series as a whole. Each patch email has a Subject line of "[PATCH M of N] ...", using the first line of the changeset description as the subject text. The message contains two or three parts. First, the changeset description. With the -d/--diffstat option, if the diffstat program is installed, the result of running diffstat on the patch is inserted. Finally, the patch itself, as generated by :hg:`export`. With the -d/--diffstat or --confirm options, you will be presented with a final summary of all messages and asked for confirmation before the messages are sent. By default the patch is included as text in the email body for easy reviewing. Using the -a/--attach option will instead create an attachment for the patch. With -i/--inline an inline attachment will be created. You can include a patch both as text in the email body and as a regular or an inline attachment by combining the -a/--attach or -i/--inline with the --body option. With -o/--outgoing, emails will be generated for patches not found in the destination repository (or only those which are ancestors of the specified revisions if any are provided) With -b/--bundle, changesets are selected as for --outgoing, but a single email containing a binary Mercurial bundle as an attachment will be sent. With -m/--mbox, instead of previewing each patchbomb message in a pager or sending the messages directly, it will create a UNIX mailbox file with the patch emails. This mailbox file can be previewed with any mail user agent which supports UNIX mbox files. With -n/--test, all steps will run, but mail will not be sent. You will be prompted for an email recipient address, a subject and an introductory message describing the patches of your patchbomb. Then when all is done, patchbomb messages are displayed. If the PAGER environment variable is set, your pager will be fired up once for each patchbomb message, so you can verify everything is alright. In case email sending fails, you will find a backup of your series introductory message in ``.hg/last-email.txt``. Examples:: hg email -r 3000 # send patch 3000 only hg email -r 3000 -r 3001 # send patches 3000 and 3001 hg email -r 3000:3005 # send patches 3000 through 3005 hg email 3000 # send patch 3000 (deprecated) hg email -o # send all patches not in default hg email -o DEST # send all patches not in DEST hg email -o -r 3000 # send all ancestors of 3000 not in default hg email -o -r 3000 DEST # send all ancestors of 3000 not in DEST hg email -b # send bundle of all patches not in default hg email -b DEST # send bundle of all patches not in DEST hg email -b -r 3000 # bundle of all ancestors of 3000 not in default hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in DEST hg email -o -m mbox && # generate an mbox file... mutt -R -f mbox # ... and view it with mutt hg email -o -m mbox && # generate an mbox file ... formail -s sendmail \\ # ... and use formail to send from the mbox -bm -t < mbox # ... using sendmail Before using this command, you will need to enable email in your hgrc. See the [email] section in hgrc(5) for details. ''' _charsets = mail._charsets(ui) bundle = opts.get('bundle') date = opts.get('date') mbox = opts.get('mbox') outgoing = opts.get('outgoing') rev = opts.get('rev') # internal option used by pbranches patches = opts.get('patches') def getoutgoing(dest, revs): '''Return the revisions present locally but not in dest''' url = ui.expandpath(dest or 'default-push', dest or 'default') url = hg.parseurl(url)[0] ui.status(_('comparing with %s\n') % util.hidepassword(url)) revs = [r for r in scmutil.revrange(repo, revs) if r >= 0] if not revs: revs = [len(repo) - 1] revs = repo.revs('outgoing(%s) and ::%ld', dest or '', revs) if not revs: ui.status(_("no changes found\n")) return [] return [str(r) for r in revs] def getpatches(revs): for r in scmutil.revrange(repo, revs): output = cStringIO.StringIO() cmdutil.export(repo, [r], fp=output, opts=patch.diffopts(ui, opts)) yield output.getvalue().split('\n') def getbundle(dest): tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-') tmpfn = os.path.join(tmpdir, 'bundle') try: commands.bundle(ui, repo, tmpfn, dest, **opts) fp = open(tmpfn, 'rb') data = fp.read() fp.close() return data finally: try: os.unlink(tmpfn) except OSError: pass os.rmdir(tmpdir) if not (opts.get('test') or mbox): # really sending mail.validateconfig(ui) if not (revs or rev or outgoing or bundle or patches): raise util.Abort(_('specify at least one changeset with -r or -o')) if outgoing and bundle: raise util.Abort(_("--outgoing mode always on with --bundle;" " do not re-specify --outgoing")) if outgoing or bundle: if len(revs) > 1: raise util.Abort(_("too many destinations")) dest = revs and revs[0] or None revs = [] if rev: if revs: raise util.Abort(_('use only one form to specify the revision')) revs = rev if outgoing: revs = getoutgoing(dest, rev) if bundle: opts['revs'] = revs # start if date: start_time = util.parsedate(date) else: start_time = util.makedate() def genmsgid(id): return '<%s.%s@%s>' % (id[:20], int(start_time[0]), socket.getfqdn()) def getdescription(body, sender): if opts.get('desc'): body = open(opts.get('desc')).read() else: ui.write(_('\nWrite the introductory message for the ' 'patch series.\n\n')) body = ui.edit(body, sender) # Save series description in case sendmail fails msgfile = repo.opener('last-email.txt', 'wb') msgfile.write(body) msgfile.close() return body def getpatchmsgs(patches, patchnames=None): msgs = [] ui.write(_('this patch series consists of %d patches.\n\n') % len(patches)) # build the intro message, or skip it if the user declines if introwanted(opts, len(patches)): msg = makeintro(patches) if msg: msgs.append(msg) # are we going to send more than one message? numbered = len(msgs) + len(patches) > 1 # now generate the actual patch messages name = None for i, p in enumerate(patches): if patchnames: name = patchnames[i] msg = makepatch(ui, repo, p, opts, _charsets, i + 1, len(patches), numbered, name) msgs.append(msg) return msgs def makeintro(patches): tlen = len(str(len(patches))) flag = opts.get('flag') or '' if flag: flag = ' ' + ' '.join(flag) prefix = '[PATCH %0*d of %d%s]' % (tlen, 0, len(patches), flag) subj = (opts.get('subject') or prompt(ui, '(optional) Subject: ', rest=prefix, default='')) if not subj: return None # skip intro if the user doesn't bother subj = prefix + ' ' + subj body = '' if opts.get('diffstat'): # generate a cumulative diffstat of the whole patch series diffstat = patch.diffstat(sum(patches, [])) body = '\n' + diffstat else: diffstat = None body = getdescription(body, sender) msg = mail.mimeencode(ui, body, _charsets, opts.get('test')) msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test')) return (msg, subj, diffstat) def getbundlemsgs(bundle): subj = (opts.get('subject') or prompt(ui, 'Subject:', 'A bundle for your repository')) body = getdescription('', sender) msg = email.MIMEMultipart.MIMEMultipart() if body: msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test'))) datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle') datapart.set_payload(bundle) bundlename = '%s.hg' % opts.get('bundlename', 'bundle') datapart.add_header('Content-Disposition', 'attachment', filename=bundlename) email.Encoders.encode_base64(datapart) msg.attach(datapart) msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test')) return [(msg, subj, None)] sender = (opts.get('from') or ui.config('email', 'from') or ui.config('patchbomb', 'from') or prompt(ui, 'From', ui.username())) if patches: msgs = getpatchmsgs(patches, opts.get('patchnames')) elif bundle: msgs = getbundlemsgs(getbundle(dest)) else: msgs = getpatchmsgs(list(getpatches(revs))) showaddrs = [] def getaddrs(header, ask=False, default=None): configkey = header.lower() opt = header.replace('-', '_').lower() addrs = opts.get(opt) if addrs: showaddrs.append('%s: %s' % (header, ', '.join(addrs))) return mail.addrlistencode(ui, addrs, _charsets, opts.get('test')) # not on the command line: fallback to config and then maybe ask addr = (ui.config('email', configkey) or ui.config('patchbomb', configkey) or '') if not addr and ask: addr = prompt(ui, header, default=default) if addr: showaddrs.append('%s: %s' % (header, addr)) return mail.addrlistencode(ui, [addr], _charsets, opts.get('test')) else: return default to = getaddrs('To', ask=True) if not to: # we can get here in non-interactive mode raise util.Abort(_('no recipient addresses provided')) cc = getaddrs('Cc', ask=True, default='') or [] bcc = getaddrs('Bcc') or [] replyto = getaddrs('Reply-To') if opts.get('diffstat') or opts.get('confirm'): ui.write(_('\nFinal summary:\n\n')) ui.write(('From: %s\n' % sender)) for addr in showaddrs: ui.write('%s\n' % addr) for m, subj, ds in msgs: ui.write(('Subject: %s\n' % subj)) if ds: ui.write(ds) ui.write('\n') if ui.promptchoice(_('are you sure you want to send (yn)?' '$$ &Yes $$ &No')): raise util.Abort(_('patchbomb canceled')) ui.write('\n') parent = opts.get('in_reply_to') or None # angle brackets may be omitted, they're not semantically part of the msg-id if parent is not None: if not parent.startswith('<'): parent = '<' + parent if not parent.endswith('>'): parent += '>' sender_addr = email.Utils.parseaddr(sender)[1] sender = mail.addressencode(ui, sender, _charsets, opts.get('test')) sendmail = None for i, (m, subj, ds) in enumerate(msgs): try: m['Message-Id'] = genmsgid(m['X-Mercurial-Node']) except TypeError: m['Message-Id'] = genmsgid('patchbomb') if parent: m['In-Reply-To'] = parent m['References'] = parent if not parent or 'X-Mercurial-Node' not in m: parent = m['Message-Id'] m['User-Agent'] = 'Mercurial-patchbomb/%s' % util.version() m['Date'] = email.Utils.formatdate(start_time[0], localtime=True) start_time = (start_time[0] + 1, start_time[1]) m['From'] = sender m['To'] = ', '.join(to) if cc: m['Cc'] = ', '.join(cc) if bcc: m['Bcc'] = ', '.join(bcc) if replyto: m['Reply-To'] = ', '.join(replyto) if opts.get('test'): ui.status(_('displaying '), subj, ' ...\n') ui.flush() if 'PAGER' in os.environ and not ui.plain(): fp = util.popen(os.environ['PAGER'], 'w') else: fp = ui generator = email.Generator.Generator(fp, mangle_from_=False) try: generator.flatten(m, 0) fp.write('\n') except IOError, inst: if inst.errno != errno.EPIPE: raise if fp is not ui: fp.close() else: if not sendmail: verifycert = ui.config('smtp', 'verifycert') if opts.get('insecure'): ui.setconfig('smtp', 'verifycert', 'loose') try: sendmail = mail.connect(ui, mbox=mbox) finally: ui.setconfig('smtp', 'verifycert', verifycert) ui.status(_('sending '), subj, ' ...\n') ui.progress(_('sending'), i, item=subj, total=len(msgs)) if not mbox: # Exim does not remove the Bcc field del m['Bcc'] fp = cStringIO.StringIO() generator = email.Generator.Generator(fp, mangle_from_=False) generator.flatten(m, 0) sendmail(sender_addr, to + bcc + cc, fp.getvalue()) ui.progress(_('writing'), None) ui.progress(_('sending'), None) mercurial-2.8.2/hgext/pager.py0000644000175000017500000001132612261160437020110 0ustar oxymoronoxymoron00000000000000# pager.py - display output using a pager # # Copyright 2008 David Soria Parra # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. # # To load the extension, add it to your configuration file: # # [extension] # pager = # # Run "hg help pager" to get info on configuration. '''browse command output with an external pager To set the pager that should be used, set the application variable:: [pager] pager = less -FRX If no pager is set, the pager extensions uses the environment variable $PAGER. If neither pager.pager, nor $PAGER is set, no pager is used. You can disable the pager for certain commands by adding them to the pager.ignore list:: [pager] ignore = version, help, update You can also enable the pager only for certain commands using pager.attend. Below is the default list of commands to be paged:: [pager] attend = annotate, cat, diff, export, glog, log, qdiff Setting pager.attend to an empty value will cause all commands to be paged. If pager.attend is present, pager.ignore will be ignored. To ignore global commands like :hg:`version` or :hg:`help`, you have to specify them in your user configuration file. The --pager=... option can also be used to control when the pager is used. Use a boolean value like yes, no, on, off, or use auto for normal behavior. ''' import atexit, sys, os, signal, subprocess, errno, shlex from mercurial import commands, dispatch, util, extensions, cmdutil from mercurial.i18n import _ testedwith = 'internal' def _pagerfork(ui, p): if not util.safehasattr(os, 'fork'): sys.stdout = util.popen(p, 'wb') if ui._isatty(sys.stderr): sys.stderr = sys.stdout return fdin, fdout = os.pipe() pid = os.fork() if pid == 0: os.close(fdin) os.dup2(fdout, sys.stdout.fileno()) if ui._isatty(sys.stderr): os.dup2(fdout, sys.stderr.fileno()) os.close(fdout) return os.dup2(fdin, sys.stdin.fileno()) os.close(fdin) os.close(fdout) try: os.execvp('/bin/sh', ['/bin/sh', '-c', p]) except OSError, e: if e.errno == errno.ENOENT: # no /bin/sh, try executing the pager directly args = shlex.split(p) os.execvp(args[0], args) else: raise def _pagersubprocess(ui, p): pager = subprocess.Popen(p, shell=True, bufsize=-1, close_fds=util.closefds, stdin=subprocess.PIPE, stdout=sys.stdout, stderr=sys.stderr) stdout = os.dup(sys.stdout.fileno()) stderr = os.dup(sys.stderr.fileno()) os.dup2(pager.stdin.fileno(), sys.stdout.fileno()) if ui._isatty(sys.stderr): os.dup2(pager.stdin.fileno(), sys.stderr.fileno()) @atexit.register def killpager(): if util.safehasattr(signal, "SIGINT"): signal.signal(signal.SIGINT, signal.SIG_IGN) pager.stdin.close() os.dup2(stdout, sys.stdout.fileno()) os.dup2(stderr, sys.stderr.fileno()) pager.wait() def _runpager(ui, p): # The subprocess module shipped with Python <= 2.4 is buggy (issue3533). # The compat version is buggy on Windows (issue3225), but has been shipping # with hg for a long time. Preserve existing functionality. if sys.version_info >= (2, 5): _pagersubprocess(ui, p) else: _pagerfork(ui, p) def uisetup(ui): if '--debugger' in sys.argv or not ui.formatted(): return def pagecmd(orig, ui, options, cmd, cmdfunc): p = ui.config("pager", "pager", os.environ.get("PAGER")) if p: attend = ui.configlist('pager', 'attend', attended) auto = options['pager'] == 'auto' always = util.parsebool(options['pager']) cmds, _ = cmdutil.findcmd(cmd, commands.table) ignore = ui.configlist('pager', 'ignore') for cmd in cmds: if (always or auto and (cmd in attend or (cmd not in ignore and not attend))): ui.setconfig('ui', 'formatted', ui.formatted()) ui.setconfig('ui', 'interactive', False) if util.safehasattr(signal, "SIGPIPE"): signal.signal(signal.SIGPIPE, signal.SIG_DFL) _runpager(ui, p) break return orig(ui, options, cmd, cmdfunc) extensions.wrapfunction(dispatch, '_runcommand', pagecmd) def extsetup(ui): commands.globalopts.append( ('', 'pager', 'auto', _("when to paginate (boolean, always, auto, or never)"), _('TYPE'))) attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff'] mercurial-2.8.2/hgext/bugzilla.py0000644000175000017500000010417112261160437020624 0ustar oxymoronoxymoron00000000000000# bugzilla.py - bugzilla integration for mercurial # # Copyright 2006 Vadim Gelfer # Copyright 2011-2 Jim Hague # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''hooks for integrating with the Bugzilla bug tracker This hook extension adds comments on bugs in Bugzilla when changesets that refer to bugs by Bugzilla ID are seen. The comment is formatted using the Mercurial template mechanism. The bug references can optionally include an update for Bugzilla of the hours spent working on the bug. Bugs can also be marked fixed. Three basic modes of access to Bugzilla are provided: 1. Access via the Bugzilla XMLRPC interface. Requires Bugzilla 3.4 or later. 2. Check data via the Bugzilla XMLRPC interface and submit bug change via email to Bugzilla email interface. Requires Bugzilla 3.4 or later. 3. Writing directly to the Bugzilla database. Only Bugzilla installations using MySQL are supported. Requires Python MySQLdb. Writing directly to the database is susceptible to schema changes, and relies on a Bugzilla contrib script to send out bug change notification emails. This script runs as the user running Mercurial, must be run on the host with the Bugzilla install, and requires permission to read Bugzilla configuration details and the necessary MySQL user and password to have full access rights to the Bugzilla database. For these reasons this access mode is now considered deprecated, and will not be updated for new Bugzilla versions going forward. Only adding comments is supported in this access mode. Access via XMLRPC needs a Bugzilla username and password to be specified in the configuration. Comments are added under that username. Since the configuration must be readable by all Mercurial users, it is recommended that the rights of that user are restricted in Bugzilla to the minimum necessary to add comments. Marking bugs fixed requires Bugzilla 4.0 and later. Access via XMLRPC/email uses XMLRPC to query Bugzilla, but sends email to the Bugzilla email interface to submit comments to bugs. The From: address in the email is set to the email address of the Mercurial user, so the comment appears to come from the Mercurial user. In the event that the Mercurial user email is not recognized by Bugzilla as a Bugzilla user, the email associated with the Bugzilla username used to log into Bugzilla is used instead as the source of the comment. Marking bugs fixed works on all supported Bugzilla versions. Configuration items common to all access modes: bugzilla.version The access type to use. Values recognized are: :``xmlrpc``: Bugzilla XMLRPC interface. :``xmlrpc+email``: Bugzilla XMLRPC and email interfaces. :``3.0``: MySQL access, Bugzilla 3.0 and later. :``2.18``: MySQL access, Bugzilla 2.18 and up to but not including 3.0. :``2.16``: MySQL access, Bugzilla 2.16 and up to but not including 2.18. bugzilla.regexp Regular expression to match bug IDs for update in changeset commit message. It must contain one "()" named group ```` containing the bug IDs separated by non-digit characters. It may also contain a named group ```` with a floating-point number giving the hours worked on the bug. If no named groups are present, the first "()" group is assumed to contain the bug IDs, and work time is not updated. The default expression matches ``Bug 1234``, ``Bug no. 1234``, ``Bug number 1234``, ``Bugs 1234,5678``, ``Bug 1234 and 5678`` and variations thereof, followed by an hours number prefixed by ``h`` or ``hours``, e.g. ``hours 1.5``. Matching is case insensitive. bugzilla.fixregexp Regular expression to match bug IDs for marking fixed in changeset commit message. This must contain a "()" named group ``` containing the bug IDs separated by non-digit characters. It may also contain a named group ```` with a floating-point number giving the hours worked on the bug. If no named groups are present, the first "()" group is assumed to contain the bug IDs, and work time is not updated. The default expression matches ``Fixes 1234``, ``Fixes bug 1234``, ``Fixes bugs 1234,5678``, ``Fixes 1234 and 5678`` and variations thereof, followed by an hours number prefixed by ``h`` or ``hours``, e.g. ``hours 1.5``. Matching is case insensitive. bugzilla.fixstatus The status to set a bug to when marking fixed. Default ``RESOLVED``. bugzilla.fixresolution The resolution to set a bug to when marking fixed. Default ``FIXED``. bugzilla.style The style file to use when formatting comments. bugzilla.template Template to use when formatting comments. Overrides style if specified. In addition to the usual Mercurial keywords, the extension specifies: :``{bug}``: The Bugzilla bug ID. :``{root}``: The full pathname of the Mercurial repository. :``{webroot}``: Stripped pathname of the Mercurial repository. :``{hgweb}``: Base URL for browsing Mercurial repositories. Default ``changeset {node|short} in repo {root} refers to bug {bug}.\\ndetails:\\n\\t{desc|tabindent}`` bugzilla.strip The number of path separator characters to strip from the front of the Mercurial repository path (``{root}`` in templates) to produce ``{webroot}``. For example, a repository with ``{root}`` ``/var/local/my-project`` with a strip of 2 gives a value for ``{webroot}`` of ``my-project``. Default 0. web.baseurl Base URL for browsing Mercurial repositories. Referenced from templates as ``{hgweb}``. Configuration items common to XMLRPC+email and MySQL access modes: bugzilla.usermap Path of file containing Mercurial committer email to Bugzilla user email mappings. If specified, the file should contain one mapping per line:: committer = Bugzilla user See also the ``[usermap]`` section. The ``[usermap]`` section is used to specify mappings of Mercurial committer email to Bugzilla user email. See also ``bugzilla.usermap``. Contains entries of the form ``committer = Bugzilla user``. XMLRPC access mode configuration: bugzilla.bzurl The base URL for the Bugzilla installation. Default ``http://localhost/bugzilla``. bugzilla.user The username to use to log into Bugzilla via XMLRPC. Default ``bugs``. bugzilla.password The password for Bugzilla login. XMLRPC+email access mode uses the XMLRPC access mode configuration items, and also: bugzilla.bzemail The Bugzilla email address. In addition, the Mercurial email settings must be configured. See the documentation in hgrc(5), sections ``[email]`` and ``[smtp]``. MySQL access mode configuration: bugzilla.host Hostname of the MySQL server holding the Bugzilla database. Default ``localhost``. bugzilla.db Name of the Bugzilla database in MySQL. Default ``bugs``. bugzilla.user Username to use to access MySQL server. Default ``bugs``. bugzilla.password Password to use to access MySQL server. bugzilla.timeout Database connection timeout (seconds). Default 5. bugzilla.bzuser Fallback Bugzilla user name to record comments with, if changeset committer cannot be found as a Bugzilla user. bugzilla.bzdir Bugzilla install directory. Used by default notify. Default ``/var/www/html/bugzilla``. bugzilla.notify The command to run to get Bugzilla to send bug change notification emails. Substitutes from a map with 3 keys, ``bzdir``, ``id`` (bug id) and ``user`` (committer bugzilla email). Default depends on version; from 2.18 it is "cd %(bzdir)s && perl -T contrib/sendbugmail.pl %(id)s %(user)s". Activating the extension:: [extensions] bugzilla = [hooks] # run bugzilla hook on every change pulled or pushed in here incoming.bugzilla = python:hgext.bugzilla.hook Example configurations: XMLRPC example configuration. This uses the Bugzilla at ``http://my-project.org/bugzilla``, logging in as user ``bugmail@my-project.org`` with password ``plugh``. It is used with a collection of Mercurial repositories in ``/var/local/hg/repos/``, with a web interface at ``http://my-project.org/hg``. :: [bugzilla] bzurl=http://my-project.org/bugzilla user=bugmail@my-project.org password=plugh version=xmlrpc template=Changeset {node|short} in {root|basename}. {hgweb}/{webroot}/rev/{node|short}\\n {desc}\\n strip=5 [web] baseurl=http://my-project.org/hg XMLRPC+email example configuration. This uses the Bugzilla at ``http://my-project.org/bugzilla``, logging in as user ``bugmail@my-project.org`` with password ``plugh``. It is used with a collection of Mercurial repositories in ``/var/local/hg/repos/``, with a web interface at ``http://my-project.org/hg``. Bug comments are sent to the Bugzilla email address ``bugzilla@my-project.org``. :: [bugzilla] bzurl=http://my-project.org/bugzilla user=bugmail@my-project.org password=plugh version=xmlrpc bzemail=bugzilla@my-project.org template=Changeset {node|short} in {root|basename}. {hgweb}/{webroot}/rev/{node|short}\\n {desc}\\n strip=5 [web] baseurl=http://my-project.org/hg [usermap] user@emaildomain.com=user.name@bugzilladomain.com MySQL example configuration. This has a local Bugzilla 3.2 installation in ``/opt/bugzilla-3.2``. The MySQL database is on ``localhost``, the Bugzilla database name is ``bugs`` and MySQL is accessed with MySQL username ``bugs`` password ``XYZZY``. It is used with a collection of Mercurial repositories in ``/var/local/hg/repos/``, with a web interface at ``http://my-project.org/hg``. :: [bugzilla] host=localhost password=XYZZY version=3.0 bzuser=unknown@domain.com bzdir=/opt/bugzilla-3.2 template=Changeset {node|short} in {root|basename}. {hgweb}/{webroot}/rev/{node|short}\\n {desc}\\n strip=5 [web] baseurl=http://my-project.org/hg [usermap] user@emaildomain.com=user.name@bugzilladomain.com All the above add a comment to the Bugzilla bug record of the form:: Changeset 3b16791d6642 in repository-name. http://my-project.org/hg/repository-name/rev/3b16791d6642 Changeset commit comment. Bug 1234. ''' from mercurial.i18n import _ from mercurial.node import short from mercurial import cmdutil, mail, templater, util import re, time, urlparse, xmlrpclib testedwith = 'internal' class bzaccess(object): '''Base class for access to Bugzilla.''' def __init__(self, ui): self.ui = ui usermap = self.ui.config('bugzilla', 'usermap') if usermap: self.ui.readconfig(usermap, sections=['usermap']) def map_committer(self, user): '''map name of committer to Bugzilla user name.''' for committer, bzuser in self.ui.configitems('usermap'): if committer.lower() == user.lower(): return bzuser return user # Methods to be implemented by access classes. # # 'bugs' is a dict keyed on bug id, where values are a dict holding # updates to bug state. Recognized dict keys are: # # 'hours': Value, float containing work hours to be updated. # 'fix': If key present, bug is to be marked fixed. Value ignored. def filter_real_bug_ids(self, bugs): '''remove bug IDs that do not exist in Bugzilla from bugs.''' pass def filter_cset_known_bug_ids(self, node, bugs): '''remove bug IDs where node occurs in comment text from bugs.''' pass def updatebug(self, bugid, newstate, text, committer): '''update the specified bug. Add comment text and set new states. If possible add the comment as being from the committer of the changeset. Otherwise use the default Bugzilla user. ''' pass def notify(self, bugs, committer): '''Force sending of Bugzilla notification emails. Only required if the access method does not trigger notification emails automatically. ''' pass # Bugzilla via direct access to MySQL database. class bzmysql(bzaccess): '''Support for direct MySQL access to Bugzilla. The earliest Bugzilla version this is tested with is version 2.16. If your Bugzilla is version 3.4 or above, you are strongly recommended to use the XMLRPC access method instead. ''' @staticmethod def sql_buglist(ids): '''return SQL-friendly list of bug ids''' return '(' + ','.join(map(str, ids)) + ')' _MySQLdb = None def __init__(self, ui): try: import MySQLdb as mysql bzmysql._MySQLdb = mysql except ImportError, err: raise util.Abort(_('python mysql support not available: %s') % err) bzaccess.__init__(self, ui) host = self.ui.config('bugzilla', 'host', 'localhost') user = self.ui.config('bugzilla', 'user', 'bugs') passwd = self.ui.config('bugzilla', 'password') db = self.ui.config('bugzilla', 'db', 'bugs') timeout = int(self.ui.config('bugzilla', 'timeout', 5)) self.ui.note(_('connecting to %s:%s as %s, password %s\n') % (host, db, user, '*' * len(passwd))) self.conn = bzmysql._MySQLdb.connect(host=host, user=user, passwd=passwd, db=db, connect_timeout=timeout) self.cursor = self.conn.cursor() self.longdesc_id = self.get_longdesc_id() self.user_ids = {} self.default_notify = "cd %(bzdir)s && ./processmail %(id)s %(user)s" def run(self, *args, **kwargs): '''run a query.''' self.ui.note(_('query: %s %s\n') % (args, kwargs)) try: self.cursor.execute(*args, **kwargs) except bzmysql._MySQLdb.MySQLError: self.ui.note(_('failed query: %s %s\n') % (args, kwargs)) raise def get_longdesc_id(self): '''get identity of longdesc field''' self.run('select fieldid from fielddefs where name = "longdesc"') ids = self.cursor.fetchall() if len(ids) != 1: raise util.Abort(_('unknown database schema')) return ids[0][0] def filter_real_bug_ids(self, bugs): '''filter not-existing bugs from set.''' self.run('select bug_id from bugs where bug_id in %s' % bzmysql.sql_buglist(bugs.keys())) existing = [id for (id,) in self.cursor.fetchall()] for id in bugs.keys(): if id not in existing: self.ui.status(_('bug %d does not exist\n') % id) del bugs[id] def filter_cset_known_bug_ids(self, node, bugs): '''filter bug ids that already refer to this changeset from set.''' self.run('''select bug_id from longdescs where bug_id in %s and thetext like "%%%s%%"''' % (bzmysql.sql_buglist(bugs.keys()), short(node))) for (id,) in self.cursor.fetchall(): self.ui.status(_('bug %d already knows about changeset %s\n') % (id, short(node))) del bugs[id] def notify(self, bugs, committer): '''tell bugzilla to send mail.''' self.ui.status(_('telling bugzilla to send mail:\n')) (user, userid) = self.get_bugzilla_user(committer) for id in bugs.keys(): self.ui.status(_(' bug %s\n') % id) cmdfmt = self.ui.config('bugzilla', 'notify', self.default_notify) bzdir = self.ui.config('bugzilla', 'bzdir', '/var/www/html/bugzilla') try: # Backwards-compatible with old notify string, which # took one string. This will throw with a new format # string. cmd = cmdfmt % id except TypeError: cmd = cmdfmt % {'bzdir': bzdir, 'id': id, 'user': user} self.ui.note(_('running notify command %s\n') % cmd) fp = util.popen('(%s) 2>&1' % cmd) out = fp.read() ret = fp.close() if ret: self.ui.warn(out) raise util.Abort(_('bugzilla notify command %s') % util.explainexit(ret)[0]) self.ui.status(_('done\n')) def get_user_id(self, user): '''look up numeric bugzilla user id.''' try: return self.user_ids[user] except KeyError: try: userid = int(user) except ValueError: self.ui.note(_('looking up user %s\n') % user) self.run('''select userid from profiles where login_name like %s''', user) all = self.cursor.fetchall() if len(all) != 1: raise KeyError(user) userid = int(all[0][0]) self.user_ids[user] = userid return userid def get_bugzilla_user(self, committer): '''See if committer is a registered bugzilla user. Return bugzilla username and userid if so. If not, return default bugzilla username and userid.''' user = self.map_committer(committer) try: userid = self.get_user_id(user) except KeyError: try: defaultuser = self.ui.config('bugzilla', 'bzuser') if not defaultuser: raise util.Abort(_('cannot find bugzilla user id for %s') % user) userid = self.get_user_id(defaultuser) user = defaultuser except KeyError: raise util.Abort(_('cannot find bugzilla user id for %s or %s') % (user, defaultuser)) return (user, userid) def updatebug(self, bugid, newstate, text, committer): '''update bug state with comment text. Try adding comment as committer of changeset, otherwise as default bugzilla user.''' if len(newstate) > 0: self.ui.warn(_("Bugzilla/MySQL cannot update bug state\n")) (user, userid) = self.get_bugzilla_user(committer) now = time.strftime('%Y-%m-%d %H:%M:%S') self.run('''insert into longdescs (bug_id, who, bug_when, thetext) values (%s, %s, %s, %s)''', (bugid, userid, now, text)) self.run('''insert into bugs_activity (bug_id, who, bug_when, fieldid) values (%s, %s, %s, %s)''', (bugid, userid, now, self.longdesc_id)) self.conn.commit() class bzmysql_2_18(bzmysql): '''support for bugzilla 2.18 series.''' def __init__(self, ui): bzmysql.__init__(self, ui) self.default_notify = \ "cd %(bzdir)s && perl -T contrib/sendbugmail.pl %(id)s %(user)s" class bzmysql_3_0(bzmysql_2_18): '''support for bugzilla 3.0 series.''' def __init__(self, ui): bzmysql_2_18.__init__(self, ui) def get_longdesc_id(self): '''get identity of longdesc field''' self.run('select id from fielddefs where name = "longdesc"') ids = self.cursor.fetchall() if len(ids) != 1: raise util.Abort(_('unknown database schema')) return ids[0][0] # Bugzilla via XMLRPC interface. class cookietransportrequest(object): """A Transport request method that retains cookies over its lifetime. The regular xmlrpclib transports ignore cookies. Which causes a bit of a problem when you need a cookie-based login, as with the Bugzilla XMLRPC interface. So this is a helper for defining a Transport which looks for cookies being set in responses and saves them to add to all future requests. """ # Inspiration drawn from # http://blog.godson.in/2010/09/how-to-make-python-xmlrpclib-client.html # http://www.itkovian.net/base/transport-class-for-pythons-xml-rpc-lib/ cookies = [] def send_cookies(self, connection): if self.cookies: for cookie in self.cookies: connection.putheader("Cookie", cookie) def request(self, host, handler, request_body, verbose=0): self.verbose = verbose self.accept_gzip_encoding = False # issue XML-RPC request h = self.make_connection(host) if verbose: h.set_debuglevel(1) self.send_request(h, handler, request_body) self.send_host(h, host) self.send_cookies(h) self.send_user_agent(h) self.send_content(h, request_body) # Deal with differences between Python 2.4-2.6 and 2.7. # In the former h is a HTTP(S). In the latter it's a # HTTP(S)Connection. Luckily, the 2.4-2.6 implementation of # HTTP(S) has an underlying HTTP(S)Connection, so extract # that and use it. try: response = h.getresponse() except AttributeError: response = h._conn.getresponse() # Add any cookie definitions to our list. for header in response.msg.getallmatchingheaders("Set-Cookie"): val = header.split(": ", 1)[1] cookie = val.split(";", 1)[0] self.cookies.append(cookie) if response.status != 200: raise xmlrpclib.ProtocolError(host + handler, response.status, response.reason, response.msg.headers) payload = response.read() parser, unmarshaller = self.getparser() parser.feed(payload) parser.close() return unmarshaller.close() # The explicit calls to the underlying xmlrpclib __init__() methods are # necessary. The xmlrpclib.Transport classes are old-style classes, and # it turns out their __init__() doesn't get called when doing multiple # inheritance with a new-style class. class cookietransport(cookietransportrequest, xmlrpclib.Transport): def __init__(self, use_datetime=0): if util.safehasattr(xmlrpclib.Transport, "__init__"): xmlrpclib.Transport.__init__(self, use_datetime) class cookiesafetransport(cookietransportrequest, xmlrpclib.SafeTransport): def __init__(self, use_datetime=0): if util.safehasattr(xmlrpclib.Transport, "__init__"): xmlrpclib.SafeTransport.__init__(self, use_datetime) class bzxmlrpc(bzaccess): """Support for access to Bugzilla via the Bugzilla XMLRPC API. Requires a minimum Bugzilla version 3.4. """ def __init__(self, ui): bzaccess.__init__(self, ui) bzweb = self.ui.config('bugzilla', 'bzurl', 'http://localhost/bugzilla/') bzweb = bzweb.rstrip("/") + "/xmlrpc.cgi" user = self.ui.config('bugzilla', 'user', 'bugs') passwd = self.ui.config('bugzilla', 'password') self.fixstatus = self.ui.config('bugzilla', 'fixstatus', 'RESOLVED') self.fixresolution = self.ui.config('bugzilla', 'fixresolution', 'FIXED') self.bzproxy = xmlrpclib.ServerProxy(bzweb, self.transport(bzweb)) ver = self.bzproxy.Bugzilla.version()['version'].split('.') self.bzvermajor = int(ver[0]) self.bzverminor = int(ver[1]) self.bzproxy.User.login(dict(login=user, password=passwd)) def transport(self, uri): if urlparse.urlparse(uri, "http")[0] == "https": return cookiesafetransport() else: return cookietransport() def get_bug_comments(self, id): """Return a string with all comment text for a bug.""" c = self.bzproxy.Bug.comments(dict(ids=[id], include_fields=['text'])) return ''.join([t['text'] for t in c['bugs'][str(id)]['comments']]) def filter_real_bug_ids(self, bugs): probe = self.bzproxy.Bug.get(dict(ids=sorted(bugs.keys()), include_fields=[], permissive=True)) for badbug in probe['faults']: id = badbug['id'] self.ui.status(_('bug %d does not exist\n') % id) del bugs[id] def filter_cset_known_bug_ids(self, node, bugs): for id in sorted(bugs.keys()): if self.get_bug_comments(id).find(short(node)) != -1: self.ui.status(_('bug %d already knows about changeset %s\n') % (id, short(node))) del bugs[id] def updatebug(self, bugid, newstate, text, committer): args = {} if 'hours' in newstate: args['work_time'] = newstate['hours'] if self.bzvermajor >= 4: args['ids'] = [bugid] args['comment'] = {'body' : text} if 'fix' in newstate: args['status'] = self.fixstatus args['resolution'] = self.fixresolution self.bzproxy.Bug.update(args) else: if 'fix' in newstate: self.ui.warn(_("Bugzilla/XMLRPC needs Bugzilla 4.0 or later " "to mark bugs fixed\n")) args['id'] = bugid args['comment'] = text self.bzproxy.Bug.add_comment(args) class bzxmlrpcemail(bzxmlrpc): """Read data from Bugzilla via XMLRPC, send updates via email. Advantages of sending updates via email: 1. Comments can be added as any user, not just logged in user. 2. Bug statuses or other fields not accessible via XMLRPC can potentially be updated. There is no XMLRPC function to change bug status before Bugzilla 4.0, so bugs cannot be marked fixed via XMLRPC before Bugzilla 4.0. But bugs can be marked fixed via email from 3.4 onwards. """ # The email interface changes subtly between 3.4 and 3.6. In 3.4, # in-email fields are specified as '@ = '. In # 3.6 this becomes '@ '. And fieldname @bug_id # in 3.4 becomes @id in 3.6. 3.6 and 4.0 both maintain backwards # compatibility, but rather than rely on this use the new format for # 4.0 onwards. def __init__(self, ui): bzxmlrpc.__init__(self, ui) self.bzemail = self.ui.config('bugzilla', 'bzemail') if not self.bzemail: raise util.Abort(_("configuration 'bzemail' missing")) mail.validateconfig(self.ui) def makecommandline(self, fieldname, value): if self.bzvermajor >= 4: return "@%s %s" % (fieldname, str(value)) else: if fieldname == "id": fieldname = "bug_id" return "@%s = %s" % (fieldname, str(value)) def send_bug_modify_email(self, bugid, commands, comment, committer): '''send modification message to Bugzilla bug via email. The message format is documented in the Bugzilla email_in.pl specification. commands is a list of command lines, comment is the comment text. To stop users from crafting commit comments with Bugzilla commands, specify the bug ID via the message body, rather than the subject line, and leave a blank line after it. ''' user = self.map_committer(committer) matches = self.bzproxy.User.get(dict(match=[user])) if not matches['users']: user = self.ui.config('bugzilla', 'user', 'bugs') matches = self.bzproxy.User.get(dict(match=[user])) if not matches['users']: raise util.Abort(_("default bugzilla user %s email not found") % user) user = matches['users'][0]['email'] commands.append(self.makecommandline("id", bugid)) text = "\n".join(commands) + "\n\n" + comment _charsets = mail._charsets(self.ui) user = mail.addressencode(self.ui, user, _charsets) bzemail = mail.addressencode(self.ui, self.bzemail, _charsets) msg = mail.mimeencode(self.ui, text, _charsets) msg['From'] = user msg['To'] = bzemail msg['Subject'] = mail.headencode(self.ui, "Bug modification", _charsets) sendmail = mail.connect(self.ui) sendmail(user, bzemail, msg.as_string()) def updatebug(self, bugid, newstate, text, committer): cmds = [] if 'hours' in newstate: cmds.append(self.makecommandline("work_time", newstate['hours'])) if 'fix' in newstate: cmds.append(self.makecommandline("bug_status", self.fixstatus)) cmds.append(self.makecommandline("resolution", self.fixresolution)) self.send_bug_modify_email(bugid, cmds, text, committer) class bugzilla(object): # supported versions of bugzilla. different versions have # different schemas. _versions = { '2.16': bzmysql, '2.18': bzmysql_2_18, '3.0': bzmysql_3_0, 'xmlrpc': bzxmlrpc, 'xmlrpc+email': bzxmlrpcemail } _default_bug_re = (r'bugs?\s*,?\s*(?:#|nos?\.?|num(?:ber)?s?)?\s*' r'(?P(?:\d+\s*(?:,?\s*(?:and)?)?\s*)+)' r'\.?\s*(?:h(?:ours?)?\s*(?P\d*(?:\.\d+)?))?') _default_fix_re = (r'fix(?:es)?\s*(?:bugs?\s*)?,?\s*' r'(?:nos?\.?|num(?:ber)?s?)?\s*' r'(?P(?:#?\d+\s*(?:,?\s*(?:and)?)?\s*)+)' r'\.?\s*(?:h(?:ours?)?\s*(?P\d*(?:\.\d+)?))?') _bz = None def __init__(self, ui, repo): self.ui = ui self.repo = repo def bz(self): '''return object that knows how to talk to bugzilla version in use.''' if bugzilla._bz is None: bzversion = self.ui.config('bugzilla', 'version') try: bzclass = bugzilla._versions[bzversion] except KeyError: raise util.Abort(_('bugzilla version %s not supported') % bzversion) bugzilla._bz = bzclass(self.ui) return bugzilla._bz def __getattr__(self, key): return getattr(self.bz(), key) _bug_re = None _fix_re = None _split_re = None def find_bugs(self, ctx): '''return bugs dictionary created from commit comment. Extract bug info from changeset comments. Filter out any that are not known to Bugzilla, and any that already have a reference to the given changeset in their comments. ''' if bugzilla._bug_re is None: bugzilla._bug_re = re.compile( self.ui.config('bugzilla', 'regexp', bugzilla._default_bug_re), re.IGNORECASE) bugzilla._fix_re = re.compile( self.ui.config('bugzilla', 'fixregexp', bugzilla._default_fix_re), re.IGNORECASE) bugzilla._split_re = re.compile(r'\D+') start = 0 hours = 0.0 bugs = {} bugmatch = bugzilla._bug_re.search(ctx.description(), start) fixmatch = bugzilla._fix_re.search(ctx.description(), start) while True: bugattribs = {} if not bugmatch and not fixmatch: break if not bugmatch: m = fixmatch elif not fixmatch: m = bugmatch else: if bugmatch.start() < fixmatch.start(): m = bugmatch else: m = fixmatch start = m.end() if m is bugmatch: bugmatch = bugzilla._bug_re.search(ctx.description(), start) if 'fix' in bugattribs: del bugattribs['fix'] else: fixmatch = bugzilla._fix_re.search(ctx.description(), start) bugattribs['fix'] = None try: ids = m.group('ids') except IndexError: ids = m.group(1) try: hours = float(m.group('hours')) bugattribs['hours'] = hours except IndexError: pass except TypeError: pass except ValueError: self.ui.status(_("%s: invalid hours\n") % m.group('hours')) for id in bugzilla._split_re.split(ids): if not id: continue bugs[int(id)] = bugattribs if bugs: self.filter_real_bug_ids(bugs) if bugs: self.filter_cset_known_bug_ids(ctx.node(), bugs) return bugs def update(self, bugid, newstate, ctx): '''update bugzilla bug with reference to changeset.''' def webroot(root): '''strip leading prefix of repo root and turn into url-safe path.''' count = int(self.ui.config('bugzilla', 'strip', 0)) root = util.pconvert(root) while count > 0: c = root.find('/') if c == -1: break root = root[c + 1:] count -= 1 return root mapfile = self.ui.config('bugzilla', 'style') tmpl = self.ui.config('bugzilla', 'template') t = cmdutil.changeset_templater(self.ui, self.repo, False, None, mapfile, False) if not mapfile and not tmpl: tmpl = _('changeset {node|short} in repo {root} refers ' 'to bug {bug}.\ndetails:\n\t{desc|tabindent}') if tmpl: tmpl = templater.parsestring(tmpl, quoted=False) t.use_template(tmpl) self.ui.pushbuffer() t.show(ctx, changes=ctx.changeset(), bug=str(bugid), hgweb=self.ui.config('web', 'baseurl'), root=self.repo.root, webroot=webroot(self.repo.root)) data = self.ui.popbuffer() self.updatebug(bugid, newstate, data, util.email(ctx.user())) def hook(ui, repo, hooktype, node=None, **kwargs): '''add comment to bugzilla for each changeset that refers to a bugzilla bug id. only add a comment once per bug, so same change seen multiple times does not fill bug with duplicate data.''' if node is None: raise util.Abort(_('hook type %s does not pass a changeset id') % hooktype) try: bz = bugzilla(ui, repo) ctx = repo[node] bugs = bz.find_bugs(ctx) if bugs: for bug in bugs: bz.update(bug, bugs[bug], ctx) bz.notify(bugs, util.email(ctx.user())) except Exception, e: raise util.Abort(_('Bugzilla error: %s') % e) mercurial-2.8.2/hgext/eol.py0000644000175000017500000003173312261160437017575 0ustar oxymoronoxymoron00000000000000"""automatically manage newlines in repository files This extension allows you to manage the type of line endings (CRLF or LF) that are used in the repository and in the local working directory. That way you can get CRLF line endings on Windows and LF on Unix/Mac, thereby letting everybody use their OS native line endings. The extension reads its configuration from a versioned ``.hgeol`` configuration file found in the root of the working copy. The ``.hgeol`` file use the same syntax as all other Mercurial configuration files. It uses two sections, ``[patterns]`` and ``[repository]``. The ``[patterns]`` section specifies how line endings should be converted between the working copy and the repository. The format is specified by a file pattern. The first match is used, so put more specific patterns first. The available line endings are ``LF``, ``CRLF``, and ``BIN``. Files with the declared format of ``CRLF`` or ``LF`` are always checked out and stored in the repository in that format and files declared to be binary (``BIN``) are left unchanged. Additionally, ``native`` is an alias for checking out in the platform's default line ending: ``LF`` on Unix (including Mac OS X) and ``CRLF`` on Windows. Note that ``BIN`` (do nothing to line endings) is Mercurial's default behaviour; it is only needed if you need to override a later, more general pattern. The optional ``[repository]`` section specifies the line endings to use for files stored in the repository. It has a single setting, ``native``, which determines the storage line endings for files declared as ``native`` in the ``[patterns]`` section. It can be set to ``LF`` or ``CRLF``. The default is ``LF``. For example, this means that on Windows, files configured as ``native`` (``CRLF`` by default) will be converted to ``LF`` when stored in the repository. Files declared as ``LF``, ``CRLF``, or ``BIN`` in the ``[patterns]`` section are always stored as-is in the repository. Example versioned ``.hgeol`` file:: [patterns] **.py = native **.vcproj = CRLF **.txt = native Makefile = LF **.jpg = BIN [repository] native = LF .. note:: The rules will first apply when files are touched in the working copy, e.g. by updating to null and back to tip to touch all files. The extension uses an optional ``[eol]`` section read from both the normal Mercurial configuration files and the ``.hgeol`` file, with the latter overriding the former. You can use that section to control the overall behavior. There are three settings: - ``eol.native`` (default ``os.linesep``) can be set to ``LF`` or ``CRLF`` to override the default interpretation of ``native`` for checkout. This can be used with :hg:`archive` on Unix, say, to generate an archive where files have line endings for Windows. - ``eol.only-consistent`` (default True) can be set to False to make the extension convert files with inconsistent EOLs. Inconsistent means that there is both ``CRLF`` and ``LF`` present in the file. Such files are normally not touched under the assumption that they have mixed EOLs on purpose. - ``eol.fix-trailing-newline`` (default False) can be set to True to ensure that converted files end with a EOL character (either ``\\n`` or ``\\r\\n`` as per the configured patterns). The extension provides ``cleverencode:`` and ``cleverdecode:`` filters like the deprecated win32text extension does. This means that you can disable win32text and enable eol and your filters will still work. You only need to these filters until you have prepared a ``.hgeol`` file. The ``win32text.forbid*`` hooks provided by the win32text extension have been unified into a single hook named ``eol.checkheadshook``. The hook will lookup the expected line endings from the ``.hgeol`` file, which means you must migrate to a ``.hgeol`` file first before using the hook. ``eol.checkheadshook`` only checks heads, intermediate invalid revisions will be pushed. To forbid them completely, use the ``eol.checkallhook`` hook. These hooks are best used as ``pretxnchangegroup`` hooks. See :hg:`help patterns` for more information about the glob patterns used. """ from mercurial.i18n import _ from mercurial import util, config, extensions, match, error import re, os testedwith = 'internal' # Matches a lone LF, i.e., one that is not part of CRLF. singlelf = re.compile('(^|[^\r])\n') # Matches a single EOL which can either be a CRLF where repeated CR # are removed or a LF. We do not care about old Macintosh files, so a # stray CR is an error. eolre = re.compile('\r*\n') def inconsistenteol(data): return '\r\n' in data and singlelf.search(data) def tolf(s, params, ui, **kwargs): """Filter to convert to LF EOLs.""" if util.binary(s): return s if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s): return s if (ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n'): s = s + '\n' return eolre.sub('\n', s) def tocrlf(s, params, ui, **kwargs): """Filter to convert to CRLF EOLs.""" if util.binary(s): return s if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s): return s if (ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n'): s = s + '\n' return eolre.sub('\r\n', s) def isbinary(s, params): """Filter to do nothing with the file.""" return s filters = { 'to-lf': tolf, 'to-crlf': tocrlf, 'is-binary': isbinary, # The following provide backwards compatibility with win32text 'cleverencode:': tolf, 'cleverdecode:': tocrlf } class eolfile(object): def __init__(self, ui, root, data): self._decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'} self._encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'} self.cfg = config.config() # Our files should not be touched. The pattern must be # inserted first override a '** = native' pattern. self.cfg.set('patterns', '.hg*', 'BIN') # We can then parse the user's patterns. self.cfg.parse('.hgeol', data) isrepolf = self.cfg.get('repository', 'native') != 'CRLF' self._encode['NATIVE'] = isrepolf and 'to-lf' or 'to-crlf' iswdlf = ui.config('eol', 'native', os.linesep) in ('LF', '\n') self._decode['NATIVE'] = iswdlf and 'to-lf' or 'to-crlf' include = [] exclude = [] for pattern, style in self.cfg.items('patterns'): key = style.upper() if key == 'BIN': exclude.append(pattern) else: include.append(pattern) # This will match the files for which we need to care # about inconsistent newlines. self.match = match.match(root, '', [], include, exclude) def copytoui(self, ui): for pattern, style in self.cfg.items('patterns'): key = style.upper() try: ui.setconfig('decode', pattern, self._decode[key]) ui.setconfig('encode', pattern, self._encode[key]) except KeyError: ui.warn(_("ignoring unknown EOL style '%s' from %s\n") % (style, self.cfg.source('patterns', pattern))) # eol.only-consistent can be specified in ~/.hgrc or .hgeol for k, v in self.cfg.items('eol'): ui.setconfig('eol', k, v) def checkrev(self, repo, ctx, files): failed = [] for f in (files or ctx.files()): if f not in ctx: continue for pattern, style in self.cfg.items('patterns'): if not match.match(repo.root, '', [pattern])(f): continue target = self._encode[style.upper()] data = ctx[f].data() if (target == "to-lf" and "\r\n" in data or target == "to-crlf" and singlelf.search(data)): failed.append((str(ctx), target, f)) break return failed def parseeol(ui, repo, nodes): try: for node in nodes: try: if node is None: # Cannot use workingctx.data() since it would load # and cache the filters before we configure them. data = repo.wfile('.hgeol').read() else: data = repo[node]['.hgeol'].data() return eolfile(ui, repo.root, data) except (IOError, LookupError): pass except error.ParseError, inst: ui.warn(_("warning: ignoring .hgeol file due to parse error " "at %s: %s\n") % (inst.args[1], inst.args[0])) return None def _checkhook(ui, repo, node, headsonly): # Get revisions to check and touched files at the same time files = set() revs = set() for rev in xrange(repo[node].rev(), len(repo)): revs.add(rev) if headsonly: ctx = repo[rev] files.update(ctx.files()) for pctx in ctx.parents(): revs.discard(pctx.rev()) failed = [] for rev in revs: ctx = repo[rev] eol = parseeol(ui, repo, [ctx.node()]) if eol: failed.extend(eol.checkrev(repo, ctx, files)) if failed: eols = {'to-lf': 'CRLF', 'to-crlf': 'LF'} msgs = [] for node, target, f in failed: msgs.append(_(" %s in %s should not have %s line endings") % (f, node, eols[target])) raise util.Abort(_("end-of-line check failed:\n") + "\n".join(msgs)) def checkallhook(ui, repo, node, hooktype, **kwargs): """verify that files have expected EOLs""" _checkhook(ui, repo, node, False) def checkheadshook(ui, repo, node, hooktype, **kwargs): """verify that files have expected EOLs""" _checkhook(ui, repo, node, True) # "checkheadshook" used to be called "hook" hook = checkheadshook def preupdate(ui, repo, hooktype, parent1, parent2): repo.loadeol([parent1]) return False def uisetup(ui): ui.setconfig('hooks', 'preupdate.eol', preupdate) def extsetup(ui): try: extensions.find('win32text') ui.warn(_("the eol extension is incompatible with the " "win32text extension\n")) except KeyError: pass def reposetup(ui, repo): uisetup(repo.ui) if not repo.local(): return for name, fn in filters.iteritems(): repo.adddatafilter(name, fn) ui.setconfig('patch', 'eol', 'auto') class eolrepo(repo.__class__): def loadeol(self, nodes): eol = parseeol(self.ui, self, nodes) if eol is None: return None eol.copytoui(self.ui) return eol.match def _hgcleardirstate(self): self._eolfile = self.loadeol([None, 'tip']) if not self._eolfile: self._eolfile = util.never return try: cachemtime = os.path.getmtime(self.join("eol.cache")) except OSError: cachemtime = 0 try: eolmtime = os.path.getmtime(self.wjoin(".hgeol")) except OSError: eolmtime = 0 if eolmtime > cachemtime: self.ui.debug("eol: detected change in .hgeol\n") wlock = None try: wlock = self.wlock() for f in self.dirstate: if self.dirstate[f] == 'n': # all normal files need to be looked at # again since the new .hgeol file might no # longer match a file it matched before self.dirstate.normallookup(f) # Create or touch the cache to update mtime self.opener("eol.cache", "w").close() wlock.release() except error.LockUnavailable: # If we cannot lock the repository and clear the # dirstate, then a commit might not see all files # as modified. But if we cannot lock the # repository, then we can also not make a commit, # so ignore the error. pass def commitctx(self, ctx, error=False): for f in sorted(ctx.added() + ctx.modified()): if not self._eolfile(f): continue try: data = ctx[f].data() except IOError: continue if util.binary(data): # We should not abort here, since the user should # be able to say "** = native" to automatically # have all non-binary files taken care of. continue if inconsistenteol(data): raise util.Abort(_("inconsistent newline style " "in %s\n" % f)) return super(eolrepo, self).commitctx(ctx, error) repo.__class__ = eolrepo repo._hgcleardirstate() mercurial-2.8.2/hgext/win32text.py0000644000175000017500000001312212261160437020655 0ustar oxymoronoxymoron00000000000000# win32text.py - LF <-> CRLF/CR translation utilities for Windows/Mac users # # Copyright 2005, 2007-2009 Matt Mackall and others # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''perform automatic newline conversion Deprecation: The win32text extension requires each user to configure the extension again and again for each clone since the configuration is not copied when cloning. We have therefore made the ``eol`` as an alternative. The ``eol`` uses a version controlled file for its configuration and each clone will therefore use the right settings from the start. To perform automatic newline conversion, use:: [extensions] win32text = [encode] ** = cleverencode: # or ** = macencode: [decode] ** = cleverdecode: # or ** = macdecode: If not doing conversion, to make sure you do not commit CRLF/CR by accident:: [hooks] pretxncommit.crlf = python:hgext.win32text.forbidcrlf # or pretxncommit.cr = python:hgext.win32text.forbidcr To do the same check on a server to prevent CRLF/CR from being pushed or pulled:: [hooks] pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf # or pretxnchangegroup.cr = python:hgext.win32text.forbidcr ''' from mercurial.i18n import _ from mercurial.node import short from mercurial import util import re testedwith = 'internal' # regexp for single LF without CR preceding. re_single_lf = re.compile('(^|[^\r])\n', re.MULTILINE) newlinestr = {'\r\n': 'CRLF', '\r': 'CR'} filterstr = {'\r\n': 'clever', '\r': 'mac'} def checknewline(s, newline, ui=None, repo=None, filename=None): # warn if already has 'newline' in repository. # it might cause unexpected eol conversion. # see issue 302: # http://mercurial.selenic.com/bts/issue302 if newline in s and ui and filename and repo: ui.warn(_('WARNING: %s already has %s line endings\n' 'and does not need EOL conversion by the win32text plugin.\n' 'Before your next commit, please reconsider your ' 'encode/decode settings in \nMercurial.ini or %s.\n') % (filename, newlinestr[newline], repo.join('hgrc'))) def dumbdecode(s, cmd, **kwargs): checknewline(s, '\r\n', **kwargs) # replace single LF to CRLF return re_single_lf.sub('\\1\r\n', s) def dumbencode(s, cmd): return s.replace('\r\n', '\n') def macdumbdecode(s, cmd, **kwargs): checknewline(s, '\r', **kwargs) return s.replace('\n', '\r') def macdumbencode(s, cmd): return s.replace('\r', '\n') def cleverdecode(s, cmd, **kwargs): if not util.binary(s): return dumbdecode(s, cmd, **kwargs) return s def cleverencode(s, cmd): if not util.binary(s): return dumbencode(s, cmd) return s def macdecode(s, cmd, **kwargs): if not util.binary(s): return macdumbdecode(s, cmd, **kwargs) return s def macencode(s, cmd): if not util.binary(s): return macdumbencode(s, cmd) return s _filters = { 'dumbdecode:': dumbdecode, 'dumbencode:': dumbencode, 'cleverdecode:': cleverdecode, 'cleverencode:': cleverencode, 'macdumbdecode:': macdumbdecode, 'macdumbencode:': macdumbencode, 'macdecode:': macdecode, 'macencode:': macencode, } def forbidnewline(ui, repo, hooktype, node, newline, **kwargs): halt = False seen = set() # we try to walk changesets in reverse order from newest to # oldest, so that if we see a file multiple times, we take the # newest version as canonical. this prevents us from blocking a # changegroup that contains an unacceptable commit followed later # by a commit that fixes the problem. tip = repo['tip'] for rev in xrange(len(repo) - 1, repo[node].rev() - 1, -1): c = repo[rev] for f in c.files(): if f in seen or f not in tip or f not in c: continue seen.add(f) data = c[f].data() if not util.binary(data) and newline in data: if not halt: ui.warn(_('attempt to commit or push text file(s) ' 'using %s line endings\n') % newlinestr[newline]) ui.warn(_('in %s: %s\n') % (short(c.node()), f)) halt = True if halt and hooktype == 'pretxnchangegroup': crlf = newlinestr[newline].lower() filter = filterstr[newline] ui.warn(_('\nTo prevent this mistake in your local repository,\n' 'add to Mercurial.ini or .hg/hgrc:\n' '\n' '[hooks]\n' 'pretxncommit.%s = python:hgext.win32text.forbid%s\n' '\n' 'and also consider adding:\n' '\n' '[extensions]\n' 'win32text =\n' '[encode]\n' '** = %sencode:\n' '[decode]\n' '** = %sdecode:\n') % (crlf, crlf, filter, filter)) return halt def forbidcrlf(ui, repo, hooktype, node, **kwargs): return forbidnewline(ui, repo, hooktype, node, '\r\n', **kwargs) def forbidcr(ui, repo, hooktype, node, **kwargs): return forbidnewline(ui, repo, hooktype, node, '\r', **kwargs) def reposetup(ui, repo): if not repo.local(): return for name, fn in _filters.iteritems(): repo.adddatafilter(name, fn) def extsetup(ui): if ui.configbool('win32text', 'warn', True): ui.warn(_("win32text is deprecated: " "http://mercurial.selenic.com/wiki/Win32TextExtension\n")) mercurial-2.8.2/hgext/children.py0000644000175000017500000000302412261160437020576 0ustar oxymoronoxymoron00000000000000# Mercurial extension to provide the 'hg children' command # # Copyright 2007 by Intevation GmbH # # Author(s): # Thomas Arendsen Hein # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. '''command to display child changesets (DEPRECATED) This extension is deprecated. You should use :hg:`log -r "children(REV)"` instead. ''' from mercurial import cmdutil, commands from mercurial.commands import templateopts from mercurial.i18n import _ testedwith = 'internal' def children(ui, repo, file_=None, **opts): """show the children of the given or working directory revision Print the children of the working directory's revisions. If a revision is given via -r/--rev, the children of that revision will be printed. If a file argument is given, revision in which the file was last changed (after the working directory revision or the argument to --rev if given) is printed. """ rev = opts.get('rev') if file_: ctx = repo.filectx(file_, changeid=rev) else: ctx = repo[rev] displayer = cmdutil.show_changeset(ui, repo, opts) for cctx in ctx.children(): displayer.show(cctx) displayer.close() cmdtable = { "children": (children, [('r', 'rev', '', _('show children of the specified revision'), _('REV')), ] + templateopts, _('hg children [-r REV] [FILE]')), } commands.inferrepo += " children" mercurial-2.8.2/hgext/strip.py0000644000175000017500000002102512261160437020150 0ustar oxymoronoxymoron00000000000000"""strip changesets and their descendents from history This extension allows you to strip changesets and all their descendants from the repository. See the command help for details. """ from mercurial.i18n import _ from mercurial.node import nullid from mercurial.lock import release from mercurial import cmdutil, hg, scmutil, util from mercurial import repair, bookmarks cmdtable = {} command = cmdutil.command(cmdtable) testedwith = 'internal' def checksubstate(repo, baserev=None): '''return list of subrepos at a different revision than substate. Abort if any subrepos have uncommitted changes.''' inclsubs = [] wctx = repo[None] if baserev: bctx = repo[baserev] else: bctx = wctx.parents()[0] for s in sorted(wctx.substate): if wctx.sub(s).dirty(True): raise util.Abort( _("uncommitted changes in subrepository %s") % s) elif s not in bctx.substate or bctx.sub(s).dirty(): inclsubs.append(s) return inclsubs def checklocalchanges(repo, force=False, excsuffix=''): cmdutil.checkunfinished(repo) m, a, r, d = repo.status()[:4] if not force: if (m or a or r or d): _("local changes found") # i18n tool detection raise util.Abort(_("local changes found" + excsuffix)) if checksubstate(repo): _("local changed subrepos found") # i18n tool detection raise util.Abort(_("local changed subrepos found" + excsuffix)) return m, a, r, d def strip(ui, repo, revs, update=True, backup="all", force=None): wlock = lock = None try: wlock = repo.wlock() lock = repo.lock() if update: checklocalchanges(repo, force=force) urev, p2 = repo.changelog.parents(revs[0]) if (util.safehasattr(repo, 'mq') and p2 != nullid and p2 in [x.node for x in repo.mq.applied]): urev = p2 hg.clean(repo, urev) repo.dirstate.write() repair.strip(ui, repo, revs, backup) finally: release(lock, wlock) @command("strip", [ ('r', 'rev', [], _('strip specified revision (optional, ' 'can specify revisions without this ' 'option)'), _('REV')), ('f', 'force', None, _('force removal of changesets, discard ' 'uncommitted changes (no backup)')), ('b', 'backup', None, _('bundle only changesets with local revision' ' number greater than REV which are not' ' descendants of REV (DEPRECATED)')), ('', 'no-backup', None, _('no backups')), ('', 'nobackup', None, _('no backups (DEPRECATED)')), ('n', '', None, _('ignored (DEPRECATED)')), ('k', 'keep', None, _("do not modify working copy during strip")), ('B', 'bookmark', '', _("remove revs only reachable from given" " bookmark"))], _('hg strip [-k] [-f] [-n] [-B bookmark] [-r] REV...')) def stripcmd(ui, repo, *revs, **opts): """strip changesets and all their descendants from the repository The strip command removes the specified changesets and all their descendants. If the working directory has uncommitted changes, the operation is aborted unless the --force flag is supplied, in which case changes will be discarded. If a parent of the working directory is stripped, then the working directory will automatically be updated to the most recent available ancestor of the stripped parent after the operation completes. Any stripped changesets are stored in ``.hg/strip-backup`` as a bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`, where BUNDLE is the bundle file created by the strip. Note that the local revision numbers will in general be different after the restore. Use the --no-backup option to discard the backup bundle once the operation completes. Strip is not a history-rewriting operation and can be used on changesets in the public phase. But if the stripped changesets have been pushed to a remote repository you will likely pull them again. Return 0 on success. """ backup = 'all' if opts.get('backup'): backup = 'strip' elif opts.get('no_backup') or opts.get('nobackup'): backup = 'none' cl = repo.changelog revs = list(revs) + opts.get('rev') revs = set(scmutil.revrange(repo, revs)) wlock = repo.wlock() try: if opts.get('bookmark'): mark = opts.get('bookmark') marks = repo._bookmarks if mark not in marks: raise util.Abort(_("bookmark '%s' not found") % mark) # If the requested bookmark is not the only one pointing to a # a revision we have to only delete the bookmark and not strip # anything. revsets cannot detect that case. uniquebm = True for m, n in marks.iteritems(): if m != mark and n == repo[mark].node(): uniquebm = False break if uniquebm: rsrevs = repo.revs("ancestors(bookmark(%s)) - " "ancestors(head() and not bookmark(%s)) - " "ancestors(bookmark() and not bookmark(%s))", mark, mark, mark) revs.update(set(rsrevs)) if not revs: del marks[mark] marks.write() ui.write(_("bookmark '%s' deleted\n") % mark) if not revs: raise util.Abort(_('empty revision set')) descendants = set(cl.descendants(revs)) strippedrevs = revs.union(descendants) roots = revs.difference(descendants) update = False # if one of the wdir parent is stripped we'll need # to update away to an earlier revision for p in repo.dirstate.parents(): if p != nullid and cl.rev(p) in strippedrevs: update = True break rootnodes = set(cl.node(r) for r in roots) q = getattr(repo, 'mq', None) if q is not None and q.applied: # refresh queue state if we're about to strip # applied patches if cl.rev(repo.lookup('qtip')) in strippedrevs: q.applieddirty = True start = 0 end = len(q.applied) for i, statusentry in enumerate(q.applied): if statusentry.node in rootnodes: # if one of the stripped roots is an applied # patch, only part of the queue is stripped start = i break del q.applied[start:end] q.savedirty() revs = sorted(rootnodes) if update and opts.get('keep'): wlock = repo.wlock() try: urev, p2 = repo.changelog.parents(revs[0]) if (util.safehasattr(repo, 'mq') and p2 != nullid and p2 in [x.node for x in repo.mq.applied]): urev = p2 uctx = repo[urev] # only reset the dirstate for files that would actually change # between the working context and uctx descendantrevs = repo.revs("%s::." % uctx.rev()) changedfiles = [] for rev in descendantrevs: # blindly reset the files, regardless of what actually # changed changedfiles.extend(repo[rev].files()) # reset files that only changed in the dirstate too dirstate = repo.dirstate dirchanges = [f for f in dirstate if dirstate[f] != 'n'] changedfiles.extend(dirchanges) repo.dirstate.rebuild(urev, uctx.manifest(), changedfiles) repo.dirstate.write() update = False finally: wlock.release() if opts.get('bookmark'): if mark == repo._bookmarkcurrent: bookmarks.setcurrent(repo, None) del marks[mark] marks.write() ui.write(_("bookmark '%s' deleted\n") % mark) strip(ui, repo, revs, backup=backup, update=update, force=opts.get('force')) finally: wlock.release() return 0 mercurial-2.8.2/hgext/schemes.py0000644000175000017500000000645412261160437020447 0ustar oxymoronoxymoron00000000000000# Copyright 2009, Alexander Solovyov # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. """extend schemes with shortcuts to repository swarms This extension allows you to specify shortcuts for parent URLs with a lot of repositories to act like a scheme, for example:: [schemes] py = http://code.python.org/hg/ After that you can use it like:: hg clone py://trunk/ Additionally there is support for some more complex schemas, for example used by Google Code:: [schemes] gcode = http://{1}.googlecode.com/hg/ The syntax is taken from Mercurial templates, and you have unlimited number of variables, starting with ``{1}`` and continuing with ``{2}``, ``{3}`` and so on. This variables will receive parts of URL supplied, split by ``/``. Anything not specified as ``{part}`` will be just appended to an URL. For convenience, the extension adds these schemes by default:: [schemes] py = http://hg.python.org/ bb = https://bitbucket.org/ bb+ssh = ssh://hg@bitbucket.org/ gcode = https://{1}.googlecode.com/hg/ kiln = https://{1}.kilnhg.com/Repo/ You can override a predefined scheme by defining a new scheme with the same name. """ import os, re from mercurial import extensions, hg, templater, util from mercurial.i18n import _ testedwith = 'internal' class ShortRepository(object): def __init__(self, url, scheme, templater): self.scheme = scheme self.templater = templater self.url = url try: self.parts = max(map(int, re.findall(r'\{(\d+)\}', self.url))) except ValueError: self.parts = 0 def __repr__(self): return '' % self.scheme def instance(self, ui, url, create): # Should this use the util.url class, or is manual parsing better? try: url = url.split('://', 1)[1] except IndexError: raise util.Abort(_("no '://' in scheme url '%s'") % url) parts = url.split('/', self.parts) if len(parts) > self.parts: tail = parts[-1] parts = parts[:-1] else: tail = '' context = dict((str(i + 1), v) for i, v in enumerate(parts)) url = ''.join(self.templater.process(self.url, context)) + tail return hg._peerlookup(url).instance(ui, url, create) def hasdriveletter(orig, path): if path: for scheme in schemes: if path.startswith(scheme + ':'): return False return orig(path) schemes = { 'py': 'http://hg.python.org/', 'bb': 'https://bitbucket.org/', 'bb+ssh': 'ssh://hg@bitbucket.org/', 'gcode': 'https://{1}.googlecode.com/hg/', 'kiln': 'https://{1}.kilnhg.com/Repo/' } def extsetup(ui): schemes.update(dict(ui.configitems('schemes'))) t = templater.engine(lambda x: x) for scheme, url in schemes.items(): if (os.name == 'nt' and len(scheme) == 1 and scheme.isalpha() and os.path.exists('%s:\\' % scheme)): raise util.Abort(_('custom scheme %s:// conflicts with drive ' 'letter %s:\\\n') % (scheme, scheme.upper())) hg.schemes[scheme] = ShortRepository(url, scheme, t) extensions.wrapfunction(util, 'hasdriveletter', hasdriveletter) mercurial-2.8.2/hgext/relink.py0000644000175000017500000001360212261160437020275 0ustar oxymoronoxymoron00000000000000# Mercurial extension to provide 'hg relink' command # # Copyright (C) 2007 Brendan Cully # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. """recreates hardlinks between repository clones""" from mercurial import hg, util from mercurial.i18n import _ import os, stat testedwith = 'internal' def relink(ui, repo, origin=None, **opts): """recreate hardlinks between two repositories When repositories are cloned locally, their data files will be hardlinked so that they only use the space of a single repository. Unfortunately, subsequent pulls into either repository will break hardlinks for any files touched by the new changesets, even if both repositories end up pulling the same changes. Similarly, passing --rev to "hg clone" will fail to use any hardlinks, falling back to a complete copy of the source repository. This command lets you recreate those hardlinks and reclaim that wasted space. This repository will be relinked to share space with ORIGIN, which must be on the same local disk. If ORIGIN is omitted, looks for "default-relink", then "default", in [paths]. Do not attempt any read operations on this repository while the command is running. (Both repositories will be locked against writes.) """ if (not util.safehasattr(util, 'samefile') or not util.safehasattr(util, 'samedevice')): raise util.Abort(_('hardlinks are not supported on this system')) src = hg.repository(repo.baseui, ui.expandpath(origin or 'default-relink', origin or 'default')) ui.status(_('relinking %s to %s\n') % (src.store.path, repo.store.path)) if repo.root == src.root: ui.status(_('there is nothing to relink\n')) return locallock = repo.lock() try: remotelock = src.lock() try: candidates = sorted(collect(src, ui)) targets = prune(candidates, src.store.path, repo.store.path, ui) do_relink(src.store.path, repo.store.path, targets, ui) finally: remotelock.release() finally: locallock.release() def collect(src, ui): seplen = len(os.path.sep) candidates = [] live = len(src['tip'].manifest()) # Your average repository has some files which were deleted before # the tip revision. We account for that by assuming that there are # 3 tracked files for every 2 live files as of the tip version of # the repository. # # mozilla-central as of 2010-06-10 had a ratio of just over 7:5. total = live * 3 // 2 src = src.store.path pos = 0 ui.status(_("tip has %d files, estimated total number of files: %s\n") % (live, total)) for dirpath, dirnames, filenames in os.walk(src): dirnames.sort() relpath = dirpath[len(src) + seplen:] for filename in sorted(filenames): if filename[-2:] not in ('.d', '.i'): continue st = os.stat(os.path.join(dirpath, filename)) if not stat.S_ISREG(st.st_mode): continue pos += 1 candidates.append((os.path.join(relpath, filename), st)) ui.progress(_('collecting'), pos, filename, _('files'), total) ui.progress(_('collecting'), None) ui.status(_('collected %d candidate storage files\n') % len(candidates)) return candidates def prune(candidates, src, dst, ui): def linkfilter(src, dst, st): try: ts = os.stat(dst) except OSError: # Destination doesn't have this file? return False if util.samefile(src, dst): return False if not util.samedevice(src, dst): # No point in continuing raise util.Abort( _('source and destination are on different devices')) if st.st_size != ts.st_size: return False return st targets = [] total = len(candidates) pos = 0 for fn, st in candidates: pos += 1 srcpath = os.path.join(src, fn) tgt = os.path.join(dst, fn) ts = linkfilter(srcpath, tgt, st) if not ts: ui.debug('not linkable: %s\n' % fn) continue targets.append((fn, ts.st_size)) ui.progress(_('pruning'), pos, fn, _('files'), total) ui.progress(_('pruning'), None) ui.status(_('pruned down to %d probably relinkable files\n') % len(targets)) return targets def do_relink(src, dst, files, ui): def relinkfile(src, dst): bak = dst + '.bak' os.rename(dst, bak) try: util.oslink(src, dst) except OSError: os.rename(bak, dst) raise os.remove(bak) CHUNKLEN = 65536 relinked = 0 savedbytes = 0 pos = 0 total = len(files) for f, sz in files: pos += 1 source = os.path.join(src, f) tgt = os.path.join(dst, f) # Binary mode, so that read() works correctly, especially on Windows sfp = file(source, 'rb') dfp = file(tgt, 'rb') sin = sfp.read(CHUNKLEN) while sin: din = dfp.read(CHUNKLEN) if sin != din: break sin = sfp.read(CHUNKLEN) sfp.close() dfp.close() if sin: ui.debug('not linkable: %s\n' % f) continue try: relinkfile(source, tgt) ui.progress(_('relinking'), pos, f, _('files'), total) relinked += 1 savedbytes += sz except OSError, inst: ui.warn('%s: %s\n' % (tgt, str(inst))) ui.progress(_('relinking'), None) ui.status(_('relinked %d files (%s reclaimed)\n') % (relinked, util.bytecount(savedbytes))) cmdtable = { 'relink': ( relink, [], _('[ORIGIN]') ) } mercurial-2.8.2/hgext/highlight/0000755000175000017500000000000012261160455020404 5ustar oxymoronoxymoron00000000000000mercurial-2.8.2/hgext/highlight/__init__.py0000644000175000017500000000464412261160437022525 0ustar oxymoronoxymoron00000000000000# highlight - syntax highlighting in hgweb, based on Pygments # # Copyright 2008, 2009 Patrick Mezard and others # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. # # The original module was split in an interface and an implementation # file to defer pygments loading and speedup extension setup. """syntax highlighting for hgweb (requires Pygments) It depends on the Pygments syntax highlighting library: http://pygments.org/ There is a single configuration option:: [web] pygments_style =