summaryrefslogtreecommitdiffstats
path: root/src/com/gitblit
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/gitblit')
-rw-r--r--src/com/gitblit/models/IssueModel.java310
-rw-r--r--src/com/gitblit/utils/IssueUtils.java455
-rw-r--r--src/com/gitblit/utils/JGitUtils.java33
-rw-r--r--src/com/gitblit/utils/JsonUtils.java34
4 files changed, 821 insertions, 11 deletions
diff --git a/src/com/gitblit/models/IssueModel.java b/src/com/gitblit/models/IssueModel.java
new file mode 100644
index 00000000..3c6d9a0b
--- /dev/null
+++ b/src/com/gitblit/models/IssueModel.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2012 gitblit.com.
+ *
+ * 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 com.gitblit.models;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.StringUtils;
+
+/**
+ * The Gitblit Issue model, its component classes, and enums.
+ *
+ * @author James Moger
+ *
+ */
+public class IssueModel implements Serializable, Comparable<IssueModel> {
+
+ private static final long serialVersionUID = 1L;;
+
+ public String id;
+
+ public Type type;
+
+ public Status status;
+
+ public Priority priority;
+
+ public Date created;
+
+ public String summary;
+
+ public String description;
+
+ public String reporter;
+
+ public String owner;
+
+ public String milestone;
+
+ public List<Change> changes;
+
+ public IssueModel() {
+ created = new Date();
+
+ type = Type.Defect;
+ status = Status.New;
+ priority = Priority.Medium;
+
+ changes = new ArrayList<Change>();
+ }
+
+ public String getStatus() {
+ String s = status.toString();
+ if (!StringUtils.isEmpty(owner))
+ s += " (" + owner + ")";
+ return s;
+ }
+
+ public List<String> getLabels() {
+ List<String> list = new ArrayList<String>();
+ String labels = null;
+ for (Change change : changes) {
+ if (change.hasFieldChanges()) {
+ FieldChange field = change.getField(Field.Labels);
+ if (field != null) {
+ labels = field.value.toString();
+ }
+ }
+ }
+ if (!StringUtils.isEmpty(labels)) {
+ list.addAll(StringUtils.getStringsFromValue(labels, " "));
+ }
+ return list;
+ }
+
+ public boolean hasLabel(String label) {
+ return getLabels().contains(label);
+ }
+
+ public Attachment getAttachment(String name) {
+ Attachment attachment = null;
+ for (Change change : changes) {
+ if (change.hasAttachments()) {
+ Attachment a = change.getAttachment(name);
+ if (a != null) {
+ attachment = a;
+ }
+ }
+ }
+ return attachment;
+ }
+
+ public void addChange(Change change) {
+ if (changes == null) {
+ changes = new ArrayList<Change>();
+ }
+ changes.add(change);
+ }
+
+ @Override
+ public String toString() {
+ return summary;
+ }
+
+ @Override
+ public int compareTo(IssueModel o) {
+ return o.created.compareTo(created);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof IssueModel)
+ return id.equals(((IssueModel) o).id);
+ return super.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ public static class Change implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ public Date created;
+
+ public String author;
+
+ public Comment comment;
+
+ public List<FieldChange> fieldChanges;
+
+ public List<Attachment> attachments;
+
+ public void comment(String text) {
+ comment = new Comment(text);
+ }
+
+ public boolean hasComment() {
+ return comment != null;
+ }
+
+ public boolean hasAttachments() {
+ return !ArrayUtils.isEmpty(attachments);
+ }
+
+ public boolean hasFieldChanges() {
+ return !ArrayUtils.isEmpty(fieldChanges);
+ }
+
+ public FieldChange getField(Field field) {
+ if (fieldChanges != null) {
+ for (FieldChange fieldChange : fieldChanges) {
+ if (fieldChange.field == field) {
+ return fieldChange;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void setField(Field field, Object value) {
+ FieldChange fieldChange = new FieldChange();
+ fieldChange.field = field;
+ fieldChange.value = value;
+ if (fieldChanges == null) {
+ fieldChanges = new ArrayList<FieldChange>();
+ }
+ fieldChanges.add(fieldChange);
+ }
+
+ public String getString(Field field) {
+ FieldChange fieldChange = getField(field);
+ if (fieldChange == null) {
+ return null;
+ }
+ return fieldChange.value.toString();
+ }
+
+ public void addAttachment(Attachment attachment) {
+ if (attachments == null) {
+ attachments = new ArrayList<Attachment>();
+ }
+ attachments.add(attachment);
+ }
+
+ public Attachment getAttachment(String name) {
+ for (Attachment attachment : attachments) {
+ if (attachment.name.equalsIgnoreCase(name)) {
+ return attachment;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return created.toString() + " by " + author;
+ }
+ }
+
+ public static class Comment implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ public String text;
+ public boolean deleted;
+
+ Comment(String text) {
+ this.text = text;
+ }
+
+ @Override
+ public String toString() {
+ return text;
+ }
+ }
+
+ public static class FieldChange implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ public Field field;
+
+ public Object value;
+
+ @Override
+ public String toString() {
+ return field + ": " + value;
+ }
+ }
+
+ public static class Attachment implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ public String name;
+ public long size;
+ public byte[] content;
+ public boolean deleted;
+
+ public Attachment(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ public static enum Field {
+ Summary, Description, Reporter, Owner, Type, Status, Priority, Milestone, Labels;
+ }
+
+ public static enum Type {
+ Defect, Enhancement, Task, Review, Other;
+ }
+
+ public static enum Priority {
+ Low, Medium, High, Critical;
+ }
+
+ public static enum Status {
+ New, Accepted, Started, Review, Queued, Testing, Done, Fixed, WontFix, Duplicate, Invalid;
+
+ public boolean atLeast(Status status) {
+ return ordinal() >= status.ordinal();
+ }
+
+ public boolean exceeds(Status status) {
+ return ordinal() > status.ordinal();
+ }
+
+ public Status next() {
+ switch (this) {
+ case New:
+ return Started;
+ case Accepted:
+ return Started;
+ case Started:
+ return Testing;
+ case Review:
+ return Testing;
+ case Queued:
+ return Testing;
+ case Testing:
+ return Done;
+ }
+ return Accepted;
+ }
+ }
+}
diff --git a/src/com/gitblit/utils/IssueUtils.java b/src/com/gitblit/utils/IssueUtils.java
new file mode 100644
index 00000000..82170703
--- /dev/null
+++ b/src/com/gitblit/utils/IssueUtils.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2012 gitblit.com.
+ *
+ * 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 com.gitblit.utils;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+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.PersonIdent;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.RefUpdate.Result;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
+import org.eclipse.jgit.treewalk.TreeWalk;
+
+import com.gitblit.models.IssueModel;
+import com.gitblit.models.IssueModel.Attachment;
+import com.gitblit.models.IssueModel.Change;
+import com.gitblit.models.IssueModel.Field;
+import com.gitblit.models.PathModel;
+import com.gitblit.models.RefModel;
+import com.gitblit.utils.JsonUtils.ExcludeField;
+import com.google.gson.Gson;
+
+/**
+ * Utility class for reading Gitblit issues.
+ *
+ * @author James Moger
+ *
+ */
+public class IssueUtils {
+
+ public static final String GB_ISSUES = "refs/heads/gb-issues";
+
+ /**
+ * Returns a RefModel for the gb-issues branch in the repository. If the
+ * branch can not be found, null is returned.
+ *
+ * @param repository
+ * @return a refmodel for the gb-issues branch or null
+ */
+ public static RefModel getIssuesBranch(Repository repository) {
+ return JGitUtils.getBranch(repository, "gb-issues");
+ }
+
+ /**
+ * Returns all the issues in the repository.
+ *
+ * @param repository
+ * @param filter
+ * optional issue filter to only return matching results
+ * @return a list of issues
+ */
+ public static List<IssueModel> getIssues(Repository repository, IssueFilter filter) {
+ List<IssueModel> list = new ArrayList<IssueModel>();
+ RefModel issuesBranch = getIssuesBranch(repository);
+ if (issuesBranch == null) {
+ return list;
+ }
+ List<PathModel> paths = JGitUtils
+ .getDocuments(repository, Arrays.asList("json"), GB_ISSUES);
+ RevTree tree = JGitUtils.getCommit(repository, GB_ISSUES).getTree();
+ for (PathModel path : paths) {
+ String json = JGitUtils.getStringContent(repository, tree, path.path);
+ IssueModel issue = JsonUtils.fromJsonString(json, IssueModel.class);
+ if (filter == null) {
+ list.add(issue);
+ } else {
+ if (filter.accept(issue)) {
+ list.add(issue);
+ }
+ }
+ }
+ Collections.sort(list);
+ return list;
+ }
+
+ /**
+ * Retrieves the specified issue from the repository with complete changes
+ * history.
+ *
+ * @param repository
+ * @param issueId
+ * @return an issue, if it exists, otherwise null
+ */
+ public static IssueModel getIssue(Repository repository, String issueId) {
+ RefModel issuesBranch = getIssuesBranch(repository);
+ if (issuesBranch == null) {
+ return null;
+ }
+
+ if (StringUtils.isEmpty(issueId)) {
+ return null;
+ }
+
+ // deserialize the issue model object
+ IssueModel issue = null;
+ String issuePath = getIssuePath(issueId);
+ RevTree tree = JGitUtils.getCommit(repository, GB_ISSUES).getTree();
+ String json = JGitUtils.getStringContent(repository, tree, issuePath + "/issue.json");
+ issue = JsonUtils.fromJsonString(json, IssueModel.class);
+ return issue;
+ }
+
+ /**
+ * Retrieves the specified attachment from an issue.
+ *
+ * @param repository
+ * @param issueId
+ * @param filename
+ * @return an attachment, if found, null otherwise
+ */
+ public static Attachment getIssueAttachment(Repository repository, String issueId,
+ String filename) {
+ RefModel issuesBranch = getIssuesBranch(repository);
+ if (issuesBranch == null) {
+ return null;
+ }
+
+ if (StringUtils.isEmpty(issueId)) {
+ return null;
+ }
+
+ // deserialize the issue model so that we have the attachment metadata
+ String issuePath = getIssuePath(issueId);
+ RevTree tree = JGitUtils.getCommit(repository, GB_ISSUES).getTree();
+ String json = JGitUtils.getStringContent(repository, tree, issuePath + "/issue.json");
+ IssueModel issue = JsonUtils.fromJsonString(json, IssueModel.class);
+ Attachment attachment = issue.getAttachment(filename);
+
+ // attachment not found
+ if (attachment == null) {
+ return null;
+ }
+
+ // retrieve the attachment content
+ byte[] content = JGitUtils.getByteContent(repository, tree, issuePath + "/" + filename);
+ attachment.content = content;
+ attachment.size = content.length;
+ return attachment;
+ }
+
+ /**
+ * Stores an issue in the gb-issues branch of the repository. The branch is
+ * automatically created if it does not already exist.
+ *
+ * @param repository
+ * @param change
+ * @return true if successful
+ */
+ public static IssueModel createIssue(Repository repository, Change change) {
+ RefModel issuesBranch = getIssuesBranch(repository);
+ if (issuesBranch == null) {
+ JGitUtils.createOrphanBranch(repository, "gb-issues", null);
+ }
+ change.created = new Date();
+
+ IssueModel issue = new IssueModel();
+ issue.created = change.created;
+ issue.summary = change.getString(Field.Summary);
+ issue.description = change.getString(Field.Description);
+ issue.reporter = change.getString(Field.Reporter);
+
+ if (StringUtils.isEmpty(issue.summary)) {
+ throw new RuntimeException("Must specify an issue summary!");
+ }
+ if (StringUtils.isEmpty(change.getString(Field.Description))) {
+ throw new RuntimeException("Must specify an issue description!");
+ }
+ if (StringUtils.isEmpty(change.getString(Field.Reporter))) {
+ throw new RuntimeException("Must specify an issue reporter!");
+ }
+
+ issue.id = StringUtils.getSHA1(issue.created.toString() + issue.reporter + issue.summary
+ + issue.description);
+
+ String message = createChangelog('+', issue.id, change);
+ boolean success = commit(repository, issue, change, message);
+ if (success) {
+ return issue;
+ }
+ return null;
+ }
+
+ /**
+ * Updates an issue in the gb-issues branch of the repository.
+ *
+ * @param repository
+ * @param issue
+ * @param change
+ * @return true if successful
+ */
+ public static boolean updateIssue(Repository repository, String issueId, Change change) {
+ boolean success = false;
+ RefModel issuesBranch = getIssuesBranch(repository);
+
+ if (issuesBranch == null) {
+ throw new RuntimeException("gb-issues branch does not exist!");
+ }
+
+ if (change == null) {
+ throw new RuntimeException("change can not be null!");
+ }
+
+ if (StringUtils.isEmpty(change.author)) {
+ throw new RuntimeException("must specify change.author!");
+ }
+
+ IssueModel issue = getIssue(repository, issueId);
+ change.created = new Date();
+
+ String message = createChangelog('=', issueId, change);
+ success = commit(repository, issue, change, message);
+ return success;
+ }
+
+ private static String createChangelog(char type, String issueId, Change change) {
+ return type + " " + issueId + "\n\n" + toJson(change);
+ }
+
+ /**
+ *
+ * @param repository
+ * @param issue
+ * @param change
+ * @param changelog
+ * @return
+ */
+ private static boolean commit(Repository repository, IssueModel issue, Change change,
+ String changelog) {
+ boolean success = false;
+ String issuePath = getIssuePath(issue.id);
+ try {
+ issue.addChange(change);
+
+ // serialize the issue as json
+ String json = toJson(issue);
+
+ // cache the issue "files" in a map
+ Map<String, CommitFile> files = new HashMap<String, CommitFile>();
+ CommitFile issueFile = new CommitFile(issuePath + "/issue.json", change.created);
+ issueFile.content = json.getBytes(Constants.CHARACTER_ENCODING);
+ files.put(issueFile.path, issueFile);
+
+ if (change.hasAttachments()) {
+ for (Attachment attachment : change.attachments) {
+ if (!ArrayUtils.isEmpty(attachment.content)) {
+ CommitFile file = new CommitFile(issuePath + "/" + attachment.name,
+ change.created);
+ file.content = attachment.content;
+ files.put(file.path, file);
+ }
+ }
+ }
+
+ ObjectId headId = repository.resolve(GB_ISSUES + "^{commit}");
+
+ ObjectInserter odi = repository.newObjectInserter();
+ try {
+ // Create the in-memory index of the new/updated issue.
+ DirCache index = createIndex(repository, headId, files);
+ ObjectId indexTreeId = index.writeTree(odi);
+
+ // Create a commit object
+ PersonIdent author = new PersonIdent(issue.reporter, issue.reporter + "@gitblit");
+ CommitBuilder commit = new CommitBuilder();
+ commit.setAuthor(author);
+ commit.setCommitter(author);
+ commit.setEncoding(Constants.CHARACTER_ENCODING);
+ commit.setMessage(changelog);
+ commit.setParentId(headId);
+ commit.setTreeId(indexTreeId);
+
+ // Insert the commit into the repository
+ ObjectId commitId = odi.insert(commit);
+ odi.flush();
+
+ RevWalk revWalk = new RevWalk(repository);
+ try {
+ RevCommit revCommit = revWalk.parseCommit(commitId);
+ RefUpdate ru = repository.updateRef(GB_ISSUES);
+ ru.setNewObjectId(commitId);
+ ru.setExpectedOldObjectId(headId);
+ ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
+ Result rc = ru.forceUpdate();
+ switch (rc) {
+ case NEW:
+ case FORCED:
+ case FAST_FORWARD:
+ success = true;
+ break;
+ case REJECTED:
+ case LOCK_FAILURE:
+ throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD,
+ ru.getRef(), rc);
+ default:
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().updatingRefFailed, GB_ISSUES, commitId.toString(),
+ rc));
+ }
+ } finally {
+ revWalk.release();
+ }
+ } finally {
+ odi.release();
+ }
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ return success;
+ }
+
+ private static String toJson(Object o) {
+ try {
+ // exclude the attachment content field from json serialization
+ Gson gson = JsonUtils.gson(new ExcludeField(
+ "com.gitblit.models.IssueModel$Attachment.content"));
+ String json = gson.toJson(o);
+ return json;
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+ /**
+ * Returns the issue path. This follows the same scheme as Git's object
+ * store path where the first two characters of the hash id are the root
+ * folder with the remaining characters as a subfolder within that folder.
+ *
+ * @param issueId
+ * @return the root path of the issue content on the gb-issues branch
+ */
+ private static String getIssuePath(String issueId) {
+ return issueId.substring(0, 2) + "/" + issueId.substring(2);
+ }
+
+ /**
+ * Creates an in-memory index of the issue change.
+ *
+ * @param repo
+ * @param headId
+ * @param files
+ * @param time
+ * @return an in-memory index
+ * @throws IOException
+ */
+ private static DirCache createIndex(Repository repo, ObjectId headId,
+ Map<String, CommitFile> files) throws IOException {
+
+ DirCache inCoreIndex = DirCache.newInCore();
+ DirCacheBuilder dcBuilder = inCoreIndex.builder();
+ ObjectInserter inserter = repo.newObjectInserter();
+
+ try {
+ // Add the issue files to the temporary index
+ for (CommitFile file : files.values()) {
+ // create an index entry for the file
+ final DirCacheEntry dcEntry = new DirCacheEntry(file.path);
+ dcEntry.setLength(file.content.length);
+ dcEntry.setLastModified(file.time);
+ dcEntry.setFileMode(FileMode.REGULAR_FILE);
+
+ // insert object
+ dcEntry.setObjectId(inserter.insert(Constants.OBJ_BLOB, file.content));
+
+ // add to temporary in-core index
+ dcBuilder.add(dcEntry);
+ }
+
+ // Traverse HEAD to add all other paths
+ TreeWalk treeWalk = new TreeWalk(repo);
+ int hIdx = -1;
+ if (headId != null)
+ hIdx = treeWalk.addTree(new RevWalk(repo).parseTree(headId));
+ treeWalk.setRecursive(true);
+
+ while (treeWalk.next()) {
+ String path = treeWalk.getPathString();
+ CanonicalTreeParser hTree = null;
+ if (hIdx != -1)
+ hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class);
+ if (!files.containsKey(path)) {
+ // add entries from HEAD for all other paths
+ if (hTree != null) {
+ // create a new DirCacheEntry with data retrieved from
+ // HEAD
+ final DirCacheEntry dcEntry = new DirCacheEntry(path);
+ dcEntry.setObjectId(hTree.getEntryObjectId());
+ dcEntry.setFileMode(hTree.getEntryFileMode());
+
+ // add to temporary in-core index
+ dcBuilder.add(dcEntry);
+ }
+ }
+ }
+
+ // release the treewalk
+ treeWalk.release();
+
+ // finish temporary in-core index used for this commit
+ dcBuilder.finish();
+ } finally {
+ inserter.release();
+ }
+ return inCoreIndex;
+ }
+
+ private static class CommitFile {
+ String path;
+ long time;
+ byte[] content;
+
+ CommitFile(String path, Date date) {
+ this.path = path;
+ this.time = date.getTime();
+ }
+ }
+
+ public static interface IssueFilter {
+ public abstract boolean accept(IssueModel issue);
+ }
+}
diff --git a/src/com/gitblit/utils/JGitUtils.java b/src/com/gitblit/utils/JGitUtils.java
index a540c2aa..5d6011a2 100644
--- a/src/com/gitblit/utils/JGitUtils.java
+++ b/src/com/gitblit/utils/JGitUtils.java
@@ -24,7 +24,6 @@ import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -748,25 +747,40 @@ public class JGitUtils {
}
/**
- * Returns the list of files in the repository that match one of the
- * specified extensions. This is a CASE-SENSITIVE search. If the repository
- * does not exist or is empty, an empty list is returned.
+ * Returns the list of files in the repository on the default branch that
+ * match one of the specified extensions. This is a CASE-SENSITIVE search.
+ * If the repository does not exist or is empty, an empty list is returned.
*
* @param repository
* @param extensions
* @return list of files in repository with a matching extension
*/
public static List<PathModel> getDocuments(Repository repository, List<String> extensions) {
+ return getDocuments(repository, extensions, null);
+ }
+
+ /**
+ * Returns the list of files in the repository in the specified commit that
+ * match one of the specified extensions. This is a CASE-SENSITIVE search.
+ * If the repository does not exist or is empty, an empty list is returned.
+ *
+ * @param repository
+ * @param extensions
+ * @param objectId
+ * @return list of files in repository with a matching extension
+ */
+ public static List<PathModel> getDocuments(Repository repository, List<String> extensions,
+ String objectId) {
List<PathModel> list = new ArrayList<PathModel>();
if (!hasCommits(repository)) {
return list;
}
- RevCommit commit = getCommit(repository, null);
+ RevCommit commit = getCommit(repository, objectId);
final TreeWalk tw = new TreeWalk(repository);
try {
tw.addTree(commit.getTree());
if (extensions != null && extensions.size() > 0) {
- Collection<TreeFilter> suffixFilters = new ArrayList<TreeFilter>();
+ List<TreeFilter> suffixFilters = new ArrayList<TreeFilter>();
for (String extension : extensions) {
if (extension.charAt(0) == '.') {
suffixFilters.add(PathSuffixFilter.create("\\" + extension));
@@ -775,7 +789,12 @@ public class JGitUtils {
suffixFilters.add(PathSuffixFilter.create("\\." + extension));
}
}
- TreeFilter filter = OrTreeFilter.create(suffixFilters);
+ TreeFilter filter;
+ if (suffixFilters.size() == 1) {
+ filter = suffixFilters.get(0);
+ } else {
+ filter = OrTreeFilter.create(suffixFilters);
+ }
tw.setFilter(filter);
tw.setRecursive(true);
}
diff --git a/src/com/gitblit/utils/JsonUtils.java b/src/com/gitblit/utils/JsonUtils.java
index da9c99d2..aea46bbb 100644
--- a/src/com/gitblit/utils/JsonUtils.java
+++ b/src/com/gitblit/utils/JsonUtils.java
@@ -38,6 +38,8 @@ import com.gitblit.GitBlitException.UnauthorizedException;
import com.gitblit.GitBlitException.UnknownRequestException;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
+import com.google.gson.ExclusionStrategy;
+import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
@@ -108,7 +110,7 @@ public class JsonUtils {
UnauthorizedException {
return retrieveJson(url, type, null, null);
}
-
+
/**
* Reads a gson object from the specified url.
*
@@ -169,10 +171,11 @@ public class JsonUtils {
*/
public static String retrieveJsonString(String url, String username, char[] password)
throws IOException {
- try {
+ try {
URLConnection conn = ConnectionUtils.openReadConnection(url, username, password);
InputStream is = conn.getInputStream();
- BufferedReader reader = new BufferedReader(new InputStreamReader(is, ConnectionUtils.CHARSET));
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is,
+ ConnectionUtils.CHARSET));
StringBuilder json = new StringBuilder();
char[] buffer = new char[4096];
int len = 0;
@@ -260,10 +263,13 @@ public class JsonUtils {
// build custom gson instance with GMT date serializer/deserializer
// http://code.google.com/p/google-gson/issues/detail?id=281
- private static Gson gson() {
+ public static Gson gson(ExclusionStrategy... strategies) {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Date.class, new GmtDateTypeAdapter());
builder.setPrettyPrinting();
+ if (!ArrayUtils.isEmpty(strategies)) {
+ builder.setExclusionStrategies(strategies);
+ }
return builder.create();
}
@@ -296,4 +302,24 @@ public class JsonUtils {
}
}
}
+
+ public static class ExcludeField implements ExclusionStrategy {
+
+ private Class<?> c;
+ private String fieldName;
+
+ public ExcludeField(String fqfn) throws SecurityException, NoSuchFieldException,
+ ClassNotFoundException {
+ this.c = Class.forName(fqfn.substring(0, fqfn.lastIndexOf(".")));
+ this.fieldName = fqfn.substring(fqfn.lastIndexOf(".") + 1);
+ }
+
+ public boolean shouldSkipClass(Class<?> arg0) {
+ return false;
+ }
+
+ public boolean shouldSkipField(FieldAttributes f) {
+ return (f.getDeclaringClass() == c && f.getName().equals(fieldName));
+ }
+ }
}