Применить команду ко всем фиксациям

В попытке собрать некоторую статистику о репозитории Git, я ищу способ сделать следующее:

  • Для каждой фиксации выполните команду (ex; du -h).
  • Эта команда должна запускаться из базового каталога хранилища "как это было похоже" после фиксации.
  • В идеале команда будет иметь доступ к хешированию фиксации и отметке времени.

Одно приложение, выраженное в квази- Bash, должно было запускать

echo $HASH $TIME `du -hs --exclude=".git" . | awk '{ print $1; }'` >> ../sizeovertime

для всех коммитов, чтобы получить представление о росте репозитория.

(Как-то кажется, что для этого нужно использовать git filter-branch --tree-filter, но для меня это выглядит ужасно.)

Ответ 1

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

Вот как вы можете это сделать с помощью bash:

#! /bin/bash

while read co dt ; do
    git checkout $co > /dev/null 2>&1
    size=$(du -hs --exclude=.git|cut -f1)
    echo $co $size $dt
done < <(git rev-list --pretty=format:"%H %ci" --all --date-order |grep -v "^commit")

Предупреждение: это оставит вас в состоянии отдельной головы, на самом старом коммите, что не является хорошим местом.

Ответ 2

Чтобы вычислить размер каждой фиксации в репо, будет довольно медленно проверить каждую фиксацию. Во-первых, вы дублируете много работы, так как вы будете перекомпилировать размеры файлов, которые не меняются. Кроме того, вы будете забивать вашу файловую систему, постоянно проверяя ситуацию. Вот script, который запрашивает репозиторий git, чтобы получить нужную вам информацию. Основное преимущество заключается в том, что вы никогда не смотрите на какой-либо из блоков, чтобы вычислить их размер, но просто попросите git рассказать вам. Кроме того, вы запрашиваете только git для каждого блоба один раз (через магию Memoize).
Нет сомнений в том, что этот script нуждается в работе (автоматическое распознавание любых ошибок git было бы хорошей идеей), но оно должно дать вам место для начала. (Я изменил это из первоначальной публикации, включив аргумент, который можно использовать как refspec.Если вы вызываете без аргумента, это печатает информацию для каждого фиксации в истории. Вы можете передать ref-spec, как rev-list, ограничьте работу. Например, если у вас есть теги v0 и v1, вы можете передать "v0..v1" в качестве первого аргумента.)

#!/usr/bin/env perl

use warnings;
use strict;
use Memoize;

my $rev_list = $ARGV[ 0 ] || "--all";

# Query git for the size of a blob.  This is memoized, so we only
# ask for any blob once.
sub get_blob_size($) {
    my $hash = shift;
    my $size = qx( git cat-file -s $hash );
    return int( $size );
}
memoize( 'get_blob_size' );

# Recursively compute the size of a tree.  Note that git cat-file -s
# does not give the cumulative size of all the blobs in a tree.
sub compute_tree_size($);
sub compute_tree_size($) {
    my $sha = shift;
    my $size;
    open my $objects, '-|', "git cat-file -p $sha";
    while( <$objects> ) {
        my ( $mode, $type, $hash, $name ) = split;
        if( $type eq 'blob' ) {
            $size += get_blob_size( $hash );
        } elsif( $type eq 'tree' ) {
            $size += compute_tree_size( $hash );
        }
    }
    return $size;
}
memoize( 'compute_tree_size' );

# Generate a list of all commits
open my $objects, '-|', "git rev-list $rev_list |
    git cat-file --batch-check";

# Traverse the commit list and report on the size of each.
while( <$objects> ) {
    my( $commit, $type, $size ) = split;
    my( $tree, $date ) = split( '@',
        qx( git show --format="%[email protected]%ci" $commit | sed 1q ));
    chop $date;
    printf "$date: %d\n", compute_tree_size $tree;
}