【问题标题】:git smudge/clean filter between branches分支之间的 git 涂抹/清洁过滤器
【发布时间】:2014-05-19 12:44:30
【问题描述】:

有许多涉及涂抹/清洁过滤器的相关问题 - 我花了几个小时阅读它们,并尝试了各种选项,但仍然失败。我希望我能以一种我能得到适合我的答案的方式提问。

具体来说,我已经阅读了大部分答案链接到的页面:


tl;博士

这是一个详细的问题,但总结是:

  • 我可以将DEBUG = false 存储在一个分支的文件中,并将DEBUG = true 存储在另一个分支中,使用涂抹/清洁过滤器来管理该文件吗?怎么做?

背景

我在 bitbucket 上托管了各种远程存储库。我在 Win8 上使用 SourceTree,将远程存储库克隆到我的笔记本电脑。我为开发、功能、发布等创建了不同的分支(遵循A successful Git branching model 是好是坏)。

我有一个名为 Dbug.java 的 Android java 类,其中包含一个布尔值,可以在我的代码中打开/关闭各种调试日志记录、模拟等功能。

public static final boolean DEBUG = false;

我希望这个值在我的“生产”(主)分支上是 false,在我的功能分支上是 true

  • 这是否可以使用过滤器,还是我已经误解了用例?
  • 我不确定过滤器是否在同一个本地托管存储库的 2 个分支之间像这样工作,或者过滤器是否仅在 2 个存储库之间工作。

创建过滤器

在本地工作,我检查了生产分支。我创建了一个名为debug_flag.txt 的测试文件,其内容如下:

// false on production branch
// true on other branches
DEBUG = false;

我在本地 repo 的根目录中创建了一个名为 .gitattributes 的文件,并为其添加了过滤器引用:

debug_flag.txt filter=debug_on_off

我用过滤器定义更新了.git/config 文件:

[filter "debug_on_off"]
    clean = sed -e 's/DEBUG = true/DEBUG = false/'
    smudge = sed -s 's/DEBUG = false/DEBUG = true/'
  • 据我了解,这应确保我的文件始终具有 生产中的错误值,但当我从 生产。
  • 这是正确的理解吗?

测试过滤器

我创建了一个新分支 test 使用:

git checkout -b test

我检查了文件的内容:

$ cat debug_flag.txt

// false on production branch
// true on other branches
DEBUG = false;
  • 我希望在文件中看到值true
  • 当我签出文件时,“污迹”过滤器是否应该运行?

我在文件中添加了一个新行,并提交了。然后我切换回生产分支,这就是事情变得奇怪的地方。

如果我查看 SourceTree 中的文件,该分支自创建以来没有任何更改。这是我所期望的,因为唯一的更改是在不同的分支上进行的。

如果我在终端或 Notepad++ 中查看文件,我发现我的值已更改:

$ cat debug_flag.txt

// false on production branch
// true on other branches
DEBUG = true;

我还没有合并测试分支对面的更改,我还没有在生产分支上提交,但是文件已经改变了

  • 看起来污迹过滤器是在此分支内的文件上运行的,但不是跨分支。

我错过了一个重要的难题,希望它是一些简单的东西,可以被有经验的人发现。

我敢打赌,这是对这个概念的简单误解。

请提示任何缺失的信息...


根据 VonC 的回复更新

设置基本过滤器效果很好。将config文件中的过滤器定义为:

[filter "debug_on_off"]
    clean = sed -e 's/DEBUG = true/DEBUG = false/'
    smudge = sed -s 's/DEBUG = false/DEBUG = true/'

创建新分支修复 false -> true,合并更改 true -> false。

将更改限制在生产(主)分支需要自定义脚本,这些脚本知道它们正在从哪个分支运行。所以config文件变成了:

[filter "debug_on_off"]
    clean = ./scripts/master_clean.sh
    smudge = ./scripts/master_smudge.sh

master_clean.sh:

#!/bin/sh
branch=$(git rev-parse --symbolic --abbrev-ref HEAD)
if [ "master" = "$branch" ]; then
    sed -e s/DEBUG = true/DEBUG = false/ $1
else
    cat $1
fi

master_smudge.sh:

#!/bin/sh
branch=$(git rev-parse --symbolic --abbrev-ref HEAD)
if [ "master" = "$branch" ]; then
    sed -e s/DEBUG = false/DEBUG = true/ $1
else
    cat $1
fi

此时,我遇到了 SourceTree 看到的内容与 Notepad++ 中显示的调试文件内容不一致的问题。 SourceTree 显示了更改,但 Notepad++ 没有。

我接受VonC's answer,因为它回答了我提出的基本问题。

但是,我可能会实施solution I wrote,因为它以一种更简单的方式(对我而言)解决了我试图解决的基本问题:在不同的分支上保留不同的配置文件。

【问题讨论】:

  • 您可能需要在 $1 周围添加引号以支持带有空格的文件。
  • 你不是在配置文件中缺少 %f 吗? (不确定是否带引号,以防万一您需要转义它们,因为 git 本身在解析配置文件 AFAIK 时也会解释它们)
  • @phk 一年多前我就放弃了。我现在手动进行(当然这很痛苦)。但是,如果您有时间并认为您有一个正常工作的解决方案,请随时发布。根据我的发现,git 不支持此功能,我认为这是“设计使然”。
  • 我当时也放弃了,Linus 的名言让我在构建脚本中做这些事情。

标签: git gitattributes


【解决方案1】:

我希望在文件中看到值 true

你刚刚创建了一个新分支,没有检查它的内容(因为它的内容和你所在的分支相同)

要强制 smudge 运行,请在 repo 的顶部执行:

git checkout HEAD --

我还没有合并测试分支对面的更改,我还没有在生产分支上提交,但是文件已经改变了。

这就是内容过滤器驱动程序的想法:它修改内容,而不影响git status(仍将修改后的文件报告为“未更改”)。

要让每个分支的污迹表现不同,我建议调用一个脚本,该脚本首先查看当前分支的名称。
请参阅我的旧答案“Best practice - Git + Build automation - Keeping configs separate”中的示例。

#!/bin/sh
branch=$(git rev-parse --symbolic --abbrev-ref HEAD)

【讨论】:

  • 谢谢,我以为到目前为止我已经看到了你关于这个话题的所有答案,但我错过了这个。所以过滤器与分支无关?我想这是我最大的误解。是不是工作副本总是被弄脏了,而仓库内的副本(不是工作副本)总是干净的?但是我们没有建立“干净”版本,因此链接到另一个答案......?
  • @RichardLeMesurier 默认情况下,smudge 脚本不知道分支。如果.gitattributes 在一个分支中声明,它可能与分支相关,但在另一个分支中没有.gitattributes。但是如果在两个分支中都声明了它,那么您需要在您的smudge 脚本中添加一个检测步骤。
  • 不幸的是,我无法解决 2 个不同的 .gitattributes 文件 - 鸡和蛋的情况;我也无法在.gitattributes 上运行 SED 过滤器。我将继续尝试使 smudge 分支相关。
  • @RichardLeMesurier 的目标是拥有两个不同的.gitattributes 文件。它是让 一个 涂抹脚本足够智能,可以根据其执行环境(如当前分支)做正确的事情。
  • 接下来我会继续努力。只是解决您的评论:“如果 .gitattributes 在一个分支中声明,则可能与分支相关,但另一个分支中没有 .gitattributes。”也许这是可能的,但不是我,现在。
【解决方案2】:

VonC's advice 解决了我提出的确切问题,但我无法确定最终的细节(根据我对问题的更新)。这个答案详细说明了我是如何做事的。


更新

以下方法适用于第一次合并。但在那之后它不再工作了。我把它留在这里,因为它代表了我目前的调查状态。

似乎不再调用合并驱动程序。

还尝试使用 exit 0touch %A 或自定义脚本合并驱动程序 (https://stackoverflow.com/a/930495/383414) 而非 true 对相关问题进行各种修改,如下所示。


我找到了一个解决方法,它使用自定义merge 策略来解决根本问题,即:

  • 我希望我的构建分支中的构建文件始终设置为关闭所有调试值。
  • 这可以防止任何带有模拟设置、本地主机设置、日志记录打开等的产品意外发布。

我根据这个问题的信息得出以下结论:.gitattributes & individual merge strategy for a file

1) 在.git/config文件中定义自定义合并驱动如下:

[merge "ours"]
    name = "Keep ours merge"
    driver = true

我不确定是否需要此步骤 - 但它似乎可以解决某些(较旧的?)系统上的错误。

(详情:https://stackoverflow.com/a/13000959/383414

2) 在 build/production/pristine 分支中设置.gitattributes 文件,以便特殊调试标志使用上述合并策略。

所以使用我的问题中的文件,转到“生产”分支,然后将以下行添加到 .gitattributes 文件中:

debug_flag.txt merge=ours

任何时候合并回到“生产”分支,git 都会寻找定义为“我们的”的合并策略,并防止 debug_flag.txt 被覆盖。

3) 在其他分支上,设置您的 .gitattributes 文件,而不使用该自定义合并策略。

4) 配置过程的最后(但重要)步骤是在所有分支中正确设置 debug_flag.txt 文件,并将更改提交到每个分支。

您现在应该有 2 个分支,每个分支都包含不同版本的 .gitattributes 和 debug_flag.txt 文件。这样可以确保每次合并时都会出现冲突。

如果没有冲突,则不会调用自定义的“我们的”合并策略,并且文件可能会被覆盖。

(详情:https://stackoverflow.com/a/16739788/383414

5) 最后将您的新分支合并回“生产”。由于步骤 3 和 4,您将有合并冲突。解决冲突,以便 2 个分支保持它们的差异。提交更改。

这两个分支之间的所有未来合并都将无缝忽略 debug_flag.txt 文件。


这实现了在不同分支上拥有 2 个不同配置文件的目标,这样您就可以轻松地将调试与生产代码等分开。这似乎是一个常见的用例,在这个论坛上有很多相关的问题,但它仍然需要我需要几天时间才能把它弄好。

【讨论】:

  • 合并驱动程序的有趣使用,比我的回答更精确。 +1
  • @VonC - 谢谢,但感谢我联系的其他人。但我确实想以一种易于理解的方式记录它。正忙于根据您的回答制定解决方案。到目前为止看起来不错。
  • 这项技术今天早些时候在我身上出现了故障 - 必须进行更多测试,看看是我做了什么,还是我的回答有误。
【解决方案3】:

看看expandr。这是一个脚本,可让您使用 smudge/clean 在不同的分支上设置不同的配置。基本上就是你最初要求的。

目前最大的问题是在切换分支后,有时我需要执行git checkout HEAD -- "$(git rev-parse --show-toplevel)" 才能正确涂抹工作目录。然而,其他时候,它似乎工作得很好。我还没弄清楚为什么。我认为这可能与打开“合并重新规范化”有关,导致一些问题?我不知道。 (我已经戴上了。)

另一个问题是您必须使用 merge=ours 保护每个分支的 .gitattributes 文件本身,方法是在其中添加一行 .gitattributes merge=ours,当然还要为此打开驱动程序(正如您已经提到的)。这里的gotcha是,在创建每个单独的分支之后,现在您必须进入每个 .gitattributes 文件并修改每个文件(我建议现在添加注释,如#touched for merge=ours on master#touched for merge=ours on test branch 等。 ,所以你会记得它为什么在那里)。您必须这样做,因为merge=ours 只会保护文件不会在合并中更改为传入分支的版本,前提是该文件在传入分支及其父分支上创建分支后已更改。 (记住 git 处理更改,而不是文件。)

【讨论】:

  • 看起来不错 - 当我有时间尝试时,已将其指定为注意事项。您的额外结帐命令可能是解决我遇到问题的最终解决方案。
【解决方案4】:

最直接的解决方案是更新您的 makefile 以检查当前签出的分支。如果分支是命名列表的一部分,则定义一个新的构建参数 -DDEBUG=true 或 -DDEBUG=false 否则。

How to programmatically determine the current checked out Git branch

branch_name=$(git symbolic-ref -q HEAD)
branch_name=${branch_name##refs/heads/}
branch_name=${branch_name:-HEAD}

PROD_BRANCHES := master \
                QA
debug_flag=
ifneq ($(filter $(branch_name),$(PROD_BRANCHES)),)
    debug_flag="-DDEBUG=true"
endif
ifeq($debug_flag,)
    debug_flag="-DDEBUG=false"
endif

【讨论】:

    【解决方案5】:

    我有一个类似的用例,并且能够使用过滤器和结帐后挂钩来解决它。这个钩子很好,因为它为你做了干净+涂抹,并在通过 GitHub Desktop 切换分支时立即更新文件。使用这种方法,每个沙箱/层在每个分支都有自己的配置。

    详细文章地址:https://c1.eagen.net/git-smudge-clean.html

    示例存储库位于:https://github.com/vinnyjames/git-filter-demo.git

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-20
      • 1970-01-01
      • 2016-12-08
      相关资源
      最近更新 更多