forked from dpocock/sync2git
-
Notifications
You must be signed in to change notification settings - Fork 5
/
sync2git
executable file
·205 lines (172 loc) · 6.35 KB
/
sync2git
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#!/bin/bash
set -e
function cleanup_lock {
if [ ! -z "${_LOCK_FILE}" ];
then
if [ -e "${_LOCK_FILE}" ];
then
echo "Deleting stray lockfile..."
rm -rf "${_LOCK_FILE}"
fi
fi
}
# Synchronize GIT repo to SVN, *without* changing commit hashes
# in GIT. This is only 1-way synchronization, from GIT to SVN, so never commit
# to SVN anymore (with any other means than this script).
#
# Advantage over normal sync2git synchronization (with COMMIT_GIT_TO_SVN=true)
# is that GIT repo is unchanged by this. GIT commit hashes are unchanged.
# We effectively cancel out the "git commit hash wrangling" done by normal
# "git svn dcommit" + "git svn rebase" combo (that commits to SVN, then gets
# from SVN, and same GIT commit gets different hash).
#
# TODO: this requires that "${SVN_CLONE}" already exists and is initialized.
function synchronize_only_git2svn ()
{
if [ ! -d "${SVN_CLONE}/.git" ];
then
echo "git-svn clone ({SVN_CLONE}/.git) does not exist, ONLY_COMMIT_GIT_TO_SVN implemetation is not ready for this!"
exit 1
fi
echo "git2svn synchronization for: ${PROJECT_NAME}"
local LAST_COMMITTED_REVISION_FILE="${PROJECT_ROOT}/last-committed-revision-to-svn.txt"
cd "${SVN_CLONE}"
if [ ! -f "${LAST_COMMITTED_REVISION_FILE}" ]; then
local CURRENT_GIT_HEAD=`git show-ref --head --hash HEAD | head -n 1`
echo "First run with ONLY_COMMIT_GIT_TO_SVN, assuming that everything up to and including ${CURRENT_GIT_HEAD} is already synchronized with SVN."
echo "${CURRENT_GIT_HEAD}" > "${LAST_COMMITTED_REVISION_FILE}"
fi
local PREVIOUS_GIT_HEAD="`cat \"${LAST_COMMITTED_REVISION_FILE}\"`"
git pull --rebase
local CURRENT_GIT_HEAD=`git show-ref --head --hash HEAD | head -n 1`
if [ "${PREVIOUS_GIT_HEAD}" = "${CURRENT_GIT_HEAD}" ]; then
echo "Nothing to commit, latest GIT revision ${CURRENT_GIT_HEAD} is already synchronized to SVN."
else
local TEMP_COMMIT_FILE="${PROJECT_ROOT}/commit-message.txt"
git log --pretty=format:"%s (by %an)" "${PREVIOUS_GIT_HEAD}..${CURRENT_GIT_HEAD}" > "${TEMP_COMMIT_FILE}"
echo >> "${TEMP_COMMIT_FILE}"
echo >> "${TEMP_COMMIT_FILE}"
echo 'Full log from GIT:' >> "${TEMP_COMMIT_FILE}"
git log "${PREVIOUS_GIT_HEAD}..${CURRENT_GIT_HEAD}" >> "${TEMP_COMMIT_FILE}"
local SVN_URL="`git svn info --url`"
echo "Commiting this:"
cat "${TEMP_COMMIT_FILE}"
# We use "git svn commit-diff" instead of "git svn dcommit", because "git svn dcommit"
# assumes that all previous commits have either magic "git-svn-id" inside their log message,
# or should be committed to SVN. We cannot work around it, using "git svn dcommit --revision"
# is too weak, it still sends to large change.
#
# So we use commit-diff, that has some disadvantages: it always makes a single SVN commit,
# even for multiple GIT commits, and we have to construct like log message ourselves.
echo 'Running:' git svn commit-diff -rHEAD --file="${TEMP_COMMIT_FILE}" "${PREVIOUS_GIT_HEAD}" "${CURRENT_GIT_HEAD}" "${SVN_URL}"
git svn commit-diff -rHEAD --file="${TEMP_COMMIT_FILE}" "${PREVIOUS_GIT_HEAD}" "${CURRENT_GIT_HEAD}" "${SVN_URL}"
echo "${CURRENT_GIT_HEAD}" > "${LAST_COMMITTED_REVISION_FILE}"
fi
}
trap cleanup_lock EXIT
ETC_PROJECTS=/etc/sync2git/projects
if [ $# -eq 0 ];
then
PROJECTS=`cd "${ETC_PROJECTS}" ; ls`
else
PROJECTS="$@"
fi
for PROJECT_NAME in ${PROJECTS}
do
_LOCK_FILE=
# Set some defaults (can be overridden by config file)
ETC_DIR=/etc/sync2git/projects/${PROJECT_NAME}
VAR_DIR=/var/lib/sync2git
PROJECT_ROOT=${VAR_DIR}/${PROJECT_NAME}
CONFIG_FILE=${ETC_DIR}/config
AUTHORS_FILE=${ETC_DIR}/authors.txt
SVN_LAYOUT="--stdlayout"
# Need to reset all variables here, because this code block may execute for multiple projects.
COMMIT_GIT_TO_SVN=''
ONLY_COMMIT_GIT_TO_SVN=''
. "${CONFIG_FILE}" || exit 1
if [ ! -d "${PROJECT_ROOT}" ];
then
mkdir -p "${PROJECT_ROOT}"
fi
LOCK_FILE="${PROJECT_ROOT}/.lock"
if [ -e "${LOCK_FILE}" ];
then
echo "lock file ${LOCK_FILE} exists, aborting"
exit 1
else
touch "${LOCK_FILE}"
# now it is our lock file, we have to clean it up:
_LOCK_FILE="${LOCK_FILE}"
fi
if [ ! -z "$AUTHORS_URL" ];
then
AUTHORS_FILE="${PROJECT_ROOT}/authors.txt"
wget -q -O "${AUTHORS_FILE}" "${AUTHORS_URL}"
fi
SVN_CLONE="${PROJECT_ROOT}/svn-clone"
if [ ! -z "$ONLY_COMMIT_GIT_TO_SVN" ];
then
synchronize_only_git2svn
rm -rf "${LOCK_FILE}"
continue
fi
GIT_BARE=`mktemp -d 2>/dev/null || mktemp -d -t 'sync2git'`
cd "${PROJECT_ROOT}"
if [ ! -d "${SVN_CLONE}" ];
then
echo "First run, doing a full git-svn clone, this may take a while..."
git svn clone \
"${SVN_REPO}" \
${FETCH_ARGS} \
--prefix=origin/ \
-A "${AUTHORS_FILE}" \
${SVN_LAYOUT} \
"${SVN_CLONE}"
cd "${SVN_CLONE}"
else
echo "git-svn clone already exists, confirm fetched..."
cd "${SVN_CLONE}"
# If a network issue disrupts a large a initial clone, then only a partial history is checked out.
# This state can be recoved by running a fetch. There is no harm in attempting this,
# so do it unconditionally if the clone already exists.
git svn fetch ${FETCH_ARGS}
echo "Doing a rebase..."
git remote rm bare || echo "failed to delete remote:bare, proceeding anyway"
git svn rebase \
${FETCH_ARGS} \
--fetch-all \
-A "${AUTHORS_FILE}"
fi
git remote add bare "${GIT_BARE}"
git config remote.bare.push 'refs/remotes/*:refs/heads/*'
cd "${GIT_BARE}"
git init --bare .
git symbolic-ref HEAD refs/heads/trunk
cd "${SVN_CLONE}"
if [ ! -z "$COMMIT_GIT_TO_SVN" ];
then
git pull "${GIT_REPO}"
git svn dcommit
# --force is required to be able to later push SVN back to GIT.
# It may change commits and their hashes, no way around it?
FORCE="--force"
fi
git push bare
cd "${GIT_BARE}"
git branch -m origin/trunk master
git for-each-ref --format='%(refname)' refs/heads/origin/tags | \
cut -d / -f 5 | \
while read ref;
do
git tag "$ref" "refs/heads/origin/tags/$ref"
git branch -D "origin/tags/$ref"
done
git remote add origin "${GIT_REPO}"
git config branch.master.remote origin
git config branch.master.merge refs/heads/master
git push $FORCE --tags origin master
git push $FORCE --all
rm -rf "${GIT_BARE}"
rm -rf "${LOCK_FILE}"
done