package main import ( "context" "errors" "fmt" "dehub.dev/src/dehub.git" "dehub.dev/src/dehub.git/cmd/dehub/dcmd" "dehub.dev/src/dehub.git/sigcred" "gopkg.in/src-d/go-git.v4/plumbing" ) func cmdCommit(ctx context.Context, cmd *dcmd.Cmd) { flag := cmd.FlagSet() accountID := flag.String("as", "", "Account to accredit commit with") pgpKeyID := flag.String("anon-pgp-key", "", "ID of pgp key to sign with instead of using an account") var proj proj proj.initFlags(flag) accreditAndCommit := func(payUn dehub.PayloadUnion) error { var sig sigcred.Signifier if *accountID != "" { cfg, err := proj.LoadConfig() if err != nil { return err } var account dehub.Account var ok bool for _, account = range cfg.Accounts { if account.ID == *accountID { ok = true break } } if !ok { return fmt.Errorf("account ID %q not found in config", *accountID) } else if l := len(account.Signifiers); l == 0 || l > 1 { return fmt.Errorf("account %q has %d signifiers, only one is supported right now", *accountID, l) } sig = account.Signifiers[0].Signifier(*accountID) } else { var err error if sig, err = sigcred.LoadSignifierPGP(*pgpKeyID, true); err != nil { return fmt.Errorf("loading pgp key %q: %w", *pgpKeyID, err) } } payUn, err := proj.AccreditPayload(payUn, sig) if err != nil { return fmt.Errorf("accrediting payload: %w", err) } commit, err := proj.Commit(payUn) if err != nil { return fmt.Errorf("committing to git: %w", err) } fmt.Printf("committed to HEAD as %s\n", commit.Hash) return nil } var hasStaged bool body := func() (context.Context, error) { if *accountID == "" && *pgpKeyID == "" { return nil, errors.New("-as or -anon-pgp-key is required") } if err := proj.openProj(); err != nil { return nil, err } var err error if hasStaged, err = proj.HasStagedChanges(); err != nil { return nil, fmt.Errorf("determining if any changes have been staged: %w", err) } return ctx, nil } cmd.SubCmd("change", "Commit file changes", func(ctx context.Context, cmd *dcmd.Cmd) { flag := cmd.FlagSet() description := flag.String("descr", "", "Description of changes") amend := flag.Bool("amend", false, "Add changes to HEAD commit, amend its message, and re-accredit it") cmd.Run(func() (context.Context, error) { if !hasStaged && !*amend { return nil, errors.New("no changes have been staged for commit") } var prevMsg string if *amend { oldHead, err := proj.softReset("change") if err != nil { return nil, err } prevMsg = oldHead.Payload.Change.Description } if *description == "" { var err error if *description, err = tmpFileMsg(defaultCommitFileMsgTpl, prevMsg); err != nil { return nil, fmt.Errorf("error collecting commit message from user: %w", err) } else if *description == "" { return nil, errors.New("empty commit message, not doing anything") } } payUn, err := proj.NewPayloadChange(*description) if err != nil { return nil, fmt.Errorf("could not construct change payload: %w", err) } else if err := accreditAndCommit(payUn); err != nil { return nil, err } return nil, nil }) }, ) cmd.SubCmd("credential", "Commit credential of one or more change commits", func(ctx context.Context, cmd *dcmd.Cmd) { flag := cmd.FlagSet() startRev := flag.String("start", "", "Revision of the starting commit to accredit (when accrediting a range of changes)") endRev := flag.String("end", "HEAD", "Revision of the ending commit to accredit (when accrediting a range of changes)") rev := flag.String("rev", "", "Revision of commit to accredit (when accrediting a single commit)") cmd.Run(func() (context.Context, error) { if *rev == "" && *startRev == "" { return nil, errors.New("-rev or -start is required") } else if hasStaged { return nil, errors.New("credential commit cannot have staged changes") } var credPayUn dehub.PayloadUnion if *rev != "" { commit, err := proj.GetCommitByRevision(plumbing.Revision(*rev)) if err != nil { return nil, fmt.Errorf("resolving revision %q: %w", *rev, err) } if credPayUn, err = proj.NewPayloadCredentialFromChanges([]dehub.Commit{commit}); err != nil { return nil, fmt.Errorf("constructing credential commit: %w", err) } } else { commits, err := proj.GetCommitRangeByRevision( plumbing.Revision(*startRev), plumbing.Revision(*endRev), ) if err != nil { return nil, fmt.Errorf("resolving revisions %q to %q: %w", *startRev, *endRev, err) } else if credPayUn, err = proj.NewPayloadCredentialFromChanges(commits); err != nil { return nil, fmt.Errorf("constructing credential commit: %w", err) } } if err := accreditAndCommit(credPayUn); err != nil { return nil, err } return nil, nil }) }, ) cmd.SubCmd("comment", "Commit a comment to a branch", func(ctx context.Context, cmd *dcmd.Cmd) { flag := cmd.FlagSet() comment := flag.String("comment", "", "Comment message") amend := flag.Bool("amend", false, "Amend the comment message currently in HEAD") cmd.Run(func() (context.Context, error) { if hasStaged { return nil, errors.New("comment commit cannot have staged changes") } var prevComment string if *amend { oldHead, err := proj.softReset("comment") if err != nil { return nil, err } prevComment = oldHead.Payload.Comment.Comment } if *comment == "" { var err error if *comment, err = tmpFileMsg(defaultCommitFileMsgTpl, prevComment); err != nil { return nil, fmt.Errorf("collecting comment message from user: %w", err) } else if *comment == "" { return nil, errors.New("empty comment message, not doing anything") } } payUn, err := proj.NewPayloadComment(*comment) if err != nil { return nil, fmt.Errorf("constructing comment commit: %w", err) } return nil, accreditAndCommit(payUn) }) }, ) cmd.Run(body) } func cmdCombine(ctx context.Context, cmd *dcmd.Cmd) { flag := cmd.FlagSet() onto := flag.String("onto", "", "Branch the new commit should be put onto") startRev := flag.String("start", "", "Revision of the starting commit to combine") endRev := flag.String("end", "", "Revision of the ending commit to combine") var proj proj proj.initFlags(flag) cmd.Run(func() (context.Context, error) { if *onto == "" || *startRev == "" || *endRev == "" { return nil, errors.New("-onto, -start, and -end are required") } if err := proj.openProj(); err != nil { return nil, err } commits, err := proj.GetCommitRangeByRevision( plumbing.Revision(*startRev), plumbing.Revision(*endRev), ) if err != nil { return nil, fmt.Errorf("error getting commits %q to %q: %w", *startRev, *endRev, err) } ontoBranch := plumbing.NewBranchReferenceName(*onto) commit, err := proj.CombinePayloadChanges(commits, ontoBranch) if err != nil { return nil, err } fmt.Printf("new commit %q added to branch %q\n", commit.Hash, ontoBranch.Short()) return nil, nil }) }