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