diff --git a/src/config_cache.c b/src/config_cache.c index dca9976f89e..45c39ce1795 100644 --- a/src/config_cache.c +++ b/src/config_cache.c @@ -51,6 +51,12 @@ static git_cvar_map _cvar_map_autocrlf[] = { {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT} }; +static git_cvar_map _cvar_map_safecrlf[] = { + {GIT_CVAR_FALSE, NULL, GIT_SAFE_CRLF_FALSE}, + {GIT_CVAR_TRUE, NULL, GIT_SAFE_CRLF_FAIL}, + {GIT_CVAR_STRING, "warn", GIT_SAFE_CRLF_WARN} +}; + /* * Generic map for integer values */ @@ -68,7 +74,7 @@ static struct map_data _cvar_maps[] = { {"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT }, {"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_DEFAULT }, {"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT }, - {"core.safecrlf", NULL, 0, GIT_SAFE_CRLF_DEFAULT}, + {"core.safecrlf", _cvar_map_safecrlf, ARRAY_SIZE(_cvar_map_safecrlf), GIT_SAFE_CRLF_DEFAULT}, {"core.logallrefupdates", NULL, 0, GIT_LOGALLREFUPDATES_DEFAULT }, }; diff --git a/src/diff_print.c b/src/diff_print.c index 26753515b1f..bb925ef985e 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -286,50 +286,46 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new) { git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT, *out = NULL; const void *old_data, *new_data; - git_off_t off_t_old_data_len, off_t_new_data_len; - unsigned long old_data_len, new_data_len, delta_data_len, inflated_len; - size_t remain; + git_off_t old_data_len, new_data_len; + unsigned long delta_data_len, inflated_len; const char *out_type = "literal"; - char *ptr; + char *scan, *end; int error; old_data = old ? git_blob_rawcontent(old) : NULL; new_data = new ? git_blob_rawcontent(new) : NULL; - off_t_old_data_len = old ? git_blob_rawsize(old) : 0; - off_t_new_data_len = new ? git_blob_rawsize(new) : 0; + old_data_len = old ? git_blob_rawsize(old) : 0; + new_data_len = new ? git_blob_rawsize(new) : 0; /* The git_delta function accepts unsigned long only */ - if (off_t_old_data_len > ULONG_MAX || off_t_new_data_len > ULONG_MAX) { - error = -1; - goto done; - } - - old_data_len = (unsigned long)off_t_old_data_len; - new_data_len = (unsigned long)off_t_new_data_len; + if (!git__is_ulong(old_data_len) || !git__is_ulong(new_data_len)) + return GIT_EBUFS; out = &deflate; - inflated_len = new_data_len; + inflated_len = (unsigned long)new_data_len; if ((error = git_zstream_deflatebuf( - &deflate, new_data, new_data_len)) < 0) + out, new_data, (size_t)new_data_len)) < 0) goto done; /* The git_delta function accepts unsigned long only */ - if (deflate.size > ULONG_MAX) { - error = -1; + if (!git__is_ulong((git_off_t)deflate.size)) { + error = GIT_EBUFS; goto done; } if (old && new) { - void *delta_data; - - delta_data = git_delta(old_data, old_data_len, new_data, - new_data_len, &delta_data_len, (unsigned long)deflate.size); + void *delta_data = git_delta( + old_data, (unsigned long)old_data_len, + new_data, (unsigned long)new_data_len, + &delta_data_len, (unsigned long)deflate.size); if (delta_data) { - error = git_zstream_deflatebuf(&delta, delta_data, delta_data_len); - free(delta_data); + error = git_zstream_deflatebuf( + &delta, delta_data, (size_t)delta_data_len); + + git__free(delta_data); if (error < 0) goto done; @@ -345,15 +341,17 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new) git_buf_printf(pi->buf, "%s %lu\n", out_type, inflated_len); pi->line.num_lines++; - for (ptr = out->ptr, remain = out->size; remain > 0; ) { - size_t chunk_len = (52 < remain) ? 52 : remain; + for (scan = out->ptr, end = out->ptr + out->size; scan < end; ) { + size_t chunk_len = end - scan; + if (chunk_len > 52) + chunk_len = 52; if (chunk_len <= 26) git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1); else git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1); - git_buf_put_base85(pi->buf, ptr, chunk_len); + git_buf_put_base85(pi->buf, scan, chunk_len); git_buf_putc(pi->buf, '\n'); if (git_buf_oom(pi->buf)) { @@ -361,8 +359,7 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new) goto done; } - ptr += chunk_len; - remain -= chunk_len; + scan += chunk_len; pi->line.num_lines++; } @@ -381,26 +378,33 @@ static int diff_print_patch_file_binary( git_blob *old = NULL, *new = NULL; const git_oid *old_id, *new_id; int error; + size_t pre_binary_size; - if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0) { - pi->line.num_lines = 1; - return diff_delta_format_with_paths( - pi->buf, delta, oldpfx, newpfx, - "Binary files %s%s and %s%s differ\n"); - } + if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0) + goto noshow; + pre_binary_size = pi->buf->size; git_buf_printf(pi->buf, "GIT binary patch\n"); pi->line.num_lines++; old_id = (delta->status != GIT_DELTA_ADDED) ? &delta->old_file.id : NULL; new_id = (delta->status != GIT_DELTA_DELETED) ? &delta->new_file.id : NULL; - if ((old_id && (error = git_blob_lookup(&old, pi->diff->repo, old_id)) < 0) || - (new_id && (error = git_blob_lookup(&new, pi->diff->repo,new_id)) < 0) || - (error = print_binary_hunk(pi, old, new)) < 0 || + if (old_id && (error = git_blob_lookup(&old, pi->diff->repo, old_id)) < 0) + goto done; + if (new_id && (error = git_blob_lookup(&new, pi->diff->repo,new_id)) < 0) + goto done; + + if ((error = print_binary_hunk(pi, old, new)) < 0 || (error = git_buf_putc(pi->buf, '\n')) < 0 || (error = print_binary_hunk(pi, new, old)) < 0) - goto done; + { + if (error == GIT_EBUFS) { + giterr_clear(); + git_buf_truncate(pi->buf, pre_binary_size); + goto noshow; + } + } pi->line.num_lines++; @@ -409,6 +413,12 @@ static int diff_print_patch_file_binary( git_blob_free(new); return error; + +noshow: + pi->line.num_lines = 1; + return diff_delta_format_with_paths( + pi->buf, delta, oldpfx, newpfx, + "Binary files %s%s and %s%s differ\n"); } static int diff_print_patch_file( diff --git a/src/remote.c b/src/remote.c index f2e2e2f7aa6..b56bf3b24c8 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1809,24 +1809,50 @@ static int remove_branch_config_related_entries( return error; } -static int remove_refs(git_repository *repo, const char *glob) +static int remove_refs(git_repository *repo, const git_refspec *spec) { - git_reference_iterator *iter; + git_reference_iterator *iter = NULL; + git_vector refs; const char *name; + char *dup; int error; + size_t i; - if ((error = git_reference_iterator_glob_new(&iter, repo, glob)) < 0) + if ((error = git_vector_init(&refs, 8, NULL)) < 0) return error; + if ((error = git_reference_iterator_new(&iter, repo)) < 0) + goto cleanup; + while ((error = git_reference_next_name(&name, iter)) == 0) { - if ((error = git_reference_remove(repo, name)) < 0) - break; - } - git_reference_iterator_free(iter); + if (!git_refspec_dst_matches(spec, name)) + continue; + + dup = git__strdup(name); + if (!dup) { + error = -1; + goto cleanup; + } + if ((error = git_vector_insert(&refs, dup)) < 0) + goto cleanup; + } if (error == GIT_ITEROVER) error = 0; + if (error < 0) + goto cleanup; + + git_vector_foreach(&refs, i, name) { + if ((error = git_reference_remove(repo, name)) < 0) + break; + } +cleanup: + git_reference_iterator_free(iter); + git_vector_foreach(&refs, i, dup) { + git__free(dup); + } + git_vector_free(&refs); return error; } @@ -1848,7 +1874,7 @@ static int remove_remote_tracking(git_repository *repo, const char *remote_name) if (refspec == NULL) continue; - if ((error = remove_refs(repo, git_refspec_dst(refspec))) < 0) + if ((error = remove_refs(repo, refspec)) < 0) break; } diff --git a/src/util.h b/src/util.h index 6fb2dc0f42a..ca676c03902 100644 --- a/src/util.h +++ b/src/util.h @@ -133,6 +133,13 @@ GIT_INLINE(int) git__is_uint32(size_t p) return p == (size_t)r; } +/** @return true if p fits into the range of an unsigned long */ +GIT_INLINE(int) git__is_ulong(git_off_t p) +{ + unsigned long r = (unsigned long)p; + return p == (git_off_t)r; +} + /* 32-bit cross-platform rotl */ #ifdef _MSC_VER /* use built-in method in MSVC */ # define git__rotl(v, s) (uint32_t)_rotl(v, s) diff --git a/tests/filter/crlf.c b/tests/filter/crlf.c index 334b1e3493a..a31dac9651f 100644 --- a/tests/filter/crlf.c +++ b/tests/filter/crlf.c @@ -196,3 +196,44 @@ void test_filter_crlf__no_safecrlf(void) git_buf_free(&out); } +void test_filter_crlf__safecrlf_warn(void) +{ + git_filter_list *fl; + git_filter *crlf; + git_buf in = {0}, out = GIT_BUF_INIT; + + cl_repo_set_string(g_repo, "core.safecrlf", "warn"); + + cl_git_pass(git_filter_list_new( + &fl, g_repo, GIT_FILTER_TO_ODB, 0)); + + crlf = git_filter_lookup(GIT_FILTER_CRLF); + cl_assert(crlf != NULL); + + cl_git_pass(git_filter_list_push(fl, crlf, NULL)); + + /* Normalized \r\n succeeds with safecrlf=warn */ + in.ptr = "Normal\r\nCRLF\r\nline-endings.\r\n"; + in.size = strlen(in.ptr); + + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr); + + /* Mix of line endings succeeds with safecrlf=warn */ + in.ptr = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n"; + in.size = strlen(in.ptr); + + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + /* TODO: check for warning */ + cl_assert_equal_s("Mixed\nup\nLF\nand\nCRLF\nline-endings.\n", out.ptr); + + /* Normalized \n is reversible, so does not fail with safecrlf=warn */ + in.ptr = "Normal\nLF\nonly\nline-endings.\n"; + in.size = strlen(in.ptr); + + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + cl_assert_equal_s(in.ptr, out.ptr); + + git_filter_list_free(fl); + git_buf_free(&out); +}