func()

in internal/git/localrepo/commit.go [123:240]


func (repo *Repo) WriteCommit(ctx context.Context, cfg WriteCommitConfig) (git.ObjectID, error) {
	if err := validateWriteCommitConfig(cfg); err != nil {
		return "", err
	}

	if cfg.AuthorDate.IsZero() {
		cfg.AuthorDate = time.Now()
	}

	if cfg.CommitterDate.IsZero() {
		cfg.CommitterDate = time.Now()
	}

	// Use 'commit-tree' instead of 'commit' because we are in a bare
	// repository. What we do here is the same as "commit -m message
	// --allow-empty".
	commitArgs := []string{string(cfg.TreeID)}

	repoPath, err := repo.Path(ctx)
	if err != nil {
		return "", fmt.Errorf("getting repo path: %w", err)
	}

	var env []string
	if cfg.AlternateObjectDir != "" {
		if !filepath.IsAbs(cfg.AlternateObjectDir) {
			return "", errors.New("alternate object directory must be an absolute path")
		}

		if err := os.MkdirAll(cfg.AlternateObjectDir, mode.Directory); err != nil {
			return "", err
		}

		env = append(env,
			fmt.Sprintf("GIT_OBJECT_DIRECTORY=%s", cfg.AlternateObjectDir),
			fmt.Sprintf("GIT_ALTERNATE_OBJECT_DIRECTORIES=%s", filepath.Join(repoPath, "objects")),
		)
	}

	env = append(env,
		fmt.Sprintf("GIT_AUTHOR_DATE=%s", git.FormatTime(cfg.AuthorDate)),
		fmt.Sprintf("GIT_AUTHOR_NAME=%s", cfg.AuthorName),
		fmt.Sprintf("GIT_AUTHOR_EMAIL=%s", cfg.AuthorEmail),
		fmt.Sprintf("GIT_COMMITTER_DATE=%s", git.FormatTime(cfg.CommitterDate)),
	)

	if featureflag.GPGSigning.IsEnabled(ctx) && cfg.Sign && cfg.GitConfig.CommitterName != "" && cfg.GitConfig.CommitterEmail != "" {
		env = append(env,
			fmt.Sprintf("GIT_COMMITTER_NAME=%s", cfg.GitConfig.CommitterName),
			fmt.Sprintf("GIT_COMMITTER_EMAIL=%s", cfg.GitConfig.CommitterEmail),
		)
	} else {
		env = append(env,
			fmt.Sprintf("GIT_COMMITTER_NAME=%s", cfg.CommitterName),
			fmt.Sprintf("GIT_COMMITTER_EMAIL=%s", cfg.CommitterEmail),
		)
	}

	var flags []gitcmd.Option

	for _, parent := range cfg.Parents {
		flags = append(flags, gitcmd.ValueFlag{Name: "-p", Value: parent.String()})
	}

	flags = append(flags, gitcmd.ValueFlag{Name: "-F", Value: "-"})

	var stdout, stderr bytes.Buffer

	opts := []gitcmd.CmdOpt{
		gitcmd.WithStdout(&stdout),
		gitcmd.WithStderr(&stderr),
		gitcmd.WithStdin(strings.NewReader(cfg.Message)),
		gitcmd.WithEnv(env...),
	}

	if featureflag.GPGSigning.IsEnabled(ctx) && cfg.Sign && cfg.GitConfig.SigningKey != "" {
		flags = append(flags, gitcmd.Flag{Name: "--gpg-sign=" + cfg.GitConfig.SigningKey})
		opts = append(opts, gitcmd.WithGitalyGPG())
	}

	if err := repo.ExecAndWait(ctx,
		gitcmd.Command{
			Name:  "commit-tree",
			Flags: flags,
			Args:  commitArgs,
		},
		opts...,
	); err != nil {
		if strings.Contains(stderr.String(), "name consists only of disallowed characters") {
			return "", ErrDisallowedCharacters
		}

		return "", fmt.Errorf("commit-tree: %w: %s %s", err, stderr.String(), stdout.String())
	}

	objectHash, err := repo.ObjectHash(ctx)
	if err != nil {
		return "", fmt.Errorf("detecting object hash: %w", err)
	}

	oid, err := objectHash.FromHex(text.ChompBytes(stdout.Bytes()))
	if err != nil {
		return "", fmt.Errorf("hex to object hash: %w", err)
	}

	if cfg.Reference != "" {
		if err := repo.UpdateRef(
			ctx,
			git.ReferenceName(cfg.Reference),
			oid,
			"",
		); err != nil {
			return "", fmt.Errorf("updating ref: %w", err)
		}
	}

	return oid, nil
}