git - 当本地有新的重命名文件时如何恢复最新提交?
问题描述
我myBranch
正在进行一些更改,包括重命名文件。
我想进行实验,然后如果出现问题,可能会擦除我的所有更改(恢复到最新的提交/HEAD)。
通常我会这样做:
git checkout -- .
就是这样。
但是,删除重命名的暂存文件以进行提交(也将它们从我的工作目录中删除)存在问题。
因此,当我想重置时,我正在处理的场景是:
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: old_file.txt -> new_file.txt
...
< files >
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
...
< files >
可能有很多不同的文件。
如何恢复到头丢弃所有已提交和未提交的更改,包括重命名文件?
我想出的最干净的解决方案是:
git reset
git clean -f
git checkout -- .
解决方案
TL;博士
只需删除任何多余的不需要的副本,之后您的git checkout -- .
技巧就会正常工作。
如果您非常确定不会丢失您想要保存的任何工作,您可以git reset --hard
一次完成所有工作(但请注意,这会影响整个工作树,而不仅仅是您可能位于的任何子目录)。
长
在 Git 中,文件重命名操作不会记录在任何地方。它们是根据内容检测的。暂时从“恢复工作树内容”的直接问题退后一步,考虑一下当你有两个现有的 Git 提交时会发生什么,这两个提交包含所有文件的两个不同的即时快照。
比较两个提交
在旧提交中,您有文件 A、B 和 C。我们将此提交放在左侧。在新提交中,您有文件 B、C 和 N。我们将此提交放在右侧。他们来了:
old new
--- ---
A
B B
C C
N
当 Git 比较左侧和右侧时,它看到两个 B 文件和两个 C 文件,然后对自己说:啊哈,每次都必须是相同的文件,可能内容不同。我将比较旧 B 和新 B;然后我将比较 old-C 和 new-C。如果配对的文件是一样的,我就不说了。如果配对文件不同,我会告诉你有什么不同。
剩下的是旧的A和新的N。在 Git 只是说删除文件 A 并使用全新的内容创建 N之前,你可以——而且 Git 会自动这样做,因为 Git 2.12 左右——让 Git检查: N 中的内容与 A 中的内容很相似吗?* 如果是, Git 会说:将文件 A 重命名为 N,然后,如果需要,还显示要更改哪些内容以使文件 A 变为文件 N。
Git 对你的工作树文件做同样的事情
我将在这里快速讨论一些重要的事情:Git 实际上每个文件都有三个副本,而不仅仅是两个。当前提交中的每个文件都有一个永久冻结的副本,然后每个文件的一个副本准备进入您现在可以进行的下一次提交,存储在 Git 的index中,最后,有一个副本每个文件都作为普通文件,以便您可以查看并使用它。
冻结的副本采用特殊的、只读的、仅限 Git 的格式,自动针对所有其他提交进行重复数据删除。索引“副本”——它不是真正的副本,因为它使用带有重复数据删除的冻结格式——不是直接可见的。1 只有您的工作树副本是可见的:该副本是您计算机上所有普通的非 Git 程序都可以使用的格式,因此当您查看目录时,您看到的就是这些。
Git 大部分时间不使用这个工作树副本:它只是在您签出该提交时从提交中复制出来(首先将提交复制到 Git 的索引,然后将索引副本解冻到工作树中) . 然而,该git add
命令将工作树副本复制回 Git 的索引,替换旧的索引副本。这就是为什么git add
即使 Git 已经知道文件,你也必须保存它的原因:在进行新提交时,Git 使用索引副本,而不是工作树副本。
要重命名文件,通常同时重命名索引副本和工作树副本。(您不能更改任何现有提交的任何部分,因此这个问题甚至永远不会出现。)通常,您可能git mv
会这样做。然而,真正重要的是,一个名为的文件的索引副本path/to/file
现在不见了,取而代之的是,索引现在包含一个名为new/name/of/somefile
. 同样,工作树文件path/to/file
现在消失了,取而代之的是工作树包含一个名为new/name/of/somefile
.
1实际上,您可以直接查看索引内容,使用git ls-files --stage
. 只是这不是普通用户真正想要的:该ls-files
命令用于编写 Git 命令,而不是日常使用。git status
对于日常工作,查看打印的内容更为有用。
git status
当你运行时git status
,Git 会运行两个内部git diff
操作,并始终启用重命名检测:
首先,Git 将
HEAD
提交的文件(在提交的HEAD
名称中永久冻结)与索引中的所有文件进行比较。如果某个文件在左侧消失了,但右侧出现了一些不在左侧的名称,Git 会检查:新的右侧文件真的只是左侧文件的重命名吗? 如果是这样,git status
将告诉您在暂存为提交的更改中,某些文件正在被重命名。对于每个在和索引中相同的文件,什么都不说。对于以某种方式不匹配的每个文件,告诉您它不匹配的方式。这些是为 commit 上演的更改。他们实际上并没有承诺!
HEAD
git status
git status
然后,Git 将索引中的所有文件与工作树中的文件进行比较。如果某个文件从索引中消失了,但工作树中出现了一些新名称,Git 将检查这是否可能来自重命名文件。
和以前一样,当索引和工作树副本不匹配时,
git status
说明这一点。这些是未暂存的更改 commit。提交实际上使用了 Git 索引中的文件副本,而不是工作树副本,所以这就是为什么不暂存这些文件的原因。
因此,就像任何两次提交一样,Git 实际上并不存储重命名操作。它只是说:呵呵,左边有文件A,右边没有A,但右边有新文件N。让我们看看我们是否可以通过重命名A来构建文件N。如果可以 ,Git要求将A重命名为N。
因此,如果您的工作树中有一些不应该存在的额外文件 F,并且您删除了 F,那么现在它已经消失了。如果你有一个丢失的文件 G,你可以从提交中恢复它,现在它就在那里。现在没有重命名。如果你想要重命名,你只需要删除 G 并创建 F(具有正确的内容),现在 Git 将比较左边(有 F,没有 G)和右边(没有 F,有 G)哦嘿,看,改名!
这意味着您需要做的所有(?)操作就是操作索引和工作树的内容。要将新文件添加到索引中,请在工作树中创建文件并运行. 要从索引中删除文件,请使用:git add file
git rm file
: 从索引和工作树中删除文件,或git rm --cached file
: 从 Git 的索引中删除该文件,但将其单独留在您的工作树中。
该git restore
命令(在输出中提到git status
)通过从以下位置复制文件来工作:
- 任何提交,或
- 索引
并将副本放入:
- 索引,和/或
- 你的工作树
您的任务是以所需名称创建正确的文件,并删除使用任何不需要的名称存储的文件。
git reset --hard
使用git reset
可以使这无痛,但请注意,这git reset
可能具有很大的破坏性。我们之前提到了关于 Git 的索引保存将进入下一次提交的文件副本的整个事情。该reset
命令执行以下一项、两项或全部三项操作:
首先,
git reset
影响当前分支名称。您必须告诉 Git如何影响名称。如果你什么都不说,Git 假定效果是将它“重置”为
HEAD
,它已经在哪里了。因此,默认重置是无操作重置。这本身没有用,但是如果您告诉git reset
继续执行第 2 步,或者同时执行第 2 步和第 3 步,它就会变得有用。如果使用
--soft
,Git 会在执行完第 1 步后停止。否则继续执行第 2 步。然后,
git reset
重置 Git 的索引。请记住,索引已经包含应该进入下一次提交的每个文件。如果您没有对索引做任何事情,那么索引的文件就是那些从您上次签出的提交中复制出来的文件。如果您从那时起对索引进行了处理,则索引具有这些文件的新版本,和/或其中更改了某些文件名,和/或有新文件和/或缺少旧文件(重命名是与删除旧名称并创建具有相同内容的新名称相同)。
重置命令现在将使索引内容与提交内容匹配。如果您在步骤 1 中选择了其他提交,这就是其他提交的内容。如果不是,这就是同一个提交的内容——所以这个步骤只有在你以某种方式更新了索引的文件时才真正有用。但是,如果您希望 Git 继续执行第 3 步,则必须让它执行第 2 步。
如果您使用
--mixed
或默认值,Git 将在此处停止。最后,
git reset
重置您的工作树。这实际上与第 2 步一起发生:每当 Git 必须在第 2 步中删除文件的索引副本时,它也会删除文件的工作树副本。如果 Git 必须在步骤 2 中添加文件的索引副本,它将清除工作树中副本的内容,并将其替换为更新索引中的(解冻)副本。但是,如果该文件在索引中不存在,则它是未跟踪的,Git 不会在第 3 步中触及它。如果您使用
--hard
. 如果您修改了索引内容,或者使用了两个单独git reset
的 s,一个是 s,一个是 s,--mixed
这--hard
可能会在您的工作树中留下一些文件,因为步骤 2 可能使它们无法跟踪。但是,如果此时它们妨碍了它们,您只需要使用系统上的普通日常文件删除器将它们删除即可。
要记住的主要事情git reset
是,它会选择性地重置 Git 的索引(通常包含您可以从某个地方恢复的文件,因为它们大多来自一些较早的提交)和您的工作树文件。那些工作树文件不在 Git 中,如果它们不是来自某个提交,则无法从 Git 中取回它们。因此,在使用之前git reset --hard
,请确保您不会丢失要保留的工作树文件。
推荐阅读
- solace - REST - 将消息路由到不同的 HTTP 消费者
- azure - Azure 来宾用户访问问题
- codenameone - 同一表单中的多个闪烁光标
- cmake - 谷歌测试的 CMakeLists.txt 设置
- c# - SignInManager.SignInAsync 没有让我登录(Vue.js SPA)
- ruby-on-rails - ActiveStorage - 创建或更新附件
- c - 使用联合和堆的类型双关语
- macos - 如何将已经运行的二进制文件设置为在启动时运行
- javascript - jquery“.load”内容的window.history.back()?
- sql-server - 将空格添加到带有 Caps 的 Camelcase