diff --git a/archive.c b/archive.c
index feed5c02fb2..ca28d6ca3b8 100644
--- a/archive.c
+++ b/archive.c
@@ -230,7 +230,7 @@ static int write_directory(struct archiver_context *c)
 
 static int queue_or_write_archive_entry(const struct object_id *oid,
 					struct strbuf *base, const char *filename,
-					unsigned mode,
+					enum object_type object_type, unsigned mode,
 					void *context)
 {
 	struct archiver_context *c = context;
@@ -243,7 +243,7 @@ static int queue_or_write_archive_entry(const struct object_id *oid,
 		c->bottom = next;
 	}
 
-	if (S_ISDIR(mode)) {
+	if (object_type == OBJ_TREE) {
 		size_t baselen = base->len;
 		const struct attr_check *check;
 
@@ -378,13 +378,13 @@ struct path_exists_context {
 
 static int reject_entry(const struct object_id *oid, struct strbuf *base,
 			const char *filename,
-			unsigned mode,
+			enum object_type object_type, unsigned mode,
 			void *context)
 {
 	int ret = -1;
 	struct path_exists_context *ctx = context;
 
-	if (S_ISDIR(mode)) {
+	if (object_type == OBJ_TREE) {
 		struct strbuf sb = STRBUF_INIT;
 		strbuf_addbuf(&sb, base);
 		strbuf_addstr(&sb, filename);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 0887352db2a..95b2b0edf22 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -115,14 +115,14 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm
 
 static int update_some(const struct object_id *oid, struct strbuf *base,
 		       const char *pathname,
-		       unsigned mode,
+		       enum object_type object_type, unsigned mode,
 		       void *context)
 {
 	int len;
 	struct cache_entry *ce;
 	int pos;
 
-	if (S_ISDIR(mode))
+	if (object_type == OBJ_TREE)
 		return READ_TREE_RECURSIVE;
 
 	len = base->len + strlen(pathname);
diff --git a/builtin/log.c b/builtin/log.c
index b7b76856a9f..c25f75472ff 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -599,11 +599,11 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)
 
 static int show_tree_object(const struct object_id *oid,
 			    struct strbuf *base, const char *pathname,
-			    unsigned mode,
+			    enum object_type object_type, unsigned mode,
 			    void *context)
 {
 	FILE *file = context;
-	fprintf(file, "%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
+	fprintf(file, "%s%s\n", pathname, object_type == OBJ_TREE ? "/" : "");
 	return 0;
 }
 
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 60a2913a01e..cb217870cbd 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -446,7 +446,8 @@ static int read_one_entry_opt(struct index_state *istate,
 }
 
 static int read_one_entry(const struct object_id *oid, struct strbuf *base,
-			  const char *pathname, unsigned mode,
+			  const char *pathname,
+			  enum object_type object_type, unsigned mode,
 			  void *context)
 {
 	struct index_state *istate = context;
@@ -460,7 +461,8 @@ static int read_one_entry(const struct object_id *oid, struct strbuf *base,
  * the stage that will conflict with the entry being added.
  */
 static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,
-				const char *pathname, unsigned mode,
+				const char *pathname,
+				enum object_type object_type, unsigned mode,
 				void *context)
 {
 	struct index_state *istate = context;
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index 8d5c3fd0582..7176d2ae065 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -63,14 +63,13 @@ static int show_recursive(const char *base, int baselen, const char *pathname)
 
 static int show_tree(const struct object_id *oid, struct strbuf *base,
 		     const char *pathname,
-		     unsigned mode,
+		     enum object_type object_type, unsigned mode,
 		     void *context)
 {
 	int retval = 0;
 	int baselen;
-	const char *type = blob_type;
 
-	if (S_ISGITLINK(mode)) {
+	if (object_type == OBJ_COMMIT) {
 		/*
 		 * Maybe we want to have some recursive version here?
 		 *
@@ -80,22 +79,21 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,
 			retval = READ_TREE_RECURSIVE;
 		 *
 		 */
-		type = commit_type;
-	} else if (S_ISDIR(mode)) {
+	} else if (object_type == OBJ_TREE) {
 		if (show_recursive(base->buf, base->len, pathname)) {
 			retval = READ_TREE_RECURSIVE;
 			if (!(ls_options & LS_SHOW_TREES))
 				return retval;
 		}
-		type = tree_type;
 	}
 	else if (ls_options & LS_TREE_ONLY)
 		return 0;
 
 	if (!(ls_options & LS_NAME_ONLY)) {
+		const char *type = type_name(object_type);
 		if (ls_options & LS_SHOW_SIZE) {
 			char size_text[24];
-			if (!strcmp(type, blob_type)) {
+			if (object_type == OBJ_BLOB) {
 				unsigned long size;
 				if (oid_object_info(the_repository, oid, &size) == OBJ_BAD)
 					xsnprintf(size_text, sizeof(size_text),
diff --git a/merge-recursive.c b/merge-recursive.c
index 57198b29fe7..6bc1e659a7b 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -453,7 +453,7 @@ static void unpack_trees_finish(struct merge_options *opt)
 
 static int save_files_dirs(const struct object_id *oid,
 			   struct strbuf *base, const char *path,
-			   unsigned int mode,
+			   enum object_type object_type, unsigned int mode,
 			   void *context)
 {
 	struct path_hashmap_entry *entry;
@@ -467,7 +467,9 @@ static int save_files_dirs(const struct object_id *oid,
 	hashmap_add(&opt->priv->current_file_dir_set, &entry->e);
 
 	strbuf_setlen(base, baselen);
-	return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
+	if (object_type != OBJ_TREE)
+		return 0;
+	return READ_TREE_RECURSIVE;
 }
 
 static void get_files_dirs(struct merge_options *opt, struct tree *tree)
diff --git a/tree.c b/tree.c
index 410e3b477e5..f48b170090f 100644
--- a/tree.c
+++ b/tree.c
@@ -28,6 +28,8 @@ int read_tree_at(struct repository *r,
 	init_tree_desc(&desc, tree->buffer, tree->size);
 
 	while (tree_entry(&desc, &entry)) {
+		struct commit *commit;
+
 		if (retval != all_entries_interesting) {
 			retval = tree_entry_interesting(r->index, &entry,
 							base, 0, pathspec);
@@ -38,7 +40,7 @@ int read_tree_at(struct repository *r,
 		}
 
 		switch (fn(&entry.oid, base,
-			   entry.path, entry.mode, context)) {
+			   entry.path, entry.object_type, entry.mode, context)) {
 		case 0:
 			continue;
 		case READ_TREE_RECURSIVE:
@@ -47,11 +49,11 @@ int read_tree_at(struct repository *r,
 			return -1;
 		}
 
-		if (S_ISDIR(entry.mode))
+		switch (entry.object_type) {
+		case OBJ_TREE:
 			oidcpy(&oid, &entry.oid);
-		else if (S_ISGITLINK(entry.mode)) {
-			struct commit *commit;
-
+			break;
+		case OBJ_COMMIT:
 			commit = lookup_commit(r, &entry.oid);
 			if (!commit)
 				die("Commit %s in submodule path %s%s not found",
@@ -64,9 +66,12 @@ int read_tree_at(struct repository *r,
 				    base->buf, entry.path);
 
 			oidcpy(&oid, get_commit_tree_oid(commit));
-		}
-		else
+			break;
+		case OBJ_BLOB:
 			continue;
+		default:
+			BUG("unreachable");
+		}
 
 		len = tree_entry_len(&entry);
 		strbuf_add(base, entry.path, len);
diff --git a/tree.h b/tree.h
index 10c8637ab3e..1675da7e85c 100644
--- a/tree.h
+++ b/tree.h
@@ -33,7 +33,7 @@ int cmp_cache_name_compare(const void *a_, const void *b_);
 #define READ_TREE_RECURSIVE 1
 typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *,
 			      const char *,
-			      unsigned int,
+			      enum object_type, unsigned int,
 			      void *);
 
 int read_tree_at(struct repository *r,
