Download raw body.
allow merging tags
Make 'got merge' accept tags as merge source argument.
Reuse a tag-resolution function from tog.c, moving it to the lib directory
such that the got command can make use of it as well.
Add test coverage.
ok?
I need this for github-specific workflows used in the Plakar project.
M got/got.c | 2+ 1-
M include/got_reference.h | 10+ 0-
M lib/reference.c | 63+ 0-
M regress/cmdline/merge.sh | 107+ 0-
M tog/tog.c | 2+ 64-
5 files changed, 184 insertions(+), 65 deletions(-)
commit - 69ac886cd64b82483fbb0e3114eb447f1d2ff9e0
commit + b15b671e96f233b655a2ea7de6d1bcc617c738b7
blob - 7b2602c1b70e154f1f39d3222ca03af18120b5ff
blob + f57ee6d3b40565d80708f683462754d670794ab1
--- got/got.c
+++ got/got.c
@@ -13928,7 +13928,8 @@ cmd_merge(int argc, char *argv[])
error = got_error_from_errno("strdup");
goto done;
}
- error = got_ref_resolve(&branch_tip, repo, branch);
+ error = got_ref_resolve_commit_or_tag(&branch_tip, repo,
+ branch);
if (error)
goto done;
}
blob - 55e4df44b40d37a2a722f120946119046d3edc41
blob + 2a94c23cc790b37daf1e5b78619811eb1faeb399
--- include/got_reference.h
+++ include/got_reference.h
@@ -91,6 +91,16 @@ const struct got_error *got_ref_resolve(struct got_obj
struct got_repository *, struct got_reference *);
/*
+ * Attempt to resolve a reference (symbolic or not) to an object ID,
+ * assuming the reference points at a commit or tag object. If the reference
+ * points at a tag then return the commit ID which the tag resolves to.
+ * Object types other than commit and tag result in GOT_ERR_OBJ_TYPE.
+ *
+ */
+const struct got_error *got_ref_resolve_commit_or_tag(struct got_object_id **,
+ struct got_repository *, struct got_reference *);
+
+/*
* Return a string representation of a reference.
* The caller must dispose of it with free(3).
*/
blob - 792d13a093587be73459dbc1856937bce61a1b6c
blob + 40573c4679ab45fe6119a377d17ee557b87498f2
--- lib/reference.c
+++ lib/reference.c
@@ -613,6 +613,69 @@ got_ref_resolve(struct got_object_id **id, struct got_
return ref_resolve(id, repo, ref, GOT_REF_RECURSE_MAX);
}
+const struct got_error *
+got_ref_resolve_commit_or_tag(struct got_object_id **commit_id,
+ struct got_repository *repo, struct got_reference *ref)
+{
+ const struct got_error *err = NULL;
+ struct got_object_id *obj_id;
+ struct got_tag_object *tag = NULL;
+ int obj_type;
+
+ *commit_id = NULL;
+
+ err = got_ref_resolve(&obj_id, repo, ref);
+ if (err)
+ return err;
+
+ err = got_object_get_type(&obj_type, repo, obj_id);
+ if (err)
+ goto done;
+
+ switch (obj_type) {
+ case GOT_OBJ_TYPE_COMMIT:
+ break;
+ case GOT_OBJ_TYPE_TAG:
+ /*
+ * Git allows nested tags that point to tags; keep peeling
+ * till we reach the bottom, which is always a non-tag ref.
+ */
+ do {
+ if (tag != NULL)
+ got_object_tag_close(tag);
+ err = got_object_open_as_tag(&tag, repo, obj_id);
+ if (err)
+ goto done;
+ free(obj_id);
+ obj_id = got_object_id_dup(
+ got_object_tag_get_object_id(tag));
+ if (obj_id == NULL) {
+ err = got_error_from_errno("got_object_id_dup");
+ goto done;
+ }
+ err = got_object_get_type(&obj_type, repo, obj_id);
+ if (err)
+ goto done;
+ } while (obj_type == GOT_OBJ_TYPE_TAG);
+ if (obj_type != GOT_OBJ_TYPE_COMMIT)
+ err = got_error(GOT_ERR_OBJ_TYPE);
+ break;
+ default:
+ err = got_error(GOT_ERR_OBJ_TYPE);
+ break;
+ }
+
+done:
+ if (tag)
+ got_object_tag_close(tag);
+ if (err == NULL)
+ *commit_id = obj_id;
+ else
+ free(obj_id);
+ return err;
+}
+
+
char *
got_ref_to_str(struct got_reference *ref)
{
blob - cd6a8a4afe865e1846927b4aa4b6ef571c40bee8
blob + e367646e800cc49403b273328b99c40f5eda42cd
--- regress/cmdline/merge.sh
+++ regress/cmdline/merge.sh
@@ -2012,6 +2012,112 @@ test_merge_fetched_branch_remote() {
test_done "$testroot" "$ret"
}
+test_merge_tag() {
+ local testroot=`test_init merge_tag`
+ local commit0=`git_show_head $testroot/repo`
+ local commit0_author_time=`git_show_author_time $testroot/repo`
+
+ git -C $testroot/repo checkout -q -b newbranch
+ echo "modified delta on branch" > $testroot/repo/gamma/delta
+ git_commit $testroot/repo -m "committing to delta on newbranch"
+ local branch_commit=`git_show_branch_head $testroot/repo newbranch`
+
+ got checkout -b master $testroot/repo $testroot/wt > /dev/null
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "got checkout failed unexpectedly" >&2
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # create a divergent commit
+ git -C $testroot/repo checkout -q master
+ echo "modified zeta on master" > $testroot/repo/epsilon/zeta
+ git_commit $testroot/repo -m "committing to zeta on master"
+ local master_commit=`git_show_head $testroot/repo`
+
+
+ # tag the branch
+ got tag -r $testroot/repo -c newbranch -m "1.0" 1.0 > /dev/null
+
+ # need an up-to-date work tree for 'got merge'
+ (cd $testroot/wt && got update > /dev/null)
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "got update failed unexpectedly" >&2
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ (cd $testroot/wt && got merge 1.0 > $testroot/stdout)
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "got merge failed unexpectedly" >&2
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ local merge_commit=`git_show_head $testroot/repo`
+
+ echo "G gamma/delta" >> $testroot/stdout.expected
+ echo -n "Merged refs/tags/1.0 into refs/heads/master: " \
+ >> $testroot/stdout.expected
+ echo $merge_commit >> $testroot/stdout.expected
+
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo "modified delta on branch" > $testroot/content.expected
+ cat $testroot/wt/gamma/delta > $testroot/content
+ cmp -s $testroot/content.expected $testroot/content
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/content.expected $testroot/content
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+
+ (cd $testroot/wt && got status > $testroot/stdout)
+
+ echo -n > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ (cd $testroot/wt && got log -l3 | grep ^commit > $testroot/stdout)
+ echo "commit $merge_commit (master)" > $testroot/stdout.expected
+ echo "commit $master_commit" >> $testroot/stdout.expected
+ echo "commit $commit0" >> $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # We should have created a merge commit with two parents.
+ (cd $testroot/wt && got log -l1 | grep ^parent > $testroot/stdout)
+ echo "parent 1: $master_commit" > $testroot/stdout.expected
+ echo "parent 2: $branch_commit" >> $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ fi
+ test_done "$testroot" "$ret"
+}
+
test_parseargs "$@"
run_test test_merge_basic
run_test test_merge_forward
@@ -2031,3 +2137,4 @@ run_test test_merge_umask
run_test test_merge_gitconfig_author
run_test test_merge_fetched_branch
run_test test_merge_fetched_branch_remote
+run_test test_merge_tag
blob - 26d4f62fa1f5983edc7f7f2c380fbdc94facf7d5
blob + f4120d83d6a20bb1906c735a5037e405bdfb9790
--- tog/tog.c
+++ tog/tog.c
@@ -10072,68 +10072,6 @@ close_ref_view(struct tog_view *view)
}
static const struct got_error *
-resolve_reflist_entry(struct got_object_id **commit_id,
- struct tog_reflist_entry *re, struct got_repository *repo)
-{
- const struct got_error *err = NULL;
- struct got_object_id *obj_id;
- struct got_tag_object *tag = NULL;
- int obj_type;
-
- *commit_id = NULL;
-
- err = got_ref_resolve(&obj_id, repo, re->ref);
- if (err)
- return err;
-
- err = got_object_get_type(&obj_type, repo, obj_id);
- if (err)
- goto done;
-
- switch (obj_type) {
- case GOT_OBJ_TYPE_COMMIT:
- break;
- case GOT_OBJ_TYPE_TAG:
- /*
- * Git allows nested tags that point to tags; keep peeling
- * till we reach the bottom, which is always a non-tag ref.
- */
- do {
- if (tag != NULL)
- got_object_tag_close(tag);
- err = got_object_open_as_tag(&tag, repo, obj_id);
- if (err)
- goto done;
- free(obj_id);
- obj_id = got_object_id_dup(
- got_object_tag_get_object_id(tag));
- if (obj_id == NULL) {
- err = got_error_from_errno("got_object_id_dup");
- goto done;
- }
- err = got_object_get_type(&obj_type, repo, obj_id);
- if (err)
- goto done;
- } while (obj_type == GOT_OBJ_TYPE_TAG);
- if (obj_type != GOT_OBJ_TYPE_COMMIT)
- err = got_error(GOT_ERR_OBJ_TYPE);
- break;
- default:
- err = got_error(GOT_ERR_OBJ_TYPE);
- break;
- }
-
-done:
- if (tag)
- got_object_tag_close(tag);
- if (err == NULL)
- *commit_id = obj_id;
- else
- free(obj_id);
- return err;
-}
-
-static const struct got_error *
log_ref_entry(struct tog_view **new_view, int begin_y, int begin_x,
struct tog_reflist_entry *re, struct got_repository *repo)
{
@@ -10143,7 +10081,7 @@ log_ref_entry(struct tog_view **new_view, int begin_y,
*new_view = NULL;
- err = resolve_reflist_entry(&commit_id, re, repo);
+ err = got_ref_resolve_commit_or_tag(&commit_id, repo, re->ref);
if (err)
return err;
@@ -10466,7 +10404,7 @@ browse_ref_tree(struct tog_view **new_view, int begin_
*new_view = NULL;
- err = resolve_reflist_entry(&commit_id, re, repo);
+ err = got_ref_resolve_commit_or_tag(&commit_id, repo, re->ref);
if (err)
return err;
allow merging tags