Git:如何忘记很旧的提交

标签 git repository backup

场景:我有一个不时变化的目录结构。我想要备份它曾经处于的所有状态。为此,我只需将其设置为 git 存储库,并让 cron 作业执行 git commit -m 'croncommit'每天一次。这工作得很好,使我能够查看历史记录中目录结构的任何状态。

但是 git 存储库会增长,即使目录结构没有增长。如果我曾经在其中短暂保存过一个巨大的文件,它将始终保留在存储库中。当然,从 git 的角度来看,这是很好且正确的,但由于对我来说这只是一个备份工具,因此想要仅保留最近的状态(例如上个月的状态)是有意义的。

我正在寻找一种方法来从给定存储库中删除早于特定持续时间(例如一个月)的状态(提交)。我认为这可以通过将所有早于特定年龄的提交折叠为一个来完成。

但我无法找到此任务的正确命令和语法。

我该怎么做?

最佳答案

使用 git log--since 选项来查找历史记录的新起点,并使用 git commit-tree 创建新的无父提交。重用其树状态。然后,将所有子级重新设置为新根,并将分支引用移动到新 HEAD。

#! /usr/bin/env perl

use strict;
use warnings;

my $MAX_AGE = 30;
my $BRANCH  = "master";

# assumes linear history
my($new_start,$rebase) = `git log --reverse --since="$MAX_AGE days ago" --format=%H`;
die "$0: failed to determine new root commit"
  unless defined($new_start) && $? == 0;

chomp $new_start;

my $new_base = `echo Forget old commits | git commit-tree "$new_start^{tree}"`;
die "$0: failed to orphan $new_start" unless $? == 0;
chomp $new_base;

# don't assume multiple commits more recent than $MAX_AGE
if (defined $rebase) {
  system("git rebase --onto $new_base $new_start HEAD") == 0
    or die "$0: git rebase failed";
}

system("git branch -f $BRANCH HEAD") == 0
  or die "$0: failed to move $BRANCH";

system("git reflog expire --expire=now --all && git gc --prune=now") == 0
  or die "$0: cleanup failed";

例如:

$ git lol --name-status
* 186d2e5 (HEAD, master) C
| A     new-data
* 66b4a19 B
| D     huge-file
* 5e89273 A
  A     huge-file

$ git lol --since='30 days ago'
* 186d2e5 (HEAD, master) C
* 66b4a19 B

$ ../forget-old 
First, rewinding head to replay your work on top of it...
Applying: C
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (5/5), done.
Total 5 (delta 1), reused 0 (delta 0)

$ git lol --name-status
* b882852 (HEAD, master) C
| A     new-data
* 63bb958 Forget old commits

请注意,git lol 是非标准的,但 highly useful alias相当于

git log --graph --decorate --pretty=oneline --abbrev-commit

OP 的添加:这是上面 Perl 脚本的 bash 版本:

#!/bin/bash -xe

MAX_AGE=${MAX_AGE:-30}
BRANCH=${BRANCH:-master}

# assumes linear history
{
  read new_start
  read rebase
} < <(git log --reverse --since="$MAX_AGE days ago" --format=%H)
[ -n "$new_start" ]  # assertion

read new_base < <(
  echo "Forget old commits" | git commit-tree "$new_start^{tree}"
)

# don't assume multiple commits more recent than $MAX_AGE
[ -n "$rebase" ] && git rebase --onto $new_base $new_start HEAD

git branch -f "$BRANCH" HEAD

git reflog expire --expire=now --all
git gc --prune=now

git checkout "$BRANCH"  # avoid ending on "no branch"

关于Git:如何忘记很旧的提交,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35919050/

相关文章:

git - 如何在不丢失历史记录的情况下将 .git 文件夹移动到父文件夹?

git - 如何修复因 git fetch 中断而损坏的 git 存储库?

git-过滤器分支 : leave directory structure unchanged

c# - 如何使用 XmlSerializer 创建 XmlRepository?

python - 在哪里克隆 Python 模块 git 存储库?

git - 如何在 GitHub 中创建嵌套存储库?

sql - Azure 应用服务 SQL 数据库备份失败

json - 错误: SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data cpanel backup restore

git-remote-https 坏了

mysql - 复制 MySQL InnoDB 文件