aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaulo Gomes <pjbgf@linux.com>2023-04-11 21:00:47 +0100
committerGitHub <noreply@github.com>2023-04-11 21:00:47 +0100
commitd5b1afd3cc786e56a90cec910b79815ed3b23cc7 (patch)
tree4e0b93ea480fdea37761d2e879a073ce6f5c1747
parentce62f3e9ff86270538a514a68d3bd5563a733e3b (diff)
parent8e8281008ee98e9864fa9597be3e16aaecdf9891 (diff)
downloadgo-git-d5b1afd3cc786e56a90cec910b79815ed3b23cc7.tar.gz
Merge pull request #707 from pjbgf/experimental-sha256
*: Add support for initializing SHA256 repositories
-rw-r--r--.github/workflows/git.yml3
-rw-r--r--Makefile6
-rw-r--r--_examples/sha256/main.go66
-rw-r--r--config/config.go73
-rw-r--r--options.go8
-rw-r--r--plumbing/format/commitgraph/encoder.go7
-rw-r--r--plumbing/format/config/format.go53
-rw-r--r--plumbing/format/idxfile/decoder.go3
-rw-r--r--plumbing/format/idxfile/encoder.go7
-rw-r--r--plumbing/format/idxfile/idxfile.go5
-rw-r--r--plumbing/format/index/decoder.go3
-rw-r--r--plumbing/format/index/encoder.go3
-rw-r--r--plumbing/format/packfile/encoder.go3
-rw-r--r--plumbing/format/packfile/encoder_test.go9
-rw-r--r--plumbing/format/packfile/scanner_test.go3
-rw-r--r--plumbing/hash.go14
-rw-r--r--plumbing/hash/hash.go3
-rw-r--r--plumbing/hash/hash_sha1.go15
-rw-r--r--plumbing/hash/hash_sha256.go15
-rw-r--r--plumbing/protocol/packp/ulreq_decode_test.go5
-rw-r--r--repository.go37
-rw-r--r--storage/filesystem/dotgit/dotgit.go12
-rw-r--r--storage/filesystem/dotgit/writers.go5
23 files changed, 298 insertions, 60 deletions
diff --git a/.github/workflows/git.yml b/.github/workflows/git.yml
index c945e72..3a40c0c 100644
--- a/.github/workflows/git.yml
+++ b/.github/workflows/git.yml
@@ -42,6 +42,9 @@ jobs:
- name: Test
run: make test-coverage
+ - name: Test SHA256
+ run: make test-sha256
+
- name: Build go-git with CGO disabled
run: go build ./...
env:
diff --git a/Makefile b/Makefile
index 2acb8bc..f0c1abb 100644
--- a/Makefile
+++ b/Makefile
@@ -29,6 +29,12 @@ test:
@echo "running against `git version`"; \
$(GOTEST) -race ./...
+TEMP_REPO := $(shell mktemp)
+test-sha256:
+ $(GOCMD) run -tags sha256 _examples/sha256/main.go $(TEMP_REPO)
+ cd $(TEMP_REPO) && git fsck
+ rm -rf $(TEMP_REPO)
+
test-coverage:
@echo "running against `git version`"; \
echo "" > $(COVERAGE_REPORT); \
diff --git a/_examples/sha256/main.go b/_examples/sha256/main.go
new file mode 100644
index 0000000..2a257e8
--- /dev/null
+++ b/_examples/sha256/main.go
@@ -0,0 +1,66 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "time"
+
+ "github.com/go-git/go-git/v5"
+ . "github.com/go-git/go-git/v5/_examples"
+ "github.com/go-git/go-git/v5/plumbing/format/config"
+ "github.com/go-git/go-git/v5/plumbing/object"
+)
+
+// This example requires building with the sha256 tag for it to work:
+// go run -tags sha256 main.go /tmp/repository
+
+// Basic example of how to initialise a repository using sha256 as the hashing algorithmn.
+func main() {
+ CheckArgs("<directory>")
+ directory := os.Args[1]
+
+ os.RemoveAll(directory)
+
+ // Init a new repository using the ObjectFormat SHA256.
+ r, err := git.PlainInitWithOptions(directory, &git.PlainInitOptions{ObjectFormat: config.SHA256})
+ CheckIfError(err)
+
+ w, err := r.Worktree()
+ CheckIfError(err)
+
+ // ... we need a file to commit so let's create a new file inside of the
+ // worktree of the project using the go standard library.
+ Info("echo \"hello world!\" > example-git-file")
+ filename := filepath.Join(directory, "example-git-file")
+ err = ioutil.WriteFile(filename, []byte("hello world!"), 0644)
+ CheckIfError(err)
+
+ // Adds the new file to the staging area.
+ Info("git add example-git-file")
+ _, err = w.Add("example-git-file")
+ CheckIfError(err)
+
+ // Commits the current staging area to the repository, with the new file
+ // just created. We should provide the object.Signature of Author of the
+ // commit Since version 5.0.1, we can omit the Author signature, being read
+ // from the git config files.
+ Info("git commit -m \"example go-git commit\"")
+ commit, err := w.Commit("example go-git commit", &git.CommitOptions{
+ Author: &object.Signature{
+ Name: "John Doe",
+ Email: "john@doe.org",
+ When: time.Now(),
+ },
+ })
+
+ CheckIfError(err)
+
+ // Prints the current HEAD to verify that all worked well.
+ Info("git show -s")
+ obj, err := r.CommitObject(commit)
+ CheckIfError(err)
+
+ fmt.Println(obj)
+}
diff --git a/config/config.go b/config/config.go
index 8051bc1..83629fc 100644
--- a/config/config.go
+++ b/config/config.go
@@ -59,6 +59,8 @@ type Config struct {
// CommentChar is the character indicating the start of a
// comment for commands like commit and tag
CommentChar string
+ // RepositoryFormatVersion identifies the repository format and layout version.
+ RepositoryFormatVersion format.RepositoryFormatVersion
}
User struct {
@@ -96,6 +98,17 @@ type Config struct {
DefaultBranch string
}
+ Extensions struct {
+ // ObjectFormat specifies the hash algorithm to use. The
+ // acceptable values are sha1 and sha256. If not specified,
+ // sha1 is assumed. It is an error to specify this key unless
+ // core.repositoryFormatVersion is 1.
+ //
+ // This setting must not be changed after repository initialization
+ // (e.g. clone or init).
+ ObjectFormat format.ObjectFormat
+ }
+
// Remotes list of repository remotes, the key of the map is the name
// of the remote, should equal to RemoteConfig.Name.
Remotes map[string]*RemoteConfig
@@ -226,28 +239,31 @@ func (c *Config) Validate() error {
}
const (
- remoteSection = "remote"
- submoduleSection = "submodule"
- branchSection = "branch"
- coreSection = "core"
- packSection = "pack"
- userSection = "user"
- authorSection = "author"
- committerSection = "committer"
- initSection = "init"
- urlSection = "url"
- fetchKey = "fetch"
- urlKey = "url"
- bareKey = "bare"
- worktreeKey = "worktree"
- commentCharKey = "commentChar"
- windowKey = "window"
- mergeKey = "merge"
- rebaseKey = "rebase"
- nameKey = "name"
- emailKey = "email"
- descriptionKey = "description"
- defaultBranchKey = "defaultBranch"
+ remoteSection = "remote"
+ submoduleSection = "submodule"
+ branchSection = "branch"
+ coreSection = "core"
+ packSection = "pack"
+ userSection = "user"
+ authorSection = "author"
+ committerSection = "committer"
+ initSection = "init"
+ urlSection = "url"
+ extensionsSection = "extensions"
+ fetchKey = "fetch"
+ urlKey = "url"
+ bareKey = "bare"
+ worktreeKey = "worktree"
+ commentCharKey = "commentChar"
+ windowKey = "window"
+ mergeKey = "merge"
+ rebaseKey = "rebase"
+ nameKey = "name"
+ emailKey = "email"
+ descriptionKey = "description"
+ defaultBranchKey = "defaultBranch"
+ repositoryFormatVersionKey = "repositoryformatversion"
+ objectFormat = "objectformat"
// DefaultPackWindow holds the number of previous objects used to
// generate deltas. The value 10 is the same used by git command.
@@ -391,6 +407,7 @@ func (c *Config) unmarshalInit() {
// Marshal returns Config encoded as a git-config file.
func (c *Config) Marshal() ([]byte, error) {
c.marshalCore()
+ c.marshalExtensions()
c.marshalUser()
c.marshalPack()
c.marshalRemotes()
@@ -410,12 +427,24 @@ func (c *Config) Marshal() ([]byte, error) {
func (c *Config) marshalCore() {
s := c.Raw.Section(coreSection)
s.SetOption(bareKey, fmt.Sprintf("%t", c.Core.IsBare))
+ if string(c.Core.RepositoryFormatVersion) != "" {
+ s.SetOption(repositoryFormatVersionKey, string(c.Core.RepositoryFormatVersion))
+ }
if c.Core.Worktree != "" {
s.SetOption(worktreeKey, c.Core.Worktree)
}
}
+func (c *Config) marshalExtensions() {
+ // Extensions are only supported on Version 1, therefore
+ // ignore them otherwise.
+ if c.Core.RepositoryFormatVersion == format.Version_1 {
+ s := c.Raw.Section(extensionsSection)
+ s.SetOption(objectFormat, string(c.Extensions.ObjectFormat))
+ }
+}
+
func (c *Config) marshalUser() {
s := c.Raw.Section(userSection)
if c.User.Name != "" {
diff --git a/options.go b/options.go
index 747d512..738c1ce 100644
--- a/options.go
+++ b/options.go
@@ -10,6 +10,7 @@ import (
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
+ formatcfg "github.com/go-git/go-git/v5/plumbing/format/config"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband"
"github.com/go-git/go-git/v5/plumbing/transport"
@@ -672,3 +673,10 @@ type PlainOpenOptions struct {
// Validate validates the fields and sets the default values.
func (o *PlainOpenOptions) Validate() error { return nil }
+
+type PlainInitOptions struct {
+ ObjectFormat formatcfg.ObjectFormat
+}
+
+// Validate validates the fields and sets the default values.
+func (o *PlainInitOptions) Validate() error { return nil }
diff --git a/plumbing/format/commitgraph/encoder.go b/plumbing/format/commitgraph/encoder.go
index bcf7d03..f61025b 100644
--- a/plumbing/format/commitgraph/encoder.go
+++ b/plumbing/format/commitgraph/encoder.go
@@ -1,7 +1,6 @@
package commitgraph
import (
- "crypto"
"io"
"github.com/go-git/go-git/v5/plumbing"
@@ -17,7 +16,7 @@ type Encoder struct {
// NewEncoder returns a new stream encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
- h := hash.New(crypto.SHA1)
+ h := hash.New(hash.CryptoType)
mw := io.MultiWriter(w, h)
return &Encoder{mw, h}
}
@@ -31,7 +30,7 @@ func (e *Encoder) Encode(idx Index) error {
hashToIndex, fanout, extraEdgesCount := e.prepare(idx, hashes)
chunkSignatures := [][]byte{oidFanoutSignature, oidLookupSignature, commitDataSignature}
- chunkSizes := []uint64{4 * 256, uint64(len(hashes)) * 20, uint64(len(hashes)) * 36}
+ chunkSizes := []uint64{4 * 256, uint64(len(hashes)) * hash.Size, uint64(len(hashes)) * 36}
if extraEdgesCount > 0 {
chunkSignatures = append(chunkSignatures, extraEdgeListSignature)
chunkSizes = append(chunkSizes, uint64(extraEdgesCount)*4)
@@ -183,6 +182,6 @@ func (e *Encoder) encodeExtraEdges(extraEdges []uint32) (err error) {
}
func (e *Encoder) encodeChecksum() error {
- _, err := e.Write(e.hash.Sum(nil)[:20])
+ _, err := e.Write(e.hash.Sum(nil)[:hash.Size])
return err
}
diff --git a/plumbing/format/config/format.go b/plumbing/format/config/format.go
new file mode 100644
index 0000000..4873ea9
--- /dev/null
+++ b/plumbing/format/config/format.go
@@ -0,0 +1,53 @@
+package config
+
+// RepositoryFormatVersion represents the repository format version,
+// as per defined at:
+//
+// https://git-scm.com/docs/repository-version
+type RepositoryFormatVersion string
+
+const (
+ // Version_0 is the format defined by the initial version of git,
+ // including but not limited to the format of the repository
+ // directory, the repository configuration file, and the object
+ // and ref storage.
+ //
+ // Specifying the complete behavior of git is beyond the scope
+ // of this document.
+ Version_0 = "0"
+
+ // Version_1 is identical to version 0, with the following exceptions:
+ //
+ // 1. When reading the core.repositoryformatversion variable, a git
+ // implementation which supports version 1 MUST also read any
+ // configuration keys found in the extensions section of the
+ // configuration file.
+ //
+ // 2. If a version-1 repository specifies any extensions.* keys that
+ // the running git has not implemented, the operation MUST NOT proceed.
+ // Similarly, if the value of any known key is not understood by the
+ // implementation, the operation MUST NOT proceed.
+ //
+ // Note that if no extensions are specified in the config file, then
+ // core.repositoryformatversion SHOULD be set to 0 (setting it to 1 provides
+ // no benefit, and makes the repository incompatible with older
+ // implementations of git).
+ Version_1 = "1"
+
+ // DefaultRepositoryFormatVersion holds the default repository format version.
+ DefaultRepositoryFormatVersion = Version_0
+)
+
+// ObjectFormat defines the object format.
+type ObjectFormat string
+
+const (
+ // SHA1 represents the object format used for SHA1.
+ SHA1 ObjectFormat = "sha1"
+
+ // SHA256 represents the object format used for SHA256.
+ SHA256 ObjectFormat = "sha256"
+
+ // DefaultObjectFormat holds the default object format.
+ DefaultObjectFormat = SHA1
+)
diff --git a/plumbing/format/idxfile/decoder.go b/plumbing/format/idxfile/decoder.go
index 51a3904..9afdce3 100644
--- a/plumbing/format/idxfile/decoder.go
+++ b/plumbing/format/idxfile/decoder.go
@@ -6,6 +6,7 @@ import (
"errors"
"io"
+ "github.com/go-git/go-git/v5/plumbing/hash"
"github.com/go-git/go-git/v5/utils/binary"
)
@@ -19,7 +20,7 @@ var (
const (
fanout = 256
- objectIDLength = 20
+ objectIDLength = hash.Size
)
// Decoder reads and decodes idx files from an input stream.
diff --git a/plumbing/format/idxfile/encoder.go b/plumbing/format/idxfile/encoder.go
index 6ac445f..7514737 100644
--- a/plumbing/format/idxfile/encoder.go
+++ b/plumbing/format/idxfile/encoder.go
@@ -1,7 +1,6 @@
package idxfile
import (
- "crypto"
"io"
"github.com/go-git/go-git/v5/plumbing/hash"
@@ -16,7 +15,7 @@ type Encoder struct {
// NewEncoder returns a new stream encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
- h := hash.New(crypto.SHA1)
+ h := hash.New(hash.CryptoType)
mw := io.MultiWriter(w, h)
return &Encoder{mw, h}
}
@@ -133,10 +132,10 @@ func (e *Encoder) encodeChecksums(idx *MemoryIndex) (int, error) {
return 0, err
}
- copy(idx.IdxChecksum[:], e.hash.Sum(nil)[:20])
+ copy(idx.IdxChecksum[:], e.hash.Sum(nil)[:hash.Size])
if _, err := e.Write(idx.IdxChecksum[:]); err != nil {
return 0, err
}
- return 40, nil
+ return hash.HexSize, nil
}
diff --git a/plumbing/format/idxfile/idxfile.go b/plumbing/format/idxfile/idxfile.go
index 64dd8dc..9237a74 100644
--- a/plumbing/format/idxfile/idxfile.go
+++ b/plumbing/format/idxfile/idxfile.go
@@ -8,6 +8,7 @@ import (
encbin "encoding/binary"
"github.com/go-git/go-git/v5/plumbing"
+ "github.com/go-git/go-git/v5/plumbing/hash"
)
const (
@@ -53,8 +54,8 @@ type MemoryIndex struct {
Offset32 [][]byte
CRC32 [][]byte
Offset64 []byte
- PackfileChecksum [20]byte
- IdxChecksum [20]byte
+ PackfileChecksum [hash.Size]byte
+ IdxChecksum [hash.Size]byte
offsetHash map[int64]plumbing.Hash
offsetHashIsFull bool
diff --git a/plumbing/format/index/decoder.go b/plumbing/format/index/decoder.go
index c4da20c..8834e25 100644
--- a/plumbing/format/index/decoder.go
+++ b/plumbing/format/index/decoder.go
@@ -3,7 +3,6 @@ package index
import (
"bufio"
"bytes"
- "crypto"
"errors"
"io"
"io/ioutil"
@@ -49,7 +48,7 @@ type Decoder struct {
// NewDecoder returns a new decoder that reads from r.
func NewDecoder(r io.Reader) *Decoder {
- h := hash.New(crypto.SHA1)
+ h := hash.New(hash.CryptoType)
return &Decoder{
r: io.TeeReader(r, h),
hash: h,
diff --git a/plumbing/format/index/encoder.go b/plumbing/format/index/encoder.go
index a915378..fa2d814 100644
--- a/plumbing/format/index/encoder.go
+++ b/plumbing/format/index/encoder.go
@@ -2,7 +2,6 @@ package index
import (
"bytes"
- "crypto"
"errors"
"io"
"sort"
@@ -29,7 +28,7 @@ type Encoder struct {
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
- h := hash.New(crypto.SHA1)
+ h := hash.New(hash.CryptoType)
mw := io.MultiWriter(w, h)
return &Encoder{mw, h}
}
diff --git a/plumbing/format/packfile/encoder.go b/plumbing/format/packfile/encoder.go
index a8a7e96..c9d19b3 100644
--- a/plumbing/format/packfile/encoder.go
+++ b/plumbing/format/packfile/encoder.go
@@ -2,7 +2,6 @@ package packfile
import (
"compress/zlib"
- "crypto"
"fmt"
"io"
@@ -29,7 +28,7 @@ type Encoder struct {
// OFSDeltaObject. To use Reference deltas, set useRefDeltas to true.
func NewEncoder(w io.Writer, s storer.EncodedObjectStorer, useRefDeltas bool) *Encoder {
h := plumbing.Hasher{
- Hash: hash.New(crypto.SHA1),
+ Hash: hash.New(hash.CryptoType),
}
mw := io.MultiWriter(w, h)
ow := newOffsetWriter(mw)
diff --git a/plumbing/format/packfile/encoder_test.go b/plumbing/format/packfile/encoder_test.go
index c9d49c3..902d8cc 100644
--- a/plumbing/format/packfile/encoder_test.go
+++ b/plumbing/format/packfile/encoder_test.go
@@ -7,6 +7,7 @@ import (
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/idxfile"
+ "github.com/go-git/go-git/v5/plumbing/hash"
"github.com/go-git/go-git/v5/storage/memory"
"github.com/go-git/go-billy/v5/memfs"
@@ -30,10 +31,10 @@ func (s *EncoderSuite) SetUpTest(c *C) {
}
func (s *EncoderSuite) TestCorrectPackHeader(c *C) {
- hash, err := s.enc.Encode([]plumbing.Hash{}, 10)
+ h, err := s.enc.Encode([]plumbing.Hash{}, 10)
c.Assert(err, IsNil)
- hb := [20]byte(hash)
+ hb := [hash.Size]byte(h)
// PACK + VERSION + OBJECTS + HASH
expectedResult := []byte{'P', 'A', 'C', 'K', 0, 0, 0, 2, 0, 0, 0, 0}
@@ -51,7 +52,7 @@ func (s *EncoderSuite) TestCorrectPackWithOneEmptyObject(c *C) {
_, err := s.store.SetEncodedObject(o)
c.Assert(err, IsNil)
- hash, err := s.enc.Encode([]plumbing.Hash{o.Hash()}, 10)
+ h, err := s.enc.Encode([]plumbing.Hash{o.Hash()}, 10)
c.Assert(err, IsNil)
// PACK + VERSION(2) + OBJECT NUMBER(1)
@@ -64,7 +65,7 @@ func (s *EncoderSuite) TestCorrectPackWithOneEmptyObject(c *C) {
[]byte{120, 156, 1, 0, 0, 255, 255, 0, 0, 0, 1}...)
// + HASH
- hb := [20]byte(hash)
+ hb := [hash.Size]byte(h)
expectedResult = append(expectedResult, hb[:]...)
result := s.buf.Bytes()
diff --git a/plumbing/format/packfile/scanner_test.go b/plumbing/format/packfile/scanner_test.go
index 892a27c..9dcc359 100644
--- a/plumbing/format/packfile/scanner_test.go
+++ b/plumbing/format/packfile/scanner_test.go
@@ -6,6 +6,7 @@ import (
fixtures "github.com/go-git/go-git-fixtures/v4"
"github.com/go-git/go-git/v5/plumbing"
+ "github.com/go-git/go-git/v5/plumbing/hash"
. "gopkg.in/check.v1"
)
@@ -71,7 +72,7 @@ func (s *ScannerSuite) testNextObjectHeader(c *C, tag string,
n, err := p.Checksum()
c.Assert(err, IsNil)
- c.Assert(n, HasLen, 20)
+ c.Assert(n, HasLen, hash.Size)
}
func (s *ScannerSuite) TestNextObjectHeaderWithOutReadObject(c *C) {
diff --git a/plumbing/hash.go b/plumbing/hash.go
index 2fab759..39bb73f 100644
--- a/plumbing/hash.go
+++ b/plumbing/hash.go
@@ -2,7 +2,6 @@ package plumbing
import (
"bytes"
- "crypto"
"encoding/hex"
"sort"
"strconv"
@@ -11,7 +10,7 @@ import (
)
// Hash SHA1 hashed content
-type Hash [20]byte
+type Hash [hash.Size]byte
// ZeroHash is Hash with value zero
var ZeroHash Hash
@@ -47,7 +46,7 @@ type Hasher struct {
}
func NewHasher(t ObjectType, size int64) Hasher {
- h := Hasher{hash.New(crypto.SHA1)}
+ h := Hasher{hash.New(hash.CryptoType)}
h.Write(t.Bytes())
h.Write([]byte(" "))
h.Write([]byte(strconv.FormatInt(size, 10)))
@@ -75,10 +74,11 @@ func (p HashSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// IsHash returns true if the given string is a valid hash.
func IsHash(s string) bool {
- if len(s) != 40 {
+ switch len(s) {
+ case hash.HexSize:
+ _, err := hex.DecodeString(s)
+ return err == nil
+ default:
return false
}
-
- _, err := hex.DecodeString(s)
- return err == nil
}
diff --git a/plumbing/hash/hash.go b/plumbing/hash/hash.go
index 80e4b5f..82d1856 100644
--- a/plumbing/hash/hash.go
+++ b/plumbing/hash/hash.go
@@ -21,6 +21,7 @@ func init() {
// that registers new algorithms to avoid side effects.
func reset() {
algos[crypto.SHA1] = sha1cd.New
+ algos[crypto.SHA256] = crypto.SHA256.New
}
// RegisterHash allows for the hash algorithm used to be overriden.
@@ -34,6 +35,8 @@ func RegisterHash(h crypto.Hash, f func() hash.Hash) error {
switch h {
case crypto.SHA1:
algos[h] = f
+ case crypto.SHA256:
+ algos[h] = f
default:
return fmt.Errorf("unsupported hash function: %v", h)
}
diff --git a/plumbing/hash/hash_sha1.go b/plumbing/hash/hash_sha1.go
new file mode 100644
index 0000000..e3cb60f
--- /dev/null
+++ b/plumbing/hash/hash_sha1.go
@@ -0,0 +1,15 @@
+//go:build !sha256
+// +build !sha256
+
+package hash
+
+import "crypto"
+
+const (
+ // CryptoType defines what hash algorithm is being used.
+ CryptoType = crypto.SHA1
+ // Size defines the amount of bytes the hash yields.
+ Size = 20
+ // HexSize defines the strings size of the hash when represented in hexadecimal.
+ HexSize = 40
+)
diff --git a/plumbing/hash/hash_sha256.go b/plumbing/hash/hash_sha256.go
new file mode 100644
index 0000000..1c52b89
--- /dev/null
+++ b/plumbing/hash/hash_sha256.go
@@ -0,0 +1,15 @@
+//go:build sha256
+// +build sha256
+
+package hash
+
+import "crypto"
+
+const (
+ // CryptoType defines what hash algorithm is being used.
+ CryptoType = crypto.SHA256
+ // Size defines the amount of bytes the hash yields.
+ Size = 32
+ // HexSize defines the strings size of the hash when represented in hexadecimal.
+ HexSize = 64
+)
diff --git a/plumbing/protocol/packp/ulreq_decode_test.go b/plumbing/protocol/packp/ulreq_decode_test.go
index 9628f0f..efcc7b4 100644
--- a/plumbing/protocol/packp/ulreq_decode_test.go
+++ b/plumbing/protocol/packp/ulreq_decode_test.go
@@ -8,6 +8,7 @@ import (
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/pktline"
+ "github.com/go-git/go-git/v5/plumbing/hash"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
. "gopkg.in/check.v1"
@@ -119,8 +120,8 @@ type byHash []plumbing.Hash
func (a byHash) Len() int { return len(a) }
func (a byHash) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byHash) Less(i, j int) bool {
- ii := [20]byte(a[i])
- jj := [20]byte(a[j])
+ ii := [hash.Size]byte(a[i])
+ jj := [hash.Size]byte(a[j])
return bytes.Compare(ii[:], jj[:]) < 0
}
diff --git a/repository.go b/repository.go
index 2a06f8b..56ae976 100644
--- a/repository.go
+++ b/repository.go
@@ -3,6 +3,7 @@ package git
import (
"bytes"
"context"
+ "crypto"
"encoding/hex"
"errors"
"fmt"
@@ -21,7 +22,9 @@ import (
"github.com/go-git/go-git/v5/internal/revision"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/cache"
+ formatcfg "github.com/go-git/go-git/v5/plumbing/format/config"
"github.com/go-git/go-git/v5/plumbing/format/packfile"
+ "github.com/go-git/go-git/v5/plumbing/hash"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/storage"
@@ -57,6 +60,7 @@ var (
ErrIsBareRepository = errors.New("worktree not available in a bare repository")
ErrUnableToResolveCommit = errors.New("unable to resolve commit")
ErrPackedObjectsNotSupported = errors.New("packed objects not supported")
+ ErrSHA256NotSupported = errors.New("go-git was not compiled with SHA256 support")
)
// Repository represents a git repository
@@ -228,6 +232,39 @@ func PlainInit(path string, isBare bool) (*Repository, error) {
return Init(s, wt)
}
+func PlainInitWithOptions(path string, opts *PlainInitOptions) (*Repository, error) {
+ wt := osfs.New(path)
+ dot, _ := wt.Chroot(GitDirName)
+
+ s := filesystem.NewStorage(dot, cache.NewObjectLRUDefault())
+
+ r, err := Init(s, wt)
+ if err != nil {
+ return nil, err
+ }
+
+ cfg, err := r.Config()
+ if err != nil {
+ return nil, err
+ }
+
+ if opts != nil {
+ if opts.ObjectFormat == formatcfg.SHA256 && hash.CryptoType != crypto.SHA256 {
+ return nil, ErrSHA256NotSupported
+ }
+
+ cfg.Core.RepositoryFormatVersion = formatcfg.Version_1
+ cfg.Extensions.ObjectFormat = opts.ObjectFormat
+ }
+
+ err = r.Storer.SetConfig(cfg)
+ if err != nil {
+ return nil, err
+ }
+
+ return r, err
+}
+
// PlainOpen opens a git repository from the given path. It detects if the
// repository is bare or a normal one. If the path doesn't contain a valid
// repository ErrRepositoryNotExists is returned
diff --git a/storage/filesystem/dotgit/dotgit.go b/storage/filesystem/dotgit/dotgit.go
index 2be2bae..6868324 100644
--- a/storage/filesystem/dotgit/dotgit.go
+++ b/storage/filesystem/dotgit/dotgit.go
@@ -16,6 +16,7 @@ import (
"github.com/go-git/go-billy/v5/osfs"
"github.com/go-git/go-git/v5/plumbing"
+ "github.com/go-git/go-git/v5/plumbing/hash"
"github.com/go-git/go-git/v5/storage"
"github.com/go-git/go-git/v5/utils/ioutil"
@@ -552,8 +553,8 @@ func (d *DotGit) hasPack(h plumbing.Hash) error {
}
func (d *DotGit) objectPath(h plumbing.Hash) string {
- hash := h.String()
- return d.fs.Join(objectsPath, hash[0:2], hash[2:40])
+ hex := h.String()
+ return d.fs.Join(objectsPath, hex[0:2], hex[2:hash.HexSize])
}
// incomingObjectPath is intended to add support for a git pre-receive hook
@@ -563,15 +564,16 @@ func (d *DotGit) objectPath(h plumbing.Hash) string {
//
// More on git hooks found here : https://git-scm.com/docs/githooks
// More on 'quarantine'/incoming directory here:
-// https://git-scm.com/docs/git-receive-pack
+//
+// https://git-scm.com/docs/git-receive-pack
func (d *DotGit) incomingObjectPath(h plumbing.Hash) string {
hString := h.String()
if d.incomingDirName == "" {
- return d.fs.Join(objectsPath, hString[0:2], hString[2:40])
+ return d.fs.Join(objectsPath, hString[0:2], hString[2:hash.HexSize])
}
- return d.fs.Join(objectsPath, d.incomingDirName, hString[0:2], hString[2:40])
+ return d.fs.Join(objectsPath, d.incomingDirName, hString[0:2], hString[2:hash.HexSize])
}
// hasIncomingObjects searches for an incoming directory and keeps its name
diff --git a/storage/filesystem/dotgit/writers.go b/storage/filesystem/dotgit/writers.go
index e2ede93..849b7a1 100644
--- a/storage/filesystem/dotgit/writers.go
+++ b/storage/filesystem/dotgit/writers.go
@@ -9,6 +9,7 @@ import (
"github.com/go-git/go-git/v5/plumbing/format/idxfile"
"github.com/go-git/go-git/v5/plumbing/format/objfile"
"github.com/go-git/go-git/v5/plumbing/format/packfile"
+ "github.com/go-git/go-git/v5/plumbing/hash"
"github.com/go-git/go-billy/v5"
)
@@ -277,8 +278,8 @@ func (w *ObjectWriter) Close() error {
}
func (w *ObjectWriter) save() error {
- hash := w.Hash().String()
- file := w.fs.Join(objectsPath, hash[0:2], hash[2:40])
+ hex := w.Hash().String()
+ file := w.fs.Join(objectsPath, hex[0:2], hex[2:hash.HexSize])
return w.fs.Rename(w.f.Name(), file)
}