gpt4 book ai didi

git - 以增量方式使用过滤器分支的任何方式

转载 作者:行者123 更新时间:2023-12-01 15:24:32 37 4
gpt4 key购买 nike

有什么方法可以在分支上以增量方式使用分支过滤器吗?

大致这样说(但这实际上不起作用):

git checkout -b branchA origin/branchA  
git branch headBranchA
# inital rewrite
git filter-branch ... -- branchA
git fetch origin
# incremental rewrite
git filter-branch ... -- headBranchA..origin/branchA
git merge origin/branchA

最佳答案

我不确定您真正要实现的目标,因此我在这里要说的是“是的,有点,但可能不是您的想法,无论那是什么,它都可能无法帮助您实现目标”。

重要的是,不仅要了解filter-branch的功能,而且还要在某种程度上了解它的功能。

背景(使此答案对其他人有用)

git存储库包含一些提交图。这些是通过以下方式找到的:通过外部引用找到了一些起始提交节点(主要是分支和标签名称,还有带注释的标签,在这种情况下,我认为这并不是特别重要),然后使用这些起始节点来查找更多的节点,直到找到所有“可达”节点。

每个提交都有零个或多个“父提交”。大多数普通的提交都有一个父母。合并有两个或多个父母。根提交(例如存储库中的初始提交)没有父级。

分支名称指向一个特定的提交,它指向其父级,依此类推。

  B-C-D
/ \
A---E---F <-- master
\
G J <-- branch1
\ /
H-I-K <-- branch2

分支名称 master指向提交 F(这是一个合并提交)。名称 branch1branch2分别指向commit JK

我们还要注意,由于提交指向其父母,因此名称 master中的“可访问集”为 A B C D E Fbranch1的集为 A G H I J,而 branch2的集为 A G H I K

每个提交节点的“真实名称”是其SHA-1,它是提交内容的加密校验和。内容包括相应工作树内容的SHA-1校验和以及父提交的SHA-1。因此,如果您去复制一个提交但不做任何更改(不是一个位),您将获得相同的SHA-1,因此最终将获得相同的提交;但是,即使您只更改了一点(例如,更改提交者名称的拼写,任何时间戳或相关工作树的任何部分),您都会得到一个新的,不同的提交。
git rev-parsegit rev-list
这两个命令对于大多数git操作来说都是非常重要的。
rev-parse命令将 any valid git revision specifier转换为提交ID。 (它也有很多我们可以称为“辅助模式”的功能,它们允许将大多数git命令作为shell脚本编写,而 git filter-branch实际上是一个shell脚本。)
rev-list命令将修订范围(也在 gitrevisions中)转换为提交ID列表。仅给出一个分支名称,它就会找到该分支可到达的所有修订的集合,因此,在上面的示例提交图中,给出 branch2,它列出了 AGHIK的SHA-1值。 (默认情况下按相反的时间顺序列出它们,但是可以告诉他们按“地形顺序”列出它们,这对于 filter-branch很重要,而不是我打算在这里深入了解细节。)

但是,在这种情况下,您将需要使用“提交限制”:给定修订范围(如 A..B语法)或给定诸如 B ^A的内容, git rev-list将其输出修订集限制为 B可以访问但不可访问的提交。来自 A。因此,给定 branch2~3..branch2或等效地称为 branch2 ^branch2~3,它列出了 HIK的SHA-1值。这是因为 branch2~3名称落实 G,因此将 AG的提交从可达集中删除。
git filter-branch
filter-branch脚本相当复杂,但是总结其对“命令行上给定的引用名称”的操作并不难。

首先,它使用 git rev-parse查找要过滤的一个或多个分支的实际头部修订。实际上,它使用了两次:一次获取SHA-1值,一次获取名称。给定例如 headBranchA..origin/branchA,它需要获取“真实全名” refs/remotes/origin/branchA:
git rev-parse --revs-only --symbolic-full-name headBranchA..origin/branchA

将打印:
refs/remotes/origin/branchA
^refs/heads/headBranchA

filter-branch脚本丢弃任何以 ^前缀的结果,以获取“正引用名称”列表;这些都是最终打算重写的内容。

这些是git-filter-branch manual中描述的“正引用”。

然后,它使用 git rev-list获取要在其上应用过滤器的提交SHA-1的完整列表。这就是 headBranchA..origin/branchA限制语法出现的地方:该脚本现在知道仅适用于 origin/branchA可以到达的提交,而不适用于 headBranchA可以到达的提交。

一旦有了提交ID列表, git filter-branch实际上就会应用过滤器。这些做出新的 promise 。

与往常一样,如果新提交与原始提交完全相同,则提交ID不变。但是,如果想使用filter-branch,可能会在某些时候更改某些提交,从而为它们提供新的SHA-1。这些提交的任何直接子代都必须获取新的父ID,因此这些提交也将更改,并且这些更改会传播到最终分支提示。

最后,在将过滤器应用于所有列出的提交之后, filter-branch脚本更新了“正引用”。

下一部分取决于您的实际过滤器。为了说明起见,我们假设您的过滤器在每次提交时都会更改作者姓名的拼写,或者在每次提交时(或类似情况)更改时间戳,以便每次提交都被重写,但出于某种原因它会使根提交保持不变,以便新分支和旧分支具有共同的祖先。

我们从这个开始:
git checkout -b branchA origin/branchA

(您现在位于 branchA上,即 HEAD包含 ref: refs/heads/branchA)
git branch headBranchA

(这会使另一个分支标签指向当前的 HEAD提交,但不会更改 HEAD)
# inital rewrite
git filter-branch ... -- branchA

在这种情况下,“正参考”是 branchA。除了根提交 branchA之外,其余的提交都是可以从 o到达的每个提交,即下面的所有 R节点(开始提交图组成的图示)。
R-o-o-x-x-x   <-- master
\
o-o-o <-- headBranchA, HEAD=branchA, origin/branchA

复制每个 o提交,并移动 branchA指向最后一个新提交:
R-o-o-x-x-x   <-- master
| \
| o-o-o <-- headBranchA, origin/branchA
\
*-*-*-*-* <-- HEAD=branchA

以后,您可以从远程 origin拾取新内容:
git fetch origin

假设这添加了标记为 n的提交(我将仅添加一个):
R-o-o-x-x-x   <-- master
| \
| o-o-o <-- headBranchA
| \
| n <-- origin/branchA
\
*-*-*-*-* <-- HEAD=branchA

这是出问题的地方:
git filter-branch ... -- headBranchA..origin/branchA

这里的“positive ref”是 origin/branchA,因此将被移动。 rev-list选择的提交只是标记为 n的那些,这就是您想要的。这次让我们拼写重写的提交 N(大写):
R-o-o-x-x-x   <-- master
| \
| o-o-o <-- headBranchA
| |\
| | n [semi-abandoned - filter-branch writes refs/original/...]
| \
| N <-- origin/branchA
\
*-*-*-*-* <-- HEAD=branchA

现在,您尝试 git merge origin/branchA,这意味着 git merge提交 N,这需要找到 *链与commit N ...之间的合并基础,这就是 R的提交。

我认为,这根本不是您的意思。

我怀疑您要执行的操作是将 N落实到 *链上。让我们画一下:
R-o-o-x-x-x   <-- master
| \
| o-o-o <-- headBranchA
| |\
| | n [semi-abandoned - filter-branch writes refs/original/...]
| \
| N <-- origin/branchA
\
*-*-*-*-*-N'<-- HEAD=branchA

这部分还可以,但将来会一团糟。事实证明,您实际上根本不需要提交 N,并且也不想移动 origin/branchA,因为(我假设)您希望以后可以重复 git fetch origin步骤。因此,让我们“撤消”此操作并尝试其他操作。让我们完全删除 headBranchA标签,并从此开始:
R-o-o-x-x-x   <-- master
| \
| o-o-o <-- origin/branchA
\
*-*-*-*-* <-- HEAD=branchA

让我们为 origin/branchA指向的提交添加一个临时标记,然后运行 git fetch origin,以便获得commit n:
R-o-o-x-x-x     <-- master
| \ .--------temp
| o-o-o-n <-- origin/branchA
\
*-*-*-*-* <-- HEAD=branchA

现在,让我们将commit n复制到 branchA,并在我们对其进行复制的同时,对其进行修改(执行对 git filter-branch的任何修改)以获得一次提交,我们将只调用 N:
R-o-o-x-x-x     <-- master
| \ .--------temp
| o-o-o-n <-- origin/branchA
\
*-*-*-*-*-N <-- HEAD=branchA

完成此操作后,我们将删除 temp并准备重复该循环。

使它工作

这就留下了几个问题。最明显的是:我们如何复制 n(或几个/很多 n)然后进行修改?好吧,假设您已经使用了 filter-branch,最简单的方法是使用 git cherry-pick复制它们,然后使用 git filter-branch过滤它们。

这仅在 cherry-pick步骤不会遇到树差异问题的情况下才有效,因此这取决于您的过滤器执行的操作:
# all of this to be done while on branchA
git tag temp origin/branchA
git fetch origin # pick up `n` commit(s)

git tag temp2 # mark the point for filtering
git cherry-pick temp..origin/branchA
git filter-branch ... -- temp2..branchA

# remove temporary markers
git tag -d temp temp2

如果您的筛选器分支更改了树,该方法将永远无法使用怎么办?好吧,我们可以求助于直接将过滤器应用于 n提交,给出 n'提交,然后复制 n'提交。这些( n'')提交将保留在本地(已过滤的) branchA上。 n'提交一旦被复制就不再需要,因此我们将其丢弃。
# lay down temporary marker as before, and fetch
git tag temp origin/branchA
git fetch origin

# now make a new branch, just for filtering
git checkout -b temp2 origin/branchA
git filter-branch ... -- temp..temp2
# the now-altered new branch, temp..temp2, has filtered commits n'

# copy n' commits to n'' commits on branchA
git checkout branchA
git cherry-pick temp..temp2

# and finally, delete the temporary marker and the temporary branch
git tag -d temp
git branch -D temp2 # temp2 requires a force-delete

其他问题

我们已经(在图形图中)介绍了如何将新提交复制并修改到“增量过滤”的 branchA中。但是,如果当您查阅 origin时发现提交已被删除,会发生什么情况?

也就是说,我们从以下开始:
R-o-o-x-x-x   <-- master
| \
| o-o-o <-- origin/branchA
\
*-*-*-*-* <-- HEAD=branchA

我们像往常一样放下临时标记,然后执行 git fetch origin。但是他们所做的是删除最后的 o提交,并在其末端强制执行。现在我们有:
R-o-o-x-x-x   <-- master
| \
| o-o <-- origin/branchA
| `o.......temp
\
*-*-*-*-* <-- HEAD=branchA

此处的含义是,我们可能也应该将 branchA备份为一个修订版本。

是否要处理此完全取决于您。我会在这里注意到 git rev-list temp..origin/branchA的结果在这种特殊情况下将为空(修订后的 origin/branchA上没有从 temp无法到达的提交),但是 origin/branchA..temp不会为空:它将列出一个“已删除”的提交。如果删除了两个提交,它将列出两个提交,依此类推。

控制 origin的任何人都有可能删除了多个提交并添加了一些其他新的提交(实际上,这正是“上游重新设置”所发生的)。在这种情况下,两个 git rev-list命令都是非空的: origin/branchA..temp将显示删除的内容, temp..origin/branchA将显示添加的内容。

最后,控制 origin的任何人都有可能为您彻底破坏一切。他们能:
  • 完全删除其branchA
  • 将其标签branchA指向一个不相关的分支。

  • 同样,由您自己决定是否以及如何处理这些情况。

    关于git - 以增量方式使用过滤器分支的任何方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22014090/

    37 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com