LinuxカーネルHack: Gitを活用して特定のコミッターのコードをまとめて読む

どうしたらもっと効率よくカーネルのコードを読めるか?という問題意識を持ちながらgit logのmanを読んでいたら、使えそうなオプションをいくつか発見。それらのオプションを、普段、カーネルのコードを読むにあたって、いくつかのユースケースに当てはめて活用してみる。

% man git-log
[...]
OPTIONS
[...]
       --stat[=width[,name-width]]
           Generate a diffstat. You can override the default output width for
           80-column terminal by "--stat=width". The width of the filename part
           can be controlled by giving another width to it separated by a comma.
[...]
       --name-only
           Show only names of changed files.
[...]
       --color
           Show colored diff.
[...]
       --full-diff
           Without this flag, "git log -p <paths>..." shows commits that touch
           the specified paths, and diffs about the same specified paths. With
           this, the full diff is shown for commits that touch the specified
           paths; this means that "<paths>..." limits only commits, and doesn't
           limit diff for those commits.
[...]
   Commit Formatting
       --pretty[=<format>]
           Pretty-print the contents of the commit logs in a given format, where
           <format> can be one of oneline, short, medium, full, fuller, email,
           raw and format:<string>. When omitted, the format defaults to medium.
[...]
   Commit Limiting
       Besides specifying a range of commits that should be listed using the
       special notations explained in the description, additional commit limiting
       may be applied.

       -n number, --max-count=number
           Limit the number of commits output.
[...]
       --author=pattern, --committer=pattern
           Limit the commits output to ones with author/committer header lines
           that match the specified pattern (regular expression).
[...]

特定のコミッターのコードをまとめて読みたい場合

例えば、KOSAKIさんのコミットをまとめて読みたい場合。以下のようにすると読みやすい。git logにパスを指定していないので、特定のコンポーネントに限定せず、すべてを対象にしてコミットが表示される。また、パスを指定をしない場合は、--full-diffオプションは指定しなくても、それぞれのコミットで複数のファイルを変更していても、ちゃんと表示される。

% git log -p --author=KOSAKI --stat --color
[...]
commit be71cf2202971e50ce4953d473649c724799eb8a
Author: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Date:   Thu Aug 19 14:13:38 2010 -0700

    oom: fix NULL pointer dereference

    Commit b940fd7035 ("oom: remove unnecessary code and cleanup") added an
    unnecessary NULL pointer dereference.  remove it.

    Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
    Reviewed-by: Minchan Kim <minchan.kim@gmail.com>
    Acked-by: David Rientjes <rientjes@google.com>
    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---
 mm/oom_kill.c |    5 ++---
 1 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index 5014e50..17d48a6 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -401,10 +401,9 @@ static void dump_header(struct task_struct *p, gfp_t gfp_mask, int order,
 static int oom_kill_task(struct task_struct *p, struct mem_cgroup *mem)
 {
        p = find_lock_task_mm(p);
-       if (!p) {
-               task_unlock(p);
+       if (!p)
                return 1;
-       }
+
        pr_err("Killed process %d (%s) total-vm:%lukB, anon-rss:%lukB, file-rss:%lukB\n",
                task_pid_nr(p), p->comm, K(p->mm->total_vm),
                K(get_mm_counter(p->mm, MM_ANONPAGES)),


commit 13d7e3a2dba6a79589ed34dc0b9114d7b5ff9eab
Author: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Date:   Tue Aug 10 18:03:06 2010 -0700

    memcg: convert to use zone_to_nid() from bare zone->zone_pgdat->node_id

    We have zone_to_nid().  this patch convert all existing users of
    zone->zone_pgdat->node_id.

    Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
    Acked-by: Balbir Singh <balbir@linux.vnet.ibm.com>
    Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
    Cc: Mel Gorman <mel@csn.ul.ie>
    Cc: Nishimura Daisuke <d-nishimura@mtf.biglobe.ne.jp>
    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---
 mm/memcontrol.c |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 5e95996..3eed583 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c

興味のあるコンポーネントに対する、特定のコミッターのコードをまとめて読みたい場合

例えば、net/ipv4/icmp.cに対して、YOSHIFUJIさんのコミットをまとめて読みたい場合。以下のようにすると読みやすい。この場合、--full-diffを付けることで、それぞれのコミットで複数のファイル(別のコンポーネントも含む)が変更された場合も、まとめてdiffにしてくれる。

% git log -p --author=YOSHIFUJI --stat --color --full-diff net/ipv4/icmp.c
commit f25c3d613b12b4b6219d03e9930cac5f59541468
Author: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date:   Mon Apr 21 02:34:08 2008 -0700

    [IPV4]: Convert do_gettimeofday() to getnstimeofday().

    What do_gettimeofday() does is to call getnstimeofday() and
    to convert the result from timespec{} to timeval{}.
    After that, these callers convert the result again to msec.
    Use getnstimeofday() and convert the units at once.

    Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
    Signed-off-by: David S. Miller <davem@davemloft.net>
---
 net/ipv4/icmp.c       |    8 ++++----
 net/ipv4/ip_options.c |   12 ++++++------
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index fee171e..c67d00e 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -847,7 +847,7 @@ static void icmp_echo(struct sk_buff *skb)
  */
 static void icmp_timestamp(struct sk_buff *skb)
 {
-       struct timeval tv;
+       struct timespec tv;
        struct icmp_bxm icmp_param;
        /*
         *      Too short.
@@ -858,9 +858,9 @@ static void icmp_timestamp(struct sk_buff *skb)

[...]

特定のコミッターの得意領域を探りたい場合

興味のあるコミッターが、これまでどのファイルに対して変更をかけてきたのかを一覧したい場合。以下を実行して眺めると傾向が見えてくる。

% git log --author=YOSHIFUJI --pretty=oneline --stat --color | less -MNR
      1 1fafc7a9353ef68e1b8d4bb130cb6402cf7dfd5a bridge br_multicast: Ensure to initialize BR_INPUT_SKB_CB(skb)->mrouters_only.
      2  net/bridge/br_multicast.c |    9 +++------
      3  1 files changed, 3 insertions(+), 6 deletions(-)
      4 4eb8b9031a0314539605733597b1e30222d4da70 bridge br_multicast: Ensure to initialize BR_INPUT_SKB_CB(skb)->mrouters_only.
      5  net/bridge/br_multicast.c |    6 +++---
      6  1 files changed, 3 insertions(+), 3 deletions(-)
      7 08b202b6726459626c73ecfa08fcdc8c3efc76c2 bridge br_multicast: IPv6 MLD support.
      8  net/bridge/Kconfig        |    6 +-
      9  net/bridge/br_multicast.c |  424 ++++++++++++++++++++++++++++++++++++++++++++-
     10  net/bridge/br_private.h   |    3 +
     11  3 files changed, 429 insertions(+), 4 deletions(-)
     12 8ef2a9a59854994bace13b5c4f7edc2c8d4d124e bridge br_multicast: Make functions less ipv4 dependent.
     13  net/bridge/br_multicast.c |  197 ++++++++++++++++++++++++++++++++-------------
     14  net/bridge/br_private.h   |   12 +++-
[...]

これまでの変更ファイルをソートして一覧で見ると、さらに大局的に傾向をつかみやすい。--name-onlyオプションで、純粋に変更ファイルを出力し、--prettyオプションでは、汚いけど、ファイル名以外の情報は出さないようにしている。

% git log --author="YOSHIFUJI" --name-only --pretty=format:"" | sort | uniq | less -MN
      1
      2 CREDITS
      3 Documentation/networking/ip-sysctl.txt
      4 arch/ia64/hp/sim/simeth.c
      5 drivers/atm/eni.c
      6 drivers/atm/eni.h
      7 drivers/block/aoe/aoenet.c
      8 drivers/net/arm/ether3.c
      9 drivers/net/bonding/bond_3ad.c
     10 drivers/net/bonding/bond_alb.c
     11 drivers/net/bonding/bond_main.c
     12 drivers/net/cxgb3/l2t.c
[...]

コミットハッシュから、そのコミットだけを集中して読みたい場合

これはおまけ。git logにコミットハッシュを指定して読む場合、-nオプションを指定しないと、はじめは指定したコミットハッシュのコミットが出力されるが、その後、関係の無いコミットも出力される。気がまぎれるので、-nオプションで1を指定することで、そのコミットだけを出力する。

% git log -p --color --full-diff -n 1 1c2499ae87f828eabddf6483b0dfc11da1100c07
commit 1c2499ae87f828eabddf6483b0dfc11da1100c07
Author: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Date:   Wed Sep 22 13:05:06 2010 -0700

    /proc/pid/smaps: fix dirty pages accounting

    Currently, /proc/<pid>/smaps has wrong dirty pages accounting.
    Shared_Dirty and Private_Dirty output only pte dirty pages and ignore
    PG_dirty page flag.  It is difference against documentation, but also
    inconsistent against Referenced field.  (Referenced checks both pte and
    page flags)
[...]

追記

git logの--authorオプションに気づくまで、即席で組んだスクリプトを使ってた。--authorオプションを使う方が、このスクリプトよりも速く結果が得られる。今後、凝ったことをやりたいことが出てくるかもしれないので、コードだけはメモっておく。ひとまず、これからは--authorオプションを使っていこう。

#!/usr/bin/perl
use strict;
use warnings;

my $author = shift or die "$0 AUTHOR [PATH]";
my $path = shift || "";
my $prev_line = "";

foreach my $line(`git log $path`)
{
    if ($line =~ /^Author: $author/)
    {
        if ($prev_line =~ /^commit (.+)$/)
        {
            my $commit_hash = $1;
            my $log = `git log -p -n 1 --full-diff --color $commit_hash`;
            print $log;
            print "-" x 80 . "\n";
        }
    }
    $prev_line = $line;
}