Merge changes Ie595754a,I02523870 * changes: ChangeScreen: Show file size increase/decrease for binary files FileInfo: Include size delta
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt index 806d4cd..7e96556 100644 --- a/Documentation/rest-api-changes.txt +++ b/Documentation/rest-api-changes.txt
@@ -413,29 +413,36 @@ }, "files": { "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeCache.java": { - "lines_deleted": 8 + "lines_deleted": 8, + "size_delta": -412 }, "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeDetailCache.java": { - "lines_inserted": 1 + "lines_inserted": 1, + "size_delta": 23 }, "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java": { "lines_inserted": 11, - "lines_deleted": 19 + "lines_deleted": 19, + "size_delta": -298 }, "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java": { "lines_inserted": 23, - "lines_deleted": 20 + "lines_deleted": 20, + "size_delta": 132 }, "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarCache.java": { "status": "D", - "lines_deleted": 139 + "lines_deleted": 139, + "size_delta": -5512 }, "gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarredChanges.java": { "status": "A", - "lines_inserted": 204 + "lines_inserted": 204, + "size_delta": 8345 }, "gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java": { - "lines_deleted": 9 + "lines_deleted": 9, + "size_delta": -343 } } } @@ -3262,11 +3269,13 @@ { "/COMMIT_MSG": { "status": "A", - "lines_inserted": 7 + "lines_inserted": 7, + "size_delta": 551 }, "gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java": { "lines_inserted": 5, - "lines_deleted": 3 + "lines_deleted": 3, + "size_delta": 98 } } ---- @@ -4207,6 +4216,8 @@ |`lines_deleted` |optional| Number of deleted lines. + Not set for binary files or if no lines were deleted. +|`size_delta` || +Number of bytes by which the file size increased/decreased. |============================= [[fix-input]]
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/FileInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/FileInfo.java index 58f5494..00d0c18 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/FileInfo.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/FileInfo.java
@@ -20,4 +20,5 @@ public String oldPath; public Integer linesInserted; public Integer linesDeleted; + public long sizeDelta; }
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/FileInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/FileInfo.java index b21078e..d95f9ef 100644 --- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/FileInfo.java +++ b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/FileInfo.java
@@ -30,6 +30,15 @@ public final native boolean binary() /*-{ return this.binary || false; }-*/; public final native String status() /*-{ return this.status; }-*/; + + // JSNI methods cannot have 'long' as a parameter type or a return type and + // it's suggested to use double in this case: + // http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html#important + public final long sizeDelta() { + return (long)_sizeDelta(); + } + private final native double _sizeDelta() /*-{ return this.size_delta || 0; }-*/; + public final native int _row() /*-{ return this._row }-*/; public final native void _row(int r) /*-{ this._row = r }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java index 80aa9cc..08fe75d 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
@@ -17,6 +17,7 @@ import com.google.gerrit.client.info.AccountInfo; import com.google.gerrit.client.info.AccountPreferencesInfo; import com.google.gerrit.reviewdb.client.Account; +import com.google.gwt.i18n.client.NumberFormat; import java.util.Date; @@ -107,4 +108,19 @@ private static AccountFormatter createAccountFormatter() { return new AccountFormatter(Gerrit.info().user().anonymousCowardName()); } + + public static String formatBytes(long bytes) { + if (bytes == 0) { + return "+/- 0 B"; + } + + if (Math.abs(bytes) < 1024) { + return (bytes > 0 ? "+" : "") + bytes + " B"; + } + + int exp = (int) (Math.log(Math.abs(bytes)) / Math.log(1024)); + return (bytes > 0 ? "+" : "") + + NumberFormat.getFormat("#.0").format(bytes / Math.pow(1024, exp)) + + " " + "KMGTPE".charAt(exp - 1) + "iB"; + } }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java index ad61446..932cda4 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java
@@ -15,6 +15,7 @@ package com.google.gerrit.client.change; import com.google.gerrit.client.Dispatcher; +import com.google.gerrit.client.FormatUtil; import com.google.gerrit.client.Gerrit; import com.google.gerrit.client.VoidResult; import com.google.gerrit.client.changes.ChangeApi; @@ -750,6 +751,8 @@ .append(info.linesDeleted()); } } + } else if (info.binary()) { + sb.append(FormatUtil.formatBytes(info.sizeDelta())); } sb.closeTd(); }
diff --git a/gerrit-gwtui/src/test/java/com/google/gerrit/client/FormatUtilTest.java b/gerrit-gwtui/src/test/java/com/google/gerrit/client/FormatUtilTest.java new file mode 100644 index 0000000..6ea09fd --- /dev/null +++ b/gerrit-gwtui/src/test/java/com/google/gerrit/client/FormatUtilTest.java
@@ -0,0 +1,49 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// 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.google.gerrit.client; + +import static org.junit.Assert.assertEquals; + +import com.googlecode.gwt.test.GwtModule; +import com.googlecode.gwt.test.GwtTest; + +import static com.google.gerrit.client.FormatUtil.formatBytes; + +import org.junit.Ignore; +import org.junit.Test; + +@GwtModule("com.google.gerrit.GerritGwtUI") +@Ignore +public class FormatUtilTest extends GwtTest { + @Test + public void testFormatBytes() { + assertEquals("+/- 0 B", formatBytes(0)); + assertEquals("+27 B", formatBytes(27)); + assertEquals("+999 B", formatBytes(999)); + assertEquals("+1000 B", formatBytes(1000)); + assertEquals("+1023 B", formatBytes(1023)); + assertEquals("+1.0 KiB", formatBytes(1024)); + assertEquals("+1.7 KiB", formatBytes(1728)); + assertEquals("+108.0 KiB", formatBytes(110592)); + assertEquals("+6.8 MiB", formatBytes(7077888)); + assertEquals("+432.0 MiB", formatBytes(452984832)); + assertEquals("+27.0 GiB", formatBytes(28991029248L)); + assertEquals("+1.7 TiB", formatBytes(1855425871872L)); + assertEquals("+8.0 EiB", formatBytes(9223372036854775807L)); + + assertEquals("-27 B", formatBytes(-27)); + assertEquals("-1.7 MiB", formatBytes(-1728)); + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/FileInfoJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/FileInfoJson.java index 9ddb0ed..84f8a04 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/FileInfoJson.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/FileInfoJson.java
@@ -63,6 +63,7 @@ d.status = e.getChangeType() != Patch.ChangeType.MODIFIED ? e.getChangeType().getCode() : null; d.oldPath = e.getOldName(); + d.sizeDelta = e.getSizeDelta(); if (e.getPatchType() == Patch.PatchType.BINARY) { d.binary = true; } else { @@ -76,6 +77,7 @@ // when the file was rewritten and too little content survived. Write // a single record with data from both sides. d.status = Patch.ChangeType.REWRITE.getCode(); + d.sizeDelta = o.sizeDelta; if (o.binary != null && o.binary) { d.binary = true; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListEntry.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListEntry.java index e7c56be..d0069ec 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListEntry.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListEntry.java
@@ -18,10 +18,12 @@ import static com.google.gerrit.server.ioutil.BasicSerialization.readEnum; import static com.google.gerrit.server.ioutil.BasicSerialization.readString; import static com.google.gerrit.server.ioutil.BasicSerialization.readVarInt32; +import static com.google.gerrit.server.ioutil.BasicSerialization.readFixInt64; import static com.google.gerrit.server.ioutil.BasicSerialization.writeBytes; import static com.google.gerrit.server.ioutil.BasicSerialization.writeEnum; import static com.google.gerrit.server.ioutil.BasicSerialization.writeString; import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32; +import static com.google.gerrit.server.ioutil.BasicSerialization.writeFixInt64; import com.google.gerrit.reviewdb.client.Patch; import com.google.gerrit.reviewdb.client.Patch.ChangeType; @@ -49,7 +51,7 @@ static PatchListEntry empty(final String fileName) { return new PatchListEntry(ChangeType.MODIFIED, PatchType.UNIFIED, null, - fileName, EMPTY_HEADER, Collections.<Edit> emptyList(), 0, 0); + fileName, EMPTY_HEADER, Collections.<Edit> emptyList(), 0, 0, 0); } private final ChangeType changeType; @@ -60,8 +62,9 @@ private final List<Edit> edits; private final int insertions; private final int deletions; + private final long sizeDelta; - PatchListEntry(final FileHeader hdr, List<Edit> editList) { + PatchListEntry(FileHeader hdr, List<Edit> editList, long sizeDelta) { changeType = toChangeType(hdr); patchType = toPatchType(hdr); @@ -106,12 +109,12 @@ } insertions = ins; deletions = del; + this.sizeDelta = sizeDelta; } - private PatchListEntry(final ChangeType changeType, - final PatchType patchType, final String oldName, final String newName, - final byte[] header, final List<Edit> edits, final int insertions, - final int deletions) { + private PatchListEntry(ChangeType changeType, PatchType patchType, + String oldName, String newName, byte[] header, List<Edit> edits, + int insertions, int deletions, long sizeDelta) { this.changeType = changeType; this.patchType = patchType; this.oldName = oldName; @@ -120,6 +123,7 @@ this.edits = edits; this.insertions = insertions; this.deletions = deletions; + this.sizeDelta = sizeDelta; } int weigh() { @@ -166,6 +170,10 @@ return deletions; } + public long getSizeDelta() { + return sizeDelta; + } + public List<String> getHeaderLines() { final IntList m = RawParseUtils.lineMap(header, 0, header.length); final List<String> headerLines = new ArrayList<>(m.size() - 1); @@ -190,7 +198,7 @@ return p; } - void writeTo(final OutputStream out) throws IOException { + void writeTo(OutputStream out) throws IOException { writeEnum(out, changeType); writeEnum(out, patchType); writeString(out, oldName); @@ -198,6 +206,7 @@ writeBytes(out, header); writeVarInt32(out, insertions); writeVarInt32(out, deletions); + writeFixInt64(out, sizeDelta); writeVarInt32(out, edits.size()); for (final Edit e : edits) { @@ -208,17 +217,18 @@ } } - static PatchListEntry readFrom(final InputStream in) throws IOException { - final ChangeType changeType = readEnum(in, ChangeType.values()); - final PatchType patchType = readEnum(in, PatchType.values()); - final String oldName = readString(in); - final String newName = readString(in); - final byte[] hdr = readBytes(in); - final int ins = readVarInt32(in); - final int del = readVarInt32(in); + static PatchListEntry readFrom(InputStream in) throws IOException { + ChangeType changeType = readEnum(in, ChangeType.values()); + PatchType patchType = readEnum(in, PatchType.values()); + String oldName = readString(in); + String newName = readString(in); + byte[] hdr = readBytes(in); + int ins = readVarInt32(in); + int del = readVarInt32(in); + long sizeDelta = readFixInt64(in); - final int editCount = readVarInt32(in); - final Edit[] editArray = new Edit[editCount]; + int editCount = readVarInt32(in); + Edit[] editArray = new Edit[editCount]; for (int i = 0; i < editCount; i++) { int beginA = readVarInt32(in); int endA = readVarInt32(in); @@ -228,7 +238,7 @@ } return new PatchListEntry(changeType, patchType, oldName, newName, hdr, - toList(editArray), ins, del); + toList(editArray), ins, del, sizeDelta); } private static List<Edit> toList(Edit[] l) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java index 8eab7b9..42b107e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java
@@ -15,6 +15,8 @@ package com.google.gerrit.server.patch; +import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; + import com.google.common.base.Function; import com.google.common.base.Throwables; import com.google.common.collect.FluentIterable; @@ -59,6 +61,7 @@ import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.util.TemporaryBuffer; import org.eclipse.jgit.util.io.DisabledOutputStream; import org.slf4j.Logger; @@ -196,8 +199,12 @@ DiffEntry diffEntry = diffEntries.get(i); if (paths == null || paths.contains(diffEntry.getNewPath()) || paths.contains(diffEntry.getOldPath())) { + FileHeader fh = toFileHeader(key, df, diffEntry); - entries.add(newEntry(aTree, fh)); + long sizeDelta = + getFileSize(repo, reader, diffEntry.getNewPath(), bTree) + - getFileSize(repo, reader, diffEntry.getOldPath(), aTree); + entries.add(newEntry(aTree, fh, sizeDelta)); } } return new PatchList(a, b, againstParent, @@ -205,6 +212,15 @@ } } + private static long getFileSize(Repository repo, ObjectReader reader, + String path, RevTree t) throws IOException { + try (TreeWalk tw = TreeWalk.forPath(reader, path, t)) { + return tw != null + ? repo.open(tw.getObjectId(0), OBJ_BLOB).getSize() + : 0; + } + } + private FileHeader toFileHeader(PatchListKey key, final DiffFormatter diffFormatter, final DiffEntry diffEntry) throws IOException { @@ -273,32 +289,39 @@ Text bText = Text.forCommit(reader, bCommit); byte[] rawHdr = hdr.toString().getBytes("UTF-8"); - RawText aRawText = new RawText(aText.getContent()); - RawText bRawText = new RawText(bText.getContent()); + byte[] aContent = aText.getContent(); + byte[] bContent = bText.getContent(); + long sizeDelta = bContent.length - aContent.length; + RawText aRawText = new RawText(aContent); + RawText bRawText = new RawText(bContent); EditList edits = new HistogramDiff().diff(cmp, aRawText, bRawText); FileHeader fh = new FileHeader(rawHdr, edits, PatchType.UNIFIED); - return new PatchListEntry(fh, edits); + return new PatchListEntry(fh, edits, sizeDelta); } - private PatchListEntry newEntry(RevTree aTree, FileHeader fileHeader) { + private PatchListEntry newEntry(RevTree aTree, FileHeader fileHeader, + long sizeDelta) { final FileMode oldMode = fileHeader.getOldMode(); final FileMode newMode = fileHeader.getNewMode(); if (oldMode == FileMode.GITLINK || newMode == FileMode.GITLINK) { - return new PatchListEntry(fileHeader, Collections.<Edit> emptyList()); + return new PatchListEntry(fileHeader, Collections.<Edit> emptyList(), + sizeDelta); } if (aTree == null // want combined diff || fileHeader.getPatchType() != PatchType.UNIFIED || fileHeader.getHunks().isEmpty()) { - return new PatchListEntry(fileHeader, Collections.<Edit> emptyList()); + return new PatchListEntry(fileHeader, Collections.<Edit> emptyList(), + sizeDelta); } List<Edit> edits = fileHeader.toEditList(); if (edits.isEmpty()) { - return new PatchListEntry(fileHeader, Collections.<Edit> emptyList()); + return new PatchListEntry(fileHeader, Collections.<Edit> emptyList(), + sizeDelta); } else { - return new PatchListEntry(fileHeader, edits); + return new PatchListEntry(fileHeader, edits, sizeDelta); } }