使用 git reset --soft,标记一次提交,以便您稍后可以 *向下* 压缩它

Using git reset --soft, Marking a commit so that you can squash *down* to it later

希望压缩提交,这样 publicly 面向的项目就不会像 "temp" 或 "temp123".

这样的无意义的提交消息

我正在寻找一种使用 git reset --soft HEAD~5 的方法,但我不想随机选择数字 5,而是想返回 Git 日志并找到所有未提交的提交匹配一个模式,一旦我找到了模式,我就停止了。所以假设我的 git log 提交消息看起来像:

temp123
fooGit
temp
fml
temp24
tmp69
tttoday
publish/release:xyz

所以我会压缩前 7 个最近的提交,但保留所有内容 "below" publish/release:xyz.

由于相当复杂的原因,public 面向分支发布的 git merge --squash 对我不起作用,因为我最终在私有分支的项目中删除或重命名了很多文件所以合并冲突太多了。在我看来,在尝试了几种方法之后,最好用 -f 推送到面向 public 的分支,而不用担心那样会发生冲突。

我认为对我来说更好的方法是像这样压缩提交:

# on private "dev" branch
# make a bunch of changes, rename files, yadda yadda
git add . && git add -A && git commit -am "temp" &&
git checkout -b squash_branch  # we do squashing on this branch to be safer
git reset --soft head~15 # apparently this undoes commits for the last 15 commits
git commit -am "A serious commit message"
git push -u public master -f  # overwrite public master

这样,我永远不必解决 public 面向分支和私有开发分支之间的冲突,根据我的经验,这可能是超级无意义和烦人的。

但是,我要解决的问题与上面的数字15有关。我只是在猜测 "squash" 有多少提交。我宁愿知道要挤压的确切数量。

也许我可以压缩所有没有符合特定模式的 Git 提交消息的提交?所以当我 运行 压缩时,我创建了一个具有特定模式的提交消息,假设它是 "XYZ"。我可以运行git reset --soft HEAD~(some git commit pattern matching to find the count)吗?

还有一个问题。在 squash_branch 上完成压缩 (git reset --soft) 之后,我是否应该将该分支合并回私有 dev 分支?

我假设您的 dev 分支会在您开始工作之前跟踪 master。如果没有,请在进行任何临时提交之前创建一个新分支。

要确定您从 master 分支出来的点(以及要重置回的点),请执行:

git merge-base master HEAD

您可以显示该点和 HEAD 之间的所有提交(这应该包括您所有的临时提交,并且只包括那些提交):

git log --oneline $(git merge-base master HEAD)..HEAD

您可以将该命令的输出通过管道传输到 wc -l 并获取提交次数,这是您的 git reset 命令所需的次数。

或者,您可以直接通过以下方式获取该号码:

git rev-list --count $(git merge-base master HEAD)..HEAD

综合起来:

git reset --soft HEAD~$(git log --oneline $(git merge-base master HEAD)..HEAD | wc -l)

或:

git reset --soft HEAD~$(git rev-list --count $(git merge-base master HEAD)..HEAD)

进行重置后,与其将 squash_branch 合并到 dev,不如进行硬重置(当然,在您确认没有任何损坏之后):

git checkout dev
git reset --hard squash_branch

此外,合并冲突的发生往往是有原因的,所以我对盲目地强制推送到远程保持警惕。如果您必须 强制推动,则改为这样做:

git push origin master --force-with-lease

--force-with-lease 标志将导致 Git 覆盖您已经获取的任何提交,但如果远程有您不知道的新提交,该命令将失败。

可以找到计数,但这有点危险,因为如果有合并提交,这将是错误的数字存在于链中(此处未说明):

git rev-list --count HEAD^{/publish/release:xyz}..HEAD

符号 rev^{/<em>text</em>} 表示 "starting from the given rev, search each commit message (a la git log --grep) for the given text"。因此,第一个表达式在其提交消息中将提交与 publish/release:xyz 匹配。然后 .. 语法使用该提交的 ID 删除 该提交和更早的提交,这些提交来自名称 HEAD 选择的提交集。 --count 然后给我们一个剩余提交的计数。因此,如果您的图表如下所示:

...--o--*--o--o--o--o   <-- HEAD

其中 * 是其中包含 text 的提交,这会丢弃提交 * 和其他更左边的提交,留下最后四个o 提交 HEAD 指向的节点。 --count 然后计算这四个提交并打印 4.

但总的来说,这是一个糟糕的策略。最好先用 分支或标记名称 标记该点。分支或标签名称通过 指向它 来标识提交。与其尝试按模式查找提交 *,不如在提交时标记它:

git tag xyz

现在你有这个:

     tag:xyz
        |
        v
...--o--*--o--o--o   <-- HEAD -> dev

xyz..HEAD 为您提供正确的提交集,您可以 git reset --soft xyz 使分支标签 dev 指向提交 *:

     tag:xyz
        |
        v
...--o--*   <-- HEAD -> dev
         \
          o--o--o   [only findable via reflogs]

完成此操作后,只需删除标签即可。

(同样,您可以使用 分支或标签 名称;使用您喜欢的任何一个;我用标签说明了这一点,因为标签名称不是 假设的 移动,而分支名称 do 移动。它们 自动移动 ,事实上,每当您 git checkout 分支然后进行提交或使用 git reset。标记名称指向一个特定的提交并停留在那里。)


顺便说一下,您可以在创建标签时使用搜索符号:

git tag xyz HEAD^{/publish/release:xyz}

也就是说,您在创建标签名称时确定哪些提交给标签。 (实际上,分支名称也是如此。)然后您可以看到带有 git log --decorate 的标签,并且像 gitk 这样的图形查看器通常会显示哪些提交也有哪些标签。

(搜索和标记是更好地过渡到这种习惯的好方法,至少在理论上是这样。)