不同git分支版本化的最佳方法

4
我们有以下情况:我们有几个基本版本的游戏OpenLieroX,目前是0.57、0.58和0.59。对于每个基本版本,我们都有一个单独的分支。每个这样的基本版本都有几个发布版(如0.57 beta1-beta8和rc1、0.58 beta1-beta9)。
当我们在开发新功能时,我们在最高基础版本分支上工作(现在是0.59)。当我们修复一些已报告的错误时,我们会在最早出现问题的版本中进行修复(通常是0.58)。不时地,我们总是将所有0.58的更改合并到0.59中(只要我们仍然维护和更改旧分支)。
这一切都很好,直到涉及到我们只想在0.58而不是0.59中进行某些更改的情况。到目前为止,只有一个情况发生:版本号。我们有一些Version.cpp文件(还有其他一些文件),其中包含版本号。因此,当我们想为0.58推送新版本时,我们会将其中的版本字符串更改为“0.58 beta10”(或其他内容)。现在,当我们从0.58合并到0.59时,这个更改也会被应用。我们目前通过再次覆盖正确的版本号(或在其他糟糕提交的情况下,可能是还原)来修复这些情况。
这些关于不想要的更改的细节对我来说有点丑陋。我们通常管理方式是否不好/不常见?如何以获得相同结果的最简单方法?挑选所有0.58提交到0.59中将是更麻烦的工作。
还有一个可能使情况变得更加复杂的进一步细节:在编写代码时,我必须设置即将推出的版本号。这是因为我们有一个网络引擎,我们可能已经引入了一些新功能,并且代码中有像“if(client->version() >= Version(X,Y,Z))...”这样的检查。现在,当我们引入新内容时,通常也意味着在某些点上进行此类检查。(但我们试图避免在旧分支中进行这些更改。)
另一个问题是,我们不仅仅计数版本号(如0.58.1、0.58.2,...),而是像这样计数:0.58 beta1、0.58 beta2、...、0.58 betaX、0.58 rc1、...、0.58、0.58.1、0.58.2,...这是因为我们希望在开始时将其标记为实验性阶段(beta阶段),然后标记为大多数稳定或稳定。在某些罕见情况下,甚至可能在两个不同的beta版本之间存在严重更改(当然,我们试图避免这种情况,但有时没有办法)。
4个回答

5

带有额外分支

在0.59分支与0.58分支分离后,您可以使用单独的“发布”分支来管理0.58版本号的更改。每个版本(除了最新版本)都将拥有自己的“发布”分支,该分支仅包含来自基础分支的合并和更新外部版本号的更改。分支结构可能如下所示:

                A--------o--B                  0.58-release
               /        /
...--o--o--o--o--o--o--o--o                    0.58
      \        \        \  \
       \        \        \  \            C     0.59-release
        \        \        \  \          /
         o--o--o--o--o--o--o--o--o--o--o--o    0.59
                                  \     \
                                   o--o--o     0.60
  • 软件版本“0.58 beta9”由A标记
  • 软件版本“0.58 rc1”由B标记
  • 0.58有未发布的更改
  • 软件版本“0.59 beta1”由C标记
  • 0.59有未发布的更改
  • 0.60与0.59尚未完全更新

或者,如果您严格要求仅在A、B、C等处进行更改,以更改外部版本号(没有重大代码更改,这些属于“基础”分支:0.58、0.59等),则可以不使用“发布”分支。相反,您可以使用一个分离的HEAD或临时分支(在版本化后删除),以进行外部版本更新提交并将其保存在标签中。

                A        B
               /        /
...--o--o--o--o--o--o--o--o                    0.58
      \        \        \  \
       \        \        \  \            C
        \        \        \  \          /
         o--o--o--o--o--o--o--o--o--o--o--o    0.59
                                  \     \
                                   o--o--o     0.60
  • A将软件标记为“0.58 beta9”,标签为0.58-beta9
  • B将软件标记为“0.58 rc1”,标签为0.58-rc1
  • C将软件标记为“0.59 beta1”,标签为0.59-beta1

类似Git的版本控制方法

你也可以看一下Git是如何进行自己的版本控制的。

对于从Git工作树中构建的版本,版本号是从git describe HEAD的输出中生成的。 Makefile 知道哪些文件需要重新编译/重建,如果版本号更改,则始终运行 GIT-VERSION-GEN 脚本以确保它具有最新的版本号。 通过包含生成的版本文件,可以在 Makefile 中获取版本号。 它通过编译器参数(-DGIT_VERSION=…)传递给C文件,并通过使用 sed 将其替换到脚本中。

还有一些规定,可以覆盖构建中“烧录”的版本号,但它们通常仅适用于在工作树之外进行的构建(例如,从tar文件中提取的树中进行的构建)。

将版本字符串更改与其他更改混合

在您对问题的补充中,您声明需要在开发过程中调整版本号。首先,我认为seh和我描述的“0.58-release”分支方案仍然适用于您。只是需要更多的纪律来分离您的更改。如果您将 *-release 分支视为“发布供内部测试”的分支,而不仅仅是“发布给客户(或用于外部测试)”,那么它仍然是有意义的。始终在基本分支上进行开发(例如,“0.58”),并始终将基本分支合并到发布分支(例如,“0.58-release”)中,然后再进行需要特定版本号的构建(始终从这样合并的发布分支进行构建)。

如果您坚持将版本号更改和(非合并的)代码更改放在同一行历史记录中,则在合并时似乎您将别无选择,除非您使用 git cherry-pick(根据Damien Wilson或针对git rebase -i的自动编辑脚本)处理冲突。

如果您的“版本文件”包含版本信息,则您可以使用 .gitattributes 将您的 Version.cpp 文件标记为不可合并,从而减轻冲突解决。

在包含 Version.cpp 的目录中的 .gitattributes

/Version.cpp -merge

将其标记为这样(与merge=binary相同)将始终在合并的分支之间存在文件差异时引起冲突。合并后的工作树版本将默认为您已经检出的分支的版本(而不是您正在合并的分支/分支的版本),因此您只需git add Version.cpp && git commit即可完成合并(假设所有其他冲突也已解决)。


我有些想法已经打算这样做了,但还有一个问题:在编写代码时,我必须提前设置即将发布的版本号。这是因为我们有一个网络引擎,可能新增了一些功能,并且代码中有像“如果(client->version() >= Version(X,Y,Z)) ...”这样的检查。现在,当我们引入新的东西时,通常也会在某些点上进行此类检查。另一个问题是,我们不仅要递增版本号(比如0.58.1、0.58.2等),而是在某个时候,我们不再称之为beta,而是rc(比如0.58 betaX < 0.58 rc1)。 - Albert
我添加了一个新的章节“将版本字符串更改与其他更改混合”,提供了另一种思考 *-release 分支含义的方式,并提出了一个额外的建议(将版本文件标记为不可合并)。至于从带编号的 beta 到带编号的 rc 再到带编号的更改,我不认为这与 Git 有任何关系。只要您的代码能够理解它们,您想要的任何版本字符串都应该没问题。 - Chris Johnsen
感谢提供的额外信息。但我认为每次都必须切换分支来执行简单的“make”会非常烦人。总是产生冲突也不是真正的解决方案 - 可能有助于不会意外忽略这样的更改,但似乎大多数情况下也很烦人。但也许我可以为一个简单的文件设置合并策略?在这种情况下,我可以将“ours”策略设置为此文件。那实际上就解决了一切。 - Albert

0

难点在于从底层代码中分离版本指定。

您可以将版本指定的分支浮动到已发布的代码分支之上,这样您就可以安全地将所有代码从已发布的分支(0.58)合并到主分支(0.59),而不会混淆冲突的版本指定。该版本指定分支永远不会合并回已发布的分支;当您想要在版本化的发布中包含新代码时,您只需将其重新基于已发布的分支。

您可以通过以下条目轻松安排此操作:.git/config文件:

[branch "0.58-release"]
        remote = .
        merge = refs/heads/0.58
        rebase = true

有了这个,你的发布分支被称为“0.58”,并且你将使用“0.58-release”分支来制作版本化的构建。当到达制作发布构建的时间时,你需要执行以下命令:

git checkout 0.58-release
git pull
# Edit the version-designating file.
git commit -a -m'Updated version.'

所有对版本指定文件的更改都只存在于“58-release”分支上,并且可以通过git rebase安全地向前移动。或者,如果您希望每个对版本指定文件的更改都与“0.58”分支上的代码状态保持一致,您可以将“0.58”分支合并到“0.58-release”分支中,而不是将“0.58-release”在“0.58”之上进行rebase。

我不确定像那样使用rebase是否有多大意义。如果你刚刚发布了0.58-rc2,那么在0.58-release分支的历史记录中向后查看,我们会看到“标记提交”(仅修改“版本指定文件”的提交)为0.58-rc2、0.58-rc1、0.58-beta9等等,最后是0.58-rc2本身的“内容提交”的顶端。如果“标记提交”只包含将特定版本号打印到代码中的提交,为什么要继续拖动所有旧的提交?使用合并会产生我回答中的第一个图表。 - Chris Johnsen
是的,Chris,我同意。我分享了一个重新利用的技巧,用于更合适的事情 - 保留项目Makefile的本地定制版本,它应该始终在最新的上游更改之上进行rebase,并且永远不会合并回来。我的答案在最后推荐合并而不是rebase以保持时间一致性。这与您的答案相匹配,您在我完成编辑之前不久发布了您的答案。我投了您的答案的赞成票。 - seh
这也存在问题,因为我在开发过程中必须设置即将发布的版本号(请参见问题描述,我在最后扩展了该部分)。 - Albert

0

看起来你现在做的是正确的(为了这个结果)。但也许你应该重新考虑你的版本系统。

你可以添加一个构建版本,0.58.1、0.58.2等等。这样你就可以添加任意多的东西,而你仍然可以把它们统称为“0.58”,尽管它有一个构建号。

发布时你可以添加beta、RC等等,但这不是版本控制系统应该关心的事情。

发布“0.58.3(beta1)”只是意味着版本0.58.3。beta1部分是人类信息,而不是版本控制系统所关心的。


在“0.58 beta X”中,X是构建编号。我们也可以称其为“0.58.X”。问题在于:在Version.cpp中,我们有一些静态常量std :: string VERSION =“0.58.8”。现在,我们做了一些工作,然后我们想发布一个新版本,我们将那个std :: string更改为“0.58.9”。这个版本更改的提交是有问题的,因为这个单独的提交在0.59分支中没有意义(尽管我们只想将0.58的所有其他提交放入0.59中,除了这一个)。 - Albert
也许可以从仓库中排除version.cpp文件,只在构建时包含它?你是否可以将版本作为某种构建变量?我不是C / C ++程序员,所以对构建方式不太熟悉。 - Tor Valamo
那只会让整个事情变得更加复杂。我应该把它存储在哪里?我希望能够切换分支并进行一些重新编译。不得不手动设置正确的版本,通过从某个地方复制一些 Version.cpp 来完成,这看起来并不那么好。 - Albert
我意识到这可能会很困难。但是构建变量选项呢? - Tor Valamo

0
如果我理解问题正确的话,听起来你想从不同的分支中包含某个特定子集的提交。如果是这样的话,你可能只需要运行git cherry-pick并且只应用你想要的提交。
关于该命令的更多信息可以在git文档Git Ready上获得。

是的,但那不是一个好的解决方案,因为这将需要更多的工作,而我正在寻找最简单/最好的解决方案。在0.58中可能有100个提交左右,我们希望它们出现在0.59中,但只有一个(版本字符串更改)我们不想要。 - Albert
你尝试过改变合并策略吗?也许可以尝试子树合并?在SO上有另一个关于git合并策略的帖子,介绍了何时使用特定的策略。http://www.kernel.org/pub/software/scm/git/docs/howto/using-merge-subtree.html https://dev59.com/_XRC5IYBdhLWcg3wP-Zh - Damien Wilson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接