diff options
author | Carlos Martín Nieto <cmn@dwim.me> | 2017-11-04 18:03:26 +0100 |
---|---|---|
committer | Carlos Martín Nieto <cmn@dwim.me> | 2017-11-04 18:03:26 +0100 |
commit | 42627933327bb8f91ae847bc8c72ab15ec1e2592 (patch) | |
tree | 4e7363374dff5ed9fd55023129c0c1c1c71909e2 | |
parent | 1475b981a4ee82409f3e92e4949dcf4e417a9f1f (diff) | |
parent | 990d2b854ab7eef28dd09209f77ff95d8e6a4ca2 (diff) | |
download | libgit2-42627933327bb8f91ae847bc8c72ab15ec1e2592.tar.gz |
Merge remote-tracking branch 'upstream/master' into pks/conditional-includes
-rw-r--r-- | .travis.yml | 2 | ||||
-rw-r--r-- | CHANGELOG.md | 4 | ||||
-rw-r--r-- | CMakeLists.txt | 14 | ||||
-rw-r--r-- | PROJECTS.md | 4 | ||||
-rw-r--r-- | deps/winhttp/CMakeLists.txt | 2 | ||||
-rw-r--r-- | examples/.gitignore | 15 | ||||
-rw-r--r-- | examples/Makefile | 17 | ||||
-rw-r--r-- | examples/log.c | 13 | ||||
-rw-r--r-- | src/CMakeLists.txt | 47 | ||||
-rw-r--r-- | src/config_file.c | 29 | ||||
-rw-r--r-- | src/transports/smart_protocol.c | 12 | ||||
-rw-r--r-- | src/transports/ssh.c | 6 | ||||
-rw-r--r-- | src/xdiff/xdiff.h | 2 | ||||
-rw-r--r-- | src/xdiff/xdiffi.c | 599 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 14 | ||||
-rw-r--r-- | tests/checkout/tree.c | 33 | ||||
-rw-r--r-- | tests/config/write.c | 23 |
17 files changed, 649 insertions, 187 deletions
diff --git a/.travis.yml b/.travis.yml index 13143458f..569a6a7d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 43724b5f3..b07a0a050 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f360e2af..600c5d66b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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>") diff --git a/PROJECTS.md b/PROJECTS.md index f53a2e120..419fdcdfc 100644 --- a/PROJECTS.md +++ b/PROJECTS.md @@ -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 diff --git a/deps/winhttp/CMakeLists.txt b/deps/winhttp/CMakeLists.txt index baa9fe2f0..148ac3ebc 100644 --- a/deps/winhttp/CMakeLists.txt +++ b/deps/winhttp/CMakeLists.txt @@ -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}) diff --git a/examples/.gitignore b/examples/.gitignore deleted file mode 100644 index 0e491598a..000000000 --- a/examples/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -general -showindex -diff -rev-list -blame -cat-file -init -log -rev-parse -remote -status -tag -for-each-ref -describe -*.dSYM diff --git a/examples/Makefile b/examples/Makefile deleted file mode 100644 index bd7e92dc9..000000000 --- a/examples/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -.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 diff --git a/examples/log.c b/examples/log.c index e54eed3ce..a107470ee 100644 --- a/examples/log.c +++ b/examples/log.c @@ -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); } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index daecc5a84..bbdbfdb74 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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} ) diff --git a/src/config_file.c b/src/config_file.c index 6e004ff69..cc4e7b3b7 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -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) { diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index d51238f12..aecfece0a 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -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,16 +300,13 @@ 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: - git_revwalk_free(walk); + if (error) + git_revwalk_free(walk); git_reference_free(ref); git_strarray_free(&refs); return error; diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 47d328c41..b37299215 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -419,8 +419,10 @@ 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) - return GIT_EAUTH; + if (rc == LIBSSH2_ERROR_PASSWORD_EXPIRED || + rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED || + rc == LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED) + return GIT_EAUTH; if (rc != LIBSSH2_ERROR_NONE) { if (!giterr_last()) diff --git a/src/xdiff/xdiff.h b/src/xdiff/xdiff.h index f08f72e16..b7dc05697 100644 --- a/src/xdiff/xdiff.h +++ b/src/xdiff/xdiff.h @@ -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) diff --git a/src/xdiff/xdiffi.c b/src/xdiff/xdiffi.c index f4d01b48c..3e65b6ce5 100644 --- a/src/xdiff/xdiffi.c +++ b/src/xdiff/xdiffi.c @@ -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,106 +409,544 @@ 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;;) { - /* - * 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. - */ - for (; ix < nrec && !rchg[ix]; ix++) - while (rchgo[ixo++]); - if (ix == nrec) + int end_of_file; + + /* + * How much is the line immediately following the split indented (or -1 if + * the line is blank): + */ + 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) +{ + /* + * A place to accumulate penalty factors (positive makes this index more + * favored): + */ + int post_blank, total_blank, indent, any_blanks; + + 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; + + /* + * Set post_blank to the number of blank lines following the split, + * including the line immediately after the split: + */ + 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 { /* - * 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). + * 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: */ - ixs = ix; - for (ix++; rchg[ix]; ix++); - for (; rchgo[ixo]; ixo++); + if (m->post_indent != -1 && m->post_indent > indent) { + /* + * The following line is indented more. So it is likely + * that this line is the start of a block. + */ + 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 { + /* + * The index of the first changed line in the group, or the index of + * the unchanged line above which the (empty) group is located. + */ + long start; + + /* + * 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. + */ +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 { - grpsiz = ix - ixs; + groupsize = g.end - g.start; /* - * If the line before the current change group, is equal to - * the last line of the current change group, shift backward - * the group. + * 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: */ - 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; - - /* - * 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). - */ - for (; rchg[ixs - 1]; ixs--); - while (rchgo[--ixo]); - } + end_matching_other = -1; - /* - * 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). - */ - ixref = rchgo[ixo - 1] ? ix: nrec; + /* 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"); /* - * If the first line of the current change group, is equal to - * the line next of the current change group, shift forward - * the group. + * This is this highest that this group can be shifted. + * Record its end index: */ - 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; - - /* - * 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. - */ - 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; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 53265cc55..6e2cd9dda 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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) diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index c3475f411..56513eab7 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -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); diff --git a/tests/config/write.c b/tests/config/write.c index 44131cffa..6687ba1f7 100644 --- a/tests/config/write.c +++ b/tests/config/write.c @@ -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); +} |