简体   繁体   English

不同版本之间 Git 合并的最佳实践

[英]Best Practices for Git Merge between different versions

Imagine we use Gitflow where we have split a release branch from develop that will eventually be merged into both main and develop .想象一下,我们使用Gitflow ,我们从develop中拆分了一个发布分支,最终将合并到maindevelop On release there are only quality improvements.release时只有质量改进。 Most of these require a deployment to an integration stage, so their version in several pom.xml (multi-module) and package.json is updated and tagged on the release branch.其中大多数需要部署到集成阶段,因此它们在几个pom.xml (多模块)和package.json中的release会在release分支上更新和标记。

On develop there is regular (unstable) feature development for future releases, and the version has been set accordingly.develop有针对未来版本的常规(不稳定)功能开发,并且版本已相应设置。 Occasionally the improvements from release are merged back into develop .有时, release的改进会合并回develop There we get merge conflicts, marked with an X in the following illustration.我们会遇到合并冲突,在下图中用 X 标记。

main     ----------------------o----
                              /
release        o---o-----o-o-o
              /     \     \   \
develop  ----o---o---x--o--x-o-x----
                           ^
               we are here |

Example:例子:

  • On release the version number is 1.0.0-SNAPSHOT . release的版本号是1.0.0-SNAPSHOT
  • On develop the version number is 1.1.0-SNAPSHOT after branching off.develop分支后,版本号为1.1.0-SNAPSHOT
  • New features go into develop , the version number stays constant there.新功能进入develop ,版本号在那里保持不变。
  • The version in release is incremented (and tagged) occasionally to 1.0.1 , 1.0.2 , 1.0.3 and so on. release版中的release偶尔会增加(并标记)到1.0.11.0.21.0.3等等。
  • Now of course there is a conflict when I want to merge version 1.0.x into 1.1.0 while the common ancestor is 1.0.0.现在当然当我想将版本 1.0.x 合并到 1.1.0 而共同祖先是 1.0.0 时会发生冲突
    • (We fully understand what happens there, don't need explanation for that.) (我们完全理解那里发生的事情,不需要解释。)
$ git checkout develop
$ git merge --no-commit --no-ff release

Auto-merging pom.xml
CONFLICT (content): Merge conflict in pom.xml
...
Auto-merging client/package.json
CONFLICT (content): Merge conflict in client/package.json
Automatic merge failed; fix conflicts and then commit the result.

We are looking for ideas to handle this situation.我们正在寻找处理这种情况的想法。 Some research shows that this is not an uncommon problem, so I found several suggestions.一些研究表明,这并不是一个罕见的问题,所以我找到了几个建议。 Mostly it is said to just manually resolve the conflicts.大多数情况下,据说只是手动解决冲突。 But I am still eager to find a way that can be automated in a script.但我仍然渴望找到一种可以在脚本中自动化的方法。 Maybe there is some Git magic to help?也许有一些 Git 魔法可以提供帮助? Maybe we started badly in the first place?也许我们一开始就很糟糕?

The following discussion brings a better image to it, where we are at "Only bugfixes!":以下讨论为它带来了更好的形象,我们在“只有错误修正!”:

Approach 0 -- Do not do this?方法 0 -- 不要这样做?

Since our team started doing this kind of version increments and tagging, I've been unsure if this is a good practice.由于我们的团队开始进行这种版本增量和标记,我一直不确定这是否是一个好的做法。 But it basically works, we defined the details of the workflows together, and our customer and partners and test team require us to deliver release candidates as if it were actual releases.但它基本上有效,我们一起定义了工作流程的细节,我们的客户、合作伙伴和测试团队要求我们像实际发布一样交付候选版本。 When a version xyz has been tested successfully, it goes to productive environment unchanged and then release is merged into main .当 xyz 版本测试成功后,它会原封不动地进入生产环境,然后将release合并到main But the problem stays: As soon as a hotfix is made in main and should be backported to develop , we are going to get version conflicts again.但问题仍然存在:一旦在main进行了修补程序并且应该向后移植到develop ,我们将再次遇到版本冲突。

Approach 1 -- Cherry Picking?方法一——樱桃采摘?

Sorry, I won't do this.对不起,我不会这样做。 Too often I read that cherry picks are evil.我经常读到樱桃镐是邪恶的。 And they would be against Gitflow.他们会反对 Gitflow。

Approach 2 -- Accept to resolve manually?方法 2 -- 接受手动解决?

That is what we do now.这就是我们现在所做的。 The process is not automated.该过程不是自动化的。 Every time the version number in release is changed, the following merge gets a conflict that has to be manually resolved.每次release的版本号发生变化时,下面的合并都会遇到必须手动解决的冲突。 We accept it but are unhappy with that.我们接受它,但对此不满意。

Approach 3 -- Do not merge that often?方法 3——不要经常合并?

I guess that would be bad practice.我想那将是不好的做法。 We want to have the quality improvements merged to all our branches.我们希望将质量改进合并到我们所有的分支机构。

Approach 4 -- Use merge option --ours or similar?方法 4 -- 使用合并选项--ours或类似选项?

The problem is that automated "resolution" of merge conflicts is file-based, from what all I could find out, not line- or block-based.问题是合并冲突的自动“解决”是基于文件的,据我所知,不是基于行或块。 We need to keep the version number from develop , but other changes in those files pom.xml or package.json might be on either side and not to be overridden blindly, so these kind of conflicts we want to see and resolve manually.我们需要将版本号保留在develop ,但是这些文件pom.xmlpackage.json其他更改可能在任一侧,而不是盲目覆盖,因此我们希望手动查看和解决此类冲突。 I am open to any suggestions in this direction though!不过,我对这个方向的任何建议持开放态度!

Approach 5 -- Move version number to separate file?方法 5 -- 将版本号移动到单独的文件?

This way we would reduce the conflicts to one single location where it can be trivially resolved using --ours .通过这种方式,我们可以将冲突减少到一个位置,在那里可以使用--ours解决。 While it seems to be possible with newer Maven versions, I am not aware of an approch for package.json to refer to an externally defined version number.虽然使用较新的 Maven 版本似乎是可能的,但我不知道package.json引用外部定义的版本号的方法。 Has anyone made good experience with that and would recommend to go further this way?有没有人对此有很好的经验,并建议以这种方式走得更远?

Approach 6 -- Prepare and reset version in develop ?方法 6 - 在develop准备和重置版本?

I saw such behavior by jgitflow-maven-plugin which is not maintained for over 6 years now.我通过jgitflow-maven-plugin看到了这种行为,现在已经超过 6 年没有维护了。 We could make a commit in develop , writing the release version into the files, then merge, and change the version back to the original one.我们可以在develop进行提交,将release版本写入文件,然后合并,并将版本更改回原始版本。

I dislike that there would be additional commits that have nothing to do with actual development, and I see no possiblity to clean up the Git history.我不喜欢有与实际开发无关的额外提交,而且我认为没有可能清理 Git 历史记录。

So this would be an interesting follow-up question: I know I can rebase/squash D into C, but I don't see how I can rebase/squash A or B into C. Does anyone else?所以这将是一个有趣的后续问题:我知道我可以将 D 变基/压扁为 C,但我不知道如何将 A 或 B 变基/压扁为 C。还有其他人吗?

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

Approach 7 -- Prepare version in release ?方法 7 - 在release准备版本?

Similar to the previous approach, we could make a commit in release , write the target version, then merge to develop without conflict.与之前的方法类似,我们可以在release进行提交,编写目标版本,然后合并进行develop不会发生冲突。 We would then not need a revert commit in release but could just move the branch pointer back with git reset --hard HEAD^ and/or simply not push it, so this preparation commit would be located "between" the two branches.然后我们将不需要在release进行还原提交,而只需使用git reset --hard HEAD^将分支指针移回和/或根本不推送它,因此此准备提交将位于两个分支“之间”。

-----B-----------
      \
       B'
        \
---A-----C---D---

The following article describes a similar thing using an intermediate branch (to fulfill the requirement for a pull request), but it is several manual steps that don't solve my challenge.下面的文章描述了使用中间分支(以满足拉取请求的要求)的类似事情,但它是几个手动步骤并不能解决我的挑战。

Approach 8 -- Prepare version without commit?方法 8 -- 准备版本而不提交?

My favorite solution would be to just write the target version in local develop without commit, then merge release onto that... but git merge does not allow this.我最喜欢的解决方案是只在本地develop编写目标版本而不提交,然后合并release ......但git merge不允许这样做。 I do not see any switch to override this behavior and ignore unmerged我没有看到任何开关来覆盖此行为并忽略未合并

error: Your local changes to the following files would be overwritten by merge:
        client/package.json
        ...
        pom.xml
Please commit your changes or stash them before you merge.
Aborting

Searching the web tells me to stash the local changes, but that is not an option of course.搜索网络告诉我隐藏本地更改,但这当然不是一个选项。

Approach 9 -- Write program to resolve conflicts?方法 9 -- 编写程序来解决冲突?

I play with the idea that these conflicts are well-structured and can even be fully predicted, so it should be possible to write a small shell script to grep/sed the conflicts in order to automatically resolve and commit the merge.我认为这些冲突结构良好,甚至可以完全预测,因此应该可以编写一个小的 shell 脚本来 grep/sed 冲突,以便自动解决和提交合并。 But I hesitate to put large efforts here and hope for enlightenment by other people!但我犹豫要不要在这里付出很大的努力,希望得到其他人的启发!

Note : "Best Practice" for something like this is difficult to define, since everyone's situation is likely different.注意:像这样的事情的“最佳实践”很难定义,因为每个人的情况可能不同。

That being said, one of our projects has a similar situation as yours: we use Git Flow, and our develop branch build numbers are always different than the release branch build numbers.话虽如此,我们的一个项目与您的情况类似:我们使用 Git Flow,我们的develop分支构建号始终与release分支构建号不同。 Our potentially ideal solution has not been implemented, but would likely be similar to your suggested Approach 8, where we would inject the version into the build pipeline without it being hard-coded in a commit (ie don't even modify the version file at all).我们潜在的理想解决方案尚未实现,但可能与您建议的方法 8 类似,我们会将版本注入构建管道,而无需在提交中对其进行硬编码(即,甚至不要在以下位置修改版本文件)全部)。 The downside of this though is you can't know what version is represented by a specific commit based on code alone.但是,这样做的缺点是您无法仅根据代码就知道特定提交代表什么版本。 But you could tag the commit with a specific version, which is probably what we would do if we implemented that.但是你可以用一个特定的版本来标记提交,如果我们实现它,这可能是我们会做的。 We could also bake the commit ID along with version info into the artifact meta data for easy lookup.我们还可以将提交 ID 和版本信息一起烘焙到工件元数据中,以便于查找。

The solution we currently use is a combination of Approaches 4, 5, and 7. We separate version files (your Approach 5), and every time we create a release (or hotfix ) branch, the first commit only changes the version file to the upcoming release version (your Approach 7).我们目前使用的解决方案是方法4、5、7的组合。我们将版本文件(你的方法5)分开,每次我们创建一个release (或hotfix )分支时,第一次提交只将版本文件更改为即将发布的版本(您的方法 7)。 We make sure that release always has the tip of main in it, so that anytime we deploy release to production we can cleanly merge release to main .我们确保release总是包含main的提示,这样无论何时我们将release部署到生产中,我们都可以干净地将release合并到main (Note we still use --no-ff as suggested by Git Flow but the point is we could fast-forward if we wanted to.) (请注意,我们仍然按照 Git Flow 的建议使用--no-ff ,但关键是我们可以根据需要快进。)

Now, after you complete the release branch into main , Git Flow suggests merging release back to develop , but we find merging main back to develop slightly more efficient so that the tip of main is also on develop , but occasionally we also merge release back into develop before deployment if important bug fixes appear on release .现在,在您将release分支完成到main ,Git Flow 建议将release合并回develop ,但我们发现合并maindevelop更有效一些,因此main的提示也在develop ,但有时我们也会将release合并回develop如果重要的错误修复出现在部署之前release Either way, both of those merges back to develop will always have conflicts with the version files on develop , and we use your Approach 4 to automate choosing the develop version of those files.无论哪种方式,这两个合并的回develop总是有上的版本文件的冲突develop ,而我们用你的方法4自动选择develop这些文件的版本。 This enables the merge back to be fully automated, however, sometimes there are still other conflicts that have to be resolved manually, just as a course of normal development happening on develop and release simultaneously.这使得合并回可以完全自动化,但是,有时仍然需要手动解决其他冲突,就像在developrelease同时发生的正常开发过程一样。 But at least it's usually clean.但至少它通常是干净的。

Note that a side effect of our approach is that our versions files are always different on develop and main , and that's fine with us.请注意,我们方法的一个副作用是我们的版本文件在developmain总是不同的,这对我们来说很好。

What about using an external tool to manage the version?使用外部工具来管理版本怎么样? We use GitVersion for this.为此,我们使用GitVersion Now I am not sure if there is a smarter way, but a brute-force one is to have something like this <version>${env.GitVersion_SemVer}</version> in your pom.xml, where env.GitVersion_SemVer is an output from GitVersion.现在我不确定是否有更聪明的方法,但一个蛮力的方法是在你的 pom.xml 中有这样的<version>${env.GitVersion_SemVer}</version> ,其中 env.GitVersion_SemVer 是一个输出来自 GitVersion。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM