123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- #! /bin/bash
-
- # Copyright 2017 Sébastien Bigaret <sebastien.bigaret@telecom-bretagne.eu>
- # License: 3-clause BSD, cf. https://opensource.org/licenses/BSD-3-Clause
-
- #set -x
- #export LANG=C
-
- echo "WARNING: the script does not check all things that can go wrong.
-
- Hence: before running the script, please:
-
- - DO BACKUP your local repository: in case something goes wrong, you MUST be able to restore it from scratch (including its .git directory)
-
- - Make sure that it is in a clean state (uncommitted changes and untracked files can make this script fail as well).
-
- When this is done, edit this script and remove the exit statement below.
- "
- #exit 1
-
- # Adapt the following settings to your situation
-
- # name of the upstream remote
- origin=origin
- # name of the current branch
- branch=master
-
- # Maximum size allowed by the remote http server or proxy for a single push
- MAX_SIZE=$((90*1024*1024)) # 90Mo e.g.
-
- # NB: to cache https credentials for 10 minutes (600 seconds), run:
- #git config credential.helper 'cache --timeout=600'
-
-
- # ----------------------------------------------------------------------
- # fetch upstream changes, if any
- git fetch ${origin}
-
- # get the revisions existing only locally, not pushed:
- readarray -t revs < <( git cherry -v redmine/${branch} | grep '^+ ' | cut -d' ' -f2 )
-
- git branch old_${branch}_HEAD # mark the original branch head
- git reset --hard ${revs[0]}^ # back to the last revision pushed upstream
-
- RM=/bin/rm
- GIT=/usr/local/bin/git
-
- base_rev=${branch}
-
- function save_properties()
- {
- # to correctly preserve the file's attributes, we'd better stay on the
- # same filesystem (FS): let's use the directory where the file is
- # stored, as the temp.dir. may be on a different FS,
- local file_properties=$(mktemp -p $(dirname ${1}))
- cp --attributes-only --preserve=all "${1}" "${file_properties}"
- echo "${file_properties}"
- }
-
- function restore_properties()
- {
- local file_properties="$1"
- local file="$2"
- cp --attributes-only --preserve=all "${file_properties}" "${file}"
- }
-
- for rev in "${revs[@]}"; do
- # For each revision:
- # 1. try to push it upstream;
- # 2. if the push fails, break it into pieces, one file after the other
- # 3. if a file is too big for a push, split it into smaller pieces
- #commit_prefix="rev. ${rev:0:10} - add $file - " # debugging purpose
- commit_prefix=""
-
- rev_comment=$(git log -1 --pretty=%B ${rev})
- # apply a revision (fast-forward)
- git merge $rev
- # Push it upstream, as a whole
- if git push ${origin} ${branch}; then
- base_rev=${rev}
- continue
- else
- git reset HEAD^ # back-pedal
- fi;
-
- # ok, the push failed, lets do it one file after the other
- git checkout -b temporary_branch
- git cherry-pick --no-commit $rev
- # get the list of files involved in that rev.
- readarray -t files \
- < <(git diff-tree --no-commit-id --name-only -r "${base_rev}" ${rev})
- nb_files=${#files[*]}
- idx=0
- for file in "${files[@]}"; do
- idx=$((idx+1))
- file_size=$(wc -c < "${file}")
- if [ $file_size -le $MAX_SIZE ]; then
- git add $file
- git commit -m "${rev_comment} (${idx}/${nb_files})"
- git push $origin temporary_branch
- else
- # the file is too big: let's split it
- split --additional-suffix=.split -d -e -b${MAX_SIZE} "$file" "$file."
-
- # we take care of preserving the file attributes
- file_properties=$(save_properties "${file}")
-
- echo > $file # *squash*
-
- shopt -s nullglob
- splitted_files=("${file}".[0-9]*.split)
-
- idx_split=0
- for s in "${splitted_files[@]}"; do
- cat "$s" >> "${file}"
- ${RM} -f "$s"
- restore_properties "${file_properties}" "${file}"
- git add $file
- git commit -m "${commit_prefix}{rev_comment} (${idx}/${nb_files})-(${idx_split}/${#splitted_files[*]})"
- git push $origin temporary_branch
- done
- ${RM} "${file_properties}"
- fi
- done # done for ${rev}
-
- # merge the commit(s) back into the main branch
- if [ ${nb_files} -le 1 ]; then
- git commit --amend -m "${rev_comment}"
- git checkout "${branch}"
- git merge temporary_branch
- git push -f "${origin}" temporary_branch
- else
- git checkout "${branch}"
- git merge --no-ff -m "${rev_comment}" temporary_branch
- fi
-
- git push "${origin}" ${branch}
- # clean up and prepare the next iteration
- git branch -d temporary_branch
- git push "${origin}" :temporary_branch
- base_rev="${rev}"
- done
-
- # check that everything is ok.
- # When you are sure, the branch 'OLD_<branch_name>_HEAD'can be deleted.
|