4 ms·
Does it fix the current UX issue with Squash & Merge? Right now I manually do "stacked PRs" like this: main <- PR A <- PR B (PR B's merge target branch is PR
by akersten 6mo ago
Does it fix the current UX issue with Squash & Merge?
Right now I manually do "stacked PRs" like this:
main <- PR A <- PR B (PR B's merge target branch is PR A) <- PR C, etc.
If PR B merges first, PR A can merge to main no problems. If PR A merges to main first, fixing PR B is a nightmare. The GitHub UI automatically changes the "target" branch of the PR to main, but instantly conflicts spawn from nowhere. Try to rebase it and you're going to be manually looking at every non-conflicting change that ever happened on that branch, for no apparent reason (yes, the reason is that PR A merging to main created a new merge commit at the head of main, and git just can't handle that or whatever).
So I don't really need a new UI for this, I need the tool to Just Work in a way that makes sense to anyone who wasn't Linus in 1998 when the gospel of rebase was delivered from On High to us unwashed Gentry through his fingertips..
- deleted 6mo ago[deleted]
- xixixao 6mo agoConflicts spawn most likely because PR A was squashed, and once you squash Git doesn't know that PR B's ancestors commits are the same thing as the squashed commit on main. No idea if this feature fixes this. Edit: Hopefully `gh stack sync` does the rebasing correctly (rebase --onto with the PR A's last commit as base)
- akersten 6mo ago> Conflicts spawn most likely because PR A was squashed, and once you squash Git doesn't know that PR B's ancestors commits are the same thing as the squashed commit on main. Yeah, and I kind of see how git gets confused because the squashed commits essentially disappear. But I don't know why the rebase can't be smart when it sees that file content between the eventual destination commit (the squash) is the same as the tip of the branch (instead of rebasing one commit at a time).
- skydhash 6mo agoBecause at first your have this main <- PR A <- PR B Then you'll have main, squashed A \ \-> PR A -> PR B The tip of B is the list of changes of both A and B, while the tip of main is now the squashed version of the changes of A. Unless a branch tracks the end of A in the PR B, It looks like more you want to apply A and B on top of A again. A quick analogy to math main is X A is 3 B is 5 Before you have X + 3 + 5 which was equivalent to X + 8, but then when you squash A on on X, it looks like (X + 3) + (3 + 5) from `main`'s point of view, while from B, it should be X + (3 + 5). So you need to rebase B to remove its 3 so that it can be (X + 3) + 5. Branches only store the commits at the top. The rest is found using the parent metadata in each commits (a linked list. Squashing A does not remove its commits. It creates a new one, and the tip of `main` as its parent and set the new commit as the tip of `main`. But the list of commits in B still refer to the old tip of `main` as their ancestor and still includes the old commits of A. Which is why you can't merge the PR because it would have applies the commits of A twice.
- SkiFire13 6mo agoEven if you tried something like that it will eventually break when other commits are added to main that are not present in PR B, even if those commit don't conflict with neither PR A nor PR B changes.
- gregoryl 6mo agoIf I'm following correctly, the conflicts arise from other commits made to main already - you've implicitly caught branch A up to main, and now you need catch branch B up to main, for a clean merge. I don't see how there is any other way to achieve this cleanly, it's not a git thing, it's a logic thing right?
- Smaug123 6mo agoNo, it's a Git thing arising from squash commits. There are workflows to make it work (I've linked the cleanest one I know that works without force pushing), but ultimately they're basically all hacks. https://www.patrickstevens.co.uk/posts/2023-10-18-squash-stacked-prs/ https://www.patrickstevens.co.uk/posts/2023-10-18-squash-sta...
- punkbit 6mo agoThis is actually a reasonable workflow. Although requires some preparation. I’ll try it out!
- mckn1ght 6mo agoYep that's how I do it if I have to deal with stacked PRs. I also just never use rebase once anything has happened in a PR review that incurs historical state, like reviews or other people checking out the branch (that I know of, anyways). I'll rebase while it's local to keep my branch histories tidy, but I'll merge from upstream once shared things are happening. There are a bunch of tools out there for merging/rebasing entire branch stacks, I use https://github.com/dashed/git-chain https://github.com/dashed/git-chain.
- akersten 6mo agoI've no issue with the logic of needing to update feature branches before merging, that's pretty bread and butter. The specific issue with this workflow is that the "update branch" button for PR B is grayed out because there are these hallucinated conflicts due to the new squash commit. The update branch button works normally when I don't stack the PRs, so I don't know. It just feels like a half baked feature that GitHub automatically changes the PR target branch in this scenario but doesn't automatically do whatever it takes for a 'git merge origin/main' to work.
- pastel8739 6mo agoI agree that this is annoying and unintuitive. But I don’t see the simplest solution here, so: All you need to do is pull main, then do an interactive rebase with the next branch in your stack with ‘git rebase -i main’, then drop all the commits that are from the branch you just merged.
- adregan 6mo agoI typically prefix my commit messages with the ticket number to make it easier to spot the commits to drop.
- claytonjy 6mo agoThis is what i often do, but i have never been able to get many coworkers onboard. In my experience I’d say less than 5% of all software folk i’ve worked with are willing to do an interactive rebase; everyone else finds it too scary
- contravariant 6mo agoOh that's annoying, seems to me there wouldn't have been an issue if you just merged B into A after merging A into main, or the other way around but that already works fine as you pointed out. I mean if you've got a feature set to merge into dev, and it suddenly merges into main after someone merged dev into main then that's very annoying.
- sameenkarim 6mo agoYes, we handle this both in the CLI and server using git rebase --onto git rebase --onto <new_commit_sha_generated_by_squash> <original_commit_sha_from_tip_of_merged_branch> <branch_name> So for ex in this scenario: PR1: main <- A, B (branch1) PR2: main <- A, B, C, D (branch2) PR3: main <- A, B, C, D, E, F (branch3) When PR 1 and 2 are squash merged, main now looks like: S1 (squash of A+B), S2 (squash of C+D) Then we run the following: git rebase --onto S2 D branch3 Which rewrites branch3 to: S1, S2, E, F This operation moves the unique commits from the unmerged branch and replays them on top of the newly squashed commits on the base branch, avoiding any merge conflicts.
- puelocesar 6mo agoThat’s how I’ve been working for years now. Does anyone know how this gh stacks work internally? Does it do the same thing under the hood? I’m conflicted about it, seems like a good convenience, but I wouldn’t want my team to get dependent on an exclusive feature of a single provider
- steveklabnik 6mo ago(You're replying to someone from product at GitHub, they're explaining that's how it works there)
- jd3 6mo agoWe dealt with this headache for 7+ years at my former employer. Thanks so much for this.
- patrickthebold 6mo agoI'm not sure I follow your workflow exactly. If PR B is merged, then I'd expect PR A to already be merged (I'd normally branch off of A to make B.) That said, after the squash merge of A and git fetch origin, you want something like git rebase --update-refs --onto origin/main A C (or whatever the tip of the chain of branches is) The --update-refs will make sure pr B is in the right spot. Of course, you need to (force) push the updated branches. AFAICT the gh command line tool makes this a bit smoother.
- jiveturkey 6mo agoYou "just" need to know the original merge-base of PR B to fix this. github support is not really required for that. To me that's the least valuable part of support for stacked PRs since that is already doable yourself. The github UI may change the target to main but your local working branch doesn't, and that's where you `rebase --onto` to fix it, before push to origin. It's appropriate for github to automatically change the target branch, because you want the diff in the ui to be representative. IIRC gitlab does a much better job of this but this is already achievable. What is actually useful with natively supported stacks is if you can land the entire stack together and only do 1 CI/actions run. I didn't read the announcement to see if it does that. You typically can't do that even if you merge PR B,C,D first because each merge would normally trigger CI. EDIT: i see from another comment (apparently from a github person) that the feature does in fact let you land the entire stack and only needs 1 CI run. wunderbar!
- Phlogistique 6mo agoI made a tool that adresses this precise problem: https://github.com/scortexio/autorestack-action/ https://github.com/scortexio/autorestack-action/ It does some merge magic so that PR B shows the correct diff; and does so without needing to force push, so on your side you can just "git pull" and continue working. Of course I expect this repo to become obsolete when GitHub makes their native stacking public.
- SkiFire13 6mo ago> Try to rebase it and you're going to be manually looking at every non-conflicting change that ever happened on that branch, for no apparent reason My "fix" is to do an interactive rebase of PR B on main and drop all of PR A's commits from PR B in the process. I remember seeing a way to do this automatically, but it requires an option that I never remember. IMO this is kind of the issue with git: a lot of improved workflows sit behind some flags that most people never learn. Interactive rebases work for me because they are one primitive, always working in the same way.