Example #1
0
// Revert implements got revert
func Revert(c *cli.Context) {
	// Open the database
	db := util.OpenDB()
	defer db.Close()

	// Perform operations in a read-only lock
	err := db.View(func(tx *bolt.Tx) error {
		// Get the current commit sha
		info := tx.Bucket(util.INFO)
		objects := tx.Bucket(util.OBJECTS)
		current := info.Get(util.CURRENT)

		// Get the commit object and associated tree
		commit := types.DeserializeCommitObject(objects.Get(current))
		tree := types.DeserializeTreeObject(objects.Get(commit.Tree))

		for _, file := range c.Args() {
			fmt.Println("Reverting " + file + "...")
			fileData := TreeLookup(objects, tree, path.Clean(file))
			ioutil.WriteFile(file, fileData, 0644)
		}

		return nil
	})

	if err != nil {
		log.Fatal("Error reading from the database.")
	}
}
Example #2
0
// Status implements the `got status` command.
func Status(c *cli.Context) {
	// Open the database
	db := util.OpenDB()
	defer db.Close()

	// Colored output
	yellow := ansi.ColorFunc("yellow+h:black")
	green := ansi.ColorFunc("green+h:black")
	red := ansi.ColorFunc("red+h:black")

	// Perform operations in a read-only lock
	err := db.View(func(tx *bolt.Tx) error {
		// Get the current commit sha
		info := tx.Bucket(util.INFO)
		objects := tx.Bucket(util.OBJECTS)
		current := info.Get(util.CURRENT)

		// Find the differences between the working directory and the tree of the current commit.
		differences := []Difference{}
		if current != nil {
			// Load commit object
			commit := types.DeserializeCommitObject(objects.Get(current))
			util.DebugLog("Comparing working directory to commit '" + commit.Message + "'.")
			differences = TreeDiff(objects, commit.Tree, ".")
		} else {
			// Compare directory to the empty hash
			util.DebugLog("Comparing working directory to empty tree.")
			differences = TreeDiff(objects, types.EMPTY, ".")
		}

		// Print out the found differences
		for _, difference := range differences {
			line := fmt.Sprintf("%s %s", difference.Type, difference.FilePath)
			if difference.Type == "A" {
				fmt.Println(green(line))
			}
			if difference.Type == "R" {
				fmt.Println(red(line))
			}
			if difference.Type == "M" {
				fmt.Println(yellow(line))
			}
		}

		return nil
	})

	if err != nil {
		log.Fatal("Error reading from the database.")
	}
}
Example #3
0
func Log(c *cli.Context) {
	db := util.OpenDB()
	defer db.Close()

	yellow := ansi.ColorFunc("yellow+h:black")

	// Perform operations in a read-only lock
	db.View(func(tx *bolt.Tx) error {
		info := tx.Bucket(util.INFO)
		objects := tx.Bucket(util.OBJECTS)
		headsBytes := info.Get(util.HEADS)

		if headsBytes != nil {
			queue := types.DeserializeHashes(headsBytes)

			// TODO: Keep a visited set, so we don't repeat commits

			for len(queue) > 0 {
				i := GetNewestCommit(objects, queue)

				// Get commit and remove it from the priority queue
				commitSha := queue[i]
				commit := types.DeserializeCommitObject(objects.Get(commitSha))
				queue = append(queue[:i], queue[i+1:]...)

				fmt.Printf(yellow("commit %s\n"), commitSha)
				fmt.Printf("Message: %s\n", commit.Message)
				fmt.Printf("Author: %s\n", commit.Author)
				fmt.Printf("Date: %s\n", commit.Time)

				if len(commit.Parents) > 0 {
					fmt.Printf("Parents: %s\n", commit.Parents)
				}

				fmt.Printf("Tree: %s\n", commit.Tree)
				fmt.Println()

				// Append parents of this commit to the queue
				queue = append(queue, commit.Parents...)
			}

			return nil
		}

		fmt.Println("There are no commits in this repository...")
		return nil
	})
}
Example #4
0
// Application entry point
func main() {
	app := cli.NewApp()
	app.Name = "got"
	app.Usage = "A VCS written in golang."

	app.Commands = []cli.Command{
		{
			Name:  "init",
			Usage: "Create an empty got repository in the current directory.",
			Action: func(c *cli.Context) {
				if _, err := os.Stat(util.DBName); err == nil {
					fmt.Printf("Got repository already exists in this folder.\n")
				} else {
					db, err := bolt.Open(util.DBName, 0600, nil)
					if err != nil {
						log.Fatal("Could not create " + util.DBName + "\n")
					}

					db.Update(func(tx *bolt.Tx) error {
						tx.CreateBucket(util.INFO)
						tx.CreateBucket(util.OBJECTS)
						return nil
					})

					defer db.Close()
				}
			},
		},
		{
			Name:   "log",
			Usage:  "Show commit logs.",
			Action: commands.Log,
		},
		{
			Name:   "status",
			Usage:  "Diff the current working directory with the last commit.",
			Action: commands.Status,
		},
		{
			Name:  "commit",
			Usage: "Record changes to the repository.",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "message, m",
					Usage: "Commit message",
				},
				cli.StringFlag{
					Name:  "author, a",
					Usage: "Commit author",
				},
			},
			Action: commands.Commit,
		},
		{
			Name:  "checkout",
			Usage: "Checkout a the working tree to a commit.",
			Action: func(c *cli.Context) {
				db := util.OpenDB()
				defer db.Close()
			},
		},
		{
			Name:   "revert",
			Usage:  "Revert a file to the last commit.",
			Action: commands.Revert,
		},
	}

	app.Run(os.Args)
}
Example #5
0
// Commit implements `got commit`
func Commit(c *cli.Context) {
	db := util.OpenDB()
	defer db.Close()

	if len(c.String("message")) == 0 {
		log.Fatalln("Must supply a commit message.")
	}

	if len(c.String("author")) == 0 {
		log.Fatalln("Must supply a commit author.")
	}

	// Perform operations in a write lock
	err := db.Update(func(tx *bolt.Tx) error {
		info := tx.Bucket(util.INFO)       // The bucket holding repositry metadata
		objects := tx.Bucket(util.OBJECTS) // The bucket holding got objects
		current := info.Get(util.CURRENT)

		// Create a commit object from the current directory snapshot
		util.DebugLog("Taking snapshot of repo...")
		hash := TakeSnapshot(tx.Bucket(util.OBJECTS), ".")
		util.DebugLog("Repo snapshot has hash " + hash.String() + ".")

		commit := types.CommitObject{
			Author:  c.String("author"),
			Message: c.String("message"),
			Time:    time.Now(),
			Tree:    hash,
			Parents: make([]types.Hash, 0),
		}

		// There is a 'current' commit, then it is the parent of the new commit
		if current != nil {
			commit.Parents = append(commit.Parents, current)
		}

		commitBytes := commit.Serialize()
		commitSha := types.CalculateHash(commitBytes)
		objects.Put(commitSha, commitBytes)
		util.DebugLog("Created commit of sha " + commitSha.String() + ".")

		// CURRENT now corresponds to the commit that we just created
		info.Put(util.CURRENT, commitSha)

		// Update heads
		headsBytes := info.Get(util.HEADS)
		if headsBytes != nil {
			heads := types.DeserializeHashes(headsBytes)
			util.DebugLog("Currently " + strconv.Itoa(len(heads)) + " heads in this repo...")

			// Remove current, if it is a head
			for i, head := range heads {
				if head.Equal(current) {
					heads = append(heads[:i], heads[i+1:]...)
					break
				}
			}

			// Add the new commit as a head
			heads = append(heads, commitSha)

			info.Put(util.HEADS, types.SerializeHashes(heads))
		} else {
			util.DebugLog("Currently no heads in this repo. Putting HEADS: " + fmt.Sprint(([]types.Hash{commitSha})))
			info.Put(util.HEADS, types.SerializeHashes([]types.Hash{commitSha}))
		}

		return nil
	})
	if err != nil {
		log.Fatal("Error reading from the database.")
	}
}