diff options
Diffstat (limited to 'mgrapp/src/com/google/codereview/manager/unpack/DiffReader.java')
-rw-r--r-- | mgrapp/src/com/google/codereview/manager/unpack/DiffReader.java | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/mgrapp/src/com/google/codereview/manager/unpack/DiffReader.java b/mgrapp/src/com/google/codereview/manager/unpack/DiffReader.java new file mode 100644 index 0000000000..866ca7598a --- /dev/null +++ b/mgrapp/src/com/google/codereview/manager/unpack/DiffReader.java @@ -0,0 +1,149 @@ +// Copyright 2008 Google Inc. +// +// 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.codereview.manager.unpack; + +import static org.spearce.jgit.lib.Constants.encodeASCII; + +import com.google.codereview.internal.UploadPatchsetFile.UploadPatchsetFileRequest.StatusType; + +import org.spearce.jgit.lib.Constants; +import org.spearce.jgit.lib.ObjectId; +import org.spearce.jgit.lib.ObjectWriter; +import org.spearce.jgit.lib.Repository; +import org.spearce.jgit.lib.Tree; +import org.spearce.jgit.revwalk.RevCommit; +import org.spearce.jgit.util.RawParseUtils; + +import java.io.IOException; + +/** Parses 'git diff-tree' output into {@link FileDiff} objects. */ +class DiffReader { + private static final byte[] GIT_DIFF = encodeASCII("diff --git a/"); + private static final byte[] H_DELETED_FILE = encodeASCII("deleted file "); + private static final byte[] H_NEW_FILE = encodeASCII("new file "); + private static final byte[] H_INDEX = encodeASCII("index "); + static final byte[] H_NEWPATH = encodeASCII("+++ b/"); + private static final byte[] H_BINARY = encodeASCII("Binary file"); + private static final int MAX_PATCH_SIZE = 1024 * 1024; // bytes + + static boolean match(final byte[] key, final byte[] line, int offset) { + int remain = line.length - offset; + if (remain < key.length) { + return false; + } + for (int k = 0; k < key.length;) { + if (key[k++] != line[offset++]) { + return false; + } + } + return true; + } + + private static String str(final byte[] b, final int off) { + return RawParseUtils.decode(Constants.CHARSET, b, off, b.length); + } + + private Process proc; + private RecordInputStream in; + private FileDiff current; + + DiffReader(final Repository db, final RevCommit c) throws IOException { + final String baseTree; + if (c.getParentCount() > 0) { + baseTree = c.getParent(0).getTree().getId().name(); + } else { + baseTree = new ObjectWriter(db).writeTree(new Tree(db)).name(); + } + final String headTree = c.getTree().getId().name(); + proc = + Runtime.getRuntime().exec( + new String[] {"git", "--git-dir=.", "diff-tree", "--unified=1", + "-M", "--full-index", baseTree, headTree}, null, + db.getDirectory()); + proc.getOutputStream().close(); + proc.getErrorStream().close(); + in = new RecordInputStream(proc.getInputStream()); + } + + FileDiff next() throws IOException { + return readOneDiff(); + } + + private FileDiff readOneDiff() throws IOException { + boolean consume = false; + for (;;) { + final byte[] hdr = in.readRecord('\n'); + if (hdr == null) { + final FileDiff prior = current; + current = null; + return prior; + } + + if (match(GIT_DIFF, hdr, 0)) { + final FileDiff prior = current; + current = new FileDiff(); + current.appendLine(hdr); + + // TODO(sop) This can split the old and new names wrong if the + // old name was "f b/c". Until we can do diffs in-core we'll + // just assume nobody uses spaces in filenames. + // + final String hdrStr = str(hdr, 0); + final int newpos = hdrStr.indexOf(" b/"); + if (newpos > 0) { + current.setFilename(hdrStr.substring(newpos + 3)); + } + + if (prior != null) { + return prior; + } else { + continue; + } + } + + if (match(H_INDEX, hdr, 0)) { + current.setBaseId(ObjectId.fromString(hdr, H_INDEX.length)); + current.setFinalId(ObjectId.fromString(hdr, H_INDEX.length + + Constants.OBJECT_ID_LENGTH * 2 + 2)); + } else if (match(H_NEW_FILE, hdr, 0)) { + current.setStatus(StatusType.ADD); + } else if (match(H_DELETED_FILE, hdr, 0)) { + current.setStatus(StatusType.DELETE); + } else if (match(H_BINARY, hdr, 0)) { + current.setBinary(true); + } else if (match(H_NEWPATH, hdr, 0)) { + current.setFilename(str(hdr, H_NEWPATH.length)); + } + + if (consume) { + continue; + } else if (current.getPatchSize() + hdr.length >= MAX_PATCH_SIZE) { + current.truncatePatch(); + consume = true; + } else { + current.appendLine(hdr); + } + } + } + + void close() throws IOException { + in.close(); + try { + proc.waitFor(); + } catch (InterruptedException ie) { + // + } + } +} |