Komit mana yang memiliki gumpalan ini?

149

Mengingat hash gumpalan, adakah cara untuk mendapatkan daftar commit yang memiliki gumpalan ini di pohon mereka?

Hanya baca
sumber
2
"Hash of a blob" adalah yang dikembalikan oleh git hash-objectatau sha1("blob " + filesize + "\0" + data), dan bukan hanya sha1sum dari isi blob.
Ivan Hamilton
1
Awalnya saya pikir pertanyaan ini cocok dengan pertanyaan saya, tetapi sepertinya tidak. Saya ingin tahu satu komit yang pertama diperkenalkan gumpalan ini ke repositori.
Jesse Glick
Jika Anda tahu filepath, Anda dapat menggunakan git log --follow filepath(dan menggunakan ini untuk mempercepat solusi Aristoteles, jika Anda mau).
Zaz
Protip ™: Letakkan salah satu script Belew di ~/.bindan nama itu git-find-object. Anda kemudian dapat menggunakannya dengan git find-object.
Zaz
1
Catatan: Dengan Git 2.16 (Q1 2018), Anda dapat mempertimbangkan git describe <hash>: Lihat jawaban saya di bawah ini .
VonC

Jawaban:

107

Kedua skrip berikut mengambil SHA1 gumpalan sebagai argumen pertama, dan setelahnya, opsional, argumen apa pun yang git logakan mengerti. Misalnya --alluntuk mencari di semua cabang, bukan hanya yang saat ini, atau -guntuk mencari di reflog, atau apa pun yang Anda suka.

Ini dia sebagai skrip shell - pendek dan manis, tapi lambat:

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

Dan versi yang dioptimalkan dalam Perl, masih cukup singkat tetapi jauh lebih cepat:

#!/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 );
}
Aristoteles Pagaltzis
sumber
8
FYI Anda harus menggunakan SHA penuh gumpalan. Awalan, meskipun unik, tidak akan berfungsi. Untuk mendapatkan SHA lengkap dari awalan, Anda dapat menggunakangit rev-parse --verify $theprefix
John Douthat
1
Terima kasih @JohnDouthat untuk komentar ini. Inilah cara memasukkannya ke dalam skrip di atas (maaf atas inlining dalam komentar): 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";
Ingo Karkat
Mungkin ada bug di skrip shell atas. Loop sementara hanya dieksekusi jika ada lebih banyak baris untuk dibaca, dan untuk alasan apa pun git log tidak meletakkan crlf akhir di akhir. Saya harus menambahkan umpan baris dan mengabaikan baris kosong. 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
Mixologic
7
Ini hanya menemukan komitmen pada cabang saat ini kecuali Anda lulus --allsebagai argumen tambahan. (Menemukan semua komit lebar adalah penting dalam kasus-kasus seperti menghapus file besar dari sejarah repo ).
peterflynn
1
Tip: meneruskan flag -g ke skrip shell (setelah objek ID) untuk memeriksa reflog.
Bram Schoenmakers
24

Sayangnya skrip agak lambat bagi saya, jadi saya harus mengoptimalkan sedikit. Untungnya saya tidak hanya memiliki hash tetapi juga path file.

git log --all --pretty=format:%H -- <path> | xargs -n1 -I% sh -c "git ls-tree % -- <path> | grep -q <hash> && echo %"
aragaer
sumber
1
Jawaban yang sangat bagus karena sangat sederhana. Hanya dengan membuat asumsi yang masuk akal bahwa jalan itu diketahui. Namun, orang harus tahu bahwa itu mengembalikan komit di mana jalan diubah ke hash yang diberikan.
Unapiedra
1
Jika seseorang menginginkan komit terbaru yang berisi <hash>pada yang diberikan <path>, maka menghapus <path>argumen dari git logakan bekerja. Hasil yang dikembalikan pertama adalah komit yang diinginkan.
Unapiedra
10

Mengingat hash gumpalan, adakah cara untuk mendapatkan daftar commit yang memiliki gumpalan ini di pohon mereka?

Dengan Git 2.16 (Q1 2018), git describeakan menjadi solusi yang baik, karena diajarkan untuk menggali lebih dalam pohon untuk menemukan <commit-ish>:<path>yang merujuk pada objek gumpalan yang diberikan.

Lihat komit 644eb60 , komit 4dbc59a , komit cdaed0c , komit c87b653 , komit ce5b6f9 (16 Nov 2017), dan komit 91904f5 , komit 2deda00 (02 Nov 2017) oleh Stefan Beller ( stefanbeller) .
(Digabung oleh Junio ​​C Hamano - gitster- dalam komit 556de1a , 28 Des 2017)

builtin/describe.c: menggambarkan gumpalan

Kadang-kadang pengguna diberi hash objek dan mereka ingin mengidentifikasinya lebih lanjut (mis .: Gunakan verify-packuntuk menemukan gumpalan terbesar, tetapi apakah ini? Atau pertanyaan SO ini " Komit mana yang memiliki gumpalan ini? ")

Saat menjelaskan komitmen, kami mencoba menambatkannya ke tag atau referensi, karena ini secara konseptual lebih tinggi daripada komit. Dan jika tidak ada referensi atau tanda yang sama persis, kami kurang beruntung.
Jadi kami menggunakan heuristik untuk membuat nama untuk komit. Nama-nama ini ambigu, mungkin ada tag atau referensi yang berbeda untuk disematkan, dan mungkin ada jalur berbeda di DAG untuk melakukan perjalanan untuk sampai di komit secara tepat.

Saat mendeskripsikan gumpalan, kami juga ingin menggambarkan gumpalan dari lapisan yang lebih tinggi, yang merupakan tuple (commit, deep/path)karena objek pohon yang terlibat agak tidak menarik.
Gumpalan yang sama dapat direferensikan oleh banyak komit, jadi bagaimana kita memutuskan komit yang akan digunakan?

Tambalan ini menerapkan pendekatan yang agak naif dalam hal ini: Karena tidak ada titik balik dari gumpalan untuk melakukan di mana gumpalan terjadi, kami akan mulai berjalan dari setiap tips yang tersedia, daftar gumpalan dalam urutan komit dan setelah kami menemukan gumpalan, kami akan mengambil komit pertama yang terdaftar gumpalan .

Sebagai contoh:

git describe --tags v0.99:Makefile
conversion-901-g7672db20c2:Makefile

memberitahu kita Makefileseperti saat v0.99diperkenalkan di commit 7672db2 .

Berjalan dilakukan dalam urutan terbalik untuk menunjukkan pengenalan gumpalan daripada kejadian terakhirnya.

Itu berarti git describehalaman manual menambahkan ke tujuan perintah ini:

Alih-alih hanya menggambarkan komit menggunakan tag terbaru yang dapat dijangkau darinya, git describesebenarnya akan memberikan objek nama yang dapat dibaca manusia berdasarkan referensi yang tersedia saat digunakan sebagai git describe <blob>.

Jika objek yang diberikan mengacu pada gumpalan, itu akan digambarkan sebagai <commit-ish>:<path>, sehingga gumpalan dapat ditemukan di <path>dalam <commit-ish>, yang itu sendiri menggambarkan pertama komit di mana gumpalan ini terjadi dalam revisi berjalan terbalik dari HEAD.

Tapi:

BUG

Objek pohon serta objek tag yang tidak menunjuk komit, tidak dapat dijelaskan .
Saat mendeskripsikan gumpalan, tag ringan yang menunjuk pada gumpalan diabaikan, tetapi gumpalan masih digambarkan seolah <committ-ish>:<path>-olah tag ringan menguntungkan.

VONC
sumber
1
Baik digunakan bersamaan 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, yang menghasilkan 20 gumpalan terbesar. Kemudian Anda dapat mengirimkan blob ID dari output di atas ke git describe. Bekerja sebagai pesona! Terima kasih!
Alexander Pogrebnyak
7

Saya pikir ini akan menjadi hal yang bermanfaat secara umum, jadi saya menulis sedikit skrip perl untuk melakukannya:

#!/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);
}

Saya akan meletakkan ini di github ketika saya pulang malam ini.

Pembaruan: Sepertinya seseorang sudah melakukan ini . Yang satu menggunakan ide umum yang sama tetapi detailnya berbeda dan implementasinya jauh lebih pendek. Saya tidak tahu mana yang lebih cepat tetapi kinerja mungkin bukan masalah di sini!

Pembaruan 2: Untuk apa nilainya, implementasi saya adalah urutan besarnya lebih cepat, terutama untuk repositori besar. Itu git ls-tree -rsangat menyakitkan.

Pembaruan 3: Saya harus mencatat bahwa komentar kinerja saya di atas berlaku untuk implementasi yang saya tautkan di atas dalam Pembaruan pertama. Implementasi Aristoteles berkinerja sebanding dengan saya. Lebih detail dalam komentar untuk mereka yang penasaran.

Greg Hewgill
sumber
Hmm, bagaimana bisa yang jauh lebih cepat? Anda tetap berjalan di pohon, bukan? Pekerjaan apa yang dilakukan git-ls-tree yang Anda hindari? (NB .: grep akan memberikan jaminan pada pertandingan pertama, SIGPIPE 'the git-ls-tree.) Ketika saya mencobanya, saya harus Ctrl-C skrip Anda setelah 30 detik; punyaku selesai pada 4.
Aristoteles Pagaltzis
1
Script saya membuat cache hasil subtree di hash% tree, jadi tidak harus terus mencari subtree yang belum berubah.
Greg Hewgill
Sebenarnya, saya mencoba implementasi yang saya temukan di github yang saya tautkan. Dalam beberapa kasus, Anda lebih cepat, tetapi sangat tergantung pada apakah file yang Anda cari ada di awal atau akhir daftar ls-tree. Repositori saya memiliki 9574 file di dalamnya sekarang.
Greg Hewgill
Itu juga terjadi pada saya bahwa beberapa sejarah proyek nonlinear mungkin menyebabkan skrip saya melakukan lebih banyak pekerjaan daripada yang diperlukan (ini dapat diperbaiki). Ini mungkin mengapa butuh waktu lama untuk menjalankan untuk Anda. Respositori saya adalah mirror git-svn dari repositori Subversion, jadi ini linear dengan baik.
Greg Hewgill
Alih-alih mengurai file kucing untuk mendapatkan pohon hanya lakukangit rev-parse $commit^{}
jthill
6

Meskipun pertanyaan awal tidak menanyakannya, saya pikir akan bermanfaat juga untuk memeriksa area pementasan untuk melihat apakah gumpalan direferensikan. Saya memodifikasi skrip bash asli untuk melakukan ini dan menemukan apa yang mereferensikan gumpalan rusak di repositori saya:

#!/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
Mario
sumber
3
Saya hanya ingin memberikan kredit karena sudah jatuh tempo: terima kasih RAM korupsi karena telah membuat saya BSOD dan memaksa saya untuk memperbaiki repo git saya.
Mario
4

Jadi ... Saya perlu menemukan semua file melebihi batas yang diberikan dalam repo lebih dari 8GB dalam ukuran, dengan lebih dari 108.000 revisi. Saya mengadaptasi skrip perl Aristoteles bersama dengan skrip ruby ​​yang saya tulis untuk mencapai solusi lengkap ini.

Pertama, git gc- lakukan ini untuk memastikan semua objek ada dalam paket - kami tidak memindai objek tidak dalam file paket.

Selanjutnya Jalankan skrip ini untuk mencari semua gumpalan di atas byte CUTOFF_SIZE. Capture output ke file seperti "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

Selanjutnya, edit file untuk menghapus gumpalan yang tidak Anda tunggu dan INPUT_THREAD bit di atas. setelah Anda hanya memiliki baris untuk sha1s yang ingin Anda temukan, jalankan skrip berikut seperti ini:

cat edited-large-files.log | cut -d' ' -f4 | xargs git-find-blob | tee large-file-paths.log

Di mana git-find-blobskrip di bawah ini.

#!/usr/bin/perl

# taken from: http://stackoverflow.com/questions/223678/which-commit-has-this-blob
# and modified by Carl Myers <[email protected]> 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;
}

Outputnya akan terlihat seperti ini:

<hash prefix> <oneline log message>
    path/to/file.txt
    path/to/file2.txt
    ...
<hash prefix2> <oneline log msg...>

Dan seterusnya. Setiap komit yang berisi file besar di pohonnya akan terdaftar. jika Anda grepkeluar dari garis yang dimulai dengan tab, dan uniqitu, Anda akan memiliki daftar semua jalur yang bisa Anda filter-cabang untuk dihapus, atau Anda bisa melakukan sesuatu yang lebih rumit.

Biarkan saya ulangi: proses ini berjalan dengan sukses, pada repo 10GB dengan 108.000 komit. Butuh waktu lebih lama daripada yang saya perkirakan saat menjalankan sejumlah besar gumpalan, lebih dari 10 jam, saya harus melihat apakah bit menghafal bekerja ...

cmyers
sumber
1
Seperti jawaban Aristoteles di atas, ini hanya menemukan komit pada cabang saat ini kecuali jika Anda melewati argumen tambahan: -- --all. (Menemukan semua komit repo-lebar adalah penting dalam kasus-kasus seperti benar - benar menghapus file besar dari sejarah repo ).
peterflynn
4

Selain itu git describe, yang saya sebutkan dalam jawaban saya sebelumnya , git logdan git diffsekarang mendapat manfaat juga dari opsi " --find-object=<object-id>" untuk membatasi temuan hanya pada perubahan yang melibatkan objek bernama.
Itu ada di Git 2.16.x / 2.17 (Q1 2018)

Lihat komit 4d8c51a , komit 5e50525 , komit 15af58c , komit cf63051 , komit c1ddc46 , komit 929ed70 (04 Jan 2018) oleh Stefan Beller ( stefanbeller) .
(Digabung oleh Junio ​​C Hamano - gitster- di commit c0d75f0 , 23 Jan 2018)

diffcore: tambahkan opsi beliung untuk menemukan gumpalan tertentu

Kadang-kadang pengguna diberi hash objek dan mereka ingin mengidentifikasinya lebih lanjut (mis .: Gunakan verifikasi-paket untuk menemukan gumpalan terbesar, tetapi apakah ini? Atau pertanyaan Stack Overflow ini " Komit mana yang memiliki gumpalan ini? ")

Seseorang mungkin tergoda untuk memperluas git-describejuga bekerja dengan gumpalan, sehingga git describe <blob-id>memberikan deskripsi sebagai ':'.
Ini diterapkan di sini ; seperti yang terlihat oleh banyaknya respons (> 110), ternyata ini sulit untuk dilakukan dengan benar.
Bagian yang sulit untuk mendapatkan yang benar adalah memilih 'commit-ish' yang benar karena itu bisa menjadi komit yang (kembali) memperkenalkan gumpalan atau gumpalan yang menghilangkan gumpalan; gumpalan itu bisa ada di cabang yang berbeda.

Junio ​​mengisyaratkan pendekatan berbeda untuk menyelesaikan masalah ini, yang diterapkan patch ini.
Ajari diffmesin bendera lain untuk membatasi informasi pada apa yang ditampilkan.
Sebagai contoh:

$ ./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:"

kami mengamati bahwa Makefileketika dikirim dengan 2.0muncul di v1.9.2-471-g47fbfded53dan di v2.0.0-rc1-5-gb2feb6430b.
Alasan mengapa kedua komit ini terjadi sebelum v2.0.0 adalah gabungan jahat yang tidak ditemukan menggunakan mekanisme baru ini.

VONC
sumber