]> source.dussan.org Git - gitblit.git/commitdiff
Implemented compare page for branch/tag/manual diffs (issue-75, issue-133)
authorJames Moger <james.moger@gitblit.com>
Fri, 24 May 2013 03:13:59 +0000 (23:13 -0400)
committerJames Moger <james.moger@gitblit.com>
Fri, 24 May 2013 03:13:59 +0000 (23:13 -0400)
src/main/java/com/gitblit/wicket/GitBlitWebApp.java
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
src/main/java/com/gitblit/wicket/WicketUtils.java
src/main/java/com/gitblit/wicket/pages/ComparePage.html [new file with mode: 0644]
src/main/java/com/gitblit/wicket/pages/ComparePage.java [new file with mode: 0644]
src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
src/main/resources/gitblit.css

index 20d2f2a8e7bbda96478f32f480ac9aec7bf51248..dcae53ef44d8ba4c058ec2dd5fda798dbff8c223 100644 (file)
@@ -36,6 +36,7 @@ import com.gitblit.wicket.pages.BlobPage;
 import com.gitblit.wicket.pages.BranchesPage;\r
 import com.gitblit.wicket.pages.CommitDiffPage;\r
 import com.gitblit.wicket.pages.CommitPage;\r
+import com.gitblit.wicket.pages.ComparePage;\r
 import com.gitblit.wicket.pages.DocsPage;\r
 import com.gitblit.wicket.pages.FederationRegistrationPage;\r
 import com.gitblit.wicket.pages.ForkPage;\r
@@ -106,6 +107,7 @@ public class GitBlitWebApp extends WebApplication {
                mount("/raw", RawPage.class, "r", "h", "f");\r
                mount("/blobdiff", BlobDiffPage.class, "r", "h", "f");\r
                mount("/commitdiff", CommitDiffPage.class, "r", "h");\r
+               mount("/compare", ComparePage.class, "r", "h");\r
                mount("/patch", PatchPage.class, "r", "h", "f");\r
                mount("/history", HistoryPage.class, "r", "h", "f");\r
                mount("/search", GitSearchPage.class);\r
index a6cac54655a51b701c4aa4f6b414571c9b36c86f..bec8b35b6232787e1e56a7a7ecd05008be77acf7 100644 (file)
@@ -454,4 +454,8 @@ gb.viewAccess = You do not have Gitblit read or write access
 gb.overview = overview\r
 gb.home = home\r
 gb.monthlyActivity = monthly activity\r
-gb.myProfile = my profile
\ No newline at end of file
+gb.myProfile = my profile\r
+gb.compare = compare\r
+gb.manual = manual\r
+gb.from = from\r
+gb.to = to
\ No newline at end of file
index 2170d0b7c72c03a8d768465f1cd39c6daf294dbe..91686b67caffaa3d150c040f9eb13a686e692bee 100644 (file)
@@ -319,6 +319,10 @@ public class WicketUtils {
                return new PageParameters("r=" + repositoryName + ",h=" + objectId);\r
        }\r
 \r
+       public static PageParameters newRangeParameter(String repositoryName, String startRange, String endRange) {\r
+               return new PageParameters("r=" + repositoryName + ",h=" + startRange + ".." + endRange);\r
+       }\r
+\r
        public static PageParameters newPathParameter(String repositoryName, String objectId,\r
                        String path) {\r
                if (StringUtils.isEmpty(path)) {\r
diff --git a/src/main/java/com/gitblit/wicket/pages/ComparePage.html b/src/main/java/com/gitblit/wicket/pages/ComparePage.html
new file mode 100644 (file)
index 0000000..8fb6e4f
--- /dev/null
@@ -0,0 +1,79 @@
+<!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
+\r
+<body>\r
+<wicket:extend>\r
+\r
+       <div class="tabbable">\r
+       <ul class="nav nav-pills">\r
+               <li class="active"><a href="#refs" data-toggle="tab"><wicket:message key="gb.refs"></wicket:message></a></li>\r
+               <li><a href="#ids" data-toggle="tab"><wicket:message key="gb.manual"></wicket:message></a></li>\r
+       </ul>\r
+       <div class="tab-content">\r
+               <div class="tab-pane active" id="refs">\r
+                               <form wicket:id="compareRefsForm" class="form-inline">\r
+                                       <select wicket:id="fromRef" class="span3" />\r
+                                       <select wicket:id="toRef" class="span3" />\r
+                                       <button class="btn" type="submit"><wicket:message key="gb.compare"></wicket:message></button>\r
+                               </form>\r
+                       </div>\r
+               <div class="tab-pane" id="ids">\r
+                               <form wicket:id="compareIdsForm" class="form-inline">\r
+                                       <input wicket:id="fromId" type="text" class="span3" />\r
+                                       <input wicket:id="toId" type="text" class="span3" />\r
+                                       <button class="btn" type="submit"><wicket:message key="gb.compare"></wicket:message></button>\r
+                               </form>\r
+                       </div>\r
+               </div>\r
+       </div>\r
+\r
+       <div wicket:id="comparison"></div>\r
+\r
+       <wicket:fragment wicket:id="comparisonFragment">\r
+       \r
+               <div class="tabbable">\r
+               <ul class="nav nav-tabs">\r
+                       <li class="active"><a href="#commits" data-toggle="tab"><wicket:message key="gb.commits"></wicket:message></a></li>\r
+                       <li><a href="#diff" data-toggle="tab"><wicket:message key="gb.diff"></wicket:message></a></li>\r
+               </ul>\r
+               <div class="tab-content">\r
+                       <div class="tab-pane active" id="commits">\r
+                                       <!-- commit list -->\r
+                                       <div wicket:id="commitList">[commit list]</div>\r
+                       </div>\r
+                       <div class="tab-pane" id="diff">\r
+<!--                                   <div class="page_nav2"> -->\r
+<!--                                           <a wicket:id="patchLink"><wicket:message key="gb.patch"></wicket:message></a> -->\r
+<!--                                   </div>   -->\r
+                       \r
+                               <!-- changed paths -->\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>\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="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
+                                                       </td>\r
+                                               </tr>\r
+                                       </table>\r
+               \r
+                                       <!--  diff content -->\r
+                                       <pre style="padding-top:10px;" wicket:id="diffText">[diff text]</pre>\r
+                       </div>\r
+               </div>\r
+       </div>\r
+       </wicket:fragment>\r
+\r
+</wicket:extend>\r
+</body>\r
+</html>
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/ComparePage.java b/src/main/java/com/gitblit/wicket/pages/ComparePage.java
new file mode 100644 (file)
index 0000000..f62dc64
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * 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.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.GitBlit;
+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.utils.DiffUtils;
+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.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>("");
+
+       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);
+                       
+                       DiffOutputType diffType = DiffOutputType.forName(GitBlit.getString(Keys.web.diffStyle,
+                                       DiffOutputType.GITBLIT.name()));
+
+                       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());
+                       }
+
+                       final String startId = fromCommit.getId().getName();
+                       final String endId = toCommit.getId().getName();
+
+                       // commit ids
+                       fromCommitId.setObject(startId);
+                       toCommitId.setObject(endId);
+
+                       String diff = DiffUtils.getDiff(r, fromCommit, toCommit, diffType);
+
+                       // 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
+                       List<PathChangeModel> paths = JGitUtils.getFilesInRange(r, fromCommit, toCommit);
+
+                       comparison.add(new CommitLegendPanel("commitLegend", paths));
+                       ListDataProvider<PathChangeModel> pathsDp = new ListDataProvider<PathChangeModel>(paths);
+                       DataView<PathChangeModel> pathsView = new DataView<PathChangeModel>("changedPath", pathsDp) {
+                               private static final long serialVersionUID = 1L;
+                               int counter;
+
+                               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);
+
+                                       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), "#" + entry.path));
+                                       } else {
+                                               // add relative link
+                                               item.add(new LinkPanel("pathName", "list", entry.path, "#" + entry.path));
+                                       }
+
+                                       // 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("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)));
+                                               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).setEscapeModelStrings(false));
+               }
+
+               //
+               // 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();
+                               
+                               PageParameters params = WicketUtils.newRangeParameter(repositoryName, from, to);
+                               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));
+               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();
+                               
+                               PageParameters params = WicketUtils.newRangeParameter(repositoryName, from, to);
+                               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);
+               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;
+       }
+}
index 90fdd40bf0b1291eb0c77fced8348a2b1b149e16..8a233ead419d9a27f27c0999946de45fe5bddb21 100644 (file)
@@ -169,6 +169,7 @@ public abstract class RepositoryPage extends RootPage {
                }\r
                pages.put("commits", new PageRegistration("gb.commits", LogPage.class, params));\r
                pages.put("tree", new PageRegistration("gb.tree", TreePage.class, params));\r
+               pages.put("compare", new PageRegistration("gb.compare", ComparePage.class, params));\r
                if (GitBlit.getBoolean(Keys.web.allowForking, true)) {\r
                        pages.put("forks", new PageRegistration("gb.forks", ForksPage.class, params));\r
                }\r
index 88f7930ee5f3389bbffb825a54787ffeb1c18e79..0a1c3b0b5ed89bcca0cb7618a848989b182aeb80 100644 (file)
@@ -119,6 +119,11 @@ navbar div>ul .menu-dropdown li a:hover,.nav .menu-dropdown li a:hover,.navbar d
        color: #ffffff !important;\r
 }\r
 \r
+.nav-pills > .active > a, .nav-pills > .active > a:hover {\r
+    color: #fff;\r
+    background-color: #002060;\r
+}\r
+\r
 .repositorynavbar {\r
        background-color: #fbfbfb;\r
        border-bottom: 1px solid #ccc;\r