从 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
有某个文件的某个版本,并且提交Z
有same 文件的same 版本,那么两个提交可以简单地共享一个底层文件. (这实际上是基于文件内容,而不是文件名。)但这也有一个很大的缺点:这意味着你实际上不能做任何事情new,即做任何新作品,包括这些冻结的文件。
因此,Git 需要一种方法和一个地方来解冻和解压缩(即再水合)冷冻和压缩(脱水)文件。 Git 将它们放置在您的工作树 中。您的工作树中的文件是普通的日常文件,由您的计算机提供,因此您可以完成工作。
所以这解释了你所有文件的三个副本中的两个:在当前(或HEAD
)提交中存在README.md
的脱水副本,在你的工作中存在README.md
的普通且有用的副本-tree,您可以在其中使用它。但是这第三个副本在做什么呢?
答案是:它以冻干的格式 放在您的索引或“暂存区”中,准备好进行新的提交。如果你现在运行git commit
,Git 将从索引中文件的冻干副本构建 new 提交。为什么不使用提交中的那些?这应该很明显:这是因为您无法更改那些副本!但是您可以替换索引中的冻干副本。这就是 git add
所做的:它压缩(冻结)文件的工作树版本并将其写入索引。3
因此,假设您修改了README.md
的工作树版本。 之前 git add README.md
,README.md
的索引副本与README.md
的HEAD
副本匹配,并且工作树副本不同。 之后 git add README.md
,README.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从技术上讲,索引保存 references 到 blob 对象,这是 Git 以冻结格式存储文件的方式。除非/直到您开始使用 git hash-object
和 git update-index
之类的东西,否则将索引视为包含每个文件的冻结格式副本同样有效。
3这就是git hash-object -w
和git 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 版本并从一个分支发布包,但将标签保留在另一个分支中