以编程方式交换最后两次提交

nne*_*neo 25 git

我知道如何使用git rebase交互式交换最后两个提交(git rebase -i HEAD~2,ddjp:x在Vim中),但我想用编程器脚本以编程方式进行,因为这是我最终经常做的事情.

更具体一点,我想重写历史

A---B---C---D HEAD
Run Code Online (Sandbox Code Playgroud)

A---B---D---C HEAD
Run Code Online (Sandbox Code Playgroud)

以完全脚本的方式.理想情况下,如果交换失败,它应该允许我以交互方式修复它,或者只是放弃并告诉我手动完成它.

Nei*_*ter 39

这应该这样做:

git tag old
git reset --hard HEAD~2
git cherry-pick old
git cherry-pick old~1
git tag -d old
Run Code Online (Sandbox Code Playgroud)

首先,标记您所在的位置old,然后返回两个提交,git cherry-pick其他顺序的提交,并删除old标记.

  • 仅供参考:这不完全有效,因为`git reset HEAD~2`会留下未提交的更改,导致`cherry-pick`失败. (6认同)
  • 您可以将其合并为`git cherry-pick old old〜1`。 (3认同)

edd*_*oya 7

由于您经常要这样做,我假设您希望减少到一个步骤的过程.我会让这有点教育,但打破它.

重要

不要在已经与其他开发人员共享或推送到远程的任何提交上执行此类操作.重写共享历史记录是一种灾难.

除了公共服务公告......

步骤1

A---B---C---D (master, HEAD, ORIG_HEAD)

git rebase --quiet --onto HEAD~2 HEAD~1 HEAD
Run Code Online (Sandbox Code Playgroud)

它接受HEAD中不在HEAD~1中的任何内容,并将其应用于HEAD~2.运行此rebase后,您将拥有此历史记录.请记住,这git-rebase --onto会让你陷入无头状态.(我已经--quiet进去了,所以最后的命令不会向你的屏幕吐出一面文字墙).

A---B---C---D (master, ORIG_HEAD)
     \
      D'  (HEAD)
Run Code Online (Sandbox Code Playgroud)

现在我们需要在C之后应用D',为此我们可以使用git-cherry-pick.当a git-rebase --onto执行如上所述时,保存rebase之前的原始提交历史记录,ORIG_HEAD因为git不会更改它直到您执行其他活动.如果你搞砸了一个rebase,这很有用,但我们会在这里使用它来挑选.

第2步

git cherry-pick ORIG_HEAD~1

A---B---C---D (master, ORIG_HEAD)
     \
      D'---C'  (HEAD)
Run Code Online (Sandbox Code Playgroud)

HEAD的状态现在完全符合您的要求,只需要两个命令.我假设这通常会在分支内发生,并且您希望使用新的提交顺序更新该分支.如果我错了,那就是它和你的完成.如果你想更新你所在的分支,有几种方法可以做到这一点.

最简单的手动操作是做以下事情......

git log -1    ###copy the the SHA1
git checkout master
git reset --hard <SHA1>
Run Code Online (Sandbox Code Playgroud)

然而,重点是自动化,并且这样做的方法不那么明显,但需要的命令更少.

第3步

git update-ref refs/heads/master $(git rev-parse HEAD)
Run Code Online (Sandbox Code Playgroud)

通过使用git-rev-parse,我只得到HEAD的提交而没有别的.将其应用于`git-update-ref,我可以"重置"主分支,而不必先检查它.现在master已正确设置,我可以继续检查master(update-ref的重点是减少最终别名/ bash脚本所涉及的步骤数).

第4步

git checkout --quiet master
Run Code Online (Sandbox Code Playgroud)

再次,我传递--quiet以减少每个命令后在屏幕上转储的文本量.

Bash脚本

如果你想以bash脚本的形式做到这一点,你可以进一步自动化整个过程,并使它在你想要的任何分支上动态工作,而不仅仅是master.

#!/bin/bash
branch=$(git name-rev --name-only HEAD)
git rebase --onto HEAD~2 HEAD~1 HEAD
git cherry-pick ORIG_HEAD~1
git update-ref refs/heads/$branch $(git rev-parse HEAD)
git checkout --quiet $branch
Run Code Online (Sandbox Code Playgroud)

Git Alias

也就是说,这很容易转储到git别名中,而无需为bash脚本实际创建文件.无论如何创建文件和设置别名并不难,但要理解是另一回事,很多人都不这样做.这是一个简单的git别名,运行此命令一次..

git config --global alias.flip-last "!branch=$(git name-rev --name-only HEAD); git rebase --quiet --onto HEAD~2 HEAD~1 HEAD; git cherry-pick ORIG_HEAD~1; git update-ref refs/heads/$branch $(git rev-parse HEAD); git checkout --quiet $branch"
Run Code Online (Sandbox Code Playgroud)

现在,只要您想翻转最后两次提交,只需使用...

git flip-last
Run Code Online (Sandbox Code Playgroud)

GitHub Gist

这是一个有趣的小脚本,我把它扔在github上.随意分叉,做出改变,明星,无论如何.

https://gist.github.com/eddiemoya/5456992