view lisp/vc-hg.el @ 96044:c1ef445563bb

2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-colview.el (org-columns-next-allowed-value): Bug fix. * org-colview-xemacs.el (org-columns-next-allowed-value): Bug fix. * org-agenda.el (org-agenda-get-closed): Get the end time into the agenda prefix as well. * org-publish.el (org-publish-org-index): Make a properly indented list. * org.el (org-calendar-agenda-action-key): New option. (org-get-cursor-date): New function. (org-mark-entry-for-agenda-action): New command. (org-overriding-default-time): New variable. (org-read-date): Respect `org-overriding-default-time'. * org-remember.el (org-remember-apply-template): Respect the ovverriding default time. * org-agenda.el (org-agenda-action-marker): New variable. (org-agenda-action): New command. (org-agenda-do-action): New function. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org.el (org-schedule, org-deadline): Protect scheduled and deadline tasks against changes that accidently remove the repeater. Also show a message with the new date when done. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org.el (org-beginning-of-line): Cater for the case when there are tags but no headline text. (org-align-tags-here): Convert to tabs only when indent-tabs-mode it set. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-mhe.el (org-mhe-get-message-folder-from-index): Make sure the return value is nil instead of "nil" when there is no match. * org-exp.el (org-insert-centered): Use fill-column instead of 80. (org-export-as-ascii): Use string-width to measure the width of the heading. * org.el (org-diary-to-ical-string): No longer kill buffer FROMBUF, this is now done by the caller. * org-exp.el (org-print-icalendar-entries): Move the call to `org-diary-to-ical-string' out of the loop, and kill the buffer afterwords. * org-remember.el (org-remember-visit-immediately): Position cursor after moving to the note. (org-remember-apply-template): Use a text property to record the cursor position. (org-remember-handler): Align tags after pasting the note. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-bbdb.el (org-bbdb-follow-anniversary-link): New function. * org-agenda.el (org-agenda-open-link): If there is an org-bbdb-name property in the current line, jump to that bbdb entry. * org-bbdb.el (org-bbdb-anniversaries): Add the bbdb-name as a text property, so that the agenda knows where this entry comes from. * org-agenda.el (org-agenda-clock-in): Fixed bug in the interaction between clocking-in from the agenda, and automatic task state switching. * org-macs.el (org-with-point-at): Bug fix in macro defintion. * org.el (org-beginning-of-line, org-end-of-line): Make sure the zmacs-region stays after this command in XEmacs. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org.el (org-scan-tags): Allow new values for ACTION parameter. * org-remember.el (org-remember-templates): Fix bug in customization type definition. * org.el (org-map-entries): New function. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-agenda.el (org-agenda-skip-comment-trees): New option. (org-agenda-skip): Respect `org-agenda-skip-comment-trees'. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-remember.el (org-jump-to-target-location): New variable. (org-remember-apply-template): Set `org-remember-apply-template' if requested by template. (org-remember-handler): Start an idle timer to jump to remember location. * org-exp.el (org-get-current-options): Add the FILETAGS setting. * org.el (org-set-regexps-and-options): Fix bug with parsing of file tags. (org-get-tags-at): Add the content of `org-file-tags'. * org-exp.el (org-export-handle-comments): Fix bug with several comment lines after each other. (org-number-to-roman, org-number-to-counter): New functions. (org-export-section-number-format): New option. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-exp.el (org-export-protect-examples): Catch the case of a missing end_example line. * org.el (org-set-regexps-and-options): Set `org-file-properties' and `org-file-tags' to nil. * org-colview.el (org-columns-next-allowed-value): Handle next argument NTH to directly select a value. * org-colview-xemacs.el (org-columns-next-allowed-value): Handle next argument NTH to directly select a value. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-agenda.el (org-agenda-scheduled-leaders): Fix docstring. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org.el (org-columns-ellipses): New option. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-colview.el (org-columns-add-ellipses): New function. (org-columns-compact-links): New function. (org-columns-cleanup-item): Call `org-columns-compact-links'. (org-columns-display-here): Call `org-agenda-columns-cleanup-item' when in agenda. (org-columns-edit-value): Fixed bug with editing values from agenda column view. (org-columns-redo): Also redo the agenda itself. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-agenda.el (org-agenda-columns-remove-prefix-from-item): New option. * org-colview.el (org-agenda-columns-cleanup-item): New function. * org-exp.el (org-export-ascii-preprocess): Renamed from `org-export-ascii-clean-string'. (org-export-kill-licensed-text) (org-export-define-heading-targets) (org-export-handle-invisible-targets) (org-export-target-internal-links) (org-export-remove-or-extract-drawers) (org-export-remove-archived-trees) (org-export-protect-quoted-subtrees) (org-export-protect-verbatim, org-export-protect-examples) (org-export-select-backend-specific-text) (org-export-mark-blockquote-and-verse) (org-export-remove-comment-blocks-and-subtrees) (org-export-handle-comments, org-export-mark-radio-links) (org-export-remove-special-table-lines) (org-export-normalize-links) (org-export-concatenate-multiline-links) (org-export-concatenate-multiline-emphasis): New functions, obtained from spliiting the export preprocessor. * org-table.el (org-table-recalculate): Improve error message if the row number is invalid. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-archive.el (org-archive-save-context-info): Fix bugs in customization setup and docstring. * org-exp.el (org-export-html-style): Changed the size of in the <pre> element to 90%. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org.el (org-find-src-example-start): Function removed. (org-edit-src-find-region-and-lang): New function. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org.el (org-edit-src-exit): New function. (org-exit-edit-mode): New minor mode. * org-exp.el (org-export-preprocess-string): Fix bug with removing comment-like lines from protected examples. * org.el (org-edit-src-example, org-find-src-example-start) (org-protect-source-example, org-edit-special): New functions. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-publish.el (org-publish-project-alist): Fix typo in docstring. (org-publish-project-alist): Handle :index-title property. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-export-latex.el (org-export-as-latex): Make sure region bounds are correct. Parse subtree properties relating to export. * org-exp.el (org-export-add-options-to-plist): New function. (org-infile-export-plist): Use `org-export-add-options-to-plist'. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org.el (org-default-properties): Add EXPORT_FILE_NAME and EXPORT_TITLE. * org-exp.el (org-export-get-title-from-subtree) (org-export-as-ascii, org-export-as-html): Make sure the original region-beginning and region-end are used, even after moving point. (org-export-get-title-from-subtree): Also try the EXPORT_TITLE property. * org-remember.el (org-remember-last-stored-marker): New variable. (org-remember-goto-last-stored): Use `org-goto-marker-or-bmk'. (org-remember-handler): Also use marker to remember last-stored position. * org.el (org-goto-marker-or-bmk): New function. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org.el (org-file-properties): Renamed from `org-local-properties'. (org-scan-tags): Take file tags into account. (org-tags-match-list-sublevels): Default changed to t. * org-exp.el (org-export-as-html): Close paragraph after a footnote. * org.el (org-update-parent-todo-statistics): New function. * org-exp.el (org-icalendar-store-UID): New option. (org-icalendar-force-UID): Option removed. (org-print-icalendar-entries): IMplement UIDs. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-mhe.el (org-mhe-follow-link): Fix bug in mhe searches. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-faces.el (org-column): Document how this face is being used and why sometimes the background faces shine through. * org-mhe.el (org-mhe-follow-link): Improve handling of searches. * org-publish.el (org-publish-attachment): Create publishing directory if it does not yet exist. * org-table.el (org-calc-default-modes): Change default number format to (float 8). * org.el (org-olpath-completing-read): New function. (org-time-clocksum-format): New option. (org-minutes-to-hh:mm-string): Use `org-time-clocksum-format'. * org-clock.el (org-clock-display, org-clock-out) (org-update-mode-line): Use `org-time-clocksum-format'. * org-colview-xemacs.el (org-columns-number-to-string): Use `org-time-clocksum-format'. * org-colview.el (org-columns-number-to-string): Use `org-time-clocksum-format'. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-id.el: New file, move from contrib to core. * org-exp.el (org-icalendar-force-UID): New option. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-exp.el (org-print-icalendar-entries): Make sure DTEND is shifted by one day if theere is a date range without an end time. * org.el (org-try-structure-completion): New function. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org.el (org-set-font-lock-defaults): Improve fontification of description lists. (org-insert-item): Handle description lists. (org-adaptive-fill-function): Improve auto indentation in description lists. * org-exp.el (org-export-as-html, org-export-preprocess-string): Implement VERSE environment. (org-export-preprocess-string): Implement the COMMENT environment. * org-export-latex.el (org-export-latex-preprocess): Implement VERSE environment. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-jsinfo.el (org-infojs-opts-table): Add entry for FIXED_TOC option. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-table.el (orgtbl-to-tsv, orgtbl-to-csv): New functions. * org.el (org-quote-csv-field): New functions. * org-table.el (org-table-export-default-format): Remove :splice from default format, we get the same effect by not specifying :tstart and :tend. (org-table-export): Improve setup, distinguish better between interactive and non-interactive use, allow specifying the format on the fly, better protection against wrong file names. (orgtbl-to-generic): Fix documentation. Do not require :tstart and :tend when :splice is omitted. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-clock.el (org-clock-select-task): Make sure the selection letters are 1-9 and A-Z, no special characters. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-exp.el (org-export-htmlize): New group. (org-export-htmlize-output-type) (org-export-htmlize-css-font-prefix): New options. (org-export-htmlize-region-for-paste): New function. (org-export-htmlize-generate-css): New command. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org.el (org-set-visibility-according-to-property): New function. (org-ctrl-c-ctrl-c): Do not restart org-mode, just get the options and compute the regular expressions, and update font-lock. (org-property-re): Allow a dash in property names. * org-archive.el (org-extract-archive-file): Insert the file name without the path into the format, to allow the location format to contain a subdirectory. * org-agenda.el (org-agenda-post-command-hook): If point is at end of buffer, and the `org-agenda-type' property undefined, use the value from the character before. * org.el (org-add-planning-info): Don't let indentation for would-be timestamp become extra whitespace at the end of headline. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org.el (org-remove-double-quotes, org-file-contents): New functions. * org-exp.el (org-infile-export-plist): Also parse the contents of #+SETUPFILE files, recursively. * org.el (org-set-regexps-and-options): Also parse the contents of #+SETUPFILE files, recursively. * org-exp.el (org-export-handle-include-files): New function. (org-export-preprocess-string): Call `org-export-handle-include-files'. * org.el (org-delete-property-globally) (org-delete-property, org-set-property): Ignore case during completion. (org-set-property): Use `org-completing-read' instead of `completing-read'. * org.el (org-complete-expand-structure-template): New, experimental function. (org-structure-template-alist): New, experimental option. (org-complete): Call `org-complete-expand-structure-template'. 2008-06-17 Bastien Guerry <bzg@altern.org> * org-export-latex.el (org-export-latex-preprocess): Added support for blockquotes. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org.el (org-read-date-analyze): Catch the case where only a weekday is given. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org.el (org-set-font-lock-defaults): Make the description tag bold. * org-exp.el (org-export-as-html, org-close-li): Implement description lists. 2008-06-17 Jason Riedy <jason@acm.org> * org-table.el (*orgtbl-default-fmt*): New variable. (orgtbl-format-line): Use the value of *orgtbl-default-fmt* when there is no other fmt available. (orgtbl-to-generic): Allow an explicitly nil :tstart or :tend to suppress the appropriate string. (orgtbl-to-orgtbl): New function for translating to another orgtbl table. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org.el (org-read-date-analyze): "." as an alias for "+0" in read date. * org-clock.el (org-clock-save-markers-for-cut-and-paste): New function. * org-agenda.el (org-agenda-save-markers-for-cut-and-paste): New function. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-clock.el (org-clock-find-position): Don't include notes into clock drawer. * org-archive.el (org-archive-subtree): No longer remove an extra line after cutting the subtree. `org-cut-subtree' already takes care of this. * org-remember.el (org-remember-handler): Only kill the target buffer if it does not contain the running clock. * org.el (org-markers-to-move): New variable. (org-save-markers-in-region, org-check-and-save-marker) (org-reinstall-markers-in-region): New function. (org-move-subtree-down, org-copy-subtree): Remember relative marker positions before cutting. (org-move-subtree-down, org-paste-subtree): Restore relative marker positions after pasting. * org-remember.el (org-remember-clock-out-on-exit): New option. (org-remember-finalize): Clock out only if the setting in `org-remember-clock-out-on-exit' requires it. (org-remember-handler): Do the cleanup in the buffer, to make sure that the clock marker remains in tact. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-clock.el (org-clock-goto): Widen buffer if necessary. (org-clock-in): Make sure that also tasks outside the narrowed region will be clocked in correctly. (org-clock-insert-selection-line): Widen the buffer so that we can find the correct task heading. * org.el (org-base-buffer): New function. * org-exp.el (org-icalendar-cleanup-string): Make sure '," and ";" are escaped. (org-print-icalendar-entries): Also apply `org-icalendar-cleanup-string' to the headline, not only to the summary property. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org-exp.el (org-export-preprocess-hook): New hook. (org-export-preprocess-string): Call `org-export-preprocess-hook'. * org.el (org-font-lock-hook): New variable. (org-font-lock-hook): New function. (org-set-font-lock-defaults): Call `org-font-lock-hook'. 2008-06-17 Carsten Dominik <dominik@science.uva.nl> * org.texi: Modify license to no longer include back- and front cover matters. (Using the mapping API): New section. (Agenda column view): New section. (Moving subtrees): Document archiving to the archive sibling. (Agenda commands): Document columns view in the agenda. (Using the property API): Document the API for multi-valued properties.
author Carsten Dominik <dominik@science.uva.nl>
date Tue, 17 Jun 2008 15:22:00 +0000 (2008-06-17)
parents 054335cbee3d
children 09da7727f35f
line wrap: on
line source
;;; vc-hg.el --- VC backend for the mercurial version control system

;; Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc.

;; Author: Ivan Kanis
;; Keywords: tools

;; This file is part of GNU Emacs.

;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; This is a mercurial version control backend

;;; Thanks:

;;; Bugs:

;;; Installation:

;;; Todo:

;; 1) Implement the rest of the vc interface. See the comment at the
;; beginning of vc.el. The current status is:

;; FUNCTION NAME                               STATUS
;; BACKEND PROPERTIES
;; * revision-granularity                      OK
;; STATE-QUERYING FUNCTIONS
;; * registered (file)                         OK
;; * state (file)                              OK
;; - state-heuristic (file)                    NOT NEEDED
;; * working-revision (file)                   OK
;; - latest-on-branch-p (file)                 ??
;; * checkout-model (files)                    OK
;; - workfile-unchanged-p (file)               OK
;; - mode-line-string (file)                   NOT NEEDED
;; - prettify-state-info (file)                OK
;; STATE-CHANGING FUNCTIONS
;; * register (files &optional rev comment)    OK
;; * create-repo ()                            OK
;; - init-revision ()                          NOT NEEDED
;; - responsible-p (file)                      OK
;; - could-register (file)                     OK
;; - receive-file (file rev)                   ?? PROBABLY NOT NEEDED
;; - unregister (file)                         COMMENTED OUT, MAY BE INCORRECT
;; * checkin (files rev comment)               OK
;; * find-revision (file rev buffer)           OK
;; * checkout (file &optional editable rev)    OK
;; * revert (file &optional contents-done)     OK
;; - rollback (files)                          ?? PROBABLY NOT NEEDED
;; - merge (file rev1 rev2)                    NEEDED
;; - merge-news (file)                         NEEDED
;; - steal-lock (file &optional revision)      NOT NEEDED
;; HISTORY FUNCTIONS
;; * print-log (files &optional buffer)        OK
;; - log-view-mode ()                          OK
;; - show-log-entry (revision)                 NOT NEEDED, DEFAULT IS GOOD
;; - comment-history (file)                    NOT NEEDED
;; - update-changelog (files)                  NOT NEEDED
;; * diff (files &optional rev1 rev2 buffer)   OK
;; - revision-completion-table (files)         OK?
;; - annotate-command (file buf &optional rev) OK
;; - annotate-time ()                          OK
;; - annotate-current-time ()                  NOT NEEDED
;; - annotate-extract-revision-at-line ()      OK
;; TAG SYSTEM
;; - create-tag (dir name branchp)       NEEDED
;; - retrieve-tag (dir name update)       NEEDED
;; MISCELLANEOUS
;; - make-version-backups-p (file)             ??
;; - repository-hostname (dirname)             ??
;; - previous-revision (file rev)              OK
;; - next-revision (file rev)                  OK
;; - check-headers ()                          ??
;; - clear-headers ()                          ??
;; - delete-file (file)                        TEST IT
;; - rename-file (old new)                     OK
;; - find-file-hook ()                         PROBABLY NOT NEEDED
;; - find-file-not-found-hook ()               PROBABLY NOT NEEDED

;; 2) Implement Stefan Monnier's advice:
;; vc-hg-registered and vc-hg-state
;; Both of those functions should be super extra careful to fail gracefully in
;; unexpected circumstances. The reason this is important is that any error
;; there will prevent the user from even looking at the file :-(
;; Ideally, just like in vc-arch and vc-cvs, checking that the file is under
;; mercurial's control and extracting the current revision should be done
;; without even using `hg' (this way even if you don't have `hg' installed,
;; Emacs is able to tell you this file is under mercurial's control).

;;; History:
;;

;;; Code:

(eval-when-compile
  (require 'cl)
  (require 'vc))

;;; Customization options

(defcustom vc-hg-global-switches nil
  "*Global switches to pass to any Hg command."
  :type '(choice (const :tag "None" nil)
         (string :tag "Argument String")
         (repeat :tag "Argument List"
             :value ("")
             string))
  :version "22.2"
  :group 'vc)


;;; Properties of the backend

(defun vc-hg-revision-granularity () 'repository)
(defun vc-hg-checkout-model (files) 'implicit)

;;; State querying functions

;;;###autoload (defun vc-hg-registered (file)
;;;###autoload   "Return non-nil if FILE is registered with hg."
;;;###autoload   (if (vc-find-root file ".hg")       ; short cut
;;;###autoload       (progn
;;;###autoload         (load "vc-hg")
;;;###autoload         (vc-hg-registered file))))

;; Modelled after the similar function in vc-bzr.el
(defun vc-hg-registered (file)
  "Return non-nil if FILE is registered with hg."
  (when (vc-hg-root file)           ; short cut
    (let ((state (vc-hg-state file)))  ; expensive
      (vc-file-setprop file 'vc-state state)
      (and state (not (memq state '(ignored unregistered)))))))

(defun vc-hg-state (file)
  "Hg-specific version of `vc-state'."
  (let*
      ((status nil)
       (out
	(with-output-to-string
	  (with-current-buffer
	      standard-output
	    (setq status
		  (condition-case nil
		      ;; Ignore all errors.
		      (call-process
		       "hg" nil t nil "--cwd" (file-name-directory file)
		       "status" "-A" (file-name-nondirectory file))
		    ;; Some problem happened.  E.g. We can't find an `hg'
		    ;; executable.
		    (error nil)))))))
    (when (eq 0 status)
	(when (null (string-match ".*: No such file or directory$" out))
	  (let ((state (aref out 0)))
	    (cond
	     ((eq state ?=) 'up-to-date)
	     ((eq state ?A) 'added)
	     ((eq state ?M) 'edited)
	     ((eq state ?I) 'ignored)
	     ((eq state ?R) 'removed)
	     ((eq state ?!) 'missing)
	     ((eq state ??) 'unregistered)
	     ((eq state ?C) 'up-to-date) ;; Older mercurials use this
	     (t 'up-to-date)))))))

(defun vc-hg-working-revision (file)
  "Hg-specific version of `vc-working-revision'."
  (let*
      ((status nil)
       (out
	(with-output-to-string
	  (with-current-buffer
	      standard-output
	    (setq status
		  (condition-case nil
		      ;; Ignore all errors.
		      (call-process
		       "hg" nil t nil "--cwd" (file-name-directory file)
		       "log" "-l1" (file-name-nondirectory file))
		    ;; Some problem happened.  E.g. We can't find an `hg'
		    ;; executable.
		    (error nil)))))))
    (when (eq 0 status)
      (if (string-match "changeset: *\\([0-9]*\\)" out)
	  (match-string 1 out)
	"0"))))

;;; History functions

(defun vc-hg-print-log (files &optional buffer)
  "Get change log associated with FILES."
  ;; `log-view-mode' needs to have the file names in order to function
  ;; correctly. "hg log" does not print it, so we insert it here by
  ;; hand.

  ;; `vc-do-command' creates the buffer, but we need it before running
  ;; the command.
  (vc-setup-buffer buffer)
  ;; If the buffer exists from a previous invocation it might be
  ;; read-only.
  (let ((inhibit-read-only t))
    (with-current-buffer
	buffer
      (vc-hg-command buffer 0 files "log"))))

(defvar log-view-message-re)
(defvar log-view-file-re)
(defvar log-view-font-lock-keywords)
(defvar log-view-per-file-logs)

(define-derived-mode vc-hg-log-view-mode log-view-mode "Hg-Log-View"
  (require 'add-log) ;; we need the add-log faces
  (set (make-local-variable 'log-view-file-re) "\\`a\\`")
  (set (make-local-variable 'log-view-per-file-logs) nil)
  (set (make-local-variable 'log-view-message-re)
       "^changeset:[ \t]*\\([0-9]+\\):\\(.+\\)")
  (set (make-local-variable 'log-view-font-lock-keywords)
       (append
	log-view-font-lock-keywords
	'(
	  ;; Handle the case:
	  ;; user: FirstName LastName <foo@bar>
	  ("^user:[ \t]+\\([^<(]+?\\)[ \t]*[(<]\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)[>)]"
	   (1 'change-log-name)
	   (2 'change-log-email))
	  ;; Handle the cases:
	  ;; user: foo@bar
	  ;; and
	  ;; user: foo
	  ("^user:[ \t]+\\([A-Za-z0-9_.+-]+\\(?:@[A-Za-z0-9_.-]+\\)?\\)"
	   (1 'change-log-email))
	  ("^date: \\(.+\\)" (1 'change-log-date))
	  ("^summary:[ \t]+\\(.+\\)" (1 'log-view-message))))))

(defun vc-hg-diff (files &optional oldvers newvers buffer)
  "Get a difference report using hg between two revisions of FILES."
  (let* ((firstfile (car files))
	 (working (and firstfile (vc-working-revision firstfile))))
    (when (and (equal oldvers working) (not newvers))
      (setq oldvers nil))
    (when (and (not oldvers) newvers)
      (setq oldvers working))
    (apply #'vc-hg-command (or buffer "*vc-diff*") nil
	   (mapcar (lambda (file) (file-name-nondirectory file)) files)
	   "--cwd" (or (when firstfile (file-name-directory firstfile))
		       (expand-file-name default-directory))
	   "diff"
	   (append
	    (when oldvers
	      (if newvers
		  (list "-r" oldvers "-r" newvers)
		(list "-r" oldvers)))))))

(defun vc-hg-revision-table (files)
  (let ((default-directory (file-name-directory (car files))))
    (with-temp-buffer
      (vc-hg-command t nil files "log" "--template" "{rev} ")
      (split-string
       (buffer-substring-no-properties (point-min) (point-max))))))

;; Modelled after the similar function in vc-cvs.el
(defun vc-hg-revision-completion-table (files)
  (lexical-let ((files files)
                table)
    (setq table (lazy-completion-table
                 table (lambda () (vc-hg-revision-table files))))
    table))

(defun vc-hg-annotate-command (file buffer &optional revision)
  "Execute \"hg annotate\" on FILE, inserting the contents in BUFFER.
Optional arg REVISION is a revision to annotate from."
  (vc-hg-command buffer 0 file "annotate" "-d" "-n"
		 (when revision (concat "-r" revision)))
  (with-current-buffer buffer
    (goto-char (point-min))
    (re-search-forward "^[ \t]*[0-9]")
    (delete-region (point-min) (match-beginning 0))))


;; The format for one line output by "hg annotate -d -n" looks like this:
;;215 Wed Jun 20 21:22:58 2007 -0700: CONTENTS
;; i.e: VERSION_NUMBER DATE: CONTENTS
;; If the user has set the "--follow" option, the output looks like:
;;215 Wed Jun 20 21:22:58 2007 -0700 foo.c: CONTENTS
;; i.e. VERSION_NUMBER DATE FILENAME: CONTENTS
(defconst vc-hg-annotate-re
  "^[ \t]*\\([0-9]+\\) \\(.\\{30\\}\\)[^:\n]*\\(:[^ \n][^:\n]*\\)*: ")

(defun vc-hg-annotate-time ()
  (when (looking-at vc-hg-annotate-re)
    (goto-char (match-end 0))
    (vc-annotate-convert-time
     (date-to-time (match-string-no-properties 2)))))

(defun vc-hg-annotate-extract-revision-at-line ()
  (save-excursion
    (beginning-of-line)
    (when (looking-at vc-hg-annotate-re) (match-string-no-properties 1))))

(defun vc-hg-previous-revision (file rev)
  (let ((newrev (1- (string-to-number rev))))
    (when (>= newrev 0)
      (number-to-string newrev))))

(defun vc-hg-next-revision (file rev)
  (let ((newrev (1+ (string-to-number rev)))
	(tip-revision
	 (with-temp-buffer
	   (vc-hg-command t 0 nil "tip")
	   (goto-char (point-min))
	   (re-search-forward "^changeset:[ \t]*\\([0-9]+\\):")
	   (string-to-number (match-string-no-properties 1)))))
    ;; We don't want to exceed the maximum possible revision number, ie
    ;; the tip revision.
    (when (<= newrev tip-revision)
      (number-to-string newrev))))

;; Modelled after the similar function in vc-bzr.el
(defun vc-hg-delete-file (file)
  "Delete FILE and delete it in the hg repository."
  (condition-case ()
      (delete-file file)
    (file-error nil))
  (vc-hg-command nil 0 file "remove" "--after" "--force"))

;; Modelled after the similar function in vc-bzr.el
(defun vc-hg-rename-file (old new)
  "Rename file from OLD to NEW using `hg mv'."
  (vc-hg-command nil 0 new "mv" old))

(defun vc-hg-register (files &optional rev comment)
  "Register FILES under hg.
REV is ignored.
COMMENT is ignored."
  (vc-hg-command nil 0 files "add"))

(defun vc-hg-create-repo ()
  "Create a new Mercurial repository."
  (vc-hg-command nil 0 nil "init"))

(defalias 'vc-hg-responsible-p 'vc-hg-root)

;; Modelled after the similar function in vc-bzr.el
(defun vc-hg-could-register (file)
  "Return non-nil if FILE could be registered under hg."
  (and (vc-hg-responsible-p file)      ; shortcut
       (condition-case ()
           (with-temp-buffer
             (vc-hg-command t nil file "add" "--dry-run"))
             ;; The command succeeds with no output if file is
             ;; registered.
         (error))))

;; XXX This would remove the file. Is that correct?
;; (defun vc-hg-unregister (file)
;;   "Unregister FILE from hg."
;;   (vc-hg-command nil nil file "remove"))

(defun vc-hg-checkin (files rev comment)
  "Hg-specific version of `vc-backend-checkin'.
REV is ignored."
  (vc-hg-command nil 0 files  "commit" "-m" comment))

(defun vc-hg-find-revision (file rev buffer)
  (let ((coding-system-for-read 'binary)
        (coding-system-for-write 'binary))
    (if rev
	(vc-hg-command buffer 0 file "cat" "-r" rev)
      (vc-hg-command buffer 0 file "cat"))))

;; Modelled after the similar function in vc-bzr.el
(defun vc-hg-checkout (file &optional editable rev)
  "Retrieve a revision of FILE.
EDITABLE is ignored.
REV is the revision to check out into WORKFILE."
  (let ((coding-system-for-read 'binary)
        (coding-system-for-write 'binary))
  (with-current-buffer (or (get-file-buffer file) (current-buffer))
    (if rev
        (vc-hg-command t 0 file "cat" "-r" rev)
      (vc-hg-command t 0 file "cat")))))

;; Modelled after the similar function in vc-bzr.el
(defun vc-hg-workfile-unchanged-p (file)
  (eq 'up-to-date (vc-hg-state file)))

;; Modelled after the similar function in vc-bzr.el
(defun vc-hg-revert (file &optional contents-done)
  (unless contents-done
    (with-temp-buffer (vc-hg-command t 0 file "revert"))))

;;; Hg specific functionality.

(defvar vc-hg-extra-menu-map
  (let ((map (make-sparse-keymap)))
    (define-key map [incoming] '(menu-item "Show incoming" vc-hg-incoming))
    (define-key map [outgoing] '(menu-item "Show outgoing" vc-hg-outgoing))
    map))

(defun vc-hg-extra-menu () vc-hg-extra-menu-map)

(defun vc-hg-extra-status-menu () vc-hg-extra-menu-map)

(define-derived-mode vc-hg-outgoing-mode vc-hg-log-view-mode "Hg-Outgoing")

(define-derived-mode vc-hg-incoming-mode vc-hg-log-view-mode "Hg-Incoming")

(defstruct (vc-hg-extra-fileinfo
            (:copier nil)
            (:constructor vc-hg-create-extra-fileinfo (rename-state extra-name))
            (:conc-name vc-hg-extra-fileinfo->))
  rename-state        ;; rename or copy state
  extra-name)         ;; original name for copies and rename targets, new name for

(defun vc-hg-status-printer (info)
  "Pretty-printer for the vc-dir-fileinfo structure."
  (let ((extra (vc-dir-fileinfo->extra info)))
    (vc-default-status-printer 'Hg info)
    (when extra
      (insert (propertize
	       (format "   (%s %s)"
		       (case (vc-hg-extra-fileinfo->rename-state extra)
			 ('copied "copied from")
			 ('renamed-from "renamed from")
			 ('renamed-to "renamed to"))
		       (vc-hg-extra-fileinfo->extra-name extra))
	       'face 'font-lock-comment-face)))))

(defun vc-hg-after-dir-status (update-function)
  (let ((status-char nil)
	(file nil)
	(translation '((?= . up-to-date)
		       (?C . up-to-date)
		       (?A . added)
		       (?R . removed)
		       (?M . edited)
		       (?I . ignored)
		       (?! . missing)
		       (?  . copy-rename-line)
		       (?? . unregistered)))
	(translated nil)
	(result nil)
	(last-added nil)
	(last-line-copy nil))
      (goto-char (point-min))
      (while (not (eobp))
	(setq translated (cdr (assoc (char-after) translation)))
	(setq file
	      (buffer-substring-no-properties (+ (point) 2)
					      (line-end-position)))
	(cond ((not translated)
	       (setq last-line-copy nil))
	      ((eq translated 'up-to-date)
	       (setq last-line-copy nil))
	      ((eq translated 'copy-rename-line)
	       ;; For copied files the output looks like this:
	       ;; A COPIED_FILE_NAME
	       ;;   ORIGINAL_FILE_NAME
	       (setf (nth 2 last-added)
		     (vc-hg-create-extra-fileinfo 'copied file))
	       (setq last-line-copy t))
	      ((and last-line-copy (eq translated 'removed))
	       ;; For renamed files the output looks like this:
	       ;; A NEW_FILE_NAME
	       ;;   ORIGINAL_FILE_NAME
	       ;; R ORIGINAL_FILE_NAME
	       ;; We need to adjust the previous entry to not think it is a copy.
	       (setf (vc-hg-extra-fileinfo->rename-state (nth 2 last-added))
		     'renamed-from)
	       (push (list file translated
			   (vc-hg-create-extra-fileinfo
			    'renamed-to (nth 0 last-added))) result)
	       (setq last-line-copy nil))
	      (t
	       (setq last-added (list file translated nil))
	       (push last-added result)
	       (setq last-line-copy nil)))
	(forward-line))
      (funcall update-function result)))

(defun vc-hg-dir-status (dir update-function)
  (vc-hg-command (current-buffer) 'async dir "status" "-C")
  (vc-exec-after
   `(vc-hg-after-dir-status (quote ,update-function))))

(defun vc-hg-status-extra-header (name &rest commands)
  (concat (propertize name 'face 'font-lock-type-face)
          (propertize
           (with-temp-buffer
             (apply 'vc-hg-command (current-buffer) 0 nil commands)
             (buffer-substring-no-properties (point-min) (1- (point-max))))
           'face 'font-lock-variable-name-face)))

(defun vc-hg-status-extra-headers (dir)
  "Generate extra status headers for a Mercurial tree."
  (let ((default-directory dir))
    (concat
     (vc-hg-status-extra-header "Root       : " "root") "\n"
     (vc-hg-status-extra-header "Branch     : " "id" "-b") "\n"
     (vc-hg-status-extra-header "Tags       : " "id" "-t") ; "\n"
     ;; these change after each commit
     ;; (vc-hg-status-extra-header "Local num  : " "id" "-n") "\n"
     ;; (vc-hg-status-extra-header "Global id  : " "id" "-i")
     )))

;; XXX this adds another top level menu, instead figure out how to
;; replace the Log-View menu.
(easy-menu-define log-view-mode-menu vc-hg-outgoing-mode-map
  "Hg-outgoing Display Menu"
  `("Hg-outgoing"
    ["Push selected"  vc-hg-push]))

(easy-menu-define log-view-mode-menu vc-hg-incoming-mode-map
  "Hg-incoming Display Menu"
  `("Hg-incoming"
    ["Pull selected"  vc-hg-pull]))

(defun vc-hg-outgoing ()
  (interactive)
  (let ((bname "*Hg outgoing*"))
    (vc-hg-command bname 0 nil "outgoing" "-n")
    (pop-to-buffer bname)
    (vc-hg-outgoing-mode)))

(defun vc-hg-incoming ()
  (interactive)
  (let ((bname "*Hg incoming*"))
    (vc-hg-command bname 0 nil "incoming" "-n")
    (pop-to-buffer bname)
    (vc-hg-incoming-mode)))

(declare-function log-view-get-marked "log-view" ())

;; XXX maybe also add key bindings for these functions.
(defun vc-hg-push ()
  (interactive)
  (let ((marked-list (log-view-get-marked)))
    (if marked-list
	(vc-hg-command
	 nil 0 nil
	 (cons "push"
	       (apply 'nconc
		      (mapcar (lambda (arg) (list "-r" arg)) marked-list))))
	 (error "No log entries selected for push"))))

(defun vc-hg-pull ()
  (interactive)
  (let ((marked-list (log-view-get-marked)))
    (if marked-list
	(vc-hg-command
	 nil 0 nil
	 (cons "pull"
	       (apply 'nconc
		      (mapcar (lambda (arg) (list "-r" arg)) marked-list))))
      (error "No log entries selected for pull"))))

;;; Internal functions

(defun vc-hg-command (buffer okstatus file-or-list &rest flags)
  "A wrapper around `vc-do-command' for use in vc-hg.el.
The difference to vc-do-command is that this function always invokes `hg',
and that it passes `vc-hg-global-switches' to it before FLAGS."
  (apply 'vc-do-command (or buffer "*vc*") okstatus "hg" file-or-list
         (if (stringp vc-hg-global-switches)
             (cons vc-hg-global-switches flags)
           (append vc-hg-global-switches
                   flags))))

(defun vc-hg-root (file)
  (vc-find-root file ".hg"))

(provide 'vc-hg)

;; arch-tag: bd094dc5-715a-434f-a331-37b9fb7cd954
;;; vc-hg.el ends here