Пример #1
0
func (p *VCSDataProvider) Keys(keyPrefix string) ([]string, error) {
	keyPrefix = strings.TrimPrefix(keyPrefix, "/")
	keyPrefix = filepath.Clean(keyPrefix)
	if strings.HasPrefix(keyPrefix, "..") || strings.HasPrefix(keyPrefix, "/") {
		return nil, errors.New("invalid keyPrefix")
	}
	topDir := filepath.Join(p.conf.StorageDir, keyPrefix)

	var keys []string
	err := filepath.Walk(topDir, func(path string, info os.FileInfo, err error) error {
		// Ignore errors for broken symlinks.
		if err != nil {
			if info == nil {
				return err
			}
			if info.Mode()&os.ModeSymlink == 0 {
				return err
			}
		}

		if info.Mode().IsDir() {
			vcsTypes := []string{"git", "hg"}
			for _, vcsType := range vcsTypes {
				_, err := vcs.Open(vcsType, path)
				if err == nil {
					key, err := filepath.Rel(p.conf.StorageDir, path)
					if err != nil {
						return err
					}

					keys = append(keys, key)
					return filepath.SkipDir
				}
			}
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	return keys, nil
}
Пример #2
0
func main() {
	log.SetFlags(0)
	flag.Parse()

	if flag.NArg() == 0 {
		log.Fatal("Must specify a subcommand.")
	}

	if *chdir != "" {
		if err := os.Chdir(*chdir); err != nil {
			log.Fatal("Chdir:", err)
		}
	}
	cwd, err := os.Getwd()
	if err != nil {
		log.Fatalln("Getwd:", err)
	}

	subcmd := flag.Arg(0)
	args := flag.Args()[1:]
	switch subcmd {
	case "git-clone-mirror":
		if len(args) != 2 {
			log.Fatal("git-clone requires 2 args: <clone URL> <dir>.")
		}
		cloneURLStr, dir := args[0], args[1]

		cloneURL, err := url.Parse(cloneURLStr)
		if err != nil {
			log.Fatal(err)
		}

		if _, err := os.Stat(dir); !os.IsNotExist(err) {
			log.Fatalf("Clone destination dir must not exist: %s.", dir)
		}
		if _, err := os.Stat(filepath.Join(dir, "..")); err != nil {
			log.Fatalf("Clone destination dir parent must exist: %s.", filepath.Join(dir, ".."))
		}

		log.Printf("Cloning %s to %s...", cloneURL, dir)

		opt := vcs.CloneOpt{}
		if *sshKeyFile != "" {
			key, err := ioutil.ReadFile(*sshKeyFile)
			if err != nil {
				log.Fatal(err)
			}
			opt.RemoteOpts.SSH = &vcs.SSHConfig{PrivateKey: key}
		}
		repo, err := vcs.Clone("git", cloneURL.String(), dir, opt)
		if err != nil {
			log.Fatal(err)
		}

		log.Printf("Cloned: %T.", repo)

	case "update-everything":
		if len(args) != 1 {
			log.Fatal("update-everything takes 1 argument: <repo dir>.")
		}
		dir := args[0]

		cmd, dir, err := fromDir(dir)
		if err != nil {
			log.Fatalln("no supported vcs found:", err)
		}
		repo, err := vcs.Open(cmd, dir)
		if err != nil {
			log.Fatal(err)
		}

		getHEADCommit := func() *vcs.Commit {
			commitID, err := repo.ResolveRevision("HEAD")
			if err != nil {
				log.Fatal("Resolving HEAD revision:", err)
			}
			commit, err := repo.GetCommit(commitID)
			if err != nil {
				log.Fatal("GetCommit:", err)
			}
			return commit
		}

		preCommit := getHEADCommit()
		log.Printf("Before remote update, HEAD is %s (from %s ago).", preCommit.ID, preCommit.Author.Date)

		log.Printf("Remote-updating repo in dir %s...", dir)
		result, err := repo.(vcs.RemoteUpdater).UpdateEverything(vcs.RemoteOpts{})
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("Result is: %+v\n", result)

		postCommit := getHEADCommit()
		log.Printf("After remote update, HEAD is %s (from %s ago).", postCommit.ID, postCommit.Author.Date)

	case "show":
		if len(args) != 1 {
			log.Fatal("show takes 1 argument: <revspec>.")
		}
		revspec := args[0]

		cmd, dir, err := fromDir(cwd)
		if err != nil {
			log.Fatalln("no supported vcs found:", err)
		}
		repo, err := vcs.Open(cmd, dir)
		if err != nil {
			log.Fatal(err)
		}

		commitID, err := repo.ResolveRevision(revspec)
		if err != nil {
			log.Fatal(err)
		}

		commit, err := repo.GetCommit(commitID)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Revspec %q resolves to commit %s:\n", revspec, commitID)
		printCommit(commit)

	case "grep":
		if len(args) != 2 {
			log.Fatal("grep takes 2 arguments.")
		}

		cmd, dir, err := fromDir(cwd)
		if err != nil {
			log.Fatalln("no supported vcs found:", err)
		}
		repo, err := vcs.Open(cmd, dir)
		if err != nil {
			log.Fatal(err)
		}

		rev, err := repo.ResolveRevision(args[0])
		if err != nil {
			log.Fatal(err)
		}

		results, err := repo.(vcs.Searcher).Search(rev, vcs.SearchOptions{
			Query:        args[1],
			QueryType:    vcs.FixedQuery,
			ContextLines: 2,
			N:            5,
			Offset:       0,
		})
		if err != nil {
			log.Fatal(err)
		}

		log.Printf("# %d matches", len(results))
		log.Println()
		for _, res := range results {
			log.Printf("# %s:%d-%d", res.File, res.StartLine, res.EndLine)
			fmt.Println(string(res.Match))
		}

	case "show-file":
		if len(args) != 2 {
			log.Fatal("show-file takes 2 arguments: <commit> <path>.")
		}

		cmd, dir, err := fromDir(cwd)
		if err != nil {
			log.Fatalln("no supported vcs found:", err)
		}
		repo, err := vcs.Open(cmd, dir)
		if err != nil {
			log.Fatal(err)
		}

		rev, err := repo.ResolveRevision(args[0])
		if err != nil {
			log.Fatal(err)
		}

		fs, err := repo.FileSystem(rev)
		if err != nil {
			log.Fatal(err)
		}

		f, err := fs.Open(args[1])
		if err != nil {
			log.Fatal(err)
		}

		b, err := ioutil.ReadAll(f)
		if err != nil {
			log.Fatal(err)
		}
		if _, err := os.Stdout.Write(b); err != nil {
			log.Fatal(err)
		}

	case "read-dir":
		if len(args) != 2 {
			log.Fatal("read-dir takes 2 arguments: <commit> <path>.")
		}

		started := time.Now()

		cmd, dir, err := fromDir(cwd)
		if err != nil {
			log.Fatalln("no supported vcs found:", err)
		}
		repo, err := vcs.Open(cmd, dir)
		if err != nil {
			log.Fatal(err)
		}

		rev, err := repo.ResolveRevision(args[0])
		if err != nil {
			log.Fatal(err)
		}

		fs, err := repo.FileSystem(rev)
		if err != nil {
			log.Fatal(err)
		}

		startedReadDir := time.Now()
		fis, err := fs.ReadDir(args[1])
		readDirTaken := time.Since(startedReadDir)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# ReadDir (%d total):\n", len(fis))
		for _, fi := range fis {
			fmt.Println(fi.ModTime().Local(), fi.Name())
		}

		fmt.Println("fs.ReadDir() taken:", readDirTaken)
		fmt.Println("read-dir taken:", time.Since(started))

	case "log":
		if len(args) != 0 {
			log.Fatal("log takes no arguments.")
		}

		cmd, dir, err := fromDir(cwd)
		if err != nil {
			log.Fatalln("no supported vcs found:", err)
		}
		repo, err := vcs.Open(cmd, dir)
		if err != nil {
			log.Fatal(err)
		}

		commitID, err := repo.ResolveRevision("HEAD")
		if err != nil {
			log.Fatal(err)
		}

		commits, total, err := repo.Commits(vcs.CommitsOptions{Head: commitID, N: 250})
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Commits (%d total):\n", total)
		for _, c := range commits {
			printCommit(c)
		}

	case "blame":
		newestCommit, file := args[0], args[1]

		cmd, dir, err := fromDir(cwd)
		if err != nil {
			log.Fatalln("no supported vcs found:", err)
		}
		repo, err := vcs.Open(cmd, dir)
		if err != nil {
			log.Fatal(err)
		}

		newestCommitID, err := repo.ResolveRevision(newestCommit)
		if err != nil {
			log.Fatal(err)
		}

		hunks, err := repo.(vcs.Blamer).BlameFile(file, &vcs.BlameOptions{NewestCommit: newestCommitID})
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Hunks (%d total):\n", len(hunks))
		for _, c := range hunks {
			printHunk(c)
		}

	case "diff", "diffstat":
		if len(args) != 2 {
			log.Fatalf("%s takes 2 args (base and head), behavior is like `git diff base...head` (note triple dot).", subcmd)
		}
		baseRev := args[0]
		headRev := args[1]

		cmd, dir, err := fromDir(cwd)
		if err != nil {
			log.Fatalln("no supported vcs found:", err)
		}
		repo, err := vcs.Open(cmd, dir)
		if err != nil {
			log.Fatal(err)
		}

		base, err := repo.ResolveRevision(baseRev)
		if err != nil {
			log.Fatal(err)
		}
		head, err := repo.ResolveRevision(headRev)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("git diff %s..%s", base, head)

		vdiff, err := repo.(vcs.Differ).Diff(base, head, &vcs.DiffOptions{ExcludeReachableFromBoth: true})
		if err != nil {
			log.Fatal(err)
		}

		switch subcmd {
		case "diff":
			fmt.Println(vdiff.Raw)
		case "diffstat":
			pdiff, err := diff.ParseMultiFileDiff([]byte(vdiff.Raw))
			if err != nil {
				log.Fatal(err)
			}
			for _, fdiff := range pdiff {
				var name string
				if fdiff.NewName == "/dev/null" {
					name = fdiff.OrigName
				} else {
					name = fdiff.NewName
				}
				fmt.Printf("%-50s    ", name)
				st := fdiff.Stat()
				const w = 30
				total := st.Added + st.Changed + st.Deleted
				if st.Added > 0 {
					st.Added = (st.Added*w)/total + 1
				}
				if st.Changed > 0 {
					st.Changed = (st.Changed*w)/total + 1
				}
				if st.Deleted > 0 {
					st.Deleted = (st.Deleted*w)/total + 1
				}
				fmt.Print(strings.Repeat("+", int(st.Added)), strings.Repeat("Δ", int(st.Changed)), strings.Repeat("-", int(st.Deleted)), "\n")
			}
		}

	case "branches":
		if len(args) > 1 {
			log.Fatal("branches takes 0 or 1 arguments: [<behind-ahead-branch>].")
		}

		cmd, dir, err := fromDir(cwd)
		if err != nil {
			log.Fatalln("no supported vcs found:", err)
		}
		repo, err := vcs.Open(cmd, dir)
		if err != nil {
			log.Fatal(err)
		}

		var opt vcs.BranchesOptions
		if len(args) == 1 {
			opt.BehindAheadBranch = args[0]
		}
		branches, err := repo.Branches(opt)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Branches (%d total):\n", len(branches))
		for _, b := range branches {
			switch {
			case b.Counts == nil:
				fmt.Printf("%s %s\n", b.Head, b.Name)
			case b.Counts != nil:
				fmt.Printf("-%v | +%v | %s\n", b.Counts.Behind, b.Counts.Ahead, b.Name)
			}
		}

	case "tags":
		if len(args) != 0 {
			log.Fatal("tags takes no arguments.")
		}

		cmd, dir, err := fromDir(cwd)
		if err != nil {
			log.Fatalln("no supported vcs found:", err)
		}
		repo, err := vcs.Open(cmd, dir)
		if err != nil {
			log.Fatal(err)
		}

		tags, err := repo.Tags()
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Tags (%d total):\n", len(tags))
		for _, t := range tags {
			fmt.Printf("%s %s\n", t.CommitID, t.Name)
		}

	case "history":
		if len(args) != 1 {
			log.Fatal("history takes 1 argument.")
		}
		path := args[0]

		cmd, dir, err := fromDir(cwd)
		if err != nil {
			log.Fatalln("no supported vcs found:", err)
		}
		repo, err := vcs.Open(cmd, dir)
		if err != nil {
			log.Fatal(err)
		}

		commitID, err := repo.ResolveRevision("HEAD")
		if err != nil {
			log.Fatal(err)
		}

		commits, total, err := repo.Commits(vcs.CommitsOptions{Head: commitID, N: 10, Path: path})
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# History (%d total):\n", total)
		for _, c := range commits {
			printCommit(c)
		}

	case "committers":
		if len(args) > 2 {
			log.Fatal("committers takes at most 2 arguments.")
		}

		var opt vcs.CommittersOptions
		if len(args) > 0 {
			opt.Rev = args[0]
		}
		if len(args) > 1 {
			var err error
			opt.N, err = strconv.Atoi(args[1])
			if err != nil {
				log.Fatal(err)
			}
		}

		cmd, dir, err := fromDir(cwd)
		if err != nil {
			log.Fatalln("no supported vcs found:", err)
		}
		repo, err := vcs.Open(cmd, dir)
		if err != nil {
			log.Fatal(err)
		}

		committers, err := repo.Committers(opt)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Committers (%d total):\n", len(committers))
		for _, c := range committers {
			fmt.Printf("%s <%s> has %v commits\n", c.Name, c.Email, c.Commits)
		}

	case "file-lister":
		if len(args) != 1 {
			log.Fatal("history takes 1 argument: <commit>.")
		}

		cmd, dir, err := fromDir(cwd)
		if err != nil {
			log.Fatalln("no supported vcs found:", err)
		}
		repo, err := vcs.Open(cmd, dir)
		if err != nil {
			log.Fatal(err)
		}

		fileLister, ok := repo.(vcs.FileLister)
		if !ok {
			log.Println("repo is not a FileLister")
			break
		}

		rev, err := repo.ResolveRevision(args[0])
		if err != nil {
			log.Fatal(err)
		}

		files, err := fileLister.ListFiles(rev)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("# Files (%d total):\n", len(files))
		for _, f := range files {
			fmt.Printf("%q\n", f)
		}
	default:
		log.Fatalln("unrecognized subcmd:", subcmd)
	}
}
Пример #3
0
func (dir *DirSpec) buildContext() (*build.Context, error) {
	if dir.ctx != nil {
		return dir.ctx, nil
	}

	ctx := build.Default // copy

	if dir.VCS != "" && dir.Revision != "" {
		if dir.root == "" {
			cmd := exec.Command("git", "rev-parse", "--show-toplevel")
			cmd.Dir = dir.Path

			out, err := cmd.Output()
			if err != nil {
				return nil, err
			}

			dir.root = strings.TrimRight(string(out), "\n")
		}

		repo, err := vcs.Open(dir.VCS, dir.root)
		if err != nil {
			return nil, err
		}

		commit, err := repo.ResolveRevision(dir.Revision)
		if err != nil {
			return nil, err
		}

		fs, err := repo.FileSystem(commit)
		if err != nil {
			return nil, err
		}

		ctx.IsDir = func(path string) bool {
			if buildutil.IsAbsPath(&ctx, path) {
				if strings.HasPrefix(path, dir.root) {
					var err error
					path, err = filepath.Rel(dir.root, path)
					if err != nil {
						return false
					}
				} else {
					fi, err := os.Stat(path)
					return err == nil && fi.IsDir()
				}
			}

			fi, err := fs.Stat(path)
			return err == nil && fi.IsDir()
		}

		ctx.OpenFile = func(path string) (io.ReadCloser, error) {
			if buildutil.IsAbsPath(&ctx, path) {
				// the path maybe outside of repository (for standard libraries)
				if strings.HasPrefix(path, dir.root) {
					var err error
					path, err = filepath.Rel(dir.root, path)
					if err != nil {
						return nil, err
					}
				} else {
					return os.Open(path)
				}
			}

			return fs.Open(path)
		}

		ctx.ReadDir = func(path string) ([]os.FileInfo, error) {
			if filepath.IsAbs(path) {
				if strings.HasPrefix(path, dir.root) {
					var err error
					path, err = filepath.Rel(dir.root, path)
					if err != nil {
						return nil, err
					}
				} else {
					return ioutil.ReadDir(path)
				}
			}

			return fs.ReadDir(path)
		}
	}

	dir.ctx = &ctx

	return dir.ctx, nil
}