为什么没有参数的git rebase以它的方式工作?

时间:2018-06-01 12:03:37

标签: git rebase

每隔一段时间我就会执行一个没有参数的git rebase,并发现不是对我配置的上游进行重新定位,而是使用了--fork-point选项并反对...其他内容,导致我承诺消失。

这符合git rebase的文档所说的内容:

  

如果< upstream>未指定,上游在分支中配置。< name> .remote和branch。将使用< name> .merge选项(有关详细信息,请参阅git-config [1])并假设--fork-point选项

我的问题是:为什么以这种方式运作?就个人而言,我觉得如果我没有选项运行git rebase,我想要对配置的上游分支进行重新绑定。如果我想反对别的东西,我会这样说。

显然Git的开发人员不这么认为,所以我想知道是否有人能以一种能帮助我记住这种区别的方式来解释这种想法。

3 个答案:

答案 0 :(得分:1)

问题的措辞(“反对......别的东西”)暗示你要么不确定,要么不买入fork-point应该做的事情。如果我们从一个清晰的图片开始(如果我们假设它正常工作并且没有破坏性的副作用),那么使用它作为默认值的动机似乎更清楚。

(说实话,在查看这个问题之前,我从来没有想过要弄清楚fork-point的作用。最好的解释似乎在git merge-base文档中 - {3 }}。)

在进入杂草之前,有两个“大图”观察:

1)我不认为git开发人员会同意使用fork-point意味着你“反对其他东西”;我认为他们可能会说你仍然在反对上游,但是对于哪些承诺重写新基地更具选择性。

2)虽然fork-point可能并不总是有用,但我不清楚在什么情况下它会导致你的提交消失[1]。看到任何显示问题的最小测试用例会很有趣(因为上面我提到“假设没有破坏性的副作用”,认为这是一个合理的默认值)。

所以进入它......

fork-point尝试做什么?

简短的回答是,如果提交以前是上游的一部分并且是通过历史编辑从上游删除的,那么--fork-point会告诉rebase将该提交留下,理由是:(1)它不是真的分支的一部分,(2)它已被上游拒绝,(3)我们可能不会试图撤销某人的历史重写。

它通过推断reflogs中的信息来实现。因为reflog是本地的和临时的,这是一个kludge;你对rebase的尝试可能会得到一个不同的结果,因为别人试图在一个看似相同的回购克隆中执行相同的rebase。

如果你的reflog似乎暗示事实上你的提交曾经是上游的一部分,那可能会导致问题。

那么为什么要将它作为默认使用?

我想底线是,开发人员假设如果提交先前在上游并从上游删除,则删除是确定的。这是否“通常是真的”可能取决于开发人员。

奇怪的是,merge-base文档(上面引用的)中使用的示例看起来很奇怪。它将上游显示为origin/master,这意味着在某些时候远程的master被重写 - 这是一个上游的rebase情况,通常不鼓励。

由于无关紧要的原因,我习惯在变基时总是指定我的上游。这意味着,根据您的看法,我错过了和/或从未承受默认fork-point选项的风险。

[1]我确实提出了一个可能的案例,但我不确定你是否遇到了这种情况。如果您是主人,并且您开始开发一个功能,并且只有在您提交后才意识到您忘记创建功能分支;因此,您可以“就地”创建分支,然后快退master以取消对更改的混合;然后使用fork-point将功能重新设置为master可能会做错事。

对于这种情况可能有帮助的一个解决方案是,在重绕master之后,你可以做一个强制rebase来从不在主reflog中的新提交中重新生成你的分支。

答案 1 :(得分:0)

一般来说,git rebase的签名是

git rebase --onto <newbase> <upstream> <branch>

其中 <branch>merge base<branch><upstream> 之间的提交(即从管道命令 git merge-base 返回的值)被重放到 { {1}}。 (<newbase> 之所以这么叫是因为它返回 merge base<base> 之间的三路合并的潜在 <branch> 提交;“3”中的“3” " 因为这样的合并涉及到提示提交 <upstream><branch> 以及基础 <upstream>)。在这种情况下,我们可以将 <base> 视为“分叉点”。

通常,用户签出他们想要变基的分支并在命令中指定上游

merge base

被解释为

git rebase <upstream>

如果要变基的分支有跟踪分支,则可以在没有选项或参数的情况下自行调用 git rebase --onto <upstream> <upstream> HEAD,并将其重播到跟踪分支(例如 rebase)上

origin/branch

不过在这种情况下,git rebase --onto <origin/branch> <origin/branch> HEAD 会重放 HEAD 和 rebase 的 HEAD 和 git merge-base --fork-point 之间的提交,这与 HEAD 和 {{1} 的 origin/branch 略有不同}}。 AFAIK,当分支和远程曾经指向同一个基地,但远程后来被重播到不同的基地时,这会有所帮助。

每当指定 git merge-base 时,默认情况下都不会使用 origin/branch,所以我最好的猜测是 Git 开发人员希望 upstream 在指定远程上游时默认为 --fork-point 而他们没有'没有办法区分本地上游和远程上游。他们的解决方案是单独引入 --fork-point,在这种情况下将默认选项从 git rebase 切换为 --no-fork-point,并且仅在为分支。

不过,我认为让命令在没有选项的情况下成功运行是危险的(因为它可能在输入选项和参数之前意外运行)。如果您想自己运行 --fork-point 并让它像指定上游一样运行,则必须明确提供 git rebase(即 --no-fork-point)。同样,这仅在分支具有跟踪分支时才有效。

答案 2 :(得分:-1)

我用2个本地回购进行了测试。

#simulate a remote repo
git init server
cd server
> a
git add .
git commit -m 'a'
git branch new
> b
git add .
git commit -m 'b'
git checkout new
> c
git add .
git commit -m 'c'
git checkout master
cd -

git log --oneline --graph --decorate的图表是:

* 912e28a (HEAD -> master) b
| * 8c71449 (new) c
|/
* bd95493 a

模拟当地的另一个回购:

git clone server -- client
cd client
git reset HEAD^ --hard
> d
git add .
git commit -m 'd'

图表:

* 7173a5f (HEAD -> master) d
| * 912e28a (origin/master, origin/HEAD) b
|/
| * 8c71449 (origin/new) c
|/
* bd95493 a

origin/mastermaster的上游分支。现在运行git rebase,图表变为:

* 3bc57c5 (HEAD -> master) d
* 912e28a (origin/master, origin/HEAD) b
| * 8c71449 (origin/new) c
|/
* bd95493 a

该图表与git rebase origin/mastergit rebase origin/master master的结果相同。

然后再次尝试更改上游分支:

#go back to the graph before the rebase
git reset 7173a5f --hard
#change the upstream
git config branch.master.merge refs/heads/new
git status
#try rebase again, without arguments
git rebase

图表转为:

* b3bfd13 (HEAD -> master) d
* 8c71449 (origin/new) c
| * 912e28a (origin/master, origin/HEAD) b
|/
* bd95493 a

好像已经git rebase origin/newgit rebase origin/new master了。

所以我认为它确实表现得像你一直期待的那样,将当前的分支机构与上游分支。我想知道哪些提交已经消失了。

合并提交在没有-p--preserve-merges的情况下消失。如果上游已经有相同的提交,那么当前分支上的提交也会消失。