func getDependencyPathFileName(ctx *tool.Context, branch string) (string, error) { topLevel, err := ctx.Git().TopLevel() if err != nil { return "", err } return filepath.Join(topLevel, project.MetadataDirName(), branch, dependencyPathFileName), nil }
func newCL(ctx *tool.Context, args []string) error { topLevel, err := ctx.Git().TopLevel() if err != nil { return err } originalBranch, err := ctx.Git().CurrentBranchName() if err != nil { return err } // Create a new branch using the current branch. newBranch := args[0] if err := ctx.Git().CreateAndCheckoutBranch(newBranch); err != nil { return err } // Register a cleanup handler in case of subsequent errors. cleanup := true defer func() { if cleanup { ctx.Git().CheckoutBranch(originalBranch, gitutil.ForceOpt(true)) ctx.Git().DeleteBranch(newBranch, gitutil.ForceOpt(true)) } }() // Record the dependent CLs for the new branch. The dependent CLs // are recorded in a <dependencyPathFileName> file as a // newline-separated list of branch names. branches, err := getDependentCLs(ctx, originalBranch) if err != nil { return err } branches = append(branches, originalBranch) newMetadataDir := filepath.Join(topLevel, project.MetadataDirName(), newBranch) if err := ctx.Run().MkdirAll(newMetadataDir, os.FileMode(0755)); err != nil { return err } file, err := getDependencyPathFileName(ctx, newBranch) if err != nil { return err } if err := ctx.Run().WriteFile(file, []byte(strings.Join(branches, "\n")), os.FileMode(0644)); err != nil { return err } cleanup = false return nil }
// createRepo creates a new repository in the given working directory. func createRepo(t *testing.T, ctx *tool.Context, workingDir, prefix string) string { repoPath, err := ctx.Run().TempDir(workingDir, "repo-"+prefix) if err != nil { t.Fatalf("TempDir() failed: %v", err) } if err := os.Chmod(repoPath, 0777); err != nil { t.Fatalf("Chmod(%v) failed: %v", repoPath, err) } if err := ctx.Git().Init(repoPath); err != nil { t.Fatalf("%v", err) } if err := ctx.Run().MkdirAll(filepath.Join(repoPath, project.MetadataDirName()), os.FileMode(0755)); err != nil { t.Fatalf("%v", err) } return repoPath }
// updateReviewMessage writes the commit message to the given file. func (review *review) updateReviewMessage(file string) error { if err := review.ctx.Git().CheckoutBranch(review.reviewBranch); err != nil { return err } newMessage, err := review.ctx.Git().LatestCommitMessage() if err != nil { return err } // For the initial commit where the commit message file doesn't exist, // add/remove labels after users finish editing the commit message. // // This behavior is consistent with how Change-ID is added for the // initial commit so we don't confuse users. if _, err := review.ctx.Run().Stat(file); err != nil { if os.IsNotExist(err) { newMessage = review.processLabels(newMessage) if err := review.ctx.Git().CommitAmendWithMessage(newMessage); err != nil { return err } } else { return err } } topLevel, err := review.ctx.Git().TopLevel() if err != nil { return err } newMetadataDir := filepath.Join(topLevel, project.MetadataDirName(), review.CLOpts.Branch) if err := review.ctx.Run().MkdirAll(newMetadataDir, os.FileMode(0755)); err != nil { return err } if err := review.ctx.Run().WriteFile(file, []byte(newMessage), 0644); err != nil { return err } return nil }
func cleanupBranch(ctx *tool.Context, branch string) error { if err := ctx.Git().CheckoutBranch(branch); err != nil { return err } if !forceFlag { trackingBranch := "origin/" + remoteBranchFlag if err := ctx.Git().Merge(trackingBranch); err != nil { return err } files, err := ctx.Git().ModifiedFiles(trackingBranch, branch) if err != nil { return err } if len(files) != 0 { return fmt.Errorf("unmerged changes in\n%s", strings.Join(files, "\n")) } } if err := ctx.Git().CheckoutBranch(remoteBranchFlag); err != nil { return err } if err := ctx.Git().DeleteBranch(branch, gitutil.ForceOpt(true)); err != nil { return err } reviewBranch := branch + "-REVIEW" if ctx.Git().BranchExists(reviewBranch) { if err := ctx.Git().DeleteBranch(reviewBranch, gitutil.ForceOpt(true)); err != nil { return err } } // Delete branch metadata. topLevel, err := ctx.Git().TopLevel() if err != nil { return err } metadataDir := filepath.Join(topLevel, project.MetadataDirName()) if err := ctx.Run().RemoveAll(filepath.Join(metadataDir, branch)); err != nil { return err } // Remove the branch from all dependency paths. fileInfos, err := ctx.Run().ReadDir(metadataDir) if err != nil { return err } for _, fileInfo := range fileInfos { if !fileInfo.IsDir() { continue } file, err := getDependencyPathFileName(ctx, fileInfo.Name()) if err != nil { return err } data, err := ctx.Run().ReadFile(file) if err != nil { if !os.IsNotExist(err) { return err } continue } branches := strings.Split(string(data), "\n") for i, tmpBranch := range branches { if branch == tmpBranch { data := []byte(strings.Join(append(branches[:i], branches[i+1:]...), "\n")) if err := ctx.Run().WriteFile(file, data, os.FileMode(0644)); err != nil { return err } break } } } return nil }
return nil } // cmdCLNew represents the "jiri cl new" command. var cmdCLNew = &cmdline.Command{ Runner: cmdline.RunnerFunc(runCLNew), Name: "new", Short: "Create a new local branch for a changelist", Long: fmt.Sprintf(` Command "new" creates a new local branch for a changelist. In particular, it forks a new branch with the given name from the current branch and records the relationship between the current branch and the new branch in the %v metadata directory. The information recorded in the %v metadata directory tracks dependencies between CLs and is used by the "jiri cl sync" and "jiri cl mail" commands. `, project.MetadataDirName(), project.MetadataDirName()), ArgsName: "<name>", ArgsLong: "<name> is the changelist name.", } func runCLNew(env *cmdline.Env, args []string) error { if got, want := len(args), 1; got != want { return env.UsageErrorf("unexpected number of arguments: got %v, want %v", got, want) } ctx := tool.NewContextFromEnv(env) return newCL(ctx, args) } func newCL(ctx *tool.Context, args []string) error { topLevel, err := ctx.Git().TopLevel() if err != nil {