Skip to content

Instantly share code, notes, and snippets.

@guignol
Last active October 15, 2017 11:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guignol/3267a0a0783f4e03bdf0cd90137f5cb5 to your computer and use it in GitHub Desktop.
Save guignol/3267a0a0783f4e03bdf0cd90137f5cb5 to your computer and use it in GitHub Desktop.
import java.util.List;
public class PatchEditor {
/**
* @param diffHeader diff全体のヘッダー
* @param hunkHeader hunkのヘッダー
* @param hunkLines hunkのボディ
* @param range 選択範囲
* @param unstage unstageかどうか
* @return 選択範囲を反映したパッチ文字列
*/
static String edit(final String diffHeader,
final String hunkHeader,
final List<String> hunkLines,
final Range range,
final boolean unstage) {
// @@ -2,7 +2,7 @@
// @@ -1 +1,2 @@
final String hunkStart = hunkHeader.split(" ")[1].split(",")[0].split("-")[1];
final int offset = Integer.parseInt(hunkStart);
final PreContextBuffer pre_context = new PreContextBuffer();
int before = 0;
int after = 0;
final StringBuilder patch = new StringBuilder();
// (選択範囲外にある)applyしないためcontext lineとして扱う行のprefix
final String to_context = unstage ? "+" : "-";
for (int i = 0; i < hunkLines.size(); i++) {
final String line = hunkLines.get(i);
// 選択範囲内の、プラスまたはマイナスで始まる行
if (range.contains(i) && (line.startsWith("-") || line.startsWith("+"))) {
// a line to stage/unstage
if (line.startsWith("-")) {
before++;
patch.append(pre_context.flush());
patch.append(line);
patch.append("\n");
} else {
after++;
patch.append(line);
patch.append("\n");
}
}
// 選択範囲かどうかに関係なく、プラスまたはマイナスで始まらない行
else if (!line.startsWith("-") && !line.startsWith("+")) {
// context line
patch.append(pre_context.flush());
patch.append(line);
patch.append("\n");
// Skip the "\ No newline at end of
// file". Depending on the locale setting
// we don't know what this line looks
// like exactly. The only thing we do
// know is that it starts with "\ "
if (!line.startsWith("\\ ")) {
// No newline at end of fileの行以外
before++;
after++;
}
}
// 選択範囲外の、プラスまたはマイナスで始まる行のうち
// apply先(※)には存在しているため、context lineとして扱うもの
// ※stageではindex、unstageではワークスペース
else if (line.startsWith(to_context)) {
// turn change line into context line
final String contextLine = " " + line.substring(1); // 2文字目以降
if (line.startsWith("-")) {
pre_context.appendLine(contextLine);
} else {
patch.append(contextLine);
patch.append("\n");
}
before++;
after++;
}
// 選択範囲外の、プラスまたはマイナスで始まる行のうち
// apply先には存在しないため行を削除するもの
else {
// a change in the opposite direction of
// to_context which is outside the range of
// lines to apply.
patch.append(pre_context.flush());
}
}
patch.append(pre_context.flush());
final String newHunkHeader = String.format("@@ -%d,%d +%d,%d @@\n", offset, before, offset, after);
final String wholePatch = newHunkHeader + patch.toString();
return diffHeader + wholePatch;
}
}
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collector;
import java.util.stream.Collectors;
class PreContextBuffer {
static final Collector<CharSequence, ?, String> WITH_LINE_BREAK = Collectors.joining(
"\n",
"",
"\n");
private final List<String> lines = new ArrayList<>();
void appendLine(String line) {
lines.add(line);
}
String flush() {
if (lines.isEmpty()) {
return "";
} else {
final String collect = lines.stream().collect(WITH_LINE_BREAK);
lines.clear();
return collect;
}
}
}
class Range {
final int begin;
final int end; // 含む
Range(int begin, int end) {
this.begin = begin;
this.end = end;
}
boolean exists() {
return 0 <= begin && 0 <= end && 0 <= end - begin;
}
boolean contains(int target) {
return exists() && begin <= target && target <= end;
}
@Override
public String toString() {
return begin + " , " + end;
}
}
@guignol
Copy link
Author

guignol commented Oct 15, 2017

git-guiのapply_range_or_lineのjavaへの改変移植です。
apply_range_or_lineとの違いは以下です。

  • 複数のhunkを扱わず、1つのhunkのみ扱う
  • コマンドは作成せず、パッチのみ作成

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment