unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Updating *vc-dir* marks from *VC-log*
@ 2010-11-21  0:58 Bob Rogers
  2010-11-21  4:46 ` Dan Nicolaescu
  2010-11-21 18:43 ` Stefan Monnier
  0 siblings, 2 replies; 8+ messages in thread
From: Bob Rogers @ 2010-11-21  0:58 UTC (permalink / raw)
  To: emacs-devel

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



^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2010-11-23 17:06 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-11-21  0:58 Updating *vc-dir* marks from *VC-log* Bob Rogers
2010-11-21  4:46 ` Dan Nicolaescu
2010-11-21 16:14   ` Bob Rogers
2010-11-21 17:02     ` Dan Nicolaescu
2010-11-21 18:30       ` Bob Rogers
2010-11-23 17:06         ` Dan Nicolaescu
2010-11-21 18:43 ` Stefan Monnier
2010-11-21 20:46   ` Bob Rogers

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).