Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Adding a file directly to index (stage area), without using working tree.
- //
- // $ go build index-add.go && yes | mv index-add ~/code/workspace/bin
- // $ mkdir -p /tmp/sample && cd /tmp/sample
- // $ index-add
- package main
- import (
- "bytes"
- "fmt"
- "path"
- "sort"
- "strconv"
- "strings"
- "time"
- "golang.org/x/crypto/openpgp"
- "gopkg.in/src-d/go-billy.v4"
- "gopkg.in/src-d/go-billy.v4/osfs"
- "gopkg.in/src-d/go-git.v4"
- . "gopkg.in/src-d/go-git.v4/_examples"
- "gopkg.in/src-d/go-git.v4/plumbing"
- "gopkg.in/src-d/go-git.v4/plumbing/filemode"
- "gopkg.in/src-d/go-git.v4/plumbing/format/index"
- "gopkg.in/src-d/go-git.v4/plumbing/object"
- "gopkg.in/src-d/go-git.v4/storage"
- fsfs "gopkg.in/src-d/go-git.v4/storage/filesystem"
- )
- // 获取git log
- func gitLog(r *git.Repository) {
- // ... retrieves the branch pointed by HEAD
- ref, err := r.Head()
- CheckIfError(err)
- // ... retrieves the commit history
- cIter, err := r.Log(&git.LogOptions{From: ref.Hash()})
- CheckIfError(err)
- // ... just iterates over the commits, printing it
- err = cIter.ForEach(func(c *object.Commit) error {
- fmt.Println(c)
- return nil
- })
- CheckIfError(err)
- }
- // 创建一个新的 bare repository
- func gitInit(repoPath string) *git.Repository {
- s := fsfs.NewStorage(osfs.New(repoPath), nil)
- r, err := git.Init(s, nil)
- CheckIfError(err)
- return r
- }
- // 打开一个已经存在的 repository
- func gitPlainOpen(repoPath string) *git.Repository {
- r, err := git.PlainOpen(repoPath)
- CheckIfError(err)
- return r
- }
- // 添加文件到 index cache
- func gitAdd(r *git.Repository, path string, content []byte) {
- // Find index from Storer, here we are using filesystem.
- idx, err := r.Storer.Index()
- CheckIfError(err)
- // Get an object, write data into the object, then save to object storage.
- obj := r.Storer.NewEncodedObject()
- obj.SetType(plumbing.BlobObject)
- obj.SetSize(int64(len(content)))
- // The implementation of "obj.Writer.Write()" is MemoryObject, which
- // makes a copy of the object.
- writer, err := obj.Writer()
- CheckIfError(err)
- _, err = writer.Write(content)
- CheckIfError(err)
- writer.Close()
- // Here we again copy the object from "obj" to underline storage. Once
- // saved, it is officially considered part of git database.
- // ** Improvement Needed to avoid Double Copy**
- h, err := r.Storer.SetEncodedObject(obj)
- CheckIfError(err)
- e := idx.Add(path)
- // Add a new entry (we can use "idx.Entry(path)" to check if path exists).
- e.Hash = h
- e.Mode = filemode.Regular
- // Set index, which will be translated to tree object once we commit.
- r.Storer.SetIndex(idx)
- }
- func gitDelete(r *git.Repository, filePath string) {
- // Find index from Storer, here we are using filesystem.
- idx, err := r.Storer.Index()
- CheckIfError(err)
- _, err = idx.Remove(filePath)
- CheckIfError(err)
- err = r.Storer.SetIndex(idx)
- CheckIfError(err)
- }
- // 发布变更
- func gitCommit(r *git.Repository, msg string, opts *git.CommitOptions) (plumbing.Hash, error) {
- if err := opts.Validate(r); err != nil {
- return plumbing.ZeroHash, err
- }
- idx, err := r.Storer.Index()
- if err != nil {
- return plumbing.ZeroHash, err
- }
- h := &buildTreeHelper{
- s: r.Storer,
- }
- tree, err := h.BuildTree(idx)
- if err != nil {
- return plumbing.ZeroHash, err
- }
- commit, err := buildCommitObject(r, msg, opts, tree)
- if err != nil {
- return plumbing.ZeroHash, err
- }
- // updateHEAD
- head, err := r.Storer.Reference(plumbing.HEAD)
- if err != nil {
- return commit, err
- }
- name := plumbing.HEAD
- if head.Type() != plumbing.HashReference {
- name = head.Target()
- }
- ref := plumbing.NewHashReference(name, commit)
- return commit, r.Storer.SetReference(ref)
- }
- func main() {
- // repoPath = "/tmp/interesting"
- // gitInit or gitPlainOpen
- r := gitInit("/tmp/interesting")
- // gitAdd
- for i := 0; i < 10; i++ {
- content := []byte(strconv.Itoa(i))
- filePath := strconv.Itoa(i)
- gitAdd(r, filePath, content)
- }
- // gitCommit
- _, err := gitCommit(r, "add files", &git.CommitOptions{
- Author: &object.Signature{
- Name: "John Doe",
- Email: "john@doe.org",
- When: time.Now(),
- },
- Parents: []plumbing.Hash{},
- })
- CheckIfError(err)
- // gitDelete
- gitDelete(r, "3")
- // gitCommit
- _, err = gitCommit(r, "remove file", &git.CommitOptions{
- Author: &object.Signature{
- Name: "John Doe",
- Email: "john@doe.org",
- When: time.Now(),
- },
- Parents: []plumbing.Hash{},
- })
- CheckIfError(err)
- // gitLog
- gitLog(r)
- }
- /////////////////////////////////////////////////////////////////////
- ///////////////// ///////////////
- ///////////////// The following copy from work tree ///////////////
- ///////////////// ///////////////
- /////////////////////////////////////////////////////////////////////
- // buildTreeHelper converts a given index.Index file into multiple git objects
- // reading the blobs from the given filesystem and creating the trees from the
- // index structure. The created objects are pushed to a given Storer.
- type buildTreeHelper struct {
- fs billy.Filesystem
- s storage.Storer
- trees map[string]*object.Tree
- entries map[string]*object.TreeEntry
- }
- // BuildTree builds the tree objects and push its to the storer, the hash
- // of the root tree is returned.
- func (h *buildTreeHelper) BuildTree(idx *index.Index) (plumbing.Hash, error) {
- const rootNode = ""
- h.trees = map[string]*object.Tree{rootNode: {}}
- h.entries = map[string]*object.TreeEntry{}
- for _, e := range idx.Entries {
- if err := h.commitIndexEntry(e); err != nil {
- return plumbing.ZeroHash, err
- }
- }
- return h.copyTreeToStorageRecursive(rootNode, h.trees[rootNode])
- }
- func (h *buildTreeHelper) commitIndexEntry(e *index.Entry) error {
- parts := strings.Split(e.Name, "/")
- var fullpath string
- for _, part := range parts {
- parent := fullpath
- fullpath = path.Join(fullpath, part)
- h.doBuildTree(e, parent, fullpath)
- }
- return nil
- }
- func (h *buildTreeHelper) doBuildTree(e *index.Entry, parent, fullpath string) {
- if _, ok := h.trees[fullpath]; ok {
- return
- }
- if _, ok := h.entries[fullpath]; ok {
- return
- }
- te := object.TreeEntry{Name: path.Base(fullpath)}
- if fullpath == e.Name {
- te.Mode = e.Mode
- te.Hash = e.Hash
- } else {
- te.Mode = filemode.Dir
- h.trees[fullpath] = &object.Tree{}
- }
- h.trees[parent].Entries = append(h.trees[parent].Entries, te)
- }
- type sortableEntries []object.TreeEntry
- func (sortableEntries) sortName(te object.TreeEntry) string {
- if te.Mode == filemode.Dir {
- return te.Name + "/"
- }
- return te.Name
- }
- func (se sortableEntries) Len() int { return len(se) }
- func (se sortableEntries) Less(i int, j int) bool { return se.sortName(se[i]) < se.sortName(se[j]) }
- func (se sortableEntries) Swap(i int, j int) { se[i], se[j] = se[j], se[i] }
- func (h *buildTreeHelper) copyTreeToStorageRecursive(parent string, t *object.Tree) (plumbing.Hash, error) {
- sort.Sort(sortableEntries(t.Entries))
- for i, e := range t.Entries {
- if e.Mode != filemode.Dir && !e.Hash.IsZero() {
- continue
- }
- path := path.Join(parent, e.Name)
- var err error
- e.Hash, err = h.copyTreeToStorageRecursive(path, h.trees[path])
- if err != nil {
- return plumbing.ZeroHash, err
- }
- t.Entries[i] = e
- }
- o := h.s.NewEncodedObject()
- if err := t.Encode(o); err != nil {
- return plumbing.ZeroHash, err
- }
- return h.s.SetEncodedObject(o)
- }
- func buildCommitObject(r *git.Repository, msg string, opts *git.CommitOptions, tree plumbing.Hash) (plumbing.Hash, error) {
- commit := &object.Commit{
- Author: *opts.Author,
- Committer: *opts.Committer,
- Message: msg,
- TreeHash: tree,
- ParentHashes: opts.Parents,
- }
- if opts.SignKey != nil {
- sig, err := buildCommitSignature(commit, opts.SignKey)
- if err != nil {
- return plumbing.ZeroHash, err
- }
- commit.PGPSignature = sig
- }
- obj := r.Storer.NewEncodedObject()
- if err := commit.Encode(obj); err != nil {
- return plumbing.ZeroHash, err
- }
- return r.Storer.SetEncodedObject(obj)
- }
- func buildCommitSignature(commit *object.Commit, signKey *openpgp.Entity) (string, error) {
- encoded := &plumbing.MemoryObject{}
- if err := commit.Encode(encoded); err != nil {
- return "", err
- }
- r, err := encoded.Reader()
- if err != nil {
- return "", err
- }
- var b bytes.Buffer
- if err := openpgp.ArmoredDetachSign(&b, signKey, r, nil); err != nil {
- return "", err
- }
- return b.String(), nil
- }
Add Comment
Please, Sign In to add comment