/*
 * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
 * Copyright (C) 2008-2010, Google Inc.
 * Copyright (C) 2006-2010, Robin Rosenberg <robin.rosenberg@dewire.com>
 * Copyright (C) 2006-2012, Shawn O. Pearce <spearce@spearce.org>
 * Copyright (C) 2012, Daniel Megert <daniel_megert@ch.ibm.com>
 * Copyright (C) 2017, Wim Jongman <wim.jongman@remainsoftware.com>
 * and other copyright owners as documented in the project's IP log.
 *
 * This program and the accompanying materials are made available
 * under the terms of the Eclipse Distribution License v1.0 which
 * accompanies this distribution, is reproduced below, and is
 * available at http://www.eclipse.org/org/documents/edl-v10.php
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following
 *   disclaimer in the documentation and/or other materials provided
 *   with the distribution.
 *
 * - Neither the name of the Eclipse Foundation, Inc. nor the
 *   names of its contributors may be used to endorse or promote
 *   products derived from this software without specific prior
 *   written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.eclipse.jgit.lib;

import static org.eclipse.jgit.lib.Constants.LOCK_SUFFIX;
import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Pattern;

import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.attributes.AttributesNodeProvider;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.errors.AmbiguousObjectException;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.NoWorkTreeException;
import org.eclipse.jgit.errors.RevisionSyntaxException;
import org.eclipse.jgit.events.IndexChangedEvent;
import org.eclipse.jgit.events.IndexChangedListener;
import org.eclipse.jgit.events.ListenerList;
import org.eclipse.jgit.events.RepositoryEvent;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
import org.eclipse.jgit.util.SystemReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Represents a Git repository.

A repository holds all objects and refs used for managing source code (could be any type of file, but source code is what SCM's are typically used for).

The thread-safety of a Repository very much depends on the concrete implementation. Applications working with a generic Repository type must not assume the instance is thread-safe.

  • FileRepository is thread-safe.
  • DfsRepository thread-safety is determined by its subclass.
/** * Represents a Git repository. * <p> * A repository holds all objects and refs used for managing source code (could * be any type of file, but source code is what SCM's are typically used for). * <p> * The thread-safety of a {@link org.eclipse.jgit.lib.Repository} very much * depends on the concrete implementation. Applications working with a generic * {@code Repository} type must not assume the instance is thread-safe. * <ul> * <li>{@code FileRepository} is thread-safe. * <li>{@code DfsRepository} thread-safety is determined by its subclass. * </ul> */
public abstract class Repository implements AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(Repository.class); private static final ListenerList globalListeners = new ListenerList();
Branch names containing slashes should not have a name component that is one of the reserved device names on Windows.
See Also:
  • normalizeBranchName(String)
/** * Branch names containing slashes should not have a name component that is * one of the reserved device names on Windows. * * @see #normalizeBranchName(String) */
private static final Pattern FORBIDDEN_BRANCH_NAME_COMPONENTS = Pattern .compile( "(^|/)(aux|com[1-9]|con|lpt[1-9]|nul|prn)(\\.[^/]*)?", //$NON-NLS-1$ Pattern.CASE_INSENSITIVE);
Get the global listener list observing all events in this JVM.
Returns:the global listener list observing all events in this JVM.
/** * Get the global listener list observing all events in this JVM. * * @return the global listener list observing all events in this JVM. */
public static ListenerList getGlobalListenerList() { return globalListeners; }
Use counter
/** Use counter */
final AtomicInteger useCnt = new AtomicInteger(1); final AtomicLong closedAt = new AtomicLong();
Metadata directory holding the repository's critical files.
/** Metadata directory holding the repository's critical files. */
private final File gitDir;
File abstraction used to resolve paths.
/** File abstraction used to resolve paths. */
private final FS fs; private final ListenerList myListeners = new ListenerList();
If not bare, the top level directory of the working files.
/** If not bare, the top level directory of the working files. */
private final File workTree;
If not bare, the index file caching the working file states.
/** If not bare, the index file caching the working file states. */
private final File indexFile;
Initialize a new repository instance.
Params:
  • options – options to configure the repository.
/** * Initialize a new repository instance. * * @param options * options to configure the repository. */
protected Repository(BaseRepositoryBuilder options) { gitDir = options.getGitDir(); fs = options.getFS(); workTree = options.getWorkTree(); indexFile = options.getIndexFile(); }
Get listeners observing only events on this repository.
Returns:listeners observing only events on this repository.
/** * Get listeners observing only events on this repository. * * @return listeners observing only events on this repository. */
@NonNull public ListenerList getListenerList() { return myListeners; }
Fire an event to all registered listeners.

The source repository of the event is automatically set to this repository, before the event is delivered to any listeners.

Params:
  • event – the event to deliver.
/** * Fire an event to all registered listeners. * <p> * The source repository of the event is automatically set to this * repository, before the event is delivered to any listeners. * * @param event * the event to deliver. */
public void fireEvent(RepositoryEvent<?> event) { event.setRepository(this); myListeners.dispatch(event); globalListeners.dispatch(event); }
Create a new Git repository.

Repository with working tree is created using this method. This method is the same as create(false).

Throws:
See Also:
/** * Create a new Git repository. * <p> * Repository with working tree is created using this method. This method is * the same as {@code create(false)}. * * @throws java.io.IOException * @see #create(boolean) */
public void create() throws IOException { create(false); }
Create a new Git repository initializing the necessary files and directories.
Params:
  • bare – if true, a bare repository (a repository without a working directory) is created.
Throws:
/** * Create a new Git repository initializing the necessary files and * directories. * * @param bare * if true, a bare repository (a repository without a working * directory) is created. * @throws java.io.IOException * in case of IO problem */
public abstract void create(boolean bare) throws IOException;
Get local metadata directory
Returns:local metadata directory; null if repository isn't local.
/** * Get local metadata directory * * @return local metadata directory; {@code null} if repository isn't local. */
/* * TODO This method should be annotated as Nullable, because in some * specific configurations metadata is not located in the local file system * (for example in memory databases). In "usual" repositories this * annotation would only cause compiler errors at places where the actual * directory can never be null. */ public File getDirectory() { return gitDir; }
Get repository identifier.
Returns:repository identifier. The returned identifier has to be unique within a given Git server.
Since:5.4
/** * Get repository identifier. * * @return repository identifier. The returned identifier has to be unique * within a given Git server. * @since 5.4 */
public abstract String getIdentifier();
Get the object database which stores this repository's data.
Returns:the object database which stores this repository's data.
/** * Get the object database which stores this repository's data. * * @return the object database which stores this repository's data. */
@NonNull public abstract ObjectDatabase getObjectDatabase();
Create a new inserter to create objects in getObjectDatabase().
Returns:a new inserter to create objects in getObjectDatabase().
/** * Create a new inserter to create objects in {@link #getObjectDatabase()}. * * @return a new inserter to create objects in {@link #getObjectDatabase()}. */
@NonNull public ObjectInserter newObjectInserter() { return getObjectDatabase().newInserter(); }
Create a new reader to read objects from getObjectDatabase().
Returns:a new reader to read objects from getObjectDatabase().
/** * Create a new reader to read objects from {@link #getObjectDatabase()}. * * @return a new reader to read objects from {@link #getObjectDatabase()}. */
@NonNull public ObjectReader newObjectReader() { return getObjectDatabase().newReader(); }
Get the reference database which stores the reference namespace.
Returns:the reference database which stores the reference namespace.
/** * Get the reference database which stores the reference namespace. * * @return the reference database which stores the reference namespace. */
@NonNull public abstract RefDatabase getRefDatabase();
Get the configuration of this repository.
Returns:the configuration of this repository.
/** * Get the configuration of this repository. * * @return the configuration of this repository. */
@NonNull public abstract StoredConfig getConfig();
Create a new AttributesNodeProvider.
Returns:a new AttributesNodeProvider. This AttributesNodeProvider is lazy loaded only once. It means that it will not be updated after loading. Prefer creating new instance for each use.
Since:4.2
/** * Create a new {@link org.eclipse.jgit.attributes.AttributesNodeProvider}. * * @return a new {@link org.eclipse.jgit.attributes.AttributesNodeProvider}. * This {@link org.eclipse.jgit.attributes.AttributesNodeProvider} * is lazy loaded only once. It means that it will not be updated * after loading. Prefer creating new instance for each use. * @since 4.2 */
@NonNull public abstract AttributesNodeProvider createAttributesNodeProvider();
Get the used file system abstraction.
Returns:the used file system abstraction, or null if repository isn't local.
/** * Get the used file system abstraction. * * @return the used file system abstraction, or {@code null} if * repository isn't local. */
/* * TODO This method should be annotated as Nullable, because in some * specific configurations metadata is not located in the local file system * (for example in memory databases). In "usual" repositories this * annotation would only cause compiler errors at places where the actual * directory can never be null. */ public FS getFS() { return fs; }
Whether the specified object is stored in this repo or any of the known shared repositories.
Params:
Returns:true if the specified object is stored in this repo or any of the known shared repositories.
Deprecated:use getObjectDatabase().has(objectId)
/** * Whether the specified object is stored in this repo or any of the known * shared repositories. * * @param objectId * a {@link org.eclipse.jgit.lib.AnyObjectId} object. * @return true if the specified object is stored in this repo or any of the * known shared repositories. * @deprecated use {@code getObjectDatabase().has(objectId)} */
@Deprecated public boolean hasObject(AnyObjectId objectId) { try { return getObjectDatabase().has(objectId); } catch (IOException e) { throw new UncheckedIOException(e); } }
Open an object from this repository.

This is a one-shot call interface which may be faster than allocating a newObjectReader() to perform the lookup.

Params:
  • objectId – identity of the object to open.
Throws:
Returns:a ObjectLoader for accessing the object.
/** * Open an object from this repository. * <p> * This is a one-shot call interface which may be faster than allocating a * {@link #newObjectReader()} to perform the lookup. * * @param objectId * identity of the object to open. * @return a {@link org.eclipse.jgit.lib.ObjectLoader} for accessing the * object. * @throws org.eclipse.jgit.errors.MissingObjectException * the object does not exist. * @throws java.io.IOException * the object store cannot be accessed. */
@NonNull public ObjectLoader open(AnyObjectId objectId) throws MissingObjectException, IOException { return getObjectDatabase().open(objectId); }
Open an object from this repository.

This is a one-shot call interface which may be faster than allocating a newObjectReader() to perform the lookup.

Params:
  • objectId – identity of the object to open.
  • typeHint – hint about the type of object being requested, e.g. Constants.OBJ_BLOB; ObjectReader.OBJ_ANY if the object type is not known, or does not matter to the caller.
Throws:
Returns:a ObjectLoader for accessing the object.
/** * Open an object from this repository. * <p> * This is a one-shot call interface which may be faster than allocating a * {@link #newObjectReader()} to perform the lookup. * * @param objectId * identity of the object to open. * @param typeHint * hint about the type of object being requested, e.g. * {@link org.eclipse.jgit.lib.Constants#OBJ_BLOB}; * {@link org.eclipse.jgit.lib.ObjectReader#OBJ_ANY} if the * object type is not known, or does not matter to the caller. * @return a {@link org.eclipse.jgit.lib.ObjectLoader} for accessing the * object. * @throws org.eclipse.jgit.errors.MissingObjectException * the object does not exist. * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException * typeHint was not OBJ_ANY, and the object's actual type does * not match typeHint. * @throws java.io.IOException * the object store cannot be accessed. */
@NonNull public ObjectLoader open(AnyObjectId objectId, int typeHint) throws MissingObjectException, IncorrectObjectTypeException, IOException { return getObjectDatabase().open(objectId, typeHint); }
Create a command to update, create or delete a ref in this repository.
Params:
  • ref – name of the ref the caller wants to modify.
Throws:
  • IOException – a symbolic ref was passed in and could not be resolved back to the base ref, as the symbolic ref could not be read.
Returns:an update command. The caller must finish populating this command and then invoke one of the update methods to actually make a change.
/** * Create a command to update, create or delete a ref in this repository. * * @param ref * name of the ref the caller wants to modify. * @return an update command. The caller must finish populating this command * and then invoke one of the update methods to actually make a * change. * @throws java.io.IOException * a symbolic ref was passed in and could not be resolved back * to the base ref, as the symbolic ref could not be read. */
@NonNull public RefUpdate updateRef(String ref) throws IOException { return updateRef(ref, false); }
Create a command to update, create or delete a ref in this repository.
Params:
  • ref – name of the ref the caller wants to modify.
  • detach – true to create a detached head
Throws:
  • IOException – a symbolic ref was passed in and could not be resolved back to the base ref, as the symbolic ref could not be read.
Returns:an update command. The caller must finish populating this command and then invoke one of the update methods to actually make a change.
/** * Create a command to update, create or delete a ref in this repository. * * @param ref * name of the ref the caller wants to modify. * @param detach * true to create a detached head * @return an update command. The caller must finish populating this command * and then invoke one of the update methods to actually make a * change. * @throws java.io.IOException * a symbolic ref was passed in and could not be resolved back * to the base ref, as the symbolic ref could not be read. */
@NonNull public RefUpdate updateRef(String ref, boolean detach) throws IOException { return getRefDatabase().newUpdate(ref, detach); }
Create a command to rename a ref in this repository
Params:
  • fromRef – name of ref to rename from
  • toRef – name of ref to rename to
Throws:
Returns:an update command that knows how to rename a branch to another.
/** * Create a command to rename a ref in this repository * * @param fromRef * name of ref to rename from * @param toRef * name of ref to rename to * @return an update command that knows how to rename a branch to another. * @throws java.io.IOException * the rename could not be performed. */
@NonNull public RefRename renameRef(String fromRef, String toRef) throws IOException { return getRefDatabase().newRename(fromRef, toRef); }
Parse a git revision string and return an object id. Combinations of these operators are supported:
  • HEAD, MERGE_HEAD, FETCH_HEAD
  • SHA-1: a complete or abbreviated SHA-1
  • refs/...: a complete reference name
  • short-name: a short reference name under refs/heads, refs/tags, or refs/remotes namespace
  • tag-NN-gABBREV: output from describe, parsed by treating ABBREV as an abbreviated SHA-1.
  • id^: first parent of commit id, this is the same as id^1
  • id^0: ensure id is a commit
  • id^n: n-th parent of commit id
  • id~n: n-th historical ancestor of id, by first parent. id~3 is equivalent to id^1^1^1 or id^^^.
  • id:path: Lookup path under tree named by id
  • id^{commit}: ensure id is a commit
  • id^{tree}: ensure id is a tree
  • id^{tag}: ensure id is a tag
  • id^{blob}: ensure id is a blob

The following operators are specified by Git conventions, but are not supported by this method:

  • ref@{n}: n-th version of ref as given by its reflog
  • ref@{time}: value of ref at the designated time
Params:
  • revstr – A git object references expression
Throws:
  • AmbiguousObjectExceptionrevstr contains an abbreviated ObjectId and this repository contains more than one object which match to the input abbreviation.
  • IncorrectObjectTypeException – the id parsed does not meet the type required to finish applying the operators in the expression.
  • RevisionSyntaxException – the expression is not supported by this implementation, or does not meet the standard syntax.
  • IOException – on serious errors
Returns:an ObjectId or null if revstr can't be resolved to any ObjectId
/** * Parse a git revision string and return an object id. * * Combinations of these operators are supported: * <ul> * <li><b>HEAD</b>, <b>MERGE_HEAD</b>, <b>FETCH_HEAD</b></li> * <li><b>SHA-1</b>: a complete or abbreviated SHA-1</li> * <li><b>refs/...</b>: a complete reference name</li> * <li><b>short-name</b>: a short reference name under {@code refs/heads}, * {@code refs/tags}, or {@code refs/remotes} namespace</li> * <li><b>tag-NN-gABBREV</b>: output from describe, parsed by treating * {@code ABBREV} as an abbreviated SHA-1.</li> * <li><i>id</i><b>^</b>: first parent of commit <i>id</i>, this is the same * as {@code id^1}</li> * <li><i>id</i><b>^0</b>: ensure <i>id</i> is a commit</li> * <li><i>id</i><b>^n</b>: n-th parent of commit <i>id</i></li> * <li><i>id</i><b>~n</b>: n-th historical ancestor of <i>id</i>, by first * parent. {@code id~3} is equivalent to {@code id^1^1^1} or {@code id^^^}.</li> * <li><i>id</i><b>:path</b>: Lookup path under tree named by <i>id</i></li> * <li><i>id</i><b>^{commit}</b>: ensure <i>id</i> is a commit</li> * <li><i>id</i><b>^{tree}</b>: ensure <i>id</i> is a tree</li> * <li><i>id</i><b>^{tag}</b>: ensure <i>id</i> is a tag</li> * <li><i>id</i><b>^{blob}</b>: ensure <i>id</i> is a blob</li> * </ul> * * <p> * The following operators are specified by Git conventions, but are not * supported by this method: * <ul> * <li><b>ref@{n}</b>: n-th version of ref as given by its reflog</li> * <li><b>ref@{time}</b>: value of ref at the designated time</li> * </ul> * * @param revstr * A git object references expression * @return an ObjectId or {@code null} if revstr can't be resolved to any * ObjectId * @throws org.eclipse.jgit.errors.AmbiguousObjectException * {@code revstr} contains an abbreviated ObjectId and this * repository contains more than one object which match to the * input abbreviation. * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException * the id parsed does not meet the type required to finish * applying the operators in the expression. * @throws org.eclipse.jgit.errors.RevisionSyntaxException * the expression is not supported by this implementation, or * does not meet the standard syntax. * @throws java.io.IOException * on serious errors */
@Nullable public ObjectId resolve(String revstr) throws AmbiguousObjectException, IncorrectObjectTypeException, RevisionSyntaxException, IOException { try (RevWalk rw = new RevWalk(this)) { rw.setRetainBody(false); Object resolved = resolve(rw, revstr); if (resolved instanceof String) { final Ref ref = findRef((String) resolved); return ref != null ? ref.getLeaf().getObjectId() : null; } else { return (ObjectId) resolved; } } }
Simplify an expression, but unlike resolve(String) it will not resolve a branch passed or resulting from the expression, such as @{-}. Thus this method can be used to process an expression to a method that expects a branch or revision id.
Params:
Throws:
Returns:object id or ref name from resolved expression or null if given expression cannot be resolved
/** * Simplify an expression, but unlike {@link #resolve(String)} it will not * resolve a branch passed or resulting from the expression, such as @{-}. * Thus this method can be used to process an expression to a method that * expects a branch or revision id. * * @param revstr a {@link java.lang.String} object. * @return object id or ref name from resolved expression or {@code null} if * given expression cannot be resolved * @throws org.eclipse.jgit.errors.AmbiguousObjectException * @throws java.io.IOException */
@Nullable public String simplify(String revstr) throws AmbiguousObjectException, IOException { try (RevWalk rw = new RevWalk(this)) { rw.setRetainBody(true); Object resolved = resolve(rw, revstr); if (resolved != null) if (resolved instanceof String) return (String) resolved; else return ((AnyObjectId) resolved).getName(); return null; } } @Nullable private Object resolve(RevWalk rw, String revstr) throws IOException { char[] revChars = revstr.toCharArray(); RevObject rev = null; String name = null; int done = 0; for (int i = 0; i < revChars.length; ++i) { switch (revChars[i]) { case '^': if (rev == null) { if (name == null) if (done == 0) name = new String(revChars, done, i); else { done = i + 1; break; } rev = parseSimple(rw, name); name = null; if (rev == null) return null; } if (i + 1 < revChars.length) { switch (revChars[i + 1]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': int j; rev = rw.parseCommit(rev); for (j = i + 1; j < revChars.length; ++j) { if (!Character.isDigit(revChars[j])) break; } String parentnum = new String(revChars, i + 1, j - i - 1); int pnum; try { pnum = Integer.parseInt(parentnum); } catch (NumberFormatException e) { throw new RevisionSyntaxException( JGitText.get().invalidCommitParentNumber, revstr); } if (pnum != 0) { RevCommit commit = (RevCommit) rev; if (pnum > commit.getParentCount()) rev = null; else rev = commit.getParent(pnum - 1); } i = j - 1; done = j; break; case '{': int k; String item = null; for (k = i + 2; k < revChars.length; ++k) { if (revChars[k] == '}') { item = new String(revChars, i + 2, k - i - 2); break; } } i = k; if (item != null) if (item.equals("tree")) { //$NON-NLS-1$ rev = rw.parseTree(rev); } else if (item.equals("commit")) { //$NON-NLS-1$ rev = rw.parseCommit(rev); } else if (item.equals("blob")) { //$NON-NLS-1$ rev = rw.peel(rev); if (!(rev instanceof RevBlob)) throw new IncorrectObjectTypeException(rev, Constants.TYPE_BLOB); } else if (item.isEmpty()) { rev = rw.peel(rev); } else throw new RevisionSyntaxException(revstr); else throw new RevisionSyntaxException(revstr); done = k; break; default: rev = rw.peel(rev); if (rev instanceof RevCommit) { RevCommit commit = ((RevCommit) rev); if (commit.getParentCount() == 0) rev = null; else rev = commit.getParent(0); } else throw new IncorrectObjectTypeException(rev, Constants.TYPE_COMMIT); } } else { rev = rw.peel(rev); if (rev instanceof RevCommit) { RevCommit commit = ((RevCommit) rev); if (commit.getParentCount() == 0) rev = null; else rev = commit.getParent(0); } else throw new IncorrectObjectTypeException(rev, Constants.TYPE_COMMIT); } done = i + 1; break; case '~': if (rev == null) { if (name == null) if (done == 0) name = new String(revChars, done, i); else { done = i + 1; break; } rev = parseSimple(rw, name); name = null; if (rev == null) return null; } rev = rw.peel(rev); if (!(rev instanceof RevCommit)) throw new IncorrectObjectTypeException(rev, Constants.TYPE_COMMIT); int l; for (l = i + 1; l < revChars.length; ++l) { if (!Character.isDigit(revChars[l])) break; } int dist; if (l - i > 1) { String distnum = new String(revChars, i + 1, l - i - 1); try { dist = Integer.parseInt(distnum); } catch (NumberFormatException e) { throw new RevisionSyntaxException( JGitText.get().invalidAncestryLength, revstr); } } else dist = 1; while (dist > 0) { RevCommit commit = (RevCommit) rev; if (commit.getParentCount() == 0) { rev = null; break; } commit = commit.getParent(0); rw.parseHeaders(commit); rev = commit; --dist; } i = l - 1; done = l; break; case '@': if (rev != null) throw new RevisionSyntaxException(revstr); if (i + 1 == revChars.length) continue; if (i + 1 < revChars.length && revChars[i + 1] != '{') continue; int m; String time = null; for (m = i + 2; m < revChars.length; ++m) { if (revChars[m] == '}') { time = new String(revChars, i + 2, m - i - 2); break; } } if (time != null) { if (time.equals("upstream")) { //$NON-NLS-1$ if (name == null) name = new String(revChars, done, i); if (name.isEmpty()) // Currently checked out branch, HEAD if // detached name = Constants.HEAD; if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$ throw new RevisionSyntaxException(MessageFormat .format(JGitText.get().invalidRefName, name), revstr); Ref ref = findRef(name); name = null; if (ref == null) return null; if (ref.isSymbolic()) ref = ref.getLeaf(); name = ref.getName(); RemoteConfig remoteConfig; try { remoteConfig = new RemoteConfig(getConfig(), "origin"); //$NON-NLS-1$ } catch (URISyntaxException e) { throw new RevisionSyntaxException(revstr); } String remoteBranchName = getConfig() .getString( ConfigConstants.CONFIG_BRANCH_SECTION, Repository.shortenRefName(ref.getName()), ConfigConstants.CONFIG_KEY_MERGE); List<RefSpec> fetchRefSpecs = remoteConfig .getFetchRefSpecs(); for (RefSpec refSpec : fetchRefSpecs) { if (refSpec.matchSource(remoteBranchName)) { RefSpec expandFromSource = refSpec .expandFromSource(remoteBranchName); name = expandFromSource.getDestination(); break; } } if (name == null) throw new RevisionSyntaxException(revstr); } else if (time.matches("^-\\d+$")) { //$NON-NLS-1$ if (name != null) throw new RevisionSyntaxException(revstr); else { String previousCheckout = resolveReflogCheckout(-Integer .parseInt(time)); if (ObjectId.isId(previousCheckout)) rev = parseSimple(rw, previousCheckout); else name = previousCheckout; } } else { if (name == null) name = new String(revChars, done, i); if (name.isEmpty()) name = Constants.HEAD; if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$ throw new RevisionSyntaxException(MessageFormat .format(JGitText.get().invalidRefName, name), revstr); Ref ref = findRef(name); name = null; if (ref == null) return null; // @{n} means current branch, not HEAD@{1} unless // detached if (ref.isSymbolic()) ref = ref.getLeaf(); rev = resolveReflog(rw, ref, time); } i = m; } else throw new RevisionSyntaxException(revstr); break; case ':': { RevTree tree; if (rev == null) { if (name == null) name = new String(revChars, done, i); if (name.isEmpty()) name = Constants.HEAD; rev = parseSimple(rw, name); name = null; } if (rev == null) return null; tree = rw.parseTree(rev); if (i == revChars.length - 1) return tree.copy(); TreeWalk tw = TreeWalk.forPath(rw.getObjectReader(), new String(revChars, i + 1, revChars.length - i - 1), tree); return tw != null ? tw.getObjectId(0) : null; } default: if (rev != null) throw new RevisionSyntaxException(revstr); } } if (rev != null) return rev.copy(); if (name != null) return name; if (done == revstr.length()) return null; name = revstr.substring(done); if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$ throw new RevisionSyntaxException( MessageFormat.format(JGitText.get().invalidRefName, name), revstr); if (findRef(name) != null) return name; return resolveSimple(name); } private static boolean isHex(char c) { return ('0' <= c && c <= '9') // || ('a' <= c && c <= 'f') // || ('A' <= c && c <= 'F'); } private static boolean isAllHex(String str, int ptr) { while (ptr < str.length()) { if (!isHex(str.charAt(ptr++))) return false; } return true; } @Nullable private RevObject parseSimple(RevWalk rw, String revstr) throws IOException { ObjectId id = resolveSimple(revstr); return id != null ? rw.parseAny(id) : null; } @Nullable private ObjectId resolveSimple(String revstr) throws IOException { if (ObjectId.isId(revstr)) return ObjectId.fromString(revstr); if (Repository.isValidRefName("x/" + revstr)) { //$NON-NLS-1$ Ref r = getRefDatabase().findRef(revstr); if (r != null) return r.getObjectId(); } if (AbbreviatedObjectId.isId(revstr)) return resolveAbbreviation(revstr); int dashg = revstr.indexOf("-g"); //$NON-NLS-1$ if ((dashg + 5) < revstr.length() && 0 <= dashg && isHex(revstr.charAt(dashg + 2)) && isHex(revstr.charAt(dashg + 3)) && isAllHex(revstr, dashg + 4)) { // Possibly output from git describe? String s = revstr.substring(dashg + 2); if (AbbreviatedObjectId.isId(s)) return resolveAbbreviation(s); } return null; } @Nullable private String resolveReflogCheckout(int checkoutNo) throws IOException { ReflogReader reader = getReflogReader(Constants.HEAD); if (reader == null) { return null; } List<ReflogEntry> reflogEntries = reader.getReverseEntries(); for (ReflogEntry entry : reflogEntries) { CheckoutEntry checkout = entry.parseCheckout(); if (checkout != null) if (checkoutNo-- == 1) return checkout.getFromBranch(); } return null; } private RevCommit resolveReflog(RevWalk rw, Ref ref, String time) throws IOException { int number; try { number = Integer.parseInt(time); } catch (NumberFormatException nfe) { throw new RevisionSyntaxException(MessageFormat.format( JGitText.get().invalidReflogRevision, time)); } assert number >= 0; ReflogReader reader = getReflogReader(ref.getName()); if (reader == null) { throw new RevisionSyntaxException( MessageFormat.format(JGitText.get().reflogEntryNotFound, Integer.valueOf(number), ref.getName())); } ReflogEntry entry = reader.getReverseEntry(number); if (entry == null) throw new RevisionSyntaxException(MessageFormat.format( JGitText.get().reflogEntryNotFound, Integer.valueOf(number), ref.getName())); return rw.parseCommit(entry.getNewId()); } @Nullable private ObjectId resolveAbbreviation(String revstr) throws IOException, AmbiguousObjectException { AbbreviatedObjectId id = AbbreviatedObjectId.fromString(revstr); try (ObjectReader reader = newObjectReader()) { Collection<ObjectId> matches = reader.resolve(id); if (matches.isEmpty()) return null; else if (matches.size() == 1) return matches.iterator().next(); else throw new AmbiguousObjectException(id, matches); } }
Increment the use counter by one, requiring a matched close().
/** * Increment the use counter by one, requiring a matched {@link #close()}. */
public void incrementOpen() { useCnt.incrementAndGet(); }
{@inheritDoc}

Decrement the use count, and maybe close resources.

/** * {@inheritDoc} * <p> * Decrement the use count, and maybe close resources. */
@Override public void close() { int newCount = useCnt.decrementAndGet(); if (newCount == 0) { if (RepositoryCache.isCached(this)) { closedAt.set(System.currentTimeMillis()); } else { doClose(); } } else if (newCount == -1) { // should not happen, only log when useCnt became negative to // minimize number of log entries String message = MessageFormat.format(JGitText.get().corruptUseCnt, toString()); if (LOG.isDebugEnabled()) { LOG.debug(message, new IllegalStateException()); } else { LOG.warn(message); } if (RepositoryCache.isCached(this)) { closedAt.set(System.currentTimeMillis()); } } }
Invoked when the use count drops to zero during close().

The default implementation closes the object and ref databases.

/** * Invoked when the use count drops to zero during {@link #close()}. * <p> * The default implementation closes the object and ref databases. */
protected void doClose() { getObjectDatabase().close(); getRefDatabase().close(); }
{@inheritDoc}
/** {@inheritDoc} */
@Override @NonNull public String toString() { String desc; File directory = getDirectory(); if (directory != null) desc = directory.getPath(); else desc = getClass().getSimpleName() + "-" //$NON-NLS-1$ + System.identityHashCode(this); return "Repository[" + desc + "]"; //$NON-NLS-1$ //$NON-NLS-2$ }
Get the name of the reference that HEAD points to.

This is essentially the same as doing:

return exactRef(Constants.HEAD).getTarget().getName()
Except when HEAD is detached, in which case this method returns the current ObjectId in hexadecimal string format.
Throws:
Returns:name of current branch (for example refs/heads/master), an ObjectId in hex format if the current branch is detached, or null if the repository is corrupt and has no HEAD reference.
/** * Get the name of the reference that {@code HEAD} points to. * <p> * This is essentially the same as doing: * * <pre> * return exactRef(Constants.HEAD).getTarget().getName() * </pre> * * Except when HEAD is detached, in which case this method returns the * current ObjectId in hexadecimal string format. * * @return name of current branch (for example {@code refs/heads/master}), * an ObjectId in hex format if the current branch is detached, or * {@code null} if the repository is corrupt and has no HEAD * reference. * @throws java.io.IOException */
@Nullable public String getFullBranch() throws IOException { Ref head = exactRef(Constants.HEAD); if (head == null) { return null; } if (head.isSymbolic()) { return head.getTarget().getName(); } ObjectId objectId = head.getObjectId(); if (objectId != null) { return objectId.name(); } return null; }
Get the short name of the current branch that HEAD points to.

This is essentially the same as getFullBranch(), except the leading prefix refs/heads/ is removed from the reference before it is returned to the caller.

Throws:
Returns:name of current branch (for example master), an ObjectId in hex format if the current branch is detached, or null if the repository is corrupt and has no HEAD reference.
/** * Get the short name of the current branch that {@code HEAD} points to. * <p> * This is essentially the same as {@link #getFullBranch()}, except the * leading prefix {@code refs/heads/} is removed from the reference before * it is returned to the caller. * * @return name of current branch (for example {@code master}), an ObjectId * in hex format if the current branch is detached, or {@code null} * if the repository is corrupt and has no HEAD reference. * @throws java.io.IOException */
@Nullable public String getBranch() throws IOException { String name = getFullBranch(); if (name != null) return shortenRefName(name); return null; }
Objects known to exist but not expressed by getAllRefs().

When a repository borrows objects from another repository, it can advertise that it safely has that other repository's references, without exposing any other details about the other repository. This may help a client trying to push changes avoid pushing more than it needs to.

Returns:unmodifiable collection of other known objects.
/** * Objects known to exist but not expressed by {@link #getAllRefs()}. * <p> * When a repository borrows objects from another repository, it can * advertise that it safely has that other repository's references, without * exposing any other details about the other repository. This may help * a client trying to push changes avoid pushing more than it needs to. * * @return unmodifiable collection of other known objects. */
@NonNull public Set<ObjectId> getAdditionalHaves() { return Collections.emptySet(); }
Get a ref by name.
Params:
  • name – the name of the ref to lookup. Must not be a short-hand form; e.g., "master" is not automatically expanded to "refs/heads/master".
Throws:
Returns:the Ref with the given name, or null if it does not exist
Since:4.2
/** * Get a ref by name. * * @param name * the name of the ref to lookup. Must not be a short-hand * form; e.g., "master" is not automatically expanded to * "refs/heads/master". * @return the Ref with the given name, or {@code null} if it does not exist * @throws java.io.IOException * @since 4.2 */
@Nullable public final Ref exactRef(String name) throws IOException { return getRefDatabase().exactRef(name); }
Search for a ref by (possibly abbreviated) name.
Params:
  • name – the name of the ref to lookup. May be a short-hand form, e.g. "master" which is automatically expanded to "refs/heads/master" if "refs/heads/master" already exists.
Throws:
Returns:the Ref with the given name, or null if it does not exist
Since:4.2
/** * Search for a ref by (possibly abbreviated) name. * * @param name * the name of the ref to lookup. May be a short-hand form, e.g. * "master" which is automatically expanded to * "refs/heads/master" if "refs/heads/master" already exists. * @return the Ref with the given name, or {@code null} if it does not exist * @throws java.io.IOException * @since 4.2 */
@Nullable public final Ref findRef(String name) throws IOException { return getRefDatabase().findRef(name); }
Get mutable map of all known refs, including symrefs like HEAD that may not point to any object yet.
Returns:mutable map of all known refs (heads, tags, remotes).
Deprecated:use getRefDatabase().getRefs() instead.
/** * Get mutable map of all known refs, including symrefs like HEAD that may * not point to any object yet. * * @return mutable map of all known refs (heads, tags, remotes). * @deprecated use {@code getRefDatabase().getRefs()} instead. */
@Deprecated @NonNull public Map<String, Ref> getAllRefs() { try { return getRefDatabase().getRefs(RefDatabase.ALL); } catch (IOException e) { throw new UncheckedIOException(e); } }
Get mutable map of all tags
Returns:mutable map of all tags; key is short tag name ("v1.0") and value of the entry contains the ref with the full tag name ("refs/tags/v1.0").
Deprecated:use getRefDatabase().getRefsByPrefix(R_TAGS) instead
/** * Get mutable map of all tags * * @return mutable map of all tags; key is short tag name ("v1.0") and value * of the entry contains the ref with the full tag name * ("refs/tags/v1.0"). * @deprecated use {@code getRefDatabase().getRefsByPrefix(R_TAGS)} instead */
@Deprecated @NonNull public Map<String, Ref> getTags() { try { return getRefDatabase().getRefs(Constants.R_TAGS); } catch (IOException e) { throw new UncheckedIOException(e); } }
Peel a possibly unpeeled reference to an annotated tag.

If the ref cannot be peeled (as it does not refer to an annotated tag) the peeled id stays null, but Ref.isPeeled() will be true.

Params:
  • ref – The ref to peel
Returns:ref if ref.isPeeled() is true; else a new Ref object representing the same data as Ref, but isPeeled() will be true and getPeeledObjectId will contain the peeled object (or null).
Deprecated:use getRefDatabase().peel(ref) instead.
/** * Peel a possibly unpeeled reference to an annotated tag. * <p> * If the ref cannot be peeled (as it does not refer to an annotated tag) * the peeled id stays null, but {@link org.eclipse.jgit.lib.Ref#isPeeled()} * will be true. * * @param ref * The ref to peel * @return <code>ref</code> if <code>ref.isPeeled()</code> is true; else a * new Ref object representing the same data as Ref, but isPeeled() * will be true and getPeeledObjectId will contain the peeled object * (or null). * @deprecated use {@code getRefDatabase().peel(ref)} instead. */
@Deprecated @NonNull public Ref peel(Ref ref) { try { return getRefDatabase().peel(ref); } catch (IOException e) { // Historical accident; if the reference cannot be peeled due // to some sort of repository access problem we claim that the // same as if the reference was not an annotated tag. return ref; } }
Get a map with all objects referenced by a peeled ref.
Returns:a map with all objects referenced by a peeled ref.
/** * Get a map with all objects referenced by a peeled ref. * * @return a map with all objects referenced by a peeled ref. */
@NonNull public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() { Map<String, Ref> allRefs = getAllRefs(); Map<AnyObjectId, Set<Ref>> ret = new HashMap<>(allRefs.size()); for (Ref ref : allRefs.values()) { ref = peel(ref); AnyObjectId target = ref.getPeeledObjectId(); if (target == null) target = ref.getObjectId(); // We assume most Sets here are singletons Set<Ref> oset = ret.put(target, Collections.singleton(ref)); if (oset != null) { // that was not the case (rare) if (oset.size() == 1) { // Was a read-only singleton, we must copy to a new Set oset = new HashSet<>(oset); } ret.put(target, oset); oset.add(ref); } } return ret; }
Get the index file location or null if repository isn't local.
Throws:
Returns:the index file location or null if repository isn't local.
/** * Get the index file location or {@code null} if repository isn't local. * * @return the index file location or {@code null} if repository isn't * local. * @throws org.eclipse.jgit.errors.NoWorkTreeException * if this is bare, which implies it has no working directory. * See {@link #isBare()}. */
@NonNull public File getIndexFile() throws NoWorkTreeException { if (isBare()) throw new NoWorkTreeException(); return indexFile; }
Locate a reference to a commit and immediately parse its content.

This method only returns successfully if the commit object exists, is verified to be a commit, and was parsed without error.

Params:
  • id – name of the commit object.
Throws:
Returns:reference to the commit object. Never null.
Since:4.8
/** * Locate a reference to a commit and immediately parse its content. * <p> * This method only returns successfully if the commit object exists, * is verified to be a commit, and was parsed without error. * * @param id * name of the commit object. * @return reference to the commit object. Never null. * @throws org.eclipse.jgit.errors.MissingObjectException * the supplied commit does not exist. * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException * the supplied id is not a commit or an annotated tag. * @throws java.io.IOException * a pack file or loose object could not be read. * @since 4.8 */
public RevCommit parseCommit(AnyObjectId id) throws IncorrectObjectTypeException, IOException, MissingObjectException { if (id instanceof RevCommit && ((RevCommit) id).getRawBuffer() != null) { return (RevCommit) id; } try (RevWalk walk = new RevWalk(this)) { return walk.parseCommit(id); } }
Create a new in-core index representation and read an index from disk.

The new index will be read before it is returned to the caller. Read failures are reported as exceptions and therefore prevent the method from returning a partially populated index.

Throws:
Returns:a cache representing the contents of the specified index file (if it exists) or an empty cache if the file does not exist.
/** * Create a new in-core index representation and read an index from disk. * <p> * The new index will be read before it is returned to the caller. Read * failures are reported as exceptions and therefore prevent the method from * returning a partially populated index. * * @return a cache representing the contents of the specified index file (if * it exists) or an empty cache if the file does not exist. * @throws org.eclipse.jgit.errors.NoWorkTreeException * if this is bare, which implies it has no working directory. * See {@link #isBare()}. * @throws java.io.IOException * the index file is present but could not be read. * @throws org.eclipse.jgit.errors.CorruptObjectException * the index file is using a format or extension that this * library does not support. */
@NonNull public DirCache readDirCache() throws NoWorkTreeException, CorruptObjectException, IOException { return DirCache.read(this); }
Create a new in-core index representation, lock it, and read from disk.

The new index will be locked and then read before it is returned to the caller. Read failures are reported as exceptions and therefore prevent the method from returning a partially populated index.

Throws:
  • NoWorkTreeException – if this is bare, which implies it has no working directory. See isBare().
  • IOException – the index file is present but could not be read, or the lock could not be obtained.
  • CorruptObjectException – the index file is using a format or extension that this library does not support.
Returns:a cache representing the contents of the specified index file (if it exists) or an empty cache if the file does not exist.
/** * Create a new in-core index representation, lock it, and read from disk. * <p> * The new index will be locked and then read before it is returned to the * caller. Read failures are reported as exceptions and therefore prevent * the method from returning a partially populated index. * * @return a cache representing the contents of the specified index file (if * it exists) or an empty cache if the file does not exist. * @throws org.eclipse.jgit.errors.NoWorkTreeException * if this is bare, which implies it has no working directory. * See {@link #isBare()}. * @throws java.io.IOException * the index file is present but could not be read, or the lock * could not be obtained. * @throws org.eclipse.jgit.errors.CorruptObjectException * the index file is using a format or extension that this * library does not support. */
@NonNull public DirCache lockDirCache() throws NoWorkTreeException, CorruptObjectException, IOException { // we want DirCache to inform us so that we can inform registered // listeners about index changes IndexChangedListener l = (IndexChangedEvent event) -> { notifyIndexChanged(true); }; return DirCache.lock(this, l); }
Get the repository state
Returns:the repository state
/** * Get the repository state * * @return the repository state */
@NonNull public RepositoryState getRepositoryState() { if (isBare() || getDirectory() == null) return RepositoryState.BARE; // Pre Git-1.6 logic if (new File(getWorkTree(), ".dotest").exists()) //$NON-NLS-1$ return RepositoryState.REBASING; if (new File(getDirectory(), ".dotest-merge").exists()) //$NON-NLS-1$ return RepositoryState.REBASING_INTERACTIVE; // From 1.6 onwards if (new File(getDirectory(),"rebase-apply/rebasing").exists()) //$NON-NLS-1$ return RepositoryState.REBASING_REBASING; if (new File(getDirectory(),"rebase-apply/applying").exists()) //$NON-NLS-1$ return RepositoryState.APPLY; if (new File(getDirectory(),"rebase-apply").exists()) //$NON-NLS-1$ return RepositoryState.REBASING; if (new File(getDirectory(),"rebase-merge/interactive").exists()) //$NON-NLS-1$ return RepositoryState.REBASING_INTERACTIVE; if (new File(getDirectory(),"rebase-merge").exists()) //$NON-NLS-1$ return RepositoryState.REBASING_MERGE; // Both versions if (new File(getDirectory(), Constants.MERGE_HEAD).exists()) { // we are merging - now check whether we have unmerged paths try { if (!readDirCache().hasUnmergedPaths()) { // no unmerged paths -> return the MERGING_RESOLVED state return RepositoryState.MERGING_RESOLVED; } } catch (IOException e) { throw new UncheckedIOException(e); } return RepositoryState.MERGING; } if (new File(getDirectory(), "BISECT_LOG").exists()) //$NON-NLS-1$ return RepositoryState.BISECTING; if (new File(getDirectory(), Constants.CHERRY_PICK_HEAD).exists()) { try { if (!readDirCache().hasUnmergedPaths()) { // no unmerged paths return RepositoryState.CHERRY_PICKING_RESOLVED; } } catch (IOException e) { throw new UncheckedIOException(e); } return RepositoryState.CHERRY_PICKING; } if (new File(getDirectory(), Constants.REVERT_HEAD).exists()) { try { if (!readDirCache().hasUnmergedPaths()) { // no unmerged paths return RepositoryState.REVERTING_RESOLVED; } } catch (IOException e) { throw new UncheckedIOException(e); } return RepositoryState.REVERTING; } return RepositoryState.SAFE; }
Check validity of a ref name. It must not contain character that has a special meaning in a Git object reference expression. Some other dangerous characters are also excluded. For portability reasons '\' is excluded
Params:
Returns:true if refName is a valid ref name
/** * Check validity of a ref name. It must not contain character that has * a special meaning in a Git object reference expression. Some other * dangerous characters are also excluded. * * For portability reasons '\' is excluded * * @param refName a {@link java.lang.String} object. * @return true if refName is a valid ref name */
public static boolean isValidRefName(String refName) { final int len = refName.length(); if (len == 0) { return false; } if (refName.endsWith(LOCK_SUFFIX)) { return false; } // Refs may be stored as loose files so invalid paths // on the local system must also be invalid refs. try { SystemReader.getInstance().checkPath(refName); } catch (CorruptObjectException e) { return false; } int components = 1; char p = '\0'; for (int i = 0; i < len; i++) { final char c = refName.charAt(i); if (c <= ' ') return false; switch (c) { case '.': switch (p) { case '\0': case '/': case '.': return false; } if (i == len -1) return false; break; case '/': if (i == 0 || i == len - 1) return false; if (p == '/') return false; components++; break; case '{': if (p == '@') return false; break; case '~': case '^': case ':': case '?': case '[': case '*': case '\\': case '\u007F': return false; } p = c; } return components > 1; }
Normalizes the passed branch name into a possible valid branch name. The validity of the returned name should be checked by a subsequent call to isValidRefName(String).

Future implementations of this method could be more restrictive or more lenient about the validity of specific characters in the returned name.

The current implementation returns the trimmed input string if this is already a valid branch name. Otherwise it returns a trimmed string with special characters not allowed by isValidRefName(String) replaced by hyphens ('-') and blanks replaced by underscores ('_'). Leading and trailing slashes, dots, hyphens, and underscores are removed.

Params:
  • name – to normalize
See Also:
Returns:The normalized name or an empty String if it is null or empty.
Since:4.7
/** * Normalizes the passed branch name into a possible valid branch name. The * validity of the returned name should be checked by a subsequent call to * {@link #isValidRefName(String)}. * <p> * Future implementations of this method could be more restrictive or more * lenient about the validity of specific characters in the returned name. * <p> * The current implementation returns the trimmed input string if this is * already a valid branch name. Otherwise it returns a trimmed string with * special characters not allowed by {@link #isValidRefName(String)} * replaced by hyphens ('-') and blanks replaced by underscores ('_'). * Leading and trailing slashes, dots, hyphens, and underscores are removed. * * @param name * to normalize * @return The normalized name or an empty String if it is {@code null} or * empty. * @since 4.7 * @see #isValidRefName(String) */
public static String normalizeBranchName(String name) { if (name == null || name.isEmpty()) { return ""; //$NON-NLS-1$ } String result = name.trim(); String fullName = result.startsWith(Constants.R_HEADS) ? result : Constants.R_HEADS + result; if (isValidRefName(fullName)) { return result; } // All Unicode blanks to underscore result = result.replaceAll("(?:\\h|\\v)+", "_"); //$NON-NLS-1$ //$NON-NLS-2$ StringBuilder b = new StringBuilder(); char p = '/'; for (int i = 0, len = result.length(); i < len; i++) { char c = result.charAt(i); if (c < ' ' || c == 127) { continue; } // Substitute a dash for problematic characters switch (c) { case '\\': case '^': case '~': case ':': case '?': case '*': case '[': case '@': case '<': case '>': case '|': case '"': c = '-'; break; default: break; } // Collapse multiple slashes, dashes, dots, underscores, and omit // dashes, dots, and underscores following a slash. switch (c) { case '/': if (p == '/') { continue; } p = '/'; break; case '.': case '_': case '-': if (p == '/' || p == '-') { continue; } p = '-'; break; default: p = c; break; } b.append(c); } // Strip trailing special characters, and avoid the .lock extension result = b.toString().replaceFirst("[/_.-]+$", "") //$NON-NLS-1$ //$NON-NLS-2$ .replaceAll("\\.lock($|/)", "_lock$1"); //$NON-NLS-1$ //$NON-NLS-2$ return FORBIDDEN_BRANCH_NAME_COMPONENTS.matcher(result) .replaceAll("$1+$2$3"); //$NON-NLS-1$ }
Strip work dir and return normalized repository path.
Params:
  • workDir – Work dir
  • file – File whose path shall be stripped of its workdir
Returns:normalized repository relative path or the empty string if the file is not relative to the work directory.
/** * Strip work dir and return normalized repository path. * * @param workDir * Work dir * @param file * File whose path shall be stripped of its workdir * @return normalized repository relative path or the empty string if the * file is not relative to the work directory. */
@NonNull public static String stripWorkDir(File workDir, File file) { final String filePath = file.getPath(); final String workDirPath = workDir.getPath(); if (filePath.length() <= workDirPath.length() || filePath.charAt(workDirPath.length()) != File.separatorChar || !filePath.startsWith(workDirPath)) { File absWd = workDir.isAbsolute() ? workDir : workDir.getAbsoluteFile(); File absFile = file.isAbsolute() ? file : file.getAbsoluteFile(); if (absWd.equals(workDir) && absFile.equals(file)) { return ""; //$NON-NLS-1$ } return stripWorkDir(absWd, absFile); } String relName = filePath.substring(workDirPath.length() + 1); if (File.separatorChar != '/') { relName = relName.replace(File.separatorChar, '/'); } return relName; }
Whether this repository is bare
Returns:true if this is bare, which implies it has no working directory.
/** * Whether this repository is bare * * @return true if this is bare, which implies it has no working directory. */
public boolean isBare() { return workTree == null; }
Get the root directory of the working tree, where files are checked out for viewing and editing.
Throws:
Returns:the root directory of the working tree, where files are checked out for viewing and editing.
/** * Get the root directory of the working tree, where files are checked out * for viewing and editing. * * @return the root directory of the working tree, where files are checked * out for viewing and editing. * @throws org.eclipse.jgit.errors.NoWorkTreeException * if this is bare, which implies it has no working directory. * See {@link #isBare()}. */
@NonNull public File getWorkTree() throws NoWorkTreeException { if (isBare()) throw new NoWorkTreeException(); return workTree; }
Force a scan for changed refs. Fires an IndexChangedEvent(false) if changes are detected.
Throws:
  • IOException –
/** * Force a scan for changed refs. Fires an IndexChangedEvent(false) if * changes are detected. * * @throws java.io.IOException */
public abstract void scanForRepoChanges() throws IOException;
Notify that the index changed by firing an IndexChangedEvent.
Params:
  • internal – true if the index was changed by the same JGit process
Since:5.0
/** * Notify that the index changed by firing an IndexChangedEvent. * * @param internal * {@code true} if the index was changed by the same * JGit process * @since 5.0 */
public abstract void notifyIndexChanged(boolean internal);
Get a shortened more user friendly ref name
Params:
Returns:a more user friendly ref name
/** * Get a shortened more user friendly ref name * * @param refName * a {@link java.lang.String} object. * @return a more user friendly ref name */
@NonNull public static String shortenRefName(String refName) { if (refName.startsWith(Constants.R_HEADS)) return refName.substring(Constants.R_HEADS.length()); if (refName.startsWith(Constants.R_TAGS)) return refName.substring(Constants.R_TAGS.length()); if (refName.startsWith(Constants.R_REMOTES)) return refName.substring(Constants.R_REMOTES.length()); return refName; }
Get a shortened more user friendly remote tracking branch name
Params:
Returns:the remote branch name part of refName, i.e. without the refs/remotes/<remote> prefix, if refName represents a remote tracking branch; otherwise null.
Since:3.4
/** * Get a shortened more user friendly remote tracking branch name * * @param refName * a {@link java.lang.String} object. * @return the remote branch name part of <code>refName</code>, i.e. without * the <code>refs/remotes/&lt;remote&gt;</code> prefix, if * <code>refName</code> represents a remote tracking branch; * otherwise {@code null}. * @since 3.4 */
@Nullable public String shortenRemoteBranchName(String refName) { for (String remote : getRemoteNames()) { String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$ if (refName.startsWith(remotePrefix)) return refName.substring(remotePrefix.length()); } return null; }
Get remote name
Params:
Returns:the remote name part of refName, i.e. without the refs/remotes/<remote> prefix, if refName represents a remote tracking branch; otherwise null.
Since:3.4
/** * Get remote name * * @param refName * a {@link java.lang.String} object. * @return the remote name part of <code>refName</code>, i.e. without the * <code>refs/remotes/&lt;remote&gt;</code> prefix, if * <code>refName</code> represents a remote tracking branch; * otherwise {@code null}. * @since 3.4 */
@Nullable public String getRemoteName(String refName) { for (String remote : getRemoteNames()) { String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$ if (refName.startsWith(remotePrefix)) return remote; } return null; }
Read the GIT_DIR/description file for gitweb.
Throws:
Returns:description text; null if no description has been configured.
Since:4.6
/** * Read the {@code GIT_DIR/description} file for gitweb. * * @return description text; null if no description has been configured. * @throws java.io.IOException * description cannot be accessed. * @since 4.6 */
@Nullable public String getGitwebDescription() throws IOException { return null; }
Set the GIT_DIR/description file for gitweb.
Params:
  • description – new description; null to clear the description.
Throws:
Since:4.6
/** * Set the {@code GIT_DIR/description} file for gitweb. * * @param description * new description; null to clear the description. * @throws java.io.IOException * description cannot be persisted. * @since 4.6 */
public void setGitwebDescription(@Nullable String description) throws IOException { throw new IOException(JGitText.get().unsupportedRepositoryDescription); }
Get the reflog reader
Params:
Throws:
Returns:a ReflogReader for the supplied refname, or null if the named ref does not exist.
Since:3.0
/** * Get the reflog reader * * @param refName * a {@link java.lang.String} object. * @return a {@link org.eclipse.jgit.lib.ReflogReader} for the supplied * refname, or {@code null} if the named ref does not exist. * @throws java.io.IOException * the ref could not be accessed. * @since 3.0 */
@Nullable public abstract ReflogReader getReflogReader(String refName) throws IOException;
Return the information stored in the file $GIT_DIR/MERGE_MSG. In this file operations triggering a merge will store a template for the commit message of the merge commit.
Throws:
Returns:a String containing the content of the MERGE_MSG file or null if this file doesn't exist
/** * Return the information stored in the file $GIT_DIR/MERGE_MSG. In this * file operations triggering a merge will store a template for the commit * message of the merge commit. * * @return a String containing the content of the MERGE_MSG file or * {@code null} if this file doesn't exist * @throws java.io.IOException * @throws org.eclipse.jgit.errors.NoWorkTreeException * if this is bare, which implies it has no working directory. * See {@link #isBare()}. */
@Nullable public String readMergeCommitMsg() throws IOException, NoWorkTreeException { return readCommitMsgFile(Constants.MERGE_MSG); }
Write new content to the file $GIT_DIR/MERGE_MSG. In this file operations triggering a merge will store a template for the commit message of the merge commit. If null is specified as message the file will be deleted.
Params:
  • msg – the message which should be written or null to delete the file
Throws:
/** * Write new content to the file $GIT_DIR/MERGE_MSG. In this file operations * triggering a merge will store a template for the commit message of the * merge commit. If <code>null</code> is specified as message the file will * be deleted. * * @param msg * the message which should be written or <code>null</code> to * delete the file * @throws java.io.IOException */
public void writeMergeCommitMsg(String msg) throws IOException { File mergeMsgFile = new File(gitDir, Constants.MERGE_MSG); writeCommitMsg(mergeMsgFile, msg); }
Return the information stored in the file $GIT_DIR/COMMIT_EDITMSG. In this file hooks triggered by an operation may read or modify the current commit message.
Throws:
Returns:a String containing the content of the COMMIT_EDITMSG file or null if this file doesn't exist
Since:4.0
/** * Return the information stored in the file $GIT_DIR/COMMIT_EDITMSG. In * this file hooks triggered by an operation may read or modify the current * commit message. * * @return a String containing the content of the COMMIT_EDITMSG file or * {@code null} if this file doesn't exist * @throws java.io.IOException * @throws org.eclipse.jgit.errors.NoWorkTreeException * if this is bare, which implies it has no working directory. * See {@link #isBare()}. * @since 4.0 */
@Nullable public String readCommitEditMsg() throws IOException, NoWorkTreeException { return readCommitMsgFile(Constants.COMMIT_EDITMSG); }
Write new content to the file $GIT_DIR/COMMIT_EDITMSG. In this file hooks triggered by an operation may read or modify the current commit message. If null is specified as message the file will be deleted.
Params:
  • msg – the message which should be written or null to delete the file
Throws:
Since:4.0
/** * Write new content to the file $GIT_DIR/COMMIT_EDITMSG. In this file hooks * triggered by an operation may read or modify the current commit message. * If {@code null} is specified as message the file will be deleted. * * @param msg * the message which should be written or {@code null} to delete * the file * @throws java.io.IOException * @since 4.0 */
public void writeCommitEditMsg(String msg) throws IOException { File commiEditMsgFile = new File(gitDir, Constants.COMMIT_EDITMSG); writeCommitMsg(commiEditMsgFile, msg); }
Return the information stored in the file $GIT_DIR/MERGE_HEAD. In this file operations triggering a merge will store the IDs of all heads which should be merged together with HEAD.
Throws:
Returns:a list of commits which IDs are listed in the MERGE_HEAD file or null if this file doesn't exist. Also if the file exists but is empty null will be returned
/** * Return the information stored in the file $GIT_DIR/MERGE_HEAD. In this * file operations triggering a merge will store the IDs of all heads which * should be merged together with HEAD. * * @return a list of commits which IDs are listed in the MERGE_HEAD file or * {@code null} if this file doesn't exist. Also if the file exists * but is empty {@code null} will be returned * @throws java.io.IOException * @throws org.eclipse.jgit.errors.NoWorkTreeException * if this is bare, which implies it has no working directory. * See {@link #isBare()}. */
@Nullable public List<ObjectId> readMergeHeads() throws IOException, NoWorkTreeException { if (isBare() || getDirectory() == null) throw new NoWorkTreeException(); byte[] raw = readGitDirectoryFile(Constants.MERGE_HEAD); if (raw == null) return null; LinkedList<ObjectId> heads = new LinkedList<>(); for (int p = 0; p < raw.length;) { heads.add(ObjectId.fromString(raw, p)); p = RawParseUtils .nextLF(raw, p + Constants.OBJECT_ID_STRING_LENGTH); } return heads; }
Write new merge-heads into $GIT_DIR/MERGE_HEAD. In this file operations triggering a merge will store the IDs of all heads which should be merged together with HEAD. If null is specified as list of commits the file will be deleted
Params:
  • heads – a list of commits which IDs should be written to $GIT_DIR/MERGE_HEAD or null to delete the file
Throws:
/** * Write new merge-heads into $GIT_DIR/MERGE_HEAD. In this file operations * triggering a merge will store the IDs of all heads which should be merged * together with HEAD. If <code>null</code> is specified as list of commits * the file will be deleted * * @param heads * a list of commits which IDs should be written to * $GIT_DIR/MERGE_HEAD or <code>null</code> to delete the file * @throws java.io.IOException */
public void writeMergeHeads(List<? extends ObjectId> heads) throws IOException { writeHeadsFile(heads, Constants.MERGE_HEAD); }
Return the information stored in the file $GIT_DIR/CHERRY_PICK_HEAD.
Throws:
Returns:object id from CHERRY_PICK_HEAD file or null if this file doesn't exist. Also if the file exists but is empty null will be returned
/** * Return the information stored in the file $GIT_DIR/CHERRY_PICK_HEAD. * * @return object id from CHERRY_PICK_HEAD file or {@code null} if this file * doesn't exist. Also if the file exists but is empty {@code null} * will be returned * @throws java.io.IOException * @throws org.eclipse.jgit.errors.NoWorkTreeException * if this is bare, which implies it has no working directory. * See {@link #isBare()}. */
@Nullable public ObjectId readCherryPickHead() throws IOException, NoWorkTreeException { if (isBare() || getDirectory() == null) throw new NoWorkTreeException(); byte[] raw = readGitDirectoryFile(Constants.CHERRY_PICK_HEAD); if (raw == null) return null; return ObjectId.fromString(raw, 0); }
Return the information stored in the file $GIT_DIR/REVERT_HEAD.
Throws:
Returns:object id from REVERT_HEAD file or null if this file doesn't exist. Also if the file exists but is empty null will be returned
/** * Return the information stored in the file $GIT_DIR/REVERT_HEAD. * * @return object id from REVERT_HEAD file or {@code null} if this file * doesn't exist. Also if the file exists but is empty {@code null} * will be returned * @throws java.io.IOException * @throws org.eclipse.jgit.errors.NoWorkTreeException * if this is bare, which implies it has no working directory. * See {@link #isBare()}. */
@Nullable public ObjectId readRevertHead() throws IOException, NoWorkTreeException { if (isBare() || getDirectory() == null) throw new NoWorkTreeException(); byte[] raw = readGitDirectoryFile(Constants.REVERT_HEAD); if (raw == null) return null; return ObjectId.fromString(raw, 0); }
Write cherry pick commit into $GIT_DIR/CHERRY_PICK_HEAD. This is used in case of conflicts to store the cherry which was tried to be picked.
Params:
  • head – an object id of the cherry commit or null to delete the file
Throws:
/** * Write cherry pick commit into $GIT_DIR/CHERRY_PICK_HEAD. This is used in * case of conflicts to store the cherry which was tried to be picked. * * @param head * an object id of the cherry commit or <code>null</code> to * delete the file * @throws java.io.IOException */
public void writeCherryPickHead(ObjectId head) throws IOException { List<ObjectId> heads = (head != null) ? Collections.singletonList(head) : null; writeHeadsFile(heads, Constants.CHERRY_PICK_HEAD); }
Write revert commit into $GIT_DIR/REVERT_HEAD. This is used in case of conflicts to store the revert which was tried to be picked.
Params:
  • head – an object id of the revert commit or null to delete the file
Throws:
/** * Write revert commit into $GIT_DIR/REVERT_HEAD. This is used in case of * conflicts to store the revert which was tried to be picked. * * @param head * an object id of the revert commit or <code>null</code> to * delete the file * @throws java.io.IOException */
public void writeRevertHead(ObjectId head) throws IOException { List<ObjectId> heads = (head != null) ? Collections.singletonList(head) : null; writeHeadsFile(heads, Constants.REVERT_HEAD); }
Write original HEAD commit into $GIT_DIR/ORIG_HEAD.
Params:
  • head – an object id of the original HEAD commit or null to delete the file
Throws:
/** * Write original HEAD commit into $GIT_DIR/ORIG_HEAD. * * @param head * an object id of the original HEAD commit or <code>null</code> * to delete the file * @throws java.io.IOException */
public void writeOrigHead(ObjectId head) throws IOException { List<ObjectId> heads = head != null ? Collections.singletonList(head) : null; writeHeadsFile(heads, Constants.ORIG_HEAD); }
Return the information stored in the file $GIT_DIR/ORIG_HEAD.
Throws:
Returns:object id from ORIG_HEAD file or null if this file doesn't exist. Also if the file exists but is empty null will be returned
/** * Return the information stored in the file $GIT_DIR/ORIG_HEAD. * * @return object id from ORIG_HEAD file or {@code null} if this file * doesn't exist. Also if the file exists but is empty {@code null} * will be returned * @throws java.io.IOException * @throws org.eclipse.jgit.errors.NoWorkTreeException * if this is bare, which implies it has no working directory. * See {@link #isBare()}. */
@Nullable public ObjectId readOrigHead() throws IOException, NoWorkTreeException { if (isBare() || getDirectory() == null) throw new NoWorkTreeException(); byte[] raw = readGitDirectoryFile(Constants.ORIG_HEAD); return raw != null ? ObjectId.fromString(raw, 0) : null; }
Return the information stored in the file $GIT_DIR/SQUASH_MSG. In this file operations triggering a squashed merge will store a template for the commit message of the squash commit.
Throws:
Returns:a String containing the content of the SQUASH_MSG file or null if this file doesn't exist
/** * Return the information stored in the file $GIT_DIR/SQUASH_MSG. In this * file operations triggering a squashed merge will store a template for the * commit message of the squash commit. * * @return a String containing the content of the SQUASH_MSG file or * {@code null} if this file doesn't exist * @throws java.io.IOException * @throws NoWorkTreeException * if this is bare, which implies it has no working directory. * See {@link #isBare()}. */
@Nullable public String readSquashCommitMsg() throws IOException { return readCommitMsgFile(Constants.SQUASH_MSG); }
Write new content to the file $GIT_DIR/SQUASH_MSG. In this file operations triggering a squashed merge will store a template for the commit message of the squash commit. If null is specified as message the file will be deleted.
Params:
  • msg – the message which should be written or null to delete the file
Throws:
/** * Write new content to the file $GIT_DIR/SQUASH_MSG. In this file * operations triggering a squashed merge will store a template for the * commit message of the squash commit. If <code>null</code> is specified as * message the file will be deleted. * * @param msg * the message which should be written or <code>null</code> to * delete the file * @throws java.io.IOException */
public void writeSquashCommitMsg(String msg) throws IOException { File squashMsgFile = new File(gitDir, Constants.SQUASH_MSG); writeCommitMsg(squashMsgFile, msg); } @Nullable private String readCommitMsgFile(String msgFilename) throws IOException { if (isBare() || getDirectory() == null) throw new NoWorkTreeException(); File mergeMsgFile = new File(getDirectory(), msgFilename); try { return RawParseUtils.decode(IO.readFully(mergeMsgFile)); } catch (FileNotFoundException e) { if (mergeMsgFile.exists()) { throw e; } // the file has disappeared in the meantime ignore it return null; } } private void writeCommitMsg(File msgFile, String msg) throws IOException { if (msg != null) { try (FileOutputStream fos = new FileOutputStream(msgFile)) { fos.write(msg.getBytes(UTF_8)); } } else { FileUtils.delete(msgFile, FileUtils.SKIP_MISSING); } }
Read a file from the git directory.
Params:
  • filename –
Throws:
Returns:the raw contents or null if the file doesn't exist or is empty
/** * Read a file from the git directory. * * @param filename * @return the raw contents or {@code null} if the file doesn't exist or is * empty * @throws IOException */
private byte[] readGitDirectoryFile(String filename) throws IOException { File file = new File(getDirectory(), filename); try { byte[] raw = IO.readFully(file); return raw.length > 0 ? raw : null; } catch (FileNotFoundException notFound) { if (file.exists()) { throw notFound; } return null; } }
Write the given heads to a file in the git directory.
Params:
  • heads – a list of object ids to write or null if the file should be deleted.
  • filename –
Throws:
/** * Write the given heads to a file in the git directory. * * @param heads * a list of object ids to write or null if the file should be * deleted. * @param filename * @throws FileNotFoundException * @throws IOException */
private void writeHeadsFile(List<? extends ObjectId> heads, String filename) throws FileNotFoundException, IOException { File headsFile = new File(getDirectory(), filename); if (heads != null) { try (OutputStream bos = new BufferedOutputStream( new FileOutputStream(headsFile))) { for (ObjectId id : heads) { id.copyTo(bos); bos.write('\n'); } } } else { FileUtils.delete(headsFile, FileUtils.SKIP_MISSING); } }
Read a file formatted like the git-rebase-todo file. The "done" file is also formatted like the git-rebase-todo file. These files can be found in .git/rebase-merge/ or .git/rebase-append/ folders.
Params:
  • path – path to the file relative to the repository's git-dir. E.g. "rebase-merge/git-rebase-todo" or "rebase-append/done"
  • includeComments – true if also comments should be reported
Throws:
Returns:the list of steps
Since:3.2
/** * Read a file formatted like the git-rebase-todo file. The "done" file is * also formatted like the git-rebase-todo file. These files can be found in * .git/rebase-merge/ or .git/rebase-append/ folders. * * @param path * path to the file relative to the repository's git-dir. E.g. * "rebase-merge/git-rebase-todo" or "rebase-append/done" * @param includeComments * <code>true</code> if also comments should be reported * @return the list of steps * @throws java.io.IOException * @since 3.2 */
@NonNull public List<RebaseTodoLine> readRebaseTodo(String path, boolean includeComments) throws IOException { return new RebaseTodoFile(this).readRebaseTodo(path, includeComments); }
Write a file formatted like a git-rebase-todo file.
Params:
  • path – path to the file relative to the repository's git-dir. E.g. "rebase-merge/git-rebase-todo" or "rebase-append/done"
  • steps – the steps to be written
  • append – whether to append to an existing file or to write a new file
Throws:
Since:3.2
/** * Write a file formatted like a git-rebase-todo file. * * @param path * path to the file relative to the repository's git-dir. E.g. * "rebase-merge/git-rebase-todo" or "rebase-append/done" * @param steps * the steps to be written * @param append * whether to append to an existing file or to write a new file * @throws java.io.IOException * @since 3.2 */
public void writeRebaseTodoFile(String path, List<RebaseTodoLine> steps, boolean append) throws IOException { new RebaseTodoFile(this).writeRebaseTodoFile(path, steps, append); }
Get the names of all known remotes
Returns:the names of all known remotes
Since:3.4
/** * Get the names of all known remotes * * @return the names of all known remotes * @since 3.4 */
@NonNull public Set<String> getRemoteNames() { return getConfig() .getSubsections(ConfigConstants.CONFIG_REMOTE_SECTION); }
Check whether any housekeeping is required; if yes, run garbage collection; if not, exit without performing any work. Some JGit commands run autoGC after performing operations that could create many loose objects.

Currently this option is supported for repositories of type FileRepository only. See GC.setAuto(boolean) for configuration details.

Params:
  • monitor – to report progress
Since:4.6
/** * Check whether any housekeeping is required; if yes, run garbage * collection; if not, exit without performing any work. Some JGit commands * run autoGC after performing operations that could create many loose * objects. * <p> * Currently this option is supported for repositories of type * {@code FileRepository} only. See * {@link org.eclipse.jgit.internal.storage.file.GC#setAuto(boolean)} for * configuration details. * * @param monitor * to report progress * @since 4.6 */
public void autoGC(ProgressMonitor monitor) { // default does nothing } }