How Can I Make The Commit History Clean In Git When Sync Upstream's Commit?

Viewed 1223

I have forked a repo from GitHub and made some commit on my master branch. And the upstream repo's master branch have some commits ahead of mine. So I need sync these commits.

$ git remote -v
origin  git@github.com:johnwatsondev/react-navigation.git (fetch)
origin  git@github.com:johnwatsondev/react-navigation.git (push)
upstream    https://github.com/react-community/react-navigation.git (fetch)
upstream    https://github.com/react-community/react-navigation.git (push)


$ git pull --rebase upstream master
From https://github.com/react-community/react-navigation
 * branch            master     -> FETCH_HEAD
First, rewinding head to replay your work on top of it...
Applying: Update: add logic for invoking back key pressed listener in CardStack.js and remove default process logic
Applying: Update: only android platform need process physical back key pressed event


$ git status
On branch master
Your branch and 'origin/master' have diverged,
and have 6 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)

nothing to commit, working tree clean


$ git pull
Merge made by the 'recursive' strategy.


$ git status
On branch master
Your branch is ahead of 'origin/master' by 7 commits.
(use "git push" to publish your local commits)

nothing to commit, working tree clean

I got this ugly commit history in my origin repo's master branch below:

commit history ugly

How can I make a elegant OP to sync my origin repo's master branch to upstream's master branch without ugly duplicate commits and the Merge branch *** commit?
(PS: I have make some change in my origin repo's master branch)

3 Answers

As answered in Squash my last X commits together using Git, you can squash the previous n commits into a single commit using

git rebase HEAD~n -i

When you run this command, a text editor will open, and you have to replace the pick in front of all n-1 commits you want to squash, with s. Please note that you have to do a force push after squashing. This way you can merge many commits into one.

If you want to get rid of the first three commits from remote, you need to force push to squash the commits.

  • git reset HEAD~3
  • git stash save 'Save the commit changes just in case'
  • git push origin -f master

Check this article out as well https://ncona.com/2011/07/how-to-delete-a-commit-in-git-local-and-remote/

DISCLAIMER: This could lead to data loss. Force push is not at all safe but that is the only way I know to remove commits from remote. Please feel free to suggest a robust solution or edit the answer that is more safe.

There are already lots of answers available on SO regarding how to reset a remote and local history, but I guess your question is more about how to avoid these ugly merge commits during a pull.

In general you don't want to use the default pull behavior when you want to keep a linear history, otherwise you get this ugly merge commits when there are both local and remote changes. You should get into the habit of using git pull -r to use rebase instead of merge strategy during pulls.

Of course you can also can make an alias for that or change the default pull behavior via pull.rebase=true or branch.master.rebase=true if you want to set this in a branch specific way. But git pull -r does some pretty magic stuff sometimes so I'm always wary in changing the default behavior.

The way I like to do it is to do an explicit fetch first via git fetch and only then do a git merge if it's a fast forward merge, or do a git rebase after reviewing the remote changes. This gives way more control. In fact git pull (in its default behavior) is basically a git fetch && git merge which is explains why you get a merge commit when there are new local and remote commits.

Of course you can also work on a local non-remote tracking branch and do explicit merges and rebases on the master branch.

Related