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/foo
和cfg/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 clone
用cfg/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 - 在线存储库中有未跟踪的文件[重复]的主要内容,如果未能解决你的问题,请参考以下文章