[HN Gopher] A Better Git Flow
       ___________________________________________________________________
        
       A Better Git Flow
        
       Author : dnilasor
       Score  : 87 points
       Date   : 2022-01-19 18:50 UTC (4 hours ago)
        
 (HTM) web link (render.com)
 (TXT) w3m dump (render.com)
        
       | ltbarcly3 wrote:
       | The use of `git reset` is a little silly and is playing with fire
       | for no absolutely no benefit.
       | 
       | If you want to follow the pattern described here, create a new
       | branch and do `git checkout the_feature_branch -- $filename` and
       | pull the changes from the branch you did the work in into the new
       | branch, making commits as described in the article. You can even
       | do `git diff --numstat $feature_branch $new_branch` to see what
       | has changed.
        
         | geoffmanning wrote:
         | Trying to understand where the danger is with git reset. To
         | avoid a very harmless and easily reversible command, you
         | complicated the process with another branch. The fact you feel
         | it is dangerous tells me that you don't have a strong
         | understanding of how git works. It's always kind of funny to me
         | to see how strong of an opinion devs get over how to use git
         | when it's clear they don't really understand the toolset. It
         | took a while to get all the devs at my last company to
         | understand how simple it is to rebase/reset, and write some
         | publish worthy commits, but once they all understood the value
         | it adds, they all became converts and did the evangelism job
         | for me the next time we hired a dev. I recommend reading Pro
         | Git by Scott Chacon. Git is pretty dang amazing.
        
         | Rolcol wrote:
         | > and is playing with fire for no absolutely no benefit
         | 
         | How so? The previous commits are still recoverable from the
         | reflog.
        
           | deadbunny wrote:
           | I've found most people can barely use git, yet alone fucking
           | with reflog
        
       | krainboltgreene wrote:
       | I hate to see git guides where they tell the user to make commits
       | that are literally the description of the diff rather than the
       | _why_ of the change.
       | 
       | "Added new styles to navigation"
       | 
       | No duh, that's what the diff shows, but that doesn't tell us why
       | you made the change, and that's the part we're going to struggle
       | to remember in 6 months.
        
       | jayd16 wrote:
       | I hate Perforce (most because of bugs) BUT the in progress CL
       | workflow is better than this.
       | 
       | Git should introduce stages and allow you to have any number of
       | stages. Git should also have some porcelain around stashing all
       | but one stage and restoring all etc etc.
        
       | hill613 wrote:
       | And if you have PR comments/commits you do this everytime? Just
       | squash
        
       | mattwad wrote:
       | My two cents: I just always squash commits when merging to
       | master/main. That way the main branch has clean commit messages
       | and it's easy to revert later. I think cherry-picking commits
       | after the fact can also be very time-consuming, especially if you
       | do a lot of refactoring or "clean as you go" as I like to call
       | it. However, I could see it being worth it for open-source.
        
         | derekp7 wrote:
         | When you go grocery shopping, you can A) create a list of
         | dinner ideas and the ingredients that you need to pick up,
         | group them in the order that they appear in the store, and head
         | straight to the aisles to get the items. Or B) go in with the
         | list in mind, but wander through the whole store and you may
         | find something else interesting to pick up. Or C) not have any
         | but the most vague dinner ideas, go without a list, and create
         | a week's worth of dinners as you browse picking up interesting
         | items and figuring out what to do with them.
         | 
         | All depends on if you want the most efficient approach, or if
         | you want serendipity to strike.
        
         | okl wrote:
         | > easy to revert later
         | 
         | Depends on what you want to revert? git revert accepts more
         | than one commit!
        
           | leni536 wrote:
           | You can even revert a merge commit, if you specify the
           | parent.
        
       | joelmbell wrote:
       | I think this makes a lot of sense. I usually use git in a
       | completely different way when developing than I do when I'm
       | pushing up code for others into a shared branch.
       | 
       | When developing my priority is to easily get back to a last known
       | working state. This allows me to try out risky changes, and throw
       | them all away if it doesn't work out.
       | 
       | When pushing my changes for PR, those working states I saved
       | before may not be the best logically. I usually re-write my
       | commits to break it into more logical chunks, that are easy to
       | revert and easier for other teammates to digest.
        
       | _hao wrote:
       | What a waste of time and effort. Squash when merging to master
       | and be done with it. Every PR/commit merged to master should be a
       | clean logical unit. That means not fixing a bug in a branch where
       | I'm doing something else. Those will be two separate PR's. The
       | second problem shouldn't exist IMO.
        
         | mttjj wrote:
         | Bingo! This strategy has worked well for my org (75+ engineers)
         | for years. If an engineer makes non-related bug fixes in the
         | same branch we require them to revert the change and make a new
         | branch. We also have each GitHub repo configured so the "Squash
         | and Merge" option is the ONLY option available when merging a
         | PR.
         | 
         | I don't care one lick what someone's branch history looks like.
         | If they want to commit every day, or every hour, or after every
         | keystroke - I don't care. All I know is that once the PR is
         | merged, it's all going to be squashed into a logical unit so
         | the `main` commit history will look just fine.
        
         | jcranmer wrote:
         | The problem comes when you have a related set of changes where
         | you both want to see how everything eventually fits together
         | and where you still want to keep small "clean logical units."
         | You can see this kind of thing play out frequently in, for
         | example, Linux changesets, where you might have a 24-patch
         | series of changes that need to go in for a feature.
        
           | laserbeam wrote:
           | There's no such thing as "clean logical units". There's a
           | product you work on. There are bugs. The prodct needs some
           | features, good UX, performance requirements.
           | 
           | Spending effort on managing git is mental effort you don't
           | spend on solving your actual problems. By far the best
           | experience I've ever hadeith git was: everyone works straight
           | on the dev branch, just rebase, fix your stuff, test often,
           | and if you're doing some multi-day work then sure, branch and
           | think it over then merge.
           | 
           | That's it. That's all you need. I've had a million more
           | problems with every attempt at making this process "clean",
           | or "smart". Dumb was by far more efficient, more enjoyable,
           | helped us find and fix bugs faster, and had the shortest time
           | to market ever.
        
             | ninkendo wrote:
             | If you're in a large organization, you're expecting other
             | engineers to make sense of the work you're submitting.
             | Anything that helps them here is a good thing (although
             | it's always a tradeoff.)
             | 
             | The advice I try to live by, is that however messy my work
             | was leading up to a PR, I make sure the end result is
             | something somebody can review without additional context.
             | Commits should have lengthy descriptions of changes that
             | describe the "why" and "how" of a particular change, in a
             | way that makes it easy to digest for a reviewer. Sometimes
             | multiple commits make sense (like if you're renaming a
             | module/class, put that in a single commit, then put the
             | actual code change in the next one), sometimes they're not
             | necessary. But it's worth it to put in the effort here if
             | it means it helps a reviewer, IMO.
        
         | Double_a_92 wrote:
         | This does not work well if you have long-lived branches (i.e.
         | weeks) for more substantial features. Completely squashing it
         | would lose all the granular commits and especially their
         | commmit messages, which might be useful for debugging later on.
        
         | cerved wrote:
         | What's the benefit of pretending everything happened in one
         | discrete commit? I'm not sure I see the value, only information
         | destroyed
        
         | garyrule wrote:
         | This is what we're doing as well. No merged commits to master
        
         | Supermancho wrote:
         | aka Premature process optimization.
         | 
         | Dont make work upfront, that is rarely important. When it is
         | important, find the commit and split it up (as necessary) at
         | that singular point (instead of across all PRs). You have now
         | cut down how much time it takes to make PRs while retaining the
         | same end result.
        
         | ninkendo wrote:
         | I think you underestimate just how much stupid garbage I have
         | in my commit history. It's embarrassing. Even if you squash
         | when merging the PR, the squashed commit message is gonna have
         | a ton of messages that look like "Why the hell isn't this
         | compiling?", "wtf?", "FINALLY PASSES!" etc etc etc.
         | 
         | I could avoid creating those commits in the first place, but
         | asking me to only commit my local changes when I have something
         | intelligent to say about them, is a damn near impossibility for
         | me. I basically use git to "save my work" before I try an
         | approach to something. I reset back if it doesn't work out.
         | Sometimes I reset back again, if the approach that didn't work
         | out turned out to be the least worst option. I create commits
         | to experiment with something, so that I can quickly compare
         | back and forth between two approaches. etc. etc. etc.
         | 
         | I would instead say, that if _you 're_ only making commits when
         | you have something logical to say, you're probably not using
         | git to its fullest potential. You should really go nuts with it
         | IMO, and only bother to sound intelligent in your commit
         | messages once you're ready to do so, and (most importantly) you
         | should _still make commits_ before that happens. Git 's
         | decentralized for a reason, take advantage!
        
           | kristaps wrote:
           | It's true that messy commits happen, that's why squash is
           | there.
           | 
           | Nothing forces you to keep the messy commit messages either -
           | keep the short commit message and make sure it's good, then
           | delete the combined individual commit messages from the long
           | message field, done.
        
             | ninkendo wrote:
             | My point is that I don't want anyone to see my WIP commits
             | in the first place. Squashing only in the end when merging
             | to master means the code reviewers get to see my messy
             | commit history, which is what I'm trying to avoid. (Yes, my
             | commit history is that bad that it's embarrassing. But at
             | least I commit early and often, which has saved my ass more
             | times than I can count.)
        
           | ojkelly wrote:
           | You can remove those messages from the final commit when you
           | squash.
           | 
           | If someone else is doing the merge and you're unsure if they
           | will you can preemptively squash your whole branch so there's
           | only one commit in the pr.
        
             | ninkendo wrote:
             | > If someone else is doing the merge and you're unsure if
             | they will you can preemptively squash your whole branch so
             | there's only one commit in the pr.
             | 
             | I agree! And that's what I do. But OP said that would be "a
             | waste of time and effort", hence my reply.
        
         | nhaehnle wrote:
         | > Every PR/commit merged to master should be a clean logical
         | unit.
         | 
         | The issue is one of review scaling. I wrote a blog post about
         | this a while ago[0], but the gist of it is that those clean
         | logical units are often too small for meaningful high-level
         | reviews of more complex work.
         | 
         | With complex features or refactorings, you're often in a
         | situation where those clean logical units allow reviewers to do
         | a good low-level review (do a check for logic corner cases,
         | style issues, etc.) but they don't allow a high-level review of
         | how all the pieces of the feature work together.
         | 
         | IMHO the most open-source process friendly solution to the
         | issue is to review patch series, where you can review the
         | series as a whole for the big picture, but also dig into
         | individual commits for the details. Building such a patch
         | series requires an approach as described in the article.
         | 
         | (In closed source environments, you _may_ get a good enough
         | approximation of the result with a separate, disciplined
         | software design process.)
         | 
         | [0] http://nhaehnle.blogspot.com/2020/06/they-want-to-be-
         | small-t...
        
       | alkonaut wrote:
       | The article starts by stating a rather obvious problem: if you
       | revert a commit, you might break something because any commit
       | following the reverted one could build on what was added in the
       | reverted commit.
       | 
       | Then the article describes a very elaborate way of... not
       | addressing the problem?
       | 
       | Reset-Cleanup is a decent idea and helps keep a tidy repo which
       | is a good goal. But the revert reason seems contrived.
       | 
       | The problem is going to be later code (from other pull requests)
       | being dependent on the code in my pull request.
       | 
       | If my pull request is reverted then any later change can break.
       | If my PR consists of 5 well separated commits or 3 messsy ones
       | doesn't matter in that scenario.
       | 
       | If someone against all odds wants to revert one individual commit
       | from a merged branch but _not_ revert the whole PR by reverting
       | the merge commit then not only can the feature in the PR stop
       | working (if the feature /bug fix in the PR didn't need that
       | commit to work then why was it there in the first place!?), any
       | later code can break just as it can when PR is reverted as a
       | whole.
       | 
       | This is why I recommend squashing for almost all cases. For
       | really complex features with dozens of commits you can always do
       | a rebase + FF (but in that case all commits should build + pass
       | tests, which is an unrealistic goal in all code bases where a
       | build + test takes hours).
        
       | agd wrote:
       | > do the work first, clean up the commits later
       | 
       | I'm not sure this gives the right impression. Yes
       | rename/squash/interactive rebase if necessary to tidy, however I
       | still believe you should strive to create clear, separate commits
       | as you go.
       | 
       | If you have to regularly make major changes to history before
       | review, I could be a sign that your process/approach is
       | disorganised.
        
         | okl wrote:
         | > If you have to regularly make major changes to history before
         | review, I could be a sign that your process/approach is
         | disorganised.
         | 
         | Right. Next time think before do.
        
         | ninkendo wrote:
         | > If you have to regularly make major changes to history before
         | review, I could be a sign that your process/approach is
         | disorganised.
         | 
         | Of course my process is disorganized! I'm an extremely
         | disorganized person. I like to try out wild tangents in my
         | coding, experimenting with some crazy idea or another and
         | saving my place before and after I do so. I even have a
         | separate .txt file of stream-of-consciousness writing about my
         | coding, to keep track of the tremendous amount of complexity I
         | have to deal with on a regular basis.
         | 
         | My reflog reads like a personal diary of failings, dead-ends,
         | "FINALLY COMPILES!" commits, etc etc. I use `git branch
         | someNewBranch` really often, keeping a namespace of local
         | branches under the name `deadend/*` to mark tombstones of
         | approaches I tried in code but didn't work out, and I `reset
         | --hard` back to a previous commit after I save the branch. I
         | like it this way, it maps better to my totally disorganized
         | brain.
         | 
         | But I'm also a firm believer that none of my colleagues should
         | be able to tell how disorganized all of this is, because by the
         | time I make a PR, I squash it all down and write a very long,
         | detailed commit message of what exactly I did, why I'm doing
         | it, and how it works (sometimes, albeit rarely, spread across
         | multiple logical commits in one PR.) They never get to see my
         | private commit history.
         | 
         | Why does it matter to anyone how much I have to prune my commit
         | history before a PR? That's like complaining to another student
         | in class that their short-form handwriting is hard to read, in
         | their own personal notes they're taking during class, when said
         | student is acing all the tests. It's simply not the metric you
         | should be judging people on.
        
       | mb20281 wrote:
       | uhh...git rebase -i anyone?
        
         | pwdisswordfish9 wrote:
         | Yeah, this basically how git was supposed to be used to begin
         | with.
        
       | whoomp12342 wrote:
       | Or you can just use staging to bunch up your changes until you
       | are ready for a logical commit....
       | 
       | the problem with your approach is that sometimes, changes are
       | logically grouped but cross file.
        
       | timmit wrote:
       | It makes sense sometime, but not always. Group commits by files.
       | 
       | For example,
       | 
       | Most of one core change is in multiple files, it will be very bad
       | to commit by files group
        
         | okl wrote:
         | I disagree. It's not difficult to stage single lines using the
         | right tool.
        
       | ninkendo wrote:
       | Most folks are complaining about `git reset` being more dangerous
       | and to just use `rebase -i` instead. To each their own... I'm one
       | of those weirdos who also uses the `git reset` in their workflow.
       | I prefer it to `rebase -i` because it doesn't mess with my
       | working tree a bunch while it's happening, plus I like to
       | construct brand new logical commits, in order, in the manner
       | described by the article.
       | 
       | But this part set off alarm bells:
       | 
       | > Once you've finished making your changes, it's time to prepare
       | your work for some "git clean up." To do this, we'll run the
       | following command:
       | 
       | > git reset origin/main
       | 
       | Be very careful here! If you've run `git fetch origin` since
       | you've started your work, you may be resetting to a commit that's
       | _newer_ than what you based your work on, and thus you 'll wind
       | up creating a commit which effectively reverts anything that's
       | happened on `main` since then.
       | 
       | The more technically correct command would be:
       | 
       | > git reset $(git merge-base origin/main HEAD)
       | 
       | Since that resets to the commit you started your branch from.
        
         | iamed2 wrote:
         | Yes, this suggestion effectively addresses the biggest problem
         | with the flow described in the linked post. Accidentally
         | clobbering changes in the main branch used to be one of the top
         | sources of bugs when I started at my current job (incidentally,
         | switching to a rebase workflow helped with this, though I'm
         | sure that wasn't the only route we could have taken).
        
         | cerved wrote:
         | the only gotcha with rebase is that it cherry picks commits and
         | so if you do it willy nilly then you can end up introducing
         | changes where you don't expect them and reverting reverts
         | 
         | it's more complicated but vastly more powerful than reset
        
           | ninkendo wrote:
           | My problem with rebasing is that the vast majority of my
           | commits are garbage "WIP" commits, and I want to squash them
           | away anyway.
           | 
           | Squash-rebasing on top of main makes git replay all of those
           | dumb commits, just to squash them, and I have to fix merge
           | conflicts individually, for every commit I don't even care
           | about, before it's done.
           | 
           | I'd much rather soft-reset to the merge-base, make one clean
           | commit, then rebase _that_ one commit, than any of the other
           | approaches.
           | 
           | The typical response people have is that I shouldn't create
           | so many garbage WIP commits, but... that's just not how my
           | brain works. See
           | https://news.ycombinator.com/item?id=30000320
        
       | leifg wrote:
       | This to me seems more like a logical separation than anything
       | technical.
       | 
       | I use GitHub and always squash commits before merging a PR. This
       | keeps the commit log clean and also has the side effect that you
       | have the PR number in the merge commit.
       | 
       | Having said that I also suggest keeping PRs small. If you are
       | going to reformat a code base, make that a separate commit.
       | Updating a library, separate commit. Adding a library you need
       | for a new feature: create a PR for the library update, base your
       | implementation off that branch and rebase against main when the
       | first PR is merged.
        
         | dnilasor wrote:
         | +1 to keeping PRs small. This makes it much more logical to me.
         | But I have worked at organizations where this is frowned
         | upon...teams liked the PRs to be one logical
         | unit/fix/improvement and component parts became frustrating or
         | got merged at different times, creating the need for rework.
         | 
         | From reading a lot of feedback on this post one thing that
         | stands out is, the best way to use git just depends on the
         | context. But it doesn't hurt to have commands like this in your
         | toolbox and know how to use the tool well. Plus we all have our
         | private, icky antipatterns that we know we should improve,
         | right?
        
         | hcarvalhoalves wrote:
         | I dislike the rule of "small PRs" because people tend to
         | interpret it as "arbitrary number of lines changed", and end up
         | breaking a bugfix or feature into many PRs that don't make
         | sense to review or release in isolation. I interpret "small
         | PRs" as being "PRs should be about one bugfix/feature".
        
         | okl wrote:
         | Squashing commits is something lazy people do! :P
        
       | bronzecarnage wrote:
       | I use lazygit[0] to essentially do the same thing. (or even vim-
       | fugitive[1])
       | 
       | Previously I avoided creating messy commits simply because it was
       | "tedious" to reorganize commits. And making overly atomic commits
       | and typing out git commands more frequently didn't appeal to me
       | either.
       | 
       | Once I got used to the above tools, life got so much easier.
       | Lazygit makes it super easy to amend, reword, and even re-
       | position commits in a TUI environment. Real life-changer for me.
       | I can stage hunks super easily too, though that's even easier in
       | neovim.
       | 
       | The only issue I face is my C-j/C-k keys are already bound to
       | tmux, but are needed by lazygit to reposition commits.
       | 
       | [0]: https://github.com/jesseduffield/lazygit [1]:
       | https://github.com/tpope/vim-fugitive
        
       | zwieback wrote:
       | Why commit at all if you're going to reset? Sounds like the
       | intermediate commits are just used as a backup method. The real
       | trickery is mentioned in the last part, if commits don't break up
       | tidily along file boundaries you'll have to commit hunks somehow
       | and then go back and test whether the code works.
       | 
       | Either way, in the end it's up to developer discipline to make
       | clean commits, git doesn't really help all that much although in
       | my (long) source control history before git I never thought of
       | committing hunks.
        
         | ninkendo wrote:
         | > Why commit at all if you're going to reset?
         | 
         | Because committing is useful? It saves my work. I can get to a
         | point where my code finally compiles, make a quick commit, then
         | do some more dangerous refactoring. If something breaks, I can
         | `git diff` to see what changes I've made since it last
         | compiled.
         | 
         | I'll turn this around and ask you: Why do you feel like you can
         | only make a commit if you have something that's reviewable? Is
         | your code writing style so perfect that it all works the first
         | time? Or do you just not bother committing anything until your
         | work is done? (That personally sounds terrifying to me... I
         | really need to be able to say "what have I changed since 10
         | minutes ago when this compiled?" on a very regular basis.)
        
           | zwieback wrote:
           | Oh, I totally agree with all your use cases for committing,
           | I'm just wondering why the reset. I would either go off on
           | another branch to preserve my temporary work and then clean
           | that up and commit or merge to one of the main branches or I
           | would just push all my intermediate work.
        
             | Double_a_92 wrote:
             | Yeah I also don't see the need for it. Maybe if you often
             | commit completely broken code with meaningless messages...
             | But usually most commits we do at work are still proper
             | little changes (even if it's just 2 lines of a huge
             | feature) with meaningful information in the git message. I
             | would not want to lose that when merging back into the main
             | branch.
        
               | ninkendo wrote:
               | > Maybe if you often commit completely broken code with
               | meaningless messages
               | 
               | Bingo. I do this all. the. time. I don't want anybody
               | seeing it. I honestly couldn't imagine another way of
               | working... commit early, commit often, I say. Hell,
               | commit on a timer every minute, if that's your thing
               | (I've never done this but totally understand people who
               | do.) Leave the meaningful messages for later when you're
               | actually ready to craft a PR.
               | 
               | Honest question: if you avoid committing code unless it's
               | working and you have a meaningful message to make, what
               | happens if you screw up your editor undo, and you need to
               | go back to where you were 10 minutes ago? This happens
               | all the time for me... I undo a whole bunch and then
               | accidentally type the "z" button into my buffer because I
               | fumbled the keyboard, and now I can't "redo" back again.
               | If I commit early and often, I can recover the work to
               | where it was, even if it's not compiling yet. If I have
               | to wait for my code to be basically "reviewable", I'd be
               | screwed.
        
               | Double_a_92 wrote:
               | We usually work in a team of 2-3 on the same feature
               | branch, so we can't _push_ half broken things to out
               | teammates.
               | 
               | And the WIP commits can still be done, but _locally_.
               | Then I clean them up everytime I push to the remote
               | branch, but _not_ when that branch is merged.
        
               | ninkendo wrote:
               | Of course I'm talking about local commits, who brought up
               | pushing? I only push when I'm ready to. But turning my
               | local WIPs into something I _can_ push, is most easily
               | done with the reset workflow (but to each their own.)
               | 
               | It occurs to me that people like me might actually be
               | quite rare, who use git almost entirely locally and
               | rarely push. The only times I push are when creating a PR
               | (hence all the resetting and rewriting first), or to back
               | up my WIP work, if it's important. And even then it's to
               | my own fork, and under a branch namespace that nobody
               | would ever confuse with real code that should be
               | reviewed.
        
             | ninkendo wrote:
             | The reset is just a straightforward way to squash your work
             | into one commit without much fuss.
             | 
             | A typical workflow for me:                   $ git commit
             | -a -m "WIP" # picture this, but 100 times as I go         $
             | git reset $(git merge-base main HEAD) # Reset to the merge-
             | base to avoid reverting upstream work         $ git diff #
             | Get a nice overview of everything I've changed, to aid in
             | crafting a nice commit message         $ git add .
             | $ git commit # Type up a nice commit message describing the
             | whole change
             | 
             | Contrast this with using `rebase -i` to squash:
             | $ git commit -a -m "WIP" # picture this, but 100 times as I
             | go         $ git rebase -i $(git merge-base origin/main
             | HEAD) # rebase to the merge-base because I don't want to
             | deal with merge conflicts yet         $ # search/replace
             | 'commit' with 'squash' in a text editor         $ #
             | save/quit         $ # get prompted for new commit message,
             | type it out         $ # save/quit
             | 
             | They both feel like the same amount of typing to me, but
             | `rebase -i` thrashes my working tree around with each
             | commit, which has a habit of confusing my IDE quite a bit
             | and breaking its build cache. Also, I like the `git diff`
             | step in the reset workflow because it gives me a nice
             | reminder of what my change looks like, which helps inform
             | how I should word my commit. So I use reset.
             | 
             | If I want to keep my intermediate work locally for
             | posterity, I usually use `git branch
             | prprep/<bug_number>_unsquashed` or something so that I can
             | dig it up later if need be. I don't push any of my
             | `prprep/` branches.
        
       | dljsjr wrote:
       | The reset part seem superfluous, most of this can be handled via
       | rebasing. In fact, this is exactly what we do at <day job>.
       | 
       | Much like the thesis of this article, the goal is to have a set
       | of well organized commits so that when it comes time to do a PR
       | review, you have 1-3 logical units of change and it also improves
       | the ability to do reverts.
       | 
       | But you can very easily do this just by rebasing instead of
       | resetting and re-committing.
       | 
       | In particular, `git commit --fixup <ref>` and `git commit
       | --squash <ref>` are _extremely_ useful for this during the  "WIP"
       | stage as well as when handling PR comments, and these are things
       | that I only learned about in the last 6 months. I would recommend
       | doing a google search on "fixup commits" to learn more about
       | them. I enjoyed this article:
       | https://www.mikulskibartosz.name/git-fixup-explained/
       | 
       | Yes, rebasing is scary if you're new to git, but it does
       | everything in this article in a way that's much cleaner and once
       | you learn how to use it effectively you'll feel like you have
       | super powers. It's worth taking the time to learn.
        
         | jcranmer wrote:
         | The one thing I wish were easier would be breaking a single
         | commit into two commits.
         | 
         | Edit: to be clear, I know it's possible by using git reset...
         | but I'd much rather have something like git add -i that
         | interactively shifts changes from the commit itself and moves
         | them into working directory-only changes.
        
           | dljsjr wrote:
           | 1. `git rebase -i` to the commit before the one you want to
           | split
           | 
           | 2. mark the commit you want to split as an `edit`
           | 
           | 3. remove the file(s) you want to edit from the index so that
           | it's part of your working tree
           | 
           | 4. use `git add -p` to stage the hunks you want in the first
           | commit (assuming you want to split commits in a single file)
           | or just commit the files you want in the first commit first,
           | second commit second.
        
             | okl wrote:
             | > 4. use `git add -p`
             | 
             | I strongly recommend tig
        
               | cerved wrote:
               | what's the advantage over partial?
        
               | okl wrote:
               | Give it a try and compare. tig lets you either stage
               | hunks ('u') or lines ('1'). You can also adjust the
               | context ('[', ']') and split chunks ('\') and revert
               | chunks ('!') all from the same view.
        
               | dljsjr wrote:
               | lazygit is also great.
               | 
               | VS Code also makes it easy to stage hunks in the editor
               | if you like to stay in your editor; in the diff viewer
               | from the Git view you can highlight the lines you want to
               | stage and right click -> stage selected ranges.
               | 
               | I've never used tig. I'll look at it.
        
               | atq2119 wrote:
               | +1 for tig, and for the folks that are less TUI-inclined,
               | there's `git gui`; a neat little tool which is far too
               | little known considering that it's part of core Git. One
               | of the nice things about `git gui` is that it has an
               | "amend last commit" mode that makes splitting up a commit
               | trivial (unless the commit also adds or removes files --
               | in that case, you're going to have to do some more work).
        
           | okl wrote:
           | Not much trouble if you're used to it.                   git
           | rebase --interactive master         In Vim: ciw e ESC :x
           | git reset HEAD~         // Make your commits         git
           | rebase --continue
        
         | atq2119 wrote:
         | Yes, rebase is awesome and every Git user should learn about
         | it. But why not both?
         | 
         | When I'm working on a more complex patch series, it's not
         | uncommon for me to have a bunch of already cleaned up, or semi-
         | cleaned up commits on top of the remote branch I'm working
         | against, and then a bunch of random garbage "WIP" commits.
         | Something that often happens is that I `git reset` not to the
         | upstream branch but to my most recent clean commit, take the
         | resulting changes apart into individual commits (often, some of
         | them will be squash/fixup as I discovered a bug or missing
         | piece of earlier work) and then `git rebase` with
         | `rebase.autosquash` enabled in my global Git config.
        
           | dljsjr wrote:
           | Sure. I do that as well. But this article was advocating for
           | using a soft reset to replicate what is ostensibly the built-
           | in fixup mechanisms from interactive rebasing.
        
         | okl wrote:
         | Right, I don't think rebasing is that daunting if you take a
         | few hours to practice. It's time well spent.
         | 
         | Sometimes you can even create empty commits (--allow-empty) up-
         | front (if you already know about all the things that have to be
         | changed) and fill them via fixup/rebase --autosquash
        
           | dljsjr wrote:
           | After learning about fixup and squash commits, I started
           | typing `git rebase -i --autosquash` so frequently that I made
           | an alias for it. It has totally changed the way that I work.
           | You get to have the best of both worlds; commit early, commit
           | often but then also having nice and tidy PR's and histories.
        
             | evanrelf wrote:
             | You can also enable auto-squash by default:
             | git config --global rebase.autoSquash true
        
             | okl wrote:
             | My aliases:                   alias.c commit
             | alias.cf commit --fixup         alias.cm commit --message
             | alias.co checkout         alias.f fetch -v -p
             | alias.par pull --rebase --autostash         alias.ri rebase
             | --interactive --autosquash --autostash         alias.rim
             | rebase --interactive --autosquash --autostash master
             | alias.s status         alias.l !git --no-pager log
             | --oneline -n10         alias.pf push --force-with-lease
             | alias.bs !git --no-pager diff --stat master..HEAD
             | alias.p push         alias.alias !git --no-pager config
             | --get-regexp alias
             | 
             | Sometimes I get the feeling that my job isn't programming
             | but managing text snippets (diffs).
        
         | sandgiant wrote:
         | Rebase is great, but it's usefulness really depends on the
         | commit history.
         | 
         | If your branch consists of 10 commits of trying various things,
         | then the sum of changes in all the commits can easily become
         | significantly larger than the final branch diff. In that case
         | resetting is orders of magnitudes faster than fiddling with
         | rebase.
        
           | okl wrote:
           | > If your branch consists of 10 commits of trying various
           | things
           | 
           | Use fixup commits.
        
           | GauntletWizard wrote:
           | That's the point of squashing commits - Many of your changes
           | will ultimately pare down into just a few logical thoughts,
           | adding function X to feature A, then using that in function Y
           | in feature B etc... It's not something that comes for free,
           | of course, but if you make many small commits and try to get
           | each file to compile each time you can often basically just
           | squash by file or directory.
        
           | dljsjr wrote:
           | I would reject a merge from a branch that does more than one
           | thing at a time as you've described during code review. It's
           | okay to have a branch that does a bunch of things at the same
           | time, but come time to merge back to `main` this should be
           | broken up in to several different PR's.
           | 
           | I would probably branch off my branch for each logical change
           | I wanted merged and use `rebase -i` to wholesale drop commits
           | that I didn't want on the branch anymore.
           | 
           | This is also an example of where stacked diffs are far
           | superior to PR's but that's a separate issue.
        
             | ninkendo wrote:
             | Nothing in sandgiant's post implied that there's more than
             | one thing happening in the branch. Quite the opposite
             | actually, they said that the final diff could be much
             | smaller than the sum of all the small WIP commits along the
             | way. It could be that after hours of digging, you found the
             | real bug was a typo somewhere, and the final diff may be
             | just a 2-line change, where one of the WIP commits was
             | rewriting the whole auth layer because you were chasing a
             | dead-end.
        
               | dljsjr wrote:
               | That's exactly what `git commit --fixup` and `git rebase
               | --autosquash` does then.
        
               | ninkendo wrote:
               | Even with autosquash, rebasing has to iterate through
               | each commit individually before creating the final
               | commit. Which means it's touching a bunch of files in
               | your working tree while that's happening, which means
               | build tools like make now have to rebuild them because
               | the timestamps have changed.
               | 
               | What's worse, is if you rebase (say, 20 commits) onto a
               | newer branch than the one you started with, and you have
               | merge conflicts (say, on commit #10), now you might have
               | to fix the conflicts over and over for every commit after
               | the first conflicting one (depending on the contents of
               | the commits). I know there's `git rerere`, but after 16
               | years using git I've still never learned how it works.
               | 
               | Using reset to squash first, guarantees that you create
               | your single commit first, without dealing with merge
               | conflicts/etc, and _then_ you can rebase that resulting
               | commit once, to deal with conflicts once.
        
         | cerved wrote:
         | Fix-ups are nice. I developed a script that takes the all
         | staged content and breaks them up into separate fix-ups that
         | fixup the last commit that changed those lines. I find it
         | pretty handy
        
           | karl42 wrote:
           | That sounds much like git absorb. See
           | https://github.com/tummychow/git-absorb
        
       | sandofsky wrote:
       | I wrote a similar suggestion in 2011:
       | https://sandofsky.com/workflow/git-workflow/
       | 
       | As far as I could tell, this was the recommended workflow from
       | the earliest users of git. Look at the Linux kernel's public
       | history.
       | 
       | When git gained widespread adoption, it was sold as "Subversion,
       | but with cheap branching." People don't realize that this power
       | requires discipline, otherwise you end up with complicated
       | history that makes change management a nightmare.
       | 
       | In code review systems like Gerrit, you have to approve every
       | single commit, and you can also enforce rules like "fast-forward
       | merges only." It sure makes Github's pull-request model feel like
       | an anti pattern.
        
         | ninkendo wrote:
         | I love this quote:
         | 
         | > Treat yourself as a writer and approach each commit as a
         | chapter in a book. Writers don't publish first drafts. Michael
         | Crichton said, "Great books aren't written-- they're
         | rewritten."
         | 
         | This is a more succinct and poignant way of describing my
         | workflow, and why "just put a PR up with all your WIP commits"
         | is so difficult for me. I don't want people to see how long I
         | spent getting this one unit test to pass when the answer was
         | staring me in the face the whole time. I don't people to see
         | just how badly I wrote the first draft of my code, when my
         | approach was basically "make it work e2e first, no matter how
         | bad of a hack it is, then actually figure out how the code
         | should be laid out later."
         | 
         | Folks say "your PR can just be squashed when merging anyway",
         | but it misses the point: My private commit history is very,
         | very, intensely personal to me, and I don't want anybody seeing
         | it, ever.
        
       | ozim wrote:
       | Yes git rebase -i is better as others pointed out.
       | 
       | Example from the start ... revert.
       | 
       | Don't revert stuff, fix stuff forward (make new commits with new
       | changes) you won't introduce bugs in silly way.
        
       | alunchbox wrote:
       | I'm surprised no one has mentioned trunk based development yet.
       | Never going back to git flow at a medium/large org again. Merge
       | hell is a nightmare and releases more so.
       | 
       | > When the feature is complete, make a pull request.
       | 
       | gotta love 100 file commits that take a day to review.
        
       | danhab99 wrote:
       | Why do we keep trying to make git "smart"er than it needs to be?
       | Whenever I hear someone complain about git it's 9/10 times
       | because of ignorance, and it's a simple mistake to solve. No
       | amount of tooling or smartening (as this author seemed to be
       | described) is a valid alternative to training and understanding.
       | The consequences of that is that a smarter tool used by dumb
       | people means those people are held back, and made unproductive by
       | the tool in question.
        
         | Double_a_92 wrote:
         | The article isn't proposing to change anything about git, it
         | just shows one way of cleaning up your branches before merging
         | them... which I guess makes people understand git better? The
         | article is nothing groundbreaking, it's just showing how to use
         | git reset, but still.
        
       | travisd wrote:
       | This article undermines itself the second it moves from "here's
       | the problem" to "here's the solution:"
       | 
       | > Be mindful of not leaving your codebase in a broken state
       | during this step
       | 
       | You're still relying on very fallible human intervention here.
       | Even worse, often times when grouping commits post-facto you've
       | forgotten some of the context of what depends on what.
       | 
       | Ostensibly the approach presented could be better than other
       | strategies in this regard, but that's now how the article
       | presents itself and it loses credibility for that.
        
       | okl wrote:
       | I highly recommend tig because it lets you easily stage each
       | line/change separately. Together with interactive rebase and
       | fixup commits, it becomes super easy to group changes where they
       | belong. For fixup commits with tig (in main view go to the commit
       | to fixup and press '='), my .gitconfig has:
       | [tig "bind"]             main = = !git commit --fixup=%(commit)
        
       ___________________________________________________________________
       (page generated 2022-01-19 23:02 UTC)