Commit ac281d7f authored by Jonas Bernoulli's avatar Jonas Bernoulli

magit-{branch,checkout}-pull-request: new commands

parent ce31549d
......@@ -190,6 +190,11 @@ Changes since v2.11.0
request and then visits it in a browser. For now this only supports
Github, but other Git forges will be supported in the future. #3134
* Added new commands `magit-checkout-pull-request' and
`magit-branch-pull-request' to the branch popup. For now they only
support Github, but other Git forges will be supported in the future.
#3134
Fixes since v2.11.0
-------------------
......
......@@ -7,7 +7,7 @@
#+TEXINFO_DIR_CATEGORY: Emacs
#+TEXINFO_DIR_TITLE: Magit: (magit).
#+TEXINFO_DIR_DESC: Using Git from Emacs with Magit.
#+SUBTITLE: for version 2.11.0 (2.11.0-283-g2d82dc0e5+1)
#+SUBTITLE: for version 2.11.0 (2.11.0-301-gce31549d7+1)
#+BIND: ox-texinfo+-before-export-hook ox-texinfo+-update-version-strings
#+TEXINFO_DEFFN: t
......@@ -22,7 +22,7 @@ directly from within Emacs. While many fine Git clients exist, only
Magit and Git itself deserve to be called porcelains.
#+BEGIN_QUOTE
This manual is for Magit version 2.11.0 (2.11.0-283-g2d82dc0e5+1).
This manual is for Magit version 2.11.0 (2.11.0-301-gce31549d7+1).
Copyright (C) 2015-2017 Jonas Bernoulli <jonas@bernoul.li>
......@@ -4043,6 +4043,94 @@ features are available from separate popups.
branch. If FROM is not reachable from ~HEAD~ or is reachable from the
source branch's upstream, then an error is raised.
- Key: b p, magit-branch-pull-request
This command creates and configures a new branch from a Github
pull-request, creating and configuring a new remote if necessary.
The name of the local branch is the same as the name of the remote
branch that you are being asked to merge, unless the contributor
could not be bother to properly name the branch before opening the
pull-request. The most likely such case is when you are being asked
to merge something like "fork/master" into "origin/master". In such
cases the local branch will be named "pr-N", where ~N~ is the
pull-request number.
These variables are always set by this command:
- ~branch.<name>.pullRequest~ is set to the pull-request number.
- ~branch.<name>.description~ is set to the pull-request title.
- ~branch.<name>.rebase~ is set to ~true~ because there should be no
merge commits among the commits in a pull-request.
This command also configures the upstream and the push-remote of the
local branch that it creates.
The branch against which the pull-request was opened, is always used
as the upstream. This makes it easy to see what commits you are
being asked to merge in the section titled something like "Unmerged
into origin/master".
Like for other commands that create a branch it depends on the
option ~magit-branch-prefer-remote-upstream~ whether the remote branch
itself or the respective local branch is used as the upstream, so
this section may also be titled e.g. "Unmerged into master".
When necessary and possible, then the remote pull-request branch is
configured to be used as the push-target. This makes it easy to see
what further changes the contributor has made since you last
reviewed their changes in the section titled something like
"Unpulled from origin/new-feature" or "Unpulled from
fork/new-feature".
- If the pull-request branch is located in the upstream repository,
then it is not necessary to configure the push-target.
(If you have push access to the upstream repository, then it is
usually a good idea to configure that as the push-remote using
~remote.pushDefault~. However even if you are not using that
recommended setting, then it is still easy enough to set
~branch.<name>.pushRemote~ while pushing using ~P p~.)
- If the pull-request branch is located inside a fork, then you
usually are able to push to that branch, because Github by default
allows the recipient of a pull-request to push to the remote
pull-request branch even if it is located in a fork. The
contributor has to explicitly disable this.
- If you are not allowed to push to the pull-request branch on
the fork, then a branch by the same name located in the
upstream repository is configured as the push-target.
- A — sadly rather common — special case is when the contributor
didn't bother to use a dedicated branch for the pull-request.
The most likely such case is when you are being asked to merge
something like "fork/master" into "origin/master". The special
push permission mentioned above is never granted for the branch
that is the repository's default branch, and that is almost
certainly be the case in this scenario.
To enable you to easily push somewhere anyway, the local branch
is named "pr-N" (where ~N~ is the pull-request number) and the
upstream repository is used as the push-remote.
- Finally, if you are allowed to push to the pull-request branch
and the contributor had the foresight to use a dedicated branch,
then the fork is configured as the push-remote.
The push-remote is configured using ~branch.<name>.pushRemote~, even
if the used value is identical to that of ~remote.pushDefault~, just
in case you change the value of the latter later on. Additionally
the variable ~branch.<name>.pullRequestRemote~ is set to the fork
remote.
- Key: b p, magit-checkout-pull-request
This command creates and configures a new branch from a pull
request, the same way ~magit-branch-pull-request~ does. Additionally
it checks out the new branch.
- Key: b x, magit-branch-reset
This command resets a branch, defaulting to the branch at point, to
......
......@@ -30,7 +30,7 @@ General Public License for more details.
@finalout
@titlepage
@title Magit User Manual
@subtitle for version 2.11.0 (2.11.0-283-g2d82dc0e5+1)
@subtitle for version 2.11.0 (2.11.0-301-gce31549d7+1)
@author Jonas Bernoulli
@page
@vskip 0pt plus 1filll
......@@ -52,7 +52,7 @@ directly from within Emacs. While many fine Git clients exist, only
Magit and Git itself deserve to be called porcelains.
@quotation
This manual is for Magit version 2.11.0 (2.11.0-283-g2d82dc0e5+1).
This manual is for Magit version 2.11.0 (2.11.0-301-gce31549d7+1).
Copyright (C) 2015-2017 Jonas Bernoulli <jonas@@bernoul.li>
......@@ -5494,6 +5494,117 @@ matter, all commits between FROM and @code{HEAD} are moved to the new
branch. If FROM is not reachable from @code{HEAD} or is reachable from the
source branch's upstream, then an error is raised.
@kindex b p
@cindex magit-branch-pull-request
@item @kbd{b p} @tie{}@tie{}@tie{}@tie{}(@code{magit-branch-pull-request})
This command creates and configures a new branch from a Github
pull-request, creating and configuring a new remote if necessary.
The name of the local branch is the same as the name of the remote
branch that you are being asked to merge, unless the contributor
could not be bother to properly name the branch before opening the
pull-request. The most likely such case is when you are being asked
to merge something like "fork/master" into "origin/master". In such
cases the local branch will be named "pr-N", where @code{N} is the
pull-request number.
These variables are always set by this command:
@itemize
@item
@code{branch.<name>.pullRequest} is set to the pull-request number.
@item
@code{branch.<name>.description} is set to the pull-request title.
@item
@code{branch.<name>.rebase} is set to @code{true} because there should be no
merge commits among the commits in a pull-request.
@end itemize
This command also configures the upstream and the push-remote of the
local branch that it creates.
The branch against which the pull-request was opened, is always used
as the upstream. This makes it easy to see what commits you are
being asked to merge in the section titled something like "Unmerged
into origin/master".
Like for other commands that create a branch it depends on the
option @code{magit-branch-prefer-remote-upstream} whether the remote branch
itself or the respective local branch is used as the upstream, so
this section may also be titled e.g. "Unmerged into master".
When necessary and possible, then the remote pull-request branch is
configured to be used as the push-target. This makes it easy to see
what further changes the contributor has made since you last
reviewed their changes in the section titled something like
"Unpulled from origin/new-feature" or "Unpulled from
fork/new-feature".
@itemize
@item
If the pull-request branch is located in the upstream repository,
then it is not necessary to configure the push-target.
(If you have push access to the upstream repository, then it is
usually a good idea to configure that as the push-remote using
@code{remote.pushDefault}. However even if you are not using that
recommended setting, then it is still easy enough to set
@code{branch.<name>.pushRemote} while pushing using @code{P p}.)
@item
If the pull-request branch is located inside a fork, then you
usually are able to push to that branch, because Github by default
allows the recipient of a pull-request to push to the remote
pull-request branch even if it is located in a fork. The
contributor has to explicitly disable this.
@itemize
@item
If you are not allowed to push to the pull-request branch on
the fork, then a branch by the same name located in the
upstream repository is configured as the push-target.
@item
A — sadly rather common — special case is when the contributor
didn't bother to use a dedicated branch for the pull-request.
The most likely such case is when you are being asked to merge
something like "fork/master" into "origin/master". The special
push permission mentioned above is never granted for the branch
that is the repository's default branch, and that is almost
certainly be the case in this scenario.
To enable you to easily push somewhere anyway, the local branch
is named "pr-N" (where @code{N} is the pull-request number) and the
upstream repository is used as the push-remote.
@item
Finally, if you are allowed to push to the pull-request branch
and the contributor had the foresight to use a dedicated branch,
then the fork is configured as the push-remote.
@end itemize
The push-remote is configured using @code{branch.<name>.pushRemote}, even
if the used value is identical to that of @code{remote.pushDefault}, just
in case you change the value of the latter later on. Additionally
the variable @code{branch.<name>.pullRequestRemote} is set to the fork
remote.
@end itemize
@kindex b p
@cindex magit-checkout-pull-request
@item @kbd{b p} @tie{}@tie{}@tie{}@tie{}(@code{magit-checkout-pull-request})
This command creates and configures a new branch from a pull
request, the same way @code{magit-branch-pull-request} does. Additionally
it checks out the new branch.
@kindex b x
@cindex magit-branch-reset
@item @kbd{b x} @tie{}@tie{}@tie{}@tie{}(@code{magit-branch-reset})
......
......@@ -178,9 +178,11 @@ and change branch related variables."
(?c "Checkout new branch" magit-branch-and-checkout)
(?n "Create new branch" magit-branch)
(?x "Reset" magit-branch-reset)
(?r "Checkout pull-request" magit-checkout-pull-request)
(?R "Create from pull-request" magit-branch-pull-request)
(?k "Delete" magit-branch-delete)
(?w "Checkout new worktree" magit-worktree-checkout)
(?W "Create new worktree" magit-worktree-branch)
(?k "Delete" magit-branch-delete))
(?W "Create new worktree" magit-worktree-branch))
:default-action 'magit-checkout
:max-action-columns 3
:setup-function 'magit-branch-popup-setup)
......@@ -330,6 +332,79 @@ when using `magit-branch-and-checkout'."
(interactive (magit-branch-read-args "Create and checkout orphan branch"))
(magit-run-git "checkout" "--orphan" args branch start-point))
;;;###autoload
(defun magit-branch-pull-request (pr)
"Create and configure a new branch from a pull-request.
Please see the manual for more information."
(interactive (list (magit-read-pull-request "Branch pull request")))
(let-alist pr
(let* ((upstream (or (--first (magit--github-url-equal
(magit-get "remote" it "url")
.base.repo.ssh_url)
(magit-list-remotes))
(user-error
"Upstream repository %s not available as a remote"
.base.repo.ssh_url)))
(upstream-url (magit-get "remote" upstream "url"))
(remote .head.repo.owner.login)
(branch .head.ref)
(pr-branch branch))
(when (member branch (list .base.ref .base.default_branch))
(setq branch (format "pr-%s" .number)))
(when (magit-branch-p branch)
(user-error "Branch `%s' already exists" branch))
(if (equal .head.repo.full_name
.base.repo.full_name)
(let ((inhibit-magit-refresh t))
(magit-branch branch (concat upstream "/" pr-branch)))
(if (magit-remote-p remote)
(let ((url (magit-get "remote" remote "url"))
(fetch (magit-get-all "remote" remote "fetch")))
(unless (magit--github-url-equal url .head.repo.ssh_url)
(user-error
"Remote `%s' already exists but does not point to %s"
remote url))
(unless (member (format "+refs/heads/*:refs/remotes/%s/*" remote)
fetch)
(magit-call-git "remote" "set-branches"
"--add" remote pr-branch)
(magit-call-git "fetch" remote)))
(magit-call-git
"remote" "add" "-f" "--no-tags"
"-t" pr-branch remote
(cond ((or (string-prefix-p "git@" upstream-url)
(string-prefix-p "ssh://git@" upstream-url))
.head.repo.ssh_url)
((string-prefix-p "https://" upstream-url)
.head.repo.clone_url)
((string-prefix-p "git://" upstream-url)
.head.repo.git_url)
(t (error "%s has an unexpected format" upstream-url)))))
(magit-call-git "branch" branch
(concat remote "/" pr-branch))
(magit-call-git "branch" branch
(concat "--set-upstream-to="
(if magit-branch-prefer-remote-upstream
(concat upstream "/" .base.ref)
.base.ref)))
(magit-set "true" "branch" branch "rebase")
(if (or .locked (not (equal branch pr-branch)))
(magit-set upstream "branch" branch "pushRemote")
(magit-set remote "branch" branch "pushRemote"))
(magit-set remote "branch" branch "pullRequestRemote"))
(magit-set (number-to-string .number) "branch" branch "pullRequest")
(magit-set .title "branch" branch "description")
(magit-refresh)
branch)))
(defun magit-checkout-pull-request (pr)
"Create, configure and checkout a new branch from a pull-request.
Please see the manual for more information."
(interactive (list (magit-read-pull-request "Checkout pull request")))
(magit-checkout
(let ((inhibit-magit-refresh t))
(magit-branch-pull-request pr))))
(defun magit-branch-read-args (prompt)
(let ((args (magit-branch-arguments)))
(if magit-branch-read-upstream-first
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment