从 git 分支中删除文件但将其保留在另一个?

Posted

技术标签:

【中文标题】从 git 分支中删除文件但将其保留在另一个?【英文标题】:Remove a file from a git branch but keep it in another? 【发布时间】:2020-03-23 03:16:48 【问题描述】:

最近,我在同一个项目的 2 个不同分支中与其他人一起工作。我不小心将包含他的代码的分支develop 合并到我的中。然后我在我的合并请求中看到了他的文件,随后我学会了这样做的艰难方法

git rm HIS_FILES

关于他在我的分支中不需要的文件,不仅会从我的分支中删除它们,还会从他的(以及整个 git 索引)中删除它们。

我想知道的是,我如何正确地从我的分支中删除他的文件,以便它们不会也从他的分支中删除?一旦我意识到他的文件在我的分支中,我是否会创建一个新分支?在将分支 develop 合并到本地分支之前,我是否会恢复到上一次提交?

感谢您的帮助

【问题讨论】:

【参考方案1】:

git rm 仅从您的分支中删除该文件。

文件在其他分支被删除的可能原因是你的分支已经合并到其他分支。合并应该已删除该文件。

【讨论】:

【参考方案2】:

(这在技术上是评论而不是答案,但我希望能够使用格式......而且它永远不适合。)

[git rm] 不仅会从我的分支中删除 [files],还会从他的分支(以及整个 git 索引)中删除。

事实并非如此。而且,这也不是理解 Git 索引的正确方式。

索引有三个名称:Git 有时称它为索引,但有时称它为暂存区。在一些地方,Git 使用了 cache 这个词。这些大多都指的是同一个东西,其具体实现大多只是.git目录下名为index的文件。1但是索引,不管你用这些名字中的哪一个, 与 existing 提交关系不大。索引的主要功能是它是您构建提议的下一次提交的地方。

当我们使用短语 staging area 谈论索引时,我们关心的是保存在索引中的文件副本。2 Git 的工作方式,您随时可以拥有每个文件的 三个 个副本!您选择一个提交(使用 git checkout 或 Git 2.23 或更高版本中的新 git switch)作为您的当前提交。该提交以特殊的只读(和仅 Git)压缩格式保存所有文件的快照。

提交中的这些冻结格式文件无法更改any 现有提交的任何内容都无法更改。这样做有一些很大的优势:例如,由于文件不能被更改,它们可以被共享。如果提交A 有某个文件的某个版本,并且提交Zsame 文件的same 版本,那么两个提交可以简单地共享一个底层文件. (这实际上是基于文件内容,而不是文件名。)但这也有一个很大的缺点:这意味着你实际上不能做任何事情new,即做任何新作品,包括这些冻结的文件。

因此,Git 需要一种方法和一个地方来解冻和解压缩(即再水合)冷冻和压缩(脱水)文件。 Git 将它们放置在您的工作树 中。您的工作树中的文件是普通的日常文件,由您的计算机提供,因此您可以完成工作。

所以这解释了你所有文件的三个副本中的两个:在当前(或HEAD)提交中存在README.md 的脱水副本,在你的工作中存在README.md 的普通且有用的副本-tree,您可以在其中使用它。但是这第三个副本在做什么呢?

答案是:它以冻干的格式 放在您的索引或“暂存区”中,准备好进行新的提交。如果你现在运行git commit,Git 将从索引中文件的冻干副本构建 new 提交。为什么不使用提交中的那些?这应该很明显:这是因为您无法更改那些副本!但是您可以替换索引中的冻干副本。这就是 git add 所做的:它压缩(冻结)文件的工作树版本并将其写入索引。3

因此,假设您修改了README.md 的工作树版本。 之前 git add README.mdREADME.md 的索引副本与README.mdHEAD 副本匹配,并且工作树副本不同。 之后 git add README.mdREADME.md 的索引副本与工作树副本匹配(冻干格式除外)。始终存在三个副本,但其中两个匹配。使用git add 替换索引副本,使其与工作树副本匹配。 (HEAD 副本无法更改。)

这意味着在任何时候,索引都准备好了:git commit 只是将冻干的索引文件打包到一个新的提交中。新的提交成为HEAD 提交,被添加到当前分支。新的提交现在具有 every 文件的完整且完整(并永久冻结)的副本,如索引中所示。现在HEAD 提交和索引匹配,如果索引匹配工作树,所有三个副本都匹配。

当您使用git rm 时,Git 会从 索引 工作树中删除命名文件。下一个git commit没有该文件,因为它不在索引中。

如果您随后 git checkout 某个 other 分支,Git 现在会在作为该其他分支尖端的冻结提交中找到所有文件。 Git 将所有这些冻结格式的文件复制到索引中,以便它们准备好进入您所做的 next 提交;在更新了索引副本后,Git 将这些副本重新水化到工作树中,以便您可以查看和使用这些文件。现在(新选择的,不同的)HEAD 提交、索引和工作树再次全部匹配,您就可以开始工作了。

如果在从提交 Z 切换回提交 A 期间,Git 发现提交 Z 有一些文件——to-be-deleted.txt 可能——那 不是 在提交 @987654353 @,Git 从索引中删除to-be-deleted.txt,并从工作树中删除。所以现在它已经消失了——但它仍然存在于提交 Z 中。如果你git checkout 提交Z,Git 会在提交A 中看到to-be-deleted.txt 不是,在索引中不是,并且 在提交Z 中,因此它将to-be-deleted.txt 的提交Z 版本复制到索引和工作树中......现在,再次,HEAD、索引和工作-树都匹配。

始终要牢记的一个关键是 Git 是关于 commits 我们将 git checkout 一些 分支名称 进行切换分支,但 name 标识 一个特定的提交。然后,Git 将填充索引和工作树——它们都是临时区域!——来自那个提交。当我们进行新的提交时,Git 简单地打包索引中的所有内容,添加我们的名称等,写出新提交,其父提交是我们签出的提交,然后更新分支名称 来记住 new 提交的哈希 ID。所以分支名称​​move。提交一旦提交,就永远不会改变,并且通常会永远持续下去。4


1我们不得不说大部分,因为这条规则有很多例外。但是,您可以将 Git 指向一个 不同 文件,出于特殊目的,通过将环境变量 GIT_INDEX_FILE 设置为您希望 Git 使用的临时索引的路径名。如果该文件不存在,Git 将创建该文件,然后将其用作索引。例如,git stash 命令使用它从临时索引创建提交,所有这些都不会干扰(主或实际)索引。

2从技术上讲,索引保存 referencesblob 对象,这是 Git 以冻结格式存储文件的方式。除非/直到您开始使用 git hash-objectgit update-index 之类的东西,否则将索引视为包含每个文件的冻结格式副本同样有效。

3这就是git hash-object -wgit update-index 出现的地方:git add 压缩并写入一个新的冻干blob 对象,或者发现一个现有的blob 具有正确的内容,因此最终会重新使用现有的、已经冻结的 blob 对象。该 blob 对象具有唯一的哈希 ID,或者如果它是新的,则获得唯一的哈希 ID,git add 使用与 git update-index 相同的代码将正确的哈希 ID 写入索引。

我们同样可以问为什么不从工作树构建新的提交?这个问题没有很好的答案:5 其他版本控制系统,没有'不要总是在你的脸上摆出一个索引,实际上那个,或者看起来像那样的东西。但是 Git 会将索引推到你面前,所以你需要了解它。

4要摆脱提交,您需要安排一些事情,以便您无法找到提交。由于分支名称标识了 last 提交,因此很容易删除位于末尾的提交:只需将分支名称向后移动,以标识该提交的 。但是您可以通过一次一个步骤的倒退找到每个 较早的提交,因此您实际上只能删除尾部提交。 (这在其他一些 VCS 中更清楚,例如 Mercurial,它们不允许提交在多个分支上。在 Git 中事情变得混乱,其中提交可能同时在 许多 分支上。 )

5人们可以指出作为结果提供的各种功能,例如git add -p,但这是一种弱的事后证明。真正的问题是这些功能是否值得最初的复杂性。我不认为它们是——我认为 Git 可以以其他方式提供它们,不会使索引像现在这样直接——但这既是意见也是猜测,这并不适合 *** .

【讨论】:

以上是关于从 git 分支中删除文件但将其保留在另一个?的主要内容,如果未能解决你的问题,请参考以下文章

Bump 版本并从一个分支发布包,但将标签保留在另一个分支中

如何删除git嵌套repo但保留其中一个分支的文件?

删除 git repo 中的文件夹? [复制]

git merge:删除我想保留的文件!

从图像中删除边框,但将文本保留在边框上(OCR 之前的预处理)

SVN 从存储库中删除更改,但将它们保留在我的结帐中?