changeset 108009:17d3324f96dd

Make the log-edit comments use RFC822 format throughout. * vc.el (vc-checkin, vc-modify-change-comment): Adjust to new vc-start/finish-logentry. (vc-find-conflicted-file): New command. (vc-transfer-file): Adjust to new vc-checkin. (vc-next-action): Improve scoping. * vc-hg.el (vc-hg-log-edit-mode): Remove. (vc-hg-checkin): Remove extra arg. Use log-edit-extract-headers. * vc-git.el (vc-git-log-edit-mode): Remove. (vc-git-checkin): Remove extra arg. Use log-edit-extract-headers. (vc-git-commits-coding-system): Rename from git-commits-coding-system. * vc-dispatcher.el (vc-log-edit): Shorten names for log-edit-show-files. (vc-start-logentry): Remove argument `extra'. (vc-finish-logentry): Remove extra args. * vc-bzr.el (vc-bzr-log-edit-mode): Remove. (vc-bzr-checkin): Remove extra arg. Use log-edit-extract-headers. (vc-bzr-conflicted-files): New function. * log-edit.el (log-edit-extra-flags) (log-edit-before-checkin-process): Remove. (log-edit-summary, log-edit-header, log-edit-unknown-header): New faces. (log-edit-headers-alist): New var. (log-edit-header-contents-regexp): New const. (log-edit-match-to-eoh): New function. (log-edit-font-lock-keywords): Use them. (log-edit): Insert a "Summary:" header as default. (log-edit-mode): Mark font-lock rules as case-insensitive. (log-edit-done): Cleanup headers. (log-view-process-buffer): Remove. (log-edit-extract-headers): New function to replace it.
author Stefan Monnier <monnier@iro.umontreal.ca>
date Tue, 20 Apr 2010 22:05:24 -0400
parents 3ccddf080698
children 4ae75c6f19d6
files etc/NEWS lisp/ChangeLog lisp/emacs-lisp/cl-loaddefs.el lisp/log-edit.el lisp/vc-bzr.el lisp/vc-dispatcher.el lisp/vc-git.el lisp/vc-hg.el lisp/vc.el
diffstat 9 files changed, 268 insertions(+), 158 deletions(-) [+]
line wrap: on
line diff
--- a/etc/NEWS	Wed Apr 21 02:49:40 2010 +0200
+++ b/etc/NEWS	Tue Apr 20 22:05:24 2010 -0400
@@ -88,6 +88,8 @@
 ** Archive Mode has basic support to browse 7z archives.
 
 ** partial-completion-mode is now obsolete.
+You can get the same behavior with
+(setq completion-styles '(partial-completion initials)).
 
 ** mpc.el: Can use pseudo tags of the form tag1|tag2 as a union of two tags.
 ** Customize
@@ -106,23 +108,18 @@
 
 ** VC and related modes
 
-*** New VC commands: vc-log-incoming and vc-log-outgoing.
+*** New VC commands: vc-log-incoming, vc-log-outgoing, vc-find-conflicted-file.
 
 *** vc-dir for Bzr supports viewing shelve contents and shelving snapshots.
 
 *** Special markup can be added to log-edit buffers.
-
-**** For Bzr, adding an
-Author: NAME
-line will add "--author NAME" to the "bzr commit" command.
-
-**** For Git, adding an
-Author: NAME
-line will add "--author NAME" to the "git commit" command.
-
-**** For Hg, adding an
-Author: NAME
-line will add "--user NAME" to the "hg commit" command.
+The log-edit buffers are expected to have a format similar to email messages
+with headers of the form:
+  Author: <author of this change>
+  Summary: <one line summary of this change>
+  Fixes: <reference to the bug fixed by this change>
+Some backends handle some of those headers specially, but any unknown header
+is just left as is in the message, so it is not lost.
 
 ** Directory local variables can apply to file-less buffers.
 For example, adding "(diff-mode . ((mode . whitespace)))" to your
--- a/lisp/ChangeLog	Wed Apr 21 02:49:40 2010 +0200
+++ b/lisp/ChangeLog	Tue Apr 20 22:05:24 2010 -0400
@@ -1,3 +1,39 @@
+2010-04-21  Stefan Monnier  <monnier@iro.umontreal.ca>
+
+	* vc.el (vc-checkin, vc-modify-change-comment):
+	Adjust to new vc-start/finish-logentry.
+	(vc-find-conflicted-file): New command.
+	(vc-transfer-file): Adjust to new vc-checkin.
+	(vc-next-action): Improve scoping.
+
+	* vc-hg.el (vc-hg-log-edit-mode): Remove.
+	(vc-hg-checkin): Remove extra arg.  Use log-edit-extract-headers.
+
+	* vc-git.el (vc-git-log-edit-mode): Remove.
+	(vc-git-checkin): Remove extra arg.  Use log-edit-extract-headers.
+	(vc-git-commits-coding-system): Rename from git-commits-coding-system.
+
+	* vc-dispatcher.el (vc-log-edit): Shorten names for log-edit-show-files.
+	(vc-start-logentry): Remove argument `extra'.
+	(vc-finish-logentry): Remove extra args.
+
+	* vc-bzr.el (vc-bzr-log-edit-mode): Remove.
+	(vc-bzr-checkin): Remove extra arg.  Use log-edit-extract-headers.
+	(vc-bzr-conflicted-files): New function.
+
+	* log-edit.el (log-edit-extra-flags)
+	(log-edit-before-checkin-process): Remove.
+	(log-edit-summary, log-edit-header, log-edit-unknown-header): New faces.
+	(log-edit-headers-alist): New var.
+	(log-edit-header-contents-regexp): New const.
+	(log-edit-match-to-eoh): New function.
+	(log-edit-font-lock-keywords): Use them.
+	(log-edit): Insert a "Summary:" header as default.
+	(log-edit-mode): Mark font-lock rules as case-insensitive.
+	(log-edit-done): Cleanup headers.
+	(log-view-process-buffer): Remove.
+	(log-edit-extract-headers): New function to replace it.
+
 2010-04-20  Juanma Barranquero  <lekktu@gmail.com>
 
 	* subr.el (default-direction-reversed): Remove obsolescence info.
--- a/lisp/emacs-lisp/cl-loaddefs.el	Wed Apr 21 02:49:40 2010 +0200
+++ b/lisp/emacs-lisp/cl-loaddefs.el	Tue Apr 20 22:05:24 2010 -0400
@@ -1242,7 +1242,6 @@
 ;; version-control: never
 ;; no-byte-compile: t
 ;; no-update-autoloads: t
+;; coding: utf-8
 ;; End:
-
-;; arch-tag: 08cc5aab-e992-47f6-992e-12a7428c1a0e
 ;;; cl-loaddefs.el ends here
--- a/lisp/log-edit.el	Wed Apr 21 02:49:40 2010 +0200
+++ b/lisp/log-edit.el	Tue Apr 20 22:05:24 2010 -0400
@@ -125,6 +125,7 @@
   :type 'boolean)
 
 (defcustom log-edit-hook '(log-edit-insert-cvs-template
+                           log-edit-show-files
 			   log-edit-insert-changelog)
   "Hook run at the end of `log-edit'."
   :group 'log-edit
@@ -188,22 +189,6 @@
 (defvar log-edit-callback nil)
 (defvar log-edit-diff-function nil)
 (defvar log-edit-listfun nil)
-(defvar log-edit-extra-flags nil
-  "List of extra flags to pass to the check in command.")
-(defvar log-edit-before-checkin-process nil
-  "Alist with instructions for processing the commit message before check in.
-The format is: (REGEXP . INSTRUCTIONS).
-All lines matching REGEXP are removed.  For example:
-
-\(\"^#.*\" . nil)
-
-means: just remove all lines starting with #.  This can be used
-to insert lines in the commit buffer that contain, for example, the
-list of files to be committed.
-
-\(\"Author: \\\\(.*\\\\)\" . (list \"--author\" (match-string 1)))
-
-means: append (list \"--author\" (match-string 1)) to `log-edit-extra-flags'.")
 
 (defvar log-edit-parent-buffer nil)
 
@@ -329,10 +314,53 @@
 ;;; Actual code
 ;;;
 
+(defface log-edit-summary '((t :inherit font-lock-function-name-face))
+  "Face for the summary in `log-edit-mode' buffers.")
+
+(defface log-edit-header '((t :inherit font-lock-keyword-face))
+  "Face for the headers in `log-edit-mode' buffers.")
+
+(defface log-edit-unknown-header '((t :inherit font-lock-comment-face))
+  "Face for unknown headers in `log-edit-mode' buffers.")
+
+(defvar log-edit-headers-alist '(("Summary" . log-edit-summary)
+                                 ("Fixes") ("Author"))
+  "AList of known headers and the face to use to highlight them.")
+
+(defconst log-edit-header-contents-regexp
+  "[ \t]*\\(.*\\(\n[ \t].*\\)*\\)\n?")
+
+(defun log-edit-match-to-eoh (limit)
+  ;; FIXME: copied from message-match-to-eoh.
+  (let ((start (point)))
+    (rfc822-goto-eoh)
+    ;; Typical situation: some temporary change causes the header to be
+    ;; incorrect, so EOH comes earlier than intended: the last lines of the
+    ;; intended headers are now not considered part of the header any more,
+    ;; so they don't have the multiline property set.  When the change is
+    ;; completed and the header has its correct shape again, the lack of the
+    ;; multiline property means we won't rehighlight the last lines of
+    ;; the header.
+    (if (< (point) start)
+        nil                             ;No header within start..limit.
+      ;; Here we disregard LIMIT so that we may extend the area again.
+      (set-match-data (list start (point)))
+      (point))))
+
 (defvar log-edit-font-lock-keywords
-  '(("\\`\\(Summary:\\)\\(.*\\)"
-     (1 font-lock-keyword-face)
-     (2 font-lock-function-name-face))))
+  ;; Copied/inspired by message-font-lock-keywords.
+  `((log-edit-match-to-eoh
+     (,(concat "^\\(\\([a-z]+\\):\\)" log-edit-header-contents-regexp
+               "\\|\\(.*\\)")
+      (progn (goto-char (match-beginning 0)) (match-end 0)) nil
+      (1 (if (assoc (match-string 2) log-edit-headers-alist)
+             'log-edit-header
+           'log-edit-unknown-header)
+         nil lax)
+      (3 (or (cdr (assoc (match-string 2) log-edit-headers-alist))
+             'log-edit-header)
+         nil lax)
+      (4 font-lock-warning-face)))))
 
 ;;;###autoload
 (defun log-edit (callback &optional setup params buffer mode &rest ignore)
@@ -358,7 +386,10 @@
     (if buffer (pop-to-buffer buffer))
     (when (and log-edit-setup-invert (not (eq setup 'force)))
       (setq setup (not setup)))
-    (when setup (erase-buffer))
+    (when setup
+      (erase-buffer)
+      (insert "Summary: ")
+      (save-excursion (insert "\n\n")))
     (if mode
 	(funcall mode)
       (log-edit-mode))
@@ -387,7 +418,7 @@
 
 \\{log-edit-mode-map}"
   (set (make-local-variable 'font-lock-defaults)
-       '(log-edit-font-lock-keywords t))
+       '(log-edit-font-lock-keywords t t))
   (make-local-variable 'log-edit-comment-ring-index)
   (hack-dir-local-variables-non-file-buffer))
 
@@ -401,6 +432,17 @@
   "Finish editing the log message and commit the files.
 If you want to abort the commit, simply delete the buffer."
   (interactive)
+  ;; Clean up empty headers.
+  (goto-char (point-min))
+  (while (looking-at (concat "^[a-z]*:" log-edit-header-contents-regexp))
+    (let ((beg (match-beginning 0)))
+      (goto-char (match-end 0))
+      (if (string-match "\\`[ \n\t]*\\'" (match-string 1))
+          (delete-region beg (point)))))
+  ;; Get rid of leading empty lines.
+  (goto-char (point-min))
+  (when (looking-at "\\([ \t]*\n\\)+")
+    (delete-region (match-beginning 0) (match-end 0)))
   ;; Get rid of trailing empty lines
   (goto-char (point-max))
   (skip-syntax-backward " ")
@@ -458,12 +500,13 @@
   "(Un)Indent the current buffer rigidly to `log-edit-common-indent'."
   (save-excursion
     (let ((common (point-max)))
-      (goto-char (point-min))
+      (rfc822-goto-eoh)
       (while (< (point) (point-max))
         (if (not (looking-at "^[ \t]*$"))
             (setq common (min common (current-indentation))))
         (forward-line 1))
-      (indent-rigidly (point-min) (point-max)
+      (rfc822-goto-eoh)
+      (indent-rigidly (point) (point-max)
 		      (- log-edit-common-indent common)))))
 
 (defun log-edit-show-diff ()
@@ -546,6 +589,10 @@
 or if the command is repeated a second time in a row, use the first log entry
 regardless of user name or time."
   (interactive "P")
+  (let ((eoh (save-excursion (rfc822-goto-eoh) (point))))
+    (when (<= (point) eoh)
+      (goto-char eoh)
+      (if (looking-at "\n") (forward-char 1))))
   (let ((log-edit-changelog-use-first
 	 (or use-first (eq last-command 'log-edit-insert-changelog))))
     (log-edit-insert-changelog-entries (log-edit-files)))
@@ -731,16 +778,39 @@
       (log-edit-changelog-insert-entries (car buffer-entry) (cdr buffer-entry))
       (when (cdr buffer-entry) (newline)))))
 
-(defun log-view-process-buffer ()
-  (when log-edit-before-checkin-process
-    (dolist (crt log-edit-before-checkin-process)
-      ;; Remove all lines matching (car crt)
-      ;; Append to `log-edit-extra-flags' the results of (cdr crt).
+(defun log-edit-extract-headers (headers comment)
+  "Extract headers from COMMENT to form command line arguments.
+HEADERS should be an alist with elements of the form (HEADER . CMDARG)
+associating header names to the corresponding cmdline option name and the
+result is then a list of the form (MSG CMDARG1 HDRTEXT1 CMDARG2 HDRTEXT2...).
+where MSG is the remaining text from STRING.
+If \"Summary\" is not in HEADERS, then the \"Summary\" header is extracted
+anyway and put back as the first line of MSG."
+  (with-temp-buffer
+    (insert comment)
+    (rfc822-goto-eoh)
+    (narrow-to-region (point-min) (point))
+    (let ((case-fold-search t)
+          (summary ())
+          (res ()))
+      (dolist (header (if (assoc "Summary" headers) headers
+                        (cons '("Summary" . t) headers)))
+        (goto-char (point-min))
+        (while (re-search-forward (concat "^" (car header)
+                                          ":" log-edit-header-contents-regexp)
+                                  nil t)
+          (if (eq t (cdr header))
+              (setq summary (match-string 1))
+            (push (match-string 1) res)
+            (push (or (cdr header) (car header)) res))
+          (replace-match "" t t)))
+      ;; Remove header separator if the header is empty.
+      (widen)
       (goto-char (point-min))
-      (while (re-search-forward (car crt) nil t)
-	(when (cdr crt)
-	  (setq log-edit-extra-flags (append log-edit-extra-flags (eval (cdr crt)))))
-	(replace-match "" nil t)))))
+      (when (looking-at "\\([ \t]*\n\\)+")
+        (delete-region (match-beginning 0) (match-end 0)))
+      (if summary (insert summary "\n"))
+      (cons (buffer-string) res))))
 
 (provide 'log-edit)
 
--- a/lisp/vc-bzr.el	Wed Apr 21 02:49:40 2010 +0200
+++ b/lisp/vc-bzr.el	Tue Apr 20 22:05:24 2010 -0400
@@ -451,11 +451,16 @@
   "Unregister FILE from bzr."
   (vc-bzr-command "remove" nil 0 file "--keep"))
 
-(defun vc-bzr-checkin (files rev comment &optional extra-args)
+(declare-function log-edit-extract-headers "log-edit" (headers string))
+
+(defun vc-bzr-checkin (files rev comment)
   "Check FILE in to bzr with log message COMMENT.
 REV non-nil gets an error."
   (if rev (error "Can't check in a specific revision with bzr"))
-  (apply 'vc-bzr-command "commit" nil 0 files (append (list "-m" comment) extra-args)))
+  (apply 'vc-bzr-command "commit" nil 'async
+         files (cons "-m" (log-edit-extract-headers '(("Author" . "--author")
+                                                      ("Fixes" . "--fixes"))
+                                                    comment))))
 
 (defun vc-bzr-find-revision (file rev buffer)
   "Fetch revision REV of file FILE and put it into BUFFER."
@@ -552,23 +557,6 @@
 	(goto-char (point-min)))
       found)))
 
-(declare-function log-edit-mode "log-edit" ())
-(defvar log-edit-extra-flags)
-(defvar log-edit-before-checkin-process)
-
-(define-derived-mode vc-bzr-log-edit-mode log-edit-mode "Bzr-Log-Edit"
-  "Mode for editing Bzr commit logs.
-If a line like:
-Author: NAME
-is present in the log, it is removed, and
---author NAME
-is passed to the bzr commit command.  Similarly with Fixes: and --fixes."
-  (set (make-local-variable 'log-edit-extra-flags) nil)
-  (set (make-local-variable 'log-edit-before-checkin-process)
-       '(("^\\(Author\\|Fixes\\):[ \t]+\\(.*\\)[ \t]*$" .
-          (list (format "--%s" (downcase (match-string 1)))
-                (match-string 2))))))
-
 (defun vc-bzr-diff (files &optional rev1 rev2 buffer)
   "VC bzr backend for diff."
   ;; `bzr diff' exits with code 1 if diff is non-empty.
@@ -983,6 +971,19 @@
           (setq loglines (buffer-substring-no-properties start (point-max))))))
     vc-bzr-revisions))
 
+(defun vc-bzr-conflicted-files (dir)
+  (let ((default-directory (vc-bzr-root dir))
+        (files ()))
+    (with-temp-buffer
+      (vc-bzr-command "status" t 0 default-directory)
+      (goto-char (point-min))
+      (when (re-search-forward "^conflicts:\n" nil t)
+        (while (looking-at "  \\(?:Text conflict in \\(.*\\)\\|.*\\)\n")
+          (if (match-end 1)
+              (push (expand-file-name (match-string 1)) files))
+          (goto-char (match-end 0)))))
+    files))
+
 ;;; Revision completion
 
 (eval-and-compile
--- a/lisp/vc-dispatcher.el	Wed Apr 21 02:49:40 2010 +0200
+++ b/lisp/vc-dispatcher.el	Tue Apr 20 22:05:24 2010 -0400
@@ -141,7 +141,6 @@
 (defvar vc-log-operation nil)
 (defvar vc-log-after-operation-hook nil)
 (defvar vc-log-fileset)
-(defvar vc-log-extra)
 
 ;; In a log entry buffer, this is a local variable
 ;; that points to the buffer for which it was made
@@ -521,17 +520,20 @@
 	(with-current-buffer vc-parent-buffer default-directory))
   (log-edit 'vc-finish-logentry
 	    nil
-	    `((log-edit-listfun . (lambda () ',fileset))
+	    `((log-edit-listfun . (lambda ()
+                                    ;; FIXME: Should expand the list
+                                    ;; for directories.
+                                    (mapcar 'file-relative-name
+                                            ',fileset)))
 	      (log-edit-diff-function . (lambda () (vc-diff nil))))
 	    nil
 	    mode)
   (set (make-local-variable 'vc-log-fileset) fileset)
-  (make-local-variable 'vc-log-extra)
   (set-buffer-modified-p nil)
   (setq buffer-file-name nil))
 
-(defun vc-start-logentry (files extra comment initial-contents msg logbuf mode action &optional after-hook)
-  "Accept a comment for an operation on FILES with extra data EXTRA.
+(defun vc-start-logentry (files comment initial-contents msg logbuf mode action &optional after-hook)
+  "Accept a comment for an operation on FILES.
 If COMMENT is nil, pop up a LOGBUF buffer, emit MSG, and set the
 action on close to ACTION.  If COMMENT is a string and
 INITIAL-CONTENTS is non-nil, then COMMENT is used as the initial
@@ -561,7 +563,6 @@
     (when after-hook
       (setq vc-log-after-operation-hook after-hook))
     (setq vc-log-operation action)
-    (setq vc-log-extra extra)
     (when comment
       (erase-buffer)
       (when (stringp comment) (insert comment)))
@@ -570,10 +571,8 @@
       (vc-finish-logentry (eq comment t)))))
 
 (declare-function vc-dir-move-to-goal-column "vc-dir" ())
-;; vc-finish-logentry is called from a log-edit buffer (see above).
-(declare-function log-view-process-buffer "log-edit" ())
-(defvar log-edit-extra-flags)
-
+;; vc-finish-logentry is typically called from a log-edit buffer (see
+;; vc-start-logentry).
 (defun vc-finish-logentry (&optional nocomment)
   "Complete the operation implied by the current log entry.
 Use the contents of the current buffer as a check-in or registration
@@ -590,25 +589,21 @@
   (unless vc-log-operation
     (error "No log operation is pending"))
 
-  (log-view-process-buffer)
-
   ;; save the parameters held in buffer-local variables
   (let ((logbuf (current-buffer))
 	(log-operation vc-log-operation)
+        ;; FIXME: When coming from VC-Dir, we should check that the
+        ;; set of selected files is still equal to vc-log-fileset,
+        ;; to avoid surprises.
 	(log-fileset vc-log-fileset)
-	(log-extra vc-log-extra)
 	(log-entry (buffer-string))
-	(extra-flags log-edit-extra-flags)
 	(after-hook vc-log-after-operation-hook))
     (pop-to-buffer vc-parent-buffer)
     ;; OK, do it to it
     (save-excursion
       (funcall log-operation
 	       log-fileset
-	       log-extra
-	       log-entry
-	       extra-flags
-	       ))
+	       log-entry))
     ;; Remove checkin window (after the checkin so that if that fails
     ;; we don't zap the log buffer and the typing therein).
     ;; -- IMO this should be replaced with quit-window
--- a/lisp/vc-git.el	Wed Apr 21 02:49:40 2010 +0200
+++ b/lisp/vc-git.el	Tue Apr 20 22:05:24 2010 -0400
@@ -118,7 +118,7 @@
   :version "23.1"
   :group 'vc)
 
-(defvar git-commits-coding-system 'utf-8
+(defvar vc-git-commits-coding-system 'utf-8
   "Default coding system for git commits.")
 
 ;;; BACKEND PROPERTIES
@@ -548,11 +548,15 @@
 (defun vc-git-unregister (file)
   (vc-git-command nil 0 file "rm" "-f" "--cached" "--"))
 
+(declare-function log-edit-extract-headers "log-edit" (headers string))
 
-(defun vc-git-checkin (files rev comment  &optional extra-args)
-  (let ((coding-system-for-write git-commits-coding-system))
+(defun vc-git-checkin (files rev comment)
+  (let ((coding-system-for-write vc-git-commits-coding-system))
     (apply 'vc-git-command nil 0 files
-	   (nconc (list "commit" "-m" comment) extra-args (list "--only" "--")))))
+	   (nconc (list "commit" "-m")
+                  (log-edit-extract-headers '(("Author" . "--author"))
+                                            comment)
+                  (list "--only" "--")))))
 
 (defun vc-git-find-revision (file rev buffer)
   (let* (process-file-side-effects
@@ -582,7 +586,7 @@
   "Get change log associated with FILES.
 Note that using SHORTLOG requires at least Git version 1.5.6,
 for the --graph option."
-  (let ((coding-system-for-read git-commits-coding-system))
+  (let ((coding-system-for-read vc-git-commits-coding-system))
     ;; `vc-do-command' creates the buffer, but we need it before running
     ;; the command.
     (vc-setup-buffer buffer)
@@ -793,21 +797,6 @@
                    (progn (forward-line 1) (1- (point)))))))))
     (or (vc-git-symbolic-commit next-rev) next-rev)))
 
-(declare-function log-edit-mode "log-edit" ())
-(defvar log-edit-extra-flags)
-(defvar log-edit-before-checkin-process)
-
-(define-derived-mode vc-git-log-edit-mode log-edit-mode "Git-log-edit"
-  "Mode for editing Git commit logs.
-If a line like:
-Author: NAME
-is present in the log, it is removed, and
---author=NAME
-is passed to the git commit command."
-  (set (make-local-variable 'log-edit-extra-flags) nil)
-  (set (make-local-variable 'log-edit-before-checkin-process)
-       '(("^Author:[ \t]+\\(.*\\)[ \t]*$" . (list "--author" (match-string 1))))))
-
 (defun vc-git-delete-file (file)
   (vc-git-command nil 0 file "rm" "-f" "--"))
 
--- a/lisp/vc-hg.el	Wed Apr 21 02:49:40 2010 +0200
+++ b/lisp/vc-hg.el	Tue Apr 20 22:05:24 2010 -0400
@@ -296,20 +296,7 @@
 	  ("^tag: +\\([^ ]+\\)$" (1 'highlight))
 	  ("^summary:[ \t]+\\(.+\\)" (1 'log-view-message)))))))
 
-(declare-function log-edit-mode "log-edit" ())
-(defvar log-edit-extra-flags)
-(defvar log-edit-before-checkin-process)
-
-(define-derived-mode vc-hg-log-edit-mode log-edit-mode "Hg-log-edit"
-  "Mode for editing Hg commit logs.
-If a line like:
-Author: NAME
-is present in the log, it is removed, and
---author NAME
-is passed to the hg commit command."
-  (set (make-local-variable 'log-edit-extra-flags) nil)
-  (set (make-local-variable 'log-edit-before-checkin-process)
-       '(("^Author:[ \t]+\\(.*\\)[ \t]*$" . (list "--user" (match-string 1))))))
+(declare-function log-edit-extract-headers "log-edit" (headers string))
 
 (defun vc-hg-diff (files &optional oldvers newvers buffer)
   "Get a difference report using hg between two revisions of FILES."
@@ -434,11 +421,15 @@
 ;;   "Unregister FILE from hg."
 ;;   (vc-hg-command nil nil file "remove"))
 
-(defun vc-hg-checkin (files rev comment &optional extra-args)
+(declare-function log-edit-extract-headers "log-edit" (headers string))
+
+(defun vc-hg-checkin (files rev comment)
   "Hg-specific version of `vc-backend-checkin'.
 REV is ignored."
   (apply 'vc-hg-command nil 0 files
-         (nconc (list "commit" "-m" comment) extra-args)))
+         (nconc (list "commit" "-m")
+                (log-edit-extract-headers '(("Author" . "--user"))
+                                          comment))))
 
 (defun vc-hg-find-revision (file rev buffer)
   (let ((coding-system-for-read 'binary)
--- a/lisp/vc.el	Wed Apr 21 02:49:40 2010 +0200
+++ b/lisp/vc.el	Tue Apr 20 22:05:24 2010 -0400
@@ -268,15 +268,12 @@
 ;;   Unregister FILE from this backend.  This is only needed if this
 ;;   backend may be used as a "more local" backend for temporary editing.
 ;;
-;; * checkin (files rev comment &optional extra-args)
+;; * checkin (files rev comment)
 ;;
-;;   Commit changes in FILES to this backend.  If REV is non-nil, that
-;;   should become the new revision number (not all backends do
-;;   anything with it).  COMMENT is used as a check-in comment.  The
-;;   implementation should pass the value of vc-checkin-switches to
-;;   the backend command.  (Note: in older versions of VC, this
-;;   command took a single file argument and not a list.)
-;;   EXTRA-ARGS should be passed to the backend command.
+;;   Commit changes in FILES to this backend.  REV is a historical artifact
+;;   and should be ignored.  COMMENT is used as a check-in comment.
+;;   The implementation should pass the value of vc-checkin-switches to
+;;   the backend command.
 ;;
 ;; * find-revision (file rev buffer)
 ;;
@@ -548,6 +545,12 @@
 ;;   makes it possible to provide menu entries for functionality that
 ;;   is specific to a backend and which does not map to any of the VC
 ;;   generic concepts.
+;;
+;; - conflicted-files (dir)
+;;
+;;   Return the list of files where conflict resolution is needed in
+;;   the project that contains DIR.
+;;   FIXME: what should it do with non-text conflicts?
 
 ;;; Todo:
 
@@ -1054,8 +1057,7 @@
          (state (nth 3 vc-fileset))
          ;; The backend should check that the checkout-model is consistent
          ;; among all the `files'.
-	 (model (nth 4 vc-fileset))
-	 revision)
+	 (model (nth 4 vc-fileset)))
 
     ;; Do the right thing
     (cond
@@ -1070,11 +1072,13 @@
       (cond
        (verbose
 	;; go to a different revision
-	(setq revision (read-string "Branch, revision, or backend to move to: "))
-	(let ((revision-downcase (downcase revision)))
+	(let* ((revision
+                (read-string "Branch, revision, or backend to move to: "))
+               (revision-downcase (downcase revision)))
 	  (if (member
 	       revision-downcase
-	       (mapcar (lambda (arg) (downcase (symbol-name arg))) vc-handled-backends))
+	       (mapcar (lambda (arg) (downcase (symbol-name arg)))
+                       vc-handled-backends))
 	      (let ((vsym (intern-soft revision-downcase)))
 		(dolist (file files) (vc-transfer-file file vsym)))
 	    (dolist (file files)
@@ -1119,8 +1123,8 @@
 	    (message "No files remain to be committed")
 	  (if (not verbose)
 	      (vc-checkin ready-for-commit backend)
-	    (setq revision (read-string "New revision or backend: "))
-	    (let ((revision-downcase (downcase revision)))
+	    (let* ((revision (read-string "New revision or backend: "))
+                   (revision-downcase (downcase revision)))
 	      (if (member
 		   revision-downcase
 		   (mapcar (lambda (arg) (downcase (symbol-name arg)))
@@ -1365,7 +1369,7 @@
 (defun vc-checkin (files backend &optional rev comment initial-contents)
   "Check in FILES.
 The optional argument REV may be a string specifying the new revision
-level (if nil increment the current level).  COMMENT is a comment
+level (strongly deprecated).  COMMENT is a comment
 string; if omitted, a buffer is popped up to accept a comment.  If
 INITIAL-CONTENTS is non-nil, then COMMENT is used as the initial contents
 of the log entry buffer.
@@ -1379,28 +1383,30 @@
   (lexical-let
    ((backend backend))
    (vc-start-logentry
-    files rev comment initial-contents
+    files comment initial-contents
     "Enter a change comment."
     "*VC-log*"
     (lambda ()
       (vc-call-backend backend 'log-edit-mode))
-    (lambda (files rev comment extra-flags)
-      (message "Checking in %s..." (vc-delistify files))
-      ;; "This log message intentionally left almost blank".
-      ;; RCS 5.7 gripes about white-space-only comments too.
-      (or (and comment (string-match "[^\t\n ]" comment))
-	  (setq comment "*** empty log message ***"))
-      (with-vc-properties
-       files
-       ;; We used to change buffers to get local value of vc-checkin-switches,
-       ;; but 'the' local buffer is not a well-defined concept for filesets.
-       (progn
-	 (vc-call-backend backend 'checkin files rev comment extra-flags)
-	 (mapc 'vc-delete-automatic-version-backups files))
-       `((vc-state . up-to-date)
-	 (vc-checkout-time . ,(nth 5 (file-attributes file)))
-	 (vc-working-revision . nil)))
-      (message "Checking in %s...done" (vc-delistify files)))
+    (lexical-let ((rev rev))
+      (lambda (files comment)
+        (message "Checking in %s..." (vc-delistify files))
+        ;; "This log message intentionally left almost blank".
+        ;; RCS 5.7 gripes about white-space-only comments too.
+        (or (and comment (string-match "[^\t\n ]" comment))
+            (setq comment "*** empty log message ***"))
+        (with-vc-properties
+            files
+          ;; We used to change buffers to get local value of
+          ;; vc-checkin-switches, but 'the' local buffer is
+          ;; not a well-defined concept for filesets.
+          (progn
+            (vc-call-backend backend 'checkin files rev comment)
+            (mapc 'vc-delete-automatic-version-backups files))
+          `((vc-state . up-to-date)
+            (vc-checkout-time . ,(nth 5 (file-attributes file)))
+            (vc-working-revision . nil)))
+        (message "Checking in %s...done" (vc-delistify files))))
     'vc-checkin-hook)))
 
 ;;; Additional entry points for examining version histories
@@ -1772,13 +1778,14 @@
   ;; case the more general operation ever becomes meaningful.
   (let ((backend (vc-responsible-backend (car files))))
     (vc-start-logentry
-     files rev oldcomment t
+     files oldcomment t
      "Enter a replacement change comment."
      "*VC-log*"
      (lambda () (vc-call-backend backend 'log-edit-mode))
-     (lambda (files rev comment ignored)
-       (vc-call-backend backend
-                        'modify-change-comment files rev comment)))))
+     (lexical-let ((rev rev))
+       (lambda (files comment)
+         (vc-call-backend backend
+                          'modify-change-comment files rev comment))))))
 
 ;;;###autoload
 (defun vc-merge ()
@@ -1839,6 +1846,31 @@
 ;;;###autoload
 (defalias 'vc-resolve-conflicts 'smerge-ediff)
 
+;; TODO: This is OK but maybe we could integrate it better.
+;; E.g. it could be run semi-automatically (via a prompt?) when saving a file
+;; that was conflicted (i.e. upon mark-resolved).
+;; FIXME: should we add an "other-window" version?  Or maybe we should
+;; hook it inside find-file so it automatically works for
+;; find-file-other-window as well.  E.g. find-file could use a new
+;; `default-next-file' variable for its default file (M-n), and
+;; we could then set it upon mark-resolve, so C-x C-s C-x C-f M-n would
+;; automatically offer the next conflicted file.
+(defun vc-find-conflicted-file ()
+  "Visit the next conflicted file in the current project."
+  (interactive)
+  (let* ((backend (or (if buffer-file-name (vc-backend buffer-file-name))
+                      (vc-responsible-backend default-directory)
+                      (error "No VC backend")))
+         (files (vc-call-backend backend
+                                 'conflicted-files default-directory)))
+    ;; Don't try and visit the current file.
+    (if (equal (car files) buffer-file-name) (pop files))
+    (if (null files)
+        (message "No more conflicted files")
+      (find-file (pop files))
+      (message "%s more conflicted files after this one"
+               (if files (length files) "No")))))
+
 ;; Named-configuration entry points
 
 (defun vc-tag-precondition (dir)