检出子目录中的所有文件
Checking out all files in sub directory
我正在尝试检出特定目录 "code/app" 中的所有文件和子目录,但是 git 给了我 code/app 目录及其内容。我只想要它的内容。我想在 post-receive git 挂钩中使用它。
git checkout -f master -- code/app
我也试了下面的方法都没用
git checkout -f master -- code/app/*
git checkout -f master -- code/app/.
如何获得上述预期的行为?
更新:
post-接收
#!/usr/bin/env ruby
# post-receive
# Read STDIN
from, to, branch = ARGF.read.split " "
if (branch =~ /master$/)
puts "Received branch #{branch}, deploying."
# Copy files to deploy directory
deploy_to_dir = File.expand_path('../development')
dir_to_checkout = '06\ -\ Code/app/'
`GIT_WORK_TREE="#{deploy_to_dir}" git checkout -f master -- #{dir_to_checkout}`
puts "DEPLOY: master(#{to}) copied to '#{deploy_to_dir}'"
exit
end
# Only deploy if pre-production branch was pushed
if (branch =~ /pre-production$/)
puts "Received branch #{branch}, deploying."
# Copy files to deploy directory
deploy_to_dir = File.expand_path('../staging')
dir_to_checkout = '06\ -\ Code/app/'
`GIT_WORK_TREE="#{deploy_to_dir}" git checkout -f pre-production -- #{dir_to_checkout}`
puts "DEPLOY: pre-production(#{to}) copied to '#{deploy_to_dir}'"
exit
end
if (branch =~ /production$/)
puts "Received branch #{branch}, deploying."
# Copy files to deploy directory
deploy_to_dir = File.expand_path('../')
dir_to_checkout = '06\ -\ Code/app/'
`GIT_WORK_TREE="#{deploy_to_dir}" git checkout -f production -- #{dir_to_checkout}`
puts "DEPLOY: production(#{to}) copied to '#{deploy_to_dir}'"
exit
end
我不是 Ruby 专家,但幸运的是,这似乎并不重要:我可以阅读你的代码应该做什么(虽然没有说它是否有任何 Ruby - 具体问题)。
如果我可以稍微改一下你的问题,你是说:
The git checkout
command mostly does what I want:
git checkout -f <branch> -- <dir-path>
e.g.
git checkout -f master -- Code/app/
extracts all the files that are named Code/app/<em>somedir/somefile</em>
. The part I don't like is that it puts them in files named Code/app/<em>somedir/somefile</em>
when I want them to just be in <em>somedir/somefile</em>
.
有 种重命名文件的方法,但其中 none 方法简单易行,至少与仅进行结帐然后重命名文件相比.因此——进行结帐,然后重命名文件——可能是您应该做的。
这里还有一些更微妙的问题。
首先,这种特殊形式的 git checkout
只是 添加或替换工作树中的 文件。假设在您的部署分支之一(master
、pre-production
和 production
)中,您 删除了 一些不需要的文件。部署脚本将 添加或更新 任何添加或更新的文件,但不会 删除 文件,因此删除的文件不会从您的部署区域中删除.
(您可能 想要 某些文件的这种行为。为 "just the right files" 实现它可能很棘手。)
其次,您的脚本似乎部署了名称 以 这三个名称结尾的任何分支。这在实践中可能不是问题:谁要将他们的分支命名为 fred/master
?但如果有人这样做,您可能会做错事,除非您会 想要部署这样一个分支。最好检查引用名称是否字面上 refs/heads/master
、refs/heads/pre-production
、and/or refs/heads/production
.
最后一个……好吧,这个很复杂。请记住,Git 有一个名为 index 或 staging area 的东西(有时 cache ): 当你 git add
文件时,你正在将它们复制到索引中,而当你 git commit
、Git 拍摄索引内容的快照时,它成为新的提交。这里的含义之一,我认为 Git 文档强调得不够,是索引 always 包含 every file that will be在下一次提交中。 (在提交之后,索引是 而不是 "empty",因为几个命令和一些文档暗示:相反,它充满了你刚刚提交的所有内容。)
git checkout
命令利用了这个事实,可能会让您感到悲伤。
您在这些不同的结帐命令之前设置变量 GIT_WORK_TREE
,但您没有 设置 GIT_INDEX_FILE
。因此,Git 将使用 索引文件——一个单一的标准文件,每次 相同的 文件——用于这些不同的结帐中的每一个命令。
为什么索引文件对结帐很重要
Git 努力优化 结帐。其中一些只是为了速度,还有一些是因为 "switch branches" checkout 故意 不会 破坏工作树更改,让您将它们带到新分支以防万一是你想要的。 (有关详细信息,请参阅 Git - checkout another branch when there are uncommitted changes on the current branch。)无论哪种方式,Git 使用此技巧:
- 如果索引条目表明正确的文件已经存在,并且工作树中的文件不比索引时间戳更新,请不要触摸工作树文件。
也就是说,假设我们假装是 Git,并且我们正在做 git checkout $branch
(有或没有 -- Code/app
或其他任何东西:有和没有表现有些不同,但是对于下一个案例,我们可以使用一个测试)。我们将分支名称 $branch
转换为提交 ID,并将其转换为树 ID,然后开始查看树及其子树。我们找到一个名为 Code/app/xyz.rb
的文件。我们查看 index/cache,发现它有一个 Code/app/xyz.rb
.
的条目
索引条目显示“blob a123456...
安装为 Code/app/xyz.rb
,时间戳 1467505093
(2016 年 7 月 2 日星期六 17:18:13 PDT)。我们检查工作树文件 Code/app/xyz.rb
。通过 lstat
调用获得的时间戳是 1467500000
(大约在同一天的 3:33 下午,即早几个小时)。好吧,真奇怪,它回到了过去,但显然自从我们上次检查以来它没有改变——所以让我们把它留在原地吧!
但是等一下,我们最初是如何陷入这种情况的?嗯,大约两个小时前我们 运行 部署 pre-production
。我们(记住,"we" 这里是 us-being-Git-checkout)实际上 did copy Git blob a123456...
to work-当时树 Code/app/xyz.rb
。但是$GIT_WORK_TREE
当时是“/some/path/to/preprod/area”,现在是“/some/path/to/master/area”。
我们必须在比这两个更早的某个时间部署 master
,以便该文件存在于当前工作树中。但是当我们这样做时,master
的树说要使用 Git blob 01234567
而不是 a123456
。然后,两个小时前,我们被要求部署 pre-production
,并且那个特定的 Code/app/xyz.rb
已更新到版本 a123456
。现在我们被要求重新部署 master
并且它的 Code/app/xyz.rb
应该 也 是 a123456
,但是我们看到我们已经部署了版本 a123456
—它在索引中!—工作树版本从那时起就没有被编辑过,所以它一定没问题。
(不行,那是不同的工作树。但是我们不知道:索引不记录工作树的路径;我们是假设现在的 $GIT_WORK_TREE
与当时的 $GIT_WORK_TREE
相同。)
有几种方法可以在这里工作:
删除工作树,这样 Git 必须重新部署每个文件。
这种方法的优点是简单。它的缺点是 Git 复制出每个文件。当然,如果您要重命名工作树的子目录,无论如何 "just works" 可能是一个不错的方法。
删除索引(缓存),这样Git必须重新部署每个文件。
这和以前差不多。 Git 将重建索引。
每个工作树使用一个索引。
这是最复杂的方法,但应该是最高效的。此外,这是新的(自 Git 版本 2.5 起)git worktree add
命令所做的,git worktree add
将隐藏所有的复杂性。如果您的 Git 足够新,您可以用新的工作树功能替换这个有点复杂的部署脚本。
只做底层操作,不用更新(甚至检查)HEAD
:
git read-tree -um `git write-tree` master:code/app
绕过方便的命令,直接做你想做的事。您没有签出开发分支,您正在签出的内容没有提交。 git write-tree
现在为索引中列出的内容吐出树,master:code/app
在 master
提交中命名该树,双树 git read-tree -um
在索引和工作树中进行转换。
如果您要维护多个工作树,请通过将 GIT_INDEX_FILE
导出为“$GIT_DIR/index
”以外的内容来为每个工作树维护一个索引。
@torek,@jthill 我从你的帖子中学到了很多东西。里面有很棒的想法。但是,我选择 PM2 which does this fairly well from a client machine. I've also found shipit 作为我问题中说明的问题的另一种选择。
我正在尝试检出特定目录 "code/app" 中的所有文件和子目录,但是 git 给了我 code/app 目录及其内容。我只想要它的内容。我想在 post-receive git 挂钩中使用它。
git checkout -f master -- code/app
我也试了下面的方法都没用
git checkout -f master -- code/app/*
git checkout -f master -- code/app/.
如何获得上述预期的行为?
更新:
post-接收
#!/usr/bin/env ruby
# post-receive
# Read STDIN
from, to, branch = ARGF.read.split " "
if (branch =~ /master$/)
puts "Received branch #{branch}, deploying."
# Copy files to deploy directory
deploy_to_dir = File.expand_path('../development')
dir_to_checkout = '06\ -\ Code/app/'
`GIT_WORK_TREE="#{deploy_to_dir}" git checkout -f master -- #{dir_to_checkout}`
puts "DEPLOY: master(#{to}) copied to '#{deploy_to_dir}'"
exit
end
# Only deploy if pre-production branch was pushed
if (branch =~ /pre-production$/)
puts "Received branch #{branch}, deploying."
# Copy files to deploy directory
deploy_to_dir = File.expand_path('../staging')
dir_to_checkout = '06\ -\ Code/app/'
`GIT_WORK_TREE="#{deploy_to_dir}" git checkout -f pre-production -- #{dir_to_checkout}`
puts "DEPLOY: pre-production(#{to}) copied to '#{deploy_to_dir}'"
exit
end
if (branch =~ /production$/)
puts "Received branch #{branch}, deploying."
# Copy files to deploy directory
deploy_to_dir = File.expand_path('../')
dir_to_checkout = '06\ -\ Code/app/'
`GIT_WORK_TREE="#{deploy_to_dir}" git checkout -f production -- #{dir_to_checkout}`
puts "DEPLOY: production(#{to}) copied to '#{deploy_to_dir}'"
exit
end
我不是 Ruby 专家,但幸运的是,这似乎并不重要:我可以阅读你的代码应该做什么(虽然没有说它是否有任何 Ruby - 具体问题)。
如果我可以稍微改一下你的问题,你是说:
The
git checkout
command mostly does what I want:git checkout -f <branch> -- <dir-path>
e.g.
git checkout -f master -- Code/app/
extracts all the files that are named
Code/app/<em>somedir/somefile</em>
. The part I don't like is that it puts them in files namedCode/app/<em>somedir/somefile</em>
when I want them to just be in<em>somedir/somefile</em>
.
有 种重命名文件的方法,但其中 none 方法简单易行,至少与仅进行结帐然后重命名文件相比.因此——进行结帐,然后重命名文件——可能是您应该做的。
这里还有一些更微妙的问题。
首先,这种特殊形式的 git checkout
只是 添加或替换工作树中的 文件。假设在您的部署分支之一(master
、pre-production
和 production
)中,您 删除了 一些不需要的文件。部署脚本将 添加或更新 任何添加或更新的文件,但不会 删除 文件,因此删除的文件不会从您的部署区域中删除.
(您可能 想要 某些文件的这种行为。为 "just the right files" 实现它可能很棘手。)
其次,您的脚本似乎部署了名称 以 这三个名称结尾的任何分支。这在实践中可能不是问题:谁要将他们的分支命名为 fred/master
?但如果有人这样做,您可能会做错事,除非您会 想要部署这样一个分支。最好检查引用名称是否字面上 refs/heads/master
、refs/heads/pre-production
、and/or refs/heads/production
.
最后一个……好吧,这个很复杂。请记住,Git 有一个名为 index 或 staging area 的东西(有时 cache ): 当你 git add
文件时,你正在将它们复制到索引中,而当你 git commit
、Git 拍摄索引内容的快照时,它成为新的提交。这里的含义之一,我认为 Git 文档强调得不够,是索引 always 包含 every file that will be在下一次提交中。 (在提交之后,索引是 而不是 "empty",因为几个命令和一些文档暗示:相反,它充满了你刚刚提交的所有内容。)
git checkout
命令利用了这个事实,可能会让您感到悲伤。
您在这些不同的结帐命令之前设置变量 GIT_WORK_TREE
,但您没有 设置 GIT_INDEX_FILE
。因此,Git 将使用 索引文件——一个单一的标准文件,每次 相同的 文件——用于这些不同的结帐中的每一个命令。
为什么索引文件对结帐很重要
Git 努力优化 结帐。其中一些只是为了速度,还有一些是因为 "switch branches" checkout 故意 不会 破坏工作树更改,让您将它们带到新分支以防万一是你想要的。 (有关详细信息,请参阅 Git - checkout another branch when there are uncommitted changes on the current branch。)无论哪种方式,Git 使用此技巧:
- 如果索引条目表明正确的文件已经存在,并且工作树中的文件不比索引时间戳更新,请不要触摸工作树文件。
也就是说,假设我们假装是 Git,并且我们正在做 git checkout $branch
(有或没有 -- Code/app
或其他任何东西:有和没有表现有些不同,但是对于下一个案例,我们可以使用一个测试)。我们将分支名称 $branch
转换为提交 ID,并将其转换为树 ID,然后开始查看树及其子树。我们找到一个名为 Code/app/xyz.rb
的文件。我们查看 index/cache,发现它有一个 Code/app/xyz.rb
.
索引条目显示“blob a123456...
安装为 Code/app/xyz.rb
,时间戳 1467505093
(2016 年 7 月 2 日星期六 17:18:13 PDT)。我们检查工作树文件 Code/app/xyz.rb
。通过 lstat
调用获得的时间戳是 1467500000
(大约在同一天的 3:33 下午,即早几个小时)。好吧,真奇怪,它回到了过去,但显然自从我们上次检查以来它没有改变——所以让我们把它留在原地吧!
但是等一下,我们最初是如何陷入这种情况的?嗯,大约两个小时前我们 运行 部署 pre-production
。我们(记住,"we" 这里是 us-being-Git-checkout)实际上 did copy Git blob a123456...
to work-当时树 Code/app/xyz.rb
。但是$GIT_WORK_TREE
当时是“/some/path/to/preprod/area”,现在是“/some/path/to/master/area”。
我们必须在比这两个更早的某个时间部署 master
,以便该文件存在于当前工作树中。但是当我们这样做时,master
的树说要使用 Git blob 01234567
而不是 a123456
。然后,两个小时前,我们被要求部署 pre-production
,并且那个特定的 Code/app/xyz.rb
已更新到版本 a123456
。现在我们被要求重新部署 master
并且它的 Code/app/xyz.rb
应该 也 是 a123456
,但是我们看到我们已经部署了版本 a123456
—它在索引中!—工作树版本从那时起就没有被编辑过,所以它一定没问题。
(不行,那是不同的工作树。但是我们不知道:索引不记录工作树的路径;我们是假设现在的 $GIT_WORK_TREE
与当时的 $GIT_WORK_TREE
相同。)
有几种方法可以在这里工作:
删除工作树,这样 Git 必须重新部署每个文件。
这种方法的优点是简单。它的缺点是 Git 复制出每个文件。当然,如果您要重命名工作树的子目录,无论如何 "just works" 可能是一个不错的方法。
删除索引(缓存),这样Git必须重新部署每个文件。
这和以前差不多。 Git 将重建索引。
每个工作树使用一个索引。
这是最复杂的方法,但应该是最高效的。此外,这是新的(自 Git 版本 2.5 起)
git worktree add
命令所做的,git worktree add
将隐藏所有的复杂性。如果您的 Git 足够新,您可以用新的工作树功能替换这个有点复杂的部署脚本。
只做底层操作,不用更新(甚至检查)HEAD
:
git read-tree -um `git write-tree` master:code/app
绕过方便的命令,直接做你想做的事。您没有签出开发分支,您正在签出的内容没有提交。 git write-tree
现在为索引中列出的内容吐出树,master:code/app
在 master
提交中命名该树,双树 git read-tree -um
在索引和工作树中进行转换。
如果您要维护多个工作树,请通过将 GIT_INDEX_FILE
导出为“$GIT_DIR/index
”以外的内容来为每个工作树维护一个索引。
@torek,@jthill 我从你的帖子中学到了很多东西。里面有很棒的想法。但是,我选择 PM2 which does this fairly well from a client machine. I've also found shipit 作为我问题中说明的问题的另一种选择。