File Coverage

deps/libgit2/src/libgit2/refdb.c
Criterion Covered Total %
statement 169 213 79.3
branch 82 174 47.1
condition n/a
subroutine n/a
pod n/a
total 251 387 64.8


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