summaryrefslogtreecommitdiff
path: root/Utilities/GitSetup/git-gitlab-push
blob: 768f8534ed78e32c0489c3f6aa967eaa08b581a4 (plain)
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#!/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=''
lease=false
lease_flag=''
no_topic=''
no_default=''
dry_run=''

# Parse the command line options.
while test $# != 0; do
	case "$1" in
		-f|--force)   force='+'; lease=true ;;
		--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

	if $lease; then
		have_ref=false
		remoteref="refs/remotes/$remote/$topic"
		if git rev-parse --verify -q "$remoteref"; then
			have_ref=true
		else
			die "It seems that a local ref for the branch is
missing; forcing a push is dangerous and may overwrite
previous work.  Fetch from the $remote remote first or
push without '-f' or '--force'."
		fi

		have_lease_flag=false
		if git push -h | egrep-q -e '--force-with-lease'; then
			have_lease_flag=true
		fi

		if $have_lease_flag && $have_ref; then
			# Set the lease flag.
			lease_flag="--force-with-lease=$topic:$remoteref"
			# Clear the force string.
			force=''
		fi
	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 $lease_flag --porcelain $dry_run "$remote" $refspecs); push_exit=$?
echo "$push_stdout"

if test "$push_exit" -ne 0 && test -z "$force"; then
	# Advise the user to fetch if needed.
	if echo "$push_stdout" | egrep-q 'stale info'; then
		echo "
You have pushed to your branch from another machine; you may be overwriting
commits unintentionally.  Fetch from the $remote remote and check that you are
not pushing an outdated branch."
	fi

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

# Reproduce the push exit code.
exit $push_exit