aboutsummaryrefslogtreecommitdiffstats
path: root/formats/index
diff options
context:
space:
mode:
Diffstat (limited to 'formats/index')
-rw-r--r--formats/index/decoder.go446
-rw-r--r--formats/index/decoder_test.go196
-rw-r--r--formats/index/doc.go302
-rw-r--r--formats/index/encoder.go141
-rw-r--r--formats/index/encoder_test.go78
-rw-r--r--formats/index/index.go108
6 files changed, 0 insertions, 1271 deletions
diff --git a/formats/index/decoder.go b/formats/index/decoder.go
deleted file mode 100644
index f3d4343..0000000
--- a/formats/index/decoder.go
+++ /dev/null
@@ -1,446 +0,0 @@
-package index
-
-import (
- "bytes"
- "crypto/sha1"
- "errors"
- "hash"
- "io"
- "io/ioutil"
- "strconv"
- "time"
-
- "gopkg.in/src-d/go-git.v4/core"
- "gopkg.in/src-d/go-git.v4/utils/binary"
-)
-
-var (
- // DecodeVersionSupported is the range of supported index versions
- DecodeVersionSupported = struct{ Min, Max uint32 }{Min: 2, Max: 4}
-
- // ErrMalformedSignature is returned by Decode when the index header file is
- // malformed
- ErrMalformedSignature = errors.New("malformed index signature file")
- // ErrInvalidChecksum is returned by Decode if the SHA1 hash missmatch with
- // the read content
- ErrInvalidChecksum = errors.New("invalid checksum")
-
- errUnknownExtension = errors.New("unknown extension")
-)
-
-const (
- entryHeaderLength = 62
- entryExtended = 0x4000
- entryValid = 0x8000
- nameMask = 0xfff
- intentToAddMask = 1 << 13
- skipWorkTreeMask = 1 << 14
-)
-
-// A Decoder reads and decodes idx files from an input stream.
-type Decoder struct {
- r io.Reader
- hash hash.Hash
- lastEntry *Entry
-}
-
-// NewDecoder returns a new decoder that reads from r.
-func NewDecoder(r io.Reader) *Decoder {
- h := sha1.New()
- return &Decoder{
- r: io.TeeReader(r, h),
- hash: h,
- }
-}
-
-// Decode reads the whole index object from its input and stores it in the
-// value pointed to by idx.
-func (d *Decoder) Decode(idx *Index) error {
- var err error
- idx.Version, err = validateHeader(d.r)
- if err != nil {
- return err
- }
-
- entryCount, err := binary.ReadUint32(d.r)
- if err != nil {
- return err
- }
-
- if err := d.readEntries(idx, int(entryCount)); err != nil {
- return err
- }
-
- return d.readExtensions(idx)
-}
-
-func (d *Decoder) readEntries(idx *Index, count int) error {
- for i := 0; i < count; i++ {
- e, err := d.readEntry(idx)
- if err != nil {
- return err
- }
-
- d.lastEntry = e
- idx.Entries = append(idx.Entries, *e)
- }
-
- return nil
-}
-
-func (d *Decoder) readEntry(idx *Index) (*Entry, error) {
- e := &Entry{}
-
- var msec, mnsec, sec, nsec uint32
- var flags uint16
-
- flow := []interface{}{
- &sec, &nsec,
- &msec, &mnsec,
- &e.Dev,
- &e.Inode,
- &e.Mode,
- &e.UID,
- &e.GID,
- &e.Size,
- &e.Hash,
- &flags,
- }
-
- if err := binary.Read(d.r, flow...); err != nil {
- return nil, err
- }
-
- read := entryHeaderLength
- e.CreatedAt = time.Unix(int64(sec), int64(nsec))
- e.ModifiedAt = time.Unix(int64(msec), int64(mnsec))
- e.Stage = Stage(flags>>12) & 0x3
-
- if flags&entryExtended != 0 {
- extended, err := binary.ReadUint16(d.r)
- if err != nil {
- return nil, err
- }
-
- read += 2
- e.IntentToAdd = extended&intentToAddMask != 0
- e.SkipWorktree = extended&skipWorkTreeMask != 0
- }
-
- if err := d.readEntryName(idx, e, flags); err != nil {
- return nil, err
- }
-
- return e, d.padEntry(idx, e, read)
-}
-
-func (d *Decoder) readEntryName(idx *Index, e *Entry, flags uint16) error {
- var name string
- var err error
-
- switch idx.Version {
- case 2, 3:
- len := flags & nameMask
- name, err = d.doReadEntryName(len)
- case 4:
- name, err = d.doReadEntryNameV4()
- default:
- return ErrUnsupportedVersion
- }
-
- if err != nil {
- return err
- }
-
- e.Name = name
- return nil
-}
-
-func (d *Decoder) doReadEntryNameV4() (string, error) {
- l, err := binary.ReadVariableWidthInt(d.r)
- if err != nil {
- return "", err
- }
-
- var base string
- if d.lastEntry != nil {
- base = d.lastEntry.Name[:len(d.lastEntry.Name)-int(l)]
- }
-
- name, err := binary.ReadUntil(d.r, '\x00')
- if err != nil {
- return "", err
- }
-
- return base + string(name), nil
-}
-
-func (d *Decoder) doReadEntryName(len uint16) (string, error) {
- name := make([]byte, len)
- if err := binary.Read(d.r, &name); err != nil {
- return "", err
- }
-
- return string(name), nil
-}
-
-// Index entries are padded out to the next 8 byte alignment
-// for historical reasons related to how C Git read the files.
-func (d *Decoder) padEntry(idx *Index, e *Entry, read int) error {
- if idx.Version == 4 {
- return nil
- }
-
- entrySize := read + len(e.Name)
- padLen := 8 - entrySize%8
- if _, err := io.CopyN(ioutil.Discard, d.r, int64(padLen)); err != nil {
- return err
- }
-
- return nil
-}
-
-// TODO: support 'Split index' and 'Untracked cache' extensions, take in count
-// that they are not supported by jgit or libgit
-func (d *Decoder) readExtensions(idx *Index) error {
- var expected []byte
- var err error
-
- var header [4]byte
- for {
- expected = d.hash.Sum(nil)
-
- var n int
- if n, err = io.ReadFull(d.r, header[:]); err != nil {
- if n == 0 {
- err = io.EOF
- }
-
- break
- }
-
- err = d.readExtension(idx, header[:])
- if err != nil {
- break
- }
- }
-
- if err != errUnknownExtension {
- return err
- }
-
- return d.readChecksum(expected, header)
-}
-
-func (d *Decoder) readExtension(idx *Index, header []byte) error {
- switch {
- case bytes.Equal(header, treeExtSignature):
- r, err := d.getExtensionReader()
- if err != nil {
- return err
- }
-
- idx.Cache = &Tree{}
- d := &treeExtensionDecoder{r}
- if err := d.Decode(idx.Cache); err != nil {
- return err
- }
- case bytes.Equal(header, resolveUndoExtSignature):
- r, err := d.getExtensionReader()
- if err != nil {
- return err
- }
-
- idx.ResolveUndo = &ResolveUndo{}
- d := &resolveUndoDecoder{r}
- if err := d.Decode(idx.ResolveUndo); err != nil {
- return err
- }
- default:
- return errUnknownExtension
- }
-
- return nil
-}
-
-func (d *Decoder) getExtensionReader() (io.Reader, error) {
- len, err := binary.ReadUint32(d.r)
- if err != nil {
- return nil, err
- }
-
- return &io.LimitedReader{R: d.r, N: int64(len)}, nil
-}
-
-func (d *Decoder) readChecksum(expected []byte, alreadyRead [4]byte) error {
- var h core.Hash
- copy(h[:4], alreadyRead[:])
-
- if err := binary.Read(d.r, h[4:]); err != nil {
- return err
- }
-
- if bytes.Compare(h[:], expected) != 0 {
- return ErrInvalidChecksum
- }
-
- return nil
-}
-
-func validateHeader(r io.Reader) (version uint32, err error) {
- var s = make([]byte, 4)
- if _, err := io.ReadFull(r, s); err != nil {
- return 0, err
- }
-
- if !bytes.Equal(s, indexSignature) {
- return 0, ErrMalformedSignature
- }
-
- version, err = binary.ReadUint32(r)
- if err != nil {
- return 0, err
- }
-
- if version < DecodeVersionSupported.Min || version > DecodeVersionSupported.Max {
- return 0, ErrUnsupportedVersion
- }
-
- return
-}
-
-type treeExtensionDecoder struct {
- r io.Reader
-}
-
-func (d *treeExtensionDecoder) Decode(t *Tree) error {
- for {
- e, err := d.readEntry()
- if err != nil {
- if err == io.EOF {
- return nil
- }
-
- return err
- }
-
- if e == nil {
- continue
- }
-
- t.Entries = append(t.Entries, *e)
- }
-}
-
-func (d *treeExtensionDecoder) readEntry() (*TreeEntry, error) {
- e := &TreeEntry{}
-
- path, err := binary.ReadUntil(d.r, '\x00')
- if err != nil {
- return nil, err
- }
-
- e.Path = string(path)
-
- count, err := binary.ReadUntil(d.r, ' ')
- if err != nil {
- return nil, err
- }
-
- i, err := strconv.Atoi(string(count))
- if err != nil {
- return nil, err
- }
-
- // An entry can be in an invalidated state and is represented by having a
- // negative number in the entry_count field.
- if i == -1 {
- return nil, nil
- }
-
- e.Entries = i
- trees, err := binary.ReadUntil(d.r, '\n')
- if err != nil {
- return nil, err
- }
-
- i, err = strconv.Atoi(string(trees))
- if err != nil {
- return nil, err
- }
-
- e.Trees = i
-
- if err := binary.Read(d.r, &e.Hash); err != nil {
- return nil, err
- }
-
- return e, nil
-}
-
-type resolveUndoDecoder struct {
- r io.Reader
-}
-
-func (d *resolveUndoDecoder) Decode(ru *ResolveUndo) error {
- for {
- e, err := d.readEntry()
- if err != nil {
- if err == io.EOF {
- return nil
- }
-
- return err
- }
-
- ru.Entries = append(ru.Entries, *e)
- }
-}
-
-func (d *resolveUndoDecoder) readEntry() (*ResolveUndoEntry, error) {
- e := &ResolveUndoEntry{
- Stages: make(map[Stage]core.Hash, 0),
- }
-
- path, err := binary.ReadUntil(d.r, '\x00')
- if err != nil {
- return nil, err
- }
-
- e.Path = string(path)
-
- for i := 0; i < 3; i++ {
- if err := d.readStage(e, Stage(i+1)); err != nil {
- return nil, err
- }
- }
-
- for s := range e.Stages {
- var hash core.Hash
- if err := binary.Read(d.r, hash[:]); err != nil {
- return nil, err
- }
-
- e.Stages[s] = hash
- }
-
- return e, nil
-}
-
-func (d *resolveUndoDecoder) readStage(e *ResolveUndoEntry, s Stage) error {
- ascii, err := binary.ReadUntil(d.r, '\x00')
- if err != nil {
- return err
- }
-
- stage, err := strconv.ParseInt(string(ascii), 8, 64)
- if err != nil {
- return err
- }
-
- if stage != 0 {
- e.Stages[s] = core.ZeroHash
- }
-
- return nil
-}
diff --git a/formats/index/decoder_test.go b/formats/index/decoder_test.go
deleted file mode 100644
index a05417d..0000000
--- a/formats/index/decoder_test.go
+++ /dev/null
@@ -1,196 +0,0 @@
-package index
-
-import (
- "testing"
-
- . "gopkg.in/check.v1"
- "gopkg.in/src-d/go-git.v4/core"
- "gopkg.in/src-d/go-git.v4/fixtures"
-)
-
-func Test(t *testing.T) { TestingT(t) }
-
-type IdxfileSuite struct {
- fixtures.Suite
-}
-
-var _ = Suite(&IdxfileSuite{})
-
-func (s *IdxfileSuite) TestDecode(c *C) {
- f, err := fixtures.Basic().One().DotGit().Open("index")
- c.Assert(err, IsNil)
-
- idx := &Index{}
- d := NewDecoder(f)
- err = d.Decode(idx)
- c.Assert(err, IsNil)
-
- c.Assert(idx.Version, Equals, uint32(2))
- c.Assert(idx.Entries, HasLen, 9)
-}
-
-func (s *IdxfileSuite) TestDecodeEntries(c *C) {
- f, err := fixtures.Basic().One().DotGit().Open("index")
- c.Assert(err, IsNil)
-
- idx := &Index{}
- d := NewDecoder(f)
- err = d.Decode(idx)
- c.Assert(err, IsNil)
-
- c.Assert(idx.Entries, HasLen, 9)
-
- e := idx.Entries[0]
- c.Assert(e.CreatedAt.Unix(), Equals, int64(1473350251))
- c.Assert(e.CreatedAt.Nanosecond(), Equals, 12059307)
- c.Assert(e.ModifiedAt.Unix(), Equals, int64(1473350251))
- c.Assert(e.ModifiedAt.Nanosecond(), Equals, 12059307)
- c.Assert(e.Dev, Equals, uint32(38))
- c.Assert(e.Inode, Equals, uint32(1715795))
- c.Assert(e.UID, Equals, uint32(1000))
- c.Assert(e.GID, Equals, uint32(100))
- c.Assert(e.Size, Equals, uint32(189))
- c.Assert(e.Hash.String(), Equals, "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88")
- c.Assert(e.Name, Equals, ".gitignore")
- c.Assert(e.Mode.String(), Equals, "-rw-r--r--")
-
- e = idx.Entries[1]
- c.Assert(e.Name, Equals, "CHANGELOG")
-}
-
-func (s *IdxfileSuite) TestDecodeCacheTree(c *C) {
- f, err := fixtures.Basic().One().DotGit().Open("index")
- c.Assert(err, IsNil)
-
- idx := &Index{}
- d := NewDecoder(f)
- err = d.Decode(idx)
- c.Assert(err, IsNil)
-
- c.Assert(idx.Entries, HasLen, 9)
- c.Assert(idx.Cache.Entries, HasLen, 5)
-
- for i, expected := range expectedEntries {
- c.Assert(idx.Cache.Entries[i].Path, Equals, expected.Path)
- c.Assert(idx.Cache.Entries[i].Entries, Equals, expected.Entries)
- c.Assert(idx.Cache.Entries[i].Trees, Equals, expected.Trees)
- c.Assert(idx.Cache.Entries[i].Hash.String(), Equals, expected.Hash.String())
- }
-
-}
-
-var expectedEntries = []TreeEntry{
- {Path: "", Entries: 9, Trees: 4, Hash: core.NewHash("a8d315b2b1c615d43042c3a62402b8a54288cf5c")},
- {Path: "go", Entries: 1, Trees: 0, Hash: core.NewHash("a39771a7651f97faf5c72e08224d857fc35133db")},
- {Path: "php", Entries: 1, Trees: 0, Hash: core.NewHash("586af567d0bb5e771e49bdd9434f5e0fb76d25fa")},
- {Path: "json", Entries: 2, Trees: 0, Hash: core.NewHash("5a877e6a906a2743ad6e45d99c1793642aaf8eda")},
- {Path: "vendor", Entries: 1, Trees: 0, Hash: core.NewHash("cf4aa3b38974fb7d81f367c0830f7d78d65ab86b")},
-}
-
-func (s *IdxfileSuite) TestDecodeMergeConflict(c *C) {
- f, err := fixtures.Basic().ByTag("merge-conflict").One().DotGit().Open("index")
- c.Assert(err, IsNil)
-
- idx := &Index{}
- d := NewDecoder(f)
- err = d.Decode(idx)
- c.Assert(err, IsNil)
-
- c.Assert(idx.Version, Equals, uint32(2))
- c.Assert(idx.Entries, HasLen, 13)
-
- expected := []struct {
- Stage Stage
- Hash string
- }{
- {AncestorMode, "880cd14280f4b9b6ed3986d6671f907d7cc2a198"},
- {OurMode, "d499a1a0b79b7d87a35155afd0c1cce78b37a91c"},
- {TheirMode, "14f8e368114f561c38e134f6e68ea6fea12d77ed"},
- }
-
- // stagged files
- for i, e := range idx.Entries[4:7] {
- c.Assert(e.Stage, Equals, expected[i].Stage)
- c.Assert(e.CreatedAt.Unix(), Equals, int64(0))
- c.Assert(e.CreatedAt.Nanosecond(), Equals, 0)
- c.Assert(e.ModifiedAt.Unix(), Equals, int64(0))
- c.Assert(e.ModifiedAt.Nanosecond(), Equals, 0)
- c.Assert(e.Dev, Equals, uint32(0))
- c.Assert(e.Inode, Equals, uint32(0))
- c.Assert(e.UID, Equals, uint32(0))
- c.Assert(e.GID, Equals, uint32(0))
- c.Assert(e.Size, Equals, uint32(0))
- c.Assert(e.Hash.String(), Equals, expected[i].Hash)
- c.Assert(e.Name, Equals, "go/example.go")
- }
-
-}
-
-func (s *IdxfileSuite) TestDecodeExtendedV3(c *C) {
- f, err := fixtures.Basic().ByTag("intent-to-add").One().DotGit().Open("index")
- c.Assert(err, IsNil)
-
- idx := &Index{}
- d := NewDecoder(f)
- err = d.Decode(idx)
- c.Assert(err, IsNil)
-
- c.Assert(idx.Version, Equals, uint32(3))
- c.Assert(idx.Entries, HasLen, 11)
-
- c.Assert(idx.Entries[6].Name, Equals, "intent-to-add")
- c.Assert(idx.Entries[6].IntentToAdd, Equals, true)
- c.Assert(idx.Entries[6].SkipWorktree, Equals, false)
-}
-
-func (s *IdxfileSuite) TestDecodeResolveUndo(c *C) {
- f, err := fixtures.Basic().ByTag("resolve-undo").One().DotGit().Open("index")
- c.Assert(err, IsNil)
-
- idx := &Index{}
- d := NewDecoder(f)
- err = d.Decode(idx)
- c.Assert(err, IsNil)
-
- c.Assert(idx.Version, Equals, uint32(2))
- c.Assert(idx.Entries, HasLen, 8)
-
- ru := idx.ResolveUndo
- c.Assert(ru.Entries, HasLen, 2)
- c.Assert(ru.Entries[0].Path, Equals, "go/example.go")
- c.Assert(ru.Entries[0].Stages, HasLen, 3)
- c.Assert(ru.Entries[0].Stages[AncestorMode], Not(Equals), core.ZeroHash)
- c.Assert(ru.Entries[0].Stages[OurMode], Not(Equals), core.ZeroHash)
- c.Assert(ru.Entries[0].Stages[TheirMode], Not(Equals), core.ZeroHash)
- c.Assert(ru.Entries[1].Path, Equals, "haskal/haskal.hs")
- c.Assert(ru.Entries[1].Stages, HasLen, 2)
- c.Assert(ru.Entries[1].Stages[OurMode], Not(Equals), core.ZeroHash)
- c.Assert(ru.Entries[1].Stages[TheirMode], Not(Equals), core.ZeroHash)
-}
-
-func (s *IdxfileSuite) TestDecodeV4(c *C) {
- f, err := fixtures.Basic().ByTag("index-v4").One().DotGit().Open("index")
- c.Assert(err, IsNil)
-
- idx := &Index{}
- d := NewDecoder(f)
- err = d.Decode(idx)
- c.Assert(err, IsNil)
-
- c.Assert(idx.Version, Equals, uint32(4))
- c.Assert(idx.Entries, HasLen, 11)
-
- names := []string{
- ".gitignore", "CHANGELOG", "LICENSE", "binary.jpg", "go/example.go",
- "haskal/haskal.hs", "intent-to-add", "json/long.json",
- "json/short.json", "php/crappy.php", "vendor/foo.go",
- }
-
- for i, e := range idx.Entries {
- c.Assert(e.Name, Equals, names[i])
- }
-
- c.Assert(idx.Entries[6].Name, Equals, "intent-to-add")
- c.Assert(idx.Entries[6].IntentToAdd, Equals, true)
- c.Assert(idx.Entries[6].SkipWorktree, Equals, false)
-}
diff --git a/formats/index/doc.go b/formats/index/doc.go
deleted file mode 100644
index 00466af..0000000
--- a/formats/index/doc.go
+++ /dev/null
@@ -1,302 +0,0 @@
-// Package index implements a encoder/decoder of index format files
-package index
-
-/*
-Git index format
-================
-
-== The Git index file has the following format
-
- All binary numbers are in network byte order. Version 2 is described
- here unless stated otherwise.
-
- - A 12-byte header consisting of
-
- 4-byte signature:
- The signature is { 'D', 'I', 'R', 'C' } (stands for "dircache")
-
- 4-byte version number:
- The current supported versions are 2, 3 and 4.
-
- 32-bit number of index entries.
-
- - A number of sorted index entries (see below).
-
- - Extensions
-
- Extensions are identified by signature. Optional extensions can
- be ignored if Git does not understand them.
-
- Git currently supports cached tree and resolve undo extensions.
-
- 4-byte extension signature. If the first byte is 'A'..'Z' the
- extension is optional and can be ignored.
-
- 32-bit size of the extension
-
- Extension data
-
- - 160-bit SHA-1 over the content of the index file before this
- checksum.
-
-== Index entry
-
- Index entries are sorted in ascending order on the name field,
- interpreted as a string of unsigned bytes (i.e. memcmp() order, no
- localization, no special casing of directory separator '/'). Entries
- with the same name are sorted by their stage field.
-
- 32-bit ctime seconds, the last time a file's metadata changed
- this is stat(2) data
-
- 32-bit ctime nanosecond fractions
- this is stat(2) data
-
- 32-bit mtime seconds, the last time a file's data changed
- this is stat(2) data
-
- 32-bit mtime nanosecond fractions
- this is stat(2) data
-
- 32-bit dev
- this is stat(2) data
-
- 32-bit ino
- this is stat(2) data
-
- 32-bit mode, split into (high to low bits)
-
- 4-bit object type
- valid values in binary are 1000 (regular file), 1010 (symbolic link)
- and 1110 (gitlink)
-
- 3-bit unused
-
- 9-bit unix permission. Only 0755 and 0644 are valid for regular files.
- Symbolic links and gitlinks have value 0 in this field.
-
- 32-bit uid
- this is stat(2) data
-
- 32-bit gid
- this is stat(2) data
-
- 32-bit file size
- This is the on-disk size from stat(2), truncated to 32-bit.
-
- 160-bit SHA-1 for the represented object
-
- A 16-bit 'flags' field split into (high to low bits)
-
- 1-bit assume-valid flag
-
- 1-bit extended flag (must be zero in version 2)
-
- 2-bit stage (during merge)
-
- 12-bit name length if the length is less than 0xFFF; otherwise 0xFFF
- is stored in this field.
-
- (Version 3 or later) A 16-bit field, only applicable if the
- "extended flag" above is 1, split into (high to low bits).
-
- 1-bit reserved for future
-
- 1-bit skip-worktree flag (used by sparse checkout)
-
- 1-bit intent-to-add flag (used by "git add -N")
-
- 13-bit unused, must be zero
-
- Entry path name (variable length) relative to top level directory
- (without leading slash). '/' is used as path separator. The special
- path components ".", ".." and ".git" (without quotes) are disallowed.
- Trailing slash is also disallowed.
-
- The exact encoding is undefined, but the '.' and '/' characters
- are encoded in 7-bit ASCII and the encoding cannot contain a NUL
- byte (iow, this is a UNIX pathname).
-
- (Version 4) In version 4, the entry path name is prefix-compressed
- relative to the path name for the previous entry (the very first
- entry is encoded as if the path name for the previous entry is an
- empty string). At the beginning of an entry, an integer N in the
- variable width encoding (the same encoding as the offset is encoded
- for OFS_DELTA pack entries; see pack-format.txt) is stored, followed
- by a NUL-terminated string S. Removing N bytes from the end of the
- path name for the previous entry, and replacing it with the string S
- yields the path name for this entry.
-
- 1-8 nul bytes as necessary to pad the entry to a multiple of eight bytes
- while keeping the name NUL-terminated.
-
- (Version 4) In version 4, the padding after the pathname does not
- exist.
-
- Interpretation of index entries in split index mode is completely
- different. See below for details.
-
-== Extensions
-
-=== Cached tree
-
- Cached tree extension contains pre-computed hashes for trees that can
- be derived from the index. It helps speed up tree object generation
- from index for a new commit.
-
- When a path is updated in index, the path must be invalidated and
- removed from tree cache.
-
- The signature for this extension is { 'T', 'R', 'E', 'E' }.
-
- A series of entries fill the entire extension; each of which
- consists of:
-
- - NUL-terminated path component (relative to its parent directory);
-
- - ASCII decimal number of entries in the index that is covered by the
- tree this entry represents (entry_count);
-
- - A space (ASCII 32);
-
- - ASCII decimal number that represents the number of subtrees this
- tree has;
-
- - A newline (ASCII 10); and
-
- - 160-bit object name for the object that would result from writing
- this span of index as a tree.
-
- An entry can be in an invalidated state and is represented by having
- a negative number in the entry_count field. In this case, there is no
- object name and the next entry starts immediately after the newline.
- When writing an invalid entry, -1 should always be used as entry_count.
-
- The entries are written out in the top-down, depth-first order. The
- first entry represents the root level of the repository, followed by the
- first subtree--let's call this A--of the root level (with its name
- relative to the root level), followed by the first subtree of A (with
- its name relative to A), ...
-
-=== Resolve undo
-
- A conflict is represented in the index as a set of higher stage entries.
- When a conflict is resolved (e.g. with "git add path"), these higher
- stage entries will be removed and a stage-0 entry with proper resolution
- is added.
-
- When these higher stage entries are removed, they are saved in the
- resolve undo extension, so that conflicts can be recreated (e.g. with
- "git checkout -m"), in case users want to redo a conflict resolution
- from scratch.
-
- The signature for this extension is { 'R', 'E', 'U', 'C' }.
-
- A series of entries fill the entire extension; each of which
- consists of:
-
- - NUL-terminated pathname the entry describes (relative to the root of
- the repository, i.e. full pathname);
-
- - Three NUL-terminated ASCII octal numbers, entry mode of entries in
- stage 1 to 3 (a missing stage is represented by "0" in this field);
- and
-
- - At most three 160-bit object names of the entry in stages from 1 to 3
- (nothing is written for a missing stage).
-
-=== Split index
-
- In split index mode, the majority of index entries could be stored
- in a separate file. This extension records the changes to be made on
- top of that to produce the final index.
-
- The signature for this extension is { 'l', 'i', 'n', 'k' }.
-
- The extension consists of:
-
- - 160-bit SHA-1 of the shared index file. The shared index file path
- is $GIT_DIR/sharedindex.<SHA-1>. If all 160 bits are zero, the
- index does not require a shared index file.
-
- - An ewah-encoded delete bitmap, each bit represents an entry in the
- shared index. If a bit is set, its corresponding entry in the
- shared index will be removed from the final index. Note, because
- a delete operation changes index entry positions, but we do need
- original positions in replace phase, it's best to just mark
- entries for removal, then do a mass deletion after replacement.
-
- - An ewah-encoded replace bitmap, each bit represents an entry in
- the shared index. If a bit is set, its corresponding entry in the
- shared index will be replaced with an entry in this index
- file. All replaced entries are stored in sorted order in this
- index. The first "1" bit in the replace bitmap corresponds to the
- first index entry, the second "1" bit to the second entry and so
- on. Replaced entries may have empty path names to save space.
-
- The remaining index entries after replaced ones will be added to the
- final index. These added entries are also sorted by entry name then
- stage.
-
-== Untracked cache
-
- Untracked cache saves the untracked file list and necessary data to
- verify the cache. The signature for this extension is { 'U', 'N',
- 'T', 'R' }.
-
- The extension starts with
-
- - A sequence of NUL-terminated strings, preceded by the size of the
- sequence in variable width encoding. Each string describes the
- environment where the cache can be used.
-
- - Stat data of $GIT_DIR/info/exclude. See "Index entry" section from
- ctime field until "file size".
-
- - Stat data of core.excludesfile
-
- - 32-bit dir_flags (see struct dir_struct)
-
- - 160-bit SHA-1 of $GIT_DIR/info/exclude. Null SHA-1 means the file
- does not exist.
-
- - 160-bit SHA-1 of core.excludesfile. Null SHA-1 means the file does
- not exist.
-
- - NUL-terminated string of per-dir exclude file name. This usually
- is ".gitignore".
-
- - The number of following directory blocks, variable width
- encoding. If this number is zero, the extension ends here with a
- following NUL.
-
- - A number of directory blocks in depth-first-search order, each
- consists of
-
- - The number of untracked entries, variable width encoding.
-
- - The number of sub-directory blocks, variable width encoding.
-
- - The directory name terminated by NUL.
-
- - A number of untracked file/dir names terminated by NUL.
-
-The remaining data of each directory block is grouped by type:
-
- - An ewah bitmap, the n-th bit marks whether the n-th directory has
- valid untracked cache entries.
-
- - An ewah bitmap, the n-th bit records "check-only" bit of
- read_directory_recursive() for the n-th directory.
-
- - An ewah bitmap, the n-th bit indicates whether SHA-1 and stat data
- is valid for the n-th directory and exists in the next data.
-
- - An array of stat data. The n-th data corresponds with the n-th
- "one" bit in the previous ewah bitmap.
-
- - An array of SHA-1. The n-th SHA-1 corresponds with the n-th "one" bit
- in the previous ewah bitmap.
-
- - One NUL.
-*/
diff --git a/formats/index/encoder.go b/formats/index/encoder.go
deleted file mode 100644
index 94fbc68..0000000
--- a/formats/index/encoder.go
+++ /dev/null
@@ -1,141 +0,0 @@
-package index
-
-import (
- "bytes"
- "crypto/sha1"
- "errors"
- "hash"
- "io"
- "time"
-
- "gopkg.in/src-d/go-git.v4/utils/binary"
-)
-
-var (
- // EncodeVersionSupported is the range of supported index versions
- EncodeVersionSupported uint32 = 2
-
- // ErrInvalidTimestamp is returned by Encode if a Index with a Entry with
- // negative timestamp values
- ErrInvalidTimestamp = errors.New("negative timestamps are not allowed")
-)
-
-// An Encoder writes an Index to an output stream.
-type Encoder struct {
- w io.Writer
- hash hash.Hash
-}
-
-// NewEncoder returns a new encoder that writes to w.
-func NewEncoder(w io.Writer) *Encoder {
- h := sha1.New()
- mw := io.MultiWriter(w, h)
- return &Encoder{mw, h}
-}
-
-// Encode writes the Index to the stream of the encoder.
-func (e *Encoder) Encode(idx *Index) error {
- // TODO: support versions v3 and v4
- // TODO: support extensions
- if idx.Version != EncodeVersionSupported {
- return ErrUnsupportedVersion
- }
-
- if err := e.encodeHeader(idx); err != nil {
- return err
- }
-
- if err := e.encodeEntries(idx); err != nil {
- return err
- }
-
- return e.encodeFooter()
-}
-
-func (e *Encoder) encodeHeader(idx *Index) error {
- return binary.Write(e.w,
- indexSignature,
- idx.Version,
- uint32(len(idx.Entries)),
- )
-}
-
-func (e *Encoder) encodeEntries(idx *Index) error {
- for _, entry := range idx.Entries {
- if err := e.encodeEntry(&entry); err != nil {
- return err
- }
-
- wrote := entryHeaderLength + len(entry.Name)
- if err := e.padEntry(wrote); err != nil {
- return err
- }
- }
-
- return nil
-}
-
-func (e *Encoder) encodeEntry(entry *Entry) error {
- if entry.IntentToAdd || entry.SkipWorktree {
- return ErrUnsupportedVersion
- }
-
- sec, nsec, err := e.timeToUint32(&entry.CreatedAt)
- if err != nil {
- return err
- }
-
- msec, mnsec, err := e.timeToUint32(&entry.ModifiedAt)
- if err != nil {
- return err
- }
-
- flags := uint16(entry.Stage&0x3) << 12
- if l := len(entry.Name); l < nameMask {
- flags |= uint16(l)
- } else {
- flags |= nameMask
- }
-
- flow := []interface{}{
- sec, nsec,
- msec, mnsec,
- entry.Dev,
- entry.Inode,
- entry.Mode,
- entry.UID,
- entry.GID,
- entry.Size,
- entry.Hash[:],
- flags,
- }
-
- if err := binary.Write(e.w, flow...); err != nil {
- return err
- }
-
- return binary.Write(e.w, []byte(entry.Name))
-}
-
-func (e *Encoder) timeToUint32(t *time.Time) (uint32, uint32, error) {
- if t.IsZero() {
- return 0, 0, nil
- }
-
- if t.Unix() < 0 || t.UnixNano() < 0 {
- return 0, 0, ErrInvalidTimestamp
- }
-
- return uint32(t.Unix()), uint32(t.Nanosecond()), nil
-}
-
-func (e *Encoder) padEntry(wrote int) error {
- padLen := 8 - wrote%8
-
- _, err := e.w.Write(bytes.Repeat([]byte{'\x00'}, padLen))
- return err
-}
-
-func (e *Encoder) encodeFooter() error {
- return binary.Write(e.w, e.hash.Sum(nil))
-}
diff --git a/formats/index/encoder_test.go b/formats/index/encoder_test.go
deleted file mode 100644
index 3085988..0000000
--- a/formats/index/encoder_test.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package index
-
-import (
- "bytes"
- "strings"
- "time"
-
- . "gopkg.in/check.v1"
- "gopkg.in/src-d/go-git.v4/core"
-)
-
-func (s *IdxfileSuite) TestEncode(c *C) {
- idx := &Index{
- Version: 2,
- Entries: []Entry{{
- CreatedAt: time.Now(),
- ModifiedAt: time.Now(),
- Dev: 4242,
- Inode: 424242,
- UID: 84,
- GID: 8484,
- Size: 42,
- Stage: TheirMode,
- Hash: core.NewHash("e25b29c8946e0e192fae2edc1dabf7be71e8ecf3"),
- Name: "foo",
- }, {
- CreatedAt: time.Now(),
- ModifiedAt: time.Now(),
- Name: strings.Repeat(" ", 20),
- Size: 82,
- }},
- }
-
- buf := bytes.NewBuffer(nil)
- e := NewEncoder(buf)
- err := e.Encode(idx)
- c.Assert(err, IsNil)
-
- output := &Index{}
- d := NewDecoder(buf)
- err = d.Decode(output)
- c.Assert(err, IsNil)
-
- c.Assert(idx, DeepEquals, output)
-}
-
-func (s *IdxfileSuite) TestEncodeUnsuportedVersion(c *C) {
- idx := &Index{Version: 3}
-
- buf := bytes.NewBuffer(nil)
- e := NewEncoder(buf)
- err := e.Encode(idx)
- c.Assert(err, Equals, ErrUnsupportedVersion)
-}
-
-func (s *IdxfileSuite) TestEncodeWithIntentToAddUnsuportedVersion(c *C) {
- idx := &Index{
- Version: 2,
- Entries: []Entry{{IntentToAdd: true}},
- }
-
- buf := bytes.NewBuffer(nil)
- e := NewEncoder(buf)
- err := e.Encode(idx)
- c.Assert(err, Equals, ErrUnsupportedVersion)
-}
-
-func (s *IdxfileSuite) TestEncodeWithSkipWorktreeUnsuportedVersion(c *C) {
- idx := &Index{
- Version: 2,
- Entries: []Entry{{SkipWorktree: true}},
- }
-
- buf := bytes.NewBuffer(nil)
- e := NewEncoder(buf)
- err := e.Encode(idx)
- c.Assert(err, Equals, ErrUnsupportedVersion)
-}
diff --git a/formats/index/index.go b/formats/index/index.go
deleted file mode 100644
index 35a5391..0000000
--- a/formats/index/index.go
+++ /dev/null
@@ -1,108 +0,0 @@
-package index
-
-import (
- "errors"
- "os"
- "time"
-
- "gopkg.in/src-d/go-git.v4/core"
-)
-
-var (
- // ErrUnsupportedVersion is returned by Decode when the idxindex file
- // version is not supported.
- ErrUnsupportedVersion = errors.New("Unsuported version")
-
- indexSignature = []byte{'D', 'I', 'R', 'C'}
- treeExtSignature = []byte{'T', 'R', 'E', 'E'}
- resolveUndoExtSignature = []byte{'R', 'E', 'U', 'C'}
-)
-
-// Stage during merge
-type Stage int
-
-const (
- // Merged is the default stage, fully merged
- Merged Stage = 1
- // AncestorMode is the base revision
- AncestorMode Stage = 1
- // OurMode is the first tree revision, ours
- OurMode Stage = 2
- // TheirMode is the second tree revision, theirs
- TheirMode Stage = 3
-)
-
-// Index contains the information about which objects are currently checked out
-// in the worktree, having information about the working files. Changes in
-// worktree are detected using this Index. The Index is also used during merges
-type Index struct {
- Version uint32
- Entries []Entry
- Cache *Tree
- ResolveUndo *ResolveUndo
-}
-
-// Entry represents a single file (or stage of a file) in the cache. An entry
-// represents exactly one stage of a file. If a file path is unmerged then
-// multiple Entry instances may appear for the same path name.
-type Entry struct {
- // Hash is the SHA1 of the represented file
- Hash core.Hash
- // Name is the Entry path name relative to top level directory
- Name string
- // CreatedAt time when the tracked path was created
- CreatedAt time.Time
- // ModifiedAt time when the tracked path was changed
- ModifiedAt time.Time
- // Dev and Inode of the tracked path
- Dev, Inode uint32
- // Mode of the path
- Mode os.FileMode
- // UID and GID, userid and group id of the owner
- UID, GID uint32
- // Size is the length in bytes for regular files
- Size uint32
- // Stage on a merge is defines what stage is representing this entry
- // https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging
- Stage Stage
- // SkipWorktree used in sparse checkouts
- // https://git-scm.com/docs/git-read-tree#_sparse_checkout
- SkipWorktree bool
- // IntentToAdd record only the fact that the path will be added later
- // https://git-scm.com/docs/git-add ("git add -N")
- IntentToAdd bool
-}
-
-// Tree contains pre-computed hashes for trees that can be derived from the
-// index. It helps speed up tree object generation from index for a new commit.
-type Tree struct {
- Entries []TreeEntry
-}
-
-// TreeEntry entry of a cached Tree
-type TreeEntry struct {
- // Path component (relative to its parent directory)
- Path string
- // Entries is the number of entries in the index that is covered by the tree
- // this entry represents
- Entries int
- // Trees is the number that represents the number of subtrees this tree has
- Trees int
- // Hash object name for the object that would result from writing this span
- // of index as a tree.
- Hash core.Hash
-}
-
-// ResolveUndo when a conflict is resolved (e.g. with "git add path"), these
-// higher stage entries will be removed and a stage-0 entry with proper
-// resolution is added. When these higher stage entries are removed, they are
-// saved in the resolve undo extension
-type ResolveUndo struct {
- Entries []ResolveUndoEntry
-}
-
-// ResolveUndoEntry contains the information about a conflict when is resolved
-type ResolveUndoEntry struct {
- Path string
- Stages map[Stage]core.Hash
-}