Don't create .gitkeep files, use .gitignore instead (2023)

adamj.eu

92 points

frou_dh

a day ago


62 comments

jkubicek 18 hours ago

I'm not sure if I'm the one to blame for this or not, but the earliest reference to ".gitkeep" I can find online is my 2010 answer on Stack Overflow: https://stackoverflow.com/a/4250082/28422

If this is all my fault, I'm sorry.

  • hn92726819 5 hours ago

    Yeah... I don't think you were wrong. Having 100 tiny gitignores makes finding out why something is excluded annoying. Our policy is one root level gitgnore and gitkeeps where required.

    Some devs will just open the first gitignore they see and throw stuff into it. No thank you.

  • selridge 18 hours ago

    This is delightful. Accidental load-bearing SO post.

    • jkubicek 17 hours ago

      It's especially funny since my answer is wrong anyway! The other top answer is much better. I did get a lot of early SO brownie points from that one answer though.

      • juggerl6 16 hours ago
        2 more

        Thankfully AI has put an end to the scourge of confidently-wrong SO hallucinations.

        • adastra22 15 hours ago

          Well, Claude is here making .gitkeep files like nobody's business.

Arrowmaster 18 hours ago

The author makes a very common mistake of not reading the very first line of the documentation for .gitignore.

  A gitignore file specifies intentionally untracked files that Git should ignore. Files already tracked by Git are not affected; see the NOTES below for details.
You should never be putting "!.gitignore" in .gitignore. Just do `echo "*" > .gitignore; git add -f .gitignore`. Once a file is tracked any changes to it will be tracked without needing to use --force with git add.
  • BlackFly 13 hours ago

    The point of that line is to robustly survive a rename of the directory which won't be automatically tracked without that line. You have to read between the lines to see this: they complain about this problem with .gitkeep files.

  • AgentME 18 hours ago

    If you have a project template or a tool that otherwise sets up a project but leaves it in the user's hands to create a git repo for it or commit the project into an existing repo, then it would be better for it to create a self-excepting .gitignore file than to have to instruct the user on special git commands to use later.

  • ekipan 18 hours ago

    Yeah, this. Plus a mistake from the article:

      $ echo '*\n!.gitignore' > build/.gitignore
    
    The \n won't be interpreted specially by echo unless it gets the -e option.

    Personally if I need a build directory I just have it mkdir itself in my Makefile and rm -rf it in `make clean`. With the article's scheme this would cause `git status` noise that a `/build/` line in a root .gitignore wouldn't. I'm not really sure there's a good tradeoff there.

    • Aaron2222 13 hours ago

      > The \n won't be interpreted specially by echo unless it gets the -e option.

      Author's probably using Zsh, which interprets them by default.

  • xg15 8 hours ago

    I think I'd prefer to have all ignores and un-ignores explicitly in the file and not have some of them defined implicitly because a file was added to tracking at some point.

  • smrq 16 hours ago

    Why is this approach better than the author's?

  • nebezb 14 hours ago

    This is functionally the same. What do you mean by “you should never”? According to who?

    What an arrogant take. This is preference. Don’t mistake it for correctness.

GreenDolphinSys 16 hours ago

.gitkeep is intuitive and easy to understand. Unignoring a .gitignore is not intuitive. This falls squarely into "clever optimization tricks that obscure intent and readability". Don't do things like this.

It's not that hard to update a .gitignore file every now and then.

  • rswail 6 hours ago

    Then put a comment in the .gitignore.

    Using the actual tools built in to git directly removes steps in the process, which is always a good thing, it's documented as part of the git documentation, so you don't have to create a wiki page explaining why there is a ".gitkeep" file that git doesn't recognize itself.

    Saying "It's not that hard..." is fine for projects with a few contributors but does not scale.

cortesoft 19 hours ago

Not sure why you can’t just have your build script create the build directory?

  • twic 9 minutes ago

    Usually, you can. But occasionally you get mildly defective tools that require some directory to exist, even though it's empty. It's easier to add a gitkeep than fix them.

  • xg15 7 hours ago

    There may be other directories. I think it's useful to be able to see the entire directory structure of a repo when you check it out, and not just after running some scripts.

  • andybak 19 hours ago

    Because you might not have a build script?

    • cortesoft 15 hours ago

      Then how is anything ending up in the build directory?

    • drdec 17 hours ago

      Then why do you need a build directory?

      • himata4113 17 hours ago
        3 more

        qemu: mkdir build; cd build; ../configure, some projects are like that

        • xigoi 11 hours ago
          2 more

          Why can’t the configure script do this?

          • hn92726819 5 hours ago

            You can. But this makes intent clear. If you clone a git repo and see build/ with only a gitkeep, you are safe to bet your life savings on that being the compiled assets dir.

beej71 16 hours ago

What am I missing about this use case? It seems like you should just create `build/.gitignore` with `*` in it and `add -f` it and be done.

I'd use `.gitkeep` (or an empty `.gitignore`) if I needed to commit an otherwise-empty hierarchy. But if I'm going to have a `.gitignore` in there anyway, it's not empty.

> The directory is now “tracked” with a single, standard file that will work even after renames.

Does `.gitkeep` not work after renames? Or `.gitignore`?

So I am missing something. :)

  • aezart 4 hours ago

    It makes the behavior more obvious from simply looking at the file, for one thing, and it means you can just lump it into your next `git add -A` without needing to handle it specially.

  • KPGv2 15 hours ago

    That's a hack. What you should do is a .gitignore with * and then a whitelist of paths like src/**/*.

    If you rely on `add -f` you will forget to commit something important.

    For example, for a tree sitter grammar I developed a couple years ago, here is my .gitignore:

    ```

    # Ignore everything

    *

    # Top-level whitelist

    CHANGELOG.md

    # Allow git to see inside subdirectories

    !*/

    # Whitelist the grammar and tests

    !/grammar/*.js

    !/test/corpus/*.txt

    # Whitelist any grammar and tests in subdirectories

    !/grammar/**/*.js

    !/test/corpus/**/*.txt

    ```*

    • beej71 12 hours ago

      > If you rely on `add -f` you will forget to commit something important.

      But isn't the idea in TFA to blacklist the entire `build/` tree? We don't want to add anything there.

Kuraj 17 hours ago

If you need to do this, I think .gitkeep communicates intent better. You don't need to document it or risk it being removed as thought to be a left over.

prmoustache 7 hours ago

How about fixing your build scripts and makefiles instead? Convoluted solutions for a non-existing problem.

OptionOfT 16 hours ago

For me, I put them in directories that have to be there, because the underlying code doesn't create the directory, and without it, it fails.

Another example is where you want an empty directory mounted in Docker. If the directory is not there it is created with root permissions and then I can't even look into it.

yjftsjthsd-h 19 hours ago

I'm confused. Having a file gitignored doesn't stop you from committing it; AFAIK you can just

  touch build/.gitkeep
  git add build/.gitkeep
  git commit build/.gitkeep
And that's it? There's no need to exclude anything.
  • williadc 19 hours ago

    The idea is that you don't want to check-in any builds.

    • yjftsjthsd-h 19 hours ago

      Sure, so gitignore build/ or whatever. But you don't need to unignore .gitkeep

      • akerl_ 18 hours ago
        6 more

        The idea is that instead of adding a nonsense file, you use the native .gitignore functionality.

        ".gitkeep" is just a human thing; it would work the same if you called it ".blahblah".

        So their pitch is that if you want to explicitly keep the existence of the directory as a committed part of the repo, you're better off using the actual .gitignore functionality to check in the .gitignore file but ignore anything else in the directory.

        I don't find it amazingly compelling; .gitkeep isn't breaking anything.

jiffygist 11 hours ago

I don't understand why would you ever want to have an empty directory. Besides if I see a directory named "build" I expect to be able to just nuke it any time without consequences.

kderbyma 16 hours ago

Arent Gitkeep files specifically for empty folders that are intended to be there?

That is what I have always used them for....

suralind 19 hours ago

I want to like it, but I pretty much always have a "cleanup" script that just deletes the entire directory and touches a .gitkeep file. Obviously an even better pattern is to not have any .gitkeep files, but sometimes they are just handy.

dmarinus 13 hours ago

if possible you can also just create directories if they don't exist (ie. mkdir -p) and just exclude it in your root .gitignore (ie. ignore all build directories). That would safe you from creating multiple .gitignore files.

macote 19 hours ago

The author is misusing .gitkeep. I use it to keep source code folders that don’t contain any code yet, but whose structure is already defined.

  • xyzzy_plugh 19 hours ago

    Truly, what purpose does this serve? Defining a hierarchy without using is injecting immediate debt. Just introduce it when stuff goes there! If you really insist then at least put something in the folder. It doesn't take much effort to make the change at least a tiny bit meaningful.

    Better yet just do the work. If you want make a commit in a branch that's destined to be squashed or something, sure, but keep it away from the shared history and certainly remove it when it's not needed anymore.

    • abustamam 18 hours ago

      I play around with ComfyUI on my computer to make silly images.

      To manually install it, you must clone the repo. Then you have to download models into the right place. Where's the right place? Well, there's an empty directory called models. They go in there.

      IMO that's an effective use of gitkeep.

    • akoboldfrying 18 hours ago

      > Truly, what purpose does this serve?

      The simplest answer is that sometimes other existing software that I need to use treats an empty directory (or, hopefully, a directory containing just an irrelevant file like .gitkeep) differently from an absent directory, and I want that software to behave in the first way instead of the second.

      A more thorough answer would be: Filesystems can represent empty directories, so a technology that supports versioned filesystems should be able to as well. And if that technology can't quite support fully versioned filesystems -- perhaps because it was never designed with that goal in mind -- but can nevertheless support them well enough to cover a huge number of use cases that people actually have, then massaging it a bit to handle those rough edges still makes sense.

      • xyzzy_plugh 5 hours ago

        Legitimately asking, please share the name of software that expects/requires an empty directory and interprets .gitkeep in this way, but chokes on a README file.

        Many filesystems cannot represent empty directories. Many archive formats also do not. I don't think this a problem in practice. I find this argument extremely weak.

  • CGamesPlay 16 hours ago

    You can rename `.gitkeep` to `.gitignore` and both be happy in that case.

cyberrock 15 hours ago

File filtering is so delightfully broken everywhere. Everytime I revisit git, rsync, restic, borg, etc. something just goes wrong somewhere on this seemingly simple task, and SO and thus LLMs are filled to the brim with slightly wrong answers. We need a xkcd/927 because it can't possibly get any worse.

deafpolygon 11 hours ago

Claims the wrong thing is common and tells you not to do it , then tells you to do the right thing.

I have never heard of .gitkeep before today, and if you need an empty directory to exist, use a build script.

Don’t do stupid workarounds.