Enable commenting on commit messages
The commit message is now shown as a magical "Commit Message" file at the start of each patch listing. Normally its compared against the empty blob content, and thus appears all new. When comparing between two patch sets the message is compared against the message of the other patch set, enabling a fast view of what was modified in the message. When showing a commit message we use a friendly format that looks a lot like the `git log --format=fuller` layout, only we show the parent commit and its short subject in addition to the other headers. This helps reviewers to identify a rebase, or easily see merges. Comments made against the commit message are saved under the file "/COMMIT_MSG". Any name starting with "/" is legal in a Git repository because the empty string is not a valid tree entry name. Consequently we are reasonably safe to use "/" to denote a meta file which will never appear in real repositories. The name "COMMIT_MSG" comes from the name of the file some versions of Git used to store the message-in-progress under ".git" in, and really has no important meaning other than to be unique within a Gerrit database. Bug: issue 361 Change-Id: I11004eddfa7ab10e6491eedcf437771bf1e5af64 Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
@@ -59,6 +59,7 @@ public interface ChangeConstants extends Constants {
|
||||
String patchTableDiffUnified();
|
||||
String patchTableDownloadPreImage();
|
||||
String patchTableDownloadPostImage();
|
||||
String commitMessage();
|
||||
|
||||
String patchTablePrev();
|
||||
String patchTableNext();
|
||||
|
@@ -39,6 +39,7 @@ patchTableDiffSideBySide = Side-by-Side
|
||||
patchTableDiffUnified = Unified
|
||||
patchTableDownloadPreImage = old
|
||||
patchTableDownloadPostImage = new
|
||||
commitMessage = Commit Message
|
||||
|
||||
patchTablePrev = Previous file
|
||||
patchTableNext = Next file
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@@ -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();
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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) {
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -177,7 +177,7 @@ class PatchScriptFactory extends Handler<PatchScript> {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@@ -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<PatchSet.Id> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
@@ -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) {
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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.
|
||||
* <p>
|
||||
@@ -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++) {
|
||||
|
@@ -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<DiffEntry> 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<Edit> 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;
|
||||
|
@@ -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;
|
||||
|
@@ -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));
|
||||
}
|
||||
|
Reference in New Issue
Block a user