来自特定提交的检出文件失败,路径中有 space
Checkout files from a specific commit failed with space in the path
我想要实现的是获取特定提交 76363636 从 branch1 到 branch2 的所有更新。
我使用了以下代码而不是仅仅检查这个提交,因为它不符合我的目的:
git checkout branch1 $(git diff-tree --no-commit-id --name-only -r 76363636)
当提交中的文件路径之间没有 space 时,这个工作正常。我多次使用此代码。
但不是当文件路径中有 space 时,例如。下面:
force-app/main/default/layouts/PersonAccount-Layout
Professionnel.layout-meta.xml
我收到以下错误:
error: pathspec 'force-app/main/default/layouts/PersonAccount-Layout' did not match any file(s) known to git
如何在我动态获取文件时使用双引号实际保护文件
下面的代码将不起作用:
git checkout branch1 "$(git diff-tree --no-commit-id --name-only -r 76363636)"
TL;DR
您想在 运行 执行命令时临时设置 IFS
:
save="$IFS"
IFS=$'\n' # depends on shell
git checkout branch1 $(git diff-tree --no-commit-id --name-only -r 76363636)
IFS="$save"
或类似的。您可能必须在此处使用 $'\n'
以外的其他内容。您也可以考虑使用 git restore
而不是 git checkout
。查看下面的所有详细信息。
长
这不是 Git 的问题——嗯,不完全是——而是 shell 你使用。解决方法是让 shell 表现得更好,或者完全绕过它(请参阅下面的 git restore
部分)。
当您在命令提示符下输入命令时:
$ command with some arguments
shell——command-line 解释器——将这里的部分分解:command , with, some, arguments 各成为一个“词”, shell 然后在系统某处找到一些名为 command
的可执行文件,并 运行 将所有四个“单词”作为四个单独的 argv
参数提供。 argv[0]
参数通常要么被完全忽略,要么用于增加错误消息,以防命令以与预期不同的名称安装(例如,git-2.17
到 运行 旧版本 Git).剩余的参数,在本例中从 argv[1]
到 argv[3]
,然后由程序解释——但请注意它们是 pre-divided。
您是否希望运行,例如
$ git restore --source=HEAD -SW "file with spaces"
你 必须 使用引号(双引号或单引号),以便 shell 调用带有参数的 git restore, --source=HEAD, -SW, 和 file with spaces。请注意封闭的引号是如何消失的,但 spaces 被保留:有一个包含两个空格的参数。
命令:
git diff-tree --no-commit-id --name-only -r 76363636
本身分为六个字,以git开头,以76363636结尾。该命令的 shell 运行 并且——因为整个命令包含在 $(...)
中—— 读取它的输出 。 shell 然后 将其输出解释为一系列单词 ,由白色 space 分隔:space、制表符和换行符。 shell 将这些单词打散,然后 运行:
git checkout branch1 <word1> <word2> ... <wordN>
所有 N broken-apart 个单词。
因为是shell在做breaking-up,所以是shell 你必须在这里克服。有一种方法可以做到这一点。
shell 使用 $IFS
断词
Bourne-derived shells 使用 内部字段分隔符 变量 $IFS
来确定什么使某物成为“词”。默认 IFS 设置为 space-tab-newline。这有点难以显示,因为 space、制表符和换行符都 在屏幕上显示 为空白或空无。
为了表示space、制表符和换行符,我们可以在引号中使用文字space,序列\t
代表TAB,以及 NL 的序列 \n
(换行符)。 有些 shell让你直接这样做:
var=$' \t\n'
这是一个单引号,前面有一个 dollar-sign $
字符:然后用反斜杠序列解释其中的文本,处理方式类似于它们在 C 编程语言中的用法。
某些版本的 shell 不允许这样做;在这里,我们可以使用 POSIX printf
命令:
printf " \t\n"
(此处双引号或单引号均可)
我们想要,当然是shell只在换行处换行。如果你的文件名包含嵌入换行符,这将不起作用——但这样的文件名特别邪恶,似乎不常用,不像 Windows 和 macOS 文件名通常嵌入 space在其中。如果你真的想要 bullet-proofing,你会想要 -z
选项和 NUL-terminated 路径:ASCII NUL 是唯一在 Linux 上的路径名中字面禁止的字符(因此在 Git 中也是如此)。
让 shell 在换行处分解我们的路径名后,我们应该——为了“shell 卫生”,如果没有别的——恢复 $IFS
之后设置。为此,我们可以直接将 IFS
设置回 space-tab-newline:
IFS=$'\n'
... do things using $(...) ...
IFS=$' \t\n'
或者,我们可以获取当前设置,将其设置为我们需要的设置,然后恢复旧设置。这对于可能出于 他们的 目的设置 IFS 并且不希望我们破坏它的其他用户来说很好:
f() {
local save
save="$IFS"
IFS=$(printf '\n')
... our code ...
IFS="$save"
}
函数 f
现在可以从 也 暂时更改 IFS
的其他 shell 函数安全地调用,而无需继续设置IFS
在另一个 shell 函数中。
(在这里,我使用 printf
而不是 $'...'
,以防我们有一个 shell 不允许使用 $'...'
。)
使用git restore
git checkout
命令在这里有几个缺陷:
- 如果 dif 列表
git diff-tree
的 erences 是 empty,我们检查整个分支。这可能不是我们想要的。这是一个很大的缺陷!
- 如果某些文件名以
-
开头,这个 git checkout
将表现不佳。我们可以通过添加 --
来解决这个问题,这可能是个好主意。
- 如果
branch1
不是有效的分支名称,git checkout
将表现不佳。
我们可以避免所有这些问题并且通过使用git restore
及其--pathspec-from-file
和[来解决整个IFS
相关的问题=58=]旗帜。 (请确保您的 git restore
的 Git 版本支持这些标志;它们首次出现在 Git 2.25 中,而 git restore
本身首次出现在 Git 2.23 中。) -z
标志在 git diff-tree
中存在的时间更长,因此如果您的 Git 至少为 2.25,那么您同时拥有两者。
那么我们要做的是:
git diff-tree <options> -z <rev> |
git --literal-pathspecs restore --source=branch1 --pathspec-from-file=- --pathspec-file-nul
git diff-tree
运行 和往常一样,但这次输出 un-encoded 路径名——这解决了一些你没有解决的问题yet encountered——并以 ASCII NUL 终止每个路径名。然后 git restore
命令 从标准输入中读取 这些路径名(作为路径规范)。 git
的 --literal-pathspecs
选项本身告诉 git restore
不要尝试解释任何输入路径中的路径规范魔法。
我想要实现的是获取特定提交 76363636 从 branch1 到 branch2 的所有更新。
我使用了以下代码而不是仅仅检查这个提交,因为它不符合我的目的:
git checkout branch1 $(git diff-tree --no-commit-id --name-only -r 76363636)
当提交中的文件路径之间没有 space 时,这个工作正常。我多次使用此代码。
但不是当文件路径中有 space 时,例如。下面:
force-app/main/default/layouts/PersonAccount-Layout Professionnel.layout-meta.xml
我收到以下错误:
error: pathspec 'force-app/main/default/layouts/PersonAccount-Layout' did not match any file(s) known to git
如何在我动态获取文件时使用双引号实际保护文件
下面的代码将不起作用:
git checkout branch1 "$(git diff-tree --no-commit-id --name-only -r 76363636)"
TL;DR
您想在 运行 执行命令时临时设置 IFS
:
save="$IFS"
IFS=$'\n' # depends on shell
git checkout branch1 $(git diff-tree --no-commit-id --name-only -r 76363636)
IFS="$save"
或类似的。您可能必须在此处使用 $'\n'
以外的其他内容。您也可以考虑使用 git restore
而不是 git checkout
。查看下面的所有详细信息。
长
这不是 Git 的问题——嗯,不完全是——而是 shell 你使用。解决方法是让 shell 表现得更好,或者完全绕过它(请参阅下面的 git restore
部分)。
当您在命令提示符下输入命令时:
$ command with some arguments
shell——command-line 解释器——将这里的部分分解:command , with, some, arguments 各成为一个“词”, shell 然后在系统某处找到一些名为 command
的可执行文件,并 运行 将所有四个“单词”作为四个单独的 argv
参数提供。 argv[0]
参数通常要么被完全忽略,要么用于增加错误消息,以防命令以与预期不同的名称安装(例如,git-2.17
到 运行 旧版本 Git).剩余的参数,在本例中从 argv[1]
到 argv[3]
,然后由程序解释——但请注意它们是 pre-divided。
您是否希望运行,例如
$ git restore --source=HEAD -SW "file with spaces"
你 必须 使用引号(双引号或单引号),以便 shell 调用带有参数的 git restore, --source=HEAD, -SW, 和 file with spaces。请注意封闭的引号是如何消失的,但 spaces 被保留:有一个包含两个空格的参数。
命令:
git diff-tree --no-commit-id --name-only -r 76363636
本身分为六个字,以git开头,以76363636结尾。该命令的 shell 运行 并且——因为整个命令包含在 $(...)
中—— 读取它的输出 。 shell 然后 将其输出解释为一系列单词 ,由白色 space 分隔:space、制表符和换行符。 shell 将这些单词打散,然后 运行:
git checkout branch1 <word1> <word2> ... <wordN>
所有 N broken-apart 个单词。
因为是shell在做breaking-up,所以是shell 你必须在这里克服。有一种方法可以做到这一点。
shell 使用 $IFS
断词
Bourne-derived shells 使用 内部字段分隔符 变量 $IFS
来确定什么使某物成为“词”。默认 IFS 设置为 space-tab-newline。这有点难以显示,因为 space、制表符和换行符都 在屏幕上显示 为空白或空无。
为了表示space、制表符和换行符,我们可以在引号中使用文字space,序列\t
代表TAB,以及 NL 的序列 \n
(换行符)。 有些 shell让你直接这样做:
var=$' \t\n'
这是一个单引号,前面有一个 dollar-sign $
字符:然后用反斜杠序列解释其中的文本,处理方式类似于它们在 C 编程语言中的用法。
某些版本的 shell 不允许这样做;在这里,我们可以使用 POSIX printf
命令:
printf " \t\n"
(此处双引号或单引号均可)
我们想要,当然是shell只在换行处换行。如果你的文件名包含嵌入换行符,这将不起作用——但这样的文件名特别邪恶,似乎不常用,不像 Windows 和 macOS 文件名通常嵌入 space在其中。如果你真的想要 bullet-proofing,你会想要 -z
选项和 NUL-terminated 路径:ASCII NUL 是唯一在 Linux 上的路径名中字面禁止的字符(因此在 Git 中也是如此)。
让 shell 在换行处分解我们的路径名后,我们应该——为了“shell 卫生”,如果没有别的——恢复 $IFS
之后设置。为此,我们可以直接将 IFS
设置回 space-tab-newline:
IFS=$'\n'
... do things using $(...) ...
IFS=$' \t\n'
或者,我们可以获取当前设置,将其设置为我们需要的设置,然后恢复旧设置。这对于可能出于 他们的 目的设置 IFS 并且不希望我们破坏它的其他用户来说很好:
f() {
local save
save="$IFS"
IFS=$(printf '\n')
... our code ...
IFS="$save"
}
函数 f
现在可以从 也 暂时更改 IFS
的其他 shell 函数安全地调用,而无需继续设置IFS
在另一个 shell 函数中。
(在这里,我使用 printf
而不是 $'...'
,以防我们有一个 shell 不允许使用 $'...'
。)
使用git restore
git checkout
命令在这里有几个缺陷:
- 如果 dif 列表
git diff-tree
的 erences 是 empty,我们检查整个分支。这可能不是我们想要的。这是一个很大的缺陷! - 如果某些文件名以
-
开头,这个git checkout
将表现不佳。我们可以通过添加--
来解决这个问题,这可能是个好主意。 - 如果
branch1
不是有效的分支名称,git checkout
将表现不佳。
我们可以避免所有这些问题并且通过使用git restore
及其--pathspec-from-file
和[来解决整个IFS
相关的问题=58=]旗帜。 (请确保您的 git restore
的 Git 版本支持这些标志;它们首次出现在 Git 2.25 中,而 git restore
本身首次出现在 Git 2.23 中。) -z
标志在 git diff-tree
中存在的时间更长,因此如果您的 Git 至少为 2.25,那么您同时拥有两者。
那么我们要做的是:
git diff-tree <options> -z <rev> |
git --literal-pathspecs restore --source=branch1 --pathspec-from-file=- --pathspec-file-nul
git diff-tree
运行 和往常一样,但这次输出 un-encoded 路径名——这解决了一些你没有解决的问题yet encountered——并以 ASCII NUL 终止每个路径名。然后 git restore
命令 从标准输入中读取 这些路径名(作为路径规范)。 git
的 --literal-pathspecs
选项本身告诉 git restore
不要尝试解释任何输入路径中的路径规范魔法。