Git Revert and Reset Review and Comparison
- if it is followed by a file or nothing, it just remove the specified file or all files from staging area, this does not affect the history at all
- if it is followed by a commit (e.g., HEAD~1, sha1), it moves the current branch tip backward to that commit, all commits after that will be removed permanently, the changes could still be in the working directory depending on the –hard option, see below, but the history is altered already
- –hard determines whether the working directory will be overidden or not
Say we have a repo and it has commit 1, commit 2, commit 3, these three commits in history of the master branch, now we are on master:
git reset --hard HEAD~1: remove the HEAD commit (commit 3) permanently, and get back to HEAD~1 (commit 2), now the repo only have commit 1 and commit 2 in history (see git log), and the working directory is clean.
git reset HEAD~1: remove the HEAD commit (commit 3) permanently, and get back to HEAD~1 (commit 2), now the repo only have commit 1 and commit 2 in history (see git log), and the working directory is not clean but contains the changes of commit 3, you can either choose the recommit or git checkout to clean it up.
git reset HEAD~2: remove the commit 2 and commit 3 permanently (history changes, see git log), and get back to commit 1, now the repo only have commit 1 in history (see git log), and the working directory is not clean but contains the changes of commit 2 and 3, you can either choose the recommit or git checkout to clean it up, for example, if the repo has readme.txt, commit 2 just change readme.txt by adding abc at the end of file, commit 3 just add another line ABC at the end of readme file, the the working directly will contains the readme.txt with abc and ABC at the end of file, that is, it keeps the changes from both commit 2 and 3.
git revert <commit>: figures out how to undo the changes introduced by the commit (could be an arbitrary point in history) and appends a
new commit with the resulting content, for example, if there is a history of 4 commits in a git repository, after executing
git revert HEAD, there will be a 5th commit and the project would have exactly the same code/status as in 3rd commit, because the command just undo the HEAD commit, that is the 4th commit.
git reset completely removes a changeset, whereas
git revert maintains the original changeset and uses a new commit to apply the undo. Make sure that we use
git reset <commit> on a local experiment that went wrong—not on published changes. If we need to fix a public commit, the
git revert command was designed specifically for this purpose.
The above is a short comparison and quick review, for more deteails, please read the following content
git revert command undoes a committed snapshot. But, instead of removing the commit from the project history, it figures out how to undo the changes introduced by the commit and appends a
new commit with the resulting content. This prevents Git from losing history, which is important for the integrity of your revision history and for reliable collaboration.
git revert <commit>
Generate a new commit that undoes all of the changes introduced in
<commit>, then apply it to the current branch.
Reverting should be used when you want to remove an entire commit from your project history. This can be useful, for example, if you’re tracking down a bug and find that it was introduced by a single commit. Instead of manually going in, fixing it, and committing a new snapshot, you can use
git revert to automatically do all of this for you.
Reverting vs. Resetting
It’s important to understand that
git revert undoes a single commit—it does not “revert” back to the previous state of a project by removing all subsequent commits. In Git, this is actually called a
reset, not a
Reverting has two important advantages over resetting. First, it doesn’t change the project history, which makes it a “safe” operation for commits that have already been published to a shared repository. For details about why altering shared history is dangerous, please see the
git reset page.
git revert is able to target an individual commit at an arbitrary point in the history, whereas
git reset can only work backwards from the current commit. For example, if you wanted to undo an old commit with
git reset, you would have to remove all of the commits that occurred after the target commit, remove it, then re-commit all of the subsequent commits. Needless to say, this is not an elegant undo solution.
The following example is a simple demonstration of
git revert. It commits a snapshot, then immediately undoes it with a revert.
# Edit some tracked files # Commit a snapshot git commit -m "Make some changes that will be undone" # Revert the commit we just created git revert HEAD
This can be visualized as the following:
Note that the 4th commit is still in the project history after the revert. Instead of deleting it,
git revert added a new commit to undo its changes. As a result, the 3rd and 5th commits represent the exact same code base, and the 4th commit is still in our history just in case we want to go back to it down the road.
git revert is a “safe” way to undo changes, you can think of
git reset as the dangerous method. When you undo with `git reset1(and the commits are no longer referenced by any ref or the reflog), there is no way to retrieve the original copy—it is a permanent undo. Care must be taken when using this tool, as it’s one of the only Git commands that has the potential to lose your work.
git reset is a versatile command with many configurations. It can be used to remove committed snapshots, although it’s more often used to undo changes in the staging area and the working directory. In either case, it should only be used to undo local changes—you should never reset snapshots that have been shared with other developers.
git reset <file>
Remove the specified file from the staging area, but leave the working directory unchanged. This unstages a file without overwriting any changes.
Reset the staging area to match the most recent commit, but leave the working directory unchanged. This unstages all files without overwriting any changes, giving you the opportunity to re-build the staged snapshot from scratch.
git reset --hard
Reset the staging area and the working directory to match the most recent commit. In addition to unstaging changes, the –hard flag tells Git to overwrite all changes in the working directory, too. Put another way: this obliterates all uncommitted changes, so make sure you really want to throw away your local developments before using it.
git reset <commit>
Move the current branch tip backward to
git reset --hard <commit>
Move the current branch tip backward to
All of the above invocations are used to remove changes from a repository. Without the –hard flag, git reset is a way to clean up a repository by unstaging changes or uncommitting a series of snapshots and re-building them from scratch. The –hard flag comes in handy when an experiment has gone horribly wrong and you need a clean slate to work with.
Whereas reverting is designed to safely undo a public commit, git reset is designed to undo local changes. Because of their distinct goals, the two commands are implemented differently: resetting completely removes a changeset, whereas reverting maintains the original changeset and uses a new commit to apply the undo.
Don’t Reset Public History
You should never use
git reset <commit> when any snapshots after
Removing a commit that other team members have continued developing poses serious problems for collaboration. When they try to sync up with your repository, it will look like a chunk of the project history abruptly disappeared. The sequence below demonstrates what happens when you try to reset a public commit. The origin/master branch is the central repository’s version of your local master branch.
As soon as you add new commits after the reset, Git will think that your local history has diverged from origin/master, and the merge commit required to synchronize your repositories is likely to confuse and frustrate your team.
The point is, make sure that you’re using git reset
Unstaging a File
git reset command is frequently encountered while preparing the staged snapshot. The next example assumes you have two files called hello.py and main.py that you’ve already added to the repository.
# Edit both hello.py and main.py # Stage everything in the current directory git add . # Realize that the changes in hello.py and main.py # should be committed in different snapshots # Unstage main.py git reset main.py # Commit only hello.py git commit -m "Make some changes to hello.py" # Commit main.py in a separate snapshot git add main.py git commit -m "Edit main.py"
As you can see, git reset helps you keep your commits highly-focused by letting you unstage changes that aren’t related to the next commit.
Removing Local Commits
The next example shows a more advanced use case. It demonstrates what happens when you’ve been working on a new experiment for a while, but decide to completely throw it away after committing a few snapshots.
# Create a new file called `foo.py` and add some code to it # Commit it to the project history git add foo.py git commit -m "Start developing a crazy feature" # Edit `foo.py` again and change some other tracked files, too # Commit another snapshot git commit -a -m "Continue my crazy feature" # Decide to scrap the feature and remove the associated commits git reset --hard HEAD~2
git reset HEAD~2 command moves the current branch backward by two commits, effectively removing the two snapshots we just created from the project history. Remember that this kind of reset should only be used on unpublished commits. Never perform the above operation if you’ve already pushed your commits to a shared repository.
The detailed explaination is a repost of https://www.atlassian.com/git/tutorials/undoing-changes/git-revert