1

抽象的

我对问题的研究越多,我对问题的总结就越清晰:如何使用 git 基础架构在 DAG 中获取一个顶点的父节点,它实际上是我的应用程序域中的一棵树?

阅读愉快。欢迎任何想法(特定于 git)。

问题

我有一个存储库,其历史是由我通过半自动命令编写的脚本构建的:

在此处输入图像描述

因此,我可以根据需要自动将任何类型的信息放入历史记录中,例如在执行命令时以黄色标记的那些标签。

所有这一切的一个关键要求是,当发生合并冲突时(它们必须发生,因为脚本的目的是让用户意识到合并冲突,并与其他用户联系以找到解决方案问题 -如果发生合并冲突就会出现问题,我希望脚本能够激发用户之间的交流)

现在,考虑到这一点,假设我想恢复一个合并提交。这对于非冲突合并非常有用,就像install-def在这张图片中一样。我只需要使用git revert -m 1 install-def(例如)。

当我需要 revert 时,该过程变得更加复杂install-abc,因为我想准确地恢复那些提交install-abc(合并提交)并且它是来自remotes/hacklet/abc/master.

换句话说,要获得只有profile/myfirstprofile和的线性历史remotes/hacklet/def/master

假设我可以用任何标签注释几乎任何提交,在半自动化脚本中实现这一目标的最简单和最安全的方法是什么?最好不必做任何与DAG相关的事情(或就此而言,递归),因为脚本是用 bash 编写的。

“还原”执行“卸载”。git revert -m 1 ...适用于在 的情况下恢复提交install-def,但install-abc它给出:

error: could not revert 18cff49... using hacklet/abc/master in profile
myfirstprofile hint: after resolving the conflicts, mark the corrected
paths hint: with 'git add <paths>' or 'git rm <paths>' hint: and
commit the result with 'git commit'

注意profile分支可以包含许多这样的install-提交,我只想删除其中一个。“安装”和“配置文件”都不能嵌套。

注意:我知道“模块”和“子树”。请不要提他们,我在这里做的事情是特定于应用程序域的(git是应用程序的一部分),这不是关于在开发周期中使用git。我已经彻底分析了这两个功能,它们根本不适合我脚本的应用程序域。


附录 1

我已经破解了这些信息,这让我觉得更容易:

在此处输入图像描述

如果您认为这变得过于复杂,那不是,它一遍又一遍地基本上是相同的模式:

在此处输入图像描述

start-profile在这里,只有install-abcinstall-foo没有冲突。但是树的结构看起来确实“相同”。

最后一个,有所有的冲突(除了第一个,install-abc),因为我喜欢 gitk 的多彩输出:

在此处输入图像描述

还有一个,但完全没有冲突:

在此处输入图像描述

注意:可能存在未在profile/myfirstprofile分支中标记的单个提交。在这种情况下,应该保留这些提交。真的没有其他东西应该“卸载”,但提交属于install-THING.


附录 2

虽然我before-install-发现可以使用git describe --tags $(git rev-list -1 install-abc). 然而:

给定标签install-def,如何在最后一张图片中找到before-install-ghi提交?

考虑到最后一个注释(中间未标记的提交),如何找到这些“子和父”提交适合粘在一起,而不需要install-THING我想要“卸载”?

PS:我很明白有向无环图directed这个词是什么意思,但是一定有一些特定于git的方式,使用git的基础设施和概念,我闻到了。

注意:我想避免每次我想“卸载”某些东西时都必须迭代树。

4

2 回答 2

1

我发现这个解释令人费解,因为它不使用常见的 git 术语,但据我所知,你问的是三个问题之一(很确定它是 #3):

  1. “我怎样才能重置到更早的分支提交?”
  2. “如何在不重置分支或签出历史上较早的提交的情况下恢复到合并提交的父级?”
  3. “当以后的下游提交基于我正在恢复的合并父级时,我如何恢复合并提交?”

对于这个解释,考虑这个图表:

G
|
F
|\
D E
|/ 
C
|
B
|
A

首先,一些术语:

提交“D”和“E”是提交“F”的“合并父母”,这是一个合并提交。您看到的图表几乎总是有两个父级,在您的术语中是“左”和“右”,在 git 术语中是“第一”和“第二”,但对合并可能拥有的父级数量没有限制。从技术上讲,所谓的“章鱼合并”是可能的,可能是三个或更多的父母。它们在实践中很少使用。

^使用git 中的语法规范地描述合并父级。请使用此语法,因为它会使您的问题更容易理解。第一个父母是^1,第二个父母是^2。换句话说:

F^1 is equivalent to D
F^2 is equivalent to E

您问题中最令人困惑的方面是了解您要达到的状态。因此,让我们解决三个可能的问题:


“我怎样才能重置到更早的分支提交?”

假设您尝试提交“D”并且不关心由“E”、“F”或“G”引入的更改。使用 Let_Me_Be 的答案:

git reset --hard D

如果您想改为到达“E”,但根本不关心保留由“D”、“F”或“G”引入的更改,那么同样会起作用。


“如何在不重置分支或签出历史上较早的提交的情况下恢复到合并提交的父级?”

假设您想要更复杂的东西:假设您想要恢复“D”的更改而不丢失“E”或“G”中的更改。这也是一个微不足道的操作,只要“F”和“G”都不是基于“D”的变化。在这种情况下:

git revert -m 2 'F'

命令语法的意思是“恢复合并提交'F'并使第二个父主线成为主线。” 生成的历史(再次假设“G”不是基于“D”)看起来好像“D”从未合并,但当然实际的历史将如下所示:

H
|
G
|
F
|\
D E
|/ 
C
|
B
|
A

其中“H”是还原提交。如果你想恢复'F'并使用'D'作为主线,只需在命令中更改指定的父编号:(git revert -m 1 'F'注意使用1“第一父”)。


“当以后的下游提交基于我正在恢复的合并父级时,我如何恢复合并提交?”

好的。这是最棘手的情况。如果您经常这样做,那么无论上下文如何,git 都不是您问题的解决方案。如果可以的话,Git 会尽量保持对冲突解决的完全不可知论。这就是重点:如果软件可以解决合并冲突,它就会解决。如果它们在您的工作流程中经常出现,那么问题就是您的工作流程,而不是您的 VCS。

如果您想避免历史修改(即git rebase,即使我们可以完全自动化也将是疯狂的),解决您的恢复冲突的唯一方法是恢复基于“D”代码的后续提交。换句话说:

git revert G
git revert -m 1 F

然后,如果您想使用 'git cherry-pick G' (原始的 'G' 提交哈希),您可以重新应用 'G',但应用它的可能性很大。您生成的历史将如下所示:

J - Reapplication of G
|
I - Revert of F
|
H - Revert of G
|
G
|
F
|\
D E
|/ 
C
|
B
|
A

如果这个答案最能描述您的情况,我强烈建议您在 git 以外的地方寻找您的应用程序。它的设计目的不是为了轻松地做你想做的事,因为你所做的将是人类之间相当破碎的版本控制工作流程。我怀疑您的应用程序能否捕捉到所有边缘情况。让它做出明智的冲突解决方案,即使冲突本身是相当可预测的,也不是一个容易的问题。我当然可能是错的。

于 2012-08-13T18:32:46.777 回答
0

这更像是一个猜测,然后是一个答案(因为我仍然对这个问题感到非常困惑)。

但我的猜测是你想这样做:

git reset --hard start_myfirstprofile
git merge remotes/hacklet/def/master

如果要重置为合并前的状态。

git reset HEAD~1

在正确的分支上应该没问题。

于 2012-08-13T10:27:56.327 回答