summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/git2/status.h63
-rw-r--r--include/git2/types.h3
-rw-r--r--src/status.c232
-rw-r--r--src/status.h23
4 files changed, 255 insertions, 66 deletions
diff --git a/include/git2/status.h b/include/git2/status.h
index 38b6fa5bd..ce1d44a06 100644
--- a/include/git2/status.h
+++ b/include/git2/status.h
@@ -174,10 +174,73 @@ typedef struct {
git_strarray pathspec;
} git_status_options;
+/**
+ * A status entry, providing the differences between the file as it exists
+ * in HEAD and the index, and providing the differences between the index
+ * and the working directory.
+ *
+ * The `status` value provides the status flags for this file.
+ *
+ * The `head_to_index` value provides detailed information about the
+ * differences between the file in HEAD and the file in the index.
+ *
+ * The `index_to_workdir` value provides detailed information about the
+ * differences between the file in the index and the file in the
+ * working directory.
+ */
+typedef struct {
+ git_status_t status;
+ git_diff_delta *head_to_index;
+ git_diff_delta *index_to_workdir;
+} git_status_entry;
+
#define GIT_STATUS_OPTIONS_VERSION 1
#define GIT_STATUS_OPTIONS_INIT {GIT_STATUS_OPTIONS_VERSION}
/**
+ * Gather file status information and populate the `git_status_list`.
+ *
+ * @param out Pointer to store the status results in
+ * @param repo Repository object
+ * @param opts Status options structure
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_status_list_new(
+ git_status_list **out,
+ git_repository *repo,
+ const git_status_options *opts);
+
+/**
+ * Gets the count of status entries in this list.
+ *
+ * @param statuslist Existing status list object
+ * @return the number of status entries
+ */
+GIT_EXTERN(size_t) git_status_list_entrycount(
+ git_status_list *statuslist);
+
+/**
+ * Get a pointer to one of the entries in the status list.
+ *
+ * The entry is not modifiable and should not be freed.
+ *
+ * @param statuslist Existing status list object
+ * @param idx Position of the entry
+ * @return Pointer to the entry; NULL if out of bounds
+ */
+GIT_EXTERN(const git_status_entry *) git_status_byindex(
+ git_status_list *statuslist,
+ size_t idx);
+
+/**
+ * Free an existing status list
+ *
+ * @param statuslist Existing status list object
+ */
+GIT_EXTERN(void) git_status_list_free(
+ git_status_list *statuslist);
+
+/**
* Gather file status information and run callbacks as requested.
*
* This is an extended version of the `git_status_foreach()` API that
diff --git a/include/git2/types.h b/include/git2/types.h
index 1bfa73be6..dc344075c 100644
--- a/include/git2/types.h
+++ b/include/git2/types.h
@@ -174,6 +174,9 @@ typedef struct git_reference_iterator git_reference_iterator;
/** Merge heads, the input to merge */
typedef struct git_merge_head git_merge_head;
+/** Representation of a status collection */
+typedef struct git_status_list git_status_list;
+
/** Basic type of any Git reference. */
typedef enum {
diff --git a/src/status.c b/src/status.c
index 712e0d515..33e67efac 100644
--- a/src/status.c
+++ b/src/status.c
@@ -11,6 +11,7 @@
#include "hash.h"
#include "vector.h"
#include "tree.h"
+#include "status.h"
#include "git2/status.h"
#include "repository.h"
#include "ignore.h"
@@ -77,71 +78,105 @@ 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 bool status_is_included(
+ git_status_list *statuslist,
+ git_diff_delta *head2idx,
+ git_diff_delta *idx2wd)
+{
+ /* if excluding submodules and this is a submodule everywhere */
+ if ((statuslist->opts.flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0) {
+ bool in_tree = (head2idx && head2idx->status != GIT_DELTA_ADDED);
+ bool in_index = (head2idx && head2idx->status != GIT_DELTA_DELETED);
+ bool in_wd = (idx2wd && idx2wd->status != GIT_DELTA_DELETED);
+
+ if ((!in_tree || head2idx->old_file.mode == GIT_FILEMODE_COMMIT) &&
+ (!in_index || head2idx->new_file.mode == GIT_FILEMODE_COMMIT) &&
+ (!in_wd || idx2wd->new_file.mode == GIT_FILEMODE_COMMIT))
+ return 0;
+ }
-static int status_invoke_cb(
- git_diff_delta *h2i, git_diff_delta *i2w, void *payload)
+ return 1;
+}
+
+static git_status_t status_compute(
+ git_diff_delta *head2idx,
+ git_diff_delta *idx2wd)
{
- status_user_callback *usercb = payload;
- const char *path = NULL;
- unsigned int status = 0;
+ git_status_t 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 (head2idx)
+ status |= index_delta2status(head2idx->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 (idx2wd)
+ status |= workdir_delta2status(idx2wd->status);
- 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 status;
+}
+
+static int status_collect(
+ git_diff_delta *head2idx,
+ git_diff_delta *idx2wd,
+ void *payload)
+{
+ git_status_list *statuslist = payload;
+ git_status_entry *status_entry;
+
+ if (!status_is_included(statuslist, head2idx, idx2wd))
+ return 0;
+
+ status_entry = git__malloc(sizeof(git_status_entry));
+ GITERR_CHECK_ALLOC(status_entry);
+
+ status_entry->status = status_compute(head2idx, idx2wd);
+ status_entry->head_to_index = head2idx;
+ status_entry->index_to_workdir = idx2wd;
+
+ git_vector_insert(&statuslist->paired, status_entry);
- return usercb->cb(path, status, usercb->payload);
+ return 0;
}
-int git_status_foreach_ext(
+git_status_list *git_status_list_alloc(void)
+{
+ git_status_list *statuslist = NULL;
+
+ if ((statuslist = git__calloc(1, sizeof(git_status_list))) == NULL ||
+ git_vector_init(&statuslist->paired, 0, NULL) < 0)
+ return NULL;
+
+ return statuslist;
+}
+
+int git_status_list_new(
+ git_status_list **out,
git_repository *repo,
- const git_status_options *opts,
- git_status_cb cb,
- void *payload)
+ const git_status_options *opts)
{
- int err = 0;
+ git_status_list *statuslist = NULL;
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;
- status_user_callback usercb;
+ int error = 0;
assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
+ *out = NULL;
+
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;
+ if ((error = git_repository__ensure_not_bare(repo, "status")) < 0)
+ return error;
/* 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;
+ if (((error = git_repository_head_tree(&head, repo)) < 0) &&
+ !(error == GIT_ENOTFOUND || error == GIT_EORPHANEDHEAD))
+ return error;
+
+ statuslist = git_status_list_alloc();
+ GITERR_CHECK_ALLOC(statuslist);
+
+ memcpy(&statuslist->opts, opts, sizeof(git_status_options));
memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
@@ -163,41 +198,106 @@ int git_status_foreach_ext(
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;
+ error = git_diff_tree_to_index(&statuslist->head2idx, repo, head, NULL, &diffopt);
+
+ if (error < 0)
+ goto on_error;
}
if (show != GIT_STATUS_SHOW_INDEX_ONLY) {
- err = git_diff_index_to_workdir(&idx2wd, repo, NULL, &diffopt);
- if (err < 0)
- goto cleanup;
- }
+ error = git_diff_index_to_workdir(&statuslist->idx2wd, repo, NULL, &diffopt);
- usercb.cb = cb;
- usercb.payload = payload;
- usercb.opts = opts;
+ if (error < 0)
+ goto on_error;
+ }
if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {
- if ((err = git_diff__paired_foreach(
- head2idx, NULL, status_invoke_cb, &usercb)) < 0)
- goto cleanup;
+ if ((error = git_diff__paired_foreach(statuslist->head2idx, NULL, status_collect, statuslist)) < 0)
+ goto on_error;
- git_diff_list_free(head2idx);
- head2idx = NULL;
+ git_diff_list_free(statuslist->head2idx);
+ statuslist->head2idx = NULL;
}
- err = git_diff__paired_foreach(head2idx, idx2wd, status_invoke_cb, &usercb);
+ if ((error = git_diff__paired_foreach(statuslist->head2idx, statuslist->idx2wd, status_collect, statuslist)) < 0)
+ goto on_error;
+
+ *out = statuslist;
+ goto done;
+
+on_error:
+ git_status_list_free(statuslist);
-cleanup:
+done:
git_tree_free(head);
- git_diff_list_free(head2idx);
- git_diff_list_free(idx2wd);
- if (err == GIT_EUSER)
- giterr_clear();
+ return error;
+}
+
+size_t git_status_list_entrycount(git_status_list *statuslist)
+{
+ assert(statuslist);
+
+ return statuslist->paired.length;
+}
+
+const git_status_entry *git_status_byindex(
+ git_status_list *statuslist,
+ size_t i)
+{
+ assert(statuslist);
+
+ return git_vector_get(&statuslist->paired, i);
+}
+
+void git_status_list_free(git_status_list *statuslist)
+{
+ git_status_entry *status_entry;
+ size_t i;
+
+ if (statuslist == NULL)
+ return;
+
+ git_diff_list_free(statuslist->head2idx);
+ git_diff_list_free(statuslist->idx2wd);
+
+ git_vector_foreach(&statuslist->paired, i, status_entry)
+ git__free(status_entry);
- return err;
+ git_vector_free(&statuslist->paired);
+
+ git__free(statuslist);
+}
+
+int git_status_foreach_ext(
+ git_repository *repo,
+ const git_status_options *opts,
+ git_status_cb cb,
+ void *payload)
+{
+ git_status_list *statuslist;
+ const git_status_entry *status_entry;
+ size_t i;
+ int error = 0;
+
+ if ((error = git_status_list_new(&statuslist, repo, opts)) < 0)
+ return error;
+
+ git_vector_foreach(&statuslist->paired, i, status_entry) {
+ const char *path = status_entry->head_to_index ?
+ status_entry->head_to_index->old_file.path :
+ status_entry->index_to_workdir->old_file.path;
+
+ if (cb(path, status_entry->status, payload) != 0) {
+ error = GIT_EUSER;
+ giterr_clear();
+ break;
+ }
+ }
+
+ git_status_list_free(statuslist);
+
+ return error;
}
int git_status_foreach(
diff --git a/src/status.h b/src/status.h
new file mode 100644
index 000000000..b58e0ebd6
--- /dev/null
+++ b/src/status.h
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+#ifndef INCLUDE_status_h__
+#define INCLUDE_status_h__
+
+#include "diff.h"
+#include "git2/status.h"
+#include "git2/diff.h"
+
+struct git_status_list {
+ git_status_options opts;
+
+ git_diff_list *head2idx;
+ git_diff_list *idx2wd;
+
+ git_vector paired;
+};
+
+#endif