Problems
Reverting anything that has been done
Imagine you’ve made several commits, but then you run git reset --hard HEAD~2 and lose the last two commits. You can restore them as follows:
- Check reflog log
git reflog
- The output might show:
c4f2a27 (HEAD -> main) HEAD@{0}: reset: moving to HEAD~2
a3d5b8e HEAD@{1}: commit: Menambahkan file README
- You want to restore commit a3d5b8e:
git reset --hard a3d5b8e
To restore, you can use
git reset HEAD@{index}
or
git reset --hard HEAD@{index}
What’s the difference?
git reset --hard HEAD@{1}
If you want to completely restore the repository to the exact state of commit HEAD@{1} and discard all unstaged or uncommitted changes:
git reset --hard HEAD@{1}
git reset HEAD@{1}
If you want to restore the staging area to commit HEAD@{1} but keep the changes in the working directory (for example, to check or temporarily save existing changes):
git reset HEAD@{1}
Conclusion
git reset --hard: Completely restores the repository to the specified commit, including the staging area and working directory.
git reset: Restores the specified commit only for the staging area, doesn’t change the working directory.
Fixing the last commit
git add . # Or add individual files
git commit --amend --no-edit
--amendmeans to modify.--no-editmeans without edit
Modifying without edit? But we just edited the previous commit content?
amend is for the commit message, not its content. Still don’t understand?
Fixing the last commit message
git commit --amend
Here without using --no-edit, so it only changes the commit message. Clear now?
Accidentally committed on the master branch, should have been on a new branch
Ever forgot to checkout to a branch but directly coded on the master branch (or another branch)? 🤣
Don’t worry, it’s very easy.
git reset HEAD~ –hard
- On the master branch, create a new branch first
git branch new-branch-name - Reset master to the previous commit
git reset HEAD~ --hard - Checkout to the correct branch
git branch new-branch-name
git rebase -i HEAD~n
The previous method applies if there’s only 1 commit to reset. But if there are already many commits on master, what then?
The different step is only in step 2
- Drop the commits you want to delete
git rebase -i HEAD~n # n is the estimated number of commits you want to delete - In the editor, find which commit you want to delete. Change
picktodrop. Then save.
git cherry-pick commit-hash
There’s a simpler method that I use more often, cherry-pick
git cherry-pick commit-hash
After doing cherry pick, you can checkout back to the wrong branch and do git reset HEAD~ --hard or git rebase -i HEAD~n
Note
If the commits on master have already been pushed, what then?
Must force push 😢. Therefore, the master/main branch must be guarded so it can’t be committed directly, only can be merged from other branches.
For the force push method, there will be a separate explanation.
Reverting past commits
git revert [saved hash]
The difference between revert and rebase -> drop or reset --hard, revert creates a new commit. So later you can revert the commit that reverts the commit 🤣
If in the middle of the rebase process you want to cancel, use git rebase --abort
Saving state
git stash save "stash name"
Similar to when playing a game and saving the save, when coding we can save the current state and load it later
- stash stateIf without a custom name
git stash save "custom name" # custom stash name is optionalsh git stash save - List stash
git stash list - Load stateIf without
git stash apply stash@{n} # n: stash position obtained from stash liststash@{n}Then it will load the last stash. This command is the same asgit stash applygit stash apply stash@{0}
Restoring file to previous state
git checkout [hash] -- path/to/file
Helps if you want to restore the file condition according to a specific commit
Force Push
This explanation will be longer because it’s a dangerous action!
Force push (git push --force) is an action to forcefully send changes from the local repository to the remote repository, replacing the existing commit history on the remote. This is useful when you need to update the commit history on the remote after making changes like rebase or amend.
Why Force Push is Dangerous?
Force push can be dangerous because:
- Deletes commit history on the remote that might be important.
- Makes your team members’ commits disappear if they’re also working on the same branch.
- Causes conflicts if others have merged the commits you changed.
--force
Suppose you’ve changed the commit history on the main branch by doing a rebase or editing commits, and you want to forcefully update the remote repository.
- Make some changes and commit:
git commit --amend -m "Fixing commit message" - Push changes with force:
git push --force origin main
By using --force, you replace the existing commit history on the remote with the new commit history from your local repository.
--force-with-lease
--force-with-lease is a safer option than --force because it performs additional checks before pushing. This option ensures that you’ll only force push if no one else has pushed to the remote since you last pulled (pull) changes.
Advantages of Using --force-with-lease
- Prevents you from accidentally overwriting others’ commits.
- Gives a warning if there are changes on the remote that you haven’t pulled (pull) yet.
Example of Using --force-with-lease
Suppose you make changes on the main branch and want to ensure no one has pushed to the remote since you last pulled changes.
- Make some changes and commit:
git commit --amend -m "Fixing commit message" - Push changes with
--force-with-lease:git push --force-with-lease origin main
If no one has pushed to the main branch on the remote, your changes will be accepted. However, if there are changes on the remote that you haven’t pulled, Git will give a warning and stop the push, preventing potential conflicts.
Conclusion
By using --force-with-lease, you get additional protection compared to --force, making it safer to use in collaborative environments. Always be careful when using force push to avoid losing important commit history.
References
Tools
- neovim editor
- Terminal recorded with asciinema
- Player using asciinema-player
- Git interface tig
