简体   繁体   English

Git 工作流程和变基与合并问题

[英]Git workflow and rebase vs merge questions

I've been using Git now for a couple of months on a project with one other developer.我已经在与另一位开发人员的一个项目中使用 Git 几个月了。 I have several years of experience with SVN , so I guess I bring a lot of baggage to the relationship.我在SVN有几年的经验,所以我想我给这段关系带来了很多包袱。

I have heard that Git is excellent for branching and merging, and so far, I just don't see it.我听说 Git 在分支和合并方面非常出色,到目前为止,我只是没有看到。 Sure, branching is dead simple, but when I try to merge, everything goes all to hell.当然,分支非常简单,但是当我尝试合并时,一切都变得很糟糕。 Now, I'm used to that from SVN, but it seems to me that I just traded one sub-par versioning system for another.现在,我已经习惯了 SVN,但在我看来,我只是将一个低于标准的版本控制系统换成了另一个。

My partner tells me that my problems stem from my desire to merge willy-nilly, and that I should be using rebase instead of merge in many situations.我的搭档告诉我,我的问题源于我想不经意地合并,并且在许多情况下我应该使用 rebase 而不是合并。 For example, here's the workflow that he's laid down:例如,这是他制定的工作流程:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature
git checkout master
git merge my_new_feature

Essentially, create a feature branch, ALWAYS rebase from master to the branch, and merge from the branch back to master.本质上,创建一个功能分支,始终从 master 到分支,然后从分支合并回 master。 Important to note is that the branch always stays local.需要注意的重要一点是分支始终保持在本地。

Here is the workflow that I started with这是我开始的工作流程

clone remote repository
create my_new_feature branch on remote repository
git checkout -b --track my_new_feature origin/my_new_feature
..work, commit, push to origin/my_new_feature
git merge master (to get some changes that my partner added)
..work, commit, push to origin/my_new_feature
git merge master
..finish my_new_feature, push to origin/my_new_feature
git checkout master
git merge my_new_feature
delete remote branch
delete local branch

There are two essential differences (I think): I use merge always instead of rebasing, and I push my feature branch (and my feature branch commits) to the remote repository.有两个本质区别(我认为):我总是使用合并而不是变基,并且我将我的功能分支(和我的功能分支提交)推送到远程存储库。

My reasoning for the remote branch is that I want my work backed up as I'm working.我对远程分支的推理是我希望在工作时备份我的工作。 Our repository is automatically backed up and can be restored if something goes wrong.我们的存储库会自动备份,如果出现问题可以恢复。 My laptop is not, or not as thoroughly.我的笔记本没有,还是没有那么彻底。 Therefore, I hate to have code on my laptop that's not mirrored somewhere else.因此,我讨厌在我的笔记本电脑上有没有在其他地方镜像的代码。

My reasoning for the merge instead of rebase is that merge seems to be standard and rebase seems to be an advanced feature.我对合并而不是 rebase 的推理是合并似乎是标准的,而 rebase 似乎是一个高级功能。 My gut feeling is that what I'm trying to do is not an advanced setup, so rebase should be unnecessary.我的直觉是我想要做的不是高级设置,所以 rebase 应该是不必要的。 I've even perused the new Pragmatic Programming book on Git, and they cover merge extensively and barely mention rebase.我什至仔细阅读了关于 Git 的新的 Pragmatic Programming 书,它们广泛地涵盖了合并,几乎没有提到 rebase。

Anyway, I was following my workflow on a recent branch, and when I tried to merge it back to master, it all went to hell.无论如何,我在最近的一个分支上遵循我的工作流程,当我试图将它合并回 master 时,一切都变得糟糕透了。 There were tons of conflicts with things that should have not mattered.与本应无关紧要的事情发生了大量冲突。 The conflicts just made no sense to me.这些冲突对我来说毫无意义。 It took me a day to sort everything out, and eventually culminated in a forced push to the remote master, since my local master has all conflicts resolved, but the remote one still wasn't happy.我花了一天时间整理好一切,最终以强制推送到远程 master 达到高潮,因为我的本地 master 已经解决了所有冲突,但是远程 master 仍然不高兴。

What is the "correct" workflow for something like this?像这样的“正确”工作流程是什么? Git is supposed to make branching and merging super-easy, and I'm just not seeing it. Git 应该让分支和合并变得超级简单,我只是没有看到。

Update 2011-04-15更新 2011-04-15

This seems to be a very popular question, so I thought I'd update with my two years experience since I first asked.这似乎是一个非常受欢迎的问题,所以我想我会用我第一次问起两年来的经验来更新。

It turns out that the original workflow is correct, at least in our case.事实证明,原始工作流程是正确的,至少在我们的情况下是这样。 In other words, this is what we do and it works:换句话说,这就是我们所做的并且有效:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge my_new_feature

In fact, our workflow is a little different, as we tend to do squash merges instead of raw merges.事实上,我们的工作流程有点不同,因为我们倾向于进行压缩合并而不是原始合并。 ( Note: This is controversial, see below. ) This allows us to turn our entire feature branch into a single commit on master. 注意:这是有争议的,见下文。 )这允许我们将我们的整个功能分支变成对 master 的单个提交。 Then we delete our feature branch.然后我们删除我们的功能分支。 This allows us to logically structure our commits on master, even if they're a little messy on our branches.这允许我们在 master 上逻辑地构建我们的提交,即使它们在我们的分支上有点混乱。 So, this is what we do:所以,这就是我们要做的:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge --squash my_new_feature
git commit -m "added my_new_feature"
git branch -D my_new_feature

Squash Merge Controversy - As several commenters have pointed out, the squash merge will throw away all history on your feature branch. Squash Merge Controversy - 正如一些评论者所指出的, Squash 合并将丢弃您的功能分支上的所有历史记录。 As the name implies, it squashes all the commits down into a single one.顾名思义,它将所有提交压缩为一个。 For small features, this makes sense as it condenses it down into a single package.对于小功能,这是有道理的,因为它将它压缩成一个包。 For larger features, it's probably not a great idea, especially if your individual commits are already atomic.对于较大的功能,这可能不是一个好主意,特别是如果您的个人提交已经是原子的。 It really comes down to personal preference.这真的归结为个人喜好。

Github and Bitbucket (others?) Pull Requests - In case you're wondering how merge/rebase relates to Pull Requests, I recommend following all the above steps up until you're ready to merge back to master. Github 和 Bitbucket(其他?)拉取请求- 如果您想知道合并/rebase 与拉取请求的关系,我建议您按照上述所有步骤进行操作,直到您准备好合并回 master。 Instead of manually merging with git, you just accept the PR.无需手动与 git 合并,您只需接受 PR。 Note that this will not do a squash merge (at least not by default), but non-squash, non-fast-forward is the accepted merge convention in the Pull Request community (as far as I know).请注意,这不会进行压缩合并(至少默认情况下不会),但非压缩、非快进是 Pull Request 社区中公认的合并约定(据我所知)。 Specifically, it works like this:具体来说,它是这样工作的:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git push # May need to force push
...submit PR, wait for a review, make any changes requested for the PR
git rebase master
git push # Will probably need to force push (-f), due to previous rebases from master
...accept the PR, most likely also deleting the feature branch in the process
git checkout master
git branch -d my_new_feature
git remote prune origin

I've come to love Git and never want to go back to SVN.我已经爱上了 Git,再也不想回到 SVN。 If you're struggling, just stick with it and eventually you'll see the light at the end of the tunnel.如果你在挣扎,坚持下去,最终你会看到隧道尽头的曙光。

TL;DR TL; 博士

A git rebase workflow does not protect you from people who are bad at conflict resolution or people who are used to a SVN workflow, like suggested in Avoiding Git Disasters: A Gory Story . git rebase 工作流并不能保护您免受不擅长解决冲突的人或习惯于 SVN 工作流的人的侵害,就像避免 Git 灾难:血腥的故事 中所建议的那样。 It only makes conflict resolution more tedious for them and makes it harder to recover from bad conflict resolution.它只会让他们更乏味地解决冲突,并且更难从糟糕的冲突解决中恢复过来。 Instead, use diff3 so that it's not so difficult in the first place.相反,使用 diff3 以便它首先不那么困难。


Rebase workflow is not better for conflict resolution! Rebase 工作流程并不适合解决冲突!

I am very pro-rebase for cleaning up history.我非常赞成清理历史记录。 However if I ever hit a conflict, I immediately abort the rebase and do a merge instead!但是,如果我遇到冲突,我会立即中止变基并进行合并! It really kills me that people are recommending a rebase workflow as a better alternative to a merge workflow for conflict resolution (which is exactly what this question was about).人们推荐 rebase 工作流作为解决冲突的合并工作流的更好替代方案,这真的让我感到震惊(这正是这个问题的内容)。

If it goes "all to hell" during a merge, it will go "all to hell" during a rebase, and potentially a lot more hell too!如果它在合并过程中“彻底下地狱”,那么在 rebase 期间它将“彻底下地狱”,并且可能还会有更多的地狱! Here's why:原因如下:

Reason #1: Resolve conflicts once, instead of once for each commit原因 #1:解决冲突一次,而不是每次提交都解决一次

When you rebase instead of merge, you will have to perform conflict resolution up to as many times as you have commits to rebase, for the same conflict!当您变基而不是合并时,对于相同的冲突,您必须执行与变基提交的次数一样多的冲突解决!

Real scenario真实场景

I branch off of master to refactor a complicated method in a branch.我从 master 分支来重构一个分支中的复杂方法。 My refactoring work is comprised of 15 commits total as I work to refactor it and get code reviews.我的重构工作总共包含 15 次提交,因为我正在努力重构它并获得代码审查。 Part of my refactoring involves fixing the mixed tabs and spaces that were present in master before.我的部分重构涉及修复 master 中存在的混合制表符和空格。 This is necessary, but unfortunately it will conflict with any change made afterward to this method in master.这是必要的,但不幸的是,它会与随后在 master 中对该方法所做的任何更改发生冲突。 Sure enough, while I'm working on this method, someone makes a simple, legitimate change to the same method in the master branch that should be merged in with my changes.果然,当我在研究这个方法时,有人对 master 分支中的相同方法进行了简单、合法的更改,该更改应该与我的更改合并。

When it's time to merge my branch back with master, I have two options:当需要将我的分支与 master 合并时,我有两个选择:

git merge: I get a conflict. git merge:我遇到了冲突。 I see the change they made to master and merge it in with (the final product of) my branch.我看到他们对 master 所做的更改并将其与我的分支(的最终产品)合并。 Done.完成。

git rebase: I get a conflict with my first commit. git rebase:我与第一次提交发生冲突。 I resolve the conflict and continue the rebase.我解决了冲突并继续 rebase。 I get a conflict with my second commit.我与第二次提交发生冲突。 I resolve the conflict and continue the rebase.我解决了冲突并继续 rebase。 I get a conflict with my third commit.我与第三次提交发生冲突。 I resolve the conflict and continue the rebase.我解决了冲突并继续 rebase。 I get a conflict with my fourth commit.我与第四次提交发生冲突。 I resolve the conflict and continue the rebase.我解决了冲突并继续 rebase。 I get a conflict with my fifth commit.我与第五次提交发生冲突。 I resolve the conflict and continue the rebase.我解决了冲突并继续 rebase。 I get a conflict with my sixth commit.我与第六次提交发生冲突。 I resolve the conflict and continue the rebase.我解决了冲突并继续 rebase。 I get a conflict with my seventh commit.我与第七次提交发生冲突。 I resolve the conflict and continue the rebase.我解决了冲突并继续 rebase。 I get a conflict with my eighth commit.我与第八次提交发生冲突。 I resolve the conflict and continue the rebase.我解决了冲突并继续 rebase。 I get a conflict with my ninth commit.我与第九次提交发生冲突。 I resolve the conflict and continue the rebase.我解决了冲突并继续 rebase。 I get a conflict with my tenth commit.我与第十次提交发生冲突。 I resolve the conflict and continue the rebase.我解决了冲突并继续 rebase。 I get a conflict with my eleventh commit.我与第十一次提交发生冲突。 I resolve the conflict and continue the rebase.我解决了冲突并继续 rebase。 I get a conflict with my twelfth commit.我与第十二次提交发生冲突。 I resolve the conflict and continue the rebase.我解决了冲突并继续 rebase。 I get a conflict with my thirteenth commit.我与第十三次提交发生冲突。 I resolve the conflict and continue the rebase.我解决了冲突并继续 rebase。 I get a conflict with my fourteenth commit.我与第十四次提交发生冲突。 I resolve the conflict and continue the rebase.我解决了冲突并继续 rebase。 I get a conflict with my fifteenth commit.我与第十五次提交发生冲突。 I resolve the conflict and continue the rebase.我解决了冲突并继续 rebase。

You have got to be kidding me if this is your preferred workflow.如果是您的首选工作流程,您一定是在开玩笑。 All it takes is a whitespace fix that conflicts with one change made on master, and every commit will conflict and must be resolved.所需要的只是一个与 master 上所做的更改冲突的空白修复,并且每次提交都会发生冲突并且必须解决。 And this is a simple scenario with only a whitespace conflict.这是一个只有空格冲突的简单场景。 Heaven forbid you have a real conflict involving major code changes across files and have to resolve that multiple times.天堂禁止您有涉及跨文件的主要代码更改的真正冲突,并且必须多次解决冲突。

With all the extra conflict resolution you need to do, it just increases the possibility that you will make a mistake .有了你需要做的所有额外的冲突解决,它只会增加你犯错误的可能性。 But mistakes are fine in git since you can undo, right?但是 git 中的错误没有问题,因为您可以撤消,对吗? Except of course...当然除了...

Reason #2: With rebase, there is no undo!原因#2:使用rebase,无法撤消!

I think we can all agree that conflict resolution can be difficult, and also that some people are very bad at it.我想我们都同意解决冲突可能很困难,而且有些人对此很不擅长。 It can be very prone to mistakes, which why it's so great that git makes it easy to undo!它很容易出错,这就是为什么 git 可以轻松撤消如此出色的原因!

When you merge a branch, git creates a merge commit that can be discarded or amended if the conflict resolution goes poorly.当您合并一个分支时,git 会创建一个合并提交,如果冲突解决效果不佳,可以丢弃或修改该提交。 Even if you have already pushed the bad merge commit to the public/authoritative repo, you can use git revert to undo the changes introduced by the merge and redo the merge correctly in a new merge commit.即使您已经将错误的合并提交推送到公共/权威存储库,您也可以使用git revert撤消合并引入的更改,并在新的合并提交中正确地重做合并。

When you rebase a branch, in the likely event that conflict resolution is done wrong, you're screwed.当你对一个分支进行rebase 时,如果冲突解决可能出错,你就完蛋了。 Every commit now contains the bad merge, and you can't just redo the rebase*.现在每个提交都包含错误的合并,您不能只重做 rebase*。 At best, you have to go back and amend each of the affected commits.充其量,您必须返回并修改每个受影响的提交。 Not fun.不好玩。

After a rebase, it's impossible to determine what was originally part of the commits and what was introduced as a result of bad conflict resolution.在 rebase 之后,无法确定提交的最初部分以及由于错误的冲突解决而引入的内容。

*It can be possible to undo a rebase if you can dig the old refs out of git's internal logs, or if you create a third branch that points to the last commit before rebasing. *如果您可以从 git 的内部日志中挖掘旧的 refs,或者如果您创建第三个分支指向 rebase 之前的最后一次提交,则可以撤消 rebase。

Take the hell out of conflict resolution: use diff3彻底解决冲突:使用 diff3

Take this conflict for example:以这个冲突为例:

<<<<<<< HEAD
TextMessage.send(:include_timestamp => true)
=======
EmailMessage.send(:include_timestamp => false)
>>>>>>> feature-branch

Looking at the conflict, it's impossible to tell what each branch changed or what its intent was.纵观冲突,无法判断每个分支更改了什么或其意图是什么。 This is the biggest reason in my opinion why conflict resolution is confusing and hard.这是我认为解决冲突令人困惑和困难的最大原因。

diff3 to the rescue! diff3 来救援!

git config --global merge.conflictstyle diff3

When you use the diff3, each new conflict will have a 3rd section, the merged common ancestor.当您使用 diff3 时,每个新冲突都会有一个第三部分,即合并的共同祖先。

<<<<<<< HEAD
TextMessage.send(:include_timestamp => true)
||||||| merged common ancestor
EmailMessage.send(:include_timestamp => true)
=======
EmailMessage.send(:include_timestamp => false)
>>>>>>> feature-branch

First examine the merged common ancestor.首先检查合并的共同祖先。 Then compare each side to determine each branch's intent.然后比较每一侧以确定每个分支的意图。 You can see that HEAD changed EmailMessage to TextMessage.可以看到 HEAD 将 EmailMessage 更改为 TextMessage。 Its intent is to change the class used to TextMessage, passing the same parameters.它的目的是改变用于 TextMessage 的类,传递相同的参数。 You can also see that feature-branch's intent is to pass false instead of true for the :include_timestamp option.您还可以看到 feature-branch 的意图是为 :include_timestamp 选项传递 false 而不是 true。 To merge these changes, combine the intent of both:要合并这些更改,请结合两者的意图:

TextMessage.send(:include_timestamp => false)

In general:一般来说:

  1. Compare the common ancestor with each branch, and determine which branch has the simplest change比较每个分支的共同祖先,并确定哪个分支的变化最简单
  2. Apply that simple change to the other branch's version of the code, so that it contains both the simpler and the more complex change将这个简单的更改应用到另一个分支的代码版本,以便它包含更简单和更复杂的更改
  3. Remove all the sections of conflict code other than the one that you just merged the changes together into删除所有冲突代码部分,而不是您刚刚将更改合并到一起的部分

Alternate: Resolve by manually applying the branch's changes替代:通过手动应用分支的更改来解决

Finally, some conflicts are terrible to understand even with diff3.最后,即使使用 diff3,一些冲突也很难理解。 This happens especially when diff finds lines in common that are not semantically common (eg. both branches happened to have a blank line at the same place!).这种情况尤其发生在 diff 发现语义上不常见的共同行时(例如,两个分支碰巧在同一个地方有一个空行!)。 For example, one branch changes the indentation of the body of a class or reorders similar methods.例如,一个分支更改了类主体的缩进或重新排序类似的方法。 In these cases, a better resolution strategy can be to examine the change from either side of the merge and manually apply the diff to the other file.在这些情况下,更好的解决策略可以是检查合并任一侧的更改并手动将差异应用到另一个文件。

Let's look at how we might resolve a conflict in a scenario where merging origin/feature1 where lib/message.rb conflicts.让我们看看在合并origin/feature1 lib/message.rb发生冲突的情况下,我们如何解决冲突。

  1. Decide whether our currently checked out branch ( HEAD , or --ours ) or the branch we're merging ( origin/feature1 , or --theirs ) is a simpler change to apply.确定我们当前签出的分支( HEAD--ours )还是我们正在合并的分支( origin/feature1--theirs )是一个更简单的应用更改。 Using diff with triple dot ( git diff a...b ) shows the changes that happened on b since its last divergence from a , or in other words, compare the common ancestor of a and b with b.使用带三点的 diff ( git diff a...b ) 显示自b上次与a发散以来发生的变化,或者换句话说,将 a 和 b 的共同祖先与 b 进行比较。

     git diff HEAD...origin/feature1 -- lib/message.rb # show the change in feature1 git diff origin/feature1...HEAD -- lib/message.rb # show the change in our branch
  2. Check out the more complicated version of the file.查看更复杂的文件版本。 This will remove all conflict markers and use the side you choose.这将删除所有冲突标记并使用您选择的一侧。

     git checkout --ours -- lib/message.rb # if our branch's change is more complicated git checkout --theirs -- lib/message.rb # if origin/feature1's change is more complicated
  3. With the complicated change checked out, pull up the diff of the simpler change (see step 1).签出复杂的更改后,拉起较简单更改的差异(请参阅步骤 1)。 Apply each change from this diff to the conflicting file.将此差异的每个更改应用到冲突文件。

"Conflicts" mean "parallel evolutions of a same content". “冲突”意味着“相同内容的平行演变”。 So if it goes "all to hell" during a merge, it means you have massive evolutions on the same set of files.因此,如果它在合并过程中“一败涂地”,则意味着您对同一组文件进行了大量演变。

The reason why a rebase is then better than a merge is that:变基比合并更好的原因是:

  • you rewrite your local commit history with the one of the master (and then reapply your work, resolving any conflict then)你用主人之一重写你的本地提交历史(然后重新应用你的工作,然后解决任何冲突)
  • the final merge will certainly be a "fast forward" one, because it will have all the commit history of the master, plus only your changes to reapply.最终的合并肯定是“快进”的,因为它将拥有 master 的所有提交历史,以及您要重新应用的更改。

I confirm that the correct workflow in that case (evolutions on common set of files) is rebase first, then merge .我确认在这种情况下正确的工作流程(公共文件集的演变)首先rebase ,然后是 merge

However, that means that, if you push your local branch (for backup reason), that branch should not be pulled (or at least used) by anyone else (since the commit history will be rewritten by the successive rebase).但是,这意味着,如果您推送本地分支(出于备份原因),则其他任何人都不应该拉取(或至少使用)该分支(因为提交历史将被连续的 rebase 重写)。


On that topic (rebase then merge workflow), barraponto mentions in the comments two interesting posts, both from randyfay.com :关于这个话题(rebase 然后合并工作流程), barraponto在评论中提到了两个有趣的帖子,都来自randyfay.com

Using this technique, your work always goes on top of the public branch like a patch that is up-to-date with current HEAD .使用这种技术,您的工作始终在公共分支之上,就像与当前HEAD的补丁一样。

(a similar technique exists for bazaar ) (类似的技术存在于 bazaar

In my workflow, I rebase as much as possible (and I try to do it often. Not letting the discrepancies accumulate drastically reduces the amount and the severity of collisions between branches).在我的工作流程中,我尽可能多地变基(并且我尝试经常这样做。不让差异累积会大大减少分支之间冲突的数量和严重程度)。

However, even in a mostly rebase-based workflow, there is a place for merges.然而,即使在大部分基于 rebase 的工作流中,也有合并的地方。

Recall that merge actually creates a node that has two parents.回想一下,merge 实际上创建了一个有两个父节点的节点。 Now consider the following situation: I have two independent feature brances A and B, and now want to develop stuff on feature branch C which depends on both A and B, while A and B are getting reviewed.现在考虑以下情况:我有两个独立的功能分支 A 和 B,现在想在依赖于 A 和 B 的功能分支 C 上开发东西,而 A 和 B 正在接受审查。

What I do then, is the following:然后我要做的是:

  1. Create (and checkout) branch C on top of A.在 A 之上创建(并结帐)分支 C。
  2. Merge it with B与 B 合并

Now branch C includes changes from both A and B, and I can continue developing on it.现在分支 C 包含来自 A 和 B 的更改,我可以继续开发它。 If I do any change to A, then I reconstruct the graph of branches in the following way:如果我对 A 进行任何更改,那么我会按以下方式重建分支图:

  1. create branch T on the new top of A在 A 的新顶部创建分支 T
  2. merge T with B将 T 与 B 合并
  3. rebase C onto T将 C 变基到 T
  4. delete branch T删除分支 T

This way I can actually maintain arbitrary graphs of branches, but doing something more complex than the situation described above is already too complex, given that there is no automatic tool to do the rebasing when the parent changes.这样我实际上可以维护任意的分支图,但是做一些比上面描述的情况更复杂的事情已经太复杂了,因为没有自动工具来在父更改时进行重新定位。

DO NOT use git push origin --mirror UNDER ALMOST ANY CIRCUMSTANCE.不要在几乎任何情况下使用 git push origin --mirror 。

It does not ask if you're sure you want to do this, and you'd better be sure, because it will erase all of your remote branches that are not on your local box.它不会询问您是否确定要执行此操作,您最好确定一下,因为它会删除所有不在本地机器上的远程分支。

http://twitter.com/dysinger/status/1273652486 http://twitter.com/dysinger/status/1273652486

In your situation I think your partner is correct.在你的情况下,我认为你的伴侣是正确的。 What's nice about rebasing is that to the outsider your changes look like they all happened in a clean sequence all by themselves.变基的好处在于,对于局外人来说,您的更改看起来就像它们全部以干净的顺序发生。 This means这意味着

  • your changes are very easy to review您的更改很容易查看
  • you can continue to make nice, small commits and yet you can make sets of those commits public (by merging into master) all at once你可以继续做出漂亮的小提交,但你可以一次性公开这些提交的集合(通过合并到 master 中)
  • when you look at the public master branch you'll see different series of commits for different features by different developers but they won't all be intermixed当您查看公共主分支时,您会看到不同开发人员针对不同功能的不同系列提交,但它们不会全部混合

You can still continue to push your private development branch to the remote repository for the sake of backup but others should not treat that as a "public" branch since you'll be rebasing.为了备份,您仍然可以继续将您的私有开发分支推送到远程存储库,但其他人不应将其视为“公共”分支,因为您将进行变基。 BTW, an easy command for doing this is git push --mirror origin .顺便说一句,执行此操作的一个简单命令是git push --mirror origin

The article Packaging software using Git does a fairly nice job explaining the trade offs in merging versus rebasing. 使用 Git 打包软件一文很好地解释了合并与变基的权衡。 It's a little different context but the principals are the same -- it basically comes down to whether your branches are public or private and how you plan to integrate them into the mainline.这是一个稍微不同的上下文,但主体是相同的——它基本上归结为您的分支是公共的还是私有的,以及您计划如何将它们集成到主线中。

I have one question after reading your explanation: Could it be that you never did a看了你的解释后,我有一个问题:难道你从来没有做过

git checkout master
git pull origin
git checkout my_new_feature

before doing the 'git rebase/merge master' in your feature branch?在您的功能分支中执行“git rebase/merge master”之前?

Because your master branch won't update automatically from your friend's repository.因为您的主分支不会从您朋友的存储库自动更新。 You have to do that with the git pull origin .你必须用git pull origin来做到这一点。 Ie maybe you would always rebase from a never-changing local master branch?也就是说,也许你总是从一个永不改变的本地 master 分支变基? And then come push time, you are pushing in a repository which has (local) commits you never saw and thus the push fails.然后是推送时间,您正在推送一个存储库,该存储库具有您从未见过的(本地)提交,因此推送失败。

Anyway, I was following my workflow on a recent branch, and when I tried to merge it back to master, it all went to hell.无论如何,我在最近的一个分支上遵循我的工作流程,当我试图将它合并回 master 时,一切都变得糟糕透了。 There were tons of conflicts with things that should have not mattered.与本应无关紧要的事情发生了大量冲突。 The conflicts just made no sense to me.这些冲突对我来说毫无意义。 It took me a day to sort everything out, and eventually culminated in a forced push to the remote master, since my local master has all conflicts resolved, but the remote one still wasn't happy.我花了一天时间整理好一切,最终以强制推送到远程 master 达到高潮,因为我的本地 master 已经解决了所有冲突,但是远程 master 仍然不高兴。

In neither your partner's nor your suggested workflows should you have come across conflicts that didn't make sense.在您的合作伙伴和您建议的工作流程中,您都不应该遇到没有意义的冲突。 Even if you had, if you are following the suggested workflows then after resolution a 'forced' push should not be required.即使您有,如果您遵循建议的工作流程,那么在解决问题后,不应需要“强制”推送。 It suggests that you haven't actually merged the branch to which you were pushing, but have had to push a branch that wasn't a descendent of the remote tip.这表明您实际上并没有合并您要推送的分支,而是不得不推送一个不是远程提示的后代的分支。

I think you need to look carefully at what happened.我认为你需要仔细看看发生了什么。 Could someone else have (deliberately or not) rewound the remote master branch between your creation of the local branch and the point at which you attempted to merge it back into the local branch?其他人是否可以(有意或无意)在您创建本地分支和您尝试将其合并回本地分支之间重绕远程主分支?

Compared to many other version control systems I've found that using Git involves less fighting the tool and allows you to get to work on the problems that are fundamental to your source streams.与许多其他版本控制系统相比,我发现使用 Git 涉及的工具较少,并允许您开始处理对源流至关重要的问题。 Git doesn't perform magic, so conflicting changes cause conflicts, but it should make it easy to do the write thing by its tracking of commit parentage. Git 不会执行魔术,因此冲突的更改会导致冲突,但它应该通过跟踪提交的出身来使写入操作变得容易。

"Even if you're a single developer with only a few branches, it's worth it to get in the habit of using rebase and merge properly. The basic work pattern will look like: “即使你是一个只有几个分支的单一开发者,养成正确使用 rebase 和 merge 的习惯也是值得的。基本的工作模式将如下所示:

  • Create new branch B from existing branch A从现有分支 A 创建新分支 B

  • Add/commit changes on branch B在分支 B 上添加/提交更改

  • Rebase updates from branch A来自分支 A 的 Rebase 更新

  • Merge changes from branch B onto branch A"将分支 B 的更改合并到分支 A"

https://www.atlassian.com/git/tutorials/merging-vs-rebasing/https://www.atlassian.com/git/tutorials/merging-vs-rebasing/

From what I have observed, git merge tends to keep the branches separate even after merging, whereas rebase then merge combines it into one single branch.根据我的观察, git merge 即使在合并后也倾向于保持分支分开,而 rebase then merge 将其合并为一个分支。 The latter comes out much cleaner, whereas in the former, it would be easier to find out which commits belong to which branch even after merging.后者更简洁,而在前者中,即使在合并后也更容易找出哪些提交属于哪个分支。

With Git there is no “correct” workflow. Git 没有“正确”的工作流程。 Use whatever floats your boat.使用任何漂浮你的船的东西。 However, if you constantly get conflicts when merging branches maybe you should coordinate your efforts better with your fellow developer(s)?但是,如果您在合并分支时经常遇到冲突,也许您应该与其他开发人员更好地协调您的工作? Sounds like the two of you keep editing the same files.听起来你们两个一直在编辑相同的文件。 Also, watch out for whitespace and subversion keywords (ie, “$Id$” and others).另外,注意空格和颠覆关键字(即“$Id$”等)。

I only use rebase workflow, because it is visually clearer(not only in GitKraken, but also in Intellij and in gitk , but I recommend the first one most): you have a branch, it originates from the master, and it goes back to master.我只使用 rebase 工作流,因为它在视觉上更清晰(不仅在 GitKraken 中,而且在 Intellij 和gitk ,但我最推荐第一个):你有一个分支,它起源于 master,它回到主人。 When the diagram is clean and beautiful, you will know that nothing goes to hell, ever .当图表干净漂亮时,你就会知道没有什么是地狱,永远

在此处输入图片说明

My workflow is almost the same from yours, but with only one small difference: I squash commits into one in my local branch before rebase my branch onto the latest changes on master , because:我的工作流程几乎与你相同,但只有一个小的差异:我squash之前提交到一个在我的本地分支rebase我的分支上的最新变化master ,这是因为:

rebase works on basis of each commit rebase基于每次提交工作

which means, if you have 15 commits changing the same line as master does, you have to check 15 times if you don't squash, but what matters is the final result, right?这意味着,如果您有 15 次提交更改了与master相同的行,那么如果您不压缩,则必须检查 15 次,但重要的是最终结果,对吗?

So, the whole workflow is:所以,整个工作流程是:

  1. Checkout to master and pull to ensure that you have the latest version Checkout to master和 pull 以确保您拥有最新版本

  2. From there, create a new branch从那里,创建一个新分支

  3. Do your work there, you can freely commit several times, and push to remote, no worries, because it is your branch.在那里做你的工作,你可以自由提交几次,然后推送到远程,不用担心,因为它是你的分支。

  4. If someone tells you, "hey, my PR/MR is approved, now it is merged to master", you can fetch them/pull them.如果有人告诉你,“嘿,我的 PR/MR 被批准了,现在它合并到 master 了”,你可以 fetch 他们/pull 他们。 You can do it anytime, or in step 6.您可以随时执行此操作,也可以在步骤 6 中执行此操作。

  5. After doing all your work, commit them, and if you have several commits, squash them (they are all your work, and how many times you change a line of code does not matter; the only important thing is the final version).完成所有工作后,提交它们,如果您有多次提交,则压缩它们(它们都是您的工作,您更改一行代码多少次无关紧要;唯一重要的是最终版本)。 Push it or not, it doesn't matter.推不推,无所谓。

  6. Checkout to master , pull again to ensure you have the latest master in local.结帐到master ,再次pull以确保您在本地拥有最新的master Your diagram should be similar to this:您的图表应该类似于:

在此处输入图片说明

As you can see, you are on your local branch, which originates from an outdated status on master , while master (both local and remote) has moved forward with changes of your colleague.如您所见,您在本地分支上,这源于master上的过时状态,而master (本地和远程)随着您同事的变化而前进。

  1. Checkout back to your branch, and rebase to master.结帐回到您的分支,并重新设置为 master。 You will now have one commit only, so you solve the conflicts only once .(And in GitKraken, you only have to drag your branch on to master and choose "Rebase"; another reason why I like it.) After that, you will be like:您现在将只有一次提交,因此您只需解决一次冲突。(在 GitKraken 中,您只需将您的分支拖到master并选择“Rebase”;我喜欢它的另一个原因。)在那之后,您将就像:

在此处输入图片说明

  1. So now, you have all the changes on the latest master , combined with changes on your branch.所以现在,您拥有最新master上的所有更改,以及您的分支上的更改。 You can now push to your remote, and, if you have pushed before, you will have to force push ;您现在可以推送到您的遥控器,如果您之前推送过,则必须强制推送 Git will tell you that you cannot simply fast forward. Git 会告诉你,你不能简单地快进。 That's normal, because of the rebase, you have changed the start point of your branch.这是正常的,因为 rebase,你已经改变了你的分支的起点。 But you should not fear: wisely use the force, but without fear .但你不应该害怕:明智地使用力量,但不要害怕 In the end, the remote is also your branch so you do not affect master even if you do something wrong.最后,远程也是你的分支,所以即使你做错了,你也不会影响master

  2. Create PR/MR and wait until it is approved, so master will have your contribution.创建 PR/MR 并等待它被批准,这样master就会有你的贡献。 Congrats!恭喜! So you can now checkout to master , pull your changes, and delete your local branch in order to clean up the diagram .因此,您现在可以结帐到master ,拉取更改并删除本地分支以清理图表 The remote branch should be deleted too, if this is not done when you merge it into master.远程分支也应该被删除,如果你在将它合并到 master 时没有这样做。

The final diagram is clean and clear again:最后的图表又干净又清晰:

在此处输入图片说明

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

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