2

I have two non-sequential commit abc123 and def456.
I want to squash them together in a way that fulfil the following requirements,

  • their original commit messages remain reusable.
  • done in a non-interactive way (no git rebase -i).

Is there a way to achieve this?

5
  • 2
    Is there a reason why you cannot or do not want to use an interactive rebase? Commented Nov 13, 2024 at 15:13
  • @dani-vta I am currently doing it in interactive way, but I have to do it often and every time I want to commit which became tedious. I wanted to write a script where I will give it a list of commits as arguments and squash will happen automatically. Commented Nov 13, 2024 at 16:12
  • 1
    You can set GIT_SEQUENCE_EDITOR to any shell command you want, it doesn't have to be an ineractive editor; the pick-list manipulation doesn't have to interact with you. And there's --autosquash. Commented Nov 13, 2024 at 16:29
  • I wasn't aware of that it doesn't have to be interactive editor. Thank you for let me know. Commented Nov 13, 2024 at 17:02
  • Link to --autosquash doc. The TLDR is: if you use git commit --squash abc123 when creating a commit, you can then use git rebase --autosquash <some relevant commit> to do what you want. Commented Nov 14, 2024 at 2:51

2 Answers 2

3

You can use a non-interactive editor with git rebase -i. For example, you can do the rebase with something like:

$ cd ~/tmp/; rm -rf foo; mkdir foo; cd foo; git init > /dev/null
$ for i in a b c d; do echo $i > $i; git add "$i"; git commit -m "Add $i"; done > /dev/null
$ git log --pretty=oneline
e3a2476246dcb165fbe9096065b1fa2916518fc4 (HEAD -> main) Add d
63e8e3f6b61dc05ebc3fcba8f940fb7a410c3f31 Add c
6639cfa03d7380c7f47ba898a53571b052e542e4 Add b
c3a7c74ffb370195fc69260530d0c46520623c1f Add a
$ GIT_EDITOR='perl -i -pe "s/Add/Merge b and c/"' GIT_SEQUENCE_EDITOR='perl -i -pe "s/pick/fixup/ if m/63e8e3/; s/pick/r/ if m/6639cf/"' git rebase -i HEAD~3
[detached HEAD 6d2f63b] Merge b and c b
 Date: Wed Nov 13 09:35:54 2024 -0700
 1 file changed, 1 insertion(+)
 create mode 100644 b
Successfully rebased and updated refs/heads/main.
$ git log --pretty=oneline
b1ec0a75d8c1139002e5df80be577bc0ecb5113a (HEAD -> main) Add d
ec6556e006a90c647fb8c656e1f6bebcb828ba7e Merge b and c b
c3a7c74ffb370195fc69260530d0c46520623c1f Add a
Sign up to request clarification or add additional context in comments.

3 Comments

I am not familiar with perl, but assuming non-interactive editor command has to read text from stdin and do search and replace? is there more to it? Can one do more complex operation like reorder lines ( commits to be squashed are not necessarily sequential) ?
@nabik An editor, interactive or not, must edit a file. Its name is an argument of the editor invocation. Stdin is not relevant. Perl is certainly able to all of what you would need.
@nabik You can do anything at all. The solution here embeds the script directly in GIT_EDITOR (used to edit the commit messag) and GIT_SEQUENCE_EDITOR (used to select the oid's to be included in the rebase), but they could easily just be paths to an arbitrary script that does whatever you want. Rather than streaming, it will take a filename as an argument and modify that file.
2

If you really want to do this non-interactively, assuming these commits are the latest 2 on your branch, you could do the following, making sure your working tree is clean first:

git reset --soft HEAD~2
git commit -m "$(git log --reverse --oneline --format="%B" ...@{1})"

This resets the branch back to the commit before the ones you want to squash, then commits the combined changes using a concatenation of the commit messages. This is flexible, so HEAD~n goes back n parents.

If your commits are anywhere but the latest commits on your branch, this won't be possible without using interactive rebase, or basically recreating the functionality of interactive rebase.

Relevant question: Is there a way to squash a number of commits non-interactively?

1 Comment

The question did say the commits were non-sequential, so there's an assumption failure there.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.