Skip to content

Commit 1fb6fea

Browse files
committed
Initial commit
0 parents  commit 1fb6fea

File tree

4 files changed

+257
-0
lines changed

4 files changed

+257
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*~

LICENSE

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Copyright (c) 2014, Richard Hansen
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are met:
6+
7+
* Redistributions of source code must retain the above copyright notice, this
8+
list of conditions and the following disclaimer.
9+
10+
* Redistributions in binary form must reproduce the above copyright notice,
11+
this list of conditions and the following disclaimer in the documentation
12+
and/or other materials provided with the distribution.
13+
14+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
git-update-branch
2+
=================
3+
4+
Synopsis
5+
--------
6+
7+
git-update-branch [options] [--] [<branch>...]
8+
9+
Description
10+
-----------
11+
12+
Performs a fast-forward update of the given branches to the upstream
13+
tracking branch. If no branches are specified, the current branch is
14+
updated.
15+
16+
Options
17+
-------
18+
19+
* `-h`, `--help`: Print a usage message and exit.
20+
* `-a`, `--all`: Update all local branches (no branch arguments may
21+
be given).
22+
* `-q`, `--quiet`: Don't print log messages.
23+
* `--`: Treat the remaining arguments as branch names.

git-update-branch

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
#!/bin/sh
2+
3+
# Copyright (c) 2014, Richard Hansen
4+
# All rights reserved.
5+
#
6+
# Redistribution and use in source and binary forms, with or without
7+
# modification, are permitted provided that the following conditions
8+
# are met:
9+
#
10+
# * Redistributions of source code must retain the above copyright
11+
# notice, this list of conditions and the following disclaimer.
12+
#
13+
# * Redistributions in binary form must reproduce the above copyright
14+
# notice, this list of conditions and the following disclaimer in
15+
# the documentation and/or other materials provided with the
16+
# distribution.
17+
#
18+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21+
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22+
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23+
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24+
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27+
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28+
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29+
# POSSIBILITY OF SUCH DAMAGE.
30+
31+
# put all of the logic in a function so that this script can be easily
32+
# turned into a helper library by removing the last line of this file
33+
git_update_branch() {
34+
# subshell to protect the caller's environment
35+
(
36+
newline="
37+
"
38+
log_helper() { eval "${log_quiet}" || printf %s "$*"; }
39+
log() { log_helper "$*${newline}"; }
40+
logn() { log_helper "$*"; }
41+
error() { printf %s\\n "ERROR: $@" >&2; }
42+
fatal() { error "$@"; exit 1; }
43+
try() { "$@" || fatal "'$@' failed"; }
44+
usage_fatal() { error "$@"; usage >&2; exit 1; }
45+
46+
usage() {
47+
MYNAME=$(basename "$0") || fatal "basename '$0' failed"
48+
case ${MYNAME} in
49+
git-update-branch);;
50+
*) MYNAME=git_update_branch;;
51+
esac
52+
cat <<EOF
53+
Usage: ${MYNAME} [options] [--] [<branch>...]
54+
55+
Description:
56+
57+
Performs a fast-forward update of the given branches to the upstream
58+
tracking branch. If no branches are specified, the current branch
59+
is updated.
60+
61+
Options:
62+
63+
-h, --help
64+
Print this usage message and exit.
65+
66+
-a, --all
67+
Update all local branches (no branch arguments may be given).
68+
69+
-q, --quiet
70+
Don't print log messages.
71+
72+
--
73+
Treat the remaining arguments as branch names.
74+
EOF
75+
}
76+
77+
ALL=false
78+
log_quiet=false
79+
while [ "$#" -gt 0 ]; do
80+
case $1 in
81+
-h|--help) usage; exit 0;;
82+
-a|--all) ALL=true;;
83+
-q|--quiet) log_quiet=true;;
84+
--) shift; break;;
85+
*) break;;
86+
esac
87+
shift
88+
done
89+
! eval "${ALL}" || {
90+
[ "$#" -eq 0 ] \
91+
|| usage_fatal "no branches may be given if using '-a'"
92+
ALL_BRANCHES=$(git show-ref --heads) \
93+
|| fatal "unable to get list of branches"
94+
set --
95+
while IFS= read -r line; do
96+
# strip off sha1
97+
b=${line#* }
98+
# skip non-branches
99+
case ${b} in refs/heads/*);; *) continue;; esac
100+
# get abbreviated version
101+
ba=$(try git rev-parse --abbrev-ref=strict "${b}") || exit 1
102+
set -- "$@" "${ba}"
103+
done <<EOF
104+
${ALL_BRANCHES}
105+
EOF
106+
}
107+
108+
# get the name of the current branch, if applicable
109+
unset h
110+
! git symbolic-ref -q HEAD >/dev/null || {
111+
h=$(try git symbolic-ref HEAD) || exit 1
112+
# default to updating the current branch
113+
[ "$#" -gt 0 ] || {
114+
ha=$(try git rev-parse --abbrev-ref=strict "${h}") || exit 1
115+
set -- "${ha}"
116+
}
117+
}
118+
119+
ret=0
120+
for raw_b in "$@"; do
121+
eval "${ALL}" || logn "Updating ${raw_b}: "
122+
123+
# get the full branch ref
124+
b=$(git rev-parse --symbolic-full-name "${raw_b}") || {
125+
error "unknown branch ${raw_b}"
126+
ret=$((ret+1))
127+
continue
128+
}
129+
130+
# make sure there is only one branch ref
131+
num_b=$(printf '%s\n' "${b}" | wc -l)
132+
[ "${num_b}" -le 1 ] || {
133+
error "ambiguous branch name ${raw_b}"
134+
ret=$((ret+1))
135+
continue
136+
}
137+
[ "${num_b}" -ge 1 ] || {
138+
error "unknown branch ${raw_b}"
139+
ret=$((ret+1))
140+
continue
141+
}
142+
143+
# strip off 'refs/heads/' because the '@{u}' suffix
144+
# doesn't support it
145+
upstream=${b#refs/heads/}@{u}
146+
147+
has_upstream=true
148+
git rev-parse -q --verify "${upstream}" >/dev/null 2>&1 \
149+
|| has_upstream=false
150+
151+
# silently skip branches without an upstream if we're
152+
# doing all branches
153+
! eval "${ALL}" || {
154+
eval "${has_upstream}" || continue
155+
logn "Updating ${raw_b}: "
156+
}
157+
158+
# make sure there's an upstream branch
159+
eval "${has_upstream}" || {
160+
error "no upstream branch found for ${raw_b}"
161+
ret=$((ret+1))
162+
continue
163+
}
164+
165+
u=$(try git rev-parse --symbolic-full-name "${upstream}") || exit 1
166+
u_abbrev=$(try git rev-parse --abbrev-ref=strict "${u}") || exit 1
167+
168+
# see if the local branch is already ahead of the upstream
169+
# branch
170+
git merge-base --is-ancestor "${u}" "${b}"; is_ancestor=$?
171+
[ "${is_ancestor}" -le 1 ] \
172+
|| fatal "'git merge-base --is-ancestor ${u} ${b}' failed"
173+
[ "${is_ancestor}" -ne 0 ] || {
174+
log "Already up-to-date."
175+
continue
176+
}
177+
178+
# see if we can fast-forward the branch to the remote revision
179+
git merge-base --is-ancestor "${b}" "${u}"; can_ff=$?
180+
[ "${can_ff}" -le 1 ] \
181+
|| fatal "'git merge-base --is-ancestor ${b} ${u}' failed"
182+
[ "${can_ff}" -eq 0 ] || {
183+
error "${raw_b} can't be fast-forwarded to ${u_abbrev};\
184+
branches have diverged"
185+
ret=$((ret+1))
186+
continue
187+
}
188+
189+
old=$(try git rev-parse --short "${b}") || exit 1
190+
new=$(try git rev-parse --short "${u}") || exit 1
191+
192+
# do a ff merge if we have a working directory and the
193+
# branch is currently checked out.
194+
is_bare=$(try git rev-parse --is-bare-repository) || exit 1
195+
if [ "${is_bare}" = true ] \
196+
|| [ -z "${h+set}" ] \
197+
|| [ "${b}" != "${h}" ]
198+
then
199+
try git update-ref "${b}" "${u}"
200+
else
201+
try git merge -q --ff-only @{u}
202+
fi
203+
204+
log "${old}..${new}"
205+
done
206+
exit "${ret}"
207+
) || exit $?
208+
}
209+
210+
git_update_branch "$@"

0 commit comments

Comments
 (0)