{"id":24387754,"url":"https://github.com/modular/stack-pr","last_synced_at":"2025-05-16T06:05:17.365Z","repository":{"id":249874464,"uuid":"828313886","full_name":"modular/stack-pr","owner":"modular","description":"A tool for working with stacked PRs on github.","archived":false,"fork":false,"pushed_at":"2025-04-06T22:15:54.000Z","size":110,"stargazers_count":343,"open_issues_count":19,"forks_count":15,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-04-08T16:06:08.267Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/modular.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-13T18:48:17.000Z","updated_at":"2025-04-07T09:43:46.000Z","dependencies_parsed_at":"2024-11-04T16:34:31.184Z","dependency_job_id":"ecae747d-8d2b-4511-a994-da906d224675","html_url":"https://github.com/modular/stack-pr","commit_stats":null,"previous_names":["modularml/stack-pr","modular/stack-pr"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modular%2Fstack-pr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modular%2Fstack-pr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modular%2Fstack-pr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modular%2Fstack-pr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/modular","download_url":"https://codeload.github.com/modular/stack-pr/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254478187,"owners_count":22077676,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-19T13:01:04.045Z","updated_at":"2025-05-16T06:05:17.336Z","avatar_url":"https://github.com/modular.png","language":"Python","readme":"# Stacked PRs for GitHub\n\nThis is a command-line tool that helps you create multiple GitHub\npull requests (PRs) all at once, with a stacked order of dependencies.\n\nImagine that we have a change `A` and a change `B` depending on `A`, and we\nwould like to get them both reviewed. Without stacked PRs one would have to\ncreate two PRs: `A` and `A+B`. The second PR would be difficult to review as it\nincludes all the changes simultaneously. With stacked PRs the first PR will\nhave only the change `A`, and the second PR will only have the change `B`. With\nstacked PRs one can group related changes together making them easier to\nreview.\n\nExample:\n\n![StackedPRExample1](https://modular-assets.s3.amazonaws.com/images/stackpr/example_0.png)\n\n## Installation\n\n### Dependencies\n\nThis is a non-comprehensive list of dependencies required by `stack-pr.py`:\n\n- Install `gh`, e.g., `brew install gh` on MacOS.\n- Run `gh auth login` with SSH\n\n\n### Installation with `pipx`\n\nTo install via [pipx](https://pipx.pypa.io/stable/) run:\n\n```bash\npipx install stack-pr\n```\n\n### Manual installation from source\n\nManually, you can clone the repository and run the following command:\n\n```bash\npipx install .\n```\n\n## Usage\n\n`stack-pr` allows you to work with stacked PRs: submit, view, and land them.\n\n### Basic Workflow\n\nThe most common workflow is simple:\n\n1. Create a feature branch from `main`:\n```bash\ngit checkout main\ngit pull\ngit checkout -b my-feature\n```\n\n2. Make your changes and create multiple commits (one commit per PR you want to create)\n```bash\n# Make some changes\ngit commit -m \"First change\"\n# Make more changes\ngit commit -m \"Second change\"\n# And so on...\n```\n\n3. Review what will be in your stack:\n```bash\nstack-pr view  # Always safe to run, helps catch issues early\n```\n\n4. Create/update the stack of PRs:\n```bash\nstack-pr submit\n```\n\u003e **Note**: `export` is an alias for `submit`.\n\n5. To update any PR in the stack:\n- Amend the corresponding commit\n- Run `stack-pr view` to verify your changes\n- Run `stack-pr submit` again\n\n6. To rebase your stack on the latest main:\n```bash\ngit checkout my-feature\ngit pull origin main  # Get the latest main\ngit rebase main       # Rebase your commits on top of main\nstack-pr submit       # Resubmit to update all PRs\n```\n\n7. When your PRs are ready to merge, you have two options:\n\n**Option A**: Using `stack-pr land`:\n```bash\nstack-pr land\n```\nThis will:\n- Merge the bottom-most PR in your stack\n- Automatically rebase your remaining PRs\n- You can run `stack-pr land` again to merge the next PR once CI passes\n\n**Option B**: Using GitHub web interface:\n1. Merge the bottom-most PR through GitHub UI\n2. After the merge, on your local machine:\n   ```bash\n   git checkout my-feature\n   git pull origin main  # Get the merged changes\n   stack-pr submit       # Resubmit the stack to rebase remaining PRs\n   ```\n3. Repeat for each PR in the stack\n\nThat's it!\n\n\u003e **Pro-tip**: Run `stack-pr view` frequently - it's a safe command that helps you understand the current state of your stack and catch any potential issues early.\n\n### Commands\n\n`stack-pr` has four main commands:\n\n- `submit` (or `export`) - create a new stack of PRs from the given set of\n  commits. One can think of this as “push my local changes to the corresponding\n  remote branches and update the corresponding PRs (or create new PRs if they\n  don’t exist yet)”.\n- `view` - inspect the given set of commits and find the linked PRs. This\n  command does not push any changes anywhere and does not change any commits.\n  It can be used to examine what other commands did or will do.\n- `abandon` - remove all stack metadata from the given set of commits. Apart\n  from removing the metadata from the affected commits, this command deletes\n  the corresponding local and remote branches and closes the PRs.\n- `land` - merge the bottom-most PR in the current stack and rebase the rest of\n  the stack on the latest main.\n\nA usual workflow is the following:\n\n```bash\nwhile not ready to merge:\n    make local changes\n    commit to local git repo or amend existing commits\n    create or update the stack with `stack-pr.py submit`\nmerge changes with `stack-pr.py land`\n```\n\nYou can also use `view` at any point to examine the current state, and\n`abandon` to drop the stack.\n\nUnder the hood the tool creates and maintains branches named\n`$USERNAME/stack/$BRANCH_NUM` (the name pattern can be customized via\n`--branch-name-template` option) and embeds stack metadata into commit messages,\nbut you are not supposed to work with those branches or edit that metadata\nmanually. I.e. instead of pushing to these branches you should use `submit`,\ninstead of deleting them you should use `abandon` and instead of merging them\nyou should use `land`.\n\nThe tool looks at commits in the range `BASE..HEAD` and creates a stack of PRs\nto apply these commits to `TARGET`. By default, `BASE` is `main` (local\nbranch), `HEAD` is the git revision `HEAD`, and `TARGET` is `main` on remote\n(i.e. `origin/main`). These parameters can be changed with options `-B`, `-H`,\nand `-T` respectively and accept the standard git notation: e.g. one can use\n`-B HEAD~2`, to create a stack from the last two commits.\n\n### Example\n\nThe first step before creating a stack of PRs is to double-check the changes\nwe’re going to post.\n\nBy default `stack-pr` will look at commits in `main..HEAD` range and will create\na PR for every commit in that range.\n\nFor instance, if we have\n\n```bash\n# git checkout my-feature\n# git log -n 4  --format=oneline\n**cc932b71c** (**my-feature**)        Optimized navigation algorithms for deep space travel\n**3475c898f**                         Fixed zero-gravity coffee spill bug in beverage dispenser\n**99c4cd9a7**                         Added warp drive functionality to spaceship engine.\n**d2b7bcf87** (**origin/main, main**) Added module for deploying remote space probes\n\n```\n\nThen the tool will consider the top three commits as changes, for which we’re\ntrying to create a stack.\n\n\u003e **Pro-tip**: a convenient way to see what commits will be considered by\n\u003e default is the following command:\n\u003e\n\n```bash\nalias githist='git log --abbrev-commit --oneline $(git merge-base origin/main HEAD)^..HEAD'\n```\n\nWe can double-check that by running the script with `view` command - it is\nalways a safe command to run:\n\n```bash\n# stack-pr view\n...\nVIEW\n**Stack:**\n   * **cc932b71** (No PR): Optimized navigation algorithms for deep space travel\n   * **3475c898** (No PR): Fixed zero-gravity coffee spill bug in beverage dispenser\n   * **99c4cd9a** (No PR): Added warp drive functionality to spaceship engine.\nSUCCESS!\n```\n\nIf everything looks correct, we can now submit the stack, i.e. create all the\ncorresponding PRs and cross-link them. To do that, we run the tool with\n`submit` command:\n\n```bash\n# stack-pr submit\n...\nSUCCESS!\n```\n\nThe command accepts a couple of options that might be useful, namely:\n\n- `--draft` - mark all created PRs as draft. This helps to avoid over-burdening\n  CI.\n- `--draft-bitmask` - mark select PRs in a stack as draft using a bitmask where\n    `1` indicates draft, and `0` indicates non-draft.\n    For example `--draft-bitmask 0010` to make the third PR a draft in a stack\n    of four.\n    The length of the bitmask must match the number of stacked PRs.\n    Overridden by `--draft` when passed.\n- `--reviewer=\"handle1,handle2\"` - assign specified reviewers.\n\nIf the command succeeded, we should see “SUCCESS!” in the end, and we can now\nrun `view` again to look at the new stack:\n\n```python\n# stack-pr view\n...\nVIEW\n**Stack:**\n   * **cc932b71** (#439, 'ZolotukhinM/stack/103' -\u003e 'ZolotukhinM/stack/102'): Optimized navigation algorithms for deep space travel\n   * **3475c898** (#438, 'ZolotukhinM/stack/102' -\u003e 'ZolotukhinM/stack/101'): Fixed zero-gravity coffee spill bug in beverage dispenser\n   * **99c4cd9a** (#437, 'ZolotukhinM/stack/101' -\u003e 'main'): Added warp drive functionality to spaceship engine.\nSUCCESS!\n```\n\nWe can also go to github and check our PRs there:\n\n![StackedPRExample2](https://modular-assets.s3.amazonaws.com/images/stackpr/example_1.png)\n\nIf we need to make changes to any of the PRs (e.g. to address the review\nfeedback), we simply amend the desired changes to the appropriate git commits\nand run `submit` again. If needed, we can rearrange commits or add new ones.\n\n`submit` simply syncs the local changes with the corresponding PRs. This is why\nwe use the same `stack-pr submit` command when we create a new stack, rebase our\nchanges on the latest main, update any PR in the stack, add new commits to the\nstack, or rearrange commits in the stack.\n\nWhen we are ready to merge our changes, we use `land` command.\n\n```python\n# stack-pr land\nLAND\nStack:\n   * cc932b71 (#439, 'ZolotukhinM/stack/103' -\u003e 'ZolotukhinM/stack/102'): Optimized navigation algorithms for deep space travel\n   * 3475c898 (#438, 'ZolotukhinM/stack/102' -\u003e 'ZolotukhinM/stack/101'): Fixed zero-gravity coffee spill bug in beverage dispenser\n   * 99c4cd9a (#437, 'ZolotukhinM/stack/101' -\u003e 'main'): Added warp drive functionality to spaceship engine.\nLanding 99c4cd9a (#437, 'ZolotukhinM/stack/101' -\u003e 'main'): Added warp drive functionality to spaceship engine.\n...\nRebasing 3475c898 (#438, 'ZolotukhinM/stack/102' -\u003e 'ZolotukhinM/stack/101'): Fixed zero-gravity coffee spill bug in beverage dispenser\n...\nRebasing cc932b71 (#439, 'ZolotukhinM/stack/103' -\u003e 'ZolotukhinM/stack/102'): Optimized navigation algorithms for deep space travel\n...\nSUCCESS!\n```\n\nThis command lands the first PR of the stack and rebases the rest. If we run\n`view` command after `land` we will find the remaining, not yet-landed PRs\nthere:\n\n```python\n# stack-pr view\nVIEW\n**Stack:**\n   * **8177f347** (#439, 'ZolotukhinM/stack/103' -\u003e 'ZolotukhinM/stack/102'): Optimized navigation algorithms for deep space travel\n   * **35c429c8** (#438, 'ZolotukhinM/stack/102' -\u003e 'main'): Fixed zero-gravity coffee spill bug in beverage dispenser\n```\n\nThis way we can land all the PRs from the stack one by one.\n\n### Specifying custom commit ranges\n\nThe example above used the default commit range - `main..HEAD`, but you can\nspecify a custom range too. Below are several commonly useful invocations of\nthe script:\n\n```bash\n# Submit a stack of last 5 commits\nstack-pr submit -B HEAD~5\n\n# Use 'origin/main' instead of 'main' as the base for the stack\nstack-pr submit -B origin/main\n\n# Do not include last two commits to the stack\nstack-pr submit -H HEAD~2\n```\n\nThese options work for all script commands (and it’s recommended to first use\nthem with `view` to double check the result). It is possible to mix and match\nthem too - e.g. one can first submit the stack for the last 5 commits and then\nland first three of them:\n\n```bash\n# Inspect what commits will be included HEAD~5..HEAD\nstack-pr view -B HEAD~5\n# Create a stack from last five commits\nstack-pr submit -B HEAD~5\n\n# Inspect what commits will be included into the range HEAD~5..HEAD~2\nstack-pr view -B HEAD~5 -H HEAD~2\n# Land first three PRs from the stack\nstack-pr land -B HEAD~5 -H HEAD~2\n```\n\nNote that generally one doesn't need to specify the base and head branches\nexplicitly - `stack-pr` will figure out the correct range based on the current\nbranch and the remote `main` by default.\n\n## Command Line Options Reference\n\n### Common Arguments\n\nThese arguments can be used with any subcommand:\n\n- `-R, --remote`: Remote name (default: \"origin\")\n- `-B, --base`: Local base branch\n- `-H, --head`: Local head branch (default: \"HEAD\")\n- `-T, --target`: Remote target branch (default: \"main\")\n- `--hyperlinks/--no-hyperlinks`: Enable/disable hyperlink support (default: enabled)\n- `-V, --verbose`: Enable verbose output from Git subcommands (default: false)\n- `--branch-name-template`: Template for generated branch names (default: \"$USERNAME/stack\"). The following variables are supported:\n   - `$USERNAME`: The username of the current user\n   - `$BRANCH`: The current branch name\n   - `$ID`: The location for the ID of the branch. The ID is determined by the order of creation of the branches. If `$ID` is not found in the template, the template will be appended with `/$ID`.\n\n### Subcommands\n\n#### submit (alias: export)\n\nSubmit a stack of PRs.\n\nOptions:\n\n- `--keep-body`: Keep current PR body, only update cross-links (default: false)\n- `-d, --draft`: Submit PRs in draft mode (default: false)\n- `--draft-bitmask`: Bitmask for setting draft status per PR\n- `--reviewer`: List of reviewers for the PRs (default: from $STACK_PR_DEFAULT_REVIEWER or config)\n- `-s, --stash`: Stash all uncommitted changes before submitting the PR\n\n#### land\n\nLand the bottom-most PR in the current stack.\n\nTakes no additional arguments beyond common ones.\n\n#### abandon\n\nAbandon the current stack.\n\nTakes no additional arguments beyond common ones.\n\n#### view\n\nInspect the current stack\n\nTakes no additional arguments beyond common ones.\n\n### Config files\n\nDefault values for command line options can be specified via a config file.\nPath to the config file can be specified via `STACKPR_CONFIG` envvar, and by\ndefault it's assumed to be `.stack-pr.cfg` in the current folder.\n\nAn example of a config file:\n\n```cfg\n[common]\nverbose=True\nhyperlinks=True\ndraft=False\nkeep_body=False\nstash=False\n[repo]\nremote=origin\ntarget=main\nreviewer=GithubHandle1,GithubHandle2\nbranch_name_template=$USERNAME/$BRANCH\n```\n","funding_links":[],"categories":["Python"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmodular%2Fstack-pr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmodular%2Fstack-pr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmodular%2Fstack-pr/lists"}