File Coverage

deps/libgit2/src/refdb.c
Criterion Covered Total %
statement 156 198 78.7
branch 82 174 47.1
condition n/a
subroutine n/a
pod n/a
total 238 372 63.9


line stmt bran cond sub pod time code
1             /*
2             * Copyright (C) the libgit2 contributors. All rights reserved.
3             *
4             * This file is part of libgit2, distributed under the GNU GPL v2 with
5             * a Linking Exception. For full terms see the included COPYING file.
6             */
7              
8             #include "refdb.h"
9              
10             #include "git2/object.h"
11             #include "git2/refs.h"
12             #include "git2/refdb.h"
13             #include "git2/sys/refdb_backend.h"
14              
15             #include "hash.h"
16             #include "refs.h"
17             #include "reflog.h"
18             #include "posix.h"
19              
20             #define DEFAULT_NESTING_LEVEL 5
21             #define MAX_NESTING_LEVEL 10
22              
23 54           int git_refdb_new(git_refdb **out, git_repository *repo)
24             {
25             git_refdb *db;
26              
27 54 50         assert(out && repo);
    50          
28              
29 54           db = git__calloc(1, sizeof(*db));
30 54 50         GIT_ERROR_CHECK_ALLOC(db);
31              
32 54           db->repo = repo;
33              
34 54           *out = db;
35 54           GIT_REFCOUNT_INC(db);
36 54           return 0;
37             }
38              
39 54           int git_refdb_open(git_refdb **out, git_repository *repo)
40             {
41             git_refdb *db;
42             git_refdb_backend *dir;
43              
44 54 50         assert(out && repo);
    50          
45              
46 54           *out = NULL;
47              
48 54 50         if (git_refdb_new(&db, repo) < 0)
49 0           return -1;
50              
51             /* Add the default (filesystem) backend */
52 54 50         if (git_refdb_backend_fs(&dir, repo) < 0) {
53 0           git_refdb_free(db);
54 0           return -1;
55             }
56              
57 54           db->repo = repo;
58 54           db->backend = dir;
59              
60 54           *out = db;
61 54           return 0;
62             }
63              
64 49           static void refdb_free_backend(git_refdb *db)
65             {
66 49 50         if (db->backend)
67 49           db->backend->free(db->backend);
68 49           }
69              
70 0           int git_refdb_set_backend(git_refdb *db, git_refdb_backend *backend)
71             {
72 0 0         GIT_ERROR_CHECK_VERSION(backend, GIT_REFDB_BACKEND_VERSION, "git_refdb_backend");
73              
74 0 0         if (!backend->exists || !backend->lookup || !backend->iterator ||
    0          
    0          
    0          
75 0 0         !backend->write || !backend->rename || !backend->del ||
    0          
    0          
76 0 0         !backend->has_log || !backend->ensure_log || !backend->free ||
    0          
    0          
77 0 0         !backend->reflog_read || !backend->reflog_write ||
    0          
78 0 0         !backend->reflog_rename || !backend->reflog_delete ||
    0          
79 0 0         (backend->lock && !backend->unlock)) {
80 0           git_error_set(GIT_ERROR_REFERENCE, "incomplete refdb backend implementation");
81 0           return GIT_EINVALID;
82             }
83              
84 0           refdb_free_backend(db);
85 0           db->backend = backend;
86              
87 0           return 0;
88             }
89              
90 0           int git_refdb_compress(git_refdb *db)
91             {
92 0 0         assert(db);
93              
94 0 0         if (db->backend->compress)
95 0           return db->backend->compress(db->backend);
96              
97 0           return 0;
98             }
99              
100 49           void git_refdb__free(git_refdb *db)
101             {
102 49           refdb_free_backend(db);
103 49           git__memzero(db, sizeof(*db));
104 49           git__free(db);
105 49           }
106              
107 135           void git_refdb_free(git_refdb *db)
108             {
109 135 50         if (db == NULL)
110 0           return;
111              
112 135 100         GIT_REFCOUNT_DEC(db, git_refdb__free);
    50          
113             }
114              
115 0           int git_refdb_exists(int *exists, git_refdb *refdb, const char *ref_name)
116             {
117 0 0         assert(exists && refdb && refdb->backend);
    0          
    0          
118              
119 0           return refdb->backend->exists(exists, refdb->backend, ref_name);
120             }
121              
122 1185           int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name)
123             {
124             git_reference *ref;
125             int error;
126              
127 1185 50         assert(db && db->backend && out && ref_name);
    50          
    50          
    50          
128              
129 1185           error = db->backend->lookup(&ref, db->backend, ref_name);
130 1185 100         if (error < 0)
131 135           return error;
132              
133 1050           GIT_REFCOUNT_INC(db);
134 1050           ref->db = db;
135              
136 1050           *out = ref;
137 1185           return 0;
138             }
139              
140 1048           int git_refdb_resolve(
141             git_reference **out,
142             git_refdb *db,
143             const char *ref_name,
144             int max_nesting)
145             {
146 1048           git_reference *ref = NULL;
147 1048           int error = 0, nesting;
148              
149 1048           *out = NULL;
150              
151 1048 50         if (max_nesting > MAX_NESTING_LEVEL)
152 0           max_nesting = MAX_NESTING_LEVEL;
153 1048 100         else if (max_nesting < 0)
154 590           max_nesting = DEFAULT_NESTING_LEVEL;
155              
156 1048 100         if ((error = git_refdb_lookup(&ref, db, ref_name)) < 0)
157 129           goto out;
158              
159 988 100         for (nesting = 0; nesting < max_nesting; nesting++) {
160             git_reference *resolved;
161              
162 566 100         if (ref->type == GIT_REFERENCE_DIRECT)
163 491           break;
164              
165 75 100         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 6 50         if (error == GIT_ENOTFOUND) {
168 6           error = 0;
169 6           *out = ref;
170 6           ref = NULL;
171             }
172 6           goto out;
173             }
174              
175 69           git_reference_free(ref);
176 69           ref = resolved;
177             }
178              
179 913 100         if (ref->type != GIT_REFERENCE_DIRECT && max_nesting != 0) {
    50          
180 0           git_error_set(GIT_ERROR_REFERENCE,
181             "cannot resolve reference (>%u levels deep)", max_nesting);
182 0           error = -1;
183 0           goto out;
184             }
185              
186 913           *out = ref;
187 913           ref = NULL;
188             out:
189 1048           git_reference_free(ref);
190 1048           return error;
191             }
192              
193 32           int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob)
194             {
195             int error;
196              
197 32 50         if (!db->backend || !db->backend->iterator) {
    50          
198 0           git_error_set(GIT_ERROR_REFERENCE, "this backend doesn't support iterators");
199 0           return -1;
200             }
201              
202 32 50         if ((error = db->backend->iterator(out, db->backend, glob)) < 0)
203 0           return error;
204              
205 32           GIT_REFCOUNT_INC(db);
206 32           (*out)->db = db;
207              
208 32           return 0;
209             }
210              
211 19           int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter)
212             {
213             int error;
214              
215 19 100         if ((error = iter->next(out, iter)) < 0)
216 8           return error;
217              
218 11           GIT_REFCOUNT_INC(iter->db);
219 11           (*out)->db = iter->db;
220              
221 11           return 0;
222             }
223              
224 140           int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter)
225             {
226 140           return iter->next_name(out, iter);
227             }
228              
229 32           void git_refdb_iterator_free(git_reference_iterator *iter)
230             {
231 32 50         GIT_REFCOUNT_DEC(iter->db, git_refdb__free);
    0          
232 32           iter->free(iter);
233 32           }
234              
235 99           int git_refdb_write(git_refdb *db, git_reference *ref, int force, const git_signature *who, const char *message, const git_oid *old_id, const char *old_target)
236             {
237 99 50         assert(db && db->backend);
    50          
238              
239 99           GIT_REFCOUNT_INC(db);
240 99           ref->db = db;
241              
242 99           return db->backend->write(db->backend, ref, force, who, message, old_id, old_target);
243             }
244              
245 1           int git_refdb_rename(
246             git_reference **out,
247             git_refdb *db,
248             const char *old_name,
249             const char *new_name,
250             int force,
251             const git_signature *who,
252             const char *message)
253             {
254             int error;
255              
256 1 50         assert(db && db->backend);
    50          
257 1           error = db->backend->rename(out, db->backend, old_name, new_name, force, who, message);
258 1 50         if (error < 0)
259 0           return error;
260              
261 1 50         if (out) {
262 1           GIT_REFCOUNT_INC(db);
263 1           (*out)->db = db;
264             }
265              
266 1           return 0;
267             }
268              
269 6           int git_refdb_delete(struct git_refdb *db, const char *ref_name, const git_oid *old_id, const char *old_target)
270             {
271 6 50         assert(db && db->backend);
    50          
272 6           return db->backend->del(db->backend, ref_name, old_id, old_target);
273             }
274              
275 20           int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name)
276             {
277             int error;
278              
279 20 50         assert(db && db->backend);
    50          
280              
281 20 50         if ((error = db->backend->reflog_read(out, db->backend, name)) < 0)
282 0           return error;
283              
284 20           GIT_REFCOUNT_INC(db);
285 20           (*out)->db = db;
286              
287 20           return 0;
288             }
289              
290 90           int git_refdb_should_write_reflog(int *out, git_refdb *db, const git_reference *ref)
291             {
292             int error, logall;
293              
294 90           error = git_repository__configmap_lookup(&logall, db->repo, GIT_CONFIGMAP_LOGALLREFUPDATES);
295 90 50         if (error < 0)
296 0           return error;
297              
298             /* Defaults to the opposite of the repo being bare */
299 90 100         if (logall == GIT_LOGALLREFUPDATES_UNSET)
300 2           logall = !git_repository_is_bare(db->repo);
301              
302 90           *out = 0;
303 90           switch (logall) {
304             case GIT_LOGALLREFUPDATES_FALSE:
305 2           *out = 0;
306 2           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 117 100         *out = git_refdb_has_log(db, ref->name) ||
313 39 50         !git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) ||
314 10 50         !git__strcmp(ref->name, GIT_HEAD_FILE) ||
315 127 100         !git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR) ||
    100          
316 10           !git__prefixcmp(ref->name, GIT_REFS_NOTES_DIR);
317 88           break;
318              
319             case GIT_LOGALLREFUPDATES_ALWAYS:
320 0           *out = 1;
321 0           break;
322             }
323              
324 90           return 0;
325             }
326              
327 80           int git_refdb_should_write_head_reflog(int *out, git_refdb *db, const git_reference *ref)
328             {
329 80           git_reference *head = NULL, *resolved = NULL;
330             const char *name;
331             int error;
332              
333 80           *out = 0;
334              
335 80 100         if (ref->type == GIT_REFERENCE_SYMBOLIC) {
336 18           error = 0;
337 18           goto out;
338             }
339              
340 62 50         if ((error = git_refdb_lookup(&head, db, GIT_HEAD_FILE)) < 0)
341 0           goto out;
342              
343 62 100         if (git_reference_type(head) == GIT_REFERENCE_DIRECT)
344 4           goto out;
345              
346             /* Go down the symref chain until we find the branch */
347 58 100         if ((error = git_refdb_resolve(&resolved, db, git_reference_symbolic_target(head), -1)) < 0) {
348 3 50         if (error != GIT_ENOTFOUND)
349 0           goto out;
350 3           error = 0;
351 3           name = git_reference_symbolic_target(head);
352 55 50         } else if (git_reference_type(resolved) == GIT_REFERENCE_SYMBOLIC) {
353 0           name = git_reference_symbolic_target(resolved);
354             } else {
355 55           name = git_reference_name(resolved);
356             }
357              
358 58 100         if (strcmp(name, ref->name))
359 28           goto out;
360              
361 30           *out = 1;
362              
363             out:
364 80           git_reference_free(resolved);
365 80           git_reference_free(head);
366 80           return error;
367             }
368              
369 88           int git_refdb_has_log(git_refdb *db, const char *refname)
370             {
371 88 50         assert(db && refname);
    50          
372              
373 88           return db->backend->has_log(db->backend, refname);
374             }
375              
376 6           int git_refdb_ensure_log(git_refdb *db, const char *refname)
377             {
378 6 50         assert(db && refname);
    50          
379              
380 6           return db->backend->ensure_log(db->backend, refname);
381             }
382              
383 54           int git_refdb_init_backend(git_refdb_backend *backend, unsigned int version)
384             {
385 54 50         GIT_INIT_STRUCTURE_FROM_TEMPLATE(
386             backend, version, git_refdb_backend, GIT_REFDB_BACKEND_INIT);
387 54           return 0;
388             }
389              
390 3           int git_refdb_lock(void **payload, git_refdb *db, const char *refname)
391             {
392 3 50         assert(payload && db && refname);
    50          
    50          
393              
394 3 50         if (!db->backend->lock) {
395 0           git_error_set(GIT_ERROR_REFERENCE, "backend does not support locking");
396 0           return -1;
397             }
398              
399 3           return db->backend->lock(payload, db->backend, refname);
400             }
401              
402 3           int git_refdb_unlock(git_refdb *db, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message)
403             {
404 3 50         assert(db);
405              
406 3           return db->backend->unlock(db->backend, payload, success, update_reflog, ref, sig, message);
407             }