How to commit part of file in Git
newbeelearn.com
newbeelearn.com
I have lived through multiple examples of this, the biggest one being a bad protocol (that was provably badly designed at the beginning) that got adoption, many tools built around it, and ten years later everybody hates it and keeps complaining but it's so hard to replace because of all the tooling built around it.
I think there is a point to make that better code is often more profitable in the longer term. But the norm (at least where I work) is to swim in crappy code all day and complain about it while producing more of it.
If you use rebase on your own branch, but merge commits on the stable branch (whatever name you use for it), you get both things:
* Small individual commits with the size and ordering that the original author intended on their branch.
* A "commit group" that describes that set of changes for the sake of future maintainers' sanity, and that can be atomically reverted.
More so in PR-based flows. You can have `git log --first-parent` display only the PR merge commits[1], and `git log --no-merges` display the linear history.
[1]: Assuming you actually put useful info on those commits. If you use GitHub, those merge commits can be set to default to PR title and description.
Surely you'd revert to the last good release? I'm sure you could revert a single commit and re-release, but it seems slower (you have to find the right commit to revert, and make sure that the result works correctly) and riskier (since now you're effectively rolling forward to a combination of commits that's never been deployed before)
If you’re very diligent about making good atomic commits that always pass all test that can work, but I find that squashing PRs (and PRs being relatively small) is a very good trade off
I've recently started to like squashing after usually preferring rebase merges, because with squashes I have an easy option to rewrite the commit message and edit out all of those meaningless "fix typo", "improve" and "now actually working" lines...
And why couldn't you rewrite the commit messages while rebasing?
EDIT: oh, you mean from the GitHub webUI, I presume. Got it.
Yes, exactly, while reviewing and merging other peoples PRs. Technically I could still rewrite in other ways, but while squashing in the web UI was the most comfortable workflow.
So the idea is really "what's the point of having a history full of broken state?".
> It must be impossible to understand commits' diffs with the changes all squashed together.
This would be a hint that your PR was too big and addressing more than one thing.
I rebase commits so they don't break the build but the history remains clean and incremental. Selective fixups and so on isn't the same as squashing everything into a single commit.
> This would be a hint that your PR was too big and addressing more than one thing.
I don't think so. Sure, that can be true, but squashes can also simply lose vital history. Suppose you remove a file and then replace it with code copied and modified from another file. If you then squash that, all Git will say is you made a massive edit to the file.
Sure, and that's fine. The idea of the squash workflow is that they don't expect that. It's just different, and that's the rationale behind it :-).
> all Git will say is you made a massive edit to the file.
Which IMO is exactly what happened in this case xD. But again... whatever floats your boat, I was just talking from the point of view of a squash workflow.
And the answer is that you don't; each commit is individually testable and reviewable. Changes requested by reviewers are squashed into the commits and then merged into the project. Unfortunately, while the git command line has "range-diff" to ease review with this workflow, neither GitHub not Gitlab have an equivalent in their UI.
Of course, if your workflow is different, then... well it is different. Doesn't make the "squash workflows" irrational.
Disclaimer: I don't squash PRs.
How does this work in practice? Is every single atomic commit reviewed by someone? When do they review each of those commits? How many commits typically go into a PR?
> Changes requested by reviewers are squashed into the commits and then merged into the project.
So a reviewer finds the appropriate commit that their comment applies to, and then changes the actual commit itself? Who is the author of the commit at that point?
I'm trying to understand what you're talking about, because you seem to have something figured out, for a problem that every team I've worked on struggles with.
1) yes 2) when a PR is submitted 3) it can be a lot for a huge project-wide refactoring, but generally I would say 1 to 5 is typical and up to 20 is not strange.
> So a reviewer finds the appropriate commit that their comment applies to, and then changes the actual commit itself?
No, the author applies the requested change and force-pushes once he has gotten all the requested changes applied.
> because you seem to have something figured out
Thanks! But it's not me—it's how Linux has used git from the beginning, for example. In fact it's the only workflow that is used by projects that still use email instead of GitHub/Gitlab PRs, but (trading some old pain with new pain) it is possible to use it even with the latter. The harder part is marching the review comments to the new patch, which is actually pretty easy to do with emails.
It's quite some work and there's some learning curve. But depending on the project it can be invaluable when debugging. It depends a lot on how much the code can be covered by tests, in particular.
Of course it's got the same end result as doing an interactive rebase & combining all the in-progress commits into a single reviewable unit of change with a good commit message, but it's a bit more automatic.
usually you should err on the side of keeping the commits together, unless you’re close to release, patching a bug in release, etc.
I use `git checkout --patch` mostly when splitting a big commit into smaller commits, retroactively. I start an interactive rebase, chuck 'break' before the big commit, then I start pulling the changes 'forward' from the big commit into the working directory with `git checkout --patch big-commit-hash */path.ext`. Sometimes this works out easier than popping a commit off and re-committing everything seperately, particularly if I already have a commit with a detailed message.
https://github.com/microsoft/vscode/issues/96104
If you staged selected ranges it could change the line endings of the whole file from CRLF to LF. It's very easy to miss since it doesn't show in the normal diff, but then you end up with merge conflicts galore later on.
[alias]
sync = "git squash && git pull --rebase && git pop && git push" # YMMV
Git GUIs are always changing the interface to git. It gives you this weird lockin and islandification. Knowing how to actually use git can give you super powers.Magit's USP is that it's a great interface for those who already use (or are willing to use) `emacs`. `lazygit` is a great interface in its own right.
I use git rebase and git add/reset -p left and right.
I will definitely checkout lazygit though, thank you and others in this HN submission for the mention, looks useful.
But that's the beauty of it: git makes it easy for anyone to use their favourite tool (be it CLI, TUI or GUI) :-).
Something I found that I like about the approach is that sometimes while working on a change, some unrelated code will catch my eye that I want to fix but not as part of my next commit. Being free to make the edit, knowing that I'll see it again later when staging changes, is nice because I would often otherwise forget to go back and revisit the thing I saw. The git add -p sessions then wind up being an opportunity to decide what changes go into each commit, etc. I do burn myself occasionally by having an unrelated edit that is in a hunk that can't be split- I suspect there are tweaks to diff I could make to help with that but have yet to overcome my apathy wrt research.
`git add -p` allows you to "edit [e]" the patch, meaning that you can really decide exactly what should be added. And that's a great opportunity to learn how to manipulate patches, which is an important skill IMO!
https://github.com/tpope/vim-fugitive/blob/master/doc/fugiti...
It's unlikely to remember this feature's existence and benefit from it without habitual usage.
commit.verbose true This adds the whole commit diff in the text editor where you’re writing your commit message, to help you remember what you were doing.
https://jvns.ca/blog/2024/02/16/popular-git-config-options/#...
Regardless I also really like `git add -p`. I don't use it often but it really makes me focus on and consider each hunk. It was the defacto way to do it at an old company I worked at and no, it was not a small startup "hipster" company, we built enterprise ERP software.
(2/5) Stage this hunk [y,n,q,a,d,K,j,J,g,/,e,?]?
Wow, so intuitive! LOL. You really showed him.
This UX is laughably bad. It presents you every hunk, one at a time, without context, and if you want to include some lines and not others, you can't unless all the sub-hunks are exactly one line or you let it launch a text editor and you dick around in the raw diff which is its own domain-specific language.
Why don't people play starcraft like this?
(1/1) Move this zergling [u,d,l,r,b,a,J,g,/,e?]?
The fact that this needs a write-up and discussion is all you need to know. Where is the big how-to for committing partial files in GitHub Desktop? Oh, yeah, nobody needs one because it's obvious and right in front of your face.
If you have no limbs and can't use a mouse to click on things then this UI is great though.
The ‘s’ to split changes is one of my favorite feature.
It takes a few minutes to get used to it, then becomes automatic: add the whole file ‘a’, skip file ‘d’, skip chunk ‘n’, stage chunk ‘y’, split ‘s’, stop here ‘q’, rollback ctrl-c.
The “chunk per chunk” approach is actually really nice, you’re focusing on what exactly you want to get your commit to be about.
You learn it once, that will never change, you’re set for decades.
EDIT:
You have extremely low standards for what constitutes a professional tool these days.
You also didn't address my point. split is brain damaged and doesn't work:
+ assertEquals(byte[].class, p.get0().getClass());
+ assertEquals(String.class, p.get1().getClass());
You try to split this hunk and it won't do it. This task is beyond trivial in any non-retarded GUI.-p makes you more productive because you don't know how to use an electron app or Java app
Oh, I forgot that it probably couldn't be fixed even if they wanted to because if split suddenly started working it would break someone's spacebar heating script.
If you personally prefer a GUI then that’s obviously your choice. Some people do and I doubt you’ll find many people claiming Git has the world’s best UI. It does work everywhere, though, and I doubt it’s any slower than using a GUI for a task like your example. Either way, you can have lines staged or not pretty much as fast as you can read them and decide whether they should be.
It's like you're saying "guys you should use this new hand drill I've discovered! It's so much more productive than digging holes with an awl!" while ignoring the fact that electric drills exist.