This repository was archived by the owner on Jan 19, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cf9e18a
commit 0af81b5
Showing
8 changed files
with
79 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,82 @@ | ||
# gh-stack | ||
|
||
I use this tool to help managed stacked pull requests on Github, which are notoriously difficult to manage manually. Here are a few examples of the kind of things I'm talking about: | ||
|
||
- https://unhashable.com/stacked-pull-requests-keeping-github-diffs-small | ||
- https://stackoverflow.com/questions/26619478/are-dependent-pull-requests-in-github-possible | ||
- https://gist.github.com/Jlevyd15/66743bab4982838932dda4f13f2bd02a | ||
|
||
This tool assumes that all PRs in a single "stack" all have a unique identifier in their title (I typically use a Jira ticket number for this). It then looks for all PRs containing this containing this identifier and builds a dependency graph in memory. This can technically support a "branched stack" instead of a single chain, but I haven't really tried the latter style (which seems much more complex). | ||
|
||
With this graph built up, the tool can: | ||
|
||
- Add a markdown table to the PR description (idempotently) of each PR in the stack describing _all_ PRs in the stack. | ||
- Log a simple list of all PRs in the stack (+ dependencies) to stdout. | ||
- Emit a bash script that can update all PRs in the stack. | ||
- This generally happens in the event of: | ||
- The PR at the base of the stack is merged, leaving all the remaining PRs in a conflicted state. | ||
- One of the PRs (not the top of the stack) has a commit added to it, leaving all dependent PRs in a conflicted state. | ||
- The script requires two placeholders to be manually specified before execution. | ||
|
||
## Usage | ||
|
||
```bash | ||
$ export GHSTACK_OAUTH_TOKEN='<personal access token>' | ||
|
||
# Idempotently add a markdown table summarizing the stack | ||
# to the description of each PR in the stack. | ||
$ gh-stack github 'stack-identifier' | ||
|
||
# Same as above, but precede the markdown table with the | ||
# contents of `filename.txt`. | ||
$ gh-stack github 'stack-identifier' filename.txt | ||
|
||
# Print a description of the stack to stdout. | ||
$ gh-stack log 'stack-identifier' | ||
|
||
# Emit a bash script that can update a stack in the case of conflicts. | ||
# WARNING: This script could potentially cause destructive behavior. | ||
$ gh-stack rebase 'stack-identifier' | ||
``` | ||
gh-stack <pattern> | ||
``` | ||
|
||
## Strategy | ||
|
||
Here's a quick summary of the strategy that the bash script described above uses to keep the stack up-to-date. | ||
|
||
Let's use this stack as an example: | ||
|
||
 | ||
|
||
## Feature Changes | ||
|
||
In the first case, let's assume that "feature part 1" had some changes added to it in the form of a commit; this leaves parts 2 & 3 in a conflicted state: | ||
|
||
 | ||
|
||
The script requires that you pass in a `PREBASE` (which is essentially the boundary for the feature part you're operating on, the last/oldest commit in feature-part-2 in this case). | ||
An initial `TO` ref is also required, which is the point upon which you want to rebase the rest of the stack. In this case, that ref is `remote/feature-part-1`). | ||
|
||
The script executes a single step, we now have this intermediate state: | ||
|
||
 | ||
|
||
The script completes execution, and we now have this final state with the entire stack updated/recreated: | ||
|
||
 | ||
|
||
## Feature Complete & Merged | ||
|
||
In the second case, let's assume that "feature part 2" is done and has been merged to `develop`: | ||
|
||
 | ||
|
||
This immediately leaves feature parts 2 & 3 in a conflicted state. The script can fix this situation as well. | ||
As before, pass a `PREBASE` (in this case the oldest commit in feature part 2) and an initial `TO` ref to rebase on (in this case `remote/develop`). | ||
|
||
Once the script executes a single step, we're left with: | ||
|
||
 | ||
|
||
And once the script is done: | ||
|
||
 |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.