Gre*_*reg 2 git merge git-merge git-commit
是否有任何命令可以在两个分支中查找文件的共同祖先?
假设有一个文件在两个分支中独立修改。我想找到两个分支共有的该文件的最后一个版本。我相信这可以归结为在两个分支中找到该文件的单父提交。
但是,merge-base 只允许查找提交的父提交,而不是文件。我尝试指定最后两次提交修改各自分支中的文件,但我得到的父提交不在任一分支中该文件的更改历史记录中,这可能是由于提交通常包含的更改超过一个文件。
\n\n\n是否有任何命令可以在两个分支中查找文件的共同祖先?
\n
不,或者是,或者也许:这取决于你的意思。
\n\n\n\n\n假设有一个文件在两个分支中独立修改。我想找到两个分支共有的该文件的最后一个版本。我相信这可以归结为在两个分支中找到该文件的单父提交。
\n
文件没有父提交。只有提交才有父提交。
\n\n更糟糕的是,每次提交都会存储每个文件(即,提交时属于暂存区域一部分的每个文件)。因此,从某种意义上说,这要么是每次提交,要么是常规的普通合并基础。显然这不是你的意思,所以让我们看看我们还能说些什么。
\n\n让我们尝试一个思想实验。假设您有两个分支提示br1,并且br2最终有一个共同的祖先提交:
o--o--o--Y <-- br1\n /\n...--X\n \\\n o--o--o--Z <-- br2\nRun Code Online (Sandbox Code Playgroud)\n\n还考虑一个更复杂的图,它仍然有一个共同的祖先和两个分支提示:
\n\n o\n / \\\n o o--o--Y <-- br1\n / \\ /\n...--X o\n \\\n o--o--o--Z <-- br2\nRun Code Online (Sandbox Code Playgroud)\n\n考虑到图表的方式和工作方式git merge,“常规”合并(或使用git merge-base)将找到 merge-base X,此时我认为大多数人会同意某些文件已存在X并被传播(可能通过重命名)到Y,也Z有共同的祖先X。Y这个共同祖先可能出现在或中的不同路径名下Z(甚至出现在 和 中Y ) Z,但它仍然是共同祖先,因此它被用作合并基础版本。
但这里有一个问题:git 不记录重命名。相反,它每次进行差异时都会“发现”它们。为了发现文件generic/b.cinX现在位于specific/b.c中Y,git 必须将 下的整个树X与 下的整个树进行比较Y。这意味着它必须找到 commit X。
这对于常规合并来说并不太难,因为它使用提交图:它从两个提交开始Y,并向Z后遍历历史记录以找到最近的公共提交(当然就X在这里)。一旦我们知道(或 git 知道)要使用X,它就会生成两个差异,X-vs-Y和X-vs- Z,然后它就可以合并对公共文件内容的更改,无论它在和中具有什么路径。YZ
(十字合并还有一个第二个问题,其中可能有多个最近的共同提交,但我们现在可以忽略它。)
\n\n但是,如果我们(至少暂时)放弃查找重命名的想法,那么我们可以在给定某些路径的情况下p使用不同的方法,我认为这就是您要问的问题:
cy之间的每个提交(包括和 从 向后工作),以及和之间的每个提交(同样从 向后工作),比较和。XYXYczXZZcy/pcz/p请注意,这会将X\ 的路径版本p与X\ 的版本(当然是相同的)进行比较,并且还将与任一提交链上的每个版本进行比较,同时还将每个版本与其他版本进行比较。
制作了这个完整的矩阵(我们可以稍后优化),我们现在可以找到许多“有趣的”提交:
\n\ncy中的最后一次提交具有相同的内容(这是该链中未更改的最新提交)XYpXpcz中的最后一次提交具有相同的内容(另一个链中最新的未更改)XZpXcy位置与p提交中的内容相同Y(这是最后一次在-to-链p中修改路径)XYcz位置p与提交中的内容相同Zp任一链中的任何提交与另一个链中的任何提交具有相同的内容。我想您可能正在考虑在这里查找第 1 项和第 2 项。但尚不清楚原因。如果您只关心路径下存储的内容p,我们已经(在上面)确定这两个提交存储的内容与p您在 中找到的内容相同X。因此,在识别这些内容方面“同样好”,您不妨使用 commit 。X:pX
如果您正在谈论查找项目 3 和 4,那么也不太清楚为什么p,因为我们已经确定这些内容与它们的最尖端提交具有相同的内容,因此和对于识别这些内容。Y:pZ:p
但也许您正在处理第 5 项:在两个链上提交,其中路径下的内容p相同(与另一个链上的其他提交相同),但不一定与最顶端提交中的内容相同。
可以有很多这样的对。例如,假设在X(找到的绝对共同祖先git merge-base)中,路径p有五行。然后,在前进到 的过程中Y,该路径中的第一次提交将删除最后一行。同时,在X-to-Z序列中,多次提交保留所有 5 行,然后删除最后一行。现在,这个版本p在两条开发线中都是相同的,直到下一次提交修改p. 假设该序列位于X另一Z行被删除的位置。然后在X-to-Y序列中,同一行被删除;然后,两次提交都会删除更多行,直到最终文件在一个或两个分支提示处完全为空。
定义“最近”还存在另一个问题。让我们再次看一下更复杂的X图形Y片段,但加入一些更明显的字母:
R\n / \\\n P T--o--Y <-- br1\n / \\ /\n...--X S\nRun Code Online (Sandbox Code Playgroud)\n\n假设该路径在 commits和p中具有相同的内容,但在 和 中具有不同的内容。两者与或 的图形距离相同。只要您只关心 path ,这可能是无关紧要的,但它确实表明不一定有唯一的提交。RSPTXYp
在我开始讨论您想要使用的一些命令之前,已经有很多废话了,以便解决您想要解决的问题。
\n\n将使您更接近解决方案的命令(甚至可能一直到那里,取决于您想要什么,尽管您似乎可能需要使用其他命令,有些甚至不需要 git 命令)是git rev-list。这可以找到修改了特定路径的提交(与这些提交的父提交相比;请注意,通常必须专门处理合并,因为它们有多个父提交)。如果您确实使用一个或多个路径来限制 列出的修订git rev-list,请注意,它将执行“历史简化”,以便从其输出中省略一些提交。根据您希望如何处理 DAG 级分支(如更复杂的链中的分支X)Y,这可能就是您想要的。
基本上,git rev-list X..Y -- path会发现从 可以到达的提交Y,不包括从 可以到达的那些X修改path,其中“修改”意味着“与父级的差异显示对该路径的更改”。(有关如何处理合并,请参阅文档。)提交的列出顺序取决于您选择的排序(有或没有拓扑约束;请参阅“提交排序”部分)。
如果您使用 重复此操作X..Z,您可以找到哪些提交修改了那里的路径。
这两个git rev-list本质上是从整个修订链X到两个分支提示,但是因为它们让您将其输出限制为“修改某些路径的提交”,所以它们可以优化我在思想实验中概述的过程。
您可能想X在此处包含提交。默认情况下,rev-list不会:您可以提前开始一次提交(在 的父级X),但如果X本身是合并,则可能会失败;或者您可以使用--boundary,它指示rev-list包含提交X的 SHA-1 (前缀为-)。
找出特定路径下存储的内容在两次不同的提交中是否相同\xe2\x80\x94显然如果你在这里使用相同的提交ID两次内容是相同的,但它仍然有效\xe2\x80\x94you可以比较存储的 blob 的 SHA-1 ID:
\n\npath=dir/file\n...\nrev_a=... # something from git rev-list, for instance\nrev_b=...\nif [ $(git rev-parse ${rev_a}:${path}) = $(git rev-parse ${rev_b}:${path} ]; then\n ... the contents match ...\nelse\n ... the contents differ (at least slightly) ...\nfi\nRun Code Online (Sandbox Code Playgroud)\n\n这些都不会检测重命名;为此,您必须使用成熟的git diff(打开重命名检测)。
| 归档时间: |
|
| 查看次数: |
1040 次 |
| 最近记录: |