From: Tracey Emery Subject: Re: let 'got branch' switch and update the work tree To: gameoftrees@openbsd.org Date: Mon, 24 Feb 2020 07:55:45 -0700 On Mon, Feb 24, 2020 at 03:41:02PM +0100, Stefan Sperling wrote: > 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 > Yes please. I've done this far too many times when in a hurry to try a small change and commit to the master branch instead. Ok. > 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="$?" -- Tracey Emery