James Moger
2015-11-22 ed552ba47c02779c270ffd62841d6d1048dade70
commit | author | age
f13c4c 1 /*
JM 2  * Copyright 2011 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  */
f602a2 16 package com.gitblit.wicket.panels;
JM 17
ea3abf 18 import java.text.MessageFormat;
657a65 19 import java.util.ArrayList;
d83bd6 20 import java.util.Collections;
f602a2 21 import java.util.Date;
657a65 22 import java.util.HashMap;
JM 23 import java.util.LinkedHashSet;
f602a2 24 import java.util.List;
JM 25 import java.util.Map;
657a65 26 import java.util.Set;
f602a2 27
JM 28 import org.apache.wicket.markup.html.basic.Label;
29 import org.apache.wicket.markup.html.link.BookmarkablePageLink;
e299e1 30 import org.apache.wicket.markup.html.panel.Fragment;
f602a2 31 import org.apache.wicket.markup.repeater.Item;
JM 32 import org.apache.wicket.markup.repeater.data.DataView;
33 import org.apache.wicket.markup.repeater.data.ListDataProvider;
34 import org.apache.wicket.model.StringResourceModel;
d83bd6 35 import org.eclipse.jgit.diff.DiffEntry.ChangeType;
f602a2 36 import org.eclipse.jgit.lib.ObjectId;
JM 37 import org.eclipse.jgit.lib.Repository;
38 import org.eclipse.jgit.revwalk.RevCommit;
d83bd6 39 import org.eclipse.jgit.treewalk.TreeWalk;
JM 40 import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
f602a2 41
33d8d8 42 import com.gitblit.Constants;
f602a2 43 import com.gitblit.Keys;
1f9dae 44 import com.gitblit.models.PathModel;
JM 45 import com.gitblit.models.PathModel.PathChangeModel;
4ab184 46 import com.gitblit.models.RefModel;
fd9d28 47 import com.gitblit.models.SubmoduleModel;
f602a2 48 import com.gitblit.utils.JGitUtils;
ea3abf 49 import com.gitblit.utils.MarkdownUtils;
9bc17d 50 import com.gitblit.utils.StringUtils;
f602a2 51 import com.gitblit.wicket.WicketUtils;
JM 52 import com.gitblit.wicket.pages.BlobDiffPage;
e299e1 53 import com.gitblit.wicket.pages.BlobPage;
f602a2 54 import com.gitblit.wicket.pages.CommitDiffPage;
JM 55 import com.gitblit.wicket.pages.CommitPage;
4e1930 56 import com.gitblit.wicket.pages.GitSearchPage;
75705a 57 import com.gitblit.wicket.pages.HistoryPage;
e299e1 58 import com.gitblit.wicket.pages.TreePage;
f602a2 59
JM 60 public class HistoryPanel extends BasePanel {
61
62     private static final long serialVersionUID = 1L;
e299e1 63
2a7306 64     private boolean hasMore;
f602a2 65
2a7306 66     public HistoryPanel(String wicketId, final String repositoryName, final String objectId,
1e1b85 67             final String path, Repository r, int limit, int pageOffset, boolean showRemoteRefs) {
f602a2 68         super(wicketId);
JM 69         boolean pageResults = limit <= 0;
99d0d4 70         int itemsPerPage = app().settings().getInteger(Keys.web.itemsPerPage, 50);
f602a2 71         if (itemsPerPage <= 1) {
JM 72             itemsPerPage = 50;
73         }
e299e1 74
JM 75         RevCommit commit = JGitUtils.getCommit(r, objectId);
76         PathModel matchingPath = null;
ea3abf 77         Map<String, SubmoduleModel> submodules = new HashMap<String, SubmoduleModel>();
JM 78
79         if (commit == null) {
80             // commit missing
81             String msg = MessageFormat.format("Failed to find history of **{0}** *{1}*",
82                     path, objectId);
20fd88 83             logger().error(msg + " " + repositoryName);
ea3abf 84             add(new Label("commitHeader", MarkdownUtils.transformMarkdown(msg)).setEscapeModelStrings(false));
JM 85             add(new Label("breadcrumbs"));
86         } else {
87             // commit found
a21f53 88             List<PathChangeModel> paths = JGitUtils.getFilesInCommit(r, commit);
ea3abf 89             add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
JM 90             add(new PathBreadcrumbsPanel("breadcrumbs", repositoryName, path, objectId));
91             for (SubmoduleModel model : JGitUtils.getSubmodules(r, commit.getTree())) {
92                 submodules.put(model.path, model);
e299e1 93             }
ea3abf 94
JM 95             for (PathModel p : paths) {
96                 if (p.path.equals(path)) {
97                     matchingPath = p;
98                     break;
d83bd6 99                 }
ea3abf 100             }
JM 101             if (matchingPath == null) {
102                 // path not in commit
103                 // manually locate path in tree
104                 TreeWalk tw = new TreeWalk(r);
105                 tw.reset();
106                 tw.setRecursive(true);
107                 try {
108                     tw.addTree(commit.getTree());
109                     tw.setFilter(PathFilterGroup.createFromStrings(Collections.singleton(path)));
110                     while (tw.next()) {
111                         if (tw.getPathString().equals(path)) {
112                             matchingPath = new PathChangeModel(tw.getPathString(), tw.getPathString(), 0, tw
113                                 .getRawMode(0), tw.getObjectId(0).getName(), commit.getId().getName(),
114                                 ChangeType.MODIFY);
115                         }
116                     }
117                 } catch (Exception e) {
118                 } finally {
a1cee6 119                     tw.close();
ea3abf 120                 }
d83bd6 121             }
JM 122         }
699e71 123
e299e1 124         final boolean isTree = matchingPath == null ? true : matchingPath.isTree();
66a8db 125         final boolean isSubmodule = matchingPath == null ? false : matchingPath.isSubmodule();
e299e1 126
657a65 127         // submodule
JM 128         final String submodulePath;
699e71 129         final boolean hasSubmodule;
66a8db 130         if (isSubmodule) {
JM 131             SubmoduleModel submodule = getSubmodule(submodules, repositoryName, matchingPath == null ? null : matchingPath.path);
657a65 132             submodulePath = submodule.gitblitPath;
JM 133             hasSubmodule = submodule.hasSubmodule;
134         } else {
135             submodulePath = "";
136             hasSubmodule = false;
137         }
699e71 138
1e1b85 139         final Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(r, showRemoteRefs);
f602a2 140         List<RevCommit> commits;
JM 141         if (pageResults) {
142             // Paging result set
2a7306 143             commits = JGitUtils.getRevLog(r, objectId, path, pageOffset * itemsPerPage,
JM 144                     itemsPerPage);
f602a2 145         } else {
JM 146             // Fixed size result set
147             commits = JGitUtils.getRevLog(r, objectId, path, 0, limit);
148         }
e299e1 149
f602a2 150         // inaccurate way to determine if there are more commits.
e299e1 151         // works unless commits.size() represents the exact end.
f602a2 152         hasMore = commits.size() >= itemsPerPage;
JM 153
99d0d4 154         final int hashLen = app().settings().getInteger(Keys.web.shortCommitIdLength, 6);
f602a2 155         ListDataProvider<RevCommit> dp = new ListDataProvider<RevCommit>(commits);
JM 156         DataView<RevCommit> logView = new DataView<RevCommit>("commit", dp) {
157             private static final long serialVersionUID = 1L;
2a7306 158             int counter;
f602a2 159
699e71 160             @Override
f602a2 161             public void populateItem(final Item<RevCommit> item) {
JM 162                 final RevCommit entry = item.getModelObject();
a59232 163                 final Date date = JGitUtils.getAuthorDate(entry);
f602a2 164
9adf62 165                 item.add(WicketUtils.createDateLabel("commitDate", date, getTimeZone(), getTimeUtils()));
f602a2 166
98ce17 167                 // author search link
f602a2 168                 String author = entry.getAuthorIdent().getName();
2a7306 169                 LinkPanel authorLink = new LinkPanel("commitAuthor", "list", author,
4e1930 170                         GitSearchPage.class,
b26eda 171                         WicketUtils.newSearchParameter(repositoryName, null,
33d8d8 172                                 author, Constants.SearchType.AUTHOR));
JM 173                 setPersonSearchTooltip(authorLink, author, Constants.SearchType.AUTHOR);
98ce17 174                 item.add(authorLink);
f5d0ad 175
a645ba 176                 // merge icon
JM 177                 if (entry.getParentCount() > 1) {
1e8390 178                     item.add(WicketUtils.newImage("commitIcon", "commit_merge_16x16.png"));
a645ba 179                 } else {
1e8390 180                     item.add(WicketUtils.newBlankImage("commitIcon"));
a645ba 181                 }
JM 182
f602a2 183                 String shortMessage = entry.getShortMessage();
a45caa 184                 String trimmedMessage = shortMessage;
JM 185                 if (allRefs.containsKey(entry.getId())) {
186                     trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG_REFS);
187                 } else {
188                     trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG);
189                 }
2a7306 190                 LinkPanel shortlog = new LinkPanel("commitShortMessage", "list subject",
JM 191                         trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(
192                                 repositoryName, entry.getName()));
f602a2 193                 if (!shortMessage.equals(trimmedMessage)) {
1e8390 194                     WicketUtils.setHtmlTooltip(shortlog, shortMessage);
f602a2 195                 }
JM 196                 item.add(shortlog);
197
198                 item.add(new RefsPanel("commitRefs", repositoryName, entry, allRefs));
199
e299e1 200                 if (isTree) {
75705a 201                     // tree
JM 202                     item.add(new Label("hashLabel", getString("gb.tree") + "@"));
203                     LinkPanel commitHash = new LinkPanel("hashLink", null, entry.getName().substring(0, hashLen),
204                             TreePage.class, WicketUtils.newObjectParameter(
205                                     repositoryName, entry.getName()));
87d72e 206                     WicketUtils.setCssClass(commitHash, "shortsha1");
699e71 207                     WicketUtils.setHtmlTooltip(commitHash, entry.getName());
75705a 208                     item.add(commitHash);
699e71 209
e299e1 210                     Fragment links = new Fragment("historyLinks", "treeLinks", this);
2a7306 211                     links.add(new BookmarkablePageLink<Void>("commitdiff", CommitDiffPage.class,
JM 212                             WicketUtils.newObjectParameter(repositoryName, entry.getName())));
e299e1 213                     item.add(links);
657a65 214                 } else if (isSubmodule) {
JM 215                     // submodule
99d0d4 216                     Repository repository = app().repositories().getRepository(repositoryName);
657a65 217                     String submoduleId = JGitUtils.getSubmoduleCommitId(repository, path, entry);
JM 218                     repository.close();
bb3311 219                     if (StringUtils.isEmpty(submoduleId)) {
JM 220                         // not a submodule at this commit, just a matching path
221                         item.add(new Label("hashLabel").setVisible(false));
222                         item.add(new Label("hashLink").setVisible(false));
223                     } else {
224                         // really a submodule
225                         item.add(new Label("hashLabel", submodulePath + "@"));
226                         LinkPanel commitHash = new LinkPanel("hashLink", null, submoduleId.substring(0, hashLen),
227                                 TreePage.class, WicketUtils.newObjectParameter(
228                                         submodulePath, submoduleId));
229                         WicketUtils.setCssClass(commitHash, "shortsha1");
699e71 230                         WicketUtils.setHtmlTooltip(commitHash, submoduleId);
bb3311 231                         item.add(commitHash.setEnabled(hasSubmodule));
JM 232                     }
657a65 233                     Fragment links = new Fragment("historyLinks", "treeLinks", this);
JM 234                     links.add(new BookmarkablePageLink<Void>("commitdiff", CommitDiffPage.class,
235                             WicketUtils.newObjectParameter(repositoryName, entry.getName())));
236                     item.add(links);
699e71 237                 } else {
75705a 238                     // commit
JM 239                     item.add(new Label("hashLabel", getString("gb.blob") + "@"));
240                     LinkPanel commitHash = new LinkPanel("hashLink", null, entry.getName().substring(0, hashLen),
241                             BlobPage.class, WicketUtils.newPathParameter(
242                                     repositoryName, entry.getName(), path));
243                     WicketUtils.setCssClass(commitHash, "sha1");
244                     WicketUtils.setHtmlTooltip(commitHash, entry.getName());
245                     item.add(commitHash);
699e71 246
e299e1 247                     Fragment links = new Fragment("historyLinks", "blobLinks", this);
2a7306 248                     links.add(new BookmarkablePageLink<Void>("commitdiff", CommitDiffPage.class,
JM 249                             WicketUtils.newObjectParameter(repositoryName, entry.getName())));
250                     links.add(new BookmarkablePageLink<Void>("difftocurrent", BlobDiffPage.class,
251                             WicketUtils.newBlobDiffParameter(repositoryName, entry.getName(),
252                                     objectId, path)).setEnabled(counter > 0));
e299e1 253                     item.add(links);
JM 254                 }
f602a2 255
JM 256                 WicketUtils.setAlternatingBackground(item, counter);
257                 counter++;
258             }
259         };
260         add(logView);
261
262         // determine to show pager, more, or neither
263         if (limit <= 0) {
264             // no display limit
265             add(new Label("moreHistory", "").setVisible(false));
266         } else {
267             if (pageResults) {
268                 // paging
269                 add(new Label("moreHistory", "").setVisible(false));
270             } else {
271                 // more
272                 if (commits.size() == limit) {
273                     // show more
2a7306 274                     add(new LinkPanel("moreHistory", "link", new StringResourceModel(
JM 275                             "gb.moreHistory", this, null), HistoryPage.class,
276                             WicketUtils.newPathParameter(repositoryName, objectId, path)));
f602a2 277                 } else {
JM 278                     // no more
279                     add(new Label("moreHistory", "").setVisible(false));
280                 }
281             }
282         }
283     }
e299e1 284
f602a2 285     public boolean hasMore() {
JM 286         return hasMore;
287     }
699e71 288
657a65 289     protected SubmoduleModel getSubmodule(Map<String, SubmoduleModel> submodules, String repositoryName, String path) {
JM 290         SubmoduleModel model = submodules.get(path);
291         if (model == null) {
292             // undefined submodule?!
293             model = new SubmoduleModel(path.substring(path.lastIndexOf('/') + 1), path, path);
294             model.hasSubmodule = false;
295             model.gitblitPath = model.name;
296             return model;
297         } else {
298             // extract the repository name from the clone url
99d0d4 299             List<String> patterns = app().settings().getStrings(Keys.git.submoduleUrlPatterns);
657a65 300             String submoduleName = StringUtils.extractRepositoryPath(model.url, patterns.toArray(new String[0]));
699e71 301
657a65 302             // determine the current path for constructing paths relative
JM 303             // to the current repository
304             String currentPath = "";
305             if (repositoryName.indexOf('/') > -1) {
306                 currentPath = repositoryName.substring(0, repositoryName.lastIndexOf('/') + 1);
307             }
308
309             // try to locate the submodule repository
310             // prefer bare to non-bare names
311             List<String> candidates = new ArrayList<String>();
312
313             // relative
314             candidates.add(currentPath + StringUtils.stripDotGit(submoduleName));
315             candidates.add(candidates.get(candidates.size() - 1) + ".git");
316
317             // relative, no subfolder
318             if (submoduleName.lastIndexOf('/') > -1) {
319                 String name = submoduleName.substring(submoduleName.lastIndexOf('/') + 1);
320                 candidates.add(currentPath + StringUtils.stripDotGit(name));
5e3771 321                 candidates.add(candidates.get(candidates.size() - 1) + ".git");
657a65 322             }
JM 323
324             // absolute
325             candidates.add(StringUtils.stripDotGit(submoduleName));
326             candidates.add(candidates.get(candidates.size() - 1) + ".git");
327
328             // absolute, no subfolder
329             if (submoduleName.lastIndexOf('/') > -1) {
330                 String name = submoduleName.substring(submoduleName.lastIndexOf('/') + 1);
331                 candidates.add(StringUtils.stripDotGit(name));
332                 candidates.add(candidates.get(candidates.size() - 1) + ".git");
333             }
334
335             // create a unique, ordered set of candidate paths
336             Set<String> paths = new LinkedHashSet<String>(candidates);
337             for (String candidate : paths) {
99d0d4 338                 if (app().repositories().hasRepository(candidate)) {
657a65 339                     model.hasSubmodule = true;
JM 340                     model.gitblitPath = candidate;
341                     return model;
342                 }
343             }
699e71 344
657a65 345             // we do not have a copy of the submodule, but we need a path
JM 346             model.gitblitPath = candidates.get(0);
347             return model;
699e71 348         }
657a65 349     }
f602a2 350 }