Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/libgit2.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/status.c')
-rw-r--r--src/status.c218
1 files changed, 146 insertions, 72 deletions
diff --git a/src/status.c b/src/status.c
index e9ad3cfe4..ac6b4379b 100644
--- a/src/status.c
+++ b/src/status.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -17,6 +17,7 @@
#include "git2/diff.h"
#include "diff.h"
+#include "diff_output.h"
static unsigned int index_delta2status(git_delta_t index_status)
{
@@ -25,7 +26,6 @@ static unsigned int index_delta2status(git_delta_t index_status)
switch (index_status) {
case GIT_DELTA_ADDED:
case GIT_DELTA_COPIED:
- case GIT_DELTA_RENAMED:
st = GIT_STATUS_INDEX_NEW;
break;
case GIT_DELTA_DELETED:
@@ -34,6 +34,12 @@ static unsigned int index_delta2status(git_delta_t index_status)
case GIT_DELTA_MODIFIED:
st = GIT_STATUS_INDEX_MODIFIED;
break;
+ case GIT_DELTA_RENAMED:
+ st = GIT_STATUS_INDEX_RENAMED;
+ break;
+ case GIT_DELTA_TYPECHANGE:
+ st = GIT_STATUS_INDEX_TYPECHANGE;
+ break;
default:
break;
}
@@ -47,8 +53,8 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
switch (workdir_status) {
case GIT_DELTA_ADDED:
- case GIT_DELTA_COPIED:
case GIT_DELTA_RENAMED:
+ case GIT_DELTA_COPIED:
case GIT_DELTA_UNTRACKED:
st = GIT_STATUS_WT_NEW;
break;
@@ -61,6 +67,9 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
case GIT_DELTA_IGNORED:
st = GIT_STATUS_IGNORED;
break;
+ case GIT_DELTA_TYPECHANGE:
+ st = GIT_STATUS_WT_TYPECHANGE;
+ break;
default:
break;
}
@@ -68,29 +77,76 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
return st;
}
+typedef struct {
+ git_status_cb cb;
+ void *payload;
+ const git_status_options *opts;
+} status_user_callback;
+
+static int status_invoke_cb(
+ git_diff_delta *h2i, git_diff_delta *i2w, void *payload)
+{
+ status_user_callback *usercb = payload;
+ const char *path = NULL;
+ unsigned int status = 0;
+
+ if (i2w) {
+ path = i2w->old_file.path;
+ status |= workdir_delta2status(i2w->status);
+ }
+ if (h2i) {
+ path = h2i->old_file.path;
+ status |= index_delta2status(h2i->status);
+ }
+
+ /* if excluding submodules and this is a submodule everywhere */
+ if (usercb->opts &&
+ (usercb->opts->flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0)
+ {
+ bool in_tree = (h2i && h2i->status != GIT_DELTA_ADDED);
+ bool in_index = (h2i && h2i->status != GIT_DELTA_DELETED);
+ bool in_wd = (i2w && i2w->status != GIT_DELTA_DELETED);
+
+ if ((!in_tree || h2i->old_file.mode == GIT_FILEMODE_COMMIT) &&
+ (!in_index || h2i->new_file.mode == GIT_FILEMODE_COMMIT) &&
+ (!in_wd || i2w->new_file.mode == GIT_FILEMODE_COMMIT))
+ return 0;
+ }
+
+ return usercb->cb(path, status, usercb->payload);
+}
+
int git_status_foreach_ext(
git_repository *repo,
const git_status_options *opts,
- int (*cb)(const char *, unsigned int, void *),
- void *cbdata)
+ git_status_cb cb,
+ void *payload)
{
- int err = 0, cmp;
- git_diff_options diffopt;
- git_diff_list *idx2head = NULL, *wd2idx = NULL;
+ int err = 0;
+ git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT;
+ git_diff_list *head2idx = NULL, *idx2wd = NULL;
git_tree *head = NULL;
git_status_show_t show =
opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
- git_diff_delta *i2h, *w2i;
- unsigned int i, j, i_max, j_max;
+ status_user_callback usercb;
assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
- if ((err = git_repository_head_tree(&head, repo)) < 0)
+ GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options");
+
+ if (show != GIT_STATUS_SHOW_INDEX_ONLY &&
+ (err = git_repository__ensure_not_bare(repo, "status")) < 0)
return err;
- memset(&diffopt, 0, sizeof(diffopt));
+ /* if there is no HEAD, that's okay - we'll make an empty iterator */
+ if (((err = git_repository_head_tree(&head, repo)) < 0) &&
+ !(err == GIT_ENOTFOUND || err == GIT_EORPHANEDHEAD))
+ return err;
+
memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
+ diffopt.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
+
if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
if ((opts->flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0)
@@ -99,62 +155,58 @@ int git_status_foreach_ext(
diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED;
if ((opts->flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
- /* TODO: support EXCLUDE_SUBMODULES flag */
+ if ((opts->flags & GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_DISABLE_PATHSPEC_MATCH;
+ if ((opts->flags & GIT_STATUS_OPT_RECURSE_IGNORED_DIRS) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_IGNORED_DIRS;
+ if ((opts->flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES;
+
+ if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) {
+ err = git_diff_tree_to_index(&head2idx, repo, head, NULL, &diffopt);
+ if (err < 0)
+ goto cleanup;
+ }
- if (show != GIT_STATUS_SHOW_WORKDIR_ONLY &&
- (err = git_diff_index_to_tree(repo, &diffopt, head, &idx2head)) < 0)
- goto cleanup;
+ if (show != GIT_STATUS_SHOW_INDEX_ONLY) {
+ err = git_diff_index_to_workdir(&idx2wd, repo, NULL, &diffopt);
+ if (err < 0)
+ goto cleanup;
+ }
- if (show != GIT_STATUS_SHOW_INDEX_ONLY &&
- (err = git_diff_workdir_to_index(repo, &diffopt, &wd2idx)) < 0)
- goto cleanup;
+ usercb.cb = cb;
+ usercb.payload = payload;
+ usercb.opts = opts;
if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {
- for (i = 0; !err && i < idx2head->deltas.length; i++) {
- i2h = GIT_VECTOR_GET(&idx2head->deltas, i);
- err = cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata);
- }
- git_diff_list_free(idx2head);
- idx2head = NULL;
- }
+ if ((err = git_diff__paired_foreach(
+ head2idx, NULL, status_invoke_cb, &usercb)) < 0)
+ goto cleanup;
- i_max = idx2head ? idx2head->deltas.length : 0;
- j_max = wd2idx ? wd2idx->deltas.length : 0;
-
- for (i = 0, j = 0; !err && (i < i_max || j < j_max); ) {
- i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
- w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
-
- cmp = !w2i ? -1 : !i2h ? 1 : strcmp(i2h->old_file.path, w2i->old_file.path);
-
- if (cmp < 0) {
- err = cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata);
- i++;
- } else if (cmp > 0) {
- err = cb(w2i->old_file.path, workdir_delta2status(w2i->status), cbdata);
- j++;
- } else {
- err = cb(i2h->old_file.path, index_delta2status(i2h->status) |
- workdir_delta2status(w2i->status), cbdata);
- i++; j++;
- }
+ git_diff_list_free(head2idx);
+ head2idx = NULL;
}
+ err = git_diff__paired_foreach(head2idx, idx2wd, status_invoke_cb, &usercb);
+
cleanup:
git_tree_free(head);
- git_diff_list_free(idx2head);
- git_diff_list_free(wd2idx);
+ git_diff_list_free(head2idx);
+ git_diff_list_free(idx2wd);
+
+ if (err == GIT_EUSER)
+ giterr_clear();
+
return err;
}
int git_status_foreach(
git_repository *repo,
- int (*callback)(const char *, unsigned int, void *),
+ git_status_cb callback,
void *payload)
{
- git_status_options opts;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- memset(&opts, 0, sizeof(opts));
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
GIT_STATUS_OPT_INCLUDE_UNTRACKED |
@@ -164,22 +216,29 @@ int git_status_foreach(
}
struct status_file_info {
+ char *expected;
unsigned int count;
unsigned int status;
- char *expected;
+ int fnm_flags;
+ int ambiguous;
};
static int get_one_status(const char *path, unsigned int status, void *data)
{
struct status_file_info *sfi = data;
+ int (*strcomp)(const char *a, const char *b);
sfi->count++;
sfi->status = status;
- if (sfi->count > 1 || strcmp(sfi->expected, path) != 0) {
- giterr_set(GITERR_INVALID,
- "Ambiguous path '%s' given to git_status_file", sfi->expected);
- return -1;
+ strcomp = (sfi->fnm_flags & FNM_CASEFOLD) ? git__strcasecmp : git__strcmp;
+
+ if (sfi->count > 1 ||
+ (strcomp(sfi->expected, path) != 0 &&
+ p_fnmatch(sfi->expected, path, sfi->fnm_flags) != 0))
+ {
+ sfi->ambiguous = true;
+ return GIT_EAMBIGUOUS;
}
return 0;
@@ -191,16 +250,20 @@ int git_status_file(
const char *path)
{
int error;
- git_status_options opts;
- struct status_file_info sfi;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_file_info sfi = {0};
+ git_index *index;
assert(status_flags && repo && path);
- memset(&sfi, 0, sizeof(sfi));
+ if ((error = git_repository_index__weakptr(&index, repo)) < 0)
+ return error;
+
if ((sfi.expected = git__strdup(path)) == NULL)
return -1;
+ if (index->ignore_case)
+ sfi.fnm_flags = FNM_CASEFOLD;
- memset(&opts, 0, sizeof(opts));
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
GIT_STATUS_OPT_INCLUDE_UNTRACKED |
@@ -211,10 +274,29 @@ int git_status_file(
error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi);
- if (!error && !sfi.count) {
+ if (error < 0 && sfi.ambiguous) {
giterr_set(GITERR_INVALID,
- "Attempt to get status of nonexistent file '%s'", path);
- error = GIT_ENOTFOUND;
+ "Ambiguous path '%s' given to git_status_file", sfi.expected);
+ error = GIT_EAMBIGUOUS;
+ }
+
+ if (!error && !sfi.count) {
+ git_buf full = GIT_BUF_INIT;
+
+ /* if the file actually exists and we still did not get a callback
+ * for it, then it must be contained inside an ignored directory, so
+ * mark it as such instead of generating an error.
+ */
+ if (!git_buf_joinpath(&full, git_repository_workdir(repo), path) &&
+ git_path_exists(full.ptr))
+ sfi.status = GIT_STATUS_IGNORED;
+ else {
+ giterr_set(GITERR_INVALID,
+ "Attempt to get status of nonexistent file '%s'", path);
+ error = GIT_ENOTFOUND;
+ }
+
+ git_buf_free(&full);
}
*status_flags = sfi.status;
@@ -229,14 +311,6 @@ int git_status_should_ignore(
git_repository *repo,
const char *path)
{
- int error;
- git_ignores ignores;
-
- if (git_ignore__for_path(repo, path, &ignores) < 0)
- return -1;
-
- error = git_ignore__lookup(&ignores, path, ignored);
- git_ignore__free(&ignores);
- return error;
+ return git_ignore_path_is_ignored(ignored, repo, path);
}