Commit 1ee2ef87 by Edward Thomson Committed by Russell Belfer

status access by index, providing more details to callers

parent 09c2f91c
......@@ -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
......
......@@ -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 {
......
......@@ -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(
......
/*
* 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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment