以下是我所做的。
- 在github repo中直接对我的README进行了3次提交更改
- 在本地又进行了4次提交,而没有将这3次提交拉入
现在,当我尝试推送时,它说先拉那些远程提交,但当我进行git拉时,它显示错误。
warning: Pulling without specifying how to reconcile divergent branches is discouraged. You can squelch this message by running one of the following commands sometime before your next pull:
git config pull.rebase false # merge (the default strategy)
git config pull.rebase true # rebase
git config pull.ff only # fast-forward only
所以我只是运行命令git config pull.ff only
现在,当我拉动它时,它说的是fatal: Not possible to fast-forward, aborting.
好的。。。。当前设置(使用pull.ff only
)的问题是,它是一个非常限制的设置。
假设这个启动图
A <- B <- C <- D <- (main)
您从main启动一个功能分支:
A <- B <- C <- D <- (main)
^
- (feature)
过了一段时间,图表是这样的:
A <- B <- C <- D <- F <- G <- (main)
^
- (feature)
请注意feature
没有移动。此时,feature
指向main
的祖先,因此快进将起作用,它将执行以下操作:
A <- B <- C <- D <- F <- G <- (main)
^
- (feature)
因为,顾名思义,快进只移动指针,所以在这个过程中不会进行任何重新定基/合并。
然而,在一个正常的功能分支上,您会引入提交,所以您会有这样的东西:
A <- B <- C <- D <- F <- G <- (main)
^
- I <- J <- K <- (feature)
在这种情况下,假设feaure
没有指向main
的祖先,不可能执行FF……但您的设置要求仅在它是FF的情况下运行pull……因此,它失败了。您需要能够重新建立基础或合并才能工作,因此pull.ff only
是一个禁止。
注意:我并不反对eftshift0的答案,因为我同意将pull.ff
设置为only
是有限制的。但我要指出,这一限制可能是个好主意。这是我想要的,事实上,我自己也用这种方式配置了Git。
当git pull
或git pull originbranch
因fatal: Not possible to fast-forward, aborting
错误而失败时,是时候使用git log
检查传入的提交了,例如:
git log --graph --decorate --left-right --boundary ...@{u}
(或与添加--oneline
相同),或:
git log --oneline ..@{u}
以显示";传入";提交(已提取,只是尚未合并或重新基于)和:
git log --oneline @{u}..
以显示";仅本地";commits(存在于我的存储库中,但不存在于我提取的其他Git中)。这些git log
命令都使用了@{u}
结构,它是branch@{upstream}
的缩写。
分支名称的上游是您可以设置的,但通常在执行此操作时已正确设置。例如,如果您在develop
分支上;上游";develop
的可能是origin/develop
,这可能是正确的。上游的这个特殊概念——Git在这里很糟糕,它使用单词upstream来表示多个不同的概念1——就是我们在这里谈论的一个:无论你是什么分支;关于";现在;上游";这个名称的origin/
版本,您可能想与git rebase
、git merge
或您想做的任何操作一起使用。
写出develop@{upstream}
就可以了,但如果你不是在develop
上,而是在feature/long
上呢?现在您必须编写feature/long@{upstream}
。Git有一个简单的解决方案:如果去掉分支名称,只写@{upstream}
,这意味着当前分支的上游。这就是我们在这里所做的。我们也可以将HEAD@{upstream}
写得更明确,但当HEAD
部分很明显时,我们可以省略它。
同时,两个点符号HEAD..HEAD@{upstream}
或A..B
或main..origin/main
或其他任何符号也有非常具体的含义。对于CCD_ 36——类似于大多数Git命令2——它意味着";使用我在右边指定的提交,可以找到很多提交;当您找到那些使用我在左边指定的提交的提交时,请停止查找这些提交"这意味着CCD_ 37或CCD_。
如果你不习惯Git,最后一段可能只是看起来像是很多随机的单词。但使用eftshift0绘制的相同类型的图表,我们可以理解这一点。以下是发生的事情:
I--J <-- feature/long (HEAD)
/
...--G--H <-- main
K--L <-- origin/feature/long
在这里,我是"关于";我的分支机构CCD_ 39。其上游为origin/feature/long
。I、 他们都是从共同的起点提交H
开始的:我提交了两个I-J
,他们——无论他们是谁——都提交了两次K-L
。
当我在原点上运行git fetch
时——请记住,git pull
运行的第一个命令始终是git fetch
——我得到了他们的K-L
提交,我的Git软件更新了我的origin/feature/long
以记住提交L
,我现在已经有了。这就是为什么这个图看起来是这样的:这就是我的Git存储库中的内容。(到目前为止,他们的Git存储库完全没有提交I-J
。)
现在,请注意,HEAD
的字面意思是feature/long
,而HEAD@{upstream}
的意思是origin/feature/long
。进一步注意,CCD_;从B
开始并向后工作所发现的提交,不包括从A
开始并向后操作所发现的"提交";。如果我们将这些与HEAD
和HEAD@{upstream}
放在一起作为A
和B
部分,我们要求Git找到:
- 像
L
这样的提交,我们可以在origin/feature/long
中找到 - 包括像
K
这样的提交,我们可以从那里开始并向后工作,就像Git喜欢做的那样 - 但不,包括
J
、I
和H
等提交
结果是git log HEAD..HEAD@{upstream}
显示提交L
和K
,然后停止!这正是";传入";提交。
同时,用git log HEAD@{upstream}..HEAD
反转这两个参数,显示提交J
,然后是I
,然后停止:这些正是我的提交,而它们没有。
添加--graph
和/或--oneline
有助于更好地显示这类内容。由于我们可以在Git清楚我们的意思是HEAD
时删除单词HEAD
,并且由于@{u}
是@{upstream}
的缩写,所以我们可以写..@{u}
和@{u}..
。
事实上,我经常使用git log ..@{u}
,所以我为它创建了一个Git别名:
[alias]
lin = log ..@{u}
这使我可以运行git lin
到l
og(或查看)即将提交的in
。(我有一个包括--oneline
的incoming
别名,以及一个具有--oneline
并交换@{u}
侧的git outgoing
。)
与git log ...@{u}
一样,三个点的形式更为奇特:它意味着左侧或右侧都有提交,但不是两者都有。这会发现提交J-I
和L-K
,换句话说,对于上面绘制的图。这里的缺点是,您无法判断哪个提交在哪个"提交"上;侧面";。添加--left-right
修复了这一问题;添加CCD_ 95绘制图形;并且添加CCD_ 96告诉Git也包括提交CCD_。不过,我并没有真正使用这种形式,也没有为它使用别名:";传入";以及";传出";这里是我的常用别名3
在任何情况下,在检查传入的提交之后,我选择是合并、重新建立基础,还是完全做其他事情。然后我就这么做。我只想做快进,如果我可以做快进;在这种情况下,让Git使用git pull
自动执行可能是可以的。因此,将pull.ff
设置为only
是我想要的。
1我把Git比作一个聚会,每个人都叫Bruce。有人走到你面前说:";嘿,Bruce,Bruce让我告诉Bruce Bruce正在找他。如果你看到布鲁斯,让他知道,好吗"在Git中,一次又一次地将同一个词疯狂地重复用于多种不同的事物,这非常令人困惑——就像在这次Bruce聚会上一样。
2同样,Git在这里并不总是很好:一些Git命令使用双点符号来表示其他含义。git diff
命令是最明显的罪魁祸首。它同时使用两点和三点表示法来表示不同的意思。我们不会在这里报道。
3它们也是hg log
的两种Mercurial形式,并非完全巧合。