index.c 40.9 KB
Newer Older
1
/*
Edward Thomson committed
2
 * Copyright (C) the libgit2 contributors. All rights reserved.
3
 *
Vicent Marti committed
4 5
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
6 7 8 9 10
 */

#include <stddef.h>

#include "common.h"
11
#include "repository.h"
12
#include "index.h"
13
#include "tree.h"
14
#include "tree-cache.h"
15
#include "hash.h"
16 17
#include "iterator.h"
#include "pathspec.h"
18
#include "git2/odb.h"
19
#include "git2/oid.h"
20
#include "git2/blob.h"
21
#include "git2/config.h"
22 23 24 25 26 27 28 29 30 31 32

#define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7)
#define short_entry_size(len) entry_size(struct entry_short, len)
#define long_entry_size(len) entry_size(struct entry_long, len)

#define minimal_entry_size (offsetof(struct entry_short, path))

static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ;
static const size_t INDEX_HEADER_SIZE = 12;

static const unsigned int INDEX_VERSION_NUMBER = 2;
33 34 35 36
static const unsigned int INDEX_VERSION_NUMBER_EXT = 3;

static const unsigned int INDEX_HEADER_SIG = 0x44495243;
static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'};
37
static const char INDEX_EXT_UNMERGED_SIG[] = {'R', 'E', 'U', 'C'};
38

39 40
#define INDEX_OWNER(idx) ((git_repository *)(GIT_REFCOUNT_OWNER(idx)))

41 42 43 44 45 46 47 48 49 50 51
struct index_header {
	uint32_t signature;
	uint32_t version;
	uint32_t entry_count;
};

struct index_extension {
	char signature[4];
	uint32_t extension_size;
};

52 53 54 55 56
struct entry_time {
	uint32_t seconds;
	uint32_t nanoseconds;
};

57
struct entry_short {
58 59
	struct entry_time ctime;
	struct entry_time mtime;
60 61 62 63 64 65 66 67
	uint32_t dev;
	uint32_t ino;
	uint32_t mode;
	uint32_t uid;
	uint32_t gid;
	uint32_t file_size;
	git_oid oid;
	uint16_t flags;
schu committed
68
	char path[1]; /* arbitrary length */
69 70 71
};

struct entry_long {
72 73
	struct entry_time ctime;
	struct entry_time mtime;
74 75 76 77 78 79 80 81 82
	uint32_t dev;
	uint32_t ino;
	uint32_t mode;
	uint32_t uid;
	uint32_t gid;
	uint32_t file_size;
	git_oid oid;
	uint16_t flags;
	uint16_t flags_extended;
schu committed
83
	char path[1]; /* arbitrary length */
84 85
};

Edward Thomson committed
86 87 88 89 90
struct entry_srch_key {
	const char *path;
	int stage;
};

91 92 93 94 95
/* local declarations */
static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size);
static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size);
static int read_header(struct index_header *dest, const void *buffer);

96
static int parse_index(git_index *index, const char *buffer, size_t buffer_size);
97
static bool is_index_extended(git_index *index);
98
static int write_index(git_index *index, git_filebuf *file);
99

Edward Thomson committed
100 101
static int index_find(git_index *index, const char *path, int stage);

102
static void index_entry_free(git_index_entry *entry);
Edward Thomson committed
103 104 105 106 107 108
static void index_entry_reuc_free(git_index_reuc_entry *reuc);

GIT_INLINE(int) index_entry_stage(const git_index_entry *entry)
{
	return (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT;
}
109

110
static int index_srch(const void *key, const void *array_member)
111
{
Edward Thomson committed
112
	const struct entry_srch_key *srch_key = key;
113
	const git_index_entry *entry = array_member;
Edward Thomson committed
114
	int ret;
115

Edward Thomson committed
116 117 118 119 120 121
	ret = strcmp(srch_key->path, entry->path);

	if (ret == 0)
		ret = srch_key->stage - index_entry_stage(entry);

	return ret;
122 123
}

124 125
static int index_isrch(const void *key, const void *array_member)
{
Edward Thomson committed
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
	const struct entry_srch_key *srch_key = key;
	const git_index_entry *entry = array_member;
	int ret;

	ret = strcasecmp(srch_key->path, entry->path);

	if (ret == 0)
		ret = srch_key->stage - index_entry_stage(entry);

	return ret;
}

static int index_cmp_path(const void *a, const void *b)
{
	return strcmp((const char *)a, (const char *)b);
}

static int index_icmp_path(const void *a, const void *b)
{
	return strcasecmp((const char *)a, (const char *)b);
}

static int index_srch_path(const void *path, const void *array_member)
{
150 151
	const git_index_entry *entry = array_member;

Edward Thomson committed
152 153 154 155 156 157 158 159
	return strcmp((const char *)path, entry->path);
}

static int index_isrch_path(const void *path, const void *array_member)
{
	const git_index_entry *entry = array_member;

	return strcasecmp((const char *)path, entry->path);
160 161
}

162
static int index_cmp(const void *a, const void *b)
163
{
Edward Thomson committed
164
	int diff;
165 166
	const git_index_entry *entry_a = a;
	const git_index_entry *entry_b = b;
167

Edward Thomson committed
168 169 170 171 172 173
	diff = strcmp(entry_a->path, entry_b->path);

	if (diff == 0)
		diff = (index_entry_stage(entry_a) - index_entry_stage(entry_b));

	return diff;
174 175
}

176 177
static int index_icmp(const void *a, const void *b)
{
Edward Thomson committed
178
	int diff;
179 180 181
	const git_index_entry *entry_a = a;
	const git_index_entry *entry_b = b;

Edward Thomson committed
182 183 184 185 186 187 188 189 190 191 192 193 194
	diff = strcasecmp(entry_a->path, entry_b->path);

	if (diff == 0)
		diff = (index_entry_stage(entry_a) - index_entry_stage(entry_b));

	return diff;
}

static int reuc_srch(const void *key, const void *array_member)
{
	const git_index_reuc_entry *reuc = array_member;

	return strcmp(key, reuc->path);
195 196
}

Edward Thomson committed
197
static int reuc_isrch(const void *key, const void *array_member)
198
{
Edward Thomson committed
199
	const git_index_reuc_entry *reuc = array_member;
200

Edward Thomson committed
201
	return strcasecmp(key, reuc->path);
202 203
}

Edward Thomson committed
204
static int reuc_cmp(const void *a, const void *b)
205
{
Edward Thomson committed
206 207
	const git_index_reuc_entry *info_a = a;
	const git_index_reuc_entry *info_b = b;
208 209 210

	return strcmp(info_a->path, info_b->path);
}
211

Edward Thomson committed
212 213 214 215 216 217 218 219
static int reuc_icmp(const void *a, const void *b)
{
	const git_index_reuc_entry *info_a = a;
	const git_index_reuc_entry *info_b = b;

	return strcasecmp(info_a->path, info_b->path);
}

220
static unsigned int index_create_mode(unsigned int mode)
221 222 223
{
	if (S_ISLNK(mode))
		return S_IFLNK;
224

225 226
	if (S_ISDIR(mode) || (mode & S_IFMT) == (S_IFLNK | S_IFDIR))
		return (S_IFLNK | S_IFDIR);
227

228 229 230
	return S_IFREG | ((mode & 0100) ? 0755 : 0644);
}

231 232 233 234 235 236 237 238 239 240 241 242 243 244
static unsigned int index_merge_mode(
	git_index *index, git_index_entry *existing, unsigned int mode)
{
	if (index->no_symlinks && S_ISREG(mode) &&
		existing && S_ISLNK(existing->mode))
		return existing->mode;

	if (index->distrust_filemode && S_ISREG(mode))
		return (existing && S_ISREG(existing->mode)) ?
			existing->mode : index_create_mode(0666);

	return index_create_mode(mode);
}

245 246 247
static void index_set_ignore_case(git_index *index, bool ignore_case)
{
	index->entries._cmp = ignore_case ? index_icmp : index_cmp;
Edward Thomson committed
248
	index->entries_cmp_path = ignore_case ? index_icmp_path : index_cmp_path;
249
	index->entries_search = ignore_case ? index_isrch : index_srch;
Edward Thomson committed
250
	index->entries_search_path = ignore_case ? index_isrch_path : index_srch_path;
251 252
	index->entries.sorted = 0;
	git_vector_sort(&index->entries);
Edward Thomson committed
253 254 255 256 257

	index->reuc._cmp = ignore_case ? reuc_icmp : reuc_cmp;
	index->reuc_search = ignore_case ? reuc_isrch : reuc_srch;
	index->reuc.sorted = 0;
	git_vector_sort(&index->reuc);
258 259
}

260
int git_index_open(git_index **index_out, const char *index_path)
261 262 263
{
	git_index *index;

264
	assert(index_out);
265

266 267
	index = git__calloc(1, sizeof(git_index));
	GITERR_CHECK_ALLOC(index);
268

269 270 271 272 273 274 275 276
	if (index_path != NULL) {
		index->index_file_path = git__strdup(index_path);
		GITERR_CHECK_ALLOC(index->index_file_path);

		/* Check if index file is stored on disk already */
		if (git_path_exists(index->index_file_path) == true)
			index->on_disk = 1;
	}
277

278 279
	if (git_vector_init(&index->entries, 32, index_cmp) < 0)
		return -1;
280

Edward Thomson committed
281
	index->entries_cmp_path = index_cmp_path;
282
	index->entries_search = index_srch;
Edward Thomson committed
283 284
	index->entries_search_path = index_srch_path;
	index->reuc_search = reuc_srch;
285

Vicent Marti committed
286
	*index_out = index;
287
	GIT_REFCOUNT_INC(index);
288 289

	return (index_path != NULL) ? git_index_read(index) : 0;
290 291
}

292 293 294 295 296
int git_index_new(git_index **out)
{
	return git_index_open(out, NULL);
}

297
static void index_free(git_index *index)
298
{
299
	git_index_entry *e;
Edward Thomson committed
300
	git_index_reuc_entry *reuc;
301
	size_t i;
302

303
	git_index_clear(index);
304 305 306
	git_vector_foreach(&index->entries, i, e) {
		index_entry_free(e);
	}
307
	git_vector_free(&index->entries);
Edward Thomson committed
308 309
	git_vector_foreach(&index->reuc, i, reuc) {
		index_entry_reuc_free(reuc);
310
	}
Edward Thomson committed
311
	git_vector_free(&index->reuc);
312

313 314
	git__free(index->index_file_path);
	git__free(index);
315 316
}

317 318
void git_index_free(git_index *index)
{
319
	if (index == NULL)
320 321
		return;

322
	GIT_REFCOUNT_DEC(index, index_free);
323 324
}

325 326 327
void git_index_clear(git_index *index)
{
	unsigned int i;
328 329 330

	assert(index);

331 332 333
	for (i = 0; i < index->entries.length; ++i) {
		git_index_entry *e;
		e = git_vector_get(&index->entries, i);
334 335
		git__free(e->path);
		git__free(e);
336
	}
337

Edward Thomson committed
338 339 340
	for (i = 0; i < index->reuc.length; ++i) {
		git_index_reuc_entry *e;
		e = git_vector_get(&index->reuc, i);
341 342
		git__free(e->path);
		git__free(e);
343 344
	}

345
	git_vector_clear(&index->entries);
Edward Thomson committed
346
	git_vector_clear(&index->reuc);
347
	git_futils_filestamp_set(&index->stamp, NULL);
348

349
	git_tree_cache_free(index->tree);
350 351 352
	index->tree = NULL;
}

353 354 355 356 357 358
static int create_index_error(int error, const char *msg)
{
	giterr_set(GITERR_INDEX, msg);
	return error;
}

359 360
int git_index_set_caps(git_index *index, unsigned int caps)
{
361 362
	int old_ignore_case;

363 364
	assert(index);

365 366
	old_ignore_case = index->ignore_case;

367 368 369 370 371 372
	if (caps == GIT_INDEXCAP_FROM_OWNER) {
		git_config *cfg;
		int val;

		if (INDEX_OWNER(index) == NULL ||
			git_repository_config__weakptr(&cfg, INDEX_OWNER(index)) < 0)
373 374
				return create_index_error(-1,
					"Cannot get repository config to set index caps");
375 376 377 378 379 380

		if (git_config_get_bool(&val, cfg, "core.ignorecase") == 0)
			index->ignore_case = (val != 0);
		if (git_config_get_bool(&val, cfg, "core.filemode") == 0)
			index->distrust_filemode = (val == 0);
		if (git_config_get_bool(&val, cfg, "core.symlinks") == 0)
381
			index->no_symlinks = (val == 0);
382 383 384 385 386 387 388
	}
	else {
		index->ignore_case = ((caps & GIT_INDEXCAP_IGNORE_CASE) != 0);
		index->distrust_filemode = ((caps & GIT_INDEXCAP_NO_FILEMODE) != 0);
		index->no_symlinks = ((caps & GIT_INDEXCAP_NO_SYMLINKS) != 0);
	}

389 390 391 392 393
	if (old_ignore_case != index->ignore_case)
	{
		index_set_ignore_case(index, index->ignore_case);
	}

394 395 396 397 398 399 400 401 402 403
	return 0;
}

unsigned int git_index_caps(const git_index *index)
{
	return ((index->ignore_case ? GIT_INDEXCAP_IGNORE_CASE : 0) |
			(index->distrust_filemode ? GIT_INDEXCAP_NO_FILEMODE : 0) |
			(index->no_symlinks ? GIT_INDEXCAP_NO_SYMLINKS : 0));
}

404 405
int git_index_read(git_index *index)
{
406
	int error = 0, updated;
407
	git_buf buffer = GIT_BUF_INIT;
408
	git_futils_filestamp stamp = {0};
409

410 411
	if (!index->index_file_path)
		return create_index_error(-1,
412
			"Failed to read index: The index is in-memory only");
413

414
	if (!index->on_disk || git_path_exists(index->index_file_path) == false) {
415 416
		git_index_clear(index);
		index->on_disk = 0;
417
		return 0;
418 419
	}

420 421 422 423 424
	updated = git_futils_filestamp_check(&stamp, index->index_file_path);
	if (updated <= 0)
		return updated;

	error = git_futils_readbuffer(&buffer, index->index_file_path);
425 426
	if (error < 0)
		return error;
427

428 429
	git_index_clear(index);
	error = parse_index(index, buffer.ptr, buffer.size);
430

431 432
	if (!error)
		git_futils_filestamp_set(&index->stamp, &stamp);
433

434
	git_buf_free(&buffer);
435 436 437 438 439
	return error;
}

int git_index_write(git_index *index)
{
440
	git_filebuf file = GIT_FILEBUF_INIT;
441
	int error;
442

443 444 445
	if (!index->index_file_path)
		return create_index_error(-1,
			"Failed to read index: The index is in-memory only");
446

447
	git_vector_sort(&index->entries);
Edward Thomson committed
448
	git_vector_sort(&index->reuc);
449

450 451 452
	if ((error = git_filebuf_open(
			 &file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < 0)
		return error;
453

454
	if ((error = write_index(index, &file)) < 0) {
455
		git_filebuf_cleanup(&file);
456
		return error;
457 458
	}

459 460
	if ((error = git_filebuf_commit(&file, GIT_INDEX_FILE_MODE)) < 0)
		return error;
461

462 463 464
	error = git_futils_filestamp_check(&index->stamp, index->index_file_path);
	if (error < 0)
		return error;
465

466
	index->on_disk = 1;
467
	return 0;
468 469
}

470 471 472 473 474 475
int git_index_write_tree(git_oid *oid, git_index *index)
{
	git_repository *repo;

	assert(oid && index);

476
	repo = INDEX_OWNER(index);
477

478 479
	if (repo == NULL)
		return create_index_error(-1, "Failed to write tree. "
480 481 482 483 484 485 486 487 488 489 490
		  "The index file is not backed up by an existing repository");

	return git_tree__write_index(oid, index, repo);
}

int git_index_write_tree_to(git_oid *oid, git_index *index, git_repository *repo)
{
	assert(oid && index && repo);
	return git_tree__write_index(oid, index, repo);
}

491
size_t git_index_entrycount(const git_index *index)
492 493
{
	assert(index);
494
	return index->entries.length;
495 496
}

497 498
const git_index_entry *git_index_get_byindex(
	git_index *index, size_t n)
499 500
{
	assert(index);
Edward Thomson committed
501 502
	git_vector_sort(&index->entries);
	return git_vector_get(&index->entries, n);
503 504
}

505 506
const git_index_entry *git_index_get_bypath(
	git_index *index, const char *path, int stage)
507
{
Edward Thomson committed
508 509 510 511
	int pos;

	assert(index);

512
	git_vector_sort(&index->entries);
Edward Thomson committed
513 514 515 516 517

	if((pos = index_find(index, path, stage)) < 0)
		return NULL;

	return git_index_get_byindex(index, pos);
518 519
}

520
void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st)
521 522 523 524 525 526 527 528 529 530 531 532 533
{
	entry->ctime.seconds = (git_time_t)st->st_ctime;
	entry->mtime.seconds = (git_time_t)st->st_mtime;
	/* entry->mtime.nanoseconds = st->st_mtimensec; */
	/* entry->ctime.nanoseconds = st->st_ctimensec; */
	entry->dev  = st->st_rdev;
	entry->ino  = st->st_ino;
	entry->mode = index_create_mode(st->st_mode);
	entry->uid  = st->st_uid;
	entry->gid  = st->st_gid;
	entry->file_size = st->st_size;
}

534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
int git_index_entry__cmp(const void *a, const void *b)
{
	const git_index_entry *entry_a = a;
	const git_index_entry *entry_b = b;

	return strcmp(entry_a->path, entry_b->path);
}

int git_index_entry__cmp_icase(const void *a, const void *b)
{
	const git_index_entry *entry_a = a;
	const git_index_entry *entry_b = b;

	return strcasecmp(entry_a->path, entry_b->path);
}

Edward Thomson committed
550
static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path)
551
{
552
	git_index_entry *entry = NULL;
553 554
	struct stat st;
	git_oid oid;
555 556
	const char *workdir;
	git_buf full_path = GIT_BUF_INIT;
557
	int error;
558

559 560 561 562 563 564 565 566 567
	if (INDEX_OWNER(index) == NULL)
		return create_index_error(-1,
			"Could not initialize index entry. "
			"Index is not backed up by an existing repository.");

	workdir = git_repository_workdir(INDEX_OWNER(index));

	if (!workdir)
		return create_index_error(GIT_EBAREREPO,
568
			"Could not initialize index entry. Repository is bare");
569

570
	if ((error = git_buf_joinpath(&full_path, workdir, rel_path)) < 0)
571 572
		return error;

573
	if ((error = git_path_lstat(full_path.ptr, &st)) < 0) {
574 575 576 577 578 579
		git_buf_free(&full_path);
		return error;
	}

	git_buf_free(&full_path); /* done with full path */

580 581 582 583
	/* There is no need to validate the rel_path here, since it will be
	 * immediately validated by the call to git_blob_create_fromfile.
	 */

584
	/* write the blob to disk and get the oid */
Vicent Marti committed
585
	if ((error = git_blob_create_fromworkdir(&oid, INDEX_OWNER(index), rel_path)) < 0)
586
		return error;
587

588
	entry = git__calloc(1, sizeof(git_index_entry));
589
	GITERR_CHECK_ALLOC(entry);
590

591
	git_index_entry__init_from_stat(entry, &st);
592 593

	entry->oid = oid;
594
	entry->path = git__strdup(rel_path);
595
	GITERR_CHECK_ALLOC(entry->path);
596 597

	*entry_out = entry;
598
	return 0;
599 600
}

Edward Thomson committed
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618
static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
	const char *path,
	int ancestor_mode, git_oid *ancestor_oid,
	int our_mode, git_oid *our_oid, int their_mode, git_oid *their_oid)
{
	git_index_reuc_entry *reuc = NULL;

	assert(reuc_out && path);

	*reuc_out = NULL;

	reuc = git__calloc(1, sizeof(git_index_reuc_entry));
	GITERR_CHECK_ALLOC(reuc);

	reuc->path = git__strdup(path);
	if (reuc->path == NULL)
		return -1;

619 620
	if ((reuc->mode[0] = ancestor_mode) > 0)
		git_oid_cpy(&reuc->oid[0], ancestor_oid);
Edward Thomson committed
621

622 623
	if ((reuc->mode[1] = our_mode) > 0)
		git_oid_cpy(&reuc->oid[1], our_oid);
Edward Thomson committed
624

625 626
	if ((reuc->mode[2] = their_mode) > 0)
		git_oid_cpy(&reuc->oid[2], their_oid);
Edward Thomson committed
627 628 629 630 631 632 633 634 635 636 637 638 639 640

	*reuc_out = reuc;
	return 0;
}

static void index_entry_reuc_free(git_index_reuc_entry *reuc)
{
	if (!reuc)
		return;

	git__free(reuc->path);
	git__free(reuc);
}

641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
static git_index_entry *index_entry_dup(const git_index_entry *source_entry)
{
	git_index_entry *entry;

	entry = git__malloc(sizeof(git_index_entry));
	if (!entry)
		return NULL;

	memcpy(entry, source_entry, sizeof(git_index_entry));

	/* duplicate the path string so we own it */
	entry->path = git__strdup(entry->path);
	if (!entry->path)
		return NULL;

	return entry;
}

659 660 661 662
static void index_entry_free(git_index_entry *entry)
{
	if (!entry)
		return;
663 664
	git__free(entry->path);
	git__free(entry);
665 666
}

667
static int index_insert(git_index *index, git_index_entry *entry, int replace)
668
{
669 670
	size_t path_length;
	int position;
671
	git_index_entry **existing = NULL;
672

673
	assert(index && entry && entry->path != NULL);
674

675
	/* make sure that the path length flag is correct */
676
	path_length = strlen(entry->path);
677

678
	entry->flags &= ~GIT_IDXENTRY_NAMEMASK;
679 680

	if (path_length < GIT_IDXENTRY_NAMEMASK)
681
		entry->flags |= path_length & GIT_IDXENTRY_NAMEMASK;
682
	else
Edward Thomson committed
683
		entry->flags |= GIT_IDXENTRY_NAMEMASK;
684

685
	/* look if an entry with this path already exists */
Edward Thomson committed
686
	if ((position = index_find(index, entry->path, index_entry_stage(entry))) >= 0) {
687
		existing = (git_index_entry **)&index->entries.contents[position];
688

689 690 691 692 693 694
		/* update filemode to existing values if stat is not trusted */
		entry->mode = index_merge_mode(index, *existing, entry->mode);
	}

	/* if replacing is not requested or no existing entry exists, just
	 * insert entry at the end; the index is no longer sorted
695
	 */
696
	if (!replace || !existing)
697
		return git_vector_insert(&index->entries, entry);
698 699

	/* exists, replace it */
700 701 702
	git__free((*existing)->path);
	git__free(*existing);
	*existing = entry;
703

704
	return 0;
705 706
}

Edward Thomson committed
707
static int index_conflict_to_reuc(git_index *index, const char *path)
708
{
Edward Thomson committed
709 710 711
	git_index_entry *conflict_entries[3];
	int ancestor_mode, our_mode, their_mode;
	git_oid *ancestor_oid, *our_oid, *their_oid;
712
	int ret;
713

Edward Thomson committed
714 715
	if ((ret = git_index_conflict_get(&conflict_entries[0],
		&conflict_entries[1], &conflict_entries[2], index, path)) < 0)
716
		return ret;
717

Edward Thomson committed
718 719 720
	ancestor_mode = conflict_entries[0] == NULL ? 0 : conflict_entries[0]->mode;
	our_mode = conflict_entries[1] == NULL ? 0 : conflict_entries[1]->mode;
	their_mode = conflict_entries[2] == NULL ? 0 : conflict_entries[2]->mode;
721

Edward Thomson committed
722 723 724 725 726 727 728 729 730
	ancestor_oid = conflict_entries[0] == NULL ? NULL : &conflict_entries[0]->oid;
	our_oid = conflict_entries[1] == NULL ? NULL : &conflict_entries[1]->oid;
	their_oid = conflict_entries[2] == NULL ? NULL : &conflict_entries[2]->oid;

	if ((ret = git_index_reuc_add(index, path, ancestor_mode, ancestor_oid,
		our_mode, our_oid, their_mode, their_oid)) >= 0)
		ret = git_index_conflict_remove(index, path);

	return ret;
731 732
}

733
int git_index_add_bypath(git_index *index, const char *path)
734
{
Edward Thomson committed
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
	git_index_entry *entry = NULL;
	int ret;

	assert(index && path);

	if ((ret = index_entry_init(&entry, index, path)) < 0 ||
		(ret = index_insert(index, entry, 1)) < 0)
		goto on_error;

	/* Adding implies conflict was resolved, move conflict entries to REUC */
	if ((ret = index_conflict_to_reuc(index, path)) < 0 && ret != GIT_ENOTFOUND)
		goto on_error;

	git_tree_cache_invalidate_path(index->tree, entry->path);
	return 0;

on_error:
	index_entry_free(entry);
	return ret;
754 755
}

756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
int git_index_remove_bypath(git_index *index, const char *path)
{
	int ret;

	assert(index && path);

	if (((ret = git_index_remove(index, path, 0)) < 0 &&
		ret != GIT_ENOTFOUND) ||
		((ret = index_conflict_to_reuc(index, path)) < 0 &&
		ret != GIT_ENOTFOUND))
		return ret;

	return 0;
}

Edward Thomson committed
771
int git_index_add(git_index *index, const git_index_entry *source_entry)
772 773 774 775 776
{
	git_index_entry *entry = NULL;
	int ret;

	entry = index_entry_dup(source_entry);
777 778
	if (entry == NULL)
		return -1;
779

Edward Thomson committed
780
	if ((ret = index_insert(index, entry, 1)) < 0) {
781 782 783
		index_entry_free(entry);
		return ret;
	}
784

785
	git_tree_cache_invalidate_path(index->tree, entry->path);
786
	return 0;
787 788
}

Edward Thomson committed
789
int git_index_remove(git_index *index, const char *path, int stage)
790
{
Edward Thomson committed
791
	int position;
792
	int error;
793 794
	git_index_entry *entry;

795
	git_vector_sort(&index->entries);
796

Edward Thomson committed
797 798 799
	if ((position = index_find(index, path, stage)) < 0)
		return position;

800 801 802 803
	entry = git_vector_get(&index->entries, position);
	if (entry != NULL)
		git_tree_cache_invalidate_path(index->tree, entry->path);

804 805
	error = git_vector_remove(&index->entries, (unsigned int)position);

806
	if (!error)
807 808 809
		index_entry_free(entry);

	return error;
810
}
811

812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849
int git_index_remove_directory(git_index *index, const char *dir, int stage)
{
	git_buf pfx = GIT_BUF_INIT;
	int error = 0;
	size_t pos;
	git_index_entry *entry;

	if (git_buf_sets(&pfx, dir) < 0 || git_path_to_dir(&pfx) < 0)
		return -1;

	git_vector_sort(&index->entries);

	pos = git_index__prefix_position(index, pfx.ptr);

	while (1) {
		entry = git_vector_get(&index->entries, pos);
		if (!entry || git__prefixcmp(entry->path, pfx.ptr) != 0)
			break;

		if (index_entry_stage(entry) != stage) {
			++pos;
			continue;
		}

		git_tree_cache_invalidate_path(index->tree, entry->path);

		if ((error = git_vector_remove(&index->entries, pos)) < 0)
			break;
		index_entry_free(entry);

		/* removed entry at 'pos' so we don't need to increment it */
	}

	git_buf_free(&pfx);

	return error;
}

Edward Thomson committed
850 851 852 853 854 855 856 857 858 859 860 861
static int index_find(git_index *index, const char *path, int stage)
{
	struct entry_srch_key srch_key;

	assert(path);

	srch_key.path = path;
	srch_key.stage = stage;

	return git_vector_bsearch2(&index->entries, index->entries_search, &srch_key);
}

862 863
int git_index_find(git_index *index, const char *path)
{
Edward Thomson committed
864 865 866 867
	int pos;

	assert(index && path);

868 869
	if ((pos = git_vector_bsearch2(
			&index->entries, index->entries_search_path, path)) < 0)
870 871
	{
		giterr_set(GITERR_INDEX, "Index does not contain %s", path);
Edward Thomson committed
872
		return pos;
873
	}
Edward Thomson committed
874 875

	/* Since our binary search only looked at path, we may be in the
876 877
	 * middle of a list of stages.
	 */
Edward Thomson committed
878
	while (pos > 0) {
879
		const git_index_entry *prev = git_vector_get(&index->entries, pos-1);
Edward Thomson committed
880 881 882 883 884 885 886 887

		if (index->entries_cmp_path(prev->path, path) != 0)
			break;

		--pos;
	}

	return pos;
888 889
}

890
size_t git_index__prefix_position(git_index *index, const char *path)
891
{
Edward Thomson committed
892
	struct entry_srch_key srch_key;
893
	size_t pos;
894

Edward Thomson committed
895 896 897
	srch_key.path = path;
	srch_key.stage = 0;

898 899 900
	git_vector_sort(&index->entries);
	git_vector_bsearch3(
		&pos, &index->entries, index->entries_search, &srch_key);
901 902 903 904

	return pos;
}

Edward Thomson committed
905 906 907 908 909 910
int git_index_conflict_add(git_index *index,
	const git_index_entry *ancestor_entry,
	const git_index_entry *our_entry,
	const git_index_entry *their_entry)
{
	git_index_entry *entries[3] = { 0 };
911
	unsigned short i;
Edward Thomson committed
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948
	int ret = 0;

	assert (index);

	if ((ancestor_entry != NULL && (entries[0] = index_entry_dup(ancestor_entry)) == NULL) ||
		(our_entry != NULL && (entries[1] = index_entry_dup(our_entry)) == NULL) ||
		(their_entry != NULL && (entries[2] = index_entry_dup(their_entry)) == NULL))
		return -1;

	for (i = 0; i < 3; i++) {
		if (entries[i] == NULL)
			continue;

		/* Make sure stage is correct */
		entries[i]->flags = (entries[i]->flags & ~GIT_IDXENTRY_STAGEMASK) |
			((i+1) << GIT_IDXENTRY_STAGESHIFT);

		if ((ret = index_insert(index, entries[i], 1)) < 0)
			goto on_error;
	}

    return 0;

on_error:
	for (i = 0; i < 3; i++) {
		if (entries[i] != NULL)
			index_entry_free(entries[i]);
	}

	return ret;
}

int git_index_conflict_get(git_index_entry **ancestor_out,
	git_index_entry **our_out,
	git_index_entry **their_out,
	git_index *index, const char *path)
{
949
	int pos, posmax, stage;
Edward Thomson committed
950 951 952 953 954 955 956 957 958 959 960 961
	git_index_entry *conflict_entry;
	int error = GIT_ENOTFOUND;

	assert(ancestor_out && our_out && their_out && index && path);

	*ancestor_out = NULL;
	*our_out = NULL;
	*their_out = NULL;

	if ((pos = git_index_find(index, path)) < 0)
		return pos;

962 963
	for (posmax = (int)git_index_entrycount(index); pos < posmax; ++pos) {

Edward Thomson committed
964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993
		conflict_entry = git_vector_get(&index->entries, pos);

		if (index->entries_cmp_path(conflict_entry->path, path) != 0)
			break;

		stage = index_entry_stage(conflict_entry);

		switch (stage) {
		case 3:
			*their_out = conflict_entry;
			error = 0;
			break;
		case 2:
			*our_out = conflict_entry;
			error = 0;
			break;
		case 1:
			*ancestor_out = conflict_entry;
			error = 0;
			break;
		default:
			break;
		};
	}

	return error;
}

int git_index_conflict_remove(git_index *index, const char *path)
{
994
	int pos, posmax;
Edward Thomson committed
995
	git_index_entry *conflict_entry;
996
	int error = 0;
Edward Thomson committed
997 998 999 1000 1001 1002

	assert(index && path);

	if ((pos = git_index_find(index, path)) < 0)
		return pos;

1003 1004 1005
	posmax = (int)git_index_entrycount(index);

	while (pos < posmax) {
Edward Thomson committed
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
		conflict_entry = git_vector_get(&index->entries, pos);

		if (index->entries_cmp_path(conflict_entry->path, path) != 0)
			break;

		if (index_entry_stage(conflict_entry) == 0) {
			pos++;
			continue;
		}

1016 1017
		if ((error = git_vector_remove(&index->entries, (unsigned int)pos)) < 0)
			return error;
1018

1019 1020
		index_entry_free(conflict_entry);
		posmax--;
Edward Thomson committed
1021 1022
	}

1023
	return 0;
Edward Thomson committed
1024 1025
}

1026
static int index_conflicts_match(const git_vector *v, size_t idx)
Edward Thomson committed
1027 1028 1029
{
	git_index_entry *entry = git_vector_get(v, idx);

1030 1031
	if (index_entry_stage(entry) > 0) {
		index_entry_free(entry);
Edward Thomson committed
1032
		return 1;
1033
	}
Edward Thomson committed
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043

	return 0;
}

void git_index_conflict_cleanup(git_index *index)
{
	assert(index);
	git_vector_remove_matching(&index->entries, index_conflicts_match);
}

1044
int git_index_has_conflicts(const git_index *index)
1045
{
1046
	size_t i;
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
	git_index_entry *entry;

	assert(index);

	git_vector_foreach(&index->entries, i, entry) {
		if (index_entry_stage(entry) > 0)
			return 1;
	}

	return 0;
}

Edward Thomson committed
1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
unsigned int git_index_reuc_entrycount(git_index *index)
{
	assert(index);
	return (unsigned int)index->reuc.length;
}

static int index_reuc_insert(git_index *index, git_index_reuc_entry *reuc, int replace)
{
	git_index_reuc_entry **existing = NULL;
	int position;

	assert(index && reuc && reuc->path != NULL);

	if ((position = git_index_reuc_find(index, reuc->path)) >= 0)
		existing = (git_index_reuc_entry **)&index->reuc.contents[position];

	if (!replace || !existing)
		return git_vector_insert(&index->reuc, reuc);

	/* exists, replace it */
	git__free((*existing)->path);
	git__free(*existing);
	*existing = reuc;

	return 0;
}

int git_index_reuc_add(git_index *index, const char *path,
	int ancestor_mode, git_oid *ancestor_oid,
	int our_mode, git_oid *our_oid,
	int their_mode, git_oid *their_oid)
{
	git_index_reuc_entry *reuc = NULL;
	int error = 0;

	assert(index && path);

	if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode, ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 ||
		(error = index_reuc_insert(index, reuc, 1)) < 0)
	{
		index_entry_reuc_free(reuc);
		return error;
	}

	return error;
} 

int git_index_reuc_find(git_index *index, const char *path)
1107
{
Edward Thomson committed
1108
	return git_vector_bsearch2(&index->reuc, index->reuc_search, path);
1109 1110
}

Edward Thomson committed
1111
const git_index_reuc_entry *git_index_reuc_get_bypath(
1112
	git_index *index, const char *path)
1113 1114
{
	int pos;
1115
	assert(index && path);
1116

Edward Thomson committed
1117
	if (!index->reuc.length)
1118
		return NULL;
1119

Edward Thomson committed
1120 1121 1122
	git_vector_sort(&index->reuc);

	if ((pos = git_index_reuc_find(index, path)) < 0)
1123
		return NULL;
1124

Edward Thomson committed
1125
	return git_vector_get(&index->reuc, pos);
1126 1127
}

Edward Thomson committed
1128
const git_index_reuc_entry *git_index_reuc_get_byindex(
1129
	git_index *index, size_t n)
1130 1131
{
	assert(index);
Edward Thomson committed
1132 1133 1134 1135 1136

	git_vector_sort(&index->reuc);
	return git_vector_get(&index->reuc, n);
}

Ben Straub committed
1137
int git_index_reuc_remove(git_index *index, size_t position)
Edward Thomson committed
1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
{
	int error;
	git_index_reuc_entry *reuc;

	git_vector_sort(&index->reuc);

	reuc = git_vector_get(&index->reuc, position);
	error = git_vector_remove(&index->reuc, (unsigned int)position);

	if (!error)
		index_entry_reuc_free(reuc);

	return error;
1151 1152
}

1153 1154 1155 1156 1157 1158
static int index_error_invalid(const char *message)
{
	giterr_set(GITERR_INDEX, "Invalid data in index - %s", message);
	return -1;
}

Edward Thomson committed
1159
static int read_reuc(git_index *index, const char *buffer, size_t size)
1160
{
1161 1162
	const char *endptr;
	size_t len;
1163 1164
	int i;

1165 1166
	/* This gets called multiple times, the vector might already be initialized */
	if (index->reuc._alloc_size == 0 && git_vector_init(&index->reuc, 16, reuc_cmp) < 0)
1167
		return -1;
1168 1169

	while (size) {
Edward Thomson committed
1170
		git_index_reuc_entry *lost;
1171 1172 1173

		len = strlen(buffer) + 1;
		if (size <= len)
Edward Thomson committed
1174
			return index_error_invalid("reading reuc entries");
1175

Edward Thomson committed
1176
		lost = git__malloc(sizeof(git_index_reuc_entry));
1177
		GITERR_CHECK_ALLOC(lost);
1178

Edward Thomson committed
1179
		if (git_vector_insert(&index->reuc, lost) < 0)
1180
			return -1;
1181

1182
		/* read NUL-terminated pathname for entry */
1183
		lost->path = git__strdup(buffer);
1184
		GITERR_CHECK_ALLOC(lost->path);
1185

1186 1187 1188
		size -= len;
		buffer += len;

1189
		/* read 3 ASCII octal numbers for stage entries */
1190
		for (i = 0; i < 3; i++) {
1191
			int tmp;
1192

1193 1194 1195
			if (git__strtol32(&tmp, buffer, &endptr, 8) < 0 ||
				!endptr || endptr == buffer || *endptr ||
				(unsigned)tmp > UINT_MAX)
Edward Thomson committed
1196
				return index_error_invalid("reading reuc entry stage");
1197

1198 1199
			lost->mode[i] = tmp;

1200
			len = (endptr + 1) - buffer;
1201
			if (size <= len)
Edward Thomson committed
1202
				return index_error_invalid("reading reuc entry stage");
1203

1204 1205 1206 1207
			size -= len;
			buffer += len;
		}

1208
		/* read up to 3 OIDs for stage entries */
1209 1210 1211 1212
		for (i = 0; i < 3; i++) {
			if (!lost->mode[i])
				continue;
			if (size < 20)
Edward Thomson committed
1213
				return index_error_invalid("reading reuc entry oid");
1214

1215
			git_oid_fromraw(&lost->oid[i], (const unsigned char *) buffer);
1216 1217 1218 1219 1220
			size -= 20;
			buffer += 20;
		}
	}

Edward Thomson committed
1221 1222 1223
	/* entries are guaranteed to be sorted on-disk */
	index->reuc.sorted = 1;

1224
	return 0;
1225 1226
}

1227 1228 1229 1230 1231
static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size)
{
	size_t path_length, entry_size;
	uint16_t flags_raw;
	const char *path_ptr;
1232
	const struct entry_short *source = buffer;
1233 1234 1235 1236

	if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size)
		return 0;

1237 1238
	memset(dest, 0x0, sizeof(git_index_entry));

1239 1240 1241 1242
	dest->ctime.seconds = (git_time_t)ntohl(source->ctime.seconds);
	dest->ctime.nanoseconds = ntohl(source->ctime.nanoseconds);
	dest->mtime.seconds = (git_time_t)ntohl(source->mtime.seconds);
	dest->mtime.nanoseconds = ntohl(source->mtime.nanoseconds);
1243 1244 1245 1246 1247 1248 1249 1250 1251 1252
	dest->dev = ntohl(source->dev);
	dest->ino = ntohl(source->ino);
	dest->mode = ntohl(source->mode);
	dest->uid = ntohl(source->uid);
	dest->gid = ntohl(source->gid);
	dest->file_size = ntohl(source->file_size);
	git_oid_cpy(&dest->oid, &source->oid);
	dest->flags = ntohs(source->flags);

	if (dest->flags & GIT_IDXENTRY_EXTENDED) {
1253
		const struct entry_long *source_l = (const struct entry_long *)source;
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269
		path_ptr = source_l->path;

		flags_raw = ntohs(source_l->flags_extended);
		memcpy(&dest->flags_extended, &flags_raw, 2);
	} else
		path_ptr = source->path;

	path_length = dest->flags & GIT_IDXENTRY_NAMEMASK;

	/* if this is a very long string, we must find its
	 * real length without overflowing */
	if (path_length == 0xFFF) {
		const char *path_end;

		path_end = memchr(path_ptr, '\0', buffer_size);
		if (path_end == NULL)
1270
			return 0;
1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283

		path_length = path_end - path_ptr;
	}

	if (dest->flags & GIT_IDXENTRY_EXTENDED)
		entry_size = long_entry_size(path_length);
	else
		entry_size = short_entry_size(path_length);

	if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
		return 0;

	dest->path = git__strdup(path_ptr);
1284
	assert(dest->path);
1285 1286 1287 1288 1289 1290

	return entry_size;
}

static int read_header(struct index_header *dest, const void *buffer)
{
1291
	const struct index_header *source = buffer;
1292

1293 1294
	dest->signature = ntohl(source->signature);
	if (dest->signature != INDEX_HEADER_SIG)
1295
		return index_error_invalid("incorrect header signature");
1296 1297

	dest->version = ntohl(source->version);
1298 1299
	if (dest->version != INDEX_VERSION_NUMBER_EXT &&
		dest->version != INDEX_VERSION_NUMBER)
1300
		return index_error_invalid("incorrect header version");
1301 1302

	dest->entry_count = ntohl(source->entry_count);
1303
	return 0;
1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325
}

static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size)
{
	const struct index_extension *source;
	struct index_extension dest;
	size_t total_size;

	source = (const struct index_extension *)(buffer);

	memcpy(dest.signature, source->signature, 4);
	dest.extension_size = ntohl(source->extension_size);

	total_size = dest.extension_size + sizeof(struct index_extension);

	if (buffer_size - total_size < INDEX_FOOTER_SIZE)
		return 0;

	/* optional extension */
	if (dest.signature[0] >= 'A' && dest.signature[0] <= 'Z') {
		/* tree cache */
		if (memcmp(dest.signature, INDEX_EXT_TREECACHE_SIG, 4) == 0) {
1326
			if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size) < 0)
1327
				return 0;
1328
		} else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) {
Edward Thomson committed
1329
			if (read_reuc(index, buffer + 8, dest.extension_size) < 0)
1330
				return 0;
1331
		}
1332 1333
		/* else, unsupported extension. We cannot parse this, but we can skip
		 * it by returning `total_size */
1334 1335 1336 1337 1338 1339 1340 1341 1342
	} else {
		/* we cannot handle non-ignorable extensions;
		 * in fact they aren't even defined in the standard */
		return 0;
	}

	return total_size;
}

1343
static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
1344 1345 1346 1347 1348 1349 1350
{
	unsigned int i;
	struct index_header header;
	git_oid checksum_calculated, checksum_expected;

#define seek_forward(_increase) { \
	if (_increase >= buffer_size) \
1351
		return index_error_invalid("ran out of data while parsing"); \
1352 1353 1354 1355 1356
	buffer += _increase; \
	buffer_size -= _increase;\
}

	if (buffer_size < INDEX_HEADER_SIZE + INDEX_FOOTER_SIZE)
1357
		return index_error_invalid("insufficient buffer space");
1358 1359 1360

	/* Precalculate the SHA1 of the files's contents -- we'll match it to
	 * the provided SHA1 in the footer */
1361
	git_hash_buf(&checksum_calculated, buffer, buffer_size - INDEX_FOOTER_SIZE);
1362 1363

	/* Parse header */
1364 1365
	if (read_header(&header, buffer) < 0)
		return -1;
1366 1367 1368

	seek_forward(INDEX_HEADER_SIZE);

1369
	git_vector_clear(&index->entries);
1370 1371

	/* Parse all the entries */
1372
	for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
1373
		size_t entry_size;
1374 1375 1376
		git_index_entry *entry;

		entry = git__malloc(sizeof(git_index_entry));
1377
		GITERR_CHECK_ALLOC(entry);
1378 1379

		entry_size = read_entry(entry, buffer, buffer_size);
1380 1381 1382

		/* 0 bytes read means an object corruption */
		if (entry_size == 0)
1383
			return index_error_invalid("invalid entry");
1384

1385 1386
		if (git_vector_insert(&index->entries, entry) < 0)
			return -1;
1387

1388 1389 1390
		seek_forward(entry_size);
	}

1391
	if (i != header.entry_count)
1392
		return index_error_invalid("header entries changed while parsing");
1393

1394 1395 1396 1397 1398 1399 1400 1401
	/* There's still space for some extensions! */
	while (buffer_size > INDEX_FOOTER_SIZE) {
		size_t extension_size;

		extension_size = read_extension(index, buffer, buffer_size);

		/* see if we have read any bytes from the extension */
		if (extension_size == 0)
1402
			return index_error_invalid("extension size is zero");
1403 1404 1405 1406 1407

		seek_forward(extension_size);
	}

	if (buffer_size != INDEX_FOOTER_SIZE)
1408
		return index_error_invalid("buffer size does not match index footer size");
1409 1410

	/* 160-bit SHA-1 over the content of the index file before this checksum. */
Vicent Marti committed
1411
	git_oid_fromraw(&checksum_expected, (const unsigned char *)buffer);
1412 1413

	if (git_oid_cmp(&checksum_calculated, &checksum_expected) != 0)
1414
		return index_error_invalid("calculated checksum does not match expected");
1415 1416 1417

#undef seek_forward

1418 1419 1420 1421
	/* Entries are stored case-sensitively on disk. */
	index->entries.sorted = !index->ignore_case;
	git_vector_sort(&index->entries);

1422
	return 0;
1423 1424
}

1425
static bool is_index_extended(git_index *index)
Vicent Marti committed
1426
{
1427
	size_t i, extended;
1428
	git_index_entry *entry;
Vicent Marti committed
1429 1430 1431

	extended = 0;

1432
	git_vector_foreach(&index->entries, i, entry) {
Vicent Marti committed
1433 1434 1435 1436 1437 1438
		entry->flags &= ~GIT_IDXENTRY_EXTENDED;
		if (entry->flags_extended & GIT_IDXENTRY_EXTENDED_FLAGS) {
			extended++;
			entry->flags |= GIT_IDXENTRY_EXTENDED;
		}
	}
1439

1440
	return (extended > 0);
Vicent Marti committed
1441 1442
}

1443
static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
1444
{
1445
	void *mem = NULL;
1446
	struct entry_short *ondisk;
1447
	size_t path_len, disk_size;
1448
	char *path;
1449

1450
	path_len = strlen(entry->path);
1451

1452
	if (entry->flags & GIT_IDXENTRY_EXTENDED)
1453
		disk_size = long_entry_size(path_len);
1454
	else
1455
		disk_size = short_entry_size(path_len);
1456

1457 1458
	if (git_filebuf_reserve(file, &mem, disk_size) < 0)
		return -1;
1459

1460 1461
	ondisk = (struct entry_short *)mem;

1462
	memset(ondisk, 0x0, disk_size);
1463

1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475
	/**
	 * Yes, we have to truncate.
	 *
	 * The on-disk format for Index entries clearly defines
	 * the time and size fields to be 4 bytes each -- so even if
	 * we store these values with 8 bytes on-memory, they must
	 * be truncated to 4 bytes before writing to disk.
	 *
	 * In 2038 I will be either too dead or too rich to care about this
	 */
	ondisk->ctime.seconds = htonl((uint32_t)entry->ctime.seconds);
	ondisk->mtime.seconds = htonl((uint32_t)entry->mtime.seconds);
1476 1477
	ondisk->ctime.nanoseconds = htonl(entry->ctime.nanoseconds);
	ondisk->mtime.nanoseconds = htonl(entry->mtime.nanoseconds);
Vicent Marti committed
1478 1479
	ondisk->dev = htonl(entry->dev);
	ondisk->ino = htonl(entry->ino);
1480
	ondisk->mode = htonl(entry->mode);
Vicent Marti committed
1481 1482
	ondisk->uid = htonl(entry->uid);
	ondisk->gid = htonl(entry->gid);
1483
	ondisk->file_size = htonl((uint32_t)entry->file_size);
1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498

	git_oid_cpy(&ondisk->oid, &entry->oid);

	ondisk->flags = htons(entry->flags);

	if (entry->flags & GIT_IDXENTRY_EXTENDED) {
		struct entry_long *ondisk_ext;
		ondisk_ext = (struct entry_long *)ondisk;
		ondisk_ext->flags_extended = htons(entry->flags_extended);
		path = ondisk_ext->path;
	}
	else
		path = ondisk->path;

	memcpy(path, entry->path, path_len);
1499

1500
	return 0;
1501 1502
}

1503
static int write_entries(git_index *index, git_filebuf *file)
1504
{
1505
	int error = 0;
1506
	size_t i;
1507 1508 1509 1510 1511 1512 1513 1514 1515 1516
	git_vector case_sorted;
	git_index_entry *entry;
	git_vector *out = &index->entries;

	/* If index->entries is sorted case-insensitively, then we need
	 * to re-sort it case-sensitively before writing */
	if (index->ignore_case) {
		git_vector_dup(&case_sorted, &index->entries, index_cmp);
		git_vector_sort(&case_sorted);
		out = &case_sorted;
1517 1518
	}

1519 1520 1521 1522 1523 1524 1525 1526
	git_vector_foreach(out, i, entry)
		if ((error = write_disk_entry(file, entry)) < 0)
			break;

	if (index->ignore_case)
		git_vector_free(&case_sorted);

	return error;
1527 1528
}

Edward Thomson committed
1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571
static int write_extension(git_filebuf *file, struct index_extension *header, git_buf *data)
{
	struct index_extension ondisk;
	int error = 0;

	memset(&ondisk, 0x0, sizeof(struct index_extension));
	memcpy(&ondisk, header, 4);
	ondisk.extension_size = htonl(header->extension_size);

	if ((error = git_filebuf_write(file, &ondisk, sizeof(struct index_extension))) == 0)
		error = git_filebuf_write(file, data->ptr, data->size);

	return error;
}

static int create_reuc_extension_data(git_buf *reuc_buf, git_index_reuc_entry *reuc)
{
	int i;
	int error = 0;

	if ((error = git_buf_put(reuc_buf, reuc->path, strlen(reuc->path) + 1)) < 0)
		return error;

	for (i = 0; i < 3; i++) {
		if ((error = git_buf_printf(reuc_buf, "%o", reuc->mode[i])) < 0 ||
			(error = git_buf_put(reuc_buf, "\0", 1)) < 0)
			return error;
	}

	for (i = 0; i < 3; i++) {
		if (reuc->mode[i] && (error = git_buf_put(reuc_buf, (char *)&reuc->oid[i].id, GIT_OID_RAWSZ)) < 0)
			return error;
	}

	return 0;
}

static int write_reuc_extension(git_index *index, git_filebuf *file)
{
	git_buf reuc_buf = GIT_BUF_INIT;
	git_vector *out = &index->reuc;
	git_index_reuc_entry *reuc;
	struct index_extension extension;
1572
	size_t i;
Edward Thomson committed
1573 1574 1575 1576 1577 1578 1579 1580 1581
	int error = 0;

	git_vector_foreach(out, i, reuc) {
		if ((error = create_reuc_extension_data(&reuc_buf, reuc)) < 0)
			goto done;
	}

	memset(&extension, 0x0, sizeof(struct index_extension));
	memcpy(&extension.signature, INDEX_EXT_UNMERGED_SIG, 4);
1582
	extension.extension_size = (uint32_t)reuc_buf.size;
Edward Thomson committed
1583 1584 1585 1586 1587 1588 1589 1590 1591

	error = write_extension(file, &extension, &reuc_buf);

	git_buf_free(&reuc_buf);

done:
	return error;
}

1592
static int write_index(git_index *index, git_filebuf *file)
1593 1594 1595
{
	git_oid hash_final;
	struct index_header header;
1596
	bool is_extended;
1597

1598
	assert(index && file);
1599

Vicent Marti committed
1600 1601
	is_extended = is_index_extended(index);

1602
	header.signature = htonl(INDEX_HEADER_SIG);
Vicent Marti committed
1603
	header.version = htonl(is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER);
1604
	header.entry_count = htonl((uint32_t)index->entries.length);
1605

1606 1607
	if (git_filebuf_write(file, &header, sizeof(struct index_header)) < 0)
		return -1;
1608

1609 1610
	if (write_entries(index, file) < 0)
		return -1;
1611

Edward Thomson committed
1612 1613 1614 1615 1616
	/* TODO: write tree cache extension */

	/* write the reuc extension */
	if (index->reuc.length > 0 && write_reuc_extension(index, file) < 0)
		return -1;
1617

1618 1619 1620 1621
	/* get out the hash for all the contents we've appended to the file */
	git_filebuf_hash(&hash_final, file);

	/* write it at the end of the file */
1622
	return git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ);
1623
}
1624 1625 1626

int git_index_entry_stage(const git_index_entry *entry)
{
Edward Thomson committed
1627
	return index_entry_stage(entry);
1628
}
1629

1630 1631
typedef struct read_tree_data {
	git_index *index;
1632
	git_transfer_progress *stats;
1633 1634
} read_tree_data;

1635
static int read_tree_cb(const char *root, const git_tree_entry *tentry, void *data)
1636
{
1637
	git_index *index = (git_index *)data;
1638 1639 1640
	git_index_entry *entry = NULL;
	git_buf path = GIT_BUF_INIT;

Vicent Martí committed
1641
	if (git_tree_entry__is_tree(tentry))
1642
		return 0;
1643

1644 1645
	if (git_buf_joinpath(&path, root, tentry->filename) < 0)
		return -1;
1646 1647

	entry = git__calloc(1, sizeof(git_index_entry));
1648
	GITERR_CHECK_ALLOC(entry);
1649 1650 1651 1652 1653 1654

	entry->mode = tentry->attr;
	entry->oid = tentry->oid;
	entry->path = git_buf_detach(&path);
	git_buf_free(&path);

1655
	if (index_insert(index, entry, 0) < 0) {
1656
		index_entry_free(entry);
1657 1658 1659 1660
		return -1;
	}

	return 0;
1661 1662
}

Ben Straub committed
1663
int git_index_read_tree(git_index *index, const git_tree *tree)
1664 1665 1666
{
	git_index_clear(index);

1667
	return git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, index);
1668
}
1669 1670 1671 1672 1673

git_repository *git_index_owner(const git_index *index)
{
	return INDEX_OWNER(index);
}
1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701

int git_index_read_tree_match(
	git_index *index, git_tree *tree, git_strarray *strspec)
{
#if 0
	git_iterator *iter = NULL;
	const git_index_entry *entry;
	char *pfx = NULL;
	git_vector pathspec = GIT_VECTOR_INIT;
	git_pool pathpool = GIT_POOL_INIT_STRINGPOOL;
#endif

	if (!git_pathspec_is_interesting(strspec))
		return git_index_read_tree(index, tree);

	return git_index_read_tree(index, tree);

#if 0
	/* The following loads the matches into the index, but doesn't
	 * erase obsoleted entries (e.g. you load a blob at "a/b" which
	 * should obsolete a blob at "a/b/c/d" since b is no longer a tree)
	 */

	if (git_pathspec_init(&pathspec, strspec, &pathpool) < 0)
		return -1;

	pfx = git_pathspec_prefix(strspec);

Russell Belfer committed
1702
	if ((error = git_iterator_for_tree_range(&iter, tree, pfx, pfx)) < 0 ||
1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723
		(error = git_iterator_current(iter, &entry)) < 0)
		goto cleanup;

	while (entry != NULL) {
		if (git_pathspec_match_path(&pathspec, entry->path, false, false) &&
			(error = git_index_add(index, entry)) < 0)
			goto cleanup;

		if ((error = git_iterator_advance(iter, &entry)) < 0)
			goto cleanup;
	}

cleanup:
	git_iterator_free(iter);
	git_pathspec_free(&pathspec);
	git_pool_clear(&pathpool);
	git__free(pfx);

	return error;
#endif
}