在将功能分支合并到与功能分支来自现在已删除分支同名的分支后,我们缺少一些提交。
为了更清楚一点,这是事件的顺序:
完成合并后,我在功能分支中缺少生产支持中的提交。
编辑:合并后,我错过了一些但不是所有来自Prod支持中的功能分支的提交。
我是否认为删除生产支持部门可能会导致此问题?或者我应该去别处看看?
答案 0 :(得分:2)
合并不会删除提交。合并要么添加提交,要么在“快进”合并的情况下,只需“向前滑动标签”。
我发现提交图的图纸,甚至(或者可能是“特别”)简化的图表,帮助很多。
根据你的描述你有类似的东西:
..- o - o - o - o - o - o <-- root
\
o - C - P <-- production_support
\
o - o <-- feature
其中每个o
是一个“无趣的”提交(或者甚至可能是相当多的提交),而大写字母是我打算在下面讨论的提交,所以我给了它们字母名称。< / p>
这里有三个分支,其分支标记为root
,production_support
和feature
。提交C
是production_support
和feature
共有的提交,提交P
只能通过branch-tip-label production_support
访问。
同样,根据您的描述,有人删除了分支标签production_support
。该图现在看起来像这样:
..- o - o - o - o - o - o <-- root
\
o - C - P [no label - abandoned]
\
o - o <-- feature
提交P
现在无法访问,除非通过git的“reflogs”,它会记录标签用于指向的位置。
删除标签时删除分支标签production_support
的reflog,这样就没有帮助;但至少在一个存储库中,在提交P
时,它是在HEAD
以及production_support
上创建的。那个特定的HEAD-reflog-entry可以保护P
一段时间(默认为30天)。
一旦提交的最后一个引用消失,提交就有资格进行垃圾回收。因此,如果没有其他任何内容挂起来提交P
,它将与其他垃圾一起收集并丢弃。
回到你的描述,在上面发生之后(也可能在P
被垃圾收集之后),有人做了一个新的分支标签,拼写方式与旧的分支标签相同,然后提交了在那里,将图片改为这样的:
o <-- production_support
/
..- o - o - o - o - o - o <-- root
\
o - C
\
o - o <-- feature
然后你做了:
$ git checkout production_support && git merge feature
它创建了一个新的合并提交M
(图形现在变得非常混乱:-) ...我们可以重绘它,但这可能同样糟糕,在这一点上):
o - M <-- production_support
/ /
..- o - o - o - o - o - o <-------- root
\ |
o - C |
\ /
o - o <---- feature
确实提交P
(仍然)无处可见,但这不是因为合并。合并刚刚添加了一个新的提交M
,有两个父母。 M
的第一个父级是旧的分支提示(o
左侧的M
; production_support
现在指向M
而是第二个parent是合并提交(feature
指向的提交。)
要返回提交P
,如果它完全存在(没有被垃圾收集),你必须通过branch-label-name之外的其他方式找到它,因为当分支标签时它就消失了production_support
首先被删除。查看创建它的存储库中的HEAD
reflog,或使用git fsck
查看是否存在可以通过添加标签(例如分支或标记名称)来恢复的“悬空提交”他们。
在这种特殊情况下,提交P
有C
作为其直接祖先,如果您发现SHA-1 ID提交并认为可能为{{ 1}},你可以通过查看它和它的父母来检查。例如:
P
这将显示“might-be-P”及其父项的日志,如果提交$ git fsck
...
dangling commit de7c26f2b124f0038ab0ed03da9cb47647fc9867
...
$ git log de7c26f2b124f0038ab0ed03da9cb47647fc9867
显示可能是它。或者,您可以通过收集所有候选项,查找提交C的原始SHA-1以及比较每个候选项的父项来自动化它:
C
(未经过实际测试,可能包含拼写错误或类似内容)。
如果$ id_for_c=... # find SHA-1 for commit C, put that in here
$ for id in $(cat /tmp/candidates); do
> [ $(git rev-parse ${id}^) = $id_for_c ] && echo "likely match: ${id}"
> done
确实代表了一系列提交,那么检查P
作为直接父级将是毫无意义的(您需要知道找回C
的距离)但是您仍然可以进行血统测试(请参阅C
的{{1}}选项。)
答案 1 :(得分:2)
好的,每个人(我们所有人:-))都将这个问题解释为“查看提交图,我希望成为合并提交的祖先的一些提交,不是合并提交的祖先”。但那不是你要问的。你的意思是“我做了一个合并,似乎进展顺利,但现在合并的内容并不是我所期望的。”
让我们快速了解一下合并的机制,特别是它在git中的完成方式。让我们从这个提交DAG开始,基本上与我的其他答案中的相同,但我会略有不同。我还将小o
个节点更改为*
,以便首都 - O
节点更加明显。
..- * - * - B - * - * - * <-------- root
\ \
\ O <---- production_support
\
* - * - T <---- feature
要合并这些,请运行:
$ git checkout production_support; git merge feature
重要:我假设此处没有--strategy=ours
或-X ours
个参数。
要进行合并,git需要识别三个特定的提交:“O
urs”,“T
继承人”和“B
ase”。提交O
只是当前分支的提示,即production_support
指向的提交(因为这是您在开始合并时检出的内容)。提交T
来自git merge
的参数,而且只是提交本身。
提交B
是棘手的。 Git需要为两个分支找到“merge-base”。为此,它从两个已识别的提交中倒退,以找到“最佳共同祖先”。在这样一个简单的图中,“最常见的祖先”是第一个共同的祖先:很明显,两个分支在B
分歧,所以它们在B
- 和 - 之前是相同的永远不同以后。 (在具有多个交叉点的复杂图形中,有时没有明显的最佳共同祖先。有关详细信息,请参阅the git-merge-base
documentation。)
找到三个提交后,git会对三个提交进行标准的三向合并(例如,参见三向合并的Stack Overflow和Wikipedia描述)。但是,嘿,等等,等一下。 : - )
三向合并适用于文件,而不适用于提交。 git在这里做的是查看三次提交中的所有文件。实际上,它会“检出”所有三次提交B
,O
和T
。 (它们分别在内部编号为-1,2和3,并且在冲突的情况下实际显示在索引/登台区域中。请参阅gitrevisions及其:n:path
语法。) Git还应用了它的重命名检测逻辑 - 就像你在两次提交时运行git diff
时看到的那样 - 猜测基本版本中的文件data-format.txt
现在是否被命名为doc/data-format.txt
例如,在O
或T
中。运行重命名检测并确定基本data-format.txt
与“他们的”data-format.txt
相同,但现在在“我们的”版本中称为doc/data-format.txt
,git执行三个 - 合并这三个,使用“我们的”版本中的名称作为“最终”名称。
Git会自动检测一些冲突,并强迫你进行合并,解决这些冲突。但是不能保证它会自动检测每个冲突......如果代码库“过多”分歧,它可能会错误检测或不检测各种文件重命名。它总是总是由你来做,合并的人,检查合并结果是否合理, git是否能够自动解决冲突。例如,如果有人向文件A添加了一个文本块,而另一个人从文件B中删除了一个文本块,那么git可以轻松地合并它 - 但是如果文件A中的新文本是文件B中的内容的描述,你现在已经对不再存在的东西进行了描述。
还有一种替代方法(git-imerge
,一种“渐进式”合并)理论上应该(我还没有测试过)在历史逐渐分化的情况下帮助很多。根据各种文件的修改方式,这仍然可能无法帮助您。实际查看提交本身是无可替代的。
在任何情况下,如果您不喜欢合并的结果,即与新提交M
相关联的工作树:
..- * - * - B - * - * - * <-------- root
\ \
\ O - M <-- production_support
\ /
* - * - T <---- feature
你可以(只要你没有push
编辑这个)(逻辑提交M
保留在存储库中,由reflog引用,如其他答案中所述)删除它,使用git reset --hard production_support^
,使production_support
再次指向提交O
。然后,您可以重新执行git merge
(如果需要,可以使用--no-commit
)并在这次修复工作树,并git add
生成的文件然后git commit
答案 2 :(得分:1)
让我先警告你:我不是Git的专家。由于我自己的愚蠢(嗯,就是:尝试使用Git而不阅读手册......)我碰巧“失去”了一些东西,并且不得不以某种方式恢复它们。任何提示/描述都是非正式的。你必须阅读更多关于我在下面说的话
-
根据您提供的事件顺序,我想:
如果我是对的,你真的第一次合并,那么拉拉,那么你当地的Git还不知道新的分支。它将功能合并为AAA。然后你拉了,删除了AAA并创建了BBB。
如果是这样,你现在的目标是找到丢失的AAA分支并用“Prod”之外的某个名称重命名/标记它,现在它指向BBB。然后,您将能够将ProdOld中的更改移动/复制/等等到NewProd。
-
如果您在本地合并了分支,那么您仍然可以恢复它。通常,Git不会立即删除内容。您可能仍然拥有本地“工作副本”上的所有内容,它可能只是“分离/孤立”。它失去了它的名字并且没有连接,因此看不见,但是如果你知道它的HASH,那么你可以像普通的“生活”分支那样简单地检查它。
..好吧,当然除非你在此期间删除并重新克隆它。或以任何其他方式清理/修剪/ GC,这将导致孤立的条目被永久删除..
首先,尽可能多地记住丢失的提交。记录消息,日期,ID /哈希值?
如果你知道哈希,那很好,请跳到下一点。
如果您不知道哈希值,但记得日志消息,日期或任何有用的内容,请尝试reflog
命令。它将向您显示存储库中任何分支的任何提示的所有“最近”更新。 Git经常会记住旧条目一段时间。即使他们被遗弃/删除/丢失,他们仍然可能坐在那里。只需运行
git reflog
在您进行合并的本地工作目录中 它会告诉你一些提交,最重要的是 - 它们的哈希值。如果你不知道丢失的提交的哈希 - 现在你可以学习它们。
如果reflog没有它们,我不知道该怎么办。我记得我读到有另一个较低级别的机制可以让你失去哈希,但我不记得那是什么,对不起。
假设您现在拥有丢失提交的哈希值:
明确地检查出来:
git checkout a76sa7..THE_HASH_HERE
即使提交“丢失”,如果只有git repo还没有'pruned / GC'ed',提交将被提取,当前提示将指向它。现在从中创建一个分支,只是为了标记它并防止被修剪/ GCed,例如:
git branch temp_restoring_commit
新分支temp_restoring_commit
将标记提交以便于参考。从现在开始,您可以将提交视为“已恢复且安全”,除非您删除分支,否则它将不会消失。 (当然,仍然可以通过reflog再次帮助你)
现在,您可以尝试:
正如我所说,当我在本地删除一个尚未推向生产的分支时,我已经做了几次。我很幸运,因为“损坏”是新的,当地的回购还没有“清理”。如果您的本地仓库不再记住这些提交,您可以将实时连接到Prod仓库并尝试reflog /分支,但我的知识在此处结束。只是不要尝试克隆远程仓库 - 它不会像旧的未连接的丢失提交那样获取“垃圾”。您需要将手放在“脏”的存储库中。