如何使用'git rebase -i'重新设置分支中的所有更改?


70

这是一个例子:

>git status
# On branch master
nothing to commit (working directory clean)
>git checkout -b test-branch
>vi test.c
>git add test.c
>git commit -m "modified test.c"
>vi README
>git add README
>git commit -m "modified README"

现在,我想做一个' git rebase -i',让我重新分配该分支的所有提交。是否存在类似“ git rebase -i HEAD~MASTER”或类似内容。我认为我可以做' git rebase -i HEAD~2',但是我真的不想数已提交的次数。我也可以做' git rebase -i sha1',但是我不想梳理git log来找到第一个提交sha1。有任何想法吗?


1
请给您的问题加标题。也许提到您想对分支中的所有更改进行交互式变基。最好以问题的形式(尽管并不总是可能的)。
达斯汀

您要基于修改的内容master还是只编辑刚刚进行的提交test-branch
DylanYoung '17

Answers:


49

您是否尝试过:git rebase -i master


20
如果主服务器在分支中的当前合并库之前,则此操作将失败。
亚历克斯·布朗

4
问题git rebase -i master在于您可能有合并冲突,您现在并不一定要处理该冲突,或者您可以解决一个提交中的冲突,而只是在重新设置基准的过程中再次解决另一个提交中的冲突。我添加了一个答案,提供了一个替代方案,并指定了确切的提交或要作为基准的提交数量。
赛斯·

rerere每当您下垒时,您就是您的朋友。
DylanYoung '17

对我不起作用,在我执行操作之后会导致奇怪的合并冲突……从字面上看,什么都没有!我的意思是,我所做的一切git rebase master -i不仅用于测试),而且已经导致冲突。
Hi-Angel

72

好的,我假设分支称为“功能”,它是从“主”分支出来的。

有一个小的git命令,称为merge-base。它需要两次提交,并为您提供这两个提交的第一个共同祖先。所以...

git merge-base feature master

...将为您提供这两次提交的第一个共同祖先。猜猜当您将提交传递给git rebase -i时会发生什么,例如...

git rebase -i `git merge-base feature master`

从master和feature分支的第一个共同祖先开始交互式交互。利润!;)


2
虽然很丑-手上没有语法糖吗?
亚历克斯·布朗

14
与许多git解决方案相比,这是相当不错的。
studgeek

6
我建议使用git merge-base master HEAD哪个应该始终对当前分支有效,而无需输入当前分支名称。别名此命令,您将获得简短的git命令。
jayeff 2013年

3
git rebase -i $(git merge-base @{u} HEAD) -假设您当前的分支设置为跟踪基本分支。示例:git branch feature1 origin/master将跟踪来源/母版。因此,现在您甚至不必键入该名称。
亚历山大·伯德

63

所有提供的解决方案的问题是,它们不允许您从第一次提交就重新确定基础。如果第一个提交哈希为XYZ,则执行以下操作:

git rebase -i XYZ

您只能从第二次提交开始进行基准调整。

如果要从第一次提交中恢复基准,请执行以下操作:

git rebase -i --root

16
但是,“-root”是根据有史以来的第一次提交而不是分支机构要求的第一次提交重新确定的
克里斯·B

21

在其他平台上使用gitk(* nix)或gitx(OS X)或类似版本,并查看哪个提交是分支的根。然后运行:

git rebase -i <the SHA hash of the root commit>

例如,我有一个使用gitx检查的存储库:

gitx屏幕截图

现在,我知道了根哈希,可以运行以下命令:

git rebase -i 38965ed29d89a4136e47b688ca10b522b6bc335f

然后我的编辑器弹出,我可以随意重新排列/压缩/任何内容。

pick 50b2cff File 1 changes.
pick 345df08 File 2 changes.
pick 9894931 File 3 changes.
pick 9a62b92 File 4 changes.
pick 640b1f8 File 5 changes.
pick 1c437f7 File 6 changes.
pick b014597 File 7 changes.
pick b1f52bc File 8 changes.
pick 40ae0fc File 9 changes.

# Rebase 38965ed..40ae0fc onto 38965ed
#
# Commands:
#  pick = use commit
#  edit = use commit, but stop for amending
#  squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

我确信有一种神奇的方法可以说服git自动找出树的根,但是我不知道它是什么。

编辑:魔术是这样的:

git log master..other_feature | cat

这将向您显示该分支上的所有提交,而传递给cat的管道将禁用寻呼机,因此您会立即看到第一个提交。

编辑:结合以上提供了一个完全自动化的解决方案:

git rebase -i  `git log master..other_feature --pretty=format:"%h" | tail -n 1`~

1
| cat可以使用时,猫似乎没用git --no-pager
Matthieu Moy 2015年

@MatthieuMoy --no-pager可能不是我在2008年12月使用的版本。该选项直到2008年7月才出现在获得的代码库中。github.com/git/git/commit/…
Otto

@Otto啊,我没有注意到您的发帖日期。您实际上要指向github.com/git/git/commit/…
Matthieu Moy 2015年

6

从另一个分支重新部署的问题

问题git rebase -i master在于您可能有合并冲突,您现在并不一定要处理该冲突,或者您可以解决一个提交中的冲突,而只是在重新设置基准的过程中再次解决另一个提交中的冲突。

从已知提交重新部署的问题

这里的整个问题是,您必须知道必须通过SHA或HEAD〜x等来引用哪个提交。这只是一个小麻烦,但这是一个麻烦。

更好的方法

如果您想将当前分支中的所有提交作为基准,因为它与父分支共享了最近的提交,则可以在.gitconfig中添加以下别名:

rbi = !sh -c \"git rebase -i `git merge-base $1 HEAD`\" -

用法

git rbi parentBranch

这个怎么运作

该别名只是一个shell脚本,它使用的参数指向父分支。传递该参数是git merge-base为了确定该分支与当前分支之间的最新共享提交。


我不确定这个答案对于给定的拓扑是否有意义。此处的合并基础将mastertest-branch创建之时(例如3hgn45)。因此,此变基实际上不执行任何操作。它表示将该分支从(但不包括)3hgn45迁移到(并包括)HEAD到上 3hgn45。但是,也许我误会了您的建议...
DylanYoung

编辑:我明白了;OP的问题对我来说还不清楚。您可能会考虑添加一条警告,警告说这将不会保持更改master(即,它并不是真正的基础程序,因为您正在维护相同的基础,只需在当前分支上编辑一些提交即可)。
DylanYoung '17

1
@DylanYoung-是的,我对OP的问题的理解是,他们实际上并不想基于master,而是只是将所有提交压缩为一个,因为与背离了master
赛斯花

这是一个类似的“ rebase common祖先”别名,它允许使用其他参数:rbca = "!git rebase $(git merge-base HEAD \"$1\") ${@:2} #"

4

从Git v1.7.10开始,您git rebase无需参数即可运行,它将找到派生点并将本地更改重新建立在上游分支上。

您需要配置上游分支才能使其正常工作(即,git pull不带参数的情况下应该可以正常工作)。

有关更多详细信息,请参阅git rebase的文档:

如果未指定,将使用在branch..remote和branch..merge选项中配置的上游(有关详细信息,请参见git-config [1]),并假定使用--fork-point选项。如果您当前不在任何分支上,或者当前分支上没有配置上游,则重新定位将中止。


2

通用解决方案(如果您不知道上游分支的名称)是:

git rebase -i @{upstream}

请注意,如果自上次重新设置基准以来上游(可能是跟踪分支)已更新,则将从上游提取新提交。如果您不想引入新的提交,请使用

git rebase -i `git merge-base --all HEAD @{upstream}`

但这有点满口。


我已经看到一些建议,可以在git rebase中使用对称差异HEAD ... master(将合并基数作为否定的第三个sha),但我无法解决。
亚历克斯·布朗

1
git rebase -i --onto @{u}... @{u}

从HEAD及其上游的单个合并点开始的交互式重新基准化,包括HEAD中不在上游的所有提交。

换句话说,正是您想要的。


但是,如果@{u}已配置,则可以使用无参数git rebase,请参阅我的答案。
Matthieu Moy 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.