繁体   English   中英

使用 git filter-repo 从 Git repo 历史记录中删除所有文件,文件名中的路径具有转义 \

[英]Remove all files from Git repo history with path having escape \ in filename with git filter-repo

我在 Debian 10 Linux 上的 Git 存储库中存储了带有转义字符的特殊文件名。

问题:无法在 Windows 上 git 签出文件,文件名中包含不兼容的字符。

例子:

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 上检出那些文件名中包含不兼容字符的文件)

更新1:

尝试按照建议将\\x2d字符串替换为 - 在输入文件列表中,但 git 历史记录删除仍然不成功:

# 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 :-(

更新2:

试图将 git_repo_files_w_escape.txt 中的\\x2d替换为\\\\x2d\x2d但没有一个导致从 Git 历史记录中删除文件名中具有\\x2d的文件。

更新3:

我正在寻找基于 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-repo的解决方案看起来很乏味。 您可以结合使用git fast-exportgit 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

基于关于 git 日志如何工作的常见但不正确的假设,您将错误的输入输入到 filter-repo 中。

看自己的output:

$ 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 将寻找一个名为"systemd/system/default.target.wants/snap-git\\x2dfilter\\x2drepo-7.mount"删除。 您的存储库中没有这样的文件。 相反,您有一个名为systemd/system/default.target.wants/snap-git\x2dfilter\x2drepo-7.mount (请注意,我已经删除了两个"字符和两个\字符。)

这里的问题是您假设 git 日志会按原样列出文件名,但只要有特殊字符,它就不会这样做。 你通常可以通过设置 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 将不适合您。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM