从SVN迁移到Gitlab CE的项目,并在工作流程和历史上苦苦挣扎 [英] Migrated project from SVN to Gitlab CE and struggling with workflow and history

查看:71
本文介绍了从SVN迁移到Gitlab CE的项目,并在工作流程和历史上苦苦挣扎的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以前的SVN工作流程

  • svn cp $ PATH/trunk $ REPO/branches/feature_xxx
  • git checkout $ REPO/branchches/feature_xxx
  • 开发人员研究分支的工作副本,偶尔将其与master合并,然后将分支标记为准备合并"
  • 功能就绪时,所有者进行svn merge --reintegrate $REPO/branches/feature_xxx,运行完整的测试套件,如果一切正常,则```svn commit -m"merged feature_xxx"
  • 准备好发布svn cp $REPO/trunk $REPO/tags/vX.Y.Z并释放该标签时.

svn中继历史记录看起来很干净:

version 4.5.4
feature_xxx
bugfix_yyy
version 4.5.3
feature_zzz
version 4.5.3
bugfix_aaa

我们不在乎内部提交,但是如果我们愿意,可以使用svn log -g将合并的提交扩展到内部提交(有用一两次).

现在,我们切换到Git和Gitlab,主要是因为它具有对合并请求和差异进行注释的功能.但是我们正在努力获得相同的干净工作流程,这或多或少是发生了什么:

策略1:尝试合并功能分支(通过git或Gitlab合并"按钮)

好..这很容易!嗯,等待WTF是那些先前的提交,这些提交正在污染我的主日志.

解决方案:不可能,这是一团糟.

策略2:了解git merge --squash

好吧..这就是我们想要的..现在我们有一段不错的历史,但是我们必须从命令行进行操作,因为Gitlab CE不允许从GUI( https://gitlab.com/gitlab-org/gitlab -ce/issues/13268 .

解决方案:历史看起来不错,但是我们需要手动关闭合并请求,因此我们失去了从已关闭但通过南瓜手动合并"中跟踪未打算执行"关闭的请求的能力.当然,必须有更好的方法.

策略3:了解git rebase.

好,所以当分支完成时,我们先执行git rebase origin/master然后执行git rebase -i XXXXX,其中XXXX是最古老的共同祖先,并且我们用消息"implemented feature_xxx"压缩所有提交.然后我们只需通过cmd行或GUI与master合并.

解决方案:现在gitlab检测到合并了哪些合并请求.但是重新定标对于长分支来说可能是PITA,有时会浪费时间,因此当事情变得繁琐时,我们只需回到merge --squash.另外,由于我们为每个分支都拥有已实现的feature_xxx ,然后又提交合并的feature_xxx ,因此日志不是很干净.它不像简单地合并那样糟糕,但仍然很杂乱. /p>

结论

所以现在我们使用的是最后一种策略..大多数时候尝试使用git rebase,但是当事情变得繁琐时又恢复为git merge --squash.但是,老实说,我们不是100%满意,SVN工作流程干净简单.

我们丢失了什么吗?谢谢

解决方案

从Subversion迁移到Git并不是一件容易的事,因为Git功能更强大,更复杂.但这绝对是富有成果的举动.这是一组相关的备注/建议/参考:

原子提交

我们不在乎内部提交,但是如果我们愿意,我们可以使用svn log -g将合并的提交扩展为内部提交(有用一两次).

即使您不希望所有时间都查看构成功能的内部提交,使用Git的最佳实践也是尽早并经常提交".这个想法是使每个提交变小,并且仅实现(或修复)一件事.例如,请参阅此博客文章 atomic-commits .也有编写良好提交消息"的最佳实践,请参见那个人.

我记得在Git中,每个提交都有很多元数据(作者名称+电子邮件+时间戳;提交者名称+电子邮件+时间戳;以及SHA1签名),与SVN相反,git commit仅在本地执行操作,因此您需要执行git push才能将更改发布到远程存储库.所有这些元数据都可以通过GUI工具显示,例如 gitk .

功能分支

在您提到的3种策略中,第一种绝对是最佳策略:为每个功能创建一个功能分支,然后将其合并"到母版中.但是,此策略有几种变体,我将在下面的工作流程"部分中详细说明.

策略1:尝试合并功能分支(通过git或Gitlab合并"按钮) 解决方法:没办法,这是一团糟.

了解合并的一个关键概念是快速合并的概念a>与非快进合并或真正合并.

>

如果两种类型的合并都是可行的,则CLI命令git checkout master && git merge feature将执行快进合并,如果功能分支仅包含一次提交,则可以接受.否则,最佳实践是通过执行git merge --no-ff feature来强制执行非快进合并.但是,如果您不使用命令行,而是单击GitHub或GitLab的合并"按钮,它将执行非快进合并.

非快速合并(= true合并)的优点是您的历史记录像一棵树(不同于SVN的典型线性历史记录),它使人们可以轻松地跟踪属于该功能的提交子集

例如,查看此 gitk 的屏幕截图,其中可能包含合并了两个分支的主历史记录:

好..这很容易!嗯,等待WTF是那些先前的提交,这些提交正在污染我的主日志.

这并不是真正的问题,但是仅供参考,在git loggitk中有一个功能,可以在一个这样的历史记录中隐藏功能分支的内部提交"(假设功能分支已与合并) --no-ff模式): --first-parent 选项.

以下是与gitk --first-parent相同示例对应的屏幕截图:

工作流程

分支和合并在Git中非常强大,因此有许多可能的方法来开发和集成master中的更改.但是,最好在开发团队中遵循系统的工作流程,并且两种不同的工作流程非常受欢迎,并且已经证明是非常有效的:

我只是放置了指向介绍这两个工作流程的主要参考的链接,但是您可以在Web上找到许多其他参考,包括脚本来促进Git流的应用.

总结两者之间的区别:Git流具有两个主要分支developmaster,具有特定的发布约定,而GitHub流更简单(没有develop分支)并且更适合连续交付的情况.

重新定基

您在帖子中提到了一些git rebase命令,所以我想您已经熟悉了该命令和相关含义,但是为了完整起见,这里有几点说明:

重定基础(git rebase another-branch)的基本含义是将当前分支的提交重播到另一个分支上",因此重定基础是历史重写"的一种形式,而重写提交意味着其SHA1已更改.因此,这里的主要规则是您不应使用git push为已经发布的更改建立基础.

请注意,Git流和GitHub流都使用git merge,但没有使用git rebase.

实际上,优良作法是在推送之前重写自己的本地历史记录,以确保提交是原子的或具有明智的提交消息等.为此,可以使用git commit --amendgit rebase --interactive ancestor-commit.

因此,现在我们使用的是最后一种策略..大多数时候,尝试git重新设置基准,但是当事情变得繁琐时,恢复到git merge --squash.但是,老实说,我们不是100%满意,SVN工作流程干净简单.

从命令行的角度来看,您提到的策略2和3确实可以保持线性历史记录,但是实际上,您将遵循Git的SVN工作流程,而通常情况下, Git中的非线性历史,并利用其功能分支设施...

好吧..这就是我们想要的..现在我们有一个很好的历史记录,但是我们必须从命令行进行操作,因为Gitlab CE不允许从GUI中提供它

实际上,我猜想GitLab CE不允许您轻松做到这一点,因为它最初是为支持Git工作流而设计的(通常意味着进行真正的合并等).

其他参考

这是GitLab关于这些主题的一篇很长的文章,但值得阅读: https://docs.gitlab.com/ee/workflow/gitlab_flow.html

另一个有用的参考是 https://git.github.io/git-reference/,其中总结了主要的Git命令,包括 git标签在我的帖子中没有提到,但是使用Git流的重要性非常重要.

Previous SVN workflow

  • svn cp $PATH/trunk $REPO/branches/feature_xxx
  • git checkout $REPO/branches/feature_xxx
  • developers worked on working copy of branch, ocasionally merging back with master and before marking the branch as "ready to merge"
  • when the feature was ready, owner did svn merge --reintegrate $REPO/branches/feature_xxx, run complete test suite and if everything ok then ```svn commit -m "merged feature_xxx"
  • when a release was ready svn cp $REPO/trunk $REPO/tags/vX.Y.Z and released that tag.

The svn trunk history looked nice and clean:

version 4.5.4
feature_xxx
bugfix_yyy
version 4.5.3
feature_zzz
version 4.5.3
bugfix_aaa

We don't care about internal commits, but if we wanted we can use svn log -g that will expand the merged commits into it's internal ones (was useful one or two times).

Now we switched to Git and Gitlab, mosty because of it's feature to comment on merge requests and diffs. But we are struggling to get the same clean workflow, this was more or less what happened:

Strategy 1: attemped to merge a feature branch (via git or Gitlab "Merge" button)

Ok.. this is easy! Ehh wait WTF are those previous commits that are polluting my master's log.

Resolution: no way, this is a mess.

Strategy 2: learned about git merge --squash

Ok.. this is what we want.. now we have a nice history, but we have to do it from command line because Gitlab CE doesnt' allows it from GUI (https://gitlab.com/gitlab-org/gitlab-ce/issues/34591). No big deal, we do it from command line.. oh wait, why hasn't Gitlab detected we closed the merge request..!? that was happening automatically before.

So, we learned there is no way for it to detect the branch was merged.. sure there is another way (eg. closing with a commit message). Ups not implemented yet: https://gitlab.com/gitlab-org/gitlab-ce/issues/13268.

Resolution: the history looks nice, BUT we need to manually close the merge requests so we loose the ability to track which ones we closed as "not intented to do" from "closed but merged manually via squash". Sure there must be a better way..

Strategy 3: learned about git rebase..

Ok, so when a branch is finished we do git rebase origin/master and then git rebase -i XXXXX where XXXX is the oldest common ancestor and we sqash all commits with a message "implemented feature_xxx". And then we simply merge with master via cmd line or GUI.

Resolution: now gitlab detects which merge requests are merged.. good. But rebasing can be a PITA for long branches and a loss of time sometimes, so when things get hairy we simply go back to merge --squash. Also the log is not so clean becaouse we have for every branch the implemented feature_xxx and then merged feature_xxx commits.. it's not as bad as simply merging but still is noise.

Conclusion

So now we are using the last strategy.. trying to git rebase most of the time but reverting to git merge --squash when things get hairy. But to be honest we are not 100% happy, the SVN workflow was clean and simpler.

Are we missing something? Thanks

解决方案

Migrating from Subversion to Git is not an easy task because Git is much more powerful and more complex. But it is definitely a fruitful move. Here is a set of related remarks/advice/references:

Atomic commits

We don't care about internal commits, but if we wanted we can use svn log -g that will expand the merged commits into it's internal ones (was useful one or two times).

Even if you don't want all the time to look at the internal commits that constitute a feature, with Git the best practice is to "commit early and often". The idea is to make each commit small and implement (or fix) only one thing. See for example this blog article atomic-commits. There are also best practices to write "good commit messages", see this article or that one.

I recall that in Git, each commit has a lot of metadata (an author name + e-mail + timestamp; a committer name + e-mail + timestamp; and a SHA1 signature) and contrarily to SVN, git commit only acts locally, so that you need to do git push to publish your changes to a remote repository. All this meta-data can be shown by GUI tools such as gitk.

Feature branches

Among the 3 strategies you are mentioning, the first one is definitely the best one: create one feature branch per feature, then "merge" them in master. But this strategy has several variants that I'll elaborate in the Workflows section below.

Strategy 1: attemped to merge a feature branch (via git or Gitlab "Merge" button) Resolution: no way, this is a mess.

A key concept to know about merges is the notion of fast-forward merge vs. non fast-forward merge or true merge.

If both types of merge are possible, the CLI command git checkout master && git merge feature will do a fast-forward merge, which could be acceptable if the feature branch contains only one commit. Otherwise it is best practice to force doing a non-fast-forward merge by doing git merge --no-ff feature. But if you don't use command-line and instead click on the "Merge" button of GitHub or GitLab, it will perform a non-fast-forward merge.

The advantage of a non-fast-forward merge (= true merge) is that your history looks like a tree (unlike SVN's typical linear history) that allows one to easily keep track of the subset of commits that belongs to the feature.

See for example this screenshot of gitk with a possible master history with 2 branches merged:

Ok.. this is easy! Ehh wait WTF are those previous commits that are polluting my master's log.

This is not really an issue, but FYI there is a feature of git log and gitk that allows to hide the "internal commits" of the feature branches in one such history (assuming the feature branches have been merged with --no-ff mode): the --first-parent option.

Here is a screenshot corresponding to the same example with gitk --first-parent:

Workflows

Branching and merging is very powerful in Git so that there are many possible ways to develop and integrate changes in master. However, it is better to follow a systematic workflow in one's development team, and two different workflows are very popular and have been proven to be very effective:

I just put a link to the main reference presenting these two workflows but you can find many other references on the Web, including scripts to facilitate the application of the Git flow for example.

To summarize the difference between the two: the Git flow has two main branches develop and master, with specific conventions for releases, while the GitHub flow is simpler (no develop branch) and more adapted to the case of continuous delivery.

Rebasing

You mentioned some git rebase commands in your post so I guess you are familiar with this command and related implications, but just to be self-contained here are several remarks:

Rebasing (git rebase another-branch) basically means "replaying the commits of current branch upon another branch", so rebasing is a form of "history rewriting", and rewriting a commit implies its SHA1 is changed. So the main rule here is that you should not rebase changes that have been already published with git push.

Note that both Git flow and GitHub flow use git merge, but not git rebase.

Actually, it is good practice to rewrite one's local history before pushing, to ensure that the commits are atomic or have sensible commit messages, etc. To this aim, one can use git commit --amend or git rebase --interactive ancestor-commit.

So now we are using the last strategy.. trying to git rebase most of the time but reverting to git merge --squash when things get hairy. But to be honest we are not 100% happy, the SVN workflow was clean and simpler.

The strategies 2 and 3 you mention are indeed possible from a command-line-only perspective to keep a linear history, but doing this you'd actually be following a SVN workflow with Git, while it is much more usual to have a non-linear history in Git and take advantage of its feature branches facilities...

Ok.. this is what we want.. now we have a nice history, but we have to do it from command line because Gitlab CE doesnt' allows it from GUI

Actually I guess that GitLab CE doesn't allow you to do this easily because it has been designed in the first place to support Git workflows (which typically implies doing true merges and so on).

Extra references

Here is a long article, but worth reading, written by GitLab on these topics: https://docs.gitlab.com/ee/workflow/gitlab_flow.html

Another useful reference is https://git.github.io/git-reference/, which gives a summary of the main Git commands, including git tag that I did not mention in my post but which is very important what it comes to using the Git flow.

这篇关于从SVN迁移到Gitlab CE的项目,并在工作流程和历史上苦苦挣扎的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆