Skip to content

Commit 694a164

Browse files
committed
Add literal binary patch support
1 parent 2e6c318 commit 694a164

File tree

3 files changed

+121
-15
lines changed

3 files changed

+121
-15
lines changed

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

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import java.util.Collection;
2929
import java.util.Collections;
3030
import java.util.List;
31+
import java.util.zip.Deflater;
32+
import java.util.zip.DeflaterOutputStream;
3133

3234
import org.openrewrite.jgit.diff.DiffAlgorithm.SupportedAlgorithm;
3335
import org.openrewrite.jgit.diff.DiffEntry.ChangeType;
@@ -68,6 +70,7 @@
6870
import org.openrewrite.jgit.treewalk.filter.TreeFilter;
6971
import org.openrewrite.jgit.util.LfsFactory;
7072
import org.openrewrite.jgit.util.QuotedString;
73+
import org.openrewrite.jgit.util.io.BinaryHunkOutputStream;
7174

7275
/**
7376
* Format a Git style patch script.
@@ -96,6 +99,8 @@ public class DiffFormatter implements AutoCloseable {
9699

97100
private RawTextComparator comparator = RawTextComparator.DEFAULT;
98101

102+
private PatchType patchType = PatchType.UNIFIED;
103+
99104
private int binaryFileThreshold = DEFAULT_BINARY_FILE_THRESHOLD;
100105

101106
private String oldPrefix = "a/"; //$NON-NLS-1$
@@ -259,6 +264,10 @@ public void setBinaryFileThreshold(int threshold) {
259264
this.binaryFileThreshold = threshold;
260265
}
261266

267+
public void setPatchType(PatchType type) {
268+
this.patchType = type;
269+
}
270+
262271
/**
263272
* Set the prefix applied in front of old file paths.
264273
*
@@ -714,7 +723,7 @@ private static byte[] writeGitLinkText(AbbreviatedObjectId id) {
714723
}
715724

716725
private String format(AbbreviatedObjectId id) {
717-
if (id.isComplete() && reader != null) {
726+
if (id.isComplete() && reader != null && patchType != PatchType.GIT_BINARY) {
718727
try {
719728
id = reader.abbreviate(id.toObjectId(), abbreviationLength);
720729
} catch (IOException cannotAbbreviate) {
@@ -764,6 +773,8 @@ public void format(FileHeader head, RawText a, RawText b)
764773
out.write(head.getBuffer(), start, end - start);
765774
if (head.getPatchType() == PatchType.UNIFIED)
766775
format(head.toEditList(), a, b);
776+
else if (head.getPatchType() == PatchType.GIT_BINARY)
777+
formatBinary(a, b);
767778
}
768779

769780
/**
@@ -816,6 +827,26 @@ public void format(EditList edits, RawText a, RawText b)
816827
}
817828
}
818829

830+
private void formatBinary(RawText a, RawText b) throws IOException {
831+
byte[] oldImage = a.getRawContent();
832+
byte[] newImage = b.getRawContent();
833+
out.write(encodeASCII("literal " + newImage.length + "\n"));
834+
Deflater deflater = new Deflater();
835+
deflater.setLevel(Deflater.BEST_SPEED);
836+
try (OutputStream os = new DeflaterOutputStream(new BinaryHunkOutputStream(out), deflater, 1024)) {
837+
os.write(newImage);
838+
}
839+
out.write('\n');
840+
out.write(encodeASCII("literal " + oldImage.length + "\n"));
841+
deflater = new Deflater(); // deflater is stateful, reset it
842+
deflater.setLevel(Deflater.BEST_SPEED);
843+
try (OutputStream os = new DeflaterOutputStream(new BinaryHunkOutputStream(out), deflater, 1024)) {
844+
os.write(oldImage);
845+
}
846+
out.write('\n');
847+
out.write('\n');
848+
}
849+
819850
/**
820851
* Output a line of context (unmodified line).
821852
*
@@ -997,17 +1028,39 @@ private FormatResult createFormatResult(DiffEntry ent) throws IOException,
9971028
aRaw = new RawText(writeGitLinkText(ent.getOldId()));
9981029
bRaw = new RawText(writeGitLinkText(ent.getNewId()));
9991030
} 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);
1031+
if (patchType == PatchType.GIT_BINARY) {
1032+
try {
1033+
aRaw = open(OLD, ent);
1034+
bRaw = open(NEW, ent);
1035+
} catch (BinaryBlobException e) {
1036+
// Do nothing; we check for null below.
1037+
formatOldNewPaths(buf, ent);
1038+
buf.write(encodeASCII("Binary files differ\n")); //$NON-NLS-1$
1039+
editList = new EditList();
1040+
type = PatchType.BINARY;
1041+
res.header = new FileHeader(buf.toByteArray(), editList, type);
1042+
return res;
1043+
}
1044+
1045+
buf.write(encodeASCII("GIT binary patch\n"));
1046+
type = PatchType.GIT_BINARY;
1047+
res.header = new FileHeader(buf.toByteArray(), type);
1048+
res.a = aRaw;
1049+
res.b = bRaw;
10101050
return res;
1051+
} else {
1052+
try {
1053+
aRaw = open(OLD, ent);
1054+
bRaw = open(NEW, ent);
1055+
} catch (BinaryBlobException e) {
1056+
// Do nothing; we check for null below.
1057+
formatOldNewPaths(buf, ent);
1058+
buf.write(encodeASCII("Binary files differ\n")); //$NON-NLS-1$
1059+
editList = new EditList();
1060+
type = PatchType.BINARY;
1061+
res.header = new FileHeader(buf.toByteArray(), editList, type);
1062+
return res;
1063+
}
10111064
}
10121065
}
10131066

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

10731126
ObjectLoader ldr = LfsFactory.getInstance().applySmudgeFilter(repository,
10741127
source.open(side, entry), entry.getDiffAttribute());
1075-
return RawText.load(ldr, binaryFileThreshold);
1128+
if (patchType == PatchType.GIT_BINARY) {
1129+
return RawText.loadBinary(ldr, binaryFileThreshold);
1130+
} else {
1131+
return RawText.load(ldr, binaryFileThreshold);
1132+
}
10761133
}
10771134

10781135
/**

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)