2016-03-10 4 views
2

Сценарий: У меня есть структура каталогов, которая время от времени меняется. Я хотел бы иметь резервные копии всех состояний, в которых он когда-то был. Для этого я просто установил его как репозиторий git и выполнял задание cron git commit -m 'croncommit' один раз в день. Это прекрасно работает и позволяет мне просмотреть любое состояние структуры каталогов в истории.Git: Как забыть очень старые коммиты

Но репозиторий git растет, даже если структура каталогов не работает. Если бы у меня когда-то был огромный файл там на короткое время, он всегда оставался в репозитории. Это хорошо и правильно с точки зрения git, конечно, но поскольку для меня это просто средство резервного копирования, имеет смысл хотеть сохранить только последние состояния, скажем, за последний месяц.

Я ищу способ удаления состояний (коммиттов) старше определенной продолжительности (например, один месяц) из данного репозитория. Я думаю, что это можно сделать, свернув все коммиты, которые старше определенного возраста на один.

Но я не могу найти правильную команду и синтаксис для этой задачи.

Как это сделать?

ответ

3

Используйте --since вариант git log, чтобы найти новую начальную точку вашей истории и создать новую фиксацию без сохранения с использованием 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 

КРОМЕ О.П.: Вот версия Баш сценария Perl выше :

#!/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" 
+0

Это то, что я искал, спасибо большое. Я взял на себя смелость переписать сценарий Perl как скрипт Bash (и добавил его к вашему ответу для полноты). Надеюсь, вы не возражаете :) – Alfe

+0

Остается один вопрос: в каком случае вы проверяете, когда вы проверяете определение '$ rebase'? Когда это может быть неопределенным (или пустым в версии bash)? – Alfe

+1

@Alfe Добро пожаловать. Ура! Проверка заключается в маловероятном случае, когда в вашей ветке присутствует одиночная фиксация с датой коммиттера (что используется 'git log --since = ...'), новее 30 дней. –

Смежные вопросы