git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH v10 14/15] contrib: related: add option to show commits
@ 2013-10-12  7:06 Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 00/15] New git-related helper Felipe Contreras
                   ` (14 more replies)
  0 siblings, 15 replies; 16+ messages in thread
From: Felipe Contreras @ 2013-10-12  7:06 UTC (permalink / raw)
  To: git; +Cc: Duy Nguyen, Felipe Contreras

Instead of showing the authors and signers, show the commits themselves.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/related/git-related    | 23 +++++++++++++++++++++++
 contrib/related/test-related.t | 10 ++++++++++
 2 files changed, 33 insertions(+)

diff --git a/contrib/related/git-related b/contrib/related/git-related
index d6b44c7..b9c8619 100755
--- a/contrib/related/git-related
+++ b/contrib/related/git-related
@@ -7,6 +7,7 @@ $since = '5-years-ago'
 $min_percent = 10
 $files = []
 $rev_args = []
+$show_commits = false
 
 $mailmaps = {}
 $mailmaps_complex = {}
@@ -128,6 +129,10 @@ opts.on('d', 'since', 'How far back to search for relevant commits') do |v|
   $since = v
 end
 
+opts.on('c', 'commits', 'List commits instead of persons') do |v|
+  $show_commits = v
+end
+
 opts.parse
 
 class Person
@@ -238,6 +243,10 @@ class Commits
     @items.each(&block)
   end
 
+  def list
+    @items.keys
+  end
+
   def import
     return if @items.empty?
     File.popen(%w[git cat-file --batch], 'r+') do |p|
@@ -339,6 +348,20 @@ else
 end
 commits.import
 
+if $show_commits
+  cmd = nil
+  case $show_commits
+  when 'raw'
+    puts commits.list
+  when 'full'
+    cmd = %w[git log --patch --no-walk]
+  else
+    cmd = %w[git log --oneline --no-walk]
+  end
+  system(*cmd + commits.list) if cmd
+  exit 0
+end
+
 persons = Persons.new
 
 persons.sort.reverse.each do |person|
diff --git a/contrib/related/test-related.t b/contrib/related/test-related.t
index fa8c1a7..f357e30 100755
--- a/contrib/related/test-related.t
+++ b/contrib/related/test-related.t
@@ -94,4 +94,14 @@ test_expect_success "mailmap" "
 	test_cmp expected actual
 "
 
+test_expect_success "commits" "
+	git related -craw -1 master | git log --format='%s' --no-walk --stdin > actual &&
+	cat > expected <<-EOF &&
+	four
+	three
+	one
+	EOF
+	test_cmp expected actual
+"
+
 test_done
-- 
1.8.4-fc

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v10 00/15] New git-related helper
  2013-10-12  7:06 [PATCH v10 14/15] contrib: related: add option to show commits Felipe Contreras
@ 2013-10-12  7:06 ` Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 07/15] contrib: related: add helper Person classes Felipe Contreras
                   ` (13 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Felipe Contreras @ 2013-10-12  7:06 UTC (permalink / raw)
  To: git; +Cc: Duy Nguyen, Felipe Contreras

This tool finds people that might be interested in a patch, by going
back through the history for each single hunk modified, and finding
people that reviewed, acknowledged, signed, or authored the code the
patch is modifying.

It does this by running `git blame` incrementally on each hunk, and
then parsing the commit message. After gathering all the relevant
people, it groups them to show what exactly was their role when the
participated in the development of the relevant commit, and on how
many relevant commits they participated. They are only displayed if
they pass a minimum threshold of participation.

For example:

  % git related master..fc/transport/improv
  Junio C Hamano <gitster@pobox.com> (signer: 90%, author: 5%)
  Felipe Contreras <felipe.contreras@gmail.com> (author: 25%, reviewer: 2%)
  Sverre Rabbelier <srabbelier@gmail.com> (author: 17%, acker: 2%, signer: 7%)
  Jeff King <peff@peff.net> (acker: 17%, author: 10%)
  Shawn O. Pearce <spearce@spearce.org> (author: 5%, signer: 2%, cced: 2%)
  Elijah Newren <newren@gmail.com> (author: 10%)

In addition, it has an option to output the list of commits, instead
of the contributors, which allows you to easily find out the previous
changes to the lines your patches modify.

  % git related -c master..fc/transport/improv
  99d9ec0 Merge branch 'fc/transport-helper-no-refspec'
  67c9c78 transport-helper: barf when user tries old:new
  0460ed2 documentation: trivial style cleanups
  126aac5 transport-helper: fix remote helper namespace regression
  21610d8 transport-helper: clarify pushing without refspecs
  a93b4a0 transport-helper: warn when refspec is not used
  664059f transport-helper: update remote helper namespace
  c4458ec fast-export: Allow pruned-references in mark file
  ...

Moreover, when sending patches for review, you can configure `git
send-email` to use `git related` to find relevant people that should
be Cc'ed:

  % git send-email --cc-cmd='git related' *.patch

It has way many more features than git-contacts which is a rewrite in
Perl of this script, but only the initial bare minimum stage.

I've also added tests, and this is how git-contacts fares in comparison:

--- expected 2013-10-10 21:13:45.938291298 +0000
+++ actual 2013-10-10 21:13:45.937291298 +0000
@@ -1,2 +1,2 @@
-Jon Stewart <jon@stewart.com> (author: 50%)
-Pablo Escobar <pablo@escobar.com> (author: 50%)
+Jon Stewart <jon@stewart.com>
+Pablo Escobar <pablo@escobar.com>
not ok 1 - basic

git-contacts doesn't show the amount of involvement, but that's easy to fix.

--- expected 2013-10-10 21:13:45.975291299 +0000
+++ actual 2013-10-10 21:13:45.974291299 +0000
@@ -1,2 +1,3 @@
-John Poppins <john@doe.com> (author: 66%)
-Jon Stewart <jon@stewart.com> (reviewer: 33%, author: 33%)
+John Doe <john@doe.com>
+John Poppins <john@doe.com>
+Jon Stewart <jon@stewart.com>
not ok 2 - others

git-contacts doesn't show the kind of involvement (reviewer, author)
nor does it group people by their email address.

(skip other failures)

expecting success:
git related -craw -1 master | git log --format='%s' --no-walk --stdin
> actual &&
cat > expected <<-EOF &&
four
three
one
EOF
test_cmp expected actual

not ok 7 - commits

Also, it doesn't have the option to show the commits themselves, which
is useful to investigate the diffs. This feature alone is what makes
the make "git-contacts" not appropriate, because if this feature is
implemented, 'git contacts --commits' wouldn't have anything to do
with "contacts".

For that matter it doesn't support any options at all (e.g.
--min-percent, --since), and doesn't have a usage helper (--help).

Changes since v9:

diff --git a/contrib/related/Makefile b/contrib/related/Makefile
new file mode 100644
index 0000000..1224fce
--- /dev/null
+++ b/contrib/related/Makefile
@@ -0,0 +1,17 @@
+TESTS := $(wildcard test-*.t)
+
+export T := $(addprefix $(CURDIR)/,$(TESTS))
+export MAKE := $(MAKE) -e
+export PATH := $(CURDIR):$(PATH)
+export TEST_LINT := test-lint-executable test-lint-shell-syntax
+export TEST_DIRECTORY := $(CURDIR)/../../t
+
+all:
+
+test:
+	$(MAKE) -C ../../t $@
+
+$(TESTS): all
+	$(MAKE) -C ../../t $(CURDIR)/$@
+
+.PHONY: all test $(TESTS)
diff --git a/contrib/related/README b/contrib/related/README
new file mode 100644
index 0000000..26188ca
--- /dev/null
+++ b/contrib/related/README
@@ -0,0 +1,65 @@
+= git-related =
+
+This tool finds people that might be interested in a patch, by going
+back through the history for each single hunk modified, and finding
+people that reviewed, acknowledged, signed, or authored the code the
+patch is modifying.
+
+It does this by running `git blame` incrementally on each hunk, and then
+parsing the commit message. After gathering all the relevant people, it
+groups them to show what exactly was their role when the participated in
+the development of the relevant commit, and on how many relevant commits
+they participated. They are only displayed if they pass a minimum
+threshold of participation.
+
+For example:
+
+------------
+% git related master..fc/transport/improv
+Junio C Hamano <gitster@pobox.com> (signer: 90%, author: 5%)
+Felipe Contreras <felipe.contreras@gmail.com> (author: 25%, reviewer: 2%)
+Sverre Rabbelier <srabbelier@gmail.com> (author: 17%, acker: 2%, signer: 7%)
+Jeff King <peff@peff.net> (acker: 17%, author: 10%)
+Shawn O. Pearce <spearce@spearce.org> (author: 5%, signer: 2%, cced: 2%)
+Elijah Newren <newren@gmail.com> (author: 10%)
+------------
+
+In addition, it has an option to output the list of commits, instead of the
+contributors, which allows you to easily find out the previous changes to the
+lines your patches modify.
+
+------------
+% git related -c master..fc/transport/improv
+99d9ec0 Merge branch 'fc/transport-helper-no-refspec'
+67c9c78 transport-helper: barf when user tries old:new
+0460ed2 documentation: trivial style cleanups
+126aac5 transport-helper: fix remote helper namespace regression
+21610d8 transport-helper: clarify pushing without refspecs
+a93b4a0 transport-helper: warn when refspec is not used
+664059f transport-helper: update remote helper namespace
+c4458ec fast-export: Allow pruned-references in mark file
+...
+------------
+
+Moreover, when sending patches for review, you can configure `git send-email`
+to use `git related` to find relevant people that should be Cc'ed:
+
+------------
+% git send-email --cc-cmd='git related' *.patch
+------------
+
+== Installation ==
+
+To use this script, simply put `git-related` in your `$PATH` and make sure it
+has executable permissions:
+
+------------
+wget https://raw.github.com/felipec/git-related/master/git-related -O ~/bin/git-related
+chmod +x ~/bin/git-related
+------------
+
+And make sure `~/bin` is in your `$PATH`:
+
+------------
+export PATH="$HOME/bin:$PATH"
+------------
diff --git a/contrib/related/git-related b/contrib/related/git-related
index 7248f0f..b9c8619 100755
--- a/contrib/related/git-related
+++ b/contrib/related/git-related
@@ -17,9 +17,10 @@ $base_dir = File.dirname(git_dir)
 $cur_dir = Dir.pwd
 
 KNOWN_ROLES = {
-  'Signed-off' => :signer,
-  'Reviewed' => :reviewer,
-  'Acked' => :acker,
+  'Signed-off-by' => :signer,
+  'Reviewed-by' => :reviewer,
+  'Acked-by' => :acker,
+  'Cc' => :cced,
 }
 
 def get_mailmap(filename)
@@ -45,17 +46,16 @@ get_mailmap(File.join($base_dir, '.mailmap'))
 mailmap_file = %x[git config mailmap.file].chomp
 get_mailmap(mailmap_file)
 
-class SimpleParser
+class ParseOpt
   attr_writer :usage
 
   class Option
-    attr_reader :short, :long, :values, :help
+    attr_reader :short, :long, :help
 
-    def initialize(short, long, values, help, &block)
+    def initialize(short, long, help, &block)
       @block = block
       @short = short
       @long = long
-      @values = values
       @help = help
     end
 
@@ -68,49 +68,49 @@ class SimpleParser
     @list = {}
   end
 
-  def on(*args, &block)
-    short = args.shift if args.first.is_a?(String)
-    long = args.shift if args.first.is_a?(String)
-    values = args.shift if args.first.is_a?(Array)
-    help = args.shift if args.first.is_a?(String)
-    opt = Option.new(short, long, values, help, &block)
+  def on(short = nil, long = nil, help = nil, &block)
+    opt = Option.new(short, long, help, &block)
     @list[short] = opt if short
     @list[long] = opt if long
   end
 
   def parse
-    i = 0
     if ARGV.member?('-h') or ARGV.member?('--help')
       usage
-      exit 1
+      exit 0
     end
-    while cur = ARGV[i] do
-      if cur =~ /^(-.+?)(?:=(.*))?$/
+    seen_dash = false
+    ARGV.delete_if do |cur|
+      opt = val = nil
+      next false if cur[0] != '-' or seen_dash
+      case cur
+      when '--'
+        seen_dash = true
+        next true
+      when /^--no-(.+)$/
         opt = @list[$1]
-        if opt
-          v = $2
-          if not v
-            if not opt.values
-              extra = true
-            else
-              extra = !!opt.values.map(&:to_s).member?(ARGV[i + 1])
-            end
-            v = extra ? ARGV.delete_at(i + 1) : true
-          end
-          opt.call(v)
-          ARGV.delete_at(i)
-          next
-        end
+        val = false
+      when /^-([^-])(.+)?$/, /^--(.+?)(?:=(.+))?$/
+        opt = @list[$1]
+        val = $2 || true
+      end
+      if opt
+        opt.call(val)
+        true
       end
-      i += 1
     end
   end
 
   def usage
+    def fmt(prefix, str)
+      return str ? prefix + str : nil
+    end
     puts 'usage: %s' % @usage
     @list.values.uniq.each do |opt|
       s = '    '
-      s << [opt.short, opt.long].compact.join(', ')
+      s << ''
+      s << [fmt('-', opt.short), fmt('--', opt.long)].compact.join(', ')
+      s << ''
       s << '%*s%s' % [26 - s.size, '', opt.help] if opt.help
       puts s
     end
@@ -118,21 +118,18 @@ class SimpleParser
 
 end
 
-opts = SimpleParser.new
-opts.usage = 'usage: git related [options] <files | rev-list options>'
+opts = ParseOpt.new
+opts.usage = 'git related [options] <files | rev-list options>'
 
-opts.on('-p', '--min-percent',
-        'Minium percentage of role participation') do |v|
+opts.on('p', 'min-percent', 'Minium percentage of role participation') do |v|
   $min_percent = v.to_i
 end
 
-opts.on('-d', '--since',
-        'How far back to search for relevant commits') do |v|
+opts.on('d', 'since', 'How far back to search for relevant commits') do |v|
   $since = v
 end
 
-opts.on('-c', '--commits', [:raw, :full],
-        'List commits instead of persons') do |v|
+opts.on('c', 'commits', 'List commits instead of persons') do |v|
   $show_commits = v
 end
 
@@ -207,19 +204,19 @@ class Commit
   end
 
   def parse(data)
-    msg = author = nil
+    in_body = author = nil
     data.each_line do |line|
-      if not msg
+      if not in_body
         case line
         when /^author ([^<>]+) <(\S+)> (.+)$/
           author = Persons.get($1, $2)
           author.add_role(@id, :author)
         when /^$/
-          msg = true
+          in_body = true
         end
       else
         role_regex = KNOWN_ROLES.keys.join('|')
-        if line =~ /^(#{role_regex})-by: ([^<>]+) <(\S+?)>$/
+        if line =~ /^(#{role_regex}): ([^<>]+) <(\S+?)>$/
           person = Persons.get($2, $3)
           role = KNOWN_ROLES[$1]
           next if role == :signer and person == author
@@ -276,29 +273,32 @@ class Commits
       p.each do |line|
         if line =~ /^\h{40}/
           id = $&
-          @items[id] = Commit.new(id) if not @main_commits.include?(id)
+          @items[id] ||= Commit.new(id) if not @main_commits.include?(id)
         end
       end
     end
     Dir.chdir($cur_dir)
   end
 
-  def from_patches(files)
+  def scan_patch(f, id = nil)
     source = nil
+    f.each do |line|
+      case line
+      when /^From (\h+) (.+)$/
+        id = $1
+        @main_commits[id] = true
+      when /^---\s+(\S+)/
+        source = $1 != '/dev/null' ? $1[2..-1] : nil
+      when /^@@ -(\d+)(?:,(\d+))?/
+        get_blame(source, $1, $2, id) if source and id
+      end
+    end
+  end
+
+  def from_patches(files)
     files.each do |file|
-      from = nil
       File.open(file) do |f|
-        f.each do |line|
-          case line
-          when /^From (\h+) (.+)$/
-            from = $1
-            @main_commits[from] = true
-          when /^---\s+(\S+)/
-            source = $1 != '/dev/null' ? $1[2..-1] : nil
-          when /^@@ -(\d+)(?:,(\d+))?/
-            get_blame(source, $1, $2, from) if source and from
-          end
-        end
+        scan_patch(f)
       end
     end
   end
@@ -319,20 +319,12 @@ class Commits
       args = revs
     end
 
-    source = nil
     File.popen(%w[git rev-list --reverse] + args) do |p|
       p.each do |e|
         id = e.chomp
         @main_commits[id] = true
         File.popen(%w[git show -C --oneline] + [id]) do |p|
-          p.each do |e|
-            case e
-            when /^---\s+(\S+)/
-              source = $1 != '/dev/null' ? $1[2..-1] : nil
-            when /^@@ -(\d+)(?:,(\d+))?/
-              get_blame(source, $1, $2, id) if source
-            end
-          end
+          scan_patch(p, id)
         end
       end
     end
@@ -359,9 +351,9 @@ commits.import
 if $show_commits
   cmd = nil
   case $show_commits
-  when :raw
+  when 'raw'
     puts commits.list
-  when :full
+  when 'full'
     cmd = %w[git log --patch --no-walk]
   else
     cmd = %w[git log --oneline --no-walk]
diff --git a/contrib/related/test-related.t b/contrib/related/test-related.t
new file mode 100755
index 0000000..f357e30
--- /dev/null
+++ b/contrib/related/test-related.t
@@ -0,0 +1,107 @@
+#!/bin/sh
+
+test_description="Test git related"
+
+test -z "$TEST_DIRECTORY" && TEST_DIRECTORY="$PWD/../../t"
+. "$TEST_DIRECTORY"/test-lib.sh
+
+setup() {
+	git init &&
+	echo one > content &&
+	git add content &&
+	git commit -q -m one --author='Pablo Escobar <pablo@escobar.com>' &&
+	echo two >> content &&
+	git commit -q -a -m one --author='Jon Stewart <jon@stewart.com>' &&
+	echo three >> content &&
+	git commit -q -a -m three --author='John Doe <john@doe.com>' &&
+	echo four >> content &&
+	git branch basic &&
+	git commit -q -a -F - --author='John Poppins <john@doe.com>' <<-EOF &&
+	four
+
+	Reviewed-by: Jon Stewart <jon@stewart.com>
+	EOF
+	echo five >> content &&
+	git commit -q -a -m five --author='Mary Poppins <mary@yahoo.com.uk>'
+	git checkout -b next &&
+	echo six >> content &&
+	git commit -q -a -m six --author='Ocatio Paz <octavio.paz@gmail.com>'
+}
+
+setup
+
+test_expect_success "basic" "
+	git format-patch --stdout -1 basic > patch &&
+	git related patch | sort > actual &&
+	cat > expected <<-EOF &&
+	Jon Stewart <jon@stewart.com> (author: 50%)
+	Pablo Escobar <pablo@escobar.com> (author: 50%)
+	EOF
+	test_cmp expected actual
+"
+
+test_expect_success "others" "
+	git format-patch --stdout -1 master > patch &&
+	git related patch | sort > actual &&
+	cat > expected <<-EOF &&
+	John Poppins <john@doe.com> (author: 66%)
+	Jon Stewart <jon@stewart.com> (reviewer: 33%, author: 33%)
+	EOF
+	test_cmp expected actual
+"
+
+test_expect_success "multiple patches" "
+	git format-patch --stdout -1 master > patch1 &&
+	git format-patch --stdout -1 master^ > patch2 &&
+	git related patch1 patch2 | sort > actual &&
+	cat > expected <<-EOF &&
+	John Poppins <john@doe.com> (author: 50%)
+	Jon Stewart <jon@stewart.com> (reviewer: 25%, author: 25%)
+	Pablo Escobar <pablo@escobar.com> (author: 25%)
+	EOF
+	test_cmp expected actual
+"
+
+test_expect_success "from committish" "
+	git related -1 master | sort > actual &&
+	cat > expected <<-EOF &&
+	John Poppins <john@doe.com> (author: 66%)
+	Jon Stewart <jon@stewart.com> (reviewer: 33%, author: 33%)
+	EOF
+	test_cmp expected actual
+"
+
+test_expect_success "from single rev committish" "
+	git related -1 master | sort > actual &&
+	cat > expected <<-EOF &&
+	John Poppins <john@doe.com> (author: 66%)
+	Jon Stewart <jon@stewart.com> (reviewer: 33%, author: 33%)
+	EOF
+	test_cmp expected actual
+"
+
+test_expect_success "mailmap" "
+	test_when_finished 'rm -rf .mailmap' &&
+	cat > .mailmap <<-EOF &&
+	Jon McAvoy <jon@stewart.com>
+	John Poppins <john@poppins.com> <john@doe.com>
+	EOF
+	git related -1 master | sort > actual &&
+	cat > expected <<-EOF &&
+	John Poppins <john@poppins.com> (author: 66%)
+	Jon McAvoy <jon@stewart.com> (reviewer: 33%, author: 33%)
+	EOF
+	test_cmp expected actual
+"
+
+test_expect_success "commits" "
+	git related -craw -1 master | git log --format='%s' --no-walk --stdin > actual &&
+	cat > expected <<-EOF &&
+	four
+	three
+	one
+	EOF
+	test_cmp expected actual
+"
+
+test_done

Felipe Contreras (15):
  Add new git-related helper to contrib
  contrib: related: add tests
  contrib: related: add support for multiple patches
  contrib: related: add option to parse from committish
  contrib: related: parse committish like format-patch
  contrib: related: print the amount of involvement
  contrib: related: add helper Person classes
  contrib: related: show role count
  contrib: related: add support for more roles
  contrib: related: group persons with same email
  contrib: related: allow usage on other directories
  contrib: related: add mailmap support
  contrib: related: add option parsing
  contrib: related: add option to show commits
  contrib: related: add README

 contrib/related/Makefile       |  17 ++
 contrib/related/README         |  65 +++++++
 contrib/related/git-related    | 377 +++++++++++++++++++++++++++++++++++++++++
 contrib/related/test-related.t | 107 ++++++++++++
 4 files changed, 566 insertions(+)
 create mode 100644 contrib/related/Makefile
 create mode 100644 contrib/related/README
 create mode 100755 contrib/related/git-related
 create mode 100755 contrib/related/test-related.t

-- 
1.8.4-fc

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v10 07/15] contrib: related: add helper Person classes
  2013-10-12  7:06 [PATCH v10 14/15] contrib: related: add option to show commits Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 00/15] New git-related helper Felipe Contreras
@ 2013-10-12  7:06 ` Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 10/15] contrib: related: group persons with same email Felipe Contreras
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Felipe Contreras @ 2013-10-12  7:06 UTC (permalink / raw)
  To: git; +Cc: Duy Nguyen, Felipe Contreras

No functional changes.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/related/git-related | 71 +++++++++++++++++++++++++++++++++++----------
 1 file changed, 56 insertions(+), 15 deletions(-)

diff --git a/contrib/related/git-related b/contrib/related/git-related
index ffce839..3cac925 100755
--- a/contrib/related/git-related
+++ b/contrib/related/git-related
@@ -8,13 +8,59 @@ $min_percent = 10
 $files = []
 $rev_args = []
 
-class Commit
+class Person
+
+  attr_reader :roles
+
+  def initialize(name, email)
+    @name = name
+    @email = email
+    @commits = {}
+  end
+
+  def add_role(commit)
+    @commits[commit] = true
+  end
+
+  def <=>(b)
+    self.size <=> b.size
+  end
+
+  def size
+    @commits.size
+  end
+
+  def to_s
+    '%s <%s>' % [@name, @email]
+  end
+
+end
 
-  attr_reader :persons
+class Persons
+
+  @@index = {}
+
+  include Enumerable
+
+  def each(&block)
+    @@index.values.each(&block)
+  end
+
+  def self.get(name, email)
+    id = [name, email]
+    person = @@index[id]
+    if not person
+      person = @@index[id] = Person.new(name, email)
+    end
+    person
+  end
+
+end
+
+class Commit
 
   def initialize(id)
     @id = id
-    @persons = []
   end
 
   def parse(data)
@@ -23,17 +69,18 @@ class Commit
       if not in_body
         case line
         when /^author ([^<>]+) <(\S+)> (.+)$/
-          @persons << '%s <%s>' % [$1, $2]
+          author = Persons.get($1, $2)
+          author.add_role(@id)
         when /^$/
           in_body = true
         end
       else
         if line =~ /^(Signed-off-by|Reviewed-by|Acked-by|Cc): ([^<>]+) <(\S+?)>$/
-          @persons << '%s <%s>' % [$2, $3]
+          person = Persons.get($2, $3)
+          person.add_role(@id)
         end
       end
     end
-    @persons.uniq!
   end
 
 end
@@ -152,16 +199,10 @@ else
 end
 commits.import
 
-count_per_person = Hash.new(0)
-
-commits.each do |id, commit|
-  commit.persons.each do |person|
-    count_per_person[person] += 1
-  end
-end
+persons = Persons.new
 
-count_per_person.each do |person, count|
-  percent = count.to_f * 100 / commits.size
+persons.sort.reverse.each do |person|
+  percent = person.size.to_f * 100 / commits.size
   next if percent < $min_percent
   puts '%s (involved: %u%%)' % [person, percent]
 end
-- 
1.8.4-fc

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v10 10/15] contrib: related: group persons with same email
  2013-10-12  7:06 [PATCH v10 14/15] contrib: related: add option to show commits Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 00/15] New git-related helper Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 07/15] contrib: related: add helper Person classes Felipe Contreras
@ 2013-10-12  7:06 ` Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 02/15] contrib: related: add tests Felipe Contreras
                   ` (11 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Felipe Contreras @ 2013-10-12  7:06 UTC (permalink / raw)
  To: git; +Cc: Duy Nguyen, Felipe Contreras

Suggested-by: Duy Nguyen <pclouds@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/related/git-related    |  2 +-
 contrib/related/test-related.t | 12 ++++--------
 2 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/contrib/related/git-related b/contrib/related/git-related
index f94f5f4..3399307 100755
--- a/contrib/related/git-related
+++ b/contrib/related/git-related
@@ -56,7 +56,7 @@ class Persons
   end
 
   def self.get(name, email)
-    id = [name, email]
+    id = email.downcase
     person = @@index[id]
     if not person
       person = @@index[id] = Person.new(name, email)
diff --git a/contrib/related/test-related.t b/contrib/related/test-related.t
index 9da2693..a19ad32 100755
--- a/contrib/related/test-related.t
+++ b/contrib/related/test-related.t
@@ -44,8 +44,7 @@ test_expect_success "others" "
 	git format-patch --stdout -1 master > patch &&
 	git related patch | sort > actual &&
 	cat > expected <<-EOF &&
-	John Doe <john@doe.com> (author: 33%)
-	John Poppins <john@doe.com> (author: 33%)
+	John Poppins <john@doe.com> (author: 66%)
 	Jon Stewart <jon@stewart.com> (reviewer: 33%, author: 33%)
 	EOF
 	test_cmp expected actual
@@ -56,8 +55,7 @@ test_expect_success "multiple patches" "
 	git format-patch --stdout -1 master^ > patch2 &&
 	git related patch1 patch2 | sort > actual &&
 	cat > expected <<-EOF &&
-	John Doe <john@doe.com> (author: 25%)
-	John Poppins <john@doe.com> (author: 25%)
+	John Poppins <john@doe.com> (author: 50%)
 	Jon Stewart <jon@stewart.com> (reviewer: 25%, author: 25%)
 	Pablo Escobar <pablo@escobar.com> (author: 25%)
 	EOF
@@ -67,8 +65,7 @@ test_expect_success "multiple patches" "
 test_expect_success "from committish" "
 	git related -1 master | sort > actual &&
 	cat > expected <<-EOF &&
-	John Doe <john@doe.com> (author: 33%)
-	John Poppins <john@doe.com> (author: 33%)
+	John Poppins <john@doe.com> (author: 66%)
 	Jon Stewart <jon@stewart.com> (reviewer: 33%, author: 33%)
 	EOF
 	test_cmp expected actual
@@ -77,8 +74,7 @@ test_expect_success "from committish" "
 test_expect_success "from single rev committish" "
 	git related -1 master | sort > actual &&
 	cat > expected <<-EOF &&
-	John Doe <john@doe.com> (author: 33%)
-	John Poppins <john@doe.com> (author: 33%)
+	John Poppins <john@doe.com> (author: 66%)
 	Jon Stewart <jon@stewart.com> (reviewer: 33%, author: 33%)
 	EOF
 	test_cmp expected actual
-- 
1.8.4-fc

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v10 02/15] contrib: related: add tests
  2013-10-12  7:06 [PATCH v10 14/15] contrib: related: add option to show commits Felipe Contreras
                   ` (2 preceding siblings ...)
  2013-10-12  7:06 ` [PATCH v10 10/15] contrib: related: group persons with same email Felipe Contreras
@ 2013-10-12  7:06 ` Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 06/15] contrib: related: print the amount of involvement Felipe Contreras
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Felipe Contreras @ 2013-10-12  7:06 UTC (permalink / raw)
  To: git; +Cc: Duy Nguyen, Felipe Contreras

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/related/Makefile       | 17 +++++++++++++
 contrib/related/test-related.t | 54 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 71 insertions(+)
 create mode 100644 contrib/related/Makefile
 create mode 100755 contrib/related/test-related.t

diff --git a/contrib/related/Makefile b/contrib/related/Makefile
new file mode 100644
index 0000000..1224fce
--- /dev/null
+++ b/contrib/related/Makefile
@@ -0,0 +1,17 @@
+TESTS := $(wildcard test-*.t)
+
+export T := $(addprefix $(CURDIR)/,$(TESTS))
+export MAKE := $(MAKE) -e
+export PATH := $(CURDIR):$(PATH)
+export TEST_LINT := test-lint-executable test-lint-shell-syntax
+export TEST_DIRECTORY := $(CURDIR)/../../t
+
+all:
+
+test:
+	$(MAKE) -C ../../t $@
+
+$(TESTS): all
+	$(MAKE) -C ../../t $(CURDIR)/$@
+
+.PHONY: all test $(TESTS)
diff --git a/contrib/related/test-related.t b/contrib/related/test-related.t
new file mode 100755
index 0000000..b69684d
--- /dev/null
+++ b/contrib/related/test-related.t
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+test_description="Test git related"
+
+test -z "$TEST_DIRECTORY" && TEST_DIRECTORY="$PWD/../../t"
+. "$TEST_DIRECTORY"/test-lib.sh
+
+setup() {
+	git init &&
+	echo one > content &&
+	git add content &&
+	git commit -q -m one --author='Pablo Escobar <pablo@escobar.com>' &&
+	echo two >> content &&
+	git commit -q -a -m one --author='Jon Stewart <jon@stewart.com>' &&
+	echo three >> content &&
+	git commit -q -a -m three --author='John Doe <john@doe.com>' &&
+	echo four >> content &&
+	git branch basic &&
+	git commit -q -a -F - --author='John Poppins <john@doe.com>' <<-EOF &&
+	four
+
+	Reviewed-by: Jon Stewart <jon@stewart.com>
+	EOF
+	echo five >> content &&
+	git commit -q -a -m five --author='Mary Poppins <mary@yahoo.com.uk>'
+	git checkout -b next &&
+	echo six >> content &&
+	git commit -q -a -m six --author='Ocatio Paz <octavio.paz@gmail.com>'
+}
+
+setup
+
+test_expect_success "basic" "
+	git format-patch --stdout -1 basic > patch &&
+	git related patch | sort > actual &&
+	cat > expected <<-EOF &&
+	Jon Stewart <jon@stewart.com>
+	Pablo Escobar <pablo@escobar.com>
+	EOF
+	test_cmp expected actual
+"
+
+test_expect_success "others" "
+	git format-patch --stdout -1 master > patch &&
+	git related patch | sort > actual &&
+	cat > expected <<-EOF &&
+	John Doe <john@doe.com>
+	John Poppins <john@doe.com>
+	Jon Stewart <jon@stewart.com>
+	EOF
+	test_cmp expected actual
+"
+
+test_done
-- 
1.8.4-fc

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v10 06/15] contrib: related: print the amount of involvement
  2013-10-12  7:06 [PATCH v10 14/15] contrib: related: add option to show commits Felipe Contreras
                   ` (3 preceding siblings ...)
  2013-10-12  7:06 ` [PATCH v10 02/15] contrib: related: add tests Felipe Contreras
@ 2013-10-12  7:06 ` Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 04/15] contrib: related: add option to parse from committish Felipe Contreras
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Felipe Contreras @ 2013-10-12  7:06 UTC (permalink / raw)
  To: git; +Cc: Duy Nguyen, Felipe Contreras

100% means the person was involved in all the commits, in one way or the
other.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/related/git-related    |  2 +-
 contrib/related/test-related.t | 30 +++++++++++++++---------------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/contrib/related/git-related b/contrib/related/git-related
index 80e1f17..ffce839 100755
--- a/contrib/related/git-related
+++ b/contrib/related/git-related
@@ -163,5 +163,5 @@ end
 count_per_person.each do |person, count|
   percent = count.to_f * 100 / commits.size
   next if percent < $min_percent
-  puts person
+  puts '%s (involved: %u%%)' % [person, percent]
 end
diff --git a/contrib/related/test-related.t b/contrib/related/test-related.t
index 39b4fe9..ec2680a 100755
--- a/contrib/related/test-related.t
+++ b/contrib/related/test-related.t
@@ -34,8 +34,8 @@ test_expect_success "basic" "
 	git format-patch --stdout -1 basic > patch &&
 	git related patch | sort > actual &&
 	cat > expected <<-EOF &&
-	Jon Stewart <jon@stewart.com>
-	Pablo Escobar <pablo@escobar.com>
+	Jon Stewart <jon@stewart.com> (involved: 50%)
+	Pablo Escobar <pablo@escobar.com> (involved: 50%)
 	EOF
 	test_cmp expected actual
 "
@@ -44,9 +44,9 @@ test_expect_success "others" "
 	git format-patch --stdout -1 master > patch &&
 	git related patch | sort > actual &&
 	cat > expected <<-EOF &&
-	John Doe <john@doe.com>
-	John Poppins <john@doe.com>
-	Jon Stewart <jon@stewart.com>
+	John Doe <john@doe.com> (involved: 33%)
+	John Poppins <john@doe.com> (involved: 33%)
+	Jon Stewart <jon@stewart.com> (involved: 66%)
 	EOF
 	test_cmp expected actual
 "
@@ -56,10 +56,10 @@ test_expect_success "multiple patches" "
 	git format-patch --stdout -1 master^ > patch2 &&
 	git related patch1 patch2 | sort > actual &&
 	cat > expected <<-EOF &&
-	John Doe <john@doe.com>
-	John Poppins <john@doe.com>
-	Jon Stewart <jon@stewart.com>
-	Pablo Escobar <pablo@escobar.com>
+	John Doe <john@doe.com> (involved: 25%)
+	John Poppins <john@doe.com> (involved: 25%)
+	Jon Stewart <jon@stewart.com> (involved: 50%)
+	Pablo Escobar <pablo@escobar.com> (involved: 25%)
 	EOF
 	test_cmp expected actual
 "
@@ -67,9 +67,9 @@ test_expect_success "multiple patches" "
 test_expect_success "from committish" "
 	git related -1 master | sort > actual &&
 	cat > expected <<-EOF &&
-	John Doe <john@doe.com>
-	John Poppins <john@doe.com>
-	Jon Stewart <jon@stewart.com>
+	John Doe <john@doe.com> (involved: 33%)
+	John Poppins <john@doe.com> (involved: 33%)
+	Jon Stewart <jon@stewart.com> (involved: 66%)
 	EOF
 	test_cmp expected actual
 "
@@ -77,9 +77,9 @@ test_expect_success "from committish" "
 test_expect_success "from single rev committish" "
 	git related -1 master | sort > actual &&
 	cat > expected <<-EOF &&
-	John Doe <john@doe.com>
-	John Poppins <john@doe.com>
-	Jon Stewart <jon@stewart.com>
+	John Doe <john@doe.com> (involved: 33%)
+	John Poppins <john@doe.com> (involved: 33%)
+	Jon Stewart <jon@stewart.com> (involved: 66%)
 	EOF
 	test_cmp expected actual
 "
-- 
1.8.4-fc

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v10 04/15] contrib: related: add option to parse from committish
  2013-10-12  7:06 [PATCH v10 14/15] contrib: related: add option to show commits Felipe Contreras
                   ` (4 preceding siblings ...)
  2013-10-12  7:06 ` [PATCH v10 06/15] contrib: related: print the amount of involvement Felipe Contreras
@ 2013-10-12  7:06 ` Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 15/15] contrib: related: add README Felipe Contreras
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Felipe Contreras @ 2013-10-12  7:06 UTC (permalink / raw)
  To: git; +Cc: Duy Nguyen, Felipe Contreras

For example master..feature-a.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/related/git-related    | 57 +++++++++++++++++++++++++++++++-----------
 contrib/related/test-related.t | 10 ++++++++
 2 files changed, 53 insertions(+), 14 deletions(-)

diff --git a/contrib/related/git-related b/contrib/related/git-related
index 7e79d78..4f78304 100755
--- a/contrib/related/git-related
+++ b/contrib/related/git-related
@@ -1,10 +1,12 @@
 #!/usr/bin/env ruby
 
 # This script finds people that might be interested in a patch
-# usage: git related <files>
+# usage: git related <files | rev-list options>
 
 $since = '5-years-ago'
 $min_percent = 10
+$files = []
+$rev_args = []
 
 class Commit
 
@@ -82,21 +84,36 @@ class Commits
     end
   end
 
-  def from_patches(files)
+  def scan_patch(f, id = nil)
     source = nil
+    f.each do |line|
+      case line
+      when /^From (\h+) (.+)$/
+        id = $1
+        @main_commits[id] = true
+      when /^---\s+(\S+)/
+        source = $1 != '/dev/null' ? $1[2..-1] : nil
+      when /^@@ -(\d+)(?:,(\d+))?/
+        get_blame(source, $1, $2, id) if source and id
+      end
+    end
+  end
+
+  def from_patches(files)
     files.each do |file|
-      from = nil
       File.open(file) do |f|
-        f.each do |line|
-          case line
-          when /^From (\h+) (.+)$/
-            from = $1
-            @main_commits[from] = true
-          when /^---\s+(\S+)/
-            source = $1 != '/dev/null' ? $1[2..-1] : nil
-          when /^@@ -(\d+)(?:,(\d+))?/
-            get_blame(source, $1, $2, from) if source and from
-          end
+        scan_patch(f)
+      end
+    end
+  end
+
+  def from_rev_args(args)
+    File.popen(%w[git rev-list --reverse] + args) do |p|
+      p.each do |e|
+        id = e.chomp
+        @main_commits[id] = true
+        File.popen(%w[git show -C --oneline] + [id]) do |p|
+          scan_patch(p, id)
         end
       end
     end
@@ -104,8 +121,20 @@ class Commits
 
 end
 
+ARGV.each do |e|
+  if File.exists?(e)
+    $files << e
+  else
+    $rev_args << e
+  end
+end
+
 commits = Commits.new
-commits.from_patches(ARGV)
+if $files.empty?
+  commits.from_rev_args($rev_args)
+else
+  commits.from_patches($files)
+end
 commits.import
 
 count_per_person = Hash.new(0)
diff --git a/contrib/related/test-related.t b/contrib/related/test-related.t
index 8102b3c..b623d69 100755
--- a/contrib/related/test-related.t
+++ b/contrib/related/test-related.t
@@ -64,4 +64,14 @@ test_expect_success "multiple patches" "
 	test_cmp expected actual
 "
 
+test_expect_success "from committish" "
+	git related -1 master | sort > actual &&
+	cat > expected <<-EOF &&
+	John Doe <john@doe.com>
+	John Poppins <john@doe.com>
+	Jon Stewart <jon@stewart.com>
+	EOF
+	test_cmp expected actual
+"
+
 test_done
-- 
1.8.4-fc

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v10 15/15] contrib: related: add README
  2013-10-12  7:06 [PATCH v10 14/15] contrib: related: add option to show commits Felipe Contreras
                   ` (5 preceding siblings ...)
  2013-10-12  7:06 ` [PATCH v10 04/15] contrib: related: add option to parse from committish Felipe Contreras
@ 2013-10-12  7:06 ` Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 09/15] contrib: related: add support for more roles Felipe Contreras
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Felipe Contreras @ 2013-10-12  7:06 UTC (permalink / raw)
  To: git; +Cc: Duy Nguyen, Felipe Contreras

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/related/README | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100644 contrib/related/README

diff --git a/contrib/related/README b/contrib/related/README
new file mode 100644
index 0000000..26188ca
--- /dev/null
+++ b/contrib/related/README
@@ -0,0 +1,65 @@
+= git-related =
+
+This tool finds people that might be interested in a patch, by going
+back through the history for each single hunk modified, and finding
+people that reviewed, acknowledged, signed, or authored the code the
+patch is modifying.
+
+It does this by running `git blame` incrementally on each hunk, and then
+parsing the commit message. After gathering all the relevant people, it
+groups them to show what exactly was their role when the participated in
+the development of the relevant commit, and on how many relevant commits
+they participated. They are only displayed if they pass a minimum
+threshold of participation.
+
+For example:
+
+------------
+% git related master..fc/transport/improv
+Junio C Hamano <gitster@pobox.com> (signer: 90%, author: 5%)
+Felipe Contreras <felipe.contreras@gmail.com> (author: 25%, reviewer: 2%)
+Sverre Rabbelier <srabbelier@gmail.com> (author: 17%, acker: 2%, signer: 7%)
+Jeff King <peff@peff.net> (acker: 17%, author: 10%)
+Shawn O. Pearce <spearce@spearce.org> (author: 5%, signer: 2%, cced: 2%)
+Elijah Newren <newren@gmail.com> (author: 10%)
+------------
+
+In addition, it has an option to output the list of commits, instead of the
+contributors, which allows you to easily find out the previous changes to the
+lines your patches modify.
+
+------------
+% git related -c master..fc/transport/improv
+99d9ec0 Merge branch 'fc/transport-helper-no-refspec'
+67c9c78 transport-helper: barf when user tries old:new
+0460ed2 documentation: trivial style cleanups
+126aac5 transport-helper: fix remote helper namespace regression
+21610d8 transport-helper: clarify pushing without refspecs
+a93b4a0 transport-helper: warn when refspec is not used
+664059f transport-helper: update remote helper namespace
+c4458ec fast-export: Allow pruned-references in mark file
+...
+------------
+
+Moreover, when sending patches for review, you can configure `git send-email`
+to use `git related` to find relevant people that should be Cc'ed:
+
+------------
+% git send-email --cc-cmd='git related' *.patch
+------------
+
+== Installation ==
+
+To use this script, simply put `git-related` in your `$PATH` and make sure it
+has executable permissions:
+
+------------
+wget https://raw.github.com/felipec/git-related/master/git-related -O ~/bin/git-related
+chmod +x ~/bin/git-related
+------------
+
+And make sure `~/bin` is in your `$PATH`:
+
+------------
+export PATH="$HOME/bin:$PATH"
+------------
-- 
1.8.4-fc

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v10 09/15] contrib: related: add support for more roles
  2013-10-12  7:06 [PATCH v10 14/15] contrib: related: add option to show commits Felipe Contreras
                   ` (6 preceding siblings ...)
  2013-10-12  7:06 ` [PATCH v10 15/15] contrib: related: add README Felipe Contreras
@ 2013-10-12  7:06 ` Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 03/15] contrib: related: add support for multiple patches Felipe Contreras
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Felipe Contreras @ 2013-10-12  7:06 UTC (permalink / raw)
  To: git; +Cc: Duy Nguyen, Felipe Contreras

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/related/git-related    | 14 ++++++++++++--
 contrib/related/test-related.t |  8 ++++----
 2 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/contrib/related/git-related b/contrib/related/git-related
index 04c56f5..f94f5f4 100755
--- a/contrib/related/git-related
+++ b/contrib/related/git-related
@@ -8,6 +8,13 @@ $min_percent = 10
 $files = []
 $rev_args = []
 
+KNOWN_ROLES = {
+  'Signed-off-by' => :signer,
+  'Reviewed-by' => :reviewer,
+  'Acked-by' => :acker,
+  'Cc' => :cced,
+}
+
 class Person
 
   attr_reader :roles
@@ -77,9 +84,12 @@ class Commit
           in_body = true
         end
       else
-        if line =~ /^(Signed-off-by|Reviewed-by|Acked-by|Cc): ([^<>]+) <(\S+?)>$/
+        role_regex = KNOWN_ROLES.keys.join('|')
+        if line =~ /^(#{role_regex}): ([^<>]+) <(\S+?)>$/
           person = Persons.get($2, $3)
-          person.add_role(@id, :signer) if person != author
+          role = KNOWN_ROLES[$1]
+          next if role == :signer and person == author
+          person.add_role(@id, role)
         end
       end
     end
diff --git a/contrib/related/test-related.t b/contrib/related/test-related.t
index 90cc516..9da2693 100755
--- a/contrib/related/test-related.t
+++ b/contrib/related/test-related.t
@@ -46,7 +46,7 @@ test_expect_success "others" "
 	cat > expected <<-EOF &&
 	John Doe <john@doe.com> (author: 33%)
 	John Poppins <john@doe.com> (author: 33%)
-	Jon Stewart <jon@stewart.com> (signer: 33%, author: 33%)
+	Jon Stewart <jon@stewart.com> (reviewer: 33%, author: 33%)
 	EOF
 	test_cmp expected actual
 "
@@ -58,7 +58,7 @@ test_expect_success "multiple patches" "
 	cat > expected <<-EOF &&
 	John Doe <john@doe.com> (author: 25%)
 	John Poppins <john@doe.com> (author: 25%)
-	Jon Stewart <jon@stewart.com> (signer: 25%, author: 25%)
+	Jon Stewart <jon@stewart.com> (reviewer: 25%, author: 25%)
 	Pablo Escobar <pablo@escobar.com> (author: 25%)
 	EOF
 	test_cmp expected actual
@@ -69,7 +69,7 @@ test_expect_success "from committish" "
 	cat > expected <<-EOF &&
 	John Doe <john@doe.com> (author: 33%)
 	John Poppins <john@doe.com> (author: 33%)
-	Jon Stewart <jon@stewart.com> (signer: 33%, author: 33%)
+	Jon Stewart <jon@stewart.com> (reviewer: 33%, author: 33%)
 	EOF
 	test_cmp expected actual
 "
@@ -79,7 +79,7 @@ test_expect_success "from single rev committish" "
 	cat > expected <<-EOF &&
 	John Doe <john@doe.com> (author: 33%)
 	John Poppins <john@doe.com> (author: 33%)
-	Jon Stewart <jon@stewart.com> (signer: 33%, author: 33%)
+	Jon Stewart <jon@stewart.com> (reviewer: 33%, author: 33%)
 	EOF
 	test_cmp expected actual
 "
-- 
1.8.4-fc

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v10 03/15] contrib: related: add support for multiple patches
  2013-10-12  7:06 [PATCH v10 14/15] contrib: related: add option to show commits Felipe Contreras
                   ` (7 preceding siblings ...)
  2013-10-12  7:06 ` [PATCH v10 09/15] contrib: related: add support for more roles Felipe Contreras
@ 2013-10-12  7:06 ` Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 12/15] contrib: related: add mailmap support Felipe Contreras
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Felipe Contreras @ 2013-10-12  7:06 UTC (permalink / raw)
  To: git; +Cc: Duy Nguyen, Felipe Contreras

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/related/git-related    | 35 +++++++++++++++++++----------------
 contrib/related/test-related.t | 13 +++++++++++++
 2 files changed, 32 insertions(+), 16 deletions(-)

diff --git a/contrib/related/git-related b/contrib/related/git-related
index 66c1009..7e79d78 100755
--- a/contrib/related/git-related
+++ b/contrib/related/git-related
@@ -1,7 +1,7 @@
 #!/usr/bin/env ruby
 
 # This script finds people that might be interested in a patch
-# usage: git related <file>
+# usage: git related <files>
 
 $since = '5-years-ago'
 $min_percent = 10
@@ -40,6 +40,7 @@ class Commits
 
   def initialize
     @items = {}
+    @main_commits = {}
   end
 
   def size
@@ -75,23 +76,27 @@ class Commits
       p.each do |line|
         if line =~ /^\h{40}/
           id = $&
-          @items[id] ||= Commit.new(id)
+          @items[id] ||= Commit.new(id) if not @main_commits.include?(id)
         end
       end
     end
   end
 
-  def from_patch(file)
-    from = source = nil
-    File.open(file) do |f|
-      f.each do |line|
-        case line
-        when /^From (\h+) (.+)$/
-          from = $1
-        when /^---\s+(\S+)/
-          source = $1 != '/dev/null' ? $1[2..-1] : nil
-        when /^@@ -(\d+)(?:,(\d+))?/
-          get_blame(source, $1, $2, from) if source and from
+  def from_patches(files)
+    source = nil
+    files.each do |file|
+      from = nil
+      File.open(file) do |f|
+        f.each do |line|
+          case line
+          when /^From (\h+) (.+)$/
+            from = $1
+            @main_commits[from] = true
+          when /^---\s+(\S+)/
+            source = $1 != '/dev/null' ? $1[2..-1] : nil
+          when /^@@ -(\d+)(?:,(\d+))?/
+            get_blame(source, $1, $2, from) if source and from
+          end
         end
       end
     end
@@ -99,10 +104,8 @@ class Commits
 
 end
 
-exit 1 if ARGV.size != 1
-
 commits = Commits.new
-commits.from_patch(ARGV[0])
+commits.from_patches(ARGV)
 commits.import
 
 count_per_person = Hash.new(0)
diff --git a/contrib/related/test-related.t b/contrib/related/test-related.t
index b69684d..8102b3c 100755
--- a/contrib/related/test-related.t
+++ b/contrib/related/test-related.t
@@ -51,4 +51,17 @@ test_expect_success "others" "
 	test_cmp expected actual
 "
 
+test_expect_success "multiple patches" "
+	git format-patch --stdout -1 master > patch1 &&
+	git format-patch --stdout -1 master^ > patch2 &&
+	git related patch1 patch2 | sort > actual &&
+	cat > expected <<-EOF &&
+	John Doe <john@doe.com>
+	John Poppins <john@doe.com>
+	Jon Stewart <jon@stewart.com>
+	Pablo Escobar <pablo@escobar.com>
+	EOF
+	test_cmp expected actual
+"
+
 test_done
-- 
1.8.4-fc

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v10 12/15] contrib: related: add mailmap support
  2013-10-12  7:06 [PATCH v10 14/15] contrib: related: add option to show commits Felipe Contreras
                   ` (8 preceding siblings ...)
  2013-10-12  7:06 ` [PATCH v10 03/15] contrib: related: add support for multiple patches Felipe Contreras
@ 2013-10-12  7:06 ` Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 13/15] contrib: related: add option parsing Felipe Contreras
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Felipe Contreras @ 2013-10-12  7:06 UTC (permalink / raw)
  To: git; +Cc: Duy Nguyen, Felipe Contreras

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/related/git-related    | 37 +++++++++++++++++++++++++++++++++++++
 contrib/related/test-related.t | 14 ++++++++++++++
 2 files changed, 51 insertions(+)

diff --git a/contrib/related/git-related b/contrib/related/git-related
index f15e4e7..6ab74c7 100755
--- a/contrib/related/git-related
+++ b/contrib/related/git-related
@@ -8,6 +8,9 @@ $min_percent = 10
 $files = []
 $rev_args = []
 
+$mailmaps = {}
+$mailmaps_complex = {}
+
 git_dir = %x[git rev-parse --git-dir].chomp
 $base_dir = File.dirname(git_dir)
 $cur_dir = Dir.pwd
@@ -19,6 +22,29 @@ KNOWN_ROLES = {
   'Cc' => :cced,
 }
 
+def get_mailmap(filename)
+  return unless File.exists?(filename)
+  File.open(filename) do |f|
+    f.each do |line|
+      case line.gsub(/\s*#.*$/, '')
+      when /^([^<>]+)\s+<(\S+)>$/
+        $mailmaps[$2] = [ $1, nil ]
+      when /^<(\S+)>\s+<(\S+)>$/
+        $mailmaps[$2] = [ nil, $1 ]
+      when /^([^<>]+)\s+<(\S+)>\s+<(\S+)>$/
+        $mailmaps[$3] = [ $1, $2 ]
+      when /^([^<>]+)\s+<(\S+)>\s+([^<>]+)\s+<(\S+)>$/
+        $mailmaps_complex[[$3, $4]] = [ $1, $2 ]
+      end
+    end
+  end
+end
+
+get_aliases if $get_aliases
+get_mailmap(File.join($base_dir, '.mailmap'))
+mailmap_file = %x[git config mailmap.file].chomp
+get_mailmap(mailmap_file)
+
 class Person
 
   attr_reader :roles
@@ -60,6 +86,17 @@ class Persons
   end
 
   def self.get(name, email)
+
+    # fix with mailmap
+    person = [name, email]
+    new = nil
+    new = $mailmaps_complex[person] if not new and $mailmaps_complex.include?(person)
+    new = $mailmaps[email] if not new and $mailmaps.include?(email)
+    if new
+      name = new[0] if new[0]
+      email = new[1] if new[1]
+    end
+
     id = email.downcase
     person = @@index[id]
     if not person
diff --git a/contrib/related/test-related.t b/contrib/related/test-related.t
index a19ad32..fa8c1a7 100755
--- a/contrib/related/test-related.t
+++ b/contrib/related/test-related.t
@@ -80,4 +80,18 @@ test_expect_success "from single rev committish" "
 	test_cmp expected actual
 "
 
+test_expect_success "mailmap" "
+	test_when_finished 'rm -rf .mailmap' &&
+	cat > .mailmap <<-EOF &&
+	Jon McAvoy <jon@stewart.com>
+	John Poppins <john@poppins.com> <john@doe.com>
+	EOF
+	git related -1 master | sort > actual &&
+	cat > expected <<-EOF &&
+	John Poppins <john@poppins.com> (author: 66%)
+	Jon McAvoy <jon@stewart.com> (reviewer: 33%, author: 33%)
+	EOF
+	test_cmp expected actual
+"
+
 test_done
-- 
1.8.4-fc

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v10 13/15] contrib: related: add option parsing
  2013-10-12  7:06 [PATCH v10 14/15] contrib: related: add option to show commits Felipe Contreras
                   ` (9 preceding siblings ...)
  2013-10-12  7:06 ` [PATCH v10 12/15] contrib: related: add mailmap support Felipe Contreras
@ 2013-10-12  7:06 ` Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 01/15] Add new git-related helper to contrib Felipe Contreras
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Felipe Contreras @ 2013-10-12  7:06 UTC (permalink / raw)
  To: git; +Cc: Duy Nguyen, Felipe Contreras

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/related/git-related | 85 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)

diff --git a/contrib/related/git-related b/contrib/related/git-related
index 6ab74c7..d6b44c7 100755
--- a/contrib/related/git-related
+++ b/contrib/related/git-related
@@ -45,6 +45,91 @@ get_mailmap(File.join($base_dir, '.mailmap'))
 mailmap_file = %x[git config mailmap.file].chomp
 get_mailmap(mailmap_file)
 
+class ParseOpt
+  attr_writer :usage
+
+  class Option
+    attr_reader :short, :long, :help
+
+    def initialize(short, long, help, &block)
+      @block = block
+      @short = short
+      @long = long
+      @help = help
+    end
+
+    def call(v)
+      @block.call(v)
+    end
+  end
+
+  def initialize
+    @list = {}
+  end
+
+  def on(short = nil, long = nil, help = nil, &block)
+    opt = Option.new(short, long, help, &block)
+    @list[short] = opt if short
+    @list[long] = opt if long
+  end
+
+  def parse
+    if ARGV.member?('-h') or ARGV.member?('--help')
+      usage
+      exit 0
+    end
+    seen_dash = false
+    ARGV.delete_if do |cur|
+      opt = val = nil
+      next false if cur[0] != '-' or seen_dash
+      case cur
+      when '--'
+        seen_dash = true
+        next true
+      when /^--no-(.+)$/
+        opt = @list[$1]
+        val = false
+      when /^-([^-])(.+)?$/, /^--(.+?)(?:=(.+))?$/
+        opt = @list[$1]
+        val = $2 || true
+      end
+      if opt
+        opt.call(val)
+        true
+      end
+    end
+  end
+
+  def usage
+    def fmt(prefix, str)
+      return str ? prefix + str : nil
+    end
+    puts 'usage: %s' % @usage
+    @list.values.uniq.each do |opt|
+      s = '    '
+      s << ''
+      s << [fmt('-', opt.short), fmt('--', opt.long)].compact.join(', ')
+      s << ''
+      s << '%*s%s' % [26 - s.size, '', opt.help] if opt.help
+      puts s
+    end
+  end
+
+end
+
+opts = ParseOpt.new
+opts.usage = 'git related [options] <files | rev-list options>'
+
+opts.on('p', 'min-percent', 'Minium percentage of role participation') do |v|
+  $min_percent = v.to_i
+end
+
+opts.on('d', 'since', 'How far back to search for relevant commits') do |v|
+  $since = v
+end
+
+opts.parse
+
 class Person
 
   attr_reader :roles
-- 
1.8.4-fc

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v10 01/15] Add new git-related helper to contrib
  2013-10-12  7:06 [PATCH v10 14/15] contrib: related: add option to show commits Felipe Contreras
                   ` (10 preceding siblings ...)
  2013-10-12  7:06 ` [PATCH v10 13/15] contrib: related: add option parsing Felipe Contreras
@ 2013-10-12  7:06 ` Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 11/15] contrib: related: allow usage on other directories Felipe Contreras
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Felipe Contreras @ 2013-10-12  7:06 UTC (permalink / raw)
  To: git; +Cc: Duy Nguyen, Felipe Contreras

This script find people that might be interested in a patch, by going
back through the history for each single hunk modified, and finding
people that reviewed, acknowledge, signed, or authored the code the
patch is modifying.

It does this by running 'git blame' incrementally on each hunk, and then
parsing the commit message. After gathering all the relevant people, it
groups them to show what exactly was their role when the participated in
the development of the relevant commit, and on how many relevant commits
they participated. They are only displayed if they pass a minimum
threshold of participation.

For example:

  % git related 0001-remote-hg-trivial-cleanups.patch
  Felipe Contreras <felipe.contreras@gmail.com>
  Jeff King <peff@peff.net>
  Max Horn <max@quendi.de>
  Junio C Hamano <gitster@pobox.com>

Thus it can be used for 'git send-email' as a cc-cmd.

There might be some other related functions to this script, not just to
be used as a cc-cmd.

Comments-by: Ramkumar Ramachandra <artagnon@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/related/git-related | 120 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 120 insertions(+)
 create mode 100755 contrib/related/git-related

diff --git a/contrib/related/git-related b/contrib/related/git-related
new file mode 100755
index 0000000..66c1009
--- /dev/null
+++ b/contrib/related/git-related
@@ -0,0 +1,120 @@
+#!/usr/bin/env ruby
+
+# This script finds people that might be interested in a patch
+# usage: git related <file>
+
+$since = '5-years-ago'
+$min_percent = 10
+
+class Commit
+
+  attr_reader :persons
+
+  def initialize(id)
+    @id = id
+    @persons = []
+  end
+
+  def parse(data)
+    in_body = nil
+    data.each_line do |line|
+      if not in_body
+        case line
+        when /^author ([^<>]+) <(\S+)> (.+)$/
+          @persons << '%s <%s>' % [$1, $2]
+        when /^$/
+          in_body = true
+        end
+      else
+        if line =~ /^(Signed-off-by|Reviewed-by|Acked-by|Cc): ([^<>]+) <(\S+?)>$/
+          @persons << '%s <%s>' % [$2, $3]
+        end
+      end
+    end
+    @persons.uniq!
+  end
+
+end
+
+class Commits
+
+  def initialize
+    @items = {}
+  end
+
+  def size
+    @items.size
+  end
+
+  def each(&block)
+    @items.each(&block)
+  end
+
+  def import
+    return if @items.empty?
+    File.popen(%w[git cat-file --batch], 'r+') do |p|
+      p.write(@items.keys.join("\n"))
+      p.close_write
+      p.each do |line|
+        if line =~ /^(\h{40}) commit (\d+)/
+          id, len = $1, $2
+          data = p.read($2.to_i)
+          @items[id].parse(data)
+        end
+      end
+    end
+  end
+
+  def get_blame(source, start, len, from)
+    return if len == 0
+    len ||= 1
+    File.popen(['git', 'blame', '--incremental', '-C', '-C',
+               '-L', '%u,+%u' % [start, len],
+               '--since', $since, from + '^',
+               '--', source]) do |p|
+      p.each do |line|
+        if line =~ /^\h{40}/
+          id = $&
+          @items[id] ||= Commit.new(id)
+        end
+      end
+    end
+  end
+
+  def from_patch(file)
+    from = source = nil
+    File.open(file) do |f|
+      f.each do |line|
+        case line
+        when /^From (\h+) (.+)$/
+          from = $1
+        when /^---\s+(\S+)/
+          source = $1 != '/dev/null' ? $1[2..-1] : nil
+        when /^@@ -(\d+)(?:,(\d+))?/
+          get_blame(source, $1, $2, from) if source and from
+        end
+      end
+    end
+  end
+
+end
+
+exit 1 if ARGV.size != 1
+
+commits = Commits.new
+commits.from_patch(ARGV[0])
+commits.import
+
+count_per_person = Hash.new(0)
+
+commits.each do |id, commit|
+  commit.persons.each do |person|
+    count_per_person[person] += 1
+  end
+end
+
+count_per_person.each do |person, count|
+  percent = count.to_f * 100 / commits.size
+  next if percent < $min_percent
+  puts person
+end
-- 
1.8.4-fc

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v10 11/15] contrib: related: allow usage on other directories
  2013-10-12  7:06 [PATCH v10 14/15] contrib: related: add option to show commits Felipe Contreras
                   ` (11 preceding siblings ...)
  2013-10-12  7:06 ` [PATCH v10 01/15] Add new git-related helper to contrib Felipe Contreras
@ 2013-10-12  7:06 ` Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 08/15] contrib: related: show role count Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 05/15] contrib: related: parse committish like format-patch Felipe Contreras
  14 siblings, 0 replies; 16+ messages in thread
From: Felipe Contreras @ 2013-10-12  7:06 UTC (permalink / raw)
  To: git; +Cc: Duy Nguyen, Felipe Contreras

Not just the root one (of the project).

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/related/git-related | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/contrib/related/git-related b/contrib/related/git-related
index 3399307..f15e4e7 100755
--- a/contrib/related/git-related
+++ b/contrib/related/git-related
@@ -8,6 +8,10 @@ $min_percent = 10
 $files = []
 $rev_args = []
 
+git_dir = %x[git rev-parse --git-dir].chomp
+$base_dir = File.dirname(git_dir)
+$cur_dir = Dir.pwd
+
 KNOWN_ROLES = {
   'Signed-off-by' => :signer,
   'Reviewed-by' => :reviewer,
@@ -130,6 +134,7 @@ class Commits
   def get_blame(source, start, len, from)
     return if len == 0
     len ||= 1
+    Dir.chdir($base_dir)
     File.popen(['git', 'blame', '--incremental', '-C', '-C',
                '-L', '%u,+%u' % [start, len],
                '--since', $since, from + '^',
@@ -141,6 +146,7 @@ class Commits
         end
       end
     end
+    Dir.chdir($cur_dir)
   end
 
   def scan_patch(f, id = nil)
-- 
1.8.4-fc

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v10 08/15] contrib: related: show role count
  2013-10-12  7:06 [PATCH v10 14/15] contrib: related: add option to show commits Felipe Contreras
                   ` (12 preceding siblings ...)
  2013-10-12  7:06 ` [PATCH v10 11/15] contrib: related: allow usage on other directories Felipe Contreras
@ 2013-10-12  7:06 ` Felipe Contreras
  2013-10-12  7:06 ` [PATCH v10 05/15] contrib: related: parse committish like format-patch Felipe Contreras
  14 siblings, 0 replies; 16+ messages in thread
From: Felipe Contreras @ 2013-10-12  7:06 UTC (permalink / raw)
  To: git; +Cc: Duy Nguyen, Felipe Contreras

Instead of showing the total involvement, show it per role: author, or
signer.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/related/git-related    | 18 +++++++++++++-----
 contrib/related/test-related.t | 30 +++++++++++++++---------------
 2 files changed, 28 insertions(+), 20 deletions(-)

diff --git a/contrib/related/git-related b/contrib/related/git-related
index 3cac925..04c56f5 100755
--- a/contrib/related/git-related
+++ b/contrib/related/git-related
@@ -16,10 +16,12 @@ class Person
     @name = name
     @email = email
     @commits = {}
+    @roles = Hash.new(0)
   end
 
-  def add_role(commit)
+  def add_role(commit, role)
     @commits[commit] = true
+    @roles[role] += 1
   end
 
   def <=>(b)
@@ -64,20 +66,20 @@ class Commit
   end
 
   def parse(data)
-    in_body = nil
+    in_body = author = nil
     data.each_line do |line|
       if not in_body
         case line
         when /^author ([^<>]+) <(\S+)> (.+)$/
           author = Persons.get($1, $2)
-          author.add_role(@id)
+          author.add_role(@id, :author)
         when /^$/
           in_body = true
         end
       else
         if line =~ /^(Signed-off-by|Reviewed-by|Acked-by|Cc): ([^<>]+) <(\S+?)>$/
           person = Persons.get($2, $3)
-          person.add_role(@id)
+          person.add_role(@id, :signer) if person != author
         end
       end
     end
@@ -204,5 +206,11 @@ persons = Persons.new
 persons.sort.reverse.each do |person|
   percent = person.size.to_f * 100 / commits.size
   next if percent < $min_percent
-  puts '%s (involved: %u%%)' % [person, percent]
+
+  roles = person.roles.map do |role, role_count|
+    role_percent = role_count.to_f * 100 / commits.size
+    '%s: %u%%' % [role, role_percent]
+  end
+
+  puts '%s (%s)' % [person, roles.join(', ')]
 end
diff --git a/contrib/related/test-related.t b/contrib/related/test-related.t
index ec2680a..90cc516 100755
--- a/contrib/related/test-related.t
+++ b/contrib/related/test-related.t
@@ -34,8 +34,8 @@ test_expect_success "basic" "
 	git format-patch --stdout -1 basic > patch &&
 	git related patch | sort > actual &&
 	cat > expected <<-EOF &&
-	Jon Stewart <jon@stewart.com> (involved: 50%)
-	Pablo Escobar <pablo@escobar.com> (involved: 50%)
+	Jon Stewart <jon@stewart.com> (author: 50%)
+	Pablo Escobar <pablo@escobar.com> (author: 50%)
 	EOF
 	test_cmp expected actual
 "
@@ -44,9 +44,9 @@ test_expect_success "others" "
 	git format-patch --stdout -1 master > patch &&
 	git related patch | sort > actual &&
 	cat > expected <<-EOF &&
-	John Doe <john@doe.com> (involved: 33%)
-	John Poppins <john@doe.com> (involved: 33%)
-	Jon Stewart <jon@stewart.com> (involved: 66%)
+	John Doe <john@doe.com> (author: 33%)
+	John Poppins <john@doe.com> (author: 33%)
+	Jon Stewart <jon@stewart.com> (signer: 33%, author: 33%)
 	EOF
 	test_cmp expected actual
 "
@@ -56,10 +56,10 @@ test_expect_success "multiple patches" "
 	git format-patch --stdout -1 master^ > patch2 &&
 	git related patch1 patch2 | sort > actual &&
 	cat > expected <<-EOF &&
-	John Doe <john@doe.com> (involved: 25%)
-	John Poppins <john@doe.com> (involved: 25%)
-	Jon Stewart <jon@stewart.com> (involved: 50%)
-	Pablo Escobar <pablo@escobar.com> (involved: 25%)
+	John Doe <john@doe.com> (author: 25%)
+	John Poppins <john@doe.com> (author: 25%)
+	Jon Stewart <jon@stewart.com> (signer: 25%, author: 25%)
+	Pablo Escobar <pablo@escobar.com> (author: 25%)
 	EOF
 	test_cmp expected actual
 "
@@ -67,9 +67,9 @@ test_expect_success "multiple patches" "
 test_expect_success "from committish" "
 	git related -1 master | sort > actual &&
 	cat > expected <<-EOF &&
-	John Doe <john@doe.com> (involved: 33%)
-	John Poppins <john@doe.com> (involved: 33%)
-	Jon Stewart <jon@stewart.com> (involved: 66%)
+	John Doe <john@doe.com> (author: 33%)
+	John Poppins <john@doe.com> (author: 33%)
+	Jon Stewart <jon@stewart.com> (signer: 33%, author: 33%)
 	EOF
 	test_cmp expected actual
 "
@@ -77,9 +77,9 @@ test_expect_success "from committish" "
 test_expect_success "from single rev committish" "
 	git related -1 master | sort > actual &&
 	cat > expected <<-EOF &&
-	John Doe <john@doe.com> (involved: 33%)
-	John Poppins <john@doe.com> (involved: 33%)
-	Jon Stewart <jon@stewart.com> (involved: 66%)
+	John Doe <john@doe.com> (author: 33%)
+	John Poppins <john@doe.com> (author: 33%)
+	Jon Stewart <jon@stewart.com> (signer: 33%, author: 33%)
 	EOF
 	test_cmp expected actual
 "
-- 
1.8.4-fc

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v10 05/15] contrib: related: parse committish like format-patch
  2013-10-12  7:06 [PATCH v10 14/15] contrib: related: add option to show commits Felipe Contreras
                   ` (13 preceding siblings ...)
  2013-10-12  7:06 ` [PATCH v10 08/15] contrib: related: show role count Felipe Contreras
@ 2013-10-12  7:06 ` Felipe Contreras
  14 siblings, 0 replies; 16+ messages in thread
From: Felipe Contreras @ 2013-10-12  7:06 UTC (permalink / raw)
  To: git; +Cc: Duy Nguyen, Felipe Contreras

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/related/git-related    | 15 +++++++++++++++
 contrib/related/test-related.t | 10 ++++++++++
 2 files changed, 25 insertions(+)

diff --git a/contrib/related/git-related b/contrib/related/git-related
index 4f78304..80e1f17 100755
--- a/contrib/related/git-related
+++ b/contrib/related/git-related
@@ -108,6 +108,21 @@ class Commits
   end
 
   def from_rev_args(args)
+    revs = []
+
+    File.popen(%w[git rev-parse --revs-only --default HEAD --symbolic] + args).each do |rev|
+      revs << rev.chomp
+    end
+
+    case revs.size
+    when 1
+      r = revs[0]
+      r = '^' + r if r[0] != '-'
+      args = [ r, 'HEAD' ]
+    else
+      args = revs
+    end
+
     File.popen(%w[git rev-list --reverse] + args) do |p|
       p.each do |e|
         id = e.chomp
diff --git a/contrib/related/test-related.t b/contrib/related/test-related.t
index b623d69..39b4fe9 100755
--- a/contrib/related/test-related.t
+++ b/contrib/related/test-related.t
@@ -74,4 +74,14 @@ test_expect_success "from committish" "
 	test_cmp expected actual
 "
 
+test_expect_success "from single rev committish" "
+	git related -1 master | sort > actual &&
+	cat > expected <<-EOF &&
+	John Doe <john@doe.com>
+	John Poppins <john@doe.com>
+	Jon Stewart <jon@stewart.com>
+	EOF
+	test_cmp expected actual
+"
+
 test_done
-- 
1.8.4-fc

^ permalink raw reply related	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2013-10-12  7:13 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-10-12  7:06 [PATCH v10 14/15] contrib: related: add option to show commits Felipe Contreras
2013-10-12  7:06 ` [PATCH v10 00/15] New git-related helper Felipe Contreras
2013-10-12  7:06 ` [PATCH v10 07/15] contrib: related: add helper Person classes Felipe Contreras
2013-10-12  7:06 ` [PATCH v10 10/15] contrib: related: group persons with same email Felipe Contreras
2013-10-12  7:06 ` [PATCH v10 02/15] contrib: related: add tests Felipe Contreras
2013-10-12  7:06 ` [PATCH v10 06/15] contrib: related: print the amount of involvement Felipe Contreras
2013-10-12  7:06 ` [PATCH v10 04/15] contrib: related: add option to parse from committish Felipe Contreras
2013-10-12  7:06 ` [PATCH v10 15/15] contrib: related: add README Felipe Contreras
2013-10-12  7:06 ` [PATCH v10 09/15] contrib: related: add support for more roles Felipe Contreras
2013-10-12  7:06 ` [PATCH v10 03/15] contrib: related: add support for multiple patches Felipe Contreras
2013-10-12  7:06 ` [PATCH v10 12/15] contrib: related: add mailmap support Felipe Contreras
2013-10-12  7:06 ` [PATCH v10 13/15] contrib: related: add option parsing Felipe Contreras
2013-10-12  7:06 ` [PATCH v10 01/15] Add new git-related helper to contrib Felipe Contreras
2013-10-12  7:06 ` [PATCH v10 11/15] contrib: related: allow usage on other directories Felipe Contreras
2013-10-12  7:06 ` [PATCH v10 08/15] contrib: related: show role count Felipe Contreras
2013-10-12  7:06 ` [PATCH v10 05/15] contrib: related: parse committish like format-patch Felipe Contreras

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).