提问人:klor 提问时间:1/18/2023 最后编辑:klor 更新时间:1/22/2023 访问量:381
从 Git 存储库历史记录中删除所有文件,其路径在文件名中具有转义 \ ,并带有 git filter-repo
Remove all files from Git repo history with path having escape \ in filename with git filter-repo
问:
我有特殊的文件名,带有转义\字符,存储在 Debian 10 Linux 的 Git 存储库中。
问题:无法在 Windows 上 git checkout 文件,文件名中包含不兼容的字符。
例:
git log --all --name-only -m --pretty= '*\\*'
"systemd/system/default.target.wants/snap-git\\x2dfilter\\x2drepo-7.mount"
"systemd/system/multi-user.target.wants/snap-git\\x2dfilter\\x2drepo-7.mount"
"systemd/system/snap-git\\x2dfilter\\x2drepo-7.mount"
我在 Windows 结帐时收到以下 Git 错误:
C:\Git\bin\git.exe reset --hard "5ef1cac3a03304c35b455edf32bd1bb78060c5b9" --
error: invalid path 'systemd/system/default.target.wants/snap-git\x2dfilter\x2drepo-7.mount'
fatal: Could not reset index file to revision '5ef1cac3a03304c35b455edf32bd1bb78060c5b9'.
Done
问题重现步骤:
# Clone repository, to be executed on a safe repo:
git clone --no-local /source/repo/path/ /target/path/to/repo/clone/
# Cloning into '/target/path/to/repo/clone'...
# remote: Enumerating objects: 9534, done.
# remote: Counting objects: 100% (9534/9534), done.
# remote: Compressing objects: 100% (4776/4776), done.
# remote: Total 9534 (delta 4215), reused 8043 (delta 3136), pack-reused 0
# Receiving objects: 100% (9534/9534), 7.41 MiB | 16.78 MiB/s, done.
# Resolving deltas: 100% (4215/4215), done.
cd /target/path/to/repo/clone/
# List the files with escape \ from repo history into a list file:
git log --all --name-only -m --pretty= '*\\*' | sort -u >/opt/git_repo_files_w_escape.txt
# Remove the files with escape \ from repo history:
git filter-repo --invert-paths --paths-from-file /opt/git_repo_files_w_escape.txt
Parsed 592 commits
New history written in 0.25 seconds; now repacking/cleaning...
Repacking your repo and cleaning out old unneeded objects
HEAD is now at 71128f3 .gitignore: ADD snap-git to be ignored
Enumerating objects: 9354, done.
Counting objects: 100% (9354/9354), done.
Delta compression using up to 8 threads
Compressing objects: 100% (3694/3694), done.
Writing objects: 100% (9354/9354), done.
Total 9354 (delta 4085), reused 9354 (delta 4085), pack-reused 0
Completely finished after 0.55 seconds.
# List files with escape \ to check result:
git log --format="reference" --name-status --diff-filter=A '*\\*'
# "systemd/system/default.target.wants/snap-git\\x2dfilter\\x2drepo-7.mount"
# "systemd/system/multi-user.target.wants/snap-git\\x2dfilter\\x2drepo-7.mount"
# "systemd/system/snap-git\\x2dfilter\\x2drepo-7.mount"
# Unfortunately it seems filter-repo was executed, but log still lists filenames with escape \ :-(
问题:
1) 如何从 Git 存储库历史记录中删除所有文件,路径在文件名中至少有一个转义\字符?
(原因:无法在 Windows 上签出文件名中包含不兼容字符的文件)
UPDATE1:
尝试按照建议将输入文件列表中的字符串替换为 - ,但 git history remove 仍然不成功:\\x2d
# List the files with escape \ from repo history into a list file:
git log --all --name-only -m --pretty= '*\\*' | sort -u >/opt/git_repo_files_w_escape.txt
# Replace \\x2d string to - in git_repo_files_w_escape.txt:
sed -i 's/\\\\x2d/-/g' /opt/git_repo_files_w_escape.txt
# Remove the listed files from repo history:
git filter-repo --invert-paths --paths-from-file /opt/git_repo_files_w_escape.txt
Parsed 592 commits
New history written in 0.25 seconds; now repacking/cleaning...
Repacking your repo and cleaning out old unneeded objects
HEAD is now at 71128f3 .gitignore: ADD snap-git to be ignored
Enumerating objects: 9354, done.
Counting objects: 100% (9354/9354), done.
Delta compression using up to 8 threads
Compressing objects: 100% (3694/3694), done.
Writing objects: 100% (9354/9354), done.
Total 9354 (delta 4085), reused 9354 (delta 4085), pack-reused 0
Completely finished after 0.55 seconds.
# List files with escape \ to check result:
git log --format="reference" --name-status --diff-filter=A '*\\*'
# "systemd/system/default.target.wants/snap-git\\x2dfilter\\x2drepo-7.mount"
# "systemd/system/multi-user.target.wants/snap-git\\x2dfilter\\x2drepo-7.mount"
# "systemd/system/snap-git\\x2dfilter\\x2drepo-7.mount"
# Unfortunately log still lists filenames with \\x2d :-(
UPDATE2:
尝试替换 in git_repo_files_w_escape.txt to 或,但都没有导致从 Git 历史记录中删除文件名的文件。\\x2d
\\\\x2d
\x2d
\\x2d
UPDATE3:
我正在寻找一个基于 git filter-repo 的工作解决方案。
还有什么想法吗?
答:
fwiw,这适用于 linux 系统,这允许我重写 HEAD 提交,而无需在磁盘上签出文件:
git ls-files | grep -a -e '\\' | while read f; do
f=$(echo $f | sed -e 's|"||g')
new=$(echo "$f" | sed -e 's|\\\\x2d|-|g')
git show "@:$f" > $new
git rm --cached "$f"
git add "$new"
done
git status
git commit --amend
相同的命令应该适用于 Windows。git-bash
评论
git filter-branch
regex:(.*)(\\\\x2d)(.*)=>\1-\3
假设您有许多要修复的文件分散在层次结构中,则解决方案看起来很乏味。您可以改用 和 的组合来修改整个历史记录中的文件名。git filter-repo
git fast-export
git fast-import
git fast-export --no-data --all > exported
现在删除包含反斜杠的文件条目:
grep -v '^[DM] .*\\' exported > fixed
除了删除文件外,您还可以修改文件名。例如,要用破折号替换反斜杠,您可以尝试以下操作:-
sed -e '/^[DM] /s,\\,-,g' < exported > fixed
现在,您可以调查这两个文件之间的差异,以确保没有修改任何提交消息:
diff -u exported fixed | less
现在尝试导入修改后的历史记录:
git fast-import < fixed
这将停止并显示一个错误,告诉您不会修改分支,因为旧的分支头不是新头的子集。如果没有其他错误,您现在可以强制修改:
git fast-import --force < fixed
评论
--no-data
你根据一个常见但不正确的假设,将错误的输入到 filter-repo 中,这是关于 git 日志如何工作的。
看看你自己的输出:
$ git log --format="reference" --name-status --diff-filter=A '*\\*'
"systemd/system/default.target.wants/snap-git\\x2dfilter\\x2drepo-7.mount"
"systemd/system/multi-user.target.wants/snap-git\\x2dfilter\\x2drepo-7.mount"
"systemd/system/snap-git\\x2dfilter\\x2drepo-7.mount"
让我们以第一行为例。如果你要将其存储在一个文件中,并将其传递给 --paths-from-file,那么 git-filter-repo 将寻找一个名为 remove.存储库中没有此类文件。相反,你有一个名为 .(请注意,我已经删除了两个字符和其中两个字符。"systemd/system/default.target.wants/snap-git\\x2dfilter\\x2drepo-7.mount"
systemd/system/default.target.wants/snap-git\x2dfilter\x2drepo-7.mount
"
\
这里的问题是,您假设 git log 会按原样列出文件名,只要有特殊字符,它就不会这样做。您通常可以通过设置 core.quotepath=false 来解决这个问题(这在您有非 ASCII 字符时特别有帮助),但当您有反斜杠时,即使这样也会被忽略。
以下是可能更适合您生成要排除的文件名列表的方法:
git log -z --all --name-only -m --pretty= '*\\*' | tr '\0' '\n' | sort -u >/opt/git_repo_files_w_escape.txt
但它假定您没有带换行符的文件名。(但是,如果您确实有带有换行符的文件,那么 --paths-from-file 将不适合您。
更简单的方法是绕过创建具有错误名称的文件列表,然后通过模式以编程方式删除它们:
git filter-repo --filename-callback 'return None if b'\\' in filename else filename'
评论
git filter-repo
评论