Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "JGit/User Guide"

(Wrap remaining code examples in source tag)
(Snippets)
(31 intermediate revisions by 6 users not shown)
Line 1: Line 1:
= Porcelain API =
+
= Getting Started =
  
While JGit contains a lot of low level code to work with Git repositories, it also contains a higher level API that mimics some of the Git porcelain commands in the ''org.eclipse.jgit.api'' package.  
+
If you're new to Git or distributed version control systems generally, then you might want to read [[EGit/Git For Eclipse Users|Git for Eclipse Users]] first. If you need more details and background read the [http://book.git-scm.com Git Community Book] or [http://progit.org/book/ Git Pro].
  
Most users of JGit should start here.
+
== Taking JGit for a Spin ==
  
== AddCommand (git-add) ==
+
Although you are probably interested in JGit because you want to integrate it into an existing application or create a tool, JGit is more than simply a Java library for working with git repository. So before diving into the different aspects of the library let's take JGit for a spin.
  
AddCommand allows you to add files to the index and has options available via its setter methods.
+
You are probably familiar with the git command line interface (CLI) that can be used from the shell or in scripts. JGit comes with its own small CLI, which, although not as feature-full as the git CLI, is a good way to showcase what JGIt can do. Furthermore, the programs serve as an excellent source of inspiration for how to accomplish different tasks.  
  
* addFilepattern()
+
=== Building the JGit CLI ===
  
Here's a quick example of how to add a set of files to the index using the porcelain API.
+
Assuming that you have the EGit git repository cloned and ready, build the jgit binary by running the jgit maven build (see [[EGit/Contributor_Guide#JGit | the Contributor Guide]]):
  
<source lang="java">
+
~/src/jgit$ mvn clean install
Git git = new Git(db);
+
AddCommand add = git.add();
+
add.addFilepattern("someDirectory").call();
+
</source>
+
  
== CommitCommand (git-commit) ==
+
Find the jgit binary here (path relative to root of working tree of your clone of the jgit repository):
 +
org.eclipse.jgit.pgm/target/jgit
  
CommitCommand allows you to perform commits and has options available via its setter methods.
+
Check your build by running the "version" command:
  
* setAuthor()
+
prompt$ ./jgit version
* setCommitter()
+
jgit version 0.10.0-SNAPSHOT
* setAll()
+
  
Here's a quick example of how to commit using the porcelain API.
+
If you want to use jgit frequently you may consider to ease running it via a symbolic link (usually goes under /usr/local/bin)
 +
sudo ln -s /path/to/jgit /usr/local/bin/jgit
  
<source lang="java">
+
=== Running the JGit CLI ===
Git git = new Git(db);
+
CommitCommand commit = git.commit();
+
commit.setMessage("initial commit").call();
+
</source>
+
  
== TagCommand (git-tag) ==
+
==== Overview ====
 +
When given the '''-h''' flag, commands provide a helpful message listing what flags they support.
  
TagCommand supports a variety of tagging options through its setter methods.
+
prompt$ ./jgit version -h
 +
jgit version [--help (-h)]
 +
 +
  --help (-h) : display this help text
  
* setName()
+
Running '''jgit''' with no arguments lists the most commonly used commands.
* setMessage()
+
* setTagger()
+
* setObjectId()
+
* setForceUpdate()
+
* setSigned() - not supported yet, will throw exception
+
  
Here's a quick example of how to tag a commit using the porcelain API.
+
prompt$ ./jgit
 +
jgit --git-dir GIT_DIR --help (-h) --show-stack-trace command [ARG ...]
 +
 +
The most commonly used commands are:
 +
  branch  List, create, or delete branches
 +
  clone    Clone a repository into a new directory
 +
  commit  Record changes to the repository
 +
  daemon  Export repositories over git://
 +
  diff    Show diffs
 +
  fetch    Update remote refs from another repository
 +
  init    Create an empty git repository
 +
  log      View commit history
 +
  push    Update remote repository from local refs
 +
  rm      Stop tracking a file
 +
  tag     Create a tag
 +
  version  Display the version of jgit
  
<source lang="java">
+
The commands are modeled after their corresponding command in the git CLI.  
Git git = new Git(db);
+
We will not cover all the commands here, but simply give some examples.
RevCommit commit = git.commit().setMessage("initial commit").call();
+
RevTag tag = git.tag().setName("tag").call();
+
</source>
+
  
== LogCommand (git-log) ==
+
'''jgit''' also provides a number of debug and test commands, to list all the available commands run
  
LogCommand allows you to easily walk a commit graph.
+
prompt$ ./jgit debug-show-commands
  
* add(AnyObjectId start)
+
====Inspecting the Repository====
* addRange(AnyObjectId since, AnyObjectId until)
+
  
Here's a quick example of how get some log messages.
+
Before inspecting the most recent commits, you probably want to know which branches the repository contains and what branch is currently checked out. Using the branch commands -v flag, you get a small summary of branches, their revision, and the first line of the revision's commit message.
  
<source lang="java">
+
prompt$ ./jgit branch -v
Git git = new Git(db);
+
  master      4d4adfb Git Project import: don't hide but gray out existing projects
LogCommand log = git.log().call();
+
* traceHistory 6b9fe04 [historyView] Add trace instrumentation
</source>
+
  
== MergeCommand (git-merge) ==
+
The log command, like [http://www.kernel.org/pub/software/scm/git/docs/git-log.html git-log(1)], shows the commit log. For example,
  
TODO
+
prompt$ ./jgit log --author Matthias --grep tycho master
 +
commit 482442b599abf75b63b397680aaff09c4e48c0ed
 +
Author: Matthias Sohn <matthias.sohn@sap.com>
 +
Date:  Fri Oct 08 10:58:52 2010 +0200
 +
 +
    Update build to use tycho 0.10.0
 +
...
 +
will show you all commits in the "master" branch, where the author name matches "Matthias" and the commit messages contains the word tycho. More search criteria to filter the commit log, such as committer name, can be given.
 +
 
 +
==== Graphical History View ====
 +
 
 +
Finally, to show some of the graphical capabilities of JGit, we will end this small tour by launching the graphical log tool.
 +
 
 +
prompt$ ./jgit glog
 +
 
 +
This should give you a window with the revision graph plotted to the left and three columns containing the first line of the message, the author name, and the commit date.
 +
 
 +
[[Image:Jgit-glog.png]]
 +
 
 +
= Concepts =
  
= API =
+
== API ==
  
== Repository ==
+
=== Repository ===
  
 
A '''Repository''' holds all objects and refs used for managing source code.
 
A '''Repository''' holds all objects and refs used for managing source code.
Line 81: Line 102:
  
 
<source lang="java">
 
<source lang="java">
FileRepositoryBuilder builder = new RepositoryBuilder();
+
FileRepositoryBuilder builder = new FileRepositoryBuilder();
Repository repository = builder.setGitDir("/my/git/directory")
+
Repository repository = builder.setGitDir(new File("/my/git/directory"))
.readEnvironment() // scan environment GIT_* variables
+
  .readEnvironment() // scan environment GIT_* variables
.findGitDir() // scan up the file system tree
+
  .findGitDir() // scan up the file system tree
.build();
+
  .build();
 
</source>
 
</source>
  
== Git Objects ==
+
=== Git Objects ===
  
 
All objects are represented by a SHA-1 id in the Git object model. In JGit, this is represented by the '''AnyObjectId''' and '''ObjectId''' classes.  
 
All objects are represented by a SHA-1 id in the Git object model. In JGit, this is represented by the '''AnyObjectId''' and '''ObjectId''' classes.  
Line 105: Line 126:
 
To resolve an object from a repository, simply pass in the right revision string.
 
To resolve an object from a repository, simply pass in the right revision string.
  
<source lang="java">
+
ObjectId head = repository.resolve("HEAD");
ObjectId head = repository.resolve("HEAD");
+
</source>
+
  
== Ref ==
+
=== Ref ===
  
 
A ref is a variable that holds a single object identifier. The object identifier can be any valid Git object (blob, tree, commit, tag).
 
A ref is a variable that holds a single object identifier. The object identifier can be any valid Git object (blob, tree, commit, tag).
Line 115: Line 134:
 
For example, to query for the reference to head, you can simply call
 
For example, to query for the reference to head, you can simply call
  
<source lang="java">
+
Ref HEAD = repository.getRef("refs/heads/master");
Ref HEAD = repository.getRef("refs/heads/master");
+
</source>
+
  
== RevWalk ==
+
=== RevWalk ===
  
 
A '''RevWalk''' walks a commit graph and produces the matching commits in order.
 
A '''RevWalk''' walks a commit graph and produces the matching commits in order.
  
<source lang="java">
+
RevWalk walk = new RevWalk(repository);
RevWalk walk = new RevWalk(repository);
+
</source>
+
  
 
TODO talk about filters
 
TODO talk about filters
  
== RevCommit ==
+
=== RevCommit ===
  
 
A '''RevCommit''' represents a commit in the Git object model.
 
A '''RevCommit''' represents a commit in the Git object model.
Line 135: Line 150:
 
To parse a commit, simply use a '''RevWalk''' instance:
 
To parse a commit, simply use a '''RevWalk''' instance:
 
   
 
   
<source lang="java">
+
RevWalk walk = new RevWalk(repository);
RevWalk walk = new RevWalk(repository);
+
RevCommit commit = walk.parseCommit(objectIdOfCommit);
RevCommit commit = walk.parseCommit(objectIdOfCommit);
+
</source>
+
  
== RevTag ==
+
=== RevTag ===
  
 
A '''RevTag''' represents a tag in the Git object model.
 
A '''RevTag''' represents a tag in the Git object model.
Line 146: Line 159:
 
To parse a tag, simply use a '''RevWalk''' instance:
 
To parse a tag, simply use a '''RevWalk''' instance:
 
   
 
   
<source lang="java">
+
RevWalk walk = new RevWalk(repository);
RevWalk walk = new RevWalk(repository);
+
RevTag tag = walk.parseTag(objectIdOfTag);
RevTag tag = walk.parseTag(objectIdOfTag);
+
</source>
+
  
== RevTree ==
+
=== RevTree ===
  
 
A '''RevTree''' represents a tree in the Git object model.
 
A '''RevTree''' represents a tree in the Git object model.
  
To parse a commit, simply use a '''RevWalk''' instance:
+
To parse a tree, simply use a '''RevWalk''' instance:
 
   
 
   
<source lang="java">
+
RevWalk walk = new RevWalk(repository);
RevWalk walk = new RevWalk(repository);
+
RevTree tree = walk.parseTree(objectIdOfTree);
RevTree tree = walk.parseTree(objectIdOfTree);
+
</source>
+
  
= API Changes =
+
= Reference =
  
JGit is still in incubation hence we sometimes do incompatible API changes to reach a better stable API.
+
== Porcelain API ==
  
<br>These are listed here as a reference for other projects depending on the JGit API.
+
While JGit contains a lot of low level code to work with Git repositories, it also contains a higher level API that mimics some of the Git porcelain commands in the ''org.eclipse.jgit.api'' package.  
  
== Changes in JGit 0.9 ==
+
Most users of JGit should start here.
  
''RepositoryConfig removed'':  Use Config or FileBasedConfig instead.  To replace getCore(), getTransfer() and getUserConfig() methods, use config.get(CoreConfig.KEY), config.get(TransferConfig.KEY), and config.get(UserConfig.KEY).  To replace getAuthorName() and friends, use the UserConfig object returned by config.get(UserConfig.KEY).
+
=== AddCommand (git-add) ===
  
''Repository class is abstract'':  Use a RepositoryBuilder.
+
AddCommand allows you to add files to the index and has options available via its setter methods.
  
''Repository constructors removed'':  To create a Repository instance, use a RepositoryBuilder.  If you know it must be a classical local file system based Repository (as opposed to other types that JGit will support in the future), you can use the FileRepositoryBuilder instead to ensure its a FileRepository that is returned.
+
* addFilepattern()
  
''Repository.getDirectory() can return null'':  It is no longer a requirement that every Repository instance has a java.io.File associated with it.  In the future some types of Git repositories that are not on the local filesystem will be supported, and those types will return null.
+
Here's a quick example of how to add a set of files to the index using the porcelain API.
  
''Repository.getWorkDir() renamed'': The method is now called getWorkTree().
+
Git git = new Git(db);
 +
AddCommand add = git.add();
 +
  add.addFilepattern("someDirectory").call();
  
''Repository.openObject(), openBlob(), renamed'':  To read an object, use Repository.open() or repository.newObjectReader() to get a reader and use the reader's open() method.  An ObjectReader is preferred if the application will access several objects in a short time span (e.g. in response to the current UI event, or the current network connection).
+
=== CommitCommand (git-commit) ===
  
''Repository.hasObject() renamed'':  To check if an object exists, use has(AnyObjectId) instead.
+
CommitCommand allows you to perform commits and has options available via its setter methods.
  
''Repository.open() checks existence, type'':  The open object methods on Repository, ObjectDatabase and ObjectReader now check that an object exists, and if not throws ObjectNotFoundException.  If the type hint is supplied, they also validate that the object is actually of the type hint, or throw IncorrectObjectTypeException.  This simplifies most application code, as null is no longer a valid return value.
+
* setAuthor()
 +
* setCommitter()
 +
* setAll()
  
''Repository event listener changes'':  Repository events are now delivered through a completely different API.  Each event type has a corresponding Listener interface to receive that event, and listeners must be registered through the Repository.getListenerList().add*Listener().
+
Here's a quick example of how to commit using the porcelain API.
  
''RepositoryState value BARE added'': To correctly denote a bare repository whose work tree state is undefined, the enum RepositoryState returned by repository.getRepositoryState() returns BARE when isBare() is true or getDirectory() returns null.
+
  Git git = new Git(db);
 +
CommitCommand commit = git.commit();
 +
commit.setMessage("initial commit").call();
  
''WindowCursor removed'':  Instead use repository.newObjectReader(), and examine objects through the methods on the returned ObjectReader.  Please note that an ObjectReader must be released with its release() method after it is no longer useful to the application.
+
=== TagCommand (git-tag) ===
  
''RevWalk requires release'':  RevWalk now embeds an ObjectReader, and therefore must be released through its release() method when it is no longer required by the application that created it.  Optionally the caller can now specify the ObjectReader the walker should use, allowing the caller to more explicitly manage the release.
+
TagCommand supports a variety of tagging options through its setter methods.
  
''TreeWalk requires release'':  TreeWalk now embeds an ObjectReader, and therefore must be released through its release() method when it is no longer required by the application that created it.  Optionally the caller can now specify the ObjectReader the walker should use, allowing the caller to more explicitly manage the release.
+
* setName()
 +
* setMessage()
 +
* setTagger()
 +
* setObjectId()
 +
* setForceUpdate()
 +
* setSigned() - not supported yet, will throw exception
  
''ObjectWriter deprecated'':  ObjectWriter will be removed in a future version of JGit.  Applications are strongly encouraged to switch to the ObjectInserter API, which can be obtained from repository.newObjectInserter().  Like the ObjectReader, an ObjectInserter must be released through its release() method after use.
+
Here's a quick example of how to tag a commit using the porcelain API.
  
''NoWorkTreeException thrown'': A bare repository (one without a working directory) will throw NoWorkTreeException if its getIndexFile(), getIndex(), getWorkTree(), readCommitMessage(), or readMergeMessage() is called on it, or if its corresponding DirCache is read or locked.  This is a RuntimeException so applications need to be careful about knowing what the return value of repository.isBare() is for any given repository they operate on.
+
Git git = new Git(db);
 +
RevCommit commit = git.commit().setMessage("initial commit").call();
 +
RevTag tag = git.tag().setName("tag").call();
 +
 
 +
=== LogCommand (git-log) ===
 +
 
 +
LogCommand allows you to easily walk a commit graph.
 +
 
 +
* add(AnyObjectId start)
 +
* addRange(AnyObjectId since, AnyObjectId until)
 +
 
 +
Here's a quick example of how get some log messages.
 +
 
 +
Git git = new Git(db);
 +
Iterable<RevCommit> log = git.log().call();
 +
 
 +
=== MergeCommand (git-merge) ===
 +
 
 +
TODO
 +
 
 +
== Ant Tasks ==
 +
 
 +
JGit has Ant tasks for some common tasks contained in the '''org.eclipse.jgit.ant''' bundle.
 +
 
 +
To use these tasks:
 +
 
 +
    <taskdef resource="org/eclipse/jgit/ant/ant-tasks.properties">
 +
        <classpath>
 +
          <pathelement location="path/to/org.eclipse.jgit.ant-VERSION.jar"/>
 +
          <pathelement location="path/to/org.eclipse.jgit-VERSION.jar"/>
 +
          <pathelement location="path/to/jsch-0.1.44-1.jar"/>
 +
        </classpath>
 +
    </taskdef>
 +
 
 +
This would then provide git-clone, git-init and git-checkout tasks.
 +
 
 +
=== git-clone ===
 +
 
 +
    <git-clone uri="http://egit.eclipse.org/jgit.git" />
 +
 
 +
The following attributes are required:
 +
* uri: the uri to clone from
 +
 
 +
The following attributes are optional:
 +
* dest: the destination to clone to (defaults to use a human readable directory name based on the last path component of the uri)
 +
* bare: true/false/yes/no to indicate if the cloned repository should be bare or not (defaults to false)
 +
* branch: the initial branch to check out when cloning the repository (defaults to HEAD)
 +
 
 +
=== git-init ===
 +
 
 +
    <git-init />
 +
 
 +
The following attributes are optional:
 +
* dest: the path where a git repository is initialized (defaults $GIT_DIR or the current directory)
 +
* bare: true/false/yes/no to indicate if the repository should be bare or not (defaults to false)
 +
 
 +
=== git-checkout ===
 +
 
 +
    <git-checkout src="path/to/repo" branch="origin/experimental" />
 +
 
 +
The following attributes are required:
 +
* src: the path to the git repository
 +
* branch: the initial branch to checkout
 +
 
 +
The following attributes are optional:
 +
* createbranch: true/false/yes/no to indicate whether the branch should be created if it does not already exist (defaults to false)
 +
* force: true/false/yes/no: if true/yes and the branch with the given name already exists, the start-point of an existing branch will be set to a new start-point; if false, the existing branch will not be changed (defaults to false)
 +
 
 +
=== git-add ===
 +
 
 +
TODO
 +
 
 +
= Snippets =
 +
 
 +
== Finding children of a commit ==
 +
 
 +
<pre>
 +
PlotWalk revWalk = new PlotWalk(repo());
 +
ObjectId rootId = (branch==null)?repo().resolve(HEAD):branch.getObjectId();
 +
RevCommit root = revWalk.parseCommit(rootId);
 +
revWalk.markStart(root);
 +
PlotCommitList<PlotLane> plotCommitList = new PlotCommitList<PlotLane>();
 +
plotCommitList.source(revWalk);
 +
plotCommitList.fillTo(Integer.MAX_VALUE);
 +
return revWalk;
 +
</pre>
 +
 
 +
== Snippet Collection ==
 +
 
 +
There is a collection of ready-to-run JGit code snippets available at https://github.com/centic9/jgit-cookbook
 +
 
 +
= Advanced Topics =
 +
 
 +
== Reducing memory usage with RevWalk ==
 +
 
 +
The revision walk interface and the RevWalk and RevCommit
 +
classes are designed to be light-weight. However, when used
 +
with any repository of considerable size they may still
 +
require a lot of memory. This section provides hints on what
 +
you can do to reduce memory when walking the revision graph.
 +
 
 +
=== Restrict the walked revision graph ===
 +
 
 +
Try to walk only the amount of the graph you
 +
actually need to walk.  That is, if you are looking for the commits in
 +
refs/heads/master not yet in refs/remotes/origin/master, make sure you
 +
markStart() for refs/heads/master and markUninteresting()
 +
refs/remotes/origin/master.  The RevWalk traversal will only parse the
 +
commits necessary for it to answer you, and will try to avoid looking
 +
back further in history.  That reduces the size of the internal object
 +
map, and thus reduces overall memory usage.
 +
 
 +
RevWalk walk = new RevWalk(repository);
 +
ObjectId from = repository.resolve("refs/heads/master");
 +
ObjectId to = repository.resolve("refs/remotes/origin/master");
 +
 +
walk.markStart(walk.parseCommit(from));
 +
walk.markUninteresting(walk.parseCommit(to));
 +
 +
// ...
 +
 
 +
=== Discard the body of a commit ===
 +
 
 +
There is a setRetainBody(false) method you can use to discard the body
 +
of a commit if you don't need the author, committer or message
 +
information during the traversal.  Examples of when you don't need
 +
this data is when you are only using the RevWalk to compute the merge
 +
base between branches, or to perform a task you would have used `git
 +
rev-list` with its default formatting for.
 +
 
 +
  RevWalk walk = new RevWalk(repository);
 +
walk.setRetainBody(false);
 +
// ...
 +
 
 +
If you do need the body, consider extracting the data you need and
 +
then calling dispose() on the RevCommit, assuming you only need the
 +
data once and can then discard it. If you need to hang onto the data,
 +
you may find that JGit's internal representation uses less overall
 +
memory than if you held onto it yourself, especially if you want the
 +
full message. This is because JGit uses a byte[] internally to store the
 +
message in UTF-8.  Java String storage would be bigger using UTF-16,
 +
assuming the message is mostly US-ASCII data.
 +
 
 +
RevWalk walk = new RevWalk(repository);
 +
// more setup
 +
Set<String> authorEmails = new HashSet<String>();
 +
 +
for (RevCommit commit : walk) {
 +
// extract the commit fields you need, for example:
 +
authorEmails.add(commit.getAuthorIdent().getEmailAddress());
 +
 
 +
  commit.dispose();
 +
}
 +
 
 +
=== Subclassing RevWalk and RevCommit ===
 +
 
 +
If you need to attach additional data to a commit, consider
 +
subclassing both RevWalk and RevCommit, and using the createCommit()
 +
method in RevWalk to consruct an instance of your RevCommit subclass.
 +
Put the additional data as fields in your RevCommit subclass, so that
 +
you don't need to use an auxiliary HashMap to translate from RevCommit
 +
or ObjectId to your additional data fields.
 +
 
 +
public class ReviewedRevision extends RevCommit {
 +
 +
private final Date reviewDate;
 +
 +
private ReviewedRevision(AnyObjectId id, Date reviewDate) {
 +
super(id);
 +
this.reviewDate = reviewDate;
 +
}
 +
 +
public List<String> getReviewedBy() {
 +
return getFooterLines("Reviewed-by");
 +
}
 +
 +
public Date getReviewDate() {
 +
return reviewDate;
 +
}
 +
 +
public static class Walk extends RevWalk {
 +
 +
public Walk(Repository repo) {
 +
super(repo);
 +
}
 +
 +
@Override
 +
protected RevCommit createCommit(AnyObjectId id) {
 +
return new ReviewedRevision(id, getReviewDate(id));
 +
}
 +
 +
private Date getReviewDate(AnyObjectId id) {
 +
// ...
 +
}
 +
 +
}
 +
}
  
''DirCache read(), lock() moved'':  The methods were moved to Repository, to better permit a specific repository implementation to manage how their DirCache should be accessed.
+
=== Cleaning up after a revision walk ===
  
''ObjectLoader getCachedBytes(), getBytes() can throw LargeObjectException'': If an object is bigger than core.streamFileThreshold, it cannot be accessed as a contiguous array of bytesLargeObjectException is thrown from the accessors, and applications must use openStream() or copyTo() on the ObjectLoader to obtain the object's contents. By default core.streamFileThreshold is 1 MiB, but is capped at no more than 1/4 of the JVM maximum heap size.
+
A RevWalk cannot shrink its internal object map. If you have just
 +
done a huge traversal of say all history of the repository, that will
 +
load everything into the object map, and it cannot be releasedIf
 +
you don't need this data in the near future, it may be a good idea to
 +
throw away the RevWalk and allocate a new one for your next traversal.
 +
That will let the GC reclaim everything and make it available for
 +
another use. On the other hand, reusing an existing object map is
 +
much faster than building a new one from scratch.  So you need to
 +
balance the reclaiming of memory against the user's desire to perform
 +
fast updates of an existing repository view.
  
''RevObject.equals() by identity check replaced by AnyObjectId.equals() by value comparison'': equals() was not consistent across AnyObjectId's class hierarchy which defines value object semantics by using instanceof type check and value comparison while RevObject which is a subclass of AnyObjectId overrode equals() with identity check semantics. This broke the symmetry and transititivity properties of the equals contract defined in javadoc for Object.equals(). To fix that RevObject.equals() was backed out and AnyObjectId.equals() made final. Applications that were depending on reference equality of RevObjects should now use == and not .equals().
+
RevWalk walk = new RevWalk(repository);
 +
// ...
 +
for (RevCommit commit : walk) {
 +
// ...
 +
}
 +
walk.dispose();

Revision as of 03:50, 2 August 2013

Getting Started

If you're new to Git or distributed version control systems generally, then you might want to read Git for Eclipse Users first. If you need more details and background read the Git Community Book or Git Pro.

Taking JGit for a Spin

Although you are probably interested in JGit because you want to integrate it into an existing application or create a tool, JGit is more than simply a Java library for working with git repository. So before diving into the different aspects of the library let's take JGit for a spin.

You are probably familiar with the git command line interface (CLI) that can be used from the shell or in scripts. JGit comes with its own small CLI, which, although not as feature-full as the git CLI, is a good way to showcase what JGIt can do. Furthermore, the programs serve as an excellent source of inspiration for how to accomplish different tasks.

Building the JGit CLI

Assuming that you have the EGit git repository cloned and ready, build the jgit binary by running the jgit maven build (see the Contributor Guide):

~/src/jgit$ mvn clean install

Find the jgit binary here (path relative to root of working tree of your clone of the jgit repository):

org.eclipse.jgit.pgm/target/jgit

Check your build by running the "version" command:

prompt$ ./jgit version
jgit version 0.10.0-SNAPSHOT

If you want to use jgit frequently you may consider to ease running it via a symbolic link (usually goes under /usr/local/bin)

sudo ln -s /path/to/jgit /usr/local/bin/jgit

Running the JGit CLI

Overview

When given the -h flag, commands provide a helpful message listing what flags they support.

prompt$ ./jgit version -h
jgit version [--help (-h)]

 --help (-h) : display this help text

Running jgit with no arguments lists the most commonly used commands.

prompt$ ./jgit
jgit --git-dir GIT_DIR --help (-h) --show-stack-trace command [ARG ...]

The most commonly used commands are:
 branch   List, create, or delete branches
 clone    Clone a repository into a new directory
 commit   Record changes to the repository
 daemon   Export repositories over git://
 diff     Show diffs
 fetch    Update remote refs from another repository
 init     Create an empty git repository
 log      View commit history
 push     Update remote repository from local refs
 rm       Stop tracking a file
 tag      Create a tag
 version  Display the version of jgit

The commands are modeled after their corresponding command in the git CLI. We will not cover all the commands here, but simply give some examples.

jgit also provides a number of debug and test commands, to list all the available commands run

prompt$ ./jgit debug-show-commands

Inspecting the Repository

Before inspecting the most recent commits, you probably want to know which branches the repository contains and what branch is currently checked out. Using the branch commands -v flag, you get a small summary of branches, their revision, and the first line of the revision's commit message.

prompt$ ./jgit branch -v
  master       4d4adfb Git Project import: don't hide but gray out existing projects
* traceHistory 6b9fe04 [historyView] Add trace instrumentation

The log command, like git-log(1), shows the commit log. For example,

prompt$ ./jgit log --author Matthias --grep tycho master
commit 482442b599abf75b63b397680aaff09c4e48c0ed
Author: Matthias Sohn <matthias.sohn@sap.com>
Date:   Fri Oct 08 10:58:52 2010 +0200

    Update build to use tycho 0.10.0
...

will show you all commits in the "master" branch, where the author name matches "Matthias" and the commit messages contains the word tycho. More search criteria to filter the commit log, such as committer name, can be given.

Graphical History View

Finally, to show some of the graphical capabilities of JGit, we will end this small tour by launching the graphical log tool.

prompt$ ./jgit glog

This should give you a window with the revision graph plotted to the left and three columns containing the first line of the message, the author name, and the commit date.

Jgit-glog.png

Concepts

API

Repository

A Repository holds all objects and refs used for managing source code.

To build a repository, you invoke flavors of RepositoryBuilder.

FileRepositoryBuilder builder = new FileRepositoryBuilder();
Repository repository = builder.setGitDir(new File("/my/git/directory"))
  .readEnvironment() // scan environment GIT_* variables
  .findGitDir() // scan up the file system tree
  .build();

Git Objects

All objects are represented by a SHA-1 id in the Git object model. In JGit, this is represented by the AnyObjectId and ObjectId classes.

There are four types of objects in the Git object model:

  • blob
    • is used to store file data
  • tree
    • can be thought of as a directory; it references other trees and blobs
  • commit
    • a commit points to a single tree
  • tag
    • marks a commit as special; generally used to mark specific releases

To resolve an object from a repository, simply pass in the right revision string.

ObjectId head = repository.resolve("HEAD");

Ref

A ref is a variable that holds a single object identifier. The object identifier can be any valid Git object (blob, tree, commit, tag).

For example, to query for the reference to head, you can simply call

Ref HEAD = repository.getRef("refs/heads/master");

RevWalk

A RevWalk walks a commit graph and produces the matching commits in order.

RevWalk walk = new RevWalk(repository);

TODO talk about filters

RevCommit

A RevCommit represents a commit in the Git object model.

To parse a commit, simply use a RevWalk instance:

RevWalk walk = new RevWalk(repository);
RevCommit commit = walk.parseCommit(objectIdOfCommit);

RevTag

A RevTag represents a tag in the Git object model.

To parse a tag, simply use a RevWalk instance:

RevWalk walk = new RevWalk(repository);
RevTag tag = walk.parseTag(objectIdOfTag);

RevTree

A RevTree represents a tree in the Git object model.

To parse a tree, simply use a RevWalk instance:

RevWalk walk = new RevWalk(repository);
RevTree tree = walk.parseTree(objectIdOfTree);

Reference

Porcelain API

While JGit contains a lot of low level code to work with Git repositories, it also contains a higher level API that mimics some of the Git porcelain commands in the org.eclipse.jgit.api package.

Most users of JGit should start here.

AddCommand (git-add)

AddCommand allows you to add files to the index and has options available via its setter methods.

  • addFilepattern()

Here's a quick example of how to add a set of files to the index using the porcelain API.

Git git = new Git(db);
AddCommand add = git.add();
add.addFilepattern("someDirectory").call();

CommitCommand (git-commit)

CommitCommand allows you to perform commits and has options available via its setter methods.

  • setAuthor()
  • setCommitter()
  • setAll()

Here's a quick example of how to commit using the porcelain API.

Git git = new Git(db);
CommitCommand commit = git.commit();
commit.setMessage("initial commit").call();

TagCommand (git-tag)

TagCommand supports a variety of tagging options through its setter methods.

  • setName()
  • setMessage()
  • setTagger()
  • setObjectId()
  • setForceUpdate()
  • setSigned() - not supported yet, will throw exception

Here's a quick example of how to tag a commit using the porcelain API.

Git git = new Git(db);
RevCommit commit = git.commit().setMessage("initial commit").call();
RevTag tag = git.tag().setName("tag").call();

LogCommand (git-log)

LogCommand allows you to easily walk a commit graph.

  • add(AnyObjectId start)
  • addRange(AnyObjectId since, AnyObjectId until)

Here's a quick example of how get some log messages.

Git git = new Git(db);
Iterable<RevCommit> log = git.log().call();

MergeCommand (git-merge)

TODO

Ant Tasks

JGit has Ant tasks for some common tasks contained in the org.eclipse.jgit.ant bundle.

To use these tasks:

   <taskdef resource="org/eclipse/jgit/ant/ant-tasks.properties">
       <classpath>
         <pathelement location="path/to/org.eclipse.jgit.ant-VERSION.jar"/>
         <pathelement location="path/to/org.eclipse.jgit-VERSION.jar"/>
         <pathelement location="path/to/jsch-0.1.44-1.jar"/>
       </classpath>
   </taskdef>

This would then provide git-clone, git-init and git-checkout tasks.

git-clone

   <git-clone uri="http://egit.eclipse.org/jgit.git" />

The following attributes are required:

  • uri: the uri to clone from

The following attributes are optional:

  • dest: the destination to clone to (defaults to use a human readable directory name based on the last path component of the uri)
  • bare: true/false/yes/no to indicate if the cloned repository should be bare or not (defaults to false)
  • branch: the initial branch to check out when cloning the repository (defaults to HEAD)

git-init

   <git-init />

The following attributes are optional:

  • dest: the path where a git repository is initialized (defaults $GIT_DIR or the current directory)
  • bare: true/false/yes/no to indicate if the repository should be bare or not (defaults to false)

git-checkout

   <git-checkout src="path/to/repo" branch="origin/experimental" />

The following attributes are required:

  • src: the path to the git repository
  • branch: the initial branch to checkout

The following attributes are optional:

  • createbranch: true/false/yes/no to indicate whether the branch should be created if it does not already exist (defaults to false)
  • force: true/false/yes/no: if true/yes and the branch with the given name already exists, the start-point of an existing branch will be set to a new start-point; if false, the existing branch will not be changed (defaults to false)

git-add

TODO

Snippets

Finding children of a commit

PlotWalk revWalk = new PlotWalk(repo());
ObjectId rootId = (branch==null)?repo().resolve(HEAD):branch.getObjectId();
RevCommit root = revWalk.parseCommit(rootId);
revWalk.markStart(root);
PlotCommitList<PlotLane> plotCommitList = new PlotCommitList<PlotLane>();
plotCommitList.source(revWalk);
plotCommitList.fillTo(Integer.MAX_VALUE);
return revWalk;

Snippet Collection

There is a collection of ready-to-run JGit code snippets available at https://github.com/centic9/jgit-cookbook

Advanced Topics

Reducing memory usage with RevWalk

The revision walk interface and the RevWalk and RevCommit classes are designed to be light-weight. However, when used with any repository of considerable size they may still require a lot of memory. This section provides hints on what you can do to reduce memory when walking the revision graph.

Restrict the walked revision graph

Try to walk only the amount of the graph you actually need to walk. That is, if you are looking for the commits in refs/heads/master not yet in refs/remotes/origin/master, make sure you markStart() for refs/heads/master and markUninteresting() refs/remotes/origin/master. The RevWalk traversal will only parse the commits necessary for it to answer you, and will try to avoid looking back further in history. That reduces the size of the internal object map, and thus reduces overall memory usage.

RevWalk walk = new RevWalk(repository);
ObjectId from = repository.resolve("refs/heads/master");
ObjectId to = repository.resolve("refs/remotes/origin/master");

walk.markStart(walk.parseCommit(from));
walk.markUninteresting(walk.parseCommit(to));

// ...

Discard the body of a commit

There is a setRetainBody(false) method you can use to discard the body of a commit if you don't need the author, committer or message information during the traversal. Examples of when you don't need this data is when you are only using the RevWalk to compute the merge base between branches, or to perform a task you would have used `git rev-list` with its default formatting for.

RevWalk walk = new RevWalk(repository);
walk.setRetainBody(false);
// ...

If you do need the body, consider extracting the data you need and then calling dispose() on the RevCommit, assuming you only need the data once and can then discard it. If you need to hang onto the data, you may find that JGit's internal representation uses less overall memory than if you held onto it yourself, especially if you want the full message. This is because JGit uses a byte[] internally to store the message in UTF-8. Java String storage would be bigger using UTF-16, assuming the message is mostly US-ASCII data.

RevWalk walk = new RevWalk(repository);
// more setup
Set<String> authorEmails = new HashSet<String>();

for (RevCommit commit : walk) {
	// extract the commit fields you need, for example:
	authorEmails.add(commit.getAuthorIdent().getEmailAddress());
 
 	commit.dispose();
}

Subclassing RevWalk and RevCommit

If you need to attach additional data to a commit, consider subclassing both RevWalk and RevCommit, and using the createCommit() method in RevWalk to consruct an instance of your RevCommit subclass. Put the additional data as fields in your RevCommit subclass, so that you don't need to use an auxiliary HashMap to translate from RevCommit or ObjectId to your additional data fields.

public class ReviewedRevision extends RevCommit {

	private final Date reviewDate;

	private ReviewedRevision(AnyObjectId id, Date reviewDate) {
		super(id);
		this.reviewDate = reviewDate;
	}

	public List<String> getReviewedBy() {
		return getFooterLines("Reviewed-by");
	}

	public Date getReviewDate() {
		return reviewDate;
	}

	public static class Walk extends RevWalk {

		public Walk(Repository repo) {
			super(repo);
		}

		@Override
		protected RevCommit createCommit(AnyObjectId id) {
			return new ReviewedRevision(id, getReviewDate(id));
		}

		private Date getReviewDate(AnyObjectId id) {
			// ...
		}

	}
}

Cleaning up after a revision walk

A RevWalk cannot shrink its internal object map. If you have just done a huge traversal of say all history of the repository, that will load everything into the object map, and it cannot be released. If you don't need this data in the near future, it may be a good idea to throw away the RevWalk and allocate a new one for your next traversal. That will let the GC reclaim everything and make it available for another use. On the other hand, reusing an existing object map is much faster than building a new one from scratch. So you need to balance the reclaiming of memory against the user's desire to perform fast updates of an existing repository view.

RevWalk walk = new RevWalk(repository);
// ...
for (RevCommit commit : walk) {
	// ...
}
walk.dispose();

Copyright © Eclipse Foundation, Inc. All Rights Reserved.