68

特定の範囲の行に接触しgit blameた一連のコミットを取得するための使用方法を理解するのに苦労していますこのような同様の質問がありますが、受け入れられた答えは私にそれ以上のことをもたらしません。

の1000行目から始まる定義があるとしましょうfoo.rb。たった5行の長さですが、これらの行を変更したコミットの数は膨大です。私が行った場合

git blame foo.rb -L 1000,+5

私はこれらの行を変更した(多くても)5つの異なるコミットへの参照を取得しますが、「それらの背後にある」コミットにも興味があります。

同様に、

git rev-list HEAD -- foo.rb | xargs git log --oneline

ほぼ私が望むものですが、行範囲を指定することはできませんgit rev-list

git blameこれらの5行に触れたことのあるコミットのリストを取得するためにフラグを渡すことはできますか、またはそのような情報を抽出するスクリプトを作成する最も簡単な方法は何ですか?定義がかつて5行より多いまたは少ない可能性を今のところ無視しましょう。

4

6 に答える 6

71

Git 1.8.4以降git log、さまざま-Lなラインの進化を確認する必要があります。

たとえば、git blameの出力を見てみましょう。

((aa27064...))[mlm@macbook:~/w/mlm/git]
$ git blame -L150,+11 -- git-web--browse.sh
a180055a git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:36 +0100 150)            die "The browser $browser is not
a180055a git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:36 +0100 151)    fi
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 152) fi
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 153) 
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 154) case "$browser" in
81f42f11 git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:38 +0100 155) firefox|iceweasel|seamonkey|iceape)
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 156)    # Check version because firefox < 2.0 do
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 157)    vers=$(expr "$($browser_path -version)" 
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 158)    NEWTAB='-new-tab'
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 159)    test "$vers" -lt 2 && NEWTAB=''
a0685a4f git-web--browse.sh (Dmitry Potapov   2008-02-09 23:22:22 -0800 160)    "$browser_path" $NEWTAB "$@" &

そして、あなたは現在155行目であるものの歴史を知りたいです。

それで:

((aa27064...))[mlm@macbook:~/w/mlm/git]
$ git log --topo-order --graph -u -L 155,155:git-web--browse.sh
* commit 81f42f11496b9117273939c98d270af273c8a463
| Author: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
| Date:   Fri Dec 3 17:47:38 2010 +0100
| 
|     web--browse: support opera, seamonkey and elinks
|     
|     The list of supported browsers is also updated in the documentation.
|     
|     Signed-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
|     Signed-off-by: Junio C Hamano <gitster@pobox.com>
| 
| diff --git a/git-web--browse.sh b/git-web--browse.sh
| --- a/git-web--browse.sh
| +++ b/git-web--browse.sh
| @@ -143,1 +143,1 @@
| -firefox|iceweasel)
| +firefox|iceweasel|seamonkey|iceape)
|  
* commit a180055a47c6793eaaba6289f623cff32644215b
| Author: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
| Date:   Fri Dec 3 17:47:36 2010 +0100
| 
|     web--browse: coding style
|     
|     Retab and deindent choices in case statements.
|     
|     Signed-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
|     Signed-off-by: Junio C Hamano <gitster@pobox.com>
| 
| diff --git a/git-web--browse.sh b/git-web--browse.sh
| --- a/git-web--browse.sh
| +++ b/git-web--browse.sh
| @@ -142,1 +142,1 @@
| -    firefox|iceweasel)
| +firefox|iceweasel)
|  
* commit 5884f1fe96b33d9666a78e660042b1e3e5f9f4d9
  Author: Christian Couder <chriscool@tuxfamily.org>
  Date:   Sat Feb 2 07:32:53 2008 +0100

      Rename 'git-help--browse.sh' to 'git-web--browse.sh'.

      Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
      Signed-off-by: Junio C Hamano <gitster@pobox.com>

  diff --git a/git-web--browse.sh b/git-web--browse.sh
  --- /dev/null
  +++ b/git-web--browse.sh
  @@ -0,0 +127,1 @@
  +    firefox|iceweasel)

この機能を頻繁に使用する場合は、gitエイリアスが役立つ場合があります。~/.gitconfigそれを行うには、あなたの:を入れてください

[alias]
    # Follow evolution of certain lines in a file
    # arg1=file, arg2=first line, arg3=last line or blank for just the first line
    follow = "!sh -c 'git log --topo-order -u -L $2,${3:-$2}:"$1"'" -

そして今、あなたはただすることができますgit follow git-web--browse.sh 155

于 2013-11-03T20:03:03.153 に答える
23

私はこれがあなたが望むものだと思います:

git rev-list HEAD -- foo.rb | ( 
    while read rev; do
        git blame -l -L 1000,+5 $rev -- foo.rb | cut -d ' ' -f 1
    done;
) | awk '{ if (!h[$0]) { print $0; h[$0]=1 } }'

これにより、選択した行が編集された各コミットのリビジョン番号が出力されます。

手順は次のとおりです。

  1. 最初の部分git rev-list HEAD -- foo.rbは、選択したファイルが編集されたすべてのリビジョンを出力します。

  2. 次に、これらの各リビジョンは2番目の部分に入り、それぞれを取得してに入れgit blame -l -L 1000,+5 $rev -- foo.rb | cut -d ' ' -f 1ます。これは2つの部分からなるコマンドです。

    1. git blame -l -L 1000,+5 $rev -- foo.rb選択した行の非難を出力します。リビジョン番号を入力することで、先頭から開始するのではなく、そのコミットから開始してそこから実行するように指示しています。
    2. blameは不要な情報を大量に出力するため、blame出力cut -d ' ' -f 1の最初の列(リビジョン番号)を示します。
  3. awk '{ if (!h[$0]) { print $0; h[$0]=1 } }'表示された順序を維持しながら、隣接していない重複行を削除します。このコマンドの詳細については、http://jeetworks.org/node/94を参照してください。

ここに最後のステップを追加して、よりきれいな出力を得ることができます。すべてをパイプしてxargs -L 1 git log --oneline -1、リビジョンのリストに対応するコミットメッセージを取得します。この最後のステップを使用すると、出力されたいくつかのリビジョンごとに次のボタンを押し続ける必要があるという奇妙な問題が発生しました。それがなぜだったのかわかりません。そのため、ソリューションに含めませんでした。

于 2013-01-13T04:12:52.597 に答える
12

何をしたいのかわからないが、おそらくgit log-Sがあなたのためにトリックを行うことができる:

-S<string>
    Look for differences that introduce or remove an instance of <string>. 
    Note that this is different than the string simply appearing
    in diff output; see the pickaxe entry in gitdiffcore(7) for more
    details.

フォローしようとしている変更(または変更の一部)を文字列に入力すると、この変更にこれまでに触れたコミットが一覧表示されます。

于 2013-01-07T14:37:31.270 に答える
1

私はこのパズルが好きでした、それはその微妙さを持っています。このファイルinit foo.rb 1000,1005を入手し、指示に従ってください。完了すると、ファイル@changesにはトポロジカル順にコミットの正しいリストが含まれ@blames、それぞれからの実際の非難出力が含まれます。

これは、上記で受け入れられているソリューションよりも劇的に複雑です。時にはもっと便利で再現が難しい出力を生成し、コーディングするのは楽しかったです。

履歴をさかのぼって行番号の範囲を自動的に追跡しようとする場合の問題は、変更ハンクが行番号の範囲の境界を越える場合、そのハンクのどこに新しい範囲の境界があるかを自動的に判断できないことです。大きな追加のために大きな範囲を含めて、無関係な変更を(時にはたくさん)蓄積するか、手動モードにドロップしてそれが正しいことを確認するか(もちろんここに戻ってきます)、時には極端な損失を受け入れます。

出力を正確にしたい場合は、 `/ ^ type function(/、/ ^} /'などの信頼できる正規表現範囲で上記の回答を使用するか、これを使用します。これは実際にはそれほど悪くはありませんが、1ステップごとに数秒かかります。時間内に。

余分な複雑さと引き換えに、トポロジシーケンスでヒットリストを生成し、少なくとも(かなり成功して)各ステップで痛みを改善しようとします。たとえば、冗長な非難を実行することはなく、update-rangesを使用すると行番号の調整が容易になります。そしてもちろん、ハンクを個別に目で確認しなければならなかったという信頼性があります... :-P

これをフルオートで実行するには、次のように言います{ init foo.rb /^class foo/,/^end/; auto; } 2>&-

 ### functions here create random @-prefix files in the current directory ###
#
# git blame history for a range, finding every change to that range
# throughout the available history.  It's somewhat, ahh, "intended for
# customization", is that enough of a warning?  It works as advertised
# but drops @-prefix temporary files in your current directory and
# defines new commands
#
# Source this file in a subshell, it defines functions for your use.
# If you have @-prefix files you care about, change all @ in this file
# to something you don't have and source it again.
#
#    init path/to/file [<start>,<end>]  # range optional
#    update-ranges           # check range boundaries for the next step
#    cycle [<start>,<end>]   # range unchanged if not supplied
#    prettyblame             # pretty colors, 
#       blue="child commit doesn't have this line"
#       green="parent commit doesn't have this line"
#           brown=both
#    shhh # silence the pre-cycle blurb
#
# For regex ranges, you can _usually_ source this file and say `init
# path/to/file /startpattern/,/endpattern/` and then cycle until it says 0
# commits remain in the checklist
#
# for line-number ranges, or regex ranges you think might be unworthy, you
# need to check and possibly update the range before each cycle.  File
# @next is the next blame start-point revision text; and command
# update-ranges will bring up vim with the current range V-selected.  If
# that looks good, `@M` is set up to quit even while selecting, so `@M` and
# cycle.  If it doesn't look good, 'o' and the arrow keys will make getting
# good line numbers easy, or you can find better regex's.  Either way, `@M`
# out and say `cycle <start>,<end>` to update the ranges.

init () { 
    file=$1;
    range="$2"
    rm -f @changes
    git rev-list --topo-order HEAD -- "$file" \
    | tee @checklist \
    | cat -n | sort -k2 > @sequence
    git blame "-ln${range:+L$range}" -- "$file" > @latest || echo >@checklist
    check-cycle
    cp @latest @blames
}

update-latest-checklist() {
    # update $latest with the latest sha that actually touched our range,
    # and delete that and everything later than that from the checklist.
    latest=$(
        sed s,^^,, @latest \
        | sort -uk1,1 \
        | join -1 2 -o1.1,1.2 @sequence - \
        | sort -unk1,1 \
        | sed 1q \
        | cut -d" " -f2
    )
    sed -i 1,/^$latest/d @checklist
}
shhh () { shhh=1; }

check-cycle () {
    update-latest-checklist
    sed -n q1 @checklist || git log $latest~..$latest --format=%H\ %s | tee -a @changes
    next=`sed 1q @checklist`
    git cat-file -p `git rev-parse $next:"$file"` > @next
    test -z "$shh$shhh$shhhh" && {
        echo "A blame from the (next-)most recent alteration (id `git rev-parse --short $latest`) to '$file'"
        echo is in file @latest, save its contents where you like
        echo 
        echo you will need to look in file @next to determine the correct next range,
        echo and say '`cycle its-start-line,its-end-line`' to continue
        echo the "update-ranges" function starts you out with the range selected
    } >&2
    ncommits=`wc -l @checklist | cut -d\  -f1`
    echo  $ncommits commits remain in the checklist >&2
    return $((ncommits==0))
}

update-ranges () {
    start="${range%,*}"
    end="${range#*,}"
    case "$start" in
    */*)    startcmd="1G$start"$'\n' ;;
    *)      startcmd="${start}G" ;;
    esac
    case "$end" in
    */*)    endcmd="$end"$'\n' ;;
    [0-9]*) endcmd="${end}G" ;;
    +[0-9]*) endcmd="${end}j" ;;
    *) endcmd="echohl Search|echo "can\'t" get to '${end}'\"|echohl None" ;;
    esac
    vim -c 'set buftype=nofile|let @m=":|q'$'\n"' -c "norm!${startcmd}V${endcmd}z.o" @next
}

cycle () {
    sed -n q1 @checklist && { echo "No more commits to check"; return 1; }
    range="${1:-$range}"
    git blame "-ln${range:+L$range}" $next -- "$file" >@latest || echo >@checklist
    echo >>@blames
    cat @latest >>@blames
    check-cycle
}

auto () {
    while cycle; do true; done
}

prettyblames () {
cat >@pretty <<-\EOD
BEGIN {
    RS=""
    colors[0]="\033[0;30m"
    colors[1]="\033[0;34m"
    colors[2]="\033[0;32m"
    colors[3]="\033[0;33m"
    getline commits < "@changes"
    split(commits,commit,/\n/)
}
NR!=1 { print "" }
{
    thiscommit=gensub(/ .*/,"",1,commit[NR])
    printf "%s\n","\033[0;31m"commit[NR]"\033[0m"
    split($0,line,/\n/)
    for ( n=1; n<=length(line); ++n ) {
        color=0
        split(line[n],key,/[1-9][0-9]*)/)
        if ( NR!=1 && !seen[key[1]] ) color+=1
        seen[key[1]]=1;
        linecommit = gensub(/ .*/,"",1,line[n])
        if (linecommit==thiscommit) color+=2
        printf "%s%s\033[0m\n",colors[color],line[n]
    }
}
EOD
awk -f @pretty @blames | less -R
}
于 2013-01-13T08:58:00.990 に答える
1

ここに投稿された回答を参照してください。特定のファイルのすべてのコミットを一覧表示します。それはまさにあなたが必要とするものです。

于 2015-08-13T10:00:07.850 に答える
0

いくつかの考え..

これはこの投稿に似ているように聞こえますが、次のようなものに近づく可能性があります。

git blame -L '/variable_name *= */',+1

一致する定義を知っている限り(正規表現の場合)。

andの使用についてのスレッドディスカッションがここにあります(これは明らかにこれを処理する可能性があります)。私はまだこれを試したことがないので、確認できません(後で試してみます)。tiggit gui

于 2013-01-12T05:33:43.573 に答える