Git API

Last modified by Admin on 2024/07/08 00:12

cogMake it easy to run Git commands in Java and from wiki pages
TypeJAR
Category
Developed by

Vincent Massol, XWiki Development Team

Active Installs13
Rating
0 Votes
LicenseGNU Lesser General Public License 2.1

Installable with the Extension Manager

Description

This module uses JGit and Gitective to provide the following APIs which can be used directly from within Script macros to easily perform Git actions:

import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.gitective.core.stat.UserCommitActivity;
...

   /**
     * Clone a Git repository by storing it locally in the XWiki Permanent directory. If the repository is already
     * cloned, no action is done.
     *
     * @param repositoryURI the URI to the Git repository to clone (eg "git://github.com/xwiki/xwiki-commons.git")
     * @param localDirectoryName the name of the directory where the Git repository will be cloned (this directory is
     *        relative to the permanent directory
     * @return the cloned Repository instance
     */

    Repository getRepository(String repositoryURI, String localDirectoryName);

   /**
     * Clone a protected Git repository by using the credentials provided by user and store it locally in the
     * XWiki Permanent directory. If the repository is already cloned, no action is done.
     *
     * @param repositoryURI the URI to the Git repository to clone (eg "git://github.com/xwiki/xwiki-commons.git")
     * @param localDirectoryName the name of the directory where the Git repository will be cloned (this directory is
     *        relative to the permanent directory
     * @param username the username of the Git user
     * @param accessCode the password or OAuth or personal access token that authenticates with the Git user
     * @return the cloned Repository instance
     * @since 9.9
     */

    Repository getRepository(String repositoryURI, String localDirectoryName, String username, String accessCode);

   /**
     * Clone a Git repository using the CloneCommand provided by user and store it locally in the
     * XWiki Permanent directory. If the repository is already cloned, no action is done.
     *
     * @param repositoryURI the URI to the Git repository to clone (eg "git://github.com/xwiki/xwiki-commons.git")
     * @param localDirectoryName the name of the directory where the Git repository will be cloned (this directory is
     *        relative to the permanent directory
     * @param cloneCommand the CloneCommand used for clone options
     * @return the cloned Repository instance
     * @since 9.10
     */

    Repository getRepository(String repositoryURI, String localDirectoryName, CloneCommand cloneCommand);

   /**
     * Create a CloneCommand object for custom clone options.
     *
     * @return the CloneCommand instance
     * @since 9.10
     */

    CloneCommand createCloneCommand();

   /**
     * Find all authors who have ever committed code in the passed repositories.
     *
     * @param repositories the list of repositories in which to look for authors
     * @return the list of authors who have ever contributed code in the passed repository
     */

    Set<PersonIdent> findAuthors(List<Repository> repositories);

   /**
     * Count commits done by all authors in the passed repositories and since the passed date.
     *
     * @param since the date from which to start counting. If null then counts from the beginning
     * @param repositories the list of repositories in which to look for commits
     * @return the author commit activity
     */

    UserCommitActivity[] countAuthorCommits(Date since, List<Repository> repositories);

The returned Repository is a org.eclipse.jgit.lib.Repository instance which can then be used to perform actions on that repository, as shown below in the examples.

Examples

The repositories are cloned only once (in XWiki's Permanent Directory, in a git subdirectory) and each time the script below is executed a pull is done. For better efficiency we recommend wrapping the Groovy macro with a Cache Macro.

Example 1: List Authors

{{velocity}}
#set ($repository =  $services.git.getRepository("https://github.com/xwiki/xwiki-commons.git", "xwiki/xwiki-commons"))
#set ($data = $services.git.findAuthors($repository))
#foreach ($authorCommits in $data)
  * $authorCommits.name <${authorCommits.emailAddress}>
#end
{{/velocity}}

Gives:

jgit-example0.png

Example 2: Count commits and Authors

Count number of commits done in the past year on the XWiki Commons and XWiki Rendering Git repositories and list all the authors who have participated in those commits.

The example below was before we added the new findAuthors API in XWiki 5.3M2. With this new API it's as simple as:

{{velocity}}
#set ($repository =  $services.git.getRepository("https://github.com/xwiki/xwiki-commons.git", "xwiki/xwiki-commons"))
#set ($data = $services.git.countAuthorCommits(365, $repository))
#foreach ($authorCommits in $data)
  * $authorCommits.name <${authorCommits.email}> - Count: ${authorCommits.count}
#end
{{/velocity}}

Result:

jgit-example1-0.png

Before 5.3M2 you had to write the following. We're keeping the example since it's interesting and useful if you need to do something slightly differently.

{{groovy}}
import org.apache.commons.io.*
import org.eclipse.jgit.api.*
import org.eclipse.jgit.lib.*
import org.eclipse.jgit.revwalk.*
import org.eclipse.jgit.storage.file.*
import org.gitective.core.*
import org.gitective.core.filter.commit.*

def service = services.get("git")

def commonsRepository = service.getRepository("git://github.com/xwiki/xwiki-commons.git", "xwiki-commons")
// Do a Git pull to get latest commits
new Git(commonsRepository).pull().call()

def renderingRepository = service.getRepository("git://github.com/xwiki/xwiki-rendering.git", "xwiki-rendering")
// Do a Git pull to get latest commits
new Git(renderingRepository).pull().call()

def finder = new CommitFinder(commonsRepository, renderingRepository)
def dateFilter = new CommitterDateFilter(System.currentTimeMillis() - 365*24*60*60*1000L)
def countFilter = new CommitCountFilter()
def authorFilter = new AuthorSetFilter()
def filters = new AndCommitFilter()
filters.add(dateFilter, countFilter, authorFilter)
finder.setFilter(filters)
finder.find()

println "There have been ${countFilter.count} commits in the past year!"
println ""

println "The following committers have participated in those commits:"
authorFilter.getPersons().each() {
    println "* ${it.name} (${it.emailAddress})"
}
{{/groovy}}

When executed this script returns:

jgit-example1.png

Example 3: Count Commit and Authors using repositories defined on GitHub

Same as above but taking the definition of repositories from a GitHub organization (see the GitHub Integration):

{{groovy}}
import org.apache.commons.io.*
import org.eclipse.jgit.api.*
import org.eclipse.jgit.lib.*
import org.eclipse.jgit.revwalk.*
import org.eclipse.jgit.storage.file.*
import org.gitective.core.*
import org.gitective.core.filter.commit.*
import groovy.json.*

def service = services.get("git")

// Get all XWiki organization repositories from GitHub
def url = "https://api.github.com/users/xwiki/repos".toURL().text
def root = new JsonSlurper().parseText(url)

// For each repo, clone it locally to get stats for it
def repos = []
root.each() { repoXML ->
  def repo = service.getRepository(repoXML.git_url, repoXML.name)
 new Git(repo).pull().call()
  repos.add(repo)
}

def finder = new CommitFinder(repos)
def dateFilter = new CommitterDateFilter(System.currentTimeMillis() - 365*24*60*60*1000L)
def countFilter = new CommitCountFilter()
def authorFilter = new AuthorSetFilter()
def filters = new AndCommitFilter()
filters.add(dateFilter, countFilter, authorFilter)
finder.setFilter(filters)
finder.find()

println "There have been ${countFilter.count} commits in the past year!"
println ""

println "The following committers have participated in those commits:"
authorFilter.getPersons().each() {
    println "* ${it.name} (${it.emailAddress})"
}
{{/groovy}}

Example 4: Count all commits for all committers for the past year

{{groovy}}
import org.apache.commons.io.*
import org.eclipse.jgit.api.*
import org.eclipse.jgit.lib.*
import org.eclipse.jgit.revwalk.*
import org.eclipse.jgit.storage.file.*
import org.gitective.core.*
import org.gitective.core.filter.commit.*
import org.gitective.core.stat.*

def service = services.get("git")

def commonsRepository = service.getRepository("git://github.com/xwiki/xwiki-commons.git", "xwiki-commons")
new Git(commonsRepository).pull().call()
def renderingRepository = service.getRepository("git://github.com/xwiki/xwiki-rendering.git", "xwiki-rendering")
new Git(renderingRepository).pull().call()
def platformRepository = service.getRepository("git://github.com/xwiki/xwiki-platform.git", "xwiki-platform")
new Git(platformRepository).pull().call()
def enterpriseRepository = service.getRepository("git://github.com/xwiki/xwiki-enterprise.git", "xwiki-enterprise")
new Git(enterpriseRepository).pull().call()
def managerRepository = service.getRepository("git://github.com/xwiki/xwiki-manager.git", "xwiki-manager")
new Git(managerRepository).pull().call()
def finder = new CommitFinder(commonsRepository, renderingRepository, platformRepository, enterpriseRepository, managerRepository)

def dateFilter = new CommitterDateFilter(System.currentTimeMillis() - 365*24*60*60*1000L)
def authorHistogramFilter = new AuthorHistogramFilter()
def filters = new AndCommitFilter()
filters.add(dateFilter, authorHistogramFilter)
finder.setFilter(filters)
finder.find()

// Sum up all commit counts and resolve users with aliases
def userStats = authorHistogramFilter.getHistogram().getUserActivity()
def mappings = ["tmortagne":"Thomas Mortagne"]
def userMap = [:]
userStats.each() {
  def realName = mappings.get(it.name) ?: it.name
  def count = userMap.get(realName) ?: 0
  count += it.count
  userMap.put(realName, count)
}
userMap = userMap.sort {a, b -> b.value <=> a.value}

println "|=Committer|=Number of commits"
userMap.each() { user, count ->
  println "|${user}|${count}"
}
println ""

println "{{chart type='bar3D' source='inline' params='range:B1-B${userMap.size()};series:columns;' title='Commit Stats for the past year' width='1400' height='800'}}"
userMap.each() { user, count ->
  println "|${user}|${count}"
}
println "{{/chart}}"
{{/groovy}}

When executed this script returns:

jgit-example3-1.png

jgit-example3-2.png

Example 5: Count diffs for each author

{{groovy}}
import org.eclipse.jgit.lib.*
import org.eclipse.jgit.revwalk.*
import org.gitective.core.*
import org.gitective.core.filter.commit.*
import org.eclipse.jgit.diff.*

def repo = services.git.getRepository("https://github.com/xwiki/xwiki-commons.git", "xwiki/xwiki-commons")

def finder = new CommitFinder(repo)
def dateFilter = new CommitterDateFilter(System.currentTimeMillis() - 365*24*60*60*1000L)

class AuthorDiffFilter extends CommitDiffEditFilter
{
   private Map<String, Long[]> counters = new HashMap<>()

   public AuthorDiffFilter()
   {
       super()
   }

   public AuthorDiffFilter(final boolean detectRenames)
   {
       super(detectRenames)
   }

   protected boolean include(RevCommit commit, DiffEntry diff, Edit hunk)
   {
        PersonIdent person = commit.getAuthorIdent()
        Long[] counter = counters.get(person.name)
       if (counter == null) {
            counter = new Long[3]
            counter[0] = counter[1] = counter[2] = 0
       }  

       switch (hunk.getType()) {
           case Edit.Type.DELETE:
                counter[0] += hunk.getLengthA()
                counters.put(person.name, counter)
               break;
           case Edit.Type.INSERT:
                counter[1] += hunk.getLengthB()
                counters.put(person.name, counter)
               break;
           case Edit.Type.REPLACE:
                counter[2] += hunk.getLengthB();
                counters.put(person.name, counter)
               break;
       }
       return true;
   }
}

def customDiffFilter = new AuthorDiffFilter()
def filters = new AndCommitFilter()
filters.add(dateFilter, customDiffFilter)
finder.setFilter(filters)
finder.find()

customDiffFilter.counters.each{ name, counter ->
  println "* ${name} - Deleted Lines: ${counter[0]} - Added Lines: ${counter[1]} - Modified Lines: ${counter[2]}"
}
{{/groovy}}

When executed this script returns:

jgit-example5.png

Example 6: Save repository as bare using custom CloneCommand

{{velocity}}
#set ($cmd = $services.git.createCloneCommand())
#set ($discard = $cmd.setBare(1))
#set ($repository =  $services.git.getRepository("https://github.com/xwiki/xwiki-commons.git", "xwiki/xwiki-commons", $cmd))
{{/velocity}}

Gives:

A Bare repository in data/git folder.

Prerequisites & Installation Instructions

We recommend using the Extension Manager to install this extension (Make sure that the text "Installable with the Extension Manager" is displayed at the top right location on this page to know if this extension can be installed with the Extension Manager).

You can also use the manual method which involves dropping the JAR file and all its dependencies into the WEB-INF/lib folder and restarting XWiki.


If you want to install/upgrade to a more recent version (>=4.2-milestone-1) of this extension through the Extension Manager then you have to use the advanced search to look for org.xwiki.platform:xwiki-platform-git extension ID and a specific version. This limitation comes from the fact that starting with version 4.2-milestone-1 this extension is stored in the Maven extension repository which currently doesn't support keyword search so you can't use the simple search form of the Extension Manager.

Release Notes

v9.10

v9.9

v9.8

v9.7

v9.6

v9.5

This was the last release when the Git API was included in XWiki Platform.

Dependencies

Dependencies for this extension (org.xwiki.contrib:api-git 9.10):

Get Connected