원래 생성 / 수정 된 타임 스탬프가있는 오래된 파일 체크 아웃


82

원래 생성 / 수정 된 타임 스탬프를 알거나 얻을 수있는 방법이 있습니까?


1
이것은 청소기 페이지이지만, 두 질문 대부분의 투표 대답은 기본적으로 중복 : stackoverflow.com/questions/1964470/...
cregox


이것이 귀하의 질문에 대답합니까? git의 use-commit-times에 해당하는 것은 무엇입니까?
Trilarion

답변:


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를 설계하는 것은 근시안적입니다. 나는 이것이 힘내의 잘못이라고 생각합니다. 따라서 이것이 기본 동작 이 아니라는 것은 말이 되지 않습니다 . Make 파일은 타임 스탬프가 아닌 파일 내용에서 실행되어야합니다. 파일을 해시하고 해시가 빌드 한 것과 일치하는지 확인하는 것이 훨씬 더 강력합니다.
BT

4
나는 BT 및 귀하의 의견 Dietrich의 일부에 동의합니다. BT가 OP에 대해 의미하는 것은 귀하의 답변이 파일의 원래 시간을 실제로 유지할 수 없다는 것입니다. 대신 원래 체크 아웃 시간으로 대체됩니다. 똑같지는 않습니다 ... 그래서 그가 당신의 게시물에 사실적인 오류가 있다고 분명히 말한 것 같습니다. 여러분이 지적 하셨듯이 타임 스탬프를 저장하지 않기로 한 결정이 어디에서 왔는지 알 수 있습니다. 나는 또한 BT가 거기에서 그 추론으로 약간 되돌아 가고 있다고 생각합니다. 나는 다시 BT에 동의합니다-전혀 할 수없는 좋은 이유가 아닙니다. 다른 모든 VCS가이를 수행 할 수 있습니다.
cregox

57

, metastore 또는 git-cache-meta 는 이러한 (meta-) 정보를 저장할 수 있습니다! Git 자체는 타사 도구 없이는 불가능합니다. Metastore 또는 git-cache-meta 는 파일에 대한 모든 파일 메타 데이터를 저장할 수 있습니다.

메타 스토어 또는 git-cache-meta는 백업 유틸리티 및 동기화 도구를 지원할뿐만 아니라 그 목적을 위해 고안된 것입니다.

(Jakub의 대답에 약간의 재미를 돌려서 죄송합니다)


8
당신은 심지어 그의 모든 대문자를 모방했습니다! 굵은 글씨도 적용하면 더 많은 찬성표를 얻을 수 있습니다. ;-)
Michael Scheper 2014

1
그래서 저는 주로이 두 가지 도구 (일부 조사를 마친 후)가 macOS에서 멋진 방식으로 공을 떨어 뜨리기 때문에 약간 실망했습니다. 그들은 Linux에서 완전히 이식 할 수 없습니다. git-cache-meta는 GNU find-printf확장 에 의존 하며, 저는 메타 스토어 (C 프로젝트)가 이식성을 높이기 위해 훨씬 더 많은 작업을 수행한다고 확신합니다. 매우 유감입니다. 이 상황이 바뀌면 여기에 다시 게시하겠습니다.
Steven Lu

39

아니요 , 간단히 힘내 metastore 또는 git-cache-meta와 같은 타사 도구를 사용하지 않는 한 이러한 (meta-) 정보를 저장하지 않습니다 . 저장되는 유일한 타임 스탬프는 패치 / 변경이 생성 된 시간 (작성자 시간)과 커밋이 생성 된 시간 (커미터 시간)입니다.

Git은 백업 유틸리티 나 동기화 도구가 아닌 버전 제어 시스템이므로 의도적으로 설계된 것입니다.


win32 용 메타 스토어 빌드가 있습니까? 아니면 Windows 용 스크립트 / 후크를 다시 만들어야합니까? Franklt, 다른 속성은 필요하지 않습니다. mtime 만
Arioch 'The

8
귀하의 대답은 실제로 "예! Metastore 또는 git-cache-meta가이 작업을 수행 할 수 있습니다!"라고 생각합니다. 나는 그것이 패배 주의자와 낙관주의 자 태도의 차이라고 생각한다.
BT

3
게다가 내가 들었던 바와 같이, bazaarmercurial 은 메타 정보를 저장하는 "버전 제어 시스템"이기도합니다. 그렇게 하는 것이 그렇게 잘못된 것은 아닙니다 .
cregox

설명 : Git은 각 파일에 대해 두 개의 타임 스탬프를 유지합니다 : 작성자 날짜 (Jakub이 '시간 패치'에서 의미하는 것임)와 커미터 날짜입니다. 전자는 파일이 처음 커밋 된 시간이고 후자는 파일이 가장 최근에 커밋 된 시간입니다.
Michael Scheper 2014

4
"Git은 백업 유틸리티 나 동기화 도구가 아닌 버전 제어 시스템이므로 의도적으로 설계된 것입니다." 이는 비공식적입니다 . 메타 데이터 ( 특히 버전과 밀접한 관련이있는 날짜)를 무시 하는 것은 VCS 또는 백업 도구와 관련이 없습니다. 또한 모든 VCS는 백업 도구와 기능이 크게 중복됩니다. 둘 다 중요한 과거 상태를 보존하기 위해 노력합니다. 마지막으로 Git조차도 VCS 임에도 불구하고 모든 메타 데이터를 무시하지 않습니다 (예 : exec. 비트 추적). 아직 입니다 내용에 대한 망할 놈의 독점 초점 : 그냥 다른 이유하지만, 설계.
Sz.

13

UPDATE : TL; DR : git 자체는 원래 시간을 저장하지 않지만 일부 솔루션은 다양한 방법으로이를 우회합니다. git-restore-mtime다음 중 하나입니다.

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, 디버그 메시지와 함께 핵심 기능 . 작업 트리 내 어디에서나 실행 가능
  • 다양한 옵션을 갖춘 본격적인 짐승 . 모든 저장소 레이아웃을 지원합니다.

아래는 스크립트 의 진짜 베어 본 버전입니다. 실제 사용을 위해 위의보다 강력한 버전 중 하나를 강력히 제안합니다.

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

$ 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을 사용했는데, 지적 해주셔서 Python 3에서 코드를 약간 수정해야하는 것 같습니다. 그 이유는 : str파이썬 2의 것과 동일 bytestring하지만, 파이썬 3에서 str파이썬 3에 unicode당신이에서이 문제를보고하십시오 수 파이썬 2에 github.com/MestreLion/git-tools/issues ?
MestreLion 2014 년

그것은 단지 "폭탄"이 아닙니다. 또한 코드가 수행하는 작업을 훨씬 더 자세히 설명하여 명확하게 설명합니다.
cregox 2014 년

6

이것은 그가 우분투에서 나를 위해 속임수를 쓴 것입니다 (OSX의 date (1)에 "-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 및 파일 타임 스탬프와 충돌했습니다.

당신의 아이디어 중 일부를 테스트하고 내가 원하는 것을 거의 수행하는 perl에서 스크립트를 찾을 때까지 (일부 git wiki에서) 엄청나게 크고 전임자 / 램 무거운 스크립트를 만들었습니다. 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;

리포지토리에 10,000 개 이상의 파일이 없다고 가정하면 실행하는 데 몇 초가 걸리므로 체크 아웃, 풀 또는 기타 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에서 스크립트 쉘을, sort그리고 perl자식에 의해 요구하고 선택적으로 된, chown, chgrptouch) 자식, 실행할 수있는 플랫폼 설치해야합니다 추가하는 것도 있도록 바람직한 성능을 수만와의 repo에 대해 ( 파일의 경우 메타 데이터 파일을 업데이트하는 데 10 초 미만이 걸리며 생성하는 데 더 오래 걸리지 만 일반 텍스트 형식으로 데이터를 저장하며 "저장"또는 "로드"할 메타 데이터를 사용자 정의 할 수 있습니다 .

그것은 나를 위해 잘 작동했습니다. 메타 스토어, 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

이것이 커밋 시간을 의미하는지 아니면 다른 것을 의미하는지에 대한 내 (및 다른 사람들의) 해석에 약간 모호성이 있지만 커밋 시간을 의미한다고 가정하면이 간단한 한 줄이 Linux에서 작동합니다 (Dietrich Epp의 답변 스 니펫을 기반으로 함). ) :

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

그러나 cregox의 댓글에서 원래 질문에 연결된 더 정교한 답변 (git hooks 포함)이 있습니다.


lol 이것은 내 체크 아웃에 엄청난 수의 파일을 덤프했습니다--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
Ярослав Рахматуллин aug.

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

여기 내 꺼야.

발견 된 각 파일에 대해 '로그 가져 오기'를 호출하지 않기 때문에 다른 파일보다 조금 더 빠릅니다. 대신 'git log'를 한 번 호출하고 해당 출력을 터치 명령으로 변환합니다.

단일 셸 명령 버퍼에 맞추기에는 하나의 커밋에 나열된 파일이 너무 많은 경우가 있습니다. "getconf ARG_MAX"를 실행하여 명령의 최대 길이 (바이트)를 확인하십시오. 데비안 설치에서는 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) "Date :. "를 ~ touch -c -m -d로 바꿉니다. (터치 명령 옵션은 -c = 존재하지 않는 경우 생성하지 않음, -m = 파일 수정 시간 변경, -d = 제공된 날짜 / 시간 사용)
  • tilda (~) 및 newline (\ n) 문자를 각각 개행 문자와 공백으로 변환
  • 결과적인 텍스트 행 스트림을 쉘로 파이프하십시오.

속도면에서 700 개 디렉토리의 6500 개 파일에 대해 5 초 1700이 커밋됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.