Sera Geyran
Posted on November 6, 2020
Git is the most powerful version control system, saving software developers from many headaches for years now. I remember the joy and excitement I felt when I got introduced to it and realized I wouldn't have to name my files with suffixes like “file_revision5.txt” or “file_latest.txt” ever again!
Below, there are a few scenarios that a developer friend or I came across over the years. If you're new to Git or think there is a cleaner solution for your problem and found this article, I hope you can find a scenario that resembles your situation and it helps you out!
When you commit in the wrong branch
“I was peacefully developing a feature in a separate branch until someone came and reported of a critical bug. I fixed the bug and committed the changes but forgot to checkout to a new branch first. Now, the feature development -that is still in progress- and the bug fix -that is now done and has to go live- are in the same branch. Do I checkout to a new branch from master and copy-paste the changes related to the bug fix?”
Checking out to a new branch from master bit is correct, but the copy and paste part is not. The correct command you're looking for is cherry-pick.
$ git cherry-pick <commit hash>
After the changes related to bug fix are moved to the correct branch, you may want to delete those commits irrelevant to your other work from your feature branch to keep a clean history. This need leads us to the next scenario…
When you need to delete a commit
“I want to delete one or two commits in order to keep a clean commit history in my branch.”
You'll need to do an interactive rebase and delete the line of the commit you want to remove from your commit history. What's more, you can also change the order of your commits or change the commit message.
$ git rebase -i master
A HUGE warning about rebase: I wouldn't recommend rebasing if it's the master branch or a branch where many people work together. The rebase operation moves the head and it causes conflicts. You may end up getting many evil looks at your direction.
When you accidentally delete a commit
“I lost a commit during rebase. Is it gone forever???”
Reflog to the rescue! One great feature about Git is that, it keeps every change -even the ones you've deleted- for 90 days.
$ git reflog
When you find the commit with the changes you lost, copy its hash and use cherry-pick to add the changes your branch again.
When life is too short to commit often
“I finished the whole job without committing even once! Now, I have many changes on the same document that could have been under different commits.”
I've been there my dear lazy developer friend, I have been at the exact same spot as you are, but fear not! There definitely is a simple solution and it's called the patch option.
$ git add -p file.txt
The “--patch” option will make Git show you each change and ask you what to do with them. You can even split a block of changes, stage a part of it and leave the rest as unstaged for a different commit.
You can view descriptions of your options when you type “?” and hit enter.
(1/3) Stage this hunk [y,n,q,a,d,K,j,J,g,/,s,e,?]? ?
y - stage this hunk
n - do not stage this hunk
q - quit; do not stage this hunk or any of the remaining ones
a - stage this hunk and all later hunks in the file
d - do not stage this hunk or any of the later hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print help
y - stage this hunk
n - do not stage this hunk
q - quit; do not stage this hunk or any of the remaining ones
a - stage this hunk and all later hunks in the file
d - do not stage this hunk or any of the later hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print help
When you need to find out when a specific change happened
“Someone accidentally merged a typo in a commit, and I really have to find that very commit. Unfortunately, the commit history is too long. Should I tell everyone to stop what they are doing and scan the whole history?”
While that solution may help you to see the loyalty and patience of your team, manual work in such situation is not necessary at all! All you need is the bisect command.
Bisect still requires some manual checks but dramatically shortens the search time for the faulty commit - O(log n).
Check your commits and find a commit in which you know your code works as it's supposed to and another one in which you know something's gone wrong and copy their hash. You're going to use those hashes to set a range of commits to check.
$ git bisect start
$ git bisect good <good commit's hash>
$ git bisect bad <bad commit's hash>
When you start bisecting, Git will checkout to a commit within the range you set and wait for you to check your code and mark that commit with either git bisect good
or git bisect bad
.
If you want to save the operation log, run git bisect log > bisect.log
command before ending the bisect session. You can, of course, use a different name then bisect.log for your file name. :)
When you are done, just run git bisect reset
to end the bisect session and return to HEAD.
When you forget to add an untracked file to a commit
“I committed my changes and realized I forgot to add an untracked file. I don't want to create another commit with the same message. What do I do?”
I agree, changes about a single job should be grouped in the same commit. You can stage changes and/or add untracked files and add them to the previous commit with the help of options “--amend” and “--no-edit”.
$ git add missing-file.txt
$ git commit --amend --no-edit
Be careful though, the “--amend” option also changes the signature of the commit.
When you want commit additional changes to a previous commit
“The changes in my code can go under one commit, but they are very related to one of the previous commits. How do I add the changes to that previous commit?”
If it's the previous commit, you can use git commit --amend --no-edit
.
If it's a previous commit, start with finding the hash of the commit you want to use with git log
. Once you do, commit your changes using the --fixup option and it will save you some time and trouble later when you're rebasing -yes, you have to rebase in order to merge those 2 commits-
$ git commit --fixup <previous commit's hash>
When you're all done with your branch, run rebase with --autosquash option.
When you are not ready to commit yet, but also have to change the branch
“I was working in my branch when a friend asked me if we could pair up to finish an urgent work in her branch. The changes I have made are not yet ready to be committed. Do I copy all of my changes to another file to backup until the urgent work is finished then paste them back to the correct files in my branch?”
No, you stash them. Don't forget to use the --include-untracked option if you have untracked files. Otherwise, they'll just dangle even if you checkout to another branch.
$ git stash save --include-untracked "stash message"
I highly recommend you to write a meaningful and short description after the “save” command. It makes life a lot easier if you have stashed changes more than one occasion. You can view your stash list with the following command:
$ git stash list
When it's time to apply the changes in the stash, there are two commands you can use: apply or pop. While both of them applies the changes in your current branch, pop also removes the applied stash item from your list.
$ git stash apply stash@{1}
Bonus/The Combo: When you have an untracked file with changes that should be split and go under different commits
“I have an untracked file that has changes that could go under different commits. If I add the file, all of the changes will be indexed. Is there a way out or do I copy and paste the changes as a penance for my not-committing-often sin?”
What if I told you that you can index your files without its content? The option that will save you from the copy/paste torment is intent-to-add which should go with the “git add” command.
After adding the file, you can add the changes you want to commit with the “patch” option explained above in “When life is too short to commit often” scenario.
Now, there will be some manual work here. You'll have to select the e -manually edit the current hunk
option in the interactive menu and delete the lines that should not go in the current commit. Don't worry, the changes will still be there; they won't be deleted, just won't get staged.
$ git add -N -p file.txt
Cover photo by Yancy Min
Posted on November 6, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.