场景:我的目录结构会不时发生变化。我想备份它曾经存在的所有状态。为此我只需将其设置为git存储库并让cron作业每天执行一次git commit -m 'croncommit'
。这工作正常,使我能够查看历史记录中任何目录结构的状态。
但即使目录结构没有,git存储库也会增长。如果我曾经在那里有一个巨大的文件很短的时间,它将永远留在存储库中。从git的角度来看,这是好的和正确的,但是对我而言,这只是一个备用工具,想要保留最近的状态,比如上个月,这是有意义的。
我正在寻找一种方法来从给定的存储库中删除早于特定持续时间(例如一个月)的状态(提交)。我认为这可以通过将所有早于特定年龄的提交折叠成一个来完成。
但是我找不到这个任务的正确命令和语法。
我该怎么做?
答案 0 :(得分:3)
使用--since
git log
选项查找历史记录的新起点,并使用重用其树状态的git commit-tree
创建新的无父提交。然后,将所有子项重新绑定到新根上,并将分支ref移动到新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"