Question

Ok. I am at my wits end trying to find out why I can't merge two commits into one. Git is horribly complicated. I have series of commits that looks like this locally and remotely:

commit 6abb264c06b06d42ea7e07a469107b9d3d72dffa
Author: XXXX <xxx@xxxx.xxx>
Date:   Thu Jan 23 15:59:03 2014 -0500

   First Check-in again (forgot to add project file and forgot to tick off append to last commit)


commit 90b2468f5267a471d52f2d7cc7cb1cd8604b3b86
Author: XXXX <xxx@xxxx.xxx>
Date:   Wed Jan 22 22:57:20 2014 -0500

   First check-in 

I want to combine them together into to look like this:

commit 90b2468f5267a471d52f2d7cc7cb1cd8604b3b86
Author: XXXX <xxx@xxxx.xxx>
Date:   Wed Jan 22 22:57:20 2014 -0500

   First check-in 

I tried to rebase using pick, squash and whatever gourd named command available to no avail. It tells me it has squash/merged/picked whatever, but I always see two commits instead of one.

Also couldn't figure out what the difference was between pick and squash. Also, I can't tell what the difference is between a commit and a branch. Moreover, I read that a branch is just a commit, but why do we have to merge branches but pick/squash (??) commits? This is terribly confusing.

Was it helpful?

Solution

You're tripping over a bunch of minor irritations I have with git, all at the same time. This is actually a good sign. :-)

First, rebase has problems with "rebasing" a root commit. In reasonably modern versions you can do this with git rebase -i --root, which will let you squash or fixup the second commit into the first (root) commit. You need that --root argument. (Older versions of git lack --root and make this substantially more difficult.)

Next:

what the difference was between pick and squash

In an interactive rebase, pick means "take the commit as is", and squash means "add this commit atop the previous commit". There's no fundamental difference between squash and fixup except that the former gives you a chance to edit the commit message, and the latter just takes the "fixup" as a tree change (throwing away the commit message text entirely).

what the difference is between a commit and a branch

A commit is an actual object inside the repository. "A branch" is an ambiguous term, which sometimes means "a branch label"—which is just a reference to a commit, or equivalently, a name for a single commit—and sometimes means the data structure formed by a series of commits with their parentage.

It's been said that "git makes more sense when you understand X", for many different values of X. This generalizes to "git makes much more sense once you understand git." :-) Which actually is true in more than just the tautological sense, but is not much help in getting you there! Fortunately this web series is very helpful (in my opinion). Read through it all, and the way that "branch" is ambiguous, but usually obvious which one someone means, will probably make more sense.


Edit: example session:

$ cd /tmp/trepo
$ git init
Initialized empty Git repository in /tmp/trepo/.git/
$ echo data > somefile; git add somefile; git commit -m 'first check in'
[master (root-commit) 64a2d9f] first check in
 1 file changed, 1 insertion(+)
 create mode 100644 somefile
$ echo other > another-file; git add another-file
$ git commit -m 'combine with first check in'
[master d375b81] combine with first check in
 1 file changed, 1 insertion(+)
 create mode 100644 another-file
$ git rebase -i --root
[in editor, change second line from "pick" to "fixup"; write and quit]
".git/rebase-merge/git-rebase-todo" 20L, 667C written
[detached HEAD 5b2a979] first check in
 2 files changed, 2 insertions(+)
 create mode 100644 another-file
 create mode 100644 somefile
Successfully rebased and updated refs/heads/master.
$ git log --oneline
5b2a979 first check in

Note another minor git irritation: git log shows commits in new-to-old order, but git rebase -i shows them in old-to-new order.

And: if you've pushed, the remote has the old commits, not the new one. So you'll see both the old ones, which are reachable via origin/master (assuming the remote is named origin and you're using the usual setup), and the (single) new one, which is reachable via master. You'll have to get the remote repo to take the new master, e.g., via git push -f (with all its consequences...).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top