与其他人合作时,我通常的工作流程是创建一个功能分支,并让双方都对此分支进行更改。在一起工作时,通常会有无意义的提交,就像'嘿,我做了这个小小的改变,你觉得怎么样?'但是,在我们PR之前将它合并为master之前,我会在开始工作之前回到第一个提交git reset
,并将历史分成合理的逻辑块。
然而,这次某位同事合并为主人,使我们的历史难以理解。或者至少我现在无法git reset
,因为我们的提交不再是连续的。
我们的分支是master的超集,只有额外的提交。我想删除所有这些提交但离开工作,允许我将更改提交到逻辑块。
思想?
答案 0 :(得分:2)
我发现你的描述比照亮更令人困惑。特别是,合并的存在(或缺乏)不是git reset
的障碍。 git reset
做的是更改当前分支指向的提交ID。
每当你进行任何提交(甚至是合并)时,git只会向您的存储库添加 new 提交。每个提交都有自己的SHA-1 ID,这是其真正的名称" (该提交具有该ID,该ID 表示提交,并且没有其他人将使用该ID)。每个提交还包含其父(其中一个或多个)的ID。合并只包含至少两个父ID,而常规提交只有一个父ID。我们可以说这些父母ID"指回"到父提交,因此我们可以绘制提交图:
A <- B <- C <-- master
\
D <- E <-- feature
右边的名称是分支标签,git通常 1 通过包含每个分支的tip的SHA-1 ID的文件来实现。也就是说,master
的文件具有C
的SHA-1,而feature
的文件在此处具有E
的SHA-1。
对于这张图片,我们应该添加HEAD
,这基本上只是一个文件git用来保存&#34;当前分支的名称&#34;:
A <- B <- C <-- master
\
D <- E <-- HEAD=feature
现在可以非常简单地将<{1}}描述为 2 :&#34;读取HEAD以查看要更改的分支,然后更改存储在该分支中的SHA-1 ID& #39; s文件到git reset
命令行上给出的commit-ID。&#34;因此,如果git reset
说HEAD
,feature
说&#34;提交master
&#34;,那么:
C
告诉git:&#34;将git reset [options] master
的ID写入C
&#34;。 (feature
部分使[options]
执行更多或更少的操作,例如更新索引和/或工作树,或跳过这些更新。)
(当您通过branch-name或reset
或gitrevisions
允许的任何名称命名提交时,git只会将其转换为提交ID。要查看此操作,使用HEAD~2
,将名称转换为commit-ID:
git rev-parse
等等。)
要进行新的提交,git会将新提交写入存储库,将其父ID设置为当前(git rev-parse HEAD
git rev-parse master
git rev-parse feature^
)提交的ID(并使用&#34的暂存区域;进入这个提交&#34;)。然后它将新提交的ID写入当前分支文件,并且全部完成!只需将新的提交ID写入分支文件即可扩展分支。
为了便于说明,我们将合并提交添加到HEAD
。首先,我们需要向feature
添加更多提交,除非我们正在合并另一个分支。然后我们将添加合并提交master
。我要停止画箭头,只记得它们指向左边(可能是左边和右边等)。
M
此处,提交A - B - C - F - G <-- master
\ \
D - E - M <-- HEAD=feature
是一个合并提交,因为它有两个父项:M
和E
。 G
分支文件包含feature
的ID。但是M
的工作方式与以往一样:如果我们告诉git reset
指向提交feature
,则提交D
和E
将变为不可见(尽管他们还在那里)我们所看到的就是:
M
A - B - C - F - G <-- master
\
D <-- HEAD=feature
和E
的幽灵版本默认至少保留一个月,保存在git&#34; ref-logs&#34; (如果您使用M
或git reflog
,您实际上可以看到它们。但他们不再是&#34;在#34; git log -g
分支,现在以提交feature
结束,因为我们已经D
。
git reset
告诉git你在哪个分支。HEAD
以获取其父ID,然后将其新的提交ID写入分支文件。新的合并提交执行相同的操作,除了它们记录git rev-parse HEAD
ID 和合并分支的ID。HEAD
将旧(现有)commit-ID写入当前分支。使用git reset
,它还会更新索引/登台区域和工作树。 (--hard
只有分支和索引,而--mixed
只有分支。)请注意,如果您使用--soft
将git reset
倒回到C,然后开始执行新的feature
,则只需添加新的提交,就像之前一样。它们与您之前添加的提交具有不同的ID,因此图形如下所示:
git commit
(我跳到A - B - C <-- master
| \
\ D - E <-- [old feature, via reflog]
\
X - Y <-- feature
,以便明确说明这些与上面的X
,F
等不同。)
1 更具体地说,标签可以打包&#34;,这样一堆标签都可以共享一个文件。
2 也许有点简单,因为G
也可以选择性地更新索引/登台区域和/或工作树。
答案 1 :(得分:0)
你的工作流程很糟糕:) 而不是重置和重做作业(我不知道你如何“清理”历史)你应该使用 rebase 命令。
此处的一部分,要恢复合并,您只需将头重置为功能分支的最后一次提交。所以得到最后一次提交的sha1(不是合并提交的id,而是普遍的提交)并且执行
git reset --hard shaidofthecommit
此时,分支的头部将重置为该提交,您将放弃合并提交。
答案 2 :(得分:0)
对于未来的人们,我发现解决我的问题的最好方法是只修改master和我自己之间的差异,然后将更改应用到新分支,然后PR。通过这种方式,我可以获得所有更改,但可以稍微清理一下历史记录。
git diff --binary master my-branch > ../thing.patch
然后
git apply ../thing.patch
我知道我的问题有点不稳定,我的解决方案也是如此。但是,希望这将有助于将来的某个人。