This is the final article in the series — ‘Git For Beginners’. In the first article, I’ve explained the basic Git terminologies and we followed up with creating a basic Git repository and working on it. In the second part, we learned how to create a repository, clone it in our local machine, make a commit, push/pull changes, creating branches, and check Git status & logs. Now we will dive deep into some more concepts like — creating a merge request, resolving merge conflicts, rebasing, stashing, resetting, and a few more commands which will help you tackle most of the situations when working with Git.
I’ve covered Git in 3 parts of Git For Beginners series —
- Basic Git Concepts.
- Working With Basic Commands.
- Working With Advanced Git Commands (You are here).
Git Merge
When we want to combine two branches together, we use the Git Merge command. It takes the common base of the two branches and creates a new commit by merging the changes into the current branch.
Say, if you want to merge the Feature branch into your master branch, first check out the Master branch. On the Master branch, we will execute the following command —
git merge {branch_name}
Now, Git will find the common base and create a new commit in Master by merging the changes of the feature branch into the Master branch.
Initially, the two branch looks like this —
Now after we execute the Git Merge command, it will look like —
Git Rebase
Git Rebase will look similar to Git Pull and Git Merge but there is one major difference between them. It pulls the new commits from the remote but it helps in maintaining the Git history. Git Rebase will pick out the local commits then pull all the new commits and then place the local commit on top of the newly pulled commits. In that way, the Git history remains flat. It can be done using the command —
git rebase {Commit ID or Branch Name}
Another important usage of Git Rebase is when we want to make the changes from another branch and keep our changes on top of it. We can also do that again by using the same command. Here, Git Rebase differs from Git Merge as the history tree remains flat.
Creating Pull / Merge Requests
When we are done with committing our changes in the branch and want to merge it with the Master/Development branch, we can do it by creating Pull or Merge Requests. Creating PR/MR can be done easily from the UI itself by navigating to the Pull Requests tab in GitHub —
Squash Commits
Git Squash is a useful command when we want to keep commit history clean and readable. Many times we would be working on a feature and think it is complete and make a detailed commit. Then we would see that we missed adding a basic thing such as log lines or comment lines and we would have to add extra commits only for that and we end up adding commits with messages like — ‘Changes’, ‘Final Changes’, ‘Added comment lines’ etc, which becomes meaningless and confusing later when we or someone else wants to review the commits.
In such a case, we can combine several commits into one using Git Squash. For squashing commits, we will be modifying the Git history so we will have to force push the changes. For squashing the last ’n’ commits, execute the following command —
git rebase --interactive HEAD~{n}
Here, {n} can be 2 or more commits (number of commits you want to squash into one).
This will open an editor where the last ’n’ commits will be listed. Here, you can replace the pick in front of the commits (except the top-most) with ‘squash’. Doing this will open another editor where we can write a new commit message or use the existing commit messages from various commits that we are squashing. Once done, close the editor (press ESC key and type — ‘:wq’). Now, we will use the git push -f command (-f is the tag for force push) to push the changes to Git. Since we have modified the Git history and there is a conflict, we will force push the changes.
One important thing to remember here is — You are changing the Git history permanently and it cannot be recovered. So make sure you are fully aware of what you are trying to do.
You can do more when trying to squash commits. Sam Lindstrom has a nice article written on it — here.
Git Cherry-Pick
Git Cherry-Pick is a useful command when you want to pull a random commit from a different branch or take a commit from someone else’s work and append it to the current working HEAD. It is also useful when you might have made a commit to the wrong branch but can easily fix it by checking out the correct branch and cherry-picking that commit.
Cherry-Picking a commit can be done using the following command —
git cherry-pick {CommitID}
Git Stash
When we are working on a branch and have some unchanged files, switching between the branch or when trying to pull changes from remote will throw us an error saying — ‘Stash or commit changes before switching’. Git Stash helps us to record the current changes of the working directory separately and reverts back to the clean working directory stage. Stash is useful when you want to save the current changes for later use or if you want to apply those changes to a different branch.
We can create stash with either a name or without one. If we do not specify a name, it is created with a generic name as — ‘WIP on {branch_name}’. It is better to use a name in cases where we have multiple stashes as it will help us to know which stash was created for which changes.
For creating a stash, we can use the following command —
git stash (or) git stash save "{stash_name}"
Command to view the stashes available — git stash list.
Command to view the changes in the stash — git stash show.
git stash show -p stash@{n}
Using ‘-p’ will show code changes within the files. Without it, it will show the file name which is changed and the number of lines changed.
So now if we want to use our stash, we can do it in two ways —
git stash pop (or) git stash pop stash@{n}
This command will apply the stash and delete it.
git stash apply (or) git stash apply stash@{n}
This will apply the stash but will keep still keep a copy of the stash.
@stash{n} — It can be used to pop/apply nth stash rather than the last created stash.
For deleting a stash, we can either drop them one by one or clear all of them. The command to drop one stash at a time —
git stash drop (or) git stash drop stash@{n}
For clearing all the stashes —
git stash clear
We can directly create branches from a stash. For doing so, use the following command —
git stash branch {name}
git stash branch {name} stash@{0}
Sometimes, we might want to checkout only a single file or specific files from a stash. We can do that easily using the following command —
git checkout stash@{n} -- filename (or) git checkout stash -- filename
Resolving Merge Conflicts
When trying to merge, sometimes we will get merge conflicts that we will need to fix to continue with the merge process. Merge Conflicts occur when we are trying to merge a branch or pull new commits from Git and there are changes in the same files that we did on our local machine. To proceed, we will either have to accept the changes and make changes on our local copy or we can discard either remote or local changes completely.
We had made some changes to index.html and another contributor also was working on the same file and had committed his changes. Now when we were trying to pull the changes, it showed us that we have a merge conflict. In the below image, the conflict in the index.html can be seen. After resolving the conflicts, we used the ‘git add’ command and then used ‘git rebase –continue’ to finish the pull operation. If you are not sure of the changes, you can use ‘git rebase –abort’ which will revert the process entirely.
Here, we had a merge conflict when we tried to pull in the latest changes from the Git. There can be multiple instances in the same file or multiple files too. When searching for conflict instances in a file, we can search for ‘<<<<<<<’. Here, the changes before ‘=======’ refers to the Git remote changes, and the next lines till ‘>>>>>>>’ refers to our local changes. For resolving, we can remove the <,>, and = lines and keep the necessary lines and continue the pull/merge or the rebase process we were doing by appending the ‘–continue’ tag to the operation. If you wish to discard the ongoing process, you can do it by appending the ‘–abort’ tag.
Destructive Git Commands
Now that we have done many constructive things, many times we would end up messing big time. But no worries. Even if you happen to do so, there are commands which you can execute with a chill mind to get back on track. But make sure you are in control of the commands you are executing or else you will end up destroying more things than before.
Here, we have two main Git Commands —
Git Reset
For undoing a commit, we can make use of the git reset command. We can undo ’n’ number of commits in two ways — soft, hard. If we go with soft, the changes will be undone but it will be available in our Staging Area. If we go with the hard reset, the changes will be discarded directly. We can reset commits using the commands —
git reset --soft HEAD~{n}
git reset --hard HEAD~{n}
Here, {n} is the number of commits you want to reset. Instead of using the number of commits, we can do it by specifying the Commit ID also —
git reset --{soft/hard} {Commit ID}
For undoing a reset command (Irony!), we can do it using git reflog and got reset. We will first use the ‘git reflog‘ command first to find the ID and then do —
git reset {Commit ID}
This will undo the reset command we tried to do earlier. Alternatively, we can use —
git reset ORIG_HEAD
ORIG_HEAD references to the commit the HEAD was previously referenced.
Git Revert
Git Revert and Git Reset might sound similar but they are different from each other. While Git Reset modifies the HEAD pointer and moves the reference, Git Revert does not move it. Git Revert is used to undo changes to a repository’s commit history without moving the reference pointer. It will also create a new commit with the undone changes. The command to revert is —
git revert {Commit ID}
If we do not wish to create a new commit, we can neglect that by using the ‘–n’ tag to the git revert command which stands for ‘–no-commit’ —
git revert -n {Commit ID}
Git Client Tools
While doing all the Git operations manually can be a pain, there are few Git Client software developed for managing Git easily using a GUI. Using the software makes doing Git operations easily as well as we do not accidentally end up messing the Git history.
Some of the software I’ve personally used and would recommend are —
- SourceTree — Download Here.
- TortoiseGit — Download Here.
Final Words
We have reached the end of a thorough Git session here. I hope now you would be comfortable the next time you are asked to work on a project using Git or would take the initiative to manage the project using Git Version Control System.
If you are looking for more such articles and to learn in-depth concepts on Git, Atlassian has crafted some amazing and well-written articles. Do give it a read.
If you want to explore more and learn the internal working of Git, here’s a video by GitLab — https://www.youtube.com/watch?v=P6jD966jzlk.