[HN Gopher] Correct Git commits with git-autofixup
       ___________________________________________________________________
        
       Correct Git commits with git-autofixup
        
       Author : tosh
       Score  : 75 points
       Date   : 2021-03-18 08:55 UTC (14 hours ago)
        
 (HTM) web link (symflower.com)
 (TXT) w3m dump (symflower.com)
        
       | Tobu wrote:
       | git-absorb is another implementation:
       | 
       | - https://github.com/tummychow/git-absorb
       | 
       | - https://lib.rs/crates/git-absorb
        
         | NateEag wrote:
         | Can anyone give a comparison of the two from experience?
         | 
         | I'm painfully familiar with the problem they solve and am
         | delighted to discover they exist.
         | 
         | I'd love some input about how they differ.
        
           | Ambroisie wrote:
           | I have only used `git-absorb`, but one difference from
           | reading the article is that it does its own analysis of your
           | previous commits to choose what each line should `fixup`.
           | Because of this, `git-absorb` can only fix up commits up to a
           | specified base (10 commits earlier by default) when you use
           | it.
        
             | krobelus wrote:
             | > does its own analysis of your previous commits
             | 
             | Not really; you can pass any commit ref to git-autofixup,
             | and it will create commits only for commits since that ref.
             | So it's for the user to decide :)
        
           | krobelus wrote:
           | I have looked at both but only used git-autofixup. Some
           | observations:
           | 
           | - git-autofixup has a changelog
           | 
           | - git-autofixup includes sober, technical documentation that
           | actually explains how it works. OTOH, git-absorb has a great
           | elevator pitch for users that are not so familiar with Git.
           | 
           | - git-autofixup does not have unresolved bugs, unlike git-
           | absorb
           | 
           | - git-absorb creates fixup commits for the last 10 commits,
           | which seems really odd. It's better to use symbolic
           | references like @{upstream}, but it looks like they dont'
           | support this yet?
           | 
           | - git-autofixup is stable and mostly done software, while
           | git-absorb has a sizeable list of todos
           | 
           | - git-autofixup is written in Perl (like some tools in Git
           | itself), making it easier to install than git-absorb which
           | uses Rust.
           | 
           | Then again, as author of this article, I'm obviously biased
           | ;)
        
       | hashkb wrote:
       | For this issue and so many others, everyone should just get
       | comfortable with interactive rebase. Crutches like this trap you
       | in this space of never really learning git. And that's a tragedy.
       | All because you're impatient? Afraid?
        
         | quesera wrote:
         | Coming from the other direction, I am very familiar with
         | interactive rebase, and I use it on my working branch all the
         | time before pushing.
         | 
         | I am struggling to understand the use case for autofixup and
         | absorb. The descriptions talk about post-code-review changes,
         | which means the branch has been pushed. So are these tools only
         | for a flow that uses force-pushing regularly?
         | 
         | I don't think that would be a feature for my teams. We think of
         | code review as an event in time, and changes post-code review
         | should be clearly differentiated from changes pre-code review.
         | 
         | Am I missing the point?
        
           | vlovich123 wrote:
           | I rely on code review tools that can show me the difference
           | between two patch sets. I really love Phabricator for that
           | ability - push a set of commits and each gets a review.
           | Absorb any fixes, push and suddenly each commit has a new
           | version that you can diff against old ones if you care. It
           | makes reviewing the patch set in totality trickier. However,
           | since I'm practice you want individual commits within a set
           | to be standalone ones, I've rarely missed the ability.
        
           | Vinnl wrote:
           | I generally use autosquash when a code review of a PR turns
           | up things I missed, which should have been part of a
           | particular commit on that PR's branch. However, I don't want
           | to amend the previous commit right away if I'd like another
           | review for the new changes. Thus, I do a `git commit --fixup
           | <HEAD|hash>` and push it. I can then ask the review to only
           | look at the new commit, I won't break their local checkouts
           | of the branch, and only when everything's been approved I run
           | `git rebase --autosquash` and then merge it in.
           | 
           | (Before I started using autosquash for this, I'd do the same,
           | except I had to figure out at the time of merging onto which
           | commit every new commit had to be fixed up, rather than at
           | the time of writing it.)
        
         | Vinnl wrote:
         | This whole article seems to hinge on the fact that you already
         | know how to run an interactive rebase though?
         | 
         | As in, I'm very comfortable doing an interactive rebase, but
         | have to figure out the correct commit to fixup on is tedious,
         | so it sounds very useful to me if a tool can help me with that.
        
           | globular-toast wrote:
           | Unfortunately I think fixup commits are one of the least
           | understood parts of git. People seem to confuse them with
           | rebasing. They support rebasing but don't replace it.
        
         | zimmski wrote:
         | Learning rebasing is one thing but wasting hours every week on
         | rebasing because you are doing the same thing over and over
         | again is another. When i do a one line fixup i just do not want
         | to figure out the correct commit and then rebase i just want it
         | to happen because i already know how i would rebase that: you
         | just use git-autofixup or other similar tools discussed here to
         | get it done.
        
           | tomxor wrote:
           | It's possible the other reason is trying to manipulate git
           | history too frequently. Keep in mind this comes from a
           | prolific git history abuser and rebaser... if hacking on a
           | branch, a trail of messy commits is fine, it's your work in
           | progress, it's also useful if you messed up otherwise you are
           | forced to use reflog. Rebase when you are at the final stage
           | of tidying things up and squashing commits down into a
           | legible history, not on every single amendment... for this
           | style of history rewriting rebase is perfect because you have
           | the full power to rearrange rewrite and squash commits.
        
             | zimmski wrote:
             | Totally agree. I guess what i am trying to say is that
             | learning rebase is a must and powerful tool but if you are
             | doing the same things again and again you should think
             | about automating them and use your thinking-power and time
             | for non-automated things.
        
             | urxvtcd wrote:
             | I'd disagree. The longer the branch to rebase the larger
             | the chance of conflicts occuring.
        
             | hashkb wrote:
             | Rebase early and often. Always keep a close eye on upstream
             | while you're working downstream. Otherwise you're making it
             | harder on yourself when you do that final cleanup.
        
               | tomxor wrote:
               | That's a fair point, if you are working against a fast
               | moving target that forces you to rebase onto it often
               | then you really want a short and simple history... a
               | trail of WIPs or changes on top of changes tend blow up
               | into a cascade of conflicts.
               | 
               | However in my original comment I was actually just
               | referring to the scenario where you only want to rewrite
               | history onto the same base, in which case I stand by my
               | suggestion... In fact I find this can be a good strategy
               | if your history has grown and you also need to rebase
               | onto a new upstream - i.e first clean up your history
               | onto the same base with a `git rebase -i current-base`
               | and squash it all down and tidy it up, then do the `git
               | rebase upstream` so you can do conflict resolution with
               | more holistic commit diffs.
        
           | hashkb wrote:
           | Practice. You'll get better and faster. I do it tens of times
           | daily and each fixup takes ten or fifteen seconds.
        
             | zimmski wrote:
             | Yep. I have that practice too and i thought that was good
             | enough but with git-autofixup and my posted alias i am down
             | to under one second. So it will save you 1-14 sesconds of
             | your life with every fixup you do... unless it cannot be
             | done automatically. Try it and let us know what you think
             | :-)
        
       | zimmski wrote:
       | git-autofixup definitely changed my daily development life.... I
       | mostly use it with this Git alias
       | 
       | ``` autofixup = !"autofixup origin/master --exit-code; test $?
       | -lt 2 && GIT_SEQUENCE_EDITOR=true git rebase -i --autosquash
       | $(git merge-base HEAD origin/master)" ```
       | 
       | Works almost every time perfectly, and when not i always get a
       | comment during a code review.
        
         | jasonpeacock wrote:
         | > when not i always get a comment during a code review.
         | 
         | Why during CR? Is the error not obvious to you? You should be
         | able to check the results of your changes before pushing,
         | right?
        
           | zimmski wrote:
           | I/we do but sometimes you just overlook something especially
           | if there are a lot of changes. But having someone else review
           | what you do almost completely removes that problem.
           | 
           | I usually do not look through all commits again when a basic
           | review already happend and i "just" integrate the review
           | comments. That is where for me personally these problems
           | happen that git-autofixup is sometimes wrong but in over a
           | few thousand commits in the last months this happend only
           | thrice. Which for me just speaks for the tool IMHO. Ow the
           | original author big time :-)
        
       | rollcat wrote:
       | If you're an Emacs user, I must recommend magit
       | (https://magit.vc/) - the interactive rebasing (including
       | squashing/fixups) is one of the best UIs (overall, not just git)
       | I've seen in my life.
       | 
       | I'd switch away from Emacs but magit keeps me hooked!
        
         | shepmaster wrote:
         | Does Magit have a similar feature to what is discussed in this
         | article? Specifically "magically figure out which previous
         | commit these changes should be combined with"?
        
           | globular-toast wrote:
           | It doesn't do it magically, but it makes it a lot easier to
           | do. Easy enough that I've never really wanted a tool like
           | this.
           | 
           | Having said that, if autofixup works well it might be worth
           | integrating it into magit via a plugin as it would certainly
           | reduce the repetition.
        
             | shepmaster wrote:
             | Could you share what your workflow would be? I'm assuming
             | you put the point on the line you want to fix, then use
             | Magit to start a rebase at the last commit that changed
             | that line, then apply your changes, then continue?
             | 
             | I'm a casual Magit user, so learning from other users would
             | be very beneficial.
        
               | globular-toast wrote:
               | I typed it out but HN seems to be rate limiting me or
               | something and tells me I'm "posting too fast". I seem to
               | be able to post shorter comments, though.
               | 
               | I've been meaning to write an article on a fixup workflow
               | for a while now. Just need to find somewhere to host it.
        
               | shepmaster wrote:
               | You could throw it in a gist to start with.
        
               | jpeloquin wrote:
               | That works. My habit is to make the fix, commit it as
               | "!fixup whatever", start an interactive rebase, move the
               | fixup commit just after the bad commit (M-|), press f on
               | the fixup commit to tell rebase to merge it with the bad
               | commit, and run the rebase (C-c C-c). This is pretty much
               | the same method as yours, but committing first means the
               | fixup will still be there if you have to cancel & redo
               | the rebase for some reason, such as unexpected and
               | confusing merge conflicts.
        
               | shepmaster wrote:
               | Right, that process is what I do now, outside of Magit.
               | The point of this article is a tool that (attempts to)
               | automatically find the correct commit to combine with.
               | Conceptually it removes the effort taken to move the
               | fixup commit to the right spot; both keystroke-wise and
               | mental effort wise.
               | 
               | That's what I'm looking for: a Magit-native way to make
               | my little fixups without having to manually track which
               | fixup should be applied where in history.
        
               | globular-toast wrote:
               | > move the fixup commit just after the bad commit (M-|),
               | press f on the fixup commit to tell rebase to merge it
               | with the bad commit,
               | 
               | That's what --autosquash is for. If you do fixup commits
               | correctly (using --fixup), it will put them in the
               | correct place and mark them as fixups for you.
        
               | shepmaster wrote:
               | > using --fixup
               | 
               | If I understand you correctly, this requires that I think
               | about which commit I want to fixup. Once identified, I
               | can mark my fixup with what commit it should be combined
               | with automatically.
               | 
               | My understanding of the article is that it attempts to
               | automatically figure out which commit to combine with.
               | That's the feature that I'm interested in.
        
               | globular-toast wrote:
               | Yeah, it does require you to think about it. But magit
               | has really quick and easy diffing and blame so it can
               | make it significantly easier. In practice I rarely have
               | to think very hard before knowing which commit to fixup.
        
               | jpeloquin wrote:
               | So I guess what I should be doing is make fix - magit-
               | blame to get the commit ID for the last prior edit - copy
               | commit ID or message to clipboard - magit commit with `c
               | F`, C-s to the commit ID I just copied, and C-c C-c to
               | set up and run the autosquash rebase.
        
               | shepmaster wrote:
               | This process is what I think the article describes and
               | what I'm curious if can be automated by magit itself.
        
               | jpeloquin wrote:
               | Magit appears to have an integration with 'git absorb'.
               | The issue that tracked the integration discussed git
               | absorb as providing similar function to git autofixup. I
               | don't particularly desire unsupervised changes to my
               | commit history though so don't plan on looking into it
               | further.
               | 
               | https://github.com/magit/magit/issues/3053
        
           | Amorymeltzer wrote:
           | Magit incorporates git-autofixup directly via `magit-commit-
           | autofixup`; also available is `magit-commit-absorb` for git-
           | absorb.
        
         | sundarurfriend wrote:
         | How does Vim's fugitive (https://github.com/tpope/vim-
         | fugitive/) plugin compare to this, in case anyone here has used
         | them both? I've only used it for some basic things so far, and
         | it seems nice enough, but I'm wondering if it's good with more
         | advanced git-fu like the above.
        
       | pjc50 wrote:
       | Hmm. This sounds like my workflow with gerrit, which goes like
       | this:
       | 
       | - each work item has its own branch. When pushed, gerrit turns
       | this into a review.
       | 
       | - a git alias, "alias.fixlast=commit -a --amend --no-edit" which
       | amends the last commit to match the working directory
       | 
       | Plus these configurations:
       | 
       | branch.autosetuprebase=local
       | 
       | branch.autosetupmerge=always
       | 
       | pull.rebase=true
       | 
       | The effect is that every branch I create off the main "develop"
       | branch is automatically set up to rebase from that branch when I
       | do "git pull". So when I get some review comments for review
       | "xyz", I just do checkout - pull - make changes - fixup - push.
       | 
       | Gerrit's workflow is slighly unusual in that each review must be
       | a single git change, but it keeps a history of its own of
       | previous changes you made to that review.
        
       | chrismorgan wrote:
       | An excerpt from my ~/.gitconfig, showing a related approach
       | (piggy-backing on git-revise):                 [alias]        #
       | Revise into the commit that last changed File        rf = "!f() {
       | if [ $# -eq 0 ]; then REV=\"$(git status --porcelain --untracked-
       | files=no | sed '/^ /d;s/^.. //' | xargs -n1 git rev-list -1 HEAD
       | --)\"; NUM_REVS=\"$(echo \"$REV\" | wc -l)\"; if [ $NUM_REVS -ne
       | 1 ]; then >&2 echo Files in the index were not all last modified
       | in the same commit; exit 1; fi; else REV=\"$(git rev-list -1 HEAD
       | -- \"$1\")\"; shift; fi; git revise \"$REV\" \"$@\"; }; f"
       | 
       | (This alias is three times as long as my next longest aliases,
       | which are ports of Mercurial's id, tip, incoming and outgoing
       | commands.)
       | 
       | This is more coarse-grained than the technique in this article,
       | as it only goes down to the file level--because that was
       | sufficient for me when I wrote the alias, and probably easier to
       | implement. I've been vaguely contemplating trying git-autofixup
       | and git-absorb for a while too, which include what are
       | essentially more polished and powerful versions of my alias.
        
         | muxator wrote:
         | hg incoming and hg outgoing are really useful.
         | 
         | When I am in git, I use this version from
         | https://github.com/sympy/sympy/wiki/Git-hg-rosetta-
         | stone#set...:                   [alias]         outgoing = !git
         | fetch && git log FETCH_HEAD..         incoming = !git fetch &&
         | git log ..FETCH_HEAD
         | 
         | Feature-wise, the mercurial command is way more powerful:
         | https://www.mercurial-scm.org/doc/hg.1.html#incoming
        
           | chrismorgan wrote:
           | My aliases for those two deals with remote-tracking branches
           | (see git-rev-parse(1) for documentation of the @{upstream}
           | part if you're not familiar with it). Might as well provide a
           | more full excerpt:                 # I almost always use glog
           | rather than log.       glog = log --graph       # "Short log"
           | slog = log --graph --oneline       # `git id` = `git rev-list
           | --max-count=1` with default refspec HEAD.       id = "!f() {
           | case \"x$1\" in x-*|x) refspec=HEAD;; *) refspec=\"$1\";
           | shift;; esac; git rev-list --max-count=1 \"$refspec\" \"$@\";
           | }; f"       # `git tip` = `git log --max-count=1` with
           | default refspec HEAD.       tip = "!f() { case \"x$1\" in
           | x-*|x) refspec=HEAD;; *) refspec=\"$1\"; shift;; esac; git
           | log --max-count=1 \"$refspec\" \"$@\"; }; f"       # `git
           | out` = glog commits that exist locally but not on the
           | upstream branch. No way to refer to different origins, sorry.
           | out = "!f() { case \"x$1\" in x-*|x) branch=;; *)
           | branch=\"$1\"; shift;; esac; git glog
           | \"$branch@{upstream}..$branch\" \"$@\"; }; f"       sout =
           | "!f() { case \"x$1\" in x-*|x) branch=;; *) branch=\"$1\";
           | shift;; esac; git slog \"$branch@{upstream}..$branch\"
           | \"$@\"; }; f"       # `git in` = glog commits that exist on
           | the upstream branch (remember to fetch them first) but not
           | locally.       in  = "!f() { case \"x$1\" in x-*|x) branch=;;
           | *) branch=\"$1\"; shift;; esac; git glog
           | \"$branch..$branch@{upstream}\" \"$@\"; }; f"       sin  =
           | "!f() { case \"x$1\" in x-*|x) branch=;; *) branch=\"$1\";
           | shift;; esac; git slog \"$branch..$branch@{upstream}\"
           | \"$@\"; }; f"
        
         | taisalie wrote:
         | You know that can go in $PATH/git-rf, right.
        
           | chrismorgan wrote:
           | If it grew much bigger I'd do that, but until it's definitely
           | too large, keeping all of this stuff in ~/.gitconfig is
           | simpler, especially for sharing across heterogeneous
           | machines.
        
             | taisalie wrote:
             | You can add a version-controlled directory to your PATH and
             | put scripts in it. This can be in the same repository as
             | your .gitconfig. So you can graduate aliases to scripts.
        
               | chrismorgan wrote:
               | Copying a single file is regularly easier, and more
               | flexible when working on machines not set up with your
               | SSH keys or whatever.
        
       | rurban wrote:
       | I'll keep using mine. It advances on magits autofixup by fixing
       | up multiple files. And it checks if the commit was already
       | published. git blame is not needed.
       | 
       | https://github.com/rurban/home-bin/blob/master/git-cifixup
        
       | samwestdev wrote:
       | Sublime Merge do the same thing in just a couple of clicks, just
       | saying
        
         | globular-toast wrote:
         | I doubt it. There's an open feature request currently for
         | adding fixup commits. This article goes a step beyond that. Are
         | you sure you understood the article?
        
           | ruffel wrote:
           | Select two commits. Edit Commit -> Squash Selected Commits,
           | ignoring new messages (fixup)
        
             | globular-toast wrote:
             | That's not a fixup commit, it's a rebase. It's missing the
             | point of fixup commits which is to commit now and defer the
             | rebase until later.
             | 
             | It's also completely missing the point of the feature under
             | discussion here which is to automatically decide which
             | commit to squash the fix into.
        
       | yegle wrote:
       | This is a lot like hg absorb. https://github.com/quark-
       | zju/timepad/blob/master/assets/hgab... explained `hg absorb`
       | pretty well with good visual.
        
       ___________________________________________________________________
       (page generated 2021-03-18 23:02 UTC)