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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2179722/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 07:46:18  来源:igfitidea点击:

Checking out old file WITH original create/modified timestamps

git

提问by Scud

Is there any way to know or get the original create/modified timestamps? Thanks.

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

采纳答案by Dietrich Epp

I believe that the only timestamps recorded in the Git database are the author and commit timestamps. I don't see an option for Git to modify the file's timestamp to match the most recent commit, and it makes sense that this wouldn't be the default behavior (because if it were, Makefiles wouldn't work correctly).

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

You could write a script to set the modification date of your files to the the time of the most recent commit. It might look something like this:

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

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

回答by B T

YES, metastore or git-cache-meta can store such (meta-)information!Git by itself, without third party tools, can't. Metastoreor git-cache-metacan store any file metadata for a file.

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

That is by design, as metastore or git-cache-meta are intended for that very purpose, as well as supporting backup utilities and synchronization tools.

这是设计使然,因为 Metastore 或 git-cache-meta 就是为此目的而设计的,并且支持备份实用程序和同步工具。

(Sorry just a little fun spin on Jakub's answer)

(抱歉,Jakub 的回答有点有趣)

回答by Jakub Nar?bski

NO, Git simply does not store such (meta-)information, unless you use third party tools like metastoreor git-cache-meta. The only timestamp that get stored is the time patch/change was created (author time), and the time commit was created (committer time).

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

That is by design, as Git is version control system, not a backup utility or synchronization tool.

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

回答by MestreLion

This python script may help: for each file applies the timestamp of the most recent commit where the file was modified:

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

  • 核心功能,带有 --help,调试消息。可以在工作树中的任何地方运行
  • 成熟的野兽,有很多选择。支持任何存储库布局。

Below is a reallybare-bones version of the script. For actual usage I strongly suggest one of the more robust versions above:

下面是脚本的一个非常简单的版本。对于实际使用,我强烈建议使用上面更强大的版本之一:

#!/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

All versions parse the full log generated by a single git whatchangedcommand, which is hundreds of times faster than lopping for each file. Under 4 seconds for git (24,000 commits, 2,500 files) and less than 1 minute for linux kernel (40,000 files, 300,000 commits)

所有版本都解析由单个git whatchanged命令生成的完整日志,这比对每个文件进行 lopping 快数百倍。git 不到 4 秒(24,000 次提交,2,500 个文件),Linux 内核不到 1 分钟(40,000 个文件,300,000 次提交)

回答by eludom

This did he trick for me on ubuntu (which lacks OSX's the "-j" flag on date(1))

这是他在 ubuntu 上骗我的(在 date(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 

回答by Lukasz Kruszyna

I have been skirmishing with git and file timestamps for some time already.

一段时间以来,我一直在与 git 和文件时间戳发生冲突。

Tested some of your ideas and made my own awfully huge and predecessor/ram heavy scripts, untill i found (on some git wiki) a script in perl that does almost what i wanted. https://git.wiki.kernel.org/index.php/ExampleScripts

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

And what i wanted is to be able to preserve last modification of files based on commit dates.

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

So after some readjustment the script is able to change creation and modification date of 200kfiles in around 2-3min.

因此,经过一些重新调整后,脚本能够在大约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;
    }
}
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) = (, );
    } elsif (/^:\S+\s+1\S+\s+\S+\s+\S+\s+\S\s+(.*)$/) {
        if ($attributions{} == -1) {
            $attributions{} = "$date";
            $remaining--;

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

Assuming that your repositories wont have 10k+ files this should take seconds to execute, so you can hook it to the checkout, pull or other git basic hooks.

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

回答by sailnfool

I hope you appreciate the simplicity:

我希望你喜欢简单:

# 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 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

回答by Lilian A. Moraru

Here is my solution that takes into consideration paths that contain spaces:

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

#! /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

Note that this does not take the time which git logreports, it's the time reported by the system. If you want the time since the files were commited use git logsolution instead of date -r

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

回答by Danny Lin

Native git doesn't have the functionality, but it can be achieved by hook scripts or third party tools.

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

I've tried metastore. It's very fast, but I don't like the need to install and that metadata are not stored in plain text format. git-cache-metais a simple tool I've tried, but it's extremely slow for large repos (for a repo with tens of thousands of files, it takes minutes to update the metadata file) and could have cross-platform compatibility issues. setgitpermsand other approaches also have their shortcomings that I don't like.

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

At last I made a hook script for this job: git-store-meta. It has very light dependency(*nix shell, sort, and perl, which is required by git, and optionally chown, chgrpand touch) so that nothing additional have to be installed for a platform that can run git, desirable performance(for a repo with tens of thousands of files, it takes < 10 seconds to update the metadata file; although longer to create), saves data in plain text format, and which metadata to be "saved" or "loaded" is customizable.

最后我为这项工作制作了一个钩子脚本:git-store-meta。它具有非常轻的依赖关系(*nix shell, sort, and perl,这是 git 所需要的,以及可选的chown, chgrpand touch),因此不需要为可以运行 git 的平台安装任何额外的东西,理想的性能(对于拥有数万个文件,更新元数据文件需要 < 10 秒;虽然创建时间更长),以纯文本格式保存数据,并且可以自定义要“保存”或“加载”哪些元数据。

It has worked fine for me. Try this if you are not satisfied with metastore, git-cache-meta, and other approaches.

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

回答by MichaSchumann

For Windows environment I wrote a small (quick and dirty) EXE in Delphi 10.1 Berlin that collects all file dates in the source tree into the file .gitfilattr and can apply them on the checked our source tree again.

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

Of course I share the code in GitHub:

当然我在GitHub上分享了代码:

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

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

I use it in my build system based on GitLab runners.

我在基于 GitLab runners 的构建系统中使用它。