From ea6105fbbf390c02290a47111237c51115093923 Mon Sep 17 00:00:00 2001 From: Marius Vollmer Date: Tue, 28 Jul 2009 02:25:44 +0300 Subject: [PATCH] Support for magit-repo-dirs and nice completion based on it. Thanks to Travis B. Hartwell for the initial implementation! --- NEWS | 7 +++++ magit.el | 76 +++++++++++++++++++++++++++++++++++++++++++++++++----- magit.texi | 22 +++++++++++++++- 3 files changed, 97 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index bc41b25c..9906dac4 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,12 @@ Changes in magit 0.8: +* By setting magit-repo-dirs, you can get better repo completion. + Magit will offer all subdirectories (upto magit-repo-dirs level + deep) of the listed directories when magit-status asks for a + directory to work on. + + You can get the old behavior with a double prefix to magit-status. + * Hitting 'c' or 'C' while resolving a conflict in the middle of a rebase will offer to continue the rebase instead of trying to commit your changes. diff --git a/magit.el b/magit.el index 253aa504..00d4b5b4 100644 --- a/magit.el +++ b/magit.el @@ -68,6 +68,17 @@ :group 'magit :type '(repeat string)) +(defcustom magit-repo-dirs nil + "Directories containing Git repositories. +Magit will look into these directories for Git repositories and offers them as choices for magit-status." + :group 'magit + :type '(repeat string)) + +(defcustom magit-repo-dirs-depth 3 + "When looking for Git repositors below the directories in magit-repo-dirs, Magit will only descend this many levels deep." + :group 'magit + :type 'integer) + (defcustom magit-save-some-buffers t "Non-nil means that \\[magit-status] will save modified buffers before running. Setting this to t will ask which buffers to save, setting it to 'dontask will @@ -272,6 +283,51 @@ Many Magit faces inherit from this one by default." (magit-git-string "config" (magit-concat-with-delim "." keys) val) (magit-git-string "config" "--unset" (magit-concat-with-delim "." keys)))) +(defun magit-remove-conflicts (alist) + (let ((dict (make-hash-table :test 'equal)) + (result nil)) + (dolist (a alist) + (puthash (car a) (cons (cdr a) (gethash (car a) dict)) + dict)) + (maphash (lambda (key value) + (if (= (length value) 1) + (push (cons key (car value)) result) + (let ((sub (magit-remove-conflicts + (mapcar (lambda (entry) + (let ((dir (directory-file-name + (subseq entry 0 (- (length key)))))) + (cons (concat (file-name-nondirectory dir) "/" key) + entry))) + value)))) + (setq result (append result sub))))) + dict) + result)) + +(defun magit-git-repo-p (dir) + (file-exists-p (expand-file-name ".git" dir))) + +(defun magit-list-repos* (dir level) + (if (magit-git-repo-p dir) + (list dir) + (apply #'append + (mapcar (lambda (entry) + (unless (or (string= (substring entry -3) "/..") + (string= (substring entry -2) "/.")) + (magit-list-repos* entry (+ level 1)))) + (and (file-directory-p dir) + (< level magit-repo-dirs-depth) + (directory-files dir t nil t)))))) + +(defun magit-list-repos (dirs) + (magit-remove-conflicts + (apply #'append + (mapcar (lambda (dir) + (mapcar #'(lambda (repo) + (cons (file-name-nondirectory repo) + repo)) + (magit-list-repos* dir 0))) + dirs)))) + (defun magit-get-top-dir (cwd) (let ((cwd (expand-file-name cwd))) (and (file-directory-p cwd) @@ -295,11 +351,17 @@ Many Magit faces inherit from this one by default." (defun magit-ref-exists-p (ref) (= (magit-git-exit-code "show-ref" "--verify" ref) 0)) -(defun magit-read-top-dir () - (file-name-as-directory - (read-directory-name "Git repository: " - (or (magit-get-top-dir default-directory) - default-directory)))) +(defun magit-read-top-dir (rawp) + (if (and (not rawp) magit-repo-dirs) + (let* ((repos (magit-list-repos magit-repo-dirs)) + (reply (completing-read "Git repository: " + (magit-list-repos magit-repo-dirs)))) + (file-name-as-directory + (cdr (assoc reply repos)))) + (file-name-as-directory + (read-directory-name "Git repository: " + (or (magit-get-top-dir default-directory) + default-directory))))) (defun magit-name-rev (rev) (and rev @@ -1927,7 +1989,8 @@ in log buffer." (defun magit-status (dir) (interactive (list (or (and (not current-prefix-arg) (magit-get-top-dir default-directory)) - (magit-read-top-dir)))) + (magit-read-top-dir (and (listp current-prefix-arg) + (> (car current-prefix-arg) 4)))))) (if magit-save-some-buffers (save-some-buffers (eq magit-save-some-buffers 'dontask))) (let ((topdir (magit-get-top-dir dir))) @@ -1946,7 +2009,6 @@ in log buffer." (switch-to-buffer buf) (magit-mode-init topdir 'status #'magit-refresh-status))))) - ;;; Staging and Unstaging (defun magit-stage-item () diff --git a/magit.texi b/magit.texi index 89d712a6..7fa1b500 100644 --- a/magit.texi +++ b/magit.texi @@ -166,8 +166,28 @@ will switch to the status buffer of that repository. Otherwise, it will prompt for a directory. With a prefix argument, it will always prompt. +You can set @code{magit-repo-dirs} to customize how +@code{magit-status} asks for the repository to work on. When +@code{magit-repo-dirs} is nil, @code{magit-status} will simply ask for +a directory. + If you specify a directory that is not a Git repository, @kbd{M-x -magit-status} will ask whether to create one. +magit-status} will offer to initialize it as one. + +When @code{magit-repo-dirs} is not nil, it is treated as a list of +directory names, and @code{magit-status} will find all Git +repositories in those directories and offer them for completion. +(Magit will only look @code{magit-repo-dirs-depth} levels deep, +however.) + +With two prefix arguments, @code{magit-status} will always prompt for +a raw directory. + +Thus, you would normally set @code{magit-repo-dirs} to the places +where you keep most of your Git repositories and switch between them +with @kbd{C-u M-x magit-status}. If you want to go to a repository +outside of your normal working areas, or if you want to create a new +repository, you would use @kbd{C-u C-u M-x magit-status}. You need to explicitly refresh the status buffer when you have made changes to the repository from outside of Emacs. You can type @kbd{g}