Commit 42627933 by Carlos Martín Nieto

Merge remote-tracking branch 'upstream/master' into pks/conditional-includes

parents 1475b981 990d2b85
......@@ -27,7 +27,7 @@ addons:
apt:
sources:
- sourceline: 'deb http://libgit2deps.edwardthomson.com trusty libgit2deps'
key_url: 'https://pgp.mit.edu/pks/lookup?op=get&search=0x5656187599131CD5'
key_url: 'https://www.edwardthomson.com/keys/ethomson@libgit2.org'
packages:
cmake
curl
......
......@@ -6,6 +6,10 @@ v0.26 + 1
* Improved `p_unlink` in `posix_w32.c` to try and make a file writable
before sleeping in the retry loop to prevent unnecessary calls to sleep.
* Writing to a configuration file now preserves the case of the key given by the
caller for the case-insensitive portions of the key (existing sections are
used even if they don't match).
### API additions
* `git_remote_create_detached()` creates a remote that is not associated
......
......@@ -14,12 +14,12 @@
PROJECT(libgit2 C)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11)
CMAKE_POLICY(SET CMP0015 NEW)
IF (CMAKE_VERSION VERSION_GREATER 3.0)
IF (NOT CMAKE_VERSION VERSION_LESS 3.1)
CMAKE_POLICY(SET CMP0051 NEW)
ENDIF()
# Add find modules to the path
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${libgit2_SOURCE_DIR}/cmake/Modules/")
INCLUDE(CheckLibraryExists)
INCLUDE(CheckFunctionExists)
......@@ -125,7 +125,7 @@ FUNCTION(IDE_SPLIT_SOURCES target)
GET_TARGET_PROPERTY(sources ${target} SOURCES)
FOREACH(source ${sources})
IF(source MATCHES ".*/")
STRING(REPLACE ${CMAKE_SOURCE_DIR}/ "" rel ${source})
STRING(REPLACE ${libgit2_SOURCE_DIR}/ "" rel ${source})
IF(rel)
STRING(REGEX REPLACE "/([^/]*)$" "" rel ${rel})
IF(rel)
......@@ -138,14 +138,14 @@ FUNCTION(IDE_SPLIT_SOURCES target)
ENDIF()
ENDFUNCTION()
FILE(STRINGS "${CMAKE_SOURCE_DIR}/include/git2/version.h" GIT2_HEADER REGEX "^#define LIBGIT2_VERSION \"[^\"]*\"$")
FILE(STRINGS "${libgit2_SOURCE_DIR}/include/git2/version.h" GIT2_HEADER REGEX "^#define LIBGIT2_VERSION \"[^\"]*\"$")
STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"([0-9]+).*$" "\\1" LIBGIT2_VERSION_MAJOR "${GIT2_HEADER}")
STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"[0-9]+\\.([0-9]+).*$" "\\1" LIBGIT2_VERSION_MINOR "${GIT2_HEADER}")
STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" LIBGIT2_VERSION_REV "${GIT2_HEADER}")
SET(LIBGIT2_VERSION_STRING "${LIBGIT2_VERSION_MAJOR}.${LIBGIT2_VERSION_MINOR}.${LIBGIT2_VERSION_REV}")
FILE(STRINGS "${CMAKE_SOURCE_DIR}/include/git2/version.h" GIT2_HEADER_SOVERSION REGEX "^#define LIBGIT2_SOVERSION [0-9]+$")
FILE(STRINGS "${libgit2_SOURCE_DIR}/include/git2/version.h" GIT2_HEADER_SOVERSION REGEX "^#define LIBGIT2_SOVERSION [0-9]+$")
STRING(REGEX REPLACE "^.*LIBGIT2_SOVERSION ([0-9]+)$" "\\1" LIBGIT2_SOVERSION "${GIT2_HEADER_SOVERSION}")
# Platform specific compilation flags
......@@ -225,6 +225,10 @@ IF (MSVC)
SET(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}")
SET(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL}")
ELSE ()
IF (NOT BUILD_SHARED_LIBS)
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
ENDIF()
IF (ENABLE_REPRODUCIBLE_BUILDS)
SET(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> Dqc <TARGET> <LINK_FLAGS> <OBJECTS>")
SET(CMAKE_C_ARCHIVE_APPEND "<CMAKE_AR> Dq <TARGET> <LINK_FLAGS> <OBJECTS>")
......
......@@ -36,10 +36,6 @@ These are good small projects to get started with libgit2.
trick to this one will be doing it in a manner that is clean and
simple, but still handles the various cases correctly (e.g. `-B/70%`
is apparently a legal setting).
* Implement the `--log-size` option for `examples/log.c`. I think all
the data is available, you would just need to add the code into the
`print_commit()` routine (along with a way of passing the option
into that function).
* As an extension to the matching idea for `examples/log.c`, add the
`-i` option to use `strcasestr()` for matches.
* For `examples/log.c`, implement the `--first-parent` option now that
......
......@@ -3,7 +3,7 @@ IF (NOT DLLTOOL)
MESSAGE(FATAL_ERROR "Could not find dlltool command")
ENDIF ()
SET(LIBWINHTTP_PATH "${CMAKE_BINARY_DIR}/deps/winhttp")
SET(LIBWINHTTP_PATH "${libgit2_BINARY_DIR}/deps/winhttp")
SET(LIBWINHTTP_PATH ${LIBWINHTTP_PATH} PARENT_SCOPE)
FILE(MAKE_DIRECTORY ${LIBWINHTTP_PATH})
......
general
showindex
diff
rev-list
blame
cat-file
init
log
rev-parse
remote
status
tag
for-each-ref
describe
*.dSYM
.PHONY: all
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 status log rev-parse init blame tag remote
APPS += for-each-ref
APPS += describe
all: $(APPS)
% : %.c
$(CC) -o $@ common.c $(CFLAGS) $< $(LFLAGS)
clean:
$(RM) $(APPS)
$(RM) -r *.dSYM
......@@ -50,6 +50,7 @@ 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 show_log_size;
int skip, limit;
int min_parents, max_parents;
git_time_t before;
......@@ -63,7 +64,7 @@ struct log_options {
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 void print_commit(git_commit *commit, struct log_options *opts);
static int match_with_parent(git_commit *commit, int i, git_diff_options *);
/** utility functions for filtering */
......@@ -148,7 +149,7 @@ int main(int argc, char *argv[])
break;
}
print_commit(commit);
print_commit(commit, &opt);
if (opt.show_diff) {
git_tree *a = NULL, *b = NULL;
......@@ -337,7 +338,7 @@ static void print_time(const git_time *intime, const char *prefix)
}
/** Helper to print a commit object. */
static void print_commit(git_commit *commit)
static void print_commit(git_commit *commit, struct log_options *opts)
{
char buf[GIT_OID_HEXSZ + 1];
int i, count;
......@@ -347,6 +348,10 @@ static void print_commit(git_commit *commit)
git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
printf("commit %s\n", buf);
if (opts->show_log_size) {
printf("log size %d\n", (int)strlen(git_commit_message(commit)));
}
if ((count = (int)git_commit_parentcount(commit)) > 1) {
printf("Merge:");
for (i = 0; i < count; ++i) {
......@@ -470,6 +475,8 @@ static int parse_options(
/** Found valid --min_parents. */;
else if (!strcmp(a, "-p") || !strcmp(a, "-u") || !strcmp(a, "--patch"))
opt->show_diff = 1;
else if (!strcmp(a, "--log-size"))
opt->show_log_size = 1;
else
usage("Unsupported argument", a);
}
......
......@@ -2,7 +2,10 @@ IF(DEBUG_POOL)
SET(GIT_DEBUG_POOL 1)
ENDIF()
SET(LIBGIT2_OBJECTS "")
# Add the features.h file as a dummy. This is required for Xcode
# to successfully build the libgit2 library when using only
# object libraries.
SET(LIBGIT2_OBJECTS "${CMAKE_CURRENT_BINARY_DIR}/git2/sys/features.h")
# This variable will contain the libraries we need to put into
# libgit2.pc's Requires.private. That is, what we're linking to or
......@@ -15,8 +18,8 @@ SET(LIBGIT2_PC_LIBS "")
SET(LIBGIT2_INCLUDES
"${CMAKE_CURRENT_BINARY_DIR}"
"${CMAKE_SOURCE_DIR}/src"
"${CMAKE_SOURCE_DIR}/include")
"${libgit2_SOURCE_DIR}/src"
"${libgit2_SOURCE_DIR}/include")
SET(LIBGIT2_LIBS "")
SET(LIBGIT2_LIBDIRS "")
......@@ -126,9 +129,9 @@ IF (WIN32 AND WINHTTP)
# Since MinGW does not come with headers or an import library for winhttp,
# we have to include a private header and generate our own import library
IF (MINGW)
ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/deps/winhttp" "${CMAKE_BINARY_DIR}/deps/winhttp")
ADD_SUBDIRECTORY("${libgit2_SOURCE_DIR}/deps/winhttp" "${libgit2_BINARY_DIR}/deps/winhttp")
LIST(APPEND LIBGIT2_LIBS winhttp)
LIST(APPEND LIBGIT2_INCLUDES "${CMAKE_SOURCE_DIR}/deps/winhttp")
LIST(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/winhttp")
LIST(APPEND LIBGIT2_LIBDIRS ${LIBWINHTTP_PATH})
ELSE()
LIST(APPEND LIBGIT2_LIBS "winhttp")
......@@ -181,8 +184,8 @@ ENDIF()
# Include POSIX regex when it is required
IF(WIN32 OR AMIGA OR CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/deps/regex" "${CMAKE_BINARY_DIR}/deps/regex")
LIST(APPEND LIBGIT2_INCLUDES "${CMAKE_SOURCE_DIR}/deps/regex")
ADD_SUBDIRECTORY("${libgit2_SOURCE_DIR}/deps/regex" "${libgit2_BINARY_DIR}/deps/regex")
LIST(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/regex")
LIST(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:regex>)
ENDIF()
......@@ -194,8 +197,8 @@ IF (USE_EXT_HTTP_PARSER AND HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUA
LIST(APPEND LIBGIT2_PC_LIBS "-lhttp_parser")
ELSE()
MESSAGE(STATUS "http-parser version 2 was not found or disabled; using bundled 3rd-party sources.")
ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/deps/http-parser" "${CMAKE_BINARY_DIR}/deps/http-parser")
LIST(APPEND LIBGIT2_INCLUDES "${CMAKE_SOURCE_DIR}/deps/http-parser")
ADD_SUBDIRECTORY("${libgit2_SOURCE_DIR}/deps/http-parser" "${libgit2_BINARY_DIR}/deps/http-parser")
LIST(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/http-parser")
LIST(APPEND LIBGIT2_OBJECTS "$<TARGET_OBJECTS:http-parser>")
ENDIF()
......@@ -212,8 +215,8 @@ IF (ZLIB_FOUND)
ENDIF()
ELSE()
MESSAGE(STATUS "zlib was not found; using bundled 3rd-party sources." )
ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/deps/zlib" "${CMAKE_BINARY_DIR}/deps/zlib")
LIST(APPEND LIBGIT2_INCLUDES "${CMAKE_SOURCE_DIR}/deps/zlib")
ADD_SUBDIRECTORY("${libgit2_SOURCE_DIR}/deps/zlib" "${libgit2_BINARY_DIR}/deps/zlib")
LIST(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/zlib")
LIST(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:zlib>)
ENDIF()
......@@ -296,9 +299,9 @@ ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64)
# Collect sourcefiles
FILE(GLOB SRC_H
"${CMAKE_SOURCE_DIR}/include/git2.h"
"${CMAKE_SOURCE_DIR}/include/git2/*.h"
"${CMAKE_SOURCE_DIR}/include/git2/sys/*.h")
"${libgit2_SOURCE_DIR}/include/git2.h"
"${libgit2_SOURCE_DIR}/include/git2/*.h"
"${libgit2_SOURCE_DIR}/include/git2/sys/*.h")
# On Windows use specific platform sources
IF (WIN32 AND NOT CYGWIN)
......@@ -343,7 +346,7 @@ IF (${CMAKE_VERSION} VERSION_LESS 2.8.12)
ELSE()
TARGET_INCLUDE_DIRECTORIES(git2internal
PRIVATE ${LIBGIT2_INCLUDES}
PUBLIC ${CMAKE_SOURCE_DIR}/include)
PUBLIC ${libgit2_SOURCE_DIR}/include)
ENDIF()
SET(LIBGIT2_OBJECTS ${LIBGIT2_OBJECTS} PARENT_SCOPE)
......@@ -356,9 +359,9 @@ LINK_DIRECTORIES(${LIBGIT2_LIBDIRS})
ADD_LIBRARY(git2 ${WIN_RC} ${LIBGIT2_OBJECTS})
TARGET_LINK_LIBRARIES(git2 ${LIBGIT2_LIBS})
SET_TARGET_PROPERTIES(git2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
SET_TARGET_PROPERTIES(git2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
SET_TARGET_PROPERTIES(git2 PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
SET_TARGET_PROPERTIES(git2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
SET_TARGET_PROPERTIES(git2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
SET_TARGET_PROPERTIES(git2 PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240)
# Win64+MSVC+static libs = linker error
......@@ -379,7 +382,7 @@ IF (SONAME)
ENDIF()
ENDIF()
STRING(REPLACE ";" " " LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS}")
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/libgit2.pc.in ${CMAKE_BINARY_DIR}/libgit2.pc @ONLY)
CONFIGURE_FILE(${libgit2_SOURCE_DIR}/libgit2.pc.in ${libgit2_BINARY_DIR}/libgit2.pc @ONLY)
IF (MSVC_IDE)
# Precompiled headers
......@@ -393,6 +396,6 @@ INSTALL(TARGETS git2
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
)
INSTALL(FILES ${CMAKE_BINARY_DIR}/libgit2.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig )
INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/include/git2 DESTINATION ${INCLUDE_INSTALL_DIR} )
INSTALL(FILES ${CMAKE_SOURCE_DIR}/include/git2.h DESTINATION ${INCLUDE_INSTALL_DIR} )
INSTALL(FILES ${libgit2_BINARY_DIR}/libgit2.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig )
INSTALL(DIRECTORY ${libgit2_SOURCE_DIR}/include/git2 DESTINATION ${INCLUDE_INSTALL_DIR} )
INSTALL(FILES ${libgit2_SOURCE_DIR}/include/git2.h DESTINATION ${INCLUDE_INSTALL_DIR} )
......@@ -121,7 +121,7 @@ typedef struct {
} diskfile_readonly_backend;
static int config_read(git_strmap *values, const git_repository *repo, struct config_file *file, git_config_level_t level, int depth);
static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const regex_t *preg, const char *value);
static char *escape_value(const char *ptr);
int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in);
......@@ -515,7 +515,7 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
GITERR_CHECK_ALLOC(esc_value);
}
if ((ret = config_write(b, key, NULL, esc_value)) < 0)
if ((ret = config_write(b, name, key, NULL, esc_value)) < 0)
goto out;
ret = config_refresh(cfg);
......@@ -593,7 +593,7 @@ static int config_set_multivar(
}
/* If we do have it, set call config_write() and reload */
if ((result = config_write(b, key, &preg, value)) < 0)
if ((result = config_write(b, name, key, &preg, value)) < 0)
goto out;
result = config_refresh(cfg);
......@@ -643,7 +643,7 @@ static int config_delete(git_config_backend *cfg, const char *name)
return -1;
}
if ((result = config_write(b, var->entry->name, NULL, NULL)) < 0)
if ((result = config_write(b, name, var->entry->name, NULL, NULL)) < 0)
return result;
return config_refresh(cfg);
......@@ -684,7 +684,7 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con
goto out;
}
if ((result = config_write(b, key, &preg, NULL)) < 0)
if ((result = config_write(b, name, key, &preg, NULL)) < 0)
goto out;
result = config_refresh(cfg);
......@@ -1866,7 +1866,9 @@ struct write_data {
git_buf buffered_comment;
unsigned int in_section : 1,
preg_replaced : 1;
const char *orig_section;
const char *section;
const char *orig_name;
const char *name;
const regex_t *preg;
const char *value;
......@@ -1894,7 +1896,7 @@ static int write_value(struct write_data *write_data)
q = quotes_for_value(write_data->value);
result = git_buf_printf(write_data->buf,
"\t%s = %s%s%s\n", write_data->name, q, write_data->value, q);
"\t%s = %s%s%s\n", write_data->orig_name, q, write_data->value, q);
/* If we are updating a single name/value, we're done. Setting `value`
* to `NULL` will prevent us from trying to write it again later (in
......@@ -2025,7 +2027,7 @@ static int write_on_eof(
if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) {
/* write the section header unless we're already in it */
if (!current_section || strcmp(current_section, write_data->section))
result = write_section(write_data->buf, write_data->section);
result = write_section(write_data->buf, write_data->orig_section);
if (!result)
result = write_value(write_data);
......@@ -2037,10 +2039,10 @@ static int write_on_eof(
/*
* This is pretty much the parsing, except we write out anything we don't have
*/
static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const regex_t *preg, const char* value)
{
int result;
char *section, *name, *ldot;
char *orig_section, *section, *orig_name, *name, *ldot;
git_filebuf file = GIT_FILEBUF_INIT;
git_buf buf = GIT_BUF_INIT;
struct reader reader;
......@@ -2080,18 +2082,27 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
ldot = strrchr(key, '.');
name = ldot + 1;
section = git__strndup(key, ldot - key);
GITERR_CHECK_ALLOC(section);
ldot = strrchr(orig_key, '.');
orig_name = ldot + 1;
orig_section = git__strndup(orig_key, ldot - orig_key);
GITERR_CHECK_ALLOC(orig_section);
write_data.buf = &buf;
git_buf_init(&write_data.buffered_comment, 0);
write_data.orig_section = orig_section;
write_data.section = section;
write_data.in_section = 0;
write_data.preg_replaced = 0;
write_data.orig_name = orig_name;
write_data.name = name;
write_data.preg = preg;
write_data.value = value;
result = config_parse(&reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data);
git__free(section);
git__free(orig_section);
git_buf_free(&write_data.buffered_comment);
if (result < 0) {
......
......@@ -273,7 +273,7 @@ static int fetch_setup_walk(git_revwalk **out, git_repository *repo)
git_revwalk *walk = NULL;
git_strarray refs;
unsigned int i;
git_reference *ref;
git_reference *ref = NULL;
int error;
if ((error = git_reference_list(&refs, repo)) < 0)
......@@ -285,6 +285,9 @@ static int fetch_setup_walk(git_revwalk **out, git_repository *repo)
git_revwalk_sorting(walk, GIT_SORT_TIME);
for (i = 0; i < refs.count; ++i) {
git_reference_free(ref);
ref = NULL;
/* No tags */
if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
continue;
......@@ -297,15 +300,12 @@ static int fetch_setup_walk(git_revwalk **out, git_repository *repo)
if ((error = git_revwalk_push(walk, git_reference_target(ref))) < 0)
goto on_error;
git_reference_free(ref);
}
git_strarray_free(&refs);
*out = walk;
return 0;
on_error:
if (error)
git_revwalk_free(walk);
git_reference_free(ref);
git_strarray_free(&refs);
......
......@@ -419,7 +419,9 @@ static int _git_ssh_authenticate_session(
}
} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
if (rc == LIBSSH2_ERROR_PASSWORD_EXPIRED || rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED)
if (rc == LIBSSH2_ERROR_PASSWORD_EXPIRED ||
rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED ||
rc == LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED)
return GIT_EAUTH;
if (rc != LIBSSH2_ERROR_NONE) {
......
......@@ -43,6 +43,8 @@ extern "C" {
#define XDF_IGNORE_BLANK_LINES (1 << 7)
#define XDF_INDENT_HEURISTIC (1 << 8)
#define XDL_EMIT_FUNCNAMES (1 << 0)
#define XDL_EMIT_COMMON (1 << 1)
#define XDL_EMIT_FUNCCONTEXT (1 << 2)
......
......@@ -31,7 +31,12 @@
#define XDL_SNAKE_CNT 20
#define XDL_K_HEUR 4
/** Declare a function as always inlined. */
#if defined(_MSC_VER)
# define XDL_INLINE(type) static __inline type
#else
# define XDL_INLINE(type) static inline type
#endif
typedef struct s_xdpsplit {
long i1, i2;
......@@ -404,105 +409,543 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,
}
int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec;
char *rchg = xdf->rchg, *rchgo = xdfo->rchg;
xrecord_t **recs = xdf->recs;
static int recs_match(xrecord_t *rec1, xrecord_t *rec2, long flags)
{
return (rec1->ha == rec2->ha &&
xdl_recmatch(rec1->ptr, rec1->size,
rec2->ptr, rec2->size,
flags));
}
/*
* If a line is indented more than this, get_indent() just returns this value.
* This avoids having to do absurd amounts of work for data that are not
* human-readable text, and also ensures that the output of get_indent fits within
* an int.
*/
#define MAX_INDENT 200
/*
* Return the amount of indentation of the specified line, treating TAB as 8
* columns. Return -1 if line is empty or contains only whitespace. Clamp the
* output value at MAX_INDENT.
*/
static int get_indent(xrecord_t *rec)
{
long i;
int ret = 0;
for (i = 0; i < rec->size; i++) {
char c = rec->ptr[i];
if (!XDL_ISSPACE(c))
return ret;
else if (c == ' ')
ret += 1;
else if (c == '\t')
ret += 8 - ret % 8;
/* ignore other whitespace characters */
if (ret >= MAX_INDENT)
return MAX_INDENT;
}
/* The line contains only whitespace. */
return -1;
}
/*
* If more than this number of consecutive blank rows are found, just return this
* value. This avoids requiring O(N^2) work for pathological cases, and also
* ensures that the output of score_split fits in an int.
*/
#define MAX_BLANKS 20
/* Characteristics measured about a hypothetical split position. */
struct split_measurement {
/*
* This is the same of what GNU diff does. Move back and forward
* change groups for a consistent and pretty diff output. This also
* helps in finding joinable change groups and reduce the diff size.
* Is the split at the end of the file (aside from any blank lines)?
*/
for (ix = ixo = 0;;) {
int end_of_file;
/*
* Find the first changed line in the to-be-compacted file.
* We need to keep track of both indexes, so if we find a
* changed lines group on the other file, while scanning the
* to-be-compacted file, we need to skip it properly. Note
* that loops that are testing for changed lines on rchg* do
* not need index bounding since the array is prepared with
* a zero at position -1 and N.
* How much is the line immediately following the split indented (or -1 if
* the line is blank):
*/
for (; ix < nrec && !rchg[ix]; ix++)
while (rchgo[ixo++]);
if (ix == nrec)
int indent;
/*
* How many consecutive lines above the split are blank?
*/
int pre_blank;
/*
* How much is the nearest non-blank line above the split indented (or -1
* if there is no such line)?
*/
int pre_indent;
/*
* How many lines after the line following the split are blank?
*/
int post_blank;
/*
* How much is the nearest non-blank line after the line following the
* split indented (or -1 if there is no such line)?
*/
int post_indent;
};
struct split_score {
/* The effective indent of this split (smaller is preferred). */
int effective_indent;
/* Penalty for this split (smaller is preferred). */
int penalty;
};
/*
* Fill m with information about a hypothetical split of xdf above line split.
*/
static void measure_split(const xdfile_t *xdf, long split,
struct split_measurement *m)
{
long i;
if (split >= xdf->nrec) {
m->end_of_file = 1;
m->indent = -1;
} else {
m->end_of_file = 0;
m->indent = get_indent(xdf->recs[split]);
}
m->pre_blank = 0;
m->pre_indent = -1;
for (i = split - 1; i >= 0; i--) {
m->pre_indent = get_indent(xdf->recs[i]);
if (m->pre_indent != -1)
break;
m->pre_blank += 1;
if (m->pre_blank == MAX_BLANKS) {
m->pre_indent = 0;
break;
}
}
m->post_blank = 0;
m->post_indent = -1;
for (i = split + 1; i < xdf->nrec; i++) {
m->post_indent = get_indent(xdf->recs[i]);
if (m->post_indent != -1)
break;
m->post_blank += 1;
if (m->post_blank == MAX_BLANKS) {
m->post_indent = 0;
break;
}
}
}
/*
* The empirically-determined weight factors used by score_split() below.
* Larger values means that the position is a less favorable place to split.
*
* Note that scores are only ever compared against each other, so multiplying
* all of these weight/penalty values by the same factor wouldn't change the
* heuristic's behavior. Still, we need to set that arbitrary scale *somehow*.
* In practice, these numbers are chosen to be large enough that they can be
* adjusted relative to each other with sufficient precision despite using
* integer math.
*/
/* Penalty if there are no non-blank lines before the split */
#define START_OF_FILE_PENALTY 1
/* Penalty if there are no non-blank lines after the split */
#define END_OF_FILE_PENALTY 21
/* Multiplier for the number of blank lines around the split */
#define TOTAL_BLANK_WEIGHT (-30)
/* Multiplier for the number of blank lines after the split */
#define POST_BLANK_WEIGHT 6
/*
* Penalties applied if the line is indented more than its predecessor
*/
#define RELATIVE_INDENT_PENALTY (-4)
#define RELATIVE_INDENT_WITH_BLANK_PENALTY 10
/*
* Penalties applied if the line is indented less than both its predecessor and
* its successor
*/
#define RELATIVE_OUTDENT_PENALTY 24
#define RELATIVE_OUTDENT_WITH_BLANK_PENALTY 17
/*
* Penalties applied if the line is indented less than its predecessor but not
* less than its successor
*/
#define RELATIVE_DEDENT_PENALTY 23
#define RELATIVE_DEDENT_WITH_BLANK_PENALTY 17
/*
* We only consider whether the sum of the effective indents for splits are
* less than (-1), equal to (0), or greater than (+1) each other. The resulting
* value is multiplied by the following weight and combined with the penalty to
* determine the better of two scores.
*/
#define INDENT_WEIGHT 60
/*
* Compute a badness score for the hypothetical split whose measurements are
* stored in m. The weight factors were determined empirically using the tools and
* corpus described in
*
* https://github.com/mhagger/diff-slider-tools
*
* Also see that project if you want to improve the weights based on, for example,
* a larger or more diverse corpus.
*/
static void score_add_split(const struct split_measurement *m, struct split_score *s)
{
/*
* Record the start of a changed-group in the to-be-compacted file
* and find the end of it, on both to-be-compacted and other file
* indexes (ix and ixo).
* A place to accumulate penalty factors (positive makes this index more
* favored):
*/
ixs = ix;
for (ix++; rchg[ix]; ix++);
for (; rchgo[ixo]; ixo++);
int post_blank, total_blank, indent, any_blanks;
do {
grpsiz = ix - ixs;
if (m->pre_indent == -1 && m->pre_blank == 0)
s->penalty += START_OF_FILE_PENALTY;
if (m->end_of_file)
s->penalty += END_OF_FILE_PENALTY;
/*
* If the line before the current change group, is equal to
* the last line of the current change group, shift backward
* the group.
* Set post_blank to the number of blank lines following the split,
* including the line immediately after the split:
*/
while (ixs > 0 && recs[ixs - 1]->ha == recs[ix - 1]->ha &&
xdl_recmatch(recs[ixs - 1]->ptr, recs[ixs - 1]->size, recs[ix - 1]->ptr, recs[ix - 1]->size, flags)) {
rchg[--ixs] = 1;
rchg[--ix] = 0;
post_blank = (m->indent == -1) ? 1 + m->post_blank : 0;
total_blank = m->pre_blank + post_blank;
/* Penalties based on nearby blank lines: */
s->penalty += TOTAL_BLANK_WEIGHT * total_blank;
s->penalty += POST_BLANK_WEIGHT * post_blank;
if (m->indent != -1)
indent = m->indent;
else
indent = m->post_indent;
any_blanks = (total_blank != 0);
/* Note that the effective indent is -1 at the end of the file: */
s->effective_indent += indent;
if (indent == -1) {
/* No additional adjustments needed. */
} else if (m->pre_indent == -1) {
/* No additional adjustments needed. */
} else if (indent > m->pre_indent) {
/*
* The line is indented more than its predecessor.
*/
s->penalty += any_blanks ?
RELATIVE_INDENT_WITH_BLANK_PENALTY :
RELATIVE_INDENT_PENALTY;
} else if (indent == m->pre_indent) {
/*
* The line has the same indentation level as its predecessor.
* No additional adjustments needed.
*/
} else {
/*
* The line is indented less than its predecessor. It could be
* the block terminator of the previous block, but it could
* also be the start of a new block (e.g., an "else" block, or
* maybe the previous block didn't have a block terminator).
* Try to distinguish those cases based on what comes next:
*/
if (m->post_indent != -1 && m->post_indent > indent) {
/*
* This change might have joined two change groups,
* so we try to take this scenario in account by moving
* the start index accordingly (and so the other-file
* end-of-group index).
* The following line is indented more. So it is likely
* that this line is the start of a block.
*/
for (; rchg[ixs - 1]; ixs--);
while (rchgo[--ixo]);
s->penalty += any_blanks ?
RELATIVE_OUTDENT_WITH_BLANK_PENALTY :
RELATIVE_OUTDENT_PENALTY;
} else {
/*
* That was probably the end of a block.
*/
s->penalty += any_blanks ?
RELATIVE_DEDENT_WITH_BLANK_PENALTY :
RELATIVE_DEDENT_PENALTY;
}
}
}
static int score_cmp(struct split_score *s1, struct split_score *s2)
{
/* -1 if s1.effective_indent < s2->effective_indent, etc. */
int cmp_indents = ((s1->effective_indent > s2->effective_indent) -
(s1->effective_indent < s2->effective_indent));
return INDENT_WEIGHT * cmp_indents + (s1->penalty - s2->penalty);
}
/*
* Represent a group of changed lines in an xdfile_t (i.e., a contiguous group
* of lines that was inserted or deleted from the corresponding version of the
* file). We consider there to be such a group at the beginning of the file, at
* the end of the file, and between any two unchanged lines, though most such
* groups will usually be empty.
*
* If the first line in a group is equal to the line following the group, then
* the group can be slid down. Similarly, if the last line in a group is equal
* to the line preceding the group, then the group can be slid up. See
* group_slide_down() and group_slide_up().
*
* Note that loops that are testing for changed lines in xdf->rchg do not need
* index bounding since the array is prepared with a zero at position -1 and N.
*/
struct xdlgroup {
/*
* Record the end-of-group position in case we are matched
* with a group of changes in the other file (that is, the
* change record before the end-of-group index in the other
* file is set).
* The index of the first changed line in the group, or the index of
* the unchanged line above which the (empty) group is located.
*/
ixref = rchgo[ixo - 1] ? ix: nrec;
long start;
/*
* If the first line of the current change group, is equal to
* the line next of the current change group, shift forward
* the group.
* The index of the first unchanged line after the group. For an empty
* group, end is equal to start.
*/
long end;
};
/*
* Initialize g to point at the first group in xdf.
*/
static void group_init(xdfile_t *xdf, struct xdlgroup *g)
{
g->start = g->end = 0;
while (xdf->rchg[g->end])
g->end++;
}
/*
* Move g to describe the next (possibly empty) group in xdf and return 0. If g
* is already at the end of the file, do nothing and return -1.
*/
while (ix < nrec && recs[ixs]->ha == recs[ix]->ha &&
xdl_recmatch(recs[ixs]->ptr, recs[ixs]->size, recs[ix]->ptr, recs[ix]->size, flags)) {
rchg[ixs++] = 0;
rchg[ix++] = 1;
XDL_INLINE(int) group_next(xdfile_t *xdf, struct xdlgroup *g)
{
if (g->end == xdf->nrec)
return -1;
g->start = g->end + 1;
for (g->end = g->start; xdf->rchg[g->end]; g->end++)
;
return 0;
}
/*
* Move g to describe the previous (possibly empty) group in xdf and return 0.
* If g is already at the beginning of the file, do nothing and return -1.
*/
XDL_INLINE(int) group_previous(xdfile_t *xdf, struct xdlgroup *g)
{
if (g->start == 0)
return -1;
g->end = g->start - 1;
for (g->start = g->end; xdf->rchg[g->start - 1]; g->start--)
;
return 0;
}
/*
* If g can be slid toward the end of the file, do so, and if it bumps into a
* following group, expand this group to include it. Return 0 on success or -1
* if g cannot be slid down.
*/
static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g, long flags)
{
if (g->end < xdf->nrec &&
recs_match(xdf->recs[g->start], xdf->recs[g->end], flags)) {
xdf->rchg[g->start++] = 0;
xdf->rchg[g->end++] = 1;
while (xdf->rchg[g->end])
g->end++;
return 0;
} else {
return -1;
}
}
/*
* If g can be slid toward the beginning of the file, do so, and if it bumps
* into a previous group, expand this group to include it. Return 0 on success
* or -1 if g cannot be slid up.
*/
static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g, long flags)
{
if (g->start > 0 &&
recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1], flags)) {
xdf->rchg[--g->start] = 1;
xdf->rchg[--g->end] = 0;
while (xdf->rchg[g->start - 1])
g->start--;
return 0;
} else {
return -1;
}
}
static void xdl_bug(const char *msg)
{
fprintf(stderr, "BUG: %s\n", msg);
exit(1);
}
/*
* Move back and forward change groups for a consistent and pretty diff output.
* This also helps in finding joinable change groups and reducing the diff
* size.
*/
int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
struct xdlgroup g, go;
long earliest_end, end_matching_other;
long groupsize;
group_init(xdf, &g);
group_init(xdfo, &go);
while (1) {
/* If the group is empty in the to-be-compacted file, skip it: */
if (g.end == g.start)
goto next;
/*
* Now shift the change up and then down as far as possible in
* each direction. If it bumps into any other changes, merge them.
*/
do {
groupsize = g.end - g.start;
/*
* Keep track of the last "end" index that causes this
* group to align with a group of changed lines in the
* other file. -1 indicates that we haven't found such
* a match yet:
*/
end_matching_other = -1;
/* Shift the group backward as much as possible: */
while (!group_slide_up(xdf, &g, flags))
if (group_previous(xdfo, &go))
xdl_bug("group sync broken sliding up");
/*
* This change might have joined two change groups,
* so we try to take this scenario in account by moving
* the start index accordingly (and so the other-file
* end-of-group index). Keep tracking the reference
* index in case we are shifting together with a
* corresponding group of changes in the other file.
* This is this highest that this group can be shifted.
* Record its end index:
*/
for (; rchg[ix]; ix++);
while (rchgo[++ixo])
ixref = ix;
earliest_end = g.end;
if (go.end > go.start)
end_matching_other = g.end;
/* Now shift the group forward as far as possible: */
while (1) {
if (group_slide_down(xdf, &g, flags))
break;
if (group_next(xdfo, &go))
xdl_bug("group sync broken sliding down");
if (go.end > go.start)
end_matching_other = g.end;
}
} while (grpsiz != ix - ixs);
} while (groupsize != g.end - g.start);
/*
* Try to move back the possibly merged group of changes, to match
* the recorded position in the other file.
* If the group can be shifted, then we can possibly use this
* freedom to produce a more intuitive diff.
*
* The group is currently shifted as far down as possible, so the
* heuristics below only have to handle upwards shifts.
*/
while (ixref < ix) {
rchg[--ixs] = 1;
rchg[--ix] = 0;
while (rchgo[--ixo]);
if (g.end == earliest_end) {
/* no shifting was possible */
} else if (end_matching_other != -1) {
/*
* Move the possibly merged group of changes back to line
* up with the last group of changes from the other file
* that it can align with.
*/
while (go.end == go.start) {
if (group_slide_up(xdf, &g, flags))
xdl_bug("match disappeared");
if (group_previous(xdfo, &go))
xdl_bug("group sync broken sliding to match");
}
} else if (flags & XDF_INDENT_HEURISTIC) {
/*
* Indent heuristic: a group of pure add/delete lines
* implies two splits, one between the end of the "before"
* context and the start of the group, and another between
* the end of the group and the beginning of the "after"
* context. Some splits are aesthetically better and some
* are worse. We compute a badness "score" for each split,
* and add the scores for the two splits to define a
* "score" for each position that the group can be shifted
* to. Then we pick the shift with the lowest score.
*/
long shift, best_shift = -1;
struct split_score best_score;
for (shift = earliest_end; shift <= g.end; shift++) {
struct split_measurement m;
struct split_score score = {0, 0};
measure_split(xdf, shift, &m);
score_add_split(&m, &score);
measure_split(xdf, shift - groupsize, &m);
score_add_split(&m, &score);
if (best_shift == -1 ||
score_cmp(&score, &best_score) <= 0) {
best_score.effective_indent = score.effective_indent;
best_score.penalty = score.penalty;
best_shift = shift;
}
}
while (g.end > best_shift) {
if (group_slide_up(xdf, &g, flags))
xdl_bug("best shift unreached");
if (group_previous(xdfo, &go))
xdl_bug("group sync broken sliding to blank line");
}
}
next:
/* Move past the just-processed group: */
if (group_next(xdf, &g))
break;
if (group_next(xdfo, &go))
xdl_bug("group sync broken moving to next group");
}
if (!group_next(xdfo, &go))
xdl_bug("group sync broken at end of file");
return 0;
}
......
......@@ -10,7 +10,7 @@ SET(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
ADD_DEFINITIONS(-DCLAR_FIXTURE_PATH=\"${CLAR_FIXTURES}\")
ADD_DEFINITIONS(-DCLAR_TMPDIR=\"libgit2_tests\")
INCLUDE_DIRECTORIES(${CLAR_PATH} ${CMAKE_BINARY_DIR}/src)
INCLUDE_DIRECTORIES(${CLAR_PATH} ${libgit2_BINARY_DIR}/src)
FILE(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c ${CLAR_PATH}/*/*.h)
SET(SRC_CLAR "main.c" "clar_libgit2.c" "clar_libgit2_trace.c" "clar_libgit2_timer.c" "clar.c")
......@@ -35,7 +35,7 @@ INCLUDE_DIRECTORIES(${LIBGIT2_INCLUDES})
ADD_EXECUTABLE(libgit2_clar ${SRC_CLAR} ${SRC_TEST} ${LIBGIT2_OBJECTS})
SET_TARGET_PROPERTIES(libgit2_clar PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
SET_TARGET_PROPERTIES(libgit2_clar PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
IF (${CMAKE_VERSION} VERSION_LESS 2.8.12)
# Already handled by a global INCLUDE_DIRECTORY()
......@@ -53,13 +53,13 @@ IF (MSVC_IDE)
ENDIF ()
IF (WINHTTP OR OPENSSL_FOUND OR SECURITY_FOUND)
ADD_TEST(libgit2_clar "${CMAKE_BINARY_DIR}/libgit2_clar" -ionline -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style)
ADD_TEST(libgit2_clar "${libgit2_BINARY_DIR}/libgit2_clar" -ionline -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style)
ELSE ()
ADD_TEST(libgit2_clar "${CMAKE_BINARY_DIR}/libgit2_clar" -v -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style)
ADD_TEST(libgit2_clar "${libgit2_BINARY_DIR}/libgit2_clar" -v -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style)
ENDIF ()
# Add a test target which runs the cred callback tests, to be
# called after setting the url and user
ADD_TEST(libgit2_clar-cred_callback "${CMAKE_BINARY_DIR}/libgit2_clar" -v -sonline::clone::cred_callback)
ADD_TEST(libgit2_clar-proxy_credentials_in_url "${CMAKE_BINARY_DIR}/libgit2_clar" -v -sonline::clone::proxy_credentials_in_url)
ADD_TEST(libgit2_clar-proxy_credentials_request "${CMAKE_BINARY_DIR}/libgit2_clar" -v -sonline::clone::proxy_credentials_request)
ADD_TEST(libgit2_clar-cred_callback "${libgit2_BINARY_DIR}/libgit2_clar" -v -sonline::clone::cred_callback)
ADD_TEST(libgit2_clar-proxy_credentials_in_url "${libgit2_BINARY_DIR}/libgit2_clar" -v -sonline::clone::proxy_credentials_in_url)
ADD_TEST(libgit2_clar-proxy_credentials_request "${libgit2_BINARY_DIR}/libgit2_clar" -v -sonline::clone::proxy_credentials_request)
......@@ -10,6 +10,16 @@ static git_repository *g_repo;
static git_checkout_options g_opts;
static git_object *g_object;
static void assert_status_entrycount(git_repository *repo, size_t count)
{
git_status_list *status;
cl_git_pass(git_status_list_new(&status, repo, NULL));
cl_assert_equal_i(count, git_status_list_entrycount(status));
git_status_list_free(status);
}
void test_checkout_tree__initialize(void)
{
g_repo = cl_git_sandbox_init("testrepo");
......@@ -1480,7 +1490,6 @@ void test_checkout_tree__baseline_is_empty_when_no_index(void)
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
git_reference *head;
git_object *obj;
git_status_list *status;
size_t conflicts = 0;
assert_on_branch(g_repo, "master");
......@@ -1506,9 +1515,7 @@ void test_checkout_tree__baseline_is_empty_when_no_index(void)
opts.checkout_strategy |= GIT_CHECKOUT_FORCE;
cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
cl_git_pass(git_status_list_new(&status, g_repo, NULL));
cl_assert_equal_i(0, git_status_list_entrycount(status));
git_status_list_free(status);
assert_status_entrycount(g_repo, 0);
git_object_free(obj);
git_reference_free(head);
......@@ -1519,7 +1526,6 @@ void test_checkout_tree__mode_change_is_force_updated(void)
git_index *index;
git_reference *head;
git_object *obj;
git_status_list *status;
if (!cl_is_chmod_supported())
clar__skip();
......@@ -1530,29 +1536,22 @@ void test_checkout_tree__mode_change_is_force_updated(void)
cl_git_pass(git_reference_peel(&obj, head, GIT_OBJ_COMMIT));
cl_git_pass(git_reset(g_repo, obj, GIT_RESET_HARD, NULL));
cl_git_pass(git_status_list_new(&status, g_repo, NULL));
cl_assert_equal_i(0, git_status_list_entrycount(status));
git_status_list_free(status);
assert_status_entrycount(g_repo, 0);
/* update the mode on-disk */
cl_must_pass(p_chmod("testrepo/README", 0755));
assert_status_entrycount(g_repo, 1);
cl_git_pass(git_checkout_tree(g_repo, obj, &g_opts));
cl_git_pass(git_status_list_new(&status, g_repo, NULL));
cl_assert_equal_i(0, git_status_list_entrycount(status));
git_status_list_free(status);
assert_status_entrycount(g_repo, 0);
/* update the mode on-disk and in the index */
cl_must_pass(p_chmod("testrepo/README", 0755));
cl_must_pass(git_index_add_bypath(index, "README"));
assert_status_entrycount(g_repo, 1);
cl_git_pass(git_checkout_tree(g_repo, obj, &g_opts));
cl_git_pass(git_status_list_new(&status, g_repo, NULL));
cl_assert_equal_i(0, git_status_list_entrycount(status));
git_status_list_free(status);
assert_status_entrycount(g_repo, 0);
git_object_free(obj);
git_reference_free(head);
......
......@@ -722,3 +722,26 @@ void test_config_write__repeated(void)
git_config_free(cfg);
}
void test_config_write__preserve_case(void)
{
const char *filename = "config-preserve-case";
git_config *cfg;
git_buf result = GIT_BUF_INIT;
const char *expected = "[sOMe]\n" \
"\tThInG = foo\n" \
"\tOtheR = thing\n";
cl_git_pass(git_config_open_ondisk(&cfg, filename));
cl_git_pass(git_config_set_string(cfg, "sOMe.ThInG", "foo"));
cl_git_pass(git_config_set_string(cfg, "SomE.OtheR", "thing"));
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, filename));
cl_git_pass(git_futils_readbuffer(&result, filename));
cl_assert_equal_s(expected, result.ptr);
git_buf_free(&result);
git_config_free(cfg);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment