No Description

split_and_push.sh 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. #! /bin/bash
  2. # Copyright 2017 Sébastien Bigaret <sebastien.bigaret@telecom-bretagne.eu>
  3. # License: 3-clause BSD, cf. https://opensource.org/licenses/BSD-3-Clause
  4. #set -x
  5. #export LANG=C
  6. echo "WARNING: the script does not check all things that can go wrong.
  7. Hence: before running the script, please:
  8. - DO BACKUP your local repository: in case something goes wrong, you MUST be able to restore it from scratch (including its .git directory)
  9. - Make sure that it is in a clean state (uncommitted changes and untracked files can make this script fail as well).
  10. When this is done, edit this script and remove the exit statement below.
  11. "
  12. #exit 1
  13. # Adapt the following settings to your situation
  14. # name of the upstream remote
  15. origin=origin
  16. # name of the current branch
  17. branch=master
  18. # Maximum size allowed by the remote http server or proxy for a single push
  19. MAX_SIZE=$((90*1024*1024)) # 90Mo e.g.
  20. # NB: to cache https credentials for 10 minutes (600 seconds), run:
  21. #git config credential.helper 'cache --timeout=600'
  22. # ----------------------------------------------------------------------
  23. # fetch upstream changes, if any
  24. git fetch ${origin}
  25. # get the revisions existing only locally, not pushed:
  26. readarray -t revs < <( git cherry -v redmine/${branch} | grep '^+ ' | cut -d' ' -f2 )
  27. git branch old_${branch}_HEAD # mark the original branch head
  28. git reset --hard ${revs[0]}^ # back to the last revision pushed upstream
  29. RM=/bin/rm
  30. GIT=/usr/local/bin/git
  31. base_rev=${branch}
  32. function save_properties()
  33. {
  34. # to correctly preserve the file's attributes, we'd better stay on the
  35. # same filesystem (FS): let's use the directory where the file is
  36. # stored, as the temp.dir. may be on a different FS,
  37. local file_properties=$(mktemp -p $(dirname ${1}))
  38. cp --attributes-only --preserve=all "${1}" "${file_properties}"
  39. echo "${file_properties}"
  40. }
  41. function restore_properties()
  42. {
  43. local file_properties="$1"
  44. local file="$2"
  45. cp --attributes-only --preserve=all "${file_properties}" "${file}"
  46. }
  47. for rev in "${revs[@]}"; do
  48. # For each revision:
  49. # 1. try to push it upstream;
  50. # 2. if the push fails, break it into pieces, one file after the other
  51. # 3. if a file is too big for a push, split it into smaller pieces
  52. #commit_prefix="rev. ${rev:0:10} - add $file - " # debugging purpose
  53. commit_prefix=""
  54. rev_comment=$(git log -1 --pretty=%B ${rev})
  55. # apply a revision (fast-forward)
  56. git merge $rev
  57. # Push it upstream, as a whole
  58. if git push ${origin} ${branch}; then
  59. base_rev=${rev}
  60. continue
  61. else
  62. git reset HEAD^ # back-pedal
  63. fi;
  64. # ok, the push failed, lets do it one file after the other
  65. git checkout -b temporary_branch
  66. git cherry-pick --no-commit $rev
  67. # get the list of files involved in that rev.
  68. readarray -t files \
  69. < <(git diff-tree --no-commit-id --name-only -r "${base_rev}" ${rev})
  70. nb_files=${#files[*]}
  71. idx=0
  72. for file in "${files[@]}"; do
  73. idx=$((idx+1))
  74. file_size=$(wc -c < "${file}")
  75. if [ $file_size -le $MAX_SIZE ]; then
  76. git add $file
  77. git commit -m "${rev_comment} (${idx}/${nb_files})"
  78. git push $origin temporary_branch
  79. else
  80. # the file is too big: let's split it
  81. split --additional-suffix=.split -d -e -b${MAX_SIZE} "$file" "$file."
  82. # we take care of preserving the file attributes
  83. file_properties=$(save_properties "${file}")
  84. echo > $file # *squash*
  85. shopt -s nullglob
  86. splitted_files=("${file}".[0-9]*.split)
  87. idx_split=0
  88. for s in "${splitted_files[@]}"; do
  89. cat "$s" >> "${file}"
  90. ${RM} -f "$s"
  91. restore_properties "${file_properties}" "${file}"
  92. git add $file
  93. git commit -m "${commit_prefix}{rev_comment} (${idx}/${nb_files})-(${idx_split}/${#splitted_files[*]})"
  94. git push $origin temporary_branch
  95. done
  96. ${RM} "${file_properties}"
  97. fi
  98. done # done for ${rev}
  99. # merge the commit(s) back into the main branch
  100. if [ ${nb_files} -le 1 ]; then
  101. git commit --amend -m "${rev_comment}"
  102. git checkout "${branch}"
  103. git merge temporary_branch
  104. git push -f "${origin}" temporary_branch
  105. else
  106. git checkout "${branch}"
  107. git merge --no-ff -m "${rev_comment}" temporary_branch
  108. fi
  109. git push "${origin}" ${branch}
  110. # clean up and prepare the next iteration
  111. git branch -d temporary_branch
  112. git push "${origin}" :temporary_branch
  113. base_rev="${rev}"
  114. done
  115. # check that everything is ok.
  116. # When you are sure, the branch 'OLD_<branch_name>_HEAD'can be deleted.