You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

427 lines
13 KiB

  1. #!/bin/bash
  2. #
  3. # (c) 2012 - 2016 PrydeWorX
  4. # Sven Eden, PrydeWorX - Bardowick, Germany
  5. # yamakuzure@users.sourceforge.net
  6. #
  7. # This program is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. #
  20. # History and ChangePWX_ERR_LOG:
  21. # Version Date Maintainer Change(s)
  22. # 0.0.1 2012-12-29 sed, PrydeWorX First Design.
  23. # 0.0.2 2013-01-08 sed, PrydeWorX First working version.
  24. # 0.1.0 2013-01-10 sed, PrydeWorX Initial private release.
  25. # 0.1.1 2013-06-01 sed, PrydeWorX Use functions from pwx_git_funcs.sh.
  26. # 0.2.0 2014-09-02 sed, PrydeWorX Use GNU enhanced getopt for command
  27. # line parsing.
  28. # 0.3.0 2016-11-18 sed, PrydeWorX Added option -T|--theirs to force
  29. # merge errors to be resolved by
  30. # throwing away all local changes.
  31. # 0.3.1 2017-03-22 sed, PrydeWorX Show full part of a patch that is to be
  32. # deleted after manually fixing merge
  33. # conflicts.
  34. # Show tried command if we fail with
  35. # full rejects.
  36. # 0.4.0 2017-04-24 sed, PrydeWorX Remove some remote-is-right-automatisms
  37. # 0.5.0 2017-07-04 sed, PrydeWorX If normal processing fails, use
  38. # check_tree.pl to generate diffs for the
  39. # specific commit, and apply them after
  40. # letting the user have a look.
  41. # Common functions
  42. PROGDIR="$(readlink -f $(dirname $0))"
  43. source ${PROGDIR}/pwx_git_funcs.sh
  44. # Version, please keep this current
  45. VERSION="0.5.0"
  46. # Global values to be filled in:
  47. PATCH_DIR="${PROGDIR}/patches"
  48. TAG_TO_USE=""
  49. # Editor to edit individual patches when needed
  50. PWX_EDIT=/usr/bin/kate
  51. # The options for the git am command:
  52. GIT_AM_OPTS="--committer-date-is-author-date"
  53. # Options and the corresponding help text
  54. OPT_SHORT=hi:T
  55. OPT_LONG=help,input:,theirs
  56. HELP_TEXT="Usage: $0 [OPTIONS] <source dir> <tag>
  57. Take all commit patches from the input directory and
  58. apply them to the local tree.
  59. They are assumed to be from tag <tag> of some source
  60. tree.
  61. OPTIONS:
  62. -h|--help Show this help and exit.
  63. -i|--input <path> : Path to where to patches are.
  64. The default is to read from the
  65. subdirectory 'patches' of the
  66. current directory.
  67. Notes:
  68. - When the script succeeds, it adds a line to the commit
  69. - file \"${PWX_COMMIT_FILE}\" of the form:
  70. <tag>-merged <last used commit>
  71. "
  72. # =========================================
  73. # === Use getopt (GNU enhanced version) ===
  74. # =========================================
  75. # Check the version first, so we do not run into trouble
  76. getopt --test > /dev/null
  77. if [[ $? -ne 4 ]]; then
  78. echo "ERROR: getopt is not the GNU enhanced version."
  79. exit 1
  80. fi
  81. # Store the output so we can check for errors.
  82. OPT_PARSED=$(getopt --options $OPT_SHORT --longoptions $OPT_LONG --name "$0" -- "$@")
  83. if [[ $? -ne 0 ]]; then
  84. # getopt has already complained about wrong arguments to stdout
  85. exit 2
  86. fi
  87. # Use eval with "$OPT_PARSED" to properly handle the quoting
  88. eval set -- "$OPT_PARSED"
  89. # --------------------
  90. # --- Handle input ---
  91. # --------------------
  92. while true; do
  93. case "$1" in
  94. -h|--help)
  95. echo "$HELP_TEXT"
  96. exit 0
  97. ;;
  98. -i|--input)
  99. PATCH_DIR="$2"
  100. shift 2
  101. ;;
  102. --)
  103. shift
  104. break
  105. ;;
  106. *)
  107. echo "Something went mysteriously wrong."
  108. exit 3
  109. ;;
  110. esac
  111. done
  112. # At this point we must have <source dir> and <tag> left
  113. if [[ $# -ne 2 ]]; then
  114. echo "$HELP_TEXT"
  115. exit 4
  116. fi
  117. # So these must be it:
  118. SOURCE_DIR="$1"
  119. TAG_TO_USE="$2"
  120. if [[ ! -d "$SOURCE_DIR" ]]; then
  121. echo "$SOURCE_DIR does not exist"
  122. echo
  123. echo "$HELP_TEXT"
  124. exit 5
  125. fi
  126. # ===========================================
  127. # === The PATCH_DIR directory must exist. ===
  128. # ===========================================
  129. if [[ -n "$PATCH_DIR" ]]; then
  130. if [[ ! -d "$PATCH_DIR" ]]; then
  131. echo "ERROR: $PATCH_DIR does not exist"
  132. exit 4
  133. fi
  134. else
  135. echo "You have set the patch directory to be"
  136. echo "an empty string. Where should I find the"
  137. echo "patches, then?"
  138. exit 5
  139. fi
  140. # =============================================================
  141. # === We need two file lists. ===
  142. # === A) The list of root files. ===
  143. # === These have not been used to generate commit ===
  144. # === patches and must be ignored by 'git am'. ===
  145. # === B) The patches to work with. ===
  146. # =============================================================
  147. echo -n "Building file lists ..."
  148. # --- File list a) Root files
  149. declare -a root_files=( $(find ./ -mindepth 1 -maxdepth 1 -type f \
  150. -not -name '*~' -and \
  151. -not -name '*.diff' -and \
  152. -not -name '*.orig' -and \
  153. -not -name '*.bak' -printf "%f ") )
  154. # --- File list b) Patch files
  155. # --- Here we might find patches that failed for single files. Those
  156. # --- must not clutter the list, they wouldn't apply anyway.
  157. declare -a patch_files=( $(find "$PATCH_DIR"/ -mindepth 1 -maxdepth 1 -type f \
  158. -name '????-*.patch' -and -not -name '*-failed_patch_for-*' \
  159. -printf "%f\n" | sort -n) )
  160. echo " done"
  161. # --- Add an error log file to the list of temp files
  162. PWX_ERR_LOG="/tmp/pwx_git_applier_$$.log"
  163. add_temp "$PWX_ERR_LOG"
  164. touch $PWX_ERR_LOG || die "Unable to create $PWX_ERR_LOG"
  165. # ===================================================
  166. # === Build a basic exclude string to begin with. ===
  167. # ===================================================
  168. basic_excludes=""
  169. for e in "${root_files[@]}" ; do
  170. if [[ "" = "$(echo -n "$basic_excludes" | \
  171. grep -P "^\sexclude=$e")" ]]; then
  172. basic_excludes+=" --exclude=$e"
  173. fi
  174. done
  175. # ============================================
  176. # === Main loop over the found patch files ===
  177. # ============================================
  178. for p in "${patch_files[@]}" ; do
  179. # For further processing the number and the full path
  180. # are needed.
  181. pnum=${p%%-*}
  182. psrc="${PATCH_DIR}/${p}"
  183. # We start with normal 3-way-patching
  184. GIT_USE_TWP="-3 "
  185. # ====================================================
  186. # === Step 1) Reset the exclude list of root files ===
  187. # ====================================================
  188. excludes="$basic_excludes"
  189. # ==============================================
  190. # === Step 2) Start applying the patch as is ===
  191. # ==============================================
  192. echo -n "Applying $p ..."
  193. git am $GIT_USE_TWP$GIT_AM_OPTS$excludes < $psrc 1>/dev/null 2>$PWX_ERR_LOG
  194. res=$?
  195. echo " done [$res]"
  196. # ===========================================================
  197. # === Step 3) Look for reasons to not use 3-way patching ===
  198. # === Symptom : "could not build fake ancestor" ===
  199. # === Reason : No common root can be built ===
  200. # === Resolution: Do not use "-3" option ===
  201. # ===========================================================
  202. if [[ 0 -ne $res ]] && \
  203. [[ $(grep -c "could not build fake ancestor" $PWX_ERR_LOG) -gt 0 ]];
  204. then
  205. echo -n "Trying again without 3-way-patching ..."
  206. GIT_USE_TWP=""
  207. git am --abort 1>/dev/null 2>&1
  208. git am $GIT_USE_TWP$GIT_AM_OPTS$excludes < $psrc 1>/dev/null 2>$PWX_ERR_LOG
  209. res=$?
  210. echo " done [$res]"
  211. fi
  212. # ====================================================================
  213. # === Step 4) Look for more files to exclude ===
  214. # === Symptom : "error: <file>: does not exist in index" ===
  215. # === Reason : The file to patch isn't there ===
  216. # === Resolution: Exclude the offending file(s) ===
  217. # ====================================================================
  218. if [[ 0 -ne $res ]] && \
  219. [[ $(grep -c "does not exist in index" $PWX_ERR_LOG) -gt 0 ]];
  220. then
  221. declare -a nff_files=( $( \
  222. grep "does not exist in index" $PWX_ERR_LOG | \
  223. cut -d ':' -f 2) )
  224. for nff in "${nff_files[@]}" ; do
  225. echo "Excluding $nff ..."
  226. excludes+=" --exclude=$nff"
  227. # A special an evil case is a rename copy of something non-existent.
  228. # git am then needs *two* excludes, one for the (non-existing)
  229. # source and one for the still not existing target.
  230. nff_tgt="$(grep -A 1 "copy from $nff" $psrc | \
  231. grep "copy to " | \
  232. cut -d ' ' -f 3)"
  233. if [[ "x" != "x$nff_tgt" ]]; then
  234. echo "Excluding $nff_tgt ..."
  235. excludes+=" --exclude=$nff_tgt"
  236. fi
  237. done
  238. echo -n "Trying again without non-existing files ..."
  239. git am --abort 1>/dev/null 2>&1
  240. git am $GIT_USE_TWP$GIT_AM_OPTS$excludes < $psrc 1>/dev/null 2>$PWX_ERR_LOG
  241. res=$?
  242. echo " done [$res]"
  243. unset nff_files
  244. fi
  245. # ====================================================================
  246. # === Step 5) If this still doesn't work, let check_tree.pl check ===
  247. # === out the commit and build diffs for the touched files ===
  248. # === against the tree. Then let the user have a chance to ===
  249. # === decide what to apply before continuing. ===
  250. # ====================================================================
  251. if [[ 0 -ne $res ]] && \
  252. [[ $(grep -c "patch does not apply" $PWX_ERR_LOG) -gt 0 ]];
  253. then
  254. res=0
  255. xCommit="$(head -n 1 $psrc | cut -d ' ' -f 2)"
  256. echo "git am failed to apply the patch automatically:"
  257. echo -e "\n--------"
  258. cat $PWX_ERR_LOG
  259. echo -e "--------\n"
  260. echo "Building patch diffs for commit $xCommit ..."
  261. # We need to know which files are relevant:
  262. xFiles=""
  263. xPatches=""
  264. for pF in $(grep "^diff \--git" $psrc | cut -d ' ' -f 3,4 | \
  265. while read a b ; do \
  266. echo -e "$a\n$b" | cut -d '/' -f 2- ; \
  267. done | sort -u) ; do
  268. xFiles+="-f $pF "
  269. xPatches+="${PROGDIR}/patches/${pF//\//_}.patch "
  270. done
  271. # If we have our lists, do it:
  272. if [[ "x" != "x$xFiles" ]] && [[ "x" != "x$xPatches" ]]; then
  273. $(PROGDIR)/check_tree.pl -c $xCommit $xFiles "$SOURCE_DIR"
  274. # Let's see which patches got built
  275. xResult=""
  276. for xP in $xPatches ; do
  277. echo -n "Checkin $xP : "
  278. if [[ -f "$xP" ]]; then
  279. echo "present"
  280. xResult+="$xP "
  281. else
  282. echo "missing"
  283. fi
  284. done
  285. # So, if no patches have been found, this whole thing
  286. # can be skipped.
  287. if [[ "x" = "x$xResult" ]]; then
  288. echo "No relevant patches found."
  289. echo -n "Skipping $psrc"
  290. git am --skip
  291. else
  292. # Okay, something to do found.
  293. echo "Please edit/delete the diffs as they are"
  294. echo "and then close $PWX_EDIT to continue"
  295. echo " ... to skip, just delete all patches"
  296. $PWX_EDIT $xResult 1>/dev/null 2>&1
  297. # Apply patch-by-patch and add to git.
  298. have_patch=0
  299. for xP in $xResult ; do
  300. while [[ -f "$xP" ]]; do
  301. have_patch=1
  302. echo "Applying $xP ..."
  303. patch -p 1 -i $xP
  304. if [[ 0 -ne $? ]]; then
  305. echo "Something is wrong with $xP"
  306. $PWX_EDIT $xP 1>/dev/null 2>&1
  307. else
  308. rm -f $xP
  309. fi
  310. done
  311. done
  312. if [[ 0 -eq $have_patch ]]; then
  313. echo "All patches deleted."
  314. git am --skip
  315. else
  316. git add man src
  317. git status
  318. echo "Patch: $psrc"
  319. echo -e -n "\nDoes this look in order to you? [Y/n]"
  320. read answer
  321. if [[ "xn" = "x$answer" ]]; then
  322. echo "Okay, then see what you can do"
  323. exit 0
  324. fi
  325. echo -n "Finishing $psrc ..."
  326. git am --continue 1>/dev/null 2>$PWX_ERR_LOG
  327. res=$?
  328. echo " done [$res]"
  329. fi
  330. fi
  331. else
  332. echo "ERROR: Could not get file list from $psrc"
  333. echo
  334. echo "You have to do this by hand. Sorry."
  335. exit 1
  336. fi
  337. fi
  338. # ===========================================
  339. # === Step 6) Exit if we couldn't help it ===
  340. # ===========================================
  341. if [[ $res -ne 0 ]]; then
  342. echo -e "\n ==> $psrc can not be applied"
  343. echo " ==> The command that failed was:"
  344. echo "git am $GIT_USE_TWP$GIT_AM_OPTS$excludes < $psrc"
  345. echo -e " ==> The Error message is:\n"
  346. cat $PWX_ERR_LOG
  347. echo -e "\nPlease try to apply the remaining parts by hand"
  348. echo "and then finish with 'git am --continue'"
  349. cleanup
  350. exit $res
  351. fi
  352. # ==================================
  353. # === Step 7) Clean up behind us ===
  354. # ==================================
  355. # The patch is applied or irrelevant, remove it.
  356. rm -f $psrc
  357. # Remove merge debris
  358. find -iname '*.orig' | xargs rm -f
  359. # Remove temp files
  360. cleanup
  361. done