]> git.proxmox.com Git - libgit2.git/blob - src/refdb.c
New upstream version 1.1.0+dfsg.1
[libgit2.git] / src / refdb.c
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 int git_refdb_new(git_refdb **out, git_repository *repo)
24 {
25 git_refdb *db;
26
27 assert(out && repo);
28
29 db = git__calloc(1, sizeof(*db));
30 GIT_ERROR_CHECK_ALLOC(db);
31
32 db->repo = repo;
33
34 *out = db;
35 GIT_REFCOUNT_INC(db);
36 return 0;
37 }
38
39 int git_refdb_open(git_refdb **out, git_repository *repo)
40 {
41 git_refdb *db;
42 git_refdb_backend *dir;
43
44 assert(out && repo);
45
46 *out = NULL;
47
48 if (git_refdb_new(&db, repo) < 0)
49 return -1;
50
51 /* Add the default (filesystem) backend */
52 if (git_refdb_backend_fs(&dir, repo) < 0) {
53 git_refdb_free(db);
54 return -1;
55 }
56
57 db->repo = repo;
58 db->backend = dir;
59
60 *out = db;
61 return 0;
62 }
63
64 static void refdb_free_backend(git_refdb *db)
65 {
66 if (db->backend)
67 db->backend->free(db->backend);
68 }
69
70 int git_refdb_set_backend(git_refdb *db, git_refdb_backend *backend)
71 {
72 GIT_ERROR_CHECK_VERSION(backend, GIT_REFDB_BACKEND_VERSION, "git_refdb_backend");
73
74 if (!backend->exists || !backend->lookup || !backend->iterator ||
75 !backend->write || !backend->rename || !backend->del ||
76 !backend->has_log || !backend->ensure_log || !backend->free ||
77 !backend->reflog_read || !backend->reflog_write ||
78 !backend->reflog_rename || !backend->reflog_delete ||
79 (backend->lock && !backend->unlock)) {
80 git_error_set(GIT_ERROR_REFERENCE, "incomplete refdb backend implementation");
81 return GIT_EINVALID;
82 }
83
84 refdb_free_backend(db);
85 db->backend = backend;
86
87 return 0;
88 }
89
90 int git_refdb_compress(git_refdb *db)
91 {
92 assert(db);
93
94 if (db->backend->compress)
95 return db->backend->compress(db->backend);
96
97 return 0;
98 }
99
100 void git_refdb__free(git_refdb *db)
101 {
102 refdb_free_backend(db);
103 git__memzero(db, sizeof(*db));
104 git__free(db);
105 }
106
107 void git_refdb_free(git_refdb *db)
108 {
109 if (db == NULL)
110 return;
111
112 GIT_REFCOUNT_DEC(db, git_refdb__free);
113 }
114
115 int git_refdb_exists(int *exists, git_refdb *refdb, const char *ref_name)
116 {
117 assert(exists && refdb && refdb->backend);
118
119 return refdb->backend->exists(exists, refdb->backend, ref_name);
120 }
121
122 int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name)
123 {
124 git_reference *ref;
125 int error;
126
127 assert(db && db->backend && out && ref_name);
128
129 error = db->backend->lookup(&ref, db->backend, ref_name);
130 if (error < 0)
131 return error;
132
133 GIT_REFCOUNT_INC(db);
134 ref->db = db;
135
136 *out = ref;
137 return 0;
138 }
139
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
193 int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob)
194 {
195 int error;
196
197 if (!db->backend || !db->backend->iterator) {
198 git_error_set(GIT_ERROR_REFERENCE, "this backend doesn't support iterators");
199 return -1;
200 }
201
202 if ((error = db->backend->iterator(out, db->backend, glob)) < 0)
203 return error;
204
205 GIT_REFCOUNT_INC(db);
206 (*out)->db = db;
207
208 return 0;
209 }
210
211 int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter)
212 {
213 int error;
214
215 if ((error = iter->next(out, iter)) < 0)
216 return error;
217
218 GIT_REFCOUNT_INC(iter->db);
219 (*out)->db = iter->db;
220
221 return 0;
222 }
223
224 int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter)
225 {
226 return iter->next_name(out, iter);
227 }
228
229 void git_refdb_iterator_free(git_reference_iterator *iter)
230 {
231 GIT_REFCOUNT_DEC(iter->db, git_refdb__free);
232 iter->free(iter);
233 }
234
235 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 assert(db && db->backend);
238
239 GIT_REFCOUNT_INC(db);
240 ref->db = db;
241
242 return db->backend->write(db->backend, ref, force, who, message, old_id, old_target);
243 }
244
245 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 assert(db && db->backend);
257 error = db->backend->rename(out, db->backend, old_name, new_name, force, who, message);
258 if (error < 0)
259 return error;
260
261 if (out) {
262 GIT_REFCOUNT_INC(db);
263 (*out)->db = db;
264 }
265
266 return 0;
267 }
268
269 int git_refdb_delete(struct git_refdb *db, const char *ref_name, const git_oid *old_id, const char *old_target)
270 {
271 assert(db && db->backend);
272 return db->backend->del(db->backend, ref_name, old_id, old_target);
273 }
274
275 int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name)
276 {
277 int error;
278
279 assert(db && db->backend);
280
281 if ((error = db->backend->reflog_read(out, db->backend, name)) < 0)
282 return error;
283
284 GIT_REFCOUNT_INC(db);
285 (*out)->db = db;
286
287 return 0;
288 }
289
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
369 int git_refdb_has_log(git_refdb *db, const char *refname)
370 {
371 assert(db && refname);
372
373 return db->backend->has_log(db->backend, refname);
374 }
375
376 int git_refdb_ensure_log(git_refdb *db, const char *refname)
377 {
378 assert(db && refname);
379
380 return db->backend->ensure_log(db->backend, refname);
381 }
382
383 int git_refdb_init_backend(git_refdb_backend *backend, unsigned int version)
384 {
385 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
386 backend, version, git_refdb_backend, GIT_REFDB_BACKEND_INIT);
387 return 0;
388 }
389
390 int git_refdb_lock(void **payload, git_refdb *db, const char *refname)
391 {
392 assert(payload && db && refname);
393
394 if (!db->backend->lock) {
395 git_error_set(GIT_ERROR_REFERENCE, "backend does not support locking");
396 return -1;
397 }
398
399 return db->backend->lock(payload, db->backend, refname);
400 }
401
402 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 assert(db);
405
406 return db->backend->unlock(db->backend, payload, success, update_reflog, ref, sig, message);
407 }