Subject: [PATCH v2 4/8] sha1_file: new object source for submodule's alt object database

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Tue, 30 Apr 2013 10:42:48 +0700

This patch separates submodule odb sources from ordinary alternate
sources. The new sources can be accessed with ODB_EXTALT (e.g. via
read_sha1_file_extended).

ODB_EXTALT is only added to odb_default in certain cases. Basically:

 - External commands do not access submodule odb by default
 - unpack-objects, index-pack and rev-list do not
 - All other builtin commands do

unpack-objects, index-pack and rev-list take new objects from outside
and have to make sure the repository is still in good state. They
should not pay attention to submodule's odb, especially rev-list
because it does connectivity check.

External commands also do not have default access to submodule odb,
simply because I see no reasons why the should. They don't usually
play a big role in the user front, where submodule integration happens
and requires looking into submodule odb.

The die() in add_submodule_odb() may be too strong. There might be a
use case where somebody wants to add_submodule_odb() and look some up
with read_sha1_file_extended() even if odb_default does not contain
ODB_EXTALT. Right now such a use case may need to work around die() by
temporarily adding ODB_EXTALT to odb_default. Not nice, but as no such
use case exists yet to worry about.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 cache.h     |  1 +
 git.c       | 10 +++++++---
 sha1_file.c | 24 +++++++++++++++++-------
 submodule.c |  3 +++
 4 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/cache.h b/cache.h
index b8d8e03..bc3ccd8 100644
--- a/cache.h
+++ b/cache.h
@@ -759,6 +759,7 @@ int offset_1st_component(const char *path);
  * function cals explicitly.
  */
 #define ODB_DEFAULT	8
+#define ODB_EXTALT	16
 
 extern unsigned int odb_default;
 
diff --git a/git.c b/git.c
index 1ada169..49a66fa 100644
--- a/git.c
+++ b/git.c
@@ -242,6 +242,7 @@ static int handle_alias(int *argcp, const char ***argv)
  * RUN_SETUP for reading from the configuration file.
  */
 #define NEED_WORK_TREE		(1<<3)
+#define NO_ODB_EXTALT		(1<<4)
 
 struct cmd_struct {
 	const char *cmd;
@@ -258,6 +259,9 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 	prefix = NULL;
 	help = argc == 2 && !strcmp(argv[1], "-h");
 	if (!help) {
+		if (!(p->option & NO_ODB_EXTALT))
+			odb_default |= ODB_EXTALT;
+
 		if (p->option & RUN_SETUP)
 			prefix = setup_git_directory();
 		if (p->option & RUN_SETUP_GENTLY) {
@@ -349,7 +353,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "grep", cmd_grep, RUN_SETUP_GENTLY },
 		{ "hash-object", cmd_hash_object },
 		{ "help", cmd_help },
-		{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
+		{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY | NO_ODB_EXTALT },
 		{ "init", cmd_init_db },
 		{ "init-db", cmd_init_db },
 		{ "log", cmd_log, RUN_SETUP },
@@ -392,7 +396,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "repo-config", cmd_repo_config, RUN_SETUP_GENTLY },
 		{ "rerere", cmd_rerere, RUN_SETUP },
 		{ "reset", cmd_reset, RUN_SETUP },
-		{ "rev-list", cmd_rev_list, RUN_SETUP },
+		{ "rev-list", cmd_rev_list, RUN_SETUP | NO_ODB_EXTALT },
 		{ "rev-parse", cmd_rev_parse },
 		{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
 		{ "rm", cmd_rm, RUN_SETUP },
@@ -408,7 +412,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "tag", cmd_tag, RUN_SETUP },
 		{ "tar-tree", cmd_tar_tree },
 		{ "unpack-file", cmd_unpack_file, RUN_SETUP },
-		{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
+		{ "unpack-objects", cmd_unpack_objects, RUN_SETUP | NO_ODB_EXTALT },
 		{ "update-index", cmd_update_index, RUN_SETUP },
 		{ "update-ref", cmd_update_ref, RUN_SETUP },
 		{ "update-server-info", cmd_update_server_info, RUN_SETUP },
diff --git a/sha1_file.c b/sha1_file.c
index eb682b3..53f93ab 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -37,6 +37,10 @@ static inline uintmax_t sz_fmt(size_t s) { return s; }
 const unsigned char null_sha1[20];
 
 /*
+ * Some commands may not want to touch ODB_EXTALT at all. So
+ * ODB_EXTALT is only enabled later, not now, when we know which
+ * command is running.
+ *
  * clear_delta_base_cache() may be needed if odb_default is changed to
  * a narrower origin set.
  */
@@ -433,10 +437,12 @@ void foreach_alt_odb(alt_odb_fn fn, void *cb)
 			return;
 }
 
-static inline int match_origin(unsigned int origin, int pack_local)
+static inline int match_origin(unsigned int origin,
+			       struct alternate_object_database *alt)
 {
-	return (pack_local && (origin & ODB_LOCAL)) ||
-		(!pack_local && (origin & ODB_ALT));
+	return (!alt && (origin & ODB_LOCAL)) ||
+		(alt && (origin & ODB_ALT)    && !alt->external) ||
+		(alt && (origin & ODB_EXTALT) &&  alt->external);
 }
 
 void prepare_alt_odb(void)
@@ -464,10 +470,12 @@ static int has_loose_object_extended(const unsigned char *sha1,
 			return 1;
 	}
 
-	if (origin & ODB_ALT) {
+	if (origin & (ODB_ALT | ODB_EXTALT)) {
 		struct alternate_object_database *alt;
 		prepare_alt_odb();
 		for (alt = alt_odb_list; alt; alt = alt->next) {
+			if (!match_origin(origin, alt))
+				continue;
 			fill_sha1_path(alt->name, sha1);
 			if (!access(alt->base, F_OK))
 				return 1;
@@ -478,7 +486,7 @@ static int has_loose_object_extended(const unsigned char *sha1,
 
 int has_loose_object_nonlocal(const unsigned char *sha1)
 {
-	unsigned int origin = ODB_ALT;
+	unsigned int origin = ODB_ALT | ODB_EXTALT;
 	return has_loose_object_extended(sha1, origin);
 }
 
@@ -1354,7 +1362,7 @@ static int open_sha1_file(const unsigned char *sha1, unsigned int origin)
 	    (fd = git_open_noatime(name)) >= 0)
 		return fd;
 
-	if (!(origin & ODB_ALT)) {
+	if (!(origin & (ODB_ALT | ODB_EXTALT))) {
 		errno = ENOENT;
 		return -1;
 	}
@@ -1362,6 +1370,8 @@ static int open_sha1_file(const unsigned char *sha1, unsigned int origin)
 	prepare_alt_odb();
 	errno = ENOENT;
 	for (alt = alt_odb_list; alt; alt = alt->next) {
+		if (!match_origin(origin, alt))
+			continue;
 		name = alt->name;
 		fill_sha1_path(name, sha1);
 		fd = git_open_noatime(alt->base);
@@ -2336,7 +2346,7 @@ static int fill_pack_entry(const unsigned char *sha1,
 {
 	off_t offset;
 
-	if (!match_origin(origin, p->pack_local))
+	if (!match_origin(origin, p->alt))
 		return 0;
 
 	if (p->num_bad_objects) {
diff --git a/submodule.c b/submodule.c
index 3cb63f7..1271366 100644
--- a/submodule.c
+++ b/submodule.c
@@ -37,6 +37,9 @@ static int add_submodule_odb(const char *path)
 	int ret = 0;
 	const char *git_dir;
 
+	if (!(odb_default & ODB_EXTALT))
+		die("BUG: this command does not support submodule odb");
+
 	strbuf_addf(&objects_directory, "%s/.git", path);
 	git_dir = read_gitfile(objects_directory.buf);
 	if (git_dir) {
-- 
1.8.2.83.gc99314b