Illustrated Git commands
Git is an open source distributed version control system that can handle versioning from very small to very large projects efficiently and at high speed, and is the most widely used version management tool available
Although Git is a very powerful tool, I think most people would agree with me that it can also be... A complete nightmare 😐 Always found it useful to imagine in my head what happens when I use Git: how does the branch interact when I execute a certain command, how will it affect the history? Lydia Hallie, a foreign programmer from British Columbia "CS Visualized: Useful Git Commands". In this article, she shows developers how merge, rebase, reset, revert, cherry-pick, and other common Git commands work in a more visual way through animations
Merging
- Fast-forward (--ff)
A fast-forward merge may occur when the current branch has no additional commits compared to the branch being merged, Git first tries to perform the simplest option Fast-forward mode merge does not create a new commit, but instead merges the commits on the merged branch in the current branch
Now all changes made on the dev branch are available on the master branch. So, what happened to No-fast-foward?
- No-fast-foward (--no-ff)
It would be great if your current branch didn't have any extra commits compared to the branch to be merged, but unfortunately that rarely happens! If we commit changes on the current branch that the branch to be merged does not have, git will perform a No-fast-foward merge. With a No-fast-foward merge, Git creates a new merge commit on the active branch. The commit's parent commit points to the active branch and the branch to be merged!
No big deal, perfect merge! The 🎉master branch now contains all the changes we made on the dev branch.
Merge Conflicts
While Git is good at deciding how to merge branches and add changes to files, it can't always make that decision on its own 🙂 This can happen when two branches we're trying to merge have changes on the same line in the same file, or if one branch deletes a file modified by the other branch, etc.
In this case, Git will ask for your help in deciding which of the two options we want to keep! Suppose that on both branches we edit README.md.
If we want to merge dev to master, this will cause a merge conflict: do you want the title to be Hello! or Hey!
When trying to merge a branch, Git will tell you where the conflict occurs. We can manually delete the changes we don't want to keep, save the changes, add the changed files again and then commit the changes 🥳
Yay! While merge conflicts are often annoying, it makes total sense: Git shouldn't just assume which changes we want to keep.
2.Rebasing
We have just seen how to apply changes from one branch to another by performing a git merge. Another way to add changes from one branch to another is to do a git rebase.
Perfect, all the changes we make on the branch are now master available to dev on the branch!
One big difference compared to merging is that Git doesn't try to figure out what files to keep and what not to keep. The branch we're basing always has the latest changes we want to keep! This way, you don't run into any merge conflicts and keep a nice linear Git history.
This example shows a master branch based change base. However, in larger projects, you don't usually want to do this. agit rebase changes the project's history because a new hash is created for the replicated commit!
Rebasing is great whenever you are working on a functional branch and the master branch has been updated. You get all the updates on the branch, which will prevent future merge conflicts! 😄
Interactive Rebase
We can modify them before resubmitting the commit! 😃 We can do this with an interactive rebase. Interactive rebases are also useful for branches that you are currently working on and want to modify some commits.
We can perform 6 actions on the commit we are rebasing.
reword: change the commit information
edit: modifies this commit
squash: merge the current commit into the previous commit
fixup: merges the current commit into the previous commit, without keeping the commit log message
exec: execute a command on each commit that needs to be base changed
drop: delete commits
Amazing! This gives us full control over our commits. If we want to delete a commit, we can drop to do so.
Or, if we want to compress multiple submissions together to get a clearer history, no problem!
Interactive varbases give you a lot of control over the commits that try to varbases, even on the currently active branch!
Resetting
We may submit changes that we don't want later. Maybe it's a WIP commit, or maybe it's a commit that introduces an error! 🐛 In this case, we can run git reset.
Agit reset gets rid of all the currently staged files and allows us to control where HEAD should point.
Soft reset
Soft reset moves the HEAD to the specified commit (or the index of the commit compared to the HEAD) without removing the changes introduced later in the commit!
Suppose we don't want to keep the commit where 9e78i added the style.css file, or the commit where 035cc added the index.js file. However, we do want to keep the newly added style.css file index.js! A perfect use case for a soft reset.
Type in git status and you'll see that we still have access to all the changes we made to the previous commit. This is great, because it means we can fix the contents of these files and commit them again later!
Hard reset
Sometimes we don't want to keep the changes introduced by certain commits. Unlike a soft reset, where we no longer need access to them, Git should simply reset its state back to the state it was in when the commit was specified: this even includes changes in the working directory and staging files! 💣
Git drops the changes 035cc introduced on 9e78i and and resets its state to the state ec5be at commit time.
Reverting
Another way to undo changes is to run git revert. By reverting a commit, we create a new commit that contains the reverted changes!
Let's say ec5be adds an index.js file. Later, we actually realise that we no longer want this change introduced by this commit! Let's restore the ec5be commit.
Perfect! Commit 9e78i reverts the changes introduced by the ec5be commit. Performing agit revert is very useful for undoing a commit without having to modify the branch history.
Cherry-picking
When a branch contains a commit that introduces the changes we need on the active branch, we can use this command with cherry-picking! With a cherry-picking commit, we create a new commit on the active branch that contains the changes introduced by the cherry-picked commit.
Let's say that the commit dev on branch 76d12 adds changes to the file index.js that we want in the master branch. We don't want this to be the only commit we care about!
Cool, the master branch now contains the changes introduced by 76d12!
Fetching
If we have a remote Git branch, like a branch on Github, it might happen that the remote branch has commits that the current branch doesn't have! Maybe another branch was merged, your colleague pushed a quick fix, etc.
With git fetch we can get these changes locally by running a on the remote branch! It doesn't affect your local branch in any way: fetch simply downloads the new data.
We can now see all the changes that have been made since the last push! Now that we have the new data locally, we can decide what we want to do with it.
Pulling
While agit fetch is very useful for getting remote information about a branch, we can also do a git pull. agit pull is actually two commands in one: agit fetch and a git merge. When we pull changes from the source, we first fetch all the data as we would with a git fetch, and then the latest changes are automatically merged into the local branch. then the latest changes are automatically merged into the local branch.
Great, we're now perfectly synced with the remote branch and have all the latest changes! 🤩
Reflog
Everyone makes mistakes, and that's totally okay! Sometimes you might feel like you've messed up your git repo to the point where you just want to delete it completely.
git reflog is a very useful command for displaying a log of all the actions that have been taken! This includes merges, resets, restores: basically any changes made to a branch.
If you make a mistake, you can HEAD easily redo this based on the information reflog provided to us!
Let's say we don't actually want to merge the origin branch. When we run the git reflog command, we see that the status of the repo before the merge is at HEAD@{1}. Let's run a git reset to point HEAD back to where it was at HEAD@{1}!
We can see that the latest action has been pushed to reflog!
Git has so many useful porcelain and plumbing commands, I wish I could go over them all! 😄 I know there are many other commands or changes I don't have time to cover right now - let me know what your favourite/most useful commands are and I'll probably cover them in another post!
As always, feel free to get in touch with me! 😊