From 2f65b790bcd3ce4675c8a3ae059aee565f59f4ce Mon Sep 17 00:00:00 2001 From: Mathias Bogaert Date: Sat, 20 Sep 2025 23:19:42 +0100 Subject: [PATCH] feat: Add rebase strategy options to put operation - Add rebase_strategy parameter for merge strategies - Add rebase_strategy_option parameter for strategy options (e.g., -Xtheirs) - Enables automated conflict resolution in rebase workflows - Update schema and tests Implements #414 Signed-off-by: Mathias Bogaert --- assets/out | 30 ++++++- assets/out_schema.json | 2 + test/helpers.sh | 42 ++++++++++ test/put.sh | 185 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 257 insertions(+), 2 deletions(-) diff --git a/assets/out b/assets/out index 5d3b9d67..304162e5 100755 --- a/assets/out +++ b/assets/out @@ -40,6 +40,8 @@ repository=$(jq -r '.params.repository // ""' <<< "$payload") tag=$(jq -r '.params.tag // ""' <<< "$payload") tag_prefix=$(jq -r '.params.tag_prefix // ""' <<< "$payload") rebase=$(jq -r '.params.rebase // false' <<< "$payload") +rebase_strategy=$(jq -r '.params.rebase_strategy // ""' <<< "$payload") +rebase_strategy_option=$(jq -r '.params.rebase_strategy_option // ""' <<< "$payload") merge=$(jq -r '.params.merge // false' <<< "$payload") returning=$(jq -r '.params.returning // "merged"' <<< "$payload") force=$(jq -r '.params.force // false' <<< "$payload") @@ -189,11 +191,35 @@ elif [ "$merge" = "true" ]; then echo "merging and trying again..." done elif [ "$rebase" = "true" ]; then + rebase_cmd="git pull --rebase=merges" + + # Add strategy if specified + if [ -n "$rebase_strategy" ]; then + rebase_cmd="$rebase_cmd --strategy=$rebase_strategy" + fi + + # Add strategy options if specified, can be string or array + if [ -n "$rebase_strategy_option" ] && [ "$rebase_strategy_option" != "null" ]; then + # Check if it's an array or string + if echo "$rebase_strategy_option" | jq -e 'type == "array"' > /dev/null 2>&1; then + # It's already an array from jq + strategy_options=$(echo "$rebase_strategy_option" | jq -r '.[] | "-X" + .') + for opt in $strategy_options; do + rebase_cmd="$rebase_cmd $opt" + done + else + # It's a string - could be space-separated options + for opt in $rebase_strategy_option; do + rebase_cmd="$rebase_cmd -X$opt" + done + fi + fi + while true; do - echo "rebasing..." + echo "rebasing using rebase command: $rebase_cmd push-target $branch..." git fetch push-target "refs/notes/*:refs/notes/*" - git pull --rebase=merges push-target $branch + $rebase_cmd push-target $branch result="0" push_with_result_check result diff --git a/assets/out_schema.json b/assets/out_schema.json index 6f83b7c1..6eae2faf 100644 --- a/assets/out_schema.json +++ b/assets/out_schema.json @@ -1,6 +1,8 @@ { "repository": "", "rebase": "", + "rebase_strategy": "", + "rebase_strategy_option": "", "merge": "", "returning": "", "tag": "", diff --git a/test/helpers.sh b/test/helpers.sh index 91034142..76074432 100644 --- a/test/helpers.sh +++ b/test/helpers.sh @@ -1296,6 +1296,48 @@ put_uri_with_rebase_with_tag_and_prefix() { }" | ${resource_dir}/out "$2" | tee /dev/stderr } +put_uri_with_rebase_strategy_option() { + jq -n "{ + source: { + uri: $(echo $1 | jq -R .), + branch: \"master\" + }, + params: { + repository: $(echo $3 | jq -R .), + rebase: true, + rebase_strategy_option: $(echo $4 | jq -R .) + } + }" | ${resource_dir}/out "$2" | tee /dev/stderr +} + +put_uri_with_rebase_multiple_options_string() { + jq -n "{ + source: { + uri: $(echo $1 | jq -R .), + branch: \"master\" + }, + params: { + repository: $(echo $3 | jq -R .), + rebase: true, + rebase_strategy_option: \"theirs ignore-space-change\" + } + }" | ${resource_dir}/out "$2" | tee /dev/stderr +} + +put_uri_with_rebase_multiple_options_array() { + jq -n "{ + source: { + uri: $(echo $1 | jq -R .), + branch: \"master\" + }, + params: { + repository: $(echo $3 | jq -R .), + rebase: true, + rebase_strategy_option: [\"theirs\", \"ignore-space-change\"] + } + }" | ${resource_dir}/out "$2" | tee /dev/stderr +} + put_uri_with_notes() { jq -n "{ source: { diff --git a/test/put.sh b/test/put.sh index b5d88421..d0d8c70a 100755 --- a/test/put.sh +++ b/test/put.sh @@ -261,6 +261,187 @@ it_can_put_to_url_with_rebase_with_tag_and_prefix() { test "$(git -C $repo1 rev-parse v1.0)" = $rebased_ref } + +it_can_put_to_url_with_rebase_strategy_options() { + local repo1=$(init_repo) + + local src=$(mktemp -d $TMPDIR/put-src.XXXXXX) + local repo2=$src/repo + git clone $repo1 $repo2 + + # create initial commit with a file both will modify + echo "original" > $repo1/conflict-file + git -C $repo1 add conflict-file + git -C $repo1 commit -m "initial" + git -C $repo2 pull + + # make a commit that will conflict when rebasing + local baseref=$(make_commit_to_file $repo1 conflict-file) + + # make a conflicting local commit + echo "local" > $repo2/conflict-file + git -C $repo2 add conflict-file + git -C $repo2 commit -m "local change" + + # cannot push to repo while it's checked out to a branch + git -C $repo1 checkout refs/heads/master + + local response=$(mktemp $TMPDIR/rebased-response.XXXXXX) + + put_uri_with_rebase_strategy_option $repo1 $src repo "theirs" > $response + + local rebased_ref=$(git -C $repo2 rev-parse HEAD) + + jq -e " + .version == {ref: $(echo $rebased_ref | jq -R .)} + " < $response + + # switch back to master + git -C $repo1 checkout master + + test -e $repo1/conflict-file + test "$(git -C $repo1 rev-parse HEAD)" = $rebased_ref +} + +it_can_put_with_rebase_ignore_space_change() { + local repo1=$(init_repo) + + local src=$(mktemp -d $TMPDIR/put-src.XXXXXX) + local repo2=$src/repo + git clone $repo1 $repo2 + + # create a file with specific spacing + printf "line1\nline2 \nline3\n" > $repo1/spacefile + git -C $repo1 add spacefile + git -C $repo1 commit -m "initial spacing" + git -C $repo2 pull + + # repo1: change content but keep trailing spaces + printf "line1-changed\nline2 \nline3\n" > $repo1/spacefile + git -C $repo1 add spacefile + git -C $repo1 commit -m "content change" + + # repo2: remove trailing spaces but keep old content + printf "line1\nline2\nline3-changed\n" > $repo2/spacefile + git -C $repo2 add spacefile + git -C $repo2 commit -m "spacing change" + + # cannot push to repo while it's checked out to a branch + git -C $repo1 checkout refs/heads/master + + local response=$(mktemp $TMPDIR/rebased-response.XXXXXX) + + put_uri_with_rebase_strategy_option $repo1 $src repo "ignore-space-change" > $response + + local rebased_ref=$(git -C $repo2 rev-parse HEAD) + + jq -e " + .version == {ref: $(echo $rebased_ref | jq -R .)} + " < $response + + # switch back to master + git -C $repo1 checkout master + + # verify the rebase succeeded and content is merged + grep "line1-changed" $repo1/spacefile + grep "line3-changed" $repo1/spacefile + test "$(git -C $repo1 rev-parse HEAD)" = $rebased_ref +} + +it_can_put_with_rebase_multiple_strategy_options_string() { + local repo1=$(init_repo) + + local src=$(mktemp -d $TMPDIR/put-src.XXXXXX) + local repo2=$src/repo + git clone $repo1 $repo2 + + # create a file with spacing issues + printf "line1\nline2 \nline3\n" > $repo1/testfile + git -C $repo1 add testfile + git -C $repo1 commit -m "initial" + git -C $repo2 pull + + # repo1: modify line1 and clean up spacing on line2 + printf "line1-remote\nline2\nline3\n" > $repo1/testfile + git -C $repo1 add testfile + git -C $repo1 commit -m "remote changes" + + # repo2: modify line3 but keep spacing on line2 + printf "line1\nline2 \nline3-local\n" > $repo2/testfile + git -C $repo2 add testfile + git -C $repo2 commit -m "local changes" + + # cannot push to repo while it's checked out to a branch + git -C $repo1 checkout refs/heads/master + + local response=$(mktemp $TMPDIR/rebased-response.XXXXXX) + + # This uses both "theirs" and "ignore-space-change" + put_uri_with_rebase_multiple_options_string $repo1 $src repo > $response + + local rebased_ref=$(git -C $repo2 rev-parse HEAD) + + jq -e " + .version == {ref: $(echo $rebased_ref | jq -R .)} + " < $response + + # switch back to master + git -C $repo1 checkout master + + # Should have remote's line1 change + grep "line1-remote" $repo1/testfile + # Should have local's line3 change (non-conflicting) + grep "line3-local" $repo1/testfile + test "$(git -C $repo1 rev-parse HEAD)" = $rebased_ref +} + +it_can_put_with_rebase_multiple_strategy_options_array() { + local repo1=$(init_repo) + + local src=$(mktemp -d $TMPDIR/put-src.XXXXXX) + local repo2=$src/repo + git clone $repo1 $repo2 + + # create a file with spacing issues + printf "line1\nline2 \nline3\n" > $repo1/testfile + git -C $repo1 add testfile + git -C $repo1 commit -m "initial" + git -C $repo2 pull + + # repo1: modify line1 and clean up spacing on line2 + printf "line1-remote\nline2\nline3\n" > $repo1/testfile + git -C $repo1 add testfile + git -C $repo1 commit -m "remote changes" + + # repo2: modify line3 but keep spacing on line2 + printf "line1\nline2 \nline3-local\n" > $repo2/testfile + git -C $repo2 add testfile + git -C $repo2 commit -m "local changes" + + # cannot push to repo while it's checked out to a branch + git -C $repo1 checkout refs/heads/master + + local response=$(mktemp $TMPDIR/rebased-response.XXXXXX) + + # This uses both "theirs" and "ignore-space-change" + put_uri_with_rebase_multiple_options_array $repo1 $src repo > $response + + local rebased_ref=$(git -C $repo2 rev-parse HEAD) + + jq -e " + .version == {ref: $(echo $rebased_ref | jq -R .)} + " < $response + + # switch back to master + git -C $repo1 checkout master + + # Should have remote's line1 change + grep "line1-remote" $repo1/testfile + # Should have local's line3 change (non-conflicting) + grep "line3-local" $repo1/testfile + test "$(git -C $repo1 rev-parse HEAD)" = $rebased_ref +} + it_can_put_to_url_with_merge_commit() { local repo1=$(init_repo) @@ -631,6 +812,10 @@ run it_can_put_to_url_with_rebase_with_notes run it_can_put_to_url_with_rebase run it_can_put_to_url_with_rebase_with_tag run it_can_put_to_url_with_rebase_with_tag_and_prefix +run it_can_put_to_url_with_rebase_strategy_options +run it_can_put_with_rebase_ignore_space_change +run it_can_put_with_rebase_multiple_strategy_options_string +run it_can_put_with_rebase_multiple_strategy_options_array run it_will_fail_put_if_merge_and_rebase_are_set run it_can_put_to_url_with_merge_commit run it_chooses_the_unmerged_commit_ref