- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个已重命名然后编辑的文件。我想告诉 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
git rm
,没问题:
$ git rm old-name.txt
mv
在 shell 中,
git add
,然后恢复新内容,但这似乎是很长的路要走!)
最佳答案
Git 并没有真正进行重命名。它们都是以“事后”的方式计算的:git 将一个提交与另一个提交进行比较,并在比较时决定是否有重命名。这意味着 git 是否考虑“重命名”是动态变化的。我知道你在问一个你甚至还没有做出的提交,但请耐心等待,这确实是相关的(但答案会很长)。
当您询问 git(通过 git show
或 git log -p
或 git 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 status
在 git 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
sed
命令。第一个替换更改文件模式(100644,但任何模式都会变成全零)和 SHA-1(匹配任何 SHA-1,替换为 git 的特殊全零 SHA-1),第二个离开模式并替换名称时单独使用 SHA-1。
old-name.txt
的未 merge 条目,这可能会严重失败。因为文件可能还有其他阶段(1 到 3)。
关于git - 如何在 git 中不进行后续编辑的情况下进行重命名?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29706086/
我是一名优秀的程序员,十分优秀!