diff options
Diffstat (limited to 'object/commit')
| -rw-r--r-- | object/commit/append.go | 6 | ||||
| -rw-r--r-- | object/commit/append_test.go | 6 | ||||
| -rw-r--r-- | object/commit/clone.go | 33 | ||||
| -rw-r--r-- | object/commit/commit.go | 4 | ||||
| -rw-r--r-- | object/commit/parse.go | 18 | ||||
| -rw-r--r-- | object/commit/parse_test.go | 6 | ||||
| -rw-r--r-- | object/commit/roundtrip_test.go | 18 | ||||
| -rw-r--r-- | object/commit/type.go | 2 |
8 files changed, 68 insertions, 25 deletions
diff --git a/object/commit/append.go b/object/commit/append.go index 84410b80..02d69058 100644 --- a/object/commit/append.go +++ b/object/commit/append.go @@ -33,7 +33,7 @@ func (commit *Commit) AppendWithoutHeader(dst []byte) ([]byte, error) { dst = append(dst, byte('\n')) - if commit.ChangeID != "" { + if len(commit.ChangeID) != 0 { dst = append(dst, []byte("change-id ")...) dst = append(dst, commit.ChangeID...) dst = append(dst, byte('\n')) @@ -41,7 +41,7 @@ func (commit *Commit) AppendWithoutHeader(dst []byte) ([]byte, error) { for _, h := range commit.ExtraHeaders { // GIGO on empty keys and such. - dst = append(dst, []byte(h.Key)...) + dst = append(dst, h.Key...) dst = append(dst, byte(' ')) dst = append(dst, h.Value...) dst = append(dst, byte('\n')) @@ -61,7 +61,7 @@ func (commit *Commit) AppendWithHeader(dst []byte) ([]byte, error) { return dst, err } - dst = header.Append(dst, typ.TypeCommit, uint64(len(body))) + dst = header.Append(dst, typ.Commit, len(body)) return append(dst, body...), nil } diff --git a/object/commit/append_test.go b/object/commit/append_test.go index ca32903f..7a54f19e 100644 --- a/object/commit/append_test.go +++ b/object/commit/append_test.go @@ -24,13 +24,13 @@ func TestAppendGitFsck(t *testing.T) { t.Fatalf("NewRepo: %v", err) } - blobID, err := repo.HashObject(t, typ.TypeBlob, strings.NewReader("content\n")) + blobID, err := repo.HashObject(t, typ.Blob, strings.NewReader("content\n")) if err != nil { t.Fatalf("HashObject(blob): %v", err) } treeID, err := repo.MkTree(t, []testgit.TreeEntry{ - {Mode: "100644", Type: typ.TypeBlob, OID: blobID, Name: "file.txt"}, + {Mode: "100644", Type: typ.Blob, OID: blobID, Name: "file.txt"}, }) if err != nil { t.Fatalf("MkTree: %v", err) @@ -58,7 +58,7 @@ func TestAppendGitFsck(t *testing.T) { t.Fatalf("AppendWithoutHeader: %v", err) } - commitID, err := repo.HashObject(t, typ.TypeCommit, bytes.NewReader(rawBody)) + commitID, err := repo.HashObject(t, typ.Commit, bytes.NewReader(rawBody)) if err != nil { t.Fatalf("HashObject(commit): %v", err) } diff --git a/object/commit/clone.go b/object/commit/clone.go new file mode 100644 index 00000000..08987f26 --- /dev/null +++ b/object/commit/clone.go @@ -0,0 +1,33 @@ +package commit + +import ( + "bytes" + "slices" +) + +// Clone returns a deep copy of the commit +// whose byte fields are independent of any memory the original may alias. +// +// Labels: Life-Independent. +func (commit *Commit) Clone() *Commit { + clone := &Commit{ + Tree: commit.Tree, + Parents: slices.Clone(commit.Parents), + Author: commit.Author.Clone(), + Committer: commit.Committer.Clone(), + Message: bytes.Clone(commit.Message), + ChangeID: bytes.Clone(commit.ChangeID), + } + + if commit.ExtraHeaders != nil { + clone.ExtraHeaders = make([]ExtraHeader, len(commit.ExtraHeaders)) + for i, h := range commit.ExtraHeaders { + clone.ExtraHeaders[i] = ExtraHeader{ + Key: bytes.Clone(h.Key), + Value: bytes.Clone(h.Value), + } + } + } + + return clone +} diff --git a/object/commit/commit.go b/object/commit/commit.go index 6a89bce9..a8a247bf 100644 --- a/object/commit/commit.go +++ b/object/commit/commit.go @@ -14,12 +14,12 @@ type Commit struct { Author signature.Signature Committer signature.Signature Message []byte - ChangeID string + ChangeID []byte ExtraHeaders []ExtraHeader } // ExtraHeader represents an extra header in a Git object. type ExtraHeader struct { - Key string + Key []byte Value []byte } diff --git a/object/commit/parse.go b/object/commit/parse.go index 20353e14..74f607f1 100644 --- a/object/commit/parse.go +++ b/object/commit/parse.go @@ -13,6 +13,16 @@ import ( var ErrInvalidCommit = errors.New("object/commit: invalid commit") // Parse decodes a commit object body. +// +// The returned commit aliases body: +// its Message, ChangeID, and extra-header fields, +// along with the byte fields of its signatures, +// share body's backing array. +// The commit inherits body's lifetime +// and must not be mutated unless body may be. +// Use [Commit.Clone] for an independent copy. +// +// Labels: Life-Parent, Mut-No. func Parse(body []byte, objectFormat id.ObjectFormat) (*Commit, error) { c := new(Commit) @@ -95,7 +105,7 @@ func Parse(body []byte, objectFormat id.ObjectFormat) (*Commit, error) { return nil, fmt.Errorf("%w: unexpected change-id header at offset %d", ErrInvalidCommit, lineStart) } - c.ChangeID = string(value) + c.ChangeID = value case "gpgsig", "gpgsig-sha256": if state != parseStateExtra { return nil, fmt.Errorf("%w: unexpected %s header at offset %d", ErrInvalidCommit, key, lineStart) @@ -119,8 +129,8 @@ func Parse(body []byte, objectFormat id.ObjectFormat) (*Commit, error) { } c.ExtraHeaders = append(c.ExtraHeaders, ExtraHeader{ - Key: string(key), - Value: append([]byte(nil), value...), + Key: key, + Value: value, }) } } @@ -141,7 +151,7 @@ func Parse(body []byte, objectFormat id.ObjectFormat) (*Commit, error) { panic("unreachable parse state") } - c.Message = append([]byte(nil), body[i:]...) + c.Message = body[i:] return c, nil } diff --git a/object/commit/parse_test.go b/object/commit/parse_test.go index 48202676..2264f84d 100644 --- a/object/commit/parse_test.go +++ b/object/commit/parse_test.go @@ -39,13 +39,13 @@ func TestParse(t *testing.T) { t.Fatalf("NewRepo: %v", err) } - blobID, err := repo.HashObject(t, typ.TypeBlob, strings.NewReader("content\n")) + blobID, err := repo.HashObject(t, typ.Blob, strings.NewReader("content\n")) if err != nil { t.Fatalf("HashObject: %v", err) } treeID, err := repo.MkTree(t, []testgit.TreeEntry{ - {Mode: "100644", Type: typ.TypeBlob, OID: blobID, Name: "file.txt"}, + {Mode: "100644", Type: typ.Blob, OID: blobID, Name: "file.txt"}, }) if err != nil { t.Fatalf("MkTree: %v", err) @@ -96,7 +96,7 @@ func TestParse(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { - rawBody, err := repo.CatFile(t, typ.TypeCommit, tc.oid) + rawBody, err := repo.CatFile(t, typ.Commit, tc.oid) if err != nil { t.Fatalf("CatFile: %v", err) } diff --git a/object/commit/roundtrip_test.go b/object/commit/roundtrip_test.go index 8169e75f..1cfcaaac 100644 --- a/object/commit/roundtrip_test.go +++ b/object/commit/roundtrip_test.go @@ -25,13 +25,13 @@ func TestRoundTrip(t *testing.T) { t.Fatalf("NewRepo: %v", err) } - blobID, err := repo.HashObject(t, typ.TypeBlob, strings.NewReader("roundtrip\n")) + blobID, err := repo.HashObject(t, typ.Blob, strings.NewReader("roundtrip\n")) if err != nil { t.Fatalf("HashObject(blob): %v", err) } treeID, err := repo.MkTree(t, []testgit.TreeEntry{ - {Mode: "100644", Type: typ.TypeBlob, OID: blobID, Name: "roundtrip.txt"}, + {Mode: "100644", Type: typ.Blob, OID: blobID, Name: "roundtrip.txt"}, }) if err != nil { t.Fatalf("MkTree: %v", err) @@ -87,10 +87,10 @@ func TestRoundTrip(t *testing.T) { OffsetMinutes: 330, }, Message: []byte("roundtrip subject\n\nroundtrip body\n\n"), - ChangeID: "zyxwvutsrqponmlk", + ChangeID: []byte("zyxwvutsrqponmlk"), ExtraHeaders: []commit.ExtraHeader{ - {Key: "encoding", Value: []byte("UTF-8")}, - {Key: "x-test-header", Value: []byte("value")}, + {Key: []byte("encoding"), Value: []byte("UTF-8")}, + {Key: []byte("x-test-header"), Value: []byte("value")}, }, } @@ -99,7 +99,7 @@ func TestRoundTrip(t *testing.T) { t.Fatalf("AppendWithoutHeader: %v", err) } - roundTripID, err := repo.HashObject(t, typ.TypeCommit, bytes.NewReader(rawBody)) + roundTripID, err := repo.HashObject(t, typ.Commit, bytes.NewReader(rawBody)) if err != nil { t.Fatalf("HashObject(commit): %v", err) } @@ -112,7 +112,7 @@ func TestRoundTrip(t *testing.T) { t.Fatalf("Fsck: %v", err) } - gitBody, err := repo.CatFile(t, typ.TypeCommit, roundTripID) + gitBody, err := repo.CatFile(t, typ.Commit, roundTripID) if err != nil { t.Fatalf("CatFile: %v", err) } @@ -145,7 +145,7 @@ func assertCommitEqual(t *testing.T, got *commit.Commit, want *commit.Commit) { t.Fatalf("message = %q, want %q", got.Message, want.Message) } - if got.ChangeID != want.ChangeID { + if !bytes.Equal(got.ChangeID, want.ChangeID) { t.Fatalf("change id = %q, want %q", got.ChangeID, want.ChangeID) } @@ -155,7 +155,7 @@ func assertCommitEqual(t *testing.T, got *commit.Commit, want *commit.Commit) { for i, wantHeader := range want.ExtraHeaders { gotHeader := got.ExtraHeaders[i] - if gotHeader.Key != wantHeader.Key { + if !bytes.Equal(gotHeader.Key, wantHeader.Key) { t.Fatalf("extra header[%d] key = %q, want %q", i, gotHeader.Key, wantHeader.Key) } diff --git a/object/commit/type.go b/object/commit/type.go index 80715a72..2ec67320 100644 --- a/object/commit/type.go +++ b/object/commit/type.go @@ -8,5 +8,5 @@ import ( func (commit *Commit) ObjectType() typ.Type { _ = commit - return typ.TypeCommit + return typ.Commit } |
