/*
* The MIT License
*
* Copyright (c) 2004-2011, Oracle Corporation, Andrew Bayer, Anton Kozak, Nikita Levyankov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.plugins.git;
import hudson.EnvVars;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Launcher.LocalLauncher;
import hudson.model.TaskListener;
import hudson.plugins.git.util.GitConstants;
import hudson.remoting.VirtualChannel;
import hudson.util.ArgumentListBuilder;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ListBranchCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.NoFilepatternException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.util.FS;
public class GitAPI implements IGitAPI {
private static final Logger LOGGER = Logger.getLogger(GitAPI.class.getName());
private Launcher launcher;
private FilePath workspace;
private TaskListener listener;
private String gitExe;
private EnvVars environment;
private Git jGitDelegate;
private PersonIdent author;
private PersonIdent committer;
public GitAPI(String gitExe, FilePath workspace, TaskListener listener, EnvVars environment) {
this.workspace = workspace;
this.listener = listener;
this.gitExe = gitExe;
this.environment = environment;
launcher = new LocalLauncher(GitSCM.VERBOSE ? listener : TaskListener.NULL);
if (hasGitRepo()) {//Wrap file repository if exists in order to perform operations and initialize jGitDelegate
try {
File gitDir = RepositoryCache.FileKey.resolve(new File(workspace.getRemote()), FS.DETECTED);
jGitDelegate = Git.wrap(new FileRepositoryBuilder().setGitDir(gitDir).build());
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Returns author
*
* @return {@link PersonIdent}
*/
protected PersonIdent getAuthor() {
return author;
}
/**
* Returns committer
*
* @return {@link PersonIdent}
*/
protected PersonIdent getCommitter() {
return committer;
}
public String getGitExe() {
return gitExe;
}
public EnvVars getEnvironment() {
return environment;
}
public void init() throws GitException {
if (hasGitRepo()) {
throw new GitException(Messages.GitAPI_Repository_FailedInitTwiceMsg());
}
jGitDelegate = Git.init().setDirectory(new File(workspace.getRemote())).call();
}
public boolean hasGitRepo() throws GitException {
return hasGitRepo(Constants.DOT_GIT);
}
public boolean hasGitRepo(String gitDir) throws GitException {
try {
FilePath dotGit = workspace.child(gitDir);
return dotGit.exists();
} catch (SecurityException ex) {
throw new GitException(Messages.GitAPI_Repository_SecurityFailureCheckMsg(), ex);
} catch (Exception e) {
throw new GitException(Messages.GitAPI_Repository_FailedCheckMsg(), e);
}
}
public boolean hasGitModules() throws GitException {
try {
FilePath dotGit = workspace.child(".gitmodules");
return dotGit.exists();
} catch (SecurityException ex) {
throw new GitException(
"Security error when trying to check for .gitmodules. Are you sure you have correct permissions?", ex);
} catch (Exception e) {
throw new GitException("Couldn't check for .gitmodules", e);
}
}
public List<IndexEntry> getSubmodules(String treeIsh) throws GitException {
List<IndexEntry> submodules = lsTree(treeIsh);
// Remove anything that isn't a submodule
for (Iterator<IndexEntry> it = submodules.iterator(); it.hasNext(); ) {
if (!it.next().getMode().equals("160000")) {
it.remove();
}
}
return submodules;
}
public boolean hasGitModules(String treeIsh) throws GitException {
return hasGitModules() && !getSubmodules(treeIsh).isEmpty();
}
public void fetch(String repository, String refspec) throws GitException {
listener.getLogger().println(
"Fetching upstream changes"
+ (repository != null ? " from " + repository : ""));
ArgumentListBuilder args = new ArgumentListBuilder();
args.add("fetch", "-t");
if (repository != null) {
args.add(repository);
if (refspec != null) {
args.add(refspec);
}
}
launchCommand(args);
}
public void fetch() throws GitException {
fetch(null, null);
}
/**
* Start from scratch and clone the whole repository. Cloning into an
* existing directory is not allowed, so the workspace is first deleted
* entirely, then <tt>git clone</tt> is performed.
*
* @param remoteConfig remote config
* @throws GitException if deleting or cloning the workspace fails
*/
public void clone(final RemoteConfig remoteConfig) throws GitException {
listener.getLogger().println(Messages.GitAPI_Repository_CloningRepositoryMsg(remoteConfig.getName()));
try {
workspace.deleteRecursive();
} catch (Exception e) {
e.printStackTrace(listener.error(Messages.GitAPI_Workspace_FailedCleanupMsg()));
throw new GitException(Messages.GitAPI_Workspace_FailedDeleteMsg(), e);
}
// Assume only 1 URL for this repository
final URIish source = remoteConfig.getURIs().get(0);
try {
workspace.act(new FilePath.FileCallable<String>() {
private static final long serialVersionUID = 1L;
public String invoke(File workspace,
VirtualChannel channel) throws IOException {
jGitDelegate = Git.cloneRepository()
.setDirectory(workspace.getAbsoluteFile())
.setURI(source.toPrivateString())
.setRemote(remoteConfig.getName())
.call();
return Messages.GitAPI_Repository_CloneSuccessMsg(source.toPrivateString(),
workspace.getAbsolutePath());
}
});
} catch (Exception e) {
throw new GitException(Messages.GitAPI_Repository_FailedCloneMsg(source), e);
}
}
public void clean() throws GitException {
verifyGitRepository();
jGitDelegate.clean().call();
}
public ObjectId revParse(String revName) throws GitException {
String result = launchCommand("rev-parse", revName);
return ObjectId.fromString(firstLine(result).trim());
}
public String describe(String commitIsh) throws GitException {
String result = launchCommand("describe", "--tags", commitIsh);
return firstLine(result).trim();
}
public void prune(RemoteConfig repository) throws GitException {
ArgumentListBuilder args = new ArgumentListBuilder();
args.add("remote", "prune", repository.getName());
launchCommand(args);
}
private String firstLine(String result) {
BufferedReader reader = new BufferedReader(new StringReader(result));
String line;
try {
line = reader.readLine();
if (line == null) {
return null;
}
if (reader.readLine() != null) {
throw new GitException("Result has multiple lines");
}
} catch (IOException e) {
throw new GitException("Error parsing result", e);
}
return line;
}
public void changelog(String revFrom, String revTo, OutputStream outputStream) throws GitException {
whatchanged(revFrom, revTo, outputStream, "--no-abbrev", "-M", "--pretty=raw");
}
private void whatchanged(String revFrom, String revTo, OutputStream outputStream, String... extraargs)
throws GitException {
String revSpec = revFrom + ".." + revTo;
ArgumentListBuilder args = new ArgumentListBuilder();
args.add(getGitExe(), "whatchanged");
args.add(extraargs);
args.add(revSpec);
try {
if (launcher.launch().cmds(args).envs(environment).stdout(
outputStream).pwd(workspace).join() != 0) {
throw new GitException("Error launching git whatchanged");
}
} catch (Exception e) {
throw new GitException("Error performing git whatchanged", e);
}
}
/**
* Given a Revision, show it as if it were an entry from git whatchanged, so that it
* can be parsed by GitChangeLogParser.
*
* @param r The Revision object
* @return The git show output, in List form.
* @throws GitException if errors were encountered running git show.
*/
public List<String> showRevision(Revision r) throws GitException {
String revName = r.getSha1String();
String result = "";
if (revName != null) {
result = launchCommand("show", "--no-abbrev", "--format=raw", "-M", "--raw", revName);
}
List<String> revShow = new ArrayList<String>();
if (result != null) {
revShow = new ArrayList<String>(Arrays.asList(result.split("\\n")));
}
return revShow;
}
/**
* Merge any changes into the head.
*
* @param revSpec the revision
* @throws GitException if the emrge fails
*/
public void merge(String revSpec) throws GitException {
try {
launchCommand("merge", revSpec);
} catch (GitException e) {
throw new GitException("Could not merge " + revSpec, e);
}
}
/**
* Init submodules.
*
* @throws GitException if executing the Git command fails
*/
public void submoduleInit() throws GitException {
launchCommand("submodule", "init");
}
/**
* Sync submodule URLs
*/
public void submoduleSync() throws GitException {
// Check if git submodule has sync support.
// Only available in git 1.6.1 and above
launchCommand("submodule", "sync");
}
/**
* Update submodules.
*
* @param recursive if true, will recursively update submodules (requires git>=1.6.5)
* @throws GitException if executing the Git command fails
*/
public void submoduleUpdate(boolean recursive) throws GitException {
ArgumentListBuilder args = new ArgumentListBuilder();
args.add("submodule", "update");
if (recursive) {
args.add("--init", "--recursive");
}
launchCommand(args);
}
/**
* Cleans submodules
*
* @param recursive if true, will recursively clean submodules (requres git>=1.6.5)
* @throws GitException if executing the git command fails
*/
public void submoduleClean(boolean recursive) throws GitException {
ArgumentListBuilder args = new ArgumentListBuilder();
args.add("submodule", "foreach");
if (recursive) {
args.add("--recursive");
}
args.add("git clean -fdx");
launchCommand(args);
}
/**
* Get submodule URL
*
* @param name The name of the submodule
* @throws GitException if executing the git command fails
*/
public String getSubmoduleUrl(String name) throws GitException {
String result = launchCommand("config", "--get", "submodule." + name + ".url");
return firstLine(result).trim();
}
/**
* Set submodule URL
*
* @param name The name of the submodule
* @param url The new value of the submodule's URL
* @throws GitException if executing the git command fails
*/
public void setSubmoduleUrl(String name, String url) throws GitException {
launchCommand("config", "submodule." + name + ".url", url);
}
/**
* Get a remote's URL
*
* @param name The name of the remote (e.g. origin)
* @throws GitException if executing the git command fails
*/
public String getRemoteUrl(String name) throws GitException {
String result = launchCommand("config", "--get", "remote." + name + ".url");
return firstLine(result).trim();
}
/**
* Set a remote's URL
*
* @param name The name of the remote (e.g. origin)
* @param url The new value of the remote's URL
* @throws GitException if executing the git command fails
*/
public void setRemoteUrl(String name, String url) throws GitException {
launchCommand("config", "remote." + name + ".url", url);
}
/**
* From a given repository, get a remote's URL
*
* @param name The name of the remote (e.g. origin)
* @param GIT_DIR The path to the repository (must be to .git dir)
* @throws GitException if executing the git command fails
*/
public String getRemoteUrl(String name, String GIT_DIR) throws GitException {
String result
= launchCommand("--git-dir=" + GIT_DIR,
"config", "--get", "remote." + name + ".url");
return firstLine(result).trim();
}
/**
* For a given repository, set a remote's URL
*
* @param name The name of the remote (e.g. origin)
* @param url The new value of the remote's URL
* @param GIT_DIR The path to the repository (must be to .git dir)
* @throws GitException if executing the git command fails
*/
public void setRemoteUrl(String name, String url, String GIT_DIR) throws GitException {
launchCommand("--git-dir=" + GIT_DIR,
"config", "remote." + name + ".url", url);
}
/**
* Get the default remote.
*
* @param _default_ The default remote to use if more than one exists.
* @return _default_ if it exists, otherwise return the first remote.
* @throws GitException if executing the git command fails
*/
public String getDefaultRemote(String _default_) throws GitException {
BufferedReader rdr =
new BufferedReader(
new StringReader(launchCommand("remote"))
);
List<String> remotes = new ArrayList<String>();
String line;
try {
while ((line = rdr.readLine()) != null) {
remotes.add(line);
}
} catch (IOException e) {
throw new GitException("Error parsing remotes", e);
}
if (remotes.contains(_default_)) {
return _default_;
} else if (remotes.size() >= 1) {
return remotes.get(0);
} else {
throw new GitException("No remotes found!");
}
}
/**
* Get the default remote.
*
* @return "origin" if it exists, otherwise return the first remote.
* @throws GitException if executing the git command fails
*/
public String getDefaultRemote() throws GitException {
return getDefaultRemote(Constants.DEFAULT_REMOTE_NAME);
}
/**
* Detect whether a repository is bare or not.
*
* @throws GitException
*/
public boolean isBareRepository() throws GitException {
return isBareRepository("");
}
/**
* Detect whether a repository at the given path is bare or not.
*
* @param GIT_DIR The path to the repository (must be to .git dir).
* @throws GitException
*/
public boolean isBareRepository(String GIT_DIR) throws GitException {
String ret;
if ("".equals(GIT_DIR)) {
ret = launchCommand("rev-parse", "--is-bare-repository");
} else {
String gitDir = "--git-dir=" + GIT_DIR;
ret = launchCommand(gitDir, "rev-parse", "--is-bare-repository");
}
if ("false".equals(firstLine(ret).trim())) {
return false;
} else {
return true;
}
}
private String pathJoin(String a, String b) {
return new File(a, b).toString();
}
/**
* Fixes urls for submodule as stored in .git/config and
* $SUBMODULE/.git/config for when the remote repo is NOT a bare repository.
* It is only really possible to detect whether a repository is bare if we
* have local access to the repository. If the repository is remote, we
* therefore must default to believing that it is either bare or NON-bare.
* The defaults are according to the ending of the super-project
* remote.origin.url:
* - Ends with "/.git": default is NON-bare
* - otherwise: default is bare
* .
*
* @param listener The task listener.
* @throws GitException if executing the git command fails
*/
public void fixSubmoduleUrls(String remote,
TaskListener listener) throws GitException {
boolean is_bare = true;
URI origin;
try {
String url = getRemoteUrl(remote);
// ensure that any /.git ending is removed
String gitEnd = pathJoin("", ".git");
if (url.endsWith(gitEnd)) {
url = url.substring(0, url.length() - gitEnd.length());
// change the default detection value to NON-bare
is_bare = false;
}
origin = new URI(url);
} catch (URISyntaxException e) {
// Sometimes the URI is of a form that we can't parse; like
// user@git.somehost.com:repository
// In these cases, origin is null and it's best to just exit early.
return;
} catch (Exception e) {
throw new GitException("Could determine remote.origin.url", e);
}
if (origin.getScheme() == null ||
("file".equalsIgnoreCase(origin.getScheme()) &&
(origin.getHost() == null || "".equals(origin.getHost()))
)
) {
// The uri is a local path, so we will test to see if it is a bare
// repository...
List<String> paths = new ArrayList<String>();
paths.add(origin.getPath());
paths.add(pathJoin(origin.getPath(), ".git"));
for (String path : paths) {
try {
is_bare = isBareRepository(path);
break;// we can break already if we don't have an exception
} catch (GitException e) {
LOGGER.log(Level.FINEST, "Exception occurred while detecting repository type by path: " + path);
}
}
}
if (!is_bare) {
try {
List<IndexEntry> submodules = getSubmodules("HEAD");
for (IndexEntry submodule : submodules) {
// First fix the URL to the submodule inside the super-project
String sUrl = pathJoin(origin.getPath(), submodule.getFile());
setSubmoduleUrl(submodule.getFile(), sUrl);
// Second, if the submodule already has been cloned, fix its own
// url...
String subGitDir = pathJoin(submodule.getFile(), ".git");
/* it is possible that the submodule does not exist yet
* since we wait until after checkout to do 'submodule
* udpate' */
if (hasGitRepo(subGitDir) && !"".equals(getRemoteUrl("origin", subGitDir))) {
setRemoteUrl("origin", sUrl, subGitDir);
}
}
} catch (GitException e) {
// this can fail for example HEAD doesn't exist yet
LOGGER.log(Level.FINEST, "Exception occurred while working with git repo.");
}
} else {
LOGGER.log(Level.FINER, "The origin is non-bare.");
// we've made a reasonable attempt to detect whether the origin is
// non-bare, so we'll just assume it is bare from here on out and
// thus the URLs are correct as given by (which is default behavior)
// git config --get submodule.NAME.url
}
}
/**
* Set up submodule URLs so that they correspond to the remote pertaining to
* the revision that has been checked out.
*/
public void setupSubmoduleUrls(Revision rev, TaskListener listener) throws GitException {
String remote;
Iterator<Branch> bi = rev.getBranches().iterator();
if (bi.hasNext()) {
// this is supposed to be a remote branch
String b = bi.next().getName();
if (b != null) {
int slash = b.indexOf('/');
if (slash == -1) {
throw new GitException("no remote from branch name (" + b + ")");
}
remote = getDefaultRemote(b.substring(0, slash));
} else {
remote = getDefaultRemote();
}
} else {
remote = getDefaultRemote();
}
setupSubmoduleUrls(remote, listener);
}
public void setupSubmoduleUrls(String remote, TaskListener listener) throws GitException {
// This is to make sure that we don't miss any new submodules or
// changes in submodule origin paths...
submoduleInit();
submoduleSync();
// This allows us to seamlessly use bare and non-bare superproject
// repositories.
fixSubmoduleUrls(remote, listener);
}
public void tag(String tagName, String comment) throws GitException {
tagName = tagName.replace(' ', '_');
try {
launchCommand("tag", "-a", "-f", "-m", comment, tagName);
} catch (GitException e) {
throw new GitException("Could not apply tag " + tagName, e);
}
}
/**
* Launch command using the workspace as working directory
*
* @param args
* @return command output
* @throws GitException
*/
public String launchCommand(ArgumentListBuilder args) throws GitException {
return launchCommandIn(args, workspace);
}
/**
* Launch command using the workspace as working directory
*
* @param args
* @return command output
* @throws GitException
*/
public String launchCommand(String... args) throws GitException {
return launchCommand(new ArgumentListBuilder(args));
}
/**
* @param args
* @param workDir
* @return command output
* @throws GitException
*/
private String launchCommandIn(ArgumentListBuilder args, FilePath workDir) throws GitException {
ByteArrayOutputStream fos = new ByteArrayOutputStream();
try {
args.prepend(getGitExe());
int status = launcher.launch().cmds(args.toCommandArray()).
envs(environment).stdout(fos).pwd(workDir).join();
String result = fos.toString();
if (status != 0) {
throw new GitException(
"Command \"" + StringUtils.join(args.toCommandArray(), " ") + "\" returned status code " + status
+ ": " + result);
}
return result;
} catch (Exception e) {
throw new GitException("Error performing command: " + StringUtils.join(args.toCommandArray(), " ")
+ "\n" + e.getMessage(), e);
}
}
public void push(RemoteConfig repository, String refspec) throws GitException {
ArgumentListBuilder args = new ArgumentListBuilder();
args.add("push", repository.getURIs().get(0).toPrivateString());
if (refspec != null) {
args.add(refspec);
}
launchCommand(args);
// Ignore output for now as there's many different formats
// That are possible.
}
private List<Branch> parseBranches(String fos) throws GitException {
// TODO: git branch -a -v --abbrev=0 would do this in one shot..
List<Branch> tags = new ArrayList<Branch>();
BufferedReader rdr = new BufferedReader(new StringReader(fos));
String line;
try {
while ((line = rdr.readLine()) != null) {
// Ignore the 1st
line = line.substring(2);
// Ignore '(no branch)' or anything with " -> ", since I think
// that's just noise
if ((!line.startsWith("("))
&& (line.indexOf(" -> ") == -1)) {
tags.add(new Branch(line, revParse(line)));
}
}
} catch (IOException e) {
throw new GitException("Error parsing branches", e);
}
return tags;
}
public List<Branch> getBranches() throws GitException {
verifyGitRepository();
List<Ref> refList = jGitDelegate.branchList().setListMode(ListBranchCommand.ListMode.ALL).call();
return parseRefList(refList);
}
public List<Branch> getRemoteBranches() throws GitException, IOException {
verifyGitRepository();
List<Ref> refList = jGitDelegate.branchList().setListMode(ListBranchCommand.ListMode.REMOTE).call();
return parseRefList(refList);
}
public List<Branch> getBranchesContaining(String revspec)
throws GitException {
return parseBranches(launchCommand("branch", "-a", "--contains", revspec));
}
public void checkout(String commitish) throws GitException {
checkoutBranch(null, commitish);
}
public void checkoutBranch(String branch, String commitish) throws GitException {
verifyGitRepository();
try {
// First, checkout to detached HEAD, so we can delete the branch.
launchCommand("checkout", "-f", commitish);
if (null != branch) {
jGitDelegate.checkout()
.setForce(true)
.setStartPoint(commitish)
.setName(branch)
.setCreateBranch(true)
.call();
}
} catch (GitAPIException e) {
throw new GitException(Messages.GitAPI_Branch_CheckoutErrorMsg(branch, commitish), e);
}
}
public boolean tagExists(String tagName) throws GitException {
tagName = tagName.replace(' ', '_');
return launchCommand("tag", "-l", tagName).trim().equals(tagName);
}
public void deleteBranch(String name) throws GitException {
verifyGitRepository();
try {
jGitDelegate.branchDelete().setBranchNames(name).call();
} catch (GitAPIException e) {
throw new GitException(Messages.GitAPI_Branch_DeleteErrorMsg(name), e);
}
}
public void deleteTag(String tagName) throws GitException {
tagName = tagName.replace(' ', '_');
try {
launchCommand("tag", "-d", tagName);
} catch (GitException e) {
throw new GitException("Could not delete tag " + tagName, e);
}
}
public List<IndexEntry> lsTree(String treeIsh) throws GitException {
List<IndexEntry> entries = new ArrayList<IndexEntry>();
String result = launchCommand("ls-tree", treeIsh);
BufferedReader rdr = new BufferedReader(new StringReader(result));
String line;
try {
while ((line = rdr.readLine()) != null) {
String[] entry = line.split("\\s+");
entries.add(new IndexEntry(entry[0], entry[1], entry[2],
entry[3]));
}
} catch (IOException e) {
throw new GitException("Error parsing ls tree", e);
}
return entries;
}
public List<ObjectId> revListAll() throws GitException {
return revList("--all");
}
public List<ObjectId> revListBranch(String branchId) throws GitException {
return revList(branchId);
}
public List<ObjectId> revList(String... extraArgs) throws GitException {
List<ObjectId> entries = new ArrayList<ObjectId>();
ArgumentListBuilder args = new ArgumentListBuilder("rev-list");
args.add(extraArgs);
String result = launchCommand(args);
BufferedReader rdr = new BufferedReader(new StringReader(result));
String line;
try {
while ((line = rdr.readLine()) != null) {
// Add the SHA1
entries.add(ObjectId.fromString(line));
}
} catch (IOException e) {
throw new GitException("Error parsing rev list", e);
}
return entries;
}
public boolean isCommitInRepo(String sha1) {
RevWalk revWalk = new RevWalk(jGitDelegate.getRepository());
try {
revWalk.parseCommit(ObjectId.fromString(sha1));
} catch (IOException e) {
return false;
}
return true;
}
public void add(String filePattern) throws GitException {
verifyGitRepository();
try {
jGitDelegate.add().addFilepattern(filePattern).call();
} catch (NoFilepatternException ex) {
throw new GitException(ex);
}
}
public void branch(String name) throws GitException {
verifyGitRepository();
try {
jGitDelegate.branchCreate().setName(name).call();
} catch (GitAPIException e) {
throw new GitException(Messages.GitAPI_Branch_CreateErrorMsg(name), e);
}
}
public void commit(String message) throws GitException {
verifyGitRepository();
parseEnvVars(getEnvironment());
try {
jGitDelegate.commit()
.setMessage(message)
.setAuthor(getAuthor())
.setCommitter(getCommitter())
.call();
} catch (Exception e) {
throw new GitException(Messages.GitAPI_Commit_FailedMsg(message), e);
}
}
public void commit(File f) throws GitException {
try {
launchCommand("commit", "-F", f.getAbsolutePath());
} catch (GitException e) {
throw new GitException("Cannot commit " + f, e);
}
}
public void fetch(RemoteConfig remoteRepository) throws GitException {
// Assume there is only 1 URL / refspec for simplicity
fetch(remoteRepository.getURIs().get(0).toPrivateString(),
remoteRepository.getFetchRefSpecs().get(0).toString());
}
public ObjectId mergeBase(ObjectId id1, ObjectId id2) {
try {
String result;
try {
result = launchCommand("merge-base", id1.name(), id2.name());
} catch (GitException ge) {
return null;
}
BufferedReader rdr = new BufferedReader(new StringReader(result));
String line;
while ((line = rdr.readLine()) != null) {
// Add the SHA1
return ObjectId.fromString(line);
}
} catch (Exception e) {
throw new GitException("Error parsing merge base", e);
}
return null;
}
public String getAllLogEntries(String branch) {
return launchCommand("log", "--all", "--pretty=format:'%H#%ct'", branch);
}
protected Repository getRepository() throws IOException {
verifyGitRepository();
return jGitDelegate.getRepository();
}
public List<Tag> getTagsOnCommit(String revName) throws GitException, IOException {
Repository db = getRepository();
ObjectId commit = db.resolve(revName);
List<Tag> result = new ArrayList<Tag>();
if (null != commit) {
for (final Map.Entry<String, Ref> tag : db.getTags().entrySet()) {
Ref ref = tag.getValue();
if (ref.getObjectId().equals(commit)) {
result.add(new Tag(tag.getKey(), ref.getObjectId()));
}
}
}
return result;
}
public Set<String> getTagNames(String tagPattern) throws GitException {
try {
ArgumentListBuilder args = new ArgumentListBuilder();
args.add("tag", "-l", tagPattern);
String result = launchCommandIn(args, workspace);
Set<String> tags = new HashSet<String>();
BufferedReader rdr = new BufferedReader(new StringReader(result));
String tag;
while ((tag = rdr.readLine()) != null) {
// Add the SHA1
tags.add(tag);
}
return tags;
} catch (Exception e) {
throw new GitException("Error retrieving tag names", e);
}
}
private void verifyGitRepository() {
if (!hasGitRepo() || null == jGitDelegate) {
throw new GitException(Messages.GitAPI_Repository_InvalidStateMsg());
}
}
protected List<Branch> parseRefList(List<Ref> refList) {
List<Branch> result = new ArrayList<Branch>();
if (CollectionUtils.isNotEmpty(refList)) {
for (Ref ref : refList) {
Branch buildBranch = new Branch(ref);
result.add(buildBranch);
listener.getLogger().println(Messages.GitAPI_Branch_BranchInRepoMsg(buildBranch.getName()));
}
}
return result;
}
/**
* Check environment variables for authors and committers data. If name or email is not empty -
* instantiate author/commiter as {@link PersonIdent}
*
* @param envVars environment variables.
* @see hudson.plugins.git.util.GitConstants#GIT_AUTHOR_NAME_ENV_VAR
* @see hudson.plugins.git.util.GitConstants#GIT_AUTHOR_EMAIL_ENV_VAR
* @see hudson.plugins.git.util.GitConstants#GIT_COMMITTER_NAME_ENV_VAR
* @see hudson.plugins.git.util.GitConstants#GIT_COMMITTER_EMAIL_ENV_VAR
*/
protected void parseEnvVars(EnvVars envVars) {
author = buildPersonIdent(envVars, GitConstants.GIT_AUTHOR_NAME_ENV_VAR, GitConstants.GIT_AUTHOR_EMAIL_ENV_VAR);
committer = buildPersonIdent(envVars, GitConstants.GIT_COMMITTER_NAME_ENV_VAR,
GitConstants.GIT_COMMITTER_EMAIL_ENV_VAR);
}
/**
* Create person from environment vars by name key and email key.
*
* @param envVars EnvVars.
* @param nameEnvKey String.
* @param emailEnvKey String.
* @return person instance
*/
private PersonIdent buildPersonIdent(EnvVars envVars, String nameEnvKey, String emailEnvKey) {
PersonIdent result = null;
if (null != envVars) {
String name = envVars.get(nameEnvKey);
String email = envVars.get(emailEnvKey);
if (StringUtils.isNotBlank(name) && StringUtils.isNotBlank(email)) {
result = new PersonIdent(name, email);
}
}
return result;
}
}