git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[WIP/RFC PATCH 1/2] Introduce GIT_INDEX_PREFIX

From
Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Date
Jun 4, 2008, 16:28 UTC
Message-ID
<20080604162825.GB23975@laptop>

GIT_INDEX_PREFIX is used to limit write access to a specific directory. Only "important" information is protected by index prefix (those will be used to create tree objects)

When GIT_INDEX_PREFIX is set, any attempt to modify the index (refresh
it is okay though) will bail out. read-tree and merge, however, can
write to full index. For merge, no conflict is allowed outside index
prefix.
---
 builtin-merge-recursive.c |    4 ++-
 builtin-read-tree.c       |    5 +++
 builtin-rev-parse.c       |    5 +++
 builtin-update-index.c    |    3 ++
 cache.h                   |    2 +
 environment.c             |   12 ++++++
 read-cache.c              |   84 +++++++++++++++++++++++++++++++++++++++++++-
 unpack-trees.c            |    3 ++
 unpack-trees.h            |    3 +-
 9 files changed, 117 insertions(+), 4 deletions(-)
diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
index 362c290..94ca808 100644
--- a/builtin-merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -209,8 +209,10 @@ static int git_merge_trees(int index_only,
 	memset(&opts, 0, sizeof(opts));
 	if (index_only)
 		opts.index_only = 1;
-	else
+	else {
 		opts.update = 1;
+		opts.check_index_prefix = 1;
+	}
 	opts.merge = 1;
 	opts.head_idx = 2;
 	opts.fn = threeway_merge;
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 5a09e17..ea0a929 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -242,6 +242,11 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
 			opts.head_idx = 1;
 	}
 
+	if (get_index_prefix()) {
+		read_cache();
+		opts.check_index_prefix = 1;
+	}
+
 	for (i = 0; i < nr_trees; i++) {
 		struct tree *tree = trees[i];
 		parse_tree(tree);
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index a7860ed..1888208 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -499,6 +499,11 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 					puts(prefix);
 				continue;
 			}
+			if (!strcmp(arg, "--show-index-prefix")) {
+				if (get_index_prefix())
+					puts(get_index_prefix());
+				continue;
+			}
 			if (!strcmp(arg, "--show-cdup")) {
 				const char *pfx = prefix;
 				if (!is_inside_work_tree()) {
diff --git a/builtin-update-index.c b/builtin-update-index.c
index 9e0d7ab..71c83ec 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -242,6 +242,9 @@ static void chmod_path(int flip, const char *path)
 	struct cache_entry *ce;
 	unsigned int mode;
 
+	if (get_index_prefix() && prefixcmp(path, get_index_prefix()))
+		die("%s: cannot update index outside %s", path, get_index_prefix());
+
 	pos = cache_name_pos(path, strlen(path));
 	if (pos < 0)
 		goto fail;
diff --git a/cache.h b/cache.h
index eab1a17..9f5a613 100644
--- a/cache.h
+++ b/cache.h
@@ -300,6 +300,7 @@ static inline enum object_type object_type(unsigned int mode)
 #define CONFIG_ENVIRONMENT "GIT_CONFIG"
 #define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL"
 #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
+#define INDEX_PREFIX_ENVIRONMENT "GIT_INDEX_PREFIX"
 #define GITATTRIBUTES_FILE ".gitattributes"
 #define INFOATTRIBUTES_FILE "info/attributes"
 #define ATTRIBUTE_MACRO_PREFIX "[attr]"
@@ -318,6 +319,7 @@ extern int set_git_dir(const char *path);
 extern const char *get_git_work_tree(void);
 extern const char *read_gitfile_gently(const char *path);
 extern void set_git_work_tree(const char *tree);
+extern const char *get_index_prefix(void);
 
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
diff --git a/environment.c b/environment.c
index 73feb2d..a8edaf5 100644
--- a/environment.c
+++ b/environment.c
@@ -46,6 +46,8 @@ enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
 char *git_work_tree_cfg;
 static char *work_tree;
 
+static const char *index_prefix;
+
 static const char *git_dir;
 static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
 
@@ -71,6 +73,9 @@ static void setup_git_env(void)
 	git_graft_file = getenv(GRAFT_ENVIRONMENT);
 	if (!git_graft_file)
 		git_graft_file = xstrdup(git_path("info/grafts"));
+	index_prefix = getenv(INDEX_PREFIX_ENVIRONMENT);
+	if (index_prefix && (!*index_prefix || index_prefix[strlen(index_prefix)-1] != '/'))
+		die("GIT_INDEX_PREFIX must end with a slash");
 }
 
 int is_bare_repository(void)
@@ -122,6 +127,13 @@ const char *get_git_work_tree(void)
 	return work_tree;
 }
 
+const char *get_index_prefix()
+{
+	if (!git_dir)
+		setup_git_env();
+	return index_prefix;
+}
+
 char *get_object_directory(void)
 {
 	if (!git_object_dir)
diff --git a/read-cache.c b/read-cache.c
index ac9a8e7..4f8d44b 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -23,6 +23,11 @@
 
 struct index_state the_index;
 
+static int outside_index_prefix(const struct index_state *istate, const char *ce_name)
+{
+	return istate == &the_index && get_index_prefix() && prefixcmp(ce_name, get_index_prefix());
+}
+
 static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
 {
 	istate->cache[nr] = ce;
@@ -380,6 +385,8 @@ int remove_index_entry_at(struct index_state *istate, int pos)
 {
 	struct cache_entry *ce = istate->cache[pos];
 
+	if (outside_index_prefix(istate, ce->name))
+		die("%s: cannot remove from index outside %s", ce->name, get_index_prefix());
 	remove_name_hash(ce);
 	istate->cache_changed = 1;
 	istate->cache_nr--;
@@ -394,6 +401,10 @@ int remove_index_entry_at(struct index_state *istate, int pos)
 int remove_file_from_index(struct index_state *istate, const char *path)
 {
 	int pos = index_name_pos(istate, path, strlen(path));
+
+	if (outside_index_prefix(istate, path))
+		die("%s: cannot remove from index from outside %s", path, get_index_prefix());
+
 	if (pos < 0)
 		pos = -pos-1;
 	cache_tree_invalidate_path(istate->cache_tree, path);
@@ -793,21 +804,35 @@ static int check_file_directory_conflict(struct index_state *istate,
 	return retval + has_dir_name(istate, ce, pos, ok_to_replace);
 }
 
+static int ce_compare(const struct cache_entry *ce1, const struct cache_entry *ce2)
+{
+	return ce1->ce_mode == ce2->ce_mode &&
+		((ce1->ce_flags ^ ce2->ce_flags) & ~(CE_HASHED | CE_UPDATE)) == 0 &&
+		!memcmp(ce1->sha1, ce2->sha1, 20) &&
+		!strcmp(ce1->name, ce2->name);
+}
+
 static int add_index_entry_with_check(struct index_state *istate, struct cache_entry *ce, int option)
 {
 	int pos;
 	int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
 	int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE;
 	int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
+	int is_outside_index_prefix = outside_index_prefix(istate, ce->name);
 
 	cache_tree_invalidate_path(istate->cache_tree, ce->name);
 	pos = index_name_pos(istate, ce->name, ce->ce_flags);
 
 	/* existing match? Just replace it. */
 	if (pos >= 0) {
+		if (is_outside_index_prefix && !ce_compare(istate->cache[pos], ce))
+			die("%s: cannot add to index outside %s", ce->name, get_index_prefix());
 		replace_index_entry(istate, pos, ce);
 		return 0;
 	}
+
+	if (is_outside_index_prefix)
+		die("%s: cannot add to index outside %s", ce->name, get_index_prefix());
 	pos = -pos-1;
 
 	/*
@@ -842,9 +867,11 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti
 {
 	int pos;
 
-	if (option & ADD_CACHE_JUST_APPEND)
+	if (option & ADD_CACHE_JUST_APPEND) {
+		if (outside_index_prefix(istate, ce->name))
+			die("%s: cannot add to index outside %s", ce->name, get_index_prefix());
 		pos = istate->cache_nr;
-	else {
+	} else {
 		int ret;
 		ret = add_index_entry_with_check(istate, ce, option);
 		if (ret <= 0)
@@ -1385,3 +1412,56 @@ int write_index(const struct index_state *istate, int newfd)
 	}
 	return ce_flush(&c, newfd);
 }
+
+int check_index_prefix(const struct index_state *index, int is_merge)
+{
+	unsigned start,end1,end2,i;
+	struct cache_entry **cache1, **cache2;
+	const char *index_prefix = get_index_prefix();
+
+	if (!index_prefix)
+		return 0;
+
+	/* Check for unmerged entries first */
+	for (i = 0; i < index->cache_nr; i++) {
+		struct cache_entry *ce = index->cache[i];
+		if (ce_stage(ce) && prefixcmp(ce->name, index_prefix))
+			return 1;
+	}
+
+	if (is_merge)
+		return 0;
+
+	/* not a merge, no change is allowed outside index prefix */
+
+	cache1 = the_index.cache;
+	cache2 = index->cache;
+	start = 0;
+	end1 = the_index.cache_nr ? the_index.cache_nr - 1 : 0;
+	end2 = index->cache_nr ? index->cache_nr - 1 : 0;
+	while (start < end1 && start < end2 &&
+		ce_compare(cache1[start], cache2[start]))
+		start ++;
+
+	while (end1 > start && end2 > start &&
+		ce_compare(cache1[end1], cache2[end2])) {
+		end1 --;
+		end2 --;
+	}
+
+	/*
+	 * everything in start..end1 and start..end2 must
+	 * be prefixed by get_index_prefix()
+	 */
+	if (start < end1 &&
+		(prefixcmp(cache1[start]->name, index_prefix) ||
+		prefixcmp(cache1[end1]->name, index_prefix)))
+		return 1;
+
+	if (start < end2 &&
+		(prefixcmp(cache2[start]->name, index_prefix) ||
+		prefixcmp(cache2[end2]->name, index_prefix)))
+		return 1;
+
+	return 0;
+}
diff --git a/unpack-trees.c b/unpack-trees.c
index 0de5a31..2ef4827 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -403,6 +403,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 	if (o->trivial_merges_only && o->nontrivial_merge)
 		return unpack_failed(o, "Merge requires file-level merging");
 
+	if (o->check_index_prefix && check_index_prefix(&o->result, o->merge && !o->prefix))
+		return unpack_failed(o, "Merge outside index prefix");
+
 	o->src_index = NULL;
 	if (check_updates(o))
 		return -1;
diff --git a/unpack-trees.h b/unpack-trees.h
index 94e5672..a1b46f9 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -26,7 +26,8 @@ struct unpack_trees_options {
 		     verbose_update:1,
 		     aggressive:1,
 		     skip_unmerged:1,
-		     gently:1;
+		     gently:1,
+		     check_index_prefix:1;
 	const char *prefix;
 	int pos;
 	struct dir_struct *dir;
-- 
1.5.5.GIT
Next: Junio C Hamano
Message 1 of 5 in “Introduce GIT_INDEX_PREFIX”
  1. 1/2 Introduce GIT_INDEX_PREFIXNguyễn Thái Ngọc Duy, Jun 4, 2008
  2. Junio C HamanoJun 4, 2008
  3. Nguyen Thai Ngoc DuyJun 5, 2008
  4. Johannes SchindelinJun 5, 2008
  5. Nguyen Thai Ngoc DuyJun 5, 2008

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.