Free parse strings.
[geda-pcb/pcjc2.git] / antifork / antifork.sh
blobe34baf750c8730fe5b1e5c8f7aaba6dd4475f09a
1 #!/bin/bash
3 ##
4 # \file antifork/antifork.sh
6 # \brief Main script for the Antifork system.
8 # <hr>
10 # <h1><b>Copyright.</b></h1>\n
12 # Antifork for
13 # PCB, interactive printed circuit board design
14 # Copyright (C) 2015 Markus "Traumflug" Hitter <mah@jump-ing.de>
16 # This program is free software; you can redistribute it and/or modify
17 # it under the terms of the GNU General Public License as published by
18 # the Free Software Foundation; either version 2 of the License, or
19 # (at your option) any later version.
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 # GNU General Public License for more details.
26 # You should have received a copy of the GNU General Public License
27 # along with this program; if not, write to the Free Software
28 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 # Antifork fetches from known, forked repositores and creates local branches
32 # of their branches. The point is to make all the hidden stuff visible, to
33 # allow easy cherry-picking, to allow rebasing, and also to deal with forks
34 # using StGit (which hilariously messes up a Git repository in a way not
35 # viewable in gitk).
37 # Along the way, branches in forks can be filtered. This is useful for
38 # forked branches which simply duplicate branches in the official repo,
39 # which handle topics already finished and similar stuff.
41 # Antifork is meant to run in local clones, not in the official repo. Copied
42 # branches are prefixed with the name of the fork when fetched for the first
43 # time. If a forked branch got updated in the fork and is fetched again, it
44 # additionally gets the prefix "today-". This means to make it easy to
45 # recognize changes in hte fork.
47 # Happy antiforking, everybody!
50 # Format of the repository description files:
52 # - The name of the file is also the prefix of the local branch copies.
54 # - First line is the repository URL.
56 # - Lines starting with 'ignorebranch' followed by the hash of the last
57 # commit of a branch mark remote branches which should be ignored.
58 # Typically this is the case when all commits in a branch were reviewed and
59 # found to be not helpful or the branch was picked to the official repo
60 # already.
62 # It's the last known (stale) commit instead of the branch name,
63 # because if there appear new commits on the branch, the branch
64 # should be reviewed again.
67 # TODO: also allow to ignore single commits, for example by changing their
68 # commit message to "obsolete" and squashing consecutive obsolete commits.
69 # This should help reviewing a lot.
71 # Not exactly trivial, because this requires rebasing and rebasing
72 # changes the commit hash.
74 BASE_CMD=$(basename "$0")
76 if ! ls -d "${BASE_CMD}" >/dev/null 2>&1; then
77 echo "Run ${BASE_CMD} from the directory it sits in."
78 exit 1
81 # Handle parameters.
82 while [ "${1}" != "" ]; do
83 case "${1}" in
84 -h|--help)
85 echo
86 echo " This script attaches a number of known repositores and creates"
87 echo " local branches from their branches. The point is to make all the"
88 echo " hidden stuff visible."
89 echo
90 echo " -h,--help Show this help."
91 echo
92 echo " --also-svn Also attach and fetch forks done in Subversion"
93 echo " repositories. These integrate not too well, so"
94 echo " they're ignored by default."
95 echo
96 echo " Running ${BASE_CMD} without parameters fetches these"
97 echo " repositories:"
98 echo
99 echo " name location"
100 echo
101 for REPO in *; do
102 if [ "${REPO}" != "${BASE_CMD}" ]; then
103 printf " %-16s " "${REPO}"
104 head -1 "${REPO}"
106 done
107 echo
108 echo " Passing one or more of the names as parameters fetches only the"
109 echo " given ones."
110 exit
112 --also-svn)
113 ALSO_SVN="true"
114 shift
117 if [ -r "${1}" ]; then
118 REPOS=("${REPOS[@]}" "${1}")
119 else
120 echo -n "$(basename "$0"): no description file found for repository "
121 echo "'${1}'. Dropping."
123 shift
125 esac
126 done
128 # No parameters case.
129 if [ ${#REPOS[*]} = 0 ]; then
130 for REPO in *; do
131 if [ "${REPO}" != "${BASE_CMD}" ]; then
132 REPOS=("${REPOS[@]}" "${REPO}")
134 done
138 # Get into action on Git forks.
139 for REPO in "${REPOS[@]}"; do
141 URL=$(head -1 "${REPO}")
142 if [ "${URL}" != "${URL#svn}" ]; then
143 echo "Eeeek! ${REPO} = ${URL} is a Subversion repo!"
144 continue
147 git remote add "${REPO}" "${URL}"
148 echo -n "Fetching ${REPO} ..."
149 git fetch "${REPO}" >/dev/null
150 echo " done."
152 git branch -r | grep "${REPO}/" | grep -v ".stgit" | \
153 while read BRANCH; do
154 LOCAL_BRANCH=$(echo "${BRANCH}" | tr -d ' ' | tr / -)
155 TODAY_BRANCH="today-${LOCAL_BRANCH}"
156 RAW_BRANCH=$(git show -q "${BRANCH}" | awk '{ print $2; exit; }')
158 if cat "${REPO}" | grep "^ignorebranch" | grep -q "${RAW_BRANCH}"; then
159 echo "Ignoring ${BRANCH}."
160 if git show -q "${LOCAL_BRANCH}" >/dev/null 2>&1; then
161 git branch -D "${LOCAL_BRANCH}"
163 continue
164 else
165 echo "Handling ${BRANCH}."
168 if git show -q "${LOCAL_BRANCH}" >/dev/null 2>&1; then
169 # Local branch exists.
170 if git show -q "${TODAY_BRANCH}" >/dev/null 2>&1; then
171 # Today branch exists. Make it the new LOCAL_BRANCH.
172 git branch -M "${TODAY_BRANCH}" "${LOCAL_BRANCH}"
175 # If the remote branch changed, make it a TODAY_BRANCH.
176 if [ "$(git show -q "${BRANCH}" | head -1)" != \
177 "$(git show -q "${LOCAL_BRANCH}" | head -1)" ]
178 then
179 git branch "${TODAY_BRANCH}" "${BRANCH}"
181 else
182 # NO local branch.
183 git branch "${LOCAL_BRANCH}" "${BRANCH}"
186 done
188 git remote remove "${REPO}"
190 done
192 if [ -z "${ALSO_SVN}" ]; then
193 echo "Ignoring forks in Subversion repositories."
194 exit 0
197 # Handle Subversion repos.
199 # Cloning a Subversion repo is a very time consuming operation, so we handle
200 # these entirely different:
202 # - Don't drop the clone after being done. Actually, Git has no function to
203 # remove a SVN remote repo once added. See below on how to do it anyways.
205 # - Just fetch the branches, no further handling.
207 # Advantage for our purposes here is, branching an dealing with branches
208 # is rather expensive with SVN, so developers using SVN do so rarely. They
209 # also can't rebase, so even topic branches can't change.
210 for REPO in "${REPOS[@]}"; do
212 URL=$(head -1 "${REPO}")
213 if [ "${URL}" = "${URL#svn}" ]; then
214 continue
217 ORG_PWD="$(pwd)"
218 trap "cd '${ORG_PWD}'" 0
220 while ! ls -d .git >/dev/null 2>&1; do
221 cd ..
222 if [ "$(pwd)" = "/" ]; then
223 echo "Not inside a Git repo ?!?."
224 exit 1
226 done
228 if ! git branch -a | grep -q "${REPO}/"; then
229 # We have no clone, yet.
230 git svn init --stdlayout --svn-remote="${REPO}" --prefix="${REPO}/" "${URL}"
233 echo -n "Fetching ${REPO} ... "
234 git svn fetch "${REPO}"
235 echo " done."
237 done
240 # How to remove a SVN remote with the name XYZ from a Git repository.
242 # Note: to the best of my research there is no official Git documentation on
243 # how to do this, so make sure you have a backup of your Git repo
244 # (a simple copy of the directory is sufficient) and: use this on your
245 # own risk! --Traumflug 2015-08-22
247 # 1. git checkout master
249 # 2. vi .git/config
251 # Remove the section '[svn-remote "XYZ"]'. All lines up to the next '[' or
252 # to the end of file.
254 # 3. vi .git/svn/.metadata
256 # Do the very same as in 2.
258 # 4. rm -r .git/logs/refs/remotes/XYZ
260 # 5. rm -r .git/svn/refs/remotes/XYZ
262 # 6. rm -r .git/refs/remotes/XYZ
264 # 7. git svn gc
265 # git gc
267 # The remote is removed now. To also remove the branches once created by the
268 # SVN remote, do this:
270 # 8. git branch -a | grep remotes/rnd/ | while read B; do
271 # git branch -rD "${B#remotes/}";
272 # done