Konstantin Shemyak
Konstantin Shemyak

Reputation: 2537

What directories in .git/refs have special meaning?

In .git/refs, three directories are described in The Git Book:

heads
remotes
tags

Additionally, git replace creates directory replace. So at least those 4 names are known by, and have special meaning to Git.

But e.g. Gerrit creates directories for and changes in own repository. (This is just an illustration, I'm not asking anything about Gerrit.) I can, for example, fetch the 'changes' refs. Git will know that they are not branches and not remotes - are they handled like tags?

How in general does Git understand "non-standard" content in .git/refs? What can be a use case for such? Are there any other "standard" directories for refs besides the ones I listed above?

Upvotes: 2

Views: 597

Answers (2)

Schwern
Schwern

Reputation: 165456

How in general does Git understand "non-standard" content in .git/refs?

It doesn't. It ignores them.

.git is a directory tree. Like any other directory tree, the file and directory names have meaning to git. Each top level directory of .git/refs/ has a specific meaning for git.

  • .git/refs/heads/ contains the branch heads (ie. master)
  • .git/refs/tags/ contains tags (ie. v1.2.3)
  • .git/refs/remotes/ contains remote branch heads (ie. origin/master)

Git commands will walk through the files within those directories to discover what branches, tags, and remotes are available. And it knows when it wants, for example, tags it looks in .git/refs/tags/.

For example, git branch is effectively listing all the files in .git/refs/heads/ and its subdirectories. git tag -l does .git/refs/tags/. And git branch -r does .git/refs/remotes/. Note there are optimizations like "packed refs" which make this not so simple anymore.

Anything else in there git simply doesn't use. Just like there are files and directories in your home directory put there by many programs, they each only care about their own.


What can be a use case for such?

You've already answered your question with Gerrit. When 3rd party Git utilities need to store extra information about a local repository, they can put it in .git. Unlike plopping a .gerrit directory at the top of your code tree, extra files and directories in .git are ignored by most other utilities. And if you move or copy the repo directory, the extra Gerrit information in .git will go with it.

For example, when Gerrit wants to store additional types of references, it can put them in .git/refs/changes/ or .git/refs/for/ without interfering with normal git functioning.


Are there any other "standard" directories for refs besides the ones I listed above?

I doubt there's a definitive list, you'd probably have to scan the source code of git for that. See @torek's answer for a list of known directories.

Upvotes: 2

torek
torek

Reputation: 489718

I have a slightly different take on this than in Schwern's answer: no directories have special meaning at all. Instead, certain prefix strings have meaning.

What Git cares about are references, which are names beginning with refs/ that meet the restrictions imposed by git check-ref-format. Git sometimes chooses to store these names as ordinary file path names, and sometimes not—in particular .git/packed-refs stores them all in a simple text-database file. In the future, Git may use a real database in addition to, or instead of, the flat file and/or the directory tree. If you stick to the published interfaces (outlined below) you can insulate yourself from any format changes in the future.

The defined prefix strings within Git itself are (I'm attempting to make this list complete but I forgot at least one that Schwern added so I added it :-) ):

  • refs/bisect/: information about an ongoing bisection
  • refs/heads/: branch names
  • refs/notes/: notes names
  • refs/original/: git filter-branch uses this to store all the original (pre-filter operation) references
  • refs/remotes/: remote-tracking names
  • refs/replace/: replacement objects
  • refs/stash: the (singular) stash
  • refs/tags/: tag names

All other names are available, at least until some third party extension gets integrated into Git proper.

To write a reference, use the shell command git update-ref. This can create, update, or delete a reference to any valid hash ID. To write a symbolic reference, use git symbolic-ref. Both commands will update the reference's reflog if it exists, or create or delete it if appropriate.

To walk all references, use git for-each-ref. To walk specific references within the logical tree structure, you can continue to use git for-each-ref (it takes prefixes and glob patterns), or use some of the more user-oriented commands (like git branch and git tag), or use --branches, --tags, and/or --remotes arguments to git rev-list --no-walk, for instance.

If you want to use lower level operations, note that currently, the existence of a file within .git/refs overrides any value stored in the flat-file .git/packed-refs file. I suspect that if/when Git acquires a true reference database, the true-database entries would override all others, and there might be database entry type-codes for "this name used to exist, but now does not". (If the database is efficient it will probably also hold the reflog entries, and this would be a good way to allow "undeleting" an old reference.)

Upvotes: 4

Related Questions