aboutsummaryrefslogtreecommitdiff
path: root/object/commit
diff options
context:
space:
mode:
Diffstat (limited to 'object/commit')
-rw-r--r--object/commit/append.go6
-rw-r--r--object/commit/append_test.go6
-rw-r--r--object/commit/clone.go33
-rw-r--r--object/commit/commit.go4
-rw-r--r--object/commit/parse.go18
-rw-r--r--object/commit/parse_test.go6
-rw-r--r--object/commit/roundtrip_test.go18
-rw-r--r--object/commit/type.go2
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
}