Emacs — How to push a Git repository to multiple remotes

守給你的承諾、 提交于 2019-11-29 10:38:46

EDIT (April 23, 2014):  Added a non-Magit solution to stage all, commit all (with a default commit message), and push to all remotes.

EDIT (April 24, 2014):  The printed output of all processes is now sent to the git-status-buffer, which is displayed at the end of the function -- with options for the user to choose what to do with the window -- e.g., delete window, delete buffer and window, or do nothing. Added some pretty coloring with (propertize "[...]" 'face 'font-lock-warning-face). The first draft of the function that relies upon a pre-existing installation of Magit has been moved to the bottom of this answer -- that function works, but is not as sophisticated as the current version that does not rely upon an installation of Magit.

(defvar git-status-buffer "*GIT-STATUS*"
  "The buffer name of the git-status-buffer.")

(defvar git-branch-name nil
"The current branch of the working Git directory.")
(make-variable-buffer-local 'git-branch-name)

(defvar git-remote-list nil
"List of remote locations -- e.g., lawlist_remote or github_remote.")
(make-variable-buffer-local 'git-remote-list)

(defvar git-commit-message (format "Committed -- %s" (current-time-string))
"The predetermined Git commit message.")
(make-variable-buffer-local 'git-commit-message)

(defun git-branch-process-filter (proc string)
  (with-current-buffer (get-buffer git-status-buffer)
    (set (make-local-variable 'git-branch-name)
      (car (split-string string "\n")))))

(defun git-push-process-filter (proc string)
  (when (string-match "password" string)
    (process-send-string
      proc
      (concat (read-passwd "Password:  ") "\n")))
  (when (and
      (not (string-equal "Password: " string))
      (not (string-equal "\n" string))
      (not (string-equal "stdin: is not a tty\n" string)))
    (with-current-buffer git-status-buffer
      (goto-char (point-max))
      (insert "\n" (replace-regexp-in-string "\^M" "\n" string)))))

(defun git-push-process-sentinel (proc string)
  (when (= 0 (process-exit-status proc))
    (with-current-buffer (get-buffer git-status-buffer)
      (insert
        "\n"
        (propertize
          (format "Process `%s` has finished pushing to `%s`." proc git-remote-name)
          'face 'font-lock-warning-face)
        "\n"))
    (throw 'exit nil)))

(defun stage-commit-push-all ()
"This function does the following:
  * Save the current working buffer if it has been modified.
  * Obtain the name of the selected branch in the current working buffer.
  * Gather a list of all remotes associated with working directory Git project.
  * Stage all -- `/usr/local/git/bin/git add .`
  * Commit all -- `/usr/local/git/bin/git commit -m [git-commit-message]`
  * Push to all remotes:  `/usr/local/git/bin/git push -v [remote] [current-branch]`"
(interactive)
  (when (buffer-modified-p)
    (save-buffer))
  (when (get-buffer git-status-buffer)
    (with-current-buffer (get-buffer git-status-buffer)
      (kill-local-variable 'git-remote-list)
      (kill-local-variable 'git-branch-name)
      (erase-buffer)))
  (start-process
    "current-branch"
    git-status-buffer
    "/usr/local/git/bin/git"
    "rev-parse"
    "--abbrev-ref"
    "HEAD")
  (set-process-filter (get-process "current-branch") 'git-branch-process-filter)
  (set-process-sentinel
    (get-process "current-branch")
    (lambda (p e) (when (= 0 (process-exit-status p))
      (set-process-sentinel
        (start-process
          "list-remotes"
          git-status-buffer
          "/usr/local/git/bin/git"
          "remote"
          "-v")
        (lambda (p e) (when (= 0 (process-exit-status p))
          (let* (
              beg
              end
              git-remote-name)
            (with-current-buffer (get-buffer git-status-buffer)
              (goto-char (point-max))
              (while (re-search-backward "\(push\)" nil t)
                (beginning-of-line 1)
                (setq beg (point))
                (re-search-forward "\t" nil t)
                (setq end (- (point) 1))
                (setq git-remote-name (buffer-substring-no-properties beg end))
                (setq git-remote-list
                  (append (cons git-remote-name git-remote-list)))) ))
          (set-process-sentinel
            (start-process
              "stage-all"
              git-status-buffer
              "/usr/local/git/bin/git"
              "add"
              ".")
            (lambda (p e) (when (= 0 (process-exit-status p))
              (with-current-buffer (get-buffer git-status-buffer)
                (goto-char (point-max))
                (insert "\n"))
              (set-process-sentinel
                (start-process
                  "commit-all"
                  git-status-buffer
                  "/usr/local/git/bin/git"
                  "commit"
                  "-m"
                  git-commit-message)
                (lambda (p e) (when (= 0 (process-exit-status p))
                  (mapcar (lambda (git-remote-name)
                    (let ((proc
                        (start-process
                          "push-process"
                          git-status-buffer
                          "/usr/local/git/bin/git"
                          "push"
                          "-v"
                          (format "%s" git-remote-name)
                          (format "%s"
                            (with-current-buffer (get-buffer git-status-buffer)
                              git-branch-name)) )))
                      (set-process-filter proc 'git-push-process-filter)
                      (set-process-sentinel proc 'git-push-process-sentinel)
                      (recursive-edit) ))
                    (with-current-buffer (get-buffer git-status-buffer)
                      git-remote-list) )
                  (display-buffer (get-buffer git-status-buffer))
                  (message (concat
                    git-status-buffer
                    " -- ["
                    (propertize "d" 'face 'font-lock-warning-face)
                    "]elete window | ["
                    (propertize "k" 'face 'font-lock-warning-face)
                    "]ill buffer + delete window | ["
                    (propertize "n" 'face 'font-lock-warning-face)
                    "]othing"))
                  (let* (
                      (git-window-options (read-char-exclusive))
                      (target-window (get-buffer-window git-status-buffer)))
                    (cond
                      ((eq git-window-options ?d)
                        (with-current-buffer (get-buffer git-status-buffer)
                          (delete-window target-window)))
                      ((eq git-window-options ?k)
                        (with-current-buffer (get-buffer git-status-buffer)
                          (delete-window target-window)
                          (kill-buffer (get-buffer git-status-buffer))))
                      ((eq git-window-options ?n)
                        (message "Done!"))
                      (t (message "You have exited the sub-function.")) ))
                )))))))))))))

FIRST DRAFT (April 19, 2014):  This function requires a pre-existing installation of Magit. The code set forth above does not require installation of Magit.

(defun push-to-all-remotes ()
"This function requires a pre-existing installation of Magit, and the function assumes
that the user has already staged and committed -- i.e., it only pushes to all remotes."
(interactive)
  (let* (beg end remote)
    (when (get-buffer "*REMOTES*")
      (with-current-buffer (get-buffer "*REMOTES*")
        (erase-buffer)))
    (set-process-sentinel
      (start-process
        "list-remotes"
        "*REMOTES*"
        "/usr/local/git/bin/git"
        "remote"
        "-v")
      (lambda (p e) (when (= 0 (process-exit-status p))
        (with-current-buffer (get-buffer "*REMOTES*")
          (goto-char (point-max))
          (while (re-search-backward "\(push\)" nil t)
            (beginning-of-line 1)
            (setq beg (point))
            (re-search-forward "\t" nil t)
            (setq end (- (point) 1))
            (setq remote (buffer-substring-no-properties beg end))
            (magit-run-git-async
              "push"
              "-v"
              remote
              (magit-get-current-branch))) ))))
    (display-buffer (get-buffer magit-process-buffer-name)) ))
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!