diff options
| author | Vicent Marti <vicent@github.com> | 2014-03-26 18:29:34 +0100 | 
|---|---|---|
| committer | Vicent Marti <vicent@github.com> | 2014-03-26 18:29:34 +0100 | 
| commit | dc7efa1aef2d3694e7a1955d746d26013402a21d (patch) | |
| tree | 53fe478631e02e372508d981093c2abace29ce42 /src | |
| parent | 77b699e0da3d4a4fed742893bd172f3ee7b936d5 (diff) | |
| parent | 591e82952a2835c3d411ee5abec78be3b0816861 (diff) | |
| download | libgit2-dc7efa1aef2d3694e7a1955d746d26013402a21d.tar.gz | |
Merge pull request #2204 from libgit2/rb/submodule-reference-counting
Make submodules externally refcounted
Diffstat (limited to 'src')
| -rw-r--r-- | src/checkout.c | 40 | ||||
| -rw-r--r-- | src/diff.c | 31 | ||||
| -rw-r--r-- | src/diff_file.c | 17 | ||||
| -rw-r--r-- | src/submodule.c | 219 | ||||
| -rw-r--r-- | src/submodule.h | 8 | 
5 files changed, 220 insertions, 95 deletions
diff --git a/src/checkout.c b/src/checkout.c index f882f3593..468c8dc6e 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -147,19 +147,23 @@ static bool checkout_is_workdir_modified(  		git_submodule *sm;  		unsigned int sm_status = 0;  		const git_oid *sm_oid = NULL; +		bool rval = false; -		if (git_submodule_lookup(&sm, data->repo, wditem->path) < 0 || -			git_submodule_status(&sm_status, sm) < 0) -			return true; - -		if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) +		if (git_submodule_lookup(&sm, data->repo, wditem->path) < 0) { +			giterr_clear();  			return true; +		} -		sm_oid = git_submodule_wd_id(sm); -		if (!sm_oid) -			return false; +		if (git_submodule_status(&sm_status, sm) < 0 || +			GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) +			rval = true; +		else if ((sm_oid = git_submodule_wd_id(sm)) == NULL) +			rval = false; +		else +			rval = (git_oid__cmp(&baseitem->id, sm_oid) != 0); -		return (git_oid__cmp(&baseitem->id, sm_oid) != 0); +		git_submodule_free(sm); +		return rval;  	}  	/* Look at the cache to decide if the workdir is modified.  If not, @@ -324,12 +328,17 @@ static bool submodule_is_config_only(  {  	git_submodule *sm = NULL;  	unsigned int sm_loc = 0; +	bool rval = false; -	if (git_submodule_lookup(&sm, data->repo, path) < 0 || -		git_submodule_location(&sm_loc, sm) < 0 || -		sm_loc == GIT_SUBMODULE_STATUS_IN_CONFIG) +	if (git_submodule_lookup(&sm, data->repo, path) < 0)  		return true; +	if (git_submodule_location(&sm_loc, sm) < 0 || +		sm_loc == GIT_SUBMODULE_STATUS_IN_CONFIG) +		rval = true; + +	git_submodule_free(sm); +  	return false;  } @@ -1258,7 +1267,6 @@ static int checkout_submodule(  	const git_diff_file *file)  {  	int error = 0; -	git_submodule *sm;  	/* Until submodules are supported, UPDATE_ONLY means do nothing here */  	if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) @@ -1269,7 +1277,7 @@ static int checkout_submodule(  			data->opts.dir_mode, GIT_MKDIR_PATH)) < 0)  		return error; -	if ((error = git_submodule_lookup(&sm, data->repo, file->path)) < 0) { +	if ((error = git_submodule_lookup(NULL, data->repo, file->path)) < 0) {  		/* I've observed repos with submodules in the tree that do not  		 * have a .gitmodules - core Git just makes an empty directory  		 */ @@ -1510,7 +1518,7 @@ static int checkout_create_submodules(  	/* initial reload of submodules if .gitmodules was changed */  	if (data->reload_submodules && -		(error = git_submodule_reload_all(data->repo)) < 0) +		(error = git_submodule_reload_all(data->repo, 1)) < 0)  		return error;  	git_vector_foreach(&data->diff->deltas, i, delta) { @@ -1534,7 +1542,7 @@ static int checkout_create_submodules(  	}  	/* final reload once submodules have been updated */ -	return git_submodule_reload_all(data->repo); +	return git_submodule_reload_all(data->repo, 1);  }  static int checkout_lookup_head_tree(git_tree **out, git_repository *repo) diff --git a/src/diff.c b/src/diff.c index dc7735f4f..25c5937e6 100644 --- a/src/diff.c +++ b/src/diff.c @@ -528,12 +528,15 @@ int git_diff__oid_for_file(  	/* calculate OID for file if possible */  	if (S_ISGITLINK(mode)) {  		git_submodule *sm; -		const git_oid *sm_oid; -		if (!git_submodule_lookup(&sm, repo, path) && -			(sm_oid = git_submodule_wd_id(sm)) != NULL) -			git_oid_cpy(oid, sm_oid); -		else { +		memset(oid, 0, sizeof(*oid)); + +		if (!git_submodule_lookup(&sm, repo, path)) { +			const git_oid *sm_oid = git_submodule_wd_id(sm); +			if (sm_oid) +				git_oid_cpy(oid, sm_oid); +			git_submodule_free(sm); +		} else {  			/* if submodule lookup failed probably just in an intermediate  			 * state where some init hasn't happened, so ignore the error  			 */ @@ -615,24 +618,24 @@ static int maybe_modified_submodule(  	}  	if (ign <= 0 && git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL) -		return 0; - -	if ((error = git_submodule__status( +		/* ignore it */; +	else if ((error = git_submodule__status(  			&sm_status, NULL, NULL, found_oid, sub, ign)) < 0) -		return error; +		/* return error below */;  	/* check IS_WD_UNMODIFIED because this case is only used  	 * when the new side of the diff is the working directory  	 */ -	if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status)) +	else if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status))  		*status = GIT_DELTA_MODIFIED;  	/* now that we have a HEAD OID, check if HEAD moved */ -	if ((sm_status & GIT_SUBMODULE_STATUS_IN_WD) != 0 && +	else if ((sm_status & GIT_SUBMODULE_STATUS_IN_WD) != 0 &&  		!git_oid_equal(&info->oitem->id, found_oid))  		*status = GIT_DELTA_MODIFIED; -	return 0; +	git_submodule_free(sub); +	return error;  }  static int maybe_modified( @@ -960,10 +963,8 @@ static int handle_unmatched_new_item(  		delta_type = GIT_DELTA_ADDED;  	else if (nitem->mode == GIT_FILEMODE_COMMIT) { -		git_submodule *sm; -  		/* ignore things that are not actual submodules */ -		if (git_submodule_lookup(&sm, info->repo, nitem->path) != 0) { +		if (git_submodule_lookup(NULL, info->repo, nitem->path) != 0) {  			giterr_clear();  			delta_type = GIT_DELTA_IGNORED;  		} diff --git a/src/diff_file.c b/src/diff_file.c index 7dabf8d6f..b9f92df3f 100644 --- a/src/diff_file.c +++ b/src/diff_file.c @@ -177,11 +177,17 @@ static int diff_file_content_commit_to_str(  		unsigned int sm_status = 0;  		const git_oid *sm_head; -		if ((error = git_submodule_lookup(&sm, fc->repo, fc->file->path)) < 0 || -			(error = git_submodule_status(&sm_status, sm)) < 0) { +		if ((error = git_submodule_lookup(&sm, fc->repo, fc->file->path)) < 0) {  			/* GIT_EEXISTS means a "submodule" that has not been git added */ -			if (error == GIT_EEXISTS) +			if (error == GIT_EEXISTS) { +				giterr_clear();  				error = 0; +			} +			return error; +		} + +		if ((error = git_submodule_status(&sm_status, sm)) < 0) { +			git_submodule_free(sm);  			return error;  		} @@ -196,6 +202,8 @@ static int diff_file_content_commit_to_str(  		if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))  			status = "-dirty"; + +		git_submodule_free(sm);  	}  	git_oid_tostr(oid, sizeof(oid), &fc->file->id); @@ -312,7 +320,8 @@ static int diff_file_content_load_workdir_file(  		error = git_filter_list_apply_to_data(&out, fl, &raw); -		git_buf_free(&raw); +		if (out.ptr != raw.ptr) +			git_buf_free(&raw);  		if (!error) {  			fc->map.len  = out.size; diff --git a/src/submodule.c b/src/submodule.c index 9eaf77dae..769b092a0 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -77,12 +77,12 @@ __KHASH_IMPL(  	str, static kh_inline, const char *, void *, 1,  	str_hash_no_trailing_slash, str_equal_no_trailing_slash); -static int load_submodule_config(git_repository *repo); +static int load_submodule_config(git_repository *repo, bool reload);  static git_config_backend *open_gitmodules(git_repository *, bool, const git_oid *);  static int lookup_head_remote(git_buf *url, git_repository *repo);  static int submodule_get(git_submodule **, git_repository *, const char *, const char *);  static int submodule_load_from_config(const git_config_entry *, void *); -static int submodule_load_from_wd_lite(git_submodule *, const char *, void *); +static int submodule_load_from_wd_lite(git_submodule *);  static int submodule_update_config(git_submodule *, const char *, const char *, bool, bool);  static void submodule_get_index_status(unsigned int *, git_submodule *);  static void submodule_get_wd_status(unsigned int *, git_submodule *, git_repository *, git_submodule_ignore_t); @@ -99,27 +99,55 @@ static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix)  	return git_buf_puts(key, suffix);  } +/* lookup submodule or return ENOTFOUND if it doesn't exist */ +static int submodule_lookup( +	git_submodule **out, +	git_strmap *cache, +	const char *name, +	const char *alternate) +{ +	khiter_t pos; + +	/* lock cache */ + +	pos = git_strmap_lookup_index(cache, name); + +	if (!git_strmap_valid_index(cache, pos) && alternate) +		pos = git_strmap_lookup_index(cache, alternate); + +	if (!git_strmap_valid_index(cache, pos)) { +		/* unlock cache */ +		return GIT_ENOTFOUND; /* don't set error - caller will cope */ +	} + +	if (out != NULL) { +		git_submodule *sm = git_strmap_value_at(cache, pos); +		GIT_REFCOUNT_INC(sm); +		*out = sm; +	} + +	/* unlock cache */ + +	return 0; +} +  /*   * PUBLIC APIS   */  int git_submodule_lookup( -	git_submodule **sm_ptr, /* NULL if user only wants to test existence */ +	git_submodule **out, /* NULL if user only wants to test existence */  	git_repository *repo, -	const char *name)       /* trailing slash is allowed */ +	const char *name)    /* trailing slash is allowed */  {  	int error; -	khiter_t pos;  	assert(repo && name); -	if ((error = load_submodule_config(repo)) < 0) +	if ((error = load_submodule_config(repo, false)) < 0)  		return error; -	pos = git_strmap_lookup_index(repo->submodules, name); - -	if (!git_strmap_valid_index(repo->submodules, pos)) { -		error = GIT_ENOTFOUND; +	if ((error = submodule_lookup(out, repo->submodules, name, NULL)) < 0) {  		/* check if a plausible submodule exists at path */  		if (git_repository_workdir(repo)) { @@ -137,14 +165,9 @@ int git_submodule_lookup(  		giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ?  			"No submodule named '%s'" :  			"Submodule '%s' has not been added yet", name); - -		return error;  	} -	if (sm_ptr) -		*sm_ptr = git_strmap_value_at(repo->submodules, pos); - -	return 0; +	return error;  }  int git_submodule_foreach( @@ -159,7 +182,7 @@ int git_submodule_foreach(  	assert(repo && callback); -	if ((error = load_submodule_config(repo)) < 0) +	if ((error = load_submodule_config(repo, true)) < 0)  		return error;  	git_strmap_foreach_value(repo->submodules, sm, { @@ -198,12 +221,15 @@ void git_submodule_config_free(git_repository *repo)  	if (smcfg == NULL)  		return; -	git_strmap_foreach_value(smcfg, sm, { git_submodule_free(sm); }); +	git_strmap_foreach_value(smcfg, sm, { +		sm->repo = NULL; /* disconnect from repo */; +		git_submodule_free(sm); +	});  	git_strmap_free(smcfg);  }  int git_submodule_add_setup( -	git_submodule **submodule, +	git_submodule **out,  	git_repository *repo,  	const char *url,  	const char *path, @@ -211,7 +237,7 @@ int git_submodule_add_setup(  {  	int error = 0;  	git_config_backend *mods = NULL; -	git_submodule *sm; +	git_submodule *sm = NULL;  	git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT;  	git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;  	git_repository *subrepo = NULL; @@ -223,6 +249,7 @@ int git_submodule_add_setup(  	if (git_submodule_lookup(&sm, repo, path) < 0)  		giterr_clear();  	else { +		git_submodule_free(sm);  		giterr_set(GITERR_SUBMODULE,  			"Attempt to add a submodule that already exists");  		return GIT_EEXISTS; @@ -307,12 +334,16 @@ int git_submodule_add_setup(  	/* add submodule to hash and "reload" it */  	if (!(error = submodule_get(&sm, repo, path, NULL)) && -		!(error = git_submodule_reload(sm))) +		!(error = git_submodule_reload(sm, false)))  		error = git_submodule_init(sm, false);  cleanup: -	if (submodule != NULL) -		*submodule = !error ? sm : NULL; +	if (error && sm) { +		git_submodule_free(sm); +		sm = NULL; +	} +	if (out != NULL) +		*out = sm;  	if (mods != NULL)  		git_config_file_free(mods); @@ -775,11 +806,60 @@ int git_submodule_open(git_repository **subrepo, git_submodule *sm)  	return git_submodule__open(subrepo, sm, false);  } -int git_submodule_reload_all(git_repository *repo) +static void submodule_cache_remove_item( +	git_strmap *cache, +	const char *name, +	git_submodule *expected, +	bool free_after_remove)  { +	khiter_t pos; +	git_submodule *found; + +	if (!cache) +		return; + +	pos = git_strmap_lookup_index(cache, name); + +	if (!git_strmap_valid_index(cache, pos)) +		return; + +	found = git_strmap_value_at(cache, pos); + +	if (expected && found != expected) +		return; + +	git_strmap_set_value_at(cache, pos, NULL); +	git_strmap_delete_at(cache, pos); + +	if (free_after_remove) +		git_submodule_free(found); +} + +int git_submodule_reload_all(git_repository *repo, int force) +{ +	int error = 0; +	git_submodule *sm; + +	GIT_UNUSED(force);  	assert(repo); -	git_submodule_config_free(repo); -	return load_submodule_config(repo); + +	if (repo->submodules) +		git_strmap_foreach_value(repo->submodules, sm, { sm->flags = 0; }); + +	error = load_submodule_config(repo, true); + +	git_strmap_foreach_value(repo->submodules, sm, { +		git_strmap *cache = repo->submodules; + +		if ((sm->flags & GIT_SUBMODULE_STATUS__IN_FLAGS) == 0) { +			submodule_cache_remove_item(cache, sm->name, sm, true); + +			if (sm->path != sm->name) +				submodule_cache_remove_item(cache, sm->path, sm, true); +		} +	}); + +	return error;  }  static void submodule_update_from_index_entry( @@ -855,11 +935,14 @@ static int submodule_update_head(git_submodule *submodule)  	return 0;  } -int git_submodule_reload(git_submodule *submodule) + +int git_submodule_reload(git_submodule *submodule, int force)  {  	int error = 0;  	git_config_backend *mods; +	GIT_UNUSED(force); +  	assert(submodule);  	/* refresh index data */ @@ -870,6 +953,10 @@ int git_submodule_reload(git_submodule *submodule)  	if ((error = submodule_update_head(submodule)) < 0)  		return error; +	/* done if bare */ +	if (git_repository_is_bare(submodule->repo)) +		return error; +  	/* refresh config data */  	mods = open_gitmodules(submodule->repo, false, NULL);  	if (mods != NULL) { @@ -893,11 +980,9 @@ int git_submodule_reload(git_submodule *submodule)  	}  	/* refresh wd data */ -	submodule->flags = submodule->flags & -		~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID); +	submodule->flags &= ~GIT_SUBMODULE_STATUS__ALL_WD_FLAGS; -	return submodule_load_from_wd_lite( -		submodule, submodule->path, submodule->repo); +	return submodule_load_from_wd_lite(submodule);  }  static void submodule_copy_oid_maybe( @@ -1026,6 +1111,13 @@ static void submodule_release(git_submodule *sm)  	if (!sm)  		return; +	if (sm->repo) { +		git_strmap *cache = sm->repo->submodules; +		submodule_cache_remove_item(cache, sm->name, sm, false); +		if (sm->path != sm->name) +			submodule_cache_remove_item(cache, sm->path, sm, false); +	} +  	if (sm->path != sm->name)  		git__free(sm->path);  	git__free(sm->name); @@ -1042,17 +1134,15 @@ void git_submodule_free(git_submodule *sm)  }  static int submodule_get( -	git_submodule **sm_ptr, +	git_submodule **out,  	git_repository *repo,  	const char *name,  	const char *alternate)  { +	int error = 0;  	git_strmap *smcfg = repo->submodules;  	khiter_t pos;  	git_submodule *sm; -	int error; - -	assert(repo && name);  	pos = git_strmap_lookup_index(smcfg, name); @@ -1068,22 +1158,28 @@ static int submodule_get(  		 */  		pos = kh_put(str, smcfg, sm->name, &error); -		if (error < 0) { -			git_submodule_free(sm); -			sm = NULL; -		} else if (error == 0) { +		if (error < 0) +			goto done; +		else if (error == 0) {  			git_submodule_free(sm);  			sm = git_strmap_value_at(smcfg, pos);  		} else { +			error = 0;  			git_strmap_set_value_at(smcfg, pos, sm);  		}  	} else {  		sm = git_strmap_value_at(smcfg, pos);  	} -	*sm_ptr = sm; +done: +	if (error < 0) +		git_submodule_free(sm); +	else if (out) { +		GIT_REFCOUNT_INC(sm); +		*out = sm; +	} -	return (sm != NULL) ? 0 : -1; +	return error;  }  static int submodule_config_error(const char *property, const char *value) @@ -1143,7 +1239,7 @@ static int submodule_load_from_config(  	const char *namestart, *property, *alternate = NULL;  	const char *key = entry->name, *value = entry->value, *path;  	git_buf name = GIT_BUF_INIT; -	git_submodule *sm; +	git_submodule *sm = NULL;  	int error = 0;  	if (git__prefixcmp(key, "submodule.") != 0) @@ -1230,8 +1326,8 @@ static int submodule_load_from_config(  		sm->update_default = sm->update;  	}  	else if (strcasecmp(property, "fetchRecurseSubmodules") == 0) { -		if (git_submodule_parse_recurse(&sm->fetch_recurse, value) < 0) -			return -1; +		if ((error = git_submodule_parse_recurse(&sm->fetch_recurse, value)) < 0) +			goto done;  		sm->fetch_recurse_default = sm->fetch_recurse;  	}  	else if (strcasecmp(property, "ignore") == 0) { @@ -1242,20 +1338,15 @@ static int submodule_load_from_config(  	/* ignore other unknown submodule properties */  done: +	git_submodule_free(sm); /* offset refcount inc from submodule_get() */  	git_buf_free(&name);  	return error;  } -static int submodule_load_from_wd_lite( -	git_submodule *sm, const char *name, void *payload) +static int submodule_load_from_wd_lite(git_submodule *sm)  {  	git_buf path = GIT_BUF_INIT; -	GIT_UNUSED(name); GIT_UNUSED(payload); - -	if (git_repository_is_bare(sm->repo)) -		return 0; -  	if (git_buf_joinpath(&path, git_repository_workdir(sm->repo), sm->path) < 0)  		return -1; @@ -1293,8 +1384,10 @@ static int load_submodule_config_from_index(  			else  				sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;  		} else if (S_ISGITLINK(entry->mode)) { -			if (!submodule_get(&sm, repo, entry->path, NULL)) +			if (!submodule_get(&sm, repo, entry->path, NULL)) {  				submodule_update_from_index_entry(sm, entry); +				git_submodule_free(sm); +			}  		} else if (strcmp(entry->path, GIT_MODULES_FILE) == 0)  			git_oid_cpy(gitmodules_oid, &entry->id);  	} @@ -1339,9 +1432,11 @@ static int load_submodule_config_from_head(  			else  				sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;  		} else if (S_ISGITLINK(entry->mode)) { -			if (!submodule_get(&sm, repo, entry->path, NULL)) +			if (!submodule_get(&sm, repo, entry->path, NULL)) {  				submodule_update_from_head_data(  					sm, entry->mode, &entry->id); +				git_submodule_free(sm); +			}  		} else if (strcmp(entry->path, GIT_MODULES_FILE) == 0 &&  				   git_oid_iszero(gitmodules_oid)) {  			git_oid_cpy(gitmodules_oid, &entry->id); @@ -1395,13 +1490,13 @@ static git_config_backend *open_gitmodules(  	return mods;  } -static int load_submodule_config(git_repository *repo) +static int load_submodule_config(git_repository *repo, bool reload)  {  	int error;  	git_oid gitmodules_oid;  	git_config_backend *mods = NULL; -	if (repo->submodules) +	if (!reload && repo->submodules)  		return 0;  	memset(&gitmodules_oid, 0, sizeof(gitmodules_oid)); @@ -1414,6 +1509,8 @@ static int load_submodule_config(git_repository *repo)  		GITERR_CHECK_ALLOC(repo->submodules);  	} +	/* TODO: only do the following if the sources appear modified */ +  	/* add submodule information from index */  	if ((error = load_submodule_config_from_index(repo, &gitmodules_oid)) < 0) @@ -1433,8 +1530,16 @@ static int load_submodule_config(git_repository *repo)  	/* shallow scan submodules in work tree */ -	if (!git_repository_is_bare(repo)) -		error = git_submodule_foreach(repo, submodule_load_from_wd_lite, NULL); +	if (!git_repository_is_bare(repo)) { +		git_submodule *sm; + +		git_strmap_foreach_value(repo->submodules, sm, { +			sm->flags &= ~GIT_SUBMODULE_STATUS__ALL_WD_FLAGS; +		}); +		git_strmap_foreach_value(repo->submodules, sm, { +			submodule_load_from_wd_lite(sm); +		}); +	}  cleanup:  	if (mods != NULL) diff --git a/src/submodule.h b/src/submodule.h index 5e532e1ae..053cb61e0 100644 --- a/src/submodule.h +++ b/src/submodule.h @@ -111,6 +111,11 @@ enum {  	GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES = (1u << 27),  }; +#define GIT_SUBMODULE_STATUS__ALL_WD_FLAGS \ +	(GIT_SUBMODULE_STATUS_IN_WD | \ +	 GIT_SUBMODULE_STATUS__WD_OID_VALID | \ +	 GIT_SUBMODULE_STATUS__WD_FLAGS) +  #define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \  	((S) & ~(0xFFFFFFFFu << 20)) @@ -128,9 +133,6 @@ extern int git_submodule_open_bare(  	git_repository **repo,  	git_submodule *submodule); -/* Release reference to submodule object - not currently for external use */ -extern void git_submodule_free(git_submodule *sm); -  extern int git_submodule_parse_ignore(  	git_submodule_ignore_t *out, const char *value);  extern int git_submodule_parse_update(  | 
