Exemple #1
0
func (r *Repository) Commits(opt vcs.CommitsOptions) ([]*vcs.Commit, uint, error) {
	r.editLock.RLock()
	defer r.editLock.RUnlock()

	walk, err := r.u.Walk()
	if err != nil {
		return nil, 0, err
	}
	defer walk.Free()

	walk.Sorting(git2go.SortTime)

	oid, err := git2go.NewOid(string(opt.Head))
	if err != nil {
		return nil, 0, err
	}
	if err := walk.Push(oid); err != nil {
		if git2go.IsErrorCode(err, git2go.ErrNotFound) {
			return nil, 0, vcs.ErrCommitNotFound
		}
		return nil, 0, err
	}

	if opt.Base != "" {
		baseOID, err := git2go.NewOid(string(opt.Base))
		if err != nil {
			return nil, 0, err
		}
		if err := walk.Hide(baseOID); err != nil {
			if git2go.IsErrorCode(err, git2go.ErrNotFound) {
				return nil, 0, vcs.ErrCommitNotFound
			}
			return nil, 0, err
		}
	}

	var commits []*vcs.Commit
	total := uint(0)
	err = walk.Iterate(func(c *git2go.Commit) bool {
		if total >= opt.Skip && (opt.N == 0 || uint(len(commits)) < opt.N) {
			commits = append(commits, r.makeCommit(c))
		}
		total++
		// If we want total, keep going until the end.
		if !opt.NoTotal {
			return true
		}
		// Otherwise return once N has been satisfied.
		return (opt.N == 0 || uint(len(commits)) < opt.N)
	})
	if err != nil {
		return nil, 0, err
	}
	if opt.NoTotal {
		total = 0
	}

	return commits, total, nil
}
Exemple #2
0
func (r *Repository) Commits(opt vcs.CommitsOptions) ([]*vcs.Commit, uint, error) {
	r.editLock.RLock()
	defer r.editLock.RUnlock()

	walk, err := r.u.Walk()
	if err != nil {
		return nil, 0, err
	}
	defer walk.Free()

	walk.Sorting(git2go.SortTopological)

	oid, err := git2go.NewOid(string(opt.Head))
	if err != nil {
		return nil, 0, err
	}
	if err := walk.Push(oid); err != nil {
		if git2go.IsErrorCode(err, git2go.ErrNotFound) {
			return nil, 0, vcs.ErrCommitNotFound
		}
		return nil, 0, err
	}

	if opt.Base != "" {
		baseOID, err := git2go.NewOid(string(opt.Base))
		if err != nil {
			return nil, 0, err
		}
		if err := walk.Hide(baseOID); err != nil {
			if git2go.IsErrorCode(err, git2go.ErrNotFound) {
				return nil, 0, vcs.ErrCommitNotFound
			}
			return nil, 0, err
		}
	}

	var commits []*vcs.Commit
	total := uint(0)
	err = walk.Iterate(func(c *git2go.Commit) bool {
		if total >= opt.Skip && (opt.N == 0 || uint(len(commits)) < opt.N) {
			commits = append(commits, r.makeCommit(c))
		}
		total++
		return total < uint(MaxCommits)
	})
	if err != nil {
		return nil, 0, err
	}

	return commits, total, nil
}
Exemple #3
0
// mergeBaseHoldingEditLock performs a merge-base. Callers must hold
// the r.editLock (either as a reader or writer).
func (r *Repository) mergeBaseHoldingEditLock(a, b vcs.CommitID) (vcs.CommitID, error) {
	ao, err := git2go.NewOid(string(a))
	if err != nil {
		return "", err
	}
	bo, err := git2go.NewOid(string(b))
	if err != nil {
		return "", err
	}
	mb, err := r.u.MergeBase(ao, bo)
	if err != nil {
		return "", err
	}
	return vcs.CommitID(mb.String()), nil
}
Exemple #4
0
// getCommit finds and returns the raw git2go Commit. The caller is
// responsible for freeing it (c.Free()).
func (r *Repository) getCommit(id vcs.CommitID) (*git2go.Commit, error) {
	oid, err := git2go.NewOid(string(id))
	if err != nil {
		return nil, err
	}

	c, err := r.u.LookupCommit(oid)
	if err != nil {
		if git2go.IsErrorCode(err, git2go.ErrNotFound) {
			return nil, vcs.ErrCommitNotFound
		}
		return nil, err
	}
	return c, nil
}
Exemple #5
0
func (r *Repository) BlameFile(path string, opt *vcs.BlameOptions) ([]*vcs.Hunk, error) {
	r.editLock.RLock()
	defer r.editLock.RUnlock()

	gopt := git2go.BlameOptions{}
	if opt != nil {
		var err error
		if opt.NewestCommit != "" {
			gopt.NewestCommit, err = git2go.NewOid(string(opt.NewestCommit))
			if err != nil {
				return nil, err
			}
		}
		if opt.OldestCommit != "" {
			gopt.OldestCommit, err = git2go.NewOid(string(opt.OldestCommit))
			if err != nil {
				return nil, err
			}
		}
		gopt.MinLine = uint32(opt.StartLine)
		gopt.MaxLine = uint32(opt.EndLine)
	}

	blame, err := r.u.BlameFile(path, &gopt)
	if err != nil {
		return nil, err
	}
	defer blame.Free()

	// Read file contents so we can set hunk byte start and end.
	fs, err := r.FileSystem(vcs.CommitID(gopt.NewestCommit.String()))
	if err != nil {
		return nil, err
	}
	b, err := fs.(*gitFSLibGit2).readFileBytes(path)
	if err != nil {
		return nil, err
	}
	lines := bytes.SplitAfter(b, []byte{'\n'})

	byteOffset := 0
	hunks := make([]*vcs.Hunk, blame.HunkCount())
	for i := 0; i < len(hunks); i++ {
		hunk, err := blame.HunkByIndex(i)
		if err != nil {
			return nil, err
		}

		hunkBytes := 0
		for j := uint16(0); j < hunk.LinesInHunk; j++ {
			hunkBytes += len(lines[j])
		}
		endByteOffset := byteOffset + hunkBytes

		hunks[i] = &vcs.Hunk{
			StartLine: int(hunk.FinalStartLineNumber),
			EndLine:   int(hunk.FinalStartLineNumber + hunk.LinesInHunk),
			StartByte: byteOffset,
			EndByte:   endByteOffset,
			CommitID:  vcs.CommitID(hunk.FinalCommitId.String()),
			Author: vcs.Signature{
				Name:  hunk.FinalSignature.Name,
				Email: hunk.FinalSignature.Email,
				Date:  pbtypes.NewTimestamp(hunk.FinalSignature.When.In(time.UTC)),
			},
		}
		byteOffset = endByteOffset
		lines = lines[hunk.LinesInHunk:]
	}

	return hunks, nil
}
Exemple #6
0
func (input *GraderInput) createArchiveFromGit(archivePath string) (int64, error) {
	repository, err := git.OpenRepository(input.repositoryPath)
	if err != nil {
		return 0, err
	}
	defer repository.Free()

	treeOid, err := git.NewOid(input.Hash())
	if err != nil {
		return 0, err
	}

	tree, err := repository.LookupTree(treeOid)
	if err != nil {
		return 0, err
	}
	defer tree.Free()
	odb, err := repository.Odb()
	if err != nil {
		return 0, err
	}
	defer odb.Free()

	tmpFd, err := os.Create(archivePath)
	if err != nil {
		return 0, err
	}
	defer tmpFd.Close()

	gz := gzip.NewWriter(tmpFd)
	defer gz.Close()

	archive := tar.NewWriter(gz)
	defer archive.Close()

	var walkErr error = nil
	foundSettings := false
	var uncompressedSize int64 = 0
	tree.Walk(func(parent string, entry *git.TreeEntry) int {
		entryPath := path.Join(parent, entry.Name)
		if entryPath == "settings.json" {
			foundSettings = true
		}
		switch entry.Type {
		case git.ObjectTree:
			hdr := &tar.Header{
				Name:     entryPath,
				Typeflag: tar.TypeDir,
				Mode:     0755,
				Size:     0,
			}
			if walkErr = archive.WriteHeader(hdr); walkErr != nil {
				return -1
			}
		case git.ObjectBlob:
			blob, walkErr := repository.LookupBlob(entry.Id)
			if walkErr != nil {
				return -1
			}
			defer blob.Free()

			hdr := &tar.Header{
				Name:     entryPath,
				Typeflag: tar.TypeReg,
				Mode:     0644,
				Size:     blob.Size(),
			}
			uncompressedSize += blob.Size()
			if walkErr = archive.WriteHeader(hdr); walkErr != nil {
				return -1
			}

			stream, err := odb.NewReadStream(entry.Id)
			if err == nil {
				defer stream.Free()
				if _, walkErr := io.Copy(archive, stream); walkErr != nil {
					return -1
				}
			} else {
				// That particular object cannot be streamed. Allocate the blob in
				// memory and write it to the archive.
				if _, walkErr := archive.Write(blob.Contents()); walkErr != nil {
					return -1
				}
			}
		}
		return 0
	})

	if walkErr != nil {
		return 0, walkErr
	}
	if !foundSettings {
		return 0, fmt.Errorf(
			"Could not find `settings.json` in %s:%s",
			input.repositoryPath,
			input.Hash(),
		)
	}
	return uncompressedSize, nil
}
Exemple #7
0
func (input *v1CompatGraderInput) createArchiveFromGit(
	archivePath string,
) (int64, error) {
	err := input.db.QueryRow(
		`SELECT
			extra_wall_time, memory_limit, output_limit, overall_wall_time_limit,
			time_limit, validator_time_limit, slow, validator
		FROM
			Problems
		WHERE
			alias = ?;`, input.problemName).Scan(
		&input.Settings().Limits.ExtraWallTime,
		&input.Settings().Limits.MemoryLimit,
		&input.Settings().Limits.OutputLimit,
		&input.Settings().Limits.OverallWallTimeLimit,
		&input.Settings().Limits.TimeLimit,
		&input.Settings().Limits.ValidatorTimeLimit,
		&input.Settings().Slow,
		&input.Settings().Validator.Name,
	)
	if err != nil {
		return 0, err
	}
	input.Settings().Limits.MemoryLimit *= 1024
	if input.Settings().Validator.Name == "token-numeric" {
		tolerance := 1e-6
		input.Settings().Validator.Tolerance = &tolerance
	}

	repository, err := git.OpenRepository(input.repositoryPath)
	if err != nil {
		return 0, err
	}
	defer repository.Free()

	treeOid, err := git.NewOid(input.Hash())
	if err != nil {
		return 0, err
	}

	tree, err := repository.LookupTree(treeOid)
	if err != nil {
		return 0, err
	}
	defer tree.Free()
	odb, err := repository.Odb()
	if err != nil {
		return 0, err
	}
	defer odb.Free()

	tmpFd, err := os.Create(archivePath)
	if err != nil {
		return 0, err
	}
	defer tmpFd.Close()

	gz := gzip.NewWriter(tmpFd)
	defer gz.Close()

	archive := tar.NewWriter(gz)
	defer archive.Close()

	// TODO(lhchavez): Support libinteractive.
	var walkErr error = nil
	var uncompressedSize int64 = 0
	rawCaseWeights := make(map[string]float64)
	tree.Walk(func(parent string, entry *git.TreeEntry) int {
		untrimmedPath := path.Join(parent, entry.Name)
		if untrimmedPath == "testplan" && entry.Type == git.ObjectBlob {
			blob, walkErr := repository.LookupBlob(entry.Id)
			if walkErr != nil {
				return -1
			}
			defer blob.Free()
			testplanRe := regexp.MustCompile(`^\s*([^# \t]+)\s+([0-9.]+).*$`)
			for _, line := range strings.Split(string(blob.Contents()), "\n") {
				m := testplanRe.FindStringSubmatch(line)
				if m == nil {
					continue
				}
				rawCaseWeights[m[1]], walkErr = strconv.ParseFloat(m[2], 64)
				if walkErr != nil {
					return -1
				}
			}
		}
		if strings.HasPrefix(untrimmedPath, "validator.") &&
			input.Settings().Validator.Name == "custom" &&
			entry.Type == git.ObjectBlob {
			lang := filepath.Ext(untrimmedPath)
			input.Settings().Validator.Lang = &lang
			blob, walkErr := repository.LookupBlob(entry.Id)
			if walkErr != nil {
				return -1
			}
			defer blob.Free()
			hdr := &tar.Header{
				Name:     untrimmedPath,
				Typeflag: tar.TypeReg,
				Mode:     0644,
				Size:     blob.Size(),
			}
			uncompressedSize += blob.Size()
			if walkErr = archive.WriteHeader(hdr); walkErr != nil {
				return -1
			}
			if _, walkErr := archive.Write(blob.Contents()); walkErr != nil {
				return -1
			}
		}
		if !strings.HasPrefix(untrimmedPath, "cases/") {
			return 0
		}
		entryPath := strings.TrimPrefix(untrimmedPath, "cases/")
		if strings.HasPrefix(entryPath, "in/") {
			caseName := strings.TrimSuffix(strings.TrimPrefix(entryPath, "in/"), ".in")
			if _, ok := rawCaseWeights[caseName]; !ok {
				rawCaseWeights[caseName] = 1.0
			}
		}
		switch entry.Type {
		case git.ObjectTree:
			hdr := &tar.Header{
				Name:     entryPath,
				Typeflag: tar.TypeDir,
				Mode:     0755,
				Size:     0,
			}
			if walkErr = archive.WriteHeader(hdr); walkErr != nil {
				return -1
			}
		case git.ObjectBlob:
			blob, walkErr := repository.LookupBlob(entry.Id)
			if walkErr != nil {
				return -1
			}
			defer blob.Free()

			hdr := &tar.Header{
				Name:     entryPath,
				Typeflag: tar.TypeReg,
				Mode:     0644,
				Size:     blob.Size(),
			}
			uncompressedSize += blob.Size()
			if walkErr = archive.WriteHeader(hdr); walkErr != nil {
				return -1
			}

			stream, err := odb.NewReadStream(entry.Id)
			if err == nil {
				defer stream.Free()
				if _, walkErr := io.Copy(archive, stream); walkErr != nil {
					return -1
				}
			} else {
				// That particular object cannot be streamed. Allocate the blob in
				// memory and write it to the archive.
				if _, walkErr := archive.Write(blob.Contents()); walkErr != nil {
					return -1
				}
			}
		}
		return 0
	})
	if walkErr != nil {
		return 0, walkErr
	}

	// Generate the group/case settings.
	cases := make(map[string][]common.CaseSettings)
	groupWeights := make(map[string]float64)
	totalWeight := 0.0
	for _, weight := range rawCaseWeights {
		totalWeight += weight
	}
	for caseName, weight := range rawCaseWeights {
		components := strings.SplitN(caseName, ".", 2)
		groupName := components[0]
		if _, ok := groupWeights[groupName]; !ok {
			groupWeights[groupName] = 0
		}
		groupWeights[groupName] += weight / totalWeight
		if _, ok := cases[groupName]; !ok {
			cases[groupName] = make([]common.CaseSettings, 0)
		}
		cases[groupName] = append(cases[groupName], common.CaseSettings{
			Name:   caseName,
			Weight: weight / totalWeight,
		})
	}
	input.Settings().Cases = make([]common.GroupSettings, 0)
	for groupName, cases := range cases {
		sort.Sort(common.ByCaseName(cases))
		input.Settings().Cases = append(input.Settings().Cases, common.GroupSettings{
			Cases:  cases,
			Name:   groupName,
			Weight: groupWeights[groupName],
		})
	}
	sort.Sort(common.ByGroupName(input.Settings().Cases))

	// Finally, write settings.json.
	settingsBlob, err := json.MarshalIndent(input.Settings(), "", "  ")
	if err != nil {
		return 0, err
	}
	hdr := &tar.Header{
		Name:     "settings.json",
		Typeflag: tar.TypeReg,
		Mode:     0644,
		Size:     int64(len(settingsBlob)),
	}
	uncompressedSize += int64(len(settingsBlob))
	if err = archive.WriteHeader(hdr); err != nil {
		return 0, err
	}
	if _, err = archive.Write(settingsBlob); err != nil {
		return 0, err
	}

	return uncompressedSize, nil
}
Exemple #8
0
// Run runs each step of the pipeline in sequence, each time passing
// the output of step N as input to step N+1.
// If an error is encountered, the pipeline is aborted.
func (t *Pipeline) Run() (*git.Tree, error) {
	var in *git.Tree
	// Call the previous operation before our own
	// (unless the current operation is Empty or Base, since they would
	// discard the result anyway)
	if t.prev != nil && t.op != OpEmpty && t.op != OpBase {
		prevOut, err := t.prev.Run()
		if err != nil {
			return nil, err
		}
		in = prevOut
	}
	switch t.op {
	case OpEmpty:
		{
			empty, err := emptyTree(t.repo)
			if err != nil {
				return nil, err
			}
			return lookupTree(t.repo, empty)
		}
	case OpBase:
		{
			base, ok := t.arg.(*git.Tree)
			if !ok {
				return nil, fmt.Errorf("base: invalid argument: %v", t.arg)
			}
			return base, nil
		}
	case OpAdd:
		{
			arg, ok := t.arg.(*addArg)
			if !ok {
				return nil, fmt.Errorf("add: invalid argument: %v", t.arg)
			}
			var id *git.Oid
			switch val := arg.val.(type) {
			case *Pipeline:
				{
					if out, err := val.Run(); err != nil {
						return nil, fmt.Errorf("add: run source: %v", err)
					} else {
						id = out.Id()
					}
				}
			case git.Object:
				{
					id = val.Id()
				}
			case *git.Oid:
				{
					id = val
				}
			case *DB:
				{
					tree, err := val.Tree()
					// FIXME: distinguish "no such entry" errors (which we can ignore)
					// from other errors (which we should forward)
					if err == nil && tree != nil {
						id = tree.Id()
					}
				}
			default:
				{
					return nil, fmt.Errorf("invalid value: %#v", val)
				}
			}
			return treeAdd(t.repo, in, arg.key, id, arg.merge)
		}
	case OpDelete:
		{
			key, ok := t.arg.(string)
			if !ok {
				return nil, fmt.Errorf("delete: invalid argument: %v", t.arg)
			}

			return treeDel(t.repo, in, key)
		}
	case OpMkdir:
		{
			key, ok := t.arg.(string)
			if !ok {
				return nil, fmt.Errorf("mkdir: invalid argument: %v", t.arg)
			}
			empty, err := emptyTree(t.repo)
			if err != nil {
				return nil, err
			}
			return treeAdd(t.repo, in, key, empty, true)
		}
	case OpSet:
		{
			kv, ok := t.arg.([]string)
			if !ok {
				return nil, fmt.Errorf("invalid argument")
			}
			if len(kv) != 2 {
				return nil, fmt.Errorf("invalid argument")
			}
			// FIXME: libgit2 crashes if value is empty.
			// Work around this by shelling out to git.
			var (
				id  *git.Oid
				err error
			)
			if kv[1] == "" {
				out, err := exec.Command("git", "--git-dir", t.repo.Path(), "hash-object", "-w", "--stdin").Output()
				if err != nil {
					return nil, fmt.Errorf("git hash-object: %v", err)
				}
				id, err = git.NewOid(strings.Trim(string(out), " \t\r\n"))
				if err != nil {
					return nil, fmt.Errorf("git newoid %v", err)
				}
			} else {
				id, err = t.repo.CreateBlobFromBuffer([]byte(kv[1]))
				if err != nil {
					return nil, err
				}
			}
			return treeAdd(t.repo, in, kv[0], id, true)
		}
	case OpScope:
		{
			key, ok := t.arg.(string)
			if !ok {
				return nil, fmt.Errorf("invalid argument")
			}
			return TreeScope(t.repo, in, key)
		}
	}
	return nil, fmt.Errorf("invalid op: %v", t.op)
}