9

Key word here is why. There are plenty of questions where the answer is, "git doesn't allow that" but I want to know why it doesn't allow this.

I've been reading about the architecture of git and it has this image in it:

DAG with tree nodes

This image shows that there are tree nodes. Technically, it looks like it'd be straight forward to save a tree without any children. So why does git forbid this?

There's a part in this book that mentions this:

For each directory above the changed file (plus the repository root directory), a new tree object is created with a new identifier. A DAG is created starting from the newly created root tree object pointing to blobs (reusing existing blob references where the files content has not changed in this commit) and referencing the newly created blob in place of that file's previous blob object in the previous tree hierarchy. (A blob represents a file stored in the repository.)

I feel like this might be the reason, but it's kind of glossing over the details with respect to what I want answered.

Daniel Kaplan
  • 62,768
  • 50
  • 234
  • 356
  • 1
    @Cicada yes, but **why**? :P – Daniel Kaplan Sep 27 '14 at 22:05
  • AFAIK, it's because none of the git developers cared enough to implement that. – Lucas Trzesniewski Sep 27 '14 at 22:10
  • @tieTYT The reason is most likely status-by-design: Git cares about file contents, not directories. – jub0bs Sep 27 '14 at 22:11
  • @LucasTrzesniewski I see, I always assumed it was for a technical reason. If that's the real reason, I'd consider it an acceptable answer. – Daniel Kaplan Sep 27 '14 at 22:11
  • @keyser I don't know what you mean by that, I feel like your comment can be interpreted many ways. As a concept, the image is showing that directories are explicit. – Daniel Kaplan Sep 27 '14 at 22:23
  • Here's a clarification ([a quote](http://stackoverflow.com/a/3030432/645270)): _"git actually can (in theory) record empty directories. The problem lies in the index file (the staging area): it only lists files; and commits are built from the index file."_ – keyser Sep 27 '14 at 22:24
  • @keyser doesn't the image show the exact opposite of that answer? – Daniel Kaplan Sep 27 '14 at 22:27
  • Here's another good source: [Git FAQ - Can I add empty directories?](https://git.wiki.kernel.org/index.php/Git_FAQ#Can_I_add_empty_directories.3F) – keyser Sep 27 '14 at 22:27
  • @tieTYT I'm not sure what you mean. The wording "_forbidden_" is off, yes, but still, the part about empty dirs being of no interest to git holds. – keyser Sep 27 '14 at 22:30
  • Note that `git` doesn't explicitly **forbid** adding empty directories. Rather the design **doesn't allow** it. – Code-Apprentice Sep 27 '14 at 22:30
  • @tieTYT - Can I ask _why_ you feel it is necessary to store an empty folder in your repository? Ultimately Git cares about changes to _files_, and an empty folder it not something you need to compare. – Tony Sep 27 '14 at 22:32
  • @Tony I don't have a reason or necessarily a want to do it. I just want to learn more about git. – Daniel Kaplan Sep 27 '14 at 22:33

3 Answers3

7

The reason for this is actually simple - directories in the git index only exist as part of the file paths.

If you have directories dir1 and dir2, with dir1 containing fileA and fileB and dir2 containing fileC, git will commit the following (i.e. add the following to the index file):

  • dir1/fileA
  • dir1/fileB
  • dir2/fileC

In other words, neither dir1 nor dir2 are truly committed, they are simply constructed implicitly based on the paths of files contained inside them.

Now whether there are any deeper reasons for this or it simply made the implementation easier, I don't know.

fstanis
  • 5,234
  • 1
  • 23
  • 42
  • Can you explain this in terms of the image? It looks to me like it is **explicitly** storing the `tree`. – Daniel Kaplan Sep 27 '14 at 22:29
  • 1
    Yes, but as mentioned in the comments, the important thing here is the [git index](http://www.gitguys.com/topics/whats-the-deal-with-the-git-index/) which is, basically, a list of *files* contained inside a commit. This is the only information git uses to construct a directory structure. – fstanis Sep 27 '14 at 22:38
  • Ah, now it makes sense to me. – Daniel Kaplan Sep 27 '14 at 22:41
  • 1
    For what it's worth (not a whole lot) git actually has a built in empty tree in every repository, and it's possible to use it, but this is a bit dangerous: see http://stackoverflow.com/a/8944077/1256452 (see http://stackoverflow.com/q/9765453/1256452 for more on the empty tree). – torek Sep 28 '14 at 00:11
6

GIT FAQ is pretty straightforward about it:

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.

Link:

https://git.wiki.kernel.org/index.php/GitFaq#Can_I_add_empty_directories.3F

Piotr Oktaba
  • 775
  • 1
  • 4
  • 14
  • Note, though, that your quote only states what the current state of affairs is, not what originally motivated this design decision. – jub0bs Sep 27 '14 at 23:30
3

It's actually entirely possible to create and commit and external directory using the low-level commands git mktree and git commit-tree (git ls-tree is useful for inspection). I think this is useful for mirroring other VCS's that do support empty directories. It's only the high-level commands that don't support it.

o11c
  • 15,265
  • 4
  • 50
  • 75
  • 1
    High-level commands like `git checkout`, you mean? Checking out a commit with empty directories will not create those directories, so even if empty directories would be allowed by some sort of special option to `git commit`, there wouldn't be a whole lot you'd be able to do with it. –  Sep 27 '14 at 22:48
  • See the list of porcelain commands in `git(1)`, then the list of plumbing commands. – o11c Sep 27 '14 at 22:57
  • I should've used `git read-tree`, which also doesn't support empty directories. Like the other answers note, the index doesn't support it, and it doesn't matter whether you modify the index using high-level commands or low-level commands, it's just not going to work. The only commands that can handle it are those that do not use the index at all. –  Sep 27 '14 at 23:01