我现在已经和GIT挣扎了一段时间,因为它做了一些奇怪的事情。
我有一个带有初始代码的基本提交。
好的下一步我做了一个分支做我的编辑并提交并推送:
git checkout -b feature/feature1
git commit -m "added feature1"
git push origin feature/feature1
现在我对该功能提出拉取请求。
之后我改变分支并更新例如:
git checkout -b chore/update-framework
git commit -m "updated framework"
git push origin chore/update-framework
此时,当我在第二次推送时尝试拉取请求时,它包含第一次提交,其中包含所有功能内容。因此,该提交中的所有已更改文件都在新的pull请求中,而旧的pull请求仅包含要素文件。
我做错了什么?
答案 0 :(得分:5)
当你这样做时:
git checkout -b chore/update-framework
您没有在主之前结帐。
从您想要的提交开始一个新分支(以master为例):
git checkout -b chore/update-framework master
或:
git checkout master
git checkout -b chore/update-framework
或:
git branch chore/update-framework master
git checkout chore/update-framework
要纠正错误,您可以:
git rebase --onto master feature/feature1 chore/update-framework
Git将选择所有chore / update-framework的提交,这些提交未被feature / feature1指向,然后它将把它们放到master上,最后更新你的chore / update-framework分支。
答案 1 :(得分:2)
我认为你的问题归结为理解提交是什么意思(或者说包含在中)可能更好。这在Git中与许多传统版本控制系统不同。
在Git中,每个提交都在(或包含在)许多分支中,至少是潜在的。它可以在没有分支,1个分支,2个分支等等。这是因为,在Git中,分支名称只是指向一个特定提交的可移动指针。把它想象成一个黄色的粘滞便笺。创建新的分支名称意味着抓取空白便签并在其上写下名称feature/feature1
。
但附注的地方在哪里?好吧,这里绘制(部分)提交图有很大帮助。
在Git中,每次提交都指向"其前身(或父母承诺)。对于线性提交链,这基本上是一系列向后指向的箭头:
... <- o <- o <- o <- ... <- o
此处的每个o
代表一个提交,每个提交都指向其(一个)父级。
合并提交指向两个父项,这实际上是使它成为合并提交的原因。当限制为StackOverflow上的纯文本时,使用箭头绘制起来要困难得多:
o--o
/ \
... --o--* M--o
\ /
o--o
想象一下所有线上的箭头,以便它们都指向左边(上下左右,或者左右,如果需要的话)。 M
这里是合并提交,有两个父母。我在这里标记了一个提交,提交*
。它不是不是合并提交,但它确实有两个子项(两个提交指向它)。这有时会使它更有趣。
事实上,在我们进行合并提交之前,提交*
特别有趣。让我们画一下(通过删除M
和一些o
):
o
/
... --o--*
\
o--o
这是分支机构真正得到适当关注的地方。让我们添加名称和更长的<--
箭头:
o <-- bra1
/
... --o--*
\
o--o <-- bra2
分支bra1
指向(或者,相当于粘贴到黄色粘滞便笺上)最高端o
提交,bra2
指向最低端({1}} {1}}。
我们称为o
的提交在两个分支上都是 (就像它左边的每次提交一样)。
这个特殊难题还有两个部分。一个是名称*
,另一个是与我们添加新提交和写新分支名称时发生的情况有关。
HEAD
Git中的名称HEAD
告诉我们当前的提交和当前分支。 方式 Git这样做几乎是可笑的简单:HEAD
通常只包含分支的名称。把它想象成另一个粘滞便笺(可能是亮粉色,或绿色或紫色,只是为了使它显然有点不同)。 可以直接指向提交 - &#34;分离的HEAD&#34;你毫无疑问看到的东西 - 但通常它只是写有一个分支名称:
HEAD
这意味着我们在分支 o <-- HEAD->bra1
/
... --o--*
\
o--o <-- bra2
。
让我们在bra1
进行新的提交。
在进行新提交时,Git:
bra1
(它说HEAD
)。branch bra1
中读取提交ID(这是一些丑陋的SHA-1 bra1
或其他)。a1c397f...
编辑的任何内容进行新提交。新提交获得一个新的唯一SHA-1(例如git add
)。0bc3112...
。第4步导致bra1
指向新提交,现在我们有:
bra1
为了完整起见,让我们看一下合并提交。
进行合并本身的过程可能很麻烦,但实际上合并 commit 很容易:它只是一个有两个父母的提交。请注意,我们仍然在 o--o <-- HEAD->bra1
/
... --o--*
\
o--o <-- bra2
。我们运行bra1
。 Git启动合并机制来完成工作树中的工作。如果存在冲突,则会造成混乱,我们必须手动修复,但如果没有,它会自动启动新的提交。
新提交就像以前一样发生,只做了一个小改动。在步骤3中,不是编写一个父ID(来自现有git merge bra2
的父ID),而是写入两个父ID:第一个是通常的ID ,第二个是从阅读bra1
获得的ID。
第4步正常工作,将新提交的ID写入bra2
(因为bra1
仍然说&#34;使用分支HEAD
&#34;):
bra1
因为 o--o
/ \
... --o--* M <-- HEAD->bra1
\ /
o--o <-- bra2
是一个合并(有两个父母),这意味着以前所有提交的M
提交现在都在bra2
上了!
提交bra1
曾经在两个分支上,但仍然是。而且,从*
开始并向左工作,我们仍可以提交这两个*
次提交。
我们只允许向左移动(ish),因此从bra2
开始,我们不允许移至bra2
,这意味着我们无法到达顶部的提交。我们只能从那里开始,或从M
开始。但是,我们不仅被允许,而且实际上必需,以跟随所有合并的父母M
。所以从M
开始,我们得到bra1
,然后我们得到分支结构的两边一直回到提交M
,并从那里继续向左。
如果计算节点,您将看到*
中包含的三个提交未包含在bra1
中...但bra2
中包含的所有提交都包含在内在bra2
。
此处bra1
重新发挥作用。
您可以使用HEAD
创建分支:
git branch
默认情况下,这会显示$ git branch bra3
以确定我们现在的位置。 HEAD
表示HEAD
和bra1
中包含bra1
个ID。因此,这会使M
指向bra3
:
M
请注意 o--o
/ \
... --o--* M <-- HEAD->bra1, bra3
\ /
o--o <-- bra2
仍然指向以前的位置,也没有其他分支受到干扰,我们只添加了一个新的可移动标签HEAD
。如果我们现在移动bra3
或bra1
,则bra2
继续指向bra3
。
由于M
仍然指向HEAD
,新的提交会使bra1
更改。
如果我们使用bra1
创建新分支,我们会改为:
git checkout -b
这看起来几乎完全一样。不同之处在于,除了添加新的分支名称外,我们还更改了 o--o
/ \
... --o--* M <-- bra1, HEAD->bra3
\ /
o--o <-- bra2
。现在它指向HEAD
。
让我们回到bra3
,它只是创建分支而不会更改git branch
。 HEAD
命令还有一个可选参数:
git branch
而不是让$ git branch bra4 bra2
指向与bra4
相同的提交,而是说#34; make HEAD
指向与bra4
相同的提交&#34; 。所以现在我们有:
bra2
现在让 o--o
/ \
... --o--* M <-- bra1, HEAD->bra3
\ /
o--o <-- bra2, bra4
,只是为了看看它做了什么(就图表而言 - 它当然还会检出文件):
git checkout bra4
同样,任何分支标签本身都没有发生任何变化。在图表中,我们只需更改$ git checkout bra4
o--o
/ \
... --o--* M <-- bra1, bra3
\ /
o--o <-- bra2, HEAD->bra4
点的位置。
(我们本来可以使用合并后的表单 - 我们现在已经有HEAD
了,所以现在已经太晚了,但是这可能是一个命令而不是两个 - 通过bra4
。 )
这意味着每当我们创建新分支时,我们都可以选择提交新分支指向。 默认是&#34;只要git checkout -b bra4 bra2
指向&#34;,这通常意味着读取另一个分支名称。我们可以先将HEAD
指向某个有用的地方,或者我们可以将它添加到分支创建者中并使它们一起发生。
这留下了一个问题,它并不总是有一个正确的答案:我们应该在哪里指出新的分支?
我们可能希望将其指向HEAD
或origin/master
。有时候选择其他起点更有意义,甚至可能没有标签指向它。
origin/feature1
是标签吗?是的,这些东西都是分支标签 - 也是提交的可移动指针。它们不是你移动的标签。当你运行origin/master
(或拉动,但拉动只是获取,然后是另一个步骤)时,让它们移动。换句话说,他们跟踪遥控器&#34; (如果需要,您git fetch
和git fetch origin
移动);所以他们是远程跟踪分支。
当我们绘制上面的图表时,我们可能应该包含这些标签......可能是这样的:
origin/*
如果您想制作一个从 o <-- HEAD->feature1
/
... --o--* <-- origin/feature1
\
o--o <-- feature2
长出的新feature3
,*
也会这样做。然后,提交git checkout -b feature3 origin/feature1
将包含在四个分支中:*
,feature1
,origin/feature1
和feature2
。