Reputation: 9591
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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.
Upvotes: 0
Views: 677
Reputation: 490118
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 push
ed, 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...).
Upvotes: 5