mirror of
https://github.com/paulirish/git-open.git
synced 2026-09-10 07:26:15 -04:00
Merge c29a7ab503 into 63c0e77aaf
This commit is contained in:
commit
102a907a66
24
README.md
24
README.md
|
|
@ -18,13 +18,23 @@ git open --issue
|
|||
git open -i
|
||||
# If this branch is named like issue/#123, this will open the corresponding
|
||||
# issue in the repo website
|
||||
# Supports multiple formats: issue/123, bugfix-123, #123, feature/123, fix-123, etc.
|
||||
|
||||
git open --pull-request
|
||||
git open -r
|
||||
# Open the pull requests / merge requests page for the repository
|
||||
# Supports GitHub, GitLab, Bitbucket, Azure DevOps, and cnb.cool
|
||||
|
||||
git open --path
|
||||
# Open the current directory path in the repo website
|
||||
# Useful when you want to browse the current folder in the browser
|
||||
|
||||
git open --print
|
||||
git open -p
|
||||
# Only print the url at the terminal, but don't open it
|
||||
```
|
||||
|
||||
(`git open` works with these [hosted repo providers](#supported-remote-repositories), `git open --issue` currently only works with GitHub, Visual Studio Team Services and Team Foundation Server)
|
||||
(`git open` works with these [hosted repo providers](#supported-remote-repositories), `git open --issue` currently only works with GitHub, GitLab, Visual Studio Team Services and Team Foundation Server)
|
||||
|
||||
### Examples
|
||||
|
||||
|
|
@ -39,14 +49,24 @@ $ git open someremote somebranch
|
|||
# opens https://github.com/PROVIDED_REMOTE_USER/CURRENT_REPO/tree/PROVIDED_BRANCH
|
||||
|
||||
$ git open --issue
|
||||
# If branches use naming convention of issues/#123,
|
||||
# If branches use naming convention of issues/#123, bugfix-456, feature-789, etc.
|
||||
# opens https://github.com/TRACKED_REMOTE_USER/CURRENT_REPO/issues/123
|
||||
|
||||
$ git open --pull-request
|
||||
# opens https://github.com/TRACKED_REMOTE_USER/CURRENT_REPO/pulls (GitHub)
|
||||
# or https://gitlab.example.com/user/repo/-/merge_requests (GitLab)
|
||||
|
||||
$ git open --path
|
||||
# opens https://github.com/TRACKED_REMOTE_USER/CURRENT_REPO/tree/CURRENT_BRANCH/current/directory
|
||||
|
||||
$ git open --print
|
||||
# prints https://github.com/TRACKED_REMOTE_USER/CURRENT_REPO/tree/CURRENT_BRANCH
|
||||
|
||||
$ git open --suffix pulls
|
||||
# opens https://github.com/TRACKED_REMOTE_USER/CURRENT_REPO/pulls
|
||||
|
||||
$ git open --file README.md
|
||||
# opens https://github.com/TRACKED_REMOTE_USER/CURRENT_REPO/tree/CURRENT_BRANCH/README.md
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
|
|
|||
107
git-open
107
git-open
|
|
@ -18,8 +18,10 @@ git open [remote] [branch]
|
|||
Available options are
|
||||
c,commit! open current commit
|
||||
i,issue! open issues page
|
||||
r,pull-request! open pull request page
|
||||
s,suffix= append this suffix
|
||||
f,file= append this file
|
||||
path! open current directory path
|
||||
p,print! just print the url
|
||||
"
|
||||
|
||||
|
|
@ -30,17 +32,22 @@ SUBDIRECTORY_OK='Yes' . "$(git --exec-path)/git-sh-setup"
|
|||
# Defaults
|
||||
is_commit=0
|
||||
is_issue=0
|
||||
is_pull_request=0
|
||||
protocol="https"
|
||||
print_only=0
|
||||
suffix_flag=""
|
||||
file_flag=""
|
||||
path_flag=""
|
||||
|
||||
while test $# != 0; do
|
||||
case "$1" in
|
||||
--commit) is_commit=1;;
|
||||
--issue) is_issue=1;;
|
||||
--pull-request) is_pull_request=1;;
|
||||
-r) is_pull_request=1;;
|
||||
--suffix=*) suffix_flag="$1";;
|
||||
--file=*) file_flag="$1";;
|
||||
--path) path_flag=1;;
|
||||
--print) print_only=1;;
|
||||
--) shift; break ;;
|
||||
esac
|
||||
|
|
@ -52,16 +59,28 @@ IFS='=' read -ra suffix_flag <<< "$suffix_flag"
|
|||
function join_by { local IFS="$1"; shift; echo "$*"; }
|
||||
suffix=$(join_by "=" "${suffix_flag[@]:1}")
|
||||
|
||||
# parse file from file=value
|
||||
IFS='=' read -ra file_flag <<< "$file_flag"
|
||||
file=$(join_by "=" "${file_flag[@]:1}")
|
||||
|
||||
# are we in a git repo?
|
||||
if ! git rev-parse --is-inside-work-tree &>/dev/null; then
|
||||
echo "Not a git repository." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# parse file from file=value
|
||||
IFS='=' read -ra file_flag <<< "$file_flag"
|
||||
file=$(join_by "=" "${file_flag[@]:1}")
|
||||
|
||||
# handle --path: use current directory relative to repo root
|
||||
path_suffix=""
|
||||
if [[ "$path_flag" == "1" ]]; then
|
||||
repo_root=$(git rev-parse --show-toplevel)
|
||||
current_dir=$(pwd)
|
||||
if [[ "$current_dir" != "$repo_root" ]]; then
|
||||
# Get relative path from repo root
|
||||
rel_path="${current_dir#"$repo_root"/}"
|
||||
path_suffix="$rel_path"
|
||||
fi
|
||||
fi
|
||||
|
||||
# choose remote. priority to: provided argument, default in config, detected tracked remote, 'origin'
|
||||
branch=${2:-$(git symbolic-ref -q --short HEAD)}
|
||||
upstream_branch_full_name=$(git config "branch.$branch.merge")
|
||||
|
|
@ -183,10 +202,47 @@ remote_ref=${upstream_branch:-${branch:-$(git describe --tags --exact-match 2>/d
|
|||
# Split arguments on '/'
|
||||
IFS='/' read -r -a pathargs <<<"$urlpath"
|
||||
|
||||
# Extract issue number from branch name
|
||||
# Supports formats: issue/123, bugfix-123, #123, issues/123, feature/123, fix-123, etc.
|
||||
function extract_issue_number() {
|
||||
local branch_name="$1"
|
||||
local issue_num=""
|
||||
|
||||
# Try various patterns: issue/123, bugfix-123, #123, etc.
|
||||
if [[ "$branch_name" =~ (^|[-/])(issues?|bugfix|feature|fix|hotfix)?[-/]?[#]?([0-9]+) ]]; then
|
||||
issue_num="${BASH_REMATCH[3]}"
|
||||
elif [[ "$branch_name" =~ ^#?([0-9]+)$ ]]; then
|
||||
# Just a number like #123 or 123
|
||||
issue_num="${BASH_REMATCH[1]}"
|
||||
fi
|
||||
|
||||
echo "$issue_num"
|
||||
}
|
||||
|
||||
if (( is_issue )); then
|
||||
# For issues, take the numbers and preprend 'issues/'
|
||||
[[ $remote_ref =~ [0-9]+ ]]
|
||||
providerBranchRef="/issues/${BASH_REMATCH[0]}"
|
||||
issue_number=$(extract_issue_number "$remote_ref")
|
||||
if [[ -z "$issue_number" ]]; then
|
||||
echo "Could not extract issue number from branch name: $remote_ref" 1>&2
|
||||
echo "Supported formats: issue/123, bugfix-123, #123, 123, etc." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
providerBranchRef="/issues/$issue_number"
|
||||
elif (( is_pull_request )); then
|
||||
# Generate pull request URL based on platform
|
||||
if [[ "$domain" == 'github.com' || "$domain" == *'github'* ]]; then
|
||||
providerBranchRef="/pulls"
|
||||
elif [[ "$domain" == 'gitlab.com' || "$domain" == *'gitlab'* || "$forge" == 'gitlab' ]]; then
|
||||
providerBranchRef="/-/merge_requests"
|
||||
elif [[ "$domain" == 'bitbucket.org' || "$domain" == *'bitbucket'* ]]; then
|
||||
providerBranchRef="/pull-requests"
|
||||
elif [[ "$domain" == *'visualstudio.com'* || "$domain" == *'azure.com'* || "$domain" == *'dev.azure.com'* ]]; then
|
||||
providerBranchRef="/_git/pullrequests"
|
||||
elif [[ "$domain" =~ cnb\.cool$ ]]; then
|
||||
providerBranchRef="/-/merge_requests"
|
||||
else
|
||||
# Default to GitHub-style /pulls
|
||||
providerBranchRef="/pulls"
|
||||
fi
|
||||
else
|
||||
# Make # and % characters url friendly
|
||||
# github.com/paulirish/git-open/pull/24
|
||||
|
|
@ -198,7 +254,11 @@ else
|
|||
fi
|
||||
fi
|
||||
|
||||
if [[ "$domain" == 'bitbucket.org' ]]; then
|
||||
# Skip provider-specific branch ref modifications for pull-request only
|
||||
# (issue handling is provider-specific and needs to be processed below)
|
||||
if (( is_pull_request )); then
|
||||
: # Do nothing, keep the providerBranchRef set earlier
|
||||
elif [[ "$domain" == 'bitbucket.org' ]]; then
|
||||
providerBranchRef="/src/$remote_ref"
|
||||
elif [[ "${#pathargs[@]}" -ge 3 && ${pathargs[${#pathargs[@]} - 3]} == 'scm' ]]; then
|
||||
# Bitbucket server always has /scm/ as the third to last segment in the url path, e.g. /scm/ppp/test-repo.git
|
||||
|
|
@ -257,12 +317,18 @@ elif [[ "$domain" =~ amazonaws\.com$ ]]; then
|
|||
providerBranchRef="${providerBranchRef##*/}/--/"
|
||||
elif [[ "$domain" =~ cnb\.cool$ ]]; then
|
||||
# cnb.cool
|
||||
# Replace URL path.
|
||||
# Ex. repos/example -> repos/example/-
|
||||
urlpath="$urlpath/-"
|
||||
if [[ $remote_ref = "master" ]]; then
|
||||
# repos/example/tree/master -> repos/example/-/tree/master
|
||||
urlpath="$urlpath$providerBranchRef"
|
||||
if (( is_issue || is_pull_request )); then
|
||||
# For issue and pull-request, add the path prefix to urlpath
|
||||
# providerBranchRef already starts with /issues/ or /-/merge_requests
|
||||
urlpath="$urlpath/-"
|
||||
else
|
||||
# Replace URL path.
|
||||
# Ex: repos/example -> repos/example/-
|
||||
urlpath="$urlpath/-"
|
||||
if [[ $remote_ref = "master" ]]; then
|
||||
# repos/example/tree/master -> repos/example/-/tree/master
|
||||
urlpath="$urlpath$providerBranchRef"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
openurl="$protocol://$domain/$urlpath"
|
||||
|
|
@ -270,7 +336,13 @@ openurl="$protocol://$domain/$urlpath"
|
|||
if (( is_commit )); then
|
||||
sha=$(git rev-parse HEAD)
|
||||
openurl="$openurl/commit/$sha"
|
||||
elif [[ $remote_ref != "master" || "$file" ]]; then
|
||||
elif (( is_pull_request )); then
|
||||
# For pull requests, just add the PR path
|
||||
openurl="$openurl$providerBranchRef"
|
||||
elif (( is_issue )); then
|
||||
# For issues, just add the issue path
|
||||
openurl="$openurl$providerBranchRef"
|
||||
elif [[ $remote_ref != "master" || "$file" || "$path_suffix" ]]; then
|
||||
# simplify URL for master
|
||||
openurl="$openurl$providerBranchRef"
|
||||
fi
|
||||
|
|
@ -284,6 +356,11 @@ if [ "$file" ]; then
|
|||
openurl="$openurl/$absfile"
|
||||
fi
|
||||
|
||||
# Add path_suffix from --path option
|
||||
if [ "$path_suffix" ]; then
|
||||
openurl="$openurl/$path_suffix"
|
||||
fi
|
||||
|
||||
if [ "$suffix" ]; then
|
||||
openurl="$openurl/$suffix"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
## SYNOPSIS
|
||||
|
||||
`git open` [--issue] [--commit] [--suffix some_suffix] [remote-name] [branch-name]
|
||||
`git open` [--issue] [--commit] [--pull-request] [--path] [--suffix some_suffix] [--file some_file] [remote-name] [branch-name]
|
||||
|
||||
|
||||
## DESCRIPTION
|
||||
|
|
@ -15,14 +15,40 @@ git hosting services are supported.
|
|||
## OPTIONS
|
||||
|
||||
`-c`, `--commit`
|
||||
Open the current commit. See `EXAMPLES` for more information.
|
||||
Open the current commit. See `EXAMPLES` for more information.
|
||||
Only tested with GitHub & GitLab.
|
||||
|
||||
`-i`, `--issue`
|
||||
Open the current issue. When the name of the current branch matches the right pattern,
|
||||
it will open the webpage with that issue. See `EXAMPLES` for more information.
|
||||
Open the current issue. When the name of the current branch matches the right pattern,
|
||||
it will open the webpage with that issue. See `EXAMPLES` for more information.
|
||||
This only works on GitHub, GitLab, Visual Studio Team Services and Team Foundation Server at the moment.
|
||||
|
||||
Supported branch name formats:
|
||||
- `issue/123`, `issues/123`
|
||||
- `bugfix-123`, `bugfix/123`
|
||||
- `feature-123`, `feature/123`
|
||||
- `fix-123`, `fix/123`
|
||||
- `hotfix-123`, `hotfix/123`
|
||||
- `#123` (just the issue number with hash)
|
||||
|
||||
`-r`, `--pull-request`
|
||||
Open the pull requests / merge requests page for the repository.
|
||||
The URL format is automatically determined based on the git hosting service:
|
||||
- GitHub: `/pulls`
|
||||
- GitLab: `/-/merge_requests`
|
||||
- Bitbucket: `/pull-requests`
|
||||
- Azure DevOps: `/_git/pullrequests`
|
||||
- cnb.cool: `/-/merge_requests`
|
||||
|
||||
`--path`
|
||||
Open the current directory path in the repo website. This uses the current
|
||||
working directory relative to the repository root to construct the URL.
|
||||
Useful when you want to browse the current folder in the browser.
|
||||
|
||||
`-f`, `--file` some_file
|
||||
Append the given file path to the URL. The file path is relative to the
|
||||
repository root. Use `--path` to automatically use the current directory.
|
||||
|
||||
`-s`, `--suffix` some_suffix
|
||||
Append the given suffix to the url
|
||||
|
||||
|
|
@ -56,9 +82,28 @@ It opens https://github.com/PROVIDED_REMOTE_USER/CURRENT_REPO/tree/PROVIDED_BRAN
|
|||
git open --issue
|
||||
```
|
||||
|
||||
If branches use naming convention of `issues/#123`, it opens
|
||||
If branches use naming convention of `issues/#123`, `bugfix-123`, `feature/456`, etc., it opens
|
||||
https://github.com/TRACKED_REMOTE_USER/CURRENT_REPO/issues/123
|
||||
|
||||
```sh
|
||||
git open --pull-request
|
||||
```
|
||||
|
||||
It opens the URL https://github.com/TRACKED_REMOTE_USER/CURRENT_REPO/pulls (GitHub)
|
||||
or https://gitlab.example.com/user/repo/-/merge_requests (GitLab)
|
||||
|
||||
```sh
|
||||
git open --path
|
||||
```
|
||||
|
||||
It opens the URL https://github.com/TRACKED_REMOTE_USER/CURRENT_REPO/tree/CURRENT_BRANCH/current/directory
|
||||
|
||||
```sh
|
||||
git open --file README.md
|
||||
```
|
||||
|
||||
It opens the URL https://github.com/TRACKED_REMOTE_USER/CURRENT_REPO/tree/CURRENT_BRANCH/README.md
|
||||
|
||||
```sh
|
||||
git open --suffix pulls
|
||||
```
|
||||
|
|
|
|||
|
|
@ -666,6 +666,140 @@ setup() {
|
|||
assert_output "https://cnb.cool/repos/repo/-/issues/10"
|
||||
}
|
||||
|
||||
##
|
||||
## Enhanced --issue with multiple branch name formats
|
||||
##
|
||||
|
||||
@test "gh: git open --issue with bugfix- prefix" {
|
||||
git remote set-url origin "github.com:paulirish/git-open.git"
|
||||
git checkout -B "bugfix-123"
|
||||
run ../git-open "--issue"
|
||||
assert_output "https://github.com/paulirish/git-open/issues/123"
|
||||
}
|
||||
|
||||
@test "gh: git open --issue with feature/ prefix" {
|
||||
git remote set-url origin "github.com:paulirish/git-open.git"
|
||||
git checkout -B "feature/456"
|
||||
run ../git-open "--issue"
|
||||
assert_output "https://github.com/paulirish/git-open/issues/456"
|
||||
}
|
||||
|
||||
@test "gh: git open --issue with fix- prefix" {
|
||||
git remote set-url origin "github.com:paulirish/git-open.git"
|
||||
git checkout -B "fix-789"
|
||||
run ../git-open "--issue"
|
||||
assert_output "https://github.com/paulirish/git-open/issues/789"
|
||||
}
|
||||
|
||||
@test "gh: git open --issue with hotfix- prefix" {
|
||||
git remote set-url origin "github.com:paulirish/git-open.git"
|
||||
git checkout -B "hotfix-999"
|
||||
run ../git-open "--issue"
|
||||
assert_output "https://github.com/paulirish/git-open/issues/999"
|
||||
}
|
||||
|
||||
@test "gh: git open --issue with # prefix" {
|
||||
git remote set-url origin "github.com:paulirish/git-open.git"
|
||||
git checkout -B "#42"
|
||||
run ../git-open "--issue"
|
||||
assert_output "https://github.com/paulirish/git-open/issues/42"
|
||||
}
|
||||
|
||||
@test "gh: git open --issue error when no issue number found" {
|
||||
git remote set-url origin "github.com:paulirish/git-open.git"
|
||||
git checkout -B "some-random-branch"
|
||||
run ../git-open "--issue"
|
||||
[ "$status" -eq 1 ]
|
||||
assert_output --partial "Could not extract issue number from branch name"
|
||||
assert_output --partial "Supported formats: issue/123, bugfix-123, #123, 123, etc."
|
||||
}
|
||||
|
||||
##
|
||||
## --pull-request tests
|
||||
##
|
||||
|
||||
@test "gh: git open --pull-request" {
|
||||
git remote set-url origin "git@github.com:user/repo.git"
|
||||
run ../git-open "--pull-request"
|
||||
assert_output "https://github.com/user/repo/pulls"
|
||||
}
|
||||
|
||||
@test "gh: git open -r (short option)" {
|
||||
git remote set-url origin "git@github.com:user/repo.git"
|
||||
run ../git-open "-r"
|
||||
assert_output "https://github.com/user/repo/pulls"
|
||||
}
|
||||
|
||||
@test "gitlab: git open --pull-request" {
|
||||
git remote set-url origin "git@gitlab.com:user/repo.git"
|
||||
run ../git-open "--pull-request"
|
||||
assert_output "https://gitlab.com/user/repo/-/merge_requests"
|
||||
}
|
||||
|
||||
@test "gitlab: git open -r (short option)" {
|
||||
git remote set-url origin "git@gitlab.com:user/repo.git"
|
||||
run ../git-open "-r"
|
||||
assert_output "https://gitlab.com/user/repo/-/merge_requests"
|
||||
}
|
||||
|
||||
@test "bitbucket: git open --pull-request" {
|
||||
git remote set-url origin "git@bitbucket.org:user/repo.git"
|
||||
run ../git-open "--pull-request"
|
||||
assert_output "https://bitbucket.org/user/repo/pull-requests"
|
||||
}
|
||||
|
||||
@test "bitbucket: git open -r (short option)" {
|
||||
git remote set-url origin "git@bitbucket.org:user/repo.git"
|
||||
run ../git-open "-r"
|
||||
assert_output "https://bitbucket.org/user/repo/pull-requests"
|
||||
}
|
||||
|
||||
@test "cnb: git open --pull-request" {
|
||||
git remote set-url origin "https://cnb.cool/repos/repo"
|
||||
run ../git-open "--pull-request"
|
||||
assert_output "https://cnb.cool/repos/repo/-/merge_requests"
|
||||
}
|
||||
|
||||
@test "cnb: git open -r (short option)" {
|
||||
git remote set-url origin "https://cnb.cool/repos/repo"
|
||||
run ../git-open "-r"
|
||||
assert_output "https://cnb.cool/repos/repo/-/merge_requests"
|
||||
}
|
||||
|
||||
##
|
||||
## --path tests
|
||||
##
|
||||
|
||||
@test "gh: git open --path in root directory" {
|
||||
git remote set-url origin "git@github.com:user/repo.git"
|
||||
run ../git-open "--path"
|
||||
assert_output "https://github.com/user/repo"
|
||||
}
|
||||
|
||||
@test "gh: git open --path in subdirectory" {
|
||||
git remote set-url origin "git@github.com:user/repo.git"
|
||||
mkdir -p subdir/nested
|
||||
# Create a file in the directory so git tracks it
|
||||
touch subdir/nested/.gitkeep
|
||||
git add subdir
|
||||
git commit -m "add subdir" -q
|
||||
# Run from the subdirectory with explicit cd
|
||||
run bash -c 'cd subdir/nested && "$0" --path' "$BATS_TEST_DIRNAME/../git-open"
|
||||
assert_output "https://github.com/user/repo/tree/master/subdir/nested"
|
||||
}
|
||||
|
||||
@test "gh: git open --path combined with branch" {
|
||||
git remote set-url origin "git@github.com:user/repo.git"
|
||||
git checkout -B "feature-branch"
|
||||
mkdir -p src/components
|
||||
touch src/components/.gitkeep
|
||||
git add src
|
||||
git commit -m "add src dir" -q
|
||||
# Run from the subdirectory with explicit cd
|
||||
run bash -c 'cd src/components && "$0" --path' "$BATS_TEST_DIRNAME/../git-open"
|
||||
assert_output "https://github.com/user/repo/tree/feature-branch/src/components"
|
||||
}
|
||||
|
||||
teardown() {
|
||||
cd ..
|
||||
rm -rf "$foldername"
|
||||
|
|
|
|||
Loading…
Reference in a new issue