You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ComparePage.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /*
  2. * Copyright 2013 gitblit.com.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.gitblit.wicket.pages;
  17. import java.text.MessageFormat;
  18. import java.util.ArrayList;
  19. import java.util.List;
  20. import org.apache.wicket.PageParameters;
  21. import org.apache.wicket.markup.html.basic.Label;
  22. import org.apache.wicket.markup.html.form.CheckBox;
  23. import org.apache.wicket.markup.html.form.DropDownChoice;
  24. import org.apache.wicket.markup.html.form.TextField;
  25. import org.apache.wicket.markup.html.link.BookmarkablePageLink;
  26. import org.apache.wicket.markup.html.link.ExternalLink;
  27. import org.apache.wicket.markup.html.panel.Fragment;
  28. import org.apache.wicket.markup.repeater.Item;
  29. import org.apache.wicket.markup.repeater.data.DataView;
  30. import org.apache.wicket.markup.repeater.data.ListDataProvider;
  31. import org.apache.wicket.model.IModel;
  32. import org.apache.wicket.model.Model;
  33. import org.apache.wicket.protocol.http.RequestUtils;
  34. import org.apache.wicket.request.target.basic.RedirectRequestTarget;
  35. import org.eclipse.jgit.diff.DiffEntry.ChangeType;
  36. import org.eclipse.jgit.lib.Repository;
  37. import org.eclipse.jgit.revwalk.RevCommit;
  38. import com.gitblit.Keys;
  39. import com.gitblit.models.PathModel.PathChangeModel;
  40. import com.gitblit.models.RefModel;
  41. import com.gitblit.models.RepositoryModel;
  42. import com.gitblit.models.SubmoduleModel;
  43. import com.gitblit.servlet.RawServlet;
  44. import com.gitblit.utils.DiffUtils;
  45. import com.gitblit.utils.DiffUtils.DiffComparator;
  46. import com.gitblit.utils.DiffUtils.DiffOutput;
  47. import com.gitblit.utils.DiffUtils.DiffOutputType;
  48. import com.gitblit.utils.JGitUtils;
  49. import com.gitblit.utils.StringUtils;
  50. import com.gitblit.wicket.SessionlessForm;
  51. import com.gitblit.wicket.WicketUtils;
  52. import com.gitblit.wicket.panels.CommitLegendPanel;
  53. import com.gitblit.wicket.panels.DiffStatPanel;
  54. import com.gitblit.wicket.panels.LinkPanel;
  55. import com.gitblit.wicket.panels.LogPanel;
  56. /**
  57. * The compare page allows you to compare two branches, tags, or hash ids.
  58. *
  59. * @author James Moger
  60. *
  61. */
  62. public class ComparePage extends RepositoryPage {
  63. IModel<String> fromCommitId = new Model<String>("");
  64. IModel<String> toCommitId = new Model<String>("");
  65. IModel<String> fromRefId = new Model<String>("");
  66. IModel<String> toRefId = new Model<String>("");
  67. IModel<Boolean> ignoreWhitespace = Model.of(true);
  68. public ComparePage(PageParameters params) {
  69. super(params);
  70. Repository r = getRepository();
  71. RepositoryModel repository = getRepositoryModel();
  72. if (StringUtils.isEmpty(objectId)) {
  73. // seleciton form
  74. add(new Label("comparison").setVisible(false));
  75. } else {
  76. // active comparison
  77. Fragment comparison = new Fragment("comparison", "comparisonFragment", this);
  78. add(comparison);
  79. RevCommit fromCommit;
  80. RevCommit toCommit;
  81. String[] parts = objectId.split("\\.\\.");
  82. if (parts[0].startsWith("refs/") && parts[1].startsWith("refs/")) {
  83. // set the ref models
  84. fromRefId.setObject(parts[0]);
  85. toRefId.setObject(parts[1]);
  86. fromCommit = getCommit(r, fromRefId.getObject());
  87. toCommit = getCommit(r, toRefId.getObject());
  88. } else {
  89. // set the id models
  90. fromCommitId.setObject(parts[0]);
  91. toCommitId.setObject(parts[1]);
  92. fromCommit = getCommit(r, fromCommitId.getObject());
  93. toCommit = getCommit(r, toCommitId.getObject());
  94. }
  95. // prepare submodules
  96. getSubmodules(toCommit);
  97. final String startId = fromCommit.getId().getName();
  98. final String endId = toCommit.getId().getName();
  99. // commit ids
  100. fromCommitId.setObject(startId);
  101. toCommitId.setObject(endId);
  102. final List<String> imageExtensions = app().settings().getStrings(Keys.web.imageExtensions);
  103. final ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName,
  104. fromCommit.getName(), toCommit.getName(), imageExtensions);
  105. final DiffComparator diffComparator = WicketUtils.getDiffComparator(params);
  106. final int tabLength = app().settings().getInteger(Keys.web.tabLength, 4);
  107. final DiffOutput diff = DiffUtils.getDiff(r, fromCommit, toCommit, diffComparator, DiffOutputType.HTML, handler, tabLength);
  108. if (handler.getImgDiffCount() > 0) {
  109. addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs
  110. }
  111. // add compare diffstat
  112. int insertions = 0;
  113. int deletions = 0;
  114. for (PathChangeModel pcm : diff.stat.paths) {
  115. insertions += pcm.insertions;
  116. deletions += pcm.deletions;
  117. }
  118. comparison.add(new DiffStatPanel("diffStat", insertions, deletions));
  119. // compare page links
  120. // comparison.add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class,
  121. // WicketUtils.newRangeParameter(repositoryName, fromCommitId.toString(), toCommitId.getObject())));
  122. // display list of commits
  123. comparison.add(new LogPanel("commitList", repositoryName, objectId, r, 0, 0, repository.showRemoteBranches));
  124. // changed paths list
  125. comparison.add(new CommitLegendPanel("commitLegend", diff.stat.paths));
  126. ListDataProvider<PathChangeModel> pathsDp = new ListDataProvider<PathChangeModel>(diff.stat.paths);
  127. DataView<PathChangeModel> pathsView = new DataView<PathChangeModel>("changedPath", pathsDp) {
  128. private static final long serialVersionUID = 1L;
  129. int counter;
  130. @Override
  131. public void populateItem(final Item<PathChangeModel> item) {
  132. final PathChangeModel entry = item.getModelObject();
  133. Label changeType = new Label("changeType", "");
  134. WicketUtils.setChangeTypeCssClass(changeType, entry.changeType);
  135. setChangeTypeTooltip(changeType, entry.changeType);
  136. item.add(changeType);
  137. item.add(new DiffStatPanel("diffStat", entry.insertions, entry.deletions, true));
  138. boolean hasSubmodule = false;
  139. String submodulePath = null;
  140. if (entry.isTree()) {
  141. // tree
  142. item.add(new LinkPanel("pathName", null, entry.path, TreePage.class,
  143. WicketUtils
  144. .newPathParameter(repositoryName, endId, entry.path)));
  145. } else if (entry.isSubmodule()) {
  146. // submodule
  147. String submoduleId = entry.objectId;
  148. SubmoduleModel submodule = getSubmodule(entry.path);
  149. submodulePath = submodule.gitblitPath;
  150. hasSubmodule = submodule.hasSubmodule;
  151. // add relative link
  152. item.add(new LinkPanel("pathName", "list", entry.path + " @ " + getShortObjectId(submoduleId), "#n" + entry.objectId));
  153. } else {
  154. // add relative link
  155. item.add(new LinkPanel("pathName", "list", entry.path, "#n" + entry.objectId));
  156. }
  157. // quick links
  158. if (entry.isSubmodule()) {
  159. // submodule
  160. item.add(new ExternalLink("patch", "").setEnabled(false));
  161. item.add(new BookmarkablePageLink<Void>("view", CommitPage.class, WicketUtils
  162. .newObjectParameter(submodulePath, entry.objectId)).setEnabled(hasSubmodule));
  163. item.add(new ExternalLink("raw", "").setEnabled(false));
  164. item.add(new ExternalLink("blame", "").setEnabled(false));
  165. item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
  166. .newPathParameter(repositoryName, endId, entry.path))
  167. .setEnabled(!entry.changeType.equals(ChangeType.ADD)));
  168. } else {
  169. // tree or blob
  170. item.add(new BookmarkablePageLink<Void>("patch", PatchPage.class, WicketUtils
  171. .newBlobDiffParameter(repositoryName, startId, endId, entry.path))
  172. .setEnabled(!entry.changeType.equals(ChangeType.DELETE)));
  173. item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils
  174. .newPathParameter(repositoryName, endId, entry.path))
  175. .setEnabled(!entry.changeType.equals(ChangeType.DELETE)));
  176. String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, endId, entry.path);
  177. item.add(new ExternalLink("raw", rawUrl)
  178. .setEnabled(!entry.changeType.equals(ChangeType.DELETE)));
  179. item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
  180. .newPathParameter(repositoryName, endId, entry.path))
  181. .setEnabled(!entry.changeType.equals(ChangeType.ADD)
  182. && !entry.changeType.equals(ChangeType.DELETE)));
  183. item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
  184. .newPathParameter(repositoryName, endId, entry.path))
  185. .setEnabled(!entry.changeType.equals(ChangeType.ADD)));
  186. }
  187. WicketUtils.setAlternatingBackground(item, counter);
  188. counter++;
  189. }
  190. };
  191. comparison.add(pathsView);
  192. comparison.add(new Label("diffText", diff.content).setEscapeModelStrings(false));
  193. }
  194. // set the default DiffComparator
  195. DiffComparator diffComparator = WicketUtils.getDiffComparator(params);
  196. ignoreWhitespace.setObject(DiffComparator.IGNORE_WHITESPACE == diffComparator);
  197. //
  198. // ref selection form
  199. //
  200. SessionlessForm<Void> refsForm = new SessionlessForm<Void>("compareRefsForm", getClass(), getPageParameters()) {
  201. private static final long serialVersionUID = 1L;
  202. @Override
  203. public void onSubmit() {
  204. String from = ComparePage.this.fromRefId.getObject();
  205. String to = ComparePage.this.toRefId.getObject();
  206. boolean ignoreWS = ignoreWhitespace.getObject();
  207. PageParameters params = WicketUtils.newRangeParameter(repositoryName, from, to);
  208. if (ignoreWS) {
  209. params.put("w", 1);
  210. }
  211. String relativeUrl = urlFor(ComparePage.class, params).toString();
  212. String absoluteUrl = RequestUtils.toAbsolutePath(relativeUrl);
  213. getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl));
  214. }
  215. };
  216. List<String> refs = new ArrayList<String>();
  217. for (RefModel ref : JGitUtils.getLocalBranches(r, true, -1)) {
  218. refs.add(ref.getName());
  219. }
  220. if (repository.showRemoteBranches) {
  221. for (RefModel ref : JGitUtils.getRemoteBranches(r, true, -1)) {
  222. refs.add(ref.getName());
  223. }
  224. }
  225. for (RefModel ref : JGitUtils.getTags(r, true, -1)) {
  226. refs.add(ref.getName());
  227. }
  228. refsForm.add(new DropDownChoice<String>("fromRef", fromRefId, refs).setEnabled(refs.size() > 0));
  229. refsForm.add(new DropDownChoice<String>("toRef", toRefId, refs).setEnabled(refs.size() > 0));
  230. refsForm.add(new Label("ignoreWhitespaceLabel", getString(DiffComparator.IGNORE_WHITESPACE.getTranslationKey())));
  231. refsForm.add(new CheckBox("ignoreWhitespaceCheckbox", ignoreWhitespace));
  232. add(refsForm);
  233. //
  234. // manual ids form
  235. //
  236. SessionlessForm<Void> idsForm = new SessionlessForm<Void>("compareIdsForm", getClass(), getPageParameters()) {
  237. private static final long serialVersionUID = 1L;
  238. @Override
  239. public void onSubmit() {
  240. String from = ComparePage.this.fromCommitId.getObject();
  241. String to = ComparePage.this.toCommitId.getObject();
  242. boolean ignoreWS = ignoreWhitespace.getObject();
  243. PageParameters params = WicketUtils.newRangeParameter(repositoryName, from, to);
  244. if (ignoreWS) {
  245. params.put("w", 1);
  246. }
  247. String relativeUrl = urlFor(ComparePage.class, params).toString();
  248. String absoluteUrl = RequestUtils.toAbsolutePath(relativeUrl);
  249. getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl));
  250. }
  251. };
  252. TextField<String> fromIdField = new TextField<String>("fromId", fromCommitId);
  253. WicketUtils.setInputPlaceholder(fromIdField, getString("gb.from") + "...");
  254. idsForm.add(fromIdField);
  255. TextField<String> toIdField = new TextField<String>("toId", toCommitId);
  256. WicketUtils.setInputPlaceholder(toIdField, getString("gb.to") + "...");
  257. idsForm.add(toIdField);
  258. idsForm.add(new Label("ignoreWhitespaceLabel", getString(DiffComparator.IGNORE_WHITESPACE.getTranslationKey())));
  259. idsForm.add(new CheckBox("ignoreWhitespaceCheckbox", ignoreWhitespace));
  260. add(idsForm);
  261. r.close();
  262. }
  263. @Override
  264. protected String getPageName() {
  265. return getString("gb.compare");
  266. }
  267. @Override
  268. protected Class<? extends BasePage> getRepoNavPageClass() {
  269. return ComparePage.class;
  270. }
  271. private RevCommit getCommit(Repository r, String rev)
  272. {
  273. RevCommit otherCommit = JGitUtils.getCommit(r, rev);
  274. if (otherCommit == null) {
  275. error(MessageFormat.format(getString("gb.failedToFindCommit"), rev, repositoryName, getPageName()), true);
  276. }
  277. return otherCommit;
  278. }
  279. }