Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
git-mirror
/
git
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
0
Pull requests
0
Actions
Projects
0
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Security
Insights
Files
def16e7
Documentation
arm
compat
contrib
git-gui
gitk-git
gitweb
mozilla-sha1
perl
ppc
t
templates
xdiff
.gitignore
.mailmap
COPYING
GIT-VERSION-GEN
INSTALL
Makefile
README
RelNotes
alloc.c
archive-tar.c
archive-zip.c
archive.c
archive.h
attr.c
attr.h
base85.c
blob.c
blob.h
builtin-add.c
builtin-annotate.c
builtin-apply.c
builtin-archive.c
builtin-blame.c
builtin-branch.c
builtin-bundle.c
builtin-cat-file.c
builtin-check-attr.c
builtin-check-ref-format.c
builtin-checkout-index.c
builtin-clean.c
builtin-commit-tree.c
builtin-commit.c
builtin-config.c
builtin-count-objects.c
builtin-describe.c
builtin-diff-files.c
builtin-diff-index.c
builtin-diff-tree.c
builtin-diff.c
builtin-fast-export.c
builtin-fetch--tool.c
builtin-fetch-pack.c
builtin-fetch.c
builtin-fmt-merge-msg.c
builtin-for-each-ref.c
builtin-fsck.c
builtin-gc.c
builtin-grep.c
builtin-http-fetch.c
builtin-init-db.c
builtin-log.c
builtin-ls-files.c
builtin-ls-remote.c
builtin-ls-tree.c
builtin-mailinfo.c
builtin-mailsplit.c
builtin-merge-base.c
builtin-merge-file.c
builtin-merge-ours.c
builtin-mv.c
builtin-name-rev.c
builtin-pack-objects.c
builtin-pack-refs.c
builtin-prune-packed.c
builtin-prune.c
builtin-push.c
builtin-read-tree.c
builtin-reflog.c
builtin-rerere.c
builtin-reset.c
builtin-rev-list.c
builtin-rev-parse.c
builtin-revert.c
builtin-rm.c
builtin-send-pack.c
builtin-shortlog.c
builtin-show-branch.c
builtin-show-ref.c
builtin-stripspace.c
builtin-symbolic-ref.c
builtin-tag.c
builtin-tar-tree.c
builtin-unpack-objects.c
builtin-update-index.c
builtin-update-ref.c
builtin-upload-archive.c
builtin-verify-pack.c
builtin-verify-tag.c
builtin-write-tree.c
builtin.h
bundle.c
bundle.h
cache-tree.c
cache-tree.h
cache.h
check-builtins.sh
check-racy.c
color.c
color.h
combine-diff.c
command-list.txt
commit.c
commit.h
config.c
config.mak.in
configure.ac
connect.c
convert.c
copy.c
csum-file.c
csum-file.h
ctype.c
daemon.c
date.c
decorate.c
decorate.h
delta.h
diff-delta.c
diff-lib.c
diff.c
diff.h
diffcore-break.c
diffcore-delta.c
diffcore-order.c
diffcore-pickaxe.c
diffcore-rename.c
diffcore.h
dir.c
dir.h
dump-cache-tree.c
entry.c
environment.c
exec_cmd.c
exec_cmd.h
fast-import.c
fetch-pack.h
fixup-builtins
generate-cmdlist.sh
git-add--interactive.perl
git-am.sh
git-archimport.perl
git-bisect.sh
git-checkout.sh
git-clone.sh
git-compat-util.h
git-cvsexportcommit.perl
git-cvsimport.perl
git-cvsserver.perl
git-filter-branch.sh
git-help--browse.sh
git-instaweb.sh
git-lost-found.sh
git-merge-octopus.sh
git-merge-one-file.sh
git-merge-resolve.sh
git-merge-stupid.sh
git-merge.sh
git-mergetool.sh
git-parse-remote.sh
git-pull.sh
git-quiltimport.sh
git-rebase--interactive.sh
git-rebase.sh
git-relink.perl
git-remote.perl
git-repack.sh
git-request-pull.sh
git-send-email.perl
git-sh-setup.sh
git-stash.sh
git-submodule.sh
git-svn.perl
git.c
git.spec.in
grep.c
grep.h
hash-object.c
hash.c
hash.h
help.c
http-push.c
http-walker.c
http.c
http.h
ident.c
imap-send.c
index-pack.c
interpolate.c
interpolate.h
list-objects.c
list-objects.h
lockfile.c
log-tree.c
log-tree.h
mailmap.c
mailmap.h
match-trees.c
merge-file.c
merge-index.c
merge-recursive.c
merge-tree.c
mktag.c
mktree.c
object-refs.c
object.c
object.h
pack-check.c
pack-redundant.c
pack-write.c
pack.h
pager.c
parse-options.c
parse-options.h
patch-delta.c
patch-id.c
patch-ids.c
patch-ids.h
path-list.c
path-list.h
path.c
pkt-line.c
pkt-line.h
pretty.c
progress.c
progress.h
quote.c
quote.h
reachable.c
reachable.h
read-cache.c
receive-pack.c
reflog-walk.c
reflog-walk.h
refs.c
refs.h
remote.c
remote.h
revision.c
revision.h
run-command.c
run-command.h
send-pack.h
server-info.c
setup.c
sha1_file.c
sha1_name.c
shallow.c
shell.c
show-index.c
sideband.c
sideband.h
strbuf.c
strbuf.h
symlinks.c
tag.c
tag.h
tar.h
test-absolute-path.c
test-chmtime.c
test-date.c
test-delta.c
test-genrandom.c
test-match-trees.c
test-parse-options.c
test-sha1.c
test-sha1.sh
trace.c
transport.c
transport.h
tree-diff.c
tree-walk.c
tree-walk.h
tree.c
tree.h
unpack-file.c
unpack-trees.c
unpack-trees.h
update-server-info.c
upload-pack.c
usage.c
utf8.c
utf8.h
var.c
walker.c
walker.h
write_or_die.c
ws.c
wt-status.c
wt-status.h
xdiff-interface.c
xdiff-interface.h
Breadcrumbs
git
/
git-filter-branch.sh
Blame
Blame
Latest commit
History
History
executable file
·
422 lines (363 loc) · 9.8 KB
Breadcrumbs
git
/
git-filter-branch.sh
Top
File metadata and controls
Code
Blame
executable file
·
422 lines (363 loc) · 9.8 KB
Raw
#!/bin/sh # # Rewrite revision history # Copyright (c) Petr Baudis, 2006 # Minimal changes to "port" it to core-git (c) Johannes Schindelin, 2007 # # Lets you rewrite the revision history of the current branch, creating # a new branch. You can specify a number of filters to modify the commits, # files and trees. # The following functions will also be available in the commit filter: functions=$(cat << \EOF warn () { echo "$*" >&2 } map() { # if it was not rewritten, take the original if test -r "$workdir/../map/$1" then cat "$workdir/../map/$1" else echo "$1" fi } # if you run 'skip_commit "$@"' in a commit filter, it will print # the (mapped) parents, effectively skipping the commit. skip_commit() { shift; while [ -n "$1" ]; do shift; map "$1"; shift; done; } # override die(): this version puts in an extra line break, so that # the progress is still visible die() { echo >&2 echo "$*" >&2 exit 1 } EOF ) eval "$functions" # When piped a commit, output a script to set the ident of either # "author" or "committer set_ident () { lid="$(echo "$1" | tr "A-Z" "a-z")" uid="$(echo "$1" | tr "a-z" "A-Z")" pick_id_script=' /^'$lid' /{ s/'\''/'\''\\'\'\''/g h s/^'$lid' \([^<]*\) <[^>]*> .*$/\1/ s/'\''/'\''\'\'\''/g s/.*/GIT_'$uid'_NAME='\''&'\''; export GIT_'$uid'_NAME/p g s/^'$lid' [^<]* <\([^>]*\)> .*$/\1/ s/'\''/'\''\'\'\''/g s/.*/GIT_'$uid'_EMAIL='\''&'\''; export GIT_'$uid'_EMAIL/p g s/^'$lid' [^<]* <[^>]*> \(.*\)$/\1/ s/'\''/'\''\'\'\''/g s/.*/GIT_'$uid'_DATE='\''&'\''; export GIT_'$uid'_DATE/p q } ' LANG=C LC_ALL=C sed -ne "$pick_id_script" # Ensure non-empty id name. echo "case \"\$GIT_${uid}_NAME\" in \"\") GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\" && export GIT_${uid}_NAME;; esac" } USAGE="[--env-filter <command>] [--tree-filter <command>] \ [--index-filter <command>] [--parent-filter <command>] \ [--msg-filter <command>] [--commit-filter <command>] \ [--tag-name-filter <command>] [--subdirectory-filter <directory>] \ [--original <namespace>] [-d <directory>] [-f | --force] \ [<rev-list options>...]" OPTIONS_SPEC= . git-sh-setup git diff-files --quiet && git diff-index --cached --quiet HEAD -- || die "Cannot rewrite branch(es) with a dirty working directory." tempdir=.git-rewrite filter_env= filter_tree= filter_index= filter_parent= filter_msg=cat filter_commit='git commit-tree "$@"' filter_tag_name= filter_subdir= orig_namespace=refs/original/ force= while : do test $# = 0 && usage case "$1" in --) shift break ;; --force|-f) shift force=t continue ;; -*) ;; *) break; esac # all switches take one argument ARG="$1" case "$#" in 1) usage ;; esac shift OPTARG="$1" shift case "$ARG" in -d) tempdir="$OPTARG" ;; --env-filter) filter_env="$OPTARG" ;; --tree-filter) filter_tree="$OPTARG" ;; --index-filter) filter_index="$OPTARG" ;; --parent-filter) filter_parent="$OPTARG" ;; --msg-filter) filter_msg="$OPTARG" ;; --commit-filter) filter_commit="$functions; $OPTARG" ;; --tag-name-filter) filter_tag_name="$OPTARG" ;; --subdirectory-filter) filter_subdir="$OPTARG" ;; --original) orig_namespace=$(expr "$OPTARG/" : '\(.*[^/]\)/*$')/ ;; *) usage ;; esac done case "$force" in t) rm -rf "$tempdir" ;; '') test -d "$tempdir" && die "$tempdir already exists, please remove it" esac mkdir -p "$tempdir/t" && tempdir="$(cd "$tempdir"; pwd)" && cd "$tempdir/t" && workdir="$(pwd)" || die "" # Remove tempdir on exit trap 'cd ../..; rm -rf "$tempdir"' 0 # Make sure refs/original is empty git for-each-ref > "$tempdir"/backup-refs while read sha1 type name do case "$force,$name" in ,$orig_namespace*) die "Namespace $orig_namespace not empty" ;; t,$orig_namespace*) git update-ref -d "$name" $sha1 ;; esac done < "$tempdir"/backup-refs ORIG_GIT_DIR="$GIT_DIR" ORIG_GIT_WORK_TREE="$GIT_WORK_TREE" ORIG_GIT_INDEX_FILE="$GIT_INDEX_FILE" GIT_WORK_TREE=. export GIT_DIR GIT_WORK_TREE # The refs should be updated if their heads were rewritten git rev-parse --no-flags --revs-only --symbolic-full-name "$@" | sed -e '/^^/d' >"$tempdir"/heads test -s "$tempdir"/heads || die "Which ref do you want to rewrite?" GIT_INDEX_FILE="$(pwd)/../index" export GIT_INDEX_FILE git read-tree || die "Could not seed the index" ret=0 # map old->new commit ids for rewriting parents mkdir ../map || die "Could not create map/ directory" case "$filter_subdir" in "") git rev-list --reverse --topo-order --default HEAD \ --parents "$@" ;; *) git rev-list --reverse --topo-order --default HEAD \ --parents --full-history "$@" -- "$filter_subdir" esac > ../revs || die "Could not get the commits" commits=$(wc -l <../revs | tr -d " ") test $commits -eq 0 && die "Found nothing to rewrite" # Rewrite the commits i=0 while read commit parents; do i=$(($i+1)) printf "\rRewrite $commit ($i/$commits)" case "$filter_subdir" in "") git read-tree -i -m $commit ;; *) git read-tree -i -m $commit:"$filter_subdir" esac || die "Could not initialize the index" GIT_COMMIT=$commit export GIT_COMMIT git cat-file commit "$commit" >../commit || die "Cannot read commit $commit" eval "$(set_ident AUTHOR <../commit)" || die "setting author failed for commit $commit" eval "$(set_ident COMMITTER <../commit)" || die "setting committer failed for commit $commit" eval "$filter_env" < /dev/null || die "env filter failed: $filter_env" if [ "$filter_tree" ]; then git checkout-index -f -u -a || die "Could not checkout the index" # files that $commit removed are now still in the working tree; # remove them, else they would be added again git ls-files -z --others | xargs -0 rm -f eval "$filter_tree" < /dev/null || die "tree filter failed: $filter_tree" git diff-index -r $commit | cut -f 2- | tr '\012' '\000' | \ xargs -0 git update-index --add --replace --remove git ls-files -z --others | \ xargs -0 git update-index --add --replace --remove fi eval "$filter_index" < /dev/null || die "index filter failed: $filter_index" parentstr= for parent in $parents; do for reparent in $(map "$parent"); do parentstr="$parentstr -p $reparent" done done if [ "$filter_parent" ]; then parentstr="$(echo "$parentstr" | eval "$filter_parent")" || die "parent filter failed: $filter_parent" fi sed -e '1,/^$/d' <../commit | \ eval "$filter_msg" > ../message || die "msg filter failed: $filter_msg" sh -c "$filter_commit" "git commit-tree" \ $(git write-tree) $parentstr < ../message > ../map/$commit done <../revs # In case of a subdirectory filter, it is possible that a specified head # is not in the set of rewritten commits, because it was pruned by the # revision walker. Fix it by mapping these heads to the next rewritten # ancestor(s), i.e. the boundaries in the set of rewritten commits. # NEEDSWORK: we should sort the unmapped refs topologically first while read ref do sha1=$(git rev-parse "$ref"^0) test -f "$workdir"/../map/$sha1 && continue # Assign the boundarie(s) in the set of rewritten commits # as the replacement commit(s). # (This would look a bit nicer if --not --stdin worked.) for p in $( (cd "$workdir"/../map; ls | sed "s/^/^/") | git rev-list $ref --boundary --stdin | sed -n "s/^-//p") do map $p >> "$workdir"/../map/$sha1 done done < "$tempdir"/heads # Finally update the refs _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" echo while read ref do # avoid rewriting a ref twice test -f "$orig_namespace$ref" && continue sha1=$(git rev-parse "$ref"^0) rewritten=$(map $sha1) test $sha1 = "$rewritten" && warn "WARNING: Ref '$ref' is unchanged" && continue case "$rewritten" in '') echo "Ref '$ref' was deleted" git update-ref -m "filter-branch: delete" -d "$ref" $sha1 || die "Could not delete $ref" ;; $_x40) echo "Ref '$ref' was rewritten" git update-ref -m "filter-branch: rewrite" \ "$ref" $rewritten $sha1 || die "Could not rewrite $ref" ;; *) # NEEDSWORK: possibly add -Werror, making this an error warn "WARNING: '$ref' was rewritten into multiple commits:" warn "$rewritten" warn "WARNING: Ref '$ref' points to the first one now." rewritten=$(echo "$rewritten" | head -n 1) git update-ref -m "filter-branch: rewrite to first" \ "$ref" $rewritten $sha1 || die "Could not rewrite $ref" ;; esac git update-ref -m "filter-branch: backup" "$orig_namespace$ref" $sha1 done < "$tempdir"/heads # TODO: This should possibly go, with the semantics that all positive given # refs are updated, and their original heads stored in refs/original/ # Filter tags if [ "$filter_tag_name" ]; then git for-each-ref --format='%(objectname) %(objecttype) %(refname)' refs/tags | while read sha1 type ref; do ref="${ref#refs/tags/}" # XXX: Rewrite tagged trees as well? if [ "$type" != "commit" -a "$type" != "tag" ]; then continue; fi if [ "$type" = "tag" ]; then # Dereference to a commit sha1t="$sha1" sha1="$(git rev-parse "$sha1"^{commit} 2>/dev/null)" || continue fi [ -f "../map/$sha1" ] || continue new_sha1="$(cat "../map/$sha1")" GIT_COMMIT="$sha1" export GIT_COMMIT new_ref="$(echo "$ref" | eval "$filter_tag_name")" || die "tag name filter failed: $filter_tag_name" echo "$ref -> $new_ref ($sha1 -> $new_sha1)" if [ "$type" = "tag" ]; then # Warn that we are not rewriting the tag object itself. warn "unreferencing tag object $sha1t" fi git update-ref "refs/tags/$new_ref" "$new_sha1" || die "Could not write tag $new_ref" done fi cd ../.. rm -rf "$tempdir" trap - 0 unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE test -z "$ORIG_GIT_DIR" || GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR test -z "$ORIG_GIT_WORK_TREE" || GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" && export GIT_WORK_TREE test -z "$ORIG_GIT_INDEX_FILE" || GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" && export GIT_INDEX_FILE git read-tree -u -m HEAD exit $ret
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
You can’t perform that action at this time.