Git无法合并不相关的分支

时间:2018-03-12 20:24:19

标签: git github merge

我在一些软件项目中,我们将代码存储在GitHub上。有一个主人和开发分支的回购。我在我的笔记本电脑上没有连接互联网,我想开始研究另一个功能,所以我做了以下几点:

git init
git remote add origin git://repo_adress
git checkout -b webapp <- created a new branch

我添加了一些文件,文件夹等

然后我做了:

git add .
git commit -m "something"
git push origin webapp

我查了GitHub,现在有3个分支。我想将webapp合并到开发中(没有冲突,我在一个单独的文件夹中工作)。不幸的是,当我尝试git pull时,发生了这种情况:

 * [new branch]      development -> origin/development
 * [new branch]      master      -> origin/master
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.

现在当我使用git pull origin development时,会显示:

From github.com:repo_address
 * branch            development -> FETCH_HEAD
fatal: refusing to merge unrelated histories

我现在能做什么?我应该改变吗?

2 个答案:

答案 0 :(得分:4)

TL; DR

你应该创建一个新的分支并挑选你的提交。您可以与--allow-unrelated-histories合并。无论哪种方式,您至少需要一个新的提交,以绑定到现有的分支。

这部分是问题的关键:

  

我的笔记本电脑没有连接互联网......

您在笔记本电脑上创建了一个与其他存储库无关的存储库。

git init

我假设您在一个空目录中执行此操作,因此它实际上创建了一个新的空存储库。您的新存储库没有提交,因此没有分支,即使您(在分支master上(矛盾地)。也就是说,您已经master,但master并不存在!

git remote add origin git://repo_address

这会添加一个遥控器,但不会联系遥控器(当然这是不可能的)。因此,您的存储库仍然没有提交。

git checkout -b webapp   <- created a new branch

事实上,这还没有创建分支。它所做的就是将您从不存在的master分支转变为不存在的webapp分支。

  

然后我做了:

git add .
git commit -m "something"

这是分支实际创建的地方,当你在这个空的存储库中创建它的第一个提交时。并且,由于此存储库中的第一个提交,因此它具有不具有提交的特殊属性。 Git称之为 root commit

git push origin webapp

当然,这需要访问Internet,因为它会在您提供的URL处调用Git。但是,只要你有访问权限,它所做的就是转移你提交的提交 - 单个root commit-intact,其所有父项(没有)及其所有文件,并在{{1}请求另一个Git更改或创建其分支名称repo_address以匹配您自己webapp上的最后一次提交。

到目前为止一切正常,但现在你有一个有两个根的存储库

webapp的Git存储库现在有一个提交图,看起来像一个更复杂的版本:

repo_address

我假设了一些提交(因此我可以用单个字母而不是大丑陋的哈希ID对它们进行编号)。提交A--...--M <-- master \ N <-- development O <-- webapp A是root提交。所有其他提交都来自OA(但实际上没有任何内容来自O,除非您在笔记本电脑上进行了多次提交。)

  

不幸的是,当我尝试O时,发生了这种情况:

git pull

* [new branch] development -> origin/development * [new branch] master -> origin/master There is no tracking information for the current branch. Please specify which branch you want to merge with. See git-pull(1) for details. 命令只运行git pull,然后运行第二个Git命令。在这种情况下,它运行git fetch而没有其他限制(因为您的git fetch origin存储库中没有设置上游),所以你的Git调用了其他Git,即webapp上的那个,并下载了他们所拥有的所有提交和分支,此时此作为repo_addressmaster工作,以及那些提交。现在你拥有相同的图表,除了你的Git使用远程跟踪名称而不是分支名称:

development

请注意,您仍然拥有自己的分支机构名称,并且两个名称 - 您的分支名称A--...--M <-- origin/master \ N <-- origin/development O <-- webapp (HEAD), origin/webapp 和远程跟踪名称webapp都会记住其分支名称origin/webapp - 指向提交webapp,你的新根提交。

O运行的第二个命令通常是git pull。如果您还没有指定要合并的内容,则此合并需要上游设置,并且由于您没有上游并且未指定要合并的内容,因此该部分失败。

然后你跑了:

git merge

指定要合并的内容:由git pull origin development 标识的提交,您自己的Git通过远程跟踪名称development进行记忆。 (我把它作为提交origin/development绘制,在我的猜测图中。)

N命令通过查找共享提交来工作。这是&#34;最近的&#34; (无论这意味着什么:它从图形图中直观)在两个分支上提交。两个分支中的一个是您当前的分支,即您git merge所附加的分支。另一个分支是从您命名的提交中找到的分支,即提交HEAD。因此,Git从提交N返回其根,提交N,以找到适当的共享提交。它还从提交A向其根目录返回,以查找共享提交。但是O 是一个根,因此从O 向后移动会在O停止。没有共享提交!

这是为什么 Git抱怨。附加到提交O的历史记录包含一个提交,即提交O本身。提交O背后有更多的历史记录,但它永远不会导致提交N。没有共享历史记录,合并没有意义。

摘樱桃

挑选樱桃包括将提交 - 这是一个快照 - 变成一个变更集来查看&#34;发生了什么&#34;在该提交中,然后将相同的更改集应用于其他提交。更改集只是差异列表:将一个提交转换为另一个提交的说明。例如,O显示的内容。要将提交转换为diff,Git会将提交与其父级进行比较。

显然,挑选root提交有点棘手,但Git 可以做到这一点。它的作用是将根提交与空提交进行比较(更确切地说,是empty tree)。生成的diff包含以下命令:添加此新文件;这是内容。

因此,您现在可以查看现有的分支机构名称git diff,但有一个问题:您不会拥有名为development的现有分支。记住,你有这个:

development

A--...--M <-- origin/master \ N <-- origin/development O <-- webapp (HEAD), origin/webapp 为您创建一个名为git checkout的分支,如果您要求它查看development,因为它会在那里找到&#39 ; s development看起来像origin/development,它将继续创建本地名称,指向同一个提交,然后将其设为development

HEAD

您现在可以使用A--...--M <-- origin/master \ N <-- development (HEAD), origin/development O <-- webapp, origin/webapp 创建另一个新分支,例如webapp2,也指向提交N

git checkout -b webapp2

现在您可以合并或挑选提交A--...--M <-- origin/master \ N <-- development, origin/development, webapp2 (HEAD) O <-- webapp, origin/webapp

分支名称与分支

我将本节的大部分内容外包给另一个StackOverflow问题:What exactly do we mean by "branch"?当我们谈论&#34;分支&#34;在这里,如果我们不是指分支名称,我们指的是以分支提示结尾的一系列提交,如OM。您将希望您的工作(在分支图结构或分支 - 祖先术语中)与这些现有分支相关联。这意味着您希望新的提交链接回提交NN的当前提示。

如果你挑选

假设您如上所述创建development,然后运行webapp2。 Git将复制提交git cherry-pick webapp到适用于当前提交O的新提交O'。 Git将通过执行所有这些&#34;使用这些内容创建新文件来完成此操作&#34;通过将提交N与空树进行比较而生成的git diff的操作。由于新文件不会干扰任何现有文件,因此这一切都可以正常工作。

当Git进行新提交时,它将向前拖动当前分支O以容纳它,您将拥有:

webapp2

这可能是你一直想要的。现在,您可以删除自己的A--...--M <-- origin/master \ N <-- development, origin/development, webapp2 (HEAD) \ O' <-- webapp2 (HEAD) O <-- webapp, origin/webapp ,然后在webapp删除其他Git中的webapp,并在任何地方使用repo_address,就像您按照自己的方式使用webapp2一样如果您之前能够访问互联网,那就可以了。

如果合并

您可以运行git merge --allow-unrelated-histories webapp。这将告诉Git使用与公共起点相同的空树来制作两个差异。一个比较提交N的内容:添加每个文件。一个比较提交O的内容:添加每个文件。 Git然后将操作组合在一起 - 这些操作都在不同的文件名上 - 并与两个父母进行合并提交,

A--...--M   <-- origin/master
     \
      N   <-- development, origin/development, webapp2 (HEAD)
       \
        P   <-- webapp2 (HEAD)
  _____/
 /
O   <-- webapp, origin/webapp

您可以再次删除webapp。奇怪的是,在您的历史记录(您的提交集)中,您现在拥有这个额外的根提交O

练习:git merge --squash

查看git merge --squash的作用并预测它将如何离开您的存储库。这与樱桃采摘相比如何? (如果你愿意,可以在克隆中试试。)

答案 1 :(得分:0)

简单rebase也可能无法正常工作,因为它也会尝试查找“上次常见提交”。

最好的机会是树操作员表单

rebase --onto target_branch start_commit_exclusive end_commit_inclusive

其中target_branch是远程主服务器,start_commit_exclusive是您无关分支中的第一个提交,end_commit_inclusive是您不相关分支的HEAD。

问题是你不能包含你的无关分支的“初始”提交,所以你可以选择“首先进入远程主服务器:

git checkout -b master origin/master
git cerry-pick SHA_OF_YOUR_FIRST_COMMIT
git rebase --onto master  SHA_OF_YOUR_FIRST_COMMIT webapp