summaryrefslogtreecommitdiffstats
path: root/src/main/java/com/gitblit/utils/JGitUtils.java
diff options
context:
space:
mode:
authorMilos Cubrilo <milos.cubrilo@gmail.com>2015-01-11 13:41:29 +0100
committerMilos Cubrilo <milos.cubrilo@gmail.com>2015-01-11 13:41:29 +0100
commita9a2ffcf9a34bd25fe2e05bfdd4cde74725bb17d (patch)
tree69489cfef9060831b3c802bbb237f9d70f4a13ac /src/main/java/com/gitblit/utils/JGitUtils.java
parentde6f6625cb96775b640240ed2e57499743670fe2 (diff)
downloadgitblit-a9a2ffcf9a34bd25fe2e05bfdd4cde74725bb17d.tar.gz
gitblit-a9a2ffcf9a34bd25fe2e05bfdd4cde74725bb17d.zip
#230 - Improve empty folder navigation.
Empty folders are automatically skipped when browsing repository tree (similar to github "folder jumping" feature).
Diffstat (limited to 'src/main/java/com/gitblit/utils/JGitUtils.java')
-rw-r--r--src/main/java/com/gitblit/utils/JGitUtils.java502
1 files changed, 300 insertions, 202 deletions
diff --git a/src/main/java/com/gitblit/utils/JGitUtils.java b/src/main/java/com/gitblit/utils/JGitUtils.java
index 68c62ead..69084ca1 100644
--- a/src/main/java/com/gitblit/utils/JGitUtils.java
+++ b/src/main/java/com/gitblit/utils/JGitUtils.java
@@ -30,6 +30,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;
+import com.google.common.base.Strings;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.FetchCommand;
@@ -891,6 +892,63 @@ public class JGitUtils {
}
/**
+ * Returns the list of files in the specified folder at the specified
+ * commit. If the repository does not exist or is empty, an empty list is
+ * returned.
+ *
+ * This is modified version that implements path compression feature.
+ *
+ * @param repository
+ * @param path
+ * if unspecified, root folder is assumed.
+ * @param commit
+ * if null, HEAD is assumed.
+ * @return list of files in specified path
+ */
+ public static List<PathModel> getFilesInPath2(Repository repository, String path, RevCommit commit) {
+
+ List<PathModel> list = new ArrayList<PathModel>();
+ if (!hasCommits(repository)) {
+ return list;
+ }
+ if (commit == null) {
+ commit = getCommit(repository, null);
+ }
+ final TreeWalk tw = new TreeWalk(repository);
+ try {
+
+ tw.addTree(commit.getTree());
+ final boolean isPathEmpty = Strings.isNullOrEmpty(path);
+
+ if (!isPathEmpty) {
+ PathFilter f = PathFilter.create(path);
+ tw.setFilter(f);
+ }
+
+ tw.setRecursive(true);
+ List<String> paths = new ArrayList<>();
+
+ while (tw.next()) {
+ String child = isPathEmpty ? tw.getPathString()
+ : tw.getPathString().replaceFirst(String.format("%s/", path), "");
+ paths.add(child);
+ }
+
+ for(String p: PathUtils.compressPaths(paths)) {
+ String pathString = isPathEmpty ? p : String.format("%s/%s", path, p);
+ list.add(getPathModel(repository, pathString, path, commit));
+ }
+
+ } catch (IOException e) {
+ error(e, repository, "{0} failed to get files for commit {1}", commit.getName());
+ } finally {
+ tw.release();
+ }
+ Collections.sort(list);
+ return list;
+ }
+
+ /**
* Returns the list of files changed in a specified commit. If the
* repository does not exist or is empty, an empty list is returned.
*
@@ -1124,6 +1182,46 @@ public class JGitUtils {
}
/**
+ * Returns a path model by path string
+ *
+ * @param repo
+ * @param path
+ * @param filter
+ * @param commit
+ * @return a path model of the specified object
+ */
+ private static PathModel getPathModel(Repository repo, String path, String filter, RevCommit commit)
+ throws IOException {
+
+ long size = 0;
+ TreeWalk tw = TreeWalk.forPath(repo, path, commit.getTree());
+ String pathString = path;
+
+ if (!tw.isSubtree() && (tw.getFileMode(0) != FileMode.GITLINK)) {
+ size = tw.getObjectReader().getObjectSize(tw.getObjectId(0), Constants.OBJ_BLOB);
+ pathString = PathUtils.getLastPathComponent(pathString);
+
+ } else if (tw.isSubtree()) {
+
+ // do not display dirs that are behind in the path
+ if (!Strings.isNullOrEmpty(filter)) {
+ pathString = path.replaceFirst(filter + "/", "");
+ }
+
+ // remove the last slash from path in displayed link
+ if (pathString != null && pathString.charAt(pathString.length()-1) == '/') {
+ pathString = pathString.substring(0, pathString.length()-1);
+ }
+ }
+
+ return new PathModel(pathString, tw.getPathString(), size, tw.getFileMode(0).getBits(),
+ tw.getObjectId(0).getName(), commit.getName());
+
+
+ }
+
+
+ /**
* Returns a permissions representation of the mode bits.
*
* @param mode
@@ -2194,97 +2292,97 @@ public class JGitUtils {
}
return false;
}
-
- /**
- * Returns true if the commit identified by commitId is an ancestor or the
- * the commit identified by tipId.
- *
- * @param repository
- * @param commitId
- * @param tipId
- * @return true if there is the commit is an ancestor of the tip
- */
- public static boolean isMergedInto(Repository repository, String commitId, String tipId) {
- try {
- return isMergedInto(repository, repository.resolve(commitId), repository.resolve(tipId));
- } catch (Exception e) {
- LOGGER.error("Failed to determine isMergedInto", e);
- }
- return false;
- }
-
- /**
- * Returns true if the commit identified by commitId is an ancestor or the
- * the commit identified by tipId.
- *
- * @param repository
- * @param commitId
- * @param tipId
- * @return true if there is the commit is an ancestor of the tip
- */
- public static boolean isMergedInto(Repository repository, ObjectId commitId, ObjectId tipCommitId) {
- // traverse the revlog looking for a commit chain between the endpoints
- RevWalk rw = new RevWalk(repository);
- try {
- // must re-lookup RevCommits to workaround undocumented RevWalk bug
- RevCommit tip = rw.lookupCommit(tipCommitId);
- RevCommit commit = rw.lookupCommit(commitId);
- return rw.isMergedInto(commit, tip);
- } catch (Exception e) {
- LOGGER.error("Failed to determine isMergedInto", e);
- } finally {
- rw.dispose();
- }
- return false;
- }
-
- /**
- * Returns the merge base of two commits or null if there is no common
- * ancestry.
- *
- * @param repository
- * @param commitIdA
- * @param commitIdB
- * @return the commit id of the merge base or null if there is no common base
- */
- public static String getMergeBase(Repository repository, ObjectId commitIdA, ObjectId commitIdB) {
- RevWalk rw = new RevWalk(repository);
- try {
- RevCommit a = rw.lookupCommit(commitIdA);
- RevCommit b = rw.lookupCommit(commitIdB);
-
- rw.setRevFilter(RevFilter.MERGE_BASE);
- rw.markStart(a);
- rw.markStart(b);
- RevCommit mergeBase = rw.next();
- if (mergeBase == null) {
- return null;
- }
- return mergeBase.getName();
- } catch (Exception e) {
- LOGGER.error("Failed to determine merge base", e);
- } finally {
- rw.dispose();
- }
- return null;
- }
-
- public static enum MergeStatus {
- MISSING_INTEGRATION_BRANCH, MISSING_SRC_BRANCH, NOT_MERGEABLE, FAILED, ALREADY_MERGED, MERGEABLE, MERGED;
- }
-
- /**
- * Determines if we can cleanly merge one branch into another. Returns true
- * if we can merge without conflict, otherwise returns false.
- *
- * @param repository
- * @param src
- * @param toBranch
- * @return true if we can merge without conflict
- */
- public static MergeStatus canMerge(Repository repository, String src, String toBranch) {
- RevWalk revWalk = null;
- try {
+
+ /**
+ * Returns true if the commit identified by commitId is an ancestor or the
+ * the commit identified by tipId.
+ *
+ * @param repository
+ * @param commitId
+ * @param tipId
+ * @return true if there is the commit is an ancestor of the tip
+ */
+ public static boolean isMergedInto(Repository repository, String commitId, String tipId) {
+ try {
+ return isMergedInto(repository, repository.resolve(commitId), repository.resolve(tipId));
+ } catch (Exception e) {
+ LOGGER.error("Failed to determine isMergedInto", e);
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the commit identified by commitId is an ancestor or the
+ * the commit identified by tipId.
+ *
+ * @param repository
+ * @param commitId
+ * @param tipId
+ * @return true if there is the commit is an ancestor of the tip
+ */
+ public static boolean isMergedInto(Repository repository, ObjectId commitId, ObjectId tipCommitId) {
+ // traverse the revlog looking for a commit chain between the endpoints
+ RevWalk rw = new RevWalk(repository);
+ try {
+ // must re-lookup RevCommits to workaround undocumented RevWalk bug
+ RevCommit tip = rw.lookupCommit(tipCommitId);
+ RevCommit commit = rw.lookupCommit(commitId);
+ return rw.isMergedInto(commit, tip);
+ } catch (Exception e) {
+ LOGGER.error("Failed to determine isMergedInto", e);
+ } finally {
+ rw.dispose();
+ }
+ return false;
+ }
+
+ /**
+ * Returns the merge base of two commits or null if there is no common
+ * ancestry.
+ *
+ * @param repository
+ * @param commitIdA
+ * @param commitIdB
+ * @return the commit id of the merge base or null if there is no common base
+ */
+ public static String getMergeBase(Repository repository, ObjectId commitIdA, ObjectId commitIdB) {
+ RevWalk rw = new RevWalk(repository);
+ try {
+ RevCommit a = rw.lookupCommit(commitIdA);
+ RevCommit b = rw.lookupCommit(commitIdB);
+
+ rw.setRevFilter(RevFilter.MERGE_BASE);
+ rw.markStart(a);
+ rw.markStart(b);
+ RevCommit mergeBase = rw.next();
+ if (mergeBase == null) {
+ return null;
+ }
+ return mergeBase.getName();
+ } catch (Exception e) {
+ LOGGER.error("Failed to determine merge base", e);
+ } finally {
+ rw.dispose();
+ }
+ return null;
+ }
+
+ public static enum MergeStatus {
+ MISSING_INTEGRATION_BRANCH, MISSING_SRC_BRANCH, NOT_MERGEABLE, FAILED, ALREADY_MERGED, MERGEABLE, MERGED;
+ }
+
+ /**
+ * Determines if we can cleanly merge one branch into another. Returns true
+ * if we can merge without conflict, otherwise returns false.
+ *
+ * @param repository
+ * @param src
+ * @param toBranch
+ * @return true if we can merge without conflict
+ */
+ public static MergeStatus canMerge(Repository repository, String src, String toBranch) {
+ RevWalk revWalk = null;
+ try {
revWalk = new RevWalk(repository);
ObjectId branchId = repository.resolve(toBranch);
if (branchId == null) {
@@ -2294,122 +2392,122 @@ public class JGitUtils {
if (srcId == null) {
return MergeStatus.MISSING_SRC_BRANCH;
}
- RevCommit branchTip = revWalk.lookupCommit(branchId);
- RevCommit srcTip = revWalk.lookupCommit(srcId);
- if (revWalk.isMergedInto(srcTip, branchTip)) {
- // already merged
- return MergeStatus.ALREADY_MERGED;
- } else if (revWalk.isMergedInto(branchTip, srcTip)) {
- // fast-forward
- return MergeStatus.MERGEABLE;
- }
- RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
- boolean canMerge = merger.merge(branchTip, srcTip);
- if (canMerge) {
- return MergeStatus.MERGEABLE;
- }
+ RevCommit branchTip = revWalk.lookupCommit(branchId);
+ RevCommit srcTip = revWalk.lookupCommit(srcId);
+ if (revWalk.isMergedInto(srcTip, branchTip)) {
+ // already merged
+ return MergeStatus.ALREADY_MERGED;
+ } else if (revWalk.isMergedInto(branchTip, srcTip)) {
+ // fast-forward
+ return MergeStatus.MERGEABLE;
+ }
+ RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
+ boolean canMerge = merger.merge(branchTip, srcTip);
+ if (canMerge) {
+ return MergeStatus.MERGEABLE;
+ }
} catch (NullPointerException e) {
LOGGER.error("Failed to determine canMerge", e);
- } catch (IOException e) {
- LOGGER.error("Failed to determine canMerge", e);
+ } catch (IOException e) {
+ LOGGER.error("Failed to determine canMerge", e);
} finally {
- if (revWalk != null) {
+ if (revWalk != null) {
revWalk.release();
- }
- }
- return MergeStatus.NOT_MERGEABLE;
- }
-
-
- public static class MergeResult {
- public final MergeStatus status;
- public final String sha;
-
- MergeResult(MergeStatus status, String sha) {
- this.status = status;
- this.sha = sha;
- }
- }
-
- /**
- * Tries to merge a commit into a branch. If there are conflicts, the merge
- * will fail.
- *
- * @param repository
- * @param src
- * @param toBranch
- * @param committer
- * @param message
- * @return the merge result
- */
- public static MergeResult merge(Repository repository, String src, String toBranch,
- PersonIdent committer, String message) {
-
- if (!toBranch.startsWith(Constants.R_REFS)) {
- // branch ref doesn't start with ref, assume this is a branch head
- toBranch = Constants.R_HEADS + toBranch;
- }
-
- RevWalk revWalk = null;
- try {
- revWalk = new RevWalk(repository);
- RevCommit branchTip = revWalk.lookupCommit(repository.resolve(toBranch));
- RevCommit srcTip = revWalk.lookupCommit(repository.resolve(src));
- if (revWalk.isMergedInto(srcTip, branchTip)) {
- // already merged
- return new MergeResult(MergeStatus.ALREADY_MERGED, null);
- }
- RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
- boolean merged = merger.merge(branchTip, srcTip);
- if (merged) {
- // create a merge commit and a reference to track the merge commit
- ObjectId treeId = merger.getResultTreeId();
- ObjectInserter odi = repository.newObjectInserter();
- try {
- // Create a commit object
- CommitBuilder commitBuilder = new CommitBuilder();
- commitBuilder.setCommitter(committer);
- commitBuilder.setAuthor(committer);
- commitBuilder.setEncoding(Constants.CHARSET);
- if (StringUtils.isEmpty(message)) {
- message = MessageFormat.format("merge {0} into {1}", srcTip.getName(), branchTip.getName());
- }
- commitBuilder.setMessage(message);
- commitBuilder.setParentIds(branchTip.getId(), srcTip.getId());
- commitBuilder.setTreeId(treeId);
-
- // Insert the merge commit into the repository
- ObjectId mergeCommitId = odi.insert(commitBuilder);
- odi.flush();
-
- // set the merge ref to the merge commit
- RevCommit mergeCommit = revWalk.parseCommit(mergeCommitId);
- RefUpdate mergeRefUpdate = repository.updateRef(toBranch);
- mergeRefUpdate.setNewObjectId(mergeCommitId);
- mergeRefUpdate.setRefLogMessage("commit: " + mergeCommit.getShortMessage(), false);
- RefUpdate.Result rc = mergeRefUpdate.update();
- switch (rc) {
- case FAST_FORWARD:
- // successful, clean merge
+ }
+ }
+ return MergeStatus.NOT_MERGEABLE;
+ }
+
+
+ public static class MergeResult {
+ public final MergeStatus status;
+ public final String sha;
+
+ MergeResult(MergeStatus status, String sha) {
+ this.status = status;
+ this.sha = sha;
+ }
+ }
+
+ /**
+ * Tries to merge a commit into a branch. If there are conflicts, the merge
+ * will fail.
+ *
+ * @param repository
+ * @param src
+ * @param toBranch
+ * @param committer
+ * @param message
+ * @return the merge result
+ */
+ public static MergeResult merge(Repository repository, String src, String toBranch,
+ PersonIdent committer, String message) {
+
+ if (!toBranch.startsWith(Constants.R_REFS)) {
+ // branch ref doesn't start with ref, assume this is a branch head
+ toBranch = Constants.R_HEADS + toBranch;
+ }
+
+ RevWalk revWalk = null;
+ try {
+ revWalk = new RevWalk(repository);
+ RevCommit branchTip = revWalk.lookupCommit(repository.resolve(toBranch));
+ RevCommit srcTip = revWalk.lookupCommit(repository.resolve(src));
+ if (revWalk.isMergedInto(srcTip, branchTip)) {
+ // already merged
+ return new MergeResult(MergeStatus.ALREADY_MERGED, null);
+ }
+ RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
+ boolean merged = merger.merge(branchTip, srcTip);
+ if (merged) {
+ // create a merge commit and a reference to track the merge commit
+ ObjectId treeId = merger.getResultTreeId();
+ ObjectInserter odi = repository.newObjectInserter();
+ try {
+ // Create a commit object
+ CommitBuilder commitBuilder = new CommitBuilder();
+ commitBuilder.setCommitter(committer);
+ commitBuilder.setAuthor(committer);
+ commitBuilder.setEncoding(Constants.CHARSET);
+ if (StringUtils.isEmpty(message)) {
+ message = MessageFormat.format("merge {0} into {1}", srcTip.getName(), branchTip.getName());
+ }
+ commitBuilder.setMessage(message);
+ commitBuilder.setParentIds(branchTip.getId(), srcTip.getId());
+ commitBuilder.setTreeId(treeId);
+
+ // Insert the merge commit into the repository
+ ObjectId mergeCommitId = odi.insert(commitBuilder);
+ odi.flush();
+
+ // set the merge ref to the merge commit
+ RevCommit mergeCommit = revWalk.parseCommit(mergeCommitId);
+ RefUpdate mergeRefUpdate = repository.updateRef(toBranch);
+ mergeRefUpdate.setNewObjectId(mergeCommitId);
+ mergeRefUpdate.setRefLogMessage("commit: " + mergeCommit.getShortMessage(), false);
+ RefUpdate.Result rc = mergeRefUpdate.update();
+ switch (rc) {
+ case FAST_FORWARD:
+ // successful, clean merge
break;
- default:
- throw new GitBlitException(MessageFormat.format("Unexpected result \"{0}\" when merging commit {1} into {2} in {3}",
- rc.name(), srcTip.getName(), branchTip.getName(), repository.getDirectory()));
- }
-
- // return the merge commit id
- return new MergeResult(MergeStatus.MERGED, mergeCommitId.getName());
- } finally {
- odi.release();
- }
- }
- } catch (IOException e) {
- LOGGER.error("Failed to merge", e);
+ default:
+ throw new GitBlitException(MessageFormat.format("Unexpected result \"{0}\" when merging commit {1} into {2} in {3}",
+ rc.name(), srcTip.getName(), branchTip.getName(), repository.getDirectory()));
+ }
+
+ // return the merge commit id
+ return new MergeResult(MergeStatus.MERGED, mergeCommitId.getName());
+ } finally {
+ odi.release();
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.error("Failed to merge", e);
} finally {
- if (revWalk != null) {
+ if (revWalk != null) {
revWalk.release();
- }
- }
- return new MergeResult(MergeStatus.FAILED, null);
- }
+ }
+ }
+ return new MergeResult(MergeStatus.FAILED, null);
+ }
}