stgit.el: Move to goal column after expanding unknown directory
[stgit] / contrib / stgit.el
index 234dcaa..b0d1ec9 100644 (file)
@@ -14,6 +14,7 @@
 
 (require 'git nil t)
 (require 'cl)
+(require 'comint)
 (require 'ewoc)
 (require 'easymenu)
 (require 'format-spec)
@@ -843,6 +844,8 @@ See also `stgit-expand'."
         (stgit-process-files (lambda (f)
                                (setq node (ewoc-enter-after ewoc node f))))))
 
+    (move-to-column (stgit-goal-column))
+
     (let ((inhibit-read-only t))
       (put-text-property start end 'patch-data patch))))
 
@@ -943,7 +946,6 @@ file for (applied) copies and renames."
 (unless stgit-mode-map
   (let ((diff-map   (make-sparse-keymap))
         (toggle-map (make-sparse-keymap)))
-    (suppress-keymap diff-map)
     (mapc (lambda (arg) (define-key diff-map (car arg) (cdr arg)))
           '(("b" .        stgit-diff-base)
             ("c" .        stgit-diff-combined)
@@ -951,7 +953,6 @@ file for (applied) copies and renames."
             ("o" .        stgit-diff-ours)
             ("r" .        stgit-diff-range)
             ("t" .        stgit-diff-theirs)))
-    (suppress-keymap toggle-map)
     (mapc (lambda (arg) (define-key toggle-map (car arg) (cdr arg)))
           '(("n" .        stgit-toggle-patch-names)
             ("t" .        stgit-toggle-worktree)
@@ -1006,7 +1007,8 @@ file for (applied) copies and renames."
             ("\C-c\C-b" . stgit-rebase)
             ("t" .        ,toggle-map)
             ("d" .        ,diff-map)
-            ("q" .        stgit-quit))))
+            ("q" .        stgit-quit)
+            ("!" .        stgit-execute))))
 
   (let ((at-unmerged-file '(let ((file (stgit-patched-file-at-point)))
                              (and file (eq (stgit-file->status file)
@@ -1148,6 +1150,8 @@ Basic commands:
 
 \\[stgit-git-status]   Run `git-status' (if available)
 
+\\[stgit-execute]      Run an stg shell command
+
 Movement commands:
 \\[stgit-previous-line]        Move to previous line
 \\[stgit-next-line]    Move to next line
@@ -1434,7 +1438,7 @@ PATCHSYM."
   (stgit-assert-mode)
   (let ((old-patchsym (stgit-patch-name-at-point t t)))
     (stgit-capture-output nil
-      (stgit-run "rename" old-patchsym name))
+      (stgit-run "rename" "--" old-patchsym name))
     (let ((name-sym (intern name)))
       (when (memq old-patchsym stgit-expanded-patches)
         (setq stgit-expanded-patches
@@ -1492,9 +1496,16 @@ If ALL is not nil, also return non-stgit branches."
               ((not (string-match stgit-allowed-branch-name-re branch))
                (error "Invalid branch name"))
               ((yes-or-no-p (format "Create branch \"%s\"? " branch))
-               (stgit-capture-output nil (stgit-run "branch" "--create" "--"
-                                                    branch))
-               t))
+               (let ((branch-point (completing-read
+                                    "Branch from (default current branch): "
+                                    (stgit-available-branches))))
+                 (stgit-capture-output nil
+                   (apply 'stgit-run
+                          `("branch" "--create" "--"
+                            ,branch
+                            ,@(unless (zerop (length branch-point))
+                                (list branch-point)))))
+                 t)))
     (stgit-reload)))
 
 (defun stgit-available-refs (&optional omit-stgit)
@@ -1535,7 +1546,7 @@ what git-config branch.<branch>.stgit.parentbranch is set to."
                                       nil nil
                                       (stgit-parent-branch))))
   (stgit-assert-mode)
-  (stgit-capture-output nil (stgit-run "rebase" new-base))
+  (stgit-capture-output nil (stgit-run "rebase" "--" new-base))
   (stgit-reload))
 
 (defun stgit-commit (count)
@@ -1756,7 +1767,7 @@ patches if used on a line after or before all patches."
       (:top    (stgit-push-or-pop-patches t t))
       (:bottom (stgit-push-or-pop-patches nil t))
       (t (stgit-capture-output nil
-           (stgit-run "goto" patchsym))
+           (stgit-run "goto" "--" patchsym))
          (stgit-reload)))))
 
 (defun stgit-id (patchsym)
@@ -1765,7 +1776,7 @@ If PATCHSYM is a keyword, returns PATCHSYM unmodified."
   (if (keywordp patchsym)
       patchsym
     (let ((result (with-output-to-string
-                   (stgit-run-silent "id" patchsym))))
+                   (stgit-run-silent "id" "--" patchsym))))
       (unless (string-match "^\\([0-9A-Fa-f]\\{40\\}\\)$" result)
        (error "Cannot find commit id for %s" patchsym))
       (match-string 1 result))))
@@ -1818,6 +1829,7 @@ which stage to diff against in the case of unmerged files."
                                 (list unmerged-stage))))
              (let ((args (append '("show" "-O" "--patch-with-stat" "-O" "-M")
                                  (and space-arg (list "-O" space-arg))
+                                 '("--")
                                  (list (stgit-patch-name-at-point)))))
                (apply 'stgit-run args)))))
         (t
@@ -1984,14 +1996,14 @@ file ended up. You can then jump to the file with \
     (setq default-directory dir)
     (let ((standard-output edit-buf))
       (save-excursion
-        (stgit-run-silent "edit" "--save-template=-" patchsym)))))
+        (stgit-run-silent "edit" "--save-template=-" "--" patchsym)))))
 
 (defun stgit-confirm-edit ()
   (interactive)
   (let ((file (make-temp-file "stgit-edit-")))
     (write-region (point-min) (point-max) file)
     (stgit-capture-output nil
-      (stgit-run "edit" "-f" file stgit-edit-patchsym))
+      (stgit-run "edit" "-f" file "--" stgit-edit-patchsym))
     (with-current-buffer log-edit-parent-buffer
       (stgit-reload))))
 
@@ -2076,9 +2088,9 @@ the work tree and index."
                                (if spill-p
                                    " (spilling contents to index)"
                                  "")))
-      (let ((args (if spill-p 
-                      (cons "--spill" patchsyms)
-                    patchsyms)))
+      (let ((args (append (when spill-p '("--spill"))
+                          '("--")
+                          patchsyms)))
         (stgit-capture-output nil
           (apply 'stgit-run "delete" args))
         (stgit-reload)))))
@@ -2146,7 +2158,7 @@ Interactively, move the marked patches to where the point is."
   (let ((sorted-patchsyms (stgit-sort-patches patchsyms)))
     (stgit-capture-output nil
       (if (eq target-patch :top)
-          (apply 'stgit-run "float" sorted-patchsyms)
+          (apply 'stgit-run "float" "--" sorted-patchsyms)
         (apply 'stgit-run
                "sink"
                (append (unless (eq target-patch :bottom)
@@ -2176,7 +2188,7 @@ deepest patch had before the squash."
     (let ((result (let ((standard-output edit-buf))
                     (save-excursion
                       (apply 'stgit-run-silent "squash"
-                             "--save-template=-" sorted-patchsyms)))))
+                             "--save-template=-" "--" sorted-patchsyms)))))
 
       ;; stg squash may have reordered the patches or caused conflicts
       (with-current-buffer stgit-buffer
@@ -2194,7 +2206,7 @@ deepest patch had before the squash."
   (let ((file (make-temp-file "stgit-edit-")))
     (write-region (point-min) (point-max) file)
     (stgit-capture-output nil
-      (apply 'stgit-run "squash" "-f" file stgit-patchsyms))
+      (apply 'stgit-run "squash" "-f" file "--" stgit-patchsyms))
     (with-current-buffer log-edit-parent-buffer
       (stgit-clear-marks)
       ;; Go to first marked patch and stay there
@@ -2210,6 +2222,75 @@ deepest patch had before the squash."
   (interactive)
   (describe-function 'stgit-mode))
 
+(defun stgit-execute-process-sentinel (process sentinel)
+  (let (old-sentinel stgit-buf)
+    (with-current-buffer (process-buffer process)
+      (setq old-sentinel old-process-sentinel
+            stgit-buf    stgit-buffer))
+    (and (memq (process-status process) '(exit signal))
+         (buffer-live-p stgit-buf)
+         (with-current-buffer stgit-buf
+           (stgit-reload)))
+    (funcall old-sentinel process sentinel)))
+
+(defun stgit-execute-process-filter (process output)
+  (with-current-buffer (process-buffer process)
+    (let* ((old-point (point))
+           (pmark     (process-mark process))
+           (insert-at (marker-position pmark))
+           (at-pmark  (= insert-at old-point)))
+      (goto-char insert-at)
+      (insert-before-markers output)
+      (comint-carriage-motion insert-at (point))
+      (set-marker pmark (point))
+      (unless at-pmark
+        (goto-char old-point)))))
+
+(defun stgit-execute ()
+  "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.
+
+If the command ends in an ampersand, run it asynchronously.
+
+When the command has finished, reload the stgit buffer."
+  (interactive)
+  (stgit-assert-mode)
+  (let* ((patches (stgit-patches-marked-or-at-point nil t))
+         (patch-names (mapcar 'symbol-name patches))
+         (hyphens (find-if (lambda (s) (string-match "^-" s)) patch-names))
+         (defaultcmd (if patches
+                         (concat "stg  "
+                                 (and hyphens "-- ")
+                                 (mapconcat 'identity patch-names " "))
+                       "stg "))
+         (cmd (read-from-minibuffer "Shell command: " (cons defaultcmd 5)
+                                    nil nil 'shell-command-history))
+         (async (string-match "&[ \t]*\\'" cmd))
+         (buffer (get-buffer-create
+                  (if async
+                      "*Async Shell Command*"
+                    "*Shell Command Output*"))))
+    ;; cannot use minibuffer as stgit-reload would overwrite it; if we
+    ;; show the buffer, shell-command will not use the minibuffer
+    (display-buffer buffer)
+    (shell-command cmd)
+    (if async
+        (let ((old-buffer (current-buffer)))
+          (with-current-buffer buffer
+            (let ((process (get-buffer-process buffer)))
+              (set (make-local-variable 'old-process-sentinel)
+                   (process-sentinel process))
+              (set (make-local-variable 'stgit-buffer)
+                   old-buffer)
+              (set-process-filter process 'stgit-execute-process-filter)
+              (set-process-sentinel process 'stgit-execute-process-sentinel))))
+      (with-current-buffer buffer
+        (comint-carriage-motion (point-min) (point-max)))
+      (shrink-window-if-larger-than-buffer (get-buffer-window buffer))
+      (stgit-reload))))
+
 (defun stgit-undo-or-redo (redo hard)
   "Run stg undo or, if REDO is non-nil, stg redo.