summaryrefslogtreecommitdiff
path: root/src/status.c
diff options
context:
space:
mode:
authorRussell Belfer <arrbee@arrbee.com>2012-03-21 12:33:09 -0700
committerRussell Belfer <arrbee@arrbee.com>2012-03-21 12:33:09 -0700
commita48ea31d69a76d6b398d3a1e522a1c7363a9b92a (patch)
tree1205730de9c5a02688a61360081c284c8ab6e535 /src/status.c
parenta4c291ef128e870d4e748dedfb3798c33df0ac15 (diff)
downloadlibgit2-a48ea31d69a76d6b398d3a1e522a1c7363a9b92a.tar.gz
Reimplment git_status_foreach using git diff
This is an initial reimplementation of status using diff a la the way that core git does it.
Diffstat (limited to 'src/status.c')
-rw-r--r--src/status.c179
1 files changed, 178 insertions, 1 deletions
diff --git a/src/status.c b/src/status.c
index 2221db3d9..eab7c8850 100644
--- a/src/status.c
+++ b/src/status.c
@@ -15,6 +15,183 @@
#include "repository.h"
#include "ignore.h"
+#include "git2/diff.h"
+#include "diff.h"
+
+static int resolve_head_to_tree(git_tree **tree, git_repository *repo)
+{
+ git_reference *head = NULL;
+ git_object *obj = NULL;
+
+ if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0)
+ return -1;
+
+ if (git_reference_oid(head) == NULL) {
+ git_reference *resolved;
+
+ if (git_reference_resolve(&resolved, head) < 0) {
+ /* cannot resolve HEAD - probably brand new repo */
+ giterr_clear();
+ git_reference_free(head);
+ return GIT_ENOTFOUND;
+ }
+
+ git_reference_free(head);
+ head = resolved;
+ }
+
+ if (git_object_lookup(&obj, repo, git_reference_oid(head), GIT_OBJ_ANY) < 0)
+ goto fail;
+
+ switch (git_object_type(obj)) {
+ case GIT_OBJ_TREE:
+ *tree = (git_tree *)obj;
+ break;
+ case GIT_OBJ_COMMIT:
+ if (git_commit_tree(tree, (git_commit *)obj) < 0)
+ goto fail;
+ git_object_free(obj);
+ break;
+ default:
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ git_object_free(obj);
+ git_reference_free(head);
+ return -1;
+}
+
+static unsigned int index_delta2status(git_delta_t index_status)
+{
+ unsigned int st = GIT_STATUS_CURRENT;
+
+ 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:
+ st = GIT_STATUS_INDEX_DELETED;
+ break;
+ case GIT_DELTA_MODIFIED:
+ st = GIT_STATUS_INDEX_MODIFIED;
+ break;
+ default:
+ break;
+ }
+
+ return st;
+}
+
+static unsigned int workdir_delta2status(git_delta_t workdir_status)
+{
+ unsigned int st = GIT_STATUS_CURRENT;
+
+ switch (workdir_status) {
+ case GIT_DELTA_ADDED:
+ case GIT_DELTA_COPIED:
+ case GIT_DELTA_RENAMED:
+ case GIT_DELTA_UNTRACKED:
+ st = GIT_STATUS_WT_NEW;
+ break;
+ case GIT_DELTA_DELETED:
+ st = GIT_STATUS_WT_DELETED;
+ break;
+ case GIT_DELTA_MODIFIED:
+ st = GIT_STATUS_WT_MODIFIED;
+ break;
+ case GIT_DELTA_IGNORED:
+ st = GIT_STATUS_IGNORED;
+ break;
+ default:
+ break;
+ }
+
+ return st;
+}
+
+int git_status_foreach_ext(
+ git_repository *repo,
+ git_status_options *opts,
+ int (*cb)(const char *, unsigned int, void *),
+ void *cbdata)
+{
+ int err = 0;
+ git_diff_options diffopt;
+ git_diff_list *idx2head = NULL, *wd2idx = NULL;
+ git_tree *head = NULL;
+ git_status_show_t show =
+ opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ git_diff_delta *i2h, *w2i;
+
+ assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
+
+ switch (resolve_head_to_tree(&head, repo)) {
+ case 0: break;
+ case GIT_ENOTFOUND: return 0;
+ default: return -1;
+ }
+
+ memset(&diffopt, 0, sizeof(diffopt));
+ diffopt.flags = GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ 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_workdir_to_index(repo, &diffopt, &wd2idx)) < 0)
+ goto cleanup;
+
+ if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {
+ git_diff_list *empty = NULL;
+ GIT_DIFF_COITERATE(
+ idx2head, empty, i2h, w2i,
+ err = cb(i2h->old.path, index_delta2status(i2h->status), cbdata),
+ /* nothing */, /* nothing */, if (err < 0) break);
+
+ git_diff_list_free(idx2head);
+ idx2head = NULL;
+ }
+
+ GIT_DIFF_COITERATE(
+ idx2head, wd2idx, i2h, w2i,
+ err = cb(i2h->old.path, index_delta2status(i2h->status), cbdata),
+ err = cb(w2i->old.path, workdir_delta2status(w2i->status), cbdata),
+ err = cb(i2h->old.path, index_delta2status(i2h->status) |
+ workdir_delta2status(w2i->status), cbdata),
+ if (err < 0) break);
+
+cleanup:
+ git_tree_free(head);
+ git_diff_list_free(idx2head);
+ git_diff_list_free(wd2idx);
+ return err;
+}
+
+int git_status_foreach(
+ git_repository *repo,
+ int (*callback)(const char *, unsigned int, void *),
+ void *payload)
+{
+ git_status_options opts;
+
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
+
+ return git_status_foreach_ext(repo, &opts, callback, payload);
+}
+
+
+/*
+ * the old stuff
+ */
+
struct status_entry {
git_index_time mtime;
@@ -461,7 +638,7 @@ static int status_cmp(const void *a, const void *b)
#define DEFAULT_SIZE 16
-int git_status_foreach(
+int git_status_foreach_old(
git_repository *repo,
int (*callback)(const char *, unsigned int, void *),
void *payload)