git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH v2 0/3] Perl rewrite of Ruby git-related
@ 2013-07-03 20:59 Eric Sunshine
  2013-07-03 20:59 ` [PATCH v2 1/3] contrib: add git-contacts helper Eric Sunshine
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Eric Sunshine @ 2013-07-03 20:59 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Eric Sunshine

This is v2 of the Perl rewrite of Felipe Contreras' git-related v9 patch
series[1] which was written in Ruby. v2 addresses (hopefully all of)
Junio's v1[2] review points.

[1]: http://thread.gmane.org/gmane.comp.version-control.git/226065/
[2]: http://thread.gmane.org/gmane.comp.version-control.git/229266/


Changes since v1:

* Match Signed-off-by:, etc. labels case-insensitively.

* Recognize Cc: in addition to Signed-off-by: and other typical labels.

* Match SHA-1's and surrounding context more strictly in output of
  git-format-patch, git-blame, git-cat-file.

* Mention -C in commit message of [1/3].

* Pass just a single -C to git-blame and git-diff. Previously, git-blame
  was invoked inconsistently with '-C -C' and git-diff with '-C'.

* Employ git-blame --porcelain rather than --incremental.

* Fix bug where a "new file" ('--- /dev/null') hunk following a "changed
  file" ('--- a/path/to/file') hunk would incorrectly invoke git-blame
  on "changed file" with line numbers from "new file".

* Fix bug where filename containing whitespace was not recognized
  correctly when scanning hunk source lines ('--- /a/file name').

* Abort when unable to parse hunk source ('--- path') from diff with
  custom --src-prefix=.

* Emit diagnostic, rather than exiting silently, when user neglects to
  provide input arguments.

* Combine v1 patches [1/4] and [2/4]. As Junio pointed, a single patch
  file may contain multiple patches (for example "git format-patch
  --stdout -4 >P.mbox"), so the machinery for dealing with multiple
  patches must already be present in [1/4] whether the patches come from
  one file or many. Consequently, adding "multiple file" support
  involved only a very minor change to @ARGV handling, not deserving of
  a separate [2/4] patch.

* Drop Perl source file and line number information from user-facing
  error messages.


Possible future directions and considerations:

* Add mailmap support.

* Make participation parameters (minimum-percent, blame since-date, -C
  level) configurable.

* Optimize by coalescing ranges into a single git-blame -L invocation;
  or enhance git-blame to accept multiple -L's.

* Improve Windows support if needed, possibly via grabbing
  run_cmd_pipe() from git-add--interactive.perl or similar.


Eric Sunshine (3):
  contrib: add git-contacts helper
  contrib: contacts: add ability to parse from committish
  contrib: contacts: interpret committish akin to format-patch

 contrib/contacts/git-contacts | 170 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 170 insertions(+)
 create mode 100755 contrib/contacts/git-contacts

-- 
1.8.3.2

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

* [PATCH v2 1/3] contrib: add git-contacts helper
  2013-07-03 20:59 [PATCH v2 0/3] Perl rewrite of Ruby git-related Eric Sunshine
@ 2013-07-03 20:59 ` Eric Sunshine
  2013-07-03 20:59 ` [PATCH v2 2/3] contrib: contacts: add ability to parse from committish Eric Sunshine
  2013-07-03 20:59 ` [PATCH v2 3/3] contrib: contacts: interpret committish akin to format-patch Eric Sunshine
  2 siblings, 0 replies; 4+ messages in thread
From: Eric Sunshine @ 2013-07-03 20:59 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Eric Sunshine

This script lists people that might be interested in a patch by going
back through the history for each patch hunk, and finding people that
reviewed, acknowledge, signed, authored, or were Cc:'d on 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 participants, it
determines each person's relevance by considering how many commits
mentioned that person compared with the total number of commits under
consideration. The final output consists only of participants who pass a
minimum threshold of participation.

Several conditions controlling a person's significance are currently
hard-coded, such as minimum participation level, blame date-limiting,
and -C level for detecting moved and copied lines. In the future, these
conditions may become configurable.

For example:

  % git contacts 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 invoked as git-send-email's --cc-cmd option, among other
possible uses.

This is a Perl rewrite of Felipe Contreras' git-related patch series[1]
written in Ruby.

[1]: http://thread.gmane.org/gmane.comp.version-control.git/226065/

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 contrib/contacts/git-contacts | 127 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 127 insertions(+)
 create mode 100755 contrib/contacts/git-contacts

diff --git a/contrib/contacts/git-contacts b/contrib/contacts/git-contacts
new file mode 100755
index 0000000..6e9ab45
--- /dev/null
+++ b/contrib/contacts/git-contacts
@@ -0,0 +1,127 @@
+#!/usr/bin/perl
+
+# List people who might be interested in a patch.  Useful as the argument to
+# git-send-email --cc-cmd option, and in other situations.
+#
+# Usage: git contacts <file> ...
+
+use strict;
+use warnings;
+use IPC::Open2;
+
+my $since = '5-years-ago';
+my $min_percent = 10;
+my $labels_rx = qr/Signed-off-by|Reviewed-by|Acked-by|Cc/i;
+my %seen;
+
+sub format_contact {
+	my ($name, $email) = @_;
+	return "$name <$email>";
+}
+
+sub parse_commit {
+	my ($commit, $data) = @_;
+	my $contacts = $commit->{contacts};
+	my $inbody = 0;
+	for (split(/^/m, $data)) {
+		if (not $inbody) {
+			if (/^author ([^<>]+) <(\S+)> .+$/) {
+				$contacts->{format_contact($1, $2)} = 1;
+			} elsif (/^$/) {
+				$inbody = 1;
+			}
+		} elsif (/^$labels_rx:\s+([^<>]+)\s+<(\S+?)>$/o) {
+			$contacts->{format_contact($1, $2)} = 1;
+		}
+	}
+}
+
+sub import_commits {
+	my ($commits) = @_;
+	return unless %$commits;
+	my $pid = open2 my $reader, my $writer, qw(git cat-file --batch);
+	for my $id (keys(%$commits)) {
+		print $writer "$id\n";
+		my $line = <$reader>;
+		if ($line =~ /^([0-9a-f]{40}) commit (\d+)/) {
+			my ($cid, $len) = ($1, $2);
+			die "expected $id but got $cid\n" unless $id eq $cid;
+			my $data;
+			# cat-file emits newline after data, so read len+1
+			read $reader, $data, $len + 1;
+			parse_commit($commits->{$id}, $data);
+		}
+	}
+	close $reader;
+	close $writer;
+	waitpid($pid, 0);
+	die "git-cat-file error: $?\n" if $?;
+}
+
+sub get_blame {
+	my ($commits, $source, $start, $len, $from) = @_;
+	$len = 1 unless defined($len);
+	return if $len == 0;
+	open my $f, '-|',
+		qw(git blame --porcelain -C), '-L', "$start,+$len",
+		'--since', $since, "$from^", '--', $source or die;
+	while (<$f>) {
+		if (/^([0-9a-f]{40}) \d+ \d+ \d+$/) {
+			my $id = $1;
+			$commits->{$id} = { id => $id, contacts => {} }
+				unless $seen{$id};
+			$seen{$id} = 1;
+		}
+	}
+	close $f;
+}
+
+sub scan_patches {
+	my ($commits, $f) = @_;
+	my ($id, $source);
+	while (<$f>) {
+		if (/^From ([0-9a-f]{40}) Mon Sep 17 00:00:00 2001$/) {
+			$id = $1;
+			$seen{$id} = 1;
+		}
+		next unless $id;
+		if (m{^--- (?:a/(.+)|/dev/null)$}) {
+			$source = $1;
+		} elsif (/^--- /) {
+			die "Cannot parse hunk source: $_\n";
+		} elsif (/^@@ -(\d+)(?:,(\d+))?/ && $source) {
+			get_blame($commits, $source, $1, $2, $id);
+		}
+	}
+}
+
+sub scan_patch_file {
+	my ($commits, $file) = @_;
+	open my $f, '<', $file or die "read failure: $file: $!\n";
+	scan_patches($commits, $f);
+	close $f;
+}
+
+if (!@ARGV) {
+	die "No input patch files\n";
+}
+
+my %commits;
+for (@ARGV) {
+	scan_patch_file(\%commits, $_);
+}
+import_commits(\%commits);
+
+my %count_per_person;
+for my $commit (values %commits) {
+	for my $contact (keys %{$commit->{contacts}}) {
+		$count_per_person{$contact}++;
+	}
+}
+
+my $ncommits = scalar(keys %commits);
+for my $contact (keys %count_per_person) {
+	my $percent = $count_per_person{$contact} * 100 / $ncommits;
+	next if $percent < $min_percent;
+	print "$contact\n";
+}
-- 
1.8.3.2

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

* [PATCH v2 2/3] contrib: contacts: add ability to parse from committish
  2013-07-03 20:59 [PATCH v2 0/3] Perl rewrite of Ruby git-related Eric Sunshine
  2013-07-03 20:59 ` [PATCH v2 1/3] contrib: add git-contacts helper Eric Sunshine
@ 2013-07-03 20:59 ` Eric Sunshine
  2013-07-03 20:59 ` [PATCH v2 3/3] contrib: contacts: interpret committish akin to format-patch Eric Sunshine
  2 siblings, 0 replies; 4+ messages in thread
From: Eric Sunshine @ 2013-07-03 20:59 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Eric Sunshine

For example:

  % git contacts master..feature

Committishes and patch files can be mentioned in the same
invocation:

  % git contacts master..feature extra/*.patch

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 contrib/contacts/git-contacts | 38 ++++++++++++++++++++++++++++++++------
 1 file changed, 32 insertions(+), 6 deletions(-)

diff --git a/contrib/contacts/git-contacts b/contrib/contacts/git-contacts
index 6e9ab45..42e6059 100755
--- a/contrib/contacts/git-contacts
+++ b/contrib/contacts/git-contacts
@@ -3,7 +3,7 @@
 # List people who might be interested in a patch.  Useful as the argument to
 # git-send-email --cc-cmd option, and in other situations.
 #
-# Usage: git contacts <file> ...
+# Usage: git contacts <file | rev-list option> ...
 
 use strict;
 use warnings;
@@ -77,8 +77,8 @@ sub get_blame {
 }
 
 sub scan_patches {
-	my ($commits, $f) = @_;
-	my ($id, $source);
+	my ($commits, $id, $f) = @_;
+	my $source;
 	while (<$f>) {
 		if (/^From ([0-9a-f]{40}) Mon Sep 17 00:00:00 2001$/) {
 			$id = $1;
@@ -98,18 +98,44 @@ sub scan_patches {
 sub scan_patch_file {
 	my ($commits, $file) = @_;
 	open my $f, '<', $file or die "read failure: $file: $!\n";
-	scan_patches($commits, $f);
+	scan_patches($commits, undef, $f);
+	close $f;
+}
+
+sub scan_rev_args {
+	my ($commits, $args) = @_;
+	open my $f, '-|', qw(git rev-list --reverse), @$args or die;
+	while (<$f>) {
+		chomp;
+		my $id = $_;
+		$seen{$id} = 1;
+		open my $g, '-|', qw(git show -C --oneline), $id or die;
+		scan_patches($commits, $id, $g);
+		close $g;
+	}
 	close $f;
 }
 
 if (!@ARGV) {
-	die "No input patch files\n";
+	die "No input revisions or patch files\n";
 }
 
-my %commits;
+my (@files, @rev_args);
 for (@ARGV) {
+	if (-e) {
+		push @files, $_;
+	} else {
+		push @rev_args, $_;
+	}
+}
+
+my %commits;
+for (@files) {
 	scan_patch_file(\%commits, $_);
 }
+if (@rev_args) {
+	scan_rev_args(\%commits, \@rev_args)
+}
 import_commits(\%commits);
 
 my %count_per_person;
-- 
1.8.3.2

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

* [PATCH v2 3/3] contrib: contacts: interpret committish akin to format-patch
  2013-07-03 20:59 [PATCH v2 0/3] Perl rewrite of Ruby git-related Eric Sunshine
  2013-07-03 20:59 ` [PATCH v2 1/3] contrib: add git-contacts helper Eric Sunshine
  2013-07-03 20:59 ` [PATCH v2 2/3] contrib: contacts: add ability to parse from committish Eric Sunshine
@ 2013-07-03 20:59 ` Eric Sunshine
  2 siblings, 0 replies; 4+ messages in thread
From: Eric Sunshine @ 2013-07-03 20:59 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Eric Sunshine

As a convenience, accept the same style <since> committish as accepted
by git-format-patch. For example:

  % git contacts master

will consider commits in the current branch built atop 'master', just as
"git format-patch master" will format commits built atop 'master'.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
 contrib/contacts/git-contacts | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/contrib/contacts/git-contacts b/contrib/contacts/git-contacts
index 42e6059..3cd0ea2 100755
--- a/contrib/contacts/git-contacts
+++ b/contrib/contacts/git-contacts
@@ -102,9 +102,26 @@ sub scan_patch_file {
 	close $f;
 }
 
+sub parse_rev_args {
+	my @args = @_;
+	open my $f, '-|',
+		qw(git rev-parse --revs-only --default HEAD --symbolic), @args
+		or die;
+	my @revs;
+	while (<$f>) {
+		chomp;
+		push @revs, $_;
+	}
+	close $f;
+	return @revs if scalar(@revs) != 1;
+	return "^$revs[0]", 'HEAD' unless $revs[0] =~ /^-/;
+	return $revs[0], 'HEAD';
+}
+
 sub scan_rev_args {
 	my ($commits, $args) = @_;
-	open my $f, '-|', qw(git rev-list --reverse), @$args or die;
+	my @revs = parse_rev_args(@$args);
+	open my $f, '-|', qw(git rev-list --reverse), @revs or die;
 	while (<$f>) {
 		chomp;
 		my $id = $_;
-- 
1.8.3.2

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

end of thread, other threads:[~2013-07-03 21:00 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-07-03 20:59 [PATCH v2 0/3] Perl rewrite of Ruby git-related Eric Sunshine
2013-07-03 20:59 ` [PATCH v2 1/3] contrib: add git-contacts helper Eric Sunshine
2013-07-03 20:59 ` [PATCH v2 2/3] contrib: contacts: add ability to parse from committish Eric Sunshine
2013-07-03 20:59 ` [PATCH v2 3/3] contrib: contacts: interpret committish akin to format-patch Eric Sunshine

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).