Skip to content

Commit dc7efa1

Browse files
author
Vicent Marti
committed
Merge pull request libgit2#2204 from libgit2/rb/submodule-reference-counting
Make submodules externally refcounted
2 parents 77b699e + 591e829 commit dc7efa1

File tree

15 files changed

+461
-364
lines changed

15 files changed

+461
-364
lines changed

examples/status.c

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -363,18 +363,21 @@ static void print_short(git_repository *repo, git_status_list *status)
363363
unsigned int smstatus = 0;
364364

365365
if (!git_submodule_lookup(
366-
&sm, repo, s->index_to_workdir->new_file.path) &&
367-
!git_submodule_status(&smstatus, sm))
368-
{
369-
if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED)
370-
extra = " (new commits)";
371-
else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED)
372-
extra = " (modified content)";
373-
else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED)
374-
extra = " (modified content)";
375-
else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED)
376-
extra = " (untracked content)";
366+
&sm, repo, s->index_to_workdir->new_file.path)) {
367+
368+
if (!git_submodule_status(&smstatus, sm)) {
369+
if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED)
370+
extra = " (new commits)";
371+
else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED)
372+
extra = " (modified content)";
373+
else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED)
374+
extra = " (modified content)";
375+
else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED)
376+
extra = " (untracked content)";
377+
}
377378
}
379+
380+
git_submodule_free(sm);
378381
}
379382

380383
/**

include/git2/submodule.h

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -123,21 +123,27 @@ typedef enum {
123123
* There may or may not be anything else at that path, but nothing that
124124
* looks like a submodule. In this case, this returns GIT_ENOTFOUND.
125125
*
126-
* The submodule object is owned by the containing repo and will be freed
127-
* when the repo is freed. The caller need not free the submodule.
126+
* You must call `git_submodule_free` when done with the submodule.
128127
*
129-
* @param submodule Pointer to submodule description object pointer..
130-
* @param repo The repository.
131-
* @param name The name of the submodule. Trailing slashes will be ignored.
128+
* @param out Output ptr to submodule; pass NULL to just get return code
129+
* @param repo The parent repository
130+
* @param name The name of or path to the submodule; trailing slashes okay
132131
* @return 0 on success, GIT_ENOTFOUND if submodule does not exist,
133-
* GIT_EEXISTS if submodule exists in working directory only, -1 on
134-
* other errors.
132+
* GIT_EEXISTS if submodule exists in working directory only,
133+
* -1 on other errors.
135134
*/
136135
GIT_EXTERN(int) git_submodule_lookup(
137-
git_submodule **submodule,
136+
git_submodule **out,
138137
git_repository *repo,
139138
const char *name);
140139

140+
/**
141+
* Release a submodule
142+
*
143+
* @param submodule Submodule object
144+
*/
145+
GIT_EXTERN(void) git_submodule_free(git_submodule *submodule);
146+
141147
/**
142148
* Iterate over all tracked submodules of a repository.
143149
*
@@ -175,17 +181,19 @@ GIT_EXTERN(int) git_submodule_foreach(
175181
* `git_submodule_add_finalize()` to wrap up adding the new submodule and
176182
* .gitmodules to the index to be ready to commit.
177183
*
178-
* @param submodule The newly created submodule ready to open for clone
179-
* @param repo Superproject repository to contain the new submodule
180-
* @param url URL for the submodules remote
184+
* You must call `git_submodule_free` on the submodule object when done.
185+
*
186+
* @param out The newly created submodule ready to open for clone
187+
* @param repo The repository in which you want to create the submodule
188+
* @param url URL for the submodule's remote
181189
* @param path Path at which the submodule should be created
182190
* @param use_gitlink Should workdir contain a gitlink to the repo in
183191
* .git/modules vs. repo directly in workdir.
184192
* @return 0 on success, GIT_EEXISTS if submodule already exists,
185193
* -1 on other errors.
186194
*/
187195
GIT_EXTERN(int) git_submodule_add_setup(
188-
git_submodule **submodule,
196+
git_submodule **out,
189197
git_repository *repo,
190198
const char *url,
191199
const char *path,
@@ -493,15 +501,23 @@ GIT_EXTERN(int) git_submodule_open(
493501
*
494502
* Call this to reread cached submodule information for this submodule if
495503
* you have reason to believe that it has changed.
504+
*
505+
* @param submodule The submodule to reload
506+
* @param force Force reload even if the data doesn't seem out of date
507+
* @return 0 on success, <0 on error
496508
*/
497-
GIT_EXTERN(int) git_submodule_reload(git_submodule *submodule);
509+
GIT_EXTERN(int) git_submodule_reload(git_submodule *submodule, int force);
498510

499511
/**
500512
* Reread all submodule info.
501513
*
502514
* Call this to reload all cached submodule information for the repo.
515+
*
516+
* @param repo The repository to reload submodule data for
517+
* @param force Force full reload even if the data doesn't seem out of date
518+
* @return 0 on success, <0 on error
503519
*/
504-
GIT_EXTERN(int) git_submodule_reload_all(git_repository *repo);
520+
GIT_EXTERN(int) git_submodule_reload_all(git_repository *repo, int force);
505521

506522
/**
507523
* Get the status for a submodule.

src/checkout.c

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -147,19 +147,23 @@ static bool checkout_is_workdir_modified(
147147
git_submodule *sm;
148148
unsigned int sm_status = 0;
149149
const git_oid *sm_oid = NULL;
150+
bool rval = false;
150151

151-
if (git_submodule_lookup(&sm, data->repo, wditem->path) < 0 ||
152-
git_submodule_status(&sm_status, sm) < 0)
153-
return true;
154-
155-
if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
152+
if (git_submodule_lookup(&sm, data->repo, wditem->path) < 0) {
153+
giterr_clear();
156154
return true;
155+
}
157156

158-
sm_oid = git_submodule_wd_id(sm);
159-
if (!sm_oid)
160-
return false;
157+
if (git_submodule_status(&sm_status, sm) < 0 ||
158+
GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
159+
rval = true;
160+
else if ((sm_oid = git_submodule_wd_id(sm)) == NULL)
161+
rval = false;
162+
else
163+
rval = (git_oid__cmp(&baseitem->id, sm_oid) != 0);
161164

162-
return (git_oid__cmp(&baseitem->id, sm_oid) != 0);
165+
git_submodule_free(sm);
166+
return rval;
163167
}
164168

165169
/* Look at the cache to decide if the workdir is modified. If not,
@@ -324,12 +328,17 @@ static bool submodule_is_config_only(
324328
{
325329
git_submodule *sm = NULL;
326330
unsigned int sm_loc = 0;
331+
bool rval = false;
327332

328-
if (git_submodule_lookup(&sm, data->repo, path) < 0 ||
329-
git_submodule_location(&sm_loc, sm) < 0 ||
330-
sm_loc == GIT_SUBMODULE_STATUS_IN_CONFIG)
333+
if (git_submodule_lookup(&sm, data->repo, path) < 0)
331334
return true;
332335

336+
if (git_submodule_location(&sm_loc, sm) < 0 ||
337+
sm_loc == GIT_SUBMODULE_STATUS_IN_CONFIG)
338+
rval = true;
339+
340+
git_submodule_free(sm);
341+
333342
return false;
334343
}
335344

@@ -1258,7 +1267,6 @@ static int checkout_submodule(
12581267
const git_diff_file *file)
12591268
{
12601269
int error = 0;
1261-
git_submodule *sm;
12621270

12631271
/* Until submodules are supported, UPDATE_ONLY means do nothing here */
12641272
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
@@ -1269,7 +1277,7 @@ static int checkout_submodule(
12691277
data->opts.dir_mode, GIT_MKDIR_PATH)) < 0)
12701278
return error;
12711279

1272-
if ((error = git_submodule_lookup(&sm, data->repo, file->path)) < 0) {
1280+
if ((error = git_submodule_lookup(NULL, data->repo, file->path)) < 0) {
12731281
/* I've observed repos with submodules in the tree that do not
12741282
* have a .gitmodules - core Git just makes an empty directory
12751283
*/
@@ -1510,7 +1518,7 @@ static int checkout_create_submodules(
15101518

15111519
/* initial reload of submodules if .gitmodules was changed */
15121520
if (data->reload_submodules &&
1513-
(error = git_submodule_reload_all(data->repo)) < 0)
1521+
(error = git_submodule_reload_all(data->repo, 1)) < 0)
15141522
return error;
15151523

15161524
git_vector_foreach(&data->diff->deltas, i, delta) {
@@ -1534,7 +1542,7 @@ static int checkout_create_submodules(
15341542
}
15351543

15361544
/* final reload once submodules have been updated */
1537-
return git_submodule_reload_all(data->repo);
1545+
return git_submodule_reload_all(data->repo, 1);
15381546
}
15391547

15401548
static int checkout_lookup_head_tree(git_tree **out, git_repository *repo)

src/diff.c

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -528,12 +528,15 @@ int git_diff__oid_for_file(
528528
/* calculate OID for file if possible */
529529
if (S_ISGITLINK(mode)) {
530530
git_submodule *sm;
531-
const git_oid *sm_oid;
532531

533-
if (!git_submodule_lookup(&sm, repo, path) &&
534-
(sm_oid = git_submodule_wd_id(sm)) != NULL)
535-
git_oid_cpy(oid, sm_oid);
536-
else {
532+
memset(oid, 0, sizeof(*oid));
533+
534+
if (!git_submodule_lookup(&sm, repo, path)) {
535+
const git_oid *sm_oid = git_submodule_wd_id(sm);
536+
if (sm_oid)
537+
git_oid_cpy(oid, sm_oid);
538+
git_submodule_free(sm);
539+
} else {
537540
/* if submodule lookup failed probably just in an intermediate
538541
* state where some init hasn't happened, so ignore the error
539542
*/
@@ -615,24 +618,24 @@ static int maybe_modified_submodule(
615618
}
616619

617620
if (ign <= 0 && git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL)
618-
return 0;
619-
620-
if ((error = git_submodule__status(
621+
/* ignore it */;
622+
else if ((error = git_submodule__status(
621623
&sm_status, NULL, NULL, found_oid, sub, ign)) < 0)
622-
return error;
624+
/* return error below */;
623625

624626
/* check IS_WD_UNMODIFIED because this case is only used
625627
* when the new side of the diff is the working directory
626628
*/
627-
if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status))
629+
else if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status))
628630
*status = GIT_DELTA_MODIFIED;
629631

630632
/* now that we have a HEAD OID, check if HEAD moved */
631-
if ((sm_status & GIT_SUBMODULE_STATUS_IN_WD) != 0 &&
633+
else if ((sm_status & GIT_SUBMODULE_STATUS_IN_WD) != 0 &&
632634
!git_oid_equal(&info->oitem->id, found_oid))
633635
*status = GIT_DELTA_MODIFIED;
634636

635-
return 0;
637+
git_submodule_free(sub);
638+
return error;
636639
}
637640

638641
static int maybe_modified(
@@ -960,10 +963,8 @@ static int handle_unmatched_new_item(
960963
delta_type = GIT_DELTA_ADDED;
961964

962965
else if (nitem->mode == GIT_FILEMODE_COMMIT) {
963-
git_submodule *sm;
964-
965966
/* ignore things that are not actual submodules */
966-
if (git_submodule_lookup(&sm, info->repo, nitem->path) != 0) {
967+
if (git_submodule_lookup(NULL, info->repo, nitem->path) != 0) {
967968
giterr_clear();
968969
delta_type = GIT_DELTA_IGNORED;
969970
}

src/diff_file.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,17 @@ static int diff_file_content_commit_to_str(
177177
unsigned int sm_status = 0;
178178
const git_oid *sm_head;
179179

180-
if ((error = git_submodule_lookup(&sm, fc->repo, fc->file->path)) < 0 ||
181-
(error = git_submodule_status(&sm_status, sm)) < 0) {
180+
if ((error = git_submodule_lookup(&sm, fc->repo, fc->file->path)) < 0) {
182181
/* GIT_EEXISTS means a "submodule" that has not been git added */
183-
if (error == GIT_EEXISTS)
182+
if (error == GIT_EEXISTS) {
183+
giterr_clear();
184184
error = 0;
185+
}
186+
return error;
187+
}
188+
189+
if ((error = git_submodule_status(&sm_status, sm)) < 0) {
190+
git_submodule_free(sm);
185191
return error;
186192
}
187193

@@ -196,6 +202,8 @@ static int diff_file_content_commit_to_str(
196202

197203
if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
198204
status = "-dirty";
205+
206+
git_submodule_free(sm);
199207
}
200208

201209
git_oid_tostr(oid, sizeof(oid), &fc->file->id);
@@ -312,7 +320,8 @@ static int diff_file_content_load_workdir_file(
312320

313321
error = git_filter_list_apply_to_data(&out, fl, &raw);
314322

315-
git_buf_free(&raw);
323+
if (out.ptr != raw.ptr)
324+
git_buf_free(&raw);
316325

317326
if (!error) {
318327
fc->map.len = out.size;

0 commit comments

Comments
 (0)