Skip to content

Commit 2083e0c

Browse files
authored
Add literal binary patch support (#4)
* Add literal binary patch support * Change to a simple boolean for Git-binary patch format * Binary patch should output the full object id * If pre and post images are identical, don't output them during a rename * Upgrade to Gradle 9.2.0
1 parent ad52cac commit 2083e0c

7 files changed

Lines changed: 150 additions & 36 deletions

File tree

gradle/wrapper/gradle-wrapper.jar

-17.7 KB
Binary file not shown.

gradle/wrapper/gradle-wrapper.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
3+
distributionSha256Sum=df67a32e86e3276d011735facb1535f64d0d88df84fa87521e90becc2d735444
4+
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
45
networkTimeout=10000
56
validateDistributionUrl=true
67
zipStoreBase=GRADLE_USER_HOME

gradlew

Lines changed: 7 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gradlew.bat

Lines changed: 13 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jgit/src/main/java/org/openrewrite/jgit/diff/DiffFormatter.java

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@
2525
import java.io.ByteArrayOutputStream;
2626
import java.io.IOException;
2727
import java.io.OutputStream;
28+
import java.util.Arrays;
2829
import java.util.Collection;
2930
import java.util.Collections;
3031
import java.util.List;
32+
import java.util.zip.Deflater;
33+
import java.util.zip.DeflaterOutputStream;
3134

3235
import org.openrewrite.jgit.diff.DiffAlgorithm.SupportedAlgorithm;
3336
import org.openrewrite.jgit.diff.DiffEntry.ChangeType;
@@ -68,6 +71,7 @@
6871
import org.openrewrite.jgit.treewalk.filter.TreeFilter;
6972
import org.openrewrite.jgit.util.LfsFactory;
7073
import org.openrewrite.jgit.util.QuotedString;
74+
import org.openrewrite.jgit.util.io.BinaryHunkOutputStream;
7175

7276
/**
7377
* Format a Git style patch script.
@@ -96,6 +100,8 @@ public class DiffFormatter implements AutoCloseable {
96100

97101
private RawTextComparator comparator = RawTextComparator.DEFAULT;
98102

103+
private boolean binaryPatch = false;
104+
99105
private int binaryFileThreshold = DEFAULT_BINARY_FILE_THRESHOLD;
100106

101107
private String oldPrefix = "a/"; //$NON-NLS-1$
@@ -259,6 +265,10 @@ public void setBinaryFileThreshold(int threshold) {
259265
this.binaryFileThreshold = threshold;
260266
}
261267

268+
public void setBinary(boolean binaryPatch) {
269+
this.binaryPatch = binaryPatch;
270+
}
271+
262272
/**
263273
* Set the prefix applied in front of old file paths.
264274
*
@@ -714,7 +724,7 @@ private static byte[] writeGitLinkText(AbbreviatedObjectId id) {
714724
}
715725

716726
private String format(AbbreviatedObjectId id) {
717-
if (id.isComplete() && reader != null) {
727+
if (id.isComplete() && reader != null && !binaryPatch) {
718728
try {
719729
id = reader.abbreviate(id.toObjectId(), abbreviationLength);
720730
} catch (IOException cannotAbbreviate) {
@@ -764,6 +774,8 @@ public void format(FileHeader head, RawText a, RawText b)
764774
out.write(head.getBuffer(), start, end - start);
765775
if (head.getPatchType() == PatchType.UNIFIED)
766776
format(head.toEditList(), a, b);
777+
else if (head.getPatchType() == PatchType.GIT_BINARY && a != null && b != null)
778+
formatBinary(a, b);
767779
}
768780

769781
/**
@@ -816,6 +828,26 @@ public void format(EditList edits, RawText a, RawText b)
816828
}
817829
}
818830

831+
private void formatBinary(RawText a, RawText b) throws IOException {
832+
byte[] oldImage = a.getRawContent();
833+
byte[] newImage = b.getRawContent();
834+
out.write(encodeASCII("literal " + newImage.length + "\n"));
835+
Deflater deflater = new Deflater();
836+
deflater.setLevel(Deflater.BEST_SPEED);
837+
try (OutputStream os = new DeflaterOutputStream(new BinaryHunkOutputStream(out), deflater, 1024)) {
838+
os.write(newImage);
839+
}
840+
out.write('\n');
841+
out.write(encodeASCII("literal " + oldImage.length + "\n"));
842+
deflater = new Deflater(); // deflater is stateful, reset it
843+
deflater.setLevel(Deflater.BEST_SPEED);
844+
try (OutputStream os = new DeflaterOutputStream(new BinaryHunkOutputStream(out), deflater, 1024)) {
845+
os.write(oldImage);
846+
}
847+
out.write('\n');
848+
out.write('\n');
849+
}
850+
819851
/**
820852
* Output a line of context (unmodified line).
821853
*
@@ -997,17 +1029,45 @@ private FormatResult createFormatResult(DiffEntry ent) throws IOException,
9971029
aRaw = new RawText(writeGitLinkText(ent.getOldId()));
9981030
bRaw = new RawText(writeGitLinkText(ent.getNewId()));
9991031
} else {
1000-
try {
1001-
aRaw = open(OLD, ent);
1002-
bRaw = open(NEW, ent);
1003-
} catch (BinaryBlobException e) {
1004-
// Do nothing; we check for null below.
1005-
formatOldNewPaths(buf, ent);
1006-
buf.write(encodeASCII("Binary files differ\n")); //$NON-NLS-1$
1007-
editList = new EditList();
1008-
type = PatchType.BINARY;
1009-
res.header = new FileHeader(buf.toByteArray(), editList, type);
1032+
if (binaryPatch) {
1033+
try {
1034+
aRaw = open(OLD, ent);
1035+
bRaw = open(NEW, ent);
1036+
} catch (BinaryBlobException e) {
1037+
// Do nothing; we check for null below.
1038+
formatOldNewPaths(buf, ent);
1039+
buf.write(encodeASCII("Binary files differ\n")); //$NON-NLS-1$
1040+
editList = new EditList();
1041+
type = PatchType.BINARY;
1042+
res.header = new FileHeader(buf.toByteArray(), editList, type);
1043+
return res;
1044+
}
1045+
1046+
if (Arrays.equals(aRaw.getRawContent(), bRaw.getRawContent())) {
1047+
type = PatchType.GIT_BINARY;
1048+
res.header = new FileHeader(buf.toByteArray(), type);
1049+
return res;
1050+
}
1051+
1052+
buf.write(encodeASCII("GIT binary patch\n"));
1053+
type = PatchType.GIT_BINARY;
1054+
res.header = new FileHeader(buf.toByteArray(), type);
1055+
res.a = aRaw;
1056+
res.b = bRaw;
10101057
return res;
1058+
} else {
1059+
try {
1060+
aRaw = open(OLD, ent);
1061+
bRaw = open(NEW, ent);
1062+
} catch (BinaryBlobException e) {
1063+
// Do nothing; we check for null below.
1064+
formatOldNewPaths(buf, ent);
1065+
buf.write(encodeASCII("Binary files differ\n")); //$NON-NLS-1$
1066+
editList = new EditList();
1067+
type = PatchType.BINARY;
1068+
res.header = new FileHeader(buf.toByteArray(), editList, type);
1069+
return res;
1070+
}
10111071
}
10121072
}
10131073

@@ -1072,7 +1132,11 @@ private RawText open(DiffEntry.Side side, DiffEntry entry)
10721132

10731133
ObjectLoader ldr = LfsFactory.getInstance().applySmudgeFilter(repository,
10741134
source.open(side, entry), entry.getDiffAttribute());
1075-
return RawText.load(ldr, binaryFileThreshold);
1135+
if (binaryPatch) {
1136+
return RawText.loadBinary(ldr, binaryFileThreshold);
1137+
} else {
1138+
return RawText.load(ldr, binaryFileThreshold);
1139+
}
10761140
}
10771141

10781142
/**

jgit/src/main/java/org/openrewrite/jgit/diff/RawText.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,4 +449,41 @@ public static RawText load(ObjectLoader ldr, int threshold)
449449
return new RawText(data, RawParseUtils.lineMapOrBinary(data, 0, (int) sz));
450450
}
451451
}
452+
453+
/**
454+
* Read a blob object into RawText, or throw BinaryBlobException if the blob
455+
* is binary.
456+
*
457+
* @param ldr
458+
* the ObjectLoader for the blob
459+
* @param threshold
460+
* if the blob is larger than this size, it is always assumed to
461+
* be binary.
462+
* @since 4.10
463+
* @return the RawText representing the blob.
464+
* @throws org.openrewrite.jgit.errors.BinaryBlobException
465+
* if the blob contains binary data.
466+
* @throws java.io.IOException
467+
* if the input could not be read.
468+
*/
469+
public static RawText loadBinary(ObjectLoader ldr, int threshold)
470+
throws IOException, BinaryBlobException {
471+
long sz = ldr.getSize();
472+
473+
if (sz > threshold) {
474+
throw new BinaryBlobException();
475+
}
476+
477+
try (InputStream stream = ldr.openStream()) {
478+
byte[] data;
479+
try {
480+
data = new byte[(int) sz];
481+
} catch (OutOfMemoryError e) {
482+
throw new LargeObjectException.OutOfMemory(e);
483+
}
484+
485+
IO.readFully(stream, data, 0, (int) sz);
486+
return new RawText(data);
487+
}
488+
}
452489
}

jgit/src/main/java/org/openrewrite/jgit/patch/FileHeader.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,17 +108,29 @@ public enum PatchType {
108108
*
109109
* @param headerLines
110110
* buffer holding the diff header for this file
111-
* @param edits
112-
* the edits for this file
113111
* @param type
114112
* the type of patch used to modify this file
115113
*/
116-
public FileHeader(byte[] headerLines, EditList edits, PatchType type) {
114+
public FileHeader(byte[] headerLines, PatchType type) {
117115
this(headerLines, 0);
118116
endOffset = headerLines.length;
119117
int ptr = parseGitFileName(Patch.DIFF_GIT.length, headerLines.length);
120118
parseGitHeaders(ptr, headerLines.length);
121119
this.patchType = type;
120+
}
121+
122+
/**
123+
* Constructs a new FileHeader
124+
*
125+
* @param headerLines
126+
* buffer holding the diff header for this file
127+
* @param edits
128+
* the edits for this file
129+
* @param type
130+
* the type of patch used to modify this file
131+
*/
132+
public FileHeader(byte[] headerLines, EditList edits, PatchType type) {
133+
this(headerLines, type);
122134
addHunk(new HunkHeader(this, edits));
123135
}
124136

0 commit comments

Comments
 (0)