]> git.proxmox.com Git - libgit2.git/blame - src/odb_pack.c
Move `url` to last place in parameter list
[libgit2.git] / src / odb_pack.c
CommitLineData
7d7cd885 1/*
5e0de328 2 * Copyright (C) 2009-2012 the libgit2 contributors
7d7cd885 3 *
bb742ede
VM
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.
7d7cd885
VM
6 */
7
8#include "common.h"
0c3bae62 9#include <zlib.h>
44908fe7 10#include "git2/repository.h"
f1d01851 11#include "git2/oid.h"
7d7cd885
VM
12#include "fileops.h"
13#include "hash.h"
14#include "odb.h"
15#include "delta-apply.h"
dd453c4d 16#include "sha1_lookup.h"
7bfdb3d2 17#include "mwindow.h"
c7c9e183 18#include "pack.h"
7d7cd885 19
44908fe7 20#include "git2/odb_backend.h"
d12299fe 21
58d06cf1
VM
22struct pack_backend {
23 git_odb_backend parent;
24 git_vector packs;
a070f152 25 struct git_pack_file *last_found;
90d743cd 26 char *pack_folder;
58d06cf1
VM
27};
28
09cc0b92
ET
29struct pack_writepack {
30 struct git_odb_writepack parent;
31 git_indexer_stream *indexer_stream;
32};
33
58d06cf1
VM
34/**
35 * The wonderful tale of a Packed Object lookup query
36 * ===================================================
87d9869f
VM
37 * A riveting and epic story of epicness and ASCII
38 * art, presented by yours truly,
39 * Sir Vicent of Marti
58d06cf1
VM
40 *
41 *
42 * Chapter 1: Once upon a time...
43 * Initialization of the Pack Backend
44 * --------------------------------------------------
45 *
46 * # git_odb_backend_pack
47 * | Creates the pack backend structure, initializes the
48 * | callback pointers to our default read() and exist() methods,
49 * | and tries to preload all the known packfiles in the ODB.
87d9869f 50 * |
58d06cf1 51 * |-# packfile_load_all
87d9869f
VM
52 * | Tries to find the `pack` folder, if it exists. ODBs without
53 * | a pack folder are ignored altogether. If there's a `pack` folder
54 * | we run a `dirent` callback through every file in the pack folder
55 * | to find our packfiles. The packfiles are then sorted according
56 * | to a sorting callback.
57 * |
58 * |-# packfile_load__cb
59 * | | This callback is called from `dirent` with every single file
60 * | | inside the pack folder. We find the packs by actually locating
61 * | | their index (ends in ".idx"). From that index, we verify that
62 * | | the corresponding packfile exists and is valid, and if so, we
63 * | | add it to the pack list.
64 * | |
65 * | |-# packfile_check
66 * | Make sure that there's a packfile to back this index, and store
67 * | some very basic information regarding the packfile itself,
68 * | such as the full path, the size, and the modification time.
69 * | We don't actually open the packfile to check for internal consistency.
70 * |
71 * |-# packfile_sort__cb
72 * Sort all the preloaded packs according to some specific criteria:
73 * we prioritize the "newer" packs because it's more likely they
74 * contain the objects we are looking for, and we prioritize local
75 * packs over remote ones.
58d06cf1
VM
76 *
77 *
78 *
79 * Chapter 2: To be, or not to be...
80 * A standard packed `exist` query for an OID
81 * --------------------------------------------------
82 *
87d9869f
VM
83 * # pack_backend__exists
84 * | Check if the given SHA1 oid exists in any of the packs
85 * | that have been loaded for our ODB.
86 * |
87 * |-# pack_entry_find
88 * | Iterate through all the packs that have been preloaded
89 * | (starting by the pack where the latest object was found)
90 * | to try to find the OID in one of them.
91 * |
92 * |-# pack_entry_find1
93 * | Check the index of an individual pack to see if the SHA1
94 * | OID can be found. If we can find the offset to that SHA1
95 * | inside of the index, that means the object is contained
96 * | inside of the packfile and we can stop searching.
97 * | Before returning, we verify that the packfile behing the
98 * | index we are searching still exists on disk.
99 * |
100 * |-# pack_entry_find_offset
101 * | | Mmap the actual index file to disk if it hasn't been opened
102 * | | yet, and run a binary search through it to find the OID.
103 * | | See <http://book.git-scm.com/7_the_packfile.html> for specifics
104 * | | on the Packfile Index format and how do we find entries in it.
105 * | |
106 * | |-# pack_index_open
107 * | | Guess the name of the index based on the full path to the
108 * | | packfile, open it and verify its contents. Only if the index
109 * | | has not been opened already.
110 * | |
111 * | |-# pack_index_check
112 * | Mmap the index file and do a quick run through the header
113 * | to guess the index version (right now we support v1 and v2),
114 * | and to verify that the size of the index makes sense.
115 * |
116 * |-# packfile_open
117 * See `packfile_open` in Chapter 3
58d06cf1
VM
118 *
119 *
120 *
121 * Chapter 3: The neverending story...
122 * A standard packed `lookup` query for an OID
123 * --------------------------------------------------
124 * TODO
125 *
126 */
7d7cd885
VM
127
128
7d7cd885
VM
129/***********************************************************
130 *
58d06cf1 131 * FORWARD DECLARATIONS
7d7cd885
VM
132 *
133 ***********************************************************/
134
a070f152 135static void pack_window_free_all(struct pack_backend *backend, struct git_pack_file *p);
7bfdb3d2 136static int pack_window_contains(git_mwindow *win, off_t offset);
7d7cd885 137
58d06cf1 138static int packfile_sort__cb(const void *a_, const void *b_);
7d7cd885 139
97769280 140static int packfile_load__cb(void *_data, git_buf *path);
90d743cd 141static int packfile_refresh_all(struct pack_backend *backend);
7d7cd885 142
a070f152 143static int pack_entry_find(struct git_pack_entry *e,
e1de726c 144 struct pack_backend *backend, const git_oid *oid);
7d7cd885 145
dd453c4d
MP
146/* Can find the offset of an object given
147 * a prefix of an identifier.
e1de726c 148 * Sets GIT_EAMBIGUOUS if short oid is ambiguous.
ac2b94ad
MP
149 * This method assumes that len is between
150 * GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ.
dd453c4d 151 */
e1de726c
RB
152static int pack_entry_find_prefix(
153 struct git_pack_entry *e,
154 struct pack_backend *backend,
155 const git_oid *short_oid,
b8457baa 156 size_t len);
dd453c4d 157
58d06cf1
VM
158
159
160/***********************************************************
161 *
162 * PACK WINDOW MANAGEMENT
163 *
164 ***********************************************************/
7d7cd885 165
854eccbb 166GIT_INLINE(void) pack_window_free_all(struct pack_backend *backend, struct git_pack_file *p)
7d7cd885 167{
854eccbb 168 GIT_UNUSED(backend);
7bfdb3d2 169 git_mwindow_free_all(&p->mwf);
7d7cd885
VM
170}
171
7bfdb3d2 172GIT_INLINE(int) pack_window_contains(git_mwindow *win, off_t offset)
7d7cd885 173{
58d06cf1
VM
174 /* We must promise at least 20 bytes (one hash) after the
175 * offset is available from this window, otherwise the offset
176 * is not actually in this window and a different window (which
87d9869f 177 * has that one hash excess) must be used. This is to support
58d06cf1
VM
178 * the object header and delta base parsing routines below.
179 */
7bfdb3d2 180 return git_mwindow_contains(win, offset + 20);
7d7cd885
VM
181}
182
58d06cf1 183static int packfile_sort__cb(const void *a_, const void *b_)
7d7cd885 184{
a070f152
CMN
185 const struct git_pack_file *a = a_;
186 const struct git_pack_file *b = b_;
58d06cf1 187 int st;
7d7cd885 188
58d06cf1
VM
189 /*
190 * Local packs tend to contain objects specific to our
87d9869f 191 * variant of the project than remote ones. In addition,
58d06cf1
VM
192 * remote ones could be on a network mounted filesystem.
193 * Favor local ones for these reasons.
194 */
195 st = a->pack_local - b->pack_local;
196 if (st)
197 return -st;
198
199 /*
200 * Younger packs tend to contain more recent objects,
201 * and more recent objects tend to get accessed more
202 * often.
203 */
204 if (a->mtime < b->mtime)
205 return 1;
206 else if (a->mtime == b->mtime)
207 return 0;
208
209 return -1;
7d7cd885
VM
210}
211
58d06cf1 212
7d7cd885 213
97769280 214static int packfile_load__cb(void *_data, git_buf *path)
7d7cd885 215{
90d743cd 216 struct pack_backend *backend = (struct pack_backend *)_data;
a070f152 217 struct git_pack_file *pack;
58d06cf1 218 int error;
44ef8b1b 219 unsigned int i;
7d7cd885 220
97769280 221 if (git__suffixcmp(path->ptr, ".idx") != 0)
e1de726c 222 return 0; /* not an index */
7d7cd885 223
90d743cd 224 for (i = 0; i < backend->packs.length; ++i) {
a070f152 225 struct git_pack_file *p = git_vector_get(&backend->packs, i);
fa6420f7 226 if (memcmp(p->pack_name, git_buf_cstr(path), git_buf_len(path) - strlen(".idx")) == 0)
e1de726c 227 return 0;
90d743cd 228 }
7d7cd885 229
97769280 230 error = git_packfile_check(&pack, path->ptr);
e1de726c 231 if (error == GIT_ENOTFOUND)
599297fd 232 /* ignore missing .pack file as git does */
0d0fa7c3 233 return 0;
e1de726c
RB
234 else if (error < 0)
235 return error;
58d06cf1 236
e1de726c 237 return git_vector_insert(&backend->packs, pack);
7d7cd885
VM
238}
239
78216495 240static int packfile_refresh_all(struct pack_backend *backend)
7d7cd885 241{
58d06cf1 242 int error;
90d743cd 243 struct stat st;
5bb0dc93 244 git_buf path = GIT_BUF_INIT;
7d7cd885 245
90d743cd 246 if (backend->pack_folder == NULL)
e1de726c 247 return 0;
7d7cd885 248
f79026b4 249 if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
282283ac 250 return git_odb__error_notfound("failed to refresh packfiles", NULL);
7d7cd885 251
78216495 252 git_buf_sets(&path, backend->pack_folder);
90d743cd 253
78216495
BS
254 /* reload all packs */
255 error = git_path_direach(&path, packfile_load__cb, (void *)backend);
97769280 256
78216495 257 git_buf_free(&path);
e1de726c 258
78216495
BS
259 if (error < 0)
260 return error;
90d743cd 261
78216495 262 git_vector_sort(&backend->packs);
7d7cd885 263
e1de726c 264 return 0;
7d7cd885
VM
265}
266
63409451
BS
267static int pack_entry_find_inner(
268 struct git_pack_entry *e,
269 struct pack_backend *backend,
270 const git_oid *oid,
271 struct git_pack_file *last_found)
58d06cf1 272{
44ef8b1b 273 unsigned int i;
7d7cd885 274
3d7617e4
MS
275 if (last_found &&
276 git_pack_entry_find(e, last_found, oid, GIT_OID_HEXSZ) == 0)
e1de726c 277 return 0;
7d7cd885 278
58d06cf1 279 for (i = 0; i < backend->packs.length; ++i) {
a070f152 280 struct git_pack_file *p;
7d7cd885 281
58d06cf1 282 p = git_vector_get(&backend->packs, i);
3d7617e4 283 if (p == last_found)
58d06cf1 284 continue;
7d7cd885 285
e1de726c 286 if (git_pack_entry_find(e, p, oid, GIT_OID_HEXSZ) == 0) {
58d06cf1 287 backend->last_found = p;
e1de726c 288 return 0;
7d7cd885 289 }
58d06cf1
VM
290 }
291
5bb0dc93
BS
292 return -1;
293}
294
295static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid)
296{
297 int error;
63409451 298 struct git_pack_file *last_found = backend->last_found;
5bb0dc93
BS
299
300 if (backend->last_found &&
301 git_pack_entry_find(e, backend->last_found, oid, GIT_OID_HEXSZ) == 0)
302 return 0;
303
63409451 304 if (!pack_entry_find_inner(e, backend, oid, last_found))
5bb0dc93 305 return 0;
78216495 306 if ((error = packfile_refresh_all(backend)) < 0)
5bb0dc93 307 return error;
63409451 308 if (!pack_entry_find_inner(e, backend, oid, last_found))
5bb0dc93
BS
309 return 0;
310
282283ac 311 return git_odb__error_notfound("failed to find pack entry", oid);
58d06cf1
VM
312}
313
63409451
BS
314static unsigned pack_entry_find_prefix_inner(
315 struct git_pack_entry *e,
316 struct pack_backend *backend,
317 const git_oid *short_oid,
318 size_t len,
319 struct git_pack_file *last_found)
dd453c4d
MP
320{
321 int error;
44ef8b1b 322 unsigned int i;
7107b599 323 unsigned found = 0;
dd453c4d 324
3d7617e4
MS
325 if (last_found) {
326 error = git_pack_entry_find(e, last_found, short_oid, len);
e1de726c
RB
327 if (error == GIT_EAMBIGUOUS)
328 return error;
329 if (!error)
dd453c4d 330 found = 1;
dd453c4d
MP
331 }
332
333 for (i = 0; i < backend->packs.length; ++i) {
a070f152 334 struct git_pack_file *p;
dd453c4d
MP
335
336 p = git_vector_get(&backend->packs, i);
3d7617e4 337 if (p == last_found)
dd453c4d
MP
338 continue;
339
a070f152 340 error = git_pack_entry_find(e, p, short_oid, len);
e1de726c
RB
341 if (error == GIT_EAMBIGUOUS)
342 return error;
343 if (!error) {
344 if (++found > 1)
dd453c4d
MP
345 break;
346 backend->last_found = p;
347 }
348 }
349
78216495
BS
350 return found;
351}
352
353static int pack_entry_find_prefix(
354 struct git_pack_entry *e,
355 struct pack_backend *backend,
356 const git_oid *short_oid,
357 size_t len)
358{
359 unsigned found = 0;
360 int error;
63409451 361 struct git_pack_file *last_found = backend->last_found;
78216495 362
63409451 363 if ((found = pack_entry_find_prefix_inner(e, backend, short_oid, len, last_found)) > 0)
78216495
BS
364 goto cleanup;
365 if ((error = packfile_refresh_all(backend)) < 0)
366 return error;
63409451 367 found = pack_entry_find_prefix_inner(e, backend, short_oid, len, last_found);
78216495
BS
368
369cleanup:
e1de726c 370 if (!found)
282283ac 371 return git_odb__error_notfound("no matching pack entry for prefix", short_oid);
e1de726c
RB
372 else if (found > 1)
373 return git_odb__error_ambiguous("found multiple pack entries");
374 else
375 return 0;
dd453c4d
MP
376}
377
58d06cf1 378
7d7cd885
VM
379/***********************************************************
380 *
381 * PACKED BACKEND PUBLIC API
382 *
383 * Implement the git_odb_backend API calls
384 *
385 ***********************************************************/
386
bfb8bcc1 387static int pack_backend__read_header(size_t *len_p, git_otype *type_p, struct git_odb_backend *backend, const git_oid *oid)
7d7cd885 388{
bfb8bcc1
DMB
389 struct git_pack_entry e;
390 int error;
7d7cd885 391
bfb8bcc1 392 assert(len_p && type_p && backend && oid);
7d7cd885 393
bfb8bcc1
DMB
394 if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0)
395 return error;
7d7cd885 396
bfb8bcc1 397 return git_packfile_resolve_header(len_p, type_p, e.p, e.offset);
7d7cd885
VM
398}
399
d568d585 400static int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
7d7cd885 401{
a070f152 402 struct git_pack_entry e;
72a3fe42 403 git_rawobj raw;
58d06cf1 404 int error;
7d7cd885 405
e1de726c
RB
406 if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0 ||
407 (error = git_packfile_unpack(&raw, e.p, &e.offset)) < 0)
408 return error;
72a3fe42
VM
409
410 *buffer_p = raw.data;
411 *len_p = raw.len;
412 *type_p = raw.type;
413
e1de726c 414 return 0;
7d7cd885
VM
415}
416
d568d585 417static int pack_backend__read_prefix(
d0323a5f
VM
418 git_oid *out_oid,
419 void **buffer_p,
420 size_t *len_p,
421 git_otype *type_p,
422 git_odb_backend *backend,
423 const git_oid *short_oid,
b8457baa 424 size_t len)
ecd6fdf1 425{
e1de726c
RB
426 int error = 0;
427
6c8ca697 428 if (len < GIT_OID_MINPREFIXLEN)
e1de726c 429 error = git_odb__error_ambiguous("prefix length too short");
dd453c4d 430
e1de726c 431 else if (len >= GIT_OID_HEXSZ) {
6c8ca697 432 /* We can fall back to regular read method */
e1de726c
RB
433 error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid);
434 if (!error)
6c8ca697 435 git_oid_cpy(out_oid, short_oid);
6c8ca697 436 } else {
a070f152 437 struct git_pack_entry e;
6c8ca697 438 git_rawobj raw;
6c8ca697 439
e1de726c
RB
440 if ((error = pack_entry_find_prefix(
441 &e, (struct pack_backend *)backend, short_oid, len)) == 0 &&
442 (error = git_packfile_unpack(&raw, e.p, &e.offset)) == 0)
443 {
444 *buffer_p = raw.data;
445 *len_p = raw.len;
446 *type_p = raw.type;
447 git_oid_cpy(out_oid, &e.sha1);
448 }
6c8ca697 449 }
dd453c4d 450
e1de726c 451 return error;
ecd6fdf1
MP
452}
453
d568d585 454static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
7d7cd885 455{
a070f152 456 struct git_pack_entry e;
e1de726c 457 return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;
7d7cd885
VM
458}
459
c3fb7d04 460static int pack_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
521aedad 461{
5dca2010 462 int error;
521aedad
CMN
463 struct git_pack_file *p;
464 struct pack_backend *backend;
465 unsigned int i;
466
467 assert(_backend && cb);
468 backend = (struct pack_backend *)_backend;
469
470 /* Make sure we know about the packfiles */
5dca2010
RB
471 if ((error = packfile_refresh_all(backend)) < 0)
472 return error;
521aedad
CMN
473
474 git_vector_foreach(&backend->packs, i, p) {
f9988d4e 475 if ((error = git_pack_foreach_entry(p, cb, data)) < 0)
5dca2010 476 return error;
521aedad 477 }
5dca2010 478
521aedad
CMN
479 return 0;
480}
481
09cc0b92
ET
482static int pack_backend__writepack_add(struct git_odb_writepack *_writepack, const void *data, size_t size, git_transfer_progress *stats)
483{
484 struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
485
486 assert(writepack);
487
488 return git_indexer_stream_add(writepack->indexer_stream, data, size, stats);
489}
490
491static int pack_backend__writepack_commit(struct git_odb_writepack *_writepack, git_transfer_progress *stats)
492{
493 struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
494
495 assert(writepack);
496
497 return git_indexer_stream_finalize(writepack->indexer_stream, stats);
498}
499
500static void pack_backend__writepack_free(struct git_odb_writepack *_writepack)
501{
502 struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
503
504 assert(writepack);
505
506 git_indexer_stream_free(writepack->indexer_stream);
507 git__free(writepack);
508}
509
510static int pack_backend__writepack(struct git_odb_writepack **out,
511 git_odb_backend *_backend,
512 git_transfer_progress_callback progress_cb,
513 void *progress_payload)
514{
515 struct pack_backend *backend;
516 struct pack_writepack *writepack;
517
518 assert(out && _backend);
519
520 *out = NULL;
521
522 backend = (struct pack_backend *)_backend;
523
524 writepack = git__calloc(1, sizeof(struct pack_writepack));
525 GITERR_CHECK_ALLOC(writepack);
526
527 if (git_indexer_stream_new(&writepack->indexer_stream,
528 backend->pack_folder, progress_cb, progress_payload) < 0) {
529 git__free(writepack);
530 return -1;
531 }
532
533 writepack->parent.backend = _backend;
534 writepack->parent.add = pack_backend__writepack_add;
535 writepack->parent.commit = pack_backend__writepack_commit;
536 writepack->parent.free = pack_backend__writepack_free;
537
538 *out = (git_odb_writepack *)writepack;
539
540 return 0;
541}
542
d568d585 543static void pack_backend__free(git_odb_backend *_backend)
7d7cd885 544{
58d06cf1 545 struct pack_backend *backend;
44ef8b1b 546 unsigned int i;
7d7cd885
VM
547
548 assert(_backend);
549
58d06cf1 550 backend = (struct pack_backend *)_backend;
7d7cd885 551
58d06cf1 552 for (i = 0; i < backend->packs.length; ++i) {
a070f152
CMN
553 struct git_pack_file *p = git_vector_get(&backend->packs, i);
554 packfile_free(p);
58d06cf1 555 }
7d7cd885 556
58d06cf1 557 git_vector_free(&backend->packs);
3286c408
VM
558 git__free(backend->pack_folder);
559 git__free(backend);
7d7cd885
VM
560}
561
507523c3
CMN
562int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx)
563{
564 struct pack_backend *backend = NULL;
565 struct git_pack_file *packfile = NULL;
566
567 if (git_packfile_check(&packfile, idx) < 0)
568 return -1;
569
570 backend = git__calloc(1, sizeof(struct pack_backend));
571 GITERR_CHECK_ALLOC(backend);
55f6f21b 572 backend->parent.version = GIT_ODB_BACKEND_VERSION;
507523c3
CMN
573
574 if (git_vector_init(&backend->packs, 1, NULL) < 0)
575 goto on_error;
576
577 if (git_vector_insert(&backend->packs, packfile) < 0)
578 goto on_error;
579
580 backend->parent.read = &pack_backend__read;
581 backend->parent.read_prefix = &pack_backend__read_prefix;
bfb8bcc1 582 backend->parent.read_header = &pack_backend__read_header;
507523c3
CMN
583 backend->parent.exists = &pack_backend__exists;
584 backend->parent.foreach = &pack_backend__foreach;
585 backend->parent.free = &pack_backend__free;
586
587 *backend_out = (git_odb_backend *)backend;
588
589 return 0;
590
591on_error:
592 git_vector_free(&backend->packs);
593 git__free(backend);
594 git__free(packfile);
595 return -1;
596}
597
7d7cd885
VM
598int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
599{
97769280
RB
600 struct pack_backend *backend = NULL;
601 git_buf path = GIT_BUF_INIT;
7d7cd885 602
58d06cf1 603 backend = git__calloc(1, sizeof(struct pack_backend));
e1de726c 604 GITERR_CHECK_ALLOC(backend);
55f6f21b 605 backend->parent.version = GIT_ODB_BACKEND_VERSION;
7d7cd885 606
e1de726c
RB
607 if (git_vector_init(&backend->packs, 8, packfile_sort__cb) < 0 ||
608 git_buf_joinpath(&path, objects_dir, "pack") < 0)
609 {
610 git__free(backend);
611 return -1;
612 }
90d743cd 613
1a481123 614 if (git_path_isdir(git_buf_cstr(&path)) == true) {
97769280 615 backend->pack_folder = git_buf_detach(&path);
58d06cf1 616 }
7d7cd885
VM
617
618 backend->parent.read = &pack_backend__read;
d0323a5f 619 backend->parent.read_prefix = &pack_backend__read_prefix;
bfb8bcc1 620 backend->parent.read_header = &pack_backend__read_header;
7d7cd885 621 backend->parent.exists = &pack_backend__exists;
521aedad 622 backend->parent.foreach = &pack_backend__foreach;
09cc0b92 623 backend->parent.writepack = &pack_backend__writepack;
7d7cd885
VM
624 backend->parent.free = &pack_backend__free;
625
7d7cd885 626 *backend_out = (git_odb_backend *)backend;
97769280 627
97769280
RB
628 git_buf_free(&path);
629
e1de726c 630 return 0;
7d7cd885 631}