首页 > 解决方案 > Git 策略以最小化多个 repos 的合并冲突

问题描述

外部软件供应商将产品更新作为 zip 文件的完整快照提供。在这种情况下,它是external-base.zip。此外,它还提供几乎相同的其他 zip 文件,但都是软件的本地化版本。


问题

问题是我需要对这个软件进行一些修改,同时尽量减少手动合并冲突解决。

  1. 为此,我首先创建 git repositories ext-baseextLC_ENextLC_GE并在每次收到新的 zip 文件时提交给它们。

  2. 接下来,我将里面的任何东西推extBaseintBase. 当我创建修改时,我将它们添加到intBase. 每次我从 推 extBaseintBase,我都可能冒着丢失修改的风险。

  3. 因为我希望我的修改也能在本地化版本中结束,所以我从和推intBase入。intLC_ENintLC_GE

=> 所以我必须解决intBase, intLC_EN, 和intLC_GE每次这 3 个新更新的 zip 文件到达时的冲突,而这些冲突的解决方案始终是相似的。


注意事项

  1. 基本版和 LC 版具有几乎相同的结构。
  2. git rerere在这里有什么帮助吗?


问题

你能推荐我什么策略来最小化工作?

存储库的描述

标签: git

解决方案


你能推荐我什么策略来最小化工作?

将您的供应商快照导入单个历史 dag,并使用您选择的分支结构(由于供应商不提供祖先,您可以自由支配,这完全是为了您的方便)。然后将你自己的工作作为这些分支来维护。

git rerere在这里有什么帮助吗?

git rerere为此而设计的,以便在从……任何地方合并历史时减轻应用一组更改的任务。


对于您要构建的供应商历史记录的具体示例,让我们使用它:

  a---b---c-------e    LC_EN  (let's say you somehow didn't import the fourth one)
 /   /   /       /
A---B---C---D---E      external-base perhaps aka master
 \   \   \   \   \
  α---β---γ---δ---ε    LC_GE

带有A,BC的快照标记了他们的供应商修订号。众所周知,Git 并不关心你如何做你的工作,重点是去做。

这看起来很适合您描述的设置。您的更改是一个并行的分支结构,A'您的更改是在外部A基础上α'进行的,您的A'工作是应用到α基础上的,等等。(很抱歉使用希腊语而不是格鲁吉亚字母,这是我所知道的)。

假设您D在现有的存储库中最多,并且您拥有一E.zips。


工作一是构建供应商基础历史。工作二是构建你的修补历史。工作三是让新供应商发布的舞蹈冷下来。

要理解的是,如果你不明确地告诉它,Git 会找到一个 repo,以及它的工作树、索引和对象数据库,但你可以直接告诉它。假设您有一个提取的供应商下载,它看起来与内容快照完全一样(因为它是一个),所以告诉 Git 这就是您要从中添加的工作树。

这是一个非常直接的方法。让我们从您现有的构建历史开始,假设您D到目前为止并没有记录任何d快照,嘿,它发生了。


编辑:我很高兴充实剩下的内容,但是(a)我今天有事要做,git 核心命令让一些人感到反感,以及(b)只是第一步的细节可能足以让你走上正确的道路。询问任何令人困惑或根本不够清楚的细节,我会做更多的工作。


工作一:从现有存储库中的快照构建所需的供应商基础历史记录。为了让事情变得简单,同时仍然提供一些 Git 的 git-r-dun 风格的导览,让我们构建一个新的统一供应商历史存储库。

git init /path/to/new-vendor-history      # make the unified-vendor-history repo
cd !$                                     # switch to it
mkdir -p .git/objects/info                # set it up to leech off the older histories
printf %s\\n >.git/objects/info/alternates \
   /path/to/old/{extBase,extLC-EN,extLC_GE}/.git/objects

这比为所有现有存储库添加遥控器并获取它们的历史记录更快、更便宜,你很快就会摆脱旧历史的奶嘴,但现在,告诉 Git 嘿,我在这些地方有一堆对象现在正在使用。

由于您正在逐字获取 extBase 存储库的历史记录,因此请接受它。您已经告诉 git 在哪里可以找到对象,将当前分支(尚未指定的 local master)指向该历史记录的最快方法是

git reset -q $(git -C /path/to/old/extBase rev-parse master)  \
                                          # set my master branch to current extBase tip

现在,因为对于这个例子,你D在你现有的回购中,你new-vendor-history master看起来像

A---B---C---D

因为这就是您刚刚将其重置为的内容。

因此,要完成第一项工作,是时候从现有快照构建供应商本地化分支了。使用现有快照和单个父级开始分支(我​​使用上面绘制的提交图中的字母,实际哈希 id 中的 sub 或任何其他 ref git 可以解析为此处的字母)

git branch LC_EN A

b然后添加树的合并

git update-ref refs/heads/LC_EN $(
        git commit-tree -p LC_EN -p B -m 'your merge message here' b:
        )

依此类推。 b这是您的存储库中现有的相应提交extLC_EN,生成的提交,您的存储库中的生成提交new-vendor-base是我在上面绘制的那个b

LC_GE分支再次执行此操作,您就完成了对已经混帐的供应商快照的重组。现在您可以重新打包和剪断领带:

git repack -ad
rm .git/objects/info/alternates

完成将供应商快照转换为新系统剩下要做的唯一事情是从下载E.zips 集中添加新快照。

( cd ~/down; unzip external-base.zip )        # unpack the new snapshot
git --work-tree ~/down/external-base add .    # add to repo and update index from there
git commit                                    # commit to the current tip

( cd ~/down; unzip external-LC_EN.zip )       # unpack the new snapshot
git --work-tree ~/down/external-LC_EN add .   # add to repo and update index from there
git update-ref refs/heads/LC_EN $(            # commit to a different tip
        git commit-tree -p LC_EN -p master -m 'vendor LC_EN' `git write-tree`
        )

( cd ~/down; unzip external-LC_GE.zip )       # unpack the new snapshot
git --work-tree ~/down/external-LC_GE add .   # add to repo and update index from there
git update-ref refs/heads/LC_GE $(            # commit to a different tip
        git commit-tree -p LC_GE -p master -m 'vendor LC_GE' `git write-tree`
        )

(您可能想编写一个小脚本来分解样板文件并将供应商版本分到提交消息中,如果您愿意仔细通过三层引用,甚至可能是 repo-local git 别名雷区,特别是因为这是你的新供应商发布舞会的第一步)。


这就是第一个工作,繁重的工作,将现有结构转换为单一统一供应商历史的工作。


工作二也使您的补丁与新系统同步。

如果您愿意,您可以在同一个存储库中携带您的补丁。我会这样做,仅当 (a) 我对多个独立的并发工作树有一些具体用途或 (b) 我有那个 uh-oh-here's-my-chance-to- 时,我才会制作单独的 repos真的-screw-up-the-refs-namespace 的感觉,我想要一个克隆,如果事情向南,我可以放弃。但在这里你将拥有

rerere因此,让我们通过展示如何以intXYZ类似方式导入现有历史来积累您的目录。

git config rerere.enabled true                # light auto-rerere
printf %s\\n >.git/objects/info/alternates \
    /path/to/old/int{Base,LC_EN,LC_GE}/.git/objects

现在:rerere 的工作原理是注意到任何新的冲突或解决方案,并解决工作树中的任何旧冲突或解决方案,并在其中运行它的索引。点亮 auto-rerere 仅意味着git rerere每当合并因冲突而停止时运行,并且每当您提交冲突的正确结果时运行合并,你可以在有用的时候自己运行它。


推荐阅读