diff options
| author | nulltoken <emeric.fermas@gmail.com> | 2011-09-02 13:44:42 +0200 |
|---|---|---|
| committer | Vicent Marti <tanoku@gmail.com> | 2011-09-15 01:14:36 +0200 |
| commit | 56453d346864e312ec138626a3fc920c39890f0d (patch) | |
| tree | 3653456e1c93d7e41e7688a306d3f519167e180b /src/status.c | |
| parent | 3601c4bfce3df04ebfc8668e5db531ded39280a9 (diff) | |
| download | libgit2-56453d346864e312ec138626a3fc920c39890f0d.tar.gz | |
status: enhance determination of status for a single file
- fix retrieval of a file status when working against a newly initialized repository
- reduce memory pressure
- prevents a directory from being tested
Diffstat (limited to 'src/status.c')
| -rw-r--r-- | src/status.c | 203 |
1 files changed, 121 insertions, 82 deletions
diff --git a/src/status.c b/src/status.c index d9613c129..72792a635 100644 --- a/src/status.c +++ b/src/status.c @@ -30,10 +30,9 @@ #include "vector.h" #include "tree.h" #include "git2/status.h" +#include "repository.h" struct status_entry { - char path[GIT_PATH_MAX]; - git_index_time mtime; git_oid head_oid; @@ -41,6 +40,8 @@ struct status_entry { git_oid wt_oid; unsigned int status_flags:6; + + char path[GIT_FLEX_ARRAY]; /* more */ }; static int status_cmp(const void *a, const void *b) @@ -66,11 +67,17 @@ static int find_status_entry(git_vector *entries, const char *path) static struct status_entry *new_status_entry(git_vector *entries, const char *path) { - struct status_entry *e = git__malloc(sizeof(struct status_entry)); - memset(e, 0x0, sizeof(struct status_entry)); + struct status_entry *e = git__malloc(sizeof(*e) + strlen(path) + 1); + if (e == NULL) + return NULL; + + memset(e, 0x0, sizeof(*e)); + if (entries != NULL) git_vector_insert(entries, e); + strcpy(e->path, path); + return e; } @@ -102,33 +109,39 @@ static void recurse_tree_entries(git_tree *tree, git_vector *entries, char *path } } -static void recurse_tree_entry(git_tree *tree, struct status_entry *e, const char *path) +static int recurse_tree_entry(git_tree *tree, struct status_entry *e, const char *path) { char *dir_sep; - char buffer[GIT_PATH_MAX]; const git_tree_entry *tree_entry; git_tree *subtree; + int error = GIT_SUCCESS; - strcpy(buffer, path); + dir_sep = strchr(path, '/'); + if (!dir_sep) { + tree_entry = git_tree_entry_byname(tree, path); + if (tree_entry == NULL) + return GIT_SUCCESS; /* The leaf doesn't exist in the tree*/ - dir_sep = strchr(buffer, '/'); - if (dir_sep) { - *dir_sep = '\0'; - - tree_entry = git_tree_entry_byname(tree, buffer); - if (tree_entry != NULL) { - if (git_tree_lookup(&subtree, tree->object.repo, &tree_entry->oid) == GIT_SUCCESS) { - recurse_tree_entry(subtree, e, dir_sep+1); - git_tree_close(subtree); - return; - } - } + git_oid_cpy(&e->head_oid, &tree_entry->oid); + return GIT_SUCCESS; } + /* Retrieve subtree name */ + *dir_sep = '\0'; + tree_entry = git_tree_entry_byname(tree, path); - if (tree_entry != NULL) { - git_oid_cpy(&e->head_oid, &tree_entry->oid); - } + if (tree_entry == NULL) + return GIT_SUCCESS; /* The subtree doesn't exist in the tree*/ + + *dir_sep = '/'; + + /* Retreive subtree */ + if ((error = git_tree_lookup(&subtree, tree->object.repo, &tree_entry->oid)) < GIT_SUCCESS) + return git__throw(GIT_EOBJCORRUPTED, "Can't find tree object '%s'", &tree_entry->filename); + + error = recurse_tree_entry(subtree, e, dir_sep+1); + git_tree_close(subtree); + return error; } struct status_st { @@ -149,7 +162,7 @@ static int dirent_cb(void *state, char *full_path) struct stat filest; git_oid oid; - if ((git_futils_isdir(full_path) == GIT_SUCCESS) && (!strcmp(".git", file_path))) + if ((git_futils_isdir(full_path) == GIT_SUCCESS) && (!strcmp(DOT_GIT, file_path))) return 0; if (git_futils_isdir(full_path) == GIT_SUCCESS) @@ -175,38 +188,6 @@ static int dirent_cb(void *state, char *full_path) return 0; } -static int single_dirent_cb(void *state, char *full_path) -{ - struct status_st *st = (struct status_st *)state; - struct status_entry *e = st->entry.e; - - char *file_path = full_path + st->workdir_path_len; - struct stat filest; - git_oid oid; - - if ((git_futils_isdir(full_path) == GIT_SUCCESS) && (!strcmp(".git", file_path))) - return 0; - - if (git_futils_isdir(full_path) == GIT_SUCCESS) - return git_futils_direach(full_path, GIT_PATH_MAX, single_dirent_cb, state); - - if (!strcmp(file_path, e->path)) { - if (p_stat(full_path, &filest) < 0) - return git__throw(GIT_EOSERR, "Failed to read file %s", full_path); - - if (e->mtime.seconds == (git_time_t)filest.st_mtime) { - git_oid_cpy(&e->wt_oid, &e->index_oid); - return 1; - } - - git_odb_hashfile(&oid, full_path, GIT_OBJ_BLOB); - git_oid_cpy(&e->wt_oid, &oid); - return 1; - } - - return 0; -} - static int set_status_flags(struct status_entry *e) { git_oid zero; @@ -305,24 +286,81 @@ int git_status_foreach(git_repository *repo, int (*callback)(const char *, unsig return GIT_SUCCESS; } +static int retrieve_head_tree(git_tree **tree_out, git_repository *repo) +{ + git_reference *resolved_head_ref; + git_commit *head_commit = NULL; + git_tree *tree; + int error = GIT_SUCCESS; + + *tree_out = NULL; + + error = git_repository_head(&resolved_head_ref, repo); + if (error != GIT_SUCCESS && error != GIT_ENOTFOUND) + return git__rethrow(error, "HEAD can't be resolved"); + + /* + * We assume that a situation where HEAD exists but can not be resolved is valid. + * A new repository fits this description for instance. + */ + + if (error == GIT_ENOTFOUND) + return GIT_SUCCESS; + + if ((error = git_commit_lookup(&head_commit, repo, git_reference_oid(resolved_head_ref))) < GIT_SUCCESS) + return git__rethrow(error, "The tip of HEAD can't be retrieved"); + + if ((error = git_commit_tree(&tree, head_commit)) < GIT_SUCCESS) { + error = git__rethrow(error, "The tree of HEAD can't be retrieved"); + goto exit; + } + + *tree_out = tree; + +exit: + git_commit_close(head_commit); + return error; +} + int git_status_file(unsigned int *status_flags, git_repository *repo, const char *path) { struct status_entry *e; - git_index *index; + git_index *index = NULL; git_index_entry *index_entry; char temp_path[GIT_PATH_MAX]; - int idx, error; - git_tree *tree; - git_reference *head_ref, *resolved_head_ref; - git_commit *head_commit; - struct status_st dirent_st; + int idx, error = GIT_SUCCESS; + git_tree *tree = NULL; + struct stat filest; - assert(status_flags); + assert(status_flags && repo && path); + + git_path_join(temp_path, repo->path_workdir, path); + if (git_futils_isdir(temp_path) == GIT_SUCCESS) + return git__throw(GIT_EINVALIDPATH, "Failed to determine status of file '%s'. Provided path leads to a folder, not a file", path); e = new_status_entry(NULL, path); + if (e == NULL) + return GIT_ENOMEM; + + /* Find file in Workdir */ + if (git_futils_exists(temp_path) == GIT_SUCCESS) { + if (p_stat(temp_path, &filest) < GIT_SUCCESS) { + error = git__throw(GIT_EOSERR, "Failed to determine status of file '%s'. Can't read file", temp_path); + goto exit; + } + + if (e->mtime.seconds == (git_time_t)filest.st_mtime) + git_oid_cpy(&e->wt_oid, &e->index_oid); + else + git_odb_hashfile(&e->wt_oid, temp_path, GIT_OBJ_BLOB); + } + + /* Find file in Index */ + if ((error = git_repository_index(&index, repo)) < GIT_SUCCESS) { + error = git__rethrow(error, "Failed to determine status of file '%s'. Index can't be opened", path); + goto exit; + } - // Find file in Index - git_repository_index(&index, repo); idx = git_index_find(index, path); if (idx >= 0) { index_entry = git_index_get(index, idx); @@ -331,31 +369,32 @@ int git_status_file(unsigned int *status_flags, git_repository *repo, const char } git_index_free(index); - // Find file in HEAD - git_reference_lookup(&head_ref, repo, GIT_HEAD_FILE); - git_reference_resolve(&resolved_head_ref, head_ref); - - git_commit_lookup(&head_commit, repo, git_reference_oid(resolved_head_ref)); + if ((error = retrieve_head_tree(&tree, repo)) < GIT_SUCCESS) { + error = git__rethrow(error, "Failed to determine status of file '%s'", path); + goto exit; + } - git_commit_tree(&tree, head_commit); - recurse_tree_entry(tree, e, path); - git_tree_close(tree); - git_commit_close(head_commit); + /* If the repository is not empty, try and locate the file in HEAD */ + if (tree != NULL) { + strcpy(temp_path, path); - // Find file in Workdir - dirent_st.workdir_path_len = strlen(repo->path_workdir); - dirent_st.entry.e = e; - strcpy(temp_path, repo->path_workdir); - git_futils_direach(temp_path, GIT_PATH_MAX, single_dirent_cb, &dirent_st); + error = recurse_tree_entry(tree, e, temp_path); + if (error < GIT_SUCCESS) { + error = git__rethrow(error, "Failed to determine status of file '%s'. An error occured while processing the tree", path); + goto exit; + } + } + /* Determine status */ if ((error = set_status_flags(e)) < GIT_SUCCESS) { - free(e); - return git__throw(error, "Nonexistent file"); + error = git__throw(error, "Nonexistent file"); + goto exit; } *status_flags = e->status_flags; +exit: + git_tree_close(tree); free(e); - - return GIT_SUCCESS; + return error; } |
