git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH] Add new git-cc-cmd helper to contrib

From
Felipe Contreras <felipe.contreras@gmail.com>
Date
Nov 11, 2012, 17:10 UTC
Message-ID
<1352653804-2203-1-git-send-email-felipe.contreras@gmail.com>
You can run it like format-patch:
 % git cc-cmd master..my-branch
And you'll get relevant people to Cc.

The code finds the changes in each commit in the list, runs 'git blame' to see which other commits are relevant to those lines, and then adds the author and signer to the list.

Finally, it calculates what percentage of the total relevant commits each person was involved in, and if it passes the threshold, it goes in.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---

This cannot be used as cc-cmd option for 'git send-email' but it can as series-cc-cmd for which I just send some patches.

An example output:

% git cc-cmd --role-stat v1.8.0^^1..v1.8.0^^2 Vietor Liu <vietor@vxwo.org> (author: 12%) "Shawn O. Pearce" <spearce@spearce.org> (signer: 37%) Alexandre Erwin Ittner <alexandre@ittner.com.br> (author: 12%) Peter Krefting <peter@softwolves.pp.se> (author: 12%) Pat Thoyts <patthoyts@users.sourceforge.net> (signer: 37%, author: 12%) Christian Stimming <stimming@tuhh.de> (author: 12%) Heiko Voigt <hvoigt@hvoigt.net> (author: 25%) Heiko Voigt <heiko.voigt@mahr.de> (signer: 12%) Bert Wesarg <bert.wesarg@googlemail.com> (author: 12%)

Cheers.
 contrib/cc-cmd/git-cc-cmd | 186 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 186 insertions(+)
 create mode 100755 contrib/cc-cmd/git-cc-cmd
diff --git a/contrib/cc-cmd/git-cc-cmd b/contrib/cc-cmd/git-cc-cmd
new file mode 100755
index 0000000..17b14d4
--- /dev/null
+++ b/contrib/cc-cmd/git-cc-cmd
@@ -0,0 +1,186 @@
+#!/usr/bin/env ruby
+
+require 'optparse'
+
+$since = '3-years-ago'
+$min_percent = 5
+$role_stats = false
+
+begin
+  OptionParser.new do |opts|
+    opts.on('--role-stats') do |v|
+      $role_stats = v
+    end
+    opts.on('--min-percent N', Integer) do |v|
+      $min_percent = v
+    end
+    opts.on('--since DATE') do |v|
+      $since = v
+    end
+  end.parse!
+rescue OptionParser::InvalidOption
+end
+
+revs = []
+File.popen(%w[git rev-parse --revs-only --default=HEAD --symbolic] + ARGV).each do |rev|
+  revs << rev.chomp
+end
+
+case revs.size
+when 0
+  committish = '%s..HEAD' % '@{upstream}'
+when 1
+  committish = '%s..HEAD' % revs[0]
+else
+  committish = revs.join(' ')
+end
+
+$alias_file = "~/.mutt/aliases"
+$aliases = {}
+
+if $alias_file
+  open(File.expand_path($alias_file)).each do |line|
+    if line =~ /^\s*alias\s+(?:-group\s+\S+\s+)*(\S+)\s+(.*)$/
+      key, addresses = $1, $2.split(', ')
+      addresses.each do |address|
+        $aliases[address] = key
+      end
+    end
+  end
+end
+
+class Commit
+
+  attr_reader :id
+  attr_accessor :author, :roles
+
+  def initialize(id)
+    @id = id
+    @roles = []
+  end
+
+  def self.parse(data)
+    id = author = msg = nil
+    roles = {}
+    data.each_line do |line|
+      if not msg
+        case line
+        when /^commit (.+)$/
+          id = $1
+        when /^author ([^<>]+) <(\S+)>$/
+          author = $1, $2
+          roles[author] = 'author'
+        when /^$/
+          msg = true
+        end
+      else
+        if line =~ /^(Signed-off|Reviewed|Acked)-by: ([^<>]+) <(\S+?)>$/
+          person = $2, $3
+          roles[person] = 'signer' if person != author
+        end
+      end
+    end
+    roles = roles.map do |person, role|
+      address = "%s <%s>" % person
+      if $aliases.include?(address)
+        person = nil, $aliases[address]
+      end
+      [person, role]
+    end
+    [id, author, roles]
+  end
+
+end
+
+class Commits
+
+  attr_reader :items
+
+  def initialize(items = {})
+    @items = items
+    import
+  end
+
+  def size
+    @items.size
+  end
+
+  def import
+    return if @items.empty?
+    ids = @items.keys.join(' ')
+    format = [
+      'commit %H',
+      'author %an <%ae>',
+      '', '%B' ].join('%n')
+    open("|git show -z -s --format='format:#{format}' #{ids}").each("\0") do |data|
+      next if data == "\0" # bug in git show?
+      id, author, roles = Commit.parse(data)
+      commit = @items[id]
+      commit.author = author
+      commit.roles = roles
+    end
+  end
+
+  def each_person_role
+    commit_roles = @items.values.map { |commit| commit.roles }.flatten(1)
+    commit_roles.group_by { |person, role| person }.each do |person, commit_roles|
+      commit_roles.group_by { |person, role| role }.each do |role, commit_roles|
+        yield person, role, commit_roles.size
+      end
+    end
+  end
+
+  def self.from_blame(committish)
+    main_commits = {}
+    items = {}
+    open("|git rev-list --reverse #{committish}").each do |e|
+      id = e.chomp
+      main_commits[id] = true
+      open("|git --no-pager show -C --oneline #{id}").each do |e|
+        case e
+        when /^---\s+(\S+)/
+          @source = $1 != '/dev/null' ? $1[2..-1] : nil
+        when /^@@\s-(\d+),(\d+)/
+          next unless @source
+          open("|git blame --incremental -C -L #{$1},+#{$2} --since=#{$since} #{id}^ -- #{@source}").each do |e|
+            if e =~ /^(\h{40})/
+              items[$1] = Commit.new($1) if not main_commits.include?($1)
+            end
+          end
+        end
+      end
+    end
+    self.new(items)
+  end
+
+end
+
+commits = Commits.from_blame(committish)
+
+# hash of hashes
+persons = Hash.new { |hash, key| hash[key] = {} }
+
+commits.each_person_role do |person, role, count|
+  persons[person][role] = count
+end
+
+persons.each do |person, roles|
+  roles = roles.map do |role, count|
+    percent = count.to_f * 100 / commits.size
+    next if percent < $min_percent
+    "%s: %u%%" % [role, percent]
+  end.compact
+  next if roles.empty?
+  name, email = person
+
+  # has must quote chars?
+  name = '"%s"' % name if name =~ /[^\w \-]/i
+
+  person = name ? "%s <%s>" % [name, email] : email
+
+  if $role_stats
+    puts "%s (%s)" % [person, roles.join(', ')]
+  else
+    puts person
+  end
+end
-- 
1.8.0
Next: Ramkumar Ramachandra
Message 1 of 4 in “Add new git-cc-cmd helper to contrib”
  1. Add new git-cc-cmd helper to contribFelipe Contreras, Nov 11, 2012
  2. Ramkumar RamachandraNov 11, 2012
  3. Felipe ContrerasNov 11, 2012
  4. Ramkumar RamachandraDec 6, 2012

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.