Commit a74f7e836f1414f6c06fca610a6231e37c1e68fd

Stefan Sperling 2019-11-10T11:33:13

add -c option to 'got branch', replacing the optional second argument

diff --git a/got/got.1 b/got/got.1
index 84fbdf4..9320a09 100644
--- a/got/got.1
+++ b/got/got.1
@@ -509,7 +509,7 @@ which must be an existing reference.
 Care should be taken not to create loops between references when
 this option is used.
 .El
-.It Cm branch Oo Fl r Ar repository-path Oc Oo Fl l Oc Oo Fl d Ar name Oc Op Ar name Op Ar commit
+.It Cm branch Oo Fl c Ar commit Oc Oo Fl r Ar repository-path Oc Oo Fl l Oc Oo Fl d Ar name Oc Op Ar name
 Manage branches in a repository.
 .Pp
 Branches are managed via references which live in the
@@ -521,25 +521,24 @@ command operates on references in this namespace only.
 .Pp
 If invoked in a work tree without any arguments, print the name of the
 work tree's current branch.
-If one or two arguments are passed, attempt to create a branch reference
-with the given
-.Ar name ,
-and make it point at the given
-.Ar commit .
-The expected
-.Ar commit
-argument is a commit ID SHA1 hash or an existing reference
-or tag name which will be resolved to a commit ID.
-If no
-.Ar commit
-is specified, default to the latest commit on the work tree's current
-branch if invoked in a work tree, or to a commit resolved via the
-repository's HEAD reference.
+If a
+.Ar name
+argument is passed, attempt to create a branch reference with the given name.
+By default the new branch reference will point at the latest commit on the
+work tree's current branch if invoked in a work tree, and otherwise to a commit
+resolved via the repository's HEAD reference.
 .Pp
 The options for
 .Cm got branch
 are as follows:
 .Bl -tag -width Ds
+.It Fl c Ar commit
+Make a newly created branch reference point at the specified
+.Ar commit .
+The expected
+.Ar commit
+argument is a commit ID SHA1 hash or an existing reference
+or tag name which will be resolved to a commit ID.
 .It Fl r Ar repository-path
 Use the repository at the specified path.
 If not specified, assume the repository is located at or above the current
diff --git a/got/got.c b/got/got.c
index 3a3fb51..3b89358 100644
--- a/got/got.c
+++ b/got/got.c
@@ -3275,8 +3275,8 @@ __dead static void
 usage_branch(void)
 {
 	fprintf(stderr,
-	    "usage: %s branch [-r repository] [-l] | -d name | "
-	    "[name [commit]]\n", getprogname());
+	    "usage: %s branch [-c commit] [-r repository] [-l] | -d name | "
+	    "[name]\n", getprogname());
 	exit(1);
 }
 
@@ -3415,11 +3415,9 @@ done:
 
 static const struct got_error *
 add_branch(struct got_repository *repo, const char *branch_name,
-    const char *base_branch)
+    struct got_object_id *base_commit_id)
 {
 	const struct got_error *err = NULL;
-	struct got_object_id *id = NULL;
-	char *label;
 	struct got_reference *ref = NULL;
 	char *base_refname = NULL, *refname = NULL;
 
@@ -3431,11 +3429,6 @@ add_branch(struct got_repository *repo, const char *branch_name,
 	if (branch_name[0] == '-' && branch_name[1] == '\0')
 		return got_error_path(branch_name, GOT_ERR_BAD_REF_NAME);
 
-	err = match_object_id(&id, &label, base_branch,
-	    GOT_OBJ_TYPE_COMMIT, 1, repo);
-	if (err)
-		return err;
-
 	if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
 		 err = got_error_from_errno("asprintf");
 		 goto done;
@@ -3448,7 +3441,7 @@ add_branch(struct got_repository *repo, const char *branch_name,
 	} else if (err->code != GOT_ERR_NOT_REF)
 		goto done;
 
-	err = got_ref_alloc(&ref, refname, id);
+	err = got_ref_alloc(&ref, refname, base_commit_id);
 	if (err)
 		goto done;
 
@@ -3456,7 +3449,6 @@ add_branch(struct got_repository *repo, const char *branch_name,
 done:
 	if (ref)
 		got_ref_close(ref);
-	free(id);
 	free(base_refname);
 	free(refname);
 	return err;
@@ -3470,10 +3462,13 @@ cmd_branch(int argc, char *argv[])
 	struct got_worktree *worktree = NULL;
 	char *cwd = NULL, *repo_path = NULL;
 	int ch, do_list = 0, do_show = 0;
-	const char *delref = NULL;
+	const char *delref = NULL, *commit_id_arg = NULL;
 
-	while ((ch = getopt(argc, argv, "d:r:l")) != -1) {
+	while ((ch = getopt(argc, argv, "c:d:r:l")) != -1) {
 		switch (ch) {
+		case 'c':
+			commit_id_arg = optarg;
+			break;
 		case 'd':
 			delref = optarg;
 			break;
@@ -3502,10 +3497,13 @@ cmd_branch(int argc, char *argv[])
 	if (!do_list && !delref && argc == 0)
 		do_show = 1;
 
+	if ((do_list || delref || do_show) && commit_id_arg != NULL)
+		errx(1, "-c option can only be used when creating a branch");
+
 	if (do_list || delref) {
 		if (argc > 0)
 			usage_branch();
-	} else if (!do_show && (argc < 1 || argc > 2))
+	} else if (!do_show && argc != 1)
 		usage_branch();
 
 #ifndef PROFILE
@@ -3563,16 +3561,16 @@ cmd_branch(int argc, char *argv[])
 	else if (delref)
 		error = delete_branch(repo, worktree, delref);
 	else {
-		const char *base_branch;
-		if (argc == 1) {
-			base_branch = worktree ?
+		struct got_object_id *commit_id;
+		if (commit_id_arg == NULL)
+			commit_id_arg = worktree ?
 			    got_worktree_get_head_ref_name(worktree) :
 			    GOT_REF_HEAD;
-			if (strncmp(base_branch, "refs/heads/", 11) == 0)
-				base_branch += 11;
-		} else
-			base_branch = argv[1];
-		error = add_branch(repo, argv[0], base_branch);
+		error = resolve_commit_arg(&commit_id, commit_id_arg, repo);
+		if (error)
+			goto done;
+		error = add_branch(repo, argv[0], commit_id);
+		free(commit_id);
 	}
 done:
 	if (repo)
diff --git a/regress/cmdline/branch.sh b/regress/cmdline/branch.sh
index 05f7d1c..2a7d11e 100755
--- a/regress/cmdline/branch.sh
+++ b/regress/cmdline/branch.sh
@@ -74,7 +74,7 @@ function test_branch_create {
 	fi
 
 	# Create a branch based on another specific branch
-	(cd $testroot/wt && got branch yetanotherbranch master)
+	(cd $testroot/wt && got branch -c master yetanotherbranch)
 	ret="$?"
 	if [ "$ret" != "0" ]; then
 		test_done "$testroot" "$ret"
@@ -91,7 +91,7 @@ function test_branch_create {
 
 	# Create a branch based on a specific commit
 	local commit_id=`git_show_head $testroot/repo`
-	got branch -r $testroot/repo commitbranch $commit_id
+	got branch -r $testroot/repo -c $commit_id commitbranch
 	ret="$?"
 	if [ "$ret" != "0" ]; then
 		echo "got branch command failed unexpectedly"