Sumit Bajracharya • Jan 20, 2020

Git is arguably the most popular version control system used. It is difficult to imagine an IT project without some sort of version control system to help the software teams manage changes to the source code over time. It is a really powerful tool capable of tracking and comparing all the changes and keeping multiple teams of engineers in sync.

Git works directly on your source code and hence, it can also cause a lot of headache when not used properly. No matter how well you manage the code, things can inevitably go wrong either due to your own mistake or your colleague’s. In such case, it becomes really handy to know some of the advanced git commands that can help you clean up the mess.

1. Temporarily store uncommitted changes with git stash

Stash is useful when you want to temporarily store your uncommitted changes or you are halfway through the code change but you want to work on something else without having to commit the incomplete change.

> git stash Saved working directory and index state WIP on master: 16622e7 modified user login page

Also, you will need to use -u flag to include untracked files in the stash.

> git stash -u

git stash will store your current uncommitted changes for later use and revert those changes in your current branch. Now you can freely switch branches, pull in changes or perform any other git operations and re-apply the stashed commits later on top of it using git stash pop .

> git stash pop

Pretty handy, isn’t it?

You can also maintain multiple stashes and choose to apply a specific stash with the command git stash apply [stash index] or git stash pop [stash index] . If you use git stash apply command instead of pop, the stash will still remain in the stash list which you can clear by using git stash clear .

If you ever get merge conflicts while applying your stash, fix your merge conflicts and then do git reset .

2. Undo recent local commits

Sometimes, you might accidentally add wrong files to your commit or want to undo your commit for some reason. If you haven’t yet pushed it to the server, you can simply undo the commit with the git reset command.

> git reset HEAD~1

Here, ‘HEAD~1’ tells Git to move the HEAD pointer back one commit. If you want your HEAD pointer to go back to specific commit, you can check the commit log using git log and then use the commit hash to go back to. All the changes will still be preserved.

> git log --oneline 16622e7 (HEAD -> master) modified user login page 5a815fd added signup pageb 51ea80 project initialized > git reset 5a815fd

If you want to undo the commit as well as remove all the changes as well, you need to pass the --hard flag. This will also remove all other changes that have not yet been committed as well. Pass the --keep file to preserve the uncommitted changes.

> git reset --hard HEAD~1

> git reset --keep --hard HEAD ~1

There is also --soft flag that you can use which will undo the commits but will keep them staged.

> git reset --soft HEAD~1

Undoing public commits

In order to undo commit that has already been pushed, you can use the git revert command.

> git revert <commit sha>

If you want to revert range of commits,

> git revert <nth last commit sha>..<latest commit sha>

What this basically does is it creates a new commit that will undo the specific commit. The commits made and the revert commit will be visible in your git history.

If you want to permanently remove the commit along with its trace from the git history instead,

[Warning: This should only be done if you are the only one working on the branch and no one has pulled in the changes yet.]

> git reset --hard <commit sha>

This will undo and remove the commit. Use HEAD~1 to undo only the last commit. Then push the changes using the -f flag which will force push the change.

> git push -f <remote-name> <branch>

To modify the changes before push, remove the –hard flag. This will re-checkout all the updates locally. You can then make your changes and commit them and push with -f flag.

3. Undo uncommitted changes

To remove your uncommitted changes, you have couple of different ways.

Using stash

> git stash -u Saved working directory and index state WIP on master: 16622e7 modified user login page > git stash drop Dropped refs/stash@{0} (16622e797263b34aa1298050c119d2fc51be2708)

If you have multiple stashes maintained

> git stash drop 0 Dropped refs/stash@{0} (16622e797263b34aa1298050c119d2fc51be2708)

Using reset and checkout

> git reset --hard > git checkout . > git clean -fd

4. Move your local committed changes to different branch

For new branch

> git branch <new-branch-name> # based on the number of commits or the last commit hash > git reset --hard HEAD~1 # or > git reset --hard <commit sha> > git checkout <new-branch-name>

For existing branch

> git reset HEAD~1 > git stash -u > git checkout <branch-name> > git stash pop > git add . > git commit -m "message"

5. Merge multiple commits into single one

If you want to merge all the last local commits,

> git reset --soft <branch-name> > git commit -m "new commit message"

If you want to merge n number of previous commits

> git reset --soft HEAD~5 # number of commits you want to merge # or using the last commit sha > git reset --soft <commit sha> > git commit -m "new commit message"

This only works for merging last N commits. If you want to merge commits in the middle, you will need to use git rebase .

Let’s say you want to merge the 2nd last and the 3rd last commit into single one.

> git rebase -i HEAD~3

You will get something like below listing all the commits and the word ‘pick’ at the beginning. Notice how the commits are in reverse order i.e. oldest first.

pick 310154e added README pick 4e8ac0f updated README formatting and added blame pick f7f3f6d changed name a bit

Replace ‘pick’ with ‘squash’ or ‘s’ for the 2nd line. This will merge the 2nd last commit to the 3rd last commit. (2nd line below will be merged to the 1st line)

pick 310154e added README s 4e8ac0f updated README formatting and added blame pick f7f3f6d changed name a bit

Now save the editor with :wq. You will be prompted to select a commit message. By default, the commit messages will be concatenated into a single commit message but you can choose a different commit message altogether as well.

In case of public commits (which I have already mentioned above that it is not recommended if others have pulled in your changes), you can push the changes with -f flag.

> git push -f <remote-name> <branch>

If you are just looking to merge public commits from one branch to another branch, there is a simpler way to do it.

Note: Make sure you have no uncommitted changes

> git checkout <branch-name> # the next step is optional > git pull origin <brach-name> > git merge <branch-with-commits> > git reset origin/<current-branch> > git add --all > git commit -m "new message"

6. Rename local commit message

If you want to rename your last local commit message, you can pass --amend flag to git commit.

> git commit --amend -m "new message"

The --amend flag is also useful when you want to add new changes to your last local commit without modifying the commit message.

> git commit --amend --no-edit

This will simply add your new changes to your last commit.

If you want to rename nth last local commit message, you can use git rebase that we used earlier for merging commit.

# n is the index of commit you want to update > git rebase -i HEAD~n

You will get the list of all the commits and you can rename the commit message leaving rest the same.

pick 310154e added README pick 4e8ac0f updated README formatting and added blame pick f7f3f6d changed my name a bit

7. Copy a commit from one branch to another

Unlike merge or rebase which can apply multiple commits into your branch, there is a cherry-pick command that will apply a specific commit to your branch. To be specific, “cherry-pick commit applies the changes introduced by the named commit on the current branch”.

> git checkout <branch-name> > git cherry-pick <commit-sha>

In case you are cherry-picking from public branch, you might consider using -x flag that appends a line that says “(cherry picked from commit …​)” to the original commit message in order to indicate which commit this change was cherry-picked from.

8. Adding committed files to .gitignore

If a file or files has been added to the repository and you want to ignore it in the future commits, simply adding it in .gitignore won’t work. Since it is still present in the index, it’ll still continue to be tracked by git. You’ll first need to untrack the files.

> git rm --cached [filepath]

If you are dealing with multiple files, you can remove them all using

> git rm -r --cached . > git add . > git commit -m "removed ignored files"

This will also work if your gitignore file is acting weird for some reason.

9. Advanced logging with git log

We already know that git log lists the commits made in the repository in reverse chronological order. You can also pass in various flags to customize the log display.

> git log --stat commit adc3a1f79427c61c81b339b445523cb815e1bac4 Author: John Doe <johndoe@gee-mail.com> Date: Sat Jan 17 16:41:29 2020 +0700 changed the version number package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)

The --stat flag will display the changes stats like list of modified files, number of lines changed, files added/removed, etc with the log.

If you want to see the changes in the files as well, you can use git log --patch -1 command. `-1` will show the changes in the last commit.

> git log --pretty=oneline adc3a1f79427c61c81b339b445523cb815e1bac4 changed the version number 312f1d5003f263c2d0e4b31a92e1965cc912239f removed unnecessary files 39815723a880cefd31a8c625877828b5359475e0 project initialize > git log --online adc3a1f changed the version number 312f1d5 removed unnecessary files 3981572 project initialize

With --pretty flag, you can change the log output to formats other than the default. You can provide few prebuilt options like --pretty=oneline which prints out one line commit messages and the hash. You can achieve the same with git log --oneline well.

> git log --pretty=format:"%h - %an, %ar : %s" adc3a1f - John Doe, 30 minutes ago : changed the version number 312f1d5 - John Doe, 3 days ago : removed unnecessary test 3981572 - John Doe, 3 days ago : first commit

If you pass in --graph flag, it will add a nice little ASCII graph showing the branch and merge history as well.

> git log --pretty=format:"%h %s" --graph * 8dce841 ignore errors * ca7fb6d Merge branch 'master' of git://github.com/user/project |\ | * c787fd6 Added login validations * | 97a5b4e timeout code and tests * | c21ec9c style changes * | 08cb40f added layout structures |/ * 35a1234 api sync issue fixes * 50caad4 Merge branch 'FCY-112' into local

git reflog

Git log shows the commit logs traversing back through the repo’s ancestry. Apart from this, there is also git reflog which is slightly different.

One of the things Git does in the background while you’re working away is keep a reflog - a log of where your HEAD and branch references have been for the last few months.

> git reflog 236b472 HEAD@{0}: commit: Added login validations 97a5b4e HEAD@{1}: commit: timeout code and tests b36b94c HEAD@{2}: reset: moving to HEAD~1

This becomes particularly important when you accidentally delete your commit or made mistakes while rebasing or resetting and you want to recover them. Even though the git log command won’t display them, you can find it in your reflog. You can use the commit hash to checkout and recover the state of your project.

Pro Tip:

It becomes hard and cumbersome sometimes to remember all the long commands or flag options that goes in with the git commands specially the log flags or amend flags. In that case, keeping a list of alias can be really useful and can save you lots of time.

alias glf="git log --pretty=format:'%h - %an, %ar : %s'" alias gll="git log --stat --abbrev-commit" alias gca="git commit --amend"

I hope you found this article useful. There might be other ways too to achieve these same things. This is just a list of commands that I’ve compiled based on my experience and which I like to use frequently. If you liked this blog, do checkout other articles from Botsplash as well.