This document describes code rollback and retrieval in Git repositories.
Git is a flexible version control tool that helps teams enhance team collaboration and track different versions of codes. In real-world scenarios, some developers may intentionally or unintentionally delete some Git functions, causing problems to the team or even leading to the loss of important codes. Improper code rollback is one of the main problems.
This document explains how to perform code rollback in various scenarios to recover content mistakenly deleted.
Let's start with a typical real-world case that demonstrates the problems caused by improper code rollback.
(1) John, Jane, and Sam worked on the same branch.
(2) John used
reset to roll back some content and found that the push failed. Finally, he completed the operation using
push -f prompted that the target is a protected branch (such as
master), so the push failed. Therefore, John unlocked the branch and then run
(3) Jane and Sam performed a normal git pull, but encountered many conflicts and the commit history was messed up.
(4) After a while, they needed to check the source code of a release, but couldn't find the exact code. That's because the code was deleted by the
reset performed by John.
Before analyzing the common code rollback scenarios, let's learn about the four Git working areas.
After you clone a repository, you will see a local directory containing all the project files. We can divide the content into four working areas:
A workspace is also known as a working directory or working replica. It is the directory containing the project files after you clone a repository. This is the working area where you perform routine development operations.
Local repository (.git)
The workspace has a hidden
.git directory. This is the database of the local Git repository. The directory files in the workspace are checked out from here. After you modify files and commit your changes, they are recorded in the local repository.
*Tips: Do not manually modify content in the .git directory.**
A staging area is also called cache, an area in between the workspace and local repository. It is mainly used to mark modified content. By default, the content in the staging area is recorded to the local repository in the next commit.
For team collaboration, you need to specify a remote repository (or multiple repositories in certain cases). Team members interact with the remote repository during collaboration.
A basic Git workflow is as follows:
staging areato the
local repositoryto the
When you have modified files in the workspace but the changes have not been committed to the staging area or local repository, you can use
git checkout -- file name to roll back the changes.
Note: These changes will not be committed to the Git repository and will not appear in the Git history. After the rollback, these changes are discarded permanently.
When you use
git status, changes not committed are displayed in "Changes not staged for commit:".
Run the following command to roll back changes in the workspace:
git checkout -- build.sh
If you run
git add to add changes to the staging area, but have not commit them, you can use
git reset HEAD fill name to roll back the changes. When you use
git status, the following prompt is displayed:
Run the following command to rollback changes in the staging area:
git reset HEAD build.sh
After rollback, the changes will be retained in the workspace, so you can edit and commit again, or use
git checkout -- file name to permanently discard the changes.
When you have committed changes to the local repository but have not pushed them to the remote repository, you can use the
git reset command in the format:
git reset <commit to="" roll="" back=""> or
git reset --hard <commit to="" roll="" back="">
You must note that any commits after the commit to roll back to will be discarded.
By default, the changes discarded by
git reset are retained in the workspace so you can edit and commit again. If you add
--hard to the command, the changes are not retained. Proceed with caution.
If additional changes are required after committing, you can use "git reset" to add them in the commit,. However, Git also provides a simple method for updating the last commit.
The command format is as follows:
git commit --amend [ -m <commit description=""> ]
-m <commit description=""> is not included in the command, Git pulls up the editor to enter the log description. Example:
The "git commit --amend" can only be used to modify local commits that have not been pushed.
**Note: Use "git revert" instead of "git reset".
We want to emphasize this point because "git reset" will delete your history. This can cause various problems if you have already pushed records. But "git revert" only rolls back a commit and makes a new one without erasing history.
If you encounter a problem in this process (such as commit history was messed up when you handle a conflict), use "git revert --abort" to cancel this rollback operation.
If you want to roll back a merge commit, add "-m
" when performing revert to specify the parent node record to use as the main thread after rollback. A merge commit generally has two parent nodes numbered 1 and 2 in order. To roll back a commit that merges a branch into the master, you can use "-m 1" to take the master record as the main thread.
Rolling back a merge commit is a complicated operation. In general, we recommend you avoid this operation. For more information, see https://github.com/git/git/blob/master/Documentation/howto/revert-a-faulty-merge.txt
This section will give an example to show the difference between
git reset and
The initial status of the branch is as follows:
git reset B,
B, discarding all subsequent commits (C and D).
If you generate a new commit (
C1 will have no relationship with C and D.
git revert B,
E) is generated to roll back to
B. This does not modify the existing commit history.
Although Git is a powerful version control tool and you generally do not have to worry about code committed to repositories, you still may have to recover content in certain situations, such as after an improper reset operation or accidental branch deletion. In such case, you can use
The "git reflog" is a powerful tool used to recover local history. It can restore almost any local record, such as a commit discarded by reset or a branch deleted.
However, "git reflog" cannot recover all records. It does not work for the following content:
A typical use case for this command is when you use reset to roll back and then discover a rollback error. In this case, you can revert to another commit status.
git reflog to view commit operation history. Find the target commit and then perform reset to revert to this commit.
This example shows that a clear and meaningful commit log is very helpful. For example, if the commit log only has vague descriptions, such as "update" or "fix", it is difficult to find the target commit even with the "git reflog" tool.
Scenario: After reset rollback, you find you have discarded some necessary files.
Solution: Use reflog to find the target commit and then run the following command to restore a specific file from the commit.
git checkout <target commit=""> -- <file>
After you run reset to roll back to commit 468213d, you find the build.sh file in the latest status (commit d57f339) is still needed. Therefore, you want to restore this file to the workspace.
Scenario: After you use "git branch -D" to delete a local branch, you discover that the branch was mistakenly deleted because this branch was not merged.
Solution: Use reflog to find the branch deleted by the current commit and rebuild the branch based on a target commit.
git branch <branch name=""> <target commit="">
In the Reflog record, the commit record between "to
A Git's best practice is to delete branches after merging to keep the code repository clean and only maintain active branches.
Some developers may wish to retain branches after merging in case they may still be useful. But content merged into the master will not be deleted and can be recovered at any time by rebuilding the branch from a specific commit. Moreover, it is rarely necessary to use old development branches. Generally, even in the case of a function bug, a new branch can be created to fix the problem and verify the solution.
However, if you want to rebuild a merged branch, you can find the branch merge record through the master history, find the branch node, and create a new branch based on this commit, for example:
git branch dev/feature-abc 1f85427
The following are some suggestions for specific commands:
In addition, you should be careful when performing rollback. Do not rely too much on rollback and avoid using "git push -f". In the words of a wise man, If you use "git push -f", you must be doing something wrong!