将第三方源代码与git中的本地修改合并的最佳方法

时间:2017-12-27 20:53:46

标签: git

我们公司购买了第三方图书馆开发人员的源代码。我们每次发布新版本时都会获得其源代码的副本。但是,我们还为他们的源代码制作了一些mod,这些代码不必上游到他们的代码库。

我认为我们应该能够创建一个只处理其源代码的git存储库。然后,我们可以使用我们修改过的源代码然后执行一种rebase以使其保持同步。

我看到的一个问题是,我认为变基是一种在分支上发生的操作,将分支点从它所在的位置移动到它所在的分支上。然后会指出任何合并冲突。但是,我不确定如何做到这一点,这将使我们的存储库成为他们的主要分支。

我的想法是为他们的源代码创建一个单独的存储库(让我们称之为3rd-party),我们将根据修改进行分支,并将其源代码作为树干。然后,我们可以将我们的分支机构从该主干上移除。然后,在我们的主存储库中,我们将链接到3rd-party的分支。最后一点是,当我说 Abra-kadabra时,我的双手都是波浪状的!

我建议的解决方案是什么?或者还有其他方法可以做我要求的事情吗?

如果我没有正确使用这些条款,请原谅我。我们上个月才转向git,我还在学习。

修改

为了清楚起见,我们的源代码已经包含了我们的更改。所以,我甚至不确定引入旧源代码的最佳方法是什么,我们已成功合并到哪里,以便我们有一个共同的基础。我们的代码是主干,主干是我们的开发分支。我们已经以这种方式设置了我们的系统,并且已经设置了一些假定此设置的工具。我们还拥有不止一个图书馆。

我们当前的"流程",已从TFS迁移到

  1. 删除所有已删除的文件(我想我可以删除所有文件,只复制相关的 .vcxproj .vcxproj.filters 文件)
  2. 添加新的资源/资源/其他文件
  3. 对更改和樱桃选择进行差异处理(手动将代码复制并粘贴到新代码中)我们已经添加到新代码库中。
  4. 验证更改是否破坏了事情并修复了已损坏的任何内容。
  5. 签入更改。
  6. 当然,第3步是最成问题的,我希望这样做会简化事情,因为会有共同的祖先。

    我已经编写了一些脚本,这些脚本会自动暂存不会对其进行任何更改的文件,以尝试简化此过程。

    第二个想法

    由于变基似乎使事情变得复杂,或许我可以创建一个只包含3rd-party库的存储库。我会根据@Mark Adelsberger的回答使用该存储库进行合并。然后我可以将该存储库的HEAD复制到我们的主存储库中。

    这意味着它们将是两个独立且不同的存储库,它们之间没有联系,但可能是最好的方法。

1 个答案:

答案 0 :(得分:1)

根据评论中的讨论,

更新。不过,在我进入项目结构之前,还有一些关于项目结构的新注释。

如果您在第一个供应商源代码丢弃之前提交,那么除了供应商代码的修改版本之外,这至少表明您拥有自己的重要代码文件的可能性。这不是我从原始问题中汲取的东西。

如果是这种情况,您可能希望将供应商代码放在特定的子目录(./vendor/)中,这不会反映在我最初提供的脚本中。 (如果没有,您将如何避免他们在您用于其中一个文件的路径/文件名中添加新文件的可能性?)

所以无论如何,无论我在哪里使用命令

cp -R /path/to/latest/source/drop/* .

我的假设是代码布局正确"在/path/to/latest/source/drop

原始回答

每次你修改(或以任何方式重写)分支时,每个已经拥有该分支副本的仓库都需要进行清理。由于我希望您的所有开发团队都拥有包含您对此源代码的本地修改的分支的克隆,因此我不建议将变基作为工作流程的常规部分。

相反,只需合并。

rebase的销售推销是它产生一个干净,线性的历史。虽然它通常有成本,但它有时很有用。但在这种情况下,它根本不合理。您无法合理地生成包含您的更改和其他组织的更改的单个线性历史记录,其中他们不会合并您持续进行的某些更改,而您 获得他们持续进行的更改。 (哎呀,你甚至无法控制他们的变化是否会以线性历史开头。)

更重要的是,你要花费大量不必要的努力去做,即使条件恰到好处并且你以某种方式实现它。

我要做的是:使用适用于您团队的任何分支策略来维护您的代码。添加到"供应商"科。您的修改永远不会影响供应商分支,但您将从供应商分支合并到您的开发分支(详细信息可能会根据您的确切分支策略而有所不同)。

例如,假设您有一个dev分支,可以为即将发布的版本累积您的工作。您首先导入供应商代码,创建提交V1。然后从中分支以创建dev分支并开始进行更改。

V1 <--(vendor)
  \
   A -- B -- C <--(dev)

现在有一段时间了,你又从供应商处获得了另一个来源。查看vendor分支,然后使用新的源代码删除工作版本。

(以下假设vendor分支工作树没有理由包含供应商提供的文件以外的任何文件。在实践中,您可能会发现一些内容,例如.gitignore或.gitattributes文件,是有用的;并且在任何情况下你都需要确保.git目录没有被删除。所以你可能需要一个更小的&#34;脚本代替{{1命令我在这里显示...)

rm

产生

git checkout vendor
rm -rf *
cp -R /path/to/latest/source/drop/* .
git add .
git commit -m "20xx-xx-xx source drop from $vendor"

现在您可以从V1 -- V2 <--(vendor) \ A -- B -- C <--(dev) 合并到vendor

dev

要计算合并,git会发现git checkout dev git merge vendor 是合并基础,并将确定自V1以来vendor上的更改内容。所以你可能会遇到冲突,事实上这可能会变得很难看,因为你可能无法阻止供应商随心所欲地进行疯狂的重构,但它会像其他任何方式一样容易。

V1

然后这个循环继续;你继续开发,每当你导入V1 ----------- V2 <--(vendor) \ \ A -- B -- C -- M<--(dev) git将会意识到由于先前的合并Vn是合并基础。

<强>更新

在评论中你提到你已经设置了一些repo,里面有你的一些代码。这并不妨碍使用上述方法,但它引入了一些问题 - 因为如果您的项目基于供应商代码的修改,那么第一个源代码之前的版本是什么样的?

我看到了一些可能意味着的事情,所以请随意跳转到最能描述您情况的部分(或者,如果这些部分都没有,请澄清):

这是否意味着您没有提交原始供应商代码?

更新2:根据评论,听起来就是这种情况。我建议的第一种方法是重写历史记录;你已经声明过你#39; t想要改写历史记录。这取决于你,所以这是另一种选择。这创造了一个稍微有点怪异的历史,但是执行起来很简单并且可以很好地工作继续前进。)

与我发布的所有选项一样,您首先要创建一个&#34; clean&#34;供应商分支的历史。

V(n-1)

现在你已经&#34;假合并&#34;将其添加到现有的git checkout --orphan vendor rm -rf * cp -R /path/to/latest/source/drop/* . git add . git commit -m "initial source drop from $vendor" 分支中,以便在将未来版本添加到dev分支时,git会将其理解为合并库。

vendor

git checkout dev git merge --allow-unrelated-histories -s ours vendor 指定&#34;我们的&#34;合并策略,表示不会改变我在-s ours提交中已经拥有的内容&#34;。这将产生

HEAD

其中 V1 <--(vendor) \ A -- B -- C -- M <--(dev) 具有相同的&#34;内容&#34; (M)为TREE,但将C识别为父提交。您可以从此处继续使用最初描述的方法,因为当您引入V1时,V1将作为合并基础。

值得注意的是,当默认合并策略会产生合并冲突时,你应该只使用V2 - 它会在这里,所以没问题。如果默认合并策略能够解析合并,那么使用替代合并策略会创建一个&#34;邪恶合并&#34;,其中(相对于父母双方)的变化是&#34;隐藏&#34;以可能混淆用户和一些git命令的方式。为此,它应该没问题。

那就是说,重写会有风险的说法&#34;有两个原因值得怀疑。 (1)在您验证结果之前,您不必更换原产地,因此组织风险为零;在最坏的情况下需要时间(任何替代方案也是如此)。 (2)你在评论中描述的合并方法的类型有更多的动作部分,所以更难以推理 - 这就是为什么我说可能出错的原因(但不能确切地预测什么)。重写不太熟悉,但与风险较高的&#34;不相同。

考虑到这一点,我在这里保留了原始的重写方法:

创建第二个&#34; root&#34;在你的回购中提交。关于-s命令的相同警告适用于上述。

rm

现在你有了

git checkout --orphan vendor
rm -rf *
cp -R /path/to/latest/source/drop/* .
git add .
git commit -m "initial source drop from $vendor"

由于我现在假设V1 <--(vendor) A -- B -- C <--(dev) 已包含供应商代码的修改版本,因此您只想重新加入A

这将是历史重写,但您只需要执行一次。我推荐的这种重写方法是让所有人将所有代码都推送到共享远程,然后丢弃他们的克隆,然后你进行重写,然后每个人创建新的克隆。

重写将使用A完成。您需要找到git filter-branch的提交ID。您可以从

这样的命令中获取此信息
V1

它将是一个40个字符的十六进制数字字符串。然后重写命令看起来像

git log -n1 --format=%H vendor

如果您有多个分支,那么我说git filter-brnach --parent-filter 'sed "s/^\$/-p <commit-ID-from-above-command>/"' -- dev 您想要为所有分支命名(dev除外)。如果你有很多分支,我想你可以使用vendor而不是全部输入,但是你需要稍微复杂--all来区分你的root和供应商根目录;在这种情况下,请参阅--parent-filter文档。

如果您的历史记录包含代码,那么您希望它们移动,因此请添加git filter-branch

--tag-name-filter

结果将是

git filter-brnach --parent-filter  'sed "s/^\$/-p <commit-ID-from-above-command>/"' --tag-name-filter cat -- dev

其中V1 <--(vendor) \ A' -- B' -- C' <--(dev) 取代A'等。重要的一点是,您拥有&#34; pure&#34; A所基于的供应商代码,用作下一个供应商源代码的合并基础。从这里开始,一切都按照我最初的描述进行。

或者是否意味着您拥有自己的代码并向其添加供应商代码?

在这种情况下,您现有的提交不包含供应商代码;所以修复是类似的,但可能更简单。可能没有必要重写。

(您可以进行重写,特别是如果您希望供应商代码出现在历史记录中已存在的提交中。但这必须通过rebase而不是重新表示来完成,并且这会打开一个全新的蠕虫病毒。如果您可以在初始历史记录之后添加供应商代码,那么它会更简单;如果您需要有关如何将供应商代码重写到现有历史记录中的说明,让我们知道,我们可以添加其他信息。)

您仍然希望创建一个&#34; clean&#34;供应商分支的历史。

A'

但现在您只需将其合并到现有的git checkout --orphan vendor rm -rf * cp -R /path/to/latest/source/drop/* . git add . git commit -m "initial source drop from $vendor" 分支中,这样您就可以从那时起拥有供应商文件。

dev

只要您的文件没有任何供应商文件具有相同的路径/文件名,此合并将顺利进行,产生

git checkout dev
git merge --allow-unrelated-histories vendor

然后您可以从此处继续使用最初描述的方法,因为当您引入 V1 <--(vendor) \ A -- B -- C -- M <--(dev) V1将作为合并基础。