Removed unused Issues classes
diff --git a/src/main/java/com/gitblit/LuceneExecutor.java b/src/main/java/com/gitblit/LuceneExecutor.java
index 0e4baae..3446289 100644
--- a/src/main/java/com/gitblit/LuceneExecutor.java
+++ b/src/main/java/com/gitblit/LuceneExecutor.java
@@ -56,7 +56,6 @@
 import org.apache.lucene.search.IndexSearcher;

 import org.apache.lucene.search.Query;

 import org.apache.lucene.search.ScoreDoc;

-import org.apache.lucene.search.TermQuery;

 import org.apache.lucene.search.TopScoreDocCollector;

 import org.apache.lucene.search.highlight.Fragmenter;

 import org.apache.lucene.search.highlight.Highlighter;

@@ -86,14 +85,11 @@
 import org.slf4j.LoggerFactory;

 

 import com.gitblit.Constants.SearchObjectType;

-import com.gitblit.models.IssueModel;

-import com.gitblit.models.IssueModel.Attachment;

 import com.gitblit.models.PathModel.PathChangeModel;

 import com.gitblit.models.RefModel;

 import com.gitblit.models.RepositoryModel;

 import com.gitblit.models.SearchResult;

 import com.gitblit.utils.ArrayUtils;

-import com.gitblit.utils.IssueUtils;

 import com.gitblit.utils.JGitUtils;

 import com.gitblit.utils.StringUtils;

 

@@ -109,7 +105,6 @@
 	private static final int INDEX_VERSION = 5;

 

 	private static final String FIELD_OBJECT_TYPE = "type";

-	private static final String FIELD_ISSUE = "issue";

 	private static final String FIELD_PATH = "path";

 	private static final String FIELD_COMMIT = "commit";

 	private static final String FIELD_BRANCH = "branch";

@@ -120,7 +115,6 @@
 	private static final String FIELD_DATE = "date";

 	private static final String FIELD_TAG = "tag";

 	private static final String FIELD_LABEL = "label";

-	private static final String FIELD_ATTACHMENT = "attachment";

 

 	private static final String CONF_FILE = "lucene.conf";

 	private static final String LUCENE_DIR = "lucene";

@@ -475,9 +469,8 @@
 						&& branch.equals(defaultBranch)) {

 					// indexing "default" branch

 					indexBranch = true;

-				} else if (IssueUtils.GB_ISSUES.equals(branch)) {

-					// skip the GB_ISSUES branch because it is indexed later

-					// note: this is different than updateIndex

+				} else if (branch.getName().startsWith(com.gitblit.Constants.R_GITBLIT)) {

+					// skip Gitblit internal branches

 					indexBranch = false;

 				} else {

 					// normal explicit branch check

@@ -617,19 +610,6 @@
 			// finished

 			reader.release();

 			

-			// this repository has a gb-issues branch, index all issues

-			if (IssueUtils.getIssuesBranch(repository) != null) {

-				List<IssueModel> issues = IssueUtils.getIssues(repository, null);

-				if (issues.size() > 0) {

-					result.branchCount += 1;

-				}

-				for (IssueModel issue : issues) {

-					result.issueCount++;

-					Document doc = createDocument(issue);

-					writer.addDocument(doc);

-				}

-			}

-

 			// commit all changes and reset the searcher

 			config.setInt(CONF_INDEX, null, CONF_VERSION, INDEX_VERSION);

 			config.save();

@@ -723,55 +703,6 @@
 	}

 

 	/**

-	 * Incrementally update the index with the specified issue for the

-	 * repository.

-	 * 

-	 * @param repositoryName

-	 * @param issue

-	 * @return true, if successful

-	 */

-	public boolean index(String repositoryName, IssueModel issue) {

-		try {

-			// delete the old issue from the index, if exists

-			deleteIssue(repositoryName, issue.id);

-			Document doc = createDocument(issue);

-			return index(repositoryName, doc);

-		} catch (Exception e) {

-			logger.error(MessageFormat.format("Error while indexing issue {0} in {1}", issue.id, repositoryName), e);

-		}

-		return false;

-	}

-	

-	/**

-	 * Delete an issue from the repository index.

-	 * 

-	 * @param repositoryName

-	 * @param issueId

-	 * @throws Exception

-	 * @return true, if deleted, false if no record was deleted

-	 */

-	private boolean deleteIssue(String repositoryName, String issueId) throws Exception {

-		BooleanQuery query = new BooleanQuery();

-		Term objectTerm = new Term(FIELD_OBJECT_TYPE, SearchObjectType.issue.name());

-		query.add(new TermQuery(objectTerm), Occur.MUST);

-		Term issueidTerm = new Term(FIELD_ISSUE, issueId);

-		query.add(new TermQuery(issueidTerm), Occur.MUST);

-		

-		IndexWriter writer = getIndexWriter(repositoryName);

-		int numDocsBefore = writer.numDocs();

-		writer.deleteDocuments(query);

-		writer.commit();

-		int numDocsAfter = writer.numDocs();

-		if (numDocsBefore == numDocsAfter) {

-			logger.debug(MessageFormat.format("no records found to delete {0}", query.toString()));

-			return false;

-		} else {

-			logger.debug(MessageFormat.format("deleted {0} records with {1}", numDocsBefore - numDocsAfter, query.toString()));

-			return true;

-		}

-	}

-	

-	/**

 	 * Delete a blob from the specified branch of the repository index.

 	 * 

 	 * @param repositoryName

@@ -870,10 +801,9 @@
 						&& branch.equals(defaultBranch)) {

 					// indexing "default" branch

 					indexBranch = true;

-				} else if (IssueUtils.GB_ISSUES.equals(branch)) {

-					// update issues modified on the GB_ISSUES branch

-					// note: this is different than reindex

-					indexBranch = true;

+				} else if (branch.getName().startsWith(com.gitblit.Constants.R_GITBLIT)) {

+					// ignore internal Gitblit branches

+					indexBranch = false;

 				} else {

 					// normal explicit branch check

 					indexBranch = model.indexedBranches.contains(branch.getName());

@@ -904,35 +834,11 @@
 					result.branchCount += 1;

 				}

 				

-				// track the issue ids that we have already indexed

-				Set<String> indexedIssues = new TreeSet<String>();

-				

 				// reverse the list of commits so we start with the first commit				

 				Collections.reverse(revs);

 				for (RevCommit commit : revs) {					

-					if (IssueUtils.GB_ISSUES.equals(branch)) {

-						// only index an issue once during updateIndex

-						String issueId = commit.getShortMessage().substring(2).trim();

-						if (indexedIssues.contains(issueId)) {

-							continue;

-						}

-						indexedIssues.add(issueId);

-						

-						IssueModel issue = IssueUtils.getIssue(repository, issueId);

-						if (issue == null) {

-							// issue was deleted, remove from index

-							if (!deleteIssue(model.name, issueId)) {

-								logger.error(MessageFormat.format("Failed to delete issue {0} from Lucene index!", issueId));

-							}

-						} else {

-							// issue was updated

-							index(model.name, issue);

-							result.issueCount++;

-						}

-					} else {

-						// index a commit

-						result.add(index(model.name, repository, branchName, commit));

-					}

+					// index a commit

+					result.add(index(model.name, repository, branchName, commit));

 				}

 

 				// update the config

@@ -959,34 +865,6 @@
 	}

 	

 	/**

-	 * Creates a Lucene document from an issue.

-	 * 

-	 * @param issue

-	 * @return a Lucene document

-	 */

-	private Document createDocument(IssueModel issue) {

-		Document doc = new Document();

-		doc.add(new Field(FIELD_OBJECT_TYPE, SearchObjectType.issue.name(), Store.YES,

-				Field.Index.NOT_ANALYZED));

-		doc.add(new Field(FIELD_ISSUE, issue.id, Store.YES, Index.ANALYZED));

-		doc.add(new Field(FIELD_BRANCH, IssueUtils.GB_ISSUES, Store.YES, Index.ANALYZED));

-		doc.add(new Field(FIELD_DATE, DateTools.dateToString(issue.created, Resolution.MINUTE),

-				Store.YES, Field.Index.NO));

-		doc.add(new Field(FIELD_AUTHOR, issue.reporter, Store.YES, Index.ANALYZED));

-		List<String> attachments = new ArrayList<String>();

-		for (Attachment attachment : issue.getAttachments()) {

-			attachments.add(attachment.name.toLowerCase());

-		}

-		doc.add(new Field(FIELD_ATTACHMENT, StringUtils.flattenStrings(attachments), Store.YES,

-				Index.ANALYZED));

-		doc.add(new Field(FIELD_SUMMARY, issue.summary, Store.YES, Index.ANALYZED));

-		doc.add(new Field(FIELD_CONTENT, issue.toString(), Store.YES, Index.ANALYZED));

-		doc.add(new Field(FIELD_LABEL, StringUtils.flattenStrings(issue.getLabels()), Store.YES,

-				Index.ANALYZED));

-		return doc;

-	}

-

-	/**

 	 * Creates a Lucene document for a commit

 	 * 

 	 * @param commit

@@ -1042,7 +920,6 @@
 		result.type = SearchObjectType.fromName(doc.get(FIELD_OBJECT_TYPE));

 		result.branch = doc.get(FIELD_BRANCH);

 		result.commitId = doc.get(FIELD_COMMIT);

-		result.issueId = doc.get(FIELD_ISSUE);

 		result.path = doc.get(FIELD_PATH);

 		if (doc.get(FIELD_TAG) != null) {

 			result.tags = StringUtils.getStringsFromValue(doc.get(FIELD_TAG));

diff --git a/src/main/java/com/gitblit/models/IssueModel.java b/src/main/java/com/gitblit/models/IssueModel.java
deleted file mode 100644
index c903891..0000000
--- a/src/main/java/com/gitblit/models/IssueModel.java
+++ /dev/null
@@ -1,532 +0,0 @@
-/*

- * 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.LinkedHashSet;

-import java.util.List;

-import java.util.Set;

-

-import com.gitblit.utils.ArrayUtils;

-import com.gitblit.utils.StringUtils;

-import com.gitblit.utils.TimeUtils;

-

-/**

- * 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() {

-		// the first applied change set the date appropriately

-		created = new Date(0);

-

-		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 boolean hasLabel(String label) {

-		return getLabels().contains(label);

-	}

-

-	public List<String> getLabels() {

-		List<String> list = new ArrayList<String>();

-		String labels = null;

-		for (Change change : changes) {

-			if (change.hasField(Field.Labels)) {

-				labels = change.getString(Field.Labels);

-			}

-		}

-		if (!StringUtils.isEmpty(labels)) {

-			list.addAll(StringUtils.getStringsFromValue(labels, " "));

-		}

-		return list;

-	}

-

-	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 List<Attachment> getAttachments() {

-		List<Attachment> list = new ArrayList<Attachment>();

-		for (Change change : changes) {

-			if (change.hasAttachments()) {

-				list.addAll(change.attachments);

-			}

-		}

-		return list;

-	}

-

-	public void applyChange(Change change) {

-		if (changes.size() == 0) {

-			// first change created the issue

-			created = change.created;

-		}

-		changes.add(change);

-

-		if (change.hasFieldChanges()) {

-			for (FieldChange fieldChange : change.fieldChanges) {

-				switch (fieldChange.field) {

-				case Id:

-					id = fieldChange.value.toString();

-					break;

-				case Type:

-					type = IssueModel.Type.fromObject(fieldChange.value);

-					break;

-				case Status:

-					status = IssueModel.Status.fromObject(fieldChange.value);

-					break;

-				case Priority:

-					priority = IssueModel.Priority.fromObject(fieldChange.value);

-					break;

-				case Summary:

-					summary = fieldChange.value.toString();

-					break;

-				case Description:

-					description = fieldChange.value.toString();

-					break;

-				case Reporter:

-					reporter = fieldChange.value.toString();

-					break;

-				case Owner:

-					owner = fieldChange.value.toString();

-					break;

-				case Milestone:

-					milestone = fieldChange.value.toString();

-					break;

-				}

-			}

-		}

-	}

-

-	@Override

-	public String toString() {

-		StringBuilder sb = new StringBuilder();

-		sb.append("issue ");

-		sb.append(id.substring(0, 8));

-		sb.append(" (" + summary + ")\n");

-		for (Change change : changes) {

-			sb.append(change);

-			sb.append('\n');

-		}

-		return sb.toString();

-	}

-

-	@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, Comparable<Change> {

-

-		private static final long serialVersionUID = 1L;

-

-		public final Date created;

-

-		public final String author;

-

-		public String id;

-

-		public char code;

-

-		public Comment comment;

-

-		public Set<FieldChange> fieldChanges;

-

-		public Set<Attachment> attachments;

-

-		public Change(String author) {

-			this.created = new Date((System.currentTimeMillis() / 1000) * 1000);

-			this.author = author;

-			this.id = StringUtils.getSHA1(created.toString() + author);

-		}

-

-		public boolean hasComment() {

-			return comment != null && !comment.deleted;

-		}

-

-		public void comment(String text) {

-			comment = new Comment(text);

-			comment.id = StringUtils.getSHA1(created.toString() + author + text);

-		}

-

-		public boolean hasAttachments() {

-			return !ArrayUtils.isEmpty(attachments);

-		}

-

-		public void addAttachment(Attachment attachment) {

-			if (attachments == null) {

-				attachments = new LinkedHashSet<Attachment>();

-			}

-			attachments.add(attachment);

-		}

-

-		public Attachment getAttachment(String name) {

-			for (Attachment attachment : attachments) {

-				if (attachment.name.equalsIgnoreCase(name)) {

-					return attachment;

-				}

-			}

-			return null;

-		}

-

-		public boolean hasField(Field field) {

-			return !StringUtils.isEmpty(getString(field));

-		}

-

-		public boolean hasFieldChanges() {

-			return !ArrayUtils.isEmpty(fieldChanges);

-		}

-

-		public Object getField(Field field) {

-			if (fieldChanges != null) {

-				for (FieldChange fieldChange : fieldChanges) {

-					if (fieldChange.field == field) {

-						return fieldChange.value;

-					}

-				}

-			}

-			return null;

-		}

-

-		public void setField(Field field, Object value) {

-			FieldChange fieldChange = new FieldChange(field, value);

-			if (fieldChanges == null) {

-				fieldChanges = new LinkedHashSet<FieldChange>();

-			}

-			fieldChanges.add(fieldChange);

-		}

-

-		public String getString(Field field) {

-			Object value = getField(field);

-			if (value == null) {

-				return null;

-			}

-			return value.toString();

-		}

-

-		@Override

-		public int compareTo(Change c) {

-			return created.compareTo(c.created);

-		}

-

-		@Override

-		public int hashCode() {

-			return id.hashCode();

-		}

-

-		@Override

-		public boolean equals(Object o) {

-			if (o instanceof Change) {

-				return id.equals(((Change) o).id);

-			}

-			return false;

-		}

-

-		@Override

-		public String toString() {

-			StringBuilder sb = new StringBuilder();			

-			sb.append(new TimeUtils().timeAgo(created));

-			switch (code) {

-			case '+':

-				sb.append(" created by ");

-				break;

-			default:

-				if (hasComment()) {

-					sb.append(" commented on by ");

-				} else {

-					sb.append(" changed by ");

-				}

-			}

-			sb.append(author).append(" - ");

-			if (hasComment()) {

-				if (comment.deleted) {

-					sb.append("(deleted) ");

-				}

-				sb.append(comment.text).append(" ");

-			}

-			if (hasFieldChanges()) {

-				switch (code) {

-				case '+':

-					break;

-				default:

-					for (FieldChange fieldChange : fieldChanges) {

-						sb.append("\n  ");

-						sb.append(fieldChange);

-					}

-					break;

-				}

-			}

-			return sb.toString();

-		}

-	}

-

-	public static class Comment implements Serializable {

-

-		private static final long serialVersionUID = 1L;

-

-		public String text;

-

-		public String id;

-

-		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 final Field field;

-

-		public final Object value;

-

-		FieldChange(Field field, Object value) {

-			this.field = field;

-			this.value = value;

-		}

-

-		@Override

-		public int hashCode() {

-			return field.hashCode();

-		}

-

-		@Override

-		public boolean equals(Object o) {

-			if (o instanceof FieldChange) {

-				return field.equals(((FieldChange) o).field);

-			}

-			return false;

-		}

-

-		@Override

-		public String toString() {

-			return field + ": " + value;

-		}

-	}

-

-	public static class Attachment implements Serializable {

-

-		private static final long serialVersionUID = 1L;

-

-		public final String name;

-		public String id;

-		public long size;

-		public byte[] content;

-		public boolean deleted;

-

-		public Attachment(String name) {

-			this.name = name;

-		}

-

-		@Override

-		public int hashCode() {

-			return name.hashCode();

-		}

-

-		@Override

-		public boolean equals(Object o) {

-			if (o instanceof Attachment) {

-				return name.equalsIgnoreCase(((Attachment) o).name);

-			}

-			return false;

-		}

-

-		@Override

-		public String toString() {

-			return name;

-		}

-	}

-

-	public static enum Field {

-		Id, Summary, Description, Reporter, Owner, Type, Status, Priority, Milestone, Component, Labels;

-	}

-

-	public static enum Type {

-		Defect, Enhancement, Task, Review, Other;

-

-		public static Type fromObject(Object o) {

-			if (o instanceof Type) {

-				// cast and return

-				return (Type) o;

-			} else if (o instanceof String) {

-				// find by name

-				for (Type type : values()) {

-					String str = o.toString();

-					if (type.toString().equalsIgnoreCase(str)) {

-						return type;

-					}

-				}

-			} else if (o instanceof Number) {

-				// by ordinal

-				int id = ((Number) o).intValue();

-				if (id >= 0 && id < values().length) {

-					return values()[id];

-				}

-			}

-			return null;

-		}

-	}

-

-	public static enum Priority {

-		Low, Medium, High, Critical;

-

-		public static Priority fromObject(Object o) {

-			if (o instanceof Priority) {

-				// cast and return

-				return (Priority) o;

-			} else if (o instanceof String) {

-				// find by name

-				for (Priority priority : values()) {

-					String str = o.toString();

-					if (priority.toString().equalsIgnoreCase(str)) {

-						return priority;

-					}

-				}

-			} else if (o instanceof Number) {

-				// by ordinal

-				int id = ((Number) o).intValue();

-				if (id >= 0 && id < values().length) {

-					return values()[id];

-				}

-			}

-			return null;

-		}

-	}

-

-	public static enum Status {

-		New, Accepted, Started, Review, Queued, Testing, Done, Fixed, WontFix, Duplicate, Invalid;

-

-		public static Status fromObject(Object o) {

-			if (o instanceof Status) {

-				// cast and return

-				return (Status) o;

-			} else if (o instanceof String) {

-				// find by name

-				for (Status status : values()) {

-					String str = o.toString();

-					if (status.toString().equalsIgnoreCase(str)) {

-						return status;

-					}

-				}

-			} else if (o instanceof Number) {

-				// by ordinal

-				int id = ((Number) o).intValue();

-				if (id >= 0 && id < values().length) {

-					return values()[id];

-				}

-			}

-			return null;

-		}

-

-		public boolean atLeast(Status status) {

-			return ordinal() >= status.ordinal();

-		}

-

-		public boolean exceeds(Status status) {

-			return ordinal() > status.ordinal();

-		}

-

-		public boolean isClosed() {

-			return ordinal() >= Done.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/main/java/com/gitblit/utils/IssueUtils.java b/src/main/java/com/gitblit/utils/IssueUtils.java
deleted file mode 100644
index dd09235..0000000
--- a/src/main/java/com/gitblit/utils/IssueUtils.java
+++ /dev/null
@@ -1,829 +0,0 @@
-/*

- * 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.Collection;

-import java.util.Collections;

-import java.util.HashMap;

-import java.util.HashSet;

-import java.util.Iterator;

-import java.util.List;

-import java.util.Map;

-import java.util.Set;

-import java.util.TreeSet;

-

-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.internal.JGitText;

-import org.eclipse.jgit.lib.CommitBuilder;

-import org.eclipse.jgit.lib.Constants;

-import org.eclipse.jgit.lib.FileMode;

-import org.eclipse.jgit.lib.ObjectId;

-import org.eclipse.jgit.lib.ObjectInserter;

-import org.eclipse.jgit.lib.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 org.eclipse.jgit.treewalk.filter.AndTreeFilter;

-import org.eclipse.jgit.treewalk.filter.PathFilterGroup;

-import org.eclipse.jgit.treewalk.filter.TreeFilter;

-import org.slf4j.Logger;

-import org.slf4j.LoggerFactory;

-

-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.IssueModel.Status;

-import com.gitblit.models.RefModel;

-import com.gitblit.utils.JsonUtils.ExcludeField;

-import com.google.gson.Gson;

-import com.google.gson.reflect.TypeToken;

-

-/**

- * Utility class for reading Gitblit issues.

- * 

- * @author James Moger

- * 

- */

-public class IssueUtils {

-

-	public static interface IssueFilter {

-		public abstract boolean accept(IssueModel issue);

-	}

-

-	public static final String GB_ISSUES = "refs/gitblit/issues";

-

-	static final Logger LOGGER = LoggerFactory.getLogger(IssueUtils.class);

-

-	/**

-	 * Log an error message and exception.

-	 * 

-	 * @param t

-	 * @param repository

-	 *            if repository is not null it MUST be the {0} parameter in the

-	 *            pattern.

-	 * @param pattern

-	 * @param objects

-	 */

-	private static void error(Throwable t, Repository repository, String pattern, Object... objects) {

-		List<Object> parameters = new ArrayList<Object>();

-		if (objects != null && objects.length > 0) {

-			for (Object o : objects) {

-				parameters.add(o);

-			}

-		}

-		if (repository != null) {

-			parameters.add(0, repository.getDirectory().getAbsolutePath());

-		}

-		LOGGER.error(MessageFormat.format(pattern, parameters.toArray()), t);

-	}

-

-	/**

-	 * 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) {

-		List<RefModel> refs = JGitUtils.getRefs(repository, com.gitblit.Constants.R_GITBLIT);

-		for (RefModel ref : refs) {

-			if (ref.reference.getName().equals(GB_ISSUES)) {

-				return ref;

-			}

-		}

-		return null;

-	}

-

-	/**

-	 * Returns all the issues in the repository. Querying issues from the

-	 * repository requires deserializing all changes for all issues. This is an

-	 * expensive process and not recommended. Issues should be indexed by Lucene

-	 * and queries should be executed against that index.

-	 * 

-	 * @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;

-		}

-

-		// Collect the set of all issue paths

-		Set<String> issuePaths = new HashSet<String>();

-		final TreeWalk tw = new TreeWalk(repository);

-		try {

-			RevCommit head = JGitUtils.getCommit(repository, GB_ISSUES);

-			tw.addTree(head.getTree());

-			tw.setRecursive(false);

-			while (tw.next()) {

-				if (tw.getDepth() < 2 && tw.isSubtree()) {

-					tw.enterSubtree();

-					if (tw.getDepth() == 2) {

-						issuePaths.add(tw.getPathString());

-					}

-				}

-			}

-		} catch (IOException e) {

-			error(e, repository, "{0} failed to query issues");

-		} finally {

-			tw.release();

-		}

-

-		// Build each issue and optionally filter out unwanted issues

-

-		for (String issuePath : issuePaths) {

-			RevWalk rw = new RevWalk(repository);

-			try {

-				RevCommit start = rw.parseCommit(repository.resolve(GB_ISSUES));

-				rw.markStart(start);

-			} catch (Exception e) {

-				error(e, repository, "Failed to find {1} in {0}", GB_ISSUES);

-			}

-			TreeFilter treeFilter = AndTreeFilter.create(

-					PathFilterGroup.createFromStrings(issuePath), TreeFilter.ANY_DIFF);

-			rw.setTreeFilter(treeFilter);

-			Iterator<RevCommit> revlog = rw.iterator();

-

-			List<RevCommit> commits = new ArrayList<RevCommit>();

-			while (revlog.hasNext()) {

-				commits.add(revlog.next());

-			}

-

-			// release the revwalk

-			rw.release();

-

-			if (commits.size() == 0) {

-				LOGGER.warn("Failed to find changes for issue " + issuePath);

-				continue;

-			}

-

-			// sort by commit order, first commit first

-			Collections.reverse(commits);

-

-			StringBuilder sb = new StringBuilder("[");

-			boolean first = true;

-			for (RevCommit commit : commits) {

-				if (!first) {

-					sb.append(',');

-				}

-				String message = commit.getFullMessage();

-				// commit message is formatted: C ISSUEID\n\nJSON

-				// C is an single char commit code

-				// ISSUEID is an SHA-1 hash

-				String json = message.substring(43);

-				sb.append(json);

-				first = false;

-			}

-			sb.append(']');

-

-			// Deserialize the JSON array as a Collection<Change>, this seems

-			// slightly faster than deserializing each change by itself.

-			Collection<Change> changes = JsonUtils.fromJsonString(sb.toString(),

-					new TypeToken<Collection<Change>>() {

-					}.getType());

-

-			// create an issue object form the changes

-			IssueModel issue = buildIssue(changes, true);

-

-			// add the issue, conditionally, to the list

-			if (filter == null) {

-				list.add(issue);

-			} else {

-				if (filter.accept(issue)) {

-					list.add(issue);

-				}

-			}

-		}

-

-		// sort the issues by creation

-		Collections.sort(list);

-		return list;

-	}

-

-	/**

-	 * Retrieves the specified issue from the repository with all changes

-	 * applied to build the effective issue.

-	 * 

-	 * @param repository

-	 * @param issueId

-	 * @return an issue, if it exists, otherwise null

-	 */

-	public static IssueModel getIssue(Repository repository, String issueId) {

-		return getIssue(repository, issueId, true);

-	}

-

-	/**

-	 * Retrieves the specified issue from the repository.

-	 * 

-	 * @param repository

-	 * @param issueId

-	 * @param effective

-	 *            if true, the effective issue is built by processing comment

-	 *            changes, deletions, etc. if false, the raw issue is built

-	 *            without consideration for comment changes, deletions, etc.

-	 * @return an issue, if it exists, otherwise null

-	 */

-	public static IssueModel getIssue(Repository repository, String issueId, boolean effective) {

-		RefModel issuesBranch = getIssuesBranch(repository);

-		if (issuesBranch == null) {

-			return null;

-		}

-

-		if (StringUtils.isEmpty(issueId)) {

-			return null;

-		}

-

-		String issuePath = getIssuePath(issueId);

-

-		// Collect all changes as JSON array from commit messages

-		List<RevCommit> commits = JGitUtils.getRevLog(repository, GB_ISSUES, issuePath, 0, -1);

-

-		// sort by commit order, first commit first

-		Collections.reverse(commits);

-

-		StringBuilder sb = new StringBuilder("[");

-		boolean first = true;

-		for (RevCommit commit : commits) {

-			if (!first) {

-				sb.append(',');

-			}

-			String message = commit.getFullMessage();

-			// commit message is formatted: C ISSUEID\n\nJSON

-			// C is an single char commit code

-			// ISSUEID is an SHA-1 hash

-			String json = message.substring(43);

-			sb.append(json);

-			first = false;

-		}

-		sb.append(']');

-

-		// Deserialize the JSON array as a Collection<Change>, this seems

-		// slightly faster than deserializing each change by itself.

-		Collection<Change> changes = JsonUtils.fromJsonString(sb.toString(),

-				new TypeToken<Collection<Change>>() {

-				}.getType());

-

-		// create an issue object and apply the changes to it

-		IssueModel issue = buildIssue(changes, effective);

-		return issue;

-	}

-

-	/**

-	 * Builds an issue from a set of changes.

-	 * 

-	 * @param changes

-	 * @param effective

-	 *            if true, the effective issue is built which accounts for

-	 *            comment changes, comment deletions, etc. if false, the raw

-	 *            issue is built.

-	 * @return an issue

-	 */

-	private static IssueModel buildIssue(Collection<Change> changes, boolean effective) {

-		IssueModel issue;

-		if (effective) {

-			List<Change> effectiveChanges = new ArrayList<Change>();

-			Map<String, Change> comments = new HashMap<String, Change>();

-			for (Change change : changes) {

-				if (change.comment != null) {

-					if (comments.containsKey(change.comment.id)) {

-						Change original = comments.get(change.comment.id);

-						Change clone = DeepCopier.copy(original);

-						clone.comment.text = change.comment.text;

-						clone.comment.deleted = change.comment.deleted;

-						int idx = effectiveChanges.indexOf(original);

-						effectiveChanges.remove(original);

-						effectiveChanges.add(idx, clone);

-						comments.put(clone.comment.id, clone);

-					} else {

-						effectiveChanges.add(change);

-						comments.put(change.comment.id, change);

-					}

-				} else {

-					effectiveChanges.add(change);

-				}

-			}

-

-			// effective issue

-			issue = new IssueModel();

-			for (Change change : effectiveChanges) {

-				issue.applyChange(change);

-			}

-		} else {

-			// raw issue

-			issue = new IssueModel();

-			for (Change change : changes) {

-				issue.applyChange(change);

-			}

-		}

-		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

-		IssueModel issue = getIssue(repository, issueId, true);

-		Attachment attachment = issue.getAttachment(filename);

-

-		// attachment not found

-		if (attachment == null) {

-			return null;

-		}

-

-		// retrieve the attachment content

-		String issuePath = getIssuePath(issueId);

-		RevTree tree = JGitUtils.getCommit(repository, GB_ISSUES).getTree();

-		byte[] content = JGitUtils

-				.getByteContent(repository, tree, issuePath + "/" + attachment.id, false);

-		attachment.content = content;

-		attachment.size = content.length;

-		return attachment;

-	}

-

-	/**

-	 * Creates an issue in the gb-issues branch of the repository. The branch is

-	 * automatically created if it does not already exist. Your change must

-	 * include an author, summary, and description, at a minimum. If your change

-	 * does not have those minimum requirements a RuntimeException will be

-	 * thrown.

-	 * 

-	 * @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);

-		}

-

-		if (StringUtils.isEmpty(change.author)) {

-			throw new RuntimeException("Must specify a change author!");

-		}

-		if (!change.hasField(Field.Summary)) {

-			throw new RuntimeException("Must specify a summary!");

-		}

-		if (!change.hasField(Field.Description)) {

-			throw new RuntimeException("Must specify a description!");

-		}

-

-		change.setField(Field.Reporter, change.author);

-

-		String issueId = StringUtils.getSHA1(change.created.toString() + change.author

-				+ change.getString(Field.Summary) + change.getField(Field.Description));

-		change.setField(Field.Id, issueId);

-		change.code = '+';

-

-		boolean success = commit(repository, issueId, change);

-		if (success) {

-			return getIssue(repository, issueId, false);

-		}

-		return null;

-	}

-

-	/**

-	 * Updates an issue in the gb-issues branch of the repository.

-	 * 

-	 * @param repository

-	 * @param issueId

-	 * @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 a change author!");

-		}

-

-		// determine update code

-		// default update code is '=' for a general change

-		change.code = '=';

-		if (change.hasField(Field.Status)) {

-			Status status = Status.fromObject(change.getField(Field.Status));

-			if (status.isClosed()) {

-				// someone closed the issue

-				change.code = 'x';

-			}

-		}

-		success = commit(repository, issueId, change);

-		return success;

-	}

-

-	/**

-	 * Deletes an issue from the repository.

-	 * 

-	 * @param repository

-	 * @param issueId

-	 * @return true if successful

-	 */

-	public static boolean deleteIssue(Repository repository, String issueId, String author) {

-		boolean success = false;

-		RefModel issuesBranch = getIssuesBranch(repository);

-

-		if (issuesBranch == null) {

-			throw new RuntimeException(GB_ISSUES + " does not exist!");

-		}

-

-		if (StringUtils.isEmpty(issueId)) {

-			throw new RuntimeException("must specify an issue id!");

-		}

-

-		String issuePath = getIssuePath(issueId);

-

-		String message = "- " + issueId;

-		try {

-			ObjectId headId = repository.resolve(GB_ISSUES + "^{commit}");

-			ObjectInserter odi = repository.newObjectInserter();

-			try {

-				// Create the in-memory index of the new/updated issue

-				DirCache index = DirCache.newInCore();

-				DirCacheBuilder dcBuilder = index.builder();

-				// Traverse HEAD to add all other paths

-				TreeWalk treeWalk = new TreeWalk(repository);

-				int hIdx = -1;

-				if (headId != null)

-					hIdx = treeWalk.addTree(new RevWalk(repository).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 (!path.startsWith(issuePath)) {

-						// 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();

-

-				ObjectId indexTreeId = index.writeTree(odi);

-

-				// Create a commit object

-				PersonIdent ident = new PersonIdent(author, "gitblit@localhost");

-				CommitBuilder commit = new CommitBuilder();

-				commit.setAuthor(ident);

-				commit.setCommitter(ident);

-				commit.setEncoding(Constants.CHARACTER_ENCODING);

-				commit.setMessage(message);

-				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) {

-			error(t, repository, "Failed to delete issue {1} to {0}", issueId);

-		}

-		return success;

-	}

-

-	/**

-	 * Changes the text of an issue comment.

-	 * 

-	 * @param repository

-	 * @param issue

-	 * @param change

-	 *            the change with the comment to change

-	 * @param author

-	 *            the author of the revision

-	 * @param comment

-	 *            the revised comment

-	 * @return true, if the change was successful

-	 */

-	public static boolean changeComment(Repository repository, IssueModel issue, Change change,

-			String author, String comment) {

-		Change revision = new Change(author);

-		revision.comment(comment);

-		revision.comment.id = change.comment.id;

-		return updateIssue(repository, issue.id, revision);

-	}

-

-	/**

-	 * Deletes a comment from an issue.

-	 * 

-	 * @param repository

-	 * @param issue

-	 * @param change

-	 *            the change with the comment to delete

-	 * @param author

-	 * @return true, if the deletion was successful

-	 */

-	public static boolean deleteComment(Repository repository, IssueModel issue, Change change,

-			String author) {

-		Change deletion = new Change(author);

-		deletion.comment(change.comment.text);

-		deletion.comment.id = change.comment.id;

-		deletion.comment.deleted = true;

-		return updateIssue(repository, issue.id, deletion);

-	}

-

-	/**

-	 * Commit a change to the repository. Each issue is composed on changes.

-	 * Issues are built from applying the changes in the order they were

-	 * committed to the repository. The changes are actually specified in the

-	 * commit messages and not in the RevTrees which allows for clean,

-	 * distributed merging.

-	 * 

-	 * @param repository

-	 * @param issueId

-	 * @param change

-	 * @return true, if the change was committed

-	 */

-	private static boolean commit(Repository repository, String issueId, Change change) {

-		boolean success = false;

-

-		try {

-			// assign ids to new attachments

-			// attachments are stored by an SHA1 id

-			if (change.hasAttachments()) {

-				for (Attachment attachment : change.attachments) {

-					if (!ArrayUtils.isEmpty(attachment.content)) {

-						byte[] prefix = (change.created.toString() + change.author).getBytes();

-						byte[] bytes = new byte[prefix.length + attachment.content.length];

-						System.arraycopy(prefix, 0, bytes, 0, prefix.length);

-						System.arraycopy(attachment.content, 0, bytes, prefix.length,

-								attachment.content.length);

-						attachment.id = "attachment-" + StringUtils.getSHA1(bytes);

-					}

-				}

-			}

-

-			// serialize the change as json

-			// exclude any attachment from json serialization

-			Gson gson = JsonUtils.gson(new ExcludeField(

-					"com.gitblit.models.IssueModel$Attachment.content"));

-			String json = gson.toJson(change);

-

-			// include the json change in the commit message

-			String issuePath = getIssuePath(issueId);

-			String message = change.code + " " + issueId + "\n\n" + json;

-

-			// Create a commit file. This is required for a proper commit and

-			// ensures we can retrieve the commit log of the issue path.

-			//

-			// This file is NOT serialized as part of the Change object.

-			switch (change.code) {

-			case '+': {

-				// New Issue.

-				Attachment placeholder = new Attachment("issue");

-				placeholder.id = placeholder.name;

-				placeholder.content = "DO NOT REMOVE".getBytes(Constants.CHARACTER_ENCODING);

-				change.addAttachment(placeholder);

-				break;

-			}

-			default: {

-				// Update Issue.

-				String changeId = StringUtils.getSHA1(json);

-				Attachment placeholder = new Attachment("change-" + changeId);

-				placeholder.id = placeholder.name;

-				placeholder.content = "REMOVABLE".getBytes(Constants.CHARACTER_ENCODING);

-				change.addAttachment(placeholder);

-				break;

-			}

-			}

-

-			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, issuePath, change);

-				ObjectId indexTreeId = index.writeTree(odi);

-

-				// Create a commit object

-				PersonIdent ident = new PersonIdent(change.author, "gitblit@localhost");

-				CommitBuilder commit = new CommitBuilder();

-				commit.setAuthor(ident);

-				commit.setCommitter(ident);

-				commit.setEncoding(Constants.CHARACTER_ENCODING);

-				commit.setMessage(message);

-				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) {

-			error(t, repository, "Failed to commit issue {1} to {0}", issueId);

-		}

-		return success;

-	}

-

-	/**

-	 * 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

-	 */

-	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 change

-	 * @return an in-memory index

-	 * @throws IOException

-	 */

-	private static DirCache createIndex(Repository repo, ObjectId headId, String issuePath,

-			Change change) throws IOException {

-

-		DirCache inCoreIndex = DirCache.newInCore();

-		DirCacheBuilder dcBuilder = inCoreIndex.builder();

-		ObjectInserter inserter = repo.newObjectInserter();

-

-		Set<String> ignorePaths = new TreeSet<String>();

-		try {

-			// Add any attachments to the temporary index

-			if (change.hasAttachments()) {

-				for (Attachment attachment : change.attachments) {

-					// build a path name for the attachment and mark as ignored

-					String path = issuePath + "/" + attachment.id;

-					ignorePaths.add(path);

-

-					// create an index entry for this attachment

-					final DirCacheEntry dcEntry = new DirCacheEntry(path);

-					dcEntry.setLength(attachment.content.length);

-					dcEntry.setLastModified(change.created.getTime());

-					dcEntry.setFileMode(FileMode.REGULAR_FILE);

-

-					// insert object

-					dcEntry.setObjectId(inserter.insert(Constants.OBJ_BLOB, attachment.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 (!ignorePaths.contains(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;

-	}

-}
\ No newline at end of file
diff --git a/src/test/java/com/gitblit/tests/GitBlitSuite.java b/src/test/java/com/gitblit/tests/GitBlitSuite.java
index 51f05a9..2d653af 100644
--- a/src/test/java/com/gitblit/tests/GitBlitSuite.java
+++ b/src/test/java/com/gitblit/tests/GitBlitSuite.java
@@ -59,7 +59,7 @@
 		MarkdownUtilsTest.class, JGitUtilsTest.class, SyndicationUtilsTest.class,

 		DiffUtilsTest.class, MetricUtilsTest.class, X509UtilsTest.class,

 		GitBlitTest.class, FederationTests.class, RpcTests.class, GitServletTest.class, GitDaemonTest.class,

-		GroovyScriptTest.class, LuceneExecutorTest.class, IssuesTest.class, RepositoryModelTest.class,

+		GroovyScriptTest.class, LuceneExecutorTest.class, RepositoryModelTest.class,

 		FanoutServiceTest.class, Issue0259Test.class, Issue0271Test.class, HtpasswdUserServiceTest.class,

 		ModelUtilsTest.class, JnaUtilsTest.class })

 public class GitBlitSuite {

diff --git a/src/test/java/com/gitblit/tests/IssuesTest.java b/src/test/java/com/gitblit/tests/IssuesTest.java
deleted file mode 100644
index 54cac33..0000000
--- a/src/test/java/com/gitblit/tests/IssuesTest.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*

- * 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.tests;

-

-import static org.junit.Assert.assertEquals;

-import static org.junit.Assert.assertFalse;

-import static org.junit.Assert.assertNotNull;

-import static org.junit.Assert.assertTrue;

-

-import java.util.List;

-

-import org.bouncycastle.util.Arrays;

-import org.eclipse.jgit.lib.Repository;

-import org.junit.Test;

-

-import com.gitblit.LuceneExecutor;

-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.IssueModel.Priority;

-import com.gitblit.models.IssueModel.Status;

-import com.gitblit.models.SearchResult;

-import com.gitblit.utils.FileUtils;

-import com.gitblit.utils.IssueUtils;

-import com.gitblit.utils.IssueUtils.IssueFilter;

-

-/**

- * Tests the mechanics of distributed issue management on the gb-issues branch.

- * 

- * @author James Moger

- * 

- */

-public class IssuesTest {

-

-	@Test

-	public void testLifecycle() throws Exception {

-		Repository repository = GitBlitSuite.getIssuesTestRepository();

-		String name = FileUtils.getRelativePath(GitBlitSuite.REPOSITORIES, repository.getDirectory());

-		

-		// create and insert an issue

-		Change c1 = newChange("testCreation() " + Long.toHexString(System.currentTimeMillis()));

-		IssueModel issue = IssueUtils.createIssue(repository, c1);

-		assertNotNull(issue.id);

-

-		// retrieve issue and compare

-		IssueModel constructed = IssueUtils.getIssue(repository, issue.id);

-		compare(issue, constructed);

-

-		assertEquals(1, constructed.changes.size());

-		

-		// C1: create the issue

-		c1 = newChange("testUpdates() " + Long.toHexString(System.currentTimeMillis()));

-		issue = IssueUtils.createIssue(repository, c1);

-		assertNotNull(issue.id);

-

-		constructed = IssueUtils.getIssue(repository, issue.id);

-		compare(issue, constructed);

-		assertEquals(1, constructed.changes.size());

-

-		// C2: set owner

-		Change c2 = new Change("C2");

-		c2.comment("I'll fix this");

-		c2.setField(Field.Owner, c2.author);

-		assertTrue(IssueUtils.updateIssue(repository, issue.id, c2));

-		constructed = IssueUtils.getIssue(repository, issue.id);

-		assertEquals(2, constructed.changes.size());

-		assertEquals(c2.author, constructed.owner);

-

-		// C3: add a note

-		Change c3 = new Change("C3");

-		c3.comment("yeah, this is working");

-		assertTrue(IssueUtils.updateIssue(repository, issue.id, c3));

-		constructed = IssueUtils.getIssue(repository, issue.id);

-		assertEquals(3, constructed.changes.size());

-

-		// C4: add attachment

-		Change c4 = new Change("C4");

-		Attachment a = newAttachment();

-		c4.addAttachment(a);

-		assertTrue(IssueUtils.updateIssue(repository, issue.id, c4));

-

-		Attachment a1 = IssueUtils.getIssueAttachment(repository, issue.id, a.name);

-		assertEquals(a.content.length, a1.content.length);

-		assertTrue(Arrays.areEqual(a.content, a1.content));

-

-		// C5: close the issue

-		Change c5 = new Change("C5");

-		c5.comment("closing issue");

-		c5.setField(Field.Status, Status.Fixed);

-		assertTrue(IssueUtils.updateIssue(repository, issue.id, c5));

-

-		// retrieve issue again

-		constructed = IssueUtils.getIssue(repository, issue.id);

-

-		assertEquals(5, constructed.changes.size());

-		assertTrue(constructed.status.isClosed());

-

-		List<IssueModel> allIssues = IssueUtils.getIssues(repository, null);

-		List<IssueModel> openIssues = IssueUtils.getIssues(repository, new IssueFilter() {

-			@Override

-			public boolean accept(IssueModel issue) {

-				return !issue.status.isClosed();

-			}

-		});

-		List<IssueModel> closedIssues = IssueUtils.getIssues(repository, new IssueFilter() {

-			@Override

-			public boolean accept(IssueModel issue) {

-				return issue.status.isClosed();

-			}

-		});

-		

-		assertTrue(allIssues.size() > 0);

-		assertEquals(1, openIssues.size());

-		assertEquals(1, closedIssues.size());

-		

-		// build a new Lucene index

-		LuceneExecutor lucene = new LuceneExecutor(null, GitBlitSuite.REPOSITORIES);

-		lucene.deleteIndex(name);

-		for (IssueModel anIssue : allIssues) {

-			lucene.index(name, anIssue);

-		}

-		List<SearchResult> hits = lucene.search("working", 1, 10, name);

-		assertTrue(hits.size() == 1);

-		

-		// reindex an issue

-		issue = allIssues.get(0);

-		Change change = new Change("reindex");

-		change.comment("this is a test of reindexing an issue");

-		IssueUtils.updateIssue(repository, issue.id, change);

-		issue = IssueUtils.getIssue(repository, issue.id);

-		lucene.index(name, issue);

-

-		hits = lucene.search("working", 1, 10, name);

-		assertTrue(hits.size() == 1);

-

-		

-		// delete all issues

-		for (IssueModel anIssue : allIssues) {

-			assertTrue(IssueUtils.deleteIssue(repository, anIssue.id, "D"));

-		}

-				

-		lucene.close();

-		repository.close();

-	}

-	

-	@Test

-	public void testChangeComment() throws Exception {

-		Repository repository = GitBlitSuite.getIssuesTestRepository();

-		// C1: create the issue

-		Change c1 = newChange("testChangeComment() " + Long.toHexString(System.currentTimeMillis()));

-		IssueModel issue = IssueUtils.createIssue(repository, c1);

-		assertNotNull(issue.id);

-		assertTrue(issue.changes.get(0).hasComment());

-

-		assertTrue(IssueUtils.changeComment(repository, issue, c1, "E1", "I changed the comment"));

-		issue = IssueUtils.getIssue(repository, issue.id);

-		assertTrue(issue.changes.get(0).hasComment());

-		assertEquals("I changed the comment", issue.changes.get(0).comment.text);

-

-		assertTrue(IssueUtils.deleteIssue(repository, issue.id, "D"));

-

-		repository.close();

-	}

-

-	@Test

-	public void testDeleteComment() throws Exception {

-		Repository repository = GitBlitSuite.getIssuesTestRepository();

-		// C1: create the issue

-		Change c1 = newChange("testDeleteComment() " + Long.toHexString(System.currentTimeMillis()));

-		IssueModel issue = IssueUtils.createIssue(repository, c1);

-		assertNotNull(issue.id);

-		assertTrue(issue.changes.get(0).hasComment());

-

-		assertTrue(IssueUtils.deleteComment(repository, issue, c1, "D1"));

-		issue = IssueUtils.getIssue(repository, issue.id);

-		assertEquals(1, issue.changes.size());

-		assertFalse(issue.changes.get(0).hasComment());

-

-		issue = IssueUtils.getIssue(repository, issue.id, false);

-		assertEquals(2, issue.changes.size());

-		assertTrue(issue.changes.get(0).hasComment());

-		assertFalse(issue.changes.get(1).hasComment());

-

-		assertTrue(IssueUtils.deleteIssue(repository, issue.id, "D"));

-

-		repository.close();

-	}

-

-	private Change newChange(String summary) {

-		Change change = new Change("C1");

-		change.setField(Field.Summary, summary);

-		change.setField(Field.Description, "this is my description");

-		change.setField(Field.Priority, Priority.High);

-		change.setField(Field.Labels, "helpdesk");

-		change.comment("my comment");

-		return change;

-	}

-

-	private Attachment newAttachment() {

-		Attachment attachment = new Attachment(Long.toHexString(System.currentTimeMillis())

-				+ ".txt");

-		attachment.content = new byte[] { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,

-				0x4a };

-		return attachment;

-	}

-

-	private void compare(IssueModel issue, IssueModel constructed) {

-		assertEquals(issue.id, constructed.id);

-		assertEquals(issue.reporter, constructed.reporter);

-		assertEquals(issue.owner, constructed.owner);

-		assertEquals(issue.summary, constructed.summary);

-		assertEquals(issue.description, constructed.description);

-		assertEquals(issue.created, constructed.created);

-

-		assertTrue(issue.hasLabel("helpdesk"));

-	}

-}
\ No newline at end of file