git squash all commits into one and merge back into master with rebase and merge –ff-only

This is my workflow.

Work in a remote branch:
git checkout -b topsecretbranch

Make lots of small iterative changes, committing each one with vague messages like “small changes”, “typos” and “lol”.

At some point this code will be ready to merge back into master. Instead of polluting the commit history with confusing and unhelpful commit messages, first use rebase to squash all the changes into one.

Let’s say there were 4 commits. Then first rebase from master:
git rebase master

This will pull in all the commits from master first, without the messy merge commits that “git pull origin master” generate.

Next, squash all commits with rebase in interactive mode. Let’s say there are four commits you want to squash:
git rebase -i HEAD~4

This will open up a text editor (default VIM). Pick the commit you want to keep (usually the top one), and replace “pick” with “s” (for squash) for the rest. For example:

pick f96d52f Add new API endpoint
 s 7247ac7 add test
 s 62c7c02 fix test
 s d38ca5e typos
 Rebase 758eaa1..890aff7 onto 758eaa1 (4 commands)
 #
 Commands:
 p, pick  = use commit
 r, reword  = use commit, but edit the commit message
 e, edit  = use commit, but stop for amending
 s, squash  = use commit, but meld into previous commit
 f, fixup  = like "squash", but discard this commit's log message
 x, exec  = run command (the rest of the line) using shell
 b, break = stop here (continue rebase later with 'git rebase --continue')
 d, drop  = remove commit
 l, label  = label current HEAD with a name
 t, reset  = reset HEAD to a label
 m, merge [-C  | -c ]  [# ]
 .       create a merge commit using the original merge commit's
 .       message (or the oneline, if no original merge commit was
 .       specified). Use -c  to reword the commit message.
 #
 These lines can be re-ordered; they are executed from top to bottom.
 #
 If you remove a line here THAT COMMIT WILL BE LOST.
 #
 However, if you remove everything, the rebase will be aborted.
 #
 Note that empty commits are commented out

Save the changes. This will now pop up a new text editor window where you can edit the commit messages. I usually just keep one around and delete everything else. Once this is done, checkout master:
git checkout master

Now merge changes using fast forward only (skips the confusing merge commit):
git merge topsecretbranch –ff-only

Done correctly, you will have one clean commit in master branch of all your code changes.

Caveats – if you’ve already pushed your remote branch to origin, and you want to squash the commits in your branch be reflected in the remote as well, you’ll have to do a force push

Committing to git as a different user

I have two accounts on git, one for work, and one for personal use. On my work laptop, I push all my commits to origin on my work account. But as part of learning Scala for work, I created a sandbox where I could play around with Scalatra. I created a repository on git for my sandbox, under my personal user name. I wanted to push all commits in this repository under my personal account. I had gotten all this working and promptly forgot how I set it all up. Unfortunately, it stopped working, so I set it all up again.

I first removed the [Credential] section of my git config:
git config –global –edit

It had been using credential.helper osxkeychain and my keychain had been hosed before. This was probably why everything stopped working. I decided to start from scratch and forgo using keychain.

The first step was to create the ssh key and then adding it to git, as documented here

The second step was to tell git to use my personal account for my scalatra sandbox repository. For some reason, git was still trying to use my work email to push commits to my sandbox repo. After much frustration and googling, I found the answer on stack overflow

The missing step was to edit ~/.ssh/config and add two sections, one for work, and one for personal:

#personal
Host github.com-jieyangh
     HostName github.com
     User git
     IdentityFile ~/.ssh/id_rsa
     IdentitiesOnly yes
   
#work
   Host github.com-jiehu
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_rsa_work

Note that you always connect to git as the user “git”.

Next I deleted my remote origin and re-added it, this time specifying the key to use:

git remote remove
git remote add origin git@github.com-jieyangh:jieyangh/scalatra-sample-API.git

where git@github.com-jieyangh was the host value specified under ~/.ssh/config and jieyangh/scalatra-sample-API.git was the username followed by the git repo (git could not find the repo unless it was preceded by my username).

After that, I was able to push to my work repos as my work account, and my sandbox under my personal account once more.