1. 7

I can’t believe this functionality isn’t built in to Git. It’s so useful, I use this all the time.

(just ported an older version in Ruby to Python)

  1.  

  2. 10

    Isn’t this the same as git reset <commit>; git commit … ?

    1. 14

      Oh my god you’re right. Edit: none of this comment is sarcasm! genuinely dumbfounded

      git checkout branch1
      git treesame-commit branch2 # a complex program
      

      is the same as

      git checkout branch1
      branch1ref=$(git log --format=%H -1)
      git reset --hard branch2
      git reset $branch1ref
      git add .
      git commit -m "treesame-commit" # no complex program
      

      and this whole time I believed there was no way to do this!

      In my literally 7 years of using my own custom tool that writes git commit objects by hand, how have I never realized that? :headdesk:

      Of course, to make the latter really solid you’d also want a git clean -dffx, git submodule sync, git submodule update --init --recursive, etc. In some sense, it’s hard to beat the concreteness of writing out the exact tree hash you want into a commit object, so frankly I’ll probably still keep using my tool but I do feel a bit sheepish.

      Another person on reddit pointed out git read-tree and git checkout-index which also can solve the problem.

      1. 3

        For the 1st half of this comment, I thought you were being sarcastic!

        1. 1

          Nope! Just dumbfounded by my own stupidity. I agree though, I don’t know how to convey the dumbfoundedness without sounding sarcastic. Internetting is hard.

        2. 3

          git rev-parse $refdescriptor is a shorter more descriptive way to get the ref by the way

      2. 4

        Any example of a situation in which creating commits like this helps?

        1. 3

          Let’s say you have some complicated history pattern with merges and so on. Lots of different developers doing lots of different things. After a bunch of merges and merge conflict resolutions you have a history of sorts, but you want to clean it up. This allows you to make a single commit where the end result is the tree matches the complex history you’d like to throw away.

          Very useful for keeping dev history clean.

          1. 1

            Ah, that makes sense. So like a squashed merge, but without the merge. I guess it’s what git merge --squash --strategy=theirs would do if that merge strategy existed.

            1. 1

              Well, now that I think about it, wouldn’t saying –strategy=theirs be specifying just how conflicts are handled? My tool is saying, forget about conflicts, merging, everything, take the entire tree from the other commit wholesale. Don’t even try and merge things together.

              1. 1

                No, that’s what --strategy=recursive -X theirs does. The existing “ours” strategy just throws away the other commit and takes the tree from the current one. A fictional “theirs” strategy would do the same with the other tree.

                Merge strategies and their options are pretty confusing.

            2. 1

              Wait… you use it to delete history? But… having that history around is the reason I use git?

              1. 1

                The last thing I want to do when fighting a production fire at 3am is be sorting through 12 merges of commits that look like:

                • add feature
                • whoops
                • small fix
                • review comments
                • doh maybe this time.

                squash that crap together! What commit broke the build is infinitely harder to figure out when the problem is in some chain of merges titled “whoops”

                I decidedly prefer having my git history serve as a neatly curated form of documentation about the evolution of the codebase, not chaos of immutable trial and error

                1. 2

                  I constantly bring this up in pull requests when I see shitty commit histories like that. Squash your damn commits! If you’ve already pushed a branch, create a new one with a new name, pick your commits on top of it, rebase -i and squash them into succinct relevant feature sets (or try to get as close as you can).

                  I realize this is once that’s already gone and it’s too late (history with a ton of “squishme: interum commit” bullshit in there) and that’s the purpose of tools like yours, but teaching people good code hygiene is pretty important too. :-P

                  1. 1

                    So I agree with you on this approach, but I think I’m still not grasping what your tool accomplishes. Couldn’t the situation you’re outlining here be accomplished by squashing?

                    1. 1

                      Yeah that last comment was really more of a discussion about why you might want to clean up git history. That’s a poor example for this tool.

                      This tool is useful when there’s multiple merges along two divergent branches of history and you want to make a commit that essentially contains the entire diff from your commit down to the merge-base of another commit combined with the diff from the merge-base back up to that other commit.

                      1. 1

                        Hmm, I guess I just can’t picture in what kind of situation that would happen. Could you explain the example chronologically?

                        1. 1

                          I think @jtolds is on significantly more complicated code bases than I’ve worked on. There was an earlier post about Octopus commits:

                          https://www.destroyallsoftware.com/blog/2017/the-biggest-and-weirdest-commits-in-linux-kernel-git-history

                          and here is a visual for what that would look like:

                          https://imgur.com/gallery/oiWeZmm

            3. 3

              A totally useful comment from Reddit: git commit-tree refspec^{tree} -p HEAD was exactly what I’ve always been looking for and does 95% of what my tool does.

              https://www.reddit.com/r/git/comments/8v3pjg/comment/e1kkmhm