in org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java [546:701]
private void applyText(Repository repository, String path, RawText rt,
File f, FileHeader fh, CheckoutMetadata checkOut)
throws IOException, PatchApplyException {
List<ByteBuffer> oldLines = new ArrayList<>(rt.size());
for (int i = 0; i < rt.size(); i++) {
oldLines.add(rt.getRawString(i));
}
List<ByteBuffer> newLines = new ArrayList<>(oldLines);
int afterLastHunk = 0;
int lineNumberShift = 0;
int lastHunkNewLine = -1;
for (HunkHeader hh : fh.getHunks()) {
// We assume hunks to be ordered
if (hh.getNewStartLine() <= lastHunkNewLine) {
throw new PatchApplyException(MessageFormat
.format(JGitText.get().patchApplyException, hh));
}
lastHunkNewLine = hh.getNewStartLine();
byte[] b = new byte[hh.getEndOffset() - hh.getStartOffset()];
System.arraycopy(hh.getBuffer(), hh.getStartOffset(), b, 0,
b.length);
RawText hrt = new RawText(b);
List<ByteBuffer> hunkLines = new ArrayList<>(hrt.size());
for (int i = 0; i < hrt.size(); i++) {
hunkLines.add(hrt.getRawString(i));
}
if (hh.getNewStartLine() == 0) {
// Must be the single hunk for clearing all content
if (fh.getHunks().size() == 1
&& canApplyAt(hunkLines, newLines, 0)) {
newLines.clear();
break;
}
throw new PatchApplyException(MessageFormat
.format(JGitText.get().patchApplyException, hh));
}
// Hunk lines as reported by the hunk may be off, so don't rely on
// them.
int applyAt = hh.getNewStartLine() - 1 + lineNumberShift;
// But they definitely should not go backwards.
if (applyAt < afterLastHunk && lineNumberShift < 0) {
applyAt = hh.getNewStartLine() - 1;
lineNumberShift = 0;
}
if (applyAt < afterLastHunk) {
throw new PatchApplyException(MessageFormat
.format(JGitText.get().patchApplyException, hh));
}
boolean applies = false;
int oldLinesInHunk = hh.getLinesContext()
+ hh.getOldImage().getLinesDeleted();
if (oldLinesInHunk <= 1) {
// Don't shift hunks without context lines. Just try the
// position corrected by the current lineNumberShift, and if
// that fails, the position recorded in the hunk header.
applies = canApplyAt(hunkLines, newLines, applyAt);
if (!applies && lineNumberShift != 0) {
applyAt = hh.getNewStartLine() - 1;
applies = applyAt >= afterLastHunk
&& canApplyAt(hunkLines, newLines, applyAt);
}
} else {
int maxShift = applyAt - afterLastHunk;
for (int shift = 0; shift <= maxShift; shift++) {
if (canApplyAt(hunkLines, newLines, applyAt - shift)) {
applies = true;
applyAt -= shift;
break;
}
}
if (!applies) {
// Try shifting the hunk downwards
applyAt = hh.getNewStartLine() - 1 + lineNumberShift;
maxShift = newLines.size() - applyAt - oldLinesInHunk;
for (int shift = 1; shift <= maxShift; shift++) {
if (canApplyAt(hunkLines, newLines, applyAt + shift)) {
applies = true;
applyAt += shift;
break;
}
}
}
}
if (!applies) {
throw new PatchApplyException(MessageFormat
.format(JGitText.get().patchApplyException, hh));
}
// Hunk applies at applyAt. Apply it, and update afterLastHunk and
// lineNumberShift
lineNumberShift = applyAt - hh.getNewStartLine() + 1;
int sz = hunkLines.size();
for (int j = 1; j < sz; j++) {
ByteBuffer hunkLine = hunkLines.get(j);
if (!hunkLine.hasRemaining()) {
// Completely empty line; accept as empty context line
applyAt++;
continue;
}
switch (hunkLine.array()[hunkLine.position()]) {
case ' ':
applyAt++;
break;
case '-':
newLines.remove(applyAt);
break;
case '+':
newLines.add(applyAt++, slice(hunkLine, 1));
break;
default:
break;
}
}
afterLastHunk = applyAt;
}
if (!isNoNewlineAtEndOfFile(fh)) {
newLines.add(null);
}
if (!rt.isMissingNewlineAtEnd()) {
oldLines.add(null);
}
if (oldLines.equals(newLines)) {
return; // Unchanged; don't touch the file
}
TemporaryBuffer buffer = new TemporaryBuffer.LocalFile(null);
try {
try (OutputStream out = buffer) {
for (Iterator<ByteBuffer> l = newLines.iterator(); l
.hasNext();) {
ByteBuffer line = l.next();
if (line == null) {
// Must be the marker for the final newline
break;
}
out.write(line.array(), line.position(), line.remaining());
if (l.hasNext()) {
out.write('\n');
}
}
}
try (OutputStream output = new FileOutputStream(f)) {
DirCacheCheckout.getContent(repository, path, checkOut,
new StreamLoader(buffer::openInputStream,
buffer.length()),
null, output);
}
} finally {
buffer.destroy();
}
repository.getFS().setExecute(f,
fh.getNewMode() == FileMode.EXECUTABLE_FILE);
}