THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Git Source Code Mirror - This is a publish-only repository and all pull requests are ignored. Please follow Documentation/SubmittingPatches procedure for any of your improvements.
git
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.

677 lines
16KB

  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. # The following functions will also be available in the commit filter:
  11. functions=$(cat << \EOF
  12. EMPTY_TREE=$(git hash-object -t tree /dev/null)
  13. warn () {
  14. echo "$*" >&2
  15. }
  16. map()
  17. {
  18. # if it was not rewritten, take the original
  19. if test -r "$workdir/../map/$1"
  20. then
  21. cat "$workdir/../map/$1"
  22. else
  23. echo "$1"
  24. fi
  25. }
  26. # if you run 'skip_commit "$@"' in a commit filter, it will print
  27. # the (mapped) parents, effectively skipping the commit.
  28. skip_commit()
  29. {
  30. shift;
  31. while [ -n "$1" ];
  32. do
  33. shift;
  34. map "$1";
  35. shift;
  36. done;
  37. }
  38. # if you run 'git_commit_non_empty_tree "$@"' in a commit filter,
  39. # it will skip commits that leave the tree untouched, commit the other.
  40. git_commit_non_empty_tree()
  41. {
  42. if test $# = 3 && test "$1" = $(git rev-parse "$3^{tree}"); then
  43. map "$3"
  44. elif test $# = 1 && test "$1" = $EMPTY_TREE; then
  45. :
  46. else
  47. git commit-tree "$@"
  48. fi
  49. }
  50. # override die(): this version puts in an extra line break, so that
  51. # the progress is still visible
  52. die()
  53. {
  54. echo >&2
  55. echo "$*" >&2
  56. exit 1
  57. }
  58. EOF
  59. )
  60. eval "$functions"
  61. finish_ident() {
  62. # Ensure non-empty id name.
  63. echo "case \"\$GIT_$1_NAME\" in \"\") GIT_$1_NAME=\"\${GIT_$1_EMAIL%%@*}\" && export GIT_$1_NAME;; esac"
  64. # And make sure everything is exported.
  65. echo "export GIT_$1_NAME"
  66. echo "export GIT_$1_EMAIL"
  67. echo "export GIT_$1_DATE"
  68. }
  69. set_ident () {
  70. parse_ident_from_commit author AUTHOR committer COMMITTER
  71. finish_ident AUTHOR
  72. finish_ident COMMITTER
  73. }
  74. if test -z "$FILTER_BRANCH_SQUELCH_WARNING$GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS"
  75. then
  76. cat <<EOF
  77. WARNING: git-filter-branch has a glut of gotchas generating mangled history
  78. rewrites. Hit Ctrl-C before proceeding to abort, then use an
  79. alternative filtering tool such as 'git filter-repo'
  80. (https://github.com/newren/git-filter-repo/) instead. See the
  81. filter-branch manual page for more details; to squelch this warning,
  82. set FILTER_BRANCH_SQUELCH_WARNING=1.
  83. EOF
  84. sleep 10
  85. printf "Proceeding with filter-branch...\n\n"
  86. fi
  87. USAGE="[--setup <command>] [--subdirectory-filter <directory>] [--env-filter <command>]
  88. [--tree-filter <command>] [--index-filter <command>]
  89. [--parent-filter <command>] [--msg-filter <command>]
  90. [--commit-filter <command>] [--tag-name-filter <command>]
  91. [--original <namespace>]
  92. [-d <directory>] [-f | --force] [--state-branch <branch>]
  93. [--] [<rev-list options>...]"
  94. OPTIONS_SPEC=
  95. . git-sh-setup
  96. if [ "$(is_bare_repository)" = false ]; then
  97. require_clean_work_tree 'rewrite branches'
  98. fi
  99. tempdir=.git-rewrite
  100. filter_setup=
  101. filter_env=
  102. filter_tree=
  103. filter_index=
  104. filter_parent=
  105. filter_msg=cat
  106. filter_commit=
  107. filter_tag_name=
  108. filter_subdir=
  109. state_branch=
  110. orig_namespace=refs/original/
  111. force=
  112. prune_empty=
  113. remap_to_ancestor=
  114. while :
  115. do
  116. case "$1" in
  117. --)
  118. shift
  119. break
  120. ;;
  121. --force|-f)
  122. shift
  123. force=t
  124. continue
  125. ;;
  126. --remap-to-ancestor)
  127. # deprecated ($remap_to_ancestor is set now automatically)
  128. shift
  129. remap_to_ancestor=t
  130. continue
  131. ;;
  132. --prune-empty)
  133. shift
  134. prune_empty=t
  135. continue
  136. ;;
  137. -*)
  138. ;;
  139. *)
  140. break;
  141. esac
  142. # all switches take one argument
  143. ARG="$1"
  144. case "$#" in 1) usage ;; esac
  145. shift
  146. OPTARG="$1"
  147. shift
  148. case "$ARG" in
  149. -d)
  150. tempdir="$OPTARG"
  151. ;;
  152. --setup)
  153. filter_setup="$OPTARG"
  154. ;;
  155. --subdirectory-filter)
  156. filter_subdir="$OPTARG"
  157. remap_to_ancestor=t
  158. ;;
  159. --env-filter)
  160. filter_env="$OPTARG"
  161. ;;
  162. --tree-filter)
  163. filter_tree="$OPTARG"
  164. ;;
  165. --index-filter)
  166. filter_index="$OPTARG"
  167. ;;
  168. --parent-filter)
  169. filter_parent="$OPTARG"
  170. ;;
  171. --msg-filter)
  172. filter_msg="$OPTARG"
  173. ;;
  174. --commit-filter)
  175. filter_commit="$functions; $OPTARG"
  176. ;;
  177. --tag-name-filter)
  178. filter_tag_name="$OPTARG"
  179. ;;
  180. --original)
  181. orig_namespace=$(expr "$OPTARG/" : '\(.*[^/]\)/*$')/
  182. ;;
  183. --state-branch)
  184. state_branch="$OPTARG"
  185. ;;
  186. *)
  187. usage
  188. ;;
  189. esac
  190. done
  191. case "$prune_empty,$filter_commit" in
  192. ,)
  193. filter_commit='git commit-tree "$@"';;
  194. t,)
  195. filter_commit="$functions;"' git_commit_non_empty_tree "$@"';;
  196. ,*)
  197. ;;
  198. *)
  199. die "Cannot set --prune-empty and --commit-filter at the same time"
  200. esac
  201. case "$force" in
  202. t)
  203. rm -rf "$tempdir"
  204. ;;
  205. '')
  206. test -d "$tempdir" &&
  207. die "$tempdir already exists, please remove it"
  208. esac
  209. orig_dir=$(pwd)
  210. mkdir -p "$tempdir/t" &&
  211. tempdir="$(cd "$tempdir"; pwd)" &&
  212. cd "$tempdir/t" &&
  213. workdir="$(pwd)" ||
  214. die ""
  215. # Remove tempdir on exit
  216. trap 'cd "$orig_dir"; rm -rf "$tempdir"' 0
  217. ORIG_GIT_DIR="$GIT_DIR"
  218. ORIG_GIT_WORK_TREE="$GIT_WORK_TREE"
  219. ORIG_GIT_INDEX_FILE="$GIT_INDEX_FILE"
  220. ORIG_GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME"
  221. ORIG_GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL"
  222. ORIG_GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE"
  223. ORIG_GIT_COMMITTER_NAME="$GIT_COMMITTER_NAME"
  224. ORIG_GIT_COMMITTER_EMAIL="$GIT_COMMITTER_EMAIL"
  225. ORIG_GIT_COMMITTER_DATE="$GIT_COMMITTER_DATE"
  226. GIT_WORK_TREE=.
  227. export GIT_DIR GIT_WORK_TREE
  228. # Make sure refs/original is empty
  229. git for-each-ref > "$tempdir"/backup-refs || exit
  230. while read sha1 type name
  231. do
  232. case "$force,$name" in
  233. ,$orig_namespace*)
  234. die "Cannot create a new backup.
  235. A previous backup already exists in $orig_namespace
  236. Force overwriting the backup with -f"
  237. ;;
  238. t,$orig_namespace*)
  239. git update-ref -d "$name" $sha1
  240. ;;
  241. esac
  242. done < "$tempdir"/backup-refs
  243. # The refs should be updated if their heads were rewritten
  244. git rev-parse --no-flags --revs-only --symbolic-full-name \
  245. --default HEAD "$@" > "$tempdir"/raw-refs || exit
  246. while read ref
  247. do
  248. case "$ref" in ^?*) continue ;; esac
  249. if git rev-parse --verify "$ref"^0 >/dev/null 2>&1
  250. then
  251. echo "$ref"
  252. else
  253. warn "WARNING: not rewriting '$ref' (not a committish)"
  254. fi
  255. done >"$tempdir"/heads <"$tempdir"/raw-refs
  256. test -s "$tempdir"/heads ||
  257. die "You must specify a ref to rewrite."
  258. GIT_INDEX_FILE="$(pwd)/../index"
  259. export GIT_INDEX_FILE
  260. # map old->new commit ids for rewriting parents
  261. mkdir ../map || die "Could not create map/ directory"
  262. if test -n "$state_branch"
  263. then
  264. state_commit=$(git rev-parse --no-flags --revs-only "$state_branch")
  265. if test -n "$state_commit"
  266. then
  267. echo "Populating map from $state_branch ($state_commit)" 1>&2
  268. perl -e'open(MAP, "-|", "git show $ARGV[0]:filter.map") or die;
  269. while (<MAP>) {
  270. m/(.*):(.*)/ or die;
  271. open F, ">../map/$1" or die;
  272. print F "$2" or die;
  273. close(F) or die;
  274. }
  275. close(MAP) or die;' "$state_commit" \
  276. || die "Unable to load state from $state_branch:filter.map"
  277. else
  278. echo "Branch $state_branch does not exist. Will create" 1>&2
  279. fi
  280. fi
  281. # we need "--" only if there are no path arguments in $@
  282. nonrevs=$(git rev-parse --no-revs "$@") || exit
  283. if test -z "$nonrevs"
  284. then
  285. dashdash=--
  286. else
  287. dashdash=
  288. remap_to_ancestor=t
  289. fi
  290. git rev-parse --revs-only "$@" >../parse
  291. case "$filter_subdir" in
  292. "")
  293. eval set -- "$(git rev-parse --sq --no-revs "$@")"
  294. ;;
  295. *)
  296. eval set -- "$(git rev-parse --sq --no-revs "$@" $dashdash \
  297. "$filter_subdir")"
  298. ;;
  299. esac
  300. git rev-list --reverse --topo-order --default HEAD \
  301. --parents --simplify-merges --stdin "$@" <../parse >../revs ||
  302. die "Could not get the commits"
  303. commits=$(wc -l <../revs | tr -d " ")
  304. test $commits -eq 0 && die_with_status 2 "Found nothing to rewrite"
  305. # Rewrite the commits
  306. report_progress ()
  307. {
  308. if test -n "$progress" &&
  309. test $git_filter_branch__commit_count -gt $next_sample_at
  310. then
  311. count=$git_filter_branch__commit_count
  312. now=$(date +%s)
  313. elapsed=$(($now - $start_timestamp))
  314. remaining=$(( ($commits - $count) * $elapsed / $count ))
  315. if test $elapsed -gt 0
  316. then
  317. next_sample_at=$(( ($elapsed + 1) * $count / $elapsed ))
  318. else
  319. next_sample_at=$(($next_sample_at + 1))
  320. fi
  321. progress=" ($elapsed seconds passed, remaining $remaining predicted)"
  322. fi
  323. printf "\rRewrite $commit ($count/$commits)$progress "
  324. }
  325. git_filter_branch__commit_count=0
  326. progress= start_timestamp=
  327. if date '+%s' 2>/dev/null | grep -q '^[0-9][0-9]*$'
  328. then
  329. next_sample_at=0
  330. progress="dummy to ensure this is not empty"
  331. start_timestamp=$(date '+%s')
  332. fi
  333. if test -n "$filter_index" ||
  334. test -n "$filter_tree" ||
  335. test -n "$filter_subdir"
  336. then
  337. need_index=t
  338. else
  339. need_index=
  340. fi
  341. eval "$filter_setup" < /dev/null ||
  342. die "filter setup failed: $filter_setup"
  343. while read commit parents; do
  344. git_filter_branch__commit_count=$(($git_filter_branch__commit_count+1))
  345. report_progress
  346. test -f "$workdir"/../map/$commit && continue
  347. case "$filter_subdir" in
  348. "")
  349. if test -n "$need_index"
  350. then
  351. GIT_ALLOW_NULL_SHA1=1 git read-tree -i -m $commit
  352. fi
  353. ;;
  354. *)
  355. # The commit may not have the subdirectory at all
  356. err=$(GIT_ALLOW_NULL_SHA1=1 \
  357. git read-tree -i -m $commit:"$filter_subdir" 2>&1) || {
  358. if ! git rev-parse -q --verify $commit:"$filter_subdir"
  359. then
  360. rm -f "$GIT_INDEX_FILE"
  361. else
  362. echo >&2 "$err"
  363. false
  364. fi
  365. }
  366. esac || die "Could not initialize the index"
  367. GIT_COMMIT=$commit
  368. export GIT_COMMIT
  369. git cat-file commit "$commit" >../commit ||
  370. die "Cannot read commit $commit"
  371. eval "$(set_ident <../commit)" ||
  372. die "setting author/committer failed for commit $commit"
  373. eval "$filter_env" < /dev/null ||
  374. die "env filter failed: $filter_env"
  375. if [ "$filter_tree" ]; then
  376. git checkout-index -f -u -a ||
  377. die "Could not checkout the index"
  378. # files that $commit removed are now still in the working tree;
  379. # remove them, else they would be added again
  380. git clean -d -q -f -x
  381. eval "$filter_tree" < /dev/null ||
  382. die "tree filter failed: $filter_tree"
  383. (
  384. git diff-index -r --name-only --ignore-submodules $commit -- &&
  385. git ls-files --others
  386. ) > "$tempdir"/tree-state || exit
  387. git update-index --add --replace --remove --stdin \
  388. < "$tempdir"/tree-state || exit
  389. fi
  390. eval "$filter_index" < /dev/null ||
  391. die "index filter failed: $filter_index"
  392. parentstr=
  393. for parent in $parents; do
  394. for reparent in $(map "$parent"); do
  395. case "$parentstr " in
  396. *" -p $reparent "*)
  397. ;;
  398. *)
  399. parentstr="$parentstr -p $reparent"
  400. ;;
  401. esac
  402. done
  403. done
  404. if [ "$filter_parent" ]; then
  405. parentstr="$(echo "$parentstr" | eval "$filter_parent")" ||
  406. die "parent filter failed: $filter_parent"
  407. fi
  408. {
  409. while IFS='' read -r header_line && test -n "$header_line"
  410. do
  411. # skip header lines...
  412. :;
  413. done
  414. # and output the actual commit message
  415. cat
  416. } <../commit |
  417. eval "$filter_msg" > ../message ||
  418. die "msg filter failed: $filter_msg"
  419. if test -n "$need_index"
  420. then
  421. tree=$(git write-tree)
  422. else
  423. tree=$(git rev-parse "$commit^{tree}")
  424. fi
  425. workdir=$workdir @SHELL_PATH@ -c "$filter_commit" "git commit-tree" \
  426. "$tree" $parentstr < ../message > ../map/$commit ||
  427. die "could not write rewritten commit"
  428. done <../revs
  429. # If we are filtering for paths, as in the case of a subdirectory
  430. # filter, it is possible that a specified head is not in the set of
  431. # rewritten commits, because it was pruned by the revision walker.
  432. # Ancestor remapping fixes this by mapping these heads to the unique
  433. # nearest ancestor that survived the pruning.
  434. if test "$remap_to_ancestor" = t
  435. then
  436. while read ref
  437. do
  438. sha1=$(git rev-parse "$ref"^0)
  439. test -f "$workdir"/../map/$sha1 && continue
  440. ancestor=$(git rev-list --simplify-merges -1 "$ref" "$@")
  441. test "$ancestor" && echo $(map $ancestor) >> "$workdir"/../map/$sha1
  442. done < "$tempdir"/heads
  443. fi
  444. # Finally update the refs
  445. _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
  446. _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
  447. echo
  448. while read ref
  449. do
  450. # avoid rewriting a ref twice
  451. test -f "$orig_namespace$ref" && continue
  452. sha1=$(git rev-parse "$ref"^0)
  453. rewritten=$(map $sha1)
  454. test $sha1 = "$rewritten" &&
  455. warn "WARNING: Ref '$ref' is unchanged" &&
  456. continue
  457. case "$rewritten" in
  458. '')
  459. echo "Ref '$ref' was deleted"
  460. git update-ref -m "filter-branch: delete" -d "$ref" $sha1 ||
  461. die "Could not delete $ref"
  462. ;;
  463. $_x40)
  464. echo "Ref '$ref' was rewritten"
  465. if ! git update-ref -m "filter-branch: rewrite" \
  466. "$ref" $rewritten $sha1 2>/dev/null; then
  467. if test $(git cat-file -t "$ref") = tag; then
  468. if test -z "$filter_tag_name"; then
  469. warn "WARNING: You said to rewrite tagged commits, but not the corresponding tag."
  470. warn "WARNING: Perhaps use '--tag-name-filter cat' to rewrite the tag."
  471. fi
  472. else
  473. die "Could not rewrite $ref"
  474. fi
  475. fi
  476. ;;
  477. *)
  478. # NEEDSWORK: possibly add -Werror, making this an error
  479. warn "WARNING: '$ref' was rewritten into multiple commits:"
  480. warn "$rewritten"
  481. warn "WARNING: Ref '$ref' points to the first one now."
  482. rewritten=$(echo "$rewritten" | head -n 1)
  483. git update-ref -m "filter-branch: rewrite to first" \
  484. "$ref" $rewritten $sha1 ||
  485. die "Could not rewrite $ref"
  486. ;;
  487. esac
  488. git update-ref -m "filter-branch: backup" "$orig_namespace$ref" $sha1 ||
  489. exit
  490. done < "$tempdir"/heads
  491. # TODO: This should possibly go, with the semantics that all positive given
  492. # refs are updated, and their original heads stored in refs/original/
  493. # Filter tags
  494. if [ "$filter_tag_name" ]; then
  495. git for-each-ref --format='%(objectname) %(objecttype) %(refname)' refs/tags |
  496. while read sha1 type ref; do
  497. ref="${ref#refs/tags/}"
  498. # XXX: Rewrite tagged trees as well?
  499. if [ "$type" != "commit" -a "$type" != "tag" ]; then
  500. continue;
  501. fi
  502. if [ "$type" = "tag" ]; then
  503. # Dereference to a commit
  504. sha1t="$sha1"
  505. sha1="$(git rev-parse -q "$sha1"^{commit})" || continue
  506. fi
  507. [ -f "../map/$sha1" ] || continue
  508. new_sha1="$(cat "../map/$sha1")"
  509. GIT_COMMIT="$sha1"
  510. export GIT_COMMIT
  511. new_ref="$(echo "$ref" | eval "$filter_tag_name")" ||
  512. die "tag name filter failed: $filter_tag_name"
  513. echo "$ref -> $new_ref ($sha1 -> $new_sha1)"
  514. if [ "$type" = "tag" ]; then
  515. new_sha1=$( ( printf 'object %s\ntype commit\ntag %s\n' \
  516. "$new_sha1" "$new_ref"
  517. git cat-file tag "$ref" |
  518. sed -n \
  519. -e '1,/^$/{
  520. /^object /d
  521. /^type /d
  522. /^tag /d
  523. }' \
  524. -e '/^-----BEGIN PGP SIGNATURE-----/q' \
  525. -e 'p' ) |
  526. git hash-object -t tag -w --stdin) ||
  527. die "Could not create new tag object for $ref"
  528. if git cat-file tag "$ref" | \
  529. sane_grep '^-----BEGIN PGP SIGNATURE-----' >/dev/null 2>&1
  530. then
  531. warn "gpg signature stripped from tag object $sha1t"
  532. fi
  533. fi
  534. git update-ref "refs/tags/$new_ref" "$new_sha1" ||
  535. die "Could not write tag $new_ref"
  536. done
  537. fi
  538. unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
  539. unset GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
  540. unset GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL GIT_COMMITTER_DATE
  541. test -z "$ORIG_GIT_DIR" || {
  542. GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR
  543. }
  544. test -z "$ORIG_GIT_WORK_TREE" || {
  545. GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" &&
  546. export GIT_WORK_TREE
  547. }
  548. test -z "$ORIG_GIT_INDEX_FILE" || {
  549. GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" &&
  550. export GIT_INDEX_FILE
  551. }
  552. test -z "$ORIG_GIT_AUTHOR_NAME" || {
  553. GIT_AUTHOR_NAME="$ORIG_GIT_AUTHOR_NAME" &&
  554. export GIT_AUTHOR_NAME
  555. }
  556. test -z "$ORIG_GIT_AUTHOR_EMAIL" || {
  557. GIT_AUTHOR_EMAIL="$ORIG_GIT_AUTHOR_EMAIL" &&
  558. export GIT_AUTHOR_EMAIL
  559. }
  560. test -z "$ORIG_GIT_AUTHOR_DATE" || {
  561. GIT_AUTHOR_DATE="$ORIG_GIT_AUTHOR_DATE" &&
  562. export GIT_AUTHOR_DATE
  563. }
  564. test -z "$ORIG_GIT_COMMITTER_NAME" || {
  565. GIT_COMMITTER_NAME="$ORIG_GIT_COMMITTER_NAME" &&
  566. export GIT_COMMITTER_NAME
  567. }
  568. test -z "$ORIG_GIT_COMMITTER_EMAIL" || {
  569. GIT_COMMITTER_EMAIL="$ORIG_GIT_COMMITTER_EMAIL" &&
  570. export GIT_COMMITTER_EMAIL
  571. }
  572. test -z "$ORIG_GIT_COMMITTER_DATE" || {
  573. GIT_COMMITTER_DATE="$ORIG_GIT_COMMITTER_DATE" &&
  574. export GIT_COMMITTER_DATE
  575. }
  576. if test -n "$state_branch"
  577. then
  578. echo "Saving rewrite state to $state_branch" 1>&2
  579. state_blob=$(
  580. perl -e'opendir D, "../map" or die;
  581. open H, "|-", "git hash-object -w --stdin" or die;
  582. foreach (sort readdir(D)) {
  583. next if m/^\.\.?$/;
  584. open F, "<../map/$_" or die;
  585. chomp($f = <F>);
  586. print H "$_:$f\n" or die;
  587. }
  588. close(H) or die;' || die "Unable to save state")
  589. state_tree=$(printf '100644 blob %s\tfilter.map\n' "$state_blob" | git mktree)
  590. if test -n "$state_commit"
  591. then
  592. state_commit=$(echo "Sync" | git commit-tree "$state_tree" -p "$state_commit")
  593. else
  594. state_commit=$(echo "Sync" | git commit-tree "$state_tree" )
  595. fi
  596. git update-ref "$state_branch" "$state_commit"
  597. fi
  598. cd "$orig_dir"
  599. rm -rf "$tempdir"
  600. trap - 0
  601. if [ "$(is_bare_repository)" = false ]; then
  602. git read-tree -u -m HEAD || exit
  603. fi
  604. exit 0