【问题标题】:Restore a file's modification time in Git在 Git 中恢复文件的修改时间
【发布时间】:2011-01-28 07:40:22
【问题描述】:

我了解default Git behaviour of updating the modification time every time it changes a file,但有时我想恢复文件的原始修改时间。

有没有办法告诉 Git 这样做?

(例如,在做一个大型项目时,我对configure.ac 进行了一些更改,发现autotools 在我的系统上不起作用,并想将configure.ac 恢复到原来的内容和修改时间,以便make 不会尝试用我损坏的自动工具更新configure。)

【问题讨论】:

标签: git


【解决方案1】:

将文件列表的修改时间恢复到其最后一次提交的作者日期

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)=@F;next if !$x;$t=$x,next if !defined($f)||$s{$f};$s{$f}=utime($t,$t,$f),next if $x=~/[AM]/;'

注意:我在 builtin/clone.c 中搜索了 utime,但没有找到匹配项。

【讨论】:

  • perl 版本好像不行..
  • Re“可以试试”:行不行?它在哪里以及如何进行测试?
【解决方案2】:

Git 不这样做。就像您链接的常见问题解答所说的那样,使用基于时间戳的依赖项分析工具(如 make)会中断。

想想如果旧时间戳应用于从“旧”提交签出的文件会发生什么:

  • make 从一个干净的目录工作正常
  • 签出旧的分支/标签/提交(文件的时间戳会比现在的构建产品更早!)
  • make 现在什么都不做,因为所有构建产品都比它们的依赖项更新

但是,如果你真的想要它,所有的信息都在那里。您可以编写自己的工具来执行此操作。

在您的情况下,只需使用touch -r configure configure.ac 之类的东西来重置仅configure.ac 的修改时间,(或使用touch configure 及时提前配置)。


实际上,如果您想练习阅读 C 代码,这是一个简单的“读者练习”。更改时间戳的函数是utimeutimes。在代码中搜索这些函数的用法(提示:git.git 克隆中的git grep utime)。如果有什么用处,分析一下代码路径,看看什么时候更新时间戳。

【讨论】:

  • 我同意,这是一个很好的默认设置。我只是希望有一个非默认行为的标志或命令。我想现在我会推迟使用 git 的管道,而只使用 touch 来完成工作。
  • 如果git archive可以恢复存档中每个文件的原始文件修改时间就好了,但不幸的是,它将所有修改时间设置为当前时间或所选时间戳提交。
  • fwiw,基于 make 等不可靠的工具限制版本控制系统并不是设计系统的好方法
  • 你错了:git repo 中没有信息。这是git 近来的一大劣势。
  • 所有关于它将如何破坏构建等的 cmets。如果它是一个标志,如果我做了类似 git pull pictures --restore-times 的事情。 - 当我拉下 340 MB 的图片文件时(不关心日期,除了我在更新时正在对另一个目录进行 robocopy 操作......还有 msbuild?不,在这种情况下,这些是其它文件..) 。添加标志是有道理的,如果你想使用它,你必须了解它,如果你不使用它并且默认情况下它是关闭的,你很好。将其排除在外的可怕理由:“有人可能会在不了解其影响的情况下使用它”..
【解决方案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

【讨论】:

  • 我的 git 版本太老了,无法理解 %cI,但我发现 touch -d "$(git log --pretty=format:%ci -1 "$rev" -- "$f")"(带引号!)也可以。
  • 要让它在 macOS 上运行,请使用 touch -t 而不是 touch -d--pretty=format:%cd --date=format:%Y%m%d%H%m.%S 而不是 --pretty=format:%cI
  • 将此答案与@ClementCherlin 的提示相结合,您将得到:rev=HEAD; for f in $(git ls-tree -r -t --full-name --name-only "$rev") ; do touch -t $(git log --pretty=format:%cd --date=format:%Y%m%d%H%m.%S -1 "$rev" -- "$f") "$f"; done
  • @ClementCherlin 哎呀,你有两个小写的 %ms 月份 - 第二个应该是 %M 分钟:--date=format:%Y%m%d%H%M.%S
  • 您想用@mwag 评论更新您的答案吗?我已经在 OSX 和 Debian 上测试了 mwag 评论,它似乎对两者都有效!
【解决方案4】:

Git Tools:

sudo apt install git-restore-mtime
cd [repo]
git restore-mtime

【讨论】:

    【解决方案5】:

    我认为“正确”的解决方法是实际比较每个输入文件的 SHA-1 哈希值,看看它是否与上次构建相比发生了变化。

    这是很多工作。但是,我已经开始了一个项目来尝试创建概念验证(仍处于早期阶段)。除了识别正确的构建步骤外,它还旨在为以后的取证创建输入文件的审核列表。

    参见Git building——它基于我几年前对Subversion所做的类似事情。

    【讨论】:

    • 这对未来的项目会很好:)
    • 您可以使用 adler 或 crc 甚至 rc4,快速的 sha1 即使对于现代 CPU 来说也太费力了
    • 我其实找到了一个更好的解决方案,我将两个重要文件的最后修改时间存储在一个文本文件中作为注册表的一部分,稍后使用touch -t 将其恢复到文件中,它简单而有效,这样我就可以在服务器端 PHP 或 Apache etag 和 last 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_ =>☔+☕
    【解决方案6】:

    这个工具应该可以解决问题。它将 mtimes 更新为作者时间,将 atimes 更新为提交者时间。它可以用作结帐挂钩。

    使用DEBUG=1 运行,让它准确地告诉你它在做什么。

    还要注意,它不使用任何模块,只使用基本 Perl,因此可以在任何地方运行。

    #!/usr/bin/perl
    
    # git-utimes: update file times to last commit on them
    # Tom Christiansen <tchrist@perl.com>
    
    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;
    

    【讨论】:

      【解决方案7】:

      我们在工作中遇到了同样的问题,并且已经成功地使用了 Danny Lin 的 git-store-meta Perl 脚本。

      它确实解决了您问题中指出的问题。

      【讨论】:

      • 其实这是唯一正确的答案。其他答案中的任何其他解决方案只恢复提交时间而不是修改时间,因为 git 甚至不记录修改时间。
      【解决方案8】:

      这需要stefanct proposed 的大部分内容,但在实现类似的脚本时,我只是添加了一个并行功能。

      在我的情况下(1000 个文件),我从 60 秒缩短到 15 秒以并行执行操作。

      #!/bin/bash
      
      change_date() {
            local dd=`git log -1 HEAD --pretty="%ci" -- $1`
            if [ -z "$dd" ];
            then echo "$1 is not versionned";
            else touch -d "$dd" $1;
            fi
      }
      #list_of_files = find .
      list_of_files=`git ls-tree -r -t --full-name --name-only HEAD`
      
      for f in $list_of_files;do
        if test "$(jobs | wc -l)" -ge 16; then
          wait
        fi
        {
          change_date  $f;
        } &
      done
      wait
      

      您可以通过更改此行来调整允许的并行作业数:

      test "$(jobs | wc -l)" -ge 16
      

      【讨论】:

        【解决方案9】:

        我写了一个小工具,可以让你在使用 Git 进行合并或签出后恢复目录中文件的修改时间。

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

        在执行提交、签出或合并时,将该工具用作 Git 中的挂钩。有关 Git 挂钩的信息,请参阅 8.3 Customizing Git - Git Hooks。您可以在项目的 .git/hooks 目录中找到 Git 挂钩的示例。

        【讨论】:

        猜你喜欢
        • 2011-01-15
        • 1970-01-01
        • 2013-06-19
        • 2014-03-11
        • 2015-09-09
        • 2023-03-31
        • 1970-01-01
        • 1970-01-01
        • 2013-02-15
        相关资源
        最近更新 更多