元の作成/変更されたタイムスタンプを知るまたは取得する方法はありますか?
元の作成/変更されたタイムスタンプを知るまたは取得する方法はありますか?
回答:
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
はい、metastoreまたはgit-cache-metaはそのような(メタ)情報を保存できます!サードパーティのツールがなければ、Git自体ではできません。メタストアまたはgit-cache-metaは、ファイルの任意のファイルメタデータを保存できます。
metastoreまたはgit-cache-metaは、バックアップユーティリティと同期ツールをサポートするだけでなく、まさにその目的を目的としているため、これは仕様によるものです。
(ヤクブの答えを少しだけ楽しんで申し訳ありません)
find
の-printf
拡張機能に依存しており、メタストア(Cプロジェクトである)が移植可能にするためにさらに多くの作業を行うことはほぼ確実です。非常に残念です。この状況が変化したことがわかったら、ここに投稿します。
いいえ、Gitはそのような(メタ)情報を保存しませんあなたのようなサードパーティ製のツールを使用しない限り、メタストアをやgit-cache-meta。保存されるタイムスタンプは、パッチ/変更が作成された時刻(作成者の時刻)とコミットが作成された時刻(コミッターの時刻)のみです。
Gitはバージョン管理システムであり、バックアップユーティリティや同期ツールではないため、これは仕様によるものです。
更新:TL; DR:git自体は元の時間を保存しませんが、いくつかのソリューションはさまざまな方法でこれを回避します。git-restore-mtime
それらの1つです:
https://github.com/MestreLion/git-tools/
Ubuntu / Debian: sudo apt install git-restore-mtime
Fedora / RHEL / CentOS:sudo yum install git-tools
完全な免責事項:私はの作者です git-tools
このPythonスクリプトが役立つ場合があります。ファイルごとに、ファイルが変更された最新のコミットのタイムスタンプが適用されます。
以下は、スクリプトの本当に必要最低限のバージョンです。実際の使用については、上記のより堅牢なバージョンの1つを強くお勧めします。
#!/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の場合は4秒未満(24,000コミット、2,500ファイル)、Linuxカーネルの場合は1分未満(40,000ファイル、300,000コミット)
$ python ./git-restore-mtime Traceback (most recent call last): File "./git-restore-mtime", line 122, in <module> 'git rev-parse --show-toplevel --git-dir')).split('\n')[:2] TypeError: Type str doesn't support the buffer API
必要なPythonのバージョンを教えていただけませんか。私は3.3.3を使用しています
str
Pythonの2中の相当しbytestring
ながら、Pythonの3中str
のPython 3であるunicode
あなたがでこの問題を報告してくださいすることができPythonの2にgithub.com/MestreLion/git-tools/issues?
私はすでにしばらくの間、gitとfileのタイムスタンプで小競り合いをしています。
あなたのアイデアのいくつかをテストし、私が(いくつかのgit wikiで)私が望んでいたことをほぼ実行するスクリプトをperlで見つけるまで、私自身の非常に巨大で前任者/ RAMの重いスクリプトを作成しました。 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以上のファイルがないと仮定すると、実行には数秒かかるはずなので、チェックアウト、プル、またはその他のgit基本フックにフックできます。
スペースを含むパスを考慮した私のソリューションは次のとおりです。
#! /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
ネイティブgitには機能がありませんが、フックスクリプトまたはサードパーティツールによって実現できます。
私は試しましたmetastore
。非常に高速ですが、インストールする必要がなく、メタデータがプレーンテキスト形式で保存されていません。git-cache-meta
は私が試したシンプルなツールですが、大きなリポジトリでは非常に遅く(数万のファイルがあるリポジトリの場合、メタデータファイルの更新には数分かかります)、クロスプラットフォームの互換性の問題が発生する可能性があります。setgitperms
他のアプローチにも、私が気に入らない欠点があります。
ついに私はこの仕事のためのフックスクリプトを作りました:git-store-meta。依存関係が非常に軽いため(* nix shell 、、、sort
およびperl
gitで必要chown
でchgrp
あり、オプションで、、およびtouch
)、gitを実行できるプラットフォームに追加のインストールは必要ありません。望ましいパフォーマンス(数万のリポジトリの場合)ファイルの場合、メタデータファイルの更新には10秒未満かかりますが、作成には時間がかかります)、データをプレーンテキスト形式で保存し、「保存」または「ロード」するメタデータをカスタマイズできます。
それは私にとってはうまくいきました。metastore、git-cache-meta、およびその他のアプローチに満足できない場合は、これを試してください。
シンプルさを評価していただければ幸いです。
# 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
Windows環境の場合、Delphi 10.1ベルリンで小さな(迅速で汚い)EXEを作成しました。これは、ソースツリー内のすべてのファイル日付をファイル.gitfilattrに収集し、チェックしたソースツリーに再度適用できます。
もちろん、私はGitHubでコードを共有しています。
https://github.com/michaschumann/gitfiledates/blob/master/gitFileDates.dpr
GitLabランナーをベースにしたビルドシステムで使用しています。
私(および他の人)のOPの解釈には、これがコミット時間か他の何かを意味するかについていくつかのあいまいさがありますが、それがコミット時間を意味すると仮定すると、この単純なワンライナーはLinuxで機能します(Dietrich Eppの回答スニペットに基づく) )::
git ls-files | xargs -I{} bash -c 'touch "{}" --date=@$(git log -n1 --pretty=format:%ct -- "{}")'
しかし、コメントからcregoxによる元の質問にリンクされた、より洗練された回答(gitフックを含む)があります。
--date=@foo
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)
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ディストリビューションにすでにインストールされている方が便利な場合があります。
これが私のものです。
見つかったファイルごとに「getlog」を呼び出さないため、他のファイルよりも少し速くなります。代わりに、「git log」を一度呼び出して、その出力をタッチコマンドに変換します。
1つのコミットにリストされているファイルが多すぎて、単一のシェルコマンドバッファーに収まらない場合があります。「getconfARG_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 -
行ごとの説明:
速度に関しては、700ディレクトリの6500ファイルに対して1700が5秒コミットします。