From: Stefan Sperling Subject: let 'got branch' switch and update the work tree To: gameoftrees@openbsd.org Date: Mon, 24 Feb 2020 15:41:02 +0100 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="$?"