So I’ve just learned that git rebase -i allows users to merge individual commits in the middle of a branch’s history. For that,

$ git rebase -i

This should bring a list of all commits in reverse order, similar to the one listed below:

pick 2361d4d implements something
pick a700451 fixes a bug
pick 79f9d04 fixes a bug again
pick 3172f07 implements some other thing

# Rebase 484d6d2..3172f07 onto 484d6d2 (4 commands)
#
# Commands:
# p, pick  = use commit
# r, reword  = use commit, but edit the commit message
# e, edit  = use commit, but stop for amending
# s, squash  = use commit, but meld into previous commit
# f, fixup [-C | -c]  = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# x, exec  = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop  = remove commit
# l, label  = label current HEAD with a name
# t, reset  = reset HEAD to a label
# m, merge [-C  | -c ]  [# ]

It’s possible to merge commit 79f9d04 with a700451 by updating the list of commands to set squash instead of pick in the commit you want to flatten onto the previous one, such as:

pick 2361d4d implements something
pick a700451 fixes a bug
squash 79f9d04 fixes a bug again
pick 3172f07 implements some other thing

(...)

Once we save the commit, Git opens an editor to rework the new commit message for the squashed commits, and you’re set.

  • TechNom (nobody)
    link
    English
    14
    edit-2
    7 months ago

    I don’t think you mean ‘merge’ - that’s a specific operation. I think you mean ‘squash’, which combines multiple commits into one. ‘Fixup’ is also something similar.

    Git allows you to edit history in any way you like. The hardest is probably splitting a commit into multiple smaller ones. It requires you to edit a commit by resetting the HEAD and making multiple smaller commits before continuing with the rebase. Though it sounds complicated, it becomes easy enough after you try it a couple of times. You should check out git-rebase.io - a site dedicated to editing git history. You can learn to craft proper commits of high quality.

    Once you get familiar with rebasing, you could try out stacked git. Interactive rebasing looks weak in comparison to what stgit can do. Stgit allows you to edit history like rebase. But that’s the least of it. It allows you to create proper commits from the start, rather than by editing at the end. In some ways, stgit gives you multiple staging areas - giving you incredible flexibility.

    • @lysdexicOP
      link
      English
      16 months ago

      I don’t think you mean ‘merge’ - that’s a specific operation. I think you mean ‘squash’, which combines multiple commits into one.

      Yes, you are right. I apologize for the terminology mixup. I was referring to how it’s possible to join two changesets so that they can be considered in the commit history as a single commit.

      Once you get familiar with rebasing, you could try out stacked git.

      Thanks for the recommendation, but I’ll take a pass. Vanilla Git does the job well.

  • Max-P
    link
    fedilink
    77 months ago

    You can get git to do pretty much anything you want. You can even add completely unrelated or meaningless commits if you want. Git will happily let you fabricate any commit you want.

    You can even split a monorepo into multiple repos and then merge them back, with no loss of history.

    Git is incredibly powerful when you go beyond just the CLI. People complain a lot that the CLI is weird and unclear but when you understand what goes under the hood it makes complete sense.

    • @BatmanAoD
      link
      67 months ago

      The CLI is still pretty bad, and I say this as someone who’s gotten so used to it that I find git GUIs more annoying than helpful.

      log gets a random flag from diff, -p, that’s incredibly useful but buried in the documentation. Printing a readable graph log requires magic incantations that aren’t worth memorizing. git branch with no subcommand prints the list of branches, but to get similar behavior for git remote, you need to add -v. Etc.

  • @[email protected]
    link
    fedilink
    English
    77 months ago

    I extensively use interactive rebase, edit and squash, to structure my changes and shorten/simplify my WIP changes when I’m confident in the approach etc.

  • pudcollar [he/him]
    link
    fedilink
    English
    57 months ago

    I’m one of those guys who doesn’t even merge. I use rebase for everything. Rebase -i is really powerful. One command to remember to rewrite a message, squash commits, delete commits, etc.

    • @naonintendois
      link
      47 months ago

      You should also check out git commit --fixup ... paired with git rebase -i --autosquash .... you specify a commit target to the fixup to say you want to fix that commit with the code you’re currently committing. It will create a fixup commit at HEAD. The auto squash will reorder your fixup commit and mark it as a squash so you have way less work to do.

      • @lysdexicOP
        link
        English
        16 months ago

        You should also check out git commit --fixup … paired with git rebase -i --autosquash …

        Superb tip. I’m reading up on the feature, and it sounds like the process I’ve just learned but on steroids. Thanks for the pro tip!

    • TechNom (nobody)
      link
      English
      1
      edit-2
      7 months ago

      I use both separately. But the Linux kernel developers seem to use both together. I don’t know what criteria they use for each.

  • @lysdexicOP
    link
    English
    26 months ago

    As a followup to this post, I’ve started including git rebase -i in my workflow when working on local branches. Basically things went like this:

    • I start working on an issue in a dedicated feature branch.
    • I commit small changes such as “Updates this feature in this component”, “fixes this glitch on this function”, “removes this dead code”.
    • I keep going until I’m done.
    • If I stumble upon small nits, I just commit them right at the head.
    • when I’m done I run an interactive rebase and move these small commits around to sort commits that belong together.
      • nits and followup fixes are squashed together,
      • cleanup commits are moved to the start of the branch to line them up to be pushed as a separate pull request,
      • push a PR for the issue

    To me this really paves a way to a far better workflow and faster turnaround times.