撤消更改
本教程提供了操作软件项目版本管理所需的所有必要技能。首先,这篇文章会告诉我们如何查看旧的提交,然后解释在项目历史记录中还原公共提交与重置本地计算机上未发布的更改之间的区别。
git checkout
git checkout命令提供三个不同的功能:检出文件,检出提交和检出分支。在这篇文章中,我们只关心前两个配置。
检出提交将使整个工作目录匹配该提交。这可以用来查看您的项目的旧状态,而无需以任何方式更改您当前的状态。检出一个文件可以看到该特定文件的旧版本,使其余的工作目录保持不变。
用法
git checkout master
检出主分支。分支将在下一篇中进行深入研究,但现在您可以将其视为恢复项目“当前”状态的一种方式。
git checkout <commit> <file>
Check out a previous version of a file. This turns the
检出一个文件的以前的版本。这将工作目录中
git checkout <commit>
更新工作目录中的所有文件以匹配指定的提交。您可以使用提交
讨论
任何版本控制系统背后的整个目的是“安全”存储项目的副本,以便您无需担心破坏您的代码库。一旦建立了项目历史记录,git checkout就可以将这些保存的快照“加载”到开发机器上。
检出旧的提交是一个只读操作。查看旧修订版时不可能损害您的存储库。您的在master
分支中项目的“当前”状态保持不变(有关详细信息,请参阅分支模块)。在正常的开发过程中,HEAD
通常指向主或其他本地分支,但是当您检出以前的提交时,HEAD可能不再指向分支,它会直接指向提交。这被称为“分离HEAD”状态,它可以被看成如下:
On the other hand, checking out an old file does affect the current state of your repository. You can re-commit the old version in a new snapshot as you would any other file. So, in effect, this usage of git checkout serves as a way to revert back to an old version of an individual file.
另一方面,检出一个旧文件确实会影响您的仓库的当前状态。您可以像任何其他文件一样在新快照中重新提交旧版本。所以,实际上,git checkout这种用法可以作为恢复单个文件到旧版的一种方式。
示例
查看旧版本
此示例假设您已经开始开发一个疯狂的实验,但您不确定是否要保留。为了帮助您决定,您需要在开始实验之前先了解项目的状态。首先,您需要找到要查看的修订版本的ID。
git log --oneline
假设您的项目历史记录如下所示:
b7119f2 Continue doing crazy things
872fa7e Try something crazy
a1e8fb5 Make some important changes to hello.py
435b61d Create hello.py
9773e52 Initial import
您可以使用以下git checkout方式查看“Make some important changes”的提交:
git checkout a1e8fb5
这使您的工作目录与a1e8fb5提交的确切状态相匹配。您可以查看文件,编译项目,运行测试,甚至编辑文件,而不用担心丢失项目的当前状态。您在这里做的任何事情都将保存在您的仓库中。要继续开发,您需要回到项目的“当前”状态:
git checkout master
这假设您正在开发默认的主分支,这将在《Git分支》中进行详细讨论。一旦你回到主分支,你可以使用任何一个git revert或者git reset撤消任何不需要的更改。
检出文件
如果您只对单个文件感兴趣,您也可以使用它git checkout来获取旧版本。例如,如果您只想hello.py从旧提交中查看文件,则可以使用以下命令:
git checkout a1e8fb5 hello.py
记住,与检出提交不同,这确实会影响项目的当前状态。旧文件修订版将显示为“要进行的更改(Change to be committed)”,让您有机会恢复到以前的文件版本。如果您决定不想保留旧版本,可以使用以下内容检出回最新版本:
git checkout HEAD hello.py
git还原 (git revert)
git revert命令撤销已提交的快照。但是,它不会从项目历史记录中删除提交,它会找出如何撤消引入的修改的提交,并提供追加一个新的提交来实现撤销修改的结果。这样可以防止Git丢失历史记录,这对于修订历史的完整性和可靠的协作非常重要。
用法
git revert <commit>
生成一个新的提交,撤消所有引入的更改
讨论
当您要从项目历史记录中删除整个提交时,应使用git revert。这可能是有用的,例如,如果您正在跟踪一个错误,并发现它是由某个提交引入的。可以不用手动修改,修复它,并提交新的快照,您可以使用git revert来自动为您搞定。
还原与重置
了解git revert是如何撤消单个提交的这点很重要 - 它不会通过删除所有后续提交来“还原”回项目的先前状态。在Git中,这种做法实际上被称为重置 reset ,而不是还原(revert)。
与重置相比,还原有两个重要的优点。首先,它不会更改项目历史记录,这将使它成为已经发布到公共仓库的提交的“安全”操作。有关为什么更改公共历史记录是危险的详细信息,请参阅git reset 。
第二,git revert能够在历史上的任意位置选择某个提交,而git reset只能从当前提交中选择更早的提交。例如,如果要使用git reset撤消一个旧的提交,则必须删除目标提交之后发生的所有提交,然后将这个删除,然后重新提交这个提交后的所有后续提交。不用说,这不是一个优雅的解决方案。
示例
以下示例是一个简单的示范git 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
这可以被视为如下所示:
请注意,第4个提交仍然在还原后的项目历史中。git revert不会删除这个提交,而是添加一个新的提交来撤消其更改。因此,第3和第5个提交代表完全相同的代码,第四个提交仍然在我们的历史,以防我们想要回滚到它。
git reset (git重置)
如果git revert是一个“安全”的方式撤消更改,你能想到的git reset就是一个危险的方法。当您使用git reset撤消(并且提交不再被任何引用或reflog引用)时,无法检索原始副本 - 它是永久撤消。使用此工具时必须小心,因为它是唯一可能失去工作的Git命令之一。
像git checkout,git reset是一个多用途的命令并许多配置项。它可以用于删除提交的快照,尽管它更常用于撤销暂存区域和工作目录中的更改。在任何一种情况下,它只能用于撤消本地更改 - 您不应该重置已与其他开发人员共享的快照。
用法
git reset <file>
从暂存区中删除指定的文件,但保持工作目录不变。这不会覆盖文件而不会覆盖任何更改。
git reset
重置暂存区以匹配最近的提交,但保持工作区不变。这不会覆盖任何更改,并让所有文件处于非暂存状态,从而让您有机会从头开始重新构建暂存快照。
git reset --hard
重置暂存区和工作目录以匹配最近的提交。包括没有暂存的更改,该–hard标志还会让Git覆盖工作目录中的所有更改。换句话说,这样会抹去所有未提交的变更,所以请确保在使用之前,确实希望废弃您的本地开发工作。
git reset <commit>
将当前分支提示向后移动
git reset --hard <commit>
将当前分支提示向后移动刀
讨论
所有上述方法都用于从仓库中删除更改。如果没有–hard标志,git reset是一种通过让修改和将一系列提交改为未提交的方式来清理仓库的方法。当一个实验发生可怕的错误,你需要一个完全放弃这些内容的时候使用–hard。
而还原被设计为安全地撤销公共提交,git reset旨在撤消本地更改。由于其不同的目标,两个命令的实现方式不同:重置完全删除了一系列修改,而还原维护原始的更改集,并使用一个新的提交来应用撤消。
不要在公共仓库使用git reset
绝对不要在公共仓库使用git reset。发布提交后,您必须假设其他开发人员依赖它。
删除其他团队成员持续开发的提交,对团队开发会带来严重问题。当他们尝试与您的仓库同步时,它将看起来像项目历史的一大块突然消失。下面演示了当您尝试重置公共提交时会发生什么。该origin/master
分支是您当地master分支的中央仓库版本。
一旦您在重置后添加新的提交,Git会认为您的本地历史已经脱离了origin/master,并且同步存储库所需的合并提交可能会混淆和挫败您的团队。关键是,确保您在本地实验错误的时候使用git reset
示例
取消暂存文件
在准备提交暂存快照时经常使用git reset命令。下一个示例假设您有两个文件hello.py和main.py已经添加到存储库。
# 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"
您可以看到,git reset通过让您将无关的更改放到下一次提交,来帮助您建立保持高度集中的提交。
删除本地提交
下一个示例显示了更高级的用例。它演示了当您在一段时间内进行新实验时会发生什么,但决定在提交几个快照后完全将其删除。
# 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命令将当前分支向后移动两次提交,从而有效地从项目历史记录中删除刚刚创建的两个快照。请记住,这种重置应仅用于未发布的提交。如果已将您的提交推送到公共仓库,则不要执行上述操作。
git clean
git clean命令从您的工作目录中删除未追踪的文件。这真的是一个方便的命令,通过git status看看哪些文件没有跟踪和然后手动删除它们。像一个普通的rm命令,git clean是不可逆的,所以一定要确保在运行它之前你真的想要删除未跟踪文件。
git clean命令通常与git reset –hard结合执行。请记住,重置仅影响已跟踪的文件,因此需要单独的命令来清理未跟踪的文件。综合这两个命令可让您将工作目录恢复到特定提交的确切状态。
用法
git clean -n
执行git clean的“无效运行”模式。这将显示哪些文件将被删除,而不会真实执行。
git clean -f
Remove untracked files from the current directory. The -f (force) flag is required unless the clean.requireForce configuration option is set to false (it’s true by default). This will not remove untracked folders or files specified by .gitignore.
从当前目录中删除未跟踪的文件。除非clean.requireForce的配置选项设置为false(默认情况下这是true),则需要-f(强制)标记。这不会删除未跟踪的文件夹或.gitignore指定的文件.
git clean -f <path>
删除未跟踪的文件,但将操作限制到指定的路径。
git clean -df
从当前目录中删除未跟踪的文件和未跟踪的目录。
git clean -xf
从当前目录以及Git通常忽略的任何文件中删除未跟踪的文件。
讨论
The git reset –hard and git clean -f commands are your best friends after you’ve made some embarrassing developments in your local repository and want to burn the evidence. Running both of them will make your working directory match the most recent commit, giving you a clean slate to work with.
The git clean command can also be useful for cleaning up the working directory after a build. For example, it can easily remove the .o and .exe binaries generated by a C compiler. This is occasionally a necessary step before packaging a project for release. The -x option is particularly convenient for this purpose.
Keep in mind that, along with git reset, git clean is one of the only Git commands that has the potential to permanently delete commits, so be careful with it. In fact, it’s so easy to lose important additions that the Git maintainers require the -f flag for even the most basic operations. This prevents you from accidentally deleting everything with a naive git clean call.
如果你在本地库开发过程中遇到问题,并要烧掉所有证据,git reset –hard和git clean -f命令你一定很喜欢。运行它们将使您的工作目录匹配最近的提交,给您一个干净的工作。
git clean命令也可用于清理构建后的工作目录。例如,它可以很容易地除去.o和.exe由C编译器生成的二进制文件。在包装项目发布之前,这偶尔是一个必要的步骤。该-x选项对于此目的特别方便。
请记住,与之一起的git reset,git clean是唯一可以永久删除提交的Git命令之一,所以要小心。事实上,它是如此容易失去了Git的维护者重要的补充需要的-f,即使是最基本的操作标志。这样可以防止您无意中使用git clean删除所有内容。
示例
以下示例清除了工作目录中的所有更改,包括已添加的新文件。它假设您已经提交了几个快照,并正在尝试一些新的开发。
# Edit some existing files
# Add some new files
# Realize you have no idea what you're doing
# Undo changes in tracked files
git reset --hard
# Remove untracked files
git clean -df
运行此重置/清除序列后,工作目录和暂存区域将与最近的提交完全相同,并且git status将报告一个干净的工作目录。你现在准备重新开始了。
请注意,与第二个示例不同git reset
,新文件_not _added到存储库。因此,他们不会受到影响git reset –hard,git clean被要求删除它们。