ghstack 0.14.0__tar.gz → 0.16.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ghstack-0.14.0 → ghstack-0.16.0}/PKG-INFO +30 -8
- {ghstack-0.14.0 → ghstack-0.16.0}/README.md +29 -7
- {ghstack-0.14.0 → ghstack-0.16.0}/pyproject.toml +1 -1
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/action.py +4 -4
- ghstack-0.16.0/src/ghstack/async_script.py +57 -0
- ghstack-0.16.0/src/ghstack/checkout.py +94 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/cherry_pick.py +11 -9
- ghstack-0.16.0/src/ghstack/cli.py +602 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/config.py +41 -7
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/forensics.py +2 -2
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/github.py +11 -26
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/github_fake.py +59 -26
- ghstack-0.16.0/src/ghstack/github_real.py +295 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/github_utils.py +44 -12
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/gpg_sign.py +2 -2
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/land.py +53 -24
- ghstack-0.16.0/src/ghstack/log.py +117 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/logs.py +154 -4
- ghstack-0.16.0/src/ghstack/pull.py +270 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/shell.py +52 -30
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/status.py +2 -2
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/submit.py +868 -307
- ghstack-0.16.0/src/ghstack/sync.py +141 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/test_prelude.py +125 -49
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/unlink.py +10 -9
- ghstack-0.14.0/src/ghstack/checkout.py +0 -64
- ghstack-0.14.0/src/ghstack/cli.py +0 -342
- ghstack-0.14.0/src/ghstack/github_real.py +0 -243
- {ghstack-0.14.0 → ghstack-0.16.0}/LICENSE +0 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/__init__.py +0 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/__main__.py +0 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/cache.py +0 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/circleci.py +0 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/circleci_real.py +0 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/diff.py +0 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/git.py +0 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/github_schema.graphql +0 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/py.typed +0 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/rage.py +0 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/trailers.py +0 -0
- {ghstack-0.14.0 → ghstack-0.16.0}/src/ghstack/types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ghstack
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.16.0
|
|
4
4
|
Summary: Stack diff support for GitHub
|
|
5
5
|
Author: Edward Z. Yang
|
|
6
6
|
Author-email: Edward Z. Yang <ezyang@mit.edu>
|
|
@@ -28,7 +28,7 @@ uv tool install ghstack
|
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
ghstack is tested with
|
|
31
|
-
[several different Python versions](https://github.com/ezyang/ghstack/blob/
|
|
31
|
+
[several different Python versions](https://github.com/ezyang/ghstack/blob/main/.github/workflows/test.yml#L13).
|
|
32
32
|
It requires at least Python 3.9.1.
|
|
33
33
|
|
|
34
34
|
## How to setup
|
|
@@ -43,13 +43,30 @@ github_url = github.com
|
|
|
43
43
|
github_oauth = [your_own_token]
|
|
44
44
|
github_username = [your_username]
|
|
45
45
|
remote_name = upstream [if remote is called upstream and not origin]
|
|
46
|
+
automsg = claude [optional; also supports codex]
|
|
46
47
|
```
|
|
47
48
|
|
|
49
|
+
You can toggle generated change descriptions with:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
ghstack config automsg claude
|
|
53
|
+
ghstack config automsg codex
|
|
54
|
+
ghstack config automsg codex --model gpt-5.4
|
|
55
|
+
ghstack config --unset automsg
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
If `automsg` is set to `claude` or `codex`, ghstack writes
|
|
59
|
+
PR context (the local commit message and current PR description) to a temporary
|
|
60
|
+
file and invokes the agent with a prompt to summarize the specific update. The
|
|
61
|
+
patch update itself is included directly in the prompt. For existing PRs the
|
|
62
|
+
patch update is an interdiff against the previously submitted PR patch, not the
|
|
63
|
+
whole PR patch. Explicit `ghstack -m MESSAGE` still overrides this.
|
|
64
|
+
|
|
48
65
|
## How to use
|
|
49
66
|
|
|
50
67
|
Make sure you have write permission to the repo you're opening PR with.
|
|
51
68
|
|
|
52
|
-
Prepare a series of commits on top of
|
|
69
|
+
Prepare a series of commits on top of main, then run `ghstack`. This
|
|
53
70
|
tool will push and create pull requests for each commit on the stack.
|
|
54
71
|
|
|
55
72
|
**How do I stack another PR on top of an existing one?** Assuming
|
|
@@ -61,17 +78,22 @@ run `ghstack` again. If the commit is at the top of your stack,
|
|
|
61
78
|
you can edit it with `git commit --amend`; otherwise, you'll have
|
|
62
79
|
to use `git rebase -i` to edit the commit directly.
|
|
63
80
|
|
|
64
|
-
**How do I rebase?** The obvious way: `git rebase origin/
|
|
81
|
+
**How do I rebase?** The obvious way: `git rebase origin/main`.
|
|
65
82
|
Don't do a `git merge`; `ghstack` will throw a hissy fit if you
|
|
66
83
|
do that. (There's also a more fundamental reason why this
|
|
67
84
|
won't work: since each commit is a separate PR, you have to
|
|
68
85
|
resolve conflicts in *each* PR, not just for the entire stack.)
|
|
69
86
|
|
|
70
|
-
**
|
|
87
|
+
**What if the repository default branch changed?** ghstack caches
|
|
88
|
+
repository metadata in `.git/ghstack-repo-info.json` for the local
|
|
89
|
+
checkout. If ghstack is still using an old default branch name,
|
|
90
|
+
delete that file and rerun ghstack; it will query GitHub again.
|
|
91
|
+
|
|
92
|
+
**How do I start a new feature?** Just checkout main on a new
|
|
71
93
|
branch, and start working on a fresh branch.
|
|
72
94
|
|
|
73
95
|
**How do I merge my changes?** Warning: You will NOT be able to merge these
|
|
74
|
-
commits using the normal GitHub UI, as their branch bases won't be
|
|
96
|
+
commits using the normal GitHub UI, as their branch bases won't be main.
|
|
75
97
|
Use `ghstack land $PR_URL` (or alternatively `ghstack land #PR_NUM`) to land
|
|
76
98
|
a ghstack'ed pull request.
|
|
77
99
|
|
|
@@ -84,10 +106,10 @@ is an end-to-end example of how to do this.
|
|
|
84
106
|
Every commit in your local commit stack gets submitted into a separate
|
|
85
107
|
pull request and pushes commits onto three branches:
|
|
86
108
|
|
|
87
|
-
* `gh/username/1/base` - think of this like "
|
|
109
|
+
* `gh/username/1/base` - think of this like "main": it's the base
|
|
88
110
|
branch that your commit was based upon. It is never force pushed;
|
|
89
111
|
whenever you rebase your local stack, we add merge commits on top of
|
|
90
|
-
base from the true upstream
|
|
112
|
+
base from the true upstream main.
|
|
91
113
|
|
|
92
114
|
* `gh/username/1/head` - this branch is your change, on top of the base
|
|
93
115
|
branch. Like base, it is never force pushed. We open a pull request
|
|
@@ -7,7 +7,7 @@ uv tool install ghstack
|
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
ghstack is tested with
|
|
10
|
-
[several different Python versions](https://github.com/ezyang/ghstack/blob/
|
|
10
|
+
[several different Python versions](https://github.com/ezyang/ghstack/blob/main/.github/workflows/test.yml#L13).
|
|
11
11
|
It requires at least Python 3.9.1.
|
|
12
12
|
|
|
13
13
|
## How to setup
|
|
@@ -22,13 +22,30 @@ github_url = github.com
|
|
|
22
22
|
github_oauth = [your_own_token]
|
|
23
23
|
github_username = [your_username]
|
|
24
24
|
remote_name = upstream [if remote is called upstream and not origin]
|
|
25
|
+
automsg = claude [optional; also supports codex]
|
|
25
26
|
```
|
|
26
27
|
|
|
28
|
+
You can toggle generated change descriptions with:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
ghstack config automsg claude
|
|
32
|
+
ghstack config automsg codex
|
|
33
|
+
ghstack config automsg codex --model gpt-5.4
|
|
34
|
+
ghstack config --unset automsg
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
If `automsg` is set to `claude` or `codex`, ghstack writes
|
|
38
|
+
PR context (the local commit message and current PR description) to a temporary
|
|
39
|
+
file and invokes the agent with a prompt to summarize the specific update. The
|
|
40
|
+
patch update itself is included directly in the prompt. For existing PRs the
|
|
41
|
+
patch update is an interdiff against the previously submitted PR patch, not the
|
|
42
|
+
whole PR patch. Explicit `ghstack -m MESSAGE` still overrides this.
|
|
43
|
+
|
|
27
44
|
## How to use
|
|
28
45
|
|
|
29
46
|
Make sure you have write permission to the repo you're opening PR with.
|
|
30
47
|
|
|
31
|
-
Prepare a series of commits on top of
|
|
48
|
+
Prepare a series of commits on top of main, then run `ghstack`. This
|
|
32
49
|
tool will push and create pull requests for each commit on the stack.
|
|
33
50
|
|
|
34
51
|
**How do I stack another PR on top of an existing one?** Assuming
|
|
@@ -40,17 +57,22 @@ run `ghstack` again. If the commit is at the top of your stack,
|
|
|
40
57
|
you can edit it with `git commit --amend`; otherwise, you'll have
|
|
41
58
|
to use `git rebase -i` to edit the commit directly.
|
|
42
59
|
|
|
43
|
-
**How do I rebase?** The obvious way: `git rebase origin/
|
|
60
|
+
**How do I rebase?** The obvious way: `git rebase origin/main`.
|
|
44
61
|
Don't do a `git merge`; `ghstack` will throw a hissy fit if you
|
|
45
62
|
do that. (There's also a more fundamental reason why this
|
|
46
63
|
won't work: since each commit is a separate PR, you have to
|
|
47
64
|
resolve conflicts in *each* PR, not just for the entire stack.)
|
|
48
65
|
|
|
49
|
-
**
|
|
66
|
+
**What if the repository default branch changed?** ghstack caches
|
|
67
|
+
repository metadata in `.git/ghstack-repo-info.json` for the local
|
|
68
|
+
checkout. If ghstack is still using an old default branch name,
|
|
69
|
+
delete that file and rerun ghstack; it will query GitHub again.
|
|
70
|
+
|
|
71
|
+
**How do I start a new feature?** Just checkout main on a new
|
|
50
72
|
branch, and start working on a fresh branch.
|
|
51
73
|
|
|
52
74
|
**How do I merge my changes?** Warning: You will NOT be able to merge these
|
|
53
|
-
commits using the normal GitHub UI, as their branch bases won't be
|
|
75
|
+
commits using the normal GitHub UI, as their branch bases won't be main.
|
|
54
76
|
Use `ghstack land $PR_URL` (or alternatively `ghstack land #PR_NUM`) to land
|
|
55
77
|
a ghstack'ed pull request.
|
|
56
78
|
|
|
@@ -63,10 +85,10 @@ is an end-to-end example of how to do this.
|
|
|
63
85
|
Every commit in your local commit stack gets submitted into a separate
|
|
64
86
|
pull request and pushes commits onto three branches:
|
|
65
87
|
|
|
66
|
-
* `gh/username/1/base` - think of this like "
|
|
88
|
+
* `gh/username/1/base` - think of this like "main": it's the base
|
|
67
89
|
branch that your commit was based upon. It is never force pushed;
|
|
68
90
|
whenever you rebase your local stack, we add merge commits on top of
|
|
69
|
-
base from the true upstream
|
|
91
|
+
base from the true upstream main.
|
|
70
92
|
|
|
71
93
|
* `gh/username/1/head` - this branch is your change, on top of the base
|
|
72
94
|
branch. Like base, it is never force pushed. We open a pull request
|
|
@@ -8,15 +8,15 @@ import ghstack.github_utils
|
|
|
8
8
|
import ghstack.shell
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def main(
|
|
11
|
+
async def main(
|
|
12
12
|
pull_request: str,
|
|
13
13
|
github: ghstack.github.GitHubEndpoint,
|
|
14
14
|
sh: Optional[ghstack.shell.Shell] = None,
|
|
15
15
|
close: bool = False,
|
|
16
16
|
) -> None:
|
|
17
17
|
|
|
18
|
-
params = ghstack.github_utils.parse_pull_request(pull_request)
|
|
19
|
-
pr_result = github.graphql(
|
|
18
|
+
params = await ghstack.github_utils.parse_pull_request(pull_request)
|
|
19
|
+
pr_result = await github.graphql(
|
|
20
20
|
"""
|
|
21
21
|
query ($owner: String!, $name: String!, $number: Int!) {
|
|
22
22
|
repository(name: $name, owner: $owner) {
|
|
@@ -32,7 +32,7 @@ def main(
|
|
|
32
32
|
|
|
33
33
|
if close:
|
|
34
34
|
logging.info("Closing {owner}/{name}#{number}".format(**params))
|
|
35
|
-
github.graphql(
|
|
35
|
+
await github.graphql(
|
|
36
36
|
"""
|
|
37
37
|
mutation ($input: ClosePullRequestInput!) {
|
|
38
38
|
closePullRequest(input: $input) {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import ast
|
|
5
|
+
import asyncio
|
|
6
|
+
import inspect
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Dict, Optional, Sequence
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def run_path(
|
|
13
|
+
path: str,
|
|
14
|
+
*,
|
|
15
|
+
argv: Optional[Sequence[str]] = None,
|
|
16
|
+
globals_: Optional[Dict[str, Any]] = None,
|
|
17
|
+
) -> Dict[str, Any]:
|
|
18
|
+
script_path = str(Path(path))
|
|
19
|
+
script_argv = [script_path, *(argv or ())]
|
|
20
|
+
old_argv = sys.argv
|
|
21
|
+
sys.argv = script_argv
|
|
22
|
+
try:
|
|
23
|
+
source = Path(script_path).read_text()
|
|
24
|
+
namespace: Dict[str, Any] = {
|
|
25
|
+
"__file__": script_path,
|
|
26
|
+
"__name__": "__main__",
|
|
27
|
+
"__package__": None,
|
|
28
|
+
"__builtins__": __builtins__,
|
|
29
|
+
}
|
|
30
|
+
if globals_ is not None:
|
|
31
|
+
namespace.update(globals_)
|
|
32
|
+
code = compile(
|
|
33
|
+
source,
|
|
34
|
+
script_path,
|
|
35
|
+
"exec",
|
|
36
|
+
flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT,
|
|
37
|
+
)
|
|
38
|
+
result = eval(code, namespace)
|
|
39
|
+
if inspect.isawaitable(result):
|
|
40
|
+
await result
|
|
41
|
+
return namespace
|
|
42
|
+
finally:
|
|
43
|
+
sys.argv = old_argv
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def main(argv: Optional[Sequence[str]] = None) -> None:
|
|
47
|
+
parser = argparse.ArgumentParser(
|
|
48
|
+
description="Run a Python script with top-level await support."
|
|
49
|
+
)
|
|
50
|
+
parser.add_argument("script")
|
|
51
|
+
parser.add_argument("args", nargs=argparse.REMAINDER)
|
|
52
|
+
ns = parser.parse_args(argv)
|
|
53
|
+
asyncio.run(run_path(ns.script, argv=ns.args))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
main()
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
import re
|
|
6
|
+
from typing import Iterable
|
|
7
|
+
|
|
8
|
+
import ghstack.github
|
|
9
|
+
import ghstack.github_utils
|
|
10
|
+
import ghstack.shell
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def _fetch_refs(
|
|
14
|
+
sh: ghstack.shell.Shell, *, remote_name: str, refs: Iterable[str]
|
|
15
|
+
) -> None:
|
|
16
|
+
refspecs = [
|
|
17
|
+
f"+refs/heads/{ref}:refs/remotes/{remote_name}/{ref}"
|
|
18
|
+
for ref in sorted(set(refs))
|
|
19
|
+
]
|
|
20
|
+
await sh.agit("fetch", "--prune", remote_name, *refspecs)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
async def main(
|
|
24
|
+
pull_request: str,
|
|
25
|
+
github: ghstack.github.GitHubEndpoint,
|
|
26
|
+
sh: ghstack.shell.Shell,
|
|
27
|
+
remote_name: str,
|
|
28
|
+
same_base: bool = False,
|
|
29
|
+
) -> None:
|
|
30
|
+
|
|
31
|
+
params = await ghstack.github_utils.parse_pull_request(
|
|
32
|
+
pull_request, sh=sh, remote_name=remote_name
|
|
33
|
+
)
|
|
34
|
+
head_ref_task = asyncio.ensure_future(github.get_head_ref(**params))
|
|
35
|
+
|
|
36
|
+
if same_base:
|
|
37
|
+
repo_info_task = asyncio.ensure_future(
|
|
38
|
+
ghstack.github_utils.get_github_repo_info(
|
|
39
|
+
github=github,
|
|
40
|
+
sh=sh,
|
|
41
|
+
repo_owner=params["owner"],
|
|
42
|
+
repo_name=params["name"],
|
|
43
|
+
github_url=params["github_url"],
|
|
44
|
+
remote_name=remote_name,
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
head_ref, repo_info = await asyncio.gather(head_ref_task, repo_info_task)
|
|
48
|
+
else:
|
|
49
|
+
head_ref = await head_ref_task
|
|
50
|
+
repo_info = None
|
|
51
|
+
|
|
52
|
+
orig_ref = re.sub(r"/head$", "/orig", head_ref)
|
|
53
|
+
if orig_ref == head_ref:
|
|
54
|
+
logging.warning(
|
|
55
|
+
"The ref {} doesn't look like a ghstack reference; "
|
|
56
|
+
"delegating to gh pr checkout".format(head_ref)
|
|
57
|
+
)
|
|
58
|
+
await sh.ash("gh", "pr", "checkout", pull_request.lstrip("#"))
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
# TODO: Handle remotes correctly too (so this subsumes hub)
|
|
62
|
+
|
|
63
|
+
# If --same-base is specified, check if checkout would change the merge-base
|
|
64
|
+
if same_base:
|
|
65
|
+
assert repo_info is not None
|
|
66
|
+
default_branch = repo_info["default_branch"]
|
|
67
|
+
default_branch_ref = f"{remote_name}/{default_branch}"
|
|
68
|
+
|
|
69
|
+
# Get current merge-base with default branch
|
|
70
|
+
current_base = await sh.agit("merge-base", default_branch_ref, "HEAD")
|
|
71
|
+
else:
|
|
72
|
+
current_base = None
|
|
73
|
+
default_branch_ref = None
|
|
74
|
+
|
|
75
|
+
refs_to_fetch = [orig_ref]
|
|
76
|
+
if same_base:
|
|
77
|
+
assert repo_info is not None
|
|
78
|
+
refs_to_fetch.append(repo_info["default_branch"])
|
|
79
|
+
await _fetch_refs(sh, remote_name=remote_name, refs=refs_to_fetch)
|
|
80
|
+
|
|
81
|
+
# If --same-base is specified, check what the new merge-base would be
|
|
82
|
+
if same_base:
|
|
83
|
+
assert default_branch_ref is not None
|
|
84
|
+
assert current_base is not None
|
|
85
|
+
target_ref = remote_name + "/" + orig_ref
|
|
86
|
+
new_base = await sh.agit("merge-base", default_branch_ref, target_ref)
|
|
87
|
+
|
|
88
|
+
if current_base != new_base:
|
|
89
|
+
raise RuntimeError(
|
|
90
|
+
f"Checkout would change merge-base from {current_base[:8]} to {new_base[:8]}, "
|
|
91
|
+
f"aborting due to --same-base flag"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
await sh.agit("checkout", remote_name + "/" + orig_ref)
|
|
@@ -8,32 +8,34 @@ import ghstack.github_utils
|
|
|
8
8
|
import ghstack.shell
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def main(
|
|
11
|
+
async def main(
|
|
12
12
|
pull_request: str,
|
|
13
13
|
github: ghstack.github.GitHubEndpoint,
|
|
14
14
|
sh: ghstack.shell.Shell,
|
|
15
15
|
remote_name: str,
|
|
16
16
|
stack: bool = False,
|
|
17
|
+
no_fetch: bool = False,
|
|
17
18
|
) -> None:
|
|
18
19
|
|
|
19
|
-
params = ghstack.github_utils.parse_pull_request(
|
|
20
|
+
params = await ghstack.github_utils.parse_pull_request(
|
|
20
21
|
pull_request, sh=sh, remote_name=remote_name
|
|
21
22
|
)
|
|
22
|
-
head_ref = github.get_head_ref(**params)
|
|
23
|
+
head_ref = await github.get_head_ref(**params)
|
|
23
24
|
orig_ref = re.sub(r"/head$", "/orig", head_ref)
|
|
24
25
|
if orig_ref == head_ref:
|
|
25
26
|
logging.warning(
|
|
26
27
|
"The ref {} doesn't look like a ghstack reference".format(head_ref)
|
|
27
28
|
)
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
if not no_fetch:
|
|
31
|
+
await sh.agit("fetch", "--prune", remote_name)
|
|
30
32
|
|
|
31
33
|
if stack:
|
|
32
34
|
# Cherry-pick the entire stack from merge-base to the commit
|
|
33
35
|
remote_orig_ref = remote_name + "/" + orig_ref
|
|
34
36
|
|
|
35
37
|
# Find the merge-base with the main branch
|
|
36
|
-
repo_info = ghstack.github_utils.get_github_repo_info(
|
|
38
|
+
repo_info = await ghstack.github_utils.get_github_repo_info(
|
|
37
39
|
github=github,
|
|
38
40
|
sh=sh,
|
|
39
41
|
github_url=params["github_url"],
|
|
@@ -42,11 +44,11 @@ def main(
|
|
|
42
44
|
main_branch = f"{remote_name}/{repo_info['default_branch']}"
|
|
43
45
|
|
|
44
46
|
# Get merge-base between the commit and main branch
|
|
45
|
-
merge_base = sh.
|
|
47
|
+
merge_base = (await sh.agit("merge-base", main_branch, remote_orig_ref)).strip()
|
|
46
48
|
|
|
47
49
|
# Get all commits from merge-base to the target commit
|
|
48
50
|
commit_list = (
|
|
49
|
-
sh.
|
|
51
|
+
(await sh.agit("rev-list", "--reverse", f"{merge_base}..{remote_orig_ref}"))
|
|
50
52
|
.strip()
|
|
51
53
|
.split("\n")
|
|
52
54
|
)
|
|
@@ -56,10 +58,10 @@ def main(
|
|
|
56
58
|
|
|
57
59
|
logging.info(f"Cherry-picking {len(commit_list)} commits from stack")
|
|
58
60
|
for commit in commit_list:
|
|
59
|
-
sh.
|
|
61
|
+
await sh.agit("cherry-pick", commit)
|
|
60
62
|
logging.info(f"Cherry-picked {commit}")
|
|
61
63
|
else:
|
|
62
64
|
# Cherry-pick just the single commit
|
|
63
65
|
remote_orig_ref = remote_name + "/" + orig_ref
|
|
64
|
-
sh.
|
|
66
|
+
await sh.agit("cherry-pick", remote_orig_ref)
|
|
65
67
|
logging.info(f"Cherry-picked {orig_ref}")
|