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