src-d/gogit 刪除檔案或目錄
阿新 • • 發佈:2018-12-09
首先 commit 測試樣例:
package git
import (
"bytes"
"strings"
"time"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/armor"
"golang.org/x/crypto/openpgp/errors"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/object"
"gopkg.in/src-d/go-git.v4/plumbing/storer"
"gopkg.in/src-d/go-git.v4/storage/memory"
. "gopkg.in/check.v1"
"gopkg.in/src-d/go-billy.v4/memfs"
"gopkg.in/src-d/go-billy.v4/util"
)
func (s *WorktreeSuite) TestCommitInvalidOptions(c *C) {
r, err := Init(memory.NewStorage(), memfs.New())
c.Assert(err, IsNil)
w, err := r.Worktree()
c.Assert(err, IsNil)
hash, err := w.Commit("", &CommitOptions{})
c.Assert(err, Equals, ErrMissingAuthor)
c.Assert(hash.IsZero(), Equals, true)
}
func (s *WorktreeSuite) TestCommitInitial(c *C) {
expected := plumbing.NewHash("98c4ac7c29c913f7461eae06e024dc18e80d23a4")
fs := memfs.New()
storage := memory.NewStorage()
r, err := Init(storage, fs)
c.Assert(err, IsNil)
w, err := r.Worktree()
c.Assert(err, IsNil)
util.WriteFile(fs, "foo", []byte("foo"), 0644)
_, err = w.Add("foo")
c.Assert(err, IsNil)
hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature()})
c.Assert(hash, Equals, expected)
c.Assert(err, IsNil)
assertStorageStatus(c, r, 1, 1, 1, expected)
}
func (s *WorktreeSuite) TestCommitParent(c *C) {
expected := plumbing.NewHash("ef3ca05477530b37f48564be33ddd48063fc7a22")
fs := memfs.New()
w := &Worktree{
r: s.Repository,
Filesystem: fs,
}
err := w.Checkout(&CheckoutOptions{})
c.Assert(err, IsNil)
util.WriteFile(fs, "foo", []byte("foo"), 0644)
_, err = w.Add("foo")
c.Assert(err, IsNil)
hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature()})
c.Assert(hash, Equals, expected)
c.Assert(err, IsNil)
assertStorageStatus(c, s.Repository, 13, 11, 10, expected)
}
func (s *WorktreeSuite) TestCommitAll(c *C) {
expected := plumbing.NewHash("aede6f8c9c1c7ec9ca8d287c64b8ed151276fa28")
fs := memfs.New()
w := &Worktree{
r: s.Repository,
Filesystem: fs,
}
err := w.Checkout(&CheckoutOptions{})
c.Assert(err, IsNil)
util.WriteFile(fs, "LICENSE", []byte("foo"), 0644)
util.WriteFile(fs, "foo", []byte("foo"), 0644)
hash, err := w.Commit("foo\n", &CommitOptions{
All: true,
Author: defaultSignature(),
})
c.Assert(hash, Equals, expected)
c.Assert(err, IsNil)
assertStorageStatus(c, s.Repository, 13, 11, 10, expected)
}
func (s *WorktreeSuite) TestRemoveAndCommitAll(c *C) {
expected := plumbing.NewHash("907cd576c6ced2ecd3dab34a72bf9cf65944b9a9")
fs := memfs.New()
w := &Worktree{
r: s.Repository,
Filesystem: fs,
}
err := w.Checkout(&CheckoutOptions{})
c.Assert(err, IsNil)
util.WriteFile(fs, "foo", []byte("foo"), 0644)
_, err = w.Add("foo")
c.Assert(err, IsNil)
_, errFirst := w.Commit("Add in Repo\n", &CommitOptions{
Author: defaultSignature(),
})
c.Assert(errFirst, IsNil)
errRemove := fs.Remove("foo")
c.Assert(errRemove, IsNil)
hash, errSecond := w.Commit("Remove foo\n", &CommitOptions{
All: true,
Author: defaultSignature(),
})
c.Assert(errSecond, IsNil)
c.Assert(hash, Equals, expected)
c.Assert(err, IsNil)
assertStorageStatus(c, s.Repository, 13, 11, 11, expected)
}
func (s *WorktreeSuite) TestCommitSign(c *C) {
fs := memfs.New()
storage := memory.NewStorage()
r, err := Init(storage, fs)
c.Assert(err, IsNil)
w, err := r.Worktree()
c.Assert(err, IsNil)
util.WriteFile(fs, "foo", []byte("foo"), 0644)
_, err = w.Add("foo")
c.Assert(err, IsNil)
key := commitSignKey(c, true)
hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature(), SignKey: key})
c.Assert(err, IsNil)
// Verify the commit.
pks := new(bytes.Buffer)
pkw, err := armor.Encode(pks, openpgp.PublicKeyType, nil)
c.Assert(err, IsNil)
err = key.Serialize(pkw)
c.Assert(err, IsNil)
err = pkw.Close()
c.Assert(err, IsNil)
expectedCommit, err := r.CommitObject(hash)
c.Assert(err, IsNil)
actual, err := expectedCommit.Verify(pks.String())
c.Assert(err, IsNil)
c.Assert(actual.PrimaryKey, DeepEquals, key.PrimaryKey)
}
func (s *WorktreeSuite) TestCommitSignBadKey(c *C) {
fs := memfs.New()
storage := memory.NewStorage()
r, err := Init(storage, fs)
c.Assert(err, IsNil)
w, err := r.Worktree()
c.Assert(err, IsNil)
util.WriteFile(fs, "foo", []byte("foo"), 0644)
_, err = w.Add("foo")
c.Assert(err, IsNil)
key := commitSignKey(c, false)
_, err = w.Commit("foo\n", &CommitOptions{Author: defaultSignature(), SignKey: key})
c.Assert(err, Equals, errors.InvalidArgumentError("signing key is encrypted"))
}
func assertStorageStatus(
c *C, r *Repository,
treesCount, blobCount, commitCount int, head plumbing.Hash,
) {
trees, err := r.Storer.IterEncodedObjects(plumbing.TreeObject)
c.Assert(err, IsNil)
blobs, err := r.Storer.IterEncodedObjects(plumbing.BlobObject)
c.Assert(err, IsNil)
commits, err := r.Storer.IterEncodedObjects(plumbing.CommitObject)
c.Assert(err, IsNil)
c.Assert(lenIterEncodedObjects(trees), Equals, treesCount)
c.Assert(lenIterEncodedObjects(blobs), Equals, blobCount)
c.Assert(lenIterEncodedObjects(commits), Equals, commitCount)
ref, err := r.Head()
c.Assert(err, IsNil)
c.Assert(ref.Hash(), Equals, head)
}
func lenIterEncodedObjects(iter storer.EncodedObjectIter) int {
count := 0
iter.ForEach(func(plumbing.EncodedObject) error {
count++
return nil
})
return count
}
func defaultSignature() *object.Signature {
when, _ := time.Parse(object.DateFormat, "Thu May 04 00:03:43 2017 +0200")
return &object.Signature{
Name: "foo",
Email: " [email protected]",
When: when,
}
}
func commitSignKey(c *C, decrypt bool) *openpgp.Entity {
s := strings.NewReader(armoredKeyRing)
es, err := openpgp.ReadArmoredKeyRing(s)
c.Assert(err, IsNil)
c.Assert(es, HasLen, 1)
c.Assert(es[0].Identities, HasLen, 1)
_, ok := es[0].Identities["foo bar < [email protected]>"]
c.Assert(ok, Equals, true)
key := es[0]
if decrypt {
err = key.PrivateKey.Decrypt([]byte(keyPassphrase))
c.Assert(err, IsNil)
}
return key
}
新增檔案以及目錄是呼叫WorkTree中的Add() 方法;首先看下Add 方法在哪個檔案中,找到WorkTree.go中沒有;為了快速找到Add 只能使用LiteIDE (go 開發的ide),可以直接連線到方法所在的檔案worktree_status.go。
然後其中有remove方法:
func (w *Worktree) Add(path string) (plumbing.Hash, error) {
// TODO(mcuadros): remove plumbing.Hash from signature at v5.
s, err := w.Status()
if err != nil {
return plumbing.ZeroHash, err
}
idx, err := w.r.Storer.Index()
if err != nil {
return plumbing.ZeroHash, err
}
var h plumbing.Hash
var added bool
fi, err := w.Filesystem.Lstat(path)
if err != nil || !fi.IsDir() {
added, h, err = w.doAddFile(idx, s, path)
} else {
added, err = w.doAddDirectory(idx, s, path)
}
if err != nil {
return h, err
}
if !added {
return h, nil
}
return h, w.r.Storer.SetIndex(idx)
}
func (w *Worktree) doAddDirectory(idx *index.Index, s Status, directory string) (added bool, err error) {
files, err := w.Filesystem.ReadDir(directory)
if err != nil {
return false, err
}
for _, file := range files {
name := path.Join(directory, file.Name())
var a bool
if file.IsDir() {
if file.Name() == GitDirName {
// ignore special git directory
continue
}
a, err = w.doAddDirectory(idx, s, name)
} else {
a, _, err = w.doAddFile(idx, s, name)
}
if err != nil {
return
}
if !added && a {
added = true
}
}
return
}
// AddGlob adds all paths, matching pattern, to the index. If pattern matches a
// directory path, all directory contents are added to the index recursively. No
// error is returned if all matching paths are already staged in index.
func (w *Worktree) AddGlob(pattern string) error {
files, err := util.Glob(w.Filesystem, pattern)
if err != nil {
return err
}
if len(files) == 0 {
return ErrGlobNoMatches
}
s, err := w.Status()
if err != nil {
return err
}
idx, err := w.r.Storer.Index()
if err != nil {
return err
}
var saveIndex bool
for _, file := range files {
fi, err := w.Filesystem.Lstat(file)
if err != nil {
return err
}
var added bool
if fi.IsDir() {
added, err = w.doAddDirectory(idx, s, file)
} else {
added, _, err = w.doAddFile(idx, s, file)
}
if err != nil {
return err
}
if !saveIndex && added {
saveIndex = true
}
}
if saveIndex {
return w.r.Storer.SetIndex(idx)
}
return nil
}
// doAddFile create a new blob from path and update the index, added is true if
// the file added is different from the index.
func (w *Worktree) doAddFile(idx *index.Index, s Status, path string) (added bool, h plumbing.Hash, err error) {
if s.File(path).Worktree == Unmodified {
return false, h, nil
}
h, err = w.copyFileToStorage(path)
if err != nil {
if os.IsNotExist(err) {
added = true
h, err = w.deleteFromIndex(idx, path)
}
return
}
if err := w.addOrUpdateFileToIndex(idx, path, h); err != nil {
return false, h, err
}
return true, h, err
}
func (w *Worktree) copyFileToStorage(path string) (hash plumbing.Hash, err error) {
fi, err := w.Filesystem.Lstat(path)
if err != nil {
return plumbing.ZeroHash, err
}
obj := w.r.Storer.NewEncodedObject()
obj.SetType(plumbing.BlobObject)
obj.SetSize(fi.Size())
writer, err := obj.Writer()
if err != nil {
return plumbing.ZeroHash, err
}
defer ioutil.CheckClose(writer, &err)
if fi.Mode()&os.ModeSymlink != 0 {
err = w.fillEncodedObjectFromSymlink(writer, path, fi)
} else {
err = w.fillEncodedObjectFromFile(writer, path, fi)
}
if err != nil {
return plumbing.ZeroHash, err
}
return w.r.Storer.SetEncodedObject(obj)
}
func (w *Worktree) fillEncodedObjectFromFile(dst io.Writer, path string, fi os.FileInfo) (err error) {
src, err := w.Filesystem.Open(path)
if err != nil {
return err
}
defer ioutil.CheckClose(src, &err)
if _, err := io.Copy(dst, src); err != nil {
return err
}
return err
}
func (w *Worktree) fillEncodedObjectFromSymlink(dst io.Writer, path string, fi os.FileInfo) error {
target, err := w.Filesystem.Readlink(path)
if err != nil {
return err
}
_, err = dst.Write([]byte(target))
return err
}
func (w *Worktree) addOrUpdateFileToIndex(idx *index.Index, filename string, h plumbing.Hash) error {
e, err := idx.Entry(filename)
if err != nil && err != index.ErrEntryNotFound {
return err
}
if err == index.ErrEntryNotFound {
return w.doAddFileToIndex(idx, filename, h)
}
return w.doUpdateFileToIndex(e, filename, h)
}
func (w *Worktree) doAddFileToIndex(idx *index.Index, filename string, h plumbing.Hash) error {
return w.doUpdateFileToIndex(idx.Add(filename), filename, h)
}
func (w *Worktree) doUpdateFileToIndex(e *index.Entry, filename string, h plumbing.Hash) error {
info, err := w.Filesystem.Lstat(filename)
if err != nil {
return err
}
e.Hash = h
e.ModifiedAt = info.ModTime()
e.Mode, err = filemode.NewFromOSFileMode(info.Mode())
if err != nil {
return err
}
if e.Mode.IsRegular() {
e.Size = uint32(info.Size())
}
fillSystemInfo(e, info.Sys())
return nil
}
// Remove removes files from the working tree and from the index.
func (w *Worktree) Remove(path string) (plumbing.Hash, error) {
// TODO(mcuadros): remove plumbing.Hash from signature at v5.
idx, err := w.r.Storer.Index()
if err != nil {
return plumbing.ZeroHash, err
}
var h plumbing.Hash
fi, err := w.Filesystem.Lstat(path)
if err != nil || !fi.IsDir() {
h, err = w.doRemoveFile(idx, path)
} else {
_, err = w.doRemoveDirectory(idx, path)
}
if err != nil {
return h, err
}
return h, w.r.Storer.SetIndex(idx)
}
func (w *Worktree) doRemoveDirectory(idx *index.Index, directory string) (removed bool, err error) {
files, err := w.Filesystem.ReadDir(directory)
if err != nil {
return false, err
}
for _, file := range files {
name := path.Join(directory, file.Name())
var r bool
if file.IsDir() {
r, err = w.doRemoveDirectory(idx, name)
} else {
_, err = w.doRemoveFile(idx, name)
if err == index.ErrEntryNotFound {
err = nil
}
}
if err != nil {
return
}
if !removed && r {
removed = true
}
}
err = w.removeEmptyDirectory(directory)
return
}
func (w *Worktree) removeEmptyDirectory(path string) error {
files, err := w.Filesystem.ReadDir(path)
if err != nil {
return err
}
if len(files) != 0 {
return nil
}
return w.Filesystem.Remove(path)
}
func (w *Worktree) doRemoveFile(idx *index.Index, path string) (plumbing.Hash, error) {
hash, err := w.deleteFromIndex(idx, path)
if err != nil {
return plumbing.ZeroHash, err
}
return hash, w.deleteFromFilesystem(path)
}
func (w *Worktree) deleteFromIndex(idx *index.Index, path string) (plumbing.Hash, error) {
e, err := idx.Remove(path)
if err != nil {
return plumbing.ZeroHash, err
}
return e.Hash, nil
}
func (w *Worktree) deleteFromFilesystem(path string) error {
err := w.Filesystem.Remove(path)
if os.IsNotExist(err) {
return nil
}
return err
}
// RemoveGlob removes all paths, matching pattern, from the index. If pattern
// matches a directory path, all directory contents are removed from the index
// recursively.
func (w *Worktree) RemoveGlob(pattern string) error {
idx, err := w.r.Storer.Index()
if err != nil {
return err
}
entries, err := idx.Glob(pattern)
if err != nil {
return err
}
for _, e := range entries {
file := filepath.FromSlash(e.Name)
if _, err := w.Filesystem.Lstat(file); err != nil && !os.IsNotExist(err) {
return err
}
if _, err := w.doRemoveFile(idx, file); err != nil {
return err
}
dir, _ := filepath.Split(file)
if err := w.removeEmptyDirectory(dir); err != nil {
return err
}
}
return w.r.Storer.SetIndex(idx)
}
呼叫remove 然後commit 即可!!