不检出分支的情况下重新设置基础分支

78

我正在开发一个项目,构建时间有时很长,而且经常需要重新构建。如果我有一个旧分支正在进行一些工作(已提交,但基于较旧的父级),运行git checkout oldbranch会将工作目录更改为表示所有旧代码,这就使我需要再次运行完整构建。

但通常情况下,我只修改了一个或两个文件,其他文件不需要重置。我想做的是将此分支rebase到当前主线头,并保留这些文件中所做的更改。

基本上,如果已修改和,则我需要一种方法让这些更改以当前主线为基础,而不触及除这两个文件之外的任何文件。

是否有一种Git方式来做到这一点?目前我在使用补丁文件来完成此操作。


8
因为我可能已经转到另一个分支做更多的工作了?就这个工作而言,它已经被提交了。只是上游更新了,我正在一个更新更近的分支上修复另一个问题。 - Manishearth
如果你只是进行了一次checkout,为什么需要重新构建所有代码呢?难道你提交了已编译的二进制文件吗? - Leigh
2
只需在克隆中完成工作并将结果推回。git clone -b oldbranch . ../oldbranch; cd !$; work work commit commit lalala; git push origin oldbranch; cd - - jthill
@jthill那样做是可行的,但这并不是最理想的选择,不是吗? - Manishearth
2
为什么不呢?你有一个不想干扰的工作树,可以使用另一个。还有一件事——你知道git cherry-pick吗?它是rebase的相反操作,可以获取其他提交中的工作并将其应用到本地。 - jthill
显示剩余2条评论
5个回答

78

我现在也正在经历同样的事情,我学到git rebase可以让你一次性指定两个分支,基本上变成了git rebase <remote> <local>,例如。

git rebase origin/master dev

这执行了一个更高效的变基操作,使得您的文件不会全部被重写(如果您首先检出分支的情况下就是这种情况)。您仍然需要先解决合并冲突,最终您将获得一个本地 dev 分支被检出的存储库。


2
请注意,这可以与 --onto 标志组合使用。假设您在 master 工作树中意外地创建了一个功能分支,而不是在 release 工作树中从 release 分支创建。cd ../release-branch-worktree; git rebase --onto origin/release origin/master your_feature_branch完成了。只进行了最少必要的重新编译,工作已经移动了。 - Parker Coates
16
不确定这是否真的有所区别。文档中写道:“如果指定了<分支>,git rebase将在执行任何其他操作之前自动进行git checkout <分支>。” - Andreas Haferburg
2
这很有帮助,否则你就必须先 git checkout dev,然后再 git rebase(这将进行另一个checkout,这次是upstream的)。checkout dev 只是告诉git要rebase哪个分支,如果它在历史上很久远,它可能会无缘无故地触及各种文件。 - Paul Du Bois
2
如果你在rebase目标和你的分支之间删除了一个git子模块,检出可能会直接失败。这个答案中的方法可以在不修复由于子模块移除而导致的检出问题的情况下将历史分支rebase到当前主分支。 - Irfy

6
这似乎是使用 git cherry-pick 的好应用场景。你可以留在当前分支并从旧分支中挑选提交,而不是检出该分支并将其合并到您的当前分支上。
如果您要挑选的分支仅包含一个提交,则甚至可以通过其分支名称进行引用,例如:
git cherry-pick old-branch

例如,从old-branch中获取最新的提交并应用更改,以创建一个新的提交(保留提交消息、作者等),添加到当前分支中。


3
我查看了源代码,不幸的是它似乎总是检查基本提交(it always checks out the base commit),所以我认为你唯一能做到的就是将repo克隆到另一个目录中,在那里进行rebase,然后将更改推回原始repo,最后删除它。
请注意,将本地repo克隆到同一文件系统上的其他位置会对.git下的文件使用硬链接,因此实际上并不像您想象的那样慢。
编辑:实际上,更好的选择是使用工作树,类似于这样:
cd <your repo>
git worktree add ../tmp <branch to rebase>
cd ../tmp
git rebase master
cd -
git worktree remove tmp

请注意,您一次只能在一个工作树中检出一个分支。

2

如果您有

-A---o---o---o---o---D
 \---B---o---C

如果D被签出(我相信这是你的情况),你可以

git cherry-pick B..C

要达到这个目标:

-A---o---o---o---o---D---B---o---C

现在C是主分支,而且不会影响A..D中已经修改的文件。


1

注意: 如果您想寻找问题的简单解决方案,请查看以下答案,它们更简单。

如果您遇到“我想在不搞乱我的工作目录的情况下进行一些更改”的更一般性问题,请继续阅读。

如果您的存储库非常大,请查看有关git worktree add的注释。


克隆仓库,对克隆的副本进行变基操作,然后将其推送回去。

如果您在仓库内部,应该执行以下操作:

cd ..
git clone <name_of_your_repo_directory> tmp_repo
cd tmp_repo
git checkout origin/oldBranch
git rebase origin/master
git push -f origin HEAD:oldBranch

在 tmp_repo 中,origin 是您本地仓库的名称,当然也就是您克隆的那个仓库。
注意:这将产生与执行以下操作相同的效果。
git checkout oldBranch
git rebase master

在你的原始存储库中,而不是作为。
git checkout oldBranch
git rebase origin/master

我现在看到@jthill已经建议过了,而且是昨天的。我忽略了那个,抱歉。不过,我的答案有点长,并且带有完整的代码,所以我希望它对某些人有用。 - Frax
3
除了克隆,你还可以使用git worktree add ../新文件夹名称 要检出的分支名 - kerhac
1
@IsaacPascual 好的,对于大多数代码仓库来说,克隆工作是瞬间完成的(而且我认为对于本地项目,它将使用一些硬链接优化,使得速度比完全复制更快),所以这不是一个问题——一旦你有了克隆,它就比使用变基更加灵活——你几乎可以在那里做任何事情,而不用触及你的工作目录中的任何文件。另一方面,如果你的仓库确实很大,git worktree add 的解决方案也同样适用。 - Frax
1
@caoanan,当然可以。原帖明确要求不使用checkout的解决方案。 - Frax
git worktree 对于 OP 的情况要好得多。另外,关心别人的赞和踩有这么重要吗? - caoanan
显示剩余4条评论

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