prow/repoowners/repoowners.go
func (c *Client) LoadRepoOwners(org, repo, base string) (RepoOwner, error) {
	log := c.logger.WithFields(logrus.Fields{"org": org, "repo": repo, "base": base})
	cloneRef := fmt.Sprintf("%s/%s", org, repo)
	fullName := fmt.Sprintf("%s:%s", cloneRef, base)
	mdYaml := c.mdYAMLEnabled(org, repo)

	sha, err := c.ghc.GetRef(org, repo, fmt.Sprintf("heads/%s", base))
	if err != nil {
		return nil, fmt.Errorf("failed to get current SHA for %s: %v", fullName, err)
	}

	c.lock.Lock()
	defer c.lock.Unlock()
	entry, ok := c.cache[fullName]
	if !ok || entry.sha != sha || entry.owners == nil || !entry.matchesMDYAML(mdYaml) {
		gitRepo, err := c.git.Clone(cloneRef)
		if err != nil {
			return nil, fmt.Errorf("failed to clone %s: %v", cloneRef, err)
		}
		defer gitRepo.Clean()

		reusable := entry.fullyLoaded() && entry.matchesMDYAML(mdYaml)
		// In most sha changed cases, the files associated with the owners are unchanged.
		// The cached entry can continue to be used, so need do git diff
		if reusable {
			changes, err := gitRepo.Diff(sha, entry.sha)
			if err != nil {
				return nil, fmt.Errorf("failed to diff %s with %s", sha, entry.sha)
			}
			for _, change := range changes {
				if mdYaml && strings.HasSuffix(change, ".md") ||
					strings.HasSuffix(change, aliasesFileName) ||
					strings.HasSuffix(change, ownersFileName) {
					reusable = false
					break
				}
			}
		}
		if reusable {
			entry.sha = sha
		} else {
			if err := gitRepo.Checkout(base); err != nil {
				return nil, err
			}

			if entry.aliases == nil || entry.sha != sha {
				// aliases must be loaded
				entry.aliases = loadAliasesFrom(gitRepo.Dir, log)
			}

			dirBlacklistPatterns := append(c.ownersDirBlacklist().DirBlacklist(org, repo), commonDirBlacklist...)
			var dirBlacklist []*regexp.Regexp
			for _, pattern := range dirBlacklistPatterns {
				re, err := regexp.Compile(pattern)
				if err != nil {
					log.WithError(err).Errorf("Invalid OWNERS dir blacklist regexp %q.", pattern)
					continue
				}
				dirBlacklist = append(dirBlacklist, re)
			}

			entry.owners, err = loadOwnersFrom(gitRepo.Dir, mdYaml, entry.aliases, dirBlacklist, log)
			if err != nil {
				return nil, fmt.Errorf("failed to load RepoOwners for %s: %v", fullName, err)
			}
			entry.sha = sha
			c.cache[fullName] = entry
		}
	}

	if c.skipCollaborators(org, repo) {
		log.Debugf("Skipping collaborator checks for %s/%s", org, repo)
		return entry.owners, nil
	}

	var owners *RepoOwners
	// Filter collaborators. We must filter the RepoOwners struct even if it came from the cache
	// because the list of collaborators could have changed without the git SHA changing.
	collaborators, err := c.ghc.ListCollaborators(org, repo)
	if err != nil {
		log.WithError(err).Errorf("Failed to list collaborators while loading RepoOwners. Skipping collaborator filtering.")
		owners = entry.owners
	} else {
		owners = entry.owners.filterCollaborators(collaborators)
	}
	return owners, nil
}
func (c *Client) LoadRepoOwners(org, repo, base string) (RepoOwner, error) {
	log := c.logger.WithFields(logrus.Fields{"org": org, "repo": repo, "base": base})
	cloneRef := fmt.Sprintf("%s/%s", org, repo)
	fullName := fmt.Sprintf("%s:%s", cloneRef, base)
	mdYaml := c.mdYAMLEnabled(org, repo)

	sha, err := c.ghc.GetRef(org, repo, fmt.Sprintf("heads/%s", base))
	if err != nil {
		return nil, fmt.Errorf("failed to get current SHA for %s: %v", fullName, err)
	}

	c.lock.Lock()
	defer c.lock.Unlock()
	entry, ok := c.cache[fullName]
	if !ok || entry.sha != sha || entry.owners == nil || !entry.matchesMDYAML(mdYaml) {
		gitRepo, err := c.git.Clone(cloneRef)
		if err != nil {
			return nil, fmt.Errorf("failed to clone %s: %v", cloneRef, err)
		}
		defer gitRepo.Clean()

		reusable := entry.fullyLoaded() && entry.matchesMDYAML(mdYaml)
		// In most sha changed cases, the files associated with the owners are unchanged.
		// The cached entry can continue to be used, so need do git diff
		if reusable {
			changes, err := gitRepo.Diff(sha, entry.sha)
			if err != nil {
				return nil, fmt.Errorf("failed to diff %s with %s", sha, entry.sha)
			}
			for _, change := range changes {
				if mdYaml && strings.HasSuffix(change, ".md") ||
					strings.HasSuffix(change, aliasesFileName) ||
					strings.HasSuffix(change, ownersFileName) {
					reusable = false
					break
				}
			}
		}
		if reusable {
			entry.sha = sha
		} else {
			if err := gitRepo.Checkout(base); err != nil {
				return nil, err
			}

			if entry.aliases == nil || entry.sha != sha {
				// aliases must be loaded
				entry.aliases = loadAliasesFrom(gitRepo.Dir, log)
			}

			dirBlacklistPatterns := append(c.ownersDirBlacklist().DirBlacklist(org, repo), commonDirBlacklist...)
			var dirBlacklist []*regexp.Regexp
			for _, pattern := range dirBlacklistPatterns {
				re, err := regexp.Compile(pattern)
				if err != nil {
					log.WithError(err).Errorf("Invalid OWNERS dir blacklist regexp %q.", pattern)
					continue
				}
				dirBlacklist = append(dirBlacklist, re)
			}

			entry.owners, err = loadOwnersFrom(gitRepo.Dir, mdYaml, entry.aliases, dirBlacklist, log)
			if err != nil {
				return nil, fmt.Errorf("failed to load RepoOwners for %s: %v", fullName, err)
			}
			entry.sha = sha
			c.cache[fullName] = entry
		}
	}

	if c.skipCollaborators(org, repo) {
		log.Debugf("Skipping collaborator checks for %s/%s", org, repo)
		return entry.owners, nil
	}

	var owners *RepoOwners
	// Filter collaborators. We must filter the RepoOwners struct even if it came from the cache
	// because the list of collaborators could have changed without the git SHA changing.
	collaborators, err := c.ghc.ListCollaborators(org, repo)
	if err != nil {
		log.WithError(err).Errorf("Failed to list collaborators while loading RepoOwners. Skipping collaborator filtering.")
		owners = entry.owners
	} else {
		owners = entry.owners.filterCollaborators(collaborators)
	}
	return owners, nil
}
// LoadRepoOwners returns an up-to-date RepoOwners struct for the specified repo.
// Note: The returned *RepoOwners should be treated as read only.