使用原始创建/修改时间戳检出旧文件

时间:2010-02-01 20:13:26

标签: git

有没有办法知道或获取原始的创建/修改时间戳?感谢。

15 个答案:

答案 0 :(得分:46)

,metastore或git-cache-meta 可以存储此类(元)信息! Git本身,没有第三方工具,不能。 Metastoregit-cache-meta可以存储文件的任何文件元数据。

这是设计原因,因为metaore或git-cache-meta旨在用于此目的,并支持备份实用程序和同步工具。

(抱歉,雅库布的回答只是一点乐趣)

答案 1 :(得分:43)

我相信Git数据库中记录的唯一时间戳是作者和提交时间戳。我没有看到Git修改文件的时间戳以匹配最近提交的选项,并且这不是默认行为(因为如果是,Makefile将无法正常工作)。

您可以编写脚本以将文件的修改日期设置为最近提交的时间。它可能看起来像这样:

IFS="
"
for FILE in $(git ls-files)
do
    TIME=$(git log --pretty=format:%cd -n 1 --date=iso -- "$FILE")
    TIME=$(date -j -f '%Y-%m-%d %H:%M:%S %z' "$TIME" +%Y%m%d%H%M.%S)
    touch -m -t "$TIME" "$FILE"
done

答案 2 :(得分:35)

,Git只是 不存储此类(元)信息 ,除非您使用第三方工具,例如metastore或者git-cache-meta。存储的唯一时间戳是创建时间补丁/更改(作者时间),以及创建时间提交(提交者时间)。

这是设计,因为Git是版本控制系统,而不是备份实用程序或同步工具。

答案 3 :(得分:11)

这个python脚本可能会有所帮助:对于每个文件,应用修改文件的最新提交的时间戳:

以下是该脚本的真正裸机版本。对于实际使用,我强烈建议使用上面一个更强大的版本:

#!/usr/bin/env python
# Bare-bones version. Current dir must be top-level of work tree.
# Usage: git-restore-mtime-bare [pathspecs...]
# By default update all files
# Example: to only update only the README and files in ./doc:
# git-restore-mtime-bare README doc

import subprocess, shlex
import sys, os.path

filelist = set()
for path in (sys.argv[1:] or [os.path.curdir]):
    if os.path.isfile(path) or os.path.islink(path):
        filelist.add(os.path.relpath(path))
    elif os.path.isdir(path):
        for root, subdirs, files in os.walk(path):
            if '.git' in subdirs:
                subdirs.remove('.git')
            for file in files:
                filelist.add(os.path.relpath(os.path.join(root, file)))

mtime = 0
gitobj = subprocess.Popen(shlex.split('git whatchanged --pretty=%at'),
                          stdout=subprocess.PIPE)
for line in gitobj.stdout:
    line = line.strip()
    if not line: continue

    if line.startswith(':'):
        file = line.split('\t')[-1]
        if file in filelist:
            filelist.remove(file)
            #print mtime, file
            os.utime(file, (mtime, mtime))
    else:
        mtime = long(line)

    # All files done?
    if not filelist:
        break

所有版本都会解析单个git whatchanged命令生成的完整日志,这比每个文件的填充速度快几百倍。 git(24,000次提交,2,500个文件)不到4秒,linux内核不到1分钟(40,000个文件,300,000次提交)

答案 4 :(得分:5)

这对我来说就是ubuntu(在日期(1)上缺少OSX的“-j”标志)

for FILE in $(git ls-files)
do
    TIME=$(git log --pretty=format:%cd -n 1 --date=iso $FILE)
    TIME2=`echo $TIME | sed 's/-//g;s/ //;s/://;s/:/\./;s/ .*//'`
    touch -m -t $TIME2 $FILE
done 

答案 5 :(得分:3)

我已经用git和文件时间戳进行了一段时间的冲突。

测试了你的一些想法并制作了我自己非常庞大的前任/ ram重型脚本,直到我发现(在某些git wiki上)perl中的脚本几乎可以实现我想要的。 https://git.wiki.kernel.org/index.php/ExampleScripts

我想要的是能够根据提交日期保留文件的最后修改。

因此,经过一些调整后,脚本可以在 2-3分钟周围更改 200k 文件的创建和修改日期。

#!/usr/bin/perl
my %attributions;
my $remaining = 0;

open IN, "git ls-tree -r --full-name HEAD |" or die;
while (<IN>) {
    if (/^\S+\s+blob \S+\s+(\S+)$/) {
        $attributions{$1} = -1;
    }
}
close IN;

$remaining = (keys %attributions) + 1;
print "Number of files: $remaining\n";
open IN, "git log -r --root --raw --no-abbrev --date=raw --pretty=format:%h~%cd~ |" or die;
while (<IN>) {
    if (/^([^:~]+)~([^~]+)~$/) {
        ($commit, $date) = ($1, $2);
    } elsif (/^:\S+\s+1\S+\s+\S+\s+\S+\s+\S\s+(.*)$/) {
        if ($attributions{$1} == -1) {
            $attributions{$1} = "$date";
            $remaining--;

            utime $date, $date, $1;
            if ($remaining % 1000 == 0) {               
                print "$remaining\n";
            }
            if ($remaining <= 0) {
                break;
            }
        }
    }
}
close IN;

假设您的存储库没有10k +文件,这应该需要几秒钟才能执行,因此您可以将其挂钩到checkout,pull或其他git基本挂钩。

答案 6 :(得分:1)

这是我的解决方案,它考虑了包含空格的路径:

#! /bin/bash

IFS=$'\n'
list_of_files=($(git ls-files | sort))
unset IFS

for file in "${list_of_files[@]}"; do
  file_name=$(echo $file)

  ## When you collect the timestamps:
  TIME=$(date -r "$file_name" -Ins)

  ## When you want to recover back the timestamps:
  touch -m -d $TIME "$file_name"
done

请注意,这不会花费git log报告的时间,而是系统报告的时间。如果您想要提交文件后的时间,请使用git log解决方案而不是date -r

答案 7 :(得分:1)

原生git没有这个功能,但可以通过钩子脚本或第三方工具来实现。

我已经尝试metastore了。它非常快,但我不喜欢安装的需要,并且元数据不是以纯文本格式存储的。 git-cache-meta是我尝试过的一个简单工具,但对于大型回购而言速度极慢(对于拥有数万个文件的回购,更新元数据文件需要几分钟)并且可能有跨平台兼容性问题。 setgitperms和其他方法也存在我不喜欢的缺点。

最后我为这份工作制作了一个钩子脚本:git-store-meta。它具有非常轻的依赖(* nix shell,sortperl,这是git和chownchgrptouch)因此,对于可运行git,理想性能的平台,不需要安装任何其他内容(对于包含数万个文件的repo,更新需要<10秒)元数据文件;虽然创建时间较长),但以纯文本格式保存数据,以及保存哪些元数据&#34;或者&#34;加载&#34; 可自定义

它对我来说很好。如果您对metastore,git-cache-meta和其他方法不满意,请尝试此操作。

答案 8 :(得分:1)

我(以及其他人)对OP的解释是否意味着这意味着提交时间还是别的,但是假设它意味着提交时间,那么这个简单的单行将在linux中工作(基于答案片段)来自Dietrich Epp):

git ls-files | xargs -I{} bash -c 'touch "{}" --date=@$(git log -n1 --pretty=format:%ct -- "{}")'

但是从评论链接到cregox的原始问题还有更复杂的答案(包括git hooks)。

答案 9 :(得分:0)

对于Windows环境,我在Delphi 10.1 Berlin中编写了一个小的(快速且脏的)EXE,它将源树中的所有文件日期收集到文件.gitfilattr中,并可以再次在已检查的源代码树上应用它们。

当然我在GitHub中共享代码:

https://github.com/michaschumann/gitfiledates/blob/master/gitFileDates.dpr

我在基于GitLab跑步者的构建系统中使用它。

答案 10 :(得分:0)

使用GNU工具。

s=$(git ls-files  | wc -l); 
git ls-files -z  |
 xargs -0 -I{} -n1 bash -c \
"git log --date=format:%Y%m%d%H%M.%S '--pretty=format:touch -m -t %cd \"{}\"%n' -n1 -- {}"|
 pv -l -s$s |
 parallel -n1 -j8

 967  0:00:05 [ 171 /s] [=====================================>  ] 16% 

$ git --version ; xargs --version | sed 1q ; ls --version | sed 1q;
  parallel --version  | sed 1q;  pv --version | sed 1q; sh --version | sed 1q 
git version 2.13.0
xargs (GNU findutils) 4.6.0
ls (GNU coreutils) 8.25
GNU parallel 20150522
pv 1.6.0 - Copyright 2015 Andrew Wood <andrew.wood@ivarch.com>
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)

答案 11 :(得分:0)

我希望您欣赏它的简单性:

# getcheckin - Retrieve the last committed checkin date and time for
#              each of the files in the git project.  After a "pull"
#              of the project, you can update the timestamp on the
#              pulled files to match that date/time.  There are many
#              that don't believe that this is not a good idea, but
#              I found it useful to get the right source file dates
#
#              NOTE: This script produces commands suitable for
#                    piping into BASH or other shell
# License: Creative Commons Attribution 3.0 United States
# (CC by 3.0 US)

##########
# walk back to the project parent or the relative pathnames don't make
# sense
##########
while [ ! -d ./.git ]
do
    cd ..
done
echo "cd $(pwd)"
##########
# Note that the date format is ISO so that touch will work
##########
git ls-tree -r --full-tree HEAD |\
    sed -e "s/.*\t//" | while read filename; do
    echo "touch --date=\"$(git log -1 --date=iso --format="%ad" -- "$filename")\" -m $filename" 
done

答案 12 :(得分:0)

在CentOS 7中,您有/usr/share/doc/rsync-*/support/git-set-file-times,而在Debian(及其衍生产品)中,/usr/share/doc/rsync/scripts/git-set-file-times.gz中有相同的脚本,原始脚本来自Eric Wong,位于https://yhbt.net/git-set-file-times

它的工作速度比这里提到的其他示例要快,您可能会发现将其安装在Linux发行版中更为方便。

答案 13 :(得分:0)

这是我的。

比其他一些更快,因为我不为找到的每个文件调用“获取日志”;而是一次调用“ git log”并将其输出转换为触摸命令。

在某些情况下,一次提交中列出的文件太多,无法容纳在单个shell命令缓冲区中。运行“ getconf ARG_MAX”以查看命令的最大长度(以字节为单位)-在我的debian安装中为2MB,足够了。

# set file last modification time to last commit of file
git log --reverse --date=iso --name-only | \
  grep -vE "^(commit |Merge:|Author:|    |^$)" | \
  grep -B 1 "^[^D][^a][^t][^e][^:][^ ]" | \
  grep -v "^\-\-" | \
  sed "s|^\(.*\)$|\"\1\"|;s|^\"Date: *\(.*\)\"$|~touch -c -m -d'\1'|" | \
  tr '~\n' '\n ' | \
  sh -

按行描述:

  • 最早提交的列表和文件名
  • 过滤掉不需要的提交/合并/作者行
  • 过滤掉以双破折号开头的行
  • sed(流编辑)命令 a)在行的前面加上/附加双引号,并且 b)用〜touch -c -m -d替换“日期:。”。 (触摸命令选项是 -c =如果不存在则不创建, -m =更改文件修改时间,并且 -d =使用提供的日期/时间)
  • 分别将tilda(〜)和newline(\ n)字符转换为换行符和空格
  • 将生成的文本行流传输到外壳中。

就速度而言,它需要5秒1700提交700个目录中的6500个文件。

答案 14 :(得分:0)

https://github.com/DotCi/jenkinsci-dotci-example/commit/5a45034d13b85ab4746650995db55b5281451cec#diff-a83424d0d40754ac7e2029b13daa2db43651eb65aabf8c9a5a45005b56f259bdR19

for file in `find . -type f -not -path "./.git/*"`; do 
  touch -d "`git rev-list -n 1 HEAD \$file | xargs git show -s --format=%ai`" $file; 
done