From 93b42728952487941bc2b0b78a7f0080b7b3d600 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Tue, 9 Jun 2015 14:38:30 -0700 Subject: [PATCH 001/450] Include stacktrace summary in memory leak output. --- CMakeLists.txt | 1 + src/common.h | 4 + src/global.c | 18 +- src/util.h | 13 +- src/win32/w32_crtdbg_stacktrace.c | 343 ++++++++++++++++++++++++++++++ src/win32/w32_crtdbg_stacktrace.h | 93 ++++++++ src/win32/w32_stack.c | 180 ++++++++++++++++ src/win32/w32_stack.h | 132 ++++++++++++ tests/clar_libgit2_trace.c | 19 ++ tests/main.c | 19 -- tests/trace/windows/stacktrace.c | 151 +++++++++++++ 11 files changed, 947 insertions(+), 26 deletions(-) create mode 100644 src/win32/w32_crtdbg_stacktrace.c create mode 100644 src/win32/w32_crtdbg_stacktrace.h create mode 100644 src/win32/w32_stack.c create mode 100644 src/win32/w32_stack.h create mode 100644 tests/trace/windows/stacktrace.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c55ddd1a..994eed5e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -336,6 +336,7 @@ IF (MSVC) IF (MSVC_CRTDBG) SET(CRT_FLAG_DEBUG "${CRT_FLAG_DEBUG} /DGIT_MSVC_CRTDBG") + SET(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES}" "Dbghelp.lib") ENDIF() # /Zi - Create debugging information diff --git a/src/common.h b/src/common.h index cdb0b514d..2b1dedc01 100644 --- a/src/common.h +++ b/src/common.h @@ -46,6 +46,10 @@ # ifdef GIT_THREADS # include "win32/pthread.h" # endif +# if defined(GIT_MSVC_CRTDBG) +# include "win32/w32_stack.h" +# include "win32/w32_crtdbg_stacktrace.h" +# endif #else diff --git a/src/global.c b/src/global.c index 3f20bfd31..fc6337adc 100644 --- a/src/global.c +++ b/src/global.c @@ -11,7 +11,10 @@ #include "git2/global.h" #include "git2/sys/openssl.h" #include "thread-utils.h" - +#if defined(GIT_MSVC_CRTDBG) +#include "win32/w32_stack.h" +#include "win32/w32_crtdbg_stacktrace.h" +#endif git_mutex git__mwindow_mutex; @@ -225,6 +228,11 @@ int git_libgit2_init(void) /* Only do work on a 0 -> 1 transition of the refcount */ if ((ret = git_atomic_inc(&git__n_inits)) == 1) { +#if defined(GIT_MSVC_CRTDBG) + git_win32__crtdbg_stacktrace_init(); + git_win32__stack_init(); +#endif + if (synchronized_threads_init() < 0) ret = -1; } @@ -254,9 +262,15 @@ int git_libgit2_shutdown(void) while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); } /* Only do work on a 1 -> 0 transition of the refcount */ - if ((ret = git_atomic_dec(&git__n_inits)) == 0) + if ((ret = git_atomic_dec(&git__n_inits)) == 0) { synchronized_threads_shutdown(); +#if defined(GIT_MSVC_CRTDBG) + git_win32__crtdbg_stacktrace_cleanup(); + git_win32__stack_cleanup(); +#endif + } + /* Exit the lock */ InterlockedExchange(&_mutex, 0); diff --git a/src/util.h b/src/util.h index b2abbe6a6..8c336e3ef 100644 --- a/src/util.h +++ b/src/util.h @@ -35,6 +35,7 @@ */ #include #include +#include "win32/w32_crtdbg_stacktrace.h" #endif #include "common.h" @@ -62,23 +63,24 @@ #define CONST_STRLEN(x) ((sizeof(x)/sizeof(x[0])) - 1) #if defined(GIT_MSVC_CRTDBG) + GIT_INLINE(void *) git__crtdbg__malloc(size_t len, const char *file, int line) { - void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, file, line); + void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line); if (!ptr) giterr_set_oom(); return ptr; } GIT_INLINE(void *) git__crtdbg__calloc(size_t nelem, size_t elsize, const char *file, int line) { - void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, file, line); + void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line); if (!ptr) giterr_set_oom(); return ptr; } GIT_INLINE(char *) git__crtdbg__strdup(const char *str, const char *file, int line) { - char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, file, line); + char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line); if (!ptr) giterr_set_oom(); return ptr; } @@ -118,7 +120,7 @@ GIT_INLINE(char *) git__crtdbg__substrdup(const char *start, size_t n, const cha GIT_INLINE(void *) git__crtdbg__realloc(void *ptr, size_t size, const char *file, int line) { - void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, file, line); + void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line); if (!new_ptr) giterr_set_oom(); return new_ptr; } @@ -126,8 +128,9 @@ GIT_INLINE(void *) git__crtdbg__realloc(void *ptr, size_t size, const char *file GIT_INLINE(void *) git__crtdbg__reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line) { size_t newsize; + return GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize) ? - NULL : _realloc_dbg(ptr, newsize, _NORMAL_BLOCK, file, line); + NULL : _realloc_dbg(ptr, newsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line); } GIT_INLINE(void *) git__crtdbg__mallocarray(size_t nelem, size_t elsize, const char *file, int line) diff --git a/src/win32/w32_crtdbg_stacktrace.c b/src/win32/w32_crtdbg_stacktrace.c new file mode 100644 index 000000000..a778f4164 --- /dev/null +++ b/src/win32/w32_crtdbg_stacktrace.c @@ -0,0 +1,343 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#if defined(GIT_MSVC_CRTDBG) +#include "w32_stack.h" +#include "w32_crtdbg_stacktrace.h" + +#define CRTDBG_STACKTRACE__UID_LEN (15) + +/** + * The stacktrace of an allocation can be distilled + * to a unique id based upon the stackframe pointers + * and ignoring any size arguments. We will use these + * UIDs as the (char const*) __FILE__ argument we + * give to the CRT malloc routines. + */ +typedef struct { + char uid[CRTDBG_STACKTRACE__UID_LEN + 1]; +} git_win32__crtdbg_stacktrace__uid; + +/** + * All mallocs with the same stacktrace will be de-duped + * and aggregated into this row. + */ +typedef struct { + git_win32__crtdbg_stacktrace__uid uid; /* must be first */ + git_win32__stack__raw_data raw_data; + unsigned int count_allocs; /* times this alloc signature seen since init */ + unsigned int count_allocs_at_last_checkpoint; /* times since last mark */ + unsigned int transient_count_leaks; /* sum of leaks */ +} git_win32__crtdbg_stacktrace__row; + +static CRITICAL_SECTION g_crtdbg_stacktrace_cs; + +/** + * CRTDBG memory leak tracking takes a "char const * const file_name" + * and stores the pointer in the heap data (instead of allocing a copy + * for itself). Normally, this is not a problem, since we usually pass + * in __FILE__. But I'm going to lie to it and pass in the address of + * the UID in place of the file_name. Also, I do not want to alloc the + * stacktrace data (because we are called from inside our alloc routines). + * Therefore, I'm creating a very large static pool array to store row + * data. This also eliminates the temptation to realloc it (and move the + * UID pointers). + * + * And to efficiently look for duplicates we need an index on the rows + * so we can bsearch it. Again, without mallocing. + * + * If we observe more than MY_ROW_LIMIT unique malloc signatures, we + * fall through and use the traditional __FILE__ processing and don't + * try to de-dup them. If your testing hits this limit, just increase + * it and try again. + */ + +#define MY_ROW_LIMIT (1024 * 1024) +static git_win32__crtdbg_stacktrace__row g_cs_rows[MY_ROW_LIMIT]; +static git_win32__crtdbg_stacktrace__row *g_cs_index[MY_ROW_LIMIT]; + +static unsigned int g_cs_end = MY_ROW_LIMIT; +static unsigned int g_cs_ins = 0; /* insertion point == unique allocs seen */ +static unsigned int g_count_total_allocs = 0; /* number of allocs seen */ +static unsigned int g_transient_count_total_leaks = 0; /* number of total leaks */ +static unsigned int g_transient_count_dedup_leaks = 0; /* number of unique leaks */ +static bool g_limit_reached = false; /* had allocs after we filled row table */ + +static unsigned int g_checkpoint_id = 0; /* to better label leak checkpoints */ +static bool g_transient_leaks_since_mark = false; /* payload for hook */ + +/** + * Compare function for bsearch on g_cs_index table. + */ +static int row_cmp(const void *v1, const void *v2) +{ + git_win32__stack__raw_data *d1 = (git_win32__stack__raw_data*)v1; + git_win32__crtdbg_stacktrace__row *r2 = (git_win32__crtdbg_stacktrace__row *)v2; + + return (git_win32__stack_compare(d1, &r2->raw_data)); +} + +/** + * Unique insert the new data into the row and index tables. + * We have to sort by the stackframe data itself, not the uid. + */ +static git_win32__crtdbg_stacktrace__row * insert_unique( + const git_win32__stack__raw_data *pdata) +{ + size_t pos; + if (git__bsearch(g_cs_index, g_cs_ins, pdata, row_cmp, &pos) < 0) { + /* Append new unique item to row table. */ + memcpy(&g_cs_rows[g_cs_ins].raw_data, pdata, sizeof(*pdata)); + sprintf(g_cs_rows[g_cs_ins].uid.uid, "##%08lx", g_cs_ins); + + /* Insert pointer to it into the proper place in the index table. */ + if (pos < g_cs_ins) + memmove(&g_cs_index[pos+1], &g_cs_index[pos], (g_cs_ins - pos)*sizeof(g_cs_index[0])); + g_cs_index[pos] = &g_cs_rows[g_cs_ins]; + + g_cs_ins++; + } + + g_cs_index[pos]->count_allocs++; + + return g_cs_index[pos]; +} + +/** + * Hook function to receive leak data from the CRT. (This includes + * both ":()" data, but also each of the + * various headers and fields. + * + * Scan this for the special "##" UID forms that we substituted + * for the "". Map back to the row data and + * increment its leak count. + * + * See https://msdn.microsoft.com/en-us/library/74kabxyx.aspx + * + * We suppress the actual crtdbg output. + */ +static int __cdecl report_hook(int nRptType, char *szMsg, int *retVal) +{ + static int hook_result = TRUE; /* FALSE to get stock dump; TRUE to suppress. */ + unsigned int pos; + + *retVal = 0; /* do not invoke debugger */ + + if ((szMsg[0] != '#') || (szMsg[1] != '#')) + return hook_result; + + if (sscanf(&szMsg[2], "%08lx", &pos) < 1) + return hook_result; + if (pos >= g_cs_ins) + return hook_result; + + if (g_transient_leaks_since_mark) { + if (g_cs_rows[pos].count_allocs == g_cs_rows[pos].count_allocs_at_last_checkpoint) + return hook_result; + } + + g_cs_rows[pos].transient_count_leaks++; + + if (g_cs_rows[pos].transient_count_leaks == 1) + g_transient_count_dedup_leaks++; + + g_transient_count_total_leaks++; + + return hook_result; +} + +/** + * Write leak data to all of the various places we need. + * We force the caller to sprintf() the message first + * because we want to avoid fprintf() because it allocs. + */ +static void my_output(const char *buf) +{ + fwrite(buf, strlen(buf), 1, stderr); + OutputDebugString(buf); +} + +/** + * For each row with leaks, dump a stacktrace for it. + */ +static void dump_summary(const char *label) +{ + unsigned int k; + char buf[10 * 1024]; + + if (g_transient_count_total_leaks == 0) + return; + + fflush(stdout); + fflush(stderr); + my_output("\n"); + + if (g_limit_reached) { + sprintf(buf, + "LEAK SUMMARY: de-dup row table[%d] filled. Increase MY_ROW_LIMIT.\n", + MY_ROW_LIMIT); + my_output(buf); + } + + if (!label) + label = ""; + + if (g_transient_leaks_since_mark) { + sprintf(buf, "LEAK CHECKPOINT %d: leaks %d unique %d: %s\n", + g_checkpoint_id, g_transient_count_total_leaks, g_transient_count_dedup_leaks, label); + my_output(buf); + } else { + sprintf(buf, "LEAK SUMMARY: TOTAL leaks %d de-duped %d: %s\n", + g_transient_count_total_leaks, g_transient_count_dedup_leaks, label); + my_output(buf); + } + my_output("\n"); + + for (k = 0; k < g_cs_ins; k++) { + if (g_cs_rows[k].transient_count_leaks > 0) { + sprintf(buf, "LEAK: %s leaked %d of %d times:\n", + g_cs_rows[k].uid.uid, + g_cs_rows[k].transient_count_leaks, + g_cs_rows[k].count_allocs); + my_output(buf); + + if (git_win32__stack_format( + buf, sizeof(buf), &g_cs_rows[k].raw_data, + NULL, NULL) >= 0) { + my_output(buf); + } + + my_output("\n"); + } + } + + fflush(stderr); +} + +void git_win32__crtdbg_stacktrace_init(void) +{ + InitializeCriticalSection(&g_crtdbg_stacktrace_cs); + + EnterCriticalSection(&g_crtdbg_stacktrace_cs); + + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); + + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE); + + _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); + _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + + LeaveCriticalSection(&g_crtdbg_stacktrace_cs); +} + +int git_win32__crtdbg_stacktrace__dump( + git_win32__crtdbg_stacktrace_options opt, + const char *label) +{ + _CRT_REPORT_HOOK old; + unsigned int k; + int r = 0; + +#define IS_BIT_SET(o,b) (((o) & (b)) != 0) + + bool b_set_mark = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK); + bool b_leaks_since_mark = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK); + bool b_leaks_total = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL); + bool b_quiet = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__QUIET); + + if (b_leaks_since_mark && b_leaks_total) { + giterr_set(GITERR_INVALID, "Cannot combine LEAKS_SINCE_MARK and LEAKS_TOTAL."); + return GIT_ERROR; + } + if (!b_set_mark && !b_leaks_since_mark && !b_leaks_total) { + giterr_set(GITERR_INVALID, "Nothing to do."); + return GIT_ERROR; + } + + EnterCriticalSection(&g_crtdbg_stacktrace_cs); + + if (b_leaks_since_mark || b_leaks_total) { + /* All variables with "transient" in the name are per-dump counters + * and reset before each dump. This lets us handle checkpoints. + */ + g_transient_count_total_leaks = 0; + g_transient_count_dedup_leaks = 0; + for (k = 0; k < g_cs_ins; k++) { + g_cs_rows[k].transient_count_leaks = 0; + } + } + + g_transient_leaks_since_mark = b_leaks_since_mark; + + old = _CrtSetReportHook(report_hook); + _CrtDumpMemoryLeaks(); + _CrtSetReportHook(old); + + if (b_leaks_since_mark || b_leaks_total) { + r = g_transient_count_dedup_leaks; + + if (!b_quiet) + dump_summary(label); + } + + if (b_set_mark) { + for (k = 0; k < g_cs_ins; k++) { + g_cs_rows[k].count_allocs_at_last_checkpoint = g_cs_rows[k].count_allocs; + } + + g_checkpoint_id++; + } + + LeaveCriticalSection(&g_crtdbg_stacktrace_cs); + + return r; +} + +void git_win32__crtdbg_stacktrace_cleanup(void) +{ + /* At shutdown/cleanup, dump cummulative leak info + * with everything since startup. This might generate + * extra noise if the caller has been doing checkpoint + * dumps, but it might also eliminate some false + * positives for resources previously reported during + * checkpoints. + */ + git_win32__crtdbg_stacktrace__dump( + GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL, + "CLEANUP"); + + DeleteCriticalSection(&g_crtdbg_stacktrace_cs); +} + +const char *git_win32__crtdbg_stacktrace(int skip, const char *file) +{ + git_win32__stack__raw_data new_data; + git_win32__crtdbg_stacktrace__row *row; + const char * result = file; + + if (git_win32__stack_capture(&new_data, skip+1) < 0) + return result; + + EnterCriticalSection(&g_crtdbg_stacktrace_cs); + + if (g_cs_ins < g_cs_end) { + row = insert_unique(&new_data); + result = row->uid.uid; + } else { + g_limit_reached = true; + } + + g_count_total_allocs++; + + LeaveCriticalSection(&g_crtdbg_stacktrace_cs); + + return result; +} +#endif diff --git a/src/win32/w32_crtdbg_stacktrace.h b/src/win32/w32_crtdbg_stacktrace.h new file mode 100644 index 000000000..40ca60d53 --- /dev/null +++ b/src/win32/w32_crtdbg_stacktrace.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_w32_crtdbg_stacktrace_h__ +#define INCLUDE_w32_crtdbg_stacktrace_h__ + +#if defined(GIT_MSVC_CRTDBG) + +/** + * Initialize our memory leak tracking and de-dup data structures. + * This should ONLY be called by git_libgit2_init(). + */ +void git_win32__crtdbg_stacktrace_init(void); + +/** + * Shutdown our memory leak tracking and dump summary data. + * This should ONLY be called by git_libgit2_shutdown(). + * + * We explicitly call _CrtDumpMemoryLeaks() during here so + * that we can compute summary data for the leaks. We print + * the stacktrace of each unique leak. + * + * This cleanup does not happen if the app calls exit() + * without calling the libgit2 shutdown code. + * + * This info we print here is independent of any automatic + * reporting during exit() caused by _CRTDBG_LEAK_CHECK_DF. + * Set it in your app if you also want traditional reporting. + */ +void git_win32__crtdbg_stacktrace_cleanup(void); + +/** + * Checkpoint options. + */ +typedef enum git_win32__crtdbg_stacktrace_options { + /** + * Set checkpoint marker. + */ + GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK = (1 << 0), + + /** + * Dump leaks since last checkpoint marker. + * May not be combined with __LEAKS_TOTAL. + * + * Note that this may generate false positives for global TLS + * error state and other global caches that aren't cleaned up + * until the thread/process terminates. So when using this + * around a region of interest, also check the final (at exit) + * dump before digging into leaks reported here. + */ + GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK = (1 << 1), + + /** + * Dump leaks since init. May not be combined + * with __LEAKS_SINCE_MARK. + */ + GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL = (1 << 2), + + /** + * Suppress printing during dumps. + * Just return leak count. + */ + GIT_WIN32__CRTDBG_STACKTRACE__QUIET = (1 << 3), + +} git_win32__crtdbg_stacktrace_options; + +/** + * Checkpoint memory state and/or dump unique stack traces of + * current memory leaks. + * + * @return number of unique leaks (relative to requested starting + * point) or error. + */ +GIT_EXTERN(int) git_win32__crtdbg_stacktrace__dump( + git_win32__crtdbg_stacktrace_options opt, + const char *label); + +/** + * Construct stacktrace and append it to the global buffer. + * Return pointer to start of this string. On any error or + * lack of buffer space, just return the given file buffer + * so it will behave as usual. + * + * This should ONLY be called by our internal memory allocations + * routines. + */ +const char *git_win32__crtdbg_stacktrace(int skip, const char *file); + +#endif +#endif diff --git a/src/win32/w32_stack.c b/src/win32/w32_stack.c new file mode 100644 index 000000000..f1e589531 --- /dev/null +++ b/src/win32/w32_stack.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#if defined(GIT_MSVC_CRTDBG) +#include "Windows.h" +#include "Dbghelp.h" +#include "win32/posix.h" +#include "w32_stack.h" +#include "hash.h" + +/** + * This is supposedly defined in WinBase.h (from Windows.h) but there were linker issues. + */ +USHORT WINAPI RtlCaptureStackBackTrace(ULONG, ULONG, PVOID*, PULONG); + +static bool g_win32_stack_initialized = false; +static HANDLE g_win32_stack_process = INVALID_HANDLE_VALUE; +static git_win32__stack__aux_cb_alloc g_aux_cb_alloc = NULL; +static git_win32__stack__aux_cb_lookup g_aux_cb_lookup = NULL; + +int git_win32__stack__set_aux_cb( + git_win32__stack__aux_cb_alloc cb_alloc, + git_win32__stack__aux_cb_lookup cb_lookup) +{ + g_aux_cb_alloc = cb_alloc; + g_aux_cb_lookup = cb_lookup; + + return 0; +} + +void git_win32__stack_init(void) +{ + if (!g_win32_stack_initialized) { + g_win32_stack_process = GetCurrentProcess(); + SymSetOptions(SYMOPT_LOAD_LINES); + SymInitialize(g_win32_stack_process, NULL, TRUE); + g_win32_stack_initialized = true; + } +} + +void git_win32__stack_cleanup(void) +{ + if (g_win32_stack_initialized) { + SymCleanup(g_win32_stack_process); + g_win32_stack_process = INVALID_HANDLE_VALUE; + g_win32_stack_initialized = false; + } +} + +int git_win32__stack_capture(git_win32__stack__raw_data *pdata, int skip) +{ + if (!g_win32_stack_initialized) { + giterr_set(GITERR_INVALID, "git_win32_stack not initialized."); + return GIT_ERROR; + } + + memset(pdata, 0, sizeof(*pdata)); + pdata->nr_frames = RtlCaptureStackBackTrace( + skip+1, GIT_WIN32__STACK__MAX_FRAMES, pdata->frames, NULL); + + /* If an "aux" data provider was registered, ask it to capture + * whatever data it needs and give us an "aux_id" to it so that + * we can refer to it later when reporting. + */ + if (g_aux_cb_alloc) + (g_aux_cb_alloc)(&pdata->aux_id); + + return 0; +} + +int git_win32__stack_compare( + git_win32__stack__raw_data *d1, + git_win32__stack__raw_data *d2) +{ + return memcmp(d1, d2, sizeof(d1)); +} + +int git_win32__stack_format( + char *pbuf, int buf_len, + const git_win32__stack__raw_data *pdata, + const char *prefix, const char *suffix) +{ +#define MY_MAX_FILENAME 255 + + /* SYMBOL_INFO has char FileName[1] at the end. The docs say to + * to malloc it with extra space for your desired max filename. + * We use a union to do the same thing without mallocing. + */ + struct { + SYMBOL_INFO symbol; + char extra[MY_MAX_FILENAME + 1]; + } s; + + IMAGEHLP_LINE64 line; + int buf_used = 0; + unsigned int k; + char detail[MY_MAX_FILENAME * 2]; /* filename plus space for function name and formatting */ + int detail_len; + + if (!g_win32_stack_initialized) { + giterr_set(GITERR_INVALID, "git_win32_stack not initialized."); + return GIT_ERROR; + } + + if (!prefix) + prefix = "\t"; + if (!suffix) + suffix = "\n"; + + memset(pbuf, 0, buf_len); + + memset(&s, 0, sizeof(s)); + s.symbol.MaxNameLen = MY_MAX_FILENAME; + s.symbol.SizeOfStruct = sizeof(SYMBOL_INFO); + + memset(&line, 0, sizeof(line)); + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + + for (k=0; k < pdata->nr_frames; k++) { + DWORD64 frame_k = (DWORD64)pdata->frames[k]; + DWORD dwUnused; + + if (SymFromAddr(g_win32_stack_process, frame_k, 0, &s.symbol) && + SymGetLineFromAddr64(g_win32_stack_process, frame_k, &dwUnused, &line)) { + const char *pslash; + const char *pfile; + + pslash = strrchr(line.FileName, '\\'); + pfile = ((pslash) ? (pslash+1) : line.FileName); + p_snprintf(detail, sizeof(detail), "%s%s:%d> %s%s", + prefix, pfile, line.LineNumber, s.symbol.Name, suffix); + } else { + /* This happens when we cross into another module. + * For example, in CLAR tests, this is typically + * the CRT startup code. Just print an unknown + * frame and continue. + */ + p_snprintf(detail, sizeof(detail), "%s??%s", prefix, suffix); + } + detail_len = strlen(detail); + + if (buf_len < (buf_used + detail_len + 1)) { + /* we don't have room for this frame in the buffer, so just stop. */ + break; + } + + memcpy(&pbuf[buf_used], detail, detail_len); + buf_used += detail_len; + } + + /* If an "aux" data provider was registered, ask it to append its detailed + * data to the end of ours using the "aux_id" it gave us when this de-duped + * item was created. + */ + if (g_aux_cb_lookup) + (g_aux_cb_lookup)(pdata->aux_id, &pbuf[buf_used], (buf_len - buf_used - 1)); + + return GIT_OK; +} + +int git_win32__stack( + char * pbuf, int buf_len, + int skip, + const char *prefix, const char *suffix) +{ + git_win32__stack__raw_data data; + int error; + + if ((error = git_win32__stack_capture(&data, skip)) < 0) + return error; + if ((error = git_win32__stack_format(pbuf, buf_len, &data, prefix, suffix)) < 0) + return error; + return 0; +} + +#endif diff --git a/src/win32/w32_stack.h b/src/win32/w32_stack.h new file mode 100644 index 000000000..b457d2e8a --- /dev/null +++ b/src/win32/w32_stack.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#ifndef INCLUDE_w32_stack_h__ +#define INCLUDE_w32_stack_h__ + +#if defined(GIT_MSVC_CRTDBG) + +/** + * This type defines a callback to be used to augment a C stacktrace + * with "aux" data. This can be used, for example, to allow LibGit2Sharp + * (or other interpreted consumer libraries) to give us C# stacktrace + * data for the PInvoke. + * + * This callback will be called during crtdbg-instrumented allocs. + * + * @param aux_id [out] A returned "aux_id" representing a unique + * (de-duped at the C# layer) stacktrace. + */ +typedef void (*git_win32__stack__aux_cb_alloc)(unsigned int *aux_id); + +/** + * This type defines a callback to be used to augment the output of + * a stacktrace. This will be used to request the C# layer format + * the C# stacktrace associated with "aux_id" into the provided + * buffer. + * + * This callback will be called during leak reporting. + * + * @param aux_id The "aux_id" key associated with a stacktrace. + * @param aux_msg A buffer where a formatted message should be written. + * @param aux_msg_len The size of the buffer. + */ +typedef void (*git_win32__stack__aux_cb_lookup)(unsigned int aux_id, char *aux_msg, unsigned int aux_msg_len); + +/** + * Register an "aux" data provider to augment our C stacktrace data. + * + * This can be used, for example, to allow LibGit2Sharp (or other + * interpreted consumer libraries) to give us the C# stacktrace of + * the PInvoke. + * + * If you choose to use this feature, it should be registered during + * initialization and not changed for the duration of the process. + */ +GIT_EXTERN(int) git_win32__stack__set_aux_cb( + git_win32__stack__aux_cb_alloc cb_alloc, + git_win32__stack__aux_cb_lookup cb_lookup); + +/** + * Maximum number of stackframes to record for a + * single stacktrace. + */ +#define GIT_WIN32__STACK__MAX_FRAMES 30 + +/** + * Wrapper containing the raw unprocessed stackframe + * data for a single stacktrace and any "aux_id". + */ +typedef struct { + void *frames[GIT_WIN32__STACK__MAX_FRAMES]; + unsigned int nr_frames; + unsigned int aux_id; +} git_win32__stack__raw_data; + + +/** + * Load symbol table data. This should be done in the primary + * thread at startup (under a lock if there are other threads + * active). + */ +void git_win32__stack_init(void); + +/** + * Cleanup symbol table data. This should be done in the + * primary thead at shutdown (under a lock if there are other + * threads active). + */ +void git_win32__stack_cleanup(void); + + +/** + * Capture raw stack trace data for the current process/thread. + * + * @param skip Number of initial frames to skip. Pass 0 to + * begin with the caller of this routine. Pass 1 to begin + * with its caller. And so on. + */ +int git_win32__stack_capture(git_win32__stack__raw_data *pdata, int skip); + +/** + * Compare 2 raw stacktraces with the usual -1,0,+1 result. + * This includes any "aux_id" values in the comparison, so that + * our de-dup is also "aux" context relative. + */ +int git_win32__stack_compare( + git_win32__stack__raw_data *d1, + git_win32__stack__raw_data *d2); + +/** + * Format raw stacktrace data into buffer WITHOUT using any mallocs. + * + * @param prefix String written before each frame; defaults to "\t". + * @param suffix String written after each frame; defaults to "\n". + */ +int git_win32__stack_format( + char *pbuf, int buf_len, + const git_win32__stack__raw_data *pdata, + const char *prefix, const char *suffix); + +/** + * Convenience routine to capture and format stacktrace into + * a buffer WITHOUT using any mallocs. This is primarily a + * wrapper for testing. + * + * @param skip Number of initial frames to skip. Pass 0 to + * begin with the caller of this routine. Pass 1 to begin + * with its caller. And so on. + * @param prefix String written before each frame; defaults to "\t". + * @param suffix String written after each frame; defaults to "\n". + */ +int git_win32__stack( + char * pbuf, int buf_len, + int skip, + const char *prefix, const char *suffix); + +#endif /* GIT_MSVC_CRTDBG */ +#endif /* INCLUDE_w32_stack_h__ */ diff --git a/tests/clar_libgit2_trace.c b/tests/clar_libgit2_trace.c index ae582d1cb..aaeeb7810 100644 --- a/tests/clar_libgit2_trace.c +++ b/tests/clar_libgit2_trace.c @@ -142,9 +142,28 @@ void _cl_trace_cb__event_handler( switch (ev) { case CL_TRACE__SUITE_BEGIN: git_trace(GIT_TRACE_TRACE, "\n\n%s\n%s: Begin Suite", HR, suite_name); +#if 0 && defined(GIT_MSVC_CRTDBG) + git_win32__crtdbg_stacktrace__dump( + GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK, + suite_name); +#endif break; case CL_TRACE__SUITE_END: +#if 0 && defined(GIT_MSVC_CRTDBG) + /* As an example of checkpointing, dump leaks within this suite. + * This may generate false positives for things like the global + * TLS error state and maybe the odb cache since they aren't + * freed until the global shutdown and outside the scope of this + * set of tests. + * + * This may under-report if the test itself uses a checkpoint. + * See tests/trace/windows/stacktrace.c + */ + git_win32__crtdbg_stacktrace__dump( + GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK, + suite_name); +#endif git_trace(GIT_TRACE_TRACE, "\n\n%s: End Suite\n%s", suite_name, HR); break; diff --git a/tests/main.c b/tests/main.c index 56326da1c..f67c8ffbc 100644 --- a/tests/main.c +++ b/tests/main.c @@ -1,10 +1,3 @@ - -#if defined(GIT_MSVC_CRTDBG) -/* Enable MSVC CRTDBG memory leak reporting. See src/util.h for details. */ -#include -#include -#endif - #include "clar_libgit2.h" #include "clar_libgit2_trace.h" @@ -16,18 +9,6 @@ int main(int argc, char *argv[]) { int res; -#if defined(GIT_MSVC_CRTDBG) - _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); - - _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE); - _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE); - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE); - - _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); - _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); - _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); -#endif - clar_test_init(argc, argv); git_libgit2_init(); diff --git a/tests/trace/windows/stacktrace.c b/tests/trace/windows/stacktrace.c new file mode 100644 index 000000000..c00c1b774 --- /dev/null +++ b/tests/trace/windows/stacktrace.c @@ -0,0 +1,151 @@ +#include "clar_libgit2.h" +#include "win32/w32_stack.h" + +#if defined(GIT_MSVC_CRTDBG) +static void a(void) +{ + char buf[10000]; + + cl_assert(git_win32__stack(buf, sizeof(buf), 0, NULL, NULL) == 0); + +#if 0 + fprintf(stderr, "Stacktrace from [%s:%d]:\n%s\n", __FILE__, __LINE__, buf); +#endif +} + +static void b(void) +{ + a(); +} + +static void c(void) +{ + b(); +} +#endif + +void test_trace_windows_stacktrace__basic(void) +{ +#if defined(GIT_MSVC_CRTDBG) + c(); +#endif +} + + +void test_trace_windows_stacktrace__leaks(void) +{ +#if defined(GIT_MSVC_CRTDBG) + void * p1; + void * p2; + void * p3; + void * p4; + int before, after; + int leaks; + int error; + + /* remember outstanding leaks due to set setup + * and set mark/checkpoint. + */ + before = git_win32__crtdbg_stacktrace__dump( + GIT_WIN32__CRTDBG_STACKTRACE__QUIET | + GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL | + GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK, + NULL); + + p1 = git__malloc(5); + leaks = git_win32__crtdbg_stacktrace__dump( + GIT_WIN32__CRTDBG_STACKTRACE__QUIET | + GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK, + "p1"); + cl_assert((leaks == 1)); + + p2 = git__malloc(5); + leaks = git_win32__crtdbg_stacktrace__dump( + GIT_WIN32__CRTDBG_STACKTRACE__QUIET | + GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK, + "p1,p2"); + cl_assert((leaks == 2)); + + p3 = git__malloc(5); + leaks = git_win32__crtdbg_stacktrace__dump( + GIT_WIN32__CRTDBG_STACKTRACE__QUIET | + GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK, + "p1,p2,p3"); + cl_assert((leaks == 3)); + + git__free(p2); + leaks = git_win32__crtdbg_stacktrace__dump( + GIT_WIN32__CRTDBG_STACKTRACE__QUIET | + GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK, + "p1,p3"); + cl_assert((leaks == 2)); + + /* move the mark. only new leaks should appear afterwards */ + error = git_win32__crtdbg_stacktrace__dump( + GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK, + NULL); + cl_assert((error == 0)); + + leaks = git_win32__crtdbg_stacktrace__dump( + GIT_WIN32__CRTDBG_STACKTRACE__QUIET | + GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK, + "not_p1,not_p3"); + cl_assert((leaks == 0)); + + p4 = git__malloc(5); + leaks = git_win32__crtdbg_stacktrace__dump( + GIT_WIN32__CRTDBG_STACKTRACE__QUIET | + GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK, + "p4,not_p1,not_p3"); + cl_assert((leaks == 1)); + + git__free(p1); + git__free(p3); + leaks = git_win32__crtdbg_stacktrace__dump( + GIT_WIN32__CRTDBG_STACKTRACE__QUIET | + GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK, + "p4"); + cl_assert((leaks == 1)); + + git__free(p4); + leaks = git_win32__crtdbg_stacktrace__dump( + GIT_WIN32__CRTDBG_STACKTRACE__QUIET | + GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK, + "end"); + cl_assert((leaks == 0)); + + /* confirm current absolute leaks count matches beginning value. */ + after = git_win32__crtdbg_stacktrace__dump( + GIT_WIN32__CRTDBG_STACKTRACE__QUIET | + GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL, + "total"); + cl_assert((before == after)); +#endif +} + +#if defined(GIT_MSVC_CRTDBG) +static void aux_cb_alloc__1(unsigned int *aux_id) +{ + static unsigned int aux_counter = 0; + + *aux_id = aux_counter++; +} + +static void aux_cb_lookup__1(unsigned int aux_id, char *aux_msg, unsigned int aux_msg_len) +{ + p_snprintf(aux_msg, aux_msg_len, "\tQQ%08x\n", aux_id); +} + +#endif + +void test_trace_windows_stacktrace__aux1(void) +{ +#if defined(GIT_MSVC_CRTDBG) + git_win32__stack__set_aux_cb(aux_cb_alloc__1, aux_cb_lookup__1); + c(); + c(); + c(); + c(); + git_win32__stack__set_aux_cb(NULL, NULL); +#endif +} From 827b954ef400f3c488500ad6c8a57246223dde9c Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Sun, 28 Jun 2015 06:56:02 -0400 Subject: [PATCH 002/450] Reserve aux_id 0; sort leaks by aux_id. Fix cmp. --- src/win32/w32_stack.c | 26 +++++++++++++++++++------- src/win32/w32_stack.h | 12 +++++++++--- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/win32/w32_stack.c b/src/win32/w32_stack.c index f1e589531..15af3dcb7 100644 --- a/src/win32/w32_stack.c +++ b/src/win32/w32_stack.c @@ -76,7 +76,7 @@ int git_win32__stack_compare( git_win32__stack__raw_data *d1, git_win32__stack__raw_data *d2) { - return memcmp(d1, d2, sizeof(d1)); + return memcmp(d1, d2, sizeof(*d1)); } int git_win32__stack_format( @@ -88,7 +88,6 @@ int git_win32__stack_format( /* SYMBOL_INFO has char FileName[1] at the end. The docs say to * to malloc it with extra space for your desired max filename. - * We use a union to do the same thing without mallocing. */ struct { SYMBOL_INFO symbol; @@ -152,12 +151,25 @@ int git_win32__stack_format( buf_used += detail_len; } - /* If an "aux" data provider was registered, ask it to append its detailed - * data to the end of ours using the "aux_id" it gave us when this de-duped - * item was created. + /* "aux_id" 0 is reserved to mean no aux data. This is needed to handle + * allocs that occur before the aux callbacks were registered. */ - if (g_aux_cb_lookup) - (g_aux_cb_lookup)(pdata->aux_id, &pbuf[buf_used], (buf_len - buf_used - 1)); + if (pdata->aux_id > 0) { + p_snprintf(detail, sizeof(detail), "%saux_id: %d%s", + prefix, pdata->aux_id, suffix); + detail_len = strlen(detail); + if ((buf_used + detail_len + 1) < buf_len) { + memcpy(&pbuf[buf_used], detail, detail_len); + buf_used += detail_len; + } + + /* If an "aux" data provider is still registered, ask it to append its detailed + * data to the end of ours using the "aux_id" it gave us when this de-duped + * item was created. + */ + if (g_aux_cb_lookup) + (g_aux_cb_lookup)(pdata->aux_id, &pbuf[buf_used], (buf_len - buf_used - 1)); + } return GIT_OK; } diff --git a/src/win32/w32_stack.h b/src/win32/w32_stack.h index b457d2e8a..21170bd2f 100644 --- a/src/win32/w32_stack.h +++ b/src/win32/w32_stack.h @@ -19,7 +19,8 @@ * This callback will be called during crtdbg-instrumented allocs. * * @param aux_id [out] A returned "aux_id" representing a unique - * (de-duped at the C# layer) stacktrace. + * (de-duped at the C# layer) stacktrace. "aux_id" 0 is reserved + * to mean no aux stacktrace data. */ typedef void (*git_win32__stack__aux_cb_alloc)(unsigned int *aux_id); @@ -60,11 +61,16 @@ GIT_EXTERN(int) git_win32__stack__set_aux_cb( /** * Wrapper containing the raw unprocessed stackframe * data for a single stacktrace and any "aux_id". + * + * I put the aux_id first so leaks will be sorted by it. + * So, for example, if a specific callstack in C# leaks + * a repo handle, all of the pointers within the associated + * repo pointer will be grouped together. */ typedef struct { - void *frames[GIT_WIN32__STACK__MAX_FRAMES]; - unsigned int nr_frames; unsigned int aux_id; + unsigned int nr_frames; + void *frames[GIT_WIN32__STACK__MAX_FRAMES]; } git_win32__stack__raw_data; From ccef5adb63bdba7f5182aec9f0bdc83a2887d9d1 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Latour Date: Tue, 30 Jun 2015 09:30:20 -0700 Subject: [PATCH 003/450] Added git_diff_index_to_index() --- include/git2/diff.h | 19 +++++++++++++++++++ src/diff.c | 25 +++++++++++++++++++++++++ tests/diff/index.c | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) diff --git a/include/git2/diff.h b/include/git2/diff.h index b3ab5397e..0abbc7f06 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -835,6 +835,25 @@ GIT_EXTERN(int) git_diff_tree_to_workdir_with_index( git_tree *old_tree, const git_diff_options *opts); /**< can be NULL for defaults */ +/** + * Create a diff with the difference between two index objects. + * + * The first index will be used for the "old_file" side of the delta and the + * second index will be used for the "new_file" side of the delta. + * + * @param diff Output pointer to a git_diff pointer to be allocated. + * @param repo The repository containing the indexes. + * @param old_index A git_index object to diff from. + * @param new_index A git_index object to diff to. + * @param opts Structure with options to influence diff or NULL for defaults. + */ +GIT_EXTERN(int) git_diff_index_to_index( + git_diff **diff, + git_repository *repo, + git_index *old_index, + git_index *new_index, + const git_diff_options *opts); /**< can be NULL for defaults */ + /** * Merge one diff into another. * diff --git a/src/diff.c b/src/diff.c index c1adcc662..44f627880 100644 --- a/src/diff.c +++ b/src/diff.c @@ -1421,6 +1421,31 @@ int git_diff_tree_to_workdir_with_index( return error; } +int git_diff_index_to_index( + git_diff **diff, + git_repository *repo, + git_index *old_index, + git_index *new_index, + const git_diff_options *opts) +{ + int error = 0; + + assert(diff && old_index && new_index); + + DIFF_FROM_ITERATORS( + git_iterator_for_index( + &a, old_index, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx), + git_iterator_for_index( + &b, new_index, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx) + ); + + /* if index is in case-insensitive order, re-sort deltas to match */ + if (!error && (old_index->ignore_case || new_index->ignore_case)) + diff_set_ignore_case(*diff, true); + + return error; +} + size_t git_diff_num_deltas(const git_diff *diff) { assert(diff); diff --git a/tests/diff/index.c b/tests/diff/index.c index f702568bf..df45ad236 100644 --- a/tests/diff/index.c +++ b/tests/diff/index.c @@ -268,3 +268,35 @@ void test_diff_index__not_in_head_conflicted(void) git_index_free(index); git_tree_free(a); } + +void test_diff_index__to_index(void) +{ + const char *a_commit = "26a125ee1bf"; /* the current HEAD */ + git_tree *old_tree; + git_index *old_index; + git_index *new_index; + git_diff *diff; + diff_expects exp; + + cl_git_pass(git_index_new(&old_index)); + old_tree = resolve_commit_oid_to_tree(g_repo, a_commit); + cl_git_pass(git_index_read_tree(old_index, old_tree)); + + cl_git_pass(git_repository_index(&new_index, g_repo)); + + cl_git_pass(git_diff_index_to_index(&diff, g_repo, old_index, new_index, NULL)); + + memset(&exp, 0, sizeof(diff_expects)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(8, exp.files); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_CONFLICTED]); + + git_diff_free(diff); + git_index_free(new_index); + git_index_free(old_index); + git_tree_free(old_tree); +} From 63924435a103d34315ad9a4851d84b2b052aca38 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 1 Jul 2015 09:40:11 -0500 Subject: [PATCH 004/450] filters: custom filters with wildcard attributes Allow custom filters with wildcard attributes, so that clients can support some random `filter=foo` in a .gitattributes and look up the corresponding smudge/clean commands in the configuration file. --- CHANGELOG.md | 4 + include/git2/sys/filter.h | 5 +- src/filter.c | 7 +- tests/filter/custom.c | 107 +------------------- tests/filter/custom_helpers.c | 108 ++++++++++++++++++++ tests/filter/custom_helpers.h | 18 ++++ tests/filter/wildcard.c | 181 ++++++++++++++++++++++++++++++++++ 7 files changed, 323 insertions(+), 107 deletions(-) create mode 100644 tests/filter/custom_helpers.c create mode 100644 tests/filter/custom_helpers.h create mode 100644 tests/filter/wildcard.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 852000bba..735f1fe27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,10 @@ support for HTTPS connections insead of OpenSSL. * If libcurl is installed, we will use it to connect to HTTP(S) servers. +* Custom filters can now be registered with wildcard attributes, for + example `filter=*`. Consumers should examine the attributes parameter + of the `check` function for details. + ### API additions * The `git_merge_options` gained a `file_flags` member. diff --git a/include/git2/sys/filter.h b/include/git2/sys/filter.h index 5fd8d5566..baf1515d6 100644 --- a/include/git2/sys/filter.h +++ b/include/git2/sys/filter.h @@ -240,7 +240,10 @@ typedef void (*git_filter_cleanup_fn)( * for this filter (e.g. "eol crlf text"). If the attribute name is bare, * it will be simply loaded and passed to the `check` callback. If it has * a value (i.e. "name=value"), the attribute must match that value for - * the filter to be applied. + * the filter to be applied. The value may be a wildcard (eg, "name=*"), + * in which case the filter will be invoked for any value for the given + * attribute name. See the attribute parameter of the `check` callback + * for the attribute value that was specified. * * The `initialize`, `shutdown`, `check`, `apply`, and `cleanup` callbacks * are all documented above with the respective function pointer typedefs. diff --git a/src/filter.c b/src/filter.c index e25d37c35..70c4fa382 100644 --- a/src/filter.c +++ b/src/filter.c @@ -433,8 +433,11 @@ static int filter_list_check_attributes( want_type = git_attr_value(want); found_type = git_attr_value(strs[i]); - if (want_type != found_type || - (want_type == GIT_ATTR_VALUE_T && strcmp(want, strs[i]))) + if (want_type != found_type) + error = GIT_ENOTFOUND; + else if (want_type == GIT_ATTR_VALUE_T && + strcmp(want, strs[i]) && + strcmp(want, "*")) error = GIT_ENOTFOUND; } diff --git a/tests/filter/custom.c b/tests/filter/custom.c index 493d26c80..fd1cd271c 100644 --- a/tests/filter/custom.c +++ b/tests/filter/custom.c @@ -5,6 +5,7 @@ #include "buf_text.h" #include "git2/sys/filter.h" #include "git2/sys/repository.h" +#include "custom_helpers.h" /* going TO_WORKDIR, filters are executed low to high * going TO_ODB, filters are executed high to low @@ -12,8 +13,6 @@ #define BITFLIP_FILTER_PRIORITY -1 #define REVERSE_FILTER_PRIORITY -2 -#define VERY_SECURE_ENCRYPTION(b) ((b) ^ 0xff) - #ifdef GIT_WIN32 # define NEWLINE "\r\n" #else @@ -27,6 +26,8 @@ static char workdir_data[] = "trivially" NEWLINE "scrambled." NEWLINE; +#define REVERSED_DATA_LEN 51 + /* Represents the data above scrambled (bits flipped) after \r\n -> \n * conversion, then bytewise reversed */ @@ -63,107 +64,6 @@ void test_filter_custom__cleanup(void) g_repo = NULL; } -static int bitflip_filter_apply( - git_filter *self, - void **payload, - git_buf *to, - const git_buf *from, - const git_filter_source *source) -{ - const unsigned char *src = (const unsigned char *)from->ptr; - unsigned char *dst; - size_t i; - - GIT_UNUSED(self); GIT_UNUSED(payload); - - /* verify that attribute path match worked as expected */ - cl_assert_equal_i( - 0, git__strncmp("hero", git_filter_source_path(source), 4)); - - if (!from->size) - return 0; - - cl_git_pass(git_buf_grow(to, from->size)); - - dst = (unsigned char *)to->ptr; - - for (i = 0; i < from->size; i++) - dst[i] = VERY_SECURE_ENCRYPTION(src[i]); - - to->size = from->size; - - return 0; -} - -static void bitflip_filter_free(git_filter *f) -{ - git__free(f); -} - -static git_filter *create_bitflip_filter(void) -{ - git_filter *filter = git__calloc(1, sizeof(git_filter)); - cl_assert(filter); - - filter->version = GIT_FILTER_VERSION; - filter->attributes = "+bitflip"; - filter->shutdown = bitflip_filter_free; - filter->apply = bitflip_filter_apply; - - return filter; -} - - -static int reverse_filter_apply( - git_filter *self, - void **payload, - git_buf *to, - const git_buf *from, - const git_filter_source *source) -{ - const unsigned char *src = (const unsigned char *)from->ptr; - const unsigned char *end = src + from->size; - unsigned char *dst; - - GIT_UNUSED(self); GIT_UNUSED(payload); GIT_UNUSED(source); - - /* verify that attribute path match worked as expected */ - cl_assert_equal_i( - 0, git__strncmp("hero", git_filter_source_path(source), 4)); - - if (!from->size) - return 0; - - cl_git_pass(git_buf_grow(to, from->size)); - - dst = (unsigned char *)to->ptr + from->size - 1; - - while (src < end) - *dst-- = *src++; - - to->size = from->size; - - return 0; -} - -static void reverse_filter_free(git_filter *f) -{ - git__free(f); -} - -static git_filter *create_reverse_filter(const char *attrs) -{ - git_filter *filter = git__calloc(1, sizeof(git_filter)); - cl_assert(filter); - - filter->version = GIT_FILTER_VERSION; - filter->attributes = attrs; - filter->shutdown = reverse_filter_free; - filter->apply = reverse_filter_apply; - - return filter; -} - static void register_custom_filters(void) { static int filters_registered = 0; @@ -186,7 +86,6 @@ static void register_custom_filters(void) } } - void test_filter_custom__to_odb(void) { git_filter_list *fl; diff --git a/tests/filter/custom_helpers.c b/tests/filter/custom_helpers.c new file mode 100644 index 000000000..2c80212be --- /dev/null +++ b/tests/filter/custom_helpers.c @@ -0,0 +1,108 @@ +#include "clar_libgit2.h" +#include "posix.h" +#include "filter.h" +#include "buf_text.h" +#include "git2/sys/filter.h" + +#define VERY_SECURE_ENCRYPTION(b) ((b) ^ 0xff) + +int bitflip_filter_apply( + git_filter *self, + void **payload, + git_buf *to, + const git_buf *from, + const git_filter_source *source) +{ + const unsigned char *src = (const unsigned char *)from->ptr; + unsigned char *dst; + size_t i; + + GIT_UNUSED(self); GIT_UNUSED(payload); + + /* verify that attribute path match worked as expected */ + cl_assert_equal_i( + 0, git__strncmp("hero", git_filter_source_path(source), 4)); + + if (!from->size) + return 0; + + cl_git_pass(git_buf_grow(to, from->size)); + + dst = (unsigned char *)to->ptr; + + for (i = 0; i < from->size; i++) + dst[i] = VERY_SECURE_ENCRYPTION(src[i]); + + to->size = from->size; + + return 0; +} + +static void bitflip_filter_free(git_filter *f) +{ + git__free(f); +} + +git_filter *create_bitflip_filter(void) +{ + git_filter *filter = git__calloc(1, sizeof(git_filter)); + cl_assert(filter); + + filter->version = GIT_FILTER_VERSION; + filter->attributes = "+bitflip"; + filter->shutdown = bitflip_filter_free; + filter->apply = bitflip_filter_apply; + + return filter; +} + + +int reverse_filter_apply( + git_filter *self, + void **payload, + git_buf *to, + const git_buf *from, + const git_filter_source *source) +{ + const unsigned char *src = (const unsigned char *)from->ptr; + const unsigned char *end = src + from->size; + unsigned char *dst; + + GIT_UNUSED(self); GIT_UNUSED(payload); GIT_UNUSED(source); + + /* verify that attribute path match worked as expected */ + cl_assert_equal_i( + 0, git__strncmp("hero", git_filter_source_path(source), 4)); + + if (!from->size) + return 0; + + cl_git_pass(git_buf_grow(to, from->size)); + + dst = (unsigned char *)to->ptr + from->size - 1; + + while (src < end) + *dst-- = *src++; + + to->size = from->size; + + return 0; +} + +static void reverse_filter_free(git_filter *f) +{ + git__free(f); +} + +git_filter *create_reverse_filter(const char *attrs) +{ + git_filter *filter = git__calloc(1, sizeof(git_filter)); + cl_assert(filter); + + filter->version = GIT_FILTER_VERSION; + filter->attributes = attrs; + filter->shutdown = reverse_filter_free; + filter->apply = reverse_filter_apply; + + return filter; +} diff --git a/tests/filter/custom_helpers.h b/tests/filter/custom_helpers.h new file mode 100644 index 000000000..13cfb23ae --- /dev/null +++ b/tests/filter/custom_helpers.h @@ -0,0 +1,18 @@ +#include "git2/sys/filter.h" + +extern git_filter *create_bitflip_filter(void); +extern git_filter *create_reverse_filter(const char *attr); + +extern int bitflip_filter_apply( + git_filter *self, + void **payload, + git_buf *to, + const git_buf *from, + const git_filter_source *source); + +extern int reverse_filter_apply( + git_filter *self, + void **payload, + git_buf *to, + const git_buf *from, + const git_filter_source *source); diff --git a/tests/filter/wildcard.c b/tests/filter/wildcard.c new file mode 100644 index 000000000..ba826b5dc --- /dev/null +++ b/tests/filter/wildcard.c @@ -0,0 +1,181 @@ +#include "clar_libgit2.h" +#include "posix.h" +#include "blob.h" +#include "filter.h" +#include "buf_text.h" +#include "git2/sys/filter.h" +#include "git2/sys/repository.h" +#include "custom_helpers.h" + +static git_repository *g_repo = NULL; + +static git_filter *create_wildcard_filter(); + +#define DATA_LEN 32 + +static unsigned char input[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, +}; + +static unsigned char reversed[] = { + 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, + 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, +}; + +static unsigned char flipped[] = { + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, + 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, + 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0, +}; + +void test_filter_wildcard__initialize(void) +{ + cl_git_pass(git_filter_register( + "wildcard", create_wildcard_filter(), GIT_FILTER_DRIVER_PRIORITY)); + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + + cl_git_rewritefile( + "empty_standard_repo/.gitattributes", + "* binary\n" + "hero-flip-* filter=wcflip\n" + "hero-reverse-* filter=wcreverse\n" + "none-* filter=unregistered\n"); +} + +void test_filter_wildcard__cleanup(void) +{ + cl_git_pass(git_filter_unregister("wildcard")); + + cl_git_sandbox_cleanup(); + g_repo = NULL; +} + +static int wildcard_filter_check( + git_filter *self, + void **payload, + const git_filter_source *src, + const char **attr_values) +{ + if (strcmp(attr_values[0], "wcflip") == 0 || + strcmp(attr_values[0], "wcreverse") == 0) { + *payload = git__strdup(attr_values[0]); + GITERR_CHECK_ALLOC(*payload); + return 0; + } + + return GIT_PASSTHROUGH; +} + +static int wildcard_filter_apply( + git_filter *self, + void **payload, + git_buf *to, + const git_buf *from, + const git_filter_source *source) +{ + const char *filtername = *payload; + + if (filtername && strcmp(filtername, "wcflip") == 0) + return bitflip_filter_apply(self, payload, to, from, source); + else if (filtername && strcmp(filtername, "wcreverse") == 0) + return reverse_filter_apply(self, payload, to, from, source); + + cl_fail("Unexpected attribute"); + return GIT_PASSTHROUGH; +} + +static int wildcard_filter_cleanup(git_filter *self, void *payload) +{ + git__free(payload); + return 0; +} + +static void wildcard_filter_free(git_filter *f) +{ + git__free(f); +} + +static git_filter *create_wildcard_filter() +{ + git_filter *filter = git__calloc(1, sizeof(git_filter)); + cl_assert(filter); + + filter->version = GIT_FILTER_VERSION; + filter->attributes = "filter=*"; + filter->check = wildcard_filter_check; + filter->apply = wildcard_filter_apply; + filter->cleanup = wildcard_filter_cleanup; + filter->shutdown = wildcard_filter_free; + + return filter; +} + +void test_filter_wildcard__reverse(void) +{ + git_filter_list *fl; + git_buf in = GIT_BUF_INIT, out = GIT_BUF_INIT; + + cl_git_pass(git_filter_list_load( + &fl, g_repo, NULL, "hero-reverse-foo", GIT_FILTER_TO_ODB, 0)); + + cl_git_pass(git_buf_put(&in, input, DATA_LEN)); + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + + cl_assert_equal_i(DATA_LEN, out.size); + + cl_assert_equal_i( + 0, memcmp(reversed, out.ptr, out.size)); + + git_filter_list_free(fl); + git_buf_free(&out); + git_buf_free(&in); +} + +void test_filter_wildcard__flip(void) +{ + git_filter_list *fl; + git_buf in = GIT_BUF_INIT, out = GIT_BUF_INIT; + + cl_git_pass(git_filter_list_load( + &fl, g_repo, NULL, "hero-flip-foo", GIT_FILTER_TO_ODB, 0)); + + cl_git_pass(git_buf_put(&in, input, DATA_LEN)); + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + + cl_assert_equal_i(DATA_LEN, out.size); + + cl_assert_equal_i( + 0, memcmp(flipped, out.ptr, out.size)); + + git_filter_list_free(fl); + git_buf_free(&out); + git_buf_free(&in); +} + +void test_filter_wildcard__none(void) +{ + git_filter_list *fl; + git_buf in = GIT_BUF_INIT, out = GIT_BUF_INIT; + + cl_git_pass(git_filter_list_load( + &fl, g_repo, NULL, "none-foo", GIT_FILTER_TO_ODB, 0)); + + cl_git_pass(git_buf_put(&in, input, DATA_LEN)); + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + + cl_assert_equal_i(DATA_LEN, out.size); + + cl_assert_equal_i( + 0, memcmp(input, out.ptr, out.size)); + + git_filter_list_free(fl); + git_buf_free(&out); + git_buf_free(&in); +} From e069c621bdd62e603b048eb536f5a978a905b310 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 2 Jul 2015 09:25:48 -0500 Subject: [PATCH 005/450] git__getenv: utf-8 aware env reader Introduce `git__getenv` which is a UTF-8 aware `getenv` everywhere. Make `cl_getenv` use this to keep consistent memory handling around return values (free everywhere, as opposed to only some platforms). --- src/remote.c | 27 +++++++---- src/sysdir.c | 30 ++++++++---- src/util.c | 48 +++++++++++++++++++ src/util.h | 5 ++ src/win32/posix_w32.c | 2 + tests/clar_libgit2.c | 53 +++++++++++---------- tests/clar_libgit2.h | 1 + tests/core/env.c | 20 ++++---- tests/core/ftruncate.c | 2 +- tests/filter/stream.c | 2 +- tests/online/clone.c | 103 +++++++++++++++++++++-------------------- tests/online/push.c | 23 ++++++--- tests/repo/init.c | 2 +- 13 files changed, 204 insertions(+), 114 deletions(-) diff --git a/src/remote.c b/src/remote.c index f31fc150a..d31e1b89e 100644 --- a/src/remote.c +++ b/src/remote.c @@ -153,7 +153,7 @@ static int get_check_cert(int *out, git_repository *repo) * most specific to least specific. */ /* GIT_SSL_NO_VERIFY environment variable */ - if ((val = getenv("GIT_SSL_NO_VERIFY")) != NULL) + if ((val = p_getenv("GIT_SSL_NO_VERIFY")) != NULL) return git_config_parse_bool(out, val); /* http.sslVerify config setting */ @@ -759,7 +759,7 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur { git_config *cfg; git_config_entry *ce = NULL; - const char *val = NULL; + git_buf val = GIT_BUF_INIT; int error; assert(remote); @@ -789,7 +789,7 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur return error; if (ce && ce->value) { - val = ce->value; + *proxy_url = git__strdup(ce->value); goto found; } } @@ -797,19 +797,28 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur /* http.proxy config setting */ if ((error = git_config__lookup_entry(&ce, cfg, "http.proxy", false)) < 0) return error; + if (ce && ce->value) { - val = ce->value; + *proxy_url = git__strdup(ce->value); goto found; } /* HTTP_PROXY / HTTPS_PROXY environment variables */ - val = use_ssl ? getenv("HTTPS_PROXY") : getenv("HTTP_PROXY"); + error = git__getenv(&val, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY"); + + if (error < 0) { + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + } + + return error; + } + + *proxy_url = git_buf_detach(&val); found: - if (val && val[0]) { - *proxy_url = git__strdup(val); - GITERR_CHECK_ALLOC(*proxy_url); - } + GITERR_CHECK_ALLOC(*proxy_url); git_config_entry_free(ce); return 0; diff --git a/src/sysdir.c b/src/sysdir.c index cd94a8b57..2795de491 100644 --- a/src/sysdir.c +++ b/src/sysdir.c @@ -29,7 +29,14 @@ static int git_sysdir_guess_global_dirs(git_buf *out) #ifdef GIT_WIN32 return git_win32__find_global_dirs(out); #else - return git_buf_sets(out, getenv("HOME")); + int error = git__getenv(out, "HOME"); + + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + } + + return error; #endif } @@ -38,15 +45,22 @@ static int git_sysdir_guess_xdg_dirs(git_buf *out) #ifdef GIT_WIN32 return git_win32__find_xdg_dirs(out); #else - const char *env = NULL; + git_buf env = GIT_BUF_INIT; + int error; - if ((env = getenv("XDG_CONFIG_HOME")) != NULL) - return git_buf_joinpath(out, env, "git"); - else if ((env = getenv("HOME")) != NULL) - return git_buf_joinpath(out, env, ".config/git"); + if ((error = git__getenv(&env, "XDG_CONFIG_HOME")) == 0) + error = git_buf_joinpath(out, env.ptr, "git"); - git_buf_clear(out); - return 0; + if (error == GIT_ENOTFOUND && (error = git__getenv(&env, "HOME")) == 0) + error = git_buf_joinpath(out, env.ptr, ".config/git"); + + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + } + + git_buf_free(&env); + return error; #endif } diff --git a/src/util.c b/src/util.c index c62826420..b08b2b884 100644 --- a/src/util.c +++ b/src/util.c @@ -10,6 +10,10 @@ #include #include "posix.h" +#ifdef GIT_WIN32 +# include "win32/buffer.h" +#endif + #ifdef _MSC_VER # include #endif @@ -765,3 +769,47 @@ int git__utf8_iterate(const uint8_t *str, int str_len, int32_t *dst) *dst = uc; return length; } + +#ifdef GIT_WIN32 +int git__getenv(git_buf *out, const char *name) +{ + wchar_t *wide_name = NULL, *wide_value = NULL; + DWORD value_len; + int error = -1; + + git_buf_clear(out); + + if (git__utf8_to_16_alloc(&wide_name, name) < 0) + return -1; + + if ((value_len = GetEnvironmentVariableW(wide_name, NULL, 0)) > 0) { + wide_value = git__malloc(value_len * sizeof(wchar_t)); + GITERR_CHECK_ALLOC(wide_value); + + value_len = GetEnvironmentVariableW(wide_name, wide_value, value_len); + } + + if (value_len) + error = git_buf_put_w(out, wide_value, value_len); + else if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) + error = GIT_ENOTFOUND; + else + giterr_set(GITERR_OS, "could not read environment variable '%s'", name); + + git__free(wide_name); + git__free(wide_value); + return error; +} +#else +int git__getenv(git_buf *out, const char *name) +{ + const char *val = getenv(name); + + git_buf_clear(out); + + if (!val) + return GIT_ENOTFOUND; + + return git_buf_puts(out, val); +} +#endif diff --git a/src/util.h b/src/util.h index b2abbe6a6..458b0db41 100644 --- a/src/util.h +++ b/src/util.h @@ -7,6 +7,9 @@ #ifndef INCLUDE_util_h__ #define INCLUDE_util_h__ +#include "git2/buffer.h" +#include "buffer.h" + #if defined(GIT_MSVC_CRTDBG) /* Enable MSVC CRTDBG memory leak reporting. * @@ -596,4 +599,6 @@ GIT_INLINE(double) git__timer(void) #endif +extern int git__getenv(git_buf *out, const char *name); + #endif /* INCLUDE_util_h__ */ diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index 504562b0e..c909af6cc 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -11,6 +11,8 @@ #include "utf-conv.h" #include "repository.h" #include "reparse.h" +#include "global.h" +#include "buffer.h" #include #include #include diff --git a/tests/clar_libgit2.c b/tests/clar_libgit2.c index dabc47a09..f73fc5b00 100644 --- a/tests/clar_libgit2.c +++ b/tests/clar_libgit2.c @@ -58,31 +58,38 @@ void cl_git_rmfile(const char *filename) cl_must_pass(p_unlink(filename)); } +char *cl_getenv(const char *name) +{ + git_buf out = GIT_BUF_INIT; + int error = git__getenv(&out, name); + + cl_assert(error >= 0 || error == GIT_ENOTFOUND); + + if (error == GIT_ENOTFOUND) + return NULL; + + if (out.size == 0) { + char *dup = git__strdup(""); + cl_assert(dup); + + return dup; + } + + return git_buf_detach(&out); +} + +bool cl_is_env_set(const char *name) +{ + char *env = cl_getenv(name); + bool result = (env != NULL); + git__free(env); + return result; +} + #ifdef GIT_WIN32 #include "win32/utf-conv.h" -char *cl_getenv(const char *name) -{ - wchar_t *wide_name, *wide_value; - char *utf8_value = NULL; - DWORD value_len; - - cl_assert(git__utf8_to_16_alloc(&wide_name, name) >= 0); - - value_len = GetEnvironmentVariableW(wide_name, NULL, 0); - - if (value_len) { - cl_assert(wide_value = git__malloc(value_len * sizeof(wchar_t))); - cl_assert(GetEnvironmentVariableW(wide_name, wide_value, value_len)); - cl_assert(git__utf16_to_8_alloc(&utf8_value, wide_value) >= 0); - git__free(wide_value); - } - - git__free(wide_name); - return utf8_value; -} - int cl_setenv(const char *name, const char *value) { wchar_t *wide_name, *wide_value = NULL; @@ -138,10 +145,6 @@ int cl_rename(const char *source, const char *dest) #else #include -char *cl_getenv(const char *name) -{ - return getenv(name); -} int cl_setenv(const char *name, const char *value) { diff --git a/tests/clar_libgit2.h b/tests/clar_libgit2.h index 9ab0da4f6..d7e635302 100644 --- a/tests/clar_libgit2.h +++ b/tests/clar_libgit2.h @@ -119,6 +119,7 @@ bool cl_is_chmod_supported(void); /* Environment wrappers */ char *cl_getenv(const char *name); +bool cl_is_env_set(const char *name); int cl_setenv(const char *name, const char *value); /* Reliable rename */ diff --git a/tests/core/env.c b/tests/core/env.c index 293b786db..ee08258a6 100644 --- a/tests/core/env.c +++ b/tests/core/env.c @@ -30,14 +30,8 @@ static char *home_values[] = { void test_core_env__initialize(void) { int i; - for (i = 0; i < NUM_VARS; ++i) { - const char *original = cl_getenv(env_vars[i]); -#ifdef GIT_WIN32 - env_save[i] = (char *)original; -#else - env_save[i] = original ? git__strdup(original) : NULL; -#endif - } + for (i = 0; i < NUM_VARS; ++i) + env_save[i] = cl_getenv(env_vars[i]); } static void set_global_search_path_from_env(void) @@ -77,12 +71,14 @@ static void setenv_and_check(const char *name, const char *value) char *check; cl_git_pass(cl_setenv(name, value)); - check = cl_getenv(name); - cl_assert_equal_s(value, check); -#ifdef GIT_WIN32 + + if (value) + cl_assert_equal_s(value, check); + else + cl_assert(check == NULL); + git__free(check); -#endif } void test_core_env__0(void) diff --git a/tests/core/ftruncate.c b/tests/core/ftruncate.c index 21981d677..2f4729fc2 100644 --- a/tests/core/ftruncate.c +++ b/tests/core/ftruncate.c @@ -10,7 +10,7 @@ static int fd = -1; void test_core_ftruncate__initialize(void) { - if (!cl_getenv("GITTEST_INVASIVE_FS_SIZE")) + if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE")) cl_skip(); cl_must_pass((fd = p_open(filename, O_CREAT | O_RDWR, 0644))); diff --git a/tests/filter/stream.c b/tests/filter/stream.c index 603f19494..9228911b6 100644 --- a/tests/filter/stream.c +++ b/tests/filter/stream.c @@ -214,7 +214,7 @@ void test_filter_stream__smallfile(void) /* optionally write a 500 MB file through the compression stream */ void test_filter_stream__bigfile(void) { - if (!cl_getenv("GITTEST_INVASIVE_FS_SIZE")) + if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE")) cl_skip(); test_stream(51200); diff --git a/tests/online/clone.c b/tests/online/clone.c index e63cf55f1..225b3abe2 100644 --- a/tests/online/clone.c +++ b/tests/online/clone.c @@ -17,6 +17,15 @@ static git_repository *g_repo; static git_clone_options g_options; +static char *_remote_url = NULL; +static char *_remote_user = NULL; +static char *_remote_pass = NULL; +static char *_remote_ssh_pubkey = NULL; +static char *_remote_ssh_privkey = NULL; +static char *_remote_ssh_passphrase = NULL; +static char *_remote_ssh_fingerprint = NULL; + + void test_online_clone__initialize(void) { git_checkout_options dummy_opts = GIT_CHECKOUT_OPTIONS_INIT; @@ -29,6 +38,14 @@ void test_online_clone__initialize(void) g_options.checkout_opts = dummy_opts; g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; g_options.fetch_opts = dummy_fetch; + + _remote_url = cl_getenv("GITTEST_REMOTE_URL"); + _remote_user = cl_getenv("GITTEST_REMOTE_USER"); + _remote_pass = cl_getenv("GITTEST_REMOTE_PASS"); + _remote_ssh_pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY"); + _remote_ssh_privkey = cl_getenv("GITTEST_REMOTE_SSH_KEY"); + _remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE"); + _remote_ssh_fingerprint = cl_getenv("GITTEST_REMOTE_SSH_FINGERPRINT"); } void test_online_clone__cleanup(void) @@ -38,6 +55,14 @@ void test_online_clone__cleanup(void) g_repo = NULL; } cl_fixture_cleanup("./foo"); + + git__free(_remote_url); + git__free(_remote_user); + git__free(_remote_pass); + git__free(_remote_ssh_pubkey); + git__free(_remote_ssh_privkey); + git__free(_remote_ssh_passphrase); + git__free(_remote_ssh_fingerprint); } void test_online_clone__network_full(void) @@ -202,15 +227,12 @@ static int cred_failure_cb( void test_online_clone__cred_callback_failure_return_code_is_tunnelled(void) { - const char *remote_url = cl_getenv("GITTEST_REMOTE_URL"); - const char *remote_user = cl_getenv("GITTEST_REMOTE_USER"); - - if (!remote_url || !remote_user) + if (!_remote_url || !_remote_user) clar__skip(); g_options.fetch_opts.callbacks.credentials = cred_failure_cb; - cl_git_fail_with(-172, git_clone(&g_repo, remote_url, "./foo", &g_options)); + cl_git_fail_with(-172, git_clone(&g_repo, _remote_url, "./foo", &g_options)); } static int cred_count_calls_cb(git_cred **cred, const char *url, const char *user, @@ -233,17 +255,15 @@ static int cred_count_calls_cb(git_cred **cred, const char *url, const char *use void test_online_clone__cred_callback_called_again_on_auth_failure(void) { - const char *remote_url = cl_getenv("GITTEST_REMOTE_URL"); - const char *remote_user = cl_getenv("GITTEST_REMOTE_USER"); size_t counter = 0; - if (!remote_url || !remote_user) + if (!_remote_url || !_remote_user) clar__skip(); g_options.fetch_opts.callbacks.credentials = cred_count_calls_cb; g_options.fetch_opts.callbacks.payload = &counter; - cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, remote_url, "./foo", &g_options)); + cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, _remote_url, "./foo", &g_options)); cl_assert_equal_i(3, counter); } @@ -269,22 +289,22 @@ void test_online_clone__credentials(void) /* Remote URL environment variable must be set. * User and password are optional. */ - const char *remote_url = cl_getenv("GITTEST_REMOTE_URL"); git_cred_userpass_payload user_pass = { - cl_getenv("GITTEST_REMOTE_USER"), - cl_getenv("GITTEST_REMOTE_PASS") + _remote_user, + _remote_pass }; - if (!remote_url) return; + if (!_remote_url) + clar__skip(); - if (cl_getenv("GITTEST_REMOTE_DEFAULT")) { + if (cl_is_env_set("GITTEST_REMOTE_DEFAULT")) { g_options.fetch_opts.callbacks.credentials = cred_default; } else { g_options.fetch_opts.callbacks.credentials = git_cred_userpass; g_options.fetch_opts.callbacks.payload = &user_pass; } - cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options)); + cl_git_pass(git_clone(&g_repo, _remote_url, "./foo", &g_options)); git_repository_free(g_repo); g_repo = NULL; cl_fixture_cleanup("./foo"); } @@ -335,18 +355,15 @@ void test_online_clone__can_cancel(void) static int cred_cb(git_cred **cred, const char *url, const char *user_from_url, unsigned int allowed_types, void *payload) { - const char *remote_user = cl_getenv("GITTEST_REMOTE_USER"); - const char *pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY"); - const char *privkey = cl_getenv("GITTEST_REMOTE_SSH_KEY"); - const char *passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE"); - GIT_UNUSED(url); GIT_UNUSED(user_from_url); GIT_UNUSED(payload); if (allowed_types & GIT_CREDTYPE_USERNAME) - return git_cred_username_new(cred, remote_user); + return git_cred_username_new(cred, _remote_user); if (allowed_types & GIT_CREDTYPE_SSH_KEY) - return git_cred_ssh_key_new(cred, remote_user, pubkey, privkey, passphrase); + return git_cred_ssh_key_new(cred, + _remote_user, _remote_ssh_pubkey, + _remote_ssh_privkey, _remote_ssh_passphrase); giterr_set(GITERR_NET, "unexpected cred type"); return -1; @@ -417,13 +434,10 @@ void test_online_clone__ssh_with_paths(void) 2, }; - const char *remote_url = cl_getenv("GITTEST_REMOTE_URL"); - const char *remote_user = cl_getenv("GITTEST_REMOTE_USER"); - #ifndef GIT_SSH clar__skip(); #endif - if (!remote_url || !remote_user || strncmp(remote_url, "ssh://", 5) != 0) + if (!_remote_url || !_remote_user || strncmp(_remote_url, "ssh://", 5) != 0) clar__skip(); g_options.remote_cb = custom_remote_ssh_with_paths; @@ -431,10 +445,10 @@ void test_online_clone__ssh_with_paths(void) g_options.fetch_opts.callbacks.credentials = cred_cb; g_options.fetch_opts.callbacks.payload = &arr; - cl_git_fail(git_clone(&g_repo, remote_url, "./foo", &g_options)); + cl_git_fail(git_clone(&g_repo, _remote_url, "./foo", &g_options)); arr.strings = good_paths; - cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options)); + cl_git_pass(git_clone(&g_repo, _remote_url, "./foo", &g_options)); } static int cred_foo_bar(git_cred **cred, const char *url, const char *username_from_url, @@ -460,15 +474,13 @@ int ssh_certificate_check(git_cert *cert, int valid, const char *host, void *pay { git_cert_hostkey *key; git_oid expected = {{0}}, actual = {{0}}; - const char *expected_str; GIT_UNUSED(valid); GIT_UNUSED(payload); - expected_str = cl_getenv("GITTEST_REMOTE_SSH_FINGERPRINT"); - cl_assert(expected_str); + cl_assert(_remote_ssh_fingerprint); - cl_git_pass(git_oid_fromstrp(&expected, expected_str)); + cl_git_pass(git_oid_fromstrp(&expected, _remote_ssh_fingerprint)); cl_assert_equal_i(GIT_CERT_HOSTKEY_LIBSSH2, cert->cert_type); key = (git_cert_hostkey *) cert; @@ -477,9 +489,9 @@ int ssh_certificate_check(git_cert *cert, int valid, const char *host, void *pay * the type. Here we abuse the fact that both hashes fit into * our git_oid type. */ - if (strlen(expected_str) == 32 && key->type & GIT_CERT_SSH_MD5) { + if (strlen(_remote_ssh_fingerprint) == 32 && key->type & GIT_CERT_SSH_MD5) { memcpy(&actual.id, key->hash_md5, 16); - } else if (strlen(expected_str) == 40 && key->type & GIT_CERT_SSH_SHA1) { + } else if (strlen(_remote_ssh_fingerprint) == 40 && key->type & GIT_CERT_SSH_SHA1) { memcpy(&actual, key->hash_sha1, 20); } else { cl_fail("Cannot find a usable SSH hash"); @@ -496,7 +508,7 @@ void test_online_clone__ssh_cert(void) { g_options.fetch_opts.callbacks.certificate_check = ssh_certificate_check; - if (!cl_getenv("GITTEST_REMOTE_SSH_FINGERPRINT")) + if (!_remote_ssh_fingerprint) cl_skip(); cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, "ssh://localhost/foo", "./foo", &g_options)); @@ -525,22 +537,17 @@ static char *read_key_file(const char *path) static int ssh_memory_cred_cb(git_cred **cred, const char *url, const char *user_from_url, unsigned int allowed_types, void *payload) { - const char *remote_user = cl_getenv("GITTEST_REMOTE_USER"); - const char *pubkey_path = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY"); - const char *privkey_path = cl_getenv("GITTEST_REMOTE_SSH_KEY"); - const char *passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE"); - GIT_UNUSED(url); GIT_UNUSED(user_from_url); GIT_UNUSED(payload); if (allowed_types & GIT_CREDTYPE_USERNAME) - return git_cred_username_new(cred, remote_user); + return git_cred_username_new(cred, _remote_user); if (allowed_types & GIT_CREDTYPE_SSH_KEY) { - char *pubkey = read_key_file(pubkey_path); - char *privkey = read_key_file(privkey_path); + char *pubkey = read_key_file(_remote_ssh_pubkey); + char *privkey = read_key_file(_remote_ssh_privkey); - int ret = git_cred_ssh_key_memory_new(cred, remote_user, pubkey, privkey, passphrase); + int ret = git_cred_ssh_key_memory_new(cred, _remote_user, pubkey, privkey, _remote_ssh_passphrase); if (privkey) free(privkey); @@ -555,19 +562,15 @@ static int ssh_memory_cred_cb(git_cred **cred, const char *url, const char *user void test_online_clone__ssh_memory_auth(void) { - const char *remote_url = cl_getenv("GITTEST_REMOTE_URL"); - const char *remote_user = cl_getenv("GITTEST_REMOTE_USER"); - const char *privkey = cl_getenv("GITTEST_REMOTE_SSH_KEY"); - #ifndef GIT_SSH_MEMORY_CREDENTIALS clar__skip(); #endif - if (!remote_url || !remote_user || !privkey || strncmp(remote_url, "ssh://", 5) != 0) + if (!_remote_url || !_remote_user || !_remote_ssh_privkey || strncmp(_remote_url, "ssh://", 5) != 0) clar__skip(); g_options.fetch_opts.callbacks.credentials = ssh_memory_cred_cb; - cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options)); + cl_git_pass(git_clone(&g_repo, _remote_url, "./foo", &g_options)); } void test_online_clone__url_with_no_path_returns_EINVALIDSPEC(void) diff --git a/tests/online/push.c b/tests/online/push.c index 6cd444320..0b0892c97 100644 --- a/tests/online/push.c +++ b/tests/online/push.c @@ -9,16 +9,16 @@ static git_repository *_repo; -static char *_remote_url; +static char *_remote_url = NULL; -static char *_remote_ssh_key; -static char *_remote_ssh_pubkey; -static char *_remote_ssh_passphrase; +static char *_remote_user = NULL; +static char *_remote_pass = NULL; -static char *_remote_user; -static char *_remote_pass; +static char *_remote_ssh_key = NULL; +static char *_remote_ssh_pubkey = NULL; +static char *_remote_ssh_passphrase = NULL; -static char *_remote_default; +static char *_remote_default = NULL; static int cred_acquire_cb(git_cred **, const char *, const char *, unsigned int, void *); @@ -355,6 +355,7 @@ void test_online_push__initialize(void) git_oid_fromstr(&_tag_tag, "eea4f2705eeec2db3813f2430829afce99cd00b5"); /* Remote URL environment variable must be set. User and password are optional. */ + _remote_url = cl_getenv("GITTEST_REMOTE_URL"); _remote_user = cl_getenv("GITTEST_REMOTE_USER"); _remote_pass = cl_getenv("GITTEST_REMOTE_PASS"); @@ -406,6 +407,14 @@ void test_online_push__cleanup(void) git_remote_free(_remote); _remote = NULL; + git__free(_remote_url); + git__free(_remote_user); + git__free(_remote_pass); + git__free(_remote_ssh_key); + git__free(_remote_ssh_pubkey); + git__free(_remote_ssh_passphrase); + git__free(_remote_default); + /* Freed by cl_git_sandbox_cleanup */ _repo = NULL; diff --git a/tests/repo/init.c b/tests/repo/init.c index 525020f5a..929d74180 100644 --- a/tests/repo/init.c +++ b/tests/repo/init.c @@ -713,7 +713,7 @@ void test_repo_init__at_filesystem_root(void) git_buf root = GIT_BUF_INIT; int root_len; - if (!cl_getenv("GITTEST_INVASIVE_FS_STRUCTURE")) + if (!cl_is_env_set("GITTEST_INVASIVE_FS_STRUCTURE")) cl_skip(); root_len = git_path_root(sandbox); From d41b8ed083a3e529a8a16db513087266e4424a63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 6 Jul 2015 18:32:31 +0200 Subject: [PATCH 006/450] travis: update the homebrew db We need to make sure we are asking for the current version of packages, or we might get 404s from the download service. --- script/install-deps-osx.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/script/install-deps-osx.sh b/script/install-deps-osx.sh index c2e0162d8..a0216e9c8 100755 --- a/script/install-deps-osx.sh +++ b/script/install-deps-osx.sh @@ -2,4 +2,5 @@ set -x +brew update brew install libssh2 cmake From a0bdfe32412b687910e809cced2d31d1e76918fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 6 Jul 2015 18:42:39 +0200 Subject: [PATCH 007/450] travis: don't install CMake on OS X Homebrew will error out because it's already installed. --- script/install-deps-osx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/install-deps-osx.sh b/script/install-deps-osx.sh index a0216e9c8..5510379d4 100755 --- a/script/install-deps-osx.sh +++ b/script/install-deps-osx.sh @@ -3,4 +3,4 @@ set -x brew update -brew install libssh2 cmake +brew install libssh2 From febc8c4612b6be1d891f658e50f3f70cc5dc5945 Mon Sep 17 00:00:00 2001 From: Tony Kelman Date: Tue, 7 Jul 2015 06:55:05 -0400 Subject: [PATCH 008/450] Fix undefined reference with old versions of openssl Versions prior to 0.9.8f did not have this function, rhel/centos5 are still on a heavily backported version of 0.9.8e and theoretically supported until March 2017 Without this ifdef, I get the following link failure: ``` CMakeFiles/libgit2_clar.dir/src/openssl_stream.c.o: In function `openssl_connect': openssl_stream.c:(.text+0x45a): undefined reference to `SSL_set_tlsext_host_name' collect2: error: ld returned 1 exit status make[6]: *** [libgit2_clar] Error 1 ``` --- src/openssl_stream.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openssl_stream.c b/src/openssl_stream.c index 958252e9f..4df7c6b7c 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -324,7 +324,9 @@ int openssl_connect(git_stream *stream) SSL_set_bio(st->ssl, bio, bio); /* specify the host in case SNI is needed */ +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME SSL_set_tlsext_host_name(st->ssl, st->host); +#endif if ((ret = SSL_connect(st->ssl)) <= 0) return ssl_set_error(st->ssl, ret); From 01e031d921820efe9fa849d6b6a8848f341f2df4 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 7 Jul 2015 12:40:15 -0500 Subject: [PATCH 009/450] meta: Add Antoine Pelisse to hall-of-fame list Antoine Pelisse has kindly allowed his contributions to core git to be used under the libgit2 license. --- git.git-authors | 1 + 1 file changed, 1 insertion(+) diff --git a/git.git-authors b/git.git-authors index 9131a1fa1..6a85224b4 100644 --- a/git.git-authors +++ b/git.git-authors @@ -39,6 +39,7 @@ ok Adam Simpkins (http transport) ok Adrian Johnson ok Alexey Shumkin ok Andreas Ericsson +ok Antoine Pelisse ok Boyd Lynn Gerber ok Brandon Casey ok Brian Downing From ae8f7260ecbfc64e49d77ac2e86122971a560670 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 7 Jul 2015 16:59:14 -0500 Subject: [PATCH 010/450] merge_files: don't add trailing newlines When invoked with three files that each lack a trailing newline, the merge result should also lack a trailing newline. --- tests/merge/files.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/merge/files.c b/tests/merge/files.c index 7f461abff..2fd90d066 100644 --- a/tests/merge/files.c +++ b/tests/merge/files.c @@ -249,3 +249,42 @@ void test_merge_files__automerge_whitespace_change(void) git_merge_file_result_free(&result); } + +void test_merge_files__doesnt_add_newline(void) +{ + git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT, + ours = GIT_MERGE_FILE_INPUT_INIT, + theirs = GIT_MERGE_FILE_INPUT_INIT; + git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT; + git_merge_file_result result = {0}; + const char *expected = "Zero\n1\n2\n3\n4\n5 XXX\n6 YYY\n7\n8\n9\nTen"; + + ancestor.ptr = "0\n1\n2\n3\n4\n5 XXX\n6YYY\n7\n8\n9\n10"; + ancestor.size = strlen(ancestor.ptr); + ancestor.path = "testfile.txt"; + ancestor.mode = 0100755; + + ours.ptr = "Zero\n1\n2\n3\n4\n5 XXX\n6 YYY\n7\n8\n9\n10"; + ours.size = strlen(ours.ptr); + ours.path = "testfile.txt"; + ours.mode = 0100755; + + theirs.ptr = "0\n1\n2\n3\n4\n5 XXX\n6 YYY\n7\n8\n9\nTen"; + theirs.size = strlen(theirs.ptr); + theirs.path = "testfile.txt"; + theirs.mode = 0100755; + + opts.flags |= GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE; + cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts)); + + cl_assert_equal_i(1, result.automergeable); + + cl_assert_equal_s("testfile.txt", result.path); + cl_assert_equal_i(0100755, result.mode); + + cl_assert_equal_i(strlen(expected), result.len); + cl_assert_equal_strn(expected, result.ptr, result.len); + + git_merge_file_result_free(&result); +} + From 43ce8cb52e86373711e8ec361c3052c8c39e7a2c Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 7 Jul 2015 16:46:20 -0500 Subject: [PATCH 011/450] revert: correct test that added trailing newline --- tests/revert/workdir.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/revert/workdir.c b/tests/revert/workdir.c index 7ccf0f937..9f83bd842 100644 --- a/tests/revert/workdir.c +++ b/tests/revert/workdir.c @@ -334,16 +334,18 @@ void test_revert_workdir__again_after_edit_two(void) cl_assert(merge_test_index(repo_index, merge_index_entries, 3)); cl_git_pass(git_futils_readbuffer(&diff_buf, "revert/file.txt")); - cl_assert(strcmp(diff_buf.ptr, "a\n" \ - "<<<<<<< HEAD\n" \ - "=======\n" \ - "a\n" \ - ">>>>>>> parent of 97e52d5... Revert me\n" \ - "a\n" \ - "a\n" \ - "a\n" \ - "a\n" \ - "ab\n") == 0); + cl_assert_equal_s( + "a\n" \ + "<<<<<<< HEAD\n" \ + "=======\n" \ + "a\n" \ + ">>>>>>> parent of 97e52d5... Revert me\n" \ + "a\n" \ + "a\n" \ + "a\n" \ + "a\n" \ + "ab", + diff_buf.ptr); git_commit_free(revert_commit); git_commit_free(head_commit); From 234ca40a89fe1b2181ef56300f1f1261b5cb2836 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 7 Jul 2015 16:46:48 -0500 Subject: [PATCH 012/450] xdiff: upgrade to core git 2.4.5 Upgrade xdiff to version used in core git 2.4.5 (0df0541). Corrects an issue where an LF is added at EOF while applying an unrelated change (ba31180), cleans up some unused code (be89977 and e5b0662), and provides an improved callback to avoid leaking internal (to xdiff) structures (467d348). This also adds some additional functionality that we do not yet take advantage of, namely the ability to ignore changes whose lines are all blank (36617af). --- src/blame_git.c | 25 +++++++++------------ src/xdiff/xdiff.h | 18 ++++++++++------ src/xdiff/xdiffi.c | 48 +++++++++++++++++++++++++++++++++++++---- src/xdiff/xdiffi.h | 1 + src/xdiff/xemit.c | 49 ++++++++++++++++++++++++++++++++++++------ src/xdiff/xemit.h | 2 +- src/xdiff/xhistogram.c | 2 +- src/xdiff/xmerge.c | 4 ++-- src/xdiff/xpatience.c | 2 +- src/xdiff/xprepare.c | 21 +++++++++--------- src/xdiff/xutils.c | 42 +++++++++++------------------------- src/xdiff/xutils.h | 1 + 12 files changed, 138 insertions(+), 77 deletions(-) diff --git a/src/blame_git.c b/src/blame_git.c index e863efe2e..f481426aa 100644 --- a/src/blame_git.c +++ b/src/blame_git.c @@ -304,21 +304,16 @@ static void blame_chunk( } static int my_emit( - xdfenv_t *xe, - xdchange_t *xscr, - xdemitcb_t *ecb, - xdemitconf_t const *xecfg) + long start_a, long count_a, + long start_b, long count_b, + void *cb_data) { - xdchange_t *xch = xscr; - GIT_UNUSED(xe); - GIT_UNUSED(xecfg); - while (xch) { - blame_chunk_cb_data *d = ecb->priv; - blame_chunk(d->blame, d->tlno, d->plno, xch->i2, d->target, d->parent); - d->plno = xch->i1 + xch->chg1; - d->tlno = xch->i2 + xch->chg2; - xch = xch->next; - } + blame_chunk_cb_data *d = (blame_chunk_cb_data *)cb_data; + + blame_chunk(d->blame, d->tlno, d->plno, start_b, d->target, d->parent); + d->plno = start_a + count_a; + d->tlno = start_b + count_b; + return 0; } @@ -352,7 +347,7 @@ static int diff_hunks(mmfile_t file_a, mmfile_t file_b, void *cb_data) xdemitconf_t xecfg = {0}; xdemitcb_t ecb = {0}; - xecfg.emit_func = (void(*)(void))my_emit; + xecfg.hunk_func = my_emit; ecb.priv = cb_data; trim_common_tail(&file_a, &file_b, 0); diff --git a/src/xdiff/xdiff.h b/src/xdiff/xdiff.h index cb8b235b5..e2f1e892b 100644 --- a/src/xdiff/xdiff.h +++ b/src/xdiff/xdiff.h @@ -32,14 +32,14 @@ extern "C" { #define XDF_IGNORE_WHITESPACE (1 << 2) #define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3) #define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4) -#define XDF_PATIENCE_DIFF (1 << 5) -#define XDF_HISTOGRAM_DIFF (1 << 6) #define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL) -#define XDL_PATCH_NORMAL '-' -#define XDL_PATCH_REVERSE '+' -#define XDL_PATCH_MODEMASK ((1 << 8) - 1) -#define XDL_PATCH_IGNOREBSPACE (1 << 8) +#define XDF_PATIENCE_DIFF (1 << 5) +#define XDF_HISTOGRAM_DIFF (1 << 6) +#define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF) +#define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK) + +#define XDF_IGNORE_BLANK_LINES (1 << 7) #define XDL_EMIT_FUNCNAMES (1 << 0) #define XDL_EMIT_COMMON (1 << 1) @@ -88,13 +88,17 @@ typedef struct s_xdemitcb { typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv); +typedef int (*xdl_emit_hunk_consume_func_t)(long start_a, long count_a, + long start_b, long count_b, + void *cb_data); + typedef struct s_xdemitconf { long ctxlen; long interhunkctxlen; unsigned long flags; find_func_t find_func; void *find_func_priv; - void (*emit_func)(void); + xdl_emit_hunk_consume_func_t hunk_func; } xdemitconf_t; typedef struct s_bdiffparam { diff --git a/src/xdiff/xdiffi.c b/src/xdiff/xdiffi.c index 84aa0fcfe..2358a2d63 100644 --- a/src/xdiff/xdiffi.c +++ b/src/xdiff/xdiffi.c @@ -328,10 +328,10 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdalgoenv_t xenv; diffdata_t dd1, dd2; - if (xpp->flags & XDF_PATIENCE_DIFF) + if (XDF_DIFF_ALG(xpp->flags) == XDF_PATIENCE_DIFF) return xdl_do_patience_diff(mf1, mf2, xpp, xe); - if (xpp->flags & XDF_HISTOGRAM_DIFF) + if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF) return xdl_do_histogram_diff(mf1, mf2, xpp, xe); if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) { @@ -394,6 +394,7 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, xch->i2 = i2; xch->chg1 = chg1; xch->chg2 = chg2; + xch->ignore = 0; return xch; } @@ -538,13 +539,49 @@ void xdl_free_script(xdchange_t *xscr) { } } +static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, + xdemitconf_t const *xecfg) +{ + xdchange_t *xch, *xche; + + for (xch = xscr; xch; xch = xche->next) { + xche = xdl_get_hunk(&xch, xecfg); + if (!xch) + break; + if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1, + xch->i2, xche->i2 + xche->chg2 - xch->i2, + ecb->priv) < 0) + return -1; + } + return 0; +} + +static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags) +{ + xdchange_t *xch; + + for (xch = xscr; xch; xch = xch->next) { + int ignore = 1; + xrecord_t **rec; + long i; + + rec = &xe->xdf1.recs[xch->i1]; + for (i = 0; i < xch->chg1 && ignore; i++) + ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags); + + rec = &xe->xdf2.recs[xch->i2]; + for (i = 0; i < xch->chg2 && ignore; i++) + ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags); + + xch->ignore = ignore; + } +} int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb) { xdchange_t *xscr; xdfenv_t xe; - emit_func_t ef = xecfg->emit_func ? - (emit_func_t)xecfg->emit_func : xdl_emit_diff; + emit_func_t ef = xecfg->hunk_func ? xdl_call_hunk_func : xdl_emit_diff; if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) { @@ -558,6 +595,9 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, return -1; } if (xscr) { + if (xpp->flags & XDF_IGNORE_BLANK_LINES) + xdl_mark_ignorable(xscr, &xe, xpp->flags); + if (ef(&xe, xscr, ecb, xecfg) < 0) { xdl_free_script(xscr); diff --git a/src/xdiff/xdiffi.h b/src/xdiff/xdiffi.h index 7a92ea9c4..8b81206c9 100644 --- a/src/xdiff/xdiffi.h +++ b/src/xdiff/xdiffi.h @@ -41,6 +41,7 @@ typedef struct s_xdchange { struct s_xdchange *next; long i1, i2; long chg1, chg2; + int ignore; } xdchange_t; diff --git a/src/xdiff/xemit.c b/src/xdiff/xemit.c index e3e63d902..750a97294 100644 --- a/src/xdiff/xemit.c +++ b/src/xdiff/xemit.c @@ -56,16 +56,51 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t * /* * Starting at the passed change atom, find the latest change atom to be included * inside the differential hunk according to the specified configuration. + * Also advance xscr if the first changes must be discarded. */ -xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) { - xdchange_t *xch, *xchp; +xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) +{ + xdchange_t *xch, *xchp, *lxch; long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen; + long max_ignorable = xecfg->ctxlen; + unsigned long ignored = 0; /* number of ignored blank lines */ - for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next) - if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common) + /* remove ignorable changes that are too far before other changes */ + for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) { + xch = xchp->next; + + if (xch == NULL || + xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable) + *xscr = xch; + } + + if (*xscr == NULL) + return NULL; + + lxch = *xscr; + + for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) { + long distance = xch->i1 - (xchp->i1 + xchp->chg1); + if (distance > max_common) break; - return xchp; + if (distance < max_ignorable && (!xch->ignore || lxch == xchp)) { + lxch = xch; + ignored = 0; + } else if (distance < max_ignorable && xch->ignore) { + ignored += xch->chg2; + } else if (lxch != xchp && + xch->i1 + ignored - (lxch->i1 + lxch->chg1) > max_common) { + break; + } else if (!xch->ignore) { + lxch = xch; + ignored = 0; + } else { + ignored += xch->chg2; + } + } + + return lxch; } @@ -144,7 +179,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, return xdl_emit_common(xe, xscr, ecb, xecfg); for (xch = xscr; xch; xch = xche->next) { - xche = xdl_get_hunk(xch, xecfg); + xche = xdl_get_hunk(&xch, xecfg); + if (!xch) + break; s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0); s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0); diff --git a/src/xdiff/xemit.h b/src/xdiff/xemit.h index c2e2e8302..d29710770 100644 --- a/src/xdiff/xemit.h +++ b/src/xdiff/xemit.h @@ -27,7 +27,7 @@ typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, xdemitconf_t const *xecfg); -xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg); +xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg); int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, xdemitconf_t const *xecfg); diff --git a/src/xdiff/xhistogram.c b/src/xdiff/xhistogram.c index c84812893..500d8112a 100644 --- a/src/xdiff/xhistogram.c +++ b/src/xdiff/xhistogram.c @@ -258,7 +258,7 @@ static int fall_back_to_classic_diff(struct histindex *index, int line1, int count1, int line2, int count2) { xpparam_t xpp; - xpp.flags = index->xpp->flags & ~XDF_HISTOGRAM_DIFF; + xpp.flags = index->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK; return xdl_fall_back_diff(index->env, &xpp, line1, count1, line2, count2); diff --git a/src/xdiff/xmerge.c b/src/xdiff/xmerge.c index 84e424672..b11e59817 100644 --- a/src/xdiff/xmerge.c +++ b/src/xdiff/xmerge.c @@ -245,11 +245,11 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1, dest ? dest + size : NULL); /* Postimage from side #1 */ if (m->mode & 1) - size += xdl_recs_copy(xe1, m->i1, m->chg1, 1, + size += xdl_recs_copy(xe1, m->i1, m->chg1, (m->mode & 2), dest ? dest + size : NULL); /* Postimage from side #2 */ if (m->mode & 2) - size += xdl_recs_copy(xe2, m->i2, m->chg2, 1, + size += xdl_recs_copy(xe2, m->i2, m->chg2, 0, dest ? dest + size : NULL); } else continue; diff --git a/src/xdiff/xpatience.c b/src/xdiff/xpatience.c index fdd7d0263..04e1a1ab2 100644 --- a/src/xdiff/xpatience.c +++ b/src/xdiff/xpatience.c @@ -288,7 +288,7 @@ static int fall_back_to_classic_diff(struct hashmap *map, int line1, int count1, int line2, int count2) { xpparam_t xpp; - xpp.flags = map->xpp->flags & ~XDF_PATIENCE_DIFF; + xpp.flags = map->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK; return xdl_fall_back_diff(map->env, &xpp, line1, count1, line2, count2); diff --git a/src/xdiff/xprepare.c b/src/xdiff/xprepare.c index e419f4f72..63a22c630 100644 --- a/src/xdiff/xprepare.c +++ b/src/xdiff/xprepare.c @@ -181,7 +181,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_ if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *)))) goto abort; - if (xpp->flags & XDF_HISTOGRAM_DIFF) + if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF) hbits = hsize = 0; else { hbits = xdl_hashbits((unsigned int) narec); @@ -209,8 +209,8 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_ crec->ha = hav; recs[nrec++] = crec; - if (!(xpp->flags & XDF_HISTOGRAM_DIFF) && - xdl_classify_record(pass, cf, rhash, hbits, crec) < 0) + if ((XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) && + xdl_classify_record(pass, cf, rhash, hbits, crec) < 0) goto abort; } } @@ -273,16 +273,15 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, * (nrecs) will be updated correctly anyway by * xdl_prepare_ctx(). */ - sample = xpp->flags & XDF_HISTOGRAM_DIFF ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1; + sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF + ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1); enl1 = xdl_guess_lines(mf1, sample) + 1; enl2 = xdl_guess_lines(mf2, sample) + 1; - if (!(xpp->flags & XDF_HISTOGRAM_DIFF) && - xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) { - + if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF && + xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) return -1; - } if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) { @@ -296,9 +295,9 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, return -1; } - if (!(xpp->flags & XDF_PATIENCE_DIFF) && - !(xpp->flags & XDF_HISTOGRAM_DIFF) && - xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) { + if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) && + (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) && + xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) { xdl_free_ctx(&xe->xdf2); xdl_free_ctx(&xe->xdf1); diff --git a/src/xdiff/xutils.c b/src/xdiff/xutils.c index bb7bdee49..30f2a30ac 100644 --- a/src/xdiff/xutils.c +++ b/src/xdiff/xutils.c @@ -120,35 +120,6 @@ void *xdl_cha_alloc(chastore_t *cha) { return data; } - -void *xdl_cha_first(chastore_t *cha) { - chanode_t *sncur; - - if (!(cha->sncur = sncur = cha->head)) - return NULL; - - cha->scurr = 0; - - return (char *) sncur + sizeof(chanode_t) + cha->scurr; -} - - -void *xdl_cha_next(chastore_t *cha) { - chanode_t *sncur; - - if (!(sncur = cha->sncur)) - return NULL; - cha->scurr += cha->isize; - if (cha->scurr == sncur->icurr) { - if (!(sncur = cha->sncur = sncur->next)) - return NULL; - cha->scurr = 0; - } - - return (char *) sncur + sizeof(chanode_t) + cha->scurr; -} - - long xdl_guess_lines(mmfile_t *mf, long sample) { long nl = 0, size, tsize = 0; char const *data, *cur, *top; @@ -170,6 +141,19 @@ long xdl_guess_lines(mmfile_t *mf, long sample) { return nl + 1; } +int xdl_blankline(const char *line, long size, long flags) +{ + long i; + + if (!(flags & XDF_WHITESPACE_FLAGS)) + return (size <= 1); + + for (i = 0; i < size && XDL_ISSPACE(line[i]); i++) + ; + + return (i == size); +} + int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags) { int i1, i2; diff --git a/src/xdiff/xutils.h b/src/xdiff/xutils.h index 714719a89..8f952a8e6 100644 --- a/src/xdiff/xutils.h +++ b/src/xdiff/xutils.h @@ -34,6 +34,7 @@ void *xdl_cha_alloc(chastore_t *cha); void *xdl_cha_first(chastore_t *cha); void *xdl_cha_next(chastore_t *cha); long xdl_guess_lines(mmfile_t *mf, long sample); +int xdl_blankline(const char *line, long size, long flags); int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags); unsigned long xdl_hash_record(char const **data, char const *top, long flags); unsigned int xdl_hashbits(unsigned int size); From 79698030b090b017aea6d33fb3f02d3fcb046738 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 29 Jun 2015 22:51:18 +0000 Subject: [PATCH 013/450] git_cert: child types use proper base type --- CHANGELOG.md | 4 ++++ include/git2/transport.h | 35 ++++++++++++++--------------------- src/curl_stream.c | 14 +++++++------- src/openssl_stream.c | 5 +++-- src/stransport_stream.c | 2 +- src/transports/ssh.c | 4 ++-- src/transports/winhttp.c | 2 +- 7 files changed, 32 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0aae4da6a..fa6599190 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ v0.23 + 1 ### API removals +### Breaking API changes + +* `git_cert` descendent types now have a proper `parent` member + v0.23 ------ diff --git a/include/git2/transport.h b/include/git2/transport.h index 2eeebd565..fd55eac0a 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -37,39 +37,32 @@ typedef enum { * Hostkey information taken from libssh2 */ typedef struct { + git_cert parent; + /** - * Type of certificate. Here to share the header with - * `git_cert`. + * A hostkey type from libssh2, either + * `GIT_CERT_SSH_MD5` or `GIT_CERT_SSH_SHA1` */ - git_cert_t cert_type; - /** - * A hostkey type from libssh2, either - * `GIT_CERT_SSH_MD5` or `GIT_CERT_SSH_SHA1` - */ git_cert_ssh_t type; - /** - * Hostkey hash. If type has `GIT_CERT_SSH_MD5` set, this will - * have the MD5 hash of the hostkey. - */ + /** + * Hostkey hash. If type has `GIT_CERT_SSH_MD5` set, this will + * have the MD5 hash of the hostkey. + */ unsigned char hash_md5[16]; - /** - * Hostkey hash. If type has `GIT_CERT_SSH_SHA1` set, this will - * have the SHA-1 hash of the hostkey. - */ - unsigned char hash_sha1[20]; + /** + * Hostkey hash. If type has `GIT_CERT_SSH_SHA1` set, this will + * have the SHA-1 hash of the hostkey. + */ + unsigned char hash_sha1[20]; } git_cert_hostkey; /** * X.509 certificate information */ typedef struct { - /** - * Type of certificate. Here to share the header with - * `git_cert`. - */ - git_cert_t cert_type; + git_cert parent; /** * Pointer to the X.509 certificate data */ diff --git a/src/curl_stream.c b/src/curl_stream.c index 6534bdbbe..63421fcf7 100644 --- a/src/curl_stream.c +++ b/src/curl_stream.c @@ -67,9 +67,9 @@ static int curls_certificate(git_cert **out, git_stream *stream) /* No information is available, can happen with SecureTransport */ if (certinfo->num_of_certs == 0) { - s->cert_info.cert_type = GIT_CERT_NONE; - s->cert_info.data = NULL; - s->cert_info.len = 0; + s->cert_info.parent.cert_type = GIT_CERT_NONE; + s->cert_info.data = NULL; + s->cert_info.len = 0; return 0; } @@ -85,11 +85,11 @@ static int curls_certificate(git_cert **out, git_stream *stream) s->cert_info_strings.strings = (char **) strings.contents; s->cert_info_strings.count = strings.length; - s->cert_info.cert_type = GIT_CERT_STRARRAY; - s->cert_info.data = &s->cert_info_strings; - s->cert_info.len = strings.length; + s->cert_info.parent.cert_type = GIT_CERT_STRARRAY; + s->cert_info.data = &s->cert_info_strings; + s->cert_info.len = strings.length; - *out = (git_cert *) &s->cert_info; + *out = &s->cert_info.parent; return 0; } diff --git a/src/openssl_stream.c b/src/openssl_stream.c index 958252e9f..1bd4f1421 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -358,11 +358,12 @@ int openssl_certificate(git_cert **out, git_stream *stream) return -1; } - st->cert_info.cert_type = GIT_CERT_X509; + st->cert_info.parent.cert_type = GIT_CERT_X509; st->cert_info.data = encoded_cert; st->cert_info.len = len; - *out = (git_cert *)&st->cert_info; + *out = &st->cert_info.parent; + return 0; } diff --git a/src/stransport_stream.c b/src/stransport_stream.c index 10e19166c..33b6c5c38 100644 --- a/src/stransport_stream.c +++ b/src/stransport_stream.c @@ -108,7 +108,7 @@ int stransport_certificate(git_cert **out, git_stream *stream) return -1; } - st->cert_info.cert_type = GIT_CERT_X509; + st->cert_info.parent.cert_type = GIT_CERT_X509; st->cert_info.data = (void *) CFDataGetBytePtr(st->der_data); st->cert_info.len = CFDataGetLength(st->der_data); diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 83af137f8..0b0d4bac3 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -525,10 +525,10 @@ static int _git_ssh_setup_conn( goto done; if (t->owner->certificate_check_cb != NULL) { - git_cert_hostkey cert = { 0 }, *cert_ptr; + git_cert_hostkey cert = {{ 0 }}, *cert_ptr; const char *key; - cert.cert_type = GIT_CERT_HOSTKEY_LIBSSH2; + cert.parent.cert_type = GIT_CERT_HOSTKEY_LIBSSH2; key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); if (key != NULL) { diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index da047d690..0c43c4b0b 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -228,7 +228,7 @@ static int certificate_check(winhttp_stream *s, int valid) } giterr_clear(); - cert.cert_type = GIT_CERT_X509; + cert.parent.cert_type = GIT_CERT_X509; cert.data = cert_ctx->pbCertEncoded; cert.len = cert_ctx->cbCertEncoded; error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->connection_data.host, t->owner->cred_acquire_payload); From a3c00cd8e36b303f0753412d12203d68af8c07e4 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 10 Jul 2015 09:21:59 -0500 Subject: [PATCH 014/450] xdiff: cleanup some warnings --- src/xdiff/xdiffi.c | 2 ++ src/xdiff/xemit.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/xdiff/xdiffi.c b/src/xdiff/xdiffi.c index 2358a2d63..0620e5fff 100644 --- a/src/xdiff/xdiffi.c +++ b/src/xdiff/xdiffi.c @@ -544,6 +544,8 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, { xdchange_t *xch, *xche; + (void)xe; + for (xch = xscr; xch; xch = xche->next) { xche = xdl_get_hunk(&xch, xecfg); if (!xch) diff --git a/src/xdiff/xemit.c b/src/xdiff/xemit.c index 750a97294..600fd1fdd 100644 --- a/src/xdiff/xemit.c +++ b/src/xdiff/xemit.c @@ -90,7 +90,7 @@ xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) } else if (distance < max_ignorable && xch->ignore) { ignored += xch->chg2; } else if (lxch != xchp && - xch->i1 + ignored - (lxch->i1 + lxch->chg1) > max_common) { + xch->i1 + ignored - (lxch->i1 + lxch->chg1) > (unsigned long)max_common) { break; } else if (!xch->ignore) { lxch = xch; From bae467aec4de8bb7d0c10a3a11e585ab6d05dd8f Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 10 Jul 2015 09:25:20 -0500 Subject: [PATCH 015/450] wildcard filters: clean up some warnings in tests --- tests/filter/wildcard.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/filter/wildcard.c b/tests/filter/wildcard.c index ba826b5dc..8031ad3d3 100644 --- a/tests/filter/wildcard.c +++ b/tests/filter/wildcard.c @@ -9,7 +9,7 @@ static git_repository *g_repo = NULL; -static git_filter *create_wildcard_filter(); +static git_filter *create_wildcard_filter(void); #define DATA_LEN 32 @@ -63,6 +63,9 @@ static int wildcard_filter_check( const git_filter_source *src, const char **attr_values) { + GIT_UNUSED(self); + GIT_UNUSED(src); + if (strcmp(attr_values[0], "wcflip") == 0 || strcmp(attr_values[0], "wcreverse") == 0) { *payload = git__strdup(attr_values[0]); @@ -91,8 +94,9 @@ static int wildcard_filter_apply( return GIT_PASSTHROUGH; } -static int wildcard_filter_cleanup(git_filter *self, void *payload) +static void wildcard_filter_cleanup(git_filter *self, void *payload) { + GIT_UNUSED(self); git__free(payload); return 0; } @@ -125,7 +129,7 @@ void test_filter_wildcard__reverse(void) cl_git_pass(git_filter_list_load( &fl, g_repo, NULL, "hero-reverse-foo", GIT_FILTER_TO_ODB, 0)); - cl_git_pass(git_buf_put(&in, input, DATA_LEN)); + cl_git_pass(git_buf_put(&in, (char *)input, DATA_LEN)); cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); cl_assert_equal_i(DATA_LEN, out.size); @@ -146,7 +150,7 @@ void test_filter_wildcard__flip(void) cl_git_pass(git_filter_list_load( &fl, g_repo, NULL, "hero-flip-foo", GIT_FILTER_TO_ODB, 0)); - cl_git_pass(git_buf_put(&in, input, DATA_LEN)); + cl_git_pass(git_buf_put(&in, (char *)input, DATA_LEN)); cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); cl_assert_equal_i(DATA_LEN, out.size); @@ -167,7 +171,7 @@ void test_filter_wildcard__none(void) cl_git_pass(git_filter_list_load( &fl, g_repo, NULL, "none-foo", GIT_FILTER_TO_ODB, 0)); - cl_git_pass(git_buf_put(&in, input, DATA_LEN)); + cl_git_pass(git_buf_put(&in, (char *)input, DATA_LEN)); cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); cl_assert_equal_i(DATA_LEN, out.size); From 9a99ca7b2108e3c3ca483d695cf0f46de386630a Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 10 Jul 2015 09:25:45 -0500 Subject: [PATCH 016/450] wildcard filters: move CHANGELOG message to 0.23+1 --- CHANGELOG.md | 8 ++++---- tests/filter/wildcard.c | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0aae4da6a..5e15a2121 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ v0.23 + 1 ### Changes or improvements +* Custom filters can now be registered with wildcard attributes, for + example `filter=*`. Consumers should examine the attributes parameter + of the `check` function for details. + ### API additions ### API removals @@ -88,10 +92,6 @@ v0.23 * If libcurl is installed, we will use it to connect to HTTP(S) servers. -* Custom filters can now be registered with wildcard attributes, for - example `filter=*`. Consumers should examine the attributes parameter - of the `check` function for details. - ### API additions * The `git_merge_options` gained a `file_flags` member. diff --git a/tests/filter/wildcard.c b/tests/filter/wildcard.c index 8031ad3d3..999b33653 100644 --- a/tests/filter/wildcard.c +++ b/tests/filter/wildcard.c @@ -98,7 +98,6 @@ static void wildcard_filter_cleanup(git_filter *self, void *payload) { GIT_UNUSED(self); git__free(payload); - return 0; } static void wildcard_filter_free(git_filter *f) @@ -106,7 +105,7 @@ static void wildcard_filter_free(git_filter *f) git__free(f); } -static git_filter *create_wildcard_filter() +static git_filter *create_wildcard_filter(void) { git_filter *filter = git__calloc(1, sizeof(git_filter)); cl_assert(filter); From 9c0331026b71ed801baa969c308598d59c6891c7 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 30 Jun 2015 13:41:01 +0000 Subject: [PATCH 017/450] khash: add eol so picky compilers stop warning --- src/khash.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/khash.h b/src/khash.h index 818ac833b..71eb583d5 100644 --- a/src/khash.h +++ b/src/khash.h @@ -619,4 +619,4 @@ typedef const char *kh_cstr_t; #define KHASH_MAP_INIT_STR(name, khval_t) \ KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) -#endif /* __AC_KHASH_H */ \ No newline at end of file +#endif /* __AC_KHASH_H */ From 37c84dc58f43c609ddadff3f9ff25c1f5b52a746 Mon Sep 17 00:00:00 2001 From: Tony Kelman Date: Sun, 5 Jul 2015 10:07:48 -0400 Subject: [PATCH 018/450] Increase required version of cmake to 2.8 --- CMakeLists.txt | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c55ddd1a..d3fe48f89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ # > cmake --build . --target install PROJECT(libgit2 C) -CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) CMAKE_POLICY(SET CMP0015 NEW) # Add find modules to the path diff --git a/README.md b/README.md index 4ee1fdc2a..3191aeee2 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ Under Unix-like systems, like Linux, \*BSD and Mac OS X, libgit2 expects `pthrea they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API for threading. -The `libgit2` library is built using [CMake]() (version 2.6 or newer) on all platforms. +The `libgit2` library is built using [CMake]() (version 2.8 or newer) on all platforms. On most systems you can build the library using the following commands From a34c4f8dcee7e985444be9f5e02b627a55c16bbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 11 Jul 2015 13:32:57 +0200 Subject: [PATCH 019/450] submdule: reproduce double-reporting of a submodule in foreach When we rename a submodule, we should be merging two sets of information based on whether their path is the same. We currently only deduplicate on equal name, which causes us to double-report. --- tests/submodule/lookup.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/submodule/lookup.c b/tests/submodule/lookup.c index 4d40e2279..9b2b3aa19 100644 --- a/tests/submodule/lookup.c +++ b/tests/submodule/lookup.c @@ -1,6 +1,7 @@ #include "clar_libgit2.h" #include "submodule_helpers.h" #include "git2/sys/repository.h" +#include "repository.h" #include "fileops.h" static git_repository *g_repo = NULL; @@ -103,10 +104,27 @@ static int sm_lookup_cb(git_submodule *sm, const char *name, void *payload) void test_submodule_lookup__foreach(void) { + git_config *cfg; sm_lookup_data data; + memset(&data, 0, sizeof(data)); cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data)); cl_assert_equal_i(8, data.count); + + memset(&data, 0, sizeof(data)); + + /* Change the path for a submodule so it doesn't match the name */ + cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules")); + + cl_git_pass(git_config_set_string(cfg, "submodule.smchangedindex.path", "sm_changed_index")); + cl_git_pass(git_config_set_string(cfg, "submodule.smchangedindex.url", "../submod2_target")); + cl_git_pass(git_config_delete_entry(cfg, "submodule.sm_changed_index.path")); + cl_git_pass(git_config_delete_entry(cfg, "submodule.sm_changed_index.url")); + + git_config_free(cfg); + + cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data)); + cl_assert_equal_i(8, data.count); } void test_submodule_lookup__lookup_even_with_unborn_head(void) From 08c2d3e97c0ee45222b93c214d0b752d0c52ee2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 11 Jul 2015 18:31:28 +0200 Subject: [PATCH 020/450] submodule: lookup the submodule by path if available If we get the path from the gitmodules file, look up the submodule we're interested in by path, rather then by name. Otherwise we might get duplicate results. --- src/submodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/submodule.c b/src/submodule.c index fb3d4bf1e..892c98304 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -1647,7 +1647,7 @@ static int submodule_load_from_config( } else { khiter_t pos; git_strmap *map = data->map; - pos = git_strmap_lookup_index(map, name.ptr); + pos = git_strmap_lookup_index(map, path ? path : name.ptr); if (git_strmap_valid_index(map, pos)) { sm = git_strmap_value_at(map, pos); } else { From 8a52ed7a482935c74dbb24358e21811dfa6d91c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 11 Jul 2015 18:51:36 +0200 Subject: [PATCH 021/450] errors: add EDIRECTORY This is to be returned when the operation which the user asked for is not possible to do on a directory. --- include/git2/errors.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/git2/errors.h b/include/git2/errors.h index a81aa05d9..e189e55f1 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -48,6 +48,7 @@ typedef enum { GIT_EEOF = -20, /**< Unexpected EOF */ GIT_EINVALID = -21, /**< Invalid operation or input */ GIT_EUNCOMMITTED = -22, /**< Uncommitted changes in index prevented operation */ + GIT_EDIRECTORY = -23, /**< The operation is not valid for a directory */ GIT_PASSTHROUGH = -30, /**< Internal only */ GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */ From 0d98af0911ebf55ba64cc0c9e17a3d450c77a9be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 11 Jul 2015 19:03:38 +0200 Subject: [PATCH 022/450] blob: fail to create a blob from a dir with EDIRECTORY This also affects `git_index_add_bypath()` by providing a better error message and a specific error code when a directory is passed. --- src/blob.c | 6 ++++++ tests/index/bypath.c | 23 +++++++++++++++++++++++ tests/submodule/add.c | 1 + 3 files changed, 30 insertions(+) create mode 100644 tests/index/bypath.c diff --git a/src/blob.c b/src/blob.c index 07c4d92c8..ad0f4ac62 100644 --- a/src/blob.c +++ b/src/blob.c @@ -185,6 +185,12 @@ int git_blob__create_from_paths( (error = git_repository_odb(&odb, repo)) < 0) goto done; + if (S_ISDIR(st.st_mode)) { + giterr_set(GITERR_ODB, "cannot create blob from '%s'; it is a directory", content_path); + error = GIT_EDIRECTORY; + goto done; + } + if (out_st) memcpy(out_st, &st, sizeof(st)); diff --git a/tests/index/bypath.c b/tests/index/bypath.c new file mode 100644 index 000000000..08c9934ea --- /dev/null +++ b/tests/index/bypath.c @@ -0,0 +1,23 @@ +#include "clar_libgit2.h" +#include "repository.h" +#include "../submodule/submodule_helpers.h" + +static git_repository *g_repo; +static git_index *g_idx; + +void test_index_bypath__initialize(void) +{ + g_repo = setup_fixture_submod2(); + cl_git_pass(git_repository_index__weakptr(&g_idx, g_repo)); +} + +void test_index_bypath__cleanup(void) +{ + g_repo = NULL; + g_idx = NULL; +} + +void test_index_bypath__add_directory(void) +{ + cl_git_fail_with(GIT_EDIRECTORY, git_index_add_bypath(g_idx, "just_a_dir")); +} diff --git a/tests/submodule/add.c b/tests/submodule/add.c index 01625d3aa..c3b3e6364 100644 --- a/tests/submodule/add.c +++ b/tests/submodule/add.c @@ -4,6 +4,7 @@ #include "submodule_helpers.h" #include "config/config_helpers.h" #include "fileops.h" +#include "repository.h" static git_repository *g_repo = NULL; From 247d27c2c6868e808191e6090056ecece6da30c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 11 Jul 2015 19:41:03 +0200 Subject: [PATCH 023/450] index: allow add_bypath to update submodules Similarly to how git itself does it, allow the index update operation to stage a change in a submodule's HEAD. --- src/index.c | 24 ++++++++++++++++++++++-- tests/index/bypath.c | 12 ++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/index.c b/src/index.c index 5ce5522f8..501498e9e 100644 --- a/src/index.c +++ b/src/index.c @@ -1236,10 +1236,30 @@ int git_index_add_bypath(git_index *index, const char *path) assert(index && path); - if ((ret = index_entry_init(&entry, index, path)) < 0 || - (ret = index_insert(index, &entry, 1, false)) < 0) + if ((ret = index_entry_init(&entry, index, path)) == 0) + ret = index_insert(index, &entry, 1, false); + + /* If we were given a directory, let's see if it's a submodule */ + if (ret < 0 && ret != GIT_EDIRECTORY) return ret; + if (ret == GIT_EDIRECTORY) { + git_submodule *sm; + git_error_state err; + + giterr_capture(&err, ret); + + ret = git_submodule_lookup(&sm, INDEX_OWNER(index), path); + if (ret == GIT_ENOTFOUND) + return giterr_restore(&err); + else + git__free(err.error_msg.message); + + ret = git_submodule_add_to_index(sm, false); + git_submodule_free(sm); + return ret; + } + /* Adding implies conflict was resolved, move conflict entries to REUC */ if ((ret = index_conflict_to_reuc(index, path)) < 0 && ret != GIT_ENOTFOUND) return ret; diff --git a/tests/index/bypath.c b/tests/index/bypath.c index 08c9934ea..ddb766a22 100644 --- a/tests/index/bypath.c +++ b/tests/index/bypath.c @@ -21,3 +21,15 @@ void test_index_bypath__add_directory(void) { cl_git_fail_with(GIT_EDIRECTORY, git_index_add_bypath(g_idx, "just_a_dir")); } + +void test_index_bypath__add_submodule(void) +{ + unsigned int status; + const char *sm_name = "sm_changed_head"; + + cl_git_pass(git_submodule_status(&status, g_repo, sm_name, 0)); + cl_assert_equal_i(GIT_SUBMODULE_STATUS_WD_MODIFIED, status & GIT_SUBMODULE_STATUS_WD_MODIFIED); + cl_git_pass(git_index_add_bypath(g_idx, sm_name)); + cl_git_pass(git_submodule_status(&status, g_repo, sm_name, 0)); + cl_assert_equal_i(0, status & GIT_SUBMODULE_STATUS_WD_MODIFIED); +} From 4de7f3bfc3ad9b2b22620cbe012a70c1c11b3b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 12 Jul 2015 13:28:03 +0200 Subject: [PATCH 024/450] filter: make sure to close the stream even on error When the stream list init or write fail, we must also make sure to close the stream, as that's the function contract. --- src/filter.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/filter.c b/src/filter.c index 70c4fa382..60473e4e1 100644 --- a/src/filter.c +++ b/src/filter.c @@ -950,18 +950,20 @@ int git_filter_list_stream_data( { git_vector filter_streams = GIT_VECTOR_INIT; git_writestream *stream_start; - int error = 0; + int error = 0, close_error; git_buf_sanitize(data); - if ((error = stream_list_init( - &stream_start, &filter_streams, filters, target)) == 0 && - (error = - stream_start->write(stream_start, data->ptr, data->size)) == 0) - error = stream_start->close(stream_start); + if ((error = stream_list_init(&stream_start, &filter_streams, filters, target)) < 0) + goto out; + error = stream_start->write(stream_start, data->ptr, data->size); + +out: + close_error = stream_start->close(stream_start); stream_list_free(&filter_streams); - return error; + /* propagate the stream init or write error */ + return error < 0 ? error : close_error; } int git_filter_list_stream_blob( From 01d0c02dbaa8856c4e2481ab1435bdf7df58690a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 12 Jul 2015 19:08:06 +0200 Subject: [PATCH 025/450] refdb: delete a ref's reflog upon deletion Removing a reflog upon ref deletion is something which only some backends might wish to do. Backends which are database-backed may wish to archive a reflog, log-based ones may not need to do anything. --- CHANGELOG.md | 4 ++++ include/git2/sys/refdb_backend.h | 5 +++-- src/branch.c | 13 +------------ src/refdb_fs.c | 7 +++++++ tests/refs/branches/delete.c | 2 ++ 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0a819ed7..243b696d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ v0.23 + 1 * `git_cert` descendent types now have a proper `parent` member +* It is the responsibility fo the refdb backend to decide what to do + with the reflog on ref deletion. The file-based backend must delete + it, a database-backed one may wish to archive it. + v0.23 ------ diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index d943e550f..9f2a99b7e 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -103,8 +103,9 @@ struct git_refdb_backend { const git_signature *who, const char *message); /** - * Deletes the given reference from the refdb. A refdb implementation - * must provide this function. + * Deletes the given reference (and if necessary its reflog) + * from the refdb. A refdb implementation must provide this + * function. */ int (*del)(git_refdb_backend *backend, const char *ref_name, const git_oid *old_id, const char *old_target); diff --git a/src/branch.c b/src/branch.c index 791d55106..0dcc14c29 100644 --- a/src/branch.c +++ b/src/branch.c @@ -155,18 +155,7 @@ int git_branch_delete(git_reference *branch) git_reference_owner(branch), git_buf_cstr(&config_section), NULL) < 0) goto on_error; - if (git_reference_delete(branch) < 0) - goto on_error; - - if ((error = git_reflog_delete(git_reference_owner(branch), git_reference_name(branch))) < 0) { - if (error == GIT_ENOTFOUND) { - giterr_clear(); - error = 0; - } - goto on_error; - } - - error = 0; + error = git_reference_delete(branch); on_error: git_buf_free(&config_section); diff --git a/src/refdb_fs.c b/src/refdb_fs.c index e1a77f3ff..55d535eb4 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -63,6 +63,8 @@ typedef struct refdb_fs_backend { uint32_t direach_flags; } refdb_fs_backend; +static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name); + static int packref_cmp(const void *a_, const void *b_) { const struct packref *a = a_, *b = b_; @@ -1217,6 +1219,11 @@ static int refdb_fs_backend__delete( if ((error = loose_lock(&file, backend, ref_name)) < 0) return error; + if ((error = refdb_reflog_fs__delete(_backend, ref_name)) < 0) { + git_filebuf_cleanup(&file); + return error; + } + return refdb_fs_backend__delete_tail(_backend, &file, ref_name, old_id, old_target); } diff --git a/tests/refs/branches/delete.c b/tests/refs/branches/delete.c index 343ff0f50..8807db231 100644 --- a/tests/refs/branches/delete.c +++ b/tests/refs/branches/delete.c @@ -132,6 +132,8 @@ void test_refs_branches_delete__removes_reflog(void) cl_git_pass(git_branch_delete(branch)); git_reference_free(branch); + cl_assert_equal_i(false, git_reference_has_log(repo, "refs/heads/track-local")); + /* Reading a nonexistant reflog creates it, but it should be empty */ cl_git_pass(git_reflog_read(&log, repo, "refs/heads/track-local")); cl_assert_equal_i(0, git_reflog_entrycount(log)); From 6c7e86e158f1294a4f98c470aa42118fc85924fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 12 Jul 2015 19:41:01 +0200 Subject: [PATCH 026/450] examples: modernise the fetch example Under normal conditions, git_remote_fetch() should be the only function used to perform a fetch. Don't let the example lead people astray. --- examples/network/fetch.c | 101 +++++++++++---------------------------- 1 file changed, 27 insertions(+), 74 deletions(-) diff --git a/examples/network/fetch.c b/examples/network/fetch.c index 67444cb4a..b536959fa 100644 --- a/examples/network/fetch.c +++ b/examples/network/fetch.c @@ -23,32 +23,6 @@ static int progress_cb(const char *str, int len, void *data) return 0; } -static void *download(void *ptr) -{ - struct dl_data *data = (struct dl_data *)ptr; - - // Connect to the remote end specifying that we want to fetch - // information from it. - if (git_remote_connect(data->remote, GIT_DIRECTION_FETCH, &data->fetch_opts->callbacks) < 0) { - data->ret = -1; - goto exit; - } - - // Download the packfile and index it. This function updates the - // amount of received data and the indexer stats which lets you - // inform the user about progress. - if (git_remote_download(data->remote, NULL, data->fetch_opts) < 0) { - data->ret = -1; - goto exit; - } - - data->ret = 0; - -exit: - data->finished = 1; - return &data->ret; -} - /** * This function gets called for each remote-tracking branch that gets * updated. The message we output depends on whether it's a new one or @@ -73,6 +47,24 @@ static int update_cb(const char *refname, const git_oid *a, const git_oid *b, vo return 0; } +/** + * This gets called during the download and indexing. Here we show + * processed and total objects in the pack and the amount of received + * data. Most frontends will probably want to show a percentage and + * the download rate. + */ +static int transfer_progress_cb(const git_transfer_progress *stats, void *payload) +{ + if (stats->received_objects == stats->total_objects) { + printf("Resolving deltas %d/%d\r", + stats->indexed_deltas, stats->total_deltas); + } else if (stats->total_objects > 0) { + printf("Received %d/%d objects (%d) in %" PRIuZ " bytes\r", + stats->received_objects, stats->total_objects, + stats->indexed_objects, stats->received_bytes); + } +} + /** Entry point for this command */ int fetch(git_repository *repo, int argc, char **argv) { @@ -80,9 +72,6 @@ int fetch(git_repository *repo, int argc, char **argv) const git_transfer_progress *stats; struct dl_data data; git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT; -#ifndef _WIN32 - pthread_t worker; -#endif if (argc < 2) { fprintf(stderr, "usage: %s fetch \n", argv[-1]); @@ -99,49 +88,23 @@ int fetch(git_repository *repo, int argc, char **argv) // Set up the callbacks (only update_tips for now) fetch_opts.callbacks.update_tips = &update_cb; fetch_opts.callbacks.sideband_progress = &progress_cb; + fetch_opts.callbacks.transfer_progress = transfer_progress_cb; fetch_opts.callbacks.credentials = cred_acquire_cb; - // Set up the information for the background worker thread - data.remote = remote; - data.fetch_opts = &fetch_opts; - data.ret = 0; - data.finished = 0; - - stats = git_remote_stats(remote); - -#ifdef _WIN32 - download(&data); -#else - pthread_create(&worker, NULL, download, &data); - - // Loop while the worker thread is still running. Here we show processed - // and total objects in the pack and the amount of received - // data. Most frontends will probably want to show a percentage and - // the download rate. - do { - usleep(10000); - - if (stats->received_objects == stats->total_objects) { - printf("Resolving deltas %d/%d\r", - stats->indexed_deltas, stats->total_deltas); - } else if (stats->total_objects > 0) { - printf("Received %d/%d objects (%d) in %" PRIuZ " bytes\r", - stats->received_objects, stats->total_objects, - stats->indexed_objects, stats->received_bytes); - } - } while (!data.finished); - - if (data.ret < 0) - goto on_error; - - pthread_join(worker, NULL); -#endif + /** + * Perform the fetch with the configured refspecs from the + * config. Update the reflog for the updated references with + * "fetch". + */ + if (git_remote_fetch(remote, NULL, &fetch_opts, "fetch") < 0) + return -1; /** * If there are local objects (we got a thin pack), then tell * the user how many objects we saved from having to cross the * network. */ + stats = git_remote_stats(remote); if (stats->local_objects > 0) { printf("\rReceived %d/%d objects in %zu bytes (used %d local objects)\n", stats->indexed_objects, stats->total_objects, stats->received_bytes, stats->local_objects); @@ -150,16 +113,6 @@ int fetch(git_repository *repo, int argc, char **argv) stats->indexed_objects, stats->total_objects, stats->received_bytes); } - // Disconnect the underlying connection to prevent from idling. - git_remote_disconnect(remote); - - // Update the references in the remote's namespace to point to the - // right commits. This may be needed even if there was no packfile - // to download, which can happen e.g. when the branches have been - // changed but all the needed objects are available locally. - if (git_remote_update_tips(remote, &fetch_opts.callbacks, 1, fetch_opts.download_tags, NULL) < 0) - return -1; - git_remote_free(remote); return 0; From 768f8be31c3fac1b0ed8f4d49cf7176a30586443 Mon Sep 17 00:00:00 2001 From: Matthew Plough Date: Tue, 30 Jun 2015 19:00:41 -0400 Subject: [PATCH 027/450] Fix #3094 - improve use of portable size_t/ssize_t format specifiers. The header src/cc-compat.h defines portable format specifiers PRIuZ, PRIdZ, and PRIxZ. The original report highlighted the need to use these specifiers in examples/network/fetch.c. For this commit, I checked all C source and header files not in deps/ and transitioned to the appropriate format specifier where appropriate. --- examples/network/fetch.c | 4 ++-- src/cache.c | 8 ++++---- src/checkout.c | 4 ++-- src/merge.c | 6 +++--- src/rebase.c | 4 ++-- src/stash.c | 2 +- src/transports/http.c | 2 +- src/transports/smart_pkt.c | 2 +- tests/blame/blame_helpers.c | 2 +- tests/clar_libgit2.c | 4 ++-- tests/merge/merge_helpers.c | 2 +- 11 files changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/network/fetch.c b/examples/network/fetch.c index 67444cb4a..6be12406b 100644 --- a/examples/network/fetch.c +++ b/examples/network/fetch.c @@ -143,10 +143,10 @@ int fetch(git_repository *repo, int argc, char **argv) * network. */ if (stats->local_objects > 0) { - printf("\rReceived %d/%d objects in %zu bytes (used %d local objects)\n", + printf("\rReceived %d/%d objects in %" PRIuZ " bytes (used %d local objects)\n", stats->indexed_objects, stats->total_objects, stats->received_bytes, stats->local_objects); } else{ - printf("\rReceived %d/%d objects in %zu bytes\n", + printf("\rReceived %d/%d objects in %" PRIuZ "bytes\n", stats->indexed_objects, stats->total_objects, stats->received_bytes); } diff --git a/src/cache.c b/src/cache.c index 2f3ad1563..ca5173c0d 100644 --- a/src/cache.c +++ b/src/cache.c @@ -50,16 +50,16 @@ void git_cache_dump_stats(git_cache *cache) if (kh_size(cache->map) == 0) return; - printf("Cache %p: %d items cached, %d bytes\n", - cache, kh_size(cache->map), (int)cache->used_memory); + printf("Cache %p: %d items cached, %"PRIdZ" bytes\n", + cache, kh_size(cache->map), cache->used_memory); kh_foreach_value(cache->map, object, { char oid_str[9]; - printf(" %s%c %s (%d)\n", + printf(" %s%c %s (%"PRIuZ")\n", git_object_type2string(object->type), object->flags == GIT_CACHE_STORE_PARSED ? '*' : ' ', git_oid_tostr(oid_str, sizeof(oid_str), &object->oid), - (int)object->size + object->size ); }); } diff --git a/src/checkout.c b/src/checkout.c index a94d509d3..4b3acbcce 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1299,8 +1299,8 @@ static int checkout_get_actions( if (counts[CHECKOUT_ACTION__CONFLICT] > 0 && (data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) == 0) { - giterr_set(GITERR_CHECKOUT, "%d %s checkout", - (int)counts[CHECKOUT_ACTION__CONFLICT], + giterr_set(GITERR_CHECKOUT, "%"PRIuZ" %s checkout", + counts[CHECKOUT_ACTION__CONFLICT], counts[CHECKOUT_ACTION__CONFLICT] == 1 ? "conflict prevents" : "conflicts prevent"); error = GIT_ECONFLICT; diff --git a/src/merge.c b/src/merge.c index 9d6252ea8..863ac8f2d 100644 --- a/src/merge.c +++ b/src/merge.c @@ -79,7 +79,7 @@ int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_reposito unsigned int i; if (length < 2) { - giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %u.", length); + giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %" PRIuZ ".", length); return -1; } @@ -185,7 +185,7 @@ int git_merge_base_octopus(git_oid *out, git_repository *repo, size_t length, co assert(out && repo && input_array); if (length < 2) { - giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %u.", length); + giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %" PRIuZ ".", length); return -1; } @@ -2451,7 +2451,7 @@ int git_merge__check_result(git_repository *repo, git_index *index_new) goto done; if ((conflicts = index_conflicts + wd_conflicts) > 0) { - giterr_set(GITERR_MERGE, "%d uncommitted change%s would be overwritten by merge", + giterr_set(GITERR_MERGE, "%" PRIuZ " uncommitted change%s would be overwritten by merge", conflicts, (conflicts != 1) ? "s" : ""); error = GIT_ECONFLICT; } diff --git a/src/rebase.c b/src/rebase.c index 8da7b4f7f..17536c030 100644 --- a/src/rebase.c +++ b/src/rebase.c @@ -436,7 +436,7 @@ static int rebase_setupfiles_merge(git_rebase *rebase) size_t i; int error = 0; - if ((error = rebase_setupfile(rebase, END_FILE, -1, "%d\n", git_array_size(rebase->operations))) < 0 || + if ((error = rebase_setupfile(rebase, END_FILE, -1, "%" PRIuZ "\n", git_array_size(rebase->operations))) < 0 || (error = rebase_setupfile(rebase, ONTO_NAME_FILE, -1, "%s\n", rebase->onto_name)) < 0) goto done; @@ -789,7 +789,7 @@ static int rebase_next_merge( normalize_checkout_options_for_apply(&checkout_opts, rebase, current_commit); if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 || - (error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%d\n", rebase->current+1)) < 0 || + (error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%" PRIuZ "\n", rebase->current+1)) < 0 || (error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 || (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, NULL)) < 0 || (error = git_merge__check_result(rebase->repo, index)) < 0 || diff --git a/src/stash.c b/src/stash.c index acf89442a..fcb1112ac 100644 --- a/src/stash.c +++ b/src/stash.c @@ -770,7 +770,7 @@ static int ensure_clean_index(git_repository *repo, git_index *index) goto done; if (git_diff_num_deltas(index_diff) > 0) { - giterr_set(GITERR_STASH, "%d uncommitted changes exist in the index", + giterr_set(GITERR_STASH, "%" PRIuZ " uncommitted changes exist in the index", git_diff_num_deltas(index_diff)); error = GIT_EUNCOMMITTED; } diff --git a/src/transports/http.c b/src/transports/http.c index dd4426475..1ed292be5 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -511,7 +511,7 @@ static int write_chunk(git_stream *io, const char *buffer, size_t len) git_buf buf = GIT_BUF_INIT; /* Chunk header */ - git_buf_printf(&buf, "%X\r\n", (unsigned)len); + git_buf_printf(&buf, "%" PRIxZ "\r\n", len); if (git_buf_oom(&buf)) return -1; diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c index d214c9fa5..9ccbd8085 100644 --- a/src/transports/smart_pkt.c +++ b/src/transports/smart_pkt.c @@ -523,7 +523,7 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca if (len > 0xffff) { giterr_set(GITERR_NET, - "Tried to produce packet with invalid length %d", len); + "Tried to produce packet with invalid length %" PRIuZ, len); return -1; } diff --git a/tests/blame/blame_helpers.c b/tests/blame/blame_helpers.c index 21cd1a615..b305ba1e3 100644 --- a/tests/blame/blame_helpers.c +++ b/tests/blame/blame_helpers.c @@ -4,7 +4,7 @@ void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...) { va_list arglist; - printf("Hunk %zd (line %d +%d): ", idx, + printf("Hunk %"PRIuZ" (line %d +%d): ", idx, hunk->final_start_line_number, hunk->lines_in_hunk-1); va_start(arglist, fmt); diff --git a/tests/clar_libgit2.c b/tests/clar_libgit2.c index dabc47a09..b14af44a9 100644 --- a/tests/clar_libgit2.c +++ b/tests/clar_libgit2.c @@ -483,8 +483,8 @@ void clar__assert_equal_file( for (pos = 0; pos < bytes && expected_data[pos] == buf[pos]; ++pos) /* find differing byte offset */; p_snprintf( - buf, sizeof(buf), "file content mismatch at byte %d", - (int)(total_bytes + pos)); + buf, sizeof(buf), "file content mismatch at byte %"PRIdZ, + (ssize_t)(total_bytes + pos)); p_close(fd); clar__fail(file, line, path, buf, 1); } diff --git a/tests/merge/merge_helpers.c b/tests/merge/merge_helpers.c index 33710f403..f81471424 100644 --- a/tests/merge/merge_helpers.c +++ b/tests/merge/merge_helpers.c @@ -110,7 +110,7 @@ void merge__dump_index_entries(git_vector *index_entries) size_t i; const git_index_entry *index_entry; - printf ("\nINDEX [%d]:\n", (int)index_entries->length); + printf ("\nINDEX [%"PRIuZ"]:\n", index_entries->length); for (i = 0; i < index_entries->length; i++) { index_entry = index_entries->contents[i]; From aa51fa1e03df9a30c8c37b168758dd34dd75f353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 13 Jul 2015 08:39:35 +0200 Subject: [PATCH 028/450] submodule: add failing test for backslash in url --- tests/submodule/lookup.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/submodule/lookup.c b/tests/submodule/lookup.c index 4d40e2279..38f06abbc 100644 --- a/tests/submodule/lookup.c +++ b/tests/submodule/lookup.c @@ -133,6 +133,29 @@ void test_submodule_lookup__lookup_even_with_missing_index(void) test_submodule_lookup__simple_lookup(); /* baseline should still pass */ } +void test_submodule_lookup__backslashes(void) +{ + git_config *cfg; + git_submodule *sm; + git_repository *subrepo; + git_buf buf = GIT_BUF_INIT; + const char *backslashed_path = "..\\submod2_target"; + + cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules")); + cl_git_pass(git_config_set_string(cfg, "submodule.sm_unchanged.url", backslashed_path)); + git_config_free(cfg); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); + cl_assert_equal_s(backslashed_path, git_submodule_url(sm)); + cl_git_pass(git_submodule_open(&subrepo, sm)); + + cl_git_pass(git_submodule_resolve_url(&buf, g_repo, backslashed_path)); + + git_buf_free(&buf); + git_submodule_free(sm); + git_repository_free(subrepo); +} + static void baseline_tests(void) { /* small baseline that should work even if we change the index or make From f00f005bad3d27bb5c23ae5d7187622b940c5d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 13 Jul 2015 09:08:32 +0200 Subject: [PATCH 029/450] submodule: normalize slashes in resolve_url Our path functions expect to work with slashes, so convert a path with backslashes into one with slashes at the top of the function. --- src/submodule.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/submodule.c b/src/submodule.c index fb3d4bf1e..e4469efa3 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -781,11 +781,25 @@ const char *git_submodule_url(git_submodule *submodule) int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *url) { int error = 0; + git_buf normalized = GIT_BUF_INIT; assert(out && repo && url); git_buf_sanitize(out); + if (strchr(url, '\\')) { + char *p; + if ((error = git_buf_puts(&normalized, url)) < 0) + return error; + + for (p = normalized.ptr; *p; p++) { + if (*p == '\\') + *p = '/'; + } + + url = normalized.ptr; + } + if (git_path_is_relative(url)) { if (!(error = get_url_base(out, repo))) error = git_path_apply_relative(out, url); @@ -796,6 +810,7 @@ int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *ur error = -1; } + git_buf_free(&normalized); return error; } From a58854a0311316a66fda363e83665ab942d81ec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 13 Jul 2015 17:11:19 +0200 Subject: [PATCH 030/450] submodule, path: extract slash conversion Extract the backslash-to-slash conversion into a helper function. --- src/path.c | 16 ++++++++++++++++ src/path.h | 5 +++++ src/submodule.c | 10 +++------- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/path.c b/src/path.c index 2558058dd..16283cab2 100644 --- a/src/path.c +++ b/src/path.c @@ -1671,3 +1671,19 @@ bool git_path_isvalid( return verify_component(repo, start, (c - start), flags); } + +int git_path_normalize_slashes(git_buf *out, const char *path) +{ + int error; + char *p; + + if ((error = git_buf_puts(out, path)) < 0) + return error; + + for (p = out->ptr; *p; p++) { + if (*p == '\\') + *p = '/'; + } + + return 0; +} diff --git a/src/path.h b/src/path.h index 5927a5381..adb76865e 100644 --- a/src/path.h +++ b/src/path.h @@ -591,4 +591,9 @@ extern bool git_path_isvalid( const char *path, unsigned int flags); +/** + * Convert any backslashes into slashes + */ +int git_path_normalize_slashes(git_buf *out, const char *path); + #endif diff --git a/src/submodule.c b/src/submodule.c index e4469efa3..f16d3c5ab 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -787,19 +787,15 @@ int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *ur git_buf_sanitize(out); + /* We do this in all platforms in case someone on Windows created the .gitmodules */ if (strchr(url, '\\')) { - char *p; - if ((error = git_buf_puts(&normalized, url)) < 0) + if ((error = git_path_normalize_slashes(&normalized, url)) < 0) return error; - for (p = normalized.ptr; *p; p++) { - if (*p == '\\') - *p = '/'; - } - url = normalized.ptr; } + if (git_path_is_relative(url)) { if (!(error = get_url_base(out, repo))) error = git_path_apply_relative(out, url); From cec3569f250379aaa9aa4cec4fb49d4a78d6ee11 Mon Sep 17 00:00:00 2001 From: Fallso Date: Tue, 14 Jul 2015 15:33:56 +0100 Subject: [PATCH 031/450] Fix macro redefinition warning --- src/thread-utils.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/thread-utils.c b/src/thread-utils.c index c3baf411a..dc9b2f09e 100644 --- a/src/thread-utils.c +++ b/src/thread-utils.c @@ -8,7 +8,9 @@ #include "thread-utils.h" #ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN +#endif # include #elif defined(hpux) || defined(__hpux) || defined(_hpux) # include From 37996d474b658f7428b2195023da82a9b480f307 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Mon, 20 Jul 2015 09:21:36 -0400 Subject: [PATCH 032/450] Document git_fetch_options struct and fix typo. git_fetch_options was missing from the API docs because it lacked a documentation comment above the struct declaration. I used the git_checkout_options docstring as a template. Also fixes a typo in git_remote_prune_refs (remote, not reamote). --- include/git2/remote.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/include/git2/remote.h b/include/git2/remote.h index e47f00881..444fe5276 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -511,6 +511,14 @@ typedef enum { GIT_REMOTE_DOWNLOAD_TAGS_ALL, } git_remote_autotag_option_t; +/** + * Fetch options structure. + * + * Zero out for defaults. Initialize with `GIT_FETCH_OPTIONS_INIT` macro to + * correctly set the `version` field. E.g. + * + * git_fetch_options opts = GIT_FETCH_OPTIONS_INIT; + */ typedef struct { int version; @@ -739,7 +747,7 @@ GIT_EXTERN(int) git_remote_prune_refs(const git_remote *remote); * stored here for further processing by the caller. Always free this * strarray on successful return. * @param repo the repository in which to rename - * @param name the current name of the reamote + * @param name the current name of the remote * @param new_name the new name the remote should bear * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code */ From 318bb763e9e896472e2aa16ebe8cbf2463804e19 Mon Sep 17 00:00:00 2001 From: Sven Strickroth Date: Tue, 21 Jul 2015 23:36:39 +0200 Subject: [PATCH 033/450] Make libgit2 work on Windows Vista again (fixes issue #3316) Signed-off-by: Sven Strickroth --- src/path.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/path.c b/src/path.c index 2558058dd..3e63f06d4 100644 --- a/src/path.c +++ b/src/path.c @@ -12,6 +12,7 @@ #include "win32/posix.h" #include "win32/buffer.h" #include "win32/w32_util.h" +#include "win32/version.h" #else #include #endif @@ -1085,7 +1086,7 @@ int git_path_direach( #if defined(GIT_WIN32) && !defined(__MINGW32__) /* Using _FIND_FIRST_EX_LARGE_FETCH may increase performance in Windows 7 - * and better. Prior versions will ignore this. + * and better. */ #ifndef FIND_FIRST_EX_LARGE_FETCH # define FIND_FIRST_EX_LARGE_FETCH 2 @@ -1099,6 +1100,10 @@ int git_path_diriter_init( git_win32_path path_filter; git_buf hack = {0}; + static int is_win7_or_later = -1; + if (is_win7_or_later < 0) + is_win7_or_later = git_has_win32_version(6, 1, 0); + assert(diriter && path); memset(diriter, 0, sizeof(git_path_diriter)); @@ -1122,11 +1127,11 @@ int git_path_diriter_init( diriter->handle = FindFirstFileExW( path_filter, - FindExInfoBasic, + is_win7_or_later ? FindExInfoBasic : FindExInfoStandard, &diriter->current, FindExSearchNameMatch, NULL, - FIND_FIRST_EX_LARGE_FETCH); + is_win7_or_later ? FIND_FIRST_EX_LARGE_FETCH : 0); if (diriter->handle == INVALID_HANDLE_VALUE) { giterr_set(GITERR_OS, "Could not open directory '%s'", path); From cf198fdf2a044d2e2f0675c2c6b1cd9cdbcf4fcf Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 22 Jul 2015 10:51:38 -0400 Subject: [PATCH 034/450] Increment `git__n_inits` before doing `init_once`. Fixes #3318. --- src/global.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/global.c b/src/global.c index 3f20bfd31..cb2242405 100644 --- a/src/global.c +++ b/src/global.c @@ -330,8 +330,8 @@ int git_libgit2_init(void) { int ret; - pthread_once(&_once_init, init_once); ret = git_atomic_inc(&git__n_inits); + pthread_once(&_once_init, init_once); return init_error ? init_error : ret; } From 668053befecfd0979ff790bc9a2a3c308c38a9be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 24 Jul 2015 18:44:29 +0200 Subject: [PATCH 035/450] filebuf: failing test for leaving the lockfile when failing to rename When we fail to rename, we currently leave the lockfile laying around. This shows that behaviour. --- tests/core/filebuf.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/core/filebuf.c b/tests/core/filebuf.c index 5a3e7510f..9fd52b179 100644 --- a/tests/core/filebuf.c +++ b/tests/core/filebuf.c @@ -124,3 +124,30 @@ void test_core_filebuf__umask(void) cl_must_pass(p_unlink(test)); } +void test_core_filebuf__rename_error(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + char *dir = "subdir", *test = "subdir/test", *test_lock = "subdir/test.lock"; + int fd; + +#ifndef GIT_WIN32 + cl_skip(); +#endif + + cl_git_pass(p_mkdir(dir, 0666)); + cl_git_mkfile(test, "dummy content"); + fd = p_open(test, O_RDONLY); + cl_assert(fd > 0); + cl_git_pass(git_filebuf_open(&file, test, 0, 0666)); + + cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); + + cl_assert_equal_i(true, git_path_exists(test_lock)); + + cl_git_fail(git_filebuf_commit(&file)); + p_close(fd); + + git_filebuf_cleanup(&file); + cl_assert_equal_i(false, git_path_exists(test_lock)); + +} From 19d9beb7ffbde3e171c17fc3544d508304a30fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 24 Jul 2015 19:22:41 +0200 Subject: [PATCH 036/450] filebuf: remove lockfile upon rename errors When we have an error renaming the lockfile, we need to make sure that we remove it upon cleanup. For this, we need to keep track of whether we opened the file and whether the rename succeeded. If we did create the lockfile but the rename did not succeed, we remove the lockfile. This won't protect against all errors, but the most common ones (target file is open) does get handled. --- src/filebuf.c | 7 ++++++- src/filebuf.h | 2 ++ tests/core/filebuf.c | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/filebuf.c b/src/filebuf.c index 848ac343b..838f4b4d2 100644 --- a/src/filebuf.c +++ b/src/filebuf.c @@ -101,7 +101,7 @@ void git_filebuf_cleanup(git_filebuf *file) if (file->fd_is_open && file->fd >= 0) p_close(file->fd); - if (file->fd_is_open && file->path_lock && git_path_exists(file->path_lock)) + if (file->created_lock && !file->did_rename && file->path_lock && git_path_exists(file->path_lock)) p_unlink(file->path_lock); if (file->compute_digest) { @@ -258,6 +258,7 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode goto cleanup; } file->fd_is_open = true; + file->created_lock = true; /* No original path */ file->path_original = NULL; @@ -281,6 +282,8 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode /* open the file for locking */ if ((error = lock_file(file, flags, mode)) < 0) goto cleanup; + + file->created_lock = true; } return 0; @@ -340,6 +343,8 @@ int git_filebuf_commit(git_filebuf *file) goto on_error; } + file->did_rename = true; + git_filebuf_cleanup(file); return 0; diff --git a/src/filebuf.h b/src/filebuf.h index 2bd18dc35..f4d255b0a 100644 --- a/src/filebuf.h +++ b/src/filebuf.h @@ -44,6 +44,8 @@ struct git_filebuf { size_t buf_size, buf_pos; git_file fd; bool fd_is_open; + bool created_lock; + bool did_rename; bool do_not_buffer; int last_error; }; diff --git a/tests/core/filebuf.c b/tests/core/filebuf.c index 9fd52b179..3f7dc8569 100644 --- a/tests/core/filebuf.c +++ b/tests/core/filebuf.c @@ -148,6 +148,6 @@ void test_core_filebuf__rename_error(void) p_close(fd); git_filebuf_cleanup(&file); - cl_assert_equal_i(false, git_path_exists(test_lock)); + cl_assert_equal_i(false, git_path_exists(test_lock)); } From 12786e0f7c0e6ea23b1483f8732ca23b367bfd8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 26 Jul 2015 17:19:22 +0200 Subject: [PATCH 037/450] iterator: skip over errors in diriter init An error here will typically mean that the directory was removed between the time we iterated the parent and the time we wanted to visit it in which case we should ignore it. Other kinds of errors such as permissions (or transient errors) also better dealt with by pretending we didn't see it. --- src/iterator.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/iterator.c b/src/iterator.c index a312afb3a..cf51a340d 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -1027,8 +1027,11 @@ static int dirload_with_stat( strncomp = (flags & GIT_PATH_DIR_IGNORE_CASE) != 0 ? git__strncasecmp : git__strncmp; - if ((error = git_path_diriter_init(&diriter, dirpath, flags)) < 0) + /* Any error here is equivalent to the dir not existing, skip over it */ + if ((error = git_path_diriter_init(&diriter, dirpath, flags)) < 0) { + error = GIT_ENOTFOUND; goto done; + } while ((error = git_path_diriter_next(&diriter)) == 0) { if ((error = git_path_diriter_fullpath(&path, &path_len, &diriter)) < 0) From 0e391d8526032008a53fd3e8f7c6795d59ebdb5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 27 Jul 2015 13:31:06 +0200 Subject: [PATCH 038/450] iterator: adjust unreadable-dir test to new behaviour We don't want the iterator to make us stop whenever we hit an unreadable dir. We should instead move over to the next item. --- tests/repo/iterator.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c index 26e8954fe..bb2d3a186 100644 --- a/tests/repo/iterator.c +++ b/tests/repo/iterator.c @@ -928,7 +928,7 @@ void test_repo_iterator__fs2(void) git_iterator_free(i); } -void test_repo_iterator__fs_preserves_error(void) +void test_repo_iterator__unreadable_dir(void) { git_iterator *i; const git_index_entry *e; @@ -951,10 +951,6 @@ void test_repo_iterator__fs_preserves_error(void) cl_git_pass(git_iterator_advance(&e, i)); /* a */ cl_git_fail(git_iterator_advance(&e, i)); /* b */ - cl_assert(giterr_last()); - cl_assert(giterr_last()->message != NULL); - /* skip 'c/' empty directory */ - cl_git_pass(git_iterator_advance(&e, i)); /* d */ cl_assert_equal_i(GIT_ITEROVER, git_iterator_advance(&e, i)); cl_must_pass(p_chmod("empty_standard_repo/r/b", 0777)); From 41808d04705644b8af566c9039a3ddcbb3257c99 Mon Sep 17 00:00:00 2001 From: Ben Chatelain Date: Mon, 27 Jul 2015 14:46:50 -0600 Subject: [PATCH 039/450] Fix @param names in doc comments --- include/git2/repository.h | 2 +- include/git2/sys/odb_backend.h | 2 +- include/git2/sys/refdb_backend.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/git2/repository.h b/include/git2/repository.h index ce56fef0f..cf268ef85 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -745,7 +745,7 @@ GIT_EXTERN(int) git_repository_ident(const char **name, const char **email, cons * * @param repo the repository to configure * @param name the name to use for the reflog entries - * @param name the email to use for the reflog entries + * @param email the email to use for the reflog entries */ GIT_EXTERN(int) git_repository_set_ident(git_repository *repo, const char *name, const char *email); diff --git a/include/git2/sys/odb_backend.h b/include/git2/sys/odb_backend.h index 0a51c6dba..fe102ff3c 100644 --- a/include/git2/sys/odb_backend.h +++ b/include/git2/sys/odb_backend.h @@ -93,7 +93,7 @@ struct git_odb_backend { * Initializes a `git_odb_backend` with default values. Equivalent to * creating an instance with GIT_ODB_BACKEND_INIT. * - * @param opts the `git_odb_backend` struct to initialize. + * @param backend the `git_odb_backend` struct to initialize. * @param version Version the struct; pass `GIT_ODB_BACKEND_VERSION` * @return Zero on success; -1 on failure. */ diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index d943e550f..8b004a7e0 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -175,7 +175,7 @@ struct git_refdb_backend { * Initializes a `git_refdb_backend` with default values. Equivalent to * creating an instance with GIT_REFDB_BACKEND_INIT. * - * @param opts the `git_refdb_backend` struct to initialize + * @param backend the `git_refdb_backend` struct to initialize * @param version Version of struct; pass `GIT_REFDB_BACKEND_VERSION` * @return Zero on success; -1 on failure. */ From f90fbb8d22c77fbc4d207ef68a9fd445e68602db Mon Sep 17 00:00:00 2001 From: Ben Chatelain Date: Mon, 27 Jul 2015 17:42:08 -0600 Subject: [PATCH 040/450] Use correct Doxygen trailing comment syntax --- include/git2/sys/diff.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/git2/sys/diff.h b/include/git2/sys/diff.h index 034d5c478..aa6fed757 100644 --- a/include/git2/sys/diff.h +++ b/include/git2/sys/diff.h @@ -38,7 +38,7 @@ GIT_EXTERN(int) git_diff_print_callback__to_buf( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, - void *payload); /*< payload must be a `git_buf *` */ + void *payload); /**< payload must be a `git_buf *` */ /** * Diff print callback that writes to stdio FILE handle. @@ -58,7 +58,7 @@ GIT_EXTERN(int) git_diff_print_callback__to_file_handle( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, - void *payload); /*< payload must be a `FILE *` */ + void *payload); /**< payload must be a `FILE *` */ /** @@ -66,8 +66,8 @@ GIT_EXTERN(int) git_diff_print_callback__to_file_handle( */ typedef struct { unsigned int version; - size_t stat_calls; /*< Number of stat() calls performed */ - size_t oid_calculations; /*< Number of ID calculations */ + size_t stat_calls; /**< Number of stat() calls performed */ + size_t oid_calculations; /** Date: Mon, 27 Jul 2015 18:28:29 -0600 Subject: [PATCH 041/450] Add -Wdocumentation flag if supported --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 20e3272f9..a15ce7dbd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -413,6 +413,7 @@ ELSE () SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") ENDIF () + ADD_C_FLAG_IF_SUPPORTED(-Wdocumentation) ADD_C_FLAG_IF_SUPPORTED(-Wno-missing-field-initializers) ADD_C_FLAG_IF_SUPPORTED(-Wstrict-aliasing=2) ADD_C_FLAG_IF_SUPPORTED(-Wstrict-prototypes) From 08afd227dfd3ee56accd6d6ded3889f34c5329fc Mon Sep 17 00:00:00 2001 From: Ben Chatelain Date: Mon, 27 Jul 2015 18:32:55 -0600 Subject: [PATCH 042/450] Fix remaining documentation warnings --- include/git2/sys/config.h | 2 +- include/git2/sys/diff.h | 2 +- src/path.h | 2 +- src/push.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h index b5b7df15f..044e34417 100644 --- a/include/git2/sys/config.h +++ b/include/git2/sys/config.h @@ -76,7 +76,7 @@ struct git_config_backend { * Initializes a `git_config_backend` with default values. Equivalent to * creating an instance with GIT_CONFIG_BACKEND_INIT. * - * @param opts the `git_config_backend` struct to initialize. + * @param backend the `git_config_backend` struct to initialize. * @param version Version of struct; pass `GIT_CONFIG_BACKEND_VERSION` * @return Zero on success; -1 on failure. */ diff --git a/include/git2/sys/diff.h b/include/git2/sys/diff.h index aa6fed757..aefd7b997 100644 --- a/include/git2/sys/diff.h +++ b/include/git2/sys/diff.h @@ -67,7 +67,7 @@ GIT_EXTERN(int) git_diff_print_callback__to_file_handle( typedef struct { unsigned int version; size_t stat_calls; /**< Number of stat() calls performed */ - size_t oid_calculations; /** Date: Sun, 26 Jul 2015 21:12:00 +0200 Subject: [PATCH 043/450] error: store the error messages in a reusable buffer Instead of allocating a brand new buffer for each error string we want to store, we can use a per-thread buffer to store the error string and re-use the underlying storage. We already use the buffer to format the string, so this mostly makes that more direct. --- src/errors.c | 50 +++++++++++++++++++++++++++++++++----------------- src/global.c | 14 ++++++++------ src/global.h | 1 + 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/errors.c b/src/errors.c index 7a2600586..56fad009f 100644 --- a/src/errors.c +++ b/src/errors.c @@ -18,19 +18,30 @@ static git_error g_git_oom_error = { GITERR_NOMEMORY }; -static void set_error(int error_class, char *string) +static void set_error_from_buffer(int error_class) { git_error *error = &GIT_GLOBAL->error_t; + git_buf *buf = &GIT_GLOBAL->error_buf; - if (error->message != string) - git__free(error->message); - - error->message = string; + error->message = buf->ptr; error->klass = error_class; GIT_GLOBAL->last_error = error; } +static void set_error(int error_class, char *string) +{ + git_buf *buf = &GIT_GLOBAL->error_buf; + + git_buf_clear(buf); + if (string) { + git_buf_puts(buf, string); + git__free(string); + } + + set_error_from_buffer(error_class); +} + void giterr_set_oom(void) { GIT_GLOBAL->last_error = &g_git_oom_error; @@ -38,27 +49,28 @@ void giterr_set_oom(void) void giterr_set(int error_class, const char *string, ...) { - git_buf buf = GIT_BUF_INIT; va_list arglist; #ifdef GIT_WIN32 DWORD win32_error_code = (error_class == GITERR_OS) ? GetLastError() : 0; #endif int error_code = (error_class == GITERR_OS) ? errno : 0; + git_buf *buf = &GIT_GLOBAL->error_buf; + git_buf_clear(buf); if (string) { va_start(arglist, string); - git_buf_vprintf(&buf, string, arglist); + git_buf_vprintf(buf, string, arglist); va_end(arglist); if (error_class == GITERR_OS) - git_buf_PUTS(&buf, ": "); + git_buf_PUTS(buf, ": "); } if (error_class == GITERR_OS) { #ifdef GIT_WIN32 char * win32_error = git_win32_get_error_message(win32_error_code); if (win32_error) { - git_buf_puts(&buf, win32_error); + git_buf_puts(buf, win32_error); git__free(win32_error); SetLastError(0); @@ -66,26 +78,29 @@ void giterr_set(int error_class, const char *string, ...) else #endif if (error_code) - git_buf_puts(&buf, strerror(error_code)); + git_buf_puts(buf, strerror(error_code)); if (error_code) errno = 0; } - if (!git_buf_oom(&buf)) - set_error(error_class, git_buf_detach(&buf)); + if (!git_buf_oom(buf)) + set_error_from_buffer(error_class); } void giterr_set_str(int error_class, const char *string) { - char *message; + git_buf *buf = &GIT_GLOBAL->error_buf; assert(string); - message = git__strdup(string); + if (!string) + return; - if (message) - set_error(error_class, message); + git_buf_clear(buf); + git_buf_puts(buf, string); + if (!git_buf_oom(buf)) + set_error_from_buffer(error_class); } int giterr_set_regex(const regex_t *regex, int error_code) @@ -119,13 +134,14 @@ void giterr_clear(void) int giterr_detach(git_error *cpy) { git_error *error = GIT_GLOBAL->last_error; + git_buf *buf = &GIT_GLOBAL->error_buf; assert(cpy); if (!error) return -1; - cpy->message = error->message; + cpy->message = git_buf_detach(buf); cpy->klass = error->klass; error->message = NULL; diff --git a/src/global.c b/src/global.c index 37a47bd27..3d37ee4de 100644 --- a/src/global.c +++ b/src/global.c @@ -279,18 +279,19 @@ int git_libgit2_shutdown(void) git_global_st *git__global_state(void) { - void *ptr; + git_global_st *ptr; assert(git_atomic_get(&git__n_inits) > 0); if ((ptr = TlsGetValue(_tls_index)) != NULL) return ptr; - ptr = git__malloc(sizeof(git_global_st)); + ptr = git__calloc(1, sizeof(git_global_st)); if (!ptr) return NULL; - memset(ptr, 0x0, sizeof(git_global_st)); + git_buf_init(&ptr->error_buf, 0); + TlsSetValue(_tls_index, ptr); return ptr; } @@ -378,18 +379,18 @@ int git_libgit2_shutdown(void) git_global_st *git__global_state(void) { - void *ptr; + git_global_st *ptr; assert(git_atomic_get(&git__n_inits) > 0); if ((ptr = pthread_getspecific(_tls_key)) != NULL) return ptr; - ptr = git__malloc(sizeof(git_global_st)); + ptr = git__calloc(1, sizeof(git_global_st)); if (!ptr) return NULL; - memset(ptr, 0x0, sizeof(git_global_st)); + git_buf_init(&ptr->error_buf, 0); pthread_setspecific(_tls_key, ptr); return ptr; } @@ -407,6 +408,7 @@ int git_libgit2_init(void) ssl_inited = 1; } + git_buf_init(&__state.error_buf, 0); return git_atomic_inc(&git__n_inits); } diff --git a/src/global.h b/src/global.h index fdad6ba89..37e909ac6 100644 --- a/src/global.h +++ b/src/global.h @@ -14,6 +14,7 @@ typedef struct { git_error *last_error; git_error error_t; + git_buf error_buf; char oid_fmt[GIT_OID_HEXSZ+1]; } git_global_st; From 6d8f3a5162f3160069a2cbafeb44e9ffd4ebe77c Mon Sep 17 00:00:00 2001 From: Ben Chatelain Date: Tue, 28 Jul 2015 08:28:33 -0600 Subject: [PATCH 044/450] Better param docs --- src/push.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/push.h b/src/push.h index 53890f54b..a847ee0d0 100644 --- a/src/push.h +++ b/src/push.h @@ -83,7 +83,7 @@ int git_push_add_refspec(git_push *push, const char *refspec); * Update remote tips after a push * * @param push The push object - * @param callbacks The identity to use when updating reflogs + * @param callbacks the callbacks to use for this connection * * @return 0 or an error code */ @@ -100,6 +100,7 @@ int git_push_update_tips(git_push *push, const git_remote_callbacks *callbacks); * order to find out which updates were accepted or rejected. * * @param push The push object + * @param callbacks the callbacks to use for this connection * * @return 0 or an error code */ @@ -117,6 +118,7 @@ int git_push_finish(git_push *push, const git_remote_callbacks *callbacks); * * @param push The push object * @param cb The callback to call on each object + * @param data The payload passed to the callback * * @return 0 on success, non-zero callback return value, or error code */ From 31a76374a9bea7d61fec3741d46f09872a8bbb2a Mon Sep 17 00:00:00 2001 From: Anders Borum Date: Wed, 29 Jul 2015 22:23:00 +0200 Subject: [PATCH 045/450] case-insensitive check for WWW-Authenticate header Fixes issue #3338 --- src/transports/http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transports/http.c b/src/transports/http.c index 1ed292be5..e3d90de11 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -255,7 +255,7 @@ static int on_header_ready(http_subtransport *t) GITERR_CHECK_ALLOC(t->content_type); } } - else if (!strcmp("WWW-Authenticate", git_buf_cstr(name))) { + else if (!strcasecmp("WWW-Authenticate", git_buf_cstr(name))) { char *dup = git__strdup(git_buf_cstr(value)); GITERR_CHECK_ALLOC(dup); From c369b37919114f4976b7a06107506e95916e5dd0 Mon Sep 17 00:00:00 2001 From: Stefan Widgren Date: Fri, 31 Jul 2015 16:23:11 +0200 Subject: [PATCH 046/450] Remove extra semicolon outside of a function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this change, compiling with gcc and pedantic generates warning: ISO C does not allow extra ‘;’ outside of a function. --- src/describe.c | 2 +- src/indexer.c | 2 +- src/pack-objects.c | 2 +- src/pack.c | 4 ++-- src/revwalk.c | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/describe.c b/src/describe.c index 5aea927c1..48f04e858 100644 --- a/src/describe.c +++ b/src/describe.c @@ -19,7 +19,7 @@ #include "vector.h" #include "repository.h" -GIT__USE_OIDMAP; +GIT__USE_OIDMAP /* Ported from https://github.com/git/git/blob/89dde7882f71f846ccd0359756d27bebc31108de/builtin/describe.c */ diff --git a/src/indexer.c b/src/indexer.c index 512addf48..9aa092556 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -18,7 +18,7 @@ #include "oidmap.h" #include "zstream.h" -GIT__USE_OIDMAP; +GIT__USE_OIDMAP extern git_mutex git__mwindow_mutex; diff --git a/src/pack-objects.c b/src/pack-objects.c index e287e3306..c4c061a3a 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -41,7 +41,7 @@ struct pack_write_context { git_transfer_progress *stats; }; -GIT__USE_OIDMAP; +GIT__USE_OIDMAP #ifdef GIT_THREADS diff --git a/src/pack.c b/src/pack.c index cd6526721..45dd4d5be 100644 --- a/src/pack.c +++ b/src/pack.c @@ -16,8 +16,8 @@ #include -GIT__USE_OFFMAP; -GIT__USE_OIDMAP; +GIT__USE_OFFMAP +GIT__USE_OIDMAP static int packfile_open(struct git_pack_file *p); static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n); diff --git a/src/revwalk.c b/src/revwalk.c index 6acc5d034..dcdd97915 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -14,7 +14,7 @@ #include "git2/revparse.h" #include "merge.h" -GIT__USE_OIDMAP; +GIT__USE_OIDMAP git_commit_list_node *git_revwalk__commit_lookup( git_revwalk *walk, const git_oid *oid) From 63e5b5512226cfbea24bcf5d74b68cbc89197447 Mon Sep 17 00:00:00 2001 From: Linquize Date: Wed, 29 Jul 2015 00:08:37 +0800 Subject: [PATCH 047/450] index: add test for adding an old-style submodule to index --- tests/index/bypath.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/index/bypath.c b/tests/index/bypath.c index ddb766a22..b587a9ce8 100644 --- a/tests/index/bypath.c +++ b/tests/index/bypath.c @@ -33,3 +33,10 @@ void test_index_bypath__add_submodule(void) cl_git_pass(git_submodule_status(&status, g_repo, sm_name, 0)); cl_assert_equal_i(0, status & GIT_SUBMODULE_STATUS_WD_MODIFIED); } + +void test_index_bypath__add_submodule_old_style(void) +{ + const char *sm_name = "not-submodule"; + + cl_git_pass(git_index_add_bypath(g_idx, sm_name)); +} From b426ac90a958176131b9ace6a4676bc3ee1ace4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 1 Aug 2015 19:52:25 +0200 Subject: [PATCH 048/450] index: test that an unregistered submodule gets staged When we pass the path of a repository to `_bypath()`, we should behave like git and stage it as a `_COMMIT` regardless of whether it is registered a a submodule. --- tests/index/bypath.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/index/bypath.c b/tests/index/bypath.c index b587a9ce8..9706a8833 100644 --- a/tests/index/bypath.c +++ b/tests/index/bypath.c @@ -34,9 +34,15 @@ void test_index_bypath__add_submodule(void) cl_assert_equal_i(0, status & GIT_SUBMODULE_STATUS_WD_MODIFIED); } -void test_index_bypath__add_submodule_old_style(void) +void test_index_bypath__add_submodule_unregistered(void) { const char *sm_name = "not-submodule"; + const char *sm_head = "68e92c611b80ee1ed8f38314ff9577f0d15b2444"; + const git_index_entry *entry; cl_git_pass(git_index_add_bypath(g_idx, sm_name)); + + cl_assert(entry = git_index_get_bypath(g_idx, sm_name, 0)); + cl_assert_equal_s(sm_head, git_oid_tostr_s(&entry->id)); + cl_assert_equal_s(sm_name, entry->path); } From ea961abf244002f067699341734dce83f3b8b7f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 1 Aug 2015 19:53:53 +0200 Subject: [PATCH 049/450] index: stage an unregistered submodule as well We previously added logic to `_add_bypath()` to update a submodule. Go further and stage the submodule even if it's not registered to behave like git. --- src/index.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/src/index.c b/src/index.c index 501498e9e..73f0b3d26 100644 --- a/src/index.c +++ b/src/index.c @@ -1228,6 +1228,45 @@ int git_index_add_frombuffer( return 0; } +static int add_repo_as_submodule(git_index_entry **out, git_index *index, const char *path) +{ + git_repository *sub; + git_buf abspath = GIT_BUF_INIT; + git_repository *repo = INDEX_OWNER(index); + git_reference *head; + git_index_entry *entry; + struct stat st; + int error; + + if (index_entry_create(&entry, INDEX_OWNER(index), path) < 0) + return -1; + + if ((error = git_buf_joinpath(&abspath, git_repository_workdir(repo), path)) < 0) + return error; + + if ((error = p_stat(abspath.ptr, &st)) < 0) { + giterr_set(GITERR_OS, "failed to stat repository dir"); + return -1; + } + + git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode); + + if ((error = git_repository_open(&sub, abspath.ptr)) < 0) + return error; + + if ((error = git_repository_head(&head, sub)) < 0) + return error; + + git_oid_cpy(&entry->id, git_reference_target(head)); + entry->mode = GIT_FILEMODE_COMMIT; + + git_reference_free(head); + git_repository_free(sub); + git_buf_free(&abspath); + + *out = entry; + return 0; +} int git_index_add_bypath(git_index *index, const char *path) { @@ -1252,12 +1291,26 @@ int git_index_add_bypath(git_index *index, const char *path) ret = git_submodule_lookup(&sm, INDEX_OWNER(index), path); if (ret == GIT_ENOTFOUND) return giterr_restore(&err); - else - git__free(err.error_msg.message); - ret = git_submodule_add_to_index(sm, false); - git_submodule_free(sm); - return ret; + git__free(err.error_msg.message); + + /* + * EEXISTS means that there is a repository at that path, but it's not known + * as a submodule. We add its HEAD as an entry and don't register it. + */ + if (ret == GIT_EEXISTS) { + if ((ret = add_repo_as_submodule(&entry, index, path)) < 0) + return ret; + + if ((ret = index_insert(index, &entry, 1, false)) < 0) + return ret; + } else if (ret < 0) { + return ret; + } else { + ret = git_submodule_add_to_index(sm, false); + git_submodule_free(sm); + return ret; + } } /* Adding implies conflict was resolved, move conflict entries to REUC */ From ac728c248361df6ab8c23d8c5cfece7391c871db Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 3 Aug 2015 07:38:07 +0100 Subject: [PATCH 050/450] Handle ssh:// and git:// urls containing a '~' character. For such a path '/~/...' the leading '/' is stripped so the server will get a path starting with '~' and correctly handle it. --- src/transports/git.c | 2 ++ src/transports/ssh.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/transports/git.c b/src/transports/git.c index 7e0a47414..52de92d09 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -50,6 +50,8 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url) } repo = delim; + if (repo[1] == '~') + ++repo; delim = strchr(url, ':'); if (delim == NULL) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 0b0d4bac3..8f5a7164b 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -66,6 +66,8 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url) if (!git__prefixcmp(url, prefix_ssh)) { url = url + strlen(prefix_ssh); repo = strchr(url, '/'); + if (repo && repo[1] == '~') + ++repo; } else { repo = strchr(url, ':'); if (repo) repo++; From 5ef4b86015309c157b20260905cb5d0c9bb47ca8 Mon Sep 17 00:00:00 2001 From: Michael Procter Date: Thu, 23 Jul 2015 13:16:19 +0100 Subject: [PATCH 051/450] Add failing test for capture/restore oom error --- tests/core/errors.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/core/errors.c b/tests/core/errors.c index a06ec4abc..6aceb30fd 100644 --- a/tests/core/errors.c +++ b/tests/core/errors.c @@ -110,6 +110,29 @@ void test_core_errors__restore(void) cl_assert_equal_s("Foo: bar", giterr_last()->message); } +void test_core_errors__restore_oom(void) +{ + git_error_state err_state = {0}; + const char *static_message = NULL; + + giterr_clear(); + + giterr_set_oom(); /* internal fn */ + static_message = giterr_last()->message; + + cl_assert_equal_i(-1, giterr_capture(&err_state, -1)); + + cl_assert(giterr_last() == NULL); + + cl_assert_(err_state.error_msg.message != static_message, "pointer to static buffer exposed"); + + giterr_restore(&err_state); + + cl_assert(giterr_last()->klass == GITERR_NOMEMORY); + + giterr_clear(); +} + static int test_arraysize_multiply(size_t nelem, size_t size) { size_t out; From c2f17bda074b2e5718456aed75fedd2196c8dc94 Mon Sep 17 00:00:00 2001 From: Michael Procter Date: Thu, 23 Jul 2015 13:17:08 +0100 Subject: [PATCH 052/450] Ensure static oom error message not detached Error messages that are detached are assumed to be dynamically allocated. Passing a pointer to the static oom error message can cause an attempt to free the static buffer later. This change checks if the oom error message is about to be detached and detaches a copy instead. --- src/errors.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/errors.c b/src/errors.c index 7a2600586..979602a2a 100644 --- a/src/errors.c +++ b/src/errors.c @@ -125,10 +125,14 @@ int giterr_detach(git_error *cpy) if (!error) return -1; - cpy->message = error->message; + if (error == &g_git_oom_error) { + cpy->message = git__strdup(error->message); + } else { + cpy->message = error->message; + error->message = NULL; + } cpy->klass = error->klass; - error->message = NULL; giterr_clear(); return 0; From 25dbcf34993cad3cdc3981f1ed394d3374fb640f Mon Sep 17 00:00:00 2001 From: Michael Procter Date: Mon, 27 Jul 2015 09:59:07 +0100 Subject: [PATCH 053/450] Make giterr_detach no longer public --- include/git2/errors.h | 12 ------------ src/errors.c | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/include/git2/errors.h b/include/git2/errors.h index e189e55f1..4698366d8 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -113,18 +113,6 @@ GIT_EXTERN(const git_error *) giterr_last(void); */ GIT_EXTERN(void) giterr_clear(void); -/** - * Get the last error data and clear it. - * - * This copies the last error into the given `git_error` struct - * and returns 0 if the copy was successful, leaving the error - * cleared as if `giterr_clear` had been called. - * - * If there was no existing error in the library, -1 will be returned - * and the contents of `cpy` will be left unmodified. - */ -GIT_EXTERN(int) giterr_detach(git_error *cpy); - /** * Set the error message string for this thread. * diff --git a/src/errors.c b/src/errors.c index 979602a2a..95c62176c 100644 --- a/src/errors.c +++ b/src/errors.c @@ -116,7 +116,7 @@ void giterr_clear(void) #endif } -int giterr_detach(git_error *cpy) +static int giterr_detach(git_error *cpy) { git_error *error = GIT_GLOBAL->last_error; From 0fcfb60dc4f5e6cfd91c902d844f5d8665a5c1a7 Mon Sep 17 00:00:00 2001 From: Michael Procter Date: Mon, 27 Jul 2015 10:10:18 +0100 Subject: [PATCH 054/450] Make giterr_restore aware of g_git_oom_error Allow restoring a previously captured oom error, by detecting when the captured message pointer points to the static oom error message. This means there is no need to strdup the message in giterr_detach. --- src/errors.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/errors.c b/src/errors.c index 95c62176c..f10430c10 100644 --- a/src/errors.c +++ b/src/errors.c @@ -125,14 +125,12 @@ static int giterr_detach(git_error *cpy) if (!error) return -1; - if (error == &g_git_oom_error) { - cpy->message = git__strdup(error->message); - } else { - cpy->message = error->message; - error->message = NULL; - } + cpy->message = error->message; cpy->klass = error->klass; + if (error != &g_git_oom_error) { + error->message = NULL; + } giterr_clear(); return 0; @@ -153,8 +151,13 @@ int giterr_capture(git_error_state *state, int error_code) int giterr_restore(git_error_state *state) { - if (state && state->error_code && state->error_msg.message) - set_error(state->error_msg.klass, state->error_msg.message); + if (state && state->error_code && state->error_msg.message) { + if (state->error_msg.message == g_git_oom_error.message) { + giterr_set_oom(); + } else { + set_error(state->error_msg.klass, state->error_msg.message); + } + } else giterr_clear(); From 988ea59443b71f4a07b19fff837ccaa1659dbcc0 Mon Sep 17 00:00:00 2001 From: Michael Procter Date: Mon, 27 Jul 2015 10:13:49 +0100 Subject: [PATCH 055/450] Test: check restored oom error points to static buffer --- tests/core/errors.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/core/errors.c b/tests/core/errors.c index 6aceb30fd..25bbbd5f6 100644 --- a/tests/core/errors.c +++ b/tests/core/errors.c @@ -113,22 +113,22 @@ void test_core_errors__restore(void) void test_core_errors__restore_oom(void) { git_error_state err_state = {0}; - const char *static_message = NULL; + const git_error *oom_error = NULL; giterr_clear(); giterr_set_oom(); /* internal fn */ - static_message = giterr_last()->message; + oom_error = giterr_last(); + cl_assert(oom_error); cl_assert_equal_i(-1, giterr_capture(&err_state, -1)); cl_assert(giterr_last() == NULL); - cl_assert_(err_state.error_msg.message != static_message, "pointer to static buffer exposed"); - giterr_restore(&err_state); cl_assert(giterr_last()->klass == GITERR_NOMEMORY); + cl_assert_(giterr_last() == oom_error, "static oom error not restored"); giterr_clear(); } From bdec336301c348404b0590025025420febae5b98 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 3 Aug 2015 17:48:33 -0500 Subject: [PATCH 056/450] win32: ensure hidden files can be staged --- src/repository.c | 4 ++-- src/win32/w32_util.c | 36 ++++++++++++++++++++++++++++++------ src/win32/w32_util.h | 16 +++++++++++++--- tests/index/addall.c | 35 +++++++++++++++++++++++++++++++++++ tests/index/bypath.c | 26 ++++++++++++++++++++++++++ 5 files changed, 106 insertions(+), 11 deletions(-) diff --git a/src/repository.c b/src/repository.c index 08f4baa20..0f37cfbfe 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1279,7 +1279,7 @@ static int repo_write_template( #ifdef GIT_WIN32 if (!error && hidden) { - if (git_win32__sethidden(path.ptr) < 0) + if (git_win32__set_hidden(path.ptr, true) < 0) error = -1; } #else @@ -1373,7 +1373,7 @@ static int repo_init_structure( /* Hide the ".git" directory */ #ifdef GIT_WIN32 if ((opts->flags & GIT_REPOSITORY_INIT__HAS_DOTGIT) != 0) { - if (git_win32__sethidden(repo_dir) < 0) { + if (git_win32__set_hidden(repo_dir, true) < 0) { giterr_set(GITERR_OS, "Failed to mark Git repository folder as hidden"); return -1; diff --git a/src/win32/w32_util.c b/src/win32/w32_util.c index 2e52525d5..60311bb50 100644 --- a/src/win32/w32_util.c +++ b/src/win32/w32_util.c @@ -48,7 +48,35 @@ bool git_win32__findfirstfile_filter(git_win32_path dest, const char *src) * @param path The path which should receive the +H bit. * @return 0 on success; -1 on failure */ -int git_win32__sethidden(const char *path) +int git_win32__set_hidden(const char *path, bool hidden) +{ + git_win32_path buf; + DWORD attrs, newattrs; + + if (git_win32_path_from_utf8(buf, path) < 0) + return -1; + + attrs = GetFileAttributesW(buf); + + /* Ensure the path exists */ + if (attrs == INVALID_FILE_ATTRIBUTES) + return -1; + + if (hidden) + newattrs = attrs | FILE_ATTRIBUTE_HIDDEN; + else + newattrs = attrs & ~FILE_ATTRIBUTE_HIDDEN; + + if (attrs != newattrs && !SetFileAttributesW(buf, newattrs)) { + giterr_set(GITERR_OS, "Failed to %s hidden bit for '%s'", + hidden ? "set" : "unset", path); + return -1; + } + + return 0; +} + +int git_win32__hidden(bool *out, const char *path) { git_win32_path buf; DWORD attrs; @@ -62,11 +90,7 @@ int git_win32__sethidden(const char *path) if (attrs == INVALID_FILE_ATTRIBUTES) return -1; - /* If the item isn't already +H, add the bit */ - if ((attrs & FILE_ATTRIBUTE_HIDDEN) == 0 && - !SetFileAttributesW(buf, attrs | FILE_ATTRIBUTE_HIDDEN)) - return -1; - + *out = (attrs & FILE_ATTRIBUTE_HIDDEN) ? true : false; return 0; } diff --git a/src/win32/w32_util.h b/src/win32/w32_util.h index 377d651a8..8db3afbec 100644 --- a/src/win32/w32_util.h +++ b/src/win32/w32_util.h @@ -40,12 +40,22 @@ GIT_INLINE(bool) git_win32__isalpha(wchar_t c) bool git_win32__findfirstfile_filter(git_win32_path dest, const char *src); /** - * Ensures the given path (file or folder) has the +H (hidden) attribute set. + * Ensures the given path (file or folder) has the +H (hidden) attribute set + * or unset. * - * @param path The path which should receive the +H bit. + * @param path The path that should receive the +H bit. + * @param hidden true to set +H, false to unset it * @return 0 on success; -1 on failure */ -int git_win32__sethidden(const char *path); +extern int git_win32__set_hidden(const char *path, bool hidden); + +/** + * Determines if the given file or folder has the hidden attribute set. + * @param hidden pointer to store hidden value + * @param path The path that should be queried for hiddenness. + * @return 0 on success or an error code. + */ +extern int git_win32__hidden(bool *hidden, const char *path); /** * Removes any trailing backslashes from a path, except in the case of a drive diff --git a/tests/index/addall.c b/tests/index/addall.c index 9ddb27f95..7b7a178d1 100644 --- a/tests/index/addall.c +++ b/tests/index/addall.c @@ -307,6 +307,41 @@ void test_index_addall__files_in_folders(void) git_index_free(index); } +void test_index_addall__hidden_files(void) +{ + git_index *index; + + GIT_UNUSED(index); + +#ifdef GIT_WIN32 + addall_create_test_repo(true); + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL)); + check_stat_data(index, TEST_DIR "/file.bar", true); + check_status(g_repo, 2, 0, 0, 0, 0, 0, 1, 0); + + cl_git_mkfile(TEST_DIR "/file.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/more.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/other.zzz", "yet another one"); + + check_status(g_repo, 2, 0, 0, 3, 0, 0, 1, 0); + + cl_git_pass(git_win32__set_hidden(TEST_DIR "/file.zzz", true)); + cl_git_pass(git_win32__set_hidden(TEST_DIR "/more.zzz", true)); + cl_git_pass(git_win32__set_hidden(TEST_DIR "/other.zzz", true)); + + check_status(g_repo, 2, 0, 0, 3, 0, 0, 1, 0); + + cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL)); + check_stat_data(index, TEST_DIR "/file.bar", true); + check_status(g_repo, 5, 0, 0, 0, 0, 0, 1, 0); + + git_index_free(index); +#endif +} + static int addall_match_prefix( const char *path, const char *matched_pathspec, void *payload) { diff --git a/tests/index/bypath.c b/tests/index/bypath.c index 9706a8833..b607e1732 100644 --- a/tests/index/bypath.c +++ b/tests/index/bypath.c @@ -46,3 +46,29 @@ void test_index_bypath__add_submodule_unregistered(void) cl_assert_equal_s(sm_head, git_oid_tostr_s(&entry->id)); cl_assert_equal_s(sm_name, entry->path); } + +void test_index_bypath__add_hidden(void) +{ + const git_index_entry *entry; + bool hidden; + + GIT_UNUSED(entry); + GIT_UNUSED(hidden); + +#ifdef GIT_WIN32 + cl_git_mkfile("submod2/hidden_file", "you can't see me"); + + cl_git_pass(git_win32__hidden(&hidden, "submod2/hidden_file")); + cl_assert(!hidden); + + cl_git_pass(git_win32__set_hidden("submod2/hidden_file", true)); + + cl_git_pass(git_win32__hidden(&hidden, "submod2/hidden_file")); + cl_assert(hidden); + + cl_git_pass(git_index_add_bypath(g_idx, "hidden_file")); + + cl_assert(entry = git_index_get_bypath(g_idx, "hidden_file", 0)); + cl_assert_equal_i(GIT_FILEMODE_BLOB, entry->mode); +#endif +} From ef4857c2b3d4a61fd1d840199afc92eaf2e15345 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 3 Aug 2015 16:50:27 -0500 Subject: [PATCH 057/450] errors: tighten up git_error_state OOMs a bit more When an error state is an OOM, make sure that we treat is specially and do not try to free it. --- src/clone.c | 4 +-- src/common.h | 13 ++++--- src/errors.c | 82 ++++++++++++++++++++++++++------------------- src/index.c | 6 ++-- src/iterator.c | 4 +-- tests/core/errors.c | 33 +++++++++++++++--- 6 files changed, 91 insertions(+), 51 deletions(-) diff --git a/src/clone.c b/src/clone.c index 070daf94d..6b4b7ae53 100644 --- a/src/clone.c +++ b/src/clone.c @@ -440,14 +440,14 @@ int git_clone( if (error != 0) { git_error_state last_error = {0}; - giterr_capture(&last_error, error); + giterr_state_capture(&last_error, error); git_repository_free(repo); repo = NULL; (void)git_futils_rmdir_r(local_path, NULL, rmdir_flags); - giterr_restore(&last_error); + giterr_state_restore(&last_error); } *out = repo; diff --git a/src/common.h b/src/common.h index 2b1dedc01..6dca36fbd 100644 --- a/src/common.h +++ b/src/common.h @@ -141,20 +141,25 @@ void giterr_system_set(int code); * Structure to preserve libgit2 error state */ typedef struct { - int error_code; + int error_code; + unsigned int oom : 1; git_error error_msg; } git_error_state; /** * Capture current error state to restore later, returning error code. - * If `error_code` is zero, this does nothing and returns zero. + * If `error_code` is zero, this does not clear the current error state. + * You must either restore this error state, or free it. */ -int giterr_capture(git_error_state *state, int error_code); +extern int giterr_state_capture(git_error_state *state, int error_code); /** * Restore error state to a previous value, returning saved error code. */ -int giterr_restore(git_error_state *state); +extern int giterr_state_restore(git_error_state *state); + +/** Free an error state. */ +extern void giterr_state_free(git_error_state *state); /** * Check a versioned structure for validity diff --git a/src/errors.c b/src/errors.c index 973326c00..91acc3541 100644 --- a/src/errors.c +++ b/src/errors.c @@ -131,53 +131,65 @@ void giterr_clear(void) #endif } -static int giterr_detach(git_error *cpy) -{ - git_error *error = GIT_GLOBAL->last_error; - git_buf *buf = &GIT_GLOBAL->error_buf; - - assert(cpy); - - if (!error) - return -1; - - cpy->message = git_buf_detach(buf); - cpy->klass = error->klass; - - if (error != &g_git_oom_error) { - error->message = NULL; - } - giterr_clear(); - - return 0; -} - const git_error *giterr_last(void) { return GIT_GLOBAL->last_error; } -int giterr_capture(git_error_state *state, int error_code) +int giterr_state_capture(git_error_state *state, int error_code) { + git_error *error = GIT_GLOBAL->last_error; + git_buf *error_buf = &GIT_GLOBAL->error_buf; + + memset(state, 0, sizeof(git_error_state)); + + if (!error_code) + return 0; + state->error_code = error_code; - if (error_code) - giterr_detach(&state->error_msg); + state->oom = (error == &g_git_oom_error); + + if (error) { + state->error_msg.klass = error->klass; + + if (state->oom) + state->error_msg.message = g_git_oom_error.message; + else + state->error_msg.message = git_buf_detach(error_buf); + } + + giterr_clear(); return error_code; } -int giterr_restore(git_error_state *state) +int giterr_state_restore(git_error_state *state) { - if (state && state->error_code && state->error_msg.message) { - if (state->error_msg.message == g_git_oom_error.message) { - giterr_set_oom(); - } else { - set_error(state->error_msg.klass, state->error_msg.message); - } - } - else - giterr_clear(); + int ret = 0; - return state ? state->error_code : 0; + giterr_clear(); + + if (state && state->error_msg.message) { + if (state->oom) + giterr_set_oom(); + else + set_error(state->error_msg.klass, state->error_msg.message); + + ret = state->error_code; + memset(state, 0, sizeof(git_error_state)); + } + + return ret; +} + +void giterr_state_free(git_error_state *state) +{ + if (!state) + return; + + if (!state->oom) + git__free(state->error_msg.message); + + memset(state, 0, sizeof(git_error_state)); } int giterr_system_last(void) diff --git a/src/index.c b/src/index.c index 73f0b3d26..e424698bb 100644 --- a/src/index.c +++ b/src/index.c @@ -1286,13 +1286,13 @@ int git_index_add_bypath(git_index *index, const char *path) git_submodule *sm; git_error_state err; - giterr_capture(&err, ret); + giterr_state_capture(&err, ret); ret = git_submodule_lookup(&sm, INDEX_OWNER(index), path); if (ret == GIT_ENOTFOUND) - return giterr_restore(&err); + return giterr_state_restore(&err); - git__free(err.error_msg.message); + giterr_state_free(&err); /* * EEXISTS means that there is a repository at that path, but it's not known diff --git a/src/iterator.c b/src/iterator.c index cf51a340d..900ffdcaa 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -1120,7 +1120,7 @@ static int fs_iterator__expand_dir(fs_iterator *fi) if (error < 0) { git_error_state last_error = { 0 }; - giterr_capture(&last_error, error); + giterr_state_capture(&last_error, error); /* these callbacks may clear the error message */ fs_iterator__free_frame(ff); @@ -1128,7 +1128,7 @@ static int fs_iterator__expand_dir(fs_iterator *fi) /* next time return value we skipped to */ fi->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS; - return giterr_restore(&last_error); + return giterr_state_restore(&last_error); } if (ff->entries.length == 0) { diff --git a/tests/core/errors.c b/tests/core/errors.c index 25bbbd5f6..ab18951a6 100644 --- a/tests/core/errors.c +++ b/tests/core/errors.c @@ -93,23 +93,44 @@ void test_core_errors__restore(void) giterr_clear(); cl_assert(giterr_last() == NULL); - cl_assert_equal_i(0, giterr_capture(&err_state, 0)); + cl_assert_equal_i(0, giterr_state_capture(&err_state, 0)); memset(&err_state, 0x0, sizeof(git_error_state)); giterr_set(42, "Foo: %s", "bar"); - cl_assert_equal_i(-1, giterr_capture(&err_state, -1)); + cl_assert_equal_i(-1, giterr_state_capture(&err_state, -1)); cl_assert(giterr_last() == NULL); giterr_set(99, "Bar: %s", "foo"); - giterr_restore(&err_state); + giterr_state_restore(&err_state); cl_assert_equal_i(42, giterr_last()->klass); cl_assert_equal_s("Foo: bar", giterr_last()->message); } +void test_core_errors__free_state(void) +{ + git_error_state err_state = {0}; + + giterr_clear(); + + giterr_set(42, "Foo: %s", "bar"); + cl_assert_equal_i(-1, giterr_state_capture(&err_state, -1)); + + giterr_set(99, "Bar: %s", "foo"); + + giterr_state_free(&err_state); + + cl_assert_equal_i(99, giterr_last()->klass); + cl_assert_equal_s("Bar: foo", giterr_last()->message); + + giterr_state_restore(&err_state); + + cl_assert(giterr_last() == NULL); +} + void test_core_errors__restore_oom(void) { git_error_state err_state = {0}; @@ -121,11 +142,13 @@ void test_core_errors__restore_oom(void) oom_error = giterr_last(); cl_assert(oom_error); - cl_assert_equal_i(-1, giterr_capture(&err_state, -1)); + cl_assert_equal_i(-1, giterr_state_capture(&err_state, -1)); cl_assert(giterr_last() == NULL); + cl_assert_equal_i(GITERR_NOMEMORY, err_state.error_msg.klass); + cl_assert_equal_s("Out of memory", err_state.error_msg.message); - giterr_restore(&err_state); + giterr_state_restore(&err_state); cl_assert(giterr_last()->klass == GITERR_NOMEMORY); cl_assert_(giterr_last() == oom_error, "static oom error not restored"); From eba784d24d12ad7243708d97d80427bbdc9fd2b7 Mon Sep 17 00:00:00 2001 From: John Haley Date: Wed, 5 Aug 2015 10:19:06 -0700 Subject: [PATCH 058/450] Fix duplicate basenames to support older VS With Visual Studio versions 2008 and older they ignore the full path to files and only check the basename of the file to find a collision. Additionally, having duplicate basenames can break other build tools like GYP. This fixes https://github.com/libgit2/libgit2/issues/3356 --- src/path.c | 2 +- src/util.c | 2 +- src/win32/{buffer.c => w32_buffer.c} | 3 +-- src/win32/{buffer.h => w32_buffer.h} | 0 4 files changed, 3 insertions(+), 4 deletions(-) rename src/win32/{buffer.c => w32_buffer.c} (98%) rename src/win32/{buffer.h => w32_buffer.h} (100%) diff --git a/src/path.c b/src/path.c index 8317aaaa7..9ce5d2978 100644 --- a/src/path.c +++ b/src/path.c @@ -10,7 +10,7 @@ #include "repository.h" #ifdef GIT_WIN32 #include "win32/posix.h" -#include "win32/buffer.h" +#include "win32/w32_buffer.h" #include "win32/w32_util.h" #include "win32/version.h" #else diff --git a/src/util.c b/src/util.c index b08b2b884..b3929bca2 100644 --- a/src/util.c +++ b/src/util.c @@ -11,7 +11,7 @@ #include "posix.h" #ifdef GIT_WIN32 -# include "win32/buffer.h" +# include "win32/w32_buffer.h" #endif #ifdef _MSC_VER diff --git a/src/win32/buffer.c b/src/win32/w32_buffer.c similarity index 98% rename from src/win32/buffer.c rename to src/win32/w32_buffer.c index 74950189e..9122baaa6 100644 --- a/src/win32/buffer.c +++ b/src/win32/w32_buffer.c @@ -6,7 +6,7 @@ */ #include "common.h" -#include "buffer.h" +#include "w32_buffer.h" #include "../buffer.h" #include "utf-conv.h" @@ -52,4 +52,3 @@ int git_buf_put_w(git_buf *buf, const wchar_t *string_w, size_t len_w) buf->ptr[buf->size] = '\0'; return 0; } - diff --git a/src/win32/buffer.h b/src/win32/w32_buffer.h similarity index 100% rename from src/win32/buffer.h rename to src/win32/w32_buffer.h From c27b4afcdd80f5a45d7120044bf3d78272181abb Mon Sep 17 00:00:00 2001 From: Slava Karpenko Date: Thu, 6 Aug 2015 11:06:17 +0300 Subject: [PATCH 059/450] Forcing libssh2 lib location OS X may have libssh2 in diff locations, so CHECK_LIBRARY_EXISTS may check the wrong lib; forcing it to use a found directory. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a15ce7dbd..b6a8b6d4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -286,7 +286,7 @@ IF (LIBSSH2_FOUND) #SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} ${LIBSSH2_LDFLAGS}") SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES}) - CHECK_LIBRARY_EXISTS("${LIBSSH2_LIBRARIES}" libssh2_userauth_publickey_frommemory "" HAVE_LIBSSH2_MEMORY_CREDENTIALS) + CHECK_LIBRARY_EXISTS("${LIBSSH2_LIBRARIES}" libssh2_userauth_publickey_frommemory "${LIBSSH2_LIBRARY_DIRS}" HAVE_LIBSSH2_MEMORY_CREDENTIALS) IF (HAVE_LIBSSH2_MEMORY_CREDENTIALS) ADD_DEFINITIONS(-DGIT_SSH_MEMORY_CREDENTIALS) ENDIF() From dc0351893accfc94911cf7067f2b96736675a419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 6 Aug 2015 13:02:35 +0200 Subject: [PATCH 060/450] curl: use the most secure auth method for the proxy When curl uses a proxy, it will only use Basic unless we prompt it to try to use the most secure on it has available. This is something which git did recently, and it seems like a good idea. --- src/curl_stream.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/curl_stream.c b/src/curl_stream.c index 63421fcf7..798bd5a52 100644 --- a/src/curl_stream.c +++ b/src/curl_stream.c @@ -220,6 +220,7 @@ int git_curl_stream_new(git_stream **out, const char *host, const char *port) curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1); curl_easy_setopt(handle, CURLOPT_CERTINFO, 1); curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1); + curl_easy_setopt(handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY); /* curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); */ From a879276783de5cc2c82543a9f930337f000aa8e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 11 Aug 2015 20:44:19 +0200 Subject: [PATCH 061/450] remote: add failing test for a mirror refspec While we download the remote's remote-tracking branches, we don't download the tag. This points to the tag auto-follow rules interfering with the refspec. --- tests/network/fetchlocal.c | 39 +++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/tests/network/fetchlocal.c b/tests/network/fetchlocal.c index 06ee3dd36..17c8f26e3 100644 --- a/tests/network/fetchlocal.c +++ b/tests/network/fetchlocal.c @@ -369,17 +369,46 @@ void test_network_fetchlocal__clone_into_mirror(void) { git_clone_options opts = GIT_CLONE_OPTIONS_INIT; git_repository *repo; - git_reference *head; + git_reference *ref; opts.bare = true; opts.remote_cb = remote_mirror_cb; cl_git_pass(git_clone(&repo, cl_git_fixture_url("testrepo.git"), "./foo.git", &opts)); - cl_git_pass(git_reference_lookup(&head, repo, "HEAD")); - cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head)); - cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head)); + cl_git_pass(git_reference_lookup(&ref, repo, "HEAD")); + cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(ref)); + cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(ref)); - git_reference_free(head); + git_reference_free(ref); + cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/test/master")); + + git_reference_free(ref); + git_repository_free(repo); + cl_fixture_cleanup("./foo.git"); +} + +void test_network_fetchlocal__all_refs(void) +{ + git_repository *repo; + git_remote *remote; + git_reference *ref; + char *allrefs = "+refs/*:refs/*"; + git_strarray refspecs = { + &allrefs, + 1, + }; + + cl_git_pass(git_repository_init(&repo, "./foo.git", true)); + cl_git_pass(git_remote_create_anonymous(&remote, repo, cl_git_fixture_url("testrepo.git"))); + cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, NULL)); + + cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/test/master")); + git_reference_free(ref); + + cl_git_pass(git_reference_lookup(&ref, repo, "refs/tags/test")); + git_reference_free(ref); + + git_remote_free(remote); git_repository_free(repo); cl_fixture_cleanup("./foo.git"); } From e3e017d4839893cb6e5339b063afafa60b56bf0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 11 Aug 2015 22:51:53 +0200 Subject: [PATCH 062/450] remote: don't confuse tag auto-follow rules with refspec matching When we're looking to update a tag, we can't stop if the tag auto-follow rules don't say to update it. The tag might still match the refspec we were given. --- src/remote.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/remote.c b/src/remote.c index d31e1b89e..7404bf49f 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1334,11 +1334,13 @@ static int update_tips_for_spec( for (; i < refs->length; ++i) { head = git_vector_get(refs, i); autotag = 0; + git_buf_clear(&refname); /* Ignore malformed ref names (which also saves us from tag^{} */ if (!git_reference_is_valid_name(head->name)) continue; + /* If we have a tag, see if the auto-follow rules say to update it */ if (git_refspec_src_matches(&tagspec, head->name)) { if (tagopt != GIT_REMOTE_DOWNLOAD_TAGS_NONE) { @@ -1348,10 +1350,11 @@ static int update_tips_for_spec( git_buf_clear(&refname); if (git_buf_puts(&refname, head->name) < 0) goto on_error; - } else { - continue; } - } else if (git_refspec_src_matches(spec, head->name)) { + } + + /* If we didn't want to auto-follow the tag, check if the refspec matches */ + if (!autotag && git_refspec_src_matches(spec, head->name)) { if (spec->dst) { if (git_refspec_transform(&refname, spec, head->name) < 0) goto on_error; @@ -1365,7 +1368,10 @@ static int update_tips_for_spec( continue; } - } else { + } + + /* If we still don't have a refname, we don't want it */ + if (git_buf_len(&refname) == 0) { continue; } From 3ce9e4d23572718deeab438ce149013eece57371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 1 Jun 2015 08:45:15 +0200 Subject: [PATCH 063/450] config: write the modified file to memory Instead of writing into the filebuf directly, make the functions to write the modified config file write into a buffer which can then be dumped into the lockfile for committing. This allows us to re-use the same code for modifying a locked configuration, as we can simply skip the last step of dumping the data to disk. --- src/config_file.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/config_file.c b/src/config_file.c index 52a5376bd..fd03c200f 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -1602,7 +1602,7 @@ static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct re return config_parse(reader, NULL, read_on_variable, NULL, NULL, &parse_data); } -static int write_section(git_filebuf *file, const char *key) +static int write_section(git_buf *fbuf, const char *key) { int result; const char *dot; @@ -1626,7 +1626,7 @@ static int write_section(git_filebuf *file, const char *key) if (git_buf_oom(&buf)) return -1; - result = git_filebuf_write(file, git_buf_cstr(&buf), buf.size); + result = git_buf_put(fbuf, git_buf_cstr(&buf), buf.size); git_buf_free(&buf); return result; @@ -1651,7 +1651,7 @@ static const char *quotes_for_value(const char *value) } struct write_data { - git_filebuf *file; + git_buf *buf; unsigned int in_section : 1, preg_replaced : 1; const char *section; @@ -1662,10 +1662,10 @@ struct write_data { static int write_line(struct write_data *write_data, const char *line, size_t line_len) { - int result = git_filebuf_write(write_data->file, line, line_len); + int result = git_buf_put(write_data->buf, line, line_len); if (!result && line_len && line[line_len-1] != '\n') - result = git_filebuf_printf(write_data->file, "\n"); + result = git_buf_printf(write_data->buf, "\n"); return result; } @@ -1676,7 +1676,7 @@ static int write_value(struct write_data *write_data) int result; q = quotes_for_value(write_data->value); - result = git_filebuf_printf(write_data->file, + result = git_buf_printf(write_data->buf, "\t%s = %s%s%s\n", write_data->name, q, write_data->value, q); /* If we are updating a single name/value, we're done. Setting `value` @@ -1782,7 +1782,7 @@ static int write_on_eof(struct reader **reader, void *data) * value. */ if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) { - if ((result = write_section(write_data->file, write_data->section)) == 0) + if ((result = write_section(write_data->buf, write_data->section)) == 0) result = write_value(write_data); } @@ -1797,6 +1797,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p int result; char *section, *name, *ldot; git_filebuf file = GIT_FILEBUF_INIT; + git_buf buf = GIT_BUF_INIT; struct reader *reader = git_array_get(cfg->readers, 0); struct write_data write_data; @@ -1827,7 +1828,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p name = ldot + 1; section = git__strndup(key, ldot - key); - write_data.file = &file; + write_data.buf = &buf; write_data.section = section; write_data.in_section = 0; write_data.preg_replaced = 0; @@ -1839,10 +1840,13 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p git__free(section); if (result < 0) { + git_buf_free(&buf); git_filebuf_cleanup(&file); goto done; } + git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf)); + /* refresh stats - if this errors, then commit will error too */ (void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file); From b1667039640ba3464ea0e2d13ad28c9244d80b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 1 Jun 2015 19:17:03 +0200 Subject: [PATCH 064/450] config: implement basic transactional support When a configuration file is locked, any updates made to it will be done to the in-memory copy of the file. This allows for multiple updates to happen while we hold the lock, preventing races during complex config-file manipulation. --- include/git2/sys/config.h | 14 ++++++ src/config_file.c | 93 ++++++++++++++++++++++++++++++++++----- src/config_file.h | 10 +++++ tests/config/write.c | 46 +++++++++++++++++++ 4 files changed, 151 insertions(+), 12 deletions(-) diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h index 044e34417..4dad6da42 100644 --- a/include/git2/sys/config.h +++ b/include/git2/sys/config.h @@ -67,6 +67,20 @@ struct git_config_backend { int (*iterator)(git_config_iterator **, struct git_config_backend *); /** Produce a read-only version of this backend */ int (*snapshot)(struct git_config_backend **, struct git_config_backend *); + /** + * Lock this backend. + * + * Prevent any writes to the data store backing this + * backend. Any updates must not be visible to any other + * readers. + */ + int (*lock)(struct git_config_backend *); + /** + * Unlock the data store backing this backend. If success is + * true, the changes should be committed, otherwise rolled + * back. + */ + int (*unlock)(struct git_config_backend *, int success); void (*free)(struct git_config_backend *); }; #define GIT_CONFIG_BACKEND_VERSION 1 diff --git a/src/config_file.c b/src/config_file.c index fd03c200f..a3fec1b34 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -105,6 +105,10 @@ typedef struct { git_array_t(struct reader) readers; + bool locked; + git_filebuf locked_buf; + git_buf locked_content; + char *file_path; } diskfile_backend; @@ -685,6 +689,42 @@ static int config_snapshot(git_config_backend **out, git_config_backend *in) return git_config_file__snapshot(out, b); } +static int config_lock(git_config_backend *_cfg) +{ + diskfile_backend *cfg = (diskfile_backend *) _cfg; + int error; + + if ((error = git_filebuf_open(&cfg->locked_buf, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) + return error; + + error = git_futils_readbuffer(&cfg->locked_content, cfg->file_path); + if (error < 0 && error != GIT_ENOTFOUND) { + git_filebuf_cleanup(&cfg->locked_buf); + return error; + } + + cfg->locked = true; + return 0; + +} + +static int config_unlock(git_config_backend *_cfg, int success) +{ + diskfile_backend *cfg = (diskfile_backend *) _cfg; + int error = 0; + + if (success) { + git_filebuf_write(&cfg->locked_buf, cfg->locked_content.ptr, cfg->locked_content.size); + error = git_filebuf_commit(&cfg->locked_buf); + } + + git_filebuf_cleanup(&cfg->locked_buf); + git_buf_free(&cfg->locked_content); + cfg->locked = false; + + return error; +} + int git_config_file__ondisk(git_config_backend **out, const char *path) { diskfile_backend *backend; @@ -706,6 +746,8 @@ int git_config_file__ondisk(git_config_backend **out, const char *path) backend->header.parent.del_multivar = config_delete_multivar; backend->header.parent.iterator = config_iterator_new; backend->header.parent.snapshot = config_snapshot; + backend->header.parent.lock = config_lock; + backend->header.parent.unlock = config_unlock; backend->header.parent.free = backend_free; *out = (git_config_backend *)backend; @@ -750,6 +792,21 @@ static int config_delete_readonly(git_config_backend *cfg, const char *name) return config_error_readonly(); } +static int config_lock_readonly(git_config_backend *_cfg) +{ + GIT_UNUSED(_cfg); + + return config_error_readonly(); +} + +static int config_unlock_readonly(git_config_backend *_cfg, int success) +{ + GIT_UNUSED(_cfg); + GIT_UNUSED(success); + + return config_error_readonly(); +} + static void backend_readonly_free(git_config_backend *_backend) { diskfile_backend *backend = (diskfile_backend *)_backend; @@ -803,6 +860,8 @@ int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in) backend->header.parent.del = config_delete_readonly; backend->header.parent.del_multivar = config_delete_multivar_readonly; backend->header.parent.iterator = config_iterator_new; + backend->header.parent.lock = config_lock_readonly; + backend->header.parent.unlock = config_unlock_readonly; backend->header.parent.free = backend_readonly_free; *out = (git_config_backend *)backend; @@ -1801,15 +1860,19 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p struct reader *reader = git_array_get(cfg->readers, 0); struct write_data write_data; - /* Lock the file */ - if ((result = git_filebuf_open( - &file, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) { + if (cfg->locked) { + result = git_buf_puts(&reader->buffer, git_buf_cstr(&cfg->locked_content)); + } else { + /* Lock the file */ + if ((result = git_filebuf_open( + &file, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) { git_buf_free(&reader->buffer); return result; - } + } - /* We need to read in our own config file */ - result = git_futils_readbuffer(&reader->buffer, cfg->file_path); + /* We need to read in our own config file */ + result = git_futils_readbuffer(&reader->buffer, cfg->file_path); + } /* Initialise the reading position */ if (result == GIT_ENOTFOUND) { @@ -1840,20 +1903,26 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p git__free(section); if (result < 0) { - git_buf_free(&buf); git_filebuf_cleanup(&file); goto done; } - git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf)); + if (cfg->locked) { + size_t len = buf.asize; + /* Update our copy with the modified contents */ + git_buf_free(&cfg->locked_content); + git_buf_attach(&cfg->locked_content, git_buf_detach(&buf), len); + } else { + git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf)); - /* refresh stats - if this errors, then commit will error too */ - (void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file); + /* refresh stats - if this errors, then commit will error too */ + (void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file); - result = git_filebuf_commit(&file); - git_buf_free(&reader->buffer); + result = git_filebuf_commit(&file); + } done: + git_buf_free(&buf); git_buf_free(&reader->buffer); return result; } diff --git a/src/config_file.h b/src/config_file.h index 0d8bf740f..1c52892c3 100644 --- a/src/config_file.h +++ b/src/config_file.h @@ -55,6 +55,16 @@ GIT_INLINE(int) git_config_file_foreach_match( return git_config_backend_foreach_match(cfg, regexp, fn, data); } +GIT_INLINE(int) git_config_file_lock(git_config_backend *cfg) +{ + return cfg->lock(cfg); +} + +GIT_INLINE(int) git_config_file_unlock(git_config_backend *cfg, int success) +{ + return cfg->unlock(cfg, success); +} + extern int git_config_file_normalize_section(char *start, char *end); #endif diff --git a/tests/config/write.c b/tests/config/write.c index 2e7b8182a..5446b95c3 100644 --- a/tests/config/write.c +++ b/tests/config/write.c @@ -1,6 +1,9 @@ #include "clar_libgit2.h" #include "buffer.h" #include "fileops.h" +#include "git2/sys/config.h" +#include "config_file.h" +#include "config.h" void test_config_write__initialize(void) { @@ -630,3 +633,46 @@ void test_config_write__to_file_with_only_comment(void) git_buf_free(&result); } +void test_config_write__locking(void) +{ + git_config_backend *cfg, *cfg2; + git_config_entry *entry; + const char *filename = "locked-file"; + + /* Open the config and lock it */ + cl_git_mkfile(filename, "[section]\n\tname = value\n"); + cl_git_pass(git_config_file__ondisk(&cfg, filename)); + cl_git_pass(git_config_file_open(cfg, GIT_CONFIG_LEVEL_APP)); + cl_git_pass(git_config_file_get_string(&entry, cfg, "section.name")); + cl_assert_equal_s("value", entry->value); + git_config_entry_free(entry); + cl_git_pass(git_config_file_lock(cfg)); + + /* Change entries in the locked backend */ + cl_git_pass(git_config_file_set_string(cfg, "section.name", "other value")); + cl_git_pass(git_config_file_set_string(cfg, "section2.name3", "more value")); + + /* We can see that the file we read from hasn't changed */ + cl_git_pass(git_config_file__ondisk(&cfg2, filename)); + cl_git_pass(git_config_file_open(cfg2, GIT_CONFIG_LEVEL_APP)); + cl_git_pass(git_config_file_get_string(&entry, cfg2, "section.name")); + cl_assert_equal_s("value", entry->value); + git_config_entry_free(entry); + cl_git_fail_with(GIT_ENOTFOUND, git_config_file_get_string(&entry, cfg2, "section2.name3")); + git_config_file_free(cfg2); + + git_config_file_unlock(cfg, true); + git_config_file_free(cfg); + + /* Now that we've unlocked it, we should see both updates */ + cl_git_pass(git_config_file__ondisk(&cfg, filename)); + cl_git_pass(git_config_file_open(cfg, GIT_CONFIG_LEVEL_APP)); + cl_git_pass(git_config_file_get_string(&entry, cfg, "section.name")); + cl_assert_equal_s("other value", entry->value); + git_config_entry_free(entry); + cl_git_pass(git_config_file_get_string(&entry, cfg, "section2.name3")); + cl_assert_equal_s("more value", entry->value); + git_config_entry_free(entry); + + git_config_file_free(cfg); +} From 36f784b538c4b27f7b52427d2cfce06c535abba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 1 Jun 2015 20:02:23 +0200 Subject: [PATCH 065/450] config: expose locking via the main API This lock/unlock pair allows for the cller to lock a configuration file to avoid concurrent operations. It also allows for a transactional approach to updating a configuration file. If multiple updates must be made atomically, they can be done while the config is locked. --- CHANGELOG.md | 10 ++++++++++ include/git2/config.h | 27 +++++++++++++++++++++++++++ src/config.c | 31 +++++++++++++++++++++++++++++++ tests/config/write.c | 41 ++++++++++++++++++++++------------------- 4 files changed, 90 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 243b696d7..4442d0a3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ v0.23 + 1 ### API additions +* `git_config_lock()` and `git_config_unlock()` have been added, which + allow for transactional/atomic complex updates to the configuration, + removing the opportunity for concurrent operations and not + committing any changes until the unlock. + + ### API removals ### Breaking API changes @@ -19,6 +25,10 @@ v0.23 + 1 with the reflog on ref deletion. The file-based backend must delete it, a database-backed one may wish to archive it. +* `git_config_backend` has gained two entries. `lock` and `unlock` + with which to implement the transactional/atomic semantics for the + configuration backend. + v0.23 ------ diff --git a/include/git2/config.h b/include/git2/config.h index 6d3fdb0c2..2550f8edb 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -689,6 +689,33 @@ GIT_EXTERN(int) git_config_backend_foreach_match( void *payload); +/** + * Lock the backend with the highest priority + * + * Locking disallows anybody else from writing to that backend. Any + * updates made after locking will not be visible to a reader until + * the file is unlocked. + * + * @param cfg the configuration in which to lock + * @return 0 or an error code + */ +GIT_EXTERN(int) git_config_lock(git_config *cfg); + +/** + * Unlock the backend with the highest priority + * + * Unlocking will allow other writers to updat the configuration + * file. Optionally, any changes performed since the lock will be + * applied to the configuration. + * + * @param cfg the configuration + * @param commit boolean which indicates whether to commit any changes + * done since locking + * @return 0 or an error code + */ +GIT_EXTERN(int) git_config_unlock(git_config *cfg, int commit); + + /** @} */ GIT_END_DECL #endif diff --git a/src/config.c b/src/config.c index 77cf573e6..937d00dcb 100644 --- a/src/config.c +++ b/src/config.c @@ -1144,6 +1144,37 @@ int git_config_open_default(git_config **out) return error; } +int git_config_lock(git_config *cfg) +{ + git_config_backend *file; + file_internal *internal; + + internal = git_vector_get(&cfg->files, 0); + if (!internal || !internal->file) { + giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends/files"); + return -1; + } + file = internal->file; + + return file->lock(file); +} + +int git_config_unlock(git_config *cfg, int commit) +{ + git_config_backend *file; + file_internal *internal; + + internal = git_vector_get(&cfg->files, 0); + if (!internal || !internal->file) { + giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends/files"); + return -1; + } + + file = internal->file; + + return file->unlock(file, commit); +} + /*********** * Parsers ***********/ diff --git a/tests/config/write.c b/tests/config/write.c index 5446b95c3..e43c26bd9 100644 --- a/tests/config/write.c +++ b/tests/config/write.c @@ -635,44 +635,47 @@ void test_config_write__to_file_with_only_comment(void) void test_config_write__locking(void) { - git_config_backend *cfg, *cfg2; + git_config *cfg, *cfg2; git_config_entry *entry; const char *filename = "locked-file"; /* Open the config and lock it */ cl_git_mkfile(filename, "[section]\n\tname = value\n"); - cl_git_pass(git_config_file__ondisk(&cfg, filename)); - cl_git_pass(git_config_file_open(cfg, GIT_CONFIG_LEVEL_APP)); - cl_git_pass(git_config_file_get_string(&entry, cfg, "section.name")); + cl_git_pass(git_config_open_ondisk(&cfg, filename)); + cl_git_pass(git_config_get_entry(&entry, cfg, "section.name")); cl_assert_equal_s("value", entry->value); git_config_entry_free(entry); - cl_git_pass(git_config_file_lock(cfg)); + cl_git_pass(git_config_lock(cfg)); /* Change entries in the locked backend */ - cl_git_pass(git_config_file_set_string(cfg, "section.name", "other value")); - cl_git_pass(git_config_file_set_string(cfg, "section2.name3", "more value")); + cl_git_pass(git_config_set_string(cfg, "section.name", "other value")); + cl_git_pass(git_config_set_string(cfg, "section2.name3", "more value")); /* We can see that the file we read from hasn't changed */ - cl_git_pass(git_config_file__ondisk(&cfg2, filename)); - cl_git_pass(git_config_file_open(cfg2, GIT_CONFIG_LEVEL_APP)); - cl_git_pass(git_config_file_get_string(&entry, cfg2, "section.name")); + cl_git_pass(git_config_open_ondisk(&cfg2, filename)); + cl_git_pass(git_config_get_entry(&entry, cfg2, "section.name")); cl_assert_equal_s("value", entry->value); git_config_entry_free(entry); - cl_git_fail_with(GIT_ENOTFOUND, git_config_file_get_string(&entry, cfg2, "section2.name3")); - git_config_file_free(cfg2); + cl_git_fail_with(GIT_ENOTFOUND, git_config_get_entry(&entry, cfg2, "section2.name3")); + git_config_free(cfg2); - git_config_file_unlock(cfg, true); - git_config_file_free(cfg); + /* And we also get the old view when we read from the locked config */ + cl_git_pass(git_config_get_entry(&entry, cfg, "section.name")); + cl_assert_equal_s("value", entry->value); + git_config_entry_free(entry); + cl_git_fail_with(GIT_ENOTFOUND, git_config_get_entry(&entry, cfg, "section2.name3")); + + git_config_unlock(cfg, true); + git_config_free(cfg); /* Now that we've unlocked it, we should see both updates */ - cl_git_pass(git_config_file__ondisk(&cfg, filename)); - cl_git_pass(git_config_file_open(cfg, GIT_CONFIG_LEVEL_APP)); - cl_git_pass(git_config_file_get_string(&entry, cfg, "section.name")); + cl_git_pass(git_config_open_ondisk(&cfg, filename)); + cl_git_pass(git_config_get_entry(&entry, cfg, "section.name")); cl_assert_equal_s("other value", entry->value); git_config_entry_free(entry); - cl_git_pass(git_config_file_get_string(&entry, cfg, "section2.name3")); + cl_git_pass(git_config_get_entry(&entry, cfg, "section2.name3")); cl_assert_equal_s("more value", entry->value); git_config_entry_free(entry); - git_config_file_free(cfg); + git_config_free(cfg); } From 5340d63d3815ddbd1a7e1b5b9628fce10089e8a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 12 Jul 2015 12:50:23 +0200 Subject: [PATCH 066/450] config: perform unlocking via git_transaction This makes the API for commiting or discarding changes the same as for references. --- CHANGELOG.md | 9 ++++----- include/git2/config.h | 23 +++++++---------------- src/config.c | 8 ++++++-- src/config.h | 15 +++++++++++++++ src/transaction.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/transaction.h | 14 ++++++++++++++ tests/config/write.c | 7 ++++--- 7 files changed, 92 insertions(+), 26 deletions(-) create mode 100644 src/transaction.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 4442d0a3f..5dd4b3447 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,10 @@ v0.23 + 1 ### API additions -* `git_config_lock()` and `git_config_unlock()` have been added, which - allow for transactional/atomic complex updates to the configuration, - removing the opportunity for concurrent operations and not - committing any changes until the unlock. - +* `git_config_lock()` has been added, which allow for + transactional/atomic complex updates to the configuration, removing + the opportunity for concurrent operations and not committing any + changes until the unlock. ### API removals diff --git a/include/git2/config.h b/include/git2/config.h index 2550f8edb..05c3ad622 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -696,25 +696,16 @@ GIT_EXTERN(int) git_config_backend_foreach_match( * updates made after locking will not be visible to a reader until * the file is unlocked. * + * You can apply the changes by calling `git_transaction_commit()` + * before freeing the transaction. Either of these actions will unlock + * the config. + * + * @param tx the resulting transaction, use this to commit or undo the + * changes * @param cfg the configuration in which to lock * @return 0 or an error code */ -GIT_EXTERN(int) git_config_lock(git_config *cfg); - -/** - * Unlock the backend with the highest priority - * - * Unlocking will allow other writers to updat the configuration - * file. Optionally, any changes performed since the lock will be - * applied to the configuration. - * - * @param cfg the configuration - * @param commit boolean which indicates whether to commit any changes - * done since locking - * @return 0 or an error code - */ -GIT_EXTERN(int) git_config_unlock(git_config *cfg, int commit); - +GIT_EXTERN(int) git_config_lock(git_transaction **tx, git_config *cfg); /** @} */ GIT_END_DECL diff --git a/src/config.c b/src/config.c index 937d00dcb..2df1fc80e 100644 --- a/src/config.c +++ b/src/config.c @@ -1144,8 +1144,9 @@ int git_config_open_default(git_config **out) return error; } -int git_config_lock(git_config *cfg) +int git_config_lock(git_transaction **out, git_config *cfg) { + int error; git_config_backend *file; file_internal *internal; @@ -1156,7 +1157,10 @@ int git_config_lock(git_config *cfg) } file = internal->file; - return file->lock(file); + if ((error = file->lock(file)) < 0) + return error; + + return git_transaction_config_new(out, cfg); } int git_config_unlock(git_config *cfg, int commit) diff --git a/src/config.h b/src/config.h index f257cc90f..ba745331a 100644 --- a/src/config.h +++ b/src/config.h @@ -88,4 +88,19 @@ extern int git_config__cvar( */ int git_config_lookup_map_enum(git_cvar_t *type_out, const char **str_out, const git_cvar_map *maps, size_t map_n, int enum_val); + +/** + * Unlock the backend with the highest priority + * + * Unlocking will allow other writers to updat the configuration + * file. Optionally, any changes performed since the lock will be + * applied to the configuration. + * + * @param cfg the configuration + * @param commit boolean which indicates whether to commit any changes + * done since locking + * @return 0 or an error code + */ +GIT_EXTERN(int) git_config_unlock(git_config *cfg, int commit); + #endif diff --git a/src/transaction.c b/src/transaction.c index e8331891c..e9639bf97 100644 --- a/src/transaction.c +++ b/src/transaction.c @@ -12,6 +12,7 @@ #include "pool.h" #include "reflog.h" #include "signature.h" +#include "config.h" #include "git2/transaction.h" #include "git2/signature.h" @@ -20,6 +21,12 @@ GIT__USE_STRMAP +typedef enum { + TRANSACTION_NONE, + TRANSACTION_REFS, + TRANSACTION_CONFIG, +} transaction_t; + typedef struct { const char *name; void *payload; @@ -39,13 +46,29 @@ typedef struct { } transaction_node; struct git_transaction { + transaction_t type; git_repository *repo; git_refdb *db; + git_config *cfg; git_strmap *locks; git_pool pool; }; +int git_transaction_config_new(git_transaction **out, git_config *cfg) +{ + git_transaction *tx; + assert(out && cfg); + + tx = git__calloc(1, sizeof(git_transaction)); + GITERR_CHECK_ALLOC(tx); + + tx->type = TRANSACTION_CONFIG; + tx->cfg = cfg; + *out = tx; + return 0; +} + int git_transaction_new(git_transaction **out, git_repository *repo) { int error; @@ -71,6 +94,7 @@ int git_transaction_new(git_transaction **out, git_repository *repo) if ((error = git_repository_refdb(&tx->db, repo)) < 0) goto on_error; + tx->type = TRANSACTION_REFS; memcpy(&tx->pool, &pool, sizeof(git_pool)); tx->repo = repo; *out = tx; @@ -305,6 +329,14 @@ int git_transaction_commit(git_transaction *tx) assert(tx); + if (tx->type == TRANSACTION_CONFIG) { + error = git_config_unlock(tx->cfg, true); + git_config_free(tx->cfg); + tx->cfg = NULL; + + return error; + } + for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) { if (!git_strmap_has_data(tx->locks, pos)) continue; @@ -332,6 +364,16 @@ void git_transaction_free(git_transaction *tx) assert(tx); + if (tx->type == TRANSACTION_CONFIG) { + if (tx->cfg) { + git_config_unlock(tx->cfg, false); + git_config_free(tx->cfg); + } + + git__free(tx); + return; + } + /* start by unlocking the ones we've left hanging, if any */ for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) { if (!git_strmap_has_data(tx->locks, pos)) diff --git a/src/transaction.h b/src/transaction.h new file mode 100644 index 000000000..780c06830 --- /dev/null +++ b/src/transaction.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_transaction_h__ +#define INCLUDE_transaction_h__ + +#include "common.h" + +int git_transaction_config_new(git_transaction **out, git_config *cfg); + +#endif diff --git a/tests/config/write.c b/tests/config/write.c index e43c26bd9..3d9b1a16a 100644 --- a/tests/config/write.c +++ b/tests/config/write.c @@ -637,6 +637,7 @@ void test_config_write__locking(void) { git_config *cfg, *cfg2; git_config_entry *entry; + git_transaction *tx; const char *filename = "locked-file"; /* Open the config and lock it */ @@ -645,7 +646,7 @@ void test_config_write__locking(void) cl_git_pass(git_config_get_entry(&entry, cfg, "section.name")); cl_assert_equal_s("value", entry->value); git_config_entry_free(entry); - cl_git_pass(git_config_lock(cfg)); + cl_git_pass(git_config_lock(&tx, cfg)); /* Change entries in the locked backend */ cl_git_pass(git_config_set_string(cfg, "section.name", "other value")); @@ -665,8 +666,8 @@ void test_config_write__locking(void) git_config_entry_free(entry); cl_git_fail_with(GIT_ENOTFOUND, git_config_get_entry(&entry, cfg, "section2.name3")); - git_config_unlock(cfg, true); - git_config_free(cfg); + cl_git_pass(git_transaction_commit(tx)); + git_transaction_free(tx); /* Now that we've unlocked it, we should see both updates */ cl_git_pass(git_config_open_ondisk(&cfg, filename)); From b0b2c72274efb660e63fcb8f476222bcccb6c680 Mon Sep 17 00:00:00 2001 From: Dan Leehr Date: Thu, 13 Aug 2015 22:52:52 -0400 Subject: [PATCH 067/450] Fix bug in git_smart__push: push_transfer_progress cb is never called The conditional checked cbs->transfer_progress then used the value in cbs->push_transfer_progress. In both cases it should be push_transfer_progress --- src/transports/smart_protocol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 0920f2eef..1d46d4bc9 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -957,7 +957,7 @@ int git_smart__push(git_transport *transport, git_push *push, const git_remote_c packbuilder_payload.pb = push->pb; - if (cbs && cbs->transfer_progress) { + if (cbs && cbs->push_transfer_progress) { packbuilder_payload.cb = cbs->push_transfer_progress; packbuilder_payload.cb_payload = cbs->payload; } From 241414ee33c627092a483bc56d46f5a0d9c16ca9 Mon Sep 17 00:00:00 2001 From: Max Leske Date: Fri, 14 Aug 2015 15:42:59 +0200 Subject: [PATCH 068/450] added a single line of additional error reporting from libssh2 when failing to retrieve the list of authentication methods --- src/transports/ssh.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 8f5a7164b..ffa4a24a7 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -756,8 +756,10 @@ static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *use list = libssh2_userauth_list(session, username, strlen(username)); /* either error, or the remote accepts NONE auth, which is bizarre, let's punt */ - if (list == NULL && !libssh2_userauth_authenticated(session)) + if (list == NULL && !libssh2_userauth_authenticated(session)) { + ssh_error(session, "Failed to retrieve list of SSH authentication methods"); return -1; + } ptr = list; while (ptr) { From ac02a69470f149be5c556eb607d8cb3dcc21771f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 14 Aug 2015 21:06:09 +0200 Subject: [PATCH 069/450] Add a hashmap for index entries They are hashed case-insensitively and take the stage into account. --- src/idxmap.h | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 src/idxmap.h diff --git a/src/idxmap.h b/src/idxmap.h new file mode 100644 index 000000000..74304bb97 --- /dev/null +++ b/src/idxmap.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_idxmap_h__ +#define INCLUDE_idxmap_h__ + +#include +#include "common.h" +#include "git2/index.h" + +#define kmalloc git__malloc +#define kcalloc git__calloc +#define krealloc git__realloc +#define kreallocarray git__reallocarray +#define kfree git__free +#include "khash.h" + +__KHASH_TYPE(idx, const git_index_entry *, git_index_entry *) +__KHASH_TYPE(idxicase, const git_index_entry *, git_index_entry *) + +typedef khash_t(idx) git_idxmap; +typedef khash_t(idxicase) git_idxmap_icase; + +typedef khiter_t git_idxmap_iter; + +/* This is __ac_X31_hash_string but with tolower and it takes the entry's stage into account */ +static kh_inline khint_t idxentry_hash(const git_index_entry *e) +{ + const char *s = e->path; + khint_t h = (khint_t)git__tolower(*s); + if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)git__tolower(*s); + return h + GIT_IDXENTRY_STAGE(e); +} + +#define idxentry_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcmp(a->path, b->path) == 0) +#define idxentry_icase_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcasecmp(a->path, b->path) == 0) + +#define GIT__USE_IDXMAP \ + __KHASH_IMPL(idx, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_equal) + +#define GIT__USE_IDXMAP_ICASE \ + __KHASH_IMPL(idxicase, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_icase_equal) + +#define git_idxmap_alloc(hp) \ + ((*(hp) = kh_init(idx)) == NULL) ? giterr_set_oom(), -1 : 0 + +#define git_idxmap_icase_alloc(hp) \ + ((*(hp) = kh_init(idxicase)) == NULL) ? giterr_set_oom(), -1 : 0 + +#define git_idxmap_insert(h, key, val, rval) do { \ + khiter_t __pos = kh_put(idx, h, key, &rval); \ + if (rval >= 0) { \ + if (rval == 0) kh_key(h, __pos) = key; \ + kh_val(h, __pos) = val; \ + } } while (0) + +#define git_idxmap_icase_insert(h, key, val, rval) do { \ + khiter_t __pos = kh_put(idxicase, h, key, &rval); \ + if (rval >= 0) { \ + if (rval == 0) kh_key(h, __pos) = key; \ + kh_val(h, __pos) = val; \ + } } while (0) + +#define git_idxmap_lookup_index(h, k) kh_get(idx, h, k) +#define git_idxmap_icase_lookup_index(h, k) kh_get(idxicase, h, k) +#define git_idxmap_value_at(h, idx) kh_val(h, idx) +#define git_idxmap_valid_index(h, idx) (idx != kh_end(h)) +#define git_idxmap_has_data(h, idx) kh_exist(h, idx) + +#define git_idxmap_free(h) kh_destroy(idx, h), h = NULL +#define git_idxmap_clear(h) kh_clear(idx, h) + +#define git_idxmap_delete_at(h, id) kh_del(idx, h, id) +#define git_idxmap_icase_delete_at(h, id) kh_del(idxicase, h, id) + +#define git_idxmap_delete(h, key) do { \ + khiter_t __pos = git_idxmap_lookup_index(h, key); \ + if (git_idxmap_valid_index(h, __pos)) \ + git_idxmap_delete_at(h, __pos); } while (0) + +#define git_idxmap_icase_delete(h, key) do { \ + khiter_t __pos = git_idxmap_icase_lookup_index(h, key); \ + if (git_idxmap_valid_index(h, __pos)) \ + git_idxmap_icase_delete_at(h, __pos); } while (0) + +#define git_idxmap_begin kh_begin +#define git_idxmap_end kh_end + +#endif From c232d6c32df90e009dcb8d79309d2c9b9f51b77a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 14 Aug 2015 21:06:51 +0200 Subject: [PATCH 070/450] index: add tests around case switching We were missing tests for switching the case-sensitivity of an index in-memory and then looking up entries in it. --- tests/index/filemodes.c | 1 + tests/index/tests.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/tests/index/filemodes.c b/tests/index/filemodes.c index b3907996b..6442d7755 100644 --- a/tests/index/filemodes.c +++ b/tests/index/filemodes.c @@ -239,6 +239,7 @@ void test_index_filemodes__invalid(void) cl_git_pass(git_repository_index(&index, g_repo)); + GIT_IDXENTRY_STAGE_SET(&entry, 0); entry.path = "foo"; entry.mode = GIT_OBJ_BLOB; cl_git_fail(git_index_add(index, &entry)); diff --git a/tests/index/tests.c b/tests/index/tests.c index e1ff12ad0..f1a057853 100644 --- a/tests/index/tests.c +++ b/tests/index/tests.c @@ -792,10 +792,43 @@ void test_index_tests__reload_while_ignoring_case(void) cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEXCAP_IGNORE_CASE)); cl_git_pass(git_index_read(index, true)); cl_git_pass(git_vector_verify_sorted(&index->entries)); + cl_assert(git_index_get_bypath(index, ".HEADER", 0)); + cl_assert_equal_p(NULL, git_index_get_bypath(index, ".header", 0)); cl_git_pass(git_index_set_caps(index, caps | GIT_INDEXCAP_IGNORE_CASE)); cl_git_pass(git_index_read(index, true)); cl_git_pass(git_vector_verify_sorted(&index->entries)); + cl_assert(git_index_get_bypath(index, ".HEADER", 0)); + cl_assert(git_index_get_bypath(index, ".header", 0)); + + git_index_free(index); +} + +void test_index_tests__change_icase_on_instance(void) +{ + git_index *index; + unsigned int caps; + const git_index_entry *e; + + cl_git_pass(git_index_open(&index, TEST_INDEX_PATH)); + cl_git_pass(git_vector_verify_sorted(&index->entries)); + + caps = git_index_caps(index); + cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEXCAP_IGNORE_CASE)); + cl_assert_equal_i(false, index->ignore_case); + cl_git_pass(git_vector_verify_sorted(&index->entries)); + cl_assert(e = git_index_get_bypath(index, "src/common.h", 0)); + cl_assert_equal_p(NULL, e = git_index_get_bypath(index, "SRC/Common.h", 0)); + cl_assert(e = git_index_get_bypath(index, "COPYING", 0)); + cl_assert_equal_p(NULL, e = git_index_get_bypath(index, "copying", 0)); + + cl_git_pass(git_index_set_caps(index, caps | GIT_INDEXCAP_IGNORE_CASE)); + cl_assert_equal_i(true, index->ignore_case); + cl_git_pass(git_vector_verify_sorted(&index->entries)); + cl_assert(e = git_index_get_bypath(index, "COPYING", 0)); + cl_assert_equal_s("COPYING", e->path); + cl_assert(e = git_index_get_bypath(index, "copying", 0)); + cl_assert_equal_s("COPYING", e->path); git_index_free(index); } From af1d5239a16976bd1b8d0a9358497f043bdfed14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 14 Aug 2015 21:10:12 +0200 Subject: [PATCH 071/450] index: keep a hash table as well as a vector of entries The hash table allows quick lookup of specific paths, while we use the vector for enumeration. --- src/index.c | 111 ++++++++++++++++++++++++++++++++++++++++++++-------- src/index.h | 2 + 2 files changed, 96 insertions(+), 17 deletions(-) diff --git a/src/index.c b/src/index.c index e424698bb..e904ffcfe 100644 --- a/src/index.c +++ b/src/index.c @@ -17,6 +17,7 @@ #include "pathspec.h" #include "ignore.h" #include "blob.h" +#include "idxmap.h" #include "git2/odb.h" #include "git2/oid.h" @@ -24,6 +25,9 @@ #include "git2/config.h" #include "git2/sys/index.h" +GIT__USE_IDXMAP +GIT__USE_IDXMAP_ICASE + static int index_apply_to_wd_diff(git_index *index, int action, const git_strarray *paths, unsigned int flags, git_index_matched_path_cb cb, void *payload); @@ -425,6 +429,7 @@ int git_index_open(git_index **index_out, const char *index_path) } if (git_vector_init(&index->entries, 32, git_index_entry_cmp) < 0 || + git_idxmap_alloc(&index->entries_map) < 0 || git_vector_init(&index->names, 8, conflict_name_cmp) < 0 || git_vector_init(&index->reuc, 8, reuc_cmp) < 0 || git_vector_init(&index->deleted, 8, git_index_entry_cmp) < 0) @@ -462,6 +467,7 @@ static void index_free(git_index *index) assert(!git_atomic_get(&index->readers)); git_index_clear(index); + git_idxmap_free(index->entries_map); git_vector_free(&index->entries); git_vector_free(&index->names); git_vector_free(&index->reuc); @@ -508,6 +514,11 @@ static int index_remove_entry(git_index *index, size_t pos) if (entry != NULL) git_tree_cache_invalidate_path(index->tree, entry->path); + if (index->ignore_case) + git_idxmap_icase_delete((khash_t(idxicase) *) index->entries_map, entry); + else + git_idxmap_delete(index->entries_map, entry); + error = git_vector_remove(&index->entries, pos); if (!error) { @@ -535,6 +546,7 @@ int git_index_clear(git_index *index) return -1; } + git_idxmap_clear(index->entries_map); while (!error && index->entries.length > 0) error = index_remove_entry(index, index->entries.length - 1); index_free_deleted(index); @@ -804,16 +816,24 @@ const git_index_entry *git_index_get_byindex( const git_index_entry *git_index_get_bypath( git_index *index, const char *path, int stage) { - size_t pos; + khiter_t pos; + git_index_entry key = {{ 0 }}; assert(index); - if (index_find(&pos, index, path, 0, stage, true) < 0) { - giterr_set(GITERR_INDEX, "Index does not contain %s", path); - return NULL; - } + key.path = path; + GIT_IDXENTRY_STAGE_SET(&key, stage); - return git_index_get_byindex(index, pos); + if (index->ignore_case) + pos = git_idxmap_icase_lookup_index((khash_t(idxicase) *) index->entries_map, &key); + else + pos = git_idxmap_lookup_index(index->entries_map, &key); + + if (git_idxmap_valid_index(index->entries_map, pos)) + return git_idxmap_value_at(index->entries_map, pos); + + giterr_set(GITERR_INDEX, "Index does not contain %s", path); + return NULL; } void git_index_entry__init_from_stat( @@ -1139,6 +1159,13 @@ static int index_insert( * check for dups, this is actually cheaper in the long run.) */ error = git_vector_insert_sorted(&index->entries, entry, index_no_dups); + + if (error == 0) { + if (index->ignore_case) + git_idxmap_icase_insert((khash_t(idxicase) *) index->entries_map, entry, entry, error); else + git_idxmap_insert(index->entries_map, entry, entry, error); + + } } if (error < 0) { @@ -1364,12 +1391,20 @@ int git_index_remove(git_index *index, const char *path, int stage) { int error; size_t position; + git_index_entry remove_key = {{ 0 }}; if (git_mutex_lock(&index->lock) < 0) { giterr_set(GITERR_OS, "Failed to lock index"); return -1; } + remove_key.path = path; + GIT_IDXENTRY_STAGE_SET(&remove_key, stage); + if (index->ignore_case) + git_idxmap_icase_delete((khash_t(idxicase) *) index->entries_map, &remove_key); + else + git_idxmap_delete(index->entries_map, &remove_key); + if (index_find(&position, index, path, 0, stage, false) < 0) { giterr_set( GITERR_INDEX, "Index does not contain %s at stage %d", path, stage); @@ -2180,6 +2215,11 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) assert(!index->entries.length); + if (index->ignore_case) + kh_resize(idxicase, (khash_t(idxicase) *) index->entries_map, header.entry_count); + else + kh_resize(idx, index->entries_map, header.entry_count); + /* Parse all the entries */ for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) { git_index_entry *entry; @@ -2196,6 +2236,16 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) goto done; } + if (index->ignore_case) + git_idxmap_icase_insert((khash_t(idxicase) *) index->entries_map, entry, entry, error); + else + git_idxmap_insert(index->entries_map, entry, entry, error); + + if (error < 0) { + index_entry_free(entry); + goto done; + } + seek_forward(entry_size); } @@ -2610,7 +2660,13 @@ int git_index_read_tree(git_index *index, const git_tree *tree) { int error = 0; git_vector entries = GIT_VECTOR_INIT; + git_idxmap *entries_map; read_tree_data data; + size_t i; + git_index_entry *e; + + if (git_idxmap_alloc(&entries_map) < 0) + return -1; git_vector_set_cmp(&entries, index->entries._cmp); /* match sort */ @@ -2625,23 +2681,44 @@ int git_index_read_tree(git_index *index, const git_tree *tree) if (index_sort_if_needed(index, true) < 0) return -1; - error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data); + if ((error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data)) < 0) + goto cleanup; - if (!error) { - git_vector_sort(&entries); + if (index->ignore_case) + kh_resize(idxicase, (khash_t(idxicase) *) entries_map, entries.length); + else + kh_resize(idx, entries_map, entries.length); - if ((error = git_index_clear(index)) < 0) - /* well, this isn't good */; - else if (git_mutex_lock(&index->lock) < 0) { - giterr_set(GITERR_OS, "Unable to acquire index lock"); - error = -1; - } else { - git_vector_swap(&entries, &index->entries); - git_mutex_unlock(&index->lock); + git_vector_foreach(&entries, i, e) { + if (index->ignore_case) + git_idxmap_icase_insert((git_idxmap_icase *) entries_map, e, e, error); + else + git_idxmap_insert(entries_map, e, e, error); + + if (error < 0) { + giterr_set(GITERR_INDEX, "failed to insert entry into map"); + return error; } } + error = 0; + + git_vector_sort(&entries); + + if ((error = git_index_clear(index)) < 0) + /* well, this isn't good */; + else if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Unable to acquire index lock"); + error = -1; + } else { + git_vector_swap(&entries, &index->entries); + entries_map = git__swap(index->entries_map, entries_map); + git_mutex_unlock(&index->lock); + } + +cleanup: git_vector_free(&entries); + git_idxmap_free(entries_map); if (error < 0) return error; diff --git a/src/index.h b/src/index.h index 9c60b015c..546e677be 100644 --- a/src/index.h +++ b/src/index.h @@ -10,6 +10,7 @@ #include "fileops.h" #include "filebuf.h" #include "vector.h" +#include "idxmap.h" #include "tree-cache.h" #include "git2/odb.h" #include "git2/index.h" @@ -25,6 +26,7 @@ struct git_index { git_oid checksum; /* checksum at the end of the file */ git_vector entries; + git_idxmap *entries_map; git_mutex lock; /* lock held while entries is being changed */ git_vector deleted; /* deleted entries if readers > 0 */ From 11bca2d265d7a1136ddf3e34a39263eba6320e20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 15 Aug 2015 18:15:23 +0200 Subject: [PATCH 072/450] http: propagate the credentials callback's error code When we ask for credentials, the user may choose to return EUSER to indicate that an error has happened on its end and it wants to be given back control. We must therefore pass that back to the user instead of mentioning that it was on_headers_complete() that returned an error code. Since we can, we return the exact error code from the user (other than PASSTHROUGH) since it doesn't cost anything, though using other error codes aren't recommended. --- src/transports/http.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/transports/http.c b/src/transports/http.c index e3d90de11..87f3ee816 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -36,6 +36,8 @@ static const char *post_verb = "POST"; #define PARSE_ERROR_GENERIC -1 #define PARSE_ERROR_REPLAY -2 +/** Look at the user field */ +#define PARSE_ERROR_EXT -3 #define CHUNK_SIZE 4096 @@ -78,6 +80,7 @@ typedef struct { git_vector www_authenticate; enum last_cb last_cb; int parse_error; + int error; unsigned parse_finished : 1; /* Authentication */ @@ -351,7 +354,8 @@ static int on_headers_complete(http_parser *parser) if (error == GIT_PASSTHROUGH) { no_callback = 1; } else if (error < 0) { - return PARSE_ERROR_GENERIC; + t->error = error; + return t->parse_error = PARSE_ERROR_EXT; } else { assert(t->cred); @@ -712,6 +716,10 @@ replay: goto replay; } + if (t->parse_error == PARSE_ERROR_EXT) { + return t->error; + } + if (t->parse_error < 0) return -1; From e451cd5c0350aa70e05df44bbf5cbdc0e6504f46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 15 Aug 2015 18:46:38 +0200 Subject: [PATCH 073/450] diff: don't error out on an invalid regex When parsing user-provided regex patterns for functions, we must not fail to provide a diff just because a pattern is not well formed. Ignore it instead. --- src/diff_driver.c | 13 ++++++------- tests/diff/drivers.c | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/diff_driver.c b/src/diff_driver.c index 9d1337103..bc3518991 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -97,8 +97,7 @@ static int diff_driver_add_patterns( for (scan = regex_str; scan; scan = end) { /* get pattern to fill in */ if ((pat = git_array_alloc(drv->fn_patterns)) == NULL) { - error = -1; - break; + return -1; } pat->flags = regex_flags; @@ -117,10 +116,9 @@ static int diff_driver_add_patterns( break; if ((error = regcomp(&pat->re, buf.ptr, regex_flags)) != 0) { - /* if regex fails to compile, warn? fail? */ - error = giterr_set_regex(&pat->re, error); - regfree(&pat->re); - break; + /* + * TODO: issue a warning + */ } } @@ -128,7 +126,8 @@ static int diff_driver_add_patterns( (void)git_array_pop(drv->fn_patterns); /* release last item */ git_buf_free(&buf); - return error; + /* We want to ignore bad patterns, so return success regardless */ + return 0; } static int diff_driver_xfuncname(const git_config_entry *entry, void *payload) diff --git a/tests/diff/drivers.c b/tests/diff/drivers.c index e3a0014db..42af38a9a 100644 --- a/tests/diff/drivers.c +++ b/tests/diff/drivers.c @@ -250,3 +250,29 @@ void test_diff_drivers__builtins(void) git_buf_free(&expected); git_vector_free(&files); } + +void test_diff_drivers__invalid_pattern(void) +{ + git_config *cfg; + git_index *idx; + git_diff *diff; + git_patch *patch; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + + g_repo = cl_git_sandbox_init("userdiff"); + cl_git_mkfile("userdiff/.gitattributes", "*.storyboard diff=storyboard\n"); + + cl_git_pass(git_repository_config__weakptr(&cfg, g_repo)); + cl_git_pass(git_config_set_string(cfg, "diff.storyboard.xfuncname", "")); + + cl_git_mkfile("userdiff/dummy.storyboard", ""); + cl_git_pass(git_repository_index__weakptr(&idx, g_repo)); + cl_git_pass(git_index_add_bypath(idx, "dummy.storyboard")); + cl_git_mkfile("userdiff/dummy.storyboard", "some content\n"); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + cl_git_pass(git_patch_from_diff(&patch, diff, 0)); + + git_patch_free(patch); + git_diff_free(diff); +} From 2d1d2bb59f88956b894a6cdb002bc52a2528e4a8 Mon Sep 17 00:00:00 2001 From: Anders Borum Date: Wed, 5 Aug 2015 18:50:25 +0200 Subject: [PATCH 074/450] Include the 4 characters not recognised as hex-number when setting error in parse_len --- src/transports/smart_pkt.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c index 9ccbd8085..a6ae55d48 100644 --- a/src/transports/smart_pkt.c +++ b/src/transports/smart_pkt.c @@ -351,7 +351,7 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len) static int32_t parse_len(const char *line) { char num[PKT_LEN_SIZE + 1]; - int i, error; + int i, k, error; int32_t len; const char *num_end; @@ -360,7 +360,14 @@ static int32_t parse_len(const char *line) for (i = 0; i < PKT_LEN_SIZE; ++i) { if (!isxdigit(num[i])) { - giterr_set(GITERR_NET, "Found invalid hex digit in length"); + /* Make sure there are no special characters before passing to error message */ + for (k = 0; k < PKT_LEN_SIZE; ++k) { + if(!isprint(num[k])) { + num[k] = '.'; + } + } + + giterr_set(GITERR_NET, "invalid hex digit in length: '%s'", num); return -1; } } From 26ea28f32bb93d262f39d04af72dc19a93593bc5 Mon Sep 17 00:00:00 2001 From: Leo Yang Date: Mon, 17 Aug 2015 15:18:47 -0400 Subject: [PATCH 075/450] =?UTF-8?q?Fix=20build=20warning:=20implicit=20dec?= =?UTF-8?q?laration=20of=20function=20=E2=80=98git=5Ftransaction=5Fconfig?= =?UTF-8?q?=5Fnew=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config.c b/src/config.c index 2df1fc80e..f0b2c3a61 100644 --- a/src/config.c +++ b/src/config.c @@ -13,6 +13,7 @@ #include "vector.h" #include "buf_text.h" #include "config_file.h" +#include "transaction.h" #if GIT_WIN32 # include #endif From 47ed7e5acd5fe9fd8dfc1a6b9aac7603a50da25c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 18 Aug 2015 20:55:59 +0200 Subject: [PATCH 076/450] transport: provide a way to get the callbacks libgit2 implementations of smart subtransports can simply reach through the structure, but external implementors cannot. Add these two functions as a way for the smart subtransports to get the callbacks as set by the user. --- include/git2/sys/transport.h | 22 ++++++++++++++++++++++ src/transports/smart.c | 14 ++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h index 867fbcbce..4a75b0832 100644 --- a/include/git2/sys/transport.h +++ b/include/git2/sys/transport.h @@ -211,6 +211,28 @@ GIT_EXTERN(int) git_transport_smart( git_remote *owner, /* (git_smart_subtransport_definition *) */ void *payload); +/** + * Call the certificate check for this transport. + * + * @param transport a smart transport + * @param cert the certificate to pass to the caller + * @param valid whether we believe the certificate is valid + * @param hostname the hostname we connected to + * @return the return value of the callback + */ +GIT_EXTERN(int) git_transport_smart_certificate_check(git_transport *transport, git_cert *cert, int valid, const char *hostname); + +/** + * Call the credentials callback for this transport + * + * @param out the pointer where the creds are to be stored + * @param transport a smart transport + * @param user the user we saw on the url (if any) + * @param methods available methods for authentication + * @return the return value of the callback + */ +GIT_EXTERN(int) git_transport_smart_credentials(git_cred **out, git_transport *transport, const char *user, int methods); + /* *** End of base transport interface *** *** Begin interface for subtransports for the smart transport *** diff --git a/src/transports/smart.c b/src/transports/smart.c index 85a49e543..31a2dec7b 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -372,6 +372,20 @@ static int ref_name_cmp(const void *a, const void *b) return strcmp(ref_a->head.name, ref_b->head.name); } +int git_transport_smart_certificate_check(git_transport *transport, git_cert *cert, int valid, const char *hostname) +{ + transport_smart *t = (transport_smart *)transport; + + return t->certificate_check_cb(cert, valid, hostname, t->message_cb_payload); +} + +int git_transport_smart_credentials(git_cred **out, git_transport *transport, const char *user, int methods) +{ + transport_smart *t = (transport_smart *)transport; + + return t->cred_acquire_cb(out, t->url, user, methods, t->cred_acquire_payload); +} + int git_transport_smart(git_transport **out, git_remote *owner, void *param) { transport_smart *t; From 57af0b928ec8dd12c443c9479973ad3e39f258b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 19 Aug 2015 00:46:28 +0200 Subject: [PATCH 077/450] cred: add a free function wrapper --- include/git2/transport.h | 11 +++++++++++ src/transports/cred.c | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/include/git2/transport.h b/include/git2/transport.h index fd55eac0a..0ec241699 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -307,6 +307,17 @@ GIT_EXTERN(int) git_cred_ssh_key_memory_new( const char *privatekey, const char *passphrase); + +/** + * Free a credential. + * + * This is only necessary if you own the object; that is, if you are a + * transport. + * + * @param cred the object to free + */ +GIT_EXTERN(void) git_cred_free(git_cred *cred); + /** * Signature of a function which acquires a credential object. * diff --git a/src/transports/cred.c b/src/transports/cred.c index 044b2a262..49ede48bf 100644 --- a/src/transports/cred.c +++ b/src/transports/cred.c @@ -378,3 +378,11 @@ int git_cred_username_new(git_cred **cred, const char *username) *cred = (git_cred *) c; return 0; } + +void git_cred_free(git_cred *cred) +{ + if (!cred) + return; + + cred->free(cred); +} From b445940e2d891a72a7d623bc7efa7649e19863e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 19 Aug 2015 12:53:31 +0200 Subject: [PATCH 078/450] CMake: fall back to OpenSSL on older OS X Starting at OS X 10.8, the Security framework offers some functions which are unified across OS X and iOS. These are the functions that we use. Older versions of OS X do not have these functions and we fail to compile. In these situations, fall back to using OpenSSL for our TLS stream instead. --- CMakeLists.txt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b6a8b6d4e..6c03c718c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,8 +152,18 @@ STRING(REGEX REPLACE "^.*LIBGIT2_SOVERSION ([0-9]+)$" "\\1" LIBGIT2_SOVERSION "$ INCLUDE_DIRECTORIES(src include) IF (SECURITY_FOUND) - MESSAGE("-- Found Security ${SECURITY_DIRS}") - LIST(APPEND LIBGIT2_PC_LIBS "-framework Security") + # OS X 10.7 and older do not have some functions we use, fall back to OpenSSL there + CHECK_LIBRARY_EXISTS("${SECURITY_DIRS}" SSLCreateContext "Security/SecureTransport.h" HAVE_NEWER_SECURITY) + IF (HAVE_NEWER_SECURITY) + MESSAGE("-- Found Security ${SECURITY_DIRS}") + LIST(APPEND LIBGIT2_PC_LIBS "-framework Security") + ELSE() + MESSAGE("-- Security framework is too old, falling back to OpenSSL") + SET(SECURITY_FOUND "NO") + SET(SECURITY_DIRS "") + SET(SECURITY_DIR "") + SET(USE_OPENSSL "ON") + ENDIF() ENDIF() IF (COREFOUNDATION_FOUND) From 6d0defe31c67a61f2f38894a2e4db7818a952d60 Mon Sep 17 00:00:00 2001 From: Vsevolod Parfenov Date: Mon, 24 Aug 2015 18:47:48 +0300 Subject: [PATCH 079/450] Fix 'If we're dealing with a directory' check --- src/ignore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ignore.c b/src/ignore.c index 0031e4696..1f33687bc 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -97,7 +97,7 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match if (rule->containing_dir) { git_buf_puts(&buf, rule->containing_dir); } - if (!strchr(rule->pattern, '*')) + if (rule->flags & GIT_ATTR_FNMATCH_LEADINGDIR && !(rule->flags & GIT_ATTR_FNMATCH_NEGATIVE)) error = git_buf_printf(&buf, "%s/*", rule->pattern); else error = git_buf_puts(&buf, rule->pattern); From afe0ff1a5df1a4a8e71751aa9d33fbdfa60fed9c Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 25 Aug 2015 11:20:37 -0400 Subject: [PATCH 080/450] COPYING: include winhttp definition copyright Include the copyright notice from the deps/winhttp/ sources. Move the LGPL to the bottom of the file (since multiple dependencies are LGPL licensed) and include the actual copyright notices from the regex sources. --- COPYING | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/COPYING b/COPYING index 6f6115e3b..1b88b9b8e 100644 --- a/COPYING +++ b/COPYING @@ -407,6 +407,52 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ---------------------------------------------------------------------- The regex library (deps/regex/) is licensed under the GNU LGPL +(available at the end of this file). + +Definitions for data structures and routines for the regular +expression library. + +Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006,2008 +Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with the GNU C Library; if not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +---------------------------------------------------------------------- + +The bundled winhttp definition files (deps/winhttp/) are licensed under +the GNU LGPL (available at the end of this file). + +Copyright (C) 2007 Francois Gouget + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +---------------------------------------------------------------------- GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 From fec4a68cf9de222594583cf002fd1aea138ad50c Mon Sep 17 00:00:00 2001 From: Arthur Schreiber Date: Wed, 26 Aug 2015 23:08:03 +0200 Subject: [PATCH 081/450] Fix a typo [ci skip] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dd4b3447..8f28cbf3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ v0.23 + 1 * `git_cert` descendent types now have a proper `parent` member -* It is the responsibility fo the refdb backend to decide what to do +* It is the responsibility of the refdb backend to decide what to do with the reflog on ref deletion. The file-based backend must delete it, a database-backed one may wish to archive it. From ed1c64464a4e3126eef5d74d2c14c19133fa9cd8 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 28 Jul 2015 11:41:27 -0500 Subject: [PATCH 082/450] iterator: use an options struct instead of args --- src/checkout.c | 26 ++- src/diff.c | 44 +++-- src/index.c | 9 +- src/iterator.c | 45 ++--- src/iterator.h | 37 ++-- src/merge.c | 35 ++-- src/notes.c | 2 +- src/pathspec.c | 24 +-- src/refdb_fs.c | 6 +- src/stash.c | 29 +-- src/submodule.c | 4 +- tests/diff/iterator.c | 60 ++++-- tests/merge/trees/treediff.c | 12 +- tests/repo/iterator.c | 354 ++++++++++++++++++++++------------- tests/submodule/status.c | 6 +- tests/threads/iterator.c | 5 +- 16 files changed, 423 insertions(+), 275 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index 4b3acbcce..311040d59 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -2471,11 +2471,12 @@ int git_checkout_iterator( { int error = 0; git_iterator *baseline = NULL, *workdir = NULL; + git_iterator_options baseline_opts = GIT_ITERATOR_OPTIONS_INIT, + workdir_opts = GIT_ITERATOR_OPTIONS_INIT; checkout_data data = {0}; git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT; uint32_t *actions = NULL; size_t *counts = NULL; - git_iterator_flag_t iterflags = 0; /* initialize structures and options */ error = checkout_data_init(&data, target, opts); @@ -2499,25 +2500,30 @@ int git_checkout_iterator( /* set up iterators */ - iterflags = git_iterator_ignore_case(target) ? + workdir_opts.flags = git_iterator_ignore_case(target) ? GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE; + workdir_opts.flags |= GIT_ITERATOR_DONT_AUTOEXPAND; + workdir_opts.start = data.pfx; + workdir_opts.end = data.pfx; if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 || (error = git_iterator_for_workdir_ext( &workdir, data.repo, data.opts.target_directory, index, NULL, - iterflags | GIT_ITERATOR_DONT_AUTOEXPAND, - data.pfx, data.pfx)) < 0) + &workdir_opts)) < 0) goto cleanup; + baseline_opts.flags = git_iterator_ignore_case(target) ? + GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE; + baseline_opts.start = data.pfx; + baseline_opts.end = data.pfx; + if (data.opts.baseline_index) { if ((error = git_iterator_for_index( - &baseline, data.opts.baseline_index, - iterflags, data.pfx, data.pfx)) < 0) + &baseline, data.opts.baseline_index, &baseline_opts)) < 0) goto cleanup; } else { if ((error = git_iterator_for_tree( - &baseline, data.opts.baseline, - iterflags, data.pfx, data.pfx)) < 0) + &baseline, data.opts.baseline, &baseline_opts)) < 0) goto cleanup; } @@ -2625,7 +2631,7 @@ int git_checkout_index( return error; GIT_REFCOUNT_INC(index); - if (!(error = git_iterator_for_index(&index_i, index, 0, NULL, NULL))) + if (!(error = git_iterator_for_index(&index_i, index, NULL))) error = git_checkout_iterator(index_i, index, opts); if (owned) @@ -2681,7 +2687,7 @@ int git_checkout_tree( if ((error = git_repository_index(&index, repo)) < 0) return error; - if (!(error = git_iterator_for_tree(&tree_i, tree, 0, NULL, NULL))) + if (!(error = git_iterator_for_tree(&tree_i, tree, NULL))) error = git_checkout_iterator(tree_i, index, opts); git_iterator_free(tree_i); diff --git a/src/diff.c b/src/diff.c index 44f627880..58004db21 100644 --- a/src/diff.c +++ b/src/diff.c @@ -1264,9 +1264,17 @@ cleanup: return error; } -#define DIFF_FROM_ITERATORS(MAKE_FIRST, MAKE_SECOND) do { \ +#define DIFF_FROM_ITERATORS(MAKE_FIRST, FLAGS_FIRST, MAKE_SECOND, FLAGS_SECOND) do { \ git_iterator *a = NULL, *b = NULL; \ char *pfx = opts ? git_pathspec_prefix(&opts->pathspec) : NULL; \ + git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT, \ + b_opts = GIT_ITERATOR_OPTIONS_INIT; \ + a_opts.flags = FLAGS_FIRST; \ + a_opts.start = pfx; \ + a_opts.end = pfx; \ + b_opts.flags = FLAGS_SECOND; \ + b_opts.start = pfx; \ + b_opts.end = pfx; \ GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); \ if (!(error = MAKE_FIRST) && !(error = MAKE_SECOND)) \ error = git_diff__from_iterators(diff, repo, a, b, opts); \ @@ -1280,8 +1288,8 @@ int git_diff_tree_to_tree( git_tree *new_tree, const git_diff_options *opts) { - int error = 0; git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE; + int error = 0; assert(diff && repo); @@ -1293,8 +1301,8 @@ int git_diff_tree_to_tree( iflag = GIT_ITERATOR_IGNORE_CASE; DIFF_FROM_ITERATORS( - git_iterator_for_tree(&a, old_tree, iflag, pfx, pfx), - git_iterator_for_tree(&b, new_tree, iflag, pfx, pfx) + git_iterator_for_tree(&a, old_tree, &a_opts), iflag, + git_iterator_for_tree(&b, new_tree, &b_opts), iflag ); return error; @@ -1318,10 +1326,10 @@ int git_diff_tree_to_index( git_index *index, const git_diff_options *opts) { - int error = 0; - bool index_ignore_case = false; git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_CONFLICTS; + bool index_ignore_case = false; + int error = 0; assert(diff && repo); @@ -1331,8 +1339,8 @@ int git_diff_tree_to_index( index_ignore_case = index->ignore_case; DIFF_FROM_ITERATORS( - git_iterator_for_tree(&a, old_tree, iflag, pfx, pfx), - git_iterator_for_index(&b, index, iflag, pfx, pfx) + git_iterator_for_tree(&a, old_tree, &a_opts), iflag, + git_iterator_for_index(&b, index, &b_opts), iflag ); /* if index is in case-insensitive order, re-sort deltas to match */ @@ -1356,10 +1364,11 @@ int git_diff_index_to_workdir( return error; DIFF_FROM_ITERATORS( - git_iterator_for_index( - &a, index, GIT_ITERATOR_INCLUDE_CONFLICTS, pfx, pfx), - git_iterator_for_workdir( - &b, repo, index, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx) + git_iterator_for_index(&a, index, &a_opts), + GIT_ITERATOR_INCLUDE_CONFLICTS, + + git_iterator_for_workdir(&b, repo, index, NULL, &b_opts), + GIT_ITERATOR_DONT_AUTOEXPAND ); if (!error && DIFF_FLAG_IS_SET(*diff, GIT_DIFF_UPDATE_INDEX) && (*diff)->index_updated) @@ -1383,9 +1392,8 @@ int git_diff_tree_to_workdir( return error; DIFF_FROM_ITERATORS( - git_iterator_for_tree(&a, old_tree, 0, pfx, pfx), - git_iterator_for_workdir( - &b, repo, index, old_tree, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx) + git_iterator_for_tree(&a, old_tree, &a_opts), 0, + git_iterator_for_workdir(&b, repo, index, old_tree, &b_opts), GIT_ITERATOR_DONT_AUTOEXPAND ); return error; @@ -1433,10 +1441,8 @@ int git_diff_index_to_index( assert(diff && old_index && new_index); DIFF_FROM_ITERATORS( - git_iterator_for_index( - &a, old_index, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx), - git_iterator_for_index( - &b, new_index, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx) + git_iterator_for_index(&a, old_index, &a_opts), GIT_ITERATOR_DONT_IGNORE_CASE, + git_iterator_for_index(&b, new_index, &b_opts), GIT_ITERATOR_DONT_IGNORE_CASE ); /* if index is in case-insensitive order, re-sort deltas to match */ diff --git a/src/index.c b/src/index.c index e424698bb..a6a62e327 100644 --- a/src/index.c +++ b/src/index.c @@ -2658,6 +2658,7 @@ int git_index_read_index( remove_entries = GIT_VECTOR_INIT; git_iterator *index_iterator = NULL; git_iterator *new_iterator = NULL; + git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT; const git_index_entry *old_entry, *new_entry; git_index_entry *entry; size_t i; @@ -2667,10 +2668,10 @@ int git_index_read_index( (error = git_vector_init(&remove_entries, index->entries.length, NULL)) < 0) goto done; - if ((error = git_iterator_for_index(&index_iterator, - index, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || - (error = git_iterator_for_index(&new_iterator, - (git_index *)new_index, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0) + opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + + if ((error = git_iterator_for_index(&index_iterator, index, &opts)) < 0 || + (error = git_iterator_for_index(&new_iterator, (git_index *)new_index, &opts)) < 0) goto done; if (((error = git_iterator_current(&old_entry, index_iterator)) < 0 && diff --git a/src/iterator.c b/src/iterator.c index 900ffdcaa..374caf96d 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -31,12 +31,15 @@ (P)->base.cb = &(P)->cb; \ ITERATOR_SET_CB(P,NAME_LC); \ (P)->base.repo = (REPO); \ - (P)->base.start = start ? git__strdup(start) : NULL; \ - (P)->base.end = end ? git__strdup(end) : NULL; \ - if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \ + (P)->base.start = options && options->start ? \ + git__strdup(options->start) : NULL; \ + (P)->base.end = options && options->end ? \ + git__strdup(options->end) : NULL; \ + if ((options && options->start && !(P)->base.start) || \ + (options && options->end && !(P)->base.end)) { \ git__free(P); return -1; } \ (P)->base.prefixcomp = git__prefixcmp; \ - (P)->base.flags = flags & ~ITERATOR_CASE_FLAGS; \ + (P)->base.flags = options ? options->flags & ~ITERATOR_CASE_FLAGS : 0; \ if ((P)->base.flags & GIT_ITERATOR_DONT_AUTOEXPAND) \ (P)->base.flags |= GIT_ITERATOR_INCLUDE_TREES; \ } while (0) @@ -149,9 +152,7 @@ typedef struct { int git_iterator_for_nothing( git_iterator **iter, - git_iterator_flag_t flags, - const char *start, - const char *end) + git_iterator_options *options) { empty_iterator *i = git__calloc(1, sizeof(empty_iterator)); GITERR_CHECK_ALLOC(i); @@ -162,7 +163,7 @@ int git_iterator_for_nothing( ITERATOR_BASE_INIT(i, empty, EMPTY, NULL); - if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0) + if (options && (options->flags & GIT_ITERATOR_IGNORE_CASE) != 0) i->base.flags |= GIT_ITERATOR_IGNORE_CASE; *iter = (git_iterator *)i; @@ -607,15 +608,13 @@ static int tree_iterator__create_root_frame(tree_iterator *ti, git_tree *tree) int git_iterator_for_tree( git_iterator **iter, git_tree *tree, - git_iterator_flag_t flags, - const char *start, - const char *end) + git_iterator_options *options) { int error; tree_iterator *ti; if (tree == NULL) - return git_iterator_for_nothing(iter, flags, start, end); + return git_iterator_for_nothing(iter, options); if ((error = git_object_dup((git_object **)&tree, (git_object *)tree)) < 0) return error; @@ -625,7 +624,7 @@ int git_iterator_for_tree( ITERATOR_BASE_INIT(ti, tree, TREE, git_tree_owner(tree)); - if ((error = iterator__update_ignore_case((git_iterator *)ti, flags)) < 0) + if ((error = iterator__update_ignore_case((git_iterator *)ti, options ? options->flags : 0)) < 0) goto fail; ti->strncomp = iterator__ignore_case(ti) ? git__strncasecmp : git__strncmp; @@ -860,9 +859,7 @@ static void index_iterator__free(git_iterator *self) int git_iterator_for_index( git_iterator **iter, git_index *index, - git_iterator_flag_t flags, - const char *start, - const char *end) + git_iterator_options *options) { int error = 0; index_iterator *ii = git__calloc(1, sizeof(index_iterator)); @@ -876,7 +873,7 @@ int git_iterator_for_index( ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index)); - if ((error = iterator__update_ignore_case((git_iterator *)ii, flags)) < 0) { + if ((error = iterator__update_ignore_case((git_iterator *)ii, options ? options->flags : 0)) < 0) { git_iterator_free((git_iterator *)ii); return error; } @@ -1062,6 +1059,8 @@ static int dirload_with_stat( memcpy(ps->path, path, path_len); + /* TODO: don't stat if assume unchanged for this path */ + if ((error = git_path_diriter_stat(&ps->st, &diriter)) < 0) { if (error == GIT_ENOTFOUND) { /* file was removed between readdir and lstat */ @@ -1366,16 +1365,14 @@ static int fs_iterator__initialize( int git_iterator_for_filesystem( git_iterator **out, const char *root, - git_iterator_flag_t flags, - const char *start, - const char *end) + git_iterator_options *options) { fs_iterator *fi = git__calloc(1, sizeof(fs_iterator)); GITERR_CHECK_ALLOC(fi); ITERATOR_BASE_INIT(fi, fs, FS, NULL); - if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0) + if (options && (options->flags & GIT_ITERATOR_IGNORE_CASE) != 0) fi->base.flags |= GIT_ITERATOR_IGNORE_CASE; return fs_iterator__initialize(out, fi, root); @@ -1559,9 +1556,7 @@ int git_iterator_for_workdir_ext( const char *repo_workdir, git_index *index, git_tree *tree, - git_iterator_flag_t flags, - const char *start, - const char *end) + git_iterator_options *options) { int error, precompose = 0; workdir_iterator *wi; @@ -1583,7 +1578,7 @@ int git_iterator_for_workdir_ext( wi->fi.leave_dir_cb = workdir_iterator__leave_dir; wi->fi.update_entry_cb = workdir_iterator__update_entry; - if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0 || + if ((error = iterator__update_ignore_case((git_iterator *)wi, options ? options->flags : 0)) < 0 || (error = git_ignore__for_path(repo, ".gitignore", &wi->ignores)) < 0) { git_iterator_free((git_iterator *)wi); diff --git a/src/iterator.h b/src/iterator.h index 893e5db50..46e96f044 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -38,6 +38,17 @@ typedef enum { GIT_ITERATOR_INCLUDE_CONFLICTS = (1u << 5), } git_iterator_flag_t; + +typedef struct { + const char *start; + const char *end; + + /* flags, from above */ + unsigned int flags; +} git_iterator_options; + +#define GIT_ITERATOR_OPTIONS_INIT {0} + typedef struct { int (*current)(const git_index_entry **, git_iterator *); int (*advance)(const git_index_entry **, git_iterator *); @@ -61,9 +72,7 @@ struct git_iterator { extern int git_iterator_for_nothing( git_iterator **out, - git_iterator_flag_t flags, - const char *start, - const char *end); + git_iterator_options *options); /* tree iterators will match the ignore_case value from the index of the * repository, unless you override with a non-zero flag value @@ -71,9 +80,7 @@ extern int git_iterator_for_nothing( extern int git_iterator_for_tree( git_iterator **out, git_tree *tree, - git_iterator_flag_t flags, - const char *start, - const char *end); + git_iterator_options *options); /* index iterators will take the ignore_case value from the index; the * ignore_case flags are not used @@ -81,9 +88,7 @@ extern int git_iterator_for_tree( extern int git_iterator_for_index( git_iterator **out, git_index *index, - git_iterator_flag_t flags, - const char *start, - const char *end); + git_iterator_options *options); extern int git_iterator_for_workdir_ext( git_iterator **out, @@ -91,9 +96,7 @@ extern int git_iterator_for_workdir_ext( const char *repo_workdir, git_index *index, git_tree *tree, - git_iterator_flag_t flags, - const char *start, - const char *end); + git_iterator_options *options); /* workdir iterators will match the ignore_case value from the index of the * repository, unless you override with a non-zero flag value @@ -103,11 +106,9 @@ GIT_INLINE(int) git_iterator_for_workdir( git_repository *repo, git_index *index, git_tree *tree, - git_iterator_flag_t flags, - const char *start, - const char *end) + git_iterator_options *options) { - return git_iterator_for_workdir_ext(out, repo, NULL, index, tree, flags, start, end); + return git_iterator_for_workdir_ext(out, repo, NULL, index, tree, options); } /* for filesystem iterators, you have to explicitly pass in the ignore_case @@ -116,9 +117,7 @@ GIT_INLINE(int) git_iterator_for_workdir( extern int git_iterator_for_filesystem( git_iterator **out, const char *root, - git_iterator_flag_t flags, - const char *start, - const char *end); + git_iterator_options *options); extern void git_iterator_free(git_iterator *iter); diff --git a/src/merge.c b/src/merge.c index 863ac8f2d..16cd2aee0 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1695,10 +1695,14 @@ on_error: static git_iterator *iterator_given_or_empty(git_iterator **empty, git_iterator *given) { + git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT; + if (given) return given; - if (git_iterator_for_nothing(empty, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL) < 0) + opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + + if (git_iterator_for_nothing(empty, &opts) < 0) return NULL; return *empty; @@ -1780,14 +1784,17 @@ int git_merge_trees( const git_merge_options *merge_opts) { git_iterator *ancestor_iter = NULL, *our_iter = NULL, *their_iter = NULL; + git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; int error; - if ((error = git_iterator_for_tree(&ancestor_iter, (git_tree *)ancestor_tree, - GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || - (error = git_iterator_for_tree(&our_iter, (git_tree *)our_tree, - GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || - (error = git_iterator_for_tree(&their_iter, (git_tree *)their_tree, - GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0) + iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + + if ((error = git_iterator_for_tree( + &ancestor_iter, (git_tree *)ancestor_tree, &iter_opts)) < 0 || + (error = git_iterator_for_tree( + &our_iter, (git_tree *)our_tree, &iter_opts)) < 0 || + (error = git_iterator_for_tree( + &their_iter, (git_tree *)their_tree, &iter_opts)) < 0) goto done; error = git_merge__iterators( @@ -2319,6 +2326,7 @@ static int merge_check_index(size_t *conflicts, git_repository *repo, git_index git_tree *head_tree = NULL; git_index *index_repo = NULL; git_iterator *iter_repo = NULL, *iter_new = NULL; + git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; git_diff *staged_diff_list = NULL, *index_diff_list = NULL; git_diff_delta *delta; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; @@ -2351,8 +2359,10 @@ static int merge_check_index(size_t *conflicts, git_repository *repo, git_index opts.pathspec.count = staged_paths.length; opts.pathspec.strings = (char **)staged_paths.contents; - if ((error = git_iterator_for_index(&iter_repo, index_repo, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || - (error = git_iterator_for_index(&iter_new, index_new, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || + iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + + if ((error = git_iterator_for_index(&iter_repo, index_repo, &iter_opts)) < 0 || + (error = git_iterator_for_index(&iter_new, index_new, &iter_opts)) < 0 || (error = git_diff__from_iterators(&index_diff_list, repo, iter_repo, iter_new, &opts)) < 0) goto done; @@ -2414,6 +2424,7 @@ int git_merge__check_result(git_repository *repo, git_index *index_new) { git_tree *head_tree = NULL; git_iterator *iter_head = NULL, *iter_new = NULL; + git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; git_diff *merged_list = NULL; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_delta *delta; @@ -2422,9 +2433,11 @@ int git_merge__check_result(git_repository *repo, git_index *index_new) const git_index_entry *e; int error = 0; + iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + if ((error = git_repository_head_tree(&head_tree, repo)) < 0 || - (error = git_iterator_for_tree(&iter_head, head_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || - (error = git_iterator_for_index(&iter_new, index_new, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || + (error = git_iterator_for_tree(&iter_head, head_tree, &iter_opts)) < 0 || + (error = git_iterator_for_index(&iter_new, index_new, &iter_opts)) < 0 || (error = git_diff__from_iterators(&merged_list, repo, iter_head, iter_new, &opts)) < 0) goto done; diff --git a/src/notes.c b/src/notes.c index ef4b41b31..fe8d2164f 100644 --- a/src/notes.c +++ b/src/notes.c @@ -663,7 +663,7 @@ int git_note_iterator_new( if (error < 0) goto cleanup; - if ((error = git_iterator_for_tree(it, tree, 0, NULL, NULL)) < 0) + if ((error = git_iterator_for_tree(it, tree, NULL)) < 0) git_iterator_free(*it); cleanup: diff --git a/src/pathspec.c b/src/pathspec.c index fab6f9a76..9304da705 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -524,16 +524,16 @@ int git_pathspec_match_workdir( uint32_t flags, git_pathspec *ps) { - int error = 0; git_iterator *iter; + git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; + int error = 0; assert(repo); - if (!(error = git_iterator_for_workdir( - &iter, repo, NULL, NULL, pathspec_match_iter_flags(flags), NULL, NULL))) { + iter_opts.flags = pathspec_match_iter_flags(flags); + if (!(error = git_iterator_for_workdir(&iter, repo, NULL, NULL, &iter_opts))) { error = pathspec_match_from_iterator(out, iter, flags, ps); - git_iterator_free(iter); } @@ -546,16 +546,16 @@ int git_pathspec_match_index( uint32_t flags, git_pathspec *ps) { - int error = 0; git_iterator *iter; + git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; + int error = 0; assert(index); - if (!(error = git_iterator_for_index( - &iter, index, pathspec_match_iter_flags(flags), NULL, NULL))) { + iter_opts.flags = pathspec_match_iter_flags(flags); + if (!(error = git_iterator_for_index(&iter, index, &iter_opts))) { error = pathspec_match_from_iterator(out, iter, flags, ps); - git_iterator_free(iter); } @@ -568,16 +568,16 @@ int git_pathspec_match_tree( uint32_t flags, git_pathspec *ps) { - int error = 0; git_iterator *iter; + git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; + int error = 0; assert(tree); - if (!(error = git_iterator_for_tree( - &iter, tree, pathspec_match_iter_flags(flags), NULL, NULL))) { + iter_opts.flags = pathspec_match_iter_flags(flags); + if (!(error = git_iterator_for_tree(&iter, tree, &iter_opts))) { error = pathspec_match_from_iterator(out, iter, flags, ps); - git_iterator_free(iter); } diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 55d535eb4..1ddce4649 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -480,14 +480,16 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter) int error = 0; git_buf path = GIT_BUF_INIT; git_iterator *fsit = NULL; + git_iterator_options fsit_opts = GIT_ITERATOR_OPTIONS_INIT; const git_index_entry *entry = NULL; if (!backend->path) /* do nothing if no path for loose refs */ return 0; + fsit_opts.flags = backend->iterator_flags; + if ((error = git_buf_printf(&path, "%s/refs", backend->path)) < 0 || - (error = git_iterator_for_filesystem( - &fsit, path.ptr, backend->iterator_flags, NULL, NULL)) < 0) { + (error = git_iterator_for_filesystem(&fsit, path.ptr, &fsit_opts)) < 0) { git_buf_free(&path); return error; } diff --git a/src/stash.c b/src/stash.c index fcb1112ac..35824659a 100644 --- a/src/stash.c +++ b/src/stash.c @@ -679,12 +679,14 @@ static int merge_indexes( git_index *theirs_index) { git_iterator *ancestor = NULL, *ours = NULL, *theirs = NULL; - const git_iterator_flag_t flags = GIT_ITERATOR_DONT_IGNORE_CASE; + git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; int error; - if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, flags, NULL, NULL)) < 0 || - (error = git_iterator_for_index(&ours, ours_index, flags, NULL, NULL)) < 0 || - (error = git_iterator_for_index(&theirs, theirs_index, flags, NULL, NULL)) < 0) + iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + + if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, &iter_opts)) < 0 || + (error = git_iterator_for_index(&ours, ours_index, &iter_opts)) < 0 || + (error = git_iterator_for_index(&theirs, theirs_index, &iter_opts)) < 0) goto done; error = git_merge__iterators(out, repo, ancestor, ours, theirs, NULL); @@ -704,12 +706,14 @@ static int merge_index_and_tree( git_tree *theirs_tree) { git_iterator *ancestor = NULL, *ours = NULL, *theirs = NULL; - const git_iterator_flag_t flags = GIT_ITERATOR_DONT_IGNORE_CASE; + git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; int error; - if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, flags, NULL, NULL)) < 0 || - (error = git_iterator_for_index(&ours, ours_index, flags, NULL, NULL)) < 0 || - (error = git_iterator_for_tree(&theirs, theirs_tree, flags, NULL, NULL)) < 0) + iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + + if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, &iter_opts)) < 0 || + (error = git_iterator_for_index(&ours, ours_index, &iter_opts)) < 0 || + (error = git_iterator_for_tree(&theirs, theirs_tree, &iter_opts)) < 0) goto done; error = git_merge__iterators(out, repo, ancestor, ours, theirs, NULL); @@ -797,14 +801,15 @@ static int stage_new_files( git_tree *tree) { git_iterator *iterators[2] = { NULL, NULL }; + git_iterator_options iterator_options = GIT_ITERATOR_OPTIONS_INIT; git_index *index = NULL; int error; if ((error = git_index_new(&index)) < 0 || - (error = git_iterator_for_tree(&iterators[0], parent_tree, - GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || - (error = git_iterator_for_tree(&iterators[1], tree, - GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0) + (error = git_iterator_for_tree( + &iterators[0], parent_tree, &iterator_options)) < 0 || + (error = git_iterator_for_tree( + &iterators[1], tree, &iterator_options)) < 0) goto done; error = git_iterator_walk(iterators, 2, stage_new_file, index); diff --git a/src/submodule.c b/src/submodule.c index 991ebc8f3..7f52c3616 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -286,7 +286,7 @@ static int submodules_from_index(git_strmap *map, git_index *idx) git_iterator *i; const git_index_entry *entry; - if ((error = git_iterator_for_index(&i, idx, 0, NULL, NULL)) < 0) + if ((error = git_iterator_for_index(&i, idx, NULL)) < 0) return error; while (!(error = git_iterator_advance(&entry, i))) { @@ -322,7 +322,7 @@ static int submodules_from_head(git_strmap *map, git_tree *head) git_iterator *i; const git_index_entry *entry; - if ((error = git_iterator_for_tree(&i, head, 0, NULL, NULL)) < 0) + if ((error = git_iterator_for_tree(&i, head, NULL)) < 0) return error; while (!(error = git_iterator_advance(&entry, i))) { diff --git a/tests/diff/iterator.c b/tests/diff/iterator.c index 6011c6a9b..eafb1b9da 100644 --- a/tests/diff/iterator.c +++ b/tests/diff/iterator.c @@ -30,13 +30,17 @@ static void tree_iterator_test( { git_tree *t; git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; const git_index_entry *entry; int error, count = 0, count_post_reset = 0; git_repository *repo = cl_git_sandbox_init(sandbox); + i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + i_opts.start = start; + i_opts.end = end; + cl_assert(t = resolve_commit_oid_to_tree(repo, treeish)); - cl_git_pass(git_iterator_for_tree( - &i, t, GIT_ITERATOR_DONT_IGNORE_CASE, start, end)); + cl_git_pass(git_iterator_for_tree(&i, t, &i_opts)); /* test loop */ while (!(error = git_iterator_advance(&entry, i))) { @@ -297,6 +301,7 @@ void test_diff_iterator__tree_special_functions(void) { git_tree *t; git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; const git_index_entry *entry; git_repository *repo = cl_git_sandbox_init("attr"); int error, cases = 0; @@ -306,8 +311,9 @@ void test_diff_iterator__tree_special_functions(void) repo, "24fa9a9fc4e202313e24b648087495441dab432b"); cl_assert(t != NULL); - cl_git_pass(git_iterator_for_tree( - &i, t, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + + cl_git_pass(git_iterator_for_tree(&i, t, &i_opts)); while (!(error = git_iterator_advance(&entry, i))) { cl_assert(entry); @@ -365,11 +371,16 @@ static void index_iterator_test( const git_index_entry *entry; int error, count = 0, caps; git_repository *repo = cl_git_sandbox_init(sandbox); + git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; cl_git_pass(git_repository_index(&index, repo)); caps = git_index_caps(index); - cl_git_pass(git_iterator_for_index(&i, index, flags, start, end)); + iter_opts.flags = flags; + iter_opts.start = start; + iter_opts.end = end; + + cl_git_pass(git_iterator_for_index(&i, index, &iter_opts)); while (!(error = git_iterator_advance(&entry, i))) { cl_assert(entry); @@ -581,12 +592,16 @@ static void workdir_iterator_test( const char *an_ignored_name) { git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; const git_index_entry *entry; int error, count = 0, count_all = 0, count_all_post_reset = 0; git_repository *repo = cl_git_sandbox_init(sandbox); - cl_git_pass(git_iterator_for_workdir( - &i, repo, NULL, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, start, end)); + i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND; + i_opts.start = start; + i_opts.end = end; + + cl_git_pass(git_iterator_for_workdir(&i, repo, NULL, NULL, &i_opts)); error = git_iterator_current(&entry, i); cl_assert((error == 0 && entry != NULL) || @@ -765,6 +780,7 @@ void test_diff_iterator__workdir_builtin_ignores(void) { git_repository *repo = cl_git_sandbox_init("attr"); git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; const git_index_entry *entry; int idx; static struct { @@ -796,8 +812,12 @@ void test_diff_iterator__workdir_builtin_ignores(void) cl_git_pass(p_mkdir("attr/sub/sub/.git", 0777)); cl_git_mkfile("attr/sub/.git", "whatever"); + i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND; + i_opts.start = "dir"; + i_opts.end = "sub/sub/file"; + cl_git_pass(git_iterator_for_workdir( - &i, repo, NULL, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, "dir", "sub/sub/file")); + &i, repo, NULL, NULL, &i_opts)); cl_git_pass(git_iterator_current(&entry, i)); for (idx = 0; entry != NULL; ++idx) { @@ -827,12 +847,17 @@ static void check_wd_first_through_third_range( git_repository *repo, const char *start, const char *end) { git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; const git_index_entry *entry; int error, idx; static const char *expected[] = { "FIRST", "second", "THIRD", NULL }; + i_opts.flags = GIT_ITERATOR_IGNORE_CASE; + i_opts.start = start; + i_opts.end = end; + cl_git_pass(git_iterator_for_workdir( - &i, repo, NULL, NULL, GIT_ITERATOR_IGNORE_CASE, start, end)); + &i, repo, NULL, NULL, &i_opts)); cl_git_pass(git_iterator_current(&entry, i)); for (idx = 0; entry != NULL; ++idx) { @@ -877,14 +902,16 @@ static void check_tree_range( { git_tree *head; git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; int error, count; + i_opts.flags = ignore_case ? GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE; + i_opts.start = start; + i_opts.end = end; + cl_git_pass(git_repository_head_tree(&head, repo)); - cl_git_pass(git_iterator_for_tree( - &i, head, - ignore_case ? GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE, - start, end)); + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); for (count = 0; !(error = git_iterator_advance(NULL, i)); ++count) /* count em up */; @@ -931,6 +958,7 @@ static void check_index_range( { git_index *index; git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; int error, count, caps; bool is_ignoring_case; @@ -942,7 +970,11 @@ static void check_index_range( if (ignore_case != is_ignoring_case) cl_git_pass(git_index_set_caps(index, caps ^ GIT_INDEXCAP_IGNORE_CASE)); - cl_git_pass(git_iterator_for_index(&i, index, 0, start, end)); + i_opts.flags = 0; + i_opts.start = start; + i_opts.end = end; + + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); cl_assert(git_iterator_ignore_case(i) == ignore_case); diff --git a/tests/merge/trees/treediff.c b/tests/merge/trees/treediff.c index b96c4c4db..f21d99b6d 100644 --- a/tests/merge/trees/treediff.c +++ b/tests/merge/trees/treediff.c @@ -44,6 +44,7 @@ static void test_find_differences( git_oid ancestor_oid, ours_oid, theirs_oid; git_tree *ancestor_tree, *ours_tree, *theirs_tree; git_iterator *ancestor_iter, *ours_iter, *theirs_iter; + git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; opts.tree_flags |= GIT_MERGE_TREE_FIND_RENAMES; @@ -67,12 +68,11 @@ static void test_find_differences( cl_git_pass(git_tree_lookup(&ours_tree, repo, &ours_oid)); cl_git_pass(git_tree_lookup(&theirs_tree, repo, &theirs_oid)); - cl_git_pass(git_iterator_for_tree(&ancestor_iter, ancestor_tree, - GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)); - cl_git_pass(git_iterator_for_tree(&ours_iter, ours_tree, - GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)); - cl_git_pass(git_iterator_for_tree(&theirs_iter, theirs_tree, - GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)); + iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + + cl_git_pass(git_iterator_for_tree(&ancestor_iter, ancestor_tree, &iter_opts)); + cl_git_pass(git_iterator_for_tree(&ours_iter, ours_tree, &iter_opts)); + cl_git_pass(git_iterator_for_tree(&theirs_iter, theirs_tree, &iter_opts)); cl_git_pass(git_merge_diff_list__find_differences(merge_diff_list, ancestor_iter, ours_iter, theirs_iter)); cl_git_pass(git_merge_diff_list__find_renames(repo, merge_diff_list, &opts)); diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c index bb2d3a186..4d0d12be1 100644 --- a/tests/repo/iterator.c +++ b/tests/repo/iterator.c @@ -126,6 +126,7 @@ static void expect_iterator_items( void test_repo_iterator__index(void) { git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; git_index *index; g_repo = cl_git_sandbox_init("icase"); @@ -133,19 +134,19 @@ void test_repo_iterator__index(void) cl_git_pass(git_repository_index(&index, g_repo)); /* autoexpand with no tree entries for index */ - cl_git_pass(git_iterator_for_index(&i, index, 0, NULL, NULL)); + cl_git_pass(git_iterator_for_index(&i, index, NULL)); expect_iterator_items(i, 20, NULL, 20, NULL); git_iterator_free(i); /* auto expand with tree entries */ - cl_git_pass(git_iterator_for_index( - &i, index, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_INCLUDE_TREES; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); expect_iterator_items(i, 22, NULL, 22, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ - cl_git_pass(git_iterator_for_index( - &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); expect_iterator_items(i, 12, NULL, 22, NULL); git_iterator_free(i); @@ -155,6 +156,7 @@ void test_repo_iterator__index(void) void test_repo_iterator__index_icase(void) { git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; git_index *index; int caps; @@ -167,32 +169,45 @@ void test_repo_iterator__index_icase(void) cl_git_pass(git_index_set_caps(index, caps & ~GIT_INDEXCAP_IGNORE_CASE)); /* autoexpand with no tree entries over range */ - cl_git_pass(git_iterator_for_index(&i, index, 0, "c", "k/D")); + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); expect_iterator_items(i, 7, NULL, 7, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_index(&i, index, 0, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); expect_iterator_items(i, 3, NULL, 3, NULL); git_iterator_free(i); /* auto expand with tree entries */ - cl_git_pass(git_iterator_for_index( - &i, index, GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); + i_opts.flags = GIT_ITERATOR_INCLUDE_TREES; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); expect_iterator_items(i, 8, NULL, 8, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_index( - &i, index, GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); + + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); expect_iterator_items(i, 4, NULL, 4, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ - cl_git_pass(git_iterator_for_index( - &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); + i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); expect_iterator_items(i, 5, NULL, 8, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_index( - &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); expect_iterator_items(i, 1, NULL, 4, NULL); git_iterator_free(i); @@ -200,33 +215,47 @@ void test_repo_iterator__index_icase(void) cl_git_pass(git_index_set_caps(index, caps | GIT_INDEXCAP_IGNORE_CASE)); /* autoexpand with no tree entries over range */ - cl_git_pass(git_iterator_for_index(&i, index, 0, "c", "k/D")); + i_opts.flags = 0; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); expect_iterator_items(i, 13, NULL, 13, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_index(&i, index, 0, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); expect_iterator_items(i, 5, NULL, 5, NULL); git_iterator_free(i); /* auto expand with tree entries */ - cl_git_pass(git_iterator_for_index( - &i, index, GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); + i_opts.flags = GIT_ITERATOR_INCLUDE_TREES; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); expect_iterator_items(i, 14, NULL, 14, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_index( - &i, index, GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); expect_iterator_items(i, 6, NULL, 6, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ - cl_git_pass(git_iterator_for_index( - &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); + i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); expect_iterator_items(i, 9, NULL, 14, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_index( - &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); expect_iterator_items(i, 1, NULL, 6, NULL); git_iterator_free(i); @@ -237,6 +266,7 @@ void test_repo_iterator__index_icase(void) void test_repo_iterator__tree(void) { git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; git_tree *head; g_repo = cl_git_sandbox_init("icase"); @@ -244,19 +274,21 @@ void test_repo_iterator__tree(void) cl_git_pass(git_repository_head_tree(&head, g_repo)); /* auto expand with no tree entries */ - cl_git_pass(git_iterator_for_tree(&i, head, 0, NULL, NULL)); + cl_git_pass(git_iterator_for_tree(&i, head, NULL)); expect_iterator_items(i, 20, NULL, 20, NULL); git_iterator_free(i); /* auto expand with tree entries */ - cl_git_pass(git_iterator_for_tree( - &i, head, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_INCLUDE_TREES; + + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); expect_iterator_items(i, 22, NULL, 22, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ - cl_git_pass(git_iterator_for_tree( - &i, head, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND; + + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); expect_iterator_items(i, 12, NULL, 22, NULL); git_iterator_free(i); @@ -267,75 +299,98 @@ void test_repo_iterator__tree_icase(void) { git_iterator *i; git_tree *head; - git_iterator_flag_t flag; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; g_repo = cl_git_sandbox_init("icase"); cl_git_pass(git_repository_head_tree(&head, g_repo)); - flag = GIT_ITERATOR_DONT_IGNORE_CASE; + i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; /* auto expand with no tree entries */ - cl_git_pass(git_iterator_for_tree(&i, head, flag, "c", "k/D")); + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); expect_iterator_items(i, 7, NULL, 7, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_tree(&i, head, flag, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); expect_iterator_items(i, 3, NULL, 3, NULL); git_iterator_free(i); + i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES; + /* auto expand with tree entries */ - cl_git_pass(git_iterator_for_tree( - &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); + i_opts.start = "c"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); expect_iterator_items(i, 8, NULL, 8, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_tree( - &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); expect_iterator_items(i, 4, NULL, 4, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ - cl_git_pass(git_iterator_for_tree( - &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); + i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_DONT_AUTOEXPAND; + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); expect_iterator_items(i, 5, NULL, 8, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_tree( - &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); expect_iterator_items(i, 1, NULL, 4, NULL); git_iterator_free(i); - flag = GIT_ITERATOR_IGNORE_CASE; - /* auto expand with no tree entries */ - cl_git_pass(git_iterator_for_tree(&i, head, flag, "c", "k/D")); + i_opts.flags = GIT_ITERATOR_IGNORE_CASE; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); expect_iterator_items(i, 13, NULL, 13, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_tree(&i, head, flag, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); expect_iterator_items(i, 5, NULL, 5, NULL); git_iterator_free(i); /* auto expand with tree entries */ - cl_git_pass(git_iterator_for_tree( - &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); + i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); expect_iterator_items(i, 14, NULL, 14, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_tree( - &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); expect_iterator_items(i, 6, NULL, 6, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ - cl_git_pass(git_iterator_for_tree( - &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); + i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_AUTOEXPAND; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); expect_iterator_items(i, 9, NULL, 14, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_tree( - &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); expect_iterator_items(i, 1, NULL, 6, NULL); git_iterator_free(i); @@ -345,6 +400,7 @@ void test_repo_iterator__tree_icase(void) void test_repo_iterator__tree_more(void) { git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; git_tree *head; static const char *expect_basic[] = { "current_file", @@ -396,19 +452,21 @@ void test_repo_iterator__tree_more(void) cl_git_pass(git_repository_head_tree(&head, g_repo)); /* auto expand with no tree entries */ - cl_git_pass(git_iterator_for_tree(&i, head, 0, NULL, NULL)); + cl_git_pass(git_iterator_for_tree(&i, head, NULL)); expect_iterator_items(i, 12, expect_basic, 12, expect_basic); git_iterator_free(i); /* auto expand with tree entries */ - cl_git_pass(git_iterator_for_tree( - &i, head, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_INCLUDE_TREES; + + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); expect_iterator_items(i, 13, expect_trees, 13, expect_trees); git_iterator_free(i); /* no auto expand (implies trees included) */ - cl_git_pass(git_iterator_for_tree( - &i, head, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND; + + cl_git_pass(git_iterator_for_tree(&i, head, &i_opts)); expect_iterator_items(i, 10, expect_noauto, 13, expect_trees); git_iterator_free(i); @@ -463,6 +521,8 @@ void test_repo_iterator__tree_case_conflicts_0(void) git_tree *tree; git_oid blob_id, biga_id, littlea_id, tree_id; git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; + const char *expect_cs[] = { "A/1.file", "A/3.file", "a/2.file", "a/4.file" }; const char *expect_ci[] = { @@ -486,25 +546,23 @@ void test_repo_iterator__tree_case_conflicts_0(void) cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); - cl_git_pass(git_iterator_for_tree( - &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); expect_iterator_items(i, 4, expect_cs, 4, expect_cs); git_iterator_free(i); - cl_git_pass(git_iterator_for_tree( - &i, tree, GIT_ITERATOR_IGNORE_CASE, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_IGNORE_CASE; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); expect_iterator_items(i, 4, expect_ci, 4, expect_ci); git_iterator_free(i); - cl_git_pass(git_iterator_for_tree( - &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE | - GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); expect_iterator_items(i, 6, expect_cs_trees, 6, expect_cs_trees); git_iterator_free(i); - cl_git_pass(git_iterator_for_tree( - &i, tree, GIT_ITERATOR_IGNORE_CASE | - GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); expect_iterator_items(i, 5, expect_ci_trees, 5, expect_ci_trees); git_iterator_free(i); @@ -517,6 +575,8 @@ void test_repo_iterator__tree_case_conflicts_1(void) git_tree *tree; git_oid blob_id, Ab_id, biga_id, littlea_id, tree_id; git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; + const char *expect_cs[] = { "A/a", "A/b/1", "A/c", "a/C", "a/a", "a/b" }; const char *expect_ci[] = { @@ -541,25 +601,23 @@ void test_repo_iterator__tree_case_conflicts_1(void) cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); - cl_git_pass(git_iterator_for_tree( - &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); expect_iterator_items(i, 6, expect_cs, 6, expect_cs); git_iterator_free(i); - cl_git_pass(git_iterator_for_tree( - &i, tree, GIT_ITERATOR_IGNORE_CASE, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_IGNORE_CASE; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); expect_iterator_items(i, 4, expect_ci, 4, expect_ci); git_iterator_free(i); - cl_git_pass(git_iterator_for_tree( - &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE | - GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); expect_iterator_items(i, 9, expect_cs_trees, 9, expect_cs_trees); git_iterator_free(i); - cl_git_pass(git_iterator_for_tree( - &i, tree, GIT_ITERATOR_IGNORE_CASE | - GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); expect_iterator_items(i, 6, expect_ci_trees, 6, expect_ci_trees); git_iterator_free(i); @@ -572,6 +630,8 @@ void test_repo_iterator__tree_case_conflicts_2(void) git_tree *tree; git_oid blob_id, d1, d2, c1, c2, b1, b2, a1, a2, tree_id; git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; + const char *expect_cs[] = { "A/B/C/D/16", "A/B/C/D/foo", "A/B/C/d/15", "A/B/C/d/FOO", "A/B/c/D/14", "A/B/c/D/foo", "A/B/c/d/13", "A/B/c/d/FOO", @@ -639,19 +699,18 @@ void test_repo_iterator__tree_case_conflicts_2(void) cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); - cl_git_pass(git_iterator_for_tree( - &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); expect_iterator_items(i, 32, expect_cs, 32, expect_cs); git_iterator_free(i); - cl_git_pass(git_iterator_for_tree( - &i, tree, GIT_ITERATOR_IGNORE_CASE, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_IGNORE_CASE; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); expect_iterator_items(i, 17, expect_ci, 17, expect_ci); git_iterator_free(i); - cl_git_pass(git_iterator_for_tree( - &i, tree, GIT_ITERATOR_IGNORE_CASE | - GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); expect_iterator_items(i, 21, expect_ci_trees, 21, expect_ci_trees); git_iterator_free(i); @@ -661,23 +720,24 @@ void test_repo_iterator__tree_case_conflicts_2(void) void test_repo_iterator__workdir(void) { git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; g_repo = cl_git_sandbox_init("icase"); /* auto expand with no tree entries */ - cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, 0, NULL, NULL)); + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); expect_iterator_items(i, 20, NULL, 20, NULL); git_iterator_free(i); /* auto expand with tree entries */ - cl_git_pass(git_iterator_for_workdir( - &i, g_repo, NULL, NULL, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_INCLUDE_TREES; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); expect_iterator_items(i, 22, NULL, 22, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ - cl_git_pass(git_iterator_for_workdir( - &i, g_repo, NULL, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); expect_iterator_items(i, 12, NULL, 22, NULL); git_iterator_free(i); } @@ -685,73 +745,97 @@ void test_repo_iterator__workdir(void) void test_repo_iterator__workdir_icase(void) { git_iterator *i; - git_iterator_flag_t flag; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; g_repo = cl_git_sandbox_init("icase"); - flag = GIT_ITERATOR_DONT_IGNORE_CASE; - /* auto expand with no tree entries */ - cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, flag, "c", "k/D")); + i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); expect_iterator_items(i, 7, NULL, 7, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, flag, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); expect_iterator_items(i, 3, NULL, 3, NULL); git_iterator_free(i); /* auto expand with tree entries */ - cl_git_pass(git_iterator_for_workdir( - &i, g_repo, NULL, NULL, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); + i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); expect_iterator_items(i, 8, NULL, 8, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_workdir( - &i, g_repo, NULL, NULL, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); expect_iterator_items(i, 4, NULL, 4, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ - cl_git_pass(git_iterator_for_workdir( - &i, g_repo, NULL, NULL, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); + i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_DONT_AUTOEXPAND; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); expect_iterator_items(i, 5, NULL, 8, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_workdir( - &i, g_repo, NULL, NULL, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); expect_iterator_items(i, 1, NULL, 4, NULL); git_iterator_free(i); - flag = GIT_ITERATOR_IGNORE_CASE; - /* auto expand with no tree entries */ - cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, flag, "c", "k/D")); + i_opts.flags = GIT_ITERATOR_IGNORE_CASE; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); expect_iterator_items(i, 13, NULL, 13, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, flag, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); expect_iterator_items(i, 5, NULL, 5, NULL); git_iterator_free(i); /* auto expand with tree entries */ - cl_git_pass(git_iterator_for_workdir( - &i, g_repo, NULL, NULL, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); + i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); expect_iterator_items(i, 14, NULL, 14, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_workdir( - &i, g_repo, NULL, NULL, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); expect_iterator_items(i, 6, NULL, 6, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ - cl_git_pass(git_iterator_for_workdir( - &i, g_repo, NULL, NULL, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); + i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_AUTOEXPAND; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); expect_iterator_items(i, 9, NULL, 14, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_workdir( - &i, g_repo, NULL, NULL, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); expect_iterator_items(i, 1, NULL, 6, NULL); git_iterator_free(i); } @@ -796,6 +880,7 @@ static void build_workdir_tree(const char *root, int dirs, int subs) void test_repo_iterator__workdir_depth(void) { git_iterator *iter; + git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; g_repo = cl_git_sandbox_init("icase"); @@ -804,13 +889,13 @@ void test_repo_iterator__workdir_depth(void) build_workdir_tree("icase/dir02/sUB01", 50, 0); /* auto expand with no tree entries */ - cl_git_pass(git_iterator_for_workdir(&iter, g_repo, NULL, NULL, 0, NULL, NULL)); + cl_git_pass(git_iterator_for_workdir(&iter, g_repo, NULL, NULL, &iter_opts)); expect_iterator_items(iter, 125, NULL, 125, NULL); git_iterator_free(iter); /* auto expand with tree entries (empty dirs silently skipped) */ - cl_git_pass(git_iterator_for_workdir( - &iter, g_repo, NULL, NULL, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + iter_opts.flags = GIT_ITERATOR_INCLUDE_TREES; + cl_git_pass(git_iterator_for_workdir(&iter, g_repo, NULL, NULL, &iter_opts)); expect_iterator_items(iter, 337, NULL, 337, NULL); git_iterator_free(iter); } @@ -818,6 +903,8 @@ void test_repo_iterator__workdir_depth(void) void test_repo_iterator__fs(void) { git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; + static const char *expect_base[] = { "DIR01/Sub02/file", "DIR01/sub00/file", @@ -863,18 +950,17 @@ void test_repo_iterator__fs(void) build_workdir_tree("status/subdir", 2, 4); - cl_git_pass(git_iterator_for_filesystem( - &i, "status/subdir", 0, NULL, NULL)); + cl_git_pass(git_iterator_for_filesystem(&i, "status/subdir", NULL)); expect_iterator_items(i, 8, expect_base, 8, expect_base); git_iterator_free(i); - cl_git_pass(git_iterator_for_filesystem( - &i, "status/subdir", GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_INCLUDE_TREES; + cl_git_pass(git_iterator_for_filesystem(&i, "status/subdir", &i_opts)); expect_iterator_items(i, 18, expect_trees, 18, expect_trees); git_iterator_free(i); - cl_git_pass(git_iterator_for_filesystem( - &i, "status/subdir", GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND; + cl_git_pass(git_iterator_for_filesystem(&i, "status/subdir", &i_opts)); expect_iterator_items(i, 5, expect_noauto, 18, expect_trees); git_iterator_free(i); @@ -882,20 +968,18 @@ void test_repo_iterator__fs(void) git__tsort((void **)expect_trees, 18, (git__tsort_cmp)git__strcasecmp); git__tsort((void **)expect_noauto, 5, (git__tsort_cmp)git__strcasecmp); - cl_git_pass(git_iterator_for_filesystem( - &i, "status/subdir", GIT_ITERATOR_IGNORE_CASE, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_IGNORE_CASE; + cl_git_pass(git_iterator_for_filesystem(&i, "status/subdir", &i_opts)); expect_iterator_items(i, 8, expect_base, 8, expect_base); git_iterator_free(i); - cl_git_pass(git_iterator_for_filesystem( - &i, "status/subdir", GIT_ITERATOR_IGNORE_CASE | - GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES; + cl_git_pass(git_iterator_for_filesystem(&i, "status/subdir", &i_opts)); expect_iterator_items(i, 18, expect_trees, 18, expect_trees); git_iterator_free(i); - cl_git_pass(git_iterator_for_filesystem( - &i, "status/subdir", GIT_ITERATOR_IGNORE_CASE | - GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); + i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_AUTOEXPAND; + cl_git_pass(git_iterator_for_filesystem(&i, "status/subdir", &i_opts)); expect_iterator_items(i, 5, expect_noauto, 18, expect_trees); git_iterator_free(i); } @@ -923,7 +1007,7 @@ void test_repo_iterator__fs2(void) g_repo = cl_git_sandbox_init("testrepo"); cl_git_pass(git_iterator_for_filesystem( - &i, "testrepo/.git/refs", 0, NULL, NULL)); + &i, "testrepo/.git/refs", NULL)); expect_iterator_items(i, 13, expect_base, 13, expect_base); git_iterator_free(i); } @@ -947,7 +1031,7 @@ void test_repo_iterator__unreadable_dir(void) cl_git_mkfile("empty_standard_repo/r/d", "final"); cl_git_pass(git_iterator_for_filesystem( - &i, "empty_standard_repo/r", 0, NULL, NULL)); + &i, "empty_standard_repo/r", NULL)); cl_git_pass(git_iterator_advance(&e, i)); /* a */ cl_git_fail(git_iterator_advance(&e, i)); /* b */ diff --git a/tests/submodule/status.c b/tests/submodule/status.c index 6721ee92a..5f4e62053 100644 --- a/tests/submodule/status.c +++ b/tests/submodule/status.c @@ -264,6 +264,7 @@ static int confirm_submodule_status( void test_submodule_status__iterator(void) { git_iterator *iter; + git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; const git_index_entry *entry; size_t i; static const char *expected[] = { @@ -308,9 +309,10 @@ void test_submodule_status__iterator(void) git_status_options opts = GIT_STATUS_OPTIONS_INIT; git_index *index; + iter_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES; + cl_git_pass(git_repository_index(&index, g_repo)); - cl_git_pass(git_iterator_for_workdir(&iter, g_repo, index, NULL, - GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + cl_git_pass(git_iterator_for_workdir(&iter, g_repo, index, NULL, &iter_opts)); for (i = 0; !git_iterator_advance(&entry, iter); ++i) cl_assert_equal_s(expected[i], entry->path); diff --git a/tests/threads/iterator.c b/tests/threads/iterator.c index 8a2d79c2e..6b86cf1a0 100644 --- a/tests/threads/iterator.c +++ b/tests/threads/iterator.c @@ -13,10 +13,13 @@ static void *run_workdir_iterator(void *arg) { int error = 0; git_iterator *iter; + git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; const git_index_entry *entry = NULL; + iter_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND; + cl_git_pass(git_iterator_for_workdir( - &iter, _repo, NULL, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); + &iter, _repo, NULL, NULL, &iter_opts)); while (!error) { if (entry && entry->mode == GIT_FILEMODE_TREE) { From ef206124de957408c8d867e2f923d0611a7274fc Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 28 Jul 2015 19:55:37 -0500 Subject: [PATCH 083/450] Move filelist into the iterator handling itself. --- include/git2/diff.h | 7 ++ src/diff.c | 46 ++++++-- src/iterator.c | 115 ++++++++++++++++++-- src/iterator.h | 7 ++ src/merge.c | 5 +- tests/repo/iterator.c | 248 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 405 insertions(+), 23 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index 0abbc7f06..c3589bb13 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -163,6 +163,13 @@ typedef enum { /** Include unreadable files in the diff */ GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED = (1u << 17), + /** Use literal path matching in the iterators. This is more broad + * than the DISABLE_PATHSPEC_MATCH flag. The caller must provide an + * array of paths (no patterns or prefixes). Only values included in + * that list will be returned. + */ + GIT_DIFF_ENABLE_FILELIST_MATCH = (1u << 18), + /* * Options controlling how output will be generated */ diff --git a/src/diff.c b/src/diff.c index 58004db21..d87738fb3 100644 --- a/src/diff.c +++ b/src/diff.c @@ -74,6 +74,24 @@ static int diff_insert_delta( return error; } +static bool diff_pathspec_match( + const char **matched_pathspec, git_diff *diff, const char *path) +{ + /* The iterator has filtered out paths for us, so the fact that we're + * seeing this patch means that it must match the given path list. + */ + if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_ENABLE_FILELIST_MATCH)) { + *matched_pathspec = path; + return true; + } + + return git_pathspec__match( + &diff->pathspec, path, + DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH), + DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE), + matched_pathspec, NULL); +} + static int diff_delta__from_one( git_diff *diff, git_delta_t status, @@ -110,11 +128,7 @@ static int diff_delta__from_one( DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE)) return 0; - if (!git_pathspec__match( - &diff->pathspec, entry->path, - DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH), - DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE), - &matched_pathspec, NULL)) + if (!diff_pathspec_match(&matched_pathspec, diff, entry->path)) return 0; delta = diff_delta__alloc(diff, status, entry->path); @@ -755,11 +769,7 @@ static int maybe_modified( const char *matched_pathspec; int error = 0; - if (!git_pathspec__match( - &diff->pathspec, oitem->path, - DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH), - DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE), - &matched_pathspec, NULL)) + if (!diff_pathspec_match(&matched_pathspec, diff, oitem->path)) return 0; memset(&noid, 0, sizeof(noid)); @@ -1266,7 +1276,9 @@ cleanup: #define DIFF_FROM_ITERATORS(MAKE_FIRST, FLAGS_FIRST, MAKE_SECOND, FLAGS_SECOND) do { \ git_iterator *a = NULL, *b = NULL; \ - char *pfx = opts ? git_pathspec_prefix(&opts->pathspec) : NULL; \ + git_vector pathlist = GIT_VECTOR_INIT; \ + char *pfx = (opts && !(opts->flags & GIT_DIFF_ENABLE_FILELIST_MATCH)) ? \ + git_pathspec_prefix(&opts->pathspec) : NULL; \ git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT, \ b_opts = GIT_ITERATOR_OPTIONS_INIT; \ a_opts.flags = FLAGS_FIRST; \ @@ -1276,9 +1288,19 @@ cleanup: b_opts.start = pfx; \ b_opts.end = pfx; \ GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); \ - if (!(error = MAKE_FIRST) && !(error = MAKE_SECOND)) \ + if (opts && (opts->flags & GIT_DIFF_ENABLE_FILELIST_MATCH) && opts->pathspec.count) { \ + size_t __i; \ + error = git_vector_init(&pathlist, opts->pathspec.count, NULL); \ + for (__i = 0; !error && __i < opts->pathspec.count; __i++) { \ + error = git_vector_insert(&pathlist, opts->pathspec.strings[__i]); \ + } \ + a_opts.pathlist = &pathlist; \ + b_opts.pathlist = &pathlist; \ + } \ + if (!error && !(error = MAKE_FIRST) && !(error = MAKE_SECOND)) \ error = git_diff__from_iterators(diff, repo, a, b, opts); \ git__free(pfx); git_iterator_free(a); git_iterator_free(b); \ + git_vector_free(&pathlist); \ } while (0) int git_diff_tree_to_tree( diff --git a/src/iterator.c b/src/iterator.c index 374caf96d..28629c708 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -42,6 +42,8 @@ (P)->base.flags = options ? options->flags & ~ITERATOR_CASE_FLAGS : 0; \ if ((P)->base.flags & GIT_ITERATOR_DONT_AUTOEXPAND) \ (P)->base.flags |= GIT_ITERATOR_INCLUDE_TREES; \ + if (options && options->pathlist) \ + (P)->base.pathlist = options->pathlist; \ } while (0) #define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0) @@ -106,6 +108,12 @@ static int iterator__update_ignore_case( iter->prefixcomp = iterator__ignore_case(iter) ? git__prefixcmp_icase : git__prefixcmp; + if (iter->pathlist) { + git_vector_set_cmp(iter->pathlist, iterator__ignore_case(iter) ? + git__strcasecmp : git__strcmp); + git_vector_sort(iter->pathlist); + } + return error; } @@ -616,6 +624,9 @@ int git_iterator_for_tree( if (tree == NULL) return git_iterator_for_nothing(iter, options); + /* not yet supported */ + assert (!options || !options->pathlist); + if ((error = git_object_dup((git_object **)&tree, (git_object *)tree)) < 0) return error; @@ -668,15 +679,47 @@ static const git_index_entry *index_iterator__index_entry(index_iterator *ii) return ie; } -static const git_index_entry *index_iterator__advance_over_conflicts(index_iterator *ii) +static const git_index_entry *index_iterator__advance_over_unwanted(index_iterator *ii) { const git_index_entry *ie = index_iterator__index_entry(ii); + const char *p; + int cmp; - if (!iterator__include_conflicts(ii)) { - while (ie && git_index_entry_is_conflict(ie)) { + while (ie) { + if (!iterator__include_conflicts(ii) && + git_index_entry_is_conflict(ie)) { ii->current++; ie = index_iterator__index_entry(ii); + continue; } + + /* if we have a pathlist, this entry's path must be in it to be + * returned. otherwise, advance the pathlist entry or the iterator + * until we find the next path that we want to return. + */ + if (ii->base.pathlist) { + if (ii->base.pathlist_idx >= ii->base.pathlist->length) { + ii->current = SIZE_MAX; + ie = NULL; + break; + } + + p = ii->base.pathlist->contents[ii->base.pathlist_idx]; + cmp = ii->base.pathlist->_cmp(p, ie->path); + + if (cmp < 0) { + ii->base.pathlist_idx++; + continue; + } + + if (cmp > 0) { + ii->current++; + ie = index_iterator__index_entry(ii); + continue; + } + } + + break; } return ie; @@ -705,7 +748,7 @@ static void index_iterator__next_prefix_tree(index_iterator *ii) static int index_iterator__first_prefix_tree(index_iterator *ii) { - const git_index_entry *ie = index_iterator__advance_over_conflicts(ii); + const git_index_entry *ie = index_iterator__advance_over_unwanted(ii); const char *scan, *prior, *slash; if (!ie || !iterator__include_trees(ii)) @@ -818,17 +861,22 @@ static int index_iterator__reset( { index_iterator *ii = (index_iterator *)self; const git_index_entry *ie; + size_t pathlist_idx = 0; if (iterator__reset_range(self, start, end) < 0) return -1; ii->current = 0; + ii->base.pathlist_idx = 0; + /* if we're given a start prefix, find it; if we're given a pathlist, find + * the first of those. start at the later of the two. + */ if (ii->base.start) git_index_snapshot_find( &ii->current, &ii->entries, ii->entry_srch, ii->base.start, 0, 0); - if ((ie = index_iterator__advance_over_conflicts(ii)) == NULL) + if ((ie = index_iterator__advance_over_unwanted(ii)) == NULL) return 0; if (git_buf_sets(&ii->partial, ie->path) < 0) @@ -1004,25 +1052,60 @@ static void fs_iterator__seek_frame_start( ff->index = 0; } +typedef enum { + DIRLOAD_PATHLIST_NONE = 0, + DIRLOAD_PATHLIST_EXACT = 1, + DIRLOAD_PATHLIST_DIRECTORY = 2, +} dirload_pathlist_match_t; + +static dirload_pathlist_match_t dirload_pathlist_match( + git_vector *pathlist, + const char *path, + size_t path_len, + int (*prefixcomp)(const char *a, const char *b)) +{ + const char *matched; + size_t idx; + + if (git_vector_bsearch2( + &idx, pathlist, pathlist->_cmp, path) != GIT_ENOTFOUND) + return DIRLOAD_PATHLIST_EXACT; + + /* the explicit path we searched for was not found, but this may be + * a directory and the pathlist contains a file in it. check. + */ + if ((matched = git_vector_get(pathlist, idx)) != NULL && + prefixcomp(matched, path) == 0 && + matched[path_len] == '/') + return DIRLOAD_PATHLIST_DIRECTORY; + + return DIRLOAD_PATHLIST_NONE; +} + static int dirload_with_stat( + git_vector *contents, const char *dirpath, size_t prefix_len, unsigned int flags, const char *start_stat, const char *end_stat, - git_vector *contents) + git_vector *pathlist) { git_path_diriter diriter = GIT_PATH_DIRITER_INIT; const char *path; int (*strncomp)(const char *a, const char *b, size_t sz); + int (*prefixcomp)(const char *a, const char *b); size_t start_len = start_stat ? strlen(start_stat) : 0; size_t end_len = end_stat ? strlen(end_stat) : 0; fs_iterator_path_with_stat *ps; size_t path_len, cmp_len, ps_size; + dirload_pathlist_match_t pathlist_match = DIRLOAD_PATHLIST_EXACT; int error; strncomp = (flags & GIT_PATH_DIR_IGNORE_CASE) != 0 ? git__strncasecmp : git__strncmp; + prefixcomp = (flags & GIT_PATH_DIR_IGNORE_CASE) != 0 ? + git__prefixcmp_icase : git__prefixcmp; /* Any error here is equivalent to the dir not existing, skip over it */ if ((error = git_path_diriter_init(&diriter, dirpath, flags)) < 0) { @@ -1044,10 +1127,20 @@ static int dirload_with_stat( cmp_len = min(start_len, path_len); if (cmp_len && strncomp(path, start_stat, cmp_len) < 0) continue; + /* skip if after end_stat */ cmp_len = min(end_len, path_len); if (cmp_len && strncomp(path, end_stat, cmp_len) > 0) continue; + /* skip if we have a pathlist and this isn't in it. note that we + * haven't stat'd yet to know if it's a file or a directory, so this + * match for files like `foo` when we're looking for `foo/bar` + */ + if (pathlist && + !(pathlist_match = dirload_pathlist_match( + pathlist, path, path_len, prefixcomp))) + continue; + /* Make sure to append two bytes, one for the path's null * termination, one for a possible trailing '/' for folders. */ @@ -1068,6 +1161,12 @@ static int dirload_with_stat( continue; } + if (pathlist_match == DIRLOAD_PATHLIST_DIRECTORY) { + /* were looking for a directory, but this is a file */ + git__free(ps); + continue; + } + /* Treat the file as unreadable if we get any other error */ memset(&ps->st, 0, sizeof(ps->st)); ps->st.st_mode = GIT_FILEMODE_UNREADABLE; @@ -1113,9 +1212,9 @@ static int fs_iterator__expand_dir(fs_iterator *fi) ff = fs_iterator__alloc_frame(fi); GITERR_CHECK_ALLOC(ff); - error = dirload_with_stat( + error = dirload_with_stat(&ff->entries, fi->path.ptr, fi->root_len, fi->dirload_flags, - fi->base.start, fi->base.end, &ff->entries); + fi->base.start, fi->base.end, fi->base.pathlist); if (error < 0) { git_error_state last_error = { 0 }; diff --git a/src/iterator.h b/src/iterator.h index 46e96f044..0ea2bc053 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -43,6 +43,11 @@ typedef struct { const char *start; const char *end; + /* paths to include in the iterator (literal). any paths not listed + * will be excluded. note that this vector may be resorted! + */ + git_vector *pathlist; + /* flags, from above */ unsigned int flags; } git_iterator_options; @@ -65,6 +70,8 @@ struct git_iterator { git_repository *repo; char *start; char *end; + git_vector *pathlist; + size_t pathlist_idx; int (*prefixcomp)(const char *str, const char *prefix); size_t stat_calls; unsigned int flags; diff --git a/src/merge.c b/src/merge.c index 16cd2aee0..1460a5040 100644 --- a/src/merge.c +++ b/src/merge.c @@ -2356,10 +2356,8 @@ static int merge_check_index(size_t *conflicts, git_repository *repo, git_index goto done; } - opts.pathspec.count = staged_paths.length; - opts.pathspec.strings = (char **)staged_paths.contents; - iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + iter_opts.pathlist = &staged_paths; if ((error = git_iterator_for_index(&iter_repo, index_repo, &iter_opts)) < 0 || (error = git_iterator_for_index(&iter_new, index_new, &iter_opts)) < 0 || @@ -2406,6 +2404,7 @@ static int merge_check_workdir(size_t *conflicts, git_repository *repo, git_inde * will be applied by the merge (including conflicts). Ensure that there * are no changes in the workdir to these paths. */ + opts.flags |= GIT_DIFF_ENABLE_FILELIST_MATCH; opts.pathspec.count = merged_paths->length; opts.pathspec.strings = (char **)merged_paths->contents; diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c index 4d0d12be1..8af7bc533 100644 --- a/tests/repo/iterator.c +++ b/tests/repo/iterator.c @@ -1073,3 +1073,251 @@ void test_repo_iterator__skips_fifos_and_such(void) git_iterator_free(i); #endif } + +void test_repo_iterator__indexfilelist(void) +{ + git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; + git_index *index; + git_vector filelist; + int default_icase; + int expect; + + cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb)); + cl_git_pass(git_vector_insert(&filelist, "a")); + cl_git_pass(git_vector_insert(&filelist, "B")); + cl_git_pass(git_vector_insert(&filelist, "c")); + cl_git_pass(git_vector_insert(&filelist, "D")); + cl_git_pass(git_vector_insert(&filelist, "e")); + cl_git_pass(git_vector_insert(&filelist, "k/1")); + cl_git_pass(git_vector_insert(&filelist, "k/a")); + cl_git_pass(git_vector_insert(&filelist, "L/1")); + + g_repo = cl_git_sandbox_init("icase"); + + cl_git_pass(git_repository_index(&index, g_repo)); + /* In this test we DO NOT force a case setting on the index. */ + default_icase = ((git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0); + + i_opts.pathlist = &filelist; + + /* All indexfilelist iterator tests are "autoexpand with no tree entries" */ + + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + expect_iterator_items(i, 8, NULL, 8, NULL); + git_iterator_free(i); + + i_opts.start = "c"; + i_opts.end = NULL; + + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + /* (c D e k/1 k/a L ==> 6) vs (c e k/1 k/a ==> 4) */ + expect = ((default_icase) ? 6 : 4); + expect_iterator_items(i, expect, NULL, expect, NULL); + git_iterator_free(i); + + i_opts.start = NULL; + i_opts.end = "e"; + + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + /* (a B c D e ==> 5) vs (B D L/1 a c e ==> 6) */ + expect = ((default_icase) ? 5 : 6); + expect_iterator_items(i, expect, NULL, expect, NULL); + git_iterator_free(i); + + git_index_free(index); + git_vector_free(&filelist); +} + +void test_repo_iterator__indexfilelist_2(void) +{ + git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; + git_index *index; + git_vector filelist = GIT_VECTOR_INIT; + + g_repo = cl_git_sandbox_init("icase"); + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb)); + cl_git_pass(git_vector_insert(&filelist, "0")); + cl_git_pass(git_vector_insert(&filelist, "c")); + cl_git_pass(git_vector_insert(&filelist, "D")); + cl_git_pass(git_vector_insert(&filelist, "e")); + cl_git_pass(git_vector_insert(&filelist, "k/a")); + + i_opts.pathlist = &filelist; + + i_opts.start = "b"; + i_opts.end = "k/D"; + + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + expect_iterator_items(i, 4, NULL, 4, NULL); + git_iterator_free(i); + + git_index_free(index); + git_vector_free(&filelist); +} + +void test_repo_iterator__indexfilelist_icase(void) +{ + git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; + git_index *index; + int caps; + git_vector filelist; + + cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb)); + cl_git_pass(git_vector_insert(&filelist, "a")); + cl_git_pass(git_vector_insert(&filelist, "B")); + cl_git_pass(git_vector_insert(&filelist, "c")); + cl_git_pass(git_vector_insert(&filelist, "D")); + cl_git_pass(git_vector_insert(&filelist, "e")); + cl_git_pass(git_vector_insert(&filelist, "k/1")); + cl_git_pass(git_vector_insert(&filelist, "k/a")); + cl_git_pass(git_vector_insert(&filelist, "L/1")); + + g_repo = cl_git_sandbox_init("icase"); + + cl_git_pass(git_repository_index(&index, g_repo)); + caps = git_index_caps(index); + + /* force case sensitivity */ + cl_git_pass(git_index_set_caps(index, caps & ~GIT_INDEXCAP_IGNORE_CASE)); + + /* All indexfilelist iterator tests are "autoexpand with no tree entries" */ + + i_opts.pathlist = &filelist; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + expect_iterator_items(i, 3, NULL, 3, NULL); + git_iterator_free(i); + + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + expect_iterator_items(i, 1, NULL, 1, NULL); + git_iterator_free(i); + + /* force case insensitivity */ + cl_git_pass(git_index_set_caps(index, caps | GIT_INDEXCAP_IGNORE_CASE)); + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + expect_iterator_items(i, 5, NULL, 5, NULL); + git_iterator_free(i); + + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + expect_iterator_items(i, 2, NULL, 2, NULL); + git_iterator_free(i); + + cl_git_pass(git_index_set_caps(index, caps)); + git_index_free(index); + git_vector_free(&filelist); +} + +void test_repo_iterator__workdirfilelist(void) +{ + git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; + git_vector filelist; + bool default_icase; + int expect; + + cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb)); + cl_git_pass(git_vector_insert(&filelist, "a")); + cl_git_pass(git_vector_insert(&filelist, "B")); + cl_git_pass(git_vector_insert(&filelist, "c")); + cl_git_pass(git_vector_insert(&filelist, "D")); + cl_git_pass(git_vector_insert(&filelist, "e")); + cl_git_pass(git_vector_insert(&filelist, "k/1")); + cl_git_pass(git_vector_insert(&filelist, "k/a")); + cl_git_pass(git_vector_insert(&filelist, "L/1")); + + g_repo = cl_git_sandbox_init("icase"); + + /* All indexfilelist iterator tests are "autoexpand with no tree entries" */ + /* In this test we DO NOT force a case on the iteratords and verify default behavior. */ + + i_opts.pathlist = &filelist; + + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); + expect_iterator_items(i, 8, NULL, 8, NULL); + git_iterator_free(i); + + i_opts.start = "c"; + i_opts.end = NULL; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); + default_icase = git_iterator_ignore_case(i); + /* (c D e k/1 k/a L ==> 6) vs (c e k/1 k/a ==> 4) */ + expect = ((default_icase) ? 6 : 4); + expect_iterator_items(i, expect, NULL, expect, NULL); + git_iterator_free(i); + + i_opts.start = NULL; + i_opts.end = "e"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); + default_icase = git_iterator_ignore_case(i); + /* (a B c D e ==> 5) vs (B D L/1 a c e ==> 6) */ + expect = ((default_icase) ? 5 : 6); + expect_iterator_items(i, expect, NULL, expect, NULL); + git_iterator_free(i); + + git_vector_free(&filelist); +} + +void test_repo_iterator__workdirfilelist_icase(void) +{ + git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; + git_vector filelist; + + cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb)); + cl_git_pass(git_vector_insert(&filelist, "a")); + cl_git_pass(git_vector_insert(&filelist, "B")); + cl_git_pass(git_vector_insert(&filelist, "c")); + cl_git_pass(git_vector_insert(&filelist, "D")); + cl_git_pass(git_vector_insert(&filelist, "e")); + cl_git_pass(git_vector_insert(&filelist, "k/1")); + cl_git_pass(git_vector_insert(&filelist, "k/a")); + cl_git_pass(git_vector_insert(&filelist, "L/1")); + + g_repo = cl_git_sandbox_init("icase"); + + i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + i_opts.pathlist = &filelist; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); + expect_iterator_items(i, 3, NULL, 3, NULL); + git_iterator_free(i); + + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); + expect_iterator_items(i, 1, NULL, 1, NULL); + git_iterator_free(i); + + i_opts.flags = GIT_ITERATOR_IGNORE_CASE; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); + expect_iterator_items(i, 5, NULL, 5, NULL); + git_iterator_free(i); + + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); + expect_iterator_items(i, 2, NULL, 2, NULL); + git_iterator_free(i); + + git_vector_free(&filelist); +} From 810cabb9dfa57127e78cb47dbe8ea95943fb2642 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 28 Jul 2015 20:04:11 -0500 Subject: [PATCH 084/450] racy-git: TODO to use improved diffing --- src/index.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.c b/src/index.c index a6a62e327..53120e49c 100644 --- a/src/index.c +++ b/src/index.c @@ -728,6 +728,7 @@ static int truncate_racily_clean(git_index *index) if (!is_racy_timestamp(ts, entry)) continue; + /* TODO: use the (non-fnmatching) filelist iterator */ diff_opts.pathspec.count = 1; diff_opts.pathspec.strings = (char **) &entry->path; From 6c9352bf30e97af5d646d92ceab1c7b0f4c7a1c4 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 28 Aug 2015 18:30:39 -0400 Subject: [PATCH 085/450] iterator: sort subdirs properly with pathlist When given a pathlist, don't assume that directories sort before files. Walk through any list of entries sorting before us to make sure that we've exhausted all entries that *aren't* directories. Eg, if we're searching for 'foo/bar', and we have a 'foo.c', keep advancing the pathlist to keep looking for an entry prefixed with 'foo/'. --- src/iterator.c | 21 +++++++++++++++------ tests/repo/iterator.c | 8 +++++++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/iterator.c b/src/iterator.c index 28629c708..9fa7cab2c 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -1071,13 +1071,22 @@ static dirload_pathlist_match_t dirload_pathlist_match( &idx, pathlist, pathlist->_cmp, path) != GIT_ENOTFOUND) return DIRLOAD_PATHLIST_EXACT; - /* the explicit path we searched for was not found, but this may be - * a directory and the pathlist contains a file in it. check. + /* the explicit path that we've seen in the directory iterator was + * not found - however, we may have hit a subdirectory in the directory + * iterator. examine the pathlist to see if it contains children of the + * current path. if so, indicate that we've found a subdirectory that + * is worth examining. */ - if ((matched = git_vector_get(pathlist, idx)) != NULL && - prefixcomp(matched, path) == 0 && - matched[path_len] == '/') - return DIRLOAD_PATHLIST_DIRECTORY; + while ((matched = git_vector_get(pathlist, idx)) != NULL && + prefixcomp(matched, path) == 0) { + + if (matched[path_len] == '/') + return DIRLOAD_PATHLIST_DIRECTORY; + else if (matched[path_len] > '/') + break; + + idx++; + } return DIRLOAD_PATHLIST_NONE; } diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c index 8af7bc533..84dfbe113 100644 --- a/tests/repo/iterator.c +++ b/tests/repo/iterator.c @@ -26,7 +26,7 @@ static void expect_iterator_items( const git_index_entry *entry; int count, error; int no_trees = !(git_iterator_flags(i) & GIT_ITERATOR_INCLUDE_TREES); - bool v = false; + bool v = true; if (expected_flat < 0) { v = true; expected_flat = -expected_flat; } if (expected_total < 0) { v = true; expected_total = -expected_total; } @@ -1236,8 +1236,11 @@ void test_repo_iterator__workdirfilelist(void) cl_git_pass(git_vector_insert(&filelist, "c")); cl_git_pass(git_vector_insert(&filelist, "D")); cl_git_pass(git_vector_insert(&filelist, "e")); + cl_git_pass(git_vector_insert(&filelist, "k.a")); + cl_git_pass(git_vector_insert(&filelist, "k.b")); cl_git_pass(git_vector_insert(&filelist, "k/1")); cl_git_pass(git_vector_insert(&filelist, "k/a")); + cl_git_pass(git_vector_insert(&filelist, "kZZZZZZZ")); cl_git_pass(git_vector_insert(&filelist, "L/1")); g_repo = cl_git_sandbox_init("icase"); @@ -1284,8 +1287,11 @@ void test_repo_iterator__workdirfilelist_icase(void) cl_git_pass(git_vector_insert(&filelist, "c")); cl_git_pass(git_vector_insert(&filelist, "D")); cl_git_pass(git_vector_insert(&filelist, "e")); + cl_git_pass(git_vector_insert(&filelist, "k.a")); + cl_git_pass(git_vector_insert(&filelist, "k.b")); cl_git_pass(git_vector_insert(&filelist, "k/1")); cl_git_pass(git_vector_insert(&filelist, "k/a")); + cl_git_pass(git_vector_insert(&filelist, "kZZZZ")); cl_git_pass(git_vector_insert(&filelist, "L/1")); g_repo = cl_git_sandbox_init("icase"); From 3273ab3f0b04d673b9515b149674d5716939d9a5 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 28 Aug 2015 20:06:18 -0400 Subject: [PATCH 086/450] diff: better document GIT_DIFF_PATHSPEC_DISABLE Document that `GIT_DIFF_PATHSPEC_DISABLE` is not necessarily about explicit path matching, but also includes matching of directory names. Enforce this in a test. --- include/git2/diff.h | 4 +- tests/diff/workdir.c | 210 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 213 insertions(+), 1 deletion(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index c3589bb13..d3adf9f01 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -130,7 +130,9 @@ typedef enum { GIT_DIFF_INCLUDE_CASECHANGE = (1u << 11), /** If the pathspec is set in the diff options, this flags means to - * apply it as an exact match instead of as an fnmatch pattern. + * use exact prefix matches instead of an fnmatch pattern. Each + * path in the list must either be a full filename or a subdirectory + * prefix. */ GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1u << 12), diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c index 8a23f53ae..503d67450 100644 --- a/tests/diff/workdir.c +++ b/tests/diff/workdir.c @@ -444,6 +444,216 @@ void test_diff_workdir__to_index_with_pathspec(void) git_diff_free(diff); } +void test_diff_workdir__to_index_with_pathlist_disabling_fnmatch(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff *diff = NULL; + diff_expects exp; + char *pathspec = NULL; + int use_iterator; + + g_repo = cl_git_sandbox_init("status"); + + opts.context_lines = 3; + opts.interhunk_lines = 1; + opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED | + GIT_DIFF_DISABLE_PATHSPEC_MATCH; + opts.pathspec.strings = &pathspec; + opts.pathspec.count = 0; + + /* ensure that an empty pathspec list is ignored */ + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, diff_file_cb, NULL, NULL, NULL, &exp)); + else + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); + + cl_assert_equal_i(13, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]); + } + + git_diff_free(diff); + + /* ensure that a single NULL pathspec is filtered out (like when using + * fnmatch filtering) + */ + opts.pathspec.strings = &pathspec; + opts.pathspec.count = 1; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, diff_file_cb, NULL, NULL, NULL, &exp)); + else + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); + + cl_assert_equal_i(13, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]); + } + + git_diff_free(diff); + + pathspec = "modified_file"; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, diff_file_cb, NULL, NULL, NULL, &exp)); + else + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); + + cl_assert_equal_i(1, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]); + } + + git_diff_free(diff); + + /* ensure that subdirs can be specified */ + pathspec = "subdir"; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, diff_file_cb, NULL, NULL, NULL, &exp)); + else + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); + + cl_assert_equal_i(3, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]); + } + + git_diff_free(diff); + + /* ensure that subdirs can be specified with a trailing slash */ + pathspec = "subdir/"; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, diff_file_cb, NULL, NULL, NULL, &exp)); + else + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); + + cl_assert_equal_i(3, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]); + } + + git_diff_free(diff); + + /* ensure that fnmatching is completely disabled */ + pathspec = "subdir/*"; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, diff_file_cb, NULL, NULL, NULL, &exp)); + else + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); + + cl_assert_equal_i(0, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]); + } + + git_diff_free(diff); + + /* ensure that the prefix matching isn't completely braindead */ + pathspec = "subdi"; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, diff_file_cb, NULL, NULL, NULL, &exp)); + else + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); + + cl_assert_equal_i(0, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]); + } + + git_diff_free(diff); + + /* ensure that fnmatching isn't working at all */ + pathspec = "*_deleted"; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, diff_file_cb, NULL, NULL, NULL, &exp)); + else + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); + + cl_assert_equal_i(0, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]); + } + + git_diff_free(diff); +} + void test_diff_workdir__filemode_changes(void) { git_diff *diff = NULL; From 91c9484c2302aedd38e83b1343cbefeb1685223d Mon Sep 17 00:00:00 2001 From: Ben Chatelain Date: Sat, 29 Aug 2015 17:46:34 -0600 Subject: [PATCH 087/450] Escape @ in doc comment --- include/git2/cred_helpers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/git2/cred_helpers.h b/include/git2/cred_helpers.h index 840c22536..1416d5642 100644 --- a/include/git2/cred_helpers.h +++ b/include/git2/cred_helpers.h @@ -34,7 +34,7 @@ typedef struct git_cred_userpass_payload { * * @param cred The newly created credential object. * @param url The resource for which we are demanding a credential. - * @param user_from_url The username that was embedded in a "user@host" + * @param user_from_url The username that was embedded in a "user\@host" * remote url, or NULL if not included. * @param allowed_types A bitmask stating which cred types are OK to return. * @param payload The payload provided when specifying this callback. (This is From 4a0dbeb0d35343ded24b51906f2a8f8ef6c7910b Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 30 Aug 2015 17:06:26 -0400 Subject: [PATCH 088/450] diff: use new iterator pathlist handling When using literal pathspecs in diff with `GIT_DIFF_DISABLE_PATHSPEC_MATCH` turn on the faster iterator pathlist handling. Updates iterator pathspecs to include directory prefixes (eg, `foo/`) for compatibility with `GIT_DIFF_DISABLE_PATHSPEC_MATCH`. --- src/diff.c | 28 ++-- src/iterator.c | 292 +++++++++++++++++++++++++++--------------- src/iterator.h | 15 ++- src/merge.c | 3 +- tests/diff/workdir.c | 26 +++- tests/repo/iterator.c | 17 ++- 6 files changed, 246 insertions(+), 135 deletions(-) diff --git a/src/diff.c b/src/diff.c index d87738fb3..32c1d4aa5 100644 --- a/src/diff.c +++ b/src/diff.c @@ -80,14 +80,13 @@ static bool diff_pathspec_match( /* The iterator has filtered out paths for us, so the fact that we're * seeing this patch means that it must match the given path list. */ - if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_ENABLE_FILELIST_MATCH)) { + if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH)) { *matched_pathspec = path; return true; } return git_pathspec__match( - &diff->pathspec, path, - DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH), + &diff->pathspec, path, false, DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE), matched_pathspec, NULL); } @@ -1063,6 +1062,12 @@ static int handle_unmatched_new_item( &info->nitem, &untracked_state, info->new_iter)) < 0) return error; + /* if we found nothing that matched our pathlist filter, exclude */ + if (untracked_state == GIT_ITERATOR_STATUS_FILTERED) { + git_vector_pop(&diff->deltas); + git__free(last); + } + /* if we found nothing or just ignored items, update the record */ if (untracked_state == GIT_ITERATOR_STATUS_IGNORED || untracked_state == GIT_ITERATOR_STATUS_EMPTY) { @@ -1276,8 +1281,7 @@ cleanup: #define DIFF_FROM_ITERATORS(MAKE_FIRST, FLAGS_FIRST, MAKE_SECOND, FLAGS_SECOND) do { \ git_iterator *a = NULL, *b = NULL; \ - git_vector pathlist = GIT_VECTOR_INIT; \ - char *pfx = (opts && !(opts->flags & GIT_DIFF_ENABLE_FILELIST_MATCH)) ? \ + char *pfx = (opts && !(opts->flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH)) ? \ git_pathspec_prefix(&opts->pathspec) : NULL; \ git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT, \ b_opts = GIT_ITERATOR_OPTIONS_INIT; \ @@ -1288,19 +1292,15 @@ cleanup: b_opts.start = pfx; \ b_opts.end = pfx; \ GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); \ - if (opts && (opts->flags & GIT_DIFF_ENABLE_FILELIST_MATCH) && opts->pathspec.count) { \ - size_t __i; \ - error = git_vector_init(&pathlist, opts->pathspec.count, NULL); \ - for (__i = 0; !error && __i < opts->pathspec.count; __i++) { \ - error = git_vector_insert(&pathlist, opts->pathspec.strings[__i]); \ - } \ - a_opts.pathlist = &pathlist; \ - b_opts.pathlist = &pathlist; \ + if (opts && (opts->flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH)) { \ + a_opts.pathlist.strings = opts->pathspec.strings; \ + a_opts.pathlist.count = opts->pathspec.count; \ + b_opts.pathlist.strings = opts->pathspec.strings; \ + b_opts.pathlist.count = opts->pathspec.count; \ } \ if (!error && !(error = MAKE_FIRST) && !(error = MAKE_SECOND)) \ error = git_diff__from_iterators(diff, repo, a, b, opts); \ git__free(pfx); git_iterator_free(a); git_iterator_free(b); \ - git_vector_free(&pathlist); \ } while (0) int git_diff_tree_to_tree( diff --git a/src/iterator.c b/src/iterator.c index 9fa7cab2c..bad24d13e 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -38,12 +38,15 @@ if ((options && options->start && !(P)->base.start) || \ (options && options->end && !(P)->base.end)) { \ git__free(P); return -1; } \ + (P)->base.strcomp = git__strcmp; \ + (P)->base.strncomp = git__strncmp; \ (P)->base.prefixcomp = git__prefixcmp; \ (P)->base.flags = options ? options->flags & ~ITERATOR_CASE_FLAGS : 0; \ if ((P)->base.flags & GIT_ITERATOR_DONT_AUTOEXPAND) \ (P)->base.flags |= GIT_ITERATOR_INCLUDE_TREES; \ - if (options && options->pathlist) \ - (P)->base.pathlist = options->pathlist; \ + if (options && options->pathlist.count && \ + iterator_pathlist__init(&P->base, &options->pathlist) < 0) { \ + git__free(P); return -1; } \ } while (0) #define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0) @@ -61,6 +64,82 @@ (iterator__end(I) && ((git_iterator *)(I))->prefixcomp((PATH),iterator__end(I)) > 0) +typedef enum { + ITERATOR_PATHLIST_NONE = 0, + ITERATOR_PATHLIST_MATCH = 1, + ITERATOR_PATHLIST_MATCH_DIRECTORY = 2, + ITERATOR_PATHLIST_MATCH_CHILD = 3, +} iterator_pathlist__match_t; + +static int iterator_pathlist__init(git_iterator *iter, git_strarray *pathspec) +{ + size_t i; + + if (git_vector_init(&iter->pathlist, pathspec->count, iter->strcomp) < 0) + return -1; + + for (i = 0; i < pathspec->count; i++) { + if (!pathspec->strings[i]) + continue; + + if (git_vector_insert(&iter->pathlist, pathspec->strings[i]) < 0) + return -1; + } + + git_vector_sort(&iter->pathlist); + + return 0; +} + +static iterator_pathlist__match_t iterator_pathlist__match( + git_iterator *iter, const char *path, size_t path_len) +{ + const char *p; + size_t idx; + int error; + + error = git_vector_bsearch2(&idx, &iter->pathlist, iter->strcomp, path); + + if (error == 0) + return ITERATOR_PATHLIST_MATCH; + + /* at this point, the path we're examining may be a directory (though we + * don't know that yet, since we're avoiding a stat unless it's necessary) + * so see if the pathlist contains a file beneath this directory. + */ + while ((p = git_vector_get(&iter->pathlist, idx)) != NULL) { + if (iter->prefixcomp(p, path) != 0) + break; + + /* an exact match would have been matched by the bsearch above */ + assert(p[path_len]); + + /* is this a literal directory entry (eg `foo/`) or a file beneath */ + if (p[path_len] == '/') { + while (p[path_len] == '/') + path_len++; + + return (p[path_len] == '\0') ? + ITERATOR_PATHLIST_MATCH_DIRECTORY : + ITERATOR_PATHLIST_MATCH_CHILD; + } + + if (p[path_len] > '/') + break; + + idx++; + } + + return ITERATOR_PATHLIST_NONE; +} + +static void iterator_pathlist__update_ignore_case(git_iterator *iter) +{ + git_vector_set_cmp(&iter->pathlist, iter->strcomp); + git_vector_sort(&iter->pathlist); +} + + static int iterator__reset_range( git_iterator *iter, const char *start, const char *end) { @@ -87,7 +166,8 @@ static int iterator__update_ignore_case( git_iterator *iter, git_iterator_flag_t flags) { - int error = 0, ignore_case = -1; + bool ignore_case; + int error; if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0) ignore_case = true; @@ -96,25 +176,29 @@ static int iterator__update_ignore_case( else { git_index *index; - if (!(error = git_repository_index__weakptr(&index, iter->repo))) - ignore_case = (index->ignore_case != false); + if ((error = git_repository_index__weakptr(&index, iter->repo)) < 0) + return error; + + ignore_case = (index->ignore_case == 1); } - if (ignore_case > 0) + if (ignore_case) { iter->flags = (iter->flags | GIT_ITERATOR_IGNORE_CASE); - else if (ignore_case == 0) + + iter->strcomp = git__strcasecmp; + iter->strncomp = git__strncasecmp; + iter->prefixcomp = git__prefixcmp_icase; + } else { iter->flags = (iter->flags & ~GIT_ITERATOR_IGNORE_CASE); - iter->prefixcomp = iterator__ignore_case(iter) ? - git__prefixcmp_icase : git__prefixcmp; - - if (iter->pathlist) { - git_vector_set_cmp(iter->pathlist, iterator__ignore_case(iter) ? - git__strcasecmp : git__strcmp); - git_vector_sort(iter->pathlist); + iter->strcomp = git__strcmp; + iter->strncomp = git__strncmp; + iter->prefixcomp = git__prefixcmp; } - return error; + iterator_pathlist__update_ignore_case(iter); + + return 0; } GIT_INLINE(void) iterator__clear_entry(const git_index_entry **entry) @@ -210,7 +294,6 @@ typedef struct { int path_ambiguities; bool path_has_filename; bool entry_is_current; - int (*strncomp)(const char *a, const char *b, size_t sz); } tree_iterator; static char *tree_iterator__current_filename( @@ -280,7 +363,7 @@ static int tree_iterator__search_cmp(const void *key, const void *val, void *p) return git_path_cmp( tf->start, tf->startlen, false, te->filename, te->filename_len, te->attr == GIT_FILEMODE_TREE, - ((tree_iterator *)p)->strncomp); + ((git_iterator *)p)->strncomp); } static bool tree_iterator__move_to_next( @@ -312,7 +395,7 @@ static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf) for (; tf->next < tf->n_entries; tf->next++, last = te) { te = tf->entries[tf->next]->te; - if (last && tree_iterator__te_cmp(last, te, ti->strncomp)) + if (last && tree_iterator__te_cmp(last, te, ti->base.strncomp)) break; /* try to load trees for items in [current,next) range */ @@ -624,9 +707,6 @@ int git_iterator_for_tree( if (tree == NULL) return git_iterator_for_nothing(iter, options); - /* not yet supported */ - assert (!options || !options->pathlist); - if ((error = git_object_dup((git_object **)&tree, (git_object *)tree)) < 0) return error; @@ -637,7 +717,6 @@ int git_iterator_for_tree( if ((error = iterator__update_ignore_case((git_iterator *)ti, options ? options->flags : 0)) < 0) goto fail; - ti->strncomp = iterator__ignore_case(ti) ? git__strncasecmp : git__strncmp; if ((error = git_pool_init(&ti->pool, sizeof(tree_iterator_entry),0)) < 0 || (error = tree_iterator__create_root_frame(ti, tree)) < 0 || @@ -660,6 +739,8 @@ typedef struct { git_vector entries; git_vector_cmp entry_srch; size_t current; + /* when limiting with a pathlist, this is the current index into it */ + size_t pathlist_idx; /* when not in autoexpand mode, use these to represent "tree" state */ git_buf partial; size_t partial_pos; @@ -679,10 +760,12 @@ static const git_index_entry *index_iterator__index_entry(index_iterator *ii) return ie; } -static const git_index_entry *index_iterator__advance_over_unwanted(index_iterator *ii) +static const git_index_entry *index_iterator__advance_over_unwanted( + index_iterator *ii) { const git_index_entry *ie = index_iterator__index_entry(ii); const char *p; + size_t p_len; int cmp; while (ie) { @@ -697,24 +780,48 @@ static const git_index_entry *index_iterator__advance_over_unwanted(index_iterat * returned. otherwise, advance the pathlist entry or the iterator * until we find the next path that we want to return. */ - if (ii->base.pathlist) { - if (ii->base.pathlist_idx >= ii->base.pathlist->length) { + if (ii->base.pathlist.length) { + + if (ii->pathlist_idx >= ii->base.pathlist.length) { ii->current = SIZE_MAX; ie = NULL; break; } - p = ii->base.pathlist->contents[ii->base.pathlist_idx]; - cmp = ii->base.pathlist->_cmp(p, ie->path); + p = git_vector_get(&ii->base.pathlist, ii->pathlist_idx); + + /* trim trailing slashes that indicate an exact directory match */ + p_len = strlen(p); + + while (p_len && p[p_len-1] == '/') + p_len--; + + cmp = ii->base.strncomp(ie->path, p, p_len); + + /* we've matched the prefix - if the pathlist entry is equal to + * this entry, or if the pathlist entry is a folder (eg `foo/`) + * and this entry was beneath that, then continue. otherwise, + * sort the index entry path against the pathlist entry. + */ + if (cmp == 0) { + if (ie->path[p_len] == 0) + ; + else if (ie->path[p_len] == '/') + ; + else if (ie->path[p_len] < '/') + cmp = -1; + else if (ie->path[p_len] > '/') + cmp = 1; + } if (cmp < 0) { - ii->base.pathlist_idx++; + ii->current++; + ie = index_iterator__index_entry(ii); continue; } if (cmp > 0) { - ii->current++; - ie = index_iterator__index_entry(ii); + ii->pathlist_idx++; continue; } } @@ -861,13 +968,12 @@ static int index_iterator__reset( { index_iterator *ii = (index_iterator *)self; const git_index_entry *ie; - size_t pathlist_idx = 0; if (iterator__reset_range(self, start, end) < 0) return -1; ii->current = 0; - ii->base.pathlist_idx = 0; + ii->pathlist_idx = 0; /* if we're given a start prefix, find it; if we're given a pathlist, find * the first of those. start at the later of the two. @@ -961,6 +1067,7 @@ struct fs_iterator { size_t root_len; uint32_t dirload_flags; int depth; + iterator_pathlist__match_t pathlist_match; int (*enter_dir_cb)(fs_iterator *self); int (*leave_dir_cb)(fs_iterator *self); @@ -971,6 +1078,7 @@ struct fs_iterator { typedef struct { struct stat st; + iterator_pathlist__match_t pathlist_match; size_t path_len; char path[GIT_FLEX_ARRAY]; } fs_iterator_path_with_stat; @@ -1052,72 +1160,22 @@ static void fs_iterator__seek_frame_start( ff->index = 0; } -typedef enum { - DIRLOAD_PATHLIST_NONE = 0, - DIRLOAD_PATHLIST_EXACT = 1, - DIRLOAD_PATHLIST_DIRECTORY = 2, -} dirload_pathlist_match_t; - -static dirload_pathlist_match_t dirload_pathlist_match( - git_vector *pathlist, - const char *path, - size_t path_len, - int (*prefixcomp)(const char *a, const char *b)) -{ - const char *matched; - size_t idx; - - if (git_vector_bsearch2( - &idx, pathlist, pathlist->_cmp, path) != GIT_ENOTFOUND) - return DIRLOAD_PATHLIST_EXACT; - - /* the explicit path that we've seen in the directory iterator was - * not found - however, we may have hit a subdirectory in the directory - * iterator. examine the pathlist to see if it contains children of the - * current path. if so, indicate that we've found a subdirectory that - * is worth examining. - */ - while ((matched = git_vector_get(pathlist, idx)) != NULL && - prefixcomp(matched, path) == 0) { - - if (matched[path_len] == '/') - return DIRLOAD_PATHLIST_DIRECTORY; - else if (matched[path_len] > '/') - break; - - idx++; - } - - return DIRLOAD_PATHLIST_NONE; -} - -static int dirload_with_stat( - git_vector *contents, - const char *dirpath, - size_t prefix_len, - unsigned int flags, - const char *start_stat, - const char *end_stat, - git_vector *pathlist) +static int dirload_with_stat(git_vector *contents, size_t *filtered, fs_iterator *fi) { git_path_diriter diriter = GIT_PATH_DIRITER_INIT; const char *path; - int (*strncomp)(const char *a, const char *b, size_t sz); - int (*prefixcomp)(const char *a, const char *b); - size_t start_len = start_stat ? strlen(start_stat) : 0; - size_t end_len = end_stat ? strlen(end_stat) : 0; + size_t start_len = fi->base.start ? strlen(fi->base.start) : 0; + size_t end_len = fi->base.end ? strlen(fi->base.end) : 0; fs_iterator_path_with_stat *ps; size_t path_len, cmp_len, ps_size; - dirload_pathlist_match_t pathlist_match = DIRLOAD_PATHLIST_EXACT; + iterator_pathlist__match_t pathlist_match = ITERATOR_PATHLIST_MATCH; int error; - strncomp = (flags & GIT_PATH_DIR_IGNORE_CASE) != 0 ? - git__strncasecmp : git__strncmp; - prefixcomp = (flags & GIT_PATH_DIR_IGNORE_CASE) != 0 ? - git__prefixcmp_icase : git__prefixcmp; + *filtered = 0; /* Any error here is equivalent to the dir not existing, skip over it */ - if ((error = git_path_diriter_init(&diriter, dirpath, flags)) < 0) { + if ((error = git_path_diriter_init( + &diriter, fi->path.ptr, fi->dirload_flags)) < 0) { error = GIT_ENOTFOUND; goto done; } @@ -1126,29 +1184,35 @@ static int dirload_with_stat( if ((error = git_path_diriter_fullpath(&path, &path_len, &diriter)) < 0) goto done; - assert(path_len > prefix_len); + assert(path_len > fi->root_len); /* remove the prefix if requested */ - path += prefix_len; - path_len -= prefix_len; + path += fi->root_len; + path_len -= fi->root_len; /* skip if before start_stat or after end_stat */ cmp_len = min(start_len, path_len); - if (cmp_len && strncomp(path, start_stat, cmp_len) < 0) + if (cmp_len && fi->base.strncomp(path, fi->base.start, cmp_len) < 0) continue; /* skip if after end_stat */ cmp_len = min(end_len, path_len); - if (cmp_len && strncomp(path, end_stat, cmp_len) > 0) + if (cmp_len && fi->base.strncomp(path, fi->base.end, cmp_len) > 0) continue; - /* skip if we have a pathlist and this isn't in it. note that we - * haven't stat'd yet to know if it's a file or a directory, so this - * match for files like `foo` when we're looking for `foo/bar` + /* if we have a pathlist that we're limiting to, examine this path. + * if the frame has already deemed us inside the path (eg, we're in + * `foo/bar` and the pathlist previously was detected to say `foo/`) + * then simply continue. otherwise, examine the pathlist looking for + * this path or children of this path. */ - if (pathlist && - !(pathlist_match = dirload_pathlist_match( - pathlist, path, path_len, prefixcomp))) + if (fi->base.pathlist.length && + fi->pathlist_match != ITERATOR_PATHLIST_MATCH && + fi->pathlist_match != ITERATOR_PATHLIST_MATCH_DIRECTORY && + !(pathlist_match = iterator_pathlist__match(&fi->base, path, path_len))) { + + *filtered++; continue; + } /* Make sure to append two bytes, one for the path's null * termination, one for a possible trailing '/' for folders. @@ -1170,7 +1234,7 @@ static int dirload_with_stat( continue; } - if (pathlist_match == DIRLOAD_PATHLIST_DIRECTORY) { + if (pathlist_match == ITERATOR_PATHLIST_MATCH_DIRECTORY) { /* were looking for a directory, but this is a file */ git__free(ps); continue; @@ -1192,6 +1256,11 @@ static int dirload_with_stat( continue; } + /* record whether this path was explicitly found in the path list + * or whether we're only examining it because something beneath it + * is in the path list. + */ + ps->pathlist_match = pathlist_match; git_vector_insert(contents, ps); } @@ -1211,6 +1280,7 @@ static int fs_iterator__expand_dir(fs_iterator *fi) { int error; fs_iterator_frame *ff; + size_t filtered = 0; if (fi->depth > FS_MAX_DEPTH) { giterr_set(GITERR_REPOSITORY, @@ -1221,9 +1291,7 @@ static int fs_iterator__expand_dir(fs_iterator *fi) ff = fs_iterator__alloc_frame(fi); GITERR_CHECK_ALLOC(ff); - error = dirload_with_stat(&ff->entries, - fi->path.ptr, fi->root_len, fi->dirload_flags, - fi->base.start, fi->base.end, fi->base.pathlist); + error = dirload_with_stat(&ff->entries, &filtered, fi); if (error < 0) { git_error_state last_error = { 0 }; @@ -1418,6 +1486,7 @@ static int fs_iterator__update_entry(fs_iterator *fi) return GIT_ITEROVER; fi->entry.path = ps->path; + fi->pathlist_match = ps->pathlist_match; git_index_entry__init_from_stat(&fi->entry, &ps->st, true); /* need different mode here to keep directories during iteration */ @@ -1450,6 +1519,7 @@ static int fs_iterator__initialize( return -1; } fi->root_len = fi->path.size; + fi->pathlist_match = ITERATOR_PATHLIST_MATCH_CHILD; fi->dirload_flags = (iterator__ignore_case(fi) ? GIT_PATH_DIR_IGNORE_CASE : 0) | @@ -1721,6 +1791,7 @@ void git_iterator_free(git_iterator *iter) iter->cb->free(iter); + git_vector_free(&iter->pathlist); git__free(iter->start); git__free(iter->end); @@ -1790,7 +1861,7 @@ int git_iterator_current_parent_tree( if (!(tf = tf->down) || tf->current >= tf->n_entries || !(te = tf->entries[tf->current]->te) || - ti->strncomp(scan, te->filename, te->filename_len) != 0) + ti->base.strncomp(scan, te->filename, te->filename_len) != 0) return 0; scan += te->filename_len; @@ -1923,9 +1994,18 @@ int git_iterator_advance_over_with_status( if (!error) continue; + else if (error == GIT_ENOTFOUND) { + /* we entered this directory only hoping to find child matches to + * our pathlist (eg, this is `foo` and we had a pathlist entry for + * `foo/bar`). it should not be ignored, it should be excluded. + */ + if (wi->fi.pathlist_match == ITERATOR_PATHLIST_MATCH_CHILD) + *status = GIT_ITERATOR_STATUS_FILTERED; + else + wi->is_ignored = GIT_IGNORE_TRUE; /* mark empty dirs ignored */ + error = 0; - wi->is_ignored = GIT_IGNORE_TRUE; /* mark empty dirs ignored */ } else break; /* real error, stop here */ } else { diff --git a/src/iterator.h b/src/iterator.h index 0ea2bc053..d2d61fbff 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -38,15 +38,14 @@ typedef enum { GIT_ITERATOR_INCLUDE_CONFLICTS = (1u << 5), } git_iterator_flag_t; - typedef struct { const char *start; const char *end; - /* paths to include in the iterator (literal). any paths not listed - * will be excluded. note that this vector may be resorted! + /* paths to include in the iterator (literal). if set, any paths not + * listed here will be excluded from iteration. */ - git_vector *pathlist; + git_strarray pathlist; /* flags, from above */ unsigned int flags; @@ -70,8 +69,9 @@ struct git_iterator { git_repository *repo; char *start; char *end; - git_vector *pathlist; - size_t pathlist_idx; + git_vector pathlist; + int (*strcomp)(const char *a, const char *b); + int (*strncomp)(const char *a, const char *b, size_t n); int (*prefixcomp)(const char *str, const char *prefix); size_t stat_calls; unsigned int flags; @@ -277,7 +277,8 @@ extern git_index *git_iterator_get_index(git_iterator *iter); typedef enum { GIT_ITERATOR_STATUS_NORMAL = 0, GIT_ITERATOR_STATUS_IGNORED = 1, - GIT_ITERATOR_STATUS_EMPTY = 2 + GIT_ITERATOR_STATUS_EMPTY = 2, + GIT_ITERATOR_STATUS_FILTERED = 3 } git_iterator_status_t; /* Advance over a directory and check if it contains no files or just diff --git a/src/merge.c b/src/merge.c index 1460a5040..5ba263bb4 100644 --- a/src/merge.c +++ b/src/merge.c @@ -2357,7 +2357,8 @@ static int merge_check_index(size_t *conflicts, git_repository *repo, git_index } iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; - iter_opts.pathlist = &staged_paths; + iter_opts.pathlist.strings = (char **)staged_paths.contents; + iter_opts.pathlist.count = staged_paths.length; if ((error = git_iterator_for_index(&iter_repo, index_repo, &iter_opts)) < 0 || (error = git_iterator_for_index(&iter_new, index_new, &iter_opts)) < 0 || diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c index 503d67450..336f959f6 100644 --- a/tests/diff/workdir.c +++ b/tests/diff/workdir.c @@ -486,7 +486,7 @@ void test_diff_workdir__to_index_with_pathlist_disabling_fnmatch(void) /* ensure that a single NULL pathspec is filtered out (like when using * fnmatch filtering) */ - opts.pathspec.strings = &pathspec; + opts.pathspec.count = 1; cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); @@ -581,6 +581,30 @@ void test_diff_workdir__to_index_with_pathlist_disabling_fnmatch(void) git_diff_free(diff); + /* ensure that multiple trailing slashes are ignored */ + pathspec = "subdir//////"; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, diff_file_cb, NULL, NULL, NULL, &exp)); + else + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); + + cl_assert_equal_i(3, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]); + } + + git_diff_free(diff); + /* ensure that fnmatching is completely disabled */ pathspec = "subdir/*"; diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c index 84dfbe113..5420aad40 100644 --- a/tests/repo/iterator.c +++ b/tests/repo/iterator.c @@ -26,7 +26,7 @@ static void expect_iterator_items( const git_index_entry *entry; int count, error; int no_trees = !(git_iterator_flags(i) & GIT_ITERATOR_INCLUDE_TREES); - bool v = true; + bool v = false; if (expected_flat < 0) { v = true; expected_flat = -expected_flat; } if (expected_total < 0) { v = true; expected_total = -expected_total; } @@ -1099,7 +1099,8 @@ void test_repo_iterator__indexfilelist(void) /* In this test we DO NOT force a case setting on the index. */ default_icase = ((git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0); - i_opts.pathlist = &filelist; + i_opts.pathlist.strings = (char **)filelist.contents; + i_opts.pathlist.count = filelist.length; /* All indexfilelist iterator tests are "autoexpand with no tree entries" */ @@ -1147,7 +1148,8 @@ void test_repo_iterator__indexfilelist_2(void) cl_git_pass(git_vector_insert(&filelist, "e")); cl_git_pass(git_vector_insert(&filelist, "k/a")); - i_opts.pathlist = &filelist; + i_opts.pathlist.strings = (char **)filelist.contents; + i_opts.pathlist.count = filelist.length; i_opts.start = "b"; i_opts.end = "k/D"; @@ -1188,7 +1190,8 @@ void test_repo_iterator__indexfilelist_icase(void) /* All indexfilelist iterator tests are "autoexpand with no tree entries" */ - i_opts.pathlist = &filelist; + i_opts.pathlist.strings = (char **)filelist.contents; + i_opts.pathlist.count = filelist.length; i_opts.start = "c"; i_opts.end = "k/D"; @@ -1248,7 +1251,8 @@ void test_repo_iterator__workdirfilelist(void) /* All indexfilelist iterator tests are "autoexpand with no tree entries" */ /* In this test we DO NOT force a case on the iteratords and verify default behavior. */ - i_opts.pathlist = &filelist; + i_opts.pathlist.strings = (char **)filelist.contents; + i_opts.pathlist.count = filelist.length; cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); expect_iterator_items(i, 8, NULL, 8, NULL); @@ -1297,7 +1301,8 @@ void test_repo_iterator__workdirfilelist_icase(void) g_repo = cl_git_sandbox_init("icase"); i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; - i_opts.pathlist = &filelist; + i_opts.pathlist.strings = (char **)filelist.contents; + i_opts.pathlist.count = filelist.length; i_opts.start = "c"; i_opts.end = "k/D"; From 1af84271dd221343bc199207f3eb923781124928 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 30 Aug 2015 18:35:57 -0400 Subject: [PATCH 089/450] tree_iterator: use a pathlist --- src/iterator.c | 35 ++++++++++++- tests/repo/iterator.c | 114 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) diff --git a/src/iterator.c b/src/iterator.c index bad24d13e..9bf56b650 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -560,7 +560,7 @@ static int tree_iterator__update_entry(tree_iterator *ti) return 0; } -static int tree_iterator__current( +static int tree_iterator__current_internal( const git_index_entry **entry, git_iterator *self) { int error; @@ -583,6 +583,39 @@ static int tree_iterator__current( return 0; } +int tree_iterator__advance( + const git_index_entry **out, git_iterator *self); + +static int tree_iterator__current( + const git_index_entry **out, git_iterator *self) +{ + git_index_entry *entry = NULL; + iterator_pathlist__match_t m; + int error; + + do { + if ((error = tree_iterator__current_internal(&entry, self)) < 0) + return error; + + if (self->pathlist.length) { + m = iterator_pathlist__match( + self, entry->path, strlen(entry->path)); + + if (m != ITERATOR_PATHLIST_MATCH) { + if ((error = tree_iterator__advance(&entry, self)) < 0) + return error; + + entry = NULL; + } + } + } while (!entry); + + if (out) + *out = entry; + + return error; +} + static int tree_iterator__advance_into( const git_index_entry **entry, git_iterator *self) { diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c index 5420aad40..cb9d4cd4b 100644 --- a/tests/repo/iterator.c +++ b/tests/repo/iterator.c @@ -1332,3 +1332,117 @@ void test_repo_iterator__workdirfilelist_icase(void) git_vector_free(&filelist); } + +void test_repo_iterator__treefilelist(void) +{ + git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; + git_vector filelist; + git_tree *tree; + bool default_icase; + int expect; + + cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb)); + cl_git_pass(git_vector_insert(&filelist, "a")); + cl_git_pass(git_vector_insert(&filelist, "B")); + cl_git_pass(git_vector_insert(&filelist, "c")); + cl_git_pass(git_vector_insert(&filelist, "D")); + cl_git_pass(git_vector_insert(&filelist, "e")); + cl_git_pass(git_vector_insert(&filelist, "k.a")); + cl_git_pass(git_vector_insert(&filelist, "k.b")); + cl_git_pass(git_vector_insert(&filelist, "k/1")); + cl_git_pass(git_vector_insert(&filelist, "k/a")); + cl_git_pass(git_vector_insert(&filelist, "kZZZZZZZ")); + cl_git_pass(git_vector_insert(&filelist, "L/1")); + + g_repo = cl_git_sandbox_init("icase"); + git_repository_head_tree(&tree, g_repo); + + /* All indexfilelist iterator tests are "autoexpand with no tree entries" */ + /* In this test we DO NOT force a case on the iteratords and verify default behavior. */ + + i_opts.pathlist.strings = (char **)filelist.contents; + i_opts.pathlist.count = filelist.length; + + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); + expect_iterator_items(i, 8, NULL, 8, NULL); + git_iterator_free(i); + + i_opts.start = "c"; + i_opts.end = NULL; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); + default_icase = git_iterator_ignore_case(i); + /* (c D e k/1 k/a L ==> 6) vs (c e k/1 k/a ==> 4) */ + expect = ((default_icase) ? 6 : 4); + expect_iterator_items(i, expect, NULL, expect, NULL); + git_iterator_free(i); + + i_opts.start = NULL; + i_opts.end = "e"; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); + default_icase = git_iterator_ignore_case(i); + /* (a B c D e ==> 5) vs (B D L/1 a c e ==> 6) */ + expect = ((default_icase) ? 5 : 6); + expect_iterator_items(i, expect, NULL, expect, NULL); + git_iterator_free(i); + + git_vector_free(&filelist); + git_tree_free(tree); +} + +void test_repo_iterator__treefilelist_icase(void) +{ + git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; + git_vector filelist; + git_tree *tree; + + cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb)); + cl_git_pass(git_vector_insert(&filelist, "a")); + cl_git_pass(git_vector_insert(&filelist, "B")); + cl_git_pass(git_vector_insert(&filelist, "c")); + cl_git_pass(git_vector_insert(&filelist, "D")); + cl_git_pass(git_vector_insert(&filelist, "e")); + cl_git_pass(git_vector_insert(&filelist, "k.a")); + cl_git_pass(git_vector_insert(&filelist, "k.b")); + cl_git_pass(git_vector_insert(&filelist, "k/1")); + cl_git_pass(git_vector_insert(&filelist, "k/a")); + cl_git_pass(git_vector_insert(&filelist, "kZZZZ")); + cl_git_pass(git_vector_insert(&filelist, "L/1")); + + g_repo = cl_git_sandbox_init("icase"); + git_repository_head_tree(&tree, g_repo); + + i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + i_opts.pathlist.strings = (char **)filelist.contents; + i_opts.pathlist.count = filelist.length; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); + expect_iterator_items(i, 3, NULL, 3, NULL); + git_iterator_free(i); + + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); + expect_iterator_items(i, 1, NULL, 1, NULL); + git_iterator_free(i); + + i_opts.flags = GIT_ITERATOR_IGNORE_CASE; + + i_opts.start = "c"; + i_opts.end = "k/D"; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); + expect_iterator_items(i, 5, NULL, 5, NULL); + git_iterator_free(i); + + i_opts.start = "k"; + i_opts.end = "k/Z"; + cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts)); + expect_iterator_items(i, 2, NULL, 2, NULL); + git_iterator_free(i); + + git_vector_free(&filelist); + git_tree_free(tree); +} From 7b73739fddce91731bb53320ae6e43d7d7276169 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 30 Aug 2015 18:56:39 -0400 Subject: [PATCH 090/450] checkout: use pathlist-based iterators --- src/checkout.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/checkout.c b/src/checkout.c index 311040d59..de48c9e01 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -2652,6 +2652,7 @@ int git_checkout_tree( git_index *index; git_tree *tree = NULL; git_iterator *tree_i = NULL; + git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; if (!treeish && !repo) { giterr_set(GITERR_CHECKOUT, @@ -2687,7 +2688,12 @@ int git_checkout_tree( if ((error = git_repository_index(&index, repo)) < 0) return error; - if (!(error = git_iterator_for_tree(&tree_i, tree, NULL))) + if ((opts->checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)) { + iter_opts.pathlist.count = opts->paths.count; + iter_opts.pathlist.strings = opts->paths.strings; + } + + if (!(error = git_iterator_for_tree(&tree_i, tree, &iter_opts))) error = git_checkout_iterator(tree_i, index, opts); git_iterator_free(tree_i); From 71ef639e5f48be8e8b459cc705be7f58016256f8 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 30 Aug 2015 18:57:06 -0400 Subject: [PATCH 091/450] status test: brackets are now literal --- tests/status/worktree_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/status/worktree_init.c b/tests/status/worktree_init.c index cc7e126f1..9d5cfa5a3 100644 --- a/tests/status/worktree_init.c +++ b/tests/status/worktree_init.c @@ -191,10 +191,10 @@ void test_status_worktree_init__bracket_in_filename(void) cl_git_pass(git_status_file(&status_flags, repo, FILE_WITHOUT_BRACKET)); cl_assert(status_flags == GIT_STATUS_WT_NEW); - cl_git_pass(git_status_file(&status_flags, repo, "LICENSE\\[1\\].md")); - cl_assert(status_flags == GIT_STATUS_INDEX_NEW); + cl_git_fail_with(git_status_file(&status_flags, repo, "LICENSE\\[1\\].md"), GIT_ENOTFOUND); cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET)); + cl_assert(status_flags == GIT_STATUS_INDEX_NEW); git_index_free(index); git_repository_free(repo); From 56ed415a24d41e83169ac17f468a540260bd08ff Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 30 Aug 2015 19:10:00 -0400 Subject: [PATCH 092/450] diff: drop `FILELIST_MATCH` Now that non-pathspec matching diffs are implemented at the iterator level, drop `FILELIST_MATCH`ing. --- include/git2/diff.h | 7 ------- src/merge.c | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index d3adf9f01..491212182 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -165,13 +165,6 @@ typedef enum { /** Include unreadable files in the diff */ GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED = (1u << 17), - /** Use literal path matching in the iterators. This is more broad - * than the DISABLE_PATHSPEC_MATCH flag. The caller must provide an - * array of paths (no patterns or prefixes). Only values included in - * that list will be returned. - */ - GIT_DIFF_ENABLE_FILELIST_MATCH = (1u << 18), - /* * Options controlling how output will be generated */ diff --git a/src/merge.c b/src/merge.c index 5ba263bb4..fc5088c82 100644 --- a/src/merge.c +++ b/src/merge.c @@ -2405,7 +2405,7 @@ static int merge_check_workdir(size_t *conflicts, git_repository *repo, git_inde * will be applied by the merge (including conflicts). Ensure that there * are no changes in the workdir to these paths. */ - opts.flags |= GIT_DIFF_ENABLE_FILELIST_MATCH; + opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH; opts.pathspec.count = merged_paths->length; opts.pathspec.strings = (char **)merged_paths->contents; From d53c8880696856d695b0979c55136b06e45d6fbb Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 30 Aug 2015 19:25:47 -0400 Subject: [PATCH 093/450] iterator: saner pathlist matching for idx iterator Some nicer refactoring for index iteration walks. The index iterator doesn't binary search through the pathlist space, since it lacks directory entries, and would have to binary search each index entry and all its parents (eg, when presented with an index entry of `foo/bar/file.c`, you would have to look in the pathlist for `foo/bar/file.c`, `foo/bar` and `foo`). Since the index entries and the pathlist are both nicely sorted, we walk the index entries in lockstep with the pathlist like we do for other iteration/diff/merge walks. --- src/iterator.c | 138 +++++++++++++++++++++++------------------- src/iterator.h | 1 + tests/diff/workdir.c | 24 -------- tests/repo/iterator.c | 70 +++++++++++++++++++++ 4 files changed, 147 insertions(+), 86 deletions(-) diff --git a/src/iterator.c b/src/iterator.c index 9bf56b650..e35c8dc85 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -75,7 +75,8 @@ static int iterator_pathlist__init(git_iterator *iter, git_strarray *pathspec) { size_t i; - if (git_vector_init(&iter->pathlist, pathspec->count, iter->strcomp) < 0) + if (git_vector_init(&iter->pathlist, pathspec->count, + (git_vector_cmp)iter->strcomp) < 0) return -1; for (i = 0; i < pathspec->count; i++) { @@ -98,7 +99,8 @@ static iterator_pathlist__match_t iterator_pathlist__match( size_t idx; int error; - error = git_vector_bsearch2(&idx, &iter->pathlist, iter->strcomp, path); + error = git_vector_bsearch2(&idx, &iter->pathlist, + (git_vector_cmp)iter->strcomp, path); if (error == 0) return ITERATOR_PATHLIST_MATCH; @@ -116,10 +118,7 @@ static iterator_pathlist__match_t iterator_pathlist__match( /* is this a literal directory entry (eg `foo/`) or a file beneath */ if (p[path_len] == '/') { - while (p[path_len] == '/') - path_len++; - - return (p[path_len] == '\0') ? + return (p[path_len+1] == '\0') ? ITERATOR_PATHLIST_MATCH_DIRECTORY : ITERATOR_PATHLIST_MATCH_CHILD; } @@ -133,10 +132,68 @@ static iterator_pathlist__match_t iterator_pathlist__match( return ITERATOR_PATHLIST_NONE; } +static void iterator_pathlist_walk__reset(git_iterator *iter) +{ + iter->pathlist_walk_idx = 0; +} + +/* walker for the index iterator that allows it to walk the sorted pathlist + * entries alongside the sorted index entries. the `iter->pathlist_walk_idx` + * stores the starting position for subsequent calls, the position is advanced + * along with the index iterator, with a special case for handling directories + * in the pathlist that are specified without trailing '/'. (eg, `foo`). + * we do not advance over these entries until we're certain that the index + * iterator will not ask us for a file beneath that directory (eg, `foo/bar`). + */ +static bool iterator_pathlist_walk__contains(git_iterator *iter, const char *path) +{ + size_t i; + char *p; + size_t p_len; + int cmp; + + for (i = iter->pathlist_walk_idx; i < iter->pathlist.length; i++) { + p = iter->pathlist.contents[i]; + p_len = strlen(p); + + /* see if the pathlist entry is a prefix of this path */ + cmp = iter->strncomp(p, path, p_len); + + /* this pathlist entry sorts before the given path, try the next */ + if (!p_len || cmp < 0) + iter->pathlist_walk_idx++; + + /* this pathlist sorts after the given path, no match. */ + else if (cmp > 0) + return false; + + /* match! an exact match (`foo` vs `foo`), the path is a child of an + * explicit directory in the pathlist (`foo/` vs `foo/bar`) or the path + * is a child of an entry in the pathlist (`foo` vs `foo/bar`) + */ + else if (path[p_len] == '\0' || p[p_len - 1] == '/' || path[p_len] == '/') + return true; + + /* only advance the start index for future callers if we know that we + * will not see a child of this path. eg, a pathlist entry `foo` is + * a prefix for `foo.txt` and `foo/bar`. don't advance the start + * pathlist index when we see `foo.txt` or we would miss a subsequent + * inspection of `foo/bar`. only advance when there are no more + * potential children. + */ + else if (path[p_len] > '/') + iter->pathlist_walk_idx++; + } + + return false; +} + static void iterator_pathlist__update_ignore_case(git_iterator *iter) { - git_vector_set_cmp(&iter->pathlist, iter->strcomp); + git_vector_set_cmp(&iter->pathlist, (git_vector_cmp)iter->strcomp); git_vector_sort(&iter->pathlist); + + iter->pathlist_walk_idx = 0; } @@ -583,13 +640,13 @@ static int tree_iterator__current_internal( return 0; } -int tree_iterator__advance( +static int tree_iterator__advance( const git_index_entry **out, git_iterator *self); static int tree_iterator__current( const git_index_entry **out, git_iterator *self) { - git_index_entry *entry = NULL; + const git_index_entry *entry = NULL; iterator_pathlist__match_t m; int error; @@ -797,9 +854,7 @@ static const git_index_entry *index_iterator__advance_over_unwanted( index_iterator *ii) { const git_index_entry *ie = index_iterator__index_entry(ii); - const char *p; - size_t p_len; - int cmp; + bool match; while (ie) { if (!iterator__include_conflicts(ii) && @@ -810,53 +865,17 @@ static const git_index_entry *index_iterator__advance_over_unwanted( } /* if we have a pathlist, this entry's path must be in it to be - * returned. otherwise, advance the pathlist entry or the iterator - * until we find the next path that we want to return. + * returned. walk the pathlist in unison with the index to + * compare paths. */ if (ii->base.pathlist.length) { + match = iterator_pathlist_walk__contains(&ii->base, ie->path); - if (ii->pathlist_idx >= ii->base.pathlist.length) { - ii->current = SIZE_MAX; - ie = NULL; - break; - } - - p = git_vector_get(&ii->base.pathlist, ii->pathlist_idx); - - /* trim trailing slashes that indicate an exact directory match */ - p_len = strlen(p); - - while (p_len && p[p_len-1] == '/') - p_len--; - - cmp = ii->base.strncomp(ie->path, p, p_len); - - /* we've matched the prefix - if the pathlist entry is equal to - * this entry, or if the pathlist entry is a folder (eg `foo/`) - * and this entry was beneath that, then continue. otherwise, - * sort the index entry path against the pathlist entry. - */ - if (cmp == 0) { - if (ie->path[p_len] == 0) - ; - else if (ie->path[p_len] == '/') - ; - else if (ie->path[p_len] < '/') - cmp = -1; - else if (ie->path[p_len] > '/') - cmp = 1; - } - - if (cmp < 0) { + if (!match) { ii->current++; ie = index_iterator__index_entry(ii); continue; } - - if (cmp > 0) { - ii->pathlist_idx++; - continue; - } } break; @@ -1006,7 +1025,8 @@ static int index_iterator__reset( return -1; ii->current = 0; - ii->pathlist_idx = 0; + + iterator_pathlist_walk__reset(self); /* if we're given a start prefix, find it; if we're given a pathlist, find * the first of those. start at the later of the two. @@ -1193,7 +1213,7 @@ static void fs_iterator__seek_frame_start( ff->index = 0; } -static int dirload_with_stat(git_vector *contents, size_t *filtered, fs_iterator *fi) +static int dirload_with_stat(git_vector *contents, fs_iterator *fi) { git_path_diriter diriter = GIT_PATH_DIRITER_INIT; const char *path; @@ -1204,8 +1224,6 @@ static int dirload_with_stat(git_vector *contents, size_t *filtered, fs_iterator iterator_pathlist__match_t pathlist_match = ITERATOR_PATHLIST_MATCH; int error; - *filtered = 0; - /* Any error here is equivalent to the dir not existing, skip over it */ if ((error = git_path_diriter_init( &diriter, fi->path.ptr, fi->dirload_flags)) < 0) { @@ -1241,11 +1259,8 @@ static int dirload_with_stat(git_vector *contents, size_t *filtered, fs_iterator if (fi->base.pathlist.length && fi->pathlist_match != ITERATOR_PATHLIST_MATCH && fi->pathlist_match != ITERATOR_PATHLIST_MATCH_DIRECTORY && - !(pathlist_match = iterator_pathlist__match(&fi->base, path, path_len))) { - - *filtered++; + !(pathlist_match = iterator_pathlist__match(&fi->base, path, path_len))) continue; - } /* Make sure to append two bytes, one for the path's null * termination, one for a possible trailing '/' for folders. @@ -1313,7 +1328,6 @@ static int fs_iterator__expand_dir(fs_iterator *fi) { int error; fs_iterator_frame *ff; - size_t filtered = 0; if (fi->depth > FS_MAX_DEPTH) { giterr_set(GITERR_REPOSITORY, @@ -1324,7 +1338,7 @@ static int fs_iterator__expand_dir(fs_iterator *fi) ff = fs_iterator__alloc_frame(fi); GITERR_CHECK_ALLOC(ff); - error = dirload_with_stat(&ff->entries, &filtered, fi); + error = dirload_with_stat(&ff->entries, fi); if (error < 0) { git_error_state last_error = { 0 }; diff --git a/src/iterator.h b/src/iterator.h index d2d61fbff..59f87e9de 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -70,6 +70,7 @@ struct git_iterator { char *start; char *end; git_vector pathlist; + size_t pathlist_walk_idx; int (*strcomp)(const char *a, const char *b); int (*strncomp)(const char *a, const char *b, size_t n); int (*prefixcomp)(const char *str, const char *prefix); diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c index 336f959f6..e87769170 100644 --- a/tests/diff/workdir.c +++ b/tests/diff/workdir.c @@ -581,30 +581,6 @@ void test_diff_workdir__to_index_with_pathlist_disabling_fnmatch(void) git_diff_free(diff); - /* ensure that multiple trailing slashes are ignored */ - pathspec = "subdir//////"; - - cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); - - for (use_iterator = 0; use_iterator <= 1; use_iterator++) { - memset(&exp, 0, sizeof(exp)); - - if (use_iterator) - cl_git_pass(diff_foreach_via_iterator( - diff, diff_file_cb, NULL, NULL, NULL, &exp)); - else - cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); - - cl_assert_equal_i(3, exp.files); - cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); - cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); - cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); - cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]); - cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]); - } - - git_diff_free(diff); - /* ensure that fnmatching is completely disabled */ pathspec = "subdir/*"; diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c index cb9d4cd4b..8eeb7d376 100644 --- a/tests/repo/iterator.c +++ b/tests/repo/iterator.c @@ -1162,6 +1162,76 @@ void test_repo_iterator__indexfilelist_2(void) git_vector_free(&filelist); } +void test_repo_iterator__indexfilelist_3(void) +{ + git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; + git_index *index; + git_vector filelist = GIT_VECTOR_INIT; + + g_repo = cl_git_sandbox_init("icase"); + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb)); + cl_git_pass(git_vector_insert(&filelist, "0")); + cl_git_pass(git_vector_insert(&filelist, "c")); + cl_git_pass(git_vector_insert(&filelist, "D")); + cl_git_pass(git_vector_insert(&filelist, "e")); + cl_git_pass(git_vector_insert(&filelist, "k/")); + cl_git_pass(git_vector_insert(&filelist, "k.a")); + cl_git_pass(git_vector_insert(&filelist, "k.b")); + cl_git_pass(git_vector_insert(&filelist, "kZZZZZZZ")); + + i_opts.pathlist.strings = (char **)filelist.contents; + i_opts.pathlist.count = filelist.length; + + i_opts.start = "b"; + i_opts.end = "k/D"; + + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + expect_iterator_items(i, 8, NULL, 8, NULL); + git_iterator_free(i); + + git_index_free(index); + git_vector_free(&filelist); +} + +void test_repo_iterator__indexfilelist_4(void) +{ + git_iterator *i; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; + git_index *index; + git_vector filelist = GIT_VECTOR_INIT; + + g_repo = cl_git_sandbox_init("icase"); + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb)); + cl_git_pass(git_vector_insert(&filelist, "0")); + cl_git_pass(git_vector_insert(&filelist, "c")); + cl_git_pass(git_vector_insert(&filelist, "D")); + cl_git_pass(git_vector_insert(&filelist, "e")); + cl_git_pass(git_vector_insert(&filelist, "k")); + cl_git_pass(git_vector_insert(&filelist, "k.a")); + cl_git_pass(git_vector_insert(&filelist, "k.b")); + cl_git_pass(git_vector_insert(&filelist, "kZZZZZZZ")); + + i_opts.pathlist.strings = (char **)filelist.contents; + i_opts.pathlist.count = filelist.length; + + i_opts.start = "b"; + i_opts.end = "k/D"; + + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + expect_iterator_items(i, 8, NULL, 8, NULL); + git_iterator_free(i); + + git_index_free(index); + git_vector_free(&filelist); +} + void test_repo_iterator__indexfilelist_icase(void) { git_iterator *i; From 4d19bced3f8156b4255997b553fed99746c2a785 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 30 Aug 2015 19:33:18 -0400 Subject: [PATCH 094/450] iterator test: use new iter opts in fifo test --- tests/repo/iterator.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c index 8eeb7d376..619e11842 100644 --- a/tests/repo/iterator.c +++ b/tests/repo/iterator.c @@ -1047,6 +1047,7 @@ void test_repo_iterator__skips_fifos_and_such(void) #ifndef GIT_WIN32 git_iterator *i; const git_index_entry *e; + git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; g_repo = cl_git_sandbox_init("empty_standard_repo"); @@ -1056,9 +1057,11 @@ void test_repo_iterator__skips_fifos_and_such(void) cl_assert(!mkfifo("empty_standard_repo/fifo", 0777)); cl_assert(!access("empty_standard_repo/fifo", F_OK)); + i_opts.flags = GIT_ITERATOR_INCLUDE_TREES | + GIT_ITERATOR_DONT_AUTOEXPAND; + cl_git_pass(git_iterator_for_filesystem( - &i, "empty_standard_repo", GIT_ITERATOR_INCLUDE_TREES | - GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); + &i, "empty_standard_repo", &i_opts)); cl_git_pass(git_iterator_advance(&e, i)); /* .git */ cl_assert(S_ISDIR(e->mode)); From 03210cfa00d9fe4cd4cc236253b0d590c0d994cc Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 31 Aug 2015 12:12:21 -0400 Subject: [PATCH 095/450] iterator test: handle case (in)sensitivity --- tests/repo/iterator.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c index 619e11842..9c4cc9ea4 100644 --- a/tests/repo/iterator.c +++ b/tests/repo/iterator.c @@ -1099,6 +1099,7 @@ void test_repo_iterator__indexfilelist(void) g_repo = cl_git_sandbox_init("icase"); cl_git_pass(git_repository_index(&index, g_repo)); + /* In this test we DO NOT force a case setting on the index. */ default_icase = ((git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0); @@ -1139,6 +1140,7 @@ void test_repo_iterator__indexfilelist_2(void) git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; git_index *index; git_vector filelist = GIT_VECTOR_INIT; + int default_icase, expect; g_repo = cl_git_sandbox_init("icase"); @@ -1149,16 +1151,23 @@ void test_repo_iterator__indexfilelist_2(void) cl_git_pass(git_vector_insert(&filelist, "c")); cl_git_pass(git_vector_insert(&filelist, "D")); cl_git_pass(git_vector_insert(&filelist, "e")); + cl_git_pass(git_vector_insert(&filelist, "k/1")); cl_git_pass(git_vector_insert(&filelist, "k/a")); + /* In this test we DO NOT force a case setting on the index. */ + default_icase = ((git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0); + i_opts.pathlist.strings = (char **)filelist.contents; i_opts.pathlist.count = filelist.length; i_opts.start = "b"; i_opts.end = "k/D"; + /* (c D e k/1 k/a ==> 5) vs (c e k/1 ==> 3) */ + expect = default_icase ? 5 : 3; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); - expect_iterator_items(i, 4, NULL, 4, NULL); + expect_iterator_items(i, expect, NULL, expect, NULL); git_iterator_free(i); git_index_free(index); @@ -1171,6 +1180,7 @@ void test_repo_iterator__indexfilelist_3(void) git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; git_index *index; git_vector filelist = GIT_VECTOR_INIT; + int default_icase, expect; g_repo = cl_git_sandbox_init("icase"); @@ -1186,14 +1196,20 @@ void test_repo_iterator__indexfilelist_3(void) cl_git_pass(git_vector_insert(&filelist, "k.b")); cl_git_pass(git_vector_insert(&filelist, "kZZZZZZZ")); + /* In this test we DO NOT force a case setting on the index. */ + default_icase = ((git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0); + i_opts.pathlist.strings = (char **)filelist.contents; i_opts.pathlist.count = filelist.length; i_opts.start = "b"; i_opts.end = "k/D"; + /* (c D e k/1 k/a k/B k/c k/D) vs (c e k/1 k/B k/D) */ + expect = default_icase ? 8 : 5; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); - expect_iterator_items(i, 8, NULL, 8, NULL); + expect_iterator_items(i, expect, NULL, expect, NULL); git_iterator_free(i); git_index_free(index); @@ -1206,6 +1222,7 @@ void test_repo_iterator__indexfilelist_4(void) git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT; git_index *index; git_vector filelist = GIT_VECTOR_INIT; + int default_icase, expect; g_repo = cl_git_sandbox_init("icase"); @@ -1221,14 +1238,20 @@ void test_repo_iterator__indexfilelist_4(void) cl_git_pass(git_vector_insert(&filelist, "k.b")); cl_git_pass(git_vector_insert(&filelist, "kZZZZZZZ")); + /* In this test we DO NOT force a case setting on the index. */ + default_icase = ((git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0); + i_opts.pathlist.strings = (char **)filelist.contents; i_opts.pathlist.count = filelist.length; i_opts.start = "b"; i_opts.end = "k/D"; + /* (c D e k/1 k/a k/B k/c k/D) vs (c e k/1 k/B k/D) */ + expect = default_icase ? 8 : 5; + cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); - expect_iterator_items(i, 8, NULL, 8, NULL); + expect_iterator_items(i, expect, NULL, expect, NULL); git_iterator_free(i); git_index_free(index); From 53c2296bfed972026809803415944b4dd4eff6d3 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 31 Aug 2015 19:41:43 -0400 Subject: [PATCH 096/450] iterator: better document GIT_DIFF_DISABLE_PATHSPEC_MATCH --- include/git2/diff.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index 491212182..a0f6db350 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -129,10 +129,12 @@ typedef enum { */ GIT_DIFF_INCLUDE_CASECHANGE = (1u << 11), - /** If the pathspec is set in the diff options, this flags means to - * use exact prefix matches instead of an fnmatch pattern. Each - * path in the list must either be a full filename or a subdirectory - * prefix. + /** If the pathspec is set in the diff options, this flags indicates + * that the paths will be treated as literal paths instead of + * fnmatch patterns. Each path in the list must either be a full + * path to a file or a directory. (A trailing slash indicates that + * the path will _only_ match a directory). If a directory is + * specified, all children will be included. */ GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1u << 12), From 1cef6b9f190587a78c65bd3168879be3eb37e0fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 3 Sep 2015 11:38:21 +0200 Subject: [PATCH 097/450] config: correct documentation for non-existent config file --- include/git2/config.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/git2/config.h b/include/git2/config.h index 05c3ad622..56b5431ac 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -171,6 +171,9 @@ GIT_EXTERN(int) git_config_new(git_config **out); * parsed; it's expected to be a native Git config file following * the default Git config syntax (see man git-config). * + * If the file does not exist, the file will still be added and it + * will be created the first time we write to it. + * * Note that the configuration object will free the file * automatically. * @@ -202,8 +205,7 @@ GIT_EXTERN(int) git_config_add_file_ondisk( * * @param out The configuration instance to create * @param path Path to the on-disk file to open - * @return 0 on success, GIT_ENOTFOUND when the file doesn't exist - * or an error code + * @return 0 on success, or an error code */ GIT_EXTERN(int) git_config_open_ondisk(git_config **out, const char *path); From 01fe83741ae162224637a27454be712a4195112d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 3 Sep 2015 13:35:15 +0200 Subject: [PATCH 098/450] Revert "Get rid of libssh2 embedding" The embedding was removed as a libssh2 release with Windows crypto support became available, but dependencies are still annoying so this ahs been requested again. This reverts commit 20dcb7315cd4c5760c68402998fd9e5a6bf5505d. --- CMakeLists.txt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c03c718c..293153f86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,10 @@ IF(MSVC) # are linking statically OPTION( STATIC_CRT "Link the static CRT libraries" ON ) + # If you want to embed a copy of libssh2 into libgit2, pass a + # path to libssh2 + OPTION( EMBED_SSH_PATH "Path to libssh2 to embed (Windows)" OFF ) + ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS) ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE) ADD_DEFINITIONS(-D_CRT_NONSTDC_NO_DEPRECATE) @@ -172,6 +176,13 @@ IF (COREFOUNDATION_FOUND) ENDIF() +IF (WIN32 AND EMBED_SSH_PATH) + FILE(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c") + INCLUDE_DIRECTORIES("${EMBED_SSH_PATH}/include") + FILE(WRITE "${EMBED_SSH_PATH}/src/libssh2_config.h" "#define HAVE_WINCNG\n#define LIBSSH2_WINCNG\n#include \"../win32/libssh2_config.h\"") + ADD_DEFINITIONS(-DGIT_SSH) +ENDIF() + IF (WIN32 AND WINHTTP) ADD_DEFINITIONS(-DGIT_WINHTTP) INCLUDE_DIRECTORIES(deps/http-parser) @@ -501,7 +512,7 @@ ELSE() ENDIF() # Compile and link libgit2 -ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC}) +ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SSH} ${SRC_SHA1} ${WIN_RC}) TARGET_LINK_LIBRARIES(git2 ${SECURITY_DIRS}) TARGET_LINK_LIBRARIES(git2 ${COREFOUNDATION_DIRS}) TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES}) @@ -571,7 +582,7 @@ IF (BUILD_CLAR) ${CLAR_PATH}/clar.c PROPERTIES OBJECT_DEPENDS ${CLAR_PATH}/clar.suite) - ADD_EXECUTABLE(libgit2_clar ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1}) + ADD_EXECUTABLE(libgit2_clar ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SSH} ${SRC_SHA1}) TARGET_LINK_LIBRARIES(libgit2_clar ${COREFOUNDATION_DIRS}) TARGET_LINK_LIBRARIES(libgit2_clar ${SECURITY_DIRS}) From 81b76367571010aa83a3de38aecfee3c301e888d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 4 Sep 2015 13:30:49 +0200 Subject: [PATCH 099/450] index: put the icase insert choice in macros This should let us see more clearly what we're doing and avoid the ugly 'if' we need every time we want to interact with the map. --- src/index.c | 55 +++++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/src/index.c b/src/index.c index e904ffcfe..b9a78b21f 100644 --- a/src/index.c +++ b/src/index.c @@ -28,6 +28,29 @@ GIT__USE_IDXMAP GIT__USE_IDXMAP_ICASE +#define INSERT_IN_MAP_EX(idx, map, e, err) do { \ + if ((idx)->ignore_case) \ + git_idxmap_icase_insert((khash_t(idxicase) *) (map), (e), (e), (err)); \ + else \ + git_idxmap_insert((map), (e), (e), (err)); \ + } while (0) + +#define INSERT_IN_MAP(idx, e, err) INSERT_IN_MAP_EX(idx, (idx)->entries_map, e, err) + +#define LOOKUP_IN_MAP(p, idx, k) do { \ + if ((idx)->ignore_case) \ + (p) = git_idxmap_icase_lookup_index((khash_t(idxicase) *) index->entries_map, (k)); \ + else \ + (p) = git_idxmap_lookup_index(index->entries_map, (k)); \ + } while (0) + +#define DELETE_IN_MAP(idx, e) do { \ + if ((idx)->ignore_case) \ + git_idxmap_icase_delete((khash_t(idxicase) *) (idx)->entries_map, (e)); \ + else \ + git_idxmap_delete((idx)->entries_map, (e)); \ + } while (0) + static int index_apply_to_wd_diff(git_index *index, int action, const git_strarray *paths, unsigned int flags, git_index_matched_path_cb cb, void *payload); @@ -514,11 +537,7 @@ static int index_remove_entry(git_index *index, size_t pos) if (entry != NULL) git_tree_cache_invalidate_path(index->tree, entry->path); - if (index->ignore_case) - git_idxmap_icase_delete((khash_t(idxicase) *) index->entries_map, entry); - else - git_idxmap_delete(index->entries_map, entry); - + DELETE_IN_MAP(index, entry); error = git_vector_remove(&index->entries, pos); if (!error) { @@ -824,10 +843,7 @@ const git_index_entry *git_index_get_bypath( key.path = path; GIT_IDXENTRY_STAGE_SET(&key, stage); - if (index->ignore_case) - pos = git_idxmap_icase_lookup_index((khash_t(idxicase) *) index->entries_map, &key); - else - pos = git_idxmap_lookup_index(index->entries_map, &key); + LOOKUP_IN_MAP(pos, index, &key); if (git_idxmap_valid_index(index->entries_map, pos)) return git_idxmap_value_at(index->entries_map, pos); @@ -1161,10 +1177,7 @@ static int index_insert( error = git_vector_insert_sorted(&index->entries, entry, index_no_dups); if (error == 0) { - if (index->ignore_case) - git_idxmap_icase_insert((khash_t(idxicase) *) index->entries_map, entry, entry, error); else - git_idxmap_insert(index->entries_map, entry, entry, error); - + INSERT_IN_MAP(index, entry, error); } } @@ -1400,10 +1413,8 @@ int git_index_remove(git_index *index, const char *path, int stage) remove_key.path = path; GIT_IDXENTRY_STAGE_SET(&remove_key, stage); - if (index->ignore_case) - git_idxmap_icase_delete((khash_t(idxicase) *) index->entries_map, &remove_key); - else - git_idxmap_delete(index->entries_map, &remove_key); + + DELETE_IN_MAP(index, &remove_key); if (index_find(&position, index, path, 0, stage, false) < 0) { giterr_set( @@ -2236,10 +2247,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) goto done; } - if (index->ignore_case) - git_idxmap_icase_insert((khash_t(idxicase) *) index->entries_map, entry, entry, error); - else - git_idxmap_insert(index->entries_map, entry, entry, error); + INSERT_IN_MAP(index, entry, error); if (error < 0) { index_entry_free(entry); @@ -2690,10 +2698,7 @@ int git_index_read_tree(git_index *index, const git_tree *tree) kh_resize(idx, entries_map, entries.length); git_vector_foreach(&entries, i, e) { - if (index->ignore_case) - git_idxmap_icase_insert((git_idxmap_icase *) entries_map, e, e, error); - else - git_idxmap_insert(entries_map, e, e, error); + INSERT_IN_MAP_EX(index, entries_map, e, error); if (error < 0) { giterr_set(GITERR_INDEX, "failed to insert entry into map"); From c3733e56410d5bc73fcca0df8e062f84a0565f90 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Fri, 4 Sep 2015 08:56:26 -0400 Subject: [PATCH 100/450] Add more headers to HTTP requests --- src/transports/http.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/transports/http.c b/src/transports/http.c index 87f3ee816..f9e5da2b1 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -54,6 +54,7 @@ typedef struct { char *redirect_url; const char *verb; char *chunk_buffer; + git_strarray *extra_headers; unsigned chunk_buffer_len; unsigned sent_request : 1, received_response : 1, @@ -193,6 +194,7 @@ static int gen_request( { http_subtransport *t = OWNING_SUBTRANSPORT(s); const char *path = t->connection_data.path ? t->connection_data.path : "/"; + size_t i; git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url); @@ -210,6 +212,13 @@ static int gen_request( } else git_buf_puts(buf, "Accept: */*\r\n"); + if (s->extra_headers) { + for (i = 0; i < s->extra_headers->count; i++) { + git_buf_puts(buf, s->extra_headers->strings[i]); + git_buf_puts(buf, "\r\n"); + } + } + /* Apply credentials to the request */ if (apply_credentials(buf, t) < 0) return -1; From 6af6e69009e207da851c2b8dff395babddb3bfa4 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Fri, 4 Sep 2015 09:18:32 -0400 Subject: [PATCH 101/450] Put the extra headers on the connection_data instead --- src/netops.h | 1 + src/transports/http.c | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/netops.h b/src/netops.h index b7170a0f2..ea1072518 100644 --- a/src/netops.h +++ b/src/netops.h @@ -70,6 +70,7 @@ typedef struct gitno_connection_data { char *user; char *pass; bool use_ssl; + git_strarray *extra_headers; } gitno_connection_data; /* diff --git a/src/transports/http.c b/src/transports/http.c index f9e5da2b1..d348310c1 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -54,7 +54,6 @@ typedef struct { char *redirect_url; const char *verb; char *chunk_buffer; - git_strarray *extra_headers; unsigned chunk_buffer_len; unsigned sent_request : 1, received_response : 1, @@ -212,9 +211,9 @@ static int gen_request( } else git_buf_puts(buf, "Accept: */*\r\n"); - if (s->extra_headers) { - for (i = 0; i < s->extra_headers->count; i++) { - git_buf_puts(buf, s->extra_headers->strings[i]); + if (t->connection_data.extra_headers) { + for (i = 0; i < t->connection_data.extra_headers->count; i++) { + git_buf_puts(buf, t->connection_data.extra_headers->strings[i]); git_buf_puts(buf, "\r\n"); } } From ac9b51278996b864d0a2f7d61a827f89cbd4ff23 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Fri, 4 Sep 2015 09:20:45 -0400 Subject: [PATCH 102/450] Pull extra_http_headers from the git_remote --- src/remote.c | 2 ++ src/remote.h | 1 + src/transports/http.c | 1 + 3 files changed, 4 insertions(+) diff --git a/src/remote.c b/src/remote.c index 7404bf49f..b6fb87ece 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1643,6 +1643,8 @@ void git_remote_free(git_remote *remote) free_refspecs(&remote->passive_refspecs); git_vector_free(&remote->passive_refspecs); + git__free(remote->extra_http_headers); + git_push_free(remote->push); git__free(remote->url); git__free(remote->pushurl); diff --git a/src/remote.h b/src/remote.h index e696997f4..bfb20362b 100644 --- a/src/remote.h +++ b/src/remote.h @@ -32,6 +32,7 @@ struct git_remote { git_remote_autotag_option_t download_tags; int prune_refs; int passed_refspecs; + git_strarray *extra_http_headers; }; const char* git_remote__urlfordirection(struct git_remote *remote, int direction); diff --git a/src/transports/http.c b/src/transports/http.c index d348310c1..664cd80ab 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -974,6 +974,7 @@ static int http_action( if ((!t->connection_data.host || !t->connection_data.port || !t->connection_data.path) && (ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0) return ret; + t->connection_data.extra_headers = t->owner->owner->extra_http_headers; if ((ret = http_connect(t)) < 0) return ret; From 59d6128e2730b71da6fdebbdf9a4d04b909e9721 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Fri, 4 Sep 2015 09:36:50 -0400 Subject: [PATCH 103/450] Allow the world to set HTTP headers for remotes --- include/git2/remote.h | 3 +++ src/remote.c | 12 +++++++++++- src/remote.h | 2 +- src/transports/http.c | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/include/git2/remote.h b/include/git2/remote.h index 444fe5276..40adca0ce 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -230,6 +230,9 @@ GIT_EXTERN(size_t) git_remote_refspec_count(const git_remote *remote); */ GIT_EXTERN(const git_refspec *)git_remote_get_refspec(const git_remote *remote, size_t n); +GIT_EXTERN(int) git_remote_extra_http_headers(const git_remote *remote, git_strarray *extra_http_headers); +GIT_EXTERN(int) git_remote_set_extra_http_headers(git_remote *remote, const git_strarray extra_http_headers); + /** * Open a connection to a remote * diff --git a/src/remote.c b/src/remote.c index b6fb87ece..b7d82e708 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1643,7 +1643,7 @@ void git_remote_free(git_remote *remote) free_refspecs(&remote->passive_refspecs); git_vector_free(&remote->passive_refspecs); - git__free(remote->extra_http_headers); + git_strarray_free(&remote->extra_http_headers); git_push_free(remote->push); git__free(remote->url); @@ -2154,6 +2154,16 @@ size_t git_remote_refspec_count(const git_remote *remote) return remote->refspecs.length; } +int git_remote_extra_http_headers(const git_remote *remote, git_strarray *extra_http_headers) +{ + return git_strarray_copy(extra_http_headers, &remote->extra_http_headers); +} + +int git_remote_set_extra_http_headers(git_remote *remote, const git_strarray extra_http_headers) +{ + return git_strarray_copy(&remote->extra_http_headers, &extra_http_headers); +} + const git_refspec *git_remote_get_refspec(const git_remote *remote, size_t n) { return git_vector_get(&remote->refspecs, n); diff --git a/src/remote.h b/src/remote.h index bfb20362b..3a15288f3 100644 --- a/src/remote.h +++ b/src/remote.h @@ -32,7 +32,7 @@ struct git_remote { git_remote_autotag_option_t download_tags; int prune_refs; int passed_refspecs; - git_strarray *extra_http_headers; + git_strarray extra_http_headers; }; const char* git_remote__urlfordirection(struct git_remote *remote, int direction); diff --git a/src/transports/http.c b/src/transports/http.c index 664cd80ab..3cd5632f8 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -974,7 +974,7 @@ static int http_action( if ((!t->connection_data.host || !t->connection_data.port || !t->connection_data.path) && (ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0) return ret; - t->connection_data.extra_headers = t->owner->owner->extra_http_headers; + t->connection_data.extra_headers = &t->owner->owner->extra_http_headers; if ((ret = http_connect(t)) < 0) return ret; From c097f7173daced0f86fd816a72ad5fc0f636484a Mon Sep 17 00:00:00 2001 From: Leo Yang Date: Mon, 17 Aug 2015 15:02:02 -0400 Subject: [PATCH 104/450] New API: git_index_find_prefix Find the first index entry matching a prefix. --- include/git2/index.h | 11 +++++++++++ src/index.c | 24 ++++++++++++++++++++++++ tests/index/tests.c | 21 +++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/include/git2/index.h b/include/git2/index.h index 7caf3ed78..176ba301e 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -643,6 +643,17 @@ GIT_EXTERN(int) git_index_update_all( */ GIT_EXTERN(int) git_index_find(size_t *at_pos, git_index *index, const char *path); +/** + * Find the first position of any entries matching a prefix. To find the first position + * of a path inside a given folder, suffix the prefix with a '/'. + * + * @param at_pos the address to which the position of the index entry is written (optional) + * @param index an existing index object + * @param prefix the prefix to search for + * @return 0 with valid value in at_pos; an error code otherwise + */ +GIT_EXTERN(int) git_index_find_prefix(size_t *at_pos, git_index *index, const char *prefix); + /**@}*/ /** @name Conflict Index Entry Functions diff --git a/src/index.c b/src/index.c index 53120e49c..e32320f80 100644 --- a/src/index.c +++ b/src/index.c @@ -1420,6 +1420,30 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage) return error; } +int git_index_find_prefix(size_t *at_pos, git_index *index, const char *prefix) +{ + int error = 0; + size_t pos; + const git_index_entry *entry; + + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock index"); + return -1; + } + + index_find(&pos, index, prefix, strlen(prefix), GIT_INDEX_STAGE_ANY, false); + entry = git_vector_get(&index->entries, pos); + if (!entry || git__prefixcmp(entry->path, prefix) != 0) + error = GIT_ENOTFOUND; + + if (!error && at_pos) + *at_pos = pos; + + git_mutex_unlock(&index->lock); + + return error; +} + int git_index__find_pos( size_t *out, git_index *index, const char *path, size_t path_len, int stage) { diff --git a/tests/index/tests.c b/tests/index/tests.c index e1ff12ad0..60c212681 100644 --- a/tests/index/tests.c +++ b/tests/index/tests.c @@ -155,6 +155,27 @@ void test_index_tests__find_in_empty(void) git_index_free(index); } +void test_index_tests__find_prefix(void) +{ + git_index *index; + const git_index_entry *entry; + size_t pos; + + cl_git_pass(git_index_open(&index, TEST_INDEX_PATH)); + + cl_git_pass(git_index_find_prefix(&pos, index, "src")); + entry = git_index_get_byindex(index, pos); + cl_assert(git__strcmp(entry->path, "src/block-sha1/sha1.c") == 0); + + cl_git_pass(git_index_find_prefix(&pos, index, "src/co")); + entry = git_index_get_byindex(index, pos); + cl_assert(git__strcmp(entry->path, "src/commit.c") == 0); + + cl_assert(GIT_ENOTFOUND == git_index_find_prefix(NULL, index, "blah")); + + git_index_free(index); +} + void test_index_tests__write(void) { git_index *index; From d83b2e9f51539287d5d2b1bd939ea9540fa6e5be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 5 Sep 2015 03:54:06 +0200 Subject: [PATCH 105/450] filebuf: follow symlinks when creating a lock file We create a lockfile to update files under GIT_DIR. Sometimes these files are actually located elsewhere and a symlink takes their place. In that case we should lock and update the file at its final location rather than overwrite the symlink. --- CHANGELOG.md | 3 ++ src/filebuf.c | 84 ++++++++++++++++++++++++++++++++++++++++++-- tests/core/filebuf.c | 53 ++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f28cbf3b..1f3c3ed6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ v0.23 + 1 example `filter=*`. Consumers should examine the attributes parameter of the `check` function for details. +* Symlinks are now followed when locking a file, which can be + necessary when multiple worktrees share a base repository. + ### API additions * `git_config_lock()` has been added, which allow for diff --git a/src/filebuf.c b/src/filebuf.c index 838f4b4d2..2bbc210ba 100644 --- a/src/filebuf.c +++ b/src/filebuf.c @@ -191,6 +191,81 @@ static int write_deflate(git_filebuf *file, void *source, size_t len) return 0; } +#define MAX_SYMLINK_DEPTH 5 + +static int resolve_symlink(git_buf *out, const char *path) +{ + int i, error, root; + ssize_t ret; + struct stat st; + git_buf curpath = GIT_BUF_INIT, target = GIT_BUF_INIT; + + if ((error = git_buf_grow(&target, GIT_PATH_MAX + 1)) < 0 || + (error = git_buf_puts(&curpath, path)) < 0) + return error; + + for (i = 0; i < MAX_SYMLINK_DEPTH; i++) { + error = p_lstat(curpath.ptr, &st); + if (error < 0 && errno == ENOENT) { + error = git_buf_puts(out, curpath.ptr); + goto cleanup; + } + + if (error < 0) { + giterr_set(GITERR_OS, "failed to stat '%s'", curpath.ptr); + error = -1; + goto cleanup; + } + + if (!S_ISLNK(st.st_mode)) { + error = git_buf_puts(out, curpath.ptr); + goto cleanup; + } + + ret = p_readlink(curpath.ptr, target.ptr, GIT_PATH_MAX); + if (ret < 0) { + giterr_set(GITERR_OS, "failed to read symlink '%s'", curpath.ptr); + error = -1; + goto cleanup; + } + + if (ret == GIT_PATH_MAX) { + giterr_set(GITERR_INVALID, "symlink target too long"); + error = -1; + goto cleanup; + } + + /* readlink(2) won't NUL-terminate for us */ + target.ptr[ret] = '\0'; + target.size = ret; + + root = git_path_root(target.ptr); + if (root >= 0) { + if ((error = git_buf_puts(&curpath, target.ptr)) < 0) + goto cleanup; + } else { + git_buf dir = GIT_BUF_INIT; + + if ((error = git_path_dirname_r(&dir, curpath.ptr)) < 0) + goto cleanup; + + git_buf_swap(&curpath, &dir); + git_buf_free(&dir); + + if ((error = git_path_apply_relative(&curpath, target.ptr)) < 0) + goto cleanup; + } + } + + giterr_set(GITERR_INVALID, "maximum symlink depth reached"); + error = -1; + +cleanup: + git_buf_free(&curpath); + git_buf_free(&target); + return error; +} + int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode) { int compression, error = -1; @@ -265,11 +340,14 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode file->path_lock = git_buf_detach(&tmp_path); GITERR_CHECK_ALLOC(file->path_lock); } else { - path_len = strlen(path); + git_buf resolved_path = GIT_BUF_INIT; + + if ((error = resolve_symlink(&resolved_path, path)) < 0) + goto cleanup; /* Save the original path of the file */ - file->path_original = git__strdup(path); - GITERR_CHECK_ALLOC(file->path_original); + path_len = resolved_path.size; + file->path_original = git_buf_detach(&resolved_path); /* create the locking path by appending ".lock" to the original */ GITERR_CHECK_ALLOC_ADD(&alloc_len, path_len, GIT_FILELOCK_EXTLENGTH); diff --git a/tests/core/filebuf.c b/tests/core/filebuf.c index 3f7dc8569..39d98ff7e 100644 --- a/tests/core/filebuf.c +++ b/tests/core/filebuf.c @@ -151,3 +151,56 @@ void test_core_filebuf__rename_error(void) cl_assert_equal_i(false, git_path_exists(test_lock)); } + +void test_core_filebuf__symlink_follow(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + const char *dir = "linkdir", *source = "linkdir/link"; + +#ifdef GIT_WIN32 + cl_skip(); +#endif + + cl_git_pass(p_mkdir(dir, 0777)); + cl_git_pass(p_symlink("target", source)); + + cl_git_pass(git_filebuf_open(&file, source, 0, 0666)); + cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); + + cl_assert_equal_i(true, git_path_exists("linkdir/target.lock")); + + cl_git_pass(git_filebuf_commit(&file)); + cl_assert_equal_i(true, git_path_exists("linkdir/target")); + + git_filebuf_cleanup(&file); + + /* The second time around, the target file does exist */ + cl_git_pass(git_filebuf_open(&file, source, 0, 0666)); + cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); + + cl_assert_equal_i(true, git_path_exists("linkdir/target.lock")); + + cl_git_pass(git_filebuf_commit(&file)); + cl_assert_equal_i(true, git_path_exists("linkdir/target")); + + git_filebuf_cleanup(&file); + cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES)); +} + +void test_core_filebuf__symlink_depth(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + const char *dir = "linkdir", *source = "linkdir/link"; + +#ifdef GIT_WIN32 + cl_skip(); +#endif + + cl_git_pass(p_mkdir(dir, 0777)); + /* Endless loop */ + cl_git_pass(p_symlink("link", source)); + + cl_git_fail(git_filebuf_open(&file, source, 0, 0666)); + + cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES)); +} From 280adb3f942a1ce4f4939b7058209d0cd0467062 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 4 Aug 2015 16:51:00 -0500 Subject: [PATCH 106/450] index: canonicalize directory case when adding On case insensitive systems, when given a user-provided path in the higher-level index addition functions (eg `git_index_add_bypath` / `git_index_add_frombuffer`), examine the index to try to match the given path to an existing directory. Various mechanisms can cause the on-disk representation of a folder to not match the representation in HEAD or the index - for example, a case changing rename of some file `a/file.txt` to `A/file.txt` will update the paths in the index, but not rename the folder on disk. If a user subsequently adds `a/other.txt`, then this should be stored in the index as `A/other.txt`. --- src/index.c | 95 +++++++++++++++++++++++++-- tests/index/bypath.c | 148 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 236 insertions(+), 7 deletions(-) diff --git a/src/index.c b/src/index.c index 6be73d2c7..be86f16cb 100644 --- a/src/index.c +++ b/src/index.c @@ -1102,6 +1102,74 @@ static int check_file_directory_collision(git_index *index, return 0; } +static int canonicalize_directory_path( + git_index *index, git_index_entry *entry) +{ + const git_index_entry *match, *best = NULL; + char *search, *sep; + size_t pos, search_len, best_len; + + if (!index->ignore_case) + return 0; + + /* item already exists in the index, simply re-use the existing case */ + if ((match = git_index_get_bypath(index, entry->path, 0)) != NULL) { + memcpy((char *)entry->path, match->path, strlen(entry->path)); + return 0; + } + + /* nothing to do */ + if (strchr(entry->path, '/') == NULL) + return 0; + + if ((search = git__strdup(entry->path)) == NULL) + return -1; + + /* starting at the parent directory and descending to the root, find the + * common parent directory. + */ + while (!best && (sep = strrchr(search, '/'))) { + sep[1] = '\0'; + + search_len = strlen(search); + + git_vector_bsearch2( + &pos, &index->entries, index->entries_search_path, search); + + while ((match = git_vector_get(&index->entries, pos))) { + if (GIT_IDXENTRY_STAGE(match) != 0) { + /* conflicts do not contribute to canonical paths */ + } else if (memcmp(search, match->path, search_len) == 0) { + /* prefer an exact match to the input filename */ + best = match; + best_len = search_len; + break; + } else if (strncasecmp(search, match->path, search_len) == 0) { + /* continue walking, there may be a path with an exact + * (case sensitive) match later in the index, but use this + * as the best match until that happens. + */ + if (!best) { + best = match; + best_len = search_len; + } + } else { + break; + } + + pos++; + } + + sep[0] = '\0'; + } + + if (best) + memcpy((char *)entry->path, best->path, best_len); + + git__free(search); + return 0; +} + static int index_no_dups(void **old, void *new) { const git_index_entry *entry = new; @@ -1115,10 +1183,17 @@ static int index_no_dups(void **old, void *new) * it, then it will return an error **and also free the entry**. When * it replaces an existing entry, it will update the entry_ptr with the * actual entry in the index (and free the passed in one). + * trust_path is whether we use the given path, or whether (on case + * insensitive systems only) we try to canonicalize the given path to + * be within an existing directory. * trust_mode is whether we trust the mode in entry_ptr. */ static int index_insert( - git_index *index, git_index_entry **entry_ptr, int replace, bool trust_mode) + git_index *index, + git_index_entry **entry_ptr, + int replace, + bool trust_path, + bool trust_mode) { int error = 0; size_t path_length, position; @@ -1156,8 +1231,14 @@ static int index_insert( entry->mode = index_merge_mode(index, existing, entry->mode); } + /* canonicalize the directory name */ + if (!trust_path) + error = canonicalize_directory_path(index, entry); + /* look for tree / blob name collisions, removing conflicts if requested */ - error = check_file_directory_collision(index, entry, position, replace); + if (!error) + error = check_file_directory_collision(index, entry, position, replace); + if (error < 0) /* skip changes */; @@ -1258,7 +1339,7 @@ int git_index_add_frombuffer( git_oid_cpy(&entry->id, &id); entry->file_size = len; - if ((error = index_insert(index, &entry, 1, true)) < 0) + if ((error = index_insert(index, &entry, 1, true, true)) < 0) return error; /* Adding implies conflict was resolved, move conflict entries to REUC */ @@ -1317,7 +1398,7 @@ int git_index_add_bypath(git_index *index, const char *path) assert(index && path); if ((ret = index_entry_init(&entry, index, path)) == 0) - ret = index_insert(index, &entry, 1, false); + ret = index_insert(index, &entry, 1, false, false); /* If we were given a directory, let's see if it's a submodule */ if (ret < 0 && ret != GIT_EDIRECTORY) @@ -1343,7 +1424,7 @@ int git_index_add_bypath(git_index *index, const char *path) if ((ret = add_repo_as_submodule(&entry, index, path)) < 0) return ret; - if ((ret = index_insert(index, &entry, 1, false)) < 0) + if ((ret = index_insert(index, &entry, 1, false, false)) < 0) return ret; } else if (ret < 0) { return ret; @@ -1394,7 +1475,7 @@ int git_index_add(git_index *index, const git_index_entry *source_entry) } if ((ret = index_entry_dup(&entry, INDEX_OWNER(index), source_entry)) < 0 || - (ret = index_insert(index, &entry, 1, true)) < 0) + (ret = index_insert(index, &entry, 1, true, true)) < 0) return ret; git_tree_cache_invalidate_path(index->tree, entry->path); @@ -1555,7 +1636,7 @@ int git_index_conflict_add(git_index *index, /* Make sure stage is correct */ GIT_IDXENTRY_STAGE_SET(entries[i], i + 1); - if ((ret = index_insert(index, &entries[i], 0, true)) < 0) + if ((ret = index_insert(index, &entries[i], 0, true, true)) < 0) goto on_error; entries[i] = NULL; /* don't free if later entry fails */ diff --git a/tests/index/bypath.c b/tests/index/bypath.c index b607e1732..17bba6ad5 100644 --- a/tests/index/bypath.c +++ b/tests/index/bypath.c @@ -72,3 +72,151 @@ void test_index_bypath__add_hidden(void) cl_assert_equal_i(GIT_FILEMODE_BLOB, entry->mode); #endif } + +void test_index_bypath__add_honors_existing_case(void) +{ + const git_index_entry *entry; + + if (!cl_repo_get_bool(g_repo, "core.ignorecase")) + clar__skip(); + + cl_git_mkfile("submod2/just_a_dir/file1.txt", "This is a file"); + cl_git_mkfile("submod2/just_a_dir/file2.txt", "This is another file"); + cl_git_mkfile("submod2/just_a_dir/file3.txt", "This is another file"); + cl_git_mkfile("submod2/just_a_dir/file4.txt", "And another file"); + + cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/File1.txt")); + cl_git_pass(git_index_add_bypath(g_idx, "JUST_A_DIR/file2.txt")); + cl_git_pass(git_index_add_bypath(g_idx, "Just_A_Dir/FILE3.txt")); + + cl_assert(entry = git_index_get_bypath(g_idx, "just_a_dir/File1.txt", 0)); + cl_assert_equal_s("just_a_dir/File1.txt", entry->path); + + cl_assert(entry = git_index_get_bypath(g_idx, "JUST_A_DIR/file2.txt", 0)); + cl_assert_equal_s("just_a_dir/file2.txt", entry->path); + + cl_assert(entry = git_index_get_bypath(g_idx, "Just_A_Dir/FILE3.txt", 0)); + cl_assert_equal_s("just_a_dir/FILE3.txt", entry->path); + + cl_git_rewritefile("submod2/just_a_dir/file3.txt", "Rewritten"); + cl_git_pass(git_index_add_bypath(g_idx, "Just_A_Dir/file3.txt")); + + cl_assert(entry = git_index_get_bypath(g_idx, "Just_A_Dir/file3.txt", 0)); + cl_assert_equal_s("just_a_dir/FILE3.txt", entry->path); +} + +void test_index_bypath__add_honors_existing_case_2(void) +{ + git_index_entry dummy = { { 0 } }; + const git_index_entry *entry; + + if (!cl_repo_get_bool(g_repo, "core.ignorecase")) + clar__skip(); + + dummy.mode = GIT_FILEMODE_BLOB; + + /* note that `git_index_add` does no checking to canonical directories */ + dummy.path = "Just_a_dir/file0.txt"; + cl_git_pass(git_index_add(g_idx, &dummy)); + + dummy.path = "just_a_dir/fileA.txt"; + cl_git_pass(git_index_add(g_idx, &dummy)); + + dummy.path = "Just_A_Dir/fileB.txt"; + cl_git_pass(git_index_add(g_idx, &dummy)); + + dummy.path = "JUST_A_DIR/fileC.txt"; + cl_git_pass(git_index_add(g_idx, &dummy)); + + dummy.path = "just_A_dir/fileD.txt"; + cl_git_pass(git_index_add(g_idx, &dummy)); + + dummy.path = "JUST_a_DIR/fileE.txt"; + cl_git_pass(git_index_add(g_idx, &dummy)); + + cl_git_mkfile("submod2/just_a_dir/file1.txt", "This is a file"); + cl_git_mkfile("submod2/just_a_dir/file2.txt", "This is another file"); + cl_git_mkfile("submod2/just_a_dir/file3.txt", "This is another file"); + cl_git_mkfile("submod2/just_a_dir/file4.txt", "And another file"); + + cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/File1.txt")); + cl_git_pass(git_index_add_bypath(g_idx, "JUST_A_DIR/file2.txt")); + cl_git_pass(git_index_add_bypath(g_idx, "Just_A_Dir/FILE3.txt")); + cl_git_pass(git_index_add_bypath(g_idx, "JusT_A_DIR/FILE4.txt")); + + cl_assert(entry = git_index_get_bypath(g_idx, "just_a_dir/File1.txt", 0)); + cl_assert_equal_s("just_a_dir/File1.txt", entry->path); + + cl_assert(entry = git_index_get_bypath(g_idx, "JUST_A_DIR/file2.txt", 0)); + cl_assert_equal_s("JUST_A_DIR/file2.txt", entry->path); + + cl_assert(entry = git_index_get_bypath(g_idx, "Just_A_Dir/FILE3.txt", 0)); + cl_assert_equal_s("Just_A_Dir/FILE3.txt", entry->path); + + cl_git_rewritefile("submod2/just_a_dir/file3.txt", "Rewritten"); + cl_git_pass(git_index_add_bypath(g_idx, "Just_A_Dir/file3.txt")); + + cl_assert(entry = git_index_get_bypath(g_idx, "Just_A_Dir/file3.txt", 0)); + cl_assert_equal_s("Just_A_Dir/FILE3.txt", entry->path); +} + +void test_index_bypath__add_honors_existing_case_3(void) +{ + git_index_entry dummy = { { 0 } }; + const git_index_entry *entry; + + if (!cl_repo_get_bool(g_repo, "core.ignorecase")) + clar__skip(); + + dummy.mode = GIT_FILEMODE_BLOB; + + dummy.path = "just_a_dir/filea.txt"; + cl_git_pass(git_index_add(g_idx, &dummy)); + + dummy.path = "Just_A_Dir/fileB.txt"; + cl_git_pass(git_index_add(g_idx, &dummy)); + + dummy.path = "just_A_DIR/FILEC.txt"; + cl_git_pass(git_index_add(g_idx, &dummy)); + + dummy.path = "Just_a_DIR/FileD.txt"; + cl_git_pass(git_index_add(g_idx, &dummy)); + + cl_git_mkfile("submod2/JuSt_A_DiR/fILEE.txt", "This is a file"); + + cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/fILEE.txt")); + + cl_assert(entry = git_index_get_bypath(g_idx, "JUST_A_DIR/fILEE.txt", 0)); + cl_assert_equal_s("just_a_dir/fILEE.txt", entry->path); +} + +void test_index_bypath__add_honors_existing_case_4(void) +{ + git_index_entry dummy = { { 0 } }; + const git_index_entry *entry; + + if (!cl_repo_get_bool(g_repo, "core.ignorecase")) + clar__skip(); + + dummy.mode = GIT_FILEMODE_BLOB; + + dummy.path = "just_a_dir/a/b/c/d/e/file1.txt"; + cl_git_pass(git_index_add(g_idx, &dummy)); + + dummy.path = "just_a_dir/a/B/C/D/E/file2.txt"; + cl_git_pass(git_index_add(g_idx, &dummy)); + + cl_must_pass(p_mkdir("submod2/just_a_dir/a", 0777)); + cl_must_pass(p_mkdir("submod2/just_a_dir/a/b", 0777)); + cl_must_pass(p_mkdir("submod2/just_a_dir/a/b/z", 0777)); + cl_must_pass(p_mkdir("submod2/just_a_dir/a/b/z/y", 0777)); + cl_must_pass(p_mkdir("submod2/just_a_dir/a/b/z/y/x", 0777)); + + cl_git_mkfile("submod2/just_a_dir/a/b/z/y/x/FOO.txt", "This is a file"); + + cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/A/b/Z/y/X/foo.txt")); + + cl_assert(entry = git_index_get_bypath(g_idx, "just_a_dir/A/b/Z/y/X/foo.txt", 0)); + cl_assert_equal_s("just_a_dir/a/b/Z/y/X/foo.txt", entry->path); +} + From a32bc85e84090299ab9ba56b2d8f1761b7d91873 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 7 Aug 2015 12:43:49 -0500 Subject: [PATCH 107/450] git_index_add: allow case changing renames On case insensitive platforms, allow `git_index_add` to provide a new path for an existing index entry. Previously, we would maintain the case in an index entry without the ability to change it (except by removing an entry and re-adding it.) Higher-level functions (like `git_index_add_bypath` and `git_index_add_frombuffers`) continue to keep the old path for easier usage. --- CHANGELOG.md | 5 +++++ src/index.c | 41 ++++++++++++++++++++++++++--------------- tests/index/bypath.c | 20 ++++++++++++++++++++ tests/index/rename.c | 31 +++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f3c3ed6f..6ade3e3b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,11 @@ v0.23 + 1 with which to implement the transactional/atomic semantics for the configuration backend. +* `git_index_add` will now use the case as provided by the caller on + case insensitive systems. Previous versions would keep the case as + it existed in the index. This does not affect the higher-level + `git_index_add_bypath` or `git_index_add_frombuffer` functions. + v0.23 ------ diff --git a/src/index.c b/src/index.c index be86f16cb..3a7a3d80d 100644 --- a/src/index.c +++ b/src/index.c @@ -977,16 +977,27 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out, return 0; } -static void index_entry_cpy(git_index_entry *tgt, const git_index_entry *src) +static void index_entry_cpy( + git_index_entry *tgt, + git_index *index, + const git_index_entry *src, + bool update_path) { const char *tgt_path = tgt->path; memcpy(tgt, src, sizeof(*tgt)); - tgt->path = tgt_path; /* reset to existing path data */ + + /* keep the existing path buffer, but update the path to the one + * given by the caller, if we trust it. + */ + tgt->path = tgt_path; + + if (index->ignore_case && update_path) + memcpy((char *)tgt->path, src->path, strlen(tgt->path)); } static int index_entry_dup( git_index_entry **out, - git_repository *repo, + git_index *index, const git_index_entry *src) { git_index_entry *entry; @@ -996,10 +1007,10 @@ static int index_entry_dup( return 0; } - if (index_entry_create(&entry, repo, src->path) < 0) + if (index_entry_create(&entry, INDEX_OWNER(index), src->path) < 0) return -1; - index_entry_cpy(entry, src); + index_entry_cpy(entry, index, src, false); *out = entry; return 0; } @@ -1247,7 +1258,7 @@ static int index_insert( */ else if (existing) { if (replace) - index_entry_cpy(existing, entry); + index_entry_cpy(existing, index, entry, trust_path); index_entry_free(entry); *entry_ptr = entry = existing; } @@ -1327,7 +1338,7 @@ int git_index_add_frombuffer( return -1; } - if (index_entry_dup(&entry, INDEX_OWNER(index), source_entry) < 0) + if (index_entry_dup(&entry, index, source_entry) < 0) return -1; error = git_blob_create_frombuffer(&id, INDEX_OWNER(index), buffer, len); @@ -1474,7 +1485,7 @@ int git_index_add(git_index *index, const git_index_entry *source_entry) return -1; } - if ((ret = index_entry_dup(&entry, INDEX_OWNER(index), source_entry)) < 0 || + if ((ret = index_entry_dup(&entry, index, source_entry)) < 0 || (ret = index_insert(index, &entry, 1, true, true)) < 0) return ret; @@ -1600,9 +1611,9 @@ int git_index_conflict_add(git_index *index, assert (index); - if ((ret = index_entry_dup(&entries[0], INDEX_OWNER(index), ancestor_entry)) < 0 || - (ret = index_entry_dup(&entries[1], INDEX_OWNER(index), our_entry)) < 0 || - (ret = index_entry_dup(&entries[2], INDEX_OWNER(index), their_entry)) < 0) + if ((ret = index_entry_dup(&entries[0], index, ancestor_entry)) < 0 || + (ret = index_entry_dup(&entries[1], index, our_entry)) < 0 || + (ret = index_entry_dup(&entries[2], index, their_entry)) < 0) goto on_error; /* Validate entries */ @@ -2210,7 +2221,7 @@ static size_t read_entry( entry.path = (char *)path_ptr; - if (index_entry_dup(out, INDEX_OWNER(index), &entry) < 0) + if (index_entry_dup(out, index, &entry) < 0) return 0; return entry_size; @@ -2727,7 +2738,7 @@ static int read_tree_cb( entry->mode == old_entry->mode && git_oid_equal(&entry->id, &old_entry->id)) { - index_entry_cpy(entry, old_entry); + index_entry_cpy(entry, data->index, old_entry, false); entry->flags_extended = 0; } @@ -2859,7 +2870,7 @@ int git_index_read_index( if (diff < 0) { git_vector_insert(&remove_entries, (git_index_entry *)old_entry); } else if (diff > 0) { - if ((error = index_entry_dup(&entry, git_index_owner(index), new_entry)) < 0) + if ((error = index_entry_dup(&entry, index, new_entry)) < 0) goto done; git_vector_insert(&new_entries, entry); @@ -2870,7 +2881,7 @@ int git_index_read_index( if (git_oid_equal(&old_entry->id, &new_entry->id)) { git_vector_insert(&new_entries, (git_index_entry *)old_entry); } else { - if ((error = index_entry_dup(&entry, git_index_owner(index), new_entry)) < 0) + if ((error = index_entry_dup(&entry, index, new_entry)) < 0) goto done; git_vector_insert(&new_entries, entry); diff --git a/tests/index/bypath.c b/tests/index/bypath.c index 17bba6ad5..b152b0917 100644 --- a/tests/index/bypath.c +++ b/tests/index/bypath.c @@ -73,6 +73,26 @@ void test_index_bypath__add_hidden(void) #endif } +void test_index_bypath__add_keeps_existing_case(void) +{ + const git_index_entry *entry; + + if (!cl_repo_get_bool(g_repo, "core.ignorecase")) + clar__skip(); + + cl_git_mkfile("submod2/just_a_dir/file1.txt", "This is a file"); + cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/file1.txt")); + + cl_assert(entry = git_index_get_bypath(g_idx, "just_a_dir/file1.txt", 0)); + cl_assert_equal_s("just_a_dir/file1.txt", entry->path); + + cl_git_rewritefile("submod2/just_a_dir/file1.txt", "Updated!"); + cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/FILE1.txt")); + + cl_assert(entry = git_index_get_bypath(g_idx, "just_a_dir/FILE1.txt", 0)); + cl_assert_equal_s("just_a_dir/file1.txt", entry->path); +} + void test_index_bypath__add_honors_existing_case(void) { const git_index_entry *entry; diff --git a/tests/index/rename.c b/tests/index/rename.c index dd3cfa732..ebaa9b740 100644 --- a/tests/index/rename.c +++ b/tests/index/rename.c @@ -48,3 +48,34 @@ void test_index_rename__single_file(void) cl_fixture_cleanup("rename"); } + +void test_index_rename__casechanging(void) +{ + git_repository *repo; + git_index *index; + const git_index_entry *entry; + git_index_entry new = {{0}}; + + p_mkdir("rename", 0700); + + cl_git_pass(git_repository_init(&repo, "./rename", 0)); + cl_git_pass(git_repository_index(&index, repo)); + + cl_git_mkfile("./rename/lame.name.txt", "new_file\n"); + + cl_git_pass(git_index_add_bypath(index, "lame.name.txt")); + cl_assert_equal_i(1, git_index_entrycount(index)); + cl_assert((entry = git_index_get_bypath(index, "lame.name.txt", 0))); + + memcpy(&new, entry, sizeof(git_index_entry)); + new.path = "LAME.name.TXT"; + + cl_git_pass(git_index_add(index, &new)); + cl_assert((entry = git_index_get_bypath(index, "LAME.name.TXT", 0))); + + if (cl_repo_get_bool(repo, "core.ignorecase")) + cl_assert_equal_i(1, git_index_entrycount(index)); + else + cl_assert_equal_i(2, git_index_entrycount(index)); +} + From 24f5b4e155f701c86ad6daecb24b4af83aa183d6 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Tue, 8 Sep 2015 13:34:42 -0400 Subject: [PATCH 108/450] Drop extra_http_headers from git_remote --- include/git2/remote.h | 3 --- src/netops.h | 1 - src/remote.c | 12 ------------ src/remote.h | 1 - src/transports/http.c | 1 - 5 files changed, 18 deletions(-) diff --git a/include/git2/remote.h b/include/git2/remote.h index 40adca0ce..444fe5276 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -230,9 +230,6 @@ GIT_EXTERN(size_t) git_remote_refspec_count(const git_remote *remote); */ GIT_EXTERN(const git_refspec *)git_remote_get_refspec(const git_remote *remote, size_t n); -GIT_EXTERN(int) git_remote_extra_http_headers(const git_remote *remote, git_strarray *extra_http_headers); -GIT_EXTERN(int) git_remote_set_extra_http_headers(git_remote *remote, const git_strarray extra_http_headers); - /** * Open a connection to a remote * diff --git a/src/netops.h b/src/netops.h index ea1072518..b7170a0f2 100644 --- a/src/netops.h +++ b/src/netops.h @@ -70,7 +70,6 @@ typedef struct gitno_connection_data { char *user; char *pass; bool use_ssl; - git_strarray *extra_headers; } gitno_connection_data; /* diff --git a/src/remote.c b/src/remote.c index b7d82e708..7404bf49f 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1643,8 +1643,6 @@ void git_remote_free(git_remote *remote) free_refspecs(&remote->passive_refspecs); git_vector_free(&remote->passive_refspecs); - git_strarray_free(&remote->extra_http_headers); - git_push_free(remote->push); git__free(remote->url); git__free(remote->pushurl); @@ -2154,16 +2152,6 @@ size_t git_remote_refspec_count(const git_remote *remote) return remote->refspecs.length; } -int git_remote_extra_http_headers(const git_remote *remote, git_strarray *extra_http_headers) -{ - return git_strarray_copy(extra_http_headers, &remote->extra_http_headers); -} - -int git_remote_set_extra_http_headers(git_remote *remote, const git_strarray extra_http_headers) -{ - return git_strarray_copy(&remote->extra_http_headers, &extra_http_headers); -} - const git_refspec *git_remote_get_refspec(const git_remote *remote, size_t n) { return git_vector_get(&remote->refspecs, n); diff --git a/src/remote.h b/src/remote.h index 3a15288f3..e696997f4 100644 --- a/src/remote.h +++ b/src/remote.h @@ -32,7 +32,6 @@ struct git_remote { git_remote_autotag_option_t download_tags; int prune_refs; int passed_refspecs; - git_strarray extra_http_headers; }; const char* git_remote__urlfordirection(struct git_remote *remote, int direction); diff --git a/src/transports/http.c b/src/transports/http.c index 3cd5632f8..d348310c1 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -974,7 +974,6 @@ static int http_action( if ((!t->connection_data.host || !t->connection_data.port || !t->connection_data.path) && (ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0) return ret; - t->connection_data.extra_headers = &t->owner->owner->extra_http_headers; if ((ret = http_connect(t)) < 0) return ret; From 9da32a625564064908b61530318a1366ffff8217 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Tue, 8 Sep 2015 10:18:54 -0400 Subject: [PATCH 109/450] Add custom_headers to git_push_options --- include/git2/remote.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/git2/remote.h b/include/git2/remote.h index 444fe5276..a39e5a415 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -585,6 +585,11 @@ typedef struct { * Callbacks to use for this push operation */ git_remote_callbacks callbacks; + + /** + * Extra headers for this push operation + */ + git_strarray custom_headers; } git_push_options; #define GIT_PUSH_OPTIONS_VERSION 1 From 4f2b6093a64ead32f51a886186496821e003cee5 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Tue, 8 Sep 2015 13:53:41 -0400 Subject: [PATCH 110/450] Tell the git_transport about the custom_headers --- include/git2/remote.h | 3 ++- include/git2/sys/transport.h | 5 +++++ src/push.c | 3 ++- src/push.h | 1 + src/remote.c | 21 ++++++++++++++++----- tests/network/remote/defaultbranch.c | 6 +++--- tests/network/remote/local.c | 8 ++++---- tests/network/remote/remotes.c | 6 +++--- tests/online/fetch.c | 12 ++++++------ tests/online/push.c | 2 +- 10 files changed, 43 insertions(+), 24 deletions(-) diff --git a/include/git2/remote.h b/include/git2/remote.h index a39e5a415..9237ca255 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -241,9 +241,10 @@ GIT_EXTERN(const git_refspec *)git_remote_get_refspec(const git_remote *remote, * @param direction GIT_DIRECTION_FETCH if you want to fetch or * GIT_DIRECTION_PUSH if you want to push * @param callbacks the callbacks to use for this connection + * @param custom_headers extra HTTP headers to use in this connection * @return 0 or an error code */ -GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks); +GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_strarray *custom_headers); /** * Get the remote repository's reference advertisement list diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h index 4a75b0832..ca8617f3f 100644 --- a/include/git2/sys/transport.h +++ b/include/git2/sys/transport.h @@ -40,6 +40,11 @@ struct git_transport { git_transport_certificate_check_cb certificate_check_cb, void *payload); + /* Set custom headers for HTTP requests */ + int (*set_custom_headers)( + git_transport *transport, + const git_strarray *custom_headers); + /* Connect the transport to the remote repository, using the given * direction. */ int (*connect)( diff --git a/src/push.c b/src/push.c index a0d8a0550..3c9fa2f1b 100644 --- a/src/push.c +++ b/src/push.c @@ -73,6 +73,7 @@ int git_push_set_options(git_push *push, const git_push_options *opts) GITERR_CHECK_VERSION(opts, GIT_PUSH_OPTIONS_VERSION, "git_push_options"); push->pb_parallelism = opts->pb_parallelism; + push->custom_headers = &opts->custom_headers; return 0; } @@ -638,7 +639,7 @@ int git_push_finish(git_push *push, const git_remote_callbacks *callbacks) int error; if (!git_remote_connected(push->remote) && - (error = git_remote_connect(push->remote, GIT_DIRECTION_PUSH, callbacks)) < 0) + (error = git_remote_connect(push->remote, GIT_DIRECTION_PUSH, callbacks, push->custom_headers)) < 0) return error; if ((error = filter_refs(push->remote)) < 0 || diff --git a/src/push.h b/src/push.h index a847ee0d0..e32ad2f4d 100644 --- a/src/push.h +++ b/src/push.h @@ -38,6 +38,7 @@ struct git_push { /* options */ unsigned pb_parallelism; + const git_strarray *custom_headers; }; /** diff --git a/src/remote.c b/src/remote.c index 7404bf49f..9e907d281 100644 --- a/src/remote.c +++ b/src/remote.c @@ -687,7 +687,15 @@ int set_transport_callbacks(git_transport *t, const git_remote_callbacks *cbs) cbs->certificate_check, cbs->payload); } -int git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks) +int set_transport_custom_headers(git_transport *t, const git_strarray *custom_headers) +{ + if (!t->set_custom_headers || !custom_headers) + return 0; + + return t->set_custom_headers(t, custom_headers); +} + +int git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_strarray *custom_headers) { git_transport *t; const char *url; @@ -726,6 +734,9 @@ int git_remote_connect(git_remote *remote, git_direction direction, const git_re if (!t && (error = git_transport_new(&t, remote, url)) < 0) return error; + if ((error = set_transport_custom_headers(t, custom_headers)) != 0) + goto on_error; + if ((error = set_transport_callbacks(t, callbacks)) < 0 || (error = t->connect(t, url, credentials, payload, direction, flags)) != 0) goto on_error; @@ -893,7 +904,7 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const } if (!git_remote_connected(remote) && - (error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs)) < 0) + (error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, NULL)) < 0) goto on_error; if (ls_to_vector(&refs, remote) < 0) @@ -966,7 +977,7 @@ int git_remote_fetch( } /* Connect and download everything */ - if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs)) != 0) + if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, NULL)) != 0) return error; error = git_remote_download(remote, refspecs, opts); @@ -2384,7 +2395,7 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi cbs = &opts->callbacks; if (!git_remote_connected(remote) && - (error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs)) < 0) + (error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, &opts->custom_headers)) < 0) goto cleanup; free_refspecs(&remote->active_refspecs); @@ -2441,7 +2452,7 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_ assert(remote && refspecs); - if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs)) < 0) + if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, &opts->custom_headers)) < 0) return error; if ((error = git_remote_upload(remote, refspecs, opts)) < 0) diff --git a/tests/network/remote/defaultbranch.c b/tests/network/remote/defaultbranch.c index e83755ef6..5edd79fb8 100644 --- a/tests/network/remote/defaultbranch.c +++ b/tests/network/remote/defaultbranch.c @@ -26,7 +26,7 @@ static void assert_default_branch(const char *should) { git_buf name = GIT_BUF_INIT; - cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH, NULL)); + cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH, NULL, NULL)); cl_git_pass(git_remote_default_branch(&name, g_remote)); cl_assert_equal_s(should, name.ptr); git_buf_free(&name); @@ -57,7 +57,7 @@ void test_network_remote_defaultbranch__no_default_branch(void) git_buf buf = GIT_BUF_INIT; cl_git_pass(git_remote_create(&remote_b, g_repo_b, "self", git_repository_path(g_repo_b))); - cl_git_pass(git_remote_connect(remote_b, GIT_DIRECTION_FETCH, NULL)); + cl_git_pass(git_remote_connect(remote_b, GIT_DIRECTION_FETCH, NULL, NULL)); cl_git_pass(git_remote_ls(&heads, &len, remote_b)); cl_assert_equal_i(0, len); @@ -80,7 +80,7 @@ void test_network_remote_defaultbranch__detached_sharing_nonbranch_id(void) cl_git_pass(git_reference_create(&ref, g_repo_a, "refs/foo/bar", &id, 1, NULL)); git_reference_free(ref); - cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH, NULL)); + cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH, NULL, NULL)); cl_git_fail_with(GIT_ENOTFOUND, git_remote_default_branch(&buf, g_remote)); cl_git_pass(git_clone(&cloned_repo, git_repository_path(g_repo_a), "./local-detached", NULL)); diff --git a/tests/network/remote/local.c b/tests/network/remote/local.c index 5d726c958..4d990ab71 100644 --- a/tests/network/remote/local.c +++ b/tests/network/remote/local.c @@ -40,7 +40,7 @@ static void connect_to_local_repository(const char *local_repository) git_buf_sets(&file_path_buf, cl_git_path_url(local_repository)); cl_git_pass(git_remote_create_anonymous(&remote, repo, git_buf_cstr(&file_path_buf))); - cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL)); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL)); } void test_network_remote_local__connected(void) @@ -214,7 +214,7 @@ void test_network_remote_local__push_to_bare_remote(void) /* Connect to the bare repo */ cl_git_pass(git_remote_create_anonymous(&localremote, repo, "./localbare.git")); - cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL)); + cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL, NULL)); /* Try to push */ cl_git_pass(git_remote_upload(localremote, &push_array, NULL)); @@ -253,7 +253,7 @@ void test_network_remote_local__push_to_bare_remote_with_file_url(void) /* Connect to the bare repo */ cl_git_pass(git_remote_create_anonymous(&localremote, repo, url)); - cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL)); + cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL, NULL)); /* Try to push */ cl_git_pass(git_remote_upload(localremote, &push_array, NULL)); @@ -290,7 +290,7 @@ void test_network_remote_local__push_to_non_bare_remote(void) /* Connect to the bare repo */ cl_git_pass(git_remote_create_anonymous(&localremote, repo, "./localnonbare")); - cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL)); + cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL, NULL)); /* Try to push */ cl_git_fail_with(GIT_EBAREREPO, git_remote_upload(localremote, &push_array, NULL)); diff --git a/tests/network/remote/remotes.c b/tests/network/remote/remotes.c index 2fa21d460..46abc6d33 100644 --- a/tests/network/remote/remotes.c +++ b/tests/network/remote/remotes.c @@ -93,7 +93,7 @@ void test_network_remote_remotes__error_when_no_push_available(void) cl_git_pass(git_remote_create_anonymous(&r, _repo, cl_fixture("testrepo.git"))); callbacks.transport = git_transport_local; - cl_git_pass(git_remote_connect(r, GIT_DIRECTION_PUSH, &callbacks)); + cl_git_pass(git_remote_connect(r, GIT_DIRECTION_PUSH, &callbacks, NULL)); /* Make sure that push is really not available */ r->transport->push = NULL; @@ -359,7 +359,7 @@ void test_network_remote_remotes__can_load_with_an_empty_url(void) cl_assert(remote->url == NULL); cl_assert(remote->pushurl == NULL); - cl_git_fail(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL)); + cl_git_fail(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL)); cl_assert(giterr_last() != NULL); cl_assert(giterr_last()->klass == GITERR_INVALID); @@ -376,7 +376,7 @@ void test_network_remote_remotes__can_load_with_only_an_empty_pushurl(void) cl_assert(remote->url == NULL); cl_assert(remote->pushurl == NULL); - cl_git_fail(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL)); + cl_git_fail(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL)); git_remote_free(remote); } diff --git a/tests/online/fetch.c b/tests/online/fetch.c index 72e7c24e3..c12df069f 100644 --- a/tests/online/fetch.c +++ b/tests/online/fetch.c @@ -81,11 +81,11 @@ void test_online_fetch__fetch_twice(void) { git_remote *remote; cl_git_pass(git_remote_create(&remote, _repo, "test", "git://github.com/libgit2/TestGitRepository.git")); - cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL)); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL)); cl_git_pass(git_remote_download(remote, NULL, NULL)); git_remote_disconnect(remote); - git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL); + git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL); cl_git_pass(git_remote_download(remote, NULL, NULL)); git_remote_disconnect(remote); @@ -117,7 +117,7 @@ void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date cl_git_pass(git_repository_open(&_repository, "./fetch/lg2")); cl_git_pass(git_remote_lookup(&remote, _repository, "origin")); - cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL)); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL)); cl_assert_equal_i(false, invoked); @@ -155,7 +155,7 @@ void test_online_fetch__can_cancel(void) options.callbacks.transfer_progress = cancel_at_half; options.callbacks.payload = &bytes_received; - cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL)); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL)); cl_git_fail_with(git_remote_download(remote, NULL, &options), -4321); git_remote_disconnect(remote); git_remote_free(remote); @@ -169,7 +169,7 @@ void test_online_fetch__ls_disconnected(void) cl_git_pass(git_remote_create(&remote, _repo, "test", "http://github.com/libgit2/TestGitRepository.git")); - cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL)); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL)); cl_git_pass(git_remote_ls(&refs, &refs_len_before, remote)); git_remote_disconnect(remote); cl_git_pass(git_remote_ls(&refs, &refs_len_after, remote)); @@ -187,7 +187,7 @@ void test_online_fetch__remote_symrefs(void) cl_git_pass(git_remote_create(&remote, _repo, "test", "http://github.com/libgit2/TestGitRepository.git")); - cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL)); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL)); git_remote_disconnect(remote); cl_git_pass(git_remote_ls(&refs, &refs_len, remote)); diff --git a/tests/online/push.c b/tests/online/push.c index 0b0892c97..13d364d30 100644 --- a/tests/online/push.c +++ b/tests/online/push.c @@ -373,7 +373,7 @@ void test_online_push__initialize(void) record_callbacks_data_clear(&_record_cbs_data); - cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH, &_record_cbs)); + cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH, &_record_cbs, NULL)); /* Clean up previously pushed branches. Fails if receive.denyDeletes is * set on the remote. Also, on Git 1.7.0 and newer, you must run From 276f6aa08d4cb35ad647b24bfa254b99af89e076 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Tue, 8 Sep 2015 14:00:37 -0400 Subject: [PATCH 111/450] Hook up the custom_headers to the http transport --- src/transports/http.c | 7 +++---- src/transports/smart.c | 12 ++++++++++++ src/transports/smart.h | 1 + 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/transports/http.c b/src/transports/http.c index d348310c1..764c6a97e 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -211,10 +211,9 @@ static int gen_request( } else git_buf_puts(buf, "Accept: */*\r\n"); - if (t->connection_data.extra_headers) { - for (i = 0; i < t->connection_data.extra_headers->count; i++) { - git_buf_puts(buf, t->connection_data.extra_headers->strings[i]); - git_buf_puts(buf, "\r\n"); + if (t->owner->custom_headers) { + for (i = 0; i < t->owner->custom_headers->count; i++) { + git_buf_printf(buf, "%s\r\n", t->owner->custom_headers->strings[i]); } } diff --git a/src/transports/smart.c b/src/transports/smart.c index 31a2dec7b..15f45e11c 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -66,6 +66,17 @@ static int git_smart__set_callbacks( return 0; } +static int git_smart__set_custom_headers( + git_transport *transport, + const git_strarray *custom_headers) +{ + transport_smart *t = (transport_smart *)transport; + + t->custom_headers = custom_headers; + + return 0; +} + int git_smart__update_heads(transport_smart *t, git_vector *symrefs) { size_t i; @@ -399,6 +410,7 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param) t->parent.version = GIT_TRANSPORT_VERSION; t->parent.set_callbacks = git_smart__set_callbacks; + t->parent.set_custom_headers = git_smart__set_custom_headers; t->parent.connect = git_smart__connect; t->parent.close = git_smart__close; t->parent.free = git_smart__free; diff --git a/src/transports/smart.h b/src/transports/smart.h index 4c728c7cc..2c87e0200 100644 --- a/src/transports/smart.h +++ b/src/transports/smart.h @@ -139,6 +139,7 @@ typedef struct { git_transport_message_cb error_cb; git_transport_certificate_check_cb certificate_check_cb; void *message_cb_payload; + const git_strarray *custom_headers; git_smart_subtransport *wrapped; git_smart_subtransport_stream *current_stream; transport_smart_caps caps; From 80ee25434d076b87d1a34e2dee467600013ae4ee Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Tue, 8 Sep 2015 13:38:22 -0400 Subject: [PATCH 112/450] Teach winhttp about the extra headers --- src/transports/winhttp.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index 0c43c4b0b..d28762db1 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -409,6 +409,23 @@ static int winhttp_stream_connect(winhttp_stream *s) } } + if (t->owner->custom_headers) { + for (i = 0; i < t->owner->custom_headers->count; i++) { + git_buf_clear(&buf); + git_buf_puts(&buf, t->owner->custom_headers->strings[i]); + if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) { + giterr_set(GITERR_OS, "Failed to convert custom header to wide characters"); + goto on_error; + } + + if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L, + WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { + giterr_set(GITERR_OS, "Failed to add a header to the request"); + goto on_error; + } + } + } + /* If requested, disable certificate validation */ if (t->connection_data.use_ssl) { int flags; From 5d7cd57f9950fca5c60176c5b5a3673358386a05 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Tue, 8 Sep 2015 14:15:29 -0400 Subject: [PATCH 113/450] Update another call to git_remote_connect --- examples/network/ls-remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/network/ls-remote.c b/examples/network/ls-remote.c index 21026562f..c9da79f5f 100644 --- a/examples/network/ls-remote.c +++ b/examples/network/ls-remote.c @@ -26,7 +26,7 @@ static int use_remote(git_repository *repo, char *name) */ callbacks.credentials = cred_acquire_cb; - error = git_remote_connect(remote, GIT_DIRECTION_FETCH, &callbacks); + error = git_remote_connect(remote, GIT_DIRECTION_FETCH, &callbacks, NULL); if (error < 0) goto cleanup; From c82c2ba60f63095a3b418424d858277f5800b914 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Tue, 8 Sep 2015 14:17:59 -0400 Subject: [PATCH 114/450] o i --- src/transports/winhttp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index d28762db1..8e2bdd44f 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -277,6 +277,7 @@ static int winhttp_stream_connect(winhttp_stream *s) unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS; int default_timeout = TIMEOUT_INFINITE; int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT; + int i; /* Prepare URL */ git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_url); From ea3f2c296aa39b9609db3d75c96837518beb12a3 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 8 Sep 2015 14:35:53 -0400 Subject: [PATCH 115/450] filebuf: ensure we can lock a hidden file --- tests/core/filebuf.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/core/filebuf.c b/tests/core/filebuf.c index 39d98ff7e..915e3cc34 100644 --- a/tests/core/filebuf.c +++ b/tests/core/filebuf.c @@ -204,3 +204,29 @@ void test_core_filebuf__symlink_depth(void) cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES)); } + +void test_core_filebuf__hidden_file(void) +{ +#ifndef GIT_WIN32 + cl_skip(); +#else + git_filebuf file = GIT_FILEBUF_INIT; + char *dir = "hidden", *test = "hidden/test"; + bool hidden; + + cl_git_pass(p_mkdir(dir, 0666)); + cl_git_mkfile(test, "dummy content"); + + cl_git_pass(git_win32__set_hidden(test, true)); + cl_git_pass(git_win32__hidden(&hidden, test)); + cl_assert(hidden); + + cl_git_pass(git_filebuf_open(&file, test, 0, 0666)); + + cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); + + cl_git_pass(git_filebuf_commit(&file)); + + git_filebuf_cleanup(&file); +#endif +} From 8e736a73cac81f09197eca32bf578f74ab21e97e Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 8 Sep 2015 15:48:44 -0400 Subject: [PATCH 116/450] futils: ensure we can write a hidden file --- tests/core/futils.c | 68 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 tests/core/futils.c diff --git a/tests/core/futils.c b/tests/core/futils.c new file mode 100644 index 000000000..e7f7154ed --- /dev/null +++ b/tests/core/futils.c @@ -0,0 +1,68 @@ +#include "clar_libgit2.h" +#include "fileops.h" + +// Fixture setup and teardown +void test_core_futils__initialize(void) +{ + cl_must_pass(p_mkdir("futils", 0777)); +} + +void test_core_futils__cleanup(void) +{ + cl_fixture_cleanup("futils"); +} + +void test_core_futils__writebuffer(void) +{ + git_buf out = GIT_BUF_INIT, + append = GIT_BUF_INIT; + + /* create a new file */ + git_buf_puts(&out, "hello!\n"); + git_buf_printf(&out, "this is a %s\n", "test"); + + cl_git_pass(git_futils_writebuffer(&out, "futils/test-file", O_RDWR|O_CREAT, 0666)); + + cl_assert_equal_file(out.ptr, out.size, "futils/test-file"); + + /* append some more data */ + git_buf_puts(&append, "And some more!\n"); + git_buf_put(&out, append.ptr, append.size); + + cl_git_pass(git_futils_writebuffer(&append, "futils/test-file", O_RDWR|O_APPEND, 0666)); + + cl_assert_equal_file(out.ptr, out.size, "futils/test-file"); + + git_buf_free(&out); + git_buf_free(&append); +} + +void test_core_futils__write_hidden_file(void) +{ +#ifndef GIT_WIN32 + cl_skip(); +#else + git_buf out = GIT_BUF_INIT, append = GIT_BUF_INIT; + bool hidden; + + git_buf_puts(&out, "hidden file.\n"); + git_futils_writebuffer(&out, "futils/test-file", O_RDWR | O_CREAT, 0666); + + cl_git_pass(git_win32__set_hidden("futils/test-file", true)); + + /* append some more data */ + git_buf_puts(&append, "And some more!\n"); + git_buf_put(&out, append.ptr, append.size); + + cl_git_pass(git_futils_writebuffer(&append, "futils/test-file", O_RDWR | O_APPEND, 0666)); + + cl_assert_equal_file(out.ptr, out.size, "futils/test-file"); + + cl_git_pass(git_win32__hidden(&hidden, "futils/test-file")); + cl_assert(hidden); + + git_buf_free(&out); + git_buf_free(&append); +#endif +} + From c49126c87f1124d0928d68d9191535e5ef4ecd25 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Thu, 10 Sep 2015 08:34:35 -0400 Subject: [PATCH 117/450] Accept custom headers for fetch too --- include/git2/remote.h | 5 +++++ src/remote.c | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/include/git2/remote.h b/include/git2/remote.h index 9237ca255..c42d96710 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -547,6 +547,11 @@ typedef struct { * The default is to auto-follow tags. */ git_remote_autotag_option_t download_tags; + + /** + * Extra headers for this fetch operation + */ + git_strarray custom_headers; } git_fetch_options; #define GIT_FETCH_OPTIONS_VERSION 1 diff --git a/src/remote.c b/src/remote.c index 9e907d281..a374a84b3 100644 --- a/src/remote.c +++ b/src/remote.c @@ -895,16 +895,18 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const size_t i; git_vector *to_active, specs = GIT_VECTOR_INIT, refs = GIT_VECTOR_INIT; const git_remote_callbacks *cbs = NULL; + const git_strarray *custom_headers = NULL; assert(remote); if (opts) { GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); cbs = &opts->callbacks; + custom_headers = &opts->custom_headers; } if (!git_remote_connected(remote) && - (error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, NULL)) < 0) + (error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, custom_headers)) < 0) goto on_error; if (ls_to_vector(&refs, remote) < 0) @@ -968,16 +970,18 @@ int git_remote_fetch( bool prune = false; git_buf reflog_msg_buf = GIT_BUF_INIT; const git_remote_callbacks *cbs = NULL; + const git_strarray *custom_headers = NULL; if (opts) { GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); cbs = &opts->callbacks; + custom_headers = &opts->custom_headers; update_fetchhead = opts->update_fetchhead; tagopt = opts->download_tags; } /* Connect and download everything */ - if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, NULL)) != 0) + if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, custom_headers)) != 0) return error; error = git_remote_download(remote, refspecs, opts); From 35969c6839c0659ed3ced67bfc5b963662a721f2 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Thu, 10 Sep 2015 08:58:23 -0400 Subject: [PATCH 118/450] Ignore NULL headers --- src/transports/http.c | 3 ++- src/transports/winhttp.c | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/transports/http.c b/src/transports/http.c index 764c6a97e..73ea05043 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -213,7 +213,8 @@ static int gen_request( if (t->owner->custom_headers) { for (i = 0; i < t->owner->custom_headers->count; i++) { - git_buf_printf(buf, "%s\r\n", t->owner->custom_headers->strings[i]); + if (t->owner->custom_headers->strings[i]) + git_buf_printf(buf, "%s\r\n", t->owner->custom_headers->strings[i]); } } diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index 8e2bdd44f..5b00d9091 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -412,17 +412,19 @@ static int winhttp_stream_connect(winhttp_stream *s) if (t->owner->custom_headers) { for (i = 0; i < t->owner->custom_headers->count; i++) { - git_buf_clear(&buf); - git_buf_puts(&buf, t->owner->custom_headers->strings[i]); - if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) { - giterr_set(GITERR_OS, "Failed to convert custom header to wide characters"); - goto on_error; - } + if (t->owner->custom_headers->strings[i]) { + git_buf_clear(&buf); + git_buf_puts(&buf, t->owner->custom_headers->strings[i]); + if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) { + giterr_set(GITERR_OS, "Failed to convert custom header to wide characters"); + goto on_error; + } - if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L, - WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { - giterr_set(GITERR_OS, "Failed to add a header to the request"); - goto on_error; + if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L, + WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { + giterr_set(GITERR_OS, "Failed to add a header to the request"); + goto on_error; + } } } } From 8c876fa91d0783bf4e1f0e624b2dfa86574de4e3 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Thu, 10 Sep 2015 09:11:16 -0400 Subject: [PATCH 119/450] Validate custom http headers --- src/transports/smart.c | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/transports/smart.c b/src/transports/smart.c index 15f45e11c..ee49729aa 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -66,11 +66,64 @@ static int git_smart__set_callbacks( return 0; } +bool is_valid(const char *custom_header) +{ + const char *c; + int name_len; + + if (custom_header == NULL) + return true; + + // Disallow \r and \n + c = strchr(custom_header, '\r'); + if (c != NULL) + return false; + c = strchr(custom_header, '\n'); + if (c != NULL) + return false; + + // Require a header name followed by : + c = strchr(custom_header, ':'); + if (c == NULL) + return false; + name_len = c - custom_header; + if (name_len < 1) + return false; + + // Disallow headers that we set + return git__strncmp("User-Agent", custom_header, name_len) == 0 && + git__strncmp("Host", custom_header, name_len) == 0 && + git__strncmp("Accept", custom_header, name_len) == 0 && + git__strncmp("Content-Type", custom_header, name_len) == 0 && + git__strncmp("Transfer-Encoding", custom_header, name_len) == 0 && + git__strncmp("Content-Length", custom_header, name_len) == 0; +} + +const char *find_invalid_header(const git_strarray *custom_headers) +{ + size_t i; + + if (custom_headers == NULL || custom_headers->count == 0) + return NULL; + + for (i = 0; i < custom_headers->count; i++) + if (!is_valid(custom_headers->strings[i])) + return custom_headers->strings[i]; + + return NULL; +} + static int git_smart__set_custom_headers( git_transport *transport, const git_strarray *custom_headers) { transport_smart *t = (transport_smart *)transport; + const char *invalid_header = find_invalid_header(custom_headers); + + if (invalid_header != NULL) { + giterr_set(GITERR_INVALID, "Illegal HTTP header '%s'", invalid_header); + return -1; + } t->custom_headers = custom_headers; From 66d90e7098ee2da76ff3351a305a17a38fb9282b Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Thu, 10 Sep 2015 09:14:20 -0400 Subject: [PATCH 120/450] More specific names --- src/transports/smart.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/transports/smart.c b/src/transports/smart.c index ee49729aa..b4f8578db 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -66,7 +66,7 @@ static int git_smart__set_callbacks( return 0; } -bool is_valid(const char *custom_header) +bool is_valid_custom_header(const char *custom_header) { const char *c; int name_len; @@ -99,7 +99,7 @@ bool is_valid(const char *custom_header) git__strncmp("Content-Length", custom_header, name_len) == 0; } -const char *find_invalid_header(const git_strarray *custom_headers) +const char *find_invalid_custom_header(const git_strarray *custom_headers) { size_t i; @@ -107,7 +107,7 @@ const char *find_invalid_header(const git_strarray *custom_headers) return NULL; for (i = 0; i < custom_headers->count; i++) - if (!is_valid(custom_headers->strings[i])) + if (!is_valid_custom_header(custom_headers->strings[i])) return custom_headers->strings[i]; return NULL; @@ -118,7 +118,7 @@ static int git_smart__set_custom_headers( const git_strarray *custom_headers) { transport_smart *t = (transport_smart *)transport; - const char *invalid_header = find_invalid_header(custom_headers); + const char *invalid_header = find_invalid_custom_header(custom_headers); if (invalid_header != NULL) { giterr_set(GITERR_INVALID, "Illegal HTTP header '%s'", invalid_header); From 3245896bb7527cb42d48faf68f33858c887f2b3d Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Thu, 10 Sep 2015 13:18:26 -0400 Subject: [PATCH 121/450] Add a test for custom header validation Also, *some* custom headers actually are valid. --- src/transports/smart.c | 18 ++++++++++++------ tests/online/clone.c | 27 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/transports/smart.c b/src/transports/smart.c index b4f8578db..8388d9dc5 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -66,6 +66,10 @@ static int git_smart__set_callbacks( return 0; } +#define forbid_custom_header(disallowed_name) \ + if (strncmp(disallowed_name, custom_header, name_len) == 0) \ + return false + bool is_valid_custom_header(const char *custom_header) { const char *c; @@ -91,12 +95,14 @@ bool is_valid_custom_header(const char *custom_header) return false; // Disallow headers that we set - return git__strncmp("User-Agent", custom_header, name_len) == 0 && - git__strncmp("Host", custom_header, name_len) == 0 && - git__strncmp("Accept", custom_header, name_len) == 0 && - git__strncmp("Content-Type", custom_header, name_len) == 0 && - git__strncmp("Transfer-Encoding", custom_header, name_len) == 0 && - git__strncmp("Content-Length", custom_header, name_len) == 0; + forbid_custom_header("User-Agent"); + forbid_custom_header("Host"); + forbid_custom_header("Accept"); + forbid_custom_header("Content-Type"); + forbid_custom_header("Transfer-Encoding"); + forbid_custom_header("Content-Length"); + + return true; } const char *find_invalid_custom_header(const git_strarray *custom_headers) diff --git a/tests/online/clone.c b/tests/online/clone.c index 225b3abe2..b84be405c 100644 --- a/tests/online/clone.c +++ b/tests/online/clone.c @@ -213,6 +213,33 @@ void test_online_clone__custom_remote_callbacks(void) cl_assert(callcount > 0); } +void test_online_clone__custom_headers(void) +{ + char *empty_header = ""; + char *unnamed_header = "this is a header about nothing"; + char *newlines = "X-Custom: almost OK\n"; + char *conflict = "Accept: defined-by-git"; + char *ok = "X-Custom: this should be ok"; + + g_options.fetch_opts.custom_headers.count = 1; + + g_options.fetch_opts.custom_headers.strings = &empty_header; + cl_git_fail(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options)); + + g_options.fetch_opts.custom_headers.strings = &unnamed_header; + cl_git_fail(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options)); + + g_options.fetch_opts.custom_headers.strings = &newlines; + cl_git_fail(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options)); + + g_options.fetch_opts.custom_headers.strings = &conflict; + cl_git_fail(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options)); + + /* Finally, we got it right! */ + g_options.fetch_opts.custom_headers.strings = &ok; + cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options)); +} + static int cred_failure_cb( git_cred **cred, const char *url, From d29c5412aa91b279b1b2db4fe57dd6fe71272b91 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Thu, 10 Sep 2015 14:16:39 -0400 Subject: [PATCH 122/450] Avoid segfault when opts == NULL --- src/remote.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/remote.c b/src/remote.c index a374a84b3..ce6e13b3d 100644 --- a/src/remote.c +++ b/src/remote.c @@ -2392,14 +2392,17 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi git_push *push; git_refspec *spec; const git_remote_callbacks *cbs = NULL; + const git_strarray *custom_headers = NULL; assert(remote); - if (opts) + if (opts) { cbs = &opts->callbacks; + custom_headers = &opts->custom_headers; + } if (!git_remote_connected(remote) && - (error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, &opts->custom_headers)) < 0) + (error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, custom_headers)) < 0) goto cleanup; free_refspecs(&remote->active_refspecs); @@ -2448,15 +2451,17 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_ { int error; const git_remote_callbacks *cbs = NULL; + const git_strarray *custom_headers = NULL; if (opts) { GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); cbs = &opts->callbacks; + custom_headers = &opts->custom_headers; } assert(remote && refspecs); - if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, &opts->custom_headers)) < 0) + if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, custom_headers)) < 0) return error; if ((error = git_remote_upload(remote, refspecs, opts)) < 0) From f17525b0ff4aa6b0909c56dff80b8bb0b68d7be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 10 Aug 2015 18:36:27 +0200 Subject: [PATCH 123/450] submodule: refactor to be more explicit in the search When searching for information about a submdoule, let's be more explicit in what we expect to find. We currently insert a submodule into the map and change certain parameters when the config callback gets called. Switch to asking for the configuration we're interested in, rather than taking it in an arbitrary order. --- src/submodule.c | 381 +++++++++++++++++++++++++++++------------------- 1 file changed, 228 insertions(+), 153 deletions(-) diff --git a/src/submodule.c b/src/submodule.c index 7f52c3616..1d73dc24e 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -89,9 +89,11 @@ __KHASH_IMPL( static int submodule_alloc(git_submodule **out, git_repository *repo, const char *name); static git_config_backend *open_gitmodules(git_repository *repo, int gitmod); +static git_config *gitmodules_snapshot(git_repository *repo); static int get_url_base(git_buf *url, git_repository *repo); static int lookup_head_remote_key(git_buf *remote_key, git_repository *repo); -static int submodule_load_from_config(const git_config_entry *, void *); +static int submodule_load_each(const git_config_entry *entry, void *payload); +static int submodule_read_config(git_submodule *sm, git_config *cfg); static int submodule_load_from_wd_lite(git_submodule *); static void submodule_get_index_status(unsigned int *, git_submodule *); static void submodule_get_wd_status(unsigned int *, git_submodule *, git_repository *, git_submodule_ignore_t); @@ -144,6 +146,41 @@ static int find_by_path(const git_config_entry *entry, void *payload) return 0; } +/** + * Find out the name of a submodule from its path + */ +static int name_from_path(git_buf *out, git_config *cfg, const char *path) +{ + const char *key = "submodule\\..*\\.path"; + git_config_iterator *iter; + git_config_entry *entry; + int error; + + if ((error = git_config_iterator_glob_new(&iter, cfg, key)) < 0) + return error; + + while ((error = git_config_next(&entry, iter)) == 0) { + const char *fdot, *ldot; + /* TODO: this should maybe be strcasecmp on a case-insensitive fs */ + if (strcmp(path, entry->value) != 0) + continue; + + fdot = strchr(entry->name, '.'); + ldot = strrchr(entry->name, '.'); + + git_buf_clear(out); + git_buf_put(out, fdot + 1, ldot - fdot - 1); + return 0; + } + + if (error == GIT_ITEROVER) { + giterr_set(GITERR_SUBMODULE, "could not find a submodule name for '%s'", path); + error = GIT_ENOTFOUND; + } + + return error; +} + int git_submodule_lookup( git_submodule **out, /* NULL if user only wants to test existence */ git_repository *repo, @@ -280,11 +317,12 @@ done: return 0; } -static int submodules_from_index(git_strmap *map, git_index *idx) +static int submodules_from_index(git_strmap *map, git_index *idx, git_config *cfg) { int error; git_iterator *i; const git_index_entry *entry; + git_buf name = GIT_BUF_INIT; if ((error = git_iterator_for_index(&i, idx, NULL)) < 0) return error; @@ -293,6 +331,11 @@ static int submodules_from_index(git_strmap *map, git_index *idx) khiter_t pos = git_strmap_lookup_index(map, entry->path); git_submodule *sm; + git_buf_clear(&name); + if (!name_from_path(&name, cfg, entry->path)) { + git_strmap_lookup_index(map, name.ptr); + } + if (git_strmap_valid_index(map, pos)) { sm = git_strmap_value_at(map, pos); @@ -301,7 +344,7 @@ static int submodules_from_index(git_strmap *map, git_index *idx) else sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE; } else if (S_ISGITLINK(entry->mode)) { - if (!submodule_get_or_create(&sm, git_index_owner(idx), map, entry->path)) { + if (!submodule_get_or_create(&sm, git_index_owner(idx), map, name.ptr ? name.ptr : entry->path)) { submodule_update_from_index_entry(sm, entry); git_submodule_free(sm); } @@ -311,16 +354,18 @@ static int submodules_from_index(git_strmap *map, git_index *idx) if (error == GIT_ITEROVER) error = 0; + git_buf_free(&name); git_iterator_free(i); return error; } -static int submodules_from_head(git_strmap *map, git_tree *head) +static int submodules_from_head(git_strmap *map, git_tree *head, git_config *cfg) { int error; git_iterator *i; const git_index_entry *entry; + git_buf name = GIT_BUF_INIT; if ((error = git_iterator_for_tree(&i, head, NULL)) < 0) return error; @@ -329,6 +374,11 @@ static int submodules_from_head(git_strmap *map, git_tree *head) khiter_t pos = git_strmap_lookup_index(map, entry->path); git_submodule *sm; + git_buf_clear(&name); + if (!name_from_path(&name, cfg, entry->path)) { + git_strmap_lookup_index(map, name.ptr); + } + if (git_strmap_valid_index(map, pos)) { sm = git_strmap_value_at(map, pos); @@ -337,7 +387,7 @@ static int submodules_from_head(git_strmap *map, git_tree *head) else sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE; } else if (S_ISGITLINK(entry->mode)) { - if (!submodule_get_or_create(&sm, git_tree_owner(head), map, entry->path)) { + if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name.ptr ? name.ptr : entry->path)) { submodule_update_from_head_data( sm, entry->mode, &entry->id); git_submodule_free(sm); @@ -348,6 +398,7 @@ static int submodules_from_head(git_strmap *map, git_tree *head) if (error == GIT_ITEROVER) error = 0; + git_buf_free(&name); git_iterator_free(i); return error; @@ -355,8 +406,7 @@ static int submodules_from_head(git_strmap *map, git_tree *head) /* If have_sm is true, sm is populated, otherwise map an repo are. */ typedef struct { - int have_sm; - git_submodule *sm; + git_config *mods; git_strmap *map; git_repository *repo; } lfc_data; @@ -369,7 +419,7 @@ static int all_submodules(git_repository *repo, git_strmap *map) const char *wd = NULL; git_buf path = GIT_BUF_INIT; git_submodule *sm; - git_config_backend *mods = NULL; + git_config *mods = NULL; uint32_t mask; assert(repo && map); @@ -400,24 +450,28 @@ static int all_submodules(git_repository *repo, git_strmap *map) GIT_SUBMODULE_STATUS__WD_FLAGS | GIT_SUBMODULE_STATUS__WD_OID_VALID; - /* add back submodule information from index */ - if (idx) { - if ((error = submodules_from_index(map, idx)) < 0) - goto cleanup; - } - /* add submodule information from HEAD */ - if (head) { - if ((error = submodules_from_head(map, head)) < 0) - goto cleanup; - } /* add submodule information from .gitmodules */ if (wd) { lfc_data data = { 0 }; data.map = map; data.repo = repo; - if ((mods = open_gitmodules(repo, false)) != NULL && - (error = git_config_file_foreach( - mods, submodule_load_from_config, &data)) < 0) + + if ((mods = gitmodules_snapshot(repo)) == NULL) + goto cleanup; + + data.mods = mods; + if ((error = git_config_foreach( + mods, submodule_load_each, &data)) < 0) + goto cleanup; + } + /* add back submodule information from index */ + if (idx) { + if ((error = submodules_from_index(map, idx, mods)) < 0) + goto cleanup; + } + /* add submodule information from HEAD */ + if (head) { + if ((error = submodules_from_head(map, head, mods)) < 0) goto cleanup; } /* shallow scan submodules in work tree as needed */ @@ -428,7 +482,7 @@ static int all_submodules(git_repository *repo, git_strmap *map) } cleanup: - git_config_file_free(mods); + git_config_free(mods); /* TODO: if we got an error, mark submodule config as invalid? */ git_index_free(idx); git_tree_free(head); @@ -1370,8 +1424,7 @@ static int submodule_update_head(git_submodule *submodule) int git_submodule_reload(git_submodule *sm, int force) { int error = 0; - git_config_backend *mods; - lfc_data data = { 0 }; + git_config *mods; GIT_UNUSED(force); @@ -1390,28 +1443,14 @@ int git_submodule_reload(git_submodule *sm, int force) return error; /* refresh config data */ - mods = open_gitmodules(sm->repo, GITMODULES_EXISTING); + mods = gitmodules_snapshot(sm->repo); if (mods != NULL) { - git_buf path = GIT_BUF_INIT; + error = submodule_read_config(sm, mods); + git_config_free(mods); - git_buf_sets(&path, "submodule\\."); - git_buf_text_puts_escape_regex(&path, sm->name); - git_buf_puts(&path, "\\..*"); - - if (git_buf_oom(&path)) { - error = -1; - } else { - data.have_sm = 1; - data.sm = sm; - error = git_config_file_foreach_match( - mods, path.ptr, submodule_load_from_config, &data); - } - - git_buf_free(&path); - git_config_file_free(mods); - - if (error < 0) + if (error < 0) { return error; + } } /* refresh wd data */ @@ -1627,134 +1666,141 @@ int git_submodule_parse_recurse(git_submodule_recurse_t *out, const char *value) return 0; } -static int submodule_load_from_config( - const git_config_entry *entry, void *payload) +static int get_value(const char **out, git_config *cfg, git_buf *buf, const char *name, const char *field) { - const char *namestart, *property; - const char *key = entry->name, *value = entry->value, *path; - char *alternate = NULL, *replaced = NULL; - git_buf name = GIT_BUF_INIT; - lfc_data *data = payload; - git_submodule *sm; - int error = 0; + int error; - if (git__prefixcmp(key, "submodule.") != 0) + git_buf_clear(buf); + + if ((error = git_buf_printf(buf, "submodule.%s.%s", name, field)) < 0 || + (error = git_config_get_string(out, cfg, buf->ptr)) < 0) + return error; + + return error; +} + +static int submodule_read_config(git_submodule *sm, git_config *cfg) +{ + git_buf key = GIT_BUF_INIT; + const char *value; + int error, in_config = 0; + + /* + * TODO: Look up path in index and if it is present but not a GITLINK + * then this should be deleted (at least to match git's behavior) + */ + + if ((error = get_value(&value, cfg, &key, sm->name, "path")) == 0) { + in_config = 1; + /* + * TODO: if case insensitive filesystem, then the following strcmp + * should be strcasecmp + */ + if (strcmp(sm->name, value) != 0) { + sm->path = git__strdup(value); + GITERR_CHECK_ALLOC(sm->path); + } + } else if (error != GIT_ENOTFOUND) { + return error; + } + + if ((error = get_value(&value, cfg, &key, sm->name, "url")) == 0) { + in_config = 1; + sm->url = git__strdup(value); + GITERR_CHECK_ALLOC(sm->url); + } else if (error != GIT_ENOTFOUND) { + return error; + } + + if ((error = get_value(&value, cfg, &key, sm->name, "branch")) == 0) { + in_config = 1; + sm->branch = git__strdup(value); + GITERR_CHECK_ALLOC(sm->branch); + } else if (error != GIT_ENOTFOUND) { + return error; + } + + if ((error = get_value(&value, cfg, &key, sm->name, "update")) == 0) { + in_config = 1; + if ((error = git_submodule_parse_update(&sm->update, value)) < 0) + return error; + sm->update_default = sm->update; + } else if (error != GIT_ENOTFOUND) { + return error; + } + + if ((error = get_value(&value, cfg, &key, sm->name, "fetchRecurseSubmodules")) == 0) { + in_config = 1; + if ((error = git_submodule_parse_recurse(&sm->fetch_recurse, value)) < 0) + return error; + sm->fetch_recurse_default = sm->fetch_recurse; + } else if (error != GIT_ENOTFOUND) { + return error; + } + + if ((error = get_value(&value, cfg, &key, sm->name, "ignore")) == 0) { + in_config = 1; + if ((error = git_submodule_parse_ignore(&sm->ignore, value)) < 0) + return error; + sm->ignore_default = sm->ignore; + } else if (error != GIT_ENOTFOUND) { + return error; + } + + if (in_config) + sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG; + + return 0; +} + +static int submodule_load_each(const git_config_entry *entry, void *payload) +{ + lfc_data *data = payload; + const char *namestart, *property; + git_strmap_iter pos; + git_strmap *map = data->map; + git_buf name = GIT_BUF_INIT; + git_submodule *sm; + int error; + + if (git__prefixcmp(entry->name, "submodule.") != 0) return 0; - namestart = key + strlen("submodule."); + namestart = entry->name + strlen("submodule."); property = strrchr(namestart, '.'); if (!property || (property == namestart)) return 0; property++; - path = !strcasecmp(property, "path") ? value : NULL; if ((error = git_buf_set(&name, namestart, property - namestart -1)) < 0) + return error; + + /* + * Now that we have the submodule's name, we can use that to + * figure out whether it's in the map. If it's not, we create + * a new submodule, load the config and insert it. If it's + * already inserted, we've already loaded it, so we skip. + */ + pos = git_strmap_lookup_index(map, name.ptr); + if (git_strmap_valid_index(map, pos)) + return 0; + + if ((error = submodule_alloc(&sm, data->repo, name.ptr)) < 0) goto done; - if (data->have_sm) { - sm = data->sm; - } else { - khiter_t pos; - git_strmap *map = data->map; - pos = git_strmap_lookup_index(map, path ? path : name.ptr); - if (git_strmap_valid_index(map, pos)) { - sm = git_strmap_value_at(map, pos); - } else { - if ((error = submodule_alloc(&sm, data->repo, name.ptr)) < 0) - goto done; - - git_strmap_insert(map, sm->name, sm, error); - assert(error != 0); - if (error < 0) - goto done; - error = 0; - } + if ((error = submodule_read_config(sm, data->mods)) < 0) { + git_submodule_free(sm); + goto done; } - sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG; - - /* Only from config might we get differing names & paths. If so, then - * update the submodule and insert under the alternative key. - */ - - /* TODO: if case insensitive filesystem, then the following strcmps - * should be strcasecmp - */ - - if (strcmp(sm->name, name.ptr) != 0) { /* name changed */ - if (sm->path && !strcmp(sm->path, name.ptr)) { /* already set as path */ - replaced = sm->name; - sm->name = sm->path; - } else { - if (sm->name != sm->path) - replaced = sm->name; - alternate = sm->name = git_buf_detach(&name); - } - } - else if (path && strcmp(path, sm->path) != 0) { /* path changed */ - if (!strcmp(sm->name, value)) { /* already set as name */ - replaced = sm->path; - sm->path = sm->name; - } else { - if (sm->path != sm->name) - replaced = sm->path; - if ((alternate = git__strdup(value)) == NULL) { - error = -1; - goto done; - } - sm->path = alternate; - } - } - - /* Deregister under name being replaced */ - if (replaced) { - git__free(replaced); - } - - /* TODO: Look up path in index and if it is present but not a GITLINK - * then this should be deleted (at least to match git's behavior) - */ - - if (path) + git_strmap_insert(map, sm->name, sm, error); + assert(error != 0); + if (error < 0) goto done; - /* copy other properties into submodule entry */ - if (strcasecmp(property, "url") == 0) { - git__free(sm->url); - sm->url = NULL; - - if (value != NULL && (sm->url = git__strdup(value)) == NULL) { - error = -1; - goto done; - } - } - else if (strcasecmp(property, "branch") == 0) { - git__free(sm->branch); - sm->branch = NULL; - - if (value != NULL && (sm->branch = git__strdup(value)) == NULL) { - error = -1; - goto done; - } - } - else if (strcasecmp(property, "update") == 0) { - if ((error = git_submodule_parse_update(&sm->update, value)) < 0) - goto done; - sm->update_default = sm->update; - } - else if (strcasecmp(property, "fetchRecurseSubmodules") == 0) { - if ((error = git_submodule_parse_recurse(&sm->fetch_recurse, value)) < 0) - goto done; - sm->fetch_recurse_default = sm->fetch_recurse; - } - else if (strcasecmp(property, "ignore") == 0) { - if ((error = git_submodule_parse_ignore(&sm->ignore, value)) < 0) - goto done; - sm->ignore_default = sm->ignore; - } - /* ignore other unknown submodule properties */ + error = 0; done: git_buf_free(&name); @@ -1778,6 +1824,35 @@ static int submodule_load_from_wd_lite(git_submodule *sm) return 0; } +/** + * Returns a snapshot of $WORK_TREE/.gitmodules. + * + * We ignore any errors and just pretend the file isn't there. + */ +static git_config *gitmodules_snapshot(git_repository *repo) +{ + const char *workdir = git_repository_workdir(repo); + git_config *mods = NULL, *snap = NULL; + git_buf path = GIT_BUF_INIT; + + if (workdir != NULL) { + if (git_buf_joinpath(&path, workdir, GIT_MODULES_FILE) != 0) + return NULL; + + if (git_config_open_ondisk(&mods, path.ptr) < 0) + mods = NULL; + } + + git_buf_free(&path); + + if (mods) { + git_config_snapshot(&snap, mods); + git_config_free(mods); + } + + return snap; +} + static git_config_backend *open_gitmodules( git_repository *repo, int okay_to_create) From a3b9731ff8846870a4c5124cd57d2d797913ad1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 10 Sep 2015 21:23:03 +0200 Subject: [PATCH 124/450] submodule: add a test for a renamed submdoule dir --- tests/submodule/lookup.c | 55 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tests/submodule/lookup.c b/tests/submodule/lookup.c index ecea694e5..5f1614871 100644 --- a/tests/submodule/lookup.c +++ b/tests/submodule/lookup.c @@ -333,3 +333,58 @@ void test_submodule_lookup__prefix_name(void) git_submodule_free(sm); } + +void test_submodule_lookup__renamed(void) +{ + const char *newpath = "sm_actually_changed"; + git_index *idx; + sm_lookup_data data; + + cl_git_pass(git_repository_index__weakptr(&idx, g_repo)); + + /* We're replicating 'git mv sm_unchanged sm_actually_changed' in this test */ + + cl_git_pass(p_rename("submod2/sm_unchanged", "submod2/sm_actually_changed")); + + /* Change the path in .gitmodules and stage it*/ + { + git_config *cfg; + + cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules")); + cl_git_pass(git_config_set_string(cfg, "submodule.sm_unchanged.path", newpath)); + git_config_free(cfg); + + cl_git_pass(git_index_add_bypath(idx, ".gitmodules")); + } + + /* Change the worktree info in the the submodule's config */ + { + git_config *cfg; + + cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.git/modules/sm_unchanged/config")); + cl_git_pass(git_config_set_string(cfg, "core.worktree", "../../../sm_actually_changed")); + git_config_free(cfg); + } + + /* Rename the entry in the index */ + { + const git_index_entry *e; + git_index_entry entry = { 0 }; + + e = git_index_get_bypath(idx, "sm_unchanged", 0); + cl_assert(e); + cl_assert_equal_i(GIT_FILEMODE_COMMIT, e->mode); + + entry.path = newpath; + entry.mode = GIT_FILEMODE_COMMIT; + git_oid_cpy(&entry.id, &e->id); + + cl_git_pass(git_index_remove(idx, "sm_unchanged", 0)); + cl_git_pass(git_index_add(idx, &entry)); + cl_git_pass(git_index_write(idx)); + } + + memset(&data, 0, sizeof(data)); + cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data)); + cl_assert_equal_i(8, data.count); +} From 8e177b2bb8cacc3ca9e1edd301a8aa1132f300af Mon Sep 17 00:00:00 2001 From: Bryan Woods Date: Thu, 10 Sep 2015 14:44:52 -0700 Subject: [PATCH 125/450] Fixing dangling pointers in git_mempack_reset git_mempack_reset was leaving free'd pointers in the oidmap. --- src/odb_mempack.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/odb_mempack.c b/src/odb_mempack.c index 34355270f..538dfc521 100644 --- a/src/odb_mempack.c +++ b/src/odb_mempack.c @@ -154,6 +154,9 @@ void git_mempack_reset(git_odb_backend *_backend) }); git_array_clear(db->commits); + + git_oidmap_free(db->objects); + db->objects = git_oidmap_alloc(); } static void impl__free(git_odb_backend *_backend) From a1859e21f36f47f05771ef7915dfe89840d4f2c6 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 11 Sep 2015 17:38:28 -0400 Subject: [PATCH 126/450] iterator: advance the tree iterator smartly While advancing the tree iterator, if we advance over things that we aren't interested in, then call `current`. Which may *itself* call advance. While advancing the tree iterator, if we advance over things that we aren't interested in, then call `current`. Which may *itself* call advance. While advancing the tree iterator, if we advance over things that we aren't interested in, then call `current`. Which may *itself* call advance. While advancing the tree iterator, if we advance over things that we aren't interested in, then call `current`. Which may *itself* call advance. While advancing the tree iterator, if we advance over things that we aren't interested in, then call `current`. Which may *itself* call advance. Error: stack overflow. --- src/iterator.c | 110 ++++++++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 48 deletions(-) diff --git a/src/iterator.c b/src/iterator.c index e35c8dc85..9f3f7a9c7 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -640,8 +640,53 @@ static int tree_iterator__current_internal( return 0; } -static int tree_iterator__advance( - const git_index_entry **out, git_iterator *self); +static int tree_iterator__advance_into_internal(git_iterator *self) +{ + int error = 0; + tree_iterator *ti = (tree_iterator *)self; + + if (tree_iterator__at_tree(ti)) + error = tree_iterator__push_frame(ti); + + return error; +} + +static int tree_iterator__advance_internal(git_iterator *self) +{ + int error; + tree_iterator *ti = (tree_iterator *)self; + tree_iterator_frame *tf = ti->head; + + if (tf->current >= tf->n_entries) + return GIT_ITEROVER; + + if (!iterator__has_been_accessed(ti)) + return 0; + + if (iterator__do_autoexpand(ti) && iterator__include_trees(ti) && + tree_iterator__at_tree(ti)) + return tree_iterator__advance_into_internal(self); + + if (ti->path_has_filename) { + git_buf_rtruncate_at_char(&ti->path, '/'); + ti->path_has_filename = ti->entry_is_current = false; + } + + /* scan forward and up, advancing in frame or popping frame when done */ + while (!tree_iterator__move_to_next(ti, tf) && + tree_iterator__pop_frame(ti, false)) + tf = ti->head; + + /* find next and load trees */ + if ((error = tree_iterator__set_next(ti, tf)) < 0) + return error; + + /* deal with include_trees / auto_expand as needed */ + if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti)) + return tree_iterator__advance_into_internal(self); + + return 0; +} static int tree_iterator__current( const git_index_entry **out, git_iterator *self) @@ -659,7 +704,7 @@ static int tree_iterator__current( self, entry->path, strlen(entry->path)); if (m != ITERATOR_PATHLIST_MATCH) { - if ((error = tree_iterator__advance(&entry, self)) < 0) + if ((error = tree_iterator__advance_internal(self)) < 0) return error; entry = NULL; @@ -673,59 +718,28 @@ static int tree_iterator__current( return error; } -static int tree_iterator__advance_into( - const git_index_entry **entry, git_iterator *self) -{ - int error = 0; - tree_iterator *ti = (tree_iterator *)self; - - iterator__clear_entry(entry); - - if (tree_iterator__at_tree(ti)) - error = tree_iterator__push_frame(ti); - - if (!error && entry) - error = tree_iterator__current(entry, self); - - return error; -} - static int tree_iterator__advance( const git_index_entry **entry, git_iterator *self) { - int error; - tree_iterator *ti = (tree_iterator *)self; - tree_iterator_frame *tf = ti->head; + int error = tree_iterator__advance_internal(self); iterator__clear_entry(entry); - if (tf->current >= tf->n_entries) - return GIT_ITEROVER; - - if (!iterator__has_been_accessed(ti)) - return tree_iterator__current(entry, self); - - if (iterator__do_autoexpand(ti) && iterator__include_trees(ti) && - tree_iterator__at_tree(ti)) - return tree_iterator__advance_into(entry, self); - - if (ti->path_has_filename) { - git_buf_rtruncate_at_char(&ti->path, '/'); - ti->path_has_filename = ti->entry_is_current = false; - } - - /* scan forward and up, advancing in frame or popping frame when done */ - while (!tree_iterator__move_to_next(ti, tf) && - tree_iterator__pop_frame(ti, false)) - tf = ti->head; - - /* find next and load trees */ - if ((error = tree_iterator__set_next(ti, tf)) < 0) + if (error < 0) return error; - /* deal with include_trees / auto_expand as needed */ - if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti)) - return tree_iterator__advance_into(entry, self); + return tree_iterator__current(entry, self); +} + +static int tree_iterator__advance_into( + const git_index_entry **entry, git_iterator *self) +{ + int error = tree_iterator__advance_into_internal(self); + + iterator__clear_entry(entry); + + if (error < 0) + return error; return tree_iterator__current(entry, self); } From 707f65372b4091ada386f2a04a05e94899e32bfc Mon Sep 17 00:00:00 2001 From: Bryan Woods Date: Fri, 11 Sep 2015 16:35:14 -0700 Subject: [PATCH 127/450] Removing memory leak in mempack's free It calls git_mempack_reset which reallocates the object array. git_oidmap_free is now called on it explicitly. --- src/odb_mempack.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/odb_mempack.c b/src/odb_mempack.c index 538dfc521..25f30590c 100644 --- a/src/odb_mempack.c +++ b/src/odb_mempack.c @@ -161,8 +161,12 @@ void git_mempack_reset(git_odb_backend *_backend) static void impl__free(git_odb_backend *_backend) { - git_mempack_reset(_backend); - git__free(_backend); + struct memory_packer_db *db = (struct memory_packer_db *)_backend; + + git_mempack_reset(db); + git_oidmap_free(db->objects); + + git__free(db); } int git_mempack_new(git_odb_backend **out) From 220d6f8a104661ae9edc4f96cd8f520ef4c4dc1d Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 11 Sep 2015 20:06:14 -0400 Subject: [PATCH 128/450] mempack: expose clear function --- src/odb_mempack.c | 5 +---- src/oidmap.h | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/odb_mempack.c b/src/odb_mempack.c index 25f30590c..594a2784c 100644 --- a/src/odb_mempack.c +++ b/src/odb_mempack.c @@ -155,17 +155,14 @@ void git_mempack_reset(git_odb_backend *_backend) git_array_clear(db->commits); - git_oidmap_free(db->objects); - db->objects = git_oidmap_alloc(); + git_oidmap_clear(db->objects); } static void impl__free(git_odb_backend *_backend) { struct memory_packer_db *db = (struct memory_packer_db *)_backend; - git_mempack_reset(db); git_oidmap_free(db->objects); - git__free(db); } diff --git a/src/oidmap.h b/src/oidmap.h index d2c451e7f..2cf208f53 100644 --- a/src/oidmap.h +++ b/src/oidmap.h @@ -49,4 +49,6 @@ GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid) #define git_oidmap_size(h) kh_size(h) +#define git_oidmap_clear(h) kh_clear(oid, h) + #endif From 92f7d32b59642c82c17027bc85a39100869280a7 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sat, 12 Sep 2015 13:46:22 -0400 Subject: [PATCH 129/450] diff::workdir: ensure ignored files are not returned Ensure that a diff with the workdir is not erroneously returning directories. --- tests/diff/workdir.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c index e87769170..89f9483a6 100644 --- a/tests/diff/workdir.c +++ b/tests/diff/workdir.c @@ -2054,3 +2054,46 @@ void test_diff_workdir__only_writes_index_when_necessary(void) git_index_free(index); } +void test_diff_workdir__to_index_pathlist(void) +{ + git_index *index; + git_diff *diff; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_vector pathlist = GIT_VECTOR_INIT; + + git_vector_insert(&pathlist, "foobar/asdf"); + git_vector_insert(&pathlist, "subdir/asdf"); + git_vector_insert(&pathlist, "ignored/asdf"); + + g_repo = cl_git_sandbox_init("status"); + + cl_git_mkfile("status/.gitignore", ".gitignore\n" "ignored/\n"); + + cl_must_pass(p_mkdir("status/foobar", 0777)); + cl_git_mkfile("status/foobar/one", "one\n"); + + cl_must_pass(p_mkdir("status/ignored", 0777)); + cl_git_mkfile("status/ignored/one", "one\n"); + cl_git_mkfile("status/ignored/two", "two\n"); + cl_git_mkfile("status/ignored/three", "three\n"); + + cl_git_pass(git_repository_index(&index, g_repo)); + + opts.flags = GIT_DIFF_INCLUDE_IGNORED; + opts.pathspec.strings = pathlist.contents; + opts.pathspec.count = pathlist.length; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &opts)); + cl_assert_equal_i(0, git_diff_num_deltas(diff)); + git_diff_free(diff); + + opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &opts)); + cl_assert_equal_i(0, git_diff_num_deltas(diff)); + git_diff_free(diff); + + git_index_free(index); + git_vector_free(&pathlist); +} + From 8ab4d0e1e16bf42e4617a40a13d67385fc8fa40c Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sat, 12 Sep 2015 15:32:18 -0400 Subject: [PATCH 130/450] diff: check pathspec on non-files When we're not doing pathspec matching, we let the iterator handle file matching for us. However, we can only trust the iterator to return *files* that match the pattern, because the iterator must return directories that are not strictly in the pathlist, but that are the parents of files that match the pattern, so that diff can later recurse into them. Thus, diff must examine non-files explicitly before including them in the delta list. --- src/diff.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/diff.c b/src/diff.c index 32c1d4aa5..d97dcd9d2 100644 --- a/src/diff.c +++ b/src/diff.c @@ -75,18 +75,27 @@ static int diff_insert_delta( } static bool diff_pathspec_match( - const char **matched_pathspec, git_diff *diff, const char *path) + const char **matched_pathspec, + git_diff *diff, + const git_index_entry *entry) { - /* The iterator has filtered out paths for us, so the fact that we're - * seeing this patch means that it must match the given path list. + bool disable_pathspec_match = + DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH); + + /* If we're disabling fnmatch, then the iterator has already applied + * the filters to the files for us and we don't have to do anything. + * However, this only applies to *files* - the iterator will include + * directories that we need to recurse into when not autoexpanding, + * so we still need to apply the pathspec match to directories. */ - if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH)) { - *matched_pathspec = path; + if ((S_ISLNK(entry->mode) || S_ISREG(entry->mode)) && + disable_pathspec_match) { + *matched_pathspec = entry->path; return true; } return git_pathspec__match( - &diff->pathspec, path, false, + &diff->pathspec, entry->path, disable_pathspec_match, DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE), matched_pathspec, NULL); } @@ -127,7 +136,7 @@ static int diff_delta__from_one( DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE)) return 0; - if (!diff_pathspec_match(&matched_pathspec, diff, entry->path)) + if (!diff_pathspec_match(&matched_pathspec, diff, entry)) return 0; delta = diff_delta__alloc(diff, status, entry->path); @@ -768,7 +777,7 @@ static int maybe_modified( const char *matched_pathspec; int error = 0; - if (!diff_pathspec_match(&matched_pathspec, diff, oitem->path)) + if (!diff_pathspec_match(&matched_pathspec, diff, oitem)) return 0; memset(&noid, 0, sizeof(noid)); From 1af5aecb965817901fa9134222bad4e0579de7a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 13 Sep 2015 05:38:29 +0200 Subject: [PATCH 131/450] push: put the git_oid inline in the test structure These are small pieces of data, so there is no advantage to allocating them separately. Include the two ids inline in the struct we use to check that the expected and actual ids match. --- tests/online/push.c | 2 +- tests/online/push_util.c | 11 +++-------- tests/online/push_util.h | 4 ++-- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/tests/online/push.c b/tests/online/push.c index 0b0892c97..efb763c24 100644 --- a/tests/online/push.c +++ b/tests/online/push.c @@ -299,7 +299,7 @@ static void verify_update_tips_callback(git_remote *remote, expected_ref expecte goto failed; } - if (git_oid_cmp(expected_refs[i].oid, tip->new_oid) != 0) { + if (git_oid_cmp(expected_refs[i].oid, &tip->new_oid) != 0) { git_buf_printf(&msg, "Updated tip ID does not match expected ID"); failed = 1; goto failed; diff --git a/tests/online/push_util.c b/tests/online/push_util.c index cd483c7c0..eafec2f05 100644 --- a/tests/online/push_util.c +++ b/tests/online/push_util.c @@ -9,8 +9,6 @@ const git_oid OID_ZERO = {{ 0 }}; void updated_tip_free(updated_tip *t) { git__free(t->name); - git__free(t->old_oid); - git__free(t->new_oid); git__free(t); } @@ -46,14 +44,11 @@ int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid * updated_tip *t; record_callbacks_data *record_data = (record_callbacks_data *)data; - cl_assert(t = git__malloc(sizeof(*t))); + cl_assert(t = git__calloc(1, sizeof(*t))); cl_assert(t->name = git__strdup(refname)); - cl_assert(t->old_oid = git__malloc(sizeof(*t->old_oid))); - git_oid_cpy(t->old_oid, a); - - cl_assert(t->new_oid = git__malloc(sizeof(*t->new_oid))); - git_oid_cpy(t->new_oid, b); + git_oid_cpy(&t->old_oid, a); + git_oid_cpy(&t->new_oid, b); git_vector_insert(&record_data->updated_tips, t); diff --git a/tests/online/push_util.h b/tests/online/push_util.h index 822341bd2..570873cfe 100644 --- a/tests/online/push_util.h +++ b/tests/online/push_util.h @@ -16,8 +16,8 @@ extern const git_oid OID_ZERO; typedef struct { char *name; - git_oid *old_oid; - git_oid *new_oid; + git_oid old_oid; + git_oid new_oid; } updated_tip; typedef struct { From 657afd359e0e49627addb092c05ddd00117614de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 13 Sep 2015 06:18:49 +0200 Subject: [PATCH 132/450] ignore: add test and adjust style and comment for dir with wildmatch The previous commit left the comment referencing the earlier state of the code, change it to explain the current logic. While here, change the logic to avoid repeating the copy of the base pattern. --- src/ignore.c | 16 +++++++++------- tests/status/ignore.c | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/ignore.c b/src/ignore.c index 1f33687bc..aedc1401e 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -89,18 +89,20 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match } /* - * If we're dealing with a directory (which we know via the - * strchr() check) we want to use 'dirname/' as the - * pattern so p_fnmatch() honours FNM_PATHNAME + * When dealing with a directory, we add '/' so + * p_fnmatch() honours FNM_PATHNAME. Checking for LEADINGDIR + * alone isn't enough as that's also set for nagations, so we + * need to check that NEGATIVE is off. */ git_buf_clear(&buf); if (rule->containing_dir) { git_buf_puts(&buf, rule->containing_dir); } - if (rule->flags & GIT_ATTR_FNMATCH_LEADINGDIR && !(rule->flags & GIT_ATTR_FNMATCH_NEGATIVE)) - error = git_buf_printf(&buf, "%s/*", rule->pattern); - else - error = git_buf_puts(&buf, rule->pattern); + + error = git_buf_puts(&buf, rule->pattern); + + if ((rule->flags & (GIT_ATTR_FNMATCH_LEADINGDIR | GIT_ATTR_FNMATCH_NEGATIVE)) == GIT_ATTR_FNMATCH_LEADINGDIR) + error = git_buf_PUTS(&buf, "/*"); if (error < 0) goto out; diff --git a/tests/status/ignore.c b/tests/status/ignore.c index ba1d69a99..bbf8f4911 100644 --- a/tests/status/ignore.c +++ b/tests/status/ignore.c @@ -1022,3 +1022,20 @@ void test_status_ignore__negate_exact_previous(void) cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, ".buildpath")); cl_assert_equal_i(1, ignored); } + +void test_status_ignore__negate_starstar(void) +{ + int ignored; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + + cl_git_mkfile("empty_standard_repo/.gitignore", + "code/projects/**/packages/*\n" + "!code/projects/**/packages/repositories.config"); + + cl_git_pass(git_futils_mkdir_r("code/projects/foo/bar/packages", "empty_standard_repo", 0777)); + cl_git_mkfile("empty_standard_repo/code/projects/foo/bar/packages/repositories.config", ""); + + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "code/projects/foo/bar/packages/repositories.config")); + cl_assert_equal_i(0, ignored); +} From 548cb334344a592678778e0357f3f09000d6cf5d Mon Sep 17 00:00:00 2001 From: Arthur Schreiber Date: Sun, 13 Sep 2015 16:32:24 +0200 Subject: [PATCH 133/450] Don't free config in `git_transaction_commit`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The config is not owned by the transaction, so please don’t free it. --- src/transaction.c | 1 - tests/config/write.c | 10 ++++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/transaction.c b/src/transaction.c index e9639bf97..92e134e5b 100644 --- a/src/transaction.c +++ b/src/transaction.c @@ -331,7 +331,6 @@ int git_transaction_commit(git_transaction *tx) if (tx->type == TRANSACTION_CONFIG) { error = git_config_unlock(tx->cfg, true); - git_config_free(tx->cfg); tx->cfg = NULL; return error; diff --git a/tests/config/write.c b/tests/config/write.c index 3d9b1a16a..9ad11ab27 100644 --- a/tests/config/write.c +++ b/tests/config/write.c @@ -670,6 +670,16 @@ void test_config_write__locking(void) git_transaction_free(tx); /* Now that we've unlocked it, we should see both updates */ + cl_git_pass(git_config_get_entry(&entry, cfg, "section.name")); + cl_assert_equal_s("other value", entry->value); + git_config_entry_free(entry); + cl_git_pass(git_config_get_entry(&entry, cfg, "section2.name3")); + cl_assert_equal_s("more value", entry->value); + git_config_entry_free(entry); + + git_config_free(cfg); + + /* We should also see the changes after reopening the config */ cl_git_pass(git_config_open_ondisk(&cfg, filename)); cl_git_pass(git_config_get_entry(&entry, cfg, "section.name")); cl_assert_equal_s("other value", entry->value); From 2cde210d47f94962443cc090896cc1d5ef88452f Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 13 Sep 2015 13:52:19 -0400 Subject: [PATCH 134/450] diriter: test we can iterate root Ensure that we can iterate the filesystem root and that paths come back well-formed, not with an additional '/'. (eg, when iterating `c:/`, expect that we do not get some path like `c://autoexec.bat`). --- tests/core/dirent.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/core/dirent.c b/tests/core/dirent.c index d95e44196..2bd60269d 100644 --- a/tests/core/dirent.c +++ b/tests/core/dirent.c @@ -275,3 +275,32 @@ void test_core_dirent__diriter_with_fullname(void) check_counts(&sub); } + +void test_core_dirent__diriter_at_directory_root(void) +{ + git_path_diriter diriter = GIT_PATH_DIRITER_INIT; + const char *sandbox_path, *path; + char *root_path; + size_t path_len; + int root_offset, error; + + sandbox_path = clar_sandbox_path(); + cl_assert((root_offset = git_path_root(sandbox_path)) >= 0); + + cl_assert(root_path = git__calloc(1, root_offset + 2)); + strncpy(root_path, sandbox_path, root_offset + 1); + + cl_git_pass(git_path_diriter_init(&diriter, root_path, 0)); + + while ((error = git_path_diriter_next(&diriter)) == 0) { + cl_git_pass(git_path_diriter_fullpath(&path, &path_len, &diriter)); + + cl_assert(path_len > (size_t)(root_offset + 1)); + cl_assert(path[root_offset+1] != '/'); + } + + cl_assert_equal_i(error, GIT_ITEROVER); + + git_path_diriter_free(&diriter); + git__free(root_path); +} From 5a466befaf03021b5bf8f1c7d34d35c8cd316213 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 13 Sep 2015 13:59:41 -0400 Subject: [PATCH 135/450] diriter: don't double '/' on Windows The canonical directory path of the root directory of a volume on windows already ends in a slash (eg, `c:/`). This is true only at the volume root. Do not add a slash to paths in this case. --- src/path.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/path.c b/src/path.c index 9ce5d2978..d14a7699e 100644 --- a/src/path.c +++ b/src/path.c @@ -1166,7 +1166,11 @@ static int diriter_update_paths(git_path_diriter *diriter) diriter->path[path_len-1] = L'\0'; git_buf_truncate(&diriter->path_utf8, diriter->parent_utf8_len); - git_buf_putc(&diriter->path_utf8, '/'); + + if (diriter->parent_utf8_len > 0 && + diriter->path_utf8.ptr[diriter->parent_utf8_len-1] != '/') + git_buf_putc(&diriter->path_utf8, '/'); + git_buf_put_w(&diriter->path_utf8, diriter->current.cFileName, filename_len); if (git_buf_oom(&diriter->path_utf8)) From 26d7cf6e57c08b497fed20f4e8b88efea52cbe42 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 11 Sep 2015 18:27:04 -0400 Subject: [PATCH 136/450] iterator: loop fs_iterator advance (don't recurse) --- src/iterator.c | 85 +++++++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/src/iterator.c b/src/iterator.c index 9f3f7a9c7..e3a2abf66 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -1432,19 +1432,14 @@ static int fs_iterator__advance_into( return error; } -static int fs_iterator__advance_over( - const git_index_entry **entry, git_iterator *self) +static void fs_iterator__advance_over_internal(git_iterator *self) { - int error = 0; fs_iterator *fi = (fs_iterator *)self; fs_iterator_frame *ff; fs_iterator_path_with_stat *next; - if (entry != NULL) - *entry = NULL; - while (fi->entry.path != NULL) { - ff = fi->stack; + ff = fi->stack; next = git_vector_get(&ff->entries, ++ff->index); if (next != NULL) @@ -1452,8 +1447,19 @@ static int fs_iterator__advance_over( fs_iterator__pop_frame(fi, ff, false); } +} - error = fs_iterator__update_entry(fi); +static int fs_iterator__advance_over( + const git_index_entry **entry, git_iterator *self) +{ + int error; + + if (entry != NULL) + *entry = NULL; + + fs_iterator__advance_over_internal(self); + + error = fs_iterator__update_entry((fs_iterator *)self); if (!error && entry != NULL) error = fs_iterator__current(entry, self); @@ -1530,41 +1536,50 @@ static int fs_iterator__update_entry(fs_iterator *fi) { fs_iterator_path_with_stat *ps; - memset(&fi->entry, 0, sizeof(fi->entry)); + while (true) { + memset(&fi->entry, 0, sizeof(fi->entry)); - if (!fi->stack) - return GIT_ITEROVER; + if (!fi->stack) + return GIT_ITEROVER; - ps = git_vector_get(&fi->stack->entries, fi->stack->index); - if (!ps) - return GIT_ITEROVER; + ps = git_vector_get(&fi->stack->entries, fi->stack->index); + if (!ps) + return GIT_ITEROVER; - git_buf_truncate(&fi->path, fi->root_len); - if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0) - return -1; + git_buf_truncate(&fi->path, fi->root_len); + if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0) + return -1; - if (iterator__past_end(fi, fi->path.ptr + fi->root_len)) - return GIT_ITEROVER; + if (iterator__past_end(fi, fi->path.ptr + fi->root_len)) + return GIT_ITEROVER; - fi->entry.path = ps->path; - fi->pathlist_match = ps->pathlist_match; - git_index_entry__init_from_stat(&fi->entry, &ps->st, true); + fi->entry.path = ps->path; + fi->pathlist_match = ps->pathlist_match; + git_index_entry__init_from_stat(&fi->entry, &ps->st, true); - /* need different mode here to keep directories during iteration */ - fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode); + /* need different mode here to keep directories during iteration */ + fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode); - /* allow wrapper to check/update the entry (can force skip) */ - if (fi->update_entry_cb && - fi->update_entry_cb(fi) == GIT_ENOTFOUND) - return fs_iterator__advance_over(NULL, (git_iterator *)fi); + /* allow wrapper to check/update the entry (can force skip) */ + if (fi->update_entry_cb && + fi->update_entry_cb(fi) == GIT_ENOTFOUND) { + fs_iterator__advance_over_internal(&fi->base); + continue; + } - /* if this is a tree and trees aren't included, then skip */ - if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) { - int error = fs_iterator__advance_into(NULL, (git_iterator *)fi); - if (error != GIT_ENOTFOUND) - return error; - giterr_clear(); - return fs_iterator__advance_over(NULL, (git_iterator *)fi); + /* if this is a tree and trees aren't included, then skip */ + if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) { + int error = fs_iterator__advance_into(NULL, &fi->base); + + if (error != GIT_ENOTFOUND) + return error; + + giterr_clear(); + fs_iterator__advance_over_internal(&fi->base); + continue; + } + + break; } return 0; From 9d905541bf372cbb17e82eea6dc9c6ac36ab6ea7 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 13 Sep 2015 14:18:08 -0400 Subject: [PATCH 137/450] diriter: don't double '/' on posix The canonical directory path of the root directory of a volume on POSIX already ends in a slash (eg, `/`). This is true only at the root. Do not add a slash to paths in this case. --- src/path.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/path.c b/src/path.c index d14a7699e..cb11acee3 100644 --- a/src/path.c +++ b/src/path.c @@ -1319,7 +1319,11 @@ int git_path_diriter_next(git_path_diriter *diriter) #endif git_buf_truncate(&diriter->path, diriter->parent_len); - git_buf_putc(&diriter->path, '/'); + + if (diriter->parent_len > 0 && + diriter->path.ptr[diriter->parent_len-1] != '/') + git_buf_putc(&diriter->path, '/'); + git_buf_put(&diriter->path, filename, filename_len); if (git_buf_oom(&diriter->path)) From 4cc355c97bd3a8c490759c489cfdcc9ab37e7f39 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 14 Sep 2015 13:58:38 -0400 Subject: [PATCH 138/450] clone::nonetwork: don't use fixed size buffer --- tests/clone/nonetwork.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/clone/nonetwork.c b/tests/clone/nonetwork.c index 44a503818..7ebf19f46 100644 --- a/tests/clone/nonetwork.c +++ b/tests/clone/nonetwork.c @@ -297,16 +297,19 @@ static void assert_correct_reflog(const char *name) { git_reflog *log; const git_reflog_entry *entry; - char expected_log_message[128] = {0}; + git_buf expected_message = GIT_BUF_INIT; - sprintf(expected_log_message, "clone: from %s", cl_git_fixture_url("testrepo.git")); + git_buf_printf(&expected_message, + "clone: from %s", cl_git_fixture_url("testrepo.git")); cl_git_pass(git_reflog_read(&log, g_repo, name)); cl_assert_equal_i(1, git_reflog_entrycount(log)); entry = git_reflog_entry_byindex(log, 0); - cl_assert_equal_s(expected_log_message, git_reflog_entry_message(entry)); + cl_assert_equal_s(expected_message.ptr, git_reflog_entry_message(entry)); git_reflog_free(log); + + git_buf_free(&expected_message); } void test_clone_nonetwork__clone_updates_reflog_properly(void) From 8452fecc8fc14a59d37c7f06c9355e560aa66ed0 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 14 Sep 2015 14:05:01 -0400 Subject: [PATCH 139/450] cl_git_path_url: assert sane static buffer size --- tests/clar_libgit2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/clar_libgit2.c b/tests/clar_libgit2.c index cc687baeb..61442f88b 100644 --- a/tests/clar_libgit2.c +++ b/tests/clar_libgit2.c @@ -299,6 +299,8 @@ const char* cl_git_path_url(const char *path) in_buf++; } + cl_assert(url_buf.size < 4096); + strncpy(url, git_buf_cstr(&url_buf), 4096); git_buf_free(&url_buf); git_buf_free(&path_buf); From 6124d983b6970f99ec7c93e480896806a83e7198 Mon Sep 17 00:00:00 2001 From: Matti Virolainen Date: Mon, 1 Jun 2015 11:16:36 +0300 Subject: [PATCH 140/450] Check that an executable in index is not an executable after checkout. --- tests/checkout/tree.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index be4019822..577409511 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -973,6 +973,17 @@ void test_checkout_tree__filemode_preserved_in_index(void) git_commit_free(commit); + /* Finally, check out the text file again and check that the exec bit is cleared */ + cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9")); + cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid)); + + cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); + cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0)); + cl_assert_equal_i(0100644, entry->mode); + + git_commit_free(commit); + + git_index_free(index); } From 33cad995899c960c777b3545dd58fd342ffdc64a Mon Sep 17 00:00:00 2001 From: Matti Virolainen Date: Mon, 1 Jun 2015 14:31:49 +0300 Subject: [PATCH 141/450] Check that checkout preserves filemode in working directory. --- tests/checkout/tree.c | 63 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index 577409511..9217c12d9 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -987,6 +987,69 @@ void test_checkout_tree__filemode_preserved_in_index(void) git_index_free(index); } +mode_t read_filemode(const char *path) +{ + struct stat st; + char pathabs[256] = {0}; + + strcat(pathabs, clar_sandbox_path()); + strcat(pathabs, "/testrepo/"); + strcat(pathabs, path); + cl_must_pass(p_stat(pathabs, &st)); + + return st.st_mode; +} + +void test_checkout_tree__filemode_preserved_in_workdir(void) +{ +#ifndef GIT_WIN32 + git_oid executable_oid; + git_commit *commit; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + /* test a freshly added executable */ + cl_git_pass(git_oid_fromstr(&executable_oid, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6")); + cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid)); + + cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); + cl_assert_equal_i(0100755, read_filemode("executable.txt")); + + git_commit_free(commit); + + + /* Now start with a commit which has a text file */ + cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9")); + cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid)); + + cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); + cl_assert_equal_i(0100644, read_filemode("a/b.txt")); + + git_commit_free(commit); + + + /* And then check out to a commit which converts the text file to an executable */ + cl_git_pass(git_oid_fromstr(&executable_oid, "144344043ba4d4a405da03de3844aa829ae8be0e")); + cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid)); + + cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); + cl_assert_equal_i(0100755, read_filemode("a/b.txt")); + + git_commit_free(commit); + + + /* Finally, check out the text file again and check that the exec bit is cleared */ + cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9")); + cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid)); + + cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); + cl_assert_equal_i(0100644, read_filemode("a/b.txt")); + + git_commit_free(commit); +#endif +} + void test_checkout_tree__removes_conflicts(void) { git_oid commit_id; From b4d183a77aa01cac406aecdff8908f10cb3501cb Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 16 Sep 2015 04:12:47 +0000 Subject: [PATCH 142/450] checkout::tree tests: don't use static buffer --- tests/checkout/tree.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index 9217c12d9..3b84f43ce 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -989,15 +989,18 @@ void test_checkout_tree__filemode_preserved_in_index(void) mode_t read_filemode(const char *path) { + git_buf fullpath = GIT_BUF_INIT; struct stat st; - char pathabs[256] = {0}; + mode_t result; - strcat(pathabs, clar_sandbox_path()); - strcat(pathabs, "/testrepo/"); - strcat(pathabs, path); - cl_must_pass(p_stat(pathabs, &st)); + git_buf_joinpath(&fullpath, "testrepo", path); + cl_must_pass(p_stat(fullpath.ptr, &st)); - return st.st_mode; + result = GIT_PERMS_IS_EXEC(st.st_mode) ? 0100755 : 0100644; + + git_buf_free(&fullpath); + + return result; } void test_checkout_tree__filemode_preserved_in_workdir(void) From 6fe322843b98f0f9101258c88c820f230eec5f64 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 16 Sep 2015 10:17:54 -0400 Subject: [PATCH 143/450] checkout::tree tests: don't use hardcoded mode --- tests/checkout/tree.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index 3b84f43ce..5692e1f25 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -946,7 +946,7 @@ void test_checkout_tree__filemode_preserved_in_index(void) cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); cl_assert(entry = git_index_get_bypath(index, "executable.txt", 0)); - cl_assert_equal_i(0100755, entry->mode); + cl_assert(GIT_PERMS_IS_EXEC(entry->mode)); git_commit_free(commit); @@ -957,7 +957,7 @@ void test_checkout_tree__filemode_preserved_in_index(void) cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0)); - cl_assert_equal_i(0100644, entry->mode); + cl_assert(!GIT_PERMS_IS_EXEC(entry->mode)); git_commit_free(commit); @@ -968,7 +968,7 @@ void test_checkout_tree__filemode_preserved_in_index(void) cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0)); - cl_assert_equal_i(0100755, entry->mode); + cl_assert(GIT_PERMS_IS_EXEC(entry->mode)); git_commit_free(commit); @@ -979,7 +979,7 @@ void test_checkout_tree__filemode_preserved_in_index(void) cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0)); - cl_assert_equal_i(0100644, entry->mode); + cl_assert(!GIT_PERMS_IS_EXEC(entry->mode)); git_commit_free(commit); @@ -1017,7 +1017,7 @@ void test_checkout_tree__filemode_preserved_in_workdir(void) cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid)); cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); - cl_assert_equal_i(0100755, read_filemode("executable.txt")); + cl_assert(GIT_PERMS_IS_EXEC(read_filemode("executable.txt"))); git_commit_free(commit); @@ -1027,7 +1027,7 @@ void test_checkout_tree__filemode_preserved_in_workdir(void) cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid)); cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); - cl_assert_equal_i(0100644, read_filemode("a/b.txt")); + cl_assert(!GIT_PERMS_IS_EXEC(read_filemode("a/b.txt"))); git_commit_free(commit); @@ -1037,7 +1037,7 @@ void test_checkout_tree__filemode_preserved_in_workdir(void) cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid)); cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); - cl_assert_equal_i(0100755, read_filemode("a/b.txt")); + cl_assert(GIT_PERMS_IS_EXEC(read_filemode("a/b.txt"))); git_commit_free(commit); @@ -1047,7 +1047,7 @@ void test_checkout_tree__filemode_preserved_in_workdir(void) cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid)); cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); - cl_assert_equal_i(0100644, read_filemode("a/b.txt")); + cl_assert(!GIT_PERMS_IS_EXEC(read_filemode("a/b.txt"))); git_commit_free(commit); #endif From eea7c850248c04a6ac3aadbb13b2c72c2237013b Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 16 Sep 2015 05:44:27 +0000 Subject: [PATCH 144/450] checkout: overwrite files with differing modes When a file exists on disk and we're checking out a file that differs in executableness, remove the old file. This allows us to recreate the new file with p_open, which will take the new mode into account and handle setting the umask properly. Remove any notion of chmod'ing existing files, since it is now handled by the aforementioned removal and was incorrect, as it did not take umask into account. --- src/checkout.c | 15 ++++++--------- tests/checkout/tree.c | 3 ++- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index de48c9e01..8c06b3335 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -243,6 +243,12 @@ static int checkout_action_common( if (delta->new_file.mode == GIT_FILEMODE_LINK && wd != NULL) *action |= CHECKOUT_ACTION__REMOVE; + /* if the file is on disk and doesn't match our mode, force update */ + if (wd && + GIT_PERMS_IS_EXEC(wd->mode) != + GIT_PERMS_IS_EXEC(delta->new_file.mode)) + *action |= CHECKOUT_ACTION__REMOVE; + notify = GIT_CHECKOUT_NOTIFY_UPDATED; } @@ -1500,15 +1506,6 @@ static int blob_content_to_file( if (error < 0) return error; - if (GIT_PERMS_IS_EXEC(mode)) { - data->perfdata.chmod_calls++; - - if ((error = p_chmod(path, mode)) < 0) { - giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path); - return error; - } - } - if (st) { data->perfdata.stat_calls++; diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index 5692e1f25..5680b86df 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -996,7 +996,8 @@ mode_t read_filemode(const char *path) git_buf_joinpath(&fullpath, "testrepo", path); cl_must_pass(p_stat(fullpath.ptr, &st)); - result = GIT_PERMS_IS_EXEC(st.st_mode) ? 0100755 : 0100644; + result = GIT_PERMS_IS_EXEC(st.st_mode) ? + GIT_FILEMODE_BLOB_EXECUTABLE : GIT_FILEMODE_BLOB; git_buf_free(&fullpath); From ac2fba0ecd68e8eae348dec688cbcd0828432cdf Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 16 Sep 2015 15:07:27 -0400 Subject: [PATCH 145/450] git_futils_mkdir_*: make a relative-to-base mkdir Untangle git_futils_mkdir from git_futils_mkdir_ext - the latter assumes that we own everything beneath the base, as if it were being called with a base of the repository or working directory, and is tailored towards checkout and ensuring that there is no bogosity beneath the base that must be cleaned up. This is (at best) slow and (at worst) unsafe in the larger context of a filesystem where we do not own things and cannot do things like unlink symlinks that are in our way. --- src/checkout.c | 2 +- src/fileops.c | 25 +++++++++++-------- src/fileops.h | 17 ++++++------- src/odb_loose.c | 4 +-- src/refdb_fs.c | 3 ++- src/repository.c | 12 ++++----- tests/checkout/index.c | 2 +- tests/config/global.c | 6 ++--- tests/core/buffer.c | 2 +- tests/core/copy.c | 8 +++--- tests/core/mkdir.c | 54 ++++++++++++++++++++-------------------- tests/core/stat.c | 2 +- tests/index/tests.c | 2 +- tests/odb/alternates.c | 2 +- tests/refs/pack.c | 2 +- tests/repo/discover.c | 16 ++++++------ tests/repo/init.c | 6 ++--- tests/repo/iterator.c | 6 ++--- tests/repo/open.c | 4 +-- tests/status/ignore.c | 14 +++++------ tests/status/worktree.c | 10 ++++---- tests/submodule/status.c | 10 ++++---- 22 files changed, 105 insertions(+), 104 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index 8c06b3335..2a8bfd558 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1366,7 +1366,7 @@ static int checkout_mkdir( mkdir_opts.dir_map = data->mkdir_map; mkdir_opts.pool = &data->pool; - error = git_futils_mkdir_ext( + error = git_futils_mkdir_relative( path, base, mode, flags, &mkdir_opts); data->perfdata.mkdir_calls += mkdir_opts.perfdata.mkdir_calls; diff --git a/src/fileops.c b/src/fileops.c index b7b55159f..b986a6546 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -18,7 +18,7 @@ GIT__USE_STRMAP int git_futils_mkpath2file(const char *file_path, const mode_t mode) { return git_futils_mkdir( - file_path, NULL, mode, + file_path, mode, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR); } @@ -331,8 +331,8 @@ GIT_INLINE(int) validate_existing( return 0; } -int git_futils_mkdir_ext( - const char *path, +int git_futils_mkdir_relative( + const char *relative_path, const char *base, mode_t mode, uint32_t flags, @@ -343,9 +343,13 @@ int git_futils_mkdir_ext( ssize_t root = 0, min_root_len, root_len; char lastch = '/', *tail; struct stat st; + struct git_futils_mkdir_options empty_opts = {0}; + + if (!opts) + opts = &empty_opts; /* build path and find "root" where we should start calling mkdir */ - if (git_path_join_unrooted(&make_path, path, base, &root) < 0) + if (git_path_join_unrooted(&make_path, relative_path, base, &root) < 0) return -1; if (make_path.size == 0) { @@ -503,17 +507,16 @@ done: int git_futils_mkdir( const char *path, - const char *base, mode_t mode, uint32_t flags) { struct git_futils_mkdir_options options = {0}; - return git_futils_mkdir_ext(path, base, mode, flags, &options); + return git_futils_mkdir_relative(path, NULL, mode, flags, &options); } -int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode) +int git_futils_mkdir_r(const char *path, const mode_t mode) { - return git_futils_mkdir(path, base, mode, GIT_MKDIR_PATH); + return git_futils_mkdir(path, mode, GIT_MKDIR_PATH); } typedef struct { @@ -777,7 +780,7 @@ static int _cp_r_mkdir(cp_r_info *info, git_buf *from) /* create root directory the first time we need to create a directory */ if ((info->flags & GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT) == 0) { error = git_futils_mkdir( - info->to_root, NULL, info->dirmode, + info->to_root, info->dirmode, (info->flags & GIT_CPDIR_CHMOD_DIRS) ? GIT_MKDIR_CHMOD : 0); info->flags |= GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT; @@ -785,9 +788,9 @@ static int _cp_r_mkdir(cp_r_info *info, git_buf *from) /* create directory with root as base to prevent excess chmods */ if (!error) - error = git_futils_mkdir( + error = git_futils_mkdir_relative( from->ptr + info->from_prefix, info->to_root, - info->dirmode, info->mkdir_flags); + info->dirmode, info->mkdir_flags, NULL); return error; } diff --git a/src/fileops.h b/src/fileops.h index 0f6466c59..572ff01a5 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -55,12 +55,9 @@ extern int git_futils_creat_locked(const char *path, const mode_t mode); extern int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode); /** - * Create a path recursively - * - * If a base parameter is being passed, it's expected to be valued with a - * path pointing to an already existing directory. + * Create a path recursively. */ -extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode); +extern int git_futils_mkdir_r(const char *path, const mode_t mode); /** * Flags to pass to `git_futils_mkdir`. @@ -111,20 +108,20 @@ struct git_futils_mkdir_options * and optionally chmods the directory immediately after (or each part of the * path if requested). * - * @param path The path to create. + * @param path The path to create, relative to base. * @param base Root for relative path. These directories will never be made. * @param mode The mode to use for created directories. * @param flags Combination of the mkdir flags above. - * @param opts Extended options, use `git_futils_mkdir` if you are not interested. + * @param opts Extended options, or null. * @return 0 on success, else error code */ -extern int git_futils_mkdir_ext(const char *path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts); +extern int git_futils_mkdir_relative(const char *path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts); /** - * Create a directory or entire path. Similar to `git_futils_mkdir_withperf` + * Create a directory or entire path. Similar to `git_futils_mkdir_relative` * without performance data. */ -extern int git_futils_mkdir(const char *path, const char *base, mode_t mode, uint32_t flags); +extern int git_futils_mkdir(const char *path, mode_t mode, uint32_t flags); /** * Create all the folders required to contain diff --git a/src/odb_loose.c b/src/odb_loose.c index 99b8f7c91..730c4b1e1 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -84,9 +84,9 @@ static int object_file_name( static int object_mkdir(const git_buf *name, const loose_backend *be) { - return git_futils_mkdir( + return git_futils_mkdir_relative( name->ptr + be->objects_dirlen, be->objects_dir, be->object_dir_mode, - GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR); + GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR, NULL); } static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 1ddce4649..921f7862b 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -1413,7 +1413,8 @@ static int setup_namespace(git_buf *path, git_repository *repo) git__free(parts); /* Make sure that the folder with the namespace exists */ - if (git_futils_mkdir_r(git_buf_cstr(path), repo->path_repository, 0777) < 0) + if (git_futils_mkdir_relative(git_buf_cstr(path), repo->path_repository, + 0777, GIT_MKDIR_PATH, NULL) < 0) return -1; /* Return root of the namespaced path, i.e. without the trailing '/refs' */ diff --git a/src/repository.c b/src/repository.c index 0f37cfbfe..d0bf7dc79 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1441,8 +1441,8 @@ static int repo_init_structure( if (chmod) mkdir_flags |= GIT_MKDIR_CHMOD; - error = git_futils_mkdir( - tpl->path, repo_dir, dmode, mkdir_flags); + error = git_futils_mkdir_relative( + tpl->path, repo_dir, dmode, mkdir_flags, NULL); } else if (!external_tpl) { const char *content = tpl->content; @@ -1464,7 +1464,7 @@ static int mkdir_parent(git_buf *buf, uint32_t mode, bool skip2) * don't try to set gid or grant world write access */ return git_futils_mkdir( - buf->ptr, NULL, mode & ~(S_ISGID | 0002), + buf->ptr, mode & ~(S_ISGID | 0002), GIT_MKDIR_PATH | GIT_MKDIR_VERIFY_DIR | (skip2 ? GIT_MKDIR_SKIP_LAST2 : GIT_MKDIR_SKIP_LAST)); } @@ -1568,14 +1568,14 @@ static int repo_init_directories( /* create path #4 */ if (wd_path->size > 0 && (error = git_futils_mkdir( - wd_path->ptr, NULL, dirmode & ~S_ISGID, + wd_path->ptr, dirmode & ~S_ISGID, GIT_MKDIR_VERIFY_DIR)) < 0) return error; /* create path #2 (if not the same as #4) */ if (!natural_wd && (error = git_futils_mkdir( - repo_path->ptr, NULL, dirmode & ~S_ISGID, + repo_path->ptr, dirmode & ~S_ISGID, GIT_MKDIR_VERIFY_DIR | GIT_MKDIR_SKIP_LAST)) < 0) return error; } @@ -1585,7 +1585,7 @@ static int repo_init_directories( has_dotgit) { /* create path #1 */ - error = git_futils_mkdir(repo_path->ptr, NULL, dirmode, + error = git_futils_mkdir(repo_path->ptr, dirmode, GIT_MKDIR_VERIFY_DIR | ((dirmode & S_ISGID) ? GIT_MKDIR_CHMOD : 0)); } diff --git a/tests/checkout/index.c b/tests/checkout/index.c index 0d220e141..9fa901867 100644 --- a/tests/checkout/index.c +++ b/tests/checkout/index.c @@ -63,7 +63,7 @@ void test_checkout_index__can_remove_untracked_files(void) { git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; - git_futils_mkdir("./testrepo/dir/subdir/subsubdir", NULL, 0755, GIT_MKDIR_PATH); + git_futils_mkdir("./testrepo/dir/subdir/subsubdir", 0755, GIT_MKDIR_PATH); cl_git_mkfile("./testrepo/dir/one", "one\n"); cl_git_mkfile("./testrepo/dir/subdir/two", "two\n"); diff --git a/tests/config/global.c b/tests/config/global.c index 4481308d6..b5e83fec0 100644 --- a/tests/config/global.c +++ b/tests/config/global.c @@ -6,17 +6,17 @@ void test_config_global__initialize(void) { git_buf path = GIT_BUF_INIT; - cl_git_pass(git_futils_mkdir_r("home", NULL, 0777)); + cl_git_pass(git_futils_mkdir_r("home", 0777)); cl_git_pass(git_path_prettify(&path, "home", NULL)); cl_git_pass(git_libgit2_opts( GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); - cl_git_pass(git_futils_mkdir_r("xdg/git", NULL, 0777)); + cl_git_pass(git_futils_mkdir_r("xdg/git", 0777)); cl_git_pass(git_path_prettify(&path, "xdg/git", NULL)); cl_git_pass(git_libgit2_opts( GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr)); - cl_git_pass(git_futils_mkdir_r("etc", NULL, 0777)); + cl_git_pass(git_futils_mkdir_r("etc", 0777)); cl_git_pass(git_path_prettify(&path, "etc", NULL)); cl_git_pass(git_libgit2_opts( GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr)); diff --git a/tests/core/buffer.c b/tests/core/buffer.c index 0e7026a9c..9872af7f4 100644 --- a/tests/core/buffer.c +++ b/tests/core/buffer.c @@ -929,7 +929,7 @@ void test_core_buffer__similarity_metric(void) cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1)); cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL)); - cl_git_pass(git_futils_mkdir("scratch", NULL, 0755, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir("scratch", 0755, GIT_MKDIR_PATH)); cl_git_mkfile("scratch/testdata", SIMILARITY_TEST_DATA_1); cl_git_pass(git_hashsig_create_fromfile( &b, "scratch/testdata", GIT_HASHSIG_NORMAL)); diff --git a/tests/core/copy.c b/tests/core/copy.c index 04b2dfab5..967748cc5 100644 --- a/tests/core/copy.c +++ b/tests/core/copy.c @@ -25,7 +25,7 @@ void test_core_copy__file_in_dir(void) struct stat st; const char *content = "This is some other stuff to copy\n"; - cl_git_pass(git_futils_mkdir("an_dir/in_a_dir", NULL, 0775, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir("an_dir/in_a_dir", 0775, GIT_MKDIR_PATH)); cl_git_mkfile("an_dir/in_a_dir/copy_me", content); cl_assert(git_path_isdir("an_dir")); @@ -60,9 +60,9 @@ void test_core_copy__tree(void) struct stat st; const char *content = "File content\n"; - cl_git_pass(git_futils_mkdir("src/b", NULL, 0775, GIT_MKDIR_PATH)); - cl_git_pass(git_futils_mkdir("src/c/d", NULL, 0775, GIT_MKDIR_PATH)); - cl_git_pass(git_futils_mkdir("src/c/e", NULL, 0775, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir("src/b", 0775, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir("src/c/d", 0775, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir("src/c/e", 0775, GIT_MKDIR_PATH)); cl_git_mkfile("src/f1", content); cl_git_mkfile("src/b/f2", content); diff --git a/tests/core/mkdir.c b/tests/core/mkdir.c index f76fe1da9..e435a9a64 100644 --- a/tests/core/mkdir.c +++ b/tests/core/mkdir.c @@ -19,37 +19,37 @@ void test_core_mkdir__basic(void) /* make a directory */ cl_assert(!git_path_isdir("d0")); - cl_git_pass(git_futils_mkdir("d0", NULL, 0755, 0)); + cl_git_pass(git_futils_mkdir("d0", 0755, 0)); cl_assert(git_path_isdir("d0")); /* make a path */ cl_assert(!git_path_isdir("d1")); - cl_git_pass(git_futils_mkdir("d1/d1.1/d1.2", NULL, 0755, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir("d1/d1.1/d1.2", 0755, GIT_MKDIR_PATH)); cl_assert(git_path_isdir("d1")); cl_assert(git_path_isdir("d1/d1.1")); cl_assert(git_path_isdir("d1/d1.1/d1.2")); /* make a dir exclusively */ cl_assert(!git_path_isdir("d2")); - cl_git_pass(git_futils_mkdir("d2", NULL, 0755, GIT_MKDIR_EXCL)); + cl_git_pass(git_futils_mkdir("d2", 0755, GIT_MKDIR_EXCL)); cl_assert(git_path_isdir("d2")); /* make exclusive failure */ - cl_git_fail(git_futils_mkdir("d2", NULL, 0755, GIT_MKDIR_EXCL)); + cl_git_fail(git_futils_mkdir("d2", 0755, GIT_MKDIR_EXCL)); /* make a path exclusively */ cl_assert(!git_path_isdir("d3")); - cl_git_pass(git_futils_mkdir("d3/d3.1/d3.2", NULL, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL)); + cl_git_pass(git_futils_mkdir("d3/d3.1/d3.2", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL)); cl_assert(git_path_isdir("d3")); cl_assert(git_path_isdir("d3/d3.1/d3.2")); /* make exclusive path failure */ - cl_git_fail(git_futils_mkdir("d3/d3.1/d3.2", NULL, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL)); + cl_git_fail(git_futils_mkdir("d3/d3.1/d3.2", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL)); /* ??? Should EXCL only apply to the last item in the path? */ /* path with trailing slash? */ cl_assert(!git_path_isdir("d4")); - cl_git_pass(git_futils_mkdir("d4/d4.1/", NULL, 0755, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir("d4/d4.1/", 0755, GIT_MKDIR_PATH)); cl_assert(git_path_isdir("d4/d4.1")); } @@ -65,38 +65,38 @@ void test_core_mkdir__with_base(void) cl_set_cleanup(cleanup_basedir, NULL); - cl_git_pass(git_futils_mkdir(BASEDIR, NULL, 0755, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir(BASEDIR, 0755, GIT_MKDIR_PATH)); - cl_git_pass(git_futils_mkdir("a", BASEDIR, 0755, 0)); + cl_git_pass(git_futils_mkdir_relative("a", BASEDIR, 0755, 0, NULL)); cl_assert(git_path_isdir(BASEDIR "/a")); - cl_git_pass(git_futils_mkdir("b/b1/b2", BASEDIR, 0755, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir_relative("b/b1/b2", BASEDIR, 0755, GIT_MKDIR_PATH, NULL)); cl_assert(git_path_isdir(BASEDIR "/b/b1/b2")); /* exclusive with existing base */ - cl_git_pass(git_futils_mkdir("c/c1/c2", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL)); + cl_git_pass(git_futils_mkdir_relative("c/c1/c2", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL, NULL)); /* fail: exclusive with duplicated suffix */ - cl_git_fail(git_futils_mkdir("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL)); + cl_git_fail(git_futils_mkdir_relative("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL, NULL)); /* fail: exclusive with any duplicated component */ - cl_git_fail(git_futils_mkdir("c/cz/cz", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL)); + cl_git_fail(git_futils_mkdir_relative("c/cz/cz", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL, NULL)); /* success: exclusive without path */ - cl_git_pass(git_futils_mkdir("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_EXCL)); + cl_git_pass(git_futils_mkdir_relative("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_EXCL, NULL)); /* path with shorter base and existing dirs */ - cl_git_pass(git_futils_mkdir("dir/here/d/", "base", 0755, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir_relative("dir/here/d/", "base", 0755, GIT_MKDIR_PATH, NULL)); cl_assert(git_path_isdir("base/dir/here/d")); /* fail: path with shorter base and existing dirs */ - cl_git_fail(git_futils_mkdir("dir/here/e/", "base", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL)); + cl_git_fail(git_futils_mkdir_relative("dir/here/e/", "base", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL, NULL)); /* fail: base with missing components */ - cl_git_fail(git_futils_mkdir("f/", "base/missing", 0755, GIT_MKDIR_PATH)); + cl_git_fail(git_futils_mkdir_relative("f/", "base/missing", 0755, GIT_MKDIR_PATH, NULL)); /* success: shift missing component to path */ - cl_git_pass(git_futils_mkdir("missing/f/", "base/", 0755, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir_relative("missing/f/", "base/", 0755, GIT_MKDIR_PATH, NULL)); } static void cleanup_chmod_root(void *ref) @@ -135,9 +135,9 @@ void test_core_mkdir__chmods(void) cl_set_cleanup(cleanup_chmod_root, old); - cl_git_pass(git_futils_mkdir("r", NULL, 0777, 0)); + cl_git_pass(git_futils_mkdir("r", 0777, 0)); - cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir_relative("mode/is/important", "r", 0777, GIT_MKDIR_PATH, NULL)); cl_git_pass(git_path_lstat("r/mode", &st)); check_mode(0755, st.st_mode); @@ -146,7 +146,7 @@ void test_core_mkdir__chmods(void) cl_git_pass(git_path_lstat("r/mode/is/important", &st)); check_mode(0755, st.st_mode); - cl_git_pass(git_futils_mkdir("mode2/is2/important2", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD)); + cl_git_pass(git_futils_mkdir_relative("mode2/is2/important2", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD, NULL)); cl_git_pass(git_path_lstat("r/mode2", &st)); check_mode(0755, st.st_mode); @@ -155,7 +155,7 @@ void test_core_mkdir__chmods(void) cl_git_pass(git_path_lstat("r/mode2/is2/important2", &st)); check_mode(0777, st.st_mode); - cl_git_pass(git_futils_mkdir("mode3/is3/important3", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH)); + cl_git_pass(git_futils_mkdir_relative("mode3/is3/important3", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH, NULL)); cl_git_pass(git_path_lstat("r/mode3", &st)); check_mode(0777, st.st_mode); @@ -166,7 +166,7 @@ void test_core_mkdir__chmods(void) /* test that we chmod existing dir */ - cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD)); + cl_git_pass(git_futils_mkdir_relative("mode/is/important", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD, NULL)); cl_git_pass(git_path_lstat("r/mode", &st)); check_mode(0755, st.st_mode); @@ -177,7 +177,7 @@ void test_core_mkdir__chmods(void) /* test that we chmod even existing dirs if CHMOD_PATH is set */ - cl_git_pass(git_futils_mkdir("mode2/is2/important2.1", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH)); + cl_git_pass(git_futils_mkdir_relative("mode2/is2/important2.1", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH, NULL)); cl_git_pass(git_path_lstat("r/mode2", &st)); check_mode(0777, st.st_mode); @@ -200,8 +200,8 @@ void test_core_mkdir__mkdir_path_inside_unwriteable_parent(void) *old = p_umask(022); cl_set_cleanup(cleanup_chmod_root, old); - cl_git_pass(git_futils_mkdir("r", NULL, 0777, 0)); - cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir("r", 0777, 0)); + cl_git_pass(git_futils_mkdir_relative("mode/is/important", "r", 0777, GIT_MKDIR_PATH, NULL)); cl_git_pass(git_path_lstat("r/mode", &st)); check_mode(0755, st.st_mode); @@ -210,7 +210,7 @@ void test_core_mkdir__mkdir_path_inside_unwriteable_parent(void) check_mode(0111, st.st_mode); cl_git_pass( - git_futils_mkdir("mode/is/okay/inside", "r", 0777, GIT_MKDIR_PATH)); + git_futils_mkdir_relative("mode/is/okay/inside", "r", 0777, GIT_MKDIR_PATH, NULL)); cl_git_pass(git_path_lstat("r/mode/is/okay/inside", &st)); check_mode(0755, st.st_mode); diff --git a/tests/core/stat.c b/tests/core/stat.c index bd9b990e3..ef2e45a15 100644 --- a/tests/core/stat.c +++ b/tests/core/stat.c @@ -5,7 +5,7 @@ void test_core_stat__initialize(void) { - cl_git_pass(git_futils_mkdir("root/d1/d2", NULL, 0755, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir("root/d1/d2", 0755, GIT_MKDIR_PATH)); cl_git_mkfile("root/file", "whatever\n"); cl_git_mkfile("root/d1/file", "whatever\n"); } diff --git a/tests/index/tests.c b/tests/index/tests.c index 2a416fc7b..1498196b2 100644 --- a/tests/index/tests.c +++ b/tests/index/tests.c @@ -752,7 +752,7 @@ void test_index_tests__reload_from_disk(void) cl_set_cleanup(&cleanup_myrepo, NULL); - cl_git_pass(git_futils_mkdir("./myrepo", NULL, 0777, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir("./myrepo", 0777, GIT_MKDIR_PATH)); cl_git_mkfile("./myrepo/a.txt", "a\n"); cl_git_mkfile("./myrepo/b.txt", "b\n"); diff --git a/tests/odb/alternates.c b/tests/odb/alternates.c index c75f6feaa..b5c0e79c0 100644 --- a/tests/odb/alternates.c +++ b/tests/odb/alternates.c @@ -29,7 +29,7 @@ static void init_linked_repo(const char *path, const char *alternate) cl_git_pass(git_path_prettify(&destpath, alternate, NULL)); cl_git_pass(git_buf_joinpath(&destpath, destpath.ptr, "objects")); cl_git_pass(git_buf_joinpath(&filepath, git_repository_path(repo), "objects/info")); - cl_git_pass(git_futils_mkdir(filepath.ptr, NULL, 0755, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir(filepath.ptr, 0755, GIT_MKDIR_PATH)); cl_git_pass(git_buf_joinpath(&filepath, filepath.ptr , "alternates")); cl_git_pass(git_filebuf_open(&file, git_buf_cstr(&filepath), 0, 0666)); diff --git a/tests/refs/pack.c b/tests/refs/pack.c index 7dfaf6d8f..bda86f69a 100644 --- a/tests/refs/pack.c +++ b/tests/refs/pack.c @@ -36,7 +36,7 @@ void test_refs_pack__empty(void) git_buf temp_path = GIT_BUF_INIT; cl_git_pass(git_buf_join_n(&temp_path, '/', 3, git_repository_path(g_repo), GIT_REFS_HEADS_DIR, "empty_dir")); - cl_git_pass(git_futils_mkdir_r(temp_path.ptr, NULL, GIT_REFS_DIR_MODE)); + cl_git_pass(git_futils_mkdir_r(temp_path.ptr, GIT_REFS_DIR_MODE)); git_buf_free(&temp_path); packall(); diff --git a/tests/repo/discover.c b/tests/repo/discover.c index 7904b6496..86bd7458f 100644 --- a/tests/repo/discover.c +++ b/tests/repo/discover.c @@ -77,7 +77,7 @@ void test_repo_discover__0(void) const char *ceiling_dirs; const mode_t mode = 0777; - git_futils_mkdir_r(DISCOVER_FOLDER, NULL, mode); + git_futils_mkdir_r(DISCOVER_FOLDER, mode); append_ceiling_dir(&ceiling_dirs_buf, TEMP_REPO_FOLDER); ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf); @@ -88,15 +88,15 @@ void test_repo_discover__0(void) git_repository_free(repo); cl_git_pass(git_repository_init(&repo, SUB_REPOSITORY_FOLDER, 0)); - cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, NULL, mode)); + cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode)); cl_git_pass(git_repository_discover(&sub_repository_path, SUB_REPOSITORY_FOLDER, 0, ceiling_dirs)); - cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, NULL, mode)); + cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode)); ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs, &sub_repository_path); ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB, ceiling_dirs, &sub_repository_path); ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, ceiling_dirs, &sub_repository_path); - cl_git_pass(git_futils_mkdir_r(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, NULL, mode)); + cl_git_pass(git_futils_mkdir_r(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, mode)); write_file(REPOSITORY_ALTERNATE_FOLDER "/" DOT_GIT, "gitdir: ../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT); write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/" DOT_GIT, "gitdir: ../../../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT); write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB "/" DOT_GIT, "gitdir: ../../../../"); @@ -105,13 +105,13 @@ void test_repo_discover__0(void) ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, &sub_repository_path); ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, &repository_path); - cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER1, NULL, mode)); + cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER1, mode)); write_file(ALTERNATE_MALFORMED_FOLDER1 "/" DOT_GIT, "Anything but not gitdir:"); - cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER2, NULL, mode)); + cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER2, mode)); write_file(ALTERNATE_MALFORMED_FOLDER2 "/" DOT_GIT, "gitdir:"); - cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER3, NULL, mode)); + cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER3, mode)); write_file(ALTERNATE_MALFORMED_FOLDER3 "/" DOT_GIT, "gitdir: \n\n\n"); - cl_git_pass(git_futils_mkdir_r(ALTERNATE_NOT_FOUND_FOLDER, NULL, mode)); + cl_git_pass(git_futils_mkdir_r(ALTERNATE_NOT_FOUND_FOLDER, mode)); write_file(ALTERNATE_NOT_FOUND_FOLDER "/" DOT_GIT, "gitdir: a_repository_that_surely_does_not_exist"); cl_git_fail(git_repository_discover(&found_path, ALTERNATE_MALFORMED_FOLDER1, 0, ceiling_dirs)); cl_git_fail(git_repository_discover(&found_path, ALTERNATE_MALFORMED_FOLDER2, 0, ceiling_dirs)); diff --git a/tests/repo/init.c b/tests/repo/init.c index 929d74180..7a9ec20f1 100644 --- a/tests/repo/init.c +++ b/tests/repo/init.c @@ -99,7 +99,7 @@ void test_repo_init__bare_repo_escaping_current_workdir(void) cl_git_pass(git_path_prettify_dir(&path_current_workdir, ".", NULL)); cl_git_pass(git_buf_joinpath(&path_repository, git_buf_cstr(&path_current_workdir), "a/b/c")); - cl_git_pass(git_futils_mkdir_r(git_buf_cstr(&path_repository), NULL, GIT_DIR_MODE)); + cl_git_pass(git_futils_mkdir_r(git_buf_cstr(&path_repository), GIT_DIR_MODE)); /* Change the current working directory */ cl_git_pass(chdir(git_buf_cstr(&path_repository))); @@ -312,7 +312,7 @@ void test_repo_init__extended_0(void) cl_git_fail(git_repository_init_ext(&_repo, "extended", &opts)); /* make the directory first, then it should succeed */ - cl_git_pass(git_futils_mkdir("extended", NULL, 0775, 0)); + cl_git_pass(git_futils_mkdir("extended", 0775, 0)); cl_git_pass(git_repository_init_ext(&_repo, "extended", &opts)); cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "/extended/")); @@ -631,7 +631,7 @@ void test_repo_init__can_reinit_an_initialized_repository(void) cl_set_cleanup(&cleanup_repository, "extended"); - cl_git_pass(git_futils_mkdir("extended", NULL, 0775, 0)); + cl_git_pass(git_futils_mkdir("extended", 0775, 0)); cl_git_pass(git_repository_init(&_repo, "extended", false)); cl_git_pass(git_repository_init(&reinit, "extended", false)); diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c index 9c4cc9ea4..83b824691 100644 --- a/tests/repo/iterator.c +++ b/tests/repo/iterator.c @@ -848,14 +848,14 @@ static void build_workdir_tree(const char *root, int dirs, int subs) for (i = 0; i < dirs; ++i) { if (i % 2 == 0) { p_snprintf(buf, sizeof(buf), "%s/dir%02d", root, i); - cl_git_pass(git_futils_mkdir(buf, NULL, 0775, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir(buf, 0775, GIT_MKDIR_PATH)); p_snprintf(buf, sizeof(buf), "%s/dir%02d/file", root, i); cl_git_mkfile(buf, buf); buf[strlen(buf) - 5] = '\0'; } else { p_snprintf(buf, sizeof(buf), "%s/DIR%02d", root, i); - cl_git_pass(git_futils_mkdir(buf, NULL, 0775, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir(buf, 0775, GIT_MKDIR_PATH)); } for (j = 0; j < subs; ++j) { @@ -865,7 +865,7 @@ static void build_workdir_tree(const char *root, int dirs, int subs) case 2: p_snprintf(sub, sizeof(sub), "%s/Sub%02d", buf, j); break; case 3: p_snprintf(sub, sizeof(sub), "%s/SUB%02d", buf, j); break; } - cl_git_pass(git_futils_mkdir(sub, NULL, 0775, GIT_MKDIR_PATH)); + cl_git_pass(git_futils_mkdir(sub, 0775, GIT_MKDIR_PATH)); if (j % 2 == 0) { size_t sublen = strlen(sub); diff --git a/tests/repo/open.c b/tests/repo/open.c index eb459e51d..d3d087231 100644 --- a/tests/repo/open.c +++ b/tests/repo/open.c @@ -91,7 +91,7 @@ static void make_gitlink_dir(const char *dir, const char *linktext) { git_buf path = GIT_BUF_INIT; - cl_git_pass(git_futils_mkdir(dir, NULL, 0777, GIT_MKDIR_VERIFY_DIR)); + cl_git_pass(git_futils_mkdir(dir, 0777, GIT_MKDIR_VERIFY_DIR)); cl_git_pass(git_buf_joinpath(&path, dir, ".git")); cl_git_rewritefile(path.ptr, linktext); git_buf_free(&path); @@ -222,7 +222,7 @@ void test_repo_open__bad_gitlinks(void) cl_git_sandbox_init("attr"); cl_git_pass(p_mkdir("invalid", 0777)); - cl_git_pass(git_futils_mkdir_r("invalid2/.git", NULL, 0777)); + cl_git_pass(git_futils_mkdir_r("invalid2/.git", 0777)); for (scan = bad_links; *scan != NULL; scan++) { make_gitlink_dir("alternate", *scan); diff --git a/tests/status/ignore.c b/tests/status/ignore.c index bbf8f4911..c318046da 100644 --- a/tests/status/ignore.c +++ b/tests/status/ignore.c @@ -148,7 +148,7 @@ void test_status_ignore__ignore_pattern_contains_space(void) cl_git_pass(git_status_file(&flags, g_repo, "foo bar.txt")); cl_assert(flags == GIT_STATUS_IGNORED); - cl_git_pass(git_futils_mkdir_r("empty_standard_repo/foo", NULL, mode)); + cl_git_pass(git_futils_mkdir_r("empty_standard_repo/foo", mode)); cl_git_mkfile("empty_standard_repo/foo/look-ma.txt", "I'm not going to be ignored!"); cl_git_pass(git_status_file(&flags, g_repo, "foo/look-ma.txt")); @@ -206,7 +206,7 @@ void test_status_ignore__subdirectories(void) * used a rooted path for an ignore, so I changed this behavior. */ cl_git_pass(git_futils_mkdir_r( - "empty_standard_repo/test/ignore_me", NULL, 0775)); + "empty_standard_repo/test/ignore_me", 0775)); cl_git_mkfile( "empty_standard_repo/test/ignore_me/file", "I'm going to be ignored!"); cl_git_mkfile( @@ -230,9 +230,9 @@ static void make_test_data(const char *reponame, const char **files) g_repo = cl_git_sandbox_init(reponame); for (scan = files; *scan != NULL; ++scan) { - cl_git_pass(git_futils_mkdir( + cl_git_pass(git_futils_mkdir_relative( *scan + repolen, reponame, - 0777, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST)); + 0777, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST, NULL)); cl_git_mkfile(*scan, "contents"); } } @@ -612,7 +612,7 @@ void test_status_ignore__issue_1766_negated_ignores(void) g_repo = cl_git_sandbox_init("empty_standard_repo"); cl_git_pass(git_futils_mkdir_r( - "empty_standard_repo/a", NULL, 0775)); + "empty_standard_repo/a", 0775)); cl_git_mkfile( "empty_standard_repo/a/.gitignore", "*\n!.gitignore\n"); cl_git_mkfile( @@ -622,7 +622,7 @@ void test_status_ignore__issue_1766_negated_ignores(void) assert_is_ignored("a/ignoreme"); cl_git_pass(git_futils_mkdir_r( - "empty_standard_repo/b", NULL, 0775)); + "empty_standard_repo/b", 0775)); cl_git_mkfile( "empty_standard_repo/b/.gitignore", "*\n!.gitignore\n"); cl_git_mkfile( @@ -1033,7 +1033,7 @@ void test_status_ignore__negate_starstar(void) "code/projects/**/packages/*\n" "!code/projects/**/packages/repositories.config"); - cl_git_pass(git_futils_mkdir_r("code/projects/foo/bar/packages", "empty_standard_repo", 0777)); + cl_git_pass(git_futils_mkdir_r("empty_standard_repo/code/projects/foo/bar/packages", 0777)); cl_git_mkfile("empty_standard_repo/code/projects/foo/bar/packages/repositories.config", ""); cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "code/projects/foo/bar/packages/repositories.config")); diff --git a/tests/status/worktree.c b/tests/status/worktree.c index 75c7b71b0..fc4afc6be 100644 --- a/tests/status/worktree.c +++ b/tests/status/worktree.c @@ -195,7 +195,7 @@ void test_status_worktree__swap_subdir_with_recurse_and_pathspec(void) cl_git_pass(p_rename("status/subdir", "status/current_file")); cl_git_pass(p_rename("status/swap", "status/subdir")); cl_git_mkfile("status/.new_file", "dummy"); - cl_git_pass(git_futils_mkdir_r("status/zzz_new_dir", NULL, 0777)); + cl_git_pass(git_futils_mkdir_r("status/zzz_new_dir", 0777)); cl_git_mkfile("status/zzz_new_dir/new_file", "dummy"); cl_git_mkfile("status/zzz_new_file", "dummy"); @@ -917,7 +917,7 @@ void test_status_worktree__long_filenames(void) // Create directory with amazingly long filename sprintf(path, "empty_standard_repo/%s", longname); - cl_git_pass(git_futils_mkdir_r(path, NULL, 0777)); + cl_git_pass(git_futils_mkdir_r(path, 0777)); sprintf(path, "empty_standard_repo/%s/foo", longname); cl_git_mkfile(path, "dummy"); @@ -1007,7 +1007,7 @@ void test_status_worktree__unreadable(void) status_entry_counts counts = {0}; /* Create directory with no read permission */ - cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", NULL, 0777)); + cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", 0777)); cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy"); p_chmod("empty_standard_repo/no_permission", 0644); @@ -1041,7 +1041,7 @@ void test_status_worktree__unreadable_not_included(void) status_entry_counts counts = {0}; /* Create directory with no read permission */ - cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", NULL, 0777)); + cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", 0777)); cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy"); p_chmod("empty_standard_repo/no_permission", 0644); @@ -1074,7 +1074,7 @@ void test_status_worktree__unreadable_as_untracked(void) status_entry_counts counts = {0}; /* Create directory with no read permission */ - cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", NULL, 0777)); + cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", 0777)); cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy"); p_chmod("empty_standard_repo/no_permission", 0644); diff --git a/tests/submodule/status.c b/tests/submodule/status.c index 5f4e62053..10f385ce9 100644 --- a/tests/submodule/status.c +++ b/tests/submodule/status.c @@ -92,7 +92,7 @@ void test_submodule_status__ignore_none(void) cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0); /* now mkdir sm_unchanged to test uninitialized */ - cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0)); + cl_git_pass(git_futils_mkdir_relative("sm_unchanged", "submod2", 0755, 0, NULL)); status = get_submodule_status(g_repo, "sm_unchanged"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0); @@ -141,7 +141,7 @@ void test_submodule_status__ignore_untracked(void) cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0); /* now mkdir sm_unchanged to test uninitialized */ - cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0)); + cl_git_pass(git_futils_mkdir_relative("sm_unchanged", "submod2", 0755, 0, NULL)); cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0); @@ -185,7 +185,7 @@ void test_submodule_status__ignore_dirty(void) cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0); /* now mkdir sm_unchanged to test uninitialized */ - cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0)); + cl_git_pass(git_futils_mkdir_relative("sm_unchanged", "submod2", 0755, 0, NULL)); cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0); @@ -229,7 +229,7 @@ void test_submodule_status__ignore_all(void) cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); /* now mkdir sm_unchanged to test uninitialized */ - cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0)); + cl_git_pass(git_futils_mkdir_relative("sm_unchanged", "submod2", 0755, 0, NULL)); cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); @@ -338,7 +338,7 @@ void test_submodule_status__untracked_dirs_containing_ignored_files(void) "submod2/.git/modules/sm_unchanged/info/exclude", "\n*.ignored\n"); cl_git_pass( - git_futils_mkdir("sm_unchanged/directory", "submod2", 0755, 0)); + git_futils_mkdir_relative("sm_unchanged/directory", "submod2", 0755, 0, NULL)); cl_git_mkfile( "submod2/sm_unchanged/directory/i_am.ignored", "ignore this file, please\n"); From 08df66301ec677f6cd98175a914dc933e2fb43a9 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 16 Sep 2015 18:07:56 -0400 Subject: [PATCH 146/450] core::mkdir tests: include absolute mkdirs --- tests/core/mkdir.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/core/mkdir.c b/tests/core/mkdir.c index e435a9a64..11fecb118 100644 --- a/tests/core/mkdir.c +++ b/tests/core/mkdir.c @@ -13,6 +13,41 @@ static void cleanup_basic_dirs(void *ref) git_futils_rmdir_r("d4", NULL, GIT_RMDIR_EMPTY_HIERARCHY); } +void test_core_mkdir__absolute(void) +{ + git_buf path = GIT_BUF_INIT; + + cl_set_cleanup(cleanup_basic_dirs, NULL); + + git_buf_joinpath(&path, clar_sandbox_path(), "d0"); + + /* make a directory */ + cl_assert(!git_path_isdir(path.ptr)); + cl_git_pass(git_futils_mkdir(path.ptr, 0755, 0)); + cl_assert(git_path_isdir(path.ptr)); + + git_buf_joinpath(&path, path.ptr, "subdir"); + + /* make a directory */ + cl_assert(!git_path_isdir(path.ptr)); + cl_git_pass(git_futils_mkdir(path.ptr, 0755, 0)); + cl_assert(git_path_isdir(path.ptr)); + + git_buf_joinpath(&path, path.ptr, "another"); + + /* make a directory */ + cl_assert(!git_path_isdir(path.ptr)); + cl_git_pass(git_futils_mkdir_r(path.ptr, 0755)); + cl_assert(git_path_isdir(path.ptr)); + + git_buf_joinpath(&path, clar_sandbox_path(), "d1/foo/bar/asdf"); + + /* make a directory */ + cl_assert(!git_path_isdir(path.ptr)); + cl_git_pass(git_futils_mkdir_r(path.ptr, 0755)); + cl_assert(git_path_isdir(path.ptr)); +} + void test_core_mkdir__basic(void) { cl_set_cleanup(cleanup_basic_dirs, NULL); From 0862ec2eb9529573ab46f3975defc0b7632bede4 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 17 Sep 2015 09:58:38 -0400 Subject: [PATCH 147/450] core::mkdir tests: ensure we don't stomp symlinks in mkdir In `mkdir` and `mkdir_r`, ensure that we don't try to remove symlinks that are in our way. --- src/path.c | 11 +++++++++++ src/path.h | 6 ++++++ tests/core/mkdir.c | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/src/path.c b/src/path.c index cb11acee3..72cb289e0 100644 --- a/src/path.c +++ b/src/path.c @@ -526,6 +526,17 @@ bool git_path_isfile(const char *path) return S_ISREG(st.st_mode) != 0; } +bool git_path_islink(const char *path) +{ + struct stat st; + + assert(path); + if (p_lstat(path, &st) < 0) + return false; + + return S_ISLNK(st.st_mode) != 0; +} + #ifdef GIT_WIN32 bool git_path_is_empty_dir(const char *path) diff --git a/src/path.h b/src/path.h index 971603ea7..c76e90343 100644 --- a/src/path.h +++ b/src/path.h @@ -168,6 +168,12 @@ extern bool git_path_isdir(const char *path); */ extern bool git_path_isfile(const char *path); +/** + * Check if the given path points to a symbolic link. + * @return true or false + */ +extern bool git_path_islink(const char *path); + /** * Check if the given path is a directory, and is empty. */ diff --git a/tests/core/mkdir.c b/tests/core/mkdir.c index 11fecb118..8d487e594 100644 --- a/tests/core/mkdir.c +++ b/tests/core/mkdir.c @@ -222,6 +222,40 @@ void test_core_mkdir__chmods(void) check_mode(0777, st.st_mode); } +void test_core_mkdir__keeps_parent_symlinks(void) +{ +#ifndef GIT_WIN32 + git_buf path = GIT_BUF_INIT; + + cl_set_cleanup(cleanup_basic_dirs, NULL); + + /* make a directory */ + cl_assert(!git_path_isdir("d0")); + cl_git_pass(git_futils_mkdir("d0", 0755, 0)); + cl_assert(git_path_isdir("d0")); + + cl_must_pass(symlink("d0", "d1")); + cl_assert(git_path_islink("d1")); + + cl_git_pass(git_futils_mkdir("d1/foo/bar", 0755, GIT_MKDIR_PATH|GIT_MKDIR_REMOVE_SYMLINKS)); + cl_assert(git_path_islink("d1")); + cl_assert(git_path_isdir("d1/foo/bar")); + cl_assert(git_path_isdir("d0/foo/bar")); + + cl_must_pass(symlink("d0", "d2")); + cl_assert(git_path_islink("d2")); + + git_buf_joinpath(&path, clar_sandbox_path(), "d2/other/dir"); + + cl_git_pass(git_futils_mkdir(path.ptr, 0755, GIT_MKDIR_PATH|GIT_MKDIR_REMOVE_SYMLINKS)); + cl_assert(git_path_islink("d2")); + cl_assert(git_path_isdir("d2/other/dir")); + cl_assert(git_path_isdir("d0/other/dir")); + + git_buf_free(&path); +#endif +} + void test_core_mkdir__mkdir_path_inside_unwriteable_parent(void) { struct stat st; From e24c60dba4cd7c6b768fd6c39d4a0c003a48fb1f Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 17 Sep 2015 09:42:05 -0400 Subject: [PATCH 148/450] mkdir: find component paths for mkdir_relative `git_futils_mkdir` does not blindly call `git_futils_mkdir_relative`. `git_futils_mkdir_relative` is used when you have some base directory and want to create some path inside of it, potentially removing blocking symlinks and files in the process. This is not suitable for a general recursive mkdir within the filesystem. Instead, when `mkdir` is being recursive, locate the first existent parent directory and use that as the base for `mkdir_relative`. --- src/fileops.c | 182 +++++++++++++++++++++++++++++++++------------ tests/core/mkdir.c | 15 ++-- 2 files changed, 144 insertions(+), 53 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index b986a6546..f00c3e821 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -331,6 +331,138 @@ GIT_INLINE(int) validate_existing( return 0; } + +GIT_INLINE(int) mkdir_canonicalize( + git_buf *path, + uint32_t flags) +{ + ssize_t root_len; + + if (path->size == 0) { + giterr_set(GITERR_OS, "attempt to create empty path"); + return -1; + } + + /* Trim trailing slashes (except the root) */ + if ((root_len = git_path_root(path->ptr)) < 0) + root_len = 0; + else + root_len++; + + while (path->size > (size_t)root_len && path->ptr[path->size - 1] == '/') + path->ptr[--path->size] = '\0'; + + /* if we are not supposed to made the last element, truncate it */ + if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) { + git_path_dirname_r(path, path->ptr); + flags |= GIT_MKDIR_SKIP_LAST; + } + if ((flags & GIT_MKDIR_SKIP_LAST) != 0) { + git_path_dirname_r(path, path->ptr); + } + + /* We were either given the root path (or trimmed it to + * the root), we don't have anything to do. + */ + if (path->size <= (size_t)root_len) + git_buf_clear(path); + + return 0; +} + +int git_futils_mkdir( + const char *path, + mode_t mode, + uint32_t flags) +{ + git_buf make_path = GIT_BUF_INIT, parent_path = GIT_BUF_INIT; + const char *relative; + struct git_futils_mkdir_options opts = { 0 }; + struct stat st; + size_t depth = 0; + int len = 0, error; + + if ((error = git_buf_puts(&make_path, path)) < 0 || + (error = mkdir_canonicalize(&make_path, flags)) < 0 || + (error = git_buf_puts(&parent_path, make_path.ptr)) < 0 || + make_path.size == 0) + goto done; + + /* find the first parent directory that exists. this will be used + * as the base to dirname_relative. + */ + for (relative = make_path.ptr; parent_path.size; ) { + error = p_lstat(parent_path.ptr, &st); + + if (error == 0) { + break; + } else if (errno != ENOENT) { + giterr_set(GITERR_OS, "failed to stat '%s'", parent_path.ptr); + goto done; + } + + depth++; + + /* examine the parent of the current path */ + if ((len = git_path_dirname_r(&parent_path, parent_path.ptr)) < 0) { + error = len; + goto done; + } + + assert(len); + + /* we've walked all the given path's parents and it's either relative + * or rooted. either way, give up and make the entire path. + */ + if (len == 1 && + (parent_path.ptr[0] == '.' || parent_path.ptr[0] == '/')) { + relative = make_path.ptr; + break; + } + + relative = make_path.ptr + len + 1; + + /* not recursive? just make this directory relative to its parent. */ + if ((flags & GIT_MKDIR_PATH) == 0) + break; + } + + /* we found an item at the location we're trying to create, + * validate it. + */ + if (depth == 0) { + if ((error = validate_existing(make_path.ptr, &st, mode, flags, &opts.perfdata)) < 0) + goto done; + + if ((flags & GIT_MKDIR_EXCL) != 0) { + giterr_set(GITERR_FILESYSTEM, "failed to make directory '%s': " + "directory exists", make_path.ptr); + error = GIT_EEXISTS; + goto done; + } + + goto done; + } + + /* we already took `SKIP_LAST` and `SKIP_LAST2` into account when + * canonicalizing `make_path`. + */ + flags &= ~(GIT_MKDIR_SKIP_LAST2 | GIT_MKDIR_SKIP_LAST); + + error = git_futils_mkdir_relative(relative, + parent_path.size ? parent_path.ptr : NULL, mode, flags, &opts); + +done: + git_buf_free(&make_path); + git_buf_free(&parent_path); + return error; +} + +int git_futils_mkdir_r(const char *path, const mode_t mode) +{ + return git_futils_mkdir(path, mode, GIT_MKDIR_PATH); +} + int git_futils_mkdir_relative( const char *relative_path, const char *base, @@ -338,12 +470,12 @@ int git_futils_mkdir_relative( uint32_t flags, struct git_futils_mkdir_options *opts) { - int error = -1; git_buf make_path = GIT_BUF_INIT; - ssize_t root = 0, min_root_len, root_len; + ssize_t root = 0, min_root_len; char lastch = '/', *tail; struct stat st; struct git_futils_mkdir_options empty_opts = {0}; + int error; if (!opts) opts = &empty_opts; @@ -352,37 +484,9 @@ int git_futils_mkdir_relative( if (git_path_join_unrooted(&make_path, relative_path, base, &root) < 0) return -1; - if (make_path.size == 0) { - giterr_set(GITERR_OS, "Attempt to create empty path"); + if ((error = mkdir_canonicalize(&make_path, flags)) < 0 || + make_path.size == 0) goto done; - } - - /* Trim trailing slashes (except the root) */ - if ((root_len = git_path_root(make_path.ptr)) < 0) - root_len = 0; - else - root_len++; - - while (make_path.size > (size_t)root_len && - make_path.ptr[make_path.size - 1] == '/') - make_path.ptr[--make_path.size] = '\0'; - - /* if we are not supposed to made the last element, truncate it */ - if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) { - git_path_dirname_r(&make_path, make_path.ptr); - flags |= GIT_MKDIR_SKIP_LAST; - } - if ((flags & GIT_MKDIR_SKIP_LAST) != 0) { - git_path_dirname_r(&make_path, make_path.ptr); - } - - /* We were either given the root path (or trimmed it to - * the root), we don't have anything to do. - */ - if (make_path.size <= (size_t)root_len) { - error = 0; - goto done; - } /* if we are not supposed to make the whole path, reset root */ if ((flags & GIT_MKDIR_PATH) == 0) @@ -505,20 +609,6 @@ done: return error; } -int git_futils_mkdir( - const char *path, - mode_t mode, - uint32_t flags) -{ - struct git_futils_mkdir_options options = {0}; - return git_futils_mkdir_relative(path, NULL, mode, flags, &options); -} - -int git_futils_mkdir_r(const char *path, const mode_t mode) -{ - return git_futils_mkdir(path, mode, GIT_MKDIR_PATH); -} - typedef struct { const char *base; size_t baselen; diff --git a/tests/core/mkdir.c b/tests/core/mkdir.c index 8d487e594..5e6a06002 100644 --- a/tests/core/mkdir.c +++ b/tests/core/mkdir.c @@ -27,25 +27,27 @@ void test_core_mkdir__absolute(void) cl_assert(git_path_isdir(path.ptr)); git_buf_joinpath(&path, path.ptr, "subdir"); - - /* make a directory */ cl_assert(!git_path_isdir(path.ptr)); cl_git_pass(git_futils_mkdir(path.ptr, 0755, 0)); cl_assert(git_path_isdir(path.ptr)); + /* ensure mkdir_r works for a single subdir */ git_buf_joinpath(&path, path.ptr, "another"); - - /* make a directory */ cl_assert(!git_path_isdir(path.ptr)); cl_git_pass(git_futils_mkdir_r(path.ptr, 0755)); cl_assert(git_path_isdir(path.ptr)); + /* ensure mkdir_r works */ git_buf_joinpath(&path, clar_sandbox_path(), "d1/foo/bar/asdf"); - - /* make a directory */ cl_assert(!git_path_isdir(path.ptr)); cl_git_pass(git_futils_mkdir_r(path.ptr, 0755)); cl_assert(git_path_isdir(path.ptr)); + + /* ensure we don't imply recursive */ + git_buf_joinpath(&path, clar_sandbox_path(), "d2/foo/bar/asdf"); + cl_assert(!git_path_isdir(path.ptr)); + cl_git_fail(git_futils_mkdir(path.ptr, 0755, 0)); + cl_assert(!git_path_isdir(path.ptr)); } void test_core_mkdir__basic(void) @@ -285,4 +287,3 @@ void test_core_mkdir__mkdir_path_inside_unwriteable_parent(void) cl_must_pass(p_chmod("r/mode", 0777)); } - From 81aaf3704a2728b0478c0e9f347b2a8c61152081 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 17 Sep 2015 11:26:38 -0400 Subject: [PATCH 149/450] mkdir: chmod existing paths with `GIT_MKDIR_CHMOD` --- src/fileops.c | 91 +++++++++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index f00c3e821..739a98fc4 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -289,48 +289,76 @@ void git_futils_mmap_free(git_map *out) p_munmap(out); } -GIT_INLINE(int) validate_existing( - const char *make_path, +GIT_INLINE(int) mkdir_validate_dir( + const char *path, struct stat *st, mode_t mode, uint32_t flags, - struct git_futils_mkdir_perfdata *perfdata) + struct git_futils_mkdir_options *opts) { + /* with exclusive create, existing dir is an error */ + if ((flags & GIT_MKDIR_EXCL) != 0) { + giterr_set(GITERR_FILESYSTEM, + "Failed to make directory '%s': directory exists", path); + return GIT_EEXISTS; + } + if ((S_ISREG(st->st_mode) && (flags & GIT_MKDIR_REMOVE_FILES)) || (S_ISLNK(st->st_mode) && (flags & GIT_MKDIR_REMOVE_SYMLINKS))) { - if (p_unlink(make_path) < 0) { + if (p_unlink(path) < 0) { giterr_set(GITERR_OS, "Failed to remove %s '%s'", - S_ISLNK(st->st_mode) ? "symlink" : "file", make_path); + S_ISLNK(st->st_mode) ? "symlink" : "file", path); return GIT_EEXISTS; } - perfdata->mkdir_calls++; + opts->perfdata.mkdir_calls++; - if (p_mkdir(make_path, mode) < 0) { - giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path); + if (p_mkdir(path, mode) < 0) { + giterr_set(GITERR_OS, "Failed to make directory '%s'", path); return GIT_EEXISTS; } } else if (S_ISLNK(st->st_mode)) { /* Re-stat the target, make sure it's a directory */ - perfdata->stat_calls++; + opts->perfdata.stat_calls++; - if (p_stat(make_path, st) < 0) { - giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path); + if (p_stat(path, st) < 0) { + giterr_set(GITERR_OS, "Failed to make directory '%s'", path); return GIT_EEXISTS; } } else if (!S_ISDIR(st->st_mode)) { giterr_set(GITERR_FILESYSTEM, - "Failed to make directory '%s': directory exists", make_path); + "Failed to make directory '%s': directory exists", path); return GIT_EEXISTS; } return 0; } +GIT_INLINE(int) mkdir_validate_mode( + const char *path, + struct stat *st, + bool terminal_path, + mode_t mode, + uint32_t flags, + struct git_futils_mkdir_options *opts) +{ + if (((terminal_path && (flags & GIT_MKDIR_CHMOD) != 0) || + (flags & GIT_MKDIR_CHMOD_PATH) != 0) && st->st_mode != mode) { + + opts->perfdata.chmod_calls++; + + if (p_chmod(path, mode) < 0) { + giterr_set(GITERR_OS, "failed to set permissions on '%s'", path); + return -1; + } + } + + return 0; +} GIT_INLINE(int) mkdir_canonicalize( git_buf *path, @@ -431,15 +459,11 @@ int git_futils_mkdir( * validate it. */ if (depth == 0) { - if ((error = validate_existing(make_path.ptr, &st, mode, flags, &opts.perfdata)) < 0) - goto done; + error = mkdir_validate_dir(make_path.ptr, &st, mode, flags, &opts); - if ((flags & GIT_MKDIR_EXCL) != 0) { - giterr_set(GITERR_FILESYSTEM, "failed to make directory '%s': " - "directory exists", make_path.ptr); - error = GIT_EEXISTS; - goto done; - } + if (!error) + error = mkdir_validate_mode( + make_path.ptr, &st, true, mode, flags, &opts); goto done; } @@ -545,32 +569,15 @@ retry_lstat: goto done; } } else { - /* with exclusive create, existing dir is an error */ - if ((flags & GIT_MKDIR_EXCL) != 0) { - giterr_set(GITERR_FILESYSTEM, "Failed to make directory '%s': directory exists", make_path.ptr); - error = GIT_EEXISTS; + if ((error = mkdir_validate_dir( + make_path.ptr, &st, mode, flags, opts)) < 0) goto done; - } - - if ((error = validate_existing( - make_path.ptr, &st, mode, flags, &opts->perfdata)) < 0) - goto done; } /* chmod if requested and necessary */ - if (((flags & GIT_MKDIR_CHMOD_PATH) != 0 || - (lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) && - st.st_mode != mode) { - - opts->perfdata.chmod_calls++; - - if ((error = p_chmod(make_path.ptr, mode)) < 0 && - lastch == '\0') { - giterr_set(GITERR_OS, "Failed to set permissions on '%s'", - make_path.ptr); - goto done; - } - } + if ((error = mkdir_validate_mode( + make_path.ptr, &st, (lastch == '\0'), mode, flags, opts)) < 0) + goto done; if (opts->dir_map && opts->pool) { char *cache_path; From e164ddb11df0d7cd8178c759e323d18a7a7ea526 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 17 Sep 2015 12:23:19 -0400 Subject: [PATCH 150/450] win32: return EACCES in `p_lstat` Don't coalesce all errors into ENOENT. At least identify EACCES. All callers should be handling this case already, as the POSIX `lstat` will return this. --- src/win32/posix_w32.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index c909af6cc..414cb4701 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -148,12 +148,19 @@ static int lstat_w( return git_win32__file_attribute_to_stat(buf, &fdata, path); } - errno = ENOENT; + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + errno = EACCES; + break; + default: + errno = ENOENT; + break; + } /* To match POSIX behavior, set ENOTDIR when any of the folders in the * file path is a regular file, otherwise set ENOENT. */ - if (posix_enotdir) { + if (errno == ENOENT && posix_enotdir) { size_t path_len = wcslen(path); /* scan up path until we find an existing item */ From 9ce2e7b317277b1a27bbcef77fab4d053019b8d8 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 17 Sep 2015 12:48:37 -0400 Subject: [PATCH 151/450] `mkdir`: cope with root path on win32 --- src/fileops.c | 7 ++++--- src/path.h | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index 739a98fc4..57d2ce9c3 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -408,7 +408,7 @@ int git_futils_mkdir( struct git_futils_mkdir_options opts = { 0 }; struct stat st; size_t depth = 0; - int len = 0, error; + int len = 0, root_len, error; if ((error = git_buf_puts(&make_path, path)) < 0 || (error = mkdir_canonicalize(&make_path, flags)) < 0 || @@ -416,6 +416,8 @@ int git_futils_mkdir( make_path.size == 0) goto done; + root_len = git_path_root(make_path.ptr); + /* find the first parent directory that exists. this will be used * as the base to dirname_relative. */ @@ -442,8 +444,7 @@ int git_futils_mkdir( /* we've walked all the given path's parents and it's either relative * or rooted. either way, give up and make the entire path. */ - if (len == 1 && - (parent_path.ptr[0] == '.' || parent_path.ptr[0] == '/')) { + if ((len == 1 && parent_path.ptr[0] == '.') || len == root_len+1) { relative = make_path.ptr; break; } diff --git a/src/path.h b/src/path.h index c76e90343..7e156fce8 100644 --- a/src/path.h +++ b/src/path.h @@ -72,7 +72,7 @@ extern const char *git_path_topdir(const char *path); * This will return a number >= 0 which is the offset to the start of the * path, if the path is rooted (i.e. "/rooted/path" returns 0 and * "c:/windows/rooted/path" returns 2). If the path is not rooted, this - * returns < 0. + * returns -1. */ extern int git_path_root(const char *path); From 5540d9db206c1c967f32d1e9f09f3568a275f3ae Mon Sep 17 00:00:00 2001 From: Dominique Leuenberger Date: Thu, 10 Sep 2015 16:11:10 +0200 Subject: [PATCH 152/450] pkg-config: fix directory references in libgit2.pc Before: libdir=/usr//usr/lib64 includedir=/usr//usr/include After: libdir=/usr/lib64 includedir=/usr/include (note the duplication of /usr in the before case) --- CMakeLists.txt | 17 +++++++++++++++++ libgit2.pc.in | 5 +++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 293153f86..713640d6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,6 +99,23 @@ SET(BIN_INSTALL_DIR bin CACHE PATH "Where to install binaries to.") SET(LIB_INSTALL_DIR lib CACHE PATH "Where to install libraries to.") SET(INCLUDE_INSTALL_DIR include CACHE PATH "Where to install headers to.") +# Set a couple variables to be substituted inside the .pc file. +# We can't just use LIB_INSTALL_DIR in the .pc file, as passing them as absolue +# or relative paths is both valid and supported by cmake. +SET (PKGCONFIG_PREFIX ${CMAKE_INSTALL_PREFIX}) + +IF(IS_ABSOLUTE ${LIB_INSTALL_DIR}) + SET (PKGCONFIG_LIBDIR ${LIB_INSTALL_DIR}) +ELSE(IS_ABSOLUTE ${LIB_INSTALL_DIR}) + SET (PKGCONFIG_LIBDIR "\${prefix}/${LIB_INSTALL_DIR}") +ENDIF (IS_ABSOLUTE ${LIB_INSTALL_DIR}) + +IF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR}) + SET (PKGCONFIG_INCLUDEDIR ${INCLUDE_INSTALL_DIR}) +ELSE(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR}) + SET (PKGCONFIG_INCLUDEDIR "\${prefix}/${INCLUDE_INSTALL_DIR}") +ENDIF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR}) + FUNCTION(TARGET_OS_LIBRARIES target) IF(WIN32) TARGET_LINK_LIBRARIES(${target} ws2_32) diff --git a/libgit2.pc.in b/libgit2.pc.in index 3d825a49f..880266a30 100644 --- a/libgit2.pc.in +++ b/libgit2.pc.in @@ -1,5 +1,6 @@ -libdir=@CMAKE_INSTALL_PREFIX@/@LIB_INSTALL_DIR@ -includedir=@CMAKE_INSTALL_PREFIX@/@INCLUDE_INSTALL_DIR@ +prefix=@PKGCONFIG_PREFIX@ +libdir=@PKGCONFIG_LIBDIR@ +includedir=@PKGCONFIG_INCLUDEDIR@ Name: libgit2 Description: The git library, take 2 From dfe2856d0f3eb66e9199d28a73fab71cad0f3ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 18 Sep 2015 12:06:55 +0200 Subject: [PATCH 153/450] Fix a couple of warnings --- tests/online/push.c | 1 - tests/submodule/lookup.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/online/push.c b/tests/online/push.c index efb763c24..4d2b1d310 100644 --- a/tests/online/push.c +++ b/tests/online/push.c @@ -91,7 +91,6 @@ static int cred_acquire_cb( /** * git_push_status_foreach callback that records status entries. - * @param data (git_vector *) of push_status instances */ static int record_push_status_cb(const char *ref, const char *msg, void *payload) { diff --git a/tests/submodule/lookup.c b/tests/submodule/lookup.c index 5f1614871..38e0fa314 100644 --- a/tests/submodule/lookup.c +++ b/tests/submodule/lookup.c @@ -369,7 +369,7 @@ void test_submodule_lookup__renamed(void) /* Rename the entry in the index */ { const git_index_entry *e; - git_index_entry entry = { 0 }; + git_index_entry entry = {{ 0 }}; e = git_index_get_bypath(idx, "sm_unchanged", 0); cl_assert(e); From 08313c4b12ce80990018fa6fc9a7a93d0fb09ae0 Mon Sep 17 00:00:00 2001 From: Linquize Date: Fri, 18 Sep 2015 11:30:50 +0200 Subject: [PATCH 154/450] config: test that comments are left as with git --- tests/config/write.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/config/write.c b/tests/config/write.c index 9ad11ab27..e634aa326 100644 --- a/tests/config/write.c +++ b/tests/config/write.c @@ -530,6 +530,9 @@ void test_config_write__outside_change(void) git_config_free(cfg); } +#define FOO_COMMENT \ + "; another comment!\n" + #define SECTION_FOO \ "\n" \ " \n" \ @@ -537,7 +540,8 @@ void test_config_write__outside_change(void) " # here's a comment\n" \ "\tname = \"value\"\n" \ " name2 = \"value2\"\n" \ - "; another comment!\n" + +#define SECTION_FOO_WITH_COMMENT SECTION_FOO FOO_COMMENT #define SECTION_BAR \ "[section \"bar\"]\t\n" \ @@ -553,7 +557,7 @@ void test_config_write__preserves_whitespace_and_comments(void) git_buf newfile = GIT_BUF_INIT; /* This config can occur after removing and re-adding the origin remote */ - const char *file_content = SECTION_FOO SECTION_BAR; + const char *file_content = SECTION_FOO_WITH_COMMENT SECTION_BAR; /* Write the test config and make sure the expected entry exists */ cl_git_mkfile(file_name, file_content); @@ -567,9 +571,10 @@ void test_config_write__preserves_whitespace_and_comments(void) cl_assert_equal_strn(SECTION_FOO, n, strlen(SECTION_FOO)); n += strlen(SECTION_FOO); - cl_assert_equal_strn("\tother = otherval\n", n, strlen("\tother = otherval\n")); n += strlen("\tother = otherval\n"); + cl_assert_equal_strn(FOO_COMMENT, n, strlen(FOO_COMMENT)); + n += strlen(FOO_COMMENT); cl_assert_equal_strn(SECTION_BAR, n, strlen(SECTION_BAR)); n += strlen(SECTION_BAR); From cd677b8fe0ff6d843d4733b1a08a5bcff89e4f46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 18 Sep 2015 12:28:05 +0200 Subject: [PATCH 155/450] config: buffer comments to match git's variable-adding When there is a comment at the end of a section, git keeps it there, while we write the new variable right at the end. Keep comments buffered and dump them when we're going to output a variable or section, or reach EOF. This puts us in line with the config files which git produces. --- src/config_file.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/config_file.c b/src/config_file.c index a3fec1b34..46f21c0f1 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -1711,6 +1711,7 @@ static const char *quotes_for_value(const char *value) struct write_data { git_buf *buf; + git_buf buffered_comment; unsigned int in_section : 1, preg_replaced : 1; const char *section; @@ -1719,16 +1720,21 @@ struct write_data { const char *value; }; -static int write_line(struct write_data *write_data, const char *line, size_t line_len) +static int write_line_to(git_buf *buf, const char *line, size_t line_len) { - int result = git_buf_put(write_data->buf, line, line_len); + int result = git_buf_put(buf, line, line_len); if (!result && line_len && line[line_len-1] != '\n') - result = git_buf_printf(write_data->buf, "\n"); + result = git_buf_printf(buf, "\n"); return result; } +static int write_line(struct write_data *write_data, const char *line, size_t line_len) +{ + return write_line_to(write_data->buf, line, line_len); +} + static int write_value(struct write_data *write_data) { const char *q; @@ -1770,6 +1776,14 @@ static int write_on_section( write_data->in_section = strcmp(current_section, write_data->section) == 0; + /* + * If there were comments just before this section, dump them as well. + */ + if (!result) { + result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size); + git_buf_clear(&write_data->buffered_comment); + } + if (!result) result = write_line(write_data, line, line_len); @@ -1787,10 +1801,19 @@ static int write_on_variable( { struct write_data *write_data = (struct write_data *)data; bool has_matched = false; + int error; GIT_UNUSED(reader); GIT_UNUSED(current_section); + /* + * If there were comments just before this variable, let's dump them as well. + */ + if ((error = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0) + return error; + + git_buf_clear(&write_data->buffered_comment); + /* See if we are to update this name/value pair; first examine name */ if (write_data->in_section && strcasecmp(write_data->name, var_name) == 0) @@ -1825,7 +1848,7 @@ static int write_on_comment(struct reader **reader, const char *line, size_t lin GIT_UNUSED(reader); write_data = (struct write_data *)data; - return write_line(write_data, line, line_len); + return write_line_to(&write_data->buffered_comment, line, line_len); } static int write_on_eof(struct reader **reader, void *data) @@ -1835,6 +1858,12 @@ static int write_on_eof(struct reader **reader, void *data) GIT_UNUSED(reader); + /* + * If we've buffered comments when reaching EOF, make sure to dump them. + */ + if ((result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0) + return result; + /* If we are at the EOF and have not written our value (again, for a * simple name/value set, not a multivar) then we have never seen the * section in question and should create a new section and write the @@ -1892,6 +1921,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p section = git__strndup(key, ldot - key); write_data.buf = &buf; + git_buf_init(&write_data.buffered_comment, 0); write_data.section = section; write_data.in_section = 0; write_data.preg_replaced = 0; @@ -1901,6 +1931,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p result = config_parse(reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data); git__free(section); + git_buf_free(&write_data.buffered_comment); if (result < 0) { git_filebuf_cleanup(&file); From e8ddd8d76c119903677b5d0c638c875023ae6784 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 17 Sep 2015 17:49:32 -0400 Subject: [PATCH 156/450] repo::reservedname: test a submodule update Test an initial submodule update, where we are trying to checkout the submodule for the first time, and placing a file within the submodule working directory with the same name as the submodule (and consequently, the same name as the repository itself). --- tests/repo/reservedname.c | 24 ++++++++++++++++++ tests/resources/sub.git/HEAD | Bin 0 -> 23 bytes tests/resources/sub.git/config | Bin 0 -> 157 bytes tests/resources/sub.git/index | Bin 0 -> 405 bytes tests/resources/sub.git/logs/HEAD | Bin 0 -> 174 bytes .../resources/sub.git/logs/refs/heads/master | Bin 0 -> 174 bytes .../10/ddd6d257e01349d514541981aeecea6b2e741d | Bin 0 -> 22 bytes .../17/6a458f94e0ea5272ce67c36bf30b6be9caf623 | Bin 0 -> 28 bytes .../94/c7d78d85c933d1d95b56bc2de01833ba8559fb | Bin 0 -> 132 bytes .../b7/a59b3f4ea13b985f8a1e0d3757d5cd3331add8 | Bin 0 -> 139 bytes .../d0/ee23c41b28746d7e822511d7838bce784ae773 | Bin 0 -> 54 bytes tests/resources/sub.git/refs/heads/master | Bin 0 -> 41 bytes tests/resources/super/.gitted/COMMIT_EDITMSG | Bin 0 -> 10 bytes tests/resources/super/.gitted/HEAD | Bin 0 -> 23 bytes tests/resources/super/.gitted/config | Bin 0 -> 193 bytes tests/resources/super/.gitted/index | Bin 0 -> 217 bytes .../51/589c218bf77a8da9e9d8dbc097d76a742726c4 | Bin 0 -> 90 bytes .../79/d0d58ca6aa1688a073d280169908454cad5b91 | Bin 0 -> 132 bytes .../d7/57768b570a83e80d02edcc1032db14573e5034 | Bin 0 -> 87 bytes .../resources/super/.gitted/refs/heads/master | Bin 0 -> 41 bytes tests/resources/super/gitmodules | Bin 0 -> 48 bytes tests/submodule/submodule_helpers.c | 16 ++++++++++++ tests/submodule/submodule_helpers.h | 1 + 23 files changed, 41 insertions(+) create mode 100644 tests/resources/sub.git/HEAD create mode 100644 tests/resources/sub.git/config create mode 100644 tests/resources/sub.git/index create mode 100644 tests/resources/sub.git/logs/HEAD create mode 100644 tests/resources/sub.git/logs/refs/heads/master create mode 100644 tests/resources/sub.git/objects/10/ddd6d257e01349d514541981aeecea6b2e741d create mode 100644 tests/resources/sub.git/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623 create mode 100644 tests/resources/sub.git/objects/94/c7d78d85c933d1d95b56bc2de01833ba8559fb create mode 100644 tests/resources/sub.git/objects/b7/a59b3f4ea13b985f8a1e0d3757d5cd3331add8 create mode 100644 tests/resources/sub.git/objects/d0/ee23c41b28746d7e822511d7838bce784ae773 create mode 100644 tests/resources/sub.git/refs/heads/master create mode 100644 tests/resources/super/.gitted/COMMIT_EDITMSG create mode 100644 tests/resources/super/.gitted/HEAD create mode 100644 tests/resources/super/.gitted/config create mode 100644 tests/resources/super/.gitted/index create mode 100644 tests/resources/super/.gitted/objects/51/589c218bf77a8da9e9d8dbc097d76a742726c4 create mode 100644 tests/resources/super/.gitted/objects/79/d0d58ca6aa1688a073d280169908454cad5b91 create mode 100644 tests/resources/super/.gitted/objects/d7/57768b570a83e80d02edcc1032db14573e5034 create mode 100644 tests/resources/super/.gitted/refs/heads/master create mode 100644 tests/resources/super/gitmodules diff --git a/tests/repo/reservedname.c b/tests/repo/reservedname.c index faea0cc2b..2a5b38239 100644 --- a/tests/repo/reservedname.c +++ b/tests/repo/reservedname.c @@ -106,3 +106,27 @@ void test_repo_reservedname__submodule_pointer(void) git_repository_free(sub_repo); #endif } + +/* Like the `submodule_pointer` test (above), this ensures that we do not + * follow the gitlink to the submodule's repository location and treat that + * as a reserved name. This tests at an initial submodule update, where the + * submodule repo is being created. + */ +void test_repo_reservedname__submodule_pointer_during_create(void) +{ + git_repository *repo; + git_submodule *sm; + git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT; + git_buf url = GIT_BUF_INIT; + + repo = setup_fixture_super(); + + cl_git_pass(git_buf_joinpath(&url, clar_sandbox_path(), "sub.git")); + cl_repo_set_string(repo, "submodule.sub.url", url.ptr); + + cl_git_pass(git_submodule_lookup(&sm, repo, "sub")); + cl_git_pass(git_submodule_update(sm, 1, &update_options)); + + git_submodule_free(sm); + git_buf_free(&url); +} diff --git a/tests/resources/sub.git/HEAD b/tests/resources/sub.git/HEAD new file mode 100644 index 0000000000000000000000000000000000000000..cb089cd89a7d7686d284d8761201649346b5aa1c GIT binary patch literal 23 ecmXR)O|w!cN=+-)&qz&7Db~+TEG|hc;sO9;xClW2 literal 0 HcmV?d00001 diff --git a/tests/resources/sub.git/config b/tests/resources/sub.git/config new file mode 100644 index 0000000000000000000000000000000000000000..78387c50b472d8c0cdaeca4b56bf23c5b7f791df GIT binary patch literal 157 zcmYj~F%H5o3`OVU6g@&Oz=mM#0T6>}oMPd`mF*(s_9TLV@qN9ge_KWI9)sd8MlRa2 zXaRqt8cCf_(;P!4#v%zjWnjY?9-x23vH`PVKK2BT);-swYt{-=eU0H4(pLN73O4eu U^rVDm@k>7$ViLcSFO^&T0N7wQ1ONa4 literal 0 HcmV?d00001 diff --git a/tests/resources/sub.git/index b/tests/resources/sub.git/index new file mode 100644 index 0000000000000000000000000000000000000000..54be69e33e1917169be0a3fbaba5fd61746ea15a GIT binary patch literal 405 zcmZ?q402{*U|<4bmeAiS$AL5hkUj;X5ukAig3Z7yp5@v<<-x0SOTvLu;?`=J4eDP*<*dE;n62`k)BYy+6Fc>Nra9wz(d_-EKB)6_f kRq%Rq_qhtM=fz>Ee_l3S@LMBvbgC8SC8;G(PQUH}02WnsssI20 literal 0 HcmV?d00001 diff --git a/tests/resources/sub.git/logs/HEAD b/tests/resources/sub.git/logs/HEAD new file mode 100644 index 0000000000000000000000000000000000000000..f636268f6fd72e083ae31c48aa7ad90d6e238f08 GIT binary patch literal 174 zcma*bK?*`45P;!zonn@?DAZB(MMRqx?Yn?FN&~5&Qr9nd7wBJo{6hQ}1nbQbFze(c zhQ*oeyi9`78cQY;B1%u>;6@+c2_DZ@Myqg6bJewQ>}|9>kFk{tNUfDo3YCI`&_Zl} XxA24=SDx%&`xD$(K~HZU-0JoVU8^zE literal 0 HcmV?d00001 diff --git a/tests/resources/sub.git/logs/refs/heads/master b/tests/resources/sub.git/logs/refs/heads/master new file mode 100644 index 0000000000000000000000000000000000000000..f636268f6fd72e083ae31c48aa7ad90d6e238f08 GIT binary patch literal 174 zcma*bK?*`45P;!zonn@?DAZB(MMRqx?Yn?FN&~5&Qr9nd7wBJo{6hQ}1nbQbFze(c zhQ*oeyi9`78cQY;B1%u>;6@+c2_DZ@Myqg6bJewQ>}|9>kFk{tNUfDo3YCI`&_Zl} XxA24=SDx%&`xD$(K~HZU-0JoVU8^zE literal 0 HcmV?d00001 diff --git a/tests/resources/sub.git/objects/10/ddd6d257e01349d514541981aeecea6b2e741d b/tests/resources/sub.git/objects/10/ddd6d257e01349d514541981aeecea6b2e741d new file mode 100644 index 0000000000000000000000000000000000000000..a095b3fb822e3cde46d5d39ff21528c1e1fc70cf GIT binary patch literal 22 ecmb7F=sF|FfcPQQP4}zEJ-XWDauSLElDkA5YKY$pYq^UP|>;c z!`Yv?vtOS2rVLe?n3rFYky@lzQc=PnaQE7!@CU-4S4Bc38`r&gm91AI3sshunUjiB mjfnveC={0_F?TvpB(Aj# zP#zDX=M3Jai6%!5lQ)JGd5n0DNmG`}854s;^h*@sHCFC$qfh7rkCp4j4d%StA6;un toi|>_DRI4kvR0$kMr$}qE2Y@&J|6jxgt)gdN_axg@3Iwc;tNWyL3_OFJ+1%% literal 0 HcmV?d00001 diff --git a/tests/resources/sub.git/objects/d0/ee23c41b28746d7e822511d7838bce784ae773 b/tests/resources/sub.git/objects/d0/ee23c41b28746d7e822511d7838bce784ae773 new file mode 100644 index 0000000000000000000000000000000000000000..d9bb9c84d8053600309e1bd6393d26b61c47bd78 GIT binary patch literal 54 zcmV-60LlM&0V^p=O;s>9XD~D{Ff%bxNY2R2Nzp5*C}9w|d+k#A17XjrA|aBE>)yP| M)+><(08@Jq$s_(2XaE2J literal 0 HcmV?d00001 diff --git a/tests/resources/sub.git/refs/heads/master b/tests/resources/sub.git/refs/heads/master new file mode 100644 index 0000000000000000000000000000000000000000..0e4d6e2a7897e667e388557d25a9c64e9bf2096f GIT binary patch literal 41 ucmV~$!4Uu;2m`Rc)5z5xFb-JUe*}~EVm98HGIR!Cl+6~LE?xn!l45=I^9x=8 literal 0 HcmV?d00001 diff --git a/tests/resources/super/.gitted/COMMIT_EDITMSG b/tests/resources/super/.gitted/COMMIT_EDITMSG new file mode 100644 index 0000000000000000000000000000000000000000..e2d6b8987e3c26ce8b4444db8fe855acf8644936 GIT binary patch literal 10 RcmXReP0GzrDa}dc0st2(1KR)q literal 0 HcmV?d00001 diff --git a/tests/resources/super/.gitted/HEAD b/tests/resources/super/.gitted/HEAD new file mode 100644 index 0000000000000000000000000000000000000000..cb089cd89a7d7686d284d8761201649346b5aa1c GIT binary patch literal 23 ecmXR)O|w!cN=+-)&qz&7Db~+TEG|hc;sO9;xClW2 literal 0 HcmV?d00001 diff --git a/tests/resources/super/.gitted/config b/tests/resources/super/.gitted/config new file mode 100644 index 0000000000000000000000000000000000000000..06a8b77907e29ad26fcf19724b9c70d364c639fd GIT binary patch literal 193 zcmYk0K@NgI3`N(>DF_EZFW}DT(gP3|1Jl9AG9_(?gxg!93wQlL@8@-xSl}5Nft3}F zWzQ^2v<*a&OM7Y0p~*-50|0K8oKE=iM{R9FHCS{ySd~56Wbl_QD_f?*3rF{pzZwZ{Ld=nOy8e8_cL1f08=GFZU6uP literal 0 HcmV?d00001 diff --git a/tests/resources/super/.gitted/index b/tests/resources/super/.gitted/index new file mode 100644 index 0000000000000000000000000000000000000000..cc2ffffb980f6eade02621e7431faffc830c96e9 GIT binary patch literal 217 zcmZ?q402{*U|<5_(BFmvK$-zYgV+$zxCF)m(!qfda}>M3SM{!ZdE@qh>DRML)YXnK zaOp0i)X@;dTKg{30SZ^EfSPb1kPO Nt$y+J!W`S|A^_*II9C7w literal 0 HcmV?d00001 diff --git a/tests/resources/super/.gitted/objects/51/589c218bf77a8da9e9d8dbc097d76a742726c4 b/tests/resources/super/.gitted/objects/51/589c218bf77a8da9e9d8dbc097d76a742726c4 new file mode 100644 index 0000000000000000000000000000000000000000..727d3a696894fe8d07899ae0b520da02596720cd GIT binary patch literal 90 zcmV-g0HyzU0ZYosPg1ZjW{55>P0GzrDa}b$Py#ZQV!1dA5=$}^Y!!e!F3!@T93V5< wDkdg9vm_=aCo>618|fOy#FV5KmlVgu6r~pDmlh?b0+~P!dO%q&0A7+GCFlAeGXMYp literal 0 HcmV?d00001 diff --git a/tests/resources/super/.gitted/objects/79/d0d58ca6aa1688a073d280169908454cad5b91 b/tests/resources/super/.gitted/objects/79/d0d58ca6aa1688a073d280169908454cad5b91 new file mode 100644 index 0000000000000000000000000000000000000000..7fd889d5f1cfe79f0b769d2682e3f5a888f0fa3b GIT binary patch literal 132 zcmV-~0DJ#<0i}&e3IZ_@06pgw{Q;%3nFxb;@dth&Nw>^^u^|h7-|-FJiaJyksdXEm zV?2z;3>16_=a_xK6fH+2G)$#vw1%>FK3Mi>ol0}8(%?>?)CeA{)GlvWc(*^g)vYw? m@*Jlk^$OZKAU@$Z=Ff%bx&`ZxO$<0qG%}Fh02#lDc*!{h#cje0)w+~Fe to>ii*cEr%k00$tl#o29h3`5|2fC0kl5aFs~UyWIQVG`1<2i3UdGe literal 0 HcmV?d00001 diff --git a/tests/resources/super/gitmodules b/tests/resources/super/gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..a3d8f7f5afa24d79f145cf902746e684d1446290 GIT binary patch literal 48 ycmazpE=|hKPbtkwRZs#lm14O#3ld8*6l@iMJTA`Cq8uPoPfs7n(@W1R;Q|1ItPb)3 literal 0 HcmV?d00001 diff --git a/tests/submodule/submodule_helpers.c b/tests/submodule/submodule_helpers.c index 1dc687231..cde69d92d 100644 --- a/tests/submodule/submodule_helpers.c +++ b/tests/submodule/submodule_helpers.c @@ -126,6 +126,22 @@ git_repository *setup_fixture_submod2(void) return repo; } +git_repository *setup_fixture_super(void) +{ + git_repository *repo = cl_git_sandbox_init("super"); + + cl_fixture_sandbox("sub.git"); + p_mkdir("super/sub", 0777); + + rewrite_gitmodules(git_repository_workdir(repo)); + + cl_set_cleanup(cleanup_fixture_submodules, "sub.git"); + + cl_git_pass(git_repository_reinit_filesystem(repo, 1)); + + return repo; +} + git_repository *setup_fixture_submodule_simple(void) { git_repository *repo = cl_git_sandbox_init("submodule_simple"); diff --git a/tests/submodule/submodule_helpers.h b/tests/submodule/submodule_helpers.h index 1493f245f..1191ab35b 100644 --- a/tests/submodule/submodule_helpers.h +++ b/tests/submodule/submodule_helpers.h @@ -4,6 +4,7 @@ extern void rewrite_gitmodules(const char *workdir); extern git_repository *setup_fixture_submodules(void); extern git_repository *setup_fixture_submod2(void); extern git_repository *setup_fixture_submodule_simple(void); +extern git_repository *setup_fixture_super(void); extern unsigned int get_submodule_status(git_repository *, const char *); From 538dfc8816087f3952f499212f1564336b2cba87 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 17 Sep 2015 18:12:05 -0400 Subject: [PATCH 157/450] repository: only reserve repo dirs in the workdir Check that the repository directory is beneath the workdir before adding it to the list of reserved paths. If it is not, then there is no possibility of checking out files into it, and it should not be a reserved word. This is a particular problem with submodules where the repo directory may be in the super's .git directory. --- src/repository.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/repository.c b/src/repository.c index 0f37cfbfe..cbdfd5826 100644 --- a/src/repository.c +++ b/src/repository.c @@ -908,12 +908,28 @@ bool git_repository__reserved_names( buf->size = git_repository__reserved_names_win32[i].size; } - /* Try to add any repo-specific reserved names */ + /* Try to add any repo-specific reserved names - the gitlink file + * within a submodule or the repository (if the repository directory + * is beneath the workdir). These are typically `.git`, but should + * be protected in case they are not. Note, repo and workdir paths + * are always prettified to end in `/`, so a prefixcmp is safe. + */ if (!repo->is_bare) { - const char *reserved_path = repo->path_gitlink ? - repo->path_gitlink : repo->path_repository; + int (*prefixcmp)(const char *, const char *); + int error, ignorecase; - if (reserved_names_add8dot3(repo, reserved_path) < 0) + error = git_repository__cvar( + &ignorecase, repo, GIT_CVAR_IGNORECASE); + prefixcmp = (error || ignorecase) ? git__prefixcmp_icase : + git__prefixcmp; + + if (repo->path_gitlink && + reserved_names_add8dot3(repo, repo->path_gitlink) < 0) + goto on_error; + + if (repo->path_repository && + prefixcmp(repo->path_repository, repo->workdir) == 0 && + reserved_names_add8dot3(repo, repo->path_repository) < 0) goto on_error; } } From e7de893ef899705f7b7e2d9894a7fda147b6d091 Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Mon, 1 Jun 2015 13:43:54 -0600 Subject: [PATCH 158/450] cmake: add USE_NSEC, and only check nanosec m/ctime if enabled --- CMakeLists.txt | 5 +++++ src/diff.c | 4 ++++ src/index.c | 7 +++++-- tests/merge/workdir/dirty.c | 8 +++++++- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 713640d6a..5790a03c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF ) OPTION( USE_ICONV "Link with and use iconv library" OFF ) OPTION( USE_SSH "Link with libssh to enable SSH support" ON ) OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF ) +OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" OFF ) OPTION( VALGRIND "Configure build for valgrind" OFF ) OPTION( CURL "User curl for HTTP if available" ON) @@ -500,6 +501,10 @@ IF (THREADSAFE) ADD_DEFINITIONS(-DGIT_THREADS) ENDIF() +IF (USE_NSEC) + ADD_DEFINITIONS(-DGIT_USE_NSEC) +ENDIF() + ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64) # Collect sourcefiles diff --git a/src/diff.c b/src/diff.c index d97dcd9d2..b2259c74d 100644 --- a/src/diff.c +++ b/src/diff.c @@ -835,7 +835,11 @@ static int maybe_modified( */ } else if (git_oid_iszero(&nitem->id) && new_is_workdir) { bool use_ctime = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0); +#ifdef GIT_USE_NSEC bool use_nanos = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_NANOSECS) != 0); +#else + bool use_nanos = false; +#endif git_index *index; git_iterator_index(&index, info->new_iter); diff --git a/src/index.c b/src/index.c index 2e8934780..54a8b1eec 100644 --- a/src/index.c +++ b/src/index.c @@ -858,8 +858,11 @@ void git_index_entry__init_from_stat( { entry->ctime.seconds = (git_time_t)st->st_ctime; entry->mtime.seconds = (git_time_t)st->st_mtime; - /* entry->mtime.nanoseconds = st->st_mtimensec; */ - /* entry->ctime.nanoseconds = st->st_ctimensec; */ +#if !defined(GIT_WIN32) && !defined(__APPLE__) + /* Apple and Windows doesn't provide these struct stat fields. */ + entry->mtime.nanoseconds = st->st_mtim.tv_nsec; + entry->ctime.nanoseconds = st->st_ctim.tv_nsec; +#endif entry->dev = st->st_rdev; entry->ino = st->st_ino; entry->mode = (!trust_mode && S_ISREG(st->st_mode)) ? diff --git a/tests/merge/workdir/dirty.c b/tests/merge/workdir/dirty.c index 4bf984c23..ed402bd14 100644 --- a/tests/merge/workdir/dirty.c +++ b/tests/merge/workdir/dirty.c @@ -163,9 +163,15 @@ static void hack_index(char *files[]) cl_git_pass(p_stat(path.ptr, &statbuf)); entry->ctime.seconds = (git_time_t)statbuf.st_ctime; - entry->ctime.nanoseconds = 0; entry->mtime.seconds = (git_time_t)statbuf.st_mtime; +#if !defined(GIT_WIN32) && !defined(__APPLE__) + /* Apple and Windows doesn't provide these struct stat fields. */ + entry->ctime.nanoseconds = statbuf.st_ctim.tv_nsec; + entry->mtime.nanoseconds = statbuf.st_mtim.tv_nsec; +#else + entry->ctime.nanoseconds = 0; entry->mtime.nanoseconds = 0; +#endif entry->dev = statbuf.st_dev; entry->ino = statbuf.st_ino; entry->uid = statbuf.st_uid; From 0269833f92149a50f2eb522af8ef6481a4f17ac4 Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Tue, 2 Jun 2015 12:42:07 -0600 Subject: [PATCH 159/450] settings: expose GIT_USE_NSEC flag in git_libgit2_features --- include/git2/common.h | 5 +++-- src/settings.c | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/git2/common.h b/include/git2/common.h index d84a76512..1846e601a 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -99,8 +99,9 @@ GIT_EXTERN(void) git_libgit2_version(int *major, int *minor, int *rev); */ typedef enum { GIT_FEATURE_THREADS = (1 << 0), - GIT_FEATURE_HTTPS = (1 << 1), - GIT_FEATURE_SSH = (1 << 2), + GIT_FEATURE_HTTPS = (1 << 1), + GIT_FEATURE_SSH = (1 << 2), + GIT_FEATURE_NSEC = (1 << 3), } git_feature_t; /** diff --git a/src/settings.c b/src/settings.c index 2097ca314..22e55953a 100644 --- a/src/settings.c +++ b/src/settings.c @@ -33,6 +33,9 @@ int git_libgit2_features() #endif #if defined(GIT_SSH) | GIT_FEATURE_SSH +#endif +#if defined(GIT_USE_NSEC) + | GIT_FEATURE_NSEC #endif ; } From 2be7855727acf829de989f92a8f97a31ec3a378a Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Tue, 2 Jun 2015 12:45:30 -0600 Subject: [PATCH 160/450] caps: add test for GIT_FEATURES_NSEC --- tests/core/features.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/core/features.c b/tests/core/features.c index 5eeb05e81..85cddfeff 100644 --- a/tests/core/features.c +++ b/tests/core/features.c @@ -28,4 +28,10 @@ void test_core_features__0(void) #else cl_assert((caps & GIT_FEATURE_SSH) == 0); #endif + +#if defined(GIT_USE_NSEC) + cl_assert((caps & GIT_FEATURE_NSEC) != 0); +#else + cl_assert((caps & GIT_FEATURE_NSEC) == 0); +#endif } From e9e6df2c8f4a8577fcbcfdf56bf255ef9774b6ec Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Mon, 15 Jun 2015 09:28:55 -0600 Subject: [PATCH 161/450] cmake: Only provide USE_NSEC if struct stat members are avilable. This allows us to remove OS checks from source code, instead relying on CMake to detect whether or not `struct stat` has the nanoseconds members we rely on. --- CMakeLists.txt | 8 +++++++- src/index.c | 3 +-- tests/merge/workdir/dirty.c | 3 +-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5790a03c9..cb1f1b8b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,9 +19,13 @@ CMAKE_POLICY(SET CMP0015 NEW) SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/") INCLUDE(CheckLibraryExists) +INCLUDE(CheckStructHasMember) INCLUDE(AddCFlagIfSupported) INCLUDE(FindPkgConfig) +CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec sys/stat.h + HAVE_STRUCT_STAT_NSEC LANGUAGE C) + # Build options # OPTION( SONAME "Set the (SO)VERSION of the target" ON ) @@ -37,7 +41,9 @@ OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF ) OPTION( USE_ICONV "Link with and use iconv library" OFF ) OPTION( USE_SSH "Link with libssh to enable SSH support" ON ) OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF ) -OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" OFF ) +IF(HAVE_STRUCT_STAT_NSEC) + OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" OFF ) +ENDIF() OPTION( VALGRIND "Configure build for valgrind" OFF ) OPTION( CURL "User curl for HTTP if available" ON) diff --git a/src/index.c b/src/index.c index 54a8b1eec..bd56cc794 100644 --- a/src/index.c +++ b/src/index.c @@ -858,8 +858,7 @@ void git_index_entry__init_from_stat( { entry->ctime.seconds = (git_time_t)st->st_ctime; entry->mtime.seconds = (git_time_t)st->st_mtime; -#if !defined(GIT_WIN32) && !defined(__APPLE__) - /* Apple and Windows doesn't provide these struct stat fields. */ +#if defined(GIT_USE_NSEC) entry->mtime.nanoseconds = st->st_mtim.tv_nsec; entry->ctime.nanoseconds = st->st_ctim.tv_nsec; #endif diff --git a/tests/merge/workdir/dirty.c b/tests/merge/workdir/dirty.c index ed402bd14..f168963b2 100644 --- a/tests/merge/workdir/dirty.c +++ b/tests/merge/workdir/dirty.c @@ -164,8 +164,7 @@ static void hack_index(char *files[]) entry->ctime.seconds = (git_time_t)statbuf.st_ctime; entry->mtime.seconds = (git_time_t)statbuf.st_mtime; -#if !defined(GIT_WIN32) && !defined(__APPLE__) - /* Apple and Windows doesn't provide these struct stat fields. */ +#if defined(GIT_USE_NSEC) entry->ctime.nanoseconds = statbuf.st_ctim.tv_nsec; entry->mtime.nanoseconds = statbuf.st_mtim.tv_nsec; #else From c963fe1d3d7b576cc794049aafaf6bba46a24d30 Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Tue, 23 Jun 2015 09:05:49 -0600 Subject: [PATCH 162/450] cmake: fix CMake code organization problem --- CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cb1f1b8b4..a72659d34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,9 +23,6 @@ INCLUDE(CheckStructHasMember) INCLUDE(AddCFlagIfSupported) INCLUDE(FindPkgConfig) -CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec sys/stat.h - HAVE_STRUCT_STAT_NSEC LANGUAGE C) - # Build options # OPTION( SONAME "Set the (SO)VERSION of the target" ON ) @@ -41,9 +38,6 @@ OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF ) OPTION( USE_ICONV "Link with and use iconv library" OFF ) OPTION( USE_SSH "Link with libssh to enable SSH support" ON ) OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF ) -IF(HAVE_STRUCT_STAT_NSEC) - OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" OFF ) -ENDIF() OPTION( VALGRIND "Configure build for valgrind" OFF ) OPTION( CURL "User curl for HTTP if available" ON) @@ -91,6 +85,12 @@ IF (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") OPTION( USE_OPENSSL "Link with and use openssl library" ON ) ENDIF() +CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec sys/stat.h + HAVE_STRUCT_STAT_NSEC LANGUAGE C) +IF(HAVE_STRUCT_STAT_NSEC) + OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" OFF ) +ENDIF() + # This variable will contain the libraries we need to put into # libgit2.pc's Requires.private. That is, what we're linking to or # what someone who's statically linking us needs to link to. From 360dd4dafd30bced1dab1d516765e347d09b91ba Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Tue, 23 Jun 2015 10:02:48 -0600 Subject: [PATCH 163/450] win32: define our own POSIX struct stat, and support USE_NSEC --- CMakeLists.txt | 2 +- src/common.h | 1 + src/win32/mingw-compat.h | 6 ------ src/win32/msvc-compat.h | 3 --- src/win32/w32_util.h | 22 +++++++++++++-------- src/win32/win32-compat.h | 42 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 src/win32/win32-compat.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a72659d34..0c9c356c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,7 +87,7 @@ ENDIF() CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec sys/stat.h HAVE_STRUCT_STAT_NSEC LANGUAGE C) -IF(HAVE_STRUCT_STAT_NSEC) +IF(HAVE_STRUCT_STAT_NSEC OR WIN32) OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" OFF ) ENDIF() diff --git a/src/common.h b/src/common.h index 6dca36fbd..c87343dda 100644 --- a/src/common.h +++ b/src/common.h @@ -41,6 +41,7 @@ # include # include "win32/msvc-compat.h" # include "win32/mingw-compat.h" +# include "win32/win32-compat.h" # include "win32/error.h" # include "win32/version.h" # ifdef GIT_THREADS diff --git a/src/win32/mingw-compat.h b/src/win32/mingw-compat.h index a4a5a31c7..698ebed1a 100644 --- a/src/win32/mingw-compat.h +++ b/src/win32/mingw-compat.h @@ -11,12 +11,6 @@ #undef stat -#if _WIN32_WINNT >= 0x0601 -#define stat __stat64 -#else -#define stat _stati64 -#endif - #if _WIN32_WINNT < 0x0600 && !defined(__MINGW64_VERSION_MAJOR) #undef MemoryBarrier void __mingworg_MemoryBarrier(void); diff --git a/src/win32/msvc-compat.h b/src/win32/msvc-compat.h index 8004bc1f8..12b50d981 100644 --- a/src/win32/msvc-compat.h +++ b/src/win32/msvc-compat.h @@ -9,9 +9,6 @@ #if defined(_MSC_VER) -/* 64-bit stat information, regardless of USE_32BIT_TIME_T define */ -#define stat __stat64 - typedef unsigned short mode_t; typedef SSIZE_T ssize_t; diff --git a/src/win32/w32_util.h b/src/win32/w32_util.h index 8db3afbec..727ed1cef 100644 --- a/src/win32/w32_util.h +++ b/src/win32/w32_util.h @@ -76,17 +76,23 @@ size_t git_win32__path_trim_end(wchar_t *str, size_t len); size_t git_win32__canonicalize_path(wchar_t *str, size_t len); /** - * Converts a FILETIME structure to a time_t. + * Converts a FILETIME structure to a struct timespec. * * @param FILETIME A pointer to a FILETIME - * @return A time_t containing the same time + * @param ts A pointer to the timespec structure to fill in */ -GIT_INLINE(time_t) git_win32__filetime_to_time_t(const FILETIME *ft) +GIT_INLINE(void) git_win32__filetime_to_timespec( + const FILETIME *ft, + struct timespec *ts) { long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime; winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */ - winTime /= 10000000; /* Nano to seconds resolution */ - return (time_t)winTime; + ts->tv_sec = (time_t)(winTime / 10000000); +#ifdef GIT_USE_NSEC + ts->tv_nsec = (winTime % 10000000) * 100; +#else + ts->tv_nsec = 0; +#endif } GIT_INLINE(void) git_win32__timeval_to_filetime( @@ -122,9 +128,9 @@ GIT_INLINE(int) git_win32__file_attribute_to_stat( st->st_size = ((git_off_t)attrdata->nFileSizeHigh << 32) + attrdata->nFileSizeLow; st->st_dev = _getdrive() - 1; st->st_rdev = st->st_dev; - st->st_atime = git_win32__filetime_to_time_t(&(attrdata->ftLastAccessTime)); - st->st_mtime = git_win32__filetime_to_time_t(&(attrdata->ftLastWriteTime)); - st->st_ctime = git_win32__filetime_to_time_t(&(attrdata->ftCreationTime)); + git_win32__filetime_to_timespec(&(attrdata->ftLastAccessTime), &(st->st_atim)); + git_win32__filetime_to_timespec(&(attrdata->ftLastWriteTime), &(st->st_mtim)); + git_win32__filetime_to_timespec(&(attrdata->ftCreationTime), &(st->st_ctim)); if (attrdata->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && path) { git_win32_path target; diff --git a/src/win32/win32-compat.h b/src/win32/win32-compat.h new file mode 100644 index 000000000..8b4070df7 --- /dev/null +++ b/src/win32/win32-compat.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_win32_compat__ +#define INCLUDE_win32_compat__ + +#include +#include +#include +#include +#include + +struct p_timespec { + time_t tv_sec; + long tv_nsec; +}; + +#define timespec p_timespec + +struct p_stat { + _dev_t st_dev; + _ino_t st_ino; + mode_t st_mode; + short st_nlink; + short st_uid; + short st_gid; + _dev_t st_rdev; + uint64_t st_size; + struct timespec st_atim; + struct timespec st_mtim; + struct timespec st_ctim; +#define st_atime st_atim.tv_sec +#define st_mtime st_mtim.tv_sec +#define st_ctime st_ctim.tv_sec +}; + +#define stat p_stat + +#endif /* INCLUDE_win32_compat__ */ From 0226f7dd36c990e9bc1632adbc655fefc797513a Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Sat, 29 Aug 2015 13:59:20 -0700 Subject: [PATCH 164/450] diff/index: respect USE_NSEC for racily clean file detection --- src/diff.c | 7 +++++-- src/fileops.c | 12 +++++++++--- src/fileops.h | 2 +- src/index.c | 22 +++++++++++++++------- tests/checkout/checkout_helpers.c | 10 +++++----- tests/index/racy.c | 16 ++++++++-------- 6 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/diff.c b/src/diff.c index b2259c74d..f8e0c53d3 100644 --- a/src/diff.c +++ b/src/diff.c @@ -131,7 +131,7 @@ static int diff_delta__from_one( if (status == GIT_DELTA_UNTRACKED && DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED)) return 0; - + if (status == GIT_DELTA_UNREADABLE && DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE)) return 0; @@ -864,7 +864,10 @@ static int maybe_modified( oitem->ino != nitem->ino || oitem->uid != nitem->uid || oitem->gid != nitem->gid || - (index && nitem->mtime.seconds >= index->stamp.mtime)) + (index && + ((nitem->mtime.seconds > (int32_t) index->stamp.mtime.tv_sec) || + ((nitem->mtime.seconds == (int32_t) index->stamp.mtime.tv_sec) && + (nitem->mtime.nanoseconds >= (uint32_t) index->stamp.mtime.tv_nsec))))) { status = GIT_DELTA_MODIFIED; modified_uncertain = true; diff --git a/src/fileops.c b/src/fileops.c index 57d2ce9c3..da1d8a0bb 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -1035,12 +1035,18 @@ int git_futils_filestamp_check( if (p_stat(path, &st) < 0) return GIT_ENOTFOUND; - if (stamp->mtime == (git_time_t)st.st_mtime && + if (stamp->mtime.tv_sec == st.st_mtim.tv_sec && +#if defined(GIT_USE_NSEC) + stamp->mtime.tv_nsec == st.st_mtim.tv_nsec && +#endif stamp->size == (git_off_t)st.st_size && stamp->ino == (unsigned int)st.st_ino) return 0; - stamp->mtime = (git_time_t)st.st_mtime; + stamp->mtime.tv_sec = st.st_mtim.tv_sec; +#if defined(GIT_USE_NSEC) + stamp->mtime.tv_nsec = st.st_mtim.tv_nsec; +#endif stamp->size = (git_off_t)st.st_size; stamp->ino = (unsigned int)st.st_ino; @@ -1063,7 +1069,7 @@ void git_futils_filestamp_set_from_stat( git_futils_filestamp *stamp, struct stat *st) { if (st) { - stamp->mtime = (git_time_t)st->st_mtime; + stamp->mtime = st->st_mtim; stamp->size = (git_off_t)st->st_size; stamp->ino = (unsigned int)st->st_ino; } else { diff --git a/src/fileops.h b/src/fileops.h index 572ff01a5..d806e83ba 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -309,7 +309,7 @@ extern int git_futils_fake_symlink(const char *new, const char *old); * versions could be implemented in the future. */ typedef struct { - git_time_t mtime; + struct timespec mtime; git_off_t size; unsigned int ino; } git_futils_filestamp; diff --git a/src/index.c b/src/index.c index bd56cc794..5311fe85f 100644 --- a/src/index.c +++ b/src/index.c @@ -719,18 +719,27 @@ int git_index__changed_relative_to( return !!git_oid_cmp(&index->checksum, checksum); } -static bool is_racy_timestamp(git_time_t stamp, git_index_entry *entry) +static bool is_racy_timestamp(const struct timespec *stamp, git_index_entry *entry) { /* Git special-cases submodules in the check */ if (S_ISGITLINK(entry->mode)) return false; /* If we never read the index, we can't have this race either */ - if (stamp == 0) + if(stamp->tv_sec == 0) return false; /* If the timestamp is the same or newer than the index, it's racy */ - return ((int32_t) stamp) <= entry->mtime.seconds; +#if defined(GIT_USE_NSEC) + if((int32_t) stamp->tv_sec < entry->mtime.seconds) + return true; + else if((int32_t) stamp->tv_sec > entry->mtime.seconds) + return false; + else + return (uint32_t) stamp->tv_nsec <= entry->mtime.nanoseconds; +#else + return ((int32_t) stamp->tv_sec) <= entry->mtime.seconds; +#endif } /* @@ -742,7 +751,6 @@ static int truncate_racily_clean(git_index *index) size_t i; int error; git_index_entry *entry; - git_time_t ts = index->stamp.mtime; git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT; git_diff *diff; @@ -756,7 +764,7 @@ static int truncate_racily_clean(git_index *index) diff_opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE | GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_DISABLE_PATHSPEC_MATCH; git_vector_foreach(&index->entries, i, entry) { - if (!is_racy_timestamp(ts, entry)) + if (!is_racy_timestamp(&index->stamp.mtime, entry)) continue; /* TODO: use the (non-fnmatching) filelist iterator */ @@ -2875,9 +2883,9 @@ int git_index_read_index( (error = git_iterator_for_index(&new_iterator, (git_index *)new_index, &opts)) < 0) goto done; - if (((error = git_iterator_current(&old_entry, index_iterator)) < 0 && + if (((error = git_iterator_current(&old_entry, index_iterator)) < 0 && error != GIT_ITEROVER) || - ((error = git_iterator_current(&new_entry, new_iterator)) < 0 && + ((error = git_iterator_current(&new_entry, new_iterator)) < 0 && error != GIT_ITEROVER)) goto done; diff --git a/tests/checkout/checkout_helpers.c b/tests/checkout/checkout_helpers.c index 92a454d12..fb2f415e7 100644 --- a/tests/checkout/checkout_helpers.c +++ b/tests/checkout/checkout_helpers.c @@ -132,7 +132,7 @@ int checkout_count_callback( void tick_index(git_index *index) { - git_time_t ts; + struct timespec ts; struct timeval times[2]; cl_assert(index->on_disk); @@ -141,10 +141,10 @@ void tick_index(git_index *index) cl_git_pass(git_index_read(index, true)); ts = index->stamp.mtime; - times[0].tv_sec = ts; - times[0].tv_usec = 0; - times[1].tv_sec = ts + 5; - times[1].tv_usec = 0; + times[0].tv_sec = ts.tv_sec; + times[0].tv_usec = ts.tv_nsec / 1000; + times[1].tv_sec = ts.tv_sec + 5; + times[1].tv_usec = ts.tv_nsec / 1000; cl_git_pass(p_utimes(git_index_path(index), times)); cl_git_pass(git_index_read(index, true)); diff --git a/tests/index/racy.c b/tests/index/racy.c index 3b26aabf4..df25c851c 100644 --- a/tests/index/racy.c +++ b/tests/index/racy.c @@ -63,10 +63,10 @@ void test_index_racy__write_index_just_after_file(void) cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "A")); cl_git_mkfile(path.ptr, "A"); /* Force the file's timestamp to be a second after we wrote the index */ - times[0].tv_sec = index->stamp.mtime + 1; - times[0].tv_usec = 0; - times[1].tv_sec = index->stamp.mtime + 1; - times[1].tv_usec = 0; + times[0].tv_sec = index->stamp.mtime.tv_sec + 1; + times[0].tv_usec = index->stamp.mtime.tv_nsec / 1000; + times[1].tv_sec = index->stamp.mtime.tv_sec + 1; + times[1].tv_usec = index->stamp.mtime.tv_nsec / 1000; cl_git_pass(p_utimes(path.ptr, times)); /* @@ -82,10 +82,10 @@ void test_index_racy__write_index_just_after_file(void) * Pretend this index' modification happend a second after the * file update, and rewrite the file in that same second. */ - times[0].tv_sec = index->stamp.mtime + 2; - times[0].tv_usec = 0; - times[1].tv_sec = index->stamp.mtime + 2; - times[0].tv_usec = 0; + times[0].tv_sec = index->stamp.mtime.tv_sec + 2; + times[0].tv_usec = index->stamp.mtime.tv_nsec / 1000; + times[1].tv_sec = index->stamp.mtime.tv_sec + 2; + times[0].tv_usec = index->stamp.mtime.tv_nsec / 1000; cl_git_pass(p_utimes(git_index_path(index), times)); cl_git_pass(p_utimes(path.ptr, times)); From 28cdb3153c8c6d651c5b2afcf315a2d188e9fde9 Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Fri, 18 Sep 2015 23:48:22 -0700 Subject: [PATCH 165/450] apple: work around non-POSIX struct stat on OS X. --- src/fileops.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index da1d8a0bb..5b54298fa 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -359,7 +359,7 @@ GIT_INLINE(int) mkdir_validate_mode( return 0; } - + GIT_INLINE(int) mkdir_canonicalize( git_buf *path, uint32_t flags) @@ -1027,6 +1027,11 @@ int git_futils_filestamp_check( git_futils_filestamp *stamp, const char *path) { struct stat st; +#if defined(__APPLE__) + const struct timespec *statmtime = &st.st_mtimespec; +#else + const struct timespec *statmtime = &st.st_mtim; +#endif /* if the stamp is NULL, then always reload */ if (stamp == NULL) @@ -1035,17 +1040,17 @@ int git_futils_filestamp_check( if (p_stat(path, &st) < 0) return GIT_ENOTFOUND; - if (stamp->mtime.tv_sec == st.st_mtim.tv_sec && + if (stamp->mtime.tv_sec == statmtime->tv_sec && #if defined(GIT_USE_NSEC) - stamp->mtime.tv_nsec == st.st_mtim.tv_nsec && + stamp->mtime.tv_nsec == statmtime->tv_nsec && #endif stamp->size == (git_off_t)st.st_size && stamp->ino == (unsigned int)st.st_ino) return 0; - stamp->mtime.tv_sec = st.st_mtim.tv_sec; + stamp->mtime.tv_sec = statmtime->tv_sec; #if defined(GIT_USE_NSEC) - stamp->mtime.tv_nsec = st.st_mtim.tv_nsec; + stamp->mtime.tv_nsec = statmtime->tv_nsec; #endif stamp->size = (git_off_t)st.st_size; stamp->ino = (unsigned int)st.st_ino; @@ -1068,8 +1073,14 @@ void git_futils_filestamp_set( void git_futils_filestamp_set_from_stat( git_futils_filestamp *stamp, struct stat *st) { +#if defined(__APPLE__) + const struct timespec *statmtime = &st->st_mtimespec; +#else + const struct timespec *statmtime = &st->st_mtim; +#endif + if (st) { - stamp->mtime = st->st_mtim; + stamp->mtime = *statmtime; stamp->size = (git_off_t)st->st_size; stamp->ino = (unsigned int)st->st_ino; } else { From 92a47824d8d4dc62728ebb4ec1962ec03ae92d85 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 22 Sep 2015 23:10:56 -0400 Subject: [PATCH 166/450] win32: propogate filename too long errors --- src/win32/path_w32.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/win32/path_w32.c b/src/win32/path_w32.c index 118e8bcc5..40b95c33b 100644 --- a/src/win32/path_w32.c +++ b/src/win32/path_w32.c @@ -198,13 +198,13 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src) /* See if this is an absolute path (beginning with a drive letter) */ if (path__is_absolute(src)) { if (git__utf8_to_16(dest, MAX_PATH, src) < 0) - return -1; + goto on_error; } /* File-prefixed NT-style paths beginning with \\?\ */ else if (path__is_nt_namespace(src)) { /* Skip the NT prefix, the destination already contains it */ if (git__utf8_to_16(dest, MAX_PATH, src + PATH__NT_NAMESPACE_LEN) < 0) - return -1; + goto on_error; } /* UNC paths */ else if (path__is_unc(src)) { @@ -213,36 +213,43 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src) /* Skip the leading "\\" */ if (git__utf8_to_16(dest, MAX_PATH - 2, src + 2) < 0) - return -1; + goto on_error; } /* Absolute paths omitting the drive letter */ else if (src[0] == '\\' || src[0] == '/') { if (path__cwd(dest, MAX_PATH) < 0) - return -1; + goto on_error; if (!path__is_absolute(dest)) { errno = ENOENT; - return -1; + goto on_error; } /* Skip the drive letter specification ("C:") */ if (git__utf8_to_16(dest + 2, MAX_PATH - 2, src) < 0) - return -1; + goto on_error; } /* Relative paths */ else { int cwd_len; if ((cwd_len = git_win32_path__cwd(dest, MAX_PATH)) < 0) - return -1; + goto on_error; dest[cwd_len++] = L'\\'; if (git__utf8_to_16(dest + cwd_len, MAX_PATH - cwd_len, src) < 0) - return -1; + goto on_error; } return git_win32_path_canonicalize(out); + +on_error: + /* set windows error code so we can use its error message */ + if (errno == ENAMETOOLONG) + SetLastError(ERROR_FILENAME_EXCED_RANGE); + + return -1; } int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src) From 9768ebb1f385a1e26c317814951042089f6a65e9 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 22 Sep 2015 23:24:30 -0400 Subject: [PATCH 167/450] win32: test checkout msg on long path err --- tests/win32/longpath.c | 62 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 tests/win32/longpath.c diff --git a/tests/win32/longpath.c b/tests/win32/longpath.c new file mode 100644 index 000000000..4fe851c2a --- /dev/null +++ b/tests/win32/longpath.c @@ -0,0 +1,62 @@ +#include "clar_libgit2.h" + +#include "git2/clone.h" +#include "clone.h" +#include "buffer.h" +#include "fileops.h" + +static git_buf path = GIT_BUF_INIT; + +void test_win32_longpath__initialize(void) +{ +#ifdef GIT_WIN32 + const char *base = clar_sandbox_path(); + size_t base_len = strlen(base); + size_t remain = MAX_PATH - base_len; + size_t i; + + git_buf_clear(&path); + git_buf_puts(&path, base); + git_buf_putc(&path, '/'); + + cl_assert(remain < (MAX_PATH - 5)); + + for (i = 0; i < (remain - 5); i++) + git_buf_putc(&path, 'a'); + + printf("%s %" PRIuZ "\n", path.ptr, path.size); +#endif +} + +void test_win32_longpath__cleanup(void) +{ + git_buf_free(&path); +} + +#ifdef GIT_WIN32 +void assert_name_too_long(void) +{ + const git_error *err; + size_t expected_len, actual_len; + const char *expected_msg; + + err = giterr_last(); + actual_len = strlen(err->message); + + expected_msg = git_win32_get_error_message(ERROR_FILENAME_EXCED_RANGE); + expected_len = strlen(expected_msg); + + /* check the suffix */ + cl_assert_equal_s(expected_msg, err->message + (actual_len - expected_len)); +} +#endif + +void test_win32_longpath__errmsg_on_checkout(void) +{ +#ifdef GIT_WIN32 + git_repository *repo; + + cl_git_fail(git_clone(&repo, cl_fixture("testrepo.git"), path.ptr, NULL)); + assert_name_too_long(); +#endif +} From ab8f2c669a2a0bb66d5473f83ecb47ad805a7078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 23 Sep 2015 15:09:19 +0200 Subject: [PATCH 168/450] submodule: plug a few leaks --- src/submodule.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/submodule.c b/src/submodule.c index 1d73dc24e..998ef91fd 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -170,7 +170,7 @@ static int name_from_path(git_buf *out, git_config *cfg, const char *path) git_buf_clear(out); git_buf_put(out, fdot + 1, ldot - fdot - 1); - return 0; + goto cleanup; } if (error == GIT_ITEROVER) { @@ -178,6 +178,8 @@ static int name_from_path(git_buf *out, git_config *cfg, const char *path) error = GIT_ENOTFOUND; } +cleanup: + git_config_iterator_free(iter); return error; } @@ -1701,7 +1703,7 @@ static int submodule_read_config(git_submodule *sm, git_config *cfg) GITERR_CHECK_ALLOC(sm->path); } } else if (error != GIT_ENOTFOUND) { - return error; + goto cleanup; } if ((error = get_value(&value, cfg, &key, sm->name, "url")) == 0) { @@ -1709,7 +1711,7 @@ static int submodule_read_config(git_submodule *sm, git_config *cfg) sm->url = git__strdup(value); GITERR_CHECK_ALLOC(sm->url); } else if (error != GIT_ENOTFOUND) { - return error; + goto cleanup; } if ((error = get_value(&value, cfg, &key, sm->name, "branch")) == 0) { @@ -1717,40 +1719,44 @@ static int submodule_read_config(git_submodule *sm, git_config *cfg) sm->branch = git__strdup(value); GITERR_CHECK_ALLOC(sm->branch); } else if (error != GIT_ENOTFOUND) { - return error; + goto cleanup; } if ((error = get_value(&value, cfg, &key, sm->name, "update")) == 0) { in_config = 1; if ((error = git_submodule_parse_update(&sm->update, value)) < 0) - return error; + goto cleanup; sm->update_default = sm->update; } else if (error != GIT_ENOTFOUND) { - return error; + goto cleanup; } if ((error = get_value(&value, cfg, &key, sm->name, "fetchRecurseSubmodules")) == 0) { in_config = 1; if ((error = git_submodule_parse_recurse(&sm->fetch_recurse, value)) < 0) - return error; + goto cleanup; sm->fetch_recurse_default = sm->fetch_recurse; } else if (error != GIT_ENOTFOUND) { - return error; + goto cleanup; } if ((error = get_value(&value, cfg, &key, sm->name, "ignore")) == 0) { in_config = 1; if ((error = git_submodule_parse_ignore(&sm->ignore, value)) < 0) - return error; + goto cleanup; sm->ignore_default = sm->ignore; } else if (error != GIT_ENOTFOUND) { - return error; + goto cleanup; } if (in_config) sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG; - return 0; + error = 0; + +cleanup: + git_buf_free(&key); + return error; } static int submodule_load_each(const git_config_entry *entry, void *payload) @@ -1784,8 +1790,10 @@ static int submodule_load_each(const git_config_entry *entry, void *payload) * already inserted, we've already loaded it, so we skip. */ pos = git_strmap_lookup_index(map, name.ptr); - if (git_strmap_valid_index(map, pos)) - return 0; + if (git_strmap_valid_index(map, pos)) { + error = 0; + goto done; + } if ((error = submodule_alloc(&sm, data->repo, name.ptr)) < 0) goto done; From 098f1e6e2506b3ca6376617751f877bee6fb5d20 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Thu, 24 Sep 2015 09:09:48 -0400 Subject: [PATCH 169/450] Use an array of forbidden custom headers --- src/transports/smart.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/transports/smart.c b/src/transports/smart.c index 8388d9dc5..1ff39b48e 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -66,14 +66,20 @@ static int git_smart__set_callbacks( return 0; } -#define forbid_custom_header(disallowed_name) \ - if (strncmp(disallowed_name, custom_header, name_len) == 0) \ - return false +static char *forbidden_custom_headers[] = { + "User-Agent", + "Host", + "Accept", + "Content-Type", + "Transfer-Encoding", + "Content-Length", +}; bool is_valid_custom_header(const char *custom_header) { const char *c; int name_len; + unsigned long i; if (custom_header == NULL) return true; @@ -95,12 +101,9 @@ bool is_valid_custom_header(const char *custom_header) return false; // Disallow headers that we set - forbid_custom_header("User-Agent"); - forbid_custom_header("Host"); - forbid_custom_header("Accept"); - forbid_custom_header("Content-Type"); - forbid_custom_header("Transfer-Encoding"); - forbid_custom_header("Content-Length"); + for (i = 0; i < ARRAY_SIZE(forbidden_custom_headers); i++) + if (strncmp(forbidden_custom_headers[i], custom_header, name_len) == 0) + return false; return true; } From 63cc57232cebab60ad193731582e24e83ea5b3b9 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Thu, 24 Sep 2015 09:13:05 -0400 Subject: [PATCH 170/450] Don't null-check --- src/transports/smart.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/transports/smart.c b/src/transports/smart.c index 1ff39b48e..fc7630c49 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -81,9 +81,6 @@ bool is_valid_custom_header(const char *custom_header) int name_len; unsigned long i; - if (custom_header == NULL) - return true; - // Disallow \r and \n c = strchr(custom_header, '\r'); if (c != NULL) From e60db3c79a82459aff9bd90fc97ff9d5fb886a04 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Thu, 24 Sep 2015 09:24:10 -0400 Subject: [PATCH 171/450] Revise custom header error messages If the header doesn't look like a header (e.g. if it doesn't have a ":" or if it has newlines), report "custom HTTP header '%s' is malformed". If the header has the same name as a header already set by libgit2 (e.g. "Host"), report "HTTP header '%s' is already set by libgit2". --- src/transports/smart.c | 86 ++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/src/transports/smart.c b/src/transports/smart.c index fc7630c49..f0f212ca3 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -66,57 +66,55 @@ static int git_smart__set_callbacks( return 0; } -static char *forbidden_custom_headers[] = { - "User-Agent", - "Host", - "Accept", - "Content-Type", - "Transfer-Encoding", - "Content-Length", -}; +int http_header_name_length(const char *http_header) +{ + const char *colon = strchr(http_header, ':'); + if (!colon) + return 0; + return colon - http_header; +} -bool is_valid_custom_header(const char *custom_header) +bool is_malformed_http_header(const char *http_header) { const char *c; int name_len; - unsigned long i; // Disallow \r and \n - c = strchr(custom_header, '\r'); - if (c != NULL) - return false; - c = strchr(custom_header, '\n'); - if (c != NULL) - return false; + c = strchr(http_header, '\r'); + if (c) + return true; + c = strchr(http_header, '\n'); + if (c) + return true; // Require a header name followed by : - c = strchr(custom_header, ':'); - if (c == NULL) - return false; - name_len = c - custom_header; + name_len = http_header_name_length(http_header); if (name_len < 1) - return false; + return true; + + return false; +} + +static char *forbidden_custom_headers[] = { + "User-Agent", + "Host", + "Accept", + "Content-Type", + "Transfer-Encoding", + "Content-Length", +}; + +bool is_forbidden_custom_header(const char *custom_header) +{ + unsigned long i; + int name_len = http_header_name_length(custom_header); // Disallow headers that we set for (i = 0; i < ARRAY_SIZE(forbidden_custom_headers); i++) if (strncmp(forbidden_custom_headers[i], custom_header, name_len) == 0) - return false; + return true; - return true; -} - -const char *find_invalid_custom_header(const git_strarray *custom_headers) -{ - size_t i; - - if (custom_headers == NULL || custom_headers->count == 0) - return NULL; - - for (i = 0; i < custom_headers->count; i++) - if (!is_valid_custom_header(custom_headers->strings[i])) - return custom_headers->strings[i]; - - return NULL; + return false; } static int git_smart__set_custom_headers( @@ -124,11 +122,17 @@ static int git_smart__set_custom_headers( const git_strarray *custom_headers) { transport_smart *t = (transport_smart *)transport; - const char *invalid_header = find_invalid_custom_header(custom_headers); + size_t i; - if (invalid_header != NULL) { - giterr_set(GITERR_INVALID, "Illegal HTTP header '%s'", invalid_header); - return -1; + for (i = 0; i < custom_headers->count; i++) { + if (is_malformed_http_header(custom_headers->strings[i])) { + giterr_set(GITERR_INVALID, "custom HTTP header '%s' is malformed", custom_headers->strings[i]); + return -1; + } + if (is_forbidden_custom_header(custom_headers->strings[i])) { + giterr_set(GITERR_INVALID, "custom HTTP header '%s' is already set by libgit2", custom_headers->strings[i]); + return -1; + } } t->custom_headers = custom_headers; From d16c1b978fa3498a8a800d80ef6ed8a1e39ca314 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Thu, 24 Sep 2015 10:30:37 -0400 Subject: [PATCH 172/450] These can be static --- src/remote.c | 2 +- src/transports/smart.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/remote.c b/src/remote.c index ce6e13b3d..84b8d590f 100644 --- a/src/remote.c +++ b/src/remote.c @@ -687,7 +687,7 @@ int set_transport_callbacks(git_transport *t, const git_remote_callbacks *cbs) cbs->certificate_check, cbs->payload); } -int set_transport_custom_headers(git_transport *t, const git_strarray *custom_headers) +static int set_transport_custom_headers(git_transport *t, const git_strarray *custom_headers) { if (!t->set_custom_headers || !custom_headers) return 0; diff --git a/src/transports/smart.c b/src/transports/smart.c index f0f212ca3..8c5bc89e8 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -66,7 +66,7 @@ static int git_smart__set_callbacks( return 0; } -int http_header_name_length(const char *http_header) +static int http_header_name_length(const char *http_header) { const char *colon = strchr(http_header, ':'); if (!colon) @@ -74,7 +74,7 @@ int http_header_name_length(const char *http_header) return colon - http_header; } -bool is_malformed_http_header(const char *http_header) +static bool is_malformed_http_header(const char *http_header) { const char *c; int name_len; @@ -104,7 +104,7 @@ static char *forbidden_custom_headers[] = { "Content-Length", }; -bool is_forbidden_custom_header(const char *custom_header) +static bool is_forbidden_custom_header(const char *custom_header) { unsigned long i; int name_len = http_header_name_length(custom_header); From d7375662e7b4c885ea4865403e4db4c130e79198 Mon Sep 17 00:00:00 2001 From: Matt Burke Date: Fri, 25 Sep 2015 10:16:16 -0400 Subject: [PATCH 173/450] Copy custom_headers insteach of referencing the caller's copy --- src/remote.c | 2 +- src/transports/http.c | 8 +++----- src/transports/smart.c | 12 +++++++++--- src/transports/smart.h | 2 +- src/transports/winhttp.c | 26 ++++++++++++-------------- 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/remote.c b/src/remote.c index 84b8d590f..2f8ffcb37 100644 --- a/src/remote.c +++ b/src/remote.c @@ -689,7 +689,7 @@ int set_transport_callbacks(git_transport *t, const git_remote_callbacks *cbs) static int set_transport_custom_headers(git_transport *t, const git_strarray *custom_headers) { - if (!t->set_custom_headers || !custom_headers) + if (!t->set_custom_headers) return 0; return t->set_custom_headers(t, custom_headers); diff --git a/src/transports/http.c b/src/transports/http.c index 73ea05043..e5f2b9f28 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -211,11 +211,9 @@ static int gen_request( } else git_buf_puts(buf, "Accept: */*\r\n"); - if (t->owner->custom_headers) { - for (i = 0; i < t->owner->custom_headers->count; i++) { - if (t->owner->custom_headers->strings[i]) - git_buf_printf(buf, "%s\r\n", t->owner->custom_headers->strings[i]); - } + for (i = 0; i < t->owner->custom_headers.count; i++) { + if (t->owner->custom_headers.strings[i]) + git_buf_printf(buf, "%s\r\n", t->owner->custom_headers.strings[i]); } /* Apply credentials to the request */ diff --git a/src/transports/smart.c b/src/transports/smart.c index 8c5bc89e8..b0611c35e 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -124,6 +124,12 @@ static int git_smart__set_custom_headers( transport_smart *t = (transport_smart *)transport; size_t i; + if (t->custom_headers.count) + git_strarray_free(&t->custom_headers); + + if (!custom_headers) + return 0; + for (i = 0; i < custom_headers->count; i++) { if (is_malformed_http_header(custom_headers->strings[i])) { giterr_set(GITERR_INVALID, "custom HTTP header '%s' is malformed", custom_headers->strings[i]); @@ -135,9 +141,7 @@ static int git_smart__set_custom_headers( } } - t->custom_headers = custom_headers; - - return 0; + return git_strarray_copy(&t->custom_headers, custom_headers); } int git_smart__update_heads(transport_smart *t, git_vector *symrefs) @@ -436,6 +440,8 @@ static void git_smart__free(git_transport *transport) git_vector_free(refs); + git_strarray_free(&t->custom_headers); + git__free(t); } diff --git a/src/transports/smart.h b/src/transports/smart.h index 2c87e0200..800466adf 100644 --- a/src/transports/smart.h +++ b/src/transports/smart.h @@ -139,7 +139,7 @@ typedef struct { git_transport_message_cb error_cb; git_transport_certificate_check_cb certificate_check_cb; void *message_cb_payload; - const git_strarray *custom_headers; + git_strarray custom_headers; git_smart_subtransport *wrapped; git_smart_subtransport_stream *current_stream; transport_smart_caps caps; diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index 5b00d9091..b364e906e 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -410,21 +410,19 @@ static int winhttp_stream_connect(winhttp_stream *s) } } - if (t->owner->custom_headers) { - for (i = 0; i < t->owner->custom_headers->count; i++) { - if (t->owner->custom_headers->strings[i]) { - git_buf_clear(&buf); - git_buf_puts(&buf, t->owner->custom_headers->strings[i]); - if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) { - giterr_set(GITERR_OS, "Failed to convert custom header to wide characters"); - goto on_error; - } + for (i = 0; i < t->owner->custom_headers.count; i++) { + if (t->owner->custom_headers.strings[i]) { + git_buf_clear(&buf); + git_buf_puts(&buf, t->owner->custom_headers.strings[i]); + if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) { + giterr_set(GITERR_OS, "Failed to convert custom header to wide characters"); + goto on_error; + } - if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L, - WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { - giterr_set(GITERR_OS, "Failed to add a header to the request"); - goto on_error; - } + if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L, + WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { + giterr_set(GITERR_OS, "Failed to add a header to the request"); + goto on_error; } } } From e4b2b919bb35d94d6dbcb5f7a31805788f2e335b Mon Sep 17 00:00:00 2001 From: Guille -bisho- Date: Fri, 25 Sep 2015 10:37:41 -0700 Subject: [PATCH 174/450] Fix binary diffs git expects an empty line after the binary data: literal X ...binary data... The last literal block of the generated patches were not containing the required empty line. Example: diff --git a/binary_file b/binary_file index 3f1b3f9098131cfecea4a50ff8afab349ea66d22..86e5c1008b5ce635d3e3fffa4434c5eccd8f00b6 100644 GIT binary patch literal 8 Pc${NM&PdElPvrst3ey5{ literal 6 Nc${NM%g@i}0ssZ|0lokL diff --git a/binary_file2 b/binary_file2 index 31be99be19470da4af5b28b21e27896a2f2f9ee2..86e5c1008b5ce635d3e3fffa4434c5eccd8f00b6 100644 GIT binary patch literal 8 Pc${NM&PdElPvrst3ey5{ literal 13 Sc${NMEKbZyOexL+Qd|HZV+4u- git apply of that diff results in: error: corrupt binary patch at line 9: diff --git a/binary_file2 b/binary_file2 fatal: patch with only garbage at line 10 The proper formating is: diff --git a/binary_file b/binary_file index 3f1b3f9098131cfecea4a50ff8afab349ea66d22..86e5c1008b5ce635d3e3fffa4434c5eccd8f00b6 100644 GIT binary patch literal 8 Pc${NM&PdElPvrst3ey5{ literal 6 Nc${NM%g@i}0ssZ|0lokL diff --git a/binary_file2 b/binary_file2 index 31be99be19470da4af5b28b21e27896a2f2f9ee2..86e5c1008b5ce635d3e3fffa4434c5eccd8f00b6 100644 GIT binary patch literal 8 Pc${NM&PdElPvrst3ey5{ literal 13 Sc${NMEKbZyOexL+Qd|HZV+4u- --- src/diff_print.c | 2 +- tests/diff/binary.c | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/diff_print.c b/src/diff_print.c index d406a441a..bc2d6fab0 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -358,6 +358,7 @@ static int format_binary( scan += chunk_len; pi->line.num_lines++; } + git_buf_putc(pi->buf, '\n'); return 0; } @@ -416,7 +417,6 @@ static int diff_print_patch_file_binary( if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data, binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 || - (error = git_buf_putc(pi->buf, '\n')) < 0 || (error = format_binary(pi, binary->old_file.type, binary->old_file.data, binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) { diff --git a/tests/diff/binary.c b/tests/diff/binary.c index 5298e9ebb..173a5994e 100644 --- a/tests/diff/binary.c +++ b/tests/diff/binary.c @@ -96,7 +96,8 @@ void test_diff_binary__add(void) "Kc${Nk-~s>u4FC%O\n" "\n" \ "literal 0\n" \ - "Hc$@u4FC%O\n"; + "Kc${Nk-~s>u4FC%O\n" \ + "\n"; opts.flags = GIT_DIFF_SHOW_BINARY; @@ -177,7 +179,8 @@ void test_diff_binary__delete(void) "Hc$@u4FC%O\n"; + "Kc${Nk-~s>u4FC%O\n" \ + "\n"; opts.flags = GIT_DIFF_SHOW_BINARY; opts.id_abbrev = GIT_OID_HEXSZ; @@ -208,7 +211,8 @@ void test_diff_binary__delta(void) "delta 198\n" \ "zc$}LmI8{(0BqLQJI6p64AwNwaIJGP_Pr*5}Br~;mqJ$PQ;Y(X&QMK*C5^Br3bjG4d=XI^5@\n" \ - "JfH567LIF3FM2!Fd\n"; + "JfH567LIF3FM2!Fd\n" \ + "\n"; opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY; opts.id_abbrev = GIT_OID_HEXSZ; @@ -249,7 +253,8 @@ void test_diff_binary__delta_append(void) "nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \ "\n" \ "delta 7\n" \ - "Oc%18D`@*{63ljhg(E~C7\n"; + "Oc%18D`@*{63ljhg(E~C7\n" \ + "\n"; opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY; opts.id_abbrev = GIT_OID_HEXSZ; @@ -314,7 +319,8 @@ void test_diff_binary__index_to_workdir(void) "nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \ "\n" \ "delta 7\n" \ - "Oc%18D`@*{63ljhg(E~C7\n"; + "Oc%18D`@*{63ljhg(E~C7\n" \ + "\n"; opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY; opts.id_abbrev = GIT_OID_HEXSZ; @@ -379,7 +385,8 @@ void test_diff_binary__print_patch_from_diff(void) "nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \ "\n" \ "delta 7\n" \ - "Oc%18D`@*{63ljhg(E~C7\n"; + "Oc%18D`@*{63ljhg(E~C7\n" \ + "\n"; opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY; opts.id_abbrev = GIT_OID_HEXSZ; From 53a2870514fb06b7aebfd1c55c19a61704906596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 27 Sep 2015 22:48:39 +0200 Subject: [PATCH 175/450] net: add tests against badssl.com These provide bad X.509 certificates, which we should refuse to connect to by default. --- tests/online/badssl.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/online/badssl.c diff --git a/tests/online/badssl.c b/tests/online/badssl.c new file mode 100644 index 000000000..850468320 --- /dev/null +++ b/tests/online/badssl.c @@ -0,0 +1,27 @@ +#include "clar_libgit2.h" + +#include "git2/clone.h" + +static git_repository *g_repo; + +#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT) + +void test_online_badssl__expired(void) +{ + cl_git_fail_with(GIT_ECERTIFICATE, + git_clone(&g_repo, "https://expired.badssl.com/fake.git", "./fake", NULL)); +} + +void test_online_badssl__wrong_host(void) +{ + cl_git_fail_with(GIT_ECERTIFICATE, + git_clone(&g_repo, "https://wrong.host.badssl.com/fake.git", "./fake", NULL)); +} + +void test_online_badssl__self_signed(void) +{ + cl_git_fail_with(GIT_ECERTIFICATE, + git_clone(&g_repo, "https://self-signed.badssl.com/fake.git", "./fake", NULL)); +} + +#endif From 5c5df666b0b2ed4433c6fb931280f9641e967a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 27 Sep 2015 23:32:20 +0200 Subject: [PATCH 176/450] Plug some leaks --- src/submodule.c | 3 +++ tests/core/mkdir.c | 2 ++ tests/index/rename.c | 5 +++++ 3 files changed, 10 insertions(+) diff --git a/src/submodule.c b/src/submodule.c index 998ef91fd..3fd338843 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -229,6 +229,7 @@ int git_submodule_lookup( if (error < 0) { git_submodule_free(sm); + git_buf_free(&path); return error; } @@ -1699,6 +1700,8 @@ static int submodule_read_config(git_submodule *sm, git_config *cfg) * should be strcasecmp */ if (strcmp(sm->name, value) != 0) { + if (sm->path != sm->name) + git__free(sm->path); sm->path = git__strdup(value); GITERR_CHECK_ALLOC(sm->path); } diff --git a/tests/core/mkdir.c b/tests/core/mkdir.c index 5e6a06002..96c972396 100644 --- a/tests/core/mkdir.c +++ b/tests/core/mkdir.c @@ -48,6 +48,8 @@ void test_core_mkdir__absolute(void) cl_assert(!git_path_isdir(path.ptr)); cl_git_fail(git_futils_mkdir(path.ptr, 0755, 0)); cl_assert(!git_path_isdir(path.ptr)); + + git_buf_free(&path); } void test_core_mkdir__basic(void) diff --git a/tests/index/rename.c b/tests/index/rename.c index ebaa9b740..86eaf0053 100644 --- a/tests/index/rename.c +++ b/tests/index/rename.c @@ -77,5 +77,10 @@ void test_index_rename__casechanging(void) cl_assert_equal_i(1, git_index_entrycount(index)); else cl_assert_equal_i(2, git_index_entrycount(index)); + + git_index_free(index); + git_repository_free(repo); + + cl_fixture_cleanup("rename"); } From ea467e74871830da77bec3e351172a637c139823 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 28 Sep 2015 16:46:09 -0400 Subject: [PATCH 177/450] win32::longpath: don't print path --- tests/win32/longpath.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/win32/longpath.c b/tests/win32/longpath.c index 4fe851c2a..6de7d389a 100644 --- a/tests/win32/longpath.c +++ b/tests/win32/longpath.c @@ -23,8 +23,6 @@ void test_win32_longpath__initialize(void) for (i = 0; i < (remain - 5); i++) git_buf_putc(&path, 'a'); - - printf("%s %" PRIuZ "\n", path.ptr, path.size); #endif } From 10df661b8cd2a7e4751f8344633825dfc88be169 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 29 Sep 2015 14:16:51 -0400 Subject: [PATCH 178/450] index: test that add_bypath preserves mode --- tests/index/bypath.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/index/bypath.c b/tests/index/bypath.c index b152b0917..d26273f76 100644 --- a/tests/index/bypath.c +++ b/tests/index/bypath.c @@ -240,3 +240,26 @@ void test_index_bypath__add_honors_existing_case_4(void) cl_assert_equal_s("just_a_dir/a/b/Z/y/X/foo.txt", entry->path); } +void test_index_bypath__add_honors_mode(void) +{ + git_index_entry *entry, new_entry; + + cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); + + memcpy(&new_entry, entry, sizeof(git_index_entry)); + new_entry.path = "README.txt"; + new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE; + + cl_must_pass(p_chmod("submod2/README.txt", GIT_FILEMODE_BLOB_EXECUTABLE)); + + cl_git_pass(git_index_add(g_idx, &new_entry)); + cl_git_pass(git_index_write(g_idx)); + + cl_git_rewritefile("submod2/README.txt", "Modified but still executable"); + + cl_git_pass(git_index_add_bypath(g_idx, "README.txt")); + cl_git_pass(git_index_write(g_idx)); + + cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); + cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode); +} From 146a96de82aebeca5e9b5bfe7fc69456f2bf2d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 30 Sep 2015 09:41:25 +0200 Subject: [PATCH 179/450] openssl: don't try to teardown an unconnected SSL context SSL_shutdown() does not like it when we pass an unitialized ssl context to it. This means that when we fail to connect to a host, we hide the error message saying so with OpenSSL's indecipherable error message. --- src/openssl_stream.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/openssl_stream.c b/src/openssl_stream.c index 8ff53d4b1..54dd761ca 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -302,6 +302,7 @@ cert_fail_name: typedef struct { git_stream parent; git_stream *io; + bool connected; char *host; SSL *ssl; git_cert_x509 cert_info; @@ -318,6 +319,8 @@ int openssl_connect(git_stream *stream) if ((ret = git_stream_connect(st->io)) < 0) return ret; + st->connected = true; + bio = BIO_new(&git_stream_bio_method); GITERR_CHECK_ALLOC(bio); bio->ptr = st->io; @@ -406,9 +409,11 @@ int openssl_close(git_stream *stream) openssl_stream *st = (openssl_stream *) stream; int ret; - if ((ret = ssl_teardown(st->ssl)) < 0) + if (st->connected && (ret = ssl_teardown(st->ssl)) < 0) return -1; + st->connected = false; + return git_stream_close(st->io); } From 8649dfd8df4f0d840a64c1d6c5fc80b8e94a68d1 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 29 Sep 2015 13:36:37 -0400 Subject: [PATCH 180/450] p_futimes: support using futimens when available --- CMakeLists.txt | 6 ++++++ src/unix/posix.h | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c03c718c..a0ef89f76 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ CMAKE_POLICY(SET CMP0015 NEW) SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/") INCLUDE(CheckLibraryExists) +INCLUDE(CheckFunctionExists) INCLUDE(AddCFlagIfSupported) INCLUDE(FindPkgConfig) @@ -431,6 +432,11 @@ ELSE () ADD_C_FLAG_IF_SUPPORTED(-Wno-unused-const-variable) ADD_C_FLAG_IF_SUPPORTED(-Wno-unused-function) + CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS) + IF (HAVE_FUTIMENS) + ADD_DEFINITIONS(-DHAVE_FUTIMENS) + ENDIF () + IF (APPLE) # Apple deprecated OpenSSL ADD_C_FLAG_IF_SUPPORTED(-Wno-deprecated-declarations) ENDIF() diff --git a/src/unix/posix.h b/src/unix/posix.h index 777350990..6633689bc 100644 --- a/src/unix/posix.h +++ b/src/unix/posix.h @@ -22,7 +22,6 @@ typedef int GIT_SOCKET; #define p_stat(p,b) stat(p, b) #define p_utimes(f, t) utimes(f, t) -#define p_futimes(f, t) futimes(f, t) #define p_readlink(a, b, c) readlink(a, b, c) #define p_symlink(o,n) symlink(o, n) @@ -53,4 +52,18 @@ extern char *p_realpath(const char *, char *); #define p_localtime_r(c, r) localtime_r(c, r) #define p_gmtime_r(c, r) gmtime_r(c, r) +#ifdef HAVE_FUTIMENS +GIT_INLINE(int) p_futimes(int f, const struct timeval t[2]) +{ + struct timespec s[2]; + s[0].tv_sec = t[0].tv_sec; + s[0].tv_nsec = t[0].tv_usec * 1000; + s[1].tv_sec = t[1].tv_sec; + s[1].tv_nsec = t[1].tv_usec * 1000; + return futimens(f, s); +} +#else +# define p_futimes futimes +#endif + #endif From e683d15247ef7231143b46580f07113ecae43773 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 30 Sep 2015 05:49:04 -0400 Subject: [PATCH 181/450] qsort_r/qsort_s: detect their support --- CMakeLists.txt | 20 +++++++++++++++----- src/util.c | 21 ++++++++------------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a0ef89f76..e6c06413b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -432,11 +432,6 @@ ELSE () ADD_C_FLAG_IF_SUPPORTED(-Wno-unused-const-variable) ADD_C_FLAG_IF_SUPPORTED(-Wno-unused-function) - CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS) - IF (HAVE_FUTIMENS) - ADD_DEFINITIONS(-DHAVE_FUTIMENS) - ENDIF () - IF (APPLE) # Apple deprecated OpenSSL ADD_C_FLAG_IF_SUPPORTED(-Wno-deprecated-declarations) ENDIF() @@ -447,6 +442,21 @@ ELSE () ENDIF () ENDIF() +CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS) +IF (HAVE_FUTIMENS) + ADD_DEFINITIONS(-DHAVE_FUTIMENS) +ENDIF () + +CHECK_FUNCTION_EXISTS(qsort_r HAVE_QSORT_R) +IF (HAVE_QSORT_R) + ADD_DEFINITIONS(-DHAVE_QSORT_R) +ENDIF () + +CHECK_FUNCTION_EXISTS(qsort_s HAVE_QSORT_S) +IF (HAVE_QSORT_S) + ADD_DEFINITIONS(-DHAVE_QSORT_S) +ENDIF () + IF( NOT CMAKE_CONFIGURATION_TYPES ) # Build Debug by default IF (NOT CMAKE_BUILD_TYPE) diff --git a/src/util.c b/src/util.c index b3929bca2..9e67f4347 100644 --- a/src/util.c +++ b/src/util.c @@ -611,7 +611,7 @@ size_t git__unescape(char *str) return (pos - str); } -#if defined(GIT_WIN32) || defined(BSD) +#if defined(HAVE_QSORT_S) || (defined(HAVE_QSORT_R) && defined(BSD)) typedef struct { git__sort_r_cmp cmp; void *payload; @@ -628,21 +628,16 @@ static int GIT_STDLIB_CALL git__qsort_r_glue_cmp( void git__qsort_r( void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload) { -#if defined(__MINGW32__) || defined(AMIGA) || \ - defined(__OpenBSD__) || defined(__NetBSD__) || \ - defined(__gnu_hurd__) || defined(__ANDROID_API__) || \ - defined(__sun) || defined(__CYGWIN__) || \ - (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8) || \ - (defined(_MSC_VER) && _MSC_VER < 1500) - git__insertsort_r(els, nel, elsize, NULL, cmp, payload); -#elif defined(GIT_WIN32) - git__qsort_r_glue glue = { cmp, payload }; - qsort_s(els, nel, elsize, git__qsort_r_glue_cmp, &glue); -#elif defined(BSD) +#if defined(HAVE_QSORT_R) && defined(BSD) git__qsort_r_glue glue = { cmp, payload }; qsort_r(els, nel, elsize, &glue, git__qsort_r_glue_cmp); -#else +#elif defined(HAVE_QSORT_R) && defined(__GLIBC__) qsort_r(els, nel, elsize, cmp, payload); +#elif defined(HAVE_QSORT_S) + git__qsort_r_glue glue = { cmp, payload }; + qsort_s(els, nel, elsize, git__qsort_r_glue_cmp, &glue); +#else + git__insertsort_r(els, nel, elsize, NULL, cmp, payload); #endif } From 21515f228b739a3ecd2237bafbba50e8d219d8dd Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 29 Sep 2015 15:49:16 -0400 Subject: [PATCH 182/450] index: also try conflict mode when inserting When we do not trust the on-disk mode, we use the mode of an existing index entry. This allows us to preserve executable bits on platforms that do not honor them on the filesystem. If there is no stage 0 index entry, also look at conflicts to attempt to answer this question: prefer the data from the 'ours' side, then the 'theirs' side before falling back to the common ancestor. --- include/git2/index.h | 28 ++++++++++++---- src/index.c | 77 +++++++++++++++++++++++++++++++++++--------- tests/index/bypath.c | 67 +++++++++++++++++++++++++++++++++++++- 3 files changed, 149 insertions(+), 23 deletions(-) diff --git a/include/git2/index.h b/include/git2/index.h index 176ba301e..466765be3 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -154,13 +154,27 @@ typedef enum { GIT_INDEX_ADD_CHECK_PATHSPEC = (1u << 2), } git_index_add_option_t; -/** - * Match any index stage. - * - * Some index APIs take a stage to match; pass this value to match - * any entry matching the path regardless of stage. - */ -#define GIT_INDEX_STAGE_ANY -1 +typedef enum { + /** + * Match any index stage. + * + * Some index APIs take a stage to match; pass this value to match + * any entry matching the path regardless of stage. + */ + GIT_INDEX_STAGE_ANY = -1, + + /** A normal staged file in the index. */ + GIT_INDEX_STAGE_NORMAL = 0, + + /** The ancestor side of a conflict. */ + GIT_INDEX_STAGE_ANCESTOR = 1, + + /** The "ours" side of a conflict. */ + GIT_INDEX_STAGE_OURS = 2, + + /** The "theirs" side of a conflict. */ + GIT_INDEX_STAGE_THEIRS = 3, +} git_index_stage_t; /** @name Index File Functions * diff --git a/src/index.c b/src/index.c index 2e8934780..c0be5b90d 100644 --- a/src/index.c +++ b/src/index.c @@ -1114,7 +1114,9 @@ static int check_file_directory_collision(git_index *index, } static int canonicalize_directory_path( - git_index *index, git_index_entry *entry) + git_index *index, + git_index_entry *entry, + git_index_entry *existing) { const git_index_entry *match, *best = NULL; char *search, *sep; @@ -1124,8 +1126,8 @@ static int canonicalize_directory_path( return 0; /* item already exists in the index, simply re-use the existing case */ - if ((match = git_index_get_bypath(index, entry->path, 0)) != NULL) { - memcpy((char *)entry->path, match->path, strlen(entry->path)); + if (existing) { + memcpy((char *)entry->path, existing->path, strlen(existing->path)); return 0; } @@ -1190,6 +1192,52 @@ static int index_no_dups(void **old, void *new) return GIT_EEXISTS; } +static void index_existing_and_best( + const git_index_entry **existing, + size_t *existing_position, + const git_index_entry **best, + git_index *index, + const git_index_entry *entry) +{ + const git_index_entry *e; + size_t pos; + int error; + + error = index_find(&pos, + index, entry->path, 0, GIT_IDXENTRY_STAGE(entry), false); + + if (error == 0) { + *existing = index->entries.contents[pos]; + *existing_position = pos; + *best = index->entries.contents[pos]; + return; + } + + *existing = NULL; + *existing_position = 0; + *best = NULL; + + if (GIT_IDXENTRY_STAGE(entry) == 0) { + for (; pos < index->entries.length; pos++) { + int (*strcomp)(const char *a, const char *b) = + index->ignore_case ? git__strcasecmp : git__strcmp; + + e = index->entries.contents[pos]; + + if (strcomp(entry->path, e->path) != 0) + break; + + if (GIT_IDXENTRY_STAGE(e) == GIT_INDEX_STAGE_ANCESTOR) { + *best = e; + continue; + } else { + *best = e; + break; + } + } + } +} + /* index_insert takes ownership of the new entry - if it can't insert * it, then it will return an error **and also free the entry**. When * it replaces an existing entry, it will update the entry_ptr with the @@ -1208,7 +1256,7 @@ static int index_insert( { int error = 0; size_t path_length, position; - git_index_entry *existing = NULL, *entry; + git_index_entry *existing, *best, *entry; assert(index && entry_ptr); @@ -1231,20 +1279,19 @@ static int index_insert( git_vector_sort(&index->entries); - /* look if an entry with this path already exists */ - if (!index_find( - &position, index, entry->path, 0, GIT_IDXENTRY_STAGE(entry), false)) { - existing = index->entries.contents[position]; - /* update filemode to existing values if stat is not trusted */ - if (trust_mode) - entry->mode = git_index__create_mode(entry->mode); - else - entry->mode = index_merge_mode(index, existing, entry->mode); - } + /* look if an entry with this path already exists, either staged, or (if + * this entry is a regular staged item) as the "ours" side of a conflict. + */ + index_existing_and_best(&existing, &position, &best, index, entry); + + /* update the file mode */ + entry->mode = trust_mode ? + git_index__create_mode(entry->mode) : + index_merge_mode(index, best, entry->mode); /* canonicalize the directory name */ if (!trust_path) - error = canonicalize_directory_path(index, entry); + error = canonicalize_directory_path(index, entry, best); /* look for tree / blob name collisions, removing conflicts if requested */ if (!error) diff --git a/tests/index/bypath.c b/tests/index/bypath.c index d26273f76..0c10cfe4c 100644 --- a/tests/index/bypath.c +++ b/tests/index/bypath.c @@ -242,7 +242,8 @@ void test_index_bypath__add_honors_existing_case_4(void) void test_index_bypath__add_honors_mode(void) { - git_index_entry *entry, new_entry; + const git_index_entry *entry; + git_index_entry new_entry; cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); @@ -263,3 +264,67 @@ void test_index_bypath__add_honors_mode(void) cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode); } + +void test_index_bypath__add_honors_conflict_mode(void) +{ + const git_index_entry *entry; + git_index_entry new_entry; + int stage = 0; + + cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); + + memcpy(&new_entry, entry, sizeof(git_index_entry)); + new_entry.path = "README.txt"; + new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE; + + cl_must_pass(p_chmod("submod2/README.txt", GIT_FILEMODE_BLOB_EXECUTABLE)); + + cl_git_pass(git_index_remove_bypath(g_idx, "README.txt")); + + for (stage = 1; stage <= 3; stage++) { + new_entry.flags = stage << GIT_IDXENTRY_STAGESHIFT; + cl_git_pass(git_index_add(g_idx, &new_entry)); + } + + cl_git_pass(git_index_write(g_idx)); + + cl_git_rewritefile("submod2/README.txt", "Modified but still executable"); + + cl_git_pass(git_index_add_bypath(g_idx, "README.txt")); + cl_git_pass(git_index_write(g_idx)); + + cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); + cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode); +} + +void test_index_bypath__add_honors_conflict_case(void) +{ + const git_index_entry *entry; + git_index_entry new_entry; + int stage = 0; + + cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); + + memcpy(&new_entry, entry, sizeof(git_index_entry)); + new_entry.path = "README.txt"; + new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE; + + cl_must_pass(p_chmod("submod2/README.txt", GIT_FILEMODE_BLOB_EXECUTABLE)); + + cl_git_pass(git_index_remove_bypath(g_idx, "README.txt")); + + for (stage = 1; stage <= 3; stage++) { + new_entry.flags = stage << GIT_IDXENTRY_STAGESHIFT; + cl_git_pass(git_index_add(g_idx, &new_entry)); + } + + cl_git_pass(git_index_write(g_idx)); + + cl_git_rewritefile("submod2/README.txt", "Modified but still executable"); + + cl_git_pass(git_index_add_bypath(g_idx, "README.txt")); + cl_git_pass(git_index_write(g_idx)); + + cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); + cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode); +} From d3b29fb94bf1c1d0caec39b4a2c3d2061c63efec Mon Sep 17 00:00:00 2001 From: Arthur Schreiber Date: Thu, 1 Oct 2015 00:50:37 +0200 Subject: [PATCH 183/450] refdb and odb backends must provide `free` function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As refdb and odb backends can be allocated by client code, libgit2 can’t know whether an alternative memory allocator was used, and thus should not try to call `git__free` on those objects. Instead, odb and refdb backend implementations must always provide their own `free` functions to ensure memory gets freed correctly. --- include/git2/sys/odb_backend.h | 4 ++++ include/git2/sys/refdb_backend.h | 4 ++-- src/odb.c | 3 +-- src/refdb.c | 8 ++------ tests/odb/sorting.c | 1 + 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/git2/sys/odb_backend.h b/include/git2/sys/odb_backend.h index fe102ff3c..e423a9236 100644 --- a/include/git2/sys/odb_backend.h +++ b/include/git2/sys/odb_backend.h @@ -83,6 +83,10 @@ struct git_odb_backend { git_odb_writepack **, git_odb_backend *, git_odb *odb, git_transfer_progress_cb progress_cb, void *progress_payload); + /** + * Frees any resources held by the odb (including the `git_odb_backend` + * itself). An odb backend implementation must provide this function. + */ void (* free)(git_odb_backend *); }; diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index 01fce8009..5129ad84a 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -130,8 +130,8 @@ struct git_refdb_backend { int (*ensure_log)(git_refdb_backend *backend, const char *refname); /** - * Frees any resources held by the refdb. A refdb implementation may - * provide this function; if it is not provided, nothing will be done. + * Frees any resources held by the refdb (including the `git_refdb_backend` + * itself). A refdb backend implementation must provide this function. */ void (*free)(git_refdb_backend *backend); diff --git a/src/odb.c b/src/odb.c index b2d635109..2b2c35fe8 100644 --- a/src/odb.c +++ b/src/odb.c @@ -600,8 +600,7 @@ static void odb_free(git_odb *db) backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *backend = internal->backend; - if (backend->free) backend->free(backend); - else git__free(backend); + backend->free(backend); git__free(internal); } diff --git a/src/refdb.c b/src/refdb.c index 16fb519a6..debba1276 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -61,12 +61,8 @@ int git_refdb_open(git_refdb **out, git_repository *repo) static void refdb_free_backend(git_refdb *db) { - if (db->backend) { - if (db->backend->free) - db->backend->free(db->backend); - else - git__free(db->backend); - } + if (db->backend) + db->backend->free(db->backend); } int git_refdb_set_backend(git_refdb *db, git_refdb_backend *backend) diff --git a/tests/odb/sorting.c b/tests/odb/sorting.c index 147a160c8..d24c49c69 100644 --- a/tests/odb/sorting.c +++ b/tests/odb/sorting.c @@ -14,6 +14,7 @@ static git_odb_backend *new_backend(size_t position) if (b == NULL) return NULL; + b->base.free = (void (*)(git_odb_backend *)) git__free; b->base.version = GIT_ODB_BACKEND_VERSION; b->position = position; return (git_odb_backend *)b; From 973a09a4ca6502e45826ec310aabf931c48f196e Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Thu, 1 Oct 2015 18:35:21 -0700 Subject: [PATCH 184/450] index: don't populate nsec values if GIT_USE_NSEC is off --- src/fileops.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/fileops.c b/src/fileops.c index 5b54298fa..a13a40655 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -1081,6 +1081,9 @@ void git_futils_filestamp_set_from_stat( if (st) { stamp->mtime = *statmtime; +#if !defined(GIT_USE_NSEC) + stamp->mtime.tv_nsec = 0; +#endif stamp->size = (git_off_t)st->st_size; stamp->ino = (unsigned int)st->st_ino; } else { From 28659e50d56bb79460c8a1d2315443267d6e6f66 Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Thu, 1 Oct 2015 18:36:10 -0700 Subject: [PATCH 185/450] diff: refactor complex timestamp check into its own function --- src/diff.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/diff.c b/src/diff.c index f8e0c53d3..09f29fcee 100644 --- a/src/diff.c +++ b/src/diff.c @@ -79,7 +79,7 @@ static bool diff_pathspec_match( git_diff *diff, const git_index_entry *entry) { - bool disable_pathspec_match = + bool disable_pathspec_match = DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH); /* If we're disabling fnmatch, then the iterator has already applied @@ -703,6 +703,31 @@ static bool diff_time_eq( (!use_nanos || a->nanoseconds == b->nanoseconds); } +/* + * Test if the given index time is newer than the given existing index entry. + * If the timestamps are exactly equivalent, then the given index time is + * considered "racily newer" than the existing index entry. + */ +static bool diff_newer_than_index( + const git_index_time *a, const git_index *b, bool use_nanos) +{ + bool is_newer = false; + + if(!b) + return false; + + is_newer = is_newer || (a->seconds > (int32_t) b->stamp.mtime.tv_sec); + is_newer = is_newer || (!use_nanos && + (a->seconds == (int32_t) b->stamp.mtime.tv_sec)); + if(use_nanos) + { + is_newer = is_newer || ((a->seconds == (int32_t) b->stamp.mtime.tv_sec) && + (a->nanoseconds >= (uint32_t) b->stamp.mtime.tv_nsec)); + } + + return is_newer; +} + typedef struct { git_repository *repo; git_iterator *old_iter; @@ -864,10 +889,7 @@ static int maybe_modified( oitem->ino != nitem->ino || oitem->uid != nitem->uid || oitem->gid != nitem->gid || - (index && - ((nitem->mtime.seconds > (int32_t) index->stamp.mtime.tv_sec) || - ((nitem->mtime.seconds == (int32_t) index->stamp.mtime.tv_sec) && - (nitem->mtime.nanoseconds >= (uint32_t) index->stamp.mtime.tv_nsec))))) + diff_newer_than_index(&nitem->mtime, index, use_nanos)) { status = GIT_DELTA_MODIFIED; modified_uncertain = true; From 4bc9b74c14cd2d37de12295cc6ebfd90274e222b Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 28 Sep 2015 16:24:50 -0400 Subject: [PATCH 186/450] GITERR_CHECK_ALLOC_ADDn: multi-arg adders --- src/common.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/common.h b/src/common.h index 6dca36fbd..7170df91a 100644 --- a/src/common.h +++ b/src/common.h @@ -208,6 +208,15 @@ GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int v #define GITERR_CHECK_ALLOC_ADD(out, one, two) \ if (GIT_ADD_SIZET_OVERFLOW(out, one, two)) { return -1; } +#define GITERR_CHECK_ALLOC_ADD3(out, one, two, three) \ + if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \ + GIT_ADD_SIZET_OVERFLOW(out, *(out), three)) { return -1; } + +#define GITERR_CHECK_ALLOC_ADD4(out, one, two, three, four) \ + if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \ + GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \ + GIT_ADD_SIZET_OVERFLOW(out, *(out), four)) { return -1; } + /** Check for multiplicative overflow, failing if it would occur. */ #define GITERR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \ if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { return -1; } From 46c0e6e3c169f8d76ed739444bc51c4b3f74969b Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 28 Sep 2015 16:34:29 -0400 Subject: [PATCH 187/450] xdiff: convert size variables to size_t --- src/xdiff/xdiffi.c | 12 ++-- src/xdiff/xhistogram.c | 6 +- src/xdiff/xmerge.c | 142 ++++++++++++++++++++++++++++------------- 3 files changed, 110 insertions(+), 50 deletions(-) diff --git a/src/xdiff/xdiffi.c b/src/xdiff/xdiffi.c index 0620e5fff..f4d01b48c 100644 --- a/src/xdiff/xdiffi.c +++ b/src/xdiff/xdiffi.c @@ -21,7 +21,8 @@ */ #include "xinclude.h" - +#include "common.h" +#include "integer.h" #define XDL_MAX_COST_MIN 256 @@ -323,7 +324,7 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1, int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdfenv_t *xe) { - long ndiags; + size_t ndiags, allocsize; long *kvd, *kvdf, *kvdb; xdalgoenv_t xenv; diffdata_t dd1, dd2; @@ -343,9 +344,12 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, * Allocate and setup K vectors to be used by the differential algorithm. * One is to store the forward path and one to store the backward path. */ - ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3; - if (!(kvd = (long *) xdl_malloc((2 * ndiags + 2) * sizeof(long)))) { + GITERR_CHECK_ALLOC_ADD3(&ndiags, xe->xdf1.nreff, xe->xdf2.nreff, 3); + GITERR_CHECK_ALLOC_MULTIPLY(&allocsize, ndiags, 2); + GITERR_CHECK_ALLOC_ADD(&allocsize, allocsize, 2); + GITERR_CHECK_ALLOC_MULTIPLY(&allocsize, allocsize, sizeof(long)); + if (!(kvd = (long *) xdl_malloc(allocsize))) { xdl_free_env(xe); return -1; } diff --git a/src/xdiff/xhistogram.c b/src/xdiff/xhistogram.c index 500d8112a..0c2edb89c 100644 --- a/src/xdiff/xhistogram.c +++ b/src/xdiff/xhistogram.c @@ -44,6 +44,7 @@ #include "xinclude.h" #include "xtypes.h" #include "xdiff.h" +#include "common.h" #define MAX_PTR UINT_MAX #define MAX_CNT UINT_MAX @@ -271,7 +272,7 @@ static int histogram_diff( { struct histindex index; struct region lcs; - unsigned int sz; + size_t sz; int result = -1; if (count1 <= 0 && count2 <= 0) @@ -302,7 +303,8 @@ static int histogram_diff( index.table_bits = xdl_hashbits(count1); sz = index.records_size = 1 << index.table_bits; - sz *= sizeof(struct record *); + GITERR_CHECK_ALLOC_MULTIPLY(&sz, sz, sizeof(struct record *)); + if (!(index.records = (struct record **) xdl_malloc(sz))) goto cleanup; memset(index.records, 0, sz); diff --git a/src/xdiff/xmerge.c b/src/xdiff/xmerge.c index b11e59817..7b7e0e2d3 100644 --- a/src/xdiff/xmerge.c +++ b/src/xdiff/xmerge.c @@ -21,6 +21,7 @@ */ #include "xinclude.h" +#include "common.h" typedef struct s_xdmerge { struct s_xdmerge *next; @@ -109,59 +110,74 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2, return 0; } -static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest) +static int xdl_recs_copy_0(size_t *out, int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest) { xrecord_t **recs; - int size = 0; + size_t size = 0; + + *out = 0; recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i; if (count < 1) return 0; - for (i = 0; i < count; size += recs[i++]->size) + for (i = 0; i < count; ) { if (dest) memcpy(dest + size, recs[i]->ptr, recs[i]->size); + + GITERR_CHECK_ALLOC_ADD(&size, size, recs[i++]->size); + } + if (add_nl) { i = recs[count - 1]->size; if (i == 0 || recs[count - 1]->ptr[i - 1] != '\n') { if (dest) dest[size] = '\n'; - size++; + + GITERR_CHECK_ALLOC_ADD(&size, size, 1); } } - return size; + + *out = size; + return 0; } -static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest) +static int xdl_recs_copy(size_t *out, xdfenv_t *xe, int i, int count, int add_nl, char *dest) { - return xdl_recs_copy_0(0, xe, i, count, add_nl, dest); + return xdl_recs_copy_0(out, 0, xe, i, count, add_nl, dest); } -static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest) +static int xdl_orig_copy(size_t *out, xdfenv_t *xe, int i, int count, int add_nl, char *dest) { - return xdl_recs_copy_0(1, xe, i, count, add_nl, dest); + return xdl_recs_copy_0(out, 1, xe, i, count, add_nl, dest); } -static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, +static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1, xdfenv_t *xe2, const char *name2, const char *name3, - int size, int i, int style, + size_t size, int i, int style, xdmerge_t *m, char *dest, int marker_size) { int marker1_size = (name1 ? (int)strlen(name1) + 1 : 0); int marker2_size = (name2 ? (int)strlen(name2) + 1 : 0); int marker3_size = (name3 ? (int)strlen(name3) + 1 : 0); + size_t copied; + + *out = 0; if (marker_size <= 0) marker_size = DEFAULT_CONFLICT_MARKER_SIZE; /* Before conflicting part */ - size += xdl_recs_copy(xe1, i, m->i1 - i, 0, - dest ? dest + size : NULL); + if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0, + dest ? dest + size : NULL) < 0) + return -1; + + GITERR_CHECK_ALLOC_ADD(&size, size, copied); if (!dest) { - size += marker_size + 1 + marker1_size; + GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker1_size); } else { memset(dest + size, '<', marker_size); size += marker_size; @@ -174,13 +190,16 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, } /* Postimage from side #1 */ - size += xdl_recs_copy(xe1, m->i1, m->chg1, 1, - dest ? dest + size : NULL); + if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, 1, + dest ? dest + size : NULL) < 0) + return -1; + + GITERR_CHECK_ALLOC_ADD(&size, size, copied); if (style == XDL_MERGE_DIFF3) { /* Shared preimage */ if (!dest) { - size += marker_size + 1 + marker3_size; + GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker3_size); } else { memset(dest + size, '|', marker_size); size += marker_size; @@ -191,12 +210,15 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, } dest[size++] = '\n'; } - size += xdl_orig_copy(xe1, m->i0, m->chg0, 1, - dest ? dest + size : NULL); + + if (xdl_orig_copy(&copied, xe1, m->i0, m->chg0, 1, + dest ? dest + size : NULL) < 0) + return -1; + GITERR_CHECK_ALLOC_ADD(&size, size, copied); } if (!dest) { - size += marker_size + 1; + GITERR_CHECK_ALLOC_ADD3(&size, size, marker_size, 1); } else { memset(dest + size, '=', marker_size); size += marker_size; @@ -204,10 +226,14 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, } /* Postimage from side #2 */ - size += xdl_recs_copy(xe2, m->i2, m->chg2, 1, - dest ? dest + size : NULL); + + if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, 1, + dest ? dest + size : NULL) < 0) + return -1; + GITERR_CHECK_ALLOC_ADD(&size, size, copied); + if (!dest) { - size += marker_size + 1 + marker2_size; + GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker2_size); } else { memset(dest + size, '>', marker_size); size += marker_size; @@ -218,46 +244,69 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, } dest[size++] = '\n'; } - return size; + + *out = size; + return 0; } -static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1, +static int xdl_fill_merge_buffer(size_t *out, + xdfenv_t *xe1, const char *name1, xdfenv_t *xe2, const char *name2, const char *ancestor_name, int favor, xdmerge_t *m, char *dest, int style, int marker_size) { - int size, i; + size_t size, copied; + int i; + + *out = 0; for (size = i = 0; m; m = m->next) { if (favor && !m->mode) m->mode = favor; - if (m->mode == 0) - size = fill_conflict_hunk(xe1, name1, xe2, name2, + if (m->mode == 0) { + if (fill_conflict_hunk(&size, xe1, name1, xe2, name2, ancestor_name, size, i, style, m, dest, - marker_size); + marker_size) < 0) + return -1; + } else if (m->mode & 3) { /* Before conflicting part */ - size += xdl_recs_copy(xe1, i, m->i1 - i, 0, - dest ? dest + size : NULL); + if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0, + dest ? dest + size : NULL) < 0) + return -1; + GITERR_CHECK_ALLOC_ADD(&size, size, copied); + /* Postimage from side #1 */ - if (m->mode & 1) - size += xdl_recs_copy(xe1, m->i1, m->chg1, (m->mode & 2), - dest ? dest + size : NULL); + if (m->mode & 1) { + if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, (m->mode & 2), + dest ? dest + size : NULL) < 0) + return -1; + GITERR_CHECK_ALLOC_ADD(&size, size, copied); + } + /* Postimage from side #2 */ - if (m->mode & 2) - size += xdl_recs_copy(xe2, m->i2, m->chg2, 0, - dest ? dest + size : NULL); + if (m->mode & 2) { + if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, 0, + dest ? dest + size : NULL) < 0) + return -1; + GITERR_CHECK_ALLOC_ADD(&size, size, copied); + } } else continue; i = m->i1 + m->chg1; } - size += xdl_recs_copy(xe1, i, xe1->xdf2.nrec - i, 0, - dest ? dest + size : NULL); - return size; + + if (xdl_recs_copy(&copied, xe1, i, xe1->xdf2.nrec - i, 0, + dest ? dest + size : NULL) < 0) + return -1; + GITERR_CHECK_ALLOC_ADD(&size, size, copied); + + *out = size; + return 0; } /* @@ -551,19 +600,24 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, /* output */ if (result) { int marker_size = xmp->marker_size; - int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2, + size_t size; + + if (xdl_fill_merge_buffer(&size, xe1, name1, xe2, name2, ancestor_name, favor, changes, NULL, style, - marker_size); + marker_size) < 0) + return -1; + result->ptr = xdl_malloc(size); if (!result->ptr) { xdl_cleanup_merge(changes); return -1; } result->size = size; - xdl_fill_merge_buffer(xe1, name1, xe2, name2, + if (xdl_fill_merge_buffer(&size, xe1, name1, xe2, name2, ancestor_name, favor, changes, - result->ptr, style, marker_size); + result->ptr, style, marker_size) < 0) + return -1; } return xdl_cleanup_merge(changes); } From e43520660c6dba6470af28ce3be822be401f5788 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 28 Sep 2015 18:25:24 -0400 Subject: [PATCH 188/450] merge_file: treat large files as binary xdiff craps the bed on large files. Treat very large files as binary, so that it doesn't even have to try. Refactor our merge binary handling to better match git.git, which looks for a NUL in the first 8000 bytes. --- src/merge.c | 55 -------------------------- src/merge_file.c | 96 +++++++++++++++++++++++++++++++++++---------- src/merge_file.h | 5 +-- src/xdiff/xdiff.h | 8 ++-- tests/merge/files.c | 88 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 170 insertions(+), 82 deletions(-) diff --git a/src/merge.c b/src/merge.c index fc5088c82..89b8e8505 100644 --- a/src/merge.c +++ b/src/merge.c @@ -61,12 +61,6 @@ struct merge_diff_df_data { git_merge_diff *prev_conflict; }; -GIT_INLINE(int) merge_diff_detect_binary( - bool *binary_out, - git_repository *repo, - const git_merge_diff *conflict); - - /* Merge base computation */ int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, size_t length, const git_oid input_array[]) @@ -668,7 +662,6 @@ static int merge_conflict_resolve_automerge( git_odb *odb = NULL; git_oid automerge_oid; int error = 0; - bool binary = false; assert(resolved && diff_list && conflict); @@ -703,12 +696,6 @@ static int merge_conflict_resolve_automerge( strcmp(conflict->ancestor_entry.path, conflict->their_entry.path) != 0) return 0; - /* Reject binary conflicts */ - if ((error = merge_diff_detect_binary(&binary, diff_list->repo, conflict)) < 0) - return error; - if (binary) - return 0; - ancestor = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ? &conflict->ancestor_entry : NULL; ours = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? @@ -1314,48 +1301,6 @@ GIT_INLINE(int) merge_diff_detect_type( return 0; } -GIT_INLINE(int) merge_diff_detect_binary( - bool *binary_out, - git_repository *repo, - const git_merge_diff *conflict) -{ - git_blob *ancestor_blob = NULL, *our_blob = NULL, *their_blob = NULL; - int error = 0; - bool binary = false; - - if (GIT_MERGE_INDEX_ENTRY_ISFILE(conflict->ancestor_entry)) { - if ((error = git_blob_lookup(&ancestor_blob, repo, &conflict->ancestor_entry.id)) < 0) - goto done; - - binary = git_blob_is_binary(ancestor_blob); - } - - if (!binary && - GIT_MERGE_INDEX_ENTRY_ISFILE(conflict->our_entry)) { - if ((error = git_blob_lookup(&our_blob, repo, &conflict->our_entry.id)) < 0) - goto done; - - binary = git_blob_is_binary(our_blob); - } - - if (!binary && - GIT_MERGE_INDEX_ENTRY_ISFILE(conflict->their_entry)) { - if ((error = git_blob_lookup(&their_blob, repo, &conflict->their_entry.id)) < 0) - goto done; - - binary = git_blob_is_binary(their_blob); - } - - *binary_out = binary; - -done: - git_blob_free(ancestor_blob); - git_blob_free(our_blob); - git_blob_free(their_blob); - - return error; -} - GIT_INLINE(int) index_entry_dup_pool( git_index_entry *out, git_pool *pool, diff --git a/src/merge_file.c b/src/merge_file.c index 6d89b089d..b174231cc 100644 --- a/src/merge_file.c +++ b/src/merge_file.c @@ -15,9 +15,15 @@ #include "git2/repository.h" #include "git2/object.h" #include "git2/index.h" +#include "git2/merge.h" #include "xdiff/xdiff.h" +/* only examine the first 8000 bytes for binaryness. + * https://github.com/git/git/blob/77bd3ea9f54f1584147b594abc04c26ca516d987/xdiff-interface.c#L197 + */ +#define GIT_MERGE_FILE_BINARY_SIZE 8000 + #define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0) GIT_INLINE(const char *) merge_file_best_path( @@ -100,7 +106,7 @@ static void merge_file_normalize_opts( } } -static int git_merge_file__from_inputs( +static int merge_file__xdiff( git_merge_file_result *out, const git_merge_file_input *ancestor, const git_merge_file_input *ours, @@ -189,6 +195,63 @@ done: return error; } +static bool merge_file__is_binary(const git_merge_file_input *file) +{ + size_t len = file ? file->size : 0; + + if (len > GIT_MERGE_FILE_XDIFF_MAX) + return true; + if (len > GIT_MERGE_FILE_BINARY_SIZE) + len = GIT_MERGE_FILE_BINARY_SIZE; + + return len ? (memchr(file->ptr, 0, len) != NULL) : false; +} + +static int merge_file__binary( + git_merge_file_result *out, + const git_merge_file_input *ours, + const git_merge_file_input *theirs, + const git_merge_file_options *given_opts) +{ + const git_merge_file_input *favored = NULL; + + memset(out, 0x0, sizeof(git_merge_file_result)); + + if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_OURS) + favored = ours; + else if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_THEIRS) + favored = theirs; + else + goto done; + + if ((out->path = git__strdup(favored->path)) == NULL || + (out->ptr = git__malloc(favored->size)) == NULL) + goto done; + + memcpy((char *)out->ptr, favored->ptr, favored->size); + out->len = favored->size; + out->mode = favored->mode; + out->automergeable = 1; + +done: + return 0; +} + +static int merge_file__from_inputs( + git_merge_file_result *out, + const git_merge_file_input *ancestor, + const git_merge_file_input *ours, + const git_merge_file_input *theirs, + const git_merge_file_options *given_opts) +{ + if (merge_file__is_binary(ancestor) || + merge_file__is_binary(ours) || + merge_file__is_binary(theirs)) + return merge_file__binary(out, ours, theirs, given_opts); + + return merge_file__xdiff(out, ancestor, ours, theirs, given_opts); +} + static git_merge_file_input *git_merge_file__normalize_inputs( git_merge_file_input *out, const git_merge_file_input *given) @@ -223,7 +286,7 @@ int git_merge_file( ours = git_merge_file__normalize_inputs(&inputs[1], ours); theirs = git_merge_file__normalize_inputs(&inputs[2], theirs); - return git_merge_file__from_inputs(out, ancestor, ours, theirs, options); + return merge_file__from_inputs(out, ancestor, ours, theirs, options); } int git_merge_file_from_index( @@ -234,8 +297,8 @@ int git_merge_file_from_index( const git_index_entry *theirs, const git_merge_file_options *options) { - git_merge_file_input inputs[3] = { {0} }, - *ancestor_input = NULL, *our_input = NULL, *their_input = NULL; + git_merge_file_input *ancestor_ptr = NULL, + ancestor_input = {0}, our_input = {0}, their_input = {0}; git_odb *odb = NULL; git_odb_object *odb_object[3] = { 0 }; int error = 0; @@ -249,27 +312,20 @@ int git_merge_file_from_index( if (ancestor) { if ((error = git_merge_file__input_from_index( - &inputs[0], &odb_object[0], odb, ancestor)) < 0) + &ancestor_input, &odb_object[0], odb, ancestor)) < 0) goto done; - ancestor_input = &inputs[0]; + ancestor_ptr = &ancestor_input; } if ((error = git_merge_file__input_from_index( - &inputs[1], &odb_object[1], odb, ours)) < 0) + &our_input, &odb_object[1], odb, ours)) < 0 || + (error = git_merge_file__input_from_index( + &their_input, &odb_object[2], odb, theirs)) < 0) goto done; - our_input = &inputs[1]; - - if ((error = git_merge_file__input_from_index( - &inputs[2], &odb_object[2], odb, theirs)) < 0) - goto done; - - their_input = &inputs[2]; - - if ((error = git_merge_file__from_inputs(out, - ancestor_input, our_input, their_input, options)) < 0) - goto done; + error = merge_file__from_inputs(out, + ancestor_ptr, &our_input, &their_input, options); done: git_odb_object_free(odb_object[0]); @@ -286,7 +342,5 @@ void git_merge_file_result_free(git_merge_file_result *result) return; git__free((char *)result->path); - - /* xdiff uses malloc() not git_malloc, so we use free(), not git_free() */ - free((char *)result->ptr); + git__free((char *)result->ptr); } diff --git a/src/merge_file.h b/src/merge_file.h index 263391ee3..08ecfc095 100644 --- a/src/merge_file.h +++ b/src/merge_file.h @@ -7,8 +7,7 @@ #ifndef INCLUDE_filediff_h__ #define INCLUDE_filediff_h__ -#include "xdiff/xdiff.h" - -#include "git2/merge.h" +/* xdiff cannot cope with large files, just treat them as binary */ +#define GIT_MERGE_FILE_XDIFF_MAX (1024UL * 1024 * 1023) #endif diff --git a/src/xdiff/xdiff.h b/src/xdiff/xdiff.h index e2f1e892b..db5d59884 100644 --- a/src/xdiff/xdiff.h +++ b/src/xdiff/xdiff.h @@ -20,6 +20,8 @@ * */ +#include "util.h" + #if !defined(XDIFF_H) #define XDIFF_H @@ -106,9 +108,9 @@ typedef struct s_bdiffparam { } bdiffparam_t; -#define xdl_malloc(x) malloc(x) -#define xdl_free(ptr) free(ptr) -#define xdl_realloc(ptr,x) realloc(ptr,x) +#define xdl_malloc(x) git__malloc(x) +#define xdl_free(ptr) git__free(ptr) +#define xdl_realloc(ptr,x) git__realloc(ptr,x) void *xdl_mmfile_first(mmfile_t *mmf, long *size); long xdl_mmfile_size(mmfile_t *mmf); diff --git a/tests/merge/files.c b/tests/merge/files.c index 2fd90d066..b365d5a42 100644 --- a/tests/merge/files.c +++ b/tests/merge/files.c @@ -3,6 +3,7 @@ #include "git2/merge.h" #include "buffer.h" #include "merge.h" +#include "merge_file.h" #include "merge_helpers.h" #include "refs.h" #include "fileops.h" @@ -288,3 +289,90 @@ void test_merge_files__doesnt_add_newline(void) git_merge_file_result_free(&result); } +void test_merge_files__skips_large_files(void) +{ + git_merge_file_input ours = GIT_MERGE_FILE_INPUT_INIT, + theirs = GIT_MERGE_FILE_INPUT_INIT; + git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT; + git_merge_file_result result = {0}; + + ours.size = GIT_MERGE_FILE_XDIFF_MAX + 1; + ours.path = "testfile.txt"; + ours.mode = 0100755; + + theirs.size = GIT_MERGE_FILE_XDIFF_MAX + 1; + theirs.path = "testfile.txt"; + theirs.mode = 0100755; + + cl_git_pass(git_merge_file(&result, NULL, &ours, &theirs, &opts)); + + cl_assert_equal_i(0, result.automergeable); + + git_merge_file_result_free(&result); +} + +void test_merge_files__skips_binaries(void) +{ + git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT, + ours = GIT_MERGE_FILE_INPUT_INIT, + theirs = GIT_MERGE_FILE_INPUT_INIT; + git_merge_file_result result = {0}; + + ancestor.ptr = "ance\0stor\0"; + ancestor.size = 10; + ancestor.path = "ancestor.txt"; + ancestor.mode = 0100755; + + ours.ptr = "foo\0bar\0"; + ours.size = 8; + ours.path = "ours.txt"; + ours.mode = 0100755; + + theirs.ptr = "bar\0foo\0"; + theirs.size = 8; + theirs.path = "theirs.txt"; + theirs.mode = 0100644; + + cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, NULL)); + + cl_assert_equal_i(0, result.automergeable); + + git_merge_file_result_free(&result); +} + +void test_merge_files__handles_binaries_when_favored(void) +{ + git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT, + ours = GIT_MERGE_FILE_INPUT_INIT, + theirs = GIT_MERGE_FILE_INPUT_INIT; + git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT; + git_merge_file_result result = {0}; + + ancestor.ptr = "ance\0stor\0"; + ancestor.size = 10; + ancestor.path = "ancestor.txt"; + ancestor.mode = 0100755; + + ours.ptr = "foo\0bar\0"; + ours.size = 8; + ours.path = "ours.txt"; + ours.mode = 0100755; + + theirs.ptr = "bar\0foo\0"; + theirs.size = 8; + theirs.path = "theirs.txt"; + theirs.mode = 0100644; + + opts.favor = GIT_MERGE_FILE_FAVOR_OURS; + cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts)); + + cl_assert_equal_i(1, result.automergeable); + + cl_assert_equal_s("ours.txt", result.path); + cl_assert_equal_i(0100755, result.mode); + + cl_assert_equal_i(ours.size, result.len); + cl_assert(memcmp(result.ptr, ours.ptr, ours.size) == 0); + + git_merge_file_result_free(&result); +} From 6c014bcc54fb9923490f9af917dc43e3661e5782 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 29 Sep 2015 12:18:17 -0400 Subject: [PATCH 189/450] diff: don't feed large files to xdiff --- src/checkout.c | 3 ++- src/diff_patch.c | 4 ++++ src/diff_xdiff.c | 7 +++++++ src/diff_xdiff.h | 5 +++++ src/merge.c | 1 - src/merge_file.c | 4 ++-- src/merge_file.h | 13 ------------- tests/merge/files.c | 6 +++--- 8 files changed, 23 insertions(+), 20 deletions(-) delete mode 100644 src/merge_file.h diff --git a/src/checkout.c b/src/checkout.c index 2a8bfd558..632556622 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -18,6 +18,7 @@ #include "git2/submodule.h" #include "git2/sys/index.h" #include "git2/sys/filter.h" +#include "git2/merge.h" #include "refs.h" #include "repository.h" @@ -27,7 +28,7 @@ #include "diff.h" #include "pathspec.h" #include "buf_text.h" -#include "merge_file.h" +#include "diff_xdiff.h" #include "path.h" #include "attr.h" #include "pool.h" diff --git a/src/diff_patch.c b/src/diff_patch.c index 0628da6f2..50faa3b3f 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -30,6 +30,10 @@ static void diff_patch_update_binary(git_patch *patch) (patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0) patch->delta->flags |= GIT_DIFF_FLAG_BINARY; + else if (patch->ofile.file->size > GIT_XDIFF_MAX_SIZE || + patch->nfile.file->size > GIT_XDIFF_MAX_SIZE) + patch->delta->flags |= GIT_DIFF_FLAG_BINARY; + else if ((patch->ofile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0 && (patch->nfile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0) patch->delta->flags |= GIT_DIFF_FLAG_NOT_BINARY; diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c index e5984f1c9..1057df3aa 100644 --- a/src/diff_xdiff.c +++ b/src/diff_xdiff.c @@ -4,6 +4,7 @@ * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ +#include "git2/errors.h" #include "common.h" #include "diff.h" #include "diff_driver.h" @@ -208,6 +209,12 @@ static int git_xdiff(git_diff_output *output, git_patch *patch) git_patch__old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch); git_patch__new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch); + if (info.xd_old_data.size > GIT_XDIFF_MAX_SIZE || + info.xd_new_data.size > GIT_XDIFF_MAX_SIZE) { + giterr_set(GITERR_INVALID, "files too large for diff"); + return -1; + } + xdl_diff(&info.xd_old_data, &info.xd_new_data, &xo->params, &xo->config, &xo->callback); diff --git a/src/diff_xdiff.h b/src/diff_xdiff.h index c547b00cf..98e11b2cb 100644 --- a/src/diff_xdiff.h +++ b/src/diff_xdiff.h @@ -11,6 +11,11 @@ #include "diff_patch.h" #include "xdiff/xdiff.h" +/* xdiff cannot cope with large files. these files should not be passed to + * xdiff. callers should treat these large files as binary. + */ +#define GIT_XDIFF_MAX_SIZE (1024LL * 1024 * 1023) + /* A git_xdiff_output is a git_diff_output with extra fields necessary * to use libxdiff. Calling git_xdiff_init() will set the diff_cb field * of the output to use xdiff to generate the diffs. diff --git a/src/merge.c b/src/merge.c index 89b8e8505..930457bdb 100644 --- a/src/merge.c +++ b/src/merge.c @@ -20,7 +20,6 @@ #include "diff.h" #include "checkout.h" #include "tree.h" -#include "merge_file.h" #include "blob.h" #include "oid.h" #include "index.h" diff --git a/src/merge_file.c b/src/merge_file.c index b174231cc..6d4738065 100644 --- a/src/merge_file.c +++ b/src/merge_file.c @@ -7,10 +7,10 @@ #include "common.h" #include "repository.h" -#include "merge_file.h" #include "posix.h" #include "fileops.h" #include "index.h" +#include "diff_xdiff.h" #include "git2/repository.h" #include "git2/object.h" @@ -199,7 +199,7 @@ static bool merge_file__is_binary(const git_merge_file_input *file) { size_t len = file ? file->size : 0; - if (len > GIT_MERGE_FILE_XDIFF_MAX) + if (len > GIT_XDIFF_MAX_SIZE) return true; if (len > GIT_MERGE_FILE_BINARY_SIZE) len = GIT_MERGE_FILE_BINARY_SIZE; diff --git a/src/merge_file.h b/src/merge_file.h deleted file mode 100644 index 08ecfc095..000000000 --- a/src/merge_file.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ -#ifndef INCLUDE_filediff_h__ -#define INCLUDE_filediff_h__ - -/* xdiff cannot cope with large files, just treat them as binary */ -#define GIT_MERGE_FILE_XDIFF_MAX (1024UL * 1024 * 1023) - -#endif diff --git a/tests/merge/files.c b/tests/merge/files.c index b365d5a42..2d55df2b2 100644 --- a/tests/merge/files.c +++ b/tests/merge/files.c @@ -3,10 +3,10 @@ #include "git2/merge.h" #include "buffer.h" #include "merge.h" -#include "merge_file.h" #include "merge_helpers.h" #include "refs.h" #include "fileops.h" +#include "diff_xdiff.h" #define TEST_REPO_PATH "merge-resolve" #define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index" @@ -296,11 +296,11 @@ void test_merge_files__skips_large_files(void) git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT; git_merge_file_result result = {0}; - ours.size = GIT_MERGE_FILE_XDIFF_MAX + 1; + ours.size = GIT_XDIFF_MAX_SIZE + 1; ours.path = "testfile.txt"; ours.mode = 0100755; - theirs.size = GIT_MERGE_FILE_XDIFF_MAX + 1; + theirs.size = GIT_XDIFF_MAX_SIZE + 1; theirs.path = "testfile.txt"; theirs.mode = 0100755; From ae195a71ae898b7d5d61943f24f31d97065a87c6 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 29 Sep 2015 12:46:41 -0400 Subject: [PATCH 190/450] blame: guard xdiff calls for large files --- src/blame.c | 2 +- src/blame_git.c | 34 +++++++++++++++++++++++++++------- src/blame_git.h | 2 +- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/blame.c b/src/blame.c index a52f9402b..08a90dcfd 100644 --- a/src/blame.c +++ b/src/blame.c @@ -331,7 +331,7 @@ static int blame_internal(git_blame *blame) blame->ent = ent; - git_blame__like_git(blame, blame->options.flags); + error = git_blame__like_git(blame, blame->options.flags); cleanup: for (ent = blame->ent; ent; ) { diff --git a/src/blame_git.c b/src/blame_git.c index f481426aa..67bae2384 100644 --- a/src/blame_git.c +++ b/src/blame_git.c @@ -9,6 +9,7 @@ #include "commit.h" #include "blob.h" #include "xdiff/xinclude.h" +#include "diff_xdiff.h" /* * Origin is refcounted and usually we keep the blob contents to be @@ -351,6 +352,13 @@ static int diff_hunks(mmfile_t file_a, mmfile_t file_b, void *cb_data) ecb.priv = cb_data; trim_common_tail(&file_a, &file_b, 0); + + if (file_a.size > GIT_XDIFF_MAX_SIZE || + file_b.size > GIT_XDIFF_MAX_SIZE) { + giterr_set(GITERR_INVALID, "file too large to blame"); + return -1; + } + return xdl_diff(&file_a, &file_b, &xpp, &xecfg, &ecb); } @@ -379,7 +387,9 @@ static int pass_blame_to_parent( fill_origin_blob(parent, &file_p); fill_origin_blob(target, &file_o); - diff_hunks(file_p, file_o, &d); + if (diff_hunks(file_p, file_o, &d) < 0) + return -1; + /* The reset (i.e. anything after tlno) are the same as the parent */ blame_chunk(blame, d.tlno, d.plno, last_in_target, target, parent); @@ -477,12 +487,13 @@ static void pass_whole_blame(git_blame *blame, } } -static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt) +static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt) { git_commit *commit = origin->commit; int i, num_parents; git_blame__origin *sg_buf[16]; git_blame__origin *porigin, **sg_origin = sg_buf; + int ret, error = 0; num_parents = git_commit_parentcount(commit); if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit)) @@ -540,8 +551,13 @@ static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt origin_incref(porigin); origin->previous = porigin; } - if (pass_blame_to_parent(blame, origin, porigin)) + + if ((ret = pass_blame_to_parent(blame, origin, porigin)) != 0) { + if (ret < 0) + error = -1; + goto finish; + } } /* TODO: optionally find moves in parents' files */ @@ -554,7 +570,7 @@ finish: origin_decref(sg_origin[i]); if (sg_origin != sg_buf) git__free(sg_origin); - return; + return error; } /* @@ -583,7 +599,7 @@ static void coalesce(git_blame *blame) } } -void git_blame__like_git(git_blame *blame, uint32_t opt) +int git_blame__like_git(git_blame *blame, uint32_t opt) { while (true) { git_blame__entry *ent; @@ -594,11 +610,13 @@ void git_blame__like_git(git_blame *blame, uint32_t opt) if (!ent->guilty) suspect = ent->suspect; if (!suspect) - return; /* all done */ + return 0; /* all done */ /* We'll use this suspect later in the loop, so hold on to it for now. */ origin_incref(suspect); - pass_blame(blame, suspect, opt); + + if (pass_blame(blame, suspect, opt) < 0) + return -1; /* Take responsibility for the remaining entries */ for (ent = blame->ent; ent; ent = ent->next) { @@ -613,6 +631,8 @@ void git_blame__like_git(git_blame *blame, uint32_t opt) } coalesce(blame); + + return 0; } void git_blame__free_entry(git_blame__entry *ent) diff --git a/src/blame_git.h b/src/blame_git.h index 3ec2710b8..1891b0e1f 100644 --- a/src/blame_git.h +++ b/src/blame_git.h @@ -15,6 +15,6 @@ int git_blame__get_origin( git_commit *commit, const char *path); void git_blame__free_entry(git_blame__entry *ent); -void git_blame__like_git(git_blame *sb, uint32_t flags); +int git_blame__like_git(git_blame *sb, uint32_t flags); #endif From e3f94c71c3792716b89b3b56b7dbb984e1620e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 6 Oct 2015 13:35:45 +0200 Subject: [PATCH 191/450] CMake: be more explicit with python errors There's been a few reports of users not understanding what the python error means, so spell out the options they have. --- CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 713640d6a..abcc0e49d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -575,7 +575,12 @@ INSTALL(FILES include/git2.h DESTINATION ${INCLUDE_INSTALL_DIR} ) # Tests IF (BUILD_CLAR) - FIND_PACKAGE(PythonInterp REQUIRED) + FIND_PACKAGE(PythonInterp) + + IF(NOT PYTHONINTERP_FOUND) + MESSAGE(FATAL_ERROR "Could not find a python interpeter, which is needed to build the tests. " + "Make sure python is available, or pass -DBUILD_CLAR=OFF to skip building the tests") + ENDIF() SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources/") SET(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tests") From 8b8f1f91881a024d0d8efbf1f69e49ed65fdbc6f Mon Sep 17 00:00:00 2001 From: Eun Date: Wed, 7 Oct 2015 14:01:05 +0200 Subject: [PATCH 192/450] fix return --- examples/network/fetch.c | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/network/fetch.c b/examples/network/fetch.c index bc7a5a05e..177359b88 100644 --- a/examples/network/fetch.c +++ b/examples/network/fetch.c @@ -63,6 +63,7 @@ static int transfer_progress_cb(const git_transfer_progress *stats, void *payloa stats->received_objects, stats->total_objects, stats->indexed_objects, stats->received_bytes); } + return 0; } /** Entry point for this command */ From 5ffdea6f65bb105c17a1c4730d51732e3391bf63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 14 Oct 2015 16:49:01 +0200 Subject: [PATCH 193/450] revwalk: make commit list use 64 bits for time We moved the "main" parsing to use 64 bits for the timestamp, but the quick parsing for the revwalk did not. This means that for large timestamps we fail to parse the time and thus the walk. Move this parser to use 64 bits as well. --- src/commit_list.c | 6 +++--- src/commit_list.h | 2 +- tests/revwalk/basic.c | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/commit_list.c b/src/commit_list.c index 3054c18dd..53612d514 100644 --- a/src/commit_list.c +++ b/src/commit_list.c @@ -110,7 +110,7 @@ static int commit_quick_parse( const uint8_t *buffer_end = buffer + buffer_len; const uint8_t *parents_start, *committer_start; int i, parents = 0; - int commit_time; + int64_t commit_time; buffer += strlen("tree ") + GIT_OID_HEXSZ + 1; @@ -166,10 +166,10 @@ static int commit_quick_parse( buffer--; } - if ((buffer == committer_start) || (git__strtol32(&commit_time, (char *)(buffer + 1), NULL, 10) < 0)) + if ((buffer == committer_start) || (git__strtol64(&commit_time, (char *)(buffer + 1), NULL, 10) < 0)) return commit_error(commit, "cannot parse commit time"); - commit->time = (time_t)commit_time; + commit->time = commit_time; commit->parsed = 1; return 0; } diff --git a/src/commit_list.h b/src/commit_list.h index 6b3f473d3..b1d88e016 100644 --- a/src/commit_list.h +++ b/src/commit_list.h @@ -22,7 +22,7 @@ typedef struct git_commit_list_node { git_oid oid; - uint32_t time; + int64_t time; unsigned int seen:1, uninteresting:1, topo_delay:1, diff --git a/tests/revwalk/basic.c b/tests/revwalk/basic.c index 7e50452c9..d8236ce72 100644 --- a/tests/revwalk/basic.c +++ b/tests/revwalk/basic.c @@ -437,3 +437,38 @@ void test_revwalk_basic__mimic_git_rev_list(void) cl_git_fail_with(git_revwalk_next(&oid, _walk), GIT_ITEROVER); } + +void test_revwalk_basic__big_timestamp(void) +{ + git_reference *head; + git_commit *tip; + git_signature *sig; + git_tree *tree; + git_oid id; + int error; + + revwalk_basic_setup_walk("testrepo.git"); + + cl_git_pass(git_repository_head(&head, _repo)); + cl_git_pass(git_reference_peel((git_object **) &tip, head, GIT_OBJ_COMMIT)); + + /* Commit with a far-ahead timestamp, we should be able to parse it in the revwalk */ + cl_git_pass(git_signature_new(&sig, "Joe", "joe@example.com", 2399662595, 0)); + cl_git_pass(git_commit_tree(&tree, tip)); + + cl_git_pass(git_commit_create(&id, _repo, "HEAD", sig, sig, NULL, "some message", tree, 1, &tip)); + + cl_git_pass(git_revwalk_push_head(_walk)); + + while ((error = git_revwalk_next(&id, _walk)) == 0) { + /* nothing */ + } + + cl_assert_equal_i(GIT_ITEROVER, error); + + git_tree_free(tree); + git_commit_free(tip); + git_reference_free(head); + git_signature_free(sig); + +} From 43820f204ea32503b4083e3b6b83f30a0a0031c9 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 14 Oct 2015 19:24:07 +0200 Subject: [PATCH 194/450] odb: Be smarter when refreshing backends In the current implementation of ODB backends, each backend is tasked with refreshing itself after a failed lookup. This is standard Git behavior: we want to e.g. reload the packfiles on disk in case they have changed and that's the reason we can't find the object we're looking for. This behavior, however, becomes pathological in repositories where multiple alternates have been loaded. Given that each alternate counts as a separate backend, a miss in the main repository (which can potentially be very frequent in cases where object storage comes from the alternate) will result in refreshing all its packfiles before we move on to the alternate backend where the object will most likely be found. To fix this, the code in `odb.c` has been refactored as to perform the refresh of all the backends externally, once we've verified that the object is nowhere to be found. If the refresh is successful, we then perform the lookup sequentially through all the backends, skipping the ones that we know for sure weren't refreshed (because they have no refresh API). The on-disk pack backend has been adjusted accordingly: it no longer performs refreshes internally. --- src/odb.c | 237 +++++++++++++++++++++++++++++++++---------------- src/odb_pack.c | 82 +---------------- 2 files changed, 162 insertions(+), 157 deletions(-) diff --git a/src/odb.c b/src/odb.c index 2b2c35fe8..af1ff7fa2 100644 --- a/src/odb.c +++ b/src/odb.c @@ -620,23 +620,18 @@ void git_odb_free(git_odb *db) GIT_REFCOUNT_DEC(db, odb_free); } -int git_odb_exists(git_odb *db, const git_oid *id) +static int odb_exists_1(git_odb *db, const git_oid *id, bool only_refreshed) { - git_odb_object *object; size_t i; bool found = false; - assert(db && id); - - if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { - git_odb_object_free(object); - return (int)true; - } - for (i = 0; i < db->backends.length && !found; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; + if (only_refreshed && !b->refresh) + continue; + if (b->exists != NULL) found = (bool)b->exists(b, id); } @@ -644,12 +639,74 @@ int git_odb_exists(git_odb *db, const git_oid *id) return (int)found; } +int git_odb_exists(git_odb *db, const git_oid *id) +{ + git_odb_object *object; + + assert(db && id); + + if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { + git_odb_object_free(object); + return (int)true; + } + + if (odb_exists_1(db, id, false)) + return 1; + + if (!git_odb_refresh(db)) + return odb_exists_1(db, id, true); + + /* Failed to refresh, hence not found */ + return 0; +} + +static int odb_exists_prefix_1(git_oid *out, git_odb *db, + const git_oid *key, size_t len, bool only_refreshed) +{ + size_t i; + int error = GIT_ENOTFOUND, num_found = 0; + git_oid last_found = {{0}}, found; + + for (i = 0; i < db->backends.length; ++i) { + backend_internal *internal = git_vector_get(&db->backends, i); + git_odb_backend *b = internal->backend; + + if (only_refreshed && !b->refresh) + continue; + + if (!b->exists_prefix) + continue; + + error = b->exists_prefix(&found, b, key, len); + if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) + continue; + if (error) + return error; + + /* make sure found item doesn't introduce ambiguity */ + if (num_found) { + if (git_oid__cmp(&last_found, &found)) + return git_odb__error_ambiguous("multiple matches for prefix"); + } else { + git_oid_cpy(&last_found, &found); + num_found++; + } + } + + if (!num_found) + return GIT_ENOTFOUND; + + if (out) + git_oid_cpy(out, &last_found); + + return 0; +} + int git_odb_exists_prefix( git_oid *out, git_odb *db, const git_oid *short_id, size_t len) { - int error = GIT_ENOTFOUND, num_found = 0; - size_t i; - git_oid key = {{0}}, last_found = {{0}}, found; + int error; + git_oid key = {{0}}; assert(db && short_id); @@ -673,35 +730,15 @@ int git_odb_exists_prefix( if (len & 1) key.id[len / 2] &= 0xF0; - for (i = 0; i < db->backends.length; ++i) { - backend_internal *internal = git_vector_get(&db->backends, i); - git_odb_backend *b = internal->backend; + error = odb_exists_prefix_1(out, db, &key, len, false); - if (!b->exists_prefix) - continue; + if (error == GIT_ENOTFOUND && !git_odb_refresh(db)) + error = odb_exists_prefix_1(out, db, &key, len, true); - error = b->exists_prefix(&found, b, &key, len); - if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) - continue; - if (error) - return error; - - /* make sure found item doesn't introduce ambiguity */ - if (num_found) { - if (git_oid__cmp(&last_found, &found)) - return git_odb__error_ambiguous("multiple matches for prefix"); - } else { - git_oid_cpy(&last_found, &found); - num_found++; - } - } - - if (!num_found) + if (error == GIT_ENOTFOUND) return git_odb__error_notfound("no match for id prefix", &key); - if (out) - git_oid_cpy(out, &last_found); - return 0; + return error; } int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id) @@ -783,36 +820,38 @@ static int hardcoded_objects(git_rawobj *raw, const git_oid *id) } } -int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) +static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, + bool only_refreshed) { - size_t i, reads = 0; - int error; + size_t i; git_rawobj raw; git_odb_object *object; + bool found = false; - assert(out && db && id); + if (!hardcoded_objects(&raw, id)) + found = true; - *out = git_cache_get_raw(odb_cache(db), id); - if (*out != NULL) - return 0; - - error = hardcoded_objects(&raw, id); - - for (i = 0; i < db->backends.length && error < 0; ++i) { + for (i = 0; i < db->backends.length && !found; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; + if (only_refreshed && !b->refresh) + continue; + if (b->read != NULL) { - ++reads; - error = b->read(&raw.data, &raw.len, &raw.type, b, id); + int error = b->read(&raw.data, &raw.len, &raw.type, b, id); + if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND) + continue; + + if (error < 0) + return error; + + found = true; } } - if (error && error != GIT_PASSTHROUGH) { - if (!reads) - return git_odb__error_notfound("no match for id", id); - return error; - } + if (!found) + return GIT_ENOTFOUND; giterr_clear(); if ((object = odb_object__alloc(id, &raw)) == NULL) @@ -822,42 +861,48 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) return 0; } -int git_odb_read_prefix( - git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len) +int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) +{ + int error; + + assert(out && db && id); + + *out = git_cache_get_raw(odb_cache(db), id); + if (*out != NULL) + return 0; + + error = odb_read_1(out, db, id, false); + + if (error == GIT_ENOTFOUND && !git_odb_refresh(db)) + error = odb_read_1(out, db, id, true); + + if (error == GIT_ENOTFOUND) + return git_odb__error_notfound("no match for id", id); + + return error; +} + +static int read_prefix_1(git_odb_object **out, git_odb *db, + const git_oid *key, size_t len, bool only_refreshed) { size_t i; int error = GIT_ENOTFOUND; - git_oid key = {{0}}, found_full_oid = {{0}}; + git_oid found_full_oid = {{0}}; git_rawobj raw; void *data = NULL; bool found = false; git_odb_object *object; - assert(out && db); - - if (len < GIT_OID_MINPREFIXLEN) - return git_odb__error_ambiguous("prefix length too short"); - if (len > GIT_OID_HEXSZ) - len = GIT_OID_HEXSZ; - - if (len == GIT_OID_HEXSZ) { - *out = git_cache_get_raw(odb_cache(db), short_id); - if (*out != NULL) - return 0; - } - - /* just copy valid part of short_id */ - memcpy(&key.id, short_id->id, (len + 1) / 2); - if (len & 1) - key.id[len / 2] &= 0xF0; - for (i = 0; i < db->backends.length; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; + if (only_refreshed && !b->refresh) + continue; + if (b->read_prefix != NULL) { git_oid full_oid; - error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, &key, len); + error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, key, len); if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) continue; @@ -878,7 +923,7 @@ int git_odb_read_prefix( } if (!found) - return git_odb__error_notfound("no match for prefix", &key); + return GIT_ENOTFOUND; if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) return -1; @@ -887,6 +932,42 @@ int git_odb_read_prefix( return 0; } +int git_odb_read_prefix( + git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len) +{ + git_oid key = {{0}}; + int error; + + assert(out && db); + + if (len < GIT_OID_MINPREFIXLEN) + return git_odb__error_ambiguous("prefix length too short"); + + if (len > GIT_OID_HEXSZ) + len = GIT_OID_HEXSZ; + + if (len == GIT_OID_HEXSZ) { + *out = git_cache_get_raw(odb_cache(db), short_id); + if (*out != NULL) + return 0; + } + + /* just copy valid part of short_id */ + memcpy(&key.id, short_id->id, (len + 1) / 2); + if (len & 1) + key.id[len / 2] &= 0xF0; + + error = read_prefix_1(out, db, &key, len, false); + + if (error == GIT_ENOTFOUND && !git_odb_refresh(db)) + error = read_prefix_1(out, db, &key, len, true); + + if (error == GIT_ENOTFOUND) + return git_odb__error_notfound("no match for prefix", &key); + + return error; +} + int git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload) { unsigned int i; diff --git a/src/odb_pack.c b/src/odb_pack.c index 735158d96..77d2c75b9 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -346,7 +346,7 @@ static int pack_backend__refresh(git_odb_backend *backend_) return error; } -static int pack_backend__read_header_internal( +static int pack_backend__read_header( size_t *len_p, git_otype *type_p, struct git_odb_backend *backend, const git_oid *oid) { @@ -361,24 +361,7 @@ static int pack_backend__read_header_internal( return git_packfile_resolve_header(len_p, type_p, e.p, e.offset); } -static int pack_backend__read_header( - size_t *len_p, git_otype *type_p, - struct git_odb_backend *backend, const git_oid *oid) -{ - int error; - - error = pack_backend__read_header_internal(len_p, type_p, backend, oid); - - if (error != GIT_ENOTFOUND) - return error; - - if ((error = pack_backend__refresh(backend)) < 0) - return error; - - return pack_backend__read_header_internal(len_p, type_p, backend, oid); -} - -static int pack_backend__read_internal( +static int pack_backend__read( void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) { @@ -397,24 +380,7 @@ static int pack_backend__read_internal( return 0; } -static int pack_backend__read( - void **buffer_p, size_t *len_p, git_otype *type_p, - git_odb_backend *backend, const git_oid *oid) -{ - int error; - - error = pack_backend__read_internal(buffer_p, len_p, type_p, backend, oid); - - if (error != GIT_ENOTFOUND) - return error; - - if ((error = pack_backend__refresh(backend)) < 0) - return error; - - return pack_backend__read_internal(buffer_p, len_p, type_p, backend, oid); -} - -static int pack_backend__read_prefix_internal( +static int pack_backend__read_prefix( git_oid *out_oid, void **buffer_p, size_t *len_p, @@ -451,45 +417,9 @@ static int pack_backend__read_prefix_internal( return error; } -static int pack_backend__read_prefix( - git_oid *out_oid, - void **buffer_p, - size_t *len_p, - git_otype *type_p, - git_odb_backend *backend, - const git_oid *short_oid, - size_t len) -{ - int error; - - error = pack_backend__read_prefix_internal( - out_oid, buffer_p, len_p, type_p, backend, short_oid, len); - - if (error != GIT_ENOTFOUND) - return error; - - if ((error = pack_backend__refresh(backend)) < 0) - return error; - - return pack_backend__read_prefix_internal( - out_oid, buffer_p, len_p, type_p, backend, short_oid, len); -} - static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid) { struct git_pack_entry e; - int error; - - error = pack_entry_find(&e, (struct pack_backend *)backend, oid); - - if (error != GIT_ENOTFOUND) - return error == 0; - - if ((error = pack_backend__refresh(backend)) < 0) { - giterr_clear(); - return (int)false; - } - return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0; } @@ -501,12 +431,7 @@ static int pack_backend__exists_prefix( struct git_pack_entry e = {0}; error = pack_entry_find_prefix(&e, pb, short_id, len); - - if (error == GIT_ENOTFOUND && !(error = pack_backend__refresh(backend))) - error = pack_entry_find_prefix(&e, pb, short_id, len); - git_oid_cpy(out, &e.sha1); - return error; } @@ -674,7 +599,6 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) git_path_isdir(git_buf_cstr(&path))) { backend->pack_folder = git_buf_detach(&path); - error = pack_backend__refresh((git_odb_backend *)backend); } From a0a1b19ab043f3579aabfb7602b4c4ac4dd69e72 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 14 Oct 2015 19:31:54 +0200 Subject: [PATCH 195/450] odb: Prioritize alternate backends For most real use cases, repositories with alternates use them as main object storage. Checking the alternate for objects before the main repository should result in measurable speedups. Because of this, we're changing the sorting algorithm to prioritize alternates *in cases where two backends have the same priority*. This means that the pack backend for the alternate will be checked before the pack backend for the main repository *but* both of them will be checked before any loose backends. --- src/odb.c | 12 ++++++++---- tests/odb/sorting.c | 16 ++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/odb.c b/src/odb.c index af1ff7fa2..1c877c9fc 100644 --- a/src/odb.c +++ b/src/odb.c @@ -374,10 +374,14 @@ static int backend_sort_cmp(const void *a, const void *b) const backend_internal *backend_a = (const backend_internal *)(a); const backend_internal *backend_b = (const backend_internal *)(b); - if (backend_a->is_alternate == backend_b->is_alternate) - return (backend_b->priority - backend_a->priority); - - return backend_a->is_alternate ? 1 : -1; + if (backend_b->priority == backend_a->priority) { + if (backend_a->is_alternate) + return -1; + if (backend_b->is_alternate) + return 1; + return 0; + } + return (backend_b->priority - backend_a->priority); } int git_odb_new(git_odb **out) diff --git a/tests/odb/sorting.c b/tests/odb/sorting.c index d24c49c69..6af8b0d1b 100644 --- a/tests/odb/sorting.c +++ b/tests/odb/sorting.c @@ -57,14 +57,14 @@ void test_odb_sorting__basic_backends_sorting(void) void test_odb_sorting__alternate_backends_sorting(void) { - cl_git_pass(git_odb_add_backend(_odb, new_backend(0), 5)); - cl_git_pass(git_odb_add_backend(_odb, new_backend(2), 3)); - cl_git_pass(git_odb_add_backend(_odb, new_backend(1), 4)); - cl_git_pass(git_odb_add_backend(_odb, new_backend(3), 1)); - cl_git_pass(git_odb_add_alternate(_odb, new_backend(4), 5)); - cl_git_pass(git_odb_add_alternate(_odb, new_backend(6), 3)); - cl_git_pass(git_odb_add_alternate(_odb, new_backend(5), 4)); - cl_git_pass(git_odb_add_alternate(_odb, new_backend(7), 1)); + cl_git_pass(git_odb_add_backend(_odb, new_backend(1), 5)); + cl_git_pass(git_odb_add_backend(_odb, new_backend(5), 3)); + cl_git_pass(git_odb_add_backend(_odb, new_backend(3), 4)); + cl_git_pass(git_odb_add_backend(_odb, new_backend(7), 1)); + cl_git_pass(git_odb_add_alternate(_odb, new_backend(0), 5)); + cl_git_pass(git_odb_add_alternate(_odb, new_backend(4), 3)); + cl_git_pass(git_odb_add_alternate(_odb, new_backend(2), 4)); + cl_git_pass(git_odb_add_alternate(_odb, new_backend(6), 1)); check_backend_sorting(_odb); } From 307c4a2b6d1e3b403225b3d5b7f9894b940b24bd Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 21 Oct 2015 11:58:44 +0200 Subject: [PATCH 196/450] signature: Strip crud just like Git does --- src/signature.c | 18 ++++++++++++++++-- tests/commit/signature.c | 7 +++++++ tests/revwalk/signatureparsing.c | 2 +- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/signature.c b/src/signature.c index 818cd300e..109476efe 100644 --- a/src/signature.c +++ b/src/signature.c @@ -34,13 +34,27 @@ static bool contains_angle_brackets(const char *input) return strchr(input, '<') != NULL || strchr(input, '>') != NULL; } +static bool is_crud(unsigned char c) +{ + return c <= 32 || + c == '.' || + c == ',' || + c == ':' || + c == ';' || + c == '<' || + c == '>' || + c == '"' || + c == '\\' || + c == '\''; +} + static char *extract_trimmed(const char *ptr, size_t len) { - while (len && git__isspace(ptr[0])) { + while (len && is_crud((unsigned char)ptr[0])) { ptr++; len--; } - while (len && git__isspace(ptr[len - 1])) { + while (len && is_crud((unsigned char)ptr[len - 1])) { len--; } diff --git a/tests/commit/signature.c b/tests/commit/signature.c index 41a74b999..0070320ae 100644 --- a/tests/commit/signature.c +++ b/tests/commit/signature.c @@ -35,6 +35,13 @@ void test_commit_signature__leading_and_trailing_spaces_are_trimmed(void) assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " \t nulltoken \n", " \n emeric.fermas@gmail.com \n"); } +void test_commit_signature__leading_and_trailing_crud_is_trimmed(void) +{ + assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", "\"nulltoken\"", "\"emeric.fermas@gmail.com\""); + assert_name_and_email("nulltoken w", "emeric.fermas@gmail.com", "nulltoken w.", "emeric.fermas@gmail.com"); + assert_name_and_email("nulltoken \xe2\x98\xba", "emeric.fermas@gmail.com", "nulltoken \xe2\x98\xba", "emeric.fermas@gmail.com"); +} + void test_commit_signature__angle_brackets_in_names_are_not_supported(void) { cl_git_fail(try_build_signature("email); - cl_assert_equal_s("", signature->name); + cl_assert_equal_s("Yu V. Bin Haacked", signature->name); cl_assert_equal_i(1323847743, (int)signature->when.time); cl_assert_equal_i(60, signature->when.offset); From 128e94bbbb1f1af539be7fb2844e261bfdb28fed Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 21 Oct 2015 12:04:53 +0200 Subject: [PATCH 197/450] index: Remove unneeded consts --- src/index.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.c b/src/index.c index c0be5b90d..334a13135 100644 --- a/src/index.c +++ b/src/index.c @@ -1193,13 +1193,13 @@ static int index_no_dups(void **old, void *new) } static void index_existing_and_best( - const git_index_entry **existing, + git_index_entry **existing, size_t *existing_position, - const git_index_entry **best, + git_index_entry **best, git_index *index, const git_index_entry *entry) { - const git_index_entry *e; + git_index_entry *e; size_t pos; int error; From bbe1957b8c75760c81ce04c7edf6d203513b39f8 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 21 Oct 2015 12:09:29 +0200 Subject: [PATCH 198/450] tests: Fix warnings --- tests/diff/workdir.c | 2 +- tests/revwalk/basic.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c index 89f9483a6..dac32453b 100644 --- a/tests/diff/workdir.c +++ b/tests/diff/workdir.c @@ -2080,7 +2080,7 @@ void test_diff_workdir__to_index_pathlist(void) cl_git_pass(git_repository_index(&index, g_repo)); opts.flags = GIT_DIFF_INCLUDE_IGNORED; - opts.pathspec.strings = pathlist.contents; + opts.pathspec.strings = (char **)pathlist.contents; opts.pathspec.count = pathlist.length; cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &opts)); diff --git a/tests/revwalk/basic.c b/tests/revwalk/basic.c index d8236ce72..5ed7da4eb 100644 --- a/tests/revwalk/basic.c +++ b/tests/revwalk/basic.c @@ -456,7 +456,8 @@ void test_revwalk_basic__big_timestamp(void) cl_git_pass(git_signature_new(&sig, "Joe", "joe@example.com", 2399662595, 0)); cl_git_pass(git_commit_tree(&tree, tip)); - cl_git_pass(git_commit_create(&id, _repo, "HEAD", sig, sig, NULL, "some message", tree, 1, &tip)); + cl_git_pass(git_commit_create(&id, _repo, "HEAD", sig, sig, NULL, "some message", tree, 1, + (const git_commit **)&tip)); cl_git_pass(git_revwalk_push_head(_walk)); From 8c7c5fa585c6a63dc8186febd6e032880655e85e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 20 Oct 2015 17:42:42 +0200 Subject: [PATCH 199/450] config: add a ProgramData level This is where portable git stores the global configuration which we can use to adhere to it even though git isn't quite installed on the system. --- include/git2/config.h | 24 +++++++++++++++++++----- src/config.c | 10 ++++++++++ src/config.h | 1 + src/repository.c | 14 ++++++++++++-- src/sysdir.c | 19 ++++++++++++++++++- src/sysdir.h | 14 ++++++++++++-- src/win32/findfile.c | 10 ++++++++++ src/win32/findfile.h | 1 + tests/config/global.c | 40 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 123 insertions(+), 10 deletions(-) diff --git a/include/git2/config.h b/include/git2/config.h index 56b5431ac..d0f1ba1b3 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -29,25 +29,28 @@ GIT_BEGIN_DECL * priority levels as well. */ typedef enum { + /** System-wide on Windows, for compatibility with portable git */ + GIT_CONFIG_LEVEL_PROGRAMDATA = 1, + /** System-wide configuration file; /etc/gitconfig on Linux systems */ - GIT_CONFIG_LEVEL_SYSTEM = 1, + GIT_CONFIG_LEVEL_SYSTEM = 2, /** XDG compatible configuration file; typically ~/.config/git/config */ - GIT_CONFIG_LEVEL_XDG = 2, + GIT_CONFIG_LEVEL_XDG = 3, /** User-specific configuration file (also called Global configuration * file); typically ~/.gitconfig */ - GIT_CONFIG_LEVEL_GLOBAL = 3, + GIT_CONFIG_LEVEL_GLOBAL = 4, /** Repository specific configuration file; $WORK_DIR/.git/config on * non-bare repos */ - GIT_CONFIG_LEVEL_LOCAL = 4, + GIT_CONFIG_LEVEL_LOCAL = 5, /** Application specific configuration file; freely defined by applications */ - GIT_CONFIG_LEVEL_APP = 5, + GIT_CONFIG_LEVEL_APP = 6, /** Represents the highest level available config file (i.e. the most * specific config file available that actually is loaded) @@ -141,6 +144,17 @@ GIT_EXTERN(int) git_config_find_xdg(git_buf *out); */ GIT_EXTERN(int) git_config_find_system(git_buf *out); +/** + * Locate the path to the configuration file in ProgramData + * + * Look for the file in %PROGRAMDATA%\Git\config used by portable git. + * + * @param out Pointer to a user-allocated git_buf in which to store the path + * @return 0 if a ProgramData configuration file has been + * found. Its path will be stored in `out`. + */ +GIT_EXTERN(int) git_config_find_programdata(git_buf *out); + /** * Open the global, XDG and system configuration files * diff --git a/src/config.c b/src/config.c index f0b2c3a61..f4d4cb2b9 100644 --- a/src/config.c +++ b/src/config.c @@ -1086,6 +1086,12 @@ int git_config_find_system(git_buf *path) return git_sysdir_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM); } +int git_config_find_programdata(git_buf *path) +{ + git_buf_sanitize(path); + return git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA); +} + int git_config__global_location(git_buf *buf) { const git_buf *paths; @@ -1133,6 +1139,10 @@ int git_config_open_default(git_config **out) error = git_config_add_file_ondisk(cfg, buf.ptr, GIT_CONFIG_LEVEL_SYSTEM, 0); + if (!error && !git_config_find_programdata(&buf)) + error = git_config_add_file_ondisk(cfg, buf.ptr, + GIT_CONFIG_LEVEL_PROGRAMDATA, 0); + git_buf_free(&buf); if (error) { diff --git a/src/config.h b/src/config.h index ba745331a..00c12b50d 100644 --- a/src/config.h +++ b/src/config.h @@ -12,6 +12,7 @@ #include "vector.h" #include "repository.h" +#define GIT_CONFIG_FILENAME_PROGRAMDATA "config" #define GIT_CONFIG_FILENAME_SYSTEM "gitconfig" #define GIT_CONFIG_FILENAME_GLOBAL ".gitconfig" #define GIT_CONFIG_FILENAME_XDG "config" diff --git a/src/repository.c b/src/repository.c index 77145cfc8..38d18693a 100644 --- a/src/repository.c +++ b/src/repository.c @@ -585,7 +585,8 @@ static int load_config( git_repository *repo, const char *global_config_path, const char *xdg_config_path, - const char *system_config_path) + const char *system_config_path, + const char *programdata_path) { int error; git_buf config_path = GIT_BUF_INIT; @@ -626,6 +627,12 @@ static int load_config( error != GIT_ENOTFOUND) goto on_error; + if (programdata_path != NULL && + (error = git_config_add_file_ondisk( + cfg, programdata_path, GIT_CONFIG_LEVEL_PROGRAMDATA, 0)) < 0 && + error != GIT_ENOTFOUND) + goto on_error; + giterr_clear(); /* clear any lingering ENOTFOUND errors */ *out = cfg; @@ -651,11 +658,13 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo) git_buf global_buf = GIT_BUF_INIT; git_buf xdg_buf = GIT_BUF_INIT; git_buf system_buf = GIT_BUF_INIT; + git_buf programdata_buf = GIT_BUF_INIT; git_config *config; git_config_find_global(&global_buf); git_config_find_xdg(&xdg_buf); git_config_find_system(&system_buf); + git_config_find_programdata(&programdata_buf); /* If there is no global file, open a backend for it anyway */ if (git_buf_len(&global_buf) == 0) @@ -665,7 +674,8 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo) &config, repo, path_unless_empty(&global_buf), path_unless_empty(&xdg_buf), - path_unless_empty(&system_buf)); + path_unless_empty(&system_buf), + path_unless_empty(&programdata_buf)); if (!error) { GIT_REFCOUNT_OWN(config, repo); diff --git a/src/sysdir.c b/src/sysdir.c index 2795de491..bf53d830f 100644 --- a/src/sysdir.c +++ b/src/sysdir.c @@ -15,6 +15,16 @@ #include "win32/findfile.h" #endif +static int git_sysdir_guess_programdata_dirs(git_buf *out) +{ +#ifdef GIT_WIN32 + return git_win32__find_programdata_dirs(out); +#else + git_buf_clear(out); + return 0; +#endif +} + static int git_sysdir_guess_system_dirs(git_buf *out) { #ifdef GIT_WIN32 @@ -76,12 +86,13 @@ static int git_sysdir_guess_template_dirs(git_buf *out) typedef int (*git_sysdir_guess_cb)(git_buf *out); static git_buf git_sysdir__dirs[GIT_SYSDIR__MAX] = - { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT }; + { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT }; static git_sysdir_guess_cb git_sysdir__dir_guess[GIT_SYSDIR__MAX] = { git_sysdir_guess_system_dirs, git_sysdir_guess_global_dirs, git_sysdir_guess_xdg_dirs, + git_sysdir_guess_programdata_dirs, git_sysdir_guess_template_dirs, }; @@ -258,6 +269,12 @@ int git_sysdir_find_xdg_file(git_buf *path, const char *filename) path, filename, GIT_SYSDIR_XDG, "global/xdg"); } +int git_sysdir_find_programdata_file(git_buf *path, const char *filename) +{ + return git_sysdir_find_in_dirlist( + path, filename, GIT_SYSDIR_PROGRAMDATA, "ProgramData"); +} + int git_sysdir_find_template_dir(git_buf *path) { return git_sysdir_find_in_dirlist( diff --git a/src/sysdir.h b/src/sysdir.h index f1bbf0bae..12874fc85 100644 --- a/src/sysdir.h +++ b/src/sysdir.h @@ -38,6 +38,15 @@ extern int git_sysdir_find_xdg_file(git_buf *path, const char *filename); */ extern int git_sysdir_find_system_file(git_buf *path, const char *filename); +/** + * Find a "ProgramData" file (i.e. one in %PROGRAMDATA%) + * + * @param path buffer to write the full path into + * @param filename name of file to find in the ProgramData directory + * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error + */ +extern int git_sysdir_find_programdata_file(git_buf *path, const char *filename); + /** * Find template directory. * @@ -50,8 +59,9 @@ typedef enum { GIT_SYSDIR_SYSTEM = 0, GIT_SYSDIR_GLOBAL = 1, GIT_SYSDIR_XDG = 2, - GIT_SYSDIR_TEMPLATE = 3, - GIT_SYSDIR__MAX = 4, + GIT_SYSDIR_PROGRAMDATA = 3, + GIT_SYSDIR_TEMPLATE = 4, + GIT_SYSDIR__MAX = 5, } git_sysdir_t; /** diff --git a/src/win32/findfile.c b/src/win32/findfile.c index de27dd060..58c22279e 100644 --- a/src/win32/findfile.c +++ b/src/win32/findfile.c @@ -215,3 +215,13 @@ int git_win32__find_xdg_dirs(git_buf *out) return win32_find_existing_dirs(out, global_tmpls); } + +int git_win32__find_programdata_dirs(git_buf *out) +{ + static const wchar_t *programdata_tmpls[2] = { + L"%PROGRAMDATA%\\Git", + NULL, + }; + + return win32_find_existing_dirs(out, programdata_tmpls); +} diff --git a/src/win32/findfile.h b/src/win32/findfile.h index a50319b9a..3d5fff439 100644 --- a/src/win32/findfile.h +++ b/src/win32/findfile.h @@ -11,6 +11,7 @@ extern int git_win32__find_system_dirs(git_buf *out, const wchar_t *subpath); extern int git_win32__find_global_dirs(git_buf *out); extern int git_win32__find_xdg_dirs(git_buf *out); +extern int git_win32__find_programdata_dirs(git_buf *out); #endif diff --git a/tests/config/global.c b/tests/config/global.c index b5e83fec0..1336ef6e7 100644 --- a/tests/config/global.c +++ b/tests/config/global.c @@ -65,3 +65,43 @@ void test_config_global__open_xdg(void) git_config_free(xdg); git_config_free(cfg); } + +void test_config_global__open_programdata(void) +{ + char *programdata; + git_config *cfg; + git_repository *repo; + git_buf config_path = GIT_BUF_INIT; + git_buf var_contents = GIT_BUF_INIT; + + if (!cl_getenv("GITTEST_INVASIVE_FS_STRUCTURE")) + cl_skip(); + + programdata = cl_getenv("PROGRAMDATA"); + cl_git_pass(git_buf_printf(&config_path, "%s/Git", programdata)); + cl_git_pass(p_mkdir(config_path.ptr, 0777)); + cl_git_pass(git_buf_puts(&config_path, "/config")); + + cl_git_pass(git_config_open_ondisk(&cfg, config_path.ptr)); + cl_git_pass(git_config_set_string(cfg, "programdata.var", "even higher level")); + + git_buf_free(&config_path); + git_config_free(cfg); + + git_config_open_default(&cfg); + cl_git_pass(git_config_get_string_buf(&var_contents, cfg, "programdata.var")); + cl_assert_equal_s("even higher level", var_contents.ptr); + + git_config_free(cfg); + git_buf_free(&var_contents); + + cl_git_pass(git_repository_init(&repo, "./foo.git", true)); + cl_git_pass(git_repository_config(&cfg, repo)); + cl_git_pass(git_config_get_string_buf(&var_contents, cfg, "programdata.var")); + cl_assert_equal_s("even higher level", var_contents.ptr); + + git_config_free(cfg); + git_buf_free(&var_contents); + git_repository_free(repo); + cl_fixture_cleanup("./foo.git"); +} From 0f9b6742ad93111e95c6a0f14f2c2c5834c685eb Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 21 Oct 2015 09:24:10 -0400 Subject: [PATCH 200/450] win32: add c linkage guard around inttypes.h inclusion --- include/git2/common.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/include/git2/common.h b/include/git2/common.h index d84a76512..748226385 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -10,12 +10,6 @@ #include #include -#ifdef _MSC_VER -# include "inttypes.h" -#else -# include -#endif - #ifdef __cplusplus # define GIT_BEGIN_DECL extern "C" { # define GIT_END_DECL } @@ -26,6 +20,14 @@ # define GIT_END_DECL /* empty */ #endif +#ifdef _MSC_VER + GIT_BEGIN_DECL +# include "inttypes.h" + GIT_END_DECL +#else +# include +#endif + /** Declare a public function exported for application use. */ #if __GNUC__ >= 4 # define GIT_EXTERN(type) extern \ From 240a85cf10b0ff22a4690d7b01a652b121026d41 Mon Sep 17 00:00:00 2001 From: Linquize Date: Thu, 22 Oct 2015 07:56:34 +0800 Subject: [PATCH 201/450] inttypes.h is built-in header file since MSVC 2013 The reason is that the types defined in libgit2's inttypes.h collide with system inttypes.h 3rd party library header files may directly reference MSVC's built-in inttypes.h Fixes #3476 --- include/git2/common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/git2/common.h b/include/git2/common.h index 748226385..577906115 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -20,7 +20,7 @@ # define GIT_END_DECL /* empty */ #endif -#ifdef _MSC_VER +#if defined(_MSC_VER) && _MSC_VER < 1800 GIT_BEGIN_DECL # include "inttypes.h" GIT_END_DECL From 99a09f7f18f321c2a37e483bcd8aeb6dff781abc Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 22 Oct 2015 09:29:40 -0400 Subject: [PATCH 202/450] index: test that we round-trip nsecs Test that nanoseconds are round-tripped correctly when we read an index file that contains them. We should, however, ignore them because we don't understand them, and any new entries in the index should contain a `0` nsecs field, while existing preserving entries. --- tests/index/nsec.c | 78 ++++++++++++++++++ tests/resources/nsecs/.gitted/HEAD | Bin 0 -> 23 bytes tests/resources/nsecs/.gitted/config | Bin 0 -> 157 bytes tests/resources/nsecs/.gitted/index | Bin 0 -> 281 bytes .../03/1986a8372d1442cfe9e3b54906a9aadc524a7e | Bin 0 -> 155 bytes .../03/9afd91c98f82c14e425bb6796d8ca98e9c8cac | Bin 0 -> 102 bytes .../6d/8b18077cc99abd8dda05a6062c646406abb2d4 | Bin 0 -> 22 bytes .../c5/12b6c64656b87ea8caf37a32bc5a562d797745 | Bin 0 -> 22 bytes .../df/78d3d51c369e1d2f1eadb73464aadd931d56b4 | Bin 0 -> 22 bytes .../resources/nsecs/.gitted/refs/heads/master | Bin 0 -> 41 bytes tests/resources/nsecs/a.txt | Bin 0 -> 8 bytes tests/resources/nsecs/b.txt | Bin 0 -> 8 bytes tests/resources/nsecs/c.txt | Bin 0 -> 8 bytes 13 files changed, 78 insertions(+) create mode 100644 tests/index/nsec.c create mode 100644 tests/resources/nsecs/.gitted/HEAD create mode 100644 tests/resources/nsecs/.gitted/config create mode 100644 tests/resources/nsecs/.gitted/index create mode 100644 tests/resources/nsecs/.gitted/objects/03/1986a8372d1442cfe9e3b54906a9aadc524a7e create mode 100644 tests/resources/nsecs/.gitted/objects/03/9afd91c98f82c14e425bb6796d8ca98e9c8cac create mode 100644 tests/resources/nsecs/.gitted/objects/6d/8b18077cc99abd8dda05a6062c646406abb2d4 create mode 100644 tests/resources/nsecs/.gitted/objects/c5/12b6c64656b87ea8caf37a32bc5a562d797745 create mode 100644 tests/resources/nsecs/.gitted/objects/df/78d3d51c369e1d2f1eadb73464aadd931d56b4 create mode 100644 tests/resources/nsecs/.gitted/refs/heads/master create mode 100644 tests/resources/nsecs/a.txt create mode 100644 tests/resources/nsecs/b.txt create mode 100644 tests/resources/nsecs/c.txt diff --git a/tests/index/nsec.c b/tests/index/nsec.c new file mode 100644 index 000000000..5004339f0 --- /dev/null +++ b/tests/index/nsec.c @@ -0,0 +1,78 @@ +#include "clar_libgit2.h" +#include "index.h" +#include "git2/sys/index.h" +#include "git2/repository.h" +#include "../reset/reset_helpers.h" + +static git_repository *repo; +static git_index *repo_index; + +#define TEST_REPO_PATH "nsecs" + +// Fixture setup and teardown +void test_index_nsec__initialize(void) +{ + repo = cl_git_sandbox_init("nsecs"); + git_repository_index(&repo_index, repo); +} + +void test_index_nsec__cleanup(void) +{ + git_index_free(repo_index); + repo_index = NULL; + + cl_git_sandbox_cleanup(); +} + +static bool has_nsecs(void) +{ + const git_index_entry *entry; + size_t i; + bool has_nsecs = false; + + for (i = 0; i < git_index_entrycount(repo_index); i++) { + entry = git_index_get_byindex(repo_index, i); + + if (entry->ctime.nanoseconds || entry->mtime.nanoseconds) { + has_nsecs = true; + break; + } + } + + return has_nsecs; +} + +void test_index_nsec__has_nanos(void) +{ + cl_assert_equal_b(true, has_nsecs()); +} + +void test_index_nsec__staging_maintains_other_nanos(void) +{ + const git_index_entry *entry; + + cl_git_rewritefile("nsecs/a.txt", "This is file A"); + cl_git_pass(git_index_add_bypath(repo_index, "a.txt")); + cl_git_pass(git_index_write(repo_index)); + + cl_git_pass(git_index_write(repo_index)); + + git_index_read(repo_index, 1); + cl_assert_equal_b(true, has_nsecs()); + + cl_assert((entry = git_index_get_bypath(repo_index, "a.txt", 0))); + cl_assert_equal_i(0, entry->ctime.nanoseconds); + cl_assert_equal_i(0, entry->mtime.nanoseconds); +} + +void test_index_nsec__status_doesnt_clear_nsecs(void) +{ + git_status_list *statuslist; + + cl_git_pass(git_status_list_new(&statuslist, repo, NULL)); + + git_index_read(repo_index, 1); + cl_assert_equal_b(true, has_nsecs()); + + git_status_list_free(statuslist); +} diff --git a/tests/resources/nsecs/.gitted/HEAD b/tests/resources/nsecs/.gitted/HEAD new file mode 100644 index 0000000000000000000000000000000000000000..cb089cd89a7d7686d284d8761201649346b5aa1c GIT binary patch literal 23 ecmXR)O|w!cN=+-)&qz&7Db~+TEG|hc;sO9;xClW2 literal 0 HcmV?d00001 diff --git a/tests/resources/nsecs/.gitted/config b/tests/resources/nsecs/.gitted/config new file mode 100644 index 0000000000000000000000000000000000000000..78387c50b472d8c0cdaeca4b56bf23c5b7f791df GIT binary patch literal 157 zcmYj~F%H5o3`OVU6g@&Oz=mM#0T6>}oMPd`mF*(s_9TLV@qN9ge_KWI9)sd8MlRa2 zXaRqt8cCf_(;P!4#v%zjWnjY?9-x23vH`PVKK2BT);-swYt{-=eU0H4(pLN73O4eu U^rVDm@k>7$ViLcSFO^&T0N7wQ1ONa4 literal 0 HcmV?d00001 diff --git a/tests/resources/nsecs/.gitted/index b/tests/resources/nsecs/.gitted/index new file mode 100644 index 0000000000000000000000000000000000000000..9233f1b11e8179f0f9655631bd9064d4e4229fe0 GIT binary patch literal 281 zcmZ?q402{*U|<4b<}eMjgR(2EG=TJB_Mr0bI!T~d62^l|LnLd9wO)Yqy)Ethzf{Hf#$6YckxN zkRVrApk_%1V+8{)=2?Fyp6qWr=;su@tunV~W#62hH5rSxp7WY#$)m^phWFf~Ww-2J HvG4-`s})W= literal 0 HcmV?d00001 diff --git a/tests/resources/nsecs/.gitted/objects/03/1986a8372d1442cfe9e3b54906a9aadc524a7e b/tests/resources/nsecs/.gitted/objects/03/1986a8372d1442cfe9e3b54906a9aadc524a7e new file mode 100644 index 0000000000000000000000000000000000000000..a813b74244dbf0c6c18a967cc5930ea2c1b2e75d GIT binary patch literal 155 zcmV;M0A&Ao0i}&W3c@fHL|x|;{{l)H(=;DM6zSGYbuH=7FYO{m(n#_4M)3x&X66md zPz+(ppp;tW#0+waZtz$U+QBNSG;3A&y=l;ROAaj~6(!-Gd5j5KzqsV#GR82+JM6g7 zk2~h^t2bSN zDo6q=I4ZR5m|NJ6x)rBBR~hYz3e&ADcZDiQ1}nHdYP=+cFBy literal 0 HcmV?d00001 diff --git a/tests/resources/nsecs/a.txt b/tests/resources/nsecs/a.txt new file mode 100644 index 0000000000000000000000000000000000000000..be4c1ee68016c22d2a2069fceef064e8638d23b1 GIT binary patch literal 8 PcmZ?E%t=*n Date: Thu, 22 Oct 2015 09:30:41 -0400 Subject: [PATCH 203/450] diff: ignore nsecs when diffing Although our index contains the literal time present in the index, we do not read nanoseconds from disk, and thus we should not use them in any comparisons, lest we always think our working directory is dirty. Guard this behind a `GIT_USE_NSECS` for future improvement. --- src/diff.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/diff.c b/src/diff.c index d97dcd9d2..d98a28966 100644 --- a/src/diff.c +++ b/src/diff.c @@ -493,8 +493,10 @@ static int diff_list_apply_options( /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */ - /* Set GIT_DIFFCAPS_TRUST_NANOSECS on a platform basis */ + /* Don't trust nanoseconds; we do not load nanos from disk */ +#ifdef GIT_USE_NSEC diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_NANOSECS; +#endif /* If not given explicit `opts`, check `diff.xyz` configs */ if (!opts) { From c7b336b0845eaa8f629bf24dbb0c666612a927ab Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 22 Oct 2015 10:29:51 -0400 Subject: [PATCH 204/450] xdiff: reference util.h in parent directory Although CMake will correctly configure include directories for us, some people may use their own build system, and we should reference `util.h` based on where it actually lives. --- src/xdiff/xdiff.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xdiff/xdiff.h b/src/xdiff/xdiff.h index db5d59884..f08f72e16 100644 --- a/src/xdiff/xdiff.h +++ b/src/xdiff/xdiff.h @@ -20,7 +20,7 @@ * */ -#include "util.h" +#include "../util.h" #if !defined(XDIFF_H) #define XDIFF_H From 8683d31f080eb12fe769ab2363165ec17562c840 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 22 Oct 2015 14:39:20 -0400 Subject: [PATCH 205/450] merge: add GIT_MERGE_TREE_FAIL_ON_CONFLICT Provide a new merge option, GIT_MERGE_TREE_FAIL_ON_CONFLICT, which will stop on the first conflict and fail the merge operation with GIT_EMERGECONFLICT. --- include/git2/errors.h | 1 + include/git2/merge.h | 7 +++++++ src/merge.c | 9 ++++++++- tests/merge/merge_helpers.c | 9 +++++---- tests/merge/trees/commits.c | 18 +++++++++++++++++- 5 files changed, 38 insertions(+), 6 deletions(-) diff --git a/include/git2/errors.h b/include/git2/errors.h index 4698366d8..1b528cf25 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -49,6 +49,7 @@ typedef enum { GIT_EINVALID = -21, /**< Invalid operation or input */ GIT_EUNCOMMITTED = -22, /**< Uncommitted changes in index prevented operation */ GIT_EDIRECTORY = -23, /**< The operation is not valid for a directory */ + GIT_EMERGECONFLICT = -24, /**< A merge conflict exists and cannot continue */ GIT_PASSTHROUGH = -30, /**< Internal only */ GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */ diff --git a/include/git2/merge.h b/include/git2/merge.h index 5fef452b9..ced9e51ff 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -72,6 +72,13 @@ typedef enum { * the ability to merge between a modified and renamed file. */ GIT_MERGE_TREE_FIND_RENAMES = (1 << 0), + + /** + * If a conflict occurs, exit immediately instead of attempting to + * continue resolving conflicts. The merge operation will fail with + * GIT_EMERGECONFLICT and no index will be returned. + */ + GIT_MERGE_TREE_FAIL_ON_CONFLICT = (1 << 1), } git_merge_tree_flag_t; /** diff --git a/src/merge.c b/src/merge.c index 930457bdb..3bed0fd3b 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1701,8 +1701,15 @@ int git_merge__iterators( if ((error = merge_conflict_resolve(&resolved, diff_list, conflict, opts.file_favor, opts.file_flags)) < 0) goto done; - if (!resolved) + if (!resolved) { + if ((opts.tree_flags & GIT_MERGE_TREE_FAIL_ON_CONFLICT)) { + giterr_set(GITERR_MERGE, "merge conflicts exist"); + error = GIT_EMERGECONFLICT; + goto done; + } + git_vector_insert(&diff_list->conflicts, conflict); + } } if (!given_opts || !given_opts->metric) diff --git a/tests/merge/merge_helpers.c b/tests/merge/merge_helpers.c index f81471424..986a365db 100644 --- a/tests/merge/merge_helpers.c +++ b/tests/merge/merge_helpers.c @@ -40,7 +40,7 @@ int merge_trees_from_branches( cl_git_pass(git_commit_tree(&our_tree, our_commit)); cl_git_pass(git_commit_tree(&their_tree, their_commit)); - cl_git_pass(git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts)); + error = git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts); git_buf_free(&branch_buf); git_tree_free(our_tree); @@ -50,7 +50,7 @@ int merge_trees_from_branches( git_commit_free(their_commit); git_commit_free(ancestor_commit); - return 0; + return error; } int merge_commits_from_branches( @@ -61,6 +61,7 @@ int merge_commits_from_branches( git_commit *our_commit, *their_commit; git_oid our_oid, their_oid; git_buf branch_buf = GIT_BUF_INIT; + int error; git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name); cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr)); @@ -71,13 +72,13 @@ int merge_commits_from_branches( cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr)); cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid)); - cl_git_pass(git_merge_commits(index, repo, our_commit, their_commit, opts)); + error = git_merge_commits(index, repo, our_commit, their_commit, opts); git_buf_free(&branch_buf); git_commit_free(our_commit); git_commit_free(their_commit); - return 0; + return error; } int merge_branches(git_repository *repo, diff --git a/tests/merge/trees/commits.c b/tests/merge/trees/commits.c index c4e470997..2e3c4578b 100644 --- a/tests/merge/trees/commits.c +++ b/tests/merge/trees/commits.c @@ -94,7 +94,6 @@ void test_merge_trees_commits__no_ancestor(void) git_index_free(index); } - void test_merge_trees_commits__df_conflict(void) { git_index *index; @@ -129,3 +128,20 @@ void test_merge_trees_commits__df_conflict(void) git_index_free(index); } + +void test_merge_trees_commits__fail_on_conflict(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + opts.tree_flags |= GIT_MERGE_TREE_FAIL_ON_CONFLICT; + + cl_git_fail_with(GIT_EMERGECONFLICT, + merge_trees_from_branches(&index, repo, "df_side1", "df_side2", &opts)); + + cl_git_fail_with(GIT_EMERGECONFLICT, + merge_commits_from_branches(&index, repo, "master", "unrelated", &opts)); + cl_git_fail_with(GIT_EMERGECONFLICT, + merge_commits_from_branches(&index, repo, "master", "branch", &opts)); +} + From 7208ff4d7dd744edcdc4345c97130568e3ea7638 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 22 Oct 2015 20:17:19 -0500 Subject: [PATCH 206/450] cmake: split sources into original paths for Xcode and MSVC The MSVC_SPLIT_SOURCES function is helpful for other IDEs, like Xcode, and will split the source files up into their target directories, instead of merely placing them all in a "Sources" directory. Rename MSVC_SPLIT_SOURCES to IDE_SPLIT_SOURCES and enable it for Xcode. --- CMakeLists.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f58eb0e7..92e0081ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,13 +137,13 @@ FUNCTION(TARGET_OS_LIBRARIES target) ENDIF() ENDFUNCTION() -# For the MSVC IDE, this function splits up the source files like windows -# explorer does. This is esp. useful with the libgit2_clar project, were -# usually 2 or more files share the same name. Sadly, this file grouping -# is a per-directory option in cmake and not per-target, resulting in -# empty virtual folders "tests" for the git2.dll -FUNCTION(MSVC_SPLIT_SOURCES target) - IF(MSVC_IDE) +# This function splits the sources files up into their appropriate +# subdirectories. This is especially useful for IDEs like Xcode and +# Visual Studio, so that you can navigate into the libgit2_clar project, +# and see the folders within the tests folder (instead of just seeing all +# source and tests in a single folder.) +FUNCTION(IDE_SPLIT_SOURCES target) + IF(MSVC_IDE OR CMAKE_GENERATOR STREQUAL Xcode) GET_TARGET_PROPERTY(sources ${target} SOURCES) FOREACH(source ${sources}) IF(source MATCHES ".*/") @@ -560,7 +560,7 @@ IF(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS) SET_TARGET_PROPERTIES(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64") ENDIF() -MSVC_SPLIT_SOURCES(git2) +IDE_SPLIT_SOURCES(git2) IF (SONAME) SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING}) @@ -629,7 +629,7 @@ IF (BUILD_CLAR) TARGET_LINK_LIBRARIES(libgit2_clar ${GSSAPI_LIBRARIES}) TARGET_LINK_LIBRARIES(libgit2_clar ${ICONV_LIBRARIES}) TARGET_OS_LIBRARIES(libgit2_clar) - MSVC_SPLIT_SOURCES(libgit2_clar) + IDE_SPLIT_SOURCES(libgit2_clar) IF (MSVC_IDE) # Precompiled headers From 2b96b6ef0c6c8ccc88d93d9630ccc9c70932eeb2 Mon Sep 17 00:00:00 2001 From: Stjepan Rajko Date: Wed, 14 Oct 2015 09:41:27 -0700 Subject: [PATCH 207/450] Fix docs typo geterr_clear -> giterr_clear --- docs/error-handling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/error-handling.md b/docs/error-handling.md index 2dbe64a71..719244d2f 100644 --- a/docs/error-handling.md +++ b/docs/error-handling.md @@ -128,7 +128,7 @@ The public error API bugs, but in the meantime, please code defensively and check for NULL when calling this function. -- `void geterr_clear(void)`: This function clears the last error. The +- `void giterr_clear(void)`: This function clears the last error. The library will call this when an error is generated by low level function and the higher level function handles the error. From d307a0134b97475abb03d0365458c318ba817f95 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Tue, 27 Oct 2015 22:17:32 +0100 Subject: [PATCH 208/450] reuc: Be smarter when inserting new REUC entries Inserting new REUC entries can quickly become pathological given that each insert unsorts the REUC vector, and both subsequent lookups *and* insertions will require sorting it again before being successful. To avoid this, we're switching to `git_vector_insert_sorted`: this keeps the REUC vector constantly sorted and lets us use the `on_dup` callback to skip an extra binary search on each insertion. --- src/index.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/index.c b/src/index.c index 334a13135..d9e713899 100644 --- a/src/index.c +++ b/src/index.c @@ -2000,27 +2000,24 @@ size_t git_index_reuc_entrycount(git_index *index) return index->reuc.length; } +static int index_reuc_on_dup(void **old, void *new) +{ + index_entry_reuc_free(*old); + *old = new; + return GIT_EEXISTS; +} + static int index_reuc_insert( git_index *index, - git_index_reuc_entry *reuc, - int replace) + git_index_reuc_entry *reuc) { - git_index_reuc_entry **existing = NULL; - size_t position; + int res; assert(index && reuc && reuc->path != NULL); + assert(git_vector_is_sorted(&index->reuc)); - if (!git_index_reuc_find(&position, index, reuc->path)) - existing = (git_index_reuc_entry **)&index->reuc.contents[position]; - - if (!replace || !existing) - return git_vector_insert(&index->reuc, reuc); - - /* exists, replace it */ - git__free(*existing); - *existing = reuc; - - return 0; + res = git_vector_insert_sorted(&index->reuc, reuc, &index_reuc_on_dup); + return res == GIT_EEXISTS ? 0 : res; } int git_index_reuc_add(git_index *index, const char *path, @@ -2035,7 +2032,7 @@ int git_index_reuc_add(git_index *index, const char *path, if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode, ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 || - (error = index_reuc_insert(index, reuc, 1)) < 0) + (error = index_reuc_insert(index, reuc)) < 0) index_entry_reuc_free(reuc); return error; @@ -2055,7 +2052,7 @@ const git_index_reuc_entry *git_index_reuc_get_bypath( if (!index->reuc.length) return NULL; - git_vector_sort(&index->reuc); + assert(git_vector_is_sorted(&index->reuc)); if (git_index_reuc_find(&pos, index, path) < 0) return NULL; @@ -2067,8 +2064,8 @@ const git_index_reuc_entry *git_index_reuc_get_byindex( git_index *index, size_t n) { assert(index); + assert(git_vector_is_sorted(&index->reuc)); - git_vector_sort(&index->reuc); return git_vector_get(&index->reuc, n); } @@ -2077,7 +2074,7 @@ int git_index_reuc_remove(git_index *index, size_t position) int error; git_index_reuc_entry *reuc; - git_vector_sort(&index->reuc); + assert(git_vector_is_sorted(&index->reuc)); reuc = git_vector_get(&index->reuc, position); error = git_vector_remove(&index->reuc, position); From a1f5d691a2bcaa83f60500b98ce5f393e7c03ad9 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Tue, 27 Oct 2015 22:42:15 +0100 Subject: [PATCH 209/450] merge: Implement `GIT_MERGE_TREE_SKIP_REUC` --- include/git2/merge.h | 5 ++++ src/merge.c | 71 +++++++++++++++++++++++++++----------------- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/include/git2/merge.h b/include/git2/merge.h index ced9e51ff..b7da63e0e 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -79,6 +79,11 @@ typedef enum { * GIT_EMERGECONFLICT and no index will be returned. */ GIT_MERGE_TREE_FAIL_ON_CONFLICT = (1 << 1), + + /** + * Do not write the REUC extension on the generated index + */ + GIT_MERGE_TREE_SKIP_REUC = (1 << 2), } git_merge_tree_flag_t; /** diff --git a/src/merge.c b/src/merge.c index 3bed0fd3b..29184f67b 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1541,7 +1541,45 @@ static int merge_index_insert_reuc( mode[0], oid[0], mode[1], oid[1], mode[2], oid[2]); } -int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list) +static int index_update_reuc(git_index *index, git_merge_diff_list *diff_list) +{ + int error; + size_t i; + git_merge_diff *conflict; + + /* Add each entry in the resolved conflict to the REUC independently, since + * the paths may differ due to renames. */ + git_vector_foreach(&diff_list->resolved, i, conflict) { + const git_index_entry *ancestor = + GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ? + &conflict->ancestor_entry : NULL; + + const git_index_entry *ours = + GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? + &conflict->our_entry : NULL; + + const git_index_entry *theirs = + GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? + &conflict->their_entry : NULL; + + if (ancestor != NULL && + (error = merge_index_insert_reuc(index, TREE_IDX_ANCESTOR, ancestor)) < 0) + return error; + + if (ours != NULL && + (error = merge_index_insert_reuc(index, TREE_IDX_OURS, ours)) < 0) + return error; + + if (theirs != NULL && + (error = merge_index_insert_reuc(index, TREE_IDX_THEIRS, theirs)) < 0) + return error; + } + + return 0; +} + +static int index_from_diff_list(git_index **out, + git_merge_diff_list *diff_list, bool skip_reuc) { git_index *index; size_t i; @@ -1600,31 +1638,8 @@ int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list) } } - /* Add each entry in the resolved conflict to the REUC independently, since - * the paths may differ due to renames. */ - git_vector_foreach(&diff_list->resolved, i, conflict) { - const git_index_entry *ancestor = - GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ? - &conflict->ancestor_entry : NULL; - - const git_index_entry *ours = - GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? - &conflict->our_entry : NULL; - - const git_index_entry *theirs = - GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? - &conflict->their_entry : NULL; - - if (ancestor != NULL && - (error = merge_index_insert_reuc(index, TREE_IDX_ANCESTOR, ancestor)) < 0) - goto on_error; - - if (ours != NULL && - (error = merge_index_insert_reuc(index, TREE_IDX_OURS, ours)) < 0) - goto on_error; - - if (theirs != NULL && - (error = merge_index_insert_reuc(index, TREE_IDX_THEIRS, theirs)) < 0) + if (!skip_reuc) { + if ((error = index_update_reuc(index, diff_list)) < 0) goto on_error; } @@ -1633,7 +1648,6 @@ int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list) on_error: git_index_free(index); - return error; } @@ -1715,7 +1729,8 @@ int git_merge__iterators( if (!given_opts || !given_opts->metric) git__free(opts.metric); - error = index_from_diff_list(out, diff_list); + error = index_from_diff_list(out, diff_list, + (opts.tree_flags & GIT_MERGE_TREE_SKIP_REUC)); done: git_merge_diff_list__free(diff_list); From 7a02e93e02f34befa493405b6287595a0ccaef79 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Tue, 27 Oct 2015 22:42:40 +0100 Subject: [PATCH 210/450] merge: Plug memory leak --- src/merge.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/merge.c b/src/merge.c index 29184f67b..186c77037 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1726,13 +1726,13 @@ int git_merge__iterators( } } - if (!given_opts || !given_opts->metric) - git__free(opts.metric); - error = index_from_diff_list(out, diff_list, (opts.tree_flags & GIT_MERGE_TREE_SKIP_REUC)); done: + if (!given_opts || !given_opts->metric) + git__free(opts.metric); + git_merge_diff_list__free(diff_list); git_iterator_free(empty_ancestor); git_iterator_free(empty_ours); From 1e5e02b4f47779fe3733b1a6ab24a6ca13099ec3 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Tue, 27 Oct 2015 17:26:04 +0100 Subject: [PATCH 211/450] pool: Simplify implementation --- src/attr_file.c | 6 +- src/attrcache.c | 5 +- src/checkout.c | 7 +- src/diff.c | 5 +- src/diff_tform.c | 6 +- src/index.c | 2 +- src/iterator.c | 7 +- src/merge.c | 6 +- src/pack-objects.c | 3 +- src/pathspec.c | 12 +-- src/pool.c | 224 +++++++-------------------------------------- src/pool.h | 37 +------- src/refdb_fs.c | 5 +- src/revwalk.c | 6 +- src/sortedcache.c | 5 +- src/transaction.c | 3 +- tests/core/pool.c | 82 +++-------------- 17 files changed, 84 insertions(+), 337 deletions(-) diff --git a/src/attr_file.c b/src/attr_file.c index 89706865a..500c99bd9 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -35,11 +35,7 @@ int git_attr_file__new( return -1; } - if (git_pool_init(&attrs->pool, 1, 0) < 0) { - attr_file_free(attrs); - return -1; - } - + git_pool_init(&attrs->pool, 1); GIT_REFCOUNT_INC(attrs); attrs->entry = entry; attrs->source = source; diff --git a/src/attrcache.c b/src/attrcache.c index 5bc260460..a57110684 100644 --- a/src/attrcache.c +++ b/src/attrcache.c @@ -388,10 +388,11 @@ int git_attr_cache__do_init(git_repository *repo) * hashtable for attribute macros, and string pool */ if ((ret = git_strmap_alloc(&cache->files)) < 0 || - (ret = git_strmap_alloc(&cache->macros)) < 0 || - (ret = git_pool_init(&cache->pool, 1, 0)) < 0) + (ret = git_strmap_alloc(&cache->macros)) < 0) goto cancel; + git_pool_init(&cache->pool, 1); + cache = git__compare_and_swap(&repo->attrcache, NULL, cache); if (cache) goto cancel; /* raced with another thread, free this but no error */ diff --git a/src/checkout.c b/src/checkout.c index 632556622..d09357f2a 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1255,11 +1255,13 @@ static int checkout_get_actions( int error = 0, act; const git_index_entry *wditem; git_vector pathspec = GIT_VECTOR_INIT, *deltas; - git_pool pathpool = GIT_POOL_INIT_STRINGPOOL; + git_pool pathpool; git_diff_delta *delta; size_t i, *counts = NULL; uint32_t *actions = NULL; + git_pool_init(&pathpool, 1); + if (data->opts.paths.count > 0 && git_pathspec__vinit(&pathspec, &data->opts.paths, &pathpool) < 0) return -1; @@ -2439,10 +2441,11 @@ static int checkout_data_init( git_config_entry_free(conflict_style); } + git_pool_init(&data->pool, 1); + if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 || (error = git_vector_init(&data->remove_conflicts, 0, NULL)) < 0 || (error = git_vector_init(&data->update_conflicts, 0, NULL)) < 0 || - (error = git_pool_init(&data->pool, 1, 0)) < 0 || (error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 || (error = git_path_to_dir(&data->path)) < 0 || (error = git_strmap_alloc(&data->mkdir_map)) < 0) diff --git a/src/diff.c b/src/diff.c index d98a28966..b5e9b6cd5 100644 --- a/src/diff.c +++ b/src/diff.c @@ -430,8 +430,9 @@ static git_diff *diff_list_alloc( diff->new_src = new_iter->type; memcpy(&diff->opts, &dflt, sizeof(diff->opts)); - if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0 || - git_pool_init(&diff->pool, 1, 0) < 0) { + git_pool_init(&diff->pool, 1); + + if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0) { git_diff_free(diff); return NULL; } diff --git a/src/diff_tform.c b/src/diff_tform.c index 92647e330..7cff34159 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -134,11 +134,11 @@ int git_diff__merge( return -1; } - if (git_vector_init( - &onto_new, onto->deltas.length, git_diff_delta__cmp) < 0 || - git_pool_init(&onto_pool, 1, 0) < 0) + if (git_vector_init(&onto_new, onto->deltas.length, git_diff_delta__cmp) < 0) return -1; + git_pool_init(&onto_pool, 1); + for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) { git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i); const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j); diff --git a/src/index.c b/src/index.c index d9e713899..0adc7e157 100644 --- a/src/index.c +++ b/src/index.c @@ -439,7 +439,7 @@ int git_index_open(git_index **index_out, const char *index_path) return -1; } - git_pool_init(&index->tree_pool, 1, 0); + git_pool_init(&index->tree_pool, 1); if (index_path != NULL) { index->index_file_path = git__strdup(index_path); diff --git a/src/iterator.c b/src/iterator.c index e3a2abf66..ee348de6e 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -567,7 +567,7 @@ static bool tree_iterator__pop_frame(tree_iterator *ti, bool final) tree_iterator__move_to_next(ti, tf); if (!final) { /* if final, don't bother to clean up */ - git_pool_free_array(&ti->pool, tf->n_entries, (void **)tf->entries); + // TODO: maybe free the pool so far? git_buf_rtruncate_at_char(&ti->path, '/'); } @@ -822,8 +822,9 @@ int git_iterator_for_tree( if ((error = iterator__update_ignore_case((git_iterator *)ti, options ? options->flags : 0)) < 0) goto fail; - if ((error = git_pool_init(&ti->pool, sizeof(tree_iterator_entry),0)) < 0 || - (error = tree_iterator__create_root_frame(ti, tree)) < 0 || + git_pool_init(&ti->pool, sizeof(tree_iterator_entry)); + + if ((error = tree_iterator__create_root_frame(ti, tree)) < 0 || (error = tree_iterator__push_frame(ti)) < 0) /* expand root now */ goto fail; diff --git a/src/merge.c b/src/merge.c index 186c77037..e84b1d3e3 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1307,7 +1307,6 @@ GIT_INLINE(int) index_entry_dup_pool( { if (src != NULL) { memcpy(out, src, sizeof(git_index_entry)); - if ((out->path = git_pool_strdup(pool, src->path)) == NULL) return -1; } @@ -1442,10 +1441,11 @@ git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo) diff_list->repo = repo; + git_pool_init(&diff_list->pool, 1); + if (git_vector_init(&diff_list->staged, 0, NULL) < 0 || git_vector_init(&diff_list->conflicts, 0, NULL) < 0 || - git_vector_init(&diff_list->resolved, 0, NULL) < 0 || - git_pool_init(&diff_list->pool, 1, 0) < 0) { + git_vector_init(&diff_list->resolved, 0, NULL) < 0) { git_merge_diff_list__free(diff_list); return NULL; } diff --git a/src/pack-objects.c b/src/pack-objects.c index c4c061a3a..fd181fc5e 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -135,8 +135,7 @@ int git_packbuilder_new(git_packbuilder **out, git_repository *repo) if (!pb->walk_objects) goto on_error; - if (git_pool_init(&pb->object_pool, sizeof(git_walk_object), 0) < 0) - goto on_error; + git_pool_init(&pb->object_pool, sizeof(git_walk_object)); pb->repo = repo; pb->nr_threads = 1; /* do not spawn any thread by default */ diff --git a/src/pathspec.c b/src/pathspec.c index 9304da705..5bb69ec4b 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -237,9 +237,9 @@ int git_pathspec__init(git_pathspec *ps, const git_strarray *paths) memset(ps, 0, sizeof(*ps)); ps->prefix = git_pathspec_prefix(paths); + git_pool_init(&ps->pool, 1); - if ((error = git_pool_init(&ps->pool, 1, 0)) < 0 || - (error = git_pathspec__vinit(&ps->pathspec, paths, &ps->pool)) < 0) + if ((error = git_pathspec__vinit(&ps->pathspec, paths, &ps->pool)) < 0) git_pathspec__clear(ps); return error; @@ -312,15 +312,11 @@ static git_pathspec_match_list *pathspec_match_alloc( git_pathspec *ps, int datatype) { git_pathspec_match_list *m = git__calloc(1, sizeof(git_pathspec_match_list)); - - if (m != NULL && git_pool_init(&m->pool, 1, 0) < 0) { - pathspec_match_free(m); - m = NULL; - } - if (!m) return NULL; + git_pool_init(&m->pool, 1); + /* need to keep reference to pathspec and increment refcount because * failures array stores pointers to the pattern strings of the * pathspec that had no matches diff --git a/src/pool.c b/src/pool.c index c93d78182..78277eb40 100644 --- a/src/pool.c +++ b/src/pool.c @@ -8,67 +8,49 @@ struct git_pool_page { git_pool_page *next; uint32_t size; uint32_t avail; - GIT_ALIGN(char data[GIT_FLEX_ARRAY], 8); + char data[GIT_FLEX_ARRAY]; }; -struct pool_freelist { - struct pool_freelist *next; -}; - -#define GIT_POOL_MIN_USABLE 4 -#define GIT_POOL_MIN_PAGESZ 2 * sizeof(void*) - static void *pool_alloc_page(git_pool *pool, uint32_t size); -static void pool_insert_page(git_pool *pool, git_pool_page *page); -int git_pool_init( - git_pool *pool, uint32_t item_size, uint32_t items_per_page) +uint32_t git_pool__system_page_size(void) { + static uint32_t size = 0; + + if (!size) { + size_t page_size; + if (git__page_size(&page_size) < 0) + page_size = 4096; + size = page_size - 2 * sizeof(void *); /* allow space for malloc overhead */ + } + + return size; +} + +void git_pool_init(git_pool *pool, uint32_t item_size) +{ + const uint32_t align_size = sizeof(void *) - 1; assert(pool); - if (!item_size) - item_size = 1; - /* round up item_size for decent object alignment */ - if (item_size > 4) - item_size = (item_size + 7) & ~7; - else if (item_size == 3) - item_size = 4; - - if (!items_per_page) - items_per_page = git_pool__suggest_items_per_page(item_size); - if (item_size * items_per_page < GIT_POOL_MIN_PAGESZ) - items_per_page = (GIT_POOL_MIN_PAGESZ + item_size - 1) / item_size; + if (item_size > 1) + item_size = (item_size + align_size) & ~align_size; memset(pool, 0, sizeof(git_pool)); pool->item_size = item_size; - pool->page_size = item_size * items_per_page; - - return 0; + pool->page_size = git_pool__system_page_size(); } void git_pool_clear(git_pool *pool) { git_pool_page *scan, *next; - for (scan = pool->open; scan != NULL; scan = next) { + for (scan = pool->pages; scan != NULL; scan = next) { next = scan->next; git__free(scan); } - pool->open = NULL; - - for (scan = pool->full; scan != NULL; scan = next) { - next = scan->next; - git__free(scan); - } - pool->full = NULL; - - pool->free_list = NULL; + pool->pages = NULL; pool->items = 0; - - pool->has_string_alloc = 0; - pool->has_multi_item_alloc = 0; - pool->has_large_page_alloc = 0; } void git_pool_swap(git_pool *a, git_pool *b) @@ -83,110 +65,40 @@ void git_pool_swap(git_pool *a, git_pool *b) memcpy(b, &temp, sizeof(temp)); } -static void pool_insert_page(git_pool *pool, git_pool_page *page) -{ - git_pool_page *scan; - - /* If there are no open pages or this page has the most open space, - * insert it at the beginning of the list. This is the common case. - */ - if (pool->open == NULL || pool->open->avail < page->avail) { - page->next = pool->open; - pool->open = page; - return; - } - - /* Otherwise insert into sorted position. */ - for (scan = pool->open; - scan->next && scan->next->avail > page->avail; - scan = scan->next); - page->next = scan->next; - scan->next = page; -} - static void *pool_alloc_page(git_pool *pool, uint32_t size) { git_pool_page *page; - uint32_t new_page_size; + const uint32_t new_page_size = (size <= pool->page_size) ? pool->page_size : size; size_t alloc_size; - if (size <= pool->page_size) - new_page_size = pool->page_size; - else { - new_page_size = size; - pool->has_large_page_alloc = 1; - } - if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, new_page_size, sizeof(git_pool_page)) || !(page = git__calloc(1, alloc_size))) return NULL; - page->size = new_page_size; + page->size = new_page_size; page->avail = new_page_size - size; + page->next = pool->pages; - if (page->avail > 0) - pool_insert_page(pool, page); - else { - page->next = pool->full; - pool->full = page; - } - + pool->pages = page; pool->items++; return page->data; } -GIT_INLINE(void) pool_remove_page( - git_pool *pool, git_pool_page *page, git_pool_page *prev) -{ - if (prev == NULL) - pool->open = page->next; - else - prev->next = page->next; -} - void *git_pool_malloc(git_pool *pool, uint32_t items) { - git_pool_page *scan = pool->open, *prev; - uint32_t size = ((items * pool->item_size) + 7) & ~7; + const uint32_t size = items * pool->item_size; + + git_pool_page *page = pool->pages; void *ptr = NULL; - pool->has_string_alloc = 0; - if (items > 1) - pool->has_multi_item_alloc = 1; - else if (pool->free_list != NULL) { - ptr = pool->free_list; - pool->free_list = ((struct pool_freelist *)pool->free_list)->next; - return ptr; - } - - /* just add a block if there is no open one to accommodate this */ - if (size >= pool->page_size || !scan || scan->avail < size) + if (!page || page->avail < size) return pool_alloc_page(pool, size); + ptr = &page->data[page->size - page->avail]; + page->avail -= size; pool->items++; - /* find smallest block in free list with space */ - for (scan = pool->open, prev = NULL; - scan->next && scan->next->avail >= size; - prev = scan, scan = scan->next); - - /* allocate space from the block */ - ptr = &scan->data[scan->size - scan->avail]; - scan->avail -= size; - - /* move to full list if there is almost no space left */ - if (scan->avail < pool->item_size || scan->avail < GIT_POOL_MIN_USABLE) { - pool_remove_page(pool, scan, prev); - scan->next = pool->full; - pool->full = scan; - } - /* reorder list if block is now smaller than the one after it */ - else if (scan->next != NULL && scan->next->avail > scan->avail) { - pool_remove_page(pool, scan, prev); - pool_insert_page(pool, scan); - } - return ptr; } @@ -204,15 +116,12 @@ char *git_pool_strndup(git_pool *pool, const char *str, size_t n) ptr[n] = '\0'; } - pool->has_string_alloc = 1; - return ptr; } char *git_pool_strdup(git_pool *pool, const char *str) { assert(pool && str && pool->item_size == sizeof(char)); - return git_pool_strndup(pool, str, strlen(str)); } @@ -238,88 +147,23 @@ char *git_pool_strcat(git_pool *pool, const char *a, const char *b) memcpy(((char *)ptr) + len_a, b, len_b); *(((char *)ptr) + len_a + len_b) = '\0'; } - pool->has_string_alloc = 1; - return ptr; } -void git_pool_free(git_pool *pool, void *ptr) -{ - struct pool_freelist *item = ptr; - - assert(pool && pool->item_size >= sizeof(void*)); - - if (item) { - item->next = pool->free_list; - pool->free_list = item; - } -} - -void git_pool_free_array(git_pool *pool, size_t count, void **ptrs) -{ - struct pool_freelist **items = (struct pool_freelist **)ptrs; - size_t i; - - assert(pool && ptrs && pool->item_size >= sizeof(void*)); - - if (!count) - return; - - for (i = count - 1; i > 0; --i) - items[i]->next = items[i - 1]; - - items[i]->next = pool->free_list; - pool->free_list = items[count - 1]; -} - uint32_t git_pool__open_pages(git_pool *pool) { uint32_t ct = 0; git_pool_page *scan; - for (scan = pool->open; scan != NULL; scan = scan->next) ct++; - return ct; -} - -uint32_t git_pool__full_pages(git_pool *pool) -{ - uint32_t ct = 0; - git_pool_page *scan; - for (scan = pool->full; scan != NULL; scan = scan->next) ct++; + for (scan = pool->pages; scan != NULL; scan = scan->next) ct++; return ct; } bool git_pool__ptr_in_pool(git_pool *pool, void *ptr) { git_pool_page *scan; - for (scan = pool->open; scan != NULL; scan = scan->next) - if ((void *)scan->data <= ptr && - (void *)(((char *)scan->data) + scan->size) > ptr) - return true; - for (scan = pool->full; scan != NULL; scan = scan->next) + for (scan = pool->pages; scan != NULL; scan = scan->next) if ((void *)scan->data <= ptr && (void *)(((char *)scan->data) + scan->size) > ptr) return true; return false; } - -uint32_t git_pool__system_page_size(void) -{ - static uint32_t size = 0; - - if (!size) { - size_t page_size; - if (git__page_size(&page_size) < 0) - page_size = 4096; - size = page_size - 2 * sizeof(void *); /* allow space for malloc overhead */ - } - - return size; -} - -uint32_t git_pool__suggest_items_per_page(uint32_t item_size) -{ - uint32_t page_bytes = - git_pool__system_page_size() - sizeof(git_pool_page); - return page_bytes / item_size; -} - diff --git a/src/pool.h b/src/pool.h index b0007f315..ef71d4eb6 100644 --- a/src/pool.h +++ b/src/pool.h @@ -28,19 +28,12 @@ typedef struct git_pool_page git_pool_page; * For examples of how to set up a `git_pool` see `git_pool_init`. */ typedef struct { - git_pool_page *open; /* pages with space left */ - git_pool_page *full; /* pages with no space left */ - void *free_list; /* optional: list of freed blocks */ + git_pool_page *pages; /* pages with space left */ uint32_t item_size; /* size of single alloc unit in bytes */ uint32_t page_size; /* size of page in bytes */ uint32_t items; - unsigned has_string_alloc : 1; /* was the strdup function used */ - unsigned has_multi_item_alloc : 1; /* was items ever > 1 in malloc */ - unsigned has_large_page_alloc : 1; /* are any pages > page_size */ } git_pool; -#define GIT_POOL_INIT_STRINGPOOL { 0, 0, 0, 1, 4000, 0, 0, 0, 0 } - /** * Initialize a pool. * @@ -57,8 +50,7 @@ typedef struct { * Of course, you can use this in other ways, but those are the * two most common patterns. */ -extern int git_pool_init( - git_pool *pool, uint32_t item_size, uint32_t items_per_page); +extern void git_pool_init(git_pool *pool, uint32_t item_size); /** * Free all items in pool @@ -114,35 +106,10 @@ extern char *git_pool_strdup_safe(git_pool *pool, const char *str); */ extern char *git_pool_strcat(git_pool *pool, const char *a, const char *b); -/** - * Push a block back onto the free list for the pool. - * - * This is allowed only if the item_size is >= sizeof(void*). - * - * In some cases, it is helpful to "release" an allocated block - * for reuse. Pools don't support a general purpose free, but - * they will keep a simple free blocks linked list provided the - * native block size is large enough to hold a void pointer - */ -extern void git_pool_free(git_pool *pool, void *ptr); - -/** - * Push an array of pool allocated blocks efficiently onto the free list. - * - * This has the same constraints as `git_pool_free()` above. - */ -extern void git_pool_free_array(git_pool *pool, size_t count, void **ptrs); - /* * Misc utilities */ - extern uint32_t git_pool__open_pages(git_pool *pool); - -extern uint32_t git_pool__full_pages(git_pool *pool); - extern bool git_pool__ptr_in_pool(git_pool *pool, void *ptr); -extern uint32_t git_pool__suggest_items_per_page(uint32_t item_size); - #endif diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 921f7862b..af96821df 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -626,8 +626,9 @@ static int refdb_fs_backend__iterator( iter = git__calloc(1, sizeof(refdb_fs_iter)); GITERR_CHECK_ALLOC(iter); - if (git_pool_init(&iter->pool, 1, 0) < 0 || - git_vector_init(&iter->loose, 8, NULL) < 0) + git_pool_init(&iter->pool, 1); + + if (git_vector_init(&iter->loose, 8, NULL) < 0) goto fail; if (glob != NULL && diff --git a/src/revwalk.c b/src/revwalk.c index dcdd97915..89279ed1f 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -535,12 +535,10 @@ int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo) walk->commits = git_oidmap_alloc(); GITERR_CHECK_ALLOC(walk->commits); - if (git_pqueue_init( - &walk->iterator_time, 0, 8, git_commit_list_time_cmp) < 0 || - git_pool_init(&walk->commit_pool, 1, - git_pool__suggest_items_per_page(COMMIT_ALLOC) * COMMIT_ALLOC) < 0) + if (git_pqueue_init(&walk->iterator_time, 0, 8, git_commit_list_time_cmp) < 0) return -1; + git_pool_init(&walk->commit_pool, COMMIT_ALLOC); walk->get_next = &revwalk_next_unsorted; walk->enqueue = &revwalk_enqueue_unsorted; diff --git a/src/sortedcache.c b/src/sortedcache.c index 115175724..5c2a167a7 100644 --- a/src/sortedcache.c +++ b/src/sortedcache.c @@ -20,8 +20,9 @@ int git_sortedcache_new( sc = git__calloc(1, alloclen); GITERR_CHECK_ALLOC(sc); - if (git_pool_init(&sc->pool, 1, 0) < 0 || - git_vector_init(&sc->items, 4, item_cmp) < 0 || + git_pool_init(&sc->pool, 1); + + if (git_vector_init(&sc->items, 4, item_cmp) < 0 || git_strmap_alloc(&sc->map) < 0) goto fail; diff --git a/src/transaction.c b/src/transaction.c index 92e134e5b..2c8a1e8bd 100644 --- a/src/transaction.c +++ b/src/transaction.c @@ -77,8 +77,7 @@ int git_transaction_new(git_transaction **out, git_repository *repo) assert(out && repo); - if ((error = git_pool_init(&pool, 1, 0)) < 0) - return error; + git_pool_init(&pool, 1); tx = git_pool_mallocz(&pool, sizeof(git_transaction)); if (!tx) { diff --git a/tests/core/pool.c b/tests/core/pool.c index a7ec8801b..2d42b930a 100644 --- a/tests/core/pool.c +++ b/tests/core/pool.c @@ -8,7 +8,7 @@ void test_core_pool__0(void) git_pool p; void *ptr; - cl_git_pass(git_pool_init(&p, 1, 4000)); + git_pool_init(&p, 1); for (i = 1; i < 10000; i *= 2) { ptr = git_pool_malloc(&p, i); @@ -17,13 +17,6 @@ void test_core_pool__0(void) cl_assert(!git_pool__ptr_in_pool(&p, &i)); } - /* 1+2+4+8+16+32+64+128+256+512+1024 -> original block */ - /* 2048 -> 1 block */ - /* 4096 -> 1 block */ - /* 8192 -> 1 block */ - - cl_assert(git_pool__open_pages(&p) + git_pool__full_pages(&p) == 4); - git_pool_clear(&p); } @@ -32,26 +25,24 @@ void test_core_pool__1(void) int i; git_pool p; - cl_git_pass(git_pool_init(&p, 1, 4000)); + git_pool_init(&p, 1); + p.page_size = 4000; for (i = 2010; i > 0; i--) cl_assert(git_pool_malloc(&p, i) != NULL); /* with fixed page size, allocation must end up with these values */ - cl_assert_equal_i(1, git_pool__open_pages(&p)); - cl_assert_equal_i(507, git_pool__full_pages(&p)); - + cl_assert_equal_i(590, git_pool__open_pages(&p)); git_pool_clear(&p); - cl_git_pass(git_pool_init(&p, 1, 4120)); + git_pool_init(&p, 1); + p.page_size = 4120; for (i = 2010; i > 0; i--) cl_assert(git_pool_malloc(&p, i) != NULL); /* with fixed page size, allocation must end up with these values */ - cl_assert_equal_i(1, git_pool__open_pages(&p)); - cl_assert_equal_i(492, git_pool__full_pages(&p)); - + cl_assert_equal_i(573, git_pool__open_pages(&p)); git_pool_clear(&p); } @@ -66,7 +57,8 @@ void test_core_pool__2(void) memset(oid_hex, '0', sizeof(oid_hex)); - cl_git_pass(git_pool_init(&p, sizeof(git_oid), 100)); + git_pool_init(&p, sizeof(git_oid)); + p.page_size = 4000; for (i = 1000; i < 10000; i++) { oid = git_pool_malloc(&p, 1); @@ -78,59 +70,7 @@ void test_core_pool__2(void) } /* with fixed page size, allocation must end up with these values */ - cl_assert(git_pool__open_pages(&p) == 0); - cl_assert(git_pool__full_pages(&p) == 90); - - git_pool_clear(&p); -} - -void test_core_pool__free_list(void) -{ - int i; - git_pool p; - void *ptr, *ptrs[50]; - - cl_git_pass(git_pool_init(&p, 100, 100)); - - for (i = 0; i < 10; ++i) { - ptr = git_pool_malloc(&p, 1); - cl_assert(ptr != NULL); - } - cl_assert_equal_i(10, (int)p.items); - - for (i = 0; i < 50; ++i) { - ptrs[i] = git_pool_malloc(&p, 1); - cl_assert(ptrs[i] != NULL); - } - cl_assert_equal_i(60, (int)p.items); - - git_pool_free(&p, ptr); - cl_assert_equal_i(60, (int)p.items); - - git_pool_free_array(&p, 50, ptrs); - cl_assert_equal_i(60, (int)p.items); - - for (i = 0; i < 50; ++i) { - ptrs[i] = git_pool_malloc(&p, 1); - cl_assert(ptrs[i] != NULL); - } - cl_assert_equal_i(60, (int)p.items); - - for (i = 0; i < 111; ++i) { - ptr = git_pool_malloc(&p, 1); - cl_assert(ptr != NULL); - } - cl_assert_equal_i(170, (int)p.items); - - git_pool_free_array(&p, 50, ptrs); - cl_assert_equal_i(170, (int)p.items); - - for (i = 0; i < 50; ++i) { - ptrs[i] = git_pool_malloc(&p, 1); - cl_assert(ptrs[i] != NULL); - } - cl_assert_equal_i(170, (int)p.items); - + cl_assert_equal_i(55, git_pool__open_pages(&p)); git_pool_clear(&p); } @@ -138,7 +78,7 @@ void test_core_pool__strndup_limit(void) { git_pool p; - cl_git_pass(git_pool_init(&p, 1, 100)); + git_pool_init(&p, 1); /* ensure 64 bit doesn't overflow */ cl_assert(git_pool_strndup(&p, "foo", (size_t)-1) == NULL); git_pool_clear(&p); From 4f971852d5b06d61e7a8782edd76bea876e79ed8 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 28 Oct 2015 10:15:24 +0100 Subject: [PATCH 212/450] repository: plug memory leak cc @carlosmn --- src/repository.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/repository.c b/src/repository.c index 38d18693a..c61d0e4f0 100644 --- a/src/repository.c +++ b/src/repository.c @@ -689,6 +689,7 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo) git_buf_free(&global_buf); git_buf_free(&xdg_buf); git_buf_free(&system_buf); + git_buf_free(&programdata_buf); } *out = repo->_config; From 410efda80b101d427aafcc151b44c8cf0197461d Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 28 Oct 2015 10:28:43 +0100 Subject: [PATCH 213/450] pool: Take into account malloc overhead & pool page size --- src/pool.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pool.c b/src/pool.c index 78277eb40..fe6c42987 100644 --- a/src/pool.c +++ b/src/pool.c @@ -21,7 +21,8 @@ uint32_t git_pool__system_page_size(void) size_t page_size; if (git__page_size(&page_size) < 0) page_size = 4096; - size = page_size - 2 * sizeof(void *); /* allow space for malloc overhead */ + /* allow space for malloc overhead */ + size = page_size - (2 * sizeof(void *)) - sizeof(git_pool_page); } return size; From 66eb7660a87a1fe56fde21a7e544e34224a3a257 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 28 Oct 2015 10:29:00 +0100 Subject: [PATCH 214/450] pool: Handle 32 bit systems --- tests/core/pool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pool.c b/tests/core/pool.c index 2d42b930a..f90adfbc3 100644 --- a/tests/core/pool.c +++ b/tests/core/pool.c @@ -70,7 +70,7 @@ void test_core_pool__2(void) } /* with fixed page size, allocation must end up with these values */ - cl_assert_equal_i(55, git_pool__open_pages(&p)); + cl_assert_equal_i(sizeof(void *) == 8 ? 55 : 45, git_pool__open_pages(&p)); git_pool_clear(&p); } From d3416dfe29e59ba7de7bf49e060bf13c07960344 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 28 Oct 2015 10:50:25 +0100 Subject: [PATCH 215/450] pool: Dot not assume mallocs are zeroed out --- src/commit_list.c | 2 +- src/merge.c | 8 ++++---- src/pool.c | 24 +++++++++++++++++------- src/pool.h | 13 +------------ 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/commit_list.c b/src/commit_list.c index 53612d514..28948c88b 100644 --- a/src/commit_list.c +++ b/src/commit_list.c @@ -47,7 +47,7 @@ git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_ git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk) { - return (git_commit_list_node *)git_pool_malloc(&walk->commit_pool, COMMIT_ALLOC); + return (git_commit_list_node *)git_pool_mallocz(&walk->commit_pool, 1); } static int commit_error(git_commit_list_node *commit, const char *msg) diff --git a/src/merge.c b/src/merge.c index e84b1d3e3..a124cbdc9 100644 --- a/src/merge.c +++ b/src/merge.c @@ -626,7 +626,7 @@ static int merge_conflict_resolve_one_renamed( git_oid__cmp(&conflict->our_entry.id, &conflict->their_entry.id) != 0) return 0; - if ((merged = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL) + if ((merged = git_pool_mallocz(&diff_list->pool, sizeof(git_index_entry))) == NULL) return -1; if (ours_changed) @@ -711,7 +711,7 @@ static int merge_conflict_resolve_automerge( (error = git_odb_write(&automerge_oid, odb, result.ptr, result.len, GIT_OBJ_BLOB)) < 0) goto done; - if ((index_entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL) + if ((index_entry = git_pool_mallocz(&diff_list->pool, sizeof(git_index_entry))) == NULL) GITERR_CHECK_ALLOC(index_entry); index_entry->path = git_pool_strdup(&diff_list->pool, result.path); @@ -1342,7 +1342,7 @@ static git_merge_diff *merge_diff_from_index_entries( git_merge_diff *conflict; git_pool *pool = &diff_list->pool; - if ((conflict = git_pool_malloc(pool, sizeof(git_merge_diff))) == NULL) + if ((conflict = git_pool_mallocz(pool, sizeof(git_merge_diff))) == NULL) return NULL; if (index_entry_dup_pool(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 || @@ -1383,7 +1383,7 @@ static int merge_diff_list_insert_unmodified( int error = 0; git_index_entry *entry; - entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry)); + entry = git_pool_mallocz(&diff_list->pool, sizeof(git_index_entry)); GITERR_CHECK_ALLOC(entry); if ((error = index_entry_dup_pool(entry, &diff_list->pool, tree_items[0])) >= 0) diff --git a/src/pool.c b/src/pool.c index fe6c42987..aab63beee 100644 --- a/src/pool.c +++ b/src/pool.c @@ -51,7 +51,6 @@ void git_pool_clear(git_pool *pool) } pool->pages = NULL; - pool->items = 0; } void git_pool_swap(git_pool *a, git_pool *b) @@ -73,7 +72,7 @@ static void *pool_alloc_page(git_pool *pool, uint32_t size) size_t alloc_size; if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, new_page_size, sizeof(git_pool_page)) || - !(page = git__calloc(1, alloc_size))) + !(page = git__malloc(alloc_size))) return NULL; page->size = new_page_size; @@ -81,15 +80,12 @@ static void *pool_alloc_page(git_pool *pool, uint32_t size) page->next = pool->pages; pool->pages = page; - pool->items++; return page->data; } -void *git_pool_malloc(git_pool *pool, uint32_t items) +static void *pool_alloc(git_pool *pool, uint32_t size) { - const uint32_t size = items * pool->item_size; - git_pool_page *page = pool->pages; void *ptr = NULL; @@ -98,11 +94,25 @@ void *git_pool_malloc(git_pool *pool, uint32_t items) ptr = &page->data[page->size - page->avail]; page->avail -= size; - pool->items++; return ptr; } +void *git_pool_malloc(git_pool *pool, uint32_t items) +{ + const uint32_t size = items * pool->item_size; + return pool_alloc(pool, size); +} + +void *git_pool_mallocz(git_pool *pool, uint32_t items) +{ + const uint32_t size = items * pool->item_size; + void *ptr = pool_alloc(pool, size); + if (ptr) + memset(ptr, 0x0, size); + return ptr; +} + char *git_pool_strndup(git_pool *pool, const char *str, size_t n) { char *ptr = NULL; diff --git a/src/pool.h b/src/pool.h index ef71d4eb6..f9ac86773 100644 --- a/src/pool.h +++ b/src/pool.h @@ -31,7 +31,6 @@ typedef struct { git_pool_page *pages; /* pages with space left */ uint32_t item_size; /* size of single alloc unit in bytes */ uint32_t page_size; /* size of page in bytes */ - uint32_t items; } git_pool; /** @@ -66,17 +65,7 @@ extern void git_pool_swap(git_pool *a, git_pool *b); * Allocate space for one or more items from a pool. */ extern void *git_pool_malloc(git_pool *pool, uint32_t items); - -/** - * Allocate space and zero it out. - */ -GIT_INLINE(void *) git_pool_mallocz(git_pool *pool, uint32_t items) -{ - void *ptr = git_pool_malloc(pool, items); - if (ptr) - memset(ptr, 0, (size_t)items * (size_t)pool->item_size); - return ptr; -} +extern void *git_pool_mallocz(git_pool *pool, uint32_t items); /** * Allocate space and duplicate string data into it. From 340b15b71cca16b47db2e5c882952497efece14f Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 28 Oct 2015 14:31:09 +0100 Subject: [PATCH 216/450] pool: update comment --- src/pool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pool.h b/src/pool.h index f9ac86773..4f1e6f304 100644 --- a/src/pool.h +++ b/src/pool.h @@ -28,7 +28,7 @@ typedef struct git_pool_page git_pool_page; * For examples of how to set up a `git_pool` see `git_pool_init`. */ typedef struct { - git_pool_page *pages; /* pages with space left */ + git_pool_page *pages; /* allocated pages */ uint32_t item_size; /* size of single alloc unit in bytes */ uint32_t page_size; /* size of page in bytes */ } git_pool; From d845abe6394afafc88db637f02888d1341f20559 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 28 Oct 2015 14:49:28 +0100 Subject: [PATCH 217/450] merge: Do not mallocz unecessary entries --- src/merge.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/merge.c b/src/merge.c index a124cbdc9..ac0caeabd 100644 --- a/src/merge.c +++ b/src/merge.c @@ -626,7 +626,7 @@ static int merge_conflict_resolve_one_renamed( git_oid__cmp(&conflict->our_entry.id, &conflict->their_entry.id) != 0) return 0; - if ((merged = git_pool_mallocz(&diff_list->pool, sizeof(git_index_entry))) == NULL) + if ((merged = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL) return -1; if (ours_changed) @@ -1383,7 +1383,7 @@ static int merge_diff_list_insert_unmodified( int error = 0; git_index_entry *entry; - entry = git_pool_mallocz(&diff_list->pool, sizeof(git_index_entry)); + entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry)); GITERR_CHECK_ALLOC(entry); if ((error = index_entry_dup_pool(entry, &diff_list->pool, tree_items[0])) >= 0) From 1b4449b40d1a69026311a45d2e3534b1d2cb66a9 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 28 Oct 2015 10:53:03 -0400 Subject: [PATCH 218/450] pool: fix documentation --- src/pool.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pool.h b/src/pool.h index 4f1e6f304..d16bd349a 100644 --- a/src/pool.h +++ b/src/pool.h @@ -38,12 +38,12 @@ typedef struct { * * To allocation strings, use like this: * - * git_pool_init(&string_pool, 1, 0); + * git_pool_init(&string_pool, 1); * my_string = git_pool_strdup(&string_pool, your_string); * * To allocate items of fixed size, use like this: * - * git_pool_init(&pool, sizeof(item), 0); + * git_pool_init(&pool, sizeof(item)); * my_item = git_pool_malloc(&pool, 1); * * Of course, you can use this in other ways, but those are the From 335c9e2f45b75e9fe8d8256e6ecfdb3d333fcbcc Mon Sep 17 00:00:00 2001 From: Stjepan Rajko Date: Mon, 26 Oct 2015 15:33:00 -0700 Subject: [PATCH 219/450] Prevent segfault when parsing a reflog with oid parse error Using calloc instead of malloc because the parse error will lead to an immediate free of committer (and its properties, which can segfault on free if undefined - test_refs_reflog_reflog__reading_a_reflog_with_invalid_format_returns_error segfaulted before the fix). #3458 --- src/refdb_fs.c | 2 +- tests/refs/reflog/reflog.c | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 921f7862b..9f87b30d4 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -1463,7 +1463,7 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size) entry = git__calloc(1, sizeof(git_reflog_entry)); GITERR_CHECK_ALLOC(entry); - entry->committer = git__malloc(sizeof(git_signature)); + entry->committer = git__calloc(1, sizeof(git_signature)); GITERR_CHECK_ALLOC(entry->committer); if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0) diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c index 56ec422c3..3fbf412e4 100644 --- a/tests/refs/reflog/reflog.c +++ b/tests/refs/reflog/reflog.c @@ -154,6 +154,49 @@ void test_refs_reflog_reflog__reading_the_reflog_from_a_reference_with_no_log_re git_buf_free(&subtrees_log_path); } +void test_refs_reflog_reflog__reading_a_reflog_with_invalid_format_returns_error(void) +{ + git_reflog *reflog; + const git_error *error; + const char *refname = "refs/heads/newline"; + const char *refmessage = + "Reflog*message with a newline and enough content after it to pass the GIT_REFLOG_SIZE_MIN check inside reflog_parse."; + git_reference *ref; + git_oid id; + git_buf logpath = GIT_BUF_INIT, logcontents = GIT_BUF_INIT; + char *star; + + git_oid_fromstr(&id, current_master_tip); + + /* create a new branch */ + cl_git_pass(git_reference_create(&ref, g_repo, refname, &id, 1, refmessage)); + + /* corrupt the branch reflog by introducing a newline inside the reflog message (we replace '*' with '\n') */ + git_buf_join_n(&logpath, '/', 3, git_repository_path(g_repo), GIT_REFLOG_DIR, refname); + cl_git_pass(git_futils_readbuffer(&logcontents, git_buf_cstr(&logpath))); + cl_assert((star = strchr(git_buf_cstr(&logcontents), '*')) != NULL); + *star = '\n'; + cl_git_rewritefile(git_buf_cstr(&logpath), git_buf_cstr(&logcontents)); + + /* confirm that the file was rewritten successfully and now contains a '\n' in the expected location */ + cl_git_pass(git_futils_readbuffer(&logcontents, git_buf_cstr(&logpath))); + cl_assert(strstr(git_buf_cstr(&logcontents), "Reflog\nmessage") != NULL); + + /* clear the error state so we can capture the error generated by git_reflog_read */ + giterr_clear(); + + cl_git_fail(git_reflog_read(&reflog, g_repo, refname)); + + error = giterr_last(); + + cl_assert(error != NULL); + cl_assert_equal_s("Unable to parse OID - contains invalid characters", error->message); + + git_reference_free(ref); + git_buf_free(&logpath); + git_buf_free(&logcontents); +} + void test_refs_reflog_reflog__cannot_write_a_moved_reflog(void) { git_reference *master, *new_master; From 45d295e08cc7bd8b2dc27ed88d067eff6991d5c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 30 Oct 2015 17:51:50 +0100 Subject: [PATCH 220/450] git: accept NULL as argument to its stream free --- src/transports/git.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transports/git.c b/src/transports/git.c index 52de92d09..3e7e5a04f 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -132,9 +132,9 @@ static void git_proto_stream_free(git_smart_subtransport_stream *stream) { git_proto_stream *s = (git_proto_stream *)stream; git_subtransport *t = OWNING_SUBTRANSPORT(s); - int ret; - GIT_UNUSED(ret); + if (!stream) + return; t->current_stream = NULL; From 0bf77e3283cfbb2d27f8824b5922df0b3ebd2529 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 30 Oct 2015 13:07:02 -0400 Subject: [PATCH 221/450] index: read_index must update hashes --- src/index.c | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/index.c b/src/index.c index 0adc7e157..dcf46fe50 100644 --- a/src/index.c +++ b/src/index.c @@ -2899,6 +2899,7 @@ int git_index_read_index( { git_vector new_entries = GIT_VECTOR_INIT, remove_entries = GIT_VECTOR_INIT; + git_idxmap *new_entries_map = NULL; git_iterator *index_iterator = NULL; git_iterator *new_iterator = NULL; git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT; @@ -2908,9 +2909,15 @@ int git_index_read_index( int error; if ((error = git_vector_init(&new_entries, new_index->entries.length, index->entries._cmp)) < 0 || - (error = git_vector_init(&remove_entries, index->entries.length, NULL)) < 0) + (error = git_vector_init(&remove_entries, index->entries.length, NULL)) < 0 || + (error = git_idxmap_alloc(&new_entries_map)) < 0) goto done; + if (index->ignore_case) + kh_resize(idxicase, (khash_t(idxicase) *) new_entries_map, new_index->entries.length); + else + kh_resize(idx, new_entries_map, new_index->entries.length); + opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; if ((error = git_iterator_for_index(&index_iterator, index, &opts)) < 0 || @@ -2924,6 +2931,7 @@ int git_index_read_index( goto done; while (true) { + git_index_entry *add_entry = NULL, *remove_entry = NULL; int diff; if (old_entry && new_entry) @@ -2936,27 +2944,37 @@ int git_index_read_index( break; if (diff < 0) { - git_vector_insert(&remove_entries, (git_index_entry *)old_entry); + remove_entry = (git_index_entry *)old_entry; } else if (diff > 0) { - if ((error = index_entry_dup(&entry, index, new_entry)) < 0) + if ((error = index_entry_dup(&add_entry, index, new_entry)) < 0) goto done; - - git_vector_insert(&new_entries, entry); } else { /* Path and stage are equal, if the OID is equal, keep it to * keep the stat cache data. */ if (git_oid_equal(&old_entry->id, &new_entry->id)) { - git_vector_insert(&new_entries, (git_index_entry *)old_entry); + add_entry = (git_index_entry *)old_entry; } else { - if ((error = index_entry_dup(&entry, index, new_entry)) < 0) + if ((error = index_entry_dup(&add_entry, index, new_entry)) < 0) goto done; - git_vector_insert(&new_entries, entry); - git_vector_insert(&remove_entries, (git_index_entry *)old_entry); + remove_entry = (git_index_entry *)old_entry; } } + if (add_entry) { + if ((error = git_vector_insert(&new_entries, add_entry)) == 0) + INSERT_IN_MAP_EX(index, new_entries_map, add_entry, error); + } + + if (remove_entry && !error) + error = git_vector_insert(&remove_entries, remove_entry); + + if (error < 0) { + giterr_set(GITERR_INDEX, "failed to insert entry"); + return error; + } + if (diff <= 0) { if ((error = git_iterator_advance(&old_entry, index_iterator)) < 0 && error != GIT_ITEROVER) @@ -2974,6 +2992,7 @@ int git_index_read_index( git_index_reuc_clear(index); git_vector_swap(&new_entries, &index->entries); + new_entries_map = git__swap(index->entries_map, new_entries_map); git_vector_foreach(&remove_entries, i, entry) { if (index->tree) @@ -2985,6 +3004,7 @@ int git_index_read_index( error = 0; done: + git_idxmap_free(new_entries_map); git_vector_free(&new_entries); git_vector_free(&remove_entries); git_iterator_free(index_iterator); From e0be1d605f6a219b07bc3eaa8541318074c986b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 30 Oct 2015 18:23:17 +0100 Subject: [PATCH 222/450] git: put NULL check at the top --- src/transports/git.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/transports/git.c b/src/transports/git.c index 3e7e5a04f..6c6acf9c5 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -130,12 +130,15 @@ static int git_proto_stream_write( static void git_proto_stream_free(git_smart_subtransport_stream *stream) { - git_proto_stream *s = (git_proto_stream *)stream; - git_subtransport *t = OWNING_SUBTRANSPORT(s); + git_proto_stream *s; + git_subtransport *t; if (!stream) return; + s = (git_proto_stream *)stream; + t = OWNING_SUBTRANSPORT(s); + t->current_stream = NULL; git_stream_close(s->io); From bf28da47186b4f121e7bea026055b10105aaf994 Mon Sep 17 00:00:00 2001 From: Leo Yang Date: Fri, 30 Oct 2015 14:12:19 -0400 Subject: [PATCH 223/450] Fix build for custom transport users We should explicitly include the declaration of git_strarray from "include/git2/sys/transport.h" --- include/git2/sys/transport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h index ca8617f3f..ce0234a18 100644 --- a/include/git2/sys/transport.h +++ b/include/git2/sys/transport.h @@ -10,6 +10,7 @@ #include "git2/net.h" #include "git2/types.h" +#include "git2/strarray.h" /** * @file git2/sys/transport.h From a2f96479abfe17357f666a80d9d0163dd8014fa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 29 Oct 2015 20:31:25 +0100 Subject: [PATCH 224/450] config: add failing test for an external modification We currently use the timestamp in order to decide whether a config file has changed since we last read it. This scheme falls down if the file is written twice within the same second, as we fail to detect the file change after the first read in that second. --- tests/config/stress.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/config/stress.c b/tests/config/stress.c index 503f44f03..6e960425c 100644 --- a/tests/config/stress.c +++ b/tests/config/stress.c @@ -107,3 +107,23 @@ void test_config_stress__complex(void) git_config_free(config); } + +void test_config_stress__quick_write(void) +{ + git_config *config_w, *config_r; + const char *path = "./config-quick-write"; + const char *key = "quick.write"; + int32_t i; + + /* Create an external writer for one instance with the other one */ + cl_git_pass(git_config_open_ondisk(&config_w, path)); + cl_git_pass(git_config_open_ondisk(&config_r, path)); + + /* Write and read in the same second (repeat to increase the chance of it happening) */ + for (i = 0; i < 10; i++) { + int32_t val; + cl_git_pass(git_config_set_int32(config_w, key, i)); + cl_git_pass(git_config_get_int32(&val, config_r, key)); + cl_assert_equal_i(i, val); + } +} From eb5977991a75f8d1630348a529805ba40765a616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 29 Oct 2015 21:12:37 +0100 Subject: [PATCH 225/450] filebuf: use a checksum to detect file changes Instead of relying on the size and timestamp, which can hide changes performed in the same second, hash the file content's when we care about detecting changes. --- src/config_file.c | 15 +++++---------- src/fileops.c | 49 +++++++++++++++++++++++++---------------------- src/fileops.h | 3 ++- 3 files changed, 33 insertions(+), 34 deletions(-) diff --git a/src/config_file.c b/src/config_file.c index 46f21c0f1..5f5e309e0 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -77,8 +77,7 @@ typedef struct git_config_file_iter { (iter) = (tmp)) struct reader { - time_t file_mtime; - size_t file_size; + git_oid checksum; char *file_path; git_buf buffer; char *read_ptr; @@ -285,7 +284,7 @@ static int config_open(git_config_backend *cfg, git_config_level_t level) git_buf_init(&reader->buffer, 0); res = git_futils_readbuffer_updated( - &reader->buffer, b->file_path, &reader->file_mtime, &reader->file_size, NULL); + &reader->buffer, b->file_path, &reader->checksum, NULL); /* It's fine if the file doesn't exist */ if (res == GIT_ENOTFOUND) @@ -345,7 +344,7 @@ static int config_refresh(git_config_backend *cfg) reader = git_array_get(b->readers, i); error = git_futils_readbuffer_updated( &reader->buffer, reader->file_path, - &reader->file_mtime, &reader->file_size, &updated); + &reader->checksum, &updated); if (error < 0 && error != GIT_ENOTFOUND) return error; @@ -1618,7 +1617,7 @@ static int read_on_variable( git_buf_init(&r->buffer, 0); result = git_futils_readbuffer_updated( - &r->buffer, r->file_path, &r->file_mtime, &r->file_size, NULL); + &r->buffer, r->file_path, &r->checksum, NULL); if (result == 0) { result = config_read(parse_data->values, parse_data->cfg_file, r, parse_data->level, parse_data->depth+1); @@ -1894,7 +1893,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p } else { /* Lock the file */ if ((result = git_filebuf_open( - &file, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) { + &file, cfg->file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) { git_buf_free(&reader->buffer); return result; } @@ -1945,10 +1944,6 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p git_buf_attach(&cfg->locked_content, git_buf_detach(&buf), len); } else { git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf)); - - /* refresh stats - if this errors, then commit will error too */ - (void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file); - result = git_filebuf_commit(&file); } diff --git a/src/fileops.c b/src/fileops.c index 57d2ce9c3..9bef02847 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -153,11 +153,12 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len) } int git_futils_readbuffer_updated( - git_buf *buf, const char *path, time_t *mtime, size_t *size, int *updated) + git_buf *buf, const char *path, git_oid *checksum, int *updated) { + int error; git_file fd; struct stat st; - bool changed = false; + git_oid checksum_new; assert(buf && path && *path); @@ -178,26 +179,6 @@ int git_futils_readbuffer_updated( return -1; } - /* - * If we were given a time and/or a size, we only want to read the file - * if it has been modified. - */ - if (size && *size != (size_t)st.st_size) - changed = true; - if (mtime && *mtime != (time_t)st.st_mtime) - changed = true; - if (!size && !mtime) - changed = true; - - if (!changed) { - return 0; - } - - if (mtime != NULL) - *mtime = st.st_mtime; - if (size != NULL) - *size = (size_t)st.st_size; - if ((fd = git_futils_open_ro(path)) < 0) return fd; @@ -208,6 +189,28 @@ int git_futils_readbuffer_updated( p_close(fd); + if ((error = git_hash_buf(&checksum_new, buf->ptr, buf->size)) < 0) { + git_buf_free(buf); + return error; + } + + /* + * If we were given a checksum, we only want to use it if it's different + */ + if (checksum && !git_oid__cmp(checksum, &checksum_new)) { + git_buf_free(buf); + if (updated) + *updated = 0; + + return 0; + } + + /* + * If we're here, the file did change, or the user didn't have an old version + */ + if (checksum) + git_oid_cpy(checksum, &checksum_new); + if (updated != NULL) *updated = 1; @@ -216,7 +219,7 @@ int git_futils_readbuffer_updated( int git_futils_readbuffer(git_buf *buf, const char *path) { - return git_futils_readbuffer_updated(buf, path, NULL, NULL, NULL); + return git_futils_readbuffer_updated(buf, path, NULL, NULL); } int git_futils_writebuffer( diff --git a/src/fileops.h b/src/fileops.h index 572ff01a5..c6db1953e 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -13,6 +13,7 @@ #include "path.h" #include "pool.h" #include "strmap.h" +#include "oid.h" /** * Filebuffer methods @@ -21,7 +22,7 @@ */ extern int git_futils_readbuffer(git_buf *obj, const char *path); extern int git_futils_readbuffer_updated( - git_buf *obj, const char *path, time_t *mtime, size_t *size, int *updated); + git_buf *obj, const char *path, git_oid *checksum, int *updated); extern int git_futils_readbuffer_fd(git_buf *obj, git_file fd, size_t len); extern int git_futils_writebuffer( From 3547b122b57f4aafd173013d7f82e326b9cffb24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 30 Oct 2015 21:36:51 +0100 Subject: [PATCH 226/450] filebuf: use an internal buffer This reduces the chances of a crash in the thread tests. This shouldn't affect general usage too much, since the main usage of these functions are to read into an empty buffer. --- src/fileops.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index 9bef02847..cfc22bb53 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -153,14 +153,15 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len) } int git_futils_readbuffer_updated( - git_buf *buf, const char *path, git_oid *checksum, int *updated) + git_buf *out, const char *path, git_oid *checksum, int *updated) { int error; git_file fd; struct stat st; + git_buf buf = GIT_BUF_INIT; git_oid checksum_new; - assert(buf && path && *path); + assert(out && path && *path); if (updated != NULL) *updated = 0; @@ -182,15 +183,15 @@ int git_futils_readbuffer_updated( if ((fd = git_futils_open_ro(path)) < 0) return fd; - if (git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size) < 0) { + if (git_futils_readbuffer_fd(&buf, fd, (size_t)st.st_size) < 0) { p_close(fd); return -1; } p_close(fd); - if ((error = git_hash_buf(&checksum_new, buf->ptr, buf->size)) < 0) { - git_buf_free(buf); + if ((error = git_hash_buf(&checksum_new, buf.ptr, buf.size)) < 0) { + git_buf_free(&buf); return error; } @@ -198,7 +199,7 @@ int git_futils_readbuffer_updated( * If we were given a checksum, we only want to use it if it's different */ if (checksum && !git_oid__cmp(checksum, &checksum_new)) { - git_buf_free(buf); + git_buf_free(&buf); if (updated) *updated = 0; @@ -214,6 +215,9 @@ int git_futils_readbuffer_updated( if (updated != NULL) *updated = 1; + git_buf_swap(out, &buf); + git_buf_free(&buf); + return 0; } From 136a71f4eee60052cd1b55ba7c9b600871baf6d4 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Fri, 30 Oct 2015 11:45:52 +0100 Subject: [PATCH 227/450] merge-base: Remove redundant merge bases --- src/commit_list.h | 1 + src/merge.c | 191 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 168 insertions(+), 24 deletions(-) diff --git a/src/commit_list.h b/src/commit_list.h index b1d88e016..a6967bcef 100644 --- a/src/commit_list.h +++ b/src/commit_list.h @@ -13,6 +13,7 @@ #define PARENT2 (1 << 1) #define RESULT (1 << 2) #define STALE (1 << 3) +#define ALL_FLAGS (PARENT1 | PARENT2 | STALE | RESULT) #define PARENTS_PER_COMMIT 2 #define COMMIT_ALLOC \ diff --git a/src/merge.c b/src/merge.c index ac0caeabd..0c132c4a3 100644 --- a/src/merge.c +++ b/src/merge.c @@ -302,32 +302,61 @@ static int interesting(git_pqueue *list) return 0; } -int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos) +static void clear_commit_marks_1(git_commit_list **plist, + git_commit_list_node *commit, unsigned int mark) { + while (commit) { + unsigned int i; + + if (!(mark & commit->flags)) + return; + + commit->flags &= ~mark; + + for (i = 1; i < commit->out_degree; i++) { + git_commit_list_node *p = commit->parents[i]; + git_commit_list_insert(p, plist); + } + + commit = commit->parents[0]; + } +} + +static void clear_commit_marks_many(git_vector *commits, unsigned int mark) +{ + git_commit_list *list = NULL; + git_commit_list_node *c; + unsigned int i; + + git_vector_foreach(commits, i, c) { + git_commit_list_insert(c, &list); + } + + while (list) + clear_commit_marks_1(&list, git_commit_list_pop(&list), mark); +} + +static void clear_commit_marks(git_commit_list_node *commit, unsigned int mark) +{ + git_commit_list *list = NULL; + git_commit_list_insert(commit, &list); + while (list) + clear_commit_marks_1(&list, git_commit_list_pop(&list), mark); +} + +static int paint_down_to_common( + git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos) +{ + git_pqueue list; + git_commit_list *result = NULL; + git_commit_list_node *two; + int error; unsigned int i; - git_commit_list_node *two; - git_commit_list *result = NULL, *tmp = NULL; - git_pqueue list; - - /* If there's only the one commit, there can be no merge bases */ - if (twos->length == 0) { - *out = NULL; - return 0; - } - - /* if the commit is repeated, we have a our merge base already */ - git_vector_foreach(twos, i, two) { - if (one == two) - return git_commit_list_insert(one, out) ? 0 : -1; - } if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_time_cmp) < 0) return -1; - if (git_commit_list_parse(walk, one) < 0) - return -1; - one->flags |= PARENT1; if (git_pqueue_insert(&list, one) < 0) return -1; @@ -376,19 +405,133 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l } git_pqueue_free(&list); + *out = result; + return 0; +} + +static int remove_redundant(git_revwalk *walk, git_vector *commits) +{ + git_vector work = GIT_VECTOR_INIT; + unsigned char *redundant; + unsigned int *filled_index; + unsigned int i, j; + int error = 0; + + redundant = git__calloc(commits->length, 1); + filled_index = git__calloc((commits->length - 1), sizeof(unsigned int)); + + for (i = 0; i < commits->length; ++i) { + if ((error = git_commit_list_parse(walk, commits->contents[i])) < 0) + goto done; + } + + for (i = 0; i < commits->length; ++i) { + git_commit_list *common = NULL; + git_commit_list_node *commit = commits->contents[i]; + + if (redundant[i]) + continue; + + git_vector_clear(&work); + + for (j = 0; j < commits->length; j++) { + if (i == j || redundant[j]) + continue; + + filled_index[work.length] = j; + if ((error = git_vector_insert(&work, commits->contents[j])) < 0) + goto done; + } + + error = paint_down_to_common(&common, walk, commit, &work); + if (error < 0) + goto done; + + if (commit->flags & PARENT2) + redundant[i] = 1; + + for (j = 0; j < work.length; j++) { + git_commit_list_node *w = work.contents[j]; + if (w->flags & PARENT1) + redundant[filled_index[j]] = 1; + } + + clear_commit_marks(commit, ALL_FLAGS); + clear_commit_marks_many(&work, ALL_FLAGS); + + git_commit_list_free(&common); + } + + for (i = 0; i < commits->length; ++i) { + if (redundant[i]) + commits->contents[i] = NULL; + } + +done: + git__free(redundant); + git__free(filled_index); + git_vector_free(&work); + return error; +} + +int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos) +{ + int error; + unsigned int i; + git_commit_list_node *two; + git_commit_list *result = NULL, *tmp = NULL; + + /* If there's only the one commit, there can be no merge bases */ + if (twos->length == 0) { + *out = NULL; + return 0; + } + + /* if the commit is repeated, we have a our merge base already */ + git_vector_foreach(twos, i, two) { + if (one == two) + return git_commit_list_insert(one, out) ? 0 : -1; + } + + if (git_commit_list_parse(walk, one) < 0) + return -1; + + error = paint_down_to_common(&result, walk, one, twos); + if (error < 0) + return error; /* filter out any stale commits in the results */ tmp = result; result = NULL; while (tmp) { - struct git_commit_list *next = tmp->next; - if (!(tmp->item->flags & STALE)) - if (git_commit_list_insert_by_date(tmp->item, &result) == NULL) + git_commit_list_node *c = git_commit_list_pop(&tmp); + if (!(c->flags & STALE)) + if (git_commit_list_insert_by_date(c, &result) == NULL) return -1; + } - git__free(tmp); - tmp = next; + /* more than one merge base -- remove redundants */ + if (result && result->next) { + git_vector redundant = GIT_VECTOR_INIT; + + while (result) + git_vector_insert(&redundant, git_commit_list_pop(&result)); + + clear_commit_marks(one, ALL_FLAGS); + clear_commit_marks_many(twos, ALL_FLAGS); + + if ((error = remove_redundant(walk, &redundant)) < 0) { + git_vector_free(&redundant); + return error; + } + + git_vector_foreach(&redundant, i, two) { + if (two != NULL) + git_commit_list_insert_by_date(two, &result); + } + + git_vector_free(&redundant); } *out = result; From 4cacf5b59452caac3226a113135c476815a85617 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Fri, 30 Oct 2015 11:50:43 +0100 Subject: [PATCH 228/450] merge-base: Do not read parents from the root --- src/merge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/merge.c b/src/merge.c index 0c132c4a3..44904ca04 100644 --- a/src/merge.c +++ b/src/merge.c @@ -318,7 +318,7 @@ static void clear_commit_marks_1(git_commit_list **plist, git_commit_list_insert(p, plist); } - commit = commit->parents[0]; + commit = commit->out_degree ? commit->parents[0] : NULL; } } From 1d0bed9de1de09388f0dbc304a8ad767600b04cf Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Fri, 30 Oct 2015 14:02:01 +0100 Subject: [PATCH 229/450] merge-base: Style --- src/merge.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/merge.c b/src/merge.c index 44904ca04..bad5f9552 100644 --- a/src/merge.c +++ b/src/merge.c @@ -418,7 +418,9 @@ static int remove_redundant(git_revwalk *walk, git_vector *commits) int error = 0; redundant = git__calloc(commits->length, 1); + GITERR_CHECK_ALLOC(redundant); filled_index = git__calloc((commits->length - 1), sizeof(unsigned int)); + GITERR_CHECK_ALLOC(filled_index); for (i = 0; i < commits->length; ++i) { if ((error = git_commit_list_parse(walk, commits->contents[i])) < 0) @@ -511,7 +513,10 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l return -1; } - /* more than one merge base -- remove redundants */ + /* + * more than one merge base -- see if there are redundant merge + * bases and remove them + */ if (result && result->next) { git_vector redundant = GIT_VECTOR_INIT; From 85196232e80384e79a29830300146a8e2663c049 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Fri, 30 Oct 2015 18:32:34 +0100 Subject: [PATCH 230/450] Add test case --- tests/resources/redundant.git/HEAD | Bin 0 -> 23 bytes tests/resources/redundant.git/config | Bin 0 -> 119 bytes .../redundant.git/objects/info/packs | Bin 0 -> 54 bytes ...944c0c5bcb6b16209af847052c6ff1a521529d.idx | Bin 0 -> 121136 bytes ...44c0c5bcb6b16209af847052c6ff1a521529d.pack | Bin 0 -> 309860 bytes tests/resources/redundant.git/packed-refs | Bin 0 -> 161 bytes tests/resources/redundant.git/refs/.gitkeep | Bin tests/revwalk/mergebase.c | 19 ++++++++++++++++++ 8 files changed, 19 insertions(+) create mode 100644 tests/resources/redundant.git/HEAD create mode 100755 tests/resources/redundant.git/config create mode 100644 tests/resources/redundant.git/objects/info/packs create mode 100644 tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.idx create mode 100644 tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.pack create mode 100644 tests/resources/redundant.git/packed-refs create mode 100644 tests/resources/redundant.git/refs/.gitkeep diff --git a/tests/resources/redundant.git/HEAD b/tests/resources/redundant.git/HEAD new file mode 100644 index 0000000000000000000000000000000000000000..cb089cd89a7d7686d284d8761201649346b5aa1c GIT binary patch literal 23 ecmXR)O|w!cN=+-)&qz&7Db~+TEG|hc;sO9;xClW2 literal 0 HcmV?d00001 diff --git a/tests/resources/redundant.git/config b/tests/resources/redundant.git/config new file mode 100755 index 0000000000000000000000000000000000000000..2f8958058adf2a043db52a721e66a626658814a1 GIT binary patch literal 119 zcmaz}&M!)h?o#qRO=VqTIxivecsD%=|nBTLlBSnzYQE)ZF}(RG?r< gQE4h%JSnjVLo6phJuxSzC^fCLASJORwHT%c08(KibN~PV literal 0 HcmV?d00001 diff --git a/tests/resources/redundant.git/objects/info/packs b/tests/resources/redundant.git/objects/info/packs new file mode 100644 index 0000000000000000000000000000000000000000..fbf960f10a5ac311220f34f97f9bec96da0a6da4 GIT binary patch literal 54 zcmWGgC`e4s)-_JCG%-mwNH$GMPBKd}G&3@=OiZ&dF*h(ZN;XSNGfXr!GBh=^Owj|W H=HdbXrBDvm literal 0 HcmV?d00001 diff --git a/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.idx b/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.idx new file mode 100644 index 0000000000000000000000000000000000000000..d8e099a984354feb9b22929c72bc9a0004931514 GIT binary patch literal 121136 zcmWLCWmFq$5C&j0NN{(81$TFc;ts{NxVvj{cPkVtUfiWv(c%uJT(r2m6z=|be$IJk z=G&Z;&2HX3)huCPVBi2~03rY_fEOSKFavl1f&mGD96%|c6EF$*4LAlo!N9A>bB>hX(-Ja7+MxfD*tI-~#vz$O2RYfLyp~z$V}l z1_mA$fCHcd2ms^(+5j6s008J2J|FNM0Mx<{0oDQMfOi-e1WW)qfE^$VPzRU;Tmeyl zOaM@eFbDv05&i>Q!@wXS0Pp}nE+ViiL|Fh(hv)`~1$+Ti0=fXBfK>pnC&V`x7!WFe z48Q^q1pqyRGy&ECA3zMC0088IfSy5ffIol}z!MA%5;6c60Q8I`0RZ-lWC`#EL<5om zz|2U%zL5F=z&RlS^+;DRFv!53k?{eH06~Bnzyja}0Omyo_KXbFAp`Rw1G&h+yvV@3 zD2M<804o5fLjleQ#Sq{G0CG_h0YJ|vwSXP~kck4E5em>V3UI!tAOMhyN(tZsNCNZ# zK;NkD0AOZRAR86f8EPwF1h5SN&KL~r8jJ&g0)SdDFf&*M0Mvp50KmS%xd5PVa5rEG z0Q3$9W(EW241NbLZ7cvS0N5FtFyI3~A7Bdz2LRb<-vBLupMYh+U%(v<3_8#|IxzsK zN9P2H0f70@jQ|dSP(UW29`FM&1^}*Y^nC!3j{ye&`o~}Za0A2vS^(g)f#CuO0>lG~ z0YE;+0ALYt0C<9d!2|70Tp4(pVW>%YQ|-qC zA}z2Ks7uZ)VfBNsV7UJdH6jp}*GxnFvWm}K58cxoVD#Y!Vyihs-oG<`QBYWyK!=m{ zh6$COFeQ{&2$|F#c0mpIox}U@2`1_}x1nGgtJ>$+N?RO`Z@r25B22R2QMJaTKZ;`L z=c2aUzfqQDjxcG(d*US2RQdE$r%Fx(nHeS5|}%o!)AL>diC1x?jGv7cYo&eahUs%dzi@;>_tQhVb(Z8 zLjO6}aac;5)t{IgYlDHmb1Rh(X6IWC-C^Y{Cs+`rTmNC`rkX`(-l42_48v;2N<~TH zDw%RhuTRhnHJfdrd)XjuvQj=G#Cvz0ku5l-9_rNYMOwP`EZ+n1+>?~34qP}D*XG@Wrv#6GDa|YmiY zXe_|F8a?v=$d$JkAbGzn1?yMDx>Ca>;PV;oOw$^@VGiY!&5)rsJYB;582>qbX6F71 zQjj(E`nhGT;2aG%w!&Ikl6bp8TPbX6@ZuNB{D%;3;-O6E$5{+BNBI*q5V84em|n4sc|q;)oHaNy7~lRoab>JM`&=zBHv)Zv=bO{| zE&nE#Dd`=poFKFhp{|677c46vjhZrjUWt9BIf~e*`T>=K7lyJj9$>H5;~%#`iqglS zsdJO?&Q6>BNLNyGvCF=$dy(RezGiXo?xQ26-nbgy4iX?!w+*GIqp5K4Aq5tgIpf(a313XaZE<2(`YH6}79obn5jVP#3Y>;nZ(#ZNcz z&C2CuKM_(!DJW07F0`8kAa3jM16e<~{P6QO>3Qy8p7g(i@gA(;huK0?q!bw_uO9-& z6$X69k&QFp7e^0}Yo{RIkLMrVLcboEN0rgSuYL(*;UCh?xm}@>yy`;CKj?rUU}$}e zGLxd@?xl(o)=1t3{c31Kz*zG3CH}!qH{mw&YLH#(Ib`yRfF^)ZdZzjZdImO z#F7AyfITKw{||w85;HzK=D# zgo;A=NQ*!g8|DAJG9>2($)?>nHn!22*K9BA|h)aGZyg6 z)+MoWXFy^9eWO&BL`43+g-+F7`QdpJLCq~fIu&^#5|OO#j%WNIN<1_f#X1I+El@(P z6cLKF-LC0CPmmSeP;^p1%JcW68Ih^^+0T7`cOOZ7M5p&*lh-#g2a%;b*1%Crpf_dQ z)3Y1K_Z*Gu5Rq@(Z$k_1{{NnuA5JVA^I#U`f!vgP}vjw{P&{X%2C5YmC z7|)qaSdX|%fBvyX(Vfq&#vsbl-3Q86BMK>OIHDZ<`dzOVpo-{}Iv6+iD$qSh`O_aw zmEO4xuMp9v&rExde^vIQjR$ z-k)Kdo9T>9zhDrTAF2O5MgJ+XQxJGYct#sav3*DUolZQo;aX=7fBHiC@6B7c-L?sF z6V(W2#WOMCw$M{Q)ztL~1*;kHXhGl3Vej#n-jtIj%P~-{rjiQr6g$$3RoK>~Z;;6! z!xQm}X$%MPbRVgs{c~K;7h!9`7r(T&g0UkI`k&2dX~Trrcz)*U(SJqaL8@mU>@34& zf;cm!$W}AH$bi#M0{Gt`vZZ!}BbZRs_M6o5txgp*UARk-PvSR>JlFI`*PhAUW-|U~M&bO1=q0NE9GcuW@P5S&4 z5!oivV%VZ)xOqU41XWe^Ep~)RYggi4HdUldkSS2~(yx$f`#hdAS9nkLw~wW4LgS#A zouZwfsHn&QXYAX3f=*S7)-F)}QyOwT$|#mdcw!nmRmL~Ag?&($>L;81`1UN-&z7ZV zTvu%S#toqE9vpk5LxW=pnXltwg(=CWscN7JyhhI!GCXovt~uEFOL=|u)nw4r(s;y8 z$NQht2Cfkh4uMaLq6FyobGQ$uN(;;K4R864jjC6uc0Oo}(S`9*fXMd?)kpi;ok%BW zXC~+(I1zclnrp3k_X+#qwzv^txDxcBwb9ZGub{?#WocEt+(TAH0x*;Ur9M#oYJW}8LVcnm~ z%bE-aWVJ}aGBYx;7cn0sTD||Z;OG9msAxm#%@jb+P@b~Pqp17HIQ<$9AH#_BGjC?! zK~h?YK_^cH=H_%xYAgonkUEJCW%f+LIgaa{Pj!__QIHksX4h~elDYIx3mT`JIEPt# zDLWQ2tacY>mM-hwvg-7cXMqg9re7j54h4cF0n^enf0yc5)y-$OsE%x8Jkcla-$h8j zAdmgIRgc4VxzIdhQmqWv{X-Ndd4e>|qjfD+8zC%YQ3U7x!UGw?XVlv6gBb$X#!(Dp zt^AO!-!;G9i5F@X+B0Juv#m;zJ$*v54nGN*aAlWgT%PNUkNe@a9pLc!oA?WW%QSj0Vttl&N>(?!ol|dj2lD08L zQDp3rulcZ|-$Cu!MK2Z7YZG~fV)M8#gQ7j)y_up>@2pgwj>PPU;{2%+yH}Wp)5L0f z5$X98eKaRAivJaCVk4#_7aWa&HH9c;;CllLN?nY#hL)~27z>N*rU_j3>3b~%WfB`C z_{kdoZSUd<;j2qKznJO+%KVpzsu3)CgHh3`xcTa@Jm3>_l*4stIPZNzcuM$V&CkQ` zoA!Fzs4&Eb|3b@(8-xiW9!a?}(x-wjP|=Q6@;86Vb250@Q5+3Y?IZM1pmMP{!&?<7 zESh+{zPak#rZ~gOqnavqj%~nFddu_4SUqmh%<~y7qT1EYJn&u__&WGAvPs|CD%?0I zq833DI56dEGV&QfRRaHc^DO<6Lanxt4gaS=gt%q`bF-c?PYZtCL2Y$9FAL~z>?>wO z68YoOBM$3ciF#O?On7|6FUKt#I&cLm;a2S#jrx>cvs;9?V%{FucJtj{5rP z&0jaX+bEDQ4F?ih=FGf)2Zlv4;R(i`?B~Y!QM}JP<5Y8d0>efcGxymdhe<|sf%nmD zZE%RUz{t`e*)f5+ z+YN@9ONJI+MvCPxU5G}Qjw78m*)H3XnX4JnC ztUl(0%J3&6V@Y|R2GRIxE53{n{4wSaSp@eaS{~hH)Z^F0YVO~3U`xJjXN70wv{%@a zV=WQ3ewR0Mu>Hu9B___!zwa|9EpjH;Plq@oUz-a8mpj0*l)0axXE;D}A!CP@z@nPXWI&hOC$ zjN<%G;ArGN827;W%zDEz?9yK?O)#yU;FO3~yg_MG!M`4dg2RroYk9G%;PlFFhPJ=) z?|QnXiOPTMxwusQ!KL?`gm-Fa-%yGTPd&>5mgBGgfPeJY+;b+dA|B;xg zSv2_LM7vzHk4DKhX*Hd^HD{9aH~5O_5cP>t9F0=seBZD)Qe}cE<)EgCDN7|f9gX@) z-+Wm#Doyzp>Zin9*x(Qh;qKYw26`Jre>5B zN5~fa0ZoU4V#>(|JkoK-+}Ecsm~8rK6wSaZBp*%T63=KUKI4(Qr+$%l0?j<~bnyLe zR{6tlZ<4(^PFN*w5}GB*50RT#e7E#(Pb@K=M^RNO6wS@%Z=D}qG)sZfOk+(sylb4o zJzBIVXq?L>zx>!k%Yb#z7lTee5G_HJcS*b;>Kd_tP7q8$${){{g!W^%gHd9mHU)m0 zePA0;S5Jbg4Q+yT^*=f%I`nl299(kcI>cEp3vJ)Eo<5=N+icuT>)1ZhS5P3+5Zd1( z0bHI%qNlH-IHti}l%EKGN1;7cwWO-2>a^e8)1mMAV(cRX5TU&tR#nT@hS90+{J8pr z<=>Zx07gfkD4s7<2xho;x^ABPZK}xbafwcA&ns-0Kd>cjI!&zJ{W%rSE*hP_nUrts zzh~UlbGy$I(|?uXHeS%h@_w*r9d-)J{NzLDzE#w(ci%ym*~7rOrW3*zCL+*B+BfRm z5xPQGCT`07*aRNSv9Z%>QxH3!5dMd*t;a4}2S=?|*%$loQ7Yx3g6bAsx6P8oHe*MD zkK^ozwo2RUi+mOOM~i!;NpU!i`4*Q(d#nm{&S6Y+pF8^m?kH>@e?~4-f20Pqu)hN6 z{&f?n#HrNPjx)D|S-S-9&#H;&300f!LZ{fHusGa!jrd~Mtg&_I$*!?sTiGU3l(jw? zjeA4b=c|b5>EAZ+TU=;wo;JiCt`UX!)Zy>Y-(02)@j@*X#e=s~l+b>wRusNsU^A@L z-#gT3&q(tX63s#!Z_%?c@VAWNbiY$h4lRBf>OY67cCFcA$V~iKV#~+P)1rut5fc|T zbRleqp~}X2T@SJGpT#dc?<)%^NafVW&@d(IgsJhe+;ZAVWBK8*VJilQVXZqf&7fN( z{Mptc32iSPq4UZI!>eR;*YBf-Ri&9Y9w{9){T74=BZ^dI9K*(bm65#PA?NdFm1Y-l zjNV)bgz<<{thYlCv-%87#fi~_F&JYaTf0mXCq`QRIzOQ2o5#zIG3i;GI*F%mjwrIs zyY{%CckaJ|G5subZ#T$-;WCw*&o2k%lesm-INl(W`+drVP!YH>neR$m6tBmP@hFjy z71Nu-@#y>#8M9(&PmVZ>@qU@RXF-<`QA?DVz9sxVkmCFS6Ojv$l zE|L~$I?vG}z)VeTYT!z}$( zZ{y&tC30yzmcT79i#y?#hFJ%XgMd=5IW+uwT1bOxY?4Bji`n7K0JrJb*sLyFSIbNg zdNOvZgt^bCvop}v7Dq03Yjo1R`fOengN2)1sC+1jHQUtxC+My2QHW8h35(=(W&yos zbN-eiT_H65qDPi?1`87HD}@JUoT;8(A4>C;+t9zqz=DqDU}P31O7Y+ywNdn!Fe@U< zVQH&3cYVpZ>pcJ3;99ks*%4GghGmDzdk&9t8D1(?9<|#6c{A6$!V3Cc8;imO#X{vu z{^4-nsqvB61FPazku-=@m!ID+rjZim?9zmS0jtuaFDtE&J&K1ls!3YP%XYGthPP{TRDNWC&wWBYT-{u1B%&R(oFI-^96 z5t*vtIuP#*v%otz;TdZqp2tziS@&%Vq~PshKS@cNRe*I$hwuWCwS=FA)gT*BmAt?6 zy~27+#22ta&dNbaVKlvkIJ0W@S73vu51ZL9opM94hQ487TjD#JJz*=&;=7yhZ_9O* z#i73ZyDwz?R)npBrQcs_!Sff_K{FsoqTj{ixulTyD;D<0 zk&vc~%m)SUg{Fo@W~_V$VkT^lpg0khoyu48`CfFgBa6Cmm|AS_=d&PGOp@@!0sAnS z|D2DHBXzL-uqOT8){?&^lhYAhR7rh!gk{4{Nyq(r|0qP_(QW=OlQJ7o)aMF&qW95W zS0K8(Q%TuUHQ(634K^5iezxDlJ$f*IaEUsbgqtmL**+6{N6vR4px>m!Kz}eM(#Vp< z4S^T?sZN_@50QPd--RRV;dmIC^B@QNt^S~><4(3tNsoAEF!Km;puZCb&SQIZF@lwj z^4quK&40O|{qkcREY}@br=N}!$2G71Wj{CJv+go*$UI-`u!J?elfGQIu3c{hec8;z zVH$Pk(XHrFPpX`VIsW(!4^uZ3hl^m6OJeaWMZqwl-eVFR)>Fzb4!0f!bY2>>IEsMg zDO(sxndYZgoY>77dNa$>P3nJs(lHiI<5TPF09b!%T<0~IeH!B z-gFfwp}@g2TzC@3Lz1_aQHfg&cFfBEpzlSIxJa@B7#=98-t#fe91pv6Qhh<$xI{)0 zg43T#{TvB(mA~MLHnI3E;*zf@Jf(Vj?&Ps8@1%9n3&G?Q;ZoK%V2on;49#>L(cYwN zI{07R;7SFo)G-i}@9;1yDSAhA8Kvr5;mShZ29x|g*v;V7B>WhRs&A2~!Znb!&R%T! z<)wm;=!bJe-ikXBi2G^q%kfqkJUrv&`4#keyO;I%2(I-RCoLxN;l2{jfLHD|MvW$K zBW}DJT4zp>v)f!REs~xIzf7QtA8thvdc(9M%pbd}fMedNQe z_1C>CC+yS0{xa`L7TJHeNWeYGCZwUOFg)-JVx|em)9iP{JH@^E9dWEFo+)zgn%h;% zK%2e#;T-qjrI|@hgb2c63tQ`pS=}G@@C^?}!p_)!Cqi(XN}f&wjj@3WeIAb{x@*M| zm5|SBwFE^nl{I_k4I7VT?(19V4O>0eQB335Xo3NpuRfkMUbAzaly8#}pDuzT4nMNXqEOH4CNZ z<^FfUCI2ZZ{09Zz=U$g348HEbT15c}c8%~h?*}2gWV%$Zc32uwkk@NP~=ezl{C%aTR6MSsELdX%1rQb3{37?s=kktEm^?Nim0CB|b#X5^B|#eJvKl+iU+v zYpm4nMm76wV5iXDIGUgvpU{Rq_pJ1T&lgS-Jb%rNw6bD+d|HWw(Bz;T;#_50qH!f3 zm)N!`d=6>tirH{7J*5dZ+lb3Yd}%3a>FNQf z*6i7t_Hx8z(gmU{zT9+JZ?E3btEsDW7+54!Qkq&6U;fUGH$IV-VU*qVsSinatI*sM zU;c>?Uxvs__;@tr^PW0ksu^+s{>K|I*kskAF;SEGus1hwLjNEc--m9A5Nno9b8-wu zO2F!rF~si;KU@TEIa!#yzW-}uS%sJnoz{aOeuC5P7(*%ETK%MVXfS;!BXLnU{@3GE ziLKn%e<~jH99Q`U@4$03{Bpx@^KW+eg2FvjP7+dGn$Oea_ya>b?2{xiN!~|_>&lXa zaC;%#_+tctKOFf#$9jahEv^0X=BUkS#h+PO8q$q!{jXV}PXdQPc8nC268~K$qxREj z&BSh7Z_#8=A!4uHec0O4G6d@M6ST54L_bIeIQ7C z>y-Y_v<=UDHT{(@Q1fw+ZHpj%YVVu!ykPj$RH|o8gEvxkY$d@6j)nTk#P7Hg7^<^_ ziB@Nt-;@Mrrl<*xo4?`~ZH$oWe_rtoeXAq5hz}vv>it#Ge6~k}4+c+Z`jrs;gI;F% zh_Vs>8TI7;O-jAU#B@!FX&({^*P)oKe`F_38qL$!FMdr3;d6Dpcu$`)mJB%}qHQG+ zDP!2>(a zO(G)>ynfo>C+8Hxf|(D?FOEC>os23pW+eiv?JeI4i+=GOd9wdk=0h{1Xc6K5h$cu+ zSiiM!H{rJ?pzf6tngJ8u4NWs7Y)8EBbHXPQZ7@PFikMbvnE^!;c8mPXAsAee5Z0}n zjFH1Z9J1;q97C|eEcLIG#6`FuOm!9nR0Oo>inaC^MW(cvpHe z34+DKziBSQzjcbBTyI(?g4dC`Y^76B$jgG#N4%d71kV~1q0(*aInJ5~ajL$O@K!~= zpP^6^u`PV!L)a%B|L@ajb4G!w$99xBkyJ8tmD%tyZOODt(Wy~&tiRWZNEYg9$VN-; zMEZ%}W_OON{NdvTk-`>BS#Oe8=%IP%E~C-2DFX~CkurgJFZB{7dtrx`AI!hh*-H0O zBAsSRJnop{g*b!ExbKisk(`;177bfr^67tvr3{Es;G zk|17V|Lv0`JKYf$G@=nUb(ZOS^MHT%vCN2)vmNc4{}IizmPK&l8hlcxb2EL0;K^7Q z{UKT~S1F-B@S*5ol{hAF24#K;N+mi*SsxxT5Ze9* zHjjd(s^hms8Fx|gcX{HBU#M}!KBAdsoP#qr!mMY}k z@IMikc_w`T|Fnu{?b=$aXOzxTQ64A$zJrJWCm^ot^YiZZ>a7eK_5Fpot%2QfCq3HX zvjF1oA6N%Gqq=P3AgyUWB;&!x(THOM;gBs>e0p^{Rhr0q9vrA}upFG~;c z_QT`93)Y@D-i=$#RE?H?aE%A?(VUh5<~rVEkt}kn*&TUP9E}w51#+42r9$(LHGBMe z@Aw!p44WYFbs?$cM;*R#-vQemZa+xpSOS>%VX@|gvJ)~G5oGDJ)DqXcxQt1HZWm;? zuIQig_%iw_Yg&$5B9!Wn^*!~iJu6HL!aYbZsD*=0`!rRm}QaC z4t^?oAS-*g&6$^y1f1#9F%{Q^6)Co!=*3CPCN86rq?)R<_RIymB~)uOJg8HiqmcWN zr2Zio5AVlyPVApCMVuF53l~5psq`vyWlHBWAXVugUnu7DG~U7`>A|1`&qnzpLOo@` zV86aC%<3tUpJF?r=_)J6)QW`_|9Thv#Pvf+_ES+Fzmy8Muu5MV@SvlL>yagqBBiO4 zvkFrV^fkOj6=!dlg*g|Iq9B}fT#f%yny=3h@@#0DDX2X!jul3l>N{UYgVBg3m?S83I@dvrX=B!@;QxFpZ5BS zNnl!225{IJ3cyE@ihTKVl}LCl;c+o7GaT*nr}7q;R3*IPKjW}1eCNZ!C8_;y{7GW} zkvc`<4j4}Ar#cIQmRxD@50qo!NL|p~Y*Z$lC;j#%hJuMZid7ehNnJ0d;Y~(4S^pyH zzlT%zV*IgACQak)!MVougdd)E#ovAUsP+j$MOtDl!*Ea3$ly5wAx9n!(O&;6Vcjh>!WUqYWw zxE#V7VM%X``HfUFgjdW%nK>Pumr(GFGfCgVW#unXdULg7Q+?WMB$S&&KxF7Slx~?U z*c8pE!%3|Ypa1Dn_L5<{;VIpu<@b{oSl;4KVQ1mf@si;lPBQO1=SNCdi#N;-WWfl1 zI3=SH*&@|dzbzUmi!eKVD?UMN}4FsF|suOd!<8T|z z^&5B+xsrWOQ*(+EPq%E38S%uM%Ka^N@{=s`Nm_Jh`sbAoT>t4`9x)Q#2S&2=0xY*s zSB!{iF=U8mHXZf8Fg#i3D_Q;KgS9ceNLb`Yzx}=5^JKCyZp82FZtmBC)Air5zoiYS zx1*8m?abo9-w$n?=-?N!CQ?dMO4yMd{n*Mwb8%%&≈&i>o)EFFhkWV=_%t56}1l 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 literal 0 HcmV?d00001 diff --git a/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.pack b/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.pack new file mode 100644 index 0000000000000000000000000000000000000000..02ea49f4b34e87f6bb9d88bd1dd1340829cfefd3 GIT binary patch literal 309860 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 literal 0 HcmV?d00001 diff --git a/tests/resources/redundant.git/packed-refs b/tests/resources/redundant.git/packed-refs new file mode 100644 index 0000000000000000000000000000000000000000..e8bf04d65d57ac7a24892f51ed4ec6b4058b2798 GIT binary patch literal 161 zcmYk!%L>9U5QX7=o+9A7B$JynzRjeWp<;y6RK&Np1s84({EPEl?8@_F+C+za-}mKa zSCKBw>Z|w9^a~?^%HfJAG$(=3Fhcg|ks~y%T7e~&r1pf|BHV_1^4MiF1ez-4rin9U apO_G0dc9!hZMp!ovzx-j?v literal 0 HcmV?d00001 diff --git a/tests/resources/redundant.git/refs/.gitkeep b/tests/resources/redundant.git/refs/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/revwalk/mergebase.c b/tests/revwalk/mergebase.c index aafe97d71..d486bbaf3 100644 --- a/tests/revwalk/mergebase.c +++ b/tests/revwalk/mergebase.c @@ -492,3 +492,22 @@ void test_revwalk_mergebase__octopus_merge_branch(void) * * a */ + +void test_revwalk_mergebase__remove_redundant(void) +{ + git_repository *repo; + git_oid one, two, base; + git_oidarray result = {NULL, 0}; + + cl_git_pass(git_repository_open(&repo, cl_fixture("redundant.git"))); + + cl_git_pass(git_oid_fromstr(&one, "d89137c93ba1ee749214ff4ce52ae9137bc833f9")); + cl_git_pass(git_oid_fromstr(&two, "91f4b95df4a59504a9813ba66912562931d990e3")); + cl_git_pass(git_oid_fromstr(&base, "6cb1f2352d974e1c5a776093017e8772416ac97a")); + + cl_git_pass(git_merge_bases(&result, repo, &one, &two)); + cl_assert_equal_i(1, result.count); + cl_assert_equal_oid(&base, &result.ids[0]); + + git_repository_free(repo); +} From b656e5eb4f29e05e5cff2231a368be45db894807 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Mon, 2 Nov 2015 12:06:50 +0100 Subject: [PATCH 231/450] merge: Fix memory leak in test --- tests/revwalk/mergebase.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/revwalk/mergebase.c b/tests/revwalk/mergebase.c index d486bbaf3..ee078b3e7 100644 --- a/tests/revwalk/mergebase.c +++ b/tests/revwalk/mergebase.c @@ -509,5 +509,6 @@ void test_revwalk_mergebase__remove_redundant(void) cl_assert_equal_i(1, result.count); cl_assert_equal_oid(&base, &result.ids[0]); + git_oidarray_free(&result); git_repository_free(repo); } From 505e4531b7e52daf6caa9eac9904d9a014e0d14f Mon Sep 17 00:00:00 2001 From: Leo Yang Date: Fri, 30 Oct 2015 13:53:53 -0400 Subject: [PATCH 232/450] Fix build for unit test If none of GIT_OPENSSL, GIT_WINHTTP or GIT_SECURE_TRANSPORT is defined we should also be able to build the unit test. --- tests/online/badssl.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/online/badssl.c b/tests/online/badssl.c index 850468320..12badbda3 100644 --- a/tests/online/badssl.c +++ b/tests/online/badssl.c @@ -5,23 +5,34 @@ static git_repository *g_repo; #if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT) +static bool g_has_ssl = true; +#else +static bool g_has_ssl = false; +#endif void test_online_badssl__expired(void) { + if (!g_has_ssl) + cl_skip(); + cl_git_fail_with(GIT_ECERTIFICATE, git_clone(&g_repo, "https://expired.badssl.com/fake.git", "./fake", NULL)); } void test_online_badssl__wrong_host(void) { + if (!g_has_ssl) + cl_skip(); + cl_git_fail_with(GIT_ECERTIFICATE, git_clone(&g_repo, "https://wrong.host.badssl.com/fake.git", "./fake", NULL)); } void test_online_badssl__self_signed(void) { + if (!g_has_ssl) + cl_skip(); + cl_git_fail_with(GIT_ECERTIFICATE, git_clone(&g_repo, "https://self-signed.badssl.com/fake.git", "./fake", NULL)); } - -#endif From 3138ad936682addd191913c42946aae431b4eece Mon Sep 17 00:00:00 2001 From: Jason Haslam Date: Thu, 16 Jul 2015 10:17:16 -0600 Subject: [PATCH 233/450] Add diff progress callback. --- CHANGELOG.md | 8 ++++++++ include/git2/diff.h | 29 ++++++++++++++++++++++++----- src/diff.c | 15 +++++++++++++-- tests/diff/notify.c | 29 ++++++++++++++++++++++++++++- tests/diff/tree.c | 2 +- 5 files changed, 74 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ade3e3b1..0b2e43308 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,11 @@ v0.23 + 1 the opportunity for concurrent operations and not committing any changes until the unlock. +* `git_diff_options` added a new callback `progress_cb` to report on the + progress of the diff as files are being compared. The documentation of + the existing callback `notify_cb` was updated to reflect that it only + gets called when new deltas are added to the diff. + ### API removals ### Breaking API changes @@ -36,6 +41,9 @@ v0.23 + 1 it existed in the index. This does not affect the higher-level `git_index_add_bypath` or `git_index_add_frombuffer` functions. +* The `notify_payload` field of `git_diff_options` was renamed to `payload` + to reflect that it's also the payload for the new progress callback. + v0.23 ------ diff --git a/include/git2/diff.h b/include/git2/diff.h index a0f6db350..cbffdb49a 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -350,6 +350,22 @@ typedef int (*git_diff_notify_cb)( const char *matched_pathspec, void *payload); +/** + * Diff progress callback. + * + * Called before each file comparison. + * + * @param diff_so_far The diff being generated. + * @param old_path The path to the old file or NULL. + * @param new_path The path to the new file or NULL. + * @return Non-zero to abort the diff. + */ +typedef int (*git_diff_progress_cb)( + const git_diff *diff_so_far, + const char *old_path, + const char *new_path, + void *payload); + /** * Structure describing options about how the diff should be executed. * @@ -370,8 +386,10 @@ typedef int (*git_diff_notify_cb)( * - `max_size` is a file size (in bytes) above which a blob will be marked * as binary automatically; pass a negative value to disable. * - `notify_cb` is an optional callback function, notifying the consumer of - * which files are being examined as the diff is generated - * - `notify_payload` is the payload data to pass to the `notify_cb` function + * changes to the diff as new deltas are added. + * - `progress_cb` is an optional callback function, notifying the consumer of + * which files are being examined as the diff is generated. + * - `payload` is the payload to pass to the callback functions. * - `ignore_submodules` overrides the submodule ignore setting for all * submodules in the diff. */ @@ -383,8 +401,9 @@ typedef struct { git_submodule_ignore_t ignore_submodules; /**< submodule ignore rule */ git_strarray pathspec; /**< defaults to include all paths */ - git_diff_notify_cb notify_cb; - void *notify_payload; + git_diff_notify_cb notify_cb; + git_diff_progress_cb progress_cb; + void *payload; /* options controlling how to diff text is generated */ @@ -403,7 +422,7 @@ typedef struct { * `git_diff_options_init` programmatic initialization. */ #define GIT_DIFF_OPTIONS_INIT \ - {GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, 3} + {GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, NULL, 3} /** * Initializes a `git_diff_options` with default values. Equivalent to diff --git a/src/diff.c b/src/diff.c index b5e9b6cd5..c2362358a 100644 --- a/src/diff.c +++ b/src/diff.c @@ -56,7 +56,7 @@ static int diff_insert_delta( if (diff->opts.notify_cb) { error = diff->opts.notify_cb( - diff, delta, matched_pathspec, diff->opts.notify_payload); + diff, delta, matched_pathspec, diff->opts.payload); if (error) { git__free(delta); @@ -1260,7 +1260,18 @@ int git_diff__from_iterators( /* run iterators building diffs */ while (!error && (info.oitem || info.nitem)) { - int cmp = info.oitem ? + int cmp; + + /* report progress */ + if (opts && opts->progress_cb) { + if ((error = opts->progress_cb(diff, + info.oitem ? info.oitem->path : NULL, + info.nitem ? info.nitem->path : NULL, + opts->payload))) + break; + } + + cmp = info.oitem ? (info.nitem ? diff->entrycomp(info.oitem, info.nitem) : -1) : 1; /* create DELETED records for old items not matched in new */ diff --git a/tests/diff/notify.c b/tests/diff/notify.c index 6ef4af573..74abbc93b 100644 --- a/tests/diff/notify.c +++ b/tests/diff/notify.c @@ -55,7 +55,7 @@ static void test_notify( opts.pathspec.strings = searched_pathspecs; opts.pathspec.count = pathspecs_count; - opts.notify_payload = expected_matched_pathspecs; + opts.payload = expected_matched_pathspecs; memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); @@ -228,3 +228,30 @@ void test_diff_notify__notify_cb_can_be_used_as_filtering_function(void) git_diff_free(diff); } + +static int progress_abort_diff( + const git_diff *diff_so_far, + const char *old_path, + const char *new_path, + void *payload) +{ + GIT_UNUSED(old_path); + GIT_UNUSED(new_path); + GIT_UNUSED(payload); + + return -42; +} + +void test_diff_notify__progress_cb_can_abort_diff(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff *diff = NULL; + + g_repo = cl_git_sandbox_init("status"); + + opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED; + opts.progress_cb = progress_abort_diff; + + cl_git_fail_with( + git_diff_index_to_workdir(&diff, g_repo, NULL, &opts), -42); +} diff --git a/tests/diff/tree.c b/tests/diff/tree.c index 2bc9e6a55..e4b2a8bbe 100644 --- a/tests/diff/tree.c +++ b/tests/diff/tree.c @@ -90,7 +90,7 @@ void test_diff_tree__0(void) #define DIFF_OPTS(FLAGS, CTXT) \ {GIT_DIFF_OPTIONS_VERSION, (FLAGS), GIT_SUBMODULE_IGNORE_UNSPECIFIED, \ - {NULL,0}, NULL, NULL, (CTXT), 1} + {NULL,0}, NULL, NULL, NULL, (CTXT), 1} void test_diff_tree__options(void) { From d39f643a0a09336ad77f04b7037cbc1c486bdd50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 13 Oct 2015 19:34:07 +0200 Subject: [PATCH 234/450] stream: accept NULL in the free function --- src/stream.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/stream.h b/src/stream.h index 43fcc3045..4692c7115 100644 --- a/src/stream.h +++ b/src/stream.h @@ -62,6 +62,9 @@ GIT_INLINE(int) git_stream_close(git_stream *st) GIT_INLINE(void) git_stream_free(git_stream *st) { + if (!st) + return; + st->free(st); } From 7fafde632580f75ff3647ddf9e1e92ee388f317e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 13 Oct 2015 11:25:41 +0200 Subject: [PATCH 235/450] stream: allow registering a user-provided TLS constructor This allows the application to use their own TLS stream, regardless of the capabilities of libgit2 itself. --- CHANGELOG.md | 5 +++++ include/git2/sys/stream.h | 13 +++++++++++ src/tls_stream.c | 13 +++++++++++ tests/core/stream.c | 47 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 tests/core/stream.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b2e43308..dec40e49e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,11 +17,16 @@ v0.23 + 1 the opportunity for concurrent operations and not committing any changes until the unlock. + * `git_diff_options` added a new callback `progress_cb` to report on the progress of the diff as files are being compared. The documentation of the existing callback `notify_cb` was updated to reflect that it only gets called when new deltas are added to the diff. +* `git_stream_register_tls()` lets you register a callback to be used + as the constructor for a TLS stream instead of the libgit2 built-in + one. + ### API removals ### Breaking API changes diff --git a/include/git2/sys/stream.h b/include/git2/sys/stream.h index 55a714bbb..2b4ff7fd8 100644 --- a/include/git2/sys/stream.h +++ b/include/git2/sys/stream.h @@ -39,6 +39,19 @@ typedef struct git_stream { void (*free)(struct git_stream *); } git_stream; +typedef int (*git_stream_cb)(git_stream **out, const char *host, const char *port); + +/** + * Register a TLS stream constructor for the library to use + * + * If a constructor is already set, it will be overwritten. Pass + * `NULL` in order to deregister the current constructor. + * + * @param ctor the constructor to use + * @return 0 or an error code + */ +GIT_EXTERN(int) git_stream_register_tls(git_stream_cb ctor); + GIT_END_DECL #endif diff --git a/src/tls_stream.c b/src/tls_stream.c index 39a8ce343..83e2d064a 100644 --- a/src/tls_stream.c +++ b/src/tls_stream.c @@ -11,8 +11,21 @@ #include "openssl_stream.h" #include "stransport_stream.h" +static git_stream_cb tls_ctor; + +int git_stream_register_tls(git_stream_cb ctor) +{ + tls_ctor = ctor; + + return 0; +} + int git_tls_stream_new(git_stream **out, const char *host, const char *port) { + + if (tls_ctor) + return tls_ctor(out, host, port); + #ifdef GIT_SECURE_TRANSPORT return git_stransport_stream_new(out, host, port); #elif defined(GIT_OPENSSL) diff --git a/tests/core/stream.c b/tests/core/stream.c new file mode 100644 index 000000000..ace6a05da --- /dev/null +++ b/tests/core/stream.c @@ -0,0 +1,47 @@ +#include "clar_libgit2.h" +#include "git2/sys/stream.h" +#include "tls_stream.h" +#include "stream.h" + +static git_stream test_stream; +static int ctor_called; + +static int test_ctor(git_stream **out, const char *host, const char *port) +{ + GIT_UNUSED(host); + GIT_UNUSED(port); + + ctor_called = 1; + *out = &test_stream; + + return 0; +} + +void test_core_stream__register_tls(void) +{ + git_stream *stream; + int error; + + ctor_called = 0; + cl_git_pass(git_stream_register_tls(test_ctor)); + cl_git_pass(git_tls_stream_new(&stream, "localhost", "443")); + cl_assert_equal_i(1, ctor_called); + cl_assert_equal_p(&test_stream, stream); + + ctor_called = 0; + stream = NULL; + cl_git_pass(git_stream_register_tls(NULL)); + error = git_tls_stream_new(&stream, "localhost", "443"); + + /* We don't have arbitrary TLS stream support on Windows */ +#if GIT_WIN32 + cl_git_fail_with(-1, error); +#else + cl_git_pass(error); +#endif + + cl_assert_equal_i(0, ctor_called); + cl_assert(&test_stream != stream); + + git_stream_free(stream); +} From f20480ab0ccbba077e6ec060d3b4ef7f74650828 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 3 Nov 2015 09:40:30 -0500 Subject: [PATCH 236/450] diff: test "symlinks" in wd are respected on win32 When `core.symlinks = false`, we write the symlinks content (target) to a regular file. We should ensure that when we later see that regular file, we treat it specially - and that changing that regular file would actually change the symlink target. (For compatibility with Git for Windows). --- tests/diff/workdir.c | 61 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c index dac32453b..4c782339d 100644 --- a/tests/diff/workdir.c +++ b/tests/diff/workdir.c @@ -2097,3 +2097,64 @@ void test_diff_workdir__to_index_pathlist(void) git_vector_free(&pathlist); } +void test_diff_workdir__symlink_changed_on_non_symlink_platform(void) +{ + git_tree *tree; + git_diff *diff; + diff_expects exp = {0}; + const git_diff_delta *delta; + const char *commit = "7fccd7"; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_vector pathlist = GIT_VECTOR_INIT; + int symlinks; + + g_repo = cl_git_sandbox_init("unsymlinked.git"); + + cl_git_pass(git_repository__cvar(&symlinks, g_repo, GIT_CVAR_SYMLINKS)); + + if (symlinks) + cl_skip(); + + cl_git_pass(git_vector_insert(&pathlist, "include/Nu/Nu.h")); + + opts.pathspec.strings = (char **)pathlist.contents; + opts.pathspec.count = pathlist.length; + + cl_must_pass(p_mkdir("symlink", 0777)); + cl_git_pass(git_repository_set_workdir(g_repo, "symlink", false)); + + cl_assert((tree = resolve_commit_oid_to_tree(g_repo, commit)) != NULL); + + /* first, do the diff with the original contents */ + + cl_git_pass(git_futils_mkpath2file("symlink/include/Nu/Nu.h", 0755)); + cl_git_mkfile("symlink/include/Nu/Nu.h", "../../objc/Nu.h"); + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts)); + cl_assert_equal_i(0, git_diff_num_deltas(diff)); + git_diff_free(diff); + + /* now update the contents and expect a difference, but that the file + * mode has persisted as a symbolic link. + */ + + cl_git_rewritefile("symlink/include/Nu/Nu.h", "awesome content\n"); + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts)); + + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(1, exp.files); + + cl_assert_equal_i(1, git_diff_num_deltas(diff)); + delta = git_diff_get_delta(diff, 0); + cl_assert_equal_i(GIT_FILEMODE_LINK, delta->old_file.mode); + cl_assert_equal_i(GIT_FILEMODE_LINK, delta->new_file.mode); + + git_diff_free(diff); + + cl_git_pass(git_futils_rmdir_r("symlink", NULL, GIT_RMDIR_REMOVE_FILES)); + + git_tree_free(tree); + git_vector_free(&pathlist); +} From 6b0fc6abc159c6f15f49bf5ab40b1152d8c6165f Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 3 Nov 2015 09:43:18 -0500 Subject: [PATCH 237/450] diff: on win32, treat fake "symlinks" specially On platforms that lack `core.symlinks`, we should not go looking for symbolic links and `p_readlink` their target. Instead, we should examine the file's contents. --- src/diff_file.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/diff_file.c b/src/diff_file.c index c60362865..ecc34cf55 100644 --- a/src/diff_file.c +++ b/src/diff_file.c @@ -259,10 +259,35 @@ static int diff_file_content_load_blob( return error; } +static int diff_file_content_load_workdir_symlink_fake( + git_diff_file_content *fc, git_buf *path) +{ + git_buf target = GIT_BUF_INIT; + int error; + + if ((error = git_futils_readbuffer(&target, path->ptr)) < 0) + return error; + + fc->map.len = git_buf_len(&target); + fc->map.data = git_buf_detach(&target); + fc->flags |= GIT_DIFF_FLAG__FREE_DATA; + + git_buf_free(&target); + return error; +} + static int diff_file_content_load_workdir_symlink( git_diff_file_content *fc, git_buf *path) { ssize_t alloc_len, read_len; + int symlink_supported, error; + + if ((error = git_repository__cvar( + &symlink_supported, fc->repo, GIT_CVAR_SYMLINKS)) < 0) + return -1; + + if (!symlink_supported) + return diff_file_content_load_workdir_symlink_fake(fc, path); /* link path on disk could be UTF-16, so prepare a buffer that is * big enough to handle some UTF-8 data expansion From 6cc5023bfdb5eea30728aadce92612e01834415f Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 3 Nov 2015 11:27:31 -0500 Subject: [PATCH 238/450] index: test that add_bypath preserves symlinks Test that on platforms without `core.symlinks`, we preserve symlinks in `git_index_add_bypath`. (Users should correct the actual index entry's mode to change a link to a regular file.) --- tests/index/bypath.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/index/bypath.c b/tests/index/bypath.c index 0c10cfe4c..88a76178a 100644 --- a/tests/index/bypath.c +++ b/tests/index/bypath.c @@ -328,3 +328,32 @@ void test_index_bypath__add_honors_conflict_case(void) cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode); } + +void test_index_bypath__add_honors_symlink(void) +{ + const git_index_entry *entry; + git_index_entry new_entry; + int symlinks; + + cl_git_pass(git_repository__cvar(&symlinks, g_repo, GIT_CVAR_SYMLINKS)); + + if (symlinks) + cl_skip(); + + cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); + + memcpy(&new_entry, entry, sizeof(git_index_entry)); + new_entry.path = "README.txt"; + new_entry.mode = GIT_FILEMODE_LINK; + + cl_git_pass(git_index_add(g_idx, &new_entry)); + cl_git_pass(git_index_write(g_idx)); + + cl_git_rewritefile("submod2/README.txt", "Modified but still a (fake) symlink"); + + cl_git_pass(git_index_add_bypath(g_idx, "README.txt")); + cl_git_pass(git_index_write(g_idx)); + + cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL); + cl_assert_equal_i(GIT_FILEMODE_LINK, entry->mode); +} From 2d556f31665e9fde0310f4ae89cd3f01a5810b1c Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 3 Nov 2015 14:48:31 -0500 Subject: [PATCH 239/450] reflog: test reflog is deleted when ref is deleted --- tests/refs/reflog/reflog.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c index 3fbf412e4..ba40c76b8 100644 --- a/tests/refs/reflog/reflog.c +++ b/tests/refs/reflog/reflog.c @@ -125,6 +125,24 @@ void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void) git_buf_free(&master_log_path); } +void test_refs_reflog_reflog__deleting_the_reference_deletes_the_reflog(void) +{ + git_reference *master; + git_buf master_log_path = GIT_BUF_INIT; + + git_buf_joinpath(&master_log_path, git_repository_path(g_repo), GIT_REFLOG_DIR); + git_buf_joinpath(&master_log_path, git_buf_cstr(&master_log_path), "refs/heads/master"); + + cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&master_log_path))); + + cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master")); + cl_git_pass(git_reference_delete(master)); + git_reference_free(master); + + cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&master_log_path))); + git_buf_free(&master_log_path); +} + static void assert_has_reflog(bool expected_result, const char *name) { cl_assert_equal_i(expected_result, git_reference_has_log(g_repo, name)); From ec50b23acc5a9f00f597fa877ad09cad56cb1204 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 3 Nov 2015 17:02:07 -0500 Subject: [PATCH 240/450] filebuf: detect directories in our way When creating a filebuf, detect a directory that exists in our target file location. This prevents a failure later, when we try to move the lock file to the destination. --- src/filebuf.c | 6 ++++++ tests/core/filebuf.c | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/src/filebuf.c b/src/filebuf.c index 2bbc210ba..17efe872e 100644 --- a/src/filebuf.c +++ b/src/filebuf.c @@ -357,6 +357,12 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode memcpy(file->path_lock, file->path_original, path_len); memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH); + if (git_path_isdir(file->path_original)) { + giterr_set(GITERR_FILESYSTEM, "path '%s' is a directory", file->path_original); + error = GIT_EDIRECTORY; + goto cleanup; + } + /* open the file for locking */ if ((error = lock_file(file, flags, mode)) < 0) goto cleanup; diff --git a/tests/core/filebuf.c b/tests/core/filebuf.c index 915e3cc34..93aaed759 100644 --- a/tests/core/filebuf.c +++ b/tests/core/filebuf.c @@ -230,3 +230,12 @@ void test_core_filebuf__hidden_file(void) git_filebuf_cleanup(&file); #endif } + +void test_core_filebuf__detects_directory(void) +{ + git_filebuf file = GIT_FILEBUF_INIT, fail = GIT_FILEBUF_INIT; + + cl_must_pass(p_mkdir("foo", 0777)); + cl_git_fail_with(GIT_EDIRECTORY, git_filebuf_open(&file, "foo", 0, 0666)); + cl_must_pass(p_rmdir("foo")); +} From f5f96a23ee7c7774bca5c52404f819cf56c78501 Mon Sep 17 00:00:00 2001 From: Stjepan Rajko Date: Fri, 9 Oct 2015 10:41:06 -0700 Subject: [PATCH 241/450] Fix git_commit_summary to convert newlines to spaces even after whitespace. Collapse spaces around newlines for the summary. --- src/commit.c | 35 +++++++++++++++++++++++++---------- tests/commit/commit.c | 8 ++++++-- tests/commit/write.c | 2 +- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/commit.c b/src/commit.c index 616f947db..b42f9de28 100644 --- a/src/commit.c +++ b/src/commit.c @@ -431,22 +431,37 @@ const char *git_commit_summary(git_commit *commit) { git_buf summary = GIT_BUF_INIT; const char *msg, *space; + bool space_contains_newline = false; assert(commit); if (!commit->summary) { for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) { - if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n')) + char next_character = msg[0]; + /* stop processing at the end of the first paragraph */ + if (next_character == '\n' && (!msg[1] || msg[1] == '\n')) break; - else if (msg[0] == '\n') - git_buf_putc(&summary, ' '); - else if (git__isspace(msg[0])) - space = space ? space : msg; - else if (space) { - git_buf_put(&summary, space, (msg - space) + 1); - space = NULL; - } else - git_buf_putc(&summary, *msg); + /* record the beginning of contiguous whitespace runs */ + else if (git__isspace(next_character)) { + if(space == NULL) { + space = msg; + space_contains_newline = false; + } + space_contains_newline |= next_character == '\n'; + } + /* the next character is non-space */ + else { + /* process any recorded whitespace */ + if (space) { + if(space_contains_newline) + git_buf_putc(&summary, ' '); /* if the space contains a newline, collapse to ' ' */ + else + git_buf_put(&summary, space, (msg - space)); /* otherwise copy it */ + space = NULL; + } + /* copy the next character */ + git_buf_putc(&summary, next_character); + } } commit->summary = git_buf_detach(&summary); diff --git a/tests/commit/commit.c b/tests/commit/commit.c index f5461cfd3..81aaf80d3 100644 --- a/tests/commit/commit.c +++ b/tests/commit/commit.c @@ -70,12 +70,16 @@ void test_commit_commit__summary(void) assert_commit_summary("Trimmed leading&trailing newlines", "\n\nTrimmed leading&trailing newlines\n\n"); assert_commit_summary("First paragraph only", "\nFirst paragraph only\n\n(There are more!)"); assert_commit_summary("First paragraph with unwrapped trailing\tlines", "\nFirst paragraph\nwith unwrapped\ntrailing\tlines\n\n(Yes, unwrapped!)"); - assert_commit_summary("\tLeading \ttabs", "\tLeading\n\ttabs\n\nis preserved"); - assert_commit_summary(" Leading Spaces", " Leading\n Spaces\n\nare preserved"); + assert_commit_summary("\tLeading tabs", "\tLeading\n\ttabs\n\nare preserved"); /* tabs around newlines are collapsed down to a single space */ + assert_commit_summary(" Leading Spaces", " Leading\n Spaces\n\nare preserved"); /* spaces around newlines are collapsed down to a single space */ assert_commit_summary("Trailing tabs\tare removed", "Trailing tabs\tare removed\t\t"); assert_commit_summary("Trailing spaces are removed", "Trailing spaces are removed "); assert_commit_summary("Trailing tabs", "Trailing tabs\t\n\nare removed"); assert_commit_summary("Trailing spaces", "Trailing spaces \n\nare removed"); + assert_commit_summary("Newlines are replaced by spaces", "Newlines\nare\nreplaced by spaces\n"); + assert_commit_summary(" Spaces after newlines are collapsed", "\n Spaces after newlines\n are\n collapsed\n "); /* newlines at the very beginning are ignored and not collapsed */ + assert_commit_summary(" Spaces before newlines are collapsed", " \nSpaces before newlines \nare \ncollapsed \n"); + assert_commit_summary(" Spaces around newlines are collapsed", " \n Spaces around newlines \n are \n collapsed \n "); assert_commit_summary("", ""); assert_commit_summary("", " "); assert_commit_summary("", "\n"); diff --git a/tests/commit/write.c b/tests/commit/write.c index ee9eb8237..176965cbd 100644 --- a/tests/commit/write.c +++ b/tests/commit/write.c @@ -8,7 +8,7 @@ static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd"; static const char *root_commit_message = "This is a root commit\n\ This is a root commit and should be the only one in this branch\n"; static const char *root_reflog_message = "commit (initial): This is a root commit \ - This is a root commit and should be the only one in this branch"; +This is a root commit and should be the only one in this branch"; static char *head_old; static git_reference *head, *branch; static git_commit *commit; From 790012ce782e2b97e90e52ae9fdaff098b56b999 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 4 Nov 2015 16:16:51 -0500 Subject: [PATCH 242/450] submodule: test updating a submodule w/ a path Test that `git_submodule_update` can handle a submodule that is freshly cloned and has a path differing from its name. --- .../resources/submodule_with_path/.gitmodules | Bin 0 -> 67 bytes .../submodule_with_path/.gitted/HEAD | Bin 0 -> 23 bytes .../submodule_with_path/.gitted/config | Bin 0 -> 157 bytes .../submodule_with_path/.gitted/index | Bin 0 -> 253 bytes .../18/372280a56a54340fa600aa91315065c6c4c693 | Bin 0 -> 85 bytes .../36/683131578275f6a8fd1c539e0d5da0d8adff26 | Bin 0 -> 63 bytes .../89/ca686bb21bfb75dda99a02313831a0c418f921 | Bin 0 -> 161 bytes .../b1/620ef2628d10416a84d19c783e33dc4556c9c3 | Bin 0 -> 86 bytes .../ba/34c47dc9d3d0b1bb335b45c9d26ba1f0fc90c7 | Bin 0 -> 68 bytes .../c8/4bf57ba2254dba216ab5c6eb1a19fe8bd0e0d6 | Bin 0 -> 127 bytes .../d5/45fc6b40ec9e67332b6a1d2dedcbdb1bffeb6b | Bin 0 -> 51 bytes .../.gitted/refs/heads/master | Bin 0 -> 41 bytes tests/submodule/submodule_helpers.c | 15 ++++++ tests/submodule/submodule_helpers.h | 1 + tests/submodule/update.c | 48 ++++++++++++++++++ 15 files changed, 64 insertions(+) create mode 100644 tests/resources/submodule_with_path/.gitmodules create mode 100644 tests/resources/submodule_with_path/.gitted/HEAD create mode 100644 tests/resources/submodule_with_path/.gitted/config create mode 100644 tests/resources/submodule_with_path/.gitted/index create mode 100644 tests/resources/submodule_with_path/.gitted/objects/18/372280a56a54340fa600aa91315065c6c4c693 create mode 100644 tests/resources/submodule_with_path/.gitted/objects/36/683131578275f6a8fd1c539e0d5da0d8adff26 create mode 100644 tests/resources/submodule_with_path/.gitted/objects/89/ca686bb21bfb75dda99a02313831a0c418f921 create mode 100644 tests/resources/submodule_with_path/.gitted/objects/b1/620ef2628d10416a84d19c783e33dc4556c9c3 create mode 100644 tests/resources/submodule_with_path/.gitted/objects/ba/34c47dc9d3d0b1bb335b45c9d26ba1f0fc90c7 create mode 100644 tests/resources/submodule_with_path/.gitted/objects/c8/4bf57ba2254dba216ab5c6eb1a19fe8bd0e0d6 create mode 100644 tests/resources/submodule_with_path/.gitted/objects/d5/45fc6b40ec9e67332b6a1d2dedcbdb1bffeb6b create mode 100644 tests/resources/submodule_with_path/.gitted/refs/heads/master diff --git a/tests/resources/submodule_with_path/.gitmodules b/tests/resources/submodule_with_path/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..ba34c47dc9d3d0b1bb335b45c9d26ba1f0fc90c7 GIT binary patch literal 67 zcmazpE=|hKPbtkwRZuEPEiNfaEy!1j<>D+zEXh!?RmjOq(uWCiah4Y40LAq5;6i%o HnI&8RAAA}oMPd`mF*(s_9TLV@qN9ge_KWI9)sd8MlRa2 zXaRqt8cCf_(;P!4#v%zjWnjY?9-x23vH`PVKK2BT);-swYt{-=eU0H4(pLN73O4eu U^rVDm@k>7$ViLcSFO^&T0N7wQ1ONa4 literal 0 HcmV?d00001 diff --git a/tests/resources/submodule_with_path/.gitted/index b/tests/resources/submodule_with_path/.gitted/index new file mode 100644 index 0000000000000000000000000000000000000000..a740b4b91e5595fef2e2b0983480d834da7bdf9e GIT binary patch literal 253 zcmZ?q402{*U|<5_Fsqa(Ak7eFmF@zf5ukAig3aK(%j8Jy$;%fu?lz8gJ$Wg6;fFsH zjx%uUrDvAp=BJeAq!vRJ1I-HqF@ba>l71lb0Sb3n{M*m7-alcwygN_jzOR4$laTTN z2A-VEB>j@q;*z4&f_#YbkRVrAAj_1&NWqY6V-nw|q+S8Xtd@&&D(sB!xQ3lP4AjD4 ys9?Z#)%8!d!<%{O#@bo3x^GY4mj3@b`_cPT!M_=Km$h&Qx=xo~|8AY~c_{$IhD+W6 literal 0 HcmV?d00001 diff --git a/tests/resources/submodule_with_path/.gitted/objects/18/372280a56a54340fa600aa91315065c6c4c693 b/tests/resources/submodule_with_path/.gitted/objects/18/372280a56a54340fa600aa91315065c6c4c693 new file mode 100644 index 0000000000000000000000000000000000000000..d9b4313e187ed50eb03ad8c99a11ae9bbfa14879 GIT binary patch literal 85 zcmV-b0IL6Z0V^p=O;s?nWH2-^Ff%bx&`ZxO$<0qG%}Fh0Fv~DB3~ws^w&Jf$@I2nw r1vl3IS2HmH0)?E+B!;W5f3h9k%u6@c&XU!Ad-}HY|JT_795W{55>P0GzrDa}b$P%23+E-6Ya$XANx;w(rk$xyIW$jMC7 VhY53WmKNmz#q{(LLI7Du7m_mY8dU%Q literal 0 HcmV?d00001 diff --git a/tests/resources/submodule_with_path/.gitted/objects/89/ca686bb21bfb75dda99a02313831a0c418f921 b/tests/resources/submodule_with_path/.gitted/objects/89/ca686bb21bfb75dda99a02313831a0c418f921 new file mode 100644 index 0000000000000000000000000000000000000000..7c1af6645b3678f7cddabf69568fbe93b73c26c7 GIT binary patch literal 161 zcmV;S0ABxi0i}-14Z<)GL^-<(HvsbbvvDMZxX=L&*z1jwgW1S2qJ2yaoM!Z-HyV;! zx2~;&Q*X>V16sq2MH>qk5167aFw+zrJ6FhufHad+dusgZnxfB3m~yf<_%dP`IS_J& z1oOf%7sIBYO7Ff((~t5=t?1_}^^ljo@}R$VuNyTvWa$@@deh)NB1W*F&n6h71|3H` P|1qIN_CtLE2lz_SuD?t; literal 0 HcmV?d00001 diff --git a/tests/resources/submodule_with_path/.gitted/objects/b1/620ef2628d10416a84d19c783e33dc4556c9c3 b/tests/resources/submodule_with_path/.gitted/objects/b1/620ef2628d10416a84d19c783e33dc4556c9c3 new file mode 100644 index 0000000000000000000000000000000000000000..4475582597be6c70d7350a9d6e9dd3106b6d7d31 GIT binary patch literal 86 zcmV-c0IC0Y0V^p=O;s?nWH2-^Ff%bx&`ZxO$<0qG%}Fh0*ky90_T=RY8+RK=yPmw1 sz3{`I3CB$gfIuN9Gl}7<>z{0gH}leswX5XNWE?P0GzrDa}b$P%23+E-6Ya$XANx;w(rk$xyIW$jMC7 ahY53WmKNmz#q{*xLVD?$C0qc#AQ=eHq#alQ literal 0 HcmV?d00001 diff --git a/tests/resources/submodule_with_path/.gitted/objects/c8/4bf57ba2254dba216ab5c6eb1a19fe8bd0e0d6 b/tests/resources/submodule_with_path/.gitted/objects/c8/4bf57ba2254dba216ab5c6eb1a19fe8bd0e0d6 new file mode 100644 index 0000000000000000000000000000000000000000..9f664569ce0477eeddf845279556266ded79ab69 GIT binary patch literal 127 zcmV-_0D%8^0i})04Z<)CKsjd$F933WZYm+v3mdS2up4eLK1{#Z}`(UZSY*b+*WvPS`U50JrDb%rS8)7 h(9&9V=y!`00M=Q)Z&t&)Pj75W$|x6&wBra>GJM8mHWQ_@lQg^ J0{~*65=1l57(D<0 literal 0 HcmV?d00001 diff --git a/tests/resources/submodule_with_path/.gitted/refs/heads/master b/tests/resources/submodule_with_path/.gitted/refs/heads/master new file mode 100644 index 0000000000000000000000000000000000000000..4b5a5a21d3e5931df4bfb9c19574711228bc603c GIT binary patch literal 41 vcmV~$!4Uu;2m`Rc(@;=^aVQ@9k6;qn<>91)!+YMV%n~(N7)p90CKc-g@PrD* literal 0 HcmV?d00001 diff --git a/tests/submodule/submodule_helpers.c b/tests/submodule/submodule_helpers.c index cde69d92d..4ff4b4da7 100644 --- a/tests/submodule/submodule_helpers.c +++ b/tests/submodule/submodule_helpers.c @@ -156,6 +156,21 @@ git_repository *setup_fixture_submodule_simple(void) return repo; } +git_repository *setup_fixture_submodule_with_path(void) +{ + git_repository *repo = cl_git_sandbox_init("submodule_with_path"); + + cl_fixture_sandbox("testrepo.git"); + p_mkdir("submodule_with_path/lib", 0777); + p_mkdir("submodule_with_path/lib/testrepo", 0777); + + cl_set_cleanup(cleanup_fixture_submodules, "testrepo.git"); + + cl_git_pass(git_repository_reinit_filesystem(repo, 1)); + + return repo; +} + void assert__submodule_exists( git_repository *repo, const char *name, const char *msg, const char *file, int line) diff --git a/tests/submodule/submodule_helpers.h b/tests/submodule/submodule_helpers.h index 1191ab35b..42b14a7bc 100644 --- a/tests/submodule/submodule_helpers.h +++ b/tests/submodule/submodule_helpers.h @@ -5,6 +5,7 @@ extern git_repository *setup_fixture_submodules(void); extern git_repository *setup_fixture_submod2(void); extern git_repository *setup_fixture_submodule_simple(void); extern git_repository *setup_fixture_super(void); +extern git_repository *setup_fixture_submodule_with_path(void); extern unsigned int get_submodule_status(git_repository *, const char *); diff --git a/tests/submodule/update.c b/tests/submodule/update.c index 40d24d0a7..cbd519d81 100644 --- a/tests/submodule/update.c +++ b/tests/submodule/update.c @@ -131,6 +131,53 @@ void test_submodule_update__update_submodule(void) git_submodule_free(sm); } +void test_submodule_update__update_submodule_with_path(void) +{ + git_submodule *sm; + git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT; + unsigned int submodule_status = 0; + struct update_submodule_cb_payload update_payload = { 0 }; + + g_repo = setup_fixture_submodule_with_path(); + + update_options.checkout_opts.progress_cb = checkout_progress_cb; + update_options.checkout_opts.progress_payload = &update_payload; + + update_options.fetch_opts.callbacks.update_tips = update_tips; + update_options.fetch_opts.callbacks.payload = &update_payload; + + /* get the submodule */ + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); + + /* verify the initial state of the submodule */ + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED)); + cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS_IN_CONFIG | + GIT_SUBMODULE_STATUS_WD_UNINITIALIZED); + + /* initialize and update the submodule */ + cl_git_pass(git_submodule_init(sm, 0)); + cl_git_pass(git_submodule_update(sm, 0, &update_options)); + + /* verify state */ + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED)); + cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS_IN_CONFIG | + GIT_SUBMODULE_STATUS_IN_WD); + + cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + cl_assert(git_oid_streq(git_submodule_wd_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + + /* verify that the expected callbacks have been called. */ + cl_assert_equal_i(1, update_payload.checkout_progress_called); + cl_assert_equal_i(1, update_payload.update_tips_called); + + git_submodule_free(sm); +} + void test_submodule_update__update_and_init_submodule(void) { git_submodule *sm; @@ -390,3 +437,4 @@ void test_submodule_update__can_force_update(void) git_object_free(branch_commit); git_reference_free(branch_reference); } + From f4b02671220e35fd0c20b915ebe6359b89440f54 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 4 Nov 2015 16:17:51 -0500 Subject: [PATCH 243/450] submodule: reload HEAD/index after reading config Reload the HEAD and index data for a submodule after reading the configuration. The configuration may specify a `path`, so we must update HEAD and index data with that path in mind. --- src/submodule.c | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/src/submodule.c b/src/submodule.c index 3fd338843..1148f8790 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -1423,7 +1423,6 @@ static int submodule_update_head(git_submodule *submodule) return 0; } - int git_submodule_reload(git_submodule *sm, int force) { int error = 0; @@ -1433,35 +1432,30 @@ int git_submodule_reload(git_submodule *sm, int force) assert(sm); - /* refresh index data */ - if ((error = submodule_update_index(sm)) < 0) - return error; + if (!git_repository_is_bare(sm->repo)) { + /* refresh config data */ + mods = gitmodules_snapshot(sm->repo); + if (mods != NULL) { + error = submodule_read_config(sm, mods); + git_config_free(mods); - /* refresh HEAD tree data */ - if ((error = submodule_update_head(sm)) < 0) - return error; - - /* done if bare */ - if (git_repository_is_bare(sm->repo)) - return error; - - /* refresh config data */ - mods = gitmodules_snapshot(sm->repo); - if (mods != NULL) { - error = submodule_read_config(sm, mods); - git_config_free(mods); - - if (error < 0) { - return error; + if (error < 0) + return error; } + + /* refresh wd data */ + sm->flags &= + ~(GIT_SUBMODULE_STATUS_IN_WD | + GIT_SUBMODULE_STATUS__WD_OID_VALID | + GIT_SUBMODULE_STATUS__WD_FLAGS); + + error = submodule_load_from_wd_lite(sm); } - /* refresh wd data */ - sm->flags &= - ~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID | - GIT_SUBMODULE_STATUS__WD_FLAGS); + if (error == 0 && (error = submodule_update_index(sm)) == 0) + error = submodule_update_head(sm); - return submodule_load_from_wd_lite(sm); + return error; } static void submodule_copy_oid_maybe( From 1314af8d63ca5d12af585e68ae0334f68c0b980e Mon Sep 17 00:00:00 2001 From: nulltoken Date: Tue, 26 Aug 2014 13:51:37 +0200 Subject: [PATCH 244/450] Failing test for case sensitive conflicts in the index --- tests/index/conflicts.c | 91 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/tests/index/conflicts.c b/tests/index/conflicts.c index b7a2456eb..8e94cd441 100644 --- a/tests/index/conflicts.c +++ b/tests/index/conflicts.c @@ -342,3 +342,94 @@ void test_index_conflicts__partial(void) cl_assert(conflict_entry[1] == NULL); cl_assert(conflict_entry[2] == NULL); } + +void test_index_conflicts__case_matters(void) +{ + const git_index_entry *conflict_entry[3]; + git_oid oid; + const char *upper_case = "DIFFERS-IN-CASE.TXT"; + const char *mixed_case = "Differs-In-Case.txt"; + const char *correct_case; + bool ignorecase = cl_repo_get_bool(repo, "core.ignorecase"); + + git_index_entry ancestor_entry, our_entry, their_entry; + + memset(&ancestor_entry, 0x0, sizeof(git_index_entry)); + memset(&our_entry, 0x0, sizeof(git_index_entry)); + memset(&their_entry, 0x0, sizeof(git_index_entry)); + + ancestor_entry.path = upper_case; + GIT_IDXENTRY_STAGE_SET(&ancestor_entry, GIT_INDEX_STAGE_ANCESTOR); + git_oid_fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID); + ancestor_entry.mode = GIT_FILEMODE_BLOB; + + our_entry.path = upper_case; + GIT_IDXENTRY_STAGE_SET(&our_entry, GIT_INDEX_STAGE_OURS); + git_oid_fromstr(&our_entry.id, CONFLICTS_ONE_OUR_OID); + our_entry.mode = GIT_FILEMODE_BLOB; + + their_entry.path = upper_case; + GIT_IDXENTRY_STAGE_SET(&their_entry, GIT_INDEX_STAGE_THEIRS); + git_oid_fromstr(&their_entry.id, CONFLICTS_ONE_THEIR_OID); + their_entry.mode = GIT_FILEMODE_BLOB; + + cl_git_pass(git_index_conflict_add(repo_index, + &ancestor_entry, &our_entry, &their_entry)); + + ancestor_entry.path = mixed_case; + GIT_IDXENTRY_STAGE_SET(&ancestor_entry, GIT_INDEX_STAGE_ANCESTOR); + git_oid_fromstr(&ancestor_entry.id, CONFLICTS_TWO_ANCESTOR_OID); + ancestor_entry.mode = GIT_FILEMODE_BLOB; + + our_entry.path = mixed_case; + GIT_IDXENTRY_STAGE_SET(&ancestor_entry, GIT_INDEX_STAGE_ANCESTOR); + git_oid_fromstr(&our_entry.id, CONFLICTS_TWO_OUR_OID); + ancestor_entry.mode = GIT_FILEMODE_BLOB; + + their_entry.path = mixed_case; + GIT_IDXENTRY_STAGE_SET(&their_entry, GIT_INDEX_STAGE_THEIRS); + git_oid_fromstr(&their_entry.id, CONFLICTS_TWO_THEIR_OID); + their_entry.mode = GIT_FILEMODE_BLOB; + + cl_git_pass(git_index_conflict_add(repo_index, + &ancestor_entry, &our_entry, &their_entry)); + + cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1], + &conflict_entry[2], repo_index, upper_case)); + + /* + * We inserted with mixed case last, so on a case-insensitive + * fs we should get the mixed case. + */ + if (ignorecase) + correct_case = mixed_case; + else + correct_case = upper_case; + + cl_assert_equal_s(correct_case, conflict_entry[0]->path); + git_oid_fromstr(&oid, ignorecase ? CONFLICTS_TWO_ANCESTOR_OID : CONFLICTS_ONE_ANCESTOR_OID); + cl_assert_equal_oid(&oid, &conflict_entry[0]->id); + + cl_assert_equal_s(correct_case, conflict_entry[1]->path); + git_oid_fromstr(&oid, ignorecase ? CONFLICTS_TWO_OUR_OID : CONFLICTS_ONE_OUR_OID); + cl_assert_equal_oid(&oid, &conflict_entry[1]->id); + + cl_assert_equal_s(correct_case, conflict_entry[2]->path); + git_oid_fromstr(&oid, ignorecase ? CONFLICTS_TWO_THEIR_OID : CONFLICTS_ONE_THEIR_OID); + cl_assert_equal_oid(&oid, &conflict_entry[2]->id); + + cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1], + &conflict_entry[2], repo_index, mixed_case)); + + cl_assert_equal_s(mixed_case, conflict_entry[0]->path); + git_oid_fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID); + cl_assert_equal_oid(&oid, &conflict_entry[0]->id); + + cl_assert_equal_s(mixed_case, conflict_entry[1]->path); + git_oid_fromstr(&oid, CONFLICTS_TWO_OUR_OID); + cl_assert_equal_oid(&oid, &conflict_entry[1]->id); + + cl_assert_equal_s(mixed_case, conflict_entry[2]->path); + git_oid_fromstr(&oid, CONFLICTS_TWO_THEIR_OID); + cl_assert_equal_oid(&oid, &conflict_entry[2]->id); +} From 16604d74697cff526b0fc1cd2b03bedd56641059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 11 Nov 2015 00:36:15 +0100 Subject: [PATCH 245/450] index: correctly report which conflict stage has a wrong filemode When we're at offset 'i', we're dealing with the 'i+1' stage, since conflicts start at 1. --- src/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.c b/src/index.c index dcf46fe50..f9fff27ba 100644 --- a/src/index.c +++ b/src/index.c @@ -1691,7 +1691,7 @@ int git_index_conflict_add(git_index *index, for (i = 0; i < 3; i++) { if (entries[i] && !valid_filemode(entries[i]->mode)) { giterr_set(GITERR_INDEX, "invalid filemode for stage %d entry", - i); + i + 1); return -1; } } From ad8509ef9faeb1db0693eeb1af98aa1fdad0b874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 12 Nov 2015 11:54:06 +0100 Subject: [PATCH 246/450] index: overwrite the path when inserting conflicts When we insert a conflict in a case-insensitive index, accept the new entry's path as the correct case instead of leaving the path we already had. This puts `git_index_conflict_add()` on the same level as `git_index_add()` in this respect. --- CHANGELOG.md | 9 +++++---- src/index.c | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dec40e49e..359e78dbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,10 +41,11 @@ v0.23 + 1 with which to implement the transactional/atomic semantics for the configuration backend. -* `git_index_add` will now use the case as provided by the caller on - case insensitive systems. Previous versions would keep the case as - it existed in the index. This does not affect the higher-level - `git_index_add_bypath` or `git_index_add_frombuffer` functions. +* `git_index_add` and `git_index_conflict_add()` will now use the case + as provided by the caller on case insensitive systems. Previous + versions would keep the case as it existed in the index. This does + not affect the higher-level `git_index_add_bypath` or + `git_index_add_frombuffer` functions. * The `notify_payload` field of `git_diff_options` was renamed to `payload` to reflect that it's also the payload for the new progress callback. diff --git a/src/index.c b/src/index.c index f9fff27ba..d3b8afd39 100644 --- a/src/index.c +++ b/src/index.c @@ -1718,7 +1718,7 @@ int git_index_conflict_add(git_index *index, /* Make sure stage is correct */ GIT_IDXENTRY_STAGE_SET(entries[i], i + 1); - if ((ret = index_insert(index, &entries[i], 0, true, true)) < 0) + if ((ret = index_insert(index, &entries[i], 1, true, true)) < 0) goto on_error; entries[i] = NULL; /* don't free if later entry fails */ From b46c7ee5e270cc29e2fc0420a55e347a3242b3b5 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 3 Nov 2015 17:18:00 -0500 Subject: [PATCH 247/450] refs: complain when a directory exists at ref When a (non-empty) directory exists at the reference target location, complain with a more actionable error message. --- src/refdb_fs.c | 5 ++++- tests/refs/create.c | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 6d8c76236..c550ee92a 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -733,8 +733,11 @@ static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char * error = git_filebuf_open(file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE); + if (error == GIT_EDIRECTORY) + giterr_set(GITERR_REFERENCE, "cannot lock ref '%s', there are refs beneath that folder", name); + git_buf_free(&ref_path); - return error; + return error; } static int loose_commit(git_filebuf *file, const git_reference *ref) diff --git a/tests/refs/create.c b/tests/refs/create.c index 192551dbd..48194ae3b 100644 --- a/tests/refs/create.c +++ b/tests/refs/create.c @@ -151,6 +151,23 @@ void test_refs_create__propagate_eexists(void) cl_assert(error == GIT_EEXISTS); } +void test_refs_create__existing_dir_propagates_edirectory(void) +{ + git_reference *new_reference, *fail_reference; + git_oid id; + const char *dir_head = "refs/heads/new-dir/new-head", + *fail_head = "refs/heads/new-dir"; + + git_oid_fromstr(&id, current_master_tip); + + /* Create and write the new object id reference */ + cl_git_pass(git_reference_create(&new_reference, g_repo, dir_head, &id, 1, NULL)); + cl_git_fail_with(GIT_EDIRECTORY, + git_reference_create(&fail_reference, g_repo, fail_head, &id, false, NULL)); + + git_reference_free(new_reference); +} + static void test_invalid_name(const char *name) { git_reference *new_reference; From 0a700ee368818da526981b09c1c5bbef8c9b5fb8 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 3 Nov 2015 17:34:54 -0500 Subject: [PATCH 248/450] reflog: error when a directory is at reflog path When a non-empty directory exists and prevents the creation of a reflog, provide a more informative error message. --- src/refdb_fs.c | 15 ++++++++--- tests/refs/reflog/reflog.c | 53 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index c550ee92a..85b5034d6 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -1788,10 +1788,17 @@ static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, co /* If the new branch matches part of the namespace of a previously deleted branch, * there maybe an obsolete/unused directory (or directory hierarchy) in the way. */ - if (git_path_isdir(git_buf_cstr(&path)) && - (git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) { - error = -1; - goto cleanup; + if (git_path_isdir(git_buf_cstr(&path))) { + if ((git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) + error = -1; + else if (git_path_isdir(git_buf_cstr(&path))) { + giterr_set(GITERR_REFERENCE, "cannot create reflog at '%s', there are reflogs beneath that folder", + ref->name); + error = GIT_EDIRECTORY; + } + + if (error != 0) + goto cleanup; } error = git_futils_writebuffer(&buf, git_buf_cstr(&path), O_WRONLY|O_CREAT|O_APPEND, GIT_REFLOG_FILE_MODE); diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c index ba40c76b8..fdb15502c 100644 --- a/tests/refs/reflog/reflog.c +++ b/tests/refs/reflog/reflog.c @@ -143,6 +143,59 @@ void test_refs_reflog_reflog__deleting_the_reference_deletes_the_reflog(void) git_buf_free(&master_log_path); } +void test_refs_reflog_reflog__removes_empty_reflog_dir(void) +{ + git_reference *ref; + git_buf log_path = GIT_BUF_INIT; + git_oid id; + + /* Create a new branch pointing at the HEAD */ + git_oid_fromstr(&id, current_master_tip); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir/new-head", &id, 0, NULL)); + + git_buf_joinpath(&log_path, git_repository_path(g_repo), GIT_REFLOG_DIR); + git_buf_joinpath(&log_path, git_buf_cstr(&log_path), "refs/heads/new-dir/new-head"); + + cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&log_path))); + + cl_git_pass(git_reference_delete(ref)); + git_reference_free(ref); + + /* new ref creation should succeed since new-dir is empty */ + git_oid_fromstr(&id, current_master_tip); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir", &id, 0, NULL)); + git_reference_free(ref); + + git_buf_free(&log_path); +} + +void test_refs_reflog_reflog__fails_gracefully_on_nonempty_reflog_dir(void) +{ + git_reference *ref; + git_buf log_path = GIT_BUF_INIT; + git_oid id; + + /* Create a new branch pointing at the HEAD */ + git_oid_fromstr(&id, current_master_tip); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir/new-head", &id, 0, NULL)); + git_reference_free(ref); + + git_buf_joinpath(&log_path, git_repository_path(g_repo), GIT_REFLOG_DIR); + git_buf_joinpath(&log_path, git_buf_cstr(&log_path), "refs/heads/new-dir/new-head"); + + cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&log_path))); + + /* delete the ref manually, leave the reflog */ + cl_must_pass(p_unlink("testrepo.git/refs/heads/new-dir/new-head")); + + /* new ref creation should fail since new-dir contains reflogs still */ + git_oid_fromstr(&id, current_master_tip); + cl_git_fail_with(GIT_EDIRECTORY, git_reference_create(&ref, g_repo, "refs/heads/new-dir", &id, 0, NULL)); + git_reference_free(ref); + + git_buf_free(&log_path); +} + static void assert_has_reflog(bool expected_result, const char *name) { cl_assert_equal_i(expected_result, git_reference_has_log(g_repo, name)); From de870533e02505f3868403dabd7699da01e4ceda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 2 Oct 2015 03:43:11 +0200 Subject: [PATCH 249/450] settings: add a setter for a custom user-agent --- include/git2/common.h | 3 +++ src/global.c | 5 +++++ src/global.h | 2 ++ src/settings.c | 16 ++++++++++++++++ tests/core/useragent.c | 11 +++++++++++ 5 files changed, 37 insertions(+) create mode 100644 tests/core/useragent.c diff --git a/include/git2/common.h b/include/git2/common.h index 577906115..bf341e79f 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -145,6 +145,7 @@ typedef enum { GIT_OPT_GET_TEMPLATE_PATH, GIT_OPT_SET_TEMPLATE_PATH, GIT_OPT_SET_SSL_CERT_LOCATIONS, + GIT_OPT_SET_USER_AGENT, } git_libgit2_opt_t; /** @@ -240,6 +241,8 @@ typedef enum { * > * > Either parameter may be `NULL`, but not both. * + * * opts(GIT_OPT_SET_USER_AGENT, const char *user_agent) + * * @param option Option key * @param ... value to set the option * @return 0 on success, <0 on failure diff --git a/src/global.c b/src/global.c index 3d37ee4de..0eab8d552 100644 --- a/src/global.c +++ b/src/global.c @@ -31,6 +31,7 @@ static git_mutex *openssl_locks; static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB]; static git_atomic git__n_shutdown_callbacks; static git_atomic git__n_inits; +char *git__user_agent; void git__on_shutdown(git_global_shutdown_fn callback) { @@ -269,6 +270,8 @@ int git_libgit2_shutdown(void) git_win32__crtdbg_stacktrace_cleanup(); git_win32__stack_cleanup(); #endif + + git__free(git__user_agent); } /* Exit the lock */ @@ -369,6 +372,7 @@ int git_libgit2_shutdown(void) git__global_state_cleanup(ptr); git__free(ptr); + git__free(git__user_agent); pthread_key_delete(_tls_key); git_mutex_free(&git__mwindow_mutex); @@ -423,6 +427,7 @@ int git_libgit2_shutdown(void) git__shutdown(); git__global_state_cleanup(&__state); uninit_ssl(); + git__free(git__user_agent); return 0; } diff --git a/src/global.h b/src/global.h index 37e909ac6..9fdcee573 100644 --- a/src/global.h +++ b/src/global.h @@ -35,4 +35,6 @@ extern void git__on_shutdown(git_global_shutdown_fn callback); extern void git__free_tls_data(void); +extern const char *git_libgit2__user_agent(void); + #endif diff --git a/src/settings.c b/src/settings.c index 2097ca314..030d28537 100644 --- a/src/settings.c +++ b/src/settings.c @@ -57,6 +57,13 @@ static int config_level_to_sysdir(int config_level) return val; } +extern char *git__user_agent; + +const char *git_libgit2__user_agent() +{ + return git__user_agent; +} + int git_libgit2_opts(int key, ...) { int error = 0; @@ -152,6 +159,15 @@ int git_libgit2_opts(int key, ...) giterr_set(GITERR_NET, "Cannot set certificate locations: OpenSSL is not enabled"); error = -1; #endif + break; + case GIT_OPT_SET_USER_AGENT: + git__free(git__user_agent); + git__user_agent = git__strdup(va_arg(ap, const char *)); + if (!git__user_agent) { + giterr_set_oom(); + error = -1; + } + break; } diff --git a/tests/core/useragent.c b/tests/core/useragent.c new file mode 100644 index 000000000..6d06693a8 --- /dev/null +++ b/tests/core/useragent.c @@ -0,0 +1,11 @@ +#include "clar_libgit2.h" +#include "global.h" + +void test_core_useragent__get(void) +{ + const char *custom_name = "super duper git"; + + cl_assert_equal_p(NULL, git_libgit2__user_agent()); + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, custom_name)); + cl_assert_equal_s(custom_name, git_libgit2__user_agent()); +} From 94bac76c3f701647b71e3f6dd6f8c434524a4b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 2 Oct 2015 03:46:34 +0200 Subject: [PATCH 250/450] http: use a custom user-agent if the user has set it We still prefix it with "git/1.0" since that's required in many situations, but we replace the area which mentions libgit2. --- src/transports/http.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/transports/http.c b/src/transports/http.c index e5f2b9f28..88b124bf7 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -10,6 +10,7 @@ #include "http_parser.h" #include "buffer.h" #include "netops.h" +#include "global.h" #include "remote.h" #include "smart.h" #include "auth.h" @@ -186,6 +187,16 @@ static int apply_credentials(git_buf *buf, http_subtransport *t) return context->next_token(buf, context, cred); } +static const char *user_agent(void) +{ + const char *custom = git_libgit2__user_agent(); + + if (custom) + return custom; + + return "libgit2 " LIBGIT2_VERSION; +} + static int gen_request( git_buf *buf, http_stream *s, @@ -197,7 +208,7 @@ static int gen_request( git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url); - git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n"); + git_buf_printf(buf, "User-Agent: git/1.0 (%s)\r\n", user_agent()); git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host); if (s->chunked || content_length > 0) { From 1411cb9eb5fa8177a8baa45da89dc35aaa542818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 2 Oct 2015 03:57:14 +0200 Subject: [PATCH 251/450] winhttp: use a custom user-agent if the user has set it We also keep the "git/1.0" prefix in order to maintain compatibility with hosters. --- src/transports/winhttp.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index b364e906e..77d939bd3 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -15,6 +15,7 @@ #include "smart.h" #include "remote.h" #include "repository.h" +#include "global.h" #include #include @@ -567,12 +568,28 @@ static int winhttp_close_connection(winhttp_subtransport *t) return ret; } +static int user_agent(git_buf *ua) +{ + const char *custom = git_libgit2__user_agent(); + + git_buf_clear(ua); + git_buf_PUTS(ua, "git/1.0 ("); + + if (custom) + git_buf_puts(ua, custom); + else + git_buf_PUTS(ua, "libgit2 " LIBGIT2_VERSION); + + return git_buf_putc(ua, ')'); +} + static int winhttp_connect( winhttp_subtransport *t) { - wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")"; wchar_t *wide_host; int32_t port; + wchar_t *wide_ua; + git_buf ua = GIT_BUF_INIT; int error = -1; int default_timeout = TIMEOUT_INFINITE; int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT; @@ -590,9 +607,23 @@ static int winhttp_connect( return -1; } + if ((error = user_agent(&ua)) < 0) { + git__free(wide_host); + return error; + } + + if (git__utf8_to_16_alloc(&wide_ua, git_buf_cstr(&ua)) < 0) { + giterr_set(GITERR_OS, "Unable to convert host to wide characters"); + git__free(wide_host); + git_buf_free(&ua); + return -1; + } + + git_buf_free(&ua); + /* Establish session */ t->session = WinHttpOpen( - ua, + wide_ua, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, @@ -628,6 +659,7 @@ on_error: winhttp_close_connection(t); git__free(wide_host); + git__free(wide_ua); return error; } From 027bbaa72145c81b0d57b74bb9cf8fc9cb70db11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 12 Nov 2015 17:20:30 +0100 Subject: [PATCH 252/450] CHANGELOG: add note about custom user-agent --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dec40e49e..08a307932 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ v0.23 + 1 * Symlinks are now followed when locking a file, which can be necessary when multiple worktrees share a base repository. +* You can now set your own user-agent to be sent for HTTP requests by + using the `GIT_OPT_SET_USER_AGENT` with `git_libgit2_opts()`. + ### API additions * `git_config_lock()` has been added, which allow for From 7ff7ca623e9ea8c55cb1dab8ce998dd48c0aeb68 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Thu, 12 Nov 2015 20:51:01 +0100 Subject: [PATCH 253/450] pool: Never return unaligned buffers --- src/pool.c | 24 ++++++++++++++++-------- tests/core/pool.c | 4 ++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/pool.c b/src/pool.c index aab63beee..e519b75bb 100644 --- a/src/pool.c +++ b/src/pool.c @@ -8,7 +8,7 @@ struct git_pool_page { git_pool_page *next; uint32_t size; uint32_t avail; - char data[GIT_FLEX_ARRAY]; + GIT_ALIGN(char data[GIT_FLEX_ARRAY], 8); }; static void *pool_alloc_page(git_pool *pool, uint32_t size); @@ -30,11 +30,8 @@ uint32_t git_pool__system_page_size(void) void git_pool_init(git_pool *pool, uint32_t item_size) { - const uint32_t align_size = sizeof(void *) - 1; assert(pool); - - if (item_size > 1) - item_size = (item_size + align_size) & ~align_size; + assert(item_size >= 1); memset(pool, 0, sizeof(git_pool)); pool->item_size = item_size; @@ -98,15 +95,26 @@ static void *pool_alloc(git_pool *pool, uint32_t size) return ptr; } +static uint32_t alloc_size(git_pool *pool, uint32_t count) +{ + const uint32_t align = sizeof(void *) - 1; + + if (pool->item_size > 1) { + const uint32_t item_size = (pool->item_size + align) & ~align; + return item_size * count; + } + + return (count + align) & ~align; +} + void *git_pool_malloc(git_pool *pool, uint32_t items) { - const uint32_t size = items * pool->item_size; - return pool_alloc(pool, size); + return pool_alloc(pool, alloc_size(pool, items)); } void *git_pool_mallocz(git_pool *pool, uint32_t items) { - const uint32_t size = items * pool->item_size; + const uint32_t size = alloc_size(pool, items); void *ptr = pool_alloc(pool, size); if (ptr) memset(ptr, 0x0, size); diff --git a/tests/core/pool.c b/tests/core/pool.c index f90adfbc3..c43c1db67 100644 --- a/tests/core/pool.c +++ b/tests/core/pool.c @@ -32,7 +32,7 @@ void test_core_pool__1(void) cl_assert(git_pool_malloc(&p, i) != NULL); /* with fixed page size, allocation must end up with these values */ - cl_assert_equal_i(590, git_pool__open_pages(&p)); + cl_assert_equal_i(591, git_pool__open_pages(&p)); git_pool_clear(&p); git_pool_init(&p, 1); @@ -42,7 +42,7 @@ void test_core_pool__1(void) cl_assert(git_pool_malloc(&p, i) != NULL); /* with fixed page size, allocation must end up with these values */ - cl_assert_equal_i(573, git_pool__open_pages(&p)); + cl_assert_equal_i(sizeof(void *) == 8 ? 575 : 573, git_pool__open_pages(&p)); git_pool_clear(&p); } From cb0ff012d3cb9658b88ad4f752046286ba5ea442 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 6 Nov 2015 17:15:35 -0500 Subject: [PATCH 254/450] racy-git: do a single index->workdir diff When examining paths that are racily clean, do a single index->workdir diff over the entirety of the racily clean files, instead of a diff per file. --- src/index.c | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/index.c b/src/index.c index 0903770bb..9eb1289c3 100644 --- a/src/index.c +++ b/src/index.c @@ -18,6 +18,7 @@ #include "ignore.h" #include "blob.h" #include "idxmap.h" +#include "diff.h" #include "git2/odb.h" #include "git2/oid.h" @@ -752,7 +753,9 @@ static int truncate_racily_clean(git_index *index) int error; git_index_entry *entry; git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT; - git_diff *diff; + git_diff *diff = NULL; + git_vector paths = GIT_VECTOR_INIT; + git_diff_delta *delta; /* Nothing to do if there's no repo to talk about */ if (!INDEX_OWNER(index)) @@ -765,21 +768,31 @@ static int truncate_racily_clean(git_index *index) diff_opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE | GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_DISABLE_PATHSPEC_MATCH; git_vector_foreach(&index->entries, i, entry) { if (!is_racy_timestamp(&index->stamp.mtime, entry)) - continue; - - /* TODO: use the (non-fnmatching) filelist iterator */ - diff_opts.pathspec.count = 1; - diff_opts.pathspec.strings = (char **) &entry->path; - - if ((error = git_diff_index_to_workdir(&diff, INDEX_OWNER(index), index, &diff_opts)) < 0) - return error; - - if (git_diff_num_deltas(diff) > 0) - entry->file_size = 0; - - git_diff_free(diff); + git_vector_insert(&paths, (char *)entry->path); } + if (paths.length == 0) + goto done; + + diff_opts.pathspec.count = paths.length; + diff_opts.pathspec.strings = (char **)paths.contents; + + if ((error = git_diff_index_to_workdir(&diff, INDEX_OWNER(index), index, &diff_opts)) < 0) + return error; + + git_vector_foreach(&diff->deltas, i, delta) { + entry = (git_index_entry *)git_index_get_bypath(index, delta->old_file.path, 0); + + /* Ensure that we have a stage 0 for this file (ie, it's not a + * conflict), otherwise smudging it is quite pointless. + */ + if (entry) + entry->file_size = 0; + } + +done: + git_diff_free(diff); + git_vector_free(&paths); return 0; } From d1101263f751a33d70ec585da908bec938f587fd Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 13 Nov 2015 15:32:48 -0500 Subject: [PATCH 255/450] index: don't detect raciness in uptodate entries Keep track of entries that we believe are up-to-date, because we added the index entries since the index was loaded. This prevents us from unnecessarily examining files that we wrote during the cleanup of racy entries (when we smudge racily clean files that have a timestamp newer than or equal to the index's timestamp when we read it). Without keeping track of this, we would examine every file that we just checked out for raciness, since all their timestamps would be newer than the index's timestamp. --- src/index.c | 9 ++++-- tests/index/racy.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/index.c b/src/index.c index 9eb1289c3..b3fb077cc 100644 --- a/src/index.c +++ b/src/index.c @@ -767,7 +767,8 @@ static int truncate_racily_clean(git_index *index) diff_opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE | GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_DISABLE_PATHSPEC_MATCH; git_vector_foreach(&index->entries, i, entry) { - if (!is_racy_timestamp(&index->stamp.mtime, entry)) + if ((entry->flags_extended & GIT_IDXENTRY_UPTODATE) == 0 && + is_racy_timestamp(&index->stamp.mtime, entry)) git_vector_insert(&paths, (char *)entry->path); } @@ -1295,6 +1296,9 @@ static int index_insert( else entry->flags |= GIT_IDXENTRY_NAMEMASK; + /* this entry is now up-to-date and should not be checked for raciness */ + entry->flags_extended |= GIT_IDXENTRY_UPTODATE; + if (git_mutex_lock(&index->lock) < 0) { giterr_set(GITERR_OS, "Unable to acquire index lock"); return -1; @@ -2558,7 +2562,8 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry) if (entry->flags & GIT_IDXENTRY_EXTENDED) { struct entry_long *ondisk_ext; ondisk_ext = (struct entry_long *)ondisk; - ondisk_ext->flags_extended = htons(entry->flags_extended); + ondisk_ext->flags_extended = htons(entry->flags_extended & + GIT_IDXENTRY_EXTENDED_FLAGS); path = ondisk_ext->path; } else diff --git a/tests/index/racy.c b/tests/index/racy.c index df25c851c..6ff12a39a 100644 --- a/tests/index/racy.c +++ b/tests/index/racy.c @@ -145,3 +145,80 @@ void test_index_racy__empty_file_after_smudge(void) git_buf_free(&path); git_diff_free(diff); } + +static void setup_uptodate_files(void) +{ + git_buf path = GIT_BUF_INIT; + git_index *index; + git_index_entry new_entry = {0}; + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "A")); + cl_git_mkfile(path.ptr, "A"); + + /* Put 'A' into the index */ + cl_git_pass(git_index_add_bypath(index, "A")); + + /* Put 'B' into the index */ + new_entry.path = "B"; + new_entry.mode = GIT_FILEMODE_BLOB; + cl_git_pass(git_index_add(index, &new_entry)); + + /* Put 'C' into the index */ + new_entry.path = "C"; + new_entry.mode = GIT_FILEMODE_BLOB; + cl_git_pass(git_index_add_frombuffer(index, &new_entry, "hello!\n", 7)); + + git_index_free(index); + git_buf_free(&path); +} + +void test_index_racy__adding_to_index_is_uptodate(void) +{ + git_index *index; + const git_index_entry *entry; + + setup_uptodate_files(); + + cl_git_pass(git_repository_index(&index, g_repo)); + + /* ensure that they're all uptodate */ + cl_assert((entry = git_index_get_bypath(index, "A", 0))); + cl_assert_equal_i(GIT_IDXENTRY_UPTODATE, (entry->flags_extended & GIT_IDXENTRY_UPTODATE)); + + cl_assert((entry = git_index_get_bypath(index, "B", 0))); + cl_assert_equal_i(GIT_IDXENTRY_UPTODATE, (entry->flags_extended & GIT_IDXENTRY_UPTODATE)); + + cl_assert((entry = git_index_get_bypath(index, "C", 0))); + cl_assert_equal_i(GIT_IDXENTRY_UPTODATE, (entry->flags_extended & GIT_IDXENTRY_UPTODATE)); + + cl_git_pass(git_index_write(index)); + + git_index_free(index); +} + +void test_index_racy__reading_clears_uptodate_bit(void) +{ + git_index *index; + const git_index_entry *entry; + + setup_uptodate_files(); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_index_read(index, true)); + + /* ensure that no files are uptodate */ + cl_assert((entry = git_index_get_bypath(index, "A", 0))); + cl_assert_equal_i(0, (entry->flags_extended & GIT_IDXENTRY_UPTODATE)); + + cl_assert((entry = git_index_get_bypath(index, "B", 0))); + cl_assert_equal_i(0, (entry->flags_extended & GIT_IDXENTRY_UPTODATE)); + + cl_assert((entry = git_index_get_bypath(index, "C", 0))); + cl_assert_equal_i(0, (entry->flags_extended & GIT_IDXENTRY_UPTODATE)); + + git_index_free(index); +} From de999f260ffdf0266bc0109cd71621ecf65cad98 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 13 Nov 2015 15:36:45 -0500 Subject: [PATCH 256/450] checkout::crlf test: don't crash when no idx entry When there's no matching index entry (for whatever reason), don't try to dereference the null return value to get at the id. Otherwise when we break something in the index API, the checkout test crashes for confusing reasons and causes us to step through it in a debugger thinking that we had broken much more than we actually did. --- tests/checkout/crlf.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/checkout/crlf.c b/tests/checkout/crlf.c index 8e77d0845..347c22198 100644 --- a/tests/checkout/crlf.c +++ b/tests/checkout/crlf.c @@ -278,6 +278,7 @@ void test_checkout_crlf__autocrlf_true_index_size_is_filtered_size(void) void test_checkout_crlf__with_ident(void) { git_index *index; + git_index_entry *entry; git_blob *blob; git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_FORCE; @@ -310,14 +311,14 @@ void test_checkout_crlf__with_ident(void) /* check that blobs have $Id$ */ - cl_git_pass(git_blob_lookup(&blob, g_repo, - & git_index_get_bypath(index, "lf.ident", 0)->id)); + cl_assert((entry = git_index_get_bypath(index, "lf.ident", 0))); + cl_git_pass(git_blob_lookup(&blob, g_repo, &entry->id)); cl_assert_equal_s( ALL_LF_TEXT_RAW "\n$Id$\n", git_blob_rawcontent(blob)); git_blob_free(blob); - cl_git_pass(git_blob_lookup(&blob, g_repo, - & git_index_get_bypath(index, "more2.identcrlf", 0)->id)); + cl_assert((entry = git_index_get_bypath(index, "more2.identcrlf", 0))); + cl_git_pass(git_blob_lookup(&blob, g_repo, &entry->id)); cl_assert_equal_s( "\n$Id$\n" MORE_CRLF_TEXT_AS_LF, git_blob_rawcontent(blob)); git_blob_free(blob); From 956f4da897aa0eec3f66f00cb7a4a47914c24059 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 13 Nov 2015 16:30:39 -0500 Subject: [PATCH 257/450] index: test for smudged entries on write only Test that entries are only smudged when we write the index: the entry smudging is to prevent us from updating an index in a way that it would be impossible to tell that an item was racy. Consider when we load an index: any entries that have the same (or newer) timestamp than the index itself are considered racy, and are subject to further scrutiny. If we *save* that index with the same entries that we loaded, then the index would now have a newer timestamp than the entries, and they would no longer be given that additional scrutiny, failing our racy detection! So test that we smudge those entries only on writing the new index, but that we can detect them (in diff) without having to write. --- tests/index/racy.c | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/tests/index/racy.c b/tests/index/racy.c index 6ff12a39a..fc48f07a8 100644 --- a/tests/index/racy.c +++ b/tests/index/racy.c @@ -100,13 +100,13 @@ void test_index_racy__write_index_just_after_file(void) git_index_free(index); } -void test_index_racy__empty_file_after_smudge(void) + +static void setup_race(void) { - git_index *index; - git_diff *diff; git_buf path = GIT_BUF_INIT; - int i, found_race = 0; + git_index *index; const git_index_entry *entry; + int i, found_race = 0; /* Make sure we do have a timestamp */ cl_git_pass(git_repository_index__weakptr(&index, g_repo)); @@ -131,18 +131,46 @@ void test_index_racy__empty_file_after_smudge(void) found_race = 1; break; } - } if (!found_race) cl_fail("failed to find race after 10 attempts"); + git_buf_free(&path); +} + +void test_index_racy__smudges_index_entry_on_save(void) +{ + git_index *index; + const git_index_entry *entry; + + setup_race(); + + /* write the index, which will smudge anything that had the same timestamp + * as the index when the index was loaded. that way future loads of the + * index (with the new timestamp) will know that these files were not + * clean. + */ + + cl_git_pass(git_repository_index__weakptr(&index, g_repo)); + cl_git_pass(git_index_write(index)); + + cl_assert(entry = git_index_get_bypath(index, "A", 0)); cl_assert_equal_i(0, entry->file_size); +} + +void test_index_racy__detects_diff_of_change_in_identical_timestamp(void) +{ + git_index *index; + git_diff *diff; + + cl_git_pass(git_repository_index__weakptr(&index, g_repo)); + + setup_race(); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL)); cl_assert_equal_i(1, git_diff_num_deltas(diff)); - git_buf_free(&path); git_diff_free(diff); } @@ -204,7 +232,7 @@ void test_index_racy__reading_clears_uptodate_bit(void) const git_index_entry *entry; setup_uptodate_files(); - + cl_git_pass(git_repository_index(&index, g_repo)); cl_git_pass(git_index_write(index)); From 27bc41cf1798c8937b323a7fe6c7fe459c70f8c1 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 13 Nov 2015 16:31:51 -0500 Subject: [PATCH 258/450] index: clear uptodate bit on save The uptodate bit should have a lifecycle of a single read->write on the index. Once the index is written, the files within it should be scanned for racy timestamps against the new index timestamp. --- src/index.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/index.c b/src/index.c index b3fb077cc..ed4079665 100644 --- a/src/index.c +++ b/src/index.c @@ -2746,6 +2746,15 @@ static int write_tree_extension(git_index *index, git_filebuf *file) return error; } +static void clear_uptodate(git_index *index) +{ + git_index_entry *entry; + size_t i; + + git_vector_foreach(&index->entries, i, entry) + entry->flags_extended &= ~GIT_IDXENTRY_UPTODATE; +} + static int write_index(git_oid *checksum, git_index *index, git_filebuf *file) { git_oid hash_final; @@ -2785,7 +2794,13 @@ static int write_index(git_oid *checksum, git_index *index, git_filebuf *file) git_oid_cpy(checksum, &hash_final); /* write it at the end of the file */ - return git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ); + if (git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ) < 0) + return -1; + + /* file entries are no longer up to date */ + clear_uptodate(index); + + return 0; } int git_index_entry_stage(const git_index_entry *entry) From c30051f0d0fd6ff74d6e2fbe0fc5b0209ecf8b85 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 16 Nov 2015 18:05:46 -0500 Subject: [PATCH 259/450] racy: ensure git_index_read_tree clears uptodate Ensure that `git_index_read_tree` clears the uptodate bit on files that it modifies. --- tests/index/racy.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/index/racy.c b/tests/index/racy.c index fc48f07a8..ce395316f 100644 --- a/tests/index/racy.c +++ b/tests/index/racy.c @@ -250,3 +250,31 @@ void test_index_racy__reading_clears_uptodate_bit(void) git_index_free(index); } + +void test_index_racy__read_tree_clears_uptodate_bit(void) +{ + git_index *index; + git_tree *tree; + const git_index_entry *entry; + git_oid id; + + setup_uptodate_files(); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_write_tree_to(&id, index, g_repo)); + cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); + cl_git_pass(git_index_read_tree(index, tree)); + + /* ensure that no files are uptodate */ + cl_assert((entry = git_index_get_bypath(index, "A", 0))); + cl_assert_equal_i(0, (entry->flags_extended & GIT_IDXENTRY_UPTODATE)); + + cl_assert((entry = git_index_get_bypath(index, "B", 0))); + cl_assert_equal_i(0, (entry->flags_extended & GIT_IDXENTRY_UPTODATE)); + + cl_assert((entry = git_index_get_bypath(index, "C", 0))); + cl_assert_equal_i(0, (entry->flags_extended & GIT_IDXENTRY_UPTODATE)); + + git_tree_free(tree); + git_index_free(index); +} From 5f32c50683cefaf51fa4f8825abfe533f36fe6f3 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 16 Nov 2015 18:06:52 -0500 Subject: [PATCH 260/450] racy: make git_index_read_index handle raciness Ensure that `git_index_read_index` clears the uptodate bit on files that it modifies. Further, do not propagate the cache from an on-disk index into another on-disk index. Although this should not be done, as `git_index_read_index` is used to bring an in-memory index into another index (that may or may not be on-disk), ensure that we do not accidentally bring in these bits when misused. --- src/index.c | 82 ++++++++++++++++++++++++++++------------------ tests/index/racy.c | 48 +++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 32 deletions(-) diff --git a/src/index.c b/src/index.c index ed4079665..bce16348c 100644 --- a/src/index.c +++ b/src/index.c @@ -1003,20 +1003,11 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out, static void index_entry_cpy( git_index_entry *tgt, - git_index *index, - const git_index_entry *src, - bool update_path) + const git_index_entry *src) { const char *tgt_path = tgt->path; memcpy(tgt, src, sizeof(*tgt)); - - /* keep the existing path buffer, but update the path to the one - * given by the caller, if we trust it. - */ tgt->path = tgt_path; - - if (index->ignore_case && update_path) - memcpy((char *)tgt->path, src->path, strlen(tgt->path)); } static int index_entry_dup( @@ -1024,18 +1015,32 @@ static int index_entry_dup( git_index *index, const git_index_entry *src) { - git_index_entry *entry; - - if (!src) { - *out = NULL; - return 0; - } - - if (index_entry_create(&entry, INDEX_OWNER(index), src->path) < 0) + if (index_entry_create(out, INDEX_OWNER(index), src->path) < 0) return -1; - index_entry_cpy(entry, index, src, false); - *out = entry; + index_entry_cpy(*out, src); + return 0; +} + +static void index_entry_cpy_nocache( + git_index_entry *tgt, + const git_index_entry *src) +{ + git_oid_cpy(&tgt->id, &src->id); + tgt->mode = src->mode; + tgt->flags = src->flags; + tgt->flags_extended = (src->flags_extended & GIT_IDXENTRY_EXTENDED_FLAGS); +} + +static int index_entry_dup_nocache( + git_index_entry **out, + git_index *index, + const git_index_entry *src) +{ + if (index_entry_create(out, INDEX_OWNER(index), src->path) < 0) + return -1; + + index_entry_cpy_nocache(*out, src); return 0; } @@ -1331,8 +1336,13 @@ static int index_insert( * and return it in place of the passed in one. */ else if (existing) { - if (replace) - index_entry_cpy(existing, index, entry, trust_path); + if (replace) { + index_entry_cpy(existing, entry); + + if (trust_path) + memcpy((char *)existing->path, entry->path, strlen(entry->path)); + } + index_entry_free(entry); *entry_ptr = entry = existing; } @@ -1709,9 +1719,12 @@ int git_index_conflict_add(git_index *index, assert (index); - if ((ret = index_entry_dup(&entries[0], index, ancestor_entry)) < 0 || - (ret = index_entry_dup(&entries[1], index, our_entry)) < 0 || - (ret = index_entry_dup(&entries[2], index, their_entry)) < 0) + if ((ancestor_entry && + (ret = index_entry_dup(&entries[0], index, ancestor_entry)) < 0) || + (our_entry && + (ret = index_entry_dup(&entries[1], index, our_entry)) < 0) || + (their_entry && + (ret = index_entry_dup(&entries[2], index, their_entry)) < 0)) goto on_error; /* Validate entries */ @@ -2849,7 +2862,7 @@ static int read_tree_cb( entry->mode == old_entry->mode && git_oid_equal(&entry->id, &old_entry->id)) { - index_entry_cpy(entry, data->index, old_entry, false); + index_entry_cpy(entry, old_entry); entry->flags_extended = 0; } @@ -2974,7 +2987,10 @@ int git_index_read_index( goto done; while (true) { - git_index_entry *add_entry = NULL, *remove_entry = NULL; + git_index_entry + *dup_entry = NULL, + *add_entry = NULL, + *remove_entry = NULL; int diff; if (old_entry && new_entry) @@ -2989,8 +3005,7 @@ int git_index_read_index( if (diff < 0) { remove_entry = (git_index_entry *)old_entry; } else if (diff > 0) { - if ((error = index_entry_dup(&add_entry, index, new_entry)) < 0) - goto done; + dup_entry = (git_index_entry *)new_entry; } else { /* Path and stage are equal, if the OID is equal, keep it to * keep the stat cache data. @@ -2998,13 +3013,16 @@ int git_index_read_index( if (git_oid_equal(&old_entry->id, &new_entry->id)) { add_entry = (git_index_entry *)old_entry; } else { - if ((error = index_entry_dup(&add_entry, index, new_entry)) < 0) - goto done; - + dup_entry = (git_index_entry *)new_entry; remove_entry = (git_index_entry *)old_entry; } } + if (dup_entry) { + if ((error = index_entry_dup_nocache(&add_entry, index, dup_entry)) < 0) + goto done; + } + if (add_entry) { if ((error = git_vector_insert(&new_entries, add_entry)) == 0) INSERT_IN_MAP_EX(index, new_entries_map, add_entry, error); diff --git a/tests/index/racy.c b/tests/index/racy.c index ce395316f..f7440c3af 100644 --- a/tests/index/racy.c +++ b/tests/index/racy.c @@ -278,3 +278,51 @@ void test_index_racy__read_tree_clears_uptodate_bit(void) git_tree_free(tree); git_index_free(index); } + +void test_index_racy__read_index_smudges(void) +{ + git_index *index, *newindex; + const git_index_entry *entry; + + /* if we are reading an index into our new index, ensure that any + * racy entries in the index that we're reading are smudged so that + * we don't propagate their timestamps without further investigation. + */ + setup_race(); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_new(&newindex)); + cl_git_pass(git_index_read_index(newindex, index)); + + cl_assert(entry = git_index_get_bypath(newindex, "A", 0)); + cl_assert_equal_i(0, entry->file_size); + + git_index_free(index); + git_index_free(newindex); +} + +void test_index_racy__read_index_clears_uptodate_bit(void) +{ + git_index *index, *newindex; + const git_index_entry *entry; + git_oid id; + + setup_uptodate_files(); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_new(&newindex)); + cl_git_pass(git_index_read_index(newindex, index)); + + /* ensure that files brought in from the other index are not uptodate */ + cl_assert((entry = git_index_get_bypath(newindex, "A", 0))); + cl_assert_equal_i(0, (entry->flags_extended & GIT_IDXENTRY_UPTODATE)); + + cl_assert((entry = git_index_get_bypath(newindex, "B", 0))); + cl_assert_equal_i(0, (entry->flags_extended & GIT_IDXENTRY_UPTODATE)); + + cl_assert((entry = git_index_get_bypath(newindex, "C", 0))); + cl_assert_equal_i(0, (entry->flags_extended & GIT_IDXENTRY_UPTODATE)); + + git_index_free(index); + git_index_free(newindex); +} From 3eac1037d68a355f77787d7c8e496694715dd28c Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 16 Nov 2015 23:31:19 -0500 Subject: [PATCH 261/450] settings: allow users to set PROGRAMDATA Allow users to set the `git_libgit2_opts` search path for the `GIT_CONFIG_LEVEL_PROGRAMDATA`. Convert `GIT_CONFIG_LEVEL_PROGRAMDATA` to `GIT_SYSDIR_PROGRAMDATA` for setting the configuration. --- include/git2/common.h | 11 ++++++----- src/settings.c | 15 ++++++++++++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/include/git2/common.h b/include/git2/common.h index e687977d5..ee230dfae 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -174,9 +174,9 @@ typedef enum { * * opts(GIT_OPT_GET_SEARCH_PATH, int level, git_buf *buf) * * > Get the search path for a given level of config data. "level" must - * > be one of `GIT_CONFIG_LEVEL_SYSTEM`, `GIT_CONFIG_LEVEL_GLOBAL`, or - * > `GIT_CONFIG_LEVEL_XDG`. The search path is written to the `out` - * > buffer. + * > be one of `GIT_CONFIG_LEVEL_SYSTEM`, `GIT_CONFIG_LEVEL_GLOBAL`, + * > `GIT_CONFIG_LEVEL_XDG`, or `GIT_CONFIG_LEVEL_PROGRAMDATA`. + * > The search path is written to the `out` buffer. * * * opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path) * @@ -188,8 +188,9 @@ typedef enum { * > variables). Use magic path `$PATH` to include the old value * > of the path (if you want to prepend or append, for instance). * > - * > - `level` must be GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_GLOBAL, - * > or GIT_CONFIG_LEVEL_XDG. + * > - `level` must be `GIT_CONFIG_LEVEL_SYSTEM`, + * > `GIT_CONFIG_LEVEL_GLOBAL`, `GIT_CONFIG_LEVEL_XDG`, or + * > `GIT_CONFIG_LEVEL_PROGRAMDATA`. * * * opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, git_otype type, size_t size) * diff --git a/src/settings.c b/src/settings.c index 91bdfd520..da99b59e2 100644 --- a/src/settings.c +++ b/src/settings.c @@ -49,9 +49,18 @@ static int config_level_to_sysdir(int config_level) int val = -1; switch (config_level) { - case GIT_CONFIG_LEVEL_SYSTEM: val = GIT_SYSDIR_SYSTEM; break; - case GIT_CONFIG_LEVEL_XDG: val = GIT_SYSDIR_XDG; break; - case GIT_CONFIG_LEVEL_GLOBAL: val = GIT_SYSDIR_GLOBAL; break; + case GIT_CONFIG_LEVEL_SYSTEM: + val = GIT_SYSDIR_SYSTEM; + break; + case GIT_CONFIG_LEVEL_XDG: + val = GIT_SYSDIR_XDG; + break; + case GIT_CONFIG_LEVEL_GLOBAL: + val = GIT_SYSDIR_GLOBAL; + break; + case GIT_CONFIG_LEVEL_PROGRAMDATA: + val = GIT_SYSDIR_PROGRAMDATA; + break; default: giterr_set( GITERR_INVALID, "Invalid config path selector %d", config_level); From 6f7c411834be0ccd5047f58d39daee1f6ec2edc3 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 17 Nov 2015 08:38:46 -0500 Subject: [PATCH 262/450] config::global: use PROGRAMDATA configuration Query the `GIT_CONFIG_LEVEL_PROGRAMDATA` location when setting it up for tests, in case the test runner has sandboxed it. --- tests/config/global.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/config/global.c b/tests/config/global.c index 1336ef6e7..0ddd204ad 100644 --- a/tests/config/global.c +++ b/tests/config/global.c @@ -68,7 +68,6 @@ void test_config_global__open_xdg(void) void test_config_global__open_programdata(void) { - char *programdata; git_config *cfg; git_repository *repo; git_buf config_path = GIT_BUF_INIT; @@ -77,9 +76,12 @@ void test_config_global__open_programdata(void) if (!cl_getenv("GITTEST_INVASIVE_FS_STRUCTURE")) cl_skip(); - programdata = cl_getenv("PROGRAMDATA"); - cl_git_pass(git_buf_printf(&config_path, "%s/Git", programdata)); - cl_git_pass(p_mkdir(config_path.ptr, 0777)); + cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, + GIT_CONFIG_LEVEL_PROGRAMDATA, &config_path)); + + if (!git_path_isdir(config_path.ptr)) + cl_git_pass(p_mkdir(config_path.ptr, 0777)); + cl_git_pass(git_buf_puts(&config_path, "/config")); cl_git_pass(git_config_open_ondisk(&cfg, config_path.ptr)); From fe96374823b5f944622dfa3b91f460d4753bc935 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 16 Nov 2015 23:34:26 -0500 Subject: [PATCH 263/450] tests: set PROGRAMDATA directory for running tests --- tests/clar_libgit2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/clar_libgit2.c b/tests/clar_libgit2.c index 61442f88b..1bf69c5d4 100644 --- a/tests/clar_libgit2.c +++ b/tests/clar_libgit2.c @@ -549,6 +549,8 @@ void cl_sandbox_set_search_path_defaults(void) GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, sandbox_path); git_libgit2_opts( GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, sandbox_path); + git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_PROGRAMDATA, sandbox_path); } #ifdef GIT_WIN32 From 88638f9b1841a83e82fc5f79bf00f90d40fb426f Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 17 Nov 2015 11:22:01 -0500 Subject: [PATCH 264/450] tests: use out-of-the-way config dir in sandbox Don't put the configuration in a subdir of the sandbox named `config`, lest some tests decide to create their own directory called `config`. Prefix with some underscores for uniqueness. --- tests/clar_libgit2.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/clar_libgit2.c b/tests/clar_libgit2.c index 1bf69c5d4..314d3441e 100644 --- a/tests/clar_libgit2.c +++ b/tests/clar_libgit2.c @@ -541,16 +541,23 @@ void cl_fake_home(void) void cl_sandbox_set_search_path_defaults(void) { - const char *sandbox_path = clar_sandbox_path(); + git_buf path = GIT_BUF_INIT; + + git_buf_joinpath(&path, clar_sandbox_path(), "__config"); + + if (!git_path_exists(path.ptr)) + cl_must_pass(p_mkdir(path.ptr, 0777)); git_libgit2_opts( - GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, sandbox_path); + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr); git_libgit2_opts( - GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, sandbox_path); + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr); git_libgit2_opts( - GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, sandbox_path); + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr); git_libgit2_opts( - GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_PROGRAMDATA, sandbox_path); + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_PROGRAMDATA, path.ptr); + + git_buf_free(&path); } #ifdef GIT_WIN32 From 2ea40fdaac2a3b6ceabb02f17734d14831fd3470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 20 Nov 2015 13:19:23 -0500 Subject: [PATCH 265/450] repository: distinguish sequencer cherry-pick and revert These are not quite like their plain counterparts and require special handling. --- include/git2/repository.h | 2 ++ src/refs.h | 5 +++++ src/repository.c | 13 ++++++++++--- tests/repo/state.c | 18 ++++++++++++++++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/include/git2/repository.h b/include/git2/repository.h index cf268ef85..85b7e6861 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -675,7 +675,9 @@ typedef enum { GIT_REPOSITORY_STATE_NONE, GIT_REPOSITORY_STATE_MERGE, GIT_REPOSITORY_STATE_REVERT, + GIT_REPOSITORY_STATE_REVERT_SEQUENCE, GIT_REPOSITORY_STATE_CHERRYPICK, + GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE, GIT_REPOSITORY_STATE_BISECT, GIT_REPOSITORY_STATE_REBASE, GIT_REPOSITORY_STATE_REBASE_INTERACTIVE, diff --git a/src/refs.h b/src/refs.h index f78ea06b0..fda9532de 100644 --- a/src/refs.h +++ b/src/refs.h @@ -44,6 +44,11 @@ #define GIT_REBASE_APPLY_APPLYING_FILE GIT_REBASE_APPLY_DIR "applying" #define GIT_REFS_HEADS_MASTER_FILE GIT_REFS_HEADS_DIR "master" +#define GIT_SEQUENCER_DIR "sequencer/" +#define GIT_SEQUENCER_HEAD_FILE GIT_SEQUENCER_DIR "head" +#define GIT_SEQUENCER_OPTIONS_FILE GIT_SEQUENCER_DIR "options" +#define GIT_SEQUENCER_TODO_FILE GIT_SEQUENCER_DIR "todo" + #define GIT_STASH_FILE "stash" #define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE diff --git a/src/repository.c b/src/repository.c index c61d0e4f0..6234cd595 100644 --- a/src/repository.c +++ b/src/repository.c @@ -2222,11 +2222,17 @@ int git_repository_state(git_repository *repo) state = GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE; else if (git_path_contains_file(&repo_path, GIT_MERGE_HEAD_FILE)) state = GIT_REPOSITORY_STATE_MERGE; - else if(git_path_contains_file(&repo_path, GIT_REVERT_HEAD_FILE)) + else if (git_path_contains_file(&repo_path, GIT_REVERT_HEAD_FILE)) { state = GIT_REPOSITORY_STATE_REVERT; - else if(git_path_contains_file(&repo_path, GIT_CHERRYPICK_HEAD_FILE)) + if (git_path_contains_file(&repo_path, GIT_SEQUENCER_TODO_FILE)) { + state = GIT_REPOSITORY_STATE_REVERT_SEQUENCE; + } + } else if (git_path_contains_file(&repo_path, GIT_CHERRYPICK_HEAD_FILE)) { state = GIT_REPOSITORY_STATE_CHERRYPICK; - else if(git_path_contains_file(&repo_path, GIT_BISECT_LOG_FILE)) + if (git_path_contains_file(&repo_path, GIT_SEQUENCER_TODO_FILE)) { + state = GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE; + } + } else if (git_path_contains_file(&repo_path, GIT_BISECT_LOG_FILE)) state = GIT_REPOSITORY_STATE_BISECT; git_buf_free(&repo_path); @@ -2271,6 +2277,7 @@ static const char *state_files[] = { GIT_BISECT_LOG_FILE, GIT_REBASE_MERGE_DIR, GIT_REBASE_APPLY_DIR, + GIT_SEQUENCER_DIR, }; int git_repository_state_cleanup(git_repository *repo) diff --git a/tests/repo/state.c b/tests/repo/state.c index bf2633c17..7f20eebe8 100644 --- a/tests/repo/state.c +++ b/tests/repo/state.c @@ -57,6 +57,15 @@ void test_repo_state__revert(void) assert_repo_state(GIT_REPOSITORY_STATE_NONE); } +void test_repo_state__revert_sequence(void) +{ + setup_simple_state(GIT_REVERT_HEAD_FILE); + setup_simple_state(GIT_SEQUENCER_TODO_FILE); + assert_repo_state(GIT_REPOSITORY_STATE_REVERT_SEQUENCE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); +} + void test_repo_state__cherry_pick(void) { setup_simple_state(GIT_CHERRYPICK_HEAD_FILE); @@ -65,6 +74,15 @@ void test_repo_state__cherry_pick(void) assert_repo_state(GIT_REPOSITORY_STATE_NONE); } +void test_repo_state__cherrypick_sequence(void) +{ + setup_simple_state(GIT_CHERRYPICK_HEAD_FILE); + setup_simple_state(GIT_SEQUENCER_TODO_FILE); + assert_repo_state(GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); +} + void test_repo_state__bisect(void) { setup_simple_state(GIT_BISECT_LOG_FILE); From 87428c556f36066040a79ffab416939a0ff9bae1 Mon Sep 17 00:00:00 2001 From: Jacques Germishuys Date: Fri, 20 Nov 2015 20:48:51 +0200 Subject: [PATCH 266/450] Fix some warnings --- tests/checkout/crlf.c | 2 +- tests/core/filebuf.c | 2 +- tests/diff/notify.c | 1 + tests/index/racy.c | 1 - 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/checkout/crlf.c b/tests/checkout/crlf.c index 347c22198..d467eaadd 100644 --- a/tests/checkout/crlf.c +++ b/tests/checkout/crlf.c @@ -278,7 +278,7 @@ void test_checkout_crlf__autocrlf_true_index_size_is_filtered_size(void) void test_checkout_crlf__with_ident(void) { git_index *index; - git_index_entry *entry; + const git_index_entry *entry; git_blob *blob; git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_FORCE; diff --git a/tests/core/filebuf.c b/tests/core/filebuf.c index 93aaed759..04a380b20 100644 --- a/tests/core/filebuf.c +++ b/tests/core/filebuf.c @@ -233,7 +233,7 @@ void test_core_filebuf__hidden_file(void) void test_core_filebuf__detects_directory(void) { - git_filebuf file = GIT_FILEBUF_INIT, fail = GIT_FILEBUF_INIT; + git_filebuf file = GIT_FILEBUF_INIT; cl_must_pass(p_mkdir("foo", 0777)); cl_git_fail_with(GIT_EDIRECTORY, git_filebuf_open(&file, "foo", 0, 0666)); diff --git a/tests/diff/notify.c b/tests/diff/notify.c index 74abbc93b..653512795 100644 --- a/tests/diff/notify.c +++ b/tests/diff/notify.c @@ -235,6 +235,7 @@ static int progress_abort_diff( const char *new_path, void *payload) { + GIT_UNUSED(diff_so_far); GIT_UNUSED(old_path); GIT_UNUSED(new_path); GIT_UNUSED(payload); diff --git a/tests/index/racy.c b/tests/index/racy.c index f7440c3af..862c5ab7e 100644 --- a/tests/index/racy.c +++ b/tests/index/racy.c @@ -305,7 +305,6 @@ void test_index_racy__read_index_clears_uptodate_bit(void) { git_index *index, *newindex; const git_index_entry *entry; - git_oid id; setup_uptodate_files(); From eb11fac629aa6acee934fc310621ed9a50a200fa Mon Sep 17 00:00:00 2001 From: Jacques Germishuys Date: Fri, 20 Nov 2015 18:57:13 +0200 Subject: [PATCH 267/450] Detect stat's structure --- CMakeLists.txt | 21 ++++++++++++++++++--- src/fileops.c | 14 ++++++-------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0537b19d1..0b7a03c67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,9 +86,20 @@ IF (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") OPTION( USE_OPENSSL "Link with and use openssl library" ON ) ENDIF() -CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec sys/stat.h - HAVE_STRUCT_STAT_NSEC LANGUAGE C) -IF(HAVE_STRUCT_STAT_NSEC OR WIN32) +CHECK_STRUCT_HAS_MEMBER ("struct stat" st_atim "sys/types.h;sys/stat.h" + HAVE_STRUCT_STAT_ST_ATIM LANGUAGE C) +CHECK_STRUCT_HAS_MEMBER ("struct stat" st_atimespec "sys/types.h;sys/stat.h" + HAVE_STRUCT_STAT_ST_ATIMESPEC LANGUAGE C) + +IF (HAVE_STRUCT_STAT_ST_ATIM) + CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec sys/stat.h + HAVE_STRUCT_STAT_NSEC LANGUAGE C) +ELSEIF (HAVE_STRUCT_STAT_ST_ATIMESPEC) + CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtimespec.tv_nsec sys/stat.h + HAVE_STRUCT_STAT_NSEC LANGUAGE C) +ENDIF() + +IF (HAVE_STRUCT_STAT_NSEC OR WIN32) OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" OFF ) ENDIF() @@ -527,6 +538,10 @@ IF (USE_NSEC) ADD_DEFINITIONS(-DGIT_USE_NSEC) ENDIF() +IF (HAVE_STRUCT_STAT_ST_ATIMESPEC) + ADD_DEFINITIONS(-DGIT_USE_STAT_ATIMESPEC) +ENDIF() + ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64) # Collect sourcefiles diff --git a/src/fileops.c b/src/fileops.c index 07eb504bd..6aafd06b6 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -13,6 +13,12 @@ #include "win32/findfile.h" #endif +#ifdef GIT_USE_STAT_ATIMESPEC +#define st_atim st_atimespec +#define st_ctim st_ctimespec +#define st_mtim st_mtimespec +#endif + GIT__USE_STRMAP int git_futils_mkpath2file(const char *file_path, const mode_t mode) @@ -1034,11 +1040,7 @@ int git_futils_filestamp_check( git_futils_filestamp *stamp, const char *path) { struct stat st; -#if defined(__APPLE__) - const struct timespec *statmtime = &st.st_mtimespec; -#else const struct timespec *statmtime = &st.st_mtim; -#endif /* if the stamp is NULL, then always reload */ if (stamp == NULL) @@ -1080,11 +1082,7 @@ void git_futils_filestamp_set( void git_futils_filestamp_set_from_stat( git_futils_filestamp *stamp, struct stat *st) { -#if defined(__APPLE__) - const struct timespec *statmtime = &st->st_mtimespec; -#else const struct timespec *statmtime = &st->st_mtim; -#endif if (st) { stamp->mtime = *statmtime; From e78e8fae5c0fe00327512b1f0b9ffd6ef696d29b Mon Sep 17 00:00:00 2001 From: Jacques Germishuys Date: Fri, 20 Nov 2015 20:22:38 +0200 Subject: [PATCH 268/450] Make stat.st_size a __int64 not a uint64_t --- src/win32/win32-compat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/win32/win32-compat.h b/src/win32/win32-compat.h index 8b4070df7..d3a5b68a3 100644 --- a/src/win32/win32-compat.h +++ b/src/win32/win32-compat.h @@ -28,7 +28,7 @@ struct p_stat { short st_uid; short st_gid; _dev_t st_rdev; - uint64_t st_size; + __int64 st_size; struct timespec st_atim; struct timespec st_mtim; struct timespec st_ctim; From 25e84f959ae77f21c32849aa98f274b9d8283f2d Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 23 Nov 2015 15:49:54 -0500 Subject: [PATCH 269/450] checkout: only consider nsecs when built that way When examining the working directory and determining whether it's up-to-date, only consider the nanoseconds in the index entry when built with `GIT_USE_NSEC`. This prevents us from believing that the working directory is always dirty when the index was originally written with a git client that uinderstands nsecs (like git 2.x). --- src/checkout.c | 3 +-- src/diff.c | 49 +++---------------------------------------------- src/diff.h | 1 - src/index.c | 20 +++----------------- src/index.h | 39 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 46 insertions(+), 66 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index d09357f2a..a92ad0825 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -200,8 +200,7 @@ static bool checkout_is_workdir_modified( * out.) */ if ((ie = git_index_get_bypath(data->index, wditem->path, 0)) != NULL) { - if (wditem->mtime.seconds == ie->mtime.seconds && - wditem->mtime.nanoseconds == ie->mtime.nanoseconds && + if (git_index_time_eq(&wditem->mtime, &ie->mtime) && wditem->file_size == ie->file_size) return !is_workdir_base_or_new(&ie->id, baseitem, newitem); } diff --git a/src/diff.c b/src/diff.c index 0ab3b8d1f..9402b6e61 100644 --- a/src/diff.c +++ b/src/diff.c @@ -494,11 +494,6 @@ static int diff_list_apply_options( /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */ - /* Don't trust nanoseconds; we do not load nanos from disk */ -#ifdef GIT_USE_NSEC - diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_NANOSECS; -#endif - /* If not given explicit `opts`, check `diff.xyz` configs */ if (!opts) { int context = git_config__get_int_force(cfg, "diff.context", 3); @@ -699,38 +694,6 @@ int git_diff__oid_for_entry( return error; } -static bool diff_time_eq( - const git_index_time *a, const git_index_time *b, bool use_nanos) -{ - return a->seconds == b->seconds && - (!use_nanos || a->nanoseconds == b->nanoseconds); -} - -/* - * Test if the given index time is newer than the given existing index entry. - * If the timestamps are exactly equivalent, then the given index time is - * considered "racily newer" than the existing index entry. - */ -static bool diff_newer_than_index( - const git_index_time *a, const git_index *b, bool use_nanos) -{ - bool is_newer = false; - - if(!b) - return false; - - is_newer = is_newer || (a->seconds > (int32_t) b->stamp.mtime.tv_sec); - is_newer = is_newer || (!use_nanos && - (a->seconds == (int32_t) b->stamp.mtime.tv_sec)); - if(use_nanos) - { - is_newer = is_newer || ((a->seconds == (int32_t) b->stamp.mtime.tv_sec) && - (a->nanoseconds >= (uint32_t) b->stamp.mtime.tv_nsec)); - } - - return is_newer; -} - typedef struct { git_repository *repo; git_iterator *old_iter; @@ -863,11 +826,6 @@ static int maybe_modified( */ } else if (git_oid_iszero(&nitem->id) && new_is_workdir) { bool use_ctime = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0); -#ifdef GIT_USE_NSEC - bool use_nanos = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_NANOSECS) != 0); -#else - bool use_nanos = false; -#endif git_index *index; git_iterator_index(&index, info->new_iter); @@ -886,13 +844,12 @@ static int maybe_modified( modified_uncertain = (oitem->file_size <= 0 && nitem->file_size > 0); } - else if (!diff_time_eq(&oitem->mtime, &nitem->mtime, use_nanos) || - (use_ctime && - !diff_time_eq(&oitem->ctime, &nitem->ctime, use_nanos)) || + else if (!git_index_time_eq(&oitem->mtime, &nitem->mtime) || + (use_ctime && !git_index_time_eq(&oitem->ctime, &nitem->ctime)) || oitem->ino != nitem->ino || oitem->uid != nitem->uid || oitem->gid != nitem->gid || - diff_newer_than_index(&nitem->mtime, index, use_nanos)) + git_index_entry_newer_than_index(nitem, index)) { status = GIT_DELTA_MODIFIED; modified_uncertain = true; diff --git a/src/diff.h b/src/diff.h index 2dfc2c615..47743f88b 100644 --- a/src/diff.h +++ b/src/diff.h @@ -28,7 +28,6 @@ enum { GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */ GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */ GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */ - GIT_DIFFCAPS_TRUST_NANOSECS = (1 << 5), /* use stat time nanoseconds */ }; #define DIFF_FLAGS_KNOWN_BINARY (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY) diff --git a/src/index.c b/src/index.c index bce16348c..ca5b2c46e 100644 --- a/src/index.c +++ b/src/index.c @@ -720,27 +720,13 @@ int git_index__changed_relative_to( return !!git_oid_cmp(&index->checksum, checksum); } -static bool is_racy_timestamp(const struct timespec *stamp, git_index_entry *entry) +static bool is_racy_entry(git_index *index, const git_index_entry *entry) { /* Git special-cases submodules in the check */ if (S_ISGITLINK(entry->mode)) return false; - /* If we never read the index, we can't have this race either */ - if(stamp->tv_sec == 0) - return false; - - /* If the timestamp is the same or newer than the index, it's racy */ -#if defined(GIT_USE_NSEC) - if((int32_t) stamp->tv_sec < entry->mtime.seconds) - return true; - else if((int32_t) stamp->tv_sec > entry->mtime.seconds) - return false; - else - return (uint32_t) stamp->tv_nsec <= entry->mtime.nanoseconds; -#else - return ((int32_t) stamp->tv_sec) <= entry->mtime.seconds; -#endif + return git_index_entry_newer_than_index(entry, index); } /* @@ -768,7 +754,7 @@ static int truncate_racily_clean(git_index *index) diff_opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE | GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_DISABLE_PATHSPEC_MATCH; git_vector_foreach(&index->entries, i, entry) { if ((entry->flags_extended & GIT_IDXENTRY_UPTODATE) == 0 && - is_racy_timestamp(&index->stamp.mtime, entry)) + is_racy_entry(index, entry)) git_vector_insert(&paths, (char *)entry->path); } diff --git a/src/index.h b/src/index.h index 546e677be..9baf976ee 100644 --- a/src/index.h +++ b/src/index.h @@ -65,6 +65,45 @@ extern int git_index_entry_icmp(const void *a, const void *b); extern int git_index_entry_srch(const void *a, const void *b); extern int git_index_entry_isrch(const void *a, const void *b); +/* Index time handling functions */ +GIT_INLINE(bool) git_index_time_eq(const git_index_time *one, const git_index_time *two) +{ + if (one->seconds != two->seconds) + return false; + +#ifdef GIT_USE_NSEC + if (one->nanoseconds != two->nanoseconds) + return false; +#endif + + return true; +} + +/* + * Test if the given index time is newer than the given existing index entry. + * If the timestamps are exactly equivalent, then the given index time is + * considered "racily newer" than the existing index entry. + */ +GIT_INLINE(bool) git_index_entry_newer_than_index( + const git_index_entry *entry, git_index *index) +{ + /* If we never read the index, we can't have this race either */ + if (!index || index->stamp.mtime.tv_sec == 0) + return false; + + /* If the timestamp is the same or newer than the index, it's racy */ +#if defined(GIT_USE_NSEC) + if ((int32_t)index->stamp.tv_sec < entry->mtime.seconds) + return true; + else if ((int32_t)index->stamp.mtime.tv_sec > entry->mtime.seconds) + return false; + else + return (uint32_t)index->stamp.mtime.tv_nsec <= entry->mtime.nanoseconds; +#else + return ((int32_t)index->stamp.mtime.tv_sec) <= entry->mtime.seconds; +#endif +} + /* Search index for `path`, returning GIT_ENOTFOUND if it does not exist * (but not setting an error message). * From c8fab201ebbb9d3c871a7ca426e780b90271a8e0 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 24 Nov 2015 14:29:32 +0100 Subject: [PATCH 270/450] tests: config::global: fix memleak in open_programdata --- tests/config/global.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/config/global.c b/tests/config/global.c index 0ddd204ad..a149dc0be 100644 --- a/tests/config/global.c +++ b/tests/config/global.c @@ -73,7 +73,7 @@ void test_config_global__open_programdata(void) git_buf config_path = GIT_BUF_INIT; git_buf var_contents = GIT_BUF_INIT; - if (!cl_getenv("GITTEST_INVASIVE_FS_STRUCTURE")) + if (cl_is_env_set("GITTEST_INVASIVE_FS_STRUCTURE")) cl_skip(); cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, From 9031be180b67b50a14cacae7ff9a8b5216ca1e8a Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 24 Nov 2015 14:38:17 +0100 Subject: [PATCH 271/450] tests: config::stress: free `git_config` structs --- tests/config/stress.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/config/stress.c b/tests/config/stress.c index 6e960425c..a6b665590 100644 --- a/tests/config/stress.c +++ b/tests/config/stress.c @@ -126,4 +126,7 @@ void test_config_stress__quick_write(void) cl_git_pass(git_config_get_int32(&val, config_r, key)); cl_assert_equal_i(i, val); } + + git_config_free(config_r); + git_config_free(config_w); } From 77b79dde84b6b53a3c9bdde844fcd2a73b9bcca7 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 24 Nov 2015 14:36:46 +0100 Subject: [PATCH 272/450] tests: win32::longpath: free expected_msg --- tests/win32/longpath.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/win32/longpath.c b/tests/win32/longpath.c index 6de7d389a..07eecd394 100644 --- a/tests/win32/longpath.c +++ b/tests/win32/longpath.c @@ -46,6 +46,8 @@ void assert_name_too_long(void) /* check the suffix */ cl_assert_equal_s(expected_msg, err->message + (actual_len - expected_len)); + + git__free(expected_msg); } #endif From fa78782f67d17c5b139a2bbe3ceefe54c8c71dd3 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 22 Oct 2015 17:00:09 -0400 Subject: [PATCH 273/450] merge: rename `git_merge_tree_flags_t` -> `git_merge_flags_t` --- CHANGELOG.md | 8 ++++++++ include/git2/merge.h | 36 ++++++++++++++++++----------------- src/merge.c | 12 ++++++------ src/merge.h | 4 ++-- tests/cherrypick/workdir.c | 4 ++-- tests/merge/trees/commits.c | 2 +- tests/merge/trees/treediff.c | 2 +- tests/merge/workdir/renames.c | 6 +++--- tests/revert/workdir.c | 4 ++-- 9 files changed, 44 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d5a4166a..196ad705a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,14 @@ v0.23 + 1 ### Breaking API changes +* The `git_merge_tree_flag_t` is now `git_merge_flag_t`. Subsequently, + its members are no longer prefixed with `GIT_MERGE_TREE_FLAG` but are + now prefixed with `GIT_MERGE_FLAG`, and the `tree_flags` field of the + `git_merge_options` structure is now named `flags`. + +* The `git_merge_file_flags_t` enum is now `git_merge_file_flag_t` for + consistency with other enum type names. + * `git_cert` descendent types now have a proper `parent` member * It is the responsibility of the refdb backend to decide what to do diff --git a/include/git2/merge.h b/include/git2/merge.h index b7da63e0e..de224aeac 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -62,8 +62,8 @@ GIT_EXTERN(int) git_merge_file_init_input( unsigned int version); /** - * Flags for `git_merge_tree` options. A combination of these flags can be - * passed in via the `tree_flags` value in the `git_merge_options`. + * Flags for `git_merge` options. A combination of these flags can be + * passed in via the `flags` value in the `git_merge_options`. */ typedef enum { /** @@ -71,20 +71,20 @@ typedef enum { * side or the common ancestor and the "theirs" side. This will enable * the ability to merge between a modified and renamed file. */ - GIT_MERGE_TREE_FIND_RENAMES = (1 << 0), + GIT_MERGE_FIND_RENAMES = (1 << 0), + + /** + * Do not write the REUC extension on the generated index + */ + GIT_MERGE_SKIP_REUC = (1 << 2), /** * If a conflict occurs, exit immediately instead of attempting to * continue resolving conflicts. The merge operation will fail with * GIT_EMERGECONFLICT and no index will be returned. */ - GIT_MERGE_TREE_FAIL_ON_CONFLICT = (1 << 1), - - /** - * Do not write the REUC extension on the generated index - */ - GIT_MERGE_TREE_SKIP_REUC = (1 << 2), -} git_merge_tree_flag_t; + GIT_MERGE_FAIL_ON_CONFLICT = (1 << 1), +} git_merge_flag_t; /** * Merge file favor options for `git_merge_options` instruct the file-level @@ -152,7 +152,7 @@ typedef enum { /** Take extra time to find minimal diff */ GIT_MERGE_FILE_DIFF_MINIMAL = (1 << 7), -} git_merge_file_flags_t; +} git_merge_file_flag_t; /** * Options for merging a file @@ -181,8 +181,8 @@ typedef struct { /** The file to favor in region conflicts. */ git_merge_file_favor_t favor; - /** see `git_merge_file_flags_t` above */ - unsigned int flags; + /** see `git_merge_file_flag_t` above */ + git_merge_file_flag_t flags; } git_merge_file_options; #define GIT_MERGE_FILE_OPTIONS_VERSION 1 @@ -232,11 +232,13 @@ typedef struct { */ typedef struct { unsigned int version; - git_merge_tree_flag_t tree_flags; + + /** See `git_merge_flag_t` above */ + git_merge_flag_t flags; /** * Similarity to consider a file renamed (default 50). If - * `GIT_MERGE_TREE_FIND_RENAMES` is enabled, added files will be compared + * `GIT_MERGE_FIND_RENAMES` is enabled, added files will be compared * with deleted files to determine their similarity. Files that are * more similar than the rename threshold (percentage-wise) will be * treated as a rename. @@ -258,8 +260,8 @@ typedef struct { /** Flags for handling conflicting content. */ git_merge_file_favor_t file_favor; - /** see `git_merge_file_flags_t` above */ - unsigned int file_flags; + /** see `git_merge_file_flag_t` above */ + git_merge_file_flag_t file_flags; } git_merge_options; #define GIT_MERGE_OPTIONS_VERSION 1 diff --git a/src/merge.c b/src/merge.c index bad5f9552..d7b23e2c8 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1296,7 +1296,7 @@ int git_merge_diff_list__find_renames( assert(diff_list && opts); - if ((opts->tree_flags & GIT_MERGE_TREE_FIND_RENAMES) == 0) + if ((opts->flags & GIT_MERGE_FIND_RENAMES) == 0) return 0; similarity_ours = git__calloc(diff_list->conflicts.length, @@ -1632,8 +1632,8 @@ static int merge_normalize_opts( git_merge_options init = GIT_MERGE_OPTIONS_INIT; memcpy(opts, &init, sizeof(init)); - opts->tree_flags = GIT_MERGE_TREE_FIND_RENAMES; - opts->rename_threshold = GIT_MERGE_TREE_RENAME_THRESHOLD; + opts->flags = GIT_MERGE_FIND_RENAMES; + opts->rename_threshold = GIT_MERGE_DEFAULT_RENAME_THRESHOLD; } if (!opts->target_limit) { @@ -1643,7 +1643,7 @@ static int merge_normalize_opts( limit = git_config__get_int_force(cfg, "diff.renamelimit", 0); opts->target_limit = (limit <= 0) ? - GIT_MERGE_TREE_TARGET_LIMIT : (unsigned int)limit; + GIT_MERGE_DEFAULT_TARGET_LIMIT : (unsigned int)limit; } /* assign the internal metric with whitespace flag as payload */ @@ -1864,7 +1864,7 @@ int git_merge__iterators( goto done; if (!resolved) { - if ((opts.tree_flags & GIT_MERGE_TREE_FAIL_ON_CONFLICT)) { + if ((opts.flags & GIT_MERGE_FAIL_ON_CONFLICT)) { giterr_set(GITERR_MERGE, "merge conflicts exist"); error = GIT_EMERGECONFLICT; goto done; @@ -1875,7 +1875,7 @@ int git_merge__iterators( } error = index_from_diff_list(out, diff_list, - (opts.tree_flags & GIT_MERGE_TREE_SKIP_REUC)); + (opts.flags & GIT_MERGE_SKIP_REUC)); done: if (!given_opts || !given_opts->metric) diff --git a/src/merge.h b/src/merge.h index 3caf617c6..bd839be49 100644 --- a/src/merge.h +++ b/src/merge.h @@ -19,8 +19,8 @@ #define GIT_MERGE_MODE_FILE "MERGE_MODE" #define GIT_MERGE_FILE_MODE 0666 -#define GIT_MERGE_TREE_RENAME_THRESHOLD 50 -#define GIT_MERGE_TREE_TARGET_LIMIT 1000 +#define GIT_MERGE_DEFAULT_RENAME_THRESHOLD 50 +#define GIT_MERGE_DEFAULT_TARGET_LIMIT 1000 /** Types of changes when files are merged from branch to branch. */ typedef enum { diff --git a/tests/cherrypick/workdir.c b/tests/cherrypick/workdir.c index 787f1f4d4..2b45f5a33 100644 --- a/tests/cherrypick/workdir.c +++ b/tests/cherrypick/workdir.c @@ -300,7 +300,7 @@ void test_cherrypick_workdir__rename(void) { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 0, "file3.txt.renamed" }, }; - opts.merge_opts.tree_flags |= GIT_MERGE_TREE_FIND_RENAMES; + opts.merge_opts.flags |= GIT_MERGE_FIND_RENAMES; opts.merge_opts.rename_threshold = 50; git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15"); @@ -335,7 +335,7 @@ void test_cherrypick_workdir__both_renamed(void) { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 2, "file3.txt.renamed_on_branch" }, }; - opts.merge_opts.tree_flags |= GIT_MERGE_TREE_FIND_RENAMES; + opts.merge_opts.flags |= GIT_MERGE_FIND_RENAMES; opts.merge_opts.rename_threshold = 50; git_oid_fromstr(&head_oid, "44cd2ed2052c9c68f9a439d208e9614dc2a55c70"); diff --git a/tests/merge/trees/commits.c b/tests/merge/trees/commits.c index 2e3c4578b..dd1e383ac 100644 --- a/tests/merge/trees/commits.c +++ b/tests/merge/trees/commits.c @@ -134,7 +134,7 @@ void test_merge_trees_commits__fail_on_conflict(void) git_index *index; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; - opts.tree_flags |= GIT_MERGE_TREE_FAIL_ON_CONFLICT; + opts.flags |= GIT_MERGE_FAIL_ON_CONFLICT; cl_git_fail_with(GIT_EMERGECONFLICT, merge_trees_from_branches(&index, repo, "df_side1", "df_side2", &opts)); diff --git a/tests/merge/trees/treediff.c b/tests/merge/trees/treediff.c index f21d99b6d..3634568de 100644 --- a/tests/merge/trees/treediff.c +++ b/tests/merge/trees/treediff.c @@ -47,7 +47,7 @@ static void test_find_differences( git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; - opts.tree_flags |= GIT_MERGE_TREE_FIND_RENAMES; + opts.flags |= GIT_MERGE_FIND_RENAMES; opts.target_limit = 1000; opts.rename_threshold = 50; diff --git a/tests/merge/workdir/renames.c b/tests/merge/workdir/renames.c index 83006a703..fabcda2a8 100644 --- a/tests/merge/workdir/renames.c +++ b/tests/merge/workdir/renames.c @@ -63,7 +63,7 @@ void test_merge_workdir_renames__renames(void) { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed.txt~rename_conflict_theirs" }, }; - merge_opts.tree_flags |= GIT_MERGE_TREE_FIND_RENAMES; + merge_opts.flags |= GIT_MERGE_FIND_RENAMES; merge_opts.rename_threshold = 50; cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &merge_opts, NULL)); @@ -99,7 +99,7 @@ void test_merge_workdir_renames__ours(void) { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "7-both-renamed.txt" }, }; - merge_opts.tree_flags |= GIT_MERGE_TREE_FIND_RENAMES; + merge_opts.flags |= GIT_MERGE_FIND_RENAMES; merge_opts.rename_threshold = 50; checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS; @@ -147,7 +147,7 @@ void test_merge_workdir_renames__similar(void) { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed.txt~rename_conflict_theirs" }, }; - merge_opts.tree_flags |= GIT_MERGE_TREE_FIND_RENAMES; + merge_opts.flags |= GIT_MERGE_FIND_RENAMES; merge_opts.rename_threshold = 50; cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &merge_opts, NULL)); diff --git a/tests/revert/workdir.c b/tests/revert/workdir.c index 9f83bd842..802819c75 100644 --- a/tests/revert/workdir.c +++ b/tests/revert/workdir.c @@ -410,7 +410,7 @@ void test_revert_workdir__rename_1_of_2(void) { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 2, "file6.txt" }, }; - opts.merge_opts.tree_flags |= GIT_MERGE_TREE_FIND_RENAMES; + opts.merge_opts.flags |= GIT_MERGE_FIND_RENAMES; opts.merge_opts.rename_threshold = 50; git_oid_fromstr(&head_oid, "cef56612d71a6af8d8015691e4865f7fece905b5"); @@ -444,7 +444,7 @@ void test_revert_workdir__rename(void) { "file4.txt", "file5.txt", "" }, }; - opts.merge_opts.tree_flags |= GIT_MERGE_TREE_FIND_RENAMES; + opts.merge_opts.flags |= GIT_MERGE_FIND_RENAMES; opts.merge_opts.rename_threshold = 50; git_oid_fromstr(&head_oid, "55568c8de5322ff9a95d72747a239cdb64a19965"); From 86c8d02c071d3713e1c9f0b95d9f4599108d2c29 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 22 Oct 2015 20:20:07 -0400 Subject: [PATCH 274/450] merge: add simple recursive test Add a simple recursive test - where multiple ancestors exist and creating a virtual merge base from them would prevent a conflict. --- include/git2/merge.h | 18 ++- tests/merge/merge_helpers.c | 3 +- tests/merge/trees/recursive.c | 110 ++++++++++++++++++ tests/resources/merge-recursive/.gitted/HEAD | Bin 0 -> 26 bytes .../resources/merge-recursive/.gitted/config | Bin 0 -> 137 bytes tests/resources/merge-recursive/.gitted/index | Bin 0 -> 632 bytes .../merge-recursive/.gitted/info/refs | Bin 0 -> 59 bytes .../00/6b298c5702b04c00370d0414959765b82fd722 | Bin 0 -> 207 bytes .../01/6eef4a6fefd36bdcaa93ad773449ddc5c73cbb | Bin 0 -> 208 bytes .../05/c6a04ac101ab1a9836a95d5ec8d16b6f6304fd | Bin 0 -> 208 bytes .../07/10c3c796e0704361472ecb904413fca0107a25 | Bin 0 -> 208 bytes .../07/2d89dcf3a7671ac34a8e875bb72fb39bcf14d7 | Bin 0 -> 208 bytes .../0b/b7ed583d7e9ad507e8b902594f5c9126ea456b | Bin 0 -> 161 bytes .../12/4d4fe29d3433fdaa2f0f455d226f2c79d89cf3 | Bin 0 -> 208 bytes .../16/895aa5e13f8907d4adab81285557d938fad342 | Bin 0 -> 634 bytes .../3a/3f5a6ec1c968d1d2d5d20dee0d161a4351f279 | Bin 0 -> 162 bytes .../3b/919b6e8a575b4779c8243ebea3e3beb436e88f | Bin 0 -> 208 bytes .../3f/d41804a7906db846af5e868444782e546af46a | Bin 0 -> 206 bytes .../42/1b392106e079df6d412babd5636697938269ec | Bin 0 -> 163 bytes .../42/cdad903aef3e7b614675e6584a8be417941911 | Bin 0 -> 208 bytes .../43/2faca0c62dc556ad71a22f23e541a46a8b0f6f | Bin 0 -> 207 bytes .../43/5424798e5e1b21dd4588d1c291ba4eb179a838 | Bin 0 -> 208 bytes .../4b/7c5650008b2e747fe1809eeb5a1dde0e80850a | Bin 0 -> 615 bytes .../4c/49317a0912ca559d2048bc329994eb7d10474f | Bin 0 -> 183 bytes .../4e/21d2d63357bde5027d1625f5ec6b430cdeb143 | Bin 0 -> 662 bytes .../53/9bd011c4822c560c1d17cab095006b7a10f707 | Bin 0 -> 163 bytes .../65/bea8448ca5b3104628ffbca553c54bde54b0fc | Bin 0 -> 160 bytes .../66/6ffdfcf1eaa5641fa31064bf2607327e843c09 | Bin 0 -> 664 bytes .../68/af1fc7407fd9addf1701a87eb1c95c7494c598 | Bin 0 -> 443 bytes .../68/f6182f4c85d39e1309d97c7e456156dc9c0096 | Bin 0 -> 755 bytes .../71/3e438567b28543235faf265c4c5b02b437c7fd | Bin 0 -> 207 bytes .../72/3181f1bfd30e47a6d1d36a4d874e31e7a0a1a4 | Bin 0 -> 163 bytes .../74/4df1bdf0f7bca20deb23e5a5eb8255fc237901 | Bin 0 -> 207 bytes .../7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f | Bin 0 -> 198 bytes .../7c/7e08f9559d9e1551b91e1cf68f1d0066109add | Bin 0 -> 443 bytes .../88/eb3f98849f4b8d0555395f514800900a01dc8f | Bin 0 -> 209 bytes .../8f/35f30bfe09513f96cf8aa4df0834ae34e93bae | Bin 0 -> 161 bytes .../94/d2c01087f48213bd157222d54edfefd77c9bba | Bin 0 -> 621 bytes .../95/78b04e2087976e382622322ba476aa40398dc7 | Bin 0 -> 620 bytes .../97/3b70322e758da87e1ce21d2195d86c5e4e9647 | Bin 0 -> 207 bytes .../9c/3f1c70db28c00ce74b22ba3edafe16d9cf03d4 | Bin 0 -> 208 bytes .../a0/65d3022e99a1943177c10a53cce38bc2127042 | Bin 0 -> 162 bytes .../a2/fa36ffc4a565a223e225d15b18774f87d0c4f0 | Bin 0 -> 167 bytes .../a3/4e5a16feabbd0335a633aadb8217c9f3dba58d | Bin 0 -> 164 bytes .../a7/b066537e6be7109abfe4ff97b675d4e077da20 | Bin 0 -> 621 bytes .../a8/2a121ea36b115548d6dad2cd86ec27f06f7b30 | Bin 0 -> 208 bytes .../b2/a81ead9e722af0099fccfb478cea88eea749a2 | Bin 0 -> 664 bytes .../b4/cefb3c75770e57bb8bb44e4a50d9578009e847 | Bin 0 -> 639 bytes .../c4/e6cca3ec6ae0148ed231f97257df8c311e015f | Bin 0 -> 268 bytes .../cb/49ad76147f5f9439cbd6133708b76142660660 | Bin 0 -> 641 bytes .../d0/dd5d9083bda65ec99aa8b9b64a5a278771b70a | Bin 0 -> 620 bytes .../de/a7215f259b2cced87d1bda6c72f8b4ce37a2ff | Bin 0 -> 357 bytes .../e2/93bfdddb81a853bbb16b8b58e68626f30841a4 | Bin 0 -> 207 bytes .../e2/c84bb33992a455b1a7a5019f0e38d883d3f475 | Bin 0 -> 208 bytes .../f1/3e1bc6ba935fce2efffa5be4c4832404034ef1 | Bin 0 -> 206 bytes .../f3/5f159ff5d44dfd9f52d63dd5b659f0521ff569 | Bin 0 -> 669 bytes .../f5/1658077d85f2264fa179b4d0848268cb3475c3 | Bin 0 -> 355 bytes .../fa/567f568ed72157c0c617438d077695b99d9aac | Bin 0 -> 662 bytes .../fd/8b5fe88cda995e70a22ed98701e65b843e05ec | Bin 0 -> 165 bytes .../fe/f01f3104c8047d05e8572e521c454f8fd4b8db | Bin 0 -> 207 bytes .../ff/b36e513f5fdf8a6ba850a20142676a2ac4807d | Bin 0 -> 355 bytes .../.gitted/refs/heads/branchA-1 | Bin 0 -> 41 bytes .../.gitted/refs/heads/branchA-2 | Bin 0 -> 41 bytes .../.gitted/refs/heads/branchB-1 | Bin 0 -> 41 bytes .../.gitted/refs/heads/branchB-2 | Bin 0 -> 41 bytes tests/resources/merge-recursive/asparagus.txt | Bin 0 -> 587 bytes tests/resources/merge-recursive/beef.txt | Bin 0 -> 1372 bytes tests/resources/merge-recursive/bouilli.txt | Bin 0 -> 1092 bytes tests/resources/merge-recursive/gravy.txt | Bin 0 -> 390 bytes tests/resources/merge-recursive/oyster.txt | Bin 0 -> 774 bytes tests/resources/merge-recursive/veal.txt | Bin 0 -> 1095 bytes 71 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 tests/merge/trees/recursive.c create mode 100644 tests/resources/merge-recursive/.gitted/HEAD create mode 100644 tests/resources/merge-recursive/.gitted/config create mode 100644 tests/resources/merge-recursive/.gitted/index create mode 100644 tests/resources/merge-recursive/.gitted/info/refs create mode 100644 tests/resources/merge-recursive/.gitted/objects/00/6b298c5702b04c00370d0414959765b82fd722 create mode 100644 tests/resources/merge-recursive/.gitted/objects/01/6eef4a6fefd36bdcaa93ad773449ddc5c73cbb create mode 100644 tests/resources/merge-recursive/.gitted/objects/05/c6a04ac101ab1a9836a95d5ec8d16b6f6304fd create mode 100644 tests/resources/merge-recursive/.gitted/objects/07/10c3c796e0704361472ecb904413fca0107a25 create mode 100644 tests/resources/merge-recursive/.gitted/objects/07/2d89dcf3a7671ac34a8e875bb72fb39bcf14d7 create mode 100644 tests/resources/merge-recursive/.gitted/objects/0b/b7ed583d7e9ad507e8b902594f5c9126ea456b create mode 100644 tests/resources/merge-recursive/.gitted/objects/12/4d4fe29d3433fdaa2f0f455d226f2c79d89cf3 create mode 100644 tests/resources/merge-recursive/.gitted/objects/16/895aa5e13f8907d4adab81285557d938fad342 create mode 100644 tests/resources/merge-recursive/.gitted/objects/3a/3f5a6ec1c968d1d2d5d20dee0d161a4351f279 create mode 100644 tests/resources/merge-recursive/.gitted/objects/3b/919b6e8a575b4779c8243ebea3e3beb436e88f create mode 100644 tests/resources/merge-recursive/.gitted/objects/3f/d41804a7906db846af5e868444782e546af46a create mode 100644 tests/resources/merge-recursive/.gitted/objects/42/1b392106e079df6d412babd5636697938269ec create mode 100644 tests/resources/merge-recursive/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911 create mode 100644 tests/resources/merge-recursive/.gitted/objects/43/2faca0c62dc556ad71a22f23e541a46a8b0f6f create mode 100644 tests/resources/merge-recursive/.gitted/objects/43/5424798e5e1b21dd4588d1c291ba4eb179a838 create mode 100644 tests/resources/merge-recursive/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a create mode 100644 tests/resources/merge-recursive/.gitted/objects/4c/49317a0912ca559d2048bc329994eb7d10474f create mode 100644 tests/resources/merge-recursive/.gitted/objects/4e/21d2d63357bde5027d1625f5ec6b430cdeb143 create mode 100644 tests/resources/merge-recursive/.gitted/objects/53/9bd011c4822c560c1d17cab095006b7a10f707 create mode 100644 tests/resources/merge-recursive/.gitted/objects/65/bea8448ca5b3104628ffbca553c54bde54b0fc create mode 100644 tests/resources/merge-recursive/.gitted/objects/66/6ffdfcf1eaa5641fa31064bf2607327e843c09 create mode 100644 tests/resources/merge-recursive/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598 create mode 100644 tests/resources/merge-recursive/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096 create mode 100644 tests/resources/merge-recursive/.gitted/objects/71/3e438567b28543235faf265c4c5b02b437c7fd create mode 100644 tests/resources/merge-recursive/.gitted/objects/72/3181f1bfd30e47a6d1d36a4d874e31e7a0a1a4 create mode 100644 tests/resources/merge-recursive/.gitted/objects/74/4df1bdf0f7bca20deb23e5a5eb8255fc237901 create mode 100644 tests/resources/merge-recursive/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f create mode 100644 tests/resources/merge-recursive/.gitted/objects/7c/7e08f9559d9e1551b91e1cf68f1d0066109add create mode 100644 tests/resources/merge-recursive/.gitted/objects/88/eb3f98849f4b8d0555395f514800900a01dc8f create mode 100644 tests/resources/merge-recursive/.gitted/objects/8f/35f30bfe09513f96cf8aa4df0834ae34e93bae create mode 100644 tests/resources/merge-recursive/.gitted/objects/94/d2c01087f48213bd157222d54edfefd77c9bba create mode 100644 tests/resources/merge-recursive/.gitted/objects/95/78b04e2087976e382622322ba476aa40398dc7 create mode 100644 tests/resources/merge-recursive/.gitted/objects/97/3b70322e758da87e1ce21d2195d86c5e4e9647 create mode 100644 tests/resources/merge-recursive/.gitted/objects/9c/3f1c70db28c00ce74b22ba3edafe16d9cf03d4 create mode 100644 tests/resources/merge-recursive/.gitted/objects/a0/65d3022e99a1943177c10a53cce38bc2127042 create mode 100644 tests/resources/merge-recursive/.gitted/objects/a2/fa36ffc4a565a223e225d15b18774f87d0c4f0 create mode 100644 tests/resources/merge-recursive/.gitted/objects/a3/4e5a16feabbd0335a633aadb8217c9f3dba58d create mode 100644 tests/resources/merge-recursive/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20 create mode 100644 tests/resources/merge-recursive/.gitted/objects/a8/2a121ea36b115548d6dad2cd86ec27f06f7b30 create mode 100644 tests/resources/merge-recursive/.gitted/objects/b2/a81ead9e722af0099fccfb478cea88eea749a2 create mode 100644 tests/resources/merge-recursive/.gitted/objects/b4/cefb3c75770e57bb8bb44e4a50d9578009e847 create mode 100644 tests/resources/merge-recursive/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f create mode 100644 tests/resources/merge-recursive/.gitted/objects/cb/49ad76147f5f9439cbd6133708b76142660660 create mode 100644 tests/resources/merge-recursive/.gitted/objects/d0/dd5d9083bda65ec99aa8b9b64a5a278771b70a create mode 100644 tests/resources/merge-recursive/.gitted/objects/de/a7215f259b2cced87d1bda6c72f8b4ce37a2ff create mode 100644 tests/resources/merge-recursive/.gitted/objects/e2/93bfdddb81a853bbb16b8b58e68626f30841a4 create mode 100644 tests/resources/merge-recursive/.gitted/objects/e2/c84bb33992a455b1a7a5019f0e38d883d3f475 create mode 100644 tests/resources/merge-recursive/.gitted/objects/f1/3e1bc6ba935fce2efffa5be4c4832404034ef1 create mode 100644 tests/resources/merge-recursive/.gitted/objects/f3/5f159ff5d44dfd9f52d63dd5b659f0521ff569 create mode 100644 tests/resources/merge-recursive/.gitted/objects/f5/1658077d85f2264fa179b4d0848268cb3475c3 create mode 100644 tests/resources/merge-recursive/.gitted/objects/fa/567f568ed72157c0c617438d077695b99d9aac create mode 100644 tests/resources/merge-recursive/.gitted/objects/fd/8b5fe88cda995e70a22ed98701e65b843e05ec create mode 100644 tests/resources/merge-recursive/.gitted/objects/fe/f01f3104c8047d05e8572e521c454f8fd4b8db create mode 100644 tests/resources/merge-recursive/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchA-1 create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchA-2 create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchB-1 create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchB-2 create mode 100644 tests/resources/merge-recursive/asparagus.txt create mode 100644 tests/resources/merge-recursive/beef.txt create mode 100644 tests/resources/merge-recursive/bouilli.txt create mode 100644 tests/resources/merge-recursive/gravy.txt create mode 100644 tests/resources/merge-recursive/oyster.txt create mode 100644 tests/resources/merge-recursive/veal.txt diff --git a/include/git2/merge.h b/include/git2/merge.h index de224aeac..a272e8be4 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -73,17 +73,25 @@ typedef enum { */ GIT_MERGE_FIND_RENAMES = (1 << 0), - /** - * Do not write the REUC extension on the generated index - */ - GIT_MERGE_SKIP_REUC = (1 << 2), - /** * If a conflict occurs, exit immediately instead of attempting to * continue resolving conflicts. The merge operation will fail with * GIT_EMERGECONFLICT and no index will be returned. */ GIT_MERGE_FAIL_ON_CONFLICT = (1 << 1), + + /** + * Do not write the REUC extension on the generated index + */ + GIT_MERGE_SKIP_REUC = (1 << 2), + + /** + * If the commits being merged have multiple merge bases, do not build + * a recursive merge base (by merging the multiple merge bases), + * instead simply use the first base. This flag provides a similar + * merge base to `git-merge-resolve`. + */ + GIT_MERGE_NO_RECURSIVE = (1 << 3), } git_merge_flag_t; /** diff --git a/tests/merge/merge_helpers.c b/tests/merge/merge_helpers.c index 986a365db..4b1b7d262 100644 --- a/tests/merge/merge_helpers.c +++ b/tests/merge/merge_helpers.c @@ -4,6 +4,7 @@ #include "tree.h" #include "merge_helpers.h" #include "merge.h" +#include "index.h" #include "git2/merge.h" #include "git2/sys/index.h" #include "git2/annotated_commit.h" @@ -239,7 +240,7 @@ int merge_test_index(git_index *index, const struct merge_index_entry expected[] const git_index_entry *index_entry; /* - dump_index_entries(&index->entries); + merge__dump_index_entries(&index->entries); */ if (git_index_entrycount(index) != expected_len) diff --git a/tests/merge/trees/recursive.c b/tests/merge/trees/recursive.c new file mode 100644 index 000000000..1d3586162 --- /dev/null +++ b/tests/merge/trees/recursive.c @@ -0,0 +1,110 @@ +#include "clar_libgit2.h" +#include "git2/repository.h" +#include "git2/merge.h" +#include "merge.h" +#include "../merge_helpers.h" + +static git_repository *repo; + +#define TEST_REPO_PATH "merge-recursive" + +void test_merge_trees_recursive__initialize(void) +{ + repo = cl_git_sandbox_init(TEST_REPO_PATH); +} + +void test_merge_trees_recursive__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_merge_trees_recursive__one(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "dea7215f259b2cced87d1bda6c72f8b4ce37a2ff", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" }, + }; + + cl_git_pass(merge_commits_from_branches(&index, repo, "branchA-1", "branchA-2", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 6)); + + git_index_free(index); +} + +void test_merge_trees_recursive__one_norecursive(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "dea7215f259b2cced87d1bda6c72f8b4ce37a2ff", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" }, + }; + + opts.flags |= GIT_MERGE_NO_RECURSIVE; + + cl_git_pass(merge_commits_from_branches(&index, repo, "branchA-1", "branchA-2", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 6)); + + git_index_free(index); +} + +void test_merge_trees_recursive__two(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "666ffdfcf1eaa5641fa31064bf2607327e843c09", 0, "veal.txt" }, + }; + + cl_git_pass(merge_commits_from_branches(&index, repo, "branchB-1", "branchB-2", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 6)); + + git_index_free(index); +} + +void test_merge_trees_recursive__two_norecursive(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + opts.flags |= GIT_MERGE_NO_RECURSIVE; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "cb49ad76147f5f9439cbd6133708b76142660660", 1, "veal.txt" }, + { 0100644, "b2a81ead9e722af0099fccfb478cea88eea749a2", 2, "veal.txt" }, + { 0100644, "4e21d2d63357bde5027d1625f5ec6b430cdeb143", 3, "veal.txt" }, + }; + + cl_git_pass(merge_commits_from_branches(&index, repo, "branchB-1", "branchB-2", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 8)); + + git_index_free(index); +} + diff --git a/tests/resources/merge-recursive/.gitted/HEAD b/tests/resources/merge-recursive/.gitted/HEAD new file mode 100644 index 0000000000000000000000000000000000000000..77e35742d01dcf26e0594d689548d3ae37549fca GIT binary patch literal 26 hcmXR)O|w!cN=+-)&qz&7Db`OaO3X{naMCs80swu12*UsX literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/config b/tests/resources/merge-recursive/.gitted/config new file mode 100644 index 0000000000000000000000000000000000000000..6c9406b7d9320db083eca69b3f8bee9a6c7b50d4 GIT binary patch literal 137 zcmYk#%?-jZ3KJaaM(b)IThRcka) zn6vinzTI-FENHqTG;JZL7ug4u#6zM7i5Th{J5BgE#z&9!LjG%xu(tTZ>RkRd-|~Df A#{d8T literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/index b/tests/resources/merge-recursive/.gitted/index new file mode 100644 index 0000000000000000000000000000000000000000..1e47851a50c3550bd04e7e1a481346d39b101267 GIT binary patch literal 632 zcmZ?q402{*U|<4bjxf!rYe1R-Ml%A%8G{Pq7#JFtFfcHF1xkqkg}nc7&I`1Uzu%R; zB481tQ+k%xk%n3Z-o)a9#G=IX(qg@miV~19Ky$+7kZFi{d2>8O z=gpcr*Tk?pa~P&Ea3rOsra_FtVa_~Mb68xwYr+B;y7fxxA2!T;9VL5@uc4KTfjcR` zG&3hB6YeUU?wXHk9%I{)XJ;0_$$B8tcggT)QTY8HLpjEH2F~=N#Ij1TX*k@q0M#63 zwv6@i#~tc#uDvhLxT0?3$(WKUM`ti_ literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/info/refs b/tests/resources/merge-recursive/.gitted/info/refs new file mode 100644 index 0000000000000000000000000000000000000000..96482e6cb196feb3c5c51b901f02ede9aa30f38f GIT binary patch literal 59 zcmV~$xe>r13m_Qw&% literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/00/6b298c5702b04c00370d0414959765b82fd722 b/tests/resources/merge-recursive/.gitted/objects/00/6b298c5702b04c00370d0414959765b82fd722 new file mode 100644 index 0000000000000000000000000000000000000000..d3fb85fcabae19adf089fc080e9075f72b41fe6f GIT binary patch literal 207 zcmV;=05Jb}0V^p=O;s>5GG{O}FfcPQQAjKi J1OUKwTBN=BVB7!z literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/01/6eef4a6fefd36bdcaa93ad773449ddc5c73cbb b/tests/resources/merge-recursive/.gitted/objects/01/6eef4a6fefd36bdcaa93ad773449ddc5c73cbb new file mode 100644 index 0000000000000000000000000000000000000000..90085847c54162c46a745b3561e2efa9d09a3cb6 GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjKNcK?DVcI~22@p9YGMvp&D4qwehTf=^DNYqjI@`Ot#YvJ KJq`f;l3aIJ+G3Re literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/05/c6a04ac101ab1a9836a95d5ec8d16b6f6304fd b/tests/resources/merge-recursive/.gitted/objects/05/c6a04ac101ab1a9836a95d5ec8d16b6f6304fd new file mode 100644 index 0000000000000000000000000000000000000000..c6a3a3b8def22521b0be327923d3bb028613e4e4 GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjKdv{d K)&Ky^dR($J31Z&> literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/07/10c3c796e0704361472ecb904413fca0107a25 b/tests/resources/merge-recursive/.gitted/objects/07/10c3c796e0704361472ecb904413fca0107a25 new file mode 100644 index 0000000000000000000000000000000000000000..9f48594b5e9dd2d86998975017e8b841120dcc46 GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjKNcK?DVcI~22@p9YGMvp&6G05AV|0V^p=O;s>5GG{O}FfcPQQAjK9+jN$ zO%DLOR++W2fmr~9up}2^BE;EY8b=2(r$nAcM=`1=R(>=s*0GHgVqbY79#}-sM+Sn) z4Pd}HV6h6fwv?(b>4i1v>r(Ps9{R{V{irj4i=R7C$tP_AyoYJ_M(;bvsGjz-jsL_b PT>E>xZL4|%(vDH^g)m9w literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/12/4d4fe29d3433fdaa2f0f455d226f2c79d89cf3 b/tests/resources/merge-recursive/.gitted/objects/12/4d4fe29d3433fdaa2f0f455d226f2c79d89cf3 new file mode 100644 index 0000000000000000000000000000000000000000..f0ea020fb001f27b4f65ca32006f9280676ad0e1 GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjKNrj^%~6Yxstxo^mZp7Iv;f>@s$ z+FYKFlXO5E@vh^#(X$OS+2u^0K`m1oohQj`d*}B$HN}$TOftI#7fkFrD_{mjyHe#q z>rEBrEy?79F8|3Eod1VaRu#?o7)DNK%64Xo7zYmhV+BHoj=D4sL;XJ{w!-8FNZgG8 z$@i3;FlZ=_rP29n%ot-&9j*gralZ}+Ku!r{+j@RqZ4~%uz;y7C2)=2j0Ao~FH4&V4 z_H#8#M|m8DT1H5LHr7NWCkwTCa$>M3mjp68Yx|xgE==7N5l>I6P2zb!YfkC-C9$l? zl-iwohB%hSNOnnMIN%kj-p5I;joO6bG}OhAT_`S$gf`Wwf)lT^lNF;eVr%WeHFY=< zN=ED$IDEhQn(T%HWUi^{2Gx0@%V6}Q+6dJYP*@ag;4Y&XvN40l&8KS3TX6shywzT) zE8WZi9L=V|bS3p3`Kez>8|qK=7!z{qCW}7M9t$mYuYI!A$KUWdxs}0qu{nxyp`pPg zL6UY>?bm9+Gc+Ygqh6wMum-+Nyug{71`WLZ)$f9tpym45Hf=EO?sYYEqGVeAl^G76 z=!gDaY=0&01cSR$HA_(ot-gxerXqN+roGj_zMDjI UDBY|+At``qBPE~AAEX6|z4XmEIRF3v literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/3a/3f5a6ec1c968d1d2d5d20dee0d161a4351f279 b/tests/resources/merge-recursive/.gitted/objects/3a/3f5a6ec1c968d1d2d5d20dee0d161a4351f279 new file mode 100644 index 0000000000000000000000000000000000000000..f39a1271fc3ea3223935f59995dfd19bc503e815 GIT binary patch literal 162 zcmV;T0A2rh0i{pd4FVw$^qVSZK(pY&LSl?R*MSZ2SaK%bVGy-_(b$21^TCeflPfAKm%vzCMmXyf ziL8x2xr{}GN1tnxQ+|b(<$12ly{_`WL;au=e~lm8rMeuYLNvyJvrcXS0mM+p*v@~} Qh$)ZDPYze{1{JSPI$SSJ%K!iX literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/3b/919b6e8a575b4779c8243ebea3e3beb436e88f b/tests/resources/merge-recursive/.gitted/objects/3b/919b6e8a575b4779c8243ebea3e3beb436e88f new file mode 100644 index 0000000000000000000000000000000000000000..c85731d6bddd857db09da0d014be4038dfe35be3 GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjKTj;SFV484ZsWk8T$lY^VAlR8|EF&&z4D;^ KmI44rFkeE4X<!?*!?#? z_;cH~%K&1!8C?~Sa}e~BEm}!d;zB_p7uLKK8&ar&(AQ#KrRzRmsl=5qRmI4bY7Dt9 zBED2y$cvKKNJ;c}PePR?Xtp_qNi42#aadGw;;ndMTisnW@;27i;jz3+FYvs!?b>!Y z=}bS>S-*-OcbnUG57v8TwhjXv(Ic9f&T~Wmi7`*wUlhJ+aQN$m16=meAa&XE`fzmS I18dK3v%N-Vt^fc4 literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/42/1b392106e079df6d412babd5636697938269ec b/tests/resources/merge-recursive/.gitted/objects/42/1b392106e079df6d412babd5636697938269ec new file mode 100644 index 0000000000000000000000000000000000000000..3a8324c1b1bab7a033edfd165fdba6cfb84bfb39 GIT binary patch literal 163 zcmV;U09^lg0i};o3c@fDME%Ywasg!%H^~-6{OJw6K$G32f^DQmy}nhvfqye^V3;X& znl87akNM3?>1p_K6V|Tqxm~H#(K^HskfMur3kYCVx{ht~ RXN~E3z5JBnYTiBXPm^J?P0RoQ literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911 b/tests/resources/merge-recursive/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911 new file mode 100644 index 0000000000000000000000000000000000000000..99b5e6d2c8f1feb318ae3effc47c16d1f66918ce GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjK5GG{O}FfcPQQAjKVORhF literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/43/5424798e5e1b21dd4588d1c291ba4eb179a838 b/tests/resources/merge-recursive/.gitted/objects/43/5424798e5e1b21dd4588d1c291ba4eb179a838 new file mode 100644 index 0000000000000000000000000000000000000000..58ab2391707739b6ce0fe4b0c7c9f33400b20ee7 GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjKX{GYz9^vZ+s KTM7VwgIs}8-eWre literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a b/tests/resources/merge-recursive/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a new file mode 100644 index 0000000000000000000000000000000000000000..016398531ebf1408a8bcff0d4a2974da890c3874 GIT binary patch literal 615 zcmV-t0+{`H0Zo(5j@vK{gnRZ=@BzF*+grQ0qCgv9fi2Ko^eo%7Em#pD%8Onl|EL(9jB2A{ZB9%z(Q zDp~`0c9}Zy!LU4TnP`R_a2j)I$sDeGffp+4!3AhA43rTJ zWFou6(+HCX3{H(@VP2uCCwfgfFK{R0k?|pZ5Tn=5VX^q(kEe+?4x#DTDaRH8q9={` zWw}8EEsSAvd9G9{tKCHm=?0zGU48B3V`RP-+bXJ(v;jb>SGxw2^@RZPNXkU^> z6Gz{R_OmI(24P;kl#I&RsBV>}TARmX?7g^Q>{EiS6pz508ZlJQ-6Vs%~M z*Ma+pM6e6PV2lD}J@`D|vUsK*C(2mlkIO%jE^wf>mCH&U9&2BqrrV&ut0!AnSYv1%RxAJj literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/4e/21d2d63357bde5027d1625f5ec6b430cdeb143 b/tests/resources/merge-recursive/.gitted/objects/4e/21d2d63357bde5027d1625f5ec6b430cdeb143 new file mode 100644 index 0000000000000000000000000000000000000000..34f183dd17d3105fc11c39606b2e10b2924c3024 GIT binary patch literal 662 zcmV;H0%`qt0Zo)abJH*og*oe2EccL^a^R9v4(ZSthJ-eyXW3rc8q1Q=${6$Oc`GNt zt&yeGd*6F%kLet8AD4_Hxcb!nhHpBD6nuz3N;Wqb93@*xZf*3t(n}#HgOm!a-PeYSU;(0x2 zPHFojah{MVRVy_Nb1XX}*(Jrejn_$KKUNB0#WY1^TE~!Gm|f@y+{>vMD_+m`R*c4o zTT2gase?qAGh)Yqjr)yjvI`cFxu&K!sLwq;wMKswjBrgMW^^sJTxT>x7G~(Ub1Gh&ahNcABDN8gCXuzfC5oAgl)bjGzzYC^^mjyW{ybf(J_O9h> z_(aLH`V%tm7XuWuD^;O_mv<9oSI z)1_IDxM_>3W%BI2Q|{v6TnI=Kg~ex~sM+@gV(Ett%#9bFnZsr1=XSYH2kQ_&04er%3kYD=bcJpD R=ZrbYdikk*HE-92PWSkwP(uI! literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/65/bea8448ca5b3104628ffbca553c54bde54b0fc b/tests/resources/merge-recursive/.gitted/objects/65/bea8448ca5b3104628ffbca553c54bde54b0fc new file mode 100644 index 0000000000000000000000000000000000000000..031c91359a1032ccb0cebabf44195a087d265f35 GIT binary patch literal 160 zcmV;R0AK%j0i}-74FVw$ME#};8qlyT9FQ2}j~&>+9=n$_k;5Qr`<})Q{F`}`OkS>~ zT)MWHHob{xu{pYNL`=z&^L_*h1v*Yb2aJ4U!?JAu_OK&(^Sr zVHkiMdYEiTiXMHgO`rHhjrDo1_iV1m_K&Yk4=R+m`knz literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/66/6ffdfcf1eaa5641fa31064bf2607327e843c09 b/tests/resources/merge-recursive/.gitted/objects/66/6ffdfcf1eaa5641fa31064bf2607327e843c09 new file mode 100644 index 0000000000000000000000000000000000000000..8d6be4216a30ddcf37e38c8dfa900b9b9c2439bf GIT binary patch literal 664 zcmV;J0%!er0Zo*#a@#NvL{sA{F1?}|r%0npg*@?$$8u~ru0irhVh92Z4h+M5efB^p zNoRq;VfXFAm5&wO-F^P@;rr?MNU!I&=e;q{Zx?!ax}51q&*uwWzP_AJ^zH5Va(Sim zJ-r_OO?0vKod_BCz|3M8g6!ELqmCq zxK)oFg_$!mkR7K}WGTX-awVdkXoa8_Py%U3r@|++>&)TLTi6c|pN5s?6WRrMa z51LcjK1rM>q)gRH4Z|GE&PaAiF>d2^QrVA{0$4Fk5t-I8WEW-^Is*4{YQ~D!v%M9g zG2+(JgKO#_5$258abV+q<(lk*1!S(N=?&_0PY^Q-~Q|OD)$K&5(r|I&Pc_ znjf>nkibjng;J>p4$x>84W^z`3Ia1k_NTB{PpjG>EUHTjtQ?r8;rec zxf(uEGOhl^41#-l*YBfkuEL$*;Hpy1IIED&WXY_&Gg#aD6oX&)bPO0}Q@*j)S8=;( y7Q9ze-RfW7O`;*p^`M-P6ozRdCF6g`1trr(3nN{hSC9xyiE9x0X#N1NX^iCWjYJFp literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598 b/tests/resources/merge-recursive/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598 new file mode 100644 index 0000000000000000000000000000000000000000..6aaf79fcbfc06afb1e1f44d7b4d3b28dc0039f73 GIT binary patch literal 443 zcmV;s0Yv_I0cBD^J)#)~`ji=kUB$8BMjIypcL0h$;xRP)6!ZczhVDt&1Uz7p0H#<{OJl97U zgcwL^i5W6ZZB!^p_&s_KCn&Y+wT76b&1wP);T2CVV{KWjLBcUKIyRwQV5gK<6 z)~_RqSxxl-GK~?&j23};H_~@_E=n^{u@yQ>XL$7&gwgEO=&L0>iH;?yFt$ALrztI(nqaC8z&a>7Zp&I8pAN0WCFP$2O-q(?ym5+LeLvPrCSY~yt{$?L+G6)tk4kU%Y>M`aoYkefxZI?kit~>LRrGIFjO%(nP(;)X}h2946ur z+LbAYF6|e45M+MnQfz#Cg!Q=4 zcScoIbgOWsAqe9+zywtAm4JvTr`nO`;^Q?4rJEG{3*AXqz;l)RAP<^TiqjU1LQY#8 zWu!jvaHT9cOX*tOx+lA!9O@(b7D2)2Q&eZP)c?X6q-l}(Qc33dRH8Kk2>NQ?wqlvpHMgV+B-53jsBV)y09GA#Q*rNI9BU?-PR8n4rX8b{NiUDrkJS z#>IVIL}6sEP#W!F$k9Vhr9Eu{287ne%oiFZ_yGf?T1>7bFzwL9(DilYug92+rlY;JP3e_oWtY%)Op0?3(po5hq;V1r2sz+akEz z?=)AlD5y6_xc@uC++(7eNLWJI3%(6++bHOZX5GG{O}FfcPQQAjKTj;SFV484ZsW?AiFEQdC+9rnV945H2X<%(J;g~EU(}5nuN{Vx(L{|D_S)wu%0h7* zeMH0%tsDsuDSGs|HW>4ZT88snmwR2|z(POOiNAwiw@Y<7f5GG{O}FfcPQQAjKO^aWb JodCsxT!hleVc-A& literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f b/tests/resources/merge-recursive/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f new file mode 100644 index 0000000000000000000000000000000000000000..fe8b15777c9b82728282919b53e65c78672beb47 GIT binary patch literal 198 zcmV;%06G770i}(xPQ)+}M5+B1tE=H;=Nu=I5P|{;`V;-e-dt9Sy^-x)exHE&1G<^_ zq!}+Ua)qXM4>c2kZG6Bm=nK+xp>A}>x~?VH_7?k*tmy`824fU%b(Re8VaFWcI!i2) z;F%8klV0gp{I#=}_yWf2)-|T>H9TpnwL0i=Ug@7R>N9a=Tp-h(3rkY!4JO*5$hIEP z^GXG#ED=uEnc$1_jT0k$$X00QiB6AzcjN`fg@6;(nbf+C3wsC>k<(dy15#OGDOags A)Bpeg literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/7c/7e08f9559d9e1551b91e1cf68f1d0066109add b/tests/resources/merge-recursive/.gitted/objects/7c/7e08f9559d9e1551b91e1cf68f1d0066109add new file mode 100644 index 0000000000000000000000000000000000000000..48d22f6a920dbfbeac68d36f21840d9254bf22bb GIT binary patch literal 443 zcmV;s0Yv_I0cBFXZrm^o?yRR^cQ;dk44FH0O3|b!I?6U>5hsg|M4jumFDc(8Lr00^ z_eYM2M|gbv`sw-4%j>tF@bdiY$7kn$`x?RL8Rp_CgX|1%%@?V_0r;qbr4Fz)0g1Q* zXV^)iJ__PTr`x?Y4!3X4#JR#0dE0P?wrN3ex!meC(S)5Kq0bQgstl;T#hH@olRUx5 zlz^0$Qi6hWn-q#Oeu+MX0jhMp)({J~MNL2@yyD;rN-Jt5A|!fiGy`Sf{@z$lGip__ zP6OsN6x1Bv{TjKulPk~xz97^V0!@Tj(gCy14?Q`FG+po{tlYLUp}?)Uy0In^TIw80 zzs@KYHPr(sG)0&SS_J0ZL?4(gy;$ilWZYfy>H(Lf!ibu)t^iZ|NXSFL9ahRsG*usT z6CLNKi}C_gZH12V1HAeR!en+Nda;CO*0Ib2Q!8_NH>E{W6HK)MSO;a7+Pc*zs93IO z(|5l;ZeQgd;t9W_64~___4i0QXUzR9bn`RSiMtIGX)Ahg{{W#06#c{j+qVD! literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/88/eb3f98849f4b8d0555395f514800900a01dc8f b/tests/resources/merge-recursive/.gitted/objects/88/eb3f98849f4b8d0555395f514800900a01dc8f new file mode 100644 index 0000000000000000000000000000000000000000..41c812f10d9ba495e041524671e43bfacaf76380 GIT binary patch literal 209 zcmV;?051P{0i};SP6RO!M2US0XB!Q&`{R$aLTo@t#s&OoGaJkp86&RG0Lux8s_Ip% zr7C^fu2Unx-5es?TNJ{;&SwZr2*@opa9pTxHjoK@@vW-2G-RKez-EkKjT7TzZLX4q zoN{G{&P5+FfQwT8?h~|xuC+pm9VY-30Irs^4c=1=zM4YKsPr+{KJ-)lOrz?TwQuiz z*JnBCw|kVI;+F19-yXCjVx|xx>&1{U>Y&HCDgS(&Liz*)u&&*EO64-efAr_ LELMF1OGsspd1+=4 literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/8f/35f30bfe09513f96cf8aa4df0834ae34e93bae b/tests/resources/merge-recursive/.gitted/objects/8f/35f30bfe09513f96cf8aa4df0834ae34e93bae new file mode 100644 index 0000000000000000000000000000000000000000..1011a885dad02485ffb3df66146a55cf0416c94e GIT binary patch literal 161 zcmV;S0ABxi0i{pN2?8+?^_(iwfRcY^LBz{;U;~-VgcV#PF>3opu>)`K1MidTx}Li< z&TV>QmTV@Ag@_Pz0&fvf2L$xM0fGs|DsVbEaZ8P_UB(=-fafVf(WD_OA3}-BkjWTh z2smXRS44XBr8YU!D>ag*rLOmSm3toQ2Oarq{Matn^&qu#-uuyKwVy1p-r|;GVp2~W$cLKNM%oUK{=F;c3zVALLE5QQ@Nl(JJ(P;zT>A#b=A* zgg2yiA1AFgW)q6j(U4;CvHEx=w5d&1oCKSlS)Ij*t*r;Q)Zs*|1+in`^!?^*<{byf z+)}d*rt?Bile3RzBUDpFVNtY6hJt43E&?8RpPDr<)dM8T-g;rK42wi?bh`yJ)I0{{ zXMQ1VsK3ysl#$yoGx|VBs zmuMWUfiJVHaHgd}lk9)>`{-t9xjnYs8jQO~TMeBknN@!ifrA(NVgFYiZ^XS~@Gz=w ztw!V&*)nVI4ARa%r5KJ2Jw`y`ec!#cS8?A|74Oyc_xiVYlWB={Sj;CR1u$!*mW%rX H{Cb92!PzdT literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/95/78b04e2087976e382622322ba476aa40398dc7 b/tests/resources/merge-recursive/.gitted/objects/95/78b04e2087976e382622322ba476aa40398dc7 new file mode 100644 index 0000000000000000000000000000000000000000..e3d15aac36c0fbbfd053d8ab640d1422fdfebd9f GIT binary patch literal 620 zcmV-y0+aoC0Zmi8uGBCPrS?}0eON(3fwrOm2?>cSA)uZllZ_R}&h^-Y@b#Q=c7ZaE z=gfJz52>d&uiw3W@%_X5&-C@nx2KoRJ=H=!mPAZjDpckyLeG_RQA!i-TqJk*;}m_0 zN&`l8kI*SooTMzE3!--f6V#mPg``y>fI}VV! zrDhvU=Y>8^&OVxrP)!kqMbRc13Yww22zWevYSuhg50EH(>xH>8EE2)d?H0^X^B9ny z`GvHh{z4y9MsCB*=mQ<8(#l8c6VsS}!{_Wb0ppcRQs-kwlh2AIc`@zxYQQrzC2D70 zqH(YWzRa@1nU)4kvj5fZqnn}S{@8YFFzz00HFTn6R{c!`4qoVo{a<;!6ZeY2!>GEo z8j(|E%dEXKNIUzKVmL1JJ^~8w`{Avl6NZB)akV zn~^5Bb-nDs!Z&+21C;C)on}JPL>dEojZ(yNDCCo`p3&7Zy=u2@1eL3(w@f+uL6o^j znX#DRz)2ko)%h;|@}aU+##9ZX5ZCB)4XUnG4D8g{S>&WP>D#`v4v*zsdx7Vrt=G1} z$%gx>&i3{Axo&e??}3~X5uzw?M29#HcZ}QZpEc8y^%sNhHaPrs!T~PZ-XL||^73$` J=>uRAa5Ko2Zmj?S literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/9c/3f1c70db28c00ce74b22ba3edafe16d9cf03d4 b/tests/resources/merge-recursive/.gitted/objects/9c/3f1c70db28c00ce74b22ba3edafe16d9cf03d4 new file mode 100644 index 0000000000000000000000000000000000000000..b0ef1af86c8ef909ea5a8c752c205e591fedf38d GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjKw?ZxlQ5_CD}FxvcBC zX+Yex%B(FQp)f=ma2SMA0x^Q)NB~YC`$!>X8PqLRzBcW%pCybOeGp4FWHUM^-T*x;%4xSN@mOlDG@ z=SPzo!lA9q(oh%zIrh<_B|r?xYcNb44I5$t)1fco8CPDK#DrA5<$%5ndGJYD=L#yF zbs|LPs25}?BJORfs~qzyu36ruI&XE6XYT1oUHJ=s?v(0$kpqCW;GMGas6Z*v(>}KG VpEY94{c_y;g?QgL@dgtZQTd25Q}_S? literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/a3/4e5a16feabbd0335a633aadb8217c9f3dba58d b/tests/resources/merge-recursive/.gitted/objects/a3/4e5a16feabbd0335a633aadb8217c9f3dba58d new file mode 100644 index 0000000000000000000000000000000000000000..00f9c2ddd1d8e6b2df28ccc58ebedb4e3369507d GIT binary patch literal 164 zcmV;V09*ff0i}=K4FVw$gngz88qn~wu#gzz%XMG_!0zQtJTZvczG&>gx0!E}$xNy1 zdhXHyYQpSGzyV#(Xy$BVF0%$gaZ8P_T}HGSl{JRZ1xf)~7dD!V zj*fyaj)9{G6zS2I+T=v9)JUF|y58$m?s?FUI`TL8xm~I2L23Z!oHoJBtpcUQpkr+3 SKWoH9lIJ-a@ z$8+Yq+=tZD+c)puz54#)=`(%(^6mNJ+;c7DV@brcr9x%SBJ@%@7o{}O&P8%}KTgr7 zs5D?iR}P`06vgS5Qk-hA>wVy1p-r|;GVp2~W$cLKNM%oUK{=F;c3<2ht)u`f#+VIn}IJ(#5a1;LR5xTw{XwHZoM`8?_-rwp z@P^dxM!&$W#l%@j6TqjDy@99J~55yH+;^16EI%6By~P^H2JJZk{8o{uLe9rQ=)d} zB^n27;L9v4oM~y$B>P|eKDrrN?vHJ^2IKD0RzoLBX4T(B;NXRR*#DKsJ8`cVJdCPa zs}VUxw#?c)gS4|xDTd=hPZ3af-w$u?RXjFT#d~%AqyFvPWLjb!7V`;70n8ey<>LMT HT=<4_?*22G literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/a8/2a121ea36b115548d6dad2cd86ec27f06f7b30 b/tests/resources/merge-recursive/.gitted/objects/a8/2a121ea36b115548d6dad2cd86ec27f06f7b30 new file mode 100644 index 0000000000000000000000000000000000000000..e740872fa03f7c70cd80cba278edec8365618b7b GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjKTj;SFV484ZsW<738(nBvk`QV@dir7t@=1fT|i4aAqT&k-2^_`{c zwzrrfmosmMseG*H;o;Ng58qG6uk?C;yX=j*yj|((`Ff@!UCvj!et9{a=-b=z<@!qJ zM|wYAPcJ*lePX5oJmIO{f^GhCnHmgp`AU&mMU-SfhL|NPBg_iG~DdShKBMI zb%R)+ceJ?NZ!2jVZN$5pMR$0%fqFY0@K+|6sfx~%WVVg-YdO`&Imv-!a3eODSl3pd z42o8j%7G?GbRkDQZ%KL=bo5WQ(ER^cRaK@LA4A9KK-ms3isP6=|5QQH=A&F1+qQh~ z5?f(%EiA4^Fv&NRoG@hQ97~<^1xy!XLp8Qz%;9dX&3PsA8Mpd0|aNaam3&>nk(;L+1o}OBxKMF>;rVul_mRhbenjs4_blf== zG(TpCA%U0D3#C#I9H7xG8caQ>#v?!Fg|y**PmeJnx4O6J1FdnU(XORWmYVn*oRgav zibtEH7#DWbx+F-_4(k0@4KPDfg6xzf8V5As((?#1B@JqM`Rm^W)5FVx91~uLHW+)? zay5LSWLo`+83gzAuHQ%7+=M&9!BwT2aaJLl$&y)lXRx;QDF(mp=@>A|rhI3sui}2w yEO@V`y4SzFn?ysH>p?joDGbv_O2+?=D@vw|7Dl>0uOJba64xNKH-7-HgN*UF>qEl; literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/b4/cefb3c75770e57bb8bb44e4a50d9578009e847 b/tests/resources/merge-recursive/.gitted/objects/b4/cefb3c75770e57bb8bb44e4a50d9578009e847 new file mode 100644 index 0000000000000000000000000000000000000000..836bb4edcb1dec682ff243dd480df1024b5c1553 GIT binary patch literal 639 zcmV-_0)YK^0Zml9uGBCPrS@0M2ZWWNpxNpcAR!@fC0_L;nQW{$cCN=JgsoAr8w1K*ZaW3LYr)xWZ=~}%GeRdk;Ep1!KGnf1v4-@jH*Q19;z_Uq_dB<{HHKD z{|{@d8k!3!-XveC@FJ8j4jlT&3WQD_ZE2dO_WhQHRme|}csK=87%6)d&@ddPn-9&H zTS_AhxDJ@r|2iE2IU`WG5AwR%sPNH&=@cLleDgsO#+a^VA~+oqmS&WV%I&7q3qp#t zizT8ZGt`#ZtHYvFGsxI1k0WVXg@!pHo{=`r8sxZIPWk+#scy)W`h!M+IML2&@!4WH z;SH(X$4RS=*@WVBG^AL3tUlfdZE8~$C&6ZCR%bC{YwN*1bvO}gLF^bfeZTvfdB*`V zx72Kd>AcX#$=Q!)BUDpFVNtY6hJt43E&?78pPDr<)dM8T-g;rK42wi?bh`yJ)I0{{ zXMQ1VsK3yMl#$yoGx|VBs zmuMWUfiJVHaHgd}lk9)>`{-t9xj(ku8jQO~TMeBknN@!ifrA(NVc%CC@5H@g@Gz=w ztw!V&*)nVI4ARa%r5KJ2Jw-s_eLuXlSMk_X74OyckNUTFlWB={Sj;CR1u$!*7W{X7 ZMak@tA>=lf15QMtECU?6xIZ-FkkMG{L{+DzX0l7ngPDQ6qH@4`Z1v)B;k{EET)RHf_-&f9U0!fjS z`XoJGUZ0;XU++J^xPE+pof#bXwHmP}3$b>vlXKxQayA7iCMh_$#kW-I)?tsfJTTXXze?Gn-CKv9j7x_W zHAzacAF{2;z^WKy;la3Z&6Dhms}*uz4+zcMhaz=eK+woHepo4&oA)xfpxxpLM};ir zrDm*BxcTK^LfQiQ!ZfxsqrJg)fl;6Mq?<_AsixL-t?FU2Vjxp@-eXFjaRh}aX2z80 S=1+q8cKeOjJBNSmtBjvLQHhWM literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/cb/49ad76147f5f9439cbd6133708b76142660660 b/tests/resources/merge-recursive/.gitted/objects/cb/49ad76147f5f9439cbd6133708b76142660660 new file mode 100644 index 0000000000000000000000000000000000000000..849668c8b64d036d37466e7d3036b4c2dd30ec16 GIT binary patch literal 641 zcmV-{0)G8?0ZmiOlG88{G*}7&o7UAV;)~F^zij^rXxL`FLe3*bUM+um*dmrna-c+ z^>{fw?I;h4nPPA;2(=}XOxF7yPT0sCW9W;g*i++eJJQVa%!%e$LdV^XZ0IO2Q7?$~ zc}I)O!*-H3Xd~XWEPBJU4KfM1zhrY1U1lG%37uXSpOCCPzgbQ3O^*tS-{42)K# z%7JE3bRkEbw zRTIH!Wj|J|n)cjM!Rx za7!Idgpv_E1~%Vsz9zfi0GVrQx7h6Jr`ibB6klA#JGN)2En_TRT|vf!0`PvTN;=r7r%4&&kaU#*@ua zj0-#JT@oZ|N7a6-20TMkg6z~wG!E9lmw_iZQ`4ZHm%sX5FaxwK$T8t{XoGQgt*fCE zCDZE9%y4i|uljwm-Ho^t46aJmOht)oB5P*#ok7~xrx^UYr(*z=P5JJvzKZ*%B6zQ+ bx!1qGn?z$M?WjH>DS&AsCGX82&R2r-HC8|q literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/d0/dd5d9083bda65ec99aa8b9b64a5a278771b70a b/tests/resources/merge-recursive/.gitted/objects/d0/dd5d9083bda65ec99aa8b9b64a5a278771b70a new file mode 100644 index 0000000000000000000000000000000000000000..b0d951c9e75d5555fc1c0b95e1b8d44bbee483e4 GIT binary patch literal 620 zcmV-y0+aoC0Zmi8uGBCPrS?}0eON(3fwrOm2?>cSA)uZllZ_R}&h^-Y@b#Q=c7ZaE z=gfJz52>d&uiw3W@%_X5&-C@nx2KD9PqmPbB@xq>3Y9sF&~xQnl+r{y7s=iII7OeL z(tr_NIfRZ<6sKECajLeVNabd_p@7evq~? z2uGTEy;2ZRE7T_+NQ?03!_lVZRI^@57r)|yOT)klW?*y}Rf)7cRAHV;XCH0(PhoKW zAJ$kkG#665Nxo9yMJQn$IP{Md2%S3G(lkx&|1Arvke?v&a0;X_QuZpKVK_`TADS_@ zltvnG9Wbl^bvgiYMxbyXa5^L`%_tj{+fAt#gcNBP zOGHa%s4cTshef4kkg-`FN7A$k4Rb;~BW;{D$Z@rt^7%_s-H<8u2aN)8qMg&?v&C@2 z8&bQElU5tE3B~DXNU``>eY_Fc)TSyanl;bW10>4cdSR{%i$rjAy9G1UJO<=v zej#nBztG2&k=rmc`anmjwDQsV#5AVg@HzWUz*0QkATAaet2uI;<2eJ-mB{$^>6Pc(-P~jm`_LwVAe=2FWnzp G-iBs$mNeu5 literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/de/a7215f259b2cced87d1bda6c72f8b4ce37a2ff b/tests/resources/merge-recursive/.gitted/objects/de/a7215f259b2cced87d1bda6c72f8b4ce37a2ff new file mode 100644 index 0000000000000000000000000000000000000000..55c1983a8cee9611837f6ae359504e403078d219 GIT binary patch literal 357 zcmV-r0h<1J0Yy?xPs1<_<(yw(?h`iz5~rQGATcra1KiYIqh?7+9H#wyUeW@mrq1*G zurv5L(Bt#d-Trvkf9zjRM>@Wr4)?}<@**_Fn#prM3k@~cSqjC8xFpVeu4PBXW==v* zDL5I0NTxD3=ZgKp{7+VNlKDCVPUT?hR2cg8tJQY)*ff`Fhu89L3O^L z=+_N|zp7KA5+~g?P%wwn+hK#xIo5NtRh)dws#`QO@&USLM1D+ZN7vcenJ%>`4MNCh z5MgPfXwVe(r4{kt@!+&*SmZxcpFqR5fno&aZyOu;%p<7L;o0GYmGop-Q4L7ebc)~- z4ln^{J(kK@OsUi4tdpSrMkChOg09+KbMkl$AghX%^?_cTU$(T8zxykw>B0N}=djOq D3bL=J literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/e2/93bfdddb81a853bbb16b8b58e68626f30841a4 b/tests/resources/merge-recursive/.gitted/objects/e2/93bfdddb81a853bbb16b8b58e68626f30841a4 new file mode 100644 index 0000000000000000000000000000000000000000..fab55fea641a6f1468010aec61395fccd7374c4b GIT binary patch literal 207 zcmV;=05Jb}0V^p=O;s>5GG{O}FfcPQQAjK05AV|0V^p=O;s>5GG{O}FfcPQQAjKBRoFqh2M5$J? z4c>DLzM8_#{O%cTrij)!H9pqnDqGAcS8?QA^pOI(sAWlSbL|5j>U$a$p4Ps-_8o5Y zNI%t`eic9Ny7cW9EVBrQXc5kajaiQLI5+j5nB`H&v%)tGPJg{{g3CTTicf{M(4xzxK`?T zz0m&a<$&4a;X;?!({4xKuG`b)OotbG-(Gg76{RjPlXu3uNOevW2W{;NC#>S=y=${2 z-%@3D-P6e9$bm+mUBk_av}-6$UIwDJX+@Js-EtBZXceugIm(1(?Wohk2EP)SM3py| zVq)DGJD07_=MXm(doy4_`MTB-%)n?Cs_bY4MdOl}eNC}5PN09X2Iv1_g;haQ*1H>r z4JF+}DAs{P|5$<0qN7~u+qQha1=f+tw2(Nf2qfE3Fp)t+a5UYFEymouZ>YvNUt-VXeW#euH-^*6UiIv?>MTts4gxImL_Kq!2{KPM$4fSh! z@d3HjokkyM_L&Afmp*A~{BQUi%*bFo=;Wg^ZbhvL5lQM^wBM@%&(IVXSK=ia2W#L< z#{--xX;914U;W0Z4q7JU81Ooz!I+)P)zFEON%co&IJl;F`9A38PTWK+&O%iUS%@qm zOJ?PrLE6$M@9ey$tpk(}>EW%sipNbk;=QWsQUCI80`)G}z4(Nr049x$X|4VMzm1CR DzwlOd literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/f5/1658077d85f2264fa179b4d0848268cb3475c3 b/tests/resources/merge-recursive/.gitted/objects/f5/1658077d85f2264fa179b4d0848268cb3475c3 new file mode 100644 index 0000000000000000000000000000000000000000..3b4eb97e97d52f89e379338afcb19b587f261683 GIT binary patch literal 355 zcmV-p0i6DL0Yy@~P6IIzr1n=VeME;qN|lO|h>#E1^X=uVb8N?6C(7S5_64Z?nw`h4 z2OkG|eSdq}9rnAg-N*4jhtK2w*_dx$gr-U~E<2*bJ(fL(So$ovP zbpzo`bt+Wiq?-l`CRN`ppK;8wo|>)VJ)Dn`~DdUk$!q?LT_zo4cU^8<7L&-Q!` BuvGv6 literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/fa/567f568ed72157c0c617438d077695b99d9aac b/tests/resources/merge-recursive/.gitted/objects/fa/567f568ed72157c0c617438d077695b99d9aac new file mode 100644 index 0000000000000000000000000000000000000000..ad5a3cf4f2289302c712888568f61f6afd43fa1a GIT binary patch literal 662 zcmV;H0%`qt0Zo*_a?~&oL^*we5nZCWAp06)- zd7!t`_555>9uhOf;9?MJmP|5P?<=gZkvYcDEuLaWwYSYkGtV<8n&T2$ZY#2(rMyPn zAlB!KR+oo;CGDe)c-OG#4$n5wV5cMg%H%TD(Rq^0wsn3hr-ryBIg*TS!UhwY#tM`{ z(WX*4&4&H{>c`a{~xQW$~5C+=s6uJ+Yv@_9CPTODhS$rluKjRmG6CG zD@?9~#mxvN`IeFsh76r!sdv7B>0@lE!FG(9+^?Mg#HoO6YtQcsMgd0yrJaXG=uI01 zfKgusA~bF6#{x=^a^DNpY%@HstcgfY7H;$4#NeX5B#6|30E2cRj(5<&8}1ME5EF8128%w>78jcAR{CVAjlaP;xtXDO zvN?)zp`y+uL6UY<@3(4z8JZHLQkG~O(16Rp6UdY_sN?moe;3REFDr6Pcpchc?A^-M z@QIRX^=D=fJkXndpKN;*?gR%nm1?F%h3qCvX62p1+TN!a{C1#Iz$lyYovpr#`%R1B wy_))7|MG4UjbUj<<%Fa#OdBZ~|2wWInJ!uw>4&_5L|{tXfY8DG0Xe~qQL@xg)c^nh literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/fd/8b5fe88cda995e70a22ed98701e65b843e05ec b/tests/resources/merge-recursive/.gitted/objects/fd/8b5fe88cda995e70a22ed98701e65b843e05ec new file mode 100644 index 0000000000000000000000000000000000000000..b6f14634e403147301851fbcdffc67b275b3bd7c GIT binary patch literal 165 zcmV;W09yZe0i}=44FVw$g*{UR4QPNJU`UMdvK`m}FhkbEieWLv?nPq<-ri60<-J_j z_1dKYx9g2r!eqgK56G5vGMWf1BI#^`P7ye1A0kdD?y>Q$OP_rzXgPQ+nlxr*ohx1$ zGC6tgTnZTpiinTC)Fx+o#zyk8)OD*jdE}9P)S17<&mD4IPf`O|3+N`4dj(2~k&dyQ T|HO!y#^w6j21&dDYs63!76MG; literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/fe/f01f3104c8047d05e8572e521c454f8fd4b8db b/tests/resources/merge-recursive/.gitted/objects/fe/f01f3104c8047d05e8572e521c454f8fd4b8db new file mode 100644 index 0000000000000000000000000000000000000000..715b6a8657cb82115e844624d7b8153d1d17b9f3 GIT binary patch literal 207 zcmV;=05Jb}0V^p=O;s>5GG{O}FfcPQQAjKnBG<2*eK(fL(So$ovP zbpzpRbt+Wiq?-l`CRN`pUvSK^o|~=WB!phksfhaE=JlVLJ)Dn`~DdU1Yvrj>l_zo4eA`2mp_&;vFX BuR#C+ literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchA-1 b/tests/resources/merge-recursive/.gitted/refs/heads/branchA-1 new file mode 100644 index 0000000000000000000000000000000000000000..b55325c3efd276f01c0c53e28b713337e5139d9c GIT binary patch literal 41 ucmV~$$q@h`2n4Xd)$F2xl*2LhUxJD5r~%pMinJPnv0#=^HbNKq*5(-WL literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchA-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchA-2 new file mode 100644 index 0000000000000000000000000000000000000000..d35574340651df1d5d1ea6c08a9bab1a72292b8b GIT binary patch literal 41 vcmV~$NdW*L2n4{tX%JbCI3U0+|D2m`>sX~YR)oFULZf_KC&rC1juaso;N$P8a{-fd)1J;w+AL<`se literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchB-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchB-2 new file mode 100644 index 0000000000000000000000000000000000000000..d5cfb2762cabe96e94c4ed71118badde1c95a0cd GIT binary patch literal 41 ucmV~$!4Uu;2m`Rc(@^0k>(m4GAHjszQCPffw+Sj6?WyQujAJMh6z2ow%L?fL literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/asparagus.txt b/tests/resources/merge-recursive/asparagus.txt new file mode 100644 index 0000000000000000000000000000000000000000..f51658077d85f2264fa179b4d0848268cb3475c3 GIT binary patch literal 587 zcmYLHOLD^?4Bc}Ib)V@4vdXH z(wKnfFoN&ZZdN?glT|W`=9oIFDEp;!SOQUp#n4tWxh(W_IulcJVx?Ib*v0_b-DC_7 z=w_j&O)N=Dk~?8xuSdhqW+1L1phDpca6hXx8JRo^YI zIObSy%~o;pkWIH}X5<5O%ZU8w(t)0{voo!=C=Ei$Xb@p(t!U5`_1G?P?s#xo6fE)| zs&}AaTSL(T^EZv1d*%_;sPOEt!$^8EjHm`AYdS^n2nXnZvmQg`ET+_Ha@I*u|DqA& fYe6^dt~q&p29Q<7$a+Ip=a)UL*~#u literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/beef.txt b/tests/resources/merge-recursive/beef.txt new file mode 100644 index 0000000000000000000000000000000000000000..68f6182f4c85d39e1309d97c7e456156dc9c0096 GIT binary patch literal 1372 zcmZux!LHLV5WM><_QoZzAHcgIJq}zz;vvqPWRqBRY~ytr%GWb%_X#1mv~p|j&dkp2 zhnJU+^!4+%FBj)t`6^U5q0Psc6rYq98by|YrlaDp5QosNEJ1W@zu1Ey>(iK`4DQ_g zw9$TO#Y3v77>Q55lf>$ag!@3LvQ56;4E?j8}!gvla0Tp~D zAY#g?4y3vGcnw17CdK|jchVj3T;(yyljfA-as;E0(-CKxX$(AFDND{$hE}(p$u1~| z`i#CsP%!!w)fp}IziGJHgi^kSG_P-&|)#-4{#a9+Mv5=BW7oFw!-%v zP$4Y@)F4(D2X= zg^*60(ateSc~6kQ@tbM>v2%o19!2z6+2F!KNQy7;j$V-|v4~(+zPe^7G(q|v#sCK( zsTMta?-IbWg)F7367qDXnag$Pc5KB_harv)!Y10Mfq=jp7y<=fEB-=cp|;=QWJTJM z(A7G;?F_gs$i;oFg&T9PorPVq{yXA?>!+Z>>1$gA5BqgXp zl)d2F@V1SDzL*9U(1EDO<{xn7xx>#=;i4E literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/bouilli.txt b/tests/resources/merge-recursive/bouilli.txt new file mode 100644 index 0000000000000000000000000000000000000000..4b7c5650008b2e747fe1809eeb5a1dde0e80850a GIT binary patch literal 1092 zcmYk5U5?u@420ix3Le0l19abt0__3|v_N;!XW6E0!HVcgR9xS_Lpl2c1OcK@!*7Pv z@1L)~=+D#35BmD^_38Qf>EhgrUzv1aN@3ulwE3(gQ#;Zn3@i0Hl$G0I&pdVHd#Oqs z<^|WFC}9nj|-QLEB74>h?%i!wBm5pC2?H z5NajGUzv7)bR-F<_b!siCt9T1ZAqLv6nmvZDufQV_!~S0Tm{XR8J0K!ahVf?yz%*q@#zm^@){YAh@B235V# zd(vfvJDJXmPw|r&y$+76#Secx&$M%lu4k{DIs}NGG~t)!1`V_@hTWH?QK@Wp7ct~J zblGx9Yh*HbhkElfd{qu1EIC5uWy8Yvvy*o*9A$W-Ljl-{*^E!`a&in=ugH z4yQ{kUmhM>rk+ZjrZ5CfN`I?Q8uV!X-lk-HH=bF2ANhUYAt4d$!Z;eEg^9VN;Gt~T zs;NfP$k>b>*_2nZdFFDS~^jr)hcPt3-R{ e9o9Bu**`CHo3z$;vxxw{7`=)w_G;UjT-<*kZ-7$( literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/gravy.txt b/tests/resources/merge-recursive/gravy.txt new file mode 100644 index 0000000000000000000000000000000000000000..c4e6cca3ec6ae0148ed231f97257df8c311e015f GIT binary patch literal 390 zcmX9)L2kn^47>Xkd_da|?6gyl8&+(Y(Y^3lH<7BTm(g|Y>S3~CB2#zXb19%0K_TVJNQEB$B$#iH-*~-C_yiai$1`1g+}r15kD{bR+?Mf56N8>)Vgt z@cQ!S*V(zhz9sNwf~k5cAREI+_f;Bj1m2rqZ6{c}fFxXi3v47w9~JSt(`{ZGPxo&w z#HB$IYY&%2ZiE-Ps!(a zf+j<+HKfXYRuj+&Z+LPQrB}6*5Hfvqnt+P%c&{vn8MUfdhXHdMD{2m3evRBc$Q5V@ zUlCf5kpf|nyu+mPr;(f_3RgS`8~61{C~~i^t*lAJo(D(PuOo_CO^pC5g#@9ZMPS}d z^p5#DijDq3!Odl_9&nx;Tu^h?6=2Ta33-UP!9t~rrs|_^V&L3$QJI07tW_mdrYW;}#xl-A QH$PDeJZzXqThZBl0*7D{sQ>@~ literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/veal.txt b/tests/resources/merge-recursive/veal.txt new file mode 100644 index 0000000000000000000000000000000000000000..94d2c01087f48213bd157222d54edfefd77c9bba GIT binary patch literal 1095 zcmYjQ%dXTg4Bh7|%)ZPI%<3*cLPFw72w1zV+eS^3-Z-I@uje?O0hUeUz8k@@0@$Bg?ucDnD$htELnu!D(9kJgY*Be#;T#Ykm4-)Muk_QgmK`| zKUN@g>1a#SG`0V87FHoYLE`BYNMWSxRY1dVm}VcEF>^{I4Y&@N&HuU_068O2xDWEa z*{JZ*f$0<=5q$GW5yqIVW+FJ964qvvjmkVL^@5Nh9b$=S$qcn+@#?Us)C@8<%i~Cz zHlbljh-ajovj#cumQ%idX{tLirT(N*AWpP%T70$`E_g#~_i@r{V>Y2U9StcKAFGcu zp-pY7;w0GY!s;wWY;8Swqz)%yEr=Zhm+uc>GaooW=9ZdmFr7F0G&%cdHbON;6c$CB zWGHBc9wOlJ^r>0%Ry{zX9IY4T%CJfVM|W5-L(OAAe&!d_hWZT{TYMH literal 0 HcmV?d00001 From 75dee59c94d39b308529d45dfb993b25d2e9a5f0 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 26 Oct 2015 10:37:58 -0400 Subject: [PATCH 275/450] merge: build virtual base of multiple merge bases When the commits to merge have multiple common ancestors, build a "virtual" base tree by merging the common ancestors. --- src/merge.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 123 insertions(+), 10 deletions(-) diff --git a/src/merge.c b/src/merge.c index d7b23e2c8..05ca3d0ec 100644 --- a/src/merge.c +++ b/src/merge.c @@ -27,6 +27,7 @@ #include "config.h" #include "oidarray.h" #include "annotated_commit.h" +#include "commit.h" #include "git2/types.h" #include "git2/repository.h" @@ -1922,6 +1923,117 @@ done: return error; } +#define INSERT_VIRTUAL_BASE_PARENT(commit, parent_id) \ + do { \ + id = git_array_alloc(commit->parent_ids); \ + GITERR_CHECK_ALLOC(id); \ + git_oid_cpy(id, parent_id); \ + } while(0) + +static int build_virtual_base( + git_commit **out, + git_repository *repo, + const git_commit *one, + bool one_is_virtual, + const git_oid *two_id) +{ + git_commit *two = NULL, *result; + git_index *index = NULL; + git_oid tree_id, *id; + int error; + + /* TODO: propagate merge options */ + if ((error = git_commit_lookup(&two, repo, two_id)) < 0 || + (error = git_merge_commits(&index, repo, one, two, NULL)) < 0) + goto done; + + if ((error = git_index_write_tree_to(&tree_id, index, repo)) < 0) + goto done; + + if ((result = git__calloc(1, sizeof(git_commit))) == NULL) + goto done; + + result->object.repo = repo; + + /* if the initial commit we were given is virtual, we are octopus + * merging - that virtual base's parents should actually be the + * parents that we use for our new virtual commit. otherwise, it + * is an actual parent. + */ + if (one_is_virtual) { + size_t i, cnt = git_commit_parentcount(one); + + for (i = 0; i < cnt; i++) + INSERT_VIRTUAL_BASE_PARENT(result, git_commit_parent_id(one, i)); + } else { + INSERT_VIRTUAL_BASE_PARENT(result, git_commit_id(one)); + } + + INSERT_VIRTUAL_BASE_PARENT(result, two_id); + + git_oid_cpy(&result->tree_id, &tree_id); + + *out = result; + +done: + git_index_free(index); + git_commit_free(two); + return error; +} + +#undef INSERT_VIRTUAL_BASE_PARENT + +static int compute_base_tree( + git_tree **out, + git_repository *repo, + const git_commit *our_commit, + const git_commit *their_commit, + bool recursive) +{ + git_commit_list *base_list; + git_revwalk *walk; + git_commit *base = NULL; + bool base_virtual = false; + int error = 0; + + *out = NULL; + + if ((error = merge_bases(&base_list, &walk, repo, + git_commit_id(our_commit), git_commit_id(their_commit))) < 0) + return error; + + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + goto done; + } + + if ((error = git_commit_lookup(&base, repo, &base_list->item->oid)) < 0) + goto done; + + while (recursive && base_list->next) { + git_commit *new_base; + + base_list = base_list->next; + + if ((error = build_virtual_base(&new_base, repo, base, base_virtual, + &base_list->item->oid)) < 0) + goto done; + + git_commit_free(base); + base = new_base; + base_virtual = true; + } + + error = git_commit_tree(out, base); + +done: + git_commit_free(base); + git_commit_list_free(&base_list); + git_revwalk_free(walk); + + return error; +} int git_merge_commits( git_index **out, @@ -1930,18 +2042,20 @@ int git_merge_commits( const git_commit *their_commit, const git_merge_options *opts) { - git_oid ancestor_oid; - git_commit *ancestor_commit = NULL; git_tree *our_tree = NULL, *their_tree = NULL, *ancestor_tree = NULL; + bool recursive; int error = 0; - if ((error = git_merge_base(&ancestor_oid, repo, git_commit_id(our_commit), git_commit_id(their_commit))) < 0 && - error == GIT_ENOTFOUND) - giterr_clear(); - else if (error < 0 || - (error = git_commit_lookup(&ancestor_commit, repo, &ancestor_oid)) < 0 || - (error = git_commit_tree(&ancestor_tree, ancestor_commit)) < 0) - goto done; + recursive = !opts || (opts->flags & GIT_MERGE_NO_RECURSIVE) == 0; + + if ((error = compute_base_tree(&ancestor_tree, repo, + our_commit, their_commit, recursive)) < 0) { + + if (error == GIT_ENOTFOUND) + giterr_clear(); + else + goto done; + } if ((error = git_commit_tree(&our_tree, our_commit)) < 0 || (error = git_commit_tree(&their_tree, their_commit)) < 0 || @@ -1949,7 +2063,6 @@ int git_merge_commits( goto done; done: - git_commit_free(ancestor_commit); git_tree_free(our_tree); git_tree_free(their_tree); git_tree_free(ancestor_tree); From cdb6c1c83dd6f0d30b798a7f62d4f3849a1f11a1 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 26 Oct 2015 17:14:28 -0400 Subject: [PATCH 276/450] merge: add a second-level recursive merge --- tests/merge/trees/recursive.c | 50 +++++++++++++++++- .../0f/a6ead2731b9d138afe38c336c9727ea05027a7 | Bin 0 -> 206 bytes .../41/71bb8d40e9fc830d79b757dc06ec6c14548b78 | Bin 0 -> 207 bytes .../4e/70a6b06fc62481f80fbb74327849e7170eebff | Bin 0 -> 207 bytes .../68/a2e1ee61a23a4728fe6b35580fbbbf729df370 | Bin 0 -> 665 bytes .../75/c653822173a8e5795153ec3773dfe44bb9bb63 | Bin 0 -> 206 bytes .../81/5b5a1c80ca749d705c7aa0cb294a00cbedd340 | Bin 0 -> 170 bytes .../89/8d12687fb35be271c27c795a6b32c8b51da79e | Bin 0 -> 663 bytes .../ad/2ace9e15f66b3d1138922e6ffdc3ea3f967fa6 | Bin 0 -> 170 bytes .../c0/bd078a61d2cc22c52ca5ce04abdcdc5cc1829e | Bin 0 -> 207 bytes .../.gitted/refs/heads/branchC-1 | Bin 0 -> 41 bytes .../.gitted/refs/heads/branchC-2 | Bin 0 -> 41 bytes 12 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 tests/resources/merge-recursive/.gitted/objects/0f/a6ead2731b9d138afe38c336c9727ea05027a7 create mode 100644 tests/resources/merge-recursive/.gitted/objects/41/71bb8d40e9fc830d79b757dc06ec6c14548b78 create mode 100644 tests/resources/merge-recursive/.gitted/objects/4e/70a6b06fc62481f80fbb74327849e7170eebff create mode 100644 tests/resources/merge-recursive/.gitted/objects/68/a2e1ee61a23a4728fe6b35580fbbbf729df370 create mode 100644 tests/resources/merge-recursive/.gitted/objects/75/c653822173a8e5795153ec3773dfe44bb9bb63 create mode 100644 tests/resources/merge-recursive/.gitted/objects/81/5b5a1c80ca749d705c7aa0cb294a00cbedd340 create mode 100644 tests/resources/merge-recursive/.gitted/objects/89/8d12687fb35be271c27c795a6b32c8b51da79e create mode 100644 tests/resources/merge-recursive/.gitted/objects/ad/2ace9e15f66b3d1138922e6ffdc3ea3f967fa6 create mode 100644 tests/resources/merge-recursive/.gitted/objects/c0/bd078a61d2cc22c52ca5ce04abdcdc5cc1829e create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchC-1 create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchC-2 diff --git a/tests/merge/trees/recursive.c b/tests/merge/trees/recursive.c index 1d3586162..a7358e7e3 100644 --- a/tests/merge/trees/recursive.c +++ b/tests/merge/trees/recursive.c @@ -88,8 +88,6 @@ void test_merge_trees_recursive__two_norecursive(void) git_index *index; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; - opts.flags |= GIT_MERGE_NO_RECURSIVE; - struct merge_index_entry merge_index_entries[] = { { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, @@ -101,6 +99,8 @@ void test_merge_trees_recursive__two_norecursive(void) { 0100644, "4e21d2d63357bde5027d1625f5ec6b430cdeb143", 3, "veal.txt" }, }; + opts.flags |= GIT_MERGE_NO_RECURSIVE; + cl_git_pass(merge_commits_from_branches(&index, repo, "branchB-1", "branchB-2", &opts)); cl_assert(merge_test_index(index, merge_index_entries, 8)); @@ -108,3 +108,49 @@ void test_merge_trees_recursive__two_norecursive(void) git_index_free(index); } +void test_merge_trees_recursive__three(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "15faa0c9991f2d65686e844651faa2ff9827887b", 0, "veal.txt" }, + }; + + cl_git_pass(merge_commits_from_branches(&index, repo, "branchC-1", "branchC-2", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 6)); + + git_index_free(index); +} + +void test_merge_trees_recursive__three_norecursive(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "b2a81ead9e722af0099fccfb478cea88eea749a2", 1, "veal.txt" }, + { 0100644, "898d12687fb35be271c27c795a6b32c8b51da79e", 2, "veal.txt" }, + { 0100644, "68a2e1ee61a23a4728fe6b35580fbbbf729df370", 3, "veal.txt" }, + }; + + opts.flags |= GIT_MERGE_NO_RECURSIVE; + + cl_git_pass(merge_commits_from_branches(&index, repo, "branchC-1", "branchC-2", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 8)); + + git_index_free(index); +} + diff --git a/tests/resources/merge-recursive/.gitted/objects/0f/a6ead2731b9d138afe38c336c9727ea05027a7 b/tests/resources/merge-recursive/.gitted/objects/0f/a6ead2731b9d138afe38c336c9727ea05027a7 new file mode 100644 index 0000000000000000000000000000000000000000..b06362dd8762c3f120e98a65923f5bce9fcf5441 GIT binary patch literal 206 zcmV;<05Sh~0i}<-N(4a=MUCexD%)tKYpePNBEn*5<_}bN)i@i>w6rbz`;M<%FuN}f z+}hUlGJrhYjBW;u5lczR&hlInIgPnQAGNw*HB@BJsl+tBO1Eu*5Steavq>rHh`avk0-)}GnQG&8Al05GG{O}FfcPQQAjKKV#Ms+PV JoB-JMTjB{mWX1ph literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/4e/70a6b06fc62481f80fbb74327849e7170eebff b/tests/resources/merge-recursive/.gitted/objects/4e/70a6b06fc62481f80fbb74327849e7170eebff new file mode 100644 index 0000000000000000000000000000000000000000..1dfcec50bbd97451a545c4c11b1be3e1d637d061 GIT binary patch literal 207 zcmV;=05Jb}0V^p=O;s>5GG{O}FfcPQQAjKOO?0vKod_BCz|3M8g6!ELqmCq zdO)ntJ6c@sx0STDHsW2)q9;7tK)oFg_?5|Js-p8GnQi0zTB`auCpnM|Zo~l->)HyG zLD8yGInV@&F65~7mZWz!6@KpptSX{2)${g z05IySK!m21{ZK$@DR-Sv#Wut9!kUQWWZ^dVP7E%}bAlMHwM|13N2a=uh^L{+Ch@!; zG^ey}Nt`F7Ow~#a!yL=bNOnmvZtb;GIggbBSTRiznbt967iJea0{2oiW5sK2Z^dYg zxV6l|HFb~(b4Kha*tlQ0Cc9t(nQLl#gZkXlV{7!IV1#Q5F=N(J%XP+N$ifUAH%B#F7cGo*eO^H#FeR=*=%e`qr0I-=FuYHp literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/75/c653822173a8e5795153ec3773dfe44bb9bb63 b/tests/resources/merge-recursive/.gitted/objects/75/c653822173a8e5795153ec3773dfe44bb9bb63 new file mode 100644 index 0000000000000000000000000000000000000000..1495f70f4bfee18f37f71850b0fcbd5b8b0f723b GIT binary patch literal 206 zcmV;<05Sh~0i}<-N(C_xhOPH0raRS`zn6ib#2=%AalsGcizF-v2)Xr9_NPr6Ei(pe>V80!RfCTPH@>rgVJTM>;2hJ I9~XmgJZE)kP5=M^ literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/81/5b5a1c80ca749d705c7aa0cb294a00cbedd340 b/tests/resources/merge-recursive/.gitted/objects/81/5b5a1c80ca749d705c7aa0cb294a00cbedd340 new file mode 100644 index 0000000000000000000000000000000000000000..12eb0662ae04224d99b15e0e5232c198f22be9d7 GIT binary patch literal 170 zcmV;b09F5Z0i}*h3IZ_@L|x|;eSwjDI_V6E2;vRAK$7WT7MVzlczs9l2JWg}LGeuUTZ}rc(ycKWeq1o zWR}c`G0rhMBf@jsT8GQ>3Vng=t<^_c;lLaHq!WLKU)!fvkDw5(pS4v4lSC=SMmO2P Yf8L1NmXb>fqihOgh4ZAv8v-^`V*1rhTmS$7 literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/89/8d12687fb35be271c27c795a6b32c8b51da79e b/tests/resources/merge-recursive/.gitted/objects/89/8d12687fb35be271c27c795a6b32c8b51da79e new file mode 100644 index 0000000000000000000000000000000000000000..2ce4f7f0a4c72bf27a73631283d2ad87f508e7ba GIT binary patch literal 663 zcmV;I0%-ks0Zo)abJH*og*oe2EO(QcmSaviq(f(zrnD(N%l6vVSeA@d#+YBvTO|Q* zjV!I+``%M)A8We1yTAYN{do9Fucx=?oiWdEXL@`(pXfl(r!$?uyd018?d|Y#ex=g` zy&ulUmx?}93@!$tX2~Rz^}eFh)6vKrW9Sx7v7_4CW~7w$5+m)DV{>dy>&j*kEGQSb;Jq z+A7TBKrDy+M8M=&>{UqhN$<3NfQ=spBT28L~1%$Bk1# z^J8%s5_m1WP%6#H0UFJ!!8A*1J@QjtNE_~V^biwrYX*xx&=wb(>{j|@sg1wEIk}mk zc(OT)aiOBlB|(yQRPWbnfEk(+q*9h>9MFKvz!S)nG^peCuYVWJ052z> z)$oauY4vAj5ZuwbexGc6748HFHF~8I==~!Z2;5Wc=Saqhz{hVWc1O1`>fOaRWjh%^z&2iu=paO|$?2 literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/ad/2ace9e15f66b3d1138922e6ffdc3ea3f967fa6 b/tests/resources/merge-recursive/.gitted/objects/ad/2ace9e15f66b3d1138922e6ffdc3ea3f967fa6 new file mode 100644 index 0000000000000000000000000000000000000000..8ae3ba5a744f2a3f167491861e15ec70c7ba69f5 GIT binary patch literal 170 zcmV;b09F5Z0i}+?3c@fDL_Oy#@&n3dlQb!a2;$M3Kd{~1Vh?SkM*MxF_y@uqX5PRs zT;_SI7Gv1dRn;;K0x47H1ep;zB2#3AK!dPwWOf;QHFsKdsTM}+lmwG^Lod!}8r5g^ zzT-Y&Qi2GWC>eUzTUqT{UbKq6-pc$ai`{FZA9vJm@k=`{^TE0hV(u|Uuv-WKrqOn` Y>Yp>_Qe+w@raH9?O{c9hZ%Q&#vn`5F+W-In literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/c0/bd078a61d2cc22c52ca5ce04abdcdc5cc1829e b/tests/resources/merge-recursive/.gitted/objects/c0/bd078a61d2cc22c52ca5ce04abdcdc5cc1829e new file mode 100644 index 0000000000000000000000000000000000000000..3dde6c243e93aec666ae3ef6a209106eb9e07e14 GIT binary patch literal 207 zcmV;=05Jb}0V^p=O;s>5GG{O}FfcPQQAjKNiI}Dm+wE8I^5xVyo=( Jc>u|AT#}r9UuXaT literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchC-1 b/tests/resources/merge-recursive/.gitted/refs/heads/branchC-1 new file mode 100644 index 0000000000000000000000000000000000000000..346b039b497e04072fdeb1e843f0547252be167c GIT binary patch literal 41 ucmV~$!4Uu;2m`Rc+W=>kaS+=5M=+`JY9?(9crj*i)18o}gB3~~71js-JqyPG literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchC-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchC-2 new file mode 100644 index 0000000000000000000000000000000000000000..67f3153f52a2c4fba8fc2cccc44499e448f0b046 GIT binary patch literal 41 ucmcC8G)*#1G)%TINKQ02u}m>HFikd3Of*PNGO{#DGyu}6DJjM#23!E_Pzyf* literal 0 HcmV?d00001 From a200bcf72838488c1550b1709a3f075f722e0244 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 26 Oct 2015 17:25:42 -0400 Subject: [PATCH 277/450] merge: add a third-level recursive merge --- tests/merge/trees/recursive.c | 46 ++++++++++++++++++ .../00/7f1ee2af8e5d99906867c4237510e1790a89b8 | Bin 0 -> 169 bytes .../15/faa0c9991f2d65686e844651faa2ff9827887b | Bin 0 -> 665 bytes .../4d/fc1be85a9d6c9898152444d32b238b4aecf8cc | Bin 0 -> 168 bytes .../50/e4facaafb746cfed89287206274193c1417288 | Bin 0 -> 207 bytes .../5e/8747f5200fac0f945a07daf6163ca9cb1a8da9 | Bin 0 -> 672 bytes .../5f/18576d464946eb2338daeb8b4030019961f505 | Bin 0 -> 208 bytes .../ad/1ea02c2cc4f55c1dff87b80a086206a73885eb | Bin 0 -> 207 bytes .../ca/49d1a8b6116ffeba22667bba265fa5261df7ab | Bin 0 -> 208 bytes .../e1/dcfc3038be54195a59817c89782b261e46cb05 | Bin 0 -> 212 bytes .../f1/b44c04989a3a1c14b036cfadfa328d53a7bc5e | Bin 0 -> 672 bytes .../.gitted/refs/heads/branchD-1 | Bin 0 -> 41 bytes .../.gitted/refs/heads/branchD-2 | Bin 0 -> 41 bytes 13 files changed, 46 insertions(+) create mode 100644 tests/resources/merge-recursive/.gitted/objects/00/7f1ee2af8e5d99906867c4237510e1790a89b8 create mode 100644 tests/resources/merge-recursive/.gitted/objects/15/faa0c9991f2d65686e844651faa2ff9827887b create mode 100644 tests/resources/merge-recursive/.gitted/objects/4d/fc1be85a9d6c9898152444d32b238b4aecf8cc create mode 100644 tests/resources/merge-recursive/.gitted/objects/50/e4facaafb746cfed89287206274193c1417288 create mode 100644 tests/resources/merge-recursive/.gitted/objects/5e/8747f5200fac0f945a07daf6163ca9cb1a8da9 create mode 100644 tests/resources/merge-recursive/.gitted/objects/5f/18576d464946eb2338daeb8b4030019961f505 create mode 100644 tests/resources/merge-recursive/.gitted/objects/ad/1ea02c2cc4f55c1dff87b80a086206a73885eb create mode 100644 tests/resources/merge-recursive/.gitted/objects/ca/49d1a8b6116ffeba22667bba265fa5261df7ab create mode 100644 tests/resources/merge-recursive/.gitted/objects/e1/dcfc3038be54195a59817c89782b261e46cb05 create mode 100644 tests/resources/merge-recursive/.gitted/objects/f1/b44c04989a3a1c14b036cfadfa328d53a7bc5e create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchD-1 create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchD-2 diff --git a/tests/merge/trees/recursive.c b/tests/merge/trees/recursive.c index a7358e7e3..cd879677f 100644 --- a/tests/merge/trees/recursive.c +++ b/tests/merge/trees/recursive.c @@ -154,3 +154,49 @@ void test_merge_trees_recursive__three_norecursive(void) git_index_free(index); } +void test_merge_trees_recursive__four(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "d55e5dc038c52f1a36548625bcb666cbc06db9e6", 0, "veal.txt" }, + }; + + cl_git_pass(merge_commits_from_branches(&index, repo, "branchD-2", "branchD-1", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 6)); + + git_index_free(index); +} + +void test_merge_trees_recursive__four_norecursive(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "898d12687fb35be271c27c795a6b32c8b51da79e", 1, "veal.txt" }, + { 0100644, "f1b44c04989a3a1c14b036cfadfa328d53a7bc5e", 2, "veal.txt" }, + { 0100644, "5e8747f5200fac0f945a07daf6163ca9cb1a8da9", 3, "veal.txt" }, + }; + + opts.flags |= GIT_MERGE_NO_RECURSIVE; + + cl_git_pass(merge_commits_from_branches(&index, repo, "branchD-2", "branchD-1", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 8)); + + git_index_free(index); +} + diff --git a/tests/resources/merge-recursive/.gitted/objects/00/7f1ee2af8e5d99906867c4237510e1790a89b8 b/tests/resources/merge-recursive/.gitted/objects/00/7f1ee2af8e5d99906867c4237510e1790a89b8 new file mode 100644 index 0000000000000000000000000000000000000000..d9399d71c227358546c7b4c9f7a02f01b31fe31a GIT binary patch literal 169 zcmV;a09OBa0i{k!3IZ_@^gOS~2S&1VX8{pGJbLp3SvpJ(naCLN`$q8xLLF+OsMu;P zok@gU--sAyVbSOW8XRZ}QYo7}1si}X&V_SYWaK#A{YGnNBvQ;#ab*J`2!|e>6((9| ztPD~M0v*HX`)6O;#xCjQx5REstBRC0;FmvLd>Jj>|~U}8~$%7#e^ literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/15/faa0c9991f2d65686e844651faa2ff9827887b b/tests/resources/merge-recursive/.gitted/objects/15/faa0c9991f2d65686e844651faa2ff9827887b new file mode 100644 index 0000000000000000000000000000000000000000..54cd6f26ebd11798f73ecd010880bbaa79b46ac1 GIT binary patch literal 665 zcmV;K0%rYq0Zo)abJH*og*oe2EO(QcmSaviq(f(zrnD(N%l6vVSeA@d#+YBvTO|Q* zv7}wS_q|nWA8We1yTAYN{do9Fucx=?oiWdEXL@`(pXfl(r!$?uyd018?d|Y#ex=g` zy&ulUmx?}93@!$tX2~Rz^}eFh)6vKrW9Sx7v7_4CW~70DrJM0F^qlsT?Z^~Sjz08H6$D*;luKjRmHj@k z6(-lg;${S$d`rm*Lx%RT)H`3m^f9*7;5gb$?$?C?#HoO6YtNqwMgd0yrJaXG=uI01 zfKgusA~bF6#{x=Ax$lK)wi%vR)71JD%X%jG6WLGR8b4^WeP@g+`?2LXCjBrgMX3SdZxXG9dS(%~Z#;KtB zu{bmdyq398D$U3N8qKP~G)rnd@>5<&8}4`X5EF8128(&1EiN?Kt<009HvR_Zu1=g^D_t1WDRayobo6It5Zuwb?oYP83U`9SO{JP?Q6U$TCA0F*VC^zbG5Bpqhk#Z#F~8I==~LNlF6$@t$me|db>MGGVSkT;MBOo2 z9i&#f-iQFBAmlR05LD7Sh?tVI-f`s4NN&*1&S~=Oj*Yes0o4)(I&Xw9DUnAhjj?{P z(FwIQW5J?l`0Q(KaEUK$5pHWOkGjEuM*2}F`W8R8549XYD5c$^=4FA2E6&+S$JtJQ W&ai8ZIpu&oj|&6mNw7CPgj3meM@@$S literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/50/e4facaafb746cfed89287206274193c1417288 b/tests/resources/merge-recursive/.gitted/objects/50/e4facaafb746cfed89287206274193c1417288 new file mode 100644 index 0000000000000000000000000000000000000000..b1eaee5572fc754d31eb4fd30360ab4a6d75c181 GIT binary patch literal 207 zcmV;=05Jb}0V^p=O;s>5GG{O}FfcPQQAjKF)01=Xc+ahp+TFAo z@=c>7)Irl)p&vV%A=9|zrCw7UjFZ+sS=;KrwxX(ZQ`Wnl!=92JnLNtThyJO8pv_0Q z)OTIk?*r?|WI9+}RYWJ-QZSJrL;7gyjV)k$?^|kc9Bn4^Ya;-0A|UD7@`r*kf}?`c z&cY(}hLs$^h_3<>npU=B0VSo}_mOJW37!|Wa2He1aGM4b6)s9SK#bJtwx#GNre^Sn zr=?kkXnEaBPGQ@MKF>&+>XllCIgXWz$ppc;wULB5ALhLBmxL>&jyEOKNh1_}tM$r{tqxgllp>Vb(&&O~Pcv zg&8_-oC=yB*`SHzrObs;X-0O?s1^yP$)UB#Pk141xZlwiACOx!Xv_nxKGUSvGEbUX z{~MfxnHh>FoqSZrRn(afk)$5Q`*k$H43pwwB`h(>Km#rVPasp$ppKWn{*6-uye!Bu z;C09ZjxZRW^-m9u_<6quQpwZ=K6i!GA&152D!hgs4{^3OyHH`E_T0z1w1#Up-z4`;| Ggp305AV|0V^p=O;s>5GG{O}FfcPQQAjKsMr4E8`L(}{dV`nm Ki30$}rCe1lgkm%R literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/ad/1ea02c2cc4f55c1dff87b80a086206a73885eb b/tests/resources/merge-recursive/.gitted/objects/ad/1ea02c2cc4f55c1dff87b80a086206a73885eb new file mode 100644 index 0000000000000000000000000000000000000000..99207a9dd65bf2aafc27ac58179a678b7b07084a GIT binary patch literal 207 zcmV;=05Jb}0V^p=O;s>5GG{O}FfcPQQAjK>zkO(%F|N4 JD*?MaTYLMiW3vDN literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/ca/49d1a8b6116ffeba22667bba265fa5261df7ab b/tests/resources/merge-recursive/.gitted/objects/ca/49d1a8b6116ffeba22667bba265fa5261df7ab new file mode 100644 index 0000000000000000000000000000000000000000..1ea596763d7fab7757478d282f14a88fe220af89 GIT binary patch literal 208 zcmV;>05AV|0i}<-P6aUxM5*^H+}-M;IB{MQLP&^`jvw$k4ttB;pb*6G6J9+MmB(l7 zkw)6SUj{huu0~e{ZI$Np z4^~61MVzv$ImF80_dcOtJ);it;0ik=U(7xYx`?E z;6^9?RCoGi{A}0K_FF(ApE6nWa5iMjGU+Tg^q(=yqxNTo?=(35wZaK5$7ry2K4iN; KP0{LLBbw~kpoumm^+sLx0m=Bqli{S9BIXjc z=SZY^u^CCvDWy2nzj-Q_Sh5&jLs(of#?1#-Lal|JkgGYwN4SX+mu@j-s_-C5{ literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/f1/b44c04989a3a1c14b036cfadfa328d53a7bc5e b/tests/resources/merge-recursive/.gitted/objects/f1/b44c04989a3a1c14b036cfadfa328d53a7bc5e new file mode 100644 index 0000000000000000000000000000000000000000..7cbaaeecf596e2fa780fe9255822559fc9527259 GIT binary patch literal 672 zcmV;R0$=@j0Zo)okJB&^#X0*^4A&}DT9CN()I(Rh651}iT{t_*B(dVy!G8$h)APn@ zfm>sH#_#>!xV7~)-Q9is{O;T7_=R52Z_j(Bp5HF?_;fkbk)F>Nx;(s`PW1Kd_;Pur z^F4h(UQRC+eIoCScafT$C{Ehiiq20b6=&~Vmo52@YO9-(W}asbH2dsYZY$ERrL=fC z5VcJeEhY`yN}x0ZW)-bzILZmj+R>n=1AZkkiE3{w#l*Tbb}dzd&mkTtj%LCE<(o!F zD1)N4LO*siL!xoXOTDHz7$>cNvWDjW+ls2vOXf;J!JQr~rD zzYnYF?(akQDtuZ;l2iGZYQ%O48H2#yL$I}3}@ z8&+}vBfbhmXj<8h1(cL>-$$xhCwN|15{W5jxJ`qJ3KyjuAVz9++fwusQ!{wP)6%R% zw7ec9r?72BpJ${@^-3+n9LGw+|D2m`>sX$C5=IH=G+f_H$oRB3k9ek5UGewR+)f)Xb(cw&6xH40Mz literal 0 HcmV?d00001 From 99d9d9a470ca9b693f6dbee05f1cfcd98a5d148d Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 26 Oct 2015 17:44:36 -0400 Subject: [PATCH 278/450] merge: improve test names in recursive merge tests --- tests/merge/trees/recursive.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/merge/trees/recursive.c b/tests/merge/trees/recursive.c index cd879677f..bff5d7e39 100644 --- a/tests/merge/trees/recursive.c +++ b/tests/merge/trees/recursive.c @@ -18,7 +18,7 @@ void test_merge_trees_recursive__cleanup(void) cl_git_sandbox_cleanup(); } -void test_merge_trees_recursive__one(void) +void test_merge_trees_recursive__one_base_commit(void) { git_index *index; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; @@ -39,7 +39,7 @@ void test_merge_trees_recursive__one(void) git_index_free(index); } -void test_merge_trees_recursive__one_norecursive(void) +void test_merge_trees_recursive__one_base_commit_norecursive(void) { git_index *index; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; @@ -62,7 +62,7 @@ void test_merge_trees_recursive__one_norecursive(void) git_index_free(index); } -void test_merge_trees_recursive__two(void) +void test_merge_trees_recursive__two_base_commits(void) { git_index *index; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; @@ -83,7 +83,7 @@ void test_merge_trees_recursive__two(void) git_index_free(index); } -void test_merge_trees_recursive__two_norecursive(void) +void test_merge_trees_recursive__two_base_commits_norecursive(void) { git_index *index; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; @@ -108,7 +108,7 @@ void test_merge_trees_recursive__two_norecursive(void) git_index_free(index); } -void test_merge_trees_recursive__three(void) +void test_merge_trees_recursive__two_levels_of_multiple_bases(void) { git_index *index; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; @@ -129,7 +129,7 @@ void test_merge_trees_recursive__three(void) git_index_free(index); } -void test_merge_trees_recursive__three_norecursive(void) +void test_merge_trees_recursive__two_levels_of_multiple_bases_norecursive(void) { git_index *index; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; @@ -154,7 +154,7 @@ void test_merge_trees_recursive__three_norecursive(void) git_index_free(index); } -void test_merge_trees_recursive__four(void) +void test_merge_trees_recursive__three_levels_of_multiple_bases(void) { git_index *index; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; @@ -175,7 +175,7 @@ void test_merge_trees_recursive__four(void) git_index_free(index); } -void test_merge_trees_recursive__four_norecursive(void) +void test_merge_trees_recursive__three_levels_of_multiple_bases_norecursive(void) { git_index *index; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; From fccad82ee8f431e06097b3a1282228b40ae7128f Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 27 Oct 2015 14:23:35 -0500 Subject: [PATCH 279/450] merge: add recursive test with three merge bases --- tests/merge/trees/recursive.c | 46 ++++++++++++++++++ .../06/db153c36829fc656e05cdf5a3bf7183f3c10aa | Bin 0 -> 272 bytes .../0e/8126647ec607f0a14122cec4b15315d790c8ff | Bin 0 -> 208 bytes .../1c/1bdb80c04233d1a9b9755913ee233987be6175 | Bin 0 -> 208 bytes .../1e/8dff96faaaa24f84943d2d9601dde61cb0398a | Bin 0 -> 268 bytes .../43/6ea75c99f527e4b42fddb46abedf7726eb719d | Bin 0 -> 173 bytes .../5a/ba269b3be41fc8db38068d3948c8af543fe609 | Bin 0 -> 208 bytes .../73/b20c8e09fa2726d69ff66969186014165da3c3 | Bin 0 -> 208 bytes .../7e/3056f6765b3044ab09701077dbe1eb5b0e9ad0 | Bin 0 -> 208 bytes .../8a/bda8de114a93f2d3c5a975ee2960f31e24be58 | Bin 0 -> 169 bytes .../96/23368f0fc562d6d840372ae17dc4cc32d51a80 | Bin 0 -> 172 bytes .../9a/e63b4a8ce0f181b2d1d098971733a103226917 | Bin 0 -> 240 bytes .../9b/258ad4c39f40c24f66bf1faf48eb6202d59c85 | Bin 0 -> 240 bytes .../9e/12bce04446d097ae1782967a5888c2e2a0d35b | Bin 0 -> 268 bytes .../a2/8c21c90aa36580641b345011869d1a899a6783 | Bin 0 -> 239 bytes .../ad/98bfa4679fb00b89207a0a11b8bbf91a3e4de9 | Bin 0 -> 208 bytes .../bd/97980c22d122509cdd915fd9788d56c8d3ae20 | Bin 0 -> 163 bytes .../ca/224bba0a8a24f1768804fe5f565b1014af7ef2 | Bin 0 -> 170 bytes .../d2/682aaf9594080ce877b5eeee110850fd6e3480 | Bin 0 -> 272 bytes .../d8/dd349b78f19a4ebe3357bacb8138f00bf5ed41 | Bin 0 -> 277 bytes .../d8/e05a90b3c2240d71a20c2502c937d9b7d22777 | Bin 0 -> 272 bytes .../da/b7b53383a1fec46632e60a1d847ce4f9ae14f2 | Bin 0 -> 208 bytes .../e2/d185fa827d58134cea20b9e1df893833c6560e | Bin 0 -> 208 bytes .../e5/0fbbd701458757bdfe9815f58ed717c588d1b5 | Bin 0 -> 268 bytes .../f1/72517a8cf39e009ffff541ee52429b89e418f3 | Bin 0 -> 268 bytes .../.gitted/refs/heads/branchE-1 | Bin 0 -> 41 bytes .../.gitted/refs/heads/branchE-2 | Bin 0 -> 41 bytes .../.gitted/refs/heads/branchE-3 | Bin 0 -> 41 bytes 28 files changed, 46 insertions(+) create mode 100644 tests/resources/merge-recursive/.gitted/objects/06/db153c36829fc656e05cdf5a3bf7183f3c10aa create mode 100644 tests/resources/merge-recursive/.gitted/objects/0e/8126647ec607f0a14122cec4b15315d790c8ff create mode 100644 tests/resources/merge-recursive/.gitted/objects/1c/1bdb80c04233d1a9b9755913ee233987be6175 create mode 100644 tests/resources/merge-recursive/.gitted/objects/1e/8dff96faaaa24f84943d2d9601dde61cb0398a create mode 100644 tests/resources/merge-recursive/.gitted/objects/43/6ea75c99f527e4b42fddb46abedf7726eb719d create mode 100644 tests/resources/merge-recursive/.gitted/objects/5a/ba269b3be41fc8db38068d3948c8af543fe609 create mode 100644 tests/resources/merge-recursive/.gitted/objects/73/b20c8e09fa2726d69ff66969186014165da3c3 create mode 100644 tests/resources/merge-recursive/.gitted/objects/7e/3056f6765b3044ab09701077dbe1eb5b0e9ad0 create mode 100644 tests/resources/merge-recursive/.gitted/objects/8a/bda8de114a93f2d3c5a975ee2960f31e24be58 create mode 100644 tests/resources/merge-recursive/.gitted/objects/96/23368f0fc562d6d840372ae17dc4cc32d51a80 create mode 100644 tests/resources/merge-recursive/.gitted/objects/9a/e63b4a8ce0f181b2d1d098971733a103226917 create mode 100644 tests/resources/merge-recursive/.gitted/objects/9b/258ad4c39f40c24f66bf1faf48eb6202d59c85 create mode 100644 tests/resources/merge-recursive/.gitted/objects/9e/12bce04446d097ae1782967a5888c2e2a0d35b create mode 100644 tests/resources/merge-recursive/.gitted/objects/a2/8c21c90aa36580641b345011869d1a899a6783 create mode 100644 tests/resources/merge-recursive/.gitted/objects/ad/98bfa4679fb00b89207a0a11b8bbf91a3e4de9 create mode 100644 tests/resources/merge-recursive/.gitted/objects/bd/97980c22d122509cdd915fd9788d56c8d3ae20 create mode 100644 tests/resources/merge-recursive/.gitted/objects/ca/224bba0a8a24f1768804fe5f565b1014af7ef2 create mode 100644 tests/resources/merge-recursive/.gitted/objects/d2/682aaf9594080ce877b5eeee110850fd6e3480 create mode 100644 tests/resources/merge-recursive/.gitted/objects/d8/dd349b78f19a4ebe3357bacb8138f00bf5ed41 create mode 100644 tests/resources/merge-recursive/.gitted/objects/d8/e05a90b3c2240d71a20c2502c937d9b7d22777 create mode 100644 tests/resources/merge-recursive/.gitted/objects/da/b7b53383a1fec46632e60a1d847ce4f9ae14f2 create mode 100644 tests/resources/merge-recursive/.gitted/objects/e2/d185fa827d58134cea20b9e1df893833c6560e create mode 100644 tests/resources/merge-recursive/.gitted/objects/e5/0fbbd701458757bdfe9815f58ed717c588d1b5 create mode 100644 tests/resources/merge-recursive/.gitted/objects/f1/72517a8cf39e009ffff541ee52429b89e418f3 create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchE-1 create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchE-2 create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchE-3 diff --git a/tests/merge/trees/recursive.c b/tests/merge/trees/recursive.c index bff5d7e39..46effa5c2 100644 --- a/tests/merge/trees/recursive.c +++ b/tests/merge/trees/recursive.c @@ -200,3 +200,49 @@ void test_merge_trees_recursive__three_levels_of_multiple_bases_norecursive(void git_index_free(index); } +void test_merge_trees_recursive__three_base_commits(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4f7269b07c76d02755d75ccaf05c0b4c36cdc6c", 0, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" }, + }; + + cl_git_pass(merge_commits_from_branches(&index, repo, "branchE-1", "branchE-2", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 6)); + + git_index_free(index); +} + +void test_merge_trees_recursive__three_base_commits_norecursive(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "9e12bce04446d097ae1782967a5888c2e2a0d35b", 1, "gravy.txt" }, + { 0100644, "d8dd349b78f19a4ebe3357bacb8138f00bf5ed41", 2, "gravy.txt" }, + { 0100644, "e50fbbd701458757bdfe9815f58ed717c588d1b5", 3, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" }, + }; + + opts.flags |= GIT_MERGE_NO_RECURSIVE; + + cl_git_pass(merge_commits_from_branches(&index, repo, "branchE-1", "branchE-2", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 8)); + + git_index_free(index); +} + diff --git a/tests/resources/merge-recursive/.gitted/objects/06/db153c36829fc656e05cdf5a3bf7183f3c10aa b/tests/resources/merge-recursive/.gitted/objects/06/db153c36829fc656e05cdf5a3bf7183f3c10aa new file mode 100644 index 0000000000000000000000000000000000000000..85887e0f532f756c021a17fed77a7e867fe9d146 GIT binary patch literal 272 zcmV+r0q_2J0VPnoZo@zf>wI6q4@iOnor+GGiY|R6DUeaNW&42h$vDX|^7qv_n?O<| zr6x&(m)GZ~_uu2+6{oMu_aTILHgFt9!z@eg6w;#=D#}Pq84Yv4->;-2qM_X|${iBj zD-bj8vXn53?JzGJU?W3mVkd{fUF2*EQuI==aiedkEb|U)wDEztKKxvcpXZPBwRTv^ zxa?4)dPzyvO*R*4SruI@+!|M|dXgP*vO@0b0ik;PP-GcK5H#|QA6Clc=DiFqsJD2+ zRw0XdsR5G|u6{X~kk){vFm?6JXm7ApVALeu>nxJBUsCNlE$U%1p(RsyUSmq2t^05AV|0V^p=O;s>5GG{O}FfcPQQAjK*bF-)ZbitUz~A8-NutKB~y;hfT}7>P0RtSS-v4HxGwv-z^wgG{!ialdgVd+ KEd>DYnqMw)Z)Roy literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/1c/1bdb80c04233d1a9b9755913ee233987be6175 b/tests/resources/merge-recursive/.gitted/objects/1c/1bdb80c04233d1a9b9755913ee233987be6175 new file mode 100644 index 0000000000000000000000000000000000000000..a2146496decf282eba468c692c6f7e10bf64d1c0 GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjKTiU*U#^-LkMR!aGYktDm4!Z8PN+BWhADIj(I$tHqsf<(C--K0g3Jn zh#7aOC9GmctaS%$W+*M} zHA+gd?XtB<&#D+=;oi7$&6DgIS1aVc9uS(h4@K%UgP@Ub{IF6kH}7R|LA%8h_6k|d zOHEj$aP!N-gtP^8g=uJKMtg^C0;4YRNmr4qV@<8=Qq{v`K~JXcyv3A2!vG3n%#1P7 S&7TDG?e-h5cL@L5MT~_s`-Tqy literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/43/6ea75c99f527e4b42fddb46abedf7726eb719d b/tests/resources/merge-recursive/.gitted/objects/43/6ea75c99f527e4b42fddb46abedf7726eb719d new file mode 100644 index 0000000000000000000000000000000000000000..e8825d867a2b797bf5a0039671e9419b80c200cf GIT binary patch literal 173 zcmV;e08;;W0i}*hY6CG4M6=c@bODEMsr3V6yqRTozCgRx!^~nvW-OEIM+v!sp%zuI zpeSkEwsz|Ree^@sf@X9vQc{VKW?&?APE_cQ!DFbYfaG~L4?cA7mR%NyQh?dVkRcM@ zJq19_u~g(-3dfYa;a6YUVW;}dN42-5ZO^vbvkv;}uKHK}r%!3y#UfG2CHR!=2?)Rp bI?i@|bH;qNy3VU`U&n=^>Ez50Hyu$ygu7Sn literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/5a/ba269b3be41fc8db38068d3948c8af543fe609 b/tests/resources/merge-recursive/.gitted/objects/5a/ba269b3be41fc8db38068d3948c8af543fe609 new file mode 100644 index 0000000000000000000000000000000000000000..85bc8f569e855cbf01f67de58f490d51f7e8b5f8 GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjKP}<4`%GqoWfxRYer0h< zY7xZ7_43CZ>Tj;SFV484ZsWk8T$lY^VAlR8|EF&&z4D;^ KmI45u^05AV|0V^p=O;s>5GG{O}FfcPQQAjKTj;SFV484ZsWk8T$lY^VAlR8|EF&&z4D;^ KmI443F<*?<6J%rn literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/7e/3056f6765b3044ab09701077dbe1eb5b0e9ad0 b/tests/resources/merge-recursive/.gitted/objects/7e/3056f6765b3044ab09701077dbe1eb5b0e9ad0 new file mode 100644 index 0000000000000000000000000000000000000000..c4b8355186272c1eb916727bae0a16c6ad75a545 GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjKgOR@qzp6TSus({L12z z)FOzD>*bF-)ZbitUz~A8-NutKB~y;hfT}7>P0RtSS-v4HxGwv-z^wgG{!ialdgVd+ KEd>AsJzr5;MP&d0 literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/8a/bda8de114a93f2d3c5a975ee2960f31e24be58 b/tests/resources/merge-recursive/.gitted/objects/8a/bda8de114a93f2d3c5a975ee2960f31e24be58 new file mode 100644 index 0000000000000000000000000000000000000000..a03624d8162248d98931d06f6a3f8e82907f58c1 GIT binary patch literal 169 zcmV;a09OBa0i{km3IZ_{w9YAVftCF80wOkB+IfNO%L|(#8(AY>-zeTdm|{N6FxYA> zohxe&eJA3ik_4M0D4#4yZIGf8OCXst1PxlFvrZ7Z$DOv$J^CesrQjjUQAJ^$%U-CY zaPi)`B_Je|S$y`j?R-wJxF>#FTYa>RpJ=!rcA;J=1) zb=ykk%D|yFBA!BY(P-^;keLvy)rzbLGI?-Fkmo=WvSRnpXz$#ii`g5RozF5!kHT1+ zolr?(bk5p1qfd}oc=ok6zNA-ZiQm?`J?hR+H0Z}&=v(~K5$ks562SP`NCSKlAOst9 aobB}I47=7;a>3BcxG=O#ioF5MBU3vdf>!DP literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/9a/e63b4a8ce0f181b2d1d098971733a103226917 b/tests/resources/merge-recursive/.gitted/objects/9a/e63b4a8ce0f181b2d1d098971733a103226917 new file mode 100644 index 0000000000000000000000000000000000000000..0cbd00d539ac5fd660de5c73b1558663e690bc6f GIT binary patch literal 240 zcmVVPQ*YAg{gCj>9$&Adwya|2+?dw#|1oN4@;2*WrMgr0Tws_-Fx5r z5=9_JG4{E}-YbvAk3Q$=HMg>KzTx%E9=*e7$S6zMHLNytMp%EDpWsqhj znEQ^IEtkd?)d_xlDHLPj4!z|f9nB7SV z^oUNWdgejjw)3)vlfCO|;px0gmt_ujzSGb2;9tYF-L_@A513MB$;dGrAps0K-Jh}f qKhF%0zCL@XtIqB0mvHz_rz0JL&i2>iA-v4nay3s!96kWmDSDv#$acp7 literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/9b/258ad4c39f40c24f66bf1faf48eb6202d59c85 b/tests/resources/merge-recursive/.gitted/objects/9b/258ad4c39f40c24f66bf1faf48eb6202d59c85 new file mode 100644 index 0000000000000000000000000000000000000000..305e1f3e92c78d686d7e067ced299cfdb8a3b3ee GIT binary patch literal 240 zcmV_5Q>b2+>?g#|wDZUhaxyQ3&Go2|q`D5}p0c zc%*Ud^L*Z1On9@cW=@o;G>LiQRsaey!XzNx7b>N(mtawihgVr`*&InxAvN!#aA>HZ zCgCZWA!lg88^&UZF8-oCF+oLCkC=d~D*M!SOr$AS(jbO#@pHxyGBs#5Wz?(`AxtRd zC#|v8fSSAz4D!C6`s$AQA*;IQQ=i}Z;_hvyU(=&~3%}cK?el~4F(%@KF}njqfMKWm qGdBBY%TiU*U#^-QwV1^aGYktDm4!Z8PN+BWhADIj(I#DH_{o=(C--K0g3Jn zh#7aOC9GlxtaS%$W+*M}FLD0xIepo4&oA)xfpxxpLdxb3K zr6w#=xcTK^LfQhl!ZfrqqrJm6fl-(EsH;fUv8L8_sp?^}peIvz-eO9iVE~0OX2zK4 S=1+q8cKeOjdk+8Yk&Keol8Ar+ literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/a2/8c21c90aa36580641b345011869d1a899a6783 b/tests/resources/merge-recursive/.gitted/objects/a2/8c21c90aa36580641b345011869d1a899a6783 new file mode 100644 index 0000000000000000000000000000000000000000..91ffb4b88e6e674b9ac108f9833d90c2b6fa0d9c GIT binary patch literal 239 zcmV_5Q>b2+>?g#|wDZUhaxyQ3&Go34d3gL}!09 z9%)?rJfAlg6W(mAnGS9jD8S=BwC`ux@xcW*oWnjY<2_}y-6pC6o$F%c(3?+y?FhMn%u p*zBJ%!;`Hq=6=%Lcx~nJ?s9T%>(QN;t-G?yQcuSN4xbL0i(#MRb<_X= literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/ad/98bfa4679fb00b89207a0a11b8bbf91a3e4de9 b/tests/resources/merge-recursive/.gitted/objects/ad/98bfa4679fb00b89207a0a11b8bbf91a3e4de9 new file mode 100644 index 0000000000000000000000000000000000000000..457f9da1f1bc3fc5daa3c7f492f9fb2685f6700c GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjKTj;SFV484ZsWk8T$lY^VAlR8|EF&&z4D;^ KmI44|zFlT3oMPwz literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/bd/97980c22d122509cdd915fd9788d56c8d3ae20 b/tests/resources/merge-recursive/.gitted/objects/bd/97980c22d122509cdd915fd9788d56c8d3ae20 new file mode 100644 index 0000000000000000000000000000000000000000..71295e07145cb9dbc13de764cd77335bdc1c0983 GIT binary patch literal 163 zcmV;U09^lg0i{pL4FVw)^-L8sFy_O+2NGjU%yeJ_ET4=A27?&e7ncsay(M`|tfgE# zm&)#XBjRQb5~3w=*-Ilaq01T3p%WHCD1kcpInM0n8*QDNXhOD{oUvIXVIye7Y(OR% zgE5G~MVnM+{?XUk_>!K!C4O0Jxz~*!XsB;=qL1-o`&i473#GJm8l2=4C;&FpF}Bm6 RG3@+3J^GhPuov*HPsMvROWptg literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/ca/224bba0a8a24f1768804fe5f565b1014af7ef2 b/tests/resources/merge-recursive/.gitted/objects/ca/224bba0a8a24f1768804fe5f565b1014af7ef2 new file mode 100644 index 0000000000000000000000000000000000000000..0dd861f2c43e7f10a1ccbaf4ad794e9b7bac5091 GIT binary patch literal 170 zcmV;b09F5Z0i}*j3c@fHgk9$pxj^N;{5Bxsrb~BT;3avbU9^!H@%l#b2Er_6zJXy< z*|xPwWzC^gX6adjb+hx%Mh(VLgx~?e`y3fL+6WvdgSb=Wy~#lAbTl+3h8ahUDr1H) z29I8ALeNUNfL_qEEv3pSzo=$;Tgvt*yF78HA9vwz@k_^4wzEWI>=>-JP7YuIM5q01 Y<3DG_wd8eP6ScKp=$Zx;Z>2v`!Lnvk6951J literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/d2/682aaf9594080ce877b5eeee110850fd6e3480 b/tests/resources/merge-recursive/.gitted/objects/d2/682aaf9594080ce877b5eeee110850fd6e3480 new file mode 100644 index 0000000000000000000000000000000000000000..c79a3bb0f929cebff178786097b6685093bcaaa6 GIT binary patch literal 272 zcmV+r0q_2J0VPnoZo@zf>wI6q?nzLfQ_(3?(50^=1v1LE93OB#87DbL{=PbA6G)1r z)Ff%}`u6g4_}%|qaQr%dKZkH&1IO!Nm}JhKLVC19MHz`Hqhap1+l6#MG_)&5xkI9R z1!Bfs<`O2c4W@YoY+xvj?Bum@7de}P6ulIz+~8X(^R&SdZFpd=4?pMq=jr2gsT~$F zE*sRSUQ&`}l}$xjRz(*Jx5kyLo@6^5t&sbAK&ak66q&CB2paju4=d$z^IirQ)LT4Z zt&qjM)D@!?u6{X~kk){vFm?6JXz#E^VALeu>Lil2pHuBR&gx+@q9s#zUSdk1t^5lYGkOc6D6x7#hG1H7SGHHs|) z-HYHO?lLDgiES~>t6&2|Y2AlPuv(6uVj)v<6NEgha;-54_C6Ynq=+=5Y+RHA5qHbmbnah zpxlxhR)i>)p?Zvxxa37=LRt#y#MG8Ey}iR0fl(cJr;|w3ZVrWYoHduph=xpa^5R1P bwJpeXJ~FyMxBMhn-Y&n%cpLW*MV5+%UnhpR literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/d8/e05a90b3c2240d71a20c2502c937d9b7d22777 b/tests/resources/merge-recursive/.gitted/objects/d8/e05a90b3c2240d71a20c2502c937d9b7d22777 new file mode 100644 index 0000000000000000000000000000000000000000..b157ba17c290d5451ce52c7be2dc01fa691a2806 GIT binary patch literal 272 zcmV+r0q_2J0VPnoZo@zf>wI6q>`76eQ_(3?(50^=1v1LE93OB#87DbL{=PbA6G)1r z)Ff%}`u6g4_}%|qaQr%dZ$db*f#Y>BOfu(AAw625qKw3p(J=SB-9kDb8rl`3+#%7u z0x{z*a|x5!7Sp@}HZYV%cJf-di=0hCie3s!$HasxbhoAHQ^Yn4L)D8<7 zmn~{kFDc2g%BCVMtD=jATjR=APqJqmt&sbAK&ak66q&CB2paju4=d$z^IirQ)LT4Z zt&qjM)D@!?u6{X~kk){vFm?6JXz#E^VALeu=_Hc1pHuBR&gx+@q9s#zUSdk1t^05AV|0V^p=O;s>5GG{O}FfcPQQAjK$0B<%-a9t|MYF8S00q# KQUCxBQe1Jq@?@z1 literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/e2/d185fa827d58134cea20b9e1df893833c6560e b/tests/resources/merge-recursive/.gitted/objects/e2/d185fa827d58134cea20b9e1df893833c6560e new file mode 100644 index 0000000000000000000000000000000000000000..fc80c08515319eca869719cabe8f50ddee61f8f9 GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjK*bF-)ZbitUz~A8-NutKB~y;hfT}7>P0RtSS-v4HxGwv-z^wgG{!ialdgVd+ KEd>C}tX{rBZ(}Y1 literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/e5/0fbbd701458757bdfe9815f58ed717c588d1b5 b/tests/resources/merge-recursive/.gitted/objects/e5/0fbbd701458757bdfe9815f58ed717c588d1b5 new file mode 100644 index 0000000000000000000000000000000000000000..96467c1066d719f545dd1762c1fdd254e3262d27 GIT binary patch literal 268 zcmV+n0rUQN0VPl|Zp1JQ>+Dys``w{Hr=nA*qDz58uR}*AQ4#}=6Xcs}o>GoqnCFvTiU*U#^-=Mc_p;5f~ORcam-GNKnM%1BHZ9rJiRZlp7!q2Dpe0}|aE z5Hs#lOIXDYSnCeh%urg`$*J%VIh%qMqZI7i>{}{zJz$GAKQPyaze?Gn-CKu^jLQKn zYLt{@+huE!o>ei#!o6|hnkU%_S1aVc9uS(h4@K%UgP@Ub{IF6kH}7R|LA%8h_6k|d zOHEj$aP!N-gtP^8g=uJKMtg^C0;4YRQCE?yV@<8=Qq{v`K~JXcyv3A2!vG3n%#1P7 S&7TDG?e-h5cMAX7UyPDbtcA4z literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchE-1 b/tests/resources/merge-recursive/.gitted/refs/heads/branchE-1 new file mode 100644 index 0000000000000000000000000000000000000000..b8d011e2d58d7a0b6fe2f91ba3e44d03ce95df09 GIT binary patch literal 41 ucmV~$!4Uu;2m`Rc(?Ab|b&5y#AHn1ih#4B22zlLZ3vDWIj++NnE1VDTEDId~ literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchE-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchE-2 new file mode 100644 index 0000000000000000000000000000000000000000..5e1e1acd9a317fed388ba990f28596ae35a2b4e4 GIT binary patch literal 41 ucmV~$!4Uu;2m`Rc+lWYyaln@SM= Date: Tue, 27 Oct 2015 14:24:51 -0500 Subject: [PATCH 280/450] merge: compute octopus merge bases --- src/merge.c | 191 +++++++++++++++++++++++++++------------------------- 1 file changed, 101 insertions(+), 90 deletions(-) diff --git a/src/merge.c b/src/merge.c index 05ca3d0ec..0a837b5f4 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1923,83 +1923,41 @@ done: return error; } -#define INSERT_VIRTUAL_BASE_PARENT(commit, parent_id) \ - do { \ - id = git_array_alloc(commit->parent_ids); \ - GITERR_CHECK_ALLOC(id); \ - git_oid_cpy(id, parent_id); \ +static int merge_trees_with_heads( + git_index **out, + git_repository *repo, + const git_tree *ours, + const git_tree *theirs, + const git_oid heads[], + size_t heads_len, + const git_merge_options *opts); + +#define INSERT_ID(_ar, _id) do { \ + git_oid *_alloced = git_array_alloc(_ar); \ + GITERR_CHECK_ALLOC(_alloced); \ + git_oid_cpy(_alloced, _id); \ } while(0) -static int build_virtual_base( - git_commit **out, - git_repository *repo, - const git_commit *one, - bool one_is_virtual, - const git_oid *two_id) -{ - git_commit *two = NULL, *result; - git_index *index = NULL; - git_oid tree_id, *id; - int error; - - /* TODO: propagate merge options */ - if ((error = git_commit_lookup(&two, repo, two_id)) < 0 || - (error = git_merge_commits(&index, repo, one, two, NULL)) < 0) - goto done; - - if ((error = git_index_write_tree_to(&tree_id, index, repo)) < 0) - goto done; - - if ((result = git__calloc(1, sizeof(git_commit))) == NULL) - goto done; - - result->object.repo = repo; - - /* if the initial commit we were given is virtual, we are octopus - * merging - that virtual base's parents should actually be the - * parents that we use for our new virtual commit. otherwise, it - * is an actual parent. - */ - if (one_is_virtual) { - size_t i, cnt = git_commit_parentcount(one); - - for (i = 0; i < cnt; i++) - INSERT_VIRTUAL_BASE_PARENT(result, git_commit_parent_id(one, i)); - } else { - INSERT_VIRTUAL_BASE_PARENT(result, git_commit_id(one)); - } - - INSERT_VIRTUAL_BASE_PARENT(result, two_id); - - git_oid_cpy(&result->tree_id, &tree_id); - - *out = result; - -done: - git_index_free(index); - git_commit_free(two); - return error; -} - -#undef INSERT_VIRTUAL_BASE_PARENT - -static int compute_base_tree( +static int compute_base( git_tree **out, git_repository *repo, - const git_commit *our_commit, - const git_commit *their_commit, - bool recursive) + const git_oid heads[], + size_t heads_len, + const git_merge_options *opts) { - git_commit_list *base_list; - git_revwalk *walk; - git_commit *base = NULL; - bool base_virtual = false; + git_commit_list *base_list = NULL; + git_revwalk *walk = NULL; + git_commit *base_commit = NULL, *next_commit = NULL; + git_tree *base_tree = NULL, *next_tree = NULL; + git_array_t(git_oid) base_ids = GIT_ARRAY_INIT; + git_index *index = NULL; + bool recursive = !opts || (opts->flags & GIT_MERGE_NO_RECURSIVE) == 0; int error = 0; *out = NULL; - if ((error = merge_bases(&base_list, &walk, repo, - git_commit_id(our_commit), git_commit_id(their_commit))) < 0) + if ((error = merge_bases_many(&base_list, &walk, repo, + heads_len, heads)) < 0) return error; if (error == GIT_ENOTFOUND) { @@ -2008,29 +1966,88 @@ static int compute_base_tree( goto done; } - if ((error = git_commit_lookup(&base, repo, &base_list->item->oid)) < 0) + if ((error = git_commit_lookup(&base_commit, repo, + &base_list->item->oid)) < 0 || + (error = git_commit_tree(&base_tree, base_commit)) < 0) goto done; + INSERT_ID(base_ids, git_commit_id(base_commit)); + while (recursive && base_list->next) { - git_commit *new_base; + git_tree *new_tree; + git_oid new_tree_id; base_list = base_list->next; - if ((error = build_virtual_base(&new_base, repo, base, base_virtual, - &base_list->item->oid)) < 0) + if ((error = git_commit_lookup(&next_commit, repo, + &base_list->item->oid)) < 0 || + (error = git_commit_tree(&next_tree, next_commit)) < 0) goto done; - git_commit_free(base); - base = new_base; - base_virtual = true; + INSERT_ID(base_ids, git_commit_id(next_commit)); + + if ((error = merge_trees_with_heads(&index, repo, base_tree, + next_tree, base_ids.ptr, base_ids.size, opts)) < 0) + goto done; + + /* TODO: conflicts!! */ + + if ((error = git_index_write_tree_to(&new_tree_id, index, repo)) < 0 || + (error = git_tree_lookup(&new_tree, repo, &new_tree_id)) < 0) + goto done; + + git_index_free(index); + index = NULL; + + git_tree_free(next_tree); + next_tree = NULL; + + git_commit_free(next_commit); + next_commit = NULL; + + git_tree_free(base_tree); + base_tree = new_tree; } - error = git_commit_tree(out, base); + *out = base_tree; + base_tree = NULL; done: - git_commit_free(base); + git_index_free(index); + git_tree_free(next_tree); + git_tree_free(base_tree); + git_commit_free(next_commit); + git_commit_free(base_commit); git_commit_list_free(&base_list); git_revwalk_free(walk); + git_array_clear(base_ids); + + return error; +} + +static int merge_trees_with_heads( + git_index **out, + git_repository *repo, + const git_tree *ours, + const git_tree *theirs, + const git_oid heads[], + size_t heads_len, + const git_merge_options *opts) +{ + git_tree *ancestor = NULL; + int error = 0; + + if ((error = compute_base(&ancestor, repo, heads, heads_len, opts)) < 0) { + if (error == GIT_ENOTFOUND) + giterr_clear(); + else + goto done; + } + + error = git_merge_trees(out, repo, ancestor, ours, theirs, opts); + +done: + git_tree_free(ancestor); return error; } @@ -2042,30 +2059,24 @@ int git_merge_commits( const git_commit *their_commit, const git_merge_options *opts) { - git_tree *our_tree = NULL, *their_tree = NULL, *ancestor_tree = NULL; - bool recursive; + git_tree *our_tree = NULL, *their_tree = NULL; + git_oid heads[2]; int error = 0; - recursive = !opts || (opts->flags & GIT_MERGE_NO_RECURSIVE) == 0; + *out = NULL; - if ((error = compute_base_tree(&ancestor_tree, repo, - our_commit, their_commit, recursive)) < 0) { - - if (error == GIT_ENOTFOUND) - giterr_clear(); - else - goto done; - } + git_oid_cpy(&heads[0], git_commit_id(our_commit)); + git_oid_cpy(&heads[1], git_commit_id(their_commit)); if ((error = git_commit_tree(&our_tree, our_commit)) < 0 || (error = git_commit_tree(&their_tree, their_commit)) < 0 || - (error = git_merge_trees(out, repo, ancestor_tree, our_tree, their_tree, opts)) < 0) + (error = merge_trees_with_heads(out, repo, our_tree, their_tree, + heads, 2, opts)) < 0) goto done; done: git_tree_free(our_tree); git_tree_free(their_tree); - git_tree_free(ancestor_tree); return error; } From b1eef912cffb9e3ce9792b6aee816c1a45c85fb0 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 27 Oct 2015 18:00:30 -0500 Subject: [PATCH 281/450] merge: add recursive test with conflicting contents --- tests/merge/trees/recursive.c | 23 ++++++++++++++++++ .../21/950d5e4e4d1a871b4dfcf72ecb6b9c162c434e | Bin 0 -> 670 bytes .../38/55170cef875708da06ab9ad7fc6a73b531cda1 | Bin 0 -> 664 bytes .../5b/8e1e56cb99e8b99ac22eec8aebf6422ecd08c0 | Bin 0 -> 208 bytes .../78/3d6539dde96b8873c5b5da3e79cc14cd64830b | Bin 0 -> 176 bytes .../ca/7d316d6d9af99d2481e980d68b77e572d80fe7 | Bin 0 -> 207 bytes .../ef/1783444b61a8671beea4ce1f4d0202677dfbfb | Bin 0 -> 174 bytes .../.gitted/refs/heads/branchF-1 | Bin 0 -> 41 bytes .../.gitted/refs/heads/branchF-2 | Bin 0 -> 41 bytes 9 files changed, 23 insertions(+) create mode 100644 tests/resources/merge-recursive/.gitted/objects/21/950d5e4e4d1a871b4dfcf72ecb6b9c162c434e create mode 100644 tests/resources/merge-recursive/.gitted/objects/38/55170cef875708da06ab9ad7fc6a73b531cda1 create mode 100644 tests/resources/merge-recursive/.gitted/objects/5b/8e1e56cb99e8b99ac22eec8aebf6422ecd08c0 create mode 100644 tests/resources/merge-recursive/.gitted/objects/78/3d6539dde96b8873c5b5da3e79cc14cd64830b create mode 100644 tests/resources/merge-recursive/.gitted/objects/ca/7d316d6d9af99d2481e980d68b77e572d80fe7 create mode 100644 tests/resources/merge-recursive/.gitted/objects/ef/1783444b61a8671beea4ce1f4d0202677dfbfb create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchF-1 create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchF-2 diff --git a/tests/merge/trees/recursive.c b/tests/merge/trees/recursive.c index 46effa5c2..abca01727 100644 --- a/tests/merge/trees/recursive.c +++ b/tests/merge/trees/recursive.c @@ -246,3 +246,26 @@ void test_merge_trees_recursive__three_base_commits_norecursive(void) git_index_free(index); } +void test_merge_trees_recursive__conflict(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "fa567f568ed72157c0c617438d077695b99d9aac", 1, "veal.txt" }, + { 0100644, "21950d5e4e4d1a871b4dfcf72ecb6b9c162c434e", 2, "veal.txt" }, + { 0100644, "3855170cef875708da06ab9ad7fc6a73b531cda1", 3, "veal.txt" }, + }; + + cl_git_pass(merge_commits_from_branches(&index, repo, "branchF-1", "branchF-2", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 8)); + + git_index_free(index); +} + diff --git a/tests/resources/merge-recursive/.gitted/objects/21/950d5e4e4d1a871b4dfcf72ecb6b9c162c434e b/tests/resources/merge-recursive/.gitted/objects/21/950d5e4e4d1a871b4dfcf72ecb6b9c162c434e new file mode 100644 index 0000000000000000000000000000000000000000..a87732611a38ea041d580d75e7e8b5a865babf96 GIT binary patch literal 670 zcmV;P0%84l0Zo*_Zrd;rM0@5}><738f&#txBtjIaa;d87*LRk( z+ukgRT+X~1y7IB2hlfv}KYTwOztZdZ?XoxK@^+=C=j)k{bU9z?`sL+xqHk} z)CFRF-qGT6zr9J@Xd~X$EV{z84b9_Ib1Zev7cgCn4b@nVF{Asn5r8-qkZtApW5FokXrQ$9un4_r zr2sJMt3ZUNmHkja=~nJKp^9yW=Y=&9$;rZP?wuGMl;;F7y4N-hNgSE#J|domCY!|b zdeEHG_DSMAA!VvoY8d8Nc1E&Gig6pSlgfI$DS#Ez6p?8iLv~?yp(AiFyJo!cy0^Dt zG)A0SdT>h}B*L5#J2q_GZ(NgI@B*1@YI=hD+|yHQ^r2vcYYH)=YpLZrqZzU=L&u#{ zLGxpF7!r6Xy-+IkzyTV~qQTU2YCQ5&UPv46_w*POa;tlbKF}Ix8tq#8WT}b2!8y5! zp?I`8ig95_txJL=?V#Ro)c`XzCCE-$qH#b2E~`6+lS~pTjvYJ>A^dv2aa!Qk z*z@>aZslV|4-X$dz59MTexaB1>t%1u<@HKWU$196(&c=m>*wdwiN3uapRX@;ex$eK z_4K@>+$Uy=!Nnj{&zVBZlw%|rt@k_Ju#qXo(9WJ>OO?0vKod_BCz|3M8g6!ELqmCq zS|HZv9W5^R+ezA181b%V(F)HtP;bWrer0l*s^~mPX4^Qwmaaa|Ne(1~8&P0lU0VS{ zAX*hN2buuYg&eitlJqX<=$~wX`Twzss!TIJhK|#LvK>Ge?U+OVPyx`!qb!YWTkgBW zR+wB1imMS!@(m>?3>Z4cQs;aTri-zm8s!)>x?h_D0H*-5tvr7yG75AwK-zjx1m3h# zfH2CdhzLw8`=JP>t=x4&726EX3#%iNlLgz{J26<4=L9g?Ynz57j!bnQVNXMoP2zby zs84CTk~mNBl&X~)2050Uk?fMnxQ*9Ir5-0mV3lc#@U)I0yD+=ZAs~Q(t{Ep@dwVNJ zZN%1+gPZG6BFq`KqhZtirfaeb4&b@ErW=&!o}OBxA4Nv6E@4E_TCOvaAqz8b+;u9_ z{Fohv1YSxmRF!(*0E}i)W9m6I9{#Cba2xFR^cWL-t9y$)&>CkN?OO6=sfoX#b8-^{ z@n~}tK^~{ZW0Y)t_Rf#PGOiPQZoKSt_YbPS`g{_yh4edM@68|Lp^zqDFGSh^ literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/5b/8e1e56cb99e8b99ac22eec8aebf6422ecd08c0 b/tests/resources/merge-recursive/.gitted/objects/5b/8e1e56cb99e8b99ac22eec8aebf6422ecd08c0 new file mode 100644 index 0000000000000000000000000000000000000000..c3e6d31ca217790364fa5f70e6111377525bf55b GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjK-wLp;;n{f K7XkpfJzPUFfMJ;c literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/78/3d6539dde96b8873c5b5da3e79cc14cd64830b b/tests/resources/merge-recursive/.gitted/objects/78/3d6539dde96b8873c5b5da3e79cc14cd64830b new file mode 100644 index 0000000000000000000000000000000000000000..e2f34d6bb2585aa9fc7a1df8d940733bedae26d4 GIT binary patch literal 176 zcmV;h08jsT0i};?3c@fDg#DdU>;)=$G%pYlL{H!aHrb6W*hXr^>s!Se_;2PL7-n*v zrm=x`a?=)O$mq!^M~)(v7)kd*aSW1NNZzycnu09s#T^%(8=&dgqH={XC6dNibjBc3 z3R?Lbi=l)zkicgf>H;Tv;X-g3>h!2H?0BUgb>MICbK6{}J*d97F*qX?bka&GR=SRD e{AZ0g*16oq+{XC|OzZvE*akRvTD$=tv{mv^x>e2q literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/ca/7d316d6d9af99d2481e980d68b77e572d80fe7 b/tests/resources/merge-recursive/.gitted/objects/ca/7d316d6d9af99d2481e980d68b77e572d80fe7 new file mode 100644 index 0000000000000000000000000000000000000000..0733fa232bf68c48a5dc822e57159816e07198d7 GIT binary patch literal 207 zcmV;=05Jb}0V^p=O;s>5GG{O}FfcPQQAjKl?)z_%qA{hM7=u zo+~ITH+5x(e)Npo+b|9T`vw%0Vh)~oOx83WqVz$EJFUD_a7v@cgky?YvTs)#!vHlq1CcTW~iCg`=cb(PU!0T^BEF=GZ8V*u6gMN(&VL literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchF-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchF-2 new file mode 100644 index 0000000000000000000000000000000000000000..abe2ea947d13454d75b588ed856abbb37615881f GIT binary patch literal 41 ucmV~$!4Uv31O&i;s&R6Ib_n_}A-l8ixFpMvVUC(fzQM9ZMa{jpw$2CY@C#W0 literal 0 HcmV?d00001 From 3f2bb387a43185991d7e077fa5e6c0bb467f2abc Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 28 Oct 2015 11:00:55 -0400 Subject: [PATCH 282/450] merge: octopus merge common ancestors when >2 When there are more than two common ancestors, continue merging the virtual base with the additional common ancestors, effectively octopus merging a new virtual base. --- src/annotated_commit.c | 21 +++ src/annotated_commit.h | 3 + src/merge.c | 286 ++++++++++++++++++++++++----------------- 3 files changed, 190 insertions(+), 120 deletions(-) diff --git a/src/annotated_commit.c b/src/annotated_commit.c index 3f2d2ed17..036e601c1 100644 --- a/src/annotated_commit.c +++ b/src/annotated_commit.c @@ -7,6 +7,7 @@ #include "common.h" #include "annotated_commit.h" +#include "refs.h" #include "git2/commit.h" #include "git2/refs.h" @@ -75,6 +76,26 @@ int git_annotated_commit_from_ref( return error; } +int git_annotated_commit_from_head( + git_annotated_commit **out, + git_repository *repo) +{ + git_reference *head; + int error; + + assert(out && repo); + + *out = NULL; + + if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0) + return -1; + + error = git_annotated_commit_from_ref(out, repo, head); + + git_reference_free(head); + return error; +} + int git_annotated_commit_lookup( git_annotated_commit **out, git_repository *repo, diff --git a/src/annotated_commit.h b/src/annotated_commit.h index e873184ae..37e3d9951 100644 --- a/src/annotated_commit.h +++ b/src/annotated_commit.h @@ -19,4 +19,7 @@ struct git_annotated_commit { char id_str[GIT_OID_HEXSZ+1]; }; +extern int git_annotated_commit_from_head(git_annotated_commit **out, + git_repository *repo); + #endif diff --git a/src/merge.c b/src/merge.c index 0a837b5f4..ce6f4a6ff 100644 --- a/src/merge.c +++ b/src/merge.c @@ -28,6 +28,7 @@ #include "oidarray.h" #include "annotated_commit.h" #include "commit.h" +#include "oidarray.h" #include "git2/types.h" #include "git2/repository.h" @@ -1925,6 +1926,7 @@ done: static int merge_trees_with_heads( git_index **out, + git_commit **base_commit_out, git_repository *repo, const git_tree *ours, const git_tree *theirs, @@ -1938,24 +1940,62 @@ static int merge_trees_with_heads( git_oid_cpy(_alloced, _id); \ } while(0) -static int compute_base( +static int create_virtual_base( git_tree **out, git_repository *repo, + git_tree *base_tree, + git_array_oid_t base_ids, + git_oid *next_commit_id, + const git_merge_options *opts) +{ + git_commit *next_commit = NULL, *intermediate_base = NULL; + git_tree *next_tree = NULL; + git_index *index = NULL; + git_oid new_tree_id; + int error; + + if ((error = git_commit_lookup(&next_commit, repo, next_commit_id)) < 0 || + (error = git_commit_tree(&next_tree, next_commit)) < 0) + goto done; + + INSERT_ID(base_ids, git_commit_id(next_commit)); + + if ((error = merge_trees_with_heads(&index, &intermediate_base, repo, + base_tree, next_tree, base_ids.ptr, base_ids.size, opts)) < 0) + goto done; + + /* TODO: conflicts!! */ + + if ((error = git_index_write_tree_to(&new_tree_id, index, repo)) < 0) + goto done; + + error = git_tree_lookup(out, repo, &new_tree_id); + +done: + git_index_free(index); + git_tree_free(next_tree); + git_commit_free(intermediate_base); + git_commit_free(next_commit); + + return error; +} + +static int compute_base( + git_tree **tree_out, + git_commit **commit_out, + git_repository *repo, const git_oid heads[], size_t heads_len, const git_merge_options *opts) { - git_commit_list *base_list = NULL; + git_commit_list *base_list = NULL, *base_iter; git_revwalk *walk = NULL; - git_commit *base_commit = NULL, *next_commit = NULL; + git_commit *base_commit = NULL; git_tree *base_tree = NULL, *next_tree = NULL; - git_array_t(git_oid) base_ids = GIT_ARRAY_INIT; - git_index *index = NULL; + git_array_oid_t base_ids = GIT_ARRAY_INIT; bool recursive = !opts || (opts->flags & GIT_MERGE_NO_RECURSIVE) == 0; int error = 0; - *out = NULL; - if ((error = merge_bases_many(&base_list, &walk, repo, heads_len, heads)) < 0) return error; @@ -1966,58 +2006,40 @@ static int compute_base( goto done; } + base_iter = base_list; + if ((error = git_commit_lookup(&base_commit, repo, - &base_list->item->oid)) < 0 || + &base_iter->item->oid)) < 0 || (error = git_commit_tree(&base_tree, base_commit)) < 0) goto done; INSERT_ID(base_ids, git_commit_id(base_commit)); - while (recursive && base_list->next) { - git_tree *new_tree; - git_oid new_tree_id; + while (recursive && base_iter->next) { + base_iter = base_iter->next; - base_list = base_list->next; - - if ((error = git_commit_lookup(&next_commit, repo, - &base_list->item->oid)) < 0 || - (error = git_commit_tree(&next_tree, next_commit)) < 0) + if ((error = create_virtual_base(&next_tree, repo, base_tree, + base_ids, &base_iter->item->oid, opts)) < 0) goto done; - INSERT_ID(base_ids, git_commit_id(next_commit)); - - if ((error = merge_trees_with_heads(&index, repo, base_tree, - next_tree, base_ids.ptr, base_ids.size, opts)) < 0) - goto done; - - /* TODO: conflicts!! */ - - if ((error = git_index_write_tree_to(&new_tree_id, index, repo)) < 0 || - (error = git_tree_lookup(&new_tree, repo, &new_tree_id)) < 0) - goto done; - - git_index_free(index); - index = NULL; - - git_tree_free(next_tree); - next_tree = NULL; - - git_commit_free(next_commit); - next_commit = NULL; - git_tree_free(base_tree); - base_tree = new_tree; + base_tree = next_tree; + next_tree = NULL; + + git_commit_free(base_commit); + base_commit = NULL; } - *out = base_tree; - base_tree = NULL; + *tree_out = base_tree; + *commit_out = base_commit; done: - git_index_free(index); + if (error < 0) { + git_tree_free(base_tree); + git_commit_free(base_commit); + } + git_tree_free(next_tree); - git_tree_free(base_tree); - git_commit_free(next_commit); - git_commit_free(base_commit); git_commit_list_free(&base_list); git_revwalk_free(walk); git_array_clear(base_ids); @@ -2025,35 +2047,52 @@ done: return error; } +#undef INSERT_ID + static int merge_trees_with_heads( git_index **out, + git_commit **base_out, git_repository *repo, - const git_tree *ours, - const git_tree *theirs, + const git_tree *our_tree, + const git_tree *their_tree, const git_oid heads[], size_t heads_len, const git_merge_options *opts) { - git_tree *ancestor = NULL; + git_commit *ancestor_commit = NULL; + git_tree *ancestor_tree = NULL; int error = 0; - if ((error = compute_base(&ancestor, repo, heads, heads_len, opts)) < 0) { + *out = NULL; + *base_out = NULL; + + if ((error = compute_base(&ancestor_tree, &ancestor_commit, repo, + heads, heads_len, opts)) < 0) { + if (error == GIT_ENOTFOUND) giterr_clear(); else goto done; } - error = git_merge_trees(out, repo, ancestor, ours, theirs, opts); + if ((error = git_merge_trees(out, + repo, ancestor_tree, our_tree, their_tree, opts)) < 0) + goto done; + + *base_out = ancestor_commit; done: - git_tree_free(ancestor); + if (error < 0) + git_commit_free(ancestor_commit); + + git_tree_free(ancestor_tree); return error; } -int git_merge_commits( +static int merge_commits( git_index **out, + git_commit **base_out, git_repository *repo, const git_commit *our_commit, const git_commit *their_commit, @@ -2070,8 +2109,8 @@ int git_merge_commits( if ((error = git_commit_tree(&our_tree, our_commit)) < 0 || (error = git_commit_tree(&their_tree, their_commit)) < 0 || - (error = merge_trees_with_heads(out, repo, our_tree, their_tree, - heads, 2, opts)) < 0) + (error = merge_trees_with_heads(out, base_out, repo, + our_tree, their_tree, heads, 2, opts)) < 0) goto done; done: @@ -2081,6 +2120,23 @@ done: return error; } +int git_merge_commits( + git_index **out, + git_repository *repo, + const git_commit *our_commit, + const git_commit *their_commit, + const git_merge_options *opts) +{ + git_commit *base_commit = NULL; + int error; + + error = merge_commits(out, &base_commit, repo, + our_commit, their_commit, opts); + + git_commit_free(base_commit); + return error; +} + /* Merge setup / cleanup */ static int write_merge_head( @@ -2511,49 +2567,51 @@ const char *merge_their_label(const char *branchname) } static int merge_normalize_checkout_opts( + git_checkout_options *out, git_repository *repo, - git_checkout_options *checkout_opts, const git_checkout_options *given_checkout_opts, - const git_annotated_commit *ancestor_head, + unsigned int checkout_strategy, + git_commit *ancestor_commit, const git_annotated_commit *our_head, - size_t their_heads_len, - const git_annotated_commit **their_heads) + const git_annotated_commit **their_heads, + size_t their_heads_len) { + git_checkout_options default_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; int error = 0; GIT_UNUSED(repo); if (given_checkout_opts != NULL) - memcpy(checkout_opts, given_checkout_opts, sizeof(git_checkout_options)); - else { - git_checkout_options default_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; - default_checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + memcpy(out, given_checkout_opts, sizeof(git_checkout_options)); + else + memcpy(out, &default_checkout_opts, sizeof(git_checkout_options)); - memcpy(checkout_opts, &default_checkout_opts, sizeof(git_checkout_options)); - } + out->checkout_strategy = checkout_strategy; - /* TODO: for multiple ancestors in merge-recursive, this is "merged common ancestors" */ - if (!checkout_opts->ancestor_label) { - if (ancestor_head && ancestor_head->commit) - checkout_opts->ancestor_label = git_commit_summary(ancestor_head->commit); + /* TODO: disambiguate between merged common ancestors and no common + * ancestor (although git.git does not!) + */ + if (!out->ancestor_label) { + if (ancestor_commit) + out->ancestor_label = git_commit_summary(ancestor_commit); else - checkout_opts->ancestor_label = "ancestor"; + out->ancestor_label = "merged common ancestors"; } - if (!checkout_opts->our_label) { + if (!out->our_label) { if (our_head && our_head->ref_name) - checkout_opts->our_label = our_head->ref_name; + out->our_label = our_head->ref_name; else - checkout_opts->our_label = "ours"; + out->our_label = "ours"; } - if (!checkout_opts->their_label) { + if (!out->their_label) { if (their_heads_len == 1 && their_heads[0]->ref_name) - checkout_opts->their_label = merge_their_label(their_heads[0]->ref_name); + out->their_label = merge_their_label(their_heads[0]->ref_name); else if (their_heads_len == 1) - checkout_opts->their_label = their_heads[0]->id_str; + out->their_label = their_heads[0]->id_str; else - checkout_opts->their_label = "theirs"; + out->their_label = "theirs"; } return error; @@ -2906,11 +2964,11 @@ int git_merge( { git_reference *our_ref = NULL; git_checkout_options checkout_opts; - git_annotated_commit *ancestor_head = NULL, *our_head = NULL; - git_tree *ancestor_tree = NULL, *our_tree = NULL, **their_trees = NULL; + git_annotated_commit *our_head = NULL; + git_commit *base_commit = NULL; git_index *index = NULL; git_indexwriter indexwriter = GIT_INDEXWRITER_INIT; - size_t i; + unsigned int checkout_strategy; int error = 0; assert(repo && their_heads); @@ -2920,61 +2978,49 @@ int git_merge( return -1; } - their_trees = git__calloc(their_heads_len, sizeof(git_tree *)); - GITERR_CHECK_ALLOC(their_trees); + if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0) + goto done; - if ((error = merge_heads(&ancestor_head, &our_head, repo, their_heads, their_heads_len)) < 0 || - (error = merge_normalize_checkout_opts(repo, &checkout_opts, given_checkout_opts, - ancestor_head, our_head, their_heads_len, their_heads)) < 0 || - (error = git_indexwriter_init_for_operation(&indexwriter, repo, &checkout_opts.checkout_strategy)) < 0) - goto on_error; + checkout_strategy = given_checkout_opts ? + given_checkout_opts->checkout_strategy : + GIT_CHECKOUT_SAFE; - /* Write the merge files to the repository. */ - if ((error = git_merge__setup(repo, our_head, their_heads, their_heads_len)) < 0) - goto on_error; + if ((error = git_indexwriter_init_for_operation(&indexwriter, repo, + &checkout_strategy)) < 0) + goto done; - if (ancestor_head != NULL && - (error = git_commit_tree(&ancestor_tree, ancestor_head->commit)) < 0) - goto on_error; + /* Write the merge setup files to the repository. */ + if ((error = git_annotated_commit_from_head(&our_head, repo)) < 0 || + (error = git_merge__setup(repo, our_head, their_heads, + their_heads_len)) < 0) + goto done; - if ((error = git_commit_tree(&our_tree, our_head->commit)) < 0) - goto on_error; + /* TODO: octopus */ - for (i = 0; i < their_heads_len; i++) { - if ((error = git_commit_tree(&their_trees[i], their_heads[i]->commit)) < 0) - goto on_error; - } - - /* TODO: recursive, octopus, etc... */ - - if ((error = git_merge_trees(&index, repo, ancestor_tree, our_tree, their_trees[0], merge_opts)) < 0 || + if ((error = merge_commits(&index, &base_commit, repo, + our_head->commit, their_heads[0]->commit, merge_opts)) < 0 || (error = git_merge__check_result(repo, index)) < 0 || - (error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0 || - (error = git_checkout_index(repo, index, &checkout_opts)) < 0 || - (error = git_indexwriter_commit(&indexwriter)) < 0) - goto on_error; + (error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0) + goto done; - goto done; + /* check out the merge results */ -on_error: - merge_state_cleanup(repo); + if ((error = merge_normalize_checkout_opts(&checkout_opts, repo, + given_checkout_opts, checkout_strategy, + base_commit, our_head, their_heads, their_heads_len)) < 0 || + (error = git_checkout_index(repo, index, &checkout_opts)) < 0) + goto done; + + error = git_indexwriter_commit(&indexwriter); done: + if (error < 0) + merge_state_cleanup(repo); + git_indexwriter_cleanup(&indexwriter); - git_index_free(index); - - git_tree_free(ancestor_tree); - git_tree_free(our_tree); - - for (i = 0; i < their_heads_len; i++) - git_tree_free(their_trees[i]); - - git__free(their_trees); - git_annotated_commit_free(our_head); - git_annotated_commit_free(ancestor_head); - + git_commit_free(base_commit); git_reference_free(our_ref); return error; From 7730fe8e9cda1e160bff1e78dfa2a898799d4365 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 9 Nov 2015 13:01:48 -0500 Subject: [PATCH 283/450] merge: merge annotated commits instead of regular commits --- src/annotated_commit.c | 42 ++++-- src/annotated_commit.h | 2 + src/merge.c | 325 +++++++++++++++++++---------------------- 3 files changed, 181 insertions(+), 188 deletions(-) diff --git a/src/annotated_commit.c b/src/annotated_commit.c index 036e601c1..3998a1af1 100644 --- a/src/annotated_commit.c +++ b/src/annotated_commit.c @@ -8,6 +8,7 @@ #include "common.h" #include "annotated_commit.h" #include "refs.h" +#include "cache.h" #include "git2/commit.h" #include "git2/refs.h" @@ -23,14 +24,17 @@ static int annotated_commit_init( const char *remote_url) { git_annotated_commit *annotated_commit; + git_commit *commit = NULL; int error = 0; assert(out && id); *out = NULL; - annotated_commit = git__calloc(1, sizeof(git_annotated_commit)); - GITERR_CHECK_ALLOC(annotated_commit); + if ((error = git_commit_lookup(&commit, repo, id)) < 0 || + (error = git_annotated_commit_from_commit(&annotated_commit, + commit)) < 0) + goto done; if (ref_name) { annotated_commit->ref_name = git__strdup(ref_name); @@ -42,15 +46,10 @@ static int annotated_commit_init( GITERR_CHECK_ALLOC(annotated_commit->remote_url); } - git_oid_fmt(annotated_commit->id_str, id); - annotated_commit->id_str[GIT_OID_HEXSZ] = '\0'; - - if ((error = git_commit_lookup(&annotated_commit->commit, repo, id)) < 0) { - git_annotated_commit_free(annotated_commit); - return error; - } - *out = annotated_commit; + +done: + git_commit_free(commit); return error; } @@ -96,6 +95,29 @@ int git_annotated_commit_from_head( return error; } +int git_annotated_commit_from_commit( + git_annotated_commit **out, + git_commit *commit) +{ + git_annotated_commit *annotated_commit; + + assert(out && commit); + + *out = NULL; + + annotated_commit = git__calloc(1, sizeof(git_annotated_commit)); + GITERR_CHECK_ALLOC(annotated_commit); + + git_cached_obj_incref(commit); + annotated_commit->commit = commit; + + git_oid_fmt(annotated_commit->id_str, git_commit_id(commit)); + annotated_commit->id_str[GIT_OID_HEXSZ] = '\0'; + + *out = annotated_commit; + return 0; +} + int git_annotated_commit_lookup( git_annotated_commit **out, git_repository *repo, diff --git a/src/annotated_commit.h b/src/annotated_commit.h index 37e3d9951..9a041176e 100644 --- a/src/annotated_commit.h +++ b/src/annotated_commit.h @@ -21,5 +21,7 @@ struct git_annotated_commit { extern int git_annotated_commit_from_head(git_annotated_commit **out, git_repository *repo); +extern int git_annotated_commit_from_commit(git_annotated_commit **out, + git_commit *commit); #endif diff --git a/src/merge.c b/src/merge.c index ce6f4a6ff..59ac8e1ed 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1924,202 +1924,166 @@ done: return error; } -static int merge_trees_with_heads( - git_index **out, - git_commit **base_commit_out, +static int merge_annotated_commits( + git_index **index_out, + git_annotated_commit **base_out, git_repository *repo, - const git_tree *ours, - const git_tree *theirs, - const git_oid heads[], - size_t heads_len, + const git_annotated_commit *our_commit, + const git_annotated_commit *their_commit, + size_t recursion_level, const git_merge_options *opts); -#define INSERT_ID(_ar, _id) do { \ - git_oid *_alloced = git_array_alloc(_ar); \ - GITERR_CHECK_ALLOC(_alloced); \ - git_oid_cpy(_alloced, _id); \ - } while(0) - static int create_virtual_base( - git_tree **out, + git_annotated_commit **out, git_repository *repo, - git_tree *base_tree, - git_array_oid_t base_ids, - git_oid *next_commit_id, - const git_merge_options *opts) + const git_annotated_commit *one, + const git_annotated_commit *two, + size_t recursion_level) { - git_commit *next_commit = NULL, *intermediate_base = NULL; - git_tree *next_tree = NULL; git_index *index = NULL; - git_oid new_tree_id; + git_tree *tree = NULL; + git_commit *commit = NULL; + git_oid id, tree_id; + const git_commit *parents[2]; + git_signature *signature = NULL; int error; - if ((error = git_commit_lookup(&next_commit, repo, next_commit_id)) < 0 || - (error = git_commit_tree(&next_tree, next_commit)) < 0) + parents[0] = one->commit; + parents[1] = two->commit; + + if ((error = merge_annotated_commits(&index, NULL, repo, one, two, + recursion_level + 1, NULL)) < 0 || + (error = git_index_write_tree_to(&tree_id, index, repo)) < 0 || + (error = git_tree_lookup(&tree, repo, &tree_id)) < 0 || + (error = git_signature_now(&signature, "Virtual", "virtual")) < 0 || + (error = git_commit_create(&id, repo, NULL, signature, signature, + NULL, "virtual merged tree", tree, 2, parents)) < 0 || + (error = git_commit_lookup(&commit, repo, &id)) < 0) goto done; - INSERT_ID(base_ids, git_commit_id(next_commit)); - - if ((error = merge_trees_with_heads(&index, &intermediate_base, repo, - base_tree, next_tree, base_ids.ptr, base_ids.size, opts)) < 0) - goto done; - - /* TODO: conflicts!! */ - - if ((error = git_index_write_tree_to(&new_tree_id, index, repo)) < 0) - goto done; - - error = git_tree_lookup(out, repo, &new_tree_id); + error = git_annotated_commit_from_commit(out, commit); done: + git_commit_free(commit); + git_tree_free(tree); git_index_free(index); - git_tree_free(next_tree); - git_commit_free(intermediate_base); - git_commit_free(next_commit); + git_signature_free(signature); return error; } +GIT_INLINE(int) insert_head_ids( + git_array_oid_t *ids, + const git_annotated_commit *annotated_commit) +{ + git_oid *id = git_array_alloc(*ids); + GITERR_CHECK_ALLOC(id); + + git_oid_cpy(id, git_commit_id(annotated_commit->commit)); + return 0; +} + static int compute_base( - git_tree **tree_out, - git_commit **commit_out, + git_annotated_commit **out, git_repository *repo, - const git_oid heads[], - size_t heads_len, - const git_merge_options *opts) + const git_annotated_commit *one, + const git_annotated_commit *two, + bool recurse, + size_t recursion_level) { - git_commit_list *base_list = NULL, *base_iter; - git_revwalk *walk = NULL; - git_commit *base_commit = NULL; - git_tree *base_tree = NULL, *next_tree = NULL; - git_array_oid_t base_ids = GIT_ARRAY_INIT; - bool recursive = !opts || (opts->flags & GIT_MERGE_NO_RECURSIVE) == 0; - int error = 0; - - if ((error = merge_bases_many(&base_list, &walk, repo, - heads_len, heads)) < 0) - return error; - - if (error == GIT_ENOTFOUND) { - giterr_clear(); - error = 0; - goto done; - } - - base_iter = base_list; - - if ((error = git_commit_lookup(&base_commit, repo, - &base_iter->item->oid)) < 0 || - (error = git_commit_tree(&base_tree, base_commit)) < 0) - goto done; - - INSERT_ID(base_ids, git_commit_id(base_commit)); - - while (recursive && base_iter->next) { - base_iter = base_iter->next; - - if ((error = create_virtual_base(&next_tree, repo, base_tree, - base_ids, &base_iter->item->oid, opts)) < 0) - goto done; - - git_tree_free(base_tree); - base_tree = next_tree; - next_tree = NULL; - - git_commit_free(base_commit); - base_commit = NULL; - } - - *tree_out = base_tree; - *commit_out = base_commit; - -done: - if (error < 0) { - git_tree_free(base_tree); - git_commit_free(base_commit); - } - - git_tree_free(next_tree); - git_commit_list_free(&base_list); - git_revwalk_free(walk); - git_array_clear(base_ids); - - return error; -} - -#undef INSERT_ID - -static int merge_trees_with_heads( - git_index **out, - git_commit **base_out, - git_repository *repo, - const git_tree *our_tree, - const git_tree *their_tree, - const git_oid heads[], - size_t heads_len, - const git_merge_options *opts) -{ - git_commit *ancestor_commit = NULL; - git_tree *ancestor_tree = NULL; - int error = 0; - - *out = NULL; - *base_out = NULL; - - if ((error = compute_base(&ancestor_tree, &ancestor_commit, repo, - heads, heads_len, opts)) < 0) { - - if (error == GIT_ENOTFOUND) - giterr_clear(); - else - goto done; - } - - if ((error = git_merge_trees(out, - repo, ancestor_tree, our_tree, their_tree, opts)) < 0) - goto done; - - *base_out = ancestor_commit; - -done: - if (error < 0) - git_commit_free(ancestor_commit); - - git_tree_free(ancestor_tree); - - return error; -} - -static int merge_commits( - git_index **out, - git_commit **base_out, - git_repository *repo, - const git_commit *our_commit, - const git_commit *their_commit, - const git_merge_options *opts) -{ - git_tree *our_tree = NULL, *their_tree = NULL; - git_oid heads[2]; - int error = 0; + git_array_oid_t head_ids = GIT_ARRAY_INIT; + git_oidarray bases = {0}; + git_annotated_commit *base = NULL, *other = NULL, *new_base = NULL; + size_t i; + int error; *out = NULL; - git_oid_cpy(&heads[0], git_commit_id(our_commit)); - git_oid_cpy(&heads[1], git_commit_id(their_commit)); - - if ((error = git_commit_tree(&our_tree, our_commit)) < 0 || - (error = git_commit_tree(&their_tree, their_commit)) < 0 || - (error = merge_trees_with_heads(out, base_out, repo, - our_tree, their_tree, heads, 2, opts)) < 0) + if ((error = insert_head_ids(&head_ids, one)) < 0 || + (error = insert_head_ids(&head_ids, two)) < 0) goto done; + if ((error = git_merge_bases_many(&bases, repo, + head_ids.size, head_ids.ptr)) < 0 || + (error = git_annotated_commit_lookup(&base, repo, &bases.ids[0])) < 0 || + !recurse) + goto done; + + for (i = 1; i < bases.count; i++) { + recursion_level++; + + if ((error = git_annotated_commit_lookup(&other, repo, + &bases.ids[i])) < 0 || + (error = create_virtual_base(&new_base, repo, base, other, + recursion_level)) < 0) + goto done; + + git_annotated_commit_free(base); + git_annotated_commit_free(other); + + base = new_base; + new_base = NULL; + other = NULL; + } + done: + if (error == 0) + *out = base; + else + git_annotated_commit_free(base); + + git_annotated_commit_free(other); + git_annotated_commit_free(new_base); + git_oidarray_free(&bases); + git_array_clear(head_ids); + return error; +} + +static int merge_annotated_commits( + git_index **index_out, + git_annotated_commit **base_out, + git_repository *repo, + const git_annotated_commit *our_commit, + const git_annotated_commit *their_commit, + size_t recursion_level, + const git_merge_options *opts) +{ + git_annotated_commit *base = NULL; + git_tree *base_tree = NULL, *our_tree = NULL, *their_tree = NULL; + bool recurse = !opts || !(opts->flags & GIT_MERGE_NO_RECURSIVE); + int error; + + if ((error = compute_base(&base, repo, our_commit, their_commit, + recurse, recursion_level)) < 0) { + + if (error != GIT_ENOTFOUND) + goto done; + + giterr_clear(); + } else if ((error = git_commit_tree(&base_tree, base->commit)) < 0) { + goto done; + } + + if ((error = git_commit_tree(&our_tree, our_commit->commit)) < 0 || + (error = git_commit_tree(&their_tree, their_commit->commit)) < 0 || + (error = git_merge_trees(index_out, repo, base_tree, our_tree, + their_tree, opts)) < 0) + goto done; + + if (base_out) { + *base_out = base; + base = NULL; + } + +done: + git_annotated_commit_free(base); git_tree_free(our_tree); git_tree_free(their_tree); - + git_tree_free(base_tree); return error; } + int git_merge_commits( git_index **out, git_repository *repo, @@ -2127,13 +2091,19 @@ int git_merge_commits( const git_commit *their_commit, const git_merge_options *opts) { - git_commit *base_commit = NULL; - int error; + git_annotated_commit *ours = NULL, *theirs = NULL, *base = NULL; + int error = 0; - error = merge_commits(out, &base_commit, repo, - our_commit, their_commit, opts); + if ((error = git_annotated_commit_from_commit(&ours, (git_commit *)our_commit)) < 0 || + (error = git_annotated_commit_from_commit(&theirs, (git_commit *)their_commit)) < 0) + goto done; - git_commit_free(base_commit); + error = merge_annotated_commits(out, &base, repo, ours, theirs, 0, opts); + +done: + git_annotated_commit_free(ours); + git_annotated_commit_free(theirs); + git_annotated_commit_free(base); return error; } @@ -2571,7 +2541,7 @@ static int merge_normalize_checkout_opts( git_repository *repo, const git_checkout_options *given_checkout_opts, unsigned int checkout_strategy, - git_commit *ancestor_commit, + git_annotated_commit *ancestor, const git_annotated_commit *our_head, const git_annotated_commit **their_heads, size_t their_heads_len) @@ -2592,8 +2562,8 @@ static int merge_normalize_checkout_opts( * ancestor (although git.git does not!) */ if (!out->ancestor_label) { - if (ancestor_commit) - out->ancestor_label = git_commit_summary(ancestor_commit); + if (ancestor) + out->ancestor_label = git_commit_summary(ancestor->commit); else out->ancestor_label = "merged common ancestors"; } @@ -2964,8 +2934,7 @@ int git_merge( { git_reference *our_ref = NULL; git_checkout_options checkout_opts; - git_annotated_commit *our_head = NULL; - git_commit *base_commit = NULL; + git_annotated_commit *our_head = NULL, *base = NULL; git_index *index = NULL; git_indexwriter indexwriter = GIT_INDEXWRITER_INIT; unsigned int checkout_strategy; @@ -2997,8 +2966,8 @@ int git_merge( /* TODO: octopus */ - if ((error = merge_commits(&index, &base_commit, repo, - our_head->commit, their_heads[0]->commit, merge_opts)) < 0 || + if ((error = merge_annotated_commits(&index, &base, repo, our_head, + their_heads[0], 0, merge_opts)) < 0 || (error = git_merge__check_result(repo, index)) < 0 || (error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0) goto done; @@ -3007,7 +2976,7 @@ int git_merge( if ((error = merge_normalize_checkout_opts(&checkout_opts, repo, given_checkout_opts, checkout_strategy, - base_commit, our_head, their_heads, their_heads_len)) < 0 || + base, our_head, their_heads, their_heads_len)) < 0 || (error = git_checkout_index(repo, index, &checkout_opts)) < 0) goto done; @@ -3020,7 +2989,7 @@ done: git_indexwriter_cleanup(&indexwriter); git_index_free(index); git_annotated_commit_free(our_head); - git_commit_free(base_commit); + git_annotated_commit_free(base); git_reference_free(our_ref); return error; From 76ade3a0b87e279935eba54be2485105396edb7f Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 10 Nov 2015 21:21:26 -0800 Subject: [PATCH 284/450] merge: use annotated commits for recursion Use annotated commits to act as our virtual bases, instead of regular commits, to avoid polluting the odb with virtual base commits and trees. Instead, build an annotated commit with an index and pointers to the commits that it was merged from. --- src/annotated_commit.c | 26 ++++--- src/annotated_commit.h | 22 +++++- src/merge.c | 154 ++++++++++++++++++++++++----------------- 3 files changed, 128 insertions(+), 74 deletions(-) diff --git a/src/annotated_commit.c b/src/annotated_commit.c index 3998a1af1..e53b95dee 100644 --- a/src/annotated_commit.c +++ b/src/annotated_commit.c @@ -15,6 +15,8 @@ #include "git2/repository.h" #include "git2/annotated_commit.h" #include "git2/revparse.h" +#include "git2/tree.h" +#include "git2/index.h" static int annotated_commit_init( git_annotated_commit **out, @@ -108,6 +110,8 @@ int git_annotated_commit_from_commit( annotated_commit = git__calloc(1, sizeof(git_annotated_commit)); GITERR_CHECK_ALLOC(annotated_commit); + annotated_commit->type = GIT_ANNOTATED_COMMIT_REAL; + git_cached_obj_incref(commit); annotated_commit->commit = commit; @@ -179,14 +183,20 @@ void git_annotated_commit_free(git_annotated_commit *annotated_commit) if (annotated_commit == NULL) return; - if (annotated_commit->commit != NULL) - git_commit_free(annotated_commit->commit); - - if (annotated_commit->ref_name != NULL) - git__free(annotated_commit->ref_name); - - if (annotated_commit->remote_url != NULL) - git__free(annotated_commit->remote_url); + switch (annotated_commit->type) { + case GIT_ANNOTATED_COMMIT_REAL: + git_commit_free(annotated_commit->commit); + git_tree_free(annotated_commit->tree); + git__free(annotated_commit->ref_name); + git__free(annotated_commit->remote_url); + break; + case GIT_ANNOTATED_COMMIT_VIRTUAL: + git_index_free(annotated_commit->index); + git_array_clear(annotated_commit->parents); + break; + default: + abort(); + } git__free(annotated_commit); } diff --git a/src/annotated_commit.h b/src/annotated_commit.h index 9a041176e..cbb88fd22 100644 --- a/src/annotated_commit.h +++ b/src/annotated_commit.h @@ -7,11 +7,31 @@ #ifndef INCLUDE_annotated_commit_h__ #define INCLUDE_annotated_commit_h__ +#include "oidarray.h" + #include "git2/oid.h" -/** Internal structure for merge inputs */ +typedef enum { + GIT_ANNOTATED_COMMIT_REAL = 1, + GIT_ANNOTATED_COMMIT_VIRTUAL = 2, +} git_annotated_commit_t; + +/** + * Internal structure for merge inputs. An annotated commit is generally + * "real" and backed by an actual commit in the repository, but merge will + * internally create "virtual" commits that are in-memory intermediate + * commits backed by an index. + */ struct git_annotated_commit { + git_annotated_commit_t type; + + /* real commit */ git_commit *commit; + git_tree *tree; + + /* virtual commit structure */ + git_index *index; + git_array_oid_t parents; char *ref_name; char *remote_url; diff --git a/src/merge.c b/src/merge.c index 59ac8e1ed..64c8f1116 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1928,58 +1928,59 @@ static int merge_annotated_commits( git_index **index_out, git_annotated_commit **base_out, git_repository *repo, - const git_annotated_commit *our_commit, - const git_annotated_commit *their_commit, + git_annotated_commit *our_commit, + git_annotated_commit *their_commit, size_t recursion_level, const git_merge_options *opts); -static int create_virtual_base( - git_annotated_commit **out, - git_repository *repo, - const git_annotated_commit *one, - const git_annotated_commit *two, - size_t recursion_level) -{ - git_index *index = NULL; - git_tree *tree = NULL; - git_commit *commit = NULL; - git_oid id, tree_id; - const git_commit *parents[2]; - git_signature *signature = NULL; - int error; - - parents[0] = one->commit; - parents[1] = two->commit; - - if ((error = merge_annotated_commits(&index, NULL, repo, one, two, - recursion_level + 1, NULL)) < 0 || - (error = git_index_write_tree_to(&tree_id, index, repo)) < 0 || - (error = git_tree_lookup(&tree, repo, &tree_id)) < 0 || - (error = git_signature_now(&signature, "Virtual", "virtual")) < 0 || - (error = git_commit_create(&id, repo, NULL, signature, signature, - NULL, "virtual merged tree", tree, 2, parents)) < 0 || - (error = git_commit_lookup(&commit, repo, &id)) < 0) - goto done; - - error = git_annotated_commit_from_commit(out, commit); - -done: - git_commit_free(commit); - git_tree_free(tree); - git_index_free(index); - git_signature_free(signature); - - return error; -} - GIT_INLINE(int) insert_head_ids( git_array_oid_t *ids, const git_annotated_commit *annotated_commit) { - git_oid *id = git_array_alloc(*ids); - GITERR_CHECK_ALLOC(id); + git_oid *id; + size_t i; - git_oid_cpy(id, git_commit_id(annotated_commit->commit)); + if (annotated_commit->type == GIT_ANNOTATED_COMMIT_REAL) { + id = git_array_alloc(*ids); + GITERR_CHECK_ALLOC(id); + + git_oid_cpy(id, git_commit_id(annotated_commit->commit)); + } else { + for (i = 0; i < annotated_commit->parents.size; i++) { + id = git_array_alloc(*ids); + GITERR_CHECK_ALLOC(id); + + git_oid_cpy(id, &annotated_commit->parents.ptr[i]); + } + } + + return 0; +} + +static int create_virtual_base( + git_annotated_commit **out, + git_repository *repo, + git_annotated_commit *one, + git_annotated_commit *two, + size_t recursion_level) +{ + git_annotated_commit *result = NULL; + git_index *index = NULL; + + result = git__calloc(1, sizeof(git_annotated_commit)); + GITERR_CHECK_ALLOC(result); + + if ((merge_annotated_commits(&index, NULL, repo, one, two, + recursion_level + 1, NULL)) < 0) + return -1; + + result->type = GIT_ANNOTATED_COMMIT_VIRTUAL; + result->index = index; + + insert_head_ids(&result->parents, one); + insert_head_ids(&result->parents, two); + + *out = result; return 0; } @@ -2039,35 +2040,59 @@ done: return error; } +static int iterator_for_annotated_commit( + git_iterator **out, + git_annotated_commit *commit) +{ + git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT; + int error; + + opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; + + if (commit == NULL) { + error = git_iterator_for_nothing(out, &opts); + } else if (commit->type == GIT_ANNOTATED_COMMIT_VIRTUAL) { + error = git_iterator_for_index(out, commit->index, &opts); + } else { + if (!commit->tree && + (error = git_commit_tree(&commit->tree, commit->commit)) < 0) + goto done; + + error = git_iterator_for_tree(out, commit->tree, &opts); + } + +done: + return error; +} + static int merge_annotated_commits( git_index **index_out, git_annotated_commit **base_out, git_repository *repo, - const git_annotated_commit *our_commit, - const git_annotated_commit *their_commit, + git_annotated_commit *ours, + git_annotated_commit *theirs, size_t recursion_level, const git_merge_options *opts) { git_annotated_commit *base = NULL; - git_tree *base_tree = NULL, *our_tree = NULL, *their_tree = NULL; + git_iterator *base_iter = NULL, *our_iter = NULL, *their_iter = NULL; bool recurse = !opts || !(opts->flags & GIT_MERGE_NO_RECURSIVE); int error; - if ((error = compute_base(&base, repo, our_commit, their_commit, - recurse, recursion_level)) < 0) { + if ((error = compute_base(&base, repo, ours, theirs, recurse, + recursion_level)) < 0) { if (error != GIT_ENOTFOUND) goto done; giterr_clear(); - } else if ((error = git_commit_tree(&base_tree, base->commit)) < 0) { - goto done; - } + } - if ((error = git_commit_tree(&our_tree, our_commit->commit)) < 0 || - (error = git_commit_tree(&their_tree, their_commit->commit)) < 0 || - (error = git_merge_trees(index_out, repo, base_tree, our_tree, - their_tree, opts)) < 0) + if ((error = iterator_for_annotated_commit(&base_iter, base)) < 0 || + (error = iterator_for_annotated_commit(&our_iter, ours)) < 0 || + (error = iterator_for_annotated_commit(&their_iter, theirs)) < 0 || + (error = git_merge__iterators(index_out, repo, base_iter, our_iter, + their_iter, opts)) < 0) goto done; if (base_out) { @@ -2077,9 +2102,9 @@ static int merge_annotated_commits( done: git_annotated_commit_free(base); - git_tree_free(our_tree); - git_tree_free(their_tree); - git_tree_free(base_tree); + git_iterator_free(base_iter); + git_iterator_free(our_iter); + git_iterator_free(their_iter); return error; } @@ -2558,14 +2583,13 @@ static int merge_normalize_checkout_opts( out->checkout_strategy = checkout_strategy; - /* TODO: disambiguate between merged common ancestors and no common - * ancestor (although git.git does not!) - */ if (!out->ancestor_label) { - if (ancestor) + if (ancestor && ancestor->type == GIT_ANNOTATED_COMMIT_REAL) out->ancestor_label = git_commit_summary(ancestor->commit); - else + else if (ancestor) out->ancestor_label = "merged common ancestors"; + else + out->ancestor_label = "empty base"; } if (!out->our_label) { @@ -2967,7 +2991,7 @@ int git_merge( /* TODO: octopus */ if ((error = merge_annotated_commits(&index, &base, repo, our_head, - their_heads[0], 0, merge_opts)) < 0 || + (git_annotated_commit *)their_heads[0], 0, merge_opts)) < 0 || (error = git_merge__check_result(repo, index)) < 0 || (error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0) goto done; From dcde5720424961f526e8c070b7d99191d7ef75c1 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 9 Nov 2015 08:23:27 -0500 Subject: [PATCH 285/450] merge tests: move expected data into own file --- tests/merge/conflict_data.h | 42 ++++++++++++++++++++++++++++++++++ tests/merge/files.c | 1 + tests/merge/merge_helpers.h | 43 ----------------------------------- tests/merge/trees/automerge.c | 3 ++- tests/merge/trees/commits.c | 1 + tests/merge/workdir/simple.c | 1 + 6 files changed, 47 insertions(+), 44 deletions(-) create mode 100644 tests/merge/conflict_data.h diff --git a/tests/merge/conflict_data.h b/tests/merge/conflict_data.h new file mode 100644 index 000000000..173892d95 --- /dev/null +++ b/tests/merge/conflict_data.h @@ -0,0 +1,42 @@ +#define AUTOMERGEABLE_MERGED_FILE \ + "this file is changed in master\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is changed in branch\n" + +#define AUTOMERGEABLE_MERGED_FILE_CRLF \ + "this file is changed in master\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is changed in branch\r\n" + +#define CONFLICTING_MERGE_FILE \ + "<<<<<<< HEAD\n" \ + "this file is changed in master and branch\n" \ + "=======\n" \ + "this file is changed in branch and master\n" \ + ">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n" + +#define CONFLICTING_DIFF3_FILE \ + "<<<<<<< HEAD\n" \ + "this file is changed in master and branch\n" \ + "||||||| initial\n" \ + "this file is a conflict\n" \ + "=======\n" \ + "this file is changed in branch and master\n" \ + ">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n" + +#define CONFLICTING_UNION_FILE \ + "this file is changed in master and branch\n" \ + "this file is changed in branch and master\n" + diff --git a/tests/merge/files.c b/tests/merge/files.c index 2d55df2b2..daa73fada 100644 --- a/tests/merge/files.c +++ b/tests/merge/files.c @@ -4,6 +4,7 @@ #include "buffer.h" #include "merge.h" #include "merge_helpers.h" +#include "conflict_data.h" #include "refs.h" #include "fileops.h" #include "diff_xdiff.h" diff --git a/tests/merge/merge_helpers.h b/tests/merge/merge_helpers.h index 554c24b7c..e407c7d13 100644 --- a/tests/merge/merge_helpers.h +++ b/tests/merge/merge_helpers.h @@ -4,49 +4,6 @@ #include "merge.h" #include "git2/merge.h" -#define AUTOMERGEABLE_MERGED_FILE \ - "this file is changed in master\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is changed in branch\n" - -#define AUTOMERGEABLE_MERGED_FILE_CRLF \ - "this file is changed in master\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is changed in branch\r\n" - -#define CONFLICTING_MERGE_FILE \ - "<<<<<<< HEAD\n" \ - "this file is changed in master and branch\n" \ - "=======\n" \ - "this file is changed in branch and master\n" \ - ">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n" - -#define CONFLICTING_DIFF3_FILE \ - "<<<<<<< HEAD\n" \ - "this file is changed in master and branch\n" \ - "||||||| initial\n" \ - "this file is a conflict\n" \ - "=======\n" \ - "this file is changed in branch and master\n" \ - ">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n" - -#define CONFLICTING_UNION_FILE \ - "this file is changed in master and branch\n" \ - "this file is changed in branch and master\n" - - struct merge_index_entry { uint16_t mode; char oid_str[GIT_OID_HEXSZ+1]; diff --git a/tests/merge/trees/automerge.c b/tests/merge/trees/automerge.c index c18881d7c..67f2cf786 100644 --- a/tests/merge/trees/automerge.c +++ b/tests/merge/trees/automerge.c @@ -3,8 +3,9 @@ #include "git2/merge.h" #include "buffer.h" #include "merge.h" -#include "../merge_helpers.h" #include "fileops.h" +#include "../merge_helpers.h" +#include "../conflict_data.h" static git_repository *repo; diff --git a/tests/merge/trees/commits.c b/tests/merge/trees/commits.c index dd1e383ac..786a77a8b 100644 --- a/tests/merge/trees/commits.c +++ b/tests/merge/trees/commits.c @@ -3,6 +3,7 @@ #include "git2/merge.h" #include "merge.h" #include "../merge_helpers.h" +#include "../conflict_data.h" static git_repository *repo; diff --git a/tests/merge/workdir/simple.c b/tests/merge/workdir/simple.c index abc0777f7..3cdd15b5a 100644 --- a/tests/merge/workdir/simple.c +++ b/tests/merge/workdir/simple.c @@ -4,6 +4,7 @@ #include "buffer.h" #include "merge.h" #include "../merge_helpers.h" +#include "../conflict_data.h" #include "refs.h" #include "fileops.h" From 651bfd699c8eaa8ca36de0d0d992a1832d71df8f Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 9 Nov 2015 08:24:47 -0500 Subject: [PATCH 286/450] recursive: test conflict output during recursive merge --- tests/merge/conflict_data.h | 30 +++++++++++++++++++ tests/merge/workdir/recursive.c | 51 +++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 tests/merge/workdir/recursive.c diff --git a/tests/merge/conflict_data.h b/tests/merge/conflict_data.h index 173892d95..b6c51332f 100644 --- a/tests/merge/conflict_data.h +++ b/tests/merge/conflict_data.h @@ -40,3 +40,33 @@ "this file is changed in master and branch\n" \ "this file is changed in branch and master\n" +#define CONFLICTING_RECURSIVE_F1_TO_F2 \ + "VEAL SOUP.\n" \ + "\n" \ + "<<<<<<< HEAD\n" \ + "PUT INTO A POT THREE QUARTS OF WATER, three onions cut small, ONE\n" \ + "=======\n" \ + "PUT INTO A POT THREE QUARTS OF WATER, three onions cut not too small, one\n" \ + ">>>>>>> branchF-2\n" \ + "spoonful of black pepper pounded, and two of salt, with two or three\n" \ + "slices of lean ham; let it boil steadily two hours; skim it\n" \ + "occasionally, then put into it a shin of veal, let it boil two hours\n" \ + "longer; take out the slices of ham, and skim off the grease if any\n" \ + "should rise, take a gill of good cream, mix with it two table-spoonsful\n" \ + "of flour very nicely, and the yelks of two eggs beaten well, strain this\n" \ + "mixture, and add some chopped parsley; pour some soup on by degrees,\n" \ + "stir it well, and pour it into the pot, continuing to stir until it has\n" \ + "boiled two or three minutes to take off the raw taste of the eggs. If\n" \ + "the cream be not perfectly sweet, and the eggs quite new, the thickening\n" \ + "will curdle in the soup. For a change you may put a dozen ripe tomatos\n" \ + "in, first taking off their skins, by letting them stand a few minutes in\n" \ + "hot water, when they may be easily peeled. When made in this way you\n" \ + "must thicken it with the flour only. Any part of the veal may be used,\n" \ + "but the shin or knuckle is the nicest.\n" \ + "\n" \ + "<<<<<<< HEAD\n" \ + "This certainly is a mighty fine recipe.\n" \ + "=======\n" \ + "This is a mighty fine recipe!\n" \ + ">>>>>>> branchF-2\n" + diff --git a/tests/merge/workdir/recursive.c b/tests/merge/workdir/recursive.c new file mode 100644 index 000000000..a7326009a --- /dev/null +++ b/tests/merge/workdir/recursive.c @@ -0,0 +1,51 @@ +#include "clar_libgit2.h" +#include "git2/repository.h" +#include "git2/merge.h" +#include "merge.h" +#include "../merge_helpers.h" +#include "../conflict_data.h" + +static git_repository *repo; + +#define TEST_REPO_PATH "merge-recursive" + +void test_merge_workdir_recursive__initialize(void) +{ + repo = cl_git_sandbox_init(TEST_REPO_PATH); +} + +void test_merge_workdir_recursive__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_merge_workdir_recursive__writes_conflict_with_virtual_base(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + git_buf conflicting_buf = GIT_BUF_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "fa567f568ed72157c0c617438d077695b99d9aac", 1, "veal.txt" }, + { 0100644, "21950d5e4e4d1a871b4dfcf72ecb6b9c162c434e", 2, "veal.txt" }, + { 0100644, "3855170cef875708da06ab9ad7fc6a73b531cda1", 3, "veal.txt" }, + }; + + + cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR "branchF-1", GIT_REFS_HEADS_DIR "branchF-2", &opts, NULL)); + + cl_git_pass(git_repository_index(&index, repo)); + cl_assert(merge_test_index(index, merge_index_entries, 8)); + + cl_git_pass(git_futils_readbuffer(&conflicting_buf, "merge-recursive/veal.txt")); + + cl_assert_equal_s(CONFLICTING_RECURSIVE_F1_TO_F2, conflicting_buf.ptr); + + git_index_free(index); + git_buf_free(&conflicting_buf); +} From 34a51428a121800509c2bea94137a17802e37982 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 9 Nov 2015 11:55:26 -0500 Subject: [PATCH 287/450] merge tests: add complex recursive example --- tests/merge/trees/recursive.c | 28 ++++++++++++++++++ tests/resources/merge-recursive/.gitted/HEAD | Bin 26 -> 26 bytes tests/resources/merge-recursive/.gitted/index | Bin 632 -> 619 bytes .../34/8f16ffaeb73f319a75cec5b16a0a47d2d5e27c | Bin 0 -> 208 bytes .../48/3065df53c0f4a02cdc6b2910b05d388fc17ffb | Bin 0 -> 165 bytes .../c4/83ca4bb087174af5cb51d7caa9c09fe4a28ccb | Bin 0 -> 241 bytes .../d7/1c24b3b113fd1d1909998c5bfe33b86a65ee03 | Bin 0 -> 240 bytes .../.gitted/refs/heads/branchG-1 | Bin 0 -> 41 bytes .../.gitted/refs/heads/branchG-2 | Bin 0 -> 41 bytes tests/resources/merge-recursive/asparagus.txt | Bin 587 -> 587 bytes tests/resources/merge-recursive/oyster.txt | Bin 774 -> 774 bytes tests/resources/merge-recursive/veal.txt | Bin 1095 -> 1122 bytes 12 files changed, 28 insertions(+) create mode 100644 tests/resources/merge-recursive/.gitted/objects/34/8f16ffaeb73f319a75cec5b16a0a47d2d5e27c create mode 100644 tests/resources/merge-recursive/.gitted/objects/48/3065df53c0f4a02cdc6b2910b05d388fc17ffb create mode 100644 tests/resources/merge-recursive/.gitted/objects/c4/83ca4bb087174af5cb51d7caa9c09fe4a28ccb create mode 100644 tests/resources/merge-recursive/.gitted/objects/d7/1c24b3b113fd1d1909998c5bfe33b86a65ee03 create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchG-1 create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchG-2 diff --git a/tests/merge/trees/recursive.c b/tests/merge/trees/recursive.c index abca01727..1e5f61391 100644 --- a/tests/merge/trees/recursive.c +++ b/tests/merge/trees/recursive.c @@ -269,3 +269,31 @@ void test_merge_trees_recursive__conflict(void) git_index_free(index); } +/* + * Branch G-1 and G-2 have three common ancestors (815b5a1, ad2ace9, 483065d). + * The merge-base of the first two has two common ancestors (723181f, a34e5a1) + * which themselves have two common ancestors (8f35f30, 3a3f5a6), which + * finally has a common ancestor of 7c7bf85. This virtual merge base will + * be computed and merged with 483065d which also has a common ancestor of + * 7c7bf85. + */ +void test_merge_trees_recursive__oh_so_many_levels_of_recursion(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" }, + { 0100644, "7c7e08f9559d9e1551b91e1cf68f1d0066109add", 0, "oyster.txt" }, + { 0100644, "898d12687fb35be271c27c795a6b32c8b51da79e", 0, "veal.txt" }, + }; + + cl_git_pass(merge_commits_from_branches(&index, repo, "branchG-1", "branchG-2", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 6)); + + git_index_free(index); +} diff --git a/tests/resources/merge-recursive/.gitted/HEAD b/tests/resources/merge-recursive/.gitted/HEAD index 77e35742d01dcf26e0594d689548d3ae37549fca..d7cef11aa7942cf4664096bcca2b11b0ba1a99cc 100644 GIT binary patch delta 9 Qcmb1=njpsFu4}{v00>+Gj{pDw delta 9 Qcmb1=njpsFq-)3p00>3_i2wiq diff --git a/tests/resources/merge-recursive/.gitted/index b/tests/resources/merge-recursive/.gitted/index index 1e47851a50c3550bd04e7e1a481346d39b101267..571cfd02f85a253dd729b3b672b3e63e78ca19a3 100644 GIT binary patch literal 619 zcmZ?q402{*U|<4bwlIfXu0WarMl%A%8S|FSW?*Ps!oa}z6(}VF6!QMRIWN#Y{(e{X zihxCoPU%@%M;dAwcoT~Y5{nYkON;eNDoQ}cfXuN$rlICdMm3K$CgYogzEA7rdBU7G zYwBDR!|u#sn8v`7l$x3bF$#w{Q&7!eaq+GR3t;HhE2)3jFz^;7QRxSqar2Nv% zoSaO!t8lt&Dyn&mZAYG+S^OsJfk@va!=FXr_j?TG7~>f@(~A8O z=gpcr*Tk?pa~P&Ea3rOsra_FtVa_~Mb68xwYr+B;y7fxxA2!T;9VL5@uc4KTfjcR` zG&3hB6YeUU?wXHk9%I{)XJ;0_$$B8tcggT)QTY8HLpjEH2F~=N#Ij1TX*k@q0M#63 zwv6@i#~tc#uDvhLxT0?3$(WKUM`ti_ diff --git a/tests/resources/merge-recursive/.gitted/objects/34/8f16ffaeb73f319a75cec5b16a0a47d2d5e27c b/tests/resources/merge-recursive/.gitted/objects/34/8f16ffaeb73f319a75cec5b16a0a47d2d5e27c new file mode 100644 index 0000000000000000000000000000000000000000..bd1b1f69a9e070fd93f6859f9f5d8b6c29335b6d GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjKvzPwlJ zvRs>X5O!@9(J**$fRV(D&CW7|Ct_P%v^jxfi!sqa>Yi4)HH`)HL|!7&WZ6!+}F-QxJp?Lz! z!m-+dP$5W rj6?q!GrZ{jrl#(+@~mew{-VbTM?l)gY-kImz{;W8NI*V`lb|l~@J_p~haqvqTGWOXV@0tR zb8$|4g)k~%D={^SSHI5)F-Qx@&^&=-;aFXeZiZsDUhJU`oD7g20Ig zxMzC2ql%!1fj*CU+s(6l(Jtn7-jiR4N?g94-HV<$n%Q{06@hCOaK4? delta 21 ccmZo;Yh&Z_j|>iR4N?g94-L@U$n%Q{06_)@SpWb4 diff --git a/tests/resources/merge-recursive/veal.txt b/tests/resources/merge-recursive/veal.txt index 94d2c01087f48213bd157222d54edfefd77c9bba..898d12687fb35be271c27c795a6b32c8b51da79e 100644 GIT binary patch delta 120 zcmX@k@rc7S%+=9HA=p1OK#z+nAT&h5(=Wtd!BHW=KSUwKBgoZNAu!Z2C?r_H-%TOh zF~l`UN5NPjKQA*suUH|uv_zpeH!&wiN5S9Eb)xTCM(fSej5^HnTp<~m#R@=_sF0hP Wo>5Y%kd~R3s!)`goLP{n$OQm3OC#+7 delta 92 zcmaFFah$_9%+=9HA=p1OK#_|pptM9GGp{6HAyJ_qzeJ%VqbN01p|CWusH9jSKTV-L pu_U!f2P&MOmzkeetdI=UQk Date: Fri, 20 Nov 2015 17:33:49 -0500 Subject: [PATCH 288/450] merge: handle conflicts in recursive base building When building a recursive merge base, allow conflicts to occur. Use the file (with conflict markers) as the common ancestor. The user has already seen and dealt with this conflict by virtue of having a criss-cross merge. If they resolved this conflict identically in both branches, then there will be no conflict in the result. This is the best case scenario. If they did not resolve the conflict identically in the two branches, then we will generate a new conflict. If the user is simply using standard conflict output then the results will be fairly sensible. But if the user is using a mergetool or using diff3 output, then the common ancestor will be a conflict file (itself with diff3 output, haha!). This is quite terrible, but it matches git's behavior. --- src/merge.c | 67 ++++++++++----- tests/merge/conflict_data.h | 31 +++++++ tests/merge/trees/recursive.c | 80 ++++++++++++++++++ tests/merge/workdir/recursive.c | 35 +++++++- .../15/311229e70fa62653f73dde1d4deef1a8e47a11 | Bin 0 -> 710 bytes .../37/185b25a204309bf74817da1a607518f13ca3ed | Bin 0 -> 715 bytes .../37/a5054a9f9b4628e3924c5cb8f2147c6e2a3efc | Bin 0 -> 630 bytes .../42/44d13e2bbc38510320443bbb003f3967d12436 | Bin 0 -> 207 bytes .../4f/4e85a0ab8515e34302721fbcec06fa9d9c1a9a | Bin 0 -> 631 bytes .../56/07a8c4601a737daadd1f470bde3142aff57026 | Bin 0 -> 206 bytes .../63/e8773becdea9c3699c95a5740be5baa8be8d69 | Bin 0 -> 207 bytes .../6c/778edd0e4cf394f5a3df8b96db516024cc1bb8 | Bin 0 -> 636 bytes .../6e/f31d35a3f5abc1e24f4f9afa5cb2016f03fa2d | Bin 0 -> 174 bytes .../7a/9277e0c5ec75339f011c176d0c20e513c4de1c | Bin 0 -> 202 bytes .../88/8588a782ad433fbf0cc526e07cfe6f4a6b60b3 | Bin 0 -> 208 bytes .../98/1c79eb38518d3821e73bb159dc413bb42d6614 | Bin 0 -> 208 bytes .../a0/2d4fd126e0cc8fb46ee48cf38bad36d44f2dbc | Bin 0 -> 649 bytes .../aa/9e263294fd2f6f6fd9ceab23ca8ce3ea2ce707 | Bin 0 -> 175 bytes .../b9/1ef5ffa8612616c8e76051901caafd723f0e2c | Bin 0 -> 712 bytes .../ca/fa936d25f0b397432a27201f6b3284c47df8be | Bin 0 -> 712 bytes .../d6/04c75019c282144bdbbf3fd3462ba74b240efc | Bin 0 -> 620 bytes .../db/203155a789fb749aa3c14e93eea2c744a9c6c7 | Bin 0 -> 204 bytes .../e1/512550f09d980214e46e6d3f5a2b20c3d75755 | Bin 0 -> 208 bytes .../f7/929c5a67a4bdc98247fb4b5098675723932a64 | Bin 0 -> 207 bytes .../.gitted/refs/heads/branchH-1 | Bin 0 -> 41 bytes .../.gitted/refs/heads/branchH-2 | Bin 0 -> 41 bytes .../.gitted/refs/heads/branchI-1 | Bin 0 -> 41 bytes .../.gitted/refs/heads/branchI-2 | Bin 0 -> 41 bytes 28 files changed, 193 insertions(+), 20 deletions(-) create mode 100644 tests/resources/merge-recursive/.gitted/objects/15/311229e70fa62653f73dde1d4deef1a8e47a11 create mode 100644 tests/resources/merge-recursive/.gitted/objects/37/185b25a204309bf74817da1a607518f13ca3ed create mode 100644 tests/resources/merge-recursive/.gitted/objects/37/a5054a9f9b4628e3924c5cb8f2147c6e2a3efc create mode 100644 tests/resources/merge-recursive/.gitted/objects/42/44d13e2bbc38510320443bbb003f3967d12436 create mode 100644 tests/resources/merge-recursive/.gitted/objects/4f/4e85a0ab8515e34302721fbcec06fa9d9c1a9a create mode 100644 tests/resources/merge-recursive/.gitted/objects/56/07a8c4601a737daadd1f470bde3142aff57026 create mode 100644 tests/resources/merge-recursive/.gitted/objects/63/e8773becdea9c3699c95a5740be5baa8be8d69 create mode 100644 tests/resources/merge-recursive/.gitted/objects/6c/778edd0e4cf394f5a3df8b96db516024cc1bb8 create mode 100644 tests/resources/merge-recursive/.gitted/objects/6e/f31d35a3f5abc1e24f4f9afa5cb2016f03fa2d create mode 100644 tests/resources/merge-recursive/.gitted/objects/7a/9277e0c5ec75339f011c176d0c20e513c4de1c create mode 100644 tests/resources/merge-recursive/.gitted/objects/88/8588a782ad433fbf0cc526e07cfe6f4a6b60b3 create mode 100644 tests/resources/merge-recursive/.gitted/objects/98/1c79eb38518d3821e73bb159dc413bb42d6614 create mode 100644 tests/resources/merge-recursive/.gitted/objects/a0/2d4fd126e0cc8fb46ee48cf38bad36d44f2dbc create mode 100644 tests/resources/merge-recursive/.gitted/objects/aa/9e263294fd2f6f6fd9ceab23ca8ce3ea2ce707 create mode 100644 tests/resources/merge-recursive/.gitted/objects/b9/1ef5ffa8612616c8e76051901caafd723f0e2c create mode 100644 tests/resources/merge-recursive/.gitted/objects/ca/fa936d25f0b397432a27201f6b3284c47df8be create mode 100644 tests/resources/merge-recursive/.gitted/objects/d6/04c75019c282144bdbbf3fd3462ba74b240efc create mode 100644 tests/resources/merge-recursive/.gitted/objects/db/203155a789fb749aa3c14e93eea2c744a9c6c7 create mode 100644 tests/resources/merge-recursive/.gitted/objects/e1/512550f09d980214e46e6d3f5a2b20c3d75755 create mode 100644 tests/resources/merge-recursive/.gitted/objects/f7/929c5a67a4bdc98247fb4b5098675723932a64 create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchH-1 create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchH-2 create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchI-1 create mode 100644 tests/resources/merge-recursive/.gitted/refs/heads/branchI-2 diff --git a/src/merge.c b/src/merge.c index 64c8f1116..f05e45c9f 100644 --- a/src/merge.c +++ b/src/merge.c @@ -49,6 +49,19 @@ #define GIT_MERGE_INDEX_ENTRY_EXISTS(X) ((X).mode != 0) #define GIT_MERGE_INDEX_ENTRY_ISFILE(X) S_ISREG((X).mode) + +/** Internal merge flags. */ +enum { + /** The merge is for a virtual base in a recursive merge. */ + GIT_MERGE__VIRTUAL_BASE = (1 << 31), +}; + +enum { + /** Accept the conflict file, staging it as the merge result. */ + GIT_MERGE_FILE_FAVOR__CONFLICTED = 4, +}; + + typedef enum { TREE_IDX_ANCESTOR = 0, TREE_IDX_OURS = 1, @@ -801,11 +814,9 @@ static int merge_conflict_resolve_automerge( int *resolved, git_merge_diff_list *diff_list, const git_merge_diff *conflict, - unsigned int merge_file_favor, - unsigned int file_flags) + const git_merge_file_options *file_opts) { const git_index_entry *ancestor = NULL, *ours = NULL, *theirs = NULL; - git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT; git_merge_file_result result = {0}; git_index_entry *index_entry; git_odb *odb = NULL; @@ -852,12 +863,9 @@ static int merge_conflict_resolve_automerge( theirs = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? &conflict->their_entry : NULL; - opts.favor = merge_file_favor; - opts.flags = file_flags; - if ((error = git_repository_odb(&odb, diff_list->repo)) < 0 || - (error = git_merge_file_from_index(&result, diff_list->repo, ancestor, ours, theirs, &opts)) < 0 || - !result.automergeable || + (error = git_merge_file_from_index(&result, diff_list->repo, ancestor, ours, theirs, file_opts)) < 0 || + (!result.automergeable && !(file_opts->flags & GIT_MERGE_FILE_FAVOR__CONFLICTED)) || (error = git_odb_write(&automerge_oid, odb, result.ptr, result.len, GIT_OBJ_BLOB)) < 0) goto done; @@ -887,8 +895,7 @@ static int merge_conflict_resolve( int *out, git_merge_diff_list *diff_list, const git_merge_diff *conflict, - unsigned int merge_file_favor, - unsigned int file_flags) + const git_merge_file_options *file_opts) { int resolved = 0; int error = 0; @@ -904,8 +911,7 @@ static int merge_conflict_resolve( if (!resolved && (error = merge_conflict_resolve_one_renamed(&resolved, diff_list, conflict)) < 0) goto done; - if (!resolved && (error = merge_conflict_resolve_automerge(&resolved, diff_list, conflict, - merge_file_favor, file_flags)) < 0) + if (!resolved && (error = merge_conflict_resolve_automerge(&resolved, diff_list, conflict, file_opts)) < 0) goto done; *out = resolved; @@ -1829,6 +1835,7 @@ int git_merge__iterators( *empty_theirs = NULL; git_merge_diff_list *diff_list; git_merge_options opts; + git_merge_file_options file_opts = GIT_MERGE_FILE_OPTIONS_INIT; git_merge_diff *conflict; git_vector changes; size_t i; @@ -1844,6 +1851,17 @@ int git_merge__iterators( if ((error = merge_normalize_opts(repo, &opts, given_opts)) < 0) return error; + file_opts.favor = opts.file_favor; + file_opts.flags = opts.file_flags; + + /* use the git-inspired labels when virtual base building */ + if (opts.flags & GIT_MERGE__VIRTUAL_BASE) { + file_opts.ancestor_label = "merged common ancestors"; + file_opts.our_label = "Temporary merge branch 1"; + file_opts.their_label = "Temporary merge branch 2"; + file_opts.flags |= GIT_MERGE_FILE_FAVOR__CONFLICTED; + } + diff_list = git_merge_diff_list__alloc(repo); GITERR_CHECK_ALLOC(diff_list); @@ -1862,7 +1880,8 @@ int git_merge__iterators( git_vector_foreach(&changes, i, conflict) { int resolved = 0; - if ((error = merge_conflict_resolve(&resolved, diff_list, conflict, opts.file_favor, opts.file_flags)) < 0) + if ((error = merge_conflict_resolve( + &resolved, diff_list, conflict, &file_opts)) < 0) goto done; if (!resolved) { @@ -1962,16 +1981,27 @@ static int create_virtual_base( git_repository *repo, git_annotated_commit *one, git_annotated_commit *two, + const git_merge_options *opts, size_t recursion_level) { git_annotated_commit *result = NULL; git_index *index = NULL; + git_merge_options virtual_opts = GIT_MERGE_OPTIONS_INIT; result = git__calloc(1, sizeof(git_annotated_commit)); GITERR_CHECK_ALLOC(result); + /* Conflicts in the merge base creation do not propagate to conflicts + * in the result; the conflicted base will act as the common ancestor. + */ + if (opts) + memcpy(&virtual_opts, opts, sizeof(git_merge_options)); + + virtual_opts.flags &= ~GIT_MERGE_FAIL_ON_CONFLICT; + virtual_opts.flags |= GIT_MERGE__VIRTUAL_BASE; + if ((merge_annotated_commits(&index, NULL, repo, one, two, - recursion_level + 1, NULL)) < 0) + recursion_level + 1, &virtual_opts)) < 0) return -1; result->type = GIT_ANNOTATED_COMMIT_VIRTUAL; @@ -1989,7 +2019,7 @@ static int compute_base( git_repository *repo, const git_annotated_commit *one, const git_annotated_commit *two, - bool recurse, + const git_merge_options *opts, size_t recursion_level) { git_array_oid_t head_ids = GIT_ARRAY_INIT; @@ -2007,7 +2037,7 @@ static int compute_base( if ((error = git_merge_bases_many(&bases, repo, head_ids.size, head_ids.ptr)) < 0 || (error = git_annotated_commit_lookup(&base, repo, &bases.ids[0])) < 0 || - !recurse) + (opts && (opts->flags & GIT_MERGE_NO_RECURSIVE))) goto done; for (i = 1; i < bases.count; i++) { @@ -2015,7 +2045,7 @@ static int compute_base( if ((error = git_annotated_commit_lookup(&other, repo, &bases.ids[i])) < 0 || - (error = create_virtual_base(&new_base, repo, base, other, + (error = create_virtual_base(&new_base, repo, base, other, opts, recursion_level)) < 0) goto done; @@ -2076,10 +2106,9 @@ static int merge_annotated_commits( { git_annotated_commit *base = NULL; git_iterator *base_iter = NULL, *our_iter = NULL, *their_iter = NULL; - bool recurse = !opts || !(opts->flags & GIT_MERGE_NO_RECURSIVE); int error; - if ((error = compute_base(&base, repo, ours, theirs, recurse, + if ((error = compute_base(&base, repo, ours, theirs, opts, recursion_level)) < 0) { if (error != GIT_ENOTFOUND) diff --git a/tests/merge/conflict_data.h b/tests/merge/conflict_data.h index b6c51332f..e6394a9e8 100644 --- a/tests/merge/conflict_data.h +++ b/tests/merge/conflict_data.h @@ -70,3 +70,34 @@ "This is a mighty fine recipe!\n" \ ">>>>>>> branchF-2\n" +#define CONFLICTING_RECURSIVE_H1_TO_H2_WITH_DIFF3 \ + "VEAL SOUP.\n" \ + "\n" \ + "<<<<<<< HEAD\n" \ + "put into a pot three quarts of water, three onions cut small, one\n" \ + "||||||| merged common ancestors\n" \ + "<<<<<<< Temporary merge branch 1\n" \ + "Put into a pot three quarts of water, THREE ONIONS CUT SMALL, one\n" \ + "||||||| merged common ancestors\n" \ + "Put into a pot three quarts of water, three onions cut small, one\n" \ + "=======\n" \ + "PUT INTO A POT three quarts of water, three onions cut small, one\n" \ + ">>>>>>> Temporary merge branch 2\n" \ + "=======\n" \ + "Put Into A Pot Three Quarts of Water, Three Onions Cut Small, One\n" \ + ">>>>>>> branchH-2\n" \ + "spoonful of black pepper pounded, and two of salt, with two or three\n" \ + "slices of lean ham; let it boil steadily two hours; skim it\n" \ + "occasionally, then put into it a shin of veal, let it boil two hours\n" \ + "longer; take out the slices of ham, and skim off the grease if any\n" \ + "should rise, take a gill of good cream, mix with it two table-spoonsful\n" \ + "of flour very nicely, and the yelks of two eggs beaten well, strain this\n" \ + "mixture, and add some chopped parsley; pour some soup on by degrees,\n" \ + "stir it well, and pour it into the pot, continuing to stir until it has\n" \ + "boiled two or three minutes to take off the raw taste of the eggs. If\n" \ + "the cream be not perfectly sweet, and the eggs quite new, the thickening\n" \ + "will curdle in the soup. For a change you may put a dozen ripe tomatos\n" \ + "in, first taking off their skins, by letting them stand a few minutes in\n" \ + "hot water, when they may be easily peeled. When made in this way you\n" \ + "must thicken it with the flour only. Any part of the veal may be used,\n" \ + "but the shin or knuckle is the nicest.\n" diff --git a/tests/merge/trees/recursive.c b/tests/merge/trees/recursive.c index 1e5f61391..693c91065 100644 --- a/tests/merge/trees/recursive.c +++ b/tests/merge/trees/recursive.c @@ -297,3 +297,83 @@ void test_merge_trees_recursive__oh_so_many_levels_of_recursion(void) git_index_free(index); } + +/* Branch H-1 and H-2 have two common ancestors (aa9e263, 6ef31d3). The two + * ancestors themselves conflict. + */ +void test_merge_trees_recursive__conflicting_merge_base(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "3a66812fed1e03ea4a6a7ee28d8a57aec1ca6537", 1, "veal.txt" }, + { 0100644, "d604c75019c282144bdbbf3fd3462ba74b240efc", 2, "veal.txt" }, + { 0100644, "37a5054a9f9b4628e3924c5cb8f2147c6e2a3efc", 3, "veal.txt" }, + }; + + cl_git_pass(merge_commits_from_branches(&index, repo, "branchH-1", "branchH-2", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 8)); + + git_index_free(index); +} + +/* Branch H-1 and H-2 have two common ancestors (aa9e263, 6ef31d3). The two + * ancestors themselves conflict. The generated common ancestor file will + * have diff3 style conflicts inside it. + */ +void test_merge_trees_recursive__conflicting_merge_base_with_diff3(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "cd17a91513f3aee9e44114d1ede67932dd41d2fc", 1, "veal.txt" }, + { 0100644, "d604c75019c282144bdbbf3fd3462ba74b240efc", 2, "veal.txt" }, + { 0100644, "37a5054a9f9b4628e3924c5cb8f2147c6e2a3efc", 3, "veal.txt" }, + }; + + opts.file_flags |= GIT_MERGE_FILE_STYLE_DIFF3; + + cl_git_pass(merge_commits_from_branches(&index, repo, "branchH-1", "branchH-2", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 8)); + + git_index_free(index); +} + +/* Branch I-1 and I-2 have two common ancestors (aa9e263, 6ef31d3). The two + * ancestors themselves conflict, but when each was merged, the conflicts were + * resolved identically, thus merging I-1 into I-2 does not conflict. + */ +void test_merge_trees_recursive__conflicting_merge_base_since_resolved(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "a02d4fd126e0cc8fb46ee48cf38bad36d44f2dbc", 0, "veal.txt" }, + }; + + cl_git_pass(merge_commits_from_branches(&index, repo, "branchI-1", "branchI-2", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 6)); + + git_index_free(index); +} diff --git a/tests/merge/workdir/recursive.c b/tests/merge/workdir/recursive.c index a7326009a..795126255 100644 --- a/tests/merge/workdir/recursive.c +++ b/tests/merge/workdir/recursive.c @@ -36,7 +36,6 @@ void test_merge_workdir_recursive__writes_conflict_with_virtual_base(void) { 0100644, "3855170cef875708da06ab9ad7fc6a73b531cda1", 3, "veal.txt" }, }; - cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR "branchF-1", GIT_REFS_HEADS_DIR "branchF-2", &opts, NULL)); cl_git_pass(git_repository_index(&index, repo)); @@ -49,3 +48,37 @@ void test_merge_workdir_recursive__writes_conflict_with_virtual_base(void) git_index_free(index); git_buf_free(&conflicting_buf); } + +void test_merge_workdir_recursive__conflicting_merge_base_with_diff3(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + git_buf conflicting_buf = GIT_BUF_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "cd17a91513f3aee9e44114d1ede67932dd41d2fc", 1, "veal.txt" }, + { 0100644, "d604c75019c282144bdbbf3fd3462ba74b240efc", 2, "veal.txt" }, + { 0100644, "37a5054a9f9b4628e3924c5cb8f2147c6e2a3efc", 3, "veal.txt" }, + }; + + opts.file_flags |= GIT_MERGE_FILE_STYLE_DIFF3; + checkout_opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_DIFF3; + + cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR "branchH-1", GIT_REFS_HEADS_DIR "branchH-2", &opts, &checkout_opts)); + + cl_git_pass(git_repository_index(&index, repo)); + cl_assert(merge_test_index(index, merge_index_entries, 8)); + + cl_git_pass(git_futils_readbuffer(&conflicting_buf, "merge-recursive/veal.txt")); + + cl_assert_equal_s(CONFLICTING_RECURSIVE_H1_TO_H2_WITH_DIFF3, conflicting_buf.ptr); + + git_index_free(index); + git_buf_free(&conflicting_buf); +} diff --git a/tests/resources/merge-recursive/.gitted/objects/15/311229e70fa62653f73dde1d4deef1a8e47a11 b/tests/resources/merge-recursive/.gitted/objects/15/311229e70fa62653f73dde1d4deef1a8e47a11 new file mode 100644 index 0000000000000000000000000000000000000000..8c21bb357b2e5b67ef00d764c0b31886e4b19acb GIT binary patch literal 710 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 RCnSYtwvk%S?hhNSg)?_+K#TwY literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/56/07a8c4601a737daadd1f470bde3142aff57026 b/tests/resources/merge-recursive/.gitted/objects/56/07a8c4601a737daadd1f470bde3142aff57026 new file mode 100644 index 0000000000000000000000000000000000000000..bf3639d054dc4d200f0af55ac962784c6a7b02bc GIT binary patch literal 206 zcmV;<05Sh~0i};kN(C_xgk8@m`T`^A&ObcFjf=N%=LI^Qj`J2Xk-Ui4cLY6w)TZjA zC@AUsew$!Tc$h;Kazcq&i%lL=wTV!pEd_n9;_!5iY6$3-4<33H8C)b1_GXA;D_AG0REMV^jYzvApVdS15xI+2Fbo+>Y6Q^L54L I8zXXJRt8>Zb^rhX literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/63/e8773becdea9c3699c95a5740be5baa8be8d69 b/tests/resources/merge-recursive/.gitted/objects/63/e8773becdea9c3699c95a5740be5baa8be8d69 new file mode 100644 index 0000000000000000000000000000000000000000..6d5c320fe3eafb23ef1b8056324cabfef9ee5260 GIT binary patch literal 207 zcmV;=05Jb}0V^p=O;s>5GG{O}FfcPQQAjKKwxdU$y}81q;%xsW4ST4JU=CT4mqHf5nW&}uWMhMvEC zyS=4{`@4txC;IgAOiy2LPN#-qU?WS6VJKd&x88OWExat8fLB7#eM2_%l-H;i#QNOO z>hidsqepG` z&~jCUc}p_7pv!-<_04~<%BrFnAH$i`k+PkbBF2G3|5$<0uA?rE!%+X96I)?&10-%n zfaH5hP8c*4$I{vPYRoyto;q9y%Qupr*V)mE(HOC{_TZ8_ zoCqZ&b`0#kUwln=#Q`$c)O3UDJkZ@>^rPAc)f7-z6m8%xqZzU?gU8jUYRzkL013R- zUZ^YG!~q=5s=;(6^&a`DUq~D35A-=EMI$Gc^qwc>Sy21v5g+<*}{WVBFp6YUo7CwE7D( z96Znu{Xg6OLfi=kH>GOkqC|F)HM9E8AZ_nc41PP%O#l?$_tjf{71vEg@Lo-Ot$%$t WiKbAxNqs_60MkZFKA1nlLWC2#0zgav literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/6e/f31d35a3f5abc1e24f4f9afa5cb2016f03fa2d b/tests/resources/merge-recursive/.gitted/objects/6e/f31d35a3f5abc1e24f4f9afa5cb2016f03fa2d new file mode 100644 index 0000000000000000000000000000000000000000..e95a5e2dbbd6dc272ecd7ede4fd4f4ea377a0919 GIT binary patch literal 174 zcmV;f08#&V0i};YP6Z(l1zmHBzJR7-Xc`h@-25AO0iYZIBr+HV_4OiW}x^VDMjS1v7*CXUUDjTI?`#^}V= z!W=?LHHvR0f35qNaFd@r1>WwXpW}dMz3d;o_Px$=j<%la=r3Ts=frN?EIcA1n#*28 cm;NJRZgO4y*8Bnc0bS?2LdmC^Un=cTR;<=k5&!@I literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/7a/9277e0c5ec75339f011c176d0c20e513c4de1c b/tests/resources/merge-recursive/.gitted/objects/7a/9277e0c5ec75339f011c176d0c20e513c4de1c new file mode 100644 index 0000000000000000000000000000000000000000..9fb34f7eedd4a615198879a383fff0484b21e1a2 GIT binary patch literal 202 zcmV;*05$)30i{ntPDLRQbNx=i1-^lq0X~T_ZcN;`^8&*#_}+qt;bXkMxX=rjZrY}4 zQ|YZ8)1dWtbEp~uqT)~?XThs?7Qqr>&e>Y5m_jTL2*dWlL(gfLlR`j9R14IwP?9KT zK;f$>DuZaW@i!l|qATbbtLH3EfhyI+mAz!JF4Q%{P&WRYdmrYxeDNseZSU>YPxGM5 z{b5h-kkN0w&U@+Y(Kw=*B2e_^V!gH7a@Vn`|ESnrb-b&|gHPFCE?|yx_CM*iZ!lP4 Eqh?`iumAu6 literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/88/8588a782ad433fbf0cc526e07cfe6f4a6b60b3 b/tests/resources/merge-recursive/.gitted/objects/88/8588a782ad433fbf0cc526e07cfe6f4a6b60b3 new file mode 100644 index 0000000000000000000000000000000000000000..44efd3315b4326a3f381c0ce6633451ce0b1ef91 GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjK05AV|0V^p=O;s>5GG{O}FfcPQQAjKEU!e(}AAOSGsFAq@=c?o z@lIR2R={INbH=DkUgkB$!8n2b$=X)`!3wK_rmS~8hdm`dGI@*xhyJkwp-o3w>btId z?*r?|WI9M(RRof4DVWHhAvl_PV~a7p_boNJ4w%XO+8h8mA&_)!`E9W=!bb(BorOg3 z4J$bqBf5%-;Iy(Gi%~MleIKc2ouGMP3w1FC4Yg@7QDITa0c2!Ww=G3KF*SonJT1*S zM9b@5athn0=<`h8QN2>j5XZ4nF_|DXZt#Ls?&G8wEH=#^nKs@fr;=&9b zcb|$iKe7RcRS0W8c16-eS26m%ajNX&p z+CITYe3(NOm^m_MVxMbRS1X1h(I-U`(_|s1lCZ4qGY>r`kWev>x)Iw_7Z!{u8KJLU z3Qt+Awwe2$c*=;^aZ!fTfKpXiLH+aN|=q`H= do%)Z2yVdLJx8@Jn4`@2x6-qwU{Q?TRQDDTZQfvSK literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/b9/1ef5ffa8612616c8e76051901caafd723f0e2c b/tests/resources/merge-recursive/.gitted/objects/b9/1ef5ffa8612616c8e76051901caafd723f0e2c new file mode 100644 index 0000000000000000000000000000000000000000..e19652394fafa04eeadf6101eab837e67cb1c51c GIT binary patch literal 712 zcmV;(0yq750gY76j?*v@=Ip1K2MCo|aak3RDk$2OmTko@aCVbPW7V;<9)}Rb!}E>P z0v8ZIwYF#G`*RLH9_Yo@^~-0Ux0^e9*nQfsjCr*zy4h~tnf;i_g&fJ!5Hsa2G1Jp% zQx=LXO*V6?=<(*`c1yea+ui*Gz5Vn^4<9yncNN9J=Et(A=XB(T>e%-&kPS6Uj!FJ= z!ZJ7p7lTj@P|{oPmkFW6{9v{YRSRbbtnOT{yeRq_OJQS&u^q;^Fzm zX@Micr`Gn&e1Fct#{*rxdVTZa%VvE~kK3o+(wH~%qT9{-gV{ap>F!~_r8Vugd&+HM zrsu(?EEF3WZRWH%Z^z(b5UPeu(pm2p6a$-|^P-N^k!z}A*Tq0KR4h3rF*{s$AxE;* z$NWDN-p=bEJMC{jZ#J}jxZ6HF(tFT*{ItHmpRvB17u|4keN|r@>0=Dd;FapZ+v-F; z_dO>d9YW3Z0z7KUW7G>`eO}P$ay##&nTrwcz*TQ}wt-sPt?&z^GaaJyB$;jP{8Xyi zI3!t-oLq+sCRUXds0uYFm6-$e7rjt#Nm>`Q^*6TA`|q}js&q3xh9jpHWqV?ZC`TXq zg9?CV9%X23nsRTMFw(@K3x^O3}%M;_@gQOzL7vM`ceQXA&>+Ns>fP6?#Sgh(!s*^pfr zTsRVPTB-)@c&%-%7_|{oOAgMiLy0hC*p7l(_p`3aj@W?b>Y7eao=duGjQ%Myf;9y^ z1q7|(DkB*(G6Tm&ry|Ys;Ls%SSaPANR3{F=Xht=r8dB}ypXvp-!G1{}V}fs0Ymo<< z;y|69N}ep$@dtEHu4f?bY>r}FSWx4Vz)5>j?&oMgGbAO*LbXKVAPsbBxq~vr4QhD& z&EEyng3I|sF=~QwcPgX76Cu;+_smdmNniE6v-KIc6BJGg)pUabnMoGU$~yzKIZrY8 uX-VsVR`@nuwAEK}nKTIAt2tcaU*1ikQy8j~>IA3IOcN=2X?_9!X2$$2(9 literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/d6/04c75019c282144bdbbf3fd3462ba74b240efc b/tests/resources/merge-recursive/.gitted/objects/d6/04c75019c282144bdbbf3fd3462ba74b240efc new file mode 100644 index 0000000000000000000000000000000000000000..059fcfe72f6598e0a631ed1165559606dacc47cc GIT binary patch literal 620 zcmV-y0+aoC0Zmi8uGBCPrS@0M2ZR+A6lkkkfP{nu5(4T;GTB&h>|Bpc2w%?`XZJ!G z$8+Yq+=tZD+c)puz54Ot=`(%%^8M?@xvduRu_R*JQlTh&?eg^8F)30GIqpqq_QWwpd3m^yD#&3l22%-!4J|l z2H{9EuU85JYK8jb18ET+eK^|GoNCr9>Ec&haA_D=!3>NJqbiYJRAHV;XCH0(FJW;0 zAJ$kkG#665Nxo9yMJPcbaOfW^5IS|VrD>Yl|63MTAwNOl;S@+=r0i8d!*G~xJ~U%) zDUCGXI$&1+`*Z;0j6mT&$m?dK!bbno%|?x0_Ng2r1Go zmWYy}M%p-QkmG7O<@1-Ox*=2Q4;lsHL_4R&XN%#4 zH>7qSC#^PS6N=N(kYe$%`gkL>sZCX!1e={%oyCZ)tq1qi;Y6$jv18!${qAe#9S6wV zQnL-F^Fp5{XCKW*sHTX*qG*#01)})ML^+wKfJY9@z_)q@748>`nPwJX^C}M%qJuTFl(fii~9@Y GSca3)DKNPJ literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/db/203155a789fb749aa3c14e93eea2c744a9c6c7 b/tests/resources/merge-recursive/.gitted/objects/db/203155a789fb749aa3c14e93eea2c744a9c6c7 new file mode 100644 index 0000000000000000000000000000000000000000..e9f7fd8fd7656e004a2ba597912eaf83108fe564 GIT binary patch literal 204 zcmV;-05ku10i};kN(C_xgk9$pxxkYoonH`f^T3TeFVN|9JG00{^78Qdjtjkj)TZjA zC@31e?@NOT4{I{(2%J-b%G4MOK`ezzz>vJD94cb2oLemF6DJ=_M;5RR0)hmrWZ8M~ zQcy5Zb;d?9An4j}AF;5D;EI>#B%W%87^?%qObm>UIGLpIzP2&-wSD1i`h6SyHV*y7 zC;f3ReWy8YL)IgW{;aL{sU+uZ))y0uQ71i*E&j)ZdgJ-Qx=uW3d%ak_A8Y(Yr@jFK GTw_|HHDY)G literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/objects/e1/512550f09d980214e46e6d3f5a2b20c3d75755 b/tests/resources/merge-recursive/.gitted/objects/e1/512550f09d980214e46e6d3f5a2b20c3d75755 new file mode 100644 index 0000000000000000000000000000000000000000..a5f506fb30b11082e62870bbc305d849e835ca39 GIT binary patch literal 208 zcmV;>05AV|0V^p=O;s>5GG{O}FfcPQQAjK%)+xKmNHQ{M=RY(g;Oq%WDaVd5o)M#@?pNcpbslTxshTrE_?sgguFlNDY>tul#56DR%}YwK_=5AFrt*S6o= z0T(^#k9*xW>FwU?c$T)m06G^_q=*4#v78DTp{`^~qR Jrf(5uVo0VDWGVmv literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchH-1 b/tests/resources/merge-recursive/.gitted/refs/heads/branchH-1 new file mode 100644 index 0000000000000000000000000000000000000000..ffe9f8cf35a13fc79489c4e6ea51fd88579a45ba GIT binary patch literal 41 ucmV~$!4Uu;2m`Rc(@+q5jN=vDe*_cWa&k;LVn)<6K;UVU+ayA}2U;KEJPN=7 literal 0 HcmV?d00001 diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchH-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchH-2 new file mode 100644 index 0000000000000000000000000000000000000000..84ed1a2a91c23935402e15ebcb40c3340dfa3261 GIT binary patch literal 41 ucmV~$K>+|D2m`>sX%vP?oPpRsf_FD%>N2CBcVF?#sMn!AEEYw!tn}}^&6%bwypg#0}fHDRM`3G8VXqe literal 0 HcmV?d00001 From 5b9c63c3f673cbc209e53627be2a0e87c17ccb3c Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 20 Nov 2015 19:01:42 -0500 Subject: [PATCH 289/450] recursive merge: add a recursion limit --- include/git2/merge.h | 8 ++++++++ src/merge.c | 13 ++++++++++--- tests/merge/trees/recursive.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/include/git2/merge.h b/include/git2/merge.h index a272e8be4..af53ead22 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -265,6 +265,14 @@ typedef struct { /** Pluggable similarity metric; pass NULL to use internal metric */ git_diff_similarity_metric *metric; + /** + * Maximum number of times to merge common ancestors to build a + * virtual merge base when faced with criss-cross merges. When this + * limit is reached, the next ancestor will simply be used instead of + * attempting to merge it. The default is unlimited. + */ + unsigned int recursion_limit; + /** Flags for handling conflicting content. */ git_merge_file_favor_t file_favor; diff --git a/src/merge.c b/src/merge.c index f05e45c9f..9eb3b0904 100644 --- a/src/merge.c +++ b/src/merge.c @@ -2019,17 +2019,21 @@ static int compute_base( git_repository *repo, const git_annotated_commit *one, const git_annotated_commit *two, - const git_merge_options *opts, + const git_merge_options *given_opts, size_t recursion_level) { git_array_oid_t head_ids = GIT_ARRAY_INIT; git_oidarray bases = {0}; git_annotated_commit *base = NULL, *other = NULL, *new_base = NULL; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; size_t i; int error; *out = NULL; + if (given_opts) + memcpy(&opts, given_opts, sizeof(git_merge_options)); + if ((error = insert_head_ids(&head_ids, one)) < 0 || (error = insert_head_ids(&head_ids, two)) < 0) goto done; @@ -2037,15 +2041,18 @@ static int compute_base( if ((error = git_merge_bases_many(&bases, repo, head_ids.size, head_ids.ptr)) < 0 || (error = git_annotated_commit_lookup(&base, repo, &bases.ids[0])) < 0 || - (opts && (opts->flags & GIT_MERGE_NO_RECURSIVE))) + (opts.flags & GIT_MERGE_NO_RECURSIVE)) goto done; for (i = 1; i < bases.count; i++) { recursion_level++; + if (opts.recursion_limit && recursion_level > opts.recursion_limit) + break; + if ((error = git_annotated_commit_lookup(&other, repo, &bases.ids[i])) < 0 || - (error = create_virtual_base(&new_base, repo, base, other, opts, + (error = create_virtual_base(&new_base, repo, base, other, &opts, recursion_level)) < 0) goto done; diff --git a/tests/merge/trees/recursive.c b/tests/merge/trees/recursive.c index 693c91065..c5b129bf8 100644 --- a/tests/merge/trees/recursive.c +++ b/tests/merge/trees/recursive.c @@ -377,3 +377,34 @@ void test_merge_trees_recursive__conflicting_merge_base_since_resolved(void) git_index_free(index); } + +/* There are multiple levels of criss-cross merges, and multiple recursive + * merges would create a common ancestor that allows the merge to complete + * successfully. Test that we can build a single virtual base, then stop, + * which will produce a conflicting merge. + */ +void test_merge_trees_recursive__recursionlimit(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" }, + { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" }, + { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" }, + { 0100644, "ce7e553c6feb6e5f3bd67e3c3be04182fe3094b4", 1, "gravy.txt" }, + { 0100644, "d8dd349b78f19a4ebe3357bacb8138f00bf5ed41", 2, "gravy.txt" }, + { 0100644, "e50fbbd701458757bdfe9815f58ed717c588d1b5", 3, "gravy.txt" }, + { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" }, + { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" }, + }; + + opts.recursion_limit = 1; + + cl_git_pass(merge_commits_from_branches(&index, repo, "branchE-1", "branchE-2", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 8)); + + git_index_free(index); +} + From 7132150ddf7a883c1f12a89c2518c1a07c0dc94c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 14 Nov 2015 23:46:21 +0100 Subject: [PATCH 290/450] tree: avoid advancing over the filename multiple times We've already looked at the filename with `memchr()` and then used `strlen()` to allocate the entry. We already know how much we have to advance to get to the object id, so add the filename length instead of looking at each byte again. --- src/tree.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/tree.c b/src/tree.c index bdd17661b..3504452c2 100644 --- a/src/tree.c +++ b/src/tree.c @@ -414,10 +414,8 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj) entry->attr = attr; } - while (buffer < buffer_end && *buffer != 0) - buffer++; - - buffer++; + /* Advance to the ID just after the path */ + buffer += entry->filename_len + 1; git_oid_fromraw(&entry->oid, (const unsigned char *)buffer); buffer += GIT_OID_RAWSZ; From ed970748b600313306657de6e5447e5447790766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 14 Nov 2015 23:50:06 +0100 Subject: [PATCH 291/450] tree: pool the entry memory allocations These are rather small allocations, so we end up spending a non-trivial amount of time asking the OS for memory. Since these entries are tied to the lifetime of their tree, we can give the tree a pool so we speed up the allocations. --- src/tree.c | 34 +++++++++++++++++++++++++++++----- src/tree.h | 5 ++++- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/tree.c b/src/tree.c index 3504452c2..22505d23a 100644 --- a/src/tree.c +++ b/src/tree.c @@ -81,6 +81,30 @@ int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2) git__strncasecmp); } +/** + * Allocate a tree entry, borrowing the filename from the tree which + * owns it. This is useful when reading trees, so we don't allocate a + * ton of small strings but can use the pool. + */ +static git_tree_entry *alloc_entry_pooled(git_pool *pool, const char *filename) +{ + git_tree_entry *entry = NULL; + size_t filename_len = strlen(filename), tree_len; + + if (GIT_ADD_SIZET_OVERFLOW(&tree_len, sizeof(git_tree_entry), filename_len) || + GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1) || + !(entry = git_pool_malloc(pool, tree_len))) + return NULL; + + memset(entry, 0x0, sizeof(git_tree_entry)); + memcpy(entry->filename, filename, filename_len); + entry->filename[filename_len] = 0; + entry->filename_len = filename_len; + entry->pooled = true; + + return entry; +} + static git_tree_entry *alloc_entry(const char *filename) { git_tree_entry *entry = NULL; @@ -198,7 +222,7 @@ static int tree_key_search( void git_tree_entry_free(git_tree_entry *entry) { - if (entry == NULL) + if (entry == NULL || entry->pooled) return; git__free(entry); @@ -233,6 +257,7 @@ void git_tree__free(void *_tree) git_tree_entry_free(e); git_vector_free(&tree->entries); + git_pool_clear(&tree->pool); git__free(tree); } @@ -385,6 +410,7 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj) const char *buffer = git_odb_object_data(odb_obj); const char *buffer_end = buffer + git_odb_object_size(odb_obj); + git_pool_init(&tree->pool, 1); if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < 0) return -1; @@ -403,13 +429,11 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj) /** Allocate the entry and store it in the entries vector */ { - entry = alloc_entry(buffer); + entry = alloc_entry_pooled(&tree->pool, buffer); GITERR_CHECK_ALLOC(entry); - if (git_vector_insert(&tree->entries, entry) < 0) { - git__free(entry); + if (git_vector_insert(&tree->entries, entry) < 0) return -1; - } entry->attr = attr; } diff --git a/src/tree.h b/src/tree.h index d01b6fd41..51f42ca32 100644 --- a/src/tree.h +++ b/src/tree.h @@ -12,17 +12,20 @@ #include "odb.h" #include "vector.h" #include "strmap.h" +#include "pool.h" struct git_tree_entry { uint16_t attr; git_oid oid; + bool pooled; size_t filename_len; - char filename[1]; + char filename[GIT_FLEX_ARRAY]; }; struct git_tree { git_object object; git_vector entries; + git_pool pool; }; struct git_treebuilder { From 2580077fc2cbb124409bbc6453f2923217ac7957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 15 Nov 2015 00:44:02 +0100 Subject: [PATCH 292/450] tree: calculate the filename length once We already know the size due to the `memchr()` so use that information instead of calling `strlen()` on it. --- src/tree.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/tree.c b/src/tree.c index 22505d23a..ff0464f1b 100644 --- a/src/tree.c +++ b/src/tree.c @@ -86,10 +86,10 @@ int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2) * owns it. This is useful when reading trees, so we don't allocate a * ton of small strings but can use the pool. */ -static git_tree_entry *alloc_entry_pooled(git_pool *pool, const char *filename) +static git_tree_entry *alloc_entry_pooled(git_pool *pool, const char *filename, size_t filename_len) { git_tree_entry *entry = NULL; - size_t filename_len = strlen(filename), tree_len; + size_t tree_len; if (GIT_ADD_SIZET_OVERFLOW(&tree_len, sizeof(git_tree_entry), filename_len) || GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1) || @@ -416,6 +416,8 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj) while (buffer < buffer_end) { git_tree_entry *entry; + size_t filename_len; + const char *nul; int attr; if (git__strtol32(&attr, buffer, &buffer, 8) < 0 || !buffer) @@ -424,12 +426,13 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj) if (*buffer++ != ' ') return tree_error("Failed to parse tree. Object is corrupted", NULL); - if (memchr(buffer, 0, buffer_end - buffer) == NULL) + if ((nul = memchr(buffer, 0, buffer_end - buffer)) == NULL) return tree_error("Failed to parse tree. Object is corrupted", NULL); + filename_len = nul - buffer; /** Allocate the entry and store it in the entries vector */ { - entry = alloc_entry_pooled(&tree->pool, buffer); + entry = alloc_entry_pooled(&tree->pool, buffer, filename_len); GITERR_CHECK_ALLOC(entry); if (git_vector_insert(&tree->entries, entry) < 0) @@ -439,7 +442,7 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj) } /* Advance to the ID just after the path */ - buffer += entry->filename_len + 1; + buffer += filename_len + 1; git_oid_fromraw(&entry->oid, (const unsigned char *)buffer); buffer += GIT_OID_RAWSZ; From ee42bb0e3d6534b8ac4d48df90b1bb85323972ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 28 Nov 2015 19:18:29 +0100 Subject: [PATCH 293/450] tree: make path len uint16_t and avoid holes This reduces the size of the struct from 32 to 26 bytes, and leaves a single padding byte at the end of the struct (which comes from the zero-length array). --- src/tree.c | 2 +- src/tree.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tree.c b/src/tree.c index ff0464f1b..d269a5333 100644 --- a/src/tree.c +++ b/src/tree.c @@ -125,7 +125,7 @@ static git_tree_entry *alloc_entry(const char *filename) struct tree_key_search { const char *filename; - size_t filename_len; + uint16_t filename_len; }; static int homing_search_cmp(const void *key, const void *array_member) diff --git a/src/tree.h b/src/tree.h index 51f42ca32..914d788c8 100644 --- a/src/tree.h +++ b/src/tree.h @@ -16,9 +16,9 @@ struct git_tree_entry { uint16_t attr; + uint16_t filename_len; git_oid oid; bool pooled; - size_t filename_len; char filename[GIT_FLEX_ARRAY]; }; From 95ae3520c5c9f76a435f63cc2d5e18d7ba0ba171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 30 Nov 2015 17:32:18 +0100 Subject: [PATCH 294/450] tree: ensure the entry filename fits in 16 bits Return an error in case the length is too big. Also take this opportunity to have a single allocating function for the size and overflow logic. --- src/tree.c | 50 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/tree.c b/src/tree.c index d269a5333..0a32868cd 100644 --- a/src/tree.c +++ b/src/tree.c @@ -82,24 +82,47 @@ int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2) } /** - * Allocate a tree entry, borrowing the filename from the tree which - * owns it. This is useful when reading trees, so we don't allocate a - * ton of small strings but can use the pool. + * Allocate either from the pool or from the system allocator */ -static git_tree_entry *alloc_entry_pooled(git_pool *pool, const char *filename, size_t filename_len) +static git_tree_entry *alloc_entry_base(git_pool *pool, const char *filename, size_t filename_len) { git_tree_entry *entry = NULL; size_t tree_len; + if (filename_len > UINT16_MAX) { + giterr_set(GITERR_INVALID, "tree entry is over UINT16_MAX in length"); + return NULL; + } + if (GIT_ADD_SIZET_OVERFLOW(&tree_len, sizeof(git_tree_entry), filename_len) || - GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1) || - !(entry = git_pool_malloc(pool, tree_len))) + GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1)) + return NULL; + + entry = pool ? git_pool_malloc(pool, tree_len) : + git__malloc(tree_len); + if (!entry) return NULL; memset(entry, 0x0, sizeof(git_tree_entry)); memcpy(entry->filename, filename, filename_len); entry->filename[filename_len] = 0; entry->filename_len = filename_len; + + return entry; +} + +/** + * Allocate a tree entry, using the poolin the tree which owns + * it. This is useful when reading trees, so we don't allocate a ton + * of small strings but can use the pool. + */ +static git_tree_entry *alloc_entry_pooled(git_pool *pool, const char *filename, size_t filename_len) +{ + git_tree_entry *entry = NULL; + + if (!(entry = alloc_entry_base(pool, filename, filename_len))) + return NULL; + entry->pooled = true; return entry; @@ -107,20 +130,7 @@ static git_tree_entry *alloc_entry_pooled(git_pool *pool, const char *filename, static git_tree_entry *alloc_entry(const char *filename) { - git_tree_entry *entry = NULL; - size_t filename_len = strlen(filename), tree_len; - - if (GIT_ADD_SIZET_OVERFLOW(&tree_len, sizeof(git_tree_entry), filename_len) || - GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1) || - !(entry = git__malloc(tree_len))) - return NULL; - - memset(entry, 0x0, sizeof(git_tree_entry)); - memcpy(entry->filename, filename, filename_len); - entry->filename[filename_len] = 0; - entry->filename_len = filename_len; - - return entry; + return alloc_entry_base(NULL, filename, strlen(filename)); } struct tree_key_search { From a7bd157ebfcfd517b65e0ddc017286f61bc8b218 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 30 Nov 2015 17:40:49 +0100 Subject: [PATCH 295/450] tests: fix warning for nested struct initialization --- tests/index/racy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/index/racy.c b/tests/index/racy.c index 862c5ab7e..a24d47a10 100644 --- a/tests/index/racy.c +++ b/tests/index/racy.c @@ -178,7 +178,7 @@ static void setup_uptodate_files(void) { git_buf path = GIT_BUF_INIT; git_index *index; - git_index_entry new_entry = {0}; + git_index_entry new_entry = {{0}}; cl_git_pass(git_repository_index(&index, g_repo)); From bbd2fa4e70d3b1193c447db81ff01cf54661edfa Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 30 Nov 2015 18:05:27 +0100 Subject: [PATCH 296/450] object: remove unused constant OBJECT_BASE_SIZE --- src/object.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/object.c b/src/object.c index 1073559fd..b0a8199bc 100644 --- a/src/object.c +++ b/src/object.c @@ -14,8 +14,6 @@ #include "blob.h" #include "tag.h" -static const int OBJECT_BASE_SIZE = 4096; - typedef struct { const char *str; /* type name string */ size_t size; /* size in bytes of the object structure */ From cb1cb24ca9588e43edfb8b37e008c2e83af580af Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 24 Nov 2015 10:18:58 +0100 Subject: [PATCH 297/450] blame: use size_t for line counts in git_blame_hunk It is not unreasonable to have versioned files with a line count exceeding 2^16. Upon blaming such files we fail to correctly keep track of the lines as `git_blame_hunk` stores them in `uint16_t` fields. Fix this by converting the line fields of `git_blame_hunk` to `size_t`. Add test to verify behavior. --- include/git2/blame.h | 12 ++++----- src/blame.c | 24 +++++++++--------- tests/blame/blame_helpers.c | 4 +-- tests/blame/blame_helpers.h | 6 ++--- tests/blame/simple.c | 12 +++++++++ .../37/681a80ca21064efd5c3bf2ef41eb3d05a1428b | Bin 0 -> 106 bytes .../4e/ecfea484f8005d101e547f6bfb07c99e2b114e | Bin 0 -> 163 bytes .../5a/572e2e94825f54b95417eacaa089d560c5a5e9 | Bin 0 -> 324 bytes .../66/53ff42313eb5c82806f145391b18a9699800c7 | Bin 0 -> 160 bytes .../ad/9cb4eac23df2fe5e1264287a5872ea2a1ff8b2 | Bin 0 -> 106 bytes .../de/9fe35f9906e1994e083cc59c87232bf418795b | Bin 0 -> 331 bytes .../resources/blametest.git/refs/heads/master | Bin 41 -> 41 bytes 12 files changed, 34 insertions(+), 24 deletions(-) create mode 100644 tests/resources/blametest.git/objects/37/681a80ca21064efd5c3bf2ef41eb3d05a1428b create mode 100644 tests/resources/blametest.git/objects/4e/ecfea484f8005d101e547f6bfb07c99e2b114e create mode 100644 tests/resources/blametest.git/objects/5a/572e2e94825f54b95417eacaa089d560c5a5e9 create mode 100644 tests/resources/blametest.git/objects/66/53ff42313eb5c82806f145391b18a9699800c7 create mode 100644 tests/resources/blametest.git/objects/ad/9cb4eac23df2fe5e1264287a5872ea2a1ff8b2 create mode 100644 tests/resources/blametest.git/objects/de/9fe35f9906e1994e083cc59c87232bf418795b diff --git a/include/git2/blame.h b/include/git2/blame.h index 173e9994b..84bb7f94c 100644 --- a/include/git2/blame.h +++ b/include/git2/blame.h @@ -74,8 +74,8 @@ typedef struct git_blame_options { uint16_t min_match_characters; git_oid newest_commit; git_oid oldest_commit; - uint32_t min_line; - uint32_t max_line; + size_t min_line; + size_t max_line; } git_blame_options; #define GIT_BLAME_OPTIONS_VERSION 1 @@ -113,15 +113,15 @@ GIT_EXTERN(int) git_blame_init_options( * root, or the commit specified in git_blame_options.oldest_commit) */ typedef struct git_blame_hunk { - uint16_t lines_in_hunk; + size_t lines_in_hunk; git_oid final_commit_id; - uint16_t final_start_line_number; + size_t final_start_line_number; git_signature *final_signature; git_oid orig_commit_id; const char *orig_path; - uint16_t orig_start_line_number; + size_t orig_start_line_number; git_signature *orig_signature; char boundary; @@ -156,7 +156,7 @@ GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byindex( */ GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byline( git_blame *blame, - uint32_t lineno); + size_t lineno); /** * Get the blame for a single file. diff --git a/src/blame.c b/src/blame.c index 08a90dcfd..2daf91591 100644 --- a/src/blame.c +++ b/src/blame.c @@ -23,8 +23,8 @@ static int hunk_byfinalline_search_cmp(const void *key, const void *entry) git_blame_hunk *hunk = (git_blame_hunk*)entry; size_t lineno = *(size_t*)key; - size_t lines_in_hunk = (size_t)hunk->lines_in_hunk; - size_t final_start_line_number = (size_t)hunk->final_start_line_number; + size_t lines_in_hunk = hunk->lines_in_hunk; + size_t final_start_line_number = hunk->final_start_line_number; if (lineno < final_start_line_number) return -1; @@ -44,7 +44,7 @@ static int hunk_cmp(const void *_a, const void *_b) static bool hunk_ends_at_or_before_line(git_blame_hunk *hunk, size_t line) { - return line >= (size_t)(hunk->final_start_line_number + hunk->lines_in_hunk - 1); + return line >= (hunk->final_start_line_number + hunk->lines_in_hunk - 1); } static bool hunk_starts_at_or_after_line(git_blame_hunk *hunk, size_t line) @@ -53,9 +53,9 @@ static bool hunk_starts_at_or_after_line(git_blame_hunk *hunk, size_t line) } static git_blame_hunk* new_hunk( - uint16_t start, - uint16_t lines, - uint16_t orig_start, + size_t start, + size_t lines, + size_t orig_start, const char *path) { git_blame_hunk *hunk = git__calloc(1, sizeof(git_blame_hunk)); @@ -166,9 +166,9 @@ const git_blame_hunk *git_blame_get_hunk_byindex(git_blame *blame, uint32_t inde return (git_blame_hunk*)git_vector_get(&blame->hunks, index); } -const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, uint32_t lineno) +const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, size_t lineno) { - size_t i, new_lineno = (size_t)lineno; + size_t i, new_lineno = lineno; assert(blame); if (!git_vector_bsearch2(&i, &blame->hunks, hunk_byfinalline_search_cmp, &new_lineno)) { @@ -223,8 +223,8 @@ static git_blame_hunk *split_hunk_in_vector( } new_line_count = hunk->lines_in_hunk - rel_line; - nh = new_hunk((uint16_t)(hunk->final_start_line_number+rel_line), (uint16_t)new_line_count, - (uint16_t)(hunk->orig_start_line_number+rel_line), hunk->orig_path); + nh = new_hunk(hunk->final_start_line_number + rel_line, new_line_count, + hunk->orig_start_line_number + rel_line, hunk->orig_path); if (!nh) return NULL; @@ -233,7 +233,7 @@ static git_blame_hunk *split_hunk_in_vector( git_oid_cpy(&nh->orig_commit_id, &hunk->orig_commit_id); /* Adjust hunk that was split */ - hunk->lines_in_hunk -= (uint16_t)new_line_count; + hunk->lines_in_hunk -= new_line_count; git_vector_insert_sorted(vec, nh, NULL); { git_blame_hunk *ret = return_new ? nh : hunk; @@ -442,7 +442,7 @@ static int buffer_line_cb( } else { /* Create a new buffer-blame hunk with this line */ shift_hunks_by(&blame->hunks, blame->current_diff_line, 1); - blame->current_hunk = new_hunk((uint16_t)blame->current_diff_line, 1, 0, blame->path); + blame->current_hunk = new_hunk(blame->current_diff_line, 1, 0, blame->path); GITERR_CHECK_ALLOC(blame->current_hunk); git_vector_insert_sorted(&blame->hunks, blame->current_hunk, NULL); diff --git a/tests/blame/blame_helpers.c b/tests/blame/blame_helpers.c index b305ba1e3..61e87350c 100644 --- a/tests/blame/blame_helpers.c +++ b/tests/blame/blame_helpers.c @@ -4,7 +4,7 @@ void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...) { va_list arglist; - printf("Hunk %"PRIuZ" (line %d +%d): ", idx, + printf("Hunk %"PRIuZ" (line %"PRIuZ" +%"PRIuZ"): ", idx, hunk->final_start_line_number, hunk->lines_in_hunk-1); va_start(arglist, fmt); @@ -15,7 +15,7 @@ void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...) } void check_blame_hunk_index(git_repository *repo, git_blame *blame, int idx, - int start_line, int len, char boundary, const char *commit_id, const char *orig_path) + size_t start_line, size_t len, char boundary, const char *commit_id, const char *orig_path) { char expected[GIT_OID_HEXSZ+1] = {0}, actual[GIT_OID_HEXSZ+1] = {0}; const git_blame_hunk *hunk = git_blame_get_hunk_byindex(blame, idx); diff --git a/tests/blame/blame_helpers.h b/tests/blame/blame_helpers.h index 94321a5b5..fd5a35d2c 100644 --- a/tests/blame/blame_helpers.h +++ b/tests/blame/blame_helpers.h @@ -7,10 +7,8 @@ void check_blame_hunk_index( git_repository *repo, git_blame *blame, int idx, - int start_line, - int len, + size_t start_line, + size_t len, char boundary, const char *commit_id, const char *orig_path); - - diff --git a/tests/blame/simple.c b/tests/blame/simple.c index 83e5e056b..30b78168f 100644 --- a/tests/blame/simple.c +++ b/tests/blame/simple.c @@ -281,6 +281,18 @@ void test_blame_simple__can_restrict_lines_both(void) check_blame_hunk_index(g_repo, g_blame, 2, 6, 2, 0, "63d671eb", "b.txt"); } +void test_blame_simple__can_blame_huge_file(void) +{ + git_blame_options opts = GIT_BLAME_OPTIONS_INIT; + + cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git"))); + + cl_git_pass(git_blame_file(&g_blame, g_repo, "huge.txt", &opts)); + cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame)); + check_blame_hunk_index(g_repo, g_blame, 0, 1, 65536, 0, "4eecfea", "huge.txt"); + check_blame_hunk_index(g_repo, g_blame, 1, 65537, 1, 0, "6653ff4", "huge.txt"); +} + /* * $ git blame -n branch_file.txt be3563a..HEAD * orig line no final line no diff --git a/tests/resources/blametest.git/objects/37/681a80ca21064efd5c3bf2ef41eb3d05a1428b b/tests/resources/blametest.git/objects/37/681a80ca21064efd5c3bf2ef41eb3d05a1428b new file mode 100644 index 0000000000000000000000000000000000000000..a6ca0fb71bc4e4628b259c54724aefb548e19f20 GIT binary patch literal 106 zcmV-w0G0oE0V^p=O;s>7Fk&z?FfcPQQApG)sVHIK*|p_L{584Ig(oxX=g-+tCG;@F z1F9eitl+)TeP7jy&XdhWk9!9GG`iWToN^JWAfq%r6|5#ITu*OGQ+&wI5b;;17Ia=s MIJ)#D0Ko1mZLd!-CIA2c literal 0 HcmV?d00001 diff --git a/tests/resources/blametest.git/objects/4e/ecfea484f8005d101e547f6bfb07c99e2b114e b/tests/resources/blametest.git/objects/4e/ecfea484f8005d101e547f6bfb07c99e2b114e new file mode 100644 index 0000000000000000000000000000000000000000..79e0ada916ce5490fbde3d8007cf2b9dc2483c7b GIT binary patch literal 163 zcmV;U09^lg0gcZ~3c@fDfMM4;MRr{XlF4%lB7)#r@C1`&LQCm`G~QoO58wel`2V!d z^Vp<@{?ID3G{GYfsze%;w_LJICKk=b0!NdTBd{8y*r@W-WK1DBN;*LE=r8w`vFfo40Hcy7m3*$}=mr8YUcQU}R?FkuVr#U<5*a K0)x|A=6C=U8&RYH literal 0 HcmV?d00001 diff --git a/tests/resources/blametest.git/objects/66/53ff42313eb5c82806f145391b18a9699800c7 b/tests/resources/blametest.git/objects/66/53ff42313eb5c82806f145391b18a9699800c7 new file mode 100644 index 0000000000000000000000000000000000000000..1f1140931319f17adda09ce614c93aca093ec200 GIT binary patch literal 160 zcmV;R0AK%j0hP~93c@fDKw;N8MRr{Xl9^7L1VjWEZUs*;NhVlIe@Nr~1wDZWc<{Zn z&hywbLO8T4qB(~YNjPDj3l^~gI1qZr);knNhEjY)wQ-fDX%b0Am^>A4jLl#`EV(ko z5{*kC2u1)B#qai7tA22+W194{$+*0@=BCfve%+@1G|m?dM83Dy1L>myV^sS3n*3j? O+i$JAcj^PtVntJpk4Ve_ literal 0 HcmV?d00001 diff --git a/tests/resources/blametest.git/objects/ad/9cb4eac23df2fe5e1264287a5872ea2a1ff8b2 b/tests/resources/blametest.git/objects/ad/9cb4eac23df2fe5e1264287a5872ea2a1ff8b2 new file mode 100644 index 0000000000000000000000000000000000000000..077e658643b8f7d336e451489a3471e959f84683 GIT binary patch literal 106 zcmV-w0G0oE0V^p=O;s>7Fk&z?FfcPQQApG)sVHIK*|p_L{584Ig(oxX=g-+tCG;@F z1F9eitl+)TeP7jy&XdhWk9!9GG`iWToN^JWAfq%r6|Cmo{KxS#*&fdH;M1& literal 0 HcmV?d00001 diff --git a/tests/resources/blametest.git/refs/heads/master b/tests/resources/blametest.git/refs/heads/master index b763025d8c3aa0971dcd4ac03c25690bb24c47c6..d1bc4ca6b35e80309e786a6bd051b8671a82a715 100644 GIT binary patch literal 41 vcmV~$!4Uu;2m`Rc(*OmeAc?$rJUM*Y*c{fcs1#dNE?HrsNw7Wu1q)yR From 944dbd1259f930c8af543be172072a94afe9254a Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 24 Nov 2015 10:52:17 +0100 Subject: [PATCH 298/450] blame: use size_t for line counts in git_blame__entry The `git_blame__entry` struct keeps track of line counts with `int` fields. Since `int` is only guaranteed to be at least 16 bits we may overflow on certain platforms when line counts exceed 2^15. Fix this by instead storing line counts in `size_t`. --- src/blame.h | 6 +++--- src/blame_git.c | 38 ++++++++++++++++++++++---------------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/blame.h b/src/blame.h index 7e23de808..d8db8d5c1 100644 --- a/src/blame.h +++ b/src/blame.h @@ -31,10 +31,10 @@ typedef struct git_blame__entry { /* the first line of this group in the final image; * internally all line numbers are 0 based. */ - int lno; + size_t lno; /* how many lines this group has */ - int num_lines; + size_t num_lines; /* the commit that introduced this group into the final image */ git_blame__origin *suspect; @@ -51,7 +51,7 @@ typedef struct git_blame__entry { /* the line number of the first line of this group in the * suspect's file; internally all line numbers are 0 based. */ - int s_lno; + size_t s_lno; /* how significant this entry is -- cached to avoid * scanning the lines over and over. diff --git a/src/blame_git.c b/src/blame_git.c index 67bae2384..b8b568285 100644 --- a/src/blame_git.c +++ b/src/blame_git.c @@ -93,18 +93,25 @@ static bool same_suspect(git_blame__origin *a, git_blame__origin *b) } /* find the line number of the last line the target is suspected for */ -static int find_last_in_target(git_blame *blame, git_blame__origin *target) +static bool find_last_in_target(size_t *out, git_blame *blame, git_blame__origin *target) { git_blame__entry *e; - int last_in_target = -1; + size_t last_in_target = 0; + bool found = false; + + *out = 0; for (e=blame->ent; e; e=e->next) { if (e->guilty || !same_suspect(e->suspect, target)) continue; - if (last_in_target < e->s_lno + e->num_lines) + if (last_in_target < e->s_lno + e->num_lines) { + found = true; last_in_target = e->s_lno + e->num_lines; + } } - return last_in_target; + + *out = last_in_target; + return found; } /* @@ -122,9 +129,9 @@ static int find_last_in_target(git_blame *blame, git_blame__origin *target) * to be blamed for the parent, and after that portion. */ static void split_overlap(git_blame__entry *split, git_blame__entry *e, - int tlno, int plno, int same, git_blame__origin *parent) + size_t tlno, size_t plno, size_t same, git_blame__origin *parent) { - int chunk_end_lno; + size_t chunk_end_lno; if (e->s_lno < tlno) { /* there is a pre-chunk part not blamed on the parent */ @@ -265,9 +272,9 @@ static void decref_split(git_blame__entry *split) static void blame_overlap( git_blame *blame, git_blame__entry *e, - int tlno, - int plno, - int same, + size_t tlno, + size_t plno, + size_t same, git_blame__origin *parent) { git_blame__entry split[3] = {{0}}; @@ -285,9 +292,9 @@ static void blame_overlap( */ static void blame_chunk( git_blame *blame, - int tlno, - int plno, - int same, + size_t tlno, + size_t plno, + size_t same, git_blame__origin *target, git_blame__origin *parent) { @@ -314,7 +321,7 @@ static int my_emit( blame_chunk(d->blame, d->tlno, d->plno, start_b, d->target, d->parent); d->plno = start_a + count_a; d->tlno = start_b + count_b; - + return 0; } @@ -376,12 +383,11 @@ static int pass_blame_to_parent( git_blame__origin *target, git_blame__origin *parent) { - int last_in_target; + size_t last_in_target; mmfile_t file_p, file_o; blame_chunk_cb_data d = { blame, target, parent, 0, 0 }; - last_in_target = find_last_in_target(blame, target); - if (last_in_target < 0) + if (!find_last_in_target(&last_in_target, blame, target)) return 1; /* nothing remains for this target */ fill_origin_blob(parent, &file_p); From 7f8fe1d45e086adc9e7f3f0c33b624eeb3774033 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 1 Dec 2015 10:03:56 +0100 Subject: [PATCH 299/450] commit: introduce `git_commit_body` It is already possible to get a commit's summary with the `git_commit_summary` function. It is not possible to get the remaining part of the commit message, that is the commit message's body. Fix this by introducing a new function `git_commit_body`. --- include/git2/commit.h | 13 +++++++++++++ src/commit.c | 28 ++++++++++++++++++++++++++++ src/commit.h | 1 + tests/commit/commit.c | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+) diff --git a/include/git2/commit.h b/include/git2/commit.h index 04711c1fa..34d29ed81 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -127,6 +127,19 @@ GIT_EXTERN(const char *) git_commit_message_raw(const git_commit *commit); */ GIT_EXTERN(const char *) git_commit_summary(git_commit *commit); +/** + * Get the long "body" of the git commit message. + * + * The returned message is the body of the commit, comprising + * everything but the first paragraph of the message. Leading and + * trailing whitespaces are trimmed. + * + * @param commit a previously loaded commit. + * @return the body of a commit or NULL when no the message only + * consists of a summary + */ +GIT_EXTERN(const char *) git_commit_body(git_commit *commit); + /** * Get the commit time (i.e. committer time) of a commit. * diff --git a/src/commit.c b/src/commit.c index b42f9de28..81aae489f 100644 --- a/src/commit.c +++ b/src/commit.c @@ -31,6 +31,7 @@ void git_commit__free(void *_commit) git__free(commit->raw_message); git__free(commit->message_encoding); git__free(commit->summary); + git__free(commit->body); git__free(commit); } @@ -472,6 +473,33 @@ const char *git_commit_summary(git_commit *commit) return commit->summary; } +const char *git_commit_body(git_commit *commit) +{ + const char *msg, *end; + + assert(commit); + + if (!commit->body) { + /* search for end of summary */ + for (msg = git_commit_message(commit); *msg; ++msg) + if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n')) + break; + + /* trim leading and trailing whitespace */ + for (; *msg; ++msg) + if (!git__isspace(*msg)) + break; + for (end = msg + strlen(msg) - 1; msg <= end; --end) + if (!git__isspace(*end)) + break; + + if (*msg) + commit->body = git__strndup(msg, end - msg + 1); + } + + return commit->body; +} + int git_commit_tree(git_tree **tree_out, const git_commit *commit) { assert(commit); diff --git a/src/commit.h b/src/commit.h index efb080b50..d01ac2b2f 100644 --- a/src/commit.h +++ b/src/commit.h @@ -28,6 +28,7 @@ struct git_commit { char *raw_header; char *summary; + char *body; }; void git_commit__free(void *commit); diff --git a/tests/commit/commit.c b/tests/commit/commit.c index 81aaf80d3..c82971f4b 100644 --- a/tests/commit/commit.c +++ b/tests/commit/commit.c @@ -63,6 +63,18 @@ void assert_commit_summary(const char *expected, const char *given) git_commit__free(dummy); } +void assert_commit_body(const char *expected, const char *given) +{ + git_commit *dummy; + + cl_assert(dummy = git__calloc(1, sizeof(struct git_commit))); + + dummy->raw_message = git__strdup(given); + cl_assert_equal_s(expected, git_commit_body(dummy)); + + git_commit_free(dummy); +} + void test_commit_commit__summary(void) { assert_commit_summary("One-liner with no trailing newline", "One-liner with no trailing newline"); @@ -80,8 +92,35 @@ void test_commit_commit__summary(void) assert_commit_summary(" Spaces after newlines are collapsed", "\n Spaces after newlines\n are\n collapsed\n "); /* newlines at the very beginning are ignored and not collapsed */ assert_commit_summary(" Spaces before newlines are collapsed", " \nSpaces before newlines \nare \ncollapsed \n"); assert_commit_summary(" Spaces around newlines are collapsed", " \n Spaces around newlines \n are \n collapsed \n "); + assert_commit_summary(" Trailing newlines are" , " \n Trailing newlines \n are \n\n collapsed \n "); + assert_commit_summary(" Trailing spaces are stripped", " \n Trailing spaces \n are stripped \n\n \n \t "); assert_commit_summary("", ""); assert_commit_summary("", " "); assert_commit_summary("", "\n"); assert_commit_summary("", "\n \n"); } + +void test_commit_commit__body(void) +{ + assert_commit_body(NULL, "One-liner with no trailing newline"); + assert_commit_body(NULL, "One-liner with trailing newline\n"); + assert_commit_body(NULL, "\n\nTrimmed leading&trailing newlines\n\n"); + assert_commit_body("(There are more!)", "\nFirst paragraph only\n\n(There are more!)"); + assert_commit_body("(Yes, unwrapped!)", "\nFirst paragraph\nwith unwrapped\ntrailing\tlines\n\n(Yes, unwrapped!)"); + assert_commit_body("are preserved", "\tLeading\n\ttabs\n\nare preserved"); /* tabs around newlines are collapsed down to a single space */ + assert_commit_body("are preserved", " Leading\n Spaces\n\nare preserved"); /* spaces around newlines are collapsed down to a single space */ + assert_commit_body(NULL, "Trailing tabs\tare removed\t\t"); + assert_commit_body(NULL, "Trailing spaces are removed "); + assert_commit_body("are removed", "Trailing tabs\t\n\nare removed"); + assert_commit_body("are removed", "Trailing spaces \n\nare removed"); + assert_commit_body(NULL,"Newlines\nare\nreplaced by spaces\n"); + assert_commit_body(NULL , "\n Spaces after newlines\n are\n collapsed\n "); /* newlines at the very beginning are ignored and not collapsed */ + assert_commit_body(NULL , " \nSpaces before newlines \nare \ncollapsed \n"); + assert_commit_body(NULL , " \n Spaces around newlines \n are \n collapsed \n "); + assert_commit_body("collapsed" , " \n Trailing newlines \n are \n\n collapsed \n "); + assert_commit_body(NULL, " \n Trailing spaces \n are stripped \n\n \n \t "); + assert_commit_body(NULL , ""); + assert_commit_body(NULL , " "); + assert_commit_body(NULL , "\n"); + assert_commit_body(NULL , "\n \n"); +} From 254e0a33ee66c65948e15efbe93a810c4077e324 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 24 Nov 2015 13:43:43 +0100 Subject: [PATCH 300/450] diff: include commit message when formatting patch When formatting a patch as email we do not include the commit's message in the formatted patch output. Implement this and add a test that verifies behavior. --- include/git2/diff.h | 5 ++- src/diff.c | 11 ++++- tests/diff/format_email.c | 41 ++++++++++++++++++ .../resources/diff_format_email/.gitted/index | Bin 256 -> 289 bytes .../62/7e7e12d87e07a83fad5b6bfa25e86ead4a5270 | Bin 0 -> 289 bytes .../73/09653445ecf038d3e3dd9ed55edb6cb541a4ba | Bin 0 -> 28 bytes .../d5/ff67764c82f729b13c26a09576570d884d9687 | Bin 0 -> 121 bytes .../.gitted/refs/heads/master | Bin 41 -> 41 bytes tests/resources/diff_format_email/file3.txt | Bin 31 -> 37 bytes 9 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 tests/resources/diff_format_email/.gitted/objects/62/7e7e12d87e07a83fad5b6bfa25e86ead4a5270 create mode 100644 tests/resources/diff_format_email/.gitted/objects/73/09653445ecf038d3e3dd9ed55edb6cb541a4ba create mode 100644 tests/resources/diff_format_email/.gitted/objects/d5/ff67764c82f729b13c26a09576570d884d9687 diff --git a/include/git2/diff.h b/include/git2/diff.h index cbffdb49a..3eb265652 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -1286,12 +1286,15 @@ typedef struct { /** Summary of the change */ const char *summary; + /** Commit message's body */ + const char *body; + /** Author of the change */ const git_signature *author; } git_diff_format_email_options; #define GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION 1 -#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT {GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, 0, 1, 1, NULL, NULL, NULL} +#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT {GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, 0, 1, 1, NULL, NULL, NULL, NULL} /** * Create an e-mail ready patch from a diff. diff --git a/src/diff.c b/src/diff.c index 9402b6e61..67fab0763 100644 --- a/src/diff.c +++ b/src/diff.c @@ -1659,6 +1659,7 @@ int git_diff_format_email__append_header_tobuf( const git_oid *id, const git_signature *author, const char *summary, + const char *body, size_t patch_no, size_t total_patches, bool exclude_patchno_marker) @@ -1698,6 +1699,13 @@ int git_diff_format_email__append_header_tobuf( error = git_buf_printf(out, "%s\n\n", summary); + if (body) { + git_buf_puts(out, body); + + if (out->ptr[out->size - 1] != '\n') + git_buf_putc(out, '\n'); + } + return error; } @@ -1775,7 +1783,7 @@ int git_diff_format_email( error = git_diff_format_email__append_header_tobuf(out, opts->id, opts->author, summary == NULL ? opts->summary : summary, - opts->patch_no, opts->total_patches, ignore_marker); + opts->body, opts->patch_no, opts->total_patches, ignore_marker); if (error < 0) goto on_error; @@ -1818,6 +1826,7 @@ int git_diff_commit_as_email( opts.total_patches = total_patches; opts.id = git_commit_id(commit); opts.summary = git_commit_summary(commit); + opts.body = git_commit_body(commit); opts.author = git_commit_author(commit); if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0) diff --git a/tests/diff/format_email.c b/tests/diff/format_email.c index 18ad99bd5..8a0128898 100644 --- a/tests/diff/format_email.c +++ b/tests/diff/format_email.c @@ -97,6 +97,47 @@ void test_diff_format_email__simple(void) email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); } +void test_diff_format_email__with_message(void) +{ + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + const char *email = "From 627e7e12d87e07a83fad5b6bfa25e86ead4a5270 Mon Sep 17 00:00:00 2001\n" \ + "From: Patrick Steinhardt \n" \ + "Date: Tue, 24 Nov 2015 13:34:39 +0100\n" \ + "Subject: [PATCH] Modify content with message\n" \ + "\n" \ + "Modify content of file3.txt by appending a new line. Make this\n" \ + "commit message somewhat longer to test behavior with newlines\n" \ + "embedded in the message body.\n" \ + "\n" \ + "Also test if new paragraphs are included correctly.\n" \ + "---\n" \ + " file3.txt | 1 +\n" \ + " 1 file changed, 1 insertion(+), 0 deletions(-)\n" \ + "\n" \ + "diff --git a/file3.txt b/file3.txt\n" \ + "index 9a2d780..7309653 100644\n" \ + "--- a/file3.txt\n" \ + "+++ b/file3.txt\n" \ + "@@ -3,3 +3,4 @@ file3!\n" \ + " file3\n" \ + " file3\n" \ + " file3\n" \ + "+file3\n" \ + "--\n" \ + "libgit2 0.23.0\n" \ + "\n"; + + opts.body = "Modify content of file3.txt by appending a new line. Make this\n" \ + "commit message somewhat longer to test behavior with newlines\n" \ + "embedded in the message body.\n" \ + "\n" \ + "Also test if new paragraphs are included correctly."; + + assert_email_match( + email, "627e7e12d87e07a83fad5b6bfa25e86ead4a5270", &opts); +} + + void test_diff_format_email__multiple(void) { git_oid oid; diff --git a/tests/resources/diff_format_email/.gitted/index b/tests/resources/diff_format_email/.gitted/index index f73027e565387ebbe041eab863c6cce17c04085c..d94f87de830619993545c15c09363479043f1d9c 100644 GIT binary patch delta 222 zcmZo*TF7MJ;u+-3z`(!+#LS^82OT)v#q41;1H(T?h6eE`3=EBLfzmHPc%p%h3QQhG zL)AS6s#^kA*Pv1x7UC%*{ozgpnC@0)g{pgotPW_eYB6W3iR+sW7MCC2op&|vcFtDE zCA%0n(=u~XjrB?@N*F?dTwQ_cBpHkq47jfTPcQRn`mVXrMs2~=vT)uG-)ZfJIakiP VoZfL{_Pq~(g)TTLO|xf=002wANY?-W delta 189 zcmZ3;)WBrm;u+-3z`(!+#LU4SkugA;0Y)7k2CH-1tpHTF1g@?@B?gB&MPzkAbL3~~R&X79#r10M&PyxGp6&ksv0BH4fio>L hC)HT5q@sjjCbK1To7|RzlX5z99et-tKipgw3jp#`G1UM7 diff --git a/tests/resources/diff_format_email/.gitted/objects/62/7e7e12d87e07a83fad5b6bfa25e86ead4a5270 b/tests/resources/diff_format_email/.gitted/objects/62/7e7e12d87e07a83fad5b6bfa25e86ead4a5270 new file mode 100644 index 0000000000000000000000000000000000000000..269a5bcf4b8a7c651185dcb36180a472876b9f79 GIT binary patch literal 289 zcmV++0p9+20hN$VPQx$|g<1O)vqy@O*s)y+Avi!KBu?O;abh*GBYR5e?Qtt$!H)Il z``(-9^0q|+A*Kf^h~TFXq*78iJq9%%?7-b9O@Ek_oK)|%7XBc$V!J7r2(;q5mqCV6 zq2vSF-i{oFouoxG+F;xu3|E|)GcCEmo1qfj3El}~T1@c-PkVjYPqmBNGYmp#F11hx zc2_z{%UOY n*N(B*wSMz4+|%3EW-ex5Dzw!b<<|2JcexaFv^MAqRH2qTLja9U literal 0 HcmV?d00001 diff --git a/tests/resources/diff_format_email/.gitted/objects/73/09653445ecf038d3e3dd9ed55edb6cb541a4ba b/tests/resources/diff_format_email/.gitted/objects/73/09653445ecf038d3e3dd9ed55edb6cb541a4ba new file mode 100644 index 0000000000000000000000000000000000000000..ba9c5fa577c6b40c213f96f53b4f253d2d919394 GIT binary patch literal 28 jcmb7v}7mEv56!`27!$qQis literal 41 ucmV~$!4Uv31O&i;s=*1`aT)qAA-k0*iH)_KfDVO*ao*sxar>z^66XWv?F%9R diff --git a/tests/resources/diff_format_email/file3.txt b/tests/resources/diff_format_email/file3.txt index 9a2d780ac2ea0aeabdb9d2a876e6bbfff17b2c44..7309653445ecf038d3e3dd9ed55edb6cb541a4ba 100644 GIT binary patch delta 11 Scmb1FogmMamYI`k%mn}t!~)j< delta 4 LcmY$DpCAta0yF^h From 9487585ddc5d7cbb8b9d085e030a01ff805bc41b Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 1 Dec 2015 14:19:29 +0100 Subject: [PATCH 301/450] tree: mark cloned tree entries as un-pooled When duplicating a `struct git_tree_entry` with `git_tree_entry_dup` the resulting structure is not allocated inside a memory pool. As we do a 1:1 copy of the original struct, though, we also copy the `pooled` field, which is set to `true` for pooled entries. This results in a huge memory leak as we never free tree entries that were duplicated from a pooled tree entry. Fix this by marking the newly duplicated entry as un-pooled. --- src/tree.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tree.c b/src/tree.c index 0a32868cd..0e3738afa 100644 --- a/src/tree.c +++ b/src/tree.c @@ -253,6 +253,8 @@ int git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source) memcpy(copy, source, total_size); + copy->pooled = 0; + *dest = copy; return 0; } From efd9ab568b8b56e422e0497badd0368948fba590 Mon Sep 17 00:00:00 2001 From: Chris Hescock Date: Fri, 20 Nov 2015 11:26:26 -0500 Subject: [PATCH 302/450] Treat GIT_PASSTHROUGH as though git_cred_acquire_cb isn't set. --- src/transports/winhttp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index 77d939bd3..9e97a279d 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -930,7 +930,10 @@ replay: cred_error = t->owner->cred_acquire_cb(&t->cred, t->owner->url, t->connection_data.user, allowed_types, t->owner->cred_acquire_payload); - if (cred_error < 0) + /* Treat GIT_PASSTHROUGH as though git_cred_acquire_cb isn't set */ + if (cred_error == GIT_PASSTHROUGH) + cred_error = 1; + else if (cred_error < 0) return cred_error; } From 326c9fc2ed2164e6840847fd073d8894ab1032c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Tue, 1 Dec 2015 20:41:23 +0100 Subject: [PATCH 303/450] checkout test: Apply umask to file-mode test as well MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the file-mode test to expect system umask being applied to the created file as well (it is currently applied to the directory only). This fixes the test on systems where umask != 022. Signed-off-by: MichaÅ‚ Górny --- tests/checkout/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/checkout/index.c b/tests/checkout/index.c index 9fa901867..8af3e5684 100644 --- a/tests/checkout/index.c +++ b/tests/checkout/index.c @@ -298,7 +298,7 @@ void test_checkout_index__options_dir_modes(void) /* File-mode test, since we're on the 'dir' branch */ cl_git_pass(p_stat("./testrepo/a/b.txt", &st)); - cl_assert_equal_i_fmt(st.st_mode, GIT_FILEMODE_BLOB_EXECUTABLE, "%07o"); + cl_assert_equal_i_fmt(st.st_mode, GIT_FILEMODE_BLOB_EXECUTABLE & ~um, "%07o"); git_commit_free(commit); } From 0878ca9b7c0e8269f83437956189b9920722afdd Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Wed, 7 Oct 2015 10:31:07 +0200 Subject: [PATCH 304/450] CMakeLists: Compare CMAKE_SIZEOF_VOID_P as a number, not as a string --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b7a03c67..40a52bc01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -235,7 +235,7 @@ IF (WIN32 AND WINHTTP) SET(LIBWINHTTP_PATH "${CMAKE_CURRENT_BINARY_DIR}/deps/winhttp") FILE(MAKE_DIRECTORY ${LIBWINHTTP_PATH}) - IF ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + IF (CMAKE_SIZEOF_VOID_P EQUAL 8) set(WINHTTP_DEF "${CMAKE_CURRENT_SOURCE_DIR}/deps/winhttp/winhttp64.def") ELSE() set(WINHTTP_DEF "${CMAKE_CURRENT_SOURCE_DIR}/deps/winhttp/winhttp.def") From 0174f21b0ab1abb24496939d95c0cc84187aba00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 2 Dec 2015 18:56:31 +0100 Subject: [PATCH 305/450] tree: use a specialised mode parse function Instead of going out to strtol, which is made to parse generic numbers, copy a parse function from git which is specialised for file modes. --- src/tree.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/tree.c b/src/tree.c index 0e3738afa..2de8e72e1 100644 --- a/src/tree.c +++ b/src/tree.c @@ -416,6 +416,25 @@ static int tree_error(const char *str, const char *path) return -1; } +static int parse_mode(unsigned int *modep, const char *buffer, const char **buffer_out) +{ + unsigned char c; + unsigned int mode = 0; + + if (*buffer == ' ') + return -1; + + while ((c = *buffer++) != ' ') { + if (c < '0' || c > '7') + return -1; + mode = (mode << 3) + (c - '0'); + } + *modep = mode; + *buffer_out = buffer; + + return 0; +} + int git_tree__parse(void *_tree, git_odb_object *odb_obj) { git_tree *tree = _tree; @@ -430,14 +449,11 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj) git_tree_entry *entry; size_t filename_len; const char *nul; - int attr; + unsigned int attr; - if (git__strtol32(&attr, buffer, &buffer, 8) < 0 || !buffer) + if (parse_mode(&attr, buffer, &buffer) < 0 || !buffer) return tree_error("Failed to parse tree. Can't parse filemode", NULL); - if (*buffer++ != ' ') - return tree_error("Failed to parse tree. Object is corrupted", NULL); - if ((nul = memchr(buffer, 0, buffer_end - buffer)) == NULL) return tree_error("Failed to parse tree. Object is corrupted", NULL); From 626f9e243edbd8d82a27557b3433d19217714069 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 3 Dec 2015 16:27:15 -0500 Subject: [PATCH 306/450] index: canonicalize inserted paths safely When adding to the index, we look to see if a portion of the given path matches a portion of a path in the index. If so, we will use the existing path information. For example, when adding `foo/bar.c`, if there is an index entry to `FOO/other` and the filesystem is case insensitive, then we will put `bar.c` into the existing tree instead of creating a new one with a different case. Use `strncmp` to do that instead of `memcmp`. When we `bsearch` into the index, we locate the position where the new entry would go. The index entry at that position does not necessarily have a relation to the entry we're adding, so we cannot make assumptions and use `memcmp`. Instead, compare them as strings. When canonicalizing paths, we look for the first index entry that matches a given substring. --- src/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.c b/src/index.c index ca5b2c46e..391738e39 100644 --- a/src/index.c +++ b/src/index.c @@ -1167,7 +1167,7 @@ static int canonicalize_directory_path( while ((match = git_vector_get(&index->entries, pos))) { if (GIT_IDXENTRY_STAGE(match) != 0) { /* conflicts do not contribute to canonical paths */ - } else if (memcmp(search, match->path, search_len) == 0) { + } else if (strncmp(search, match->path, search_len) == 0) { /* prefer an exact match to the input filename */ best = match; best_len = search_len; From fc4364696556678f4cac4f6cefed17b94b4ec776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 6 Dec 2015 22:51:00 +0100 Subject: [PATCH 307/450] tree: mark a tree as already sorted The trees are sorted on-disk, so we don't have to go over them again. This cuts almost a fifth of time spent parsing trees. --- CHANGELOG.md | 4 ++++ src/tree.c | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 196ad705a..a2dd76eba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ v0.23 + 1 * You can now set your own user-agent to be sent for HTTP requests by using the `GIT_OPT_SET_USER_AGENT` with `git_libgit2_opts()`. +* Tree objects are now assumed to be sorted. If a tree is not + correctly formed, it will give bad results. This is the git approach + and cuts a significant amount of time when reading the trees. + ### API additions * `git_config_lock()` has been added, which allow for diff --git a/src/tree.c b/src/tree.c index 2de8e72e1..aab4b58ad 100644 --- a/src/tree.c +++ b/src/tree.c @@ -476,7 +476,8 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj) buffer += GIT_OID_RAWSZ; } - git_vector_sort(&tree->entries); + /* The tree is sorted by definition. Bad inputs give bad outputs */ + tree->entries.flags |= GIT_VECTOR_SORTED; return 0; } From b057fdef694a558221784b534de66fe15dff5782 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 8 Dec 2015 16:00:35 +0100 Subject: [PATCH 308/450] index: always queue `remove_entry` for removal When replacing an index with a new one, we need to iterate through all index entries in order to determine which entries are equal. When it is not possible to re-use old entries for the new index, we move it into a list of entries that are to be removed and thus free'd. When we encounter a non-zero error code, though, we skip adding the current index entry to the remove-queue. `INSERT_MAP_EX`, which is the function last run before adding to the remove-queue, may return a positive non-zero code that indicates what exactly happened while inserting the element. In this case we skip adding the entry to the remove-queue but still continue the current operation, leading to a leak of the current entry. Fix this by checking for a negative return value instead of a non-zero one when we want to add the current index entry to the remove-queue. --- src/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.c b/src/index.c index ca5b2c46e..f598aaf47 100644 --- a/src/index.c +++ b/src/index.c @@ -3014,7 +3014,7 @@ int git_index_read_index( INSERT_IN_MAP_EX(index, new_entries_map, add_entry, error); } - if (remove_entry && !error) + if (remove_entry && error >= 0) error = git_vector_insert(&remove_entries, remove_entry); if (error < 0) { From eda726cfb5ea44659e471ba10aee21f16ce2e800 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 8 Dec 2015 11:34:00 -0500 Subject: [PATCH 309/450] Use a typedef for the submodule_foreach callback. This fits with the style for the rest of the project, but more importantly, makes life easier for bindings authors who auto-generate code. --- include/git2/submodule.h | 14 +++++++++++++- src/submodule.c | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/include/git2/submodule.h b/include/git2/submodule.h index 689fe4b64..96187b0e1 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -107,6 +107,18 @@ typedef enum { GIT_SUBMODULE_STATUS_WD_WD_MODIFIED | \ GIT_SUBMODULE_STATUS_WD_UNTRACKED)) != 0) +/** + * Function pointer to receive each submodule + * + * `sm` is the `git_submodule` currently being visited. + * + * `name` is the name of the submodule. + * + * `payload` is the value you passed to the foreach function as payload. + */ +typedef int (*git_submodule_cb)( + git_submodule *sm, const char *name, void *payload); + /** * Submodule update options structure * @@ -239,7 +251,7 @@ GIT_EXTERN(void) git_submodule_free(git_submodule *submodule); */ GIT_EXTERN(int) git_submodule_foreach( git_repository *repo, - int (*callback)(git_submodule *sm, const char *name, void *payload), + git_submodule_cb callback, void *payload); /** diff --git a/src/submodule.c b/src/submodule.c index 1148f8790..6ec4c53e1 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -495,7 +495,7 @@ cleanup: int git_submodule_foreach( git_repository *repo, - int (*callback)(git_submodule *sm, const char *name, void *payload), + git_submodule_cb callback, void *payload) { git_vector snapshot = GIT_VECTOR_INIT; From ab273821ee0bf2c46ad9051266d3731cbc31b03b Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 8 Dec 2015 11:58:19 -0500 Subject: [PATCH 310/450] Play nice with the docs. --- include/git2/submodule.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/include/git2/submodule.h b/include/git2/submodule.h index 96187b0e1..bc94eacaa 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -110,11 +110,10 @@ typedef enum { /** * Function pointer to receive each submodule * - * `sm` is the `git_submodule` currently being visited. - * - * `name` is the name of the submodule. - * - * `payload` is the value you passed to the foreach function as payload. + * @param sm git_submodule currently being visited + * @param name name of the submodule + * @param payload value you passed to the foreach function as payload + * @return 0 on success or error code */ typedef int (*git_submodule_cb)( git_submodule *sm, const char *name, void *payload); From 828852553a6c1704a45463a3681aaa9706d8175c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 9 Dec 2015 13:57:15 +0100 Subject: [PATCH 311/450] CHANGELOG: add some things we missed --- CHANGELOG.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2dd76eba..c84d27da3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ v0.23 + 1 * You can now set your own user-agent to be sent for HTTP requests by using the `GIT_OPT_SET_USER_AGENT` with `git_libgit2_opts()`. +* You can set custom HTTP header fields to be sent along with requests + by passing them in the fetch and push options. + * Tree objects are now assumed to be sorted. If a tree is not correctly formed, it will give bad results. This is the git approach and cuts a significant amount of time when reading the trees. @@ -24,12 +27,15 @@ v0.23 + 1 the opportunity for concurrent operations and not committing any changes until the unlock. - * `git_diff_options` added a new callback `progress_cb` to report on the progress of the diff as files are being compared. The documentation of the existing callback `notify_cb` was updated to reflect that it only gets called when new deltas are added to the diff. +* `git_fetch_options` and `git_push_options` have gained a `custom_headers` + field to set the extra HTTP header fields to send. + + * `git_stream_register_tls()` lets you register a callback to be used as the constructor for a TLS stream instead of the libgit2 built-in one. @@ -65,6 +71,10 @@ v0.23 + 1 * The `notify_payload` field of `git_diff_options` was renamed to `payload` to reflect that it's also the payload for the new progress callback. +* The `git_config_level_t` enum has gained a higher-priority value + `GIT_CONFIG_LEVEL_PROGRAMDATA` which represent a rough Windows equivalent + to the system level configuration. + v0.23 ------ From 465c3b38d59548648521fa4cdbd3d8a832059bb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 9 Dec 2015 19:16:11 +0100 Subject: [PATCH 312/450] reset: perform the checkout before moving HEAD or the index This keeps the state of the workdir the same as one from HEAD, removing a source of possible confusion when calculating the work that is to be done. --- src/reset.c | 12 +++++------ tests/reset/hard.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/reset.c b/src/reset.c index 0ffa51b66..f8a1a1dc8 100644 --- a/src/reset.c +++ b/src/reset.c @@ -145,19 +145,19 @@ static int reset( if ((error = git_buf_printf(&log_message, "reset: moving to %s", to)) < 0) return error; - /* move HEAD to the new target */ - if ((error = git_reference__update_terminal(repo, GIT_HEAD_FILE, - git_object_id(commit), NULL, git_buf_cstr(&log_message))) < 0) - goto cleanup; - if (reset_type == GIT_RESET_HARD) { - /* overwrite working directory with HEAD */ + /* overwrite working directory with the new tree */ opts.checkout_strategy = GIT_CHECKOUT_FORCE; if ((error = git_checkout_tree(repo, (git_object *)tree, &opts)) < 0) goto cleanup; } + /* move HEAD to the new target */ + if ((error = git_reference__update_terminal(repo, GIT_HEAD_FILE, + git_object_id(commit), NULL, git_buf_cstr(&log_message))) < 0) + goto cleanup; + if (reset_type > GIT_RESET_SOFT) { /* reset index to the target content */ diff --git a/tests/reset/hard.c b/tests/reset/hard.c index 88055adda..149973374 100644 --- a/tests/reset/hard.c +++ b/tests/reset/hard.c @@ -235,3 +235,55 @@ void test_reset_hard__reflog_is_correct(void) git_annotated_commit_free(annotated); } + +void test_reset_hard__switch_file_to_dir(void) +{ + git_index_entry entry = { 0 }; + git_index *idx; + git_object *commit; + git_tree *tree; + git_signature *sig; + git_oid src_tree_id, tgt_tree_id; + git_oid src_id, tgt_id; + + entry.mode = GIT_FILEMODE_BLOB; + cl_git_pass(git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391")); + cl_git_pass(git_index_new(&idx)); + cl_git_pass(git_signature_now(&sig, "foo", "bar")); + + /* Create the old tree */ + entry.path = "README"; + cl_git_pass(git_index_add(idx, &entry)); + entry.path = "dir"; + cl_git_pass(git_index_add(idx, &entry)); + + cl_git_pass(git_index_write_tree_to(&src_tree_id, idx, repo)); + cl_git_pass(git_index_clear(idx)); + + cl_git_pass(git_tree_lookup(&tree, repo, &src_tree_id)); + cl_git_pass(git_commit_create(&src_id, repo, NULL, sig, sig, NULL, "foo", tree, 0, NULL)); + git_tree_free(tree); + + /* Create the new tree */ + entry.path = "README"; + cl_git_pass(git_index_add(idx, &entry)); + entry.path = "dir/FILE"; + cl_git_pass(git_index_add(idx, &entry)); + + cl_git_pass(git_index_write_tree_to(&tgt_tree_id, idx, repo)); + cl_git_pass(git_tree_lookup(&tree, repo, &tgt_tree_id)); + cl_git_pass(git_commit_create(&tgt_id, repo, NULL, sig, sig, NULL, "foo", tree, 0, NULL)); + git_tree_free(tree); + git_index_free(idx); + git_signature_free(sig); + + /* Let's go to a known state of the src commit with the file named 'dir' */ + cl_git_pass(git_object_lookup(&commit, repo, &src_id, GIT_OBJ_COMMIT)); + cl_git_pass(git_reset(repo, commit, GIT_RESET_HARD, NULL)); + git_object_free(commit); + + /* And now we move over to the commit with the directory named 'dir' */ + cl_git_pass(git_object_lookup(&commit, repo, &tgt_id, GIT_OBJ_COMMIT)); + cl_git_pass(git_reset(repo, commit, GIT_RESET_HARD, NULL)); + git_object_free(commit); +} From 95746a57c70a4020a2025f9a3ad3700386d286ee Mon Sep 17 00:00:00 2001 From: Thomas Edvalson Date: Mon, 14 Dec 2015 19:21:09 -0500 Subject: [PATCH 313/450] Fix a couple function signatures --- include/git2/stash.h | 2 +- src/path.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/git2/stash.h b/include/git2/stash.h index 526db0ba2..b321dc34e 100644 --- a/include/git2/stash.h +++ b/include/git2/stash.h @@ -68,7 +68,7 @@ GIT_EXTERN(int) git_stash_save( git_repository *repo, const git_signature *stasher, const char *message, - unsigned int flags); + uint32_t flags); /** Stash application flags. */ typedef enum { diff --git a/src/path.c b/src/path.c index 72cb289e0..18b4f03fd 100644 --- a/src/path.c +++ b/src/path.c @@ -1399,7 +1399,7 @@ int git_path_dirload( git_vector *contents, const char *path, size_t prefix_len, - unsigned int flags) + uint32_t flags) { git_path_diriter iter = GIT_PATH_DIRITER_INIT; const char *name; From cf339ede0abf066e8a1f4f7625878f837a78b84c Mon Sep 17 00:00:00 2001 From: tepas Date: Wed, 16 Dec 2015 09:25:18 +1100 Subject: [PATCH 314/450] fix git_blob_create_fromchunks documentation putting `0.` at the start of the line turns it into a numbered list. --- include/git2/blob.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/git2/blob.h b/include/git2/blob.h index 4a6d8e50a..9a57c37f5 100644 --- a/include/git2/blob.h +++ b/include/git2/blob.h @@ -171,8 +171,8 @@ typedef int (*git_blob_chunk_cb)(char *content, size_t max_length, void *payload * - The `callback` must return the number of bytes that have been * written to the `content` buffer. * - * - When there is no more data to stream, `callback` should return - * 0. This will prevent it from being invoked anymore. + * - When there is no more data to stream, `callback` should return 0. + * This will prevent it from being invoked anymore. * * - If an error occurs, the callback should return a negative value. * This value will be returned to the caller. From 879ebab314fe60cc737d436f62f190260ce13c1a Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 16 Dec 2015 12:30:52 +0100 Subject: [PATCH 315/450] merge: Use `git_index__fill` to populate the index Instead of calling `git_index_add` in a loop, use the new `git_index_fill` internal API to fill the index with the initial staged entries. The new `fill` helper assumes that all the entries will be unique and valid, so it can append them at the end of the entries vector and only sort it once at the end. It performs no validation checks. This prevents the quadratic behavior caused by having to sort the entries list once after every insertion. --- src/index.c | 37 +++++++++++++++++++++++++++++++++++++ src/index.h | 2 ++ src/merge.c | 7 ++----- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/index.c b/src/index.c index 26cd83851..60e4d443c 100644 --- a/src/index.c +++ b/src/index.c @@ -1542,6 +1542,43 @@ int git_index_remove_bypath(git_index *index, const char *path) return 0; } +int git_index__fill(git_index *index, const git_vector *source_entries) +{ + const git_index_entry *source_entry = NULL; + size_t i; + int ret = 0; + + assert(index); + + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Unable to acquire index lock"); + return -1; + } + + git_vector_foreach(source_entries, i, source_entry) { + git_index_entry *entry = NULL; + + if ((ret = index_entry_dup(&entry, index, source_entry)) < 0) + break; + + entry->flags_extended |= GIT_IDXENTRY_UPTODATE; + + ret = git_vector_insert(&index->entries, entry); + if (ret < 0) + break; + + INSERT_IN_MAP(index, entry, ret); + if (ret < 0) + break; + } + + if (!ret) + git_vector_sort(&index->entries); + + git_mutex_unlock(&index->lock); + return ret; +} + int git_index_add(git_index *index, const git_index_entry *source_entry) { diff --git a/src/index.h b/src/index.h index 9baf976ee..0909da889 100644 --- a/src/index.h +++ b/src/index.h @@ -113,6 +113,8 @@ GIT_INLINE(bool) git_index_entry_newer_than_index( extern int git_index__find_pos( size_t *at_pos, git_index *index, const char *path, size_t path_len, int stage); +extern int git_index__fill(git_index *index, const git_vector *source_entries); + extern void git_index__set_ignore_case(git_index *index, bool ignore_case); extern unsigned int git_index__create_mode(unsigned int mode); diff --git a/src/merge.c b/src/merge.c index 9eb3b0904..61ff93c19 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1739,7 +1739,6 @@ static int index_from_diff_list(git_index **out, { git_index *index; size_t i; - git_index_entry *entry; git_merge_diff *conflict; int error = 0; @@ -1748,10 +1747,8 @@ static int index_from_diff_list(git_index **out, if ((error = git_index_new(&index)) < 0) return error; - git_vector_foreach(&diff_list->staged, i, entry) { - if ((error = git_index_add(index, entry)) < 0) - goto on_error; - } + if ((error = git_index__fill(index, &diff_list->staged)) < 0) + goto on_error; git_vector_foreach(&diff_list->conflicts, i, conflict) { const git_index_entry *ancestor = From 0cc20a8c487cae629ea269fec3173f5647c38d0c Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 16 Dec 2015 16:53:06 +0100 Subject: [PATCH 316/450] index: Adjust namemask & mode when filling --- src/index.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/index.c b/src/index.c index 60e4d443c..04acd4f04 100644 --- a/src/index.c +++ b/src/index.c @@ -879,6 +879,18 @@ void git_index_entry__init_from_stat( entry->file_size = st->st_size; } +static void index_entry_adjust_namemask( + git_index_entry *entry, + size_t path_length) +{ + entry->flags &= ~GIT_IDXENTRY_NAMEMASK; + + if (path_length < GIT_IDXENTRY_NAMEMASK) + entry->flags |= path_length & GIT_IDXENTRY_NAMEMASK; + else + entry->flags |= GIT_IDXENTRY_NAMEMASK; +} + static int index_entry_create( git_index_entry **out, git_repository *repo, @@ -1279,13 +1291,7 @@ static int index_insert( /* make sure that the path length flag is correct */ path_length = ((struct entry_internal *)entry)->pathlen; - - entry->flags &= ~GIT_IDXENTRY_NAMEMASK; - - if (path_length < GIT_IDXENTRY_NAMEMASK) - entry->flags |= path_length & GIT_IDXENTRY_NAMEMASK; - else - entry->flags |= GIT_IDXENTRY_NAMEMASK; + index_entry_adjust_namemask(entry, path_length); /* this entry is now up-to-date and should not be checked for raciness */ entry->flags_extended |= GIT_IDXENTRY_UPTODATE; @@ -1561,10 +1567,11 @@ int git_index__fill(git_index *index, const git_vector *source_entries) if ((ret = index_entry_dup(&entry, index, source_entry)) < 0) break; + index_entry_adjust_namemask(entry, ((struct entry_internal *)entry)->pathlen); entry->flags_extended |= GIT_IDXENTRY_UPTODATE; + entry->mode = git_index__create_mode(entry->mode); - ret = git_vector_insert(&index->entries, entry); - if (ret < 0) + if ((ret = git_vector_insert(&index->entries, entry)) < 0) break; INSERT_IN_MAP(index, entry, ret); @@ -2889,11 +2896,7 @@ static int read_tree_cb( entry->flags_extended = 0; } - if (path.size < GIT_IDXENTRY_NAMEMASK) - entry->flags = path.size & GIT_IDXENTRY_NAMEMASK; - else - entry->flags = GIT_IDXENTRY_NAMEMASK; - + index_entry_adjust_namemask(entry, path.size); git_buf_free(&path); if (git_vector_insert(data->new_entries, entry) < 0) { From d7d46cfbfd19983ee71b53607abcaf55872be83a Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 16 Dec 2015 17:00:25 +0100 Subject: [PATCH 317/450] index: Preallocate the entries vector with size hint --- src/index.c | 8 ++++++++ src/vector.c | 7 +++++++ src/vector.h | 1 + 3 files changed, 16 insertions(+) diff --git a/src/index.c b/src/index.c index 04acd4f04..03873f427 100644 --- a/src/index.c +++ b/src/index.c @@ -1556,11 +1556,19 @@ int git_index__fill(git_index *index, const git_vector *source_entries) assert(index); + if (!source_entries->length) + return 0; + if (git_mutex_lock(&index->lock) < 0) { giterr_set(GITERR_OS, "Unable to acquire index lock"); return -1; } + if (git_vector_size_hint(&index->entries, source_entries->length) < 0) { + git_mutex_unlock(&index->lock); + return -1; + } + git_vector_foreach(source_entries, i, source_entry) { git_index_entry *entry = NULL; diff --git a/src/vector.c b/src/vector.c index 93d09bb5b..a81d463ef 100644 --- a/src/vector.c +++ b/src/vector.c @@ -40,6 +40,13 @@ GIT_INLINE(int) resize_vector(git_vector *v, size_t new_size) return 0; } +int git_vector_size_hint(git_vector *v, size_t size_hint) +{ + if (v->_alloc_size >= size_hint) + return 0; + return resize_vector(v, size_hint); +} + int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp) { size_t bytes; diff --git a/src/vector.h b/src/vector.h index aac46c4b3..b7500ded3 100644 --- a/src/vector.h +++ b/src/vector.h @@ -32,6 +32,7 @@ void git_vector_free_deep(git_vector *v); /* free each entry and self */ void git_vector_clear(git_vector *v); int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp); void git_vector_swap(git_vector *a, git_vector *b); +int git_vector_size_hint(git_vector *v, size_t size_hint); void **git_vector_detach(size_t *size, size_t *asize, git_vector *v); From ef8b7febc5624c265201400001e3d654dea96d83 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 16 Dec 2015 19:36:50 +0100 Subject: [PATCH 318/450] index: Also size-hint the hash table Note that we're not checking whether the resize succeeds; in OOM cases, we let it run with a "small" vector and hash table and see if by chance we can grow it dynamically as we insert the new entries. Nothing to lose really. --- src/idxmap.h | 1 + src/index.c | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/idxmap.h b/src/idxmap.h index 74304bb97..4122a89fe 100644 --- a/src/idxmap.h +++ b/src/idxmap.h @@ -70,6 +70,7 @@ static kh_inline khint_t idxentry_hash(const git_index_entry *e) #define git_idxmap_valid_index(h, idx) (idx != kh_end(h)) #define git_idxmap_has_data(h, idx) kh_exist(h, idx) +#define git_idxmap_resize(h,s) kh_resize(idx, h, s) #define git_idxmap_free(h) kh_destroy(idx, h), h = NULL #define git_idxmap_clear(h) kh_clear(idx, h) diff --git a/src/index.c b/src/index.c index 03873f427..5c7bd90dd 100644 --- a/src/index.c +++ b/src/index.c @@ -1564,10 +1564,8 @@ int git_index__fill(git_index *index, const git_vector *source_entries) return -1; } - if (git_vector_size_hint(&index->entries, source_entries->length) < 0) { - git_mutex_unlock(&index->lock); - return -1; - } + git_vector_size_hint(&index->entries, source_entries->length); + git_idxmap_resize(index->entries_map, source_entries->length * 1.3); git_vector_foreach(source_entries, i, source_entry) { git_index_entry *entry = NULL; From 5951445fb3d85bfbe4ccc16ca01210081676e7c5 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Thu, 17 Dec 2015 10:13:04 +0100 Subject: [PATCH 319/450] commit: Fix memory leak in test suite --- tests/commit/commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/commit/commit.c b/tests/commit/commit.c index c82971f4b..c052cd568 100644 --- a/tests/commit/commit.c +++ b/tests/commit/commit.c @@ -72,7 +72,7 @@ void assert_commit_body(const char *expected, const char *given) dummy->raw_message = git__strdup(given); cl_assert_equal_s(expected, git_commit_body(dummy)); - git_commit_free(dummy); + git_commit__free(dummy); } void test_commit_commit__summary(void) From 1181ca0c6c3f0c786b3fb4ba52d66de2bc4689fd Mon Sep 17 00:00:00 2001 From: ReadmeCritic Date: Thu, 17 Dec 2015 08:36:04 -0800 Subject: [PATCH 320/450] Update README URLs based on HTTP redirects --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3191aeee2..eaa3c0bcd 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Additionally, the example code has been released to the public domain (see the * Website: [libgit2.github.com](http://libgit2.github.com) * StackOverflow Tag: [libgit2](http://stackoverflow.com/questions/tagged/libgit2) * Issues: [GitHub Issues](https://github.com/libgit2/libgit2/issues) (Right here!) -* API documentation: +* API documentation: * IRC: [#libgit2](irc://irc.freenode.net/libgit2) on irc.freenode.net. * Mailing list: The libgit2 mailing list was traditionally hosted in Librelist but has been deprecated. We encourage you to @@ -88,7 +88,7 @@ Under Unix-like systems, like Linux, \*BSD and Mac OS X, libgit2 expects `pthrea they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API for threading. -The `libgit2` library is built using [CMake]() (version 2.8 or newer) on all platforms. +The `libgit2` library is built using [CMake]() (version 2.8 or newer) on all platforms. On most systems you can build the library using the following commands @@ -103,7 +103,7 @@ To install the library you can specify the install prefix by setting: $ cmake .. -DCMAKE_INSTALL_PREFIX=/install/prefix $ cmake --build . --target install -For more advanced use or questions about CMake please read . +For more advanced use or questions about CMake please read . The following CMake variables are declared: @@ -141,7 +141,7 @@ You need to run the CMake commands from the Visual Studio command prompt, not the regular or Windows SDK one. Select the right generator for your version with the `-G "Visual Studio X" option. -See [the website](https://libgit2.github.com/docs/guides/build-and-link) +See [the website](http://libgit2.github.com/docs/guides/build-and-link/) for more detailed instructions. Android @@ -184,9 +184,9 @@ Here are the bindings to libgit2 that are currently available: * Go * git2go * GObject - * libgit2-glib + * libgit2-glib * Haskell - * hgit2 + * hgit2 * Java * Jagged * Julia @@ -197,7 +197,7 @@ Here are the bindings to libgit2 that are currently available: * libgit2sharp * Node.js * node-gitteh - * nodegit + * nodegit * Objective-C * objective-git * OCaml From 91f0d186b4efee8ccd5d0bbedf6b806a4f5e2a3a Mon Sep 17 00:00:00 2001 From: Dmitriy Olshevskiy Date: Mon, 21 Dec 2015 20:49:14 +0300 Subject: [PATCH 321/450] typos in comments --- src/tag.c | 2 +- tests/index/racy.c | 2 +- tests/submodule/lookup.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tag.c b/src/tag.c index 6e69d760d..c4bce1f22 100644 --- a/src/tag.c +++ b/src/tag.c @@ -358,7 +358,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu git_odb_object_free(target_obj); /** Ensure the tag name doesn't conflict with an already existing - * reference unless overwriting has explictly been requested **/ + * reference unless overwriting has explicitly been requested **/ if (error == 0 && !allow_ref_overwrite) { giterr_set(GITERR_TAG, "Tag already exists"); return GIT_EEXISTS; diff --git a/tests/index/racy.c b/tests/index/racy.c index a24d47a10..e2275ea14 100644 --- a/tests/index/racy.c +++ b/tests/index/racy.c @@ -79,7 +79,7 @@ void test_index_racy__write_index_just_after_file(void) cl_git_mkfile(path.ptr, "B"); /* - * Pretend this index' modification happend a second after the + * Pretend this index' modification happened a second after the * file update, and rewrite the file in that same second. */ times[0].tv_sec = index->stamp.mtime.tv_sec + 2; diff --git a/tests/submodule/lookup.c b/tests/submodule/lookup.c index 38e0fa314..148f9273e 100644 --- a/tests/submodule/lookup.c +++ b/tests/submodule/lookup.c @@ -357,7 +357,7 @@ void test_submodule_lookup__renamed(void) cl_git_pass(git_index_add_bypath(idx, ".gitmodules")); } - /* Change the worktree info in the the submodule's config */ + /* Change the worktree info in the submodule's config */ { git_config *cfg; From 22829b74435530774a0456ea2f5d469055605ec8 Mon Sep 17 00:00:00 2001 From: drusk Date: Wed, 23 Dec 2015 21:27:40 -0800 Subject: [PATCH 322/450] Fixed minor typo in README.md. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3191aeee2..53c5d4929 100644 --- a/README.md +++ b/README.md @@ -230,7 +230,7 @@ How Can I Contribute? ================================== Check the [contribution guidelines](CONTRIBUTING.md) to understand our -workflow, the libgit2 [coding conventions](CONVENTIONS.md), and out list of +workflow, the libgit2 [coding conventions](CONVENTIONS.md), and our list of [good starting projects](PROJECTS.md). License From 02fdc2db225b1ca21aab2000ba4a873f335d6707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Sat, 26 Dec 2015 17:17:05 +0100 Subject: [PATCH 323/450] ssh_stream_read(): fix possible *bytes_read < 0 branch Fix the possibility of returning successfully from ssh_stream_read() with *bytes_read < 0. This would occur if stdout channel read resulted in 0, and stderr channel read failed afterwards. --- src/transports/ssh.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index ffa4a24a7..239e0bae7 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -136,9 +136,14 @@ static int ssh_stream_read( * not-found error, so read from stderr and signal EOF on * stderr. */ - if (rc == 0 && (rc = libssh2_channel_read_stderr(s->channel, buffer, buf_size)) > 0) { - giterr_set(GITERR_SSH, "%*s", rc, buffer); - return GIT_EEOF; + if (rc == 0) { + if ((rc = libssh2_channel_read_stderr(s->channel, buffer, buf_size)) > 0) { + giterr_set(GITERR_SSH, "%*s", rc, buffer); + return GIT_EEOF; + } else if (rc < LIBSSH2_ERROR_NONE) { + ssh_error(s->session, "SSH could not read stderr"); + return -1; + } } From 1ce509fe9ead304709571bd505cdbf3829df008d Mon Sep 17 00:00:00 2001 From: ReadmeCritic Date: Sat, 26 Dec 2015 15:51:32 -0800 Subject: [PATCH 324/450] Update libgit2-glib link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eaa3c0bcd..8e6ad64ba 100644 --- a/README.md +++ b/README.md @@ -184,7 +184,7 @@ Here are the bindings to libgit2 that are currently available: * Go * git2go * GObject - * libgit2-glib + * libgit2-glib * Haskell * hgit2 * Java From 5c042c5bf53ce630de6c54683d2544b80be45dcc Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sat, 26 Dec 2015 22:06:45 -0600 Subject: [PATCH 325/450] repo::init tests: test init.templatedir setting Ensure that `git_repository_init` honors the `init.templatedir` configuration setting. --- tests/repo/init.c | 143 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 99 insertions(+), 44 deletions(-) diff --git a/tests/repo/init.c b/tests/repo/init.c index 7a9ec20f1..fb8b863c2 100644 --- a/tests/repo/init.c +++ b/tests/repo/init.c @@ -11,6 +11,7 @@ enum repo_mode { }; static git_repository *_repo = NULL; +static git_buf _global_path = GIT_BUF_INIT; static mode_t g_umask = 0; void test_repo_init__initialize(void) @@ -22,6 +23,16 @@ void test_repo_init__initialize(void) g_umask = p_umask(022); (void)p_umask(g_umask); } + + git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, + &_global_path); +} + +void test_repo_init__cleanup(void) +{ + git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, + _global_path.ptr); + git_buf_free(&_global_path); } static void cleanup_repository(void *path) @@ -527,13 +538,68 @@ static const char *template_sandbox(const char *name) return path; } -void test_repo_init__extended_with_template(void) +static void configure_templatedir(const char *template_path) { + git_buf config_path = GIT_BUF_INIT; + git_buf config_data = GIT_BUF_INIT; + + cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, + GIT_CONFIG_LEVEL_GLOBAL, &config_path)); + cl_git_pass(git_buf_puts(&config_path, ".tmp")); + cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, + GIT_CONFIG_LEVEL_GLOBAL, config_path.ptr)); + + cl_must_pass(p_mkdir(config_path.ptr, 0777)); + + cl_git_pass(git_buf_joinpath(&config_path, config_path.ptr, ".gitconfig")); + + cl_git_pass(git_buf_printf(&config_data, + "[init]\n\ttemplatedir = \"%s\"\n", template_path)); + + cl_git_mkfile(config_path.ptr, config_data.ptr); + + git_buf_free(&config_path); + git_buf_free(&config_data); +} + +static void validate_templates(git_repository *repo, const char *template_path) +{ + git_buf template_description = GIT_BUF_INIT; + git_buf repo_description = GIT_BUF_INIT; git_buf expected = GIT_BUF_INIT; git_buf actual = GIT_BUF_INIT; - git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; int filemode; + cl_git_pass(git_buf_joinpath(&template_description, template_path, + "description")); + cl_git_pass(git_buf_joinpath(&repo_description, git_repository_path(repo), + "description")); + + cl_git_pass(git_futils_readbuffer(&expected, template_description.ptr)); + cl_git_pass(git_futils_readbuffer(&actual, repo_description.ptr)); + + cl_assert_equal_s(expected.ptr, actual.ptr); + + filemode = cl_repo_get_bool(repo, "core.filemode"); + + assert_hooks_match( + template_path, git_repository_path(repo), + "hooks/update.sample", filemode); + + assert_hooks_match( + template_path, git_repository_path(repo), + "hooks/link.sample", filemode); + + git_buf_free(&expected); + git_buf_free(&actual); + git_buf_free(&repo_description); + git_buf_free(&template_description); +} + +void test_repo_init__external_templates_specified_in_options(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + cl_set_cleanup(&cleanup_repository, "templated.git"); template_sandbox("template"); @@ -547,32 +613,41 @@ void test_repo_init__extended_with_template(void) cl_assert(!git__suffixcmp(git_repository_path(_repo), "/templated.git/")); - cl_git_pass(git_futils_readbuffer(&expected, "template/description")); - cl_git_pass(git_futils_readbuffer( - &actual, "templated.git/description")); - - cl_assert_equal_s(expected.ptr, actual.ptr); - - git_buf_free(&expected); - git_buf_free(&actual); - - filemode = cl_repo_get_bool(_repo, "core.filemode"); - - assert_hooks_match( - "template", git_repository_path(_repo), - "hooks/update.sample", filemode); - - assert_hooks_match( - "template", git_repository_path(_repo), - "hooks/link.sample", filemode); - + validate_templates(_repo, "template"); cl_fixture_cleanup("template"); } +void test_repo_init__external_templates_specified_in_config(void) +{ + git_buf template_path = GIT_BUF_INIT; + + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + + cl_set_cleanup(&cleanup_repository, "templated.git"); + template_sandbox("template"); + + cl_git_pass(git_buf_joinpath(&template_path, clar_sandbox_path(), + "template")); + + configure_templatedir(template_path.ptr); + + opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE | + GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE; + + cl_git_pass(git_repository_init_ext(&_repo, "templated.git", &opts)); + + cl_assert(git_repository_is_bare(_repo)); + + cl_assert(!git__suffixcmp(git_repository_path(_repo), "/templated.git/")); + + validate_templates(_repo, "template"); + cl_fixture_cleanup("template"); + + git_buf_free(&template_path); +} + void test_repo_init__extended_with_template_and_shared_mode(void) { - git_buf expected = GIT_BUF_INIT; - git_buf actual = GIT_BUF_INIT; git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; int filemode = true; const char *repo_path = NULL; @@ -592,16 +667,6 @@ void test_repo_init__extended_with_template_and_shared_mode(void) filemode = cl_repo_get_bool(_repo, "core.filemode"); - cl_git_pass(git_futils_readbuffer( - &expected, "template/description")); - cl_git_pass(git_futils_readbuffer( - &actual, "init_shared_from_tpl/.git/description")); - - cl_assert_equal_s(expected.ptr, actual.ptr); - - git_buf_free(&expected); - git_buf_free(&actual); - repo_path = git_repository_path(_repo); assert_mode_seems_okay(repo_path, "hooks", GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode); @@ -610,17 +675,7 @@ void test_repo_init__extended_with_template_and_shared_mode(void) assert_mode_seems_okay(repo_path, "description", GIT_FILEMODE_BLOB, false, filemode); - /* for a non-symlinked hook, it should have shared permissions now */ - assert_hooks_match( - "template", git_repository_path(_repo), - "hooks/update.sample", filemode); - - /* for a symlinked hook, the permissions still should match the - * source link, not the GIT_REPOSITORY_INIT_SHARED_GROUP value - */ - assert_hooks_match( - "template", git_repository_path(_repo), - "hooks/link.sample", filemode); + validate_templates(_repo, "template"); cl_fixture_cleanup("template"); } From 002821837f0dd8d57c6c03b11159dff060cb1982 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sat, 26 Dec 2015 22:32:17 -0600 Subject: [PATCH 326/450] repo::init tests: test a template dir with leading dot Ensure that we can handle template directories that begin with a leading dot. --- tests/repo/init.c | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/tests/repo/init.c b/tests/repo/init.c index fb8b863c2..b06a81347 100644 --- a/tests/repo/init.c +++ b/tests/repo/init.c @@ -12,6 +12,7 @@ enum repo_mode { static git_repository *_repo = NULL; static git_buf _global_path = GIT_BUF_INIT; +static git_buf _tmp_path = GIT_BUF_INIT; static mode_t g_umask = 0; void test_repo_init__initialize(void) @@ -33,6 +34,10 @@ void test_repo_init__cleanup(void) git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, _global_path.ptr); git_buf_free(&_global_path); + + if (_tmp_path.size > 0 && git_path_isdir(_tmp_path.ptr)) + git_futils_rmdir_r(_tmp_path.ptr, NULL, GIT_RMDIR_REMOVE_FILES); + git_buf_free(&_tmp_path); } static void cleanup_repository(void *path) @@ -544,14 +549,14 @@ static void configure_templatedir(const char *template_path) git_buf config_data = GIT_BUF_INIT; cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, - GIT_CONFIG_LEVEL_GLOBAL, &config_path)); - cl_git_pass(git_buf_puts(&config_path, ".tmp")); + GIT_CONFIG_LEVEL_GLOBAL, &_tmp_path)); + cl_git_pass(git_buf_puts(&_tmp_path, ".tmp")); cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, - GIT_CONFIG_LEVEL_GLOBAL, config_path.ptr)); + GIT_CONFIG_LEVEL_GLOBAL, _tmp_path.ptr)); - cl_must_pass(p_mkdir(config_path.ptr, 0777)); + cl_must_pass(p_mkdir(_tmp_path.ptr, 0777)); - cl_git_pass(git_buf_joinpath(&config_path, config_path.ptr, ".gitconfig")); + cl_git_pass(git_buf_joinpath(&config_path, _tmp_path.ptr, ".gitconfig")); cl_git_pass(git_buf_printf(&config_data, "[init]\n\ttemplatedir = \"%s\"\n", template_path)); @@ -636,16 +641,39 @@ void test_repo_init__external_templates_specified_in_config(void) cl_git_pass(git_repository_init_ext(&_repo, "templated.git", &opts)); - cl_assert(git_repository_is_bare(_repo)); - - cl_assert(!git__suffixcmp(git_repository_path(_repo), "/templated.git/")); - validate_templates(_repo, "template"); cl_fixture_cleanup("template"); git_buf_free(&template_path); } +void test_repo_init__external_templates_with_leading_dot(void) +{ + git_buf template_path = GIT_BUF_INIT; + + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + + cl_set_cleanup(&cleanup_repository, "templated.git"); + template_sandbox("template"); + + cl_must_pass(p_rename("template", ".template_with_leading_dot")); + + cl_git_pass(git_buf_joinpath(&template_path, clar_sandbox_path(), + ".template_with_leading_dot")); + + configure_templatedir(template_path.ptr); + + opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE | + GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE; + + cl_git_pass(git_repository_init_ext(&_repo, "templated.git", &opts)); + + validate_templates(_repo, ".template_with_leading_dot"); + cl_fixture_cleanup(".template_with_leading_dot"); + + git_buf_free(&template_path); +} + void test_repo_init__extended_with_template_and_shared_mode(void) { git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; From 62602547db2301493cf9681e3a76016ceb1e0b8b Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sat, 26 Dec 2015 22:39:22 -0600 Subject: [PATCH 327/450] git_repository_init: include dotfiles when copying templates Include dotfiles when copying template directory, which will handle both a template directory itself that begins with a dotfile, and any dotfiles inside the directory. --- src/repository.c | 4 +++- tests/repo/init.c | 13 ++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/repository.c b/src/repository.c index 6234cd595..8a6fef0f6 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1438,7 +1438,9 @@ static int repo_init_structure( } if (tdir) { - uint32_t cpflags = GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_SIMPLE_TO_MODE; + uint32_t cpflags = GIT_CPDIR_COPY_SYMLINKS | + GIT_CPDIR_SIMPLE_TO_MODE | + GIT_CPDIR_COPY_DOTFILES; if (opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK) cpflags |= GIT_CPDIR_CHMOD_DIRS; error = git_futils_cp_r(tdir, repo_dir, cpflags, dmode); diff --git a/tests/repo/init.c b/tests/repo/init.c index b06a81347..04d4a5c5e 100644 --- a/tests/repo/init.c +++ b/tests/repo/init.c @@ -519,7 +519,8 @@ static void assert_mode_seems_okay( static const char *template_sandbox(const char *name) { - git_buf hooks_path = GIT_BUF_INIT, link_path = GIT_BUF_INIT; + git_buf hooks_path = GIT_BUF_INIT, link_path = GIT_BUF_INIT, + dotfile_path = GIT_BUF_INIT; const char *path = cl_fixture(name); cl_fixture_sandbox(name); @@ -537,6 +538,12 @@ static const char *template_sandbox(const char *name) cl_must_pass(symlink("update.sample", link_path.ptr)); #endif + /* create a file starting with a dot */ + cl_git_pass(git_buf_joinpath(&dotfile_path, hooks_path.ptr, ".dotfile")); + cl_git_mkfile(dotfile_path.ptr, "something\n"); + git_buf_free(&dotfile_path); + + git_buf_free(&dotfile_path); git_buf_free(&link_path); git_buf_free(&hooks_path); @@ -595,6 +602,10 @@ static void validate_templates(git_repository *repo, const char *template_path) template_path, git_repository_path(repo), "hooks/link.sample", filemode); + assert_hooks_match( + template_path, git_repository_path(repo), + "hooks/.dotfile", filemode); + git_buf_free(&expected); git_buf_free(&actual); git_buf_free(&repo_description); From 9d81509ab16f26dcf2cdf0e4b5c0d0006f30b53a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 23 Dec 2015 11:54:52 +0000 Subject: [PATCH 328/450] index: get rid of the locking We don't support using an index object from multiple threads at the same time, so the locking doesn't have any effect when following the rules. If not following the rules, things are going to break down anyway. --- src/index.c | 145 ++++++---------------------------------------------- src/index.h | 1 - 2 files changed, 15 insertions(+), 131 deletions(-) diff --git a/src/index.c b/src/index.c index 5c7bd90dd..081a1ea59 100644 --- a/src/index.c +++ b/src/index.c @@ -356,28 +356,6 @@ static unsigned int index_merge_mode( return git_index__create_mode(mode); } -static int index_sort_if_needed(git_index *index, bool need_lock) -{ - /* not truly threadsafe because between when this checks and/or - * sorts the array another thread could come in and unsort it - */ - - if (git_vector_is_sorted(&index->entries)) - return 0; - - if (need_lock && git_mutex_lock(&index->lock) < 0) { - giterr_set(GITERR_OS, "Unable to lock index"); - return -1; - } - - git_vector_sort(&index->entries); - - if (need_lock) - git_mutex_unlock(&index->lock); - - return 0; -} - GIT_INLINE(int) index_find_in_entries( size_t *out, git_vector *entries, git_vector_cmp entry_srch, const char *path, size_t path_len, int stage) @@ -391,10 +369,9 @@ GIT_INLINE(int) index_find_in_entries( GIT_INLINE(int) index_find( size_t *out, git_index *index, - const char *path, size_t path_len, int stage, bool need_lock) + const char *path, size_t path_len, int stage) { - if (index_sort_if_needed(index, need_lock) < 0) - return -1; + git_vector_sort(&index->entries); return index_find_in_entries( out, &index->entries, index->entries_search, path, path_len, stage); @@ -418,7 +395,7 @@ void git_index__set_ignore_case(git_index *index, bool ignore_case) git_vector_set_cmp(&index->entries, ignore_case ? git_index_entry_icmp : git_index_entry_cmp); - index_sort_if_needed(index, true); + git_vector_sort(&index->entries); git_vector_set_cmp(&index->reuc, ignore_case ? reuc_icmp : reuc_cmp); git_vector_sort(&index->reuc); @@ -434,12 +411,6 @@ int git_index_open(git_index **index_out, const char *index_path) index = git__calloc(1, sizeof(git_index)); GITERR_CHECK_ALLOC(index); - if (git_mutex_init(&index->lock)) { - giterr_set(GITERR_OS, "Failed to initialize lock"); - git__free(index); - return -1; - } - git_pool_init(&index->tree_pool, 1); if (index_path != NULL) { @@ -498,7 +469,6 @@ static void index_free(git_index *index) git_vector_free(&index->deleted); git__free(index->index_file_path); - git_mutex_free(&index->lock); git__memzero(index, sizeof(*index)); git__free(index); @@ -561,11 +531,6 @@ int git_index_clear(git_index *index) index->tree = NULL; git_pool_clear(&index->tree_pool); - if (git_mutex_lock(&index->lock) < 0) { - giterr_set(GITERR_OS, "Failed to lock index"); - return -1; - } - git_idxmap_clear(index->entries_map); while (!error && index->entries.length > 0) error = index_remove_entry(index, index->entries.length - 1); @@ -576,8 +541,6 @@ int git_index_clear(git_index *index) git_futils_filestamp_set(&index->stamp, NULL); - git_mutex_unlock(&index->lock); - return error; } @@ -836,8 +799,7 @@ const git_index_entry *git_index_get_byindex( git_index *index, size_t n) { assert(index); - if (index_sort_if_needed(index, true) < 0) - return NULL; + git_vector_sort(&index->entries); return git_vector_get(&index->entries, n); } @@ -1094,7 +1056,7 @@ static int has_dir_name(git_index *index, } len = slash - name; - if (!index_find(&pos, index, name, len, stage, false)) { + if (!index_find(&pos, index, name, len, stage)) { retval = -1; if (!ok_to_replace) break; @@ -1231,7 +1193,7 @@ static void index_existing_and_best( int error; error = index_find(&pos, - index, entry->path, 0, GIT_IDXENTRY_STAGE(entry), false); + index, entry->path, 0, GIT_IDXENTRY_STAGE(entry)); if (error == 0) { *existing = index->entries.contents[pos]; @@ -1296,11 +1258,6 @@ static int index_insert( /* this entry is now up-to-date and should not be checked for raciness */ entry->flags_extended |= GIT_IDXENTRY_UPTODATE; - if (git_mutex_lock(&index->lock) < 0) { - giterr_set(GITERR_OS, "Unable to acquire index lock"); - return -1; - } - git_vector_sort(&index->entries); /* look if an entry with this path already exists, either staged, or (if @@ -1355,8 +1312,6 @@ static int index_insert( *entry_ptr = NULL; } - git_mutex_unlock(&index->lock); - return error; } @@ -1559,11 +1514,6 @@ int git_index__fill(git_index *index, const git_vector *source_entries) if (!source_entries->length) return 0; - if (git_mutex_lock(&index->lock) < 0) { - giterr_set(GITERR_OS, "Unable to acquire index lock"); - return -1; - } - git_vector_size_hint(&index->entries, source_entries->length); git_idxmap_resize(index->entries_map, source_entries->length * 1.3); @@ -1588,7 +1538,6 @@ int git_index__fill(git_index *index, const git_vector *source_entries) if (!ret) git_vector_sort(&index->entries); - git_mutex_unlock(&index->lock); return ret; } @@ -1619,17 +1568,12 @@ int git_index_remove(git_index *index, const char *path, int stage) size_t position; git_index_entry remove_key = {{ 0 }}; - if (git_mutex_lock(&index->lock) < 0) { - giterr_set(GITERR_OS, "Failed to lock index"); - return -1; - } - remove_key.path = path; GIT_IDXENTRY_STAGE_SET(&remove_key, stage); DELETE_IN_MAP(index, &remove_key); - if (index_find(&position, index, path, 0, stage, false) < 0) { + if (index_find(&position, index, path, 0, stage) < 0) { giterr_set( GITERR_INDEX, "Index does not contain %s at stage %d", path, stage); error = GIT_ENOTFOUND; @@ -1637,7 +1581,6 @@ int git_index_remove(git_index *index, const char *path, int stage) error = index_remove_entry(index, position); } - git_mutex_unlock(&index->lock); return error; } @@ -1648,14 +1591,9 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage) size_t pos; git_index_entry *entry; - if (git_mutex_lock(&index->lock) < 0) { - giterr_set(GITERR_OS, "Failed to lock index"); - return -1; - } - if (!(error = git_buf_sets(&pfx, dir)) && !(error = git_path_to_dir(&pfx))) - index_find(&pos, index, pfx.ptr, pfx.size, GIT_INDEX_STAGE_ANY, false); + index_find(&pos, index, pfx.ptr, pfx.size, GIT_INDEX_STAGE_ANY); while (!error) { entry = git_vector_get(&index->entries, pos); @@ -1672,7 +1610,6 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage) /* removed entry at 'pos' so we don't need to increment */ } - git_mutex_unlock(&index->lock); git_buf_free(&pfx); return error; @@ -1684,12 +1621,7 @@ int git_index_find_prefix(size_t *at_pos, git_index *index, const char *prefix) size_t pos; const git_index_entry *entry; - if (git_mutex_lock(&index->lock) < 0) { - giterr_set(GITERR_OS, "Failed to lock index"); - return -1; - } - - index_find(&pos, index, prefix, strlen(prefix), GIT_INDEX_STAGE_ANY, false); + index_find(&pos, index, prefix, strlen(prefix), GIT_INDEX_STAGE_ANY); entry = git_vector_get(&index->entries, pos); if (!entry || git__prefixcmp(entry->path, prefix) != 0) error = GIT_ENOTFOUND; @@ -1697,8 +1629,6 @@ int git_index_find_prefix(size_t *at_pos, git_index *index, const char *prefix) if (!error && at_pos) *at_pos = pos; - git_mutex_unlock(&index->lock); - return error; } @@ -1706,7 +1636,7 @@ int git_index__find_pos( size_t *out, git_index *index, const char *path, size_t path_len, int stage) { assert(index && path); - return index_find(out, index, path, path_len, stage, true); + return index_find(out, index, path, path_len, stage); } int git_index_find(size_t *at_pos, git_index *index, const char *path) @@ -1715,14 +1645,8 @@ int git_index_find(size_t *at_pos, git_index *index, const char *path) assert(index && path); - if (git_mutex_lock(&index->lock) < 0) { - giterr_set(GITERR_OS, "Failed to lock index"); - return -1; - } - if (git_vector_bsearch2( &pos, &index->entries, index->entries_search_path, path) < 0) { - git_mutex_unlock(&index->lock); giterr_set(GITERR_INDEX, "Index does not contain %s", path); return GIT_ENOTFOUND; } @@ -1740,7 +1664,6 @@ int git_index_find(size_t *at_pos, git_index *index, const char *path) if (at_pos) *at_pos = pos; - git_mutex_unlock(&index->lock); return 0; } @@ -1896,11 +1819,6 @@ static int index_conflict_remove(git_index *index, const char *path) if (path != NULL && git_index_find(&pos, index, path) < 0) return GIT_ENOTFOUND; - if (git_mutex_lock(&index->lock) < 0) { - giterr_set(GITERR_OS, "Unable to lock index"); - return -1; - } - while ((conflict_entry = git_vector_get(&index->entries, pos)) != NULL) { if (path != NULL && @@ -1916,8 +1834,6 @@ static int index_conflict_remove(git_index *index, const char *path) break; } - git_mutex_unlock(&index->lock); - return error; } @@ -2456,11 +2372,6 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) seek_forward(INDEX_HEADER_SIZE); - if (git_mutex_lock(&index->lock) < 0) { - giterr_set(GITERR_OS, "Unable to acquire index lock"); - return -1; - } - assert(!index->entries.length); if (index->ignore_case) @@ -2490,6 +2401,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) index_entry_free(entry); goto done; } + error = 0; seek_forward(entry_size); } @@ -2537,10 +2449,9 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) * in-memory index is supposed to be case-insensitive */ git_vector_set_sorted(&index->entries, !index->ignore_case); - error = index_sort_if_needed(index, false); + git_vector_sort(&index->entries); done: - git_mutex_unlock(&index->lock); return error; } @@ -2630,11 +2541,6 @@ static int write_entries(git_index *index, git_filebuf *file) git_vector case_sorted, *entries; git_index_entry *entry; - if (git_mutex_lock(&index->lock) < 0) { - giterr_set(GITERR_OS, "Failed to lock index"); - return -1; - } - /* If index->entries is sorted case-insensitively, then we need * to re-sort it case-sensitively before writing */ if (index->ignore_case) { @@ -2649,8 +2555,6 @@ static int write_entries(git_index *index, git_filebuf *file) if ((error = write_disk_entry(file, entry)) < 0) break; - git_mutex_unlock(&index->lock); - if (index->ignore_case) git_vector_free(&case_sorted); @@ -2935,8 +2839,7 @@ int git_index_read_tree(git_index *index, const git_tree *tree) index->tree = NULL; git_pool_clear(&index->tree_pool); - if (index_sort_if_needed(index, true) < 0) - return -1; + git_vector_sort(&index->entries); if ((error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data)) < 0) goto cleanup; @@ -2959,15 +2862,11 @@ int git_index_read_tree(git_index *index, const git_tree *tree) git_vector_sort(&entries); - if ((error = git_index_clear(index)) < 0) + if ((error = git_index_clear(index)) < 0) { /* well, this isn't good */; - else if (git_mutex_lock(&index->lock) < 0) { - giterr_set(GITERR_OS, "Unable to acquire index lock"); - error = -1; } else { git_vector_swap(&entries, &index->entries); entries_map = git__swap(index->entries_map, entries_map); - git_mutex_unlock(&index->lock); } cleanup: @@ -3367,18 +3266,11 @@ int git_index_snapshot_new(git_vector *snap, git_index *index) GIT_REFCOUNT_INC(index); - if (git_mutex_lock(&index->lock) < 0) { - giterr_set(GITERR_OS, "Failed to lock index"); - return -1; - } - git_atomic_inc(&index->readers); git_vector_sort(&index->entries); error = git_vector_dup(snap, &index->entries, index->entries._cmp); - git_mutex_unlock(&index->lock); - if (error < 0) git_index_free(index); @@ -3391,11 +3283,6 @@ void git_index_snapshot_release(git_vector *snap, git_index *index) git_atomic_dec(&index->readers); - if (!git_mutex_lock(&index->lock)) { - index_free_deleted(index); /* try to free pending deleted items */ - git_mutex_unlock(&index->lock); - } - git_index_free(index); } @@ -3460,9 +3347,7 @@ int git_indexwriter_commit(git_indexwriter *writer) if (!writer->should_write) return 0; - if (index_sort_if_needed(writer->index, true) < 0) - return -1; - + git_vector_sort(&writer->index->entries); git_vector_sort(&writer->index->reuc); if ((error = write_index(&checksum, writer->index, &writer->file)) < 0) { diff --git a/src/index.h b/src/index.h index 0909da889..a64c645b3 100644 --- a/src/index.h +++ b/src/index.h @@ -28,7 +28,6 @@ struct git_index { git_vector entries; git_idxmap *entries_map; - git_mutex lock; /* lock held while entries is being changed */ git_vector deleted; /* deleted entries if readers > 0 */ git_atomic readers; /* number of active iterators */ From ea5942b4cfb56eeb7375e3af2ec76545f551b4df Mon Sep 17 00:00:00 2001 From: Sebastian Bauer Date: Thu, 31 Dec 2015 11:12:57 +0100 Subject: [PATCH 329/450] Bail out early when no memory is available. --- src/posix.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/posix.c b/src/posix.c index 8d86aa8bf..de9181a42 100644 --- a/src/posix.c +++ b/src/posix.c @@ -62,7 +62,10 @@ int p_getaddrinfo( ai = ainfo; for (p = 1; ainfo->ai_hostent->h_addr_list[p] != NULL; p++) { - ai->ai_next = malloc(sizeof(struct addrinfo)); + if (!(ai->ai_next = malloc(sizeof(struct addrinfo)))) { + p_freeaddrinfo(ainfo); + return -1; + } memcpy(&ai->ai_next, ainfo, sizeof(struct addrinfo)); memcpy(&ai->ai_next->ai_addr_in.sin_addr, ainfo->ai_hostent->h_addr_list[p], From 9f9df4b6e05cc5950953177a1cb48739f1ddfc29 Mon Sep 17 00:00:00 2001 From: Sebastian Bauer Date: Thu, 31 Dec 2015 11:13:21 +0100 Subject: [PATCH 330/450] Copy into the correct destination. --- src/posix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/posix.c b/src/posix.c index de9181a42..c7201ba14 100644 --- a/src/posix.c +++ b/src/posix.c @@ -66,7 +66,7 @@ int p_getaddrinfo( p_freeaddrinfo(ainfo); return -1; } - memcpy(&ai->ai_next, ainfo, sizeof(struct addrinfo)); + memcpy(ai->ai_next, ainfo, sizeof(struct addrinfo)); memcpy(&ai->ai_next->ai_addr_in.sin_addr, ainfo->ai_hostent->h_addr_list[p], ainfo->ai_hostent->h_length); From 8a0133c030074892815ef91de802388492dad17c Mon Sep 17 00:00:00 2001 From: Arthur Schreiber Date: Tue, 5 Jan 2016 19:07:27 +0100 Subject: [PATCH 331/450] Add winhttp dependencies to pc file. --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 40a52bc01..0aa58625a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,7 +257,8 @@ IF (WIN32 AND WINHTTP) LINK_DIRECTORIES(${LIBWINHTTP_PATH}) ENDIF () - LINK_LIBRARIES(winhttp rpcrt4 crypt32) + LINK_LIBRARIES(winhttp rpcrt4 crypt32 ole32) + LIST(APPEND LIBGIT2_PC_LIBS "-lwinhttp" "-lrpcrt4" "-lcrypt32" "-lole32") ELSE () IF (CURL) PKG_CHECK_MODULES(CURL libcurl) From 768e185c54164a66fc4e2dbe7c58097eff65ebdf Mon Sep 17 00:00:00 2001 From: Arthur Schreiber Date: Tue, 5 Jan 2016 21:55:11 +0100 Subject: [PATCH 332/450] Wrap path in quotes to support paths containing whitespace. --- libgit2.pc.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgit2.pc.in b/libgit2.pc.in index 880266a30..329a560a7 100644 --- a/libgit2.pc.in +++ b/libgit2.pc.in @@ -6,7 +6,7 @@ Name: libgit2 Description: The git library, take 2 Version: @LIBGIT2_VERSION_STRING@ -Libs: -L${libdir} -lgit2 +Libs: -L"${libdir}" -lgit2 Libs.private: @LIBGIT2_PC_LIBS@ Requires.private: @LIBGIT2_PC_REQUIRES@ From ed0571f89577d77de6c042454f61f9c055230095 Mon Sep 17 00:00:00 2001 From: Ross Delinger Date: Tue, 12 Jan 2016 16:08:38 -0500 Subject: [PATCH 333/450] Add a new build flag to disable the pool allocator and pass all git_pool_malloc calls straight to git__malloc --- CMakeLists.txt | 5 +++ src/pool.c | 108 +++++++++++++++++++++++++++++++++------------- src/pool.h | 31 +++++++++++++ tests/core/pool.c | 6 +++ 4 files changed, 120 insertions(+), 30 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 40a52bc01..7e5a88918 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,11 @@ OPTION( USE_SSH "Link with libssh to enable SSH support" ON ) OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF ) OPTION( VALGRIND "Configure build for valgrind" OFF ) OPTION( CURL "User curl for HTTP if available" ON) +OPTION( DEBUG_POOL "Enable debug pool allocator" OFF ) + +IF(DEBUG_POOL) + ADD_DEFINITIONS(-DGIT_DEBUG_POOL) +ENDIF() IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") SET( USE_ICONV ON ) diff --git a/src/pool.c b/src/pool.c index e519b75bb..b4fc50fca 100644 --- a/src/pool.c +++ b/src/pool.c @@ -28,6 +28,7 @@ uint32_t git_pool__system_page_size(void) return size; } +#ifndef GIT_DEBUG_POOL void git_pool_init(git_pool *pool, uint32_t item_size) { assert(pool); @@ -50,18 +51,6 @@ void git_pool_clear(git_pool *pool) pool->pages = NULL; } -void git_pool_swap(git_pool *a, git_pool *b) -{ - git_pool temp; - - if (a == b) - return; - - memcpy(&temp, a, sizeof(temp)); - memcpy(a, b, sizeof(temp)); - memcpy(b, &temp, sizeof(temp)); -} - static void *pool_alloc_page(git_pool *pool, uint32_t size) { git_pool_page *page; @@ -95,6 +84,83 @@ static void *pool_alloc(git_pool *pool, uint32_t size) return ptr; } +uint32_t git_pool__open_pages(git_pool *pool) +{ + uint32_t ct = 0; + git_pool_page *scan; + for (scan = pool->pages; scan != NULL; scan = scan->next) ct++; + return ct; +} + +bool git_pool__ptr_in_pool(git_pool *pool, void *ptr) +{ + git_pool_page *scan; + for (scan = pool->pages; scan != NULL; scan = scan->next) + if ((void *)scan->data <= ptr && + (void *)(((char *)scan->data) + scan->size) > ptr) + return true; + return false; +} + +#else + +static int git_pool__ptr_cmp(const void * a, const void * b) +{ + if(a > b) { + return 1; + } + if(a < b) { + return -1; + } + else { + return 0; + } +} + +void git_pool_init(git_pool *pool, uint32_t item_size) +{ + assert(pool); + assert(item_size >= 1); + + memset(pool, 0, sizeof(git_pool)); + pool->item_size = item_size; + pool->page_size = git_pool__system_page_size(); + git_vector_init(&pool->allocations, 100, git_pool__ptr_cmp); +} + +void git_pool_clear(git_pool *pool) +{ + git_vector_free_deep(&pool->allocations); +} + +static void *pool_alloc(git_pool *pool, uint32_t size) { + void *ptr = NULL; + if((ptr = git__malloc(size)) == NULL) { + return NULL; + } + git_vector_insert_sorted(&pool->allocations, ptr, NULL); + return ptr; +} + +bool git_pool__ptr_in_pool(git_pool *pool, void *ptr) +{ + size_t pos; + return git_vector_bsearch(&pos, &pool->allocations, ptr) != GIT_ENOTFOUND; +} +#endif + +void git_pool_swap(git_pool *a, git_pool *b) +{ + git_pool temp; + + if (a == b) + return; + + memcpy(&temp, a, sizeof(temp)); + memcpy(a, b, sizeof(temp)); + memcpy(b, &temp, sizeof(temp)); +} + static uint32_t alloc_size(git_pool *pool, uint32_t count) { const uint32_t align = sizeof(void *) - 1; @@ -168,21 +234,3 @@ char *git_pool_strcat(git_pool *pool, const char *a, const char *b) } return ptr; } - -uint32_t git_pool__open_pages(git_pool *pool) -{ - uint32_t ct = 0; - git_pool_page *scan; - for (scan = pool->pages; scan != NULL; scan = scan->next) ct++; - return ct; -} - -bool git_pool__ptr_in_pool(git_pool *pool, void *ptr) -{ - git_pool_page *scan; - for (scan = pool->pages; scan != NULL; scan = scan->next) - if ((void *)scan->data <= ptr && - (void *)(((char *)scan->data) + scan->size) > ptr) - return true; - return false; -} diff --git a/src/pool.h b/src/pool.h index d16bd349a..0f9532db3 100644 --- a/src/pool.h +++ b/src/pool.h @@ -9,8 +9,13 @@ #include "common.h" +#ifdef GIT_DEBUG_POOL +#include "vector.h" +#endif + typedef struct git_pool_page git_pool_page; +#ifndef GIT_DEBUG_POOL /** * Chunked allocator. * @@ -33,6 +38,30 @@ typedef struct { uint32_t page_size; /* size of page in bytes */ } git_pool; +#else + +/** + * Debug chunked allocator. + * + * Acts just like `git_pool` but instead of actually pooling allocations it + * passes them through to `git__malloc`. This makes it possible to easily debug + * systems that use `git_pool` using valgrind. + * + * In order to track allocations during the lifetime of the pool we use a + * `git_vector`. When the pool is deallocated everything in the vector is + * freed. + * + * `API is exactly the same as the standard `git_pool` with one exception. + * Since we aren't allocating pages to hand out in chunks we can't easily + * implement `git_pool__open_pages`. + */ +typedef struct { + git_vector allocations; + uint32_t item_size; + uint32_t page_size; +} git_pool; +#endif + /** * Initialize a pool. * @@ -98,7 +127,9 @@ extern char *git_pool_strcat(git_pool *pool, const char *a, const char *b); /* * Misc utilities */ +#ifndef _DEBUG_POOL extern uint32_t git_pool__open_pages(git_pool *pool); +#endif extern bool git_pool__ptr_in_pool(git_pool *pool, void *ptr); #endif diff --git a/tests/core/pool.c b/tests/core/pool.c index c43c1db67..b07da0abd 100644 --- a/tests/core/pool.c +++ b/tests/core/pool.c @@ -31,8 +31,10 @@ void test_core_pool__1(void) for (i = 2010; i > 0; i--) cl_assert(git_pool_malloc(&p, i) != NULL); +#ifndef GIT_DEBUG_POOL /* with fixed page size, allocation must end up with these values */ cl_assert_equal_i(591, git_pool__open_pages(&p)); +#endif git_pool_clear(&p); git_pool_init(&p, 1); @@ -41,8 +43,10 @@ void test_core_pool__1(void) for (i = 2010; i > 0; i--) cl_assert(git_pool_malloc(&p, i) != NULL); +#ifndef GIT_DEBUG_POOL /* with fixed page size, allocation must end up with these values */ cl_assert_equal_i(sizeof(void *) == 8 ? 575 : 573, git_pool__open_pages(&p)); +#endif git_pool_clear(&p); } @@ -69,8 +73,10 @@ void test_core_pool__2(void) cl_git_pass(git_oid_fromstr(oid, oid_hex)); } +#ifndef GIT_DEBUG_POOL /* with fixed page size, allocation must end up with these values */ cl_assert_equal_i(sizeof(void *) == 8 ? 55 : 45, git_pool__open_pages(&p)); +#endif git_pool_clear(&p); } From b644e223aa04c49f7cc33fa1a4a26b2dbaf7aaf7 Mon Sep 17 00:00:00 2001 From: "P.S.V.R" Date: Wed, 13 Jan 2016 11:02:38 +0800 Subject: [PATCH 334/450] Make packfile_unpack_compressed a private API --- src/pack.c | 4 ++-- src/pack.h | 7 ------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/pack.c b/src/pack.c index 45dd4d5be..f6cb3a548 100644 --- a/src/pack.c +++ b/src/pack.c @@ -21,7 +21,7 @@ GIT__USE_OIDMAP static int packfile_open(struct git_pack_file *p); static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n); -int packfile_unpack_compressed( +static int packfile_unpack_compressed( git_rawobj *obj, struct git_pack_file *p, git_mwindow **w_curs, @@ -843,7 +843,7 @@ void git_packfile_stream_free(git_packfile_stream *obj) inflateEnd(&obj->zstream); } -int packfile_unpack_compressed( +static int packfile_unpack_compressed( git_rawobj *obj, struct git_pack_file *p, git_mwindow **w_curs, diff --git a/src/pack.h b/src/pack.h index b3d5b2993..d15247b74 100644 --- a/src/pack.h +++ b/src/pack.h @@ -138,13 +138,6 @@ int git_packfile_resolve_header( git_off_t offset); int git_packfile_unpack(git_rawobj *obj, struct git_pack_file *p, git_off_t *obj_offset); -int packfile_unpack_compressed( - git_rawobj *obj, - struct git_pack_file *p, - git_mwindow **w_curs, - git_off_t *curpos, - size_t size, - git_otype type); int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p, git_off_t curpos); ssize_t git_packfile_stream_read(git_packfile_stream *obj, void *buffer, size_t len); From 8bd1c19e767c85f4e2cc035068bb3146247e27fb Mon Sep 17 00:00:00 2001 From: Bob Kuo Date: Sat, 16 Jan 2016 19:39:24 -0600 Subject: [PATCH 335/450] Free allocated pointer to curl stream on error --- src/curl_stream.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/curl_stream.c b/src/curl_stream.c index 798bd5a52..9f8b202a8 100644 --- a/src/curl_stream.c +++ b/src/curl_stream.c @@ -207,11 +207,14 @@ int git_curl_stream_new(git_stream **out, const char *host, const char *port) handle = curl_easy_init(); if (handle == NULL) { giterr_set(GITERR_NET, "failed to create curl handle"); + git__free(st); return -1; } - if ((error = git__strtol32(&iport, port, NULL, 10)) < 0) + if ((error = git__strtol32(&iport, port, NULL, 10)) < 0) { + git__free(st); return error; + } curl_easy_setopt(handle, CURLOPT_URL, host); curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, st->curl_error); From 5c7f2f01886f95b796770bf1cdf62ee3dd701862 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 19 Jan 2016 11:13:23 -0600 Subject: [PATCH 336/450] winhttp: name mangle class / iid on mingw Standard Windows type systems define CLSID_InternetSecurityManager and IID_IInternetSecurityManager, but MinGW lacks these definitions. As a result, we must hardcode these definitions ourselves. However, we should not use a public struct with those names, lest another library do the same thing and consumers cannot link to both. --- src/transports/winhttp.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index 9e97a279d..3750f6e32 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -53,10 +53,15 @@ static const int no_check_cert_flags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_UNKNOWN_CA; #if defined(__MINGW32__) -const CLSID CLSID_InternetSecurityManager = { 0x7B8A2D94, 0x0AC9, 0x11D1, +static const CLSID CLSID_InternetSecurityManager_mingw = + { 0x7B8A2D94, 0x0AC9, 0x11D1, { 0x89, 0x6C, 0x00, 0xC0, 0x4F, 0xB6, 0xBF, 0xC4 } }; -const IID IID_IInternetSecurityManager = { 0x79EAC9EE, 0xBAF9, 0x11CE, +static const IID IID_IInternetSecurityManager_mingw = + { 0x79EAC9EE, 0xBAF9, 0x11CE, { 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B } }; + +# define CLSID_InternetSecurityManager CLSID_InternetSecurityManager_mingw +# define IID_IInternetSecurityManager IID_IInternetSecurityManager_mingw #endif #define OWNING_SUBTRANSPORT(s) ((winhttp_subtransport *)(s)->parent.subtransport) From d02720d8a653bc564b84c27bfa01a6876bf5bec2 Mon Sep 17 00:00:00 2001 From: jbreeden Date: Sat, 23 Jan 2016 17:13:25 -0800 Subject: [PATCH 337/450] Export git_stash_apply_init_options --- include/git2/stash.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/git2/stash.h b/include/git2/stash.h index b321dc34e..733d75a7f 100644 --- a/include/git2/stash.h +++ b/include/git2/stash.h @@ -150,7 +150,7 @@ typedef struct git_stash_apply_options { * `GIT_STASH_APPLY_OPTIONS_INIT` here. * @return Zero on success; -1 on failure. */ -int git_stash_apply_init_options( +GIT_EXTERN(int) git_stash_apply_init_options( git_stash_apply_options *opts, unsigned int version); /** From 0c09753cf50bbd9639c50af61b1cedf50da5f1c6 Mon Sep 17 00:00:00 2001 From: Marius Ungureanu Date: Mon, 25 Jan 2016 14:06:15 +0200 Subject: [PATCH 338/450] Fix the build when defining USE_NSEC --- src/index.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.h b/src/index.h index a64c645b3..8b9b49498 100644 --- a/src/index.h +++ b/src/index.h @@ -92,7 +92,7 @@ GIT_INLINE(bool) git_index_entry_newer_than_index( /* If the timestamp is the same or newer than the index, it's racy */ #if defined(GIT_USE_NSEC) - if ((int32_t)index->stamp.tv_sec < entry->mtime.seconds) + if ((int32_t)index->stamp.mtime.tv_sec < entry->mtime.seconds) return true; else if ((int32_t)index->stamp.mtime.tv_sec > entry->mtime.seconds) return false; From 846632cdfed81ccc1a5d73166367c980e8aae966 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 2 Feb 2016 14:49:25 -0800 Subject: [PATCH 339/450] Introduce Contributor Covenant --- CODE_OF_CONDUCT.md | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..0a0e4ebab --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,75 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at [libgit2@gmail.com][email]. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[email]: mailto:libgit2@gmail.com +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ From 1087e6be88bd43be8ce46975258758af224171f9 Mon Sep 17 00:00:00 2001 From: Nicolas Vanheuverzwijn Date: Wed, 3 Feb 2016 17:27:04 -0500 Subject: [PATCH 340/450] Fix a typo in documentation --- include/git2/rebase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/git2/rebase.h b/include/git2/rebase.h index d9aa175c7..cf1b00fd9 100644 --- a/include/git2/rebase.h +++ b/include/git2/rebase.h @@ -41,7 +41,7 @@ typedef struct { /** * Used by `git_rebase_finish`, this is the name of the notes reference * used to rewrite notes for rebased commits when finishing the rebase; - * if NULL, the contents of the coniguration option `notes.rewriteRef` + * if NULL, the contents of the configuration option `notes.rewriteRef` * is examined, unless the configuration option `notes.rewrite.rebase` * is set to false. If `notes.rewriteRef` is also NULL, notes will * not be rewritten. From 804bcd6b959f05777c7bb0777554733687293885 Mon Sep 17 00:00:00 2001 From: Ephemera Date: Fri, 5 Feb 2016 01:59:07 +0900 Subject: [PATCH 341/450] Fix typo --- include/git2/diff.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index 3eb265652..c35701a46 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -1194,7 +1194,7 @@ typedef enum { } git_diff_stats_format_t; /** - * Accumlate diff statistics for all patches. + * Accumulate diff statistics for all patches. * * @param out Structure containg the diff statistics. * @param diff A git_diff generated by one of the above functions. From fac42ff9426fccff4a6781fc5678b12e18326fe8 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 8 Feb 2016 16:58:08 +0100 Subject: [PATCH 342/450] merge: fix memory leak --- src/merge.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/merge.c b/src/merge.c index 61ff93c19..70c705af6 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1985,9 +1985,6 @@ static int create_virtual_base( git_index *index = NULL; git_merge_options virtual_opts = GIT_MERGE_OPTIONS_INIT; - result = git__calloc(1, sizeof(git_annotated_commit)); - GITERR_CHECK_ALLOC(result); - /* Conflicts in the merge base creation do not propagate to conflicts * in the result; the conflicted base will act as the common ancestor. */ @@ -2001,6 +1998,8 @@ static int create_virtual_base( recursion_level + 1, &virtual_opts)) < 0) return -1; + result = git__calloc(1, sizeof(git_annotated_commit)); + GITERR_CHECK_ALLOC(result); result->type = GIT_ANNOTATED_COMMIT_VIRTUAL; result->index = index; From a001846b25124a47ed96f865548b5321202d51ae Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 8 Feb 2016 17:05:57 +0100 Subject: [PATCH 343/450] curl_stream: fix unused cert infos When copying contents of the cURL certiinfo we duplicate the data but forget to actually put it into the vector. --- src/curl_stream.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/curl_stream.c b/src/curl_stream.c index 9f8b202a8..9963d94cc 100644 --- a/src/curl_stream.c +++ b/src/curl_stream.c @@ -79,6 +79,7 @@ static int curls_certificate(git_cert **out, git_stream *stream) for (slist = certinfo->certinfo[0]; slist; slist = slist->next) { char *str = git__strdup(slist->data); GITERR_CHECK_ALLOC(str); + git_vector_insert(&strings, str); } /* Copy the contents of the vector into a strarray so we can expose them */ From bad2702c433227fcab1690303993e5861c6861b5 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sat, 6 Feb 2016 11:25:47 -0800 Subject: [PATCH 344/450] global: refactor setup and cleanup Move the common initialization and cleanup methods to reduce unnecessary duplication. --- src/global.c | 129 ++++++++++++++++++++++++++++----------------------- 1 file changed, 70 insertions(+), 59 deletions(-) diff --git a/src/global.c b/src/global.c index 0eab8d552..35595fbb4 100644 --- a/src/global.c +++ b/src/global.c @@ -33,6 +33,9 @@ static git_atomic git__n_shutdown_callbacks; static git_atomic git__n_inits; char *git__user_agent; +static int init_ssl(void); +static void shutdown_ssl(void); + void git__on_shutdown(git_global_shutdown_fn callback) { int count = git_atomic_inc(&git__n_shutdown_callbacks); @@ -49,16 +52,51 @@ static void git__global_state_cleanup(git_global_st *st) st->error_t.message = NULL; } -static void git__shutdown(void) +static int init_common(void) +{ + int ret; + + /* Initialize the CRT debug allocator first, before our first malloc */ +#if defined(GIT_MSVC_CRTDBG) + git_win32__crtdbg_stacktrace_init(); + git_win32__stack_init(); +#endif + + /* Initialize any other subsystems that have global state */ + if ((ret = git_hash_global_init()) == 0 && + (ret = git_sysdir_global_init()) == 0 && + (ret = init_ssl()) == 0) { + } + + GIT_MEMORY_BARRIER; + + return ret; +} + +static void shutdown_common(void) { int pos; /* Shutdown subsystems that have registered */ - for (pos = git_atomic_get(&git__n_shutdown_callbacks); pos > 0; pos = git_atomic_dec(&git__n_shutdown_callbacks)) { - git_global_shutdown_fn cb = git__swap(git__shutdown_callbacks[pos - 1], NULL); + for (pos = git_atomic_get(&git__n_shutdown_callbacks); + pos > 0; + pos = git_atomic_dec(&git__n_shutdown_callbacks)) { + + git_global_shutdown_fn cb = git__swap( + git__shutdown_callbacks[pos - 1], NULL); + if (cb != NULL) cb(); } + + shutdown_ssl(); + + git__free(git__user_agent); + +#if defined(GIT_MSVC_CRTDBG) + git_win32__crtdbg_stacktrace_cleanup(); + git_win32__stack_cleanup(); +#endif } #if defined(GIT_THREADS) && defined(GIT_OPENSSL) @@ -91,7 +129,7 @@ static void shutdown_ssl_locking(void) } #endif -static void init_ssl(void) +static int init_ssl(void) { #ifdef GIT_OPENSSL long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; @@ -116,15 +154,18 @@ static void init_ssl(void) if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) { SSL_CTX_free(git__ssl_ctx); git__ssl_ctx = NULL; + return -1; } #endif + + return 0; } /** * This function aims to clean-up the SSL context which * we allocated. */ -static void uninit_ssl(void) +static void shutdown_ssl(void) { #ifdef GIT_OPENSSL if (git__ssl_ctx) { @@ -208,14 +249,13 @@ static int synchronized_threads_init(void) int error; _tls_index = TlsAlloc(); + + win32_pthread_initialize(); + if (git_mutex_init(&git__mwindow_mutex)) return -1; - /* Initialize any other subsystems that have global state */ - if ((error = git_hash_global_init()) >= 0) - error = git_sysdir_global_init(); - - win32_pthread_initialize(); + error = init_common(); return error; } @@ -229,11 +269,6 @@ int git_libgit2_init(void) /* Only do work on a 0 -> 1 transition of the refcount */ if ((ret = git_atomic_inc(&git__n_inits)) == 1) { -#if defined(GIT_MSVC_CRTDBG) - git_win32__crtdbg_stacktrace_init(); - git_win32__stack_init(); -#endif - if (synchronized_threads_init() < 0) ret = -1; } @@ -244,17 +279,6 @@ int git_libgit2_init(void) return ret; } -static void synchronized_threads_shutdown(void) -{ - /* Shut down any subsystems that have global state */ - git__shutdown(); - - git__free_tls_data(); - - TlsFree(_tls_index); - git_mutex_free(&git__mwindow_mutex); -} - int git_libgit2_shutdown(void) { int ret; @@ -264,14 +288,12 @@ int git_libgit2_shutdown(void) /* Only do work on a 1 -> 0 transition of the refcount */ if ((ret = git_atomic_dec(&git__n_inits)) == 0) { - synchronized_threads_shutdown(); + shutdown_common(); -#if defined(GIT_MSVC_CRTDBG) - git_win32__crtdbg_stacktrace_cleanup(); - git_win32__stack_cleanup(); -#endif + git__free_tls_data(); - git__free(git__user_agent); + TlsFree(_tls_index); + git_mutex_free(&git__mwindow_mutex); } /* Exit the lock */ @@ -331,17 +353,10 @@ static void init_once(void) { if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0) return; + pthread_key_create(&_tls_key, &cb__free_status); - - /* Initialize any other subsystems that have global state */ - if ((init_error = git_hash_global_init()) >= 0) - init_error = git_sysdir_global_init(); - - /* OpenSSL needs to be initialized from the main thread */ - init_ssl(); - - GIT_MEMORY_BARRIER; + init_error = init_common(); } int git_libgit2_init(void) @@ -364,15 +379,13 @@ int git_libgit2_shutdown(void) return ret; /* Shut down any subsystems that have global state */ - git__shutdown(); - uninit_ssl(); + shutdown_common(); ptr = pthread_getspecific(_tls_key); pthread_setspecific(_tls_key, NULL); git__global_state_cleanup(ptr); git__free(ptr); - git__free(git__user_agent); pthread_key_delete(_tls_key); git_mutex_free(&git__mwindow_mutex); @@ -405,15 +418,16 @@ static git_global_st __state; int git_libgit2_init(void) { - static int ssl_inited = 0; + int ret; - if (!ssl_inited) { - init_ssl(); - ssl_inited = 1; - } + /* Only init SSL the first time */ + if ((ret = git_atomic_inc(&git__n_inits)) != 1) + return ret; - git_buf_init(&__state.error_buf, 0); - return git_atomic_inc(&git__n_inits); + if ((ret = init_common()) < 0) + return ret; + + return 1; } int git_libgit2_shutdown(void) @@ -421,15 +435,12 @@ int git_libgit2_shutdown(void) int ret; /* Shut down any subsystems that have global state */ - if ((ret = git_atomic_dec(&git__n_inits)) != 0) - return ret; + if ((ret = git_atomic_dec(&git__n_inits)) == 0) { + shutdown_common(); + git__global_state_cleanup(&__state); + } - git__shutdown(); - git__global_state_cleanup(&__state); - uninit_ssl(); - git__free(git__user_agent); - - return 0; + return ret; } git_global_st *git__global_state(void) From 8a6d667763eb04ad872693ca76352821d1dee78c Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 8 Feb 2016 16:14:03 -0800 Subject: [PATCH 345/450] global: make openssl registration like the rest --- src/global.c | 121 +++---------------------------------------- src/openssl_stream.c | 120 ++++++++++++++++++++++++++++++++++++++++++ src/openssl_stream.h | 2 + 3 files changed, 128 insertions(+), 115 deletions(-) diff --git a/src/global.c b/src/global.c index 35595fbb4..4a61a062c 100644 --- a/src/global.c +++ b/src/global.c @@ -8,9 +8,11 @@ #include "global.h" #include "hash.h" #include "sysdir.h" -#include "git2/global.h" -#include "git2/sys/openssl.h" +#include "filter.h" +#include "openssl_stream.h" #include "thread-utils.h" +#include "git2/global.h" + #if defined(GIT_MSVC_CRTDBG) #include "win32/w32_stack.h" #include "win32/w32_crtdbg_stacktrace.h" @@ -33,9 +35,6 @@ static git_atomic git__n_shutdown_callbacks; static git_atomic git__n_inits; char *git__user_agent; -static int init_ssl(void); -static void shutdown_ssl(void); - void git__on_shutdown(git_global_shutdown_fn callback) { int count = git_atomic_inc(&git__n_shutdown_callbacks); @@ -65,8 +64,8 @@ static int init_common(void) /* Initialize any other subsystems that have global state */ if ((ret = git_hash_global_init()) == 0 && (ret = git_sysdir_global_init()) == 0 && - (ret = init_ssl()) == 0) { - } + (ret = git_filter_global_init()) == 0) + ret = git_openssl_stream_global_init(); GIT_MEMORY_BARRIER; @@ -89,8 +88,6 @@ static void shutdown_common(void) cb(); } - shutdown_ssl(); - git__free(git__user_agent); #if defined(GIT_MSVC_CRTDBG) @@ -99,112 +96,6 @@ static void shutdown_common(void) #endif } -#if defined(GIT_THREADS) && defined(GIT_OPENSSL) -void openssl_locking_function(int mode, int n, const char *file, int line) -{ - int lock; - - GIT_UNUSED(file); - GIT_UNUSED(line); - - lock = mode & CRYPTO_LOCK; - - if (lock) { - git_mutex_lock(&openssl_locks[n]); - } else { - git_mutex_unlock(&openssl_locks[n]); - } -} - -static void shutdown_ssl_locking(void) -{ - int num_locks, i; - - num_locks = CRYPTO_num_locks(); - CRYPTO_set_locking_callback(NULL); - - for (i = 0; i < num_locks; ++i) - git_mutex_free(openssl_locks); - git__free(openssl_locks); -} -#endif - -static int init_ssl(void) -{ -#ifdef GIT_OPENSSL - long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; - - /* Older OpenSSL and MacOS OpenSSL doesn't have this */ -#ifdef SSL_OP_NO_COMPRESSION - ssl_opts |= SSL_OP_NO_COMPRESSION; -#endif - - SSL_load_error_strings(); - OpenSSL_add_ssl_algorithms(); - /* - * Load SSLv{2,3} and TLSv1 so that we can talk with servers - * which use the SSL hellos, which are often used for - * compatibility. We then disable SSL so we only allow OpenSSL - * to speak TLSv1 to perform the encryption itself. - */ - git__ssl_ctx = SSL_CTX_new(SSLv23_method()); - SSL_CTX_set_options(git__ssl_ctx, ssl_opts); - SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL); - if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) { - SSL_CTX_free(git__ssl_ctx); - git__ssl_ctx = NULL; - return -1; - } -#endif - - return 0; -} - -/** - * This function aims to clean-up the SSL context which - * we allocated. - */ -static void shutdown_ssl(void) -{ -#ifdef GIT_OPENSSL - if (git__ssl_ctx) { - SSL_CTX_free(git__ssl_ctx); - git__ssl_ctx = NULL; - } -#endif -} - -int git_openssl_set_locking(void) -{ -#ifdef GIT_OPENSSL -# ifdef GIT_THREADS - int num_locks, i; - - num_locks = CRYPTO_num_locks(); - openssl_locks = git__calloc(num_locks, sizeof(git_mutex)); - GITERR_CHECK_ALLOC(openssl_locks); - - for (i = 0; i < num_locks; i++) { - if (git_mutex_init(&openssl_locks[i]) != 0) { - giterr_set(GITERR_SSL, "failed to initialize openssl locks"); - return -1; - } - } - - CRYPTO_set_locking_callback(openssl_locking_function); - git__on_shutdown(shutdown_ssl_locking); - return 0; -# else - giterr_set(GITERR_THREAD, "libgit2 as not built with threads"); - return -1; -# endif -#else - giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support"); - return -1; -#endif -} - /** * Handle the global state with TLS * diff --git a/src/openssl_stream.c b/src/openssl_stream.c index 54dd761ca..0705d90e7 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -31,6 +31,115 @@ #include #include +SSL_CTX *git__ssl_ctx; + +#ifdef GIT_THREADS + +static git_mutex *openssl_locks; + +static void openssl_locking_function( + int mode, int n, const char *file, int line) +{ + int lock; + + GIT_UNUSED(file); + GIT_UNUSED(line); + + lock = mode & CRYPTO_LOCK; + + if (lock) { + git_mutex_lock(&openssl_locks[n]); + } else { + git_mutex_unlock(&openssl_locks[n]); + } +} + +static void shutdown_ssl_locking(void) +{ + int num_locks, i; + + num_locks = CRYPTO_num_locks(); + CRYPTO_set_locking_callback(NULL); + + for (i = 0; i < num_locks; ++i) + git_mutex_free(openssl_locks); + git__free(openssl_locks); +} + +#endif /* GIT_THREADS */ + +/** + * This function aims to clean-up the SSL context which + * we allocated. + */ +static void shutdown_ssl(void) +{ + if (git__ssl_ctx) { + SSL_CTX_free(git__ssl_ctx); + git__ssl_ctx = NULL; + } +} + +int git_openssl_stream_global_init(void) +{ +#ifdef GIT_OPENSSL + long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; + + /* Older OpenSSL and MacOS OpenSSL doesn't have this */ +#ifdef SSL_OP_NO_COMPRESSION + ssl_opts |= SSL_OP_NO_COMPRESSION; +#endif + + SSL_load_error_strings(); + OpenSSL_add_ssl_algorithms(); + /* + * Load SSLv{2,3} and TLSv1 so that we can talk with servers + * which use the SSL hellos, which are often used for + * compatibility. We then disable SSL so we only allow OpenSSL + * to speak TLSv1 to perform the encryption itself. + */ + git__ssl_ctx = SSL_CTX_new(SSLv23_method()); + SSL_CTX_set_options(git__ssl_ctx, ssl_opts); + SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL); + if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) { + SSL_CTX_free(git__ssl_ctx); + git__ssl_ctx = NULL; + return -1; + } +#endif + + git__on_shutdown(shutdown_ssl); + + return 0; +} + +int git_openssl_set_locking(void) +{ +#ifdef GIT_THREADS + int num_locks, i; + + num_locks = CRYPTO_num_locks(); + openssl_locks = git__calloc(num_locks, sizeof(git_mutex)); + GITERR_CHECK_ALLOC(openssl_locks); + + for (i = 0; i < num_locks; i++) { + if (git_mutex_init(&openssl_locks[i]) != 0) { + giterr_set(GITERR_SSL, "failed to initialize openssl locks"); + return -1; + } + } + + CRYPTO_set_locking_callback(openssl_locking_function); + git__on_shutdown(shutdown_ssl_locking); + return 0; +#else + giterr_set(GITERR_THREAD, "libgit2 as not built with threads"); + return -1; +#endif +} + + static int bio_create(BIO *b) { b->init = 1; @@ -472,6 +581,17 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port) #include "stream.h" +int git_openssl_stream_global_init(void) +{ + return 0; +} + +int git_openssl_set_locking(void) +{ + giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support"); + return -1; +} + int git_openssl_stream_new(git_stream **out, const char *host, const char *port) { GIT_UNUSED(out); diff --git a/src/openssl_stream.h b/src/openssl_stream.h index 9ca06489e..82b5110c4 100644 --- a/src/openssl_stream.h +++ b/src/openssl_stream.h @@ -9,6 +9,8 @@ #include "git2/sys/stream.h" +extern int git_openssl_stream_global_init(void); + extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port); #endif From 6e0fc1a63138f959cd2aac05e85f70cf9e22a189 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 7 Feb 2016 14:10:38 -0800 Subject: [PATCH 346/450] mingw: use gcc-like memory barrier Use the gcc-like memory barrier (__sync_synchronize) on mingw. --- src/thread-utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thread-utils.h b/src/thread-utils.h index dd1136caf..14c8a41ff 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -275,7 +275,7 @@ GIT_INLINE(int) git_atomic_get(git_atomic *a) extern int git_online_cpus(void); -#if defined(GIT_THREADS) && defined(GIT_WIN32) +#if defined(GIT_THREADS) && defined(_MSC_VER) # define GIT_MEMORY_BARRIER MemoryBarrier() #elif defined(GIT_THREADS) # define GIT_MEMORY_BARRIER __sync_synchronize() From 2ed855a9e8f9af211e7274021c2264e600c0f86b Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 7 Feb 2016 13:16:30 -0800 Subject: [PATCH 347/450] filter: avoid races during filter registration Previously we would set the global filter registry structure before adding filters to the structure, without a lock, which is quite racy. Now, register default filters during global registration and use an rwlock to read and write the filter registry (as appopriate). --- src/filter.c | 285 ++++++++++++++++++++++++++++----------------------- src/filter.h | 2 + 2 files changed, 160 insertions(+), 127 deletions(-) diff --git a/src/filter.c b/src/filter.c index 60473e4e1..a0628d779 100644 --- a/src/filter.c +++ b/src/filter.c @@ -56,80 +56,15 @@ static int filter_def_priority_cmp(const void *a, const void *b) return (pa < pb) ? -1 : (pa > pb) ? 1 : 0; } -struct filter_registry { +struct git_filter_registry { + git_rwlock lock; git_vector filters; }; -static struct filter_registry *git__filter_registry = NULL; +static struct git_filter_registry filter_registry; -static void filter_registry_shutdown(void) -{ - struct filter_registry *reg = NULL; - size_t pos; - git_filter_def *fdef; +static void git_filter_global_shutdown(void); - if ((reg = git__swap(git__filter_registry, NULL)) == NULL) - return; - - git_vector_foreach(®->filters, pos, fdef) { - if (fdef->filter && fdef->filter->shutdown) { - fdef->filter->shutdown(fdef->filter); - fdef->initialized = false; - } - - git__free(fdef->filter_name); - git__free(fdef->attrdata); - git__free(fdef); - } - - git_vector_free(®->filters); - git__free(reg); -} - -static int filter_registry_initialize(void) -{ - int error = 0; - struct filter_registry *reg; - - if (git__filter_registry) - return 0; - - reg = git__calloc(1, sizeof(struct filter_registry)); - GITERR_CHECK_ALLOC(reg); - - if ((error = git_vector_init( - ®->filters, 2, filter_def_priority_cmp)) < 0) - goto cleanup; - - reg = git__compare_and_swap(&git__filter_registry, NULL, reg); - if (reg != NULL) - goto cleanup; - - git__on_shutdown(filter_registry_shutdown); - - /* try to register both default filters */ - { - git_filter *crlf = git_crlf_filter_new(); - git_filter *ident = git_ident_filter_new(); - - if (crlf && git_filter_register( - GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0) - crlf = NULL; - if (ident && git_filter_register( - GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0) - ident = NULL; - - if (!crlf || !ident) - return -1; - } - - return 0; - -cleanup: - git_vector_free(®->filters); - git__free(reg); - return error; -} static int filter_def_scan_attrs( git_buf *attrs, size_t *nattr, size_t *nmatch, const char *attr_str) @@ -210,40 +145,14 @@ static int filter_def_filter_key_check(const void *key, const void *fdef) return (key == filter) ? 0 : -1; } -static int filter_registry_find(size_t *pos, const char *name) -{ - return git_vector_search2( - pos, &git__filter_registry->filters, filter_def_name_key_check, name); -} - -static git_filter_def *filter_registry_lookup(size_t *pos, const char *name) -{ - git_filter_def *fdef = NULL; - - if (!filter_registry_find(pos, name)) - fdef = git_vector_get(&git__filter_registry->filters, *pos); - - return fdef; -} - -int git_filter_register( +/* Note: callers must lock the registry before calling this function */ +static int filter_registry_insert( const char *name, git_filter *filter, int priority) { git_filter_def *fdef; size_t nattr = 0, nmatch = 0, alloc_len; git_buf attrs = GIT_BUF_INIT; - assert(name && filter); - - if (filter_registry_initialize() < 0) - return -1; - - if (!filter_registry_find(NULL, name)) { - giterr_set( - GITERR_FILTER, "Attempt to reregister existing filter '%s'", name); - return GIT_EEXISTS; - } - if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0) return -1; @@ -265,21 +174,123 @@ int git_filter_register( filter_def_set_attrs(fdef); - if (git_vector_insert(&git__filter_registry->filters, fdef) < 0) { + if (git_vector_insert(&filter_registry.filters, fdef) < 0) { git__free(fdef->filter_name); git__free(fdef->attrdata); git__free(fdef); return -1; } - git_vector_sort(&git__filter_registry->filters); + git_vector_sort(&filter_registry.filters); return 0; } +int git_filter_global_init(void) +{ + git_filter *crlf = NULL, *ident = NULL; + int error = 0; + + if (git_rwlock_init(&filter_registry.lock) < 0) + return -1; + + if ((error = git_vector_init(&filter_registry.filters, 2, + filter_def_priority_cmp)) < 0) + goto done; + + if ((crlf = git_crlf_filter_new()) == NULL || + filter_registry_insert( + GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0 || + (ident = git_ident_filter_new()) == NULL || + filter_registry_insert( + GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0) + error = -1; + + git__on_shutdown(git_filter_global_shutdown); + +done: + if (error) { + git_filter_free(crlf); + git_filter_free(ident); + } + + return error; +} + +static void git_filter_global_shutdown(void) +{ + size_t pos; + git_filter_def *fdef; + + if (git_rwlock_wrlock(&filter_registry.lock) < 0) + return; + + git_vector_foreach(&filter_registry.filters, pos, fdef) { + if (fdef->filter && fdef->filter->shutdown) { + fdef->filter->shutdown(fdef->filter); + fdef->initialized = false; + } + + git__free(fdef->filter_name); + git__free(fdef->attrdata); + git__free(fdef); + } + + git_vector_free(&filter_registry.filters); + + git_rwlock_wrunlock(&filter_registry.lock); + git_rwlock_free(&filter_registry.lock); +} + +/* Note: callers must lock the registry before calling this function */ +static int filter_registry_find(size_t *pos, const char *name) +{ + return git_vector_search2( + pos, &filter_registry.filters, filter_def_name_key_check, name); +} + +/* Note: callers must lock the registry before calling this function */ +static git_filter_def *filter_registry_lookup(size_t *pos, const char *name) +{ + git_filter_def *fdef = NULL; + + if (!filter_registry_find(pos, name)) + fdef = git_vector_get(&filter_registry.filters, *pos); + + return fdef; +} + + +int git_filter_register( + const char *name, git_filter *filter, int priority) +{ + int error; + + assert(name && filter); + + if (git_rwlock_wrlock(&filter_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock filter registry"); + return -1; + } + + if (!filter_registry_find(NULL, name)) { + giterr_set( + GITERR_FILTER, "attempt to reregister existing filter '%s'", name); + error = GIT_EEXISTS; + goto done; + } + + error = filter_registry_insert(name, filter, priority); + +done: + git_rwlock_wrunlock(&filter_registry.lock); + return error; +} + int git_filter_unregister(const char *name) { size_t pos; git_filter_def *fdef; + int error = 0; assert(name); @@ -289,12 +300,18 @@ int git_filter_unregister(const char *name) return -1; } - if ((fdef = filter_registry_lookup(&pos, name)) == NULL) { - giterr_set(GITERR_FILTER, "Cannot find filter '%s' to unregister", name); - return GIT_ENOTFOUND; + if (git_rwlock_wrlock(&filter_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock filter registry"); + return -1; } - (void)git_vector_remove(&git__filter_registry->filters, pos); + if ((fdef = filter_registry_lookup(&pos, name)) == NULL) { + giterr_set(GITERR_FILTER, "Cannot find filter '%s' to unregister", name); + error = GIT_ENOTFOUND; + goto done; + } + + git_vector_remove(&filter_registry.filters, pos); if (fdef->initialized && fdef->filter && fdef->filter->shutdown) { fdef->filter->shutdown(fdef->filter); @@ -305,21 +322,18 @@ int git_filter_unregister(const char *name) git__free(fdef->attrdata); git__free(fdef); - return 0; +done: + git_rwlock_wrunlock(&filter_registry.lock); + return error; } static int filter_initialize(git_filter_def *fdef) { int error = 0; - if (!fdef->initialized && - fdef->filter && - fdef->filter->initialize && - (error = fdef->filter->initialize(fdef->filter)) < 0) - { - /* auto-unregister if initialize fails */ - git_filter_unregister(fdef->filter_name); - return error; + if (!fdef->initialized && fdef->filter && fdef->filter->initialize) { + if ((error = fdef->filter->initialize(fdef->filter)) < 0) + return error; } fdef->initialized = true; @@ -330,17 +344,22 @@ git_filter *git_filter_lookup(const char *name) { size_t pos; git_filter_def *fdef; + git_filter *filter = NULL; - if (filter_registry_initialize() < 0) + if (git_rwlock_rdlock(&filter_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock filter registry"); return NULL; + } - if ((fdef = filter_registry_lookup(&pos, name)) == NULL) - return NULL; + if ((fdef = filter_registry_lookup(&pos, name)) == NULL || + (!fdef->initialized && filter_initialize(fdef) < 0)) + goto done; - if (!fdef->initialized && filter_initialize(fdef) < 0) - return NULL; + filter = fdef->filter; - return fdef->filter; +done: + git_rwlock_rdunlock(&filter_registry.lock); + return filter; } void git_filter_free(git_filter *filter) @@ -478,8 +497,10 @@ int git_filter_list__load_ext( size_t idx; git_filter_def *fdef; - if (filter_registry_initialize() < 0) + if (git_rwlock_rdlock(&filter_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock filter registry"); return -1; + } src.repo = repo; src.path = path; @@ -489,7 +510,7 @@ int git_filter_list__load_ext( if (blob) git_oid_cpy(&src.oid, git_blob_id(blob)); - git_vector_foreach(&git__filter_registry->filters, idx, fdef) { + git_vector_foreach(&filter_registry.filters, idx, fdef) { const char **values = NULL; void *payload = NULL; @@ -523,7 +544,7 @@ int git_filter_list__load_ext( else { if (!fl) { if ((error = filter_list_new(&fl, &src)) < 0) - return error; + break; fl->temp_buf = filter_opts->temp_buf; } @@ -537,6 +558,8 @@ int git_filter_list__load_ext( } } + git_rwlock_rdunlock(&filter_registry.lock); + if (error && fl != NULL) { git_array_clear(fl->filters); git__free(fl); @@ -604,19 +627,27 @@ int git_filter_list_push( { int error = 0; size_t pos; - git_filter_def *fdef; + git_filter_def *fdef = NULL; git_filter_entry *fe; assert(fl && filter); - if (git_vector_search2( - &pos, &git__filter_registry->filters, - filter_def_filter_key_check, filter) < 0) { - giterr_set(GITERR_FILTER, "Cannot use an unregistered filter"); + if (git_rwlock_rdlock(&filter_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock filter registry"); return -1; } - fdef = git_vector_get(&git__filter_registry->filters, pos); + if (git_vector_search2( + &pos, &filter_registry.filters, + filter_def_filter_key_check, filter) == 0) + fdef = git_vector_get(&filter_registry.filters, pos); + + git_rwlock_rdunlock(&filter_registry.lock); + + if (fdef == NULL) { + giterr_set(GITERR_FILTER, "Cannot use an unregistered filter"); + return -1; + } if (!fdef->initialized && (error = filter_initialize(fdef)) < 0) return error; diff --git a/src/filter.h b/src/filter.h index 5062afba5..9bd835f94 100644 --- a/src/filter.h +++ b/src/filter.h @@ -32,6 +32,8 @@ typedef struct { #define GIT_FILTER_OPTIONS_INIT {0} +extern int git_filter_global_init(void); + extern void git_filter_free(git_filter *filter); extern int git_filter_list__load_ext( From 88a4d0844841473d05f7616c2c85e20c0be9df5b Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 2 Feb 2016 14:50:35 -0800 Subject: [PATCH 348/450] mailmap: add ethomson@github.com --- .mailmap | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index c656f64c7..8479cf6c4 100644 --- a/.mailmap +++ b/.mailmap @@ -16,6 +16,7 @@ Xavier L. Sascha Cunz Authmillenon Authmillenon -Edward Thomson +Edward Thomson +Edward Thomson J. David Ibáñez Russell Belfer From b00c959f45bfa8e2bb27bec0cfebc4bad5f70df2 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 8 Feb 2016 17:55:22 -0800 Subject: [PATCH 349/450] Better document `git_merge_commits` redux `git_merge_commits` and `git_merge` now *do* handle recursive base building for criss-cross merges. Remove the documentation that says that they do not. This reverts commit 5e44d9bcb6d5b20922f49b1913723186f8ced8b5. --- include/git2/merge.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/git2/merge.h b/include/git2/merge.h index af53ead22..560797a0c 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -527,10 +527,6 @@ GIT_EXTERN(int) git_merge_trees( * or checked out. If the index is to be converted to a tree, the caller * should resolve any conflicts that arose as part of the merge. * - * The merge performed uses the first common ancestor, unlike the - * `git-merge-recursive` strategy, which may produce an artificial common - * ancestor tree when there are multiple ancestors. - * * The returned index must be freed explicitly with `git_index_free`. * * @param out pointer to store the index result in @@ -553,10 +549,6 @@ GIT_EXTERN(int) git_merge_commits( * to the index. Callers should inspect the repository's index after this * completes, resolve any conflicts and prepare a commit. * - * The merge performed uses the first common ancestor, unlike the - * `git-merge-recursive` strategy, which may produce an artificial common - * ancestor tree when there are multiple ancestors. - * * For compatibility with git, the repository is put into a merging * state. Once the commit is done (or if the uses wishes to abort), * you should clear this state by calling From f55eca167c2d08045dff929adb8ad8b81d8ccc86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 9 Feb 2016 07:17:26 +0100 Subject: [PATCH 350/450] commit: also match the first header field when searching We were searching only past the first header field, which meant we were unable to find e.g. `tree` which is the first field. While here, make sure to set an error message in case we cannot find the field. --- src/commit.c | 39 ++++++++++++++++++++++----------------- tests/commit/parse.c | 4 ++++ 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/commit.c b/src/commit.c index 81aae489f..87301e0da 100644 --- a/src/commit.c +++ b/src/commit.c @@ -564,41 +564,45 @@ int git_commit_nth_gen_ancestor( int git_commit_header_field(git_buf *out, const git_commit *commit, const char *field) { - const char *buf = commit->raw_header; - const char *h, *eol; + const char *eol, *buf = commit->raw_header; git_buf_sanitize(out); - while ((h = strchr(buf, '\n')) && h[1] != '\0' && h[1] != '\n') { - h++; - if (git__prefixcmp(h, field)) { - buf = h; + + while ((eol = strchr(buf, '\n')) && eol[1] != '\0') { + /* We can skip continuations here */ + if (buf[0] == ' ') { + buf = eol + 1; continue; } - h += strlen(field); - eol = strchr(h, '\n'); - if (h[0] != ' ') { - buf = h; + /* Skip until we find the field we're after */ + if (git__prefixcmp(buf, field)) { + buf = eol + 1; continue; } - if (!eol) - goto malformed; - h++; /* skip the SP */ + buf += strlen(field); + /* Check that we're not matching a prefix but the field itself */ + if (buf[0] != ' ') { + buf = eol + 1; + continue; + } - git_buf_put(out, h, eol - h); + buf++; /* skip the SP */ + + git_buf_put(out, buf, eol - buf); if (git_buf_oom(out)) goto oom; /* If the next line starts with SP, it's multi-line, we must continue */ while (eol[1] == ' ') { git_buf_putc(out, '\n'); - h = eol + 2; - eol = strchr(h, '\n'); + buf = eol + 2; + eol = strchr(buf, '\n'); if (!eol) goto malformed; - git_buf_put(out, h, eol - h); + git_buf_put(out, buf, eol - buf); } if (git_buf_oom(out)) @@ -607,6 +611,7 @@ int git_commit_header_field(git_buf *out, const git_commit *commit, const char * return 0; } + giterr_set(GITERR_OBJECT, "no such field '%s'", field); return GIT_ENOTFOUND; malformed: diff --git a/tests/commit/parse.c b/tests/commit/parse.c index 388da078a..7c7264bc7 100644 --- a/tests/commit/parse.c +++ b/tests/commit/parse.c @@ -443,6 +443,10 @@ cpxtDQQMGYFpXK/71stq\n\ cl_git_pass(parse_commit(&commit, passing_commit_cases[4])); + cl_git_pass(git_commit_header_field(&buf, commit, "tree")); + cl_assert_equal_s("6b79e22d69bf46e289df0345a14ca059dfc9bdf6", buf.ptr); + git_buf_clear(&buf); + cl_git_pass(git_commit_header_field(&buf, commit, "parent")); cl_assert_equal_s("34734e478d6cf50c27c9d69026d93974d052c454", buf.ptr); git_buf_clear(&buf); From a53d2e3985505de0e1dcc580cd2c624fcf0fce93 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 9 Feb 2016 09:58:56 +0100 Subject: [PATCH 351/450] pack: do not free passed in poiter on error The function `git_packfile_stream_open` tries to free the passed in stream when an error occurs. The only call site is `git_indexer_append`, though, which passes in the address of a stream struct which has not been allocated on the heap. Fix the issue by simply removing the call to free. In case of an error we did not allocate any memory yet and otherwise it should be the caller's responsibility to manage it's object's lifetime. --- src/pack.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pack.c b/src/pack.c index f6cb3a548..081e37084 100644 --- a/src/pack.c +++ b/src/pack.c @@ -790,7 +790,6 @@ int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p, obj->zstream.next_out = Z_NULL; st = inflateInit(&obj->zstream); if (st != Z_OK) { - git__free(obj); giterr_set(GITERR_ZLIB, "failed to init packfile stream"); return -1; } From 0b2437bb680897424e208464312fe2d4610cc4b6 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 9 Feb 2016 10:43:28 +0100 Subject: [PATCH 352/450] pack-objects: fix memory leak in compute_write_order --- src/pack-objects.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pack-objects.c b/src/pack-objects.c index fd181fc5e..3046d941d 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -605,6 +605,7 @@ static git_pobject **compute_write_order(git_packbuilder *pb) } if (wo_end != pb->nr_objects) { + git__free(wo); giterr_set(GITERR_INVALID, "invalid write order"); return NULL; } From 39c9dd24d3556bc4eec584ac2165f2a01de64da1 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 9 Feb 2016 10:53:30 +0100 Subject: [PATCH 353/450] pack-objects: fix memory leak in packbuilder_config --- src/pack-objects.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/pack-objects.c b/src/pack-objects.c index 3046d941d..0afa28e62 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -91,7 +91,7 @@ static unsigned name_hash(const char *name) static int packbuilder_config(git_packbuilder *pb) { git_config *config; - int ret; + int ret = 0; int64_t val; if ((ret = git_repository_config_snapshot(&config, pb->repo)) < 0) @@ -100,8 +100,10 @@ static int packbuilder_config(git_packbuilder *pb) #define config_get(KEY,DST,DFLT) do { \ ret = git_config_get_int64(&val, config, KEY); \ if (!ret) (DST) = val; \ - else if (ret == GIT_ENOTFOUND) (DST) = (DFLT); \ - else if (ret < 0) return -1; } while (0) + else if (ret == GIT_ENOTFOUND) { \ + (DST) = (DFLT); \ + ret = 0; \ + } else if (ret < 0) goto out; } while (0) config_get("pack.deltaCacheSize", pb->max_delta_cache_size, GIT_PACK_DELTA_CACHE_SIZE); @@ -113,9 +115,10 @@ static int packbuilder_config(git_packbuilder *pb) #undef config_get +out: git_config_free(config); - return 0; + return ret; } int git_packbuilder_new(git_packbuilder **out, git_repository *repo) From e26254575306abbc39acd96c6dc54a07aa3b2aa4 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 9 Feb 2016 11:07:50 +0100 Subject: [PATCH 354/450] checkout: fix resource leak --- src/checkout.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/checkout.c b/src/checkout.c index a92ad0825..fd8e2c443 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1487,8 +1487,10 @@ static int blob_content_to_file( if (!data->opts.disable_filters && (error = git_filter_list__load_ext( &fl, data->repo, blob, hint_path, - GIT_FILTER_TO_WORKTREE, &filter_opts))) + GIT_FILTER_TO_WORKTREE, &filter_opts))) { + p_close(fd); return error; + } /* setup the writer */ memset(&writer, 0, sizeof(struct checkout_stream)); From 24b8ed2b3ae4519e9c56afcfdf1d0f00a2b95842 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 9 Feb 2016 11:11:38 +0100 Subject: [PATCH 355/450] attr_file: fix resource leak --- src/attr_file.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/attr_file.c b/src/attr_file.c index 500c99bd9..11d149358 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -123,7 +123,7 @@ int git_attr_file__load( break; } case GIT_ATTR_FILE__FROM_FILE: { - int fd; + int fd = -1; /* For open or read errors, pretend that we got ENOTFOUND. */ /* TODO: issue warning when warning API is available */ @@ -133,7 +133,8 @@ int git_attr_file__load( (fd = git_futils_open_ro(entry->fullpath)) < 0 || (error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size)) < 0) nonexistent = true; - else + + if (fd >= 0) p_close(fd); break; From a65afb757e2675eb8889a9ce1f8809434cdb3af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 8 Feb 2016 18:51:13 +0100 Subject: [PATCH 356/450] Introduce git_commit_extract_signature This returns the GPG signature for a commit and its contents without the signature block, allowing for the verification of the commit's signature. --- include/git2/commit.h | 12 ++++++ src/commit.c | 86 +++++++++++++++++++++++++++++++++++++++++++ tests/commit/parse.c | 49 ++++++++++++++++++++++++ 3 files changed, 147 insertions(+) diff --git a/include/git2/commit.h b/include/git2/commit.h index 34d29ed81..a92277417 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -263,6 +263,18 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor( */ GIT_EXTERN(int) git_commit_header_field(git_buf *out, const git_commit *commit, const char *field); +/** + * Extract the signature from a commit + * + * @param signature the signature block + * @param signed_data signed data; this is the commit contents minus the signature block + * @param repo the repository in which the commit exists + * @param commit_id the commit from which to extract the data + * @param field the name of the header field containing the signature + * block; pass `NULL` to extract the default 'gpgsig' + */ +GIT_EXTERN(int) git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field); + /** * Create new commit in the repository from a list of `git_object` pointers * diff --git a/src/commit.c b/src/commit.c index 81aae489f..c5e580139 100644 --- a/src/commit.c +++ b/src/commit.c @@ -616,3 +616,89 @@ oom: giterr_set_oom(); return -1; } + +int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field) +{ + git_odb_object *obj; + git_odb *odb; + const char *buf; + const char *h, *eol; + int error; + + git_buf_sanitize(signature); + git_buf_sanitize(signed_data); + + if (!field) + field = "gpgsig"; + + if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) + return error; + + if ((error = git_odb_read(&obj, odb, commit_id)) < 0) + return error; + + buf = git_odb_object_data(obj); + + while ((h = strchr(buf, '\n')) && h[1] != '\0' && h[1] != '\n') { + h++; + if (git__prefixcmp(buf, field)) { + if (git_buf_put(signed_data, buf, h - buf) < 0) + return -1; + + buf = h; + continue; + } + + h = buf; + h += strlen(field); + eol = strchr(h, '\n'); + if (h[0] != ' ') { + buf = h; + continue; + } + if (!eol) + goto malformed; + + h++; /* skip the SP */ + + git_buf_put(signature, h, eol - h); + if (git_buf_oom(signature)) + goto oom; + + /* If the next line starts with SP, it's multi-line, we must continue */ + while (eol[1] == ' ') { + git_buf_putc(signature, '\n'); + h = eol + 2; + eol = strchr(h, '\n'); + if (!eol) + goto malformed; + + git_buf_put(signature, h, eol - h); + } + + if (git_buf_oom(signature)) + goto oom; + + git_odb_object_free(obj); + return git_buf_puts(signed_data, eol+1); + } + + giterr_set(GITERR_INVALID, "this commit is not signed"); + error = GIT_ENOTFOUND; + goto cleanup; + +malformed: + giterr_set(GITERR_OBJECT, "malformed header"); + error = -1; + goto cleanup; +oom: + giterr_set_oom(); + error = -1; + goto cleanup; + +cleanup: + git_odb_object_free(obj); + git_buf_clear(signature); + git_buf_clear(signed_data); + return error; +} diff --git a/tests/commit/parse.c b/tests/commit/parse.c index 388da078a..f518e3878 100644 --- a/tests/commit/parse.c +++ b/tests/commit/parse.c @@ -456,3 +456,52 @@ cpxtDQQMGYFpXK/71stq\n\ git_buf_free(&buf); git_commit__free(commit); } + +void test_commit_parse__extract_signature(void) +{ + git_odb *odb; + git_oid commit_id; + git_buf signature = GIT_BUF_INIT, signed_data = GIT_BUF_INIT; + const char *gpgsig = "-----BEGIN PGP SIGNATURE-----\n\ +Version: GnuPG v1.4.12 (Darwin)\n\ +\n\ +iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n\ +o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n\ +JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n\ +AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n\ +SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n\ +who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n\ +6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n\ +cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n\ +c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n\ +ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n\ +7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n\ +cpxtDQQMGYFpXK/71stq\n\ +=ozeK\n\ +-----END PGP SIGNATURE-----"; + + const char *data = "tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n\ +parent 34734e478d6cf50c27c9d69026d93974d052c454\n\ +author Ben Burkert 1358451456 -0800\n\ +committer Ben Burkert 1358451456 -0800\n\ +\n\ +a simple commit which works\n"; + + + cl_git_pass(git_repository_odb__weakptr(&odb, g_repo)); + cl_git_pass(git_odb_write(&commit_id, odb, passing_commit_cases[4], strlen(passing_commit_cases[4]), GIT_OBJ_COMMIT)); + + cl_git_pass(git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL)); + cl_assert_equal_s(gpgsig, signature.ptr); + cl_assert_equal_s(data, signed_data.ptr); + + git_buf_clear(&signature); + git_buf_clear(&signed_data); + + cl_git_pass(git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, "gpgsig")); + cl_assert_equal_s(gpgsig, signature.ptr); + cl_assert_equal_s(data, signed_data.ptr); + + git_buf_free(&signature); + git_buf_free(&signed_data); +} From 9447b9e55a498b93668e43cf58eef33c29cc40df Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 9 Feb 2016 10:40:33 -0800 Subject: [PATCH 357/450] xplat: use st_mtimespec everywhere on mac --- src/common.h | 6 ++++++ src/fileops.c | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/common.h b/src/common.h index 2913baa92..bc4bdd856 100644 --- a/src/common.h +++ b/src/common.h @@ -62,6 +62,12 @@ # endif #define GIT_STDLIB_CALL +#ifdef GIT_USE_STAT_ATIMESPEC +# define st_atim st_atimespec +# define st_ctim st_ctimespec +# define st_mtim st_mtimespec +#endif + # include #endif diff --git a/src/fileops.c b/src/fileops.c index 6aafd06b6..150333d7a 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -13,12 +13,6 @@ #include "win32/findfile.h" #endif -#ifdef GIT_USE_STAT_ATIMESPEC -#define st_atim st_atimespec -#define st_ctim st_ctimespec -#define st_mtim st_mtimespec -#endif - GIT__USE_STRMAP int git_futils_mkpath2file(const char *file_path, const mode_t mode) From 494e61b8a39d88fa821eb752a77b7025d7703073 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 9 Feb 2016 17:44:59 -0800 Subject: [PATCH 358/450] win32: drop xp support in WideCharToMultiByte --- src/win32/utf-conv.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/src/win32/utf-conv.c b/src/win32/utf-conv.c index f1b674ea0..96fd4606e 100644 --- a/src/win32/utf-conv.c +++ b/src/win32/utf-conv.c @@ -8,20 +8,6 @@ #include "common.h" #include "utf-conv.h" -GIT_INLINE(DWORD) get_wc_flags(void) -{ - static char inited = 0; - static DWORD flags; - - /* Invalid code point check supported on Vista+ only */ - if (!inited) { - flags = git_has_win32_version(6, 0, 0) ? WC_ERR_INVALID_CHARS : 0; - inited = 1; - } - - return flags; -} - GIT_INLINE(void) git__set_errno(void) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) @@ -66,7 +52,7 @@ int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src) /* Length of -1 indicates NULL termination of the input string. Subtract 1 from the result to * turn 0 into -1 (an error code) and to not count the NULL terminator as part of the string's * length. WideCharToMultiByte never returns int's minvalue, so underflow is not possible */ - if ((len = WideCharToMultiByte(CP_UTF8, get_wc_flags(), src, -1, dest, (int)dest_size, NULL, NULL) - 1) < 0) + if ((len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, dest, (int)dest_size, NULL, NULL) - 1) < 0) git__set_errno(); return len; @@ -127,12 +113,11 @@ int git__utf8_to_16_alloc(wchar_t **dest, const char *src) int git__utf16_to_8_alloc(char **dest, const wchar_t *src) { int utf8_size; - DWORD dwFlags = get_wc_flags(); *dest = NULL; /* Length of -1 indicates NULL termination of the input string */ - utf8_size = WideCharToMultiByte(CP_UTF8, dwFlags, src, -1, NULL, 0, NULL, NULL); + utf8_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, NULL, 0, NULL, NULL); if (!utf8_size) { git__set_errno(); @@ -146,7 +131,7 @@ int git__utf16_to_8_alloc(char **dest, const wchar_t *src) return -1; } - utf8_size = WideCharToMultiByte(CP_UTF8, dwFlags, src, -1, *dest, utf8_size, NULL, NULL); + utf8_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, *dest, utf8_size, NULL, NULL); if (!utf8_size) { git__set_errno(); From 82abd40d80a8912043dd072a09962f06df193e36 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 7 Feb 2016 13:35:16 -0800 Subject: [PATCH 359/450] filter: clean up documentation around custom filters --- include/git2/sys/filter.h | 59 ++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/include/git2/sys/filter.h b/include/git2/sys/filter.h index baf1515d6..d0e5d4d6f 100644 --- a/include/git2/sys/filter.h +++ b/include/git2/sys/filter.h @@ -127,17 +127,6 @@ GIT_EXTERN(git_filter_mode_t) git_filter_source_mode(const git_filter_source *sr */ GIT_EXTERN(uint32_t) git_filter_source_flags(const git_filter_source *src); -/* - * struct git_filter - * - * The filter lifecycle: - * - initialize - first use of filter - * - shutdown - filter removed/unregistered from system - * - check - considering filter for file - * - apply - apply filter to file contents - * - cleanup - done with file - */ - /** * Initialize callback on filter * @@ -233,31 +222,51 @@ typedef void (*git_filter_cleanup_fn)( * To associate extra data with a filter, allocate extra data and put the * `git_filter` struct at the start of your data buffer, then cast the * `self` pointer to your larger structure when your callback is invoked. - * - * `version` should be set to GIT_FILTER_VERSION - * - * `attributes` is a whitespace-separated list of attribute names to check - * for this filter (e.g. "eol crlf text"). If the attribute name is bare, - * it will be simply loaded and passed to the `check` callback. If it has - * a value (i.e. "name=value"), the attribute must match that value for - * the filter to be applied. The value may be a wildcard (eg, "name=*"), - * in which case the filter will be invoked for any value for the given - * attribute name. See the attribute parameter of the `check` callback - * for the attribute value that was specified. - * - * The `initialize`, `shutdown`, `check`, `apply`, and `cleanup` callbacks - * are all documented above with the respective function pointer typedefs. */ struct git_filter { + /** The `version` field should be set to `GIT_FILTER_VERSION`. */ unsigned int version; + /** + * A whitespace-separated list of attribute names to check for this + * filter (e.g. "eol crlf text"). If the attribute name is bare, it + * will be simply loaded and passed to the `check` callback. If it + * has a value (i.e. "name=value"), the attribute must match that + * value for the filter to be applied. The value may be a wildcard + * (eg, "name=*"), in which case the filter will be invoked for any + * value for the given attribute name. See the attribute parameter + * of the `check` callback for the attribute value that was specified. + */ const char *attributes; + /** Called when the filter is first used for any file. */ git_filter_init_fn initialize; + + /** Called when the filter is removed or unregistered from the system. */ git_filter_shutdown_fn shutdown; + + /** + * Called to determine whether the filter should be invoked for a + * given file. If this function returns `GIT_PASSTHROUGH` then the + * `apply` function will not be invoked and the contents will be passed + * through unmodified. + */ git_filter_check_fn check; + + /** + * Called to actually apply the filter to file contents. If this + * function returns `GIT_PASSTHROUGH` then the contents will be passed + * through unmodified. + */ git_filter_apply_fn apply; + + /** + * Called to apply the filter in a streaming manner. If this is not + * specified then the system will call `apply` with the whole buffer. + */ git_filter_stream_fn stream; + + /** Called when the system is done filtering for a file. */ git_filter_cleanup_fn cleanup; }; From 8dddea42aaf876663fc3cc1afa31f2165cb9f993 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Wed, 10 Feb 2016 10:59:14 +0100 Subject: [PATCH 360/450] coverity: provide nodef for GITERR_CHECK_ALLOC Coverity currently lists a lot of errors with regard to GITERR_CHECK_ALLOC causing resource leaks. We know this macro is only invoked when we want to abort because we are out of memory. Coverity allows for overriding the default model where we know that certain functions guarantee a desired behavior. The user_nodefs.h is used to override the behavior of macros. Re-define GITERR_CHECK_ALLOC inside of it to specify its abort nature. --- script/coverity.sh | 2 ++ script/user_nodefs.h | 8 ++++++++ 2 files changed, 10 insertions(+) create mode 100644 script/user_nodefs.h diff --git a/script/coverity.sh b/script/coverity.sh index dcfeffc1d..785044c8a 100755 --- a/script/coverity.sh +++ b/script/coverity.sh @@ -33,6 +33,8 @@ if [ ! -d "$TOOL_BASE" ]; then ln -s "$TOOL_DIR" "$TOOL_BASE"/cov-analysis fi +cp script/user_nodefs.h "$TOOL_BASE"/cov-analysis/config/user_nodefs.h + COV_BUILD="$TOOL_BASE/cov-analysis/bin/cov-build" # Configure and build diff --git a/script/user_nodefs.h b/script/user_nodefs.h new file mode 100644 index 000000000..110f76851 --- /dev/null +++ b/script/user_nodefs.h @@ -0,0 +1,8 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#nodef GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { __coverity_panic__(); } From 50174ab459e7a6c007f6ffa15d381f2f18bd89b8 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Wed, 10 Feb 2016 11:06:23 +0100 Subject: [PATCH 361/450] coverity: use https URL for posting build When posting our instrumented build results to Coverity we have to include sensitive information, in particular our authorization token. Currently we use an unencrypted channel to post this information, leading to the token being transferred in plain. Fix this by using a secured connection instead. --- script/coverity.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/script/coverity.sh b/script/coverity.sh index 785044c8a..8c826892f 100755 --- a/script/coverity.sh +++ b/script/coverity.sh @@ -50,10 +50,9 @@ COVERITY_UNSUPPORTED=1 \ tar czf libgit2.tgz cov-int SHA=$(git rev-parse --short HEAD) curl \ - --form project=libgit2 \ --form token="$COVERITY_TOKEN" \ --form email=bs@github.com \ --form file=@libgit2.tgz \ --form version="$SHA" \ --form description="Travis build" \ - http://scan5.coverity.com/cgi-bin/upload.py + https://scan.coverity.com/builds?project=libgit2 From ee6673070a6134f0a1743c866583b14a305b6a43 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 11 Feb 2016 10:48:48 -0800 Subject: [PATCH 362/450] rebase: introduce inmemory rebasing Introduce the ability to rebase in-memory or in a bare repository. When `rebase_options.inmemory` is specified, the resultant `git_rebase` session will not be persisted to disk. Callers may still analyze the rebase operations, resolve any conflicts against the in-memory index and create the commits. Neither `HEAD` nor the working directory will be updated during this process. --- include/git2/rebase.h | 17 +- src/rebase.c | 407 ++++++++++++++++++++++++++-------------- tests/rebase/inmemory.c | 114 +++++++++++ tests/rebase/iterator.c | 30 ++- 4 files changed, 424 insertions(+), 144 deletions(-) create mode 100644 tests/rebase/inmemory.c diff --git a/include/git2/rebase.h b/include/git2/rebase.h index cf1b00fd9..17c4557e8 100644 --- a/include/git2/rebase.h +++ b/include/git2/rebase.h @@ -38,6 +38,15 @@ typedef struct { */ int quiet; + /** + * Used by `git_rebase_init`, this will begin an in-memory rebase, + * which will allow callers to step through the rebase operations and + * commit the rebased changes, but will not rewind HEAD or update the + * repository to be in a rebasing state. This will not interfere with + * the working directory (if there is one). + */ + int inmemory; + /** * Used by `git_rebase_finish`, this is the name of the notes reference * used to rewrite notes for rebased commits when finishing the rebase; @@ -101,7 +110,7 @@ typedef enum { #define GIT_REBASE_OPTIONS_VERSION 1 #define GIT_REBASE_OPTIONS_INIT \ - {GIT_REBASE_OPTIONS_VERSION, 0, NULL, GIT_CHECKOUT_OPTIONS_INIT} + {GIT_REBASE_OPTIONS_VERSION, 0, 0, NULL, GIT_CHECKOUT_OPTIONS_INIT} /** Indicates that a rebase operation is not (yet) in progress. */ #define GIT_REBASE_NO_OPERATION SIZE_MAX @@ -127,6 +136,12 @@ typedef struct { * be populated for operations of type `GIT_REBASE_OPERATION_EXEC`. */ const char *exec; + + /** + * The index that is the result of an operation. + * This is set only for in-memory rebases. + */ + git_index *index; } git_rebase_operation; /** diff --git a/src/rebase.c b/src/rebase.c index 17536c030..54affd6e2 100644 --- a/src/rebase.c +++ b/src/rebase.c @@ -63,17 +63,23 @@ struct git_rebase { char *state_path; int head_detached : 1, + inmemory : 1, quiet : 1, started : 1; - char *orig_head_name; + git_array_t(git_rebase_operation) operations; + size_t current; + + /* Used by in-memory rebase */ + git_commit *last_commit; + git_index *last_index; + + /* Used by regular (not in-memory) merge-style rebase */ git_oid orig_head_id; + char *orig_head_name; git_oid onto_id; char *onto_name; - - git_array_t(git_rebase_operation) operations; - size_t current; }; #define GIT_REBASE_STATE_INIT {0} @@ -393,6 +399,9 @@ done: static int rebase_cleanup(git_rebase *rebase) { + if (!rebase || rebase->inmemory) + return 0; + return git_path_isdir(rebase->state_path) ? git_futils_rmdir_r(rebase->state_path, NULL, GIT_RMDIR_REMOVE_FILES) : 0; @@ -601,59 +610,63 @@ static int rebase_init_merge( const git_annotated_commit *upstream, const git_annotated_commit *onto) { - if (rebase_init_operations(rebase, repo, branch, upstream, onto) < 0) - return -1; + git_reference *head_ref = NULL; + git_commit *onto_commit = NULL; + git_buf reflog = GIT_BUF_INIT; + git_buf state_path = GIT_BUF_INIT; + int error; + + GIT_UNUSED(upstream); + + if ((error = git_buf_joinpath(&state_path, repo->path_repository, REBASE_MERGE_DIR)) < 0) + goto done; + + rebase->state_path = git_buf_detach(&state_path); + GITERR_CHECK_ALLOC(rebase->state_path); + + rebase->orig_head_name = git__strdup(branch->ref_name ? branch->ref_name : ORIG_DETACHED_HEAD); + GITERR_CHECK_ALLOC(rebase->orig_head_name); rebase->onto_name = git__strdup(rebase_onto_name(onto)); GITERR_CHECK_ALLOC(rebase->onto_name); - return 0; + rebase->quiet = rebase->options.quiet; + + git_oid_cpy(&rebase->orig_head_id, git_annotated_commit_id(branch)); + git_oid_cpy(&rebase->onto_id, git_annotated_commit_id(onto)); + + if ((error = rebase_setupfiles(rebase)) < 0 || + (error = git_buf_printf(&reflog, + "rebase: checkout %s", rebase_onto_name(onto))) < 0 || + (error = git_commit_lookup( + &onto_commit, repo, git_annotated_commit_id(onto))) < 0 || + (error = git_checkout_tree(repo, + (git_object *)onto_commit, &rebase->options.checkout_options)) < 0 || + (error = git_reference_create(&head_ref, repo, GIT_HEAD_FILE, + git_annotated_commit_id(onto), 1, reflog.ptr)) < 0) + goto done; + +done: + git_reference_free(head_ref); + git_commit_free(onto_commit); + git_buf_free(&reflog); + git_buf_free(&state_path); + + return error; } -static int rebase_init( +static int rebase_init_inmemory( git_rebase *rebase, git_repository *repo, const git_annotated_commit *branch, const git_annotated_commit *upstream, const git_annotated_commit *onto) { - git_reference *head_ref = NULL; - git_annotated_commit *head_branch = NULL; - git_buf state_path = GIT_BUF_INIT; - int error; + GIT_UNUSED(branch); + GIT_UNUSED(upstream); - if ((error = git_buf_joinpath(&state_path, repo->path_repository, REBASE_MERGE_DIR)) < 0) - goto done; - - if (!branch) { - if ((error = git_repository_head(&head_ref, repo)) < 0 || - (error = git_annotated_commit_from_ref(&head_branch, repo, head_ref)) < 0) - goto done; - - branch = head_branch; - } - - rebase->repo = repo; - rebase->type = GIT_REBASE_TYPE_MERGE; - rebase->state_path = git_buf_detach(&state_path); - rebase->orig_head_name = git__strdup(branch->ref_name ? branch->ref_name : ORIG_DETACHED_HEAD); - rebase->quiet = rebase->options.quiet; - - git_oid_cpy(&rebase->orig_head_id, git_annotated_commit_id(branch)); - git_oid_cpy(&rebase->onto_id, git_annotated_commit_id(onto)); - - if (!rebase->orig_head_name || !rebase->state_path) - return -1; - - error = rebase_init_merge(rebase, repo, branch, upstream, onto); - - git_buf_free(&state_path); - -done: - git_reference_free(head_ref); - git_annotated_commit_free(head_branch); - - return error; + return git_commit_lookup( + &rebase->last_commit, repo, git_annotated_commit_id(onto)); } int git_rebase_init( @@ -665,9 +678,9 @@ int git_rebase_init( const git_rebase_options *given_opts) { git_rebase *rebase = NULL; - git_buf reflog = GIT_BUF_INIT; - git_commit *onto_commit = NULL; + git_annotated_commit *head_branch = NULL; git_reference *head_ref = NULL; + bool inmemory = (given_opts && given_opts->inmemory); int error; assert(repo && (upstream || onto)); @@ -677,39 +690,51 @@ int git_rebase_init( if (!onto) onto = upstream; - if ((error = rebase_check_versions(given_opts)) < 0 || - (error = git_repository__ensure_not_bare(repo, "rebase")) < 0 || - (error = rebase_ensure_not_in_progress(repo)) < 0 || - (error = rebase_ensure_not_dirty(repo, true, true, GIT_ERROR)) < 0 || - (error = git_commit_lookup( - &onto_commit, repo, git_annotated_commit_id(onto))) < 0) - return error; - - rebase = rebase_alloc(given_opts); - - if ((error = rebase_init( - rebase, repo, branch, upstream, onto)) < 0 || - (error = rebase_setupfiles(rebase)) < 0 || - (error = git_buf_printf(&reflog, - "rebase: checkout %s", rebase_onto_name(onto))) < 0 || - (error = git_checkout_tree( - repo, (git_object *)onto_commit, &rebase->options.checkout_options)) < 0 || - (error = git_reference_create(&head_ref, repo, GIT_HEAD_FILE, - git_annotated_commit_id(onto), 1, reflog.ptr)) < 0) + if ((error = rebase_check_versions(given_opts)) < 0) goto done; - *out = rebase; + if (!inmemory) { + if ((error = git_repository__ensure_not_bare(repo, "rebase")) < 0 || + (error = rebase_ensure_not_in_progress(repo)) < 0 || + (error = rebase_ensure_not_dirty(repo, true, true, GIT_ERROR)) < 0) + goto done; + } + + if (!branch) { + if ((error = git_repository_head(&head_ref, repo)) < 0 || + (error = git_annotated_commit_from_ref(&head_branch, repo, head_ref)) < 0) + goto done; + + branch = head_branch; + } + + rebase = rebase_alloc(given_opts); + GITERR_CHECK_ALLOC(rebase); + + rebase->repo = repo; + rebase->inmemory = inmemory; + rebase->type = GIT_REBASE_TYPE_MERGE; + + if ((error = rebase_init_operations(rebase, repo, branch, upstream, onto)) < 0) + goto done; + + if (inmemory) + error = rebase_init_inmemory(rebase, repo, branch, upstream, onto); + else + rebase_init_merge(rebase, repo, branch ,upstream, onto); + + if (error == 0) + *out = rebase; done: git_reference_free(head_ref); + git_annotated_commit_free(head_branch); + if (error < 0) { rebase_cleanup(rebase); git_rebase_free(rebase); } - git_commit_free(onto_commit); - git_buf_free(&reflog); - return error; } @@ -764,9 +789,6 @@ static int rebase_next_merge( *out = NULL; - if ((error = rebase_movenext(rebase)) < 0) - goto done; - operation = git_array_get(rebase->operations, rebase->current); if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || @@ -812,6 +834,46 @@ done: return error; } +static int rebase_next_inmemory( + git_rebase_operation **out, + git_rebase *rebase) +{ + git_commit *current_commit = NULL, *parent_commit = NULL; + git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL; + git_rebase_operation *operation; + git_index *index = NULL; + int error; + + *out = NULL; + + operation = git_array_get(rebase->operations, rebase->current); + + if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || + (error = git_commit_tree(¤t_tree, current_commit)) < 0 || + (error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 || + (error = git_commit_tree(&parent_tree, parent_commit)) < 0 || + (error = git_commit_tree(&head_tree, rebase->last_commit)) < 0 || + (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, NULL)) < 0) + goto done; + + git_index_free(rebase->last_index); + rebase->last_index = index; + operation->index = index; + index = NULL; + + *out = operation; + +done: + git_commit_free(current_commit); + git_commit_free(parent_commit); + git_tree_free(current_tree); + git_tree_free(head_tree); + git_tree_free(parent_tree); + git_index_free(index); + + return error; +} + int git_rebase_next( git_rebase_operation **out, git_rebase *rebase) @@ -820,14 +882,79 @@ int git_rebase_next( assert(out && rebase); - switch (rebase->type) { - case GIT_REBASE_TYPE_MERGE: + if ((error = rebase_movenext(rebase)) < 0) + return error; + + if (rebase->inmemory) + error = rebase_next_inmemory(out, rebase); + else if (rebase->type == GIT_REBASE_TYPE_MERGE) error = rebase_next_merge(out, rebase); - break; - default: + else abort(); + + return error; +} + +static int rebase_commit__create( + git_commit **out, + git_rebase *rebase, + git_index *index, + git_commit *parent_commit, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message) +{ + git_rebase_operation *operation; + git_commit *current_commit = NULL, *commit = NULL; + git_tree *parent_tree = NULL, *tree = NULL; + git_oid tree_id, commit_id; + int error; + + operation = git_array_get(rebase->operations, rebase->current); + + if (git_index_has_conflicts(index)) { + giterr_set(GITERR_REBASE, "conflicts have not been resolved"); + error = GIT_EUNMERGED; + goto done; } + if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || + (error = git_commit_tree(&parent_tree, parent_commit)) < 0 || + (error = git_index_write_tree_to(&tree_id, index, rebase->repo)) < 0 || + (error = git_tree_lookup(&tree, rebase->repo, &tree_id)) < 0) + goto done; + + if (git_oid_equal(&tree_id, git_tree_id(parent_tree))) { + giterr_set(GITERR_REBASE, "this patch has already been applied"); + error = GIT_EAPPLIED; + goto done; + } + + if (!author) + author = git_commit_author(current_commit); + + if (!message) { + message_encoding = git_commit_message_encoding(current_commit); + message = git_commit_message(current_commit); + } + + if ((error = git_commit_create(&commit_id, rebase->repo, NULL, author, + committer, message_encoding, message, tree, 1, + (const git_commit **)&parent_commit)) < 0 || + (error = git_commit_lookup(&commit, rebase->repo, &commit_id)) < 0) + goto done; + + *out = commit; + +done: + if (error < 0) + git_commit_free(commit); + + git_commit_free(current_commit); + git_tree_free(parent_tree); + git_tree_free(tree); + return error; } @@ -839,79 +966,77 @@ static int rebase_commit_merge( const char *message_encoding, const char *message) { - git_index *index = NULL; - git_reference *head = NULL; - git_commit *current_commit = NULL, *head_commit = NULL, *commit = NULL; git_rebase_operation *operation; - git_tree *head_tree = NULL, *tree = NULL; - git_diff *diff = NULL; - git_oid tree_id; - git_buf reflog_msg = GIT_BUF_INIT; + git_reference *head = NULL; + git_commit *head_commit = NULL, *commit = NULL; + git_index *index = NULL; char old_idstr[GIT_OID_HEXSZ], new_idstr[GIT_OID_HEXSZ]; int error; operation = git_array_get(rebase->operations, rebase->current); assert(operation); - if ((error = git_repository_index(&index, rebase->repo)) < 0) - goto done; - - if (git_index_has_conflicts(index)) { - giterr_set(GITERR_REBASE, "Conflicts have not been resolved"); - error = GIT_EUNMERGED; - goto done; - } - if ((error = rebase_ensure_not_dirty(rebase->repo, false, true, GIT_EUNMERGED)) < 0 || - (error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || (error = git_repository_head(&head, rebase->repo)) < 0 || (error = git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT)) < 0 || - (error = git_commit_tree(&head_tree, head_commit)) < 0 || - (error = git_diff_tree_to_index(&diff, rebase->repo, head_tree, index, NULL)) < 0) - goto done; - - if (git_diff_num_deltas(diff) == 0) { - giterr_set(GITERR_REBASE, "This patch has already been applied"); - error = GIT_EAPPLIED; - goto done; - } - - if ((error = git_index_write_tree(&tree_id, index)) < 0 || - (error = git_tree_lookup(&tree, rebase->repo, &tree_id)) < 0) - goto done; - - if (!author) - author = git_commit_author(current_commit); - - if (!message) { - message_encoding = git_commit_message_encoding(current_commit); - message = git_commit_message(current_commit); - } - - if ((error = git_commit_create(commit_id, rebase->repo, NULL, author, - committer, message_encoding, message, tree, 1, - (const git_commit **)&head_commit)) < 0 || - (error = git_commit_lookup(&commit, rebase->repo, commit_id)) < 0 || + (error = git_repository_index(&index, rebase->repo)) < 0 || + (error = rebase_commit__create(&commit, rebase, index, head_commit, + author, committer, message_encoding, message)) < 0 || (error = git_reference__update_for_commit( - rebase->repo, NULL, "HEAD", commit_id, "rebase")) < 0) + rebase->repo, NULL, "HEAD", git_commit_id(commit), "rebase")) < 0) goto done; - git_oid_fmt(old_idstr, git_commit_id(current_commit)); - git_oid_fmt(new_idstr, commit_id); + git_oid_fmt(old_idstr, &operation->id); + git_oid_fmt(new_idstr, git_commit_id(commit)); - error = rebase_setupfile(rebase, REWRITTEN_FILE, O_CREAT|O_WRONLY|O_APPEND, - "%.*s %.*s\n", GIT_OID_HEXSZ, old_idstr, GIT_OID_HEXSZ, new_idstr); + if ((error = rebase_setupfile(rebase, REWRITTEN_FILE, O_CREAT|O_WRONLY|O_APPEND, + "%.*s %.*s\n", GIT_OID_HEXSZ, old_idstr, GIT_OID_HEXSZ, new_idstr)) < 0) + goto done; + + git_oid_cpy(commit_id, git_commit_id(commit)); done: - git_buf_free(&reflog_msg); - git_commit_free(commit); - git_diff_free(diff); - git_tree_free(tree); - git_tree_free(head_tree); - git_commit_free(head_commit); - git_commit_free(current_commit); - git_reference_free(head); git_index_free(index); + git_reference_free(head); + git_commit_free(head_commit); + git_commit_free(commit); + return error; +} + +static int rebase_commit_inmemory( + git_oid *commit_id, + git_rebase *rebase, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message) +{ + git_rebase_operation *operation; + git_commit *commit = NULL; + int error = 0; + + operation = git_array_get(rebase->operations, rebase->current); + + assert(operation); + assert(operation->index); + assert(rebase->last_commit); + + if ((error = rebase_commit__create(&commit, rebase, operation->index, + rebase->last_commit, author, committer, message_encoding, message)) < 0) + goto done; + + git_index_free(rebase->last_index); + operation->index = NULL; + rebase->last_index = NULL; + + git_commit_free(rebase->last_commit); + rebase->last_commit = commit; + + git_oid_cpy(commit_id, git_commit_id(commit)); + +done: + if (error < 0) + git_commit_free(commit); return error; } @@ -928,14 +1053,14 @@ int git_rebase_commit( assert(rebase && committer); - switch (rebase->type) { - case GIT_REBASE_TYPE_MERGE: + if (rebase->inmemory) + error = rebase_commit_inmemory( + id, rebase, author, committer, message_encoding, message); + else if (rebase->type == GIT_REBASE_TYPE_MERGE) error = rebase_commit_merge( id, rebase, author, committer, message_encoding, message); - break; - default: + else abort(); - } return error; } @@ -948,6 +1073,9 @@ int git_rebase_abort(git_rebase *rebase) assert(rebase); + if (rebase->inmemory) + return 0; + error = rebase->head_detached ? git_reference_create(&orig_head_ref, rebase->repo, GIT_HEAD_FILE, &rebase->orig_head_id, 1, "rebase: aborting") : @@ -1125,6 +1253,9 @@ int git_rebase_finish( assert(rebase); + if (rebase->inmemory) + return 0; + git_oid_fmt(onto, &rebase->onto_id); if ((error = git_buf_printf(&branch_msg, "rebase finished: %s onto %.*s", @@ -1182,6 +1313,8 @@ void git_rebase_free(git_rebase *rebase) if (rebase == NULL) return; + git_index_free(rebase->last_index); + git_commit_free(rebase->last_commit); git__free(rebase->onto_name); git__free(rebase->orig_head_name); git__free(rebase->state_path); diff --git a/tests/rebase/inmemory.c b/tests/rebase/inmemory.c new file mode 100644 index 000000000..c7942d6af --- /dev/null +++ b/tests/rebase/inmemory.c @@ -0,0 +1,114 @@ +#include "clar_libgit2.h" +#include "git2/rebase.h" +#include "posix.h" + +#include + +static git_repository *repo; + +// Fixture setup and teardown +void test_rebase_inmemory__initialize(void) +{ + repo = cl_git_sandbox_init("rebase"); +} + +void test_rebase_inmemory__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_rebase_inmemory__not_in_rebase_state(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_options opts = GIT_REBASE_OPTIONS_INIT; + + opts.inmemory = true; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); + cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master")); + + cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref)); + cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts)); + + cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo)); + + git_rebase_free(rebase); + + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + + git_reference_free(branch_ref); + git_reference_free(upstream_ref); +} + +void test_rebase_inmemory__can_resolve_conflicts(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_operation *rebase_operation; + git_status_list *status_list; + git_oid pick_id, commit_id, expected_commit_id; + git_signature *signature; + git_index *repo_index; + git_index_entry resolution = {{0}}; + git_rebase_options opts = GIT_REBASE_OPTIONS_INIT; + + cl_git_pass(git_signature_new(&signature, + "Rebaser", "rebaser@rebaser.rb", 1405694510, 0)); + + opts.inmemory = true; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/asparagus")); + cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master")); + + cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref)); + cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase)); + + git_oid_fromstr(&pick_id, "33f915f9e4dbd9f4b24430e48731a59b45b15500"); + + cl_assert_equal_i(GIT_REBASE_OPERATION_PICK, rebase_operation->type); + cl_assert_equal_oid(&pick_id, &rebase_operation->id); + + /* ensure that we did not do anything stupid to the workdir or repo index */ + cl_git_pass(git_repository_index(&repo_index, repo)); + cl_assert(!git_index_has_conflicts(repo_index)); + + cl_git_pass(git_status_list_new(&status_list, repo, NULL)); + cl_assert_equal_i(0, git_status_list_entrycount(status_list)); + + /* but that the index returned from rebase does have conflicts */ + cl_assert(git_index_has_conflicts(rebase_operation->index)); + + cl_git_fail_with(GIT_EUNMERGED, git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); + + /* ensure that we can work with the in-memory index to resolve the conflict */ + resolution.path = "asparagus.txt"; + resolution.mode = GIT_FILEMODE_BLOB; + git_oid_fromstr(&resolution.id, "414dfc71ead79c07acd4ea47fecf91f289afc4b9"); + cl_git_pass(git_index_conflict_remove(rebase_operation->index, "asparagus.txt")); + cl_git_pass(git_index_add(rebase_operation->index, &resolution)); + + /* and finally create a commit for the resolved rebase operation */ + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); + + cl_git_pass(git_oid_fromstr(&expected_commit_id, "db7af47222181e548810da2ab5fec0e9357c5637")); + cl_assert_equal_oid(&commit_id, &expected_commit_id); + + git_signature_free(signature); + git_status_list_free(status_list); + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_index_free(repo_index); + git_rebase_free(rebase); +} diff --git a/tests/rebase/iterator.c b/tests/rebase/iterator.c index acf2a92db..e761d39d3 100644 --- a/tests/rebase/iterator.c +++ b/tests/rebase/iterator.c @@ -46,26 +46,32 @@ static void test_operations(git_rebase *rebase, size_t expected_current) } } -void test_rebase_iterator__iterates(void) +void test_iterator(bool inmemory) { git_rebase *rebase; + git_rebase_options opts = GIT_REBASE_OPTIONS_INIT; git_reference *branch_ref, *upstream_ref; git_annotated_commit *branch_head, *upstream_head; git_rebase_operation *rebase_operation; git_oid commit_id; int error; + opts.inmemory = inmemory; + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master")); cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref)); cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref)); - cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL)); + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts)); test_operations(rebase, GIT_REBASE_NO_OPERATION); - git_rebase_free(rebase); - cl_git_pass(git_rebase_open(&rebase, repo, NULL)); + if (!inmemory) { + git_rebase_free(rebase); + cl_git_pass(git_rebase_open(&rebase, repo, NULL)); + } + cl_git_pass(git_rebase_next(&rebase_operation, rebase)); cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); @@ -81,8 +87,10 @@ void test_rebase_iterator__iterates(void) NULL, NULL)); test_operations(rebase, 2); - git_rebase_free(rebase); - cl_git_pass(git_rebase_open(&rebase, repo, NULL)); + if (!inmemory) { + git_rebase_free(rebase); + cl_git_pass(git_rebase_open(&rebase, repo, NULL)); + } cl_git_pass(git_rebase_next(&rebase_operation, rebase)); cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, @@ -104,3 +112,13 @@ void test_rebase_iterator__iterates(void) git_reference_free(upstream_ref); git_rebase_free(rebase); } + +void test_rebase_iterator__iterates(void) +{ + test_iterator(false); +} + +void test_rebase_iterator__iterates_inmemory(void) +{ + test_iterator(true); +} From a202e0d45bcfd672cf1ff75abc192e6a592874ba Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 11 Feb 2016 10:11:21 -0800 Subject: [PATCH 363/450] rebase: allow custom merge_options Allow callers of rebase to specify custom merge options. This may allow custom conflict resolution, or failing fast when conflicts are detected. --- include/git2/rebase.h | 8 +++++++- src/rebase.c | 4 ++-- tests/rebase/merge.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/include/git2/rebase.h b/include/git2/rebase.h index 17c4557e8..4fda1fd2f 100644 --- a/include/git2/rebase.h +++ b/include/git2/rebase.h @@ -57,6 +57,11 @@ typedef struct { */ const char *rewrite_notes_ref; + /** + * Options to control how trees are merged during `git_rebase_next`. + */ + git_merge_options merge_options; + /** * Options to control how files are written during `git_rebase_init`, * `git_checkout_next` and `git_checkout_abort`. Note that a minimum @@ -110,7 +115,8 @@ typedef enum { #define GIT_REBASE_OPTIONS_VERSION 1 #define GIT_REBASE_OPTIONS_INIT \ - {GIT_REBASE_OPTIONS_VERSION, 0, 0, NULL, GIT_CHECKOUT_OPTIONS_INIT} + { GIT_REBASE_OPTIONS_VERSION, 0, 0, NULL, GIT_MERGE_OPTIONS_INIT, \ + GIT_CHECKOUT_OPTIONS_INIT} /** Indicates that a rebase operation is not (yet) in progress. */ #define GIT_REBASE_NO_OPERATION SIZE_MAX diff --git a/src/rebase.c b/src/rebase.c index 54affd6e2..52a6df032 100644 --- a/src/rebase.c +++ b/src/rebase.c @@ -813,7 +813,7 @@ static int rebase_next_merge( if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 || (error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%" PRIuZ "\n", rebase->current+1)) < 0 || (error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 || - (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, NULL)) < 0 || + (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0 || (error = git_merge__check_result(rebase->repo, index)) < 0 || (error = git_checkout_index(rebase->repo, index, &checkout_opts)) < 0 || (error = git_indexwriter_commit(&indexwriter)) < 0) @@ -853,7 +853,7 @@ static int rebase_next_inmemory( (error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 || (error = git_commit_tree(&parent_tree, parent_commit)) < 0 || (error = git_commit_tree(&head_tree, rebase->last_commit)) < 0 || - (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, NULL)) < 0) + (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0) goto done; git_index_free(rebase->last_index); diff --git a/tests/rebase/merge.c b/tests/rebase/merge.c index 33eadb7ed..c60113b64 100644 --- a/tests/rebase/merge.c +++ b/tests/rebase/merge.c @@ -565,3 +565,33 @@ void test_rebase_merge__custom_checkout_options(void) git_reference_free(upstream_ref); git_rebase_free(rebase); } + +void test_rebase_merge__custom_merge_options(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_options rebase_options = GIT_REBASE_OPTIONS_INIT; + git_rebase_operation *rebase_operation; + + rebase_options.merge_options.flags |= + GIT_MERGE_FAIL_ON_CONFLICT | + GIT_MERGE_SKIP_REUC; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/asparagus")); + cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master")); + + cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref)); + cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &rebase_options)); + + cl_git_fail_with(GIT_EMERGECONFLICT, git_rebase_next(&rebase_operation, rebase)); + + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); +} + From ad8aa11288d6cb25d7a26e133759e41b79670b4c Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 11 Feb 2016 11:26:42 -0800 Subject: [PATCH 364/450] reset test: fix initialization warning --- tests/reset/hard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/reset/hard.c b/tests/reset/hard.c index 149973374..0c0af914c 100644 --- a/tests/reset/hard.c +++ b/tests/reset/hard.c @@ -238,7 +238,7 @@ void test_reset_hard__reflog_is_correct(void) void test_reset_hard__switch_file_to_dir(void) { - git_index_entry entry = { 0 }; + git_index_entry entry = {{ 0 }}; git_index *idx; git_object *commit; git_tree *tree; From 263e674ec6701b774d8f464150a9d30650c6da46 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 11 Feb 2016 11:41:23 -0800 Subject: [PATCH 365/450] merge tests: correct casts --- tests/merge/workdir/dirty.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/merge/workdir/dirty.c b/tests/merge/workdir/dirty.c index f168963b2..994a1d813 100644 --- a/tests/merge/workdir/dirty.c +++ b/tests/merge/workdir/dirty.c @@ -162,8 +162,8 @@ static void hack_index(char *files[]) cl_git_pass(p_utimes(path.ptr, times)); cl_git_pass(p_stat(path.ptr, &statbuf)); - entry->ctime.seconds = (git_time_t)statbuf.st_ctime; - entry->mtime.seconds = (git_time_t)statbuf.st_mtime; + entry->ctime.seconds = (int32_t)statbuf.st_ctime; + entry->mtime.seconds = (int32_t)statbuf.st_mtime; #if defined(GIT_USE_NSEC) entry->ctime.nanoseconds = statbuf.st_ctim.tv_nsec; entry->mtime.nanoseconds = statbuf.st_mtim.tv_nsec; @@ -175,7 +175,7 @@ static void hack_index(char *files[]) entry->ino = statbuf.st_ino; entry->uid = statbuf.st_uid; entry->gid = statbuf.st_gid; - entry->file_size = statbuf.st_size; + entry->file_size = (uint32_t)statbuf.st_size; } git_buf_free(&path); From 460ae11f0a8178c5d5abc55574dc3e0e312a47ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 11 Feb 2016 22:19:20 +0100 Subject: [PATCH 366/450] commit: don't forget the last header field When we moved the logic to handle the first one, wrong loop logic was kept in place which meant we still finished early. But we now notice it because we're not reading past the last LF we find. This was not noticed before as the last field in the tested commit was multi-line which does not trigger the early break. --- src/commit.c | 2 +- tests/commit/parse.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/commit.c b/src/commit.c index 453506d2f..8faef07df 100644 --- a/src/commit.c +++ b/src/commit.c @@ -568,7 +568,7 @@ int git_commit_header_field(git_buf *out, const git_commit *commit, const char * git_buf_sanitize(out); - while ((eol = strchr(buf, '\n')) && eol[1] != '\0') { + while ((eol = strchr(buf, '\n'))) { /* We can skip continuations here */ if (buf[0] == ' ') { buf = eol + 1; diff --git a/tests/commit/parse.c b/tests/commit/parse.c index 733fbae82..3e1670ec4 100644 --- a/tests/commit/parse.c +++ b/tests/commit/parse.c @@ -453,10 +453,17 @@ cpxtDQQMGYFpXK/71stq\n\ cl_git_pass(git_commit_header_field(&buf, commit, "gpgsig")); cl_assert_equal_s(gpgsig, buf.ptr); + git_buf_clear(&buf); cl_git_fail_with(GIT_ENOTFOUND, git_commit_header_field(&buf, commit, "awesomeness")); cl_git_fail_with(GIT_ENOTFOUND, git_commit_header_field(&buf, commit, "par")); + git_commit__free(commit); + cl_git_pass(parse_commit(&commit, passing_commit_cases[0])); + + cl_git_pass(git_commit_header_field(&buf, commit, "committer")); + cl_assert_equal_s("Vicent Marti 1273848544 +0200", buf.ptr); + git_buf_free(&buf); git_commit__free(commit); } From 3679ebaef5436a662ad74819f6cbd2c1e76e6766 Mon Sep 17 00:00:00 2001 From: Arthur Schreiber Date: Thu, 11 Feb 2016 23:37:52 +0100 Subject: [PATCH 367/450] Horrible fix for #3173. --- src/checkout.c | 5 +++-- src/diff.c | 8 +++---- src/index.c | 4 ++-- src/iterator.c | 5 +++-- src/iterator.h | 1 + src/merge.c | 14 ++++++------ src/pathspec.c | 3 +-- src/stash.c | 8 +++---- src/submodule.c | 6 +++--- tests/diff/iterator.c | 4 ++-- tests/repo/iterator.c | 50 +++++++++++++++++++++---------------------- 11 files changed, 55 insertions(+), 53 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index fd8e2c443..95ce3c9d3 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -2521,7 +2521,8 @@ int git_checkout_iterator( if (data.opts.baseline_index) { if ((error = git_iterator_for_index( - &baseline, data.opts.baseline_index, &baseline_opts)) < 0) + &baseline, git_index_owner(data.opts.baseline_index), + data.opts.baseline_index, &baseline_opts)) < 0) goto cleanup; } else { if ((error = git_iterator_for_tree( @@ -2633,7 +2634,7 @@ int git_checkout_index( return error; GIT_REFCOUNT_INC(index); - if (!(error = git_iterator_for_index(&index_i, index, NULL))) + if (!(error = git_iterator_for_index(&index_i, repo, index, NULL))) error = git_checkout_iterator(index_i, index, opts); if (owned) diff --git a/src/diff.c b/src/diff.c index 67fab0763..9ac5b9250 100644 --- a/src/diff.c +++ b/src/diff.c @@ -1371,7 +1371,7 @@ int git_diff_tree_to_index( DIFF_FROM_ITERATORS( git_iterator_for_tree(&a, old_tree, &a_opts), iflag, - git_iterator_for_index(&b, index, &b_opts), iflag + git_iterator_for_index(&b, repo, index, &b_opts), iflag ); /* if index is in case-insensitive order, re-sort deltas to match */ @@ -1395,7 +1395,7 @@ int git_diff_index_to_workdir( return error; DIFF_FROM_ITERATORS( - git_iterator_for_index(&a, index, &a_opts), + git_iterator_for_index(&a, repo, index, &a_opts), GIT_ITERATOR_INCLUDE_CONFLICTS, git_iterator_for_workdir(&b, repo, index, NULL, &b_opts), @@ -1472,8 +1472,8 @@ int git_diff_index_to_index( assert(diff && old_index && new_index); DIFF_FROM_ITERATORS( - git_iterator_for_index(&a, old_index, &a_opts), GIT_ITERATOR_DONT_IGNORE_CASE, - git_iterator_for_index(&b, new_index, &b_opts), GIT_ITERATOR_DONT_IGNORE_CASE + git_iterator_for_index(&a, repo, old_index, &a_opts), GIT_ITERATOR_DONT_IGNORE_CASE, + git_iterator_for_index(&b, repo, new_index, &b_opts), GIT_ITERATOR_DONT_IGNORE_CASE ); /* if index is in case-insensitive order, re-sort deltas to match */ diff --git a/src/index.c b/src/index.c index 081a1ea59..ac4d8eee7 100644 --- a/src/index.c +++ b/src/index.c @@ -2907,8 +2907,8 @@ int git_index_read_index( opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; - if ((error = git_iterator_for_index(&index_iterator, index, &opts)) < 0 || - (error = git_iterator_for_index(&new_iterator, (git_index *)new_index, &opts)) < 0) + if ((error = git_iterator_for_index(&index_iterator, git_index_owner(index), index, &opts)) < 0 || + (error = git_iterator_for_index(&new_iterator, git_index_owner(new_index), (git_index *)new_index, &opts)) < 0) goto done; if (((error = git_iterator_current(&old_entry, index_iterator)) < 0 && diff --git a/src/iterator.c b/src/iterator.c index ee348de6e..04aac3e01 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -1080,6 +1080,7 @@ static void index_iterator__free(git_iterator *self) int git_iterator_for_index( git_iterator **iter, + git_repository *repo, git_index *index, git_iterator_options *options) { @@ -1093,7 +1094,7 @@ int git_iterator_for_index( } ii->index = index; - ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index)); + ITERATOR_BASE_INIT(ii, index, INDEX, repo); if ((error = iterator__update_ignore_case((git_iterator *)ii, options ? options->flags : 0)) < 0) { git_iterator_free((git_iterator *)ii); @@ -2071,7 +2072,7 @@ int git_iterator_advance_over_with_status( if (!error) continue; - + else if (error == GIT_ENOTFOUND) { /* we entered this directory only hoping to find child matches to * our pathlist (eg, this is `foo` and we had a pathlist entry for diff --git a/src/iterator.h b/src/iterator.h index 59f87e9de..ac17d2970 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -95,6 +95,7 @@ extern int git_iterator_for_tree( */ extern int git_iterator_for_index( git_iterator **out, + git_repository *repo, git_index *index, git_iterator_options *options); diff --git a/src/merge.c b/src/merge.c index 70c705af6..d2f92ccce 100644 --- a/src/merge.c +++ b/src/merge.c @@ -2083,9 +2083,9 @@ static int iterator_for_annotated_commit( opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; if (commit == NULL) { - error = git_iterator_for_nothing(out, &opts); + error = git_iterator_for_nothing(out, &opts); } else if (commit->type == GIT_ANNOTATED_COMMIT_VIRTUAL) { - error = git_iterator_for_index(out, commit->index, &opts); + error = git_iterator_for_index(out, git_index_owner(commit->index), commit->index, &opts); } else { if (!commit->tree && (error = git_commit_tree(&commit->tree, commit->commit)) < 0) @@ -2427,7 +2427,7 @@ static int write_merge_msg( assert(repo && heads); entries = git__calloc(heads_len, sizeof(struct merge_msg_entry)); - GITERR_CHECK_ALLOC(entries); + GITERR_CHECK_ALLOC(entries); if (git_vector_init(&matching, heads_len, NULL) < 0) { git__free(entries); @@ -2481,7 +2481,7 @@ static int write_merge_msg( if (matching.length) sep =','; - + if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_tag)) < 0 || (error = merge_msg_write_tags(&file, &matching, sep)) < 0) goto cleanup; @@ -2682,8 +2682,8 @@ static int merge_check_index(size_t *conflicts, git_repository *repo, git_index iter_opts.pathlist.strings = (char **)staged_paths.contents; iter_opts.pathlist.count = staged_paths.length; - if ((error = git_iterator_for_index(&iter_repo, index_repo, &iter_opts)) < 0 || - (error = git_iterator_for_index(&iter_new, index_new, &iter_opts)) < 0 || + if ((error = git_iterator_for_index(&iter_repo, repo, index_repo, &iter_opts)) < 0 || + (error = git_iterator_for_index(&iter_new, repo, index_new, &iter_opts)) < 0 || (error = git_diff__from_iterators(&index_diff_list, repo, iter_repo, iter_new, &opts)) < 0) goto done; @@ -2759,7 +2759,7 @@ int git_merge__check_result(git_repository *repo, git_index *index_new) if ((error = git_repository_head_tree(&head_tree, repo)) < 0 || (error = git_iterator_for_tree(&iter_head, head_tree, &iter_opts)) < 0 || - (error = git_iterator_for_index(&iter_new, index_new, &iter_opts)) < 0 || + (error = git_iterator_for_index(&iter_new, repo, index_new, &iter_opts)) < 0 || (error = git_diff__from_iterators(&merged_list, repo, iter_head, iter_new, &opts)) < 0) goto done; diff --git a/src/pathspec.c b/src/pathspec.c index 5bb69ec4b..8a93cdd50 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -550,7 +550,7 @@ int git_pathspec_match_index( iter_opts.flags = pathspec_match_iter_flags(flags); - if (!(error = git_iterator_for_index(&iter, index, &iter_opts))) { + if (!(error = git_iterator_for_index(&iter, git_index_owner(index), index, &iter_opts))) { error = pathspec_match_from_iterator(out, iter, flags, ps); git_iterator_free(iter); } @@ -718,4 +718,3 @@ const char * git_pathspec_match_list_failed_entry( return entry ? *entry : NULL; } - diff --git a/src/stash.c b/src/stash.c index 35824659a..43a464e64 100644 --- a/src/stash.c +++ b/src/stash.c @@ -685,8 +685,8 @@ static int merge_indexes( iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, &iter_opts)) < 0 || - (error = git_iterator_for_index(&ours, ours_index, &iter_opts)) < 0 || - (error = git_iterator_for_index(&theirs, theirs_index, &iter_opts)) < 0) + (error = git_iterator_for_index(&ours, repo, ours_index, &iter_opts)) < 0 || + (error = git_iterator_for_index(&theirs, repo, theirs_index, &iter_opts)) < 0) goto done; error = git_merge__iterators(out, repo, ancestor, ours, theirs, NULL); @@ -712,7 +712,7 @@ static int merge_index_and_tree( iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, &iter_opts)) < 0 || - (error = git_iterator_for_index(&ours, ours_index, &iter_opts)) < 0 || + (error = git_iterator_for_index(&ours, repo, ours_index, &iter_opts)) < 0 || (error = git_iterator_for_tree(&theirs, theirs_tree, &iter_opts)) < 0) goto done; @@ -728,7 +728,7 @@ done: static void normalize_apply_options( git_stash_apply_options *opts, const git_stash_apply_options *given_apply_opts) -{ +{ if (given_apply_opts != NULL) { memcpy(opts, given_apply_opts, sizeof(git_stash_apply_options)); } else { diff --git a/src/submodule.c b/src/submodule.c index 6ec4c53e1..3e55c5676 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -327,7 +327,7 @@ static int submodules_from_index(git_strmap *map, git_index *idx, git_config *cf const git_index_entry *entry; git_buf name = GIT_BUF_INIT; - if ((error = git_iterator_for_index(&i, idx, NULL)) < 0) + if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0) return error; while (!(error = git_iterator_advance(&entry, i))) { @@ -1037,7 +1037,7 @@ static int submodule_repo_create( /** * Repodir: path to the sub-repo. sub-repo goes in: - * /modules// with a gitlink in the + * /modules// with a gitlink in the * sub-repo workdir directory to that repository. */ error = git_buf_join3( @@ -1154,7 +1154,7 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio clone_options.repository_cb_payload = sm; /* - * Do not perform checkout as part of clone, instead we + * Do not perform checkout as part of clone, instead we * will checkout the specific commit manually. */ clone_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE; diff --git a/tests/diff/iterator.c b/tests/diff/iterator.c index eafb1b9da..8417e8ed4 100644 --- a/tests/diff/iterator.c +++ b/tests/diff/iterator.c @@ -380,7 +380,7 @@ static void index_iterator_test( iter_opts.start = start; iter_opts.end = end; - cl_git_pass(git_iterator_for_index(&i, index, &iter_opts)); + cl_git_pass(git_iterator_for_index(&i, repo, index, &iter_opts)); while (!(error = git_iterator_advance(&entry, i))) { cl_assert(entry); @@ -974,7 +974,7 @@ static void check_index_range( i_opts.start = start; i_opts.end = end; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, repo, index, &i_opts)); cl_assert(git_iterator_ignore_case(i) == ignore_case); diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c index 83b824691..c18e24a4f 100644 --- a/tests/repo/iterator.c +++ b/tests/repo/iterator.c @@ -134,19 +134,19 @@ void test_repo_iterator__index(void) cl_git_pass(git_repository_index(&index, g_repo)); /* autoexpand with no tree entries for index */ - cl_git_pass(git_iterator_for_index(&i, index, NULL)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, NULL)); expect_iterator_items(i, 20, NULL, 20, NULL); git_iterator_free(i); /* auto expand with tree entries */ i_opts.flags = GIT_ITERATOR_INCLUDE_TREES; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 22, NULL, 22, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 12, NULL, 22, NULL); git_iterator_free(i); @@ -171,13 +171,13 @@ void test_repo_iterator__index_icase(void) /* autoexpand with no tree entries over range */ i_opts.start = "c"; i_opts.end = "k/D"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 7, NULL, 7, NULL); git_iterator_free(i); i_opts.start = "k"; i_opts.end = "k/Z"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 3, NULL, 3, NULL); git_iterator_free(i); @@ -186,13 +186,13 @@ void test_repo_iterator__index_icase(void) i_opts.start = "c"; i_opts.end = "k/D"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 8, NULL, 8, NULL); git_iterator_free(i); i_opts.start = "k"; i_opts.end = "k/Z"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 4, NULL, 4, NULL); git_iterator_free(i); @@ -201,13 +201,13 @@ void test_repo_iterator__index_icase(void) i_opts.start = "c"; i_opts.end = "k/D"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 5, NULL, 8, NULL); git_iterator_free(i); i_opts.start = "k"; i_opts.end = "k/Z"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 1, NULL, 4, NULL); git_iterator_free(i); @@ -219,13 +219,13 @@ void test_repo_iterator__index_icase(void) i_opts.start = "c"; i_opts.end = "k/D"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 13, NULL, 13, NULL); git_iterator_free(i); i_opts.start = "k"; i_opts.end = "k/Z"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 5, NULL, 5, NULL); git_iterator_free(i); @@ -234,13 +234,13 @@ void test_repo_iterator__index_icase(void) i_opts.start = "c"; i_opts.end = "k/D"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 14, NULL, 14, NULL); git_iterator_free(i); i_opts.start = "k"; i_opts.end = "k/Z"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 6, NULL, 6, NULL); git_iterator_free(i); @@ -249,13 +249,13 @@ void test_repo_iterator__index_icase(void) i_opts.start = "c"; i_opts.end = "k/D"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 9, NULL, 14, NULL); git_iterator_free(i); i_opts.start = "k"; i_opts.end = "k/Z"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 1, NULL, 6, NULL); git_iterator_free(i); @@ -1108,14 +1108,14 @@ void test_repo_iterator__indexfilelist(void) /* All indexfilelist iterator tests are "autoexpand with no tree entries" */ - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 8, NULL, 8, NULL); git_iterator_free(i); i_opts.start = "c"; i_opts.end = NULL; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); /* (c D e k/1 k/a L ==> 6) vs (c e k/1 k/a ==> 4) */ expect = ((default_icase) ? 6 : 4); expect_iterator_items(i, expect, NULL, expect, NULL); @@ -1124,7 +1124,7 @@ void test_repo_iterator__indexfilelist(void) i_opts.start = NULL; i_opts.end = "e"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); /* (a B c D e ==> 5) vs (B D L/1 a c e ==> 6) */ expect = ((default_icase) ? 5 : 6); expect_iterator_items(i, expect, NULL, expect, NULL); @@ -1166,7 +1166,7 @@ void test_repo_iterator__indexfilelist_2(void) /* (c D e k/1 k/a ==> 5) vs (c e k/1 ==> 3) */ expect = default_icase ? 5 : 3; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, expect, NULL, expect, NULL); git_iterator_free(i); @@ -1208,7 +1208,7 @@ void test_repo_iterator__indexfilelist_3(void) /* (c D e k/1 k/a k/B k/c k/D) vs (c e k/1 k/B k/D) */ expect = default_icase ? 8 : 5; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, expect, NULL, expect, NULL); git_iterator_free(i); @@ -1250,7 +1250,7 @@ void test_repo_iterator__indexfilelist_4(void) /* (c D e k/1 k/a k/B k/c k/D) vs (c e k/1 k/B k/D) */ expect = default_icase ? 8 : 5; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, expect, NULL, expect, NULL); git_iterator_free(i); @@ -1291,13 +1291,13 @@ void test_repo_iterator__indexfilelist_icase(void) i_opts.start = "c"; i_opts.end = "k/D"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 3, NULL, 3, NULL); git_iterator_free(i); i_opts.start = "k"; i_opts.end = "k/Z"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 1, NULL, 1, NULL); git_iterator_free(i); @@ -1306,13 +1306,13 @@ void test_repo_iterator__indexfilelist_icase(void) i_opts.start = "c"; i_opts.end = "k/D"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 5, NULL, 5, NULL); git_iterator_free(i); i_opts.start = "k"; i_opts.end = "k/Z"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 2, NULL, 2, NULL); git_iterator_free(i); From 35439f5997c41d0c50d58997c2167ee93aad6c30 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 11 Feb 2016 12:24:21 -0800 Subject: [PATCH 368/450] win32: introduce p_timeval that isn't stupid Windows defines `timeval` with `long`, which we cannot sanely cope with. Instead, use a custom timeval struct. --- src/unix/posix.h | 4 +++- src/win32/posix.h | 5 +++-- src/win32/posix_w32.c | 4 ++-- src/win32/w32_util.h | 2 +- src/win32/win32-compat.h | 7 +++++++ tests/checkout/checkout_helpers.c | 2 +- tests/core/posix.c | 2 +- tests/diff/workdir.c | 4 ++-- tests/index/racy.c | 2 +- tests/merge/workdir/dirty.c | 2 +- 10 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/unix/posix.h b/src/unix/posix.h index 6633689bc..83edf2b7e 100644 --- a/src/unix/posix.h +++ b/src/unix/posix.h @@ -52,8 +52,10 @@ extern char *p_realpath(const char *, char *); #define p_localtime_r(c, r) localtime_r(c, r) #define p_gmtime_r(c, r) gmtime_r(c, r) +#define p_timeval timeval + #ifdef HAVE_FUTIMENS -GIT_INLINE(int) p_futimes(int f, const struct timeval t[2]) +GIT_INLINE(int) p_futimes(int f, const struct p_timeval t[2]) { struct timespec s[2]; s[0].tv_sec = t[0].tv_sec; diff --git a/src/win32/posix.h b/src/win32/posix.h index ac98fd864..732128bb0 100644 --- a/src/win32/posix.h +++ b/src/win32/posix.h @@ -9,6 +9,7 @@ #include "common.h" #include "../posix.h" +#include "win32-compat.h" #include "path_w32.h" #include "utf-conv.h" #include "dir.h" @@ -20,8 +21,8 @@ typedef SOCKET GIT_SOCKET; extern int p_lstat(const char *file_name, struct stat *buf); extern int p_stat(const char* path, struct stat* buf); -extern int p_utimes(const char *filename, const struct timeval times[2]); -extern int p_futimes(int fd, const struct timeval times[2]); +extern int p_utimes(const char *filename, const struct p_timeval times[2]); +extern int p_futimes(int fd, const struct p_timeval times[2]); extern int p_readlink(const char *path, char *buf, size_t bufsiz); extern int p_symlink(const char *old, const char *new); diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index 414cb4701..d743e8fc8 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -210,7 +210,7 @@ int p_lstat_posixly(const char *filename, struct stat *buf) return do_lstat(filename, buf, true); } -int p_utimes(const char *filename, const struct timeval times[2]) +int p_utimes(const char *filename, const struct p_timeval times[2]) { int fd, error; @@ -223,7 +223,7 @@ int p_utimes(const char *filename, const struct timeval times[2]) return error; } -int p_futimes(int fd, const struct timeval times[2]) +int p_futimes(int fd, const struct p_timeval times[2]) { HANDLE handle; FILETIME atime = {0}, mtime = {0}; diff --git a/src/win32/w32_util.h b/src/win32/w32_util.h index 727ed1cef..b095939a1 100644 --- a/src/win32/w32_util.h +++ b/src/win32/w32_util.h @@ -96,7 +96,7 @@ GIT_INLINE(void) git_win32__filetime_to_timespec( } GIT_INLINE(void) git_win32__timeval_to_filetime( - FILETIME *ft, const struct timeval tv) + FILETIME *ft, const struct p_timeval tv) { long long ticks = (tv.tv_sec * 10000000LL) + (tv.tv_usec * 10LL) + 116444736000000000LL; diff --git a/src/win32/win32-compat.h b/src/win32/win32-compat.h index d3a5b68a3..dff1f45be 100644 --- a/src/win32/win32-compat.h +++ b/src/win32/win32-compat.h @@ -13,6 +13,13 @@ #include #include +typedef long suseconds_t; + +struct p_timeval { + time_t tv_sec; + suseconds_t tv_usec; +}; + struct p_timespec { time_t tv_sec; long tv_nsec; diff --git a/tests/checkout/checkout_helpers.c b/tests/checkout/checkout_helpers.c index fb2f415e7..d7d24f33f 100644 --- a/tests/checkout/checkout_helpers.c +++ b/tests/checkout/checkout_helpers.c @@ -133,7 +133,7 @@ int checkout_count_callback( void tick_index(git_index *index) { struct timespec ts; - struct timeval times[2]; + struct p_timeval times[2]; cl_assert(index->on_disk); cl_assert(git_index_path(index)); diff --git a/tests/core/posix.c b/tests/core/posix.c index 5a9e24899..34a67bf47 100644 --- a/tests/core/posix.c +++ b/tests/core/posix.c @@ -100,7 +100,7 @@ void test_core_posix__inet_pton(void) void test_core_posix__utimes(void) { - struct timeval times[2]; + struct p_timeval times[2]; struct stat st; time_t curtime; int fd; diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c index 4c782339d..892c7b72d 100644 --- a/tests/diff/workdir.c +++ b/tests/diff/workdir.c @@ -1755,7 +1755,7 @@ void test_diff_workdir__with_stale_index(void) static int touch_file(void *payload, git_buf *path) { struct stat st; - struct timeval times[2]; + struct p_timeval times[2]; GIT_UNUSED(payload); if (git_path_isdir(path->ptr)) @@ -2006,7 +2006,7 @@ void test_diff_workdir__only_writes_index_when_necessary(void) git_oid initial, first, second; git_buf path = GIT_BUF_INIT; struct stat st; - struct timeval times[2]; + struct p_timeval times[2]; opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_UPDATE_INDEX; diff --git a/tests/index/racy.c b/tests/index/racy.c index e2275ea14..ace84d585 100644 --- a/tests/index/racy.c +++ b/tests/index/racy.c @@ -54,7 +54,7 @@ void test_index_racy__write_index_just_after_file(void) git_index *index; git_diff *diff; git_buf path = GIT_BUF_INIT; - struct timeval times[2]; + struct p_timeval times[2]; /* Make sure we do have a timestamp */ cl_git_pass(git_repository_index(&index, g_repo)); diff --git a/tests/merge/workdir/dirty.c b/tests/merge/workdir/dirty.c index 994a1d813..99e33e0cd 100644 --- a/tests/merge/workdir/dirty.c +++ b/tests/merge/workdir/dirty.c @@ -133,7 +133,7 @@ static void hack_index(char *files[]) struct stat statbuf; git_buf path = GIT_BUF_INIT; git_index_entry *entry; - struct timeval times[2]; + struct p_timeval times[2]; time_t now; size_t i; From f28bae0c380467409515ffd25247f3204dcc4019 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 15 Feb 2016 17:16:00 +0000 Subject: [PATCH 369/450] rebase: persist a single in-memory index When performing an in-memory rebase, keep a single index for the duration, so that callers have the expected index lifecycle and do not hold on to an index that is free'd out from under them. --- include/git2/rebase.h | 21 +++++++++++++++------ src/rebase.c | 35 +++++++++++++++++++++++------------ tests/rebase/inmemory.c | 10 ++++++---- tests/rebase/iterator.c | 20 ++++++++++++++++++-- 4 files changed, 62 insertions(+), 24 deletions(-) diff --git a/include/git2/rebase.h b/include/git2/rebase.h index 4fda1fd2f..ece8b3664 100644 --- a/include/git2/rebase.h +++ b/include/git2/rebase.h @@ -142,12 +142,6 @@ typedef struct { * be populated for operations of type `GIT_REBASE_OPERATION_EXEC`. */ const char *exec; - - /** - * The index that is the result of an operation. - * This is set only for in-memory rebases. - */ - git_index *index; } git_rebase_operation; /** @@ -247,6 +241,21 @@ GIT_EXTERN(int) git_rebase_next( git_rebase_operation **operation, git_rebase *rebase); +/** + * Gets the index produced by the last operation, which is the result + * of `git_rebase_next` and which will be committed by the next + * invocation of `git_rebase_commit`. This is useful for resolving + * conflicts in an in-memory rebase before committing them. You must + * call `git_index_free` when you are finished with this. + * + * This is only applicable for in-memory rebases; for rebases within + * a working directory, the changes were applied to the repository's + * index. + */ +GIT_EXTERN(int) git_rebase_inmemory_index( + git_index **index, + git_rebase *rebase); + /** * Commits the current patch. You must have resolved any conflicts that * were introduced during the patch application from the `git_rebase_next` diff --git a/src/rebase.c b/src/rebase.c index 52a6df032..b9d1d4fc5 100644 --- a/src/rebase.c +++ b/src/rebase.c @@ -71,8 +71,8 @@ struct git_rebase { size_t current; /* Used by in-memory rebase */ + git_index *index; git_commit *last_commit; - git_index *last_index; /* Used by regular (not in-memory) merge-style rebase */ git_oid orig_head_id; @@ -856,10 +856,13 @@ static int rebase_next_inmemory( (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0) goto done; - git_index_free(rebase->last_index); - rebase->last_index = index; - operation->index = index; - index = NULL; + if (!rebase->index) { + rebase->index = index; + index = NULL; + } else { + if ((error = git_index_read_index(rebase->index, index)) < 0) + goto done; + } *out = operation; @@ -895,6 +898,18 @@ int git_rebase_next( return error; } +int git_rebase_inmemory_index( + git_index **out, + git_rebase *rebase) +{ + assert(out && rebase && rebase->index); + + GIT_REFCOUNT_INC(rebase->index); + *out = rebase->index; + + return 0; +} + static int rebase_commit__create( git_commit **out, git_rebase *rebase, @@ -1018,17 +1033,13 @@ static int rebase_commit_inmemory( operation = git_array_get(rebase->operations, rebase->current); assert(operation); - assert(operation->index); + assert(rebase->index); assert(rebase->last_commit); - if ((error = rebase_commit__create(&commit, rebase, operation->index, + if ((error = rebase_commit__create(&commit, rebase, rebase->index, rebase->last_commit, author, committer, message_encoding, message)) < 0) goto done; - git_index_free(rebase->last_index); - operation->index = NULL; - rebase->last_index = NULL; - git_commit_free(rebase->last_commit); rebase->last_commit = commit; @@ -1313,7 +1324,7 @@ void git_rebase_free(git_rebase *rebase) if (rebase == NULL) return; - git_index_free(rebase->last_index); + git_index_free(rebase->index); git_commit_free(rebase->last_commit); git__free(rebase->onto_name); git__free(rebase->orig_head_name); diff --git a/tests/rebase/inmemory.c b/tests/rebase/inmemory.c index c7942d6af..d5d89c719 100644 --- a/tests/rebase/inmemory.c +++ b/tests/rebase/inmemory.c @@ -54,7 +54,7 @@ void test_rebase_inmemory__can_resolve_conflicts(void) git_status_list *status_list; git_oid pick_id, commit_id, expected_commit_id; git_signature *signature; - git_index *repo_index; + git_index *rebase_index, *repo_index; git_index_entry resolution = {{0}}; git_rebase_options opts = GIT_REBASE_OPTIONS_INIT; @@ -86,7 +86,8 @@ void test_rebase_inmemory__can_resolve_conflicts(void) cl_assert_equal_i(0, git_status_list_entrycount(status_list)); /* but that the index returned from rebase does have conflicts */ - cl_assert(git_index_has_conflicts(rebase_operation->index)); + cl_git_pass(git_rebase_inmemory_index(&rebase_index, rebase)); + cl_assert(git_index_has_conflicts(rebase_index)); cl_git_fail_with(GIT_EUNMERGED, git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); @@ -94,8 +95,8 @@ void test_rebase_inmemory__can_resolve_conflicts(void) resolution.path = "asparagus.txt"; resolution.mode = GIT_FILEMODE_BLOB; git_oid_fromstr(&resolution.id, "414dfc71ead79c07acd4ea47fecf91f289afc4b9"); - cl_git_pass(git_index_conflict_remove(rebase_operation->index, "asparagus.txt")); - cl_git_pass(git_index_add(rebase_operation->index, &resolution)); + cl_git_pass(git_index_conflict_remove(rebase_index, "asparagus.txt")); + cl_git_pass(git_index_add(rebase_index, &resolution)); /* and finally create a commit for the resolved rebase operation */ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); @@ -110,5 +111,6 @@ void test_rebase_inmemory__can_resolve_conflicts(void) git_reference_free(branch_ref); git_reference_free(upstream_ref); git_index_free(repo_index); + git_index_free(rebase_index); git_rebase_free(rebase); } diff --git a/tests/rebase/iterator.c b/tests/rebase/iterator.c index e761d39d3..db57b0a83 100644 --- a/tests/rebase/iterator.c +++ b/tests/rebase/iterator.c @@ -13,7 +13,8 @@ void test_rebase_iterator__initialize(void) { repo = cl_git_sandbox_init("rebase"); cl_git_pass(git_repository_index(&_index, repo)); - cl_git_pass(git_signature_now(&signature, "Rebaser", "rebaser@rebaser.rb")); + cl_git_pass(git_signature_new(&signature, "Rebaser", + "rebaser@rebaser.rb", 1405694510, 0)); } void test_rebase_iterator__cleanup(void) @@ -53,7 +54,7 @@ void test_iterator(bool inmemory) git_reference *branch_ref, *upstream_ref; git_annotated_commit *branch_head, *upstream_head; git_rebase_operation *rebase_operation; - git_oid commit_id; + git_oid commit_id, expected_id; int error; opts.inmemory = inmemory; @@ -77,16 +78,25 @@ void test_iterator(bool inmemory) NULL, NULL)); test_operations(rebase, 0); + git_oid_fromstr(&expected_id, "776e4c48922799f903f03f5f6e51da8b01e4cce0"); + cl_assert_equal_oid(&expected_id, &commit_id); + cl_git_pass(git_rebase_next(&rebase_operation, rebase)); cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); test_operations(rebase, 1); + git_oid_fromstr(&expected_id, "ba1f9b4fd5cf8151f7818be2111cc0869f1eb95a"); + cl_assert_equal_oid(&expected_id, &commit_id); + cl_git_pass(git_rebase_next(&rebase_operation, rebase)); cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); test_operations(rebase, 2); + git_oid_fromstr(&expected_id, "948b12fe18b84f756223a61bece4c307787cd5d4"); + cl_assert_equal_oid(&expected_id, &commit_id); + if (!inmemory) { git_rebase_free(rebase); cl_git_pass(git_rebase_open(&rebase, repo, NULL)); @@ -97,11 +107,17 @@ void test_iterator(bool inmemory) NULL, NULL)); test_operations(rebase, 3); + git_oid_fromstr(&expected_id, "d9d5d59d72c9968687f9462578d79878cd80e781"); + cl_assert_equal_oid(&expected_id, &commit_id); + cl_git_pass(git_rebase_next(&rebase_operation, rebase)); cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); test_operations(rebase, 4); + git_oid_fromstr(&expected_id, "9cf383c0a125d89e742c5dec58ed277dd07588b3"); + cl_assert_equal_oid(&expected_id, &commit_id); + cl_git_fail(error = git_rebase_next(&rebase_operation, rebase)); cl_assert_equal_i(GIT_ITEROVER, error); test_operations(rebase, 4); From aadad4059224c686f1ee1b451e3a29c76b5c4b45 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 11 Feb 2016 14:28:31 -0800 Subject: [PATCH 370/450] tree: zap warnings around `size_t` vs `uint16_t` --- src/tree.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/tree.c b/src/tree.c index aab4b58ad..cfceb3f33 100644 --- a/src/tree.c +++ b/src/tree.c @@ -17,6 +17,9 @@ #define DEFAULT_TREE_SIZE 16 #define MAX_FILEMODE_BYTES 6 +#define TREE_ENTRY_CHECK_NAMELEN(n) \ + if (n > UINT16_MAX) { giterr_set(GITERR_INVALID, "tree entry path too long"); } + GIT__USE_STRMAP static bool valid_filemode(const int filemode) @@ -89,10 +92,7 @@ static git_tree_entry *alloc_entry_base(git_pool *pool, const char *filename, si git_tree_entry *entry = NULL; size_t tree_len; - if (filename_len > UINT16_MAX) { - giterr_set(GITERR_INVALID, "tree entry is over UINT16_MAX in length"); - return NULL; - } + TREE_ENTRY_CHECK_NAMELEN(filename_len); if (GIT_ADD_SIZET_OVERFLOW(&tree_len, sizeof(git_tree_entry), filename_len) || GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1)) @@ -106,7 +106,7 @@ static git_tree_entry *alloc_entry_base(git_pool *pool, const char *filename, si memset(entry, 0x0, sizeof(git_tree_entry)); memcpy(entry->filename, filename, filename_len); entry->filename[filename_len] = 0; - entry->filename_len = filename_len; + entry->filename_len = (uint16_t)filename_len; return entry; } @@ -143,8 +143,8 @@ static int homing_search_cmp(const void *key, const void *array_member) const struct tree_key_search *ksearch = key; const git_tree_entry *entry = array_member; - const size_t len1 = ksearch->filename_len; - const size_t len2 = entry->filename_len; + const uint16_t len1 = ksearch->filename_len; + const uint16_t len2 = entry->filename_len; return memcmp( ksearch->filename, @@ -180,8 +180,10 @@ static int tree_key_search( const git_tree_entry *entry; size_t homing, i; + TREE_ENTRY_CHECK_NAMELEN(filename_len); + ksearch.filename = filename; - ksearch.filename_len = filename_len; + ksearch.filename_len = (uint16_t)filename_len; /* Initial homing search; find an entry on the tree with * the same prefix as the filename we're looking for */ @@ -334,6 +336,7 @@ const git_tree_entry *git_tree_entry_byname( const git_tree *tree, const char *filename) { assert(tree && filename); + return entry_fromname(tree, filename, strlen(filename)); } @@ -364,13 +367,16 @@ int git_tree__prefix_position(const git_tree *tree, const char *path) { const git_vector *entries = &tree->entries; struct tree_key_search ksearch; - size_t at_pos; + size_t at_pos, path_len; if (!path) return 0; + path_len = strlen(path); + TREE_ENTRY_CHECK_NAMELEN(path_len); + ksearch.filename = path; - ksearch.filename_len = strlen(path); + ksearch.filename_len = (uint16_t)path_len; /* be safe when we cast away constness - i.e. don't trigger a sort */ assert(git_vector_is_sorted(&tree->entries)); From c4d23928c3801041f392adc173ffa62a8249ee25 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 11 Feb 2016 15:41:07 -0800 Subject: [PATCH 371/450] fstat: use our custom `stat` --- src/win32/posix.h | 5 ++-- src/win32/posix_w32.c | 16 +++++++++++++ src/win32/w32_util.h | 53 +++++++++++++++++++++++++++++++++++-------- 3 files changed, 62 insertions(+), 12 deletions(-) diff --git a/src/win32/posix.h b/src/win32/posix.h index 732128bb0..5fab267c2 100644 --- a/src/win32/posix.h +++ b/src/win32/posix.h @@ -17,9 +17,10 @@ typedef SOCKET GIT_SOCKET; #define p_lseek(f,n,w) _lseeki64(f, n, w) -#define p_fstat(f,b) _fstat64(f, b) + +extern int p_fstat(int fd, struct stat *buf); extern int p_lstat(const char *file_name, struct stat *buf); -extern int p_stat(const char* path, struct stat* buf); +extern int p_stat(const char* path, struct stat *buf); extern int p_utimes(const char *filename, const struct p_timeval times[2]); extern int p_futimes(int fd, const struct p_timeval times[2]); diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index d743e8fc8..fea634b00 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -398,6 +398,22 @@ static int follow_and_lstat_link(git_win32_path path, struct stat* buf) return lstat_w(target_w, buf, false); } +int p_fstat(int fd, struct stat *buf) +{ + BY_HANDLE_FILE_INFORMATION fhInfo; + + HANDLE fh = (HANDLE)_get_osfhandle(fd); + + if (fh == INVALID_HANDLE_VALUE || + !GetFileInformationByHandle(fh, &fhInfo)) { + errno = EBADF; + return -1; + } + + git_win32__file_information_to_stat(buf, &fhInfo); + return 0; +} + int p_stat(const char* path, struct stat* buf) { git_win32_path path_w; diff --git a/src/win32/w32_util.h b/src/win32/w32_util.h index b095939a1..2e475e5e9 100644 --- a/src/win32/w32_util.h +++ b/src/win32/w32_util.h @@ -105,19 +105,25 @@ GIT_INLINE(void) git_win32__timeval_to_filetime( ft->dwLowDateTime = (ticks & 0xffffffffLL); } -GIT_INLINE(int) git_win32__file_attribute_to_stat( +GIT_INLINE(void) git_win32__stat_init( struct stat *st, - const WIN32_FILE_ATTRIBUTE_DATA *attrdata, - const wchar_t *path) + DWORD dwFileAttributes, + DWORD nFileSizeHigh, + DWORD nFileSizeLow, + FILETIME ftCreationTime, + FILETIME ftLastAccessTime, + FILETIME ftLastWriteTime) { mode_t mode = S_IREAD; - if (attrdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + memset(st, 0, sizeof(struct stat)); + + if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) mode |= S_IFDIR; else mode |= S_IFREG; - if ((attrdata->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0) + if ((dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0) mode |= S_IWRITE; st->st_ino = 0; @@ -125,12 +131,39 @@ GIT_INLINE(int) git_win32__file_attribute_to_stat( st->st_uid = 0; st->st_nlink = 1; st->st_mode = mode; - st->st_size = ((git_off_t)attrdata->nFileSizeHigh << 32) + attrdata->nFileSizeLow; + st->st_size = ((git_off_t)nFileSizeHigh << 32) + nFileSizeLow; st->st_dev = _getdrive() - 1; st->st_rdev = st->st_dev; - git_win32__filetime_to_timespec(&(attrdata->ftLastAccessTime), &(st->st_atim)); - git_win32__filetime_to_timespec(&(attrdata->ftLastWriteTime), &(st->st_mtim)); - git_win32__filetime_to_timespec(&(attrdata->ftCreationTime), &(st->st_ctim)); + git_win32__filetime_to_timespec(&ftLastAccessTime, &(st->st_atim)); + git_win32__filetime_to_timespec(&ftLastWriteTime, &(st->st_mtim)); + git_win32__filetime_to_timespec(&ftCreationTime, &(st->st_ctim)); +} + +GIT_INLINE(void) git_win32__file_information_to_stat( + struct stat *st, + const BY_HANDLE_FILE_INFORMATION *fileinfo) +{ + git_win32__stat_init(st, + fileinfo->dwFileAttributes, + fileinfo->nFileSizeHigh, + fileinfo->nFileSizeLow, + fileinfo->ftCreationTime, + fileinfo->ftLastAccessTime, + fileinfo->ftLastWriteTime); +} + +GIT_INLINE(int) git_win32__file_attribute_to_stat( + struct stat *st, + const WIN32_FILE_ATTRIBUTE_DATA *attrdata, + const wchar_t *path) +{ + git_win32__stat_init(st, + attrdata->dwFileAttributes, + attrdata->nFileSizeHigh, + attrdata->nFileSizeLow, + attrdata->ftCreationTime, + attrdata->ftLastAccessTime, + attrdata->ftLastWriteTime); if (attrdata->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && path) { git_win32_path target; @@ -139,7 +172,7 @@ GIT_INLINE(int) git_win32__file_attribute_to_stat( st->st_mode = (st->st_mode & ~S_IFMT) | S_IFLNK; /* st_size gets the UTF-8 length of the target name, in bytes, - * not counting the NULL terminator */ + * not counting the NULL terminator */ if ((st->st_size = git__utf16_to_8(NULL, 0, target)) < 0) { giterr_set(GITERR_OS, "Could not convert reparse point name for '%s'", path); return -1; From 0d9a39eaf5e7f85d1e11445cd4f9cea2291cb187 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 12 Feb 2016 10:02:18 -0800 Subject: [PATCH 372/450] win32: drop incorrect `const`ness --- tests/win32/longpath.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/win32/longpath.c b/tests/win32/longpath.c index 07eecd394..5a36875ed 100644 --- a/tests/win32/longpath.c +++ b/tests/win32/longpath.c @@ -36,7 +36,7 @@ void assert_name_too_long(void) { const git_error *err; size_t expected_len, actual_len; - const char *expected_msg; + char *expected_msg; err = giterr_last(); actual_len = strlen(err->message); From 9a634cba85747b8b86b5c91c1420aa6688d61180 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 12 Feb 2016 10:03:29 -0800 Subject: [PATCH 373/450] index: explicitly cast new hash size to an int --- src/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.c b/src/index.c index 081a1ea59..aced53225 100644 --- a/src/index.c +++ b/src/index.c @@ -1515,7 +1515,7 @@ int git_index__fill(git_index *index, const git_vector *source_entries) return 0; git_vector_size_hint(&index->entries, source_entries->length); - git_idxmap_resize(index->entries_map, source_entries->length * 1.3); + git_idxmap_resize(index->entries_map, (khint_t)(source_entries->length * 1.3)); git_vector_foreach(source_entries, i, source_entry) { git_index_entry *entry = NULL; From 997e0301541e5f3babdd18105f4155d6108d1490 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 12 Feb 2016 10:11:32 -0800 Subject: [PATCH 374/450] index: don't use `seek` return as an error code --- src/index.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.c b/src/index.c index aced53225..98e20e054 100644 --- a/src/index.c +++ b/src/index.c @@ -603,14 +603,14 @@ const git_oid *git_index_checksum(git_index *index) */ static int compare_checksum(git_index *index) { - int fd, error; + int fd; ssize_t bytes_read; git_oid checksum = {{ 0 }}; if ((fd = p_open(index->index_file_path, O_RDONLY)) < 0) return fd; - if ((error = p_lseek(fd, -20, SEEK_END)) < 0) { + if (p_lseek(fd, -20, SEEK_END) < 0) { p_close(fd); giterr_set(GITERR_OS, "failed to seek to end of file"); return -1; From b2ca8d9c29fb6e2a6b8ce695f2943a94be6ef7a4 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 12 Feb 2016 10:22:54 -0800 Subject: [PATCH 375/450] index: explicitly cast the teeny index entry members --- src/index.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.c b/src/index.c index 98e20e054..bfa9fc849 100644 --- a/src/index.c +++ b/src/index.c @@ -826,8 +826,8 @@ const git_index_entry *git_index_get_bypath( void git_index_entry__init_from_stat( git_index_entry *entry, struct stat *st, bool trust_mode) { - entry->ctime.seconds = (git_time_t)st->st_ctime; - entry->mtime.seconds = (git_time_t)st->st_mtime; + entry->ctime.seconds = (int32_t)st->st_ctime; + entry->mtime.seconds = (int32_t)st->st_mtime; #if defined(GIT_USE_NSEC) entry->mtime.nanoseconds = st->st_mtim.tv_nsec; entry->ctime.nanoseconds = st->st_ctim.tv_nsec; @@ -838,7 +838,7 @@ void git_index_entry__init_from_stat( git_index__create_mode(0666) : git_index__create_mode(st->st_mode); entry->uid = st->st_uid; entry->gid = st->st_gid; - entry->file_size = st->st_size; + entry->file_size = (uint32_t)st->st_size; } static void index_entry_adjust_namemask( From 3b2fa0fb36da71e7e00f5b97cf053920126f4cb1 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 12 Feb 2016 10:25:50 -0800 Subject: [PATCH 376/450] submodule: explicitly cast to the teensy time value --- src/submodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/submodule.c b/src/submodule.c index 6ec4c53e1..cdae3dddf 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -778,9 +778,9 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index) if ((error = git_commit_lookup(&head, sm_repo, &sm->wd_oid)) < 0) goto cleanup; - entry.ctime.seconds = git_commit_time(head); + entry.ctime.seconds = (int32_t)git_commit_time(head); entry.ctime.nanoseconds = 0; - entry.mtime.seconds = git_commit_time(head); + entry.mtime.seconds = (int32_t)git_commit_time(head); entry.mtime.nanoseconds = 0; git_commit_free(head); From 9ce0399c4dad5ff040f2219fa1dd7a4531766414 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 12 Feb 2016 10:27:05 -0800 Subject: [PATCH 377/450] winhttp: use an unsigned iterator --- src/transports/winhttp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index 3750f6e32..ded041686 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -283,7 +283,7 @@ static int winhttp_stream_connect(winhttp_stream *s) unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS; int default_timeout = TIMEOUT_INFINITE; int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT; - int i; + size_t i; /* Prepare URL */ git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_url); From eadd0f05f6faf14f94876839f00981da5d642667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 16 Feb 2016 14:06:48 +0100 Subject: [PATCH 378/450] commit: expose the different kinds of errors We should be checking whether the object we're looking up is a commit, and we should let the caller know whether the not-found return code comes from a bad object type or just a missing signature. --- include/git2/commit.h | 6 ++++++ src/commit.c | 8 +++++++- tests/commit/parse.c | 11 +++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/include/git2/commit.h b/include/git2/commit.h index a92277417..3488c7440 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -266,12 +266,18 @@ GIT_EXTERN(int) git_commit_header_field(git_buf *out, const git_commit *commit, /** * Extract the signature from a commit * + * If the id is not for a commit, the error class will be + * `GITERR_INVALID`. If the commit does not have a signature, the + * error class will be `GITERR_OBJECT`. + * * @param signature the signature block * @param signed_data signed data; this is the commit contents minus the signature block * @param repo the repository in which the commit exists * @param commit_id the commit from which to extract the data * @param field the name of the header field containing the signature * block; pass `NULL` to extract the default 'gpgsig' + * @return 0 on success, GIT_ENOTFOUND if the id is not for a commit + * or the commit does not have a signature. */ GIT_EXTERN(int) git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field); diff --git a/src/commit.c b/src/commit.c index 8faef07df..5a0509803 100644 --- a/src/commit.c +++ b/src/commit.c @@ -642,6 +642,12 @@ int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_r if ((error = git_odb_read(&obj, odb, commit_id)) < 0) return error; + if (obj->cached.type != GIT_OBJ_COMMIT) { + giterr_set(GITERR_INVALID, "the requested type does not match the type in ODB"); + error = GIT_ENOTFOUND; + goto cleanup; + } + buf = git_odb_object_data(obj); while ((h = strchr(buf, '\n')) && h[1] != '\0' && h[1] != '\n') { @@ -688,7 +694,7 @@ int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_r return git_buf_puts(signed_data, eol+1); } - giterr_set(GITERR_INVALID, "this commit is not signed"); + giterr_set(GITERR_OBJECT, "this commit is not signed"); error = GIT_ENOTFOUND; goto cleanup; diff --git a/tests/commit/parse.c b/tests/commit/parse.c index 3e1670ec4..838cfb467 100644 --- a/tests/commit/parse.c +++ b/tests/commit/parse.c @@ -513,6 +513,17 @@ a simple commit which works\n"; cl_assert_equal_s(gpgsig, signature.ptr); cl_assert_equal_s(data, signed_data.ptr); + /* Try to parse a tree */ + cl_git_pass(git_oid_fromstr(&commit_id, "45dd856fdd4d89b884c340ba0e047752d9b085d6")); + cl_git_fail_with(GIT_ENOTFOUND, git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL)); + cl_assert_equal_i(GITERR_INVALID, giterr_last()->klass); + + /* Try to parse an unsigned commit */ + cl_git_pass(git_odb_write(&commit_id, odb, passing_commit_cases[1], strlen(passing_commit_cases[1]), GIT_OBJ_COMMIT)); + cl_git_fail_with(GIT_ENOTFOUND, git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL)); + cl_assert_equal_i(GITERR_OBJECT, giterr_last()->klass); + git_buf_free(&signature); git_buf_free(&signed_data); + } From 4be2aa57c9b512d45b4071b71d0b93f3bf7e18d6 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 16 Feb 2016 18:50:08 +0000 Subject: [PATCH 379/450] win32: tests around handling forbidden paths Introduce a repository that contains some paths that were illegal on PC-DOS circa 1981 (like `aux`, `con`, `com1`) and that in a bizarre fit of retrocomputing, remain illegal on some "modern" computers, despite being "new technology". Introduce some aspirational tests that suggest that we should be able to cope with trees and indexes that contain paths that would be illegal on the filesystem, so that we can at least diff them. Further ensure that checkout will not write a repository with forbidden paths. --- tests/resources/win32-forbidden/.gitted/HEAD | Bin 0 -> 23 bytes .../resources/win32-forbidden/.gitted/config | Bin 0 -> 137 bytes tests/resources/win32-forbidden/.gitted/index | Bin 0 -> 577 bytes .../win32-forbidden/.gitted/info/exclude | Bin 0 -> 240 bytes .../10/68072702a28a82c78902cf5bf82c3864cf4356 | Bin 0 -> 143 bytes .../17/6a458f94e0ea5272ce67c36bf30b6be9caf623 | Bin 0 -> 28 bytes .../2d/7445a749d25269f32724aa621cb70b196bcc40 | Bin 0 -> 105 bytes .../34/96991d72d500af36edef68bbfcccd1661d88db | Bin 0 -> 143 bytes .../8f/45aad6f23b9509f8786c617e19c127ae76609a | Bin 0 -> 73 bytes .../da/623abd956bb2fd8052c708c7ed43f05d192d37 | Bin 0 -> 59 bytes .../ea/c7621a652e5261ef1c1d3e7ae31b0d84fcbaba | Bin 0 -> 137 bytes .../win32-forbidden/.gitted/refs/heads/master | Bin 0 -> 41 bytes tests/win32/forbidden.c | 183 ++++++++++++++++++ 13 files changed, 183 insertions(+) create mode 100644 tests/resources/win32-forbidden/.gitted/HEAD create mode 100644 tests/resources/win32-forbidden/.gitted/config create mode 100644 tests/resources/win32-forbidden/.gitted/index create mode 100644 tests/resources/win32-forbidden/.gitted/info/exclude create mode 100644 tests/resources/win32-forbidden/.gitted/objects/10/68072702a28a82c78902cf5bf82c3864cf4356 create mode 100644 tests/resources/win32-forbidden/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623 create mode 100644 tests/resources/win32-forbidden/.gitted/objects/2d/7445a749d25269f32724aa621cb70b196bcc40 create mode 100644 tests/resources/win32-forbidden/.gitted/objects/34/96991d72d500af36edef68bbfcccd1661d88db create mode 100644 tests/resources/win32-forbidden/.gitted/objects/8f/45aad6f23b9509f8786c617e19c127ae76609a create mode 100644 tests/resources/win32-forbidden/.gitted/objects/da/623abd956bb2fd8052c708c7ed43f05d192d37 create mode 100644 tests/resources/win32-forbidden/.gitted/objects/ea/c7621a652e5261ef1c1d3e7ae31b0d84fcbaba create mode 100644 tests/resources/win32-forbidden/.gitted/refs/heads/master create mode 100644 tests/win32/forbidden.c diff --git a/tests/resources/win32-forbidden/.gitted/HEAD b/tests/resources/win32-forbidden/.gitted/HEAD new file mode 100644 index 0000000000000000000000000000000000000000..cb089cd89a7d7686d284d8761201649346b5aa1c GIT binary patch literal 23 ecmXR)O|w!cN=+-)&qz&7Db~+TEG|hc;sO9;xClW2 literal 0 HcmV?d00001 diff --git a/tests/resources/win32-forbidden/.gitted/config b/tests/resources/win32-forbidden/.gitted/config new file mode 100644 index 0000000000000000000000000000000000000000..6c9406b7d9320db083eca69b3f8bee9a6c7b50d4 GIT binary patch literal 137 zcmYk#%?-jZ3KJaaM(b)IThRcka) zn6vinzTI-FENHqTG;JZL7ug4u#6zM7i5Th{J5BgE#z&9!LjG%xu(tTZ>RkRd-|~Df A#{d8T literal 0 HcmV?d00001 diff --git a/tests/resources/win32-forbidden/.gitted/index b/tests/resources/win32-forbidden/.gitted/index new file mode 100644 index 0000000000000000000000000000000000000000..1202dd9f4ae5b5246426b4b7bdc787a12af44248 GIT binary patch literal 577 zcmZ?q402{*U|<4b_OQdze}OaujAmqDU}3rwx0->WaR~zh<5!@R2oUp#XSw!IdGIQz z=v?~Y?9bfUFHe0_X5iCH&n!tSDJjZKDlJJZhL|Jy2bqSLXZjY+JiS{5F=8+@FfcPQQP4}zEJ-XWDauSLElDkA5YKY$pYq^UP|>;c z!`Yv?vtOS2rVLf-?C-~LE6Hl_)a*@v8-k8=9DnQlAy!h?94hToP-2KrJ~1&-ucV@c xq2G1YwNKVlIe%2-B-TkDR9{z?Fbir#Vrd0F6VftsQVAJRRaJ#k2>{B;WqZ3*KK=jz literal 0 HcmV?d00001 diff --git a/tests/resources/win32-forbidden/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623 b/tests/resources/win32-forbidden/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623 new file mode 100644 index 0000000000000000000000000000000000000000..ef83166706c43aabd3c68d155be9c92a23960f0f GIT binary patch literal 28 kcmb5Fkmn=FfcPQQE>M6W4M)MwRdXvroRnA$2pF_b^Z`5scR0E z_9-Ya#3!Gan5b7$QNqygy6V~|>#3YSDsmF*BoC^uD@&LKH6pRJ0-p(KnK`M1jHs%r L!l?uR{HIf1PD3w2 literal 0 HcmV?d00001 diff --git a/tests/resources/win32-forbidden/.gitted/objects/34/96991d72d500af36edef68bbfcccd1661d88db b/tests/resources/win32-forbidden/.gitted/objects/34/96991d72d500af36edef68bbfcccd1661d88db new file mode 100644 index 0000000000000000000000000000000000000000..71b6172c6f2251f9856b8aca747f384abefd1bff GIT binary patch literal 143 zcmV;A0C4|!0hNu-3IZV%gDmYHPeg?fjXLZg8AIH_4 zQ-=x7p*Qbgq6(>Inlj7KB6AuIWiH4ijKNU}7X+7z*uBTv;JUtTTj3sS-fDr_5BhDF xN9u83POj!NOhQVm?@mGXdPDv}Hc fsJ9SRioz?lFMi#hOzsp>a%59Po8VGq(58w PWV-n4LPmyA=vW?n|4-5{P)jpHIHmx(BnI=xmETB9~wSLRJ}k3 literal 0 HcmV?d00001 diff --git a/tests/resources/win32-forbidden/.gitted/refs/heads/master b/tests/resources/win32-forbidden/.gitted/refs/heads/master new file mode 100644 index 0000000000000000000000000000000000000000..47fce0fe39569818eddb5d138ebca052246eafa2 GIT binary patch literal 41 ucmV~$K>+|D2m`>sX+(5F9AVHuf_J(d?!vox=6tOvR>LAXGbw=3j4?jzS__*1 literal 0 HcmV?d00001 diff --git a/tests/win32/forbidden.c b/tests/win32/forbidden.c new file mode 100644 index 000000000..e02f41179 --- /dev/null +++ b/tests/win32/forbidden.c @@ -0,0 +1,183 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "buffer.h" +#include "submodule.h" + +static const char *repo_name = "win32-forbidden"; +static git_repository *repo; + +void test_win32_forbidden__initialize(void) +{ + repo = cl_git_sandbox_init(repo_name); +} + +void test_win32_forbidden__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_win32_forbidden__can_open_index(void) +{ + git_index *index; + cl_git_pass(git_repository_index(&index, repo)); + cl_assert_equal_i(7, git_index_entrycount(index)); + + /* ensure we can even write the unmodified index */ + cl_git_pass(git_index_write(index)); + + git_index_free(index); +} + +void test_win32_forbidden__can_add_forbidden_filename_with_entry(void) +{ + git_index *index; + git_index_entry entry = {{0}}; + + cl_git_pass(git_repository_index(&index, repo)); + + entry.path = "aux"; + entry.mode = GIT_FILEMODE_BLOB; + git_oid_fromstr(&entry.id, "da623abd956bb2fd8052c708c7ed43f05d192d37"); + + cl_git_pass(git_index_add(index, &entry)); + + git_index_free(index); +} + +void test_win32_forbidden__cannot_add_dot_git_even_with_entry(void) +{ + git_index *index; + git_index_entry entry = {{0}}; + + cl_git_pass(git_repository_index(&index, repo)); + + entry.path = "foo/.git"; + entry.mode = GIT_FILEMODE_BLOB; + git_oid_fromstr(&entry.id, "da623abd956bb2fd8052c708c7ed43f05d192d37"); + + cl_git_fail(git_index_add(index, &entry)); + + git_index_free(index); +} + +void test_win32_forbidden__cannot_add_forbidden_filename_from_filesystem(void) +{ + git_index *index; + + /* since our function calls are very low-level, we can create `aux.`, + * but we should not be able to add it to the index + */ + cl_git_pass(git_repository_index(&index, repo)); + cl_git_write2file("win32-forbidden/aux.", "foo\n", 4, O_RDWR | O_CREAT, 0666); + +#ifdef GIT_WIN32 + cl_git_fail(git_index_add_bypath(index, "aux.")); +#else + cl_git_pass(git_index_add_bypath(index, "aux.")); +#endif + + cl_must_pass(p_unlink("win32-forbidden/aux.")); + git_index_free(index); +} + +static int dummy_submodule_cb( + git_submodule *sm, const char *name, void *payload) +{ + GIT_UNUSED(sm); + GIT_UNUSED(name); + GIT_UNUSED(payload); + return 0; +} + +void test_win32_forbidden__can_diff_tree_to_index(void) +{ + git_diff *diff; + git_tree *tree; + + cl_git_pass(git_repository_head_tree(&tree, repo)); + cl_git_pass(git_diff_tree_to_index(&diff, repo, tree, NULL, NULL)); + cl_assert_equal_i(0, git_diff_num_deltas(diff)); + git_diff_free(diff); + git_tree_free(tree); +} + +void test_win32_forbidden__can_diff_tree_to_tree(void) +{ + git_diff *diff; + git_tree *tree; + + cl_git_pass(git_repository_head_tree(&tree, repo)); + cl_git_pass(git_diff_tree_to_tree(&diff, repo, tree, tree, NULL)); + cl_assert_equal_i(0, git_diff_num_deltas(diff)); + git_diff_free(diff); + git_tree_free(tree); +} + +void test_win32_forbidden__can_diff_index_to_workdir(void) +{ + git_index *index; + git_diff *diff; + const git_diff_delta *delta; + git_tree *tree; + size_t i; + + cl_git_pass(git_repository_index(&index, repo)); + cl_git_pass(git_repository_head_tree(&tree, repo)); + cl_git_pass(git_diff_index_to_workdir(&diff, repo, index, NULL)); + + for (i = 0; i < git_diff_num_deltas(diff); i++) { + delta = git_diff_get_delta(diff, i); + cl_assert_equal_i(GIT_DELTA_DELETED, delta->status); + } + + git_diff_free(diff); + git_tree_free(tree); + git_index_free(index); +} + +void test_win32_forbidden__checking_out_forbidden_index_fails(void) +{ +#ifdef GIT_WIN32 + git_index *index; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_diff *diff; + const git_diff_delta *delta; + git_tree *tree; + size_t num_deltas, i; + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_git_pass(git_repository_index(&index, repo)); + cl_git_fail(git_checkout_index(repo, index, &opts)); + + cl_git_pass(git_repository_head_tree(&tree, repo)); + cl_git_pass(git_diff_index_to_workdir(&diff, repo, index, NULL)); + + num_deltas = git_diff_num_deltas(diff); + + cl_assert(num_deltas > 0); + + for (i = 0; i < num_deltas; i++) { + delta = git_diff_get_delta(diff, i); + cl_assert_equal_i(GIT_DELTA_DELETED, delta->status); + } + + git_diff_free(diff); + git_tree_free(tree); + git_index_free(index); +#endif +} + +void test_win32_forbidden__can_query_submodules(void) +{ + cl_git_pass(git_submodule_foreach(repo, dummy_submodule_cb, NULL)); +} + +void test_win32_forbidden__can_blame_file(void) +{ + git_blame *blame; + + cl_git_pass(git_blame_file(&blame, repo, "aux", NULL)); + git_blame_free(blame); +} From a218b2f625874d9d9523f05a86845506c4735e66 Mon Sep 17 00:00:00 2001 From: Colin Xu Date: Fri, 22 Jan 2016 16:03:37 +0800 Subject: [PATCH 380/450] Validate pointer before access the member. When Git repository at network locations, sometimes git_iterator_for_tree fails at iterator__update_ignore_case so it goes to git_iterator_free. Null pointer will crash the process if not check. Signed-off-by: Colin Xu --- src/iterator.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/iterator.c b/src/iterator.c index ee348de6e..2d9ebf41d 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -558,7 +558,7 @@ static bool tree_iterator__pop_frame(tree_iterator *ti, bool final) { tree_iterator_frame *tf = ti->head; - if (!tf->up) + if (!tf || !tf->up) return false; ti->head = tf->up; @@ -581,7 +581,8 @@ static void tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final) while (tree_iterator__pop_frame(ti, final)) /* pop to root */; if (!final) { - ti->head->current = to_end ? ti->head->n_entries : 0; + if(ti->head) + ti->head->current = to_end ? ti->head->n_entries : 0; ti->path_ambiguities = 0; git_buf_clear(&ti->path); } @@ -775,7 +776,8 @@ static void tree_iterator__free(git_iterator *self) tree_iterator__pop_all(ti, true, false); - git_tree_free(ti->head->entries[0]->tree); + if(ti->head) + git_tree_free(ti->head->entries[0]->tree); git__free(ti->head); git_pool_clear(&ti->pool); git_buf_free(&ti->path); From 4fea9cffbda89562bfdcdd58b0162d31ca4feb3e Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 16 Feb 2016 13:08:55 +0000 Subject: [PATCH 381/450] iterator: assert tree_iterator has a frame Although a `tree_iterator` that failed to be properly created does not have a frame, all other `tree_iterator`s should. Do not call `pop` in the failure case, but assert that in all other cases there is a frame. --- src/iterator.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/iterator.c b/src/iterator.c index 2d9ebf41d..973f77b03 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -558,7 +558,9 @@ static bool tree_iterator__pop_frame(tree_iterator *ti, bool final) { tree_iterator_frame *tf = ti->head; - if (!tf || !tf->up) + assert(tf); + + if (!tf->up) return false; ti->head = tf->up; @@ -581,8 +583,9 @@ static void tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final) while (tree_iterator__pop_frame(ti, final)) /* pop to root */; if (!final) { - if(ti->head) - ti->head->current = to_end ? ti->head->n_entries : 0; + assert(ti->head); + + ti->head->current = to_end ? ti->head->n_entries : 0; ti->path_ambiguities = 0; git_buf_clear(&ti->path); } @@ -774,11 +777,12 @@ static void tree_iterator__free(git_iterator *self) { tree_iterator *ti = (tree_iterator *)self; - tree_iterator__pop_all(ti, true, false); - - if(ti->head) + if (ti->head) { + tree_iterator__pop_all(ti, true, false); git_tree_free(ti->head->entries[0]->tree); - git__free(ti->head); + git__free(ti->head); + } + git_pool_clear(&ti->pool); git_buf_free(&ti->path); } From 318b825e76a9dc8afefc8274c5271747ad64d5a9 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 16 Feb 2016 17:11:46 +0000 Subject: [PATCH 382/450] index: allow read of index w/ illegal entries Allow `git_index_read` to handle reading existing indexes with illegal entries. Allow the low-level `git_index_add` to add properly formed `git_index_entry`s even if they contain paths that would be illegal for the current filesystem (eg, `AUX`). Continue to disallow `git_index_add_bypath` from adding entries that are illegal universally illegal (eg, `.git`, `foo/../bar`). --- src/checkout.c | 2 +- src/index.c | 32 +++++++++++++++++++++++--------- src/path.c | 7 ++++++- src/path.h | 18 ++++++++++++++---- src/refdb_fs.c | 4 ++-- tests/path/core.c | 12 ++++++------ 6 files changed, 52 insertions(+), 23 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index a92ad0825..07b6bd633 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1226,7 +1226,7 @@ static int checkout_verify_paths( int action, git_diff_delta *delta) { - unsigned int flags = GIT_PATH_REJECT_DEFAULTS | GIT_PATH_REJECT_DOT_GIT; + unsigned int flags = GIT_PATH_REJECT_WORKDIR_DEFAULTS; if (action & CHECKOUT_ACTION__REMOVE) { if (!git_path_isvalid(repo, delta->old_file.path, flags)) { diff --git a/src/index.c b/src/index.c index 5c7bd90dd..5728008aa 100644 --- a/src/index.c +++ b/src/index.c @@ -891,17 +891,31 @@ static void index_entry_adjust_namemask( entry->flags |= GIT_IDXENTRY_NAMEMASK; } +/* When `from_workdir` is true, we will validate the paths to avoid placing + * paths that are invalid for the working directory on the current filesystem + * (eg, on Windows, we will disallow `GIT~1`, `AUX`, `COM1`, etc). This + * function will *always* prevent `.git` and directory traversal `../` from + * being added to the index. + */ static int index_entry_create( git_index_entry **out, git_repository *repo, - const char *path) + const char *path, + bool from_workdir) { size_t pathlen = strlen(path), alloclen; struct entry_internal *entry; + unsigned int path_valid_flags = GIT_PATH_REJECT_INDEX_DEFAULTS; - if (!git_path_isvalid(repo, path, - GIT_PATH_REJECT_DEFAULTS | GIT_PATH_REJECT_DOT_GIT)) { - giterr_set(GITERR_INDEX, "Invalid path: '%s'", path); + /* always reject placing `.git` in the index and directory traversal. + * when requested, disallow platform-specific filenames and upgrade to + * the platform-specific `.git` tests (eg, `git~1`, etc). + */ + if (from_workdir) + path_valid_flags |= GIT_PATH_REJECT_WORKDIR_DEFAULTS; + + if (!git_path_isvalid(repo, path, path_valid_flags)) { + giterr_set(GITERR_INDEX, "invalid path: '%s'", path); return -1; } @@ -933,7 +947,7 @@ static int index_entry_init( "Could not initialize index entry. " "Index is not backed up by an existing repository."); - if (index_entry_create(&entry, INDEX_OWNER(index), rel_path) < 0) + if (index_entry_create(&entry, INDEX_OWNER(index), rel_path, true) < 0) return -1; /* write the blob to disk and get the oid and stat info */ @@ -1013,7 +1027,7 @@ static int index_entry_dup( git_index *index, const git_index_entry *src) { - if (index_entry_create(out, INDEX_OWNER(index), src->path) < 0) + if (index_entry_create(out, INDEX_OWNER(index), src->path, false) < 0) return -1; index_entry_cpy(*out, src); @@ -1035,7 +1049,7 @@ static int index_entry_dup_nocache( git_index *index, const git_index_entry *src) { - if (index_entry_create(out, INDEX_OWNER(index), src->path) < 0) + if (index_entry_create(out, INDEX_OWNER(index), src->path, false) < 0) return -1; index_entry_cpy_nocache(*out, src); @@ -1447,7 +1461,7 @@ static int add_repo_as_submodule(git_index_entry **out, git_index *index, const struct stat st; int error; - if (index_entry_create(&entry, INDEX_OWNER(index), path) < 0) + if (index_entry_create(&entry, INDEX_OWNER(index), path, true) < 0) return -1; if ((error = git_buf_joinpath(&abspath, git_repository_workdir(repo), path)) < 0) @@ -2884,7 +2898,7 @@ static int read_tree_cb( if (git_buf_joinpath(&path, root, tentry->filename) < 0) return -1; - if (index_entry_create(&entry, INDEX_OWNER(data->index), path.ptr) < 0) + if (index_entry_create(&entry, INDEX_OWNER(data->index), path.ptr, false) < 0) return -1; entry->mode = tentry->attr; diff --git a/src/path.c b/src/path.c index 18b4f03fd..852ef576a 100644 --- a/src/path.c +++ b/src/path.c @@ -1630,9 +1630,12 @@ static bool verify_component( !verify_dotgit_ntfs(repo, component, len)) return false; + /* don't bother rerunning the `.git` test if we ran the HFS or NTFS + * specific tests, they would have already rejected `.git`. + */ if ((flags & GIT_PATH_REJECT_DOT_GIT_HFS) == 0 && (flags & GIT_PATH_REJECT_DOT_GIT_NTFS) == 0 && - (flags & GIT_PATH_REJECT_DOT_GIT) && + (flags & GIT_PATH_REJECT_DOT_GIT_LITERAL) && len == 4 && component[0] == '.' && (component[1] == 'g' || component[1] == 'G') && @@ -1649,6 +1652,8 @@ GIT_INLINE(unsigned int) dotgit_flags( { int protectHFS = 0, protectNTFS = 0; + flags |= GIT_PATH_REJECT_DOT_GIT_LITERAL; + #ifdef __APPLE__ protectHFS = 1; #endif diff --git a/src/path.h b/src/path.h index 7e156fce8..875c8cb7e 100644 --- a/src/path.h +++ b/src/path.h @@ -564,15 +564,16 @@ extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or #define GIT_PATH_REJECT_TRAILING_COLON (1 << 6) #define GIT_PATH_REJECT_DOS_PATHS (1 << 7) #define GIT_PATH_REJECT_NT_CHARS (1 << 8) -#define GIT_PATH_REJECT_DOT_GIT_HFS (1 << 9) -#define GIT_PATH_REJECT_DOT_GIT_NTFS (1 << 10) +#define GIT_PATH_REJECT_DOT_GIT_LITERAL (1 << 9) +#define GIT_PATH_REJECT_DOT_GIT_HFS (1 << 10) +#define GIT_PATH_REJECT_DOT_GIT_NTFS (1 << 11) /* Default path safety for writing files to disk: since we use the * Win32 "File Namespace" APIs ("\\?\") we need to protect from * paths that the normal Win32 APIs would not write. */ #ifdef GIT_WIN32 -# define GIT_PATH_REJECT_DEFAULTS \ +# define GIT_PATH_REJECT_FILESYSTEM_DEFAULTS \ GIT_PATH_REJECT_TRAVERSAL | \ GIT_PATH_REJECT_BACKSLASH | \ GIT_PATH_REJECT_TRAILING_DOT | \ @@ -581,9 +582,18 @@ extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or GIT_PATH_REJECT_DOS_PATHS | \ GIT_PATH_REJECT_NT_CHARS #else -# define GIT_PATH_REJECT_DEFAULTS GIT_PATH_REJECT_TRAVERSAL +# define GIT_PATH_REJECT_FILESYSTEM_DEFAULTS \ + GIT_PATH_REJECT_TRAVERSAL #endif + /* Paths that should never be written into the working directory. */ +#define GIT_PATH_REJECT_WORKDIR_DEFAULTS \ + GIT_PATH_REJECT_FILESYSTEM_DEFAULTS | GIT_PATH_REJECT_DOT_GIT + +/* Paths that should never be written to the index. */ +#define GIT_PATH_REJECT_INDEX_DEFAULTS \ + GIT_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT + /* * Determine whether a path is a valid git path or not - this must not contain * a '.' or '..' component, or a component that is ".git" (in any case). diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 85b5034d6..1348c67a1 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -717,7 +717,7 @@ static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char * assert(file && backend && name); - if (!git_path_isvalid(backend->repo, name, GIT_PATH_REJECT_DEFAULTS)) { + if (!git_path_isvalid(backend->repo, name, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) { giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", name); return GIT_EINVALIDSPEC; } @@ -1672,7 +1672,7 @@ static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char repo = backend->repo; - if (!git_path_isvalid(backend->repo, refname, GIT_PATH_REJECT_DEFAULTS)) { + if (!git_path_isvalid(backend->repo, refname, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) { giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", refname); return GIT_EINVALIDSPEC; } diff --git a/tests/path/core.c b/tests/path/core.c index 064f1492a..3dccfe5fb 100644 --- a/tests/path/core.c +++ b/tests/path/core.c @@ -105,12 +105,12 @@ void test_path_core__isvalid_dot_git(void) cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.GIT/bar", 0)); cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar/.Git", 0)); - cl_assert_equal_b(false, git_path_isvalid(NULL, ".git", GIT_PATH_REJECT_DOT_GIT)); - cl_assert_equal_b(false, git_path_isvalid(NULL, ".git/foo", GIT_PATH_REJECT_DOT_GIT)); - cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git", GIT_PATH_REJECT_DOT_GIT)); - cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git/bar", GIT_PATH_REJECT_DOT_GIT)); - cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.GIT/bar", GIT_PATH_REJECT_DOT_GIT)); - cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar/.Git", GIT_PATH_REJECT_DOT_GIT)); + cl_assert_equal_b(false, git_path_isvalid(NULL, ".git", GIT_PATH_REJECT_DOT_GIT_LITERAL)); + cl_assert_equal_b(false, git_path_isvalid(NULL, ".git/foo", GIT_PATH_REJECT_DOT_GIT_LITERAL)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git", GIT_PATH_REJECT_DOT_GIT_LITERAL)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git/bar", GIT_PATH_REJECT_DOT_GIT_LITERAL)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.GIT/bar", GIT_PATH_REJECT_DOT_GIT_LITERAL)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar/.Git", GIT_PATH_REJECT_DOT_GIT_LITERAL)); cl_assert_equal_b(true, git_path_isvalid(NULL, "!git", 0)); cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/!git", 0)); From d50bf716946ab7c9c1be7b804c6e87f43835b9a3 Mon Sep 17 00:00:00 2001 From: Prayag Verma Date: Thu, 18 Feb 2016 13:26:08 +0530 Subject: [PATCH 383/450] Fix a typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `compatability` → `compatibility` --- PROJECTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PROJECTS.md b/PROJECTS.md index 4f200b7f9..f3fffb705 100644 --- a/PROJECTS.md +++ b/PROJECTS.md @@ -48,7 +48,7 @@ These are good small projects to get started with libgit2. a new example that mirrors the behavior. Examples don't have to be perfect emulations, but should demonstrate how to use the libgit2 APIs to get results that are similar to Git commands. This lets you (and us) - easily exercise a particular facet of the API and measure compatability + easily exercise a particular facet of the API and measure compatibility and feature parity with core git. * Submit a PR to clarify documentation! While we do try to document all of the APIs, your fresh eyes on the documentation will find areas that are From 9c26f90c37926d7eef12f7d19f8c9d92493e3c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 18 Feb 2016 11:39:55 +0100 Subject: [PATCH 384/450] PROJECTS: remove a few things we do have --- PROJECTS.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/PROJECTS.md b/PROJECTS.md index 4f200b7f9..66fd5c329 100644 --- a/PROJECTS.md +++ b/PROJECTS.md @@ -75,8 +75,6 @@ might make good smaller projects by themselves. * Extract the Git tests that exercise that command * Convert the tests to call our emulation * These tests could go in examples/tests/... -* Fix symlink support for files in the .git directory (i.e. don't overwrite - the symlinks when writing the file contents back out) * Add hooks API to enumerate and manage hooks (not run them at this point) * Enumeration of available hooks * Lookup API to see which hooks have a script and get the script @@ -85,8 +83,6 @@ might make good smaller projects by themselves. executes the action in question * Isolate logic of ignore evaluation into a standalone API * Upgrade internal libxdiff code to latest from core Git -* Improve index internals with hashtable lookup for files instead of - using binary search every time * Tree builder improvements: * Extend to allow building a tree hierarchy * Apply-patch API From 5981ab1d7050143f97636ad0ce786f01cc8b6035 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 15 Feb 2016 09:41:08 +0100 Subject: [PATCH 385/450] coverity: add nodefs for abort macros Add nodefs for macros that abort the current flow due to errors. This includes macros that trigger on integer overflows and for the version check macro. This aids Coverity as we point out that these paths will cause a fatal error. --- script/user_nodefs.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/script/user_nodefs.h b/script/user_nodefs.h index 110f76851..f890511f4 100644 --- a/script/user_nodefs.h +++ b/script/user_nodefs.h @@ -6,3 +6,20 @@ */ #nodef GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { __coverity_panic__(); } + +#nodef GITERR_CHECK_ALLOC_ADD(out, one, two) \ + if (GIT_ADD_SIZET_OVERFLOW(out, one, two)) { __coverity_panic__(); } + +#nodef GITERR_CHECK_ALLOC_ADD3(out, one, two, three) \ + if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \ + GIT_ADD_SIZET_OVERFLOW(out, *(out), three)) { __coverity_panic__(); } + +#nodef GITERR_CHECK_ALLOC_ADD4(out, one, two, three, four) \ + if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \ + GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \ + GIT_ADD_SIZET_OVERFLOW(out, *(out), four)) { __coverity_panic__(); } + +#nodef GITERR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \ + if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { __coverity_panic__(); } + +#nodef GITERR_CHECK_VERSION(S,V,N) if (giterr__check_version(S,V,N) < 0) { __coverity_panic__(); } From 40f6f225175375b41317b0a4e98e8865600d1682 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 15 Feb 2016 10:58:52 +0100 Subject: [PATCH 386/450] coverity: hint that string length is at least 2 When checking if a string is prefixed by a drive letter (e.g. "C:") we verify this by inspecting the first and second character of the string. Coverity thinks this is a defect as we do not check the string's length first, but in fact we only check the second character if the first character is part of the alphabet, that is it cannot be '\0'. Fix this by overriding the macro and explicitly checking the string's length. --- script/user_nodefs.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/user_nodefs.h b/script/user_nodefs.h index f890511f4..3d25d92ec 100644 --- a/script/user_nodefs.h +++ b/script/user_nodefs.h @@ -23,3 +23,5 @@ if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { __coverity_panic__(); } #nodef GITERR_CHECK_VERSION(S,V,N) if (giterr__check_version(S,V,N) < 0) { __coverity_panic__(); } + +#nodef LOOKS_LIKE_DRIVE_PREFIX(S) (strlen(S) >= 2 && git__isalpha((S)[0]) && (S)[1] == ':') From 038d7af08595eabaa3d23da4703f25f4517af365 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 15 Feb 2016 11:30:48 +0100 Subject: [PATCH 387/450] signature: use GITERR_CHECK_ALLOC to check for OOM situation When checking for out of memory situations we usually use the GITERR_CHECK_ALLOC macro. Besides conforming to our current code base it adds the benefit of silencing errors in Coverity due to Coverity handling the macro's error path as abort. --- src/signature.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/signature.c b/src/signature.c index 109476efe..d07c93323 100644 --- a/src/signature.c +++ b/src/signature.c @@ -79,10 +79,9 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema GITERR_CHECK_ALLOC(p); p->name = extract_trimmed(name, strlen(name)); + GITERR_CHECK_ALLOC(p->name); p->email = extract_trimmed(email, strlen(email)); - - if (p->name == NULL || p->email == NULL) - return -1; /* oom */ + GITERR_CHECK_ALLOC(p->email); if (p->name[0] == '\0' || p->email[0] == '\0') { git_signature_free(p); From 704554cdf07c2cdbefbf586c1ea4e8af35c720db Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 15 Feb 2016 11:37:48 +0100 Subject: [PATCH 388/450] transports: smart: fix memory leak on OOM path --- src/transports/smart_protocol.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 1d46d4bc9..6363378ec 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -108,6 +108,7 @@ static int append_symref(const char **out, git_vector *symrefs, const char *ptr) if (giterr_last()->klass != GITERR_NOMEMORY) goto on_invalid; + git__free(mapping); return error; } @@ -120,6 +121,7 @@ static int append_symref(const char **out, git_vector *symrefs, const char *ptr) on_invalid: giterr_set(GITERR_NET, "remote sent invalid symref"); git_refspec__free(mapping); + git__free(mapping); return -1; } From b0f7512f407284c9eec91681a7ced5c93e026644 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 15 Feb 2016 11:46:10 +0100 Subject: [PATCH 389/450] transports: smart_pkt: fix memory leaks --- src/transports/smart_pkt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c index a6ae55d48..870f08497 100644 --- a/src/transports/smart_pkt.c +++ b/src/transports/smart_pkt.c @@ -271,6 +271,7 @@ static int ok_pkt(git_pkt **out, const char *line, size_t len) line += 3; /* skip "ok " */ if (!(ptr = strchr(line, '\n'))) { giterr_set(GITERR_NET, "Invalid packet line"); + git__free(pkt); return -1; } len = ptr - line; @@ -314,6 +315,8 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len) line = ptr + 1; if (!(ptr = strchr(line, '\n'))) { giterr_set(GITERR_NET, "Invalid packet line"); + git__free(pkt->ref); + git__free(pkt); return -1; } len = ptr - line; From 8a62bf11806d9118301999ff9c58d55057d06917 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 15 Feb 2016 11:28:33 +0100 Subject: [PATCH 390/450] netops: fix memory leak when an error occurs --- src/netops.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/netops.c b/src/netops.c index 5e8075597..c4241989f 100644 --- a/src/netops.c +++ b/src/netops.c @@ -261,6 +261,10 @@ int gitno_extract_url_parts( *path = git__substrdup(_path, u.field_data[UF_PATH].len); GITERR_CHECK_ALLOC(*path); } else { + git__free(*port); + *port = NULL; + git__free(*host); + *host = NULL; giterr_set(GITERR_NET, "invalid url, missing path"); return GIT_EINVALIDSPEC; } From bf127eec4af43f9c40e4120a301547fa8a9692d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 19 Feb 2016 13:24:41 +0100 Subject: [PATCH 391/450] global: remove an unused variable --- src/global.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/global.c b/src/global.c index 4a61a062c..c65b21a11 100644 --- a/src/global.c +++ b/src/global.c @@ -22,14 +22,6 @@ git_mutex git__mwindow_mutex; #define MAX_SHUTDOWN_CB 8 -#ifdef GIT_OPENSSL -# include -SSL_CTX *git__ssl_ctx; -# ifdef GIT_THREADS -static git_mutex *openssl_locks; -# endif -#endif - static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB]; static git_atomic git__n_shutdown_callbacks; static git_atomic git__n_inits; From deecaa2eceac3c9d8fff8063132786e62c6b3ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 19 Feb 2016 13:31:54 +0100 Subject: [PATCH 392/450] openssl: free the context even if we don't connect --- src/openssl_stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openssl_stream.c b/src/openssl_stream.c index 0705d90e7..58b70f5b4 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -267,7 +267,6 @@ static int ssl_teardown(SSL *ssl) else ret = 0; - SSL_free(ssl); return ret; } @@ -530,6 +529,7 @@ void openssl_free(git_stream *stream) { openssl_stream *st = (openssl_stream *) stream; + SSL_free(st->ssl); git__free(st->host); git__free(st->cert_info.data); git_stream_free(st->io); From f596946f09f3c1e51239a24ff41e27f2c1ffa2b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 19 Feb 2016 13:52:04 +0100 Subject: [PATCH 393/450] CHANGELOG: add a few missing changes --- CHANGELOG.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c84d27da3..c63f75ece 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,15 @@ v0.23 + 1 correctly formed, it will give bad results. This is the git approach and cuts a significant amount of time when reading the trees. +* Filter registration is now protected against concurrent + registration. + +* Filenames which are not valid on Windows in an index no longer cause + to fail to parse it on that OS. + +* Rebases can now be performed purely in-memory, without touching the + repository's workdir. + ### API additions * `git_config_lock()` has been added, which allow for @@ -35,11 +44,17 @@ v0.23 + 1 * `git_fetch_options` and `git_push_options` have gained a `custom_headers` field to set the extra HTTP header fields to send. - * `git_stream_register_tls()` lets you register a callback to be used as the constructor for a TLS stream instead of the libgit2 built-in one. +* `git_commit_header_field()` allows you to look up a specific header + field in a commit. + +* `git_commit_extract_signature()` extracts the signature from a + commit and gives you both the signature and the signed data so you + can verify it. + ### API removals ### Breaking API changes @@ -75,6 +90,12 @@ v0.23 + 1 `GIT_CONFIG_LEVEL_PROGRAMDATA` which represent a rough Windows equivalent to the system level configuration. +* `git_rebase_init()` not also takes a merge options. + +* The index no longer performs locking itself. This is not something + users of the library should have been relying on as it's not part of + the concurrency guarantees. + v0.23 ------ From f1260e03d976d6597cc0d6e37abe82eb9f093365 Mon Sep 17 00:00:00 2001 From: Ross Delinger Date: Fri, 19 Feb 2016 09:13:40 -0500 Subject: [PATCH 394/450] Remove unnecessary ifdef in pool.h --- src/pool.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pool.h b/src/pool.h index 0f9532db3..1cae48fd3 100644 --- a/src/pool.h +++ b/src/pool.h @@ -8,10 +8,7 @@ #define INCLUDE_pool_h__ #include "common.h" - -#ifdef GIT_DEBUG_POOL #include "vector.h" -#endif typedef struct git_pool_page git_pool_page; From c8fe6c0975431e92d3dc4569734f30923b64dd18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 19 Feb 2016 16:23:14 +0100 Subject: [PATCH 395/450] openssl: re-export the last-resort locking function We need to include the header where we define the function. Otherwise it won't be available on the DLL. --- src/openssl_stream.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/openssl_stream.c b/src/openssl_stream.c index 58b70f5b4..1dad5f637 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -15,6 +15,7 @@ #include "socket_stream.h" #include "netops.h" #include "git2/transport.h" +#include "git2/sys/openssl.h" #ifdef GIT_CURL # include "curl_stream.h" From 88ab3be6f52a6711d63266a296b6d569dc299019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 22 Feb 2016 15:41:01 +0100 Subject: [PATCH 396/450] Fix a few checkout -> rebase typos --- CHANGELOG.md | 4 ++-- include/git2/rebase.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c63f75ece..ec5a0d336 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -328,8 +328,8 @@ v0.23 * `git_rebase_options` now contains a `git_checkout_options` struct that will be used for functions that modify the working directory, - namely `git_checkout_init`, `git_checkout_next` and - `git_checkout_abort`. As a result, `git_rebase_open` now also takes + namely `git_rebase_init`, `git_rebase_next` and + `git_rebase_abort`. As a result, `git_rebase_open` now also takes a `git_rebase_options` and only the `git_rebase_init` and `git_rebase_open` functions take a `git_rebase_options`, where they will persist the options to subsequent `git_rebase` calls. diff --git a/include/git2/rebase.h b/include/git2/rebase.h index ece8b3664..9b9065ee4 100644 --- a/include/git2/rebase.h +++ b/include/git2/rebase.h @@ -64,7 +64,7 @@ typedef struct { /** * Options to control how files are written during `git_rebase_init`, - * `git_checkout_next` and `git_checkout_abort`. Note that a minimum + * `git_rebase_next` and `git_rebase_abort`. Note that a minimum * strategy of `GIT_CHECKOUT_SAFE` is defaulted in `init` and `next`, * and a minimum strategy of `GIT_CHECKOUT_FORCE` is defaulted in * `abort` to match git semantics. From 5bc93eaea0ce133caab0aa169789f73f0c0bc4ae Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 22 Feb 2016 22:26:01 -0500 Subject: [PATCH 397/450] git_libgit2_opts: document GIT_OPT_SET_USER_AGENT --- include/git2/common.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/git2/common.h b/include/git2/common.h index ee230dfae..c26030840 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -245,6 +245,12 @@ typedef enum { * * * opts(GIT_OPT_SET_USER_AGENT, const char *user_agent) * + * > Set the value of the User-Agent header. This value will be + * > appended to "git/1.0", for compatibility with other git clients. + * > + * > - `user_agent` is the value that will be delivered as the + * > User-Agent header on HTTP requests. + * * @param option Option key * @param ... value to set the option * @return 0 on success, <0 on failure From 7bab2e8fbf302fc7d7732ee92652271015db2a58 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 22 Feb 2016 23:04:40 -0500 Subject: [PATCH 398/450] git_libgit2_opts: validate key --- src/settings.c | 3 +++ tests/core/opts.c | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/src/settings.c b/src/settings.c index da99b59e2..d7341abe8 100644 --- a/src/settings.c +++ b/src/settings.c @@ -181,6 +181,9 @@ int git_libgit2_opts(int key, ...) } break; + default: + giterr_set(GITERR_INVALID, "invalid option key"); + error = -1; } va_end(ap); diff --git a/tests/core/opts.c b/tests/core/opts.c index 3173c648b..72408cbe8 100644 --- a/tests/core/opts.c +++ b/tests/core/opts.c @@ -17,3 +17,9 @@ void test_core_opts__readwrite(void) cl_assert(new_val == old_val); } + +void test_core_opts__invalid_option(void) +{ + cl_git_fail(git_libgit2_opts(-1, "foobar")); +} + From f2a554b45e52d7e682f26796e492cd64d8b9a6f4 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 22 Feb 2016 14:43:28 +0100 Subject: [PATCH 399/450] coverity: hint git_vector_foreach does not deref NULL contents Coverity does not comprehend the connection between a vector's size and the contents pointer, that is that the vector's pointer is non-NULL when its size is positive. As the vector code should be reasonably well tested and users are expected to not manually modify a vector's contents it seems save to assume that the macros will never dereference a NULL pointer. Fix Coverity warnings by overriding the foreach macros with macros that explicitly aborting when (v)->contents is NULL. --- script/user_nodefs.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/script/user_nodefs.h b/script/user_nodefs.h index 3d25d92ec..5b0be81a3 100644 --- a/script/user_nodefs.h +++ b/script/user_nodefs.h @@ -25,3 +25,9 @@ #nodef GITERR_CHECK_VERSION(S,V,N) if (giterr__check_version(S,V,N) < 0) { __coverity_panic__(); } #nodef LOOKS_LIKE_DRIVE_PREFIX(S) (strlen(S) >= 2 && git__isalpha((S)[0]) && (S)[1] == ':') + +#nodef git_vector_foreach(v, iter, elem) \ + for ((iter) = 0; (v)->contents != NULL && (iter) < (v)->length && ((elem) = (v)->contents[(iter)], 1); (iter)++ ) + +#nodef git_vector_rforeach(v, iter, elem) \ + for ((iter) = (v)->length - 1; (v)->contents != NULL && (iter) < SIZE_MAX && ((elem) = (v)->contents[(iter)], 1); (iter)-- ) From 859ed5ddc7dc0e208215079265fb012eb8b48048 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 23 Feb 2016 09:54:26 +0100 Subject: [PATCH 400/450] common: introduce GITERR_CHECK_ALLOC_BUF We commonly have to check if a git_buf has been allocated correctly or if we ran out of memory. Introduce a new macro similar to `GITERR_CHECK_ALLOC` which checks if we ran OOM and if so returns an error. Provide a `#nodef` for Coverity to mark the error case as an abort path. --- script/user_nodefs.h | 1 + src/common.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/script/user_nodefs.h b/script/user_nodefs.h index 5b0be81a3..3c06a706d 100644 --- a/script/user_nodefs.h +++ b/script/user_nodefs.h @@ -6,6 +6,7 @@ */ #nodef GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { __coverity_panic__(); } +#nodef GITERR_CHECK_ALLOC_BUF(buf) if (buf == NULL || git_buf_oom(buf)) { __coverity_panic__(); } #nodef GITERR_CHECK_ALLOC_ADD(out, one, two) \ if (GIT_ADD_SIZET_OVERFLOW(out, one, two)) { __coverity_panic__(); } diff --git a/src/common.h b/src/common.h index bc4bdd856..9abd605cb 100644 --- a/src/common.h +++ b/src/common.h @@ -89,6 +89,11 @@ */ #define GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { return -1; } +/** + * Check a buffer allocation result, returning -1 if it failed. + */ +#define GITERR_CHECK_ALLOC_BUF(buf) if ((void *)(buf) == NULL || git_buf_oom(buf)) { return -1; } + /** * Check a return value and propagate result if non-zero. */ From 42c05ed56b3bc4f5963a8c68ec6fec803b7e22e3 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 23 Feb 2016 10:02:44 +0100 Subject: [PATCH 401/450] path: use GITERR_CHECK_ALLOC_BUF to verify passed in buffer --- src/path.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/path.c b/src/path.c index 852ef576a..1fd14fcb9 100644 --- a/src/path.c +++ b/src/path.c @@ -705,8 +705,7 @@ int git_path_resolve_relative(git_buf *path, size_t ceiling) char *base, *to, *from, *next; size_t len; - if (!path || git_buf_oom(path)) - return -1; + GITERR_CHECK_ALLOC_BUF(path); if (ceiling > path->size) ceiling = path->size; From 6e2a37556d4793101b71ac83747a3acb31ee3c5b Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 23 Feb 2016 11:45:43 +0100 Subject: [PATCH 402/450] smart_pkt: check buffer with GITERR_CHECK_ALLOC_BUF --- src/transports/smart_pkt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c index 870f08497..2c33916c7 100644 --- a/src/transports/smart_pkt.c +++ b/src/transports/smart_pkt.c @@ -543,7 +543,9 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca "%04xwant %s %s\n", (unsigned int)len, oid, git_buf_cstr(&str)); git_buf_free(&str); - return git_buf_oom(buf); + GITERR_CHECK_ALLOC_BUF(buf); + + return 0; } /* From c5bd70d13843cc213ec57b10168dd94ed362843b Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 23 Feb 2016 11:48:30 +0100 Subject: [PATCH 403/450] revwalk: use GITERR_CHECK_ALLOC_BUF --- src/revwalk.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/revwalk.c b/src/revwalk.c index 89279ed1f..4815a1089 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -223,8 +223,7 @@ static int push_glob(git_revwalk *walk, const char *glob, int hide) git_buf_joinpath(&buf, GIT_REFS_DIR, glob); else git_buf_puts(&buf, glob); - if (git_buf_oom(&buf)) - return -1; + GITERR_CHECK_ALLOC_BUF(&buf); /* If no '?', '*' or '[' exist, we append '/ *' to the glob */ wildcard = strcspn(glob, "?*["); From b9f28b8d52d595943eb416fec8f5bd3f5ec21f5b Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 23 Feb 2016 10:09:03 +0100 Subject: [PATCH 404/450] refspec: check buffer with GITERR_CHECK_ALLOC_BUF --- src/refspec.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/refspec.c b/src/refspec.c index f92a6d2b6..debde8692 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -323,8 +323,8 @@ int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs) if (git__prefixcmp(spec->src, GIT_REFS_DIR)) { for (j = 0; formatters[j]; j++) { git_buf_clear(&buf); - if (git_buf_printf(&buf, formatters[j], spec->src) < 0) - return -1; + git_buf_printf(&buf, formatters[j], spec->src); + GITERR_CHECK_ALLOC_BUF(&buf); key.name = (char *) git_buf_cstr(&buf); if (!git_vector_search(&pos, refs, &key)) { @@ -348,8 +348,8 @@ int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs) git_buf_puts(&buf, GIT_REFS_HEADS_DIR); } - if (git_buf_puts(&buf, spec->dst) < 0) - return -1; + git_buf_puts(&buf, spec->dst); + GITERR_CHECK_ALLOC_BUF(&buf); cur->dst = git_buf_detach(&buf); } From 2129d6df93ba0ad5b2f7cf15d4e3cfa39487d5a0 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 22 Feb 2016 13:33:48 +0100 Subject: [PATCH 405/450] crlf: do not ignore GIT_PASSTHROUGH error When no payload is set for `crlf_apply` we try to compute the crlf attributes ourselves with `crlf_check`. When the function determines that the current file does not require any treatment we return the GIT_PASSTHROUGH error code without actually allocating the out-pointer, which indicates the file should not be passed through the filter. The `crlf_apply` function explicitly checks for the GIT_PASSTHROUGH return code and ignores it. This means we will try to apply the crlf-filter to the current file, leading us to dereference the unallocated payload-pointer. Fix this obviously incorrect behavior by not treating GIT_PASSTHROUGH in any special way. This is the correct thing to do anyway, as the code indicates that the file should not be passed through the filter. --- src/crlf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crlf.c b/src/crlf.c index f391137c1..5d7510ac7 100644 --- a/src/crlf.c +++ b/src/crlf.c @@ -346,7 +346,7 @@ static int crlf_apply( /* initialize payload in case `check` was bypassed */ if (!*payload) { int error = crlf_check(self, payload, src, NULL); - if (error < 0 && error != GIT_PASSTHROUGH) + if (error < 0) return error; } From d1c9a48df667d6c83cca2ad21b1200fb65d7a1c6 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 23 Feb 2016 10:45:09 +0100 Subject: [PATCH 406/450] pack-objects: check realloc in try_delta with GITERR_CHECK_ALLOC --- src/pack-objects.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pack-objects.c b/src/pack-objects.c index 0afa28e62..5d9c09dd7 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -850,9 +850,11 @@ static int try_delta(git_packbuilder *pb, struct unpacked *trg, git_packbuilder__cache_unlock(pb); - if (overflow || - !(trg_object->delta_data = git__realloc(delta_buf, delta_size))) + if (overflow) return -1; + + trg_object->delta_data = git__realloc(delta_buf, delta_size); + GITERR_CHECK_ALLOC(trg_object->delta_data); } else { /* create delta when writing the pack */ git_packbuilder__cache_unlock(pb); From bac52ab0f2e8d09de1f98590f177e9e847cdb11b Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 22 Feb 2016 13:48:45 +0100 Subject: [PATCH 407/450] pack-objects: return early when computing write order fails The function `compute_write_order` may return a `NULL`-pointer when an error occurs. In such cases we jump to the `done`-label where we try to clean up allocated memory. Unfortunately we try to deallocate the `write_order` array, though, which may be NULL here. Fix this error by returning early instead of jumping to the `done` label. There is no data to be cleaned up anyway. --- src/pack-objects.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pack-objects.c b/src/pack-objects.c index 5d9c09dd7..46fe8f3db 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -629,10 +629,8 @@ static int write_pack(git_packbuilder *pb, int error = 0; write_order = compute_write_order(pb); - if (write_order == NULL) { - error = -1; - goto done; - } + if (write_order == NULL) + return -1; /* Write pack header */ ph.hdr_signature = htonl(PACK_SIGNATURE); From be8479c9873315afcf6b86f0b1bb79052a23b363 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 22 Feb 2016 14:01:50 +0100 Subject: [PATCH 408/450] diff_print: assert patch is non-NULL When invoking `diff_print_info_init_frompatch` it is obvious that the patch should be non-NULL. We explicitly check if the variable is set and continue afterwards, happily dereferencing the potential NULL-pointer. Fix this by instead asserting that patch is set. This also silences Coverity. --- src/diff_print.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/diff_print.c b/src/diff_print.c index bc2d6fab0..dae9e341d 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -92,7 +92,11 @@ static int diff_print_info_init_frompatch( git_diff_line_cb cb, void *payload) { - git_repository *repo = patch && patch->diff ? patch->diff->repo : NULL; + git_repository *repo; + + assert(patch); + + repo = patch->diff ? patch->diff->repo : NULL; memset(pi, 0, sizeof(diff_print_info)); From 793e0855365bbabfeffbd730fd9ee5ee9a011546 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 22 Feb 2016 14:06:48 +0100 Subject: [PATCH 409/450] refdb_fs: remove unnecessary check for NULL The fail-label of `reflog_parse` explicitly checks the entry poitner for NULL before freeing it. When we jump to the label the variable has to be set to a non-NULL and valid pointer though: if the allocation fails we immediately return with an error code and if the loop was not entered we return with a success code, withouth executing the label's code. Remove the useless NULL-check to silence Coverity. --- src/refdb_fs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 1348c67a1..f6ed7201a 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -1512,8 +1512,7 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size) #undef seek_forward fail: - if (entry) - git_reflog_entry__free(entry); + git_reflog_entry__free(entry); return -1; } From 003c5e46a84ec7ecee134b9471e6c84ba673847d Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 22 Feb 2016 15:52:49 +0100 Subject: [PATCH 410/450] transports: smart_pkt: fix memory leaks on error paths --- src/transports/smart_pkt.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c index 2c33916c7..2ea57bb64 100644 --- a/src/transports/smart_pkt.c +++ b/src/transports/smart_pkt.c @@ -296,13 +296,12 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len) pkt = git__malloc(sizeof(*pkt)); GITERR_CHECK_ALLOC(pkt); + pkt->ref = NULL; pkt->type = GIT_PKT_NG; line += 3; /* skip "ng " */ - if (!(ptr = strchr(line, ' '))) { - giterr_set(GITERR_NET, "Invalid packet line"); - return -1; - } + if (!(ptr = strchr(line, ' '))) + goto out_err; len = ptr - line; GITERR_CHECK_ALLOC_ADD(&alloclen, len, 1); @@ -313,12 +312,8 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len) pkt->ref[len] = '\0'; line = ptr + 1; - if (!(ptr = strchr(line, '\n'))) { - giterr_set(GITERR_NET, "Invalid packet line"); - git__free(pkt->ref); - git__free(pkt); - return -1; - } + if (!(ptr = strchr(line, '\n'))) + goto out_err; len = ptr - line; GITERR_CHECK_ALLOC_ADD(&alloclen, len, 1); @@ -330,6 +325,12 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len) *out = (git_pkt *)pkt; return 0; + +out_err: + giterr_set(GITERR_NET, "Invalid packet line"); + git__free(pkt->ref); + git__free(pkt); + return -1; } static int unpack_pkt(git_pkt **out, const char *line, size_t len) From 7808c93797b3fa9f552bd2e24672089b8d27ad2a Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 22 Feb 2016 15:59:15 +0100 Subject: [PATCH 411/450] index: plug memory leak in `read_conflict_names` --- src/index.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/index.c b/src/index.c index d0a0da2c5..85c2f8ea8 100644 --- a/src/index.c +++ b/src/index.c @@ -2193,9 +2193,10 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size #define read_conflict_name(ptr) \ len = p_strnlen(buffer, size) + 1; \ - if (size < len) \ - return index_error_invalid("reading conflict name entries"); \ - \ + if (size < len) { \ + index_error_invalid("reading conflict name entries"); \ + goto out_err; \ + } \ if (len == 1) \ ptr = NULL; \ else { \ @@ -2216,7 +2217,16 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size read_conflict_name(conflict_name->theirs); if (git_vector_insert(&index->names, conflict_name) < 0) - return -1; + goto out_err; + + continue; + +out_err: + git__free(conflict_name->ancestor); + git__free(conflict_name->ours); + git__free(conflict_name->theirs); + git__free(conflict_name); + return -1; } #undef read_conflict_name From 0f1e2d2066115e62fd7396e0f436b4a5dd8384cd Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 23 Feb 2016 11:23:26 +0100 Subject: [PATCH 412/450] index: fix contradicting comparison The overflow check in `read_reuc` tries to verify if the `git__strtol32` parses an integer bigger than UINT_MAX. The `tmp` variable is casted to an unsigned int for this and then checked for being greater than UINT_MAX, which obviously can never be true. Fix this by instead fixing the `mode` field's size in `struct git_index_reuc_entry` to `uint32_t`. We can now parse the int with `git__strtol64`, which can never return a value bigger than `UINT32_MAX`, and additionally checking if the returned value is smaller than zero. We do not need to handle overflows explicitly here, as `git__strtol64` returns an error when the returned value would overflow. --- include/git2/sys/index.h | 2 +- src/index.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/git2/sys/index.h b/include/git2/sys/index.h index 29a99f798..2e2b87e68 100644 --- a/include/git2/sys/index.h +++ b/include/git2/sys/index.h @@ -25,7 +25,7 @@ typedef struct git_index_name_entry { /** Representation of a resolve undo entry in the index. */ typedef struct git_index_reuc_entry { - unsigned int mode[3]; + uint32_t mode[3]; git_oid oid[3]; char *path; } git_index_reuc_entry; diff --git a/src/index.c b/src/index.c index 85c2f8ea8..483f7af7c 100644 --- a/src/index.c +++ b/src/index.c @@ -2135,11 +2135,11 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) /* read 3 ASCII octal numbers for stage entries */ for (i = 0; i < 3; i++) { - int tmp; + int64_t tmp; - if (git__strtol32(&tmp, buffer, &endptr, 8) < 0 || + if (git__strtol64(&tmp, buffer, &endptr, 8) < 0 || !endptr || endptr == buffer || *endptr || - (unsigned)tmp > UINT_MAX) { + tmp < 0) { index_entry_reuc_free(lost); return index_error_invalid("reading reuc entry stage"); } From d0cb11e794de0dbf3998ad88357c17f5d8bf843c Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 22 Feb 2016 16:01:03 +0100 Subject: [PATCH 413/450] remote: set error code in `create_internal` Set the error code when an error occurs in any of the called functions. This ensures we pass the error up to callers and actually free the remote when an error occurs. --- src/remote.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/remote.c b/src/remote.c index 2f8ffcb37..8b7203ee2 100644 --- a/src/remote.c +++ b/src/remote.c @@ -208,8 +208,8 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n remote->repo = repo; - if (git_vector_init(&remote->refs, 32, NULL) < 0 || - canonicalize_url(&canonical_url, url) < 0) + if ((error = git_vector_init(&remote->refs, 32, NULL)) < 0 || + (error = canonicalize_url(&canonical_url, url)) < 0) goto on_error; remote->url = apply_insteadof(repo->_config, canonical_url.ptr, GIT_DIRECTION_FETCH); From 2afb6fa46df25ef77a166b92304cc6e725103c7c Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 22 Feb 2016 16:05:13 +0100 Subject: [PATCH 414/450] rebase: plug memory leak in `rebase_alloc` Convert `rebase_alloc` to use our usual error propagation patterns, that is accept an out-parameter and return an error code that is to be checked by the caller. This allows us to use the GITERR_CHECK_ALLOC macro, which helps static analysis. --- src/rebase.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/rebase.c b/src/rebase.c index b9d1d4fc5..bcad9b7cd 100644 --- a/src/rebase.c +++ b/src/rebase.c @@ -257,12 +257,12 @@ done: return error; } -static git_rebase *rebase_alloc(const git_rebase_options *rebase_opts) +static int rebase_alloc(git_rebase **out, const git_rebase_options *rebase_opts) { git_rebase *rebase = git__calloc(1, sizeof(git_rebase)); + GITERR_CHECK_ALLOC(rebase); - if (!rebase) - return NULL; + *out = NULL; if (rebase_opts) memcpy(&rebase->options, rebase_opts, sizeof(git_rebase_options)); @@ -270,14 +270,16 @@ static git_rebase *rebase_alloc(const git_rebase_options *rebase_opts) git_rebase_init_options(&rebase->options, GIT_REBASE_OPTIONS_VERSION); if (rebase_opts && rebase_opts->rewrite_notes_ref) { - if ((rebase->options.rewrite_notes_ref = git__strdup(rebase_opts->rewrite_notes_ref)) == NULL) - return NULL; + rebase->options.rewrite_notes_ref = git__strdup(rebase_opts->rewrite_notes_ref); + GITERR_CHECK_ALLOC(rebase->options.rewrite_notes_ref); } if ((rebase->options.checkout_options.checkout_strategy & (GIT_CHECKOUT_SAFE | GIT_CHECKOUT_FORCE)) == 0) rebase->options.checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE; - return rebase; + *out = rebase; + + return 0; } static int rebase_check_versions(const git_rebase_options *given_opts) @@ -305,8 +307,8 @@ int git_rebase_open( if ((error = rebase_check_versions(given_opts)) < 0) return error; - rebase = rebase_alloc(given_opts); - GITERR_CHECK_ALLOC(rebase); + if (rebase_alloc(&rebase, given_opts) < 0) + return -1; rebase->repo = repo; @@ -708,8 +710,8 @@ int git_rebase_init( branch = head_branch; } - rebase = rebase_alloc(given_opts); - GITERR_CHECK_ALLOC(rebase); + if (rebase_alloc(&rebase, given_opts) < 0) + return -1; rebase->repo = repo; rebase->inmemory = inmemory; From 2baf854e975267eb560b48f1ead0641ec676d637 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 22 Feb 2016 16:08:56 +0100 Subject: [PATCH 415/450] openssl_stream: fix memory leak when creating new stream --- src/openssl_stream.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/openssl_stream.c b/src/openssl_stream.c index 1dad5f637..840e7dc3f 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -545,6 +545,7 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port) st = git__calloc(1, sizeof(openssl_stream)); GITERR_CHECK_ALLOC(st); + st->io = NULL; #ifdef GIT_CURL error = git_curl_stream_new(&st->io, host, port); #else @@ -552,12 +553,13 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port) #endif if (error < 0) - return error; + goto out_err; st->ssl = SSL_new(git__ssl_ctx); if (st->ssl == NULL) { giterr_set(GITERR_SSL, "failed to create ssl object"); - return -1; + error = -1; + goto out_err; } st->host = git__strdup(host); @@ -576,6 +578,12 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port) *out = (git_stream *) st; return 0; + +out_err: + git_stream_free(st->io); + git__free(st); + + return error; } #else From 05bf67b90186d2ad2c6c9adfa2c0024520cdb342 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 23 Feb 2016 11:16:36 +0100 Subject: [PATCH 416/450] openssl_stream: fix NULL pointer dereference --- src/openssl_stream.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openssl_stream.c b/src/openssl_stream.c index 840e7dc3f..b713171c6 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -383,6 +383,8 @@ static int verify_server_cert(SSL *ssl, const char *host) GITERR_CHECK_ALLOC(peer_cn); memcpy(peer_cn, ASN1_STRING_data(str), size); peer_cn[size] = '\0'; + } else { + goto cert_fail_name; } } else { int size = ASN1_STRING_to_UTF8(&peer_cn, str); From 3d1abc5afcee2b878d835df11530aab8ffa0d1e1 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 22 Feb 2016 17:13:23 +0100 Subject: [PATCH 417/450] xmerge: fix memory leak on error path --- src/xdiff/xmerge.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/xdiff/xmerge.c b/src/xdiff/xmerge.c index 7b7e0e2d3..7928d1418 100644 --- a/src/xdiff/xmerge.c +++ b/src/xdiff/xmerge.c @@ -646,6 +646,8 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2, if (xdl_change_compact(&xe2.xdf1, &xe2.xdf2, xpp->flags) < 0 || xdl_change_compact(&xe2.xdf2, &xe2.xdf1, xpp->flags) < 0 || xdl_build_script(&xe2, &xscr2) < 0) { + xdl_free_script(xscr1); + xdl_free_env(&xe1); xdl_free_env(&xe2); return -1; } From 32f0798413f83cbd1c22e11d81eeb9f664181ec9 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 23 Feb 2016 11:07:03 +0100 Subject: [PATCH 418/450] diff_tform: fix potential NULL pointer access The `normalize_find_opts` function in theory allows for the incoming diff to have no repository. When the caller does not pass in diff find options or if the GIT_DIFF_FIND_BY_CONFIG value is set, though, we try to derive the configuration from the diff's repository configuration without first verifying that the repository is actually set to a non-NULL value. Fix this issue by explicitly checking if the repository is set and if it is not, fall back to a default value of GIT_DIFF_FIND_RENAMES. --- src/diff_tform.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/diff_tform.c b/src/diff_tform.c index 7cff34159..8577f06b8 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -261,18 +261,23 @@ static int normalize_find_opts( if (!given || (given->flags & GIT_DIFF_FIND_ALL) == GIT_DIFF_FIND_BY_CONFIG) { - char *rule = - git_config__get_string_force(cfg, "diff.renames", "true"); - int boolval; + if (diff->repo) { + char *rule = + git_config__get_string_force(cfg, "diff.renames", "true"); + int boolval; - if (!git__parse_bool(&boolval, rule) && !boolval) - /* don't set FIND_RENAMES if bool value is false */; - else if (!strcasecmp(rule, "copies") || !strcasecmp(rule, "copy")) - opts->flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES; - else + if (!git__parse_bool(&boolval, rule) && !boolval) + /* don't set FIND_RENAMES if bool value is false */; + else if (!strcasecmp(rule, "copies") || !strcasecmp(rule, "copy")) + opts->flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES; + else + opts->flags |= GIT_DIFF_FIND_RENAMES; + + git__free(rule); + } else { + /* set default flag */ opts->flags |= GIT_DIFF_FIND_RENAMES; - - git__free(rule); + } } /* some flags imply others */ From 04c3b35f9c90fb93390c0198bc1a8123ca13e081 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 23 Feb 2016 13:02:07 -0500 Subject: [PATCH 419/450] map: use `giterr_set` internally Use the `giterr_set` function, which actually supports `GITERR_OS`. The `giterr_set_str` function is exposed for external users and will not append the operating system's error message. --- src/unix/map.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unix/map.c b/src/unix/map.c index 87ee6594b..72abb3418 100644 --- a/src/unix/map.c +++ b/src/unix/map.c @@ -17,7 +17,7 @@ int git__page_size(size_t *page_size) { long sc_page_size = sysconf(_SC_PAGE_SIZE); if (sc_page_size < 0) { - giterr_set_str(GITERR_OS, "Can't determine system page size"); + giterr_set(GITERR_OS, "can't determine system page size"); return -1; } *page_size = (size_t) sc_page_size; From cd59e0c0c757a5bcc4ed2324b207846b411368cb Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 23 Feb 2016 13:05:49 -0500 Subject: [PATCH 420/450] giterr_set_str: remove `GITERR_OS` documentation The `giterr_set_str` does not actually honor `GITERR_OS`. Remove the documentation that claims that we do. --- include/git2/errors.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/git2/errors.h b/include/git2/errors.h index 1b528cf25..3ecea34bf 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -126,11 +126,6 @@ GIT_EXTERN(void) giterr_clear(void); * This error message is stored in thread-local storage and only applies * to the particular thread that this libgit2 call is made from. * - * NOTE: Passing the `error_class` as GITERR_OS has a special behavior: we - * attempt to append the system default error message for the last OS error - * that occurred and then clear the last error. The specific implementation - * of looking up and clearing this last OS error will vary by platform. - * * @param error_class One of the `git_error_t` enum above describing the * general subsystem that is responsible for the error. * @param string The formatted error message to keep From f3d1be7d62e67bd2f7c5266c40a8a0ea7c8dec80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 24 Feb 2016 16:38:22 +0100 Subject: [PATCH 421/450] openssl: export the locking function when building without OpenSSL This got lost duing the move and it lets the users call this function just in case. --- src/openssl_stream.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/openssl_stream.c b/src/openssl_stream.c index 1dad5f637..26eb90d72 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -603,4 +603,10 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port) return -1; } +int git_openssl_set_locking(void) +{ + /* No OpenSSL here, move along */ + return 0; +} + #endif From 68ad3156a03202d9f5be9d45e935ead3047fc97a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 24 Feb 2016 17:17:57 +0100 Subject: [PATCH 422/450] openssl: we already had the function, just needed the header --- src/openssl_stream.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/openssl_stream.c b/src/openssl_stream.c index 26eb90d72..15cabdfb8 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -581,6 +581,7 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port) #else #include "stream.h" +#include "git2/sys/openssl.h" int git_openssl_stream_global_init(void) { @@ -603,10 +604,4 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port) return -1; } -int git_openssl_set_locking(void) -{ - /* No OpenSSL here, move along */ - return 0; -} - #endif From 1f8cb02f51efb1382a130f73d5c99ee08f020ffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 25 Feb 2016 14:51:00 +0100 Subject: [PATCH 423/450] CONVENTIONS: update to include general public API principles --- CONVENTIONS.md | 32 ++++++++++++++++++++++++++++++++ README.md | 6 ++++++ 2 files changed, 38 insertions(+) diff --git a/CONVENTIONS.md b/CONVENTIONS.md index 5b8238a78..0be4b33cc 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -3,6 +3,38 @@ We like to keep the source consistent and readable. Herein are some guidelines that should help with that. +## External API + +We have a few rules to avoid surprising ways of calling functions and +some rules for consumers of the library to avoid stepping on each +other's toes. + + - Property accessors return the value directly (e.g. an `int` or + `const char *`) but if a function can fail, we return a `int` value + and the output parameters go first in the parameter list, followed + by the object that a function is operating on, and then any other + arguments the function may need. + + - If a function returns an object as a return value, that function is + a getter and the object's lifetime is tied to the parent + object. Objects which are returned as the first argument as a + pointer-to-pointer are owned by the caller and it is repsponsible + for freeing it. Strings are returned via `git_buf` in order to + allow for re-use and safe freeing. + + - Most of what libgit2 does relates to I/O so you as a general rule + you should assume that any function can fail due to errors as even + getting data from the filesystem can result in all sorts of errors + and complex failure cases. + + - Paths inside the Git system are separated by a slash (0x2F). If a + function accepts a path on disk, then backslashes (0x5C) are also + accepted on Windows. + + - Do not mix allocators. If something has been allocated by libgit2, + you do not know which is the right free function in the general + case. Use the free functions provided for each object type. + ## Compatibility `libgit2` runs on many different platforms with many different compilers. diff --git a/README.md b/README.md index bcc80d017..8ea787b3e 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,12 @@ Threading See [THREADING](THREADING.md) for information +Conventions +=========== + +See [CONVENTIONS](CONVENTIONS.md) for an overview of the external +and internal API/coding conventions we use. + Building libgit2 - Using CMake ============================== From ea9e00cb5ca271b794cca686019d17cc378e23f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 23 Feb 2016 18:15:43 +0100 Subject: [PATCH 424/450] pack: make sure we don't go out of bounds for extended entries A corrupt index might have data that tells us to go look past the end of the file for data. Catch these cases and return an appropriate error message. --- src/pack.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/pack.c b/src/pack.c index 081e37084..b9b22a2ef 100644 --- a/src/pack.c +++ b/src/pack.c @@ -1175,6 +1175,7 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path) static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n) { const unsigned char *index = p->index_map.data; + const unsigned char *end = index + p->index_map.len; index += 4 * 256; if (p->index_version == 1) { return ntohl(*((uint32_t *)(index + 24 * n))); @@ -1185,6 +1186,11 @@ static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_ if (!(off & 0x80000000)) return off; index += p->num_objects * 4 + (off & 0x7fffffff) * 8; + + /* Make sure we're not being sent out of bounds */ + if (index >= end - 8) + return -1; + return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) | ntohl(*((uint32_t *)(index + 4))); } @@ -1264,6 +1270,7 @@ static int pack_entry_find_offset( const unsigned char *index = p->index_map.data; unsigned hi, lo, stride; int pos, found = 0; + git_off_t offset; const unsigned char *current = 0; *offset_out = 0; @@ -1336,7 +1343,12 @@ static int pack_entry_find_offset( if (found > 1) return git_odb__error_ambiguous("found multiple offsets for pack entry"); - *offset_out = nth_packed_object_offset(p, pos); + if ((offset = nth_packed_object_offset(p, pos)) < 0) { + giterr_set(GITERR_ODB, "packfile index is corrupt"); + return -1; + } + + *offset_out = offset; git_oid_fromraw(found_oid, current); #ifdef INDEX_DEBUG_LOOKUP From 6d97beb91f413f4e90bab76fc7e05495bb0aab70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 25 Feb 2016 15:46:59 +0100 Subject: [PATCH 425/450] pack: don't allow a negative offset --- src/pack.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pack.c b/src/pack.c index b9b22a2ef..52c652178 100644 --- a/src/pack.c +++ b/src/pack.c @@ -365,9 +365,14 @@ static unsigned char *pack_window_open( * pointless to ask for an offset into the middle of that * hash, and the pack_window_contains function above wouldn't match * don't allow an offset too close to the end of the file. + * + * Don't allow a negative offset, as that means we've wrapped + * around. */ if (offset > (p->mwf.size - 20)) return NULL; + if (offset < 0) + return NULL; return git_mwindow_open(&p->mwf, w_cursor, offset, 20, left); } From a4c55069e3481fda0ab6abe82f0c63672eb8b3e9 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 25 Feb 2016 11:31:18 -0500 Subject: [PATCH 426/450] nsec: update staging test for GIT_USE_NSECS The index::nsec::staging_maintains_other_nanos test was created to ensure that when we stage an entry when GIT_USE_NSECS is *unset* that we truncate the index entry and do not persist the (old, invalid) nanosec values. Ensure that when GIT_USE_NSECS is *set* that we do not do that, and actually write the correct nanosecond values. --- tests/index/nsec.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/index/nsec.c b/tests/index/nsec.c index 5004339f0..244ab6362 100644 --- a/tests/index/nsec.c +++ b/tests/index/nsec.c @@ -61,8 +61,17 @@ void test_index_nsec__staging_maintains_other_nanos(void) cl_assert_equal_b(true, has_nsecs()); cl_assert((entry = git_index_get_bypath(repo_index, "a.txt", 0))); + + /* if we are writing nanoseconds to the index, expect them to be + * nonzero. if we are *not*, expect that we truncated the entry. + */ +#ifdef GIT_USE_NSEC + cl_assert(entry->ctime.nanoseconds != 0); + cl_assert(entry->mtime.nanoseconds != 0); +#else cl_assert_equal_i(0, entry->ctime.nanoseconds); cl_assert_equal_i(0, entry->mtime.nanoseconds); +#endif } void test_index_nsec__status_doesnt_clear_nsecs(void) From 3d6a42d1e14281c564d779e7490d761555d014d4 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 25 Feb 2016 11:23:19 -0500 Subject: [PATCH 427/450] nsec: support NDK's crazy nanoseconds Android NDK does not have a `struct timespec` in its `struct stat` for nanosecond support, instead it has a single nanosecond member inside the struct stat itself. We will use that and use a macro to expand to the `st_mtim` / `st_mtimespec` definition on other systems (much like the existing `st_mtime` backcompat definition). --- CMakeLists.txt | 24 ++++++++++++++++-------- src/fileops.c | 17 ++++++++--------- src/index.c | 4 ++-- src/unix/posix.h | 12 ++++++++++++ src/win32/win32-compat.h | 3 +++ 5 files changed, 41 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0aa58625a..6a7dac21d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,17 +86,21 @@ IF (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") OPTION( USE_OPENSSL "Link with and use openssl library" ON ) ENDIF() -CHECK_STRUCT_HAS_MEMBER ("struct stat" st_atim "sys/types.h;sys/stat.h" - HAVE_STRUCT_STAT_ST_ATIM LANGUAGE C) -CHECK_STRUCT_HAS_MEMBER ("struct stat" st_atimespec "sys/types.h;sys/stat.h" - HAVE_STRUCT_STAT_ST_ATIMESPEC LANGUAGE C) +CHECK_STRUCT_HAS_MEMBER ("struct stat" st_mtim "sys/types.h;sys/stat.h" + HAVE_STRUCT_STAT_ST_MTIM LANGUAGE C) +CHECK_STRUCT_HAS_MEMBER ("struct stat" st_mtimespec "sys/types.h;sys/stat.h" + HAVE_STRUCT_STAT_ST_MTIMESPEC LANGUAGE C) +CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtime_nsec sys/stat.h + HAVE_STRUCT_STAT_MTIME_NSEC LANGUAGE C) -IF (HAVE_STRUCT_STAT_ST_ATIM) +IF (HAVE_STRUCT_STAT_ST_MTIM) CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec sys/stat.h HAVE_STRUCT_STAT_NSEC LANGUAGE C) -ELSEIF (HAVE_STRUCT_STAT_ST_ATIMESPEC) +ELSEIF (HAVE_STRUCT_STAT_ST_MTIMESPEC) CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtimespec.tv_nsec sys/stat.h HAVE_STRUCT_STAT_NSEC LANGUAGE C) +ELSE () + SET( HAVE_STRUCT_STAT_NSEC ON ) ENDIF() IF (HAVE_STRUCT_STAT_NSEC OR WIN32) @@ -539,8 +543,12 @@ IF (USE_NSEC) ADD_DEFINITIONS(-DGIT_USE_NSEC) ENDIF() -IF (HAVE_STRUCT_STAT_ST_ATIMESPEC) - ADD_DEFINITIONS(-DGIT_USE_STAT_ATIMESPEC) +IF (HAVE_STRUCT_STAT_ST_MTIM) + ADD_DEFINITIONS(-DGIT_USE_STAT_MTIM) +ELSEIF (HAVE_STRUCT_STAT_ST_MTIMESPEC) + ADD_DEFINITIONS(-DGIT_USE_STAT_MTIMESPEC) +ELSEIF (HAVE_STRUCT_STAT_ST_MTIME_NSEC) + ADD_DEFINITIONS(-DGIT_USE_STAT_MTIME_NSEC) ENDIF() ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64) diff --git a/src/fileops.c b/src/fileops.c index 150333d7a..22868b489 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -1034,7 +1034,6 @@ int git_futils_filestamp_check( git_futils_filestamp *stamp, const char *path) { struct stat st; - const struct timespec *statmtime = &st.st_mtim; /* if the stamp is NULL, then always reload */ if (stamp == NULL) @@ -1043,17 +1042,17 @@ int git_futils_filestamp_check( if (p_stat(path, &st) < 0) return GIT_ENOTFOUND; - if (stamp->mtime.tv_sec == statmtime->tv_sec && + if (stamp->mtime.tv_sec == st.st_mtime && #if defined(GIT_USE_NSEC) - stamp->mtime.tv_nsec == statmtime->tv_nsec && + stamp->mtime.tv_nsec == st.st_mtime_nsec && #endif stamp->size == (git_off_t)st.st_size && stamp->ino == (unsigned int)st.st_ino) return 0; - stamp->mtime.tv_sec = statmtime->tv_sec; + stamp->mtime.tv_sec = st.st_mtime; #if defined(GIT_USE_NSEC) - stamp->mtime.tv_nsec = statmtime->tv_nsec; + stamp->mtime.tv_nsec = st.st_mtime_nsec; #endif stamp->size = (git_off_t)st.st_size; stamp->ino = (unsigned int)st.st_ino; @@ -1076,11 +1075,11 @@ void git_futils_filestamp_set( void git_futils_filestamp_set_from_stat( git_futils_filestamp *stamp, struct stat *st) { - const struct timespec *statmtime = &st->st_mtim; - if (st) { - stamp->mtime = *statmtime; -#if !defined(GIT_USE_NSEC) + stamp->mtime.tv_sec = st->st_mtime; +#if defined(GIT_USE_NSEC) + stamp->mtime.tv_nsec = st->st_mtime_nsec; +#else stamp->mtime.tv_nsec = 0; #endif stamp->size = (git_off_t)st->st_size; diff --git a/src/index.c b/src/index.c index d0a0da2c5..c18f358f4 100644 --- a/src/index.c +++ b/src/index.c @@ -829,8 +829,8 @@ void git_index_entry__init_from_stat( entry->ctime.seconds = (int32_t)st->st_ctime; entry->mtime.seconds = (int32_t)st->st_mtime; #if defined(GIT_USE_NSEC) - entry->mtime.nanoseconds = st->st_mtim.tv_nsec; - entry->ctime.nanoseconds = st->st_ctim.tv_nsec; + entry->mtime.nanoseconds = st->st_mtime_nsec; + entry->ctime.nanoseconds = st->st_ctime_nsec; #endif entry->dev = st->st_rdev; entry->ino = st->st_ino; diff --git a/src/unix/posix.h b/src/unix/posix.h index 83edf2b7e..482d2c803 100644 --- a/src/unix/posix.h +++ b/src/unix/posix.h @@ -21,6 +21,18 @@ typedef int GIT_SOCKET; #define p_lstat(p,b) lstat(p,b) #define p_stat(p,b) stat(p, b) +#if defined(GIT_USE_STAT_MTIMESPEC) +# define st_atime_nsec st_atimespec.tv_nsec +# define st_mtime_nsec st_mtimespec.tv_nsec +# define st_ctime_nsec st_ctimespec.tv_nsec +#elif defined(GIT_USE_STAT_MTIM) +# define st_atime_nsec st_atim.tv_nsec +# define st_mtime_nsec st_mtim.tv_nsec +# define st_ctime_nsec st_ctim.tv_nsec +#elif !defined(GIT_USE_STAT_MTIME_NSEC) && defined(GIT_USE_NEC) +# error GIT_USE_NSEC defined but unknown struct stat nanosecond type +#endif + #define p_utimes(f, t) utimes(f, t) #define p_readlink(a, b, c) readlink(a, b, c) diff --git a/src/win32/win32-compat.h b/src/win32/win32-compat.h index dff1f45be..f888fd69e 100644 --- a/src/win32/win32-compat.h +++ b/src/win32/win32-compat.h @@ -42,6 +42,9 @@ struct p_stat { #define st_atime st_atim.tv_sec #define st_mtime st_mtim.tv_sec #define st_ctime st_ctim.tv_sec +#define st_atime_nsec st_atim.tv_nsec +#define st_mtime_nsec st_mtim.tv_nsec +#define st_ctime_nsec st_ctim.tv_nsec }; #define stat p_stat From 93e16642280ab637f8688b8c2146b11f95f98325 Mon Sep 17 00:00:00 2001 From: Ross Delinger Date: Fri, 26 Feb 2016 12:51:13 -0500 Subject: [PATCH 428/450] Fixed typo in one of the ifndef's in pool.h used to enable/disable debug mode --- src/pool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pool.h b/src/pool.h index 1cae48fd3..e0fafa997 100644 --- a/src/pool.h +++ b/src/pool.h @@ -124,7 +124,7 @@ extern char *git_pool_strcat(git_pool *pool, const char *a, const char *b); /* * Misc utilities */ -#ifndef _DEBUG_POOL +#ifndef GIT_DEBUG_POOL extern uint32_t git_pool__open_pages(git_pool *pool); #endif extern bool git_pool__ptr_in_pool(git_pool *pool, void *ptr); From 09db7fd8a6b9b20588b1a42342eda4f0edbbffea Mon Sep 17 00:00:00 2001 From: Stjepan Rajko Date: Thu, 25 Feb 2016 15:19:51 -0700 Subject: [PATCH 429/450] Expand OpenSSL and libssh2 thread safety documentation --- THREADING.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/THREADING.md b/THREADING.md index 3717d6c88..0b9e50286 100644 --- a/THREADING.md +++ b/THREADING.md @@ -72,13 +72,19 @@ which locking function it should use. This means that libgit2 cannot know what to set as the user of libgit2 may use OpenSSL independently and the locking settings must survive libgit2 shutting down. +Even if libgit2 doesn't use OpenSSL directly, OpenSSL can still be used +by libssh2 depending on the configuration. If OpenSSL is used both by +libgit2 and libssh2, you only need to set up threading for OpenSSL once. + libgit2 does provide a last-resort convenience function `git_openssl_set_locking()` (available in `sys/openssl.h`) to use the platform-native mutex mechanisms to perform the locking, which you may rely on if you do not want to use OpenSSL outside of libgit2, or you know that libgit2 will outlive the rest of the operations. It is not safe to use OpenSSL multi-threaded after libgit2's shutdown function -has been called. +has been called. Note `git_openssl_set_locking()` only works if +libgit2 uses OpenSSL directly - if OpenSSL is only used as a dependency +of libssh2 as described above, `git_openssl_set_locking()` is a no-op. If your programming language offers a package/bindings for OpenSSL, you should very strongly prefer to use that in order to set up @@ -87,14 +93,14 @@ when using this function. See the [OpenSSL documentation](https://www.openssl.org/docs/crypto/threads.html) -on threading for more details. +on threading for more details, and http://trac.libssh2.org/wiki/MultiThreading +for a specific example of providing the threading callbacks. Be also aware that libgit2 does not always link against OpenSSL if there are alternatives provided by the system. -libssh2 may be linked against OpenSSL or libgcrypt. If it uses -OpenSSL, you only need to set up threading for OpenSSL once and the -above paragraphs are enough. If it uses libgcrypt, then you need to +libssh2 may be linked against OpenSSL or libgcrypt. If it uses OpenSSL, +see the above paragraphs. If it uses libgcrypt, then you need to set up its locking before using it multi-threaded. libgit2 has no direct connection to libgcrypt and thus has not convenience functions for it (but libgcrypt has macros). Read libgcrypt's From da0335604410f57982f973a1f05a8e2c719eb137 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 28 Feb 2016 11:34:36 -0500 Subject: [PATCH 430/450] travis ci: enable debug pool for valgrind builds --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9022fdec2..2f3ffe355 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,13 +46,13 @@ matrix: - compiler: gcc env: - VALGRIND=1 - OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DCMAKE_BUILD_TYPE=Debug" + OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DDEBUG_POOL=ON -DCMAKE_BUILD_TYPE=Debug" os: linux allow_failures: - env: COVERITY=1 - env: - VALGRIND=1 - OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DCMAKE_BUILD_TYPE=Debug" + OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DDEBUG_POOL=ON -DCMAKE_BUILD_TYPE=Debug" install: - if [ "$TRAVIS_OS_NAME" = "osx" ]; then ./script/install-deps-${TRAVIS_OS_NAME}.sh; fi From 22a19f5b5795153b4c77c75adfae790c3b919be4 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 22 Feb 2016 23:46:50 -0500 Subject: [PATCH 431/450] git_libgit2_opts: introduce `GIT_OPT_ENABLE_STRICT_OBJECT_CREATION` --- include/git2/common.h | 9 +++++++++ src/object.c | 2 ++ src/object.h | 2 ++ src/settings.c | 6 ++++++ 4 files changed, 19 insertions(+) diff --git a/include/git2/common.h b/include/git2/common.h index c26030840..4f43185f8 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -147,6 +147,7 @@ typedef enum { GIT_OPT_SET_TEMPLATE_PATH, GIT_OPT_SET_SSL_CERT_LOCATIONS, GIT_OPT_SET_USER_AGENT, + GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, } git_libgit2_opt_t; /** @@ -251,6 +252,14 @@ typedef enum { * > - `user_agent` is the value that will be delivered as the * > User-Agent header on HTTP requests. * + * * opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, int enabled) + * + * > Enable strict input validation when creating new objects + * > to ensure that all inputs to the new objects are valid. For + * > example, when this is enabled, the parent(s) and tree inputs + * > will be validated when creating a new commit. This defaults + * > to disabled. + * * @param option Option key * @param ... value to set the option * @return 0 on success, <0 on failure diff --git a/src/object.c b/src/object.c index b0a8199bc..7f7de9fee 100644 --- a/src/object.c +++ b/src/object.c @@ -14,6 +14,8 @@ #include "blob.h" #include "tag.h" +bool git_object__strict_input_validation = false; + typedef struct { const char *str; /* type name string */ size_t size; /* size in bytes of the object structure */ diff --git a/src/object.h b/src/object.h index d187c55b7..358279a3d 100644 --- a/src/object.h +++ b/src/object.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_object_h__ #define INCLUDE_object_h__ +extern bool git_object__strict_input_validation; + /** Base git object for inheritance */ struct git_object { git_cached_obj cached; diff --git a/src/settings.c b/src/settings.c index d7341abe8..88602bad0 100644 --- a/src/settings.c +++ b/src/settings.c @@ -14,6 +14,7 @@ #include "sysdir.h" #include "cache.h" #include "global.h" +#include "object.h" void git_libgit2_version(int *major, int *minor, int *rev) { @@ -181,6 +182,11 @@ int git_libgit2_opts(int key, ...) } break; + + case GIT_OPT_ENABLE_STRICT_OBJECT_CREATION: + git_object__strict_input_validation = (va_arg(ap, int) != 0); + break; + default: giterr_set(GITERR_INVALID, "invalid option key"); error = -1; From 7565dc6572e94ee10462169dd07ba8afe08388c7 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 23 Feb 2016 13:33:10 -0500 Subject: [PATCH 432/450] git_object__is_valid: simple object validity test --- src/object.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/object.h b/src/object.h index 358279a3d..782436017 100644 --- a/src/object.h +++ b/src/object.h @@ -30,4 +30,20 @@ int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid); +GIT_INLINE(bool) git_object__is_valid( + git_repository *repo, const git_oid *id, git_otype type) +{ + git_object *obj = NULL; + bool valid = true; + + if (git_object__strict_input_validation) { + if (git_object_lookup(&obj, repo, id, type) < 0) + valid = false; + + git_object_free(obj); + } + + return valid; +} + #endif From ef63bab306a2a85d15e62bfb73f49ae11f2b5df6 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 23 Feb 2016 13:34:35 -0500 Subject: [PATCH 433/450] git_commit: validate tree and parent ids When `GIT_OPT_ENABLE_STRICT_OBJECT_CREATION` is turned on, validate the tree and parent ids given to commit creation functions. --- src/commit.c | 48 ++++++++++++++----- tests/commit/write.c | 111 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 143 insertions(+), 16 deletions(-) diff --git a/src/commit.c b/src/commit.c index 5a0509803..685c642aa 100644 --- a/src/commit.c +++ b/src/commit.c @@ -17,6 +17,7 @@ #include "signature.h" #include "message.h" #include "refs.h" +#include "object.h" void git_commit__free(void *_commit) { @@ -36,7 +37,7 @@ void git_commit__free(void *_commit) git__free(commit); } -int git_commit_create_from_callback( +static int git_commit__create_internal( git_oid *id, git_repository *repo, const char *update_ref, @@ -46,7 +47,8 @@ int git_commit_create_from_callback( const char *message, const git_oid *tree, git_commit_parent_callback parent_cb, - void *parent_payload) + void *parent_payload, + bool validate) { git_reference *ref = NULL; int error = 0, matched_parent = 0; @@ -58,6 +60,9 @@ int git_commit_create_from_callback( assert(id && repo && tree && parent_cb); + if (validate && !git_object__is_valid(repo, tree, GIT_OBJ_TREE)) + return -1; + if (update_ref) { error = git_reference_lookup_resolved(&ref, repo, update_ref, 10); if (error < 0 && error != GIT_ENOTFOUND) @@ -71,6 +76,11 @@ int git_commit_create_from_callback( git_oid__writebuf(&commit, "tree ", tree); while ((parent = parent_cb(i, parent_payload)) != NULL) { + if (validate && !git_object__is_valid(repo, parent, GIT_OBJ_COMMIT)) { + error = -1; + goto on_error; + } + git_oid__writebuf(&commit, "parent ", parent); if (i == 0 && current_id && git_oid_equal(current_id, parent)) matched_parent = 1; @@ -114,10 +124,26 @@ int git_commit_create_from_callback( on_error: git_buf_free(&commit); - giterr_set(GITERR_OBJECT, "Failed to create commit."); return -1; } +int git_commit_create_from_callback( + git_oid *id, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_oid *tree, + git_commit_parent_callback parent_cb, + void *parent_payload) +{ + return git_commit__create_internal( + id, repo, update_ref, author, committer, message_encoding, message, + tree, parent_cb, parent_payload, true); +} + typedef struct { size_t total; va_list args; @@ -153,10 +179,10 @@ int git_commit_create_v( data.total = parent_count; va_start(data.args, parent_count); - error = git_commit_create_from_callback( + error = git_commit__create_internal( id, repo, update_ref, author, committer, message_encoding, message, git_tree_id(tree), - commit_parent_from_varargs, &data); + commit_parent_from_varargs, &data, false); va_end(data.args); return error; @@ -187,10 +213,10 @@ int git_commit_create_from_ids( { commit_parent_oids data = { parent_count, parents }; - return git_commit_create_from_callback( + return git_commit__create_internal( id, repo, update_ref, author, committer, message_encoding, message, tree, - commit_parent_from_ids, &data); + commit_parent_from_ids, &data, true); } typedef struct { @@ -227,10 +253,10 @@ int git_commit_create( assert(tree && git_tree_owner(tree) == repo); - return git_commit_create_from_callback( + return git_commit__create_internal( id, repo, update_ref, author, committer, message_encoding, message, git_tree_id(tree), - commit_parent_from_array, &data); + commit_parent_from_array, &data, false); } static const git_oid *commit_parent_for_amend(size_t curr, void *payload) @@ -290,9 +316,9 @@ int git_commit_amend( } } - error = git_commit_create_from_callback( + error = git_commit__create_internal( id, repo, NULL, author, committer, message_encoding, message, - &tree_id, commit_parent_for_amend, (void *)commit_to_amend); + &tree_id, commit_parent_for_amend, (void *)commit_to_amend, false); if (!error && update_ref) { error = git_reference__update_for_commit( diff --git a/tests/commit/write.c b/tests/commit/write.c index 176965cbd..303d1ce58 100644 --- a/tests/commit/write.c +++ b/tests/commit/write.c @@ -1,10 +1,12 @@ #include "clar_libgit2.h" +#include "git2/sys/commit.h" static const char *committer_name = "Vicent Marti"; static const char *committer_email = "vicent@github.com"; static const char *commit_message = "This commit has been created in memory\n\ This is a commit created in memory and it will be written back to disk\n"; -static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd"; +static const char *tree_id_str = "1810dff58d8a660512d4832e740f692884338ccd"; +static const char *parent_id_str = "8496071c1b46c854b31185ea97743be6a8774479"; static const char *root_commit_message = "This is a root commit\n\ This is a root commit and should be the only one in this branch\n"; static const char *root_reflog_message = "commit (initial): This is a root commit \ @@ -35,6 +37,8 @@ void test_commit_write__cleanup(void) head_old = NULL; cl_git_sandbox_cleanup(); + + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0)); } @@ -46,12 +50,11 @@ void test_commit_write__from_memory(void) const git_signature *author1, *committer1; git_commit *parent; git_tree *tree; - const char *commit_id_str = "8496071c1b46c854b31185ea97743be6a8774479"; - git_oid_fromstr(&tree_id, tree_oid); + git_oid_fromstr(&tree_id, tree_id_str); cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); - git_oid_fromstr(&parent_id, commit_id_str); + git_oid_fromstr(&parent_id, parent_id_str); cl_git_pass(git_commit_lookup(&parent, g_repo, &parent_id)); /* create signatures */ @@ -106,7 +109,7 @@ void test_commit_write__root(void) git_reflog *log; const git_reflog_entry *entry; - git_oid_fromstr(&tree_id, tree_oid); + git_oid_fromstr(&tree_id, tree_id_str); cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); /* create signatures */ @@ -158,3 +161,101 @@ void test_commit_write__root(void) git_signature_free(committer); git_reflog_free(log); } + +static int create_commit_from_ids( + git_oid *result, + const git_oid *tree_id, + const git_oid *parent_id) +{ + git_signature *author, *committer; + const git_oid *parent_ids[1]; + int ret; + + cl_git_pass(git_signature_new( + &committer, committer_name, committer_email, 123456789, 60)); + cl_git_pass(git_signature_new( + &author, committer_name, committer_email, 987654321, 90)); + + parent_ids[0] = parent_id; + + ret = git_commit_create_from_ids( + result, + g_repo, + NULL, + author, + committer, + NULL, + root_commit_message, + tree_id, + 1, + parent_ids); + + git_signature_free(committer); + git_signature_free(author); + + return ret; +} + +void test_commit_write__doesnt_validate_objects_by_default(void) +{ + git_oid expected_id, tree_id, parent_id, commit_id; + + /* this is a valid tree and parent */ + git_oid_fromstr(&tree_id, tree_id_str); + git_oid_fromstr(&parent_id, parent_id_str); + + git_oid_fromstr(&expected_id, "c8571bbec3a72c4bcad31648902e5a453f1adece"); + cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id)); + cl_assert_equal_oid(&expected_id, &commit_id); + + /* this is a wholly invented tree id */ + git_oid_fromstr(&tree_id, "1234567890123456789012345678901234567890"); + git_oid_fromstr(&parent_id, parent_id_str); + + git_oid_fromstr(&expected_id, "996008340b8e68d69bf3c28d7c57fb7ec3c8e202"); + cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id)); + cl_assert_equal_oid(&expected_id, &commit_id); + + /* this is a wholly invented parent id */ + git_oid_fromstr(&tree_id, tree_id_str); + git_oid_fromstr(&parent_id, "1234567890123456789012345678901234567890"); + + git_oid_fromstr(&expected_id, "d78f660cab89d9791ca6714b57978bf2a7e709fd"); + cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id)); + cl_assert_equal_oid(&expected_id, &commit_id); + + /* these are legitimate objects, but of the wrong type */ + git_oid_fromstr(&tree_id, parent_id_str); + git_oid_fromstr(&parent_id, tree_id_str); + + git_oid_fromstr(&expected_id, "5d80c07414e3f18792949699dfcacadf7748f361"); + cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id)); + cl_assert_equal_oid(&expected_id, &commit_id); +} + +void test_commit_write__can_validate_objects(void) +{ + git_oid tree_id, parent_id, commit_id; + + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1)); + + /* this is a valid tree and parent */ + git_oid_fromstr(&tree_id, tree_id_str); + git_oid_fromstr(&parent_id, parent_id_str); + cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id)); + + /* this is a wholly invented tree id */ + git_oid_fromstr(&tree_id, "1234567890123456789012345678901234567890"); + git_oid_fromstr(&parent_id, parent_id_str); + cl_git_fail(create_commit_from_ids(&commit_id, &tree_id, &parent_id)); + + /* this is a wholly invented parent id */ + git_oid_fromstr(&tree_id, tree_id_str); + git_oid_fromstr(&parent_id, "1234567890123456789012345678901234567890"); + cl_git_fail(create_commit_from_ids(&commit_id, &tree_id, &parent_id)); + + /* these are legitimate objects, but of the wrong type */ + git_oid_fromstr(&tree_id, parent_id_str); + git_oid_fromstr(&parent_id, tree_id_str); + cl_git_fail(create_commit_from_ids(&commit_id, &tree_id, &parent_id)); +} From 2bbc7d3e564ed262e9555ea4fd081ece5ceb3bff Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 23 Feb 2016 15:00:27 -0500 Subject: [PATCH 434/450] treebuilder: validate tree entries (optionally) When `GIT_OPT_ENABLE_STRICT_OBJECT_CREATION` is turned on, validate the tree and parent ids given to treebuilder insertion. --- src/tree.c | 15 +++++++++++ tests/object/tree/write.c | 55 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/src/tree.c b/src/tree.c index cfceb3f33..2c3151546 100644 --- a/src/tree.c +++ b/src/tree.c @@ -726,6 +726,18 @@ on_error: return -1; } +static git_otype otype_from_mode(git_filemode_t filemode) +{ + switch (filemode) { + case GIT_FILEMODE_TREE: + return GIT_OBJ_TREE; + case GIT_FILEMODE_COMMIT: + return GIT_OBJ_COMMIT; + default: + return GIT_OBJ_BLOB; + } +} + int git_treebuilder_insert( const git_tree_entry **entry_out, git_treebuilder *bld, @@ -745,6 +757,9 @@ int git_treebuilder_insert( if (!valid_entry_name(bld->repo, filename)) return tree_error("Failed to insert entry. Invalid name for a tree entry", filename); + if (!git_object__is_valid(bld->repo, id, otype_from_mode(filemode))) + return tree_error("Failed to insert entry; invalid object specified", filename); + pos = git_strmap_lookup_index(bld->map, filename); if (git_strmap_valid_index(bld->map, pos)) { entry = git_strmap_value_at(bld->map, pos); diff --git a/tests/object/tree/write.c b/tests/object/tree/write.c index 5433e5f03..4cd1607d8 100644 --- a/tests/object/tree/write.c +++ b/tests/object/tree/write.c @@ -18,6 +18,8 @@ void test_object_tree_write__initialize(void) void test_object_tree_write__cleanup(void) { cl_git_sandbox_cleanup(); + + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0)); } void test_object_tree_write__from_memory(void) @@ -440,3 +442,56 @@ void test_object_tree_write__protect_filesystems(void) git_treebuilder_free(builder); } + +static void test_invalid_objects(bool should_allow_invalid) +{ + git_treebuilder *builder; + git_oid valid_blob_id, invalid_blob_id, valid_tree_id, invalid_tree_id; + +#define assert_allowed(expr) \ + clar__assert(!(expr) == should_allow_invalid, __FILE__, __LINE__, \ + (should_allow_invalid ? \ + "Expected function call to succeed: " #expr : \ + "Expected function call to fail: " #expr), \ + NULL, 1) + + cl_git_pass(git_oid_fromstr(&valid_blob_id, blob_oid)); + cl_git_pass(git_oid_fromstr(&invalid_blob_id, + "1234567890123456789012345678901234567890")); + cl_git_pass(git_oid_fromstr(&valid_tree_id, first_tree)); + cl_git_pass(git_oid_fromstr(&invalid_tree_id, + "0000000000111111111122222222223333333333")); + + cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); + + /* test valid blobs and trees (these should always pass) */ + cl_git_pass(git_treebuilder_insert(NULL, builder, "file.txt", &valid_blob_id, GIT_FILEMODE_BLOB)); + cl_git_pass(git_treebuilder_insert(NULL, builder, "folder", &valid_tree_id, GIT_FILEMODE_TREE)); + + /* replace valid files and folders with invalid ones */ + assert_allowed(git_treebuilder_insert(NULL, builder, "file.txt", &invalid_blob_id, GIT_FILEMODE_BLOB)); + assert_allowed(git_treebuilder_insert(NULL, builder, "folder", &invalid_blob_id, GIT_FILEMODE_BLOB)); + + /* insert new invalid files and folders */ + assert_allowed(git_treebuilder_insert(NULL, builder, "invalid_file.txt", &invalid_blob_id, GIT_FILEMODE_BLOB)); + assert_allowed(git_treebuilder_insert(NULL, builder, "invalid_folder", &invalid_blob_id, GIT_FILEMODE_BLOB)); + + /* insert valid blobs as trees and trees as blobs */ + assert_allowed(git_treebuilder_insert(NULL, builder, "file_as_folder", &valid_blob_id, GIT_FILEMODE_TREE)); + assert_allowed(git_treebuilder_insert(NULL, builder, "folder_as_file.txt", &valid_tree_id, GIT_FILEMODE_BLOB)); + +#undef assert_allowed + + git_treebuilder_free(builder); +} + +void test_object_tree_write__object_validity(void) +{ + /* Ensure that we can add invalid objects by default */ + test_invalid_objects(true); + + /* Ensure that we can turn on validation */ + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1)); + test_invalid_objects(false); +} + From 6ddf533afca5e25b7c532b4c164ae66e06f9c1c2 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 23 Feb 2016 18:29:16 -0500 Subject: [PATCH 435/450] git_index_add: validate objects in index entries (optionally) When `GIT_OPT_ENABLE_STRICT_OBJECT_CREATION` is turned on, validate the index entries given to `git_index_add`. --- src/index.c | 26 +++++++++++---- src/object.h | 16 +++++++++ tests/index/add.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 tests/index/add.c diff --git a/src/index.c b/src/index.c index 5704432ae..b97f8091d 100644 --- a/src/index.c +++ b/src/index.c @@ -1245,17 +1245,22 @@ static void index_existing_and_best( * it, then it will return an error **and also free the entry**. When * it replaces an existing entry, it will update the entry_ptr with the * actual entry in the index (and free the passed in one). + * * trust_path is whether we use the given path, or whether (on case * insensitive systems only) we try to canonicalize the given path to * be within an existing directory. + * * trust_mode is whether we trust the mode in entry_ptr. + * + * trust_id is whether we trust the id or it should be validated. */ static int index_insert( git_index *index, git_index_entry **entry_ptr, int replace, bool trust_path, - bool trust_mode) + bool trust_mode, + bool trust_id) { int error = 0; size_t path_length, position; @@ -1288,6 +1293,15 @@ static int index_insert( if (!trust_path) error = canonicalize_directory_path(index, entry, best); + /* ensure that the given id exists (unless it's a submodule) */ + if (!error && !trust_id && INDEX_OWNER(index) && + (entry->mode & GIT_FILEMODE_COMMIT) != GIT_FILEMODE_COMMIT) { + + if (!git_object__is_valid(INDEX_OWNER(index), &entry->id, + git_object__type_from_filemode(entry->mode))) + error = -1; + } + /* look for tree / blob name collisions, removing conflicts if requested */ if (!error) error = check_file_directory_collision(index, entry, position, replace); @@ -1395,7 +1409,7 @@ int git_index_add_frombuffer( git_oid_cpy(&entry->id, &id); entry->file_size = len; - if ((error = index_insert(index, &entry, 1, true, true)) < 0) + if ((error = index_insert(index, &entry, 1, true, true, true)) < 0) return error; /* Adding implies conflict was resolved, move conflict entries to REUC */ @@ -1454,7 +1468,7 @@ int git_index_add_bypath(git_index *index, const char *path) assert(index && path); if ((ret = index_entry_init(&entry, index, path)) == 0) - ret = index_insert(index, &entry, 1, false, false); + ret = index_insert(index, &entry, 1, false, false, true); /* If we were given a directory, let's see if it's a submodule */ if (ret < 0 && ret != GIT_EDIRECTORY) @@ -1480,7 +1494,7 @@ int git_index_add_bypath(git_index *index, const char *path) if ((ret = add_repo_as_submodule(&entry, index, path)) < 0) return ret; - if ((ret = index_insert(index, &entry, 1, false, false)) < 0) + if ((ret = index_insert(index, &entry, 1, false, false, true)) < 0) return ret; } else if (ret < 0) { return ret; @@ -1569,7 +1583,7 @@ int git_index_add(git_index *index, const git_index_entry *source_entry) } if ((ret = index_entry_dup(&entry, index, source_entry)) < 0 || - (ret = index_insert(index, &entry, 1, true, true)) < 0) + (ret = index_insert(index, &entry, 1, true, true, false)) < 0) return ret; git_tree_cache_invalidate_path(index->tree, entry->path); @@ -1731,7 +1745,7 @@ int git_index_conflict_add(git_index *index, /* Make sure stage is correct */ GIT_IDXENTRY_STAGE_SET(entries[i], i + 1); - if ((ret = index_insert(index, &entries[i], 1, true, true)) < 0) + if ((ret = index_insert(index, &entries[i], 1, true, true, false)) < 0) goto on_error; entries[i] = NULL; /* don't free if later entry fails */ diff --git a/src/object.h b/src/object.h index 782436017..13edf3118 100644 --- a/src/object.h +++ b/src/object.h @@ -46,4 +46,20 @@ GIT_INLINE(bool) git_object__is_valid( return valid; } +GIT_INLINE(git_otype) git_object__type_from_filemode(git_filemode_t mode) +{ + switch (mode) { + case GIT_FILEMODE_TREE: + return GIT_OBJ_TREE; + case GIT_FILEMODE_COMMIT: + return GIT_OBJ_COMMIT; + case GIT_FILEMODE_BLOB: + case GIT_FILEMODE_BLOB_EXECUTABLE: + case GIT_FILEMODE_LINK: + return GIT_OBJ_BLOB; + default: + return GIT_OBJ_BAD; + } +} + #endif diff --git a/tests/index/add.c b/tests/index/add.c new file mode 100644 index 000000000..cfa81c4d9 --- /dev/null +++ b/tests/index/add.c @@ -0,0 +1,84 @@ +#include "clar_libgit2.h" + +static git_repository *g_repo = NULL; +static git_index *g_index = NULL; + +static const char *valid_blob_id = "fa49b077972391ad58037050f2a75f74e3671e92"; +static const char *valid_tree_id = "181037049a54a1eb5fab404658a3a250b44335d7"; +static const char *valid_commit_id = "763d71aadf09a7951596c9746c024e7eece7c7af"; +static const char *invalid_id = "1234567890123456789012345678901234567890"; + +void test_index_add__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); + cl_git_pass(git_repository_index(&g_index, g_repo)); +} + +void test_index_add__cleanup(void) +{ + git_index_free(g_index); + cl_git_sandbox_cleanup(); + g_repo = NULL; + + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0)); +} + +static void test_add_entry( + bool should_succeed, const char *idstr, git_filemode_t mode) +{ + git_index_entry entry = {{0}}; + + cl_git_pass(git_oid_fromstr(&entry.id, idstr)); + + entry.path = mode == GIT_FILEMODE_TREE ? "test_folder" : "test_file"; + entry.mode = mode; + + if (should_succeed) + cl_git_pass(git_index_add(g_index, &entry)); + else + cl_git_fail(git_index_add(g_index, &entry)); +} + +void test_index_add__invalid_entries_succeeds_by_default(void) +{ + /* + * Ensure that there is no validation on ids by default + */ + + /* ensure that we can add some actually good entries */ + test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB); + test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB_EXECUTABLE); + test_add_entry(true, valid_blob_id, GIT_FILEMODE_LINK); + + /* test that we fail to add some invalid (missing) blobs and trees */ + test_add_entry(true, invalid_id, GIT_FILEMODE_BLOB); + test_add_entry(true, invalid_id, GIT_FILEMODE_BLOB_EXECUTABLE); + test_add_entry(true, invalid_id, GIT_FILEMODE_LINK); + + /* test that we validate the types of objects */ + test_add_entry(true, valid_commit_id, GIT_FILEMODE_BLOB); + test_add_entry(true, valid_tree_id, GIT_FILEMODE_BLOB_EXECUTABLE); + test_add_entry(true, valid_commit_id, GIT_FILEMODE_LINK); + + /* + * Ensure that strict object references will fail the `index_add` + */ + + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1)); + + /* ensure that we can add some actually good entries */ + test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB); + test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB_EXECUTABLE); + test_add_entry(true, valid_blob_id, GIT_FILEMODE_LINK); + + /* test that we fail to add some invalid (missing) blobs and trees */ + test_add_entry(false, invalid_id, GIT_FILEMODE_BLOB); + test_add_entry(false, invalid_id, GIT_FILEMODE_BLOB_EXECUTABLE); + test_add_entry(false, invalid_id, GIT_FILEMODE_LINK); + + /* test that we validate the types of objects */ + test_add_entry(false, valid_commit_id, GIT_FILEMODE_BLOB); + test_add_entry(false, valid_tree_id, GIT_FILEMODE_BLOB_EXECUTABLE); + test_add_entry(false, valid_commit_id, GIT_FILEMODE_LINK); +} + From 3ef01e772729f44c2871b590577cc64e4a58a381 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 28 Feb 2016 14:37:37 -0500 Subject: [PATCH 436/450] git_object__is_valid: use `odb_read_header` This allows lighter weight validation in `git_object__is_valid` that does not require reading the entire object. --- src/object.c | 24 ++++++++++++++++++++++++ src/object.h | 19 ++++--------------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/object.c b/src/object.c index 7f7de9fee..e7c1fef09 100644 --- a/src/object.c +++ b/src/object.c @@ -467,3 +467,27 @@ int git_object_short_id(git_buf *out, const git_object *obj) return error; } +bool git_object__is_valid( + git_repository *repo, const git_oid *id, git_otype expected_type) +{ + git_odb *odb; + git_otype actual_type; + size_t len; + int error; + + if (!git_object__strict_input_validation) + return true; + + if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 || + (error = git_odb_read_header(&len, &actual_type, odb, id)) < 0) + return false; + + if (expected_type != GIT_OBJ_ANY && expected_type != actual_type) { + giterr_set(GITERR_INVALID, + "the requested type does not match the type in the ODB"); + return false; + } + + return true; +} + diff --git a/src/object.h b/src/object.h index 13edf3118..dd227d16d 100644 --- a/src/object.h +++ b/src/object.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_object_h__ #define INCLUDE_object_h__ +#include "repository.h" + extern bool git_object__strict_input_validation; /** Base git object for inheritance */ @@ -30,21 +32,8 @@ int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid); -GIT_INLINE(bool) git_object__is_valid( - git_repository *repo, const git_oid *id, git_otype type) -{ - git_object *obj = NULL; - bool valid = true; - - if (git_object__strict_input_validation) { - if (git_object_lookup(&obj, repo, id, type) < 0) - valid = false; - - git_object_free(obj); - } - - return valid; -} +bool git_object__is_valid( + git_repository *repo, const git_oid *id, git_otype expected_type); GIT_INLINE(git_otype) git_object__type_from_filemode(git_filemode_t mode) { From 98c341496f276aa93737fd6ac71a23b77249d82d Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 28 Feb 2016 15:11:15 -0500 Subject: [PATCH 437/450] refs: honor strict object creation --- src/refs.c | 8 +------- tests/refs/create.c | 26 ++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/refs.c b/src/refs.c index 7b538659d..a15e31b53 100644 --- a/src/refs.c +++ b/src/refs.c @@ -377,15 +377,9 @@ static int reference__create( return error; if (oid != NULL) { - git_odb *odb; - assert(symbolic == NULL); - /* Sanity check the reference being created - target must exist. */ - if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) - return error; - - if (!git_odb_exists(odb, oid)) { + if (!git_object__is_valid(repo, oid, GIT_OBJ_ANY)) { giterr_set(GITERR_REFERENCE, "Target OID for the reference doesn't exist on the repository"); return -1; diff --git a/tests/refs/create.c b/tests/refs/create.c index 48194ae3b..a0bc78014 100644 --- a/tests/refs/create.c +++ b/tests/refs/create.c @@ -18,6 +18,8 @@ void test_refs_create__initialize(void) void test_refs_create__cleanup(void) { cl_git_sandbox_cleanup(); + + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0)); } void test_refs_create__symbolic(void) @@ -119,9 +121,9 @@ void test_refs_create__oid(void) git_reference_free(looked_up_ref); } -void test_refs_create__oid_unknown(void) +/* Can by default create a reference that targets at an unknown id */ +void test_refs_create__oid_unknown_succeeds_by_default(void) { - // Can not create a new OID reference which targets at an unknown id git_reference *new_reference, *looked_up_ref; git_oid id; @@ -129,6 +131,26 @@ void test_refs_create__oid_unknown(void) git_oid_fromstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644"); + /* Create and write the new object id reference */ + cl_git_pass(git_reference_create(&new_reference, g_repo, new_head, &id, 0, NULL)); + + /* Ensure the reference can't be looked-up... */ + cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head)); + git_reference_free(looked_up_ref); +} + +/* Strict object enforcement enforces valid object id */ +void test_refs_create__oid_unknown_fails_strict_mode(void) +{ + git_reference *new_reference, *looked_up_ref; + git_oid id; + + const char *new_head = "refs/heads/new-head"; + + git_oid_fromstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644"); + + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1)); + /* Create and write the new object id reference */ cl_git_fail(git_reference_create(&new_reference, g_repo, new_head, &id, 0, NULL)); From 4afe536ba1c909ff8ab8d1b75997d3897b72571b Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 28 Feb 2016 16:02:49 -0500 Subject: [PATCH 438/450] tests: use legitimate object ids Use legitimate (existing) object IDs in tests so that we have the ability to turn on strict object validation when running tests. --- tests/diff/index.c | 8 ++--- tests/diff/workdir.c | 8 +++-- tests/index/bypath.c | 3 ++ tests/index/cache.c | 6 ++-- tests/index/conflicts.c | 34 +++++++----------- tests/index/filemodes.c | 7 ++++ tests/index/racy.c | 4 +++ tests/object/tree/write.c | 31 +++++++++------- .../2b/d0a343aeef7a2cf0d158478966a6e587ff3863 | Bin 0 -> 56 bytes .../d4/27e0b2e138501a3d15cc376077a3631e15bd46 | Bin 0 -> 38 bytes .../ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf | Bin 0 -> 64 bytes .../ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf | Bin 0 -> 64 bytes 12 files changed, 57 insertions(+), 44 deletions(-) create mode 100644 tests/resources/status/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 create mode 100644 tests/resources/status/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 create mode 100644 tests/resources/status/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf create mode 100644 tests/resources/testrepo/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf diff --git a/tests/diff/index.c b/tests/diff/index.c index df45ad236..0293b7821 100644 --- a/tests/diff/index.c +++ b/tests/diff/index.c @@ -185,9 +185,9 @@ static void do_conflicted_diff(diff_expects *exp, unsigned long flags) ancestor.path = ours.path = theirs.path = "staged_changes"; ancestor.mode = ours.mode = theirs.mode = GIT_FILEMODE_BLOB; - git_oid_fromstr(&ancestor.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); - git_oid_fromstr(&ours.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); - git_oid_fromstr(&theirs.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); + git_oid_fromstr(&ancestor.id, "d427e0b2e138501a3d15cc376077a3631e15bd46"); + git_oid_fromstr(&ours.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf"); + git_oid_fromstr(&theirs.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863"); cl_git_pass(git_index_conflict_add(index, &ancestor, &ours, &theirs)); cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, index, &opts)); @@ -255,7 +255,7 @@ void test_diff_index__not_in_head_conflicted(void) theirs.path = "file_not_in_head"; theirs.mode = GIT_FILEMODE_BLOB; - git_oid_fromstr(&theirs.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); + git_oid_fromstr(&theirs.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863"); cl_git_pass(git_index_conflict_add(index, NULL, NULL, &theirs)); cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, index, NULL)); diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c index 892c7b72d..e1bbce8fb 100644 --- a/tests/diff/workdir.c +++ b/tests/diff/workdir.c @@ -85,9 +85,11 @@ void test_diff_workdir__to_index_with_conflicts(void) /* Adding an entry that represents a rename gets two files in conflict */ our_entry.path = "subdir/modified_file"; our_entry.mode = 0100644; + git_oid_fromstr(&our_entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf"); their_entry.path = "subdir/rename_conflict"; their_entry.mode = 0100644; + git_oid_fromstr(&their_entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863"); cl_git_pass(git_repository_index(&index, g_repo)); cl_git_pass(git_index_conflict_add(index, NULL, &our_entry, &their_entry)); @@ -1975,9 +1977,9 @@ void test_diff_workdir__to_index_conflicted(void) { ancestor.path = ours.path = theirs.path = "_file"; ancestor.mode = ours.mode = theirs.mode = 0100644; - git_oid_fromstr(&ancestor.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); - git_oid_fromstr(&ours.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); - git_oid_fromstr(&theirs.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); + git_oid_fromstr(&ancestor.id, "d427e0b2e138501a3d15cc376077a3631e15bd46"); + git_oid_fromstr(&ours.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf"); + git_oid_fromstr(&theirs.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863"); cl_git_pass(git_index_conflict_add(index, &ancestor, &ours, &theirs)); cl_git_pass(git_diff_tree_to_index(&diff1, g_repo, a, index, NULL)); diff --git a/tests/index/bypath.c b/tests/index/bypath.c index 88a76178a..34a7412a8 100644 --- a/tests/index/bypath.c +++ b/tests/index/bypath.c @@ -134,6 +134,7 @@ void test_index_bypath__add_honors_existing_case_2(void) clar__skip(); dummy.mode = GIT_FILEMODE_BLOB; + cl_git_pass(git_oid_fromstr(&dummy.id, "f990a25a74d1a8281ce2ab018ea8df66795cd60b")); /* note that `git_index_add` does no checking to canonical directories */ dummy.path = "Just_a_dir/file0.txt"; @@ -189,6 +190,7 @@ void test_index_bypath__add_honors_existing_case_3(void) clar__skip(); dummy.mode = GIT_FILEMODE_BLOB; + cl_git_pass(git_oid_fromstr(&dummy.id, "f990a25a74d1a8281ce2ab018ea8df66795cd60b")); dummy.path = "just_a_dir/filea.txt"; cl_git_pass(git_index_add(g_idx, &dummy)); @@ -219,6 +221,7 @@ void test_index_bypath__add_honors_existing_case_4(void) clar__skip(); dummy.mode = GIT_FILEMODE_BLOB; + cl_git_pass(git_oid_fromstr(&dummy.id, "f990a25a74d1a8281ce2ab018ea8df66795cd60b")); dummy.path = "just_a_dir/a/b/c/d/e/file1.txt"; cl_git_pass(git_index_add(g_idx, &dummy)); diff --git a/tests/index/cache.c b/tests/index/cache.c index 3982bf183..56885aff7 100644 --- a/tests/index/cache.c +++ b/tests/index/cache.c @@ -111,7 +111,7 @@ void test_index_cache__read_tree_no_children(void) memset(&entry, 0x0, sizeof(git_index_entry)); entry.path = "new.txt"; entry.mode = GIT_FILEMODE_BLOB; - git_oid_fromstr(&entry.id, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057"); + git_oid_fromstr(&entry.id, "d4bcc68acd4410bf836a39f20afb2c2ece09584e"); cl_git_pass(git_index_add(index, &entry)); cl_assert_equal_i(-1, index->tree->entry_count); @@ -191,7 +191,7 @@ void test_index_cache__read_tree_children(void) memset(&entry, 0x0, sizeof(git_index_entry)); entry.path = "top-level"; entry.mode = GIT_FILEMODE_BLOB; - git_oid_fromstr(&entry.id, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057"); + git_oid_fromstr(&entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf"); cl_git_pass(git_index_add(index, &entry)); @@ -217,7 +217,7 @@ void test_index_cache__read_tree_children(void) /* override with a slightly different id, also dummy */ entry.path = "subdir/some-file"; - git_oid_fromstr(&entry.id, "45b983be36b73c0788dc9cbcb76cbb80fc7bb058"); + git_oid_fromstr(&entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf"); cl_git_pass(git_index_add(index, &entry)); cl_assert_equal_i(-1, index->tree->entry_count); diff --git a/tests/index/conflicts.c b/tests/index/conflicts.c index 8e94cd441..d4004686f 100644 --- a/tests/index/conflicts.c +++ b/tests/index/conflicts.c @@ -16,11 +16,6 @@ static git_index *repo_index; #define CONFLICTS_TWO_OUR_OID "8b3f43d2402825c200f835ca1762413e386fd0b2" #define CONFLICTS_TWO_THEIR_OID "220bd62631c8cf7a83ef39c6b94595f00517211e" -#define TEST_STAGED_OID "beefdadafeedabedcafedeedbabedeadbeaddeaf" -#define TEST_ANCESTOR_OID "f00ff00ff00ff00ff00ff00ff00ff00ff00ff00f" -#define TEST_OUR_OID "b44bb44bb44bb44bb44bb44bb44bb44bb44bb44b" -#define TEST_THEIR_OID "0123456789abcdef0123456789abcdef01234567" - // Fixture setup and teardown void test_index_conflicts__initialize(void) { @@ -49,17 +44,17 @@ void test_index_conflicts__add(void) ancestor_entry.path = "test-one.txt"; ancestor_entry.mode = 0100644; GIT_IDXENTRY_STAGE_SET(&ancestor_entry, 1); - git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID); + git_oid_fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID); our_entry.path = "test-one.txt"; our_entry.mode = 0100644; GIT_IDXENTRY_STAGE_SET(&our_entry, 2); - git_oid_fromstr(&our_entry.id, TEST_OUR_OID); + git_oid_fromstr(&our_entry.id, CONFLICTS_ONE_OUR_OID); their_entry.path = "test-one.txt"; their_entry.mode = 0100644; GIT_IDXENTRY_STAGE_SET(&ancestor_entry, 2); - git_oid_fromstr(&their_entry.id, TEST_THEIR_OID); + git_oid_fromstr(&their_entry.id, CONFLICTS_ONE_THEIR_OID); cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry)); @@ -80,17 +75,17 @@ void test_index_conflicts__add_fixes_incorrect_stage(void) ancestor_entry.path = "test-one.txt"; ancestor_entry.mode = 0100644; GIT_IDXENTRY_STAGE_SET(&ancestor_entry, 3); - git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID); + git_oid_fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID); our_entry.path = "test-one.txt"; our_entry.mode = 0100644; GIT_IDXENTRY_STAGE_SET(&our_entry, 1); - git_oid_fromstr(&our_entry.id, TEST_OUR_OID); + git_oid_fromstr(&our_entry.id, CONFLICTS_ONE_OUR_OID); their_entry.path = "test-one.txt"; their_entry.mode = 0100644; GIT_IDXENTRY_STAGE_SET(&their_entry, 2); - git_oid_fromstr(&their_entry.id, TEST_THEIR_OID); + git_oid_fromstr(&their_entry.id, CONFLICTS_ONE_THEIR_OID); cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry)); @@ -105,36 +100,33 @@ void test_index_conflicts__add_fixes_incorrect_stage(void) void test_index_conflicts__add_removes_stage_zero(void) { - git_index_entry staged, ancestor_entry, our_entry, their_entry; + git_index_entry ancestor_entry, our_entry, their_entry; const git_index_entry *conflict_entry[3]; cl_assert(git_index_entrycount(repo_index) == 8); - memset(&staged, 0x0, sizeof(git_index_entry)); memset(&ancestor_entry, 0x0, sizeof(git_index_entry)); memset(&our_entry, 0x0, sizeof(git_index_entry)); memset(&their_entry, 0x0, sizeof(git_index_entry)); - staged.path = "test-one.txt"; - staged.mode = 0100644; - git_oid_fromstr(&staged.id, TEST_STAGED_OID); - cl_git_pass(git_index_add(repo_index, &staged)); + cl_git_mkfile("./mergedrepo/test-one.txt", "new-file\n"); + cl_git_pass(git_index_add_bypath(repo_index, "test-one.txt")); cl_assert(git_index_entrycount(repo_index) == 9); ancestor_entry.path = "test-one.txt"; ancestor_entry.mode = 0100644; GIT_IDXENTRY_STAGE_SET(&ancestor_entry, 3); - git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID); + git_oid_fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID); our_entry.path = "test-one.txt"; our_entry.mode = 0100644; GIT_IDXENTRY_STAGE_SET(&our_entry, 1); - git_oid_fromstr(&our_entry.id, TEST_OUR_OID); + git_oid_fromstr(&our_entry.id, CONFLICTS_ONE_OUR_OID); their_entry.path = "test-one.txt"; their_entry.mode = 0100644; GIT_IDXENTRY_STAGE_SET(&their_entry, 2); - git_oid_fromstr(&their_entry.id, TEST_THEIR_OID); + git_oid_fromstr(&their_entry.id, CONFLICTS_ONE_THEIR_OID); cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry)); @@ -330,7 +322,7 @@ void test_index_conflicts__partial(void) ancestor_entry.path = "test-one.txt"; ancestor_entry.mode = 0100644; GIT_IDXENTRY_STAGE_SET(&ancestor_entry, 1); - git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID); + git_oid_fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID); cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, NULL, NULL)); cl_assert(git_index_entrycount(repo_index) == 9); diff --git a/tests/index/filemodes.c b/tests/index/filemodes.c index 6442d7755..2efad5b33 100644 --- a/tests/index/filemodes.c +++ b/tests/index/filemodes.c @@ -236,12 +236,19 @@ void test_index_filemodes__invalid(void) { git_index *index; git_index_entry entry; + const git_index_entry *dummy; cl_git_pass(git_repository_index(&index, g_repo)); + /* add a dummy file so that we have a valid id */ + cl_git_mkfile("./filemodes/dummy-file.txt", "new-file\n"); + cl_git_pass(git_index_add_bypath(index, "dummy-file.txt")); + cl_assert((dummy = git_index_get_bypath(index, "dummy-file.txt", 0))); + GIT_IDXENTRY_STAGE_SET(&entry, 0); entry.path = "foo"; entry.mode = GIT_OBJ_BLOB; + git_oid_cpy(&entry.id, &dummy->id); cl_git_fail(git_index_add(index, &entry)); entry.mode = GIT_FILEMODE_BLOB; diff --git a/tests/index/racy.c b/tests/index/racy.c index ace84d585..68aa46007 100644 --- a/tests/index/racy.c +++ b/tests/index/racy.c @@ -178,6 +178,7 @@ static void setup_uptodate_files(void) { git_buf path = GIT_BUF_INIT; git_index *index; + const git_index_entry *a_entry; git_index_entry new_entry = {{0}}; cl_git_pass(git_repository_index(&index, g_repo)); @@ -188,9 +189,12 @@ static void setup_uptodate_files(void) /* Put 'A' into the index */ cl_git_pass(git_index_add_bypath(index, "A")); + cl_assert((a_entry = git_index_get_bypath(index, "A", 0))); + /* Put 'B' into the index */ new_entry.path = "B"; new_entry.mode = GIT_FILEMODE_BLOB; + git_oid_cpy(&new_entry.id, &a_entry->id); cl_git_pass(git_index_add(index, &new_entry)); /* Put 'C' into the index */ diff --git a/tests/object/tree/write.c b/tests/object/tree/write.c index 4cd1607d8..f779b8ce6 100644 --- a/tests/object/tree/write.c +++ b/tests/object/tree/write.c @@ -133,15 +133,18 @@ void test_object_tree_write__sorted_subtrees(void) { GIT_FILEMODE_TREE, "vendors"} }; - git_oid blank_oid, tree_oid; + git_oid bid, tid, tree_oid; - memset(&blank_oid, 0x0, sizeof(blank_oid)); + cl_git_pass(git_oid_fromstr(&bid, blob_oid)); + cl_git_pass(git_oid_fromstr(&tid, first_tree)); cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); for (i = 0; i < ARRAY_SIZE(entries); ++i) { + git_oid *id = entries[i].attr == GIT_FILEMODE_TREE ? &tid : &bid; + cl_git_pass(git_treebuilder_insert(NULL, - builder, entries[i].filename, &blank_oid, entries[i].attr)); + builder, entries[i].filename, id, entries[i].attr)); } cl_git_pass(git_treebuilder_write(&tree_oid, builder)); @@ -189,10 +192,10 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void) { git_treebuilder *builder; int i, aardvark_i, apple_i, apple_after_i, apple_extra_i, last_i; - git_oid blank_oid, tree_oid; + git_oid entry_oid, tree_oid; git_tree *tree; - memset(&blank_oid, 0x0, sizeof(blank_oid)); + cl_git_pass(git_oid_fromstr(&entry_oid, blob_oid)); cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); @@ -200,7 +203,7 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void) for (i = 0; _entries[i].filename; ++i) cl_git_pass(git_treebuilder_insert(NULL, - builder, _entries[i].filename, &blank_oid, _entries[i].attr)); + builder, _entries[i].filename, &entry_oid, _entries[i].attr)); cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder)); @@ -211,12 +214,12 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void) cl_assert_equal_i(4, (int)git_treebuilder_entrycount(builder)); cl_git_pass(git_treebuilder_insert( - NULL, builder, "before_last", &blank_oid, GIT_FILEMODE_BLOB)); + NULL, builder, "before_last", &entry_oid, GIT_FILEMODE_BLOB)); cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder)); /* reinsert apple_after */ cl_git_pass(git_treebuilder_insert( - NULL, builder, "apple_after", &blank_oid, GIT_FILEMODE_BLOB)); + NULL, builder, "apple_after", &entry_oid, GIT_FILEMODE_BLOB)); cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder)); cl_git_pass(git_treebuilder_remove(builder, "last")); @@ -224,11 +227,11 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void) /* reinsert last */ cl_git_pass(git_treebuilder_insert( - NULL, builder, "last", &blank_oid, GIT_FILEMODE_BLOB)); + NULL, builder, "last", &entry_oid, GIT_FILEMODE_BLOB)); cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder)); cl_git_pass(git_treebuilder_insert( - NULL, builder, "apple_extra", &blank_oid, GIT_FILEMODE_BLOB)); + NULL, builder, "apple_extra", &entry_oid, GIT_FILEMODE_BLOB)); cl_assert_equal_i(7, (int)git_treebuilder_entrycount(builder)); cl_git_pass(git_treebuilder_write(&tree_oid, builder)); @@ -280,16 +283,16 @@ void test_object_tree_write__filtering(void) { git_treebuilder *builder; int i; - git_oid blank_oid, tree_oid; + git_oid entry_oid, tree_oid; git_tree *tree; - memset(&blank_oid, 0x0, sizeof(blank_oid)); + git_oid_fromstr(&entry_oid, blob_oid); cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); for (i = 0; _entries[i].filename; ++i) cl_git_pass(git_treebuilder_insert(NULL, - builder, _entries[i].filename, &blank_oid, _entries[i].attr)); + builder, _entries[i].filename, &entry_oid, _entries[i].attr)); cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder)); @@ -408,6 +411,8 @@ void test_object_tree_write__protect_filesystems(void) git_treebuilder *builder; git_oid bid; + cl_git_pass(git_oid_fromstr(&bid, "fa49b077972391ad58037050f2a75f74e3671e92")); + /* Ensure that (by default) we can write objects with funny names on * platforms that are not affected. */ diff --git a/tests/resources/status/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 b/tests/resources/status/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 new file mode 100644 index 0000000000000000000000000000000000000000..d10ca636b6bf5f66bce633362d871d17d9a292c6 GIT binary patch literal 56 zcmV-80LTA$0ZYosPf{>3VkpVTELKR%%t=)M(#aW#dFiPs3YmEdNkxfy$r%cXc_|9H OiNz(UMO*-@y%7?c&=$P_ literal 0 HcmV?d00001 diff --git a/tests/resources/status/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 b/tests/resources/status/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 new file mode 100644 index 0000000000000000000000000000000000000000..0b3611ae4c9dc635dafd5dc9560cbc2ff5e92198 GIT binary patch literal 38 ucmb4F=r^r$ShV!%gjkt0Mf}BiFxU%DGHf+3b~2JC84F=r^r$ShV!%gjkt0Mf}BiFxU%DGHf+3b~2JC8 Date: Sun, 28 Feb 2016 15:51:38 -0500 Subject: [PATCH 439/450] turn on strict object validation by default --- CHANGELOG.md | 5 +++++ src/object.c | 2 +- tests/commit/write.c | 8 +++---- tests/index/add.c | 46 +++++++++++++++++++-------------------- tests/object/tree/write.c | 12 +++++----- 5 files changed, 39 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec5a0d336..9c50f1211 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,11 @@ v0.23 + 1 * Rebases can now be performed purely in-memory, without touching the repository's workdir. +* When adding objects to the index, or when creating new tree or commit + objects, the inputs are validated to ensure that the dependent objects + exist and are of the correct type. This object validation can be + disabled with the GIT_OPT_ENABLE_STRICT_OBJECT_CREATION option. + ### API additions * `git_config_lock()` has been added, which allow for diff --git a/src/object.c b/src/object.c index e7c1fef09..ebf77fb47 100644 --- a/src/object.c +++ b/src/object.c @@ -14,7 +14,7 @@ #include "blob.h" #include "tag.h" -bool git_object__strict_input_validation = false; +bool git_object__strict_input_validation = true; typedef struct { const char *str; /* type name string */ diff --git a/tests/commit/write.c b/tests/commit/write.c index 303d1ce58..96b7cc321 100644 --- a/tests/commit/write.c +++ b/tests/commit/write.c @@ -38,7 +38,7 @@ void test_commit_write__cleanup(void) cl_git_sandbox_cleanup(); - cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0)); + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1)); } @@ -196,10 +196,12 @@ static int create_commit_from_ids( return ret; } -void test_commit_write__doesnt_validate_objects_by_default(void) +void test_commit_write__can_write_invalid_objects(void) { git_oid expected_id, tree_id, parent_id, commit_id; + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0)); + /* this is a valid tree and parent */ git_oid_fromstr(&tree_id, tree_id_str); git_oid_fromstr(&parent_id, parent_id_str); @@ -237,8 +239,6 @@ void test_commit_write__can_validate_objects(void) { git_oid tree_id, parent_id, commit_id; - cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1)); - /* this is a valid tree and parent */ git_oid_fromstr(&tree_id, tree_id_str); git_oid_fromstr(&parent_id, parent_id_str); diff --git a/tests/index/add.c b/tests/index/add.c index cfa81c4d9..f101ea266 100644 --- a/tests/index/add.c +++ b/tests/index/add.c @@ -20,7 +20,7 @@ void test_index_add__cleanup(void) cl_git_sandbox_cleanup(); g_repo = NULL; - cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0)); + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1)); } static void test_add_entry( @@ -42,7 +42,7 @@ static void test_add_entry( void test_index_add__invalid_entries_succeeds_by_default(void) { /* - * Ensure that there is no validation on ids by default + * Ensure that there is validation on object ids by default */ /* ensure that we can add some actually good entries */ @@ -50,27 +50,6 @@ void test_index_add__invalid_entries_succeeds_by_default(void) test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB_EXECUTABLE); test_add_entry(true, valid_blob_id, GIT_FILEMODE_LINK); - /* test that we fail to add some invalid (missing) blobs and trees */ - test_add_entry(true, invalid_id, GIT_FILEMODE_BLOB); - test_add_entry(true, invalid_id, GIT_FILEMODE_BLOB_EXECUTABLE); - test_add_entry(true, invalid_id, GIT_FILEMODE_LINK); - - /* test that we validate the types of objects */ - test_add_entry(true, valid_commit_id, GIT_FILEMODE_BLOB); - test_add_entry(true, valid_tree_id, GIT_FILEMODE_BLOB_EXECUTABLE); - test_add_entry(true, valid_commit_id, GIT_FILEMODE_LINK); - - /* - * Ensure that strict object references will fail the `index_add` - */ - - cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1)); - - /* ensure that we can add some actually good entries */ - test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB); - test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB_EXECUTABLE); - test_add_entry(true, valid_blob_id, GIT_FILEMODE_LINK); - /* test that we fail to add some invalid (missing) blobs and trees */ test_add_entry(false, invalid_id, GIT_FILEMODE_BLOB); test_add_entry(false, invalid_id, GIT_FILEMODE_BLOB_EXECUTABLE); @@ -80,5 +59,26 @@ void test_index_add__invalid_entries_succeeds_by_default(void) test_add_entry(false, valid_commit_id, GIT_FILEMODE_BLOB); test_add_entry(false, valid_tree_id, GIT_FILEMODE_BLOB_EXECUTABLE); test_add_entry(false, valid_commit_id, GIT_FILEMODE_LINK); + + /* + * Ensure that there we can disable validation + */ + + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0)); + + /* ensure that we can add some actually good entries */ + test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB); + test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB_EXECUTABLE); + test_add_entry(true, valid_blob_id, GIT_FILEMODE_LINK); + + /* test that we can now add some invalid (missing) blobs and trees */ + test_add_entry(true, invalid_id, GIT_FILEMODE_BLOB); + test_add_entry(true, invalid_id, GIT_FILEMODE_BLOB_EXECUTABLE); + test_add_entry(true, invalid_id, GIT_FILEMODE_LINK); + + /* test that we do not validate the types of objects */ + test_add_entry(true, valid_commit_id, GIT_FILEMODE_BLOB); + test_add_entry(true, valid_tree_id, GIT_FILEMODE_BLOB_EXECUTABLE); + test_add_entry(true, valid_commit_id, GIT_FILEMODE_LINK); } diff --git a/tests/object/tree/write.c b/tests/object/tree/write.c index f779b8ce6..341f5db72 100644 --- a/tests/object/tree/write.c +++ b/tests/object/tree/write.c @@ -19,7 +19,7 @@ void test_object_tree_write__cleanup(void) { cl_git_sandbox_cleanup(); - cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0)); + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1)); } void test_object_tree_write__from_memory(void) @@ -492,11 +492,11 @@ static void test_invalid_objects(bool should_allow_invalid) void test_object_tree_write__object_validity(void) { - /* Ensure that we can add invalid objects by default */ - test_invalid_objects(true); - - /* Ensure that we can turn on validation */ - cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1)); + /* Ensure that we cannot add invalid objects by default */ test_invalid_objects(false); + + /* Ensure that we can turn off validation */ + cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0)); + test_invalid_objects(true); } From a4ea7faaad52dd1be7ba6daaccd789bed3f01d61 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 1 Mar 2016 08:54:00 +0100 Subject: [PATCH 440/450] xdiff: fix memleak on error case Commit 3d1abc5afce fixes a memory leak in the xdiff code. In the process of upstreaming the fix it was pointed out by Johannes Schindelin that there is another memory leak present (see [1]). Fix the second memory leak by applying the upstream fix to our code base. [1]: http://thread.gmane.org/gmane.comp.version-control.git/287034 --- src/xdiff/xmerge.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/xdiff/xmerge.c b/src/xdiff/xmerge.c index 7928d1418..6448b5542 100644 --- a/src/xdiff/xmerge.c +++ b/src/xdiff/xmerge.c @@ -633,8 +633,11 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2, result->ptr = NULL; result->size = 0; - if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0 || - xdl_do_diff(orig, mf2, xpp, &xe2) < 0) { + if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0) { + return -1; + } + if (xdl_do_diff(orig, mf2, xpp, &xe2) < 0) { + xdl_free_env(&xe1); return -1; } if (xdl_change_compact(&xe1.xdf1, &xe1.xdf2, xpp->flags) < 0 || From 9b3fc895b724bbdd5a698c8e7dc9ae6551f0a83d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 3 Mar 2016 11:17:36 +0100 Subject: [PATCH 441/450] tests: plug a leak --- tests/refs/create.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/refs/create.c b/tests/refs/create.c index a0bc78014..b96d0c90a 100644 --- a/tests/refs/create.c +++ b/tests/refs/create.c @@ -133,6 +133,7 @@ void test_refs_create__oid_unknown_succeeds_by_default(void) /* Create and write the new object id reference */ cl_git_pass(git_reference_create(&new_reference, g_repo, new_head, &id, 0, NULL)); + git_reference_free(new_reference); /* Ensure the reference can't be looked-up... */ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head)); From 25205737781ec0d73e57283f43e5d4fdf355a0dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 3 Mar 2016 10:08:00 +0100 Subject: [PATCH 442/450] test: make sure we retry the auth callback on all platforms We were missing this test on Windows, which meant we didn't notice that we never fixed the single authentication attempt it tries, nor its wrong return code. Enable this for the unix platforms as well over HTTP. We previously were doing it locally but disabled it on OS X due to issues with its sshd not accepting password authentication. --- appveyor.yml | 6 +++++- script/cibuild.sh | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 166fa56b1..7186aaf63 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -36,4 +36,8 @@ build_script: - cmd: | if "%GENERATOR%"=="MSYS Makefiles" (C:\MinGW\msys\1.0\bin\sh --login /c/projects/libgit2/script/appveyor-mingw.sh) test_script: -- ps: ctest -V . +- ps: | + ctest -V . + $env:GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent" + $env:GITTEST_REMOTE_USER="libgit2test" + .\Debug\libgit2_clar -sonline::clone::cred_callback diff --git a/script/cibuild.sh b/script/cibuild.sh index de5df9ea8..029dcd413 100755 --- a/script/cibuild.sh +++ b/script/cibuild.sh @@ -56,3 +56,7 @@ if [ -e ./libgit2_clar ]; then ./libgit2_clar -sonline::clone::cred_callback || exit $? fi fi + +export GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent" +export GITTEST_REMOTE_USER="libgit2test" +./libgit2_clar -sonline::clone::cred_callback || exit $? From a4cba9d45384eec145537438a8e2d765c3a0bf11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 3 Mar 2016 10:48:24 +0100 Subject: [PATCH 443/450] winhttp: retry authentication If the caller has provided bad authentication, give them another apportunity to get it right until they give up. This brings WinHTTP in line with the other transports. --- src/transports/winhttp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index ded041686..32b838084 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -926,10 +926,11 @@ replay: if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0) return -1; - if (allowed_types && - (!t->cred || 0 == (t->cred->credtype & allowed_types))) { + if (allowed_types) { int cred_error = 1; + git_cred_free(t->cred); + t->cred = NULL; /* Start with the user-supplied credential callback, if present */ if (t->owner->cred_acquire_cb) { cred_error = t->owner->cred_acquire_cb(&t->cred, t->owner->url, From ba9bb664f3ed3f230c474ddd8937bd072cc9947f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 3 Mar 2016 19:21:07 +0100 Subject: [PATCH 444/450] tests: create a ctest target for cred_callback --- CMakeLists.txt | 4 ++++ appveyor.yml | 4 ++-- script/cibuild.sh | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 931b06459..f4e56e6cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -682,6 +682,10 @@ IF (BUILD_CLAR) ELSE () ADD_TEST(libgit2_clar libgit2_clar -v) 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 libgit2_clar -v -sonline::clone::cred_callback) ENDIF () IF (TAGS) diff --git a/appveyor.yml b/appveyor.yml index 7186aaf63..3ed3c49a1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -37,7 +37,7 @@ build_script: if "%GENERATOR%"=="MSYS Makefiles" (C:\MinGW\msys\1.0\bin\sh --login /c/projects/libgit2/script/appveyor-mingw.sh) test_script: - ps: | - ctest -V . + ctest -V -R libgit2_clar $env:GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent" $env:GITTEST_REMOTE_USER="libgit2test" - .\Debug\libgit2_clar -sonline::clone::cred_callback + ctest -V -R libgit2_clar-cred_callback diff --git a/script/cibuild.sh b/script/cibuild.sh index 029dcd413..00cde0ada 100755 --- a/script/cibuild.sh +++ b/script/cibuild.sh @@ -25,7 +25,7 @@ git daemon --listen=localhost --export-all --enable=receive-pack --base-path="$H export GITTEST_REMOTE_URL="git://localhost/test.git" # Run the test suite -ctest -V . || exit $? +ctest -V -R libgit2_clar || exit $? # Now that we've tested the raw git protocol, let's set up ssh to we # can do the push tests over it @@ -59,4 +59,4 @@ fi export GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent" export GITTEST_REMOTE_USER="libgit2test" -./libgit2_clar -sonline::clone::cred_callback || exit $? +ctest -V -R libgit2_clar-cred_callback From a7ef27af1c04e1c0a2a1fbc2c161875204bba86e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 3 Mar 2016 20:17:13 +0100 Subject: [PATCH 445/450] CHANGELOG: add note about WinHTTP cred handling --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c50f1211..1338909d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,9 @@ v0.23 + 1 exist and are of the correct type. This object validation can be disabled with the GIT_OPT_ENABLE_STRICT_OBJECT_CREATION option. +* The WinHTTP transport's handling of bad credentials now behaves like + the others, asking for credentials again. + ### API additions * `git_config_lock()` has been added, which allow for From c21c8f67e88d108d702677e186e797365fc0035d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 3 Mar 2016 20:18:55 +0100 Subject: [PATCH 446/450] CHANGELOG: prepre tamplate for release --- CHANGELOG.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1338909d9..43476b99a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,15 @@ -v0.23 + 1 +v0.24 + 1 +------- + +### Changes or improvements + +### API additions + +### API removals + +### Breaking API changes + +v0.24 ------- ### Changes or improvements @@ -65,6 +76,8 @@ v0.23 + 1 ### API removals +* No APIs were removed in this version. + ### Breaking API changes * The `git_merge_tree_flag_t` is now `git_merge_flag_t`. Subsequently, From 1e8255a39bd50c92add367d01519a76b0d782683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 3 Mar 2016 20:20:43 +0100 Subject: [PATCH 447/450] Bump version to 0.24.0 --- include/git2/version.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/git2/version.h b/include/git2/version.h index 6837e90ef..66a6623cd 100644 --- a/include/git2/version.h +++ b/include/git2/version.h @@ -7,12 +7,12 @@ #ifndef INCLUDE_git_version_h__ #define INCLUDE_git_version_h__ -#define LIBGIT2_VERSION "0.23.0" +#define LIBGIT2_VERSION "0.24.0" #define LIBGIT2_VER_MAJOR 0 -#define LIBGIT2_VER_MINOR 23 +#define LIBGIT2_VER_MINOR 24 #define LIBGIT2_VER_REVISION 0 #define LIBGIT2_VER_PATCH 0 -#define LIBGIT2_SOVERSION 23 +#define LIBGIT2_SOVERSION 24 #endif From e23efa6ddfedf54ef219e15f47da502a50f7b364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 3 Mar 2016 21:03:10 +0100 Subject: [PATCH 448/450] tests: take the version from our define --- tests/diff/format_email.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/diff/format_email.c b/tests/diff/format_email.c index 8a0128898..e55afe958 100644 --- a/tests/diff/format_email.c +++ b/tests/diff/format_email.c @@ -124,7 +124,7 @@ void test_diff_format_email__with_message(void) " file3\n" \ "+file3\n" \ "--\n" \ - "libgit2 0.23.0\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ "\n"; opts.body = "Modify content of file3.txt by appending a new line. Make this\n" \ From 22f3d3aa6b2500a0c587938f7939c05a28afacf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 3 Mar 2016 22:26:31 +0100 Subject: [PATCH 449/450] ssh: initialize libssh2 We should have been doing this, but it initializes itself upon first use, which works as long as nobody's doing concurrent network operations. Initialize it on our init to make sure it's not getting initialized concurrently. --- src/global.c | 4 +++- src/transports/ssh.c | 16 ++++++++++++++++ src/transports/ssh.h | 12 ++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/transports/ssh.h diff --git a/src/global.c b/src/global.c index c65b21a11..0bfde1e04 100644 --- a/src/global.c +++ b/src/global.c @@ -12,6 +12,7 @@ #include "openssl_stream.h" #include "thread-utils.h" #include "git2/global.h" +#include "transports/ssh.h" #if defined(GIT_MSVC_CRTDBG) #include "win32/w32_stack.h" @@ -56,7 +57,8 @@ static int init_common(void) /* Initialize any other subsystems that have global state */ if ((ret = git_hash_global_init()) == 0 && (ret = git_sysdir_global_init()) == 0 && - (ret = git_filter_global_init()) == 0) + (ret = git_filter_global_init()) == 0 && + (ret = git_transport_ssh_global_init()) == 0) ret = git_openssl_stream_global_init(); GIT_MEMORY_BARRIER; diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 239e0bae7..35739abe3 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -15,6 +15,7 @@ #include "smart.h" #include "cred.h" #include "socket_stream.h" +#include "ssh.h" #ifdef GIT_SSH @@ -876,3 +877,18 @@ int git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *p return -1; #endif } + +int git_transport_ssh_global_init(void) +{ +#ifdef GIT_SSH + + libssh2_init(0); + return 0; + +#else + + /* Nothing to initialize */ + return 0; + +#endif +} diff --git a/src/transports/ssh.h b/src/transports/ssh.h new file mode 100644 index 000000000..2db2cc5df --- /dev/null +++ b/src/transports/ssh.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_ssh_h__ +#define INCLUDE_ssh_h__ + +int git_transport_ssh_global_init(void); + +#endif From ea5bf6bbcead5a9ba24a38c4da62ee87059c5c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 4 Mar 2016 12:34:38 +0100 Subject: [PATCH 450/450] treebuilder: don't try to verify submodules exist in the odb Submodules don't exist in the objectdb and the code is making us try to look for a blob with its commit id, which is obviously not going to work. Skip the test if the user wants to insert a submodule. --- src/tree.c | 3 ++- tests/object/tree/write.c | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/tree.c b/src/tree.c index 2c3151546..48b9f121d 100644 --- a/src/tree.c +++ b/src/tree.c @@ -757,7 +757,8 @@ int git_treebuilder_insert( if (!valid_entry_name(bld->repo, filename)) return tree_error("Failed to insert entry. Invalid name for a tree entry", filename); - if (!git_object__is_valid(bld->repo, id, otype_from_mode(filemode))) + if (filemode != GIT_FILEMODE_COMMIT && + !git_object__is_valid(bld->repo, id, otype_from_mode(filemode))) return tree_error("Failed to insert entry; invalid object specified", filename); pos = git_strmap_lookup_index(bld->map, filename); diff --git a/tests/object/tree/write.c b/tests/object/tree/write.c index 341f5db72..a9decf9c1 100644 --- a/tests/object/tree/write.c +++ b/tests/object/tree/write.c @@ -490,13 +490,25 @@ static void test_invalid_objects(bool should_allow_invalid) git_treebuilder_free(builder); } +static void test_inserting_submodule(void) +{ + git_treebuilder *bld; + git_oid sm_id; + + cl_git_pass(git_treebuilder_new(&bld, g_repo, NULL)); + cl_git_pass(git_treebuilder_insert(NULL, bld, "sm", &sm_id, GIT_FILEMODE_COMMIT)); + git_treebuilder_free(bld); +} + void test_object_tree_write__object_validity(void) { /* Ensure that we cannot add invalid objects by default */ test_invalid_objects(false); + test_inserting_submodule(); /* Ensure that we can turn off validation */ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0)); test_invalid_objects(true); + test_inserting_submodule(); }