diff --git a/contrib/!source-control/github/extensions.el b/contrib/!source-control/github/extensions.el deleted file mode 100644 index 520cc2307..000000000 --- a/contrib/!source-control/github/extensions.el +++ /dev/null @@ -1,35 +0,0 @@ -;;; extensions.el --- Github Layer Extensions File for Spacemacs -;; -;; Copyright (c) 2012-2014 Sylvain Benner -;; Copyright (c) 2014-2015 Sylvain Benner & Contributors -;; -;; Author: Sylvain Benner -;; URL: https://github.com/syl20bnr/spacemacs -;; -;; This file is not part of GNU Emacs. -;; -;;; License: GPLv3 - -(setq github-post-extensions '(magit-gh-pulls)) - -(defun github/init-magit-gh-pulls () - (use-package magit-gh-pulls - :commands magit-gh-pulls-mode - :init - (progn - (defun spacemacs/load-gh-pulls-mode () - "Start `magit-gh-pulls-mode' only after a manual request." - (interactive) - (magit-gh-pulls-mode) - (magit-gh-pulls-reload)) - (defun spacemacs/fetch-gh-pulls-mode () - "Start `magit-gh-pulls-mode' only after a manual request." - (interactive) - (magit-gh-pulls-mode) - (magit-gh-pulls-fetch-commits)) - (eval-after-load 'magit - '(progn - (define-key magit-mode-map "#gg" 'spacemacs/load-gh-pulls-mode) - (define-key magit-mode-map "#gf" 'spacemacs/fetch-gh-pulls-mode)))) - :config - (spacemacs|diminish magit-gh-pulls-mode "Github-PR"))) diff --git a/contrib/!source-control/github/extensions/magit-gh-pulls/magit-gh-pulls.el b/contrib/!source-control/github/extensions/magit-gh-pulls/magit-gh-pulls.el deleted file mode 100644 index ec9d1e588..000000000 --- a/contrib/!source-control/github/extensions/magit-gh-pulls/magit-gh-pulls.el +++ /dev/null @@ -1,375 +0,0 @@ -;;; magit-gh-pulls.el --- GitHub pull requests extension for Magit - -;; Copyright (C) 2011-2015 Yann Hodique, Alexander Yakushev - -;; Author: Yann Hodique -;; Keywords: git tools -;; Version: 0.5 -;; URL: https://github.com/sigma/magit-gh-pulls -;; Package-Requires: ((emacs "24") (gh "0.4.3") (magit "2.1.0") (pcache "0.2.3") (s "1.6.1")) - -;; This file is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 2, or (at your option) -;; any later version. - -;; This file is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to -;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -;; Boston, MA 02111-1307, USA. - -;;; Commentary: - -;; This is a Magit extension for manipulating GitHub pull requests - -;; No configuration is needed in the repository if any of your remotes contain a -;; URL to Github's remote repository. If for some reason you don't have any -;; Github remotes in your config, you can specify username and repository -;; explicitly: - -;; $ git config magit.gh-pulls-repo / # your github repository - -;; Add these lines to your init.el: - -;; (require 'magit-gh-pulls) -;; (add-hook 'magit-mode-hook 'turn-on-magit-gh-pulls) - -;; There are currently 4 bindings for pull requests: -;; # g g --- refreshes the list of pull requests -;; # g f --- fetches the commits associated with the pull request at point -;; # g b --- helps you creating a topic branch from a review request -;; # g m --- merges the PR on top of the current branch -;; # g c --- creates a PR from the current branch - -;; Then, you can do whatever you want with the commit objects associated with -;; the pull request (merge, cherry-pick, diff, ...) - -;;; Code: - -(require 'eieio) - -(require 'magit) -(require 'gh-pulls) -(require 'pcache) -(require 'gh) -(require 's) - -(defvar magit-gh-pulls-maybe-filter-pulls 'identity - "Filter function which should validate pulls you want to be - viewed in magit. It receives a list of pull requests and should - return a list of pull requests.") - -(defvar magit-gh-pulls-collapse-commits nil - "Collapse commits in pull requests listing.") - -(defun magit-gh-pulls-get-api () - (gh-pulls-api "api" :sync t :num-retries 1 :cache (gh-cache "cache"))) - -(defun magit-gh-pulls-get-repo-from-config () - (let* ((cfg (magit-get "magit" "gh-pulls-repo"))) - (when cfg - (let* ((split (split-string cfg "/"))) - (cons (car split) (cadr split)))))) - -(defun magit-gh-pulls-parse-url (url) - (let ((creds (s-match "github.com[:/]\\([^/]+\\)/\\([^/]+\\)/?$" url))) - (when creds - (cons (cadr creds) (s-chop-suffix ".git" (caddr creds)))))) - -(defun magit-gh-pulls-guess-repo-from-origin () - (let ((creds nil)) - (dolist (remote (magit-git-lines "remote") creds) - (let ((parsed (magit-gh-pulls-parse-url - (magit-get "remote" remote "url")))) - (when parsed - (setq creds parsed)))))) - -(defun magit-gh-pulls-guess-repo () - (or (magit-gh-pulls-get-repo-from-config) - (magit-gh-pulls-guess-repo-from-origin))) - -(defun magit-gh-pulls-requests-cached-p (api user proj) - (let ((cache-repo (format "/repos/%s/%s/pulls" user proj)) - (cached? nil)) - (pcache-map (oref api :cache) - (lambda (key _) (when (equal (car key) cache-repo) - (setq cached? t)))) - cached?)) - -(defun magit-gh-pulls-insert-gh-pulls () - (condition-case print-section - (progn - (let* ((repo (magit-gh-pulls-guess-repo))) - (when repo - (let* ((api (magit-gh-pulls-get-api)) - (user (car repo)) - (proj (cdr repo)) - (cached? (magit-gh-pulls-requests-cached-p api user proj)) - (stubs (when cached? - (funcall magit-gh-pulls-maybe-filter-pulls - (oref (gh-pulls-list api user proj) :data)))) - (branch (magit-get-current-branch))) - (when (or (> (length stubs) 0) (not cached?)) - (magit-insert-section (pulls) - (magit-insert-heading "Pull Requests:") - (dolist (stub stubs) - (let* ((id (oref stub :number)) - (req (oref (gh-pulls-get api user proj id) :data)) - (base-sha (oref (oref req :base) :sha)) - (base-ref (oref (oref req :base) :ref)) - (head-sha (oref (oref req :head) :sha)) - ;; branch has been deleted in the meantime... - (invalid (equal (oref (oref req :head) :ref) head-sha)) - (have-commits - (and (eql 0 (magit-git-exit-code "cat-file" "-e" base-sha)) - (eql 0 (magit-git-exit-code "cat-file" "-e" head-sha)))) - (applied (and have-commits - (not (magit-git-string - "rev-list" - "--cherry-pick" "--right-only" - (format "HEAD...%s" head-sha) - "--not" - (format "%s" base-sha))))) - (heading - (format "[%s@%s] %s\n" - (propertize (number-to-string id) - 'face 'magit-tag) - (if (string= base-ref branch) - (propertize base-ref - 'face 'magit-branch-local) - base-ref) - (propertize - (oref req :title) 'face - (cond (applied 'magit-cherry-equivalent) - (have-commits nil) - (invalid 'error) - (t 'italic))))) - (info (list user proj id))) - (cond - (have-commits - (magit-insert-section - (pull info (not magit-gh-pulls-collapse-commits)) - (magit-insert heading) - (magit-insert-heading) - (when (and have-commits (not applied)) - (magit-git-wash - (apply-partially 'magit-log-wash-log 'cherry) - "cherry" "-v" (magit-abbrev-arg) - base-sha head-sha)))) - (invalid - (magit-insert-section (invalid-pull info) - (magit-insert heading))) - (t - (magit-insert-section (unfetched-pull info) - (magit-insert heading)))))) - (when (not cached?) - (insert "Press `# g g` to update the pull request list.\n\n")) - (when (> (length stubs) 0) - (insert "\n")))))))) - (error nil))) - -(defun magit-gh-pulls-guess-topic-name (req) - (let ((user (oref (oref req :user) :login)) - (topic (oref (oref req :head) :ref))) - (format "%s/%s" user topic))) - -(defun magit-gh-section-req-data (&optional section) - (oref (apply #'gh-pulls-get - (magit-gh-pulls-get-api) - (magit-section-value (or section (magit-current-section)))) - :data)) - -(defun magit-gh-pulls-create-branch () - (interactive) - (magit-section-case - (pull - (let* ((req (magit-gh-section-req-data)) - (branch (read-from-minibuffer - "Branch name: " (magit-gh-pulls-guess-topic-name req))) - (base (magit-read-branch-or-commit - "Branch base: " - (oref (oref req :base) :ref))) - (inhibit-magit-refresh t)) - (magit-branch branch base) - (magit-merge (oref (oref req :head) :sha))) - (magit-refresh)) - (unfetched-pull - (error "Please fetch pull request commits first")) - (invalid-pull - (error "This pull request refers to invalid reference")))) - -(defun magit-gh-pulls-merge-pull-request () - (interactive) - (magit-section-case - (pull - (let* ((req (magit-gh-section-req-data)) - (branch (magit-gh-pulls-guess-topic-name req)) - (base (oref (oref req :base) :ref)) - (inhibit-magit-refresh t)) - (magit-branch branch base) - (magit-merge (oref (oref req :head) :sha)) - (magit-checkout base) - (magit-merge branch) - (magit-call-git "branch" "-D" branch)) - (magit-refresh)) - (unfetched-pull - (error "Please fetch pull request commits first")) - (invalid-pull - (error "This pull request refers to invalid reference")))) - -(defun magit-gh-pulls-fetch-commits () - (interactive) - (magit-section-case - (unfetched-pull - (let* ((req (magit-gh-section-req-data)) - (head (oref req :head))) - (magit-run-git "fetch" (oref (oref head :repo) :git-url) - (oref head :ref)))) - (pull nil) - (invalid-pull - (error "This pull request refers to invalid reference")))) - -(defun magit-gh-pulls-url-for-pull (info) - "Return github url for a pull request using INFO." - (let ((url "https://github.com/%s/%s/pull/%s")) - (apply 'format url info))) - -(defun magit-gh-pulls-open-in-browser () - (interactive) - (let ((info (magit-section-value (magit-current-section)))) - (magit-section-case - (pull (browse-url (magit-gh-pulls-url-for-pull info))) - (unfetched-pull (browse-url (magit-gh-pulls-url-for-pull info)))))) - -(defun magit-gh-pulls-purge-cache () - (let* ((api (magit-gh-pulls-get-api)) - (cache (oref api :cache)) - (repo (magit-gh-pulls-guess-repo))) - (pcache-map cache (lambda (k v) - (when (string-match - (format "/repos/%s/%s/" (car repo) (cdr repo)) - (car k)) - (pcache-invalidate cache k)))))) - - -(defun magit-gh-pulls-build-req (user proj) - (let ((current (or (cdr (magit-get-remote-branch)) - (magit-get-current-branch)))) - (let* ((base - (make-instance 'gh-repos-ref :user (make-instance 'gh-users-user :name user) - :repo (make-instance 'gh-repos-repo :name proj) - :ref (completing-read "Base (master):" '() nil nil nil nil "master"))) - (head - (make-instance 'gh-repos-ref :user (make-instance 'gh-users-user :name user) - :repo (make-instance 'gh-repos-repo :name proj) - :ref (completing-read (format "Head (%s):" current) '() nil nil nil nil current))) - (title (read-string "Title: ")) - (body (read-string "Description: ")) - (req (make-instance 'gh-pulls-request :head head :base base :body body :title title))) - req))) - -(defun magit-gh-pulls-create-pull-request () - (interactive) - (let ((repo (magit-gh-pulls-guess-repo))) - (when repo - (let* ((current-branch (magit-get-current-branch)) - (api (magit-gh-pulls-get-api)) - (user (car repo)) - (proj (cdr repo)) - (req (magit-gh-pulls-build-req user proj)) - (a (gh-pulls-new api user proj req))) - (kill-new (oref (oref a :data) :html-url)))))) - -(defun magit-gh-pulls-reload () - (interactive) - (let ((creds (magit-gh-pulls-guess-repo))) - (if (not (and creds (car creds) (cdr creds))) - (message "Remote repository is not configured or incorrect.") - (magit-gh-pulls-purge-cache) - (gh-pulls-list (magit-gh-pulls-get-api) (car creds) (cdr creds)) - (magit-refresh)))) - -(easy-menu-define magit-gh-pulls-extension-menu - nil - "GitHub Pull Requests extension menu" - '("GitHub Pull Requests" - :visible magit-gh-pulls-mode - ["Reload pull request" magit-gh-pulls-reload] - ["Create pull request branch" magit-gh-pulls-create-branch] - ["Fetch pull request commits" magit-gh-pulls-fetch-commits] - ["Open pull request in browser" magit-gh-pulls-open-in-browser] - )) - -(easy-menu-add-item 'magit-mode-menu - '("Extensions") - magit-gh-pulls-extension-menu) - -(defvar magit-gh-pulls-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "# g b") 'magit-gh-pulls-create-branch) - (define-key map (kbd "# g f") 'magit-gh-pulls-fetch-commits) - (define-key map (kbd "# g g") 'magit-gh-pulls-reload) - (define-key map (kbd "# g m") 'magit-gh-pulls-merge-pull-request) - (define-key map (kbd "# g c") 'magit-gh-pulls-create-pull-request) - (define-key map (kbd "# g o") 'magit-gh-pulls-open-in-browser) - map)) - -(defvar magit-gh-pulls-mode-lighter " Pulls") - -;;;###autoload -(define-minor-mode magit-gh-pulls-mode "Pull requests support for Magit" - :lighter magit-gh-pulls-mode-lighter - :require 'magit-gh-pulls - :keymap 'magit-gh-pulls-mode-map - (or (derived-mode-p 'magit-mode) - (error "This mode only makes sense with magit")) - (if magit-gh-pulls-mode - (magit-add-section-hook - 'magit-status-sections-hook - 'magit-gh-pulls-insert-gh-pulls - 'magit-insert-stashes) - (remove-hook 'magit-status-sections-hook 'magit-gh-pulls-insert-gh-pulls)) - (when (called-interactively-p 'any) - (magit-refresh))) - -;;;###autoload -(defun turn-on-magit-gh-pulls () - "Unconditionally turn on `magit-pulls-mode'." - (magit-gh-pulls-mode 1)) - -(ert-deftest test-magit-gh-pulls-parse-url-git-at () - (should (equal '("sigma" . "magit-gh-pulls") - (magit-gh-pulls-parse-url "git@github.com:sigma/magit-gh-pulls.git")))) - -(ert-deftest test-magit-gh-pulls-parse-url-https () - (should (equal '("sigma" . "magit-gh-pulls") - (magit-gh-pulls-parse-url "https://github.com/sigma/magit-gh-pulls.git")))) - -(ert-deftest test-magit-gh-pulls-parse-url-https () - (should (equal '("sigma" . "magit-gh-pulls") - (magit-gh-pulls-parse-url "https://github.com/sigma/magit-gh-pulls/")))) - -(ert-deftest test-magit-gh-pulls-parse-url-http () - (should (equal '("sigma" . "magit-gh-pulls") - (magit-gh-pulls-parse-url "http://github.com/sigma/magit-gh-pulls.git")))) - -(ert-deftest test-magit-gh-pulls-parse-url-git () - (should (equal '("sigma" . "magit-gh-pulls") - (magit-gh-pulls-parse-url "git://github.com/sigma/magit-gh-pulls.git")))) - -(ert-deftest test-magic-gh-pulls-parse-url-invalid () - (should (eq nil (magit-gh-pulls-parse-url "http://google.com")))) - -(ert-deftest test-magic-gh-pulls-parse-url-garbage () - (should (eq nil (magit-gh-pulls-parse-url "08h3fiuandiu")))) - -(provide 'magit-gh-pulls) -;; Local Variables: -;; indent-tabs-mode: nil -;; End: -;;; magit-gh-pulls.el ends here diff --git a/contrib/!source-control/github/packages.el b/contrib/!source-control/github/packages.el index 8d69ef483..472463945 100644 --- a/contrib/!source-control/github/packages.el +++ b/contrib/!source-control/github/packages.el @@ -17,6 +17,7 @@ github-browse-file ;; not up to date ;; helm-gist + magit-gh-pulls )) (defun github/init-gist () @@ -86,3 +87,33 @@ "gfC" 'spacemacs/git-link-commit-copy-url-only) ;; default is to open the generated link (setq git-link-open-in-browser t)))) + +;; magit-gh-pulls has to be loaded via a pre-config hook because the source code +;; makes assumptions about the status of the magit-mode keymaps that are +;; incompatible with the spacemacs' evilification feature. +;; To avoid errors, magit-gh-pulls must be loaded after magit, but before magit +;; is configured by spacemacs. + +(defun github/init-magit-gh-pulls () + (spacemacs|use-package-add-hook magit + :pre-config + (progn + (use-package magit-gh-pulls + :init + (progn + (defun spacemacs/load-gh-pulls-mode () + "Start `magit-gh-pulls-mode' only after a manual request." + (interactive) + (magit-gh-pulls-mode) + (magit-gh-pulls-reload)) + + (defun spacemacs/fetch-gh-pulls-mode () + "Start `magit-gh-pulls-mode' only after a manual request." + (interactive) + (magit-gh-pulls-mode) + (magit-gh-pulls-fetch-commits)) + + (define-key magit-mode-map "#gf" 'spacemacs/fetch-gh-pulls-mode) + (define-key magit-mode-map "#gg" 'spacemacs/load-gh-pulls-mode)) + :config + (spacemacs|diminish magit-gh-pulls-mode "Github-PR")))))