53

I deleted all files from a directory in my work folder, but the directory itself still exists. After pushing the changes to a remote repository (GitHub) I checked my project and noticed that the directory was gone. Is it possible that Git deletes empty folders?

Peter Mortensen
  • 12,090
  • 23
  • 70
  • 90
vitaliy4us
  • 639
  • 1
  • 5
  • 3
  • 27
    Git does not delete empty folder but it has no concept of folders therefore it does not track folders (git only tracks paths to your files, not folders, it treats file paths like how you'd treat URLs on a website). The standard way to keep an empty folder in your project is to add a README file so git can track that file instead. – slebetman Aug 18 '19 at 19:39
  • 53
    @slebetman Actually, as mentioned in my answer, the commonly accepted way to keep an empty directory is to add a `.gitkeep` file, not a `README`, as a `.gitkeep` file will usually not be listed (due to starting with a `.`) when browsing directories. `.gitkeep` files are therefore meant to be (almost) invisible. A `README` on the other hand by its naming implies some meaning – it almost tells you that there is something that should be in there, but it's not. – slhck Aug 18 '19 at 20:59
  • 1
    @slhck I prefer a README as it can clearly document why the folder is there. I've never heard of the `.gitkeep` method so wouldn't even know to open the `.gitkeep` file to find out why the folder is there (or even worse, the `.gitkeep` file is actually empty requiring me to look through the git logs for why it was committed - or even worse still, the git logs does not document why the folder is there) – slebetman Aug 18 '19 at 21:04
  • 4
    @slebetman I understand. It's a matter of preference, I suppose. I see both ways [listed here](https://stackoverflow.com/questions/115983/how-can-i-add-an-empty-directory-to-a-git-repository), but personally I've most come across the former. – slhck Aug 18 '19 at 21:42
  • @slebetman `ls -a; git log -- path/to/.gitkeep` isn’t terrible, and it is a somewhat accepted practice, but I admit to being horribly confused the first time I saw it (thought it was special, like gitignore). READMEs is a cool way to do it. Thanks. – D. Ben Knoble Aug 19 '19 at 03:18
  • 23
    @slebetman - you don't "open" a .gitkeep file, it's a zero byte file used only to tell git to track the folder. And it's a de facto standard, so there is no reason to ever check why it was added. It's only ever added to keep the folder, that's why it's called gitKEEP. – Davor Aug 19 '19 at 10:31
  • @Davor README is also a de-facto standard and has the advantage of not confusing people – slebetman Aug 19 '19 at 10:50
  • @D.BenKnoble It's even more useful with a README.md if you host your project on github or gitlab - it makes it self-documenting when someone clicks on the folder – slebetman Aug 19 '19 at 10:54
  • OP, please see my comment on my answer about questions i have – D. Ben Knoble Aug 19 '19 at 12:22
  • 8
    @slebetman - uh, yeah, README is standard, but for a completely different thing. It has nothing to do with preserving empty directories, which is the only reason why gitkeep would ever exist. Using a **wrong** thing is exactly what would confuse people. – Davor Aug 20 '19 at 11:19
  • A file that explains _why_ the directory exists is not going to confuse anyone who reads it! – WGroleau Aug 20 '19 at 15:09
  • @Davor What I was saying is that a README file is a de-facto standard for empty directories. I'm not the only one doing it. And de-facto standard means just that: a common practice adopted by a community. A .gitkeep file is no more standard than README – slebetman Aug 20 '19 at 16:03
  • You should clarify that the directory is gone from your Github project, and not from your local work directory. – Marc.2377 Aug 21 '19 at 08:16
  • 1
    @slebetman - "What I was saying is that a README file is a de-facto standard for empty directories" - well, it's not. You're wrong, and that the whole story. The more you keep insisting, the more you look silly. – Davor Aug 24 '19 at 10:25

4 Answers4

99

Why is the directory not shown?

Git does not track directories; it only tracks files.

If there are no files in a directory, that directory does not “exist” to Git when adding or removing files. Particularly, a directory will disappear from Git's index when you've deleted all files from it and add that change to the index. Vice-versa, a directory will not be added via git add if it's empty.

In other words: If you can see the directory locally in your file browser, but it disappeared from GitHub, you most likely removed all files from the directory, added that change to the index, and committed and pushed it.

How do I track an empty directory, then?

If you want to explicitly track an empty directory, you have to create a file in it. Since Git won't track empty directories, you have to trick it into doing so by adding a file in the directory to Git's index.

Usually, people store a file called .gitkeep in a directory that they wish to track, but where the directory should stay empty for the time being. You can give the file any other name, of course, but the name .gitkeep is a convention. The .gitkeep file (due to starting with a .) will not be shown by file listings on most systems.

Instead of .gitkeep, some users also like to put a README file there instead, ideally with a short description of why the directory has to exist in the first place.

Example

$ mkdir foo
$ git init
$ git add .
$ git ls-files   # which files does Git know about?
                 # apparently, none
$ touch foo/bar  # create a file in the directory
$ git add .
$ git ls-files   # does Git know about it now?
foo/bar          # yep!

Here, the foo directory only gets added to the index once a file is in it.

What if I really want to track an empty directory?

That all said, in principle, the underlying data structure allows Git to store an empty directory, since it would be represented by an empty “tree”. Some further reading here and here.

slhck
  • 223,558
  • 70
  • 607
  • 592
  • 13
    cannot is somewhat misleading. Structure of object files (trees and blobs) supports empty directories. It just deliberately decided not to track empty directories – RiaD Aug 19 '19 at 01:07
  • @RiaD Good point. I updated my answer to be a bit more precise. – slhck Aug 19 '19 at 07:35
  • 3
    Git does not even track *files*, it tracks file contents. That's why it can recognize renamed files on the fly. – Davor Aug 20 '19 at 13:02
  • 6
    @Davor Well, that's a bit of a matter of definition. Yes, it tracks file content primarily, but it also tracks the paths that point to that content. The tree entries that Git stores *are* filenames, and they point to the actual content. – slhck Aug 20 '19 at 14:36
  • "Git does not track directories; it only tracks files." Isn't directory a special type of file on *Nix based architectures? – Worse_Username Aug 21 '19 at 11:26
  • 1
    @Worse_Username Yes, but Git is not using exactly the same concepts here. In my answer, I was talking about the Git frontend, which basically lets you add file contents to the index. The directories in which the files reside are the trees. Please have a look at the [first link](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) in my answer: *“All the content is stored as tree and blob objects, with trees corresponding to UNIX directory entries and blobs corresponding more or less to inodes or file contents”* – slhck Aug 21 '19 at 11:39
  • Interesting! good to know. – Momin Aug 22 '19 at 05:50
7

Add the following .gitignore file to the folders you wish to include in your repo.

# Ignore everything in this directory
*
# Except this file
!.gitignore

Adding .gitkeep works but isn’t an official solution. See https://stackoverflow.com/questions/7229885/what-are-the-differences-between-gitignore-and-gitkeep

sunknudsen
  • 902
  • 11
  • 23
  • 29
    This will indeed ensure Git tracks the empty directory, but it will also prevent Git from tracking any other files in that directory — so this is for if you want a directory that should *always* stay empty in Git. – NobodyNada Aug 19 '19 at 00:52
  • 3
    I think @NobodyNada's comment should be part of this answer. Some people might want their Git project to contain a directory that is empty and never tracked (like a `build` directory?) – TomTsagk Aug 19 '19 at 09:44
  • 19
    There's no such thing as an "official" solution. You just can't have empty directories in git, period. Adding a file, any file to the directory works because then it's no longer an empty directory. This is perfectly documented, "official" behaviour. .gitignore is in the documentation, unlike other file names you might use, because it does unrelated stuff: make git ignore specific files. That doesn't make it a better solution to this problem. On the contrary, that makes it confusing as people will expect it to do that other stuff, and not just be there to keep the directory. – FrederikVds Aug 19 '19 at 11:37
  • I tried editing the answer to include NobodyNada's point, but the edit was rejected. Hopefully nobody misses the comment and loses data. – chicks Aug 20 '19 at 12:15
3

Git might delete folders if you ran git clean with -dffx (possibly even with a subset of those options).

As @TRiG mentions in the comments, if the directory is empty when you commit, it won’t show up in, e.g., GitHub, because git doesn’t track directories (it track files)—the tree where those files were is no longer relevant because there are no files, so there is no tree.

Without more detail, it’s practically impossible for us to properly answer your underlying questions (what happened?).

  • The OP "deleted all files from a directory" and committed those changes. So I believe it's quite clear what happened. – slhck Aug 19 '19 at 07:36
  • 1
    @slhck not so—how did they delete? rm -rf? git clean? Drag and drop to the recycle bin? What does « checked my project » mean, and *which* directory was gone? – D. Ben Knoble Aug 19 '19 at 12:21
  • To me, it's pretty reasonable to assume that it was `rm foo/*` (or any other method of deleting), `git add foo`, then commit and push. When you check your project on GitHub (as the OP indicated), the directory will be gone. – slhck Aug 19 '19 at 13:05
  • 1
    @slhck i see a lot of assumptions—the deletion, as mentioned. Checking on GitHub? Where is that mentioned? I prefer not to assume I know the steps to reproduce something until I know them—ive been blinded to the true cause of bugs that way before. – D. Ben Knoble Aug 19 '19 at 13:16
  • The OP said, "After pushing the changes to remote repository (github) I checked my project." Also, Occam's Razor. There's a simple and straightforward explanation to the effects observed with reasonable assumptions, so it is very likely to be the solution. – slhck Aug 19 '19 at 13:27
  • 2
    Deleting all files from a directly, _not_ running `git clean -d` or similar, comitting, and pushing will have the observed effect (the empty directory still exists locally, but has disappeared from the remote project). – TRiG Aug 19 '19 at 14:28
2

Git does not track folders. In order to refrain from accumulating messes, when the index changes from (indirectly) referencing a directory to not referencing it (by doing something like git rm on the last registered file in the directory), Git tries removing the directory before it loses track of it by it no longer being in the index. If Git is successful because the directory has indeed become empty (and does not contain unregistered files), the directory will be gone, if not, it will stick around since Git no longer has notice of it.