(with-current-buffer git-status-buffer
(git-refresh-status))))))
-(defun switch-to-stgit-buffer (dir)
- "Switch to a (possibly new) buffer displaying StGit patches for DIR."
+(defun stgit-find-buffer (dir)
+ "Return the buffer displaying StGit patches for DIR, or nil if none."
(setq dir (file-name-as-directory dir))
(let ((buffers (buffer-list)))
(while (and buffers
(and (eq major-mode 'stgit-mode)
(string= default-directory dir)))))
(setq buffers (cdr buffers)))
- (switch-to-buffer (if buffers
- (car buffers)
- (create-stgit-buffer dir)))))
+ (and buffers (car buffers))))
+
+(defun switch-to-stgit-buffer (dir)
+ "Switch to a (possibly new) buffer displaying StGit patches for DIR."
+ (setq dir (file-name-as-directory dir))
+ (let ((buffer (stgit-find-buffer dir)))
+ (switch-to-buffer (or buffer
+ (create-stgit-buffer dir)))))
+
(defstruct (stgit-patch)
status name desc empty files-ewoc)
(let ((status (stgit-patch-status patch))
(start (point))
(name (stgit-patch-name patch)))
- (insert (case status
- ('applied "+")
- ('top ">")
- ('unapplied "-")
- (t "ยท"))
- (if (memq name stgit-marked-patches)
- "*" " ")
- (propertize (format "%-30s" (symbol-name name))
- 'face (case status
- ('applied 'stgit-applied-patch-face)
- ('top 'stgit-top-patch-face)
- ('unapplied 'stgit-unapplied-patch-face)))
- " "
- (if (stgit-patch-empty patch) "(empty) " "")
- (propertize (or (stgit-patch-desc patch) "")
- 'face 'stgit-description-face))
+ (case name
+ (:index (insert (propertize " Index" 'face 'italic)))
+ (:work (insert (propertize " Work tree" 'face 'italic)))
+ (t (insert (case status
+ ('applied "+")
+ ('top ">")
+ ('unapplied "-"))
+ (if (memq name stgit-marked-patches)
+ "*" " ")
+ (propertize (format "%-30s"
+ (symbol-name name))
+ 'face (case status
+ ('applied 'stgit-applied-patch-face)
+ ('top 'stgit-top-patch-face)
+ ('unapplied 'stgit-unapplied-patch-face)
+ ('index nil)
+ ('work nil)))
+ " "
+ (if (stgit-patch-empty patch) "(empty) " "")
+ (propertize (or (stgit-patch-desc patch) "")
+ 'face 'stgit-description-face))))
(put-text-property start (point) 'entry-type 'patch)
(when (memq name stgit-expanded-patches)
(stgit-insert-patch-files patch))
(setq args (stgit-make-run-args args))
(apply 'call-process "git" nil standard-output nil args))
+(defun stgit-index-empty-p ()
+ "Returns non-nil if the index contains no changes from HEAD."
+ (zerop (stgit-run-git-silent "diff-index" "--cached" "--quiet" "HEAD")))
+
+(defvar stgit-index-node nil)
+(defvar stgit-worktree-node nil)
+
+(defun stgit-refresh-index ()
+ (when stgit-index-node
+ (ewoc-invalidate (car stgit-index-node) (cdr stgit-index-node))))
+
+(defun stgit-refresh-worktree ()
+ (when stgit-worktree-node
+ (ewoc-invalidate (car stgit-worktree-node) (cdr stgit-worktree-node))))
+
(defun stgit-run-series (ewoc)
(let ((first-line t))
(with-temp-buffer
:desc (match-string 5)
:empty (string= (match-string 1) "0"))))
(setq first-line nil)
- (forward-line 1)))))))
+ (forward-line 1)))))
+ (if stgit-show-worktree
+ (setq stgit-index-node (cons ewoc (ewoc-enter-last ewoc
+ (make-stgit-patch
+ :status 'index
+ :name :index
+ :desc nil
+ :empty nil)))
+ stgit-worktree-node (cons ewoc (ewoc-enter-last ewoc
+ (make-stgit-patch
+ :status 'work
+ :name :work
+ :desc nil
+ :empty nil))))
+ (setq stgit-worktree-node nil))))
(defun stgit-reload ()
(buffer-substring (point-min) (1- (point-max))))
'face 'bold)
"\n")
- "--")
+ (if stgit-show-worktree
+ "--"
+ (propertize
+ (substitute-command-keys "--\n\"\\[stgit-toggle-worktree]\"\
+ shows the working tree\n")
+ 'face 'stgit-description-face)))
(stgit-run-series stgit-ewoc)
(if curpatch
(stgit-goto-patch curpatch)
'file-data file))))
(defun stgit-insert-patch-files (patch)
- "Expand (show modification of) the patch with name PATCHSYM (a
-symbol) after the line at point.
-`stgit-expand-find-copies-harder' controls how hard to try to
-find copied files."
- (insert "\n")
+ "Expand (show modification of) the patch PATCH after the line
+at point."
(let* ((patchsym (stgit-patch-name patch))
(end (progn (insert "#") (prog1 (point-marker) (forward-char -1))))
- (args (list "-r" "-z" (if stgit-expand-find-copies-harder
- "--find-copies-harder"
- "-C")))
+ (args (list "-z" (if stgit-expand-find-copies-harder
+ "--find-copies-harder"
+ "-C")))
(ewoc (ewoc-create #'stgit-file-pp nil nil t)))
(setf (stgit-patch-files-ewoc patch) ewoc)
(with-temp-buffer
- (apply 'stgit-run-git "diff-tree"
- (append args (list (stgit-id patchsym))))
+ (apply 'stgit-run-git
+ (cond ((eq patchsym :work)
+ `("diff-files" ,@args))
+ ((eq patchsym :index)
+ `("diff-index" ,@args "--cached" "HEAD"))
+ (t
+ `("diff-tree" ,@args "-r" ,(stgit-id patchsym)))))
(goto-char (point-min))
- (forward-char 41)
+ (unless (or (eobp) (memq patchsym '(:work :index)))
+ (forward-char 41))
(while (looking-at ":\\([0-7]+\\) \\([0-7]+\\) [0-9A-Fa-f]\\{40\\} [0-9A-Fa-f]\\{40\\} ")
(let ((old-perm (string-to-number (match-string 1) 8))
(new-perm (string-to-number (match-string 2) 8)))
"Keymap for StGit major mode.")
(unless stgit-mode-map
- (setq stgit-mode-map (make-keymap))
- (suppress-keymap stgit-mode-map)
- (mapc (lambda (arg) (define-key stgit-mode-map (car arg) (cdr arg)))
- '((" " . stgit-mark)
- ("m" . stgit-mark)
- ("\d" . stgit-unmark-up)
- ("u" . stgit-unmark-down)
- ("?" . stgit-help)
- ("h" . stgit-help)
- ("\C-p" . stgit-previous-line)
- ("\C-n" . stgit-next-line)
- ([up] . stgit-previous-line)
- ([down] . stgit-next-line)
- ("p" . stgit-previous-patch)
- ("n" . stgit-next-patch)
- ("\M-{" . stgit-previous-patch)
- ("\M-}" . stgit-next-patch)
- ("s" . stgit-git-status)
- ("g" . stgit-reload)
- ("r" . stgit-refresh)
- ("\C-c\C-r" . stgit-rename)
- ("e" . stgit-edit)
- ("M" . stgit-move-patches)
- ("S" . stgit-squash)
- ("N" . stgit-new)
- ("R" . stgit-repair)
- ("C" . stgit-commit)
- ("U" . stgit-uncommit)
- ("\r" . stgit-select)
- ("o" . stgit-find-file-other-window)
- (">" . stgit-push-next)
- ("<" . stgit-pop-next)
- ("P" . stgit-push-or-pop)
- ("G" . stgit-goto)
- ("=" . stgit-show)
- ("D" . stgit-delete)
- ([(control ?/)] . stgit-undo)
- ("\C-_" . stgit-undo)
- ("B" . stgit-branch)
- ("q" . stgit-quit))))
+ (let ((toggle-map (make-keymap)))
+ (suppress-keymap toggle-map)
+ (mapc (lambda (arg) (define-key toggle-map (car arg) (cdr arg)))
+ '(("t" . stgit-toggle-worktree)))
+ (setq stgit-mode-map (make-keymap))
+ (suppress-keymap stgit-mode-map)
+ (mapc (lambda (arg) (define-key stgit-mode-map (car arg) (cdr arg)))
+ `((" " . stgit-mark)
+ ("m" . stgit-mark)
+ ("\d" . stgit-unmark-up)
+ ("u" . stgit-unmark-down)
+ ("?" . stgit-help)
+ ("h" . stgit-help)
+ ("\C-p" . stgit-previous-line)
+ ("\C-n" . stgit-next-line)
+ ([up] . stgit-previous-line)
+ ([down] . stgit-next-line)
+ ("p" . stgit-previous-patch)
+ ("n" . stgit-next-patch)
+ ("\M-{" . stgit-previous-patch)
+ ("\M-}" . stgit-next-patch)
+ ("s" . stgit-git-status)
+ ("g" . stgit-reload)
+ ("r" . stgit-refresh)
+ ("\C-c\C-r" . stgit-rename)
+ ("e" . stgit-edit)
+ ("M" . stgit-move-patches)
+ ("S" . stgit-squash)
+ ("N" . stgit-new)
+ ("R" . stgit-repair)
+ ("C" . stgit-commit)
+ ("U" . stgit-uncommit)
+ ("\r" . stgit-select)
+ ("o" . stgit-find-file-other-window)
+ ("i" . stgit-file-toggle-index)
+ (">" . stgit-push-next)
+ ("<" . stgit-pop-next)
+ ("P" . stgit-push-or-pop)
+ ("G" . stgit-goto)
+ ("=" . stgit-show)
+ ("D" . stgit-delete)
+ ([(control ?/)] . stgit-undo)
+ ("\C-_" . stgit-undo)
+ ("B" . stgit-branch)
+ ("t" . ,toggle-map)
+ ("q" . stgit-quit)))))
(defun stgit-mode ()
"Major mode for interacting with StGit.
(set (make-local-variable 'list-buffers-directory) default-directory)
(set (make-local-variable 'stgit-marked-patches) nil)
(set (make-local-variable 'stgit-expanded-patches) nil)
+ (set (make-local-variable 'stgit-show-worktree) stgit-default-show-worktree)
(set-variable 'truncate-lines 't)
+ (add-hook 'after-save-hook 'stgit-update-saved-file)
(run-hooks 'stgit-mode-hook))
+(defun stgit-update-saved-file ()
+ (let* ((file (expand-file-name buffer-file-name))
+ (dir (file-name-directory file))
+ (gitdir (condition-case nil (git-get-top-dir dir)
+ (error nil)))
+ (buffer (and gitdir (stgit-find-buffer gitdir))))
+ (when buffer
+ (with-current-buffer buffer
+ (stgit-refresh-worktree)))))
+
(defun stgit-add-mark (patchsym)
"Mark the patch PATCHSYM."
(setq stgit-marked-patches (cons patchsym stgit-marked-patches)))
(if stgit-expand-find-copies-harder
'("--find-copies-harder")
'("-C")))
- (list (concat patch-id "^") patch-id)
+ (cond ((eq patch-id :index)
+ '("--cached"))
+ ((eq patch-id :work)
+ nil)
+ (t
+ (list (concat patch-id "^") patch-id)))
'("--")
(if (stgit-file-copy-or-rename patched-file)
(list (stgit-file-cr-from patched-file)
(goto-char (point-min))
(diff-mode))))
+(defun stgit-move-change-to-index (file)
+ "Copies the workspace state of FILE to index, using git add or git rm"
+ (let ((op (if (or (file-exists-p file) (file-symlink-p file))
+ '("add") '("rm" "-q"))))
+ (stgit-capture-output "*git output*"
+ (apply 'stgit-run-git (append op '("--") (list file))))))
+
+(defun stgit-remove-change-from-index (file)
+ "Unstages the change in FILE from the index"
+ (stgit-capture-output "*git output*"
+ (stgit-run-git "reset" "-q" "--" file)))
+
+(defun stgit-file-toggle-index ()
+ "Move modified file in or out of the index."
+ (interactive)
+ (let ((patched-file (stgit-patched-file-at-point)))
+ (unless patched-file
+ (error "No file on the current line"))
+ (let ((patch-name (stgit-patch-name-at-point)))
+ (cond ((eq patch-name :work)
+ (stgit-move-change-to-index (stgit-file-file patched-file)))
+ ((eq patch-name :index)
+ (stgit-remove-change-from-index (stgit-file-file patched-file)))
+ (t
+ (error "Can only move files in the working tree to index")))))
+ (stgit-refresh-worktree)
+ (stgit-refresh-index))
+
(defun stgit-edit ()
"Edit the patch on the current line."
(interactive)
(stgit-refresh-git-status))
(stgit-reload))
+(defcustom stgit-default-show-worktree
+ nil
+ "Set to non-nil to by default show the working tree in a new stgit buffer.
+
+This value is used as the default value for `stgit-show-worktree'."
+ :type 'boolean
+ :group 'stgit)
+
+(defvar stgit-show-worktree nil
+ "Show work tree and index in the stgit buffer.
+
+See `stgit-default-show-worktree' for its default value.")
+
+(defun stgit-toggle-worktree (&optional arg)
+ "Toggle the visibility of the work tree.
+With arg, show the work tree if arg is positive.
+
+Its initial setting is controlled by `stgit-default-show-worktree'."
+ (interactive)
+ (setq stgit-show-worktree
+ (if (numberp arg)
+ (> arg 0)
+ (not stgit-show-worktree)))
+ (stgit-reload))
+
(provide 'stgit)