diff --git a/extensions/legit/legit-common.lisp b/extensions/legit/legit-common.lisp index 338503891..ce97ec947 100644 --- a/extensions/legit/legit-common.lisp +++ b/extensions/legit/legit-common.lisp @@ -5,6 +5,7 @@ (:export :legit-status :*prompt-for-commit-abort-p* :*ignore-all-space* + :*show-stashes* :*vcs-existence-order* :*peek-legit-keymap* :peek-legit-discard-file diff --git a/extensions/legit/legit.lisp b/extensions/legit/legit.lisp index 5541caa89..89fccfa33 100644 --- a/extensions/legit/legit.lisp +++ b/extensions/legit/legit.lisp @@ -44,6 +44,9 @@ Ongoing: Currently Git-only. Concretely, this calls Git with the -w option.") +(defvar *show-stashes* t "List stashes on the Legit status buffer.") + + ;; Supercharge patch-mode with our keys. (define-major-mode legit-diff-mode lem-patch-mode:patch-mode (:name "legit-diff" @@ -103,6 +106,10 @@ Currently Git-only. Concretely, this calls Git with the -w option.") (define-key *peek-legit-keymap* "r c" 'rebase-continue) (define-key *peek-legit-keymap* "r s" 'rebase-skip) +;; Stashes +(define-key *peek-legit-keymap* "z z" 'legit-stash-push) +(define-key *peek-legit-keymap* "z p" 'legit-stash-pop) + ;; redraw everything: (define-key *peek-legit-keymap* "g" 'legit-status) @@ -466,7 +473,7 @@ Currently Git-only. Concretely, this calls Git with the -w option.") (define-command legit-status () () - "Show changes, untracked files and latest commits in an interactive window." + "Show changes, untracked files, stashes and latest commits in an interactive window." (with-current-project (vcs) (multiple-value-bind (untracked-files unstaged-files staged-files) (lem/porcelain:components vcs) @@ -503,6 +510,15 @@ Currently Git-only. Concretely, this calls Git with the -w option.") (insert-string point file :attribute 'filename-attribute :read-only t))) (collector-insert "")) + + ;; Stashes. + (collector-insert "") + (let ((stashes (lem/porcelain:stash-list vcs))) + (collector-insert (format nil "Stashes (~a)" (length stashes)) :header t) + (when *show-stashes* + (loop for line in stashes + do (collector-insert line)))) + ;; Unstaged changes (collector-insert "") (collector-insert (format nil "Unstaged changes (~a):" (length unstaged-files)) :header t) @@ -748,6 +764,21 @@ Currently Git-only. Concretely, this calls Git with the -w option.") commits-per-page))) (display-commits-log vcs last-page-offset)))) +(define-command legit-stash-push () () + "Ask for a message and stash the current changes." + (with-current-project (vcs) + (let ((message (prompt-for-string "Stash message: "))) + (lem/porcelain::stash-push vcs :message message) + (legit-status)))) + +(define-command legit-stash-pop () () + "Pop the latest staged changes" + (with-current-project (vcs) + (let ((confirm (prompt-for-y-or-n-p "Pop the latest stash to the current branch? "))) + (when confirm + (lem/porcelain::stash-pop vcs) + (legit-status))))) + (define-command legit-quit () () "Quit" (%legit-quit) @@ -767,10 +798,12 @@ Currently Git-only. Concretely, this calls Git with the -w option.") (format s "(b)ranches-> checkout another (b)ranch.~&") (format s " -> (c)reate.~&") (format s "(l)og-> (l) commits log~&") - (format s " -> (F) first page of the commits history~&") + (format s " -> (F) first page of the commits history.~&") + (format s " Navigate commit pages with (b) and (f).~&") (format s "(F)etch, pull-> (p) from remote branch~&") (format s "(P)push -> (p) to remote branch~&") (format s "(r)ebase -> (i)nteractively from commit at point, (a)bort~&") + (format s "(z) stashes -> (z) stash changes (p)op latest stash~&") (format s "(g) -> refresh~&") (format s "~%") (format s "Navigate: n and p, C-n and C-p, M-n and M-p.~&") @@ -781,6 +814,7 @@ Currently Git-only. Concretely, this calls Git with the -w option.") (format s "~%") (format s "You can customize:~&") (format s "~%") + (format s "lem/legit:*show-stashes* : set to nil to not see the list of stashes in the status buffer~&") (format s "lem/porcelain:*nb-latest-commits* which defaults to 10~&") (format s "(and more)~&") )) diff --git a/extensions/legit/porcelain-git.lisp b/extensions/legit/porcelain-git.lisp index 47bc8ebad..7241218ea 100644 --- a/extensions/legit/porcelain-git.lisp +++ b/extensions/legit/porcelain-git.lisp @@ -437,3 +437,21 @@ I am stopping in case you still have something valuable there.")) (run-git (list "rebase" "--skip"))) (t (porcelain-error "No git rebase in process? PID not found.")))) + +(defmethod stash-push ((vcs vcs-git) &key message) + "Stash the current changes. Ask for a stash message." + (if message + (run-git (list "stash" "push" "-m" message)) + (run-git (list "stash" "push")))) + +(defmethod stash-pop ((vcs vcs-git) &key (position 0)) + "Pop the latest stashed changes." + (declare (ignorable position)) + (run-git (list "stash" "pop"))) + +(defmethod stash-list ((vcs vcs-git)) + ;; each line is like + ;; stash@{7}: On main: notes: legit vim interference + ;; just display them. + (str:lines + (run-git (list "stash" "list")))) diff --git a/extensions/legit/porcelain.lisp b/extensions/legit/porcelain.lisp index a331f34ce..fd4fe6f2a 100644 --- a/extensions/legit/porcelain.lisp +++ b/extensions/legit/porcelain.lisp @@ -28,6 +28,9 @@ :show-commit-diff :stage :unstage + :stash-list + :stash-pop + :stash-push :*diff-context-lines* :commits-log :*commits-log-page-size* @@ -257,3 +260,24 @@ M src/ext/porcelain.lisp (defgeneric rebase-skip (vcs) (:method (vcs) (porcelain-error "lem/porcelain:rebase-skip not implemented for vcs ~a" (vcs-name vcs)))) + +;;; +;;; Stash. +;;; +(defgeneric stash-push (vcs &key message) + (:method (vcs &key message) + (declare (ignorable message)) + (porcelain-error "lem/porcelain:stash not implemented for vcs ~a" (vcs-name vcs))) + (:documentation "Stash the current changes. Ask for a stash message.")) + +(defgeneric stash-pop (vcs &key position) + (:method (vcs &key position) + (declare (ignorable position)) + (porcelain-error "lem/porcelain:stash-pop not implemented for vcs ~a" (vcs-name vcs))) + (:documentation "Pop saved stashes. Defaults to the latest stash.")) + +(defgeneric stash-list (vcs) + (:method (vcs) + (declare (ignorable position)) + (values)) + (:documentation "List stashes"))