commit 768705e331a0ff020ecc806142aecdecdbfb2e89 from: Stefan Sperling via: Thomas Adam date: Mon Sep 27 18:32:16 2021 UTC make it possible to merge vendor branches with 'got merge' commit - 2f4dd2c2551a389a9e13498a806657a921679554 commit + 768705e331a0ff020ecc806142aecdecdbfb2e89 blob - 06422c812cb6a86575f656e0a5293c72ed775769 blob + c408a296cf7c404394c1001c724ca9a0355c7e6a --- got/got.1 +++ got/got.1 @@ -2141,8 +2141,9 @@ If a linear project history is desired, then use of should be preferred over .Cm got merge . However, even strictly linear projects may require merge commits in order -to merge in new versions of code imported from third-party projects on -vendor branches. +to merge in new versions of third-party code stored on vendor branches +created with +.Cm got import . .Pp Merge commits are commits based on multiple parent commits. The tip commit of the work tree's current branch, which must be set with @@ -2154,13 +2155,13 @@ The tip commit of the specified .Ar branch will be used as the second parent. .Pp +No ancestral relationship between the two branches is required. +If the two branches have already been merged previously, only new changes +will be merged. +.Pp It is not possible to create merge commits with more than two parents. If more than one branch needs to be merged, then multiple merge commits with two parents each can be created in sequence. -.Pp -The -.Ar branch -must share common ancestry with the work tree's current branch. .Pp While merging changes found on the .Ar branch blob - ddcebec4e3d857a20d7894579e2d4ee08bbf0fe9 blob + ad95c4f4600316e0434df71d4de1a2c764eaa8ff --- got/got.c +++ got/got.c @@ -2701,7 +2701,7 @@ check_linear_ancestry(struct got_object_id *commit_id, struct got_object_id *yca_id; err = got_commit_graph_find_youngest_common_ancestor(&yca_id, - commit_id, base_commit_id, repo, check_cancelled, NULL); + commit_id, base_commit_id, 1, repo, check_cancelled, NULL); if (err) return err; @@ -8597,7 +8597,7 @@ print_backup_ref(const char *branch_name, const char * goto done; err = got_commit_graph_find_youngest_common_ancestor(&yca_id, - old_commit_id, new_commit_id, repo, check_cancelled, NULL); + old_commit_id, new_commit_id, 1, repo, check_cancelled, NULL); if (err) goto done; @@ -8954,7 +8954,7 @@ cmd_rebase(int argc, char *argv[]) base_commit_id = got_worktree_get_base_commit_id(worktree); error = got_commit_graph_find_youngest_common_ancestor(&yca_id, - base_commit_id, branch_head_commit_id, repo, + base_commit_id, branch_head_commit_id, 1, repo, check_cancelled, NULL); if (error) goto done; @@ -10729,16 +10729,10 @@ cmd_merge(int argc, char *argv[]) if (error) goto done; error = got_commit_graph_find_youngest_common_ancestor(&yca_id, - wt_branch_tip, branch_tip, repo, + wt_branch_tip, branch_tip, 0, repo, check_cancelled, NULL); - if (error) - goto done; - if (yca_id == NULL) { - error = got_error_msg(GOT_ERR_ANCESTRY, - "specified branch shares no common ancestry " - "with work tree's branch"); + if (error && error->code != GOT_ERR_ANCESTRY) goto done; - } if (!continue_merge) { error = check_path_prefix(wt_branch_tip, branch_tip, @@ -10746,22 +10740,24 @@ cmd_merge(int argc, char *argv[]) GOT_ERR_MERGE_PATH, repo); if (error) goto done; - error = check_same_branch(wt_branch_tip, branch, - yca_id, repo); - if (error) { - if (error->code != GOT_ERR_ANCESTRY) + if (yca_id) { + error = check_same_branch(wt_branch_tip, branch, + yca_id, repo); + if (error) { + if (error->code != GOT_ERR_ANCESTRY) + goto done; + error = NULL; + } else { + static char msg[512]; + snprintf(msg, sizeof(msg), + "cannot create a merge commit because " + "%s is based on %s; %s can be integrated " + "with 'got integrate' instead", branch_name, + got_worktree_get_head_ref_name(worktree), + branch_name); + error = got_error_msg(GOT_ERR_SAME_BRANCH, msg); goto done; - error = NULL; - } else { - static char msg[512]; - snprintf(msg, sizeof(msg), - "cannot create a merge commit because " - "%s is based on %s; %s can be integrated " - "with 'got integrate' instead", branch_name, - got_worktree_get_head_ref_name(worktree), - branch_name); - error = got_error_msg(GOT_ERR_SAME_BRANCH, msg); - goto done; + } } error = got_worktree_merge_prepare(&fileindex, worktree, branch, repo); @@ -10774,6 +10770,14 @@ cmd_merge(int argc, char *argv[]) if (error) goto done; print_update_progress_stats(&upa); + if (!upa.did_something) { + error = got_worktree_merge_abort(worktree, fileindex, + repo, update_progress, &upa); + if (error) + goto done; + printf("Already up-to-date\n"); + goto done; + } } if (upa.conflicts > 0 || upa.missing > 0) { blob - 1538d549835d34b1e9f1400e8da7166397acd4c0 blob + 617697ddc049b408054512990ba787719cfad80a --- include/got_commit_graph.h +++ include/got_commit_graph.h @@ -32,4 +32,4 @@ const struct got_error *got_commit_graph_intersect(str /* Find the youngest common ancestor of two commits. */ const struct got_error *got_commit_graph_find_youngest_common_ancestor( struct got_object_id **, struct got_object_id *, struct got_object_id *, - struct got_repository *, got_cancel_cb, void *); + int, struct got_repository *, got_cancel_cb, void *); blob - 2369ddba562872276ef866bc1cae1e2eb9e53a19 blob + e87849acb495b5499e6729f3d7fc1a01e4e11663 --- lib/commit_graph.c +++ lib/commit_graph.c @@ -599,6 +599,7 @@ got_commit_graph_iter_next(struct got_object_id **id, const struct got_error * got_commit_graph_find_youngest_common_ancestor(struct got_object_id **yca_id, struct got_object_id *commit_id, struct got_object_id *commit_id2, + int first_parent_traversal, struct got_repository *repo, got_cancel_cb cancel_cb, void *cancel_arg) { const struct got_error *err = NULL; @@ -612,11 +613,11 @@ got_commit_graph_find_youngest_common_ancestor(struct if (commit_ids == NULL) return got_error_from_errno("got_object_idset_alloc"); - err = got_commit_graph_open(&graph, "/", 1); + err = got_commit_graph_open(&graph, "/", first_parent_traversal); if (err) goto done; - err = got_commit_graph_open(&graph2, "/", 1); + err = got_commit_graph_open(&graph2, "/", first_parent_traversal); if (err) goto done; blob - d8008aed09683d24fbe274c29012d76cc3039e85 blob + 9082a982b2d18cfec0e4a2b47e43d39474a99fa2 --- lib/send.c +++ lib/send.c @@ -163,7 +163,7 @@ check_linear_ancestry(const char *refname, struct got_ "bad object type on server for %s", refname); err = got_commit_graph_find_youngest_common_ancestor(&yca_id, - my_id, their_id, repo, cancel_cb, cancel_arg); + my_id, their_id, 1, repo, cancel_cb, cancel_arg); if (err) return err; if (yca_id == NULL) blob - 1c7ea8430ba3f5a85df6f00f7a3587641ae683ff blob + 1d534c5958a2e7da9413b01d8fcff42599c6f25b --- regress/cmdline/merge.sh +++ regress/cmdline/merge.sh @@ -1123,11 +1123,116 @@ test_merge_no_op() { (cd $testroot/wt && got status > $testroot/stdout) echo -n "" > $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" +} + +test_merge_imported_branch() { + local testroot=`test_init merge_import` + local commit0=`git_show_head $testroot/repo` + local commit0_author_time=`git_show_author_time $testroot/repo` + + # import a new sub-tree to the 'files' branch such that + # none of the files added here collide with existing ones + mkdir -p $testroot/tree/there + mkdir -p $testroot/tree/be/lots + mkdir -p $testroot/tree/files + echo "there should" > $testroot/tree/there/should + echo "be lots of" > $testroot/tree/be/lots/of + echo "files here" > $testroot/tree/files/here + got import -r $testroot/repo -b files -m 'import files' \ + $testroot/tree > /dev/null + + got checkout -b master $testroot/repo $testroot/wt > /dev/null + ret="$?" + if [ "$ret" != "0" ]; then + echo "got checkout failed unexpectedly" >&2 + test_done "$testroot" "$ret" + return 1 + fi + + (cd $testroot/wt && got merge files > $testroot/stdout) + ret="$?" + if [ "$ret" != "0" ]; then + echo "got merge failed unexpectedly" >&2 + test_done "$testroot" "$ret" + return 1 + fi + + local merge_commit0=`git_show_head $testroot/repo` + cat > $testroot/stdout.expected < $testroot/stdout) + ret="$?" + if [ "$ret" != "0" ]; then + echo "got merge failed unexpectedly" >&2 + test_done "$testroot" "$ret" + return 1 + fi + echo "Already up-to-date" > $testroot/stdout.expected 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 + + # update the 'files' branch + (cd $testroot/repo && git reset -q --hard master) + (cd $testroot/repo && git checkout -q files) + echo "indeed" > $testroot/repo/indeed + (cd $testroot/repo && git add indeed) + git_commit $testroot/repo -m "adding another file indeed" + echo "be lots and lots of" > $testroot/repo/be/lots/of + git_commit $testroot/repo -m "lots of changes" + + (cd $testroot/wt && got update > /dev/null) + ret="$?" + if [ "$ret" != "0" ]; then + echo "got update failed unexpectedly" >&2 + test_done "$testroot" "$ret" + return 1 + fi + + # we should now be able to merge more changes from files branch + (cd $testroot/wt && got merge files > $testroot/stdout) + ret="$?" + if [ "$ret" != "0" ]; then + echo "got merge failed unexpectedly" >&2 + test_done "$testroot" "$ret" + return 1 + fi + + local merge_commit1=`git_show_branch_head $testroot/repo master` + cat > $testroot/stdout.expected <