Category Archives: VCS - Page 2

Git: Squash your commits

When working on a pull-request that keeps getting updated with commit after commit you sometimes need too squash some commits to get a nicer history

I’m here going to show you one way to do just that using the interactive view of git rebase

git log --pretty=oneline

This could look something like this:

5d870be Added submit button
56f962c Added knockoutjs logic
5640719 Merge pull request #1148
5015685 Set encoding on property file
4ec2f9c Updated test database scripts
567cd41 Checkstyle fix
980be70 Added border to form
405fc82 Resolved conflict with main
52eb187 Fixed login div border
fc2b99b Fixed bugg with form validation
2f8f126 Updated JQuery version
0acd0fe Fixed flow test
4dfb2dd Added form validation test
3cc1247 Added xml parser

Now we look for a good place to squash. Let’s say that we for example want to squash row 1 to 5. To start the process we run:

git rebase -i 567cd41

We here choose the hash of the commit just below the one where we want to squash from. This will bring up your favorite editor (in my case VIM) where you can mark which commits you want to keep (‘pick’) and which ones you woulkd like to squash (‘squash’). You can even rearange you commits here – just make sure to rearange the hashes too 🙂 . Note! The order of commits in the editor is reversed. Older above and newer below

pick 567cd41 Checkstyle fix
squash 4ec2f9c Updated test database scripts
squash 5015685 Set encoding on property file
squash 5640719 Merge pull request #1148
squash 56f962c Added knockoutjs logic
squash 5d870be Added submit button

# Rebase 567cd41..5d870be onto 5015685 (6 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> 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

After you have made your changes you save and quit, and the rebasing operation starts.
This process can find conflicts that needs to be resolved, like any other rebase. You will also be asked to set the commit message of the squash. This messages will contain all of the commit messages so here is a good oppertunity to make them pretty and understandable 😉

NOTE! This solution might not play nice when there are merge commits present!

Tested on Git v2.24.3 and OSX 10.15.6

Git: Remove a rebase that has not yet been pushed

Every now and then I find myself in a situation were I want to completly remove a rebase I just did. Maybe the application does not build anymore and I want another shoot at doing that rebase or squash

I’m here going to describe one way using git reflog and git reset

We start with the git reflog to find the place we want to return HEAD to.

git reflog

Below is an example of a reflog with a recent rebase (squash):

d21df0dd1 HEAD@{44}: rebase -i (squash): # Combination of 5 commits.
2b8f61237 HEAD@{45}: rebase -i (squash): # Combination of 4 commits.
049fdd05e HEAD@{46}: rebase -i (squash): # Combination of 3 commits.
a3c59ce01 HEAD@{47}: rebase -i (squash): # Combination of 2 commits.
1029f7e05 HEAD@{48}: rebase -i (start): checkout HEAD~19
5d870be58 (origin/MyBranch) HEAD@{49}: commit: Moved border to window
5d870be58 (origin/MyBranch) HEAD@{50}: commit: Added submit btn
5d870be58 (origin/MyBranch) HEAD@{51}: commit: Removed CSS class one
56f962c08 HEAD@{52}: commit (merge): Fixed conflicts with main branch

We are here looking for the entry just before the ‘rebase -i (start)‘, which in this case is HEAD{49} (HEAD{48} is the ‘rebase -i (start)’ entry)

After this all we need to do to return HEAD to that place is to run

git reset --hard HEAD{49}

NOTE: THIS WILL REMOVE ALL CHANGES BEFORE HEAD{49}

Done!

Tested on Git v2.25.0 on a Windows 10

My Git cheat sheet

INIT

Init an empty Git repository or reinitialize an existing one

git init

Clone a repository

git clone https://mygitbucket.com/niklas/my_project.git

Pull in new changes from remote

git pull

Do a dry-run fetch to see what is comming

 git fetch --dry-run

BRANCH

Create a branch locally and checkout the branch

git checkout -b <name of branch>

Push the branch to remote

 git push origin <name of branch>

Delete local branch

git branch -d <name of branch>

Delete remote and local

git branch -D <name of branch>

Show all branches

 git branch -a

List all remotes

git remote -v

Rename a local branch that you have checked out

git branch -m <new name>

Rename a branch you have not checked out

git branch -m <old name> <new name>

SUBTREE

Add a remote subtree to your project. ‘common’ is here the name of the subtree

git remote add common https://mygitbucket.com/niklas/my_project.git

Do a ‘common pull’ i.e. pull a remote branch into to subtree. ‘common’ is here the name of the subtree (and folder)

git subtree pull -P common common my-subtree-branch --squash

LOG

Pretty print log with oneline

git log --pretty=oneline

Check commits only in the current branch (descendent to master)

git log master..

Check commits only in the current branch (descendent to other branch)

git log master..<name of branch>

PATCH

Create patch files for every commit in a interval of SHA’s

git format-patch <start SHA>..<stop SHA>

HEAD and other short codes work too
Create ONE patch file for all commits in the last 10 commits (from HEAD and back)

git format-patch -10 HEAD --stdout > my.patch

Apply a patch

git apply my.patch

STASH

Show the content of your current stash

git stash show

Show a diff of the content of your stash and current code base

git stash show -p

Stash all uncommited modifications

git stash

Apply last saved stash to your current code

git stash apply

Apply last saved stash to your current code AND delete the stash

git stash pop

Manually delete the stash

git stash drop

TAG

List all tags

git tag

Create a “lightweight” tag

git tag <tag name> <SHA*>

* SHA is optional. Without it you will tag the place you are at
Create a “heavyweight” tag

git tag -a <tag name> -m <message> <SHA*>

* SHA is optional. Without it you will tag the place you are at
Remove a tag

git tag -d <tag name>

MISC

List all commits waiting to get pushed

git cherry -v