mirror of
https://git.proxmox.com/git/libgit2
synced 2025-08-02 20:31:22 +00:00
Initial implementation of status example
This commit is contained in:
parent
852ded9698
commit
cf300bb9e5
@ -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)
|
||||
|
||||
|
294
examples/status.c
Normal file
294
examples/status.c
Normal file
@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2012 the libgit2 contributors
|
||||
*
|
||||
* 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 `git_status_foreach()` to roughly
|
||||
* simulate the output of running `git status`. It should serve as a simple
|
||||
* example of how to get basic status information.
|
||||
*
|
||||
* This does not have:
|
||||
* - Robust error handling
|
||||
* - Any real command line parsing
|
||||
* - Colorized or paginated output formatting
|
||||
*
|
||||
*/
|
||||
|
||||
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("# %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)
|
||||
{
|
||||
(void)repo;
|
||||
(void)status;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user