Mercurial > hg-stable
changeset 2836:e78cad1f6b1f
Merge manifest refactor work
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Wed, 09 Aug 2006 15:03:46 -0500 |
parents | 49988d9f0758 (diff) a9f5d4149123 (current diff) |
children | 19b3bc840182 |
files | mercurial/commands.py mercurial/dirstate.py mercurial/localrepo.py mercurial/merge.py |
diffstat | 116 files changed, 4831 insertions(+), 2155 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgsigs Wed Aug 09 14:53:03 2006 -0500 +++ b/.hgsigs Wed Aug 09 15:03:46 2006 -0500 @@ -1,1 +1,2 @@ 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0 iD8DBQBEYmO2ywK+sNU5EO8RAnaYAKCO7x15xUn5mnhqWNXqk/ehlhRt2QCfRDfY0LrUq2q4oK/KypuJYPHgq1A= +2be3001847cb18a23c403439d9e7d0ace30804e9 0 iD8DBQBExUbjywK+sNU5EO8RAhzxAKCtyHAQUzcTSZTqlfJ0by6vhREwWQCghaQFHfkfN0l9/40EowNhuMOKnJk=
--- a/.hgtags Wed Aug 09 14:53:03 2006 -0500 +++ b/.hgtags Wed Aug 09 15:03:46 2006 -0500 @@ -11,3 +11,4 @@ 3a56574f329a368d645853e0f9e09472aee62349 0.8 6a03cff2b0f5d30281e6addefe96b993582f2eac 0.8.1 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0.9 +2be3001847cb18a23c403439d9e7d0ace30804e9 0.9.1
--- a/MANIFEST.in Wed Aug 09 14:53:03 2006 -0500 +++ b/MANIFEST.in Wed Aug 09 15:03:46 2006 -0500 @@ -2,7 +2,7 @@ recursive-include mercurial *.py include hgweb.cgi hgwebdir.cgi include hgeditor rewrite-log -include tests/README tests/coverage.py tests/run-tests.py tests/md5sum.py tests/test-*[a-z0-9] tests/*.out +include tests/README tests/*.py tests/test-*[a-z0-9] tests/*.out prune tests/*.err include *.txt include templates/map templates/map-*[a-z0-9] @@ -10,8 +10,10 @@ include templates/static/* include doc/README doc/Makefile doc/gendoc.py doc/*.txt doc/*.html doc/*.[0-9] recursive-include contrib * +recursive-include hgext * include README include CONTRIBUTORS include COPYING include Makefile include MANIFEST.in +prune *.elc *.orig *.rej *~ *.o *.so *.pyc *.swp *.prof
--- a/contrib/bash_completion Wed Aug 09 14:53:03 2006 -0500 +++ b/contrib/bash_completion Wed Aug 09 15:03:46 2006 -0500 @@ -288,7 +288,7 @@ _hg_cmd_qdelete() { - _hg_ext_mq_patchlist qseries + _hg_ext_mq_patchlist qunapplied } _hg_cmd_qsave() @@ -313,6 +313,11 @@ COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur")) } +_hg_cmd_export() +{ + _hg_ext_mq_patchlist qapplied +} + # hbisect _hg_cmd_bisect()
--- a/contrib/convert-repo Wed Aug 09 14:53:03 2006 -0500 +++ b/contrib/convert-repo Wed Aug 09 15:03:46 2006 -0500 @@ -28,7 +28,8 @@ self.path = path def getheads(self): - return [file(self.path + "/HEAD").read()[:-1]] + fh = os.popen("GIT_DIR=%s git-rev-parse --verify HEAD" % self.path) + return [fh.read()[:-1]] def catfile(self, rev, type): if rev == "0" * 40: raise IOError()
--- a/contrib/darcs2hg.py Wed Aug 09 14:53:03 2006 -0500 +++ b/contrib/darcs2hg.py Wed Aug 09 15:03:46 2006 -0500 @@ -92,7 +92,7 @@ def darcs_pull(hg_repo, darcs_repo, chash): old_tip = darcs_tip(darcs_repo) - res = cmd("darcs pull '%s' --all --match='hash %s'" % (darcs_repo, chash), hg_repo) + res = cmd("darcs pull \"%s\" --all --match=\"hash %s\"" % (darcs_repo, chash), hg_repo) print res new_tip = darcs_tip(darcs_repo) if not new_tip != old_tip + 1: @@ -110,7 +110,8 @@ old_tip = hg_tip(hg_repo) cmd("hg add -X _darcs", hg_repo) cmd("hg remove -X _darcs --after", hg_repo) - res = cmd("hg commit -l %s -u '%s' -d '%s 0'" % (tmpfile, author, date), hg_repo) + res = cmd("hg commit -l %s -u \"%s\" -d \"%s 0\"" % (tmpfile, author, date), hg_repo) + os.close(fd) os.unlink(tmpfile) new_tip = hg_tip(hg_repo) if not new_tip == old_tip + 1: @@ -156,7 +157,7 @@ print "Given HG repository must not exist when no SKIP is specified." sys.exit(-1) if skip == None: - cmd("hg init '%s'" % (hg_repo)) + cmd("hg init \"%s\"" % (hg_repo)) cmd("darcs initialize", hg_repo) # Get the changes from the Darcs repository change_number = 0
--- a/contrib/hgsh/hgsh.c Wed Aug 09 14:53:03 2006 -0500 +++ b/contrib/hgsh/hgsh.c Wed Aug 09 15:03:46 2006 -0500 @@ -244,13 +244,20 @@ exit(EX_OSFILE); } +enum cmdline { + hg_init, + hg_serve, +}; + + /* * paranoid wrapper, runs hg executable in server mode. */ static void serve_data(int argc, char **argv) { char *hg_root = HG_ROOT; - char *repo, *abspath; + char *repo, *repo_root; + enum cmdline cmd; char *nargv[6]; struct stat st; size_t repolen; @@ -275,7 +282,12 @@ goto badargs; } - if (sscanf(argv[2], "hg -R %as serve --stdio", &repo) != 1) { + 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; } @@ -286,7 +298,7 @@ } if (hg_root) { - if (asprintf(&abspath, "%s/%s/.hg/data", hg_root, repo) == -1) { + if (asprintf(&repo_root, "%s/%s/", hg_root, repo) == -1) { goto badargs; } @@ -296,16 +308,26 @@ * symlink that looks safe, but really breaks us out of tree. */ - if (strstr(abspath, "/../") != NULL) { + if (strstr(repo_root, "/../") != NULL) { goto badargs; } - /* verify that we really are looking at valid repo. */ + /* only hg init expects no repo. */ - if (stat(abspath, &st) == -1) { - perror(repo); - exit(EX_DATAERR); - } + if (cmd != hg_init) { + char *abs_path; + + if (asprintf(&abs_path, "%s.hg/data", repo_root) == -1) { + goto badargs; + } + + /* verify that we really are looking at valid repo. */ + + if (stat(abs_path, &st) == -1) { + perror(repo); + exit(EX_DATAERR); + } + } if (chdir(hg_root) == -1) { perror(hg_root); @@ -314,11 +336,22 @@ } i = 0; - nargv[i++] = HG; - nargv[i++] = "-R"; - nargv[i++] = repo; - nargv[i++] = "serve"; - nargv[i++] = "--stdio"; + + 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) {
--- a/contrib/macosx/Welcome.html Wed Aug 09 14:53:03 2006 -0500 +++ b/contrib/macosx/Welcome.html Wed Aug 09 15:03:46 2006 -0500 @@ -5,13 +5,73 @@ <meta http-equiv="Content-Style-Type" content="text/css"> <title></title> <style type="text/css"> - p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica} + p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Helvetica} p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; min-height: 14.0px} </style> </head> <body> <p class="p1">This is a prepackaged release of <a href="http://www.selenic.com/mercurial">Mercurial</a> for Mac OS X.</p> <p class="p2"><br></p> -<p class="p1">It is based on Mercurial 0.9.</p> +<p class="p1">It is based on Mercurial 0.9.1</p> +<br> +<pre> +Release Notes +------------- + +2006-07-24 v0.9.1 + +Major changes between Mercurial 0.9 and 0.9.1: + + New features: + - You can now configure your 'hgweb' server to let remote users + 'push' changes over http. + - You can now 'import' a patch in a mail message by saving the mail + message, and importing it. This works for patches sent either + inline or as attachments. + - The 'diff' command now accepts '-rA:B' syntax as a synonym for + '-r A -r B', and adds '-b' and '-B' options. + + New contributions and extensions: + - The 'acl' extension lets you lock down parts of a repository + against incoming changes + - The 'extdiff' extension lets you run your favourite graphical + change viewer + - Comprehensive integration with the 'vim' editor + - A restricted shell for 'ssh'-hosted repositories + - An importer for 'darcs' repositories + + New hooks added: + - 'preupdate' is run before an update or merge in the working + directory. + - 'update' is run after an update or merge in the working + directory. + + Behaviour changes: + - NOTE: Mercurial as installed by the Windows binary + installer no longer performs automatic line-ending conversion for + Unix/Linux compatibility. To re-enable this feature, edit your + 'mercurial.ini' file after you upgrade. + - The Windows binary installer now automatically adds 'hg' to your + '%PATH%'. + - The 'backout' command now runs an editor by default, to let you + modify the commit message for a backed-out changeset. + - An earlier problem with parsing of tags has been fixed. + This makes tag parsing slower but more reliable. + + Memory usage and performance improvements: + - The 'remove' command has been rewritten to be hundreds of times + faster in large repositories. + - It is now possible to 'clone' a repository very quickly over a + LAN, if the server is configured to allow it. See the new 'server' + section in the 'hgrc' documentation. + + Other changes of note: + - Mercurial will now print help for an extension if you type 'hg + help EXT_NAME'. + - The usual array of bug fixes and documentation improvements. + - The integrated web server is now more WSGI-compliant. + - Work has begun to solidify Mercurial's API for use by third-party + packages. +</pre> </body> </html>
--- a/contrib/mercurial.el Wed Aug 09 14:53:03 2006 -0500 +++ b/contrib/mercurial.el Wed Aug 09 15:03:46 2006 -0500 @@ -380,7 +380,9 @@ (save-excursion (while hg-prev-buffer (set-buffer hg-prev-buffer)) - (let ((path (or default (buffer-file-name) default-directory))) + (let ((path (or default + (buffer-file-name) + (expand-file-name default-directory)))) (if (or (not path) current-prefix-arg) (expand-file-name (eval (list* 'read-file-name @@ -716,7 +718,11 @@ (goto-char pos) (end-of-line 1) (delete-region pos (point))) - (cd (hg-root)))) + (let ((hg-root-dir (hg-root))) + (if (not hg-root-dir) + (error "error: %s: directory is not part of a Mercurial repository." + default-directory) + (cd (hg-root)))))) (defun hg-add (path) "Add PATH to the Mercurial repository on the next commit. @@ -972,7 +978,8 @@ (cd (hg-root path))) (when update (with-current-buffer buf - (set (make-local-variable 'backup-inhibited) nil) + (when (local-variable-p 'backup-inhibited) + (kill-local-variable 'backup-inhibited)) (hg-mode-line))))) (defun hg-incoming (&optional repo)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/sample.hgrc Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,123 @@ +### --- User interface + +[ui] + +### show changed files and be a bit more verbose if True + +# verbose = True + +### username data to appear in comits +### it usually takes the form: Joe User <joe.user@host.com> + +# username = Joe User <j.user@example.com> + +### --- Extensions + +[extensions] + +### each extension has its own 'extension_name=path' line +### the default python library path is used when path is left blank +### the hgext dir is used when 'hgext.extension_name=' is written + +### acl - Access control lists +### hg help acl + +# hgext.acl = + +### bisect - binary search changesets to detect bugs +### hg help bisect + +# hgext.hbisect = + +### bugzilla - update bugzilla bugs when changesets mention them +### hg help bugzilla + +# hgext.bugzilla = + +### extdiff - Use external diff application instead of builtin one + +# hgext.extdiff = + +### gpg - GPG checks and signing +### hg help gpg + +# hgext.gpg = + +### hgk - GUI repository browser +### hg help view + +# hgk = /home/user/hg/hg/contrib/hgk.py + +### mq - Mercurial patch queues +### hg help mq + +# hgext.mq = + +### notify - Template driven e-mail notifications +### hg help notify + +# hgext.notify = + +### patchbomb - send changesets as a series of patch emails +### hg help email + +# hgext.patchbomb = + +### win32text - line ending conversion filters for the Windows platform + +# hgext.win32text = + +### --- hgk additional configuration + +[hgk] + +### set executable path + +# path = /home/user/hg/hg/contrib/hgk + +### --- Hook to Mercurial actions - See hgrc man page for avaliable hooks + +[hooks] + +### Example notify hooks (load hgext.notify extension before use) + +# incoming.notify = python:hgext.notify.hook +# changegroup.notify = python:hgext.notify.hook + +### Email configuration for the notify and patchbomb extensions + +[email] + +### Your email address + +# from = user@example.com + +### Method to send email - smtp or /usr/sbin/sendmail or other program name + +# method = smtp + +### smtp server to send email to + +[smtp] + +# host = mail +# port = 25 +# tls = false +# username = user +# password = blivet +# local_hostname = myhost + +### --- Email notification hook for server + +[notify] +### multiple sources can be specified as a whitespace or comma separated list + +# sources = serve push pull bundle + +### set this to False when you're ready for mail to start sending + +# test = True + +### path to config file with names of subscribers + +# config = /path/to/subscription/file
--- a/contrib/vim/hgcommand.txt Wed Aug 09 14:53:03 2006 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,567 +0,0 @@ -*hgcommand.txt* HGCommand - -For instructions on installing this file, type - :help add-local-help -inside Vim. - -Author: Mathieu Clabaut <mathieu.clabaut@gmail.com> -Credits: Bob Hiestand <bob.hiestand@gmail.com> - -============================================================================== -1. Contents *hgcommand-contents* - - Installation : |hgcommand-install| - HGCommand Intro : |hgcommand| - HGCommand Manual : |hgcommand-manual| - Customization : |hgcommand-customize| - SSH "integration" : |hgcommand-ssh| - Bugs : |hgcommand-bugs| - -============================================================================== - -2. HGCommand Installation *hgcommand-install* - -The HGCommand plugin comprises two files, hgcommand.vim and hgcommand.txt -(this file). In order to install the plugin, place the hgcommand.vim file -into a plugin' directory in your runtime path (please see |add-global-plugin| -and |'runtimepath'|. - -HGCommand may be customized by setting variables, creating maps, and -specifying event handlers. Please see |hgcommand-customize| for more -details. - -This help file can be included in the VIM help system by copying it into a -'doc' directory in your runtime path and then executing the |:helptags| -command, specifying the full path of the 'doc' directory. Please see -|add-local-help| for more details. - -============================================================================== - -3. HGCommand Intro *hgcommand* - *hgcommand-intro* - -The HGCommand plugin provides global ex commands for manipulating -HG-controlled source files. In general, each command operates on the current -buffer and accomplishes a separate hg function, such as update, commit, log, -and others (please see |hgcommand-commands| for a list of all available -commands). The results of each operation are displayed in a scratch buffer. -Several buffer variables are defined for those scratch buffers (please see -|hgcommand-buffer-variables|). - -The notion of "current file" means either the current buffer, or, in the case -of a directory buffer, the file on the current line within the buffer. - -For convenience, any HGCommand invoked on a HGCommand scratch buffer acts as -though it was invoked on the original file and splits the screen so that the -output appears in a new window. - -Many of the commands accept revisions as arguments. By default, most operate -on the most recent revision on the current branch if no revision is specified -(though see |HGCommandInteractive| to prompt instead). - -Each HGCommand is mapped to a key sequence starting with the <Leader> -keystroke. The default mappings may be overridden by supplying different -mappings before the plugin is loaded, such as in the vimrc, in the standard -fashion for plugin mappings. For examples, please see -|hgcommand-mappings-override|. - -The HGCommand plugin may be configured in several ways. For more details, -please see |hgcommand-customize|. - -============================================================================== - -4. HGCommand Manual *hgcommand-manual* - -4.1 HGCommand commands *hgcommand-commands* - -HGCommand defines the following commands: - -|:HGAdd| -|:HGAnnotate| -|:HGCommit| -|:HGDiff| -|:HGGotoOriginal| -|:HGLog| -|:HGRevert| -|:HGReview| -|:HGStatus| -|:HGUnedit| -|:HGUpdate| -|:HGVimDiff| - -:HGAdd *:HGAdd* - -This command performs "hg add" on the current file. Please note, this does -not commit the newly-added file. - -:HGAnnotate *:HGAnnotate* - -This command performs "hg annotate" on the current file. If an argument is -given, the argument is used as a revision number to display. If not given an -argument, it uses the most recent version of the file on the current branch. -Additionally, if the current buffer is a HGAnnotate buffer already, the -version number on the current line is used. - -If the |HGCommandAnnotateParent| variable is set to a non-zero value, the -version previous to the one on the current line is used instead. This allows -one to navigate back to examine the previous version of a line. - -The filetype of the HGCommand scratch buffer is set to 'HGAnnotate', to take -advantage of the bundled syntax file. - - -:HGCommit[!] *:HGCommit* - -If called with arguments, this performs "hg commit" using the arguments as -the log message. - -If '!' is used with no arguments, an empty log message is committed. - -If called with no arguments, this is a two-step command. The first step opens -a buffer to accept a log message. When that buffer is written, it is -automatically closed and the file is committed using the information from that -log message. The commit can be abandoned if the log message buffer is deleted -or wiped before being written. - -Alternatively, the mapping that is used to invoke :HGCommit (by default -<Leader>hgc) can be used in the log message buffer to immediately commit. -This -is useful if the |HGCommandCommitOnWrite| variable is set to 0 to disable the -normal commit-on-write behavior. - -:HGDiff *:HGDiff* - -With no arguments, this performs "hg diff" on the current file against the -current repository version. - -With one argument, "hg diff" is performed on the current file against the -specified revision. - -With two arguments, hg diff is performed between the specified -revisions of the current file. - -This command uses the 'HGCommandDiffOpt' variable to specify diff options. -If that variable does not exist, then 'wbBc' is assumed. If you wish to have -no options, then set it to the empty string. - - -This command performs "hg edit" on the current file. - -:HGGotoOriginal *:HGGotoOriginal* - -This command returns the current window to the source buffer, if the current -buffer is a HG command output buffer. - -:HGGotoOriginal! - -Like ":HGGotoOriginal" but also executes :bufwipeout on all HG command -output buffers for the source buffer. - -:HGLog *:HGLog* - -Performs "hg log" on the current file. - -If an argument is given, it is passed as an argument to the "-r" option of -"hg log". - -:HGRevert *:HGRevert* - -Replaces the current file with the most recent version from the repository in -order to wipe out any undesired changes. - -:HGReview *:HGReview* - -Retrieves a particular version of the current file. If no argument is given, -the most recent version of the file on the current branch is retrieved. -Otherwise, the specified version is retrieved. - -:HGStatus *:HGStatus* - -Performs "hg status" on the current file. - -:HGUnedit *:HGUnedit* - -Performs "hg unedit" on the current file. Again, yes, the output buffer here -is basically useless. - -:HGUpdate *:HGUpdate* - -Performs "hg update" on the current file. This intentionally does not -automatically reload the current buffer, though vim should prompt the user to -do so if the underlying file is altered by this command. - -:HGVimDiff *:HGVimDiff* - -With no arguments, this prompts the user for a revision and then uses vimdiff -to display the differences between the current file and the specified -revision. If no revision is specified, the most recent version of the file on -the current branch is used. - -With one argument, that argument is used as the revision as above. With two -arguments, the differences between the two revisions is displayed using -vimdiff. - -With either zero or one argument, the original buffer is used to perform the -vimdiff. When the other buffer is closed, the original buffer will be -returned to normal mode. - -Once vimdiff mode is started using the above methods, additional vimdiff -buffers may be added by passing a single version argument to the command. -There may be up to 4 vimdiff buffers total. - -Using the 2-argument form of the command resets the vimdiff to only those 2 -versions. Additionally, invoking the command on a different file will close -the previous vimdiff buffers. - - -4.2 Mappings *hgcommand-mappings* - -By default, a mapping is defined for each command. These mappings execute the -default (no-argument) form of each command. - -<Leader>hga HGAdd -<Leader>hgn HGAnnotate -<Leader>hgc HGCommit -<Leader>hgd HGDiff -<Leader>hgg HGGotoOriginal -<Leader>hgG HGGotoOriginal! -<Leader>hgl HGLog -<Leader>hgr HGReview -<Leader>hgs HGStatus -<Leader>hgt HGUnedit -<Leader>hgu HGUpdate -<Leader>hgv HGVimDiff - - *hgcommand-mappings-override* - -The default mappings can be overriden by user-provided instead by mapping to -<Plug>CommandName. This is especially useful when these mappings collide with -other existing mappings (vim will warn of this during plugin initialization, -but will not clobber the existing mappings). - -For instance, to override the default mapping for :HGAdd to set it to '\add', -add the following to the vimrc: - -nmap \add <Plug>HGAdd - -4.3 Automatic buffer variables *hgcommand-buffer-variables* - -Several buffer variables are defined in each HGCommand result buffer. These -may be useful for additional customization in callbacks defined in the event -handlers (please see |hgcommand-events|). - -The following variables are automatically defined: - -b:hgOrigBuffNR *b:hgOrigBuffNR* - -This variable is set to the buffer number of the source file. - -b:hgcmd *b:hgcmd* - -This variable is set to the name of the hg command that created the result -buffer. -============================================================================== - -5. Configuration and customization *hgcommand-customize* - *hgcommand-config* - -The HGCommand plugin can be configured in two ways: by setting configuration -variables (see |hgcommand-options|) or by defining HGCommand event handlers -(see |hgcommand-events|). Additionally, the HGCommand plugin provides -several option for naming the HG result buffers (see |hgcommand-naming|) and -supported a customized status line (see |hgcommand-statusline| and -|hgcommand-buffer-management|). - -5.1 HGCommand configuration variables *hgcommand-options* - -Several variables affect the plugin's behavior. These variables are checked -at time of execution, and may be defined at the window, buffer, or global -level and are checked in that order of precedence. - - -The following variables are available: - -|HGCommandAnnotateParent| -|HGCommandCommitOnWrite| -|HGCommandHGExec| -|HGCommandDeleteOnHide| -|HGCommandDiffOpt| -|HGCommandDiffSplit| -|HGCommandEdit| -|HGCommandEnableBufferSetup| -|HGCommandInteractive| -|HGCommandNameMarker| -|HGCommandNameResultBuffers| -|HGCommandSplit| - -HGCommandAnnotateParent *HGCommandAnnotateParent* - -This variable, if set to a non-zero value, causes the zero-argument form of -HGAnnotate when invoked on a HGAnnotate buffer to go to the version previous -to that displayed on the current line. If not set, it defaults to 0. - -HGCommandCommitOnWrite *HGCommandCommitOnWrite* - -This variable, if set to a non-zero value, causes the pending hg commit -to take place immediately as soon as the log message buffer is written. -If set to zero, only the HGCommit mapping will cause the pending commit to -occur. If not set, it defaults to 1. - -HGCommandHGExec *HGCommandHGExec* - -This variable controls the executable used for all HG commands If not set, -it defaults to "hg". - -HGCommandDeleteOnHide *HGCommandDeleteOnHide* - -This variable, if set to a non-zero value, causes the temporary HG result -buffers to automatically delete themselves when hidden. - -HGCommandDiffOpt *HGCommandDiffOpt* - -This variable, if set, determines the options passed to the diff command of -HG. If not set, it defaults to 'wbBc'. - -HGCommandDiffSplit *HGCommandDiffSplit* - -This variable overrides the |HGCommandSplit| variable, but only for buffers -created with |:HGVimDiff|. - -HGCommandEdit *HGCommandEdit* - -This variable controls whether the original buffer is replaced ('edit') or -split ('split'). If not set, it defaults to 'edit'. - -HGCommandEnableBufferSetup *HGCommandEnableBufferSetup* - -This variable, if set to a non-zero value, activates HG buffer management -mode see (|hgcommand-buffer-management|). This mode means that two buffer -variables, 'HGRevision' and 'HGBranch', are set if the file is -HG-controlled. This is useful for displaying version information in the -status bar. - -HGCommandInteractive *HGCommandInteractive* - -This variable, if set to a non-zero value, causes appropriate commands (for -the moment, only |:HGReview|) to query the user for a revision to use instead -of the current revision if none is specified. - -HGCommandNameMarker *HGCommandNameMarker* - -This variable, if set, configures the special attention-getting characters -that appear on either side of the hg buffer type in the buffer name. This -has no effect unless |HGCommandNameResultBuffers| is set to a true value. If -not set, it defaults to '_'. - -HGCommandNameResultBuffers *HGCommandNameResultBuffers* - -This variable, if set to a true value, causes the hg result buffers to be -named in the old way ('<source file name> _<hg command>_'). If not set -or set to a false value, the result buffer is nameless. - -HGCommandSplit *HGCommandSplit* - -This variable controls the orientation of the various window splits that -may occur (such as with HGVimDiff, when using a HG command on a HG -command buffer, or when the |HGCommandEdit| variable is set to 'split'. -If set to 'horizontal', the resulting windows will be on stacked on top of -one another. If set to 'vertical', the resulting windows will be -side-by-side. If not set, it defaults to 'horizontal' for all but -HGVimDiff windows. - -5.2 HGCommand events *hgcommand-events* - -For additional customization, HGCommand can trigger user-defined events. -Event handlers are provided by defining User event autocommands (see -|autocommand|, |User|) in the HGCommand group with patterns matching the -event name. - -For instance, the following could be added to the vimrc to provide a 'q' -mapping to quit a HGCommand scratch buffer: - -augroup HGCommand - au HGCommand User HGBufferCreated silent! nmap <unique> <buffer> q: bwipeout<cr> -augroup END - -The following hooks are available: - -HGBufferCreated This event is fired just after a hg command - result buffer is created and filled with the - result of a hg command. It is executed within - the context of the HG command buffer. The - HGCommand buffer variables may be useful for - handlers of this event (please see - |hgcommand-buffer-variables|). - -HGBufferSetup This event is fired just after HG buffer setup - occurs, if enabled. - -HGPluginInit This event is fired when the HGCommand plugin - first loads. - -HGPluginFinish This event is fired just after the HGCommand - plugin loads. - -HGVimDiffFinish This event is fired just after the HGVimDiff - command executes to allow customization of, - for instance, window placement and focus. - -5.3 HGCommand buffer naming *hgcommand-naming* - -By default, the buffers containing the result of HG commands are nameless -scratch buffers. It is intended that buffer variables of those buffers be -used to customize the statusline option so that the user may fully control the -display of result buffers. - -If the old-style naming is desired, please enable the -|HGCommandNameResultBuffers| variable. Then, each result buffer will receive -a unique name that includes the source file name, the HG command, and any -extra data (such as revision numbers) that were part of the command. - -5.4 HGCommand status line support *hgcommand-statusline* - -It is intended that the user will customize the |'statusline'| option to -include HG result buffer attributes. A sample function that may be used in -the |'statusline'| option is provided by the plugin, HGGetStatusLine(). In -order to use that function in the status line, do something like the -following: - -set statusline=%<%f\ %{HGGetStatusLine()}\ %h%m%r%=%l,%c%V\ %P - -of which %{HGGetStatusLine()} is the relevant portion. - -The sample HGGetStatusLine() function handles both HG result buffers and -HG-managed files if HGCommand buffer management is enabled (please see -|hgcommand-buffer-management|). - -5.5 HGCommand buffer management *hgcommand-buffer-management* - -The HGCommand plugin can operate in buffer management mode, which means that -it attempts to set two buffer variables ('HGRevision' and 'HGBranch') upon -entry into a buffer. This is rather slow because it means that 'hg status' -will be invoked at each entry into a buffer (during the |BufEnter| -autocommand). - -This mode is disabled by default. In order to enable it, set the -|HGCommandEnableBufferSetup| variable to a true (non-zero) value. Enabling -this mode simply provides the buffer variables mentioned above. The user must -explicitly include those in the |'statusline'| option if they are to appear in -the status line (but see |hgcommand-statusline| for a simple way to do that). - -============================================================================== - -6. SSH "integration" *hgcommand-ssh* - -The following instructions are intended for use in integrating the -hgcommand.vim plugin with an SSH-based HG environment. - -Familiarity with SSH and HG are assumed. - -These instructions assume that the intent is to have a message box pop up in -order to allow the user to enter a passphrase. If, instead, the user is -comfortable using certificate-based authentication, then only instructions -6.1.1 and 6.1.2 (and optionally 6.1.4) need to be followed; ssh should then -work transparently. - -6.1 Environment settings *hgcommand-ssh-env* - -6.1.1 HGROOT should be set to something like: - - :ext:user@host:/path_to_repository - -6.1.2 HG_RSH should be set to: - - ssh - - Together, those settings tell HG to use ssh as the transport when - performing HG calls. - -6.1.3 SSH_ASKPASS should be set to the password-dialog program. In my case, - running gnome, it's set to: - - /usr/libexec/openssh/gnome-ssh-askpass - - This tells SSH how to get passwords if no input is available. - -6.1.4 OPTIONAL. You may need to set SSH_SERVER to the location of the hg - executable on the remote (server) machine. - -6.2 HG wrapper program *hgcommand-ssh-wrapper* - -Now you need to convince SSH to use the password-dialog program. This means -you need to execute SSH (and therefore HG) without standard input. The -following script is a simple perl wrapper that dissasociates the HG command -from the current terminal. Specific steps to do this may vary from system to -system; the following example works for me on linux. - -#!/usr/bin/perl -w -use strict; -use POSIX qw(setsid); -open STDIN, '/dev/null'; -fork and do {wait; exit;}; -setsid; -exec('hg', @ARGV); - -6.3 Configuring hgcommand.vim *hgcommand-ssh-config* - -At this point, you should be able to use your wrapper script to invoke HG with -various commands, and get the password dialog. All that's left is to make HG -use your newly-created wrapper script. - -6.3.1 Tell hgcommand.vim what HG executable to use. The easiest way to do this - is globally, by putting the following in your .vimrc: - - let HGCommandHGExec=/path/to/hg/wrapper/script - -6.4 Where to go from here *hgcommand-ssh-other* - -The script given above works even when non-SSH HG connections are used, -except possibly when interactively entering the message for HG commit log -(depending on the editor you use... VIM works fine). Since the hgcommand.vim -plugin handles that message without a terminal, the wrapper script can be used -all the time. - -This allows mixed-mode operation, where some work is done with SSH-based HG -repositories, and others with pserver or local access. - -It is possible, though beyond the scope of the plugin, to dynamically set the -HG executable based on the HGROOT for the file being edited. The user -events provided (such as HGBufferCreated and HGBufferSetup) can be used to -set a buffer-local value (b:HGCommandHGExec) to override the HG executable -on a file-by-file basis. Alternatively, much the same can be done (less -automatically) by the various project-oriented plugins out there. - -It is highly recommended for ease-of-use that certificates with no passphrase -or ssh-agent are employed so that the user is not given the password prompt -too often. - -============================================================================== -9. Tips *hgcommand-tips* - -9.1 Split window annotation, by Michael Anderson - -:nmap <Leader>hgN :vs<CR><C-w>h<Leader>hgn:vertical res 40<CR> - \ggdddd:set scb<CR>:set nowrap<CR><C-w>lgg:set scb<CR> - \:set nowrap<CR> - -This splits the buffer vertically, puts an annotation on the left (minus the -header) with the width set to 40. An editable/normal copy is placed on the -right. The two versions are scroll locked so they move as one. and wrapping -is turned off so that the lines line up correctly. The advantages are... - -1) You get a versioning on the right. -2) You can still edit your own code. -3) Your own code still has syntax highlighting. - -============================================================================== - -8. Known bugs *hgcommand-bugs* - -Please let me know if you run across any. - -HGVimDiff, when using the original (real) source buffer as one of the diff -buffers, uses some hacks to try to restore the state of the original buffer -when the scratch buffer containing the other version is destroyed. There may -still be bugs in here, depending on many configuration details. - -vim:tw=78:ts=8:ft=help
--- a/contrib/vim/hgcommand.vim Wed Aug 09 14:53:03 2006 -0500 +++ b/contrib/vim/hgcommand.vim Wed Aug 09 15:03:46 2006 -0500 @@ -3,220 +3,26 @@ " Vim plugin to assist in working with HG-controlled files. " " Last Change: 2006/02/22 -" Version: 1.76 +" Version: 1.77 " Maintainer: Mathieu Clabaut <mathieu.clabaut@gmail.com> " License: This file is placed in the public domain. -" Credits: {{{1 +" Credits: " Bob Hiestand <bob.hiestand@gmail.com> for the fabulous " cvscommand.vim from which this script was directly created by " means of sed commands and minor tweaks. -" Section: Documentation {{{1 -" -" Provides functions to invoke various HG commands on the current file -" (either the current buffer, or, in the case of an directory buffer, the file -" on the current line). The output of the commands is captured in a new -" scratch window. For convenience, if the functions are invoked on a HG -" output window, the original file is used for the hg operation instead after -" the window is split. This is primarily useful when running HGCommit and -" you need to see the changes made, so that HGDiff is usable and shows up in -" another window. -" -" Command documentation {{{2 -" -" HGAdd Performs "hg add" on the current file. -" -" HGAnnotate Performs "hg annotate" on the current file. If an -" argument is given, the argument is used as a revision -" number to display. If not given an argument, it uses the -" most recent version of the file on the current branch. -" Additionally, if the current buffer is a HGAnnotate buffer -" already, the version number on the current line is used. -" -" If the 'HGCommandAnnotateParent' variable is set to a -" non-zero value, the version previous to the one on the -" current line is used instead. This allows one to navigate -" back to examine the previous version of a line. -" -" HGCommit[!] If called with arguments, this performs "hg commit" using -" the arguments as the log message. -" -" If '!' is used, an empty log message is committed. -" -" If called with no arguments, this is a two-step command. -" The first step opens a buffer to accept a log message. -" When that buffer is written, it is automatically closed and -" the file is committed using the information from that log -" message. The commit can be abandoned if the log message -" buffer is deleted or wiped before being written. -" -" HGDiff With no arguments, this performs "hg diff" on the current -" file. With one argument, "hg diff" is performed on the -" current file against the specified revision. With two -" arguments, hg diff is performed between the specified -" revisions of the current file. This command uses the -" 'HGCommandDiffOpt' variable to specify diff options. If -" that variable does not exist, then 'wbBc' is assumed. If -" you wish to have no options, then set it to the empty -" string. +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " -" HGGotoOriginal Returns the current window to the source buffer if the -" current buffer is a HG output buffer. -" -" HGLog Performs "hg log" on the current file. -" -" HGRevert Replaces the modified version of the current file with the -" most recent version from the repository. -" -" HGReview Retrieves a particular version of the current file. If no -" argument is given, the most recent version of the file on -" the current branch is retrieved. The specified revision is -" retrieved into a new buffer. -" -" HGStatus Performs "hg status" on the current file. -" -" HGUpdate Performs "hg update" on the current file. -" -" HGVimDiff With no arguments, this prompts the user for a revision and -" then uses vimdiff to display the differences between the -" current file and the specified revision. If no revision is -" specified, the most recent version of the file on the -" current branch is used. With one argument, that argument -" is used as the revision as above. With two arguments, the -" differences between the two revisions is displayed using -" vimdiff. -" -" With either zero or one argument, the original buffer is used -" to perform the vimdiff. When the other buffer is closed, the -" original buffer will be returned to normal mode. -" -" Once vimdiff mode is started using the above methods, -" additional vimdiff buffers may be added by passing a single -" version argument to the command. There may be up to 4 -" vimdiff buffers total. -" -" Using the 2-argument form of the command resets the vimdiff -" to only those 2 versions. Additionally, invoking the -" command on a different file will close the previous vimdiff -" buffers. -" -" -" Mapping documentation: {{{2 -" -" By default, a mapping is defined for each command. User-provided mappings -" can be used instead by mapping to <Plug>CommandName, for instance: -" -" nnoremap ,ca <Plug>HGAdd -" -" The default mappings are as follow: +" Section: Documentation +"---------------------------- " -" <Leader>hga HGAdd -" <Leader>hgn HGAnnotate -" <Leader>hgc HGCommit -" <Leader>hgd HGDiff -" <Leader>hgg HGGotoOriginal -" <Leader>hgG HGGotoOriginal! -" <Leader>hgl HGLog -" <Leader>hgr HGReview -" <Leader>hgs HGStatus -" <Leader>hgu HGUpdate -" <Leader>hgv HGVimDiff -" -" Options documentation: {{{2 -" -" Several variables are checked by the script to determine behavior as follow: -" -" HGCommandAnnotateParent -" This variable, if set to a non-zero value, causes the zero-argument form -" of HGAnnotate when invoked on a HGAnnotate buffer to go to the version -" previous to that displayed on the current line. If not set, it defaults -" to 0. -" -" HGCommandCommitOnWrite -" This variable, if set to a non-zero value, causes the pending hg commit -" to take place immediately as soon as the log message buffer is written. -" If set to zero, only the HGCommit mapping will cause the pending commit -" to occur. If not set, it defaults to 1. -" -" HGCommandDeleteOnHide -" This variable, if set to a non-zero value, causes the temporary HG result -" buffers to automatically delete themselves when hidden. -" -" HGCommandDiffOpt -" This variable, if set, determines the options passed to the diff command -" of HG. If not set, it defaults to 'wbBc'. -" -" HGCommandDiffSplit -" This variable overrides the HGCommandSplit variable, but only for buffers -" created with HGVimDiff. -" -" HGCommandEdit -" This variable controls whether the original buffer is replaced ('edit') or -" split ('split'). If not set, it defaults to 'edit'. -" -" HGCommandEnableBufferSetup -" This variable, if set to a non-zero value, activates HG buffer management -" mode. This mode means that two buffer variables, 'HGRevision' and -" 'HGBranch', are set if the file is HG-controlled. This is useful for -" displaying version information in the status bar. +" Documentation should be available by ":help hgcommand" command, once the +" script has been copied in you .vim/plugin directory. " -" HGCommandInteractive -" This variable, if set to a non-zero value, causes appropriate functions (for -" the moment, only HGReview) to query the user for a revision to use -" instead of the current revision if none is specified. -" -" HGCommandNameMarker -" This variable, if set, configures the special attention-getting characters -" that appear on either side of the hg buffer type in the buffer name. -" This has no effect unless 'HGCommandNameResultBuffers' is set to a true -" value. If not set, it defaults to '_'. -" -" HGCommandNameResultBuffers -" This variable, if set to a true value, causes the hg result buffers to be -" named in the old way ('<source file name> _<hg command>_'). If not set -" or set to a false value, the result buffer is nameless. -" -" HGCommandSplit -" This variable controls the orientation of the various window splits that -" may occur (such as with HGVimDiff, when using a HG command on a HG -" command buffer, or when the 'HGCommandEdit' variable is set to 'split'. -" If set to 'horizontal', the resulting windows will be on stacked on top of -" one another. If set to 'vertical', the resulting windows will be -" side-by-side. If not set, it defaults to 'horizontal' for all but -" HGVimDiff windows. -" -" Event documentation {{{2 -" For additional customization, hgcommand.vim uses User event autocommand -" hooks. Each event is in the HGCommand group, and different patterns -" match the various hooks. -" -" For instance, the following could be added to the vimrc to provide a 'q' -" mapping to quit a HG buffer: -" -" augroup HGCommand -" au HGCommand User HGBufferCreated silent! nmap <unique> <buffer> q :bwipeout<cr> -" augroup END -" -" The following hooks are available: -" -" HGBufferCreated This event is fired just after a hg command -" result buffer is created and filled with the -" result of a hg command. It is executed within -" the context of the new buffer. -" -" HGBufferSetup This event is fired just after HG buffer setup -" occurs, if enabled. -" -" HGPluginInit This event is fired when the HGCommand plugin -" first loads. -" -" HGPluginFinish This event is fired just after the HGCommand -" plugin loads. -" -" HGVimDiffFinish This event is fired just after the HGVimDiff -" command executes to allow customization of, -" for instance, window placement and focus. -" +" You still can read the documentation at the end of this file. Locate it by +" searching the "hgcommand-contents" string (and set ft=help to have +" appropriate syntaxic coloration). + " Section: Plugin header {{{1 " loaded_hgcommand is set to 1 when the initialization begins, and 2 when it @@ -228,11 +34,33 @@ endif let loaded_hgcommand = 1 +" store 'compatible' settings +let s:save_cpo = &cpo +set cpo&vim + +" run checks +let s:script_name = expand("<sfile>:t:r") + +function! s:HGCleanupOnFailure(err) + echohl WarningMsg + echomsg s:script_name . ":" a:err "Plugin not loaded" + echohl None + let loaded_hgcommand = "no" + unlet s:save_cpo s:script_name +endfunction + if v:version < 602 - echohl WarningMsg|echomsg "HGCommand 1.69 or later requires VIM 6.2 or later"|echohl None + call <SID>HGCleanupOnFailure("VIM 6.2 or later required.") finish endif +if !exists("*system") + call <SID>HGCleanupOnFailure("builtin system() function required.") + finish +endif + +let s:script_version = "v0.2" + " Section: Event group setup {{{1 augroup HGCommand @@ -257,7 +85,7 @@ function! s:HGResolveLink(fileName) let resolved = resolve(a:fileName) if resolved != a:fileName - let resolved = s:HGResolveLink(resolved) + let resolved = <SID>HGResolveLink(resolved) endif return resolved endfunction @@ -268,7 +96,7 @@ function! s:HGChangeToCurrentFileDir(fileName) let oldCwd=getcwd() - let fileName=s:HGResolveLink(a:fileName) + let fileName=<SID>HGResolveLink(a:fileName) let newCwd=fnamemodify(fileName, ':h') if strlen(newCwd) > 0 execute 'cd' escape(newCwd, ' ') @@ -276,7 +104,7 @@ return oldCwd endfunction -" Function: s:HGGetOption(name, default) {{{2 +" Function: <SID>HGGetOption(name, default) {{{2 " Grab a user-specified option to override the default provided. Options are " searched in the window, buffer, then global spaces. @@ -304,9 +132,9 @@ "Name parameter will be pasted into expression. let name = escape(a:name, ' *?\') - let editCommand = s:HGGetOption('HGCommandEdit', 'edit') + let editCommand = <SID>HGGetOption('HGCommandEdit', 'edit') if editCommand != 'edit' - if s:HGGetOption('HGCommandSplit', 'horizontal') == 'horizontal' + if <SID>HGGetOption('HGCommandSplit', 'horizontal') == 'horizontal' if name == "" let editCommand = 'rightbelow new' else @@ -348,8 +176,8 @@ let resultBufferName='' - if s:HGGetOption("HGCommandNameResultBuffers", 0) - let nameMarker = s:HGGetOption("HGCommandNameMarker", '_') + if <SID>HGGetOption("HGCommandNameResultBuffers", 0) + let nameMarker = <SID>HGGetOption("HGCommandNameMarker", '_') if strlen(a:statusText) > 0 let bufName=a:cmdName . ' -- ' . a:statusText else @@ -364,8 +192,8 @@ endwhile endif - let hgCommand = s:HGGetOption("HGCommandHGExec", "hg") . " " . a:cmd - echomsg "DBG :".hgCommand + let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " " . a:cmd + "echomsg "DBG :".hgCommand let hgOut = system(hgCommand) " HACK: diff command does not return proper error codes if v:shell_error && a:cmdName != 'hgdiff' @@ -386,7 +214,7 @@ return -1 endif - if s:HGEditFile(resultBufferName, a:origBuffNR) == -1 + if <SID>HGEditFile(resultBufferName, a:origBuffNR) == -1 return -1 endif @@ -394,7 +222,7 @@ set noswapfile set filetype= - if s:HGGetOption("HGCommandDeleteOnHide", 0) + if <SID>HGGetOption("HGCommandDeleteOnHide", 0) set bufhidden=delete endif @@ -407,8 +235,8 @@ " This could be fixed by explicitly detecting whether the last line is " within a fold, but I prefer to simply unfold the result buffer altogether. - if has('folding') - normal zR + if has("folding") + setlocal nofoldenable endif $d @@ -437,7 +265,7 @@ return origBuffer else " Original buffer no longer exists. - return -1 + return -1 endif else " No original buffer @@ -450,7 +278,7 @@ " for the current buffer. function! s:HGCurrentBufferCheck() - return s:HGBufferCheck(bufnr("%")) + return <SID>HGBufferCheck(bufnr("%")) endfunction " Function: s:HGToggleDeleteOnHide() {{{2 @@ -469,8 +297,8 @@ " Returns: name of the new command buffer containing the command results function! s:HGDoCommand(cmd, cmdName, statusText) - let hgBufferCheck=s:HGCurrentBufferCheck() - if hgBufferCheck == -1 + let hgBufferCheck=<SID>HGCurrentBufferCheck() + if hgBufferCheck == -1 echo "Original buffer no longer exists, aborting." return -1 endif @@ -479,8 +307,8 @@ if isdirectory(fileName) let fileName=fileName . "/" . getline(".") endif - let realFileName = fnamemodify(s:HGResolveLink(fileName), ':t') - let oldCwd=s:HGChangeToCurrentFileDir(fileName) + let realFileName = fnamemodify(<SID>HGResolveLink(fileName), ':t') + let oldCwd=<SID>HGChangeToCurrentFileDir(fileName) try " TODO "if !filereadable('HG/Root') @@ -488,7 +316,7 @@ "endif let fullCmd = a:cmd . ' "' . realFileName . '"' "echomsg "DEBUG".fullCmd - let resultBuffer=s:HGCreateCommandBuffer(fullCmd, a:cmdName, a:statusText, hgBufferCheck) + let resultBuffer=<SID>HGCreateCommandBuffer(fullCmd, a:cmdName, a:statusText, hgBufferCheck) return resultBuffer catch echoerr v:exception @@ -508,58 +336,58 @@ " Returns: string to be exec'd that sets the multiple return values. function! s:HGGetStatusVars(revisionVar, branchVar, repositoryVar) - let hgBufferCheck=s:HGCurrentBufferCheck() - if hgBufferCheck == -1 + let hgBufferCheck=<SID>HGCurrentBufferCheck() + "echomsg "DBG : in HGGetStatusVars" + if hgBufferCheck == -1 return "" endif let fileName=bufname(hgBufferCheck) - let realFileName = fnamemodify(s:HGResolveLink(fileName), ':t') - let oldCwd=s:HGChangeToCurrentFileDir(fileName) + let fileNameWithoutLink=<SID>HGResolveLink(fileName) + let realFileName = fnamemodify(fileNameWithoutLink, ':t') + let oldCwd=<SID>HGChangeToCurrentFileDir(realFileName) try - ""TODO - "if !filereadable('HG/Root') - "return "" - "endif - let hgCommand = s:HGGetOption("HGCommandHGExec", "hg") . " status -mardui " . fileName + let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " root " + let roottext=system(hgCommand) + " Suppress ending null char ! Does it work in window ? + let roottext=substitute(roottext,'^.*/\([^/\n\r]*\)\n\_.*$','\1','') + if match(getcwd()."/".fileNameWithoutLink, roottext) == -1 + return "" + endif + let returnExpression = "" + if a:repositoryVar != "" + let returnExpression=returnExpression . " | let " . a:repositoryVar . "='" . roottext . "'" + endif + let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " status -mardui " . realFileName let statustext=system(hgCommand) if(v:shell_error) return "" endif - if match(statustext, '^[?I]') >= 0 + if match(statustext, '^[?I]') >= 0 let revision="NEW" - elseif match(statustext, '^[R]') >= 0 + elseif match(statustext, '^[R]') >= 0 let revision="REMOVED" - elseif match(statustext, '^[D]') >= 0 + elseif match(statustext, '^[D]') >= 0 let revision="DELETED" - elseif match(statustext, '^[A]') >= 0 + elseif match(statustext, '^[A]') >= 0 let revision="ADDED" - endif + else + " The file is tracked, we can try to get is revision number + let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " parents -b " + let statustext=system(hgCommand) + if(v:shell_error) + return "" + endif + let revision=substitute(statustext, '^changeset:\s*\(\d\+\):.*\_$\_.*$', '\1', "") - let hgCommand = s:HGGetOption("HGCommandHGExec", "hg") . " parents -b " - let statustext=system(hgCommand) - if(v:shell_error) - return "" + if a:branchVar != "" && match(statustext, '^\_.*\_^branch:') >= 0 + let branch=substitute(statustext, '^\_.*\_^branch:\s*\(\S\+\)\n\_.*$', '\1', "") + let returnExpression=returnExpression . " | let " . a:branchVar . "='" . branch . "'" + endif endif - if exists('revision') - let returnExpression = "let " . a:revisionVar . "='" . revision . "'" - else - let revision=substitute(statustext, '^changeset:\s*\(\d\+\):.*\_$\_.*$', '\1', "") - let returnExpression = "let " . a:revisionVar . "='" . revision . "'" + if (exists('revision')) + let returnExpression = "let " . a:revisionVar . "='" . revision . "' " . returnExpression endif - if a:branchVar != "" && match(statustext, '^\_.*\_^branch:') >= 0 - let branch=substitute(statustext, '^\_.*\_^branch:\s*\(\S\+\)\n\_.*$', '\1', "") - let returnExpression=returnExpression . " | let " . a:branchVar . "='" . branch . "'" - endif - if a:repositoryVar != "" - let hgCommand = s:HGGetOption("HGCommandHGExec", "hg") . " root " - let roottext=system(hgCommand) - let repository=substitute(roottext,'^.*/\([^/\n\r]*\)\n\_.*$','\1','') - let returnExpression=returnExpression . " | let " . a:repositoryVar . "='" . repository . "'" - endif - - - return returnExpression finally execute 'cd' escape(oldCwd, ' ') @@ -569,13 +397,13 @@ " Function: s:HGSetupBuffer() {{{2 " Attempts to set the b:HGBranch, b:HGRevision and b:HGRepository variables. -function! s:HGSetupBuffer() - if (exists("b:HGBufferSetup") && b:HGBufferSetup) +function! s:HGSetupBuffer(...) + if (exists("b:HGBufferSetup") && b:HGBufferSetup && !exists('a:1')) " This buffer is already set up. return endif - if !s:HGGetOption("HGCommandEnableBufferSetup", 0) + if !<SID>HGGetOption("HGCommandEnableBufferSetup", 0) \ || @% == "" \ || s:HGCommandEditFileRunning > 0 \ || exists("b:HGOrigBuffNR") @@ -593,7 +421,7 @@ let branch="" let repository="" - exec s:HGGetStatusVars('revision', 'branch', 'repository') + exec <SID>HGGetStatusVars('revision', 'branch', 'repository') "echomsg "DBG ".revision."#".branch."#".repository if revision != "" let b:HGRevision=revision @@ -621,11 +449,16 @@ function! s:HGMarkOrigBufferForSetup(hgBuffer) checktime if a:hgBuffer != -1 - let origBuffer = s:HGBufferCheck(a:hgBuffer) + let origBuffer = <SID>HGBufferCheck(a:hgBuffer) "This should never not work, but I'm paranoid if origBuffer != a:hgBuffer call setbufvar(origBuffer, "HGBufferSetup", 0) endif + else + "We are presumably in the original buffer + let b:HGBufferSetup = 0 + "We do the setup now as now event will be triggered allowing it later. + call <SID>HGSetupBuffer() endif return a:hgBuffer endfunction @@ -657,6 +490,105 @@ endwhile endfunction +" Function: s:HGInstallDocumentation(full_name, revision) {{{2 +" Install help documentation. +" Arguments: +" full_name: Full name of this vim plugin script, including path name. +" revision: Revision of the vim script. #version# mark in the document file +" will be replaced with this string with 'v' prefix. +" Return: +" 1 if new document installed, 0 otherwise. +" Note: Cleaned and generalized by guo-peng Wen +"''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +" Helper function to make mkdir as portable as possible +function! s:HGFlexiMkdir(dir) + if exists("*mkdir") " we can use Vim's own mkdir() + call mkdir(a:dir) + elseif !exists("+shellslash") + call system("mkdir -p '".a:dir."'") + else " M$ + let l:ssl = &shellslash + try + set shellslash + " no single quotes? + call system('mkdir "'.a:dir.'"') + finally + let &shellslash = l:ssl + endtry + endif +endfunction + +function! s:HGInstallDocumentation(full_name) + " Figure out document path based on full name of this script: + let l:vim_doc_path = fnamemodify(a:full_name, ":h:h") . "/doc" + if filewritable(l:vim_doc_path) != 2 + echomsg s:script_name . ": Trying to update docs at" l:vim_doc_path + silent! call <SID>HGFlexiMkdir(l:vim_doc_path) + if filewritable(l:vim_doc_path) != 2 + " Try first item in 'runtimepath': + let l:vim_doc_path = + \ substitute(&runtimepath, '^\([^,]*\).*', '\1/doc', 'e') + if filewritable(l:vim_doc_path) != 2 + echomsg s:script_name . ": Trying to update docs at" l:vim_doc_path + silent! call <SID>HGFlexiMkdir(l:vim_doc_path) + if filewritable(l:vim_doc_path) != 2 + " Put a warning: + echomsg "Unable to open documentation directory" + echomsg " type `:help add-local-help' for more information." + return 0 + endif + endif + endif + endif + + " Full name of documentation file: + let l:doc_file = + \ l:vim_doc_path . "/" . s:script_name . ".txt" + " Bail out if document file is still up to date: + if filereadable(l:doc_file) && + \ getftime(a:full_name) < getftime(l:doc_file) + return 0 + endif + + " temporary global settings + let l:lz = &lazyredraw + let l:hls = &hlsearch + set lazyredraw nohlsearch + " Create a new buffer & read in the plugin file (me): + 1 new + setlocal noswapfile modifiable nomodeline + if has("folding") + setlocal nofoldenable + endif + silent execute "read" escape(a:full_name, " ") + let l:doc_buf = bufnr("%") + + 1 + " Delete from first line to a line starts with + " === START_DOC + silent 1,/^=\{3,}\s\+START_DOC\C/ d + " Delete from a line starts with + " === END_DOC + " to the end of the documents: + silent /^=\{3,}\s\+END_DOC\C/,$ d + + " Add modeline for help doc: the modeline string is mangled intentionally + " to avoid it be recognized by VIM: + call append(line("$"), "") + call append(line("$"), " v" . "im:tw=78:ts=8:ft=help:norl:") + + " Replace revision: + silent execute "normal :1s/#version#/" . s:script_version . "/\<CR>" + " Save the help document and wipe out buffer: + silent execute "wq!" escape(l:doc_file, " ") "| bw" l:doc_buf + " Build help tags: + silent execute "helptags" l:vim_doc_path + + let &hlsearch = l:hls + let &lazyredraw = l:lz + return 1 +endfunction + " Section: Public functions {{{1 " Function: HGGetRevision() {{{2 @@ -665,7 +597,7 @@ function! HGGetRevision() let revision="" - exec s:HGGetStatusVars('revision', '', '') + exec <SID>HGGetStatusVars('revision', '', '') return revision endfunction @@ -684,13 +616,16 @@ let g:HGCommandEnableBufferSetup=1 augroup HGCommandPlugin au! - au BufEnter * call s:HGSetupBuffer() + au BufEnter * call <SID>HGSetupBuffer() + au BufWritePost * call <SID>HGSetupBuffer() + " Force resetting up buffer on external file change (HG update) + au FileChangedShell * call <SID>HGSetupBuffer(1) augroup END " Only auto-load if the plugin is fully loaded. This gives other plugins a " chance to run. if g:loaded_hgcommand == 2 - call s:HGSetupBuffer() + call <SID>HGSetupBuffer() endif endfunction @@ -712,13 +647,16 @@ if exists('b:HGRevision') \ && b:HGRevision != '' - \ && exists('b:HGBranch') - \ && b:HGBranch != '' \ && exists('b:HGRepository') \ && b:HGRepository != '' \ && exists('g:HGCommandEnableBufferSetup') \ && g:HGCommandEnableBufferSetup - return '[HG ' . b:HGRepository . '/' . b:HGBranch .'/' . b:HGRevision . ']' + if !exists('b:HGBranch') + let l:branch='' + else + let l:branch=b:HGBranch + endif + return '[HG ' . b:HGRepository . '/' . l:branch .'/' . b:HGRevision . ']' else return '' endif @@ -728,7 +666,7 @@ " Function: s:HGAdd() {{{2 function! s:HGAdd() - return s:HGMarkOrigBufferForSetup(s:HGDoCommand('add', 'hgadd', '')) + return <SID>HGMarkOrigBufferForSetup(<SID>HGDoCommand('add', 'hgadd', '')) endfunction " Function: s:HGAnnotate(...) {{{2 @@ -738,7 +676,7 @@ " This is a HGAnnotate buffer. Perform annotation of the version " indicated by the current line. let revision = substitute(getline("."),'\(^[0-9]*\):.*','\1','') - if s:HGGetOption('HGCommandAnnotateParent', 0) != 0 && revision > 0 + if <SID>HGGetOption('HGCommandAnnotateParent', 0) != 0 && revision > 0 let revision = revision - 1 endif else @@ -757,8 +695,8 @@ return -1 endif - let resultBuffer=s:HGDoCommand('annotate -ndu -r ' . revision, 'hgannotate', revision) - echomsg "DBG: ".resultBuffer + let resultBuffer=<SID>HGDoCommand('annotate -ndu -r ' . revision, 'hgannotate', revision) + "echomsg "DBG: ".resultBuffer if resultBuffer != -1 set filetype=HGAnnotate endif @@ -772,10 +710,10 @@ " is used; if bang is supplied, an empty message is used; otherwise, the " user is provided a buffer from which to edit the commit message. if a:2 != "" || a:1 == "!" - return s:HGMarkOrigBufferForSetup(s:HGDoCommand('commit -m "' . a:2 . '"', 'hgcommit', '')) + return <SID>HGMarkOrigBufferForSetup(<SID>HGDoCommand('commit -m "' . a:2 . '"', 'hgcommit', '')) endif - let hgBufferCheck=s:HGCurrentBufferCheck() + let hgBufferCheck=<SID>HGCurrentBufferCheck() if hgBufferCheck == -1 echo "Original buffer no longer exists, aborting." return -1 @@ -791,7 +729,7 @@ let messageFileName = tempname() let fileName=bufname(hgBufferCheck) - let realFilePath=s:HGResolveLink(fileName) + let realFilePath=<SID>HGResolveLink(fileName) let newCwd=fnamemodify(realFilePath, ':h') if strlen(newCwd) == 0 " Account for autochdir being in effect, which will make this blank, but @@ -801,7 +739,7 @@ let realFileName=fnamemodify(realFilePath, ':t') - if s:HGEditFile(messageFileName, hgBufferCheck) == -1 + if <SID>HGEditFile(messageFileName, hgBufferCheck) == -1 return endif @@ -832,9 +770,9 @@ silent put =\"HG: Enter Log. Lines beginning with `HG:' are removed automatically\" silent put ='HG: Type <leader>cc (or your own <Plug>HGCommit mapping)' - if s:HGGetOption('HGCommandCommitOnWrite', 1) == 1 + if <SID>HGGetOption('HGCommandCommitOnWrite', 1) == 1 execute 'au HGCommit BufWritePre' autoPattern 'g/^HG:/d' - execute 'au HGCommit BufWritePost' autoPattern 'call s:HGFinishCommit("' . messageFileName . '", "' . newCwd . '", "' . realFileName . '", ' . hgBufferCheck . ') | au! * ' autoPattern + execute 'au HGCommit BufWritePost' autoPattern 'call <SID>HGFinishCommit("' . messageFileName . '", "' . newCwd . '", "' . realFileName . '", ' . hgBufferCheck . ') | au! * ' autoPattern silent put ='HG: or write this buffer' endif @@ -863,7 +801,7 @@ let caption = '' endif - let hgdiffopt=s:HGGetOption('HGCommandDiffOpt', 'w') + let hgdiffopt=<SID>HGGetOption('HGCommandDiffOpt', 'w') if hgdiffopt == "" let diffoptionstring="" @@ -871,8 +809,8 @@ let diffoptionstring=" -" . hgdiffopt . " " endif - let resultBuffer = s:HGDoCommand('diff ' . diffoptionstring . revOptions , 'hgdiff', caption) - if resultBuffer != -1 + let resultBuffer = <SID>HGDoCommand('diff ' . diffoptionstring . revOptions , 'hgdiff', caption) + if resultBuffer != -1 set filetype=diff endif return resultBuffer @@ -881,7 +819,7 @@ " Function: s:HGGotoOriginal(["!]) {{{2 function! s:HGGotoOriginal(...) - let origBuffNR = s:HGCurrentBufferCheck() + let origBuffNR = <SID>HGCurrentBufferCheck() if origBuffNR > 0 let origWinNR = bufwinnr(origBuffNR) if origWinNR == -1 @@ -911,11 +849,11 @@ if strlen(a:targetDir) > 0 execute 'cd' escape(a:targetDir, ' ') endif - let resultBuffer=s:HGCreateCommandBuffer('commit -F "' . a:messageFile . '" "'. a:targetFile . '"', 'hgcommit', '', a:origBuffNR) + let resultBuffer=<SID>HGCreateCommandBuffer('commit -l "' . a:messageFile . '" "'. a:targetFile . '"', 'hgcommit', '', a:origBuffNR) execute 'cd' escape(oldCwd, ' ') execute 'bw' escape(a:messageFile, ' *?\') silent execute 'call delete("' . a:messageFile . '")' - return s:HGMarkOrigBufferForSetup(resultBuffer) + return <SID>HGMarkOrigBufferForSetup(resultBuffer) else echoerr "Can't read message file; no commit is possible." return -1 @@ -932,7 +870,7 @@ let caption = a:1 endif - let resultBuffer=s:HGDoCommand('log' . versionOption, 'hglog', caption) + let resultBuffer=<SID>HGDoCommand('log' . versionOption, 'hglog', caption) if resultBuffer != "" set filetype=rcslog endif @@ -941,14 +879,14 @@ " Function: s:HGRevert() {{{2 function! s:HGRevert() - return s:HGMarkOrigBufferForSetup(s:HGDoCommand('revert', 'hgrevert', '')) + return <SID>HGMarkOrigBufferForSetup(<SID>HGDoCommand('revert', 'hgrevert', '')) endfunction " Function: s:HGReview(...) {{{2 function! s:HGReview(...) if a:0 == 0 let versiontag="" - if s:HGGetOption('HGCommandInteractive', 0) + if <SID>HGGetOption('HGCommandInteractive', 0) let versiontag=input('Revision: ') endif if versiontag == "" @@ -962,7 +900,7 @@ let versionOption=" -r " . versiontag . " " endif - let resultBuffer = s:HGDoCommand('cat' . versionOption, 'hgreview', versiontag) + let resultBuffer = <SID>HGDoCommand('cat' . versionOption, 'hgreview', versiontag) if resultBuffer > 0 let &filetype=getbufvar(b:HGOrigBuffNR, '&filetype') endif @@ -972,18 +910,18 @@ " Function: s:HGStatus() {{{2 function! s:HGStatus() - return s:HGDoCommand('status', 'hgstatus', '') + return <SID>HGDoCommand('status', 'hgstatus', '') endfunction " Function: s:HGUpdate() {{{2 function! s:HGUpdate() - return s:HGMarkOrigBufferForSetup(s:HGDoCommand('update', 'update', '')) + return <SID>HGMarkOrigBufferForSetup(<SID>HGDoCommand('update', 'update', '')) endfunction " Function: s:HGVimDiff(...) {{{2 function! s:HGVimDiff(...) - let originalBuffer = s:HGCurrentBufferCheck() + let originalBuffer = <SID>HGCurrentBufferCheck() let s:HGCommandEditFileRunning = s:HGCommandEditFileRunning + 1 try " If there's already a VimDiff'ed window, restore it. @@ -991,16 +929,16 @@ if exists("s:vimDiffSourceBuffer") && s:vimDiffSourceBuffer != originalBuffer " Clear the existing vimdiff setup by removing the result buffers. - call s:HGWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff') + call <SID>HGWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff') endif " Split and diff if(a:0 == 2) " Reset the vimdiff system, as 2 explicit versions were provided. if exists('s:vimDiffSourceBuffer') - call s:HGWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff') + call <SID>HGWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff') endif - let resultBuffer = s:HGReview(a:1) + let resultBuffer = <SID>HGReview(a:1) if resultBuffer < 0 echomsg "Can't open HG revision " . a:1 return resultBuffer @@ -1011,10 +949,10 @@ let s:vimDiffScratchList = '{'. resultBuffer . '}' " If no split method is defined, cheat, and set it to vertical. try - call s:HGOverrideOption('HGCommandSplit', s:HGGetOption('HGCommandDiffSplit', s:HGGetOption('HGCommandSplit', 'vertical'))) - let resultBuffer=s:HGReview(a:2) + call <SID>HGOverrideOption('HGCommandSplit', <SID>HGGetOption('HGCommandDiffSplit', <SID>HGGetOption('HGCommandSplit', 'vertical'))) + let resultBuffer=<SID>HGReview(a:2) finally - call s:HGOverrideOption('HGCommandSplit') + call <SID>HGOverrideOption('HGCommandSplit') endtry if resultBuffer < 0 echomsg "Can't open HG revision " . a:1 @@ -1028,16 +966,16 @@ " Add new buffer try " Force splitting behavior, otherwise why use vimdiff? - call s:HGOverrideOption("HGCommandEdit", "split") - call s:HGOverrideOption("HGCommandSplit", s:HGGetOption('HGCommandDiffSplit', s:HGGetOption('HGCommandSplit', 'vertical'))) + call <SID>HGOverrideOption("HGCommandEdit", "split") + call <SID>HGOverrideOption("HGCommandSplit", <SID>HGGetOption('HGCommandDiffSplit', <SID>HGGetOption('HGCommandSplit', 'vertical'))) if(a:0 == 0) - let resultBuffer=s:HGReview() + let resultBuffer=<SID>HGReview() else - let resultBuffer=s:HGReview(a:1) + let resultBuffer=<SID>HGReview(a:1) endif finally - call s:HGOverrideOption("HGCommandEdit") - call s:HGOverrideOption("HGCommandSplit") + call <SID>HGOverrideOption("HGCommandEdit") + call <SID>HGOverrideOption("HGCommandSplit") endtry if resultBuffer < 0 echomsg "Can't open current HG revision" @@ -1056,14 +994,14 @@ wincmd W execute 'buffer' originalBuffer " Store info for later original buffer restore - let s:vimDiffRestoreCmd = + let s:vimDiffRestoreCmd = \ "call setbufvar(".originalBuffer.", \"&diff\", ".getbufvar(originalBuffer, '&diff').")" \ . "|call setbufvar(".originalBuffer.", \"&foldcolumn\", ".getbufvar(originalBuffer, '&foldcolumn').")" \ . "|call setbufvar(".originalBuffer.", \"&foldenable\", ".getbufvar(originalBuffer, '&foldenable').")" \ . "|call setbufvar(".originalBuffer.", \"&foldmethod\", '".getbufvar(originalBuffer, '&foldmethod')."')" \ . "|call setbufvar(".originalBuffer.", \"&scrollbind\", ".getbufvar(originalBuffer, '&scrollbind').")" \ . "|call setbufvar(".originalBuffer.", \"&wrap\", ".getbufvar(originalBuffer, '&wrap').")" - \ . "|if &foldmethod=='manual'|execute 'normal zE'|endif" + \ . "|if &foldmethod=='manual'|execute 'normal! zE'|endif" diffthis wincmd w else @@ -1093,17 +1031,17 @@ " Section: Command definitions {{{1 " Section: Primary commands {{{2 -com! HGAdd call s:HGAdd() -com! -nargs=? HGAnnotate call s:HGAnnotate(<f-args>) -com! -bang -nargs=? HGCommit call s:HGCommit(<q-bang>, <q-args>) -com! -nargs=* HGDiff call s:HGDiff(<f-args>) -com! -bang HGGotoOriginal call s:HGGotoOriginal(<q-bang>) -com! -nargs=? HGLog call s:HGLog(<f-args>) -com! HGRevert call s:HGRevert() -com! -nargs=? HGReview call s:HGReview(<f-args>) -com! HGStatus call s:HGStatus() -com! HGUpdate call s:HGUpdate() -com! -nargs=* HGVimDiff call s:HGVimDiff(<f-args>) +com! HGAdd call <SID>HGAdd() +com! -nargs=? HGAnnotate call <SID>HGAnnotate(<f-args>) +com! -bang -nargs=? HGCommit call <SID>HGCommit(<q-bang>, <q-args>) +com! -nargs=* HGDiff call <SID>HGDiff(<f-args>) +com! -bang HGGotoOriginal call <SID>HGGotoOriginal(<q-bang>) +com! -nargs=? HGLog call <SID>HGLog(<f-args>) +com! HGRevert call <SID>HGRevert() +com! -nargs=? HGReview call <SID>HGReview(<f-args>) +com! HGStatus call <SID>HGStatus() +com! HGUpdate call <SID>HGUpdate() +com! -nargs=* HGVimDiff call <SID>HGVimDiff(<f-args>) " Section: HG buffer management commands {{{2 com! HGDisableBufferSetup call HGDisableBufferSetup() @@ -1125,11 +1063,6 @@ nnoremap <silent> <Plug>HGStatus :HGStatus<CR> nnoremap <silent> <Plug>HGUpdate :HGUpdate<CR> nnoremap <silent> <Plug>HGVimDiff :HGVimDiff<CR> -nnoremap <silent> <Plug>HGWatchers :HGWatchers<CR> -nnoremap <silent> <Plug>HGWatchAdd :HGWatchAdd<CR> -nnoremap <silent> <Plug>HGWatchOn :HGWatchOn<CR> -nnoremap <silent> <Plug>HGWatchOff :HGWatchOff<CR> -nnoremap <silent> <Plug>HGWatchRemove :HGWatchRemove<CR> " Section: Default mappings {{{1 if !hasmapto('<Plug>HGAdd') @@ -1181,11 +1114,6 @@ amenu <silent> &Plugin.HG.&Status <Plug>HGStatus amenu <silent> &Plugin.HG.&Update <Plug>HGUpdate amenu <silent> &Plugin.HG.&VimDiff <Plug>HGVimDiff -amenu <silent> &Plugin.HG.&Watchers <Plug>HGWatchers -amenu <silent> &Plugin.HG.WatchAdd <Plug>HGWatchAdd -amenu <silent> &Plugin.HG.WatchOn <Plug>HGWatchOn -amenu <silent> &Plugin.HG.WatchOff <Plug>HGWatchOff -amenu <silent> &Plugin.HG.WatchRemove <Plug>HGWatchRemove " Section: Autocommands to restore vimdiff state {{{1 function! s:HGVimDiffRestore(vimDiffBuff) @@ -1249,17 +1177,521 @@ augroup HGVimDiffRestore au! - au BufUnload * call s:HGVimDiffRestore(expand("<abuf>")) + au BufUnload * call <SID>HGVimDiffRestore(expand("<abuf>")) augroup END " Section: Optional activation of buffer management {{{1 -if s:HGGetOption('HGCommandEnableBufferSetup', 0) +if s:HGGetOption('HGCommandEnableBufferSetup', 1) call HGEnableBufferSetup() endif +" Section: Doc installation {{{1 + +if <SID>HGInstallDocumentation(expand("<sfile>:p")) + echomsg s:script_name s:script_version . ": updated documentation" +endif + " Section: Plugin completion {{{1 +" delete one-time vars and functions +delfunction <SID>HGInstallDocumentation +delfunction <SID>HGFlexiMkdir +delfunction <SID>HGCleanupOnFailure +unlet s:script_version s:script_name + let loaded_hgcommand=2 silent do HGCommand User HGPluginFinish + +let &cpo = s:save_cpo +unlet s:save_cpo " vim:se expandtab sts=2 sw=2: +finish + +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Section: Documentation content {{{1 +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +=== START_DOC +*hgcommand.txt* Mercurial vim integration #version# + + + HGCOMMAND REFERENCE MANUAL~ + + +Author: Mathieu Clabaut <mathieu.clabaut@gmail.com> +Credits: Bob Hiestand <bob.hiestand@gmail.com> +Mercurial: http://www.selenic.com/mercurial + Mercurial (noted Hg) is a fast, lightweight Source Control Management + system designed for efficient handling of very large distributed projects. + +============================================================================== +1. Contents *hgcommand-contents* + + Installation : |hgcommand-install| + HGCommand Intro : |hgcommand| + HGCommand Manual : |hgcommand-manual| + Customization : |hgcommand-customize| + Bugs : |hgcommand-bugs| + +============================================================================== +2. HGCommand Installation *hgcommand-install* + + In order to install the plugin, place the hgcommand.vim file into a plugin' + directory in your runtime path (please see |add-global-plugin| and + |'runtimepath'|. + + HGCommand may be customized by setting variables, creating maps, and + specifying event handlers. Please see |hgcommand-customize| for more + details. + + *hgcommand-auto-help* + The help file is automagically generated when the |hgcommand| script is + loaded for the first time. + +============================================================================== + +3. HGCommand Intro *hgcommand* + *hgcommand-intro* + + The HGCommand plugin provides global ex commands for manipulating + HG-controlled source files. In general, each command operates on the + current buffer and accomplishes a separate hg function, such as update, + commit, log, and others (please see |hgcommand-commands| for a list of all + available commands). The results of each operation are displayed in a + scratch buffer. Several buffer variables are defined for those scratch + buffers (please see |hgcommand-buffer-variables|). + + The notion of "current file" means either the current buffer, or, in the + case of a directory buffer, the file on the current line within the buffer. + + For convenience, any HGCommand invoked on a HGCommand scratch buffer acts + as though it was invoked on the original file and splits the screen so that + the output appears in a new window. + + Many of the commands accept revisions as arguments. By default, most + operate on the most recent revision on the current branch if no revision is + specified (though see |HGCommandInteractive| to prompt instead). + + Each HGCommand is mapped to a key sequence starting with the <Leader> + keystroke. The default mappings may be overridden by supplying different + mappings before the plugin is loaded, such as in the vimrc, in the standard + fashion for plugin mappings. For examples, please see + |hgcommand-mappings-override|. + + The HGCommand plugin may be configured in several ways. For more details, + please see |hgcommand-customize|. + +============================================================================== +4. HGCommand Manual *hgcommand-manual* + +4.1 HGCommand commands *hgcommand-commands* + + HGCommand defines the following commands: + + |:HGAdd| + |:HGAnnotate| + |:HGCommit| + |:HGDiff| + |:HGGotoOriginal| + |:HGLog| + |:HGRevert| + |:HGReview| + |:HGStatus| + |:HGUpdate| + |:HGVimDiff| + +:HGAdd *:HGAdd* + + This command performs "hg add" on the current file. Please note, this does + not commit the newly-added file. + +:HGAnnotate *:HGAnnotate* + + This command performs "hg annotate" on the current file. If an argument is + given, the argument is used as a revision number to display. If not given + an argument, it uses the most recent version of the file on the current + branch. Additionally, if the current buffer is a HGAnnotate buffer + already, the version number on the current line is used. + + If the |HGCommandAnnotateParent| variable is set to a non-zero value, the + version previous to the one on the current line is used instead. This + allows one to navigate back to examine the previous version of a line. + + The filetype of the HGCommand scratch buffer is set to 'HGAnnotate', to + take advantage of the bundled syntax file. + + +:HGCommit[!] *:HGCommit* + + If called with arguments, this performs "hg commit" using the arguments as + the log message. + + If '!' is used with no arguments, an empty log message is committed. + + If called with no arguments, this is a two-step command. The first step + opens a buffer to accept a log message. When that buffer is written, it is + automatically closed and the file is committed using the information from + that log message. The commit can be abandoned if the log message buffer is + deleted or wiped before being written. + + Alternatively, the mapping that is used to invoke :HGCommit (by default + <Leader>hgc) can be used in the log message buffer to immediately commit. + This is useful if the |HGCommandCommitOnWrite| variable is set to 0 to + disable the normal commit-on-write behavior. + +:HGDiff *:HGDiff* + + With no arguments, this performs "hg diff" on the current file against the + current repository version. + + With one argument, "hg diff" is performed on the current file against the + specified revision. + + With two arguments, hg diff is performed between the specified revisions of + the current file. + + This command uses the 'HGCommandDiffOpt' variable to specify diff options. + If that variable does not exist, then 'wbBc' is assumed. If you wish to + have no options, then set it to the empty string. + + +:HGGotoOriginal *:HGGotoOriginal* + + This command returns the current window to the source buffer, if the + current buffer is a HG command output buffer. + +:HGGotoOriginal! + + Like ":HGGotoOriginal" but also executes :bufwipeout on all HG command + output buffers for the source buffer. + +:HGLog *:HGLog* + + Performs "hg log" on the current file. + + If an argument is given, it is passed as an argument to the "-r" option of + "hg log". + +:HGRevert *:HGRevert* + + Replaces the current file with the most recent version from the repository + in order to wipe out any undesired changes. + +:HGReview *:HGReview* + + Retrieves a particular version of the current file. If no argument is + given, the most recent version of the file on the current branch is + retrieved. Otherwise, the specified version is retrieved. + +:HGStatus *:HGStatus* + + Performs "hg status" on the current file. + +:HGUpdate *:HGUpdate* + + Performs "hg update" on the current file. This intentionally does not + automatically reload the current buffer, though vim should prompt the user + to do so if the underlying file is altered by this command. + +:HGVimDiff *:HGVimDiff* + + With no arguments, this prompts the user for a revision and then uses + vimdiff to display the differences between the current file and the + specified revision. If no revision is specified, the most recent version + of the file on the current branch is used. + + With one argument, that argument is used as the revision as above. With + two arguments, the differences between the two revisions is displayed using + vimdiff. + + With either zero or one argument, the original buffer is used to perform + the vimdiff. When the other buffer is closed, the original buffer will be + returned to normal mode. + + Once vimdiff mode is started using the above methods, additional vimdiff + buffers may be added by passing a single version argument to the command. + There may be up to 4 vimdiff buffers total. + + Using the 2-argument form of the command resets the vimdiff to only those 2 + versions. Additionally, invoking the command on a different file will + close the previous vimdiff buffers. + + +4.2 Mappings *hgcommand-mappings* + + By default, a mapping is defined for each command. These mappings execute + the default (no-argument) form of each command. + + <Leader>hga HGAdd + <Leader>hgn HGAnnotate + <Leader>hgc HGCommit + <Leader>hgd HGDiff + <Leader>hgg HGGotoOriginal + <Leader>hgG HGGotoOriginal! + <Leader>hgl HGLog + <Leader>hgr HGReview + <Leader>hgs HGStatus + <Leader>hgu HGUpdate + <Leader>hgv HGVimDiff + + *hgcommand-mappings-override* + + The default mappings can be overriden by user-provided instead by mapping + to <Plug>CommandName. This is especially useful when these mappings + collide with other existing mappings (vim will warn of this during plugin + initialization, but will not clobber the existing mappings). + + For instance, to override the default mapping for :HGAdd to set it to + '\add', add the following to the vimrc: > + + nmap \add <Plug>HGAdd +< +4.3 Automatic buffer variables *hgcommand-buffer-variables* + + Several buffer variables are defined in each HGCommand result buffer. + These may be useful for additional customization in callbacks defined in + the event handlers (please see |hgcommand-events|). + + The following variables are automatically defined: + +b:hgOrigBuffNR *b:hgOrigBuffNR* + + This variable is set to the buffer number of the source file. + +b:hgcmd *b:hgcmd* + + This variable is set to the name of the hg command that created the result + buffer. +============================================================================== + +5. Configuration and customization *hgcommand-customize* + *hgcommand-config* + + The HGCommand plugin can be configured in two ways: by setting + configuration variables (see |hgcommand-options|) or by defining HGCommand + event handlers (see |hgcommand-events|). Additionally, the HGCommand + plugin provides several option for naming the HG result buffers (see + |hgcommand-naming|) and supported a customized status line (see + |hgcommand-statusline| and |hgcommand-buffer-management|). + +5.1 HGCommand configuration variables *hgcommand-options* + + Several variables affect the plugin's behavior. These variables are + checked at time of execution, and may be defined at the window, buffer, or + global level and are checked in that order of precedence. + + + The following variables are available: + + |HGCommandAnnotateParent| + |HGCommandCommitOnWrite| + |HGCommandHGExec| + |HGCommandDeleteOnHide| + |HGCommandDiffOpt| + |HGCommandDiffSplit| + |HGCommandEdit| + |HGCommandEnableBufferSetup| + |HGCommandInteractive| + |HGCommandNameMarker| + |HGCommandNameResultBuffers| + |HGCommandSplit| + +HGCommandAnnotateParent *HGCommandAnnotateParent* + + This variable, if set to a non-zero value, causes the zero-argument form of + HGAnnotate when invoked on a HGAnnotate buffer to go to the version + previous to that displayed on the current line. If not set, it defaults to + 0. + +HGCommandCommitOnWrite *HGCommandCommitOnWrite* + + This variable, if set to a non-zero value, causes the pending hg commit to + take place immediately as soon as the log message buffer is written. If + set to zero, only the HGCommit mapping will cause the pending commit to + occur. If not set, it defaults to 1. + +HGCommandHGExec *HGCommandHGExec* + + This variable controls the executable used for all HG commands. If not + set, it defaults to "hg". + +HGCommandDeleteOnHide *HGCommandDeleteOnHide* + + This variable, if set to a non-zero value, causes the temporary HG result + buffers to automatically delete themselves when hidden. + +HGCommandDiffOpt *HGCommandDiffOpt* + + This variable, if set, determines the options passed to the diff command of + HG. If not set, it defaults to 'w'. + +HGCommandDiffSplit *HGCommandDiffSplit* + + This variable overrides the |HGCommandSplit| variable, but only for buffers + created with |:HGVimDiff|. + +HGCommandEdit *HGCommandEdit* + + This variable controls whether the original buffer is replaced ('edit') or + split ('split'). If not set, it defaults to 'edit'. + +HGCommandEnableBufferSetup *HGCommandEnableBufferSetup* + + This variable, if set to a non-zero value, activates HG buffer management + mode see (|hgcommand-buffer-management|). This mode means that three + buffer variables, 'HGRepository', 'HGRevision' and 'HGBranch', are set if + the file is HG-controlled. This is useful for displaying version + information in the status bar. + +HGCommandInteractive *HGCommandInteractive* + + This variable, if set to a non-zero value, causes appropriate commands (for + the moment, only |:HGReview|) to query the user for a revision to use + instead of the current revision if none is specified. + +HGCommandNameMarker *HGCommandNameMarker* + + This variable, if set, configures the special attention-getting characters + that appear on either side of the hg buffer type in the buffer name. This + has no effect unless |HGCommandNameResultBuffers| is set to a true value. + If not set, it defaults to '_'. + +HGCommandNameResultBuffers *HGCommandNameResultBuffers* + + This variable, if set to a true value, causes the hg result buffers to be + named in the old way ('<source file name> _<hg command>_'). If not set or + set to a false value, the result buffer is nameless. + +HGCommandSplit *HGCommandSplit* + + This variable controls the orientation of the various window splits that + may occur (such as with HGVimDiff, when using a HG command on a HG command + buffer, or when the |HGCommandEdit| variable is set to 'split'. If set to + 'horizontal', the resulting windows will be on stacked on top of one + another. If set to 'vertical', the resulting windows will be side-by-side. + If not set, it defaults to 'horizontal' for all but HGVimDiff windows. + +5.2 HGCommand events *hgcommand-events* + + For additional customization, HGCommand can trigger user-defined events. + Event handlers are provided by defining User event autocommands (see + |autocommand|, |User|) in the HGCommand group with patterns matching the + event name. + + For instance, the following could be added to the vimrc to provide a 'q' + mapping to quit a HGCommand scratch buffer: > + + augroup HGCommand + au HGCommand User HGBufferCreated silent! nmap <unique> <buffer> q: + bwipeout<cr> + augroup END +< + + The following hooks are available: + +HGBufferCreated This event is fired just after a hg command result + buffer is created and filled with the result of a hg + command. It is executed within the context of the HG + command buffer. The HGCommand buffer variables may be + useful for handlers of this event (please see + |hgcommand-buffer-variables|). + +HGBufferSetup This event is fired just after HG buffer setup occurs, + if enabled. + +HGPluginInit This event is fired when the HGCommand plugin first + loads. + +HGPluginFinish This event is fired just after the HGCommand plugin + loads. + +HGVimDiffFinish This event is fired just after the HGVimDiff command + executes to allow customization of, for instance, + window placement and focus. + +5.3 HGCommand buffer naming *hgcommand-naming* + + By default, the buffers containing the result of HG commands are nameless + scratch buffers. It is intended that buffer variables of those buffers be + used to customize the statusline option so that the user may fully control + the display of result buffers. + + If the old-style naming is desired, please enable the + |HGCommandNameResultBuffers| variable. Then, each result buffer will + receive a unique name that includes the source file name, the HG command, + and any extra data (such as revision numbers) that were part of the + command. + +5.4 HGCommand status line support *hgcommand-statusline* + + It is intended that the user will customize the |'statusline'| option to + include HG result buffer attributes. A sample function that may be used in + the |'statusline'| option is provided by the plugin, HGGetStatusLine(). In + order to use that function in the status line, do something like the + following: > + + set statusline=%<%f\ %{HGGetStatusLine()}\ %h%m%r%=%l,%c%V\ %P +< + of which %{HGGetStatusLine()} is the relevant portion. + + The sample HGGetStatusLine() function handles both HG result buffers and + HG-managed files if HGCommand buffer management is enabled (please see + |hgcommand-buffer-management|). + +5.5 HGCommand buffer management *hgcommand-buffer-management* + + The HGCommand plugin can operate in buffer management mode, which means + that it attempts to set two buffer variables ('HGRevision' and 'HGBranch') + upon entry into a buffer. This is rather slow because it means that 'hg + status' will be invoked at each entry into a buffer (during the |BufEnter| + autocommand). + + This mode is enabled by default. In order to disable it, set the + |HGCommandEnableBufferSetup| variable to a false (zero) value. Enabling + this mode simply provides the buffer variables mentioned above. The user + must explicitly include those in the |'statusline'| option if they are to + appear in the status line (but see |hgcommand-statusline| for a simple way + to do that). + +============================================================================== +9. Tips *hgcommand-tips* + +9.1 Split window annotation, by Michael Anderson > + + :nmap <Leader>hgN :vs<CR><C-w>h<Leader>hgn:vertical res 40<CR> + \ggdddd:set scb<CR>:set nowrap<CR><C-w>lgg:set scb<CR> + \:set nowrap<CR> +< + + This splits the buffer vertically, puts an annotation on the left (minus + the header) with the width set to 40. An editable/normal copy is placed on + the right. The two versions are scroll locked so they move as one. and + wrapping is turned off so that the lines line up correctly. The advantages + are... + + 1) You get a versioning on the right. + 2) You can still edit your own code. + 3) Your own code still has syntax highlighting. + +============================================================================== + +8. Known bugs *hgcommand-bugs* + + Please let me know if you run across any. + + HGVimDiff, when using the original (real) source buffer as one of the diff + buffers, uses some hacks to try to restore the state of the original buffer + when the scratch buffer containing the other version is destroyed. There + may still be bugs in here, depending on many configuration details. + +============================================================================== + +9. TODO *hgcommand-todo* + + Integrate symlink tracking once HG will support them. +============================================================================== +=== END_DOC +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" v im:tw=78:ts=8:ft=help:norl: +" vim600: set foldmethod=marker tabstop=8 shiftwidth=2 softtabstop=2 smartindent smarttab : +"fileencoding=iso-8859-15
--- a/contrib/win32/ReadMe.html Wed Aug 09 14:53:03 2006 -0500 +++ b/contrib/win32/ReadMe.html Wed Aug 09 15:03:46 2006 -0500 @@ -14,7 +14,7 @@ </head> <body> - <h1>Mercurial version 0.9 for Windows</h1> + <h1>Mercurial version 0.9.1 for Windows</h1> <p>Welcome to Mercurial for Windows!</p>
--- a/contrib/win32/mercurial.iss Wed Aug 09 14:53:03 2006 -0500 +++ b/contrib/win32/mercurial.iss Wed Aug 09 15:03:46 2006 -0500 @@ -4,7 +4,7 @@ [Setup] AppCopyright=Copyright 2005, 2006 Matt Mackall and others AppName=Mercurial -AppVerName=Mercurial version 0.9 +AppVerName=Mercurial version 0.9.1 InfoAfterFile=contrib/win32/postinstall.txt LicenseFile=COPYING ShowLanguageDialog=yes @@ -14,10 +14,10 @@ AppUpdatesURL=http://www.selenic.com/mercurial AppID={{4B95A5F1-EF59-4B08-BED8-C891C46121B3} AppContact=mercurial@selenic.com -OutputBaseFilename=Mercurial-0.9 +OutputBaseFilename=Mercurial-0.9.1 DefaultDirName={sd}\Mercurial SourceDir=C:\hg\hg-release -VersionInfoVersion=0.9 +VersionInfoVersion=0.9.1 VersionInfoDescription=Mercurial distributed SCM VersionInfoCopyright=Copyright 2005, 2006 Matt Mackall and others VersionInfoCompany=Matt Mackall and others
--- a/contrib/win32/postinstall.txt Wed Aug 09 14:53:03 2006 -0500 +++ b/contrib/win32/postinstall.txt Wed Aug 09 15:03:46 2006 -0500 @@ -7,6 +7,62 @@ Release Notes ------------- +2006-07-24 v0.9.1 + +Major changes between Mercurial 0.9 and 0.9.1: + + New features: + - You can now configure your 'hgweb' server to let remote users + 'push' changes over http. + - You can now 'import' a patch in a mail message by saving the mail + message, and importing it. This works for patches sent either + inline or as attachments. + - The 'diff' command now accepts '-rA:B' syntax as a synonym for + '-r A -r B', and adds '-b' and '-B' options. + + New contributions and extensions: + - The 'acl' extension lets you lock down parts of a repository + against incoming changes + - The 'extdiff' extension lets you run your favourite graphical + change viewer + - Comprehensive integration with the 'vim' editor + - A restricted shell for 'ssh'-hosted repositories + - An importer for 'darcs' repositories + + New hooks added: + - 'preupdate' is run before an update or merge in the working + directory. + - 'update' is run after an update or merge in the working + directory. + + Behaviour changes: + - NOTE: Mercurial as installed by the Windows binary + installer no longer performs automatic line-ending conversion for + Unix/Linux compatibility. To re-enable this feature, edit your + 'mercurial.ini' file after you upgrade. + - The Windows binary installer now automatically adds 'hg' to your + '%PATH%'. + - The 'backout' command now runs an editor by default, to let you + modify the commit message for a backed-out changeset. + - An earlier problem with parsing of tags has been fixed. + This makes tag parsing slower but more reliable. + + Memory usage and performance improvements: + - The 'remove' command has been rewritten to be hundreds of times + faster in large repositories. + - It is now possible to 'clone' a repository very quickly over a + LAN, if the server is configured to allow it. See the new 'server' + section in the 'hgrc' documentation. + + Other changes of note: + - Mercurial will now print help for an extension if you type 'hg + help EXT_NAME'. + - The usual array of bug fixes and documentation improvements. + - The integrated web server is now more WSGI-compliant. + - Work has begun to solidify Mercurial's API for use by third-party + packages. + + 2006-05-10 v0.9 * Major changes between Mercurial 0.8.1 and 0.9:
--- a/doc/hgrc.5.txt Wed Aug 09 14:53:03 2006 -0500 +++ b/doc/hgrc.5.txt Wed Aug 09 15:03:46 2006 -0500 @@ -138,9 +138,17 @@ from;; Optional. Email address to use in "From" header and SMTP envelope of outgoing messages. + to;; + Optional. Comma-separated list of recipients' email addresses. + cc;; + Optional. Comma-separated list of carbon copy recipients' + email addresses. + bcc;; + Optional. Comma-separated list of blind carbon copy + recipients' email addresses. Cannot be set interactively. method;; Optional. Method to use to send email messages. If value is - "smtp" (default), use SMTP (see section "[mail]" for + "smtp" (default), use SMTP (see section "[smtp]" for configuration). Otherwise, use as name of program to run that acts like sendmail (takes "-f" option for sender, list of recipients on command line, message on stdin). Normally, setting @@ -194,7 +202,8 @@ changegroup;; Run after a changegroup has been added via push, pull or - unbundle. ID of the first new changeset is in $HG_NODE. + unbundle. ID of the first new changeset is in $HG_NODE. URL from + which changes came is in $HG_URL. commit;; Run after a changeset has been created in the local repository. ID of the newly created changeset is in $HG_NODE. Parent @@ -202,7 +211,7 @@ incoming;; Run after a changeset has been pulled, pushed, or unbundled into the local repository. The ID of the newly arrived changeset is in - $HG_NODE. + $HG_NODE. URL that was source of changes came is in $HG_URL. outgoing;; Run after sending changes from local repository to another. ID of first changeset sent is in $HG_NODE. Source of operation is in @@ -210,7 +219,8 @@ prechangegroup;; 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. + will cause the push, pull or unbundle to fail. URL from which + changes will come is in $HG_URL. precommit;; Run before starting a local commit. Exit status 0 allows the commit to proceed. Non-zero status will cause the commit to fail. @@ -236,7 +246,8 @@ before accepting them. Passed the ID of the first new changeset in $HG_NODE. 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. + the push, pull or unbundle will fail. URL that was source of + changes is in $HG_URL. pretxncommit;; Run after a changeset has been created but the transaction not yet committed. Changeset is visible to hook program. This lets you @@ -317,7 +328,7 @@ 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. + setting the following entries. default;; Directory or URL to use when pulling if no source is specified. Default is set to repository from which the current repository @@ -326,6 +337,18 @@ Optional. Directory or URL to use when pushing if no destination is specified. +server:: + Controls generic server settings. + uncompressed;; + Whether to allow clients to clone a repo 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 (100Mbps 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 + 6Mbps), uncompressed streaming is slower, because of the extra + data transfer overhead. Default is False. + ui:: User interface controls. debug;; @@ -428,6 +451,9 @@ push_ssl;; Whether to require that inbound pushes be transported over SSL to prevent password sniffing. Default is true. + stripes;; + How many lines a "zebra stripe" should span in multiline output. + Default is 1; set to 0 to disable. style;; Which template map style to use. templates;;
--- a/hgext/extdiff.py Wed Aug 09 14:53:03 2006 -0500 +++ b/hgext/extdiff.py Wed Aug 09 15:03:46 2006 -0500 @@ -22,6 +22,9 @@ # cmd.vdiff = kdiff3 # # add new command called meld, runs meld (no need to name twice) # cmd.meld = +# # add new command called vimdiff, runs gvimdiff with DirDiff plugin +# #(see http://www.vim.org/scripts/script.php?script_id=102) +# cmd.vimdiff = LC_ALL=C gvim -f '+bdel 1 2' '+ execute "DirDiff ".argv(0)." ".argv(1)' # # you can use -I/-X and list of file or directory names like normal # "hg diff" command. extdiff makes snapshots of only needed files, so
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/fetch.py Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,99 @@ +# fetch.py - pull and merge remote changes +# +# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +from mercurial.demandload import * +from mercurial.i18n import gettext as _ +from mercurial.node import * +demandload(globals(), 'mercurial:commands,hg,node,util') + +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 head, the head is automatically + merged, and the result of the merge is committed. Otherwise, the + working directory is updated.''' + + def postincoming(other, modheads): + if modheads == 0: + return 0 + if modheads == 1: + return hg.update(repo, repo.changelog.tip(), wlock=wlock) + newheads = repo.heads(parent) + newchildren = [n for n in repo.heads(parent) if n != parent] + newparent = parent + if newchildren: + newparent = newchildren[0] + hg.update(repo, newparent, wlock=wlock) + newheads = [n for n in repo.heads() if n != newparent] + err = False + if newheads: + ui.status(_('merging with new head %d:%s\n') % + (repo.changelog.rev(newheads[0]), short(newheads[0]))) + err = hg.merge(repo, newheads[0], remind=False, wlock=wlock) + if not err and len(newheads) > 1: + ui.status(_('not merging with %d other new heads ' + '(use "hg heads" and "hg merge" to merge them)') % + (len(newheads) - 1)) + if not err: + mod, add, rem = repo.status(wlock=wlock)[:3] + message = (commands.logmessage(opts) or + (_('Automated merge with %s') % other.url())) + n = repo.commit(mod + add + rem, message, + opts['user'], opts['date'], lock=lock, wlock=wlock, + force_editor=opts.get('force_editor')) + ui.status(_('new changeset %d:%s merges remote changes ' + 'with local\n') % (repo.changelog.rev(n), + short(n))) + def pull(): + commands.setremoteconfig(ui, opts) + + other = hg.repository(ui, ui.expandpath(source)) + ui.status(_('pulling from %s\n') % ui.expandpath(source)) + revs = None + if opts['rev'] and not other.local(): + raise util.Abort(_("fetch -r doesn't work for remote repositories yet")) + elif opts['rev']: + revs = [other.lookup(rev) for rev in opts['rev']] + modheads = repo.pull(other, heads=revs, lock=lock) + return postincoming(other, modheads) + + parent, p2 = repo.dirstate.parents() + if parent != repo.changelog.tip(): + raise util.Abort(_('working dir not at tip ' + '(use "hg update" to check out tip)')) + if p2 != nullid: + raise util.Abort(_('outstanding uncommitted merge')) + wlock = repo.wlock() + lock = repo.lock() + try: + mod, add, rem = repo.status(wlock=wlock)[:3] + if mod or add or rem: + raise util.Abort(_('outstanding uncommitted changes')) + if len(repo.heads()) > 1: + raise util.Abort(_('multiple heads in this repository ' + '(use "hg heads" and "hg merge" to merge)')) + return pull() + finally: + lock.release() + wlock.release() + +cmdtable = { + 'fetch': + (fetch, + [('e', 'ssh', '', _('specify ssh command to use')), + ('m', 'message', '', _('use <text> as commit message')), + ('l', 'logfile', '', _('read the commit message from <file>')), + ('d', 'date', '', _('record datecode as commit date')), + ('u', 'user', '', _('record user as commiter')), + ('r', 'rev', [], _('a specific revision you would like to pull')), + ('f', 'force-editor', None, _('edit commit message')), + ('', 'remotecmd', '', _('hg command to run on the remote side'))], + 'hg fetch [SOURCE]'), + }
--- a/hgext/hbisect.py Wed Aug 09 14:53:03 2006 -0500 +++ b/hgext/hbisect.py Wed Aug 09 15:03:46 2006 -0500 @@ -50,7 +50,7 @@ if r: self.badrev = hg.bin(r.pop(0)) - def __del__(self): + def write(self): if not os.path.isdir(self.path): return f = self.opener(self.good_path, "w") @@ -197,7 +197,7 @@ check_clean(self.ui, self.repo) rev = self.next() if rev is not None: - return self.repo.update(rev, force=True) + return hg.clean(self.repo, rev) def good(self, rev): self.goodrevs.append(rev) @@ -288,7 +288,10 @@ if len(args) > bisectcmdtable[cmd][1]: ui.warn(_("bisect: Too many arguments\n")) return help_() - return bisectcmdtable[cmd][0](*args) + try: + return bisectcmdtable[cmd][0](*args) + finally: + b.write() cmdtable = { "bisect": (bisect_run, [], _("hg bisect [help|init|reset|next|good|bad]")),
--- a/hgext/hgk.py Wed Aug 09 14:53:03 2006 -0500 +++ b/hgext/hgk.py Wed Aug 09 15:03:46 2006 -0500 @@ -334,6 +334,3 @@ ('n', 'max-count', 0, 'max-count')], "hg debug-rev-list [options] revs"), } - -def reposetup(ui, repo): - pass
--- a/hgext/mq.py Wed Aug 09 14:53:03 2006 -0500 +++ b/hgext/mq.py Wed Aug 09 15:03:46 2006 -0500 @@ -1,3 +1,4 @@ + # queue.py - patch queues for mercurial # # Copyright 2005 Chris Mason <mason@suse.com> @@ -34,18 +35,26 @@ from mercurial.i18n import gettext as _ from mercurial import ui, hg, revlog, commands, util -versionstr = "0.45" +commands.norepo += " qclone qversion" -repomap = {} +class statusentry: + def __init__(self, rev, name=None): + if not name: + fields = rev.split(':') + if len(fields) == 2: + self.rev, self.name = fields + else: + self.rev, self.name = None, None + else: + self.rev, self.name = rev, name -commands.norepo += " qversion" + def __str__(self): + return self.rev + ':' + self.name + class queue: def __init__(self, ui, path, patchdir=None): self.basepath = path - if patchdir: - self.path = patchdir - else: - self.path = os.path.join(path, "patches") + self.path = patchdir or os.path.join(path, "patches") self.opener = util.opener(self.path) self.ui = ui self.applied = [] @@ -54,13 +63,20 @@ self.series_dirty = 0 self.series_path = "series" self.status_path = "status" + self.guards_path = "guards" + self.active_guards = None + self.guards_dirty = False - if os.path.exists(os.path.join(self.path, self.series_path)): + if os.path.exists(self.join(self.series_path)): self.full_series = self.opener(self.series_path).read().splitlines() - self.read_series(self.full_series) + self.parse_series() - if os.path.exists(os.path.join(self.path, self.status_path)): - self.applied = self.opener(self.status_path).read().splitlines() + if os.path.exists(self.join(self.status_path)): + lines = self.opener(self.status_path).read().splitlines() + self.applied = [statusentry(l) for l in lines] + + def join(self, *p): + return os.path.join(self.path, *p) def find_series(self, patch): pre = re.compile("(\s*)([^#]+)") @@ -75,34 +91,132 @@ index += 1 return None - def read_series(self, list): - def matcher(list): - pre = re.compile("(\s*)([^#]+)") - for l in list: - m = pre.match(l) - if m: - s = m.group(2) - s = s.rstrip() - if len(s) > 0: - yield s + guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)') + + def parse_series(self): self.series = [] - self.series = [ x for x in matcher(list) ] + self.series_guards = [] + for l in self.full_series: + h = l.find('#') + if h == -1: + patch = l + comment = '' + elif h == 0: + continue + else: + patch = l[:h] + comment = l[h:] + patch = patch.strip() + if patch: + self.series.append(patch) + self.series_guards.append(self.guard_re.findall(comment)) + + def check_guard(self, guard): + bad_chars = '# \t\r\n\f' + first = guard[0] + for c in '-+': + if first == c: + return (_('guard %r starts with invalid character: %r') % + (guard, c)) + for c in bad_chars: + if c in guard: + return _('invalid character in guard %r: %r') % (guard, c) + + def set_active(self, guards): + for guard in guards: + bad = self.check_guard(guard) + if bad: + raise util.Abort(bad) + guards = dict.fromkeys(guards).keys() + guards.sort() + self.ui.debug('active guards: %s\n' % ' '.join(guards)) + self.active_guards = guards + self.guards_dirty = True + + def active(self): + if self.active_guards is None: + self.active_guards = [] + try: + guards = self.opener(self.guards_path).read().split() + except IOError, err: + if err.errno != errno.ENOENT: raise + guards = [] + for i, guard in enumerate(guards): + bad = self.check_guard(guard) + if bad: + self.ui.warn('%s:%d: %s\n' % + (self.join(self.guards_path), i + 1, bad)) + else: + self.active_guards.append(guard) + return self.active_guards + + def set_guards(self, idx, guards): + for g in guards: + if len(g) < 2: + raise util.Abort(_('guard %r too short') % g) + if g[0] not in '-+': + raise util.Abort(_('guard %r starts with invalid char') % g) + bad = self.check_guard(g[1:]) + if bad: + raise util.Abort(bad) + drop = self.guard_re.sub('', self.full_series[idx]) + self.full_series[idx] = drop + ''.join([' #' + g for g in guards]) + self.parse_series() + self.series_dirty = True + + def pushable(self, idx): + if isinstance(idx, str): + idx = self.series.index(idx) + patchguards = self.series_guards[idx] + if not patchguards: + return True, None + default = False + guards = self.active() + exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards] + if exactneg: + return False, exactneg[0] + pos = [g for g in patchguards if g[0] == '+'] + nonpos = [g for g in pos if g[1:] not in guards] + if pos: + if not nonpos: + return True, '' + return False, nonpos + return True, '' + + def explain_pushable(self, idx, all_patches=False): + write = all_patches and self.ui.write or self.ui.warn + if all_patches or self.ui.verbose: + if isinstance(idx, str): + idx = self.series.index(idx) + pushable, why = self.pushable(idx) + if all_patches and pushable: + if why is None: + write(_('allowing %s - no guards in effect\n') % + self.series[idx]) + else: + if not why: + write(_('allowing %s - no matching negative guards\n') % + self.series[idx]) + else: + write(_('allowing %s - guarded by %r\n') % + (self.series[idx], why)) + if not pushable: + if why: + write(_('skipping %s - guarded by %r\n') % + (self.series[idx], ' '.join(why))) + else: + write(_('skipping %s - no matching guards\n') % + self.series[idx]) def save_dirty(self): - if self.applied_dirty: - if len(self.applied) > 0: - nl = "\n" - else: - nl = "" - f = self.opener(self.status_path, "w") - f.write("\n".join(self.applied) + nl) - if self.series_dirty: - if len(self.full_series) > 0: - nl = "\n" - else: - nl = "" - f = self.opener(self.series_path, "w") - f.write("\n".join(self.full_series) + nl) + def write_list(items, path): + fp = self.opener(path, 'w') + for i in items: + print >> fp, i + fp.close() + if self.applied_dirty: write_list(map(str, self.applied), self.status_path) + if self.series_dirty: write_list(self.full_series, self.series_path) + if self.guards_dirty: write_list(self.active_guards, self.guards_path) def readheaders(self, patch): def eatdiff(lines): @@ -122,7 +236,7 @@ else: break - pf = os.path.join(self.path, patch) + pf = self.join(patch) message = [] comments = [] user = None @@ -187,29 +301,25 @@ return (err, n) if n is None: - self.ui.warn("apply failed for patch %s\n" % patch) - sys.exit(1) + raise util.Abort(_("apply failed for patch %s") % patch) self.ui.warn("patch didn't work out, merging %s\n" % patch) # apply failed, strip away that rev and merge. - repo.update(head, allow=False, force=True, wlock=wlock) + hg.clean(repo, head, wlock=wlock) self.strip(repo, n, update=False, backup='strip', wlock=wlock) c = repo.changelog.read(rev) - ret = repo.update(rev, allow=True, wlock=wlock) + ret = hg.merge(repo, rev, wlock=wlock) if ret: - self.ui.warn("update returned %d\n" % ret) - sys.exit(1) + raise util.Abort(_("update returned %d") % ret) n = repo.commit(None, c[4], c[1], force=1, wlock=wlock) if n == None: - self.ui.warn("repo commit failed\n") - sys.exit(1) + raise util.Abort(_("repo commit failed")) try: message, comments, user, date, patchfound = mergeq.readheaders(patch) except: - self.ui.warn("Unable to read %s\n" % patch) - sys.exit(1) + raise util.Abort(_("unable to read %s") % patch) patchf = self.opener(patch, "w") if comments: @@ -226,12 +336,10 @@ return p1 if len(self.applied) == 0: return None - (top, patch) = self.applied[-1].split(':') - top = revlog.bin(top) - return top + return revlog.bin(self.applied[-1].rev) pp = repo.changelog.parents(rev) if pp[1] != revlog.nullid: - arevs = [ x.split(':')[0] for x in self.applied ] + arevs = [ x.rev for x in self.applied ] p0 = revlog.hex(pp[0]) p1 = revlog.hex(pp[1]) if p0 in arevs: @@ -251,17 +359,20 @@ pname = ".hg.patches.merge.marker" n = repo.commit(None, '[mq]: merge marker', user=None, force=1, wlock=wlock) - self.applied.append(revlog.hex(n) + ":" + pname) + self.applied.append(statusentry(revlog.hex(n), pname)) self.applied_dirty = 1 head = self.qparents(repo) for patch in series: - patch = mergeq.lookup(patch) + patch = mergeq.lookup(patch, strict=True) if not patch: self.ui.warn("patch %s does not exist\n" % patch) return (1, None) - + pushable, reason = self.pushable(patch) + if not pushable: + self.explain_pushable(patch, all_patches=True) + continue info = mergeq.isapplied(patch) if not info: self.ui.warn("patch %s is not applied\n" % patch) @@ -269,19 +380,55 @@ rev = revlog.bin(info[1]) (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock) if head: - self.applied.append(revlog.hex(head) + ":" + patch) + self.applied.append(statusentry(revlog.hex(head), patch)) self.applied_dirty = 1 if err: return (err, head) return (0, head) + def patch(self, repo, patchfile): + '''Apply patchfile to the working directory. + patchfile: file name of patch''' + try: + pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch') + f = os.popen("%s -d %s -p1 --no-backup-if-mismatch < %s" % + (pp, util.shellquote(repo.root), util.shellquote(patchfile))) + except: + self.ui.warn("patch failed, unable to continue (try -v)\n") + return (None, [], False) + files = [] + fuzz = False + for l in f: + l = l.rstrip('\r\n'); + if self.ui.verbose: + self.ui.warn(l + "\n") + if l[:14] == 'patching file ': + pf = os.path.normpath(util.parse_patch_output(l)) + if pf not in files: + files.append(pf) + printed_file = False + file_str = l + elif l.find('with fuzz') >= 0: + if not printed_file: + self.ui.warn(file_str + '\n') + printed_file = True + self.ui.warn(l + '\n') + fuzz = True + elif l.find('saving rejects to file') >= 0: + self.ui.warn(l + '\n') + elif l.find('FAILED') >= 0: + if not printed_file: + self.ui.warn(file_str + '\n') + printed_file = True + self.ui.warn(l + '\n') + + return (not f.close(), files, fuzz) + def apply(self, repo, series, list=False, update_status=True, strict=False, patchdir=None, merge=None, wlock=None): # TODO unify with commands.py if not patchdir: patchdir = self.path - pwd = os.getcwd() - os.chdir(repo.root) err = 0 if not wlock: wlock = repo.wlock() @@ -289,6 +436,10 @@ tr = repo.transaction() n = None for patch in series: + pushable, reason = self.pushable(patch) + if not pushable: + self.explain_pushable(patch, all_patches=True) + continue self.ui.warn("applying %s\n" % patch) pf = os.path.join(patchdir, patch) @@ -306,43 +457,8 @@ message.append("\nimported patch %s" % patch) message = '\n'.join(message) - try: - pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch') - f = os.popen("%s -p1 --no-backup-if-mismatch < '%s'" % (pp, pf)) - except: - self.ui.warn("patch failed, unable to continue (try -v)\n") - err = 1 - break - files = [] - fuzz = False - for l in f: - l = l.rstrip('\r\n'); - if self.ui.verbose: - self.ui.warn(l + "\n") - if l[:14] == 'patching file ': - pf = os.path.normpath(l[14:]) - # when patch finds a space in the file name, it puts - # single quotes around the filename. strip them off - if pf[0] == "'" and pf[-1] == "'": - pf = pf[1:-1] - if pf not in files: - files.append(pf) - printed_file = False - file_str = l - elif l.find('with fuzz') >= 0: - if not printed_file: - self.ui.warn(file_str + '\n') - printed_file = True - self.ui.warn(l + '\n') - fuzz = True - elif l.find('saving rejects to file') >= 0: - self.ui.warn(l + '\n') - elif l.find('FAILED') >= 0: - if not printed_file: - self.ui.warn(file_str + '\n') - printed_file = True - self.ui.warn(l + '\n') - patcherr = f.close() + (patcherr, files, fuzz) = self.patch(repo, pf) + patcherr = not patcherr if merge and len(files) > 0: # Mark as merged and update dirstate parent info @@ -350,17 +466,20 @@ p1, p2 = repo.dirstate.parents() repo.dirstate.setparents(p1, merge) if len(files) > 0: - commands.addremove_lock(self.ui, repo, files, + cwd = repo.getcwd() + cfiles = files + if cwd: + cfiles = [util.pathto(cwd, f) for f in files] + commands.addremove_lock(self.ui, repo, cfiles, opts={}, wlock=wlock) n = repo.commit(files, message, user, date, force=1, lock=lock, wlock=wlock) if n == None: - self.ui.warn("repo commit failed\n") - sys.exit(1) + raise util.Abort(_("repo commit failed")) if update_status: - self.applied.append(revlog.hex(n) + ":" + patch) + self.applied.append(statusentry(revlog.hex(n), patch)) if patcherr: if not patchfound: @@ -376,49 +495,50 @@ err = 1 break tr.close() - os.chdir(pwd) return (err, n) - def delete(self, repo, patch): - patch = self.lookup(patch) + def delete(self, repo, patch, force=False): + patch = self.lookup(patch, strict=True) info = self.isapplied(patch) if info: - self.ui.warn("cannot delete applied patch %s\n" % patch) - sys.exit(1) + raise util.Abort(_("cannot delete applied patch %s") % patch) if patch not in self.series: - self.ui.warn("patch %s not in series file\n" % patch) - sys.exit(1) + raise util.Abort(_("patch %s not in series file") % patch) + if force: + r = self.qrepo() + if r: + r.remove([patch], True) + else: + os.unlink(self.join(patch)) i = self.find_series(patch) del self.full_series[i] - self.read_series(self.full_series) + self.parse_series() self.series_dirty = 1 def check_toppatch(self, repo): if len(self.applied) > 0: - (top, patch) = self.applied[-1].split(':') - top = revlog.bin(top) + top = revlog.bin(self.applied[-1].rev) pp = repo.dirstate.parents() if top not in pp: - self.ui.warn("queue top not at dirstate parents. top %s dirstate %s %s\n" %( revlog.short(top), revlog.short(pp[0]), revlog.short(pp[1]))) - sys.exit(1) + raise util.Abort(_("queue top not at same revision as working directory")) return top return None def check_localchanges(self, repo): (c, a, r, d, u) = repo.changes(None, None) if c or a or d or r: - self.ui.write("Local changes found, refresh first\n") - sys.exit(1) + raise util.Abort(_("local changes found, refresh first")) def new(self, repo, patch, msg=None, force=None): + if os.path.exists(self.join(patch)): + raise util.Abort(_('patch "%s" already exists') % patch) commitfiles = [] (c, a, r, d, u) = repo.changes(None, None) if c or a or d or r: if not force: - raise util.Abort(_("Local changes found, refresh first")) - else: - commitfiles = c + a + r + raise util.Abort(_("local changes found, refresh first")) + commitfiles = c + a + r self.check_toppatch(repo) wlock = repo.wlock() - insert = self.series_end() + insert = self.full_series_end() if msg: n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True, wlock=wlock) @@ -426,11 +546,10 @@ n = repo.commit(commitfiles, "New patch: %s" % patch, force=True, wlock=wlock) if n == None: - self.ui.warn("repo commit failed\n") - sys.exit(1) + raise util.Abort(_("repo commit failed")) self.full_series[insert:insert] = [patch] - self.applied.append(revlog.hex(n) + ":" + patch) - self.read_series(self.full_series) + self.applied.append(statusentry(revlog.hex(n), patch)) + self.parse_series() self.series_dirty = 1 self.applied_dirty = 1 p = self.opener(patch, "w") @@ -442,7 +561,7 @@ r = self.qrepo() if r: r.add([patch]) if commitfiles: - self.refresh(repo, short=True) + self.refresh(repo, msg=None, short=True) def strip(self, repo, rev, update=True, backup="all", wlock=None): def limitheads(chlog, stop): @@ -509,9 +628,9 @@ # we go in two steps here so the strip loop happens in a # sensible order. When stripping many files, this helps keep # our disk access patterns under control. - list = seen.keys() - list.sort() - for f in list: + seen_list = seen.keys() + seen_list.sort() + for f in seen_list: ff = repo.file(f) filerev = seen[f] if filerev != 0: @@ -530,8 +649,11 @@ revnum = chlog.rev(rev) if update: + (c, a, r, d, u) = repo.changes(None, None) + if c or a or d or r: + raise util.Abort(_("local changes found")) urev = self.qparents(repo, rev) - repo.update(urev, allow=False, force=True, wlock=wlock) + hg.clean(repo, urev, wlock=wlock) repo.dirstate.write() # save is a list of all the branches we are truncating away @@ -540,7 +662,6 @@ saveheads = [] savebases = {} - tip = chlog.tip() heads = limitheads(chlog, rev) seen = {} @@ -571,7 +692,7 @@ savebases[x] = 1 # create a changegroup for all the branches we need to keep - if backup is "all": + if backup == "all": backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip') bundle(backupch) if saveheads: @@ -586,37 +707,87 @@ if saveheads: self.ui.status("adding branch\n") commands.unbundle(self.ui, repo, chgrpfile, update=False) - if backup is not "strip": + if backup != "strip": os.unlink(chgrpfile) def isapplied(self, patch): """returns (index, rev, patch)""" for i in xrange(len(self.applied)): - p = self.applied[i] - a = p.split(':') - if a[1] == patch: - return (i, a[0], a[1]) + a = self.applied[i] + if a.name == patch: + return (i, a.rev, a.name) return None - def lookup(self, patch): + # if the exact patch name does not exist, we try a few + # variations. If strict is passed, we try only #1 + # + # 1) a number to indicate an offset in the series file + # 2) a unique substring of the patch name was given + # 3) patchname[-+]num to indicate an offset in the series file + def lookup(self, patch, strict=False): + def partial_name(s): + if s in self.series: + return s + matches = [x for x in self.series if s in x] + if len(matches) > 1: + self.ui.warn(_('patch name "%s" is ambiguous:\n') % s) + for m in matches: + self.ui.warn(' %s\n' % m) + return None + if matches: + return matches[0] + if len(self.series) > 0 and len(self.applied) > 0: + if s == 'qtip': + return self.series[self.series_end()-1] + if s == 'qbase': + return self.series[0] + return None if patch == None: return None - if patch in self.series: - return patch - if not os.path.isfile(os.path.join(self.path, patch)): + + # we don't want to return a partial match until we make + # sure the file name passed in does not exist (checked below) + res = partial_name(patch) + if res and res == patch: + return res + + if not os.path.isfile(self.join(patch)): try: sno = int(patch) except(ValueError, OverflowError): - self.ui.warn("patch %s not in series\n" % patch) - sys.exit(1) - if sno >= len(self.series): - self.ui.warn("patch number %d is out of range\n" % sno) - sys.exit(1) - patch = self.series[sno] - else: - self.ui.warn("patch %s not in series\n" % patch) - sys.exit(1) - return patch + pass + else: + if sno < len(self.series): + return self.series[sno] + if not strict: + # return any partial match made above + if res: + return res + minus = patch.rsplit('-', 1) + if len(minus) > 1: + res = partial_name(minus[0]) + if res: + i = self.series.index(res) + try: + off = int(minus[1] or 1) + except(ValueError, OverflowError): + pass + else: + if i - off >= 0: + return self.series[i - off] + plus = patch.rsplit('+', 1) + if len(plus) > 1: + res = partial_name(plus[0]) + if res: + i = self.series.index(res) + try: + off = int(plus[1] or 1) + except(ValueError, OverflowError): + pass + else: + if i + off < len(self.series): + return self.series[i + off] + raise util.Abort(_("patch %s not in series") % patch) def push(self, repo, patch=None, force=False, list=False, mergeq=None, wlock=None): @@ -624,10 +795,10 @@ wlock = repo.wlock() patch = self.lookup(patch) if patch and self.isapplied(patch): - self.ui.warn("patch %s is already applied\n" % patch) + self.ui.warn(_("patch %s is already applied\n") % patch) sys.exit(1) if self.series_end() == len(self.series): - self.ui.warn("File series fully applied\n") + self.ui.warn(_("patch series fully applied\n")) sys.exit(1) if not force: self.check_localchanges(repo) @@ -646,7 +817,7 @@ ret = self.mergepatch(repo, mergeq, s, wlock) else: ret = self.apply(repo, s, list, wlock=wlock) - top = self.applied[-1].split(':')[1] + top = self.applied[-1].name if ret[0]: self.ui.write("Errors during apply, please fix and refresh %s\n" % top) @@ -654,7 +825,8 @@ self.ui.write("Now at: %s\n" % top) return ret[0] - def pop(self, repo, patch=None, force=False, update=True, wlock=None): + def pop(self, repo, patch=None, force=False, update=True, all=False, + wlock=None): def getfile(f, rev): t = repo.file(f).read(rev) try: @@ -675,15 +847,14 @@ patch = self.lookup(patch) info = self.isapplied(patch) if not info: - self.ui.warn("patch %s is not applied\n" % patch) - sys.exit(1) + raise util.Abort(_("patch %s is not applied") % patch) if len(self.applied) == 0: - self.ui.warn("No patches applied\n") + self.ui.warn(_("no patches applied\n")) sys.exit(1) if not update: parents = repo.dirstate.parents() - rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ] + rr = [ revlog.bin(x.rev) for x in self.applied ] for p in parents: if p in rr: self.ui.warn("qpop: forcing dirstate update\n") @@ -695,7 +866,17 @@ self.applied_dirty = 1; end = len(self.applied) if not patch: - info = [len(self.applied) - 1] + self.applied[-1].split(':') + if all: + popi = 0 + else: + popi = len(self.applied) - 1 + else: + popi = info[0] + 1 + if popi >= end: + self.ui.warn("qpop: %s is already at the top\n" % patch) + return + info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name] + start = info[0] rev = revlog.bin(info[1]) @@ -727,7 +908,7 @@ self.strip(repo, rev, update=False, backup='strip', wlock=wlock) del self.applied[start:end] if len(self.applied): - self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1]) + self.ui.write("Now at: %s\n" % self.applied[-1].name) else: self.ui.write("Patch queue now empty\n") @@ -739,20 +920,29 @@ qp = self.qparents(repo, top) commands.dodiff(sys.stdout, self.ui, repo, qp, None, files) - def refresh(self, repo, short=False): + def refresh(self, repo, msg=None, short=False): if len(self.applied) == 0: self.ui.write("No patches applied\n") return wlock = repo.wlock() self.check_toppatch(repo) - qp = self.qparents(repo) - (top, patch) = self.applied[-1].split(':') + (top, patch) = (self.applied[-1].rev, self.applied[-1].name) top = revlog.bin(top) cparents = repo.changelog.parents(top) patchparent = self.qparents(repo, top) message, comments, user, date, patchfound = self.readheaders(patch) patchf = self.opener(patch, "w") + msg = msg.rstrip() + if msg: + if comments: + # Remove existing message. + ci = 0 + for mi in range(len(message)): + while message[mi] != comments[ci]: + ci += 1 + del comments[ci] + comments.append(msg) if comments: comments = "\n".join(comments) + '\n\n' patchf.write(comments) @@ -822,13 +1012,17 @@ repo.dirstate.update(c, 'n') repo.dirstate.forget(forget) - if not message: - message = "patch queue: %s\n" % patch + if not msg: + if not message: + message = "patch queue: %s\n" % patch + else: + message = "\n".join(message) else: - message = "\n".join(message) + message = msg + self.strip(repo, top, update=False, backup='strip', wlock=wlock) n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock) - self.applied[-1] = revlog.hex(n) + ':' + patch + self.applied[-1] = statusentry(revlog.hex(n), patch) self.applied_dirty = 1 else: commands.dodiff(patchf, self.ui, repo, patchparent, None) @@ -838,35 +1032,47 @@ def init(self, repo, create=False): if os.path.isdir(self.path): - raise util.Abort("patch queue directory already exists") + raise util.Abort(_("patch queue directory already exists")) os.mkdir(self.path) if create: return self.qrepo(create=True) def unapplied(self, repo, patch=None): if patch and patch not in self.series: - self.ui.warn("%s not in the series file\n" % patch) - sys.exit(1) + raise util.Abort(_("patch %s is not in series file") % patch) if not patch: start = self.series_end() else: start = self.series.index(patch) + 1 - for p in self.series[start:]: - self.ui.write("%s\n" % p) + unapplied = [] + for i in xrange(start, len(self.series)): + pushable, reason = self.pushable(i) + if pushable: + unapplied.append((i, self.series[i])) + self.explain_pushable(i) + return unapplied - def qseries(self, repo, missing=None): - start = self.series_end() + def qseries(self, repo, missing=None, summary=False): + start = self.series_end(all_patches=True) if not missing: - for p in self.series[:start]: + for i in range(len(self.series)): + patch = self.series[i] if self.ui.verbose: - self.ui.write("%d A " % self.series.index(p)) - self.ui.write("%s\n" % p) - for p in self.series[start:]: - if self.ui.verbose: - self.ui.write("%d U " % self.series.index(p)) - self.ui.write("%s\n" % p) + if i < start: + status = 'A' + elif self.pushable(i)[0]: + status = 'U' + else: + status = 'G' + self.ui.write('%d %s ' % (i, status)) + if summary: + msg = self.readheaders(patch)[0] + msg = msg and ': ' + msg[0] or ': ' + else: + msg = '' + self.ui.write('%s%s\n' % (patch, msg)) else: - list = [] + msng_list = [] for root, dirs, files in os.walk(self.path): d = root[len(self.path) + 1:] for f in files: @@ -874,21 +1080,19 @@ if (fl not in self.series and fl not in (self.status_path, self.series_path) and not fl.startswith('.')): - list.append(fl) - list.sort() - if list: - for x in list: - if self.ui.verbose: - self.ui.write("D ") - self.ui.write("%s\n" % x) + msng_list.append(fl) + msng_list.sort() + for x in msng_list: + if self.ui.verbose: + self.ui.write("D ") + self.ui.write("%s\n" % x) def issaveline(self, l): - name = l.split(':')[1] - if name == '.hg.patches.save.line': + if l.name == '.hg.patches.save.line': return True def qrepo(self, create=False): - if create or os.path.isdir(os.path.join(self.path, ".hg")): + if create or os.path.isdir(self.join(".hg")): return hg.repository(self.ui, path=self.path, create=create) def restore(self, repo, rev, delete=None, qupdate=None): @@ -909,19 +1113,18 @@ qpp = [ hg.bin(x) for x in l ] elif datastart != None: l = lines[i].rstrip() - index = l.index(':') - id = l[:index] - file = l[index + 1:] - if id: - applied.append(l) - series.append(file) + se = statusentry(l) + file_ = se.name + if se.rev: + applied.append(se) + series.append(file_) if datastart == None: self.ui.warn("No saved patch data found\n") return 1 self.ui.warn("restoring status: %s\n" % lines[0]) self.full_series = series self.applied = applied - self.read_series(self.full_series) + self.parse_series() self.series_dirty = 1 self.applied_dirty = 1 heads = repo.changelog.heads() @@ -945,7 +1148,7 @@ if not r: self.ui.warn("Unable to load queue repository\n") return 1 - r.update(qpp[0], allow=False, force=True) + hg.clean(r, qpp[0]) def save(self, repo, msg=None): if len(self.applied) == 0: @@ -965,30 +1168,49 @@ pp = r.dirstate.parents() msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1])) msg += "\n\nPatch Data:\n" - text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar) - + '\n' or "") + text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and + "\n".join(ar) + '\n' or "") n = repo.commit(None, text, user=None, force=1) if not n: self.ui.warn("repo commit failed\n") return 1 - self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line') + self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line')) self.applied_dirty = 1 - def series_end(self): + def full_series_end(self): + if len(self.applied) > 0: + p = self.applied[-1].name + end = self.find_series(p) + if end == None: + return len(self.full_series) + return end + 1 + return 0 + + def series_end(self, all_patches=False): end = 0 + def next(start): + if all_patches: + return start + i = start + while i < len(self.series): + p, reason = self.pushable(i) + if p: + break + self.explain_pushable(i) + i += 1 + return i if len(self.applied) > 0: - (top, p) = self.applied[-1].split(':') + p = self.applied[-1].name try: end = self.series.index(p) except ValueError: return 0 - return end + 1 - return end + return next(end + 1) + return next(end) def qapplied(self, repo, patch=None): if patch and patch not in self.series: - self.ui.warn("%s not in the series file\n" % patch) - sys.exit(1) + raise util.Abort(_("patch %s is not in series file") % patch) if not patch: end = len(self.applied) else: @@ -998,9 +1220,11 @@ self.ui.write("%s\n" % p) def appliedname(self, index): - p = self.applied[index] + pname = self.applied[index].name if not self.ui.verbose: - p = p.split(':')[1] + p = pname + else: + p = str(self.series.index(pname)) + " " + p return p def top(self, repo): @@ -1015,7 +1239,10 @@ if end == len(self.series): self.ui.write("All patches applied\n") else: - self.ui.write(self.series[end] + '\n') + p = self.series[end] + if self.ui.verbose: + self.ui.write("%d " % self.series.index(p)) + self.ui.write(p + '\n') def prev(self, repo): if len(self.applied) > 1: @@ -1028,36 +1255,33 @@ def qimport(self, repo, files, patch=None, existing=None, force=None): if len(files) > 1 and patch: - self.ui.warn("-n option not valid when importing multiple files\n") - sys.exit(1) + raise util.Abort(_('option "-n" not valid when importing multiple ' + 'files')) i = 0 added = [] for filename in files: if existing: if not patch: patch = filename - if not os.path.isfile(os.path.join(self.path, patch)): - self.ui.warn("patch %s does not exist\n" % patch) - sys.exit(1) + if not os.path.isfile(self.join(patch)): + raise util.Abort(_("patch %s does not exist") % patch) else: try: text = file(filename).read() except IOError: - self.ui.warn("Unable to read %s\n" % patch) - sys.exit(1) + raise util.Abort(_("unable to read %s") % patch) if not patch: patch = os.path.split(filename)[1] - if not force and os.path.isfile(os.path.join(self.path, patch)): - self.ui.warn("patch %s already exists\n" % patch) - sys.exit(1) + if not force and os.path.exists(self.join(patch)): + raise util.Abort(_('patch "%s" already exists') % patch) patchf = self.opener(patch, "w") patchf.write(text) if patch in self.series: - self.ui.warn("patch %s is already in the series file\n" % patch) - sys.exit(1) - index = self.series_end() + i + raise util.Abort(_('patch %s is already in the series file') + % patch) + index = self.full_series_end() + i self.full_series[index:index] = [patch] - self.read_series(self.full_series) + self.parse_series() self.ui.warn("adding %s to series file\n" % patch) i += 1 added.append(patch) @@ -1068,33 +1292,43 @@ qrepo.add(added) def delete(ui, repo, patch, **opts): - """remove a patch from the series file""" - q = repomap[repo] - q.delete(repo, patch) + """remove a patch from the series file + + The patch must not be applied. + With -f, deletes the patch file as well as the series entry.""" + q = repo.mq + q.delete(repo, patch, force=opts.get('force')) q.save_dirty() return 0 def applied(ui, repo, patch=None, **opts): """print the patches already applied""" - repomap[repo].qapplied(repo, patch) + repo.mq.qapplied(repo, patch) return 0 def unapplied(ui, repo, patch=None, **opts): """print the patches not yet applied""" - repomap[repo].unapplied(repo, patch) - return 0 + for i, p in repo.mq.unapplied(repo, patch): + if ui.verbose: + ui.write("%d " % i) + ui.write("%s\n" % p) def qimport(ui, repo, *filename, **opts): """import a patch""" - q = repomap[repo] + q = repo.mq q.qimport(repo, filename, patch=opts['name'], existing=opts['existing'], force=opts['force']) q.save_dirty() return 0 def init(ui, repo, **opts): - """init a new queue repository""" - q = repomap[repo] + """init a new queue repository + + The queue repository is unversioned by default. If -c is + specified, qinit will create a separate nested repository + for patches. Use qcommit to commit changes to this queue + repository.""" + q = repo.mq r = q.init(repo, create=opts['create_repo']) q.save_dirty() if r: @@ -1106,68 +1340,250 @@ r.add(['.hgignore', 'series']) return 0 +def clone(ui, source, dest=None, **opts): + '''clone main and patch repository at same time + + 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. + ''' + commands.setremoteconfig(ui, opts) + if dest is None: + dest = hg.defaultdest(source) + sr = hg.repository(ui, ui.expandpath(source)) + qbase, destrev = None, None + if sr.local(): + reposetup(ui, sr) + if sr.mq.applied: + qbase = revlog.bin(sr.mq.applied[0].rev) + if not hg.islocal(dest): + destrev = sr.parents(qbase)[0] + ui.note(_('cloning main repo\n')) + sr, dr = hg.clone(ui, sr, dest, + pull=opts['pull'], + rev=destrev, + update=False, + stream=opts['uncompressed']) + ui.note(_('cloning patch repo\n')) + spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'), + dr.url() + '/.hg/patches', + pull=opts['pull'], + update=not opts['noupdate'], + stream=opts['uncompressed']) + if dr.local(): + if qbase: + ui.note(_('stripping applied patches from destination repo\n')) + reposetup(ui, dr) + dr.mq.strip(dr, qbase, update=False, backup=None) + if not opts['noupdate']: + ui.note(_('updating destination repo\n')) + hg.update(dr, dr.changelog.tip()) + def commit(ui, repo, *pats, **opts): """commit changes in the queue repository""" - q = repomap[repo] + q = repo.mq r = q.qrepo() if not r: raise util.Abort('no queue repository') commands.commit(r.ui, r, *pats, **opts) def series(ui, repo, **opts): """print the entire series file""" - repomap[repo].qseries(repo, missing=opts['missing']) + repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary']) return 0 def top(ui, repo, **opts): """print the name of the current patch""" - repomap[repo].top(repo) + repo.mq.top(repo) return 0 def next(ui, repo, **opts): """print the name of the next patch""" - repomap[repo].next(repo) + repo.mq.next(repo) return 0 def prev(ui, repo, **opts): """print the name of the previous patch""" - repomap[repo].prev(repo) + repo.mq.prev(repo) return 0 def new(ui, repo, patch, **opts): - """create a new patch""" - q = repomap[repo] - q.new(repo, patch, msg=opts['message'], force=opts['force']) + """create a new patch + + qnew creates a new patch on top of the currently-applied patch + (if any). It will refuse to run if there are any outstanding + changes unless -f is specified, in which case the patch will + be initialised with them. + + -m or -l set the patch header as well as the commit message. + If neither is specified, the patch header is empty and the + commit message is 'New patch: PATCH'""" + q = repo.mq + message = commands.logmessage(opts) + q.new(repo, patch, msg=message, force=opts['force']) q.save_dirty() return 0 def refresh(ui, repo, **opts): """update the current patch""" - q = repomap[repo] - q.refresh(repo, short=opts['short']) + q = repo.mq + message = commands.logmessage(opts) + if opts['edit']: + if message: + raise util.Abort(_('option "-e" incompatible with "-m" or "-l"')) + patch = q.applied[-1].name + (message, comment, user, date, hasdiff) = q.readheaders(patch) + message = ui.edit('\n'.join(message), user or ui.username()) + q.refresh(repo, msg=message, short=opts['short']) q.save_dirty() return 0 def diff(ui, repo, *files, **opts): """diff of the current patch""" # deep in the dirstate code, the walkhelper method wants a list, not a tuple - repomap[repo].diff(repo, list(files)) + repo.mq.diff(repo, list(files)) return 0 +def fold(ui, repo, *files, **opts): + """fold the named patches into the current 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 -f/--force, the folded patch files will + be removed afterwards. + + The header for each folded patch will be concatenated with + the current patch header, separated by a line of '* * *'.""" + + q = repo.mq + + if not files: + raise util.Abort(_('qfold requires at least one patch name')) + if not q.check_toppatch(repo): + raise util.Abort(_('No patches applied\n')) + + message = commands.logmessage(opts) + if opts['edit']: + if message: + raise util.Abort(_('option "-e" incompatible with "-m" or "-l"')) + + parent = q.lookup('qtip') + patches = [] + messages = [] + for f in files: + patch = q.lookup(f) + if patch in patches or patch == parent: + ui.warn(_('Skipping already folded patch %s') % patch) + if q.isapplied(patch): + raise util.Abort(_('qfold cannot fold already applied patch %s') % patch) + patches.append(patch) + + for patch in patches: + if not message: + messages.append(q.readheaders(patch)[0]) + pf = q.join(patch) + (patchsuccess, files, fuzz) = q.patch(repo, pf) + if not patchsuccess: + raise util.Abort(_('Error folding patch %s') % patch) + + if not message: + message, comments, user = q.readheaders(parent)[0:3] + for msg in messages: + message.append('* * *') + message.extend(msg) + message = '\n'.join(message) + + if opts['edit']: + message = ui.edit(message, user or ui.username()) + + q.refresh(repo, msg=message) + + for patch in patches: + q.delete(repo, patch, force=opts['force']) + + q.save_dirty() + +def guard(ui, repo, *args, **opts): + '''set or print guards for a patch + + guards control whether a patch can be pushed. a patch with no + guards is aways pushed. a patch with posative guard ("+foo") is + pushed only if qselect command enables guard "foo". a patch with + nagative guard ("-foo") is never pushed if qselect command enables + guard "foo". + + with no arguments, default is to print current active guards. + with arguments, set active guards for patch. + + to set nagative guard "-foo" on topmost patch ("--" is needed so + hg will not interpret "-foo" as argument): + hg qguard -- -foo + + to set guards on other patch: + hg qguard other.patch +2.6.17 -stable + ''' + def status(idx): + guards = q.series_guards[idx] or ['unguarded'] + ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards))) + q = repo.mq + patch = None + args = list(args) + if opts['list']: + if args or opts['none']: + raise util.Abort(_('cannot mix -l/--list with options or arguments')) + for i in xrange(len(q.series)): + status(i) + return + if not args or args[0][0:1] in '-+': + if not q.applied: + raise util.Abort(_('no patches applied')) + patch = q.applied[-1].name + if patch is None and args[0][0:1] not in '-+': + patch = args.pop(0) + if patch is None: + raise util.Abort(_('no patch to work with')) + if args or opts['none']: + q.set_guards(q.find_series(patch), args) + q.save_dirty() + else: + status(q.series.index(q.lookup(patch))) + +def header(ui, repo, patch=None): + """Print the header of the topmost or specified patch""" + q = repo.mq + + if patch: + patch = q.lookup(patch) + else: + if not q.applied: + ui.write('No patches applied\n') + return + patch = q.lookup('qtip') + message = repo.mq.readheaders(patch)[0] + + ui.write('\n'.join(message) + '\n') + def lastsavename(path): - (dir, base) = os.path.split(path) - names = os.listdir(dir) + (directory, base) = os.path.split(path) + names = os.listdir(directory) namere = re.compile("%s.([0-9]+)" % base) - max = None + maxindex = None maxname = None for f in names: m = namere.match(f) if m: index = int(m.group(1)) - if max == None or index > max: - max = index + if maxindex == None or index > maxindex: + maxindex = index maxname = f if maxname: - return (os.path.join(dir, maxname), max) + return (os.path.join(directory, maxname), maxindex) return (None, None) def savename(path): @@ -1179,7 +1595,7 @@ def push(ui, repo, patch=None, **opts): """push the next patch onto the stack""" - q = repomap[repo] + q = repo.mq mergeq = None if opts['all']: @@ -1207,17 +1623,65 @@ ui.warn('using patch queue: %s\n' % q.path) localupdate = False else: - q = repomap[repo] - if opts['all'] and len(q.applied) > 0: - patch = q.applied[0].split(':')[1] - q.pop(repo, patch, force=opts['force'], update=localupdate) + q = repo.mq + q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all']) q.save_dirty() return 0 +def rename(ui, repo, patch, name=None, **opts): + """rename a patch + + With one argument, renames the current patch to PATCH1. + With two arguments, renames PATCH1 to PATCH2.""" + + q = repo.mq + + if not name: + name = patch + patch = None + + if name in q.series: + raise util.Abort(_('A patch named %s already exists in the series file') % name) + + absdest = q.join(name) + if os.path.exists(absdest): + raise util.Abort(_('%s already exists') % absdest) + + if patch: + patch = q.lookup(patch) + else: + if not q.applied: + ui.write(_('No patches applied\n')) + return + patch = q.lookup('qtip') + + if ui.verbose: + ui.write('Renaming %s to %s\n' % (patch, name)) + i = q.find_series(patch) + q.full_series[i] = name + q.parse_series() + q.series_dirty = 1 + + info = q.isapplied(patch) + if info: + q.applied[info[0]] = statusentry(info[1], name) + q.applied_dirty = 1 + + util.rename(q.join(patch), absdest) + r = q.qrepo() + if r: + wlock = r.wlock() + if r.dirstate.state(name) == 'r': + r.undelete([name], wlock) + r.copy(patch, name, wlock) + r.remove([patch], False, wlock) + + q.save_dirty() + def restore(ui, repo, rev, **opts): """restore the queue state saved by a rev""" rev = repo.lookup(rev) - q = repomap[repo] + q = repo.mq q.restore(repo, rev, delete=opts['delete'], qupdate=opts['update']) q.save_dirty() @@ -1225,8 +1689,9 @@ def save(ui, repo, **opts): """save current queue state""" - q = repomap[repo] - ret = q.save(repo, msg=opts['message']) + q = repo.mq + message = commands.logmessage(opts) + ret = q.save(repo, msg=message) if ret: return ret q.save_dirty() @@ -1236,20 +1701,18 @@ newpath = os.path.join(q.basepath, opts['name']) if os.path.exists(newpath): if not os.path.isdir(newpath): - ui.warn("destination %s exists and is not a directory\n" % - newpath) - sys.exit(1) + raise util.Abort(_('destination %s exists and is not ' + 'a directory') % newpath) if not opts['force']: - ui.warn("destination %s exists, use -f to force\n" % - newpath) - sys.exit(1) + raise util.Abort(_('destination %s exists, ' + 'use -f to force') % newpath) else: newpath = savename(path) ui.warn("copy %s to %s\n" % (path, newpath)) util.copyfiles(path, newpath) if opts['empty']: try: - os.unlink(os.path.join(q.path, q.status_path)) + os.unlink(q.join(q.status_path)) except: pass return 0 @@ -1262,25 +1725,131 @@ backup = 'strip' elif opts['nobackup']: backup = 'none' - repomap[repo].strip(repo, rev, backup=backup) + repo.mq.strip(repo, rev, backup=backup) return 0 -def version(ui, q=None): - """print the version number""" - ui.write("mq version %s\n" % versionstr) - return 0 +def select(ui, repo, *args, **opts): + '''set or print guarded patches to push + + use qguard command to set or print guards on patch. then use + qselect to tell mq which guards to use. example: + + qguard foo.patch -stable (nagative guard) + qguard bar.patch +stable (posative guard) + qselect stable + + this sets "stable" guard. mq will skip foo.patch (because it has + nagative match) but push bar.patch (because it has posative + match). patch is pushed only if all posative guards match and no + nagative guards match. + + with no arguments, default is to print current active guards. + with arguments, set active guards as given. + + use -n/--none to deactivate guards (no other arguments needed). + when no guards active, patches with posative guards are skipped, + patches with nagative guards are pushed. + + use -s/--series to print list of all guards in series file (no + other arguments needed). use -v for more information.''' + + q = repo.mq + guards = q.active() + if args or opts['none']: + q.set_active(args) + q.save_dirty() + if not args: + ui.status(_('guards deactivated\n')) + if q.series: + ui.status(_('%d of %d unapplied patches active\n') % + (len(q.unapplied(repo)), len(q.series))) + elif opts['series']: + guards = {} + noguards = 0 + for gs in q.series_guards: + if not gs: + noguards += 1 + for g in gs: + guards.setdefault(g, 0) + guards[g] += 1 + if ui.verbose: + guards['NONE'] = noguards + guards = guards.items() + guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:])) + if guards: + ui.note(_('guards in series file:\n')) + for guard, count in guards: + ui.note('%2d ' % count) + ui.write(guard, '\n') + else: + ui.note(_('no guards in series file\n')) + else: + if guards: + ui.note(_('active guards:\n')) + for g in guards: + ui.write(g, '\n') + else: + ui.write(_('no active guards\n')) def reposetup(ui, repo): - repomap[repo] = queue(ui, repo.join("")) + class mqrepo(repo.__class__): + def tags(self): + if self.tagscache: + return self.tagscache + + tagscache = super(mqrepo, self).tags() + + q = self.mq + if not q.applied: + return tagscache + + mqtags = [(patch.rev, patch.name) for patch in q.applied] + mqtags.append((mqtags[-1][0], 'qtip')) + mqtags.append((mqtags[0][0], 'qbase')) + for patch in mqtags: + if patch[1] in tagscache: + self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1]) + else: + tagscache[patch[1]] = revlog.bin(patch[0]) + + return tagscache + + repo.__class__ = mqrepo + repo.mq = queue(ui, repo.join("")) cmdtable = { "qapplied": (applied, [], 'hg qapplied [PATCH]'), + "qclone": (clone, + [('', 'pull', None, _('use pull protocol to copy metadata')), + ('U', 'noupdate', None, _('do not update the new working directories')), + ('', 'uncompressed', None, + _('use uncompressed transfer (fast over LAN)')), + ('e', 'ssh', '', _('specify ssh command to use')), + ('p', 'patches', '', _('location of source patch repo')), + ('', 'remotecmd', '', + _('specify hg command to run on the remote side'))], + 'hg qclone [OPTION]... SOURCE [DEST]'), "qcommit|qci": (commit, commands.table["^commit|ci"][1], 'hg qcommit [OPTION]... [FILE]...'), "^qdiff": (diff, [], 'hg qdiff [FILE]...'), - "qdelete": (delete, [], 'hg qdelete PATCH'), + "qdelete": + (delete, + [('f', 'force', None, _('delete patch file'))], + 'hg qdelete [-f] PATCH'), + 'qfold': + (fold, + [('e', 'edit', None, _('edit patch header')), + ('f', 'force', None, _('delete folded patch files')), + ('m', 'message', '', _('set patch header to <text>')), + ('l', 'logfile', '', _('set patch header to contents of <file>'))], + 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'), + 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')), + ('n', 'none', None, _('drop all guards'))], + 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'), + 'qheader': (header, [], + _('hg qheader [PATCH]')), "^qimport": (qimport, [('e', 'existing', None, 'import file in patch dir'), @@ -1293,9 +1862,10 @@ 'hg qinit [-c]'), "qnew": (new, - [('m', 'message', '', 'commit message'), - ('f', 'force', None, 'force')], - 'hg qnew [-m TEXT] [-f] PATCH'), + [('m', 'message', '', _('use <text> as commit message')), + ('l', 'logfile', '', _('read the commit message from <file>')), + ('f', 'force', None, _('import uncommitted changes into patch'))], + 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'), "qnext": (next, [], 'hg qnext'), "qprev": (prev, [], 'hg qprev'), "^qpop": @@ -1314,8 +1884,13 @@ 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'), "^qrefresh": (refresh, - [('s', 'short', None, 'short refresh')], - 'hg qrefresh [-s]'), + [('e', 'edit', None, _('edit commit message')), + ('m', 'message', '', _('change commit message with <text>')), + ('l', 'logfile', '', _('change commit message with <file> content')), + ('s', 'short', None, 'short refresh')], + 'hg qrefresh [-e] [-m TEXT] [-l FILE] [-s]'), + 'qrename|qmv': + (rename, [], 'hg qrename PATCH1 [PATCH2]'), "qrestore": (restore, [('d', 'delete', None, 'delete save entry'), @@ -1323,15 +1898,21 @@ 'hg qrestore [-d] [-u] REV'), "qsave": (save, - [('m', 'message', '', 'commit message'), + [('m', 'message', '', _('use <text> as commit message')), + ('l', 'logfile', '', _('read the commit message from <file>')), ('c', 'copy', None, 'copy patch directory'), ('n', 'name', '', 'copy directory name'), ('e', 'empty', None, 'clear queue status file'), ('f', 'force', None, 'force copy')], - 'hg qsave [-m TEXT] [-c] [-n NAME] [-e] [-f]'), + 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'), + "qselect": (select, + [('n', 'none', None, _('disable all guards')), + ('s', 'series', None, _('list all guards in series file'))], + 'hg qselect [GUARDS]'), "qseries": (series, - [('m', 'missing', None, 'print patches not in series')], + [('m', 'missing', None, 'print patches not in series'), + ('s', 'summary', None, _('print first line of patch header'))], 'hg qseries [-m]'), "^strip": (strip, @@ -1341,6 +1922,5 @@ 'hg strip [-f] [-b] [-n] REV'), "qtop": (top, [], 'hg qtop'), "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'), - "qversion": (version, [], 'hg qversion') }
--- a/hgext/notify.py Wed Aug 09 14:53:03 2006 -0500 +++ b/hgext/notify.py Wed Aug 09 15:03:46 2006 -0500 @@ -255,7 +255,7 @@ changegroup. else send one email per changeset.''' n = notifier(ui, repo, hooktype) if not n.subs: - ui.debug(_('notify: no subscribers to this repo\n')) + ui.debug(_('notify: no subscribers to repo %s\n' % n.root)) return if n.skipsource(source): ui.debug(_('notify: changes have source "%s" - skipping\n') %
--- a/hgext/patchbomb.py Wed Aug 09 14:53:03 2006 -0500 +++ b/hgext/patchbomb.py Wed Aug 09 15:03:46 2006 -0500 @@ -38,12 +38,14 @@ # from = My Name <my@email> # to = recipient1, recipient2, ... # cc = cc1, cc2, ... +# bcc = bcc1, bcc2, ... from mercurial.demandload import * demandload(globals(), '''email.MIMEMultipart email.MIMEText email.Utils mercurial:commands,hg,ui os errno popen2 socket sys tempfile time''') from mercurial.i18n import gettext as _ +from mercurial.node import * try: # readline gives raw_input editing capabilities, but is not @@ -129,8 +131,26 @@ while patch and not patch[0].strip(): patch.pop(0) if opts['diffstat']: body += cdiffstat('\n'.join(desc), patch) + '\n\n' - body += '\n'.join(patch) - msg = email.MIMEText.MIMEText(body) + if opts['attach']: + msg = email.MIMEMultipart.MIMEMultipart() + if body: msg.attach(email.MIMEText.MIMEText(body, 'plain')) + p = email.MIMEText.MIMEText('\n'.join(patch), 'x-patch') + binnode = bin(node) + # if node is mq patch, it will have patch file name as tag + patchname = [t for t in repo.nodetags(binnode) + if t.endswith('.patch') or t.endswith('.diff')] + if patchname: + patchname = patchname[0] + elif total > 1: + patchname = commands.make_filename(repo, '%b-%n.patch', + binnode, idx, total) + else: + patchname = commands.make_filename(repo, '%b.patch', binnode) + p['Content-Disposition'] = 'inline; filename=' + patchname + msg.attach(p) + else: + body += '\n'.join(patch) + msg = email.MIMEText.MIMEText(body) if total == 1: subj = '[PATCH] ' + desc[0].strip() else: @@ -185,11 +205,14 @@ to = getaddrs('to', 'To') cc = getaddrs('cc', 'Cc', '') + bcc = opts['bcc'] or (ui.config('email', 'bcc') or + ui.config('patchbomb', 'bcc') or '').split(',') + bcc = [a.strip() for a in bcc if a.strip()] + if len(patches) > 1: ui.write(_('\nWrite the introductory message for the patch series.\n\n')) - msg = email.MIMEMultipart.MIMEMultipart() - msg['Subject'] = '[PATCH 0 of %d] %s' % ( + subj = '[PATCH 0 of %d] %s' % ( len(patches), opts['subject'] or prompt('Subject:', rest = ' [PATCH 0 of %d] ' % len(patches))) @@ -204,11 +227,14 @@ if l == '.': break body.append(l) - msg.attach(email.MIMEText.MIMEText('\n'.join(body) + '\n')) - if opts['diffstat']: d = cdiffstat(_('Final summary:\n'), jumbo) - if d: msg.attach(email.MIMEText.MIMEText(d)) + if d: body.append('\n' + d) + + body = '\n'.join(body) + '\n' + + msg = email.MIMEText.MIMEText(body) + msg['Subject'] = subj msgs.insert(0, msg) @@ -240,7 +266,8 @@ start_time += 1 m['From'] = sender m['To'] = ', '.join(to) - if cc: m['Cc'] = ', '.join(cc) + if cc: m['Cc'] = ', '.join(cc) + if bcc: m['Bcc'] = ', '.join(bcc) if opts['test']: ui.status('Displaying ', m['Subject'], ' ...\n') fp = os.popen(os.getenv('PAGER', 'more'), 'w') @@ -261,12 +288,16 @@ fp.close() else: ui.status('Sending ', m['Subject'], ' ...\n') - mail.sendmail(sender, to + cc, m.as_string(0)) + # Exim does not remove the Bcc field + del m['Bcc'] + mail.sendmail(sender, to + bcc + cc, m.as_string(0)) cmdtable = { 'email': (patchbomb, - [('c', 'cc', [], 'email addresses of copy recipients'), + [('a', 'attach', None, 'send patches as inline attachments'), + ('', 'bcc', [], 'email addresses of blind copy recipients'), + ('c', 'cc', [], 'email addresses of copy recipients'), ('d', 'diffstat', None, 'add diffstat output to messages'), ('f', 'from', '', 'email address of sender'), ('', 'plain', None, 'omit hg patch header'),
--- a/mercurial/bundlerepo.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/bundlerepo.py Wed Aug 09 15:03:46 2006 -0500 @@ -159,6 +159,10 @@ class bundlerepository(localrepo.localrepository): def __init__(self, ui, path, bundlename): localrepo.localrepository.__init__(self, ui, path) + + self._url = 'bundle:' + bundlename + if path: self._url += '+' + path + self.tempfile = None self.bundlefile = open(bundlename, "rb") header = self.bundlefile.read(6) @@ -208,6 +212,9 @@ for c in changegroup.chunkiter(self.bundlefile): pass + def url(self): + return self._url + def dev(self): return -1 @@ -230,3 +237,18 @@ self.bundlefile.close() if self.tempfile is not None: os.unlink(self.tempfile) + +def instance(ui, path, create): + if create: + raise util.Abort(_('cannot create new bundle repository')) + path = util.drop_scheme('file', path) + if path.startswith('bundle:'): + path = util.drop_scheme('bundle', path) + s = path.split("+", 1) + if len(s) == 1: + repopath, bundlename = "", s[0] + else: + repopath, bundlename = s + else: + repopath, bundlename = '', path + return bundlerepository(ui, repopath, bundlename)
--- a/mercurial/commands.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/commands.py Wed Aug 09 15:03:46 2006 -0500 @@ -40,6 +40,25 @@ return [util.normpath(os.path.join(cwd, x)) for x in args] return args +def logmessage(opts): + """ get the log message according to -m and -l option """ + message = opts['message'] + logfile = opts['logfile'] + + if message and logfile: + raise util.Abort(_('options --message and --logfile are mutually ' + 'exclusive')) + if not message and logfile: + try: + if logfile == '-': + message = sys.stdin.read() + else: + message = open(logfile).read() + except IOError, inst: + raise util.Abort(_("can't read commit message '%s': %s") % + (logfile, inst.strerror)) + return message + def matchpats(repo, pats=[], opts={}, head=''): cwd = repo.getcwd() if not pats and cwd: @@ -106,11 +125,22 @@ files, matchfn, anypats = matchpats(repo, pats, opts) + follow = opts.get('follow') or opts.get('follow_first') if repo.changelog.count() == 0: return [], False, matchfn - revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0'])) + if follow: + p = repo.dirstate.parents()[0] + if p == nullid: + ui.warn(_('No working directory revision; defaulting to tip\n')) + start = 'tip' + else: + start = repo.changelog.rev(p) + defrange = '%s:0' % start + else: + defrange = 'tip:0' + revs = map(int, revrange(ui, repo, opts['rev'] or [defrange])) wanted = {} slowpath = anypats fncache = {} @@ -125,33 +155,54 @@ if not slowpath and not files: # No files, no patterns. Display all revs. wanted = dict(zip(revs, revs)) + copies = [] if not slowpath: # Only files, no patterns. Check the history of each file. - def filerevgen(filelog): - for i, window in increasing_windows(filelog.count()-1, -1): + def filerevgen(filelog, node): + cl_count = repo.changelog.count() + if node is None: + last = filelog.count() - 1 + else: + last = filelog.rev(node) + for i, window in increasing_windows(last, -1): revs = [] for j in xrange(i - window, i + 1): - revs.append(filelog.linkrev(filelog.node(j))) + n = filelog.node(j) + revs.append((filelog.linkrev(n), + follow and filelog.renamed(n))) revs.reverse() for rev in revs: - yield rev - + # only yield rev for which we have the changelog, it can + # happen while doing "hg log" during a pull or commit + if rev[0] < cl_count: + yield rev + def iterfiles(): + for filename in files: + yield filename, None + for filename_node in copies: + yield filename_node minrev, maxrev = min(revs), max(revs) - for file_ in files: + for file_, node in iterfiles(): filelog = repo.file(file_) # A zero count may be a directory or deleted file, so # try to find matching entries on the slow path. if filelog.count() == 0: slowpath = True break - for rev in filerevgen(filelog): + for rev, copied in filerevgen(filelog, node): if rev <= maxrev: if rev < minrev: break fncache.setdefault(rev, []) fncache[rev].append(file_) wanted[rev] = 1 + if follow and copied: + copies.append(copied) if slowpath: + if follow: + raise util.Abort(_('can only follow copies/renames for explicit ' + 'file names')) + # The slow path checks files modified in every changeset. def changerevgen(): for i, window in increasing_windows(repo.changelog.count()-1, -1): @@ -165,10 +216,55 @@ wanted[rev] = 1 def iterate(): + class followfilter: + def __init__(self, onlyfirst=False): + self.startrev = -1 + self.roots = [] + self.onlyfirst = onlyfirst + + def match(self, rev): + def realparents(rev): + if self.onlyfirst: + return repo.changelog.parentrevs(rev)[0:1] + else: + return filter(lambda x: x != -1, repo.changelog.parentrevs(rev)) + + if self.startrev == -1: + self.startrev = rev + return True + + if rev > self.startrev: + # forward: all descendants + if not self.roots: + self.roots.append(self.startrev) + for parent in realparents(rev): + if parent in self.roots: + self.roots.append(rev) + return True + else: + # backwards: all parents + if not self.roots: + self.roots.extend(realparents(self.startrev)) + if rev in self.roots: + self.roots.remove(rev) + self.roots.extend(realparents(rev)) + return True + + return False + + if follow and not files: + ff = followfilter(onlyfirst=opts.get('follow_first')) + def want(rev): + if rev not in wanted: + return False + return ff.match(rev) + else: + def want(rev): + return rev in wanted + for i, window in increasing_windows(0, len(revs)): yield 'window', revs[0] < revs[-1], revs[-1] - nrevs = [rev for rev in revs[i:i+window] - if rev in wanted] + nrevs = [rev for rev in revs[i:i+window] if want(rev)] srevs = list(nrevs) srevs.sort() for rev in srevs: @@ -264,7 +360,8 @@ if node: expander.update(node_expander) if node and revwidth is not None: - expander['r'] = lambda: str(r.rev(node)).zfill(revwidth) + expander['r'] = (lambda: + str(repo.changelog.rev(node)).zfill(revwidth)) if total is not None: expander['N'] = lambda: str(total) if seqno is not None: @@ -409,25 +506,16 @@ diffopts['ignorewsamount'] ignoreblanklines = opts.get('ignore_blank_lines') or \ diffopts['ignoreblanklines'] - for f in modified: + + all = modified + added + removed + all.sort() + for f in all: to = None + tn = None if f in mmap: to = repo.file(f).read(mmap[f]) - tn = read(f) - fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text, - showfunc=showfunc, ignorews=ignorews, - ignorewsamount=ignorewsamount, - ignoreblanklines=ignoreblanklines)) - for f in added: - to = None - tn = read(f) - fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text, - showfunc=showfunc, ignorews=ignorews, - ignorewsamount=ignorewsamount, - ignoreblanklines=ignoreblanklines)) - for f in removed: - to = repo.file(f).read(mmap[f]) - tn = None + if f not in removed: + tn = read(f) fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text, showfunc=showfunc, ignorews=ignorews, ignorewsamount=ignorewsamount, @@ -533,6 +621,13 @@ return t return changeset_printer(ui, repo) +def setremoteconfig(ui, opts): + "copy remote options to ui tree" + if opts.get('ssh'): + ui.setconfig("ui", "ssh", opts['ssh']) + if opts.get('remotecmd'): + ui.setconfig("ui", "remotecmd", opts['remotecmd']) + def show_version(ui): """output version and copyright information""" ui.write(_("Mercurial Distributed SCM (version %s)\n") @@ -865,12 +960,23 @@ if op2 != nullid: raise util.Abort(_('outstanding uncommitted merge')) node = repo.lookup(rev) - parent, p2 = repo.changelog.parents(node) - if parent == nullid: + p1, p2 = repo.changelog.parents(node) + if p1 == nullid: raise util.Abort(_('cannot back out a change with no parents')) if p2 != nullid: - raise util.Abort(_('cannot back out a merge')) - repo.update(node, force=True, show_stats=False) + if not opts['parent']: + raise util.Abort(_('cannot back out a merge changeset without ' + '--parent')) + p = repo.lookup(opts['parent']) + if p not in (p1, p2): + raise util.Abort(_('%s is not a parent of %s' % + (short(p), short(node)))) + parent = p + else: + if opts['parent']: + raise util.Abort(_('cannot use --parent on non-merge changeset')) + parent = p1 + hg.clean(repo, node, show_stats=False) revert_opts = opts.copy() revert_opts['rev'] = hex(parent) revert(ui, repo, **revert_opts) @@ -887,11 +993,13 @@ if op1 != node: if opts['merge']: ui.status(_('merging with changeset %s\n') % nice(op1)) - doupdate(ui, repo, hex(op1), **opts) + n = _lookup(repo, hex(op1)) + hg.merge(repo, n) else: ui.status(_('the backout changeset is a new head - ' 'do not forget to merge\n')) - ui.status(_('(use "backout -m" if you want to auto-merge)\n')) + ui.status(_('(use "backout --merge" ' + 'if you want to auto-merge)\n')) def bundle(ui, repo, fname, dest=None, **opts): """create a changegroup file @@ -928,7 +1036,7 @@ %d dirname of file being printed, or '.' if in repo root %p root-relative path name of file being printed """ - ctx = repo.changectx(opts['rev'] or -1) + ctx = repo.changectx(opts['rev'] or "-1") for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()): fp = make_file(repo, opts['output'], ctx.node(), pathname=abs) fp.write(ctx.filectx(abs).data()) @@ -945,10 +1053,25 @@ .hg/hgrc file, as the default to be used for future pulls. For efficiency, hardlinks are used for cloning whenever the source - and destination are on the same filesystem. Some filesystems, - such as AFS, implement hardlinking incorrectly, but do not report - errors. In these cases, use the --pull option to avoid - hardlinking. + and destination are on the same filesystem (note this applies only + to the repository data, not to the checked out files). Some + filesystems, such as AFS, implement hardlinking incorrectly, but + do not report errors. In these cases, use the --pull option to + avoid hardlinking. + + You can safely clone repositories and checked out files using full + hardlinks with + + $ cp -al REPO REPOCLONE + + which is the fastest way to clone. However, 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). + + If you use the -r option to clone up to a specific revision, no + subsequent revisions will be present in the cloned repository. + This option implies --pull, even on local repositories. See pull for valid source format details. @@ -956,9 +1079,10 @@ .hg/hgrc will be created on the remote side. Look at the help text for the pull command for important details about ssh:// URLs. """ - ui.setconfig_remoteopts(**opts) + setremoteconfig(ui, opts) hg.clone(ui, ui.expandpath(source), dest, pull=opts['pull'], + stream=opts['uncompressed'], rev=opts['rev'], update=not opts['noupdate']) @@ -973,21 +1097,7 @@ If no commit message is specified, the editor configured in your hgrc or in the EDITOR environment variable is started to enter a message. """ - message = opts['message'] - logfile = opts['logfile'] - - if message and logfile: - raise util.Abort(_('options --message and --logfile are mutually ' - 'exclusive')) - if not message and logfile: - try: - if logfile == '-': - message = sys.stdin.read() - else: - message = open(logfile).read() - except IOError, inst: - raise util.Abort(_("can't read commit message '%s': %s") % - (logfile, inst.strerror)) + message = logmessage(opts) if opts['addremove']: addremove_lock(ui, repo, pats, opts) @@ -1739,7 +1849,7 @@ if user: ui.debug('From: %s\n' % user) diffs_seen = 0 - ok_types = ('text/plain', 'text/x-patch') + ok_types = ('text/plain', 'text/x-diff', 'text/x-patch') for part in msg.walk(): content_type = part.get_content_type() ui.debug('Content-Type: %s\n' % content_type) @@ -1794,9 +1904,13 @@ if not diffs_seen: raise util.Abort(_('no diffs found')) - files = util.patch(strip, tmpname, ui) + files = util.patch(strip, tmpname, ui, cwd=repo.root) if len(files) > 0: - addremove_lock(ui, repo, files, {}) + cfiles = files + cwd = repo.getcwd() + if cwd: + cfiles = [util.pathto(cwd, f) for f in files] + addremove_lock(ui, repo, cfiles, {}) repo.commit(files, message, user, date) finally: os.unlink(tmpname) @@ -1814,7 +1928,7 @@ See pull for valid source format details. """ source = ui.expandpath(source) - ui.setconfig_remoteopts(**opts) + setremoteconfig(ui, opts) other = hg.repository(ui, source) incoming = repo.findincoming(other, force=opts["force"]) @@ -1870,7 +1984,7 @@ Look at the help text for the pull command for important details about ssh:// URLs. """ - ui.setconfig_remoteopts(**opts) + setremoteconfig(ui, opts) hg.repository(ui, dest, create=1) def locate(ui, repo, *pats, **opts): @@ -1910,7 +2024,18 @@ def log(ui, repo, *pats, **opts): """show revision history of entire repository or files - Print the revision history of the specified files or the entire project. + Print the revision history of the specified files or the entire + project. + + File history is shown without following rename or copy history of + files. Use -f/--follow with a file name to follow history across + renames and copies. --follow without a file name will only show + ancestors or descendants of the starting revision. --follow-first + only follows the first parent of merge revisions. + + 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. By default this command outputs: changeset id and hash, tags, non-trivial parents, user, date and time, and a summary for each @@ -2029,7 +2154,7 @@ ui.write("%40s %3s %s\n" % (hex(m[f]), mf.execf(f) and "755" or "644", f)) -def merge(ui, repo, node=None, **opts): +def merge(ui, repo, node=None, force=None, branch=None): """Merge working directory with another revision Merge the contents of the current working directory and the @@ -2037,7 +2162,9 @@ marked as changed for the next commit and a commit must be performed before any further updates are allowed. """ - return doupdate(ui, repo, node=node, merge=True, **opts) + + node = _lookup(repo, node, branch) + return hg.merge(repo, node, force=force) def outgoing(ui, repo, dest=None, **opts): """show changesets not found in destination @@ -2049,7 +2176,7 @@ See pull for valid destination format details. """ dest = ui.expandpath(dest or 'default-push', dest or 'default') - ui.setconfig_remoteopts(**opts) + setremoteconfig(ui, opts) revs = None if opts['rev']: revs = [repo.lookup(rev) for rev in opts['rev']] @@ -2073,13 +2200,28 @@ dodiff(ui, ui, repo, prev, n) ui.write("\n") -def parents(ui, repo, rev=None, branches=None, **opts): +def parents(ui, repo, file_=None, rev=None, branches=None, **opts): """show the parents of the working dir or revision Print the working directory's parent revisions. """ + # legacy + if file_ and not rev: + try: + rev = repo.lookup(file_) + file_ = None + except hg.RepoError: + pass + else: + ui.warn(_("'hg parent REV' is deprecated, " + "please use 'hg parents -r REV instead\n")) + if rev: - p = repo.changelog.parents(repo.lookup(rev)) + if file_: + ctx = repo.filectx(file_, changeid=rev) + else: + ctx = repo.changectx(rev) + p = [cp.node() for cp in ctx.parents()] else: p = repo.dirstate.parents() @@ -2116,7 +2258,7 @@ return if optupdate: if modheads == 1: - return doupdate(ui, repo) + return hg.update(repo, repo.changelog.tip()) # update else: ui.status(_("not updating, since new heads added\n")) if modheads > 1: @@ -2156,7 +2298,7 @@ with the --ssh command line option. """ source = ui.expandpath(source) - ui.setconfig_remoteopts(**opts) + setremoteconfig(ui, opts) other = hg.repository(ui, source) ui.status(_('pulling from %s\n') % (source)) @@ -2194,7 +2336,7 @@ feature is enabled on the remote Mercurial server. """ dest = ui.expandpath(dest or 'default-push', dest or 'default') - ui.setconfig_remoteopts(**opts) + setremoteconfig(ui, opts) other = hg.repository(ui, dest) ui.status('pushing to %s\n' % (dest)) @@ -2248,7 +2390,7 @@ operation. It should only be necessary when Mercurial suggests it. """ if repo.recover(): - return repo.verify() + return hg.verify(repo) return 1 def remove(ui, repo, *pats, **opts): @@ -2465,8 +2607,7 @@ if not opts.get('dry_run'): repo.dirstate.forget(forget[0]) - r = repo.update(node, False, True, update.has_key, False, wlock=wlock, - show_stats=False) + r = hg.revert(repo, node, update.has_key, wlock) repo.dirstate.update(add[0], 'a') repo.dirstate.update(undelete[0], 'n') repo.dirstate.update(remove[0], 'r') @@ -2584,36 +2725,44 @@ def status(ui, repo, *pats, **opts): """show changed files in the working directory - Show changed files in the repository. If names are - given, only files that match are shown. + Show status of files in the repository. If names are given, only + files that match are shown. Files that are clean or ignored, are + not listed unless -c (clean), -i (ignored) or -A is given. The codes used to show the status of files are: M = modified A = added R = removed + C = clean ! = deleted, but still tracked ? = not tracked I = ignored (not shown by default) + = the previous added file was copied from here """ - show_ignored = opts['ignored'] and True or False + all = opts['all'] + files, matchfn, anypats = matchpats(repo, pats, opts) cwd = (pats and repo.getcwd()) or '' - modified, added, removed, deleted, unknown, ignored = [ + modified, added, removed, deleted, unknown, ignored, clean = [ [util.pathto(cwd, x) for x in n] - for n in repo.changes(files=files, match=matchfn, - show_ignored=show_ignored)] - - changetypes = [('modified', 'M', modified), + for n in repo.status(files=files, match=matchfn, + list_ignored=all or opts['ignored'], + list_clean=all or opts['clean'])] + + changetypes = (('modified', 'M', modified), ('added', 'A', added), ('removed', 'R', removed), ('deleted', '!', deleted), ('unknown', '?', unknown), - ('ignored', 'I', ignored)] + ('ignored', 'I', ignored)) + + explicit_changetypes = changetypes + (('clean', 'C', clean),) end = opts['print0'] and '\0' or '\n' - for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]] + for opt, char, changes in ([ct for ct in explicit_changetypes + if all or opts[ct[0]]] or changetypes): if opts['no_status']: format = "%%s%s" % end @@ -2622,6 +2771,9 @@ for f in changes: ui.write(format % f) + if ((all or opts.get('copies')) and not opts.get('no_status') + and opt == 'added' and repo.dirstate.copies.has_key(f)): + ui.write(' %s%s' % (repo.dirstate.copies[f], end)) def tag(ui, repo, name, rev_=None, **opts): """add a tag for the current tip or a given revision @@ -2632,7 +2784,7 @@ very useful to compare different revision, to go back to significant earlier versions or to mark branch points as releases, etc. - If no revision is given, the tip is used. + 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 @@ -2640,8 +2792,8 @@ necessary. The file '.hg/localtags' is used for local tags (not shared among repositories). """ - if name == "tip": - raise util.Abort(_("the name 'tip' is reserved")) + if name in ['tip', '.']: + raise util.Abort(_("the name '%s' is reserved") % name) if rev_ is not None: ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, " "please use 'hg tag [-r REV] NAME' instead\n")) @@ -2652,7 +2804,12 @@ if rev_: r = hex(repo.lookup(rev_)) else: - r = hex(repo.changelog.tip()) + p1, p2 = repo.dirstate.parents() + if p1 == nullid: + raise util.Abort(_('no revision to tag')) + if p2 != nullid: + raise util.Abort(_('outstanding uncommitted merges')) + r = hex(p1) repo.tag(name, r, opts['local'], opts['message'], opts['user'], opts['date']) @@ -2717,7 +2874,8 @@ raise util.Abort(_("%s: unknown bundle compression type") % fname) gen = generator(util.filechunkiter(f, 4096)) - modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle') + modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle', + 'bundle:' + fname) return postincoming(ui, repo, modheads, opts['update']) def undo(ui, repo): @@ -2732,7 +2890,7 @@ repo.rollback() def update(ui, repo, node=None, merge=False, clean=False, force=None, - branch=None, **opts): + branch=None): """update or merge working directory Update the working directory to the specified revision. @@ -2747,13 +2905,17 @@ By default, update will refuse to run if doing so would require merging or discarding local changes. """ + node = _lookup(repo, node, branch) if merge: ui.warn(_('(the -m/--merge option is deprecated; ' 'use the merge command instead)\n')) - return doupdate(ui, repo, node, merge, clean, force, branch, **opts) - -def doupdate(ui, repo, node=None, merge=False, clean=False, force=None, - branch=None, **opts): + return hg.merge(repo, node, force=force) + elif clean: + return hg.clean(repo, node) + else: + return hg.update(repo, node) + +def _lookup(repo, node, branch=None): if branch: br = repo.branchlookup(branch=branch) found = [] @@ -2761,19 +2923,19 @@ if branch in br[x]: found.append(x) if len(found) > 1: - ui.warn(_("Found multiple heads for %s\n") % branch) + repo.ui.warn(_("Found multiple heads for %s\n") % branch) for x in found: - show_changeset(ui, repo, opts).show(changenode=x, brinfo=br) - return 1 + show_changeset(ui, repo, {}).show(changenode=x, brinfo=br) + raise util.Abort("") if len(found) == 1: node = found[0] - ui.warn(_("Using head %s for branch %s\n") % (short(node), branch)) + repo.ui.warn(_("Using head %s for branch %s\n") + % (short(node), branch)) else: - ui.warn(_("branch %s not found\n") % (branch)) - return 1 + raise util.Abort(_("branch %s not found\n") % (branch)) else: node = node and repo.lookup(node) or repo.changelog.tip() - return repo.update(node, allow=merge, force=clean, forcemerge=force) + return node def verify(ui, repo): """verify the integrity of the repository @@ -2785,7 +2947,7 @@ the changelog, manifest, and tracked files, as well as the integrity of their crosslinks and indices. """ - return repo.verify() + return hg.verify(repo) # Command options and aliases are listed here, alphabetically @@ -2829,6 +2991,7 @@ ('m', 'message', '', _('use <text> as commit message')), ('l', 'logfile', '', _('read commit message from <file>')), ('d', 'date', '', _('record datecode as commit date')), + ('', 'parent', '', _('parent to choose when backing out merge')), ('u', 'user', '', _('record user as committer')), ('I', 'include', [], _('include names matching the given patterns')), ('X', 'exclude', [], _('exclude names matching the given patterns'))], @@ -2851,6 +3014,8 @@ ('r', 'rev', [], _('a changeset you would like to have after cloning')), ('', 'pull', None, _('use pull protocol to copy metadata')), + ('', 'uncompressed', None, + _('use uncompressed transfer (fast over LAN)')), ('e', 'ssh', '', _('specify ssh command to use')), ('', 'remotecmd', '', _('specify hg command to run on the remote side'))], @@ -2989,6 +3154,10 @@ "^log|history": (log, [('b', 'branches', None, _('show branches')), + ('f', 'follow', None, + _('follow changeset history, or file history across copies and renames')), + ('', 'follow-first', None, + _('only follow the first parent of merge changesets')), ('k', 'keyword', [], _('search for a keyword')), ('l', 'limit', '', _('limit number of changes displayed')), ('r', 'rev', [], _('show the specified revision or range')), @@ -3022,9 +3191,10 @@ "^parents": (parents, [('b', 'branches', None, _('show branches')), + ('r', 'rev', '', _('show parents from the specified rev')), ('', 'style', '', _('display using template map file')), ('', 'template', '', _('display with template'))], - _('hg parents [-b] [REV]')), + _('hg parents [-b] [-r REV] [FILE]')), "paths": (paths, [], _('hg paths [NAME]')), "^pull": (pull, @@ -3101,13 +3271,16 @@ _('hg serve [OPTION]...')), "^status|st": (status, - [('m', 'modified', None, _('show only modified files')), + [('A', 'all', None, _('show status of all files')), + ('m', 'modified', None, _('show only modified files')), ('a', 'added', None, _('show only added files')), ('r', 'removed', None, _('show only removed files')), ('d', 'deleted', None, _('show only deleted (but tracked) files')), + ('c', 'clean', None, _('show only files without changes')), ('u', 'unknown', None, _('show only unknown (not tracked) files')), ('i', 'ignored', None, _('show ignored files')), ('n', 'no-status', None, _('hide status prefix')), + ('C', 'copies', None, _('show source of copied files')), ('0', 'print0', None, _('end filenames with NUL, for use with xargs')), ('I', 'include', [], _('include names matching the given patterns')), @@ -3269,9 +3442,8 @@ try: return sys.modules[external[name]] except KeyError: - dotname = '.' + name for k, v in external.iteritems(): - if k.endswith('.' + name) or v == name: + if k.endswith('.' + name) or k.endswith('/' + name) or v == name: return sys.modules[v] raise KeyError(name) @@ -3309,7 +3481,7 @@ except (util.SignalInterrupt, KeyboardInterrupt): raise except Exception, inst: - u.warn(_("*** failed to import extension %s: %s\n") % (x[0], inst)) + u.warn(_("*** failed to import extension %s: %s\n") % (ext_name, inst)) if u.print_exc(): return 1 @@ -3381,6 +3553,11 @@ else: d = lambda: func(u, *args, **cmdoptions) + # reupdate the options, repo/.hg/hgrc may have changed them + u.updateopts(options["verbose"], options["debug"], options["quiet"], + not options["noninteractive"], options["traceback"], + options["config"]) + try: if options['profile']: import hotshot, hotshot.stats @@ -3500,7 +3677,9 @@ return inst.code except: u.warn(_("** unknown exception encountered, details follow\n")) - u.warn(_("** report bug details to mercurial@selenic.com\n")) + u.warn(_("** report bug details to " + "http://www.selenic.com/mercurial/bts\n")) + u.warn(_("** or mercurial@selenic.com\n")) u.warn(_("** Mercurial Distributed SCM (version %s)\n") % version.get_version()) raise
--- a/mercurial/context.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/context.py Wed Aug 09 15:03:46 2006 -0500 @@ -11,9 +11,8 @@ def __init__(self, repo, changeid): """changeid is a revision number, node, or tag""" self._repo = repo - self._id = changeid - self._node = self._repo.lookup(self._id) + self._node = self._repo.lookup(changeid) self._rev = self._repo.changelog.rev(self._node) def changeset(self): @@ -39,21 +38,23 @@ def parents(self): """return contexts for each parent changeset""" - p = self.repo.changelog.parents(self._node) + p = self._repo.changelog.parents(self._node) return [ changectx(self._repo, x) for x in p ] def children(self): """return contexts for each child changeset""" - c = self.repo.changelog.children(self._node) + c = self._repo.changelog.children(self._node) return [ changectx(self._repo, x) for x in c ] def filenode(self, path): node, flag = self._repo.manifest.find(self.changeset()[0], path) return node - def filectx(self, path): + def filectx(self, path, fileid=None): """get a file context from this changeset""" - return filectx(self._repo, path, fileid=self.filenode(path)) + if fileid is None: + fileid = self.filenode(path) + return filectx(self._repo, path, fileid=fileid) def filectxs(self): """generate a file context for each file in this changeset's @@ -72,39 +73,40 @@ fileid can be a file revision or node.""" self._repo = repo self._path = path - self._id = changeid - self._fileid = fileid - if self._id: + assert changeid or fileid + + if not fileid: # if given a changeset id, go ahead and look up the file - self._changeset = changectx(repo, self._id) - node, flag = self._repo.manifest.find(self._changeset[0], path) - self._node = node - self._filelog = self.repo.file(self._path) - elif self._fileid: + self._changeid = changeid + self._changectx = self.changectx() + self._filelog = self._repo.file(self._path) + self._filenode = self._changectx.filenode(self._path) + else: # else be lazy self._filelog = self._repo.file(self._path) - self._filenode = self._filelog.lookup(self._fileid) + self._filenode = self._filelog.lookup(fileid) + self._changeid = self._filelog.linkrev(self._filenode) self._filerev = self._filelog.rev(self._filenode) - def changeset(self): + def changectx(self): try: - return self._changeset + return self._changectx except AttributeError: - self._changeset = self._repo.changelog.read(self.node()) - return self._changeset + self._changectx = changectx(self._repo, self._changeid) + return self._changectx def filerev(self): return self._filerev def filenode(self): return self._filenode def filelog(self): return self._filelog - def rev(self): return self.changeset().rev() - def node(self): return self.changeset().node() - def user(self): return self.changeset().user() - def date(self): return self.changeset().date() - def files(self): return self.changeset().files() - def description(self): return self.changeset().description() - def manifest(self): return self.changeset().manifest() + def rev(self): return self.changectx().rev() + def node(self): return self.changectx().node() + def user(self): return self.changectx().user() + def date(self): return self.changectx().date() + def files(self): return self.changectx().files() + def description(self): return self.changectx().description() + def manifest(self): return self.changectx().manifest() def data(self): return self._filelog.read(self._filenode) def metadata(self): return self._filelog.readmeta(self._filenode)
--- a/mercurial/demandload.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/demandload.py Wed Aug 09 15:03:46 2006 -0500 @@ -96,6 +96,7 @@ foo import foo foo bar import foo, bar + foo@bar import foo as bar foo.bar import foo.bar foo:bar from foo import bar foo:bar,quux from foo import bar, quux @@ -108,6 +109,9 @@ mod = mod[:col] else: fromlist = [] + as = None + if '@' in mod: + mod, as = mod.split("@") importer = _importer(scope, mod, fromlist) if fromlist: for name in fromlist: @@ -126,4 +130,6 @@ continue else: basemod = mod - scope[basemod] = _replacer(importer, basemod) + if not as: + as = basemod + scope[as] = _replacer(importer, as)
--- a/mercurial/dirstate.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/dirstate.py Wed Aug 09 15:03:46 2006 -0500 @@ -344,6 +344,10 @@ # directly by this function, but might be modified by your statmatch call. # def walkhelper(self, files, statmatch, dc, badmatch=None): + # self.root may end with a path separator when self.root == '/' + common_prefix_len = len(self.root) + if not self.root.endswith('/'): + common_prefix_len += 1 # recursion free walker, faster than os.walk. def findfiles(s): work = [s] @@ -352,7 +356,7 @@ names = os.listdir(top) names.sort() # nd is the top of the repository dir tree - nd = util.normpath(top[len(self.root) + 1:]) + nd = util.normpath(top[common_prefix_len:]) if nd == '.': nd = '' else: @@ -434,15 +438,16 @@ if not seen(k) and (statmatch(k, None)): yield 'm', k, None - def changes(self, files=None, match=util.always, show_ignored=None): + def status(self, files=None, match=util.always, list_ignored=False, + list_clean=False): lookup, modified, added, unknown, ignored = [], [], [], [], [] - removed, deleted = [], [] + removed, deleted, clean = [], [], [] - for src, fn, st in self.statwalk(files, match, ignored=show_ignored): + for src, fn, st in self.statwalk(files, match, ignored=list_ignored): try: type_, mode, size, time = self[fn] except KeyError: - if show_ignored and self.ignore(fn): + if list_ignored and self.ignore(fn): ignored.append(fn) else: unknown.append(fn) @@ -473,6 +478,8 @@ modified.append(fn) elif time != st.st_mtime: lookup.append(fn) + elif list_clean: + clean.append(fn) elif type_ == 'm': modified.append(fn) elif type_ == 'a': @@ -480,4 +487,5 @@ elif type_ == 'r': removed.append(fn) - return (lookup, modified, added, removed, deleted, unknown, ignored) + return (lookup, modified, added, removed, deleted, unknown, ignored, + clean)
--- a/mercurial/hg.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/hg.py Wed Aug 09 15:03:46 2006 -0500 @@ -10,71 +10,54 @@ from demandload import * from i18n import gettext as _ demandload(globals(), "localrepo bundlerepo httprepo sshrepo statichttprepo") -demandload(globals(), "errno lock os shutil util") - -def bundle(ui, path): - if path.startswith('bundle://'): - path = path[9:] - else: - path = path[7:] - s = path.split("+", 1) - if len(s) == 1: - repopath, bundlename = "", s[0] - else: - repopath, bundlename = s - return bundlerepo.bundlerepository(ui, repopath, bundlename) - -def hg(ui, path): - ui.warn(_("hg:// syntax is deprecated, please use http:// instead\n")) - return httprepo.httprepository(ui, path.replace("hg://", "http://")) +demandload(globals(), "errno lock os shutil util merge@_merge verify@_verify") -def local_(ui, path, create=0): - if path.startswith('file:'): - path = path[5:] - return localrepo.localrepository(ui, path, create) - -def ssh_(ui, path, create=0): - return sshrepo.sshrepository(ui, path, create) - -def old_http(ui, path): - ui.warn(_("old-http:// syntax is deprecated, " - "please use static-http:// instead\n")) - return statichttprepo.statichttprepository( - ui, path.replace("old-http://", "http://")) - -def static_http(ui, path): - return statichttprepo.statichttprepository( - ui, path.replace("static-http://", "http://")) +def _local(path): + return (os.path.isfile(path and util.drop_scheme('file', path)) and + bundlerepo or localrepo) schemes = { - 'bundle': bundle, - 'file': local_, - 'hg': hg, - 'http': lambda ui, path: httprepo.httprepository(ui, path), - 'https': lambda ui, path: httprepo.httpsrepository(ui, path), - 'old-http': old_http, - 'ssh': ssh_, - 'static-http': static_http, + 'bundle': bundlerepo, + 'file': _local, + 'hg': httprepo, + 'http': httprepo, + 'https': httprepo, + 'old-http': statichttprepo, + 'ssh': sshrepo, + 'static-http': statichttprepo, } -def repository(ui, path=None, create=0): - scheme = None +def _lookup(path): + scheme = 'file' if path: c = path.find(':') if c > 0: - scheme = schemes.get(path[:c]) - else: - path = '' - ctor = scheme or schemes['file'] - if create: + scheme = path[:c] + thing = schemes.get(scheme) or schemes['file'] + try: + return thing(path) + except TypeError: + return thing + +def islocal(repo): + '''return true if repo or path is local''' + if isinstance(repo, str): try: - return ctor(ui, path, create) - except TypeError: - raise util.Abort(_('cannot create new repository over "%s" protocol') % - scheme) - return ctor(ui, path) + return _lookup(repo).islocal(repo) + except AttributeError: + return False + return repo.local() -def clone(ui, source, dest=None, pull=False, rev=None, update=True): +def repository(ui, path=None, create=False): + """return a repository object for the specified path""" + return _lookup(path).instance(ui, path, create) + +def defaultdest(source): + '''return default destination of clone if none is given''' + return os.path.basename(os.path.normpath(source)) + +def clone(ui, source, dest=None, pull=False, rev=None, update=True, + stream=False): """Make a copy of an existing repository. Create a copy of an existing repository in a new directory. The @@ -89,20 +72,41 @@ If an exception is raised, the partly cloned/updated destination repository will be deleted. - Keyword arguments: + Arguments: + + source: repository object or URL dest: URL of destination repository to create (defaults to base name of source repository) pull: always pull from source repository, even in local case + stream: stream raw data uncompressed from repository (fast over + LAN, slow over WAN) + rev: revision to clone up to (implies pull=True) update: update working directory after clone completes, if destination is local repository """ + if isinstance(source, str): + src_repo = repository(ui, source) + else: + src_repo = source + source = src_repo.url() + if dest is None: - dest = os.path.basename(os.path.normpath(source)) + dest = defaultdest(source) + + def localpath(path): + if path.startswith('file://'): + return path[7:] + if path.startswith('file:'): + return path[5:] + return path + + dest = localpath(dest) + source = localpath(source) if os.path.exists(dest): raise util.Abort(_("destination '%s' already exists"), dest) @@ -117,8 +121,6 @@ if self.dir_: self.rmtree(self.dir_, True) - src_repo = repository(ui, source) - dest_repo = None try: dest_repo = repository(ui, dest) @@ -129,7 +131,7 @@ dest_path = None dir_cleanup = None if dest_repo.local(): - dest_path = os.path.realpath(dest) + dest_path = os.path.realpath(dest_repo.root) dir_cleanup = DirCleanup(dest_path) abspath = source @@ -153,9 +155,9 @@ # we lock here to avoid premature writing to the target dest_lock = lock.lock(os.path.join(dest_path, ".hg", "lock")) - # we need to remove the (empty) data dir in dest so copyfiles - # can do its work - os.rmdir(os.path.join(dest_path, ".hg", "data")) + # we need to remove the (empty) data dir in dest so copyfiles + # can do its work + os.rmdir(os.path.join(dest_path, ".hg", "data")) files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i" for f in files.split(): src = os.path.join(source, ".hg", f) @@ -166,8 +168,8 @@ if inst.errno != errno.ENOENT: raise - # we need to re-init the repo after manually copying the data - # into it + # we need to re-init the repo after manually copying the data + # into it dest_repo = repository(ui, dest) else: @@ -179,7 +181,7 @@ revs = [src_repo.lookup(r) for r in rev] if dest_repo.local(): - dest_repo.pull(src_repo, heads=revs) + dest_repo.clone(src_repo, heads=revs, stream=stream) elif src_repo.local(): src_repo.push(dest_repo, revs=revs) else: @@ -198,8 +200,31 @@ dest_lock.release() if update: - dest_repo.update(dest_repo.changelog.tip()) + _merge.update(dest_repo, dest_repo.changelog.tip()) if dir_cleanup: dir_cleanup.close() return src_repo, dest_repo + +def update(repo, node): + """update the working directory to node, merging linear changes""" + return _merge.update(repo, node) + +def clean(repo, node, wlock=None, show_stats=True): + """forcibly switch the working directory to node, clobbering changes""" + return _merge.update(repo, node, force=True, wlock=wlock, + show_stats=show_stats) + +def merge(repo, node, force=None, remind=True, wlock=None): + """branch merge with node, resolving changes""" + return _merge.update(repo, node, branchmerge=True, force=force, + remind=remind, wlock=wlock) + +def revert(repo, node, choose, wlock): + """revert changes to revision in node without updating dirstate""" + return _merge.update(repo, node, force=True, partial=choose, + show_stats=False, wlock=wlock) + +def verify(repo): + """verify the consistency of a repository""" + return _verify.verify(repo)
--- a/mercurial/hgweb/hgweb_mod.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/hgweb/hgweb_mod.py Wed Aug 09 15:03:46 2006 -0500 @@ -11,7 +11,8 @@ import mimetypes from mercurial.demandload import demandload demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile") -demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater") +demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone") +demandload(globals(), "mercurial:templater") demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile") from mercurial.node import * from mercurial.i18n import gettext as _ @@ -36,6 +37,7 @@ self.mtime = -1 self.reponame = name self.archives = 'zip', 'gz', 'bz2' + self.stripecount = 1 self.templatepath = self.repo.ui.config("web", "templates", templater.templatepath()) @@ -45,6 +47,8 @@ self.mtime = mtime self.repo = hg.repository(self.repo.ui, self.repo.root) self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10)) + self.stripecount = int(self.repo.ui.config("web", "stripes", 1)) + self.maxshortchanges = int(self.repo.ui.config("web", "maxshortchanges", 60)) self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10)) self.allowpull = self.repo.ui.configbool("web", "allowpull", True) @@ -157,7 +161,7 @@ ignorewsamount=ignorewsamount, ignoreblanklines=ignoreblanklines), f, tn) - def changelog(self, pos): + def changelog(self, pos, shortlog=False): def changenav(**map): def seq(factor, maxchanges=None): if maxchanges: @@ -172,8 +176,9 @@ l = [] last = 0 - for f in seq(1, self.maxchanges): - if f < self.maxchanges or f <= last: + maxchanges = shortlog and self.maxshortchanges or self.maxchanges + for f in seq(1, maxchanges): + if f < maxchanges or f <= last: continue if f > count: break @@ -218,14 +223,15 @@ for e in l: yield e + maxchanges = shortlog and self.maxshortchanges or self.maxchanges cl = self.repo.changelog mf = cl.read(cl.tip())[0] count = cl.count() - start = max(0, pos - self.maxchanges + 1) - end = min(count, start + self.maxchanges) + start = max(0, pos - maxchanges + 1) + end = min(count, start + maxchanges) pos = end - 1 - yield self.t('changelog', + yield self.t(shortlog and 'shortlog' or 'changelog', changenav=changenav, manifest=hex(mf), rev=pos, changesets=count, entries=changelist, @@ -264,7 +270,7 @@ hn = hex(n) yield self.t('searchentry', - parity=count & 1, + parity=self.stripes(count), author=changes[1], parent=self.siblings(cl.parents(n), cl.rev), child=self.siblings(cl.children(n), cl.rev), @@ -375,7 +381,7 @@ for l, t in enumerate(text.splitlines(1)): yield {"line": t, "linenumber": "% 6d" % (l + 1), - "parity": l & 1} + "parity": self.stripes(l)} yield self.t("filerevision", file=f, @@ -408,7 +414,7 @@ mfn = cs[0] def annotate(**map): - parity = 1 + parity = 0 last = None for r, l in fl.annotate(n): try: @@ -488,10 +494,10 @@ yield {"file": full, "manifest": mnode, "filenode": hex(fnode), - "parity": parity, + "parity": self.stripes(parity), "basename": f, "permissions": mff[full]} - parity = 1 - parity + parity += 1 def dirlist(**map): parity = 0 @@ -502,11 +508,11 @@ if fnode: continue - yield {"parity": parity, + yield {"parity": self.stripes(parity), "path": os.path.join(path, f), "manifest": mnode, "basename": f[:-1]} - parity = 1 - parity + parity += 1 yield self.t("manifest", manifest=mnode, @@ -529,12 +535,12 @@ parity = 0 for k,n in i: if notip and k == "tip": continue - yield {"parity": parity, + yield {"parity": self.stripes(parity), "tag": k, "tagmanifest": hex(cl.read(n)[0]), "date": cl.read(n)[2], "node": hex(n)} - parity = 1 - parity + parity += 1 yield self.t("tags", manifest=hex(mf), @@ -564,12 +570,12 @@ t = c[2] yield self.t("tagentry", - parity = parity, + parity = self.stripes(parity), tag = k, node = hex(n), date = t, tagmanifest = hex(m)) - parity = 1 - parity + parity += 1 def changelist(**map): parity = 0 @@ -608,7 +614,8 @@ lastchange = (0, 0), # FIXME manifest = hex(mf), tags = tagentries, - shortlog = changelist) + shortlog = changelist, + archives=self.archivelist("tip")) def filediff(self, file, changeset): cl = self.repo.changelog @@ -688,6 +695,7 @@ def expand_form(form): shortcuts = { 'cl': [('cmd', ['changelog']), ('rev', None)], + 'sl': [('cmd', ['shortlog']), ('rev', None)], 'cs': [('cmd', ['changeset']), ('node', None)], 'f': [('cmd', ['file']), ('filenode', None)], 'fl': [('cmd', ['filelog']), ('filenode', None)], @@ -751,6 +759,13 @@ else: req.write(self.t("error")) + def stripes(self, parity): + "make horizontal stripes for easier reading" + if self.stripecount: + return (1 + parity / self.stripecount) & 1 + else: + return 0 + def do_changelog(self, req): hi = self.repo.changelog.count() - 1 if req.form.has_key('rev'): @@ -763,6 +778,18 @@ req.write(self.changelog(hi)) + def do_shortlog(self, req): + hi = self.repo.changelog.count() - 1 + if req.form.has_key('rev'): + hi = req.form['rev'][0] + try: + hi = self.repo.changelog.rev(self.repo.lookup(hi)) + except hg.RepoError: + req.write(self.search(hi)) # XXX redirect to 404 page? + return + + req.write(self.changelog(hi, shortlog = True)) + def do_changeset(self, req): req.write(self.changeset(req.form['node'][0])) @@ -859,7 +886,10 @@ or self.t("error", error="%r not found" % fname)) def do_capabilities(self, req): - resp = 'unbundle' + caps = ['unbundle'] + if self.repo.ui.configbool('server', 'uncompressed'): + caps.append('stream=%d' % self.repo.revlogversion) + resp = ' '.join(caps) req.httphdr("application/mercurial-0.1", length=len(resp)) req.write(resp) @@ -891,9 +921,13 @@ # require ssl by default, auth info cannot be sniffed and # replayed ssl_req = self.repo.ui.configbool('web', 'push_ssl', True) - if ssl_req and not req.env.get('HTTPS'): - bail(_('ssl required\n')) - return + if ssl_req: + if not req.env.get('HTTPS'): + bail(_('ssl required\n')) + return + proto = 'https' + else: + proto = 'http' # do not allow push unless explicitly allowed if not self.check_perm(req, 'push', False): @@ -939,7 +973,9 @@ sys.stdout = cStringIO.StringIO() try: - ret = self.repo.addchangegroup(fp, 'serve') + url = 'remote:%s:%s' % (proto, + req.env.get('REMOTE_HOST', '')) + ret = self.repo.addchangegroup(fp, 'serve', url) finally: val = sys.stdout.getvalue() sys.stdout = old_stdout @@ -950,3 +986,7 @@ finally: fp.close() os.unlink(tempname) + + def do_stream_out(self, req): + req.httphdr("application/mercurial-0.1") + streamclone.stream_out(self.repo, req)
--- a/mercurial/hgweb/server.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/hgweb/server.py Wed Aug 09 15:03:46 2006 -0500 @@ -198,6 +198,7 @@ self.webdirmaker = hgwebdir self.repoviewmaker = hgweb self.reqmaker = wsgiapplication(self.make_handler) + self.daemon_threads = True def make_handler(self): if self.webdir_conf:
--- a/mercurial/httprepo.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/httprepo.py Wed Aug 09 15:03:46 2006 -0500 @@ -115,6 +115,7 @@ class httprepository(remoterepository): def __init__(self, ui, path): + self.path = path self.caps = None scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path) if query or frag: @@ -124,8 +125,8 @@ host, port, user, passwd = netlocsplit(netloc) # urllib cannot handle URLs with embedded user or passwd - self.url = urlparse.urlunsplit((scheme, netlocunsplit(host, port), - urlpath, '', '')) + self._url = urlparse.urlunsplit((scheme, netlocunsplit(host, port), + urlpath, '', '')) self.ui = ui proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy') @@ -189,6 +190,9 @@ opener.addheaders = [('User-agent', 'mercurial/proto-1.0')] urllib2.install_opener(opener) + def url(self): + return self.path + # look up capabilities only when needed def get_caps(self): @@ -213,7 +217,7 @@ q = {"cmd": cmd} q.update(args) qs = urllib.urlencode(q) - cu = "%s?%s" % (self.url, qs) + cu = "%s?%s" % (self._url, qs) try: resp = urllib2.urlopen(urllib2.Request(cu, data, headers)) except urllib2.HTTPError, inst: @@ -234,13 +238,13 @@ not proto.startswith('text/plain') and \ not proto.startswith('application/hg-changegroup'): raise hg.RepoError(_("'%s' does not appear to be an hg repository") % - self.url) + self._url) if proto.startswith('application/mercurial'): version = proto[22:] if float(version) > 0.1: raise hg.RepoError(_("'%s' uses newer protocol %s") % - (self.url, version)) + (self._url, version)) return resp @@ -326,9 +330,22 @@ fp.close() os.unlink(tempname) + def stream_out(self): + return self.do_cmd('stream_out') + class httpsrepository(httprepository): def __init__(self, ui, path): if not has_https: raise util.Abort(_('Python support for SSL and HTTPS ' 'is not installed')) httprepository.__init__(self, ui, path) + +def instance(ui, path, create): + if create: + raise util.Abort(_('cannot create new http repository')) + if path.startswith('hg:'): + ui.warn(_("hg:// syntax is deprecated, please use http:// instead\n")) + path = 'http:' + path[3:] + if path.startswith('https:'): + return httpsrepository(ui, path) + return httprepository(ui, path)
--- a/mercurial/localrepo.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/localrepo.py Wed Aug 09 15:03:46 2006 -0500 @@ -8,17 +8,19 @@ from node import * from i18n import gettext as _ from demandload import * +import repo demandload(globals(), "appendfile changegroup") -demandload(globals(), "changelog dirstate filelog manifest repo context") +demandload(globals(), "changelog dirstate filelog manifest context") demandload(globals(), "re lock transaction tempfile stat mdiff errno ui") -demandload(globals(), "os revlog util") +demandload(globals(), "os revlog time util") -class localrepository(object): +class localrepository(repo.repository): capabilities = () def __del__(self): self.transhandle = None def __init__(self, parentui, path=None, create=0): + repo.repository.__init__(self) if not path: p = os.getcwd() while not os.path.isdir(os.path.join(p, ".hg")): @@ -81,6 +83,9 @@ self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root) + def url(self): + return 'file:' + self.root + def hook(self, name, throw=False, **args): def callhook(hname, funcname): '''call python hook. hook is callable object, looked up as @@ -287,6 +292,10 @@ try: return self.tags()[key] except KeyError: + if key == '.': + key = self.dirstate.parents()[0] + if key == nullid: + raise repo.RepoError(_("no revision checked out")) try: return self.changelog.lookup(key) except: @@ -655,9 +664,9 @@ for src, fn in self.dirstate.walk(files, match, badmatch=badmatch): yield src, fn - def changes(self, node1=None, node2=None, files=[], match=util.always, - wlock=None, show_ignored=None): - """return changes between two nodes or node and working directory + def status(self, node1=None, node2=None, files=[], match=util.always, + wlock=None, list_ignored=False, list_clean=False): + """return status of files between two nodes or node and working directory If node1 is None, use the first dirstate parent instead. If node2 is None, compare node1 with working directory. @@ -676,7 +685,9 @@ del mf[fn] return mf - modified, added, removed, deleted, unknown, ignored = [],[],[],[],[],[] + modified, added, removed, deleted, unknown = [], [], [], [], [] + ignored, clean = [], [] + compareworking = False if not node1 or (not node2 and node1 == self.dirstate.parents()[0]): compareworking = True @@ -694,8 +705,9 @@ wlock = self.wlock(wait=0) except lock.LockException: wlock = None - lookup, modified, added, removed, deleted, unknown, ignored = ( - self.dirstate.changes(files, match, show_ignored)) + (lookup, modified, added, removed, deleted, unknown, + ignored, clean) = self.dirstate.status(files, match, + list_ignored, list_clean) # are we comparing working dir against its parent? if compareworking: @@ -718,12 +730,11 @@ del mf2[f] else: # we are comparing two revisions - deleted, unknown, ignored = [], [], [] mf2 = mfmatches(node2) if not compareworking: # flush lists from dirstate before comparing manifests - modified, added = [], [] + modified, added, clean = [], [], [] # make sure to sort the files so we talk to the disk in a # reasonable order @@ -733,6 +744,8 @@ if mf1.has_key(fn): if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)): modified.append(fn) + elif list_clean: + clean.append(fn) del mf1[fn] else: added.append(fn) @@ -740,12 +753,19 @@ removed = mf1.keys() # sort and return results: - for l in modified, added, removed, deleted, unknown, ignored: + for l in modified, added, removed, deleted, unknown, ignored, clean: l.sort() - if show_ignored is None: - return (modified, added, removed, deleted, unknown) + return (modified, added, removed, deleted, unknown, ignored, clean) + + def changes(self, node1=None, node2=None, files=[], match=util.always, + wlock=None, list_ignored=False, list_clean=False): + '''DEPRECATED - use status instead''' + marduit = self.status(node1, node2, files, match, wlock, + list_ignored, list_clean) + if list_ignored: + return marduit[:-1] else: - return (modified, added, removed, deleted, unknown, ignored) + return marduit[:-2] def add(self, list, wlock=None): if not wlock: @@ -1156,22 +1176,29 @@ else: return subset - def pull(self, remote, heads=None, force=False): - l = self.lock() + def pull(self, remote, heads=None, force=False, lock=None): + mylock = False + if not lock: + lock = self.lock() + mylock = True - fetch = self.findincoming(remote, force=force) - if fetch == [nullid]: - self.ui.status(_("requesting all changes\n")) + try: + fetch = self.findincoming(remote, force=force) + if fetch == [nullid]: + self.ui.status(_("requesting all changes\n")) - if not fetch: - self.ui.status(_("no changes found\n")) - return 0 + if not fetch: + self.ui.status(_("no changes found\n")) + return 0 - if heads is None: - cg = remote.changegroup(fetch, 'pull') - else: - cg = remote.changegroupsubset(fetch, heads, 'pull') - return self.addchangegroup(cg, 'pull') + if heads is None: + cg = remote.changegroup(fetch, 'pull') + else: + cg = remote.changegroupsubset(fetch, heads, 'pull') + return self.addchangegroup(cg, 'pull', remote.url()) + finally: + if mylock: + lock.release() def push(self, remote, force=False, revs=None): # there are two ways to push to remote repo: @@ -1182,7 +1209,7 @@ # unbundle assumes local user cannot lock remote repo (new ssh # servers, http servers). - if 'unbundle' in remote.capabilities: + if remote.capable('unbundle'): return self.push_unbundle(remote, force, revs) return self.push_addchangegroup(remote, force, revs) @@ -1227,7 +1254,7 @@ ret = self.prepush(remote, force, revs) if ret[0] is not None: cg, remote_heads = ret - return remote.addchangegroup(cg, 'push') + return remote.addchangegroup(cg, 'push', self.url()) return ret[1] def push_unbundle(self, remote, force, revs): @@ -1580,7 +1607,7 @@ return util.chunkbuffer(gengroup()) - def addchangegroup(self, source, srctype): + def addchangegroup(self, source, srctype, url): """add changegroup to repo. returns number of heads modified or added + 1.""" @@ -1594,7 +1621,7 @@ if not source: return 0 - self.hook('prechangegroup', throw=True, source=srctype) + self.hook('prechangegroup', throw=True, source=srctype, url=url) changesets = files = revisions = 0 @@ -1661,544 +1688,65 @@ if changesets > 0: self.hook('pretxnchangegroup', throw=True, - node=hex(self.changelog.node(cor+1)), source=srctype) + node=hex(self.changelog.node(cor+1)), source=srctype, + url=url) tr.close() if changesets > 0: self.hook("changegroup", node=hex(self.changelog.node(cor+1)), - source=srctype) + source=srctype, url=url) for i in range(cor + 1, cnr + 1): self.hook("incoming", node=hex(self.changelog.node(i)), - source=srctype) + source=srctype, url=url) return newheads - oldheads + 1 - def update(self, node, allow=False, force=False, choose=None, - moddirstate=True, forcemerge=False, wlock=None, show_stats=True): - pl = self.dirstate.parents() - if not force and pl[1] != nullid: - raise util.Abort(_("outstanding uncommitted merges")) - - err = False - - p1, p2 = pl[0], node - pa = self.changelog.ancestor(p1, p2) - m1n = self.changelog.read(p1)[0] - m2n = self.changelog.read(p2)[0] - man = self.manifest.ancestor(m1n, m2n) - m1 = self.manifest.read(m1n) - mf1 = self.manifest.readflags(m1n) - m2 = self.manifest.read(m2n).copy() - mf2 = self.manifest.readflags(m2n) - ma = self.manifest.read(man) - mfa = self.manifest.readflags(man) - - modified, added, removed, deleted, unknown = self.changes() - - # is this a jump, or a merge? i.e. is there a linear path - # from p1 to p2? - linear_path = (pa == p1 or pa == p2) - - if allow and linear_path: - raise util.Abort(_("there is nothing to merge, just use " - "'hg update' or look at 'hg heads'")) - if allow and not forcemerge: - if modified or added or removed: - raise util.Abort(_("outstanding uncommitted changes")) - - if not forcemerge and not force: - for f in unknown: - if f in m2: - t1 = self.wread(f) - t2 = self.file(f).read(m2[f]) - if cmp(t1, t2) != 0: - raise util.Abort(_("'%s' already exists in the working" - " dir and differs from remote") % f) - - # resolve the manifest to determine which files - # we care about merging - self.ui.note(_("resolving manifests\n")) - self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") % - (force, allow, moddirstate, linear_path)) - self.ui.debug(_(" ancestor %s local %s remote %s\n") % - (short(man), short(m1n), short(m2n))) - - merge = {} - get = {} - remove = [] - - # construct a working dir manifest - mw = m1.copy() - mfw = mf1.copy() - umap = dict.fromkeys(unknown) - - for f in added + modified + unknown: - mw[f] = "" - mfw.set(f, util.is_exec(self.wjoin(f), mfw.execf(f))) - - if moddirstate and not wlock: - wlock = self.wlock() - - for f in deleted + removed: - if f in mw: - del mw[f] - - # If we're jumping between revisions (as opposed to merging), - # and if neither the working directory nor the target rev has - # the file, then we need to remove it from the dirstate, to - # prevent the dirstate from listing the file when it is no - # longer in the manifest. - if moddirstate and linear_path and f not in m2: - self.dirstate.forget((f,)) - - # Compare manifests - for f, n in mw.iteritems(): - if choose and not choose(f): - continue - if f in m2: - s = 0 - - # is the wfile new since m1, and match m2? - if f not in m1: - t1 = self.wread(f) - t2 = self.file(f).read(m2[f]) - if cmp(t1, t2) == 0: - n = m2[f] - del t1, t2 - - # are files different? - if n != m2[f]: - a = ma.get(f, nullid) - # are both different from the ancestor? - if n != a and m2[f] != a: - self.ui.debug(_(" %s versions differ, resolve\n") % f) - # merge executable bits - # "if we changed or they changed, change in merge" - a, b, c = mfa.execf(f), mfw.execf(f), mf2.execf(f) - mode = ((a^b) | (a^c)) ^ a - merge[f] = (m1.get(f, nullid), m2[f], mode) - s = 1 - # are we clobbering? - # is remote's version newer? - # or are we going back in time? - elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]): - self.ui.debug(_(" remote %s is newer, get\n") % f) - get[f] = m2[f] - s = 1 - elif f in umap or f in added: - # this unknown file is the same as the checkout - # we need to reset the dirstate if the file was added - get[f] = m2[f] - - if not s and mfw.execf(f) != mf2.execf(f): - if force: - self.ui.debug(_(" updating permissions for %s\n") % f) - util.set_exec(self.wjoin(f), mf2.execf(f)) - else: - a, b, c = mfa.execf(f), mfw.execf(f), mf2.execf(f) - mode = ((a^b) | (a^c)) ^ a - if mode != b: - self.ui.debug(_(" updating permissions for %s\n") - % f) - util.set_exec(self.wjoin(f), mode) - del m2[f] - elif f in ma: - if n != ma[f]: - r = _("d") - if not force and (linear_path or allow): - r = self.ui.prompt( - (_(" local changed %s which remote deleted\n") % f) + - _("(k)eep or (d)elete?"), _("[kd]"), _("k")) - if r == _("d"): - remove.append(f) - else: - self.ui.debug(_("other deleted %s\n") % f) - remove.append(f) # other deleted it - else: - # file is created on branch or in working directory - if force and f not in umap: - self.ui.debug(_("remote deleted %s, clobbering\n") % f) - remove.append(f) - elif n == m1.get(f, nullid): # same as parent - if p2 == pa: # going backwards? - self.ui.debug(_("remote deleted %s\n") % f) - remove.append(f) - else: - self.ui.debug(_("local modified %s, keeping\n") % f) - else: - self.ui.debug(_("working dir created %s, keeping\n") % f) - - for f, n in m2.iteritems(): - if choose and not choose(f): - continue - if f[0] == "/": - continue - if f in ma and n != ma[f]: - r = _("k") - if not force and (linear_path or allow): - r = self.ui.prompt( - (_("remote changed %s which local deleted\n") % f) + - _("(k)eep or (d)elete?"), _("[kd]"), _("k")) - if r == _("k"): - get[f] = n - elif f not in ma: - self.ui.debug(_("remote created %s\n") % f) - get[f] = n - else: - if force or p2 == pa: # going backwards? - self.ui.debug(_("local deleted %s, recreating\n") % f) - get[f] = n - else: - self.ui.debug(_("local deleted %s\n") % f) - - del mw, m1, m2, ma - - if force: - for f in merge: - get[f] = merge[f][1] - merge = {} - - if linear_path or force: - # we don't need to do any magic, just jump to the new rev - branch_merge = False - p1, p2 = p2, nullid - else: - if not allow: - self.ui.status(_("this update spans a branch" - " affecting the following files:\n")) - fl = merge.keys() + get.keys() - fl.sort() - for f in fl: - cf = "" - if f in merge: - cf = _(" (resolve)") - self.ui.status(" %s%s\n" % (f, cf)) - self.ui.warn(_("aborting update spanning branches!\n")) - self.ui.status(_("(use 'hg merge' to merge across branches" - " or 'hg update -C' to lose changes)\n")) - return 1 - branch_merge = True - - xp1 = hex(p1) - xp2 = hex(p2) - if p2 == nullid: xxp2 = '' - else: xxp2 = xp2 - - self.hook('preupdate', throw=True, parent1=xp1, parent2=xxp2) - - # get the files we don't need to change - files = get.keys() - files.sort() - for f in files: - if f[0] == "/": - continue - self.ui.note(_("getting %s\n") % f) - t = self.file(f).read(get[f]) - self.wwrite(f, t) - util.set_exec(self.wjoin(f), mf2.execf(f)) - if moddirstate: - if branch_merge: - self.dirstate.update([f], 'n', st_mtime=-1) - else: - self.dirstate.update([f], 'n') - - # merge the tricky bits - failedmerge = [] - files = merge.keys() - files.sort() - for f in files: - self.ui.status(_("merging %s\n") % f) - my, other, flag = merge[f] - ret = self.merge3(f, my, other, xp1, xp2) - if ret: - err = True - failedmerge.append(f) - util.set_exec(self.wjoin(f), flag) - if moddirstate: - if branch_merge: - # We've done a branch merge, mark this file as merged - # so that we properly record the merger later - self.dirstate.update([f], 'm') - else: - # We've update-merged a locally modified file, so - # we set the dirstate to emulate a normal checkout - # of that file some time in the past. Thus our - # merge will appear as a normal local file - # modification. - f_len = len(self.file(f).read(other)) - self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1) - remove.sort() - for f in remove: - self.ui.note(_("removing %s\n") % f) - util.audit_path(f) - try: - util.unlink(self.wjoin(f)) - except OSError, inst: - if inst.errno != errno.ENOENT: - self.ui.warn(_("update failed to remove %s: %s!\n") % - (f, inst.strerror)) - if moddirstate: - if branch_merge: - self.dirstate.update(remove, 'r') - else: - self.dirstate.forget(remove) - - if moddirstate: - self.dirstate.setparents(p1, p2) - - if show_stats: - stats = ((len(get), _("updated")), - (len(merge) - len(failedmerge), _("merged")), - (len(remove), _("removed")), - (len(failedmerge), _("unresolved"))) - note = ", ".join([_("%d files %s") % s for s in stats]) - self.ui.status("%s\n" % note) - if moddirstate: - if branch_merge: - if failedmerge: - self.ui.status(_("There are unresolved merges," - " you can redo the full merge using:\n" - " hg update -C %s\n" - " hg merge %s\n" - % (self.changelog.rev(p1), - self.changelog.rev(p2)))) - else: - self.ui.status(_("(branch merge, don't forget to commit)\n")) - elif failedmerge: - self.ui.status(_("There are unresolved merges with" - " locally modified files.\n")) - - self.hook('update', parent1=xp1, parent2=xxp2, error=int(err)) - return err - - def merge3(self, fn, my, other, p1, p2): - """perform a 3-way merge in the working directory""" - - def temp(prefix, node): - pre = "%s~%s." % (os.path.basename(fn), prefix) - (fd, name) = tempfile.mkstemp(prefix=pre) - f = os.fdopen(fd, "wb") - self.wwrite(fn, fl.read(node), f) - f.close() - return name - - fl = self.file(fn) - base = fl.ancestor(my, other) - a = self.wjoin(fn) - b = temp("base", base) - c = temp("other", other) - - self.ui.note(_("resolving %s\n") % fn) - self.ui.debug(_("file %s: my %s other %s ancestor %s\n") % - (fn, short(my), short(other), short(base))) - - cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge") - or "hgmerge") - r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=self.root, - environ={'HG_FILE': fn, - 'HG_MY_NODE': p1, - 'HG_OTHER_NODE': p2, - 'HG_FILE_MY_NODE': hex(my), - 'HG_FILE_OTHER_NODE': hex(other), - 'HG_FILE_BASE_NODE': hex(base)}) - if r: - self.ui.warn(_("merging %s failed!\n") % fn) - - os.unlink(b) - os.unlink(c) - return r - - def verify(self): - filelinkrevs = {} - filenodes = {} - changesets = revisions = files = 0 - errors = [0] - warnings = [0] - neededmanifests = {} - - def err(msg): - self.ui.warn(msg + "\n") - errors[0] += 1 - - def warn(msg): - self.ui.warn(msg + "\n") - warnings[0] += 1 - - def checksize(obj, name): - d = obj.checksize() - if d[0]: - err(_("%s data length off by %d bytes") % (name, d[0])) - if d[1]: - err(_("%s index contains %d extra bytes") % (name, d[1])) - - def checkversion(obj, name): - if obj.version != revlog.REVLOGV0: - if not revlogv1: - warn(_("warning: `%s' uses revlog format 1") % name) - elif revlogv1: - warn(_("warning: `%s' uses revlog format 0") % name) - - revlogv1 = self.revlogversion != revlog.REVLOGV0 - if self.ui.verbose or revlogv1 != self.revlogv1: - self.ui.status(_("repository uses revlog format %d\n") % - (revlogv1 and 1 or 0)) - - seen = {} - self.ui.status(_("checking changesets\n")) - checksize(self.changelog, "changelog") - - for i in range(self.changelog.count()): - changesets += 1 - n = self.changelog.node(i) - l = self.changelog.linkrev(n) - if l != i: - err(_("incorrect link (%d) for changeset revision %d") %(l, i)) - if n in seen: - err(_("duplicate changeset at revision %d") % i) - seen[n] = 1 + def stream_in(self, remote): + fp = remote.stream_out() + resp = int(fp.readline()) + if resp != 0: + raise util.Abort(_('operation forbidden by server')) + self.ui.status(_('streaming all changes\n')) + total_files, total_bytes = map(int, fp.readline().split(' ', 1)) + self.ui.status(_('%d files to transfer, %s of data\n') % + (total_files, util.bytecount(total_bytes))) + start = time.time() + for i in xrange(total_files): + name, size = fp.readline().split('\0', 1) + size = int(size) + self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size))) + ofp = self.opener(name, 'w') + for chunk in util.filechunkiter(fp, limit=size): + ofp.write(chunk) + ofp.close() + elapsed = time.time() - start + self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') % + (util.bytecount(total_bytes), elapsed, + util.bytecount(total_bytes / elapsed))) + self.reload() + return len(self.heads()) + 1 - for p in self.changelog.parents(n): - if p not in self.changelog.nodemap: - err(_("changeset %s has unknown parent %s") % - (short(n), short(p))) - try: - changes = self.changelog.read(n) - except KeyboardInterrupt: - self.ui.warn(_("interrupted")) - raise - except Exception, inst: - err(_("unpacking changeset %s: %s") % (short(n), inst)) - continue - - neededmanifests[changes[0]] = n - - for f in changes[3]: - filelinkrevs.setdefault(f, []).append(i) - - seen = {} - self.ui.status(_("checking manifests\n")) - checkversion(self.manifest, "manifest") - checksize(self.manifest, "manifest") - - for i in range(self.manifest.count()): - n = self.manifest.node(i) - l = self.manifest.linkrev(n) - - if l < 0 or l >= self.changelog.count(): - err(_("bad manifest link (%d) at revision %d") % (l, i)) - - if n in neededmanifests: - del neededmanifests[n] + def clone(self, remote, heads=[], stream=False): + '''clone remote repository. - if n in seen: - err(_("duplicate manifest at revision %d") % i) - - seen[n] = 1 - - for p in self.manifest.parents(n): - if p not in self.manifest.nodemap: - err(_("manifest %s has unknown parent %s") % - (short(n), short(p))) - - try: - delta = mdiff.patchtext(self.manifest.delta(n)) - except KeyboardInterrupt: - self.ui.warn(_("interrupted")) - raise - except Exception, inst: - err(_("unpacking manifest %s: %s") % (short(n), inst)) - continue - - try: - ff = [ l.split('\0') for l in delta.splitlines() ] - for f, fn in ff: - filenodes.setdefault(f, {})[bin(fn[:40])] = 1 - except (ValueError, TypeError), inst: - err(_("broken delta in manifest %s: %s") % (short(n), inst)) - - self.ui.status(_("crosschecking files in changesets and manifests\n")) - - for m, c in neededmanifests.items(): - err(_("Changeset %s refers to unknown manifest %s") % - (short(m), short(c))) - del neededmanifests - - for f in filenodes: - if f not in filelinkrevs: - err(_("file %s in manifest but not in changesets") % f) + keyword arguments: + heads: list of revs to clone (forces use of pull) + stream: use streaming clone if possible''' - for f in filelinkrevs: - if f not in filenodes: - err(_("file %s in changeset but not in manifest") % f) - - self.ui.status(_("checking files\n")) - ff = filenodes.keys() - ff.sort() - for f in ff: - if f == "/dev/null": - continue - files += 1 - if not f: - err(_("file without name in manifest %s") % short(n)) - continue - fl = self.file(f) - checkversion(fl, f) - checksize(fl, f) - - nodes = {nullid: 1} - seen = {} - for i in range(fl.count()): - revisions += 1 - n = fl.node(i) - - if n in seen: - err(_("%s: duplicate revision %d") % (f, i)) - if n not in filenodes[f]: - err(_("%s: %d:%s not in manifests") % (f, i, short(n))) - else: - del filenodes[f][n] + # now, all clients that can request uncompressed clones can + # read repo formats supported by all servers that can serve + # them. - flr = fl.linkrev(n) - if flr not in filelinkrevs.get(f, []): - err(_("%s:%s points to unexpected changeset %d") - % (f, short(n), flr)) - else: - filelinkrevs[f].remove(flr) - - # verify contents - try: - t = fl.read(n) - except KeyboardInterrupt: - self.ui.warn(_("interrupted")) - raise - except Exception, inst: - err(_("unpacking file %s %s: %s") % (f, short(n), inst)) + # if revlog format changes, client will have to check version + # and format flags on "stream" capability, and use + # uncompressed only if compatible. - # verify parents - (p1, p2) = fl.parents(n) - if p1 not in nodes: - err(_("file %s:%s unknown parent 1 %s") % - (f, short(n), short(p1))) - if p2 not in nodes: - err(_("file %s:%s unknown parent 2 %s") % - (f, short(n), short(p1))) - nodes[n] = 1 - - # cross-check - for node in filenodes[f]: - err(_("node %s in manifests not in %s") % (hex(node), f)) - - self.ui.status(_("%d files, %d changesets, %d total revisions\n") % - (files, changesets, revisions)) - - if warnings[0]: - self.ui.warn(_("%d warnings encountered!\n") % warnings[0]) - if errors[0]: - self.ui.warn(_("%d integrity errors encountered!\n") % errors[0]) - return 1 + if stream and not heads and remote.capable('stream'): + return self.stream_in(remote) + return self.pull(remote, heads) # used to avoid circular references so destructors work def aftertrans(base): @@ -2209,3 +1757,8 @@ os.path.join(p, "undo.dirstate")) return a +def instance(ui, path, create): + return localrepository(ui, util.drop_scheme('file', path), create) + +def islocal(path): + return True
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/merge.py Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,337 @@ +# merge.py - directory-level update/merge handling for Mercurial +# +# Copyright 2006 Matt Mackall <mpm@selenic.com> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +from node import * +from i18n import gettext as _ +from demandload import * +demandload(globals(), "util os tempfile") + +def merge3(repo, fn, my, other, p1, p2): + """perform a 3-way merge in the working directory""" + + def temp(prefix, node): + pre = "%s~%s." % (os.path.basename(fn), prefix) + (fd, name) = tempfile.mkstemp(prefix=pre) + f = os.fdopen(fd, "wb") + repo.wwrite(fn, fl.read(node), f) + f.close() + return name + + fl = repo.file(fn) + base = fl.ancestor(my, other) + a = repo.wjoin(fn) + b = temp("base", base) + c = temp("other", other) + + repo.ui.note(_("resolving %s\n") % fn) + repo.ui.debug(_("file %s: my %s other %s ancestor %s\n") % + (fn, short(my), short(other), short(base))) + + cmd = (os.environ.get("HGMERGE") or repo.ui.config("ui", "merge") + or "hgmerge") + r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=repo.root, + environ={'HG_FILE': fn, + 'HG_MY_NODE': p1, + 'HG_OTHER_NODE': p2, + 'HG_FILE_MY_NODE': hex(my), + 'HG_FILE_OTHER_NODE': hex(other), + 'HG_FILE_BASE_NODE': hex(base)}) + if r: + repo.ui.warn(_("merging %s failed!\n") % fn) + + os.unlink(b) + os.unlink(c) + return r + +def update(repo, node, branchmerge=False, force=False, partial=None, + wlock=None, show_stats=True, remind=True): + + overwrite = force and not branchmerge + forcemerge = force and branchmerge + + if not wlock: + wlock = repo.wlock() + + ### check phase + + pl = repo.dirstate.parents() + if not overwrite and pl[1] != nullid: + raise util.Abort(_("outstanding uncommitted merges")) + + p1, p2 = pl[0], node + pa = repo.changelog.ancestor(p1, p2) + + # is there a linear path from p1 to p2? + linear_path = (pa == p1 or pa == p2) + if branchmerge and linear_path: + raise util.Abort(_("there is nothing to merge, just use " + "'hg update' or look at 'hg heads'")) + + if not overwrite and not linear_path and not branchmerge: + raise util.Abort(_("update spans branches, use 'hg merge' " + "or 'hg update -C' to lose changes")) + + modified, added, removed, deleted, unknown = repo.changes() + if branchmerge and not forcemerge: + if modified or added or removed: + raise util.Abort(_("outstanding uncommitted changes")) + + m1n = repo.changelog.read(p1)[0] + m2n = repo.changelog.read(p2)[0] + man = repo.manifest.ancestor(m1n, m2n) + m1 = repo.manifest.read(m1n) + mf1 = repo.manifest.readflags(m1n) + m2 = repo.manifest.read(m2n).copy() + mf2 = repo.manifest.readflags(m2n) + ma = repo.manifest.read(man) + mfa = repo.manifest.readflags(man) + + if not forcemerge and not overwrite: + for f in unknown: + if f in m2: + t1 = repo.wread(f) + t2 = repo.file(f).read(m2[f]) + if cmp(t1, t2) != 0: + raise util.Abort(_("'%s' already exists in the working" + " dir and differs from remote") % f) + + # resolve the manifest to determine which files + # we care about merging + repo.ui.note(_("resolving manifests\n")) + repo.ui.debug(_(" overwrite %s branchmerge %s partial %s linear %s\n") % + (overwrite, branchmerge, partial and True or False, linear_path)) + repo.ui.debug(_(" ancestor %s local %s remote %s\n") % + (short(man), short(m1n), short(m2n))) + + merge = {} + get = {} + remove = [] + + # construct a working dir manifest + mw = m1.copy() + mfw = mf1.copy() + umap = dict.fromkeys(unknown) + + for f in added + modified + unknown: + mw[f] = "" + mfw.set(f, util.is_exec(repo.wjoin(f), mfw.execf(f))) + + for f in deleted + removed: + if f in mw: + del mw[f] + + # If we're jumping between revisions (as opposed to merging), + # and if neither the working directory nor the target rev has + # the file, then we need to remove it from the dirstate, to + # prevent the dirstate from listing the file when it is no + # longer in the manifest. + if not partial and linear_path and f not in m2: + repo.dirstate.forget((f,)) + + # Compare manifests + for f, n in mw.iteritems(): + if partial and not partial(f): + continue + if f in m2: + s = 0 + + # is the wfile new since m1, and match m2? + if f not in m1: + t1 = repo.wread(f) + t2 = repo.file(f).read(m2[f]) + if cmp(t1, t2) == 0: + n = m2[f] + del t1, t2 + + # are files different? + if n != m2[f]: + a = ma.get(f, nullid) + # are both different from the ancestor? + if n != a and m2[f] != a: + repo.ui.debug(_(" %s versions differ, resolve\n") % f) + # merge executable bits + # "if we changed or they changed, change in merge" + a, b, c = mfa.execf(f), mfw.execf(f), mf2.execf(f) + mode = ((a^b) | (a^c)) ^ a + merge[f] = (m1.get(f, nullid), m2[f], mode) + s = 1 + # are we clobbering? + # is remote's version newer? + # or are we going back in time? + elif overwrite or m2[f] != a or (p2 == pa and mw[f] == m1[f]): + repo.ui.debug(_(" remote %s is newer, get\n") % f) + get[f] = m2[f] + s = 1 + elif f in umap or f in added: + # this unknown file is the same as the checkout + # we need to reset the dirstate if the file was added + get[f] = m2[f] + + if not s and mfw.execf(f) != mf2.execf(f): + if overwrite: + repo.ui.debug(_(" updating permissions for %s\n") % f) + util.set_exec(repo.wjoin(f), mf2.execf(f)) + else: + a, b, c = mfa.execf(f), mfw.execf(f), mf2.execf(f) + mode = ((a^b) | (a^c)) ^ a + if mode != b: + repo.ui.debug(_(" updating permissions for %s\n") + % f) + util.set_exec(repo.wjoin(f), mode) + del m2[f] + elif f in ma: + if n != ma[f]: + r = _("d") + if not overwrite and (linear_path or branchmerge): + r = repo.ui.prompt( + (_(" local changed %s which remote deleted\n") % f) + + _("(k)eep or (d)elete?"), _("[kd]"), _("k")) + if r == _("d"): + remove.append(f) + else: + repo.ui.debug(_("other deleted %s\n") % f) + remove.append(f) # other deleted it + else: + # file is created on branch or in working directory + if overwrite and f not in umap: + repo.ui.debug(_("remote deleted %s, clobbering\n") % f) + remove.append(f) + elif n == m1.get(f, nullid): # same as parent + if p2 == pa: # going backwards? + repo.ui.debug(_("remote deleted %s\n") % f) + remove.append(f) + else: + repo.ui.debug(_("local modified %s, keeping\n") % f) + else: + repo.ui.debug(_("working dir created %s, keeping\n") % f) + + for f, n in m2.iteritems(): + if partial and not partial(f): + continue + if f[0] == "/": + continue + if f in ma and n != ma[f]: + r = _("k") + if not overwrite and (linear_path or branchmerge): + r = repo.ui.prompt( + (_("remote changed %s which local deleted\n") % f) + + _("(k)eep or (d)elete?"), _("[kd]"), _("k")) + if r == _("k"): + get[f] = n + elif f not in ma: + repo.ui.debug(_("remote created %s\n") % f) + get[f] = n + else: + if overwrite or p2 == pa: # going backwards? + repo.ui.debug(_("local deleted %s, recreating\n") % f) + get[f] = n + else: + repo.ui.debug(_("local deleted %s\n") % f) + + del mw, m1, m2, ma + + if overwrite: + for f in merge: + get[f] = merge[f][1] + merge = {} + + if linear_path or overwrite: + # we don't need to do any magic, just jump to the new rev + p1, p2 = p2, nullid + + xp1 = hex(p1) + xp2 = hex(p2) + if p2 == nullid: xxp2 = '' + else: xxp2 = xp2 + + repo.hook('preupdate', throw=True, parent1=xp1, parent2=xxp2) + + # get the files we don't need to change + files = get.keys() + files.sort() + for f in files: + if f[0] == "/": + continue + repo.ui.note(_("getting %s\n") % f) + t = repo.file(f).read(get[f]) + repo.wwrite(f, t) + util.set_exec(repo.wjoin(f), mf2.execf(f)) + if not partial: + if branchmerge: + repo.dirstate.update([f], 'n', st_mtime=-1) + else: + repo.dirstate.update([f], 'n') + + # merge the tricky bits + unresolved = [] + files = merge.keys() + files.sort() + for f in files: + repo.ui.status(_("merging %s\n") % f) + my, other, flag = merge[f] + ret = merge3(repo, f, my, other, xp1, xp2) + if ret: + unresolved.append(f) + util.set_exec(repo.wjoin(f), flag) + if not partial: + if branchmerge: + # We've done a branch merge, mark this file as merged + # so that we properly record the merger later + repo.dirstate.update([f], 'm') + else: + # We've update-merged a locally modified file, so + # we set the dirstate to emulate a normal checkout + # of that file some time in the past. Thus our + # merge will appear as a normal local file + # modification. + f_len = len(repo.file(f).read(other)) + repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1) + + remove.sort() + for f in remove: + repo.ui.note(_("removing %s\n") % f) + util.audit_path(f) + try: + util.unlink(repo.wjoin(f)) + except OSError, inst: + if inst.errno != errno.ENOENT: + repo.ui.warn(_("update failed to remove %s: %s!\n") % + (f, inst.strerror)) + if not partial: + if branchmerge: + repo.dirstate.update(remove, 'r') + else: + repo.dirstate.forget(remove) + + if not partial: + repo.dirstate.setparents(p1, p2) + + if show_stats: + stats = ((len(get), _("updated")), + (len(merge) - len(unresolved), _("merged")), + (len(remove), _("removed")), + (len(unresolved), _("unresolved"))) + note = ", ".join([_("%d files %s") % s for s in stats]) + repo.ui.status("%s\n" % note) + if not partial: + if branchmerge: + if unresolved: + repo.ui.status(_("There are unresolved merges," + " you can redo the full merge using:\n" + " hg update -C %s\n" + " hg merge %s\n" + % (repo.changelog.rev(p1), + repo.changelog.rev(p2)))) + elif remind: + repo.ui.status(_("(branch merge, don't forget to commit)\n")) + elif unresolved: + repo.ui.status(_("There are unresolved merges with" + " locally modified files.\n")) + + repo.hook('update', parent1=xp1, parent2=xxp2, error=len(unresolved)) + return len(unresolved) +
--- a/mercurial/remoterepo.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/remoterepo.py Wed Aug 09 15:03:46 2006 -0500 @@ -5,7 +5,9 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -class remoterepository(object): +import repo + +class remoterepository(repo.repository): def dev(self): return -1
--- a/mercurial/repo.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/repo.py Wed Aug 09 15:03:46 2006 -0500 @@ -5,4 +5,19 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -class RepoError(Exception): pass +class RepoError(Exception): + pass + +class repository(object): + def capable(self, name): + '''tell whether repo supports named capability. + return False if not supported. + if boolean capability, return True. + if string capability, return string.''' + name_eq = name + '=' + for cap in self.capabilities: + if name == cap: + return True + if cap.startswith(name_eq): + return cap[len(name_eq):] + return False
--- a/mercurial/revlog.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/revlog.py Wed Aug 09 15:03:46 2006 -0500 @@ -469,7 +469,8 @@ return self.nodemap[node] except KeyError: raise RevlogError(_('%s: no node %s') % (self.indexfile, hex(node))) - def linkrev(self, node): return self.index[self.rev(node)][-4] + def linkrev(self, node): + return (node == nullid) and -1 or self.index[self.rev(node)][-4] def parents(self, node): if node == nullid: return (nullid, nullid) r = self.rev(node) @@ -743,13 +744,8 @@ def lookup(self, id): """locate a node based on revision number or subset of hex nodeid""" - if id in self.nodemap: - return id if type(id) == type(0): - rev = id - if rev < 0: rev = self.count() + rev - if rev < 0 or rev >= self.count(): return None - return self.node(rev) + return self.node(id) try: rev = int(id) if str(rev) != id: raise ValueError @@ -762,10 +758,13 @@ if hex(n).startswith(id): c.append(n) if len(c) > 1: raise RevlogError(_("Ambiguous identifier")) - if len(c) < 1: raise RevlogError(_("No match found")) - return c[0] + if len(c) == 1: return c[0] - return None + # might need fixing if we change hash lengths + if len(id) == 20 and id in self.nodemap: + return id + + raise RevlogError(_("No match found")) def diff(self, a, b): """return a delta between two revisions"""
--- a/mercurial/sshrepo.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/sshrepo.py Wed Aug 09 15:03:46 2006 -0500 @@ -13,7 +13,7 @@ class sshrepository(remoterepository): def __init__(self, ui, path, create=0): - self.url = path + self._url = path self.ui = ui m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path) @@ -48,6 +48,9 @@ self.validate_repo(ui, sshcmd, args, remotecmd) + def url(self): + return self._url + def validate_repo(self, ui, sshcmd, args, remotecmd): cmd = '%s %s "%s -R %s serve --stdio"' cmd = cmd % (sshcmd, args, remotecmd, self.path) @@ -180,7 +183,7 @@ return 1 return int(r) - def addchangegroup(self, cg, source): + def addchangegroup(self, cg, source, url): d = self.call("addchangegroup") if d: raise hg.RepoError(_("push refused: %s") % d) @@ -198,3 +201,8 @@ if not r: return 1 return int(r) + + def stream_out(self): + return self.do_cmd('stream_out') + +instance = sshrepository
--- a/mercurial/sshserver.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/sshserver.py Wed Aug 09 15:03:46 2006 -0500 @@ -8,7 +8,7 @@ from demandload import demandload from i18n import gettext as _ from node import * -demandload(globals(), "os sys tempfile util") +demandload(globals(), "os streamclone sys tempfile util") class sshserver(object): def __init__(self, ui, repo): @@ -60,8 +60,10 @@ capabilities: space separated list of tokens ''' - r = "capabilities: unbundle\n" - self.respond(r) + caps = ['unbundle'] + if self.ui.configbool('server', 'uncompressed'): + caps.append('stream=%d' % self.repo.revlogversion) + self.respond("capabilities: %s\n" % (' '.join(caps),)) def do_lock(self): '''DEPRECATED - allowing remote client to lock repo is not safe''' @@ -115,9 +117,13 @@ return self.respond("") - r = self.repo.addchangegroup(self.fin, 'serve') + r = self.repo.addchangegroup(self.fin, 'serve', self.client_url()) self.respond(str(r)) + def client_url(self): + client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0] + return 'remote:ssh:' + client + def do_unbundle(self): their_heads = self.getarg()[1].split() @@ -157,7 +163,7 @@ # push can proceed fp.seek(0) - r = self.repo.addchangegroup(fp, 'serve') + r = self.repo.addchangegroup(fp, 'serve', self.client_url()) self.respond(str(r)) finally: if not was_locked: @@ -167,3 +173,5 @@ fp.close() os.unlink(tempname) + def do_stream_out(self): + streamclone.stream_out(self.repo, self.fout)
--- a/mercurial/statichttprepo.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/statichttprepo.py Wed Aug 09 15:03:46 2006 -0500 @@ -7,9 +7,10 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from demandload import demandload +from demandload import * +from i18n import gettext as _ demandload(globals(), "changelog filelog httprangereader") -demandload(globals(), "localrepo manifest os urllib urllib2") +demandload(globals(), "localrepo manifest os urllib urllib2 util") class rangereader(httprangereader.httprangereader): def read(self, size=None): @@ -30,6 +31,7 @@ class statichttprepository(localrepo.localrepository): def __init__(self, ui, path): + self._url = path self.path = (path + "/.hg") self.ui = ui self.revlogversion = 0 @@ -41,8 +43,22 @@ self.encodepats = None self.decodepats = None + def url(self): + return 'static-' + self._url + def dev(self): return -1 def local(self): return False + +def instance(ui, path, create): + if create: + raise util.Abort(_('cannot create new static-http repository')) + if path.startswith('old-http:'): + ui.warn(_("old-http:// syntax is deprecated, " + "please use static-http:// instead\n")) + path = path[4:] + else: + path = path[7:] + return statichttprepository(ui, path)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/streamclone.py Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,90 @@ +# streamclone.py - streaming clone server support for mercurial +# +# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +from demandload import demandload +from i18n import gettext as _ +demandload(globals(), "os stat util") + +# if server supports streaming clone, it advertises "stream" +# capability with value that is version+flags of repo it is serving. +# client only streams if it can read that repo format. + +def walkrepo(root): + '''iterate over metadata files in repository. + walk in natural (sorted) order. + yields 2-tuples: name of .d or .i file, size of file.''' + + strip_count = len(root) + len(os.sep) + def walk(path, recurse): + ents = os.listdir(path) + ents.sort() + for e in ents: + pe = os.path.join(path, e) + st = os.lstat(pe) + if stat.S_ISDIR(st.st_mode): + if recurse: + for x in walk(pe, True): + yield x + else: + if not stat.S_ISREG(st.st_mode) or len(e) < 2: + continue + sfx = e[-2:] + if sfx in ('.d', '.i'): + yield pe[strip_count:], st.st_size + # write file data first + for x in walk(os.path.join(root, 'data'), True): + yield x + # write manifest before changelog + meta = list(walk(root, False)) + meta.sort() + meta.reverse() + for x in meta: + yield x + +# stream file format is simple. +# +# server writes out line that says how many files, how many total +# bytes. separator is ascii space, byte counts are strings. +# +# then for each file: +# +# server writes out line that says file name, how many bytes in +# file. separator is ascii nul, byte count is string. +# +# server writes out raw file data. + +def stream_out(repo, fileobj): + '''stream out all metadata files in repository. + writes to file-like object, must support write() and optional flush().''' + + if not repo.ui.configbool('server', 'uncompressed'): + fileobj.write('1\n') + return + + fileobj.write('0\n') + + # get consistent snapshot of repo. lock during scan so lock not + # needed while we stream, and commits can happen. + lock = repo.lock() + repo.ui.debug('scanning\n') + entries = [] + total_bytes = 0 + for name, size in walkrepo(repo.path): + entries.append((name, size)) + total_bytes += size + lock.release() + + repo.ui.debug('%d files, %d bytes to transfer\n' % + (len(entries), total_bytes)) + fileobj.write('%d %d\n' % (len(entries), total_bytes)) + for name, size in entries: + repo.ui.debug('sending %s (%d bytes)\n' % (name, size)) + fileobj.write('%s\0%d\n' % (name, size)) + for chunk in util.filechunkiter(repo.opener(name), limit=size): + fileobj.write(chunk) + flush = getattr(fileobj, 'flush', None) + if flush: flush()
--- a/mercurial/templater.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/templater.py Wed Aug 09 15:03:46 2006 -0500 @@ -241,6 +241,7 @@ return text.replace('\n', '<br/>\n') def obfuscate(text): + text = unicode(text, 'utf-8', 'replace') return ''.join(['&#%d;' % ord(c) for c in text]) def domain(author):
--- a/mercurial/ui.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/ui.py Wed Aug 09 15:03:46 2006 -0500 @@ -197,7 +197,7 @@ user = os.environ.get("EMAIL") if user is None: try: - user = '%s@%s' % (getpass.getuser(), socket.getfqdn()) + user = '%s@%s' % (util.getuser(), socket.getfqdn()) except KeyError: raise util.Abort(_("Please specify a username.")) return user @@ -209,7 +209,7 @@ def expandpath(self, loc, default=None): """Return repository location relative to cwd or from [paths]""" - if "://" in loc or os.path.exists(loc): + if "://" in loc or os.path.isdir(loc): return loc path = self.config("paths", loc) @@ -217,12 +217,6 @@ path = self.config("paths", default) return path or loc - def setconfig_remoteopts(self, **opts): - if opts.get('ssh'): - self.setconfig("ui", "ssh", opts['ssh']) - if opts.get('remotecmd'): - self.setconfig("ui", "remotecmd", opts['remotecmd']) - def write(self, *args): if self.header: if self.header != self.prev_header:
--- a/mercurial/util.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/util.py Wed Aug 09 15:03:46 2006 -0500 @@ -12,9 +12,13 @@ from i18n import gettext as _ from demandload import * -demandload(globals(), "cStringIO errno popen2 re shutil sys tempfile") +demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile") demandload(globals(), "os threading time") +# used by parsedate +defaultdateformats = ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M', + '%a %b %d %H:%M:%S %Y') + class SignalInterrupt(Exception): """Exception raised on SIGTERM and SIGHUP.""" @@ -89,11 +93,15 @@ return p_name return default -def patch(strip, patchname, ui): +def patch(strip, patchname, ui, cwd=None): """apply the patch <patchname> to the working directory. a list of patched files is returned""" patcher = find_in_path('gpatch', os.environ.get('PATH', ''), 'patch') - fp = os.popen('%s -p%d < "%s"' % (patcher, strip, patchname)) + args = [] + if cwd: + args.append('-d %s' % shellquote(cwd)) + fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip, + shellquote(patchname))) files = {} for line in fp: line = line.rstrip() @@ -506,6 +514,20 @@ except AttributeError: return os.name == 'nt' and 'command' in os.environ.get('comspec', '') +getuser_fallback = None + +def getuser(): + '''return name of current user''' + try: + return getpass.getuser() + except ImportError: + # import of pwd will fail on windows - try fallback + if getuser_fallback: + return getuser_fallback() + # raised if win32api not available + raise Abort(_('user name not available - set USERNAME ' + 'environment variable')) + # Platform specific variants if os.name == 'nt': demandload(globals(), "msvcrt") @@ -589,6 +611,9 @@ def samestat(s1, s2): return False + def shellquote(s): + return '"%s"' % s.replace('"', '\\"') + def explain_exit(code): return _("exited with status %d") % code, code @@ -678,6 +703,9 @@ else: raise + def shellquote(s): + return "'%s'" % s.replace("'", "'\\''") + def testpid(pid): '''return False if pid dead, True if running or not sure''' try: @@ -883,10 +911,12 @@ when = int(time.mktime(time.strptime(date, format))) + offset return when, offset -def parsedate(string, formats=('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M')): +def parsedate(string, formats=None): """parse a localized time string and return a (unixtime, offset) tuple. The date may be a "unixtime offset" string or in one of the specified formats.""" + if not formats: + formats = defaultdateformats try: when, offset = map(int, string.split(' ')) except ValueError: @@ -955,3 +985,32 @@ else: _rcpath = os_rcpath() return _rcpath + +def bytecount(nbytes): + '''return byte count formatted as readable string, with units''' + + units = ( + (100, 1<<30, _('%.0f GB')), + (10, 1<<30, _('%.1f GB')), + (1, 1<<30, _('%.2f GB')), + (100, 1<<20, _('%.0f MB')), + (10, 1<<20, _('%.1f MB')), + (1, 1<<20, _('%.2f MB')), + (100, 1<<10, _('%.0f KB')), + (10, 1<<10, _('%.1f KB')), + (1, 1<<10, _('%.2f KB')), + (1, 1, _('%.0f bytes')), + ) + + for multiplier, divisor, format in units: + if nbytes >= divisor * multiplier: + return format % (nbytes / float(divisor)) + return units[-1][2] % nbytes + +def drop_scheme(scheme, path): + sc = scheme + ':' + if path.startswith(sc): + path = path[len(sc):] + if path.startswith('//'): + path = path[2:] + return path
--- a/mercurial/util_win32.py Wed Aug 09 14:53:03 2006 -0500 +++ b/mercurial/util_win32.py Wed Aug 09 15:03:46 2006 -0500 @@ -297,3 +297,5 @@ win32file.SetEndOfFile(self.handle) except pywintypes.error, err: raise WinIOError(err) + +getuser_fallback = win32api.GetUserName
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/verify.py Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,200 @@ +# verify.py - repository integrity checking for Mercurial +# +# Copyright 2006 Matt Mackall <mpm@selenic.com> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +from node import * +from i18n import gettext as _ +import revlog, mdiff + +def verify(repo): + filelinkrevs = {} + filenodes = {} + changesets = revisions = files = 0 + errors = [0] + warnings = [0] + neededmanifests = {} + + def err(msg): + repo.ui.warn(msg + "\n") + errors[0] += 1 + + def warn(msg): + repo.ui.warn(msg + "\n") + warnings[0] += 1 + + def checksize(obj, name): + d = obj.checksize() + if d[0]: + err(_("%s data length off by %d bytes") % (name, d[0])) + if d[1]: + err(_("%s index contains %d extra bytes") % (name, d[1])) + + def checkversion(obj, name): + if obj.version != revlog.REVLOGV0: + if not revlogv1: + warn(_("warning: `%s' uses revlog format 1") % name) + elif revlogv1: + warn(_("warning: `%s' uses revlog format 0") % name) + + revlogv1 = repo.revlogversion != revlog.REVLOGV0 + if repo.ui.verbose or revlogv1 != repo.revlogv1: + repo.ui.status(_("repository uses revlog format %d\n") % + (revlogv1 and 1 or 0)) + + seen = {} + repo.ui.status(_("checking changesets\n")) + checksize(repo.changelog, "changelog") + + for i in range(repo.changelog.count()): + changesets += 1 + n = repo.changelog.node(i) + l = repo.changelog.linkrev(n) + if l != i: + err(_("incorrect link (%d) for changeset revision %d") %(l, i)) + if n in seen: + err(_("duplicate changeset at revision %d") % i) + seen[n] = 1 + + for p in repo.changelog.parents(n): + if p not in repo.changelog.nodemap: + err(_("changeset %s has unknown parent %s") % + (short(n), short(p))) + try: + changes = repo.changelog.read(n) + except KeyboardInterrupt: + repo.ui.warn(_("interrupted")) + raise + except Exception, inst: + err(_("unpacking changeset %s: %s") % (short(n), inst)) + continue + + neededmanifests[changes[0]] = n + + for f in changes[3]: + filelinkrevs.setdefault(f, []).append(i) + + seen = {} + repo.ui.status(_("checking manifests\n")) + checkversion(repo.manifest, "manifest") + checksize(repo.manifest, "manifest") + + for i in range(repo.manifest.count()): + n = repo.manifest.node(i) + l = repo.manifest.linkrev(n) + + if l < 0 or l >= repo.changelog.count(): + err(_("bad manifest link (%d) at revision %d") % (l, i)) + + if n in neededmanifests: + del neededmanifests[n] + + if n in seen: + err(_("duplicate manifest at revision %d") % i) + + seen[n] = 1 + + for p in repo.manifest.parents(n): + if p not in repo.manifest.nodemap: + err(_("manifest %s has unknown parent %s") % + (short(n), short(p))) + + try: + delta = mdiff.patchtext(repo.manifest.delta(n)) + except KeyboardInterrupt: + repo.ui.warn(_("interrupted")) + raise + except Exception, inst: + err(_("unpacking manifest %s: %s") % (short(n), inst)) + continue + + try: + ff = [ l.split('\0') for l in delta.splitlines() ] + for f, fn in ff: + filenodes.setdefault(f, {})[bin(fn[:40])] = 1 + except (ValueError, TypeError), inst: + err(_("broken delta in manifest %s: %s") % (short(n), inst)) + + repo.ui.status(_("crosschecking files in changesets and manifests\n")) + + for m, c in neededmanifests.items(): + err(_("Changeset %s refers to unknown manifest %s") % + (short(m), short(c))) + del neededmanifests + + for f in filenodes: + if f not in filelinkrevs: + err(_("file %s in manifest but not in changesets") % f) + + for f in filelinkrevs: + if f not in filenodes: + err(_("file %s in changeset but not in manifest") % f) + + repo.ui.status(_("checking files\n")) + ff = filenodes.keys() + ff.sort() + for f in ff: + if f == "/dev/null": + continue + files += 1 + if not f: + err(_("file without name in manifest %s") % short(n)) + continue + fl = repo.file(f) + checkversion(fl, f) + checksize(fl, f) + + nodes = {nullid: 1} + seen = {} + for i in range(fl.count()): + revisions += 1 + n = fl.node(i) + + if n in seen: + err(_("%s: duplicate revision %d") % (f, i)) + if n not in filenodes[f]: + err(_("%s: %d:%s not in manifests") % (f, i, short(n))) + else: + del filenodes[f][n] + + flr = fl.linkrev(n) + if flr not in filelinkrevs.get(f, []): + err(_("%s:%s points to unexpected changeset %d") + % (f, short(n), flr)) + else: + filelinkrevs[f].remove(flr) + + # verify contents + try: + t = fl.read(n) + except KeyboardInterrupt: + repo.ui.warn(_("interrupted")) + raise + except Exception, inst: + err(_("unpacking file %s %s: %s") % (f, short(n), inst)) + + # verify parents + (p1, p2) = fl.parents(n) + if p1 not in nodes: + err(_("file %s:%s unknown parent 1 %s") % + (f, short(n), short(p1))) + if p2 not in nodes: + err(_("file %s:%s unknown parent 2 %s") % + (f, short(n), short(p1))) + nodes[n] = 1 + + # cross-check + for node in filenodes[f]: + err(_("node %s in manifests not in %s") % (hex(node), f)) + + repo.ui.status(_("%d files, %d changesets, %d total revisions\n") % + (files, changesets, revisions)) + + if warnings[0]: + repo.ui.warn(_("%d warnings encountered!\n") % warnings[0]) + if errors[0]: + repo.ui.warn(_("%d integrity errors encountered!\n") % errors[0]) + return 1 +
--- a/templates/changelog-gitweb.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/changelog-gitweb.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -20,7 +20,7 @@ </div> <div class="page_nav"> -<a href="?cmd=summary;style=gitweb">summary</a> | changelog | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/> +<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;rev=#rev#;style=gitweb">shortlog</a> | changelog | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>#archives%archiveentry#<br/> <br/> #changenav%naventry#<br/> </div>
--- a/templates/changelog.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/changelog.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -6,6 +6,7 @@ <body> <div class="buttons"> +<a href="?sl=#rev#">shortlog</a> <a href="?cmd=tags">tags</a> <a href="?mf=#manifest|short#;path=/">manifest</a> #archives%archiveentry#
--- a/templates/changeset-gitweb.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/changeset-gitweb.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -10,7 +10,7 @@ </div> <div class="page_nav"> -<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;rev=#rev#;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a> | changeset | <a href="?cmd=changeset;node=#node#;style=raw">raw</a> #archives%archiveentry#<br/> +<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;rev=#rev#;style=gitweb">shortlog</a> | <a href="?cmd=changelog;rev=#rev#;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a> | changeset | <a href="?cmd=changeset;node=#node#;style=raw">raw</a> #archives%archiveentry#<br/> </div> <div>
--- a/templates/changeset.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/changeset.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -5,6 +5,7 @@ <div class="buttons"> <a href="?cl=#rev#">changelog</a> +<a href="?sl=#rev#">shortlog</a> <a href="?cmd=tags">tags</a> <a href="?mf=#manifest|short#;path=/">manifest</a> <a href="?cs=#node|short#;style=raw">raw</a>
--- a/templates/error-gitweb.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/error-gitweb.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -10,7 +10,7 @@ </div> <div class="page_nav"> -<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/> +<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/> </div> <div>
--- a/templates/fileannotate-gitweb.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/fileannotate-gitweb.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -10,7 +10,7 @@ </div> <div class="page_nav"> -<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=#path|urlescape#;style=gitweb">manifest</a> | <a href="?cmd=changeset;node=#node#;style=gitweb">changeset</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=gitweb">file</a> | <a href="?cmd=filelog;file=#file|urlescape#;filenode=#filenode#;style=gitweb">revisions</a> | annotate | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=raw">raw</a><br/> +<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=#path|urlescape#;style=gitweb">manifest</a> | <a href="?cmd=changeset;node=#node#;style=gitweb">changeset</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=gitweb">file</a> | <a href="?cmd=filelog;file=#file|urlescape#;filenode=#filenode#;style=gitweb">revisions</a> | annotate | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=raw">raw</a><br/> </div> <div class="title">#file|escape#</div>
--- a/templates/fileannotate.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/fileannotate.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -5,6 +5,7 @@ <div class="buttons"> <a href="?cl=#rev#">changelog</a> +<a href="?sl=#rev#">shortlog</a> <a href="?tags=">tags</a> <a href="?cs=#node|short#">changeset</a> <a href="?mf=#manifest|short#;path=#path|urlescape#">manifest</a>
--- a/templates/filediff.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/filediff.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -5,6 +5,7 @@ <div class="buttons"> <a href="?cl=#rev#">changelog</a> +<a href="?sl=#rev#">shortlog</a> <a href="?tags=">tags</a> <a href="?cs=#node|short#">changeset</a> <a href="?f=#filenode|short#;file=#file|urlescape#">file</a>
--- a/templates/filelog-gitweb.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/filelog-gitweb.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -10,7 +10,7 @@ </div> <div class="page_nav"> -<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=gitweb">file</a> | revisions | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=gitweb">annotate</a> | <a href="?fl=#filenode|short#;file=#file|urlescape#;style=rss">rss</a><br/> +<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=gitweb">file</a> | revisions | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=gitweb">annotate</a> | <a href="?fl=#filenode|short#;file=#file|urlescape#;style=rss">rss</a><br/> </div> <div class="title" >#file|urlescape#</div>
--- a/templates/filelog.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/filelog.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -8,6 +8,7 @@ <div class="buttons"> <a href="?cl=tip">changelog</a> +<a href="?sl=tip">shortlog</a> <a href="?tags=">tags</a> <a href="?f=#filenode|short#;file=#file|urlescape#">file</a> <a href="?fa=#filenode|short#;file=#file|urlescape#">annotate</a>
--- a/templates/filerevision-gitweb.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/filerevision-gitweb.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -10,7 +10,7 @@ </div> <div class="page_nav"> -<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?mf=#manifest|short#;path=#path|urlescape#;style=gitweb">manifest</a> | <a href="?cmd=changeset;node=#node#;style=gitweb">changeset</a> | file | <a href="?cmd=filelog;file=#file|urlescape#;filenode=#filenode#;style=gitweb">revisions</a> | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=gitweb">annotate</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=raw">raw</a><br/> +<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?mf=#manifest|short#;path=#path|urlescape#;style=gitweb">manifest</a> | <a href="?cmd=changeset;node=#node#;style=gitweb">changeset</a> | file | <a href="?cmd=filelog;file=#file|urlescape#;filenode=#filenode#;style=gitweb">revisions</a> | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=gitweb">annotate</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=raw">raw</a><br/> </div> <div class="title">#file|escape#</div>
--- a/templates/filerevision.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/filerevision.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -5,6 +5,7 @@ <div class="buttons"> <a href="?cl=#rev#">changelog</a> +<a href="?sl=#rev#">shortlog</a> <a href="?tags=">tags</a> <a href="?cs=#node|short#">changeset</a> <a href="?mf=#manifest|short#;path=#path|urlescape#">manifest</a>
--- a/templates/manifest-gitweb.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/manifest-gitweb.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -10,7 +10,7 @@ </div> <div class="page_nav"> -<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | manifest | <a href="?cs=#node|short#;style=gitweb">changeset</a> #archives%archiveentry#<br/> +<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | manifest | <a href="?cs=#node|short#;style=gitweb">changeset</a> #archives%archiveentry#<br/> </div> <div class="title" >#path|escape#</div>
--- a/templates/manifest.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/manifest.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -5,6 +5,7 @@ <div class="buttons"> <a href="?cl=#rev#">changelog</a> +<a href="?sl=#rev#">shortlog</a> <a href="?tags=">tags</a> <a href="?cs=#node|short#">changeset</a> #archives%archiveentry#
--- a/templates/map Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/map Wed Aug 09 15:03:46 2006 -0500 @@ -3,7 +3,10 @@ footer = footer.tmpl search = search.tmpl changelog = changelog.tmpl +shortlog = shortlog.tmpl +shortlogentry = shortlogentry.tmpl naventry = '<a href="?cl=#rev#">#label|escape#</a> ' +navshortentry = '<a href="?sl=#rev#">#label|escape#</a> ' filedifflink = '<a href="?fd=#node|short#;file=#file|urlescape#">#file|escape#</a> ' filenodelink = '<a href="?f=#filenode|short#;file=#file|urlescape#">#file|escape#</a> ' fileellipses = '...'
--- a/templates/search-gitweb.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/search-gitweb.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -1,6 +1,6 @@ #header# <div class="page_nav"> -<a href="?cmd=summary;style=gitweb">summary</a> | log | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/> +<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/> </div> <h2>searching for #query|escape#</h2>
--- a/templates/search.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/search.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -5,6 +5,7 @@ <div class="buttons"> <a href="?cl=tip">changelog</a> +<a href="?sl=tip">shortlog</a> <a href="?tags=">tags</a> <a href="?mf=#manifest|short#;path=/">manifest</a> </div>
--- a/templates/shortlog-gitweb.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/shortlog-gitweb.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -1,13 +1,32 @@ #header# +<title>#repo|escape#: Shortlog</title> +<link rel="alternate" type="application/rss+xml" + href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#"> +</head> +<body> +<div class="page_header"> +<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / shortlog +</div> + +<form action="#"> +<div class="search"> +<input type="hidden" name="repo" value="#repo|escape#" /> +<input type="hidden" name="style" value="gitweb" /> +<input type="hidden" name="cmd" value="changelog" /> +<input type="text" name="rev" /> +</div> +</form> +</div> <div class="page_nav"> -<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">log</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/> +<a href="?cmd=summary;style=gitweb">summary</a> | shortlog | <a href="?cmd=changelog;rev=#rev#;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>#archives%archiveentry#<br/> +<br/> -#changenav%naventry#<br/> +#changenav%navshortentry#<br/> </div> <table cellspacing="0"> -#entries# +#entries%shortlogentry# </table> #footer#
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/shortlog.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,38 @@ +#header# +<title>#repo|escape#: shortlog</title> +<link rel="alternate" type="application/rss+xml" + href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#"> +</head> +<body> + +<div class="buttons"> +<a href="?cl=#rev#">changelog</a> +<a href="?cmd=tags">tags</a> +<a href="?mf=#manifest|short#;path=/">manifest</a> +#archives%archiveentry# +<a type="application/rss+xml" href="?style=rss">rss</a> +</div> + +<h2>shortlog for #repo|escape#</h2> + +<form action="#"> +<p> +<label for="search1">search:</label> +<input type="hidden" name="cmd" value="changelog"> +<input name="rev" id="search1" type="text" size="30"> +navigate: <small class="navigate">#changenav%navshortentry#</small> +</p> +</form> + +#entries%shortlogentry# + +<form action="#"> +<p> +<label for="search2">search:</label> +<input type="hidden" name="cmd" value="changelog"> +<input name="rev" id="search2" type="text" size="30"> +navigate: <small class="navigate">#changenav%navshortentry#</small> +</p> +</form> + +#footer#
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/shortlogentry.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,7 @@ +<table class="slogEntry parity#parity#"> + <tr> + <td class="age">#date|age#</td> + <td class="author">#author|obfuscate#</td> + <td class="node"><a href="?cs=#node|short#">#desc|strip|firstline|escape#</a></td> + </tr> +</table>
--- a/templates/static/style.css Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/static/style.css Wed Aug 09 15:03:46 2006 -0500 @@ -57,6 +57,12 @@ .logEntry th.age, .logEntry th.firstline { font-weight: bold; } .logEntry th.firstline { text-align: left; width: inherit; } +/* Shortlog entries */ +.slogEntry { width: 100%; font-size: smaller; } +.slogEntry .age { width: 7%; } +.slogEntry td { font-weight: normal; text-align: left; vertical-align: top; } +.slogEntry td.author { width: 35%; } + /* Tag entries */ #tagEntries { list-style: none; margin: 0; padding: 0; } #tagEntries .tagEntry { list-style: none; margin: 0; padding: 0; }
--- a/templates/summary-gitweb.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/summary-gitweb.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -9,7 +9,8 @@ <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / summary </div> <div class="page_nav"> -summary | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/> +summary | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>#archives%archiveentry# +<br/> </div> <div class="title"> </div>
--- a/templates/tags-gitweb.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/tags-gitweb.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -10,7 +10,7 @@ </div> <div class="page_nav"> -<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | tags | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a> +<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | tags | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a> <br/> </div>
--- a/templates/tags.tmpl Wed Aug 09 14:53:03 2006 -0500 +++ b/templates/tags.tmpl Wed Aug 09 15:03:46 2006 -0500 @@ -7,6 +7,7 @@ <div class="buttons"> <a href="?cl=tip">changelog</a> +<a href="?sl=tip">shortlog</a> <a href="?mf=#manifest|short#;path=/">manifest</a> <a type="application/rss+xml" href="?cmd=tags;style=rss">rss</a> </div>
--- a/tests/run-tests.py Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/run-tests.py Wed Aug 09 15:03:46 2006 -0500 @@ -19,13 +19,13 @@ import tempfile import time -required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"] +required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed", "merge"] parser = optparse.OptionParser("%prog [options] [tests]") parser.add_option("-v", "--verbose", action="store_true", help="output verbose messages") parser.add_option("-t", "--timeout", type="int", - help="output verbose messages") + help="kill errant tests after TIMEOUT seconds") parser.add_option("-c", "--cover", action="store_true", help="print a test coverage report") parser.add_option("-s", "--cover_stdlib", action="store_true", @@ -201,6 +201,11 @@ return ret, splitnewlines(output) def run_one(test): + '''tristate output: + None -> skipped + True -> passed + False -> failed''' + vlog("# Test", test) if not verbose: sys.stdout.write('.') @@ -217,15 +222,28 @@ os.mkdir(tmpd) os.chdir(tmpd) - if test.endswith(".py"): - cmd = '%s "%s"' % (sys.executable, os.path.join(TESTDIR, test)) - else: - cmd = '"%s"' % (os.path.join(TESTDIR, test)) + lctest = test.lower() - # To reliably get the error code from batch files on WinXP, - # the "cmd /c call" prefix is needed. Grrr - if os.name == 'nt' and test.endswith(".bat"): + if lctest.endswith('.py'): + cmd = '%s "%s"' % (sys.executable, os.path.join(TESTDIR, test)) + elif lctest.endswith('.bat'): + # do not run batch scripts on non-windows + if os.name != 'nt': + print '\nSkipping %s: batch script' % test + return None + # To reliably get the error code from batch files on WinXP, + # the "cmd /c call" prefix is needed. Grrr cmd = 'cmd /c call "%s"' % (os.path.join(TESTDIR, test)) + else: + # do not run shell scripts on windows + if os.name == 'nt': + print '\nSkipping %s: shell script' % test + return None + # do not try to run non-executable programs + if not os.access(os.path.join(TESTDIR, test), os.X_OK): + print '\nSkipping %s: not executable' % test + return None + cmd = '"%s"' % (os.path.join(TESTDIR, test)) if options.timeout > 0: signal.alarm(options.timeout) @@ -244,7 +262,7 @@ ref_out = splitnewlines(f.read()) f.close() else: - ref_out = [''] + ref_out = [] if out != ref_out: diffret = 1 print "\nERROR: %s output changed" % (test) @@ -330,16 +348,23 @@ tests = 0 failed = 0 + skipped = 0 if len(args) == 0: args = os.listdir(".") for test in args: - if test.startswith("test-") and not '~' in test and not '.' in test: - if not run_one(test): + if (test.startswith("test-") and '~' not in test and + ('.' not in test or test.endswith('.py') or + test.endswith('.bat'))): + ret = run_one(test) + if ret is None: + skipped += 1 + elif not ret: failed += 1 tests += 1 - print "\n# Ran %d tests, %d failed." % (tests, failed) + print "\n# Ran %d tests, %d skipped, %d failed." % (tests, skipped, + failed) if coverage: output_coverage() except KeyboardInterrupt:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-abort-checkin Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,17 @@ +#!/bin/sh + +HGRCPATH=$HGTMP/.hgrc; export HGRCPATH +echo "[extensions]" >> $HGTMP/.hgrc +echo "mq=" >> $HGTMP/.hgrc + +hg init foo +cd foo +echo foo > foo +hg add foo + +# mq may keep a reference to the repository so __del__ will not be called +# and .hg/journal.dirstate will not be deleted: +HGEDITOR=false hg ci +HGEDITOR=false hg ci + +exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-abort-checkin.out Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,6 @@ +abort: edit failed: false exited with status 1 +transaction abort! +rollback completed +abort: edit failed: false exited with status 1 +transaction abort! +rollback completed
--- a/tests/test-backout Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-backout Wed Aug 09 15:03:46 2006 -0500 @@ -60,4 +60,40 @@ hg backout -d '3 0' 1 hg locate b +cd .. +hg init m +cd m +echo a > a +hg commit -d '0 0' -A -m a +echo b > b +hg commit -d '1 0' -A -m b +echo c > c +hg commit -d '2 0' -A -m b +hg update 1 +echo d > d +hg commit -d '3 0' -A -m c +hg merge 2 +hg commit -d '4 0' -A -m d + +echo '# backout of merge should fail' + +hg backout 4 + +echo '# backout of merge with bad parent should fail' + +hg backout --parent 0 4 + +echo '# backout of non-merge with parent should fail' + +hg backout --parent 0 3 + +echo '# backout with valid parent should be ok' + +hg backout -d '5 0' --parent 2 4 + +hg rollback +hg update -C + +hg backout -d '6 0' --parent 3 4 + exit 0
--- a/tests/test-backout.out Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-backout.out Wed Aug 09 15:03:46 2006 -0500 @@ -27,5 +27,25 @@ reverting a changeset 3:4cbb1e70196a backs out changeset 1:22bca4c721e5 the backout changeset is a new head - do not forget to merge -(use "backout -m" if you want to auto-merge) +(use "backout --merge" if you want to auto-merge) b: No such file or directory +adding a +adding b +adding c +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +adding d +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +# backout of merge should fail +abort: cannot back out a merge changeset without --parent +# backout of merge with bad parent should fail +abort: cb9a9f314b8b is not a parent of b2f3bb92043e +# backout of non-merge with parent should fail +abort: cannot use --parent on non-merge changeset +# backout with valid parent should be ok +removing d +changeset 5:11fbd9be634c backs out changeset 4:b2f3bb92043e +rolling back last transaction +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +removing c +changeset 5:1a5f1a63bf2c backs out changeset 4:b2f3bb92043e
--- a/tests/test-bundle Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-bundle Wed Aug 09 15:03:46 2006 -0500 @@ -30,14 +30,20 @@ hg init empty hg -R test bundle full.hg empty hg -R test unbundle full.hg -hg -R empty unbundle full.hg hg -R empty heads hg -R empty verify +hg --cwd test pull ../full.hg +hg --cwd empty pull ../full.hg +hg -R empty rollback +hg --cwd empty pull ../full.hg + rm -rf empty hg init empty cd empty hg -R bundle://../full.hg log +echo '[hooks]' >> .hg/hgrc +echo 'changegroup = echo changegroup: u=$HG_URL' >> .hg/hgrc #doesn't work (yet ?) #hg -R bundle://../full.hg verify hg pull bundle://../full.hg
--- a/tests/test-bundle.out Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-bundle.out Wed Aug 09 15:03:46 2006 -0500 @@ -11,28 +11,34 @@ adding file changes added 0 changesets with 0 changes to 4 files (run 'hg update' to get a working copy) +changeset: -1:000000000000 +tag: tip +user: +date: Thu Jan 01 00:00:00 1970 +0000 + +checking changesets +checking manifests +crosschecking files in changesets and manifests +checking files +0 files, 0 changesets, 0 total revisions +pulling from ../full.hg +searching for changes +no changes found +pulling from ../full.hg +requesting all changes adding changesets adding manifests adding file changes added 9 changesets with 7 changes to 4 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge) -changeset: 8:836ac62537ab -tag: tip -parent: 3:ac69c658229d -user: test -date: Mon Jan 12 13:46:40 1970 +0000 -summary: 0.3m - -changeset: 7:80fe151401c2 -user: test -date: Mon Jan 12 13:46:40 1970 +0000 -summary: 1.3m - -checking changesets -checking manifests -crosschecking files in changesets and manifests -checking files -4 files, 9 changesets, 7 total revisions +rolling back last transaction +pulling from ../full.hg +requesting all changes +adding changesets +adding manifests +adding file changes +added 9 changesets with 7 changes to 4 files (+1 heads) +(run 'hg heads' to see heads, 'hg merge' to merge) changeset: 8:836ac62537ab tag: tip parent: 3:ac69c658229d @@ -81,6 +87,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: 0.0 +changegroup: u=bundle:../full.hg pulling from bundle://../full.hg requesting all changes adding changesets
--- a/tests/test-diff-ignore-whitespace Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-diff-ignore-whitespace Wed Aug 09 15:03:46 2006 -0500 @@ -3,7 +3,8 @@ # GNU diff is the reference for all of these results. hgdiff() { - hg diff "$@" | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/" + hg diff "$@" | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \ + -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" } test_added_blank_lines() {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-fetch Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,25 @@ +#!/bin/sh + +HGRCPATH=$HGTMP/.hgrc; export HGRCPATH +echo "[extensions]" >> $HGTMP/.hgrc +echo "fetch=" >> $HGTMP/.hgrc + +hg init a +echo a > a/a +hg --cwd a commit -d '1 0' -Ama + +hg clone a b +hg clone a c + +echo b > a/b +hg --cwd a commit -d '2 0' -Amb +hg --cwd a parents -q + +echo % should pull one change +hg --cwd b fetch ../a +hg --cwd b parents -q + +echo c > c/c +hg --cwd c commit -d '3 0' -Amc +hg --cwd c fetch -d '4 0' -m 'automated merge' ../a +ls c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-fetch.out Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,27 @@ +adding a +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +adding b +1:97d72e5f12c7 +% should pull one change +pulling from ../a +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +1:97d72e5f12c7 +adding c +pulling from ../a +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +merging with new head 2:97d72e5f12c7 +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +new changeset 3:cd3a41621cf0 merges remote changes with local +a +b +c
--- a/tests/test-help.out Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-help.out Wed Aug 09 15:03:46 2006 -0500 @@ -185,28 +185,34 @@ show changed files in the working directory - Show changed files in the repository. If names are - given, only files that match are shown. + Show status of files in the repository. If names are given, only + files that match are shown. Files that are clean or ignored, are + not listed unless -c (clean), -i (ignored) or -A is given. The codes used to show the status of files are: M = modified A = added R = removed + C = clean ! = deleted, but still tracked ? = not tracked I = ignored (not shown by default) + = the previous added file was copied from here aliases: st 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 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 -I --include include names matching the given patterns -X --exclude exclude names matching the given patterns
--- a/tests/test-hook Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-hook Wed Aug 09 15:03:46 2006 -0500 @@ -17,9 +17,9 @@ # changegroup hooks can see env vars echo '[hooks]' > .hg/hgrc -echo 'prechangegroup = echo prechangegroup hook' >> .hg/hgrc -echo 'changegroup = echo changegroup hook: n=$HG_NODE' >> .hg/hgrc -echo 'incoming = echo incoming hook: n=$HG_NODE' >> .hg/hgrc +echo 'prechangegroup = echo prechangegroup hook: u=`echo $HG_URL | sed s,file:.*,file:,`' >> .hg/hgrc +echo 'changegroup = echo changegroup hook: n=$HG_NODE u=`echo $HG_URL | sed s,file:.*,file:,`' >> .hg/hgrc +echo 'incoming = echo incoming hook: n=$HG_NODE u=`echo $HG_URL | sed s,file:.*,file:,`' >> .hg/hgrc # pretxncommit and commit hooks can see both parents of merge cd ../a
--- a/tests/test-hook.out Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-hook.out Wed Aug 09 15:03:46 2006 -0500 @@ -22,11 +22,11 @@ 3:4c52fb2e4022 commit hook: n=4c52fb2e402287dd5dc052090682536c8406c321 p1=1324a5531bac09b329c3845d35ae6a7526874edb p2=b702efe9688826e3a91283852b328b84dbf37bc2 commit hook b -prechangegroup hook -changegroup hook: n=b702efe9688826e3a91283852b328b84dbf37bc2 -incoming hook: n=b702efe9688826e3a91283852b328b84dbf37bc2 -incoming hook: n=1324a5531bac09b329c3845d35ae6a7526874edb -incoming hook: n=4c52fb2e402287dd5dc052090682536c8406c321 +prechangegroup hook: u=file: +changegroup hook: n=b702efe9688826e3a91283852b328b84dbf37bc2 u=file: +incoming hook: n=b702efe9688826e3a91283852b328b84dbf37bc2 u=file: +incoming hook: n=1324a5531bac09b329c3845d35ae6a7526874edb u=file: +incoming hook: n=4c52fb2e402287dd5dc052090682536c8406c321 u=file: pulling from ../a searching for changes adding changesets
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-http Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,34 @@ +#!/bin/sh + +hg init test +cd test +echo foo>foo +hg commit -A -d '0 0' -m 1 +hg --config server.uncompressed=True serve -p 20059 -d --pid-file=../hg1.pid +hg serve -p 20060 -d --pid-file=../hg2.pid +cd .. +cat hg1.pid hg2.pid >> $DAEMON_PIDS + +echo % clone via stream +http_proxy= hg clone --uncompressed http://localhost:20059/ copy 2>&1 | \ + sed -e 's/[0-9][0-9.]*/XXX/g' +hg verify -R copy + +echo % try to clone via stream, should use pull instead +http_proxy= hg clone --uncompressed http://localhost:20060/ copy2 + +echo % clone via pull +http_proxy= hg clone http://localhost:20059/ copy-pull +hg verify -R copy-pull + +cd test +echo bar > bar +hg commit -A -d '1 0' -m 2 +cd .. + +echo % pull +cd copy-pull +echo '[hooks]' >> .hg/hgrc +echo 'changegroup = echo changegroup: u=$HG_URL' >> .hg/hgrc +hg pull +cd ..
--- a/tests/test-http-proxy Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-http-proxy Wed Aug 09 15:03:46 2006 -0500 @@ -4,7 +4,7 @@ cd a echo a > a hg ci -Ama -d '1123456789 0' -hg serve -p 20059 -d --pid-file=hg.pid +hg --config server.uncompressed=True serve -p 20059 -d --pid-file=hg.pid cat hg.pid >> $DAEMON_PIDS cd .. @@ -13,8 +13,18 @@ cat proxy.pid >> $DAEMON_PIDS sleep 2 -echo %% url for proxy -http_proxy=http://localhost:20060/ hg --config http_proxy.always=True clone http://localhost:20059/ b +echo %% url for proxy, stream +http_proxy=http://localhost:20060/ hg --config http_proxy.always=True clone --uncompressed http://localhost:20059/ b | \ + sed -e 's/[0-9][0-9.]*/XXX/g' +cd b +hg verify +cd .. + +echo %% url for proxy, pull +http_proxy=http://localhost:20060/ hg --config http_proxy.always=True clone http://localhost:20059/ b-pull +cd b-pull +hg verify +cd .. echo %% host:port for proxy http_proxy=localhost:20060 hg clone --config http_proxy.always=True http://localhost:20059/ c
--- a/tests/test-http-proxy.out Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-http-proxy.out Wed Aug 09 15:03:46 2006 -0500 @@ -1,11 +1,26 @@ adding a -%% url for proxy +%% url for proxy, stream +streaming all changes +XXX files to transfer, XXX bytes of data +transferred XXX bytes in XXX seconds (XXX KB/sec) +XXX files updated, XXX files merged, XXX files removed, XXX files unresolved +checking changesets +checking manifests +crosschecking files in changesets and manifests +checking files +1 files, 1 changesets, 1 total revisions +%% url for proxy, pull requesting all changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +checking changesets +checking manifests +crosschecking files in changesets and manifests +checking files +1 files, 1 changesets, 1 total revisions %% host:port for proxy requesting all changes adding changesets
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-http.out Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,40 @@ +adding foo +% clone via stream +streaming all changes +XXX files to transfer, XXX bytes of data +transferred XXX bytes in XXX seconds (XXX KB/sec) +XXX files updated, XXX files merged, XXX files removed, XXX files unresolved +checking changesets +checking manifests +crosschecking files in changesets and manifests +checking files +1 files, 1 changesets, 1 total revisions +% try to clone via stream, should use pull instead +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% clone via pull +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +checking changesets +checking manifests +crosschecking files in changesets and manifests +checking files +1 files, 1 changesets, 1 total revisions +adding bar +% pull +changegroup: u=http://localhost:20059/ +pulling from http://localhost:20059/ +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +(run 'hg update' to get a working copy)
--- a/tests/test-import Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-import Wed Aug 09 15:03:46 2006 -0500 @@ -1,7 +1,10 @@ #!/bin/sh hg init a +mkdir a/d1 +mkdir a/d1/d2 echo line 1 > a/a +echo line 1 > a/d1/d2/a hg --cwd a ci -d '0 0' -Ama echo line 2 >> a/a @@ -79,3 +82,19 @@ hg --cwd b tip | grep second rm -rf b +# bug non regression test +# importing a patch in a subdirectory failed at the commit stage +echo line 2 >> a/d1/d2/a +hg --cwd a ci -u someoneelse -d '1 0' -m'subdir change' +echo % hg import in a subdirectory +hg clone -r0 a b +hg --cwd a export tip | sed -e 's/d1\/d2\///' > tip.patch +pushd b/d1/d2 2>&1 > /dev/null +hg import ../../../tip.patch +popd 2>&1 > /dev/null +echo "% message should be 'subdir change'" +hg --cwd b tip | grep 'subdir change' +echo "% committer should be 'someoneelse'" +hg --cwd b tip | grep someoneelse +echo "% should be empty" +hg --cwd b status
--- a/tests/test-import.out Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-import.out Wed Aug 09 15:03:46 2006 -0500 @@ -1,11 +1,12 @@ adding a +adding d1/d2/a % import exported patch requesting all changes adding changesets adding manifests adding file changes -added 1 changesets with 1 changes to 1 files -1 files updated, 0 files merged, 0 files removed, 0 files unresolved +added 1 changesets with 2 changes to 2 files +2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying ../tip.patch patching file a % message should be same @@ -17,8 +18,8 @@ adding changesets adding manifests adding file changes -added 1 changesets with 1 changes to 1 files -1 files updated, 0 files merged, 0 files removed, 0 files unresolved +added 1 changesets with 2 changes to 2 files +2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying ../tip.patch patching file a transaction abort! @@ -28,8 +29,8 @@ adding changesets adding manifests adding file changes -added 1 changesets with 1 changes to 1 files -1 files updated, 0 files merged, 0 files removed, 0 files unresolved +added 1 changesets with 2 changes to 2 files +2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying ../tip.patch patching file a % import from stdin @@ -37,8 +38,8 @@ adding changesets adding manifests adding file changes -added 1 changesets with 1 changes to 1 files -1 files updated, 0 files merged, 0 files removed, 0 files unresolved +added 1 changesets with 2 changes to 2 files +2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying patch from stdin patching file a % override commit message @@ -46,8 +47,8 @@ adding changesets adding manifests adding file changes -added 1 changesets with 1 changes to 1 files -1 files updated, 0 files merged, 0 files removed, 0 files unresolved +added 1 changesets with 2 changes to 2 files +2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying patch from stdin patching file a summary: override @@ -56,8 +57,8 @@ adding changesets adding manifests adding file changes -added 1 changesets with 1 changes to 1 files -1 files updated, 0 files merged, 0 files removed, 0 files unresolved +added 1 changesets with 2 changes to 2 files +2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying ../msg.patch patching file a user: email patcher @@ -67,8 +68,8 @@ adding changesets adding manifests adding file changes -added 1 changesets with 1 changes to 1 files -1 files updated, 0 files merged, 0 files removed, 0 files unresolved +added 1 changesets with 2 changes to 2 files +2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying patch from stdin patching file a % plain diff in email, subject, no message body @@ -76,8 +77,8 @@ adding changesets adding manifests adding file changes -added 1 changesets with 1 changes to 1 files -1 files updated, 0 files merged, 0 files removed, 0 files unresolved +added 1 changesets with 2 changes to 2 files +2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying patch from stdin patching file a % plain diff in email, no subject, no message body, should fail @@ -85,8 +86,8 @@ adding changesets adding manifests adding file changes -added 1 changesets with 1 changes to 1 files -1 files updated, 0 files merged, 0 files removed, 0 files unresolved +added 1 changesets with 2 changes to 2 files +2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying patch from stdin patching file a transaction abort! @@ -96,8 +97,22 @@ adding changesets adding manifests adding file changes -added 1 changesets with 1 changes to 1 files -1 files updated, 0 files merged, 0 files removed, 0 files unresolved +added 1 changesets with 2 changes to 2 files +2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying patch from stdin patching file a summary: second change +% hg import in a subdirectory +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 2 changes to 2 files +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +applying ../../../tip.patch +patching file a +% message should be 'subdir change' +summary: subdir change +% committer should be 'someoneelse' +user: someoneelse +% should be empty
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-log Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,65 @@ +#!/bin/sh + +hg init a + +cd a +echo a > a +hg ci -Ama -d '1 0' + +hg cp a b +hg ci -mb -d '2 0' + +mkdir dir +hg mv b dir +hg ci -mc -d '3 0' + +hg mv a b +hg ci -md -d '4 0' + +hg mv dir/b e +hg ci -me -d '5 0' + +hg log a +echo % -f, directory +hg log -f dir +echo % -f, but no args +hg log -f +echo % one rename +hg log -vf a +echo % many renames +hg log -vf e + +# log --follow tests +hg init ../follow +cd ../follow +echo base > base +hg ci -Ambase -d '1 0' + +echo r1 >> base +hg ci -Amr1 -d '1 0' +echo r2 >> base +hg ci -Amr2 -d '1 0' + +hg up -C 1 +echo b1 > b1 +hg ci -Amb1 -d '1 0' + +echo % log -f +hg log -f + +hg up -C 0 +echo b2 > b2 +hg ci -Amb2 -d '1 0' + +echo % log -f -r 1:tip +hg log -f -r 1:tip + +hg up -C 3 +hg merge tip +hg ci -mm12 -d '1 0' + +echo postm >> b1 +hg ci -Amb1.1 -d'1 0' + +echo % log --follow-first +hg log --follow-first
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-log.out Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,151 @@ +adding a +changeset: 0:8580ff50825a +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: a + +% -f, directory +abort: can only follow copies/renames for explicit file names +% -f, but no args +changeset: 4:8c1c8408f737 +tag: tip +user: test +date: Thu Jan 01 00:00:05 1970 +0000 +summary: e + +changeset: 3:c4ba038c90ce +user: test +date: Thu Jan 01 00:00:04 1970 +0000 +summary: d + +changeset: 2:21fba396af4c +user: test +date: Thu Jan 01 00:00:03 1970 +0000 +summary: c + +changeset: 1:c0296dabce9b +user: test +date: Thu Jan 01 00:00:02 1970 +0000 +summary: b + +changeset: 0:8580ff50825a +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: a + +% one rename +changeset: 0:8580ff50825a50c8f716709acdf8de0deddcd6ab +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +files: a +description: +a + + +% many renames +changeset: 4:8c1c8408f7371319750ea2d4fa7969828effbcf4 +tag: tip +user: test +date: Thu Jan 01 00:00:05 1970 +0000 +files: dir/b e +description: +e + + +changeset: 2:21fba396af4c801f9717de6c415b6cc9620437e8 +user: test +date: Thu Jan 01 00:00:03 1970 +0000 +files: b dir/b +description: +c + + +changeset: 1:c0296dabce9bf0cd3fdd608de26693c91cd6bbf4 +user: test +date: Thu Jan 01 00:00:02 1970 +0000 +files: b +description: +b + + +changeset: 0:8580ff50825a50c8f716709acdf8de0deddcd6ab +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +files: a +description: +a + + +adding base +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +adding b1 +% log -f +changeset: 3:e62f78d544b4 +tag: tip +parent: 1:3d5bf5654eda +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: b1 + +changeset: 1:3d5bf5654eda +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: r1 + +changeset: 0:67e992f2c4f3 +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: base + +1 files updated, 0 files merged, 1 files removed, 0 files unresolved +adding b2 +% log -f -r 1:tip +changeset: 1:3d5bf5654eda +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: r1 + +changeset: 2:60c670bf5b30 +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: r2 + +changeset: 3:e62f78d544b4 +parent: 1:3d5bf5654eda +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: b1 + +2 files updated, 0 files merged, 1 files removed, 0 files unresolved +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +% log --follow-first +changeset: 6:2404bbcab562 +tag: tip +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: b1.1 + +changeset: 5:302e9dd6890d +parent: 3:e62f78d544b4 +parent: 4:ddb82e70d1a1 +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: m12 + +changeset: 3:e62f78d544b4 +parent: 1:3d5bf5654eda +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: b1 + +changeset: 1:3d5bf5654eda +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: r1 + +changeset: 0:67e992f2c4f3 +user: test +date: Thu Jan 01 00:00:01 1970 +0000 +summary: base +
--- a/tests/test-merge5.out Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-merge5.out Wed Aug 09 15:03:46 2006 -0500 @@ -1,6 +1,3 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved removing b -this update spans a branch affecting the following files: - b -aborting update spanning branches! -(use 'hg merge' to merge across branches or 'hg update -C' to lose changes) +abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes
--- a/tests/test-merge7.out Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-merge7.out Wed Aug 09 15:03:46 2006 -0500 @@ -22,7 +22,7 @@ (run 'hg heads' to see heads, 'hg merge' to merge) merge: warning: conflicts during merge resolving manifests - force False allow True moddirstate True linear False + overwrite None branchmerge True partial False linear False ancestor 055d847dd401 local 2eded9ab0a5c remote 84cf5750dd20 test.txt versions differ, resolve merging test.txt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,114 @@ +#!/bin/sh + +HGRCPATH=$HGTMP/.hgrc; export HGRCPATH +echo "[extensions]" >> $HGTMP/.hgrc +echo "mq=" >> $HGTMP/.hgrc + +echo % help +hg help mq + +hg init a +cd a +echo a > a +mkdir b +echo z > b/z +hg ci -Ama + +echo % qinit + +hg qinit + +cd .. +hg init b + +echo % -R qinit + +hg -R b qinit + +hg init c + +echo % qinit -c + +hg --cwd c qinit -c +hg -R c/.hg/patches st + +echo % qnew implies add + +hg -R c qnew test.patch +hg -R c/.hg/patches st + +cd a + +echo % qnew -m + +hg qnew -m 'foo bar' test.patch +cat .hg/patches/test.patch + +echo % qrefresh + +echo a >> a +hg qrefresh +sed -e "s/\(^diff -r \)\([a-f0-9]* \)/\1 x/" \ + -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \ + -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/test.patch + +echo % qpop + +hg qpop + +echo % qpush + +hg qpush + +cd .. + +echo % pop/push outside repo + +hg -R a qpop +hg -R a qpush + +cd a +hg qnew test2.patch + +echo % qrefresh in subdir + +cd b +echo a > a +hg add a +hg qrefresh + +echo % pop/push -a in subdir + +hg qpop -a +hg --traceback qpush -a + +echo % qseries +hg qseries + +echo % qapplied +hg qapplied + +echo % qtop +hg qtop + +echo % qprev +hg qprev + +echo % qnext +hg qnext + +echo % pop, qnext, qprev, qapplied +hg qpop +hg qnext +hg qprev +hg qapplied + +echo % qunapplied +hg qunapplied + +echo % strip +cd ../../b +echo x>x +hg ci -Ama +hg strip tip 2>&1 | sed 's/\(saving bundle to \).*/\1/' +hg unbundle .hg/strip-backup/*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-guards Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,84 @@ +#!/bin/sh + +HGRCPATH=$HGTMP/.hgrc; export HGRCPATH +echo "[extensions]" >> $HGTMP/.hgrc +echo "mq=" >> $HGTMP/.hgrc + +hg init +hg qinit + +echo x > x +hg ci -Ama + +hg qnew a.patch +echo a > a +hg add a +hg qrefresh + +hg qnew b.patch +echo b > b +hg add b +hg qrefresh + +hg qnew c.patch +echo c > c +hg add c +hg qrefresh + +hg qpop -a + +echo % should fail +hg qguard +fail + +hg qpush +echo % should guard a.patch +hg qguard +a +echo % should print +a +hg qguard +hg qpop + +hg qguard a.patch +echo % should push b.patch +hg qpush + +hg qpop +hg qselect a +echo % should push a.patch +hg qpush + +hg qguard c.patch -a +echo % should print -a +hg qguard c.patch + +echo % should skip c.patch +hg qpush -a + +hg qguard -n c.patch +echo % should push c.patch +hg qpush -a + +hg qpop -a +hg qselect -n +echo % should push all +hg qpush -a + +hg qpop -a +hg qguard a.patch +1 +2 +hg qselect 1 +echo % should push b.patch +hg qpush +hg qpop -a + +hg qselect 2 +hg qpush +hg qpop -a + +hg qselect 1 2 +echo % should push a.patch +hg qpush +hg qpop -a + +hg qguard a.patch +1 +2 -3 +hg qselect 1 2 3 +echo % should push b.patch +hg qpush
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-guards.out Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,54 @@ +adding x +Patch queue now empty +% should fail +abort: no patches applied +applying a.patch +Now at: a.patch +% should guard a.patch +% should print +a +a.patch: +a +Patch queue now empty +a.patch: +a +% should push b.patch +applying b.patch +Now at: b.patch +Patch queue now empty +3 of 3 unapplied patches active +% should push a.patch +applying a.patch +Now at: a.patch +% should print -a +c.patch: -a +% should skip c.patch +applying b.patch +skipping c.patch - guarded by '- a' +Now at: b.patch +% should push c.patch +applying c.patch +Now at: c.patch +Patch queue now empty +guards deactivated +2 of 3 unapplied patches active +% should push all +applying b.patch +applying c.patch +Now at: c.patch +Patch queue now empty +2 of 3 unapplied patches active +% should push b.patch +applying b.patch +Now at: b.patch +Patch queue now empty +2 of 3 unapplied patches active +applying b.patch +Now at: b.patch +Patch queue now empty +3 of 3 unapplied patches active +% should push a.patch +applying a.patch +Now at: a.patch +Patch queue now empty +2 of 3 unapplied patches active +% should push b.patch +applying b.patch +Now at: b.patch
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-qnew-twice Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,15 @@ +#!/bin/sh + +HGRCPATH=$HGTMP/.hgrc; export HGRCPATH +echo "[extensions]" >> $HGTMP/.hgrc +echo "mq=" >> $HGTMP/.hgrc + +hg init a +cd a +hg qnew first.patch +hg qnew first.patch + +touch ../first.patch +hg qimport ../first.patch + +exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-qnew-twice.out Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,2 @@ +abort: patch "first.patch" already exists +abort: patch "first.patch" already exists
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-qrefresh-replace-log-message Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,51 @@ +#!/bin/sh + +# Environement setup for MQ +HGRCPATH=$HGTMP/.hgrc; export HGRCPATH +echo "[extensions]" >> $HGTMP/.hgrc +echo "mq=" >> $HGTMP/.hgrc + +#Repo init +hg init +hg qinit + +hg qnew -m "First commit message" first-patch +echo aaaa > file +hg add file +hg qrefresh +echo ======================= +echo "Should display 'First commit message'" +hg log -l1 -v | sed -n '/description/,$p' +echo + +# Testing changing message with -m +echo bbbb > file +hg qrefresh -m "Second commit message" +echo ======================= +echo "Should display 'Second commit message'" +hg log -l1 -v | sed -n '/description/,$p' +echo + + +# Testing changing message with -l +echo "Third commit message" > logfile +echo " This is the 3rd log message" >> logfile +echo bbbb > file +hg qrefresh -l logfile +echo ======================= +echo "Should display 'Third commit message\n This is the 3rd log message'" +hg log -l1 -v | sed -n '/description/,$p' +echo + +# Testing changing message with -l- +hg qnew -m "First commit message" second-patch +echo aaaa > file2 +hg add file2 +echo bbbb > file2 +(echo "Fifth commit message" +echo " This is the 5th log message" >> logfile) |\ +hg qrefresh -l- +echo ======================= +echo "Should display 'Fifth commit message\n This is the 5th log message'" +hg log -l1 -v | sed -n '/description/,$p' +echo
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-qrefresh-replace-log-message.out Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,29 @@ +======================= +Should display 'First commit message' +description: +First commit message + + + +======================= +Should display 'Second commit message' +description: +Second commit message + + + +======================= +Should display 'Third commit message\n This is the 3rd log message' +description: +Third commit message + This is the 3rd log message + + + +======================= +Should display 'Fifth commit message\n This is the 5th log message' +description: +Fifth commit message + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-qsave Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,16 @@ +#!/bin/sh + +HGRCPATH=$HGTMP/.hgrc; export HGRCPATH +echo "[extensions]" >> $HGTMP/.hgrc +echo "mq=" >> $HGTMP/.hgrc + +hg init a +cd a + +echo 'base' > base +hg ci -Ambase -d '1 0' + +hg qnew -mmqbase mqbase + +hg qsave +hg qrestore 2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-qsave.out Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,2 @@ +adding base +restoring status: hg patches saved state
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq.out Wed Aug 09 15:03:46 2006 -0500 @@ -0,0 +1,115 @@ +% help +mq extension - patch management and development + +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): + +prepare repository to work with patches qinit +create new patch qnew +import existing patch qimport + +print patch series qseries +print applied patches qapplied +print name of top applied patch qtop + +add known patch to applied stack qpush +remove patch from applied stack qpop +refresh contents of top applied patch qrefresh + +list of commands (use "hg help -v mq" to show aliases and global options): + + qapplied print the patches already applied + qclone clone main and patch repository at same time + qcommit commit changes in the queue repository + qdelete remove a patch from the series file + qdiff diff of the current patch + qfold fold the named patches into the current patch + qguard set or print guards for a patch + qheader Print the header of the topmost or specified patch + qimport import a patch + qinit init a new queue repository + qnew create a new patch + qnext print the name of the next patch + qpop pop the current patch off the stack + qprev print the name of the previous patch + qpush push the next patch onto the stack + qrefresh update the current patch + qrename rename a patch + qrestore restore the queue state saved by a rev + qsave save current queue state + qselect set or print guarded patches to push + qseries print the entire series file + qtop print the name of the current patch + qunapplied print the patches not yet applied + strip strip a revision and all later revs on the same branch +adding a +adding b/z +% qinit +% -R qinit +% qinit -c +A .hgignore +A series +% qnew implies add +A .hgignore +A series +A test.patch +% qnew -m +foo bar +% qrefresh +foo bar + +diff -r xa +--- a/a ++++ b/a +@@ -1,1 +1,2 @@ a + a ++a +% qpop +Patch queue now empty +% qpush +applying test.patch +Now at: test.patch +% pop/push outside repo +Patch queue now empty +applying test.patch +Now at: test.patch +% qrefresh in subdir +% pop/push -a in subdir +Patch queue now empty +applying test.patch +applying test2.patch +Now at: test2.patch +% qseries +test.patch +test2.patch +% qapplied +test.patch +test2.patch +% qtop +test2.patch +% qprev +test.patch +% qnext +All patches applied +% pop, qnext, qprev, qapplied +Now at: test.patch +test2.patch +Only one patch applied +test.patch +% qunapplied +test2.patch +% strip +adding x +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +saving bundle to +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +(run 'hg update' to get a working copy)
--- a/tests/test-pull Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-pull Wed Aug 09 15:03:46 2006 -0500 @@ -11,7 +11,7 @@ cat hg.pid >> $DAEMON_PIDS cd .. -http_proxy= hg clone http://localhost:20059/ copy +http_proxy= hg clone --pull http://localhost:20059/ copy cd copy hg verify hg co
--- a/tests/test-push-http Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-push-http Wed Aug 09 15:03:46 2006 -0500 @@ -36,13 +36,19 @@ echo % expect success echo 'allow_push = *' >> .hg/hgrc +echo '[hooks]' >> .hg/hgrc +echo 'changegroup = echo changegroup: u=$HG_URL >> $HGTMP/urls' >> .hg/hgrc hg serve -p 20059 -d --pid-file=hg.pid cat hg.pid >> $DAEMON_PIDS hg --cwd ../test2 push http://localhost:20059/ kill `cat hg.pid` hg rollback +sed 's/\(remote:http.*\):.*/\1/' $HGTMP/urls + echo % expect authorization error: all users denied +echo '[web]' > .hg/hgrc +echo 'push_ssl = false' >> .hg/hgrc echo 'deny_push = *' >> .hg/hgrc hg serve -p 20059 -d --pid-file=hg.pid cat hg.pid >> $DAEMON_PIDS
--- a/tests/test-push-http.out Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-push-http.out Wed Aug 09 15:03:46 2006 -0500 @@ -20,6 +20,7 @@ adding file changes added 1 changesets with 1 changes to 1 files rolling back last transaction +changegroup: u=remote:http % expect authorization error: all users denied pushing to http://localhost:20059/ searching for changes
--- a/tests/test-ssh Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-ssh Wed Aug 09 15:03:46 2006 -0500 @@ -17,6 +17,8 @@ exit -1 fi +SSH_CLIENT='127.0.0.1 1 2' +export SSH_CLIENT echo Got arguments 1:$1 2:$2 3:$3 4:$4 5:$5 >> dummylog $2 EOF @@ -27,16 +29,30 @@ cd remote echo this > foo hg ci -A -m "init" -d "1000000 0" foo +echo '[server]' > .hg/hgrc +echo 'uncompressed = True' >> .hg/hgrc +echo '[hooks]' >> .hg/hgrc +echo 'changegroup = echo changegroup in remote: u=$HG_URL >> ../dummylog' >> .hg/hgrc cd .. -echo "# clone remote" +echo "# clone remote via stream" +hg clone -e ./dummyssh --uncompressed ssh://user@dummy/remote local-stream 2>&1 | \ + sed -e 's/[0-9][0-9.]*/XXX/g' +cd local-stream +hg verify +cd .. + +echo "# clone remote via pull" hg clone -e ./dummyssh ssh://user@dummy/remote local echo "# verify" cd local hg verify +echo '[hooks]' >> .hg/hgrc +echo 'changegroup = echo changegroup in local: u=$HG_URL >> ../dummylog' >> .hg/hgrc + echo "# empty default pull" hg paths hg pull -e ../dummyssh
--- a/tests/test-ssh.out Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-ssh.out Wed Aug 09 15:03:46 2006 -0500 @@ -1,5 +1,15 @@ # creating 'remote' -# clone remote +# clone remote via stream +streaming all changes +XXX files to transfer, XXX bytes of data +transferred XXX bytes in XXX seconds (XXX KB/sec) +XXX files updated, XXX files merged, XXX files removed, XXX files unresolved +checking changesets +checking manifests +crosschecking files in changesets and manifests +checking files +1 files, 1 changesets, 1 total revisions +# clone remote via pull requesting all changes adding changesets adding manifests @@ -70,7 +80,10 @@ Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5: Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5: Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5: +Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5: Got arguments 1:user@dummy 2:hg -R local serve --stdio 3: 4: 5: Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5: +changegroup in remote: u=remote:ssh:127.0.0.1 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5: Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5: +changegroup in remote: u=remote:ssh:127.0.0.1
--- a/tests/test-static-http Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-static-http Wed Aug 09 15:03:46 2006 -0500 @@ -37,6 +37,14 @@ cd local hg verify cat bar + +cd ../remote +echo baz > quux +hg commit -A -mtest2 -d '100000000 0' + +cd ../local +echo '[hooks]' >> .hg/hgrc +echo 'changegroup = echo changegroup: u=$HG_URL' >> .hg/hgrc http_proxy= hg pull kill $!
--- a/tests/test-static-http.out Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-static-http.out Wed Aug 09 15:03:46 2006 -0500 @@ -19,6 +19,12 @@ checking files 1 files, 1 changesets, 1 total revisions foo +adding quux +changegroup: u=static-http://localhost:20059/remote pulling from static-http://localhost:20059/remote searching for changes -no changes found +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +(run 'hg update' to get a working copy)
--- a/tests/test-status Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-status Wed Aug 09 15:03:46 2006 -0500 @@ -32,3 +32,11 @@ hg status echo "hg status modified added removed deleted unknown never-existed ignored:" hg status modified added removed deleted unknown never-existed ignored +hg copy modified copied +echo "hg status -C:" +hg status -C + +echo "hg status -t:" +hg status -t +echo "hg status -A:" +hg status -A
--- a/tests/test-status.out Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-status.out Wed Aug 09 15:03:46 2006 -0500 @@ -101,3 +101,57 @@ ! deleted ? ignored ? unknown +hg status -C: +A added +A copied + modified +R removed +! deleted +? unknown +hg status -t: +hg status: option -t not recognized +hg status [OPTION]... [FILE]... + +show changed files in the working directory + + Show status of files in the repository. If names are given, only + files that match are shown. Files that are clean or ignored, are + not listed unless -c (clean), -i (ignored) or -A is given. + + The codes used to show the status of files are: + M = modified + A = added + R = removed + C = clean + ! = deleted, but still tracked + ? = not tracked + I = ignored (not shown by default) + = the previous added file was copied from here + +aliases: st + +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 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 + -I --include include names matching the given patterns + -X --exclude exclude names matching the given patterns +hg status -A: +A added +A copied + modified +R removed +! deleted +? unknown +I ignored +C .hgignore +C modified
--- a/tests/test-tag Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-tag Wed Aug 09 15:03:46 2006 -0500 @@ -19,6 +19,11 @@ cat .hgtags cat .hg/localtags +hg update 0 +hg tag -d "1000000 0" "foobar" +cat .hgtags +cat .hg/localtags + hg tag -l 'xx newline' hg tag -l 'xx:xx'
--- a/tests/test-tag.out Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-tag.out Wed Aug 09 15:03:46 2006 -0500 @@ -25,5 +25,8 @@ 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah0 c5c60883086f5526bd3e36814b94a73a4e75e172 bleah1 +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +0acdaf8983679e0aac16e811534eb49d7ee1f2b4 foobar +c5c60883086f5526bd3e36814b94a73a4e75e172 bleah1 abort: '\n' cannot be used in a tag name abort: ':' cannot be used in a tag name
--- a/tests/test-up-local-change.out Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-up-local-change.out Wed Aug 09 15:03:46 2006 -0500 @@ -17,7 +17,7 @@ summary: 1 resolving manifests - force None allow None moddirstate True linear True + overwrite False branchmerge False partial False linear True ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e a versions differ, resolve remote created b @@ -33,7 +33,7 @@ summary: 2 resolving manifests - force None allow None moddirstate True linear True + overwrite False branchmerge False partial False linear True ancestor a0c8bcbbb45c local 1165e8bd193e remote a0c8bcbbb45c remote deleted b removing b @@ -51,7 +51,7 @@ summary: 1 resolving manifests - force None allow None moddirstate True linear True + overwrite False branchmerge False partial False linear True ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e a versions differ, resolve remote created b @@ -98,21 +98,12 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: 2 -resolving manifests - force None allow None moddirstate True linear False - ancestor a0c8bcbbb45c local 1165e8bd193e remote 4096f2872392 - a versions differ, resolve - b versions differ, resolve -this update spans a branch affecting the following files: - a (resolve) - b (resolve) -aborting update spanning branches! -(use 'hg merge' to merge across branches or 'hg update -C' to lose changes) +abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes failed abort: outstanding uncommitted changes failed resolving manifests - force False allow True moddirstate True linear False + overwrite False branchmerge True partial False linear False ancestor a0c8bcbbb45c local 1165e8bd193e remote 4096f2872392 a versions differ, resolve b versions differ, resolve
--- a/tests/test-update-reverse.out Wed Aug 09 14:53:03 2006 -0500 +++ b/tests/test-update-reverse.out Wed Aug 09 15:03:46 2006 -0500 @@ -40,7 +40,7 @@ side1 side2 resolving manifests - force 1 allow None moddirstate True linear False + overwrite True branchmerge False partial False linear False ancestor 8515d4bfda76 local 1c0f48f8ece6 remote 0594b9004bae remote deleted side2, clobbering remote deleted side1, clobbering