Git - 在线存储库中有未跟踪的文件[重复]

Posted

技术标签:

【中文标题】Git - 在线存储库中有未跟踪的文件[重复]【英文标题】:Git - Have untracked files in online repository [duplicate] 【发布时间】:2019-07-01 11:28:11 【问题描述】:

我正在使用 git (bitbucket) 对我的 linux 配置文件进行源代码控制。所有文件都在目录~/.cfg/ 中。然后我在~/.cfg/local/ 中还有一些本地配置文件,这些文件应该因机器而异。

我想在我的在线存储库中保留一份本地文件的副本,作为本地配置的一种示例,但我不想跟踪这些文件。我真的不在乎它们是否会被git clone 克隆,但无论哪种方式都可以。

我尝试关注this answer,但这会从在线存储库中删除文件。

我还尝试了this blog post 中概述的解决方案,效果更好,但不幸的是有两个缺点:1)它必须在每台机器上重复,2)它实际上并没有取消关注文件。因此,如果我不小心从某台机器上传了本地配置(忘记运行帖子中的命令),任何其他机器上的下一个 git pull 将覆盖该机器的本地配置。


总而言之,我想要一个执行以下操作的解决方案:

    它将整个~/.cfg/(包括~/.cfg/local/)的初始上传保存在在线存储库中。 每当我执行标准git add -A; git commit -m "asdf"; git push 时,它会推送~/.cfg/ 的内容,而不是~/.cfg/local/ 的内容 当我git pull时,它会拉取~/.cfg/的内容,而不是~/.cfg/local/的内容。

【问题讨论】:

你应该看看使用 .gitignore 文件来指定 cfg 文件和文件夹。你试过了吗? 我有一个带有/local/*.gitignore,但这不会停止跟踪我的本地文件,除非我还使用链接答案中的git rm 命令(删除文件来自在线存储库)。 我认为该帖子的答案与我链接到的第二个解决方案相同。 This answers specifically 是推荐的方法。 【参考方案1】:

我想要一个可以做 [Git 做不到的事情] 的解决方案

抱歉,但答案是:不,Git 做不到。您可以关闭,但这并不好玩:它需要跑git clone的每一个人,从那以后就有可能导致烧伤的反复遭遇。这就是为什么标准方法是this answer到Can I 'git commit' a file and ignore its content changes?中推荐的方法

这可能有助于理解 为什么Git 不能做到这一点。让我们更具体地看一下“那个”是什么:

将整个~/.cfg/(包括~/.cfg/local/)的初始上传保留在在线存储库中。

这个,你可以做到。但措辞很奇怪,因为 Git 不存储 文件。 Git 存储 commits,其中 包含 个文件。这可能看起来只是语义,但话又说回来,它是关于“热水”水是否适合淋浴(40˚C / 104˚F:热,但不会烫伤),或者会给你第二次 -度烧伤(95˚C / 203˚F:在标准压力下接近沸腾)。

所以,您可以有一个包含cfg/foocfg/local/bar 等文件的提交。到目前为止,没有真正的问题——主要问题是你不能有一个包含空目录 cfg/local/ 的提交,因为 Git 在每个提交中只存储文件本身,而不是包含目录:它假设任何人使用 存储库稍后将根据需要自动创建目录,只要有要存储的文件,其名称会强制未来/其他 Git 调用 os.mkdir 或创建包含该文件的目录的任何内容。 p>

每当我执行标准git add -A; git commit -m "asdf"; git push 时推送~/.cfg/ 的内容而不是~/.cfg/local/ 的内容

这是第一个问题,那些“纯粹的”语义至少有点烫伤:Git 不推送文件。 Git 推送提交

这里有三个命令。第一个,git add -A,告诉 Git:更新索引中记录的所有文件的索引副本,用我的工作树中的新版本替换它。第二个,@ 987654334@,告诉 Git:使用存储在索引中的文件进行新的提交。 第三个,git push,告诉 Git:向其他 Git 发送一些提交,然后要求其他 Git 将其一个或多个引用设置为某个哈希 ID,例如它的 refs/heads/master(它的 master 分支)。

这带来了这个新术语,索引,这就是麻烦开始的地方。

如果您的cfg/local/bar 文件在您的索引中,那么它将在您的提交中。如果它在您的索引中 not,它将 not 在您的提交中。这很简单,但它的含义很糟糕:

您可以在不接触工作树版本 (git rm --cached cfg/local/bar) 的情况下从索引中删除文件,但这会导致未来出现问题。

李>

或者,您可以在您的索引中 的文件副本上设置--assume-unchanged--skip-worktree 位。这几乎足够好,但还不够。 (顺便说一句,这两者或多或少是等价的,但“skip worktree”是用于这种用途的——除了它的真正意图是用于稀疏结帐。我将在下面写“skip worktree”但是这实际上意味着任何一个。)

设置该位需要您在git clone 之后手动运行命令。该索引对于存储库的您的 副本是私有的,因此运行git clone 的每个人也必须在git clone 之后运行这个git update-index 命令,至少一次。 (Git 不会让您通过 Git 本身自动执行此操作,尽管您当然可以编写一个脚本来执行此操作并分发该脚本。)

正如您可能已经看到的那样,这只几乎有效。

当我git pull时拉~/.cfg/的内容而不是~/.cfg/local/的内容

再一次,Git 会在这里烧死你。问题是git pull 并不是真正的自己的东西:它意味着运行git fetch,然后运行第二个 Git 命令second Git 命令正在运行惹麻烦。

第二个 Git 命令通常是 git merge,我们现在假设它是。另一个选项,git rebase,对你来说更糟,因为 rebase 本质上是重复的git cherry-pick,每个cherry-pick 操作本身都是一个合并,导致 多个 合并.

像提交一样,合并发生在索引中或通过索引发生。 Git 将 所有 三个 提交中的文件加载到索引中,在两个单独的步骤中配对文件(基础与“我们的”,以及基础与他们的),然后将配对。因此,这会合并索引中的每个文件,或者,如果在较早的提交中 在索引中的文件 现在在索引中不,则 删除或重命名文件。

这意味着如果文件cfg/local/bar 存在于合并基础提交和“他们的”提交中,并且如果您希望初始git clonecfg/local/bar 填充cfg/local,则它需要存在——那么它也需要存在于“我们的”提交中,否则 Git 会坚持删除它以保留我们的更改。反过来,这意味着如果他们在 他们的 提交中更改了 他们的 副本,Git 也会希望在您的提交中将他们的更改应用于您的副本。

如果您使用git update-index 来处理--skip-worktree 标志,那么您一直在重新提交cfg/local/bar 的原始版本。该标志只是告诉 Git:嘿,不要看我自己版本的这个文件,只要假设索引中的副本仍然正确。 这会影响 git add -A 步骤:而不是 更新索引中列出的所有文件,它实际上是:更新所有没有特别标记的文件。你可以随意更改cfg/local/bar,@987654363 @ 将跳过更新:它不会将您的工作树cfg/local/bar 复制回索引中,而是保留在您第一次运行git clone 时将其放入索引中的副本@ 987654366@给你。

所以所有你的提交都有一个cfg/local/bar,但是内容这些提交存储in那个cfg/local/bar,在每个提交中, 与运行 git clone 时获得的内容相同,即使您更改了工作树副本。您的 skip-worktree 位告诉您的 Git 只保留 cfg/local/bar 的索引副本,它已经完成了。

但是现在是合并时间,并且他们已经改变了他们的cfg/local/bar,无论出于何种原因——原因并不重要,重要的是他们确实改变了它——现在你的 Git 面临着将你的改变(无)与他们的改变(一些)结合起来的工作。它通过采取唯一的更改来做到这一点——当然是他们的——现在你的 Git 会坚持复制更新的 cfg/local/bar 到你的工作树中。这将覆盖你的cfg/local/bar,这就是痛点:这就是这种方法烧伤你的地方。

如果他们 从不(从来没有,不是一次)改变他们的cfg/local/bar,这种方法——设置skip-worktree——实际上会工作。但这取决于陌生人的好意,或者至少取决于 每次提交cfg/local/bar 中的本地配置完全相同的想法......在这种情况下,重点是什么根本就犯了?

但是,如果他们确实更改了它,那么当您将他们的更改与您的缺少更改合并时,您会被烧毁(轻度或其他),因为 Git 会希望用他们更新的覆盖您的 cfg/local/bar

您在早期从索引中删除您的cfg/local/bar 的替代方案更糟糕:现在每个提交推送没有文件。 Git 将此视为一个命令:当从有文件的提交转到没有文件的提交时,删除文件。所以如果你采用这种方法,你're 更改文件的人!你告诉其他人:删除这个文件!

唯一真正的、100% 保证的、正确的处理方法是:一开始就不要提交文件。如果每个 在存储库中提交没有cfg/local/bar,该文件将永远放入索引中。如果该名称也列在.gitignore 中,则不会自动“添加所有文件”将其添加到索引中,因此它不会出现在未来提交中.这意味着当你开始时它不会在那里,当你完成时也不会。 Git 永远不会想要合并它,也不会覆盖它的副本。它始终是一个未被跟踪和忽略的文件,存在于您的工作树中,但不存在于您的任何提交中。

当然,这意味着有一点最初的痛苦:每次运行git clone <url> 时,您还必须执行:cp -r .cfg/local-committed/ .cfg/local。但是,如果您要使用--skip-worktree,那么每次运行git clone <url> 时,您必须立即使用git update-index --skip-worktree .cfg/local/bar。所以它与糟糕的选择完全相同的痛苦,没有任何坏处。

此外,如果您可以控制该软件,您可以设置该软件,以便在您第一次运行该程序时.cfg/local/不存在,该程序通过从.cfg/local-committed/ 复制创建 .cfg/local/。然后“第一次设置”的痛苦也消失了! 这就是为什么将默认配置提交到单独的文件中,用户手动或自动复制到本地配置文件,该文件永远是一个未跟踪的文件,是正确的解决方案。

【讨论】:

谢谢,这是一个非常详细的解释和有趣的阅读。您的报价“Git 不推送文件。Git 推送提交。” (这是整篇文章的要点)在这种情况下理解尤为重要。您在上一段中概述的解决方案似乎是最佳解决方案,也是我将使用的解决方案。

以上是关于Git - 在线存储库中有未跟踪的文件[重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何丢弃 GIT 存储库中未经检查的更改 [重复]

Git:忽略跟踪的文件

忽略Mercurial中文件的未来更改,但仍然跟踪它[重复]

如何从 git 存储库中删除文件而不实际删除文件 [重复]

如何转换 git 存储库中的大量提交 [重复]

使用 Git 检查脏索引或未跟踪文件