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 178 179 180 181 182 183
|
#!/usr/bin/env bash
#==========================================================================
#
# Copyright Insight Software Consortium
#
# 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.txt
#
# 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>] [--no-topic] [--no-data] [--keep-data] [--dry-run] [--]"
OPTIONS_SPEC=
SUBDIRECTORY_OK=Yes
. "$(git --exec-path)/git-sh-setup"
egrep-q() {
egrep "$@" >/dev/null 2>/dev/null
}
data_commit() {
# Get data refs. Skip if none.
test $# != 0 || return 0
state=$(git for-each-ref "$@") || return
test -n "$state" || return 0
# Convert each data ref to an index entry.
index=$(
echo "$state" |
while read obj type refname; do
# Take blobs with valid ref names.
name="${refname#refs/data/}"
if echo "$type,$name" | egrep-q '^blob,[A-Za-z0-9-]+/[0-9A-Fa-f]+$'; then
# Place the blob at the path named by the ref.
echo "100644 $obj 0 $name"
else
# Warn about unprocessed refs.
echo "unknown $refname" 1>&2
fi
done
) || return
test -n "$index" || return 0
# Convert the index into a tree.
tree=$(
GIT_INDEX_FILE="$GIT_DIR/tmp-index.$$.$RANDOM" &&
export GIT_INDEX_FILE &&
trap "rm -f '$GIT_INDEX_FILE'" EXIT &&
rm -f "$GIT_INDEX_FILE" &&
echo "$index" | git update-index --index-info &&
git write-tree
) &&
# Store the tree in a commit object.
echo 'data' | git commit-tree "$tree"
}
data_remove() {
test -z "$dry_run" || return 0
git ls-tree -r "$1" |
while read mode type obj name; do
# Remove ref only if it still has the data we expected.
git update-ref -d "refs/data/$name" "$obj" 2>/dev/null || true
done
}
data_report_and_remove() {
if test -n "$keep_data"; then
action="kept"
else
action="removed"
data_remove "$1" || true
fi &&
echo "Pushed refs/data and $action local copy:" &&
git ls-tree --name-only -r "$1" | sed "s/^/ /"
}
data_push_refspec() {
echo "$1:refs/data/commits/$1"
}
data_refs() {
git rev-list "$@" |
git diff-tree --no-commit-id --root -c -r --diff-filter=AM --stdin |
egrep '\.(md5)$' |
# read :srcmode dstmode srcobj dstobj status file
while read _ _ _ obj _ file; do
# Identify the hash algorithm used.
case "$file" in
*.md5) algo=MD5 ; validate="^[0-9a-fA-F]{32}$" ;;
*) continue ;;
esac
# Load and validate the hash.
if hash=$(git cat-file blob $obj 2>/dev/null) &&
echo "$hash" | egrep-q "$validate"; then
echo "refs/data/$algo/$hash"
fi
done
}
#-----------------------------------------------------------------------------
remote=''
refspecs=''
keep_data=''
no_topic=''
no_data=''
dry_run=''
# Parse command line options.
while test $# != 0; do
case "$1" in
--keep-data) keep_data=1 ;;
--no-topic) no_topic=1 ;;
--no-data) no_data=1 ;;
--dry-run) dry_run=--dry-run ;;
--) shift; break ;;
-*) usage ;;
*) test -z "$remote" || usage ; remote="$1" ;;
esac
shift
done
test $# = 0 || usage
# Default remote.
test -n "$remote" || remote="gerrit"
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="HEAD:refs/for/master/$topic $refspecs"
fi
# Fetch the current upstream 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"
master=$(git rev-parse FETCH_HEAD) || exit
if test -z "$no_data"; then
# Create a commit containing the data to push.
data_refs=$(data_refs $master..) &&
data=$(data_commit $data_refs) || die 'Failed to create data commit'
if test -n "$data"; then
refspecs="$(data_push_refspec "$data") $refspecs"
fi
else
data=''
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_stdout=$(git push --porcelain $dry_run "$remote" $refspecs); push_exit=$?
echo "$push_stdout"
# Check if data were pushed successfully.
if test -n "$data" &&
echo "$push_stdout" | egrep-q "^[*=+] $data"; then
data_report_and_remove "$data"
fi
# Reproduce the push exit code.
exit $push_exit
|