]>
Commit | Line | Data |
---|---|---|
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 |
23 | int 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 | ||
40 | int 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 | 66 | static 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 |
72 | int 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 | ||
92 | int 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 | 102 | void 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 |
109 | void 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 |
117 | int 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 | ||
126 | int 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 |
147 | int 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; | |
195 | out: | |
196 | git_reference_free(ref); | |
197 | return error; | |
198 | } | |
199 | ||
ec24e542 | 200 | int 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 | 218 | int 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 | 231 | int 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 | ||
236 | void 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 | 242 | 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) |
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 | ||
253 | int 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 | 279 | int 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 | |
287 | int 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 |
303 | int 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 | ||
340 | int 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 | ||
376 | out: | |
377 | git_reference_free(resolved); | |
378 | git_reference_free(head); | |
379 | return error; | |
380 | } | |
381 | ||
f2105129 CMN |
382 | int 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 |
390 | int 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 | 398 | int 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 | |
405 | int 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 | ||
419 | 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 | { | |
c25aa7cd | 421 | GIT_ASSERT_ARG(db); |
ab8d9242 CMN |
422 | |
423 | return db->backend->unlock(db->backend, payload, success, update_reflog, ref, sig, message); | |
424 | } |