Versioning your code, your database structure, or your infrastructure as code, is essential. But versioning comes with a potential problem: fixing mistakes can be tricky. Here I propose solutions for the most common problems.
Uncommitted changes
Changes were made to the files, but no commit was done yet.
Undo uncommitted changes
You made changes to the files that you want to undo. You didn’t make any commit yet.
Solution: go back to the last commit.
First, check uncommitted changes in this way:
git diff
If it’s ok to just lose all the changes, run this:
git reset --hard
Move uncommitted changes to another branch
You made changes to the wrong branch, but you didn’t commit yet.
In modern git versions, normally all you need to do is to select another branch and commit. But if git won’t allow you to select another branch, follow these steps.
Solution: stash changes, select another branch, and apply changes.
If you want to stash and re-apply all changes:
git stash
git checkout <another-branch> # add -b if the branch doesn't exist yet
git apply
If you want to only stash and re-apply some changes:
git stash -p
git checkout <another-branch>
git apply
-p
means partial. You will have to run through the changes, and choose which of them you want to keep.
Undo last commit
You made some changes, and you made a commit. But you realised that the changes are wrong and should be undone.
Solution: undo a commit by going back to the previous commit.
You can undo any number of most recent commits. Suppose that you want to only undo the last one: you go back to the commit number 2, where the count starts from the end.
git reset --soft HEAD~2
If you already pushed the commit that you just undid:
git push --force
Partially undo last commit
Your last commit is useful and most of the changes should be preserved, but you realised that you made a mistake. For example, you added a password to the repository, or you mistyped something in a comment.
Solution: undo the commit but not the changes; edit files; commit again.
git reset --soft HEAD~2
# now edit files
git commit -a
If the commit that you modified was already pushed, now run:
git push --force
Add or remove files in the last commit
You made a commit, but forgot to add a file. Or maybe you added a whole directory, and forgot that one of the files should not have been included in the repository.
Solution: add/remove files, and amend the last commit.
To add or remove files:
# add
git add <filename>
# remove
git reset <filename>
To amend the commit:
git commit --amend --no-edit
git push --force
Change last commit message
You made a typo in the commit message.
Solution: amend the commit and specify a new commit message.
git commit --amend -m 'New message'
git push --force
This can happen when you work on a new system. Maybe you forgot to setup git name and email, and you already made a commit. Similar to the previous problem.
Solution: amend the commit with correct user name and email.
git commit --amend --author='Your Name <yo**@em***.net>'
Older commits
The problem is with one or more commits, but not the last one.
Remove or revert an old commit
Things get slightly more complicated when it comes to removing old commits. The commands to use are easy, but you have to consider the consequences. So in this case, ask yourself: do I really need to delete a commit from history? Typically, the answer is no. When a commit contains sensitive information, maybe you’ll want to delete it.
Revert
Reverting a commit will not delete it from history. The commit will remain where it is, but a new commit will be created to undo it.
Find the problematic commit in this way:
git log
Now copy the hash from that commit (or the first 8 characters from the hash) and run:
git revert <hash>
Remove
Find the problematic commit in this way:
git log
Suppose we want to delete the commit number 5. We’re deleting only one commit, so it must be replaced by the commit number 4.
git rebase --onto HEAD~5 HEAD~4
We could also delete multiple consecutive commits. For example, to remove 3 commits we can specify HEAD~5
and HEAD~2
.
Too many commits
You made too many commits and you don’t want to show all of them to the public. Perhaps because some of the commits were made to fix errors made in the previous one.
Solution: squash the most recent commits.
For example, to squash two commits:
git reset --soft HEAD~2 && git commit
If you want to squash all the commits in a branch:
git merge --squash <branch_name>
Now check that you obtain the result you wanted in this way:
git log
If the commits that you squashed were already pushed, now run:
git push --force
Branches
Problems with a whole branch, or involving multiple branches.
Renaming a local or remote branch
You created a branch and made some commits, maybe you even pushed the changes. But then you realise that the branch name doesn’t follow the project standards. Don’t worry: you won’t have to redo your work!
Solution: select the branch and rename it.
Suppose that the branch is called old
, and you want to change its name to new
.
git checkout old
git branch -m new
git push origin -u new
If the branch was pushed before the renaming, a remote branch exists with the old name. Delete it in this way:
git checkout <another-branch>
git branch -D <branch-to-delete>
Delete a remote branch
Maybe you made some bad commits and pushed them. For example, commits that contain passwords.
Solution: delete a branch, both locally and remotely.
git checkout <another-branch>
git branch -D <branch-to-delete>
The first command is only necessary if you are in the branch that you want to delete. If you try to delete the current branch, you will get an error.
Move last commits to another branch
You made a grate change! You committed! You pushed, and pinged everyone to ask for a (surely positive) feedback about your great idea! But the only comment is: “why the heck did you commit on main
, ya wa**er!?”. Woops, you need to move that commit to another branch.
Solution: create a new branch from the current one, and remove recent commits from the current branch only.
git checkout -b new
git checkout main
git reset --hard HEAD~2
The new
branch will have the recent commit, even if you deleted them from main
.
Use HEAD~2
to move one commit, HEAD~3
to move two, etc.
How dirty are these solutions?
Some of the solutions I suggested include bad practices, because sometimes they are necessary. But I don’t want to encourage you to abuse these practices, so let me spend some words on this topic.
Is it ok to push –force?
When you run push --force
, you push your local unpushed commits to the remote repository, no matter what. If there some remote changes are more recent than the one you’re pushing, it will be lost. It’s like going back in time and changing an event in the Back To The Future saga.
Unfortunately, some of the solutions presented here include a push --force
because they have to. But be aware of the consequences, and talk to the authors of any commit you may delete in this way.
Is it ok to rewrite history?
Some of the solution we’ve shown rewrite history. Please make sure to understand what they do, before pushing to a remote repository. This article shows useful snippets, but it’s not a manual. To find more information about a command before running it, you’ll have to check git documentation.
So, is it ok to rewrite history? No, it isn’t. Because other people may have pulled your repository, and when they work with it, they implicitly rely on history. Also, if you deliberately change history parts, you defy the purpose of having a history track.
But we showed those commands, so there’s a but. Running into a problem is perfectly fine when it’s the only reasonable solution to address a worse problem. For example, accidentally committing and pushing sensitive information into a public repository is a major problem that should be fixed. Other contributors will have to waste some time to work around some issue with the history, but it is what it is.
Conclusions
Did you note any error? Do you want to propose a better solution for a problem? Do you have solutions for new problems, not mentioned here? Please comment!
We’ll be able to maintain this article and improve it over time, and mention the people who help us.
Federico Razzoli
0 Comments