使用 Git 或 TortoiseGit 合并一系列修订

发布于 2025-01-07 15:01:26 字数 386 浏览 0 评论 0原文

我正在尝试使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

大海や 2025-01-14 15:01:26

摘要

从命令行使用 Git,您可以将 feature 重新设置为 master

git rebase master feature

# Now force push the changes to your remote
git push <remote> feature --force

警告不要不小心强制推送重新设置基准(重写)提交/分支如果您与其他人共享这些提交/分支,因为您将导致与他们基于旧版本的提交/分支的任何更改发生冲突。在(非常)小的团队中可以以这种方式工作,但需要良好的协调。

您还可以使用 --onto 标志来指定范围,其中 是范围的唯一开始:

git rebase --onto master <start commit> feature

最后,cherry-pick 实际上可以接受一系列参数。签出 master 分支后:

# Cherry-pick all commits from `<start commit>`
# exclusive, not included) to feature:
git cherry-pick <start commit>..feature

详细说明

我最初的目标是能够将我的 feature 分支与 master 中所做的更改同步,但我不想只合并到 master 中,因为它导致了太多必须立即解决的冲突。我只是希望能够逐渐合并更改,这样我就可以将冲突分解为更小、更易于管理的部分。

输入 git rebase

最好的方法实际上是 git rebase。事实上,这很完美。它将执行的操作是获取我在 feature 中所做的所有提交,并以相同的顺序制作这些提交的副本,除了它在目标分支的最新版本之上重新提交它们,在我的例子中,它是master

这很重要,原因有二:

  1. 这相当于将 master 合并到 feature 中。为什么?因为 feature 基本上是在 master 的最新版本之上重新创建的……所有 master 的提交现在都存在于 的历史记录中>feature,包括 masterfeature 尚未提交的提交。

  2. Git 按顺序一次一个重新应用提交,因此如果存在任何冲突,它们会以几个更小、更易于管理的部分引入到流程中,一次一个,这正是我希望做的!

这就是它在视觉上的样子(示例改编自 官方 Linux 内核 Git 文档):

      A---B---C feature
     /
D---E---F---G master

在上面的示例中,自从我分支 以来,在 master 上提交了 FG >功能关闭它。我想要做的是将这些更改同步到 feature

git rebase master feature

现在我的分支如下所示:

              A'--B'--C' feature
             /
D---E---F---G master
     \
      A---B---C (no branch)

提交 AC 已重新创建为 A'C' 位于最新版本的 master 之上。旧的提交仍然存在于 Git 存储库中,但由于没有分支指针引用它们,因此它们最终将被 Git 进行垃圾收集。

Summary

Using Git from the command line, you can rebase feature onto master:

git rebase master feature

# Now force push the changes to your remote
git push <remote> feature --force

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:

git rebase --onto master <start commit> feature

Finally, cherry-pick can actually accept a range of arguments. With the master branch checked-out:

# Cherry-pick all commits from `<start commit>`
# exclusive, not included) to feature:
git cherry-pick <start commit>..feature

Detailed Explanation

My original goal was to be able to sync up my feature branch with changes made in master, but I didn't want to just merge in master 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 in feature 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, was master.

This is significant for two reasons:

  1. This is equivalent to merging master into feature. Why? Because feature is basically recreated on top of the most recent revisions of master…all of master's commits now exist in the history of feature, including the commits in master that feature didn't have yet.

  2. 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):

      A---B---C feature
     /
D---E---F---G master

In the above example, commits F and G where made on master since I branched feature off of it. What I want to do is sync those changes to feature:

git rebase master feature

Now my branches look like this:

              A'--B'--C' feature
             /
D---E---F---G master
     \
      A---B---C (no branch)

Commits A through C have been recreated as A' through C' on top of the latest version of master. 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.

你爱我像她 2025-01-14 15:01:26

是的,你可以这样做。假设您的存储库如下所示:

      master
 A---[B]
  \
   \                       feature
    (c1)---(c2)---(...)---(c100)

您想要将 feature 分支合并到 master 中,但那里有很多提交。相反,让我们创建一个新分支 tmp ,它指向 feature 上的早期提交:

 git branch tmp c2

 A---[B]
  \
   \        tmp               feature
    (c1)---[(c2)]---(...)---(c100)

现在 tmp 指向 c2 。现在我们可以只将提交 c1...c2 合并到 master 中,而不考虑 c3...c100

 git checkout master
 git merge tmp

现在,将 tmp 移动到下一批提交(我们需要-f强制它,因为tmp已经存在)。例如,如果我们现在想移动到 c6,请使用:

 git branch -f tmp c6

重复此操作,直到您要合并的所有提交都在其中。

Yes, you can do that. Let's say that your repository looks like this:

      master
 A---[B]
  \
   \                       feature
    (c1)---(c2)---(...)---(c100)

You want to merge the feature branch into master, but there's a lot of commits there. Instead, let's make a new branch tmp that points to an earlier commit along feature:

 git branch tmp c2

 A---[B]
  \
   \        tmp               feature
    (c1)---[(c2)]---(...)---(c100)

Now tmp points to c2. Now we can merge just the commits c1...c2 into master without regard to c3...c100:

 git checkout master
 git merge tmp

Now, move tmp to the next batch of commits (we need -f to force it, since tmp already exists). For instance, if we want to move to c6 now, use that:

 git branch -f tmp c6

Repeat this until all the commits you want to merge are in.

节枝 2025-01-14 15:01:26

注意:约翰的答案是正确的;这只是基于评论中的后续问题的进一步解释 - 我只是需要更多空间:)

我喜欢不必创建临时分支来合并提交的想法,但使用 git merge的问题是它只合并在单个修订中,不是它们的范围(正确吗?)。如果我有 100 个修订要合并,我是否必须使用该命令 100 次?

不,在 Git 中,历史总是相连的。因此,如果您将一个提交合并到 master 中,并且在 master 的共同祖先和该提交之间存在更多提交,那么这些提交将被完全保留。与 SVN 不同,Git 保留对先前提交的完整引用(具有无限数量的指针,因此您可以一次合并多个分支)。所以最后,你总是会看到一个分支从哪里开始,它发生了什么,之间合并了什么,以及它在哪里合并回主分支——只有名称(或者更确切地说标签 ) 分支不会保留(自动合并文本除外,如果这算 ^^)。

因此,您的历史记录可以如下所示:

* -- A -- * ---------- * ----- * -- * -- M [master]
      \
       \
        B1 -- B2 -- B3 -- B4 -- B5 -- B6 -- B7 -- B8 -- B9 [br]

假设您想要将 B9 (分支 br 上的 HEAD 提交)合并回 M 中,其中是 master 分支所指向的位置。通过直接合并,您将得到以下结果(# 是合并提交):

* -- A -- * ---------- * ----- * -- * -- M ---------------- # [master]
      \                                                    /
       \                                                  /
        B1 -- B2 -- B3 -- B4 -- B5 -- B6 -- B7 -- B8 -- B9 [br]

因此,即使您删除分支指针 br,您仍然可以看到发生在那个单独的分支。

或者,如果您想分多个步骤进行合并,您可以像这样轻松地合并它:

* -- A -- * -- * -- * -- * -- M -- #---------- # --------------- # --- # [master]
      \                           /           /                 /     /
       \                         /           /                 /     /
        B1 -- B2 ------------ - B3 -- B4 -- B5 -- B6 -- B7 -- B8 -- B9 [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 :)

I like the idea of not having to create a temporary branch to merge in commits, but the problem with using git merge <commit-hash> is that it only merges in a single revision, not a range of them (correct?). If I had 100 revisions to merge in, wouldn't I have to use that command 100 times?

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:

* -- A -- * ---------- * ----- * -- * -- M [master]
      \
       \
        B1 -- B2 -- B3 -- B4 -- B5 -- B6 -- B7 -- B8 -- B9 [br]

Suppose you want to merge B9 (the HEAD commit on the branch br) back into M which is where the master branch is pointing at. With a direct merge, you will get this (# are merge commits):

* -- A -- * ---------- * ----- * -- * -- M ---------------- # [master]
      \                                                    /
       \                                                  /
        B1 -- B2 -- B3 -- B4 -- B5 -- B6 -- B7 -- B8 -- B9 [br]

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:

* -- A -- * -- * -- * -- * -- M -- #---------- # --------------- # --- # [master]
      \                           /           /                 /     /
       \                         /           /                 /     /
        B1 -- B2 ------------ - B3 -- B4 -- B5 -- B6 -- B7 -- B8 -- B9 [br]

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文