Пример #1
0
func StoryChangesFromCommits(commits []*git.Commit) ([]*StoryChangeGroup, error) {
	// Group by Change-Id.
	changeGroups := GroupCommitsByChangeId(commits)

	// Fix the commit sources.
	if err := git.FixCommitSources(commits); err != nil {
		return nil, err
	}

	// Return the changes grouped by story ID.
	return GroupChangesByStoryId(changeGroups), nil
}
Пример #2
0
func run(remoteName, pushURL string) error {
	// Load the git-related SalsaFlow config.
	gitConfig, err := git.LoadConfig()
	if err != nil {
		return err
	}

	// Load the other necessary SalsaFlow config.
	repoConfig, err := repo.LoadConfig()
	if err != nil {
		return err
	}
	enabledTimestamp := repoConfig.SalsaFlowEnabledTimestamp

	// Only check the project remote.
	if remoteName != gitConfig.RemoteName {
		log.Log(
			fmt.Sprintf(
				"Not pushing to the main project remote (%v), check skipped",
				gitConfig.RemoteName))
		return nil
	}

	// The commits that are being pushed are listed on stdin.
	// The format is <local ref> <local sha1> <remote ref> <remote sha1>,
	// so we parse the input and collect all the local hexshas.
	var coreRefs = []string{
		"refs/heads/" + gitConfig.TrunkBranchName,
		"refs/heads/" + gitConfig.ReleaseBranchName,
		"refs/heads/" + gitConfig.StagingBranchName,
		"refs/heads/" + gitConfig.StableBranchName,
	}

	parseTask := "Parse the hook input"
	var revRanges []*revisionRange
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		var (
			line  = scanner.Text()
			parts = strings.Split(line, " ")
		)
		if len(parts) != 4 {
			return errs.NewError(parseTask, errors.New("invalid input line: "+line))
		}

		localRef, localSha, remoteRef, remoteSha := parts[0], parts[1], parts[2], parts[3]

		// Skip the refs that are being deleted.
		if localSha == git.ZeroHash {
			continue
		}

		// Check only updates to the core branches,
		// i.e. trunk, release, client or master.
		var isCoreBranch bool
		for _, ref := range coreRefs {
			if remoteRef == ref {
				isCoreBranch = true
			}
		}
		if !isCoreBranch {
			continue
		}

		// Make sure the reference is up to date.
		// In this case the reference is not up to date when
		// the remote hash cannot be found in the local clone.
		if remoteSha != git.ZeroHash {
			task := fmt.Sprintf("Make sure remote ref '%s' is up to date", remoteRef)
			if _, err := git.Run("cat-file", "-t", remoteSha); err != nil {
				hint := fmt.Sprintf(`
Commit %v does not exist locally.
This is probably because '%v' is not up to date.
Please update the reference from the remote repository,
perhaps by executing 'git pull'.

`, remoteSha, remoteRef)
				return errs.NewErrorWithHint(task, err, hint)
			}
		}

		// Append the revision range for this input line.
		var revRange *revisionRange
		if remoteSha == git.ZeroHash {
			// In case we are pushing a new branch, check commits up to trunk.
			// There is probably no better guess that we can do in general.
			revRange = &revisionRange{gitConfig.TrunkBranchName, localRef}
		} else {
			// Otherwise check the commits that are new compared to the remote ref.
			revRange = &revisionRange{remoteSha, localRef}
		}
		revRanges = append(revRanges, revRange)
	}
	if err := scanner.Err(); err != nil {
		return errs.NewError(parseTask, err)
	}

	// Check the missing Story-Id tags.
	var missing []*git.Commit

	for _, revRange := range revRanges {
		// Get the commit objects for the relevant range.
		task := "Get the commit objects to be pushed"
		commits, err := git.ShowCommitRange(fmt.Sprintf("%v..%v", revRange.From, revRange.To))
		if err != nil {
			return errs.NewError(task, err)
		}

		// Check every commit in the range.
		for _, commit := range commits {
			// Do not check merge commits.
			if commit.Merge != "" {
				continue
			}

			// Do not check commits that happened before SalsaFlow.
			if commit.AuthorDate.Before(enabledTimestamp) {
				continue
			}

			// Check the Story-Id tag.
			if commit.StoryIdTag == "" {
				missing = append(missing, commit)
			}
		}
	}

	// Prompt for confirmation in case that is needed.
	if len(missing) != 0 {
		// Fill in the commit sources.
		task := "Fix commit sources"
		if err := git.FixCommitSources(missing); err != nil {
			return errs.NewError(task, err)
		}

		// Prompt the user for confirmation.
		task = "Prompt the user for confirmation"
		confirmed, err := promptUserForConfirmation(missing)
		if err != nil {
			return errs.NewError(task, err)
		}
		if !confirmed {
			return prompt.ErrCanceled
		}
	}

	return nil
}
Пример #3
0
func hook() error {
	// There are always 3 arguments passed to this hook.
	prevRef, newRef, flag := os.Args[1], os.Args[2], os.Args[3]

	// Return in case prevRef is the zero hash since that means
	// that this hook is being run right after 'git clone'.
	if prevRef == git.ZeroHash {
		return nil
	}

	// Return in case flag is '0'. That signals retrieving a file from the index.
	if flag == "0" {
		return nil
	}

	// Return unless the new HEAD is a core branch.
	isCore, err := isCoreBranchHash(newRef)
	if err != nil {
		return err
	}
	if !isCore {
		return nil
	}

	// Return also in case we are doing something with a temporary branch.
	isNewRefTemp, err := isTempBranchHash(newRef)
	if err != nil {
		return err
	}
	isPrevRefTemp, err := isTempBranchHash(prevRef)
	if err != nil {
		return err
	}
	if isNewRefTemp || isPrevRefTemp {
		return nil
	}

	// Get the relevant commits.
	// These are the commits specified by newRef..prevRef, e.g. trunk..story/foobar.
	commits, err := git.ShowCommitRange(fmt.Sprintf("%v..%v", newRef, prevRef))
	if err != nil {
		return err
	}

	// Drop commits that happened before SalsaFlow bootstrap.
	repoConfig, err := repo.LoadConfig()
	if err != nil {
		return err
	}
	enabledTimestamp := repoConfig.SalsaFlowEnabledTimestamp
	commits = git.FilterCommits(commits, func(commit *git.Commit) bool {
		return commit.AuthorDate.After(enabledTimestamp)
	})

	// Collect the commits with missing Story-Id tag.
	missing := make([]*git.Commit, 0, len(commits))
	for _, commit := range commits {
		// Skip merge commits.
		if commit.Merge != "" {
			continue
		}

		// Add the commit in case Story-Id tag is not set.
		if commit.StoryIdTag == "" {
			missing = append(missing, commit)
		}
	}
	if len(missing) == 0 {
		return nil
	}

	// Fix commit sources.
	if err := git.FixCommitSources(missing); err != nil {
		return err
	}

	// Print the warning.
	return printWarning(missing)
}