- Added option to render Markdown commit messages (issue-203)
- Added setting to control creating a repository as --shared on Unix servers (issue-263)
- Added setting to globally disable anonymous pushes in the receive pack
+ - Added a normalized diffstat display to the commit, commitdiff, and compare pages
dependencyChanges: ~
settings:
- { name: 'git.createRepositoriesShared', defaultValue: 'false' }
\r
import java.io.Serializable;\r
\r
+import org.eclipse.jgit.diff.DiffEntry;\r
import org.eclipse.jgit.diff.DiffEntry.ChangeType;\r
import org.eclipse.jgit.lib.FileMode;\r
\r
return FileMode.TREE.equals(mode);\r
}\r
\r
+ public boolean isFile() {\r
+ return FileMode.REGULAR_FILE.equals(mode)\r
+ || FileMode.EXECUTABLE_FILE.equals(mode)\r
+ || (FileMode.MISSING.equals(mode) && !isSymlink() && !isSubmodule() && !isTree());\r
+ }\r
+\r
@Override\r
public int hashCode() {\r
return commitId.hashCode() + path.hashCode();\r
private static final long serialVersionUID = 1L;\r
\r
public ChangeType changeType;\r
-\r
+ \r
+ public int insertions;\r
+ \r
+ public int deletions;\r
+ \r
public PathChangeModel(String name, String path, long size, int mode, String objectId,\r
String commitId, ChangeType type) {\r
super(name, path, size, mode, objectId, commitId);\r
this.changeType = type;\r
}\r
+ \r
+ public void update(char op) {\r
+ switch (op) {\r
+ case '+':\r
+ insertions++;\r
+ break;\r
+ case '-':\r
+ deletions++;\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+ }\r
\r
@Override\r
public int hashCode() {\r
public boolean equals(Object o) {\r
return super.equals(o);\r
}\r
+\r
+ public static PathChangeModel from(DiffEntry diff, String commitId) {\r
+ PathChangeModel pcm;\r
+ if (diff.getChangeType().equals(ChangeType.DELETE)) {\r
+ pcm = new PathChangeModel(diff.getOldPath(), diff.getOldPath(), 0, diff\r
+ .getNewMode().getBits(), diff.getOldId().name(), commitId, diff\r
+ .getChangeType());\r
+ } else if (diff.getChangeType().equals(ChangeType.RENAME)) {\r
+ pcm = new PathChangeModel(diff.getOldPath(), diff.getNewPath(), 0, diff\r
+ .getNewMode().getBits(), diff.getNewId().name(), commitId, diff\r
+ .getChangeType());\r
+ } else {\r
+ pcm = new PathChangeModel(diff.getNewPath(), diff.getNewPath(), 0, diff\r
+ .getNewMode().getBits(), diff.getNewId().name(), commitId, diff\r
+ .getChangeType());\r
+ }\r
+ return pcm;\r
+ }\r
}\r
}\r
--- /dev/null
+/*\r
+ * Copyright 2013 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.utils;\r
+\r
+import java.io.IOException;\r
+\r
+import org.eclipse.jgit.diff.DiffEntry;\r
+import org.eclipse.jgit.diff.DiffFormatter;\r
+import org.eclipse.jgit.diff.RawText;\r
+import org.eclipse.jgit.util.io.NullOutputStream;\r
+\r
+import com.gitblit.models.PathModel.PathChangeModel;\r
+import com.gitblit.utils.DiffUtils.DiffStat;\r
+\r
+/**\r
+ * Calculates a DiffStat.\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+public class DiffStatFormatter extends DiffFormatter {\r
+\r
+ private final DiffStat diffStat;\r
+\r
+ private PathChangeModel path;\r
+\r
+ public DiffStatFormatter(String commitId) {\r
+ super(NullOutputStream.INSTANCE);\r
+ diffStat = new DiffStat(commitId);\r
+ }\r
+\r
+ @Override\r
+ public void format(DiffEntry entry) throws IOException {\r
+ path = diffStat.addPath(entry);\r
+ super.format(entry);\r
+ }\r
+\r
+ @Override\r
+ protected void writeLine(final char prefix, final RawText text, final int cur)\r
+ throws IOException {\r
+ path.update(prefix);\r
+ }\r
+\r
+ public DiffStat getDiffStat() {\r
+ return diffStat;\r
+ }\r
+}\r
package com.gitblit.utils;\r
\r
import java.io.ByteArrayOutputStream;\r
+import java.io.Serializable;\r
import java.text.MessageFormat;\r
import java.util.ArrayList;\r
import java.util.List;\r
import org.slf4j.LoggerFactory;\r
\r
import com.gitblit.models.AnnotatedLine;\r
+import com.gitblit.models.PathModel.PathChangeModel;\r
\r
/**\r
* DiffUtils is a class of utility methods related to diff, patch, and blame.\r
- * \r
+ *\r
* The diff methods support pluggable diff output types like Gitblit, Gitweb,\r
* and Plain.\r
- * \r
+ *\r
* @author James Moger\r
- * \r
+ *\r
*/\r
public class DiffUtils {\r
\r
return null;\r
}\r
}\r
+ \r
+ /**\r
+ * Encapsulates the output of a diff. \r
+ */\r
+ public static class DiffOutput implements Serializable {\r
+ private static final long serialVersionUID = 1L;\r
+ \r
+ public final DiffOutputType type;\r
+ public final String content;\r
+ public final DiffStat stat;\r
+ \r
+ DiffOutput(DiffOutputType type, String content, DiffStat stat) {\r
+ this.type = type;\r
+ this.content = content;\r
+ this.stat = stat;\r
+ }\r
+ \r
+ public PathChangeModel getPath(String path) {\r
+ if (stat == null) {\r
+ return null;\r
+ }\r
+ return stat.getPath(path);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Class that represents the number of insertions and deletions from a\r
+ * chunk.\r
+ */\r
+ public static class DiffStat implements Serializable {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+ \r
+ public final List<PathChangeModel> paths = new ArrayList<PathChangeModel>();\r
+ \r
+ private final String commitId;\r
+ \r
+ public DiffStat(String commitId) {\r
+ this.commitId = commitId;\r
+ }\r
+ \r
+ public PathChangeModel addPath(DiffEntry entry) {\r
+ PathChangeModel pcm = PathChangeModel.from(entry, commitId);\r
+ paths.add(pcm);\r
+ return pcm;\r
+ }\r
+\r
+ public int getInsertions() {\r
+ int val = 0;\r
+ for (PathChangeModel entry : paths) {\r
+ val += entry.insertions;\r
+ }\r
+ return val;\r
+ }\r
+\r
+ public int getDeletions() {\r
+ int val = 0;\r
+ for (PathChangeModel entry : paths) {\r
+ val += entry.deletions;\r
+ }\r
+ return val;\r
+ }\r
+ \r
+ public PathChangeModel getPath(String path) {\r
+ PathChangeModel stat = null;\r
+ for (PathChangeModel p : paths) {\r
+ if (p.path.equals(path)) {\r
+ stat = p;\r
+ break;\r
+ }\r
+ }\r
+ return stat;\r
+ } \r
+\r
+ @Override\r
+ public String toString() {\r
+ StringBuilder sb = new StringBuilder();\r
+ for (PathChangeModel entry : paths) {\r
+ sb.append(entry.toString()).append('\n');\r
+ }\r
+ sb.setLength(sb.length() - 1);\r
+ return sb.toString();\r
+ }\r
+ }\r
+ \r
+ public static class NormalizedDiffStat implements Serializable {\r
+ \r
+ private static final long serialVersionUID = 1L;\r
+ \r
+ public final int insertions;\r
+ public final int deletions;\r
+ public final int blanks;\r
+ \r
+ NormalizedDiffStat(int insertions, int deletions, int blanks) {\r
+ this.insertions = insertions;\r
+ this.deletions = deletions;\r
+ this.blanks = blanks;\r
+ }\r
+ }\r
\r
/**\r
* Returns the complete diff of the specified commit compared to its primary\r
* parent.\r
- * \r
+ *\r
* @param repository\r
* @param commit\r
* @param outputType\r
- * @return the diff as a string\r
+ * @return the diff\r
*/\r
- public static String getCommitDiff(Repository repository, RevCommit commit,\r
+ public static DiffOutput getCommitDiff(Repository repository, RevCommit commit,\r
DiffOutputType outputType) {\r
return getDiff(repository, null, commit, null, outputType);\r
}\r
/**\r
* Returns the diff for the specified file or folder from the specified\r
* commit compared to its primary parent.\r
- * \r
+ *\r
* @param repository\r
* @param commit\r
* @param path\r
* @param outputType\r
- * @return the diff as a string\r
+ * @return the diff\r
*/\r
- public static String getDiff(Repository repository, RevCommit commit, String path,\r
+ public static DiffOutput getDiff(Repository repository, RevCommit commit, String path,\r
DiffOutputType outputType) {\r
return getDiff(repository, null, commit, path, outputType);\r
}\r
\r
/**\r
* Returns the complete diff between the two specified commits.\r
- * \r
+ *\r
* @param repository\r
* @param baseCommit\r
* @param commit\r
* @param outputType\r
- * @return the diff as a string\r
+ * @return the diff\r
*/\r
- public static String getDiff(Repository repository, RevCommit baseCommit, RevCommit commit,\r
+ public static DiffOutput getDiff(Repository repository, RevCommit baseCommit, RevCommit commit,\r
DiffOutputType outputType) {\r
return getDiff(repository, baseCommit, commit, null, outputType);\r
}\r
\r
/**\r
* Returns the diff between two commits for the specified file.\r
- * \r
+ *\r
* @param repository\r
* @param baseCommit\r
* if base commit is null the diff is to the primary parent of\r
* if the path is specified, the diff is restricted to that file\r
* or folder. if unspecified, the diff is for the entire commit.\r
* @param outputType\r
- * @return the diff as a string\r
+ * @return the diff\r
*/\r
- public static String getDiff(Repository repository, RevCommit baseCommit, RevCommit commit,\r
+ public static DiffOutput getDiff(Repository repository, RevCommit baseCommit, RevCommit commit,\r
String path, DiffOutputType outputType) {\r
+ DiffStat stat = null;\r
String diff = null;\r
try {\r
final ByteArrayOutputStream os = new ByteArrayOutputStream();\r
DiffFormatter df;\r
switch (outputType) {\r
case HTML:\r
- df = new GitBlitDiffFormatter(os);\r
+ df = new GitBlitDiffFormatter(os, commit.getName());\r
break;\r
case PLAIN:\r
default:\r
if (df instanceof GitBlitDiffFormatter) {\r
// workaround for complex private methods in DiffFormatter\r
diff = ((GitBlitDiffFormatter) df).getHtml();\r
+ stat = ((GitBlitDiffFormatter) df).getDiffStat();\r
} else {\r
diff = os.toString();\r
}\r
} catch (Throwable t) {\r
LOGGER.error("failed to generate commit diff!", t);\r
}\r
- return diff;\r
+ \r
+ return new DiffOutput(outputType, diff, stat);\r
}\r
\r
/**\r
* Returns the diff between the two commits for the specified file or folder\r
* formatted as a patch.\r
- * \r
+ *\r
* @param repository\r
* @param baseCommit\r
* if base commit is unspecified, the patch is generated against\r
return diff;\r
}\r
\r
+ public static DiffStat getDiffStat(Repository repository, RevCommit commit) {\r
+ return getDiffStat(repository, null, commit, null);\r
+ }\r
+\r
+ /**\r
+ * Returns the diffstat between the two commits for the specified file or folder.\r
+ *\r
+ * @param repository\r
+ * @param baseCommit\r
+ * if base commit is unspecified, the diffstat is generated against\r
+ * the primary parent of the specified commit.\r
+ * @param commit\r
+ * @param path\r
+ * if path is specified, the diffstat is generated only for the\r
+ * specified file or folder. if unspecified, the diffstat is\r
+ * generated for the entire diff between the two commits.\r
+ * @return patch as a string\r
+ */\r
+ public static DiffStat getDiffStat(Repository repository, RevCommit baseCommit,\r
+ RevCommit commit, String path) {\r
+ DiffStat stat = null;\r
+ try {\r
+ RawTextComparator cmp = RawTextComparator.DEFAULT;\r
+ DiffStatFormatter df = new DiffStatFormatter(commit.getName());\r
+ df.setRepository(repository);\r
+ df.setDiffComparator(cmp);\r
+ df.setDetectRenames(true);\r
+\r
+ RevTree commitTree = commit.getTree();\r
+ RevTree baseTree;\r
+ if (baseCommit == null) {\r
+ if (commit.getParentCount() > 0) {\r
+ final RevWalk rw = new RevWalk(repository);\r
+ RevCommit parent = rw.parseCommit(commit.getParent(0).getId());\r
+ baseTree = parent.getTree();\r
+ } else {\r
+ // FIXME initial commit. no parent?!\r
+ baseTree = commitTree;\r
+ }\r
+ } else {\r
+ baseTree = baseCommit.getTree();\r
+ }\r
+\r
+ List<DiffEntry> diffEntries = df.scan(baseTree, commitTree);\r
+ if (path != null && path.length() > 0) {\r
+ for (DiffEntry diffEntry : diffEntries) {\r
+ if (diffEntry.getNewPath().equalsIgnoreCase(path)) {\r
+ df.format(diffEntry);\r
+ break;\r
+ }\r
+ }\r
+ } else {\r
+ df.format(diffEntries);\r
+ }\r
+ stat = df.getDiffStat();\r
+ df.flush();\r
+ } catch (Throwable t) {\r
+ LOGGER.error("failed to generate commit diff!", t);\r
+ }\r
+ return stat;\r
+ }\r
+\r
/**\r
* Returns the list of lines in the specified source file annotated with the\r
* source commit metadata.\r
- * \r
+ *\r
* @param repository\r
* @param blobPath\r
* @param objectId\r
}\r
return lines;\r
}\r
+ \r
+ /**\r
+ * Normalizes a diffstat to an N-segment display.\r
+ * \r
+ * @params segments\r
+ * @param insertions\r
+ * @param deletions\r
+ * @return a normalized diffstat\r
+ */\r
+ public static NormalizedDiffStat normalizeDiffStat(final int segments, final int insertions, final int deletions) {\r
+ final int total = insertions + deletions;\r
+ final float fi = ((float) insertions) / total;\r
+ int si;\r
+ int sd;\r
+ int sb;\r
+ if (deletions == 0) {\r
+ // only addition\r
+ si = Math.min(insertions, segments);\r
+ sd = 0;\r
+ sb = si < segments ? (segments - si) : 0;\r
+ } else if (insertions == 0) {\r
+ // only deletion\r
+ si = 0;\r
+ sd = Math.min(deletions, segments);\r
+ sb = sd < segments ? (segments - sd) : 0;\r
+ } else if (total <= segments) {\r
+ // total churn fits in segment display\r
+ si = insertions;\r
+ sd = deletions;\r
+ sb = segments - total;\r
+ } else if ((segments % 2) > 0 && fi > 0.45f && fi < 0.55f) {\r
+ // odd segment display, fairly even +/-, use even number of segments\r
+ si = Math.round(((float) insertions)/total * (segments - 1));\r
+ sd = segments - 1 - si;\r
+ sb = 1;\r
+ } else {\r
+ si = Math.round(((float) insertions)/total * segments);\r
+ sd = segments - si;\r
+ sb = 0;\r
+ }\r
+ \r
+ return new NormalizedDiffStat(si, sd, sb);\r
+ }\r
}\r
import java.io.OutputStream;\r
import java.text.MessageFormat;\r
\r
+import org.eclipse.jgit.diff.DiffEntry;\r
import org.eclipse.jgit.diff.DiffFormatter;\r
import org.eclipse.jgit.diff.RawText;\r
import org.eclipse.jgit.util.RawParseUtils;\r
\r
+import com.gitblit.models.PathModel.PathChangeModel;\r
+import com.gitblit.utils.DiffUtils.DiffStat;\r
+\r
/**\r
- * Generates an html snippet of a diff in Gitblit's style.\r
+ * Generates an html snippet of a diff in Gitblit's style, tracks changed paths,\r
+ * and calculates diff stats.\r
* \r
* @author James Moger\r
* \r
\r
private final OutputStream os;\r
\r
- private int left, right;\r
+ private final DiffStat diffStat;\r
+\r
+ private PathChangeModel currentPath;\r
\r
- public GitBlitDiffFormatter(OutputStream os) {\r
+ private int left, right;\r
+ \r
+ public GitBlitDiffFormatter(OutputStream os, String commitId) {\r
super(os);\r
this.os = os;\r
+ this.diffStat = new DiffStat(commitId);\r
}\r
-\r
+ \r
+ @Override\r
+ public void format(DiffEntry ent) throws IOException {\r
+ currentPath = diffStat.addPath(ent);\r
+ super.format(ent);\r
+ }\r
+ \r
/**\r
* Output a hunk header\r
* \r
@Override\r
protected void writeLine(final char prefix, final RawText text, final int cur)\r
throws IOException {\r
+ // update entry diffstat\r
+ currentPath.update(prefix);\r
+ \r
+ // output diff\r
os.write("<tr>".getBytes());\r
switch (prefix) {\r
case '+':\r
sb.append("</table></div>");\r
return sb.toString();\r
}\r
+ \r
+ public DiffStat getDiffStat() {\r
+ return diffStat;\r
+ }\r
}\r
import org.eclipse.jgit.treewalk.filter.PathSuffixFilter;\r
import org.eclipse.jgit.treewalk.filter.TreeFilter;\r
import org.eclipse.jgit.util.FS;\r
-import org.eclipse.jgit.util.io.DisabledOutputStream;\r
import org.slf4j.Logger;\r
import org.slf4j.LoggerFactory;\r
\r
Collections.sort(list);\r
return list;\r
}\r
-\r
+ \r
/**\r
* Returns the list of files changed in a specified commit. If the\r
* repository does not exist or is empty, an empty list is returned.\r
* @return list of files changed in a commit\r
*/\r
public static List<PathChangeModel> getFilesInCommit(Repository repository, RevCommit commit) {\r
+ return getFilesInCommit(repository, commit, true);\r
+ }\r
+\r
+ /**\r
+ * Returns the list of files changed in a specified commit. If the\r
+ * repository does not exist or is empty, an empty list is returned.\r
+ * \r
+ * @param repository\r
+ * @param commit\r
+ * if null, HEAD is assumed.\r
+ * @param calculateDiffStat\r
+ * if true, each PathChangeModel will have insertions/deletions\r
+ * @return list of files changed in a commit\r
+ */\r
+ public static List<PathChangeModel> getFilesInCommit(Repository repository, RevCommit commit, boolean calculateDiffStat) {\r
List<PathChangeModel> list = new ArrayList<PathChangeModel>();\r
if (!hasCommits(repository)) {\r
return list;\r
tw.release();\r
} else {\r
RevCommit parent = rw.parseCommit(commit.getParent(0).getId());\r
- DiffFormatter df = new DiffFormatter(DisabledOutputStream.INSTANCE);\r
+ DiffStatFormatter df = new DiffStatFormatter(commit.getName());\r
df.setRepository(repository);\r
df.setDiffComparator(RawTextComparator.DEFAULT);\r
df.setDetectRenames(true);\r
List<DiffEntry> diffs = df.scan(parent.getTree(), commit.getTree());\r
for (DiffEntry diff : diffs) {\r
- String objectId = diff.getNewId().name();\r
- if (diff.getChangeType().equals(ChangeType.DELETE)) {\r
- list.add(new PathChangeModel(diff.getOldPath(), diff.getOldPath(), 0, diff\r
- .getNewMode().getBits(), objectId, commit.getId().getName(), diff\r
- .getChangeType()));\r
- } else if (diff.getChangeType().equals(ChangeType.RENAME)) {\r
- list.add(new PathChangeModel(diff.getOldPath(), diff.getNewPath(), 0, diff\r
- .getNewMode().getBits(), objectId, commit.getId().getName(), diff\r
- .getChangeType()));\r
- } else {\r
- list.add(new PathChangeModel(diff.getNewPath(), diff.getNewPath(), 0, diff\r
- .getNewMode().getBits(), objectId, commit.getId().getName(), diff\r
- .getChangeType()));\r
+ // create the path change model\r
+ PathChangeModel pcm = PathChangeModel.from(diff, commit.getName());\r
+ \r
+ if (calculateDiffStat) {\r
+ // update file diffstats\r
+ df.format(diff);\r
+ PathChangeModel pathStat = df.getDiffStat().getPath(pcm.path);\r
+ if (pathStat != null) {\r
+ pcm.insertions = pathStat.insertions;\r
+ pcm.deletions = pathStat.deletions;\r
+ }\r
}\r
+ list.add(pcm);\r
}\r
}\r
} catch (Throwable t) {\r
\r
List<DiffEntry> diffEntries = df.scan(startCommit.getTree(), endCommit.getTree());\r
for (DiffEntry diff : diffEntries) {\r
- \r
- if (diff.getChangeType().equals(ChangeType.DELETE)) {\r
- list.add(new PathChangeModel(diff.getOldPath(), diff.getOldPath(), 0, diff\r
- .getNewMode().getBits(), diff.getOldId().name(), null, diff\r
- .getChangeType()));\r
- } else if (diff.getChangeType().equals(ChangeType.RENAME)) {\r
- list.add(new PathChangeModel(diff.getOldPath(), diff.getNewPath(), 0, diff\r
- .getNewMode().getBits(), diff.getNewId().name(), null, diff\r
- .getChangeType()));\r
- } else {\r
- list.add(new PathChangeModel(diff.getNewPath(), diff.getNewPath(), 0, diff\r
- .getNewMode().getBits(), diff.getNewId().name(), null, diff\r
- .getChangeType()));\r
- }\r
+ PathChangeModel pcm = PathChangeModel.from(diff, null);\r
+ list.add(pcm);\r
} \r
Collections.sort(list);\r
} catch (Throwable t) {\r
gb.todaysActivityNone = today / none
gb.noActivityToday = there has been no activity today
gb.anonymousUser= anonymous
-gb.commitMessageRenderer = commit message renderer
\ No newline at end of file
+gb.commitMessageRenderer = commit message renderer
+gb.diffStat = {0} insertions & {1} deletions
\ No newline at end of file
String diff;\r
if (StringUtils.isEmpty(baseObjectId)) {\r
// use first parent\r
- diff = DiffUtils.getDiff(r, commit, blobPath, DiffOutputType.HTML);\r
+ diff = DiffUtils.getDiff(r, commit, blobPath, DiffOutputType.HTML).content;\r
add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class,\r
WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));\r
} else {\r
// base commit specified\r
RevCommit baseCommit = JGitUtils.getCommit(r, baseObjectId);\r
- diff = DiffUtils.getDiff(r, baseCommit, commit, blobPath, DiffOutputType.HTML);\r
+ diff = DiffUtils.getDiff(r, baseCommit, commit, blobPath, DiffOutputType.HTML).content;\r
add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class,\r
WicketUtils.newBlobDiffParameter(repositoryName, baseObjectId, objectId,\r
blobPath)));\r
<div class="hidden-phone" style="text-align:right;" wicket:id="commitLegend"></div>\r
\r
<!-- changed paths -->\r
- <div class="header"><i class="icon-file"></i> <wicket:message key="gb.changedFiles">[changed files]</wicket:message></div>\r
+ <div class="header"><span wicket:id="diffStat"></span><span style="padding-left:5px;"><wicket:message key="gb.changedFiles">[changed files]</wicket:message></span></div>\r
\r
<table class="pretty">\r
<tr wicket:id="changedPath">\r
<td class="changeType"><span wicket:id="changeType">[change type]</span></td> \r
<td class="path"><span wicket:id="pathName">[commit path]</span></td> \r
<td class="hidden-phone rightAlign">\r
+ <span class="hidden-tablet" style="padding-right:20px;" wicket:id="diffStat"></span>\r
<span class="link">\r
<a wicket:id="patch"><wicket:message key="gb.patch"></wicket:message></a> | <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="blame"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a>\r
</span>\r
import com.gitblit.Constants;
import com.gitblit.GitBlit;
-import com.gitblit.models.PathModel.PathChangeModel;
import com.gitblit.models.GitNote;
+import com.gitblit.models.PathModel.PathChangeModel;
import com.gitblit.models.SubmoduleModel;
import com.gitblit.utils.DiffUtils;
+import com.gitblit.utils.DiffUtils.DiffOutput;
import com.gitblit.utils.DiffUtils.DiffOutputType;
import com.gitblit.utils.JGitUtils;
import com.gitblit.wicket.CacheControl;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.CommitHeaderPanel;
import com.gitblit.wicket.panels.CommitLegendPanel;
+import com.gitblit.wicket.panels.DiffStatPanel;
import com.gitblit.wicket.panels.GravatarImage;
import com.gitblit.wicket.panels.LinkPanel;
import com.gitblit.wicket.panels.RefsPanel;
RevCommit commit = getCommit();
- String diff = DiffUtils.getCommitDiff(r, commit, DiffOutputType.HTML);
+ final DiffOutput diff = DiffUtils.getCommitDiff(r, commit, DiffOutputType.HTML);
List<String> parents = new ArrayList<String>();
if (commit.getParentCount() > 0) {
add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
+ // add commit diffstat
+ int insertions = 0;
+ int deletions = 0;
+ for (PathChangeModel pcm : diff.stat.paths) {
+ insertions += pcm.insertions;
+ deletions += pcm.deletions;
+ }
+ add(new DiffStatPanel("diffStat", insertions, deletions));
+
addFullText("fullMessage", commit.getFullMessage());
// git notes
add(notesView.setVisible(notes.size() > 0));
// changed paths list
- List<PathChangeModel> paths = JGitUtils.getFilesInCommit(r, commit);
-
- add(new CommitLegendPanel("commitLegend", paths));
- ListDataProvider<PathChangeModel> pathsDp = new ListDataProvider<PathChangeModel>(paths);
+ add(new CommitLegendPanel("commitLegend", diff.stat.paths));
+ ListDataProvider<PathChangeModel> pathsDp = new ListDataProvider<PathChangeModel>(diff.stat.paths);
DataView<PathChangeModel> pathsView = new DataView<PathChangeModel>("changedPath", pathsDp) {
private static final long serialVersionUID = 1L;
int counter;
WicketUtils.setChangeTypeCssClass(changeType, entry.changeType);
setChangeTypeTooltip(changeType, entry.changeType);
item.add(changeType);
+ item.add(new DiffStatPanel("diffStat", entry.insertions, entry.deletions, true));
boolean hasSubmodule = false;
String submodulePath = null;
}
};
add(pathsView);
- add(new Label("diffText", diff).setEscapeModelStrings(false));
+ add(new Label("diffText", diff.content).setEscapeModelStrings(false));
}
@Override
<div class="hidden-phone" style="text-align:right;" wicket:id="commitLegend"></div>\r
\r
<!-- header -->\r
- <div class="header"><i class="icon-file"></i> <wicket:message key="gb.changedFiles">[changed files]</wicket:message></div>\r
+ <div class="header"><span wicket:id="diffStat"></span><span style="padding-left:5px;"><wicket:message key="gb.changedFiles">[changed files]</wicket:message></span></div>\r
\r
<!-- changed paths -->\r
<table class="pretty">\r
<td class="changeType"><span wicket:id="changeType">[change type]</span></td>\r
<td class="path"><span wicket:id="pathName">[commit path]</span></td> \r
<td class="hidden-phone rightAlign">\r
+ <span class="hidden-tablet" style="padding-right:20px;" wicket:id="diffStat"></span>\r
<span class="link">\r
<a wicket:id="diff"><wicket:message key="gb.diff"></wicket:message></a> | <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="blame"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a>\r
</span>\r
import com.gitblit.wicket.panels.CommitHeaderPanel;\r
import com.gitblit.wicket.panels.CommitLegendPanel;\r
import com.gitblit.wicket.panels.CompressedDownloadsPanel;\r
+import com.gitblit.wicket.panels.DiffStatPanel;\r
import com.gitblit.wicket.panels.GravatarImage;\r
import com.gitblit.wicket.panels.LinkPanel;\r
import com.gitblit.wicket.panels.RefsPanel;\r
\r
// changed paths list\r
List<PathChangeModel> paths = JGitUtils.getFilesInCommit(r, c);\r
+ \r
+ // add commit diffstat\r
+ int insertions = 0;\r
+ int deletions = 0;\r
+ for (PathChangeModel pcm : paths) {\r
+ insertions += pcm.insertions;\r
+ deletions += pcm.deletions;\r
+ }\r
+ add(new DiffStatPanel("diffStat", insertions, deletions));\r
+ \r
add(new CommitLegendPanel("commitLegend", paths));\r
ListDataProvider<PathChangeModel> pathsDp = new ListDataProvider<PathChangeModel>(paths);\r
DataView<PathChangeModel> pathsView = new DataView<PathChangeModel>("changedPath", pathsDp) {\r
WicketUtils.setChangeTypeCssClass(changeType, entry.changeType);\r
setChangeTypeTooltip(changeType, entry.changeType);\r
item.add(changeType);\r
- \r
+ item.add(new DiffStatPanel("diffStat", entry.insertions, entry.deletions, true));\r
+\r
boolean hasSubmodule = false;\r
String submodulePath = null;\r
if (entry.isTree()) {\r
<div>\r
<!-- commit legend -->\r
<div class="hidden-phone" style="text-align:right;" wicket:id="commitLegend"></div>\r
- <div class="header"><i class="icon-file"></i> <wicket:message key="gb.changedFiles">[changed files]</wicket:message></div>\r
+ <div class="header"><span wicket:id="diffStat"></span><span style="padding-left:5px;"><wicket:message key="gb.changedFiles">[changed files]</wicket:message></span></div>\r
</div>\r
<table class="pretty">\r
<tr wicket:id="changedPath">\r
<td class="changeType"><span wicket:id="changeType">[change type]</span></td> \r
<td class="path"><span wicket:id="pathName">[commit path]</span></td> \r
<td class="hidden-phone rightAlign">\r
+ <span class="hidden-tablet" style="padding-right:20px;" wicket:id="diffStat"></span>\r
<span class="link">\r
<a wicket:id="patch"><wicket:message key="gb.patch"></wicket:message></a> | <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="blame"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a>\r
</span>\r
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.SubmoduleModel;
import com.gitblit.utils.DiffUtils;
+import com.gitblit.utils.DiffUtils.DiffOutput;
import com.gitblit.utils.DiffUtils.DiffOutputType;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.SessionlessForm;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.CommitLegendPanel;
+import com.gitblit.wicket.panels.DiffStatPanel;
import com.gitblit.wicket.panels.LinkPanel;
import com.gitblit.wicket.panels.LogPanel;
fromCommitId.setObject(startId);
toCommitId.setObject(endId);
- String diff = DiffUtils.getDiff(r, fromCommit, toCommit, DiffOutputType.HTML);
+ final DiffOutput diff = DiffUtils.getDiff(r, fromCommit, toCommit, DiffOutputType.HTML);
+
+ // add compare diffstat
+ int insertions = 0;
+ int deletions = 0;
+ for (PathChangeModel pcm : diff.stat.paths) {
+ insertions += pcm.insertions;
+ deletions += pcm.deletions;
+ }
+ comparison.add(new DiffStatPanel("diffStat", insertions, deletions));
// compare page links
// comparison.add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class,
comparison.add(new LogPanel("commitList", repositoryName, objectId, r, 0, 0, repository.showRemoteBranches));
// changed paths list
- List<PathChangeModel> paths = JGitUtils.getFilesInRange(r, fromCommit, toCommit);
-
- comparison.add(new CommitLegendPanel("commitLegend", paths));
- ListDataProvider<PathChangeModel> pathsDp = new ListDataProvider<PathChangeModel>(paths);
+ comparison.add(new CommitLegendPanel("commitLegend", diff.stat.paths));
+ ListDataProvider<PathChangeModel> pathsDp = new ListDataProvider<PathChangeModel>(diff.stat.paths);
DataView<PathChangeModel> pathsView = new DataView<PathChangeModel>("changedPath", pathsDp) {
private static final long serialVersionUID = 1L;
int counter;
WicketUtils.setChangeTypeCssClass(changeType, entry.changeType);
setChangeTypeTooltip(changeType, entry.changeType);
item.add(changeType);
+ item.add(new DiffStatPanel("diffStat", entry.insertions, entry.deletions, true));
boolean hasSubmodule = false;
String submodulePath = null;
}
};
comparison.add(pathsView);
- comparison.add(new Label("diffText", diff).setEscapeModelStrings(false));
+ comparison.add(new Label("diffText", diff.content).setEscapeModelStrings(false));
}
//
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" \r
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" \r
+ xml:lang="en" \r
+ lang="en">\r
+<wicket:panel>\r
+<span class="hidden-phone hidden-tablet diffstat-total" wicket:id="total">[total]</span>\r
+<span class="diffstat-segments">\r
+ <i class="diffstat-add" wicket:id="insertions"></i><i class="diffstat-delete" wicket:id="deletions"></i><i wicket:id="blank"></i>\r
+</span>\r
+</wicket:panel>\r
+</html>
\ No newline at end of file
--- /dev/null
+/*\r
+ * Copyright 2013 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.wicket.panels;\r
+\r
+import java.text.MessageFormat;\r
+\r
+import org.apache.wicket.markup.html.basic.Label;\r
+import org.apache.wicket.markup.html.panel.Panel;\r
+\r
+import com.gitblit.utils.DiffUtils;\r
+import com.gitblit.utils.DiffUtils.NormalizedDiffStat;\r
+import com.gitblit.wicket.WicketUtils;\r
+\r
+/**\r
+ * Display a diffstat.\r
+ * \r
+ * @author James Moger\r
+ *\r
+ */\r
+public class DiffStatPanel extends Panel {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ final int total;\r
+\r
+ final int insertions;\r
+\r
+ final int deletions;\r
+\r
+ final boolean inline;\r
+\r
+ public DiffStatPanel(String wicketId, int insertions, int deletions) {\r
+ this(wicketId, insertions, deletions, false);\r
+ }\r
+\r
+ public DiffStatPanel(String wicketId, int insertions, int deletions, boolean inline) {\r
+ super(wicketId);\r
+ this.insertions = insertions;\r
+ this.deletions = deletions;\r
+ this.total = insertions + deletions;\r
+ this.inline = inline;\r
+ }\r
+\r
+ @Override\r
+ protected void onInitialize() {\r
+ super.onInitialize();\r
+\r
+ final String diffStat = MessageFormat.format(getString("gb.diffStat"), "" + insertions, "" + deletions);\r
+ WicketUtils.setHtmlTooltip(this, diffStat);\r
+\r
+ final NormalizedDiffStat n = DiffUtils.normalizeDiffStat(5, insertions, deletions);\r
+ \r
+ final String square = "■";\r
+ add(new Label("total", String.valueOf(total)));\r
+ add(new Label("insertions", timesRepeat(n.insertions, square)).setEscapeModelStrings(false).setVisible(n.insertions > 0));\r
+ add(new Label("deletions", timesRepeat(n.deletions, square)).setEscapeModelStrings(false).setVisible(n.deletions > 0));\r
+ add(new Label("blank", timesRepeat(n.blanks, square)).setEscapeModelStrings(false).setVisible(n.blanks > 0));\r
+\r
+ if (inline) {\r
+ WicketUtils.setCssClass(this, "diffstat-inline");\r
+ } else {\r
+ WicketUtils.setCssClass(this, "diffstat");\r
+ }\r
+\r
+ setVisible(total > 0);\r
+ }\r
+\r
+ String timesRepeat(int cnt, String s) {\r
+ StringBuilder sb = new StringBuilder();\r
+ for (int i = 0; i < cnt; i++) {\r
+ sb.append(s);\r
+ }\r
+ return sb.toString();\r
+ }\r
+}\r
background-color: #fafafa;\r
}\r
\r
+.diffstat {\r
+ padding: 1px 5px;\r
+ font-size: smaller;\r
+ background-color: #f5f5f5;\r
+ border: 1px solid #ccc;\r
+ color: #ccc;\r
+ font-weight:bold;\r
+}\r
+\r
+.diffstat-inline {\r
+ font-size: smaller;\r
+ color: #ccc;\r
+ font-weight:bold; \r
+}\r
+\r
+.diffstat .diffstat-total {\r
+ color: black;\r
+ border-right: 1px solid #ccc;\r
+ padding-right: 4px;\r
+ margin-right: 2px;\r
+}\r
+\r
+.diffstat-inline .diffstat-total {\r
+ color: #999;\r
+ padding-right: 2px;\r
+}\r
+\r
+.diffstat-segments {\r
+ vertical-align: top;\r
+}\r
+\r
+.diffstat-add {\r
+ color: #629E62; \r
+}\r
+\r
+.diffstat-delete {\r
+ color: #B9583B; \r
+}\r
+\r
h1 small, h2 small, h3 small, h4 small, h5 small, h6 small {\r
color: #888;\r
}\r
Repository repository = GitBlitSuite.getHelloworldRepository();\r
RevCommit commit = JGitUtils.getCommit(repository,\r
"1d0c2933a4ae69c362f76797d42d6bd182d05176");\r
- String diff = DiffUtils.getCommitDiff(repository, commit, DiffOutputType.PLAIN);\r
+ String diff = DiffUtils.getCommitDiff(repository, commit, DiffOutputType.PLAIN).content;\r
repository.close();\r
assertTrue(diff != null && diff.length() > 0);\r
String expected = "- system.out.println(\"Hello World\");\n+ System.out.println(\"Hello World\"";\r
"8baf6a833b5579384d9b9ceb8a16b5d0ea2ec4ca");\r
RevCommit commit = JGitUtils.getCommit(repository,\r
"1d0c2933a4ae69c362f76797d42d6bd182d05176");\r
- String diff = DiffUtils.getDiff(repository, baseCommit, commit, DiffOutputType.PLAIN);\r
+ String diff = DiffUtils.getDiff(repository, baseCommit, commit, DiffOutputType.PLAIN).content;\r
repository.close();\r
assertTrue(diff != null && diff.length() > 0);\r
String expected = "- system.out.println(\"Hello World\");\n+ System.out.println(\"Hello World\"";\r
Repository repository = GitBlitSuite.getHelloworldRepository();\r
RevCommit commit = JGitUtils.getCommit(repository,\r
"1d0c2933a4ae69c362f76797d42d6bd182d05176");\r
- String diff = DiffUtils.getDiff(repository, commit, "java.java", DiffOutputType.PLAIN);\r
+ String diff = DiffUtils.getDiff(repository, commit, "java.java", DiffOutputType.PLAIN).content;\r
repository.close();\r
assertTrue(diff != null && diff.length() > 0);\r
String expected = "- system.out.println(\"Hello World\");\n+ System.out.println(\"Hello World\"";\r