Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do dropped commits from a git rebase cause merge conflicts?

My Problem

I have been working on a git repo. Before publishing my changes, I'd like to remove some commits - a023b43, 315424b and 7b51754 - as they do not reflect any meaningful changes. The only important points in time are the first (70f72ca) and last (6937ddd) commits.

What Have I Tried

git rebase -i 4c802c1

I chose:

pick 70f72ca Remove repetitions from Makefile
d a023b43 Separate target for image conversion
d 315424b Remove image conversion (#1)
d 7b51754 Fix Makefile
pick 6937ddd CV 2019.01

The final goal is to have a git log that looks like:

6937ddd CV 2019.01
4c802c1 Initial commit

What Did not Work

Auto-merging src/Adam_Matan.tex
CONFLICT (content): Merge conflict in src/Adam_Matan.tex
error: could not apply 6937ddd... CV 2019.01

Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".

Could not apply 6937ddd... CV 2019.01

As I understand it, the two commits are just snapshots of my working directory. I want to have two snapshots - the original commit and the CV 2019.01 one. I don't want to merge or combine them in any way. Why do I get a merge conflict message?

My Question

How can I delete all intermediate commits from a branch, and leave only the first and last commits?

Update

Squashing creates merge conflicts as well:

git rebase -i 4c802c1
Auto-merging Makefile
CONFLICT (content): Merge conflict in Makefile
error: could not apply 315424b... Remove image conversion (#1)
like image 774
Adam Matan Avatar asked Oct 17 '25 10:10

Adam Matan


2 Answers

Rebase acts by taking a series of commits and re-applying them, perhaps on a different "base" object. It does either by creating a patch for each commit, and re-applying them, or by cherry-picking them in-order.

Regardless of the mechanism, by removing a commit from the list of rebase instructions, you have asked for those changes not to be included.

Imagine if you had some file, and in its initial revision, it has a single line with the contents one. Imagine that in the next commit, you change one to two. In the subsequent commit, two to three. Then three to four, and finally four to five. So you now have five commits, each changing this line.

Imagine that in each commit you gave it a message indicating the change in contents, and that magically the commit ID (hash) also reflected the contents. Running rebase -i --root would give you this script:

pick 1111111 add one
pick 2222222 change one to two
pick 3333333 change two to three
pick 4444444 change three to four
pick 5555555 change four to five

In other words, the first commit picks the change from nothing to one. The second one will cherry-pick the change from one to two, etc, until the last instruction cherry-picks the change from four to five.

If you remove some of those lines, giving you:

pick 1111111 add one
pick 5555555 change four to five

You will have two cherry-picks. The first will add a file with contents one. The second will try to change those contents from four to five.

Alas, those contents are not four. Thus, you have a conflict.

If your goal is to get from one to five with no intermediate commits, then you want to squash them or mark them as fixup commits.

pick 1111111 add one
pick 2222222 change one to two
squash 3333333 change two to three
squash 4444444 change three to four
squash 5555555 change four to five

In this case, the first change will be cherry-picked, so the file one will be created. The subsequent change will be cherry-picked, so the contents will be changed from one to two. And subsequent changes will be cherry-picked, further updating the file, but those changes will be "squashed" into the prior commit. Thus you'll get the full contents, but you'll only end up with two commits.

(Had you marked them as fixup commits, you would get the same contents, but their commit messages would not be included in your suggested commit message.

like image 114
Edward Thomson Avatar answered Oct 20 '25 01:10

Edward Thomson


The problem is that git rebase uses contradictory terminology to how git as a whole defines commits. A commit is a snapshot, but rebase treats a commit as a patch (a set of changes) between its parent commit and itself.

So if you tell rebase to "delete a commit", it thinks you want to undo the changes applied by that commit's patch.

If you want to remove some snapshots while keeping later snapshots the same, you tell rebase to squash the commits. In rebase's terms, this means you're combining the changes from multiple commits into the latest of those commits.

like image 43
Mark Adelsberger Avatar answered Oct 19 '25 23:10

Mark Adelsberger



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!