From 8cd8741a3fafbf769030b575c457de97804e26de Mon Sep 17 00:00:00 2001 From: Vijay Misal Date: Wed, 19 Aug 2026 18:14:30 +0530 Subject: [PATCH] feat(mr): support Forgejo/Codeberg pull request URLs Codeberg and other Forgejo instances expose pull requests under refs/pull//head instead of GitLab's refs/merge-requests//head, so passing a Codeberg pull request URL to git mr fetched the wrong ref. Recognize the /pulls/ URL shape and switch to the matching ref for that case, leaving the GitLab URL and bare numeric id behavior unchanged. Closes #1213 --- Commands.md | 13 +++++++++++-- bin/git-mr | 10 +++++++++- man/git-mr.md | 11 ++++++++++- tests/git-mr.bats | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 tests/git-mr.bats diff --git a/Commands.md b/Commands.md index 0c6ddbf..4cc2eee 100644 --- a/Commands.md +++ b/Commands.md @@ -1428,8 +1428,8 @@ $ git reset-file .htaccess dc82b19 ## git mr -Checks out a merge request from GitLab. Usage: `git mr [REMOTE]`. -Default remote is `origin`. +Checks out a merge request from GitLab, or a pull request from Forgejo/Codeberg. +Usage: `git mr [REMOTE]`. Default remote is `origin`. ``` bash $ git mr 51 @@ -1447,6 +1447,15 @@ From gitlab.com:owner/repository Switched to branch 'mr/51' ``` +A Forgejo/Codeberg pull request URL is also supported: + +``` bash +$ git mr https://codeberg.org/owner/repository/pulls/51 +From codeberg.org:owner/repository + * [new ref] refs/pull/51/head -> mr/51 +Switched to branch 'mr/51' +``` + Just like [git pr](#git-pr), `git mr` accepts a `clean` argument to trash all `mr/` branches. Ensure current branch is not one. diff --git a/bin/git-mr b/bin/git-mr index 9a359d3..5cac8df 100755 --- a/bin/git-mr +++ b/bin/git-mr @@ -7,6 +7,8 @@ if [ -z "${1-}" ] ; then exit 1 fi +remote_ref_kind=merge-requests + if test "$1" = "clean"; then git for-each-ref refs/heads/mr/* --format='%(refname)' | while read -r ref; do git branch -D "${ref#refs/heads/}" @@ -15,13 +17,19 @@ if test "$1" = "clean"; then elif [[ $1 =~ ^(https?://[^/]+/(.+))/merge_requests/([0-9]+).*$ ]]; then remote=${BASH_REMATCH[1]}.git id=${BASH_REMATCH[3]} +elif [[ $1 =~ ^(https?://[^/]+/(.+))/pulls/([0-9]+).*$ ]]; then + # Forgejo/Codeberg pull request URL, e.g. + # https://codeberg.org/owner/repository/pulls/453 + remote=${BASH_REMATCH[1]}.git + id=${BASH_REMATCH[3]} + remote_ref_kind=pull else id=$1 remote=${2:-origin} fi branch=mr/$id -remote_ref=refs/merge-requests/$id/head +remote_ref=refs/$remote_ref_kind/$id/head git fetch -fu "$remote" "$remote_ref:$branch" git checkout "$branch" git config --local --replace "branch.$branch.merge" "$remote_ref" diff --git a/man/git-mr.md b/man/git-mr.md index 03a62fd..4f9b2b8 100644 --- a/man/git-mr.md +++ b/man/git-mr.md @@ -21,7 +21,9 @@ git-mr(1) -- Checks out a merge request locally <url> GitLab merge request URL in the format - `https://gitlab.tld/owner/repository/merge_requests/453`. + `https://gitlab.tld/owner/repository/merge_requests/453`, or a + Forgejo/Codeberg pull request URL in the format + `https://codeberg.tld/owner/repository/pulls/453`. ## EXAMPLES @@ -33,6 +35,13 @@ This checks out merge request `!51` from remote `origin` to branch `mr/51`. * [new ref] refs/merge-requests/51/head -> mr/51 Switched to branch 'mr/51' +This checks out pull request `#51` from a Forgejo/Codeberg URL to branch `mr/51`. + + $ git mr https://codeberg.org/owner/repository/pulls/51 + From codeberg.org:owner/repository + * [new ref] refs/pull/51/head -> mr/51 + Switched to branch 'mr/51' + ## AUTHOR Written by Étienne BERSAC from git-pr(1). diff --git a/tests/git-mr.bats b/tests/git-mr.bats new file mode 100644 index 0000000..9f7e1f9 --- /dev/null +++ b/tests/git-mr.bats @@ -0,0 +1,48 @@ +# shellcheck shell=bash + +source "$BATS_TEST_DIRNAME/test_util.sh" + +setup_file() { + test_util.setup_file + + PATH="$BATS_TEST_DIRNAME/bin:$PATH" +} + +setup() { + test_util.cd_test + + test_util.git_init + touch ./tracked + git add ./tracked + git commit -m 'Initial commit' + + # Local bare "remote" standing in for a real GitLab/Forgejo instance, + # with a merge-requests ref and a pull ref pointing at the same commit. + remote_dir="$BATS_TEST_TMPDIR/remote.git" + git init --bare --initial-branch main "$remote_dir" + git push "$remote_dir" HEAD:refs/merge-requests/51/head + git push "$remote_dir" HEAD:refs/pull/51/head + + git remote add origin "$remote_dir" +} + +@test "checks out a merge request by numeric id" { + run git mr 51 + assert_success + assert_output --partial "refs/merge-requests/51/head" + + run git rev-parse --abbrev-ref HEAD + assert_output 'mr/51' + + run git config --get branch.mr/51.merge + assert_output 'refs/merge-requests/51/head' +} + +@test "checks out a merge request by numeric id and explicit remote" { + run git mr 51 "$remote_dir" + assert_success + assert_output --partial "refs/merge-requests/51/head" + + run git rev-parse --abbrev-ref HEAD + assert_output 'mr/51' +}