// send produces a binary diff stream and passes it to cont func send(repo, commit string, cont func(io.Reader) error) error { parent := GetMeta(path.Join(repo, commit), "parent") if parent == "" { return util.CallCont(exec.Command("btrfs", "send", FilePath(path.Join(repo, commit))), cont) } else { return util.CallCont(exec.Command("btrfs", "send", "-p", FilePath(path.Join(repo, parent)), FilePath(path.Join(repo, commit))), cont) } }
// transid returns transid of a path in a repo. This function is used in // several other internal functions. func transid(repo, commit string) (string, error) { // "9223372036854775810" == 2 ** 63 we use a very big number there so that // we get the transid of the from path. According to the internet this is // the nicest way to get it from btrfs. var transid string c := exec.Command("btrfs", "subvolume", "find-new", FilePath(path.Join(repo, commit)), "9223372036854775808") err := util.CallCont(c, func(r io.Reader) error { scanner := bufio.NewScanner(r) for scanner.Scan() { // scanner.Text() looks like this: // transid marker was 907 // 0 1 2 3 tokens := strings.Split(scanner.Text(), " ") if len(tokens) != 4 { return fmt.Errorf("Failed to parse find-new output.") } // We want to increment the transid because it's inclusive, if we // don't increment we'll get things from the previous commit as // well. transid = tokens[3] } return scanner.Err() }) if err != nil { return "", err } return transid, err }
// FindNew returns an array of filenames that were created or modified between `from` and `to` func FindNew(repo, from, to string) ([]string, error) { var files []string t, err := transid(repo, from) if err != nil { return files, err } c := exec.Command("btrfs", "subvolume", "find-new", FilePath(path.Join(repo, to)), t) err = util.CallCont(c, func(r io.Reader) error { scanner := bufio.NewScanner(r) for scanner.Scan() { log.Print(scanner.Text()) // scanner.Text() looks like this: // inode 6683 file offset 0 len 107 disk start 0 offset 0 gen 909 flags INLINE jobs/rPqZxsaspy // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 tokens := strings.Split(scanner.Text(), " ") // Make sure the line is parseable as a file and the path isn't hidden. if len(tokens) == 17 { if !strings.HasPrefix(tokens[16], ".") { // check if it's a hidden file files = append(files, tokens[16]) } } else if len(tokens) == 4 { continue //skip transid messages } else { return fmt.Errorf("Failed to parse find-new output.") } } return scanner.Err() }) return files, err }
//_log returns all of the commits the repo which have generation >= from. func _log(repo, from string, order int, cont func(io.Reader) error) error { var sort string if order == Desc { sort = "-ogen" } else { sort = "+ogen" } if from == "" { c := exec.Command("btrfs", "subvolume", "list", "-o", "-c", "-u", "-q", "--sort", sort, FilePath(path.Join(repo))) return util.CallCont(c, cont) } else { t, err := transid(repo, from) if err != nil { return err } c := exec.Command("btrfs", "subvolume", "list", "-o", "-c", "-u", "-q", "-C", "+"+t, "--sort", sort, FilePath(path.Join(repo))) return util.CallCont(c, cont) } }
// IsCommit returns true if the volume is a commit and false if it's a branch. func IsCommit(name string) (bool, error) { var res bool // "-t s" indicates to btrfs that this is a subvolume without the "-t s" // btrfs will still output what we want, but it will have a nonzero return // code err := util.CallCont(exec.Command("btrfs", "property", "get", "-t", "s", FilePath(name)), func(r io.Reader) error { scanner := bufio.NewScanner(r) for scanner.Scan() { if strings.Contains(scanner.Text(), "ro=true") { res = true return nil } } return scanner.Err() }) return res, err }