commit - 08573d5b25f67f49eebace9318c417f8d384ab10
commit + 024e9686ae5fdfe2c0699649dc600c3dd39397f0
blob - fe652787be00cc913d2c8b2aea7b0a18025407f1
blob + 9ecb75494a1c2fb158597ddb5c771284fa29c6a2
--- got/got.1
+++ got/got.1
.Ar path-prefix
will be checked out.
.El
-.It Cm update [ Fl c Ar commit ] [ Ar path ]
-Update an existing work tree to another commit on the current branch.
-By default, the latest commit on the current branch is assumed.
+.It Cm update [ Fl b Ar branch ] [ Fl c Ar commit ] [ Ar path ]
+Update an existing work tree to a different commit.
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
The base commit of such a work tree can be made consistent by running
.Cm got update
across the entire work tree.
+Specifying a
+.Ar path
+is incompatible with the
+.Fl b
+option.
.Pp
The options for
.Cm got update
are as follows:
.Bl -tag -width Ds
+.It Fl b Ar branch
+Switch the work tree's branch reference to the specified
+.Ar branch
+before updating the work tree.
+This option requires that all paths in the work tree are updated.
.It Fl c Ar commit
Update the work tree to the specified
.Ar commit .
The expected argument is a SHA1 hash which corresponds to a commit object.
+If this option is not specified, the most recent commit on the work tree's
+branch will be used.
.El
.It Cm status [ Ar path ]
Show the current modification status of files in a work tree,
blob - 6196ec8e1aee73ae1e808d8789afab3b6a6a8f03
blob + 9da47c9e14b7718c02ba130a82ba75db680fab64
--- got/got.c
+++ got/got.c
}
static const struct got_error *
-check_linear_ancestry(struct got_worktree *worktree,
- struct got_object_id *commit_id, struct got_repository *repo)
+check_linear_ancestry(struct got_object_id *commit_id,
+ struct got_object_id *base_commit_id, struct got_repository *repo)
{
const struct got_error *err = NULL;
- struct got_object_id *yca_id, *base_commit_id;
+ struct got_object_id *yca_id;
- base_commit_id = got_worktree_get_base_commit_id(worktree);
err = got_commit_graph_find_youngest_common_ancestor(&yca_id,
commit_id, base_commit_id, repo);
if (err)
commit_id_str);
if (error != NULL)
goto done;
- error = check_linear_ancestry(worktree, commit_id, repo);
+ error = check_linear_ancestry(commit_id,
+ got_worktree_get_base_commit_id(worktree), repo);
if (error != NULL) {
free(commit_id);
goto done;
__dead static void
usage_update(void)
{
- fprintf(stderr, "usage: %s update [-c commit] [path]\n",
+ fprintf(stderr, "usage: %s update [-b branch] [-c commit] [path]\n",
getprogname());
exit(1);
}
char *worktree_path = NULL, *path = NULL;
struct got_object_id *commit_id = NULL;
char *commit_id_str = NULL;
+ const char *branch_name = NULL;
+ struct got_reference *head_ref = NULL;
int ch, did_something = 0;
- while ((ch = getopt(argc, argv, "c:")) != -1) {
+ while ((ch = getopt(argc, argv, "b:c:")) != -1) {
switch (ch) {
+ case 'b':
+ branch_name = optarg;
+ break;
case 'c':
commit_id_str = strdup(optarg);
if (commit_id_str == NULL)
if (error)
goto done;
+ if (branch_name == NULL)
+ branch_name = got_worktree_get_head_ref_name(worktree);
+ error = got_ref_open(&head_ref, repo, branch_name, 0);
+ if (error != NULL)
+ goto done;
if (commit_id_str == NULL) {
- struct got_reference *head_ref;
- error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
- if (error != NULL)
- goto done;
error = got_ref_resolve(&commit_id, repo, head_ref);
if (error != NULL)
goto done;
goto done;
}
- error = check_linear_ancestry(worktree, commit_id, repo);
- if (error != NULL)
- goto done;
+ if (strcmp(got_ref_get_name(head_ref),
+ got_worktree_get_head_ref_name(worktree)) != 0) {
+ struct got_object_id *head_commit_id;
+ if (strlen(path) != 0) {
+ fprintf(stderr, "%s: switching to a different "
+ "branch requires that the entire work tree "
+ "gets updated, not just '%s'\n",
+ getprogname(), path);
+ error = got_error(GOT_ERR_BAD_PATH);
+ goto done;
+ }
+ error = got_ref_resolve(&head_commit_id, repo, head_ref);
+ if (error)
+ goto done;
+ error = check_linear_ancestry(commit_id, head_commit_id, repo);
+ if (error != NULL)
+ goto done;
+ error = got_worktree_set_head_ref(worktree, head_ref);
+ if (error)
+ goto done;
+ } else {
+ error = check_linear_ancestry(commit_id,
+ got_worktree_get_base_commit_id(worktree), repo);
+ if (error != NULL)
+ goto done;
+ }
if (got_object_id_cmp(got_worktree_get_base_commit_id(worktree),
commit_id) != 0) {
blob - 204c0b97c37f8f8c73bf17fbfc12436c416b62a3
blob + 38554a89939443afe975a7d181c7e5528eb8d42f
--- include/got_worktree.h
+++ include/got_worktree.h
const char *got_worktree_get_head_ref_name(struct got_worktree *);
/*
+ * Set the branch head reference of the work tree.
+ */
+const struct got_error *got_worktree_set_head_ref(struct got_worktree *,
+ struct got_reference *);
+
+/*
* Get the current base commit ID of a worktree.
*/
struct got_object_id *got_worktree_get_base_commit_id(struct got_worktree *);
blob - 674f852d118ec5c1e19ba14a93ab7f8731b9a8a4
blob + 7c7732c5ffc9aabbc1c18f30cbaaa200279534c7
--- lib/worktree.c
+++ lib/worktree.c
return err;
}
+static const struct got_error *
+write_head_ref(const char *path_got, struct got_reference *head_ref)
+{
+ const struct got_error *err = NULL;
+ char *refstr = NULL;
+
+ if (got_ref_is_symbolic(head_ref)) {
+ refstr = got_ref_to_str(head_ref);
+ if (refstr == NULL)
+ return got_error_from_errno("got_ref_to_str");
+ } else {
+ refstr = strdup(got_ref_get_name(head_ref));
+ if (refstr == NULL)
+ return got_error_from_errno("strdup");
+ }
+ err = update_meta_file(path_got, GOT_WORKTREE_HEAD_REF, refstr);
+ free(refstr);
+ return err;
+}
+
const struct got_error *
got_worktree_init(const char *path, struct got_reference *head_ref,
const char *prefix, struct got_repository *repo)
uint32_t uuid_status;
int obj_type;
char *path_got = NULL;
- char *refstr = NULL;
char *formatstr = NULL;
char *absprefix = NULL;
char *basestr = NULL;
goto done;
/* Write the HEAD reference. */
- if (got_ref_is_symbolic(head_ref)) {
- refstr = got_ref_to_str(head_ref);
- if (refstr == NULL) {
- err = got_error_from_errno("got_ref_to_str");
- goto done;
- }
- } else {
- refstr = strdup(got_ref_get_name(head_ref));
- if (refstr == NULL) {
- err = got_error_from_errno("strdup");
- goto done;
- }
- }
- err = create_meta_file(path_got, GOT_WORKTREE_HEAD_REF, refstr);
+ err = write_head_ref(path_got, head_ref);
if (err)
goto done;
free(commit_id);
free(path_got);
free(formatstr);
- free(refstr);
free(absprefix);
free(basestr);
free(uuidstr);
got_worktree_get_head_ref_name(struct got_worktree *worktree)
{
return worktree->head_ref_name;
+}
+
+const struct got_error *
+got_worktree_set_head_ref(struct got_worktree *worktree,
+ struct got_reference *head_ref)
+{
+ const struct got_error *err = NULL;
+ char *path_got = NULL, *head_ref_name = NULL;
+
+ if (asprintf(&path_got, "%s/%s", worktree->root_path,
+ GOT_WORKTREE_GOT_DIR) == -1) {
+ err = got_error_from_errno("asprintf");
+ path_got = NULL;
+ goto done;
+ }
+
+ head_ref_name = strdup(got_ref_get_name(head_ref));
+ if (head_ref_name == NULL) {
+ err = got_error_from_errno("strdup");
+ goto done;
+ }
+
+ err = write_head_ref(path_got, head_ref);
+ if (err)
+ goto done;
+
+ free(worktree->head_ref_name);
+ worktree->head_ref_name = head_ref_name;
+done:
+ free(path_got);
+ if (err)
+ free(head_ref_name);
+ return err;
}
struct got_object_id *
blob - 3c6429593ca295ae154c301420e7717af1201161
blob + 6d4a991bc7cf8d3edfaf88297fdbf43787ce5f9f
--- regress/cmdline/update.sh
+++ regress/cmdline/update.sh
fi
test_done "$testroot" "$ret"
}
+
+function test_update_to_another_branch {
+ local testroot=`test_init update_to_another_branch`
+
+ got checkout $testroot/repo $testroot/wt > /dev/null
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo 'refs/heads/master'> $testroot/head-ref.expected
+ cmp -s $testroot/head-ref.expected $testroot/wt/.got/head-ref
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/head-ref.expected $testroot/wt/.got/head-ref
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ (cd $testroot/repo && git checkout -q -b newbranch)
+ echo "modified alpha on new branch" > $testroot/repo/alpha
+ git_commit $testroot/repo -m "modified alpha on new branch"
+
+ echo "modified alpha in work tree" > $testroot/wt/alpha
+
+ echo "C alpha" > $testroot/stdout.expected
+ echo -n "Updated to commit " >> $testroot/stdout.expected
+ git_show_head $testroot/repo >> $testroot/stdout.expected
+ echo >> $testroot/stdout.expected
+
+ (cd $testroot/wt && got update -b newbranch > $testroot/stdout)
+
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ echo -n "<<<<<<< commit " > $testroot/content.expected
+ git_show_head $testroot/repo >> $testroot/content.expected
+ echo >> $testroot/content.expected
+ echo "modified alpha on new branch" >> $testroot/content.expected
+ echo "=======" >> $testroot/content.expected
+ echo "modified alpha in work tree" >> $testroot/content.expected
+ echo '>>>>>>> alpha' >> $testroot/content.expected
+ cat $testroot/wt/alpha > $testroot/content
+
+ cmp -s $testroot/content.expected $testroot/content
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/content.expected $testroot/content
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo 'refs/heads/newbranch'> $testroot/head-ref.expected
+ cmp -s $testroot/head-ref.expected $testroot/wt/.got/head-ref
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/head-ref.expected $testroot/wt/.got/head-ref
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ test_done "$testroot" "$ret"
+}
+
run_test test_update_basic
run_test test_update_adds_file
run_test test_update_deletes_file
run_test test_update_partial_rm
run_test test_update_partial_dir
run_test test_update_moved_branch_ref
+run_test test_update_to_another_branch