将单个Git提交进行变基

164

有没有一种方法可以将一个分支中的单个提交变基到另一个分支上?

我有这样的分支结构:

-- -- -- -- -- (Master)
            \
              -- -- -- -- -- XX (Feature-branch)

我想做的就是将Feature-branch分支的最后一次提交变基到master分支上,并回滚Feature-branch分支上的一个提交。

-- -- -- -- -- XX (Master)
            \
              -- -- -- -- -- (Feature-branch)

我该怎么做?


3
如果你可以变基任意数量的提交,那么为什么要询问如何变基单个提交?如果我能在SO上提问,我会问变基(一个提交)和挑选(cherry-picking)之间的区别是什么。 - Val
18
因为我不知道什么是挑选樱桃式提交代码(指从已有的分支中选择某些修改进行提交),而我常常会在自己的分支上“瞎搞”、“在其他分支上接收修复请求”、“修复它”、“提交到错误的分支”,以至于询问这个问题还是很有用的。 - Kevin Meyer
因为从堆栈中重新定位单个提交的命令语法并不明显。 - ChrisPhoenix
4个回答

157

你可以将XX挑选到主分支中。

git checkout master
git cherry-pick <commit ID of XX>

使用git reset命令从特性分支中移除最后一次提交。

git checkout Feature-branch
git reset --hard HEAD^

105
一个问题明确地称为“git rebase...”的问题,为什么会有一个被接受的答案包含了 cherry-pick,这是一个完全不同的概念,有时本身被认为是不干净的? - Bondax
1
不确定这是否相关,但我想要rebase的提交有一些文件被移动了,而cherry-pick使它们看起来好像从旧位置删除并在新位置创建。我想rebase本来会处理这个问题,但现在我已经向上游推送了,所以我无法测试。无论如何,如果您遇到类似情况,请注意。 - waldyrious
1
请注意:要将您在Feature-branch中的更改推送到远程仓库,您需要执行git push -f origin Feature-branch命令,因为您的Feature-branch现在被认为比origin/Feature-branch落后了1个提交。 - j-i-l
2
这个解决方案和CharlesB的解决方案之间的实际区别是什么? - Lii
3
@Bondax:因为它们并不是完全不同的概念。它们之间的关系非常密切,如果你知道如何变基(rebasing),但从未听说过“挑选”(cherry-picking)这个术语,那么很自然地会想到挑选,并将其描述为“变基单个提交”。 - PLL
显示剩余3条评论

144
git rebase --onto master branch~1 branch 

这句话的意思是“将上一个分支和分支点(也就是XX提交)之间的提交范围移动到master分支的头部”

此操作后,branch的指针移动到提交XX,因此您需要使用以下命令将其设置回去:

git checkout branch
git reset --hard branch@{1}^

这句话的意思是“将分支尖端重置为其上一个状态之前的提交”

因此,挑选一个提交(cherry pick)是一个更简单的解决方案...


5
对我来说似乎这个方法不起作用,我丢失了XX之前的提交记录,而且分支被变基到主分支上只有一个提交记录,但我以前从未使用过--onto,所以可能做错了什么。顺便说一下,原始发布者说要变基(rebase),但似乎他想要挑选(cherry-pick)。 - tewe
1
我的错误,rebase确实会在主分支上移动分支,必须进行重置。 - CharlesB
1
这个解决方案和tewe的解决方案之间的实际区别是什么? - Lii
1
@Lii,我唯一能看到的是它使用了三个步骤而不是四个。 - CharlesB
@Lii 这个更为通用。我有一个分支,想要将其变基到 master,但我只想变基除前4个提交以外的所有内容(我们称第四个提交为 X)。所以我只需要执行:git rebase --onto master X branch - alx - recommends codidact

100

其实这很简单。解决方案是进行交互式变基并“删除”您不想包含在变基中的所有提交。

git rebase -i <target_branch>,其中target_branch是您要变基到的分支

然后,您将编辑打开的文件,并pick选择您需要的提交,drop(或缩写为d)所有您不想携带的提交。


17
我认为这是一个更好的解决方案,而且它实际上回答了这个问题。 - GabrielOshiro
4
考虑到这个方案的普遍性、直观性和简洁性,它应该被接受为解决方案。 - Pablo Arias
1
+1 git rebase -i 是我最喜欢的 Git 命令。它非常灵活,同时又非常简单明了,清楚地展示了正在发生的事情。如果你学会了 git rebase -i,就不需要记忆任何其他的 rebase 命令了。 - Lii
非常好的答案,这是一个通用解决方案,因为它解决了在原始分支中保留任意数量的提交,而不仅仅是 cherry-pick 一个提交的问题。 - Arthur Colombini Gusmão
2
请注意,您未选择的提交将会“丢失”,除非它们在另一个分支上。或者像 Git 所称的那样,“被留下”。 - MiniGod
显示剩余3条评论

0

@Charles的回答是正确的。无论如何,我最终使用了这个命令很多次,主要是为了在项目上重新配置特定的配置

  * a8f9182 (HEAD -> production) 生产配置
  | * daa18b7 (pre) 预生产配置
  |/  
  | * d365f5f (local) 本地配置
  |/  
  * 27d2835 (dev) 将拯救世界的惊人新功能
* | 56d2467 (master) 项目的无聊现状
|/

我为此创建了一个新命令:

$ cat ~/bin/git-rebaseshot 
COMMIT=$1
DEST=${2:-HEAD}
git rebase ${COMMIT}^ ${COMMIT} --onto $DEST

通常情况下,您希望自动完成该命令的分支名称,因此请添加以下内容以调用此函数(添加到.bashrc或.profile):

_git_rebaseshot () 
{ 
    __gitcomp_nl "$(__git_refs)"
}

git自动完成将搜索它

您可以像这样使用此命令:

# rebase config on prepro on actual HEAD
$ git rebaseshot prepro 
# rebase config on local onto dev
$ git rebaseshot local dev
# rebase production config on master
$ git rebaseshot pro master

当你正确地划分功能时,可能性就是无限的。

* a8f9182(HEAD -> postgres)BBDD配置
* a8f9182(本地)本地配置
* a8f9182(调试)日志级别配置
* a8f9182(开发)新功能
|

我猜这就是quilt人们喜欢做的事情。

无论您提供什么sha/ref,此命令都将起作用:

$ git rebaseshot <Feature branch> master
$ git rebaseshot <commit of XX> master

// 你能提供任何项目链接,让我们看到它的实际应用吗? - Nathan Basanese
由于其性质,可用于rebase的分支不会在本地repo之外提交。只需在主分支上创建几个分支(日志级别、数据库连接、配置),并在它们之间使用命令即可看到效果。 - albfan
// 我遇到了一些问题。我会再试一次。 - Nathan Basanese

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接