X-Git-Url: https://git.distorted.org.uk/~mdw/stgit/blobdiff_plain/4f7ff561b78525e5cca8c09b188854c219b8903b..a9089e68c3ff8943103a31f044c00cdab51c4674:/contrib/stgit.el diff --git a/contrib/stgit.el b/contrib/stgit.el index 078e46f..0d99467 100644 --- a/contrib/stgit.el +++ b/contrib/stgit.el @@ -64,34 +64,26 @@ directory DIR or `default-directory'" status name desc empty files-ewoc) (defun stgit-patch-pp (patch) - (let ((status (stgit-patch-status patch)) - (start (point)) - (name (stgit-patch-name patch))) - (case name - (:index (insert " " - (propertize "Index" - 'face 'stgit-index-work-tree-title-face))) - (:work (insert " " - (propertize "Work tree" - 'face 'stgit-index-work-tree-title-face))) - (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)))) + (let* ((status (stgit-patch-status patch)) + (start (point)) + (name (stgit-patch-name patch)) + (face (cdr (assq status stgit-patch-status-face-alist)))) + (insert (case status + ('applied "+") + ('top ">") + ('unapplied "-") + (t " ")) + (if (memq name stgit-marked-patches) + "*" " ")) + (if (memq status '(index work)) + (insert (propertize (if (eq status 'index) "Index" "Work tree") + 'face face)) + (insert (format "%-30s" + (propertize (symbol-name name) 'face face)) + " " + (if (stgit-patch-empty patch) "(empty) " "") + (propertize (or (stgit-patch-desc patch) "") + 'face 'stgit-description-face))) (insert "\n") (put-text-property start (point) 'entry-type 'patch) (when (memq name stgit-expanded-patches) @@ -250,7 +242,8 @@ Returns nil if there was no output." (interactive) (let ((inhibit-read-only t) (curline (line-number-at-pos)) - (curpatch (stgit-patch-name-at-point))) + (curpatch (stgit-patch-name-at-point)) + (curfile (stgit-patched-file-at-point))) (ewoc-filter stgit-ewoc #'(lambda (x) nil)) (ewoc-set-hf stgit-ewoc (concat "Branch: " @@ -268,7 +261,7 @@ Returns nil if there was no output." 'face 'stgit-description-face))) (stgit-run-series stgit-ewoc) (if curpatch - (stgit-goto-patch curpatch) + (stgit-goto-patch curpatch (and curfile (stgit-file-file curfile))) (goto-line curline))) (stgit-refresh-git-status)) @@ -326,6 +319,11 @@ Returns nil if there was no output." "StGit mode face used for unknown file status" :group 'stgit) +(defface stgit-ignored-file-face + '((((class color) (background light)) (:foreground "grey60")) + (((class color) (background dark)) (:foreground "grey40"))) + "StGit mode face used for ignored files") + (defface stgit-file-permission-face '((((class color) (background light)) (:foreground "green" :bold t)) (((class color) (background dark)) (:foreground "green" :bold t))) @@ -359,9 +357,18 @@ flag, which reduces performance." (rename "Renamed" stgit-modified-file-face) (mode-change "Mode change" stgit-modified-file-face) (unmerged "Unmerged" stgit-unmerged-file-face) - (unknown "Unknown" stgit-unknown-file-face))) + (unknown "Unknown" stgit-unknown-file-face) + (ignore "Ignored" stgit-ignored-file-face))) "Alist of code symbols to description strings") +(defconst stgit-patch-status-face-alist + '((applied . stgit-applied-patch-face) + (top . stgit-top-patch-face) + (unapplied . stgit-unapplied-patch-face) + (index . stgit-index-work-tree-title-face) + (work . stgit-index-work-tree-title-face)) + "Alist of face to use for a given patch status") + (defun stgit-file-status-code-as-string (file) "Return stgit status code for FILE as a string" (let* ((code (assq (stgit-file-status file) @@ -380,6 +387,7 @@ flag, which reduces performance." (let ((code (assoc str '(("A" . add) ("C" . copy) ("D" . delete) + ("I" . ignore) ("M" . modify) ("R" . rename) ("T" . mode-change) @@ -479,13 +487,24 @@ Cf. `stgit-file-type-change-string'." "--find-copies-harder" "-C")) +(defun stgit-insert-ls-files (args file-flag) + (let ((start (point))) + (apply 'stgit-run-git + (append '("ls-files" "--exclude-standard" "-z") args)) + (goto-char start) + (while (looking-at "\\([^\0]*\\)\0") + (let ((name-len (- (match-end 0) (match-beginning 0)))) + (insert ":0 0 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 " file-flag "\0") + (forward-char name-len))))) + (defun stgit-insert-patch-files (patch) "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 "-z" (stgit-find-copies-harder-diff-arg))) - (ewoc (ewoc-create #'stgit-file-pp nil nil t))) + (end (point-marker)) + (args (list "-z" (stgit-find-copies-harder-diff-arg))) + (ewoc (ewoc-create #'stgit-file-pp nil nil t))) + (set-marker-insertion-type end t) (setf (stgit-patch-files-ewoc patch) ewoc) (with-temp-buffer (apply 'stgit-run-git @@ -495,6 +514,15 @@ at point." `("diff-index" ,@args "--cached" "HEAD")) (t `("diff-tree" ,@args "-r" ,(stgit-id patchsym))))) + + (when (and (eq patchsym :work)) + (when stgit-show-ignored + (stgit-insert-ls-files '("--ignored" "--others") "I")) + (when stgit-show-unknown + (stgit-insert-ls-files '("--others") "X")) + (sort-regexp-fields nil ":[^\0]*\0\\([^\0]*\\)\0" "\\1" + (point-min) (point-max))) + (goto-char (point-min)) (unless (or (eobp) (memq patchsym '(:work :index))) (forward-char 41)) @@ -534,10 +562,12 @@ at point." (ewoc-enter-last ewoc file)) (goto-char (match-end 0)))) (unless (ewoc-nth ewoc 0) - (ewoc-set-hf ewoc "" (propertize " \n" - 'face 'stgit-description-face)))) - (goto-char end) - (delete-char -1))) + (ewoc-set-hf ewoc "" + (concat " " + (propertize "" + 'face 'stgit-description-face) + "\n")))) + (goto-char end))) (defun stgit-select-file () (let ((filename (expand-file-name @@ -635,7 +665,9 @@ file for (applied) copies and renames." (let ((toggle-map (make-keymap))) (suppress-keymap toggle-map) (mapc (lambda (arg) (define-key toggle-map (car arg) (cdr arg))) - '(("t" . stgit-toggle-worktree))) + '(("t" . stgit-toggle-worktree) + ("i" . stgit-toggle-ignored) + ("u" . stgit-toggle-unknown))) (setq stgit-mode-map (make-keymap)) (suppress-keymap stgit-mode-map) (mapc (lambda (arg) (define-key stgit-mode-map (car arg) (cdr arg))) @@ -752,13 +784,26 @@ index or work tree." (list patch) '())))) -(defun stgit-goto-patch (patchsym) +(defun stgit-goto-patch (patchsym &optional file) "Move point to the line containing patch PATCHSYM. -If that patch cannot be found, do nothing." +If that patch cannot be found, do nothing. + +If the patch was found and FILE is not nil, instead move to that +file's line. If FILE cannot be found, stay on the line of +PATCHSYM." (let ((node (ewoc-nth stgit-ewoc 0))) (while (and node (not (eq (stgit-patch-name (ewoc-data node)) patchsym))) (setq node (ewoc-next stgit-ewoc node))) + (when (and node file) + (let* ((file-ewoc (stgit-patch-files-ewoc (ewoc-data node))) + (file-node (ewoc-nth file-ewoc 0))) + (while (and file-node (not (equal (stgit-file-file (ewoc-data file-node)) file))) + (setq file-node (ewoc-next file-ewoc file-node))) + (when file-node + (ewoc-goto-node file-ewoc file-node) + (move-to-column (stgit-goal-column)) + (setq node nil)))) (when node (ewoc-goto-node stgit-ewoc node) (move-to-column goal-column)))) @@ -883,6 +928,12 @@ working tree." (unless (memq patch-name '(:work :index)) (error "No index or working tree file on this line")) + (when (eq file-status 'ignore) + (error "Cannot revert ignored files")) + + (when (eq file-status 'unknown) + (error "Cannot revert unknown files")) + (let ((nfiles (+ (if rm-file 1 0) (if co-file 1 0)))) (when (yes-or-no-p (format "Revert %d file%s? " nfiles @@ -1022,22 +1073,48 @@ If PATCHSYM is a keyword, returns PATCHSYM unmodified." (stgit-run-git "reset" "-q" "--" file))) (defun stgit-file-toggle-index () - "Move modified file in or out of the index." + "Move modified file in or out of the index. + +Leaves the point where it is, but moves the mark to where the +file ended up. You can then jump to the file with \ +\\[exchange-point-and-mark]." (interactive) (let ((patched-file (stgit-patched-file-at-point))) (unless patched-file (error "No file on the current line")) (when (eq (stgit-file-status patched-file) 'unmerged) (error (substitute-command-keys "Use \\[stgit-resolve-file] to move an unmerged file to the index"))) - (let ((patch-name (stgit-patch-name-at-point))) + (when (eq (stgit-file-status patched-file) 'ignore) + (error "You cannot add ignored files to the index")) + (let* ((patch (stgit-patch-at-point)) + (patch-name (stgit-patch-name patch)) + (old-point (point)) + next-file) + + ;; find the next file in the patch, or the previous one if this + ;; was the last file + (and (zerop (forward-line 1)) + (let ((f (stgit-patched-file-at-point))) + (and f (setq next-file (stgit-file-file f))))) + (goto-char old-point) + (unless next-file + (and (zerop (forward-line -1)) + (let ((f (stgit-patched-file-at-point))) + (and f (setq next-file (stgit-file-file f))))) + (goto-char old-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)) + (error "Can only move files in the working tree to index"))) + (stgit-refresh-worktree) + (stgit-refresh-index) + (stgit-goto-patch (if (eq patch-name :index) :work :index) + (stgit-file-file patched-file)) + (push-mark nil t t) + (stgit-goto-patch patch-name next-file)))) (defun stgit-edit () "Edit the patch on the current line." @@ -1307,6 +1384,12 @@ This value is used as the default value for `stgit-show-worktree'." See also `stgit-show-worktree-mode'.") +(defvar stgit-show-ignored nil + "If nil, inhibit showing files ignored by git.") + +(defvar stgit-show-unknown nil + "If nil, inhibit showing files not registered with git.") + (defun stgit-toggle-worktree (&optional arg) "Toggle the visibility of the work tree. With arg, show the work tree if arg is positive. @@ -1322,4 +1405,28 @@ work tree will show up." (not stgit-show-worktree))) (stgit-reload)) +(defun stgit-toggle-ignored (&optional arg) + "Toggle the visibility of files ignored by git in the work +tree. With ARG, show these files if ARG is positive. + +Use \\[stgit-toggle-worktree] to show the work tree." + (interactive) + (setq stgit-show-ignored + (if (numberp arg) + (> arg 0) + (not stgit-show-ignored))) + (stgit-reload)) + +(defun stgit-toggle-unknown (&optional arg) + "Toggle the visibility of files not registered with git in the +work tree. With ARG, show these files if ARG is positive. + +Use \\[stgit-toggle-worktree] to show the work tree." + (interactive) + (setq stgit-show-unknown + (if (numberp arg) + (> arg 0) + (not stgit-show-unknown))) + (stgit-reload)) + (provide 'stgit)