Git 吉特:如何忘记过去的承诺

Git 吉特:如何忘记过去的承诺,git,repository,backup,Git,Repository,Backup,场景:我有一个目录结构,它会不时地改变。我想要它曾经所在的所有州的备份。为此,我只需将其设置为git存储库,并让一个cron作业每天执行一次git commit-m‘croncommit’。这很好,使我能够查看历史记录中目录结构的任何状态 但是git存储库在增长,即使目录结构没有增长。如果我曾经有一个巨大的文件在那里停留了很短的时间,它将永远留在存储库中。当然,从git的角度来看,这很好也很正确,但对我来说,这仅仅是一个备份工具,所以只保留最近的状态是有意义的,比如说,上个月 我正在寻找一种方法

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

但是git存储库在增长,即使目录结构没有增长。如果我曾经有一个巨大的文件在那里停留了很短的时间,它将永远留在存储库中。当然,从git的角度来看,这很好也很正确,但对我来说,这仅仅是一个备份工具,所以只保留最近的状态是有意义的,比如说,上个月

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

但是我没有找到这个任务的正确命令和语法

如何执行此操作?

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

#! /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
是一个非标准但相当于

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"
#/bin/bash-xe
MAX_AGE=${MAX_AGE:-30}
分支=${BRANCH:-master}
#假设线性历史
{
阅读新的开始
阅读重基

}<这就是我想要的,非常感谢。我冒昧地将您的Perl脚本重写为Bash脚本(并将其添加到您的答案中以确保完整性)。我希望你不介意:)还有一个问题:当你测试
$rebase
被定义时,你会检查哪种情况?什么时候可以不定义(或者在bash版本中为空)?@Alfe不客气。干杯这项检查是针对一种不太可能的情况,即您的分支上有一个单独的提交,提交者日期(what
git log--since=…
uses)超过30天。还有一件事:我发现在您的脚本运行后,我最终选择了“no branch”,因此
git status
等不再有用。因此,我在bash版本中添加了一个git签出“$BRANCH”
。如果这不是我们想要的,请纠正我。我错误地认为更新分支会让你脱离一个超然的头脑状态。请用您的更改更新答案。