[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Updating *vc-dir* marks from *VC-log*
From: |
Bob Rogers |
Subject: |
Updating *vc-dir* marks from *VC-log* |
Date: |
Sat, 20 Nov 2010 19:58:00 -0500 |
Perhaps I'm slow, but I only just noticed that it is possible to work
on multiple VC commits for the same working copy in parallel, just by
renaming *VC-log* buffers. This is cool; it perfectly suits my working
style. I would like to write it up for the Emacs manual so that others
can find it.
But it lacks something. I often start a commit, then realize that
I've left out a file or two, or maybe included one that ought to be
committed separately. When doing one commit at a time, this is a simple
matter of returning to vc-dir, updating the set, and doing "C-x v v"
again. With multiple pending commits, what is needed is a way to return
to vc-dir and "swap in" the log buffer fileset. The patch below adds a
new log-edit-visit-files-in-vc-dir command to do this. I have
tentatively bound it to "C-c @" in log-edit-mode, which I hope is
sufficiently mnemonic for "mark".
The only problem is that "C-x v v" doesn't know to which of several
log buffers it should return from *vc-dir*; it always picks *VC-log*,
creating a new one if necessary. I could hack vc-checkin, but one would
still need to be able to specify when to go back to the relevant log
buffer, and when to create a new one.
Comments?
-- Bob Rogers
http://www.rgrjr.com/
------------------------------------------------------------------------
diff --git a/lisp/vc/log-edit.el b/lisp/vc/log-edit.el
index 27290ee..726a074 100644
--- a/lisp/vc/log-edit.el
+++ b/lisp/vc/log-edit.el
@@ -57,6 +57,7 @@
("\C-c\C-a" . log-edit-insert-changelog)
("\C-c\C-d" . log-edit-show-diff)
("\C-c\C-f" . log-edit-show-files)
+ ("\C-c@" . log-edit-visit-files-in-vc-dir)
("\M-n" . log-edit-next-comment)
("\M-p" . log-edit-previous-comment)
("\M-r" . log-edit-comment-search-backward)
@@ -533,6 +534,40 @@ If you want to abort the commit, simply delete the buffer."
(shrink-window-if-larger-than-buffer)
(selected-window)))))
+(defun log-edit-visit-files-in-vc-dir ()
+ "Switch to vc-dir, marking the list of files to be committed."
+ (interactive)
+ (let ((files (mapcar 'expand-file-name (log-edit-files)))
+ (buffers nil))
+ ;; Find the set of possible vc-dir buffers.
+ (let ((tail files))
+ (while tail
+ (let* ((buf-and-node (vc-dir-find-buffer-for-file (car tail)))
+ (buffer (car buf-and-node)))
+ (message "got %S for %S" buf-and-node (car tail))
+ (if (and buffer
+ (not (member buffer buffers)))
+ (setq buffers (cons buffer buffers))))
+ (setq tail (cdr tail))))
+ (cond ((null buffers)
+ ;; Totally failed, so offer to start vc-dir.
+ (call-interactively 'vc-dir))
+ ((not (null (cdr buffers)))
+ ;; [not sure what to do here. -- rgr, 20-Nov-10.]
+ (error "Oops; got buffers %S" buffers))
+ (t
+ ;; Single-buffer case.
+ (switch-to-buffer-other-window (car buffers))
+ ;; [should we go to one of these files? should it be
+ ;; the first, or the last? -- rgr, 20-Nov-10.]
+ (let* ((n-files (length files))
+ (n-marked (vc-dir-set-marked-files files))
+ (plural (if (= n-files 1) "" "s")))
+ (if (= n-files n-marked)
+ (message "Marked %d file%s." n-files plural)
+ (message "Marked %d out of %d file%s."
+ n-marked n-files plural)))))))
+
(defun log-edit-insert-cvs-template ()
"Insert the template specified by the CVS administrator, if any.
This simply uses the local CVS/Template file."
diff --git a/lisp/vc/vc-dir.el b/lisp/vc/vc-dir.el
index 4397251..556c612 100644
--- a/lisp/vc/vc-dir.el
+++ b/lisp/vc/vc-dir.el
@@ -714,6 +714,31 @@ that share the same state."
(interactive "e")
(vc-dir-at-event e (vc-dir-mark-unmark 'vc-dir-toggle-mark-file)))
+(defun vc-dir-set-marked-files (files)
+ ;; Given a list of files, mark each file that appears in the list,
+ ;; and unmark all the others. No mention is made if a file appears
+ ;; in the list that does not appear in the vc-dir buffer, but the
+ ;; number of files actually marked is returned.
+ (let ((crt (ewoc-nth vc-ewoc 0)) (n-files-marked 0))
+ (while crt
+ (let* ((file (ewoc-data crt))
+ (new-mark-state
+ (cond ((vc-dir-fileinfo->directory file)
+ ;; Always unmark the directories.
+ nil)
+ ((let ((nodefile (vc-dir-fileinfo->name file)))
+ ;; (message "[checking %S]" nodefile)
+ (member (expand-file-name nodefile) files))
+ t))))
+ (unless (eq (not new-mark-state)
+ (not (vc-dir-fileinfo->marked file)))
+ (setf (vc-dir-fileinfo->marked file) new-mark-state)
+ (ewoc-invalidate vc-ewoc crt)
+ (when new-mark-state
+ (incf n-files-marked))))
+ (setq crt (ewoc-next vc-ewoc crt)))
+ n-files-marked))
+
(defun vc-dir-delete-file ()
"Delete the marked files, or the current file if no marks."
(interactive)
@@ -886,6 +911,45 @@ If it is a file, return the corresponding cons for the
file itself."
fileentries))
(vc-dir-update fileentries (current-buffer)))))
+(defun vc-dir-find-buffer-for-file (file)
+ ;; Look for a vc-dir buffer that includes file, or one that might
+ ;; contain file if it were visible. Returns nil if no such buffer
+ ;; was found, or a list of (buffer node) where node might be nil if
+ ;; file is not visible.
+ (message "checking %S for file %S" vc-dir-buffers file)
+ (let ((vc-dir-buffer nil) (vc-dir-default-directory-len 0)
+ (vc-dir-node nil) (tail vc-dir-buffers))
+ (while tail
+ (if (buffer-live-p (car tail))
+ (with-current-buffer (car tail)
+ ;; Search for a node for file.
+ (let ((node (ewoc-nth vc-ewoc 0))
+ (dd-len (length default-directory)))
+ (while node
+ (let ((nodefile (vc-dir-fileinfo->name (ewoc-data node))))
+ (if (string-equal (expand-file-name nodefile)
+ file)
+ ;; Success.
+ (setq vc-dir-buffer (current-buffer)
+ vc-dir-node node
+ tail nil node nil)))
+ (setq node (ewoc-next vc-ewoc node)))
+ ;; If not found, but the directory is a prefix of
+ ;; file, then remember the buffer as a fallback.
+ (if (and (null vc-dir-node)
+ (> (length file) dd-len)
+ (string-equal (substring file 0 dd-len)
+ default-directory)
+ ;; When we have multiple candidates, pick the one
+ ;; deeper in the directory hierarchy.
+ (or (null vc-dir-buffer)
+ (> dd-len vc-dir-default-directory-len)))
+ (setq vc-dir-buffer (current-buffer)
+ vc-dir-default-directory-len dd-len)))))
+ (setq tail (cdr tail)))
+ (and vc-dir-buffer
+ (list vc-dir-buffer vc-dir-node))))
+
(defun vc-dir-resynch-file (&optional fname)
"Update the entries for FNAME in any directory buffers that list it."
(let ((file (or fname (expand-file-name buffer-file-name)))
- Updating *vc-dir* marks from *VC-log*,
Bob Rogers <=
Re: Updating *vc-dir* marks from *VC-log*, Stefan Monnier, 2010/11/21