Exemple #1
0
void test_odb_freshen__loose_object(void)
{
	git_oid expected_id, id;
	struct stat before, after;
	struct p_timeval old_times[2];

	cl_git_pass(git_oid_fromstr(&expected_id, LOOSE_ID));

	old_times[0].tv_sec = 1234567890;
	old_times[0].tv_usec = 0;
	old_times[1].tv_sec = 1234567890;
	old_times[1].tv_usec = 0;

	/* set time to way back */
	cl_must_pass(p_utimes("testrepo.git/objects/" LOOSE_FN, old_times));
	cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_FN, &before));

	cl_git_pass(git_odb_write(&id, odb, LOOSE_STR, CONST_STRLEN(LOOSE_STR),
		GIT_OBJ_BLOB));
	cl_assert_equal_oid(&expected_id, &id);
	cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_FN, &after));

	cl_assert(before.st_atime < after.st_atime);
	cl_assert(before.st_mtime < after.st_mtime);
}
Exemple #2
0
int git_path_lstat(const char *path, struct stat *st)
{
	if (p_lstat(path, st) == 0)
		return 0;

	return git_path_set_error(errno, path, "stat");
}
Exemple #3
0
static void check_stat_data(git_index *index, const char *path, bool match)
{
	const git_index_entry *entry;
	struct stat st;

	cl_must_pass(p_lstat(path, &st));

	/* skip repo base dir name */
	while (*path != '/')
		++path;
	++path;

	entry = git_index_get_bypath(index, path, 0);
	cl_assert(entry);

	if (match) {
		cl_assert(st.st_ctime == entry->ctime.seconds);
		cl_assert(st.st_mtime == entry->mtime.seconds);
		cl_assert(st.st_size == entry->file_size);
		cl_assert(st.st_uid  == entry->uid);
		cl_assert(st.st_gid  == entry->gid);
		cl_assert_equal_i_fmt(
			GIT_MODE_TYPE(st.st_mode), GIT_MODE_TYPE(entry->mode), "%07o");
		if (cl_is_chmod_supported())
			cl_assert_equal_b(
				GIT_PERMS_IS_EXEC(st.st_mode), GIT_PERMS_IS_EXEC(entry->mode));
	} else {
		/* most things will still match */
		cl_assert(st.st_size != entry->file_size);
		/* would check mtime, but with second resolution it won't work :( */
	}
}
Exemple #4
0
bool git_path_islink(const char *path)
{
	struct stat st;

	assert(path);
	if (p_lstat(path, &st) < 0)
		return false;

	return S_ISLNK(st.st_mode) != 0;
}
Exemple #5
0
int git_path_lstat(const char *path, struct stat *st)
{
	int err = 0;

	if (p_lstat(path, st) < 0) {
		err = (errno == ENOENT) ? GIT_ENOTFOUND : -1;
		giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
	}

	return err;
}
Exemple #6
0
void test_odb_freshen__packed_object(void)
{
	git_oid expected_id, id;
	struct stat before, after;
	struct p_timeval old_times[2];

	cl_git_pass(git_oid_fromstr(&expected_id, PACKED_ID));

	old_times[0].tv_sec = 1234567890;
	old_times[0].tv_usec = 0;
	old_times[1].tv_sec = 1234567890;
	old_times[1].tv_usec = 0;

	/* set time to way back */
	cl_must_pass(p_utimes("testrepo.git/objects/pack/" PACKED_FN, old_times));
	cl_must_pass(p_lstat("testrepo.git/objects/pack/" PACKED_FN, &before));

	/* ensure that packfile is freshened */
	cl_git_pass(git_odb_write(&id, odb, PACKED_STR,
		CONST_STRLEN(PACKED_STR), GIT_OBJ_BLOB));
	cl_assert_equal_oid(&expected_id, &id);
	cl_must_pass(p_lstat("testrepo.git/objects/pack/" PACKED_FN, &after));

	cl_assert(before.st_atime < after.st_atime);
	cl_assert(before.st_mtime < after.st_mtime);

	memcpy(&before, &after, sizeof(struct stat));

	/* ensure that the pack file is not freshened again immediately */
	cl_git_pass(git_odb_write(&id, odb, PACKED_STR,
		CONST_STRLEN(PACKED_STR), GIT_OBJ_BLOB));
	cl_assert_equal_oid(&expected_id, &id);
	cl_must_pass(p_lstat("testrepo.git/objects/pack/" PACKED_FN, &after));

	cl_assert(before.st_atime == after.st_atime);
	cl_assert(before.st_atime_nsec == after.st_atime_nsec);
	cl_assert(before.st_mtime == after.st_mtime);
	cl_assert(before.st_mtime_nsec == after.st_mtime_nsec);
}
static bool are_symlinks_supported(const char *wd_path)
{
	git_buf path = GIT_BUF_INIT;
	int fd;
	struct stat st;
	int symlinks_supported = -1;

	if ((fd = git_futils_mktmp(&path, wd_path, 0666)) < 0 ||
		p_close(fd) < 0 ||
		p_unlink(path.ptr) < 0 ||
		p_symlink("testing", path.ptr) < 0 ||
		p_lstat(path.ptr, &st) < 0)
		symlinks_supported = false;
	else
		symlinks_supported = (S_ISLNK(st.st_mode) != 0);

	(void)p_unlink(path.ptr);
	git_buf_free(&path);

	return symlinks_supported;
}
Exemple #8
0
static int index_init_entry(git_index_entry *entry, git_index *index, const char *rel_path, int stage)
{
	char full_path[GIT_PATH_MAX];
	struct stat st;
	int error;

	if (index->repository == NULL)
		return git__throw(GIT_EBAREINDEX, "Failed to initialize entry. Repository is bare");

	git_path_join(full_path, index->repository->path_workdir, rel_path);

	if (p_lstat(full_path, &st) < 0)
		return git__throw(GIT_ENOTFOUND, "Failed to initialize entry. '%s' cannot be opened", full_path);

	if (stage < 0 || stage > 3)
		return git__throw(GIT_ERROR, "Failed to initialize entry. Invalid stage %i", stage);

	memset(entry, 0x0, sizeof(git_index_entry));

	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;

	/* write the blob to disk and get the oid */
	if ((error = git_blob_create_fromfile(&entry->oid, index->repository, rel_path)) < GIT_SUCCESS)
		return git__rethrow(error, "Failed to initialize index entry");

	entry->flags |= (stage << GIT_IDXENTRY_STAGESHIFT);
	entry->path = rel_path; /* do not duplicate; index_insert already does this */
	return GIT_SUCCESS;
}
Exemple #9
0
static void ensure_workdir_link(const char *path, const char *target)
{
#ifdef GIT_WIN32
	ensure_workdir_contents(path, target);
#else
	git_buf fullpath = GIT_BUF_INIT;
	char actual[1024];
	struct stat st;
	int len;

	cl_git_pass(
		git_buf_joinpath(&fullpath, git_repository_workdir(g_repo), path));

	cl_git_pass(p_lstat(git_buf_cstr(&fullpath), &st));
	cl_assert(S_ISLNK(st.st_mode));

	cl_assert((len = p_readlink(git_buf_cstr(&fullpath), actual, 1024)) > 0);
	actual[len] = '\0';
	cl_assert(strcmp(actual, target) == 0);

	git_buf_free(&fullpath);
#endif
}
Exemple #10
0
static int workdir_reader_read(
	git_buf *out,
	git_oid *out_id,
	git_filemode_t *out_filemode,
	git_reader *_reader,
	const char *filename)
{
	workdir_reader *reader = (workdir_reader *)_reader;
	git_buf path = GIT_BUF_INIT;
	struct stat st;
	git_filemode_t filemode;
	git_filter_list *filters = NULL;
	const git_index_entry *idx_entry;
	git_oid id;
	int error;

	if ((error = git_buf_joinpath(&path,
		git_repository_workdir(reader->repo), filename)) < 0)
		goto done;

	if ((error = p_lstat(path.ptr, &st)) < 0) {
		if (error == -1 && errno == ENOENT)
			error = GIT_ENOTFOUND;

		giterr_set(GITERR_OS, "could not stat '%s'", path.ptr);
		goto done;
	}

	filemode = git_futils_canonical_mode(st.st_mode);

	/*
	 * Patch application - for example - uses the filtered version of
	 * the working directory data to match git.  So we will run the
	 * workdir -> ODB filter on the contents in this workdir reader.
	 */
	if ((error = git_filter_list_load(&filters, reader->repo, NULL, filename,
		GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT)) < 0)
		goto done;

	if ((error = git_filter_list_apply_to_file(out,
	    filters, reader->repo, path.ptr)) < 0)
		goto done;

	if (out_id || reader->index) {
		if ((error = git_odb_hash(&id, out->ptr, out->size, GIT_OBJ_BLOB)) < 0)
			goto done;
	}

	if (reader->index) {
		if (!(idx_entry = git_index_get_bypath(reader->index, filename, 0)) ||
		    filemode != idx_entry->mode ||
		    !git_oid_equal(&id, &idx_entry->id)) {
			error = GIT_READER_MISMATCH;
			goto done;
		}
	}

	if (out_id)
		git_oid_cpy(out_id, &id);

	if (out_filemode)
		*out_filemode = filemode;

done:
	git_filter_list_free(filters);
	git_buf_dispose(&path);
	return error;
}
Exemple #11
0
int git_futils_mkdir_relative(
	const char *relative_path,
	const char *base,
	mode_t mode,
	uint32_t flags,
	struct git_futils_mkdir_options *opts)
{
	git_buf make_path = GIT_BUF_INIT;
	ssize_t root = 0, min_root_len;
	char lastch = '/', *tail;
	struct stat st;
	struct git_futils_mkdir_options empty_opts = {0};
	int error;

	if (!opts)
		opts = &empty_opts;

	/* build path and find "root" where we should start calling mkdir */
	if (git_path_join_unrooted(&make_path, relative_path, base, &root) < 0)
		return -1;

	if ((error = mkdir_canonicalize(&make_path, flags)) < 0 ||
		make_path.size == 0)
		goto done;

	/* if we are not supposed to make the whole path, reset root */
	if ((flags & GIT_MKDIR_PATH) == 0)
		root = git_buf_rfind(&make_path, '/');

	/* advance root past drive name or network mount prefix */
	min_root_len = git_path_root(make_path.ptr);
	if (root < min_root_len)
		root = min_root_len;
	while (root >= 0 && make_path.ptr[root] == '/')
		++root;

	/* clip root to make_path length */
	if (root > (ssize_t)make_path.size)
		root = (ssize_t)make_path.size; /* i.e. NUL byte of string */
	if (root < 0)
		root = 0;

	/* walk down tail of path making each directory */
	for (tail = &make_path.ptr[root]; *tail; *tail = lastch) {
		bool mkdir_attempted = false;

		/* advance tail to include next path component */
		while (*tail == '/')
			tail++;
		while (*tail && *tail != '/')
			tail++;

		/* truncate path at next component */
		lastch = *tail;
		*tail = '\0';
		st.st_mode = 0;

		if (opts->dir_map && git_strmap_exists(opts->dir_map, make_path.ptr))
			continue;

		/* See what's going on with this path component */
		opts->perfdata.stat_calls++;

retry_lstat:
		if (p_lstat(make_path.ptr, &st) < 0) {
			if (mkdir_attempted || errno != ENOENT) {
				giterr_set(GITERR_OS, "cannot access component in path '%s'", make_path.ptr);
				error = -1;
				goto done;
			}

			giterr_clear();
			opts->perfdata.mkdir_calls++;
			mkdir_attempted = true;
			if (p_mkdir(make_path.ptr, mode) < 0) {
				if (errno == EEXIST)
					goto retry_lstat;
				giterr_set(GITERR_OS, "failed to make directory '%s'", make_path.ptr);
				error = -1;
				goto done;
			}
		} else {
			if ((error = mkdir_validate_dir(
				make_path.ptr, &st, mode, flags, opts)) < 0)
				goto done;
		}

		/* chmod if requested and necessary */
		if ((error = mkdir_validate_mode(
			make_path.ptr, &st, (lastch == '\0'), mode, flags, opts)) < 0)
			goto done;

		if (opts->dir_map && opts->pool) {
			char *cache_path;
			size_t alloc_size;

			GITERR_CHECK_ALLOC_ADD(&alloc_size, make_path.size, 1);
			if (!git__is_uint32(alloc_size))
				return -1;
			cache_path = git_pool_malloc(opts->pool, (uint32_t)alloc_size);
			GITERR_CHECK_ALLOC(cache_path);

			memcpy(cache_path, make_path.ptr, make_path.size + 1);

			git_strmap_insert(opts->dir_map, cache_path, cache_path, &error);
			if (error < 0)
				goto done;
		}
	}

	error = 0;

	/* check that full path really is a directory if requested & needed */
	if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
		lastch != '\0') {
		opts->perfdata.stat_calls++;

		if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) {
			giterr_set(GITERR_OS, "path is not a directory '%s'",
				make_path.ptr);
			error = GIT_ENOTFOUND;
		}
	}

done:
	git_buf_free(&make_path);
	return error;
}
Exemple #12
0
int git_futils_mkdir(
	const char *path,
	mode_t mode,
	uint32_t flags)
{
	git_buf make_path = GIT_BUF_INIT, parent_path = GIT_BUF_INIT;
	const char *relative;
	struct git_futils_mkdir_options opts = { 0 };
	struct stat st;
	size_t depth = 0;
	int len = 0, root_len, error;

	if ((error = git_buf_puts(&make_path, path)) < 0 ||
		(error = mkdir_canonicalize(&make_path, flags)) < 0 ||
		(error = git_buf_puts(&parent_path, make_path.ptr)) < 0 ||
		make_path.size == 0)
		goto done;

	root_len = git_path_root(make_path.ptr);

	/* find the first parent directory that exists.  this will be used
	 * as the base to dirname_relative.
	 */
	for (relative = make_path.ptr; parent_path.size; ) {
		error = p_lstat(parent_path.ptr, &st);

		if (error == 0) {
			break;
		} else if (errno != ENOENT) {
			giterr_set(GITERR_OS, "failed to stat '%s'", parent_path.ptr);
			goto done;
		}

		depth++;

		/* examine the parent of the current path */
		if ((len = git_path_dirname_r(&parent_path, parent_path.ptr)) < 0) {
			error = len;
			goto done;
		}

		assert(len);

		/* we've walked all the given path's parents and it's either relative
		 * or rooted.  either way, give up and make the entire path.
		 */
		if ((len == 1 && parent_path.ptr[0] == '.') || len == root_len+1) {
			relative = make_path.ptr;
			break;
		}

		relative = make_path.ptr + len + 1;

		/* not recursive? just make this directory relative to its parent. */
		if ((flags & GIT_MKDIR_PATH) == 0)
			break;
	}

	/* we found an item at the location we're trying to create,
	 * validate it.
	 */
	if (depth == 0) {
		error = mkdir_validate_dir(make_path.ptr, &st, mode, flags, &opts);

		if (!error)
			error = mkdir_validate_mode(
				make_path.ptr, &st, true, mode, flags, &opts);

		goto done;
	}

	/* we already took `SKIP_LAST` and `SKIP_LAST2` into account when
	 * canonicalizing `make_path`.
	 */
	flags &= ~(GIT_MKDIR_SKIP_LAST2 | GIT_MKDIR_SKIP_LAST);

	error = git_futils_mkdir_relative(relative,
		parent_path.size ? parent_path.ptr : NULL, mode, flags, &opts);

done:
	git_buf_free(&make_path);
	git_buf_free(&parent_path);
	return error;
}
Exemple #13
0
int git_futils_mkdir_ext(
	const char *path,
	const char *base,
	mode_t mode,
	uint32_t flags,
	struct git_futils_mkdir_options *opts)
{
	int error = -1;
	git_buf make_path = GIT_BUF_INIT;
	ssize_t root = 0, min_root_len, root_len;
	char lastch = '/', *tail;
	struct stat st;

	/* build path and find "root" where we should start calling mkdir */
	if (git_path_join_unrooted(&make_path, path, base, &root) < 0)
		return -1;

	if (make_path.size == 0) {
		giterr_set(GITERR_OS, "Attempt to create empty path");
		goto done;
	}

	/* Trim trailing slashes (except the root) */
	if ((root_len = git_path_root(make_path.ptr)) < 0)
		root_len = 0;
	else
		root_len++;

	while (make_path.size > (size_t)root_len &&
		make_path.ptr[make_path.size - 1] == '/')
		make_path.ptr[--make_path.size] = '\0';

	/* if we are not supposed to made the last element, truncate it */
	if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) {
		git_path_dirname_r(&make_path, make_path.ptr);
		flags |= GIT_MKDIR_SKIP_LAST;
	}
	if ((flags & GIT_MKDIR_SKIP_LAST) != 0) {
		git_path_dirname_r(&make_path, make_path.ptr);
	}

	/* We were either given the root path (or trimmed it to
	 * the root), we don't have anything to do.
	 */
	if (make_path.size <= (size_t)root_len) {
		error = 0;
		goto done;
	}

	/* if we are not supposed to make the whole path, reset root */
	if ((flags & GIT_MKDIR_PATH) == 0)
		root = git_buf_rfind(&make_path, '/');

	/* advance root past drive name or network mount prefix */
	min_root_len = git_path_root(make_path.ptr);
	if (root < min_root_len)
		root = min_root_len;
	while (root >= 0 && make_path.ptr[root] == '/')
		++root;

	/* clip root to make_path length */
	if (root > (ssize_t)make_path.size)
		root = (ssize_t)make_path.size; /* i.e. NUL byte of string */
	if (root < 0)
		root = 0;

	/* walk down tail of path making each directory */
	for (tail = &make_path.ptr[root]; *tail; *tail = lastch) {

		/* advance tail to include next path component */
		while (*tail == '/')
			tail++;
		while (*tail && *tail != '/')
			tail++;

		/* truncate path at next component */
		lastch = *tail;
		*tail = '\0';
		st.st_mode = 0;

		if (opts->dir_map && git_strmap_exists(opts->dir_map, make_path.ptr))
			continue;

		/* See what's going on with this path component */
		opts->perfdata.stat_calls++;

		if (p_lstat(make_path.ptr, &st) < 0) {
			opts->perfdata.mkdir_calls++;

			if (errno != ENOENT || p_mkdir(make_path.ptr, mode) < 0) {
				giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr);
				error = GIT_EEXISTS;
				goto done;
			}

			giterr_clear();
		} else {
			/* with exclusive create, existing dir is an error */
			if ((flags & GIT_MKDIR_EXCL) != 0) {
				giterr_set(GITERR_FILESYSTEM, "Failed to make directory '%s': directory exists", make_path.ptr);
				error = GIT_EEXISTS;
				goto done;
			}

			if ((error = validate_existing(
				make_path.ptr, &st, mode, flags, &opts->perfdata)) < 0)
					goto done;
		}

		/* chmod if requested and necessary */
		if (((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
			 (lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) &&
			st.st_mode != mode) {

			opts->perfdata.chmod_calls++;

			if ((error = p_chmod(make_path.ptr, mode)) < 0 &&
				lastch == '\0') {
				giterr_set(GITERR_OS, "Failed to set permissions on '%s'",
					make_path.ptr);
				goto done;
			}
		}

		if (opts->dir_map && opts->pool) {
			char *cache_path;
			size_t alloc_size;

			GITERR_CHECK_ALLOC_ADD(&alloc_size, make_path.size, 1);
			if (!git__is_uint32(alloc_size))
				return -1;
			cache_path = git_pool_malloc(opts->pool, (uint32_t)alloc_size);
			GITERR_CHECK_ALLOC(cache_path);

			memcpy(cache_path, make_path.ptr, make_path.size + 1);

			git_strmap_insert(opts->dir_map, cache_path, cache_path, error);
			if (error < 0)
				goto done;
		}
	}

	error = 0;

	/* check that full path really is a directory if requested & needed */
	if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
		lastch != '\0') {
		opts->perfdata.stat_calls++;

		if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) {
			giterr_set(GITERR_OS, "Path is not a directory '%s'",
				make_path.ptr);
			error = GIT_ENOTFOUND;
		}
	}

done:
	git_buf_free(&make_path);
	return error;
}
Exemple #14
0
void test_core_stat__0(void)
{
	struct stat st;
	int err;

	cl_assert_equal_i(0, p_lstat("root", &st));
	cl_assert(S_ISDIR(st.st_mode));
	cl_assert_error(0);

	cl_assert_equal_i(0, p_lstat("root/", &st));
	cl_assert(S_ISDIR(st.st_mode));
	cl_assert_error(0);

	cl_assert_equal_i(0, p_lstat("root/file", &st));
	cl_assert(S_ISREG(st.st_mode));
	cl_assert_error(0);

	cl_assert_equal_i(0, p_lstat("root/d1", &st));
	cl_assert(S_ISDIR(st.st_mode));
	cl_assert_error(0);

	cl_assert_equal_i(0, p_lstat("root/d1/", &st));
	cl_assert(S_ISDIR(st.st_mode));
	cl_assert_error(0);

	cl_assert_equal_i(0, p_lstat("root/d1/file", &st));
	cl_assert(S_ISREG(st.st_mode));
	cl_assert_error(0);

	cl_assert(p_lstat("root/missing", &st) < 0);
	cl_assert_error(ENOENT);

	cl_assert(p_lstat("root/missing/but/could/be/created", &st) < 0);
	cl_assert_error(ENOENT);

	cl_assert(p_lstat_posixly("root/missing/but/could/be/created", &st) < 0);
	cl_assert_error(ENOENT);

	cl_assert(p_lstat("root/d1/missing", &st) < 0);
	cl_assert_error(ENOENT);

	cl_assert(p_lstat("root/d1/missing/deeper/path", &st) < 0);
	cl_assert_error(ENOENT);

	cl_assert(p_lstat_posixly("root/d1/missing/deeper/path", &st) < 0);
	cl_assert_error(ENOENT);

	cl_assert(p_lstat_posixly("root/d1/file/deeper/path", &st) < 0);
	cl_assert_error(ENOTDIR);

	cl_assert(p_lstat("root/file/invalid", &st) < 0);
#ifdef GIT_WIN32
	cl_assert_error(ENOENT);
#else
	cl_assert_error(ENOTDIR);
#endif

	cl_assert(p_lstat_posixly("root/file/invalid", &st) < 0);
	cl_assert_error(ENOTDIR);

	cl_assert(p_lstat("root/file/invalid/deeper_path", &st) < 0);
#ifdef GIT_WIN32
	cl_assert_error(ENOENT);
#else
	cl_assert_error(ENOTDIR);
#endif

	cl_assert(p_lstat_posixly("root/file/invalid/deeper_path", &st) < 0);
	cl_assert_error(ENOTDIR);

	cl_assert(p_lstat_posixly("root/d1/file/extra", &st) < 0);
	cl_assert_error(ENOTDIR);

	cl_assert(p_lstat_posixly("root/d1/file/further/invalid/items", &st) < 0);
	cl_assert_error(ENOTDIR);
}
Exemple #15
0
void test_object_commit_commitstagedfile__generate_predictable_object_ids(void)
{
	git_index *index;
	const git_index_entry *entry;
	git_oid expected_blob_oid, tree_oid, expected_tree_oid, commit_oid, expected_commit_oid;
	git_signature *signature;
	git_tree *tree;
	char buffer[128];

	/*
	 * The test below replicates the following git scenario
	 *
	 * $ echo "test" > test.txt
	 * $ git hash-object test.txt
	 * 9daeafb9864cf43055ae93beb0afd6c7d144bfa4
	 *
	 * $ git add .
	 * $ git commit -m "Initial commit"
	 *
	 * $ git log
	 * commit 1fe3126578fc4eca68c193e4a3a0a14a0704624d
	 * Author: nulltoken <*****@*****.**>
	 * Date:   Wed Dec 14 08:29:03 2011 +0100
	 *
	 *     Initial commit
	 *
	 * $ git show 1fe3 --format=raw
	 * commit 1fe3126578fc4eca68c193e4a3a0a14a0704624d
	 * tree 2b297e643c551e76cfa1f93810c50811382f9117
	 * author nulltoken <*****@*****.**> 1323847743 +0100
	 * committer nulltoken <*****@*****.**> 1323847743 +0100
	 * 
	 *     Initial commit
	 * 
	 * diff --git a/test.txt b/test.txt
	 * new file mode 100644
	 * index 0000000..9daeafb
	 * --- /dev/null
	 * +++ b/test.txt
	 * @@ -0,0 +1 @@
	 * +test
	 *
	 * $ git ls-tree 2b297
	 * 100644 blob 9daeafb9864cf43055ae93beb0afd6c7d144bfa4    test.txt
	 */

	cl_git_pass(git_oid_fromstr(&expected_commit_oid, "1fe3126578fc4eca68c193e4a3a0a14a0704624d"));
	cl_git_pass(git_oid_fromstr(&expected_tree_oid, "2b297e643c551e76cfa1f93810c50811382f9117"));
	cl_git_pass(git_oid_fromstr(&expected_blob_oid, "9daeafb9864cf43055ae93beb0afd6c7d144bfa4"));

	/*
	 * Add a new file to the index
	 */
	cl_git_mkfile("treebuilder/test.txt", "test\n");
	cl_git_pass(git_repository_index(&index, repo));
	cl_git_pass(git_index_add_bypath(index, "test.txt"));

	entry = git_index_get_byindex(index, 0);

	cl_assert(git_oid_cmp(&expected_blob_oid, &entry->oid) == 0);

	/*
	 * Information about index entry should match test file
	 */
	{
		struct stat st;
		cl_must_pass(p_lstat("treebuilder/test.txt", &st));
		cl_assert(entry->file_size == st.st_size);
#ifndef _WIN32
		/*
		 * Windows doesn't populate these fields, and the signage is
		 * wrong in the Windows version of the struct, so lets avoid
		 * the "comparing signed and unsigned" compilation warning in
		 * that case.
		 */
		cl_assert(entry->uid == st.st_uid);
		cl_assert(entry->gid == st.st_gid);
#endif
	}

	/*
	 * Build the tree from the index
	 */
	cl_git_pass(git_index_write_tree(&tree_oid, index));

	cl_assert(git_oid_cmp(&expected_tree_oid, &tree_oid) == 0);

	/*
	 * Commit the staged file
	 */
	cl_git_pass(git_signature_new(&signature, "nulltoken", "*****@*****.**", 1323847743, 60));
	cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));

	cl_assert_equal_i(16, git_message_prettify(buffer, 128, "Initial commit", 0));

	cl_git_pass(git_commit_create_v(
		&commit_oid,
		repo,
		"HEAD",
		signature,
		signature,
		NULL,
		buffer,
		tree,
		0));

	cl_assert(git_oid_cmp(&expected_commit_oid, &commit_oid) == 0);

	git_signature_free(signature);
	git_tree_free(tree);
	git_index_free(index);
}