mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-28 10:28:56 +00:00
Merge pull request #1662 from arrbee/examples-like-git
Command line status example (with bug fixes)
This commit is contained in:
commit
f2d110f168
@ -3,7 +3,7 @@
|
||||
CC = gcc
|
||||
CFLAGS = -g -I../include -I../src -Wall -Wextra -Wmissing-prototypes -Wno-missing-field-initializers
|
||||
LFLAGS = -L../build -lgit2 -lz
|
||||
APPS = general showindex diff rev-list cat-file
|
||||
APPS = general showindex diff rev-list cat-file status
|
||||
|
||||
all: $(APPS)
|
||||
|
||||
|
443
examples/status.c
Normal file
443
examples/status.c
Normal file
@ -0,0 +1,443 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#include <git2.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
enum {
|
||||
FORMAT_DEFAULT = 0,
|
||||
FORMAT_LONG = 1,
|
||||
FORMAT_SHORT = 2,
|
||||
FORMAT_PORCELAIN = 3,
|
||||
};
|
||||
#define MAX_PATHSPEC 8
|
||||
|
||||
/*
|
||||
* This example demonstrates the use of the libgit2 status APIs,
|
||||
* particularly the `git_status_list` object, to roughly simulate the
|
||||
* output of running `git status`. It serves as a simple example of
|
||||
* using those APIs to get basic status information.
|
||||
*
|
||||
* This does not have:
|
||||
* - Robust error handling
|
||||
* - Colorized or paginated output formatting
|
||||
*
|
||||
* This does have:
|
||||
* - Examples of translating command line arguments to the status
|
||||
* options settings to mimic `git status` results.
|
||||
* - A sample status formatter that matches the default "long" format
|
||||
* from `git status`
|
||||
* - A sample status formatter that matches the "short" format
|
||||
*/
|
||||
|
||||
static void check(int error, const char *message, const char *extra)
|
||||
{
|
||||
const git_error *lg2err;
|
||||
const char *lg2msg = "", *lg2spacer = "";
|
||||
|
||||
if (!error)
|
||||
return;
|
||||
|
||||
if ((lg2err = giterr_last()) != NULL && lg2err->message != NULL) {
|
||||
lg2msg = lg2err->message;
|
||||
lg2spacer = " - ";
|
||||
}
|
||||
|
||||
if (extra)
|
||||
fprintf(stderr, "%s '%s' [%d]%s%s\n",
|
||||
message, extra, error, lg2spacer, lg2msg);
|
||||
else
|
||||
fprintf(stderr, "%s [%d]%s%s\n",
|
||||
message, error, lg2spacer, lg2msg);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void fail(const char *message)
|
||||
{
|
||||
check(-1, message, NULL);
|
||||
}
|
||||
|
||||
static void show_branch(git_repository *repo, int format)
|
||||
{
|
||||
int error = 0;
|
||||
const char *branch = NULL;
|
||||
git_reference *head = NULL;
|
||||
|
||||
error = git_repository_head(&head, repo);
|
||||
|
||||
if (error == GIT_EORPHANEDHEAD || error == GIT_ENOTFOUND)
|
||||
branch = NULL;
|
||||
else if (!error) {
|
||||
branch = git_reference_name(head);
|
||||
if (!strncmp(branch, "refs/heads/", strlen("refs/heads/")))
|
||||
branch += strlen("refs/heads/");
|
||||
} else
|
||||
check(error, "failed to get current branch", NULL);
|
||||
|
||||
if (format == FORMAT_LONG)
|
||||
printf("# On branch %s\n",
|
||||
branch ? branch : "Not currently on any branch.");
|
||||
else
|
||||
printf("## %s\n", branch ? branch : "HEAD (no branch)");
|
||||
|
||||
git_reference_free(head);
|
||||
}
|
||||
|
||||
static void print_long(git_repository *repo, git_status_list *status)
|
||||
{
|
||||
size_t i, maxi = git_status_list_entrycount(status);
|
||||
const git_status_entry *s;
|
||||
int header = 0, changes_in_index = 0;
|
||||
int changed_in_workdir = 0, rm_in_workdir = 0;
|
||||
const char *old_path, *new_path;
|
||||
|
||||
(void)repo;
|
||||
|
||||
/* print index changes */
|
||||
|
||||
for (i = 0; i < maxi; ++i) {
|
||||
char *istatus = NULL;
|
||||
|
||||
s = git_status_byindex(status, i);
|
||||
|
||||
if (s->status == GIT_STATUS_CURRENT)
|
||||
continue;
|
||||
|
||||
if (s->status & GIT_STATUS_WT_DELETED)
|
||||
rm_in_workdir = 1;
|
||||
|
||||
if (s->status & GIT_STATUS_INDEX_NEW)
|
||||
istatus = "new file: ";
|
||||
if (s->status & GIT_STATUS_INDEX_MODIFIED)
|
||||
istatus = "modified: ";
|
||||
if (s->status & GIT_STATUS_INDEX_DELETED)
|
||||
istatus = "deleted: ";
|
||||
if (s->status & GIT_STATUS_INDEX_RENAMED)
|
||||
istatus = "renamed: ";
|
||||
if (s->status & GIT_STATUS_INDEX_TYPECHANGE)
|
||||
istatus = "typechange:";
|
||||
|
||||
if (istatus == NULL)
|
||||
continue;
|
||||
|
||||
if (!header) {
|
||||
printf("# Changes to be committed:\n");
|
||||
printf("# (use \"git reset HEAD <file>...\" to unstage)\n");
|
||||
printf("#\n");
|
||||
header = 1;
|
||||
}
|
||||
|
||||
old_path = s->head_to_index->old_file.path;
|
||||
new_path = s->head_to_index->new_file.path;
|
||||
|
||||
if (old_path && new_path && strcmp(old_path, new_path))
|
||||
printf("#\t%s %s -> %s\n", istatus, old_path, new_path);
|
||||
else
|
||||
printf("#\t%s %s\n", istatus, old_path ? old_path : new_path);
|
||||
}
|
||||
|
||||
if (header) {
|
||||
changes_in_index = 1;
|
||||
printf("#\n");
|
||||
}
|
||||
header = 0;
|
||||
|
||||
/* print workdir changes to tracked files */
|
||||
|
||||
for (i = 0; i < maxi; ++i) {
|
||||
char *wstatus = NULL;
|
||||
|
||||
s = git_status_byindex(status, i);
|
||||
|
||||
if (s->status == GIT_STATUS_CURRENT || s->index_to_workdir == NULL)
|
||||
continue;
|
||||
|
||||
if (s->status & GIT_STATUS_WT_MODIFIED)
|
||||
wstatus = "modified: ";
|
||||
if (s->status & GIT_STATUS_WT_DELETED)
|
||||
wstatus = "deleted: ";
|
||||
if (s->status & GIT_STATUS_WT_RENAMED)
|
||||
wstatus = "renamed: ";
|
||||
if (s->status & GIT_STATUS_WT_TYPECHANGE)
|
||||
wstatus = "typechange:";
|
||||
|
||||
if (wstatus == NULL)
|
||||
continue;
|
||||
|
||||
if (!header) {
|
||||
printf("# Changes not staged for commit:\n");
|
||||
printf("# (use \"git add%s <file>...\" to update what will be committed)\n", rm_in_workdir ? "/rm" : "");
|
||||
printf("# (use \"git checkout -- <file>...\" to discard changes in working directory)\n");
|
||||
printf("#\n");
|
||||
header = 1;
|
||||
}
|
||||
|
||||
old_path = s->index_to_workdir->old_file.path;
|
||||
new_path = s->index_to_workdir->new_file.path;
|
||||
|
||||
if (old_path && new_path && strcmp(old_path, new_path))
|
||||
printf("#\t%s %s -> %s\n", wstatus, old_path, new_path);
|
||||
else
|
||||
printf("#\t%s %s\n", wstatus, old_path ? old_path : new_path);
|
||||
}
|
||||
|
||||
if (header) {
|
||||
changed_in_workdir = 1;
|
||||
printf("#\n");
|
||||
}
|
||||
header = 0;
|
||||
|
||||
/* print untracked files */
|
||||
|
||||
header = 0;
|
||||
|
||||
for (i = 0; i < maxi; ++i) {
|
||||
s = git_status_byindex(status, i);
|
||||
|
||||
if (s->status == GIT_STATUS_WT_NEW) {
|
||||
|
||||
if (!header) {
|
||||
printf("# Untracked files:\n");
|
||||
printf("# (use \"git add <file>...\" to include in what will be committed)\n");
|
||||
printf("#\n");
|
||||
header = 1;
|
||||
}
|
||||
|
||||
printf("#\t%s\n", s->index_to_workdir->old_file.path);
|
||||
}
|
||||
}
|
||||
|
||||
header = 0;
|
||||
|
||||
/* print ignored files */
|
||||
|
||||
for (i = 0; i < maxi; ++i) {
|
||||
s = git_status_byindex(status, i);
|
||||
|
||||
if (s->status == GIT_STATUS_IGNORED) {
|
||||
|
||||
if (!header) {
|
||||
printf("# Ignored files:\n");
|
||||
printf("# (use \"git add -f <file>...\" to include in what will be committed)\n");
|
||||
printf("#\n");
|
||||
header = 1;
|
||||
}
|
||||
|
||||
printf("#\t%s\n", s->index_to_workdir->old_file.path);
|
||||
}
|
||||
}
|
||||
|
||||
if (!changes_in_index && changed_in_workdir)
|
||||
printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
|
||||
}
|
||||
|
||||
static void print_short(git_repository *repo, git_status_list *status)
|
||||
{
|
||||
size_t i, maxi = git_status_list_entrycount(status);
|
||||
const git_status_entry *s;
|
||||
char istatus, wstatus;
|
||||
const char *extra, *a, *b, *c;
|
||||
|
||||
for (i = 0; i < maxi; ++i) {
|
||||
s = git_status_byindex(status, i);
|
||||
|
||||
if (s->status == GIT_STATUS_CURRENT)
|
||||
continue;
|
||||
|
||||
a = b = c = NULL;
|
||||
istatus = wstatus = ' ';
|
||||
extra = "";
|
||||
|
||||
if (s->status & GIT_STATUS_INDEX_NEW)
|
||||
istatus = 'A';
|
||||
if (s->status & GIT_STATUS_INDEX_MODIFIED)
|
||||
istatus = 'M';
|
||||
if (s->status & GIT_STATUS_INDEX_DELETED)
|
||||
istatus = 'D';
|
||||
if (s->status & GIT_STATUS_INDEX_RENAMED)
|
||||
istatus = 'R';
|
||||
if (s->status & GIT_STATUS_INDEX_TYPECHANGE)
|
||||
istatus = 'T';
|
||||
|
||||
if (s->status & GIT_STATUS_WT_NEW) {
|
||||
if (istatus == ' ')
|
||||
istatus = '?';
|
||||
wstatus = '?';
|
||||
}
|
||||
if (s->status & GIT_STATUS_WT_MODIFIED)
|
||||
wstatus = 'M';
|
||||
if (s->status & GIT_STATUS_WT_DELETED)
|
||||
wstatus = 'D';
|
||||
if (s->status & GIT_STATUS_WT_RENAMED)
|
||||
wstatus = 'R';
|
||||
if (s->status & GIT_STATUS_WT_TYPECHANGE)
|
||||
wstatus = 'T';
|
||||
|
||||
if (s->status & GIT_STATUS_IGNORED) {
|
||||
istatus = '!';
|
||||
wstatus = '!';
|
||||
}
|
||||
|
||||
if (istatus == '?' && wstatus == '?')
|
||||
continue;
|
||||
|
||||
if (s->index_to_workdir &&
|
||||
s->index_to_workdir->new_file.mode == GIT_FILEMODE_COMMIT)
|
||||
{
|
||||
git_submodule *sm = NULL;
|
||||
unsigned int smstatus = 0;
|
||||
|
||||
if (!git_submodule_lookup(
|
||||
&sm, repo, s->index_to_workdir->new_file.path) &&
|
||||
!git_submodule_status(&smstatus, sm))
|
||||
{
|
||||
if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED)
|
||||
extra = " (new commits)";
|
||||
else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED)
|
||||
extra = " (modified content)";
|
||||
else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED)
|
||||
extra = " (modified content)";
|
||||
else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED)
|
||||
extra = " (untracked content)";
|
||||
}
|
||||
}
|
||||
|
||||
if (s->head_to_index) {
|
||||
a = s->head_to_index->old_file.path;
|
||||
b = s->head_to_index->new_file.path;
|
||||
}
|
||||
if (s->index_to_workdir) {
|
||||
if (!a)
|
||||
a = s->index_to_workdir->old_file.path;
|
||||
if (!b)
|
||||
b = s->index_to_workdir->old_file.path;
|
||||
c = s->index_to_workdir->new_file.path;
|
||||
}
|
||||
|
||||
if (istatus == 'R') {
|
||||
if (wstatus == 'R')
|
||||
printf("%c%c %s %s %s%s\n", istatus, wstatus, a, b, c, extra);
|
||||
else
|
||||
printf("%c%c %s %s%s\n", istatus, wstatus, a, b, extra);
|
||||
} else {
|
||||
if (wstatus == 'R')
|
||||
printf("%c%c %s %s%s\n", istatus, wstatus, a, c, extra);
|
||||
else
|
||||
printf("%c%c %s%s\n", istatus, wstatus, a, extra);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < maxi; ++i) {
|
||||
s = git_status_byindex(status, i);
|
||||
|
||||
if (s->status == GIT_STATUS_WT_NEW)
|
||||
printf("?? %s\n", s->index_to_workdir->old_file.path);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
git_repository *repo = NULL;
|
||||
int i, npaths = 0, format = FORMAT_DEFAULT, zterm = 0, showbranch = 0;
|
||||
git_status_options opt = GIT_STATUS_OPTIONS_INIT;
|
||||
git_status_list *status;
|
||||
char *repodir = ".", *pathspec[MAX_PATHSPEC];
|
||||
|
||||
opt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
|
||||
opt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
|
||||
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
|
||||
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (argv[i][0] != '-') {
|
||||
if (npaths < MAX_PATHSPEC)
|
||||
pathspec[npaths++] = argv[i];
|
||||
else
|
||||
fail("Example only supports a limited pathspec");
|
||||
}
|
||||
else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--short"))
|
||||
format = FORMAT_SHORT;
|
||||
else if (!strcmp(argv[i], "--long"))
|
||||
format = FORMAT_LONG;
|
||||
else if (!strcmp(argv[i], "--porcelain"))
|
||||
format = FORMAT_PORCELAIN;
|
||||
else if (!strcmp(argv[i], "-b") || !strcmp(argv[i], "--branch"))
|
||||
showbranch = 1;
|
||||
else if (!strcmp(argv[i], "-z")) {
|
||||
zterm = 1;
|
||||
if (format == FORMAT_DEFAULT)
|
||||
format = FORMAT_PORCELAIN;
|
||||
}
|
||||
else if (!strcmp(argv[i], "--ignored"))
|
||||
opt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
|
||||
else if (!strcmp(argv[i], "-uno") ||
|
||||
!strcmp(argv[i], "--untracked-files=no"))
|
||||
opt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||
else if (!strcmp(argv[i], "-unormal") ||
|
||||
!strcmp(argv[i], "--untracked-files=normal"))
|
||||
opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||
else if (!strcmp(argv[i], "-uall") ||
|
||||
!strcmp(argv[i], "--untracked-files=all"))
|
||||
opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
|
||||
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
|
||||
else if (!strcmp(argv[i], "--ignore-submodules=all"))
|
||||
opt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
|
||||
else if (!strncmp(argv[i], "--git-dir=", strlen("--git-dir=")))
|
||||
repodir = argv[i] + strlen("--git-dir=");
|
||||
else
|
||||
check(-1, "Unsupported option", argv[i]);
|
||||
}
|
||||
|
||||
if (format == FORMAT_DEFAULT)
|
||||
format = FORMAT_LONG;
|
||||
if (format == FORMAT_LONG)
|
||||
showbranch = 1;
|
||||
if (npaths > 0) {
|
||||
opt.pathspec.strings = pathspec;
|
||||
opt.pathspec.count = npaths;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to open the repository at the given path (or at the current
|
||||
* directory if none was given).
|
||||
*/
|
||||
check(git_repository_open_ext(&repo, repodir, 0, NULL),
|
||||
"Could not open repository", repodir);
|
||||
|
||||
if (git_repository_is_bare(repo))
|
||||
fail("Cannot report status on bare repository");
|
||||
|
||||
/*
|
||||
* Run status on the repository
|
||||
*
|
||||
* Because we want to simluate a full "git status" run and want to
|
||||
* support some command line options, we use `git_status_foreach_ext()`
|
||||
* instead of just the plain status call. This allows (a) iterating
|
||||
* over the index and then the workdir and (b) extra flags that control
|
||||
* which files are included. If you just want simple status (e.g. to
|
||||
* enumerate files that are modified) then you probably don't need the
|
||||
* extended API.
|
||||
*/
|
||||
check(git_status_list_new(&status, repo, &opt),
|
||||
"Could not get status", NULL);
|
||||
|
||||
if (showbranch)
|
||||
show_branch(repo, format);
|
||||
|
||||
if (format == FORMAT_LONG)
|
||||
print_long(repo, status);
|
||||
else
|
||||
print_short(repo, status);
|
||||
|
||||
git_status_list_free(status);
|
||||
git_repository_free(repo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -111,6 +111,12 @@ typedef enum {
|
||||
* - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates tha rename
|
||||
* detection should be run between the index and the working directory
|
||||
* and enabled GIT_STATUS_WT_RENAMED as a possible status flag.
|
||||
* - GIT_STATUS_OPT_SORT_CASE_SENSITIVELY overrides the native case
|
||||
* sensitivity for the file system and forces the output to be in
|
||||
* case-sensitive order
|
||||
* - GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY overrides the native case
|
||||
* sensitivity for the file system and forces the output to be in
|
||||
* case-insensitive order
|
||||
*
|
||||
* Calling `git_status_foreach()` is like calling the extended version
|
||||
* with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
|
||||
@ -127,6 +133,8 @@ typedef enum {
|
||||
GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6),
|
||||
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7),
|
||||
GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8),
|
||||
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9),
|
||||
GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10),
|
||||
} git_status_opt_t;
|
||||
|
||||
#define GIT_STATUS_OPT_DEFAULTS \
|
||||
|
@ -498,7 +498,7 @@ int git_attr_assignment__parse(
|
||||
|
||||
assert(assigns && !assigns->length);
|
||||
|
||||
assigns->_cmp = sort_by_hash_and_name;
|
||||
git_vector_set_cmp(assigns, sort_by_hash_and_name);
|
||||
|
||||
while (*scan && *scan != '\n') {
|
||||
const char *name_start, *value_start;
|
||||
|
25
src/diff.c
25
src/diff.c
@ -365,7 +365,7 @@ static git_diff_list *diff_list_alloc(
|
||||
diff->pfxcomp = git__prefixcmp_icase;
|
||||
diff->entrycomp = git_index_entry__cmp_icase;
|
||||
|
||||
diff->deltas._cmp = git_diff_delta__casecmp;
|
||||
git_vector_set_cmp(&diff->deltas, git_diff_delta__casecmp);
|
||||
}
|
||||
|
||||
return diff;
|
||||
@ -786,10 +786,15 @@ static int diff_scan_inside_untracked_dir(
|
||||
|
||||
/* need to recurse into non-ignored directories */
|
||||
if (!is_ignored && S_ISDIR(info->nitem->mode)) {
|
||||
if ((error = git_iterator_advance_into(
|
||||
&info->nitem, info->new_iter)) < 0)
|
||||
break;
|
||||
continue;
|
||||
error = git_iterator_advance_into(&info->nitem, info->new_iter);
|
||||
|
||||
if (!error)
|
||||
continue;
|
||||
else if (error == GIT_ENOTFOUND) {
|
||||
error = 0;
|
||||
is_ignored = true; /* treat empty as ignored */
|
||||
} else
|
||||
break; /* real error, must stop */
|
||||
}
|
||||
|
||||
/* found a non-ignored item - treat parent dir as untracked */
|
||||
@ -1160,7 +1165,7 @@ int git_diff_tree_to_index(
|
||||
d->pfxcomp = git__prefixcmp_icase;
|
||||
d->entrycomp = git_index_entry__cmp_icase;
|
||||
|
||||
d->deltas._cmp = git_diff_delta__casecmp;
|
||||
git_vector_set_cmp(&d->deltas, git_diff_delta__casecmp);
|
||||
git_vector_sort(&d->deltas);
|
||||
}
|
||||
}
|
||||
@ -1261,10 +1266,10 @@ int git_diff__paired_foreach(
|
||||
/* force case-sensitive delta sort */
|
||||
if (icase_mismatch) {
|
||||
if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) {
|
||||
head2idx->deltas._cmp = git_diff_delta__cmp;
|
||||
git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp);
|
||||
git_vector_sort(&head2idx->deltas);
|
||||
} else {
|
||||
idx2wd->deltas._cmp = git_diff_delta__cmp;
|
||||
git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__cmp);
|
||||
git_vector_sort(&idx2wd->deltas);
|
||||
}
|
||||
}
|
||||
@ -1296,10 +1301,10 @@ int git_diff__paired_foreach(
|
||||
/* restore case-insensitive delta sort */
|
||||
if (icase_mismatch) {
|
||||
if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) {
|
||||
head2idx->deltas._cmp = git_diff_delta__casecmp;
|
||||
git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp);
|
||||
git_vector_sort(&head2idx->deltas);
|
||||
} else {
|
||||
idx2wd->deltas._cmp = git_diff_delta__casecmp;
|
||||
git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__casecmp);
|
||||
git_vector_sort(&idx2wd->deltas);
|
||||
}
|
||||
}
|
||||
|
10
src/index.c
10
src/index.c
@ -290,16 +290,16 @@ void git_index__set_ignore_case(git_index *index, bool ignore_case)
|
||||
{
|
||||
index->ignore_case = ignore_case;
|
||||
|
||||
index->entries._cmp = ignore_case ? index_icmp : index_cmp;
|
||||
index->entries_cmp_path = ignore_case ? index_icmp_path : index_cmp_path;
|
||||
index->entries_search = ignore_case ? index_isrch : index_srch;
|
||||
index->entries_search_path = ignore_case ? index_isrch_path : index_srch_path;
|
||||
index->entries.sorted = 0;
|
||||
|
||||
git_vector_set_cmp(&index->entries, ignore_case ? index_icmp : index_cmp);
|
||||
git_vector_sort(&index->entries);
|
||||
|
||||
index->reuc._cmp = ignore_case ? reuc_icmp : reuc_cmp;
|
||||
index->reuc_search = ignore_case ? reuc_isrch : reuc_srch;
|
||||
index->reuc.sorted = 0;
|
||||
|
||||
git_vector_set_cmp(&index->reuc, ignore_case ? reuc_icmp : reuc_cmp);
|
||||
git_vector_sort(&index->reuc);
|
||||
}
|
||||
|
||||
@ -2024,7 +2024,7 @@ int git_index_read_tree(git_index *index, const git_tree *tree)
|
||||
|
||||
git_vector_sort(&index->entries);
|
||||
|
||||
entries._cmp = index->entries._cmp;
|
||||
git_vector_set_cmp(&entries, index->entries._cmp);
|
||||
git_vector_swap(&entries, &index->entries);
|
||||
|
||||
git_index_clear(index);
|
||||
|
12
src/status.c
12
src/status.c
@ -335,8 +335,16 @@ int git_status_list_new(
|
||||
status->head2idx, status->idx2wd, status_collect, status)) < 0)
|
||||
goto done;
|
||||
|
||||
if ((flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 ||
|
||||
(flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0)
|
||||
if (flags & GIT_STATUS_OPT_SORT_CASE_SENSITIVELY)
|
||||
git_vector_set_cmp(&status->paired, status_entry_cmp);
|
||||
if (flags & GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY)
|
||||
git_vector_set_cmp(&status->paired, status_entry_icmp);
|
||||
|
||||
if ((flags &
|
||||
(GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
|
||||
GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR |
|
||||
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY |
|
||||
GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY)) != 0)
|
||||
git_vector_sort(&status->paired);
|
||||
|
||||
done:
|
||||
|
@ -151,7 +151,7 @@ int git_submodule_foreach(
|
||||
int error;
|
||||
git_submodule *sm;
|
||||
git_vector seen = GIT_VECTOR_INIT;
|
||||
seen._cmp = submodule_cmp;
|
||||
git_vector_set_cmp(&seen, submodule_cmp);
|
||||
|
||||
assert(repo && callback);
|
||||
|
||||
|
@ -78,4 +78,13 @@ void git_vector_remove_matching(
|
||||
int git_vector_resize_to(git_vector *v, size_t new_length);
|
||||
int git_vector_set(void **old, git_vector *v, size_t position, void *value);
|
||||
|
||||
/** Set the comparison function used for sorting the vector */
|
||||
GIT_INLINE(void) git_vector_set_cmp(git_vector *v, git_vector_cmp cmp)
|
||||
{
|
||||
if (cmp != v->_cmp) {
|
||||
v->_cmp = cmp;
|
||||
v->sorted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1109,6 +1109,26 @@ void test_diff_workdir__untracked_directory_scenarios(void)
|
||||
|
||||
git_diff_list_free(diff);
|
||||
|
||||
/* empty directory in empty directory */
|
||||
|
||||
cl_git_pass(p_mkdir("status/subdir/directory/empty", 0777));
|
||||
|
||||
memset(&exp, 0, sizeof(exp));
|
||||
exp.names = files1;
|
||||
|
||||
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
|
||||
|
||||
cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
|
||||
|
||||
cl_assert_equal_i(4, exp.files);
|
||||
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
|
||||
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
|
||||
/* directory with only ignored files */
|
||||
|
||||
cl_git_pass(p_mkdir("status/subdir/directory/deeper", 0777));
|
||||
|
@ -6,6 +6,9 @@ int cb_status__normal(
|
||||
{
|
||||
status_entry_counts *counts = payload;
|
||||
|
||||
if (counts->debug)
|
||||
cb_status__print(path, status_flags, NULL);
|
||||
|
||||
if (counts->entry_count >= counts->expected_entry_count) {
|
||||
counts->wrong_status_flags_count++;
|
||||
goto exit;
|
||||
|
@ -8,6 +8,7 @@ typedef struct {
|
||||
const unsigned int* expected_statuses;
|
||||
const char** expected_paths;
|
||||
int expected_entry_count;
|
||||
bool debug;
|
||||
} status_entry_counts;
|
||||
|
||||
/* cb_status__normal takes payload of "status_entry_counts *" */
|
||||
|
@ -743,3 +743,83 @@ void test_status_worktree__simple_delete_indexed(void)
|
||||
GIT_STATUS_WT_DELETED, git_status_byindex(status, 0)->status);
|
||||
git_status_list_free(status);
|
||||
}
|
||||
|
||||
static const char *icase_paths[] = { "B", "c", "g", "H" };
|
||||
static unsigned int icase_statuses[] = {
|
||||
GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED,
|
||||
GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED,
|
||||
};
|
||||
|
||||
static const char *case_paths[] = { "B", "H", "c", "g" };
|
||||
static unsigned int case_statuses[] = {
|
||||
GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED,
|
||||
GIT_STATUS_WT_DELETED, GIT_STATUS_WT_MODIFIED,
|
||||
};
|
||||
|
||||
void test_status_worktree__sorting_by_case(void)
|
||||
{
|
||||
git_repository *repo = cl_git_sandbox_init("icase");
|
||||
git_index *index;
|
||||
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||
bool native_ignore_case;
|
||||
status_entry_counts counts;
|
||||
|
||||
cl_git_pass(git_repository_index(&index, repo));
|
||||
native_ignore_case =
|
||||
(git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0;
|
||||
git_index_free(index);
|
||||
|
||||
memset(&counts, 0, sizeof(counts));
|
||||
counts.expected_entry_count = 0;
|
||||
counts.expected_paths = NULL;
|
||||
counts.expected_statuses = NULL;
|
||||
cl_git_pass(
|
||||
git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
|
||||
cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
|
||||
cl_assert_equal_i(0, counts.wrong_status_flags_count);
|
||||
cl_assert_equal_i(0, counts.wrong_sorted_path);
|
||||
|
||||
cl_git_rewritefile("icase/B", "new stuff");
|
||||
cl_must_pass(p_unlink("icase/c"));
|
||||
cl_git_rewritefile("icase/g", "new stuff");
|
||||
cl_must_pass(p_unlink("icase/H"));
|
||||
|
||||
memset(&counts, 0, sizeof(counts));
|
||||
counts.expected_entry_count = 4;
|
||||
if (native_ignore_case) {
|
||||
counts.expected_paths = icase_paths;
|
||||
counts.expected_statuses = icase_statuses;
|
||||
} else {
|
||||
counts.expected_paths = case_paths;
|
||||
counts.expected_statuses = case_statuses;
|
||||
}
|
||||
cl_git_pass(
|
||||
git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
|
||||
cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
|
||||
cl_assert_equal_i(0, counts.wrong_status_flags_count);
|
||||
cl_assert_equal_i(0, counts.wrong_sorted_path);
|
||||
|
||||
opts.flags = GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
|
||||
|
||||
memset(&counts, 0, sizeof(counts));
|
||||
counts.expected_entry_count = 4;
|
||||
counts.expected_paths = case_paths;
|
||||
counts.expected_statuses = case_statuses;
|
||||
cl_git_pass(
|
||||
git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
|
||||
cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
|
||||
cl_assert_equal_i(0, counts.wrong_status_flags_count);
|
||||
cl_assert_equal_i(0, counts.wrong_sorted_path);
|
||||
|
||||
opts.flags = GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY;
|
||||
|
||||
memset(&counts, 0, sizeof(counts));
|
||||
counts.expected_entry_count = 4;
|
||||
counts.expected_paths = icase_paths;
|
||||
counts.expected_statuses = icase_statuses;
|
||||
cl_git_pass(
|
||||
git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
|
||||
cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
|
||||
cl_assert_equal_i(0, counts.wrong_status_flags_count);
|
||||
cl_assert_equal_i(0, counts.wrong_sorted_path);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user