Blob의 해시가 주어지면 트리 에이 Blob이있는 커밋 목록을 얻는 방법이 있습니까?
git log --follow filepath
(원한다면 Aristotle의 솔루션 속도를 높이기 위해 이것을 사용하십시오).
~/.bin
넣고 이름을 지정합니다 git-find-object
. 그런 다음와 함께 사용할 수 있습니다 git find-object
.
Blob의 해시가 주어지면 트리 에이 Blob이있는 커밋 목록을 얻는 방법이 있습니까?
git log --follow filepath
(원한다면 Aristotle의 솔루션 속도를 높이기 위해 이것을 사용하십시오).
~/.bin
넣고 이름을 지정합니다 git-find-object
. 그런 다음와 함께 사용할 수 있습니다 git find-object
.
답변:
다음 스크립트는 BLOB의 SHA1을 첫 번째 인수로 사용하고 선택적으로 git log
이해 하는 모든 인수를 사용합니다 . 예 --all
를 들어 , 현재의 지점이 아닌 모든 지점 -g
에서 검색 하거나 , reflog에서 검색하거나 다른 멋진 항목을 검색하십시오.
짧고 달콤하지만 느리게 쉘 스크립트로 작성되었습니다.
#!/bin/sh
obj_name="$1"
shift
git log "$@" --pretty=format:'%T %h %s' \
| while read tree commit subject ; do
if git ls-tree -r $tree | grep -q "$obj_name" ; then
echo $commit "$subject"
fi
done
그리고 Perl의 최적화 된 버전은 여전히 짧지 만 훨씬 빠릅니다.
#!/usr/bin/perl
use 5.008;
use strict;
use Memoize;
my $obj_name;
sub check_tree {
my ( $tree ) = @_;
my @subtree;
{
open my $ls_tree, '-|', git => 'ls-tree' => $tree
or die "Couldn't open pipe to git-ls-tree: $!\n";
while ( <$ls_tree> ) {
/\A[0-7]{6} (\S+) (\S+)/
or die "unexpected git-ls-tree output";
return 1 if $2 eq $obj_name;
push @subtree, $2 if $1 eq 'tree';
}
}
check_tree( $_ ) && return 1 for @subtree;
return;
}
memoize 'check_tree';
die "usage: git-find-blob <blob> [<git-log arguments ...>]\n"
if not @ARGV;
my $obj_short = shift @ARGV;
$obj_name = do {
local $ENV{'OBJ_NAME'} = $obj_short;
`git rev-parse --verify \$OBJ_NAME`;
} or die "Couldn't parse $obj_short: $!\n";
chomp $obj_name;
open my $log, '-|', git => log => @ARGV, '--pretty=format:%T %h %s'
or die "Couldn't open pipe to git-log: $!\n";
while ( <$log> ) {
chomp;
my ( $tree, $commit, $subject ) = split " ", $_, 3;
print "$commit $subject\n" if check_tree( $tree );
}
git rev-parse --verify $theprefix
my $blob_arg = shift; open my $rev_parse, '-|', git => 'rev-parse' => '--verify', $blob_arg or die "Couldn't open pipe to git-rev-parse: $!\n"; my $obj_name = <$rev_parse>; chomp $obj_name; close $rev_parse or die "Couldn't expand passed blob.\n"; $obj_name eq $blob_arg or print "(full blob is $obj_name)\n";
obj_name="$1" shift git log --all --pretty=format:'%T %h %s %n' -- "$@" | while read tree commit cdate subject ; do if [ -z $tree ] ; then continue fi if git ls-tree -r $tree | grep -q "$obj_name" ; then echo "$cdate $commit $@ $subject" fi done
불행히도 스크립트는 약간 느리기 때문에 조금 최적화해야했습니다. 운 좋게도 해시뿐만 아니라 파일 경로도 가지고있었습니다.
git log --all --pretty=format:%H -- <path> | xargs -n1 -I% sh -c "git ls-tree % -- <path> | grep -q <hash> && echo %"
<hash>
주어진 <path>
에를 포함하는 최신 커밋을 원하면 <path>
인수에서 인수 를 제거 git log
하면 작동합니다. 반환 된 첫 번째 결과는 원하는 커밋입니다.
Blob의 해시가 주어지면 트리 에이 Blob이있는 커밋 목록을 얻는 방법이 있습니까?
Git 2.16 (Q1 2018) 을 사용하면 주어진 얼룩 개체를 나타내는 git describe
나무를 더 깊게 파는 법을 배웠으므로 좋은 해결책이 될 것 <commit-ish>:<path>
입니다.
참조 644eb60 커밋 , 4dbc59a 커밋 , cdaed0c 커밋 , c87b653 커밋 , ce5b6f9 커밋 (2017년 11월 16일을), 및 91904f5 커밋 , 2deda00 투입 하여 (02 년 11 월 2017 년) 스테판 벨러를 ( stefanbeller
) .
(의해 병합 Junio C 하마노 - gitster
- 에 556de1a 커밋 2017 28 십이)
builtin/describe.c
: 얼룩을 설명때때로 사용자는 객체의 해시를 부여하고 추가를 식별 할 (예 : 사용
verify-pack
최대 규모의 모양을 찾을 수 있지만,이? 또는이 매우 SO 질문 무엇 " 이 방울을 가지고 커밋? ")커밋을 설명 할 때 개념적으로는 커밋보다 상위 수준이므로 태그 또는 참조에 앵커를 고정하려고합니다. 정확히 일치하는 심판이나 태그가 없으면 운이 좋지 않습니다.
그래서 우리는 커밋의 이름을 구성하기 위해 휴리스틱을 사용합니다. 이러한 이름은 모호하며 다른 태그 또는 참조가 고정 될 수 있으며 DAG에 커밋에 정확하게 도달하기 위해 이동하는 경로가 다를 수 있습니다.블롭을 기술 할 때, 우리는 상위 계층의 블롭도 기술하고자합니다. 이것은
(commit, deep/path)
관련된 트리 객체가 다소 흥미롭지 않기 때문에 튜플입니다 .
동일한 Blob을 여러 커밋으로 참조 할 수 있으므로 사용할 커밋을 어떻게 결정합니까?이 패치는 이것에 대해 다소 순진한 접근 방식을 구현합니다 . blob에서 blob이 발생하는 커밋에 대한 백 포인터가 없기 때문에 사용 가능한 모든 팁에서 걷기 시작하여 커밋 순서대로 blob을 나열합니다. blob, 우리는 blob을 나열한 첫 번째 커밋을 가져옵니다 .
예를 들면 다음과 같습니다.
git describe --tags v0.99:Makefile conversion-901-g7672db20c2:Makefile
미국 이야기
Makefile
가 있다는 사실v0.99
에 도입 된 7672db2 커밋 .걷기는 마지막 발생이 아닌 얼룩의 도입을 보여주기 위해 역순으로 수행됩니다.
이는 git describe
매뉴얼 페이지 가이 명령의 목적에 추가됨을 의미합니다 .
가장 최근에 도달 할 수있는 가장 최근의 태그를 사용하여 커밋을 설명하는 대신
git describe
실제로로 사용될 때 사용 가능한 참조를 기반으로 사람이 읽을 수있는 이름을 객체에 제공합니다git describe <blob>
.지정된 객체의 Blob를 참조하는 경우,이를 설명한다
<commit-ish>:<path>
블롭이에서 발견 될 수 있도록,<path>
에<commit-ish>
자체는 먼저이 Blob 헤드로부터 역방향 개정 거리에서 발생하는 커밋 설명한다.
그러나:
버그
커밋을 가리 키지 않는 태그 객체뿐만 아니라 트리 객체도 설명 할 수 없습니다 .
블롭을 기술 할 때, 블롭을 가리키는 경량 태그는 무시되지만<committ-ish>:<path>
, 경량 태그가 유리 함에도 불구하고 블롭은 여전히 설명된다 .
git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | awk '/^blob/ {print substr($0,6)}' | sort --numeric-sort --key=2 -r | head -n 20
하면 좋으며 이는 상위 20 개의 가장 큰 얼룩을 반환합니다. 그런 다음 위의 출력에서 Blob ID를로 전달할 수 있습니다 git describe
. 매력으로 일했다! 감사!
나는 이것이 일반적으로 유용한 것이라고 생각했기 때문에 작은 펄 스크립트를 작성했다.
#!/usr/bin/perl -w
use strict;
my @commits;
my %trees;
my $blob;
sub blob_in_tree {
my $tree = $_[0];
if (defined $trees{$tree}) {
return $trees{$tree};
}
my $r = 0;
open(my $f, "git cat-file -p $tree|") or die $!;
while (<$f>) {
if (/^\d+ blob (\w+)/ && $1 eq $blob) {
$r = 1;
} elsif (/^\d+ tree (\w+)/) {
$r = blob_in_tree($1);
}
last if $r;
}
close($f);
$trees{$tree} = $r;
return $r;
}
sub handle_commit {
my $commit = $_[0];
open(my $f, "git cat-file commit $commit|") or die $!;
my $tree = <$f>;
die unless $tree =~ /^tree (\w+)$/;
if (blob_in_tree($1)) {
print "$commit\n";
}
while (1) {
my $parent = <$f>;
last unless $parent =~ /^parent (\w+)$/;
push @commits, $1;
}
close($f);
}
if (!@ARGV) {
print STDERR "Usage: git-find-blob blob [head ...]\n";
exit 1;
}
$blob = $ARGV[0];
if (@ARGV > 1) {
foreach (@ARGV) {
handle_commit($_);
}
} else {
handle_commit("HEAD");
}
while (@commits) {
handle_commit(pop @commits);
}
오늘 저녁에 집에 도착하면 github에 올려 놓겠습니다.
업데이트 : 누군가 이미 이것을 한 것처럼 보입니다 . 하나는 동일한 일반적인 아이디어를 사용하지만 세부 사항은 다르며 구현은 훨씬 짧습니다. 어느 쪽이 더 빠를 지 모르겠지만 성능은 여기에 문제가되지 않습니다!
업데이트 2 : 가치가있는 것, 특히 대규모 저장소의 경우 구현 속도가 훨씬 빠릅니다. 그건 git ls-tree -r
정말 아프다.
업데이트 3 : 위의 성능 의견은 첫 번째 업데이트에서 위에 링크 된 구현에 적용됩니다. 아리스토텔레스의 구현 은 광산과 비슷하게 수행됩니다. 궁금한 사람들의 의견에 대한 자세한 내용.
git rev-parse $commit^{}
원래 질문에 대해서는 묻지 않지만 준비 영역을 검사하여 블롭이 참조되는지 확인하는 것이 유용하다고 생각합니다. 이 작업을 수행하기 위해 원래 bash 스크립트를 수정하고 내 저장소에서 손상된 얼룩을 참조하는 것을 발견했습니다.
#!/bin/sh
obj_name="$1"
shift
git ls-files --stage \
| if grep -q "$obj_name"; then
echo Found in staging area. Run git ls-files --stage to see.
fi
git log "$@" --pretty=format:'%T %h %s' \
| while read tree commit subject ; do
if git ls-tree -r $tree | grep -q "$obj_name" ; then
echo $commit "$subject"
fi
done
그래서 ... 10GB가 넘는 수정 사항이있는 8GB 이상의 저장소에서 주어진 제한을 초과하는 모든 파일을 찾아야했습니다. 필자는이 완벽한 솔루션에 도달하기 위해 작성한 루비 스크립트와 함께 Aristotle의 펄 스크립트를 수정했습니다.
먼저 git gc
-모든 객체가 팩 파일에 있는지 확인하려면 팩 파일에없는 객체는 검사하지 않습니다.
다음이 스크립트를 실행하여 CUTOFF_SIZE 바이트 이상의 모든 얼룩을 찾습니다. "large-blobs.log"와 같은 파일로 출력 캡처
#!/usr/bin/env ruby
require 'log4r'
# The output of git verify-pack -v is:
# SHA1 type size size-in-packfile offset-in-packfile depth base-SHA1
#
#
GIT_PACKS_RELATIVE_PATH=File.join('.git', 'objects', 'pack', '*.pack')
# 10MB cutoff
CUTOFF_SIZE=1024*1024*10
#CUTOFF_SIZE=1024
begin
include Log4r
log = Logger.new 'git-find-large-objects'
log.level = INFO
log.outputters = Outputter.stdout
git_dir = %x[ git rev-parse --show-toplevel ].chomp
if git_dir.empty?
log.fatal "ERROR: must be run in a git repository"
exit 1
end
log.debug "Git Dir: '#{git_dir}'"
pack_files = Dir[File.join(git_dir, GIT_PACKS_RELATIVE_PATH)]
log.debug "Git Packs: #{pack_files.to_s}"
# For details on this IO, see http://stackoverflow.com/questions/1154846/continuously-read-from-stdout-of-external-process-in-ruby
#
# Short version is, git verify-pack flushes buffers only on line endings, so
# this works, if it didn't, then we could get partial lines and be sad.
types = {
:blob => 1,
:tree => 1,
:commit => 1,
}
total_count = 0
counted_objects = 0
large_objects = []
IO.popen("git verify-pack -v -- #{pack_files.join(" ")}") do |pipe|
pipe.each do |line|
# The output of git verify-pack -v is:
# SHA1 type size size-in-packfile offset-in-packfile depth base-SHA1
data = line.chomp.split(' ')
# types are blob, tree, or commit
# we ignore other lines by looking for that
next unless types[data[1].to_sym] == 1
log.info "INPUT_THREAD: Processing object #{data[0]} type #{data[1]} size #{data[2]}"
hash = {
:sha1 => data[0],
:type => data[1],
:size => data[2].to_i,
}
total_count += hash[:size]
counted_objects += 1
if hash[:size] > CUTOFF_SIZE
large_objects.push hash
end
end
end
log.info "Input complete"
log.info "Counted #{counted_objects} totalling #{total_count} bytes."
log.info "Sorting"
large_objects.sort! { |a,b| b[:size] <=> a[:size] }
log.info "Sorting complete"
large_objects.each do |obj|
log.info "#{obj[:sha1]} #{obj[:type]} #{obj[:size]}"
end
exit 0
end
그런 다음 파일을 편집하여 기다리지 않는 얼룩과 맨 위에있는 INPUT_THREAD 비트를 제거하십시오. 찾으려는 sha1에 대한 행만 있으면 다음과 같이 다음 스크립트를 실행하십시오.
cat edited-large-files.log | cut -d' ' -f4 | xargs git-find-blob | tee large-file-paths.log
어디 git-find-blob
스크립트는 다음과 같습니다.
#!/usr/bin/perl
# taken from: http://stackoverflow.com/questions/223678/which-commit-has-this-blob
# and modified by Carl Myers <cmyers@cmyers.org> to scan multiple blobs at once
# Also, modified to keep the discovered filenames
# vi: ft=perl
use 5.008;
use strict;
use Memoize;
use Data::Dumper;
my $BLOBS = {};
MAIN: {
memoize 'check_tree';
die "usage: git-find-blob <blob1> <blob2> ... -- [<git-log arguments ...>]\n"
if not @ARGV;
while ( @ARGV && $ARGV[0] ne '--' ) {
my $arg = $ARGV[0];
#print "Processing argument $arg\n";
open my $rev_parse, '-|', git => 'rev-parse' => '--verify', $arg or die "Couldn't open pipe to git-rev-parse: $!\n";
my $obj_name = <$rev_parse>;
close $rev_parse or die "Couldn't expand passed blob.\n";
chomp $obj_name;
#$obj_name eq $ARGV[0] or print "($ARGV[0] expands to $obj_name)\n";
print "($arg expands to $obj_name)\n";
$BLOBS->{$obj_name} = $arg;
shift @ARGV;
}
shift @ARGV; # drop the -- if present
#print "BLOBS: " . Dumper($BLOBS) . "\n";
foreach my $blob ( keys %{$BLOBS} ) {
#print "Printing results for blob $blob:\n";
open my $log, '-|', git => log => @ARGV, '--pretty=format:%T %h %s'
or die "Couldn't open pipe to git-log: $!\n";
while ( <$log> ) {
chomp;
my ( $tree, $commit, $subject ) = split " ", $_, 3;
#print "Checking tree $tree\n";
my $results = check_tree( $tree );
#print "RESULTS: " . Dumper($results);
if (%{$results}) {
print "$commit $subject\n";
foreach my $blob ( keys %{$results} ) {
print "\t" . (join ", ", @{$results->{$blob}}) . "\n";
}
}
}
}
}
sub check_tree {
my ( $tree ) = @_;
#print "Calculating hits for tree $tree\n";
my @subtree;
# results = { BLOB => [ FILENAME1 ] }
my $results = {};
{
open my $ls_tree, '-|', git => 'ls-tree' => $tree
or die "Couldn't open pipe to git-ls-tree: $!\n";
# example git ls-tree output:
# 100644 blob 15d408e386400ee58e8695417fbe0f858f3ed424 filaname.txt
while ( <$ls_tree> ) {
/\A[0-7]{6} (\S+) (\S+)\s+(.*)/
or die "unexpected git-ls-tree output";
#print "Scanning line '$_' tree $2 file $3\n";
foreach my $blob ( keys %{$BLOBS} ) {
if ( $2 eq $blob ) {
print "Found $blob in $tree:$3\n";
push @{$results->{$blob}}, $3;
}
}
push @subtree, [$2, $3] if $1 eq 'tree';
}
}
foreach my $st ( @subtree ) {
# $st->[0] is tree, $st->[1] is dirname
my $st_result = check_tree( $st->[0] );
foreach my $blob ( keys %{$st_result} ) {
foreach my $filename ( @{$st_result->{$blob}} ) {
my $path = $st->[1] . '/' . $filename;
#print "Generating subdir path $path\n";
push @{$results->{$blob}}, $path;
}
}
}
#print "Returning results for tree $tree: " . Dumper($results) . "\n\n";
return $results;
}
결과는 다음과 같습니다.
<hash prefix> <oneline log message>
path/to/file.txt
path/to/file2.txt
...
<hash prefix2> <oneline log msg...>
등등. 트리에 큰 파일을 포함하는 모든 커밋이 나열됩니다. 만약 너라면grep
밖으로 탭으로 시작하고 줄 uniq
것을, 당신은 제거 할 모든 필터링 분기 수 경로 목록이있을 것이다, 또는 당신이 뭔가 더 복잡 할 수 있습니다.
반복하자 :이 프로세스는 108,000 개의 커밋이있는 10GB 리포지토리에서 성공적으로 실행되었습니다. 많은 블롭에서 실행될 때 예상보다 시간이 오래 걸렸지 만 10 시간이 지나면 암기 비트가 작동하는지 확인해야합니다 ...
-- --all
. (리포지토리 에서 큰 파일을 완전히 삭제하는 경우 리 포드 전체 커밋을 찾는 것이 중요 합니다 ).
의 또한 git describe
내 이전의 대답에 언급, , git log
그리고 git diff
지금 "에서뿐만 아니라 혜택을 --find-object=<object-id>
명명 된 개체를 포함하는 변화에 결과를 제한하는 '옵션을 선택합니다.
Git 2.16.x / 2.17에 있습니다 (2018 년 1 분기)
참조 4d8c51a 커밋 , 5e50525 커밋 , 15af58c 커밋 , cf63051 커밋 , c1ddc46 커밋 , 929ed70 커밋 에 의해 (2018년 1월 4일) 스테판 벨러를 ( stefanbeller
) .
( Junio C gitster
Hamano 에 의해 병합 - 커밋 c0d75f0 , 2018 년 1 월 23 일)
diffcore
: 곡괭이 옵션을 추가하여 특정 얼룩을 찾습니다.때때로 사용자에게 객체의 해시가 주어지고 더 식별하기를 원합니다 (예 : verify-pack을 사용하여 가장 큰 얼룩을 찾으십시오. 그러나 이것들은 무엇입니까? 또는이 스택 오버플로 질문 " 어느 얼룩이 있습니까? ")
':'
git-describe
과 같은git describe <blob-id>
설명 을 제공하는 얼룩과 함께 작동 하도록 확장 하려는 유혹을받을 수 있습니다 .
이것은 여기 에서 구현되었습니다 . 수많은 응답 (> 110)에서 볼 수 있듯이, 이것이 옳지 않다는 것이 밝혀졌습니다.
옳게하기 어려운 부분은 올바른 '커밋'을 선택하는 것인데, 이는 블롭 또는 블롭을 제거한 블롭을 (재) 도입 한 커밋 일 수 있습니다. 얼룩은 다른 가지에 존재할 수 있습니다.Junio는이 패치가 구현하는이 문제를 해결하는 다른 접근법을 암시했습니다. 표시된 정보로 정보를 제한하기위한 다른 플래그를 기계에
가르쳐주십시오diff
.
예를 들면 다음과 같습니다.$ ./git log --oneline --find-object=v2.0.0:Makefile b2feb64 Revert the whole "ask curl-config" topic for now 47fbfde i18n: only extract comments marked with "TRANSLATORS:"
우리는와
Makefile
함께 배송 된 제품2.0
이 안으로v1.9.2-471-g47fbfded53
그리고 안으로 나타났음을 관찰합니다v2.0.0-rc1-5-gb2feb6430b
.
이러한 커밋이 v2.0.0 이전에 발생하는 이유는이 새로운 메커니즘을 사용하여 찾을 수없는 악의적 인 병합입니다.
git hash-object
또는sha1("blob " + filesize + "\0" + data)
에 의해 반환되는 것 입니다.