Восстановите время изменения файла в Git

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

Есть ли способ, которым я могу сказать Git сделать это?

(В качестве примера, при работе над крупным проектом я внес некоторые изменения в configure.ac, выяснил, что autotools не работает в моей системе и хотел восстановить configure.ac до его исходного содержимого и времени модификации так что make не пытается обновить configure мои сломанные autotools.)

Ответ 1

Git не делает этого. Как и ваши связанные часто задаваемые вопросы, он будет разбиваться с использованием инструментов анализа зависимостей на основе временных меток, таких как make.

Подумайте, что произойдет, если старые штампы времени будут применены к файлам, извлеченным из "старых коммитов":

  • make из чистого каталога работает отлично
  • проверить прежнюю ветку/тег/фиксацию (файлы теперь будут иметь метки времени, более старые, чем продукты сборки!)
  • make теперь ничего не делает, потому что все продукты сборки новее, чем их зависимости

Но, если вы действительно этого хотите, вся информация есть. Вы можете написать свой собственный инструмент, чтобы сделать это.

В вашем случае просто используйте что-то вроде touch -r configure configure.ac to reset время модификации только configure.ac (или приведите конфигурацию вперед вовремя с помощью touch configure).


На самом деле это простое упражнение для читателя, если вы хотите практиковать чтение кода C. Функция, изменяющая временные метки, - utime или utimes. Найдите код для использования этих функций (подсказка: git grep utime в git.git-клоне). Если есть некоторые виды использования, проанализируйте пути кода, чтобы узнать, когда он обновляет отметки времени.

Ответ 2

Восстановить время модификации списка файлов с датой автора их последнего фиксации с помощью

gitmtim(){ local f;for f;do touch -d @0`git log --pretty=%at -n1 -- "$f"` "$f"; done;}; gitmtim configure.ac

Однако каталоги не будут меняться рекурсивно.

Если вы хотите изменить все дерево работы, например. после нового клона или проверки вы можете попробовать

git log --pretty=%at --name-status --reverse | perl -ane '($x,$f)[email protected];next if !$x;$t=$x,next if !defined($f)||$s{$f};$s{$f}=utime($t,$t,$f),next if $x=~/[AM]/;'

NB: я сделал grepped для utime во встроенном /clone.c и не получил совпадений.

Ответ 3

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

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

См. http://github.com/alecthegeek/gitbuilding - на основе чего-то подобного я сделал несколько лет назад с SVN

Ответ 4

Следующая оболочка script должна работать в любой POSIX-совместимой системе, чтобы установить временную метку изменения и доступа всех отслеживаемых файлов (и каталогов). Единственным недостатком, который я смог определить, является то, что он довольно медленный, но это нормально для моего варианта использования (установка правильных дат при создании архивов выпуска).

rev=HEAD
for f in $(git ls-tree -r -t --full-name --name-only "$rev") ; do
    touch -d $(git log --pretty=format:%cI -1 "$rev" -- "$f") "$f";
done

Ответ 5

Этот инструмент должен сделать трюк. Он обновляет mtimes до времени автора и периодов времени коммиттера. Это будет работать как крюк для проверки.

Запустите с DEBUG = 1, чтобы заставить его точно сказать, что он делает.

Обратите внимание, что он не использует модули, а только базовые perl, поэтому должен работать где угодно.

#!/usr/bin/perl

# git-utimes: update file times to last commit on them
# Tom Christiansen <[email protected]>

use v5.10;      # for pipe open on a list
use strict;
use warnings;
use constant DEBUG => !!$ENV{DEBUG};

my @gitlog = ( 
    qw[git log --name-only], 
    qq[--format=format:"%s" %ct %at], 
    @ARGV,
);

open(GITLOG, "-|", @gitlog)             || die "$0: Cannot open pipe from `@gitlog`: $!\n";

our $Oops = 0;
our %Seen;
$/ = ""; 

while (<GITLOG>) {
    next if /^"Merge branch/;

    s/^"(.*)" //                        || die;
    my $msg = $1; 

    s/^(\d+) (\d+)\n//gm                || die;
    my @times = ($1, $2);               # last one, others are merges

    for my $file (split /\R/) {         # I'll kill you if you put vertical whitespace in our paths
        next if $Seen{$file}++;             
        next if !-f $file;              # no longer here

        printf "atime=%s mtime=%s %s -- %s\n", 
                (map { scalar localtime $_ } @times), 
                $file, $msg,
                                        if DEBUG;

        unless (utime @times, $file) {
            print STDERR "$0: Couldn't reset utimes on $file: $!\n";
            $Oops++;
        }   
    }   

}
exit $Oops;

Ответ 6

Я написал небольшой инструмент, который позволит вам восстановить время изменения файлов в каталоге после выполнения слияния или проверки с помощью Git.

https://bitbucket.org/chabernac/metadatarestore/wiki/Home

Используйте инструмент как крючок в Git при выполнении фиксации, проверки или слияния. См. 8.3 Настройка Git - Git Крючки для получения информации о крюках Git. Примеры Git hooks в каталоге .git/hooks вашего проекта можно найти.

Ответ 7

У нас была такая же проблема на работе, и мы успешно использовали git-store-meta perl script от Danny Lin.

Это определенно решило проблему, указанную в вашем вопросе.