我正在使用git(bitbucket)来控制我的Linux配置文件。所有文件都在目录~/.cfg/
中。然后,我在~/.cfg/local/
中还有一些本地配置文件,这些文件在计算机之间可能是不同的。
我想将本地文件的副本作为示例本地配置保留在我的在线存储库中,但不希望不跟踪文件。不过,我真的不在乎是否用git clone
克隆它们,无论哪种方式都可以。
我尝试关注this answer,但这会从在线存储库中删除文件。
我还尝试了this blog post中概述的解决方案,该解决方案效果更好,但不幸的是有两个缺点:1)必须在每台计算机上重复该解决方案; 2)实际上并没有取消关注文件。因此,如果我不小心从某台计算机上上传了本地配置(忘记从帖子中运行命令),则任何其他计算机上的下一个git pull
都会覆盖该计算机的本地配置。
总而言之,我想要一个执行以下操作的解决方案:
~/.cfg/
(包括~/.cfg/local/
)的初始上传保存在在线存储库中。~/.cfg/
时,它就会推送~/.cfg/local/
的内容,而不推送git add -A; git commit -m "asdf"; git push
的内容~/.cfg/
时,它提取~/.cfg/local/
的内容,但不提取git pull
的内容。答案 0 :(得分:3)
我想要一种解决方案,它可以执行[Git无法完成的事情]
对不起,但是答案是:不,Git无法做到这一点。您可以得到 close ,但这并不有趣:它需要部分工作每个运行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在每次提交中仅存储文件本身,而不在包含目录中存储文件:它假定任何人使用之后,只要有文件要存储,存储库将根据需要自动创建目录,该文件的名称会迫使future / other Git调用os.mkdir
或创建包含该文件的目录的任何名称文件。
- 每当我执行标准
~/.cfg/
时,推送~/.cfg/local/
的内容,但不推送git add -A; git commit -m "asdf"; git push
的内容
这是第一个问题,其中那些“纯粹”的语义至少有点烫手:Git不会推送文件。 Git推动 commits 。
您在这里有三个命令。第一个git add -A
告诉Git:通过用我的工作树中的新版本替换索引中记录的所有文件的索引副本。 git commit
告诉Git:使用存储在索引中的文件进行新的提交。第三个git push
告诉Git:发送一些提交( s)到其他Git,然后要求其他Git将其一个或多个引用(例如其refs/heads/master
(其master
分支)设置为某个哈希ID。 >
这带来了新的术语索引,这就是麻烦的开始。
如果您的cfg/local/bar
文件在索引中,那么它将在您的提交中。如果索引中的 not 不在您的提交中,则 not 。这很简单,但是其含义令人讨厌:
您可以从索引中删除文件,而无需触摸工作树版本(git rm --cached cfg/local/bar
),但这会引起将来的问题。
或者,您可以在索引中 的文件副本上设置--assume-unchanged
或--skip-worktree
位。这几乎足够好,但还不够。 (顺便说一下,两者大致相同,但是“跳过工作树”是用于这种用途的-除非其真正意图确实是用于稀疏结帐。我将在下面写下“跳过工作树”,但这真的意味着一个。)
设置该位要求您在git clone
之后手动运行命令。该索引对存储库的您的副本是私有的,因此每个运行git clone
的人也必须至少在git update-index
之后运行此git clone
命令。 (尽管您当然可以编写脚本来执行并分发该脚本,但是Git不允许您通过Git本身使之自动化。)
您可能已经看到,只有几乎有效。
- 在我
~/.cfg/
时拉~/.cfg/local/
的内容,但不拉git pull
的内容
Git会再次在这里烧死你。问题在于git pull
本身并不是一个真正的东西:它意味着运行git fetch
,然后运行第二个Git命令和 second Git命令会引起麻烦。
第二个Git命令通常是git merge
,现在我们假设是。另一个选项git rebase
对您来说更糟糕 ,因为本质上重复了git cherry-pick
的重设工作,而每个Cherry-pick操作本身就是一个合并,导致 multiple < / em>合并。
合并(例如提交)在索引中或通过索引进行。 Git将来自三个的 all 个文件加载到索引中,以两个单独的步骤(基本vs“我们的”,基本vs他们的)将文件配对,然后将配对。因此,这会合并索引中的每个文件,或者,如果先前提交中的不在索引中的 文件现在不在索引中,则删除或重命名文件。
这意味着,如果文件cfg/local/bar
存在于合并基础提交和“其”提交中,并且要填充{{1 }}和git clone
-那么它也必须存在于“我们的”提交中,否则Git将坚持删除以保持我们的更改。反过来,这意味着,如果他们在他们的提交中更改了他们的副本,Git也会在您的提交中将其更改也应用于您的副本。
如果您使用cfg/local
来标记cfg/local/bar
,则表示您一直在重新提交git update-index
的原始版本。该标志仅告诉Git:嘿,不要看我自己的文件版本,只需假设索引中的副本仍然正确即可。这会影响--skip-worktree
步骤: 更新索引中列出的所有文件,实际上它会执行:更新所有未特别标记的文件。您可以随意更改cfg/local/bar
,而git add -A
会跳过更新 :它不会将您的工作树cfg/local/bar
复制回索引中,而是在复制时将其复制回索引中您首先有git add -A
为您运行cfg/local/bar
。
因此,所有您的提交都具有git clone
,但是这些提交的内容将存储在{em> 中,即使更改了工作树副本,每次提交中的内容也与运行git checkout
时得到的内容相同。您的skip-worktree位告诉您的Git仅保留cfg/local/bar
的索引副本,这已经完成了。
但是现在是合并时间,并且他们出于任何原因更改了他们的 cfg/local/bar
-原因不是重要的是,他们做了对其进行了更改— 现在您的Git面临着将您的更改(无)与他们的更改(某些)相结合的工作。通过采取唯一的更改(当然是它们)和 now 来做到这一点,您的Git会坚持将更新的git clone
复制到您的工作树中。这会覆盖您的cfg/local/bar
,这就是痛苦所在:这就是这种方法会烧伤您的地方。
如果他们 从不(永远不会,一次)更改其cfg/local/bar
,则此方法(设置跳过工作树)将 实际起作用。但这取决于陌生人的好意,或者至少取决于以下想法:每次提交中cfg/local/bar
中的本地配置完全相同...在这种情况下,提交该怎么办?
但是,如果他们确实进行了更改,那么在将其更改与缺少更改合并时,您会被烫伤(轻微或其他),因为Git会希望用其更新后的覆盖cfg/local/bar
另一种选择是,您从早期从索引中删除 cfg/local/bar
的方法变得更糟:现在每次提交 you 推送都不会完全拥有文件。 Git将其视为命令:从具有文件的提交转到没有文件的提交时,请删除该文件。因此,如果采用这种方法,您是更改文件的人!您告诉了其他所有人:删除此文件!
真正做到100%保证的 正确方法是:切勿首先提交文件。如果每 >在存储库中的提交没有没有cfg/local/bar
,该文件将从不放入索引。如果该名称也列在cfg/local/bar
中,则不会自动将“添加所有文件”自动添加到索引中,因此它不会出现在将来中>提交。这意味着当您开始或结束时都不会在其中。 Git永远不会合并它,也不会覆盖它的副本。它将始终是一个未跟踪且被忽略的文件,存在于您的工作树中,但不存在于您的任何提交中。
当然,这意味着会有一些初期的痛苦:每次运行cfg/local/bar
时,您还必须这样做:cfg/local/bar
。但是,如果要使用.gitignore
,则每次运行git clone <url>
时,都必须立即使用cp -r .cfg/local-committed/ .cfg/local
。因此,它与坏选择的痛苦程度完全相同,没有任何坏处。
此外,如果您控制该软件,则可以设置该软件,以便在第一次运行时如果--skip-worktree
不存在,则该程序< em>通过从git clone <url>
复制来创建 git update-index --skip-worktree .cfg/local/bar
。然后,“首次安装”的痛苦也消失了! 正因为如此,将默认配置提交到一个单独的文件中,这样用户可以手动或自动复制到本地配置文件(该文件永远是未跟踪的文件),这才是正确的解决方案。