view lisp/vc-hg.el @ 94414:d86cb59eea9f

2008-04-27 Carsten Dominik <dominik@science.uva.nl> * org/org.el (org-html-level-start): Always have id's in HTML (org-export-as-html): Use `org-link-protocols' to retrieve the export form of the link. (org-add-link-type): Final parameter renamed from PUBLISH. Better documentation of how it is to be used. Avoid double entries for the same link type. (org-add-link-props): New function. (org-modules-loaded): New variable. (org-load-modules-maybe, org-set-modules): New function. (org-modules): New option. (org-mode, org-cycle, orgstruct-mode, org-run-like-in-org-mode) (orgtbl-mode, org-store-link, org-insert-link-global) (org-open-at-point): Call `org-load-modules-maybe'. (org-search-view): Add more text properties. (org-agenda-schedule, org-agenda-deadline): Allow also in search-type agendas. (org-search-view): Order of arguments has been changed. Interpret prefix-arg as TODO-ONLY. (org-agenda, org-run-agenda-series, org-agenda-manipulate-query): Take new argument order of `org-search-view' into account. (org-todo-only): New variable. (org-search-syntax-table): New variable and function. (org-search-view): Do the search with the special syntax table. (define-obsolete-function-alias): Make work with XEmacs. (org-add-planning-info): Use old date as default when modifying an existing deadline or scheduled item. (org-agenda-compute-time-span): Make argument N optional. (org-agenda-format-date-aligned): Require `cal-iso'. (org-agenda-list): Include week into into agenda heading, don't list it at each date (only on Mondays). (org-read-date-analyze): Define local variable `iso-date'. (org-agenda-format-date-aligned): Remove dependency on `calendar-time-from-absolute'. (org-remember-apply-template, org-go-to-remember-target): Interpret filenames relative to `org-directory'. (org-complete): Silently fail when trying to complete keywords that don't have a default value. (org-get-current-options): Added a #+DATE: option. (org-additional-option-like-keywords): Removed "DATE:" from the list of additional keywords. (org-export-as-html): Removed (current-time) as unnecessary second argument of `format-time-string'. (org-clock-find-position): Handle special case at end of buffer. (org-agenda-day-view): New argument DAY-OF-YEAR, pass it on to `org-agenda-change-time-span'. (org-agenda-week-view): New argument ISO-WEEK, pass it on to `org-agenda-change-time-span'. (org-agenda-month-view): New argument MONTH, pass it on to `org-agenda-change-time-span'. (org-agenda-year-view): New argument YEAR, pass it on to `org-agenda-change-time-span'. (org-agenda-change-time-span): New optional argument N, pass it on to `org-agenda-compute-time-span'. (org-agenda-compute-time-span): New argument N, interpret it by changing the starting day. (org-small-year-to-year): New function. (org-scheduled-past-days): Respect `org-scheduled-past-days'. (org-auto-repeat-maybe): Make sure that repeating dates are pushed into the future, and that the shift is at least one interval, never 0. (org-update-checkbox-count): Fix bug with checkbox counting. (org-add-note): New command. (org-add-log-setup): Renamed from `org-add-log-maybe'. (org-log-note-headings): New entry for plain notes (i.e. notes not related to state changes or clocking). (org-get-org-file): Check for availability of `remember-data-file'. (org-cached-entry-get): Allow a regexp value for `org-use-property-inheritance'. (org-use-property-inheritance): Allow regexp value. Fix bug in customization type. (org-use-tag-inheritance): Allow a list and a regexp value for this variable. (org-scan-tags, org-get-tags-at): Implement selective tag inheritance. (org-entry-get): Respect value `selective' for the INHERIT argument. (org-tag-inherit-p, org-property-inherit-p): New functions. (org-agenda-format-date-aligned): Allow 10 characters for weekday, to acomodate German locale. (org-add-archive-files): New function. (org-agenda-files): New argument `ext', to get archive files as well. (org-tbl-menu): Protect the use of variables that are only available when org-table.el gets loaded. (org-read-agenda-file-list): Error if `org-agenda-files' is a single directory. (org-open-file): Allow a batch process to trigger waiting after executing a system command. (org-store-link): Link to headline when there is not target and no region in an org-mode buffer when creating a link. (org-link-types-re): New variable. (org-make-link-regexps): Compute `org-link-types-re'. (org-make-link-description-function): New option. (org-agenda-date, org-agenda-date-weekend): New faces. (org-archive-sibling-heading): New option. (org-archive-to-archive-sibling): New function. (org-iswitchb): New command. (org-buffer-list): New function. (org-agenda-columns): Also try the #+COLUMNS line in the buffer associated with the entry at point (or with the first entry in the agenda view). (org-modules): Add entry for org-bibtex.el. (org-completion-fallback-command): Moved into `org-completion' group. (org-clock-heading-function): Moved to `org-progress' group. (org-auto-repeat-maybe): Make sure that a note can be enforces if `org-log-repeat' is `note'. (org-modules): Allow additional symbols for external packages. (org-ctrl-c-ctrl-c): Allow for `org-clock-overlays' to be undefined. (org-clock-goto): Hide drawers after showing an entry with `org-clock-goto.' (org-shiftup, org-shiftdown, org-shiftright, org-shiftleft): Try also a clocktable block shift. (org-clocktable-try-shift): New function. (org-columns-hscoll-title): New function. (org-columns-previous-hscroll): New variable. (org-columns-full-header-line-format): New variable. (org-columns-display-here-title, org-columns-remove-overlays): Install `org-columns-hscoll-title' in post-command-hook. * org/org.el: Split into many small files. * org/org-agenda.el: New file, split off from org.el. * org/org-archive.el: New file, split off from org.el. * org/org-bbdb.el: New file. * org/org-bibtex.el: New file, split off from org.el. * org/org-clock.el: New file, split off from org.el. * org/org-colview.el: New file, split off from org.el. * org/org-compat.el: New file, split off from org.el. * org/org-exp.el: New file, split off from org.el. * org/org-faces.el: New file, split off from org.el. * org/org-gnus.el: New file, split off from org.el. * org/org-info.el: New file, split off from org.el. * org/org-infojs.el: New file. * org/org-irc.el: New file. * org/org-macs.el: New file, split off from org.el. * org/org-mew.el: New file. * org/org-mhe.el: New file, split off from org.el. * org/org-publish.el: New file, split off from org.el. * org/org-remember.el: New file, split off from org.el. * org/org-rmail.el: New file, split off from org.el. * org/org-table.el: New file, split off from org.el. * org/org-vm.el: New file, split off from org.el. * org/org-wl.el: New file, split off from org.el. 2008-04-27 Jason Riedy <jason@acm.org> * lisp/org-table.el (orgtbl-to-generic): Add a :remove-nil-lines parameter that supresses lines that evaluate to NIL. (orgtbl-get-fmt): New inline function for picking apart formats that may be lists. (orgtbl-apply-fmt): New inline function for applying formats that may be functions. (orgtbl-eval-str): New inline function for strings that may be functions. (orgtbl-format-line, orgtbl-to-generic): Use and document. (orgtbl-to-latex, orgtbl-to-texinfo): Document. (*orgtbl-llfmt*, *orgtbl-llstart*) (*orgtbl-llend*): Dynamic variables for last-line formatting. (orgtbl-format-section): Shift formatting to support detecting the last line and formatting it specially. (orgtbl-to-generic): Document :ll* formats. Set to the non-ll formats unless overridden. (orgtbl-to-latex): Suggest using :llend to suppress the final \\. (*orgtbl-table*, *orgtbl-rtn*): Dynamically bound variables to hold the input collection of lines and output formatted text. (*orgtbl-hline*, *orgtbl-sep*, *orgtbl-fmt*, *orgtbl-efmt*, (*orgtbl-lfmt*, *orgtbl-lstart*, *orgtbl-lend*): Dynamically bound format parameters. (orgtbl-format-line): New function encapsulating formatting for a single line. (orgtbl-format-section): Similar for each section. Rebinding the dynamic vars customizes the formatting for each section. (orgtbl-to-generic): Use orgtbl-format-line and orgtbl-format-section. (org-get-param): Now unused, so delete. (orgtbl-gather-send-defs): New function to gather all the SEND definitions before a table. (orgtbl-send-replace-tbl): New function to find the RECEIVE corresponding to the current name. (orgtbl-send-table): Use the previous two functions and implement multiple destinations for each table. * doc/org.texi (A LaTeX example): Note that fmt may be a one-argument function, and efmt may be a two-argument function. (Radio tables): Document multiple destinations. 2008-04-27 Carsten Dominik <dominik@science.uva.nl> * org/org-agenda.el (org-add-to-diary-list): New function. (org-prefix-has-effort): New variable. (org-sort-agenda-noeffort-is-high): New option. (org-agenda-columns-show-summaries) (org-agenda-columns-compute-summary-properties): New options. (org-format-agenda-item): Compute the duration of the item. (org-agenda-weekend-days): New variable. (org-agenda-list, org-timeline): Use the proper faces for dates in the agenda and timeline buffers. (org-agenda-archive-to-archive-sibling): New command. (org-agenda-start-with-clockreport-mode): New option. (org-agenda-clockreport-parameter-plist): New option. (org-agenda-clocktable-mode): New variable. (org-agenda-deadline-leaders): Allow a function value for the deadline leader. (org-agenda-get-deadlines): Deal with new function value. * lisp/org-clock.el (org-clock): New customization group. (org-clock-into-drawer, org-clock-out-when-done) (org-clock-in-switch-to-state, org-clock-heading-function): Moved into the new group. (org-clock-out-remove-zero-time-clocks): New option. (org-clock-out): Use `org-clock-out-remove-zero-time-clocks'. (org-dblock-write:clocktable): Allow a Lisp form for the scope parameter. (org-dblock-write:clocktable): Fixed bug with total time calculation. (org-dblock-write:clocktable): Request the unrestricted list of files. (org-get-clocktable): New function. (org-dblock-write:clocktable): Make sure :tstart and :tend can not only be strings but also integers (an absolute day number) and lists (m d y). * org/org-colview.el (org-columns-next-allowed-value) (org-columns-edit-value): Limit the effort for updatig in the agenda to recomputing a single file. (org-columns-compute): Only write property value if it has changed. This avoids raising the buffer-change-flag unnecessarily. (org-agenda-colview-summarize) (org-agenda-colview-compute): New functions. (org-agenda-columns): Call `org-agenda-colview-summarize'. * org/org-exp.el (org-export-run-in-background): New option. (org-export-icalendar): Allow a batch process to trigger waiting after executing a system command. (org-export-preprocess-string): Renamed-from `org-cleaned-string-for-export'. (org-export-html-style): Made target class look like normal text. (org-export-as-html): Make use of the better proprocessing in `org-cleaned-string-for-export'. (org-cleaned-string-for-export): Better treatment of heuristic targets, many more internal links will now work in HTML export. (org-get-current-options): Incorporate LINK_UP, LINK_HOME, and INFOJS. (org-export-inbuffer-options-extra): New variable. (org-export-options-filters): New hook. (org-infile-export-plist): Find also the settings keywords in `org-export-inbuffer-options-extra'. (org-infile-export-plist): Allow multiple #+OPTIONS lines and multiple #+INFOJS_OPT lines. (org-export-html-handle-js-options): New function. (org-export-html-infojs-setup): New option. (org-export-as-html): Call `org-export-html-handle-js-options'. Add autoload to all entry points. (org-skip-comments): Function removed. * org/org-table.el (org-table-make-reference): Extra parenthesis around single fields, to make sure that algebraic formulas get correctly interpreted by calc. (org-table-current-column): No longer interactive. * org/org-export-latex.el (org-export-latex-preprocess): Renamed from `org-export-latex-cleaned-string'. 2008-04-27 Bastien Guerry <bzg@altern.org> * org/org-publish.el (org-publish-get-base-files-1): New function. (org-publish-get-base-files): Use it. (org-publish-temp-files): New variable. Don't require 'dired-aux anymore. (org-publish-initial-buffer): New variable. (org-publish-org-to, org-publish): Use it. (org-publish-get-base-files-1): Bug fix: get the proper list of files when recursing thru a directory. (org-publish-get-base-files): Use the :exclude property to skip both files and directories.
author Carsten Dominik <dominik@science.uva.nl>
date Sun, 27 Apr 2008 18:33:39 +0000
parents 2ecb2ea8d5b5
children d6a121c992e3
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, 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; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

;;; Commentary:

;; This is a mercurial version control backend

;;; Thanks:

;;; Bugs:

;;; Installation:

;;; Todo:

;; 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)                    ?? PROBABLY NOT NEEDED
;; - dir-state (dir)                           OK
;; * working-revision (file)                   OK
;; - latest-on-branch-p (file)                 ??
;; * checkout-model (file)                     OK
;; - workfile-unchanged-p (file)               OK
;; - mode-line-string (file)                   NOT NEEDED
;; - dired-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
;; - wash-log (file)                           ??
;; - logentry-check ()                         NOT NEEDED
;; - 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
;; SNAPSHOT SYSTEM
;; - create-snapshot (dir name branchp)        NEEDED (probably branch?)
;; - assign-name (file name)                   NOT NEEDED
;; - retrieve-snapshot (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

;; 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)

;;; 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-dir-state (dir)
  (with-temp-buffer
    (buffer-disable-undo)		;; Because these buffers can get huge
    (vc-hg-command (current-buffer) nil dir "status" "-A")
    (goto-char (point-min))
    (let ((status-char nil)
	  (file nil))
      (while (not (eobp))
	(setq status-char (char-after))
	(setq file
	      (expand-file-name
	       (buffer-substring-no-properties (+ (point) 2)
					       (line-end-position))))
	(cond
	 ;; State flag for a clean file is now C, might change to =.
	 ;; The rest of the possible states in "hg status" output:
	 ;; 	 ! = deleted, but still tracked
	 ;; should not show up in vc-dired, so don't deal with them
	 ;; here.

	 ;; Mercurial up to 0.9.5 used C, = is used now.
 	 ((or (eq status-char ?=) (eq status-char ?C))
	  (vc-file-setprop file 'vc-backend 'Hg)
 	  (vc-file-setprop file 'vc-state 'up-to-date))
	 ((eq status-char ?A)
	  (vc-file-setprop file 'vc-backend 'Hg)
	  (vc-file-setprop file 'vc-working-revision "0")
	  (vc-file-setprop file 'vc-state 'added))
	 ((eq status-char ?R)
	  (vc-file-setprop file 'vc-backend 'Hg)
	  (vc-file-setprop file 'vc-state 'removed))
	 ((eq status-char ?M)
	  (vc-file-setprop file 'vc-backend 'Hg)
	  (vc-file-setprop file 'vc-state 'edited))
	 ((eq status-char ?I)
	  (vc-file-setprop file 'vc-backend 'Hg)
	  (vc-file-setprop file 'vc-state 'ignored))
	 ((eq status-char ??)
	  (vc-file-setprop file 'vc-backend 'none)
	  (vc-file-setprop file 'vc-state 'unregistered))
	 ((eq status-char ?!)
	  (vc-file-setprop file 'vc-backend 'Hg)
	  (vc-file-setprop file 'vc-state 'missing))
	 (t	;; Presently C, might change to = in 0.9.6
	  (vc-file-setprop file 'vc-backend 'Hg)
	  (vc-file-setprop file 'vc-state 'up-to-date)))
	(forward-line)))))

(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))
    ;; We need to loop and call "hg log" on each file separately.
    ;; "hg log" with multiple file arguments mashes all the logs
    ;; together.  Ironically enough, this puts us back near CVS
    ;; which can't generate proper fileset logs either.
    (dolist (file files)
      (with-current-buffer
	  buffer
	(insert "Working file: " file "\n"))	;; Like RCS/CVS.
      (vc-hg-command buffer 0 file "log"))))

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

(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) "^Working file:[ \t]+\\(.+\\)")
  (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 ((working (vc-working-revision (car files))))
    (if (and (equal oldvers working) (not newvers))
	(setq oldvers nil))
    (if (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" (file-name-directory (car files))
	   "diff"
	   (append
	    (if 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" (if revision (concat "-r" revision)))
  (with-current-buffer buffer
    (goto-char (point-min))
    (re-search-forward "^[0-9]")
    (delete-region (point-min) (1- (point)))))


;; 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
(defconst vc-hg-annotate-re "^[ \t]*\\([0-9]+\\) \\(.\\{30\\}\\): ")

(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)
    (if (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 old "mv"))

(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")))))

(defun vc-hg-checkout-model (file)
  'implicit)

;; 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.

;; XXX This functionality is experimental/work in progress. It might
;; change without notice.
(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")

;; XXX Experimental function for the vc-dired replacement.
(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)
		       (?? . unregistered)))
	(translated nil)
	(result nil))
      (goto-char (point-min))
      (while (not (eobp))
	(setq status-char (char-after))
	(setq file
	      (buffer-substring-no-properties (+ (point) 2)
					      (line-end-position)))
	(setq translated (assoc status-char translation))
	(when (and translated (not (eq (cdr translated) 'up-to-date)))
	  (push (list file (cdr translated)) result))
	(forward-line))
      (funcall update-function result)))

;; XXX Experimental function for the vc-dired replacement.
(defun vc-hg-dir-status (dir update-function)
  (vc-hg-command (current-buffer) 'async dir "status")
  (vc-exec-after
   `(vc-hg-after-dir-status (quote ,update-function))))

;; 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 buffer 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