Compare commits
No commits in common. "c8s" and "c8" have entirely different histories.
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,5 +1 @@
|
|||||||
/libgit2-0.26.3.tar.gz
|
SOURCES/libgit2-0.26.8.tar.gz
|
||||||
/libgit2-0.26.4.tar.gz
|
|
||||||
/libgit2-0.26.5.tar.gz
|
|
||||||
/libgit2-0.26.6.tar.gz
|
|
||||||
/libgit2-0.26.8.tar.gz
|
|
||||||
|
1
.libgit2.metadata
Normal file
1
.libgit2.metadata
Normal file
@ -0,0 +1 @@
|
|||||||
|
ec4242317439239e53c5a137f918fcee9d21bcef SOURCES/libgit2-0.26.8.tar.gz
|
@ -1,74 +0,0 @@
|
|||||||
From c31dcbfd93d85a008e95b23c129c7e8887f1316e Mon Sep 17 00:00:00 2001
|
|
||||||
From: Patrick Steinhardt <ps@pks.im>
|
|
||||||
Date: Fri, 21 Jun 2019 15:53:54 +0200
|
|
||||||
Subject: [PATCH 6/9] commit_list: fix possible buffer overflow in
|
|
||||||
`commit_quick_parse`
|
|
||||||
|
|
||||||
The function `commit_quick_parse` provides a way to quickly parse
|
|
||||||
parts of a commit without storing or verifying most of its
|
|
||||||
metadata. The first thing it does is calculating the number of
|
|
||||||
parents by skipping "parent " lines until it finds the first
|
|
||||||
non-parent line. Afterwards, this parent count is passed to
|
|
||||||
`alloc_parents`, which will allocate an array to store all the
|
|
||||||
parent.
|
|
||||||
|
|
||||||
To calculate the amount of storage required for the parents
|
|
||||||
array, `alloc_parents` simply multiplicates the number of parents
|
|
||||||
with the respective elements's size. This already screams "buffer
|
|
||||||
overflow", and in fact this problem is getting worse by the
|
|
||||||
result being cast to an `uint32_t`.
|
|
||||||
|
|
||||||
In fact, triggering this is possible: git-hash-object(1) will
|
|
||||||
happily write a commit with multiple millions of parents for you.
|
|
||||||
I've stopped at 67,108,864 parents as git-hash-object(1)
|
|
||||||
unfortunately soaks up the complete object without streaming
|
|
||||||
anything to disk and thus will cause an OOM situation at a later
|
|
||||||
point. The point here is: this commit was about 4.1GB of size but
|
|
||||||
compressed down to 24MB and thus easy to distribute.
|
|
||||||
|
|
||||||
The above doesn't yet trigger the buffer overflow, thus. As the
|
|
||||||
array's elements are all pointers which are 8 bytes on 64 bit, we
|
|
||||||
need a total of 536,870,912 parents to trigger the overflow to
|
|
||||||
`0`. The effect is that we're now underallocating the array
|
|
||||||
and do an out-of-bound writes. As the buffer is kindly provided
|
|
||||||
by the adversary, this may easily result in code execution.
|
|
||||||
|
|
||||||
Extrapolating from the test file with 67m commits to the one with
|
|
||||||
536m commits results in a factor of 8. Thus the uncompressed
|
|
||||||
contents would be about 32GB in size and the compressed ones
|
|
||||||
192MB. While still easily distributable via the network, only
|
|
||||||
servers will have that amount of RAM and not cause an
|
|
||||||
out-of-memory condition previous to triggering the overflow. This
|
|
||||||
at least makes this attack not an easy vector for client-side use
|
|
||||||
of libgit2.
|
|
||||||
|
|
||||||
(cherry picked from commit 3316f666566f768eb8aa8de521a5262524dc3424)
|
|
||||||
---
|
|
||||||
src/commit_list.c | 8 ++++++--
|
|
||||||
1 file changed, 6 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/src/commit_list.c b/src/commit_list.c
|
|
||||||
index 7df79bfd6..14d1c9813 100644
|
|
||||||
--- a/src/commit_list.c
|
|
||||||
+++ b/src/commit_list.c
|
|
||||||
@@ -69,11 +69,15 @@ static int commit_error(git_commit_list_node *commit, const char *msg)
|
|
||||||
static git_commit_list_node **alloc_parents(
|
|
||||||
git_revwalk *walk, git_commit_list_node *commit, size_t n_parents)
|
|
||||||
{
|
|
||||||
+ size_t bytes;
|
|
||||||
+
|
|
||||||
if (n_parents <= PARENTS_PER_COMMIT)
|
|
||||||
return (git_commit_list_node **)((char *)commit + sizeof(git_commit_list_node));
|
|
||||||
|
|
||||||
- return (git_commit_list_node **)git_pool_malloc(
|
|
||||||
- &walk->commit_pool, (uint32_t)(n_parents * sizeof(git_commit_list_node *)));
|
|
||||||
+ if (git__multiply_sizet_overflow(&bytes, n_parents, sizeof(git_commit_list_node *)))
|
|
||||||
+ return NULL;
|
|
||||||
+
|
|
||||||
+ return (git_commit_list_node **)git_pool_malloc(&walk->commit_pool, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
|||||||
From 9a3568814bd1d7a3e6522e0f61168d63c29efe97 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Carl Dong <accounts@carldong.me>
|
|
||||||
Date: Mon, 9 May 2022 12:09:08 -0400
|
|
||||||
Subject: [PATCH] revparse: Remove error-prone, redundant test
|
|
||||||
|
|
||||||
Originally introduced in: 776a6a8e5f8e258d31aded73c0ce38df6ac7bdc4
|
|
||||||
|
|
||||||
This test case has recently been fixed in bdab22384cc61d315005a65456a9f9563bb27c8f, but that fix will only last for a year. Next year the same problem will crop up and the test will need to be re-edited.
|
|
||||||
|
|
||||||
This is not ideal as
|
|
||||||
- This test case becomes an unnecessary burden for developers
|
|
||||||
- Downstream distros or even just users who want to build older versions of libgit2 are guaranteed to have this test fail
|
|
||||||
|
|
||||||
Furthermore, this test case is entirely unnecessary, as the functionality that was originally (see 776a6a8e5f8e258d31aded73c0ce38df6ac7bdc4) intended to be tested is well-covered by subsequent tests which specify a date instead of a "x ago" specification.
|
|
||||||
---
|
|
||||||
tests/refs/revparse.c | 2 --
|
|
||||||
1 file changed, 2 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/tests/refs/revparse.c b/tests/refs/revparse.c
|
|
||||||
index 459188cf7..2bb19ff69 100644
|
|
||||||
--- a/tests/refs/revparse.c
|
|
||||||
+++ b/tests/refs/revparse.c
|
|
||||||
@@ -400,8 +400,6 @@ void test_refs_revparse__date(void)
|
|
||||||
* a65fedf HEAD@{1335806603 -0900}: commit:
|
|
||||||
* be3563a HEAD@{1335806563 -0700}: clone: from /Users/ben/src/libgit2/tests/resour
|
|
||||||
*/
|
|
||||||
- test_object("HEAD@{10 years ago}", NULL);
|
|
||||||
-
|
|
||||||
test_object("HEAD@{1 second}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
|
|
||||||
test_object("HEAD@{1 second ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
|
|
||||||
test_object("HEAD@{2 days ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
Name: libgit2
|
Name: libgit2
|
||||||
Version: 0.26.8
|
Version: 0.26.8
|
||||||
Release: 3%{?dist}
|
Release: 2%{?dist}
|
||||||
Summary: C implementation of the Git core methods as a library with a solid API
|
Summary: C implementation of the Git core methods as a library with a solid API
|
||||||
License: GPLv2 with exceptions
|
License: GPLv2 with exceptions
|
||||||
URL: http://libgit2.github.com/
|
URL: http://libgit2.github.com/
|
||||||
@ -12,8 +12,6 @@ Patch0002: 0002-CMakeLists-increase-strict-aliasing-level-to-3.patch
|
|||||||
Patch0003: 0003-Disable-online-tests.patch
|
Patch0003: 0003-Disable-online-tests.patch
|
||||||
Patch0004: 0004-tests-Increase-TOOBIG-value-used-on-i686.patch
|
Patch0004: 0004-tests-Increase-TOOBIG-value-used-on-i686.patch
|
||||||
Patch0005: 0005-openssl-Use-the-system-profile-ciphers.patch
|
Patch0005: 0005-openssl-Use-the-system-profile-ciphers.patch
|
||||||
Patch0006: 0006-commit_list-fix-possible-buffer-overflow-in-commit_q.patch
|
|
||||||
Patch0007: 0007-revparse-Remove-error-prone-redundant-test.patch
|
|
||||||
|
|
||||||
BuildRequires: gcc
|
BuildRequires: gcc
|
||||||
BuildRequires: cmake
|
BuildRequires: cmake
|
||||||
@ -82,12 +80,6 @@ popd
|
|||||||
%{_includedir}/git2/
|
%{_includedir}/git2/
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Wed Feb 07 2024 Brian C. Lane <bcl@redhat.com> - 0.26.8-3
|
|
||||||
- revparse: Remove error-prone, redundant test
|
|
||||||
Related: RHEL-9503
|
|
||||||
- commit_list: fix possible buffer overflow in `commit_quick_parse`
|
|
||||||
Resolves: RHEL-9503
|
|
||||||
|
|
||||||
* Tue Jun 02 2020 Brian C. Lane <bcl@redhat.com> - 0.26.8-2
|
* Tue Jun 02 2020 Brian C. Lane <bcl@redhat.com> - 0.26.8-2
|
||||||
- openssl: Use the system profile ciphers
|
- openssl: Use the system profile ciphers
|
||||||
Resolves: rhbz#1842814
|
Resolves: rhbz#1842814
|
@ -1,6 +0,0 @@
|
|||||||
--- !Policy
|
|
||||||
product_versions:
|
|
||||||
- rhel-8
|
|
||||||
decision_context: osci_compose_gate
|
|
||||||
rules:
|
|
||||||
- !PassingTestCaseRule {test_case_name: osci.brew-build.tier0.functional}
|
|
1
sources
1
sources
@ -1 +0,0 @@
|
|||||||
SHA512 (libgit2-0.26.8.tar.gz) = efb9229b2882ac36910d75778d7431bec40f3529f2ad2dd6a950d5580ceb0d4dd798242a422ea6cbb880f515df4dfbb7055a65dd5a8106696d3d458851fca56e
|
|
@ -1,12 +0,0 @@
|
|||||||
.PHONY: all
|
|
||||||
|
|
||||||
CC = gcc
|
|
||||||
CFLAGS = -Wall -Wextra -Wmissing-prototypes -Wno-missing-field-initializers
|
|
||||||
LFLAGS = -lgit2 -lz
|
|
||||||
APPS = init log
|
|
||||||
|
|
||||||
all: $(APPS)
|
|
||||||
|
|
||||||
% : %.c
|
|
||||||
$(CC) -o $@ common.c $(CFLAGS) $< $(LFLAGS)
|
|
||||||
|
|
@ -1,237 +0,0 @@
|
|||||||
/*
|
|
||||||
* Utilities library for libgit2 examples
|
|
||||||
*
|
|
||||||
* Written by the libgit2 contributors
|
|
||||||
*
|
|
||||||
* To the extent possible under law, the author(s) have dedicated all copyright
|
|
||||||
* and related and neighboring rights to this software to the public domain
|
|
||||||
* worldwide. This software is distributed without any warranty.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the CC0 Public Domain Dedication along
|
|
||||||
* with this software. If not, see
|
|
||||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
void check_lg2(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fatal(const char *message, const char *extra)
|
|
||||||
{
|
|
||||||
if (extra)
|
|
||||||
fprintf(stderr, "%s %s\n", message, extra);
|
|
||||||
else
|
|
||||||
fprintf(stderr, "%s\n", message);
|
|
||||||
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t is_prefixed(const char *str, const char *pfx)
|
|
||||||
{
|
|
||||||
size_t len = strlen(pfx);
|
|
||||||
return strncmp(str, pfx, len) ? 0 : len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int optional_str_arg(
|
|
||||||
const char **out, struct args_info *args, const char *opt, const char *def)
|
|
||||||
{
|
|
||||||
const char *found = args->argv[args->pos];
|
|
||||||
size_t len = is_prefixed(found, opt);
|
|
||||||
|
|
||||||
if (!len)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!found[len]) {
|
|
||||||
if (args->pos + 1 == args->argc) {
|
|
||||||
*out = def;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
args->pos += 1;
|
|
||||||
*out = args->argv[args->pos];
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found[len] == '=') {
|
|
||||||
*out = found + len + 1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int match_str_arg(
|
|
||||||
const char **out, struct args_info *args, const char *opt)
|
|
||||||
{
|
|
||||||
const char *found = args->argv[args->pos];
|
|
||||||
size_t len = is_prefixed(found, opt);
|
|
||||||
|
|
||||||
if (!len)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!found[len]) {
|
|
||||||
if (args->pos + 1 == args->argc)
|
|
||||||
fatal("expected value following argument", opt);
|
|
||||||
args->pos += 1;
|
|
||||||
*out = args->argv[args->pos];
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found[len] == '=') {
|
|
||||||
*out = found + len + 1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *match_numeric_arg(struct args_info *args, const char *opt)
|
|
||||||
{
|
|
||||||
const char *found = args->argv[args->pos];
|
|
||||||
size_t len = is_prefixed(found, opt);
|
|
||||||
|
|
||||||
if (!len)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (!found[len]) {
|
|
||||||
if (args->pos + 1 == args->argc)
|
|
||||||
fatal("expected numeric value following argument", opt);
|
|
||||||
args->pos += 1;
|
|
||||||
found = args->argv[args->pos];
|
|
||||||
} else {
|
|
||||||
found = found + len;
|
|
||||||
if (*found == '=')
|
|
||||||
found++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
int match_uint16_arg(
|
|
||||||
uint16_t *out, struct args_info *args, const char *opt)
|
|
||||||
{
|
|
||||||
const char *found = match_numeric_arg(args, opt);
|
|
||||||
uint16_t val;
|
|
||||||
char *endptr = NULL;
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
val = (uint16_t)strtoul(found, &endptr, 0);
|
|
||||||
if (!endptr || *endptr != '\0')
|
|
||||||
fatal("expected number after argument", opt);
|
|
||||||
|
|
||||||
if (out)
|
|
||||||
*out = val;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int match_uint32_arg(
|
|
||||||
uint32_t *out, struct args_info *args, const char *opt)
|
|
||||||
{
|
|
||||||
const char *found = match_numeric_arg(args, opt);
|
|
||||||
uint16_t val;
|
|
||||||
char *endptr = NULL;
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
val = (uint32_t)strtoul(found, &endptr, 0);
|
|
||||||
if (!endptr || *endptr != '\0')
|
|
||||||
fatal("expected number after argument", opt);
|
|
||||||
|
|
||||||
if (out)
|
|
||||||
*out = val;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int match_int_internal(
|
|
||||||
int *out, const char *str, int allow_negative, const char *opt)
|
|
||||||
{
|
|
||||||
char *endptr = NULL;
|
|
||||||
int val = (int)strtol(str, &endptr, 10);
|
|
||||||
|
|
||||||
if (!endptr || *endptr != '\0')
|
|
||||||
fatal("expected number", opt);
|
|
||||||
else if (val < 0 && !allow_negative)
|
|
||||||
fatal("negative values are not allowed", opt);
|
|
||||||
|
|
||||||
if (out)
|
|
||||||
*out = val;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_integer(int *out, const char *str, int allow_negative)
|
|
||||||
{
|
|
||||||
return match_int_internal(out, str, allow_negative, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int match_int_arg(
|
|
||||||
int *out, struct args_info *args, const char *opt, int allow_negative)
|
|
||||||
{
|
|
||||||
const char *found = match_numeric_arg(args, opt);
|
|
||||||
if (!found)
|
|
||||||
return 0;
|
|
||||||
return match_int_internal(out, found, allow_negative, opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
int diff_output(
|
|
||||||
const git_diff_delta *d,
|
|
||||||
const git_diff_hunk *h,
|
|
||||||
const git_diff_line *l,
|
|
||||||
void *p)
|
|
||||||
{
|
|
||||||
FILE *fp = (FILE*)p;
|
|
||||||
|
|
||||||
(void)d; (void)h;
|
|
||||||
|
|
||||||
if (!fp)
|
|
||||||
fp = stdout;
|
|
||||||
|
|
||||||
if (l->origin == GIT_DIFF_LINE_CONTEXT ||
|
|
||||||
l->origin == GIT_DIFF_LINE_ADDITION ||
|
|
||||||
l->origin == GIT_DIFF_LINE_DELETION)
|
|
||||||
fputc(l->origin, fp);
|
|
||||||
|
|
||||||
fwrite(l->content, 1, l->content_len, fp);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void treeish_to_tree(
|
|
||||||
git_tree **out, git_repository *repo, const char *treeish)
|
|
||||||
{
|
|
||||||
git_object *obj = NULL;
|
|
||||||
|
|
||||||
check_lg2(
|
|
||||||
git_revparse_single(&obj, repo, treeish),
|
|
||||||
"looking up object", treeish);
|
|
||||||
|
|
||||||
check_lg2(
|
|
||||||
git_object_peel((git_object **)out, obj, GIT_OBJ_TREE),
|
|
||||||
"resolving object to tree", treeish);
|
|
||||||
|
|
||||||
git_object_free(obj);
|
|
||||||
}
|
|
||||||
|
|
@ -1,105 +0,0 @@
|
|||||||
/*
|
|
||||||
* Utilities library for libgit2 examples
|
|
||||||
*
|
|
||||||
* Written by the libgit2 contributors
|
|
||||||
*
|
|
||||||
* To the extent possible under law, the author(s) have dedicated all copyright
|
|
||||||
* and related and neighboring rights to this software to the public domain
|
|
||||||
* worldwide. This software is distributed without any warranty.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the CC0 Public Domain Dedication along
|
|
||||||
* with this software. If not, see
|
|
||||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <git2.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check libgit2 error code, printing error to stderr on failure and
|
|
||||||
* exiting the program.
|
|
||||||
*/
|
|
||||||
extern void check_lg2(int error, const char *message, const char *extra);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exit the program, printing error to stderr
|
|
||||||
*/
|
|
||||||
extern void fatal(const char *message, const char *extra);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a string has the given prefix. Returns 0 if not prefixed
|
|
||||||
* or the length of the prefix if it is.
|
|
||||||
*/
|
|
||||||
extern size_t is_prefixed(const char *str, const char *pfx);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Match an integer string, returning 1 if matched, 0 if not.
|
|
||||||
*/
|
|
||||||
extern int is_integer(int *out, const char *str, int allow_negative);
|
|
||||||
|
|
||||||
struct args_info {
|
|
||||||
int argc;
|
|
||||||
char **argv;
|
|
||||||
int pos;
|
|
||||||
};
|
|
||||||
#define ARGS_INFO_INIT { argc, argv, 0 }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check current `args` entry against `opt` string. If it matches
|
|
||||||
* exactly, take the next arg as a string; if it matches as a prefix with
|
|
||||||
* an equal sign, take the remainder as a string; if value not supplied,
|
|
||||||
* default value `def` will be given. otherwise return 0.
|
|
||||||
*/
|
|
||||||
extern int optional_str_arg(
|
|
||||||
const char **out, struct args_info *args, const char *opt, const char *def);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check current `args` entry against `opt` string. If it matches
|
|
||||||
* exactly, take the next arg as a string; if it matches as a prefix with
|
|
||||||
* an equal sign, take the remainder as a string; otherwise return 0.
|
|
||||||
*/
|
|
||||||
extern int match_str_arg(
|
|
||||||
const char **out, struct args_info *args, const char *opt);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check current `args` entry against `opt` string parsing as uint16. If
|
|
||||||
* `opt` matches exactly, take the next arg as a uint16_t value; if `opt`
|
|
||||||
* is a prefix (equal sign optional), take the remainder of the arg as a
|
|
||||||
* uint16_t value; otherwise return 0.
|
|
||||||
*/
|
|
||||||
extern int match_uint16_arg(
|
|
||||||
uint16_t *out, struct args_info *args, const char *opt);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check current `args` entry against `opt` string parsing as uint32. If
|
|
||||||
* `opt` matches exactly, take the next arg as a uint16_t value; if `opt`
|
|
||||||
* is a prefix (equal sign optional), take the remainder of the arg as a
|
|
||||||
* uint32_t value; otherwise return 0.
|
|
||||||
*/
|
|
||||||
extern int match_uint32_arg(
|
|
||||||
uint32_t *out, struct args_info *args, const char *opt);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check current `args` entry against `opt` string parsing as int. If
|
|
||||||
* `opt` matches exactly, take the next arg as an int value; if it matches
|
|
||||||
* as a prefix (equal sign optional), take the remainder of the arg as a
|
|
||||||
* int value; otherwise return 0.
|
|
||||||
*/
|
|
||||||
extern int match_int_arg(
|
|
||||||
int *out, struct args_info *args, const char *opt, int allow_negative);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic output function for plain text diff output
|
|
||||||
* Pass `FILE*` such as `stdout` or `stderr` as payload (or NULL == `stdout`)
|
|
||||||
*/
|
|
||||||
extern int diff_output(
|
|
||||||
const git_diff_delta*, const git_diff_hunk*, const git_diff_line*, void*);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a treeish argument to an actual tree; this will call check_lg2
|
|
||||||
* and exit the program if `treeish` cannot be resolved to a tree
|
|
||||||
*/
|
|
||||||
extern void treeish_to_tree(
|
|
||||||
git_tree **out, git_repository *repo, const char *treeish);
|
|
@ -1,253 +0,0 @@
|
|||||||
/*
|
|
||||||
* libgit2 "init" example - shows how to initialize a new repo
|
|
||||||
*
|
|
||||||
* Written by the libgit2 contributors
|
|
||||||
*
|
|
||||||
* To the extent possible under law, the author(s) have dedicated all copyright
|
|
||||||
* and related and neighboring rights to this software to the public domain
|
|
||||||
* worldwide. This software is distributed without any warranty.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the CC0 Public Domain Dedication along
|
|
||||||
* with this software. If not, see
|
|
||||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a sample program that is similar to "git init". See the
|
|
||||||
* documentation for that (try "git help init") to understand what this
|
|
||||||
* program is emulating.
|
|
||||||
*
|
|
||||||
* This demonstrates using the libgit2 APIs to initialize a new repository.
|
|
||||||
*
|
|
||||||
* This also contains a special additional option that regular "git init"
|
|
||||||
* does not support which is "--initial-commit" to make a first empty commit.
|
|
||||||
* That is demonstrated in the "create_initial_commit" helper function.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** Forward declarations of helpers */
|
|
||||||
struct opts {
|
|
||||||
int no_options;
|
|
||||||
int quiet;
|
|
||||||
int bare;
|
|
||||||
int initial_commit;
|
|
||||||
uint32_t shared;
|
|
||||||
const char *template;
|
|
||||||
const char *gitdir;
|
|
||||||
const char *dir;
|
|
||||||
};
|
|
||||||
static void create_initial_commit(git_repository *repo);
|
|
||||||
static void parse_opts(struct opts *o, int argc, char *argv[]);
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
git_repository *repo = NULL;
|
|
||||||
struct opts o = { 1, 0, 0, 0, GIT_REPOSITORY_INIT_SHARED_UMASK, 0, 0, 0 };
|
|
||||||
|
|
||||||
git_libgit2_init();
|
|
||||||
|
|
||||||
parse_opts(&o, argc, argv);
|
|
||||||
|
|
||||||
/* Initialize repository. */
|
|
||||||
|
|
||||||
if (o.no_options) {
|
|
||||||
/**
|
|
||||||
* No options were specified, so let's demonstrate the default
|
|
||||||
* simple case of git_repository_init() API usage...
|
|
||||||
*/
|
|
||||||
check_lg2(git_repository_init(&repo, o.dir, 0),
|
|
||||||
"Could not initialize repository", NULL);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/**
|
|
||||||
* Some command line options were specified, so we'll use the
|
|
||||||
* extended init API to handle them
|
|
||||||
*/
|
|
||||||
git_repository_init_options initopts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
|
|
||||||
initopts.flags = GIT_REPOSITORY_INIT_MKPATH;
|
|
||||||
|
|
||||||
if (o.bare)
|
|
||||||
initopts.flags |= GIT_REPOSITORY_INIT_BARE;
|
|
||||||
|
|
||||||
if (o.template) {
|
|
||||||
initopts.flags |= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
|
|
||||||
initopts.template_path = o.template;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (o.gitdir) {
|
|
||||||
/**
|
|
||||||
* If you specified a separate git directory, then initialize
|
|
||||||
* the repository at that path and use the second path as the
|
|
||||||
* working directory of the repository (with a git-link file)
|
|
||||||
*/
|
|
||||||
initopts.workdir_path = o.dir;
|
|
||||||
o.dir = o.gitdir;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (o.shared != 0)
|
|
||||||
initopts.mode = o.shared;
|
|
||||||
|
|
||||||
check_lg2(git_repository_init_ext(&repo, o.dir, &initopts),
|
|
||||||
"Could not initialize repository", NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Print a message to stdout like "git init" does. */
|
|
||||||
|
|
||||||
if (!o.quiet) {
|
|
||||||
if (o.bare || o.gitdir)
|
|
||||||
o.dir = git_repository_path(repo);
|
|
||||||
else
|
|
||||||
o.dir = git_repository_workdir(repo);
|
|
||||||
|
|
||||||
printf("Initialized empty Git repository in %s\n", o.dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* As an extension to the basic "git init" command, this example
|
|
||||||
* gives the option to create an empty initial commit. This is
|
|
||||||
* mostly to demonstrate what it takes to do that, but also some
|
|
||||||
* people like to have that empty base commit in their repo.
|
|
||||||
*/
|
|
||||||
if (o.initial_commit) {
|
|
||||||
create_initial_commit(repo);
|
|
||||||
printf("Created empty initial commit\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
git_repository_free(repo);
|
|
||||||
git_libgit2_shutdown();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unlike regular "git init", this example shows how to create an initial
|
|
||||||
* empty commit in the repository. This is the helper function that does
|
|
||||||
* that.
|
|
||||||
*/
|
|
||||||
static void create_initial_commit(git_repository *repo)
|
|
||||||
{
|
|
||||||
git_signature *sig;
|
|
||||||
git_index *index;
|
|
||||||
git_oid tree_id, commit_id;
|
|
||||||
git_tree *tree;
|
|
||||||
|
|
||||||
/** First use the config to initialize a commit signature for the user. */
|
|
||||||
|
|
||||||
if (git_signature_default(&sig, repo) < 0)
|
|
||||||
fatal("Unable to create a commit signature.",
|
|
||||||
"Perhaps 'user.name' and 'user.email' are not set");
|
|
||||||
|
|
||||||
/* Now let's create an empty tree for this commit */
|
|
||||||
|
|
||||||
if (git_repository_index(&index, repo) < 0)
|
|
||||||
fatal("Could not open repository index", NULL);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Outside of this example, you could call git_index_add_bypath()
|
|
||||||
* here to put actual files into the index. For our purposes, we'll
|
|
||||||
* leave it empty for now.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (git_index_write_tree(&tree_id, index) < 0)
|
|
||||||
fatal("Unable to write initial tree from index", NULL);
|
|
||||||
|
|
||||||
git_index_free(index);
|
|
||||||
|
|
||||||
if (git_tree_lookup(&tree, repo, &tree_id) < 0)
|
|
||||||
fatal("Could not look up initial tree", NULL);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ready to create the initial commit.
|
|
||||||
*
|
|
||||||
* Normally creating a commit would involve looking up the current
|
|
||||||
* HEAD commit and making that be the parent of the initial commit,
|
|
||||||
* but here this is the first commit so there will be no parent.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (git_commit_create_v(
|
|
||||||
&commit_id, repo, "HEAD", sig, sig,
|
|
||||||
NULL, "Initial commit", tree, 0) < 0)
|
|
||||||
fatal("Could not create the initial commit", NULL);
|
|
||||||
|
|
||||||
/** Clean up so we don't leak memory. */
|
|
||||||
|
|
||||||
git_tree_free(tree);
|
|
||||||
git_signature_free(sig);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usage(const char *error, const char *arg)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "error: %s '%s'\n", error, arg);
|
|
||||||
fprintf(stderr,
|
|
||||||
"usage: init [-q | --quiet] [--bare] [--template=<dir>]\n"
|
|
||||||
" [--shared[=perms]] [--initial-commit]\n"
|
|
||||||
" [--separate-git-dir] <directory>\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Parse the tail of the --shared= argument. */
|
|
||||||
static uint32_t parse_shared(const char *shared)
|
|
||||||
{
|
|
||||||
if (!strcmp(shared, "false") || !strcmp(shared, "umask"))
|
|
||||||
return GIT_REPOSITORY_INIT_SHARED_UMASK;
|
|
||||||
|
|
||||||
else if (!strcmp(shared, "true") || !strcmp(shared, "group"))
|
|
||||||
return GIT_REPOSITORY_INIT_SHARED_GROUP;
|
|
||||||
|
|
||||||
else if (!strcmp(shared, "all") || !strcmp(shared, "world") ||
|
|
||||||
!strcmp(shared, "everybody"))
|
|
||||||
return GIT_REPOSITORY_INIT_SHARED_ALL;
|
|
||||||
|
|
||||||
else if (shared[0] == '0') {
|
|
||||||
long val;
|
|
||||||
char *end = NULL;
|
|
||||||
val = strtol(shared + 1, &end, 8);
|
|
||||||
if (end == shared + 1 || *end != 0)
|
|
||||||
usage("invalid octal value for --shared", shared);
|
|
||||||
return (uint32_t)val;
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
usage("unknown value for --shared", shared);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void parse_opts(struct opts *o, int argc, char *argv[])
|
|
||||||
{
|
|
||||||
struct args_info args = ARGS_INFO_INIT;
|
|
||||||
const char *sharedarg;
|
|
||||||
|
|
||||||
/** Process arguments. */
|
|
||||||
|
|
||||||
for (args.pos = 1; args.pos < argc; ++args.pos) {
|
|
||||||
char *a = argv[args.pos];
|
|
||||||
|
|
||||||
if (a[0] == '-')
|
|
||||||
o->no_options = 0;
|
|
||||||
|
|
||||||
if (a[0] != '-') {
|
|
||||||
if (o->dir != NULL)
|
|
||||||
usage("extra argument", a);
|
|
||||||
o->dir = a;
|
|
||||||
}
|
|
||||||
else if (!strcmp(a, "-q") || !strcmp(a, "--quiet"))
|
|
||||||
o->quiet = 1;
|
|
||||||
else if (!strcmp(a, "--bare"))
|
|
||||||
o->bare = 1;
|
|
||||||
else if (!strcmp(a, "--shared"))
|
|
||||||
o->shared = GIT_REPOSITORY_INIT_SHARED_GROUP;
|
|
||||||
else if (!strcmp(a, "--initial-commit"))
|
|
||||||
o->initial_commit = 1;
|
|
||||||
else if (match_str_arg(&sharedarg, &args, "--shared"))
|
|
||||||
o->shared = parse_shared(sharedarg);
|
|
||||||
else if (!match_str_arg(&o->template, &args, "--template") ||
|
|
||||||
!match_str_arg(&o->gitdir, &args, "--separate-git-dir"))
|
|
||||||
usage("unknown option", a);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!o->dir)
|
|
||||||
usage("must specify directory to init", NULL);
|
|
||||||
}
|
|
@ -1,479 +0,0 @@
|
|||||||
/*
|
|
||||||
* libgit2 "log" example - shows how to walk history and get commit info
|
|
||||||
*
|
|
||||||
* Written by the libgit2 contributors
|
|
||||||
*
|
|
||||||
* To the extent possible under law, the author(s) have dedicated all copyright
|
|
||||||
* and related and neighboring rights to this software to the public domain
|
|
||||||
* worldwide. This software is distributed without any warranty.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the CC0 Public Domain Dedication along
|
|
||||||
* with this software. If not, see
|
|
||||||
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This example demonstrates the libgit2 rev walker APIs to roughly
|
|
||||||
* simulate the output of `git log` and a few of command line arguments.
|
|
||||||
* `git log` has many many options and this only shows a few of them.
|
|
||||||
*
|
|
||||||
* This does not have:
|
|
||||||
*
|
|
||||||
* - Robust error handling
|
|
||||||
* - Colorized or paginated output formatting
|
|
||||||
* - Most of the `git log` options
|
|
||||||
*
|
|
||||||
* This does have:
|
|
||||||
*
|
|
||||||
* - Examples of translating command line arguments to equivalent libgit2
|
|
||||||
* revwalker configuration calls
|
|
||||||
* - Simplified options to apply pathspec limits and to show basic diffs
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** log_state represents walker being configured while handling options */
|
|
||||||
struct log_state {
|
|
||||||
git_repository *repo;
|
|
||||||
const char *repodir;
|
|
||||||
git_revwalk *walker;
|
|
||||||
int hide;
|
|
||||||
int sorting;
|
|
||||||
int revisions;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** utility functions that are called to configure the walker */
|
|
||||||
static void set_sorting(struct log_state *s, unsigned int sort_mode);
|
|
||||||
static void push_rev(struct log_state *s, git_object *obj, int hide);
|
|
||||||
static int add_revision(struct log_state *s, const char *revstr);
|
|
||||||
|
|
||||||
/** log_options holds other command line options that affect log output */
|
|
||||||
struct log_options {
|
|
||||||
int show_diff;
|
|
||||||
int skip, limit;
|
|
||||||
int min_parents, max_parents;
|
|
||||||
git_time_t before;
|
|
||||||
git_time_t after;
|
|
||||||
const char *author;
|
|
||||||
const char *committer;
|
|
||||||
const char *grep;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** utility functions that parse options and help with log output */
|
|
||||||
static int parse_options(
|
|
||||||
struct log_state *s, struct log_options *opt, int argc, char **argv);
|
|
||||||
static void print_time(const git_time *intime, const char *prefix);
|
|
||||||
static void print_commit(git_commit *commit);
|
|
||||||
static int match_with_parent(git_commit *commit, int i, git_diff_options *);
|
|
||||||
|
|
||||||
/** utility functions for filtering */
|
|
||||||
static int signature_matches(const git_signature *sig, const char *filter);
|
|
||||||
static int log_message_matches(const git_commit *commit, const char *filter);
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int i, count = 0, printed = 0, parents, last_arg;
|
|
||||||
struct log_state s;
|
|
||||||
struct log_options opt;
|
|
||||||
git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
|
|
||||||
git_oid oid;
|
|
||||||
git_commit *commit = NULL;
|
|
||||||
git_pathspec *ps = NULL;
|
|
||||||
|
|
||||||
git_libgit2_init();
|
|
||||||
|
|
||||||
/** Parse arguments and set up revwalker. */
|
|
||||||
|
|
||||||
last_arg = parse_options(&s, &opt, argc, argv);
|
|
||||||
|
|
||||||
diffopts.pathspec.strings = &argv[last_arg];
|
|
||||||
diffopts.pathspec.count = argc - last_arg;
|
|
||||||
if (diffopts.pathspec.count > 0)
|
|
||||||
check_lg2(git_pathspec_new(&ps, &diffopts.pathspec),
|
|
||||||
"Building pathspec", NULL);
|
|
||||||
|
|
||||||
if (!s.revisions)
|
|
||||||
add_revision(&s, NULL);
|
|
||||||
|
|
||||||
/** Use the revwalker to traverse the history. */
|
|
||||||
|
|
||||||
printed = count = 0;
|
|
||||||
|
|
||||||
for (; !git_revwalk_next(&oid, s.walker); git_commit_free(commit)) {
|
|
||||||
check_lg2(git_commit_lookup(&commit, s.repo, &oid),
|
|
||||||
"Failed to look up commit", NULL);
|
|
||||||
|
|
||||||
parents = (int)git_commit_parentcount(commit);
|
|
||||||
if (parents < opt.min_parents)
|
|
||||||
continue;
|
|
||||||
if (opt.max_parents > 0 && parents > opt.max_parents)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (diffopts.pathspec.count > 0) {
|
|
||||||
int unmatched = parents;
|
|
||||||
|
|
||||||
if (parents == 0) {
|
|
||||||
git_tree *tree;
|
|
||||||
check_lg2(git_commit_tree(&tree, commit), "Get tree", NULL);
|
|
||||||
if (git_pathspec_match_tree(
|
|
||||||
NULL, tree, GIT_PATHSPEC_NO_MATCH_ERROR, ps) != 0)
|
|
||||||
unmatched = 1;
|
|
||||||
git_tree_free(tree);
|
|
||||||
} else if (parents == 1) {
|
|
||||||
unmatched = match_with_parent(commit, 0, &diffopts) ? 0 : 1;
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < parents; ++i) {
|
|
||||||
if (match_with_parent(commit, i, &diffopts))
|
|
||||||
unmatched--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unmatched > 0)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!signature_matches(git_commit_author(commit), opt.author))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!signature_matches(git_commit_committer(commit), opt.committer))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!log_message_matches(commit, opt.grep))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (count++ < opt.skip)
|
|
||||||
continue;
|
|
||||||
if (opt.limit != -1 && printed++ >= opt.limit) {
|
|
||||||
git_commit_free(commit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
print_commit(commit);
|
|
||||||
|
|
||||||
if (opt.show_diff) {
|
|
||||||
git_tree *a = NULL, *b = NULL;
|
|
||||||
git_diff *diff = NULL;
|
|
||||||
|
|
||||||
if (parents > 1)
|
|
||||||
continue;
|
|
||||||
check_lg2(git_commit_tree(&b, commit), "Get tree", NULL);
|
|
||||||
if (parents == 1) {
|
|
||||||
git_commit *parent;
|
|
||||||
check_lg2(git_commit_parent(&parent, commit, 0), "Get parent", NULL);
|
|
||||||
check_lg2(git_commit_tree(&a, parent), "Tree for parent", NULL);
|
|
||||||
git_commit_free(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
check_lg2(git_diff_tree_to_tree(
|
|
||||||
&diff, git_commit_owner(commit), a, b, &diffopts),
|
|
||||||
"Diff commit with parent", NULL);
|
|
||||||
check_lg2(
|
|
||||||
git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, diff_output, NULL),
|
|
||||||
"Displaying diff", NULL);
|
|
||||||
|
|
||||||
git_diff_free(diff);
|
|
||||||
git_tree_free(a);
|
|
||||||
git_tree_free(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
git_pathspec_free(ps);
|
|
||||||
git_revwalk_free(s.walker);
|
|
||||||
git_repository_free(s.repo);
|
|
||||||
git_libgit2_shutdown();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Determine if the given git_signature does not contain the filter text. */
|
|
||||||
static int signature_matches(const git_signature *sig, const char *filter) {
|
|
||||||
if (filter == NULL)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (sig != NULL &&
|
|
||||||
(strstr(sig->name, filter) != NULL ||
|
|
||||||
strstr(sig->email, filter) != NULL))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int log_message_matches(const git_commit *commit, const char *filter) {
|
|
||||||
const char *message = NULL;
|
|
||||||
|
|
||||||
if (filter == NULL)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if ((message = git_commit_message(commit)) != NULL &&
|
|
||||||
strstr(message, filter) != NULL)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Push object (for hide or show) onto revwalker. */
|
|
||||||
static void push_rev(struct log_state *s, git_object *obj, int hide)
|
|
||||||
{
|
|
||||||
hide = s->hide ^ hide;
|
|
||||||
|
|
||||||
/** Create revwalker on demand if it doesn't already exist. */
|
|
||||||
if (!s->walker) {
|
|
||||||
check_lg2(git_revwalk_new(&s->walker, s->repo),
|
|
||||||
"Could not create revision walker", NULL);
|
|
||||||
git_revwalk_sorting(s->walker, s->sorting);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!obj)
|
|
||||||
check_lg2(git_revwalk_push_head(s->walker),
|
|
||||||
"Could not find repository HEAD", NULL);
|
|
||||||
else if (hide)
|
|
||||||
check_lg2(git_revwalk_hide(s->walker, git_object_id(obj)),
|
|
||||||
"Reference does not refer to a commit", NULL);
|
|
||||||
else
|
|
||||||
check_lg2(git_revwalk_push(s->walker, git_object_id(obj)),
|
|
||||||
"Reference does not refer to a commit", NULL);
|
|
||||||
|
|
||||||
git_object_free(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Parse revision string and add revs to walker. */
|
|
||||||
static int add_revision(struct log_state *s, const char *revstr)
|
|
||||||
{
|
|
||||||
git_revspec revs;
|
|
||||||
int hide = 0;
|
|
||||||
|
|
||||||
/** Open repo on demand if it isn't already open. */
|
|
||||||
if (!s->repo) {
|
|
||||||
if (!s->repodir) s->repodir = ".";
|
|
||||||
check_lg2(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
|
|
||||||
"Could not open repository", s->repodir);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!revstr) {
|
|
||||||
push_rev(s, NULL, hide);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*revstr == '^') {
|
|
||||||
revs.flags = GIT_REVPARSE_SINGLE;
|
|
||||||
hide = !hide;
|
|
||||||
|
|
||||||
if (git_revparse_single(&revs.from, s->repo, revstr + 1) < 0)
|
|
||||||
return -1;
|
|
||||||
} else if (git_revparse(&revs, s->repo, revstr) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if ((revs.flags & GIT_REVPARSE_SINGLE) != 0)
|
|
||||||
push_rev(s, revs.from, hide);
|
|
||||||
else {
|
|
||||||
push_rev(s, revs.to, hide);
|
|
||||||
|
|
||||||
if ((revs.flags & GIT_REVPARSE_MERGE_BASE) != 0) {
|
|
||||||
git_oid base;
|
|
||||||
check_lg2(git_merge_base(&base, s->repo,
|
|
||||||
git_object_id(revs.from), git_object_id(revs.to)),
|
|
||||||
"Could not find merge base", revstr);
|
|
||||||
check_lg2(
|
|
||||||
git_object_lookup(&revs.to, s->repo, &base, GIT_OBJ_COMMIT),
|
|
||||||
"Could not find merge base commit", NULL);
|
|
||||||
|
|
||||||
push_rev(s, revs.to, hide);
|
|
||||||
}
|
|
||||||
|
|
||||||
push_rev(s, revs.from, !hide);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Update revwalker with sorting mode. */
|
|
||||||
static void set_sorting(struct log_state *s, unsigned int sort_mode)
|
|
||||||
{
|
|
||||||
/** Open repo on demand if it isn't already open. */
|
|
||||||
if (!s->repo) {
|
|
||||||
if (!s->repodir) s->repodir = ".";
|
|
||||||
check_lg2(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
|
|
||||||
"Could not open repository", s->repodir);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Create revwalker on demand if it doesn't already exist. */
|
|
||||||
if (!s->walker)
|
|
||||||
check_lg2(git_revwalk_new(&s->walker, s->repo),
|
|
||||||
"Could not create revision walker", NULL);
|
|
||||||
|
|
||||||
if (sort_mode == GIT_SORT_REVERSE)
|
|
||||||
s->sorting = s->sorting ^ GIT_SORT_REVERSE;
|
|
||||||
else
|
|
||||||
s->sorting = sort_mode | (s->sorting & GIT_SORT_REVERSE);
|
|
||||||
|
|
||||||
git_revwalk_sorting(s->walker, s->sorting);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Helper to format a git_time value like Git. */
|
|
||||||
static void print_time(const git_time *intime, const char *prefix)
|
|
||||||
{
|
|
||||||
char sign, out[32];
|
|
||||||
struct tm *intm;
|
|
||||||
int offset, hours, minutes;
|
|
||||||
time_t t;
|
|
||||||
|
|
||||||
offset = intime->offset;
|
|
||||||
if (offset < 0) {
|
|
||||||
sign = '-';
|
|
||||||
offset = -offset;
|
|
||||||
} else {
|
|
||||||
sign = '+';
|
|
||||||
}
|
|
||||||
|
|
||||||
hours = offset / 60;
|
|
||||||
minutes = offset % 60;
|
|
||||||
|
|
||||||
t = (time_t)intime->time + (intime->offset * 60);
|
|
||||||
|
|
||||||
intm = gmtime(&t);
|
|
||||||
strftime(out, sizeof(out), "%a %b %e %T %Y", intm);
|
|
||||||
|
|
||||||
printf("%s%s %c%02d%02d\n", prefix, out, sign, hours, minutes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Helper to print a commit object. */
|
|
||||||
static void print_commit(git_commit *commit)
|
|
||||||
{
|
|
||||||
char buf[GIT_OID_HEXSZ + 1];
|
|
||||||
int i, count;
|
|
||||||
const git_signature *sig;
|
|
||||||
const char *scan, *eol;
|
|
||||||
|
|
||||||
git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
|
|
||||||
printf("commit %s\n", buf);
|
|
||||||
|
|
||||||
if ((count = (int)git_commit_parentcount(commit)) > 1) {
|
|
||||||
printf("Merge:");
|
|
||||||
for (i = 0; i < count; ++i) {
|
|
||||||
git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
|
|
||||||
printf(" %s", buf);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((sig = git_commit_author(commit)) != NULL) {
|
|
||||||
printf("Author: %s <%s>\n", sig->name, sig->email);
|
|
||||||
print_time(&sig->when, "Date: ");
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
for (scan = git_commit_message(commit); scan && *scan; ) {
|
|
||||||
for (eol = scan; *eol && *eol != '\n'; ++eol) /* find eol */;
|
|
||||||
|
|
||||||
printf(" %.*s\n", (int)(eol - scan), scan);
|
|
||||||
scan = *eol ? eol + 1 : NULL;
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Helper to find how many files in a commit changed from its nth parent. */
|
|
||||||
static int match_with_parent(git_commit *commit, int i, git_diff_options *opts)
|
|
||||||
{
|
|
||||||
git_commit *parent;
|
|
||||||
git_tree *a, *b;
|
|
||||||
git_diff *diff;
|
|
||||||
int ndeltas;
|
|
||||||
|
|
||||||
check_lg2(
|
|
||||||
git_commit_parent(&parent, commit, (size_t)i), "Get parent", NULL);
|
|
||||||
check_lg2(git_commit_tree(&a, parent), "Tree for parent", NULL);
|
|
||||||
check_lg2(git_commit_tree(&b, commit), "Tree for commit", NULL);
|
|
||||||
check_lg2(
|
|
||||||
git_diff_tree_to_tree(&diff, git_commit_owner(commit), a, b, opts),
|
|
||||||
"Checking diff between parent and commit", NULL);
|
|
||||||
|
|
||||||
ndeltas = (int)git_diff_num_deltas(diff);
|
|
||||||
|
|
||||||
git_diff_free(diff);
|
|
||||||
git_tree_free(a);
|
|
||||||
git_tree_free(b);
|
|
||||||
git_commit_free(parent);
|
|
||||||
|
|
||||||
return ndeltas > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Print a usage message for the program. */
|
|
||||||
static void usage(const char *message, const char *arg)
|
|
||||||
{
|
|
||||||
if (message && arg)
|
|
||||||
fprintf(stderr, "%s: %s\n", message, arg);
|
|
||||||
else if (message)
|
|
||||||
fprintf(stderr, "%s\n", message);
|
|
||||||
fprintf(stderr, "usage: log [<options>]\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Parse some log command line options. */
|
|
||||||
static int parse_options(
|
|
||||||
struct log_state *s, struct log_options *opt, int argc, char **argv)
|
|
||||||
{
|
|
||||||
struct args_info args = ARGS_INFO_INIT;
|
|
||||||
|
|
||||||
memset(s, 0, sizeof(*s));
|
|
||||||
s->sorting = GIT_SORT_TIME;
|
|
||||||
|
|
||||||
memset(opt, 0, sizeof(*opt));
|
|
||||||
opt->max_parents = -1;
|
|
||||||
opt->limit = -1;
|
|
||||||
|
|
||||||
for (args.pos = 1; args.pos < argc; ++args.pos) {
|
|
||||||
const char *a = argv[args.pos];
|
|
||||||
|
|
||||||
if (a[0] != '-') {
|
|
||||||
if (!add_revision(s, a))
|
|
||||||
s->revisions++;
|
|
||||||
else
|
|
||||||
/** Try failed revision parse as filename. */
|
|
||||||
break;
|
|
||||||
} else if (!strcmp(a, "--")) {
|
|
||||||
++args.pos;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (!strcmp(a, "--date-order"))
|
|
||||||
set_sorting(s, GIT_SORT_TIME);
|
|
||||||
else if (!strcmp(a, "--topo-order"))
|
|
||||||
set_sorting(s, GIT_SORT_TOPOLOGICAL);
|
|
||||||
else if (!strcmp(a, "--reverse"))
|
|
||||||
set_sorting(s, GIT_SORT_REVERSE);
|
|
||||||
else if (match_str_arg(&opt->author, &args, "--author"))
|
|
||||||
/** Found valid --author */;
|
|
||||||
else if (match_str_arg(&opt->committer, &args, "--committer"))
|
|
||||||
/** Found valid --committer */;
|
|
||||||
else if (match_str_arg(&opt->grep, &args, "--grep"))
|
|
||||||
/** Found valid --grep */;
|
|
||||||
else if (match_str_arg(&s->repodir, &args, "--git-dir"))
|
|
||||||
/** Found git-dir. */;
|
|
||||||
else if (match_int_arg(&opt->skip, &args, "--skip", 0))
|
|
||||||
/** Found valid --skip. */;
|
|
||||||
else if (match_int_arg(&opt->limit, &args, "--max-count", 0))
|
|
||||||
/** Found valid --max-count. */;
|
|
||||||
else if (a[1] >= '0' && a[1] <= '9')
|
|
||||||
is_integer(&opt->limit, a + 1, 0);
|
|
||||||
else if (match_int_arg(&opt->limit, &args, "-n", 0))
|
|
||||||
/** Found valid -n. */;
|
|
||||||
else if (!strcmp(a, "--merges"))
|
|
||||||
opt->min_parents = 2;
|
|
||||||
else if (!strcmp(a, "--no-merges"))
|
|
||||||
opt->max_parents = 1;
|
|
||||||
else if (!strcmp(a, "--no-min-parents"))
|
|
||||||
opt->min_parents = 0;
|
|
||||||
else if (!strcmp(a, "--no-max-parents"))
|
|
||||||
opt->max_parents = -1;
|
|
||||||
else if (match_int_arg(&opt->max_parents, &args, "--max-parents=", 1))
|
|
||||||
/** Found valid --max-parents. */;
|
|
||||||
else if (match_int_arg(&opt->min_parents, &args, "--min-parents=", 0))
|
|
||||||
/** Found valid --min_parents. */;
|
|
||||||
else if (!strcmp(a, "-p") || !strcmp(a, "-u") || !strcmp(a, "--patch"))
|
|
||||||
opt->show_diff = 1;
|
|
||||||
else
|
|
||||||
usage("Unsupported argument", a);
|
|
||||||
}
|
|
||||||
|
|
||||||
return args.pos;
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -eux
|
|
||||||
|
|
||||||
make
|
|
||||||
|
|
||||||
if [ ! -e ~/.gitconfig ]; then
|
|
||||||
cat << EOF > ~/.gitconfig
|
|
||||||
[user]
|
|
||||||
name=test-user
|
|
||||||
email=test-email
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
./init --initial-commit ./test-libgit2
|
|
||||||
pushd ./test-libgit2
|
|
||||||
../log | grep 'Initial commit'
|
|
@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
# Run a simple test
|
|
||||||
- hosts: localhost
|
|
||||||
roles:
|
|
||||||
- role: standard-test-basic
|
|
||||||
tags:
|
|
||||||
- classic
|
|
||||||
|
|
||||||
required_packages:
|
|
||||||
- make
|
|
||||||
- gcc
|
|
||||||
- libgit2
|
|
||||||
- libgit2-devel
|
|
||||||
|
|
||||||
tests:
|
|
||||||
- simple:
|
|
||||||
dir: scripts
|
|
||||||
run: ./run_tests.sh
|
|
Loading…
Reference in New Issue
Block a user