Hacker News new | past | comments | ask | show | jobs | submit login

I usually understand "squash" to mean "bundle everything in a PR into a single commit". Which can indeed break bisect workflows.

There's a middle ground though: rebase your commits, but not necessarily into a single one. Before I submit a PR, I rebase my branch (which has lots of small commits, some of which undo previous work or are a work-in-progress), and make sure that every commit is as small as it can be without including work that is halfway done (so all tests still succeed), and have a clear description of what they're doing.

I then have both a nice history, and I can bisect to find a problematic commit, or inspect the commit history of a single line to get more context about it.




If you work on a very large projects with many released version and need to cherry-pick fixes to back-port, you'd be very, very, VERY glad that each branch got squashed.

The story told in the article mainly shows to keep your PR focused. You can squash as long as you don't do 2-week-long 37-commits branches.


Perhaps we could 'have it all' if git introduced a way of bundling commits, a bit like code-folding in an IDE. The bundles could be expanded when you need fine-grain detail of development history, but by default would be unexpanded and would behave like a single big commit, avoiding the issue of always seeing overwhelming detail.


git has that, it's called a merge commit. You can use `git log --first-parent` to see them unexpanded and then find what you want to expand.

A lot of higher level tools don't expose this functionality but it's not like it's not there.


Is there any high level tool that does expose that functionality?


Maybe... sure that's fewer commits to cherry pick. But when some reconciliation is needed to make a patch apply to different version branches, it's sure a whole lot easier to manage if the commits are small.


When rebasing my branches, most PRs will still be a single commit, some will be two or three. That's not that much worse than squashing, and far more informative.


I wish more people would naturally come to this conclusion in their careers, but many people I have worked with just don’t care about maturing their git disciplines. All it takes is one teammate who thinks it’s a waste of time to undo the progress of everyone else.

For context, my career has been in web development start ups, which generally reward the cavalier and tolerate the careful.


It's interesting that you note it only takes one person to undo the progress -- by the reverse token, every person who works in this way adds value to the practice and the history, which is why (and how) I've advocated for and to individuals to spend time on it.

Any one person can muddy the history up (by ruining bisect, say), but any one person can also improve it (by leaving good notes and right-sized commits whenever possible).


I notice that I get sloppy when I'm working on a repo with others that are sloppy as well. I guess the reason is that, if looking at the Git history is not useful half the time (because you end up at uninformative commits by other contributors), then you stop looking at the history, and then there's not much reason to leave it in a super-clean state anymore...


> All it takes is one teammate who thinks it’s a waste of time to undo the progress of everyone else.

If a strategy demands perfection, it sounds like wishful thinking honestly.


Oh yeah I completely agree. It’s really not something that gets me down, or makes me loathe working with others, it’s exactly that— wishful thinking


I think it stems from the lack of experience with actually having to backport/revert non-trivial code changes in a large repository used by many people. For most engineers version control is just seen as a mechanism to collect credit for ones work. It's only when you get into these thorny situations do you appreciate the power of git.


My commit history doesn’t always reflect my mental processes in a way I expect others to follow. Particularly when I’m rationalizing committing work in progress before leaving for lunch or before writing experimental code. So rebasing to consolidate a couple commits and reorder some others is helpful.

The commit history is performance art. You don’t need to know that I left out a comma. Or that I tried to make a bit of code easier to read but introduced regressions in the process (that also breaks bisect). You do need to know that I changed this code over here because I changed the code over there and broke something, because I either missed something or you’ll have the same issue the next time.


I admit that it takes a small bit of skill and experience to get to a place with a version control system where doing what you suggest is easy and does not consume meaningful time, but I very strongly feel that it is worth the investment.


Same here, I especially love `git rebase -i <base>` it opens an editor and I have an option to edit specific commits, reword the commit, squash multiple commits together (don't use this one often) or do fixup (which basically merges commit to the previous one)


If you haven’t yet, check out rebase’s --autosquash option, along with git commit --fixup or the commit message directives “fixup!” and “squash!”


I just learned about that recently, but haven't been able to put it in practice yet. The problem I encounter is having to specify which commit to fixup to when calling `--fixup`, which I think means having to look at my commit history. Instead, my process so far includes just describing the commit I want to fixup in my commit message, in a way that makes sense to me when I'm going through the interactive rebase.

Do you have a good way to deal with this?


Unfortunately the best way I’ve found is pulling up the log to copy either the commit hash for --fixup, or the message for fixup!/squash!

My one liner for viewing the git log is: git log --oneline --all --decorate [--graph]

I haven't tried, but I bet HEAD~n works for --fixup, or some quick rev-parse magic (which I don’t know much of).


Yes, that's exactly my process. In fact, I'm not even sure if I could predict what would happen when calling rebase without `-i`. Basically, my shell just autocompletes "git reb" to "git rebase -i origin/master".


Interactive rebase is the only way I do this now too, it’s so intuitive!


I believe that if you don't know interactive rebase, you don't know git ;)

Especially "git rebase -i -r" is an incredible tool.


An easy way to do this is to perform a 'mixed reset' from the tip of your branch back to the mainline commit where you started work. (Use a new branch, so that your original branch "backs up" the work.)

This will leave all of the final changes in the index, "squashing" any churn. From there, individually stage lines/chunks into a small handful of atomic commits for the PR.


It's not fun if you rebase, and realize that every single one of your many commits need to hand-merged.


But you need to deal with those conflicts before merging anyway? And they're a lot easier to deal with in the context of the individual commits than when dealing with one big merge commit, I find.


The issue is that when you rebase, each commit in the chain gets re-applied, one by one. If you change something in an earlier commit that affects later commits, you could end up having to fix the same or similar conflicts several times before the rebase finishes (even with git's conflict resolution cache).

In contrast, if you instead merge, you only have to resolve conflicts once, for the merge commit.

That being said, I tend to be very fastidious about cleaning up my history before pushing, even if it means tedious, repetitive conflict resolution. But I do understand that other people might not think it's worth the trouble.


I think that can be resolved by rebasing onto the first commit of your branch to first make sure all the commits are minimal, and only then to rebase onto your main branch.

That said, I must say that I've personally experienced as a pain, so I don't actually do that.


Agreed, I used to interactively rebase but have since switched to a four step process. If you have conflicts, you'll deal with them all at once when you merge in `origin/master`.

  git fetch

  git merge origin/master

  git reset origin/master

  git commit




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: