X-Git-Url: https://git.distorted.org.uk/~mdw/stgit/blobdiff_plain/b51a910fafdea01e64a1d41100f831179bf5f5b5..b4f12bc1acd2892a76e07bbf6ad34b2da1c455b5:/contrib/stgit.el?ds=sidebyside diff --git a/contrib/stgit.el b/contrib/stgit.el index 275299b..b72ba98 100644 --- a/contrib/stgit.el +++ b/contrib/stgit.el @@ -68,6 +68,16 @@ setting in an already-started StGit buffer." :group 'stgit :link '(variable-link stgit-show-ignored)) +(defcustom stgit-default-show-svn t + "Set to non-nil to by default show subversion information in a +new stgit buffer. + +Use \\\\[stgit-toggle-svn] to toggle this \ +setting in an already-started StGit buffer." + :type 'boolean + :group 'stgit + :link '(variable-link stgit-show-worktree)) + (defcustom stgit-find-copies-harder nil "Try harder to find copied files when listing patches. @@ -400,27 +410,37 @@ Returns nil if there was no output." (error "Bad element in stgit-make-run-args args: %S" x)))) args)) -(defun stgit-run-silent (&rest args) - (setq args (stgit-make-run-args args)) - (apply 'call-process "stg" nil standard-output nil args)) +(defvar stgit-inhibit-messages nil + "Set to non-nil to inhibit messages when running `stg' commands. +See also `stgit-message'.") +(defun stgit-message (format-spec &rest args) + "Call `message' on the arguments unless `stgit-inhibit-messages' is non-nil." + (unless stgit-inhibit-messages + (apply 'message format-spec args))) (defun stgit-run (&rest args) (setq args (stgit-make-run-args args)) (let ((msgcmd (mapconcat #'identity args " "))) - (message "Running stg %s..." msgcmd) - (apply 'call-process "stg" nil standard-output nil args) - (message "Running stg %s...done" msgcmd))) + (stgit-message "Running stg %s..." msgcmd) + (prog1 + (apply 'call-process "stg" nil standard-output nil args) + (stgit-message "Running stg %s...done" msgcmd)))) + +(defun stgit-run-silent (&rest args) + (let ((stgit-inhibit-messages t)) + (apply 'stgit-run args))) (defun stgit-run-git (&rest args) (setq args (stgit-make-run-args args)) (let ((msgcmd (mapconcat #'identity args " "))) - (message "Running git %s..." msgcmd) - (apply 'call-process "git" nil standard-output nil args) - (message "Running git %s...done" msgcmd))) + (stgit-message "Running git %s..." msgcmd) + (prog1 + (apply 'call-process "git" nil standard-output nil args) + (stgit-message "Running git %s...done" msgcmd)))) (defun stgit-run-git-silent (&rest args) - (setq args (stgit-make-run-args args)) - (apply 'call-process "git" nil standard-output nil args)) + (let ((stgit-inhibit-messages t)) + (apply 'stgit-run-git args))) (defun stgit-index-empty-p () "Returns non-nil if the index contains no changes from HEAD." @@ -430,9 +450,6 @@ Returns nil if there was no output." "Returns non-nil if the work tree contains no changes from index." (zerop (stgit-run-git-silent "diff-files" "--quiet"))) -(defvar stgit-index-node) -(defvar stgit-worktree-node) - (defvar stgit-did-advise nil "Set to non-nil if appropriate (non-stgit) git functions have been advised to update the stgit status when necessary.") @@ -466,25 +483,40 @@ been advised to update the stgit status when necessary.") :desc nil :empty nil))))) +(defun stgit-svn-find-rev (sha1 hash) + "Return the subversion revision corresponding to SHA1 as +reported by git svn. + +Cached data is stored in HASH, which must have been created +using (make-hash-table :test 'equal)." + (let ((result (gethash sha1 hash t))) + (when (eq result t) + (let ((svn-rev (with-output-to-string + (stgit-run-git-silent "svn" "find-rev" + "--" sha1)))) + (setq result (when (string-match "\\`[0-9]+" svn-rev) + (string-to-number (match-string 0 svn-rev)))) + (puthash sha1 result hash))) + result)) + (defun stgit-run-series (ewoc) (setq stgit-index-node nil stgit-worktree-node nil) (let (all-patchsyms) (when stgit-show-committed - (let* ((base (stgit-id "{base}")) + (let* ((show-svn stgit-show-svn) + (svn-hash stgit-svn-find-rev-hash) + (base (stgit-id "{base}")) (range (format "%s~%d..%s" base stgit-committed-count base))) (with-temp-buffer (let* ((standard-output (current-buffer)) (fmt (stgit-line-format)) (commit-abbrev (when (string-match "%-\\([0-9]+\\)n" fmt) - (list (format "--abbrev=%s" - (match-string 1 fmt))))) - (exit-status (apply 'stgit-run-git-silent - "--no-pager" - "log" "--reverse" "--pretty=oneline" - "--abbrev-commit" - `(,@commit-abbrev - ,range)))) + (string-to-number (match-string 1 fmt)))) + (exit-status (stgit-run-git-silent "--no-pager" "log" + "--reverse" + "--pretty=oneline" + range))) (goto-char (point-min)) (if (not (zerop exit-status)) (message "Failed to run git log") @@ -493,9 +525,21 @@ been advised to update the stgit status when necessary.") "\\([0-9a-f]+\\)\\(\\.\\.\\.\\)? \\(.*\\)") (error "Syntax error in output from git log")) (let* ((state 'committed) - (name (intern (match-string 1))) + (name (match-string 1)) (desc (match-string 3)) (empty nil)) + + (when show-svn + (let ((svn-rev (stgit-svn-find-rev name svn-hash))) + (when svn-rev + (setq desc (format "(r%s) %s" svn-rev desc))))) + + (and commit-abbrev + (< commit-abbrev (length name)) + (setq name (substring name 0 commit-abbrev))) + + (setq name (intern name)) + (setq all-patchsyms (cons name all-patchsyms)) (ewoc-enter-last ewoc (make-stgit-patch @@ -559,14 +603,20 @@ been advised to update the stgit status when necessary.") (stgit-run-silent "branch")) 0 -1)) -(defun stgit-reload () - "Update the contents of the StGit buffer." +(defun stgit-reload (&optional description) + "Update the contents of the StGit buffer. + +If DESCRIPTION is non-nil, it is displayed as a status message +during the operation." (interactive) (stgit-assert-mode) + (when description + (message "%s..." description)) (let ((inhibit-read-only t) (curline (line-number-at-pos)) (curpatch (stgit-patch-name-at-point)) - (curfile (stgit-patched-file-at-point))) + (curfile (stgit-patched-file-at-point)) + (stgit-inhibit-messages description)) (ewoc-filter stgit-ewoc #'(lambda (x) nil)) (ewoc-set-hf stgit-ewoc (concat "Branch: " @@ -583,8 +633,12 @@ been advised to update the stgit status when necessary.") (unless (and curpatch (stgit-goto-patch curpatch (and curfile (stgit-file->file curfile)))) - (goto-line curline))) - (stgit-refresh-git-status)) + (goto-char (point-min)) + (forward-line (1- curline)) + (move-to-column (stgit-goal-column))) + (stgit-refresh-git-status)) + (when description + (message "%s...done" description))) (defconst stgit-file-status-code-strings (mapcar (lambda (arg) @@ -1042,7 +1096,8 @@ file for (applied) copies and renames." ("t" . stgit-toggle-worktree) ("h" . stgit-toggle-committed) ("i" . stgit-toggle-ignored) - ("u" . stgit-toggle-unknown))) + ("u" . stgit-toggle-unknown) + ("s" . stgit-toggle-svn))) (setq stgit-mode-map (make-keymap)) (suppress-keymap stgit-mode-map) (mapc (lambda (arg) (define-key stgit-mode-map (car arg) (cdr arg))) @@ -1208,6 +1263,8 @@ file for (applied) copies and renames." :selected stgit-show-patch-names] ["Show recent commits" stgit-toggle-committed :style toggle :selected stgit-show-committed] + ["Show subversion info" stgit-toggle-svn :style toggle + :selected stgit-show-svn] "-" ["Switch branches" stgit-branch t :help "Switch to or create another branch"] @@ -1292,6 +1349,7 @@ Display commands: \\[stgit-toggle-unknown] Toggle showing unknown files \\[stgit-toggle-ignored] Toggle showing ignored files \\[stgit-toggle-committed] Toggle showing recent commits +\\[stgit-toggle-svn] Toggle showing subversion information Commands for diffs: \\[stgit-diff] Show diff of patch or file @@ -1320,6 +1378,7 @@ Customization variables: `stgit-default-show-unknown' `stgit-default-show-worktree' `stgit-default-show-committed' +`stgit-default-show-svn' `stgit-default-committed-count' `stgit-find-copies-harder' `stgit-show-worktree-mode' @@ -1338,10 +1397,12 @@ See also \\[customize-group] for the \"stgit\" group." (stgit-index-node . nil) (stgit-worktree-node . nil) (stgit-marked-patches . nil) + (stgit-svn-find-rev-hash . ,(make-hash-table :test 'equal)) (stgit-committed-count . ,stgit-default-committed-count) (stgit-show-committed . ,stgit-default-show-committed) (stgit-show-ignored . ,stgit-default-show-ignored) (stgit-show-patch-names . ,stgit-default-show-patch-names) + (stgit-show-svn . ,stgit-default-show-svn) (stgit-show-unknown . ,stgit-default-show-unknown) (stgit-show-worktree . ,stgit-default-show-worktree))) (set-variable 'truncate-lines 't) @@ -1955,6 +2016,8 @@ which stage to diff against in the case of unmerged files." (list unmerged-stage)) (t (list (concat patch-id "^") patch-id))) + (and (eq (stgit-file->status patched-file) 'copy) + '("--diff-filter=C")) '("--") (if (stgit-file->copy-or-rename patched-file) (list (stgit-file->cr-from patched-file) @@ -1970,7 +2033,8 @@ which stage to diff against in the case of unmerged files." (if (eq patch-id :index) '("--cached") (list unmerged-stage)))) - (let ((args (append '("show" "-O" "--patch-with-stat" "-O" "-M") + (let ((args (append '("show" "-O" "--patch-with-stat") + `("-O" ,(stgit-find-copies-harder-diff-arg)) (and space-arg (list "-O" space-arg)) '("--") (list (stgit-patch-name-at-point))))) @@ -2033,10 +2097,12 @@ greater than four (e.g., \\[universal-argument] \ (error "Can only show diff range for applied patches")) (stgit-capture-output (format "*StGit diff %s..%s*" first-patch second-patch) - (apply 'stgit-run-git (append '("diff" "--patch-with-stat") - (and whitespace-arg (list whitespace-arg)) - (list (format "%s^" (stgit-id first-patch)) - (stgit-id second-patch)))) + (apply 'stgit-run-git + "diff" "--patch-with-stat" + (stgit-find-copies-harder-diff-arg) + (append (and whitespace-arg (list whitespace-arg)) + (list (format "%s^" (stgit-id first-patch)) + (stgit-id second-patch)))) (with-current-buffer standard-output (goto-char (point-min)) (diff-mode))))) @@ -2265,16 +2331,13 @@ their position in the patch series, bottommost first. PATCHSYMS must not contain duplicate entries, unless ALLOW-DUPLICATES is not nil." - (let (sorted-patchsyms - (series (with-output-to-string - (with-current-buffer standard-output - (stgit-run-silent "series" "--noprefix")))) - start) - (while (string-match "^\\(.+\\)" series start) - (let ((patchsym (intern (match-string 1 series)))) - (when (memq patchsym patchsyms) - (setq sorted-patchsyms (cons patchsym sorted-patchsyms)))) - (setq start (match-end 0))) + (let (sorted-patchsyms) + (ewoc-map #'(lambda (patch) + (let ((name (stgit-patch->name patch))) + (when (memq name patchsyms) + (setq sorted-patchsyms (cons name sorted-patchsyms)))) + nil) + stgit-ewoc) (setq sorted-patchsyms (nreverse sorted-patchsyms)) (unless allow-duplicates @@ -2390,24 +2453,29 @@ deepest patch had before the squash." (unless at-pmark (goto-char old-point))))) -(defun stgit-execute () +(defun stgit-execute (&optional git-mode) "Prompt for an stg command to execute in a shell. The names of any marked patches or the patch at point are inserted in the command to be executed. +With a prefix argument, or if GIT-MODE is non-nil, insert SHA1 +sums of the marked patches instead, and prompt for a git command. + If the command ends in an ampersand, run it asynchronously. When the command has finished, reload the stgit buffer." - (interactive) + (interactive "P") (stgit-assert-mode) - (let* ((patches (stgit-patches-marked-or-at-point nil 'allow-committed)) + (let* ((patches (stgit-sort-patches + (stgit-patches-marked-or-at-point nil 'allow-committed))) (patch-names (mapcar 'symbol-name patches)) (hyphens (find-if (lambda (s) (string-match "^-" s)) patch-names)) (defaultcmd (if patches - (concat "stg " + (concat (if git-mode "git" "stg") " " (and hyphens "-- ") - (mapconcat 'identity patch-names " ")) + (mapconcat (if git-mode 'stgit-id 'identity) + patch-names " ")) "stg ")) (cmd (read-from-minibuffer "Shell command: " (cons defaultcmd 5) nil nil 'shell-command-history)) @@ -2504,67 +2572,69 @@ See also `stgit-show-worktree-mode'.") (defvar stgit-show-committed nil "If nil, inhibit showing recent commits.") +(defvar stgit-show-svn nil + "If nil, inhibit showing git svn information.") + (defvar stgit-committed-count nil "The number of recent commits to show.") -(defun stgit-toggle-worktree (&optional arg) +(defmacro stgit-define-toggle-view (sym desc help) + (declare (indent 1)) + (let* ((name (symbol-name sym)) + (fun (intern (concat "stgit-toggle-" name))) + (flag (intern (concat "stgit-show-" name)))) + ;; make help-follow find the correct function + `(put (quote ,fun) 'definition-name 'stgit-define-toggle-view) + `(defun ,fun (&optional arg) + ,help + (interactive "P") + (stgit-assert-mode) + (setq ,flag (if arg + (> (prefix-numeric-value arg) 0) + (not ,flag))) + (stgit-reload (format "%s %s" (if ,flag "Showing" "Hiding") ,desc))))) + +(stgit-define-toggle-view worktree + "work tree and index" "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'. `stgit-show-worktree-mode' controls where on screen the index and -work tree will show up." - (interactive) - (stgit-assert-mode) - (setq stgit-show-worktree - (if (numberp arg) - (> arg 0) - (not stgit-show-worktree))) - (stgit-reload)) +work tree will show up.") -(defun stgit-toggle-ignored (&optional arg) +(stgit-define-toggle-view ignored + "ignored files" "Toggle the visibility of files ignored by git in the work tree. With ARG, show these files if ARG is positive. Its initial setting is controlled by `stgit-default-show-ignored'. -Use \\[stgit-toggle-worktree] to show the work tree." - (interactive) - (stgit-assert-mode) - (setq stgit-show-ignored - (if (numberp arg) - (> arg 0) - (not stgit-show-ignored))) - (stgit-reload)) +Use \\[stgit-toggle-worktree] to show the work tree.") -(defun stgit-toggle-unknown (&optional arg) +(stgit-define-toggle-view unknown + "unknown files" "Toggle the visibility of files not registered with git in the work tree. With ARG, show these files if ARG is positive. Its initial setting is controlled by `stgit-default-show-unknown'. -Use \\[stgit-toggle-worktree] to show the work tree." - (interactive) - (stgit-assert-mode) - (setq stgit-show-unknown - (if (numberp arg) - (> arg 0) - (not stgit-show-unknown))) - (stgit-reload)) +Use \\[stgit-toggle-worktree] to show the work tree.") -(defun stgit-toggle-patch-names (&optional arg) +(stgit-define-toggle-view patch-names + "patch names" "Toggle the visibility of patch names. With ARG, show patch names if ARG is positive. -The initial setting is controlled by `stgit-default-show-patch-names'." - (interactive) - (stgit-assert-mode) - (setq stgit-show-patch-names - (if (numberp arg) - (> arg 0) - (not stgit-show-patch-names))) - (stgit-reload)) +The initial setting is controlled by `stgit-default-show-patch-names'.") + +(stgit-define-toggle-view svn + "subversion revisions" + "Toggle showing subversion information from git svn. With ARG, +show svn information if ARG is positive. + +The initial setting is controlled by `stgit-default-show-svn'.") (defun stgit-toggle-committed (&optional arg) "Toggle the visibility of historical git commits. @@ -2579,6 +2649,10 @@ The initial setting is controlled by `stgit-default-show-committed'." (let ((n (prefix-numeric-value arg))) (setq stgit-show-committed (> n 0)) (setq stgit-committed-count n))) - (stgit-reload)) + (stgit-reload (format "%s historical commits" + (if (and stgit-show-committed + (> stgit-committed-count 0)) + "Showing" + "Hiding")))) (provide 'stgit)