好的,这是交易。我在提交(2f6ea37)的基础上创建了一个分支(master),然后在master的基础上创建了另一个分支(develop)。在开发上进行了更改。问题是,master应该实际上是根据先前的提交(df7992e)而不是(2f6ea37)创建的。
如何在不丢失致力于开发的新内容的情况下更改master的创建位置?
现在是这样:
branch1 --> <2f6ea37> --> <df7992e>
\
master
\
develop --> <new_commit>
这是应该的样子:
branch1 --> <2f6ea37> --> <df7992e>
\
master
\
develop --> <new_commit>
答案 0 :(得分:1)
您可能只需要三个git branch -f
命令即可执行所需的操作:
git branch -f master 2f6ea37
git branch -f branch1 df7992e
git branch -f develop 2f6ea37
但是要使用git branch -f
来执行此操作,您必须使用这三个名称中的任何一个都不是的分支名称,因此您可能需要一个git reset --hard
或其他命令。如果develop
已经有其他提交,那么您将遇到更大的问题。请参阅下面的详细说明。
首先(这虽然很小,但这确实很重要),图中的箭头是错误的,因为它们是向前的。 :-) Git向后做所有事情。因此,像df7992e
这样的提交会将向后指向其前身2f6ea37
。
master
和develop
之类的分支名称仅包含一个原始哈希ID,即它们直接指向特定的提交。所以我将其绘制为:
2f6ea37 <-- branch1
\
df7992e <-- master
\
C <-D <-E <-- develop
为避免沉迷于无法读取的哈希ID,我只需致电2f6ea37
A
和df7992e
B
:
A <-- branch1
\
B <-- master
\
C--D--E <-- develop
现在,关于Git中分支名称的问题是,您可以始终将新的哈希ID填充到 any 分支名称中。现在,该分支仅指向您为其提供的哈希ID。
分支名称预期随着时间的推移而移动的方式是,以便它们积累新的提交:
A--H <-- branch1
\
B--F--G <-- master
\
C--D--E--I <-- develop
例如,已以“预期”方式移动了所有三个分支名称:branch1
现在标识具有哈希值H
的提交,它指向具有哈希值A
的提交,其中{ {1}}之前已确定。这样很好类似的论点适用于其他两个。
您显然希望名称branch1
标识提交master
,名称A
标识提交branch1
。 (我们将在下一节中担心B
!)您可以通过将正确的哈希ID强制插入每个名称中来实现这一部分:
develop
但是,如果我们将其与之前的状态进行比较,则名称A <-- master
\
B <-- branch1
\
C--D--E <-- develop
朝着“错误”的方向移动。它曾经命名为commit master
,现在命名为B
。名称A
已沿“右”方向移动(向前移动,与内部向后指向的箭头相反)。
像这样向后移动分支名称会使其他 Git存储库(和/或其用户)如果看到您的 {{1 }}之前确定提交branch1
。请记住,所有Git都共享原始哈希ID,因此,如果其他任何Git都提交了master
,他们可能会有一些名字记得它。他们可能有B
指向共享提交B
的副本,结果,他们也可能有自己的origin/master
指向B
。
如果没有其他人见过这个,那就很好。只需使用master
和/或B
将正确的哈希ID填充到正确的分支名称中即可。
最后一个问题是分支名称git branch -f
。在这里,我已将其指向提交git reset --hard
,其中develop
指向E
,指向E
,指向D
。 / p>
我相信,根据您绘制初始图表的方式,目前您真正拥有的东西看起来像这样:
C
也就是说,提交B
根本不存在。如果是这样,我们也只需要移动A <-- branch1
\
B <-- master, develop
来直接指向C-D-E
,就需要注意的是,将develop
“向后移动”与我们将A
相同方式:
develop
如果您现在master
并进行新的提交A <-- master, develop
\
B <-- branch1
,则新的git checkout develop
将指向C
:
C
此处所附的A
表示您已签出的分支目前为 C <-- develop (HEAD)
/
A <-- master
\
B <-- branch1
。
但是如果您已经拥有现有提交(HEAD)
呢?也就是说,如果您当前的图表是:
develop
这里的问题是现有提交C-D-E
已经指向现有提交A <-- branch1
\
B <-- master
\
C--D--E <-- develop (HEAD)
。提交后,关于任何提交的任何内容都无法更改。因此,您根本无法进行更改 C
。在这种情况下,您要做的是 copy B
,因此还有C
和C
到新的和改进的提交。它们的新功能和改进之处在于,新的D
(我们称为E
)指向C
,并使用C'
作为其源库。同样,新的A
指向A
,新的D'
指向C'
,这是给我们的:
E'
这也以一种意外的方式移动了名称D'
:现在,我们不仅备份了 C'-D'-E' <-- develop (HEAD)
/
A <-- branch1
\
B <-- master
\
C--D--E [abandoned]
,我们还备份了develop
,然后是{{1 }},然后也是B
。 (现有的提交E
仍可以通过某些分支名称找到,因此不会被放弃。)
将完成所有这些操作的命令是D
。将C
牢固地附加到B
上后,您将运行:
git rebase
使用原始哈希ID表示排除提交HEAD
和更早的提交,向后复制当前提交中的所有提交,新提交在提交develop
之后。然后将名称git rebase --onto 2f6ea37 df7992e
指向最后一个这样的复制提交。排除表示要复制的提交集合恰好是正确的集合B
,因此我们得到了什么我们在上面画了。
现在是时候使用两个2f6ea37
命令交换其他两个分支名称,而我们仍在开发中。或者,我们可以在启动develop
之前将其交换。
C-D-E
副本但是在git branch -f
上怎么办?假设现在可以通过以下方式绘制实际图片:
git rebase
您可以使用C-D-E
交换两个名称develop
和A <-- branch1
\
B <-- master, develop (HEAD)
,而无需以任何方式更改任何其他提交。但是现在您剩下了:
git branch -f
使用branch1
会给您一个错误:
master
但是您仍然需要移动A <-- master
\
B <-- branch1, develop (HEAD)
。有很多方法可以做到:
使用git branch -f develop
:fatal: Cannot force update the current branch.
:这从复制中排除了从develop
开始向后的提交,即所有提交;在提交git rebase
之后复制零个提交;然后移动您当前的分支名称以指向提交git rebase --onto master branch1
。
使用B
:A
:这会丢弃索引和工作树中所有正在进行的工作,而是将它们重新设置为与提交A
相匹配,然后移动您当前的分支名称git reset
指向提交git reset --hard master
。这样做的好处是它快速简便,并且可以满足您的需求。缺点是,它会清除您忘记保存在某处的所有正在进行的工作。 rebase命令将警告您。
使用A
:develop
。这与rebase和reset命令完全一样有效,但是需要输入三个单独的命令。在某些情况下,它确实能够在索引和工作树中进行未提交的工作(有关详细信息,请参见Checkout another branch when there are uncommitted changes on the current branch)。
分支名称可以很容易地移动。要移动您站立的分支,请下车(A
),或使用git checkout
或其他偷偷摸摸的方法。请记住,git checkout master; git branch -f develop master; git checkout develop
对未完成的工作具有破坏性。
您不能修改任何现有的提交。如果无需此操作即可对分支名称进行调整,则只需调整这些分支名称即可。其他任何人(例如,您提供的其他Git存储库)可能会因“异常”分支名称移动而感到困惑,因此请确保其他Git存储库及其用户已经为此做好了准备。
如果您需要复制一些提交,git checkout
将完成这项工作。这也可能以其他跟踪您的分支名称(和提交)的Git可能需要准备的方式来调整分支名称。