aboutsummaryrefslogblamecommitdiffstats
path: root/worktree_status.go
blob: d472fde378f923003d81534765a4d87e99ddbd67 (plain) (tree)




































































































































                                                                                                          
package git

import (
	"bytes"

	"gopkg.in/src-d/go-git.v4/plumbing"
	"gopkg.in/src-d/go-git.v4/plumbing/object"
	"gopkg.in/src-d/go-git.v4/utils/merkletrie"
	"gopkg.in/src-d/go-git.v4/utils/merkletrie/filesystem"
	"gopkg.in/src-d/go-git.v4/utils/merkletrie/index"
	"gopkg.in/src-d/go-git.v4/utils/merkletrie/noder"
)

// Status returns the working tree status
func (w *Worktree) Status() (Status, error) {
	ref, err := w.r.Head()
	if err == plumbing.ErrReferenceNotFound {
		return nil, nil
	}

	if err != nil {
		return nil, err
	}

	return w.status(ref.Hash())
}

func (w *Worktree) status(commit plumbing.Hash) (Status, error) {
	s := make(Status, 0)

	right, err := w.diffStagingWithWorktree()
	if err != nil {
		return nil, err
	}

	for _, ch := range right {
		a, err := ch.Action()
		if err != nil {
			return nil, err
		}

		switch a {
		case merkletrie.Delete:
			s.File(ch.From.String()).Worktree = Deleted
		case merkletrie.Insert:
			s.File(ch.To.String()).Worktree = Untracked
			s.File(ch.To.String()).Staging = Untracked
		case merkletrie.Modify:
			s.File(ch.To.String()).Worktree = Modified
		}
	}

	left, err := w.diffCommitWithStaging(commit, false)
	if err != nil {
		return nil, err
	}

	for _, ch := range left {
		a, err := ch.Action()
		if err != nil {
			return nil, err
		}

		switch a {
		case merkletrie.Delete:
			s.File(ch.From.String()).Staging = Deleted
		case merkletrie.Insert:
			s.File(ch.To.String()).Staging = Added
		case merkletrie.Modify:
			s.File(ch.To.String()).Staging = Modified
		}
	}

	return s, nil
}

func (w *Worktree) diffStagingWithWorktree() (merkletrie.Changes, error) {
	idx, err := w.r.Storer.Index()
	if err != nil {
		return nil, err
	}

	from, err := index.NewRootNode(idx)
	if err != nil {
		return nil, err
	}

	to, err := filesystem.NewRootNode(w.fs)
	if err != nil {
		return nil, err
	}

	return merkletrie.DiffTree(from, to, IsEquals)
}

func (w *Worktree) diffCommitWithStaging(commit plumbing.Hash, reverse bool) (merkletrie.Changes, error) {
	idx, err := w.r.Storer.Index()
	if err != nil {
		return nil, err
	}

	to, err := index.NewRootNode(idx)
	if err != nil {
		return nil, err
	}

	c, err := w.r.CommitObject(commit)
	if err != nil {
		return nil, err
	}

	t, err := c.Tree()
	if err != nil {
		return nil, err
	}

	from := object.NewTreeRootNode(t)
	if reverse {
		return merkletrie.DiffTree(to, from, IsEquals)
	}

	return merkletrie.DiffTree(from, to, IsEquals)
}

func IsEquals(a, b noder.Hasher) bool {
	pathA := a.(noder.Path)
	pathB := b.(noder.Path)
	if pathA[len(pathA)-1].IsDir() || pathB[len(pathB)-1].IsDir() {
		return false
	}

	return bytes.Equal(a.Hash(), b.Hash())
}