Cloning git repositories is a pretty common task. For me, it typically goes something like:
No biggie, but why go through the same steps every time? We can do better. We have a hyper malleable editor, so let's get it to grab the URL from clipboard and do its thing.
shell-command or async-shell-command can help in this space, but require additional work: change location, re-type command, what if directory already exists… This is Emacs, so we can craft the exact experience we want. I did take inspiration from shell-command to display the process buffer correctly (git progress, control codes, etc.) and landed on the following experience:
;; -*- lexical-binding: t -*-
(defun ar/git-clone-clipboard-url ()
"Clone git URL in clipboard asynchronously and open in dired when finished."
(interactive)
(cl-assert (string-match-p "^\\(http\\|https\\|ssh\\)://" (current-kill 0)) nil "No URL in clipboard")
(let* ((url (current-kill 0))
(download-dir (expand-file-name "~/Downloads/"))
(project-dir (concat (file-name-as-directory download-dir)
(file-name-base url)))
(default-directory download-dir)
(command (format "git clone %s" url))
(buffer (generate-new-buffer (format "*%s*" command)))
(proc))
(when (file-exists-p project-dir)
(if (y-or-n-p (format "%s exists. delete?" (file-name-base url)))
(delete-directory project-dir t)
(user-error "Bailed")))
(switch-to-buffer buffer)
(setq proc (start-process-shell-command (nth 0 (split-string command)) buffer command))
(with-current-buffer buffer
(setq default-directory download-dir)
(shell-command-save-pos-or-erase)
(require 'shell)
(shell-mode)
(view-mode +1))
(set-process-sentinel proc (lambda (process state)
(let ((output (with-current-buffer (process-buffer process)
(buffer-string))))
(kill-buffer (process-buffer process))
(if (= (process-exit-status process) 0)
(progn
(message "finished: %s" command)
(dired project-dir))
(user-error (format "%s\n%s" command output))))))
(set-process-filter proc #'comint-output-filter)))