代码之家  ›  专栏  ›  技术社区  ›  Archit Arora

避免在git合并期间覆盖文件

git
  •  0
  • Archit Arora  · 技术社区  · 6 年前

    考虑下面的场景。我有三个分支- Master Develop Test . 所有三个分支都有一个配置文件(例如 JenkinsFile )包含特定于分支的配置的。请注意,此文件中的配置对于所有三个分支都是不同的。现在,我创建一个特征分支 主人 ,进行一些更改并将此功能分支与合并 发展 然后是 测验

    问题是-我如何防止 詹金斯档案 保持完整,不受任何合并的影响。有没有办法“锁定”这些文件?做 gitignore

    干杯

    2 回复  |  直到 6 年前
        1
  •  1
  •   tmaj    6 年前

    在分支之间保持一个不同的文件是非常困难的,尤其是一个经常变化的文件。

    更好的解决方案是让settings.environment.json文件包含不同环境的设置,并使您的软件根据运行位置使用不同的设置文件。

    话虽如此,最好不要将生产设置保存在git中。部署管道应该包含密码等,而不是您的版本控制系统。在这种情况下,所有分支中的设置文件都包含DEV设置(可以公开),管道在准备将包部署到目标环境时使用TEST和PROD值覆盖这些设置。

        2
  •  1
  •   torek    6 年前

    问题是-如何防止Jenkins文件被任何合并覆盖?我希望Jenkins文件保持完整,不受任何合并的影响。有没有办法“锁定”这些文件?

    但是,有一种完全不同的方法来解决这个问题,它回避了整个问题。事实上,有多种方法,但我只展示一种。在获取信息方面存在着一个不幸的问题 有一个名为 Jenkinsfile (或 JenkinsFile ,但我使用了小写字母-F拼写,其内容依赖于分支。相反,只要有一个 未承诺的 Jenkins[Ff]ile 其内容依赖于分支。使 坚信的 文件有其他名称。

    出身背景

    从根本上讲 git merge 合并已完成的工作 百货商店 变化;Git商店 快照 合并分支 提交图 作品

    父母亲 commit,它是commit的直接前身。大多数都有 确切地 父级,因为它不能有一个,因为它是第一次提交。(如果有三个或三个以上的父母,则称为 但它们并没有做常规合并所不能做的事情,所以它们主要是为了炫耀。:-)

    在这些链接中,提交存储其父级的散列ID。请记住,每个提交都是通过其唯一的散列ID找到的,该散列ID是在您使提交表单向后链时分配给提交的Git。这些反链 最后的 承诺我们希望成为该分支机构的一部分:

    ... <-F <-G <-H   <--master
    

    在这里,我没有使用实际的散列ID,而是使用单个大写字母表示每个提交。名字 master 保存提交的实际哈希ID H . 我们说 主人 H . G 所以 H 指向 ,它指向 F ,依此类推,向后走。

    任何提交中的任何内容都无法更改,因此我们不需要内部箭头,我们只需要记住它们是反向的。在Git中,实际上很难向前推进:几乎所有的操作都从末尾开始,然后向后进行。一旦我们有了多个分支,就会出现这样的画面:

              G--H   <-- master
             /
    ...--E--F
             \
              I--J   <-- develop
                  \
                   K   <-- test
    

    git checkout 分支意味着*从该分支的提示提交中提取快照 . So 切换到主分支 extracts the snapshot from commit , while git签出开发 or git校验测试 extracts those snapshots in turn. Also, doing a git签出 of some branch name attaches the special name 去那家分店。Git就是这样知道的 肱支气管炎

    当你跑的时候 合并分支 ,您将给Git另一个提交的名称。这不一定是一个分支名称–提交的任何名称都可以–但是给它一个分支名称就可以了,因为它命名了该分支的提示提交。所以如果你 git checkout master 然后跑 git merge develop ,您可以从以下内容开始:

              G--H   <-- master (HEAD)
             /
    ...--E--F
             \
              I--J   <-- develop
                  \
                   K   <-- test
    

    Git找到了提交 J 和命名提交 J 合并基 在这两项犯罪中。

    不严格地说,合并基础是我们从两个技巧得到的第一个提交。这是一个正在进行的承诺 二者都 . 合并基础的概念对于理解合并是如何工作的至关重要。 因为合并的目标是 联合作业 ,通过比较提交中的快照可以找到该工作 F ,一次一个比较,对两个tip提交中的每一个进行比较 H J :

    git diff --find-renames <hash-of-F> <hash-of-H>    # what we changed
    git diff --find-renames <hash-of-F> <hash-of-J>    # what they changed
    

    为了组合这些更改,Git从中的所有文件开始 ,并查看我们更改了哪些文件以及它们更改了哪些文件。如果我们都改变了 不同的 文件,Git根据需要选择我们的或他们的。如果我们都改变了 相同的 文件 this eventually brings up a philosophical problem 稍后我们将回到这一点Git试图将我们的更改与他们的更改一起粉碎,假设如果我们接触到一些源代码行,而他们没有,那么应该是我们的,如果他们接触到一些源代码行,而我们没有,那么也应该是他们的。如果我们两个都碰了 台词 相同的 那些线在哪种情况下,Git 一份

    如果没有冲突,Git会将这些组合更改应用到合并库中的快照–中 F ,然后使用生成的文件写出新快照。该新快照是类型为的提交 合并提交 H ,因此合并如下所示:

              G--H
             /    \
    ...--E--F      L   <-- master (HEAD)
             \    /
              I--J   <-- develop
                  \
                   K   <-- test
    

    主人 (致 HEAD 附页),移动; 主人 现在指向Git刚刚进行的新合并提交。

    如果合并失败,由于合并冲突,Git将留下一片混乱。这个 ,将包含所有冲突的输入文件,工作树将包含Git的合并尝试以及冲突标记。您的工作是清理混乱,修复索引,并完成合并(使用 git merge --continue git commit --continue 就跑 commit

    詹金斯档案

    假设在提交时 F 詹金斯档案 . 此同名文件出现在提交中 J H J 不同于 F ,也许两者都不同于 F .

    Git将假定名为 在两个分支提示中都是 相同的 在里面 F . 显然,它不是完全相同的文件–内容不同–但Git会 假定 是的,而且你正试图结合在这方面所做的工作。

    因此,Git将区分 在里面 F H J . 会有 变化。如果两个分支提示都有更改,Git将合并它们(或声明冲突)。结果: 令人不快的 . 否则,Git将从更改文件的任何一方获取该文件的版本。那是你想要的那一面吗?若有,结果为: 好的 令人不快的

    总之 ,对于这种情况,有三种可能的结果:

    • 底部和头部是唯一的变化:结果很好。
    • 唯一的变化是基础对他们的:结果很糟糕。
    • 基地对头部和基地对他们都有变化:结果可能不好。

    当然,合并基提交也是可能的 F 文件名为 . 而且,有可能一个或两个 犯罪

    解决方案(以及一些问题)

    这里的解决方案是避免使用单一的固定名称文件,例如 詹金斯档案 ,在所有提交中,当该文件要依赖于分支时。相反,假设是 F 包含 Jenkinsfile.master Jenkinsfile.develop Jenkinsfile.test . 然后提交 H 将有一个 Jenkinsfile.master Jenkinsfile.develope Jenkinsfile.test 还有 变化 从…起 在里面 Jenkinsfile.master 将是你想要保留的。自认 J 它在树枝上 develop ,它应该始终具有 相同的 主人 在某个时刻 . 因此,Git的合并在这两种情况下都是正确的。

    同样的逻辑也适用于其他每个这样的文件。请注意,此时,由所有分支提示标识的提交应该具有 文件名为 (没有后缀)完全没有。当然,这是一个理想化的目标状态:要达到目标,您必须在每个分支中进行新的提交,重命名现有的分支 詹金斯档案 . 但这将有 毫无效果 无论如何 承诺。存储库中的所有历史记录都将一直冻结。这意味着在某个时候,你会跑 合并分支 合并分支 詹金斯档案 Jenkinsfile.master Jenkinsfile.develope 或任何其他后缀。

    现在我们假设 ,您已完成此重命名,但在合并库中 F 因为这是一个历史性的承诺,所以你已经臭名昭著了。所以 F 有一个 詹金斯档案 H J 詹金斯档案 但确实有重命名的文件。

    现在,还记得上面我们展示的 git diff 那是什么 合并分支 --find-renames . 这会将Git引导到 猜测 文件是否 詹金斯档案 在里面 F 是“相同”的文件吗 Jenkinsfile.master 在里面 ,进行比较时 F H . 这同样适用于比较 F J 字体是旧的吗 与新文件相同的文件 Jenkinsfile.develope ?

    如果您按照链接到 https://en.wikipedia.org/wiki/Ship_of_Theseus 你会发现,随着时间的推移,对于同一性的问题,没有哲学上正确的答案。但Git有 它的 如果文件的相似性索引为50%或更高,则为同一文件。 在这里,我们不需要担心Git如何计算这个相似性指数(有点复杂);Git很有可能在这两种情况下都检测到重命名。

    这在实践中意味着什么 那是我的名字吗 合并分支 ,Git将立即声明一个合并冲突,类型为我想调用的类型 冲突也就是说,Git会这么说 詹金斯档案 年更名为 二者都 分支,但有两个不同的名称。Git不知道是使用主版本还是开发版本,或者两者都使用,或者两者都不使用,或者什么都不使用。它将以合并冲突停止。 这没关系 因为它为您提供了解决冲突的机会,您应该通过选择 文件中显示的文件 主人 --ours 树枝 选择 Jenkinsfile.develope 文件中显示的文件 发展 --theirs 分支,作为合并结果。删除原始名称时,将这两个文件放入索引中:

    git rm --cached Jenkinsfile
    git checkout --ours Jenkinsfile.master
    git checkout --theirs Jenkinsfile.develop
    git add Jenkinsfile.master Jenkinsfile.develop
    

    现在,您已通过选择保持两个文件在两个分支提示中的显示状态来解决冲突。现在可以提交结果了。

    每次进行合并时,都会使用一个历史的、单一的- 提交时,您需要检查合并结果是否正确,或解决任何冲突。(如果不正确,在合并后,可以立即将其修复到位并使用 git commit --amend 将原始合并推到一边并选择新结果作为合并提交。如果你没有注意到一个糟糕的合并,那会有点痛苦,但最终的恢复是相似的。还记得Git是如何进行合并的吗 差异比较 s、 看看如何把正确的结果 任何 提示提交可以让您到达需要去的地方。)

    最后,现在没有了

    现在没有名为 詹金斯档案 ,您必须重定向任何希望使用此类文件的软件。有多种解决方案(取决于软件和操作系统),包括从 詹金斯档案