#include "cache.h"
 #include "blob.h"
 #include "tree.h"
+#include "commit.h"
 #include "quote.h"
 #include "builtin.h"
 
        int retval = 0;
        const char *type = blob_type;
 
-       if (S_ISDIR(mode)) {
+       if (S_ISDIRLNK(mode)) {
+               /*
+                * Maybe we want to have some recursive version here?
+                *
+                * Something like:
+                *
+               if (show_subprojects(base, baselen, pathname)) {
+                       if (fork()) {
+                               chdir(base);
+                               exec ls-tree;
+                       }
+                       waitpid();
+               }
+                *
+                * ..or similar..
+                */
+               type = commit_type;
+       } else if (S_ISDIR(mode)) {
                if (show_recursive(base, baselen, pathname)) {
                        retval = READ_TREE_RECURSIVE;
                        if (!(ls_options & LS_SHOW_TREES))
 
  */
 #include "cache.h"
 #include "cache-tree.h"
+#include "refs.h"
 
 /* Index extensions.
  *
        return match;
 }
 
+static int ce_compare_gitlink(struct cache_entry *ce)
+{
+       unsigned char sha1[20];
+
+       /*
+        * We don't actually require that the .git directory
+        * under DIRLNK directory be a valid git directory. It
+        * might even be missing (in case nobody populated that
+        * sub-project).
+        *
+        * If so, we consider it always to match.
+        */
+       if (resolve_gitlink_ref(ce->name, "HEAD", sha1) < 0)
+               return 0;
+       return hashcmp(sha1, ce->sha1);
+}
+
 static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
 {
        switch (st->st_mode & S_IFMT) {
                if (ce_compare_link(ce, xsize_t(st->st_size)))
                        return DATA_CHANGED;
                break;
+       case S_IFDIRLNK:
+               /* No need to do anything, we did the exact compare in "match_stat_basic" */
+               break;
        default:
                return TYPE_CHANGED;
        }
                    (has_symlinks || !S_ISREG(st->st_mode)))
                        changed |= TYPE_CHANGED;
                break;
+       case S_IFDIRLNK:
+               if (!S_ISDIR(st->st_mode))
+                       changed |= TYPE_CHANGED;
+               else if (ce_compare_gitlink(ce))
+                       changed |= DATA_CHANGED;
+               break;
        default:
                die("internal error: ce_mode is %o", ntohl(ce->ce_mode));
        }
                return cmp;
        c1 = name1[len];
        c2 = name2[len];
-       if (!c1 && S_ISDIR(mode1))
+       if (!c1 && (S_ISDIR(mode1) || S_ISDIRLNK(mode1)))
                c1 = '/';
-       if (!c2 && S_ISDIR(mode2))
+       if (!c2 && (S_ISDIR(mode2) || S_ISDIRLNK(mode2)))
                c2 = '/';
        return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
 }
        if (lstat(path, &st))
                die("%s: unable to stat (%s)", path, strerror(errno));
 
-       if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
-               die("%s: can only add regular files or symbolic links", path);
+       if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
+               die("%s: can only add regular files, symbolic links or git-directories", path);
 
        namelen = strlen(path);
        size = cache_entry_size(namelen);