gpt4 book ai didi

git - 如何在 git 中不进行后续编辑的情况下进行重命名?

转载 作者:行者123 更新时间:2023-12-03 07:35:06 25 4
gpt4 key购买 nike

我有一个已重命名然后编辑的文件。我想告诉 Git 进行重命名,而不是内容修改。也就是说,我希望分阶段删除旧文件名,并使用新文件名添加旧文件内容。

所以我有这个:

Changes not staged for commit:

deleted: old-name.txt

Untracked files:

new-name.txt

但想要这个:
Changes to be committed:

new file: new-name.txt
deleted: old-name.txt

Changes not staged for commit:

modified: new-name.txt

或这个:
Changes to be committed:

renamed: old-name.txt -> new-name.txt

Changes not staged for commit:

modified: new-name.txt

(其中相似性度量应为 100%)。

我想不出一个简单的方法来做到这一点。

是否有用于获取特定文件的特定修订的内容并将其添加到指定路径下的 git 暂存区域的语法?

删除部分,用 git rm ,没问题:
$ git rm old-name.txt

这是我正在努力解决的重命名的添加部分。 (我可以保存新内容, checkout 一个新副本(对于旧内容), mv 在 shell 中, git add ,然后恢复新内容,但这似乎是很长的路要走!)

谢谢!

最佳答案

Git 并没有真正进行重命名。它们都是以“事后”的方式计算的:git 将一个提交与另一个提交进行比较,并在比较时决定是否有重命名。这意味着 git 是否考虑“重命名”是动态变化的。我知道你在问一个你甚至还没有做出的提交,但请耐心等待,这确实是相关的(但答案会很长)。

当您询问 git(通过 git showgit log -pgit diff HEAD^ HEAD)“上次提交中发生了什么”时,它会运行上一次提交的差异(HEAD^HEAD~1 或实际原始 SHA-1对于之前的提交——任何这些都可以识别它)和当前的提交( HEAD )。在进行这种差异时,它可能会发现曾经有一个 old.txt并且不再存在;并且没有 new.txt但现在有。

这些文件名——曾经存在但不存在的文件,以及现在存在但不存在的文件——被放入标记为“重命名候选者”的堆中。然后,对于堆中的每个名称,git 比较“旧内容”和“新内容”。由于 git 将内容减少到 SHA-1 的方式,精确匹配的比较非常容易;如果精确匹配失败,git 会切换到可选的“内容至少相似”差异以检查重命名。与 git diff此可选步骤由 -M 控制旗帜。对于其他命令,它要么由您的 git config 设置值,或硬编码到命令中。

现在,回到集结区和 git status :git 在索引/暂存区中存储的内容基本上是“下一次提交的原型(prototype)”。当您git add git 立即存储文件内容,在此过程中计算 SHA-1,然后将 SHA-1 存储在索引中。当您git rm有些东西,git 在索引中存储了一个注释,说“这个路径名在下一次提交时被故意删除”。
git status命令,然后,简单地做一个差异——或者实际上,两个差异:HEAD vs 索引,用于将要提交的内容;和索引与工作树,对于可能(但尚未)提交的内容。

在第一个差异中,git 使用与以往相同的机制来检测重命名。如果 HEAD 中有路径提交在索引中消失了,并且索引中的路径是新的而不是在 HEAD 中提交,它是重命名检测的候选者。 git status命令硬连线将检测重命名为“on”(文件计数限制为 200;只有一个重命名检测候选者,这个限制就足够了)。

这对您的案件意味着什么?好吧,您重命名了一个文件(不使用 git mv ,但这并不重要,因为 git statusgit status 找到重命名或找不到它),现在有了更新的不同版本新文件。

如果您 git add新版本,新版本进入 repo,其 SHA-1 在索引中,当 git status做一个差异,它将比较新旧。如果它们至少“50% 相似”( git status 的硬接线值),git 会告诉您文件已重命名。

当然,git add -ing 修改后的内容并不是你所要求的:你想做一个中间提交,其中文件只被重命名,即提交带有新名称但旧内容的树。

由于上述所有动态重命名检测,您不必这样做。如果你想这样做(无论出于何种原因)......好吧,git 并没有让这一切变得那么容易。

最直接的方法就像你建议的那样:将修改后的内容移到某处,使用 git checkout -- old-name.txt ,然后 git mv old-name.txt new-name.txt ,然后提交。 git mv将重命名索引/暂存区域中的文件,并重命名工作树版本。

git mv有一个 --cached选项如 git rm是的,你可以只是 git mv --cached old-name.txt new-name.txt然后 git commit .第一步将重命名索引中的文件,而不触及工作树。但它没有:它坚持覆盖工作树版本,并且坚持旧名称必须存在于工作树中才能启动。

在不触及工作树的情况下执行此操作的单步方法是使用 git update-index --index-info ,但这也有点乱(反正我马上就会展示)。幸运的是,我们还能做最后一件事。我已经设置了与您相同的情况,通过将旧名称重命名为新名称并修改文件:

$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

deleted: old-name.txt

Untracked files:
(use "git add <file>..." to include in what will be committed)

new-name.txt

我们现在要做的是, 首先,手动将文件放回其旧名称,然后使用 git mv再次切换到新名称 :
$ mv new-name.txt old-name.txt
$ git mv old-name.txt new-name.txt

这次 git mv更新索引中的名称,但将原始内容保留为索引 SHA-1,但将工作树版本(新内容)移动到工作树中:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

renamed: old-name.txt -> new-name.txt

Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

modified: new-name.txt

现在只是 git commit使用重命名而不是新内容进行提交。

(请注意,这取决于没有旧名称的新文件!)

使用 git update-index 怎么样? ?好吧,首先让我们回到“在工作树中更改,索引匹配 HEAD 提交”状态:
$ git reset --mixed HEAD  # set index=HEAD, leave work-tree alone

现在让我们看看 old-name.txt 的索引中有什么内容:
$ git ls-files --stage -- old-name.txt
100644 2b27f2df63a3419da26984b5f7bafa29bdf5b3e3 0 old-name.txt

那么,我们需要什么 git update-index --index-info要做的就是清除 old-name.txt 的条目但为 new-name.txt 创建一个相同的条目:
$ (git ls-files --stage -- old-name.txt;
git ls-files --stage -- old-name.txt) |
sed -e \
'1s/^[0-9]* [0-9a-f]*/000000 0000000000000000000000000000000000000000/' \
-e '2s/old-name.txt$/new-name.txt/' |
git update-index --index-info

(注意:为了发布目的,我将上面的内容分解了,当我输入它时全部是一行;在 sh/bash 中,它应该像这样分解,因为我添加了反斜杠以继续“sed”命令) .

还有其他一些方法可以做到这一点,但简单地提取索引条目两次并将第一个条目修改为删除,第二个使用新名称似乎最简单,因此 sed命令。第一个替换更改文件模式(100644,但任何模式都会变成全零)和 SHA-1(匹配任何 SHA-1,替换为 git 的特殊全零 SHA-1),第二个离开模式并替换名称时单独使用 SHA-1。

当更新索引完成时,索引记录了旧路径的删除和新路径的添加(与旧路径中的模式和 SHA-1 相同)。

请注意,如果索引具有 old-name.txt 的未 merge 条目,这可能会严重失败。因为文件可能还有其他阶段(1 到 3)。

关于git - 如何在 git 中不进行后续编辑的情况下进行重命名?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29706086/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com