如何删除除最后五个之外的所有Git提交

42

我有一个非常大的Git仓库,其中只包含二进制文件,这些文件经常发生更改。自然而然,Git仓库比其中的实际文件要大得多。我并不关心旧历史记录,我只需要一些较新的历史记录来能够恢复一些错误的更改。因此,假设我想删除除最后五个提交以外的所有提交。

自然地,我希望做到这一点是为了使仓库保持小型化,因此必须完全从仓库中清除已删除的提交。

我希望用一个单独的命令(别名)或脚本进行所有这些操作,而不需要交互式地操作。我该怎么做呢?


2
这可能会有所帮助:https://dev59.com/J3VC5IYBdhLWcg3wliGe - Herr von Wurst
4
你确定你想要“移除”所有旧提交吗?这也意味着移除它们所做的更改。GIT没有在每个提交中存储“当前状态”,它只存储更改。你想做的是将所有旧提交压缩成一个,对吗? - amorfis
2个回答

21
这里有一个rebase-last-five的别名,用于重新创建当前分支,使历史记录中仅包含最近的五个提交。最好将其制作为脚本(git-rebase-last-five.sh),并放在你的PATH目录下;Git会自动查找并使用以git-....sh命名的脚本,无需任何特殊配置。该脚本应该进行更多的错误检查和处理,而不仅仅是这个简单的别名。
$ git config --global alias.rebase-last-five '!b="$(git branch --no-color | cut -c3-)" ; h="$(git rev-parse $b)" ; echo "Current branch: $b $h" ; c="$(git rev-parse $b~4)" ; echo "Recreating $b branch with initial commit $c ..." ; git checkout --orphan new-start $c ; git commit -C $c ; git rebase --onto new-start $c $b ; git branch -d new-start ; git gc'

注意:请注意更改历史的警告

请查看man页面(git help <command>在线文档)以获取更多信息。

以下是一个使用示例:

$ git --version
git version 1.7.12.rc2.16.g034161a
$ git log --all --graph --decorate --oneline
* e4b2337 (HEAD, master) 9
* e508980 8
* 01927dd 7
* 75c0fdb 6
* 20edb42 5
* 1260648 4
* b3d6cc8 3
* 187a0ef 2
* e5d09cf 1
* 07bf1e2 initial
$ git rebase-last-five 
Current branch: master e4b2337ef33d446bbb48cbc86b44afc964ba0712
Recreating master branch with initial commit 20edb42a06ae987463016e7f2c08e9df10fd94a0 ...
Switched to a new branch 'new-start'
[new-start (root-commit) 06ed4d5] 5
 1 file changed, 1 insertion(+)
 create mode 100644 A
First, rewinding head to replay your work on top of it...
Applying: 6
Applying: 7
Applying: 8
Applying: 9
Deleted branch new-start (was 06ed4d5).
Counting objects: 35, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (35/35), done.
Total 35 (delta 4), reused 0 (delta 0)
$ git log --all --graph --decorate --oneline
* a7fb54b (HEAD, master) 9
* 413e5b0 8
* 638a1ae 7
* 9949c28 6
* 06ed4d5 5

4
很好用!谢谢!但是我不仅要使用git gc,还需要使用git reflog expire --expire=now --all; git gc --prune=now才能使存储库变小。 - kayahr
1
非常感谢,它有效了。你应该在你的回答中将脚本分解成几行。我个人更喜欢在执行外部脚本之前先理解我在我的存储库中做了什么。 - JulienD
1
在我进行了一次修复后,这个方法对我很有效。git branch --no-color | cut -c3-不能为我返回当前分支;我改变了脚本使用git rev-parse --abbrev-ref HEAD。因此,我使用的完整别名是:git config --global alias.rebase-last-five '!b="$(git rev-parse --abbrev-ref HEAD)" ; h="$(git rev-parse $b)" ; echo "Current branch: $b $h" ; c="$(git rev-parse $b~4)" ; echo "Recreating $b branch with initial commit $c ..." ; git checkout --orphan new-start $c ; git commit -C $c ; git rebase --onto new-start $c $b ; git branch -d new-start ; git gc' - greg_1_anderson
git-rebase-last-five.sh 这个文件应该放在哪里? - kishan verma

14

好的,如果你想要我认为你想要的东西(请看我的评论),我认为这个做法可行:

  1. 创建一个分支来保存所有提交记录(以防万一):

    git branch fullhistory

  2. 仍然在主分支上,将代码回滚到你想要保留历史记录的那个提交点:

    git reset --hard HEAD~5

  3. 现在重新回滚到最初的历史记录,不带--hard选项,这应该不会影响你的工作区状态,它应该还是停留在HEAD~5状态。

    git reset --soft <first_commit>

  4. 所以现在你的master分支是空的,而你需要的更改都在工作区中。只需将它们提交即可。

    git commit -m "all the old changes squashed"

  5. 现在从fullhistory中挑选出你想要保留的4个提交记录:

    git cherry-pick A..B

其中A比B旧,并且记住A不包括在内。因此,它应该是你想要包括的最古老提交记录的父提交点。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接