update-third-party.bash 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. ########################################################################
  2. # Script for updating third party packages.
  3. #
  4. # This script should be sourced in a project-specific script which sets
  5. # the following variables:
  6. #
  7. # name
  8. # The name of the project.
  9. # ownership
  10. # A git author name/email for the commits.
  11. # subtree
  12. # The location of the thirdparty package within the main source
  13. # tree.
  14. # repo
  15. # The git repository to use as upstream.
  16. # tag
  17. # The tag, branch or commit hash to use for upstream.
  18. # shortlog
  19. # Optional. Set to 'true' to get a shortlog in the commit message.
  20. #
  21. # Additionally, an "extract_source" function must be defined. It will be
  22. # run within the checkout of the project on the requested tag. It should
  23. # should place the desired tree into $extractdir/$name-reduced. This
  24. # directory will be used as the newest commit for the project.
  25. #
  26. # For convenience, the function may use the "git_archive" function which
  27. # does a standard "git archive" extraction using the (optional) "paths"
  28. # variable to only extract a subset of the source tree.
  29. ########################################################################
  30. ########################################################################
  31. # Utility functions
  32. ########################################################################
  33. git_archive () {
  34. git archive --prefix="$name-reduced/" HEAD -- $paths | \
  35. tar -C "$extractdir" -x
  36. }
  37. die () {
  38. echo >&2 "$@"
  39. exit 1
  40. }
  41. warn () {
  42. echo >&2 "warning: $@"
  43. }
  44. readonly regex_date='20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
  45. readonly basehash_regex="$name $regex_date ([0-9a-f]*)"
  46. readonly basehash="$( git rev-list --author="$ownership" --grep="$basehash_regex" -n 1 HEAD )"
  47. readonly upstream_old_short="$( git cat-file commit "$basehash" | sed -n '/'"$basehash_regex"'/ {s/.*(//;s/)//;p}' | egrep '^[0-9a-f]+$' )"
  48. ########################################################################
  49. # Sanity checking
  50. ########################################################################
  51. [ -n "$name" ] || \
  52. die "'name' is empty"
  53. [ -n "$ownership" ] || \
  54. die "'ownership' is empty"
  55. [ -n "$subtree" ] || \
  56. die "'subtree' is empty"
  57. [ -n "$repo" ] || \
  58. die "'repo' is empty"
  59. [ -n "$tag" ] || \
  60. die "'tag' is empty"
  61. [ -n "$basehash" ] || \
  62. warn "'basehash' is empty; performing initial import"
  63. readonly do_shortlog="${shortlog-false}"
  64. readonly workdir="$PWD/work"
  65. readonly upstreamdir="$workdir/upstream"
  66. readonly extractdir="$workdir/extract"
  67. [ -d "$workdir" ] && \
  68. die "error: workdir '$workdir' already exists"
  69. trap "rm -rf '$workdir'" EXIT
  70. # Get upstream
  71. git clone "$repo" "$upstreamdir"
  72. if [ -n "$basehash" ]; then
  73. # Use the existing package's history
  74. git worktree add "$extractdir" "$basehash"
  75. # Clear out the working tree
  76. pushd "$extractdir"
  77. git ls-files | xargs rm -v
  78. popd
  79. else
  80. # Create a repo to hold this package's history
  81. mkdir -p "$extractdir"
  82. git -C "$extractdir" init
  83. fi
  84. # Extract the subset of upstream we care about
  85. pushd "$upstreamdir"
  86. git checkout "$tag"
  87. readonly upstream_hash="$( git rev-parse HEAD )"
  88. readonly upstream_hash_short="$( git rev-parse --short=8 "$upstream_hash" )"
  89. readonly upstream_datetime="$( git rev-list "$upstream_hash" --format='%ci' -n 1 | grep -e "^$regex_date" )"
  90. readonly upstream_date="$( echo "$upstream_datetime" | grep -o -e "$regex_date" )"
  91. if $do_shortlog && [ -n "$basehash" ]; then
  92. readonly commit_shortlog="
  93. Upstream Shortlog
  94. -----------------
  95. $( git shortlog --no-merges --abbrev=8 --format='%h %s' "$upstream_old_short".."$upstream_hash" )"
  96. else
  97. readonly commit_shortlog=""
  98. fi
  99. extract_source || \
  100. die "failed to extract source"
  101. popd
  102. [ -d "$extractdir/$name-reduced" ] || \
  103. die "expected directory to extract does not exist"
  104. readonly commit_summary="$name $upstream_date ($upstream_hash_short)"
  105. # Commit the subset
  106. pushd "$extractdir"
  107. mv -v "$name-reduced/"* .
  108. rmdir "$name-reduced/"
  109. git add -A .
  110. git commit -n --author="$ownership" --date="$upstream_datetime" -F - <<-EOF
  111. $commit_summary
  112. Code extracted from:
  113. $repo
  114. at commit $upstream_hash ($tag).$commit_shortlog
  115. EOF
  116. git branch -f "upstream-$name"
  117. popd
  118. # Merge the subset into this repository
  119. if [ -n "$basehash" ]; then
  120. git merge --log -s recursive "-Xsubtree=$subtree/" --no-commit "upstream-$name"
  121. else
  122. git fetch "$extractdir" "upstream-$name:upstream-$name"
  123. git merge --log -s ours --no-commit "upstream-$name"
  124. git read-tree -u --prefix="$subtree/" "upstream-$name"
  125. fi
  126. git commit --no-edit
  127. git branch -d "upstream-$name"