filter-repo: implement --use-base-name

This new flag allows people to filter files solely based on their
basename rather than on their full path within the repo, making it
easier to e.g. remove all .DS_Store files or keep all README.md
files.

Signed-off-by: Elijah Newren <newren@gmail.com>
This commit is contained in:
Elijah Newren 2019-05-17 11:01:24 -07:00
parent fd0b58ecdc
commit 092d0163d4
2 changed files with 38 additions and 6 deletions

View File

@ -1976,6 +1976,9 @@ class FilteringOptions(object):
help=_("Regex of paths to include in filtered history. Multiple "
"--path-regex options can be specified to get a union of "
"paths"))
path.add_argument('--use-base-name', action='store_true',
help=_("Match on file base name instead of full path from the top "
"of the repo. Incompatible with --path-rename."))
rename = parser.add_argument_group(title=_("Renaming based on paths "
"(see also --filename-callback)"))
@ -2153,6 +2156,11 @@ class FilteringOptions(object):
# achieve that is setting args.inclusive to False.
if not any(x[0] == 'filter' for x in args.path_changes):
args.inclusive = False
# Also check for incompatible --use-base-name and --path-rename flags.
if args.use_base_name:
if any(x[0] == 'rename' for x in args.path_changes):
raise SystemExit(_("Error: --use-base-name and --path-rename are "
"incompatible."))
# Also throw in a sanity check on git version here;
# PERF: remove this check once new enough git versions are common
p = subprocess.Popen('git diff-tree -h'.split(),
@ -2918,8 +2926,11 @@ class RepoFilter(object):
return True
return False
def newname(path_changes, pathname, filtering_is_inclusive):
def newname(path_changes, pathname, use_base_name, filtering_is_inclusive):
wanted = False
full_pathname = pathname
if use_base_name:
pathname = os.path.basename(pathname)
for (mod_type, match_type, path_exp) in path_changes:
if mod_type == 'filter' and not wanted:
assert match_type in ('match', 'glob', 'regex')
@ -2932,9 +2943,9 @@ class RepoFilter(object):
elif mod_type == 'rename':
old_exp, new_exp = path_exp.split(b':')
assert match_type in ('prefix',)
if match_type == 'prefix' and pathname.startswith(old_exp):
pathname = pathname.replace(old_exp, new_exp, 1)
return pathname if (wanted == filtering_is_inclusive) else None
if match_type == 'prefix' and full_pathname.startswith(old_exp):
full_pathname = full_pathname.replace(old_exp, new_exp, 1)
return full_pathname if (wanted == filtering_is_inclusive) else None
# Change the commit message according to callback
if self._message_callback:
@ -2974,7 +2985,7 @@ class RepoFilter(object):
change.filename = self._newnames[change.filename]
else:
change.filename = newname(args.path_changes, change.filename,
args.inclusive)
args.use_base_name, args.inclusive)
if self._filename_callback:
change.filename = self._filename_callback(change.filename)
self._newnames[change.filename] = change.filename

View File

@ -232,6 +232,24 @@ test_expect_success '--to-subdirectory-filter' '
)
'
test_expect_success '--use-base-name' '
(
git clone file://"$(pwd)"/metasyntactic use_base_name &&
cd use_base_name &&
git filter-repo --path small --path important --use-base-name &&
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 = 3 filenames &&
grep ^numbers/small$ filenames &&
grep ^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
)
'
test_expect_success 'refs/replace/ to skip a parent' '
(
git clone file://"$(pwd)"/metasyntactic replace_skip_ref &&
@ -837,7 +855,10 @@ test_expect_success 'other startup error cases and requests for help' '
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
test_i18ngrep ": --analyze is incompatible with --stdin" err &&
test_must_fail git filter-repo --path-rename foo:bar --use-base-name 2>err &&
test_i18ngrep ": --use-base-name and --path-rename are incompatible" err
)
'