gitgitgadget / git

GitGitGadget's Git fork. Open Pull Requests here to submit them to the Git mailing list
https://gitgitgadget.github.io/
Other
213 stars 134 forks source link

rebase --squash feature #1135

Open seregamorph opened 2 years ago

seregamorph commented 2 years ago

Squashing branch (let it be feature) commits into a single commit and rebasing them on top of target branch (let it be master) is one of the most common operations. If there is a single commit, it's trivial, if two - trivial as well (e.g. amend to parent commit first). But in many cases the feature branch can be pretty long and contain merges from target branch as well - standard situation for long PRs and active target branch. So at some point author may want to squash all changes.

TLDR: Introduce a standard concise command:

git checkout feature
git fetch origin master
git rebase --squash origin/master

Why this could be convenient: in case of possible merge commits they will be solved only once, not a nightmare rebasing all commits.

With current available options I do it this way

git checkout feature
git fetch origin master
git merge origin/master --no-edit && git reset --soft origin/master
git commit -m "FEA-123 - new feature"

It's pretty straightforward, but this flow has pitfalls when you try to explain it to someone else. The most critical case is possible merge conflict:

# fails because of the conflict
git merge origin/master --no-edit && git reset --soft origin/master
# resolve conflict, commit
git reset --soft origin/master
# danger here: it could be possible that author did not notice that origin/master was updated (e.g.
# it was implicit background action by GIT UI client)
git commit -m "FEA-123 - new feature and revert some commits from origin/master"

To avoid the case with updated master I always execute this pair:

git merge origin/master --no-edit && git reset --soft origin/master

There is another way to do it

git checkout feature
git fetch origin master
git branch old-feature
git reset --hard origin/master
git merge old-feature --squash --no-edit
# resolve possible conflict, commit

it's already better, but still not perfect - we introduce temporary branch (or store commit hash somehow).

One more way

git checkout feature
git fetch origin master
git merge-base HEAD origin/master
git reset --soft 00_hash_from_merge_base_00 && git commit -m "FEA-123 - new feature"
git rebase origin/master

Nice for geeks.

I understand, that these scenarios can be automated via shell scripts and even custom git commands and even shared via repo. But I still believe it could be a nice feature.

I'm very sorry, if it's a duplicate, or the feature is already here, but I could not find and proof for such idea.

LemmingAvalanche commented 8 months ago

At first I thought that this isn’t needed for git-rebase(1). Rebase is about replaying commits. If you just want to squash commits you can internally use something equivalent of git-commit-tree(1) with the tree of the tip commit and make the parent the base commit. (Or so I’ve answered on StackOverflow.)

But then I thought: why does it matter how git-rebase(1) is implemented (conceptually)? Should you need to know about that in order to guess that it would be “inappropriate” (I can’t seem to find the right word) and then move onto using git-commit-tree (which is kind of like “plumbing” command now I guess? As opposed to before git-commit(1) came along).

People love to squash ranges of commits (perhaps to a fault?). I often open an interactive rebase and just type fixup for a whole range. So yeah, a --squash option should be helpful for many. One streamlined option instead of worrying about the todo editor.

But this is spoken from a position of not knowing anything about the concrete implementation of this subcommand.