检出子目录中的所有文件

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 只是 添加或替换工作树中的 文件。假设在您的部署分支之一(masterpre-productionproduction)中,您 删除了 一些不需要的文件。部署脚本将 添加或更新 任何添加或更新的文件,但不会 删除 文件,因此删除的文件不会从您的部署区域中删除.

(您可能 想要 某些文件的这种行为。为 "just the right files" 实现它可能很棘手。)

其次,您的脚本似乎部署了名称 这三个名称结尾的任何分支。这在实践中可能不是问题:谁要将他们的分支命名为 fred/master?但如果有人这样做,您可能会做错事,除非您 想要部署这样一个分支。最好检查引用名称是否字面上 refs/heads/masterrefs/heads/pre-production、and/or refs/heads/production.

最后一个……好吧,这个很复杂。请记住,Git 有一个名为 indexstaging 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/appmaster 提交中命名该树,双树 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 作为我问题中说明的问题的另一种选择。