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