As I was working on a little project of mine, we were discussing with @derlin and we had an interesting conversation that led to the usage of Git.
It was a really eye-opening discussion because, as a (really) (I mean, really) lazy developer, I always used my IDE to manage my repository, commit, push my code, etc.
But, as I was complaining about the GUI of Visual Studio for Git (that I personally find a bit confusing compared to IntelliJ), @derlin asked me something I was not prepared to answer:
- Why are you using your IDE ? It so easier to do that using the terminal and the advantage is that it never changes ! Don't you know how to use it that way ?
Well, after thinking about it, I felt a bit dumb because, indeed, I don't clearly KNOW how to use Git with the terminal, and managing my repository using only command-line tools looked like an impossible mission to me.
She wrote that fantastic post about Git to GIT GUD that I suggest you to read, among other incredible posts, and I decided to complete the challenge for many reasons:
- 1. I'm a lazy developer, but one important thing is to understand the tools you're working with
There are a lot of developers that can make amazing things, code apps that are incredible, but when an hardware errors shows up - they're lost.
For me, this is related to this first point: Your tool is your computer, if you don't understand it, there's definitely something to do about it.
I'm talking when the computer shows an error like "DEFECTIVE RAM" and the user is like "WTH is that ?!?", thinking that opening their PC is like jumping on a Pandora's box.
- 2. Git is a powerful tool
I always used IDE plugins to manage that side because with great power comes great responsibility. And that's the perfect case of Git.
You're always frightened to "break" or "lose" something when in fact, when you know how to tame that power, it becomes yours.
- 3. My ego was struck
No I'm kidding, I just cried a little.
Jokes aside, I thought about it and there is a lot of cases where you could be in that situation - you have no IDE, only the terminal, and you need to apply changes efficiently to make things work (a quick fix, a reset, a rebase, and so on).
So, let's begin the challenge, shall we?
Here are the levels proposed by @derlin :
Level one
Create a branch, commit, and push, but you can't use any external tool such as Gitlab or your favorite GUI. No clickops, terminal only. This holds for all the other levels.
This is fairly simple, but I learned something. I was used to create the branch and then checkout, when you can do it in a single command.
-
git checkout -b my_branch
(To create AND checkout the branch) -
git commit -a -m "Stage all files and add this message"
(To stage and commit all files with a message) -
git push [origin main]
(Push all changes to origin from your current branch)
You can also do git commit -a
: this will show the text editor to make multi-line commit messages if you want to.
Okay, first level done, to the next one
Level two
You have to split your changes into 5 commits. Bonus: the files are from only two files (and please, don't use the old trick of copying all the changes somewhere, and then pasting them one by one to "prepare" each commit)
Since I pushed changes into another branch just before, I didn't want to reset my branch and force push later, so my first push was just a little change just for the sake of the exercise.
I then worked on my project and I had multiple changes to commit, on multiple files and not only 2 - for that, I used the command that I liked the most: git add -i
.
This show up a really cool interface to manage your changes interactively and in a really simple way. In fact, once you got your hand on that tool, Git doesn't look that complicated (still a bit, but less).
First things first then, slam that command: git add -i
.
This opens the interactive mode and offers you multiple options where you can have explanations using the help command (by typing h
, help
or 8
).
Options I used most are:
-
1
- status -
3
- revert, yes I made mistakes (A LOT GOSH DANG IT) -
4
- add untracked, for new files -
5
- patch, to select part of files to stage instead and select hunks (or split hunks into sub-hunks) - and
6
- diff, to show current changes
PS: I had to check the definition of "hunk" as I was more used to the word "chunk" - it simply means "a large piece of something" OR "a large, strong, sexually attractive man".
... Okay, I guess we're referring to the first meaning here.
You always have access to an help for each command, using the help
command above on "main" screen of the interactive mode, or by using ?
after entering in any option.
Then, to split my changes into multiple commits, I used the patch command. You enter the option, which prompts you to select files - you simply type the index of the file you want to patch (or many separated by commas, even *
for all) and Git automatically splits changes in each file into hunks.
You can then:
- navigate to previous (
k
) or next (j
) unstaged hunks - split (
s
) hunks into smaller parts - and decide if you want to stage it (
y
) or not (n
)
There are of course other commands, like quitting (q
) or choosing to not stage any of the hunks of the current file (d
) among others.
I did those patches multiple times and, for some everything went well, for others I had some problems with the beautiful (and frustrating, complex, obscure) message that is: patch does not apply
.
To make it simple, I had one hunk that I couldn't split more. That hunk was composed of two added lines, and I just wanted to keep one but not another.
After various tries I gave up, because I was not sure if this was related to a bug or not. This StackOverflow post describes the problem I had.
So instead of headbutting my computer and combo-ing it into a kamehameha, I just made it so my commit was a bit bigger than I wanted it to be.
Nice, two more to go
Level three
You pushed your commits 1 to 5, but your reviewer asks to:
- Change something in commit 2
- Reorder commits 3-4
- Meld commit 5 into 4
- Change the commit message of commit 1
- Rebase your branch to master
Another command that I liked a lot was used here: git rebase -i/--interactive
This command lets you rework your commits from a specific commit, opening a text editor to then specify what you want to do for each commit that follows up until the HEAD that they were originally based on.
For each commit, a line is present with the following structure:
[ACTION] [HASH] [COMMIT MESSAGE]
Something that I found really nice and well done with Git, is that each interactive tool comes with a clear interface, a list of commands, and explanations or usages.
So, for each step of that question, I just checked the command that seemed to answer it and used it really simply.
Change something in commit 2
For the second commit, change action pick
to edit
Reorder commits 3-4
Just cut the fourth commit and paste it before the third
Meld commit 5 into 4
For the fifth commit, change action pick
to squash
Change the commit message of commit 1
Simply change the commit message of the first commit
I'm gonna be honest: Since I was unaware of how the interactive rebase worked, I did the first step separately and then it came to my mind that I could to all others in a single step. But hey, we're here to learn something (and we did) !
If you're making multiple changes, you can then use git rebase --continue
to let Git continue with the rebase until another action is required. You can also stash
and commit --amend
while rebasing.
Rebase your branch to master
Since we've played a lot with the rebase command, this was straightforward, a simple git rebase master my_branch
did the trick.
Just keep in mind that if you have conflicts, you can:
- Edit your files manually and correct conflicting parts OR
- Use
git mergetool
(that I was not able to use but am very curious on how it shows up and works - let me know !)
Level four
You made a mistake during the level three operation. You need to "recover" and start again, but you already pushed your changes, so a force pull from the origin won't work.
Funnily enough, I did a real mistake by doing an incorrect push - I had conflicts and pushed those conflicts without resolving them, yay...
Well, that's gonna be a good exercise then. For that, I played with the git reflog
command that acts as a local history indexed by hashes that you can reset to.
This tool made me gain confidence about Git, a lot. In fact everything you do (as long as you don't burn your computer) can be reverted somehow because Git keeps a local history where you can easily go back if you lost something or made a mistake - really clever !
reflog stands for Reference logs and shows every action you've done until now - it can be commits, rebase, resets - everything.
Here is a little example:
f1ec82e (origin/main, origin/HEAD, main) HEAD@{9}: reset: moving to HEAD~1
e20e949 HEAD@{10}: checkout: moving from derlin_commit_exercice to main
92556e7 HEAD@{11}: rebase: checkout derlin_commit_exercice
92556e7 HEAD@{12}: rebase: checkout derlin_commit_exercice
92556e7 HEAD@{13}: rebase (continue) (finish): returning to refs/heads/derlin_commit_exercice
92556e7 HEAD@{14}: rebase (continue) (pick): Derlin challenge - Fifth commit
bba7667 HEAD@{15}: rebase (continue) (pick): Derlin challenge - Fourth commit [changed something in commit message]
422016e HEAD@{16}: rebase (continue): Derlin challenge - Third commit [squashed second and third commit using interactive rebase]
e20e949 HEAD@{17}: rebase (start): checkout main
What is really cool with reflog
is that you can easily see where you screwed up, note the hash (first column) and then git reset [hash]
to go back at that point.
Note that this doesn't clear the reflog history: it adds a new entry to it indicating that you made a reset to that hash !
Finally, the answer was to use git reflog
, get the hash of the commit before e20e949
and restart the rebase, then git push origin master --force
to overwrite changes pushed before.
WE DID IT !
... or did we ?
I'm a completionist, so I couldn't let that final bonus boss slip by.
Bonus
A bug was introduced into the codebase, but you have no idea when. How do you use git to find the commit that started it all?
I had to do a little research about this one, because I had no idea Git could help me with that.
At first, I thought we were talking about something external to Git, but no - there really is a tool that can help you for that - git bisect start
.
That command does a binary search on commits, where we simply need to note a commit as "good" or "bad" in case the commit is related to the aforementioned bug.[TODO develop how binary search is used here by explaining it goes up to the head]
Unfortunately, since my project is small, the command wouldn't generate much content for me to check - BUT it is a really original (and clever) way to find where a problem has occurred and what is its root !
Conclusion
Yes @derlin , I really needed to "GIT GUD".
After using those commands, I took a step back on how I used to do things that would have been SO MUCH easier using the command line. An example ? Here:
- I'm working on a project where I have to do some frontend (React) and some backend (Python)
- Sometimes, by developing a new feature on the frontend, I suddenly see that there is something not working correctly from the back so I change it
- Problem: My fixes should go in another branch. Before, I would stash my changes, switch branches, unstash the fix, commit and push, go back to my feature branch, yada-yada
- Solution: GIT GUD and use the
git add -i
!
It's funny because now I find that some plugins or IDEs lack of some elements provided by the command line, and I surprise myself saying that to be honest. Now I have a totally different perception on how Git works and how frightening it looked like, and want to use that knowledge to work more efficiently.
Thank you @derlin for that challenge, and to push the thinking on what we're using in our day-to-day and how !
And if you haven't, go check @derlin 's profile, and try to complete that challenge ! Also, if you're aware of other challenges of that kind, let us know !
Cheers, and happy coding.
May the GIT be in you
Top comments (0)