padding: 0px;\r
}\r
\r
-pre.prettyprint, pre.plainprint {\r
+pre, pre.prettyprint, pre.plainprint {\r
color: black;\r
+ font-family: monospace;\r
font-size:12px;\r
border:0px;\r
}\r
text-align: center;\r
}\r
\r
+div.diff {\r
+ font-family: monospace;\r
+}\r
+\r
+div.diff.header {\r
+ -moz-border-bottom-colors: none;\r
+ -moz-border-image: none;\r
+ -moz-border-left-colors: none;\r
+ -moz-border-right-colors: none;\r
+ -moz-border-top-colors: none;\r
+ background-color: #EDECE6;\r
+ border-color: #D9D8D1;\r
+ border-style: solid;\r
+ border-width: 1px 0;\r
+ font-weight: bold;\r
+ margin-top: 4px;\r
+ padding: 4px 0 2px;\r
+}\r
+\r
+div.diff.extended_header {\r
+ background-color: #F6F5EE;\r
+ padding: 2px 0;\r
+ font-family: inherit;\r
+}\r
+\r
+div.diff.add {\r
+ color: #008800;\r
+ font-family: inherit;\r
+}\r
+\r
+div.diff.remove {\r
+ color: #cc0000;\r
+ font-family: inherit;\r
+}\r
+\r
+div.diff.unchanged {\r
+ color: inherit;\r
+ font-family: inherit;\r
+}\r
+\r
+div.diff.hunk_header {\r
+ -moz-border-bottom-colors: none;\r
+ -moz-border-image: none;\r
+ -moz-border-left-colors: none;\r
+ -moz-border-right-colors: none;\r
+ -moz-border-top-colors: none;\r
+ border-color: #FFE0FF;\r
+ border-style: dotted;\r
+ border-width: 1px 0 0;\r
+ margin-top: 2px;\r
+ font-family: inherit;\r
+}\r
+\r
+span.diff.hunk_info {\r
+ background-color: #FFEEFF; \r
+ color: #990099;\r
+ font-family: inherit;\r
+}\r
+\r
+span.diff.hunk_section { \r
+ color: #AA22AA;\r
+ font-family: inherit;\r
+}\r
+\r
a.list {\r
text-decoration: none;\r
color: #000000;\r
\r
import com.gitblit.utils.JGitUtils;\r
import com.gitblit.utils.TicGitTicket;\r
+import com.gitblit.wicket.models.PathModel;\r
import com.gitblit.wicket.models.RefModel;\r
\r
-\r
public class JGitUtilsTest extends TestCase {\r
- \r
+\r
private File repositoriesFolder = new File("c:/projects/git");\r
private boolean exportAll = true;\r
private boolean readNested = true;\r
- \r
+\r
private List<String> getRepositories() {\r
return JGitUtils.getRepositoryList(repositoriesFolder, exportAll, readNested);\r
}\r
- \r
+\r
private Repository getRepository() throws Exception {\r
return new FileRepository(new File(repositoriesFolder, getRepositories().get(0)) + "/" + Constants.DOT_GIT);\r
}\r
- \r
+\r
public void testFindRepositories() {\r
List<String> list = getRepositories();\r
assertTrue("No repositories found in " + repositoriesFolder, list.size() > 0);\r
}\r
- \r
- public void testOpenRepository() throws Exception { \r
+\r
+ public void testOpenRepository() throws Exception {\r
Repository r = getRepository();\r
r.close();\r
assertTrue("Could not find repository!", r != null);\r
}\r
- \r
- public void testLastChangeRepository() throws Exception { \r
+\r
+ public void testLastChangeRepository() throws Exception {\r
Repository r = getRepository();\r
Date date = JGitUtils.getLastChange(r);\r
r.close();\r
assertTrue("Could not get last repository change date!", date != null);\r
}\r
- \r
+\r
public void testRetrieveRevObject() throws Exception {\r
Repository r = getRepository();\r
RevCommit commit = JGitUtils.getCommit(r, Constants.HEAD);\r
RevTree tree = commit.getTree();\r
- RevObject object = JGitUtils.getRevObject(r, tree, "AUTHORS"); \r
+ RevObject object = JGitUtils.getRevObject(r, tree, "AUTHORS");\r
r.close();\r
assertTrue("Object is null!", object != null);\r
}\r
- \r
+\r
public void testRetrieveStringContent() throws Exception {\r
Repository r = getRepository();\r
RevCommit commit = JGitUtils.getCommit(r, Constants.HEAD);\r
r.close();\r
assertTrue("Content is null!", content != null);\r
}\r
- \r
+\r
public void testTicGit() throws Exception {\r
Repository r = new FileRepository(new File(repositoriesFolder, "ticgit") + "/" + Constants.DOT_GIT);\r
RefModel ticgit = JGitUtils.getTicGitBranch(r);\r
r.close();\r
}\r
\r
+ public void testFilesInCommit() throws Exception {\r
+ Repository r = getRepository();\r
+ RevCommit commit = JGitUtils.getCommit(r, Constants.HEAD);\r
+ List<PathModel> paths = JGitUtils.getFilesInCommit(r, commit);\r
+ r.close();\r
+ assertTrue("No changed paths found!", paths.size() > 0);\r
+ }\r
+ \r
+ public void testCommitDiff() throws Exception {\r
+ Repository r = getRepository();\r
+ RevCommit commit = JGitUtils.getCommit(r, Constants.HEAD);\r
+ String diff = JGitUtils.getCommitDiff(r, commit, false);\r
+ r.close();\r
+ System.out.println(diff);\r
+ }\r
+\r
}\r
--- /dev/null
+package com.gitblit.utils;\r
+\r
+import static org.eclipse.jgit.lib.Constants.encodeASCII;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+import org.eclipse.jgit.diff.DiffFormatter;\r
+import org.eclipse.jgit.diff.RawText;\r
+\r
+public class HtmlDiffFormatter extends DiffFormatter {\r
+ private final OutputStream os;\r
+\r
+ public HtmlDiffFormatter(OutputStream os) {\r
+ super(os);\r
+ this.os = os;\r
+ }\r
+\r
+ /**\r
+ * Output a hunk header\r
+ * \r
+ * @param aStartLine\r
+ * within first source\r
+ * @param aEndLine\r
+ * within first source\r
+ * @param bStartLine\r
+ * within second source\r
+ * @param bEndLine\r
+ * within second source\r
+ * @throws IOException\r
+ */\r
+ @Override\r
+ protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine, int bEndLine) throws IOException {\r
+ os.write("<div class=\"diff hunk_header\"><span class=\"diff hunk_info\">".getBytes());\r
+ os.write('@');\r
+ os.write('@');\r
+ writeRange('-', aStartLine + 1, aEndLine - aStartLine);\r
+ writeRange('+', bStartLine + 1, bEndLine - bStartLine);\r
+ os.write(' ');\r
+ os.write('@');\r
+ os.write('@');\r
+ // TODO not sure if JGit can determine hunk section\r
+ //os.write("<span class=\"diff hunk_section\">".getBytes());\r
+ //os.write("</span>".getBytes());\r
+ os.write("</span></div>".getBytes());\r
+ }\r
+\r
+ private void writeRange(final char prefix, final int begin, final int cnt) throws IOException {\r
+ os.write(' ');\r
+ os.write(prefix);\r
+ switch (cnt) {\r
+ case 0:\r
+ // If the range is empty, its beginning number must\r
+ // be the\r
+ // line just before the range, or 0 if the range is\r
+ // at the\r
+ // start of the file stream. Here, begin is always 1\r
+ // based,\r
+ // so an empty file would produce "0,0".\r
+ //\r
+ os.write(encodeASCII(begin - 1));\r
+ os.write(',');\r
+ os.write('0');\r
+ break;\r
+\r
+ case 1:\r
+ // If the range is exactly one line, produce only\r
+ // the number.\r
+ //\r
+ os.write(encodeASCII(begin));\r
+ break;\r
+\r
+ default:\r
+ os.write(encodeASCII(begin));\r
+ os.write(',');\r
+ os.write(encodeASCII(cnt));\r
+ break;\r
+ }\r
+ }\r
+\r
+ @Override\r
+ protected void writeLine(final char prefix, final RawText text, final int cur) throws IOException {\r
+ switch (prefix) {\r
+ case '+':\r
+ os.write("<div class=\"diff add\">".getBytes());\r
+ break;\r
+ case '-':\r
+ os.write("<div class=\"diff remove\">".getBytes());\r
+ break;\r
+ }\r
+ os.write(prefix);\r
+ text.writeLine(os, cur);\r
+ switch (prefix) {\r
+ case '+':\r
+ case '-':\r
+ os.write("</div>".getBytes());\r
+ break;\r
+ default:\r
+ os.write('\n');\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Workaround function for complex private methods in DiffFormatter. This\r
+ * sets the html for the diff headers.\r
+ * \r
+ * @return\r
+ */\r
+ public String getHtml() {\r
+ String html = os.toString();\r
+ String[] lines = html.split("\n");\r
+ StringBuilder sb = new StringBuilder();\r
+ sb.append("<div class=\"diff\">");\r
+ for (String line : lines) {\r
+ if (line.startsWith("diff")) {\r
+ sb.append("<div class=\"diff header\">").append(line).append("</div>");\r
+ } else if (line.startsWith("---")) {\r
+ sb.append("<div class=\"diff remove\">").append(line).append("</div>");\r
+ } else if (line.startsWith("+++")) {\r
+ sb.append("<div class=\"diff add\">").append(line).append("</div>");\r
+ } else {\r
+ sb.append(line).append('\n');\r
+ }\r
+ }\r
+ sb.append("</div>");\r
+ return sb.toString();\r
+ }\r
+}\r
import java.util.Set;\r
\r
import org.eclipse.jgit.api.Git;\r
+import org.eclipse.jgit.diff.DiffEntry;\r
+import org.eclipse.jgit.diff.DiffFormatter;\r
+import org.eclipse.jgit.diff.RawTextComparator;\r
import org.eclipse.jgit.errors.ConfigInvalidException;\r
import org.eclipse.jgit.lib.AnyObjectId;\r
import org.eclipse.jgit.lib.Constants;\r
import org.eclipse.jgit.treewalk.TreeWalk;\r
import org.eclipse.jgit.treewalk.filter.PathFilter;\r
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;\r
+import org.eclipse.jgit.treewalk.filter.TreeFilter;\r
+import org.eclipse.jgit.util.io.DisabledOutputStream;\r
import org.slf4j.Logger;\r
import org.slf4j.LoggerFactory;\r
\r
return list;\r
}\r
\r
- public static List<PathModel> getCommitChangedPaths(Repository r, String commitId) {\r
+ public static List<PathModel> getFilesInCommit(Repository r, String commitId) {\r
RevCommit commit = getCommit(r, commitId);\r
- return getCommitChangedPaths(r, commit);\r
+ return getFilesInCommit(r, commit);\r
}\r
\r
- public static List<PathModel> getCommitChangedPaths(Repository r, RevCommit commit) {\r
+ public static List<PathModel> getFilesInCommit(Repository r, RevCommit commit) {\r
List<PathModel> list = new ArrayList<PathModel>();\r
- final TreeWalk walk = new TreeWalk(r);\r
- walk.setRecursive(false);\r
try {\r
- walk.addTree(commit.getTree());\r
- while (walk.next()) {\r
- list.add(getPathModel(walk, null, commit));\r
+ final RevWalk rw = new RevWalk(r);\r
+ RevCommit parent = rw.parseCommit(commit.getParent(0).getId());\r
+ RevTree parentTree = parent.getTree();\r
+ RevTree commitTree = commit.getTree();\r
+\r
+ final TreeWalk walk = new TreeWalk(r);\r
+ walk.reset();\r
+ walk.setRecursive(true);\r
+ walk.addTree(parentTree);\r
+ walk.addTree(commitTree);\r
+ walk.setFilter(TreeFilter.ANY_DIFF);\r
+\r
+ RawTextComparator cmp = RawTextComparator.DEFAULT;\r
+ DiffFormatter df = new DiffFormatter(DisabledOutputStream.INSTANCE);\r
+ df.setRepository(r);\r
+ df.setDiffComparator(cmp);\r
+ df.setDetectRenames(true);\r
+ List<DiffEntry> diffs = df.scan(parentTree, commitTree);\r
+ for (DiffEntry diff : diffs) {\r
+ list.add(new PathModel(diff.getNewPath(), diff.getNewPath(), 0, diff.getNewMode().getBits(), commit.getId().getName()));\r
}\r
+ } catch (Throwable t) {\r
+ LOGGER.error("failed to determine files in commit!", t);\r
+ }\r
+ return list;\r
+ }\r
\r
- } catch (IOException e) {\r
- LOGGER.error("Failed to get files for commit " + commit.getName(), e);\r
- } finally {\r
- if (walk != null) {\r
- walk.release();\r
+ public static String getCommitDiff(Repository r, RevCommit commit, boolean outputHtml) {\r
+ return getCommitDiff(r, commit, null, outputHtml);\r
+ }\r
+ \r
+ public static String getCommitDiff(Repository r, RevCommit commit, String path, boolean outputHtml) {\r
+ try {\r
+ final RevWalk rw = new RevWalk(r);\r
+ RevCommit parent = rw.parseCommit(commit.getParent(0).getId());\r
+ RevTree parentTree = parent.getTree();\r
+ RevTree commitTree = commit.getTree();\r
+\r
+ final TreeWalk walk = new TreeWalk(r);\r
+ walk.reset();\r
+ walk.setRecursive(true);\r
+ walk.addTree(parentTree);\r
+ walk.addTree(commitTree);\r
+ if (path != null && path.trim().length() > 0) {\r
+ walk.setFilter(PathFilter.create(path));\r
+ } else {\r
+ walk.setFilter(TreeFilter.ANY_DIFF);\r
+ }\r
+\r
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();\r
+ RawTextComparator cmp = RawTextComparator.DEFAULT;\r
+ DiffFormatter df;\r
+ if (outputHtml) {\r
+ df = new HtmlDiffFormatter(os);\r
+ } else {\r
+ df = new DiffFormatter(os);\r
+ }\r
+ df.setRepository(r);\r
+ df.setDiffComparator(cmp);\r
+ df.setDetectRenames(true);\r
+ List<DiffEntry> diffs = df.scan(parentTree, commitTree);\r
+ df.format(diffs);\r
+ String diff;\r
+ if (outputHtml) {\r
+ // workaround for complex private methods in DiffFormatter\r
+ diff = ((HtmlDiffFormatter) df).getHtml();\r
+ } else {\r
+ diff = os.toString();\r
}\r
+ df.flush();\r
+ return diff;\r
+ } catch (Throwable t) {\r
+ LOGGER.error("failed to generate commit diff!", t);\r
}\r
- return list;\r
+ return null;\r
}\r
\r
private static PathModel getPathModel(TreeWalk walk, String basePath, RevCommit commit) {\r
}\r
return null;\r
}\r
- \r
+\r
private static void readTicketContents(Repository r, RefModel ticgitBranch, TicGitTicket ticket) {\r
List<PathModel> ticketFiles = getFilesInPath(r, ticket.name, ticgitBranch.getCommit());\r
for (PathModel file : ticketFiles) {\r
import com.gitblit.wicket.pages.BlobPage;\r
import com.gitblit.wicket.pages.BranchesPage;\r
import com.gitblit.wicket.pages.CommitPage;\r
+import com.gitblit.wicket.pages.DiffPage;\r
import com.gitblit.wicket.pages.RepositoriesPage;\r
import com.gitblit.wicket.pages.ShortLogPage;\r
import com.gitblit.wicket.pages.SummaryPage;\r
mount(new MixedParamUrlCodingStrategy("/tag", TagPage.class, new String[] { "p", "h" }));\r
mount(new MixedParamUrlCodingStrategy("/tree", TreePage.class, new String[] { "p", "h", "f" }));\r
mount(new MixedParamUrlCodingStrategy("/blob", BlobPage.class, new String[] { "p", "h", "f" }));\r
+ mount(new MixedParamUrlCodingStrategy("/diff", DiffPage.class, new String[] { "p", "h", "f" }));\r
\r
// setup extended urls\r
mount(new MixedParamUrlCodingStrategy("/ticgit", TicGitPage.class, new String[] { "p" }));\r
addFullText("fullMessage", c.getFullMessage(), true);\r
\r
// changed paths list\r
- List<PathModel> paths = JGitUtils.getCommitChangedPaths(r, c);\r
+ List<PathModel> paths = JGitUtils.getFilesInCommit(r, c);\r
ListDataProvider<PathModel> pathsDp = new ListDataProvider<PathModel>(paths);\r
DataView<PathModel> pathsView = new DataView<PathModel>("changedPath", pathsDp) {\r
private static final long serialVersionUID = 1L;\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<html xmlns="http://www.w3.org/1999/xhtml" >\r
+<head>\r
+ <link href="prettify/prettify.css" type="text/css" rel="stylesheet" />\r
+ <script type="text/javascript" src="prettify/prettify.js"></script>\r
+</head>\r
+<body onload="prettyPrint()">\r
+ <!-- page header -->\r
+ <div wicket:id="pageHeader"></div>\r
+\r
+ <!-- page nav links --> \r
+ <div wicket:id="pageLinks"></div>\r
+ \r
+ <!-- blob nav links --> \r
+ <div class="page_nav2">\r
+ <span wicket:id="historyLink"></span> | <span wicket:id="rawLink"></span> | <span wicket:id="headLink"></span>\r
+ </div> \r
+ \r
+ <!-- shortlog header -->\r
+ <div class="header" wicket:id="shortlog"></div>\r
+\r
+ <!-- breadcrumbs -->\r
+ <div wicket:id="breadcrumbs"></div>\r
+ \r
+ <!-- diff content -->\r
+ <pre wicket:id="diffText"></pre>\r
+ \r
+ <!-- footer -->\r
+ <div wicket:id="pageFooter"></div>\r
+</body>\r
+</html>
\ No newline at end of file
--- /dev/null
+package com.gitblit.wicket.pages;\r
+\r
+import org.apache.wicket.PageParameters;\r
+import org.apache.wicket.markup.html.basic.Label;\r
+import org.eclipse.jgit.lib.Repository;\r
+import org.eclipse.jgit.revwalk.RevCommit;\r
+\r
+import com.gitblit.utils.JGitUtils;\r
+import com.gitblit.wicket.LinkPanel;\r
+import com.gitblit.wicket.RepositoryPage;\r
+import com.gitblit.wicket.panels.PathBreadcrumbsPanel;\r
+\r
+public class DiffPage extends RepositoryPage {\r
+\r
+ public DiffPage(PageParameters params) {\r
+ super(params, "diff");\r
+\r
+ final String blobPath = params.getString("f", null);\r
+\r
+ Repository r = getRepository();\r
+ RevCommit commit = JGitUtils.getCommit(r, commitId);\r
+ String diff;\r
+ if (blobPath != null && blobPath.length() > 0) {\r
+ // blob diff\r
+ diff = JGitUtils.getCommitDiff(r, commit, blobPath, true);\r
+ } else {\r
+ // commit diff\r
+ diff = JGitUtils.getCommitDiff(r, commit, true);\r
+ }\r
+ r.close();\r
+\r
+ // diff page links\r
+ add(new Label("historyLink", "history"));\r
+ add(new Label("rawLink", "raw"));\r
+ add(new Label("headLink", "HEAD"));\r
+\r
+ add(new LinkPanel("shortlog", "title", commit.getShortMessage(), CommitPage.class, newCommitParameter()));\r
+\r
+ add(new PathBreadcrumbsPanel("breadcrumbs", repositoryName, blobPath, commitId));\r
+\r
+ add(new Label("diffText", diff).setEscapeModelStrings(false));\r
+\r
+ // footer\r
+ addFooter();\r
+ }\r
+}\r
import com.gitblit.wicket.LinkPanel;\r
import com.gitblit.wicket.models.PathModel;\r
import com.gitblit.wicket.pages.BlobPage;\r
+import com.gitblit.wicket.pages.DiffPage;\r
\r
\r
public class PathLinksPanel extends Panel {\r
\r
public PathLinksPanel(String id, String repositoryName, PathModel path) {\r
super(id);\r
- add(new Label("diff", "diff"));\r
+ add(new LinkPanel("diff", null, "diff", DiffPage.class, new PageParameters("p=" + repositoryName + ",h=" + path.commitId + ",f=" + path.path)));\r
add(new LinkPanel("blob", null, "view", BlobPage.class, new PageParameters("p=" + repositoryName + ",h=" + path.commitId + ",f=" + path.path)));\r
add(new Label("history", "history"));\r
}\r
package com.gitblit.wicket.panels;\r
\r
import org.apache.wicket.PageParameters;\r
-import org.apache.wicket.markup.html.basic.Label;\r
import org.apache.wicket.markup.html.panel.Panel;\r
\r
import com.gitblit.wicket.LinkPanel;\r
import com.gitblit.wicket.pages.CommitPage;\r
+import com.gitblit.wicket.pages.DiffPage;\r
import com.gitblit.wicket.pages.TreePage;\r
\r
\r
super(id);\r
\r
add(new LinkPanel("commit", null, "commit", CommitPage.class, new PageParameters("p=" + repositoryName + ",h=" + commitId)));\r
- add(new Label("commitdiff", "commitdiff"));\r
+ add(new LinkPanel("commitdiff", null, "commitdiff", DiffPage.class, new PageParameters("p=" + repositoryName + ",h=" + commitId)));\r
add(new LinkPanel("tree", null, "tree", TreePage.class, new PageParameters("p=" + repositoryName + ",h=" + commitId)));\r
}\r
}
\ No newline at end of file