Guest User

Untitled

a guest
Jun 20th, 2018
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.89 KB | None | 0 0
  1. #!/bin/sh
  2. #
  3. # Rewrite revision history
  4. # Copyright (c) Petr Baudis, 2006
  5. # Minimal changes to "port" it to core-git (c) Johannes Schindelin, 2007
  6. #
  7. # Lets you rewrite the revision history of the current branch, creating
  8. # a new branch. You can specify a number of filters to modify the commits,
  9. # files and trees.
  10.  
  11. # The following functions will also be available in the commit filter:
  12.  
  13. functions=$(cat << \EOF
  14. warn () {
  15. echo "$*" >&2
  16. }
  17.  
  18. map()
  19. {
  20. # if it was not rewritten, take the original
  21. if test -r "$workdir/../map/$1"
  22. then
  23. cat "$workdir/../map/$1"
  24. else
  25. echo "$1"
  26. fi
  27. }
  28.  
  29. # if you run 'skip_commit "$@"' in a commit filter, it will print
  30. # the (mapped) parents, effectively skipping the commit.
  31.  
  32. skip_commit()
  33. {
  34. shift;
  35. while [ -n "$1" ];
  36. do
  37. shift;
  38. map "$1";
  39. shift;
  40. done;
  41. }
  42.  
  43. # if you run 'git_commit_non_empty_tree "$@"' in a commit filter,
  44. # it will skip commits that leave the tree untouched, commit the other.
  45. git_commit_non_empty_tree()
  46. {
  47. if test $# = 3 && test "$1" = $(git rev-parse "$3^{tree}"); then
  48. map "$3"
  49. else
  50. git commit-tree "$@"
  51. fi
  52. }
  53. # override die(): this version puts in an extra line break, so that
  54. # the progress is still visible
  55.  
  56. die()
  57. {
  58. echo >&2
  59. echo "$*" >&2
  60. exit 1
  61. }
  62. EOF
  63. )
  64.  
  65. eval "$functions"
  66.  
  67. # When piped a commit, output a script to set the ident of either
  68. # "author" or "committer
  69.  
  70. set_ident () {
  71. lid="$(echo "$1" | tr "[A-Z]" "[a-z]")"
  72. uid="$(echo "$1" | tr "[a-z]" "[A-Z]")"
  73. pick_id_script='
  74. /^'$lid' /{
  75. s/'\''/'\''\\'\'\''/g
  76. h
  77. s/^'$lid' \([^<]*\) <[^>]*> .*$/\1/
  78. s/'\''/'\''\'\'\''/g
  79. s/.*/GIT_'$uid'_NAME='\''&'\''; export GIT_'$uid'_NAME/p
  80.  
  81. g
  82. s/^'$lid' [^<]* <\([^>]*\)> .*$/\1/
  83. s/'\''/'\''\'\'\''/g
  84. s/.*/GIT_'$uid'_EMAIL='\''&'\''; export GIT_'$uid'_EMAIL/p
  85.  
  86. g
  87. s/^'$lid' [^<]* <[^>]*> \(.*\)$/\1/
  88. s/'\''/'\''\'\'\''/g
  89. s/.*/GIT_'$uid'_DATE='\''&'\''; export GIT_'$uid'_DATE/p
  90.  
  91. q
  92. }
  93. '
  94.  
  95. LANG=en_US.UTF-8 sed -ne "$pick_id_script"
  96. # Ensure non-empty id name.
  97. echo "case \"\$GIT_${uid}_NAME\" in \"\") GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\" && export GIT_${uid}_NAME;; esac"
  98. }
  99.  
  100. USAGE="[--env-filter <command>] [--tree-filter <command>]
  101. [--index-filter <command>] [--parent-filter <command>]
  102. [--msg-filter <command>] [--commit-filter <command>]
  103. [--tag-name-filter <command>] [--subdirectory-filter <directory>]
  104. [--original <namespace>] [-d <directory>] [-f | --force]
  105. [<rev-list options>...]"
  106.  
  107. OPTIONS_SPEC=
  108. . git-sh-setup
  109.  
  110. if [ "$(is_bare_repository)" = false ]; then
  111. git diff-files --ignore-submodules --quiet &&
  112. git diff-index --cached --quiet HEAD -- ||
  113. die "Cannot rewrite branch(es) with a dirty working directory."
  114. fi
  115.  
  116. tempdir=.git-rewrite
  117. filter_env=
  118. filter_tree=
  119. filter_index=
  120. filter_parent=
  121. filter_msg=cat
  122. filter_commit=
  123. filter_tag_name=
  124. filter_subdir=
  125. orig_namespace=refs/original/
  126. force=
  127. prune_empty=
  128. remap_to_ancestor=
  129. while :
  130. do
  131. case "$1" in
  132. --)
  133. shift
  134. break
  135. ;;
  136. --force|-f)
  137. shift
  138. force=t
  139. continue
  140. ;;
  141. --remap-to-ancestor)
  142. shift
  143. remap_to_ancestor=t
  144. continue
  145. ;;
  146. --prune-empty)
  147. shift
  148. prune_empty=t
  149. continue
  150. ;;
  151. -*)
  152. ;;
  153. *)
  154. break;
  155. esac
  156.  
  157. # all switches take one argument
  158. ARG="$1"
  159. case "$#" in 1) usage ;; esac
  160. shift
  161. OPTARG="$1"
  162. shift
  163.  
  164. case "$ARG" in
  165. -d)
  166. tempdir="$OPTARG"
  167. ;;
  168. --env-filter)
  169. filter_env="$OPTARG"
  170. ;;
  171. --tree-filter)
  172. filter_tree="$OPTARG"
  173. ;;
  174. --index-filter)
  175. filter_index="$OPTARG"
  176. ;;
  177. --parent-filter)
  178. filter_parent="$OPTARG"
  179. ;;
  180. --msg-filter)
  181. filter_msg="$OPTARG"
  182. ;;
  183. --commit-filter)
  184. filter_commit="$functions; $OPTARG"
  185. ;;
  186. --tag-name-filter)
  187. filter_tag_name="$OPTARG"
  188. ;;
  189. --subdirectory-filter)
  190. filter_subdir="$OPTARG"
  191. remap_to_ancestor=t
  192. ;;
  193. --original)
  194. orig_namespace=$(expr "$OPTARG/" : '\(.*[^/]\)/*$')/
  195. ;;
  196. *)
  197. usage
  198. ;;
  199. esac
  200. done
  201.  
  202. case "$prune_empty,$filter_commit" in
  203. ,)
  204. filter_commit='git commit-tree "$@"';;
  205. t,)
  206. filter_commit="$functions;"' git_commit_non_empty_tree "$@"';;
  207. ,*)
  208. ;;
  209. *)
  210. die "Cannot set --prune-empty and --commit-filter at the same time"
  211. esac
  212.  
  213. case "$force" in
  214. t)
  215. rm -rf "$tempdir"
  216. ;;
  217. '')
  218. test -d "$tempdir" &&
  219. die "$tempdir already exists, please remove it"
  220. esac
  221. mkdir -p "$tempdir/t" &&
  222. tempdir="$(cd "$tempdir"; pwd)" &&
  223. cd "$tempdir/t" &&
  224. workdir="$(pwd)" ||
  225. die ""
  226.  
  227. # Remove tempdir on exit
  228. trap 'cd ../..; rm -rf "$tempdir"' 0
  229.  
  230. ORIG_GIT_DIR="$GIT_DIR"
  231. ORIG_GIT_WORK_TREE="$GIT_WORK_TREE"
  232. ORIG_GIT_INDEX_FILE="$GIT_INDEX_FILE"
  233. GIT_WORK_TREE=.
  234. export GIT_DIR GIT_WORK_TREE
  235.  
  236. # Make sure refs/original is empty
  237. git for-each-ref > "$tempdir"/backup-refs || exit
  238. while read sha1 type name
  239. do
  240. case "$force,$name" in
  241. ,$orig_namespace*)
  242. die "Cannot create a new backup.
  243. A previous backup already exists in $orig_namespace
  244. Force overwriting the backup with -f"
  245. ;;
  246. t,$orig_namespace*)
  247. git update-ref -d "$name" $sha1
  248. ;;
  249. esac
  250. done < "$tempdir"/backup-refs
  251.  
  252. # The refs should be updated if their heads were rewritten
  253. git rev-parse --no-flags --revs-only --symbolic-full-name \
  254. --default HEAD "$@" > "$tempdir"/raw-heads || exit
  255. sed -e '/^^/d' "$tempdir"/raw-heads >"$tempdir"/heads
  256.  
  257. test -s "$tempdir"/heads ||
  258. die "Which ref do you want to rewrite?"
  259.  
  260. GIT_INDEX_FILE="$(pwd)/../index"
  261. export GIT_INDEX_FILE
  262.  
  263. # map old->new commit ids for rewriting parents
  264. mkdir ../map || die "Could not create map/ directory"
  265.  
  266. # we need "--" only if there are no path arguments in $@
  267. nonrevs=$(git rev-parse --no-revs "$@") || exit
  268. test -z "$nonrevs" && dashdash=-- || dashdash=
  269. rev_args=$(git rev-parse --revs-only "$@")
  270.  
  271. case "$filter_subdir" in
  272. "")
  273. eval set -- "$(git rev-parse --sq --no-revs "$@")"
  274. ;;
  275. *)
  276. eval set -- "$(git rev-parse --sq --no-revs "$@" $dashdash \
  277. "$filter_subdir")"
  278. ;;
  279. esac
  280.  
  281. git rev-list --reverse --topo-order --default HEAD \
  282. --parents --simplify-merges $rev_args "$@" > ../revs ||
  283. die "Could not get the commits"
  284. commits=$(wc -l <../revs | tr -d " ")
  285.  
  286. test $commits -eq 0 && die "Found nothing to rewrite"
  287.  
  288. # Rewrite the commits
  289.  
  290. git_filter_branch__commit_count=0
  291. while read commit parents; do
  292. git_filter_branch__commit_count=$(($git_filter_branch__commit_count+1))
  293. printf "\rRewrite $commit ($git_filter_branch__commit_count/$commits)"
  294.  
  295. case "$filter_subdir" in
  296. "")
  297. git read-tree -i -m $commit
  298. ;;
  299. *)
  300. # The commit may not have the subdirectory at all
  301. err=$(git read-tree -i -m $commit:"$filter_subdir" 2>&1) || {
  302. if ! git rev-parse -q --verify $commit:"$filter_subdir"
  303. then
  304. rm -f "$GIT_INDEX_FILE"
  305. else
  306. echo >&2 "$err"
  307. false
  308. fi
  309. }
  310. esac || die "Could not initialize the index"
  311.  
  312. GIT_COMMIT=$commit
  313. export GIT_COMMIT
  314. git cat-file commit "$commit" >../commit ||
  315. die "Cannot read commit $commit"
  316.  
  317. eval "$(set_ident AUTHOR <../commit)" ||
  318. die "setting author failed for commit $commit"
  319. eval "$(set_ident COMMITTER <../commit)" ||
  320. die "setting committer failed for commit $commit"
  321. eval "$filter_env" < /dev/null ||
  322. die "env filter failed: $filter_env"
  323.  
  324. if [ "$filter_tree" ]; then
  325. git checkout-index -f -u -a ||
  326. die "Could not checkout the index"
  327. # files that $commit removed are now still in the working tree;
  328. # remove them, else they would be added again
  329. git clean -d -q -f -x
  330. eval "$filter_tree" < /dev/null ||
  331. die "tree filter failed: $filter_tree"
  332.  
  333. (
  334. git diff-index -r --name-only --ignore-submodules $commit &&
  335. git ls-files --others
  336. ) > "$tempdir"/tree-state || exit
  337. git update-index --add --replace --remove --stdin \
  338. < "$tempdir"/tree-state || exit
  339. fi
  340.  
  341. eval "$filter_index" < /dev/null ||
  342. die "index filter failed: $filter_index"
  343.  
  344. parentstr=
  345. for parent in $parents; do
  346. for reparent in $(map "$parent"); do
  347. parentstr="$parentstr -p $reparent"
  348. done
  349. done
  350. if [ "$filter_parent" ]; then
  351. parentstr="$(echo "$parentstr" | eval "$filter_parent")" ||
  352. die "parent filter failed: $filter_parent"
  353. fi
  354.  
  355. sed -e '1,/^$/d' <../commit | \
  356. eval "$filter_msg" > ../message ||
  357. die "msg filter failed: $filter_msg"
  358. /bin/sh -c "$filter_commit" "git commit-tree" \
  359. $(git write-tree) $parentstr < ../message > ../map/$commit ||
  360. die "could not write rewritten commit"
  361. done <../revs
  362.  
  363. # If we are filtering for paths, as in the case of a subdirectory
  364. # filter, it is possible that a specified head is not in the set of
  365. # rewritten commits, because it was pruned by the revision walker.
  366. # Ancestor remapping fixes this by mapping these heads to the unique
  367. # nearest ancestor that survived the pruning.
  368.  
  369. if test "$remap_to_ancestor" = t
  370. then
  371. while read ref
  372. do
  373. sha1=$(git rev-parse "$ref"^0)
  374. test -f "$workdir"/../map/$sha1 && continue
  375. ancestor=$(git rev-list --simplify-merges -1 "$ref" "$@")
  376. test "$ancestor" && echo $(map $ancestor) >> "$workdir"/../map/$sha1
  377. done < "$tempdir"/heads
  378. fi
  379.  
  380. # Finally update the refs
  381.  
  382. _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
  383. _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
  384. echo
  385. while read ref
  386. do
  387. # avoid rewriting a ref twice
  388. test -f "$orig_namespace$ref" && continue
  389.  
  390. sha1=$(git rev-parse "$ref"^0)
  391. rewritten=$(map $sha1)
  392.  
  393. test $sha1 = "$rewritten" &&
  394. warn "WARNING: Ref '$ref' is unchanged" &&
  395. continue
  396.  
  397. case "$rewritten" in
  398. '')
  399. echo "Ref '$ref' was deleted"
  400. git update-ref -m "filter-branch: delete" -d "$ref" $sha1 ||
  401. die "Could not delete $ref"
  402. ;;
  403. $_x40)
  404. echo "Ref '$ref' was rewritten"
  405. if ! git update-ref -m "filter-branch: rewrite" \
  406. "$ref" $rewritten $sha1 2>/dev/null; then
  407. if test $(git cat-file -t "$ref") = tag; then
  408. if test -z "$filter_tag_name"; then
  409. warn "WARNING: You said to rewrite tagged commits, but not the corresponding tag."
  410. warn "WARNING: Perhaps use '--tag-name-filter cat' to rewrite the tag."
  411. fi
  412. else
  413. die "Could not rewrite $ref"
  414. fi
  415. fi
  416. ;;
  417. *)
  418. # NEEDSWORK: possibly add -Werror, making this an error
  419. warn "WARNING: '$ref' was rewritten into multiple commits:"
  420. warn "$rewritten"
  421. warn "WARNING: Ref '$ref' points to the first one now."
  422. rewritten=$(echo "$rewritten" | head -n 1)
  423. git update-ref -m "filter-branch: rewrite to first" \
  424. "$ref" $rewritten $sha1 ||
  425. die "Could not rewrite $ref"
  426. ;;
  427. esac
  428. git update-ref -m "filter-branch: backup" "$orig_namespace$ref" $sha1 ||
  429. exit
  430. done < "$tempdir"/heads
  431.  
  432. # TODO: This should possibly go, with the semantics that all positive given
  433. # refs are updated, and their original heads stored in refs/original/
  434. # Filter tags
  435.  
  436. if [ "$filter_tag_name" ]; then
  437. git for-each-ref --format='%(objectname) %(objecttype) %(refname)' refs/tags |
  438. while read sha1 type ref; do
  439. ref="${ref#refs/tags/}"
  440. # XXX: Rewrite tagged trees as well?
  441. if [ "$type" != "commit" -a "$type" != "tag" ]; then
  442. continue;
  443. fi
  444.  
  445. if [ "$type" = "tag" ]; then
  446. # Dereference to a commit
  447. sha1t="$sha1"
  448. sha1="$(git rev-parse -q "$sha1"^{commit})" || continue
  449. fi
  450.  
  451. [ -f "../map/$sha1" ] || continue
  452. new_sha1="$(cat "../map/$sha1")"
  453. GIT_COMMIT="$sha1"
  454. export GIT_COMMIT
  455. new_ref="$(echo "$ref" | eval "$filter_tag_name")" ||
  456. die "tag name filter failed: $filter_tag_name"
  457.  
  458. echo "$ref -> $new_ref ($sha1 -> $new_sha1)"
  459.  
  460. if [ "$type" = "tag" ]; then
  461. new_sha1=$( ( printf 'object %s\ntype commit\ntag %s\n' \
  462. "$new_sha1" "$new_ref"
  463. git cat-file tag "$ref" |
  464. sed -n \
  465. -e '1,/^$/{
  466. /^object /d
  467. /^type /d
  468. /^tag /d
  469. }' \
  470. -e '/^-----BEGIN PGP SIGNATURE-----/q' \
  471. -e 'p' ) |
  472. git mktag) ||
  473. die "Could not create new tag object for $ref"
  474. if git cat-file tag "$ref" | \
  475. sane_grep '^-----BEGIN PGP SIGNATURE-----' >/dev/null 2>&1
  476. then
  477. warn "gpg signature stripped from tag object $sha1t"
  478. fi
  479. fi
  480.  
  481. git update-ref "refs/tags/$new_ref" "$new_sha1" ||
  482. die "Could not write tag $new_ref"
  483. done
  484. fi
  485.  
  486. cd ../..
  487. rm -rf "$tempdir"
  488.  
  489. trap - 0
  490.  
  491. unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
  492. test -z "$ORIG_GIT_DIR" || {
  493. GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR
  494. }
  495. test -z "$ORIG_GIT_WORK_TREE" || {
  496. GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" &&
  497. export GIT_WORK_TREE
  498. }
  499. test -z "$ORIG_GIT_INDEX_FILE" || {
  500. GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" &&
  501. export GIT_INDEX_FILE
  502. }
  503.  
  504. if [ "$(is_bare_repository)" = false ]; then
  505. git read-tree -u -m HEAD || exit
  506. fi
  507.  
  508. exit 0
Add Comment
Please, Sign In to add comment