In response to a question about extending magit's push menu, I have successfully added a command that logs into a remote server, pulls from where I am, and runs make:

(defun magit-update-course-repo ()
  "Run pull-and-mk-repo remotely to update the course repo"
  (let ((cc (course-number-and-update-command)))
    (when cc
      (let* ((command (cdr cc)))
        (shell-command command)

But the command here is an ssh command which runs a script, and the whole thing takes many seconds to complete. During that time, the user interface does not acknowledge that anything is happening, and indeed emacs seems to freeze. Eventually, however, the magit status window updates, and a *Shell Command Output* is shown in another buffer. In other words, the functionality is there, but the user experience is not good.

I know magit provides internal tools that can do better than shell-command, but I am having trouble understanding the documentation and reading the source code. What magit functions should I use to achieve the following goals?

  • The moment the command launches, magit's user interface acknowledges that something is happening, even if it takes a long time to complete.

  • The command's output goes into magit's "process buffer", just like the output from normal git commands.

  • Ideally, while the command is running, the user can get on with life and do other things inside emacs.

I have gotten as far as discovering the existence of command magit-start-process, but I have not figured out how to use it.

1 Answer 1


Here are all the magit-specific parts of the feature you want to implement. It includes code from your other two questions about this. The non-magit-specific parts you will have to fill in yourself.

(defun my-update-course-repo ()
  "Run remote `pull-and-mk-repo' remotely to update the course repo."
  (--if-let (my--get-course-number-and-update-command)
        (magit-start-process (car it) nil (cdr it))
        (set-process-sentinel magit-this-process 'my--ssh-sentinel))
    (user-error "TODO useful description")))

(magit-define-popup-action 'magit-push-popup ?!
  (lambda ()
    (and my--update-course-repo-all-good-p
         "TODO useful description\n"))

(defun my--ssh-sentinel (process event)
  (let ((status (process-status process)))
    (when (memq status '(exit signal))
      (magit-process-sentinel process event)
      (if (eq status 'exit)
        (message "TODO useful description")))))

(defun my--update-course-repo-all-good-p ()
  ;; TODO return t iff `my-get-course-number-and-update-command' would
  ;; return non-nil.

(defun my--get-course-number-and-update-command ()
  ;; TODO Return command and arguments as a list of string, not a
  ;; single string.

The moment the command launches, magit's user interface acknowledges that something is happening, even if it takes a long time to complete.

You get messages in the echo area.

  • Apparently I'm not getting messages in the echo area, at least not until "Git Finished". Should I just call message before magit-start-process, or is there something more clever I should be thinking about? Sep 6, 2016 at 22:16
  • A right, magit-start-process does not show any message, magit-run-git-sync does. So you have to print your own message indeed.
    – tarsius
    Sep 7, 2016 at 14:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.