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.uberfire.java.nio.fs.jgit.util; import java.io.ByteArrayInputStream; 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.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.diff.DiffEntry; import org.eclipse.jgit.dircache.DirCache; 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.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.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.storage.file.FileRepository; import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.treewalk.CanonicalTreeParser; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.PathFilter; import org.uberfire.commons.data.Pair; import org.uberfire.java.nio.IOException; import org.uberfire.java.nio.base.FileTimeImpl; import org.uberfire.java.nio.base.version.VersionAttributes; import org.uberfire.java.nio.base.version.VersionHistory; import org.uberfire.java.nio.base.version.VersionRecord; import org.uberfire.java.nio.file.NoSuchFileException; import org.uberfire.java.nio.file.attribute.BasicFileAttributes; import org.uberfire.java.nio.file.attribute.FileTime; import org.uberfire.java.nio.fs.jgit.CommitInfo; import org.uberfire.java.nio.fs.jgit.JGitFileSystem; import static java.util.Collections.*; import static org.apache.commons.io.FileUtils.*; 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.uberfire.commons.data.Pair.*; import static org.uberfire.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; } return new ByteArrayInputStream( git.getRepository().open(tw.getObjectId(0), Constants.OBJ_BLOB).getBytes()); } } 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(""); } public 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) { try { forceDelete(repoFolder); } catch (final java.io.IOException e) { throw new RuntimeException(e); } 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 pushRepository(final Git git, final CredentialsProvider credentialsProvider, final String origin, boolean force) throws InvalidRemoteException { if (origin != null && !origin.isEmpty()) { 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.push().setCredentialsProvider(credentialsProvider).setRefSpecs(specs).setRemote(origin) .setForce(force).setPushAll().call(); } catch (final InvalidRemoteException e) { throw e; } catch (final Exception ex) { throw new RuntimeException(ex); } } } 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) { if (oldRef == null || newRef == null || repo == null) { return emptyList(); } try { ObjectReader reader = repo.newObjectReader(); CanonicalTreeParser oldTreeIter = new CanonicalTreeParser(); oldTreeIter.reset(reader, oldRef); CanonicalTreeParser newTreeIter = new CanonicalTreeParser(); newTreeIter.reset(reader, newRef); return new CustomDiffCommand(repo).setNewTree(newTreeIter).setOldTree(oldTreeIter) .setShowNameAndStatusOnly(true).call(); } catch (final 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 boolean amend, final Map<String, File> content) { commit(git, branchName, new CommitInfo(null, name, email, message, timeZone, when), amend, new DefaultCommitContent(content)); } public static boolean commit(final Git git, final String branchName, final CommitInfo commitInfo, final boolean amend, final CommitContent content) { if (content instanceof RevertCommitContent) { return commit(git, branchName, commitInfo, amend, resolveObjectId(git, ((RevertCommitContent) content).getRefTree()), content); } else { return commit(git, branchName, commitInfo, amend, null, content); } } public static boolean commit(final Git git, final String branchName, final CommitInfo commitInfo, final boolean amend, final ObjectId _originId, final CommitContent content) { boolean hadEffecitiveCommit = true; final PersonIdent author = buildPersonIdent(git, commitInfo.getName(), commitInfo.getEmail(), commitInfo.getTimeZone(), commitInfo.getWhen()); 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 ObjectId originId; if (_originId == null) { originId = git.getRepository().resolve(branchName + "^{commit}"); } else { originId = _originId; } final DirCache index; if (content instanceof DefaultCommitContent) { index = createTemporaryIndex(git, originId, (DefaultCommitContent) content); } else if (content instanceof MoveCommitContent) { index = createTemporaryIndex(git, originId, (MoveCommitContent) content); } else if (content instanceof CopyCommitContent) { index = createTemporaryIndex(git, originId, (CopyCommitContent) content); } else if (content instanceof RevertCommitContent) { index = createTemporaryIndex(git, originId); } else { index = null; } if (index != null) { 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(commitInfo.getMessage()); //headId can be null if the repository has no commit yet if (headId != null) { if (amend) { final List<ObjectId> parents = new LinkedList<ObjectId>(); final RevCommit previousCommit = new RevWalk(git.getRepository()).parseCommit(headId); final RevCommit[] p = previousCommit.getParents(); for (final RevCommit revCommit : p) { parents.add(0, revCommit.getId()); } commit.setParentIds(parents); } else { 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(); } } else { hadEffecitiveCommit = false; } } finally { odi.release(); } } catch (final Throwable t) { throw new RuntimeException(t); } return hadEffecitiveCommit; } 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; final String email = _email == null ? "" : _email; 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 DefaultCommitContent commitContent) { final Map<String, File> content = commitContent.getContent(); final Map<String, Pair<File, ObjectId>> paths = new HashMap<String, Pair<File, ObjectId>>(content.size()); final Set<String> path2delete = new HashSet<String>(); final DirCache inCoreIndex = DirCache.newInCore(); final ObjectInserter inserter = git.getRepository().newObjectInserter(); final DirCacheEditor editor = inCoreIndex.editor(); try { for (final Map.Entry<String, File> pathAndContent : content.entrySet()) { final String gPath = fixPath(pathAndContent.getKey()); if (pathAndContent.getValue() == null) { final TreeWalk treeWalk = new TreeWalk(git.getRepository()); treeWalk.addTree(new RevWalk(git.getRepository()).parseTree(headId)); treeWalk.setRecursive(true); treeWalk.setFilter(PathFilter.create(gPath)); while (treeWalk.next()) { path2delete.add(treeWalk.getPathString()); } treeWalk.release(); } else { try { final InputStream inputStream = new FileInputStream(pathAndContent.getValue()); try { final ObjectId objectId = inserter.insert(Constants.OBJ_BLOB, pathAndContent.getValue().length(), inputStream); paths.put(gPath, Pair.newPair(pathAndContent.getValue(), objectId)); } finally { inputStream.close(); } } catch (final Exception ex) { throw new RuntimeException(ex); } } } 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.containsKey(walkPath) && paths.get(walkPath).getK2().equals(hTree.getEntryObjectId())) { paths.remove(walkPath); } if (paths.get(walkPath) == null && !path2delete.contains(walkPath)) { final DirCacheEntry dcEntry = new DirCacheEntry(walkPath); final ObjectId _objectId = hTree.getEntryObjectId(); final FileMode _fileMode = hTree.getEntryFileMode(); // add to temporary in-core index editor.add(new DirCacheEditor.PathEdit(dcEntry) { @Override public void apply(final DirCacheEntry ent) { ent.setObjectId(_objectId); ent.setFileMode(_fileMode); } }); } } treeWalk.release(); } for (final Map.Entry<String, Pair<File, ObjectId>> pathAndContent : paths.entrySet()) { if (pathAndContent.getValue().getK1() != null) { editor.add(new DirCacheEditor.PathEdit(new DirCacheEntry(pathAndContent.getKey())) { @Override public void apply(final DirCacheEntry ent) { ent.setLength(pathAndContent.getValue().getK1().length()); ent.setLastModified(pathAndContent.getValue().getK1().lastModified()); ent.setFileMode(REGULAR_FILE); ent.setObjectId(pathAndContent.getValue().getK2()); } }); } } editor.finish(); } catch (Exception e) { throw new RuntimeException(e); } finally { inserter.release(); } if (path2delete.isEmpty() && paths.isEmpty()) { //no changes! return null; } return inCoreIndex; } private static DirCache createTemporaryIndex(final Git git, final ObjectId headId, final MoveCommitContent commitContent) { final Map<String, String> content = commitContent.getContent(); final DirCache inCoreIndex = DirCache.newInCore(); final DirCacheEditor editor = inCoreIndex.editor(); try { 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); final String toPath = content.get(walkPath); if (toPath == null) { final DirCacheEntry dcEntry = new DirCacheEntry(walkPath); final ObjectId _objectId = hTree.getEntryObjectId(); final FileMode _fileMode = hTree.getEntryFileMode(); // add to temporary in-core index editor.add(new DirCacheEditor.PathEdit(dcEntry) { @Override public void apply(final DirCacheEntry ent) { ent.setObjectId(_objectId); ent.setFileMode(_fileMode); } }); } else { final DirCacheEntry dcEntry = new DirCacheEntry(toPath); final ObjectId _objectId = hTree.getEntryObjectId(); final FileMode _fileMode = hTree.getEntryFileMode(); editor.add(new DirCacheEditor.PathEdit(dcEntry) { @Override public void apply(final DirCacheEntry ent) { ent.setFileMode(_fileMode); ent.setObjectId(_objectId); } }); } } treeWalk.release(); } editor.finish(); } catch (final Exception e) { throw new RuntimeException(e); } return inCoreIndex; } private static DirCache createTemporaryIndex(final Git git, final ObjectId headId, final CopyCommitContent commitContent) { final Map<String, String> content = commitContent.getContent(); final DirCache inCoreIndex = DirCache.newInCore(); final DirCacheEditor editor = inCoreIndex.editor(); try { 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); final String toPath = content.get(walkPath); final DirCacheEntry dcEntry = new DirCacheEntry(walkPath); final ObjectId _objectId = hTree.getEntryObjectId(); final FileMode _fileMode = hTree.getEntryFileMode(); // add to temporary in-core index editor.add(new DirCacheEditor.PathEdit(dcEntry) { @Override public void apply(final DirCacheEntry ent) { ent.setObjectId(_objectId); ent.setFileMode(_fileMode); } }); if (toPath != null) { final DirCacheEntry newdcEntry = new DirCacheEntry(toPath); final ObjectId newObjectId = hTree.getEntryObjectId(); final FileMode newFileMode = hTree.getEntryFileMode(); editor.add(new DirCacheEditor.PathEdit(newdcEntry) { @Override public void apply(final DirCacheEntry ent) { ent.setFileMode(newFileMode); ent.setObjectId(newObjectId); } }); } } treeWalk.release(); } editor.finish(); } catch (final Exception e) { throw new RuntimeException(e); } return inCoreIndex; } private static DirCache createTemporaryIndex(final Git git, final ObjectId headId) { final DirCache inCoreIndex = DirCache.newInCore(); final DirCacheEditor editor = inCoreIndex.editor(); try { 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); final DirCacheEntry dcEntry = new DirCacheEntry(walkPath); final ObjectId _objectId = hTree.getEntryObjectId(); final FileMode _fileMode = hTree.getEntryFileMode(); // add to temporary in-core index editor.add(new DirCacheEditor.PathEdit(dcEntry) { @Override public void apply(final DirCacheEntry ent) { ent.setObjectId(_objectId); ent.setFileMode(_fileMode); } }); } treeWalk.release(); } editor.finish(); } catch (final Exception e) { throw new RuntimeException(e); } 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.getAuthorIdent().getName(); } @Override public String email() { return commit.getAuthorIdent().getEmailAddress(); } @Override public String comment() { return commit.getFullMessage(); } @Override public Date date() { return commit.getAuthorIdent().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 BasicFileAttributes buildBasicAttributes(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 ObjectId id = resolveObjectId(fs.gitRepo(), branchName); final String gPath = fixPath(path); return new BasicFileAttributes() { private long lastModifiedDate = -1; private long creationDate = -1; @Override public FileTime lastModifiedTime() { if (lastModifiedDate == -1L) { RevWalk revWalk = null; try { final LogCommand logCommand = fs.gitRepo().log().add(id).setMaxCount(1); if (!gPath.isEmpty()) { logCommand.addPath(gPath); } revWalk = (RevWalk) logCommand.call(); lastModifiedDate = revWalk.iterator().next().getCommitterIdent().getWhen().getTime(); } catch (Exception ex) { lastModifiedDate = 0; } finally { if (revWalk != null) { revWalk.dispose(); } } } return new FileTimeImpl(lastModifiedDate); } @Override public FileTime lastAccessTime() { return null; } @Override public FileTime creationTime() { if (creationDate == -1L) { RevWalk revWalk = null; try { final LogCommand logCommand = fs.gitRepo().log().add(id).setMaxCount(1); if (!gPath.isEmpty()) { logCommand.addPath(gPath); } revWalk = (RevWalk) logCommand.call(); creationDate = revWalk.iterator().next().getCommitterIdent().getWhen().getTime(); } catch (Exception ex) { creationDate = 0; } finally { if (revWalk != null) { revWalk.dispose(); } } } return new FileTimeImpl(creationDate); } @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) || tw.getFileMode(0).equals(FileMode.EXECUTABLE_FILE) || tw.getFileMode(0).equals(FileMode.REGULAR_FILE)) { return newPair(PathType.FILE, tw.getObjectId(0)); } } if (tw.isSubtree()) { tw.enterSubtree(); } } } catch (final Throwable ignored) { } 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(); } } } catch (final Throwable ignored) { } 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 ignored) { } finally { if (tw != null) { tw.release(); } } return result; } 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; } } }