filter-repo: change --path-rename to work on matches instead of prefixes

Using an exact path (file or directory) for --path-rename instead of a
prefix removes an ugly caveat from the documentation, makes it operate
similarly to --path, and will make it easier to reuse common code when I
add the --paths-from-file option.  Switch over, and replace the
startswith() check by a call to filename_matches().

Signed-off-by: Elijah Newren <newren@gmail.com>
pull/13/head
Elijah Newren 5 years ago
parent 092d0163d4
commit 9744c57106

@ -400,12 +400,11 @@ renamed to the same location), use --path-rename; for example, to rename
both 'cmds/' and 'src/scripts/' to 'tools/':
```shell
git filter-repo --path-rename cmds/:tools/ --path-rename src/scripts/:tools/
git filter-repo --path-rename cmds:tools --path-rename src/scripts/:tools/
```
It is preferable with `--path-rename` to using trailing slashes on
directories since it matches on a prefix; without the trailing slash, a
path named 'cmdstuff.txt' would be renamed to 'tools/tuff.txt'.
As with `--path`, directories can be specified with or without a
trailing slash for `--path-rename`.
If you do a `--path-rename` to something that was already in use, it will
be silently overwritten. However, if you try to rename multiple files to

@ -1886,9 +1886,15 @@ class FilteringOptions(object):
class AppendFilter(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
suffix = option_string[len('--path-'):] or 'match'
if suffix == 'rename':
if suffix.startswith('rename'):
mod_type = 'rename'
match_type = 'prefix'
match_type = option_string[len('--path-rename-'):] or 'match'
values = values.split(b':', 1)
if values[0] and values[1] and not (
values[0].endswith(b'/') == values[1].endswith(b'/')):
raise SystemExit(_("Error: With --path-rename, if OLD_NAME and "
"NEW_NAME are both non-empty and either ends "
"with a slash then both must."))
else:
mod_type = 'filter'
match_type = suffix
@ -1982,12 +1988,12 @@ class FilteringOptions(object):
rename = parser.add_argument_group(title=_("Renaming based on paths "
"(see also --filename-callback)"))
rename.add_argument('--path-rename', '--path-rename-prefix',
rename.add_argument('--path-rename', '--path-rename-match',
metavar='OLD_NAME:NEW_NAME', dest='path_changes', type=os.fsencode,
action=FilteringOptions.AppendFilter,
help=_("Prefix to rename; if filename starts with OLD_NAME, "
"replace that with NEW_NAME. Multiple --path-rename "
"options can be specified."))
help=_("Path to rename; if filename or directory matches OLD_NAME "
"rename to NEW_NAME. Multiple --path-rename options can be "
"specified."))
helpers = parser.add_argument_group(title=_("Path shortcuts"))
helpers.add_argument('--subdirectory-filter', metavar='DIRECTORY',
@ -2941,10 +2947,10 @@ class RepoFilter(object):
if match_type == 'regex' and path_exp.search(pathname):
wanted = True
elif mod_type == 'rename':
old_exp, new_exp = path_exp.split(b':')
assert match_type in ('prefix',)
if match_type == 'prefix' and full_pathname.startswith(old_exp):
full_pathname = full_pathname.replace(old_exp, new_exp, 1)
match, repl = path_exp
assert match_type in ('match',)
if match_type == 'match' and filename_matches(match, full_pathname):
full_pathname = full_pathname.replace(match, repl, 1)
return full_pathname if (wanted == filtering_is_inclusive) else None
# Change the commit message according to callback

@ -858,7 +858,10 @@ test_expect_success 'other startup error cases and requests for help' '
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
test_i18ngrep ": --use-base-name and --path-rename are incompatible" err &&
test_must_fail git filter-repo --path-rename foo:bar/ 2>err &&
test_i18ngrep "either ends with a slash then both must." err
)
'

Loading…
Cancel
Save