Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Programmers can have the best of both worlds. Use granular commits on a local branch and squash merge into shared branches.

That way one gets clean shared history while preserving local work history.



The problem with this approach is the local branch is no longer represented in the shared branch. So if I'm working on a larger feature and want to PR an intermediate part and continue working, I'm in for a bad merge.

If I want to merge my hotfix topic branch into both the release and the master branch, their commits won't match so I can't check if it's present in both automatically.

If a topic branch is left up instead of deleted after a squash merge, I can't even see that master is ahead of it!

Squash is an ugly hack that creates as many problems as it solves.

I desperately wish git had a "group commits" feature that let me manage a cluster of related commits as a single commit for the purposes of history-viewing, reverting, and cherry-picking.


> I desperately wish git had a "group commits" feature that let me manage a cluster of related commits as a single commit for the purposes of history-viewing, reverting, and cherry-picking.

Merge commits work fine for most of that, you just have to adapt to merge UX/commands:

--first-parent (to git log, git annotate, etc) gives you clean history viewing of just your "groups" (your merge commits).

--first-parent even works for bisect allowing you start by figuring out which merge commit brought in a change (and then dig into the merge branch itself if needed as a second bisect).

You can revert or cherry pick merges if you provide the -m (mainline) flag to tell it which parent to consider the mainline (usually the first parent, but not always depending on your intended revert/cherry-pick; it complicates what you need to know about the revert/cherry-pick, but if you are in the process of revert/cherry-picking you should already be figuring out what your mainline is and expecting some possible complications).

I think sometimes the only "problem" with Merge commits is too few pretty UX tools default to a --first-parent view of the git graph and don't themselves provide good tool for picking that -m (mainline) for revert/cherry-pick.


This. I hated working with merge commits until I discovered --first-parent. I'm not sure how it evaded my attention for so long.


I think it is an interesting tools problem: making interesting graph diagrams of the full git log graph is a fun and interesting problem and often looks great in screenshots, but rarely is a particularly "useful" view to most users. It's not as "fun", doesn't produce as many shiny/colorful screenshots, to build --first-parent views by default and introduce (and test) graph drill-down based user experiences.


> I desperately wish git had a "group commits" feature that let me manage a cluster of related commits as a single commit for the purposes of history-viewing, reverting, and cherry-picking.

Maybe you can do something similar to that with git-replace?

Otherwise, you could just tag your commits by putting something in the commit message and use `git grep` to search for those commits. Then you could just build a small helper script that 1. greps for a tag 2. loops over the found commits 3. rewinds them or whatever and 4. squashes the resulting commits.

Dunno, there's a bunch of other ways depending on how exactly you want it. But I agree that grouping commits would be pretty cool :D


But doesn't that assume that you based your larger feature on the local branch, instead of the squashed public version? If you wanted to build on the previous commit, why wouldn't you build on the squashed version?


> If you wanted to build on the previous commit, why wouldn't you build on the squashed version?

alexmingoia proposed:

> That way one gets clean shared history while preserving local work history.

But if you keep on building upon the squashed versions each time you do a merge, you won't have convenient access to the local work history any more.

If you wanted to have access to the local work history you'd need to keep each branch alive still, each time based on the squashed history plus your individual commits up until the next squash.


I suppose you could cherry-pick the new branch onto the squashed version, but I'm still not sure of the value.

You have the local work history already, and that work is done to the point of it being squashed and pushed. Why would you need to keep referencing it to the point that it's inconvenient to have it in another branch?

> you'd need to keep each branch alive still, each time based on the squashed history plus your individual commits up until the next squash.

exactly what I do, and there are maybe just three or four branches I maintain for reference, very few squashed branches are useful a couple of weeks after they are released.


> I desperately wish git had a "group commits" feature that let me manage a cluster of related commits as a single commit for the purposes of history-viewing, reverting, and cherry-picking.

Isn't this what git does by default when you merge your changes? The merge commits group small commits together.


> if I'm working on a larger feature and want to PR an intermediate part and continue working, I'm in for a bad merge.

I usually deal with this by `git rebase`ing your feature branch on top of the shared branch as soon as the PR is merged. You sometimes still get merge conflicts with this approach, but they're always in the code you've just written so they're usually pretty easy to fix.


The problem with rebasing is you might break every one of those commits, which defeats the purpose of chunking the work like that in the first place (since its no longer an honest reflection of what was happening/what worked at each point).


I usually rebase and then squash so that becomes a non-issue. I often find I want to commit more often than the code is in a working state, so I like to be able to erase that history later. I try to keep the whole branch small enough that it'a fine being in one commit.


I don’t really like squashing on merge, since it destroys the commits I may have made. If you’re getting a PR from me and it has more than one commit, I have done that on purpose and you want to keep those separate.


> squash merge into shared branches

Why not just rely on a merge commit instead?


My gripe with merge commits is they don't integrate nicely with `git blame`. If I'm looking through historic commits (to understand why a change was made, or perhaps to debug an issue) I'll often `git blame` the line and diff that commit. If the commit is super granular, I can't get the context of the whole change: I need to dig for the merge commit then look at that, which is faff.

If there's a way that I don't know to show merge commits in blame rather than the actual source change commit, then I'd be all over it. Until then, single (whole) units of change per commit.


`git blame --first-parent` will stick to merge commits in blame output.


Why would you want one author for a merge commit? That merge can have many commits by many different authors.


They can, but

1. The large majority of PRs I've reviewed have a single contributor. Additional contributors are rare. When they do happen, they're often a minority contributor or simply consulting on a PR. It's net neutral when all PRs are squashed in the same pattern.

2. Even with multiple contributors, most features have one leader. It's much easier to talk to that person (and have them delegate) than it is to piece together multiple contributions.


That's an interesting point, but it seems like one among many very good arguments for "right-sized" or "logical" commits, i.e. not 10 similar-shaped bugfixes and also not one-line no-context diffs -- how big should the PR and merge commit be though? Maybe you put the 10 similar-shaped bugfixes together into one PR because they review together easily, but each fix is its own commit, because they all logically stand on their own.

Your use-case is actually the cause of a common rule I've seen at work of requiring a ticket reference in each commit message, which allows looking up the original ticket and associated PRs, along with any commentary & discussion at the time the commit was merged.

On a big code-archeological dig, I often follow a path like run blame -> look at the diff -> pull the ticket reference -> find ticket in issue tracker -> read its description & comments -> find linked PR #'s in the ticket tracker -> open PRs & read diffs and comments -> repeat for linked issues if needed (and then as often as not still end up baffled)

One team actually kept an old redmine VM instance running mostly based on my personal use long after we'd migrated to JIRA, so... I think my approach may be a little unusual! At the least, doing better sized commits would a huge step for every case involving blame.


I personally really dislike merge commits because it makes the tree really difficult to follow in most visualizations. If I'm trying to follow the main branch only to a certain point the graph is polluted with all the "WIP" side branch commits between head and the commit I end up getting to.

It also defaults to causing the main to have a ton of commits with "Merged from XXXX branch" as the summary lines when that's not nearly descriptive enough to quickly find what type of commit I may be looking for.


> If I'm trying to follow the main branch only to a certain point the graph is polluted with all the "WIP" side branch commits between head and the commit I end up getting to.

Using merge commits doesn't have to mean that you don't squash at all. At one extreme, you include every single commit ever made on the branch, and at the other extreme, you squash the entire branch down to a single commit. Using merge commits, you can go for any option inbetween.

> It also defaults to causing the main to have a ton of commits with "Merged from XXXX branch" as the summary lines when that's not nearly descriptive enough to quickly find what type of commit I may be looking for.

Do you often read the log linearly? 99% of the time when I investigate history in Git, it's either through "git blame" or through "git log -S somestring" (search for commits that introduced or removed "somestring"). I rarely, if ever, just read the log as-is.


Use —first-parents and you get a log of only the merge commits themselves, a flat log


I would equally argue there is nothing wrong with the local work history branches being remotely available. If I have multiple branches for one ticket nobody ever seems to care, they just care about which branch is in the PR. Besides, if you have a reasonable web UI for git, it shows it all merged together as one big changeset.


Yes




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

Search: