使用 Git 或 TortoiseGit 合并一系列修订
我正在尝试使用 git 或 TortoiseGit 将存储库主分支中所做的更改合并到开发分支中。我知道我可以使用 git pull 或 合并,但这会一次将太多更改合并到开发分支中,并使解决冲突变得更加困难。
如果我使用 SVN 或 TortoiseSVN,我可以一次一点点地合并来自主干的更改,而不是一次全部合并,使用一系列修订进行合并。我可以用 git 或 TortoiseGit 做类似的事情吗?也就是说,我可以将一系列修订合并到我的开发分支中,而不是一次合并所有更改吗?
I'm trying to merge changes made in the master branch of my repository into a development branch using git or TortoiseGit. I know that I can just use either a git pull or a merge, but this merges in too many changes into the development branch at once, and makes it more difficult to resolve conflicts.
If I was using SVN or TortoiseSVN, I could just merge in changes from the main trunk a little bit at a time instead of all at once, using a range of revisions for the merge. Could I do something similar with git or TortoiseGit? That is, could I merge in a range of revisions into my development branch instead of merging in all changes at once?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
摘要
从命令行使用 Git,您可以将
feature
重新设置为master
:警告:不要不小心强制推送重新设置基准(重写)提交/分支如果您与其他人共享这些提交/分支,因为您将导致与他们基于旧版本的提交/分支的任何更改发生冲突。在(非常)小的团队中可以以这种方式工作,但需要良好的协调。
您还可以使用
--onto
标志来指定范围,其中
是范围的唯一开始:最后,
cherry-pick
实际上可以接受一系列参数。签出master
分支后:详细说明
我最初的目标是能够将我的
feature
分支与master
中所做的更改同步,但我不想只合并到master
中,因为它导致了太多必须立即解决的冲突。我只是希望能够逐渐合并更改,这样我就可以将冲突分解为更小、更易于管理的部分。输入 git rebase
最好的方法实际上是
git rebase
。事实上,这很完美。它将执行的操作是获取我在feature
中所做的所有提交,并以相同的顺序制作这些提交的副本,除了它在目标分支的最新版本之上重新提交它们,在我的例子中,它是master
。这很重要,原因有二:
这相当于将
master
合并到feature
中。为什么?因为feature
基本上是在master
的最新版本之上重新创建的……所有master
的提交现在都存在于的历史记录中>feature
,包括master
中feature
尚未提交的提交。Git 按顺序一次一个重新应用提交,因此如果存在任何冲突,它们会以几个更小、更易于管理的部分引入到流程中,一次一个,这正是我希望做的!
这就是它在视觉上的样子(示例改编自 官方 Linux 内核 Git 文档):
在上面的示例中,自从我分支
以来,在
关闭它。我想要做的是将这些更改同步到master
上提交了F
和G
>功能feature
:现在我的分支如下所示:
提交
A
到C
已重新创建为A'
到C'
位于最新版本的master
之上。旧的提交仍然存在于 Git 存储库中,但由于没有分支指针引用它们,因此它们最终将被 Git 进行垃圾收集。Summary
Using Git from the command line, you can rebase
feature
ontomaster
:Warning: do not carelessly force push rebased (rewritten) commits/branches if you share those commits/branches with other people, because you'll cause conflicts with any changes they've based off the older versions of the commits/branches. It's possible to work this way in (very) small teams, but good coordination is required.
You can also use the
--onto
flag to specify a range, where<start commit>
is the exclusive start of the range:Finally,
cherry-pick
can actually accept a range of arguments. With themaster
branch checked-out:Detailed Explanation
My original goal was to be able to sync up my
feature
branch with changes made inmaster
, but I didn't want to just merge inmaster
because it caused too many conflicts that had to be resolved at once. I just wanted to be able to merge in the changes gradually, so I could resolve the conflicts in smaller, more manageable pieces.Enter
git rebase
The best way to do that is in fact
git rebase
. It's perfect, actually. What it will do is take all the commits that I've made infeature
and make copies of those commits in the same order, except that it recommits them on top of the latest revision of the target branch, which, in my case, wasmaster
.This is significant for two reasons:
This is equivalent to merging
master
intofeature
. Why? Becausefeature
is basically recreated on top of the most recent revisions ofmaster
…all ofmaster
's commits now exist in the history offeature
, including the commits inmaster
thatfeature
didn't have yet.Git reapplies the commits one at a time, in order, so if there are any conflicts, they're introduced into the process in several smaller, more manageable pieces, one at a time, which is exactly what I was hoping to do!
This is what it looks like visually (examples adapted from official Linux Kernel Git documentation):
In the above example, commits
F
andG
where made onmaster
since I branchedfeature
off of it. What I want to do is sync those changes tofeature
:Now my branches look like this:
Commits
A
throughC
have been recreated asA'
throughC'
on top of the latest version ofmaster
. The old commits still exist in the Git repo, but because there's no branch pointer that's referencing them, they'll eventually be garbage collected by Git.是的,你可以这样做。假设您的存储库如下所示:
您想要将
feature
分支合并到master
中,但那里有很多提交。相反,让我们创建一个新分支tmp
,它指向feature
上的早期提交:现在
tmp
指向c2
。现在我们可以只将提交c1...c2
合并到 master 中,而不考虑c3...c100
:现在,将
tmp
移动到下一批提交(我们需要-f
来强制
它,因为tmp
已经存在)。例如,如果我们现在想移动到 c6,请使用:重复此操作,直到您要合并的所有提交都在其中。
Yes, you can do that. Let's say that your repository looks like this:
You want to merge the
feature
branch intomaster
, but there's a lot of commits there. Instead, let's make a new branchtmp
that points to an earlier commit alongfeature
:Now
tmp
points toc2
. Now we can merge just the commitsc1...c2
into master without regard toc3...c100
:Now, move
tmp
to the next batch of commits (we need-f
toforce
it, sincetmp
already exists). For instance, if we want to move toc6
now, use that:Repeat this until all the commits you want to merge are in.
注意:约翰的答案是正确的;这只是基于评论中的后续问题的进一步解释 - 我只是需要更多空间:)
不,在 Git 中,历史总是相连的。因此,如果您将一个提交合并到 master 中,并且在
master
的共同祖先和该提交之间存在更多提交,那么这些提交将被完全保留。与 SVN 不同,Git 保留对先前提交的完整引用(具有无限数量的指针,因此您可以一次合并多个分支)。所以最后,你总是会看到一个分支从哪里开始,它发生了什么,之间合并了什么,以及它在哪里合并回主分支——只有名称(或者更确切地说标签 ) 分支不会保留(自动合并文本除外,如果这算 ^^)。因此,您的历史记录可以如下所示:
假设您想要将
B9
(分支br
上的 HEAD 提交)合并回M
中,其中是master
分支所指向的位置。通过直接合并,您将得到以下结果(#
是合并提交):因此,即使您删除分支指针
br
,您仍然可以看到发生在那个单独的分支。或者,如果您想分多个步骤进行合并,您可以像这样轻松地合并它:
同样,您始终可以回顾整个树,以及在分支上进行的所有单独提交 - 即使您删除了分支(同样,只是删除了指针)。
因此,也许这个解释也会告诉您,您不一定需要以如此小的步骤进行合并。在合并过程中您永远不会丢失信息,因为您始终可以回顾所有提交。
Note: John’s answer is correct; this is just a further explanation based on the follow-up questions in the comments – I just needed a bit more room :)
No, in Git, the history is always connected. So if you merge a commit into master, and between the common ancestor of
master
and that commit are more commits, those are completely preserved. Unlike SVN, Git keeps full references to previous commits (with an unlimited number of pointers, so you can merge multiple branches at once). So in the end, you will always see where a branch started, what happened on it, what was merged in between, and where it was merged back into the main branch – only the name (or rather label) of the branch is not kept (except in the auto merge text, if that counts ^^).So your history can for example look like this:
Suppose you want to merge
B9
(the HEAD commit on the branchbr
) back intoM
which is where themaster
branch is pointing at. With a direct merge, you will get this (#
are merge commits):So even if you remove the branch pointer
br
, you can still see all the commits that occured on that separate branch.Or if you want to merge in multiple steps, you can easily merge it like this:
And again, you can always look back at the whole tree, and all the separate commits that were made on the branch – even if you remove the branch (which again, just removes the pointer).
So maybe this explanation will also show you that you don’t necessarily need to make merges in such small steps. You will never lose information during a merge, as you can always look back on all commits.