What does tree-ish mean in Git?

The Short Answer (TL;DR)

“Tree-ish” is a term that refers to any identifier (as specified in the Git
revisions documentation
) that ultimately leads to a (sub)directory
tree (Git refers to directories as “trees” and “tree objects”).

In the original poster’s case, foo is a directory that he wants to
specify. The correct way to specify a (sub)directory in Git is to use this
“tree-ish” syntax (item #15 from the Git revisions documentation):

<rev>:<path>, e.g. HEAD:README, :README, master:./README

A suffix : followed by a path names the blob or tree at the given path in
the tree-ish object named by the part before the colon.

So, in other words, master:foo is the correct syntax, not master/foo.

Other “Tree-ish” (Plus Commit-ish)

Here’s a complete list of commit-ish and tree-ish identifiers (from the Git
revisions documentation
, thanks to LopSae for pointing it
out
):

----------------------------------------------------------------------
|    Commit-ish/Tree-ish    |                Examples
----------------------------------------------------------------------
|  1. <sha1>                | dae86e1950b1277e545cee180551750029cfe735
|  2. <describeOutput>      | v1.7.4.2-679-g3bee7fb
|  3. <refname>             | master, heads/master, refs/heads/master
|  4. <refname>@{<date>}    | master@{yesterday}, HEAD@{5 minutes ago}
|  5. <refname>@{<n>}       | master@{1}
|  6. @{<n>}                | @{1}
|  7. @{-<n>}               | @{-1}
|  8. <refname>@{upstream}  | master@{upstream}, @{u}
|  9. <rev>^                | HEAD^, v1.5.1^0
| 10. <rev>~<n>             | master~3
| 11. <rev>^{<type>}        | v0.99.8^{commit}
| 12. <rev>^{}              | v0.99.8^{}
| 13. <rev>^{/<text>}       | HEAD^{/fix nasty bug}
| 14. :/<text>              | :/fix nasty bug
----------------------------------------------------------------------
|       Tree-ish only       |                Examples
----------------------------------------------------------------------
| 15. <rev>:<path>          | HEAD:README, :README, master:./README
----------------------------------------------------------------------
|         Tree-ish?         |                Examples
----------------------------------------------------------------------
| 16. :<n>:<path>           | :0:README, :README
----------------------------------------------------------------------

Identifiers #1-14 are all “commit-ish”, because they all lead to commits, but
because commits also point to directory trees, they all ultimately lead to
(sub)directory tree objects, and can therefore also be used as “tree-ish”.

#15 can also be used as tree-ish when it refers to a (sub)directory, but it
can also be used to identify specific files. When it refers to files, I’m not
sure if it’s still considered “tree-ish”, or if acts more like “blob-ish” (Git
refers to files as “blobs”).

The Long Answer

At its lowest levels, Git keeps track of source code using four fundamental
objects:

  1. Annotated tags, which point to commits.
  2. Commits, which point to the root directory tree of your project.
  3. Trees, which are directories and subdirectories.
  4. Blobs, which are files.

Each of these objects has its own sha1 hash ID, since Linus Torvalds designed
Git like an content- addressable filesystem, i.e. files can be retrieved
based on their content (sha1 IDs are generated from file content). The Pro Git
book gives this example diagram:

Figure 9-3 from Pro Git book

Many Git commands can accept special identifiers for commits and (sub)directory
trees:

  • “Commit-ish” are identifiers that ultimately lead to a commit object. For example,

    tag -> commit

  • “Tree-ish” are identifiers that ultimately lead to tree (i.e. directory) objects.

    tag -> commit -> project-root-directory

Because commit objects always point to a directory tree object (the root
directory of your project), any identifier that is “commit-ish” is, by
definition, also “tree-ish”. In other words, any identifier that leads to a
commit object can also be used to lead to a (sub)directory tree object
.

But since directory tree objects never point to commits in Git’s versioning
system, not every identifier that points to a (sub)directory tree can also be
used to point to a commit. In other words, the set of “commit-ish” identifiers
is a strict subset of the set of “tree-ish” identifiers.

As explained in the documentation (thanks to Trebor for helping
me find it
):

<tree>

Indicates a tree object name.

<commit>

Indicates a commit object name.

<tree-ish>

Indicates a tree, commit or tag object name. A command that takes a <tree-ish>
argument ultimately wants to operate on a <tree> object but automatically
dereferences <commit> and <tag> objects that point at a <tree>.

<commit-ish>

Indicates a commit or tag object name. A command that takes a <commit-ish>
argument ultimately wants to operate on a <commit> object but automatically
dereferences <tag> objects that point at a <commit>.

The set of tree-ish identifiers that cannot be used as commit-ish are

  1. <rev>:<path>, which leads directly to directory trees, not commit
    objects. For example, HEAD:subdirectory.

  2. Sha1 identifiers of directory tree objects.

Leave a Comment