Skip to content

Commit 5e2ed8e

Browse files
committed
Make submit work with 'git rebase -i'
1 parent b569b3d commit 5e2ed8e

3 files changed

Lines changed: 39 additions & 10 deletions

File tree

src/stack_pr/cli.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151

5252
import argparse
5353
import configparser
54+
import contextlib
5455
import json
5556
import logging
5657
import os
@@ -526,6 +527,24 @@ def draft_bitmask_type(value: str) -> list[bool]:
526527
return [bool(int(bit)) for bit in value]
527528

528529

530+
@contextlib.contextmanager
531+
def maybe_stash_interactive_rebase() -> Iterator[None]:
532+
"""
533+
If the user is in the middle of an interactive rebase, we stash the
534+
rebase state so that we can restore it later. This is useful when
535+
the user is trying to submit only part of their commit history.
536+
"""
537+
if os.path.exists(".git/rebase-merge"):
538+
try:
539+
assert not os.path.exists(".git/rebase-merge-stashed")
540+
os.rename(".git/rebase-merge", ".git/rebase-merge-stashed")
541+
yield
542+
finally:
543+
os.rename(".git/rebase-merge-stashed", ".git/rebase-merge")
544+
else:
545+
yield
546+
547+
529548
# ===----------------------------------------------------------------------=== #
530549
# SUBMIT
531550
# ===----------------------------------------------------------------------=== #
@@ -1500,13 +1519,14 @@ def main() -> None: # noqa: PLR0912
15001519
common_args = deduce_base(common_args)
15011520

15021521
if args.command in ["submit", "export"]:
1503-
command_submit(
1504-
common_args,
1505-
draft=args.draft,
1506-
reviewer=args.reviewer,
1507-
keep_body=args.keep_body,
1508-
draft_bitmask=args.draft_bitmask,
1509-
)
1522+
with maybe_stash_interactive_rebase():
1523+
command_submit(
1524+
common_args,
1525+
draft=args.draft,
1526+
reviewer=args.reviewer,
1527+
keep_body=args.keep_body,
1528+
draft_bitmask=args.draft_bitmask,
1529+
)
15101530
elif args.command == "land":
15111531
command_land(common_args)
15121532
elif args.command == "abandon":

src/stack_pr/git.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def get_current_branch_name(repo_dir: Path | None = None) -> str:
8383
repo_dir: path to the repo. Defaults to the current working directory.
8484
8585
Returns:
86-
The name of the branch currently checked out, or "HEAD" if the repo is
86+
The name of the branch currently checked out, or a hash if the repo is
8787
in a 'detached HEAD' state
8888
8989
Raises:
@@ -92,9 +92,17 @@ def get_current_branch_name(repo_dir: Path | None = None) -> str:
9292
"""
9393

9494
try:
95-
return get_command_output(
95+
result = get_command_output(
9696
["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=repo_dir
9797
).strip()
98+
if result == "HEAD":
99+
# Detached HEAD state, return a hash so we can cleanup after ourselves properly, since
100+
# git checkout HEAD
101+
# is a no-op.
102+
result = get_command_output(
103+
["git", "rev-parse", "HEAD"], cwd=repo_dir
104+
).strip()
105+
return result
98106
except subprocess.CalledProcessError as e:
99107
if e.returncode == GIT_NOT_A_REPO_ERROR:
100108
raise GitError("Not inside a valid git repository.") from e

src/stack_pr/shell_commands.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ def run_shell_command(
4545
raise ValueError("shell support has been removed")
4646
_ = subprocess.list2cmdline(cmd)
4747
if quiet:
48-
kwargs.update({"stdout": subprocess.DEVNULL, "stderr": subprocess.DEVNULL})
48+
# Use pipes so errors result in usable error messages
49+
kwargs.update({"stdout": subprocess.PIPE, "stderr": subprocess.PIPE})
4950
logger.debug("Running: %s", cmd)
5051
return subprocess.run(list(map(str, cmd)), **kwargs, check=check)
5152

0 commit comments

Comments
 (0)