blob: 7e7ac2f5db4bb65d85c6af4bddd7422e3e7c1f51 [file] [log] [blame]
/*
* 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.pages;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.protocol.http.RequestUtils;
import org.apache.wicket.request.target.basic.RedirectRequestTarget;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.Keys;
import com.gitblit.models.PathModel.PathChangeModel;
import com.gitblit.models.RefModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.SubmoduleModel;
import com.gitblit.servlet.RawServlet;
import com.gitblit.utils.DiffUtils;
import com.gitblit.utils.DiffUtils.DiffComparator;
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;
/**
* The compare page allows you to compare two branches, tags, or hash ids.
*
* @author James Moger
*
*/
public class ComparePage extends RepositoryPage {
IModel<String> fromCommitId = new Model<String>("");
IModel<String> toCommitId = new Model<String>("");
IModel<String> fromRefId = new Model<String>("");
IModel<String> toRefId = new Model<String>("");
IModel<Boolean> ignoreWhitespace = Model.of(true);
public ComparePage(PageParameters params) {
super(params);
Repository r = getRepository();
RepositoryModel repository = getRepositoryModel();
if (StringUtils.isEmpty(objectId)) {
// seleciton form
add(new Label("comparison").setVisible(false));
} else {
// active comparison
Fragment comparison = new Fragment("comparison", "comparisonFragment", this);
add(comparison);
RevCommit fromCommit;
RevCommit toCommit;
String[] parts = objectId.split("\\.\\.");
if (parts[0].startsWith("refs/") && parts[1].startsWith("refs/")) {
// set the ref models
fromRefId.setObject(parts[0]);
toRefId.setObject(parts[1]);
fromCommit = getCommit(r, fromRefId.getObject());
toCommit = getCommit(r, toRefId.getObject());
} else {
// set the id models
fromCommitId.setObject(parts[0]);
toCommitId.setObject(parts[1]);
fromCommit = getCommit(r, fromCommitId.getObject());
toCommit = getCommit(r, toCommitId.getObject());
}
// prepare submodules
getSubmodules(toCommit);
final String startId = fromCommit.getId().getName();
final String endId = toCommit.getId().getName();
// commit ids
fromCommitId.setObject(startId);
toCommitId.setObject(endId);
final List<String> imageExtensions = app().settings().getStrings(Keys.web.imageExtensions);
final ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName,
fromCommit.getName(), toCommit.getName(), imageExtensions);
final DiffComparator diffComparator = WicketUtils.getDiffComparator(params);
final int tabLength = app().settings().getInteger(Keys.web.tabLength, 4);
final DiffOutput diff = DiffUtils.getDiff(r, fromCommit, toCommit, diffComparator, DiffOutputType.HTML, handler, tabLength);
if (handler.getImgDiffCount() > 0) {
addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs
}
// 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,
// WicketUtils.newRangeParameter(repositoryName, fromCommitId.toString(), toCommitId.getObject())));
// display list of commits
comparison.add(new LogPanel("commitList", repositoryName, objectId, r, 0, 0, repository.showRemoteBranches));
// changed paths list
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;
@Override
public void populateItem(final Item<PathChangeModel> item) {
final PathChangeModel entry = item.getModelObject();
Label changeType = new Label("changeType", "");
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()) {
// tree
item.add(new LinkPanel("pathName", null, entry.path, TreePage.class,
WicketUtils
.newPathParameter(repositoryName, endId, entry.path)));
} else if (entry.isSubmodule()) {
// submodule
String submoduleId = entry.objectId;
SubmoduleModel submodule = getSubmodule(entry.path);
submodulePath = submodule.gitblitPath;
hasSubmodule = submodule.hasSubmodule;
// add relative link
item.add(new LinkPanel("pathName", "list", entry.path + " @ " + getShortObjectId(submoduleId), "#n" + entry.objectId));
} else {
// add relative link
item.add(new LinkPanel("pathName", "list", entry.path, "#n" + entry.objectId));
}
// quick links
if (entry.isSubmodule()) {
// submodule
item.add(new ExternalLink("patch", "").setEnabled(false));
item.add(new BookmarkablePageLink<Void>("view", CommitPage.class, WicketUtils
.newObjectParameter(submodulePath, entry.objectId)).setEnabled(hasSubmodule));
item.add(new ExternalLink("raw", "").setEnabled(false));
item.add(new ExternalLink("blame", "").setEnabled(false));
item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
.newPathParameter(repositoryName, endId, entry.path))
.setEnabled(!entry.changeType.equals(ChangeType.ADD)));
} else {
// tree or blob
item.add(new BookmarkablePageLink<Void>("patch", PatchPage.class, WicketUtils
.newBlobDiffParameter(repositoryName, startId, endId, entry.path))
.setEnabled(!entry.changeType.equals(ChangeType.DELETE)));
item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils
.newPathParameter(repositoryName, endId, entry.path))
.setEnabled(!entry.changeType.equals(ChangeType.DELETE)));
String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, endId, entry.path);
item.add(new ExternalLink("raw", rawUrl)
.setEnabled(!entry.changeType.equals(ChangeType.DELETE)));
item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
.newPathParameter(repositoryName, endId, entry.path))
.setEnabled(!entry.changeType.equals(ChangeType.ADD)
&& !entry.changeType.equals(ChangeType.DELETE)));
item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
.newPathParameter(repositoryName, endId, entry.path))
.setEnabled(!entry.changeType.equals(ChangeType.ADD)));
}
WicketUtils.setAlternatingBackground(item, counter);
counter++;
}
};
comparison.add(pathsView);
comparison.add(new Label("diffText", diff.content).setEscapeModelStrings(false));
}
// set the default DiffComparator
DiffComparator diffComparator = WicketUtils.getDiffComparator(params);
ignoreWhitespace.setObject(DiffComparator.IGNORE_WHITESPACE == diffComparator);
//
// ref selection form
//
SessionlessForm<Void> refsForm = new SessionlessForm<Void>("compareRefsForm", getClass(), getPageParameters()) {
private static final long serialVersionUID = 1L;
@Override
public void onSubmit() {
String from = ComparePage.this.fromRefId.getObject();
String to = ComparePage.this.toRefId.getObject();
boolean ignoreWS = ignoreWhitespace.getObject();
PageParameters params = WicketUtils.newRangeParameter(repositoryName, from, to);
if (ignoreWS) {
params.put("w", 1);
}
String relativeUrl = urlFor(ComparePage.class, params).toString();
String absoluteUrl = RequestUtils.toAbsolutePath(relativeUrl);
getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl));
}
};
List<String> refs = new ArrayList<String>();
for (RefModel ref : JGitUtils.getLocalBranches(r, true, -1)) {
refs.add(ref.getName());
}
if (repository.showRemoteBranches) {
for (RefModel ref : JGitUtils.getRemoteBranches(r, true, -1)) {
refs.add(ref.getName());
}
}
for (RefModel ref : JGitUtils.getTags(r, true, -1)) {
refs.add(ref.getName());
}
refsForm.add(new DropDownChoice<String>("fromRef", fromRefId, refs).setEnabled(refs.size() > 0));
refsForm.add(new DropDownChoice<String>("toRef", toRefId, refs).setEnabled(refs.size() > 0));
refsForm.add(new Label("ignoreWhitespaceLabel", getString(DiffComparator.IGNORE_WHITESPACE.getTranslationKey())));
refsForm.add(new CheckBox("ignoreWhitespaceCheckbox", ignoreWhitespace));
add(refsForm);
//
// manual ids form
//
SessionlessForm<Void> idsForm = new SessionlessForm<Void>("compareIdsForm", getClass(), getPageParameters()) {
private static final long serialVersionUID = 1L;
@Override
public void onSubmit() {
String from = ComparePage.this.fromCommitId.getObject();
String to = ComparePage.this.toCommitId.getObject();
boolean ignoreWS = ignoreWhitespace.getObject();
PageParameters params = WicketUtils.newRangeParameter(repositoryName, from, to);
if (ignoreWS) {
params.put("w", 1);
}
String relativeUrl = urlFor(ComparePage.class, params).toString();
String absoluteUrl = RequestUtils.toAbsolutePath(relativeUrl);
getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl));
}
};
TextField<String> fromIdField = new TextField<String>("fromId", fromCommitId);
WicketUtils.setInputPlaceholder(fromIdField, getString("gb.from") + "...");
idsForm.add(fromIdField);
TextField<String> toIdField = new TextField<String>("toId", toCommitId);
WicketUtils.setInputPlaceholder(toIdField, getString("gb.to") + "...");
idsForm.add(toIdField);
idsForm.add(new Label("ignoreWhitespaceLabel", getString(DiffComparator.IGNORE_WHITESPACE.getTranslationKey())));
idsForm.add(new CheckBox("ignoreWhitespaceCheckbox", ignoreWhitespace));
add(idsForm);
r.close();
}
@Override
protected String getPageName() {
return getString("gb.compare");
}
@Override
protected Class<? extends BasePage> getRepoNavPageClass() {
return ComparePage.class;
}
private RevCommit getCommit(Repository r, String rev)
{
RevCommit otherCommit = JGitUtils.getCommit(r, rev);
if (otherCommit == null) {
error(MessageFormat.format(getString("gb.failedToFindCommit"), rev, repositoryName, getPageName()), true);
}
return otherCommit;
}
}