mirror of
https://git.proxmox.com/git/libgit2
synced 2025-08-04 03:23:24 +00:00
status access by index, providing more details to callers
This commit is contained in:
parent
09c2f91c15
commit
1ee2ef87ec
@ -174,9 +174,72 @@ 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.
|
||||
*
|
||||
|
@ -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 {
|
||||
|
232
src/status.c
232
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 int status_invoke_cb(
|
||||
git_diff_delta *h2i, git_diff_delta *i2w, void *payload)
|
||||
static bool status_is_included(
|
||||
git_status_list *statuslist,
|
||||
git_diff_delta *head2idx,
|
||||
git_diff_delta *idx2wd)
|
||||
{
|
||||
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 ((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 || 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))
|
||||
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;
|
||||
}
|
||||
|
||||
return usercb->cb(path, status, usercb->payload);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int git_status_foreach_ext(
|
||||
git_repository *repo,
|
||||
const git_status_options *opts,
|
||||
git_status_cb cb,
|
||||
static git_status_t status_compute(
|
||||
git_diff_delta *head2idx,
|
||||
git_diff_delta *idx2wd)
|
||||
{
|
||||
git_status_t status = 0;
|
||||
|
||||
if (head2idx)
|
||||
status |= index_delta2status(head2idx->status);
|
||||
|
||||
if (idx2wd)
|
||||
status |= workdir_delta2status(idx2wd->status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int status_collect(
|
||||
git_diff_delta *head2idx,
|
||||
git_diff_delta *idx2wd,
|
||||
void *payload)
|
||||
{
|
||||
int err = 0;
|
||||
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 0;
|
||||
}
|
||||
|
||||
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_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;
|
||||
|
||||
cleanup:
|
||||
*out = statuslist;
|
||||
goto done;
|
||||
|
||||
on_error:
|
||||
git_status_list_free(statuslist);
|
||||
|
||||
done:
|
||||
git_tree_free(head);
|
||||
git_diff_list_free(head2idx);
|
||||
git_diff_list_free(idx2wd);
|
||||
|
||||
if (err == GIT_EUSER)
|
||||
giterr_clear();
|
||||
return error;
|
||||
}
|
||||
|
||||
return err;
|
||||
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);
|
||||
|
||||
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(
|
||||
|
23
src/status.h
Normal file
23
src/status.h
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user