paulirish.git-open/git-open
Edward Thomson c956af1b63 Parse remotes as URLs or SCP-style paths
Instead of trying to strip on ':' and '/' to simplify a URL, actually
switch based on whether the remote path is a URL ("scheme://host:port/path")
or an SCP-style path (user@host:path) and parse them separately.

This allows us to handle custom ports in HTTP and HTTPS, but ignore
custom ports in an SSH url and HTTP remotes, instead of always upgrading
them to HTTPS.
2017-11-08 13:49:23 +00:00

164 lines
4.5 KiB
Bash
Executable file

#!/usr/bin/env bash
# Use git-sh-setup, similar to git-rebase
# https://www.kernel.org/pub/software/scm/git/docs/git-sh-setup.html
# https://github.com/git/git/blob/master/git-rebase.sh
# shellcheck disable=SC2034
OPTIONS_STUCKLONG=t
# shellcheck disable=SC2034
OPTIONS_KEEPDASHDASH=
# shellcheck disable=SC2034
OPTIONS_SPEC="\
git open [options]
git open [remote] [branch]
--
Opens the GitHub page for a repo/branch in your browser.
https://github.com/paulirish/git-open/
Available options are
i,issue! open issues page
"
# https://github.com/koalaman/shellcheck/wiki/SC1090
# shellcheck source=/dev/null
. "$(git --exec-path)/git-sh-setup"
# Defaults
is_issue=0
protocol="https"
while test $# != 0; do
case "$1" in
--issue) is_issue=1;;
--) shift; break ;;
esac
shift
done
# 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
# choose remote. priority to: provided argument, detected tracked remote, 'origin'
branch_name=$(git name-rev --name-only HEAD 2>/dev/null)
tracked_remote=$(git config "branch.$branch_name.remote")
remote=${1:-$tracked_remote}
remote=${remote:-"origin"}
# @TODO ls-remote will also expand "insteadOf" items `giturl=$(git ls-remote --get-url $remote)``
giturl=$(git config --get "remote.${remote}.url")
if [[ -z "$giturl" ]]; then
echo "Git remote is not set for $remote" 1>&2
exit 1
fi
# From git-fetch(5), native protocols:
# ssh://[user@]host.xz[:port]/path/to/repo.git/
# git://host.xz[:port]/path/to/repo.git/
# http[s]://host.xz[:port]/path/to/repo.git/
# ftp[s]://host.xz[:port]/path/to/repo.git/
# [user@]host.xz:path/to/repo.git/ - scp-like but is an alternative to ssh.
# Determine whether this is a url (https, ssh, git+ssh...) or an scp-style path
if [[ "$giturl" =~ ^[a-z\+]+://.* ]]; then
# Trim URL scheme and possible username
gitprotocol=${giturl%%://*}
uri=${giturl#*://}
uri=${uri#*@}
# Split on first '/ to get server name and path
domain=${uri%%/*}
urlpath=${uri#*/}
# Remove port number from non-http/https protocols (ie, ssh)
if [[ $gitprotocol != 'https' && $gitprotocol != 'http' ]]; then
domain=${domain%:*}
fi
else
# Trim possible username from SSH path
uri=${giturl##*@}
# Split on first ':' to get server name and path
domain=${uri%%:*}
urlpath=${uri#*:}
fi
# Trim "/" from beginning of URL; "/" and ".git" from end of URL
urlpath=${urlpath#/} urlpath=${urlpath%/} urlpath=${urlpath%.git}
# If the URL is provided as "http", preserve that
if [[ $gitprotocol == 'http' ]]; then
protocol='http'
fi
# Allow config options to replace the server or the protocol
openurl="$protocol://$domain"
function getConfig() {
config=$(git config --get-urlmatch "open.$1" "$openurl")
echo "${config:-${!1}}"
}
domain=$(getConfig "domain")
protocol=$(getConfig "protocol")
# Get current branch
branch=${2:-$(git symbolic-ref -q --short HEAD)}
# Split arguments on '/'
IFS='/' pathargs=($urlpath)
if (( is_issue )); then
# For issues, take the numbers and preprend 'issues/'
providerBranchRef="issues/${branch//[^0-9]/}"
else
# Make # and % characters url friendly
# github.com/paulirish/git-open/pull/24
branch=${branch//%/%25} branch=${branch//#/%23}
providerBranchRef="tree/$branch"
fi
if [[ "$domain" == 'bitbucket.org' ]]; then
# Bitbucket, see https://github.com/paulirish/git-open/issues/80 for why ?at is needed.
providerBranchRef="src?at=$branch"
elif [[ ${pathargs[0]} == 'scm' ]]; then
# Bitbucket server, which starts with 'scm'
# Replace the first element, 'scm', with 'projects'. Keep the first argument, the string 'repos', and finally the rest of the arguments.
pathargs=('projects' ${pathargs[1]} 'repos' "${pathargs[@]:2}")
IFS='/' urlpath="${pathargs[*]}"
providerBranchRef="browse?at=$branch"
fi
openurl="$protocol://$domain/$urlpath"
# simplify URL for master
if [[ $branch != "master" ]]; then
openurl="$openurl/$providerBranchRef"
fi
# get current open browser command
case $( uname -s ) in
Darwin) open='open';;
MINGW*) open='start';;
MSYS*) open='start';;
CYGWIN*) open='cygstart';;
*) # Try to detect WSL (Windows Subsystem for Linux)
if uname -r | grep -q Microsoft; then
open='powershell.exe'
openopt='Start'
else
open='xdg-open'
fi;;
esac
# Allow printing the url if BROWSER=echo
if [[ $BROWSER != "echo" ]]; then
exec &>/dev/null
fi
# open it in a browser
${BROWSER:-$open} $openopt "$openurl"