Java tutorial
/* * Copyright 2013 JBoss Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kie.commons.java.nio.fs.jgit.util; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; 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.TimeZone; import org.eclipse.jgit.api.CreateBranchCommand; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.ListBranchCommand; import org.eclipse.jgit.api.LogCommand; import org.eclipse.jgit.api.MergeResult; import org.eclipse.jgit.api.ResetCommand; import org.eclipse.jgit.api.errors.CheckoutConflictException; import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.InvalidRemoteException; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.api.errors.NoHeadException; import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheBuilder; import org.eclipse.jgit.dircache.DirCacheCheckout; import org.eclipse.jgit.dircache.DirCacheEditor; import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.CommitBuilder; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryCache; import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.merge.MergeMessageFormatter; import org.eclipse.jgit.merge.MergeStrategy; import org.eclipse.jgit.merge.Merger; import org.eclipse.jgit.merge.ResolveMerger; import org.eclipse.jgit.merge.SquashMessageFormatter; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalkUtils; import org.eclipse.jgit.storage.file.FileRepository; import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.FetchResult; import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.treewalk.CanonicalTreeParser; import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.PathFilter; import org.kie.commons.data.Pair; import org.kie.commons.java.nio.IOException; import org.kie.commons.java.nio.base.FileTimeImpl; import org.kie.commons.java.nio.base.version.VersionAttributes; import org.kie.commons.java.nio.base.version.VersionHistory; import org.kie.commons.java.nio.base.version.VersionRecord; import org.kie.commons.java.nio.file.NoSuchFileException; import org.kie.commons.java.nio.file.attribute.FileTime; import org.kie.commons.java.nio.fs.jgit.JGitFileSystem; import static java.util.Collections.*; import static org.eclipse.jgit.api.MergeResult.*; import static org.eclipse.jgit.api.MergeResult.MergeStatus.*; import static org.eclipse.jgit.lib.Constants.*; import static org.eclipse.jgit.lib.FileMode.*; import static org.eclipse.jgit.treewalk.filter.PathFilterGroup.*; import static org.eclipse.jgit.util.FS.*; import static org.kie.commons.data.Pair.*; import static org.kie.commons.validation.Preconditions.*; public final class JGitUtil { private JGitUtil() { } public static Git newRepository(final File repoFolder, final boolean bare) throws IOException { checkNotNull("repoFolder", repoFolder); try { return Git.init().setBare(bare).setDirectory(repoFolder).call(); } catch (GitAPIException e) { throw new IOException(e); } } public static List<Ref> branchList(final Git git) { checkNotNull("git", git); return branchList(git, null); } public static List<Ref> branchList(final Git git, final ListBranchCommand.ListMode listMode) { checkNotNull("git", git); try { return git.branchList().setListMode(listMode).call(); } catch (GitAPIException e) { throw new RuntimeException(e); } } public static InputStream resolveInputStream(final Git git, final String treeRef, final String path) { checkNotNull("git", git); checkNotEmpty("treeRef", treeRef); checkNotEmpty("path", path); final String gitPath = fixPath(path); RevWalk rw = null; TreeWalk tw = null; try { final ObjectId tree = git.getRepository().resolve(treeRef + "^{tree}"); rw = new RevWalk(git.getRepository()); tw = new TreeWalk(git.getRepository()); tw.setFilter(createFromStrings(singleton(gitPath))); tw.reset(tree); while (tw.next()) { if (tw.isSubtree() && !gitPath.equals(tw.getPathString())) { tw.enterSubtree(); continue; } final ObjectId entid = tw.getObjectId(0); final FileMode entmode = tw.getFileMode(0); final RevObject ro = rw.lookupAny(entid, entmode.getObjectType()); rw.parseBody(ro); final ObjectLoader ldr = git.getRepository().open(ro.getId(), Constants.OBJ_BLOB); return ldr.openStream(); } } catch (final Throwable t) { throw new NoSuchFileException("Can't find '" + gitPath + "' in tree '" + treeRef + "'"); } finally { if (rw != null) { rw.dispose(); } if (tw != null) { tw.release(); } } throw new NoSuchFileException(""); } private static String fixPath(final String path) { if (path.equals("/")) { return ""; } boolean startsWith = path.startsWith("/"); boolean endsWith = path.endsWith("/"); if (startsWith && endsWith) { return path.substring(1, path.length() - 1); } if (startsWith) { return path.substring(1); } if (endsWith) { return path.substring(0, path.length() - 1); } return path; } public static Git cloneRepository(final File repoFolder, final String fromURI, final boolean bare, final CredentialsProvider credentialsProvider) { if (!repoFolder.getName().endsWith(DOT_GIT_EXT)) { throw new RuntimeException("Invalid name"); } try { final File gitDir = RepositoryCache.FileKey.resolve(repoFolder, DETECTED); final Repository repository; final Git git; if (gitDir != null && gitDir.exists()) { repository = new FileRepository(gitDir); git = new Git(repository); } else { git = Git.cloneRepository().setBare(bare).setCloneAllBranches(true).setURI(fromURI) .setDirectory(repoFolder).setCredentialsProvider(credentialsProvider).call(); repository = git.getRepository(); } fetchRepository(git, credentialsProvider); repository.close(); return git; } catch (final Exception ex) { throw new RuntimeException(ex); } } public static void fetchRepository(final Git git, final CredentialsProvider credentialsProvider, final RefSpec... refSpecs) throws InvalidRemoteException { final List<RefSpec> specs = new ArrayList<RefSpec>(); if (refSpecs == null || refSpecs.length == 0) { specs.add(new RefSpec("+refs/heads/*:refs/remotes/origin/*")); specs.add(new RefSpec("+refs/tags/*:refs/tags/*")); specs.add(new RefSpec("+refs/notes/*:refs/notes/*")); } else { specs.addAll(Arrays.asList(refSpecs)); } try { git.fetch().setCredentialsProvider(credentialsProvider).setRefSpecs(specs).call(); } catch (final InvalidRemoteException e) { throw e; } catch (final Exception ex) { throw new RuntimeException(ex); } } public static void syncRepository(final Git git, final CredentialsProvider credentialsProvider, final String origin, boolean force) throws InvalidRemoteException { if (origin == null || origin.isEmpty()) { fetchRepository(git, credentialsProvider); } else { try { final StoredConfig config = git.getRepository().getConfig(); config.setString("remote", "upstream", "url", origin); config.save(); } catch (final Exception ex) { throw new RuntimeException(ex); } final List<RefSpec> specs = new ArrayList<RefSpec>(); specs.add(new RefSpec("+refs/heads/*:refs/remotes/upstream/*")); specs.add(new RefSpec("+refs/tags/*:refs/tags/*")); specs.add(new RefSpec("+refs/notes/*:refs/notes/*")); try { git.fetch().setCredentialsProvider(credentialsProvider).setRefSpecs(specs).setRemote(origin).call(); git.branchCreate().setName("master") .setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.SET_UPSTREAM) .setStartPoint("upstream/master").setForce(true).call(); } catch (final InvalidRemoteException e) { throw e; } catch (final Exception ex) { throw new RuntimeException(ex); } } } public static void delete(final Git git, final String branchName, final String path, final String name, final String email, final String message, final TimeZone timeZone, final Date when) { commit(git, branchName, name, email, message, timeZone, when, new HashMap<String, File>() { { put(path, null); } }); } public static ObjectId getTreeRefObjectId(final Repository repo, final String treeRef) { try { return repo.resolve(treeRef + "^{tree}"); } catch (java.io.IOException ex) { throw new RuntimeException(ex); } } public static List<DiffEntry> getDiff(final Repository repo, final ObjectId oldRef, final ObjectId newRef) { try { ObjectReader reader = repo.newObjectReader(); CanonicalTreeParser oldTreeIter = new CanonicalTreeParser(); oldTreeIter.reset(reader, oldRef); CanonicalTreeParser newTreeIter = new CanonicalTreeParser(); newTreeIter.reset(reader, newRef); return new Git(repo).diff().setNewTree(newTreeIter).setOldTree(oldTreeIter) .setShowNameAndStatusOnly(true).call(); } catch (Exception ex) { throw new RuntimeException(ex); } } public static void commit(final Git git, final String branchName, final String name, final String email, final String message, final TimeZone timeZone, final Date when, final Map<String, File> content) { final PersonIdent author = buildPersonIdent(git, name, email, timeZone, when); try { final ObjectInserter odi = git.getRepository().newObjectInserter(); try { // Create the in-memory index of the new/updated issue. final ObjectId headId = git.getRepository().resolve(branchName + "^{commit}"); final DirCache index = createTemporaryIndex(git, headId, content); final ObjectId indexTreeId = index.writeTree(odi); // Create a commit object final CommitBuilder commit = new CommitBuilder(); commit.setAuthor(author); commit.setCommitter(author); commit.setEncoding(Constants.CHARACTER_ENCODING); commit.setMessage(message); //headId can be null if the repository has no commit yet if (headId != null) { commit.setParentId(headId); } commit.setTreeId(indexTreeId); // Insert the commit into the repository final ObjectId commitId = odi.insert(commit); odi.flush(); final RevWalk revWalk = new RevWalk(git.getRepository()); try { final RevCommit revCommit = revWalk.parseCommit(commitId); final RefUpdate ru = git.getRepository().updateRef("refs/heads/" + branchName); if (headId == null) { ru.setExpectedOldObjectId(ObjectId.zeroId()); } else { ru.setExpectedOldObjectId(headId); } ru.setNewObjectId(commitId); ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false); final RefUpdate.Result rc = ru.forceUpdate(); switch (rc) { case NEW: case FORCED: case FAST_FORWARD: break; case REJECTED: case LOCK_FAILURE: throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD, ru.getRef(), rc); default: throw new JGitInternalException(MessageFormat.format(JGitText.get().updatingRefFailed, Constants.HEAD, commitId.toString(), rc)); } } finally { revWalk.release(); } } finally { odi.release(); } } catch (final Throwable t) { throw new RuntimeException(t); } } private static PersonIdent buildPersonIdent(final Git git, final String name, final String email, final TimeZone timeZone, final Date when) { final TimeZone tz = timeZone == null ? TimeZone.getDefault() : timeZone; if (name != null) { if (when != null) { return new PersonIdent(name, email, when, tz); } else { return new PersonIdent(name, email); } } return new PersonIdent(git.getRepository()); } /** * Creates an in-memory index of the issue change. */ private static DirCache createTemporaryIndex(final Git git, final ObjectId headId, final Map<String, File> content) { final DirCache inCoreIndex = DirCache.newInCore(); final DirCacheBuilder dcBuilder = inCoreIndex.builder(); final ObjectInserter inserter = git.getRepository().newObjectInserter(); boolean hadFile = false; final Set<String> paths = new HashSet<String>(content.size()); try { for (final Map.Entry<String, File> pathAndContent : content.entrySet()) { final String gPath = fixPath(pathAndContent.getKey()); paths.add(gPath); if (pathAndContent.getValue() != null) { hadFile = true; final DirCacheEntry dcEntry = new DirCacheEntry(gPath); dcEntry.setLength(pathAndContent.getValue().length()); dcEntry.setLastModified(pathAndContent.getValue().lastModified()); dcEntry.setFileMode(REGULAR_FILE); final InputStream inputStream = new FileInputStream(pathAndContent.getValue()); try { final ObjectId objectId = inserter.insert(Constants.OBJ_BLOB, pathAndContent.getValue().length(), inputStream); dcEntry.setObjectId(objectId); } finally { inputStream.close(); } dcBuilder.add(dcEntry); } if (!hadFile) { final DirCacheEditor editor = inCoreIndex.editor(); editor.add(new DirCacheEditor.DeleteTree(gPath)); editor.finish(); } } if (headId != null) { final TreeWalk treeWalk = new TreeWalk(git.getRepository()); final int hIdx = treeWalk.addTree(new RevWalk(git.getRepository()).parseTree(headId)); treeWalk.setRecursive(true); while (treeWalk.next()) { final String walkPath = treeWalk.getPathString(); final CanonicalTreeParser hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class); if (!paths.contains(walkPath)) { // add entries from HEAD for all other paths // create a new DirCacheEntry with data retrieved from HEAD final DirCacheEntry dcEntry = new DirCacheEntry(walkPath); dcEntry.setObjectId(hTree.getEntryObjectId()); dcEntry.setFileMode(hTree.getEntryFileMode()); // add to temporary in-core index dcBuilder.add(dcEntry); } } treeWalk.release(); } dcBuilder.finish(); } catch (Exception e) { throw new RuntimeException(e); } finally { inserter.release(); } return inCoreIndex; } public static ObjectId resolveObjectId(final Git git, final String name) { try { final Ref refName = getBranch(git, name); if (refName != null) { return refName.getObjectId(); } try { final ObjectId id = ObjectId.fromString(name); if (git.getRepository().getObjectDatabase().has(id)) { return id; } } catch (final IllegalArgumentException ex) { } return null; } catch (java.io.IOException e) { } return null; } public static Ref getBranch(final Git git, final String name) { try { return git.getRepository().getRefDatabase().getRef(name); } catch (java.io.IOException e) { } return null; } public static void deleteBranch(final Git git, final Ref branch) { try { git.branchDelete().setBranchNames(branch.getName()).setForce(true).call(); } catch (final GitAPIException e) { throw new IOException(e); } } public static VersionAttributes buildVersionAttributes(final JGitFileSystem fs, final String branchName, final String path) { final JGitPathInfo pathInfo = resolvePath(fs.gitRepo(), branchName, path); if (pathInfo == null) { throw new NoSuchFileException(path); } final String gPath = fixPath(path); final ObjectId id = resolveObjectId(fs.gitRepo(), branchName); final List<VersionRecord> records = new ArrayList<VersionRecord>(); if (id != null) { try { final LogCommand logCommand = fs.gitRepo().log().add(id); if (!gPath.isEmpty()) { logCommand.addPath(gPath); } for (final RevCommit commit : logCommand.call()) { records.add(new VersionRecord() { @Override public String id() { return commit.name(); } @Override public String author() { return commit.getCommitterIdent().getName(); } @Override public String email() { return commit.getCommitterIdent().getEmailAddress(); } @Override public String comment() { return commit.getFullMessage(); } @Override public Date date() { return commit.getCommitterIdent().getWhen(); } @Override public String uri() { return fs.getPath(commit.name(), path).toUri().toString(); } }); } } catch (Exception e) { throw new RuntimeException(e); } } Collections.sort(records, new Comparator<VersionRecord>() { @Override public int compare(final VersionRecord o1, final VersionRecord o2) { return o1.date().compareTo(o2.date()); } }); return new VersionAttributes() { @Override public VersionHistory history() { return new VersionHistory() { @Override public List<VersionRecord> records() { return records; } }; } @Override public FileTime lastModifiedTime() { if (records.size() > 0) { return new FileTimeImpl(records.get(records.size() - 1).date().getTime()); } return null; } @Override public FileTime lastAccessTime() { return null; } @Override public FileTime creationTime() { if (records.size() > 0) { return new FileTimeImpl(records.get(0).date().getTime()); } return null; } @Override public boolean isRegularFile() { return pathInfo.getPathType().equals(PathType.FILE); } @Override public boolean isDirectory() { return pathInfo.getPathType().equals(PathType.DIRECTORY); } @Override public boolean isSymbolicLink() { return false; } @Override public boolean isOther() { return false; } @Override public long size() { return pathInfo.getSize(); } @Override public Object fileKey() { return pathInfo.getObjectId() == null ? null : pathInfo.getObjectId().toString(); } }; } public static void createBranch(final Git git, final String source, final String target) { try { git.branchCreate().setName(target).setStartPoint(source).call(); } catch (GitAPIException e) { throw new RuntimeException(e); } } public static boolean hasBranch(final Git git, final String branchName) { checkNotNull("git", git); checkNotEmpty("branchName", branchName); return getBranch(git, branchName) != null; } public static enum PathType { NOT_FOUND, DIRECTORY, FILE; } public static Pair<PathType, ObjectId> checkPath(final Git git, final String branchName, final String path) { checkNotNull("git", git); checkNotNull("path", path); checkNotEmpty("branchName", branchName); final String gitPath = fixPath(path); if (gitPath.isEmpty()) { return newPair(PathType.DIRECTORY, null); } TreeWalk tw = null; try { final ObjectId tree = git.getRepository().resolve(branchName + "^{tree}"); tw = new TreeWalk(git.getRepository()); tw.setFilter(PathFilter.create(gitPath)); tw.reset(tree); while (tw.next()) { if (tw.getPathString().equals(gitPath)) { if (tw.getFileMode(0).equals(FileMode.TYPE_TREE)) { return newPair(PathType.DIRECTORY, tw.getObjectId(0)); } else if (tw.getFileMode(0).equals(FileMode.TYPE_FILE)) { return newPair(PathType.FILE, tw.getObjectId(0)); } } if (tw.isSubtree()) { tw.enterSubtree(); continue; } } } catch (final Throwable t) { } finally { if (tw != null) { tw.release(); } } return newPair(PathType.NOT_FOUND, null); } public static JGitPathInfo resolvePath(final Git git, final String branchName, final String path) { checkNotNull("git", git); checkNotNull("path", path); checkNotEmpty("branchName", branchName); final String gitPath = fixPath(path); if (gitPath.isEmpty()) { return new JGitPathInfo(null, "/", TREE); } TreeWalk tw = null; try { final ObjectId tree = git.getRepository().resolve(branchName + "^{tree}"); tw = new TreeWalk(git.getRepository()); tw.setFilter(PathFilter.create(gitPath)); tw.reset(tree); while (tw.next()) { if (tw.getPathString().equals(gitPath)) { if (tw.getFileMode(0).equals(TREE)) { return new JGitPathInfo(tw.getObjectId(0), tw.getPathString(), TREE); } else if (tw.getFileMode(0).equals(REGULAR_FILE) || tw.getFileMode(0).equals(EXECUTABLE_FILE)) { final long size = tw.getObjectReader().getObjectSize(tw.getObjectId(0), OBJ_BLOB); return new JGitPathInfo(tw.getObjectId(0), tw.getPathString(), REGULAR_FILE, size); } } if (tw.isSubtree()) { tw.enterSubtree(); continue; } } } catch (final Throwable t) { } finally { if (tw != null) { tw.release(); } } return null; } public static List<JGitPathInfo> listPathContent(final Git git, final String branchName, final String path) { checkNotNull("git", git); checkNotNull("path", path); checkNotEmpty("branchName", branchName); final String gitPath = fixPath(path); TreeWalk tw = null; final List<JGitPathInfo> result = new ArrayList<JGitPathInfo>(); try { final ObjectId tree = git.getRepository().resolve(branchName + "^{tree}"); tw = new TreeWalk(git.getRepository()); boolean found = false; if (gitPath.isEmpty()) { found = true; } else { tw.setFilter(PathFilter.create(gitPath)); } tw.reset(tree); while (tw.next()) { if (!found && tw.isSubtree()) { tw.enterSubtree(); } if (tw.getPathString().equals(gitPath)) { found = true; continue; } if (found) { result.add(new JGitPathInfo(tw.getObjectId(0), tw.getPathString(), tw.getFileMode(0))); } } } catch (final Throwable t) { } finally { if (tw != null) { tw.release(); } } return result; } public static MergeResult mergeBranches(final Git git, final String source, final String target) throws Exception { final Repository repo = git.getRepository(); final MergeStrategy mergeStrategy = MergeStrategy.RESOLVE; final List<Ref> commits = new LinkedList<Ref>(); final boolean squash = false; RevWalk revWalk = null; DirCacheCheckout dco = null; try { Ref head = repo.getRef(Constants.HEAD); if (head == null) { throw new NoHeadException(JGitText.get().commitOnRepoWithoutHEADCurrentlyNotSupported); } final StringBuilder refLogMessage = new StringBuilder("merge "); // Check for FAST_FORWARD, ALREADY_UP_TO_DATE revWalk = new RevWalk(repo); // we know for now there is only one commit Ref ref = commits.get(0); refLogMessage.append(ref.getName()); // handle annotated tags ObjectId objectId = ref.getPeeledObjectId(); if (objectId == null) { objectId = ref.getObjectId(); } final RevCommit srcCommit = revWalk.lookupCommit(objectId); ObjectId headId = head.getObjectId(); if (headId == null) { revWalk.parseHeaders(srcCommit); dco = new DirCacheCheckout(repo, repo.lockDirCache(), srcCommit.getTree()); dco.setFailOnConflict(true); dco.checkout(); RefUpdate refUpdate = repo.updateRef(head.getTarget().getName()); refUpdate.setNewObjectId(objectId); refUpdate.setExpectedOldObjectId(null); refUpdate.setRefLogMessage("initial pull", false); if (refUpdate.update() != RefUpdate.Result.NEW) { throw new NoHeadException(JGitText.get().commitOnRepoWithoutHEADCurrentlyNotSupported); } return new MergeResult(srcCommit, srcCommit, new ObjectId[] { null, srcCommit }, MergeStatus.FAST_FORWARD, mergeStrategy, null, null); } final RevCommit headCommit = revWalk.lookupCommit(headId); if (revWalk.isMergedInto(srcCommit, headCommit)) { return new MergeResult(headCommit, srcCommit, new ObjectId[] { headCommit, srcCommit }, ALREADY_UP_TO_DATE, mergeStrategy, null, null); } else if (revWalk.isMergedInto(headCommit, srcCommit)) { // FAST_FORWARD detected: skip doing a real merge but only // update HEAD refLogMessage.append(": " + FAST_FORWARD); dco = new DirCacheCheckout(repo, headCommit.getTree(), repo.lockDirCache(), srcCommit.getTree()); dco.setFailOnConflict(true); dco.checkout(); String msg = null; ObjectId newHead, base = null; final MergeStatus mergeStatus; if (!squash) { updateHead(git, refLogMessage, srcCommit, headId); newHead = base = srcCommit; mergeStatus = FAST_FORWARD; } else { msg = JGitText.get().squashCommitNotUpdatingHEAD; newHead = base = headId; mergeStatus = FAST_FORWARD_SQUASHED; final List<RevCommit> squashedCommits = RevWalkUtils.find(revWalk, srcCommit, headCommit); final String squashMessage = new SquashMessageFormatter().format(squashedCommits, head); repo.writeSquashCommitMsg(squashMessage); } return new MergeResult(newHead, base, new ObjectId[] { headCommit, srcCommit }, mergeStatus, mergeStrategy, null, msg); } else { String mergeMessage = ""; if (!squash) { mergeMessage = new MergeMessageFormatter().format(commits, head); repo.writeMergeCommitMsg(mergeMessage); repo.writeMergeHeads(Arrays.asList(ref.getObjectId())); } else { final List<RevCommit> squashedCommits = RevWalkUtils.find(revWalk, srcCommit, headCommit); final String squashMessage = new SquashMessageFormatter().format(squashedCommits, head); repo.writeSquashCommitMsg(squashMessage); } boolean noProblems; final Merger merger = mergeStrategy.newMerger(repo); final Map<String, org.eclipse.jgit.merge.MergeResult<?>> lowLevelResults; final Map<String, ResolveMerger.MergeFailureReason> failingPaths; final List<String> unmergedPaths; if (merger instanceof ResolveMerger) { ResolveMerger resolveMerger = (ResolveMerger) merger; resolveMerger.setCommitNames(new String[] { "BASE", "HEAD", ref.getName() }); resolveMerger.setWorkingTreeIterator(new FileTreeIterator(repo)); noProblems = merger.merge(headCommit, srcCommit); lowLevelResults = resolveMerger.getMergeResults(); failingPaths = resolveMerger.getFailingPaths(); unmergedPaths = resolveMerger.getUnmergedPaths(); } else { noProblems = merger.merge(headCommit, srcCommit); lowLevelResults = emptyMap(); failingPaths = emptyMap(); unmergedPaths = emptyList(); } refLogMessage.append(": Merge made by "); refLogMessage.append(mergeStrategy.getName()); refLogMessage.append('.'); if (noProblems) { dco = new DirCacheCheckout(repo, headCommit.getTree(), repo.lockDirCache(), merger.getResultTreeId()); dco.setFailOnConflict(true); dco.checkout(); String msg = null; RevCommit newHead = null; MergeStatus mergeStatus = null; if (!squash) { newHead = new Git(repo).commit().setReflogComment(refLogMessage.toString()).call(); mergeStatus = MERGED; } else { msg = JGitText.get().squashCommitNotUpdatingHEAD; newHead = headCommit; mergeStatus = MERGED_SQUASHED; } return new MergeResult(newHead.getId(), null, new ObjectId[] { headCommit.getId(), srcCommit.getId() }, mergeStatus, mergeStrategy, null, msg); } else { if (failingPaths != null && !failingPaths.isEmpty()) { repo.writeMergeCommitMsg(null); repo.writeMergeHeads(null); return new MergeResult(null, merger.getBaseCommit(0, 1), new ObjectId[] { headCommit.getId(), srcCommit.getId() }, FAILED, mergeStrategy, lowLevelResults, failingPaths, null); } else { final String mergeMessageWithConflicts = new MergeMessageFormatter() .formatWithConflicts(mergeMessage, unmergedPaths); repo.writeMergeCommitMsg(mergeMessageWithConflicts); return new MergeResult(null, merger.getBaseCommit(0, 1), new ObjectId[] { headCommit.getId(), srcCommit.getId() }, CONFLICTING, mergeStrategy, lowLevelResults, null); } } } } catch (org.eclipse.jgit.errors.CheckoutConflictException e) { final List<String> conflicts = (dco == null) ? Collections.<String>emptyList() : dco.getConflicts(); throw new CheckoutConflictException(conflicts, e); } catch (java.io.IOException e) { throw new JGitInternalException( MessageFormat.format(JGitText.get().exceptionCaughtDuringExecutionOfMergeCommand, e), e); } finally { if (revWalk != null) { revWalk.release(); } } } private static void updateHead(final Git git, final StringBuilder refLogMessage, final ObjectId newHeadId, final ObjectId oldHeadID) throws java.io.IOException, ConcurrentRefUpdateException { RefUpdate refUpdate = git.getRepository().updateRef(Constants.HEAD); refUpdate.setNewObjectId(newHeadId); refUpdate.setRefLogMessage(refLogMessage.toString(), false); refUpdate.setExpectedOldObjectId(oldHeadID); RefUpdate.Result rc = refUpdate.update(); switch (rc) { case NEW: case FAST_FORWARD: return; case REJECTED: case LOCK_FAILURE: throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD, refUpdate.getRef(), rc); default: throw new JGitInternalException(MessageFormat.format(JGitText.get().updatingRefFailed, Constants.HEAD, newHeadId.toString(), rc)); } } public static class JGitPathInfo { private final ObjectId objectId; private final String path; private final long size; private final PathType pathType; public JGitPathInfo(final ObjectId objectId, final String path, final FileMode fileMode) { this(objectId, path, fileMode, -1); } public JGitPathInfo(final ObjectId objectId, final String path, final FileMode fileMode, long size) { this.objectId = objectId; this.size = size; this.path = path; if (fileMode.equals(FileMode.TYPE_TREE)) { this.pathType = PathType.DIRECTORY; } else if (fileMode.equals(TYPE_FILE)) { this.pathType = PathType.FILE; } else { this.pathType = null; } } public ObjectId getObjectId() { return objectId; } public String getPath() { return path; } public PathType getPathType() { return pathType; } public long getSize() { return size; } } }