Git将不同分支中的旧提交推送到新分支

tot*_*amp 3 git github pull-request

我已经在GIT上挣扎了一段时间,因为它做了一些奇怪的事情。

我有一个初始代码的基本提交。

好的,接下来我创建一个分支来进行编辑,提交和推送:

git checkout -b feature/feature1
git commit -m "added feature1"
git push origin feature/feature1
Run Code Online (Sandbox Code Playgroud)

现在,我对该功能进行拉取请求。

之后,我更改分支并更新例如:

git checkout -b chore/update-framework
git commit -m "updated framework"
git push origin chore/update-framework
Run Code Online (Sandbox Code Playgroud)

在这一点上,当我尝试第二次推送的请求时,它包含了所有功能的第一次提交。因此,该提交中所有更改的文件都在新的请求中,而旧的请求仅包含功能文件。

我究竟做错了什么?

jba*_*rez 8

当您这样做时:

git checkout -b chore/update-framework
Run Code Online (Sandbox Code Playgroud)

你没签上主面前

要从所需的提交开始一个新分支(例如master):

git checkout -b chore/update-framework master
Run Code Online (Sandbox Code Playgroud)

要么:

git checkout master
git checkout -b chore/update-framework
Run Code Online (Sandbox Code Playgroud)

要么:

git branch chore/update-framework master
git checkout chore/update-framework
Run Code Online (Sandbox Code Playgroud)

要更正错误,您可以执行以下操作:

git rebase --onto master feature/feature1 chore/update-framework
Run Code Online (Sandbox Code Playgroud)

Git会选择您所有由feature / feature1指向的杂项/更新框架提交,然后将它们提交到master上,最后更新您的杂项/更新框架分支。


tor*_*rek 5

我认为你的问题归结为理解提交在分支上(或者最好说包含在分支中)意味着什么。这在 Git 中与许多传统版本控制系统不同。

\n\n

在 Git 中,每个提交都位于(或包含在)许多分支上,至少可能如此。它可以没有分支、1 个分支、2 个分支等等。这是因为,在 Git 中,分支名称只是指向一个特定提交的可移动指针。可以将其更多地视为黄色便签纸。创建新的分支名称意味着抓住一张空白的便签并在上面写下名称,feature/feature1

\n\n

但是附注在哪里呢?好吧,在这里绘制(部分)提交图很有帮助。

\n\n

绘制提交图(的一部分)

\n\n

在 Git 中,每个提交“指向”其前任(或父提交)。对于线性提交链,这基本上是一系列向后指向时间的箭头:

\n\n
... <- o <- o <- o <- ... <- o\n
Run Code Online (Sandbox Code Playgroud)\n\n

这里的每个o提交都代表一个提交,每个提交都指向其(一个)父级。

\n\n

合并提交指向两个父级,这实际上就是合并提交的原因。当仅限于 StackOverflow 上的纯文本时,用箭头绘制这些要困难得多:

\n\n
           o--o\n          /    \\\n... --o--*      M--o\n          \\    /\n           o--o\n
Run Code Online (Sandbox Code Playgroud)\n\n

想象一下所有线条上都有箭头,以便它们都指向左(向上和向左,或向下和向左,如果需要)。 M这是合并提交及其两个父项。我在这里标记了另一个提交, commit *。它不是合并提交,但它确实有两个子项(两个指向它的提交)。这有时会让事情变得更有趣。

\n\n

事实上,在我们进行合并提交之前,*提交是特别有趣的。让我们画出它(通过擦除和一些s):Mo

\n\n
           o\n          /\n... --o--*\n          \\\n           o--o\n
Run Code Online (Sandbox Code Playgroud)\n\n

将分支名称添加到绘图中

\n\n

这是分支真正受到关注的地方。让我们添加名称和更长的<--箭头:

\n\n
           o   <-- bra1\n          /\n... --o--*\n          \\\n           o--o   <-- bra2\n
Run Code Online (Sandbox Code Playgroud)\n\n

Branchbra1指向(或者等效地,是粘贴在其上的黄色便签)上面的最尖端o提交,并bra2指向下面的最尖端提交o

\n\n

我们在两个分支上*按原样调用该提交(就像其左侧的每个提交一样)。

\n\n

这个特殊的谜题还有两块。一是名称HEAD,另一个与我们添加新提交并编写新分支名称时发生的情况有关。

\n\n

HEAD

\n\n

Git 中的名称HEAD告诉我们当前的提交和当前的分支。Git 执行此操作的方式几乎简单得可笑:HEAD通常只包含分支的名称。把它想象成另一个便利贴(可能是亮粉色、绿色或紫色,只是为了让它明显有点不同)。它可以直接指向一个提交\xe2\x80\x94,这是你毫无疑问见过的“分离的头”东西\xe2\x80\x94,但通常它只写有一个分支名称:

\n\n
           o   <-- HEAD->bra1\n          /\n... --o--*\n          \\\n           o--o   <-- bra2\n
Run Code Online (Sandbox Code Playgroud)\n\n

这意味着我们在分支上bra1

\n\n

成长一个分支

\n\n

让我们在运行时进行新的提交bra1

\n\n

当进行新的提交时,Git:

\n\n
    \n
  1. HEAD(它说branch bra1)。
  2. \n
  3. 从分支读取提交 ID bra1(这是一些又大又难看的 SHA-1a1c397f...或其他什么)。
  4. \n
  5. 使用您编辑的任何内容以及您的日志消息进行新的提交git add,指向该父 ID。新提交将获得新的唯一 SHA-1(例如0bc3112...)。
  6. \n
  7. 将此新数字写入当前分支bra1
  8. \n
\n\n

第 4 步导致bra1指向新的提交,现在我们有:

\n\n
           o--o   <-- HEAD->bra1\n          /\n... --o--*\n          \\\n           o--o   <-- bra2\n
Run Code Online (Sandbox Code Playgroud)\n\n

添加合并提交

\n\n

为了完整起见,让我们看看进行合并提交。

\n\n

进行合并的过程本身可能很混乱,但实际上进行合并提交很简单:它只是与两个父级的提交。请注意,我们仍然处于bra1. 我们跑git merge bra2。Git 启动合并机制来完成工作树中的工作。如果存在冲突,就会留下混乱,我们必须手动修复它,但如果没有,它会自动启动新的提交。

\n\n

新的提交与以前一样,只有一个小变化。在步骤3中,它没有写入一个父ID(来自现有的bra1),而是写入两个父ID:第一个是通常的,第二个是从读取中获得的ID bra2

\n\n

第 4 步照常进行,将新提交的 ID 写入bra1(因为HEAD仍然显示“使用分支bra1”):

\n\n
           o--o\n          /    \\\n... --o--*      M   <-- HEAD->bra1\n          \\    /\n           o--o   <-- bra2\n
Run Code Online (Sandbox Code Playgroud)\n\n

因为M是合并(有两个父级),这意味着以前专门进行的所有提交bra2现在也都进行了bra1

\n\n

提交*曾经在两个分支上进行,现在仍然如此。而且,*我们仍然可以通过从左开始bra2并向左工作来实现这两个后期提交。

\n\n

我们只允许向左移动,因此从 开始bra2,我们不允许移动到M,这意味着我们无法到达提交的顶行。我们只能从那里开始,或者从 开始,才能到达那里M。然而,我们不仅被允许,而且实际上被要求遵循像这样的合并的所有M父级。所以从 开始bra1,我们得到M,然后我们得到分支结构的两边,一直回到 commit *,并从那里继续向左走。

\n\n

如果计算节点数,您将看到现在有 3 个提交包含在 中,bra1但不包含在bra2...中,但所有包含在 中的提交bra2都包含在 中bra1

\n\n

好的,那么创建分支怎么样?

\n\n

这里HEAD又开始发挥作用了。

\n\n

您可以用来git branch创建一个分支:

\n\n
$ git branch bra3\n
Run Code Online (Sandbox Code Playgroud)\n\n

默认情况下,这会HEAD读取我们现在所在的位置。 HEADbra1,里面bra1有\的ID。M所以这bra3表明M

\n\n
           o--o\n          /    \\\n... --o--*      M   <-- HEAD->bra1, bra3\n          \\    /\n           o--o   <-- bra2\n
Run Code Online (Sandbox Code Playgroud)\n\n

请注意,HEAD它仍然指向它以前的位置,并且没有其他分支受到干扰,我们只是添加了一个新的可移动标签bra3。如果我们现在移动bra1bra2bra3继续指向M

\n\n

由于HEADstill 指向bra1,新的提交将会做出bra1改变。

\n\n

但是,如果我们使用git checkout -b创建新分支,我们会得到以下结果:

\n\n
           o--o\n          /    \\\n... --o--*      M   <-- bra1, HEAD->bra3\n          \\    /\n           o--o   <-- bra2\n
Run Code Online (Sandbox Code Playgroud)\n\n

这看起来几乎一模一样。不同的是,除了添加新的分支名称之外,我们更改了HEAD. 现在它指向bra3.

\n\n

创建一个指向当前提交以外的内容的新分支

\n\n

让我们回到git branch,它只是创建了分支并且没有改变HEAD。该git branch命令还需要一个可选参数:

\n\n
$ git branch bra4 bra2\n
Run Code Online (Sandbox Code Playgroud)\n\n

这不是bra4指向与 相同的提交,而是“指向与 相同的提交”。所以现在我们有:HEADbra4bra2

\n\n
           o--o\n          /    \\\n... --o--*      M   <-- bra1, HEAD->bra3\n          \\    /\n           o--o   <-- bra2, bra4\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在让我们git checkout bra4看看它的作用(就图 xe2x80x94 而言,当然它也会检查文件):

\n\n
$ git checkout bra4\n\n           o--o\n          /    \\\n... --o--*      M   <-- bra1, bra3\n          \\    /\n           o--o   <-- bra2, HEAD->bra4\n
Run Code Online (Sandbox Code Playgroud)\n\n

同样,任何分支标签本身都没有发生任何变化。在图中,我们只需更改HEAD点即可。

\n\n

(我们本来可以使用我们bra4现在已经有了的组合形式\xe2\x80\x94,所以现在已经太晚了,但是通过执行 ,这将是一个命令而不是两个\xe2\x80\x94 git checkout -b bra4 bra2。)

\n\n

新分支指针的底线

\n\n

这意味着每当我们创建新分支时,我们都可以选择新分支指向哪个提交默认值是“wherever HEADpoint”,这通常意味着读取另一个分支名称。我们可以HEAD先在某个有用的地方指出一点,或者我们可以将其添加到分支创建器中,然后让这一切一起发生。

\n\n

这就留下了一个问题,而且并不总是有一个正确的答案:我们应该将新分支指向哪里?

\n\n

也许我们想将其指向类似origin/masteror的东西origin/feature1。有时,选择其他一些起点更有意义,甚至可能是没有标签指向的起点。

\n\n

等等,origin/master是标签吗?

\n\n

是的,这些东西也是分支标签\xe2\x80\x94指向提交\xe2\x80\x94的可移动指针。它们只是不能移动的标签。当你运行时,你让它们移动git fetch(或拉动,但拉动只是获取,然后是另一步骤)。换句话说,它们“跟踪遥控器”(您git fetch originorigin/*在需要时移动);所以它们是远程跟踪分支

\n\n

当我们绘制上面的图表时,我们可能应该包含这些标签......也许是这样的:

\n\n
           o      <-- HEAD->feature1\n          /\n... --o--*        <-- origin/feature1\n          \\\n           o--o   <-- feature2\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果你想制作一个新的东西feature3*git checkout -b feature3 origin/feature1可以这样做。然后,提交*将包含在四个分支中:feature1origin/feature1feature2feature3

\n