0
votes

I want to push several single commits to a git remote repo. I followed Geoff's answer found here to do so:

How can I pushing specific commit to a remote, and not the previous commits?

The commits I want to push are not at the head, so I have to reorder the commits using rebase first and I used these instructions to do so:

http://gitready.com/advanced/2009/03/20/reorder-commits-with-rebase.html

Essentially I've done:

git clone
git commit
git commit
...
git pull
git rebase -i HEAD~3
git push origin <SHA>:master

I got errors doing this. So I started looking deeper into the problem. I found that there are duplicate commits in my log if I do a second git pull after rebasing, for example:

git clone
git commit
git commit
...
git pull
git log --pretty=format:"%h - %an : %s" // log before rebasing
git rebase -i HEAD~3
git pull
git log --pretty=format:"%h - %an : %s" // log after rebasing
git pull 
git log --pretty=format:"%h - %an : %s" // log after rebasing after pulling

So I posted this question:

git: Duplicate Commits After Local Rebase Followed by Pull

Roger's response there led me to this question: Why do I see duplicate commits after rebasing and pulling?

From above, the log before rebasing looks like:

84e4015 - Me : Local Commit 3
0dbe86a - Me : Local Commit 2
d57ba2a - Me : Merge branch 'master' of remote repository
a86ea35 - Me : Local Commit 1 before reordering
2fc4fe7 - Remote User 2 : Remote Commit 2
b7a8656 - Remote User 1 : Remote Commit 1
8ce80fc - Me : Merge branch 'master' of remote repository

And the log after rebasing looks like:

cf1ff7b - Me : Local Commit 3
cd14463 - Me : Local Commit 2
b9d44fb - Me : Local Commit 1 after reordering
9777c56 - Remote User 2 : Remote Commit 2
a2d7d8b - Remote User 1 : Remote Commit 1
8ce80fc - Me : Merge branch 'master' of remote repository

Notice that the original 2 commits 2fc4fe7 and b7a8656 have new SHAs; 9777c56 and a2d7d8b. I believe this is the start of the problem.

Now after I do another git pull the log looks like this:

e8e1a85 - Me : Merge branch 'master' of remote repository
cf1ff7b - Me : Local Commit 3
cd14463 - Me : Local Commit 2
b9d44fb - Me : Local Commit 1 after reordering
9777c56 - Remote User 2 : Remote Commit 2
a2d7d8b - Remote User 1 : Remote Commit 1
2fc4fe7 - Remote User 2 : Remote Commit 2 // duplicate 2
b7a8656 - Remote User 1 : Remote Commit 1 // duplicate 1
8ce80fc - Me : Merge branch 'master' of remote repository

Notice that the remote commits are now duplicated, and the original SHAs of the remote commits, 2fc4fe7 and b7a8656, have returned.

In Roger's response he said that it looked like the fault of other people pushing to git and that they were rebasing their already pushed commits. But I believe it is my fault for locally rebasing a pushed commit.

Is this because I rebased a commit that had already been pushed to the remote? If so, what should I have done to avoid this? I need to rebase my commits so that I can push a single commit. Should I have used a branching system to do this? If so how would I use branches to solve this problem?

1

1 Answers

1
votes

The short answer is that rebase does not change commits,1 but rather copies them. Git normally then hides away the originals, but if your originals include those shared by other users, you (and of course they) and still see those originals.

As a general rule, you should only rebase your own private, unpublished commits. Since no one else has a copy of these by definition, the fact that you make your own copies and then (via rebase) hide away your originals is not a problem: you now see your copies instead of your originals, and no one else sees either, and you can continue to rebase if needed. As soon as you publish (via push or similar) a commit, though, you can no longer change it, because someone else now has a copy of your original, including its SHA-1 ID, and they will still have it later.

What you've done in this case is to rebase (i.e., copy) their commits as well as your own. Part of the problem stems from using git pull, which means "fetch then merge", when what you wanted was "fetch then rebase". You can either do the steps separately:

git fetch
git rebase

or use git pull --rebase:

git pull --rebase

which tells the pull script that after doing the fetch, it should do a rebase instead of a merge. You can also configure git to do this automatically, without the --rebase argument.2

The main problem right now is that you have a merge you probably didn't want. If so, you will need to "undo" that merge (with git reset; see other stackoverflow postings).


1It can't: a git object, including a commit, is named by its object ID, which is a crypographic checksum of its contents. A commit is comprised of its parent ID(s), the ID of the tree, the commit's author and committer (name, email, and timestamp), and the commit message. If you change any of these, you get a new, different commit with a different ID.

2You can even configure it to use git pull --rebase=preserve. However, preserving merges across rebase operations is a separate topic (which I've covered somewhat before in stackoverflow postings).