[英]How to remove all files from GIT repo history with path having colon : in filename?
[英]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-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
基于关于 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.