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