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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/457927/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 06:06:07  来源:igfitidea点击:

Git workflow and rebase vs merge questions

gitversion-controlgit-mergegit-rebase

提问by Micah

I've been using Git now for a couple of months on a project with one other developer. I have several years of experience with SVN, so I guess I bring a lot of baggage to the relationship.

我已经在与另一位开发人员的一个项目中使用 Git 几个月了。我在SVN有几年的经验,所以我想我给这段关系带来了很多包袱。

I have heard that Git is excellent for branching and merging, and so far, I just don't see it. 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.

我听说 Git 在分支和合并方面非常出色,到目前为止,我只是没有看到。当然,分支非常简单,但是当我尝试合并时,一切都变得很糟糕。现在,我已经习惯了 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. For example, here's the workflow that he's laid down:

我的搭档告诉我,我的问题源于我想不经意地合并,并且在许多情况下我应该使用 rebase 而不是合并。例如,这是他制定的工作流程:

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. Important to note is that the branch always stays local.

本质上,创建一个功能分支,始终从 master 到分支,然后从分支合并回 master。需要注意的重要一点是分支始终保持在本地。

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. My gut feeling is that what I'm trying to do is not an advanced setup, so rebase should be unnecessary. I've even perused the new Pragmatic Programming book on Git, and they cover merge extensively and barely mention rebase.

我对合并而不是 rebase 的推理是合并似乎是标准的,而 rebase 似乎是一个高级功能。我的直觉是我想要做的不是高级设置,所以 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. 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 已经解决了所有冲突,但是远程 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 mergesinstead of raw merges. (Note: This is controversial, see below.) This allows us to turn our entire feature branch into a single commit on 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. So, this is what we do:

事实上,我们的工作流程有点不同,因为我们倾向于进行压缩合并而不是原始合并。(注意:这是有争议的,见下文。)这允许我们将我们的整个功能分支变成对 master 的单个提交。然后我们删除我们的功能分支。这允许我们在 master 上逻辑地构建我们的提交,即使它们在我们的分支上有点混乱。所以,这就是我们要做的:

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. 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.

Squash Merge Controversy- 正如一些评论者所指出的,Squash 合并将丢弃您的功能分支上的所有历史记录。顾名思义,它将所有提交压缩为一个。对于小功能,这是有道理的,因为它将它压缩成一个包。对于较大的功能,这可能不是一个好主意,特别是如果您的个人提交已经是原子的。这真的归结为个人喜好。

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. Instead of manually merging with git, you just accept the 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). Specifically, it works like this:

Github 和 Bitbucket(其他?)拉取请求- 如果您想知道合并/rebase 与拉取请求的关系,我建议您按照上述所有步骤进行操作,直到您准备好合并回 master。无需手动与 git 合并,您只需接受 PR。请注意,这不会进行压缩合并(至少默认情况下不会),但非压缩、非快进是 Pull Request 社区中公认的合并约定(据我所知)。具体来说,它是这样工作的:

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. If you're struggling, just stick with it and eventually you'll see the light at the end of the tunnel.

我已经爱上了 Git,再也不想回到 SVN。如果你在挣扎,坚持下去,最终你会看到隧道尽头的曙光。

采纳答案by VonC

"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), barrapontomentions 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

回答by Edward Anderson

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. 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.

git rebase 工作流并不能保护您免受不擅长解决冲突的人或习惯于 SVN 工作流的人的侵害,就像避免 Git 灾难:血腥故事 中所建议的那样。它只会让他们更乏味地解决冲突,并且更难从糟糕的冲突解决中恢复过来。相反,使用 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! Here's why:

如果它在合并过程中“彻底下地狱”,那么在 rebase 期间它将“彻底下地狱”,并且可能还会有更多的地狱!原因如下:

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

When it's time to merge my branch back with master, I have two options:

当需要将我的分支与 master 合并时,我有两个选择:

git merge:I get a conflict. I see the change they made to master and merge it in with (the final product of) my branch. Done.

git merge:我遇到了冲突。我看到他们对 master 所做的更改并将其与我的分支(的最终产品)合并。完毕。

git rebase:I get a conflict with my firstcommit. I resolve the conflict and continue the rebase. I get a conflict with my secondcommit. I resolve the conflict and continue the rebase. I get a conflict with my thirdcommit. I resolve the conflict and continue the rebase. I get a conflict with my fourthcommit. I resolve the conflict and continue the rebase. I get a conflict with my fifthcommit. I resolve the conflict and continue the rebase. I get a conflict with my sixthcommit. I resolve the conflict and continue the rebase. I get a conflict with my seventhcommit. I resolve the conflict and continue the rebase. I get a conflict with my eighthcommit. I resolve the conflict and continue the rebase. I get a conflict with my ninthcommit. I resolve the conflict and continue the rebase. I get a conflict with my tenthcommit. I resolve the conflict and continue the rebase. I get a conflict with my eleventhcommit. I resolve the conflict and continue the rebase. I get a conflict with my twelfthcommit. I resolve the conflict and continue the rebase. I get a conflict with my thirteenthcommit. I resolve the conflict and continue the rebase. I get a conflict with my fourteenthcommit. I resolve the conflict and continue the rebase. I get a conflict with my fifteenthcommit. I resolve the conflict and continue the rebase.

git rebase:我与第一次提交发生冲突。我解决了冲突并继续 rebase。我与第二次提交发生冲突。我解决了冲突并继续 rebase。我与第三次提交发生冲突。我解决了冲突并继续 rebase。我与第四次提交发生冲突。我解决了冲突并继续 rebase。我与第五次提交发生冲突。我解决了冲突并继续 rebase。我与第六次提交发生冲突。我解决了冲突并继续 rebase。我和我的第七个发生冲突犯罪。我解决了冲突并继续 rebase。我与第八次提交发生冲突。我解决了冲突并继续 rebase。我与第九次提交发生冲突。我解决了冲突并继续 rebase。我与第十次提交发生冲突。我解决了冲突并继续 rebase。我与第十一次提交发生冲突。我解决了冲突并继续 rebase。我与第十二次提交发生冲突。我解决了冲突并继续 rebase。我与第十三次提交发生冲突。我解决了冲突并继续 rebase。我和我的十四岁有冲突犯罪。我解决了冲突并继续 rebase。我与第十五次提交发生冲突。我解决了冲突并继续 rebase。

You have got to be kidding me if thisis 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. And this is a simplescenario with only a whitespace conflict. Heaven forbid you have a real conflict involving major code changes across files and have to resolve thatmultiple times.

如果是您的首选工作流程,您一定是在开玩笑。所需要的只是一个与 master 上所做的更改冲突的空白修复,并且每次提交都会发生冲突并且必须解决。这是一个只有空格冲突的简单场景。天堂禁止您有涉及跨文件的主要代码更改的真正冲突,并且必须多次解决冲突。

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? Except of course...

有了你需要做的所有额外的冲突解决,它只会增加你犯错误的可能性。但是 git 中的错误没有问题,因为您可以撤消,对吗?当然除了...

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 mergea branch, git creates a merge commit that can be discarded or amended if the conflict resolution goes poorly. Even if you have already pushed the bad merge commit to the public/authoritative repo, you can use git revertto undo the changes introduced by the merge and redo the merge correctly in a new merge commit.

当您合并一个分支时,git 会创建一个合并提交,如果冲突解决效果不佳,可以丢弃或修改该提交。即使您已经将错误的合并提交推送到公共/权威存储库,您也可以使用git revert撤消合并引入的更改并在新的合并提交中正确重做合并。

When you rebasea branch, in the likely event that conflict resolution is done wrong, you're screwed. Every commit now contains the bad merge, and you can't just redo the rebase*. At best, you have to go back and amend each of the affected commits. Not fun.

当你对一个分支进行rebase 时,如果冲突解决可能出错,你就完蛋了。现在每个提交都包含错误的合并,您不能只重做 rebase*。充其量,您必须返回并修改每个受影响的提交。不好玩。

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. Its intent is to change the class used to TextMessage, passing the same parameters. You can also see that feature-branch's intent is to pass false instead of true for the :include_timestamp option. To merge these changes, combine the intent of both:

首先检查合并的共同祖先。然后比较每一侧以确定每个分支的意图。可以看到 HEAD 将 EmailMessage 更改为 TextMessage。它的目的是改变用于 TextMessage 的类,传递相同的参数。您还可以看到 feature-branch 的意图是为 :include_timestamp 选项传递 false 而不是 true。要合并这些更改,请结合两者的意图:

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
  1. 比较每个分支的共同祖先,并确定哪个分支的变化最简单
  2. 将这个简单的更改应用到另一个分支的代码版本,以便它包含更简单和更复杂的更改
  3. 删除所有冲突代码部分,而不是您刚刚将更改合并到一起的部分

Alternate: Resolve by manually applying the branch's changes

替代:通过手动应用分支的更改来解决

Finally, some conflicts are terrible to understand even with 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!). 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.

最后,即使使用 diff3,一些冲突也很难理解。这种情况尤其发生在 diff 发现语义上不常见的共同行时(例如,两个分支碰巧在同一个地方有一个空行!)。例如,一个分支更改了类主体的缩进或重新排序类似的方法。在这些情况下,更好的解决策略可以是检查合并任一侧的更改并手动将差异应用到另一个文件。

Let's look at how we might resolve a conflict in a scenario where merging origin/feature1where lib/message.rbconflicts.

让我们看看我们怎样才能在一个场景合并解决冲突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. Using diff with triple dot (git diff a...b) shows the changes that happened on bsince its last divergence from a, or in other words, compare the common ancestor of a and b with 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). Apply each change from this diff to the conflicting file.

  1. 确定我们当前签出的分支 ( HEAD, or --ours) 还是我们正在合并的分支 ( origin/feature1, or --theirs) 是一个更简单的应用更改。使用带有三点 ( git diff a...b) 的diff显示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. 查看更复杂的文件版本。这将删除所有冲突标记并使用您选择的一侧。

    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. 签出复杂的更改后,拉起较简单更改的差异(请参阅步骤 1)。将此差异的每个更改应用到冲突文件。

回答by Alex Gontmakher

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. 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.

回想一下,merge 实际上创建了一个有两个父节点的节点。现在考虑以下情况:我有两个独立的功能分支 A 和 B,现在想在依赖 A 和 B 的功能分支 C 上开发东西,而 A 和 B 正在接受。

What I do then, is the following:

然后我要做的是:

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

Now branch C includes changes from both A and B, and I can continue developing on it. If I do any change to A, then I reconstruct the graph of branches in the following way:

现在分支 C 包含来自 A 和 B 的更改,我可以继续开发它。如果我对 A 进行任何更改,那么我会按以下方式重建分支图:

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

这样我实际上可以维护任意的分支图,但是做一些比上面描述的情况更复杂的事情已经太复杂了,因为没有自动工具来在父更改时进行重新定位。

回答by Scott Brown

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

回答by knweiss

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 yourmaster branch won't update automatically from your friend's repository. You have to do that with the git pull origin. I.e. maybe you would always rebase from a never-changing local master branch? And then come push time, you are pushing in a repository which has (local) commits you never saw and thus the push fails.

因为您的主分支不会从您朋友的存储库自动更新。你必须用git pull origin. 也就是说,也许你总是从一个永不改变的本地主分支变基?然后是推送时间,您正在推送一个存储库,该存储库具有您从未见过的(本地)提交,因此推送失败。

回答by Pat Notz

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
  • 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
  • 您的更改很容易查看
  • 你可以继续做出漂亮的小提交,但你可以一次性公开这些提交的集合(通过合并到 master)
  • 当您查看公共主分支时,您会看到不同开发人员针对不同功能的不同系列提交,但它们不会全部混合

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 Gitdoes a fairly nice job explaining the trade offs in merging versus rebasing. 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.

本文使用Git包装软件做了相当不错的工作,解释在合并与垫底的权衡。这是一个稍微不同的上下文,但主体是相同的——它基本上归结为您的分支是公共的还是私有的,以及您计划如何将它们集成到主线中。

回答by CB Bailey

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. 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 已经解决了所有冲突,但是远程 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 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 涉及的工具较少,并且允许您开始处理对源流至关重要的问题。Git 不会执行魔术,因此冲突的更改会导致冲突,但它应该通过跟踪提交的出身来使写入操作变得容易。

回答by Pepe

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. 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.

根据我的观察, git merge 即使在合并后也倾向于保持分支分开,而 rebase then merge 将其合并为一个分支。后者更简洁,而在前者中,即使在合并后也更容易找出哪些提交属于哪个分支。

回答by Rakka Rage

"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

  • Add/commit changes on branch B

  • Rebase updates from branch A

  • Merge changes from branch B onto branch A"

  • 从现有分支 A 创建新分支 B

  • 在分支 B 上添加/提交更改

  • 来自分支 A 的 Rebase 更新

  • 将分支 B 的更改合并到分支 A"

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

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

回答by Bombe

With Git there is no “correct” workflow. 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 (i.e., “$Id$” and others).

Git 没有“正确”的工作流程。使用任何漂浮你的船的东西。但是,如果您在合并分支时经常遇到冲突,也许您应该与其他开发人员更好地协调您的工作?听起来你们两个一直在编辑相同的文件。另外,注意空格和颠覆关键字(即“$Id$”等)。