summaryrefslogtreecommitdiff
path: root/src/pkg/patch/git.go
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2009-11-04 17:55:06 -0800
committerRuss Cox <rsc@golang.org>2009-11-04 17:55:06 -0800
commitce9b3deaa4fa22e9d5580df05e9762b988793baa (patch)
treeeacfeb41baaf6752deba91909b8f96d90dea476e /src/pkg/patch/git.go
parent782c3f76d1c7af511daf0d44ba29737f3e1de655 (diff)
downloadgolang-ce9b3deaa4fa22e9d5580df05e9762b988793baa.tar.gz
package patch
R=r http://go/go-review/1018043
Diffstat (limited to 'src/pkg/patch/git.go')
-rw-r--r--src/pkg/patch/git.go121
1 files changed, 121 insertions, 0 deletions
diff --git a/src/pkg/patch/git.go b/src/pkg/patch/git.go
new file mode 100644
index 000000000..fd03f4a92
--- /dev/null
+++ b/src/pkg/patch/git.go
@@ -0,0 +1,121 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package patch
+
+import (
+ "bytes";
+ "compress/zlib";
+ "crypto/sha1";
+ "encoding/git85";
+ "fmt";
+ "io";
+ "os";
+)
+
+func gitSHA1(data []byte) []byte {
+ if len(data) == 0 {
+ // special case: 0 length is all zeros sum
+ return make([]byte, 20);
+ }
+ h := sha1.New();
+ fmt.Fprintf(h, "blob %d\x00", len(data));
+ h.Write(data);
+ return h.Sum();
+}
+
+// BUG(rsc): The GIT binary delta format is not implemented, only GIT binary literals.
+
+// GITBinaryLiteral represents a GIT binary literal diff.
+type GITBinaryLiteral struct {
+ OldSHA1 []byte; // if non-empty, the SHA1 hash of the original
+ New []byte; // the new contents
+}
+
+// Apply implements the Diff interface's Apply method.
+func (d *GITBinaryLiteral) Apply(old []byte) ([]byte, os.Error) {
+ if sum := gitSHA1(old); !bytes.HasPrefix(sum, d.OldSHA1) {
+ return nil, ErrPatchFailure;
+ }
+ return d.New, nil;
+}
+
+func unhex(c byte) uint8 {
+ switch {
+ case '0' <= c && c <= '9':
+ return c-'0';
+ case 'a' <= c && c <= 'f':
+ return c-'a'+10;
+ case 'A' <= c && c <= 'F':
+ return c-'A'+10;
+ }
+ return 255;
+}
+
+func getHex(s []byte) (data []byte, rest []byte) {
+ n := 0;
+ for n < len(s) && unhex(s[n]) != 255 {
+ n++;
+ }
+ n &^= 1; // Only take an even number of hex digits.
+ data = make([]byte, n/2);
+ for i := range data {
+ data[i] = unhex(s[2*i])<<4 | unhex(s[2*i + 1]);
+ }
+ rest = s[n:len(s)];
+ return;
+}
+
+// ParseGITBinary parses raw as a GIT binary patch.
+func ParseGITBinary(raw []byte) (Diff, os.Error) {
+ var oldSHA1, newSHA1 []byte;
+ var sawBinary bool;
+
+ for {
+ var first []byte;
+ first, raw, _ = getLine(raw, 1);
+ first = bytes.TrimSpace(first);
+ if s, ok := skip(first, "index "); ok {
+ oldSHA1, s = getHex(s);
+ if s, ok = skip(s, ".."); !ok {
+ continue;
+ }
+ newSHA1, s = getHex(s);
+ continue;
+ }
+ if _, ok := skip(first, "GIT binary patch"); ok {
+ sawBinary = true;
+ continue;
+ }
+ if n, _, ok := atoi(first, "literal ", 10); ok && sawBinary {
+ data := make([]byte, n);
+ d := git85.NewDecoder(bytes.NewBuffer(raw));
+ z, err := zlib.NewInflater(d);
+ if err != nil {
+ return nil, err;
+ }
+ defer z.Close();
+ if _, err = io.ReadFull(z, data); err != nil {
+ if err == os.EOF {
+ err = io.ErrUnexpectedEOF;
+ }
+ return nil, err;
+ }
+ var buf [1]byte;
+ m, err := z.Read(&buf);
+ if m != 0 || err != os.EOF {
+ return nil, os.NewError("GIT binary literal longer than expected");
+ }
+
+ if sum := gitSHA1(data); !bytes.HasPrefix(sum, newSHA1) {
+ return nil, os.NewError("GIT binary literal SHA1 mismatch");
+ }
+ return &GITBinaryLiteral{oldSHA1, data}, nil;
+ }
+ if !sawBinary {
+ return nil, os.NewError("unexpected GIT patch header: " + string(first));
+ }
+ }
+ panic("unreachable");
+}