From a04808340abb7056fefb45645d9f5653813338fc Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Tue, 17 Sep 2013 17:49:20 -0400
Subject: [PATCH] Removed unused Issues classes

---
 /dev/null                                         |  231 ---------------------------------
 src/test/java/com/gitblit/tests/GitBlitSuite.java |    2 
 src/main/java/com/gitblit/LuceneExecutor.java     |  137 +------------------
 3 files changed, 8 insertions(+), 362 deletions(-)

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();
@@ -722,55 +702,6 @@
 		return result;
 	}
 
-	/**
-	 * 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.
 	 * 
@@ -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
@@ -958,34 +864,6 @@
 		return result;
 	}
 	
-	/**
-	 * 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
 	 * 
@@ -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

--
Gitblit v1.9.1