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

[PATCH v2 6/7] repository: adapt `repo_clear()` to fully reset the repository

From
Patrick Steinhardt <ps@pks.im>
Date
Sep 28, 2026, 09:51 UTC
Message-ID
<20260928-pks-create-repository-stateless-v2-6-a03612f703fa@pks.im>
In-Reply-To
<20260928-pks-create-repository-stateless-v2-0-a03612f703fa@pks.im>

The function `repo_clear()` can be used to clear a repository's state. The way it's written though it's quite easy for it to accidentally leak some state because we don't make sure to clear the whole structure.

Refactor the function to set the whole repository to all-zeroes to avoid any kind of leaking state. Replace calls of `FREE_AND_NULL()` to instead use free(3p) to avoid zeroing out the data twice.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 repository.c | 37 ++++++++++++++++++-------------------
 repository.h |  2 +-
 2 files changed, 19 insertions(+), 20 deletions(-)
diff --git a/repository.c b/repository.c
index b857e1c580..e67ff00550 100644
--- a/repository.c
+++ b/repository.c
@@ -374,60 +374,57 @@ void repo_clear(struct repository *repo)
 	struct hashmap_iter iter;
 	struct strmap_entry *e;
 
-	FREE_AND_NULL(repo->gitdir);
-	FREE_AND_NULL(repo->commondir);
-	FREE_AND_NULL(repo->prefix);
-	FREE_AND_NULL(repo->graft_file);
-	FREE_AND_NULL(repo->index_file);
-	FREE_AND_NULL(repo->worktree);
-	FREE_AND_NULL(repo->submodule_prefix);
-	FREE_AND_NULL(repo->ref_storage_payload);
+	free(repo->gitdir);
+	free(repo->commondir);
+	free(repo->prefix);
+	free(repo->graft_file);
+	free(repo->index_file);
+	free(repo->worktree);
+	free(repo->submodule_prefix);
+	free(repo->ref_storage_payload);
 
 	odb_free(repo->objects);
-	repo->objects = NULL;
 
 	if (repo->parsed_objects)
 		parsed_object_pool_clear(repo->parsed_objects);
-	FREE_AND_NULL(repo->parsed_objects);
+	free(repo->parsed_objects);
 
 	repo_settings_clear(repo);
 	repo_config_values_clear(&repo->config_values_private_);
 
 	if (repo->config) {
 		git_configset_clear(repo->config);
-		FREE_AND_NULL(repo->config);
+		free(repo->config);
 	}
 
-	if (repo->submodule_cache) {
+	if (repo->submodule_cache)
 		submodule_cache_free(repo->submodule_cache);
-		repo->submodule_cache = NULL;
-	}
 
 	if (repo->index) {
 		discard_index(repo->index);
-		FREE_AND_NULL(repo->index);
+		free(repo->index);
 	}
 
 	if (repo->hook_config_cache) {
 		hook_cache_clear(repo->hook_config_cache);
-		FREE_AND_NULL(repo->hook_config_cache);
+		free(repo->hook_config_cache);
 	}
 	strmap_clear(&repo->event_jobs, 0); /* values are uintptr_t, not heap ptrs */
 	string_list_clear(&repo->disabled_events, 0);
 
 	if (repo->promisor_remote_config) {
 		promisor_remote_clear(repo->promisor_remote_config);
-		FREE_AND_NULL(repo->promisor_remote_config);
+		free(repo->promisor_remote_config);
 	}
 
 	if (repo->remote_state) {
 		remote_state_clear(repo->remote_state);
-		FREE_AND_NULL(repo->remote_state);
+		free(repo->remote_state);
 	}
 
 	if (repo->refs_private) {
 		ref_store_release(repo->refs_private);
-		FREE_AND_NULL(repo->refs_private);
+		free(repo->refs_private);
 	}
 
 	strmap_for_each_entry(&repo->submodule_ref_stores, &iter, e)
@@ -439,6 +436,8 @@ void repo_clear(struct repository *repo)
 	strmap_clear(&repo->worktree_ref_stores, 1);
 
 	repo_clear_path_cache(&repo->cached_paths);
+
+	memset(repo, 0, sizeof(*repo));
 }
 
 int repo_read_index(struct repository *repo)
diff --git a/repository.h b/repository.h
index 11f5c2ed10..2a348012e8 100644
--- a/repository.h
+++ b/repository.h
@@ -258,6 +258,7 @@ void repo_set_ref_storage_format(struct repository *repo,
 void initialize_repository(struct repository *repo);
 RESULT_MUST_BE_USED
 int repo_init(struct repository *r, const char *gitdir, const char *worktree);
+void repo_clear(struct repository *repo);
 
 /*
  * Initialize the repository 'subrepo' as the submodule at the given path. If
@@ -273,7 +274,6 @@ int repo_submodule_init(struct repository *subrepo,
 			struct repository *superproject,
 			const char *path,
 			const struct object_id *treeish_name);
-void repo_clear(struct repository *repo);
 
 /*
  * Populates the repository's index from its index_file, an index struct will
-- 
2.56.0.rc2.329.gd58861e689.dirty
Previous: Patrick SteinhardtNext: Patrick Steinhardt
Message 26 of 32 in “setup: enforce repo passed to `create_repository()` has no state”
  1. 0/7 setup: enforce repo passed to `create_repository()` has no statePatrick Steinhardt, Sep 24, 2026
  2. 1/7 path: drop useless `safe_create_leading_directories_1()`Patrick Steinhardt, Sep 24, 2026
  3. Karthik NayakSep 28, 2026
  4. 2/7 path: introduce `safe_create_leading_directories_no_share_const()`Patrick Steinhardt, Sep 24, 2026
  5. Kaartic SivaraamSep 25, 2026
  6. Patrick SteinhardtSep 28, 2026
  7. Kaartic SivaraamSep 28, 2026
  8. 3/7 builtin/init: refactor messy creation of leading directoriesPatrick Steinhardt, Sep 24, 2026
  9. Kaartic SivaraamSep 25, 2026
  10. 4/7 builtin/init: move handling of "core.sharedRepository" into "setup.c"Patrick Steinhardt, Sep 24, 2026
  11. Kaartic SivaraamSep 25, 2026
  12. Karthik NayakSep 28, 2026
  13. 5/7 builtin/clone: don't apply "core.sharedRepository" to leading dirsPatrick Steinhardt, Sep 24, 2026
  14. 6/7 repository: adapt `repo_clear()` to fully reset the repositoryPatrick Steinhardt, Sep 24, 2026
  15. Karthik NayakSep 28, 2026
  16. Patrick SteinhardtSep 28, 2026
  17. 7/7 setup: enforce that passed-in repo does not carry relevant statePatrick Steinhardt, Sep 24, 2026
  18. Kaartic SivaraamSep 25, 2026
  19. Karthik NayakSep 28, 2026
  20. 0/7 setup: enforce repo passed to `create_repository()` has no statePatrick Steinhardt, Sep 28, 2026
  21. 1/7 path: drop useless `safe_create_leading_directories_1()`Patrick Steinhardt, Sep 28, 2026
  22. 2/7 path: introduce `safe_create_leading_directories_no_share_const()`Patrick Steinhardt, Sep 28, 2026
  23. 3/7 builtin/init: refactor messy creation of leading directoriesPatrick Steinhardt, Sep 28, 2026
  24. 4/7 builtin/init: move handling of "core.sharedRepository" into "setup.c"Patrick Steinhardt, Sep 28, 2026
  25. 5/7 builtin/clone: don't apply "core.sharedRepository" to leading dirsPatrick Steinhardt, Sep 28, 2026
  26. 6/7 repository: adapt `repo_clear()` to fully reset the repositoryPatrick Steinhardt, Sep 28, 2026
  27. 7/7 setup: enforce that passed-in repo does not carry relevant statePatrick Steinhardt, Sep 28, 2026
  28. Kaartic SivaraamSep 28, 2026
  29. Kaartic SivaraamSep 28, 2026
  30. Patrick SteinhardtSep 28, 2026
  31. Junio C HamanoSep 28, 2026
  32. Patrick SteinhardtSep 28, 2026

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.