ghstack
ghstack provides command-line utilities to manage and submit a stack of Git commits as individual GitHub Pull Requests for review. It simplifies the workflow for projects that rely on sequential, dependent changes. The current version is 0.14.0, and it is actively maintained with a moderate release cadence.
Warnings
- gotcha ghstack requires a GitHub Personal Access Token (PAT) to be set as the `GH_TOKEN` environment variable. Ensure this token has sufficient `repo` scope permissions for creating pull requests, comments, and interacting with repository contents.
- gotcha ghstack is primarily a Command Line Interface (CLI) tool. While its internal modules can be imported, there isn't a widely-used, stable public Python API for programmatic control. Most users interact with it by executing `ghstack` commands via `subprocess.run` or directly from the terminal.
- gotcha Understanding and managing your Git commit stack with `ghstack rebase` is critical. Frequent rebasing is necessary to keep your stack updated with the target branch and to proactively resolve merge conflicts. Neglecting to rebase can lead to complex conflict resolution issues.
- gotcha ghstack modifies your local Git history. Always ensure you have a clean working directory and understand Git operations before using `ghstack` to prevent accidental data loss or difficult recovery situations.
Install
-
pip install ghstack
Imports
- ghstack_app
from ghstack.app import ghstack_app
Quickstart
import subprocess
import os
import shutil
from pathlib import Path
# --- Setup for demonstration ---
# Create a temporary directory and initialize a Git repository
repo_dir = Path("ghstack_test_repo")
if repo_dir.exists():
shutil.rmtree(repo_dir)
repo_dir.mkdir()
os.chdir(repo_dir)
subprocess.run(["git", "init", "-b", "main"], check=True)
subprocess.run(["git", "config", "user.email", "test@example.com"], check=True)
subprocess.run(["git", "config", "user.name", "Test User"], check=True)
subprocess.run(["git", "commit", "--allow-empty", "-m", "Initial commit"], check=True)
# Create a dummy commit stack
(repo_dir / "file1.txt").write_text("Hello from commit 1")
subprocess.run(["git", "add", "file1.txt"], check=True)
subprocess.run(["git", "commit", "-m", "feat: Add file1"], check=True)
(repo_dir / "file2.txt").write_text("Hello from commit 2")
subprocess.run(["git", "add", "file2.txt"], check=True)
subprocess.run(["git", "commit", "-m", "feat: Add file2"], check=True)
# --- ghstack usage (requires GH_TOKEN and a configured Git remote) ---
# IMPORTANT: This part will attempt to interact with GitHub.
# For a successful run, set a valid GitHub Personal Access Token (PAT)
# with 'repo' scope as the GH_TOKEN environment variable, and configure
# a remote named 'origin' pointing to a GitHub repository.
# For this example, we use a dummy token if not set.
github_token = os.environ.get("GH_TOKEN", "dummy_token_for_quickstart_only")
os.environ["GH_TOKEN"] = github_token # ghstack requires this env var
print("\n--- Running ghstack submit (requires valid GH_TOKEN and an upstream remote) ---")
print("If GH_TOKEN is not valid or no upstream is configured, the command will fail.")
try:
# This command attempts to push commits as GitHub Pull Requests.
# In a real scenario, the output would include URLs to the created PRs.
print(f"Attempting to submit stack with GH_TOKEN set: {github_token[:5]}...")
result = subprocess.run(
["ghstack", "submit"],
check=True, # Will raise CalledProcessError if ghstack fails
capture_output=True,
text=True
)
print("ghstack submit command executed successfully.")
print("Output:\n" + result.stdout)
if result.stderr:
print("Errors (if any):\n" + result.stderr)
except subprocess.CalledProcessError as e:
print(f"ghstack submit failed (expected if GH_TOKEN/remote is not set up):")
print(f"Stderr: {e.stderr}")
print(f"Stdout: {e.stdout}")
print("Please configure GH_TOKEN and a Git remote to run ghstack submit successfully.")
finally:
# Clean up dummy token and repository
if github_token == "dummy_token_for_quickstart_only":
del os.environ["GH_TOKEN"]
os.chdir("..")
shutil.rmtree(repo_dir)
print("\n--- ghstack demo finished ---")
print("To use ghstack effectively, ensure a valid GH_TOKEN and a configured git remote.")