Beispiel #1
0
func (d *deltaeval) Copy(repo *libgit.Repository, src ObjectReference, offset, length uint64) {
	id, _ := libgit.NewId(src)
	_, _, r, err := repo.GetRawObject(id, false)
	if err != nil {
		panic(err)
	}
	defer r.Close()
	if offset > 0 {
		tmp := make([]byte, offset)
		n, err := io.ReadFull(r, tmp)
		if err != nil {
			panic(err)
		}
		if n == 0 || uint64(n) != offset {
			panic("Couldn't correctly read offset.")
		}

	}

	reading := make([]byte, length)
	n, err := io.ReadFull(r, reading)
	if uint64(n) != length || err != nil {
		panic("Error copying data")
	}
	d.value = append(d.value, reading...)
}
Beispiel #2
0
func Branch(repo *libgit.Repository, args []string) {
	switch len(args) {
	case 0:
		branches, err := repo.GetBranches()
		if err != nil {
			fmt.Fprintln(os.Stderr, "Could not get list of branches.")
			return
		}
		head := getHeadBranch(repo)
		for _, b := range branches {
			if head == b {
				fmt.Print("* ")
			} else {
				fmt.Print("  ")
			}
			fmt.Println(b)
		}
	case 1:
		if head, err := getHeadId(repo); err == nil {
			if cerr := libgit.CreateBranch(repo.Path, args[0], head); cerr != nil {
				fmt.Fprintf(os.Stderr, "Could not create branch: %s\n", cerr.Error())
			}
		} else {
			fmt.Fprintf(os.Stderr, "Could not create branch: %s\n", err.Error())
		}
	default:
		fmt.Fprintln(os.Stderr, "Usage: go-git branch [branchname]")
	}

}
Beispiel #3
0
func writeObject(repo *libgit.Repository, objType string, rawdata []byte) error {
	obj := []byte(fmt.Sprintf("%s %d\000", objType, len(rawdata)))
	obj = append(obj, rawdata...)
	sha := sha1.Sum(obj)

	if have, _, err := repo.HaveObject(fmt.Sprintf("%x", sha)); have == true || err != nil {
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: %s\n", err)
			return err

		}

		//fmt.Fprintf(os.Stderr, "Already have object %x\n", sha)
		return ObjectExists

	}
	directory := fmt.Sprintf("%x", sha[0:1])
	file := fmt.Sprintf("%x", sha[1:])

	fmt.Printf("Putting %x in %s/%s\n", sha, directory, file)
	os.MkdirAll(repo.Path+"/objects/"+directory, os.FileMode(0755))
	f, err := os.Create(repo.Path + "/objects/" + directory + "/" + file)
	if err != nil {
		return err
	}
	defer f.Close()
	w := zlib.NewWriter(f)
	if _, err := w.Write(obj); err != nil {
		return err
	}
	defer w.Close()
	return nil
}
Beispiel #4
0
func getTreeishId(repo *libgit.Repository, treeish string) string {
	if branchId, err := repo.GetCommitIdOfBranch(treeish); err == nil {
		return branchId
	}
	if len(treeish) == 40 {
		return treeish
	}
	panic("TODO: Didn't implement getTreeishId")
}
Beispiel #5
0
func Init(repo *libgit.Repository, args []string) {
	if len(args) > 0 {
		if dir := args[len(args)-1]; dir != "init" {
			err := os.MkdirAll(dir, 0755)
			if err != nil {
				panic("Couldn't create directory for initializing git.")
			}
			err = os.Chdir(dir)
			if err != nil {
				panic("Couldn't change working directory while initializing git.")
			}
			if repo != nil {
				repo.Path = ".git/"
			}
		}
	}
	// These are all the directories created by a clean "git init"
	// with the canonical git implementation
	os.Mkdir(".git", 0755)
	os.MkdirAll(".git/objects/pack", 0755)
	os.MkdirAll(".git/objects/info", 0755)
	os.MkdirAll(".git/info", 0755)  // Should have exclude file in it
	os.MkdirAll(".git/hooks", 0755) // should have sample hooks in it.
	os.MkdirAll(".git/branches", 0755)
	os.MkdirAll(".git/refs/heads", 0755)
	os.MkdirAll(".git/refs/tags", 0755)

	ioutil.WriteFile(".git/HEAD", []byte("ref: refs/heads/master\n"), 0644)
	ioutil.WriteFile(".git/config", []byte("[core]\n\trepositoryformatversion = 0\n\tbare = false\n"), 0644)
	ioutil.WriteFile(".git/description", []byte("Unnamed repository; edit this file 'description' to name the repository.\n"), 0644)

}
Beispiel #6
0
// Calculate the final reslt of a REF_DELTA. This is largely
// based on the delta algorithm as described at:
// http://stefan.saasen.me/articles/git-clone-in-haskell-from-the-bottom-up/#pack_file_format
func calculateDelta(repo *libgit.Repository, reference ObjectReference, delta []byte) (PackEntryType, []byte) {
	deltaStream := bytes.NewBuffer(delta)

	// Read 2 variable length strings for the source and target buffer
	// length

	// read the source length, but we don't care. We just want to advance
	// the deltaStream pointer the proper amount.
	ReadVariable(deltaStream)
	// Read the target length so we know when we've finished processing
	// the delta stream.
	targetLength := ReadVariable(deltaStream)

	d := deltaeval{}
	for {
		d.DoInstruction(repo, deltaStream, reference, targetLength)
		if targetLength == uint64(len(d.value)) {
			break
		}
		if len(d.value) > int(targetLength) {
			panic("Read too much data from delta stream")
		}
	}
	// GetRawObject to find the underlying type of the original
	// reference
	id, err := libgit.NewId(reference)
	if err != nil {
		panic(err)
	}
	objt, _, _, err := repo.GetRawObject(id, true)
	if err != nil {
		panic(err)
	}
	switch objt {
	case libgit.ObjectCommit:
		return OBJ_COMMIT, d.value
	case libgit.ObjectTree:
		return OBJ_TREE, d.value
	case libgit.ObjectBlob:
		return OBJ_BLOB, d.value
	case libgit.ObjectTag:
		return OBJ_TAG, d.value
	}
	panic("Unhandle object type while calculating delta.")
	return 0, nil

}
Beispiel #7
0
func writeIndexEntries(repo *libgit.Repository, prefix string, entries []*GitIndexEntry) ([20]byte, error) {
	content := bytes.NewBuffer(nil)
	// [mode] [file/folder name]\0[SHA-1 of referencing blob or tree as [20]byte]

	lastname := ""
	firstIdxForTree := -1

	for idx, obj := range entries {
		nameBits := strings.Split(obj.PathName, "/")

		// Either it's the last entry and we haven't written a tree yet, or it's not the last
		// entry but the directory changed
		if (nameBits[0] != lastname || idx == len(entries)-1) && lastname != "" {
			var islice []*GitIndexEntry
			if idx == len(entries)-1 {
				islice = entries[firstIdxForTree:]
			} else {
				islice = entries[firstIdxForTree:idx]
			}
			subsha1, err := writeIndexSubtree(repo, lastname, islice)
			if err != nil {
				panic(err)
			}
			// Write the object
			fmt.Fprintf(content, "%o %s\x00", 0040000, lastname)
			content.Write(subsha1[:])

			// Reset the data keeping track of what this tree is.
			lastname = ""
			firstIdxForTree = -1
		}
		if len(nameBits) == 1 {
			//write the blob for the file portion
			fmt.Fprintf(content, "%o %s\x00", obj.Mode, obj.PathName)
			content.Write(obj.Sha1[:])
			lastname = ""
		} else {
			lastname = nameBits[0]
			if firstIdxForTree == -1 {
				firstIdxForTree = idx
			}
		}
	}

	sha1, err := repo.StoreObjectLoose(libgit.ObjectTree, bytes.NewReader(content.Bytes()))
	return [20]byte(sha1), err
}
Beispiel #8
0
func resetIndexFromCommit(repo *libgit.Repository, commitId string) error {
	// If the index doesn't exist, idx is a new index, so ignore
	// the path error that ReadIndex is returning
	idx, _ := ReadIndex(repo)
	com, err := repo.GetCommit(commitId)
	if err != nil {
		fmt.Printf("%s\n", err)
		return err
	}
	treeId := com.TreeId()
	tree := libgit.NewTree(repo, treeId)
	if tree == nil {
		panic("Error retriving tree for commit")
	}
	idx.ResetIndex(repo, tree)
	writeIndex(repo, idx, "index")
	return nil
}
Beispiel #9
0
func Merge(repo *libgit.Repository, args []string) {
	if len(args) != 1 {
		fmt.Fprintf(os.Stderr, "Usage: go-git merge branchname\n")
		return
	}
	commit, err := repo.GetCommitOfBranch(args[0])
	if err != nil {
		fmt.Fprintf(os.Stderr, "Invalid branch: %s\n", args[0])
		return
	}
	head, err := getHeadId(repo)
	if err != nil {
		panic(err)
	}

	// The current branch is an ancestor of HEAD. This
	// is a fast-forward.
	if headCom, err := repo.GetCommit(head); err == nil {
		if isAncestor(headCom, fmt.Sprintf("%s", commit.Id)) {
			fmt.Fprintf(os.Stderr, "Already up-to-date.\n")
			return
		}
	}

	// Head is an ancestor of the current branch.
	if isAncestor(commit, head) {
		hb := getHeadBranch(repo)
		newId := fmt.Sprintf("%s", commit.Id)

		ioutil.WriteFile(".git/refs/heads/"+hb, []byte(newId), 0644)
		resetIndexFromCommit(repo, newId)
		fmt.Printf("Hooray! A Fast forward on %s! New should should be %s\n", hb, commit.Id)
		return
	}

	panic("Only fast forward commits are currently supported.")
}
Beispiel #10
0
func Log(repo *libgit.Repository, args []string) error {
	if len(args) != 0 {
		fmt.Fprintf(os.Stderr, "Usage: go-git log\nNo options are currently supported.\n")
		return errors.New("No options are currently supported for log")
	}
	head, err := getHeadId(repo)
	if err != nil {
		return err
	}

	// CommitsBefore also returns the commit passed..
	l, err := repo.CommitsBefore(head)
	if err != nil {
		return err
	}
	for e := l.Front(); e != nil; e = e.Next() {
		c, ok := e.Value.(*libgit.Commit)
		if ok {
			printCommit(c)
		}
	}
	return nil

}
Beispiel #11
0
func CommitTree(repo *libgit.Repository, args []string) string {
	content := bytes.NewBuffer(nil)

	var parents []string
	var messageString, messageFile string
	var skipNext bool
	var tree string
	for idx, val := range args {
		if idx == 0 && val[0] != '-' {
			tree = val
			continue
		}

		if skipNext == true {
			skipNext = false
			continue
		}
		switch val {
		case "-p":
			parents = append(parents, args[idx+1])
			skipNext = true
		case "-m":
			messageString += "\n" + args[idx+1] + "\n"
			skipNext = true
		case "-F":
			messageFile = args[idx+1]
			skipNext = true

		}
	}
	if messageString == "" {
		if messageFile != "" {
			// TODO: READ commit message from messageFile here
		} else {
			// If neither messageString nor messageFile are set, read
			// from STDIN
		}
		panic("Must provide message with -m parameter to commit-tree")
	}
	if tree == "" {
		tree = args[len(args)-1]
	}
	// TODO: Validate tree id
	fmt.Fprintf(content, "tree %s\n", tree)
	for _, val := range parents {
		fmt.Fprintf(content, "parent %s\n", val)
	}

	author := getAuthor(repo)
	t := time.Now()
	_, tzoff := t.Zone()
	// for some reason t.Zone() returns the timezone offset in seconds
	// instead of hours, so convert it to an hour format string
	tzStr := fmt.Sprintf("%+03d00", tzoff/(60*60))
	fmt.Fprintf(content, "author %s %d %s\n", author, t.Unix(), tzStr)
	fmt.Fprintf(content, "committer %s %d %s\n", author, t.Unix(), tzStr)
	fmt.Fprintf(content, "%s", messageString)
	fmt.Printf("%s", content.Bytes())
	sha1, err := repo.StoreObjectLoose(libgit.ObjectCommit, bytes.NewReader(content.Bytes()))
	if err != nil {
		return ""
	}
	return fmt.Sprintf("%s", sha1)
}
Beispiel #12
0
func getHeadId(repo *libgit.Repository) (string, error) {
	if headBranch := getHeadBranch(repo); headBranch != "" {
		return repo.GetCommitIdOfBranch(getHeadBranch(repo))
	}
	return "", InvalidHead
}
Beispiel #13
0
// Adds a file to the index, without writing it to disk.
// To write it to disk after calling this, use GitIndex.WriteIndex
//
// This will do the following:
// write git object blob of file contents to .git/objects
// normalize os.File name to path relative to gitRoot
// search GitIndex for normalized name
//	if GitIndexEntry found
//		update GitIndexEntry to point to the new object blob
// 	else
// 		add new GitIndexEntry if not found
//
func (g *GitIndex) AddFile(repo *libgit.Repository, file *os.File) {

	sha1, err := repo.StoreObjectLoose(libgit.ObjectBlob, file)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error storing object: %s", err)
	}
	fmt.Printf("Sha1: %s\n", sha1)
	fmt.Printf("Name is %s\n", file.Name())
	name := getNormalizedName(file)
	for _, entry := range g.Objects {
		if entry.PathName == name {
			entry.Sha1 = sha1

			fstat, err := file.Stat()
			if err != nil {
				panic(err)
			}
			modTime := fstat.ModTime()
			entry.Mtime = uint32(modTime.Unix())
			entry.Mtimenano = uint32(modTime.Nanosecond())
			entry.Fsize = uint32(fstat.Size())
			return
		}
	}
	fstat, err := file.Stat()
	if err != nil {
		panic(err)
	}
	modTime := fstat.ModTime()
	stat := fstat.Sys().(*syscall.Stat_t)
	csec, cnano := stat.Ctim.Unix()

	var mode uint32
	if fstat.IsDir() {
		// This should really recursively call add for each file in the directory.
		panic("Add can't handle directories. yet.")
	} else {
		// if it's a regular file, just assume it's 0644 for now
		mode = 0x81A4

	}
	// mode is
	/*
	     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.

	   Flags is
	    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.

	*/
	//var flags uint16 = 0x8000 // start with "assume-valid" flag
	var flags uint16 = 0x8000 // start with "assume-valid" flag
	if len(name) >= 0x0FFF {
		flags |= 0x0FFF
	} else {
		flags |= (uint16(len(name)) & 0x0FFF)
	}

	g.Objects = append(g.Objects, &GitIndexEntry{
		fixedIndexEntry{
			uint32(csec),                 // right?
			uint32(cnano),                // right?
			uint32(modTime.Unix()),       // right
			uint32(modTime.Nanosecond()), // right
			uint32(stat.Dev),             // right
			uint32(stat.Ino),             // right
			mode,
			stat.Uid,             // right?
			stat.Gid,             // right?
			uint32(fstat.Size()), // right
			sha1,                 // this is right
			flags,                // right
		},
		name,
	})
	g.NumberIndexEntries += 1
	sort.Sort(ByPath(g.Objects))
	return
}