Git:如何从远程仓库中取消文件跟踪,但仍将其保存在所有开发人员的本地仓库中?

t9eec4r0  于 2023-03-11  发布在  Git
关注(0)|答案(2)|浏览(245)

git rm --cached mylogfile.log可以从存储库中取消跟踪mylogfile.log,同时仍将其保留在我的本地存储库中。
但是,如果其他开发人员在我提交之后从远程存储库中提取,他们将丢失本地存储库中的文件。
场景是我提交了一个不应该提交的文件,例如,一个机器特殊文件到远程仓库。我想修复错误而不影响其他开发人员。当然,我不能像git rm --cached mylogfile.log这样提交,然后所有其他开发人员丢失了他们的文件。
如果我使用git update-index --assume-unchanged,文件仍然会被git跟踪,这不是我想要的。
有什么办法解决这个问题吗?

htrmnn0y

htrmnn0y1#

在Git中,tracked 表示“在索引中”,Untracked表示“不在索引中”。
虽然只有一个(因此有了definite article "the")索引,索引与 current commit 相关。每次你 checkout 任何一个特定的commit--包括git checkout *somebranch*,它 checkout 了指定分支的 tip commit--你就在告诉Git从索引和工作树中删除任何文件的任何版本,只要它与目标commit不同,并将其插入到索引和工作树中,与目标提交一起的文件的版本。
假设你在一个哈希ID为a4ef00d的提交上,你让Git checkout 另一个哈希ID为1c0ffee的提交,如果文件mylogfile.log存在于提交a4ef00d中,但不存在于提交1c0ffee中,Git必须从索引和工作树中 * 移除 * 该文件。
现在你在1c0ffee上,如果你git checkout a4ef00d,Git必须把a4ef00dmylogfile.log的版本放进索引和工作树中,现在文件被再次跟踪,你工作树中的版本就是a4ef00d中的版本。

**无论你做什么,这总是正确的:假设一个文件在至少一个提交中 * 是 ,而在至少另一个提交中 * 不是 。当你从一个有该文件的提交移动到另一个没有该文件的提交时,Git会将其从索引和工作树中移除。当你从一个没有该文件的提交移动到另一个有该文件的提交时,Git会将其放回索引和工作树中。

这意味着文件同时被跟踪和不被跟踪(这是有可能的)会导致Git有时删除或重新创建该文件。你不能停止这个过程。文件的跟踪或不跟踪取决于你 checkout 的提交,也就是你让Git复制到索引中的内容。

索引版本可以不同于当前提交或工作树

事实上,索引是你构建下一个提交的地方,这也是它独立于“当前提交”的原因之一,你可以修改索引的内容;这些变更是为下一次提交而“暂存”的。2你不必暂存工作树变更;这样的改变是“未筹划的”,但是仍然存在,并且显示为“已修改但是未筹划的”。
设置--assume-unchanged--skip-worktree会告诉Git不要查看索引和工作树版本是否不同。因此,无论你提交了多少次新的提交,索引版本都可以与之前的提交保持一致。* 但要设置其中的任何一位,索引中必须有一个条目。

**如果文件被跟踪,则会提交该文件。**您所做的每个新提交都会以索引中当前的格式保存索引中的每个文件。您也无法停止此过程。

此困境的唯一真实的解决方案是删除该文件

一旦你删除了文件(git rm mylogfile.log),这个文件就会从索引和工作树中消失,你现在可以提交结果了,新的提交会缺少这个文件,因为它不在索引中。
现在文件不在索引中,你可以把它恢复到工作树中。它现在是 untracked。它将保持这种状态--未被跟踪,但存在于工作树中--只要你不把文件放回索引中。现在你必须避免git add-ing它,* 也 * git checkout-ing一个有它的旧提交。
当然,在移除和提交的时候,你可能会想把它保存在某个地方,最简单的方法是把它复制到Git之外的某个地方,git rm文件,提交,然后再把它复制回原处,你可以使用git rm --cached把它从索引中移除,而不需要从工作树中移除它。作为此过程的一种快捷方式-但实际上您是在将文件保存到存储库之外。请在执行以下步骤时记住这一点。
(You现在我可以通过在.gitignore文件中列出该文件来让Git停止抱怨它 * 是 * 未被跟踪的。这主要是让Git停止抱怨。它还可以防止Git在你添加“所有”文件或包含该文件的目录时自动git add-ing该文件-正如我们所看到的,这会导致该文件被跟踪。如果一个文件被跟踪,它的.gitignore条目无效。

现在,该文件将在各种操作中删除

正如我们在上面提到的,如果你移动到一个 * 确实 * 有文件的提交,那么这个文件就会进入索引和工作树(如果工作树中已经有一个同名的文件,Git会让你先把这个文件移走)。
你还指出:
但是,如果其他开发人员在我提交之后从远程存储库中提取,他们将丢失本地存储库中的文件。
更准确地说,如果其他人获得了你的提交,而你的提交 * 缺少 * 该文件(因此它是不可跟踪的),那么他们现在就有了我们上面提到的情况:如果它们从一个有该文件的提交移动到另一个没有该文件的提交(如git checkout),Git将从索引和工作树中删除该文件。

然而,git pull命令并不是简单地运行git checkout,它运行了 * 两个 * Git命令,第一个是git fetch,它从远程(另一个Git)获取提交,第二个取决于你告诉Git做什么:

  • 默认情况下git pull将运行git merge。这将执行常规合并或快进“merge”(不是真正的合并)。
  • 如果你让git pull运行git rebasegit pull也可以运行git rebase,这比git merge复杂,但这相当于执行一系列git cherry-pick操作来复制他们的原始提交,其中一个会在执行完你的一个git checkout提交后,尝试将他们的工作与你的“删除日志文件”提交合并。

使用git mergegit rebase这两个命令都会导致Git想要从索引和工作树中删除文件,类似于直接使用git checkout的情况,但有以下区别:

  • 如果他们提交 * 修改 *(保存新的/不同版本的)mylogfile.log,他们将看到一个“修改/删除冲突”,他们必须通过删除mylogfile.log来解决(在第一次保存任何他们想保存的东西之后,在其他地方)。
  • 如果不匹配(他们提交的mylogfile.log版本与“合并库”中的内容匹配),那么要么他们的工作树版本mylogfile.log与提交的版本匹配(Git会继续删除该文件),要么不匹配(Git会抱怨,他们必须保存任何他们想保存的东西,然后删除该文件,以便Git继续)。

一旦他们有一个提交--合并提交,或者他们自己的重定基提交--不再有这个文件,他们就处于和你一样的状态:文件现在从索引和工作树中消失了。2他们现在可以将它恢复到工作树中(但不能恢复到索引中),并将它作为一个未跟踪的文件来维护,就像你所做的那样。

这项工作没有出路

避免所有这些工作的唯一方法是不提交(并因此跟踪)文件。但是已经有人提交了现在每个人都必须执行这个额外的工作。注意,* 每个人 * 都必须这样做--也就是说,在其他地方保存工作树版本--* 每当他们从一个 * 没有 * 文件的提交穿越过来时,没有办法解决这个问题,因为有人在过去错误地提交了这个文件,Git会永远保存每个提交。

嗯,有一种方法可以绕过这项工作,但它涉及到做这项工作

你可以“改写历史”:把你的仓库改成一个新的,不同的仓库,在那里没有人提交过这个文件。2现在这个错误从一开始就没有犯过。3问题是,你现在必须让每个使用旧仓库的人都切换到这个新的,不同的仓库。
要像这样重写历史,可以使用git filter-branch或The BFG。有很多。
请注意,在执行此切换时,您必须做一些额外的工作:将日志文件从有错误的旧存储库复制到没有错误的新存储库的工作树中,所以你并不是真的在逃避工作:你只是在改变你做这件事的时间。

disbfnqx

disbfnqx2#

您正在查找此命令:

git update-index --skip-worktree <file>

这会让git忽略你在本地对该文件所做的任何修改,你可以执行git push,而不用担心本地版本会被其他人共享。
你也可以经常git pull没有任何问题;然而,如果这个<file>的上游版本发生了变化,即“正确的”版本有了更新,那么当您尝试执行git pull时,您将看到一条错误消息,说明本地更改将被拉取操作覆盖。
要解决这个问题,你需要“unskip”<file>,并把你所做的修改隐藏在本地,然后你可以拉取最新的,弹出你隐藏的本地版本,修复任何合并冲突,最后再次“skip”这个文件:
这种情况下的步骤如下:

1. git update-index --no-skip-worktree <file>
2. git stash
3. git pull
4. git stash pop
5. **handle any conflicts, re-apply any local-only changes you need**
6. git update-index --skip-worktree <file>

当a被“跳过”时,更改将在您的工作目录中进行,但运行git status将显示未进行任何更改。要查看哪些文件具有这些跳过的更改,您可以运行以下命令:

git ls-files -v | grep ^S

它详细地列出了所有文件(即在行首显示状态标志),然后使用grep搜索这些结果,只查找行首显示状态标志为大写S的文件(即^S)。

相关问题