Repository navigation
Expand file tree
/
Copy pathgit-sync-upstream
More file actions
executable file
·155 lines (136 loc) · 4.03 KB
/
Copy pathgit-sync-upstream
File metadata and controls
executable file
·155 lines (136 loc) · 4.03 KB
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
#!/usr/bin/env bash
#
# Requirements:
# - git
#
# Author: James Cherti
# URL: https://github.com/jamescherti/bash-stdops
#
# Description:
# ------------
# This script synchronizes the current Git branch with its upstream counterpart
# and force-pushes the result to the 'origin' remote. It is intended for
# workflows where a local branch is kept in sync with an upstream source of
# truth, and the mirror on 'origin' must match upstream exactly.
#
# The script performs the following actions:
# 1. Verifies that both 'origin' and 'upstream' remotes are defined.
# 2. Performs a rebase of the current branch onto its upstream equivalent.
# 3. Displays the diff between the rebased branch and the remote 'origin'.
# 4. Prompts for confirmation unless run in batch mode.
# 5. Merges upstream changes with --ff-only and force-pushes to 'origin'.
#
# Intended for use in CI workflows or manual synchronization where upstream is
# authoritative.
#
# Usage:
# git-sync-upstream [-h] [-b]
# -h Show help message and exit
# -b Run in batch mode (no interactive prompts)
#
# License:
# --------
# Copyright (C) 2012-2026 James Cherti
#
# Distributed under terms of the GNU General Public License version 3.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
set -euo pipefail
# shellcheck disable=SC2317
error_handler() {
local errno="$?"
echo "Error: ${BASH_SOURCE[1]}:${BASH_LINENO[0]}" \
"(${BASH_COMMAND} exited with status $errno)" >&2
exit "${errno}"
}
show_usage() {
echo "Usage: $0 [-h] [-b] [-t]" >&2
echo
echo " -h Show this help message and exit"
echo " -b Run in batch mode (skip interactive confirmations)"
echo " -t Synchronize tags from upstream to origin"
}
main() {
trap "error_handler" ERR
set -o errtrace
BATCH_MODE=0
SYNC_TAGS=0
while getopts ":hbt" opt; do
case "$opt" in
h)
show_usage
exit 0
;;
b)
BATCH_MODE=1
;;
t)
SYNC_TAGS=1
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument" >&2
exit 1
;;
esac
done
shift $((OPTIND - 1))
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "Error: Not in a Git repository." >&2
exit 1
fi
for remote in origin upstream; do
if ! git remote | grep -q "^${remote}$"; then
echo "Error: Remote '$remote' is not defined." >&2
exit 1
fi
done
CURRENT_BRANCH=$(git symbolic-ref --short HEAD)
REMOTE_ORIGIN=$(git config --get remote.origin.url || true)
echo "[SYNC MIRROR] $REMOTE_ORIGIN (Branch: $CURRENT_BRANCH)"
if [[ $BATCH_MODE -eq 0 ]]; then
read -r -p "Synchronize the '${CURRENT_BRANCH}' branch? [y,n] " ANSWER
[[ "$ANSWER" != "y" ]] && exit 1
fi
git pull -q --rebase upstream "$CURRENT_BRANCH"
if [[ $SYNC_TAGS -eq 1 ]]; then
git fetch -q upstream --tags
fi
if git diff --quiet "origin/$CURRENT_BRANCH" "$CURRENT_BRANCH" 2>/dev/null; then
if [[ $SYNC_TAGS -eq 1 ]]; then
git push origin --tags
else
echo "Nothing to do."
fi
exit 0
fi
git diff "origin/$CURRENT_BRANCH" "$CURRENT_BRANCH" || true
if [[ $BATCH_MODE -eq 0 ]]; then
read -r -p "Proceed? [y,n] " ANSWER
[[ "$ANSWER" != "y" ]] && {
echo "Aborted." >&2
exit 1
}
fi
git merge "upstream/$CURRENT_BRANCH" --ff-only --quiet
git push origin "$CURRENT_BRANCH" --force
if [[ $SYNC_TAGS -eq 1 ]]; then
git push origin --tags
fi
}
main "$@"