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

时间:2013-04-24 22:50:36

标签: git

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

更具体一点,我想从

重写历史
A---B---C---D HEAD

A---B---D---C HEAD

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

3 个答案:

答案 0 :(得分:37)

这应该这样做:

git tag old
git reset --hard HEAD~2
git cherry-pick old
git cherry-pick old~1
git tag -d old

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

答案 1 :(得分:7)

由于您经常要这样做,我假设您希望减少到一个步骤的过程。我会让这有点教育,然后分解。

重要

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

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

第1步

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

git rebase --quiet --onto HEAD~2 HEAD~1 HEAD

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

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

现在我们需要在C之后应用D',为此我们可以使用git-cherry-pick。如上所述执行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)

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

最简单的手动操作就是执行以下操作......

git log -1    ###copy the the SHA1
git checkout master
git reset --hard <SHA1>

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

第3步

git update-ref refs/heads/master $(git rev-parse HEAD)

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

第4步

git checkout --quiet master

再次,我传递--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

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"

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

git flip-last

GitHub Gist

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

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

答案 2 :(得分:3)

作为参考,这是我最终使用的脚本:

. "$(git --exec-path)/git-sh-setup"
require_clean_work_tree swap2

if [ -e $GIT_DIR/sequencer ]; then
    die "Cherry-pick already in progress; swap aborted"
fi

HEAD=`git rev-list --max-count=1 HEAD`
git reset --hard HEAD~2
if git cherry-pick $HEAD $HEAD^; then
    echo "Successfully swapped top two commits."
else
    git cherry-pick --abort
    git reset --hard $HEAD
    die "Failed to swap top two commits."
fi