如何将空目录添加到Git存储库?

How can I add an empty directory to a Git repository?

如何将空目录(不包含文件)添加到Git存储库?


另一种使目录保持(几乎)空白(在存储库中)的方法是在包含这四行的目录中创建一个.gitignore文件:

1
2
3
4
# Ignore everything in this directory
*
# Except this file
!.gitignore

那么,您不必像处理M104的解决方案那样正确地获得订单。

这还提供了这样的好处:当您执行git状态时,该目录中的文件不会显示为"未跟踪"。

使@greenasjade的评论坚持不懈:

I think it's worth noting that this solution does precisely what the question asked for, but is not perhaps what many people looking at this question will have been looking for. This solution guarantees that the directory remains empty. It says"I truly never want files checked in here". As opposed to"I don't have any files to check in here, yet, but I need the directory here, files may be coming later".


不能。请参阅Git常见问题解答。

Currently the design of the git index
(staging area) only permits files to
be listed, and nobody competent enough
to make the change to allow empty
directories has cared enough about
this situation to remedy it.

Directories are added automatically
when adding files inside them. That
is, directories never have to be added
to the repository, and are not tracked
on their own.

You can say"git add

" and it
will add files in there.

If you really need a directory to
exist in checkouts you should create a
file in it. .gitignore works well for
this purpose; you can leave it empty,
or fill in the names of files you
expect to show up in the directory.


在目录中创建一个名为.gitkeep的空文件,并添加该文件。


您可以将一个自述文件放在目录中,并解释为什么要将这个目录(否则为空)放在存储库中。


1
touch .keep

在Linux上,这将创建一个名为.keep的空文件。这个名字比.gitkeep更受欢迎,因为前者对git不可知,后者对git是特定的。其次,正如另一位用户所指出的,应该为Git本身使用的文件和目录保留.git前缀约定。

或者,如另一个答案所述,目录可以包含描述性的READMEREADME.md文件。

当然,这要求文件的存在不会导致应用程序中断。


为什么我们需要空的版本化文件夹

首先要做的是:

An empty directory cannot be part of a tree under the Git versioning system.

它根本不会被跟踪。但有些场景中"版本控制"空目录可能有意义,例如:

  • 搭建一个预定义的文件夹结构,使其可供存储库的每个用户/贡献者使用;或者,作为上述的一种特殊情况,为临时文件(如cache/logs/目录)创建一个文件夹,我们希望在其中提供文件夹,但.gitignore其内容
  • 与上述相关,有些项目在没有文件夹的情况下无法工作(这通常是一个设计糟糕的项目的提示,但这是一个经常发生的现实场景,可能会出现,例如,需要解决的权限问题)。

一些建议的解决方法

许多用户建议:

  • 放置一个README文件或另一个包含某些内容的文件,以使目录不为空,或
  • 创建一个带有"逆向逻辑"(即包括所有文件)的.gitignore文件,其目的与方法1相同。
  • 虽然这两个解决方案都可以工作,但我发现它们与Git版本控制的有意义的方法是不一致的。

    • 为什么你要把虚假的文件或自述文件放在你的项目中?
    • 为什么用.gitignore来做一件事情(保存文件),这与它的目的(不包括文件)正好相反,即使它是可能的?

    .gitkeep方法

    使用一个名为.gitkeep的空文件,以强制在版本控制系统中存在该文件夹。

    虽然这似乎没有什么大的区别:

    • 您使用的文件具有保存文件夹的单一目的。你不想把任何你不想放的信息放在那里。

      例如,您应该使用自述文件以及包含有用信息的自述文件,而不是作为保留文件夹的借口。

      分离关注点总是一件好事,您仍然可以添加一个.gitignore来忽略不需要的文件。

    • 命名它.gitkeep使它从文件名本身(以及对其他开发人员,这对于共享项目和Git存储库的核心目的之一很好)非常清楚和简单,该文件是

      • 与代码无关的文件(因为前导点和名称)
      • 一个与git明显相关的文件
      • 它的目的(keep)在其意义上是明确的、一致的和语义上相反的,可以忽略。

    采用

    我看到过非常重要的框架(如laravel、angular cli)采用的.gitkeep方法。


    如其他答案所述,Git无法在其临时区域中表示空目录。(参见Git常见问题解答)但是,如果出于您的目的,一个目录足够空,如果它只包含一个.gitignore文件,那么您只能通过以下方式在空目录中创建.gitignore文件:

    1
    find . -type d -empty -exec touch {}/.gitignore \;


    安迪·莱斯特是对的,但是如果你的目录只是空的,而不是空的,你可以把一个空的.gitignore文件放在里面作为解决方法。

    另外,这是一个实现问题,而不是一个基本的Git存储设计问题。正如在Git邮件列表中多次提到的,这一点没有实现的原因是没有人足够关心提交补丁,而不是不能或不应该这样做。


    Ruby on Rails日志文件夹创建方式:

    1
    mkdir log && touch log/.gitkeep && git add log/.gitkeep

    现在日志目录将包含在树中。它在部署时非常有用,因此不必编写生成日志目录的例程。

    日志文件可以通过发出

    1
    echo log/dev.log >> .gitignore

    但你可能知道。


    Git不跟踪空目录。有关更多说明,请参阅Git常见问题解答。建议的解决方法是将.gitignore文件放在空目录中。我不喜欢这个解决方案,因为UNIX约定"隐藏"了.gitignore。也没有解释为什么目录是空的。

    我建议在空目录中放置一个自述文件,解释为什么目录是空的,以及为什么需要在Git中跟踪它。在准备好自述文件后,就Git而言,目录不再是空的。

    真正的问题是,为什么您需要git中的空目录?通常,您有一些构建脚本可以在编译/运行之前创建空目录。如果没有,那就做一个。这比在Git中放置空目录要好得多。

    所以你有一些原因,为什么你需要在git中有一个空目录。把这个原因放到自述文件中。这样其他开发人员(以及将来的您)就知道为什么需要空目录。您还将知道,当需要空目录的问题得到解决时,可以删除空目录。

    要列出每个空目录,请使用以下命令:

    1
    find -name .git -prune -o -type d -empty -print

    要在每个空目录中创建占位符自述文件,请执行以下操作:

    1
    2
    find -name .git -prune -o -type d -empty -exec sh -c \
     "echo this directory needs to be empty because reasons > {}/README.emptydir" \;

    要忽略目录中除自述文件以外的所有内容,请在.gitignore中放置以下行:

    1
    2
    3
    4
    path/to/emptydir/*
    !path/to/emptydir/README.emptydir
    path/to/otheremptydir/*
    !path/to/otheremptydir/README.emptydir

    或者,您可以将每个自述文件排除在外,不被忽略:

    1
    2
    3
    path/to/emptydir/*
    path/to/otheremptydir/*
    !README.emptydir

    要列出已创建的每个自述文件,请执行以下操作:

    1
    find -name README.emptydir

    警告:这个调整并不是真正有效的结果。抱歉给您带来不便。

    以下原始邮件:

    我在玩Git内部时找到了一个解决方案!

  • 假设您在存储库中。
  • 创建空目录:

    1
    $ mkdir path/to/empty-folder
  • 使用管道命令和空树sha-1将其添加到索引中:

    1
    2
    $ git update-index --index-info
    040000 tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904    path/to/empty-folder

    键入命令,然后输入第二行。按enter,然后按ctrl+d终止输入。注:格式为模式[空格]类型[空格]sha-1hash[制表符]路径(制表符很重要,答案格式不保留)。

  • 就是这样!空文件夹在索引中。你所要做的就是承诺。

  • 这个解决方案很短,显然很好用(见编辑!)但这不是那么容易记住的…

    空树sha-1可以通过在其中创建一个新的空git存储库,cd并发出git write-tree来找到,后者输出空树sha-1。

    编辑:

    我找到这个解决方案后就一直在使用它。它的工作方式似乎与创建子模块完全相同,只是在任何地方都没有定义模块。这会导致发布git submodule init|update时出错。问题是,git update-index040000 tree部分重写为160000 commit

    此外,放置在该路径下的任何文件都不会被Git注意到,因为它认为它们属于其他存储库。这很讨厌,因为它很容易被忽视!

    但是,如果您还没有(并且不会)使用存储库中的任何Git子模块,"空"文件夹将保持为空,或者如果您希望Git知道它的存在并忽略其内容,则可以进行此调整。按照通常的方式使用子模块需要更多的步骤来进行调整。


    假设您需要一个名为tmp的空目录:

    1
    2
    3
    4
    5
    $ mkdir tmp
    $ touch tmp/.gitignore
    $ git add tmp
    $ echo '*' > tmp/.gitignore
    $ git commit -m 'Empty directory' tmp

    换句话说,您需要先将.git ignore文件添加到索引中,然后才能告诉git忽略它(以及空目录中的其他所有内容)。


    可能添加一个空目录看起来是阻力最小的路径,因为您有希望该目录存在的脚本(可能是因为它是生成二进制文件的目标)。另一种方法是根据需要修改脚本以创建目录。

    1
    2
    mkdir --parents .generated/bin ## create a folder for storing generated binaries
    mv myprogram1 myprogram2 .generated/bin ## populate the directory as needed

    在本例中,您可以将一个(断开的)符号链接签入到目录中,这样您就可以在不使用".generated"前缀的情况下访问它(但这是可选的)。

    1
    2
    ln -sf .generated/bin bin
    git add bin

    当您想清理源代码树时,您只需:

    1
    rm -rf .generated ## this should be in a"clean" script or in a makefile

    如果采用通常建议的方法签入一个几乎为空的文件夹,则删除内容的复杂性很小,而不同时删除".gitignore"文件。

    通过向根目录中添加以下内容,可以忽略所有生成的文件。gitignore:

    1
    .generated


    我也面临着空目录的问题。使用占位符文件的问题是,如果不再需要它们,则需要创建它们并删除它们(因为后来添加了子目录或文件)。使用大型的源代码树管理这些占位符文件可能很麻烦,而且容易出错。

    这就是为什么我决定编写一个开源工具来自动管理这些占位符文件的创建/删除。它是为.NET平台编写的,运行在Mono(.NET for Linux)和Windows下。

    看看:http://code.google.com/p/markemptydirs


    你做不到,不幸的是永远也做不到。这是莱纳斯·托瓦尔德自己做的决定。他知道什么对我们有好处。

    在我读过一次的某个地方,有一句咆哮。

    我发现了re:空目录..,但可能还有另一个。

    你必须和解决方法一起生活…不幸的是。


    我喜欢@artur79和@mjs给出的答案,所以我一直在使用两者的组合,并将其作为我们项目的标准。

    1
    find . -type d -empty -exec touch {}/.gitkeep \;

    但是,只有少数开发人员在Mac或Linux上工作。在Windows上做了很多工作,我找不到一个等效的简单的一行程序来完成相同的工作。有些人很幸运,因为其他原因安装了Cygwin,但仅仅为这个开了Cygwin的处方就显得杀伤力太大了。

    编辑以获得更好的解决方案

    因此,由于我们的大多数开发人员已经安装了Ant,我想到的第一件事就是将一个Ant构建文件组合起来,独立于平台完成这一任务。这里仍然可以找到这个

    但是,我后来认为最好将其制作成一个小的实用程序命令,所以我使用Python重新创建了它,并将其发布到了pypi中。只需运行以下命令即可安装:

    1
    pip3 install gitkeep2

    它允许您递归地创建和删除.gitkeep文件,还允许您向它们添加消息,以便您的对等方了解这些目录为什么重要。最后一点是奖励。我想如果.gitkeep文件能够自我记录就好了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    $ gitkeep --help
    Usage: gitkeep [OPTIONS] PATH

      Add a .gitkeep file to a directory in order to push them into a Git repo
      even if they're empty.

      Read more about why this is necessary at: https://git.wiki.kernel.org/inde
      x.php/Git_FAQ#Can_I_add_empty_directories.3F

    Options:
      -r, --recursive     Add or remove the .gitkeep files recursively for all
                          sub-directories in the specified path.
      -l, --let-go        Remove the .gitkeep files from the specified path.
      -e, --empty         Create empty .gitkeep files. This will ignore any
                          message provided
      -m, --message TEXT  A message to be included in the .gitkeep file, ideally
                          used to explain why it's important to push the specified
                          directory to source control even if it's empty.
      -v, --verbose       Print out everything.
      --help              Show this message and exit.

    希望你觉得它有用。


    当你添加一个.gitignore文件时,如果你想在其中放置任何数量的内容(你想让git忽略),你可能想添加一行只有星号*的内容,以确保你不会意外添加被忽略的内容。


    杰米·弗洛诺的解决方案非常有效。这里有一个稍微增强的版本来保持.htaccess

    1
    2
    3
    4
    5
    # Ignore everything in this directory
    *
    # Except this file
    !.gitignore
    !.htaccess

    使用此解决方案,您可以提交一个空文件夹,例如/log/tmp/cache,文件夹将保持为空。


    如前所述,不可能添加空目录,但这里有一个一行程序向所有目录添加空的.gitignore文件。

    ruby -e 'require"fileutils" ; Dir.glob(["target_directory","target_directory/**"]).each { |f| FileUtils.touch(File.join(f,".gitignore")) if File.directory?(f) }'

    为了方便取用,我把它塞进了一个耙子里。


    这是一个黑客,但它的工作很有趣(Git2.2.1)。类似于@teka的建议,但更容易记住:

    • 向任何存储库添加子模块(git submodule add path_to_repo)
    • 这将添加一个文件夹和一个文件.submodules。做出改变。
    • 删除.submodules文件并提交更改。

    现在,您有了一个在签出提交时创建的目录。不过,有趣的是,如果您查看此文件的树对象的内容,您将得到:

    fatal: Not a valid object name
    b64338b90b4209263b50244d18278c0999867193

    但我不鼓励使用它,因为它可能会在将来的Git版本中停止工作。这可能会导致存储库损坏。


    没有办法让Git跟踪目录,所以唯一的解决方案是在您希望Git跟踪的目录中添加一个占位符文件。

    文件可以命名并包含您想要的任何内容,但大多数人使用名为.gitkeep的空文件(尽管有些人更喜欢VCS不可知的.keep)。

    前缀.将其标记为隐藏文件。

    另一个想法是添加一个README文件,解释该目录将用于什么目的。


    我总是构建一个函数来检查我想要的文件夹结构,并在项目中为我构建它。这解决了这个问题,因为空文件夹由代理保存在Git中。

    1
    2
    3
    4
    5
    6
    7
    8
    function check_page_custom_folder_structure () {
        if (!is_dir(TEMPLATEPATH."/page-customs"))
            mkdir(TEMPLATEPATH."/page-customs");    
        if (!is_dir(TEMPLATEPATH."/page-customs/css"))
            mkdir(TEMPLATEPATH."/page-customs/css");
        if (!is_dir(TEMPLATEPATH."/page-customs/js"))
            mkdir(TEMPLATEPATH."/page-customs/js");
    }

    这是在PHP中,但我确信大多数语言都支持相同的功能,并且由于应用程序负责创建文件夹,所以文件夹将始终存在。


    你不能。这是Git维护人员的故意设计决定。基本上,像git这样的源代码管理系统的目的是管理源代码,空目录不是源代码。Git也经常被描述为内容跟踪器,同样,空目录不是内容(实际上恰恰相反),因此它们不被跟踪。


    如果要添加一个文件夹,该文件夹将在多个语义目录中存储大量临时数据,那么一种方法是将类似这样的内容添加到根目录中。

    /app/data/**/*.*
    !/app/data/**/*.md

    然后,您可以提交描述性readme.md文件(或空白文件,无所谓,只要您可以像在本例中使用*.md)那样在每个目录中唯一地针对它们,以确保目录都是repo的一部分,但文件(带扩展名)被忽略。限制:目录名中不允许使用.

    您可以使用XML/images文件或其他文件填充所有这些目录,并随着应用程序开发所需的存储空间的增加,在/app/data/下添加更多目录(其中readme.md文件用于详细描述每个存储目录的用途)。

    不需要通过为每个新目录创建一个新的.gitignore来进一步更改您的.gitignore或分散。也许不是最聪明的解决方案,但很简洁,忽略智慧,总是为我工作。又好又简单!;)

    enter image description here


    有时您必须处理写得不好的库或软件,它们需要一个"真正的"空目录和现有目录。放一个简单的.gitignore.keep可能会破坏它们并导致错误。在这些情况下,以下内容可能会有所帮助,但不能保证……

    首先创建所需目录:

    1
    mkdir empty

    然后您向这个目录添加一个中断的符号链接(但是在上面描述的用例以外的任何其他情况下,请使用一个README和一个解释):

    1
    ln -s .this.directory empty/.keep

    要忽略此目录中的文件,可以将其添加到根目录.gitignore中:

    1
    echo"/empty">> .gitignore

    要添加被忽略的文件,请使用参数强制它:

    1
    git add -f empty/.keep

    提交之后,索引中有一个中断的符号链接,Git创建了目录。断开的链接有一些优点,因为它不是常规文件,并且指向没有常规文件。因此,它甚至符合问题的一部分(不包含任何文件),不是出于意图,而是出于意义,我猜:

    1
    find empty -type f

    此命令显示空结果,因为此目录中不存在任何文件。因此,大多数应用程序(在一个目录中获取所有文件)通常看不到这个链接,至少在它们执行"文件存在"或"可读"操作时。即使某些脚本在那里也找不到任何文件:

    1
    2
    3
    4
    5
    $ php -r"var_export(glob('empty/.*'));"
    array (
      0 => 'empty/.',
      1 => 'empty/..',
    )

    但我强烈建议仅在特殊情况下使用此解决方案,在空目录中编写良好的README通常是更好的解决方案。(我不知道这是否适用于Windows文件系统…)


    再增加一个选项。

    假设您想在git中添加一个目录,在与git相关的所有目的中,该目录都应该保持为空,并且永远不会跟踪其内容,那么这里多次建议的.gitignore就可以做到这一点。

    如前所述,格式为:

    1
    2
    *
    !.gitignore

    现在,如果您想在命令行中一下子完成此操作,而在要添加的目录中,您可以执行:

    1
    $ echo"*"> .gitignore && echo '!.gitignore' >> .gitignore && git add .gitignore

    我自己有一个shell脚本,我用它来做这个。将脚本命名为您想说的任何内容,然后将其添加到include路径的某个位置,或者直接引用它:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #!/bin/bash

    dir=''

    if ["$1" !="" ]; then
        dir="$1/"
    fi

    echo"*"> $dir.gitignore && \
    echo '!.gitignore' >> $dir.gitignore && \
    git add $dir.gitignore

    有了这个,您可以从您想要添加的目录中执行它,或者引用目录作为它的第一个也是唯一的参数:

    1
    $ ignore_dir ./some/directory

    另一个选项(响应@greenasjade的评论),如果您希望跟踪一个空文件夹,该文件夹将来可能包含被跟踪的文件,但现在是空的,您可以从.gitignore文件中操作*,并将其签入。基本上,所有的文件都是说"不要忽略我",否则,目录是空的并被跟踪。

    您的.gitignore文件如下:

    1
    !.gitignore

    就是这样,检查一下,你有一个空的,但仍被跟踪的目录,你可以在以后的某个时候跟踪文件。

    我建议在文件中保留一行的原因是它给出了.gitignore的目的。否则,可能会有人想把它去掉。如果在行上方放置注释,可能会有所帮助。


    有时,我的存储库中的文件夹将只包含被认为是"内容"的文件,也就是说,它们不是我关心的被版本控制的文件,因此不应该提交。使用git的.git ignore文件,可以忽略整个目录。但有时将文件夹放在回购协议中是有益的。这里有一个很好的解决方案来满足这个需求。

    我过去所做的是将.gitignore文件放在repo的根目录下,然后排除文件夹,如下所示:

    1
    2
    /app/some-folder-to-exclude
    /another-folder-to-exclude/*

    但是,这些文件夹不会成为回购的一部分。您可以在其中添加类似于自述文件的内容。但是您必须告诉应用程序不要担心处理任何自述文件。

    如果您的应用程序依赖于存在的文件夹(尽管是空的),您可以简单地将.gitignore文件添加到相关文件夹中,并使用它来实现两个目标:

    告诉Git文件夹中有一个文件,它使Git将其添加到repo中。告诉Git忽略这个文件夹的内容,减去这个文件本身。以下是要放入空目录中的.gitignore文件:

    1
    2
    *
    !.gitignore

    第一行(*)告诉Git忽略这个目录中的所有内容。第二行告诉Git不要忽略.git ignore文件。您可以将此文件填充到要添加到存储库的每个空文件夹中。


    您可以将此代码保存为create_readme.php,并从Git项目的根目录运行php代码。

    1
    > php create_readme.php

    它将向所有空目录添加自述文件,以便这些目录随后被添加到索引中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <?php
        $path = realpath('.');
        $objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path),       RecursiveIteratorIterator::SELF_FIRST);
        foreach($objects as $name => $object){
            if ( is_dir($name) && ! is_empty_folder($name) ){
                echo"$name
    " ;
                exec("touch".$name."/"."README");
            }
        }

        function is_empty_folder($folder) {
            $files = opendir($folder);
            while ($file = readdir($files)) {
                if ($file != '.' && $file != '..')
                    return true; // Not empty
                }
            }
    ?>

    然后做

    1
    2
    git commit -m"message"
    git push