元の作成/変更されたタイムスタンプで古いファイルをチェックアウトする


82

元の作成/変更されたタイムスタンプを知るまたは取得する方法はありますか?


1
これは、クリーナーのページですが、両方の質問と最も投票の答えは基本的に重複している:stackoverflow.com/questions/1964470/...
cregox


これはあなたの質問に答えますか?gitのuse-commit-timesに相当するものは何ですか?
トリラリオン

回答:


45

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

10
このスニペットにはいくつかの問題があります。1-ファイル名にスペースがあると失敗します。2-数千ファイルを超えるプロジェクトでは失敗する可能性があります。3-パフォーマンスは、数千のコミットがある中規模のプロジェクトでは絶対に悲惨です(ファイルが少ない場合でも)
MestreLion 2012年

10
+1は、考えられるすべてのケースで機能するとは限りませんが、簡単な答えです。
qwerty9967 2013年

5
OPの質問は、ファイルへのコミットタイムスタンプを固定するのではなく、元のファイルの変更されたタイムスタンプをどのように保持するかではありませんか?
BT

15
Makeを中心としたVCSの設計は近視眼的です。これはGitのせいだと思います。したがって、これがデフォルトの動作ではないことは実際に意味がありません。Makeファイルは、タイムスタンプではなく、ファイルの内容で実行する必要があります。ファイルをハッシュして、ハッシュが作成したものと一致するかどうかを確認する方がはるかに堅牢です。
BT

4
私はBTとあなたのコメントディートリッヒの一部に同意します。BTがOPについて意味したのは、あなたの答えではファイルの元の時間を保持することが実際には許可されていないということです。代わりに、元のチェックアウト時間に置き換えられます。同じことではありません...だから、彼はあなたの投稿に事実上の誤りが含まれているとはっきり言ったと思います。ご指摘のとおり、タイムスタンプを保存しないという決定がどこから来たのかがわかります。私はまた、BTがそこでのその推論に少し戻って怒鳴っていると思います。私はBTに再び同意します-それをまったくできない理由はありません。他のすべてのVCSはそれを行うことができます。
cregox 2013

57

はい、metastoreまたはgit-cache-metaはそのような(メタ)情報を保存できます!サードパーティのツールがなければ、Git自体ではできません。メタストアまたはgit-cache-metaは、ファイルの任意のファイルメタデータを保存できます。

metastoreまたはgit-cache-metaは、バックアップユーティリティと同期ツールをサポートするだけでなく、まさにその目的を目的としているため、これは仕様によるものです。

(ヤクブの答えを少しだけ楽しんで申し訳ありません)


8
あなたも彼のオールキャップを模倣しました!太字も当てはめると、さらに多くの賛成が得られると思います。;-)
Michael Scheper 2014

1
ですから、主にこれらのツールの両方が(いくつか掘り下げた後)macOSに見事な方法でボールを落とすので、私は少し不安になります。Linuxからは完全に移植できません。git-cache-metaはGNUfind-printf拡張機能に依存しており、メタストア(Cプロジェクトである)が移植可能にするためにさらに多くの作業を行うことはほぼ確実です。非常に残念です。この状況が変化したことがわかったら、ここに投稿します。
Steven Lu

39

いいえ、Gitはそのような(メタ)情報を保存しませんあなたのようなサードパーティ製のツールを使用しない限り、メタストアをやgit-cache-meta。保存されるタイムスタンプは、パッチ/変更が作成された時刻(作成者の時刻)とコミットが作成された時刻(コミッターの時刻)のみです。

Gitはバージョン管理システムであり、バックアップユーティリティや同期ツールではないため、これは仕様によるものです。


win32用のメタストアビルドはありますか?または、Windows用のスクリプト/フックを再作成する必要がありますか?Franklt、他の属性は必要ありません。mtimeのみ
Arioch '

8
あなたの答えは実際には「はい!Metastoreまたはgit-cache-metaがあなたのためにこれを行うことができます!」だと思います。敗北者と楽観主義者の違いだと思います。
BT

3
さらに、私が聞いたように、バザールマーキュリアルはメタ情報を保存する「バージョン管理システム」でもあります。そうすることに何も悪いことありません
cregox 2013

明確化:Gitは、ファイルごとに2つのタイムスタンプを保持します。作成者の日付(Jakubが「タイムパッチ」で意味していると思います)とコミッターの日付です。前者はファイルが最初にコミットされた時間であり、後者はファイルが最後にコミットされた時間です。
Michael Scheper 2014

4
「Gitはバージョン管理システムであり、バックアップユーティリティや同期ツールではないため、これは仕様によるものです。」それがあります非sequitur:メタデータを無視して(特にVCS、またはバックアップツールであることとは何の関係もありません密接バージョンに関連している日付)。また、すべてのVCSには、バックアップツールと機能の大きな重複があります。どちらも重要な過去の状態を維持しようと努めています。最後に、Gitでさえ、VCSであるにもかかわらず、すべてのメタデータを無視するわけではありません(たとえば、exec。ビットを追跡します)。ただし、それはまだ設計によるものです。それ、Gitがコンテンツに専念しているという別の理由によるものです。
Szを。

13

更新: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スクリプトが役立つ場合があります。ファイルごとに、ファイルが変更された最新のコミットのタイムスタンプが適用されます。

  • コア機能--helpを使用した、メッセージをデバッグします。作業ツリー内のどこでも実行できます
  • たくさんのオプションを備えた本格的な獣。あらゆるリポジトリレイアウトをサポートします。

以下は、スクリプトの本当に必要最低限のバージョンです。実際の使用については、上記のより堅牢なバージョンの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コミット)


2
あなたの他の同様の答えはこれよりはるかに優れています!
cregox 2013

$ 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を使用しています
Rolf

@Cawas:ありがとう...私は推測します。しかし、両方の回答のコードは同じであるため、なぜもう一方の方が優れていると思うのかわかりません。唯一の違いは、gitについての怒りです。これはその質問にいくらか関係がありましたが、これには関係ありませんでした。
MestreLion 2014年

1
@Rolf:Python 2.7を使用しましたが、指摘してくれてありがとう、Python3でコードを微調整する必要があるようです。その理由は次のとおりです。strPythonの2中の相当しbytestringながら、Pythonの3中strのPython 3であるunicodeあなたがでこの問題を報告してくださいすることができPythonの2にgithub.com/MestreLion/git-tools/issues
MestreLion 2014年

それは単なる「暴言」ではありません。そこでは、コードが何をするのかをより詳細に説明し、したがって明確にします。
cregox 2014年

6

これは彼が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 

4

私はすでにしばらくの間、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基本フックにフックできます。


2

スペースを含むパスを考慮した私のソリューションは次のとおりです。

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


2

ネイティブgitには機能がありませんが、フックスクリプトまたはサードパーティツールによって実現できます。

私は試しましたmetastore。非常に高速ですが、インストールする必要がなく、メタデータがプレーンテキスト形式で保存されていません。git-cache-metaは私が試したシンプルなツールですが、大きなリポジトリでは非常に遅く(数万のファイルがあるリポジトリの場合、メタデータファイルの更新には数分かかります)、クロスプラットフォームの互換性の問題が発生する可能性があります。setgitperms他のアプローチにも、私が気に入らない欠点があります。

ついに私はこの仕事のためのフックスクリプトを作りました:git-store-meta依存関係非常に軽いため(* nix shell 、、、sortおよびperlgitで必要chownchgrpあり、オプションで、、およびtouch)、gitを実行できるプラットフォームに追加のインストールは必要ありません。望ましいパフォーマンス(数万のリポジトリの場合)ファイルの場合、メタデータファイルの更新には10秒未満かかりますが、作成には時間がかかります)、データをプレーンテキスト形式で保存し、「保存」または「ロード」するメタデータをカスタマイズできます。

それは私にとってはうまくいきました。metastore、git-cache-meta、およびその他のアプローチに満足できない場合は、これを試してください。


2

シンプルさを評価していただければ幸いです。

# 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

(参考までに、ヘッダーコメントに意図しない二重否定があります。これは元のコメントでも修正する必要があります。「これは良い考えではないと信じている人がたくさんいます。」)
Sz。

1

Windows環境の場合、Delphi 10.1ベルリンで小さな(迅速で汚い)EXEを作成しました。これは、ソースツリー内のすべてのファイル日付をファイル.gitfilattrに収集し、チェックしたソースツリーに再度適用できます。

もちろん、私はGitHubでコードを共有しています。

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

GitLabランナーをベースにしたビルドシステムで使用しています。


1

私(および他の人)のOPの解釈には、これがコミット時間か他の何かを意味するかについていくつかのあいまいさがありますが、それがコミット時間を意味すると仮定すると、この単純なワンライナーはLinuxで機能します(Dietrich Eppの回答スニペットに基づく) )::

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

しかし、コメントからcregoxによる元の質問にリンクされた、より洗練された回答(gitフックを含む)があります。


笑これは名前の私のチェックアウト内のファイルの膨大な数のダンプ--date=@foo
MXCL

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)

parallellはあまり機能していないようで、おそらくfs-bottleneckです。YMMV
ЯрославРахматуллин

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ディストリビューションにすでにインストールされている方が便利な場合があります。


0

これが私のものです。

見つかったファイルごとに「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 -

行ごとの説明:

  • コミットとファイル名の最初のリスト
  • 不要なコミット/マージ/作成行を除外します
  • 二点鎖線で始まる行を除外する
  • sed(ストリーム編集)コマンドa)行に二重引用符を追加/追加し、b)「日付:。」を〜touch -c -m-dに置き換えます。 (タッチコマンドのオプションは、-c =存在しない場合は作成しない、-m =ファイルの変更時刻を変更する、-d =指定された日付/時刻を使用する)です。
  • tilda(〜)とnewline(\ n)の文字をそれぞれnewlineとspaceに変換します
  • 結果のテキスト行のストリームをシェルにパイプします。

速度に関しては、700ディレクトリの6500ファイルに対して1700が5秒コミットします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.