이 얼룩이있는 커밋은 무엇입니까? 주어지면 트리 에이 Blob이있는

Blob의 해시가 주어지면 트리 에이 Blob이있는 커밋 목록을 얻는 방법이 있습니까?



답변

다음 스크립트는 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 log --all --pretty=format:%H -- <path> | xargs -n1 -I% sh -c "git ls-tree % -- <path> | grep -q <hash> && echo %"

답변

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 하마노 – gitster556de1a 커밋 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>, 경량 태그가 유리 함에도 불구하고 블롭은 여전히 ​​설명된다 .


답변

나는 이것이 일반적으로 유용한 것이라고 생각했기 때문에 작은 펄 스크립트를 작성했다.

#!/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 : 위의 성능 의견은 첫 번째 업데이트에서 위에 링크 된 구현에 적용됩니다. 아리스토텔레스의 구현 은 광산과 비슷하게 수행됩니다. 궁금한 사람들의 의견에 대한 자세한 내용.


답변

원래 질문에 대해서는 묻지 않지만 준비 영역을 검사하여 블롭이 참조되는지 확인하는 것이 유용하다고 생각합니다. 이 작업을 수행하기 위해 원래 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 시간이 지나면 암기 비트가 작동하는지 확인해야합니다 …


답변

의 또한 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 gitsterHamano 에 의해 병합 커밋 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 이전에 발생하는 이유는이 새로운 메커니즘을 사용하여 찾을 수없는 악의적 인 병합입니다.