The following guide compiles a list of useful advanced Git commands that will make our everyday coding life easier.
1. Merge vs. Rebase
The merging workflow of git allows us to integrate changes from one branch into another. Rebasing serves the same purpose, but it does so in a more streamlined fashion. Rebasing and merging use very different methods to integrate changes from one branch into another.
To understand the difference between them, let’s assume we have a repository with the following branch state.
We have total of two branches - master and feature. The base of the feature branch is commit “ab”.
If the development of the feature branch is completed and we want to move changes into the master branch, we use
git checkout mastergit merge feature
By merging feature into master, master obtains a new commit - in our example let’s assume, it is “xyz”
The new commit xyz is added into the master branch tree which represents every change that has occurred on a feature branch. Here the base of the feature branch is still “ab” commit.
Let’s rewind, and pretend that we still have to do some work on the feature branch but before we continue working on our feature branch, we decide we want to bring in the latest changes from master to keep things fresh.
Rather than merging master’s new commits into a feature, we opt to rebase our feature branch onto master.
git checkout featuregit rebase master
Actually what rebase does is pluck commits from a branch one by one chronologically and re-attach them to a different commit. And if any conflicts have occurred, we have to fix it. I have shown how to do that in point 2.
Now the base of the feature branch is changed from “ab” to “abcd”. We can see from the above tree why we choose to rebase instead of merge in this situation. Unlike with merging, rebase does not create an extra commit. This is ideal for our situation since all we are currently trying to do is keep our feature branch up-to-date with any new commits from the master. The major benefit of rebasing is that we get a much cleaner commit-tree.
The differences between a rebase and a merge are:
- The resulting tree structure of history is different. (generally only noticeable when looking at a commit-graph)
- Merge will generally create an extra commit.
- Merge and rebase will handle conflicts differently. Rebase will present conflicts of one commit at a time where merge will present them all at once.
So, when looking to incorporate changes from one Git branch into another:
- Use merge in cases where we want a set of commits to be clearly grouped together in history
- Use rebase when we want to keep a linear commit history
2. Resolve conflicts after a Git rebase
When we perform a git rebase, chances are we will run into a situation where a merge conflict is introduced. A merge conflict occurs when the exact same lines were edited in the same file by two commits. In that case, Git has no way of knowing what’s correct — we will have to look at the changes and decide how we want to handle it. When merge conflicts occur during rebase, we get the following error.
error: could not apply ba66749... something to add to patch AWhen you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
Could not apply ba66749b807321c786a65bbb9b2169b7a6ed957d... Change fake file
We have three choices to fix the commit that is causing a conflict (ba66749):
git rebase --abort: to completely undo the rebase. This will unwind all rebase changes and put the branch back in the state it was in before
git rebasewas called.
git rebase --skip: to completely skip the commit. Hence none of the changes introduced by the commit(ba66749) will be included in the history.
- We can fix the conflict using the standard procedure for merge conflict resolution.
3. Temporarily stash changes
In most cases, when we edit, add, or delete a file in your working repository, we would stage and then commit those changes to apply them to our project.
But let’s say, for instance, that team lead asks to review another branch…like Right Now. Our attention is diverted and we must switch branches, but we’re not ready to commit our changes Or, worse yet, we’ve been working on the wrong branch entirely. We don’t want to lose our work, but we can’t apply the changes to the branch currently checked out.
These are two common developer situations where stashing can save the day. When we create a stash, we are saving uncommitted changes so that we can work on other things without making a “real” commit or affecting our repository history.
$ git stash
By using the
git stashcommand, all untracked files are stashed. But what if we want to stash one specified file. Here is how we can do that:
$ git stash push <path>
4. Delete commit - locally and remotely
When working with Git we will find that sometimes commits need to be removed as they have introduced a bug or need to be reworked. Deleting a commit in Git must be approached in one of two ways, depending on if we have or have not pushed changes.
If changes have not been pushed yet and we want to delete the latest commit, here is a way to do that
# Use --soft if you want to keep your changes
git reset --soft HEAD~1# Use --hard if you don't care about keeping the changes you made
git reset --hard HEAD~1
In some of the cases, we want to delete the commit but keep our changes for a bit of editing before we do a better commit. The
--soft command will keep all working tree changes and
--hard command will discard all working tree changes. The above commands move HEAD to the commit before HEAD. If we like to delete the commits up until a specific commit, run
git log into the command line to find the specific commit id and then run below command.
# Use --soft if you want to keep your changes
git reset --soft <commit-id># Use --hard if you don't care about keeping the changes you made
git reset --hard <commit-id>
This will move HEAD to the commit .
If we already pushed changes to our origin, we have to first delete it locally like in the previous step and then push our changes to the remote by the following command.
git push origin +<branch-name>
The + sign before the name of the branch tells git to force the push.
We can delete the last n commit by the below command
# Delete last two commits
git reset --hard HEAD~2# Delete last n commits
git reset --hard HEAD~n
When we are working with a team of developers on a medium to a large-sized project, managing the changes between a number of git branches can become a complex task. Sometimes we don’t want to merge a whole branch into another, and only need to pick one or two specific commits. In order to perform this operation, we can use the cherry-pick command. It takes changes from a specific commit and applies them to your current branch in a new commit.
git cherry-pick <commit-id>
To demonstrate how to use
git cherry-pick let us assume we have a repository with the following branch state:
a - b - c - d Master
e - f - g Feature
Let's say we wanted to use commit `f` in the master branch. we execute the cherry-pick with the following command:
git cherry-pick f
Once executed our Git history will look like:
a - b - c - d - f Master
e - f - g Feature
The f commit has been successfully picked into the master branch.
6. Clean - remove all untracked changes
clean is a builtin command to clean up the untracked files. Untracked files are files that have been created within our repo’s working directory but have not yet been added to the repository’s tracking index using the
git add command. Before running the actual command and removing untracked files and directories, use the
-n option that will perform a “dry run” and show us what files and directories will be deleted:
# list all files that would be removed
git clean -n# list all files/directories that would be removed
git clean -n -d
The output will look something like this:
# Output of git clean -n command
Would remove project/blog/example.txt# Output of git clean -n -d command
Would remove project/test/
Would remove project/blog/example.txt
Once we are sure we want to go ahead and delete the untracked files and directories, type:
git clean -d -f
-d option tells git to remove untracked directories too. If we don’t want to delete empty untracked directories, omit
-f option stands for force.
git clean -f # remove untracked files
git clean -d -f # remove untracked files/directories
7. Who did all this?
If the production server is broken, we want to know the details of changes like who changed it or when. Just do a
git blame. It is used to examine the contents of a file line by line and see when each line was last modified and who was the author of the modification of that line.
git blame <fileName>
For example, we want to examine the manage.py file.
git blame manage.pyOutput
66fe3ebef (Tony 2020-06-14 11:33:01 +0530 1) import os;
3e537ffda (steve 2020-05-29 18:45:17 +0530 2) import sys;
5c5490232 (Tony 2020-06-14 09:55:03 +0530 3) import requests;
If anything was unclear, or you have any questions, comments, or suggestions, please feel free to leave them in the responses below! Thanks for your time!