threads / discuss / 25528

git as an sfc member project

Subject: git as an sfc member project

## tl;dr

21 messages between Oct 22, 2010 and Nov 2, 2010.

replies: 20people: 10as markdown or json

Jeff King· Oct 22, 2010, 18:30 UTC · lore

It's been a while since I last sent an update; the reason is that nothing was happening. :) The SFC didn't have a board meeting to vote on new projects until September, and then spent a few weeks getting paperwork together. But now we have official word: git has been accepted as a member project.

Basically, the utility of this is that the Software Freedom Conservancy will handle any money that goes to the project, especially with respect to tax implications (the SFC is a registered non-profit and has actual accountants and such). In the past, the only money has ever been Summer of Code money, and some unlucky soul had to handle the money (and tax implications) each year by themselves. Joining the SFC will also make it easier for people to give tax-deductible donations to the project. Of course I have no idea what we would spend such money on, but that is a problem we can perhaps deal with later.

There is one slight caveat, which is that JGit was not accepted, due to the complexity of their ties with the Eclipse Foundation. In practice, I don't think this will matter much at all. The only way that Git operates in any legal capacity as a group is when we do Summer of Code. This could impact us, e.g., if were to have a JGit-specific SoC project. Probably the money that goes to the organization for such a project should _not_ go through the SFC, and would have to be handled separately. Which is no worse than JGit has it today; they just can't receive the SFC services as regular git can.

Note that we're not officially a member project yet, as nothing has been signed. So if people have objections, it's not too late to say something. If we do want to go ahead, we need to fill in some details that will go in the agreement.

The draft agreement is here:
  http://peff.net/git-sponsorship-agreement.pdf

Apologies for not simply attaching it, but it is larger than vger's 100K message limit.

Bradley Kuhn also provided a some discussion of the various points in the agreement, including deciphering the legalese and explaining the motivation for each paragraph. I'll include it at the end of this email.

Basically what we need to decide on before signing is:
  1. Who should sign? These people are basically speaking for git as a
     community. Related to (2) below.
  2. What is the leadership structure of git as a legal entity? In other
     words, if we get some money that goes to the SFC (from SoC or from
     donations), who should have authority to tell the SFC to do
     something with it?
     The obvious choices (to me) are:
       a. Junio as benevolent dictator^W maintainer.
       b. Somebody else as benevolent SFC liaison.
       c. Some committee of core people (I'd say no more than 3-5) who
          would all need to agree (or perhaps some majority).
     For (b) and (c), we would need to figure out who the somebody or
     somebodies would be, and perhaps more importantly, the process for
     selecting them. As time goes on, core people may leave, and new
     core people may replace them.
     We could go as complicated as having elections, or as simple as
     "liaison appoints successor".
     Personally, I favor a small group which can approve new people to
     join, and which can leave at will. Having more than one person
     avoids hit-by-bus problems (or even just dropped-off-net problems).
     There is little enough power in such a position that I'm not too
     worried about some crazed egomaniac becoming the Git-SFC liaison.
  3. How much money should we give to the SFC?
     A big chunk of their budget comes from taking a percentage of
     member project money. As a project, we set the percentage we give
     them. So we can give them nothing if we want. But they do provide
     useful services, and even without direct benefit to git, the SFC is
     promoting free software. So probably it makes sense to choose some
     non-zero number.

More details are in the agreement linked above, or in Bradley's explanation below.

-Peff

-- >8 -- I'm glad that you are considering joining the Conservancy and I am pleased to extend an invitation to Git. Attached is a draft of the fiscal sponsorship agreement that representatives of Git will need to sign in order to join the Conservancy. (Both LaTeX source and PDF are included.) Please read this agreement and share and discuss it with all of the key people involved in Git.

As mentioned in my previous email, generally, we leave it for the Git community to decide how you'd like to discuss the document, as signing such an agreement is a big step for the project and you should consider the agreement in whatever forum is most appropriate for your community. I'm happy to answer questions from the community as you consider the document, and you should feel comfortable cc'ing me on any threads you think I should comment on. (However, before doing so, please make sure I can post back to any lists included in the Cc without being formally subscribed.) Meanwhile, you are also welcome to batch questions into one group as well and email them to me directly, and just repost my responses. Basically, whatever works well for you works fine for me.

I strongly suggest that you share the agreement draft as wide as possible throughout the community, and make sure anyone who has ever been a serious contributor to the project in the past or currently is made aware of your plans to join Conservancy. We very much rely on you to make sure that your entire community is in agreement with joining Conservancy, so please make efforts to be sure everyone has had their say.

Regarding the agreement, some of the more complex provisions of the agreement reflect the special considerations necessary to support the Conservancy's tax exempt status. However, on the whole, I believe that this agreement fairly and clearly sets out an advantageous relationship for the Conservancy's member projects. As some of the paragraphs specifically indicate, the agreement can be tailored to reflect Git's particular needs. To help you in your review, below is a section-by-section walk through, giving an explanation of the significance of each provision. If there are any sections that seem confusing or that you feel should be changed to reflect Git's needs, please let me know and we can discuss them.

Introductory Paragraph

This paragraph identifies the parties to the contract. It's more thoroughly explained in paragraph 6, but the point of this paragraph is to name the people who sign the agreement.

Recitals (the "WHEREAS" section)

These paragraphs set forth the basic understandings of the parties. Similar to the "preamble" found in the GPL and other Free Software licenses, these are *not* operative provisions of the document. Instead, they give the context of the agreement.

In this specific case, the key points of understanding are that the purpose of Git is to forward Free, Libre and Open Source Software (FLOSS) and that both the Conservancy and Git want Git to join the Conservancy. The Conservancy's mission (and charitable purpose) is to advance only FLOSS development, documentation, and usage, so it is important that this context be stated clearly.

Paragraph 1 - Term of Agreement

This paragraph says that Git is part of the Conservancy as of the signing date of the agreement. It cross references the terminations provisions in paragraph 7 (which is explained in greater detail below). Note, though, that Git can choose to leave the Conservancy at any time.

Paragraph 2 - Project Management and Activities
a) Both parties agree that Git will be FLOSS.  As noted above, this is
   the fundamental goal and charitable purpose of the Conservancy.  The
   Conservancy will not and cannot sponsor proprietary projects.
b) This clearly sets out the limits of the Conservancy's management over
   Git.  Due to requirements connected to Conservancy's tax exempt
   status, the ultimate legal control of the projects must be with the
   Conservancy.  From the IRS's perspective, the projects are part of
   the Conservancy, and the purpose of its tax exemption is to forward
   the FLOSS mission of those projects.
   However, the Conservancy does not want to interfere with the
   successful software development, documentation and advocacy work
   already underway in member projects; such activity should continue
   after the agreement without interruption or interference.  This
   paragraph delegates some of Conservancy's legal authority back to the
   developers, so that Git can run itself in day-to-day matters.
   The only limitations that we must place are to prevent Git from
   producing non-free software (as per Conservancy's charitable purpose)
   and from spending money or conducting activities that would
   jeopardize the Conservancy's tax exempt status.  All the ordinary
   activities of FLOSS projects come well within these limitations.
   Some specific activities that are restricted include lobbying
   activities and spending money in ways other than consistent with the
   charitable purposes of the Conservancy (i.e., forwarding FOSS).
   Note that developers of Git in their capacity as individuals (when
   not representing Git still may engage in for-profit service
   businesses related to their FLOSS work.  The work of Git itself must
   fit the guidelines, but individuals are free to act in their own
   capacity in other endeavors, as long as they clearly state that they
   are not acting on behalf of the project when they do so.
   If you are ever concerned that a particular activity -- be it one
   carried out for Git or one that an individual developer engaged in
   independently -- might be a problem, you can always ask the
   Conservancy for clarification.
c) As discussed above in (b), this section describes the corporate
   relationship of the project and the Conservancy.  For clarity, it
   refers to section (b), which delegates the actual management of Git
   to the relevant developers.  Conservancy, when acting as a fiscal
   sponsor, must have the legal authority to manage Git even though
   section (b) delegates the day-to-day operations to the developers.
d) This section clarifies that Git can't represent the Conservancy
   without getting written authorization first.  If you'd like to
   represent the Conservancy at a conference or other such event, you
   can always talk to us about it.
Paragraph 3 - No Fees

It's just as it sounds. The Conservancy provides services to projects to benefit the FLOSS community and does demand member projects to bear the overhead costs. Projects are encouraged to make donations to the Conservancy as a percentage of their funds to assist the Conservancy with its operating expenses. Please note, though, that Conservancy is only able to provide its services to member projects because there is a reasonably healthy general fund available. Donations from our member projects are not the only source of general fund revenue, but it is a substantial component in Conservancy' sustainability plan.

Therefore, if you choose to do so, this section provides suggested wording in brackets for donating a percentage of the Project's income to be used towards keeping the Conservancy up and running (10% is a very common rate that many umbrella organizations require for their fiscal sponsorship services).

Paragraph 4 - Project Fund/Variance Power

This sets out the financial structure in connection with the relationship described above in paragraph 2. Conservancy will separately account for Git's revenue (and Git will have its own bank account at the Conservancy once its balance reaches $3,500). For tax purposes, Conservancy will report all of the income to Git in its IRS and state filings. Git therefore will not need to file any separate tax documents with the IRS. Conservancy will keep the financial books for Git, sending periodic reports to the project's developers.

The developers will direct the Conservancy to spend the money on behalf of Git within the limitations imposed by the tax laws and Conservancy's 501(c)(3) mission. Conservancy will receive any checks on behalf of Git, and it will also write checks on behalf of Git.

Paragraph 5 - Project Fund Management/Performance of Charitable Purposes

This paragraph clarifies that all assets will be devoted to the project's purposes, as those purposes are a subset of the Conservancy's purposes. Assets cannot be used in connection with activities that would jeopardize the Conservancy's tax exempt status. As discussed above, in practice, most typical expenses of FLOSS projects will come well within these limitations. Activities that are restricted include lobbying activities and spending money in ways other than consistent with the charitable purposes of Conservancy (i.e., forwarding FLOSS).

Paragraph 6 - Representation of the Project in the Conservancy

As the note in this section indicates, we understand that each project will have its own management structure that it has developed to reflect its size and community. This paragraph requires that certain representatives be named as the individuals that can officially communicate decisions on behalf of Git. This can be a single maintainer, a committee of developers or a few specified representatives.

To the extent that it makes sense for Git to have a committee of representatives, we should indicate how decisions can be made by that committee. For example, should all decisions be communicated to the Conservancy by all members of the committee or would a simple majority suffice? Can any one representative communicate official decisions on behalf of all? Git should also consider adding a mechanism here for adding and removing representatives over time. We're happy to discuss methods that have worked for other projects with you to help you select the solution that is right for you.

We generally find this is the most difficult provisions for projects to work out, as it does require that your project consider the form and type of leadership structure it wants to have, and that structure will be legally formalized in this document for perpetuity.

Paragraph 7 - Outstanding Liabilities

In this section, Git confirms that it has told the Conservancy about any liabilities that might be outstanding prior to joining the Conservancy. This gives the Conservancy some assurance that its due diligence process has been complete and that the Conservancy's Board received all of the information it needed to properly evaluate the project. Liabilities include, for example, financial obligations, such as any debts or outstanding bills, or any legal claims that could be outstanding against Git.

If you believe some liabilities exist, or that something may be a liability and aren't sure, please err on the side of letting Conservancy know about it.

Paragraph 8 - Termination

Projects can leave Conservancy at will. This section sets out the mechanisms for termination to make sure that when a project leaves the Conservancy it does so without jeopardizing the tax exempt status of the Conservancy (and, consequently, the status of all of the other projects in the Conservancy).

There is a 60 day notice requirement so that a new tax exempt non-profit can be found for Git to join. If there isn't another fiscal sponsor or other tax exempt non-profit to take over Git, Git can incorporate as a separate entity and apply for tax exemption recognition. If there is no separate entity -- for example if a project loses momentum and has been abandoned by its developers -- the Conservancy must be left with the assets for use by the Conservancy for other FLOSS-related charitable work.

These restrictions would also apply to any separate tax exempt entity, so if Git were to incorporate and achieve tax exemption outside of Conservancy, it would have to deal with the same considerations upon any wind-up or distribution of assets. Members of the Conservancy's board are familiar with non-profit wind-down situations, and can assist in the unlikely event that this unfortunate outcome occurs.

Paragraph 9 - Miscellaneous

These provisions are standard agreement boilerplate - they clarify the enforceability of separate provisions, specify that the contract be governed by New York Law and state that any amendments to this agreement need to be agreed to in writing by all of the parties.

Paragraph 10 - Counterparts/Facsimile

Although it's good to have original signatures in the corporate records, this allows you to simply scan a copy for the contract to take effect.

We hope this explanation document has made it clear why the agreement is structured in this way. If any provisions seem problematic to you, let us know and we'll work with you to try to build an agreement that works for both of us. We look forward to Git joining the Conservancy!

Shawn Pearce· Oct 22, 2010, 19:19 UTC · re: Jeff King · lore

Re: git as an sfc member project

On Fri, Oct 22, 2010 at 11:30 AM, Jeff King <peff@peff.net> wrote:
> There is one slight caveat, which is that JGit was not accepted, due to
> the complexity of their ties with the Eclipse Foundation.

Yup. Anytime you say "Eclipse Foundation", make sure you put "complexity" into the same sentence. Its required to make the sentence accurate. :-)

> In practice, I
> don't think this will matter much at all. The only way that Git operates
> in any legal capacity as a group is when we do Summer of Code. This
> could impact us, e.g., if were to have a JGit-specific SoC project.

We had a GSoC project, but through the Eclipse Foundation participation in GSoC, not Git. So the Eclipse Foundation received our mentor stipend for us, and will spend it in some way that is unknown to me. Yay?

The decision to do JGit GSoC through Eclipse and not through Git this year wasn't really mine. I just didn't disagree loud enough to change things.

> Probably the money that goes to the organization for such a project
> should _not_ go through the SFC, and would have to be handled
> separately. Which is no worse than JGit has it today; they just can't
> receive the SFC services as regular git can.

This is fine with the JGit folks, for now anyway. We may revisit this and have JGit join SFC at some point in the future. We might not.

> Basically what we need to decide on before signing is:
>
>  1. Who should sign? These people are basically speaking for git as a
>     community. Related to (2) below.
The people listed in 2 as the leadership structure of git.
Show 13 quoted lines
>  2. What is the leadership structure of git as a legal entity? In other
>     words, if we get some money that goes to the SFC (from SoC or from
>     donations), who should have authority to tell the SFC to do
>     something with it?
>
>     The obvious choices (to me) are:
>
>       a. Junio as benevolent dictator^W maintainer.
>
>       b. Somebody else as benevolent SFC liaison.
>
>       c. Some committee of core people (I'd say no more than 3-5) who
>          would all need to agree (or perhaps some majority).
...
Show 5 quoted lines
>     Personally, I favor a small group which can approve new people to
>     join, and which can leave at will. Having more than one person
>     avoids hit-by-bus problems (or even just dropped-off-net problems).
>     There is little enough power in such a position that I'm not too
>     worried about some crazed egomaniac becoming the Git-SFC liaison.
I agree.

I think a committee of at least 3 people and at most 5, any of whom can be a benevolent SFC liasion, is fine. As far as selection goes, the committee can elect or remove a member through a majority vote, and should base its decisions based on surviving contributions to the code base, but shouldn't be tied to that (just in case someone contributes a lot of good code and then becomes a jerk).

But as you point out, there isn't much power involved here, so there isn't a lot of concern of it being abused. The important thing (the copyright on the code) is still held by individual contributors, so there is very little value involved (just the handful of GSoC dollars each year).

Show 8 quoted lines
>  3. How much money should we give to the SFC?
>
>     A big chunk of their budget comes from taking a percentage of
>     member project money. As a project, we set the percentage we give
>     them. So we can give them nothing if we want. But they do provide
>     useful services, and even without direct benefit to git, the SFC is
>     promoting free software. So probably it makes sense to choose some
>     non-zero number.
I agree, a non-zero number.  2-5%?  Any idea what is typical?
-- 
Shawn.
Jeff King· Oct 22, 2010, 19:35 UTC · re: Shawn Pearce · lore

Re: git as an sfc member project

On Fri, Oct 22, 2010 at 12:19:00PM -0700, Shawn O. Pearce wrote:
Show 7 quoted lines
> > Probably the money that goes to the organization for such a project
> > should _not_ go through the SFC, and would have to be handled
> > separately. Which is no worse than JGit has it today; they just can't
> > receive the SFC services as regular git can.
> 
> This is fine with the JGit folks, for now anyway.  We may revisit this
> and have JGit join SFC at some point in the future.  We might not.

Yeah, what I should have said to be more clear in my original email is: JGit can do what they like with SFC, but there is no reason for this caveat to prevent _Git_ from joining the SFC. The JGit folks are no worse off, and things are much better for Git.

I think it would be great if JGit could join SFC in the long run.
Show 6 quoted lines
> > Basically what we need to decide on before signing is:
> >
> >  1. Who should sign? These people are basically speaking for git as a
> >     community. Related to (2) below.
> 
> The people listed in 2 as the leadership structure of git.
Agreed (I should have written point (2) first. ;) ).
Show 6 quoted lines
> I think a committee of at least 3 people and at most 5, any of whom
> can be a benevolent SFC liasion, is fine.  As far as selection goes,
> the committee can elect or remove a member through a majority vote,
> and should base its decisions based on surviving contributions to the
> code base, but shouldn't be tied to that (just in case someone
> contributes a lot of good code and then becomes a jerk).

That sounds reasonable to me. I'm not sure what documentation, if any, we need for such a structure. I guess we have to outline it in the agreement with the SFC, so that may be sufficient. We can ask Bradley about it, too.

Show 5 quoted lines
> But as you point out, there isn't much power involved here, so there
> isn't a lot of concern of it being abused.  The important thing (the
> copyright on the code) is still held by individual contributors, so
> there is very little value involved (just the handful of GSoC dollars
> each year).

Yeah. I don't see any need to tie any decision on SFC interaction into any other part of how git is run. It should have nothing to do with how actual coding or release management works. I'm sure there will be some overlap in who is prominent in both areas, but it doesn't need to be so.

> >  3. How much money should we give to the SFC?
> [...]
> I agree, a non-zero number.  2-5%?  Any idea what is typical?

Either in the draft agreement or in the notes the number 10% is thrown out as a common value for umbrella organizations to charge. That sounds reasonable to me, as they are probably saving us at least that much in taxes by being a proper non-profit.

-Peff
Shawn Pearce· Oct 22, 2010, 20:06 UTC · re: Jeff King · lore

Re: git as an sfc member project

On Fri, Oct 22, 2010 at 12:35 PM, Jeff King <peff@peff.net> wrote:
Show 8 quoted lines
>> >  3. How much money should we give to the SFC?
>> [...]
>> I agree, a non-zero number.  2-5%?  Any idea what is typical?
>
> Either in the draft agreement or in the notes the number 10% is thrown
> out as a common value for umbrella organizations to charge. That sounds
> reasonable to me, as they are probably saving us at least that much in
> taxes by being a proper non-profit.
OK, 10% does seem reasonable given they are saving us the taxes... or more.  :-)
-- 
Shawn.
Sverre Rabbelier· Oct 22, 2010, 20:59 UTC · re: Shawn Pearce · lore

Re: git as an sfc member project

Heya,
On Fri, Oct 22, 2010 at 12:19, Shawn Pearce <spearce@spearce.org> wrote:
Show 6 quoted lines
> I think a committee of at least 3 people and at most 5, any of whom
> can be a benevolent SFC liasion, is fine.  As far as selection goes,
> the committee can elect or remove a member through a majority vote,
> and should base its decisions based on surviving contributions to the
> code base, but shouldn't be tied to that (just in case someone
> contributes a lot of good code and then becomes a jerk).

Sounds good. Something like Junio (Duh), Shawn (based on commit count), Peff (Handled GSoC money last year), and Jonathan Nieder (based on list activity)?

On Fri, Oct 22, 2010 at 13:06, Shawn Pearce <spearce@spearce.org> wrote:
> OK, 10% does seem reasonable given they are saving us the taxes... or more.  :-)
LGTM.
-- 
Cheers,

Sverre Rabbelier
Junio C Hamano· Oct 22, 2010, 21:48 UTC · re: Shawn Pearce · lore

Re: git as an sfc member project

Shawn Pearce <spearce@spearce.org> writes:
Show 16 quoted lines
> The people listed in 2 as the leadership structure of git.
> ...
>>     Personally, I favor a small group which can approve new people to
>>     join, and which can leave at will. Having more than one person
>>     avoids hit-by-bus problems (or even just dropped-off-net problems).
>>     There is little enough power in such a position that I'm not too
>>     worried about some crazed egomaniac becoming the Git-SFC liaison.
>
> I agree.
>
> I think a committee of at least 3 people and at most 5, any of whom
> can be a benevolent SFC liasion, is fine.  As far as selection goes,
> the committee can elect or remove a member through a majority vote,
> and should base its decisions based on surviving contributions to the
> code base, but shouldn't be tied to that (just in case someone
> contributes a lot of good code and then becomes a jerk).
Small group like 3 to 5 to avoid bus factor sounds reasonable to me.

Because my involvement in the project does not relate to monetizing git, I can safely be included in them, I think. If people do not mind me being that jerk you meantioned, that is ;-)

Show 10 quoted lines
>>  3. How much money should we give to the SFC?
>>
>>     A big chunk of their budget comes from taking a percentage of
>>     member project money. As a project, we set the percentage we give
>>     them. So we can give them nothing if we want. But they do provide
>>     useful services, and even without direct benefit to git, the SFC is
>>     promoting free software. So probably it makes sense to choose some
>>     non-zero number.
>
> I agree, a non-zero number.  2-5%?  Any idea what is typical?
As you two agreed 10% sounds fair to me.
Junio C Hamano· Oct 22, 2010, 22:59 UTC · re: Shawn Pearce · lore

Re: git as an sfc member project

Shawn Pearce <spearce@spearce.org> writes:
Show 6 quoted lines
> I think a committee of at least 3 people and at most 5, any of whom
> can be a benevolent SFC liasion, is fine.  As far as selection goes,
> the committee can elect or remove a member through a majority vote,
> and should base its decisions based on surviving contributions to the
> code base, but shouldn't be tied to that (just in case someone
> contributes a lot of good code and then becomes a jerk).

Just a datapoint from quick "blame -C -C -w" run as of 1.7.3.2, counting surviving lines, 7 top from each area, excluding Documentation/RelNotes.

** Everything else **

77212 Junio C Hamano 41388 Shawn O. Pearce 32676 Linus Torvalds 28618 Johannes Schindelin 22120 Ævar Arnfjörð Bjarmason 20190 Paul Mackerras 15518 Marius Storm-Olsen

** t/ **

59572 Junio C Hamano 9646 Johannes Schindelin 7606 Eric Wong 7342 Jonathan Nieder 5733 Jeff King 4978 Shawn O. Pearce 4828 Johan Herland

** Documentation/ **

22092 Junio C Hamano 10830 J. Bruce Fields 5566 Christian Couder 3904 Shawn O. Pearce 3664 Thomas Rast 3388 Johannes Schindelin 2888 David Greaves

** contrib/ **

7180 Junio C Hamano 3960 Shawn O. Pearce 3378 Alexandre Julliard 2948 Marius Storm-Olsen 2668 Aneesh Kumar K.V 2624 Simon Hausmann 1254 Matthias Urlichs

Jeff King· Oct 22, 2010, 23:18 UTC · re: Junio C Hamano · lore

Re: git as an sfc member project

On Fri, Oct 22, 2010 at 03:59:36PM -0700, Junio C Hamano wrote:
Show 22 quoted lines
> Shawn Pearce <spearce@spearce.org> writes:
> 
> > I think a committee of at least 3 people and at most 5, any of whom
> > can be a benevolent SFC liasion, is fine.  As far as selection goes,
> > the committee can elect or remove a member through a majority vote,
> > and should base its decisions based on surviving contributions to the
> > code base, but shouldn't be tied to that (just in case someone
> > contributes a lot of good code and then becomes a jerk).
> 
> Just a datapoint from quick "blame -C -C -w" run as of 1.7.3.2, counting
> surviving lines, 7 top from each area, excluding Documentation/RelNotes.
> 
> 
> ** Everything else **
> 
> 77212        Junio C Hamano
> 41388        Shawn O. Pearce
> 32676        Linus Torvalds
> 28618        Johannes Schindelin
> 22120        Ævar Arnfjörð Bjarmason
> 20190        Paul Mackerras
> 15518        Marius Storm-Olsen

How did you calculate this? I don't see how it could be right. For example, Ævar's contribution, while being impressively large lately, is only 12877 lines total over all commits, let alone surviving lines:

  $ git log --pretty=format: --numstat --author=Bjarmason |
    perl -ne '/^\d+/ and $total += $&; END { print "$total\n"; }'
  12877
-Peff
Brandon Casey· Oct 22, 2010, 23:21 UTC · re: Jeff King · lore

Re: git as an sfc member project

On 10/22/2010 06:18 PM, Jeff King wrote:
Show 32 quoted lines
> On Fri, Oct 22, 2010 at 03:59:36PM -0700, Junio C Hamano wrote:
> 
>> Shawn Pearce <spearce@spearce.org> writes:
>>
>>> I think a committee of at least 3 people and at most 5, any of whom
>>> can be a benevolent SFC liasion, is fine.  As far as selection goes,
>>> the committee can elect or remove a member through a majority vote,
>>> and should base its decisions based on surviving contributions to the
>>> code base, but shouldn't be tied to that (just in case someone
>>> contributes a lot of good code and then becomes a jerk).
>>
>> Just a datapoint from quick "blame -C -C -w" run as of 1.7.3.2, counting
>> surviving lines, 7 top from each area, excluding Documentation/RelNotes.
>>
>>
>> ** Everything else **
>>
>> 77212        Junio C Hamano
>> 41388        Shawn O. Pearce
>> 32676        Linus Torvalds
>> 28618        Johannes Schindelin
>> 22120        Ævar Arnfjörð Bjarmason
>> 20190        Paul Mackerras
>> 15518        Marius Storm-Olsen
> 
> How did you calculate this? I don't see how it could be right. For
> example, Ævar's contribution, while being impressively large lately, is
> only 12877 lines total over all commits, let alone surviving lines:
> 
>   $ git log --pretty=format: --numstat --author=Bjarmason |
>     perl -ne '/^\d+/ and $total += $&; END { print "$total\n"; }'
>   12877

I was just going to craft a reply too. I think Junio's number are all doubled.

Here are Junio's numbers for contrib/:

7180 Junio C Hamano 3960 Shawn O. Pearce 3378 Alexandre Julliard 2948 Marius Storm-Olsen 2668 Aneesh Kumar K.V 2624 Simon Hausmann 1254 Matthias Urlichs

and here's what I get for contrib/:

3590 Junio C Hamano 1980 Shawn O. Pearce 1689 Alexandre Julliard 1473 Marius Storm-Olsen 1334 Aneesh Kumar K.V 1312 Simon Hausmann 627 Matthias Urlichs

-Brandon
Junio C Hamano· Oct 22, 2010, 23:22 UTC · re: Jeff King · lore

Re: git as an sfc member project

Jeff King <peff@peff.net> writes:
> How did you calculate this? I don't see how it could be right. For
> example, Ævar's contribution, while being impressively large lately, is
> only 12877 lines total over all commits, let alone surviving lines:

Yeah, there seems to be something fishy in my math, adding up numbers from blame output.

Ævar Arnfjörð Bjarmason· Oct 23, 2010, 11:52 UTC · re: Jeff King · lore

Re: git as an sfc member project

On Fri, Oct 22, 2010 at 23:18, Jeff King <peff@peff.net> wrote:
Show 32 quoted lines
> On Fri, Oct 22, 2010 at 03:59:36PM -0700, Junio C Hamano wrote:
>
>> Shawn Pearce <spearce@spearce.org> writes:
>>
>> > I think a committee of at least 3 people and at most 5, any of whom
>> > can be a benevolent SFC liasion, is fine.  As far as selection goes,
>> > the committee can elect or remove a member through a majority vote,
>> > and should base its decisions based on surviving contributions to the
>> > code base, but shouldn't be tied to that (just in case someone
>> > contributes a lot of good code and then becomes a jerk).
>>
>> Just a datapoint from quick "blame -C -C -w" run as of 1.7.3.2, counting
>> surviving lines, 7 top from each area, excluding Documentation/RelNotes.
>>
>>
>> ** Everything else **
>>
>> 77212        Junio C Hamano
>> 41388        Shawn O. Pearce
>> 32676        Linus Torvalds
>> 28618        Johannes Schindelin
>> 22120        Ævar Arnfjörð Bjarmason
>> 20190        Paul Mackerras
>> 15518        Marius Storm-Olsen
>
> How did you calculate this? I don't see how it could be right. For
> example, Ævar's contribution, while being impressively large lately, is
> only 12877 lines total over all commits, let alone surviving lines:
>
>  $ git log --pretty=format: --numstat --author=Bjarmason |
>    perl -ne '/^\d+/ and $total += $&; END { print "$total\n"; }'
>  12877

Either way it doesn't matter, since I'm not interested in being a SFC liasion. I just want to hack, not deal with issues like these (but more power to people who want to).

But I think picking people for anything based on the number of lines that git-blame thinks people "own" is a bad criteria. My contributions to Git are relatively small, but I've happened to pick projects (the test suit, gettext) that have touched a lot of lines of code.

But other people who've done 10x more work than I have (both in time & importance) probably have 10x less lines of code assigned to them.

If I keep it up I'll probably "own" more lines of code than Linus, but I think any criteria that brings me within an order of magnitute of importance of him and Junio is pretty much broken by defintion :)

Jeff King· Oct 23, 2010, 13:39 UTC · re: Ævar Arnfjörð Bjarmason · lore

Re: git as an sfc member project

On Sat, Oct 23, 2010 at 11:52:26AM +0000, Ævar Arnfjörð Bjarmason wrote:
> Either way it doesn't matter, since I'm not interested in being a SFC
> liasion. I just want to hack, not deal with issues like these (but
> more power to people who want to).

I didn't mean to pick on you, btw. It's just that I was surprised to see you, whose first commit was only 6 months ago, in the list of top contributors by lines of code. You're productive, but not _that_ productive. :)

As it turns out, even though Junio's numbers are doubled, you are in fact high by line count. It's because of compat/regex:

  $ git log --pretty=format: --numstat --author=Bjarmason compat/regex/* |
    perl -ne '/^\d+/ and $total += $&; END { print "$total\n"; }'
  11186
which accounts for 85% of your contribution. :)
Show 7 quoted lines
> But I think picking people for anything based on the number of lines
> that git-blame thinks people "own" is a bad criteria. My contributions
> to Git are relatively small, but I've happened to pick projects (the
> test suit, gettext) that have touched a lot of lines of code.
> 
> But other people who've done 10x more work than I have (both in time &
> importance) probably have 10x less lines of code assigned to them.

I think counting surviving lines via git-blame is not that bad a metric for importance. It's certainly better than counting added lines (as I did above), as it measures lines that people are actually still using. The problem here is that we have quite large chunks of "uninteresting". Junio made some attempt to account for this by counting various parts of the codebase separately. Probably compat/ should have been removed from the core count (ditto for Marius Storm-Olsen, whose line count is inflated by importing nedmalloc; which isn't to say that any of these contributions aren't important. They just aren't the same as sitting down and writing 10,000 lines of custom git code).

In general, any line count of code (surviving or otherwise) will favor people who are adding features rather than fixing bugs. I prefer commit count, where I personally fare much better. :)

One interesting metric to me is the ratio of commit log lines to code lines. A high ratio implies (to some degree) working on bugfixes, where the actual changed lines of code are less important than the time you spend figuring out _which_ lines to change.

You can measure it with something like:
  $ git log  --format='Author: %an%n%w(0,4,4)%B' --numstat --no-merges |
    perl -ne '
      if (/^Author: (.*)/) { $author = $1 }
      elsif (/^\s{4}.+/) { $commit{$author}++ }
      elsif (/^\d+/) { $code{$author} += $& }
      END {
        print($commit{$_} / $code{$_}, " $_\n")
          for grep { $code{$_} } keys(%code)
      }
    ' | sort -rn

Of course it has its own set of flaws. One giant feature contribution can outweight a lot of bugfixes in the average.

-Peff
A Large Angry SCM· Oct 23, 2010, 16:03 UTC · re: Jeff King · lore

Re: git as an sfc member project

On 10/23/2010 09:39 AM, Jeff King wrote:
Show 10 quoted lines
> On Sat, Oct 23, 2010 at 11:52:26AM +0000, Ævar Arnfjörð Bjarmason wrote:
>
>> Either way it doesn't matter, since I'm not interested in being a SFC
>> liasion. I just want to hack, not deal with issues like these (but
>> more power to people who want to).
>
> I didn't mean to pick on you, btw. It's just that I was surprised to see
> you, whose first commit was only 6 months ago, in the list of top
> contributors by lines of code. You're productive, but not _that_
> productive. :)
For what it's worth: my nominees are:
	Junio
	Shawn
	Jeff
	?
	?

This group has been involved a long time and each is or has been THE maintainer at some point. More importantly, they have consistently demonstrated critical thinking with a long term view and the ability to handle the "politics" of the mailing list.

In addition to the above, I would like to 2 of the newer members of our community in order to bring a different perspective to the deliberations. Even better if they are USA based.

Jeff King· Oct 26, 2010, 22:39 UTC · re: Jeff King · lore

Re: git as an sfc member project

On Fri, Oct 22, 2010 at 02:30:28PM -0400, Jeff King wrote:
> The draft agreement is here:
> 
>   http://peff.net/git-sponsorship-agreement.pdf

OK, this thread seems to have died, which I'll take to mean that everybody either agrees or doesn't care.

So let's do this:
  1. There will be a committee of 3-5 Gits who will act as liaisons to
     the SFC. A simple majority of that committee is required to
     authorize SFC to do stuff (like disburse money). Existing members
     can be removed from and new members can be added to the committee
     by simple majority vote of the existing committee.
     As for the initial committee, Gitzilla already nominated Junio,
     Shawn, and me. Those sound like a good start to me (assuming the
     other two accept). I'm happy to hear other nominations. I think
     starting with the 3 of us would be fine, too, and we can add people
     who become interested in the management of git and the sfc (and if
     there _is_ anybody who is interested now, please speak up and/or
     nominate yourself; I can think of many people on the list who would
     be good candidates, but the main issue seems to me that nobody is
     interested. :) ).
  2. We'll give 10% of incoming Git money to the SFC for their
     operations (where incoming git money is basically SoC money plus
     any donations SFC collects on our behalf).

Unless I hear objections on the list, this is what I'll present to the SFC as our plan.

-Peff
Tait· Oct 27, 2010, 07:03 UTC · re: Jeff King · lore

Re: git as an sfc member project

> The draft agreement is here:
>   http://peff.net/git-sponsorship-agreement.pdf

Sorry I'm late to the thread. This agreement brings up one concern for me. It would make officially make git a United States project based out of New York, and therefore subject to the laws of New York and the United States. Among whatever other laws apply, will be export restrictions and patent law. I don't know whether any part(s) of git would be a concern under those laws (and I haven't needed to care, until now). Is legal advice for issues like this part of the services SFC can provide?

Jeff King· Oct 27, 2010, 11:08 UTC · re: Tait · lore

Re: git as an sfc member project

On Wed, Oct 27, 2010 at 12:03:48AM -0700, Tait wrote:
Show 10 quoted lines
> > The draft agreement is here:
> >   http://peff.net/git-sponsorship-agreement.pdf
> 
> Sorry I'm late to the thread. This agreement brings up one concern for
> me. It would make officially make git a United States project based out
> of New York, and therefore subject to the laws of New York and the United
> States. Among whatever other laws apply, will be export restrictions and
> patent law. I don't know whether any part(s) of git would be a concern
> under those laws (and I haven't needed to care, until now). Is legal
> advice for issues like this part of the services SFC can provide?

I am not sure that joining the SFC is going to make any difference with respect to those things. Developers and distributors of the software in the United States were already subject to such laws, and I don't see how our dealing with the SFC would create any special obligation for those outside the US. In particular, it seems to me that git as a legal entity signing this agreement as the SFC (which legally is really just an agreement between the SFC and a few members of the project) is different from git as a community of individuals who happen to contribute and distribute code. SFC will not own any copyrights, nor take any responsibility for distribution.

But I am not a lawyer, of course, and yes, this seems like exactly the sort of thing we can ask them about. So I've cc'd Bradley. :)

-Peff
Bradley M. Kuhn· Nov 2, 2010, 23:03 UTC · re: Jeff King · lore

Re: git as an sfc member project

>> > The draft agreement is here:
>> >   http://peff.net/git-sponsorship-agreement.pdf
> On Wed, Oct 27, 2010 at 12:03:48AM -0700, Tait wrote:
Show 7 quoted lines
>> This agreement brings up one concern for me. It would make officially
>> make git a United States project based out of New York, and therefore
>> subject to the laws of New York and the United States. Among whatever
>> other laws apply, will be export restrictions and patent law. I don't
>> know whether any part(s) of git would be a concern under those laws
>> (and I haven't needed to care, until now). Is legal advice for issues
>> like this part of the services SFC can provide?
Jeff King wrote on the 27th of October:
Show 10 quoted lines
> I am not sure that joining the SFC is going to make any difference
> with respect to those things. Developers and distributors of the
> software in the United States were already subject to such laws, and I
> don't see how our dealing with the SFC would create any special
> obligation for those outside the US. In particular, it seems to me
> that git as a legal entity signing this agreement as the SFC (which
> legally is really just an agreement between the SFC and a few members
> of the project) is different from git as a community of individuals
> who happen to contribute and distribute code.  SFC will not own any
> copyrights, nor take any responsibility for distribution.

I believe what Jeff says is basically correct. The "Git Project" will be part of a non-profit in New York, and it's true that the Git Project could be therefore be subject to the laws of New York. However, it isn't that much different of some Git developers being in New York and being subject thereto. Developers outside the USA continue to operate as volunteers for the project and aren't impacted any more than they already are participating in an unincorporated project.

Nevertheless, I'm going to check in with Conservancy's lawyers to verify there are no serious disadvantages to Git joining a USA non-profit that weren't already the case anyway since some developers are in the USA anyway. I'll respond back when I hear from them.

-- 
Bradley M. Kuhn, Executive Director, Software Freedom Conservancy
Brandon Casey· Oct 23, 2010, 00:09 UTC · lore

Re: git as an sfc member project

I was curious about something like this earlier this week, and I wrote this little hack.

Got anything better?

---- >8 ---- #!/usr/bin/perl -w

use lib (split(/:/, $ENV{GITPERLLIB} || "/your/git/installation/lib/perl5/site_perl/5.8.8"));

use strict; use Git;

use Getopt::Std; $Getopt::Std::STANDARD_HELP_VERSION = 1;

$main::VERSION = 0.1;
sub usage {
	my $name;
	eval {
		require File::Basename;
		$name = File::Basename::basename($0);
	} or do {
		$name = substr $0, rindex($0, '/') + 1;
	};
	print "Usage: $name [--help] [-flstv] <tree-ish> [paths...]\n";
}
sub main::HELP_MESSAGE {
	usage;
	print "\nOPTIONS\n\n",
	      " -f  use file centric view\n",
	      " -l  use longer format\n",
	      " -s  use short format\n",
	      " Both -l and -s produce an even longer format\n",
	      " -t  print total lines at the end\n",
	      " -v  print more information while processing\n",
	      "\n";
}
sub new_parser {
	my $fh = shift;
	my $ctx = shift;
	return { FH => $fh, CTX => $ctx, buf => ''};
}
sub get_next_line {
	my $obj = shift;
	my $fh = $obj->{'FH'};
	defined($obj->{'buf'} = <$fh>);
}
sub parse_blame_entry {
	my $obj = shift;
	return () unless get_next_line $obj;
	$_ = $obj->{'buf'};
	chomp;
	my ($sha1, $sourceline, $resultline, $num_lines) = split ' ', $_, 4;
	return () unless defined $num_lines;
	my %h = ( sha1 => $sha1, sourceline => $sourceline,
		  resultline => $resultline, lines => $num_lines );
	while (get_next_line $obj) {
		$_ = $obj->{'buf'};
		chomp;
		my ($key, $val) = split ' ', $_, 2;
		$h{$key} = $val;
		last if m/^filename /;
	}
	return %h;
}
sub parse_blame {
	my $obj = shift;
        my $authors = shift;
	my %commits;
	while (my %h = parse_blame_entry $obj) {
		if (! exists $commits{$h{'sha1'}}) {
			if (! exists $authors->{$h{'author'}}->{$obj->{'filename'}}) {
				$authors->{$h{'author'}}->{$obj->{'filename'}} = 0;
			}
			$commits{$h{'sha1'}} =
				\$authors->{$h{'author'}}->{$obj->{'filename'}};
		}
		${$commits{$h{'sha1'}}} += $h{'lines'};
	}
}
sub count_total_lines {
	my $authors = shift;
	my $lines = 0;
	for (values %{$authors}) {
		for (values %{$_}) { $lines += $_; }
	}
	return $lines;
}
sub count_author_lines {
	my $authors = shift;
	my %alines;
	foreach my $author (keys %{$authors}) {
		my $lines = 0;
		for (values %{$authors->{$author}}) { $lines += $_; }
		$alines{$author} = $lines;
	}
	return %alines;
}
sub count_file_lines {
	my $authors = shift;
	my %flines;
	for (values %{$authors}) {
		foreach my $file (keys %{$_}) {
			$flines{$file} += $_->{$file};
		}
	}
	return %flines;
}
sub print_short {
	my $authors = shift;
	my %alines = count_author_lines $authors;
	foreach my $author (sort {$alines{$b} <=> $alines{$a}} keys %alines) {
		printf "%6d  %s\n", $alines{$author}, $author;
	}
}
sub print_long {
	my $authors = shift;
	my %alines = count_author_lines $authors;
	foreach my $author (sort {$alines{$b} <=> $alines{$a}} keys %alines) {
		print "$author (", $alines{$author}, "):\n";
		foreach my $file (sort
		    {$authors->{$author}->{$b} <=> $authors->{$author}->{$a}}
		    keys %{$authors->{$author}}) {
			printf "  %10d %s\n", $authors->{$author}->{$file},
			      $file;
		}
	}
}
sub print_longer {
	my $authors = shift;
	my %alines = count_author_lines $authors;
	my $total_lines = count_total_lines $authors;
	foreach my $author (sort {$alines{$b} <=> $alines{$a}} keys %alines) {
		printf "%s (%d, %.2f%%):\n", $author, $alines{$author},
			100. * $alines{$author} / $total_lines;
		foreach my $file (sort
		    {$authors->{$author}->{$b} <=> $authors->{$author}->{$a}}
		    keys %{$authors->{$author}}) {
			printf "  %10d (%5.2f%%) %s\n",
			       $authors->{$author}->{$file},
			       100. *
			       $authors->{$author}->{$file} / $alines{$author},
			       $file;
		}
	}
}
sub print_with_file_percentage {
	my $authors = shift;
	my %alines = count_author_lines $authors;
	my %flines = count_file_lines $authors;
	my $total_lines = count_total_lines $authors;
	my $total_files = scalar(keys %flines);
	foreach my $author (sort {$alines{$b} <=> $alines{$a}} keys %alines) {
		printf "%s (%d lines in %d files, " .
		       "%.2f%% of all lines, %.2f%% of all files):\n",
		       $author, $alines{$author},
		       scalar(keys %{$authors->{$author}}),
		       100. * $alines{$author} / $total_lines,
		       100. * scalar(keys %{$authors->{$author}})/$total_files;
		foreach my $file (sort
		    {$authors->{$author}->{$b} <=> $authors->{$author}->{$a}}
		    keys %{$authors->{$author}}) {
			printf "  %10d (%6.2f%%) of %6d (%6.2f%%) %s\n",
			       $authors->{$author}->{$file},
			       100. *
			       $authors->{$author}->{$file} / $flines{$file},
			       $flines{$file},
			       100. *
			       $authors->{$author}->{$file} / $alines{$author},
			       $file;
		}
	}
}
sub print_with_file_perspective {
	my $authors = shift;
	my %flines = count_file_lines $authors;
	foreach my $file (sort keys %flines) {
		my @auths = grep {exists $authors->{$_}->{$file}}
			keys %{$authors};
		print "$file ($flines{$file}):\n";
		foreach my $author (sort
		    {$authors->{$b}->{$file} <=> $authors->{$a}->{$file}}
		    @auths) {
			printf " %10d %s\n", $authors->{$author}->{$file},
				$author;
		}
	}
}

my $verbose = 0; my $output_format = 0; my $show_total = 0;

our ($opt_f, $opt_l, $opt_s, $opt_t, $opt_v); getopts("flstv") or die "Invalid options specified";

	if ($opt_f) {
		$output_format = 4;
	} elsif ($opt_l && $opt_s) {
		$output_format = 3;
	} elsif ($opt_l) {
		$output_format = 2;
	} elsif ($opt_s) {
		$output_format = 1;
	}
	if ($opt_t) {
		$show_total = 1;
	}
	if ($opt_v) {
		$verbose = 1;
	}
eval {select STDERR; usage; exit 1;} unless $#ARGV >= 0;

my %authors; my $repo = Git->repository();

my ($fh, $ctx) = $repo->command_output_pipe('ls-tree', '-r', '--name-only', '--', @ARGV);
my $ls_tree = new_parser $fh, $ctx;
while (get_next_line $ls_tree) {
	chomp $ls_tree->{'buf'};
        my $is_text = `file "$ls_tree->{'buf'}"` =~ m/text/;
	if ($is_text) {
		if ($verbose) {
			print STDERR "Processing file: ", $ls_tree->{'buf'},
				"\n";
		}
	} else {
		if ($verbose) {
			print STDERR "Skipping non-text file: ",
				$ls_tree->{'buf'}, "\n";
		}
		next;
	}
	($fh, $ctx) =
	    $repo->command_output_pipe('blame', '-C', '-C', '-w', '--incremental',
		'--', $ls_tree->{'buf'});
	my $blame_obj = new_parser $fh, $ctx;
	$blame_obj->{'filename'} = $ls_tree->{'buf'};
	parse_blame $blame_obj, \%authors;
	$repo->command_close_pipe($blame_obj->{'FH'}, $blame_obj->{'CTX'});
}
$repo->command_close_pipe($ls_tree->{'FH'}, $ls_tree->{'CTX'});
if ($output_format == 0) {
	print_long \%authors;
} elsif ($output_format == 1) {
	print_short \%authors;
} elsif ($output_format == 2) {
	print_longer \%authors;
} elsif ($output_format == 3) {
	print_with_file_percentage \%authors;
} elsif ($output_format == 4) {
	print_with_file_perspective \%authors;
}
print count_total_lines(\%authors), " total lines.\n" if ($show_total);
exit;
Brandon Casey· Oct 23, 2010, 01:30 UTC · re: Brandon Casey · lore

Re: git as an sfc member project

On 10/22/2010 07:09 PM, Brandon Casey wrote:
> 	($fh, $ctx) =
> 	    $repo->command_output_pipe('blame', '-C', '-C', '-w', '--incremental',
> 		'--', $ls_tree->{'buf'});
I forgot to pass the revision to git-blame.
It should look like this:
 	($fh, $ctx) =
 	    $repo->command_output_pipe('blame', '-C', '-C', '-w', '--incremental',
 		$ARGV[0], '--', $ls_tree->{'buf'});
-Brandon
Brandon Casey· Oct 23, 2010, 22:48 UTC · re: Brandon Casey · lore

Re: git as an sfc member project

Here's an updated version of this script if anyone is interested.
It can now do the git-blame calls in parallel.  Use -t #threads.
Here's the usage info:
      ' -c     print count of all lines in all files at the end',
      ' -f     produce file centric output (overrides -l and -s)',
      ' -l     produce longer format',
      ' -s     produce short format, line count and author only',
      ' -ls    Both -l and -s produce an even longer long format',
      ' -t n   set number of threads to use',
      ' -x re  exclude files matching regex',
      ' -v     be more verbose',
Enjoy.
-Brandon
---
 git_blame_stats.perl |  387 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 387 insertions(+), 0 deletions(-)
 create mode 100755 git_blame_stats.perl
diff --git a/git_blame_stats.perl b/git_blame_stats.perl
new file mode 100755
index 0000000..618cf0a
--- /dev/null
+++ b/git_blame_stats.perl
@@ -0,0 +1,387 @@
+#!/usr/bin/perl -w
+
+use lib (split(/:/, $ENV{GITPERLLIB} || '/path/to/git/lib/perl5/site_perl/5.10.0'));
+
+use strict;
+use threads;
+use Thread::Queue;
+use Getopt::Std;
+use Git;
+
+my @LSTREE_OPTS = ('-r', '--name-only');
+my @BLAME_OPTS = ('-C', '-C', '-w', '--incremental');
+
+$Getopt::Std::STANDARD_HELP_VERSION = 1;
+$main::VERSION = 1.0;
+
+sub usage {
+	my $name;
+
+	eval {
+		require File::Basename;
+		$name = File::Basename::basename($0);
+	} or do {
+		$name = substr $0, rindex($0, '/') + 1;
+	};
+
+	print 'Usage: ', $name, ' [--help] [-cflstvx] <rev> [paths...]', "\n";
+}
+
+sub main::HELP_MESSAGE {
+	my $fh = shift;
+
+	eval {select $fh; usage};
+
+	local $\ = "\n";
+	local $, = "\n";
+
+	print $fh '',
+	      'Generate authorship statistics from a git repository.',
+	      '',
+	      'OPTIONS',
+	      ' -c     print count of all lines in all files at the end',
+	      ' -f     produce file centric output (overrides -l and -s)',
+	      ' -l     produce longer format',
+	      ' -s     produce short format, line count and author only',
+	      ' -ls    Both -l and -s produce an even longer long format',
+	      ' -t n   set number of threads to use',
+	      ' -x re  exclude files matching regex',
+	      ' -v     be more verbose',
+	      ' --help this text',
+	      '';
+}
+
+sub parse_blame_entry {
+	my $fh = shift;
+
+	return () unless defined($_ = <$fh>);
+	chomp;
+
+	my ($sha1, $sourceline, $resultline, $num_lines) = split;
+
+	return () unless defined $num_lines;
+
+	my %h = (sha1 => $sha1, sourceline => $sourceline,
+		 resultline => $resultline, lines => $num_lines);
+	while (<$fh>) {
+		chomp;
+		my ($key, $val) = split ' ', $_, 2;
+		$h{$key} = $val;
+		last if m/^filename /;
+	}
+
+	return %h;
+}
+
+sub blame_file {
+	my $repo = shift;
+	my $ref = shift;
+	my $filename = shift;
+	my $authors = shift;
+
+	my ($fh, $ctx) = $repo->command_output_pipe('blame', @BLAME_OPTS,
+		$ref, '--', $filename);
+
+	my %commits;
+	while (my %h = parse_blame_entry $fh) {
+
+		if (! exists $commits{$h{'sha1'}}) {
+
+			if (! exists $authors->{$h{'author'}}->{$filename}) {
+				$authors->{$h{'author'}}->{$filename} = 0;
+			}
+			$commits{$h{'sha1'}} =
+				\$authors->{$h{'author'}}->{$filename};
+		}
+
+		${$commits{$h{'sha1'}}} += $h{'lines'};
+	}
+
+	$repo->command_close_pipe($fh, $ctx);
+}
+
+sub count_total_lines {
+	my $authors = shift;
+
+	my $lines = 0;
+
+	for (values %{$authors}) {
+		for (values %{$_}) { $lines += $_; }
+	}
+
+	return $lines;
+}
+
+# Returns hash
+# key: author name
+# value: authored lines
+sub count_author_lines {
+	my $authors = shift;
+
+	my %alines;
+
+	foreach my $author (keys %{$authors}) {
+		my $lines = 0;
+		for (values %{$authors->{$author}}) { $lines += $_; }
+		$alines{$author} = $lines;
+	}
+
+	return %alines;
+}
+
+# Returns hash
+# key: filename
+# value: lines in file
+sub count_file_lines {
+	my $authors = shift;
+
+	my %flines;
+
+	for (values %{$authors}) {
+		foreach my $file (keys %{$_}) {
+			$flines{$file} += $_->{$file};
+		}
+	}
+
+	return %flines;
+}
+
+# Short format
+#   lines author
+sub print_short {
+	my $authors = shift;
+
+	my %alines = count_author_lines $authors;
+
+	foreach my $author (sort {$alines{$b} <=> $alines{$a}} keys %alines) {
+		printf "%6d  %s\n", $alines{$author}, $author;
+	}
+}
+
+# Long format
+# author (lines):
+#    file_lines filename
+#    file_lines filename
+#    file_lines filename
+sub print_long {
+	my $authors = shift;
+
+	my %alines = count_author_lines $authors;
+
+	foreach my $author (sort {$alines{$b} <=> $alines{$a}} keys %alines) {
+		print $author, ' (', $alines{$author}, '):', "\n";
+		foreach my $file (sort
+		    {$authors->{$author}->{$b} <=> $authors->{$author}->{$a}}
+		    keys %{$authors->{$author}}) {
+			printf "  %10d %s\n", $authors->{$author}->{$file},
+			      $file;
+		}
+	}
+}
+
+# Longer format
+# author (lines, % of all lines):
+#    file_lines (% of author lines) filename
+#    file_lines (% of author lines) filename
+sub print_longer {
+	my $authors = shift;
+
+	my %alines = count_author_lines $authors;
+	my $total_lines = count_total_lines $authors;
+
+	foreach my $author (sort {$alines{$b} <=> $alines{$a}} keys %alines) {
+		printf "%s (%d, %.2f%%):\n", $author, $alines{$author},
+			100. * $alines{$author} / $total_lines;
+		foreach my $file (sort
+		    {$authors->{$author}->{$b} <=> $authors->{$author}->{$a}}
+		    keys %{$authors->{$author}}) {
+			printf "  %10d (%5.2f%%) %s\n",
+			       $authors->{$author}->{$file},
+			       100. *
+			       $authors->{$author}->{$file} / $alines{$author},
+			       $file;
+		}
+	}
+}
+
+# Longer format
+# author (# lines in X files, % of all lines, % of all files):
+#    lines (% of file) file_lines (% of author lines) filename
+#    lines (% of file) file_lines (% of author lines) filename
+sub print_with_file_percentage {
+	my $authors = shift;
+
+	my %alines = count_author_lines $authors;
+	my %flines = count_file_lines $authors;
+	my $total_lines = count_total_lines $authors;
+	my $total_files = scalar(keys %flines);
+
+	foreach my $author (sort {$alines{$b} <=> $alines{$a}} keys %alines) {
+		printf "%s (%d lines in %d files, " .
+		       "%.2f%% of all lines, %.2f%% of all files):\n",
+		       $author, $alines{$author},
+		       scalar(keys %{$authors->{$author}}),
+		       100. * $alines{$author} / $total_lines,
+		       100. * scalar(keys %{$authors->{$author}})/$total_files;
+		foreach my $file (sort
+		    {$authors->{$author}->{$b} <=> $authors->{$author}->{$a}}
+		    keys %{$authors->{$author}}) {
+			printf "  %10d (%6.2f%%) of %6d (%6.2f%%) %s\n",
+			       $authors->{$author}->{$file},
+			       100. *
+			       $authors->{$author}->{$file} / $flines{$file},
+			       $flines{$file},
+			       100. *
+			       $authors->{$author}->{$file} / $alines{$author},
+			       $file;
+		}
+	}
+}
+
+# File perspective format
+# filename (lines):
+#    lines author
+#    lines author
+sub print_with_file_perspective {
+	my $authors = shift;
+
+	my %flines = count_file_lines $authors;
+
+	foreach my $file (sort keys %flines) {
+		my @auths = grep {exists $authors->{$_}->{$file}}
+			keys %{$authors};
+		print $file, ' (', $flines{$file}, '):', "\n";
+		foreach my $author (sort
+		    {$authors->{$b}->{$file} <=> $authors->{$a}->{$file}}
+		    @auths) {
+			printf " %10d %s\n", $authors->{$author}->{$file},
+				$author;
+		}
+	}
+}
+
+
+my $verbose = 0;
+my $output_format = 0;
+my $show_total = 0;
+my $exclude_pattern;
+my $nthreads = 1;
+
+our ($opt_c, $opt_f, $opt_l, $opt_s, $opt_t, $opt_v, $opt_x);
+getopts('cflst:vx:') or die 'Invalid options specified';
+
+	if ($opt_c) {
+		$show_total = 1;
+	}
+	if ($opt_f) {
+		$output_format = 4;
+	} elsif ($opt_l && $opt_s) {
+		$output_format = 3;
+	} elsif ($opt_l) {
+		$output_format = 2;
+	} elsif ($opt_s) {
+		$output_format = 1;
+	}
+	if (defined $opt_t) {
+		$nthreads = $opt_t;
+		if ($nthreads !~ /^\d+$/ || $nthreads < 0) {
+			die 'Error: argument to -t must be integer >= 0';
+		}
+		if ($nthreads == 0) {
+			eval {
+				require Sys::CPU;
+				$nthreads = Sys::CPU::cpu_count();
+			} or $nthreads = 1;
+		}
+	}
+	if ($opt_v) {
+		$verbose = 1;
+	}
+	if ($opt_x) {
+		$exclude_pattern = $opt_x;
+	}
+
+eval {select STDERR; usage; exit 1} unless $#ARGV >= 0;
+
+my %authors;
+my @thr;
+my $repo = Git->repository();
+
+# Spawn ls-tree now, so it can fail before creating the threads
+my ($fh, $ctx) = $repo->command_output_pipe('ls-tree', @LSTREE_OPTS,
+	'--', @ARGV);
+
+print STDERR 'Using ', $nthreads, ' thread(s).', "\n" if $verbose;
+
+my $DataQueue = Thread::Queue->new();
+
+# start the threads
+for (my $i = 0; $i < $nthreads; $i++) {
+	($thr[$i]) = threads->create(sub {
+		my $tid = threads->tid();
+		my %a;
+		while (my $f = $DataQueue->dequeue()) {
+			print STDERR "[$tid]Processing file: $f\n" if $verbose;
+			blame_file $repo, $ARGV[0], $f, \%a;
+		}
+		return %a;
+	});
+}
+
+# now queue up the files
+while (<$fh>) {
+	chomp;
+
+	if ($exclude_pattern && m/$exclude_pattern/o) {
+		print STDERR "Skipping file: $_\n" if $verbose;
+		next;
+	} else {
+		print STDERR "Queuing file: $_\n" if $verbose;
+	}
+
+	$DataQueue->enqueue($_);
+}
+$repo->command_close_pipe($fh, $ctx);
+
+# queue up an undef entry for each thread
+for (my $i = 0; $i < $nthreads; $i++) {
+	$DataQueue->enqueue(undef);
+}
+
+# merge the author hash from each thread
+for (my $i = 0; $i < $nthreads; $i++) {
+	my %th_authors = $thr[$i]->join;
+
+	foreach my $author (keys %th_authors) {
+		if (! exists $authors{$author}) {
+			$authors{$author} = $th_authors{$author};
+			next;
+		}
+		foreach my $filename (keys %{$th_authors{$author}}) {
+			if (! exists $authors{$author}->{$filename}) {
+				$authors{$author}->{$filename} =
+					$th_authors{$author}->{$filename};
+			} else {
+				$authors{$author}->{$filename} +=
+					$th_authors{$author}->{$filename};
+			}
+		}
+	}
+}
+
+
+if ($output_format == 0) {
+	print_long \%authors;
+} elsif ($output_format == 1) {
+	print_short \%authors;
+} elsif ($output_format == 2) {
+	print_longer \%authors;
+} elsif ($output_format == 3) {
+	print_with_file_percentage \%authors;
+} elsif ($output_format == 4) {
+	print_with_file_perspective \%authors;
+}
+
+printf "%6d  total lines\n", count_total_lines(\%authors) if $show_total;
+
+exit;
-- 
1.7.3.1.45.g9855b

← back to recent threads