git-gitlab-push 4.76 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
#!/usr/bin/env bash
#=============================================================================
# Copyright 2010-2015 Kitware, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#=============================================================================

USAGE='[<remote>] [<options>...] [--]

OPTIONS

--dry-run
    Show what would be pushed without actually updating the destination

-f,--force
    Force-push the topic HEAD to rewrite the destination branch

--no-default
    Do not push the default branch (e.g. master)

--no-topic
    Do not push the topic HEAD.
'
OPTIONS_SPEC=
SUBDIRECTORY_OK=Yes
. "$(git --exec-path)/git-sh-setup"

egrep-q() {
	egrep "$@" >/dev/null 2>/dev/null
}

# Load the project configuration.
gitlab_upstream='' &&
gitlab_configured='' &&
config="${BASH_SOURCE%/*}/config" &&
protocol=$(git config -f "$config" --get gitlab.protocol ||
	   echo "https") &&
host=$(git config -f "$config" --get gitlab.host) &&
site=$(git config -f "$config" --get gitlab.site ||
       echo "$protocol://$host") &&
group_path=$(git config -f "$config" --get gitlab.group-path) &&
project_path=$(git config -f "$config" --get gitlab.project-path) &&
gitlab_upstream="$site/$group_path/$project_path.git" &&
gitlab_pushurl=$(git config --get remote.gitlab.pushurl ||
		 git config --get remote.gitlab.url) &&
gitlab_configured=1

#-----------------------------------------------------------------------------

remote=''
refspecs=''
force=''
no_topic=''
no_default=''
dry_run=''

# Parse the command line options.
while test $# != 0; do
	case "$1" in
		-f|--force)   force='+' ;;
		--no-topic)   no_topic=1 ;;
		--dry-run)    dry_run=--dry-run ;;
		--no-default) no_default=1 ;;
		--) shift; break ;;
		-*) usage ;;
		*) test -z "$remote" || usage ; remote="$1" ;;
	esac
	shift
done
test $# = 0 || usage

# Default remote.
test -n "$remote" || remote="gitlab"

if test -z "$no_topic"; then
	# Identify and validate the topic branch name.
	head="$(git symbolic-ref HEAD)" && topic="${head#refs/heads/}" || topic=''
	if test -z "$topic" -o "$topic" = "master"; then
		die 'Please name your topic:
		git checkout -b descriptive-name'
	fi
	# The topic branch will be pushed by name.
	refspecs="${force}HEAD:refs/heads/$topic $refspecs"
fi

# Fetch the current remote master branch head.
# This helps computation of a minimal pack to push.
echo "Fetching $remote master"
fetch_out=$(git fetch "$remote" master 2>&1) || die "$fetch_out"
gitlab_head=$(git rev-parse FETCH_HEAD) || exit

# Fetch the current upstream master branch head.
if origin_fetchurl=$(git config --get remote.origin.url) &&
   test "$origin_fetchurl" = "$gitlab_upstream"; then
	upstream_remote='origin'
else
	upstream_remote="$gitlab_upstream"
fi
echo "Fetching $upstream_remote master"
fetch_out=$(git fetch "$upstream_remote" master 2>&1) || die "$fetch_out"
upstream_head=$(git rev-parse FETCH_HEAD) || exit

# Add a refspec to keep the remote master up to date if possible.
if test -z "$no_default" &&
   base=$(git merge-base "$gitlab_head" "$upstream_head") &&
   test "$base" = "$gitlab_head"; then
	refspecs="$upstream_head:refs/heads/master $refspecs"
fi

# Exit early if we have nothing to push.
if test -z "$refspecs"; then
	echo 'Nothing to push!'
	exit 0
fi

# Push.  Save output and exit code.
echo "Pushing to $remote"
push_config='-c advice.pushUpdateRejected=false'
push_stdout=$(git $push_config push --porcelain $dry_run "$remote" $refspecs); push_exit=$?
echo "$push_stdout"

# Advise the user to force-push if needed.
if test "$push_exit" -ne 0 && test -z "$force" &&
   echo "$push_stdout" | egrep-q 'non-fast-forward'; then
	echo '
Add "-f" or "--force" to push a rewritten topic.'
fi

# Tell the user what to do next with the topic in GitLab.
if test -z "$no_topic" &&
   test "$push_exit" -eq 0 &&
   test "$remote" = "gitlab" &&
   test -n "$gitlab_configured" &&
   echo "$gitlab_pushurl" | egrep-q "$host[^/:]*[/:][^/]*/$project_path\\.git$"; then
	userpath="${gitlab_pushurl%/*.git}" &&
	username="${userpath##*[/:]}" &&
	echo '
The topic has been pushed to your fork in GitLab.  Visit

  '"$site/$username/$project_path/tree/$topic"'

to see the files.  Visit

  '"$site/$username/$project_path/merge_requests/new?merge_request[source_branch]=$topic"'

to create a Merge Request if there is not one already.'
fi

# Reproduce the push exit code.
exit $push_exit