git 我可以在没有明确指定父分支的情况下,在分支的分叉点上重定基吗?

mec1mxoz  于 2022-11-20  发布在  Git
关注(0)|答案(3)|浏览(139)

我经常使用git rebase -i在发布之前清理我的历史记录。通常我想把提交编辑回当前分支分叉的地方,而不改变它的分叉点。我是这样做的:git rebase -i $(git show-branch --merge-base $PARENT_BRANCH HEAD)
这是一个丑陋的命令,我正在寻找一个更好的方法。只要我在做,我希望git能自动找出正确的父对象。
我 * 认为 * 我需要的是git rebase -i --fork-point $(something)的别名,其中something查找当前分支最近的共同祖先分支。它不需要是防弹的。如果它适用于线性主题分支,这就足够了。

yeotifhr

yeotifhr1#

有了Git 2.24(2019年第四季度),不再需要git rebase -i --onto @{upstream}...HEAD
新的“git rebase --keep-base <upstream>“试图找到被重定基的主题的原始基,并在同一个基之上重定基,这在运行“git rebase -i“(及其受限变体“git rebase -x“)时很有用。
该命令还学会了在更多情况下快进,而不是重放以重新创建相同的提交。
参见commit 414d924commit 4effc5bcommit c0efb4ccommit 2b318aa(2019年8月27日)和commit 793ac7ecommit 359eceb(2019年8月25日),作者为Denton Liu ( Denton-L )
帮助者:Eric Sunshine ( sunshineco )Junio C Hamano ( gitster )Ævar Arnfjörð Bjarmason ( avar )Johannes Schindelin ( dscho )
参见Ævar Arnfjörð Bjarmason ( avar )commit 6330209commit c9efc21(2019年8月27日)和commit 4336d36(2019年8月25日)。
帮助者:Eric Sunshine ( sunshineco )Junio C Hamano ( gitster )Ævar Arnfjörð Bjarmason ( avar )Johannes Schindelin ( dscho )
(由Junio C Hamano -- gitster --合并至commit 640f9cd,2019年9月30日)

重定基底:示教重定基底--keep-base

一种常见的情况是,如果用户正在处理一个主题分支,并且希望对中间提交或自动压缩进行一些更改,他们将运行如下命令

git rebase -i --onto master... master

以便保留合并基数

这在向Git邮件列表贡献补丁序列时很有用,通常从当前的'master'开始。
在开发补丁的同时,“master”也会被进一步开发,有时保持在“master”之上重定基并不是最好的主意,而是保持基提交不变。
除此之外,如果用户希望在不改变任何内容的情况下测试主题分支中的各个提交,则可以运行

git rebase -x ./test.sh master... master

由于在分支和上游的合并基础上重定基是一种常见的情况,因此引入--keep-base选项作为一种快捷方式。
这样我们就可以将上面的内容重写为

git rebase -i --keep-base master

以及:

git rebase -x ./test.sh --keep-base master

分别为。
git rebase缐上手册现在包括:

--keep-base

将创建新提交的起点设置为合并基础<upstream> <branch>
运行“git rebase --keep-base <upstream> <branch>”等效于运行“git rebase --onto <upstream>... <upstream>”。
如果要在上游分支上开发特征,此选项非常有用。
在处理功能时,上游分支可能会前进,因此保持在上游分支之上重定基并不是最好的办法,而是保持基提交不变。
虽然这个选项和--fork-point都会在<upstream> and <branch>之间寻找合并基底,但是这个选项会使用合并基底作为建立新提交的 * 起点 *,而--fork-point则会使用合并基底来决定要重新设定基底的 * 提交集合 *。
mvds建议在评论中将此与git rebase --reapply-cherry-picks结合
在我的工作流程中,要为用C编写的固件开发一个最小的二进制补丁,我需要返回到某个提交哈希,进行分支,执行修改,并在生成的二进制文件中找到差异。
在那次提交之后的时间里,我们的编译器得到了升级,变得更加智能,将代码库变成了非编译代码。
为了解决这个问题,我挑选了所有需要再次编译的提交。
除非使用--reapply-cherry-picks,否则这些精心挑选的提交将打乱--keep-base所完成的魔法。
辩论在Git邮件列表中
在Git 2.39(Q4 2022)之前,“git rebase --keep-base”(man)用于丢弃已经被挑选到上游的提交,即使“keep-base”意味着在其上重建历史的库还不包括这些被挑选的提交。
--keep-base选项现在隐含--reapply-cherry-picks--no-fork-point选项。
请参阅commit aa1df81commit ce5238acommit d42c9ffcommit a770602commit f21becdcommit b8dbfd0commit 05ec418commit 96601a2(2022年10月17日),作者为Phillip Wood ( phillipwood )
(2022年10月30日由Taylor Blau -- ttaylorr --合并至commit 003f815
第1331章:隐含--no-fork-point
签署人:菲利普伍德
给定选项的名称,如果--keep-base实际上更改了分支的基址,而没有在命令行上显式给定--fork-point,则会引起混淆。
--keep-base与显式--fork-point的组合仍然受支持,即使--fork-point意味着如果上游分支已经倒带,我们不保持相同的基数。
我们这样做是为了防止任何人依赖[tested in t3431](https://lore.kernel.org/git/20200715032014.GA10818@generichostname/)的此行为
git rebase现在在其手册页中包括:
git rebase --reapply-cherry-picks --no-fork-point --onto <upstream>...<branch> <upstream> <branch> .
git rebase现在在其手册页中包括:
如果在命令行中指定了<upstream>--keep-base,则默认值为--no-fork-point,否则默认值为--fork-point。另请参阅git config中的rebase.forkpoint
在Git 2.39中:
第1331章:隐含--reapply-cherry-picks
报告人:菲利普·布兰
签署人:菲利普伍德
由于--keep-base不会重定分支的基,因此如果它删除了那些被挑选到上游分支的提交,就会造成混乱。
由于--reapply-cherry-picks不受“apply”后端支持,因此此提交通过强制上游提交匹配onto提交(除非给出--no-reapply-cherry-picks)来确保重新应用挑选。
git rebase现在在其手册页中包括:
在上游之上重新建立基础,但保持基础提交不变。由于基础提交不变,此选项隐含--reapply-cherry-picks以避免丢失提交。
git rebase现在在其手册页中包括:
如果没有--keep-base(或者如果给定了--no-reapply-cherry-picks),这些提交将被自动丢弃。

因为这需要阅读所有的上游提交,所以在有大量上游提交需要读取的仓库中,这可能是非常昂贵的。
当使用'merge'后端时,每个丢弃的提交都会发出警告(除非指定--quiet)。除非advice.skippedCherryPicks设置为false,否则也会发出建议(请参阅git config)。

dgiusagp

dgiusagp2#

首先,--fork-point用于远程跟踪名字,它的工作方式是使用上游提供的reflog,更多信息请参见Git rebase - commit select in fork-point mode
第二,但可能更重要的是,您可以运行git rebase *upstream*git rebase --onto *newbase**upstream*,甚至只运行git rebase。当使用双参数形式时,您获得了很大的自由:

    • upstream * 参数限制了哪些提交将被复制。我们稍后会对此进行更多的讨论。
    • newbase * 参数选择哪一个提交是添加副本的点。这里的实际哈希ID非常重要。

也就是说,当你 do 使用--onto时,你必须为--onto * newbase * 参数选择一个精确的提交。这意味着你可以非常宽松地使用什么作为 * upstream * 参数。但是当你 * 不 * 使用--onto时,* upstream * 被用于 * 两种目的 。因此,upstream * 参数是一个需要非常小心和精确的参数。一个目的是宽松和自由,另一个目的不是。
这意味着你可以使用 * 两个 * 参数来重新获得你想要的(但可能不需要)额外的宽松,或者 * 没有 * 参数来获得方便。
(The由于问题的更新,下一部分的结构很奇怪。)

git show-branch --merge-base X Y = git merge-base X Y

git show-branch命令现在在给定多个参数时使用--merge-base--independent。版本号为XY。(git merge-base现在也接受--independent,而不是仅仅采用octopus策略,但这只适用于使用三个或更多提交说明符的情况。)
我更喜欢这里的git merge-base,因为我认为它更明显(当然输入起来也更短)。

如果没有参数,则 upstream 来自分支设置

每个分支可以有一个(但只能有一个)上游设置。
任何分支 B 的上游设置通常是origin/*B*,因为我们倾向于将它们推送到GitHub或Bitbucket或某个企业Web服务器,我们将其URL保存在名称origin下,这样我们就不必一直输入它。并且希望有一个git rebase将自动使用的 second 设置,那么您就有点不走运了但是如果你 * 还没有 * 用完上游的那个,就把上游设置成你想让git rebase自动使用的值。如果您现在在feature-X上,并希望它基于develop重新定基:

$ git branch --set-upstream-to=develop

现在featuredevelop作为它的一个上游。运行git rebase(带或不带-i)将重定基,就像运行带有develop作为其 * upstream * 参数的相同命令一样。
如果您已经用完了上游的别名,则可以创建自己的别名:

alias.name = !git rebase "$@" $(git config --get branch.$(git symbolic-ref --short HEAD).base) #

(pick一些名字):这允许你使用git config,为develop配置一个额外的名称,branch.feature-X.base$(git symbolic-ref --short)提取当前分支名称,git config --get获取设置,然后rebase使用它作为它的一个上游参数。

单一限制器和目标/ newbase参数的缺点

这里的缺点是,给定以下形式的图:

o--o   <-- develop
            /
...--o--o--o
            \
             A--B--C   <-- feature-X (HEAD)

最后得到的是develop提示之后的副本:

A'-B'-C'  <-- feature-X (HEAD)
                 /
             o--o   <-- develop
            /
...--o--o--o
            \
             A--B--C   [abandoned]

当你想把副本保存在同一个地方时,只需修改提交文本或者把两个副本挤在一起:

o--o   <-- develop
            /
...--o--o--o--A'-B'-C'  <-- feature-X (HEAD)
            \
             A--B--C   [abandoned]

使用--onto
对于双参数形式,--onto参数选择目标提交:

o--o   <-- develop or whatever
            /
...--o--o--*    [pick this commit as target]
            \
             A--B--C   <-- feature-X (HEAD)

现在副本将在*之后进行。要复制的提交集实际上是通过使用*upstream*..feature-X来确定的:也就是说,通过从feature-X开始并向后工作可到达的提交,但是不包括通过从 * upstream * 开始并向后工作可到达的提交。
现在你只需要找到提交*。如果你有两个名字,比如feature-Xdevelop,你可以使用gitrevisions three-dot syntaxdevelop...feature-Xfeature-X...develop(当只有一个合并基时,语法是对称的)来指定提交*。这只在新版本的Git中有效:在旧版本中,使用git show-branchgit merge-base(使用两个提交哈希ID,它们的行为方式相同)。
指定提交*作为--onto目标后,你可以再次允许分支的上游作为限制器。也就是说,你可以省略显式的 * upstream *,因为它默认为实际的上游。而且,由于你可以使用@{upstream}@{u}语法,你可以创建一个非常短和简单的别名,就像你在自己的答案中所做的那样。
如果你想保留一个独立的上游(origin/feature-X)和基,你可以回到为每个分支名称配置一个额外值的想法。在这种情况下,你需要使用它两次,所以你可能需要一个完整的脚本来代替别名,在那里你可以进行错误检查:

#! /bin/sh
# git-base - rebase on the current branch's base setting
. git-sh-setup
branch=$(git symbolic-ref --short HEAD) || exit
base=$(git config --get branch.$branch.base) || die "branch.$branch.base is not set"
mbase=$(git merge-base $base HEAD) || die "there is no merge base"
git rebase --onto $mbase "$@" $base

将其命名为git-base,并将其放在路径中,现在就可以运行git base了。

a8jjtwal

a8jjtwal3#

经过一个小时左右,我找到了一个足够好的东西。如果分支有一个上游集合,并且如果上游集合是我想要比较的集合,这个命令就可以完成我想要的任务:

$ git rebase -i --onto @{upstream}...HEAD

这些三点并不像git-log和类似的命令那样执行相同的操作。
作为一种特殊情况,如果正好有一个合并基,则可以使用“A... B”作为A和B的合并基的快捷方式。
所以这就是说“找到HEAD的合并基和它自己的上游,然后根据它来重定基”。通常本地分支没有上游,但是可以设置它,并且有一个gitconfig选项(autoSetupMerge)会自动为新分支做这个操作。因此:

$ git config --global branch.autoSetupMerge always
$ git config --global alias.fixup 'rebase -i --onto @{upstream}...HEAD'
$ git branch childbranch -u parentbranch  # Repeat for other branches as needed.

在此之后,我可以编辑历史回到分支点很容易:

git fixup

而且它对所有未来的分支机构都有效。
(note:请参阅torek's answer,以了解此处的详细说明)

相关问题