diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java index 42bb28b4c5..c5901d6a15 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java @@ -59,6 +59,7 @@ public interface ChangeConstants extends Constants { String patchTableDiffUnified(); String patchTableDownloadPreImage(); String patchTableDownloadPostImage(); + String commitMessage(); String patchTablePrev(); String patchTableNext(); diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties index 996b462139..75f3ae28b1 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties @@ -39,6 +39,7 @@ patchTableDiffSideBySide = Side-by-Side patchTableDiffUnified = Unified patchTableDownloadPreImage = old patchTableDownloadPostImage = new +commitMessage = Commit Message patchTablePrev = Previous file patchTableNext = Next file diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java index da4b78b8d1..94681cf3e4 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java @@ -14,14 +14,12 @@ package com.google.gerrit.client.changes; +import com.google.gerrit.client.Dispatcher; import com.google.gerrit.client.FormatUtil; import com.google.gerrit.client.Gerrit; import com.google.gerrit.client.rpc.GerritCallback; import com.google.gerrit.client.ui.AccountDashboardLink; import com.google.gerrit.client.ui.ComplexDisclosurePanel; -import com.google.gerrit.client.ui.PatchLink; -import com.google.gerrit.client.ui.PatchLink.SideBySide; -import com.google.gerrit.client.ui.PatchLink.Unified; import com.google.gerrit.common.data.ChangeDetail; import com.google.gerrit.common.data.GitwebLink; import com.google.gerrit.common.data.PatchSetDetail; @@ -419,9 +417,8 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel implements O @Override public void onClick(ClickEvent event) { for (Patch p : detail.getPatches()) { - SideBySide link = new PatchLink.SideBySide(p.getFileName(), p.getKey(), 0, null, null); Window.open(Window.Location.getPath() + "#" - + link.getTargetHistoryToken(), "_blank", null); + + Dispatcher.toPatchSideBySide(p.getKey()), "_blank", null); } } }); @@ -433,9 +430,8 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel implements O @Override public void onClick(ClickEvent event) { for (Patch p : detail.getPatches()) { - Unified link = new PatchLink.Unified(p.getFileName(), p.getKey(), 0, null, null); Window.open(Window.Location.getPath() + "#" - + link.getTargetHistoryToken(), "_blank", null); + + Dispatcher.toPatchUnified(p.getKey()), "_blank", null); } } }); diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java index f448b67daf..ce951781c8 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java @@ -211,7 +211,7 @@ public class PatchTable extends Composite { // Note: use '/' here and not File.pathSeparator since git paths // are always separated by / // - String fileName = patch.getFileName(); + String fileName = getDisplayFileName(patch); int s = fileName.lastIndexOf('/'); if (s >= 0) { fileName = fileName.substring(s + 1); @@ -219,6 +219,17 @@ public class PatchTable extends Composite { return fileName; } + public static String getDisplayFileName(Patch patch) { + return getDisplayFileName(patch.getKey()); + } + + public static String getDisplayFileName(Patch.Key patchKey) { + if (Patch.COMMIT_MSG.equals(patchKey.get())) { + return Util.C.commitMessage(); + } + return patchKey.get(); + } + /** * Update the reviewed status for the given patch. */ @@ -326,12 +337,12 @@ public class PatchTable extends Composite { Widget nameCol; if (patch.getPatchType() == Patch.PatchType.UNIFIED) { nameCol = - new PatchLink.SideBySide(patch.getFileName(), patch.getKey(), + new PatchLink.SideBySide(getDisplayFileName(patch), patch.getKey(), row - 1, detail, PatchTable.this); } else { nameCol = - new PatchLink.Unified(patch.getFileName(), patch.getKey(), row - 1, - detail, PatchTable.this); + new PatchLink.Unified(getDisplayFileName(patch), patch.getKey(), + row - 1, detail, PatchTable.this); } if (patch.getSourceFileName() != null) { final String text; @@ -423,7 +434,11 @@ public class PatchTable extends Composite { m.openTd(); m.setStyleName(Gerrit.RESOURCES.css().changeTypeCell()); - m.append(p.getChangeType().getCode()); + if (Patch.COMMIT_MSG.equals(p.getFileName())) { + m.nbsp(); + } else { + m.append(p.getChangeType().getCode()); + } m.closeTd(); m.openTd(); diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java index d71ee776d7..8a8996acee 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java @@ -36,7 +36,6 @@ import com.google.gerrit.reviewdb.PatchSet; import com.google.gerrit.reviewdb.PatchSetApproval; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; -import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.FormPanel; @@ -281,7 +280,8 @@ public class PublishCommentScreen extends AccountScreen implements draftsPanel.add(panel); // Parent table can be null here since we are not showing any // next/previous links - panel.add(new PatchLink.SideBySide(fn, patchKey, 0, null, null)); + panel.add(new PatchLink.SideBySide(PatchTable + .getDisplayFileName(patchKey), patchKey, 0, null, null)); priorFile = fn; } diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScreen.java index 6c45138b6c..adf9faa020 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScreen.java @@ -395,7 +395,7 @@ public abstract class PatchScreen extends Screen implements private void onResult(final PatchScript script, final boolean isFirst) { final Change.Key cid = script.getChangeId(); - final String path = patchKey.get(); + final String path = PatchTable.getDisplayFileName(patchKey); String fileName = path; final int last = fileName.lastIndexOf('/'); if (last >= 0) { diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptBuilder.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptBuilder.java index a58c7b954f..0f557ee630 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptBuilder.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptBuilder.java @@ -66,6 +66,7 @@ class PatchScriptBuilder { private Repository db; private Change change; private AccountDiffPreference diffPrefs; + private boolean againstParent; private ObjectId aId; private ObjectId bId; @@ -102,7 +103,8 @@ class PatchScriptBuilder { } } - void setTrees(final ObjectId a, final ObjectId b) { + void setTrees(final boolean ap, final ObjectId a, final ObjectId b) { + againstParent = ap; aId = a; bId = b; } @@ -365,41 +367,62 @@ class PatchScriptBuilder { void resolve(final Side other, final ObjectId within) throws IOException { try { - final TreeWalk tw = find(within); - - id = tw != null ? tw.getObjectId(0) : ObjectId.zeroId(); - mode = tw != null ? tw.getFileMode(0) : FileMode.MISSING; - - final boolean reuse = - other != null && other.id.equals(id) && other.mode == mode; - - if (reuse) { - srcContent = other.srcContent; - - } else if (mode.getObjectType() == Constants.OBJ_BLOB) { - final ObjectLoader ldr = db.openObject(id); - if (ldr == null) { - throw new MissingObjectException(id, Constants.TYPE_BLOB); - } - srcContent = ldr.getCachedBytes(); - if (ldr.getType() != Constants.OBJ_BLOB) { - throw new IncorrectObjectTypeException(id, Constants.TYPE_BLOB); + final boolean reuse; + if (Patch.COMMIT_MSG.equals(path)) { + if (againstParent && (aId == within || within.equals(aId))) { + id = ObjectId.zeroId(); + src = Text.EMPTY; + srcContent = Text.NO_BYTES; + mode = FileMode.MISSING; + displayMethod = DisplayMethod.NONE; + } else { + id = within; + src = Text.forCommit(db, within); + srcContent = src.getContent(); + if (src == Text.EMPTY) { + mode = FileMode.MISSING; + displayMethod = DisplayMethod.NONE; + } else { + mode = FileMode.REGULAR_FILE; + } } + reuse = false; } else { - srcContent = Text.NO_BYTES; - } + final TreeWalk tw = find(within); - if (reuse) { - mimeType = other.mimeType; - displayMethod = other.displayMethod; - src = other.src; + id = tw != null ? tw.getObjectId(0) : ObjectId.zeroId(); + mode = tw != null ? tw.getFileMode(0) : FileMode.MISSING; + reuse = other != null && other.id.equals(id) && other.mode == mode; - } else if (srcContent.length > 0 && FileMode.SYMLINK != mode) { - mimeType = registry.getMimeType(path, srcContent); - if ("image".equals(mimeType.getMediaType()) - && registry.isSafeInline(mimeType)) { - displayMethod = DisplayMethod.IMG; + if (reuse) { + srcContent = other.srcContent; + + } else if (mode.getObjectType() == Constants.OBJ_BLOB) { + final ObjectLoader ldr = db.openObject(id); + if (ldr == null) { + throw new MissingObjectException(id, Constants.TYPE_BLOB); + } + srcContent = ldr.getCachedBytes(); + if (ldr.getType() != Constants.OBJ_BLOB) { + throw new IncorrectObjectTypeException(id, Constants.TYPE_BLOB); + } + + } else { + srcContent = Text.NO_BYTES; + } + + if (reuse) { + mimeType = other.mimeType; + displayMethod = other.displayMethod; + src = other.src; + + } else if (srcContent.length > 0 && FileMode.SYMLINK != mode) { + mimeType = registry.getMimeType(path, srcContent); + if ("image".equals(mimeType.getMediaType()) + && registry.isSafeInline(mimeType)) { + displayMethod = DisplayMethod.IMG; + } } } diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptFactory.java index ee8241837b..d606577a3d 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptFactory.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptFactory.java @@ -177,7 +177,7 @@ class PatchScriptFactory extends Handler { b.setRepository(git); b.setChange(change); b.setDiffPrefs(dp); - b.setTrees(list.getOldId(), list.getNewId()); + b.setTrees(list.isAgainstParent(), list.getOldId(), list.getNewId()); return b; } diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Patch.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Patch.java index 22ec747a3c..28bf53cd5d 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Patch.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Patch.java @@ -19,6 +19,9 @@ import com.google.gwtorm.client.StringKey; /** A single modified file in a {@link PatchSet}. */ public final class Patch { + /** Magical file name which represents the commit message. */ + public static final String COMMIT_MSG = "/COMMIT_MSG"; + public static class Key extends StringKey { private static final long serialVersionUID = 1L; diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java index be6360fe95..fb7f32aae8 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java @@ -92,9 +92,13 @@ public class CommentSender extends ReplyToChangeSender { if (!pk.equals(currentFileKey)) { appendText("....................................................\n"); - appendText("File "); - appendText(pk.get()); - appendText("\n"); + if (Patch.COMMIT_MSG.equals(pk.get())) { + appendText("Commit Message\n"); + } else { + appendText("File "); + appendText(pk.get()); + appendText("\n"); + } currentFileKey = pk; if (patchList != null) { diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchFile.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchFile.java index 6c751dca46..22be063ebb 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchFile.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchFile.java @@ -51,14 +51,28 @@ public class PatchFile { final RevWalk rw = new RevWalk(repo); final RevCommit bCommit = rw.parseCommit(patchList.getNewId()); - if (patchList.getOldId() != null) { - aTree = rw.parseTree(patchList.getOldId()); + + if (Patch.COMMIT_MSG.equals(fileName)) { + if (patchList.isAgainstParent()) { + a = Text.EMPTY; + } else { + a = Text.forCommit(repo, patchList.getOldId()); + } + b = Text.forCommit(repo, bCommit); + + aTree = null; + bTree = null; + } else { - final RevCommit p = bCommit.getParent(0); - rw.parseHeaders(p); - aTree = p.getTree(); + if (patchList.getOldId() != null) { + aTree = rw.parseTree(patchList.getOldId()); + } else { + final RevCommit p = bCommit.getParent(0); + rw.parseHeaders(p); + aTree = p.getTree(); + } + bTree = bCommit.getTree(); } - bTree = bCommit.getTree(); } /** diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchList.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchList.java index a5121e925b..a8e3cf8775 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchList.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchList.java @@ -60,15 +60,22 @@ public class PatchList implements Serializable { private transient ObjectId oldId; private transient ObjectId newId; private transient boolean intralineDifference; + private transient boolean againstParent; private transient PatchListEntry[] patches; PatchList(@Nullable final AnyObjectId oldId, final AnyObjectId newId, - final boolean intralineDifference, final PatchListEntry[] patches) { + final boolean intralineDifference, final boolean againstParent, + final PatchListEntry[] patches) { this.oldId = oldId != null ? oldId.copy() : null; this.newId = newId.copy(); this.intralineDifference = intralineDifference; + this.againstParent = againstParent; + + // We assume index 0 contains the magic commit message entry. + if (patches.length > 1) { + Arrays.sort(patches, 1, patches.length, PATCH_CMP); + } - Arrays.sort(patches, PATCH_CMP); this.patches = patches; } @@ -93,6 +100,11 @@ public class PatchList implements Serializable { return intralineDifference; } + /** @return true if {@link #getOldId} is {@link #getNewId}'s ancestor. */ + public boolean isAgainstParent() { + return againstParent; + } + /** * Get a sorted, modifiable list of all files in this list. *

@@ -144,6 +156,7 @@ public class PatchList implements Serializable { writeCanBeNull(out, oldId); writeNotNull(out, newId); writeVarInt32(out, intralineDifference ? 1 : 0); + writeVarInt32(out, againstParent ? 1 : 0); writeVarInt32(out, patches.length); for (PatchListEntry p : patches) { p.writeTo(out); @@ -161,6 +174,7 @@ public class PatchList implements Serializable { oldId = readCanBeNull(in); newId = readNotNull(in); intralineDifference = readVarInt32(in) != 0; + againstParent = readVarInt32(in) != 0; final int cnt = readVarInt32(in); final PatchListEntry[] all = new PatchListEntry[cnt]; for (int i = 0; i < all.length; i++) { diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java index f55763f75d..bb96237226 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java @@ -62,6 +62,7 @@ package com.google.gerrit.server.patch; import com.google.gerrit.reviewdb.Change; +import com.google.gerrit.reviewdb.Patch; import com.google.gerrit.reviewdb.PatchSet; import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.AccountDiffPreference.Whitespace; @@ -80,6 +81,7 @@ import com.google.inject.name.Named; import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.diff.DiffFormatter; import org.eclipse.jgit.diff.Edit; +import org.eclipse.jgit.diff.EditList; import org.eclipse.jgit.diff.MyersDiff; import org.eclipse.jgit.diff.RawText; import org.eclipse.jgit.diff.RawTextIgnoreAllWhitespace; @@ -87,7 +89,6 @@ import org.eclipse.jgit.diff.RawTextIgnoreTrailingWhitespace; import org.eclipse.jgit.diff.RawTextIgnoreWhitespaceChange; import org.eclipse.jgit.diff.RenameDetector; import org.eclipse.jgit.diff.ReplaceEdit; -import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; @@ -98,6 +99,7 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.patch.FileHeader; import org.eclipse.jgit.patch.FileHeader.PatchType; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.treewalk.TreeWalk; @@ -185,15 +187,50 @@ public class PatchListCacheImpl implements PatchListCache { final Repository repo) throws IOException { // TODO(jeffschu) correctly handle merge commits - final RevWalk rw = new RevWalk(repo); - final RevCommit b = rw.parseCommit(key.getNewId()); - final AnyObjectId a = aFor(key, repo, b); - - if (a == null) { - return new PatchList(a, b, computeIntraline, new PatchListEntry[0]); + RawText.Factory rawTextFactory; + switch (key.getWhitespace()) { + case IGNORE_ALL_SPACE: + rawTextFactory = RawTextIgnoreAllWhitespace.FACTORY; + break; + case IGNORE_SPACE_AT_EOL: + rawTextFactory = RawTextIgnoreTrailingWhitespace.FACTORY; + break; + case IGNORE_SPACE_CHANGE: + rawTextFactory = RawTextIgnoreWhitespaceChange.FACTORY; + break; + case IGNORE_NONE: + default: + rawTextFactory = RawText.FACTORY; + break; + } + + final RevWalk rw = new RevWalk(repo); + final RevCommit b = rw.parseCommit(key.getNewId()); + final RevObject a = aFor(key, repo, rw, b); + + if (a == null) { + // This is a merge commit, compared to its ancestor. + // + final PatchListEntry[] entries = new PatchListEntry[1]; + entries[0] = newCommitMessage(rawTextFactory, repo, null, b); + return new PatchList(a, b, computeIntraline, true, entries); + } + + final boolean againstParent = + b.getParentCount() > 0 && b.getParent(0) == a; + + RevCommit aCommit; + RevTree aTree; + if (a instanceof RevCommit) { + aCommit = (RevCommit) a; + aTree = aCommit.getTree(); + } else if (a instanceof RevTree) { + aCommit = null; + aTree = (RevTree) a; + } else { + throw new IOException("Unexpected type: " + a.getClass()); } - RevTree aTree = rw.parseTree(a); RevTree bTree = b.getTree(); final TreeWalk walk = new TreeWalk(repo); @@ -205,32 +242,53 @@ public class PatchListCacheImpl implements PatchListCache { DiffFormatter df = new DiffFormatter(DisabledOutputStream.INSTANCE); df.setRepository(repo); - switch (key.getWhitespace()) { - case IGNORE_ALL_SPACE: - df.setRawTextFactory(RawTextIgnoreAllWhitespace.FACTORY); - break; - case IGNORE_NONE: - df.setRawTextFactory(RawText.FACTORY); - break; - case IGNORE_SPACE_AT_EOL: - df.setRawTextFactory(RawTextIgnoreTrailingWhitespace.FACTORY); - break; - case IGNORE_SPACE_CHANGE: - df.setRawTextFactory(RawTextIgnoreWhitespaceChange.FACTORY); - break; - } + df.setRawTextFactory(rawTextFactory); RenameDetector rd = new RenameDetector(repo); rd.addAll(DiffEntry.scan(walk)); List diffEntries = rd.compute(); final int cnt = diffEntries.size(); - final PatchListEntry[] entries = new PatchListEntry[cnt]; + final PatchListEntry[] entries = new PatchListEntry[1 + cnt]; + entries[0] = newCommitMessage(rawTextFactory, repo, // + againstParent ? null : aCommit, b); for (int i = 0; i < cnt; i++) { FileHeader fh = df.createFileHeader(diffEntries.get(i)); - entries[i] = newEntry(repo, aTree, bTree, fh); + entries[1 + i] = newEntry(repo, aTree, bTree, fh); } - return new PatchList(a, b, computeIntraline, entries); + return new PatchList(a, b, computeIntraline, againstParent, entries); + } + + private PatchListEntry newCommitMessage( + final RawText.Factory rawTextFactory, final Repository repo, + final RevCommit aCommit, final RevCommit bCommit) throws IOException { + StringBuilder hdr = new StringBuilder(); + + hdr.append("diff --git"); + if (aCommit != null) { + hdr.append(" a/" + Patch.COMMIT_MSG); + } else { + hdr.append(" " + FileHeader.DEV_NULL); + } + hdr.append(" b/" + Patch.COMMIT_MSG); + hdr.append("\n"); + + if (aCommit != null) { + hdr.append("--- a/" + Patch.COMMIT_MSG + "\n"); + } else { + hdr.append("--- " + FileHeader.DEV_NULL + "\n"); + } + hdr.append("+++ b/" + Patch.COMMIT_MSG + "\n"); + + Text aText = aCommit != null ? Text.forCommit(repo, aCommit) : Text.EMPTY; + Text bText = Text.forCommit(repo, bCommit); + + byte[] rawHdr = hdr.toString().getBytes("UTF-8"); + RawText aRawText = rawTextFactory.create(aText.getContent()); + RawText bRawText = rawTextFactory.create(bText.getContent()); + EditList edits = new MyersDiff(aRawText, bRawText).getEdits(); + FileHeader fh = new FileHeader(rawHdr, edits, PatchType.UNIFIED); + return newEntry(repo, aText, bText, edits, null, null, fh); } private PatchListEntry newEntry(Repository repo, RevTree aTree, @@ -262,9 +320,12 @@ public class PatchListCacheImpl implements PatchListCache { return new PatchListEntry(fileHeader, edits); } - Text aContent = null; - Text bContent = null; + return newEntry(repo, null, null, edits, aTree, bTree, fileHeader); + } + private PatchListEntry newEntry(Repository repo, Text aContent, + Text bContent, List edits, RevTree aTree, RevTree bTree, + FileHeader fileHeader) throws IOException { for (int i = 0; i < edits.size(); i++) { Edit e = edits.get(i); @@ -542,17 +603,21 @@ public class PatchListCacheImpl implements PatchListCache { return new Text(ldr.getCachedBytes()); } - private static AnyObjectId aFor(final PatchListKey key, - final Repository repo, final RevCommit b) throws IOException { + private static RevObject aFor(final PatchListKey key, + final Repository repo, final RevWalk rw, final RevCommit b) + throws IOException { if (key.getOldId() != null) { - return key.getOldId(); + return rw.parseAny(key.getOldId()); } switch (b.getParentCount()) { case 0: - return emptyTree(repo); - case 1: - return b.getParent(0); + return rw.parseAny(emptyTree(repo)); + case 1:{ + RevCommit r = b.getParent(0); + rw.parseBody(r); + return r; + } default: // merge commit, return null to force combined diff behavior return null; diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListKey.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListKey.java index 26ee17bdac..ffc9ac48c7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListKey.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListKey.java @@ -35,7 +35,7 @@ import java.io.Serializable; import javax.annotation.Nullable; public class PatchListKey implements Serializable { - static final long serialVersionUID = 13L; + static final long serialVersionUID = 14L; private transient ObjectId oldId; private transient ObjectId newId; diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/Text.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/Text.java index e5b24115d2..860e4b9512 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/Text.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/Text.java @@ -15,14 +15,21 @@ package com.google.gerrit.server.patch; import org.eclipse.jgit.diff.RawText; +import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.util.RawParseUtils; import org.mozilla.universalchardet.UniversalDetector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; +import java.text.SimpleDateFormat; public class Text extends RawText { private static final Logger log = LoggerFactory.getLogger(Text.class); @@ -31,6 +38,71 @@ public class Text extends RawText { public static final byte[] NO_BYTES = {}; public static final Text EMPTY = new Text(NO_BYTES); + public static Text forCommit(Repository db, AnyObjectId commitId) + throws IOException { + RevWalk rw = new RevWalk(db); + RevCommit c; + if (commitId instanceof RevCommit) { + c = (RevCommit) commitId; + } else { + c = rw.parseCommit(commitId); + } + + StringBuilder b = new StringBuilder(); + switch (c.getParentCount()) { + case 0: + break; + case 1: { + RevCommit p = c.getParent(0); + rw.parseBody(p); + b.append("Parent: "); + b.append(p.abbreviate(db, 8).name()); + b.append(" ("); + b.append(p.getShortMessage()); + b.append(")\n"); + break; + } + default: + for (int i = 0; i < c.getParentCount(); i++) { + RevCommit p = c.getParent(i); + rw.parseBody(p); + b.append(i == 0 ? "Merge Of: " : " "); + b.append(p.abbreviate(db, 8).name()); + b.append(" ("); + b.append(p.getShortMessage()); + b.append(")\n"); + } + } + appendPersonIdent(b, "Author", c.getAuthorIdent()); + appendPersonIdent(b, "Commit", c.getCommitterIdent()); + b.append("\n"); + b.append(c.getFullMessage()); + return new Text(b.toString().getBytes("UTF-8")); + } + + private static void appendPersonIdent(StringBuilder b, String field, + PersonIdent person) { + if (person != null) { + b.append(field + ": "); + if (person.getName() != null) { + b.append(" "); + b.append(person.getName()); + } + if (person.getEmailAddress() != null) { + b.append(" <"); + b.append(person.getEmailAddress()); + b.append(">"); + } + b.append("\n"); + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ZZZ"); + sdf.setTimeZone(person.getTimeZone()); + b.append(field + "Date: "); + b.append(sdf.format(person.getWhen())); + b.append("\n"); + } + } + public static String asString(byte[] content, String encoding) { return new String(content, charset(content, encoding)); }