Git无法合并无关分支

1 git merge github

我在某个软件项目中,我们将代码存储在GitHub上。有一个主和开发分支的仓库。我当时在笔记本电脑上没有互联网连接,并且想开始使用其他功能,所以我做了以下工作:

git init
git remote add origin git://repo_adress
git checkout -b webapp <- created a new branch
Run Code Online (Sandbox Code Playgroud)

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

然后我做了:

git add .
git commit -m "something"
git push origin webapp
Run Code Online (Sandbox Code Playgroud)

我在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.
Run Code Online (Sandbox Code Playgroud)

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

From github.com:repo_address
 * branch            development -> FETCH_HEAD
fatal: refusing to merge unrelated histories
Run Code Online (Sandbox Code Playgroud)

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

tor*_*rek 8

TL; DR

您可能应该创建一个新分支,并将您的提交放入其中。您可以与合并--allow-unrelated-histories。无论哪种方式,您都需要至少一个新提交来绑定到现有分支。

这部分是解决问题的关键:

我当时在笔记本电脑上没有互联网连接...

您在笔记本电脑上创建了一个与其他存储库没有任何关系的存储库。

git init
Run Code Online (Sandbox Code Playgroud)

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

git remote add origin git://repo_address
Run Code Online (Sandbox Code Playgroud)

这将添加一个遥控器,但不会该遥控器联系(当然这是不可能的)。结果,您的存储库继续没有提交。

git checkout -b webapp   <- created a new branch
Run Code Online (Sandbox Code Playgroud)

实际上,这尚未创建分支。它所做的一切就是将您从不存在的master分支变为不存在的webapp分支。

然后我做了:

git add .
git commit -m "something"
Run Code Online (Sandbox Code Playgroud)

当您在此空存储库中创建分支时,实际上就是在这里创建分支的第一个提交。而且,由于这存储库中的第一个提交,因此它具有不具有提交的特殊属性。Git将此称为根提交

git push origin webapp
Run Code Online (Sandbox Code Playgroud)

当然,这需要Internet访问,因为它会通过您提供的URL调用Git。但是,只要您有权访问,它所做的就是将您所做的提交(单根提交)完整无损地传递给其所有父项(没有父项)及其所有文件,并要求另一个Git(位于repo_address)进行更改或创建其分支名称webapp以匹配您自己的最后一次提交webapp

到目前为止一切都还可以,但是现在您有了一个具有两个根的存储库

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

A--...--M   <-- master
     \
      N   <-- development

O   <-- webapp
Run Code Online (Sandbox Code Playgroud)

我只假设了几次提交(以便我可以用单个字母而不是大的丑陋的哈希ID编号)。承诺AO有根的提交。所有其他提交都来自AO(但实际上没有任何派生自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.
Run Code Online (Sandbox Code Playgroud)

git pull命令仅运行,git fetch然后执行第二个Git命令。在这种情况下,它的运行git fetch origin没有其他限制(因为您的存储库中没有上游设置webapp),因此您的Git调用了另一个Git,一个位于repo_address,并下载了您没有的所有提交和分支。 -此时,是masterdevelopment工作以及那些提交。现在,您拥有与他们相同的图,只是您的Git使用远程跟踪名称而不是分支名称:

A--...--M   <-- origin/master
     \
      N   <-- origin/development

O   <-- webapp (HEAD), origin/webapp
Run Code Online (Sandbox Code Playgroud)

请注意,您仍然拥有自己的分支名称,并且两个名称(您的分支名称webapp)和origin/webapp记住其分支名称的远程跟踪名称都webapp指向commit O,即新的root提交。

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

然后您运行:

git pull origin development
Run Code Online (Sandbox Code Playgroud)

其中指定了要合并的内容development您的Git正在通过远程跟踪名称记住的由他们标识的提交origin/development。(我N在一张图片的猜测中将其绘制为提交。)

git merge命令通过查找共享提交来起作用。这是两个分支上的“最近”(无论是什么意思,确切地说是:从图形上看是直观的)提交。这两个分支之一是您当前的分支,即您HEAD所连接的分支。另一个分支是从您命名的提交(即commit)中找到的分支N。因此,Git从提交N返回其根目录commit A,以找到适当的共享提交。它还从提交返回O到其根目录,以找到共享的提交。不过O 一个根,所以从向后走O O。没有共享的提交!

就是 Git抱怨的原因。提交附带的历史记录O包含一个提交,即提交O本身。提交背后有更多的历史N,但永远不会导致提交O。没有共享的历史记录,合并没有任何意义。

采摘樱桃

Cherry-picking包括将提交(即快照)转换为更改集以查看该提交中的“发生了什么”,然后将相同的更改集应用于其他提交。变更集只是差异列表:将一个提交变成另一个提交的说明。git diff例如,这就是显示的内容。要将提交转换为差异,Git将提交与其父级进行比较。

显然,挑选根提交有点棘手,但是Git 可以做到。它的作用是将根提交与空提交进行比较(更准确地说,是空树)。得到的DIFF由读命令:添加这个新的文件; 这里是内容。

所以,你现在可以检查出你现有的分支名称development,但有一个问题:你不要一个名为现有的分支development。记住,你有这个:

A--...--M   <-- origin/master
     \
      N   <-- origin/development

O   <-- webapp (HEAD), origin/webapp
Run Code Online (Sandbox Code Playgroud)

但是,如果您要求它签出,git checkout则会创建一个development为您命名的分支development,因为它将发现origin/development看起来足够的分支development将继续创建本地名称,指向相同的提交,然后将其设为您的HEAD

A--...--M   <-- origin/master
     \
      N   <-- development (HEAD), origin/development

O   <-- webapp, origin/webapp
Run Code Online (Sandbox Code Playgroud)

现在,您可以创建另一个新的分支,webapp2例如,这也点提交N,使用git checkout -b webapp2

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

O   <-- webapp, origin/webapp
Run Code Online (Sandbox Code Playgroud)

现在,您可以合并或樱桃采摘提交O

分支机构名称与分支机构

我将把本节的大部分内容外包给另一个StackOverflow问题:“分支”到底是什么意思? 当我们在这里谈论“分支”时,如果我们不是指分支名称,则意味着一系列以分支尖端提交(例如M和)结尾的提交N。您将希望您的工作(以分支图结构或分支祖先术语)与这些现有分支相关。这意味着您希望您的新提交重新链接到commit N,即的当前提示development

如果您采摘樱桃

假设您webapp2如上所述创建,然后运行git cherry-pick webapp。Git会将提交复制OO'适用于当前提交的新提交N。Git将通过执行所有那些git diff通过比较提交O到空树而产生的“使用这些内容创建新文件”操作来做到这一点。由于新文件不会干扰任何现有文件,因此它们应该都可以正常工作。

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

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

O   <-- webapp, origin/webapp
Run Code Online (Sandbox Code Playgroud)

这可能就是您一直想要的。现在,您可以删除自己的webapp,然后webapp在处的另一个Git中删除repo_address,就可以webapp2在任何地方使用,就好像您已经按照预期的方式进行了操作一样,并且如果您之前可以访问Internet,也可以这样做。

如果合并

您可以改为运行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
Run Code Online (Sandbox Code Playgroud)

再一次,您可以webapp在任何地方删除。奇怪的是,您现在在您的历史记录(您的提交集合)中有这个额外的root提交O

行使: git merge --squash

查找是什么git merge --squash,并预测它将如何离开您的存储库。与采摘樱桃相比,这有什么不同?(如果愿意,可以尝试克隆)。