James Moger
2011-04-05 232890f03476b8bb231d17883eb0faff93ec5049
Simplified links some more.  Added TicGit browser.
2 files renamed
2 files deleted
7 files added
12 files modified
756 ■■■■ changed files
resources/gitblit.css 26 ●●●●● patch | view | raw | blame | history
src/com/gitblit/tests/JGitUtilsTest.java 11 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/JGitUtils.java 113 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/TicGitTicket.java 77 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/BasePage.java 10 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp.java 12 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/RepositoryPage.java 2 ●●● patch | view | raw | blame | history
src/com/gitblit/wicket/models/RefModel.java 10 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/BranchesPage.html 26 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/BranchesPage.java 71 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/HeadsPage.html 25 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/HeadsPage.java 63 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/SummaryPage.html 22 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/SummaryPage.java 46 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/TagsPage.java 10 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/TicGitPage.html 26 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/TicGitPage.java 65 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/TicGitTicketPage.html 39 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/TicGitTicketPage.java 70 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/BranchLinksPanel.html patch | view | raw | blame | history
src/com/gitblit/wicket/panels/BranchLinksPanel.java 8 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/PageLinksPanel.html 2 ●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/PageLinksPanel.java 22 ●●●●● patch | view | raw | blame | history
resources/gitblit.css
@@ -175,6 +175,32 @@
    border-width: 1px 0px 1px;
}
div.bug_open {
    padding: 2px;
    background-color: #800000;
    color: white;
    text-align: center;
}
div.bug_resolved {
    padding: 2px;
    background-color: #008000;
    color: white;
    text-align: center;
}
div.bug_invalid {
    padding: 2px;
    background-color: gray;
    text-align: center;
}
div.bug_hold {
    padding: 2px;
    background-color: orange;
    text-align: center;
}
a.list {
    text-decoration: none;
    color: #000000;
src/com/gitblit/tests/JGitUtilsTest.java
@@ -15,6 +15,8 @@
import org.eclipse.jgit.storage.file.FileRepository;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.TicGitTicket;
import com.gitblit.wicket.models.RefModel;
public class JGitUtilsTest extends TestCase {
@@ -67,5 +69,14 @@
        r.close();
        assertTrue("Content is null!", content != null);
    }
    public void testTicGit() throws Exception {
        Repository r = new FileRepository(new File(repositoriesFolder, "ticgit") + "/" + Constants.DOT_GIT);
        RefModel ticgit = JGitUtils.getTicGitBranch(r);
        assertTrue("Ticgit branch does not exist!", ticgit != null);
        List<TicGitTicket> tickets = JGitUtils.getTicGitTickets(r);
        assertTrue("No tickets found!", tickets.size() > 0);
        r.close();
    }
}
src/com/gitblit/utils/JGitUtils.java
@@ -6,6 +6,7 @@
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
@@ -37,6 +38,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.utils.TicGitTicket.Comment;
import com.gitblit.wicket.models.Metric;
import com.gitblit.wicket.models.PathModel;
import com.gitblit.wicket.models.RefModel;
@@ -333,8 +335,12 @@
        return getRefs(r, Constants.R_TAGS, maxCount);
    }
    public static List<RefModel> getHeads(Repository r, int maxCount) {
    public static List<RefModel> getLocalBranches(Repository r, int maxCount) {
        return getRefs(r, Constants.R_HEADS, maxCount);
    }
    public static List<RefModel> getRemoteBranches(Repository r, int maxCount) {
        return getRefs(r, Constants.R_REMOTES, maxCount);
    }
    public static List<RefModel> getRefs(Repository r, String refs, int maxCount) {
@@ -448,9 +454,110 @@
        List<String> keys = new ArrayList<String>(map.keySet());
        Collections.sort(keys);
        List<Metric> metrics = new ArrayList<Metric>();
        for (String key:keys) {
        for (String key : keys) {
            metrics.add(map.get(key));
        }
        }
        return metrics;
    }
    public static RefModel getTicGitBranch(Repository r) {
        RefModel ticgitBranch = null;
        try {
            // search for ticgit branch in local heads
            for (RefModel ref : getLocalBranches(r, -1)) {
                if (ref.getDisplayName().endsWith("ticgit") || ref.getDisplayName().endsWith("ticgit-ng")) {
                    ticgitBranch = ref;
                    break;
                }
            }
            // search for ticgit branch in remote heads
            if (ticgitBranch == null) {
                for (RefModel ref : getRemoteBranches(r, -1)) {
                    if (ref.getDisplayName().endsWith("ticgit") || ref.getDisplayName().endsWith("ticgit-ng")) {
                        ticgitBranch = ref;
                        break;
                    }
                }
            }
        } catch (Throwable t) {
            LOGGER.error("Failed to find ticgit branch!", t);
        }
        return ticgitBranch;
    }
    public static List<TicGitTicket> getTicGitTickets(Repository r) {
        RefModel ticgitBranch = getTicGitBranch(r);
        List<PathModel> paths = getFilesInPath(r, null, ticgitBranch.getCommit());
        List<TicGitTicket> tickets = new ArrayList<TicGitTicket>();
        for (PathModel ticketFolder : paths) {
            if (ticketFolder.isTree()) {
                try {
                    TicGitTicket t = new TicGitTicket(ticketFolder.name);
                    readTicketContents(r, ticgitBranch, t);
                    tickets.add(t);
                } catch (Throwable t) {
                    LOGGER.error("Failed to get a ticgit ticket!", t);
                }
            }
        }
        Collections.sort(tickets);
        Collections.reverse(tickets);
        return tickets;
    }
    public static TicGitTicket getTicGitTicket(Repository r, String ticketFolder) {
        RefModel ticgitBranch = getTicGitBranch(r);
        if (ticgitBranch != null) {
            try {
                TicGitTicket ticket = new TicGitTicket(ticketFolder);
                readTicketContents(r, ticgitBranch, ticket);
                return ticket;
            } catch (Throwable t) {
                LOGGER.error("Failed to get ticgit ticket " + ticketFolder, t);
            }
        }
        return null;
    }
    private static void readTicketContents(Repository r, RefModel ticgitBranch, TicGitTicket ticket) {
        List<PathModel> ticketFiles = getFilesInPath(r, ticket.name, ticgitBranch.getCommit());
        for (PathModel file : ticketFiles) {
            String content = getRawContentAsString(r, ticgitBranch.getCommit(), file.path).trim();
            if (file.name.equals("TICKET_ID")) {
                ticket.id = content;
            } else if (file.name.equals("TITLE")) {
                ticket.title = content;
            } else {
                String[] chunks = file.name.split("_");
                if (chunks[0].equals("ASSIGNED")) {
                    ticket.handler = content;
                } else if (chunks[0].equals("COMMENT")) {
                    try {
                        Comment c = new Comment(file.name, content);
                        ticket.comments.add(c);
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                } else if (chunks[0].equals("TAG")) {
                    if (content.startsWith("TAG_")) {
                        ticket.tags.add(content.substring(4));
                    } else {
                        ticket.tags.add(content);
                    }
                } else if (chunks[0].equals("STATE")) {
                    ticket.state = content;
                }
            }
        }
        Collections.sort(ticket.comments);
    }
    public static String getTicGitContent(Repository r, String filePath) {
        RefModel ticgitBranch = getTicGitBranch(r);
        if (ticgitBranch != null) {
            return getRawContentAsString(r, ticgitBranch.getCommit(), filePath);
        }
        return "";
    }
}
src/com/gitblit/utils/TicGitTicket.java
New file
@@ -0,0 +1,77 @@
package com.gitblit.utils;
import java.io.Serializable;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import com.gitblit.wicket.models.PathModel;
public class TicGitTicket implements Serializable, Comparable<TicGitTicket> {
    private static final long serialVersionUID = 1L;
    public String id;
    public String name;
    public String title;
    public String state;
    public Date date;
    public String handler;
    public String milestone;
    public String email;
    public String author;
    public List<Comment> comments;
    public List<String> tags;
    public TicGitTicket() {
        state = "open";
        comments = new ArrayList<Comment>();
        tags = new ArrayList<String>();
    }
    public TicGitTicket(String ticketName) throws ParseException {
        state = "";
        name = ticketName;
        comments = new ArrayList<Comment>();
        tags = new ArrayList<String>();
        String[] chunks = name.split("_");
        if (chunks.length == 3) {
            date = new Date(Long.parseLong(chunks[0]) * 1000l);
            title = chunks[1].replace('-', ' ');
        }
    }
    public static class Comment implements Serializable, Comparable<Comment> {
        private static final long serialVersionUID = 1L;
        public String text;
        public String author;
        public Date date;
        public Comment(String text, Date date) {
            this.text = text;
            this.date = date;
        }
        public Comment(String filename, String content) throws ParseException {
            String[] chunks = filename.split("_", -1);
            this.date = new Date(Long.parseLong(chunks[1]) * 1000l);
            this.author = chunks[2];
            this.text = content;
        }
        @Override
        public int compareTo(Comment o) {
            return date.compareTo(o.date);
        }
    }
    @Override
    public int compareTo(TicGitTicket o) {
        return date.compareTo(o.date);
    }
}
src/com/gitblit/wicket/BasePage.java
@@ -56,10 +56,14 @@
    }
    protected String trimShortLog(String string) {
        if (string.length() > 60) {
            return string.substring(0, 60) + "...";
        return trimString(string, 60);
    }
    protected String trimString(String value, int max) {
        if (value.length() <= max) {
            return value;
        }
        return string;
        return value.substring(0, max - 3) + "...";
    }
    public void error(String message, Throwable t) {
src/com/gitblit/wicket/GitBlitWebApp.java
@@ -29,13 +29,15 @@
import com.gitblit.utils.JGitUtils;
import com.gitblit.wicket.models.RepositoryModel;
import com.gitblit.wicket.pages.BlobPage;
import com.gitblit.wicket.pages.BranchesPage;
import com.gitblit.wicket.pages.CommitPage;
import com.gitblit.wicket.pages.HeadsPage;
import com.gitblit.wicket.pages.RepositoriesPage;
import com.gitblit.wicket.pages.ShortLogPage;
import com.gitblit.wicket.pages.SummaryPage;
import com.gitblit.wicket.pages.TagPage;
import com.gitblit.wicket.pages.TagsPage;
import com.gitblit.wicket.pages.TicGitPage;
import com.gitblit.wicket.pages.TicGitTicketPage;
import com.gitblit.wicket.pages.TreePage;
@@ -58,16 +60,20 @@
        // Grab Browser info (like timezone, etc)
        getRequestCycleSettings().setGatherExtendedBrowserInfo(true);
        // setup the url paths
        // setup the standard gitweb-ish urls
        mount(new MixedParamUrlCodingStrategy("/summary", SummaryPage.class, new String[] { "p" }));
        mount(new MixedParamUrlCodingStrategy("/shortlog", ShortLogPage.class, new String[] { "p", "h" }));
        mount(new MixedParamUrlCodingStrategy("/tags", TagsPage.class, new String[] { "p" }));
        mount(new MixedParamUrlCodingStrategy("/heads", HeadsPage.class, new String[] { "p" }));
        mount(new MixedParamUrlCodingStrategy("/branches", BranchesPage.class, new String[] { "p" }));
        mount(new MixedParamUrlCodingStrategy("/commit", CommitPage.class, new String[] { "p", "h" }));
        mount(new MixedParamUrlCodingStrategy("/tag", TagPage.class, new String[] { "p", "h" }));
        mount(new MixedParamUrlCodingStrategy("/tree", TreePage.class, new String[] { "p", "h", "f" }));
        mount(new MixedParamUrlCodingStrategy("/blob", BlobPage.class, new String[] { "p", "h", "f" }));
        
        // setup extended urls
        mount(new MixedParamUrlCodingStrategy("/ticgit", TicGitPage.class, new String[] { "p" }));
        mount(new MixedParamUrlCodingStrategy("/ticgittkt", TicGitTicketPage.class, new String[] { "p", "f" }));
        repositories = new File(StoredSettings.getString("repositoriesFolder", "repos"));
        exportAll = StoredSettings.getBoolean("exportAll", true);
        repositoryResolver = new FileResolver(repositories, exportAll);
src/com/gitblit/wicket/RepositoryPage.java
@@ -114,5 +114,5 @@
            return newCommitParameter();
        }
        return new PageParameters("p=" + repositoryName + ",h=" + commitId + ",f=" + path);
    }
    }
}
src/com/gitblit/wicket/models/RefModel.java
@@ -34,6 +34,10 @@
    public String getName() {
        return ref.getName();
    }
    public RevCommit getCommit() {
        return commit;
    }
    public ObjectId getCommitId() {
        return commit.getId();
@@ -46,7 +50,11 @@
    public ObjectId getObjectId() {
        return ref.getObjectId();
    }
    public boolean isAnnotatedTag() {
        return ref.isPeeled();
    }
    @Override
    public int compareTo(RefModel o) {
        return getDate().compareTo(o.getDate());
src/com/gitblit/wicket/pages/BranchesPage.html
New file
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
    <!-- page header -->
    <div wicket:id="pageHeader"></div>
    <!-- page nav links -->
    <div wicket:id="pageLinks"></div>
    <!-- shortlog -->
    <div style="margin-top:5px;" class="header" wicket:id="summary"></div>
    <table class="pretty">
        <tbody>
               <tr wicket:id="branch">
                 <td><i><span wicket:id="branchDate"></span></i></td>
                 <td><div wicket:id="branchName"></div></td>
                 <td><div wicket:id="branchType"></div></td>
                 <td class="rightAlign"><span wicket:id="branchLinks"></span></td>
               </tr>
        </tbody>
    </table>
    <!-- footer -->
    <div wicket:id="pageFooter"></div>
</body>
</html>
src/com/gitblit/wicket/pages/BranchesPage.java
New file
@@ -0,0 +1,71 @@
package com.gitblit.wicket.pages;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.Utils;
import com.gitblit.wicket.LinkPanel;
import com.gitblit.wicket.RepositoryPage;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.models.RefModel;
import com.gitblit.wicket.panels.BranchLinksPanel;
public class BranchesPage extends RepositoryPage {
    public BranchesPage(PageParameters params) {
        super(params, "branches");
        Repository r = getRepository();
        List<RefModel> branches = new ArrayList<RefModel>();
        branches.addAll(JGitUtils.getLocalBranches(r, -1));
        branches.addAll(JGitUtils.getRemoteBranches(r, -1));
        r.close();
        // shortlog
        add(new LinkPanel("summary", "title", repositoryName, SummaryPage.class, newRepositoryParameter()));
        ListDataProvider<RefModel> branchesDp = new ListDataProvider<RefModel>(branches);
        DataView<RefModel> branchView = new DataView<RefModel>("branch", branchesDp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            public void populateItem(final Item<RefModel> item) {
                final RefModel entry = item.getModelObject();
                String date;
                if (entry.getDate() != null) {
                    date = Utils.timeAgo(entry.getDate());
                } else {
                    date = "";
                }
                Label branchDateLabel = new Label("branchDate", date);
                item.add(branchDateLabel);
                WicketUtils.setCssClass(branchDateLabel, Utils.timeAgoCss(entry.getDate()));
                item.add(new LinkPanel("branchName", "list name", entry.getDisplayName(), ShortLogPage.class, newCommitParameter(entry.getName())));
                boolean remote = entry.getName().startsWith(Constants.R_REMOTES);
                item.add(new Label("branchType", remote ? "remote":"local"));
                item.add(new BranchLinksPanel("branchLinks", repositoryName, entry));
                String clazz = counter % 2 == 0 ? "dark" : "light";
                WicketUtils.setCssClass(item, clazz);
                counter++;
            }
        };
        add(branchView);
        // footer
        addFooter();
    }
}
src/com/gitblit/wicket/pages/HeadsPage.html
File was deleted
src/com/gitblit/wicket/pages/HeadsPage.java
File was deleted
src/com/gitblit/wicket/pages/SummaryPage.html
@@ -39,25 +39,25 @@
    </table>    
    <div class="pager" wicket:id="shortlogMore"></div>
    <!-- Open Heads Body -->
    <div style="width:300px;float:right;">
    <!-- Open Branches Body -->
    <div style="width:400px; float:left;">
        <!-- heads -->
        <div class="header" wicket:id="heads"></div>
        <div class="header" wicket:id="branches"></div>
        <table style="width:100%" class="pretty">
            <tbody>
                   <tr wicket:id="head">
                       <td class="date"><span wicket:id="headDate"></span></td>
                       <td><div wicket:id="headName"></div></td>
                       <td class="rightAlign"><span wicket:id="headLinks"></span></td>
                   <tr wicket:id="branch">
                       <td class="date"><span wicket:id="branchDate"></span></td>
                       <td><div wicket:id="branchName"></div></td>
                       <td class="rightAlign"><span wicket:id="branchLinks"></span></td>
                   </tr>
               </tbody>
        </table>
        <div class="pager" wicket:id="allHeads"></div>
        <!-- Close Heads Body -->
        <div class="pager" wicket:id="allBranches"></div>
        <!-- Close Branches Body -->
    </div>
    <!-- Open Tags body -->
    <div style="margin-right:305px;">
    <div style="margin-left:405px;">
        <!-- tags -->
        <div class="header" wicket:id="tags"></div>    
            <table style="width:100%" class="pretty">
src/com/gitblit/wicket/pages/SummaryPage.java
@@ -1,6 +1,8 @@
package com.gitblit.wicket.pages;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -32,7 +34,7 @@
import com.gitblit.wicket.models.Metric;
import com.gitblit.wicket.models.RefModel;
import com.gitblit.wicket.panels.AnnotatedTagLinksPanel;
import com.gitblit.wicket.panels.HeadLinksPanel;
import com.gitblit.wicket.panels.BranchLinksPanel;
import com.gitblit.wicket.panels.RefsPanel;
import com.gitblit.wicket.panels.ShortLogLinksPanel;
import com.gitblit.wicket.panels.TagLinksPanel;
@@ -126,14 +128,14 @@
                item.add(new LinkPanel("tagName", "list name", entry.getDisplayName(), CommitPage.class, newCommitParameter(entry.getCommitId().getName())));
                if (entry.getCommitId().equals(entry.getObjectId())) {
                    // simple tag
                    item.add(new Label("tagDescription", ""));
                    item.add(new TagLinksPanel("tagLinks", repositoryName, entry));
                } else {
                if (entry.isAnnotatedTag()) {
                    // annotated tag
                    item.add(new LinkPanel("tagDescription", "list subject", entry.getShortLog(), TagPage.class, newCommitParameter(entry.getObjectId().getName())));
                    item.add(new AnnotatedTagLinksPanel("tagLinks", repositoryName, entry));
                } else {
                    // simple tag on commit object
                    item.add(new Label("tagDescription", ""));
                    item.add(new TagLinksPanel("tagLinks", repositoryName, entry));
                }
                setAlternatingBackground(item, counter);
@@ -147,33 +149,41 @@
            add(new LinkPanel("allTags", "link", "all tags...", TagsPage.class, newRepositoryParameter()));
        }
        // heads
        List<RefModel> heads = JGitUtils.getHeads(r, numberRefs);
        add(new LinkPanel("heads", "title", "heads", HeadsPage.class, newRepositoryParameter()));
        // branches
        List<RefModel> branches = new ArrayList<RefModel>();
        branches.addAll(JGitUtils.getLocalBranches(r, numberRefs));
        branches.addAll(JGitUtils.getRemoteBranches(r, numberRefs));
        Collections.sort(branches);
        Collections.reverse(branches);
        if (numberRefs > 0 && branches.size() > numberRefs) {
            branches = new ArrayList<RefModel>(branches.subList(0, numberRefs));
        }
        ListDataProvider<RefModel> headsDp = new ListDataProvider<RefModel>(heads);
        DataView<RefModel> headsView = new DataView<RefModel>("head", headsDp) {
        add(new LinkPanel("branches", "title", "branches", BranchesPage.class, newRepositoryParameter()));
        ListDataProvider<RefModel> branchesDp = new ListDataProvider<RefModel>(branches);
        DataView<RefModel> branchesView = new DataView<RefModel>("branch", branchesDp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            public void populateItem(final Item<RefModel> item) {
                final RefModel entry = item.getModelObject();
                item.add(createDateLabel("headDate", entry.getDate()));
                item.add(createDateLabel("branchDate", entry.getDate()));
                item.add(new LinkPanel("headName", "list name", entry.getDisplayName(), ShortLogPage.class, newCommitParameter(entry.getName())));
                item.add(new LinkPanel("branchName", "list name", trimString(entry.getDisplayName(), 28), ShortLogPage.class, newCommitParameter(entry.getName())));
                item.add(new HeadLinksPanel("headLinks", repositoryName, entry));
                item.add(new BranchLinksPanel("branchLinks", repositoryName, entry));
                setAlternatingBackground(item, counter);
                counter++;
            }
        };
        add(headsView);
        if (heads.size() < numberRefs) {
            add(new Label("allHeads", "").setVisible(false));
        add(branchesView);
        if (branches.size() < numberRefs) {
            add(new Label("allBranches", "").setVisible(false));
        } else {
            add(new LinkPanel("allHeads", "link", "all heads...", HeadsPage.class, newRepositoryParameter()));
            add(new LinkPanel("allBranches", "link", "all branches...", BranchesPage.class, newRepositoryParameter()));
        }
        
        // Display an activity line graph
src/com/gitblit/wicket/pages/TagsPage.java
@@ -39,14 +39,14 @@
                item.add(new LinkPanel("tagName", "list name", entry.getDisplayName(), CommitPage.class, newCommitParameter(entry.getObjectId().getName())));
                if (entry.getCommitId().equals(entry.getObjectId())) {
                    // simple tag on commit object
                    item.add(new Label("tagDescription", ""));
                    item.add(new TagLinksPanel("tagLinks", repositoryName, entry));
                } else {
                if (entry.isAnnotatedTag()) {
                    // annotated tag
                    item.add(new LinkPanel("tagDescription", "list subject", entry.getShortLog(), TagPage.class, newCommitParameter(entry.getObjectId().getName())));
                    item.add(new AnnotatedTagLinksPanel("tagLinks", repositoryName, entry));
                } else {
                    // simple tag on commit object
                    item.add(new Label("tagDescription", ""));
                    item.add(new TagLinksPanel("tagLinks", repositoryName, entry));
                }
                setAlternatingBackground(item, counter);
src/com/gitblit/wicket/pages/TicGitPage.html
New file
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
    <!-- page header -->
    <div wicket:id="pageHeader"></div>
    <!-- page nav links -->
    <div wicket:id="pageLinks"></div>
    <!-- shortlog -->
    <div style="margin-top:5px;" class="header" wicket:id="summary"></div>
    <table style="width:100%" class="pretty">
        <tbody>
               <tr wicket:id="ticket">
                 <td style="padding:0; margin:0;" ><div wicket:id="ticketState"></div></td>
                 <td class="date"><span wicket:id="ticketDate"></span></td>
                 <td><div wicket:id="ticketHandler"></div></td>
                 <td><div wicket:id="ticketTitle"></div></td>
               </tr>
        </tbody>
    </table>
    <!-- footer -->
    <div wicket:id="pageFooter"></div>
</body>
</html>
src/com/gitblit/wicket/pages/TicGitPage.java
New file
@@ -0,0 +1,65 @@
package com.gitblit.wicket.pages;
import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import org.eclipse.jgit.lib.Repository;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.TicGitTicket;
import com.gitblit.wicket.LinkPanel;
import com.gitblit.wicket.RepositoryPage;
import com.gitblit.wicket.WicketUtils;
public class TicGitPage extends RepositoryPage {
    public TicGitPage(PageParameters params) {
        super(params, "ticgit");
        Repository r = getRepository();
        List<TicGitTicket> tickets = JGitUtils.getTicGitTickets(r);
        r.close();
        // shortlog
        add(new LinkPanel("summary", "title", repositoryName, SummaryPage.class, newRepositoryParameter()));
        ListDataProvider<TicGitTicket> ticketsDp = new ListDataProvider<TicGitTicket>(tickets);
        DataView<TicGitTicket> ticketsView = new DataView<TicGitTicket>("ticket", ticketsDp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            public void populateItem(final Item<TicGitTicket> item) {
                final TicGitTicket entry = item.getModelObject();
                Label stateLabel = new Label("ticketState", entry.state);
                String css = null;
                if (entry.state.equals("open")) {
                    css = "bug_open";
                } else if (entry.state.equals("hold")) {
                    css = "bug_hold";
                } else if (entry.state.equals("resolved")) {
                    css = "bug_resolved";
                } else if (entry.state.equals("invalid")) {
                    css = "bug_invalid";
                }
                if (css != null) {
                    WicketUtils.setCssClass(stateLabel, css);
                }
                item.add(stateLabel);
                item.add(createDateLabel("ticketDate", entry.date));
                item.add(new Label("ticketHandler", trimString(entry.handler, 30)));
                item.add(new LinkPanel("ticketTitle", null, trimString(entry.title, 80), TicGitTicketPage.class, newPathParameter(entry.name)));
                setAlternatingBackground(item, counter);
                counter++;
            }
        };
        add(ticketsView);
        // footer
        addFooter();
    }
}
src/com/gitblit/wicket/pages/TicGitTicketPage.html
New file
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
    <!-- page header -->
    <div wicket:id="pageHeader"></div>
    <!-- page nav links -->
    <div wicket:id="pageLinks"></div>
    <!-- ticket title -->
    <div style="font-size:150%;padding-top:5px;" wicket:id="ticketTitle"></div>
    <!-- ticket info -->
    <table class="plain">
        <tr><th>ticket id</th><td class="sha1"><span wicket:id="ticketId">Message goes here</span></td></tr>
        <tr><th>assigned</th><td><span wicket:id=ticketHandler>Message goes here</span></td></tr>
        <tr><th>open date</th><td><span wicket:id="ticketOpenDate">Message goes here</span></td></tr>
        <tr><th>state</th><td><span wicket:id="ticketState">Message goes here</span></td></tr>
        <tr><th>tags</th><td><span wicket:id="ticketTags">Message goes here</span></td></tr>
    </table>
    <!-- comments header -->
    <div class="header">Comments</div>
    <!-- comments -->
    <table style="width:100%;" class="pretty">
        <tbody>
            <tr wicket:id="comment">
                 <td class="date"><span wicket:id="commentDate"></span></td>
                 <td><b><div wicket:id="commentAuthor"></div></b></td>
                 <td><div wicket:id="commentText"></div></td>
               </tr>
           </tbody>
    </table>
    <!-- footer -->
    <div wicket:id="pageFooter"></div>
</body>
</html>
src/com/gitblit/wicket/pages/TicGitTicketPage.java
New file
@@ -0,0 +1,70 @@
package com.gitblit.wicket.pages;
import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import org.eclipse.jgit.lib.Repository;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.TicGitTicket;
import com.gitblit.utils.TicGitTicket.Comment;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.RepositoryPage;
import com.gitblit.wicket.WicketUtils;
public class TicGitTicketPage extends RepositoryPage {
    public TicGitTicketPage(PageParameters params) {
        super(params, "ticgit ticket");
        final String ticketFolder = params.getString("f", "");
        Repository r = getRepository();
        TicGitTicket t = JGitUtils.getTicGitTicket(r, ticketFolder);
        r.close();
        add(new Label("ticketTitle", t.title));
        add(new Label("ticketId", t.id));
        add(new Label("ticketHandler", t.handler));
        String openDate = GitBlitWebSession.get().formatDateTimeLong(t.date);
        add(new Label("ticketOpenDate", openDate));
        add(new Label("ticketState", t.state));
        add(new Label("ticketTags", flattenStrings(t.tags)));
        ListDataProvider<Comment> commentsDp = new ListDataProvider<Comment>(t.comments);
        DataView<Comment> commentsView = new DataView<Comment>("comment", commentsDp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            public void populateItem(final Item<Comment> item) {
                final Comment entry = item.getModelObject();
                item.add(createDateLabel("commentDate", entry.date));
                item.add(new Label("commentAuthor", entry.author));
                item.add(new Label("commentText", prepareComment(entry.text)).setEscapeModelStrings(false));
                setAlternatingBackground(item, counter);
                counter++;
            }
        };
        add(commentsView);
        // footer
        addFooter();
    }
    private String prepareComment(String comment) {
        String html = WicketUtils.breakLines(comment).trim();
        return html.replaceAll("\\bcommit\\s*([A-Za-z0-9]*)\\b", "<a href=\"/commit/" + repositoryName + "/$1\">commit $1</a>");
    }
    private String flattenStrings(List<String> values) {
        StringBuilder sb = new StringBuilder();
        for (String value : values) {
            sb.append(value).append(" ");
        }
        return sb.toString().trim();
    }
}
src/com/gitblit/wicket/panels/BranchLinksPanel.html
src/com/gitblit/wicket/panels/BranchLinksPanel.java
File was renamed from src/com/gitblit/wicket/panels/HeadLinksPanel.java
@@ -1,21 +1,21 @@
package com.gitblit.wicket.panels;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.panel.Panel;
import com.gitblit.wicket.LinkPanel;
import com.gitblit.wicket.models.RefModel;
import com.gitblit.wicket.pages.ShortLogPage;
import com.gitblit.wicket.pages.TreePage;
public class HeadLinksPanel extends Panel {
public class BranchLinksPanel extends Panel {
    private static final long serialVersionUID = 1L;
    public HeadLinksPanel(String id, String repositoryName, RefModel tag) {
    public BranchLinksPanel(String id, String repositoryName, RefModel tag) {
        super(id);
        add(new LinkPanel("shortlog", null, "shortlog", ShortLogPage.class, new PageParameters("p=" + repositoryName + ",h=" + tag.getName())));
        add(new Label("tree", "tree"));
        add(new LinkPanel("tree", null, "tree", TreePage.class, new PageParameters("p=" + repositoryName + ",h=" + tag.getName())));
    }
}
src/com/gitblit/wicket/panels/PageLinksPanel.html
@@ -3,7 +3,7 @@
<wicket:panel>
    <!-- page nav links -->    
    <div class="page_nav">
        <span wicket:id="summary"></span> | <span wicket:id="shortlog"></span> | <span wicket:id="tags"></span> | <span wicket:id="commit"></span> | <span wicket:id="commitdiff"></span> | <span wicket:id="tree"></span>
        <span wicket:id="summary"></span> | <span wicket:id="shortlog"></span> | <span wicket:id="branches"></span> | <span wicket:id="tags"></span> | <span wicket:id="tree"></span>
    </div>    
</wicket:panel>
</html>
src/com/gitblit/wicket/panels/PageLinksPanel.java
@@ -5,6 +5,7 @@
import org.apache.wicket.markup.html.panel.Panel;
import com.gitblit.wicket.LinkPanel;
import com.gitblit.wicket.pages.BranchesPage;
import com.gitblit.wicket.pages.CommitPage;
import com.gitblit.wicket.pages.ShortLogPage;
import com.gitblit.wicket.pages.SummaryPage;
@@ -32,25 +33,20 @@
            add(new LinkPanel("shortlog", null, "shortlog", ShortLogPage.class, new PageParameters("p=" + repositoryName)));
        }
        
        // branches
        if (pageName.equals("branches")) {
            add(new Label("branches", pageName));
        } else {
            add(new LinkPanel("branches", null, "branches", BranchesPage.class, new PageParameters("p=" + repositoryName)));
        }
        // tags
        if (pageName.equals("tags")) {
            add(new Label("tags", pageName));
        } else {
            add(new LinkPanel("tags", null, "tags", TagsPage.class, new PageParameters("p=" + repositoryName)));
        }
        // commit
        if (pageName.equals("commit")) {
            add(new Label("commit", pageName));
        } else {
            add(new LinkPanel("commit", null, "commit", CommitPage.class, new PageParameters("p=" + repositoryName + ",h=HEAD")));
        }
        // commitdiff
        if (pageName.equals("commitdiff")) {
            add(new Label("commitdiff", pageName));
        } else {
            add(new Label("commitdiff", "commitdiff"));
        }
        // tree
        if (pageName.equals("tree")) {
            add(new Label("tree", pageName));