2010-03-16 61 views
33

我瞭解default Git behaviour of updating the modification time every time it changes a file,但有時候我想恢復文件的原始修改時間。在Git中恢復文件的修改時間

有沒有辦法告訴Git做到這一點? (例如,在處理大型項目時,我對configure.ac進行了一些更改,發現自動工具不適用於我的系統,並且希望將configure.ac的原始內容和修改時間恢復爲如此make不會嘗試使用我的損壞的自動工具更新configure)。

+0

可能重複[什麼是相當於使用提交時間的git?](http://stackoverflow.com/questions/1964470/whats-the-equivalent-of-use-commit-times-for-git ) – MestreLion 2014-08-14 20:58:39

+0

還有:http://stackoverflow.com/questions/2179722/checking-out-file-with-original-create-modified-timestamps – MestreLion 2014-08-14 20:59:15

+0

沒有腳本在這裏爲我工作;這個答案在鏈接到類似的問題是什麼:http://stackoverflow.com/a/30143117/841830 – 2017-01-22 14:28:28

回答

14

Git不會這樣做。就像你的鏈接FAQ說的那樣,它會使用基於時間戳的依賴關係分析工具如使中斷。

想想,如果老時間戳被應用到的文件會發生什麼,從「老」簽出承諾:

  • 使從一個乾淨的目錄工作正常
  • 結賬舊的分支/標籤/ (這些文件的時間戳會比構建產品更新!)
  • 使現在什麼都不做,因爲所有構建產品都比它們的依賴項更新

但是,如果你真的想要它,所有的信息都在那裏。你可以編寫自己的工具來完成它。

在你的情況下,只需使用類似touch -r configure configure.ac的東西來重置修改時間只有configure.ac(或帶來配置與touch configure及時向前)。


實際上,如果您想練習閱讀C代碼,那麼這對於讀者來說是一個簡單的「練習」。更改時間戳的功能是utimeutimes。搜索這些函數的使用代碼(提示:git.git克隆中的git grep utime)。如果有一些用途,分析代碼路徑以找出更新時間戳的時間。

+5

我同意,這是一個很好的默認值。我只是希望有一個標誌或非默認行爲的命令。我想現在我會放棄使用git的管道,而只是使用'touch'來完成工作。 – rampion 2010-03-17 14:07:19

+3

如果'git archive'可以恢復檔案中每個文件的原始文件修改時間,但不幸的是,它會將所有修改時間設置爲當前時間或所選提交的時間戳。 – 2010-03-25 19:09:43

+0

我認爲你的意思是'touch -r configure configure.ac'而不是-t,因爲-t想要一個時間戳並且-r引用另一個文件來使用時間戳。 – 2011-06-21 09:41:13

6

我相信'正確的'修復方法是實際比較每個輸入文件的SHA1以查看它是否從上一次構建更改。

這是很多工作,但是我已經開始一個項目來嘗試創建一個概念驗證(仍然非常早期)。除了識別正確的構建步驟外,它還設計用於爲以後的取證創建輸入文件的審覈列表。

http://github.com/alecthegeek/gitbuilding - 它是基於類似的東西,我做了幾年前與SVN

+0

這將是很好的未來的項目:) – rampion 2010-03-18 15:21:29

+0

你可以使用adler或crc甚至rc4,快速的事情sha1是太多的努力,即使是現代cpus – 2015-09-29 08:48:19

+0

我實際上已經找到了更好的解決方案,我存儲最後修改將兩個重要文件的時間作爲註冊表的一部分存儲在文本文件中,然後使用touch -t將其恢復到文件中,這很簡單,但卻很有效,因此我可以使用最後修改時間計算在服務器端PHP或Apache etag和最後的mod。頭。這是你如何從'stat'中得到'touch'中使用的格式:首先使用'$(stat --format =%s _your_filename_)'獲得unix時間'然後將它提供給'date --date =「@_ the_unix_time_from_before 「+%Y%m%d%H%M」' - >'touch -t「_result_」--time = modify _filename_' =>'☔+☕' – 2016-01-06 21:19:51

11

將文件還原到其最後的作者日期列表的modificaton時間提交

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]/;' 

注意:我grepped utime內置/ clone.c並沒有得到匹配。

0

我寫了一個小工具,可以讓你在合併或檢出Git後恢復目錄中文件的修改時間。

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

使用工具Git中鉤做一個承諾,結賬或合併時。有關Git掛鉤的信息,請參閱8.3 Customizing Git - Git Hooks。您可以在項目的.git/hooks目錄中找到Git鉤子的示例。

+1

注意:結賬後的時間戳不再是* always *修改(使用Git 2.2.2+,2015年1月):http://stackoverflow.com/a/28256177/6309 – VonC 2015-01-31 20:32:21

3

以下shell腳本應該在任何與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 
+0

我的git版本足夠老,它不理解'%cI',但我發現'touch -d「$(git log --pretty = format:%ci -1」$ rev「 - 」$ f「)」'(帶引號!)也適用。 – Wolfgang 2016-11-11 03:27:54

+0

自git v2.2.0以來,該標誌被包含。在看了'touch'的POSIX規範之後(參見http://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html),我同意它最有可能和%Ci一樣好,不幸的是,這並不意味着要麼在POSIX的非常嚴格的實現上工作。 POSIX允許'%ci'而不是'T'使用的空間。然而,'+/- hh:mm'(帶或不帶前導空格)的後綴不是POSIX兼容的'touch'所必需的。無賴:) – stefanct 2016-11-11 10:41:08

+0

在OSX(這是POSIX)中找到BSD touch時不支持'-d' – arve0 2017-04-02 19:10:26

2

這個工具應該訣竅。它將作者的時間和時間更新爲提交者的時間。它將用作結賬鉤子。

用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; 
0

我們在工作中遇到了同樣的問題,並且已經成功地使用了Danny Lin的git-store-meta perl腳本。

它絕對解決了您的問題中指出的問題。