在一个文件中分支特定内容

时间:2018-12-17 16:40:16

标签: git git-merge

我正在尝试拥有一个针对每个分支的文件。 我不希望此文件被覆盖或在合并时更新。为什么这不起作用?

(我的尝试基于How to prevent tracked config files from being changed by merges in git?,但由于某种原因它没有用。我还遵循了the more detailed blog post,该答案基于该答案,并且也没有按照博客中的描述进行操作帖子。因此,这似乎是git版本问题。我使用的是2.7.4)

git init
echo "master">config
echo "config merge=alwaysours">.gitattributes
echo ".gitattributes merge=alwaysours">>.gitattributes
git config --local merge.alwaysours.driver true
git add -A
git commit -m 'Master'
git checkout -b feature
echo "feature">config
touch feature
echo ".gitattributes merge=alwaysours">.gitattributes
git add -A
git commit -m 'Add feature'
git checkout master
git merge feature 
cat config # PRINTS OUT feature INSTEAD OF master

1 个答案:

答案 0 :(得分:2)

由于我对Git merge strategy for a specific file depending on rebase / merge的回答中概述的原因,整个合并驱动程序的想法注定要失败。 1 可以采用另一种可行的方法,但这很丑陋。实际上,最后这太丑陋了,可能仍然是个坏主意。最好的选择也许是使用Git钩子(特别是post-checkoutpost-merge)来操纵工作树中的某些未跟踪和忽略的文件。

(不过,请注意,由于我不知道您真正想要在文件中包含什么内容,因此我什至没有一个很好的起点来提出这些建议作为解决方案。)< / p>

讨论

在这里值得记住,在我们甚至开始考虑专门处理其内容的文件之前,只是如何 Git处理文件。在Git中,文件并不是真的那么重要。在Git中重要的是 commit 。提交可存储文件,因此文件也很方便,但关键是提交本身,而提交文件的方式有点特殊,这在这一点上开始重要。

提交存储文件的方式是通过构建然后引用 tree 对象。树对象本质上是元组的列表:

$ git ls-tree HEAD
[lots of snippages here]
100644 blob acf853e0299463a12212e9ed5f35d7f4a9d289af    .gitattributes
040000 tree 7ba15927519648dbc42b15e61739cbf5aeebf48b    .github
100644 blob 0d77ea5894274c43c4b348c8b52b8e665a1a339e    .gitignore
...
100755 blob 54cbfecc5ab0531513ff9e069be55d74339ad427    git-bisect.sh
100644 blob 09b0102cae8c8c0e39dc239003ca599a896730cf    git-compat-util.h
100755 blob d13f02da95f3b9b3921c3ccff9e3b6a7511cd666    git-cvsexportcommit.perl
...
100644 blob 2d41fffd4c618b5d7b816146d9df684b195535e3    xdiff-interface.h
040000 tree 77abde3699bc6874e10f1c17f4b97c219492542f    xdiff
100644 blob d594cba3fc9d82d94b9277e886f2bee265e552f6    zlib.c

中间的字符串(blobtree)是从前面的模式派生的:100644100755是一个斑点,{{1} }是一棵树,并且有一堆不太常见的特殊情况。

文件并没有完全存储在Git中。而是文件的内容出现在 blob对象中,并位于列出的哈希ID处。我们可以直接看到该blob对象:

040000

$ git cat-file -p 54cbfecc5ab0531513ff9e069be55d74339ad427 #!/bin/sh USAGE='[help|start|bad|good|new|old|terms|skip|next|reset|visualize|view|replay|log|run]' LONG_USAGE='git bisect help print this long help message. ... [lots more, snipped] 命令提取对象,并取出Git冻结的内部压缩格式,并将其转换为可读的文本。因此, blob对象具有git cat-file -p Shell脚本的内容,并且 tree对象告诉Git在此特定提交中,blob对象应为在工作树中以git bisect 的名称扩展为有用的文本形式。

正是这个扩展为有用的文本形式的过程,我们可以在其中进行一些有趣的事情。我们可以使用git-bisect.sh 过滤器驱动程序,而不是合并驱动器来实现。在关键情况下,我们不希望使用合并驱动程序。将文件提取到工作树中时,总是使用过滤器驱动程序。


1 如果您仔细阅读了所链接问题的答案并指出发生了什么情况,您将发现Git有可能使有效。通过具有另一个按文件属性,例如.gitattributes。但是Git至少今天没有这个。


过滤器驱动程序

过滤器驱动程序有两种形式,Git称为污迹过滤器清洁过滤器。它们在工作树(您的文件具有对您和计算机有用的格式)和 index (Git在其中存储文件名和文件名)之间的接口上运行。该文件的压缩的随时可用快照的哈希ID(始终为 next 提交做好准备,但最初与 current 提交相同)。

污迹过滤器的目的是获取已解压缩但尚不可用的文件文本,并将其转换为可使用的工作树形式。 干净过滤器的目的是采用文件的工作树形式,并删除所有特定于工作树的数据,以便该文件准备好压缩为仅Git的内部形式。 always-merge命令以及其他一些可以冻结仅Git的对象的命令也使用污迹过滤器。 git checkout命令使用干净过滤器清除污物过滤器放入的“脏东西”。

因此,现在我们可以看到如何使某个文件的工作树副本取决于当前分支:我们只需编写一个执行此操作的涂抹过滤器即可。我们可能还应该编写一个干净的过滤器,以除去特定于分支的内容,这将使Git更好地压缩文件,但我将留给您。

要定义污迹过滤器,我们需要在某些git add.gitconfig配置文件中添加一个条目。例如,如果我们想通过一些漂亮的过滤器来运行源代码:

.git/config

(假设用于漂亮打印源文件的命令是[filter "pretty-printer-for-XYZ-language"] smudge = xyz prettyprint --stdin ,并且需要xyz prettyprint才能从标准输入中读取)。然后,我们通过--stdin告诉Git,将此过滤器应用于.gitattributes文件:

*.xyz

过滤器只需要读stdin和写stdout:Git安排过滤器的stdin来自出现在blob对象中的未压缩但“干净”的文件内容,过滤器的stdout进入将要存储的临时文件,在此过程结束时,在工作树中成为适当的文件。

例如,如果树对象中的*.xyz filter=pretty-printer-for-XYZ-language 具有一些blob哈希,Git将读取blob,将内容写入过滤器的stdin,读取过滤器的stdout,然后写入那些 somefile.xyz中的内容。不过,这里有一些重要的事情要实现:

  1. 过滤器无法直接访问名称somefile.xyz。您可以通过somefile.xyz指令告诉Git以 produce 的名称作为参数,但是请记住,过滤器必须仍然读取stdin并写入stdout。 (如果为了提高效率将过滤器重写为“长期运行的过滤器过程”,则过滤器必须遵守the documentation中所述的数据包协议,该协议还提供了文件的路径名。)
  2. 污物过滤器在之前 %f更新git checkout运行。与第1点一样,污物过滤器无法直接访问发生的事情:它们例如,不知道Git位于HEAD的中间。

此处的第2点以粗体显示,因为它是此处最大的绊脚石。可以使用当前进程树来找到调用过滤器的Git命令,并使用现有的任何OS设施来查找命令行参数。如果Git在启动此类过滤器之前在 之前设置了一个环境变量,说明正在发生的事情,这将非常有帮助:此过滤器是否代表切换到新分支操作运行,还是由于例如git checkout otherbranchgit checkout -- path/to/file索引提取而运行?但是,a,Git也不会这样做。