Skip to content

Commit aebd707

Browse files
Initial add
0 parents  commit aebd707

File tree

2 files changed

+184
-0
lines changed

2 files changed

+184
-0
lines changed

README.md

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
This script is intended to make it easier for project maintainers to check
2+
out GitHub PR branches—including ones from external contributor forks—so
3+
the maintainers can push changes to the branches and thus back to the PRs.
4+
5+
It takes as its sole argument either a GitHub PR URL or just a PR number,
6+
then into the clone where it’s run, checks out the corresponding branch
7+
from the PR contributor’s fork.
8+
9+
Fed just a PR number, it assumes you have an `upstream` or `origin` remote,
10+
and uses that remote's URL to infer which repo the PR was submitted to.
11+
12+
If the remote has a GitHub SSH URL, then it uses an SSH URL for the fork,
13+
which assumes you have write access to the contributor's branch.
14+
15+
### Checking out a PR branch
16+
17+
If your current directory is in a clone of the GitHub `whatwg/html` repo,
18+
to check out the branch for PR #1871:
19+
20+
#### Example using just a PR number
21+
```bash
22+
gpr 1871
23+
```
24+
25+
#### Example using a PR URL
26+
```bash
27+
gpr https://github.com/whatwg/html/pull/1871
28+
```
29+
30+
You should see output similar to this:
31+
32+
```
33+
Getting data for whatwg/html PR #1871...
34+
35+
Author: estark37 (Emily Stark)
36+
Title: Honor srcdoc document referrer policies when set
37+
38+
Preparing for checkout into 'estark37-srcdoc-meta-referrer-policy' local branch.
39+
Adding new remote 'estark37'.
40+
Fetching 'srcdoc-meta-referrer-policy' branch from remote 'estark37'.
41+
Checking out into 'estark37-srcdoc-meta-referrer-policy' local branch.
42+
Switched to a new branch 'estark37-srcdoc-meta-referrer-policy'
43+
Branch estark37-srcdoc-meta-referrer-policy set up to track remote branch srcdoc-meta-referrer-policy from estark37.
44+
```

gpr

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#!/usr/bin/env python3
2+
import json
3+
import re
4+
import subprocess
5+
import sys
6+
from six.moves import urllib
7+
8+
"""Checks out a GitHub pull-request branch
9+
10+
This script is intended to make it easier for project maintainers to check
11+
out GitHub PR branches--including ones from external contributor forks--so
12+
the maintainers can push changes to the branches and thus back to the PRs.
13+
14+
It takes as its sole argument either a GitHub PR URL or just a PR number,
15+
then into the clone where it's run, checks out the corresponding branch
16+
from the PR contributor's fork.
17+
18+
Fed just a PR number, it assumes you have an `upstream` or `origin` remote,
19+
and uses that remote's URL to infer which repo the PR was submitted to.
20+
21+
If the remote has a GitHub SSH URL, then it uses an SSH URL for the fork,
22+
which assumes you have write access to the contributor's branch.
23+
"""
24+
25+
26+
def main():
27+
git("rev-parse") # exit with git error message if not in a git repo
28+
if len(sys.argv) == 1:
29+
usage()
30+
upstreamOrOrginUrl = (git("config", "remote.upstream.url")
31+
or git("config", "remote.origin.url")) \
32+
.decode("utf-8").strip()
33+
ghProtocolIsSsh = False
34+
ghOrgSlashRepo = None
35+
if upstreamOrOrginUrl.startswith("[email protected]:"):
36+
ghProtocolIsSsh = True
37+
ghOrgSlashRepo = upstreamOrOrginUrl[15:][:-4]
38+
elif upstreamOrOrginUrl.startswith("https://github.com/"):
39+
ghOrgSlashRepo = upstreamOrOrginUrl[19:][:-4]
40+
prNumber = sys.argv[1]
41+
if sys.argv[1].startswith("http"):
42+
prNumber = re.match(r'https://github.com/.+/pull/([0-9]+)',
43+
sys.argv[1])[1]
44+
prUrlParts = sys.argv[1].split('/')
45+
ghOrgSlashRepo = "%s/%s" % (prUrlParts[3], prUrlParts[4])
46+
else:
47+
if not upstreamOrOrginUrl:
48+
print("fatal: no 'upstream' or 'origin' remote found")
49+
sys.exit(1)
50+
if not ghOrgSlashRepo:
51+
print("fatal: no 'upstream' or 'origin' url found")
52+
sys.exit(1)
53+
if not prNumber.isdigit():
54+
print("fatal: no usable PR number or PR URL given")
55+
usage()
56+
57+
print("Getting data for %s PR #%s..." % (ghOrgSlashRepo, prNumber))
58+
ghApiBaseUrl = "https://api.github.com/repos/%s" % (ghOrgSlashRepo)
59+
prApiUrl = "%s/pulls/%s" % (ghApiBaseUrl, prNumber)
60+
response = None
61+
pr = None
62+
try:
63+
response = urllib.request.urlopen(urllib.request.Request(prApiUrl))
64+
except urllib.error.HTTPError as e:
65+
if e.code == 404:
66+
print("fatal: no data found at %s" % prApiUrl)
67+
sys.exit(1)
68+
pr = json.load(response)
69+
70+
prAuthor = pr['user']['login']
71+
prOwner = pr['head']['repo']['owner']['login']
72+
prRepoUrl = pr['head']['repo']['clone_url']
73+
if ghProtocolIsSsh:
74+
prRepoUrl = pr['head']['repo']['ssh_url']
75+
prBranch = pr['head']['ref']
76+
prRefSpec = "+refs/heads/%s:refs/remotes/%s/%s" \
77+
% (prBranch, prOwner, prBranch)
78+
79+
author = json.load(urllib.request.urlopen("https://api.github.com/users/%s"
80+
% prAuthor))
81+
82+
print("")
83+
print("Author: %s (%s)" % (prAuthor, author['name']))
84+
print("Title: %s" % pr['title'])
85+
print("")
86+
87+
if (prRepoUrl == upstreamOrOrginUrl):
88+
print("Fetching the '%s' branch..." % prBranch)
89+
git("fetch", "origin", prBranch)
90+
print(git("checkout", prBranch).decode("utf-8").strip())
91+
else:
92+
localBranch = "%s-%s" % (prOwner, prBranch)
93+
print("Preparing for checkout into '%s' local branch." % localBranch)
94+
if git("config", "branch.%s.remote" % localBranch):
95+
print("fatal: there's already a '%s' branch here" % localBranch)
96+
sys.exit(1)
97+
98+
if not git("config", "remote.%s.url" % prOwner):
99+
print("Adding new remote '%s'." % prOwner)
100+
git("remote", "add", "--no-tags", "-t",
101+
prBranch, prOwner, prRepoUrl)
102+
103+
refSpecs = git("config", "--get-all", "remote.%s.fetch" % prOwner) \
104+
.decode("utf-8")
105+
if prRefSpec not in refSpecs:
106+
print("Adding '%s' branch info to remote '%s'."
107+
% (prBranch, prOwner))
108+
git("remote", "set-branches", "--add", prOwner, prBranch)
109+
110+
print("Fetching the '%s' branch from remote '%s'..."
111+
% (prBranch, prOwner))
112+
git("fetch", prOwner, prRefSpec)
113+
print("Checking out into the '%s' local branch." % localBranch)
114+
print(git("checkout", "-b", localBranch, "--track", "%s/%s"
115+
% (prOwner, prBranch)).decode("utf-8").strip())
116+
117+
118+
def git(cmd, *args):
119+
full_cmd = ["git", cmd] + list(args)
120+
try:
121+
return subprocess.check_output(full_cmd, stderr=subprocess.STDOUT)
122+
except subprocess.CalledProcessError as e:
123+
if e.output.strip():
124+
print(e.output)
125+
sys.exit(1)
126+
127+
128+
def usage():
129+
print("Usage:")
130+
print(" %s PR_NUMBER | PR_URL" % sys.argv[0])
131+
sys.exit(1)
132+
133+
134+
if __name__ == "__main__":
135+
try:
136+
retcode = main()
137+
except Exception:
138+
raise
139+
else:
140+
sys.exit(retcode)

0 commit comments

Comments
 (0)