与えられた範囲の行にever触れたコミットのセットを取得するためにgit blame
を使用する方法を理解するのに問題があります。 これ のような同様の質問がありますが、受け入れられた回答では、それ以上のことはできません。
foo.rb
の1000行目から始まる定義があるとします。長さはわずか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行よりも多かったり少なかったりする可能性を無視します。
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 <[email protected]>
| 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 <[email protected]>
| Signed-off-by: Junio C Hamano <[email protected]>
|
| 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 <[email protected]>
| 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 <[email protected]>
| Signed-off-by: Junio C Hamano <[email protected]>
|
| 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 <[email protected]>
Date: Sat Feb 2 07:32:53 2008 +0100
Rename 'git-help--browse.sh' to 'git-web--browse.sh'.
Signed-off-by: Christian Couder <[email protected]>
Signed-off-by: Junio C Hamano <[email protected]>
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
。
私はこれがあなたが望むものだと思います:
_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 } }'
_
これにより、選択した行が編集された各コミットのリビジョン番号が出力されます。
手順は次のとおりです。
最初の部分_git rev-list HEAD -- foo.rb
_は、選択したファイルが編集されたすべてのリビジョンを出力します。
次に、これらのリビジョンのそれぞれが2番目の部分に入ります。2番目の部分では、それぞれのリビジョンを取得して_git blame -l -L 1000,+5 $rev -- foo.rb | cut -d ' ' -f 1
_に配置します。これは2つの部分からなるコマンドです。
git blame -l -L 1000,+5 $rev -- foo.rb
_は、選択した行の注釈を出力します。リビジョン番号を入力することで、先頭からではなく、そのコミットから開始してそこに移動するように指示しています。cut -d ' ' -f 1
_はblame出力の最初の列(リビジョン番号)を提供します。awk '{ if (!h[$0]) { print $0; h[$0]=1 } }'
は、隣接していない重複行を、表示された順序を維持しながら取り出します。このコマンドの詳細については、 http://jeetworks.org/node/94 を参照してください。ここに最後のステップを追加して、よりきれいな出力を得ることができます。すべてを_xargs -L 1 git log --oneline -1
_にパイプして、リビジョンのリストに対応するコミットメッセージを取得します。私はこの最後のステップを使用して奇妙な問題を抱えていましたが、出力されたいくつかのリビジョンごとに次を押し続ける必要がありました。それがなぜかはわかりません。そのため、ソリューションに含まれていません。
あなたが何をしたいのかわからないが、多分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.
追跡しようとしている変更(または変更の一部)を文字列に入れることができます。これにより、この変更に触れたコミットが一覧表示されます。
私はこのパズルが好きでした、それはその微妙さを持っています。このファイルのソース、たとえばinit foo.rb 1000,1005
と指示に従います。完了したら、ファイル@changes
にはトポロジの順序で正しいコミットのリストがあり、@blames
には、それぞれから実際の非難出力があります。
これは 上記の受け入れられたソリューション よりも劇的に複雑です。それは時々より有用で、再現が難しい出力を生成し、コーディングするのは楽しかったです。
履歴をさかのぼって行番号の範囲を自動的に追跡しようとする場合の問題は、変更ハンクが行番号の範囲の境界を超える場合、新しい範囲の境界がそのハンクのどこにあるべきかを自動的に判断できず、大きな追加の大きな範囲を含めて、(時には多くの)無関係な変更を蓄積するか、手動モードにしてそれが正しいことを確認します(もちろん、ここに戻ることができます)。または、極端な損失を受け入れることもあります。
出力を正確にしたい場合は、 `/ ^ type function(/、/ ^}/'のような信頼できる正規表現の範囲で上記の答えを使用するか、これを使用してください。時間内に。
余分な複雑さと引き換えに、トポロジーシーケンスでヒットリストを生成し、少なくとも(かなりうまく)各ステップで痛みを改善しようとします。たとえば、冗長な非難を実行することはなく、更新範囲によって行番号の調整が容易になります。そしてもちろん、ハンクを個別に目視しなければならなかったという信頼性もあります... :-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
}
ここに投稿された回答を参照してください 特定のファイルのすべてのコミットをリスト 。それはまさにあなたが必要とするものです。