2018-11-05 14:30:50 +00:00
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
|
|
|
|
test_description='Basic filter-repo tests'
|
|
|
|
|
|
|
|
|
|
. ./test-lib.sh
|
|
|
|
|
|
|
|
|
|
export PATH=$(dirname $TEST_DIRECTORY):$PATH # Put git-filter-repo in PATH
|
|
|
|
|
|
|
|
|
|
DATA="$TEST_DIRECTORY/t9390"
|
|
|
|
|
|
|
|
|
|
filter_testcase() {
|
|
|
|
|
INPUT=$1
|
|
|
|
|
OUTPUT=$2
|
|
|
|
|
shift 2
|
|
|
|
|
REST=("$@")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NAME="check: $INPUT -> $OUTPUT using '${REST[@]}'"
|
|
|
|
|
test_expect_success "$NAME" '
|
|
|
|
|
# Clean up from previous run
|
|
|
|
|
git pack-refs --all &&
|
|
|
|
|
rm .git/packed-refs &&
|
|
|
|
|
|
|
|
|
|
# Run the example
|
filter-repo: add --replace-refs option
This adds the ability to automatically add new replacement refs for each
rewritten commit (as well as delete or update replacement refs that
existed before the run). This will allow users to use either new or old
commit hashes to reference commits locally, though old commit hashes
will need to be unabbreviated. The only requirement for this to work,
is that the person who does the rewrite also needs to push the replace
refs up where other users can grab them, and users who want to use them
need to modify their fetch refspecs to grab the replace refs.
However, other tools external to git may not understand replace refs...
Tools like Gerrit and GitHub apparently do not yet natively understand
replace refs. Trying to view "commits" by the replacement ref will
yield various forms of "Not Found" in each tool. One has to instead try
to view it as a branch with an odd name (including "refs/replace/"), and
often branches are accessed via a different URL style than commits so it
becomes very non-obvious to users how to access the info associated with
an old commit hash.
* In Gerrit, instead of being able to search on the sha1sum or use a
pre-defined URL to search and auto-redirect to the appropriate code
review with
https://gerrit.SITE.COM/#/q/${OLD_SHA1SUM},n,z
one instead has to have a special plugin and go to a URL like
https://gerrit.SITE.COM/plugins/gitiles/ORG/REPO/+/refs/replace/${OLD_SHA1SUM}
but then the user isn't shown the actual code review and will need
to guess which link to click on to get to it (and it'll only be
there if the user included a Change-Id in the commit message).
* In GitHub, instead of being able to go to a URL like
https://github.SITE.COM/ORG/REPO/commit/${OLD_SHA1SUM}
one instead has to navigate based on branch using
https://github.SITE.COM/ORG/REPO/tree/refs/replace/${OLD_SHA1SUM}
but that will show a listing of commits instead of information about
a specific commit; the user has to manually click on the first commit
to get to the desired location.
For now, providing replace refs at least allows users to access
information locally using old IDs; perhaps in time as other external
tools will gain a better understanding of how to use replace refs, the
barrier to history rewrites will decrease enough that big projects that
really need it (e.g. those that have committed many sins by commiting
stupidly large useless binary blobs) can at least seriously contemplate
the undertaking. History rewrites will always have some drawbacks and
pain associated with them, as they should, but when warranted it's nice
to have transition plans that are more smooth than a massive flag day.
Signed-off-by: Elijah Newren <newren@gmail.com>
2019-05-02 17:05:15 +00:00
|
|
|
|
cat $DATA/$INPUT | git filter-repo --stdin --quiet --force --replace-refs delete-no-add "${REST[@]}" &&
|
2018-11-05 14:30:50 +00:00
|
|
|
|
|
|
|
|
|
# Compare the resulting repo to expected value
|
|
|
|
|
git fast-export --use-done-feature --all >compare &&
|
|
|
|
|
test_cmp $DATA/$OUTPUT compare
|
|
|
|
|
'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filter_testcase basic basic-filename --path filename
|
|
|
|
|
filter_testcase basic basic-twenty --path twenty
|
|
|
|
|
filter_testcase basic basic-ten --path ten
|
2019-02-19 01:24:33 +00:00
|
|
|
|
filter_testcase basic basic-numbers --path ten --path twenty
|
|
|
|
|
filter_testcase basic basic-filename --invert-paths --path-glob 't*en*'
|
|
|
|
|
filter_testcase basic basic-numbers --invert-paths --path-regex 'f.*e.*e'
|
2019-01-29 01:26:49 +00:00
|
|
|
|
filter_testcase basic basic-mailmap --mailmap ../t9390/sample-mailmap
|
filter-repo: add text removal (or replacement) via file of expressions
Make it easy for users to search and replace text throughout the
repository history. Instead of inventing some new syntax, reuse the
same syntax used by BFG repo filter's --replace-text option, namely,
a file with one expression per line of the form
[regex:|glob:|literal:]$MATCH_EXPR[==>$REPLACEMENT_EXPR]
Where "$MATCH_EXPR" is by default considered to be literal text, but
could be a regex or a glob if the appropriate prefix is used. Also,
$REPLACEMENT_EXPR defaults to '***REMOVED***' if not specified. If
you want a literal '==>' to be part of your $MATCH_EXPR, then you
must also manually specify a replacement expression instead of taking
the default. Some examples:
sup3rs3kr3t
(replaces 'sup3rs3kr3t' with '***REMOVED***')
HeWhoShallNotBeNamed==>Voldemort
(replaces 'HeWhoShallNotBeNamed' with 'Voldemort')
very==>
(replaces 'very' with the empty string)
regex:(\d{2})/(\d{2})/(\d{4})==>\2/\1/\3
(replaces '05/17/2012' with '17/05/2012', and vice-versa)
The format for regex is as from
re.sub(<pattern>, <repl>, <string>) from
https://docs.python.org/2/library/re.html
The <string> comes from file contents of the repo, and you specify
the <pattern> and <repl>.
glob:Copy*t==>Cartel
(replaces 'Copyright' or 'Copyleft' or 'Copy my st' with 'Cartel')
Signed-off-by: Elijah Newren <newren@gmail.com>
2019-01-30 08:56:22 +00:00
|
|
|
|
filter_testcase basic basic-replace --replace-text ../t9390/sample-replace
|
2019-03-04 22:29:54 +00:00
|
|
|
|
filter_testcase empty empty-keepme --path keepme
|
2019-05-09 16:28:02 +00:00
|
|
|
|
filter_testcase empty more-empty-keepme --path keepme --empty-pruning=always \
|
|
|
|
|
--degenerate-pruning=always
|
|
|
|
|
filter_testcase empty less-empty-keepme --path keepme --empty-pruning=never \
|
|
|
|
|
--degenerate-pruning=never
|
2019-02-23 16:15:07 +00:00
|
|
|
|
filter_testcase degenerate degenerate-keepme --path moduleA/keepme
|
|
|
|
|
filter_testcase degenerate degenerate-moduleA --path moduleA
|
|
|
|
|
filter_testcase degenerate degenerate-globme --path-glob *me
|
2019-03-13 17:52:21 +00:00
|
|
|
|
filter_testcase unusual unusual-filtered --path ''
|
2018-11-05 14:30:50 +00:00
|
|
|
|
|
2019-03-07 18:49:39 +00:00
|
|
|
|
test_expect_success 'setup path_rename' '
|
|
|
|
|
test_create_repo path_rename &&
|
|
|
|
|
(
|
|
|
|
|
cd path_rename &&
|
|
|
|
|
mkdir sequences values &&
|
|
|
|
|
test_seq 1 10 >sequences/tiny &&
|
|
|
|
|
test_seq 100 110 >sequences/intermediate &&
|
|
|
|
|
test_seq 1000 1010 >sequences/large &&
|
|
|
|
|
test_seq 1000 1010 >values/large &&
|
|
|
|
|
test_seq 10000 10010 >values/huge &&
|
|
|
|
|
git add sequences values &&
|
|
|
|
|
git commit -m initial &&
|
|
|
|
|
|
|
|
|
|
git mv sequences/tiny sequences/small &&
|
|
|
|
|
cp sequences/intermediate sequences/medium &&
|
|
|
|
|
echo 10011 >values/huge &&
|
|
|
|
|
git add sequences values &&
|
|
|
|
|
git commit -m updates &&
|
|
|
|
|
|
|
|
|
|
git rm sequences/intermediate &&
|
|
|
|
|
echo 11 >sequences/small &&
|
|
|
|
|
git add sequences/small &&
|
|
|
|
|
git commit -m changes &&
|
|
|
|
|
|
|
|
|
|
echo 1011 >sequences/medium &&
|
|
|
|
|
git add sequences/medium &&
|
|
|
|
|
git commit -m final
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
|
|
|
|
test_expect_success '--path-rename sequences/tiny:sequences/small' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/path_rename path_rename_single &&
|
|
|
|
|
cd path_rename_single &&
|
|
|
|
|
git filter-repo --path-rename sequences/tiny:sequences/small &&
|
|
|
|
|
git log --format=%n --name-only | sort | uniq >filenames &&
|
|
|
|
|
test_line_count = 7 filenames &&
|
|
|
|
|
! grep sequences/tiny filenames &&
|
|
|
|
|
git rev-parse HEAD~3:sequences/small
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
|
|
|
|
test_expect_success '--path-rename sequences:numbers' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/path_rename path_rename_dir &&
|
|
|
|
|
cd path_rename_dir &&
|
|
|
|
|
git filter-repo --path-rename sequences:numbers &&
|
|
|
|
|
git log --format=%n --name-only | sort | uniq >filenames &&
|
|
|
|
|
test_line_count = 8 filenames &&
|
|
|
|
|
! grep sequences/ filenames &&
|
|
|
|
|
grep numbers/ filenames &&
|
|
|
|
|
grep values/ filenames
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
|
|
|
|
test_expect_success '--path-rename-prefix values:numbers' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/path_rename path_rename_dir_2 &&
|
|
|
|
|
cd path_rename_dir_2 &&
|
|
|
|
|
git filter-repo --path-rename values/:numbers/ &&
|
|
|
|
|
git log --format=%n --name-only | sort | uniq >filenames &&
|
|
|
|
|
test_line_count = 8 filenames &&
|
|
|
|
|
! grep values/ filenames &&
|
|
|
|
|
grep sequences/ filenames &&
|
|
|
|
|
grep numbers/ filenames
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
|
|
|
|
test_expect_success '--path-rename squashing' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/path_rename path_rename_squash &&
|
|
|
|
|
cd path_rename_squash &&
|
|
|
|
|
git filter-repo \
|
|
|
|
|
--path-rename sequences/tiny:sequences/small \
|
|
|
|
|
--path-rename sequences:numbers \
|
|
|
|
|
--path-rename values:numbers \
|
|
|
|
|
--path-rename numbers/intermediate:numbers/medium &&
|
|
|
|
|
git log --format=%n --name-only | sort | uniq >filenames &&
|
|
|
|
|
# Just small, medium, large, huge, and a blank line...
|
|
|
|
|
test_line_count = 5 filenames &&
|
|
|
|
|
! grep sequences/ filenames &&
|
|
|
|
|
! grep values/ filenames &&
|
|
|
|
|
grep numbers/ filenames
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
|
|
|
|
test_expect_success '--path-rename inability to squash' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/path_rename path_rename_bad_squash &&
|
|
|
|
|
cd path_rename_bad_squash &&
|
|
|
|
|
test_must_fail git filter-repo \
|
|
|
|
|
--path-rename values/large:values/big \
|
|
|
|
|
--path-rename values/huge:values/big 2>../err &&
|
|
|
|
|
test_i18ngrep "File renaming caused colliding pathnames" ../err
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
filter-repo: add --replace-refs option
This adds the ability to automatically add new replacement refs for each
rewritten commit (as well as delete or update replacement refs that
existed before the run). This will allow users to use either new or old
commit hashes to reference commits locally, though old commit hashes
will need to be unabbreviated. The only requirement for this to work,
is that the person who does the rewrite also needs to push the replace
refs up where other users can grab them, and users who want to use them
need to modify their fetch refspecs to grab the replace refs.
However, other tools external to git may not understand replace refs...
Tools like Gerrit and GitHub apparently do not yet natively understand
replace refs. Trying to view "commits" by the replacement ref will
yield various forms of "Not Found" in each tool. One has to instead try
to view it as a branch with an odd name (including "refs/replace/"), and
often branches are accessed via a different URL style than commits so it
becomes very non-obvious to users how to access the info associated with
an old commit hash.
* In Gerrit, instead of being able to search on the sha1sum or use a
pre-defined URL to search and auto-redirect to the appropriate code
review with
https://gerrit.SITE.COM/#/q/${OLD_SHA1SUM},n,z
one instead has to have a special plugin and go to a URL like
https://gerrit.SITE.COM/plugins/gitiles/ORG/REPO/+/refs/replace/${OLD_SHA1SUM}
but then the user isn't shown the actual code review and will need
to guess which link to click on to get to it (and it'll only be
there if the user included a Change-Id in the commit message).
* In GitHub, instead of being able to go to a URL like
https://github.SITE.COM/ORG/REPO/commit/${OLD_SHA1SUM}
one instead has to navigate based on branch using
https://github.SITE.COM/ORG/REPO/tree/refs/replace/${OLD_SHA1SUM}
but that will show a listing of commits instead of information about
a specific commit; the user has to manually click on the first commit
to get to the desired location.
For now, providing replace refs at least allows users to access
information locally using old IDs; perhaps in time as other external
tools will gain a better understanding of how to use replace refs, the
barrier to history rewrites will decrease enough that big projects that
really need it (e.g. those that have committed many sins by commiting
stupidly large useless binary blobs) can at least seriously contemplate
the undertaking. History rewrites will always have some drawbacks and
pain associated with them, as they should, but when warranted it's nice
to have transition plans that are more smooth than a massive flag day.
Signed-off-by: Elijah Newren <newren@gmail.com>
2019-05-02 17:05:15 +00:00
|
|
|
|
test_expect_success 'setup metasyntactic repo' '
|
2019-03-08 01:38:12 +00:00
|
|
|
|
test_create_repo metasyntactic &&
|
2019-03-08 00:14:35 +00:00
|
|
|
|
(
|
2019-03-08 01:38:12 +00:00
|
|
|
|
cd metasyntactic &&
|
|
|
|
|
weird_name=$(printf "file\tna\nme") &&
|
|
|
|
|
echo "funny" >"$weird_name" &&
|
2019-03-08 00:14:35 +00:00
|
|
|
|
mkdir numbers &&
|
|
|
|
|
test_seq 1 10 >numbers/small &&
|
|
|
|
|
test_seq 100 110 >numbers/medium &&
|
2019-03-08 01:38:12 +00:00
|
|
|
|
git add "$weird_name" numbers &&
|
2019-03-08 00:14:35 +00:00
|
|
|
|
git commit -m initial &&
|
|
|
|
|
git tag v1.0 &&
|
2019-03-08 01:38:12 +00:00
|
|
|
|
git tag -a -m v1.1 v1.1 &&
|
2019-03-08 00:14:35 +00:00
|
|
|
|
|
|
|
|
|
mkdir words &&
|
|
|
|
|
echo foo >words/important &&
|
|
|
|
|
echo bar >words/whimsical &&
|
|
|
|
|
echo baz >words/sequences &&
|
|
|
|
|
git add words &&
|
|
|
|
|
git commit -m some.words &&
|
|
|
|
|
git branch another_branch &&
|
|
|
|
|
git tag v2.0 &&
|
|
|
|
|
|
|
|
|
|
echo spam >words/to &&
|
2019-03-08 01:38:12 +00:00
|
|
|
|
echo eggs >words/know &&
|
2019-03-08 00:14:35 +00:00
|
|
|
|
git add words
|
2019-03-08 01:38:12 +00:00
|
|
|
|
git rm "$weird_name" &&
|
2019-03-08 00:14:35 +00:00
|
|
|
|
git commit -m more.words &&
|
|
|
|
|
git tag -a -m "Look, ma, I made a tag" v3.0
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
2019-03-08 01:38:12 +00:00
|
|
|
|
test_expect_success '--tag-rename' '
|
2019-03-08 00:14:35 +00:00
|
|
|
|
(
|
2019-03-08 01:38:12 +00:00
|
|
|
|
git clone file://"$(pwd)"/metasyntactic tag_rename &&
|
|
|
|
|
cd tag_rename &&
|
2019-03-08 00:14:35 +00:00
|
|
|
|
git filter-repo \
|
|
|
|
|
--tag-rename "":"myrepo-" \
|
|
|
|
|
--path words &&
|
|
|
|
|
test_must_fail git cat-file -t v1.0 &&
|
2019-03-08 01:38:12 +00:00
|
|
|
|
test_must_fail git cat-file -t v1.1 &&
|
2019-03-08 00:14:35 +00:00
|
|
|
|
test_must_fail git cat-file -t v2.0 &&
|
|
|
|
|
test_must_fail git cat-file -t v3.0 &&
|
|
|
|
|
test_must_fail git cat-file -t myrepo-v1.0 &&
|
2019-03-08 01:38:12 +00:00
|
|
|
|
test_must_fail git cat-file -t myrepo-v1.1 &&
|
2019-03-08 00:14:35 +00:00
|
|
|
|
test $(git cat-file -t myrepo-v2.0) = commit &&
|
|
|
|
|
test $(git cat-file -t myrepo-v3.0) = tag
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
2019-03-08 01:38:12 +00:00
|
|
|
|
test_expect_success '--subdirectory-filter' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/metasyntactic subdir_filter &&
|
|
|
|
|
cd subdir_filter &&
|
|
|
|
|
git filter-repo \
|
|
|
|
|
--subdirectory-filter words &&
|
|
|
|
|
git cat-file --batch-check --batch-all-objects >all-objs &&
|
|
|
|
|
test_line_count = 10 all-objs &&
|
|
|
|
|
git log --format=%n --name-only | sort | uniq >filenames &&
|
|
|
|
|
test_line_count = 6 filenames &&
|
|
|
|
|
grep ^important$ filenames &&
|
|
|
|
|
test_must_fail git cat-file -t v1.0 &&
|
|
|
|
|
test_must_fail git cat-file -t v1.1 &&
|
|
|
|
|
test $(git cat-file -t v2.0) = commit &&
|
|
|
|
|
test $(git cat-file -t v3.0) = tag
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
|
|
|
|
test_expect_success '--to-subdirectory-filter' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/metasyntactic to_subdir_filter &&
|
|
|
|
|
cd to_subdir_filter &&
|
|
|
|
|
git filter-repo \
|
|
|
|
|
--to-subdirectory-filter mysubdir &&
|
|
|
|
|
git cat-file --batch-check --batch-all-objects >all-objs &&
|
|
|
|
|
test_line_count = 22 all-objs &&
|
|
|
|
|
git log --format=%n --name-only | sort | uniq >filenames &&
|
|
|
|
|
test_line_count = 9 filenames &&
|
|
|
|
|
grep "^\"mysubdir/file\\\\tna\\\\nme\"$" filenames &&
|
|
|
|
|
grep ^mysubdir/words/important$ filenames &&
|
|
|
|
|
test $(git cat-file -t v1.0) = commit &&
|
|
|
|
|
test $(git cat-file -t v1.1) = tag &&
|
|
|
|
|
test $(git cat-file -t v2.0) = commit &&
|
|
|
|
|
test $(git cat-file -t v3.0) = tag
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
2019-03-09 22:24:53 +00:00
|
|
|
|
test_expect_success 'refs/replace/ to skip a parent' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/metasyntactic replace_skip_ref &&
|
|
|
|
|
cd replace_skip_ref &&
|
|
|
|
|
|
|
|
|
|
git tag -d v2.0 &&
|
|
|
|
|
git replace HEAD~1 HEAD~2 &&
|
|
|
|
|
|
filter-repo: add --replace-refs option
This adds the ability to automatically add new replacement refs for each
rewritten commit (as well as delete or update replacement refs that
existed before the run). This will allow users to use either new or old
commit hashes to reference commits locally, though old commit hashes
will need to be unabbreviated. The only requirement for this to work,
is that the person who does the rewrite also needs to push the replace
refs up where other users can grab them, and users who want to use them
need to modify their fetch refspecs to grab the replace refs.
However, other tools external to git may not understand replace refs...
Tools like Gerrit and GitHub apparently do not yet natively understand
replace refs. Trying to view "commits" by the replacement ref will
yield various forms of "Not Found" in each tool. One has to instead try
to view it as a branch with an odd name (including "refs/replace/"), and
often branches are accessed via a different URL style than commits so it
becomes very non-obvious to users how to access the info associated with
an old commit hash.
* In Gerrit, instead of being able to search on the sha1sum or use a
pre-defined URL to search and auto-redirect to the appropriate code
review with
https://gerrit.SITE.COM/#/q/${OLD_SHA1SUM},n,z
one instead has to have a special plugin and go to a URL like
https://gerrit.SITE.COM/plugins/gitiles/ORG/REPO/+/refs/replace/${OLD_SHA1SUM}
but then the user isn't shown the actual code review and will need
to guess which link to click on to get to it (and it'll only be
there if the user included a Change-Id in the commit message).
* In GitHub, instead of being able to go to a URL like
https://github.SITE.COM/ORG/REPO/commit/${OLD_SHA1SUM}
one instead has to navigate based on branch using
https://github.SITE.COM/ORG/REPO/tree/refs/replace/${OLD_SHA1SUM}
but that will show a listing of commits instead of information about
a specific commit; the user has to manually click on the first commit
to get to the desired location.
For now, providing replace refs at least allows users to access
information locally using old IDs; perhaps in time as other external
tools will gain a better understanding of how to use replace refs, the
barrier to history rewrites will decrease enough that big projects that
really need it (e.g. those that have committed many sins by commiting
stupidly large useless binary blobs) can at least seriously contemplate
the undertaking. History rewrites will always have some drawbacks and
pain associated with them, as they should, but when warranted it's nice
to have transition plans that are more smooth than a massive flag day.
Signed-off-by: Elijah Newren <newren@gmail.com>
2019-05-02 17:05:15 +00:00
|
|
|
|
git filter-repo --replace-refs delete-no-add --path "" --force &&
|
2019-03-09 22:24:53 +00:00
|
|
|
|
test $(git rev-list --count HEAD) = 2 &&
|
|
|
|
|
git cat-file --batch-check --batch-all-objects >all-objs &&
|
|
|
|
|
test_line_count = 16 all-objs &&
|
|
|
|
|
git log --format=%n --name-only | sort | uniq >filenames &&
|
|
|
|
|
test_line_count = 9 filenames &&
|
|
|
|
|
test $(git cat-file -t v1.0) = commit &&
|
|
|
|
|
test $(git cat-file -t v1.1) = tag &&
|
|
|
|
|
test_must_fail git cat-file -t v2.0 &&
|
|
|
|
|
test $(git cat-file -t v3.0) = tag
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
|
|
|
|
test_expect_success 'refs/replace/ to add more initial history' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/metasyntactic replace_add_refs &&
|
|
|
|
|
cd replace_add_refs &&
|
|
|
|
|
|
|
|
|
|
git checkout --orphan new_root &&
|
|
|
|
|
rm .git/index &&
|
|
|
|
|
git add numbers/small &&
|
|
|
|
|
git clean -fd &&
|
|
|
|
|
git commit -m new.root &&
|
|
|
|
|
|
|
|
|
|
git replace --graft master~2 new_root &&
|
|
|
|
|
git checkout master &&
|
|
|
|
|
|
|
|
|
|
git --no-replace-objects cat-file -p master~2 >grandparent &&
|
|
|
|
|
! grep parent grandparent &&
|
|
|
|
|
|
filter-repo: add --replace-refs option
This adds the ability to automatically add new replacement refs for each
rewritten commit (as well as delete or update replacement refs that
existed before the run). This will allow users to use either new or old
commit hashes to reference commits locally, though old commit hashes
will need to be unabbreviated. The only requirement for this to work,
is that the person who does the rewrite also needs to push the replace
refs up where other users can grab them, and users who want to use them
need to modify their fetch refspecs to grab the replace refs.
However, other tools external to git may not understand replace refs...
Tools like Gerrit and GitHub apparently do not yet natively understand
replace refs. Trying to view "commits" by the replacement ref will
yield various forms of "Not Found" in each tool. One has to instead try
to view it as a branch with an odd name (including "refs/replace/"), and
often branches are accessed via a different URL style than commits so it
becomes very non-obvious to users how to access the info associated with
an old commit hash.
* In Gerrit, instead of being able to search on the sha1sum or use a
pre-defined URL to search and auto-redirect to the appropriate code
review with
https://gerrit.SITE.COM/#/q/${OLD_SHA1SUM},n,z
one instead has to have a special plugin and go to a URL like
https://gerrit.SITE.COM/plugins/gitiles/ORG/REPO/+/refs/replace/${OLD_SHA1SUM}
but then the user isn't shown the actual code review and will need
to guess which link to click on to get to it (and it'll only be
there if the user included a Change-Id in the commit message).
* In GitHub, instead of being able to go to a URL like
https://github.SITE.COM/ORG/REPO/commit/${OLD_SHA1SUM}
one instead has to navigate based on branch using
https://github.SITE.COM/ORG/REPO/tree/refs/replace/${OLD_SHA1SUM}
but that will show a listing of commits instead of information about
a specific commit; the user has to manually click on the first commit
to get to the desired location.
For now, providing replace refs at least allows users to access
information locally using old IDs; perhaps in time as other external
tools will gain a better understanding of how to use replace refs, the
barrier to history rewrites will decrease enough that big projects that
really need it (e.g. those that have committed many sins by commiting
stupidly large useless binary blobs) can at least seriously contemplate
the undertaking. History rewrites will always have some drawbacks and
pain associated with them, as they should, but when warranted it's nice
to have transition plans that are more smooth than a massive flag day.
Signed-off-by: Elijah Newren <newren@gmail.com>
2019-05-02 17:05:15 +00:00
|
|
|
|
git filter-repo --replace-refs delete-no-add --path "" --force &&
|
2019-03-09 22:24:53 +00:00
|
|
|
|
|
|
|
|
|
git --no-replace-objects cat-file -p master~2 >new-grandparent &&
|
|
|
|
|
grep parent new-grandparent &&
|
|
|
|
|
|
|
|
|
|
test $(git rev-list --count HEAD) = 4 &&
|
|
|
|
|
git cat-file --batch-check --batch-all-objects >all-objs &&
|
|
|
|
|
test_line_count = 22 all-objs &&
|
|
|
|
|
git log --format=%n --name-only | sort | uniq >filenames &&
|
|
|
|
|
test_line_count = 9 filenames &&
|
|
|
|
|
test $(git cat-file -t v1.0) = commit &&
|
|
|
|
|
test $(git cat-file -t v1.1) = tag &&
|
|
|
|
|
test $(git cat-file -t v2.0) = commit &&
|
|
|
|
|
test $(git cat-file -t v3.0) = tag
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
filter-repo: add --replace-refs option
This adds the ability to automatically add new replacement refs for each
rewritten commit (as well as delete or update replacement refs that
existed before the run). This will allow users to use either new or old
commit hashes to reference commits locally, though old commit hashes
will need to be unabbreviated. The only requirement for this to work,
is that the person who does the rewrite also needs to push the replace
refs up where other users can grab them, and users who want to use them
need to modify their fetch refspecs to grab the replace refs.
However, other tools external to git may not understand replace refs...
Tools like Gerrit and GitHub apparently do not yet natively understand
replace refs. Trying to view "commits" by the replacement ref will
yield various forms of "Not Found" in each tool. One has to instead try
to view it as a branch with an odd name (including "refs/replace/"), and
often branches are accessed via a different URL style than commits so it
becomes very non-obvious to users how to access the info associated with
an old commit hash.
* In Gerrit, instead of being able to search on the sha1sum or use a
pre-defined URL to search and auto-redirect to the appropriate code
review with
https://gerrit.SITE.COM/#/q/${OLD_SHA1SUM},n,z
one instead has to have a special plugin and go to a URL like
https://gerrit.SITE.COM/plugins/gitiles/ORG/REPO/+/refs/replace/${OLD_SHA1SUM}
but then the user isn't shown the actual code review and will need
to guess which link to click on to get to it (and it'll only be
there if the user included a Change-Id in the commit message).
* In GitHub, instead of being able to go to a URL like
https://github.SITE.COM/ORG/REPO/commit/${OLD_SHA1SUM}
one instead has to navigate based on branch using
https://github.SITE.COM/ORG/REPO/tree/refs/replace/${OLD_SHA1SUM}
but that will show a listing of commits instead of information about
a specific commit; the user has to manually click on the first commit
to get to the desired location.
For now, providing replace refs at least allows users to access
information locally using old IDs; perhaps in time as other external
tools will gain a better understanding of how to use replace refs, the
barrier to history rewrites will decrease enough that big projects that
really need it (e.g. those that have committed many sins by commiting
stupidly large useless binary blobs) can at least seriously contemplate
the undertaking. History rewrites will always have some drawbacks and
pain associated with them, as they should, but when warranted it's nice
to have transition plans that are more smooth than a massive flag day.
Signed-off-by: Elijah Newren <newren@gmail.com>
2019-05-02 17:05:15 +00:00
|
|
|
|
test_expect_success 'creation/deletion/updating of replace refs' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/metasyntactic replace_handling &&
|
|
|
|
|
|
|
|
|
|
# Same setup as "refs/replace/ to skip a parent", so we
|
|
|
|
|
# do not have to check that replacement refs were used
|
|
|
|
|
# correctly in the rewrite, just that replacement refs were
|
|
|
|
|
# deleted, added, or updated correctly.
|
|
|
|
|
cd replace_handling &&
|
|
|
|
|
git tag -d v2.0 &&
|
|
|
|
|
master=$(git rev-parse master) &&
|
|
|
|
|
master_1=$(git rev-parse master~1) &&
|
|
|
|
|
master_2=$(git rev-parse master~2) &&
|
|
|
|
|
git replace HEAD~1 HEAD~2 &&
|
|
|
|
|
cd .. &&
|
|
|
|
|
|
|
|
|
|
mkdir -p test_replace_refs &&
|
|
|
|
|
cd test_replace_refs &&
|
|
|
|
|
|
|
|
|
|
rsync -a --delete ../replace_handling/ ./ &&
|
|
|
|
|
git filter-repo --replace-refs delete-no-add --path-rename numbers:counting &&
|
|
|
|
|
git show-ref >output &&
|
|
|
|
|
! grep refs/replace/ output &&
|
|
|
|
|
|
|
|
|
|
rsync -a --delete ../replace_handling/ ./ &&
|
|
|
|
|
git filter-repo --replace-refs delete-and-add --path-rename numbers:counting &&
|
|
|
|
|
echo "$(git rev-parse master) refs/replace/$master" >out &&
|
|
|
|
|
echo "$(git rev-parse master~1) refs/replace/$master_1" >>out &&
|
|
|
|
|
echo "$(git rev-parse master~1) refs/replace/$master_2" >>out &&
|
|
|
|
|
sort -k 2 out >expect &&
|
|
|
|
|
git show-ref | grep refs/replace/ >output &&
|
|
|
|
|
test_cmp output expect &&
|
|
|
|
|
|
|
|
|
|
rsync -a --delete ../replace_handling/ ./ &&
|
|
|
|
|
git filter-repo --replace-refs update-no-add --path-rename numbers:counting &&
|
|
|
|
|
echo "$(git rev-parse master~1) refs/replace/$master_1" >expect &&
|
|
|
|
|
git show-ref | grep refs/replace/ >output &&
|
|
|
|
|
test_cmp output expect &&
|
|
|
|
|
|
|
|
|
|
rsync -a --delete ../replace_handling/ ./ &&
|
|
|
|
|
git filter-repo --replace-refs update-or-add --path-rename numbers:counting &&
|
|
|
|
|
echo "$(git rev-parse master) refs/replace/$master" >>out &&
|
|
|
|
|
echo "$(git rev-parse master~1) refs/replace/$master_1" >>out &&
|
|
|
|
|
sort -k 2 out >expect &&
|
|
|
|
|
git show-ref | grep refs/replace/ >output &&
|
|
|
|
|
test_cmp output expect &&
|
|
|
|
|
|
|
|
|
|
rsync -a --delete ../replace_handling/ ./ &&
|
|
|
|
|
git filter-repo --replace-refs update-and-add --path-rename numbers:counting &&
|
|
|
|
|
echo "$(git rev-parse master) refs/replace/$master" >>out &&
|
|
|
|
|
echo "$(git rev-parse master~1) refs/replace/$master_1" >>out &&
|
|
|
|
|
echo "$(git rev-parse master~1) refs/replace/$master_2" >>out &&
|
|
|
|
|
sort -k 2 out >expect &&
|
|
|
|
|
git show-ref | grep refs/replace/ >output &&
|
|
|
|
|
test_cmp output expect
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
2019-03-13 14:39:33 +00:00
|
|
|
|
test_expect_success '--debug' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/metasyntactic debug &&
|
|
|
|
|
cd debug &&
|
|
|
|
|
|
|
|
|
|
git filter-repo --path words --debug &&
|
|
|
|
|
|
|
|
|
|
test $(git rev-list --count HEAD) = 2 &&
|
|
|
|
|
git cat-file --batch-check --batch-all-objects >all-objs &&
|
|
|
|
|
test_line_count = 12 all-objs &&
|
|
|
|
|
git log --format=%n --name-only | sort | uniq >filenames &&
|
|
|
|
|
test_line_count = 6 filenames &&
|
|
|
|
|
|
|
|
|
|
test_path_is_file .git/filter-repo/fast-export.original &&
|
|
|
|
|
grep "^commit " .git/filter-repo/fast-export.original >out &&
|
|
|
|
|
test_line_count = 3 out &&
|
|
|
|
|
test_path_is_file .git/filter-repo/fast-export.filtered &&
|
|
|
|
|
grep "^commit " .git/filter-repo/fast-export.filtered >out &&
|
|
|
|
|
test_line_count = 2 out
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
|
|
|
|
test_expect_success '--dry-run' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/metasyntactic dry_run &&
|
|
|
|
|
cd dry_run &&
|
|
|
|
|
|
|
|
|
|
git filter-repo --path words --dry-run &&
|
|
|
|
|
|
|
|
|
|
git show-ref | grep master >out &&
|
|
|
|
|
test_line_count = 2 out &&
|
|
|
|
|
awk "{print \$1}" out | uniq >out2 &&
|
|
|
|
|
test_line_count = 1 out2 &&
|
|
|
|
|
|
|
|
|
|
test $(git rev-list --count HEAD) = 3 &&
|
|
|
|
|
git cat-file --batch-check --batch-all-objects >all-objs &&
|
|
|
|
|
test_line_count = 19 all-objs &&
|
|
|
|
|
git log --format=%n --name-only | sort | uniq >filenames &&
|
|
|
|
|
test_line_count = 9 filenames &&
|
|
|
|
|
|
|
|
|
|
test_path_is_file .git/filter-repo/fast-export.original &&
|
|
|
|
|
grep "^commit " .git/filter-repo/fast-export.original >out &&
|
|
|
|
|
test_line_count = 3 out &&
|
|
|
|
|
test_path_is_file .git/filter-repo/fast-export.filtered &&
|
|
|
|
|
grep "^commit " .git/filter-repo/fast-export.filtered >out &&
|
|
|
|
|
test_line_count = 2 out
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
|
|
|
|
test_expect_success '--dry-run --stdin' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/metasyntactic dry_run_stdin &&
|
|
|
|
|
cd dry_run_stdin &&
|
|
|
|
|
|
|
|
|
|
git fast-export --all | git filter-repo --path words --dry-run --stdin &&
|
|
|
|
|
|
|
|
|
|
git show-ref | grep master >out &&
|
|
|
|
|
test_line_count = 2 out &&
|
|
|
|
|
awk "{print \$1}" out | uniq >out2 &&
|
|
|
|
|
test_line_count = 1 out2 &&
|
|
|
|
|
|
|
|
|
|
test $(git rev-list --count HEAD) = 3 &&
|
|
|
|
|
git cat-file --batch-check --batch-all-objects >all-objs &&
|
|
|
|
|
test_line_count = 19 all-objs &&
|
|
|
|
|
git log --format=%n --name-only | sort | uniq >filenames &&
|
|
|
|
|
test_line_count = 9 filenames &&
|
|
|
|
|
|
|
|
|
|
test_path_is_missing .git/filter-repo/fast-export.original &&
|
|
|
|
|
test_path_is_file .git/filter-repo/fast-export.filtered &&
|
|
|
|
|
grep "^commit " .git/filter-repo/fast-export.filtered >out &&
|
|
|
|
|
test_line_count = 2 out
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
2019-03-11 20:31:22 +00:00
|
|
|
|
test_expect_success 'setup analyze_me' '
|
|
|
|
|
test_create_repo analyze_me &&
|
|
|
|
|
(
|
|
|
|
|
cd analyze_me &&
|
|
|
|
|
mkdir numbers words &&
|
|
|
|
|
test_seq 1 10 >numbers/small.num &&
|
|
|
|
|
test_seq 100 110 >numbers/medium.num &&
|
|
|
|
|
echo spam >words/to &&
|
|
|
|
|
echo eggs >words/know &&
|
|
|
|
|
echo rename a lot >fickle &&
|
|
|
|
|
git add numbers words fickle &&
|
|
|
|
|
test_tick &&
|
|
|
|
|
git commit -m initial &&
|
|
|
|
|
|
|
|
|
|
git branch other &&
|
|
|
|
|
git mv fickle capricious &&
|
|
|
|
|
test_tick &&
|
|
|
|
|
git commit -m "rename on main branch" &&
|
|
|
|
|
|
|
|
|
|
git checkout other &&
|
|
|
|
|
echo random other change >whatever &&
|
|
|
|
|
git add whatever &&
|
|
|
|
|
git mv fickle capricious &&
|
|
|
|
|
test_tick &&
|
|
|
|
|
git commit -m "rename on other branch" &&
|
|
|
|
|
|
|
|
|
|
git checkout master &&
|
|
|
|
|
git merge --no-commit other &&
|
|
|
|
|
git mv capricious mercurial &&
|
|
|
|
|
test_tick &&
|
|
|
|
|
git commit &&
|
|
|
|
|
|
|
|
|
|
git mv words sequence &&
|
|
|
|
|
test_tick &&
|
|
|
|
|
git commit -m now.sequence &&
|
|
|
|
|
|
|
|
|
|
git rm -rf numbers &&
|
|
|
|
|
test_tick &&
|
|
|
|
|
git commit -m remove.words &&
|
|
|
|
|
|
|
|
|
|
mkdir words &&
|
|
|
|
|
echo no >words/know &&
|
|
|
|
|
git add words/know &&
|
|
|
|
|
test_tick &&
|
|
|
|
|
git commit -m "Recreated file previously renamed" &&
|
|
|
|
|
|
|
|
|
|
echo "160000 deadbeefdeadbeefdeadbeefdeadbeefdeadbeefQfake_submodule" | q_to_tab | git update-index --index-info &&
|
|
|
|
|
test_tick &&
|
|
|
|
|
git commit -m "Add a fake submodule" &&
|
|
|
|
|
|
|
|
|
|
test_tick &&
|
|
|
|
|
git commit --allow-empty -m "Final commit, empty" &&
|
|
|
|
|
|
|
|
|
|
# Add a random extra unreferenced object
|
|
|
|
|
echo foobar | git hash-object --stdin -w
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
2019-03-17 06:40:01 +00:00
|
|
|
|
test_expect_success C_LOCALE_OUTPUT '--analyze' '
|
2019-03-11 20:31:22 +00:00
|
|
|
|
(
|
|
|
|
|
cd analyze_me &&
|
|
|
|
|
|
|
|
|
|
git filter-repo --analyze &&
|
|
|
|
|
|
|
|
|
|
# It should work and overwrite report if run again
|
|
|
|
|
git filter-repo --analyze &&
|
|
|
|
|
|
|
|
|
|
test -d .git/filter-repo/analysis &&
|
|
|
|
|
cd .git/filter-repo/analysis &&
|
|
|
|
|
|
|
|
|
|
cat >expect <<-EOF &&
|
|
|
|
|
fickle ->
|
|
|
|
|
capricious
|
|
|
|
|
mercurial
|
|
|
|
|
words/to ->
|
|
|
|
|
sequence/to
|
|
|
|
|
EOF
|
|
|
|
|
test_cmp expect renames.txt &&
|
|
|
|
|
|
|
|
|
|
cat >expect <<-EOF &&
|
2019-03-17 06:39:39 +00:00
|
|
|
|
== Overall Statistics ==
|
|
|
|
|
Number of commits: 9
|
|
|
|
|
Number of filenames: 10
|
|
|
|
|
Number of directories: 4
|
2019-03-11 20:31:22 +00:00
|
|
|
|
Number of file extensions: 2
|
|
|
|
|
|
2019-03-17 06:39:39 +00:00
|
|
|
|
Total unpacked size (bytes): 147
|
|
|
|
|
Total packed size (bytes): 306
|
2019-03-11 20:31:22 +00:00
|
|
|
|
|
|
|
|
|
EOF
|
|
|
|
|
head -n 9 README >actual &&
|
|
|
|
|
test_cmp expect actual &&
|
|
|
|
|
|
2019-04-27 22:18:59 +00:00
|
|
|
|
cat >expect <<-\EOF &&
|
2019-03-17 06:39:39 +00:00
|
|
|
|
=== Files by sha and associated pathnames in reverse size ===
|
2019-03-11 20:31:22 +00:00
|
|
|
|
Format: sha, unpacked size, packed size, filename(s) object stored as
|
|
|
|
|
a89c82a2d4b713a125a4323d25adda062cc0013d 44 48 numbers/medium.num
|
|
|
|
|
f00c965d8307308469e537302baa73048488f162 21 37 numbers/small.num
|
|
|
|
|
2aa69a2a708eed00cb390e30f6bcc3eed773f390 20 36 whatever
|
2019-04-27 22:18:59 +00:00
|
|
|
|
51b95456de9274c9a95f756742808dfd480b9b35 13 29 [capricious, fickle, mercurial]
|
|
|
|
|
732c85a1b3d7ce40ec8f78fd9ffea32e9f45fae0 5 20 [sequence/know, words/know]
|
|
|
|
|
34b6a0c9d02cb6ef7f409f248c0c1224ce9dd373 5 20 [sequence/to, words/to]
|
2019-03-11 20:31:22 +00:00
|
|
|
|
7ecb56eb3fa3fa6f19dd48bca9f971950b119ede 3 18 words/know
|
|
|
|
|
EOF
|
|
|
|
|
test_cmp expect blob-shas-and-paths.txt &&
|
|
|
|
|
|
|
|
|
|
cat >expect <<-EOF &&
|
|
|
|
|
=== All directories by reverse size ===
|
|
|
|
|
Format: unpacked size, packed size, date deleted, directory name
|
|
|
|
|
147 306 <present> <toplevel>
|
|
|
|
|
65 85 2005-04-07 numbers
|
|
|
|
|
13 58 <present> words
|
|
|
|
|
10 40 <present> sequence
|
|
|
|
|
EOF
|
|
|
|
|
test_cmp expect directories-all-sizes.txt &&
|
|
|
|
|
|
|
|
|
|
cat >expect <<-EOF &&
|
|
|
|
|
=== Deleted directories by reverse size ===
|
|
|
|
|
Format: unpacked size, packed size, date deleted, directory name
|
|
|
|
|
65 85 2005-04-07 numbers
|
|
|
|
|
EOF
|
|
|
|
|
test_cmp expect directories-deleted-sizes.txt &&
|
|
|
|
|
|
|
|
|
|
cat >expect <<-EOF &&
|
|
|
|
|
=== All extensions by reverse size ===
|
|
|
|
|
Format: unpacked size, packed size, date deleted, extension name
|
|
|
|
|
82 221 <present> <no extension>
|
|
|
|
|
65 85 2005-04-07 .num
|
|
|
|
|
EOF
|
|
|
|
|
test_cmp expect extensions-all-sizes.txt &&
|
|
|
|
|
|
|
|
|
|
cat >expect <<-EOF &&
|
|
|
|
|
=== Deleted extensions by reverse size ===
|
|
|
|
|
Format: unpacked size, packed size, date deleted, extension name
|
|
|
|
|
65 85 2005-04-07 .num
|
|
|
|
|
EOF
|
|
|
|
|
test_cmp expect extensions-deleted-sizes.txt &&
|
|
|
|
|
|
|
|
|
|
cat >expect <<-EOF &&
|
|
|
|
|
=== All paths by reverse accumulated size ===
|
|
|
|
|
Format: unpacked size, packed size, date deleted, pathectory name
|
|
|
|
|
44 48 2005-04-07 numbers/medium.num
|
|
|
|
|
8 38 <present> words/know
|
|
|
|
|
21 37 2005-04-07 numbers/small.num
|
|
|
|
|
20 36 <present> whatever
|
|
|
|
|
13 29 <present> mercurial
|
2019-04-01 17:58:16 +00:00
|
|
|
|
13 29 <present> fickle
|
2019-03-11 20:31:22 +00:00
|
|
|
|
13 29 <present> capricious
|
|
|
|
|
5 20 <present> words/to
|
|
|
|
|
5 20 <present> sequence/to
|
2019-04-01 17:58:16 +00:00
|
|
|
|
5 20 <present> sequence/know
|
2019-03-11 20:31:22 +00:00
|
|
|
|
EOF
|
|
|
|
|
test_cmp expect path-all-sizes.txt &&
|
|
|
|
|
|
|
|
|
|
cat >expect <<-EOF &&
|
|
|
|
|
=== Deleted paths by reverse accumulated size ===
|
|
|
|
|
Format: unpacked size, packed size, date deleted, path name(s)
|
|
|
|
|
44 48 2005-04-07 numbers/medium.num
|
|
|
|
|
21 37 2005-04-07 numbers/small.num
|
|
|
|
|
EOF
|
|
|
|
|
test_cmp expect path-deleted-sizes.txt
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
2019-03-12 20:25:25 +00:00
|
|
|
|
test_expect_success '--replace-text all options' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/analyze_me replace_text &&
|
|
|
|
|
cd replace_text &&
|
|
|
|
|
|
|
|
|
|
cat >../replace-rules <<-\EOF &&
|
|
|
|
|
other
|
|
|
|
|
change==>variation
|
|
|
|
|
|
|
|
|
|
literal:spam==>foodstuff
|
|
|
|
|
glob:ran*m==>haphazard
|
|
|
|
|
regex:1(.[0-9])==>2\1
|
|
|
|
|
EOF
|
|
|
|
|
git filter-repo --replace-text ../replace-rules &&
|
|
|
|
|
|
|
|
|
|
test_seq 200 210 >expect &&
|
|
|
|
|
git show HEAD~4:numbers/medium.num >actual &&
|
|
|
|
|
test_cmp expect actual &&
|
|
|
|
|
|
|
|
|
|
echo "haphazard ***REMOVED*** variation" >expect &&
|
|
|
|
|
test_cmp expect whatever
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
2019-03-12 01:08:41 +00:00
|
|
|
|
test_expect_success 'setup commit message rewriting' '
|
|
|
|
|
test_create_repo commit_msg &&
|
|
|
|
|
(
|
|
|
|
|
cd commit_msg &&
|
|
|
|
|
echo two guys walking into a >bar &&
|
|
|
|
|
git add bar &&
|
|
|
|
|
git commit -m initial &&
|
|
|
|
|
|
|
|
|
|
test_commit another &&
|
|
|
|
|
|
|
|
|
|
name=$(git rev-parse HEAD) &&
|
|
|
|
|
echo hello >world &&
|
|
|
|
|
git add world &&
|
|
|
|
|
git commit -m "Commit referencing ${name:0:8}" &&
|
|
|
|
|
|
|
|
|
|
git revert HEAD &&
|
|
|
|
|
|
|
|
|
|
for i in $(test_seq 1 200)
|
|
|
|
|
do
|
|
|
|
|
git commit --allow-empty -m "another commit"
|
|
|
|
|
done &&
|
|
|
|
|
|
|
|
|
|
echo foo >bar &&
|
|
|
|
|
git add bar &&
|
|
|
|
|
git commit -m bar &&
|
|
|
|
|
|
|
|
|
|
git revert --no-commit HEAD &&
|
|
|
|
|
echo foo >baz &&
|
|
|
|
|
git add baz &&
|
|
|
|
|
git commit
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
|
|
|
|
test_expect_success 'commit message rewrite' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/commit_msg commit_msg_clone &&
|
|
|
|
|
cd commit_msg_clone &&
|
|
|
|
|
|
|
|
|
|
git filter-repo --invert-paths --path bar &&
|
|
|
|
|
|
|
|
|
|
git log --oneline >changes &&
|
|
|
|
|
test_line_count = 204 changes &&
|
|
|
|
|
|
|
|
|
|
name=$(git rev-parse HEAD~203) &&
|
|
|
|
|
echo "Commit referencing ${name:0:8}" >expect &&
|
|
|
|
|
git log --no-walk --format=%s HEAD~202 >actual &&
|
|
|
|
|
test_cmp expect actual &&
|
|
|
|
|
|
|
|
|
|
latest=$(git log --no-walk | grep reverts | awk "{print \$4}" | tr -d '.') &&
|
|
|
|
|
test -n "$latest" &&
|
|
|
|
|
test_must_fail git cat-file -e "$latest"
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
|
|
|
|
test_expect_success 'commit message rewrite unsuccessful' '
|
|
|
|
|
(
|
|
|
|
|
git init commit_msg_not_found &&
|
|
|
|
|
cd commit_msg_not_found &&
|
|
|
|
|
|
|
|
|
|
cat >input <<-\EOF &&
|
|
|
|
|
feature done
|
|
|
|
|
commit refs/heads/develop
|
|
|
|
|
mark :1
|
|
|
|
|
original-oid deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
|
|
|
|
|
author Just Me <just@here.org> 1234567890 -0200
|
|
|
|
|
committer Just Me <just@here.org> 1234567890 -0200
|
|
|
|
|
data 2
|
|
|
|
|
A
|
|
|
|
|
|
|
|
|
|
commit refs/heads/develop
|
|
|
|
|
mark :2
|
|
|
|
|
original-oid deadbeefcafedeadbeefcafedeadbeefcafecafe
|
|
|
|
|
author Just Me <just@here.org> 1234567890 -0200
|
|
|
|
|
committer Just Me <just@here.org> 1234567890 -0200
|
|
|
|
|
data 2
|
|
|
|
|
B
|
|
|
|
|
|
|
|
|
|
commit refs/heads/develop
|
|
|
|
|
mark :3
|
|
|
|
|
original-oid 0000000000000000000000000000000000000004
|
|
|
|
|
author Just Me <just@here.org> 3980014290 -0200
|
|
|
|
|
committer Just Me <just@here.org> 3980014290 -0200
|
|
|
|
|
data 93
|
|
|
|
|
Four score and seven years ago, commit deadbeef ("B",
|
|
|
|
|
2009-02-13) messed up. This fixes it.
|
|
|
|
|
done
|
|
|
|
|
EOF
|
|
|
|
|
|
|
|
|
|
cat input | git filter-repo --stdin --path salutation --force &&
|
|
|
|
|
|
|
|
|
|
git log --oneline develop >changes &&
|
|
|
|
|
test_line_count = 3 changes &&
|
|
|
|
|
|
|
|
|
|
git log develop >out &&
|
|
|
|
|
grep deadbeef out
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
2019-03-14 00:05:58 +00:00
|
|
|
|
test_expect_success 'startup sanity checks' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/analyze_me startup_sanity_checks &&
|
|
|
|
|
cd startup_sanity_checks &&
|
|
|
|
|
|
|
|
|
|
echo foobar | git hash-object -w --stdin &&
|
|
|
|
|
test_must_fail git filter-repo --path numbers 2>err &&
|
|
|
|
|
test_i18ngrep "expected freshly packed repo" err &&
|
|
|
|
|
git prune &&
|
|
|
|
|
|
|
|
|
|
git remote add another_remote /dev/null &&
|
|
|
|
|
test_must_fail git filter-repo --path numbers 2>err &&
|
|
|
|
|
test_i18ngrep "expected one remote, origin" err &&
|
|
|
|
|
git remote rm another_remote &&
|
|
|
|
|
|
|
|
|
|
git remote rename origin another_remote &&
|
|
|
|
|
test_must_fail git filter-repo --path numbers 2>err &&
|
|
|
|
|
test_i18ngrep "expected one remote, origin" err &&
|
|
|
|
|
git remote rename another_remote origin &&
|
|
|
|
|
|
|
|
|
|
cd words &&
|
|
|
|
|
test_must_fail git filter-repo --path numbers 2>err &&
|
|
|
|
|
test_i18ngrep "GIT_DIR must be .git" err &&
|
|
|
|
|
rm err &&
|
|
|
|
|
cd .. &&
|
|
|
|
|
|
|
|
|
|
git config core.bare true &&
|
|
|
|
|
test_must_fail git filter-repo --path numbers 2>err &&
|
|
|
|
|
test_i18ngrep "GIT_DIR must be ." err &&
|
|
|
|
|
git config core.bare false &&
|
|
|
|
|
|
|
|
|
|
git update-ref -m "Just Testing" refs/heads/master HEAD &&
|
|
|
|
|
test_must_fail git filter-repo --path numbers 2>err &&
|
|
|
|
|
test_i18ngrep "expected at most one entry in the reflog" err &&
|
|
|
|
|
git reflog expire --expire=now &&
|
|
|
|
|
|
|
|
|
|
echo yes >>words/know &&
|
|
|
|
|
git stash save random change &&
|
|
|
|
|
rm -rf .git/logs/ &&
|
|
|
|
|
git gc &&
|
|
|
|
|
test_must_fail git filter-repo --path numbers 2>err &&
|
|
|
|
|
test_i18ngrep "has stashed changes" err &&
|
|
|
|
|
git update-ref -d refs/stash &&
|
|
|
|
|
|
|
|
|
|
echo yes >>words/know &&
|
|
|
|
|
git add words/know &&
|
|
|
|
|
git gc --prune=now &&
|
|
|
|
|
test_must_fail git filter-repo --path numbers 2>err &&
|
|
|
|
|
test_i18ngrep "you have uncommitted changes" err &&
|
|
|
|
|
git checkout HEAD words/know &&
|
|
|
|
|
|
|
|
|
|
echo yes >>words/know &&
|
|
|
|
|
test_must_fail git filter-repo --path numbers 2>err &&
|
|
|
|
|
test_i18ngrep "you have unstaged changes" err &&
|
|
|
|
|
git checkout -- words/know &&
|
|
|
|
|
|
|
|
|
|
test_must_fail git filter-repo --path numbers 2>err &&
|
|
|
|
|
test_i18ngrep "you have untracked changes" err &&
|
|
|
|
|
rm err &&
|
|
|
|
|
|
2019-05-02 16:10:26 +00:00
|
|
|
|
git worktree add ../other-worktree HEAD &&
|
|
|
|
|
test_must_fail git filter-repo --path numbers 2>../err &&
|
|
|
|
|
test_i18ngrep "you have multiple worktrees" ../err &&
|
|
|
|
|
rm -rf ../err &&
|
|
|
|
|
git worktree remove ../other-worktree &&
|
|
|
|
|
|
2019-03-14 00:05:58 +00:00
|
|
|
|
git update-ref -d refs/remotes/origin/master &&
|
|
|
|
|
test_must_fail git filter-repo --path numbers 2>../err &&
|
|
|
|
|
test_i18ngrep "refs/heads/master exists, but refs/remotes/origin/master not found" ../err &&
|
|
|
|
|
git update-ref -m restoring refs/remotes/origin/master refs/heads/master &&
|
|
|
|
|
rm ../err &&
|
|
|
|
|
|
|
|
|
|
rm .git/logs/refs/remotes/origin/master &&
|
|
|
|
|
git update-ref -m funsies refs/remotes/origin/master refs/heads/master~1 &&
|
|
|
|
|
test_must_fail git filter-repo --path numbers 2>../err &&
|
|
|
|
|
test_i18ngrep "refs/heads/master does not match refs/remotes/origin/master" ../err &&
|
|
|
|
|
rm ../err
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
2019-03-15 01:06:12 +00:00
|
|
|
|
test_expect_success 'other startup error cases and requests for help' '
|
|
|
|
|
(
|
|
|
|
|
git init startup_errors &&
|
|
|
|
|
cd startup_errors &&
|
|
|
|
|
|
|
|
|
|
git filter-repo -h >out &&
|
|
|
|
|
test_i18ngrep "filter-repo destructively rewrites history" out &&
|
|
|
|
|
|
|
|
|
|
test_must_fail git filter-repo 2>err &&
|
|
|
|
|
test_i18ngrep "No arguments specified." err &&
|
|
|
|
|
|
|
|
|
|
test_must_fail git filter-repo --analyze 2>err &&
|
|
|
|
|
test_i18ngrep "Nothing to analyze; repository is empty" err &&
|
|
|
|
|
|
|
|
|
|
(
|
|
|
|
|
GIT_CEILING_DIRECTORIES=$(pwd) &&
|
|
|
|
|
export GIT_CEILING_DIRECTORIES &&
|
|
|
|
|
mkdir not_a_repo &&
|
|
|
|
|
cd not_a_repo &&
|
|
|
|
|
test_must_fail git filter-repo --dry-run 2>err &&
|
|
|
|
|
test_i18ngrep "returned non-zero exit status" err &&
|
|
|
|
|
rm err &&
|
|
|
|
|
cd .. &&
|
|
|
|
|
rmdir not_a_repo
|
|
|
|
|
) &&
|
|
|
|
|
|
|
|
|
|
test_must_fail git filter-repo --analyze --path foobar 2>err &&
|
|
|
|
|
test_i18ngrep ": --analyze is incompatible with --path" err &&
|
|
|
|
|
|
|
|
|
|
test_must_fail git filter-repo --analyze --stdin 2>err &&
|
|
|
|
|
test_i18ngrep ": --analyze is incompatible with --stdin" err
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
2019-03-15 15:06:49 +00:00
|
|
|
|
test_expect_success 'invalid fast-import directives' '
|
|
|
|
|
(
|
|
|
|
|
git init invalid_directives &&
|
|
|
|
|
cd invalid_directives &&
|
|
|
|
|
|
|
|
|
|
echo "get-mark :15" | \
|
|
|
|
|
test_must_fail git filter-repo --stdin --force 2>err &&
|
|
|
|
|
test_i18ngrep "Unsupported command" err &&
|
|
|
|
|
|
|
|
|
|
echo "invalid-directive" | \
|
|
|
|
|
test_must_fail git filter-repo --stdin --force 2>err &&
|
|
|
|
|
test_i18ngrep "Could not parse line" err
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
2019-03-14 02:30:24 +00:00
|
|
|
|
test_expect_success 'mailmap sanity checks' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/analyze_me mailmap_sanity_checks &&
|
|
|
|
|
cd mailmap_sanity_checks &&
|
|
|
|
|
|
|
|
|
|
test_must_fail git filter-repo --mailmap /fake/path 2>../err &&
|
|
|
|
|
test_i18ngrep "Cannot read /fake/path" ../err &&
|
|
|
|
|
|
|
|
|
|
echo "Total Bogus" >../whoopsies &&
|
|
|
|
|
test_must_fail git filter-repo --mailmap ../whoopsies 2>../err &&
|
|
|
|
|
test_i18ngrep "Unparseable mailmap file" ../err &&
|
|
|
|
|
rm ../err &&
|
|
|
|
|
rm ../whoopsies &&
|
|
|
|
|
|
|
|
|
|
echo "Me <me@site.com> Myself <yo@email.com> Extraneous" >../whoopsies &&
|
|
|
|
|
test_must_fail git filter-repo --mailmap ../whoopsies 2>../err &&
|
|
|
|
|
test_i18ngrep "Unparseable mailmap file" ../err &&
|
|
|
|
|
rm ../err &&
|
|
|
|
|
rm ../whoopsies
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
2019-03-14 06:34:47 +00:00
|
|
|
|
test_expect_success 'incremental import' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/analyze_me incremental &&
|
|
|
|
|
cd incremental &&
|
|
|
|
|
|
|
|
|
|
original=$(git rev-parse master) &&
|
|
|
|
|
git fast-export --reference-excluded-parents master~2..master \
|
2019-04-27 22:18:59 +00:00
|
|
|
|
| git filter-repo --stdin --refname-callback "return b\"develop\"" &&
|
2019-03-14 06:34:47 +00:00
|
|
|
|
test "$(git rev-parse develop)" = "$original"
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
2019-04-02 17:41:36 +00:00
|
|
|
|
test_expect_success 'setup handle funny characters' '
|
|
|
|
|
test_create_repo funny_chars &&
|
|
|
|
|
(
|
|
|
|
|
cd funny_chars &&
|
|
|
|
|
|
|
|
|
|
git symbolic-ref HEAD refs/heads/españa &&
|
|
|
|
|
|
|
|
|
|
printf "بتتكلم بالهندي؟\n" >señor &&
|
|
|
|
|
printf "Αυτά μου φαίνονται αλαμπουρνέζικα.\n" >>señor &&
|
|
|
|
|
printf "זה סינית בשבילי\n" >>señor &&
|
|
|
|
|
printf "ちんぷんかんぷん\n" >>señor &&
|
|
|
|
|
printf "За мене тоа е шпанско село\n" >>señor &&
|
|
|
|
|
printf "看起来像天书。\n" >>señor &&
|
|
|
|
|
printf "انگار ژاپنی حرف می زنه\n" >>señor &&
|
|
|
|
|
printf "Это для меня китайская грамота.\n" >>señor &&
|
|
|
|
|
printf "To mi je španska vas\n" >>señor &&
|
|
|
|
|
printf "Konuya Fransız kaldım\n" >>señor &&
|
|
|
|
|
printf "עס איז די שפּראַך פון גיבבעריש\n" >>señor &&
|
|
|
|
|
printf "Not even UTF-8:\xe0\x80\x80\x00\n" >>señor &&
|
|
|
|
|
|
|
|
|
|
cp señor señora &&
|
|
|
|
|
cp señor señorita &&
|
|
|
|
|
git add . &&
|
|
|
|
|
|
|
|
|
|
export GIT_AUTHOR_NAME="Nguyễn Arnfjörð Gábor" &&
|
|
|
|
|
export GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME &&
|
|
|
|
|
export GIT_AUTHOR_EMAIL="emails@are.ascii" &&
|
|
|
|
|
export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL" &&
|
|
|
|
|
git commit -m "€$£₽₪" &&
|
|
|
|
|
|
|
|
|
|
git tag -a -m "₪₽£€$" סְפָרַד
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
|
|
|
|
test_expect_success 'handle funny characters' '
|
|
|
|
|
(
|
|
|
|
|
git clone file://"$(pwd)"/funny_chars funny_chars_checks &&
|
|
|
|
|
cd funny_chars_checks &&
|
|
|
|
|
|
|
|
|
|
file_sha=$(git rev-parse :0:señor) &&
|
filter-repo: add --replace-refs option
This adds the ability to automatically add new replacement refs for each
rewritten commit (as well as delete or update replacement refs that
existed before the run). This will allow users to use either new or old
commit hashes to reference commits locally, though old commit hashes
will need to be unabbreviated. The only requirement for this to work,
is that the person who does the rewrite also needs to push the replace
refs up where other users can grab them, and users who want to use them
need to modify their fetch refspecs to grab the replace refs.
However, other tools external to git may not understand replace refs...
Tools like Gerrit and GitHub apparently do not yet natively understand
replace refs. Trying to view "commits" by the replacement ref will
yield various forms of "Not Found" in each tool. One has to instead try
to view it as a branch with an odd name (including "refs/replace/"), and
often branches are accessed via a different URL style than commits so it
becomes very non-obvious to users how to access the info associated with
an old commit hash.
* In Gerrit, instead of being able to search on the sha1sum or use a
pre-defined URL to search and auto-redirect to the appropriate code
review with
https://gerrit.SITE.COM/#/q/${OLD_SHA1SUM},n,z
one instead has to have a special plugin and go to a URL like
https://gerrit.SITE.COM/plugins/gitiles/ORG/REPO/+/refs/replace/${OLD_SHA1SUM}
but then the user isn't shown the actual code review and will need
to guess which link to click on to get to it (and it'll only be
there if the user included a Change-Id in the commit message).
* In GitHub, instead of being able to go to a URL like
https://github.SITE.COM/ORG/REPO/commit/${OLD_SHA1SUM}
one instead has to navigate based on branch using
https://github.SITE.COM/ORG/REPO/tree/refs/replace/${OLD_SHA1SUM}
but that will show a listing of commits instead of information about
a specific commit; the user has to manually click on the first commit
to get to the desired location.
For now, providing replace refs at least allows users to access
information locally using old IDs; perhaps in time as other external
tools will gain a better understanding of how to use replace refs, the
barrier to history rewrites will decrease enough that big projects that
really need it (e.g. those that have committed many sins by commiting
stupidly large useless binary blobs) can at least seriously contemplate
the undertaking. History rewrites will always have some drawbacks and
pain associated with them, as they should, but when warranted it's nice
to have transition plans that are more smooth than a massive flag day.
Signed-off-by: Elijah Newren <newren@gmail.com>
2019-05-02 17:05:15 +00:00
|
|
|
|
former_head_sha=$(git rev-parse HEAD) &&
|
2019-04-02 17:41:36 +00:00
|
|
|
|
git filter-repo --to-subdirectory-filter títulos &&
|
|
|
|
|
|
|
|
|
|
cat <<-EOF >expect &&
|
|
|
|
|
100644 $file_sha 0 "t\303\255tulos/se\303\261or"
|
|
|
|
|
100644 $file_sha 0 "t\303\255tulos/se\303\261ora"
|
|
|
|
|
100644 $file_sha 0 "t\303\255tulos/se\303\261orita"
|
|
|
|
|
EOF
|
|
|
|
|
|
|
|
|
|
git ls-files -s >actual &&
|
|
|
|
|
test_cmp expect actual &&
|
|
|
|
|
|
|
|
|
|
commit_sha=$(git rev-parse HEAD) &&
|
|
|
|
|
tag_sha=$(git rev-parse סְפָרַד) &&
|
|
|
|
|
cat <<-EOF >expect &&
|
|
|
|
|
$commit_sha refs/heads/españa
|
filter-repo: add --replace-refs option
This adds the ability to automatically add new replacement refs for each
rewritten commit (as well as delete or update replacement refs that
existed before the run). This will allow users to use either new or old
commit hashes to reference commits locally, though old commit hashes
will need to be unabbreviated. The only requirement for this to work,
is that the person who does the rewrite also needs to push the replace
refs up where other users can grab them, and users who want to use them
need to modify their fetch refspecs to grab the replace refs.
However, other tools external to git may not understand replace refs...
Tools like Gerrit and GitHub apparently do not yet natively understand
replace refs. Trying to view "commits" by the replacement ref will
yield various forms of "Not Found" in each tool. One has to instead try
to view it as a branch with an odd name (including "refs/replace/"), and
often branches are accessed via a different URL style than commits so it
becomes very non-obvious to users how to access the info associated with
an old commit hash.
* In Gerrit, instead of being able to search on the sha1sum or use a
pre-defined URL to search and auto-redirect to the appropriate code
review with
https://gerrit.SITE.COM/#/q/${OLD_SHA1SUM},n,z
one instead has to have a special plugin and go to a URL like
https://gerrit.SITE.COM/plugins/gitiles/ORG/REPO/+/refs/replace/${OLD_SHA1SUM}
but then the user isn't shown the actual code review and will need
to guess which link to click on to get to it (and it'll only be
there if the user included a Change-Id in the commit message).
* In GitHub, instead of being able to go to a URL like
https://github.SITE.COM/ORG/REPO/commit/${OLD_SHA1SUM}
one instead has to navigate based on branch using
https://github.SITE.COM/ORG/REPO/tree/refs/replace/${OLD_SHA1SUM}
but that will show a listing of commits instead of information about
a specific commit; the user has to manually click on the first commit
to get to the desired location.
For now, providing replace refs at least allows users to access
information locally using old IDs; perhaps in time as other external
tools will gain a better understanding of how to use replace refs, the
barrier to history rewrites will decrease enough that big projects that
really need it (e.g. those that have committed many sins by commiting
stupidly large useless binary blobs) can at least seriously contemplate
the undertaking. History rewrites will always have some drawbacks and
pain associated with them, as they should, but when warranted it's nice
to have transition plans that are more smooth than a massive flag day.
Signed-off-by: Elijah Newren <newren@gmail.com>
2019-05-02 17:05:15 +00:00
|
|
|
|
$commit_sha refs/replace/$former_head_sha
|
2019-04-02 17:41:36 +00:00
|
|
|
|
$tag_sha refs/tags/סְפָרַד
|
|
|
|
|
EOF
|
|
|
|
|
|
|
|
|
|
git show-ref >actual &&
|
|
|
|
|
test_cmp expect actual &&
|
|
|
|
|
|
|
|
|
|
echo "€$£₽₪" >expect &&
|
|
|
|
|
git cat-file -p HEAD | tail -n 1 >actual &&
|
|
|
|
|
|
|
|
|
|
echo "₪₽£€$" >expect &&
|
|
|
|
|
git cat-file -p סְפָרַד | tail -n 1 >actual
|
|
|
|
|
)
|
|
|
|
|
'
|
|
|
|
|
|
2018-11-05 14:30:50 +00:00
|
|
|
|
test_done
|