"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Stefan Sperling <stsp@stsp.name>
Subject:
let 'got branch' switch and update the work tree
To:
gameoftrees@openbsd.org
Date:
Mon, 24 Feb 2020 15:41:02 +0100

Download raw body.

Thread
I have sometimes found myself creating a new branch and committing
to the older branch I was on, with the sequence:

	got br new
	got ci

With the diff below, the above 'got ci' would go to the new branch
because in addition to creating the new branch in the repository
the work tree will be switched and updated to this branch.

So the above sequence is now equivalent to:

	got br new
	got up -b new
	got ci

The downside is that 'got branch' could leave merge conflicts behind
if the work tree contains local changes, especially when the -c option
is used to create a branch based off an older commit.
So I am adding a new -n option to 'got branch' which prevents a switch
and update of the work tree, if desired.

I hope people will have a worse time when they forget to run 'update -b'
before a commit than when they forget to pass the -n option to 'got branch'.

Of course, nothing changes if 'got branch' is run outside of a work tree,
such as in the Git repository itself.

ok?

diff 98dc66a432acfcf22aa25d814ef09fa117e20a1b /home/stsp/src/got
blob - 501781dfaf2488dd5255b6fe6d01f4c22c9693f0
file + got/got.1
--- got/got.1
+++ got/got.1
@@ -532,8 +532,8 @@ 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 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.
+.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 Oo Fl n Oc Op Ar name
+Create, list, or delete branches.
 .Pp
 Branches are managed via references which live in the
 .Dq refs/heads/
@@ -544,6 +544,7 @@ 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.
+.Pp
 If a
 .Ar name
 argument is passed, attempt to create a branch reference with the given name.
@@ -551,6 +552,22 @@ By default the new branch reference will point at 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
+If invoked in a work tree, once the branch was created successfully
+switch the work tree's head reference to the newly created branch and
+update files across the entire work tree, just like
+.Cm got update -b Ar name
+would do.
+Show the status of each affected file, using the following status codes:
+.Bl -column YXZ description
+.It U Ta file was updated and contained no local changes
+.It G Ta file was updated and local changes were merged cleanly
+.It C Ta file was updated and conflicts occurred during merge
+.It D Ta file was deleted
+.It A Ta new file was added
+.It \(a~ Ta versioned file is obstructed by a non-regular file
+.It ! Ta a missing versioned file was restored
+.El
+.Pp
 The options for
 .Cm got branch
 are as follows:
@@ -583,6 +600,8 @@ Only the branch reference is deleted.
 Any commit, tree, and blob objects belonging to the branch
 remain in the repository and may be removed separately with
 Git's garbage collector.
+.It Fl n
+Do not switch and update the work tree after creating a new branch.
 .El
 .It Cm br
 Short alias for
blob - 0b384fd1f5315c96063e7c187ccbcbd0c571dc12
file + got/got.c
--- got/got.c
+++ got/got.c
@@ -3284,8 +3284,8 @@ __dead static void
 usage_branch(void)
 {
 	fprintf(stderr,
-	    "usage: %s branch [-c commit] [-r repository] [-l] | -d name | "
-	    "[name]\n", getprogname());
+	    "usage: %s branch [-c commit] [-d] [-r repository] [-l] [-n] "
+	        "[name]\n", getprogname());
 	exit(1);
 }
 
@@ -3470,10 +3470,17 @@ cmd_branch(int argc, char *argv[])
 	struct got_repository *repo = NULL;
 	struct got_worktree *worktree = NULL;
 	char *cwd = NULL, *repo_path = NULL;
-	int ch, do_list = 0, do_show = 0;
+	int ch, do_list = 0, do_show = 0, do_update = 1;
 	const char *delref = NULL, *commit_id_arg = NULL;
+	struct got_reference *ref = NULL;
+	struct got_pathlist_head paths;
+	struct got_pathlist_entry *pe;
+	struct got_object_id *commit_id = NULL;
+	char *commit_id_str = NULL;
 
-	while ((ch = getopt(argc, argv, "c:d:r:l")) != -1) {
+	TAILQ_INIT(&paths);
+
+	while ((ch = getopt(argc, argv, "c:d:r:ln")) != -1) {
 		switch (ch) {
 		case 'c':
 			commit_id_arg = optarg;
@@ -3491,6 +3498,9 @@ cmd_branch(int argc, char *argv[])
 		case 'l':
 			do_list = 1;
 			break;
+		case 'n':
+			do_update = 0;
+			break;
 		default:
 			usage_branch();
 			/* NOTREACHED */
@@ -3570,7 +3580,6 @@ cmd_branch(int argc, char *argv[])
 	else if (delref)
 		error = delete_branch(repo, worktree, delref);
 	else {
-		struct got_object_id *commit_id;
 		if (commit_id_arg == NULL)
 			commit_id_arg = worktree ?
 			    got_worktree_get_head_ref_name(worktree) :
@@ -3580,15 +3589,59 @@ cmd_branch(int argc, char *argv[])
 		if (error)
 			goto done;
 		error = add_branch(repo, argv[0], commit_id);
-		free(commit_id);
+		if (error)
+			goto done;
+		if (worktree && do_update) {
+			int did_something = 0;
+			char *branch_refname = NULL;
+
+			error = got_object_id_str(&commit_id_str, commit_id);
+			if (error)
+				goto done;
+			error = get_worktree_paths_from_argv(&paths, 0, NULL,
+			    worktree);
+			if (error)
+				goto done;
+			if (asprintf(&branch_refname, "refs/heads/%s", argv[0])
+			    == -1) {
+				error = got_error_from_errno("asprintf");
+				goto done;
+			}
+			error = got_ref_open(&ref, repo, branch_refname, 0);
+			free(branch_refname);
+			if (error)
+				goto done;
+			error = switch_head_ref(ref, commit_id, worktree,
+			    repo);
+			if (error)
+				goto done;
+			error = got_worktree_set_base_commit_id(worktree, repo,
+			    commit_id);
+			if (error)
+				goto done;
+			error = got_worktree_checkout_files(worktree, &paths,
+			    repo, update_progress, &did_something,
+			    check_cancelled, NULL);
+			if (error)
+				goto done;
+			if (did_something)
+				printf("Updated to commit %s\n", commit_id_str);
+		}
 	}
 done:
+	if (ref)
+		got_ref_close(ref);
 	if (repo)
 		got_repo_close(repo);
 	if (worktree)
 		got_worktree_close(worktree);
 	free(cwd);
 	free(repo_path);
+	free(commit_id);
+	free(commit_id_str);
+	TAILQ_FOREACH(pe, &paths, entry)
+		free((char *)pe->path);
+	got_pathlist_free(&paths);
 	return error;
 }
 
blob - 2a7d11efbed29670e6e1ba9db1450f174a74d399
file + regress/cmdline/branch.sh
--- regress/cmdline/branch.sh
+++ regress/cmdline/branch.sh
@@ -18,6 +18,7 @@
 
 function test_branch_create {
 	local testroot=`test_init branch_create`
+	local commit_id0=`git_show_head $testroot/repo`
 
 	# Create a branch based on repository's HEAD reference
 	got branch -r $testroot/repo newbranch
@@ -58,7 +59,7 @@ function test_branch_create {
 	fi
 
 	# Create a branch based on the work tree's branch
-	(cd $testroot/wt && got branch anotherbranch)
+	(cd $testroot/wt && got branch -n anotherbranch)
 	ret="$?"
 	if [ "$ret" != "0" ]; then
 		test_done "$testroot" "$ret"
@@ -74,7 +75,7 @@ function test_branch_create {
 	fi
 
 	# Create a branch based on another specific branch
-	(cd $testroot/wt && got branch -c master yetanotherbranch)
+	(cd $testroot/wt && got branch -n -c master yetanotherbranch)
 	ret="$?"
 	if [ "$ret" != "0" ]; then
 		test_done "$testroot" "$ret"
@@ -103,6 +104,24 @@ function test_branch_create {
 	ret="$?"
 	if [ "$ret" != "0" ]; then
 		echo "git checkout command failed unexpectedly"
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	# Create a branch and let the work tree be updated to it
+	(cd $testroot/wt && got branch -c $commit_id0 updatebranch \
+		> $testroot/stdout)
+
+	echo -n "Switching work tree from refs/heads/newbranch to " \
+		> $testroot/stdout.expected
+	echo "refs/heads/updatebranch" >> $testroot/stdout.expected
+	echo "U  gamma/delta" >> $testroot/stdout.expected
+	echo "Updated to commit $commit_id0" >> $testroot/stdout.expected
+
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
 	fi
 	test_done "$testroot" "$ret"
 }
blob - 3c441f126f81e96af27255bfe1ac35115c1b2673
file + regress/cmdline/rebase.sh
--- regress/cmdline/rebase.sh
+++ regress/cmdline/rebase.sh
@@ -741,7 +741,7 @@ function test_rebase_no_commits_to_rebase {
 		return 1
 	fi
 
-	(cd $testroot/wt && got branch newbranch)
+	(cd $testroot/wt && got branch -n newbranch)
 
 	echo "modified alpha on master" > $testroot/wt/alpha
 	(cd $testroot/wt && got commit -m 'test rebase_no_commits_to_rebase' \
@@ -848,7 +848,7 @@ function test_rebase_forward {
 		return 1
 	fi
 
-	(cd $testroot/wt && got branch > $testroot/stdout)
+	(cd $testroot/wt && got branch -n > $testroot/stdout)
 	echo "master" > $testroot/stdout.expected
 	cmp -s $testroot/stdout.expected $testroot/stdout
 	ret="$?"