Skip to content

Commit 26b9e48

Browse files
authored
Merge pull request libgit2#5570 from libgit2/pks/refdb-refactorings
refdb: a set of preliminary refactorings for the reftable backend
2 parents ae30009 + 3498744 commit 26b9e48

File tree

5 files changed

+254
-174
lines changed

5 files changed

+254
-174
lines changed

src/refdb.c

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
#include "reflog.h"
1818
#include "posix.h"
1919

20+
#define DEFAULT_NESTING_LEVEL 5
21+
#define MAX_NESTING_LEVEL 10
22+
2023
int git_refdb_new(git_refdb **out, git_repository *repo)
2124
{
2225
git_refdb *db;
@@ -134,6 +137,59 @@ int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name)
134137
return 0;
135138
}
136139

140+
int git_refdb_resolve(
141+
git_reference **out,
142+
git_refdb *db,
143+
const char *ref_name,
144+
int max_nesting)
145+
{
146+
git_reference *ref = NULL;
147+
int error = 0, nesting;
148+
149+
*out = NULL;
150+
151+
if (max_nesting > MAX_NESTING_LEVEL)
152+
max_nesting = MAX_NESTING_LEVEL;
153+
else if (max_nesting < 0)
154+
max_nesting = DEFAULT_NESTING_LEVEL;
155+
156+
if ((error = git_refdb_lookup(&ref, db, ref_name)) < 0)
157+
goto out;
158+
159+
for (nesting = 0; nesting < max_nesting; nesting++) {
160+
git_reference *resolved;
161+
162+
if (ref->type == GIT_REFERENCE_DIRECT)
163+
break;
164+
165+
if ((error = git_refdb_lookup(&resolved, db, git_reference_symbolic_target(ref))) < 0) {
166+
/* If we found a symbolic reference with a nonexistent target, return it. */
167+
if (error == GIT_ENOTFOUND) {
168+
error = 0;
169+
*out = ref;
170+
ref = NULL;
171+
}
172+
goto out;
173+
}
174+
175+
git_reference_free(ref);
176+
ref = resolved;
177+
}
178+
179+
if (ref->type != GIT_REFERENCE_DIRECT && max_nesting != 0) {
180+
git_error_set(GIT_ERROR_REFERENCE,
181+
"cannot resolve reference (>%u levels deep)", max_nesting);
182+
error = -1;
183+
goto out;
184+
}
185+
186+
*out = ref;
187+
ref = NULL;
188+
out:
189+
git_reference_free(ref);
190+
return error;
191+
}
192+
137193
int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob)
138194
{
139195
int error;
@@ -231,6 +287,85 @@ int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name)
231287
return 0;
232288
}
233289

290+
int git_refdb_should_write_reflog(int *out, git_refdb *db, const git_reference *ref)
291+
{
292+
int error, logall;
293+
294+
error = git_repository__configmap_lookup(&logall, db->repo, GIT_CONFIGMAP_LOGALLREFUPDATES);
295+
if (error < 0)
296+
return error;
297+
298+
/* Defaults to the opposite of the repo being bare */
299+
if (logall == GIT_LOGALLREFUPDATES_UNSET)
300+
logall = !git_repository_is_bare(db->repo);
301+
302+
*out = 0;
303+
switch (logall) {
304+
case GIT_LOGALLREFUPDATES_FALSE:
305+
*out = 0;
306+
break;
307+
308+
case GIT_LOGALLREFUPDATES_TRUE:
309+
/* Only write if it already has a log,
310+
* or if it's under heads/, remotes/ or notes/
311+
*/
312+
*out = git_refdb_has_log(db, ref->name) ||
313+
!git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) ||
314+
!git__strcmp(ref->name, GIT_HEAD_FILE) ||
315+
!git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR) ||
316+
!git__prefixcmp(ref->name, GIT_REFS_NOTES_DIR);
317+
break;
318+
319+
case GIT_LOGALLREFUPDATES_ALWAYS:
320+
*out = 1;
321+
break;
322+
}
323+
324+
return 0;
325+
}
326+
327+
int git_refdb_should_write_head_reflog(int *out, git_refdb *db, const git_reference *ref)
328+
{
329+
git_reference *head = NULL, *resolved = NULL;
330+
const char *name;
331+
int error;
332+
333+
*out = 0;
334+
335+
if (ref->type == GIT_REFERENCE_SYMBOLIC) {
336+
error = 0;
337+
goto out;
338+
}
339+
340+
if ((error = git_refdb_lookup(&head, db, GIT_HEAD_FILE)) < 0)
341+
goto out;
342+
343+
if (git_reference_type(head) == GIT_REFERENCE_DIRECT)
344+
goto out;
345+
346+
/* Go down the symref chain until we find the branch */
347+
if ((error = git_refdb_resolve(&resolved, db, git_reference_symbolic_target(head), -1)) < 0) {
348+
if (error != GIT_ENOTFOUND)
349+
goto out;
350+
error = 0;
351+
name = git_reference_symbolic_target(head);
352+
} else if (git_reference_type(resolved) == GIT_REFERENCE_SYMBOLIC) {
353+
name = git_reference_symbolic_target(resolved);
354+
} else {
355+
name = git_reference_name(resolved);
356+
}
357+
358+
if (strcmp(name, ref->name))
359+
goto out;
360+
361+
*out = 1;
362+
363+
out:
364+
git_reference_free(resolved);
365+
git_reference_free(head);
366+
return error;
367+
}
368+
234369
int git_refdb_has_log(git_refdb *db, const char *refname)
235370
{
236371
assert(db && refname);

src/refdb.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,31 @@ int git_refdb_lookup(
3030
git_refdb *refdb,
3131
const char *ref_name);
3232

33+
/**
34+
* Resolve the reference by following symbolic references.
35+
*
36+
* Given a reference name, this function will follow any symbolic references up
37+
* to `max_nesting` deep and return the resolved direct reference. If any of
38+
* the intermediate symbolic references points to a non-existing reference,
39+
* then that symbolic reference is returned instead with an error code of `0`.
40+
* If the given reference is a direct reference already, it is returned
41+
* directly.
42+
*
43+
* If `max_nesting` is `0`, the reference will not be resolved. If it's
44+
* negative, it will be set to the default resolve depth which is `5`.
45+
*
46+
* @param out Pointer to store the result in.
47+
* @param db The refdb to use for resolving the reference.
48+
* @param ref_name The reference name to lookup and resolve.
49+
* @param max_nesting The maximum nesting depth.
50+
* @return `0` on success, a negative error code otherwise.
51+
*/
52+
int git_refdb_resolve(
53+
git_reference **out,
54+
git_refdb *db,
55+
const char *ref_name,
56+
int max_nesting);
57+
3358
int git_refdb_rename(
3459
git_reference **out,
3560
git_refdb *db,
@@ -50,6 +75,50 @@ int git_refdb_delete(git_refdb *refdb, const char *ref_name, const git_oid *old_
5075
int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name);
5176
int git_refdb_reflog_write(git_reflog *reflog);
5277

78+
/**
79+
* Determine whether a reflog entry should be created for the given reference.
80+
*
81+
* Whether or not writing to a reference should create a reflog entry is
82+
* dependent on a number of things. Most importantly, there's the
83+
* "core.logAllRefUpdates" setting that controls in which situations a
84+
* reference should get a corresponding reflog entry. The following values for
85+
* it are understood:
86+
*
87+
* - "false": Do not log reference updates.
88+
*
89+
* - "true": Log normal reference updates. This will write entries for
90+
* references in "refs/heads", "refs/remotes", "refs/notes" and
91+
* "HEAD" or if the reference already has a log entry.
92+
*
93+
* - "always": Always create a reflog entry.
94+
*
95+
* If unset, the value will default to "true" for non-bare repositories and
96+
* "false" for bare ones.
97+
*
98+
* @param out pointer to which the result will be written, `1` means a reflog
99+
* entry should be written, `0` means none should be written.
100+
* @param db The refdb to decide this for.
101+
* @param ref The reference one wants to check.
102+
* @return `0` on success, a negative error code otherwise.
103+
*/
104+
int git_refdb_should_write_reflog(int *out, git_refdb *db, const git_reference *ref);
105+
106+
/**
107+
* Determine whether a reflog entry should be created for HEAD if creating one
108+
* for the given reference
109+
*
110+
* In case the given reference is being pointed to by HEAD, then creating a
111+
* reflog entry for this reference also requires us to create a corresponding
112+
* reflog entry for HEAD. This function can be used to determine that scenario.
113+
*
114+
* @param out pointer to which the result will be written, `1` means a reflog
115+
* entry should be written, `0` means none should be written.
116+
* @param db The refdb to decide this for.
117+
* @param ref The reference one wants to check.
118+
* @return `0` on success, a negative error code otherwise.
119+
*/
120+
int git_refdb_should_write_head_reflog(int *out, git_refdb *db, const git_reference *ref);
121+
53122
int git_refdb_has_log(git_refdb *db, const char *refname);
54123
int git_refdb_ensure_log(git_refdb *refdb, const char *refname);
55124

src/refdb_fs.c

Lines changed: 17 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,44 +1128,6 @@ static int packed_delete(refdb_fs_backend *backend, const char *ref_name)
11281128
}
11291129

11301130
static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *author, const char *message);
1131-
static int has_reflog(git_repository *repo, const char *name);
1132-
1133-
static int should_write_reflog(int *write, git_repository *repo, const char *name)
1134-
{
1135-
int error, logall;
1136-
1137-
error = git_repository__configmap_lookup(&logall, repo, GIT_CONFIGMAP_LOGALLREFUPDATES);
1138-
if (error < 0)
1139-
return error;
1140-
1141-
/* Defaults to the opposite of the repo being bare */
1142-
if (logall == GIT_LOGALLREFUPDATES_UNSET)
1143-
logall = !git_repository_is_bare(repo);
1144-
1145-
*write = 0;
1146-
switch (logall) {
1147-
case GIT_LOGALLREFUPDATES_FALSE:
1148-
*write = 0;
1149-
break;
1150-
1151-
case GIT_LOGALLREFUPDATES_TRUE:
1152-
/* Only write if it already has a log,
1153-
* or if it's under heads/, remotes/ or notes/
1154-
*/
1155-
*write = has_reflog(repo, name) ||
1156-
!git__prefixcmp(name, GIT_REFS_HEADS_DIR) ||
1157-
!git__strcmp(name, GIT_HEAD_FILE) ||
1158-
!git__prefixcmp(name, GIT_REFS_REMOTES_DIR) ||
1159-
!git__prefixcmp(name, GIT_REFS_NOTES_DIR);
1160-
break;
1161-
1162-
case GIT_LOGALLREFUPDATES_ALWAYS:
1163-
*write = 1;
1164-
break;
1165-
}
1166-
1167-
return 0;
1168-
}
11691131

11701132
static int cmp_old_ref(int *cmp, git_refdb_backend *backend, const char *name,
11711133
const git_oid *old_id, const char *old_target)
@@ -1219,54 +1181,28 @@ static int cmp_old_ref(int *cmp, git_refdb_backend *backend, const char *name,
12191181
*/
12201182
static int maybe_append_head(refdb_fs_backend *backend, const git_reference *ref, const git_signature *who, const char *message)
12211183
{
1222-
int error;
1184+
git_reference *head = NULL;
1185+
git_refdb *refdb = NULL;
1186+
int error, write_reflog;
12231187
git_oid old_id;
1224-
git_reference *tmp = NULL, *head = NULL, *peeled = NULL;
1225-
const char *name;
12261188

1227-
if (ref->type == GIT_REFERENCE_SYMBOLIC)
1228-
return 0;
1189+
if ((error = git_repository_refdb(&refdb, backend->repo)) < 0 ||
1190+
(error = git_refdb_should_write_head_reflog(&write_reflog, refdb, ref)) < 0)
1191+
goto out;
1192+
if (!write_reflog)
1193+
goto out;
12291194

12301195
/* if we can't resolve, we use {0}*40 as old id */
12311196
if (git_reference_name_to_id(&old_id, backend->repo, ref->name) < 0)
12321197
memset(&old_id, 0, sizeof(old_id));
12331198

1234-
if ((error = git_reference_lookup(&head, backend->repo, GIT_HEAD_FILE)) < 0)
1235-
return error;
1236-
1237-
if (git_reference_type(head) == GIT_REFERENCE_DIRECT)
1238-
goto cleanup;
1239-
1240-
if ((error = git_reference_lookup(&tmp, backend->repo, GIT_HEAD_FILE)) < 0)
1241-
goto cleanup;
1242-
1243-
/* Go down the symref chain until we find the branch */
1244-
while (git_reference_type(tmp) == GIT_REFERENCE_SYMBOLIC) {
1245-
error = git_reference_lookup(&peeled, backend->repo, git_reference_symbolic_target(tmp));
1246-
if (error < 0)
1247-
break;
1248-
1249-
git_reference_free(tmp);
1250-
tmp = peeled;
1251-
}
1252-
1253-
if (error == GIT_ENOTFOUND) {
1254-
error = 0;
1255-
name = git_reference_symbolic_target(tmp);
1256-
} else if (error < 0) {
1257-
goto cleanup;
1258-
} else {
1259-
name = git_reference_name(tmp);
1260-
}
1261-
1262-
if (strcmp(name, ref->name))
1263-
goto cleanup;
1264-
1265-
error = reflog_append(backend, head, &old_id, git_reference_target(ref), who, message);
1199+
if ((error = git_reference_lookup(&head, backend->repo, GIT_HEAD_FILE)) < 0 ||
1200+
(error = reflog_append(backend, head, &old_id, git_reference_target(ref), who, message)) < 0)
1201+
goto out;
12661202

1267-
cleanup:
1268-
git_reference_free(tmp);
1203+
out:
12691204
git_reference_free(head);
1205+
git_refdb_free(refdb);
12701206
return error;
12711207
}
12721208

@@ -1335,7 +1271,10 @@ static int refdb_fs_backend__write_tail(
13351271
}
13361272

13371273
if (update_reflog) {
1338-
if ((error = should_write_reflog(&should_write, backend->repo, ref->name)) < 0)
1274+
git_refdb *refdb;
1275+
1276+
if ((error = git_repository_refdb__weakptr(&refdb, backend->repo)) < 0 ||
1277+
(error = git_refdb_should_write_reflog(&should_write, refdb, ref)) < 0)
13391278
goto on_error;
13401279

13411280
if (should_write) {

0 commit comments

Comments
 (0)