]> source.dussan.org Git - gitblit.git/commitdiff
Removed unused Issues classes
authorJames Moger <james.moger@gitblit.com>
Tue, 17 Sep 2013 21:49:20 +0000 (17:49 -0400)
committerJames Moger <james.moger@gitblit.com>
Tue, 17 Sep 2013 21:49:20 +0000 (17:49 -0400)
src/main/java/com/gitblit/LuceneExecutor.java
src/main/java/com/gitblit/models/IssueModel.java [deleted file]
src/main/java/com/gitblit/utils/IssueUtils.java [deleted file]
src/test/java/com/gitblit/tests/GitBlitSuite.java
src/test/java/com/gitblit/tests/IssuesTest.java [deleted file]

index 0e4baae97250e45e49595ac357d430b8d2433353..3446289a357b857bb4b9787ca9e4cd0b0cb43462 100644 (file)
@@ -56,7 +56,6 @@ import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.IndexSearcher;\r
 import org.apache.lucene.search.Query;\r
 import org.apache.lucene.search.ScoreDoc;\r
-import org.apache.lucene.search.TermQuery;\r
 import org.apache.lucene.search.TopScoreDocCollector;\r
 import org.apache.lucene.search.highlight.Fragmenter;\r
 import org.apache.lucene.search.highlight.Highlighter;\r
@@ -86,14 +85,11 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;\r
 \r
 import com.gitblit.Constants.SearchObjectType;\r
-import com.gitblit.models.IssueModel;\r
-import com.gitblit.models.IssueModel.Attachment;\r
 import com.gitblit.models.PathModel.PathChangeModel;\r
 import com.gitblit.models.RefModel;\r
 import com.gitblit.models.RepositoryModel;\r
 import com.gitblit.models.SearchResult;\r
 import com.gitblit.utils.ArrayUtils;\r
-import com.gitblit.utils.IssueUtils;\r
 import com.gitblit.utils.JGitUtils;\r
 import com.gitblit.utils.StringUtils;\r
 \r
@@ -109,7 +105,6 @@ public class LuceneExecutor implements Runnable {
        private static final int INDEX_VERSION = 5;\r
 \r
        private static final String FIELD_OBJECT_TYPE = "type";\r
-       private static final String FIELD_ISSUE = "issue";\r
        private static final String FIELD_PATH = "path";\r
        private static final String FIELD_COMMIT = "commit";\r
        private static final String FIELD_BRANCH = "branch";\r
@@ -120,7 +115,6 @@ public class LuceneExecutor implements Runnable {
        private static final String FIELD_DATE = "date";\r
        private static final String FIELD_TAG = "tag";\r
        private static final String FIELD_LABEL = "label";\r
-       private static final String FIELD_ATTACHMENT = "attachment";\r
 \r
        private static final String CONF_FILE = "lucene.conf";\r
        private static final String LUCENE_DIR = "lucene";\r
@@ -475,9 +469,8 @@ public class LuceneExecutor implements Runnable {
                                                && branch.equals(defaultBranch)) {\r
                                        // indexing "default" branch\r
                                        indexBranch = true;\r
-                               } else if (IssueUtils.GB_ISSUES.equals(branch)) {\r
-                                       // skip the GB_ISSUES branch because it is indexed later\r
-                                       // note: this is different than updateIndex\r
+                               } else if (branch.getName().startsWith(com.gitblit.Constants.R_GITBLIT)) {\r
+                                       // skip Gitblit internal branches\r
                                        indexBranch = false;\r
                                } else {\r
                                        // normal explicit branch check\r
@@ -617,19 +610,6 @@ public class LuceneExecutor implements Runnable {
                        // finished\r
                        reader.release();\r
                        \r
-                       // this repository has a gb-issues branch, index all issues\r
-                       if (IssueUtils.getIssuesBranch(repository) != null) {\r
-                               List<IssueModel> issues = IssueUtils.getIssues(repository, null);\r
-                               if (issues.size() > 0) {\r
-                                       result.branchCount += 1;\r
-                               }\r
-                               for (IssueModel issue : issues) {\r
-                                       result.issueCount++;\r
-                                       Document doc = createDocument(issue);\r
-                                       writer.addDocument(doc);\r
-                               }\r
-                       }\r
-\r
                        // commit all changes and reset the searcher\r
                        config.setInt(CONF_INDEX, null, CONF_VERSION, INDEX_VERSION);\r
                        config.save();\r
@@ -722,55 +702,6 @@ public class LuceneExecutor implements Runnable {
                return result;\r
        }\r
 \r
-       /**\r
-        * Incrementally update the index with the specified issue for the\r
-        * repository.\r
-        * \r
-        * @param repositoryName\r
-        * @param issue\r
-        * @return true, if successful\r
-        */\r
-       public boolean index(String repositoryName, IssueModel issue) {\r
-               try {\r
-                       // delete the old issue from the index, if exists\r
-                       deleteIssue(repositoryName, issue.id);\r
-                       Document doc = createDocument(issue);\r
-                       return index(repositoryName, doc);\r
-               } catch (Exception e) {\r
-                       logger.error(MessageFormat.format("Error while indexing issue {0} in {1}", issue.id, repositoryName), e);\r
-               }\r
-               return false;\r
-       }\r
-       \r
-       /**\r
-        * Delete an issue from the repository index.\r
-        * \r
-        * @param repositoryName\r
-        * @param issueId\r
-        * @throws Exception\r
-        * @return true, if deleted, false if no record was deleted\r
-        */\r
-       private boolean deleteIssue(String repositoryName, String issueId) throws Exception {\r
-               BooleanQuery query = new BooleanQuery();\r
-               Term objectTerm = new Term(FIELD_OBJECT_TYPE, SearchObjectType.issue.name());\r
-               query.add(new TermQuery(objectTerm), Occur.MUST);\r
-               Term issueidTerm = new Term(FIELD_ISSUE, issueId);\r
-               query.add(new TermQuery(issueidTerm), Occur.MUST);\r
-               \r
-               IndexWriter writer = getIndexWriter(repositoryName);\r
-               int numDocsBefore = writer.numDocs();\r
-               writer.deleteDocuments(query);\r
-               writer.commit();\r
-               int numDocsAfter = writer.numDocs();\r
-               if (numDocsBefore == numDocsAfter) {\r
-                       logger.debug(MessageFormat.format("no records found to delete {0}", query.toString()));\r
-                       return false;\r
-               } else {\r
-                       logger.debug(MessageFormat.format("deleted {0} records with {1}", numDocsBefore - numDocsAfter, query.toString()));\r
-                       return true;\r
-               }\r
-       }\r
-       \r
        /**\r
         * Delete a blob from the specified branch of the repository index.\r
         * \r
@@ -870,10 +801,9 @@ public class LuceneExecutor implements Runnable {
                                                && branch.equals(defaultBranch)) {\r
                                        // indexing "default" branch\r
                                        indexBranch = true;\r
-                               } else if (IssueUtils.GB_ISSUES.equals(branch)) {\r
-                                       // update issues modified on the GB_ISSUES branch\r
-                                       // note: this is different than reindex\r
-                                       indexBranch = true;\r
+                               } else if (branch.getName().startsWith(com.gitblit.Constants.R_GITBLIT)) {\r
+                                       // ignore internal Gitblit branches\r
+                                       indexBranch = false;\r
                                } else {\r
                                        // normal explicit branch check\r
                                        indexBranch = model.indexedBranches.contains(branch.getName());\r
@@ -904,35 +834,11 @@ public class LuceneExecutor implements Runnable {
                                        result.branchCount += 1;\r
                                }\r
                                \r
-                               // track the issue ids that we have already indexed\r
-                               Set<String> indexedIssues = new TreeSet<String>();\r
-                               \r
                                // reverse the list of commits so we start with the first commit                                \r
                                Collections.reverse(revs);\r
                                for (RevCommit commit : revs) {                                 \r
-                                       if (IssueUtils.GB_ISSUES.equals(branch)) {\r
-                                               // only index an issue once during updateIndex\r
-                                               String issueId = commit.getShortMessage().substring(2).trim();\r
-                                               if (indexedIssues.contains(issueId)) {\r
-                                                       continue;\r
-                                               }\r
-                                               indexedIssues.add(issueId);\r
-                                               \r
-                                               IssueModel issue = IssueUtils.getIssue(repository, issueId);\r
-                                               if (issue == null) {\r
-                                                       // issue was deleted, remove from index\r
-                                                       if (!deleteIssue(model.name, issueId)) {\r
-                                                               logger.error(MessageFormat.format("Failed to delete issue {0} from Lucene index!", issueId));\r
-                                                       }\r
-                                               } else {\r
-                                                       // issue was updated\r
-                                                       index(model.name, issue);\r
-                                                       result.issueCount++;\r
-                                               }\r
-                                       } else {\r
-                                               // index a commit\r
-                                               result.add(index(model.name, repository, branchName, commit));\r
-                                       }\r
+                                       // index a commit\r
+                                       result.add(index(model.name, repository, branchName, commit));\r
                                }\r
 \r
                                // update the config\r
@@ -958,34 +864,6 @@ public class LuceneExecutor implements Runnable {
                return result;\r
        }\r
        \r
-       /**\r
-        * Creates a Lucene document from an issue.\r
-        * \r
-        * @param issue\r
-        * @return a Lucene document\r
-        */\r
-       private Document createDocument(IssueModel issue) {\r
-               Document doc = new Document();\r
-               doc.add(new Field(FIELD_OBJECT_TYPE, SearchObjectType.issue.name(), Store.YES,\r
-                               Field.Index.NOT_ANALYZED));\r
-               doc.add(new Field(FIELD_ISSUE, issue.id, Store.YES, Index.ANALYZED));\r
-               doc.add(new Field(FIELD_BRANCH, IssueUtils.GB_ISSUES, Store.YES, Index.ANALYZED));\r
-               doc.add(new Field(FIELD_DATE, DateTools.dateToString(issue.created, Resolution.MINUTE),\r
-                               Store.YES, Field.Index.NO));\r
-               doc.add(new Field(FIELD_AUTHOR, issue.reporter, Store.YES, Index.ANALYZED));\r
-               List<String> attachments = new ArrayList<String>();\r
-               for (Attachment attachment : issue.getAttachments()) {\r
-                       attachments.add(attachment.name.toLowerCase());\r
-               }\r
-               doc.add(new Field(FIELD_ATTACHMENT, StringUtils.flattenStrings(attachments), Store.YES,\r
-                               Index.ANALYZED));\r
-               doc.add(new Field(FIELD_SUMMARY, issue.summary, Store.YES, Index.ANALYZED));\r
-               doc.add(new Field(FIELD_CONTENT, issue.toString(), Store.YES, Index.ANALYZED));\r
-               doc.add(new Field(FIELD_LABEL, StringUtils.flattenStrings(issue.getLabels()), Store.YES,\r
-                               Index.ANALYZED));\r
-               return doc;\r
-       }\r
-\r
        /**\r
         * Creates a Lucene document for a commit\r
         * \r
@@ -1042,7 +920,6 @@ public class LuceneExecutor implements Runnable {
                result.type = SearchObjectType.fromName(doc.get(FIELD_OBJECT_TYPE));\r
                result.branch = doc.get(FIELD_BRANCH);\r
                result.commitId = doc.get(FIELD_COMMIT);\r
-               result.issueId = doc.get(FIELD_ISSUE);\r
                result.path = doc.get(FIELD_PATH);\r
                if (doc.get(FIELD_TAG) != null) {\r
                        result.tags = StringUtils.getStringsFromValue(doc.get(FIELD_TAG));\r
diff --git a/src/main/java/com/gitblit/models/IssueModel.java b/src/main/java/com/gitblit/models/IssueModel.java
deleted file mode 100644 (file)
index c903891..0000000
+++ /dev/null
@@ -1,532 +0,0 @@
-/*\r
- * Copyright 2012 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit.models;\r
-\r
-import java.io.Serializable;\r
-import java.util.ArrayList;\r
-import java.util.Date;\r
-import java.util.LinkedHashSet;\r
-import java.util.List;\r
-import java.util.Set;\r
-\r
-import com.gitblit.utils.ArrayUtils;\r
-import com.gitblit.utils.StringUtils;\r
-import com.gitblit.utils.TimeUtils;\r
-\r
-/**\r
- * The Gitblit Issue model, its component classes, and enums.\r
- * \r
- * @author James Moger\r
- * \r
- */\r
-public class IssueModel implements Serializable, Comparable<IssueModel> {\r
-\r
-       private static final long serialVersionUID = 1L;\r
-\r
-       public String id;\r
-\r
-       public Type type;\r
-\r
-       public Status status;\r
-\r
-       public Priority priority;\r
-\r
-       public Date created;\r
-\r
-       public String summary;\r
-\r
-       public String description;\r
-\r
-       public String reporter;\r
-\r
-       public String owner;\r
-\r
-       public String milestone;\r
-\r
-       public List<Change> changes;\r
-\r
-       public IssueModel() {\r
-               // the first applied change set the date appropriately\r
-               created = new Date(0);\r
-\r
-               type = Type.Defect;\r
-               status = Status.New;\r
-               priority = Priority.Medium;\r
-\r
-               changes = new ArrayList<Change>();\r
-       }\r
-\r
-       public String getStatus() {\r
-               String s = status.toString();\r
-               if (!StringUtils.isEmpty(owner))\r
-                       s += " (" + owner + ")";\r
-               return s;\r
-       }\r
-\r
-       public boolean hasLabel(String label) {\r
-               return getLabels().contains(label);\r
-       }\r
-\r
-       public List<String> getLabels() {\r
-               List<String> list = new ArrayList<String>();\r
-               String labels = null;\r
-               for (Change change : changes) {\r
-                       if (change.hasField(Field.Labels)) {\r
-                               labels = change.getString(Field.Labels);\r
-                       }\r
-               }\r
-               if (!StringUtils.isEmpty(labels)) {\r
-                       list.addAll(StringUtils.getStringsFromValue(labels, " "));\r
-               }\r
-               return list;\r
-       }\r
-\r
-       public Attachment getAttachment(String name) {\r
-               Attachment attachment = null;\r
-               for (Change change : changes) {\r
-                       if (change.hasAttachments()) {\r
-                               Attachment a = change.getAttachment(name);\r
-                               if (a != null) {\r
-                                       attachment = a;\r
-                               }\r
-                       }\r
-               }\r
-               return attachment;\r
-       }\r
-\r
-       public List<Attachment> getAttachments() {\r
-               List<Attachment> list = new ArrayList<Attachment>();\r
-               for (Change change : changes) {\r
-                       if (change.hasAttachments()) {\r
-                               list.addAll(change.attachments);\r
-                       }\r
-               }\r
-               return list;\r
-       }\r
-\r
-       public void applyChange(Change change) {\r
-               if (changes.size() == 0) {\r
-                       // first change created the issue\r
-                       created = change.created;\r
-               }\r
-               changes.add(change);\r
-\r
-               if (change.hasFieldChanges()) {\r
-                       for (FieldChange fieldChange : change.fieldChanges) {\r
-                               switch (fieldChange.field) {\r
-                               case Id:\r
-                                       id = fieldChange.value.toString();\r
-                                       break;\r
-                               case Type:\r
-                                       type = IssueModel.Type.fromObject(fieldChange.value);\r
-                                       break;\r
-                               case Status:\r
-                                       status = IssueModel.Status.fromObject(fieldChange.value);\r
-                                       break;\r
-                               case Priority:\r
-                                       priority = IssueModel.Priority.fromObject(fieldChange.value);\r
-                                       break;\r
-                               case Summary:\r
-                                       summary = fieldChange.value.toString();\r
-                                       break;\r
-                               case Description:\r
-                                       description = fieldChange.value.toString();\r
-                                       break;\r
-                               case Reporter:\r
-                                       reporter = fieldChange.value.toString();\r
-                                       break;\r
-                               case Owner:\r
-                                       owner = fieldChange.value.toString();\r
-                                       break;\r
-                               case Milestone:\r
-                                       milestone = fieldChange.value.toString();\r
-                                       break;\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public String toString() {\r
-               StringBuilder sb = new StringBuilder();\r
-               sb.append("issue ");\r
-               sb.append(id.substring(0, 8));\r
-               sb.append(" (" + summary + ")\n");\r
-               for (Change change : changes) {\r
-                       sb.append(change);\r
-                       sb.append('\n');\r
-               }\r
-               return sb.toString();\r
-       }\r
-\r
-       @Override\r
-       public int compareTo(IssueModel o) {\r
-               return o.created.compareTo(created);\r
-       }\r
-\r
-       @Override\r
-       public boolean equals(Object o) {\r
-               if (o instanceof IssueModel)\r
-                       return id.equals(((IssueModel) o).id);\r
-               return super.equals(o);\r
-       }\r
-\r
-       @Override\r
-       public int hashCode() {\r
-               return id.hashCode();\r
-       }\r
-\r
-       public static class Change implements Serializable, Comparable<Change> {\r
-\r
-               private static final long serialVersionUID = 1L;\r
-\r
-               public final Date created;\r
-\r
-               public final String author;\r
-\r
-               public String id;\r
-\r
-               public char code;\r
-\r
-               public Comment comment;\r
-\r
-               public Set<FieldChange> fieldChanges;\r
-\r
-               public Set<Attachment> attachments;\r
-\r
-               public Change(String author) {\r
-                       this.created = new Date((System.currentTimeMillis() / 1000) * 1000);\r
-                       this.author = author;\r
-                       this.id = StringUtils.getSHA1(created.toString() + author);\r
-               }\r
-\r
-               public boolean hasComment() {\r
-                       return comment != null && !comment.deleted;\r
-               }\r
-\r
-               public void comment(String text) {\r
-                       comment = new Comment(text);\r
-                       comment.id = StringUtils.getSHA1(created.toString() + author + text);\r
-               }\r
-\r
-               public boolean hasAttachments() {\r
-                       return !ArrayUtils.isEmpty(attachments);\r
-               }\r
-\r
-               public void addAttachment(Attachment attachment) {\r
-                       if (attachments == null) {\r
-                               attachments = new LinkedHashSet<Attachment>();\r
-                       }\r
-                       attachments.add(attachment);\r
-               }\r
-\r
-               public Attachment getAttachment(String name) {\r
-                       for (Attachment attachment : attachments) {\r
-                               if (attachment.name.equalsIgnoreCase(name)) {\r
-                                       return attachment;\r
-                               }\r
-                       }\r
-                       return null;\r
-               }\r
-\r
-               public boolean hasField(Field field) {\r
-                       return !StringUtils.isEmpty(getString(field));\r
-               }\r
-\r
-               public boolean hasFieldChanges() {\r
-                       return !ArrayUtils.isEmpty(fieldChanges);\r
-               }\r
-\r
-               public Object getField(Field field) {\r
-                       if (fieldChanges != null) {\r
-                               for (FieldChange fieldChange : fieldChanges) {\r
-                                       if (fieldChange.field == field) {\r
-                                               return fieldChange.value;\r
-                                       }\r
-                               }\r
-                       }\r
-                       return null;\r
-               }\r
-\r
-               public void setField(Field field, Object value) {\r
-                       FieldChange fieldChange = new FieldChange(field, value);\r
-                       if (fieldChanges == null) {\r
-                               fieldChanges = new LinkedHashSet<FieldChange>();\r
-                       }\r
-                       fieldChanges.add(fieldChange);\r
-               }\r
-\r
-               public String getString(Field field) {\r
-                       Object value = getField(field);\r
-                       if (value == null) {\r
-                               return null;\r
-                       }\r
-                       return value.toString();\r
-               }\r
-\r
-               @Override\r
-               public int compareTo(Change c) {\r
-                       return created.compareTo(c.created);\r
-               }\r
-\r
-               @Override\r
-               public int hashCode() {\r
-                       return id.hashCode();\r
-               }\r
-\r
-               @Override\r
-               public boolean equals(Object o) {\r
-                       if (o instanceof Change) {\r
-                               return id.equals(((Change) o).id);\r
-                       }\r
-                       return false;\r
-               }\r
-\r
-               @Override\r
-               public String toString() {\r
-                       StringBuilder sb = new StringBuilder();                 \r
-                       sb.append(new TimeUtils().timeAgo(created));\r
-                       switch (code) {\r
-                       case '+':\r
-                               sb.append(" created by ");\r
-                               break;\r
-                       default:\r
-                               if (hasComment()) {\r
-                                       sb.append(" commented on by ");\r
-                               } else {\r
-                                       sb.append(" changed by ");\r
-                               }\r
-                       }\r
-                       sb.append(author).append(" - ");\r
-                       if (hasComment()) {\r
-                               if (comment.deleted) {\r
-                                       sb.append("(deleted) ");\r
-                               }\r
-                               sb.append(comment.text).append(" ");\r
-                       }\r
-                       if (hasFieldChanges()) {\r
-                               switch (code) {\r
-                               case '+':\r
-                                       break;\r
-                               default:\r
-                                       for (FieldChange fieldChange : fieldChanges) {\r
-                                               sb.append("\n  ");\r
-                                               sb.append(fieldChange);\r
-                                       }\r
-                                       break;\r
-                               }\r
-                       }\r
-                       return sb.toString();\r
-               }\r
-       }\r
-\r
-       public static class Comment implements Serializable {\r
-\r
-               private static final long serialVersionUID = 1L;\r
-\r
-               public String text;\r
-\r
-               public String id;\r
-\r
-               public boolean deleted;\r
-\r
-               Comment(String text) {\r
-                       this.text = text;\r
-               }\r
-\r
-               @Override\r
-               public String toString() {\r
-                       return text;\r
-               }\r
-       }\r
-\r
-       public static class FieldChange implements Serializable {\r
-\r
-               private static final long serialVersionUID = 1L;\r
-\r
-               public final Field field;\r
-\r
-               public final Object value;\r
-\r
-               FieldChange(Field field, Object value) {\r
-                       this.field = field;\r
-                       this.value = value;\r
-               }\r
-\r
-               @Override\r
-               public int hashCode() {\r
-                       return field.hashCode();\r
-               }\r
-\r
-               @Override\r
-               public boolean equals(Object o) {\r
-                       if (o instanceof FieldChange) {\r
-                               return field.equals(((FieldChange) o).field);\r
-                       }\r
-                       return false;\r
-               }\r
-\r
-               @Override\r
-               public String toString() {\r
-                       return field + ": " + value;\r
-               }\r
-       }\r
-\r
-       public static class Attachment implements Serializable {\r
-\r
-               private static final long serialVersionUID = 1L;\r
-\r
-               public final String name;\r
-               public String id;\r
-               public long size;\r
-               public byte[] content;\r
-               public boolean deleted;\r
-\r
-               public Attachment(String name) {\r
-                       this.name = name;\r
-               }\r
-\r
-               @Override\r
-               public int hashCode() {\r
-                       return name.hashCode();\r
-               }\r
-\r
-               @Override\r
-               public boolean equals(Object o) {\r
-                       if (o instanceof Attachment) {\r
-                               return name.equalsIgnoreCase(((Attachment) o).name);\r
-                       }\r
-                       return false;\r
-               }\r
-\r
-               @Override\r
-               public String toString() {\r
-                       return name;\r
-               }\r
-       }\r
-\r
-       public static enum Field {\r
-               Id, Summary, Description, Reporter, Owner, Type, Status, Priority, Milestone, Component, Labels;\r
-       }\r
-\r
-       public static enum Type {\r
-               Defect, Enhancement, Task, Review, Other;\r
-\r
-               public static Type fromObject(Object o) {\r
-                       if (o instanceof Type) {\r
-                               // cast and return\r
-                               return (Type) o;\r
-                       } else if (o instanceof String) {\r
-                               // find by name\r
-                               for (Type type : values()) {\r
-                                       String str = o.toString();\r
-                                       if (type.toString().equalsIgnoreCase(str)) {\r
-                                               return type;\r
-                                       }\r
-                               }\r
-                       } else if (o instanceof Number) {\r
-                               // by ordinal\r
-                               int id = ((Number) o).intValue();\r
-                               if (id >= 0 && id < values().length) {\r
-                                       return values()[id];\r
-                               }\r
-                       }\r
-                       return null;\r
-               }\r
-       }\r
-\r
-       public static enum Priority {\r
-               Low, Medium, High, Critical;\r
-\r
-               public static Priority fromObject(Object o) {\r
-                       if (o instanceof Priority) {\r
-                               // cast and return\r
-                               return (Priority) o;\r
-                       } else if (o instanceof String) {\r
-                               // find by name\r
-                               for (Priority priority : values()) {\r
-                                       String str = o.toString();\r
-                                       if (priority.toString().equalsIgnoreCase(str)) {\r
-                                               return priority;\r
-                                       }\r
-                               }\r
-                       } else if (o instanceof Number) {\r
-                               // by ordinal\r
-                               int id = ((Number) o).intValue();\r
-                               if (id >= 0 && id < values().length) {\r
-                                       return values()[id];\r
-                               }\r
-                       }\r
-                       return null;\r
-               }\r
-       }\r
-\r
-       public static enum Status {\r
-               New, Accepted, Started, Review, Queued, Testing, Done, Fixed, WontFix, Duplicate, Invalid;\r
-\r
-               public static Status fromObject(Object o) {\r
-                       if (o instanceof Status) {\r
-                               // cast and return\r
-                               return (Status) o;\r
-                       } else if (o instanceof String) {\r
-                               // find by name\r
-                               for (Status status : values()) {\r
-                                       String str = o.toString();\r
-                                       if (status.toString().equalsIgnoreCase(str)) {\r
-                                               return status;\r
-                                       }\r
-                               }\r
-                       } else if (o instanceof Number) {\r
-                               // by ordinal\r
-                               int id = ((Number) o).intValue();\r
-                               if (id >= 0 && id < values().length) {\r
-                                       return values()[id];\r
-                               }\r
-                       }\r
-                       return null;\r
-               }\r
-\r
-               public boolean atLeast(Status status) {\r
-                       return ordinal() >= status.ordinal();\r
-               }\r
-\r
-               public boolean exceeds(Status status) {\r
-                       return ordinal() > status.ordinal();\r
-               }\r
-\r
-               public boolean isClosed() {\r
-                       return ordinal() >= Done.ordinal();\r
-               }\r
-\r
-               public Status next() {\r
-                       switch (this) {\r
-                       case New:\r
-                               return Started;\r
-                       case Accepted:\r
-                               return Started;\r
-                       case Started:\r
-                               return Testing;\r
-                       case Review:\r
-                               return Testing;\r
-                       case Queued:\r
-                               return Testing;\r
-                       case Testing:\r
-                               return Done;\r
-                       }\r
-                       return Accepted;\r
-               }\r
-       }\r
-}\r
diff --git a/src/main/java/com/gitblit/utils/IssueUtils.java b/src/main/java/com/gitblit/utils/IssueUtils.java
deleted file mode 100644 (file)
index dd09235..0000000
+++ /dev/null
@@ -1,829 +0,0 @@
-/*\r
- * Copyright 2012 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit.utils;\r
-\r
-import java.io.IOException;\r
-import java.text.MessageFormat;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-import java.util.TreeSet;\r
-\r
-import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;\r
-import org.eclipse.jgit.api.errors.JGitInternalException;\r
-import org.eclipse.jgit.dircache.DirCache;\r
-import org.eclipse.jgit.dircache.DirCacheBuilder;\r
-import org.eclipse.jgit.dircache.DirCacheEntry;\r
-import org.eclipse.jgit.internal.JGitText;\r
-import org.eclipse.jgit.lib.CommitBuilder;\r
-import org.eclipse.jgit.lib.Constants;\r
-import org.eclipse.jgit.lib.FileMode;\r
-import org.eclipse.jgit.lib.ObjectId;\r
-import org.eclipse.jgit.lib.ObjectInserter;\r
-import org.eclipse.jgit.lib.PersonIdent;\r
-import org.eclipse.jgit.lib.RefUpdate;\r
-import org.eclipse.jgit.lib.RefUpdate.Result;\r
-import org.eclipse.jgit.lib.Repository;\r
-import org.eclipse.jgit.revwalk.RevCommit;\r
-import org.eclipse.jgit.revwalk.RevTree;\r
-import org.eclipse.jgit.revwalk.RevWalk;\r
-import org.eclipse.jgit.treewalk.CanonicalTreeParser;\r
-import org.eclipse.jgit.treewalk.TreeWalk;\r
-import org.eclipse.jgit.treewalk.filter.AndTreeFilter;\r
-import org.eclipse.jgit.treewalk.filter.PathFilterGroup;\r
-import org.eclipse.jgit.treewalk.filter.TreeFilter;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.models.IssueModel;\r
-import com.gitblit.models.IssueModel.Attachment;\r
-import com.gitblit.models.IssueModel.Change;\r
-import com.gitblit.models.IssueModel.Field;\r
-import com.gitblit.models.IssueModel.Status;\r
-import com.gitblit.models.RefModel;\r
-import com.gitblit.utils.JsonUtils.ExcludeField;\r
-import com.google.gson.Gson;\r
-import com.google.gson.reflect.TypeToken;\r
-\r
-/**\r
- * Utility class for reading Gitblit issues.\r
- * \r
- * @author James Moger\r
- * \r
- */\r
-public class IssueUtils {\r
-\r
-       public static interface IssueFilter {\r
-               public abstract boolean accept(IssueModel issue);\r
-       }\r
-\r
-       public static final String GB_ISSUES = "refs/gitblit/issues";\r
-\r
-       static final Logger LOGGER = LoggerFactory.getLogger(IssueUtils.class);\r
-\r
-       /**\r
-        * Log an error message and exception.\r
-        * \r
-        * @param t\r
-        * @param repository\r
-        *            if repository is not null it MUST be the {0} parameter in the\r
-        *            pattern.\r
-        * @param pattern\r
-        * @param objects\r
-        */\r
-       private static void error(Throwable t, Repository repository, String pattern, Object... objects) {\r
-               List<Object> parameters = new ArrayList<Object>();\r
-               if (objects != null && objects.length > 0) {\r
-                       for (Object o : objects) {\r
-                               parameters.add(o);\r
-                       }\r
-               }\r
-               if (repository != null) {\r
-                       parameters.add(0, repository.getDirectory().getAbsolutePath());\r
-               }\r
-               LOGGER.error(MessageFormat.format(pattern, parameters.toArray()), t);\r
-       }\r
-\r
-       /**\r
-        * Returns a RefModel for the gb-issues branch in the repository. If the\r
-        * branch can not be found, null is returned.\r
-        * \r
-        * @param repository\r
-        * @return a refmodel for the gb-issues branch or null\r
-        */\r
-       public static RefModel getIssuesBranch(Repository repository) {\r
-               List<RefModel> refs = JGitUtils.getRefs(repository, com.gitblit.Constants.R_GITBLIT);\r
-               for (RefModel ref : refs) {\r
-                       if (ref.reference.getName().equals(GB_ISSUES)) {\r
-                               return ref;\r
-                       }\r
-               }\r
-               return null;\r
-       }\r
-\r
-       /**\r
-        * Returns all the issues in the repository. Querying issues from the\r
-        * repository requires deserializing all changes for all issues. This is an\r
-        * expensive process and not recommended. Issues should be indexed by Lucene\r
-        * and queries should be executed against that index.\r
-        * \r
-        * @param repository\r
-        * @param filter\r
-        *            optional issue filter to only return matching results\r
-        * @return a list of issues\r
-        */\r
-       public static List<IssueModel> getIssues(Repository repository, IssueFilter filter) {\r
-               List<IssueModel> list = new ArrayList<IssueModel>();\r
-               RefModel issuesBranch = getIssuesBranch(repository);\r
-               if (issuesBranch == null) {\r
-                       return list;\r
-               }\r
-\r
-               // Collect the set of all issue paths\r
-               Set<String> issuePaths = new HashSet<String>();\r
-               final TreeWalk tw = new TreeWalk(repository);\r
-               try {\r
-                       RevCommit head = JGitUtils.getCommit(repository, GB_ISSUES);\r
-                       tw.addTree(head.getTree());\r
-                       tw.setRecursive(false);\r
-                       while (tw.next()) {\r
-                               if (tw.getDepth() < 2 && tw.isSubtree()) {\r
-                                       tw.enterSubtree();\r
-                                       if (tw.getDepth() == 2) {\r
-                                               issuePaths.add(tw.getPathString());\r
-                                       }\r
-                               }\r
-                       }\r
-               } catch (IOException e) {\r
-                       error(e, repository, "{0} failed to query issues");\r
-               } finally {\r
-                       tw.release();\r
-               }\r
-\r
-               // Build each issue and optionally filter out unwanted issues\r
-\r
-               for (String issuePath : issuePaths) {\r
-                       RevWalk rw = new RevWalk(repository);\r
-                       try {\r
-                               RevCommit start = rw.parseCommit(repository.resolve(GB_ISSUES));\r
-                               rw.markStart(start);\r
-                       } catch (Exception e) {\r
-                               error(e, repository, "Failed to find {1} in {0}", GB_ISSUES);\r
-                       }\r
-                       TreeFilter treeFilter = AndTreeFilter.create(\r
-                                       PathFilterGroup.createFromStrings(issuePath), TreeFilter.ANY_DIFF);\r
-                       rw.setTreeFilter(treeFilter);\r
-                       Iterator<RevCommit> revlog = rw.iterator();\r
-\r
-                       List<RevCommit> commits = new ArrayList<RevCommit>();\r
-                       while (revlog.hasNext()) {\r
-                               commits.add(revlog.next());\r
-                       }\r
-\r
-                       // release the revwalk\r
-                       rw.release();\r
-\r
-                       if (commits.size() == 0) {\r
-                               LOGGER.warn("Failed to find changes for issue " + issuePath);\r
-                               continue;\r
-                       }\r
-\r
-                       // sort by commit order, first commit first\r
-                       Collections.reverse(commits);\r
-\r
-                       StringBuilder sb = new StringBuilder("[");\r
-                       boolean first = true;\r
-                       for (RevCommit commit : commits) {\r
-                               if (!first) {\r
-                                       sb.append(',');\r
-                               }\r
-                               String message = commit.getFullMessage();\r
-                               // commit message is formatted: C ISSUEID\n\nJSON\r
-                               // C is an single char commit code\r
-                               // ISSUEID is an SHA-1 hash\r
-                               String json = message.substring(43);\r
-                               sb.append(json);\r
-                               first = false;\r
-                       }\r
-                       sb.append(']');\r
-\r
-                       // Deserialize the JSON array as a Collection<Change>, this seems\r
-                       // slightly faster than deserializing each change by itself.\r
-                       Collection<Change> changes = JsonUtils.fromJsonString(sb.toString(),\r
-                                       new TypeToken<Collection<Change>>() {\r
-                                       }.getType());\r
-\r
-                       // create an issue object form the changes\r
-                       IssueModel issue = buildIssue(changes, true);\r
-\r
-                       // add the issue, conditionally, to the list\r
-                       if (filter == null) {\r
-                               list.add(issue);\r
-                       } else {\r
-                               if (filter.accept(issue)) {\r
-                                       list.add(issue);\r
-                               }\r
-                       }\r
-               }\r
-\r
-               // sort the issues by creation\r
-               Collections.sort(list);\r
-               return list;\r
-       }\r
-\r
-       /**\r
-        * Retrieves the specified issue from the repository with all changes\r
-        * applied to build the effective issue.\r
-        * \r
-        * @param repository\r
-        * @param issueId\r
-        * @return an issue, if it exists, otherwise null\r
-        */\r
-       public static IssueModel getIssue(Repository repository, String issueId) {\r
-               return getIssue(repository, issueId, true);\r
-       }\r
-\r
-       /**\r
-        * Retrieves the specified issue from the repository.\r
-        * \r
-        * @param repository\r
-        * @param issueId\r
-        * @param effective\r
-        *            if true, the effective issue is built by processing comment\r
-        *            changes, deletions, etc. if false, the raw issue is built\r
-        *            without consideration for comment changes, deletions, etc.\r
-        * @return an issue, if it exists, otherwise null\r
-        */\r
-       public static IssueModel getIssue(Repository repository, String issueId, boolean effective) {\r
-               RefModel issuesBranch = getIssuesBranch(repository);\r
-               if (issuesBranch == null) {\r
-                       return null;\r
-               }\r
-\r
-               if (StringUtils.isEmpty(issueId)) {\r
-                       return null;\r
-               }\r
-\r
-               String issuePath = getIssuePath(issueId);\r
-\r
-               // Collect all changes as JSON array from commit messages\r
-               List<RevCommit> commits = JGitUtils.getRevLog(repository, GB_ISSUES, issuePath, 0, -1);\r
-\r
-               // sort by commit order, first commit first\r
-               Collections.reverse(commits);\r
-\r
-               StringBuilder sb = new StringBuilder("[");\r
-               boolean first = true;\r
-               for (RevCommit commit : commits) {\r
-                       if (!first) {\r
-                               sb.append(',');\r
-                       }\r
-                       String message = commit.getFullMessage();\r
-                       // commit message is formatted: C ISSUEID\n\nJSON\r
-                       // C is an single char commit code\r
-                       // ISSUEID is an SHA-1 hash\r
-                       String json = message.substring(43);\r
-                       sb.append(json);\r
-                       first = false;\r
-               }\r
-               sb.append(']');\r
-\r
-               // Deserialize the JSON array as a Collection<Change>, this seems\r
-               // slightly faster than deserializing each change by itself.\r
-               Collection<Change> changes = JsonUtils.fromJsonString(sb.toString(),\r
-                               new TypeToken<Collection<Change>>() {\r
-                               }.getType());\r
-\r
-               // create an issue object and apply the changes to it\r
-               IssueModel issue = buildIssue(changes, effective);\r
-               return issue;\r
-       }\r
-\r
-       /**\r
-        * Builds an issue from a set of changes.\r
-        * \r
-        * @param changes\r
-        * @param effective\r
-        *            if true, the effective issue is built which accounts for\r
-        *            comment changes, comment deletions, etc. if false, the raw\r
-        *            issue is built.\r
-        * @return an issue\r
-        */\r
-       private static IssueModel buildIssue(Collection<Change> changes, boolean effective) {\r
-               IssueModel issue;\r
-               if (effective) {\r
-                       List<Change> effectiveChanges = new ArrayList<Change>();\r
-                       Map<String, Change> comments = new HashMap<String, Change>();\r
-                       for (Change change : changes) {\r
-                               if (change.comment != null) {\r
-                                       if (comments.containsKey(change.comment.id)) {\r
-                                               Change original = comments.get(change.comment.id);\r
-                                               Change clone = DeepCopier.copy(original);\r
-                                               clone.comment.text = change.comment.text;\r
-                                               clone.comment.deleted = change.comment.deleted;\r
-                                               int idx = effectiveChanges.indexOf(original);\r
-                                               effectiveChanges.remove(original);\r
-                                               effectiveChanges.add(idx, clone);\r
-                                               comments.put(clone.comment.id, clone);\r
-                                       } else {\r
-                                               effectiveChanges.add(change);\r
-                                               comments.put(change.comment.id, change);\r
-                                       }\r
-                               } else {\r
-                                       effectiveChanges.add(change);\r
-                               }\r
-                       }\r
-\r
-                       // effective issue\r
-                       issue = new IssueModel();\r
-                       for (Change change : effectiveChanges) {\r
-                               issue.applyChange(change);\r
-                       }\r
-               } else {\r
-                       // raw issue\r
-                       issue = new IssueModel();\r
-                       for (Change change : changes) {\r
-                               issue.applyChange(change);\r
-                       }\r
-               }\r
-               return issue;\r
-       }\r
-\r
-       /**\r
-        * Retrieves the specified attachment from an issue.\r
-        * \r
-        * @param repository\r
-        * @param issueId\r
-        * @param filename\r
-        * @return an attachment, if found, null otherwise\r
-        */\r
-       public static Attachment getIssueAttachment(Repository repository, String issueId,\r
-                       String filename) {\r
-               RefModel issuesBranch = getIssuesBranch(repository);\r
-               if (issuesBranch == null) {\r
-                       return null;\r
-               }\r
-\r
-               if (StringUtils.isEmpty(issueId)) {\r
-                       return null;\r
-               }\r
-\r
-               // deserialize the issue model so that we have the attachment metadata\r
-               IssueModel issue = getIssue(repository, issueId, true);\r
-               Attachment attachment = issue.getAttachment(filename);\r
-\r
-               // attachment not found\r
-               if (attachment == null) {\r
-                       return null;\r
-               }\r
-\r
-               // retrieve the attachment content\r
-               String issuePath = getIssuePath(issueId);\r
-               RevTree tree = JGitUtils.getCommit(repository, GB_ISSUES).getTree();\r
-               byte[] content = JGitUtils\r
-                               .getByteContent(repository, tree, issuePath + "/" + attachment.id, false);\r
-               attachment.content = content;\r
-               attachment.size = content.length;\r
-               return attachment;\r
-       }\r
-\r
-       /**\r
-        * Creates an issue in the gb-issues branch of the repository. The branch is\r
-        * automatically created if it does not already exist. Your change must\r
-        * include an author, summary, and description, at a minimum. If your change\r
-        * does not have those minimum requirements a RuntimeException will be\r
-        * thrown.\r
-        * \r
-        * @param repository\r
-        * @param change\r
-        * @return true if successful\r
-        */\r
-       public static IssueModel createIssue(Repository repository, Change change) {\r
-               RefModel issuesBranch = getIssuesBranch(repository);\r
-               if (issuesBranch == null) {\r
-                       JGitUtils.createOrphanBranch(repository, GB_ISSUES, null);\r
-               }\r
-\r
-               if (StringUtils.isEmpty(change.author)) {\r
-                       throw new RuntimeException("Must specify a change author!");\r
-               }\r
-               if (!change.hasField(Field.Summary)) {\r
-                       throw new RuntimeException("Must specify a summary!");\r
-               }\r
-               if (!change.hasField(Field.Description)) {\r
-                       throw new RuntimeException("Must specify a description!");\r
-               }\r
-\r
-               change.setField(Field.Reporter, change.author);\r
-\r
-               String issueId = StringUtils.getSHA1(change.created.toString() + change.author\r
-                               + change.getString(Field.Summary) + change.getField(Field.Description));\r
-               change.setField(Field.Id, issueId);\r
-               change.code = '+';\r
-\r
-               boolean success = commit(repository, issueId, change);\r
-               if (success) {\r
-                       return getIssue(repository, issueId, false);\r
-               }\r
-               return null;\r
-       }\r
-\r
-       /**\r
-        * Updates an issue in the gb-issues branch of the repository.\r
-        * \r
-        * @param repository\r
-        * @param issueId\r
-        * @param change\r
-        * @return true if successful\r
-        */\r
-       public static boolean updateIssue(Repository repository, String issueId, Change change) {\r
-               boolean success = false;\r
-               RefModel issuesBranch = getIssuesBranch(repository);\r
-\r
-               if (issuesBranch == null) {\r
-                       throw new RuntimeException("gb-issues branch does not exist!");\r
-               }\r
-\r
-               if (change == null) {\r
-                       throw new RuntimeException("change can not be null!");\r
-               }\r
-\r
-               if (StringUtils.isEmpty(change.author)) {\r
-                       throw new RuntimeException("must specify a change author!");\r
-               }\r
-\r
-               // determine update code\r
-               // default update code is '=' for a general change\r
-               change.code = '=';\r
-               if (change.hasField(Field.Status)) {\r
-                       Status status = Status.fromObject(change.getField(Field.Status));\r
-                       if (status.isClosed()) {\r
-                               // someone closed the issue\r
-                               change.code = 'x';\r
-                       }\r
-               }\r
-               success = commit(repository, issueId, change);\r
-               return success;\r
-       }\r
-\r
-       /**\r
-        * Deletes an issue from the repository.\r
-        * \r
-        * @param repository\r
-        * @param issueId\r
-        * @return true if successful\r
-        */\r
-       public static boolean deleteIssue(Repository repository, String issueId, String author) {\r
-               boolean success = false;\r
-               RefModel issuesBranch = getIssuesBranch(repository);\r
-\r
-               if (issuesBranch == null) {\r
-                       throw new RuntimeException(GB_ISSUES + " does not exist!");\r
-               }\r
-\r
-               if (StringUtils.isEmpty(issueId)) {\r
-                       throw new RuntimeException("must specify an issue id!");\r
-               }\r
-\r
-               String issuePath = getIssuePath(issueId);\r
-\r
-               String message = "- " + issueId;\r
-               try {\r
-                       ObjectId headId = repository.resolve(GB_ISSUES + "^{commit}");\r
-                       ObjectInserter odi = repository.newObjectInserter();\r
-                       try {\r
-                               // Create the in-memory index of the new/updated issue\r
-                               DirCache index = DirCache.newInCore();\r
-                               DirCacheBuilder dcBuilder = index.builder();\r
-                               // Traverse HEAD to add all other paths\r
-                               TreeWalk treeWalk = new TreeWalk(repository);\r
-                               int hIdx = -1;\r
-                               if (headId != null)\r
-                                       hIdx = treeWalk.addTree(new RevWalk(repository).parseTree(headId));\r
-                               treeWalk.setRecursive(true);\r
-                               while (treeWalk.next()) {\r
-                                       String path = treeWalk.getPathString();\r
-                                       CanonicalTreeParser hTree = null;\r
-                                       if (hIdx != -1)\r
-                                               hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class);\r
-                                       if (!path.startsWith(issuePath)) {\r
-                                               // add entries from HEAD for all other paths\r
-                                               if (hTree != null) {\r
-                                                       // create a new DirCacheEntry with data retrieved\r
-                                                       // from HEAD\r
-                                                       final DirCacheEntry dcEntry = new DirCacheEntry(path);\r
-                                                       dcEntry.setObjectId(hTree.getEntryObjectId());\r
-                                                       dcEntry.setFileMode(hTree.getEntryFileMode());\r
-\r
-                                                       // add to temporary in-core index\r
-                                                       dcBuilder.add(dcEntry);\r
-                                               }\r
-                                       }\r
-                               }\r
-\r
-                               // release the treewalk\r
-                               treeWalk.release();\r
-\r
-                               // finish temporary in-core index used for this commit\r
-                               dcBuilder.finish();\r
-\r
-                               ObjectId indexTreeId = index.writeTree(odi);\r
-\r
-                               // Create a commit object\r
-                               PersonIdent ident = new PersonIdent(author, "gitblit@localhost");\r
-                               CommitBuilder commit = new CommitBuilder();\r
-                               commit.setAuthor(ident);\r
-                               commit.setCommitter(ident);\r
-                               commit.setEncoding(Constants.CHARACTER_ENCODING);\r
-                               commit.setMessage(message);\r
-                               commit.setParentId(headId);\r
-                               commit.setTreeId(indexTreeId);\r
-\r
-                               // Insert the commit into the repository\r
-                               ObjectId commitId = odi.insert(commit);\r
-                               odi.flush();\r
-\r
-                               RevWalk revWalk = new RevWalk(repository);\r
-                               try {\r
-                                       RevCommit revCommit = revWalk.parseCommit(commitId);\r
-                                       RefUpdate ru = repository.updateRef(GB_ISSUES);\r
-                                       ru.setNewObjectId(commitId);\r
-                                       ru.setExpectedOldObjectId(headId);\r
-                                       ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);\r
-                                       Result rc = ru.forceUpdate();\r
-                                       switch (rc) {\r
-                                       case NEW:\r
-                                       case FORCED:\r
-                                       case FAST_FORWARD:\r
-                                               success = true;\r
-                                               break;\r
-                                       case REJECTED:\r
-                                       case LOCK_FAILURE:\r
-                                               throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD,\r
-                                                               ru.getRef(), rc);\r
-                                       default:\r
-                                               throw new JGitInternalException(MessageFormat.format(\r
-                                                               JGitText.get().updatingRefFailed, GB_ISSUES, commitId.toString(),\r
-                                                               rc));\r
-                                       }\r
-                               } finally {\r
-                                       revWalk.release();\r
-                               }\r
-                       } finally {\r
-                               odi.release();\r
-                       }\r
-               } catch (Throwable t) {\r
-                       error(t, repository, "Failed to delete issue {1} to {0}", issueId);\r
-               }\r
-               return success;\r
-       }\r
-\r
-       /**\r
-        * Changes the text of an issue comment.\r
-        * \r
-        * @param repository\r
-        * @param issue\r
-        * @param change\r
-        *            the change with the comment to change\r
-        * @param author\r
-        *            the author of the revision\r
-        * @param comment\r
-        *            the revised comment\r
-        * @return true, if the change was successful\r
-        */\r
-       public static boolean changeComment(Repository repository, IssueModel issue, Change change,\r
-                       String author, String comment) {\r
-               Change revision = new Change(author);\r
-               revision.comment(comment);\r
-               revision.comment.id = change.comment.id;\r
-               return updateIssue(repository, issue.id, revision);\r
-       }\r
-\r
-       /**\r
-        * Deletes a comment from an issue.\r
-        * \r
-        * @param repository\r
-        * @param issue\r
-        * @param change\r
-        *            the change with the comment to delete\r
-        * @param author\r
-        * @return true, if the deletion was successful\r
-        */\r
-       public static boolean deleteComment(Repository repository, IssueModel issue, Change change,\r
-                       String author) {\r
-               Change deletion = new Change(author);\r
-               deletion.comment(change.comment.text);\r
-               deletion.comment.id = change.comment.id;\r
-               deletion.comment.deleted = true;\r
-               return updateIssue(repository, issue.id, deletion);\r
-       }\r
-\r
-       /**\r
-        * Commit a change to the repository. Each issue is composed on changes.\r
-        * Issues are built from applying the changes in the order they were\r
-        * committed to the repository. The changes are actually specified in the\r
-        * commit messages and not in the RevTrees which allows for clean,\r
-        * distributed merging.\r
-        * \r
-        * @param repository\r
-        * @param issueId\r
-        * @param change\r
-        * @return true, if the change was committed\r
-        */\r
-       private static boolean commit(Repository repository, String issueId, Change change) {\r
-               boolean success = false;\r
-\r
-               try {\r
-                       // assign ids to new attachments\r
-                       // attachments are stored by an SHA1 id\r
-                       if (change.hasAttachments()) {\r
-                               for (Attachment attachment : change.attachments) {\r
-                                       if (!ArrayUtils.isEmpty(attachment.content)) {\r
-                                               byte[] prefix = (change.created.toString() + change.author).getBytes();\r
-                                               byte[] bytes = new byte[prefix.length + attachment.content.length];\r
-                                               System.arraycopy(prefix, 0, bytes, 0, prefix.length);\r
-                                               System.arraycopy(attachment.content, 0, bytes, prefix.length,\r
-                                                               attachment.content.length);\r
-                                               attachment.id = "attachment-" + StringUtils.getSHA1(bytes);\r
-                                       }\r
-                               }\r
-                       }\r
-\r
-                       // serialize the change as json\r
-                       // exclude any attachment from json serialization\r
-                       Gson gson = JsonUtils.gson(new ExcludeField(\r
-                                       "com.gitblit.models.IssueModel$Attachment.content"));\r
-                       String json = gson.toJson(change);\r
-\r
-                       // include the json change in the commit message\r
-                       String issuePath = getIssuePath(issueId);\r
-                       String message = change.code + " " + issueId + "\n\n" + json;\r
-\r
-                       // Create a commit file. This is required for a proper commit and\r
-                       // ensures we can retrieve the commit log of the issue path.\r
-                       //\r
-                       // This file is NOT serialized as part of the Change object.\r
-                       switch (change.code) {\r
-                       case '+': {\r
-                               // New Issue.\r
-                               Attachment placeholder = new Attachment("issue");\r
-                               placeholder.id = placeholder.name;\r
-                               placeholder.content = "DO NOT REMOVE".getBytes(Constants.CHARACTER_ENCODING);\r
-                               change.addAttachment(placeholder);\r
-                               break;\r
-                       }\r
-                       default: {\r
-                               // Update Issue.\r
-                               String changeId = StringUtils.getSHA1(json);\r
-                               Attachment placeholder = new Attachment("change-" + changeId);\r
-                               placeholder.id = placeholder.name;\r
-                               placeholder.content = "REMOVABLE".getBytes(Constants.CHARACTER_ENCODING);\r
-                               change.addAttachment(placeholder);\r
-                               break;\r
-                       }\r
-                       }\r
-\r
-                       ObjectId headId = repository.resolve(GB_ISSUES + "^{commit}");\r
-                       ObjectInserter odi = repository.newObjectInserter();\r
-                       try {\r
-                               // Create the in-memory index of the new/updated issue\r
-                               DirCache index = createIndex(repository, headId, issuePath, change);\r
-                               ObjectId indexTreeId = index.writeTree(odi);\r
-\r
-                               // Create a commit object\r
-                               PersonIdent ident = new PersonIdent(change.author, "gitblit@localhost");\r
-                               CommitBuilder commit = new CommitBuilder();\r
-                               commit.setAuthor(ident);\r
-                               commit.setCommitter(ident);\r
-                               commit.setEncoding(Constants.CHARACTER_ENCODING);\r
-                               commit.setMessage(message);\r
-                               commit.setParentId(headId);\r
-                               commit.setTreeId(indexTreeId);\r
-\r
-                               // Insert the commit into the repository\r
-                               ObjectId commitId = odi.insert(commit);\r
-                               odi.flush();\r
-\r
-                               RevWalk revWalk = new RevWalk(repository);\r
-                               try {\r
-                                       RevCommit revCommit = revWalk.parseCommit(commitId);\r
-                                       RefUpdate ru = repository.updateRef(GB_ISSUES);\r
-                                       ru.setNewObjectId(commitId);\r
-                                       ru.setExpectedOldObjectId(headId);\r
-                                       ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);\r
-                                       Result rc = ru.forceUpdate();\r
-                                       switch (rc) {\r
-                                       case NEW:\r
-                                       case FORCED:\r
-                                       case FAST_FORWARD:\r
-                                               success = true;\r
-                                               break;\r
-                                       case REJECTED:\r
-                                       case LOCK_FAILURE:\r
-                                               throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD,\r
-                                                               ru.getRef(), rc);\r
-                                       default:\r
-                                               throw new JGitInternalException(MessageFormat.format(\r
-                                                               JGitText.get().updatingRefFailed, GB_ISSUES, commitId.toString(),\r
-                                                               rc));\r
-                                       }\r
-                               } finally {\r
-                                       revWalk.release();\r
-                               }\r
-                       } finally {\r
-                               odi.release();\r
-                       }\r
-               } catch (Throwable t) {\r
-                       error(t, repository, "Failed to commit issue {1} to {0}", issueId);\r
-               }\r
-               return success;\r
-       }\r
-\r
-       /**\r
-        * Returns the issue path. This follows the same scheme as Git's object\r
-        * store path where the first two characters of the hash id are the root\r
-        * folder with the remaining characters as a subfolder within that folder.\r
-        * \r
-        * @param issueId\r
-        * @return the root path of the issue content on the gb-issues branch\r
-        */\r
-       static String getIssuePath(String issueId) {\r
-               return issueId.substring(0, 2) + "/" + issueId.substring(2);\r
-       }\r
-\r
-       /**\r
-        * Creates an in-memory index of the issue change.\r
-        * \r
-        * @param repo\r
-        * @param headId\r
-        * @param change\r
-        * @return an in-memory index\r
-        * @throws IOException\r
-        */\r
-       private static DirCache createIndex(Repository repo, ObjectId headId, String issuePath,\r
-                       Change change) throws IOException {\r
-\r
-               DirCache inCoreIndex = DirCache.newInCore();\r
-               DirCacheBuilder dcBuilder = inCoreIndex.builder();\r
-               ObjectInserter inserter = repo.newObjectInserter();\r
-\r
-               Set<String> ignorePaths = new TreeSet<String>();\r
-               try {\r
-                       // Add any attachments to the temporary index\r
-                       if (change.hasAttachments()) {\r
-                               for (Attachment attachment : change.attachments) {\r
-                                       // build a path name for the attachment and mark as ignored\r
-                                       String path = issuePath + "/" + attachment.id;\r
-                                       ignorePaths.add(path);\r
-\r
-                                       // create an index entry for this attachment\r
-                                       final DirCacheEntry dcEntry = new DirCacheEntry(path);\r
-                                       dcEntry.setLength(attachment.content.length);\r
-                                       dcEntry.setLastModified(change.created.getTime());\r
-                                       dcEntry.setFileMode(FileMode.REGULAR_FILE);\r
-\r
-                                       // insert object\r
-                                       dcEntry.setObjectId(inserter.insert(Constants.OBJ_BLOB, attachment.content));\r
-\r
-                                       // add to temporary in-core index\r
-                                       dcBuilder.add(dcEntry);\r
-                               }\r
-                       }\r
-\r
-                       // Traverse HEAD to add all other paths\r
-                       TreeWalk treeWalk = new TreeWalk(repo);\r
-                       int hIdx = -1;\r
-                       if (headId != null)\r
-                               hIdx = treeWalk.addTree(new RevWalk(repo).parseTree(headId));\r
-                       treeWalk.setRecursive(true);\r
-\r
-                       while (treeWalk.next()) {\r
-                               String path = treeWalk.getPathString();\r
-                               CanonicalTreeParser hTree = null;\r
-                               if (hIdx != -1)\r
-                                       hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class);\r
-                               if (!ignorePaths.contains(path)) {\r
-                                       // add entries from HEAD for all other paths\r
-                                       if (hTree != null) {\r
-                                               // create a new DirCacheEntry with data retrieved from\r
-                                               // HEAD\r
-                                               final DirCacheEntry dcEntry = new DirCacheEntry(path);\r
-                                               dcEntry.setObjectId(hTree.getEntryObjectId());\r
-                                               dcEntry.setFileMode(hTree.getEntryFileMode());\r
-\r
-                                               // add to temporary in-core index\r
-                                               dcBuilder.add(dcEntry);\r
-                                       }\r
-                               }\r
-                       }\r
-\r
-                       // release the treewalk\r
-                       treeWalk.release();\r
-\r
-                       // finish temporary in-core index used for this commit\r
-                       dcBuilder.finish();\r
-               } finally {\r
-                       inserter.release();\r
-               }\r
-               return inCoreIndex;\r
-       }\r
-}
\ No newline at end of file
index 51f05a917b2d9ba856ae28a5741603b4b669d404..2d653af08d089139c59b02ff1aa981289b9db95b 100644 (file)
@@ -59,7 +59,7 @@ import com.gitblit.utils.JGitUtils;
                MarkdownUtilsTest.class, JGitUtilsTest.class, SyndicationUtilsTest.class,\r
                DiffUtilsTest.class, MetricUtilsTest.class, X509UtilsTest.class,\r
                GitBlitTest.class, FederationTests.class, RpcTests.class, GitServletTest.class, GitDaemonTest.class,\r
-               GroovyScriptTest.class, LuceneExecutorTest.class, IssuesTest.class, RepositoryModelTest.class,\r
+               GroovyScriptTest.class, LuceneExecutorTest.class, RepositoryModelTest.class,\r
                FanoutServiceTest.class, Issue0259Test.class, Issue0271Test.class, HtpasswdUserServiceTest.class,\r
                ModelUtilsTest.class, JnaUtilsTest.class })\r
 public class GitBlitSuite {\r
diff --git a/src/test/java/com/gitblit/tests/IssuesTest.java b/src/test/java/com/gitblit/tests/IssuesTest.java
deleted file mode 100644 (file)
index 54cac33..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/*\r
- * Copyright 2012 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit.tests;\r
-\r
-import static org.junit.Assert.assertEquals;\r
-import static org.junit.Assert.assertFalse;\r
-import static org.junit.Assert.assertNotNull;\r
-import static org.junit.Assert.assertTrue;\r
-\r
-import java.util.List;\r
-\r
-import org.bouncycastle.util.Arrays;\r
-import org.eclipse.jgit.lib.Repository;\r
-import org.junit.Test;\r
-\r
-import com.gitblit.LuceneExecutor;\r
-import com.gitblit.models.IssueModel;\r
-import com.gitblit.models.IssueModel.Attachment;\r
-import com.gitblit.models.IssueModel.Change;\r
-import com.gitblit.models.IssueModel.Field;\r
-import com.gitblit.models.IssueModel.Priority;\r
-import com.gitblit.models.IssueModel.Status;\r
-import com.gitblit.models.SearchResult;\r
-import com.gitblit.utils.FileUtils;\r
-import com.gitblit.utils.IssueUtils;\r
-import com.gitblit.utils.IssueUtils.IssueFilter;\r
-\r
-/**\r
- * Tests the mechanics of distributed issue management on the gb-issues branch.\r
- * \r
- * @author James Moger\r
- * \r
- */\r
-public class IssuesTest {\r
-\r
-       @Test\r
-       public void testLifecycle() throws Exception {\r
-               Repository repository = GitBlitSuite.getIssuesTestRepository();\r
-               String name = FileUtils.getRelativePath(GitBlitSuite.REPOSITORIES, repository.getDirectory());\r
-               \r
-               // create and insert an issue\r
-               Change c1 = newChange("testCreation() " + Long.toHexString(System.currentTimeMillis()));\r
-               IssueModel issue = IssueUtils.createIssue(repository, c1);\r
-               assertNotNull(issue.id);\r
-\r
-               // retrieve issue and compare\r
-               IssueModel constructed = IssueUtils.getIssue(repository, issue.id);\r
-               compare(issue, constructed);\r
-\r
-               assertEquals(1, constructed.changes.size());\r
-               \r
-               // C1: create the issue\r
-               c1 = newChange("testUpdates() " + Long.toHexString(System.currentTimeMillis()));\r
-               issue = IssueUtils.createIssue(repository, c1);\r
-               assertNotNull(issue.id);\r
-\r
-               constructed = IssueUtils.getIssue(repository, issue.id);\r
-               compare(issue, constructed);\r
-               assertEquals(1, constructed.changes.size());\r
-\r
-               // C2: set owner\r
-               Change c2 = new Change("C2");\r
-               c2.comment("I'll fix this");\r
-               c2.setField(Field.Owner, c2.author);\r
-               assertTrue(IssueUtils.updateIssue(repository, issue.id, c2));\r
-               constructed = IssueUtils.getIssue(repository, issue.id);\r
-               assertEquals(2, constructed.changes.size());\r
-               assertEquals(c2.author, constructed.owner);\r
-\r
-               // C3: add a note\r
-               Change c3 = new Change("C3");\r
-               c3.comment("yeah, this is working");\r
-               assertTrue(IssueUtils.updateIssue(repository, issue.id, c3));\r
-               constructed = IssueUtils.getIssue(repository, issue.id);\r
-               assertEquals(3, constructed.changes.size());\r
-\r
-               // C4: add attachment\r
-               Change c4 = new Change("C4");\r
-               Attachment a = newAttachment();\r
-               c4.addAttachment(a);\r
-               assertTrue(IssueUtils.updateIssue(repository, issue.id, c4));\r
-\r
-               Attachment a1 = IssueUtils.getIssueAttachment(repository, issue.id, a.name);\r
-               assertEquals(a.content.length, a1.content.length);\r
-               assertTrue(Arrays.areEqual(a.content, a1.content));\r
-\r
-               // C5: close the issue\r
-               Change c5 = new Change("C5");\r
-               c5.comment("closing issue");\r
-               c5.setField(Field.Status, Status.Fixed);\r
-               assertTrue(IssueUtils.updateIssue(repository, issue.id, c5));\r
-\r
-               // retrieve issue again\r
-               constructed = IssueUtils.getIssue(repository, issue.id);\r
-\r
-               assertEquals(5, constructed.changes.size());\r
-               assertTrue(constructed.status.isClosed());\r
-\r
-               List<IssueModel> allIssues = IssueUtils.getIssues(repository, null);\r
-               List<IssueModel> openIssues = IssueUtils.getIssues(repository, new IssueFilter() {\r
-                       @Override\r
-                       public boolean accept(IssueModel issue) {\r
-                               return !issue.status.isClosed();\r
-                       }\r
-               });\r
-               List<IssueModel> closedIssues = IssueUtils.getIssues(repository, new IssueFilter() {\r
-                       @Override\r
-                       public boolean accept(IssueModel issue) {\r
-                               return issue.status.isClosed();\r
-                       }\r
-               });\r
-               \r
-               assertTrue(allIssues.size() > 0);\r
-               assertEquals(1, openIssues.size());\r
-               assertEquals(1, closedIssues.size());\r
-               \r
-               // build a new Lucene index\r
-               LuceneExecutor lucene = new LuceneExecutor(null, GitBlitSuite.REPOSITORIES);\r
-               lucene.deleteIndex(name);\r
-               for (IssueModel anIssue : allIssues) {\r
-                       lucene.index(name, anIssue);\r
-               }\r
-               List<SearchResult> hits = lucene.search("working", 1, 10, name);\r
-               assertTrue(hits.size() == 1);\r
-               \r
-               // reindex an issue\r
-               issue = allIssues.get(0);\r
-               Change change = new Change("reindex");\r
-               change.comment("this is a test of reindexing an issue");\r
-               IssueUtils.updateIssue(repository, issue.id, change);\r
-               issue = IssueUtils.getIssue(repository, issue.id);\r
-               lucene.index(name, issue);\r
-\r
-               hits = lucene.search("working", 1, 10, name);\r
-               assertTrue(hits.size() == 1);\r
-\r
-               \r
-               // delete all issues\r
-               for (IssueModel anIssue : allIssues) {\r
-                       assertTrue(IssueUtils.deleteIssue(repository, anIssue.id, "D"));\r
-               }\r
-                               \r
-               lucene.close();\r
-               repository.close();\r
-       }\r
-       \r
-       @Test\r
-       public void testChangeComment() throws Exception {\r
-               Repository repository = GitBlitSuite.getIssuesTestRepository();\r
-               // C1: create the issue\r
-               Change c1 = newChange("testChangeComment() " + Long.toHexString(System.currentTimeMillis()));\r
-               IssueModel issue = IssueUtils.createIssue(repository, c1);\r
-               assertNotNull(issue.id);\r
-               assertTrue(issue.changes.get(0).hasComment());\r
-\r
-               assertTrue(IssueUtils.changeComment(repository, issue, c1, "E1", "I changed the comment"));\r
-               issue = IssueUtils.getIssue(repository, issue.id);\r
-               assertTrue(issue.changes.get(0).hasComment());\r
-               assertEquals("I changed the comment", issue.changes.get(0).comment.text);\r
-\r
-               assertTrue(IssueUtils.deleteIssue(repository, issue.id, "D"));\r
-\r
-               repository.close();\r
-       }\r
-\r
-       @Test\r
-       public void testDeleteComment() throws Exception {\r
-               Repository repository = GitBlitSuite.getIssuesTestRepository();\r
-               // C1: create the issue\r
-               Change c1 = newChange("testDeleteComment() " + Long.toHexString(System.currentTimeMillis()));\r
-               IssueModel issue = IssueUtils.createIssue(repository, c1);\r
-               assertNotNull(issue.id);\r
-               assertTrue(issue.changes.get(0).hasComment());\r
-\r
-               assertTrue(IssueUtils.deleteComment(repository, issue, c1, "D1"));\r
-               issue = IssueUtils.getIssue(repository, issue.id);\r
-               assertEquals(1, issue.changes.size());\r
-               assertFalse(issue.changes.get(0).hasComment());\r
-\r
-               issue = IssueUtils.getIssue(repository, issue.id, false);\r
-               assertEquals(2, issue.changes.size());\r
-               assertTrue(issue.changes.get(0).hasComment());\r
-               assertFalse(issue.changes.get(1).hasComment());\r
-\r
-               assertTrue(IssueUtils.deleteIssue(repository, issue.id, "D"));\r
-\r
-               repository.close();\r
-       }\r
-\r
-       private Change newChange(String summary) {\r
-               Change change = new Change("C1");\r
-               change.setField(Field.Summary, summary);\r
-               change.setField(Field.Description, "this is my description");\r
-               change.setField(Field.Priority, Priority.High);\r
-               change.setField(Field.Labels, "helpdesk");\r
-               change.comment("my comment");\r
-               return change;\r
-       }\r
-\r
-       private Attachment newAttachment() {\r
-               Attachment attachment = new Attachment(Long.toHexString(System.currentTimeMillis())\r
-                               + ".txt");\r
-               attachment.content = new byte[] { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,\r
-                               0x4a };\r
-               return attachment;\r
-       }\r
-\r
-       private void compare(IssueModel issue, IssueModel constructed) {\r
-               assertEquals(issue.id, constructed.id);\r
-               assertEquals(issue.reporter, constructed.reporter);\r
-               assertEquals(issue.owner, constructed.owner);\r
-               assertEquals(issue.summary, constructed.summary);\r
-               assertEquals(issue.description, constructed.description);\r
-               assertEquals(issue.created, constructed.created);\r
-\r
-               assertTrue(issue.hasLabel("helpdesk"));\r
-       }\r
-}
\ No newline at end of file