James Moger
2011-06-03 4ab184198bd7eac67eb767cf2e19423f618a70ae
GitNotes.  Unit testing.  More correct refs.
23 files modified
852 ■■■■■ changed files
src/com/gitblit/models/RefModel.java 79 ●●●● patch | view | raw | blame | history
src/com/gitblit/utils/JGitUtils.java 98 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/MetricUtils.java 2 ●●● patch | view | raw | blame | history
src/com/gitblit/utils/TicgitUtils.java 11 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp.properties 3 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/BlobPage.java 161 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/CommitPage.html 12 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/CommitPage.java 14 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/MarkdownPage.java 2 ●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/RawPage.java 92 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/SummaryPage.java 2 ●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/TagPage.html 6 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/TagPage.java 58 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/CommitHeaderPanel.java 8 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/HistoryPanel.java 3 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/LogPanel.java 3 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/RefsPanel.java 53 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/SearchPanel.java 3 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/TagsPanel.html 9 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/TagsPanel.java 97 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/resources/gitblit.css 27 ●●●●● patch | view | raw | blame | history
tests/com/gitblit/tests/GitBlitSuite.java 40 ●●●● patch | view | raw | blame | history
tests/com/gitblit/tests/JGitUtilsTest.java 69 ●●●● patch | view | raw | blame | history
src/com/gitblit/models/RefModel.java
@@ -19,50 +19,99 @@
import java.util.Date;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
public class RefModel implements Serializable, Comparable<RefModel> {
    private static final long serialVersionUID = 1L;
    public final String displayName;
    public final RevCommit commit;
    public transient Ref ref;
    public final RevObject referencedObject;
    public transient Ref reference;
    public RefModel(String displayName, Ref ref, RevCommit commit) {
    public RefModel(String displayName, Ref ref, RevObject refObject) {
        this.displayName = displayName;
        this.ref = ref;
        this.commit = commit;
        this.reference = ref;
        this.referencedObject = refObject;
    }
    public Date getDate() {
        return new Date(commit.getCommitTime() * 1000L);
        Date date = new Date(0);
        if (referencedObject != null) {
            if (referencedObject instanceof RevTag) {
                date = ((RevTag) referencedObject).getTaggerIdent().getWhen();
            } else if (referencedObject instanceof RevCommit) {
                date = ((RevCommit) referencedObject).getCommitterIdent().getWhen();
            }
        }
        return date;
    }
    public String getName() {
        return ref.getName();
        return reference.getName();
    }
    public ObjectId getCommitId() {
        return commit.getId();
    public int getReferencedObjectType() {
        int type = referencedObject.getType();
        if (referencedObject instanceof RevTag) {
            type = ((RevTag) referencedObject).getObject().getType();
        }
        return type;
    }
    public String getShortLog() {
        return commit.getShortMessage();
    public ObjectId getReferencedObjectId() {
        if (referencedObject instanceof RevTag) {
            return ((RevTag) referencedObject).getObject().getId();
        }
        return referencedObject.getId();
    }
    public String getShortMessage() {
        String message = "";
        if (referencedObject instanceof RevTag) {
            message = ((RevTag) referencedObject).getShortMessage();
        } else if (referencedObject instanceof RevCommit) {
            message = ((RevCommit) referencedObject).getShortMessage();
        }
        return message;
    }
    public String getFullMessage() {
        String message = "";
        if (referencedObject instanceof RevTag) {
            message = ((RevTag) referencedObject).getFullMessage();
        } else if (referencedObject instanceof RevCommit) {
            message = ((RevCommit) referencedObject).getFullMessage();
        }
        return message;
    }
    public PersonIdent getAuthorIdent() {
        if (referencedObject instanceof RevTag) {
            return ((RevTag) referencedObject).getTaggerIdent();
        } else if (referencedObject instanceof RevCommit) {
            return ((RevCommit) referencedObject).getAuthorIdent();
        }
        return null;
    }
    public ObjectId getObjectId() {
        return ref.getObjectId();
        return reference.getObjectId();
    }
    public boolean isAnnotatedTag() {
        // ref.isPeeled() ??
        return !getCommitId().equals(getObjectId());
        if (referencedObject instanceof RevTag) {
            return !getReferencedObjectId().equals(getObjectId());
        }
        return reference.getPeeledObjectId() != null;
    }
    @Override
    public int hashCode() {
        return getCommitId().hashCode() + getName().hashCode();
        return getReferencedObjectId().hashCode() + getName().hashCode();
    }
    @Override
src/com/gitblit/utils/JGitUtils.java
@@ -29,7 +29,6 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -42,7 +41,6 @@
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.StopWalkException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
@@ -212,41 +210,31 @@
        return commit;
    }
    public static Map<ObjectId, List<String>> getAllRefs(Repository r) {
        Map<ObjectId, List<String>> refs = new HashMap<ObjectId, List<String>>();
        Map<AnyObjectId, Set<Ref>> allRefs = r.getAllRefsByPeeledObjectId();
        for (Entry<AnyObjectId, Set<Ref>> setRefs : allRefs.entrySet()) {
            List<String> list = new ArrayList<String>();
            for (Ref setRef : setRefs.getValue()) {
                String name = setRef.getName();
                list.add(name);
    public static Map<ObjectId, List<RefModel>> getAllRefs(Repository r) {
        List<RefModel> list = getRefs(r, org.eclipse.jgit.lib.RefDatabase.ALL, -1);
        Map<ObjectId, List<RefModel>> refs = new HashMap<ObjectId, List<RefModel>>();
        for (RefModel ref : list) {
            ObjectId objectid = ref.getReferencedObjectId();
            if (!refs.containsKey(objectid)) {
                refs.put(objectid, new ArrayList<RefModel>());
            }
            refs.put(setRefs.getKey().toObjectId(), list);
        }
            refs.get(objectid).add(ref);
        }
        return refs;
    }
    /**
     * Lookup an entry stored in a tree, failing if not present.
     *
     * @param tree
     *            the tree to search.
     * @param path
     *            the path to find the entry of.
     * @return the parsed object entry at this path
     * @throws Exception
     */
    public static byte[] getRawContent(Repository r, RevCommit commit, final String path) {
    public static byte[] getByteContent(Repository r, RevTree tree, final String path) {
        RevWalk rw = new RevWalk(r);
        TreeWalk tw = new TreeWalk(r);
        tw.setFilter(PathFilterGroup.createFromStrings(Collections.singleton(path)));
        byte[] content = null;
        try {
            if (commit == null) {
            if (tree == null) {
                ObjectId object = r.resolve(Constants.HEAD);
                commit = rw.parseCommit(object);
                RevCommit commit = rw.parseCommit(object);
                tree = commit.getTree();
            }
            tw.reset(commit.getTree());
            tw.reset(tree);
            while (tw.next()) {
                if (tw.isSubtree() && !path.equals(tw.getPathString())) {
                    tw.enterSubtree();
@@ -268,7 +256,7 @@
                content = os.toByteArray();
            }
        } catch (Throwable t) {
            LOGGER.error("Can't find " + path + " in tree " + commit.getTree().name(), t);
            LOGGER.error("Can't find " + path + " in tree " + tree.name(), t);
        } finally {
            rw.dispose();
            tw.release();
@@ -276,8 +264,40 @@
        return content;
    }
    public static String getRawContentAsString(Repository r, RevCommit commit, String blobPath) {
        byte[] content = getRawContent(r, commit, blobPath);
    public static String getStringContent(Repository r, RevTree tree, String blobPath) {
        byte[] content = getByteContent(r, tree, blobPath);
        if (content == null) {
            return null;
        }
        return new String(content, Charset.forName(Constants.CHARACTER_ENCODING));
    }
    public static byte[] getByteContent(Repository r, String objectId) {
        RevWalk rw = new RevWalk(r);
        byte[] content = null;
        try {
            RevBlob blob = rw.lookupBlob(ObjectId.fromString(objectId));
            rw.parseBody(blob);
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ObjectLoader ldr = r.open(blob.getId(), Constants.OBJ_BLOB);
            byte[] tmp = new byte[4096];
            InputStream in = ldr.openStream();
            int n;
            while ((n = in.read(tmp)) > 0) {
                os.write(tmp, 0, n);
            }
            in.close();
            content = os.toByteArray();
        } catch (Throwable t) {
            LOGGER.error("Can't find blob " + objectId, t);
        } finally {
            rw.dispose();
        }
        return content;
    }
    public static String getStringContent(Repository r, String objectId) {
        byte[] content = getByteContent(r, objectId);
        if (content == null) {
            return null;
        }
@@ -605,7 +625,7 @@
        return getRefs(r, Constants.R_REMOTES, maxCount);
    }
    public static List<RefModel> getNotes(Repository r, int maxCount) {
    public static List<RefModel> getNotesRefs(Repository r, int maxCount) {
        return getRefs(r, Constants.R_NOTES, maxCount);
    }
@@ -613,11 +633,13 @@
        List<RefModel> list = new ArrayList<RefModel>();
        try {
            Map<String, Ref> map = r.getRefDatabase().getRefs(refs);
            RevWalk rw = new RevWalk(r);
            for (Entry<String, Ref> entry : map.entrySet()) {
                Ref ref = entry.getValue();
                RevCommit commit = getCommit(r, ref.getObjectId().getName());
                list.add(new RefModel(entry.getKey(), ref, commit));
                RevObject object = rw.parseAny(ref.getObjectId());
                list.add(new RefModel(entry.getKey(), ref, object));
            }
            rw.dispose();
            Collections.sort(list);
            Collections.reverse(list);
            if (maxCount > 0 && list.size() > maxCount) {
@@ -631,14 +653,18 @@
    public static List<GitNote> getNotesOnCommit(Repository repository, RevCommit commit) {
        List<GitNote> list = new ArrayList<GitNote>();
        List<RefModel> notesRefs = getNotes(repository, -1);
        List<RefModel> notesRefs = getNotesRefs(repository, -1);
        for (RefModel notesRef : notesRefs) {
            RevCommit notes = JGitUtils.getCommit(repository, notesRef.getName());
            RevTree notesTree = JGitUtils.getCommit(repository, notesRef.getName()).getTree();
            StringBuilder sb = new StringBuilder(commit.getName());
            sb.insert(2, '/');
            String text = getRawContentAsString(repository, notes, sb.toString());
            String notePath = sb.toString();
            String text = getStringContent(repository, notesTree, notePath);
            if (!StringUtils.isEmpty(text)) {
                GitNote gitNote = new GitNote(notesRef, text);
                List<RevCommit> history = getRevLog(repository, notesRef.getName(), notePath, 0, -1);
                RefModel noteRef = new RefModel(notesRef.displayName, null, history.get(history
                        .size() - 1));
                GitNote gitNote = new GitNote(noteRef, text);
                list.add(gitNote);
            }
        }
src/com/gitblit/utils/MetricUtils.java
@@ -47,7 +47,7 @@
            final List<RefModel> tags = JGitUtils.getTags(r, -1);
            final Map<ObjectId, RefModel> tagMap = new HashMap<ObjectId, RefModel>();
            for (RefModel tag : tags) {
                tagMap.put(tag.getCommitId(), tag);
                tagMap.put(tag.getReferencedObjectId(), tag);
            }
            try {
                RevWalk walk = new RevWalk(r);
src/com/gitblit/utils/TicgitUtils.java
@@ -21,6 +21,7 @@
import java.util.List;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -64,7 +65,8 @@
        if (ticgitBranch == null) {
            return null;
        }
        List<PathModel> paths = JGitUtils.getFilesInPath(r, null, ticgitBranch.commit);
        RevCommit commit = (RevCommit) ticgitBranch.referencedObject;
        List<PathModel> paths = JGitUtils.getFilesInPath(r, null, commit);
        List<TicketModel> tickets = new ArrayList<TicketModel>();
        for (PathModel ticketFolder : paths) {
            if (ticketFolder.isTree()) {
@@ -97,11 +99,10 @@
    }
    private static void readTicketContents(Repository r, RefModel ticketsBranch, TicketModel ticket) {
        List<PathModel> ticketFiles = JGitUtils
                .getFilesInPath(r, ticket.name, ticketsBranch.commit);
        RevCommit commit = (RevCommit) ticketsBranch.referencedObject;
        List<PathModel> ticketFiles = JGitUtils.getFilesInPath(r, ticket.name, commit);
        for (PathModel file : ticketFiles) {
            String content = JGitUtils.getRawContentAsString(r, ticketsBranch.commit, file.path)
                    .trim();
            String content = JGitUtils.getStringContent(r, commit.getTree(), file.path).trim();
            if (file.name.equals("TICKET_ID")) {
                ticket.id = content;
            } else if (file.name.equals("TITLE")) {
src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -93,4 +93,5 @@
gb.showReadme = show readme
gb.showReadmeDescription = show a \"readme\" markdown file on the summary page
gb.nameDescription = use '/' to group repositories.  e.g. libraries/mycoollib.git
gb.ownerDescription = the owner may edit repository settings
gb.ownerDescription = the owner may edit repository settings
gb.blob = blob
src/com/gitblit/wicket/pages/BlobPage.java
@@ -29,6 +29,7 @@
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.CommitHeaderPanel;
import com.gitblit.wicket.panels.PathBreadcrumbsPanel;
@@ -38,82 +39,100 @@
    public BlobPage(PageParameters params) {
        super(params);
        Repository r = getRepository();
        final String blobPath = WicketUtils.getPath(params);
        String extension = null;
        if (blobPath.lastIndexOf('.') > -1) {
            extension = blobPath.substring(blobPath.lastIndexOf('.') + 1).toLowerCase();
        }
        if (StringUtils.isEmpty(blobPath)) {
            // blob by objectid
        // see if we should redirect to the markdown page
        for (String ext : GitBlit.getStrings(Keys.web.markdownExtensions)) {
            if (ext.equals(extension)) {
                setResponsePage(MarkdownPage.class, params);
                return;
            }
        }
        // standard blob view
        Repository r = getRepository();
        RevCommit commit = getCommit();
        // blob page links
        add(new Label("blameLink", getString("gb.blame")));
        add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
                WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
        add(new BookmarkablePageLink<Void>("rawLink", RawPage.class, WicketUtils.newPathParameter(
                repositoryName, objectId, blobPath)));
        add(new BookmarkablePageLink<Void>("headLink", BlobPage.class,
                WicketUtils.newPathParameter(repositoryName, Constants.HEAD, blobPath)));
        add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
        add(new PathBreadcrumbsPanel("breadcrumbs", repositoryName, blobPath, objectId));
        // Map the extensions to types
        Map<String, Integer> map = new HashMap<String, Integer>();
        for (String ext : GitBlit.getStrings(Keys.web.prettyPrintExtensions)) {
            map.put(ext.toLowerCase(), 1);
        }
        for (String ext : GitBlit.getStrings(Keys.web.imageExtensions)) {
            map.put(ext.toLowerCase(), 2);
        }
        for (String ext : GitBlit.getStrings(Keys.web.binaryExtensions)) {
            map.put(ext.toLowerCase(), 3);
        }
        if (extension != null) {
            int type = 0;
            if (map.containsKey(extension)) {
                type = map.get(extension);
            }
            Component c = null;
            switch (type) {
            case 1:
                // PrettyPrint blob text
                c = new Label("blobText", JGitUtils.getRawContentAsString(r, commit, blobPath));
                WicketUtils.setCssClass(c, "prettyprint linenums");
                break;
            case 2:
                // TODO image blobs
                c = new Label("blobText", "Image File");
                break;
            case 3:
                // TODO binary blobs
                c = new Label("blobText", "Binary File");
                break;
            default:
                // plain text
                c = new Label("blobText", JGitUtils.getRawContentAsString(r, commit, blobPath));
                WicketUtils.setCssClass(c, "plainprint");
            }
            add(new Label("blameLink", getString("gb.blame")).setEnabled(false));
            add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class).setEnabled(false));
            add(new BookmarkablePageLink<Void>("rawLink", RawPage.class,
                    WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
            add(new BookmarkablePageLink<Void>("headLink", BlobPage.class).setEnabled(false));
            add(new CommitHeaderPanel("commitHeader", objectId));
            add(new PathBreadcrumbsPanel("breadcrumbs", repositoryName, blobPath, objectId));
            Component c = new Label("blobText", JGitUtils.getStringContent(r, objectId));
            WicketUtils.setCssClass(c, "plainprint");
            add(c);
        } else {
            // plain text
            Label blobLabel = new Label("blobText", JGitUtils.getRawContentAsString(r, commit,
                    blobPath));
            WicketUtils.setCssClass(blobLabel, "plainprint");
            add(blobLabel);
            // standard blob view
            String extension = null;
            if (blobPath.lastIndexOf('.') > -1) {
                extension = blobPath.substring(blobPath.lastIndexOf('.') + 1).toLowerCase();
            }
            // see if we should redirect to the markdown page
            for (String ext : GitBlit.getStrings(Keys.web.markdownExtensions)) {
                if (ext.equals(extension)) {
                    setResponsePage(MarkdownPage.class, params);
                    return;
                }
            }
            // manually get commit because it can be null
            RevCommit commit = JGitUtils.getCommit(r, objectId);
            // blob page links
            add(new Label("blameLink", getString("gb.blame")));
            add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
                    WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
            add(new BookmarkablePageLink<Void>("rawLink", RawPage.class,
                    WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
            add(new BookmarkablePageLink<Void>("headLink", BlobPage.class,
                    WicketUtils.newPathParameter(repositoryName, Constants.HEAD, blobPath)));
            add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
            add(new PathBreadcrumbsPanel("breadcrumbs", repositoryName, blobPath, objectId));
            // Map the extensions to types
            Map<String, Integer> map = new HashMap<String, Integer>();
            for (String ext : GitBlit.getStrings(Keys.web.prettyPrintExtensions)) {
                map.put(ext.toLowerCase(), 1);
            }
            for (String ext : GitBlit.getStrings(Keys.web.imageExtensions)) {
                map.put(ext.toLowerCase(), 2);
            }
            for (String ext : GitBlit.getStrings(Keys.web.binaryExtensions)) {
                map.put(ext.toLowerCase(), 3);
            }
            if (extension != null) {
                int type = 0;
                if (map.containsKey(extension)) {
                    type = map.get(extension);
                }
                Component c = null;
                switch (type) {
                case 1:
                    // PrettyPrint blob text
                    c = new Label("blobText", JGitUtils.getStringContent(r, commit.getTree(),
                            blobPath));
                    WicketUtils.setCssClass(c, "prettyprint linenums");
                    break;
                case 2:
                    // TODO image blobs
                    c = new Label("blobText", "Image File");
                    break;
                case 3:
                    // TODO binary blobs
                    c = new Label("blobText", "Binary File");
                    break;
                default:
                    // plain text
                    c = new Label("blobText", JGitUtils.getStringContent(r, commit.getTree(),
                            blobPath));
                    WicketUtils.setCssClass(c, "plainprint");
                }
                add(c);
            } else {
                // plain text
                Label blobLabel = new Label("blobText", JGitUtils.getStringContent(r,
                        commit.getTree(), blobPath));
                WicketUtils.setCssClass(blobLabel, "plainprint");
                add(blobLabel);
            }
        }
    }
src/com/gitblit/wicket/pages/CommitPage.html
@@ -45,10 +45,16 @@
    <div class="commit_message" wicket:id="fullMessage">[commit message]</div>
    <!--  git notes -->
    <table style="padding-bottom:5px;">
    <table class="gitnotes">
        <tr wicket:id="notes">
            <td style="vertical-align:top;"><span class="headRef" wicket:id="refName"></span><br/><span wicket:id="authorName"></span><br/><span wicket:id="authorDate"></span></td>
            <td><span wicket:id="noteContent"></span></td>
            <td class="info">
                <table>
                    <tr><td><span wicket:id="refName"></span></td></tr>
                    <tr><td><span class="sha1" wicket:id="authorName"></span></td></tr>
                    <tr><td><span class="sha1" wicket:id="authorDate"></span></td></tr>
                </table>
            </td>
            <td class="message"><span class="sha1" wicket:id="noteContent"></span></td>
        </tr>
    </table>
    
src/com/gitblit/wicket/pages/CommitPage.java
@@ -16,9 +16,9 @@
package com.gitblit.wicket.pages;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.wicket.Component;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
@@ -37,12 +37,13 @@
import com.gitblit.models.GitNote;
import com.gitblit.models.PathModel.PathChangeModel;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.JGitUtils.SearchType;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.CommitHeaderPanel;
import com.gitblit.wicket.panels.CommitLegendPanel;
import com.gitblit.wicket.panels.LinkPanel;
import com.gitblit.wicket.panels.RefsPanel;
public class CommitPage extends RepositoryPage {
@@ -123,13 +124,10 @@
            public void populateItem(final Item<GitNote> item) {
                GitNote entry = item.getModelObject();
                Component c = new LinkPanel("refName", null, entry.notesRef.displayName,
                        CommitPage.class, newCommitParameter(entry.notesRef.commit.getName()));
                WicketUtils.setCssClass(c, "headRef");
                item.add(c);
                item.add(createPersonPanel("authorName", entry.notesRef.commit.getAuthorIdent(), SearchType.AUTHOR));
                item.add(new RefsPanel("refName", repositoryName, Arrays.asList(entry.notesRef)));
                item.add(createPersonPanel("authorName", entry.notesRef.getAuthorIdent(), SearchType.AUTHOR));
                item.add(WicketUtils.createTimestampLabel("authorDate",
                        entry.notesRef.commit.getAuthorIdent().getWhen(), getTimeZone()));
                        entry.notesRef.getAuthorIdent().getWhen(), getTimeZone()));
                item.add(new Label("noteContent", StringUtils.breakLinesForHtml(entry.content)).setEscapeModelStrings(false));
            }
        };
src/com/gitblit/wicket/pages/MarkdownPage.java
@@ -48,7 +48,7 @@
                WicketUtils.newPathParameter(repositoryName, Constants.HEAD, markdownPath)));
        // Read raw markdown content and transform it to html
        String markdownText = JGitUtils.getRawContentAsString(r, commit, markdownPath);
        String markdownText = JGitUtils.getStringContent(r, commit.getTree(), markdownPath);
        String htmlText;
        try {
            htmlText = MarkdownUtils.transformMarkdown(markdownText);
src/com/gitblit/wicket/pages/RawPage.java
@@ -28,6 +28,7 @@
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.WicketUtils;
public class RawPage extends WebPage {
@@ -50,49 +51,58 @@
            return;
        }
        RevCommit commit = JGitUtils.getCommit(r, objectId);
        String extension = null;
        if (blobPath.lastIndexOf('.') > -1) {
            extension = blobPath.substring(blobPath.lastIndexOf('.') + 1);
        }
        // Map the extensions to types
        Map<String, Integer> map = new HashMap<String, Integer>();
        for (String ext : GitBlit.getStrings(Keys.web.imageExtensions)) {
            map.put(ext.toLowerCase(), 2);
        }
        for (String ext : GitBlit.getStrings(Keys.web.binaryExtensions)) {
            map.put(ext.toLowerCase(), 3);
        }
        if (extension != null) {
            int type = 0;
            if (map.containsKey(extension)) {
                type = map.get(extension);
            }
            Component c = null;
            switch (type) {
            case 2:
                // TODO image blobs
                c = new Label("rawText", "Image File");
                break;
            case 3:
                // TODO binary blobs
                c = new Label("rawText", "Binary File");
                break;
            default:
                // plain text
                c = new Label("rawText", JGitUtils.getRawContentAsString(r, commit, blobPath));
                WicketUtils.setCssClass(c, "plainprint");
            }
            add(c);
        } else {
            // plain text
            Label blobLabel = new Label("rawText", JGitUtils.getRawContentAsString(r, commit,
                    blobPath));
        if (StringUtils.isEmpty(blobPath)) {
            // objectid referenced raw view
            Label blobLabel = new Label("rawText", JGitUtils.getStringContent(r, objectId));
            WicketUtils.setCssClass(blobLabel, "plainprint");
            add(blobLabel);
        } else {
            // standard raw blob view
            RevCommit commit = JGitUtils.getCommit(r, objectId);
            String extension = null;
            if (blobPath.lastIndexOf('.') > -1) {
                extension = blobPath.substring(blobPath.lastIndexOf('.') + 1);
            }
            // Map the extensions to types
            Map<String, Integer> map = new HashMap<String, Integer>();
            for (String ext : GitBlit.getStrings(Keys.web.imageExtensions)) {
                map.put(ext.toLowerCase(), 2);
            }
            for (String ext : GitBlit.getStrings(Keys.web.binaryExtensions)) {
                map.put(ext.toLowerCase(), 3);
            }
            if (extension != null) {
                int type = 0;
                if (map.containsKey(extension)) {
                    type = map.get(extension);
                }
                Component c = null;
                switch (type) {
                case 2:
                    // TODO image blobs
                    c = new Label("rawText", "Image File");
                    break;
                case 3:
                    // TODO binary blobs
                    c = new Label("rawText", "Binary File");
                    break;
                default:
                    // plain text
                    c = new Label("rawText", JGitUtils.getStringContent(r, commit.getTree(),
                            blobPath));
                    WicketUtils.setCssClass(c, "plainprint");
                }
                add(c);
            } else {
                // plain text
                Label blobLabel = new Label("rawText", JGitUtils.getStringContent(r,
                        commit.getTree(), blobPath));
                WicketUtils.setCssClass(blobLabel, "plainprint");
                add(blobLabel);
            }
        }
        r.close();
    }
src/com/gitblit/wicket/pages/SummaryPage.java
@@ -167,7 +167,7 @@
                    }
                }
                if (!StringUtils.isEmpty(readme)) {
                    String markdownText = JGitUtils.getRawContentAsString(r, head, readme);
                    String markdownText = JGitUtils.getStringContent(r, head.getTree(), readme);
                    htmlText = MarkdownUtils.transformMarkdown(markdownText);
                }
            } catch (ParseException p) {
src/com/gitblit/wicket/pages/TagPage.html
@@ -12,8 +12,10 @@
    
    <!-- commit info -->
    <table class="plain">
        <tr><th><wicket:message key="gb.object">[object]</wicket:message></th><td><span class="sha1" wicket:id="tagId">[tag id]</span></td></tr>
        <tr><th><wicket:message key="gb.tagger">[tagger]</wicket:message></th><td><span class="sha1" wicket:id="tagAuthor">[tag author]</span></td></tr>
        <tr><th><wicket:message key="gb.name">[name]</wicket:message></th><td><span class="tagRef" wicket:id="tagName">[tag name]</span></td></tr>
        <tr><th><wicket:message key="gb.tag">[tag]</wicket:message></th><td><span class="sha1" wicket:id="tagId">[tag id]</span></td></tr>
        <tr><th><wicket:message key="gb.object">[object]</wicket:message></th><td><span class="sha1" wicket:id="taggedObject">[tagged object]</span> <span class="link" wicket:id="taggedObjectType"></span></td></tr>
        <tr><th><wicket:message key="gb.tagger">[tagger]</wicket:message></th><td><span class="sha1" wicket:id="tagger">[tagger]</span></td></tr>
        <tr><th></th><td><span class="sha1" wicket:id="tagDate">[tag date]</span></td></tr>
    </table>
    
src/com/gitblit/wicket/pages/TagPage.java
@@ -15,17 +15,21 @@
 */
package com.gitblit.wicket.pages;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.models.RefModel;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.JGitUtils.SearchType;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.LinkPanel;
import com.gitblit.wicket.panels.RefsPanel;
public class TagPage extends RepositoryPage {
@@ -33,11 +37,10 @@
        super(params);
        Repository r = getRepository();
        RevCommit c = getCommit();
        List<RefModel> tags = JGitUtils.getTags(r, -1);
        // Find tag in repository
        List<RefModel> tags = JGitUtils.getTags(r, -1);
        RefModel tagRef = null;
        // determine tag
        for (RefModel tag : tags) {
            if (tag.getName().equals(objectId) || tag.getObjectId().getName().equals(objectId)) {
                tagRef = tag;
@@ -45,25 +48,42 @@
            }
        }
        // Failed to find tag!
        if (tagRef == null) {
            // point to commit
            add(new LinkPanel("commit", "title", c.getShortMessage(), CommitPage.class,
                    newCommitParameter()));
            add(new LinkPanel("tagId", "list", c.getName(), CommitPage.class,
                    newCommitParameter(c.getName())));
        } else {
            // TODO commit or tree or blob?
            add(new LinkPanel("commit", "title", tagRef.displayName, CommitPage.class,
                    newCommitParameter()));
            add(new LinkPanel("tagId", "list", c.getName(), CommitPage.class,
                    newCommitParameter(c.getName())));
            error(MessageFormat.format("Could not find tag {0}", objectId), true);
        }
        add(createPersonPanel("tagAuthor", c.getAuthorIdent(), SearchType.AUTHOR));
        add(WicketUtils
                .createTimestampLabel("tagDate", c.getAuthorIdent().getWhen(), getTimeZone()));
        // Display tag.
        Class<? extends RepositoryPage> linkClass;
        PageParameters linkParameters = newCommitParameter(tagRef.getReferencedObjectId().getName());
        String typeKey;
        switch (tagRef.getReferencedObjectType()) {
        case Constants.OBJ_BLOB:
            typeKey = "gb.blob";
            linkClass = BlobPage.class;
            break;
        case Constants.OBJ_TREE:
            typeKey = "gb.tree";
            linkClass = TreePage.class;
            break;
        case Constants.OBJ_COMMIT:
        default:
            typeKey = "gb.commit";
            linkClass = CommitPage.class;
            break;
        }
        add(new LinkPanel("commit", "title", tagRef.displayName, linkClass, linkParameters));
        add(new RefsPanel("tagName", repositoryName, Arrays.asList(tagRef)));
        add(new Label("tagId", tagRef.getObjectId().getName()));
        add(new LinkPanel("taggedObject", "list", tagRef.getReferencedObjectId().getName(),
                linkClass, linkParameters));
        add(new Label("taggedObjectType", getString(typeKey)));
        addFullText("fullMessage", c.getFullMessage(), true);
        add(createPersonPanel("tagger", tagRef.getAuthorIdent(), SearchType.AUTHOR));
        add(WicketUtils.createTimestampLabel("tagDate", tagRef.getAuthorIdent().getWhen(),
                getTimeZone()));
        addFullText("fullMessage", tagRef.getFullMessage(), true);
    }
    @Override
src/com/gitblit/wicket/panels/CommitHeaderPanel.java
@@ -25,6 +25,14 @@
    private static final long serialVersionUID = 1L;
    public CommitHeaderPanel(String id, String title) {
        super(id);
        add(new Label("shortmessage", title));
        add(new Label("commitid"));
        add(new Label("author"));
        add(new Label("date"));
    }
    public CommitHeaderPanel(String id, String repositoryName, RevCommit c) {
        super(id);
        add(new LinkPanel("shortmessage", "title", c.getShortMessage(), CommitPage.class,
src/com/gitblit/wicket/panels/HistoryPanel.java
@@ -34,6 +34,7 @@
import com.gitblit.Keys;
import com.gitblit.models.PathModel;
import com.gitblit.models.PathModel.PathChangeModel;
import com.gitblit.models.RefModel;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.JGitUtils.SearchType;
import com.gitblit.utils.StringUtils;
@@ -73,7 +74,7 @@
        }
        final boolean isTree = matchingPath == null ? true : matchingPath.isTree();
        final Map<ObjectId, List<String>> allRefs = JGitUtils.getAllRefs(r);
        final Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(r);
        List<RevCommit> commits;
        if (pageResults) {
            // Paging result set
src/com/gitblit/wicket/panels/LogPanel.java
@@ -31,6 +31,7 @@
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.models.RefModel;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.JGitUtils.SearchType;
import com.gitblit.utils.StringUtils;
@@ -57,7 +58,7 @@
            itemsPerPage = 50;
        }
        final Map<ObjectId, List<String>> allRefs = JGitUtils.getAllRefs(r);
        final Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(r);
        List<RevCommit> commits;
        if (pageResults) {
            // Paging result set
src/com/gitblit/wicket/panels/RefsPanel.java
@@ -29,6 +29,7 @@
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.models.RefModel;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.pages.CommitPage;
import com.gitblit.wicket.pages.LogPage;
@@ -39,45 +40,55 @@
    private static final long serialVersionUID = 1L;
    public RefsPanel(String id, final String repositoryName, RevCommit c,
            Map<ObjectId, List<String>> refs) {
            Map<ObjectId, List<RefModel>> refs) {
        this(id, repositoryName, refs.get(c.getId()));
    }
    public RefsPanel(String id, final String repositoryName, List<RefModel> refs) {
        super(id);
        List<String> refNames = refs.get(c.getId());
        if (refNames == null) {
            refNames = new ArrayList<String>();
        if (refs == null) {
            refs = new ArrayList<RefModel>();
        }
        Collections.sort(refNames);
        Collections.sort(refs);
        // refNames.remove(Constants.HEAD);
        ListDataProvider<String> refsDp = new ListDataProvider<String>(refNames);
        DataView<String> refsView = new DataView<String>("ref", refsDp) {
        ListDataProvider<RefModel> refsDp = new ListDataProvider<RefModel>(refs);
        DataView<RefModel> refsView = new DataView<RefModel>("ref", refsDp) {
            private static final long serialVersionUID = 1L;
            public void populateItem(final Item<String> item) {
                String entry = item.getModelObject();
            public void populateItem(final Item<RefModel> item) {
                RefModel entry = item.getModelObject();
                String name = entry.displayName;
                String objectid = entry.getReferencedObjectId().getName();
                Component c = null;
                if (entry.startsWith(Constants.R_HEADS)) {
                if (name.startsWith(Constants.R_HEADS)) {
                    // local head
                    c = new LinkPanel("refName", null, entry.substring(Constants.R_HEADS.length()),
                            LogPage.class, WicketUtils.newObjectParameter(repositoryName, entry));
                    c = new LinkPanel("refName", null, name.substring(Constants.R_HEADS.length()),
                            LogPage.class, WicketUtils.newObjectParameter(repositoryName, objectid));
                    WicketUtils.setCssClass(c, "headRef");
                } else if (entry.startsWith(Constants.R_REMOTES)) {
                } else if (name.equals(Constants.HEAD)) {
                    // local head
                    c = new LinkPanel("refName", null, name, LogPage.class,
                            WicketUtils.newObjectParameter(repositoryName, objectid));
                    WicketUtils.setCssClass(c, "headRef");
                } else if (name.startsWith(Constants.R_REMOTES)) {
                    // remote head
                    c = new LinkPanel("refName", null,
                            entry.substring(Constants.R_REMOTES.length()), LogPage.class,
                            WicketUtils.newObjectParameter(repositoryName, entry));
                            name.substring(Constants.R_REMOTES.length()), LogPage.class,
                            WicketUtils.newObjectParameter(repositoryName, objectid));
                    WicketUtils.setCssClass(c, "remoteRef");
                } else if (entry.startsWith(Constants.R_TAGS)) {
                } else if (name.startsWith(Constants.R_TAGS)) {
                    // tag
                    c = new LinkPanel("refName", null, entry.substring(Constants.R_TAGS.length()),
                            TagPage.class, WicketUtils.newObjectParameter(repositoryName, entry));
                    c = new LinkPanel("refName", null, name.substring(Constants.R_TAGS.length()),
                            TagPage.class, WicketUtils.newObjectParameter(repositoryName, objectid));
                    WicketUtils.setCssClass(c, "tagRef");
                } else {
                    // other
                    c = new LinkPanel("refName", null, entry, CommitPage.class,
                            WicketUtils.newObjectParameter(repositoryName, entry));
                    c = new LinkPanel("refName", null, name, CommitPage.class,
                            WicketUtils.newObjectParameter(repositoryName, objectid));
                    WicketUtils.setCssClass(c, "otherRef");
                }
                WicketUtils.setHtmlTooltip(c, entry);
                WicketUtils.setHtmlTooltip(c, name);
                item.add(c);
            }
        };
src/com/gitblit/wicket/panels/SearchPanel.java
@@ -29,6 +29,7 @@
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.models.RefModel;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.JGitUtils.SearchType;
import com.gitblit.utils.StringUtils;
@@ -55,7 +56,7 @@
        RevCommit commit = JGitUtils.getCommit(r, objectId);
        final Map<ObjectId, List<String>> allRefs = JGitUtils.getAllRefs(r);
        final Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(r);
        List<RevCommit> commits;
        if (pageResults) {
            // Paging result set
src/com/gitblit/wicket/panels/TagsPanel.html
@@ -28,7 +28,7 @@
    <!--  annotated tag links -->
    <wicket:fragment wicket:id="annotatedLinks">
        <span class="link">
            <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="commit"><wicket:message key="gb.commit"></wicket:message></a> | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>
            <a wicket:id="tag"><wicket:message key="gb.tag"></wicket:message></a> | <a wicket:id="commit"><wicket:message key="gb.commit"></wicket:message></a> | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>
        </span>
    </wicket:fragment>
    
@@ -38,6 +38,13 @@
            <a wicket:id="commit"><wicket:message key="gb.commit"></wicket:message></a> | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>
        </span>
    </wicket:fragment>
    <!-- blob tag links -->
    <wicket:fragment wicket:id="blobLinks">
        <span class="link">
            <a wicket:id="tag"><wicket:message key="gb.tag"></wicket:message></a> | <a wicket:id="blob"><wicket:message key="gb.blob"></wicket:message></a> | <a wicket:id="raw"><wicket:message key="gb.raw"></wicket:message></a>
        </span>
    </wicket:fragment>
    
</wicket:panel>
</body>
src/com/gitblit/wicket/panels/TagsPanel.java
@@ -24,17 +24,22 @@
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import org.apache.wicket.model.StringResourceModel;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
import com.gitblit.models.RefModel;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.pages.BlobPage;
import com.gitblit.wicket.pages.CommitPage;
import com.gitblit.wicket.pages.LogPage;
import com.gitblit.wicket.pages.RawPage;
import com.gitblit.wicket.pages.RepositoryPage;
import com.gitblit.wicket.pages.SummaryPage;
import com.gitblit.wicket.pages.TagPage;
import com.gitblit.wicket.pages.TagsPage;
import com.gitblit.wicket.pages.TreePage;
public class TagsPanel extends BasePanel {
@@ -67,47 +72,83 @@
                item.add(WicketUtils.createDateLabel("tagDate", entry.getDate(), getTimeZone()));
                // tag icon
                if (entry.isAnnotatedTag()) {
                    item.add(WicketUtils.newImage("tagIcon", "tag_16x16.png"));
                } else {
                    item.add(WicketUtils.newBlankImage("tagIcon"));
                Class<? extends RepositoryPage> linkClass;
                switch (entry.getReferencedObjectType()) {
                case Constants.OBJ_BLOB:
                    linkClass = BlobPage.class;
                    break;
                case Constants.OBJ_TREE:
                    linkClass = TreePage.class;
                    break;
                case Constants.OBJ_COMMIT:
                default:
                    linkClass = CommitPage.class;
                    break;
                }
                item.add(new LinkPanel("tagName", "list name", entry.displayName, CommitPage.class,
                        WicketUtils.newObjectParameter(repositoryName, entry.getCommitId()
                                .getName())));
                item.add(new LinkPanel("tagName", "list name", entry.displayName, linkClass,
                        WicketUtils.newObjectParameter(repositoryName, entry
                                .getReferencedObjectId().getName())));
                String message;
                if (maxCount > 0) {
                    message = StringUtils.trimString(entry.getShortLog(), 40);
                    message = StringUtils.trimString(entry.getShortMessage(), 40);
                } else {
                    message = entry.getShortLog();
                    // workaround for RevTag returning a lengthy shortlog. :(
                    message = StringUtils.trimShortLog(entry.getShortMessage());
                }
                if (entry.isAnnotatedTag()) {
                if (linkClass.equals(BlobPage.class)) {
                    // Blob Tag Object
                    item.add(WicketUtils.newImage("tagIcon", "file_16x16.png"));
                    item.add(new LinkPanel("tagDescription", "list", message, TagPage.class,
                            WicketUtils.newObjectParameter(repositoryName, entry.getObjectId()
                                    .getName())));
                    Fragment fragment = new Fragment("tagLinks", "annotatedLinks", this);
                    fragment.add(new BookmarkablePageLink<Void>("view", TagPage.class, WicketUtils
                    Fragment fragment = new Fragment("tagLinks", "blobLinks", this);
                    fragment.add(new BookmarkablePageLink<Void>("tag", TagPage.class, WicketUtils
                            .newObjectParameter(repositoryName, entry.getObjectId().getName()))
                            .setEnabled(entry.isAnnotatedTag()));
                    fragment.add(new BookmarkablePageLink<Void>("commit", CommitPage.class,
                            WicketUtils.newObjectParameter(repositoryName, entry.getCommitId()
                    fragment.add(new BookmarkablePageLink<Void>("blob", linkClass, WicketUtils
                            .newObjectParameter(repositoryName, entry.getReferencedObjectId()
                                    .getName())));
                    fragment.add(new BookmarkablePageLink<Void>("log", LogPage.class, WicketUtils
                            .newObjectParameter(repositoryName, entry.getName())));
                    fragment.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils
                            .newObjectParameter(repositoryName, entry.getReferencedObjectId()
                                    .getName())));
                    item.add(fragment);
                } else {
                    item.add(new LinkPanel("tagDescription", "list", message, CommitPage.class,
                            WicketUtils.newObjectParameter(repositoryName, entry.getObjectId()
                                    .getName())));
                    Fragment fragment = new Fragment("tagLinks", "lightweightLinks", this);
                    fragment.add(new BookmarkablePageLink<Void>("commit", CommitPage.class,
                            WicketUtils.newObjectParameter(repositoryName, entry.getCommitId()
                                    .getName())));
                    fragment.add(new BookmarkablePageLink<Void>("log", LogPage.class, WicketUtils
                            .newObjectParameter(repositoryName, entry.getName())));
                    item.add(fragment);
                    // TODO Tree Tag Object
                    // Standard Tag Object
                    if (entry.isAnnotatedTag()) {
                        item.add(WicketUtils.newImage("tagIcon", "tag_16x16.png"));
                        item.add(new LinkPanel("tagDescription", "list", message, TagPage.class,
                                WicketUtils.newObjectParameter(repositoryName, entry.getObjectId()
                                        .getName())));
                        Fragment fragment = new Fragment("tagLinks", "annotatedLinks", this);
                        fragment.add(new BookmarkablePageLink<Void>("tag", TagPage.class,
                                WicketUtils.newObjectParameter(repositoryName, entry.getObjectId()
                                        .getName())).setEnabled(entry.isAnnotatedTag()));
                        fragment.add(new BookmarkablePageLink<Void>("commit", linkClass,
                                WicketUtils.newObjectParameter(repositoryName, entry
                                        .getReferencedObjectId().getName())));
                        fragment.add(new BookmarkablePageLink<Void>("log", LogPage.class,
                                WicketUtils.newObjectParameter(repositoryName, entry.getName())));
                        item.add(fragment);
                    } else {
                        item.add(WicketUtils.newBlankImage("tagIcon"));
                        item.add(new LinkPanel("tagDescription", "list", message, CommitPage.class,
                                WicketUtils.newObjectParameter(repositoryName, entry.getObjectId()
                                        .getName())));
                        Fragment fragment = new Fragment("tagLinks", "lightweightLinks", this);
                        fragment.add(new BookmarkablePageLink<Void>("commit", CommitPage.class,
                                WicketUtils.newObjectParameter(repositoryName, entry
                                        .getReferencedObjectId().getName())));
                        fragment.add(new BookmarkablePageLink<Void>("log", LogPage.class,
                                WicketUtils.newObjectParameter(repositoryName, entry.getName())));
                        item.add(fragment);
                    }
                }
                WicketUtils.setAlternatingBackground(item, counter);
src/com/gitblit/wicket/resources/gitblit.css
@@ -243,6 +243,7 @@
}
div.commit_message {
    font-family: monospace;
    padding: 8px;
    border: solid #bbb;
    border-width: 1px 0px 0px;
@@ -508,7 +509,7 @@
    border-right: 1px solid #bbb;    
}
table.pretty, table.comments, table.repositories {
table.pretty, table.comments, table.repositories, table.gitnotes {
    width:100%;
}
@@ -608,7 +609,29 @@
}
table.palette td.pane {
    padding: 0px;    
}
}
table.gitnotes {
    padding-bottom: 5px;
}
table.gitnotes td {
    border-top: 1px solid #ccc;
    padding-top: 3px;
    vertical-align:top;
}
table.gitnotes td table td {
    border: none;
    padding-top: 0px;
}
table.gitnotes td.info {
}
table.gitnotes td.message {
    width: 65%;
    border-left: 1px solid #ccc;
}
tr th a { padding-right: 15px; background-position: right; background-repeat:no-repeat; }
tr th.wicket_orderDown a {background-image: url(arrow_down.png); }
tests/com/gitblit/tests/GitBlitSuite.java
@@ -16,15 +16,19 @@
package com.gitblit.tests;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import junit.extensions.TestSetup;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.FetchCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.transport.RefSpec;
import com.gitblit.FileSettings;
import com.gitblit.GitBlit;
@@ -61,6 +65,14 @@
        return new FileRepository(new File(REPOSITORIES, "ticgit.git"));
    }
    public static Repository getJGitRepository() throws Exception {
        return new FileRepository(new File(REPOSITORIES, "nested/jgit.git"));
    }
    public static Repository getBluezGnomeRepository() throws Exception {
        return new FileRepository(new File(REPOSITORIES, "nested/bluez-gnome.git"));
    }
    @Override
    protected void setUp() throws Exception {
        FileSettings settings = new FileSettings("distrib/gitblit.properties");
@@ -71,12 +83,15 @@
        if (REPOSITORIES.exists() || REPOSITORIES.mkdirs()) {
            cloneOrFetch("helloworld.git", "https://github.com/git/hello-world.git", true);
            cloneOrFetch("nested/helloworld.git", "https://github.com/git/hello-world.git", true);
            cloneOrFetch("ticgit.git", "https://github.com/jeffWelling/ticgit.git", true);
            cloneOrFetch("nested/bluez-gnome.git", "https://git.kernel.org/pub/scm/bluetooth/bluez-gnome.git", true);
            cloneOrFetch("nested/jgit.git", "https://github.com/eclipse/jgit.git", true);
            cloneOrFetch("nested/helloworld.git", "https://github.com/git/hello-world.git", true);
            enableTickets("ticgit.git");
            enableDocs("ticgit.git");
            showRemoteBranches("ticgit.git");
            showRemoteBranches("nested/jgit.git");
        }
    }
@@ -84,22 +99,35 @@
        File folder = new File(REPOSITORIES, toFolder + (bare ? "" : "/.git"));
        if (folder.exists()) {
            System.out.print("Updating " + (bare ? "bare " : " ") + toFolder + "... ");
            FileRepository repository = new FileRepository(new File(REPOSITORIES, toFolder));
            Git git = new Git(repository);
            git.fetch().call();
            repository.close();
            fetch(toFolder);
            System.out.println("done.");
        } else {
            System.out.println("Cloning " + (bare ? "bare " : " ") + toFolder + "... ");
            CloneCommand clone = new CloneCommand();
            clone.setBare(bare);
            clone.setCloneAllBranches(true);
            clone.setCloneAllBranches(true);
            clone.setURI(fromUrl);
            clone.setDirectory(folder);
            clone.call();
            // Now we have to fetch because CloneCommand doesn't fetch
            // Notes nor does it allow manual RefSpec.
            fetch(toFolder);
            System.out.println("done.");
        }
    }
    private void fetch(String toFolder) throws Exception {
        FileRepository repository = new FileRepository(new File(REPOSITORIES, toFolder));
        Git git = new Git(repository);
        FetchCommand fetch = git.fetch();
        List<RefSpec> specs = new ArrayList<RefSpec>();
        specs.add(new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
        specs.add(new RefSpec("+refs/tags/*:refs/tags/*"));
        specs.add(new RefSpec("+refs/notes/*:refs/notes/*"));
        fetch.setRefSpecs(specs);
        fetch.call();
        repository.close();
    }
    private void enableTickets(String repositoryName) {
        try {
tests/com/gitblit/tests/JGitUtilsTest.java
@@ -34,11 +34,13 @@
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.models.GitNote;
import com.gitblit.models.PathModel;
import com.gitblit.models.PathModel.PathChangeModel;
import com.gitblit.models.RefModel;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.JGitUtils.SearchType;
import com.gitblit.utils.StringUtils;
public class JGitUtilsTest extends TestCase {
@@ -115,10 +117,24 @@
    }
    public void testRefs() throws Exception {
        Repository repository = GitBlitSuite.getTicgitRepository();
        Map<ObjectId, List<String>> map = JGitUtils.getAllRefs(repository);
        Repository repository = GitBlitSuite.getJGitRepository();
        Map<ObjectId, List<RefModel>> map = JGitUtils.getAllRefs(repository);
        repository.close();
        assertTrue(map.size() > 0);
        for (Map.Entry<ObjectId, List<RefModel>> entry : map.entrySet()) {
            List<RefModel> list = entry.getValue();
            for (RefModel ref : list) {
                if (ref.displayName.equals("refs/tags/spearce-gpg-pub")) {
                    assertTrue(ref.getObjectId().getName().equals("8bbde7aacf771a9afb6992434f1ae413e010c6d8"));
                    assertTrue(ref.getAuthorIdent().getEmailAddress().equals("spearce@spearce.org"));
                    assertTrue(ref.getShortMessage().startsWith("GPG key"));
                    assertTrue(ref.getFullMessage().startsWith("GPG key"));
                    assertTrue(ref.getReferencedObjectType() == Constants.OBJ_BLOB);
                } else if (ref.displayName.equals("refs/tags/v0.12.1")) {
                    assertTrue(ref.isAnnotatedTag());
                }
            }
        }
    }
    public void testBranches() throws Exception {
@@ -127,17 +143,17 @@
            assertTrue(model.getName().startsWith(Constants.R_HEADS));
            assertTrue(model.equals(model));
            assertFalse(model.equals(""));
            assertTrue(model.hashCode() == model.getCommitId().hashCode()
            assertTrue(model.hashCode() == model.getReferencedObjectId().hashCode()
                    + model.getName().hashCode());
            assertTrue(model.getShortLog().equals(model.commit.getShortMessage()));
            assertTrue(model.getShortMessage().equals(model.getShortMessage()));
        }
        for (RefModel model : JGitUtils.getRemoteBranches(repository, -1)) {
            assertTrue(model.getName().startsWith(Constants.R_REMOTES));
            assertTrue(model.equals(model));
            assertFalse(model.equals(""));
            assertTrue(model.hashCode() == model.getCommitId().hashCode()
            assertTrue(model.hashCode() == model.getReferencedObjectId().hashCode()
                    + model.getName().hashCode());
            assertTrue(model.getShortLog().equals(model.commit.getShortMessage()));
            assertTrue(model.getShortMessage().equals(model.getShortMessage()));
        }
        assertTrue(JGitUtils.getRemoteBranches(repository, 10).size() == 10);
        repository.close();
@@ -152,33 +168,52 @@
            assertTrue(model.getName().startsWith(Constants.R_TAGS));
            assertTrue(model.equals(model));
            assertFalse(model.equals(""));
            assertTrue(model.hashCode() == model.getCommitId().hashCode()
            assertTrue(model.hashCode() == model.getReferencedObjectId().hashCode()
                    + model.getName().hashCode());
            assertTrue(model.getShortLog().equals(model.commit.getShortMessage()));
        }
        repository.close();
        repository = GitBlitSuite.getBluezGnomeRepository();
        for (RefModel model : JGitUtils.getTags(repository, -1)) {
            if (model.getObjectId().getName().equals("728643ec0c438c77e182898c2f2967dbfdc231c8")) {
                assertFalse(model.isAnnotatedTag());
                assertTrue(model.getAuthorIdent().getEmailAddress().equals("marcel@holtmann.org"));
                assertTrue(model.getFullMessage().equals("Update changelog and bump version number\n"));
            }
        }
        repository.close();
    }
    public void testCommitNotes() throws Exception {
//        Repository repository = new FileRepository(new File("c:/projects/git/jgit.git/.git"));
//        RevCommit commit = JGitUtils.getCommit(repository,
//                "ada903085d1b4ef8c79e3e2d91f49fee7e188f53");
//        List<GitNote> list = JGitUtils.getNotesOnCommit(repository, commit);
//        repository.close();
//        assertTrue(list.size() > 0);
        Repository repository = GitBlitSuite.getJGitRepository();
        RevCommit commit = JGitUtils.getCommit(repository,
                "690c268c793bfc218982130fbfc25870f292295e");
        List<GitNote> list = JGitUtils.getNotesOnCommit(repository, commit);
        repository.close();
        assertTrue(list.size() > 0);
        assertTrue(list.get(0).notesRef.getReferencedObjectId().getName()
                .equals("183474d554e6f68478a02d9d7888b67a9338cdff"));
    }
    public void testStringContent() throws Exception {
        Repository repository = GitBlitSuite.getHelloworldRepository();
        String contentA = JGitUtils.getRawContentAsString(repository, null, "java.java");
        String contentA = JGitUtils.getStringContent(repository, null, "java.java");
        RevCommit commit = JGitUtils.getCommit(repository, Constants.HEAD);
        String contentB = JGitUtils.getRawContentAsString(repository, commit, "java.java");
        String contentC = JGitUtils.getRawContentAsString(repository, commit, "missing.txt");
        String contentB = JGitUtils.getStringContent(repository, commit.getTree(), "java.java");
        String contentC = JGitUtils.getStringContent(repository, commit.getTree(), "missing.txt");
        // manually construct a blob, calculate the hash, lookup the hash in git
        StringBuilder sb = new StringBuilder();
        sb.append("blob ").append(contentA.length()).append('\0');
        sb.append(contentA);
        String sha1 = StringUtils.getSHA1(sb.toString());
        String contentD = JGitUtils.getStringContent(repository, sha1);
        repository.close();
        assertTrue("ContentA is null!", contentA != null && contentA.length() > 0);
        assertTrue("ContentB is null!", contentB != null && contentB.length() > 0);
        assertTrue(contentA.equals(contentB));
        assertTrue(contentC == null);
        assertTrue(contentA.equals(contentD));
    }
    public void testFilesInCommit() throws Exception {