Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_git.c

Nilorea Library n_git module test.

Nilorea Library n_git module test

Author
Castagnier Mickael
Version
1.0
Date
26/03/2026
/*
* Nilorea Library
* Copyright (C) 2005-2026 Castagnier Mickael
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifdef HAVE_LIBGIT2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "nilorea/n_log.h"
#include "nilorea/n_str.h"
#include "nilorea/n_list.h"
#include "nilorea/n_git.h"
void usage(void) {
fprintf(stderr,
" -v version\n"
" -V log level: LOG_INFO, LOG_NOTICE, LOG_ERR, LOG_DEBUG\n"
" -h help\n");
}
void process_args(int argc, char** argv) {
int getoptret = 0,
while ((getoptret = getopt(argc, argv, "hvV:")) != EOF) {
switch (getoptret) {
case 'v':
fprintf(stderr, "Date de compilation : %s a %s.\n", __DATE__, __TIME__);
exit(1);
case 'V':
if (!strcmp("LOG_NULL", optarg))
else if (!strcmp("LOG_NOTICE", optarg))
else if (!strcmp("LOG_INFO", optarg))
else if (!strcmp("LOG_ERR", optarg))
else if (!strcmp("LOG_DEBUG", optarg))
else {
fprintf(stderr, "%s is not a valid log level.\n", optarg);
exit(-1);
}
break;
default:
case '?': {
if (optopt == 'V') {
fprintf(stderr, "\n Missing log level\n");
}
usage();
exit(1);
}
case 'h': {
usage();
exit(1);
}
}
}
}
static int write_file(const char* dir, const char* filename, const char* content) {
char path[1024];
snprintf(path, sizeof(path), "%s/%s", dir, filename);
FILE* fp = fopen(path, "w");
if (!fp) return -1;
fprintf(fp, "%s", content);
fclose(fp);
return 0;
}
static void cleanup_dir(const char* dir) {
char cmd[1088];
snprintf(cmd, sizeof(cmd), "rm -rf %s", dir);
int ret = system(cmd);
(void)ret;
}
int main(int argc, char** argv) {
process_args(argc, argv);
int errors = 0;
/* create temp directory */
char tmpdir[] = "/tmp/nilorea_git_test_XXXXXX";
if (!mkdtemp(tmpdir)) {
n_log(LOG_ERR, "mkdtemp failed");
return 1;
}
n_log(LOG_INFO, "Test repo dir: %s", tmpdir);
/* init repo */
N_GIT_REPO* repo = n_git_init(tmpdir);
if (!repo) {
n_log(LOG_ERR, "FAIL: n_git_init returned NULL");
cleanup_dir(tmpdir);
return 1;
}
n_log(LOG_INFO, "PASS: n_git_init");
/* create a file */
if (write_file(tmpdir, "test.txt", "hello") != 0) {
n_log(LOG_ERR, "FAIL: could not write test.txt");
errors++;
goto done;
}
/* stage it */
if (n_git_stage(repo, "test.txt") != 0) {
n_log(LOG_ERR, "FAIL: n_git_stage");
errors++;
goto done;
}
n_log(LOG_INFO, "PASS: n_git_stage");
/* commit */
if (n_git_commit(repo, "initial commit", "Test User", "test@example.org") != 0) {
n_log(LOG_ERR, "FAIL: n_git_commit");
errors++;
goto done;
}
n_log(LOG_INFO, "PASS: n_git_commit");
/* verify status is clean */
{
LIST* st = n_git_status(repo);
if (!st) {
n_log(LOG_ERR, "FAIL: n_git_status returned NULL");
errors++;
} else {
if (st->nb_items != 0) {
n_log(LOG_ERR, "FAIL: status should be clean, got %zu entries", st->nb_items);
errors++;
} else {
n_log(LOG_INFO, "PASS: status is clean after commit");
}
}
}
/* modify the file */
if (write_file(tmpdir, "test.txt", "hello world modified") != 0) {
n_log(LOG_ERR, "FAIL: could not modify test.txt");
errors++;
goto done;
}
/* get diff (should be non-empty) */
{
N_STR* diff = n_git_diff_workdir(repo);
if (!diff || !diff->data || diff->written == 0) {
n_log(LOG_ERR, "FAIL: diff should be non-empty after modification");
errors++;
} else {
n_log(LOG_INFO, "PASS: diff is non-empty after modification");
n_log(LOG_DEBUG, "Diff output:\n%s", diff->data);
}
free_nstr(&diff);
}
/* restore the file */
if (n_git_checkout_path(repo, "test.txt") != 0) {
n_log(LOG_ERR, "FAIL: n_git_checkout_path");
errors++;
goto done;
}
n_log(LOG_INFO, "PASS: n_git_checkout_path");
/* verify diff is now empty */
{
N_STR* diff = n_git_diff_workdir(repo);
if (diff && diff->data && diff->written > 0) {
n_log(LOG_ERR, "FAIL: diff should be empty after restore, got: %s", diff->data);
errors++;
} else {
n_log(LOG_INFO, "PASS: diff is empty after restore");
}
free_nstr(&diff);
}
/* get log (should have 1 entry) */
{
LIST* log_list = n_git_log(repo, 10);
if (!log_list) {
n_log(LOG_ERR, "FAIL: n_git_log returned NULL");
errors++;
} else {
if (log_list->nb_items != 1) {
n_log(LOG_ERR, "FAIL: log should have 1 entry, got %zu", log_list->nb_items);
errors++;
} else {
n_log(LOG_INFO, "PASS: log has 1 entry");
LIST_NODE* node = log_list->start;
if (node && node->ptr) {
n_log(LOG_INFO, " hash: %s", info->hash);
n_log(LOG_INFO, " short: %s", info->short_hash);
n_log(LOG_INFO, " msg: %s", info->message);
n_log(LOG_INFO, " author: %s", info->author);
}
}
list_destroy(&log_list);
}
}
/* test current branch */
{
char branch[256];
if (n_git_current_branch(repo, branch, sizeof(branch)) == 0) {
n_log(LOG_INFO, "PASS: current branch = %s", branch);
} else {
n_log(LOG_ERR, "FAIL: n_git_current_branch");
errors++;
}
}
/* test branch operations */
{
if (n_git_create_branch(repo, "test-branch") == 0) {
n_log(LOG_INFO, "PASS: n_git_create_branch");
} else {
n_log(LOG_ERR, "FAIL: n_git_create_branch");
errors++;
}
LIST* branches = n_git_list_branches(repo);
if (branches) {
n_log(LOG_INFO, "PASS: n_git_list_branches (%zu branches)", branches->nb_items);
list_destroy(&branches);
} else {
n_log(LOG_ERR, "FAIL: n_git_list_branches");
errors++;
}
if (n_git_delete_branch(repo, "test-branch") == 0) {
n_log(LOG_INFO, "PASS: n_git_delete_branch");
} else {
n_log(LOG_ERR, "FAIL: n_git_delete_branch");
errors++;
}
}
n_git_close(&repo);
cleanup_dir(tmpdir);
if (errors > 0) {
n_log(LOG_ERR, "RESULT: %d test(s) FAILED", errors);
return 1;
}
n_log(LOG_INFO, "RESULT: all tests PASSED");
return 0;
}
#else /* !HAVE_LIBGIT2 */
#include <stdio.h>
int main(void) {
fprintf(stderr, "ex_git: built without HAVE_LIBGIT2, skipping\n");
return 0;
}
#endif /* HAVE_LIBGIT2 */
static void usage(void)
void process_args(int argc, char **argv)
Definition ex_common.c:47
int main(void)
int getoptret
Definition ex_fluid.c:60
int log_level
Definition ex_fluid.c:61
static int write_file(const char *dir, const char *filename, const char *content)
Write a file inside the repo with given content.
Definition ex_git.c:96
static void cleanup_dir(const char *dir)
Remove a directory tree recursively (simple rm -rf via system).
Definition ex_git.c:110
bool done
void n_git_close(N_GIT_REPO **repo)
Close a Git repository and free resources.
Definition n_git.c:185
int n_git_checkout_path(N_GIT_REPO *repo, const char *filepath)
Restore a single file from HEAD (discard working directory changes).
Definition n_git.c:679
LIST * n_git_list_branches(N_GIT_REPO *repo)
List all local branch names.
Definition n_git.c:749
LIST * n_git_status(N_GIT_REPO *repo)
Get the status of all files in the working directory.
Definition n_git.c:210
LIST * n_git_log(N_GIT_REPO *repo, size_t max_entries)
Retrieve commit log entries starting from HEAD.
Definition n_git.c:491
int n_git_stage(N_GIT_REPO *repo, const char *filepath)
Stage a single file by path.
Definition n_git.c:290
int n_git_create_branch(N_GIT_REPO *repo, const char *branch_name)
Create a new branch from HEAD.
Definition n_git.c:790
int n_git_current_branch(N_GIT_REPO *repo, char *out, size_t out_size)
Get the name of the current branch.
Definition n_git.c:879
int n_git_commit(N_GIT_REPO *repo, const char *message, const char *author_name, const char *author_email)
Create a commit from the current index.
Definition n_git.c:412
N_STR * n_git_diff_workdir(N_GIT_REPO *repo)
Get a diff of the working directory against the index.
Definition n_git.c:567
N_GIT_REPO * n_git_init(const char *path)
Initialize a new Git repository.
Definition n_git.c:155
int n_git_delete_branch(N_GIT_REPO *repo, const char *branch_name)
Delete a local branch.
Definition n_git.c:909
Commit metadata.
Definition n_git.h:98
Wrapper around a git_repository handle.
Definition n_git.h:80
void * ptr
void pointer to store
Definition n_list.h:45
LIST_NODE * start
pointer to the start of the list
Definition n_list.h:65
size_t nb_items
number of item currently in the list
Definition n_list.h:60
int list_destroy(LIST **list)
Empty and Free a list container.
Definition n_list.c:547
Structure of a generic LIST container.
Definition n_list.h:58
Structure of a generic list node.
Definition n_list.h:43
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:88
#define LOG_DEBUG
debug-level messages
Definition n_log.h:83
#define LOG_ERR
error conditions
Definition n_log.h:75
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
Definition n_log.c:120
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:79
#define LOG_NULL
no log output
Definition n_log.h:45
#define LOG_INFO
informational
Definition n_log.h:81
size_t written
size of the written data inside the string
Definition n_str.h:66
char * data
the string
Definition n_str.h:62
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition n_str.h:201
A box including a string and his lenght.
Definition n_str.h:60
Common headers and low-level functions & define.
libgit2 wrapper for Git repository operations
List structures and definitions.
Generic log system.
N_STR and string function declaration.