diff options
44 files changed, 1115 insertions, 518 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6e342d116..74bab53f3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,8 +22,7 @@ jobs: # with both the sha and "latest" so that the subsequent runs need not # know the sha. Only do this on CI builds (when the event is a "push") # because PR builds from forks lack permission to write packages. - build_containers: - name: Create docker image + containers: strategy: matrix: container: @@ -46,6 +45,7 @@ jobs: - name: centos7 - name: centos8 runs-on: ubuntu-latest + name: "Create container: ${{ matrix.container.name }}" steps: - name: Check out repository uses: actions/checkout@v2 @@ -80,12 +80,11 @@ jobs: # and their details. Then we build either in a docker container (Linux) # or on the actual hosts (macOS, Windows). build: - name: Build - needs: [ build_containers ] + needs: [ containers ] strategy: matrix: platform: - - # Xenial, GCC, OpenSSL + - name: "Linux (Xenial, GCC, OpenSSL)" container: name: xenial env: @@ -93,7 +92,7 @@ jobs: CMAKE_GENERATOR: Ninja CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON -DDEBUG_STRICT_ALLOC=ON -DDEBUG_STRICT_OPEN=ON os: ubuntu-latest - - # Xenial, GCC, mbedTLS + - name: Linux (Xenial, GCC, mbedTLS) container: name: xenial env: @@ -101,7 +100,7 @@ jobs: CMAKE_GENERATOR: Ninja CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON os: ubuntu-latest - - # Xenial, Clang, OpenSSL + - name: "Linux (Xenial, Clang, OpenSSL)" container: name: xenial env: @@ -109,7 +108,7 @@ jobs: CMAKE_GENERATOR: Ninja CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON os: ubuntu-latest - - # Xenial, Clang, mbedTLS + - name: "Linux (Xenial, Clang, mbedTLS)" container: name: xenial env: @@ -117,7 +116,7 @@ jobs: CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON CMAKE_GENERATOR: Ninja os: ubuntu-latest - - # Focal, Clang 10, mbedTLS, MemorySanitizer + - name: "Linux (MemorySanitizer)" container: name: focal env: @@ -130,7 +129,7 @@ jobs: ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10 UBSAN_OPTIONS: print_stacktrace=1 os: ubuntu-latest - - # Focal, Clang 10, OpenSSL, UndefinedBehaviorSanitizer + - name: "Linux (UndefinedBehaviorSanitizer)" container: name: focal env: @@ -143,7 +142,7 @@ jobs: ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10 UBSAN_OPTIONS: print_stacktrace=1 os: ubuntu-latest - - # Focal, Clang 10, OpenSSL, ThreadSanitizer + - name: "Linux (ThreadSanitizer)" container: name: focal env: @@ -157,7 +156,7 @@ jobs: UBSAN_OPTIONS: print_stacktrace=1 TSAN_OPTIONS: suppressions=/home/libgit2/source/script/thread-sanitizer.supp second_deadlock_stack=1 os: ubuntu-latest - - # macOS + - name: "macOS" os: macos-10.15 env: CC: clang @@ -166,7 +165,7 @@ jobs: SKIP_SSH_TESTS: true SKIP_NEGOTIATE_TESTS: true setup-script: osx - - # Windows amd64 Visual Studio + - name: "Windows (amd64, Visual Studio)" os: windows-2019 env: ARCH: amd64 @@ -174,7 +173,7 @@ jobs: CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON SKIP_SSH_TESTS: true SKIP_NEGOTIATE_TESTS: true - - # Windows x86 Visual Studio + - name: "Windows (x86, Visual Studio)" os: windows-2019 env: ARCH: x86 @@ -182,7 +181,7 @@ jobs: CMAKE_OPTIONS: -A Win32 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON SKIP_SSH_TESTS: true SKIP_NEGOTIATE_TESTS: true - - # Windows amd64 mingw + - name: "Windows (amd64, mingw)" os: windows-2019 setup-script: mingw env: @@ -193,7 +192,7 @@ jobs: BUILD_PATH: D:\Temp\mingw64\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin SKIP_SSH_TESTS: true SKIP_NEGOTIATE_TESTS: true - - # Windows x86 mingw + - name: "Windows (x86, mingw)" os: windows-2019 setup-script: mingw env: @@ -207,6 +206,7 @@ jobs: fail-fast: false env: ${{ matrix.platform.env }} runs-on: ${{ matrix.platform.os }} + name: "Build: ${{ matrix.platform.name }}" steps: - name: Check out repository uses: actions/checkout@v2 @@ -269,7 +269,7 @@ jobs: # published to our documentation site. documentation: name: Generate documentation - needs: [build_containers] + needs: [ containers ] runs-on: ubuntu-latest steps: - name: Check out repository diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 9cb5525eb..5513d5b43 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -15,11 +15,10 @@ jobs: # targets and their details. Then we build either in a docker container # (Linux) or on the actual hosts (macOS, Windows). build: - name: Build strategy: matrix: platform: - - # Xenial, GCC, OpenSSL + - name: Linux (Xenial, GCC, OpenSSL) container: name: xenial env: @@ -27,7 +26,7 @@ jobs: CMAKE_GENERATOR: Ninja CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON os: ubuntu-latest - - # Xenial, GCC, mbedTLS + - name: "Linux (Xenial, GCC, mbedTLS)" container: name: xenial env: @@ -35,7 +34,7 @@ jobs: CMAKE_GENERATOR: Ninja CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON os: ubuntu-latest - - # Xenial, Clang, OpenSSL + - name: "Linux (Xenial, Clang, OpenSSL)" container: name: xenial env: @@ -43,7 +42,7 @@ jobs: CMAKE_GENERATOR: Ninja CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON os: ubuntu-latest - - # Xenial, Clang, mbedTLS + - name: "Linux (Xenial, Clang, mbedTLS)" container: name: xenial env: @@ -51,7 +50,7 @@ jobs: CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON CMAKE_GENERATOR: Ninja os: ubuntu-latest - - # Xenial, GCC, thread-free + - name: "Linux (no threads)" container: name: xenial env: @@ -59,7 +58,7 @@ jobs: CMAKE_OPTIONS: -DTHREADSAFE=OFF -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON CMAKE_GENERATOR: Ninja os: ubuntu-latest - - # Xenial, Clang, OpenSSL (dynamically loaded) + - name: "Linux (dynamically-loaded OpenSSL)" container: name: xenial env: @@ -67,7 +66,7 @@ jobs: CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON CMAKE_GENERATOR: Ninja os: ubuntu-latest - - # Focal, Clang 10, mbedTLS, MemorySanitizer + - name: "Linux (MemorySanitizer)" container: name: focal env: @@ -80,7 +79,7 @@ jobs: ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10 UBSAN_OPTIONS: print_stacktrace=1 os: ubuntu-latest - - # Focal, Clang 10, OpenSSL, UndefinedBehaviorSanitizer + - name: "Linux (UndefinedBehaviorSanitizer)" container: name: focal env: @@ -92,7 +91,7 @@ jobs: SKIP_NEGOTIATE_TESTS: true ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10 os: ubuntu-latest - - # Focal, Clang 10, OpenSSL, ThreadSanitizer + - name: "Linux (ThreadSanitizer)" container: name: focal env: @@ -105,7 +104,7 @@ jobs: ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10 TSAN_OPTIONS: suppressions=/home/libgit2/source/script/thread-sanitizer.supp second_deadlock_stack=1 os: ubuntu-latest - - # Focal, Clang 10, mmap emulation (NO_MMAP) + - name: "Linux (no mmap)" container: name: focal env: @@ -116,7 +115,7 @@ jobs: SKIP_SSH_TESTS: true SKIP_NEGOTIATE_TESTS: true os: ubuntu-latest - - # CentOS 7 + - name: "Linux (CentOS 7)" container: name: centos7 env: @@ -124,7 +123,7 @@ jobs: PKG_CONFIG_PATH: /usr/local/lib/pkgconfig SKIP_NEGOTIATE_TESTS: true os: ubuntu-latest - - # CentOS 7, OpenSSL (dynamically loaded) + - name: "Linux (CentOS 7, dynamically-loaded OpenSSL)" container: name: centos7 env: @@ -132,7 +131,7 @@ jobs: PKG_CONFIG_PATH: /usr/local/lib/pkgconfig SKIP_NEGOTIATE_TESTS: true os: ubuntu-latest - - # CentOS 8 + - name: "Linux (CentOS 8)" container: name: centos8 env: @@ -141,7 +140,7 @@ jobs: SKIP_NEGOTIATE_TESTS: true SKIP_SSH_TESTS: true os: ubuntu-latest - - # CentOS 8, OpenSSL (dynamically loaded) + - name: "Linux (CentOS 8, dynamically-loaded OpenSSL)" container: name: centos8 env: @@ -150,7 +149,7 @@ jobs: SKIP_NEGOTIATE_TESTS: true SKIP_SSH_TESTS: true os: ubuntu-latest - - # macOS + - name: "macOS" os: macos-10.15 env: CC: clang @@ -159,7 +158,7 @@ jobs: SKIP_SSH_TESTS: true SKIP_NEGOTIATE_TESTS: true setup-script: osx - - # Windows amd64 Visual Studio + - name: "Windows (amd64, Visual Studio)" os: windows-2019 env: ARCH: amd64 @@ -167,7 +166,7 @@ jobs: CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON SKIP_SSH_TESTS: true SKIP_NEGOTIATE_TESTS: true - - # Windows amd64 Visual Studio (NO_MMAP) + - name: "Windows (no mmap)" os: windows-2019 env: ARCH: amd64 @@ -176,7 +175,7 @@ jobs: CMAKE_OPTIONS: -A x64 -DDEPRECATE_HARD=ON SKIP_SSH_TESTS: true SKIP_NEGOTIATE_TESTS: true - - # Windows x86 Visual Studio + - name: "Windows (x86, Visual Studio)" os: windows-2019 env: ARCH: x86 @@ -184,7 +183,7 @@ jobs: CMAKE_OPTIONS: -A Win32 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON SKIP_SSH_TESTS: true SKIP_NEGOTIATE_TESTS: true - - # Windows amd64 mingw + - name: "Windows (amd64, mingw)" os: windows-2019 setup-script: mingw env: @@ -195,7 +194,7 @@ jobs: BUILD_PATH: D:\Temp\mingw64\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin SKIP_SSH_TESTS: true SKIP_NEGOTIATE_TESTS: true - - # Windows x86 mingw + - name: "Windows (x86, mingw)" os: windows-2019 setup-script: mingw env: @@ -206,7 +205,7 @@ jobs: BUILD_PATH: D:\Temp\mingw32\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin SKIP_SSH_TESTS: true SKIP_NEGOTIATE_TESTS: true - - # Bionic, GCC, OpenSSL (dynamically loaded) + - name: "Linux (Bionic, GCC, dynamically-loaded OpenSSL)" container: name: bionic dockerfile: bionic @@ -216,7 +215,7 @@ jobs: CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON RUN_INVASIVE_TESTS: true os: ubuntu-latest - - # Bionic, x86, Clang, OpenSSL + - name: "Linux (x86, Bionic, Clang, OpenSSL)" container: name: bionic-x86 dockerfile: bionic @@ -227,7 +226,7 @@ jobs: CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON RUN_INVASIVE_TESTS: true os: ubuntu-latest - - # Bionic, x86, GCC, OpenSSL + - name: "Linux (x86, Bionic, GCC, OpenSSL)" container: name: bionic-x86 dockerfile: bionic @@ -237,7 +236,7 @@ jobs: CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON RUN_INVASIVE_TESTS: true os: ubuntu-latest - - # Bionic, arm32, GCC, OpenSSL + - name: "Linux (arm32, Bionic, GCC, OpenSSL)" container: name: bionic-arm32 dockerfile: bionic @@ -249,7 +248,7 @@ jobs: RUN_INVASIVE_TESTS: true SKIP_PROXY_TESTS: true os: ubuntu-latest - - # Bionic, arm64, GCC, OpenSSL + - name: "Linux (arm64, Bionic, GCC, OpenSSL)" container: name: bionic-arm64 dockerfile: bionic @@ -262,6 +261,7 @@ jobs: SKIP_PROXY_TESTS: true os: ubuntu-latest fail-fast: false + name: "Build ${{ matrix.platform.name }}" env: ${{ matrix.platform.env }} runs-on: ${{ matrix.platform.os }} steps: @@ -301,6 +301,8 @@ compiler and linker. These flags are rarely used but can be useful for - `CMAKE_FIND_ROOT_PATH`: Override the search path for libraries - `ZLIB_LIBRARY`, `OPENSSL_SSL_LIBRARY` AND `OPENSSL_CRYPTO_LIBRARY`: Tell CMake where to find those specific libraries +- `LINK_WITH_STATIC_LIBRARIES`: Link only with static versions of +system libraries MacOS X ------- diff --git a/cmake/DefaultCFlags.cmake b/cmake/DefaultCFlags.cmake index fa59e1d97..a9c9ab972 100644 --- a/cmake/DefaultCFlags.cmake +++ b/cmake/DefaultCFlags.cmake @@ -92,7 +92,7 @@ else() set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -D <TARGET>") endif() - if(NOT BUILD_SHARED_LIBS) + if(NOT BUILD_SHARED_LIBS AND LINK_WITH_STATIC_LIBRARIES) set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") endif() diff --git a/docs/changelog.md b/docs/changelog.md index 8060874df..3723a080c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1963,3 +1963,8 @@ v0.22 functions. This is not something which we can know to do. A last-resort convenience function is provided in sys/openssl.h, `git_openssl_set_locking()` which can be used to set the locking. + +* `git_reference_*()` functions use mmap() + binary search for packed + refs lookups when using the fs backend. Previously all entries were + read into a hashtable, which could be slow for repositories with a + large number of refs. diff --git a/include/git2/checkout.h b/include/git2/checkout.h index f026d5bc2..9f834111a 100644 --- a/include/git2/checkout.h +++ b/include/git2/checkout.h @@ -182,7 +182,10 @@ typedef enum { * notifications; don't update the working directory or index. */ GIT_CHECKOUT_DRY_RUN = (1u << 24), - + + /** Include common ancestor data in zdiff3 format for conflicts */ + GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3 = (1u << 25), + /** * THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED */ diff --git a/include/git2/merge.h b/include/git2/merge.h index 3b3132f26..edd090cff 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -159,7 +159,10 @@ typedef enum { GIT_MERGE_FILE_DIFF_PATIENCE = (1 << 6), /** Take extra time to find minimal diff */ - GIT_MERGE_FILE_DIFF_MINIMAL = (1 << 7) + GIT_MERGE_FILE_DIFF_MINIMAL = (1 << 7), + + /** Create zdiff3 ("zealous diff3")-style files */ + GIT_MERGE_FILE_STYLE_ZDIFF3 = (1 << 8) } git_merge_file_flag_t; #define GIT_MERGE_CONFLICT_MARKER_SIZE 7 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 98881b5ad..e2da4bc90 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(git2internal OBJECT) set_target_properties(git2internal PROPERTIES C_STANDARD 90) +set_target_properties(git2internal PROPERTIES C_EXTENSIONS OFF) if(DEPRECATE_HARD) @@ -206,7 +207,17 @@ endif() # errors for the xdiff sources until we've sorted them out if(MSVC) set_source_files_properties(xdiff/xdiffi.c PROPERTIES COMPILE_FLAGS -WX-) + set_source_files_properties(xdiff/xemit.c PROPERTIES COMPILE_FLAGS -WX-) + set_source_files_properties(xdiff/xhistogram.c PROPERTIES COMPILE_FLAGS -WX-) + set_source_files_properties(xdiff/xmerge.c PROPERTIES COMPILE_FLAGS -WX-) set_source_files_properties(xdiff/xutils.c PROPERTIES COMPILE_FLAGS -WX-) + set_source_files_properties(xdiff/xpatience.c PROPERTIES COMPILE_FLAGS -WX-) +else() + set_source_files_properties(xdiff/xdiffi.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter") + set_source_files_properties(xdiff/xemit.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter") + set_source_files_properties(xdiff/xhistogram.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare") + set_source_files_properties(xdiff/xutils.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare") + set_source_files_properties(xdiff/xpatience.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare") endif() # Determine architecture of the machine diff --git a/src/blame_git.c b/src/blame_git.c index 3d514a1bc..2504b338a 100644 --- a/src/blame_git.c +++ b/src/blame_git.c @@ -393,7 +393,7 @@ static void fill_origin_blob(git_blame__origin *o, mmfile_t *file) memset(file, 0, sizeof(*file)); if (o->blob) { file->ptr = (char*)git_blob_rawcontent(o->blob); - file->size = (size_t)git_blob_rawsize(o->blob); + file->size = (long)git_blob_rawsize(o->blob); } } diff --git a/src/checkout.c b/src/checkout.c index bc3048cc8..6a4643196 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -2070,6 +2070,9 @@ static int checkout_write_merge( if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_DIFF3) opts.flags |= GIT_MERGE_FILE_STYLE_DIFF3; + if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3) + opts.flags |= GIT_MERGE_FILE_STYLE_ZDIFF3; + opts.ancestor_label = data->opts.ancestor_label ? data->opts.ancestor_label : "ancestor"; opts.our_label = data->opts.our_label ? @@ -2493,6 +2496,8 @@ static int checkout_data_init( data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_MERGE; else if (strcmp(conflict_style->value, "diff3") == 0) data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_DIFF3; + else if (strcmp(conflict_style->value, "zdiff3") == 0) + data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3; else { git_error_set(GIT_ERROR_CHECKOUT, "unknown style '%s' given for 'merge.conflictstyle'", conflict_style->value); diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c index 278e2be36..3f6eccac1 100644 --- a/src/diff_xdiff.c +++ b/src/diff_xdiff.c @@ -218,14 +218,9 @@ static int git_xdiff(git_patch_generated_output *output, git_patch_generated *pa * updates are needed to xo->params.flags */ - git_patch_generated_old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch); - git_patch_generated_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) { - git_error_set(GIT_ERROR_INVALID, "files too large for diff"); + if (git_patch_generated_old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch) < 0 || + git_patch_generated_new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch) < 0) return -1; - } xdl_diff(&info.xd_old_data, &info.xd_new_data, &xo->params, &xo->config, &xo->callback); @@ -261,5 +256,5 @@ void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts) if (flags & GIT_DIFF_IGNORE_BLANK_LINES) xo->params.flags |= XDF_IGNORE_BLANK_LINES; - xo->callback.outf = git_xdiff_cb; + xo->callback.out_line = git_xdiff_cb; } diff --git a/src/futils.c b/src/futils.c index 454ed79de..318e878f5 100644 --- a/src/futils.c +++ b/src/futils.c @@ -26,30 +26,32 @@ int git_futils_mkpath2file(const char *file_path, const mode_t mode) int git_futils_mktmp(git_str *path_out, const char *filename, mode_t mode) { + const int open_flags = O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC; + /* TMP_MAX is unrelated to mktemp but should provide a reasonable amount */ + unsigned int tries = TMP_MAX; int fd; - mode_t mask; - p_umask(mask = p_umask(0)); + while (tries--) { + git_str_sets(path_out, filename); + git_str_puts(path_out, "_git2_XXXXXX"); - git_str_sets(path_out, filename); - git_str_puts(path_out, "_git2_XXXXXX"); - - if (git_str_oom(path_out)) - return -1; + if (git_str_oom(path_out)) + return -1; - if ((fd = p_mkstemp(path_out->ptr)) < 0) { - git_error_set(GIT_ERROR_OS, - "failed to create temporary file '%s'", path_out->ptr); - return -1; - } + /* Using mktemp is safe when we open with O_CREAT | O_EXCL */ + p_mktemp(path_out->ptr); + /* mktemp sets template to empty string on failure */ + if (path_out->ptr[0] == '\0') + break; - if (p_chmod(path_out->ptr, (mode & ~mask))) { - git_error_set(GIT_ERROR_OS, - "failed to set permissions on file '%s'", path_out->ptr); - return -1; + if ((fd = p_open(path_out->ptr, open_flags, mode)) >= 0) + return fd; } - return fd; + git_error_set(GIT_ERROR_OS, + "failed to create temporary file '%s'", path_out->ptr); + git_str_dispose(path_out); + return -1; } int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode) diff --git a/src/futils.h b/src/futils.h index f0d7ec3b0..1827cbafe 100644 --- a/src/futils.h +++ b/src/futils.h @@ -173,8 +173,16 @@ typedef enum { extern int git_futils_rmdir_r(const char *path, const char *base, uint32_t flags); /** - * Create and open a temporary file with a `_git2_` suffix. - * Writes the filename into path_out. + * Create and open a temporary file with a `_git2_` suffix in a + * protected directory; the file created will created will honor + * the current `umask`. Writes the filename into path_out. + * + * This function is *NOT* suitable for use in temporary directories + * that are world writable. It uses `mktemp` (for portability) and + * many `mktemp` implementations use weak random characters. It + * should only be assumed to be suitable for atomically writing + * a new file in a directory that you control. + * * @return On success, an open file descriptor, else an error code < 0. */ extern int git_futils_mktmp(git_str *path_out, const char *filename, mode_t mode); diff --git a/src/merge.c b/src/merge.c index c871630c6..24650bbff 100644 --- a/src/merge.c +++ b/src/merge.c @@ -3126,7 +3126,7 @@ int git_merge__append_conflicts_to_merge_msg( (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_APPEND, GIT_MERGE_FILE_MODE)) < 0) goto cleanup; - git_filebuf_printf(&file, "\nConflicts:\n"); + git_filebuf_printf(&file, "\n#Conflicts:\n"); for (i = 0; i < git_index_entrycount(index); i++) { const git_index_entry *e = git_index_get_byindex(index, i); @@ -3135,7 +3135,7 @@ int git_merge__append_conflicts_to_merge_msg( continue; if (last == NULL || strcmp(e->path, last) != 0) - git_filebuf_printf(&file, "\t%s\n", e->path); + git_filebuf_printf(&file, "#\t%s\n", e->path); last = e->path; } diff --git a/src/merge_file.c b/src/merge_file.c index bfa3ec52e..732a047b1 100644 --- a/src/merge_file.c +++ b/src/merge_file.c @@ -86,22 +86,30 @@ static int merge_file__xdiff( memset(&xmparam, 0x0, sizeof(xmparam_t)); + if (ours->size > LONG_MAX || + theirs->size > LONG_MAX || + (ancestor && ancestor->size > LONG_MAX)) { + git_error_set(GIT_ERROR_MERGE, "failed to merge files"); + error = -1; + goto done; + } + if (ancestor) { xmparam.ancestor = (options.ancestor_label) ? options.ancestor_label : ancestor->path; ancestor_mmfile.ptr = (char *)ancestor->ptr; - ancestor_mmfile.size = ancestor->size; + ancestor_mmfile.size = (long)ancestor->size; } xmparam.file1 = (options.our_label) ? options.our_label : ours->path; our_mmfile.ptr = (char *)ours->ptr; - our_mmfile.size = ours->size; + our_mmfile.size = (long)ours->size; xmparam.file2 = (options.their_label) ? options.their_label : theirs->path; their_mmfile.ptr = (char *)theirs->ptr; - their_mmfile.size = theirs->size; + their_mmfile.size = (long)theirs->size; if (options.favor == GIT_MERGE_FILE_FAVOR_OURS) xmparam.favor = XDL_MERGE_FAVOR_OURS; @@ -115,6 +123,8 @@ static int merge_file__xdiff( if (options.flags & GIT_MERGE_FILE_STYLE_DIFF3) xmparam.style = XDL_MERGE_DIFF3; + if (options.flags & GIT_MERGE_FILE_STYLE_ZDIFF3) + xmparam.style = XDL_MERGE_ZEALOUS_DIFF3; if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE) xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE; diff --git a/src/patch_generate.c b/src/patch_generate.c index 6d115affe..bc598fea8 100644 --- a/src/patch_generate.c +++ b/src/patch_generate.c @@ -750,18 +750,34 @@ git_diff_driver *git_patch_generated_driver(git_patch_generated *patch) return patch->ofile.driver; } -void git_patch_generated_old_data( - char **ptr, size_t *len, git_patch_generated *patch) +int git_patch_generated_old_data( + char **ptr, long *len, git_patch_generated *patch) { + if (patch->ofile.map.len > LONG_MAX || + patch->ofile.map.len > GIT_XDIFF_MAX_SIZE) { + git_error_set(GIT_ERROR_INVALID, "files too large for diff"); + return -1; + } + *ptr = patch->ofile.map.data; - *len = patch->ofile.map.len; + *len = (long)patch->ofile.map.len; + + return 0; } -void git_patch_generated_new_data( - char **ptr, size_t *len, git_patch_generated *patch) +int git_patch_generated_new_data( + char **ptr, long *len, git_patch_generated *patch) { + if (patch->ofile.map.len > LONG_MAX || + patch->ofile.map.len > GIT_XDIFF_MAX_SIZE) { + git_error_set(GIT_ERROR_INVALID, "files too large for diff"); + return -1; + } + *ptr = patch->nfile.map.data; - *len = patch->nfile.map.len; + *len = (long)patch->nfile.map.len; + + return 0; } static int patch_generated_file_cb( diff --git a/src/patch_generate.h b/src/patch_generate.h index 0e09d27ca..56e3e9df4 100644 --- a/src/patch_generate.h +++ b/src/patch_generate.h @@ -39,10 +39,10 @@ typedef struct git_patch_generated git_patch_generated; extern git_diff_driver *git_patch_generated_driver(git_patch_generated *); -extern void git_patch_generated_old_data( - char **, size_t *, git_patch_generated *); -extern void git_patch_generated_new_data( - char **, size_t *, git_patch_generated *); +extern int git_patch_generated_old_data( + char **, long *, git_patch_generated *); +extern int git_patch_generated_new_data( + char **, long *, git_patch_generated *); extern int git_patch_generated_from_diff( git_patch **, git_diff *, size_t); diff --git a/src/posix.h b/src/posix.h index d98bc82ca..1757764a2 100644 --- a/src/posix.h +++ b/src/posix.h @@ -9,6 +9,7 @@ #include "common.h" +#include <stdlib.h> #include <fcntl.h> #include <time.h> @@ -130,6 +131,7 @@ extern ssize_t p_pwrite(int fd, const void *data, size_t size, off64_t offset); #define p_close(fd) close(fd) #define p_umask(m) umask(m) +#define p_mktemp(p) mktemp(p) extern int p_open(const char *path, int flags, ...); extern int p_creat(const char *path, mode_t mode); diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 055ca2559..95bda9404 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -65,9 +65,14 @@ typedef struct refdb_fs_backend { git_iterator_flag_t iterator_flags; uint32_t direach_flags; int fsync; + git_map packed_refs_map; + git_mutex prlock; /* protect packed_refs_map */ + git_futils_filestamp packed_refs_stamp; + bool sorted; } refdb_fs_backend; static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name); +static char *packed_set_peeling_mode(char *data, size_t data_sz, refdb_fs_backend *backend); GIT_INLINE(int) loose_path( git_str *out, @@ -134,31 +139,12 @@ static int packed_reload(refdb_fs_backend *backend) GIT_UNUSED(git_sortedcache_clear(backend->refcache, false)); - scan = (char *)packedrefs.ptr; + scan = packedrefs.ptr; eof = scan + packedrefs.size; - backend->peeling_mode = PEELING_NONE; - - if (*scan == '#') { - static const char *traits_header = "# pack-refs with: "; - - if (git__prefixcmp(scan, traits_header) == 0) { - scan += strlen(traits_header); - eol = strchr(scan, '\n'); - - if (!eol) - goto parse_failed; - *eol = '\0'; - - if (strstr(scan, " fully-peeled ") != NULL) { - backend->peeling_mode = PEELING_FULL; - } else if (strstr(scan, " peeled ") != NULL) { - backend->peeling_mode = PEELING_STANDARD; - } - - scan = eol + 1; - } - } + scan = packed_set_peeling_mode(scan, packedrefs.size, backend); + if (!scan) + goto parse_failed; while (scan < eof && *scan == '#') { if (!(eol = strchr(scan, '\n'))) @@ -466,10 +452,198 @@ static int ref_error_notfound(const char *name) return GIT_ENOTFOUND; } -static int packed_lookup( - git_reference **out, - refdb_fs_backend *backend, - const char *ref_name) +static char *packed_set_peeling_mode( + char *data, + size_t data_sz, + refdb_fs_backend *backend) +{ + static const char *traits_header = "# pack-refs with:"; + char *eol; + backend->peeling_mode = PEELING_NONE; + + if (git__prefixncmp(data, data_sz, traits_header) == 0) { + size_t hdr_sz = strlen(traits_header); + const char *sorted = " sorted "; + const char *peeled = " peeled "; + const char *fully_peeled = " fully-peeled "; + data += hdr_sz; + data_sz -= hdr_sz; + + eol = memchr(data, '\n', data_sz); + + if (!eol) + return NULL; + + if (git__memmem(data, eol - data, fully_peeled, strlen(fully_peeled))) + backend->peeling_mode = PEELING_FULL; + else if (git__memmem(data, eol - data, peeled, strlen(peeled))) + backend->peeling_mode = PEELING_STANDARD; + + backend->sorted = NULL != git__memmem(data, eol - data, sorted, strlen(sorted)); + + return eol + 1; + } + return data; +} + +static void packed_map_free(refdb_fs_backend *backend) +{ + if (backend->packed_refs_map.data) { +#ifdef GIT_WIN32 + git__free(backend->packed_refs_map.data); +#else + git_futils_mmap_free(&backend->packed_refs_map); +#endif + backend->packed_refs_map.data = NULL; + backend->packed_refs_map.len = 0; + git_futils_filestamp_set(&backend->packed_refs_stamp, NULL); + } +} + +static int packed_map_check(refdb_fs_backend *backend) +{ + int error = 0; + git_file fd = -1; + struct stat st; + + if ((error = git_mutex_lock(&backend->prlock)) < 0) + return error; + + if (backend->packed_refs_map.data && + !git_futils_filestamp_check( + &backend->packed_refs_stamp, backend->refcache->path)) { + git_mutex_unlock(&backend->prlock); + return error; + } + packed_map_free(backend); + + fd = git_futils_open_ro(backend->refcache->path); + if (fd < 0) { + git_mutex_unlock(&backend->prlock); + if (fd == GIT_ENOTFOUND) { + git_error_clear(); + return 0; + } + return fd; + } + + if (p_fstat(fd, &st) < 0) { + p_close(fd); + git_mutex_unlock(&backend->prlock); + git_error_set(GIT_ERROR_OS, "unable to stat packed-refs '%s'", backend->refcache->path); + return -1; + } + + if (st.st_size == 0) { + p_close(fd); + git_mutex_unlock(&backend->prlock); + return 0; + } + + git_futils_filestamp_set_from_stat(&backend->packed_refs_stamp, &st); + +#ifdef GIT_WIN32 + /* on windows, we copy the entire file into memory rather than using + * mmap() because using mmap() on windows also locks the file and this + * map is long-lived. */ + backend->packed_refs_map.len = (size_t)st.st_size; + backend->packed_refs_map.data = + git__malloc(backend->packed_refs_map.len); + GIT_ERROR_CHECK_ALLOC(backend->packed_refs_map.data); + { + ssize_t bytesread = + p_read(fd, backend->packed_refs_map.data, + backend->packed_refs_map.len); + error = (bytesread == (ssize_t)backend->packed_refs_map.len) ? 0 : -1; + } +#else + error = git_futils_mmap_ro(&backend->packed_refs_map, fd, 0, (size_t)st.st_size); +#endif + p_close(fd); + if (error < 0) { + git_mutex_unlock(&backend->prlock); + return error; + } + + packed_set_peeling_mode( + backend->packed_refs_map.data, backend->packed_refs_map.len, + backend); + + git_mutex_unlock(&backend->prlock); + return error; +} + +/* + * Find beginning of packed-ref record pointed to by p. + * buf - a lower-bound pointer to some memory buffer + * p - an upper-bound pointer to the same memory buffer + */ +static const char *start_of_record(const char *buf, const char *p) +{ + const char *nl = p; + while (true) { + nl = git__memrchr(buf, '\n', nl - buf); + if (!nl) + return buf; + + if (nl[1] == '^' && nl > buf) + --nl; + else + break; + }; + return nl + 1; +} + +/* + * Find end of packed-ref record pointed to by p. + * end - an upper-bound pointer to some memory buffer + * p - a lower-bound pointer to the same memory buffer + */ +static const char *end_of_record(const char *p, const char *end) +{ + while (1) { + size_t sz = end - p; + p = memchr(p, '\n', sz); + if (!p) + return end; + ++p; + if (p < end && p[0] == '^') + ++p; + else + break; + } + return p; +} + +static int +cmp_record_to_refname(const char *rec, size_t data_end, const char *ref_name) +{ + const size_t ref_len = strlen(ref_name); + int cmp_val; + const char *end; + + rec += GIT_OID_HEXSZ + 1; /* <oid> + space */ + if (data_end < GIT_OID_HEXSZ + 3) { + /* an incomplete (corrupt) record is treated as less than ref_name */ + return -1; + } + data_end -= GIT_OID_HEXSZ + 1; + + end = memchr(rec, '\n', data_end); + if (end) + data_end = end - rec; + + cmp_val = memcmp(rec, ref_name, min(ref_len, data_end)); + + if (cmp_val == 0 && data_end != ref_len) + return (data_end > ref_len) ? 1 : -1; + return cmp_val; +} + +static int packed_unsorted_lookup( + git_reference **out, + refdb_fs_backend *backend, + const char *ref_name) { int error = 0; struct packref *entry; @@ -494,6 +668,85 @@ static int packed_lookup( return error; } +static int packed_lookup( + git_reference **out, + refdb_fs_backend *backend, + const char *ref_name) +{ + int error = 0; + const char *left, *right, *data_end; + + if ((error = packed_map_check(backend)) < 0) + return error; + + if (!backend->sorted) + return packed_unsorted_lookup(out, backend, ref_name); + + left = backend->packed_refs_map.data; + right = data_end = (const char *) backend->packed_refs_map.data + + backend->packed_refs_map.len; + + while (left < right && *left == '#') { + if (!(left = memchr(left, '\n', data_end - left))) + goto parse_failed; + left++; + } + + while (left < right) { + const char *mid, *rec; + int compare; + + mid = left + (right - left) / 2; + rec = start_of_record(left, mid); + compare = cmp_record_to_refname(rec, data_end - rec, ref_name); + + if (compare < 0) { + left = end_of_record(mid, right); + } else if (compare > 0) { + right = rec; + } else { + const char *eol; + git_oid oid, peel, *peel_ptr = NULL; + + if (data_end - rec < GIT_OID_HEXSZ || + git_oid_fromstr(&oid, rec) < 0) { + goto parse_failed; + } + rec += GIT_OID_HEXSZ + 1; + if (!(eol = memchr(rec, '\n', data_end - rec))) { + goto parse_failed; + } + + /* look for optional "^<OID>\n" */ + + if (eol + 1 < data_end) { + rec = eol + 1; + + if (*rec == '^') { + rec++; + if (data_end - rec < GIT_OID_HEXSZ || + git_oid_fromstr(&peel, rec) < 0) { + goto parse_failed; + } + peel_ptr = &peel; + } + } + + *out = git_reference__alloc(ref_name, &oid, peel_ptr); + if (!*out) { + return -1; + } + + return 0; + } + } + return GIT_ENOTFOUND; + +parse_failed: + git_error_set(GIT_ERROR_REFERENCE, "corrupted packed references file"); + return -1; +} + static int refdb_fs_backend__lookup( git_reference **out, git_refdb_backend *_backend, @@ -513,7 +766,6 @@ static int refdb_fs_backend__lookup( git_error_clear(); error = packed_lookup(out, backend, ref_name); } - return error; } @@ -1081,6 +1333,15 @@ static int packed_write(refdb_fs_backend *backend) int error, open_flags = 0; size_t i; + /* take lock and close up packed-refs mmap if open */ + if ((error = git_mutex_lock(&backend->prlock)) < 0) { + return error; + } + + packed_map_free(backend); + + git_mutex_unlock(&backend->prlock); + /* lock the cache to updates while we do this */ if ((error = git_sortedcache_wlock(refcache)) < 0) return error; @@ -1568,6 +1829,12 @@ static void refdb_fs_backend__free(git_refdb_backend *_backend) return; git_sortedcache_free(backend->refcache); + + git_mutex_lock(&backend->prlock); + packed_map_free(backend); + git_mutex_unlock(&backend->prlock); + git_mutex_free(&backend->prlock); + git__free(backend->gitpath); git__free(backend->commonpath); git__free(backend); @@ -2121,6 +2388,11 @@ int git_refdb_backend_fs( backend = git__calloc(1, sizeof(refdb_fs_backend)); GIT_ERROR_CHECK_ALLOC(backend); + if (git_mutex_init(&backend->prlock) < 0) { + git__free(backend); + return -1; + } + if (git_refdb_init_backend(&backend->parent, GIT_REFDB_BACKEND_VERSION) < 0) goto fail; @@ -2183,6 +2455,7 @@ int git_refdb_backend_fs( return 0; fail: + git_mutex_free(&backend->prlock); git_str_dispose(&gitpath); git__free(backend->gitpath); git__free(backend->commonpath); diff --git a/src/revparse.c b/src/revparse.c index 5d3ff77ed..9bc28e9fc 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -799,6 +799,9 @@ static int revparse( if (temp_object != NULL) base_rev = temp_object; break; + } else if (spec[pos+1] == '\0') { + spec = "HEAD"; + break; } /* fall through */ diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c index b3a35ab02..0cf5c8af1 100644 --- a/src/streams/mbedtls.c +++ b/src/streams/mbedtls.c @@ -23,12 +23,14 @@ #endif /* Work around C90-conformance issues */ -#if defined(_MSC_VER) -# define inline __inline -#elif defined(__GNUC__) -# define inline __inline__ -#else -# define inline +#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) +# if defined(_MSC_VER) +# define inline __inline +# elif defined(__GNUC__) +# define inline __inline__ +# else +# define inline +# endif #endif #include <mbedtls/config.h> diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 69edb4a4d..06102817d 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -521,7 +521,6 @@ int git_smart__download_pack( int error = 0; struct network_packetsize_payload npp = {0}; - // TODO git_indexer_progress_cb progress_cb = t->connect_opts.callbacks.transfer_progress; void *progress_payload = t->connect_opts.callbacks.payload; diff --git a/src/xdiff/git-xdiff.h b/src/xdiff/git-xdiff.h new file mode 100644 index 000000000..b75dba819 --- /dev/null +++ b/src/xdiff/git-xdiff.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +/* + * This file provides the necessary indirection between xdiff and + * libgit2. libgit2-specific functionality should live here, so + * that git and libgit2 can share a common xdiff implementation. + */ + +#ifndef INCLUDE_git_xdiff_h__ +#define INCLUDE_git_xdiff_h__ + +#include "regexp.h" + +/* Work around C90-conformance issues */ +#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) +# if defined(_MSC_VER) +# define inline __inline +# elif defined(__GNUC__) +# define inline __inline__ +# else +# define inline +# endif +#endif + +#define xdl_malloc(x) git__malloc(x) +#define xdl_free(ptr) git__free(ptr) +#define xdl_realloc(ptr, x) git__realloc(ptr, x) + +#define XDL_BUG(msg) GIT_ASSERT(msg) + +#define xdl_regex_t git_regexp +#define xdl_regmatch_t git_regmatch + +GIT_INLINE(int) xdl_regexec_buf( + const xdl_regex_t *preg, const char *buf, size_t size, + size_t nmatch, xdl_regmatch_t pmatch[], int eflags) +{ + GIT_UNUSED(preg); + GIT_UNUSED(buf); + GIT_UNUSED(size); + GIT_UNUSED(nmatch); + GIT_UNUSED(pmatch); + GIT_UNUSED(eflags); + GIT_ASSERT("not implemented"); + return -1; +} + +#endif diff --git a/src/xdiff/xdiff.h b/src/xdiff/xdiff.h index 5b13e77a0..a7bf17cd6 100644 --- a/src/xdiff/xdiff.h +++ b/src/xdiff/xdiff.h @@ -23,6 +23,8 @@ #if !defined(XDIFF_H) #define XDIFF_H +#include "git-xdiff.h" + #ifdef __cplusplus extern "C" { #endif /* #ifdef __cplusplus */ @@ -50,16 +52,9 @@ extern "C" { /* xdemitconf_t.flags */ #define XDL_EMIT_FUNCNAMES (1 << 0) +#define XDL_EMIT_NO_HUNK_HDR (1 << 1) #define XDL_EMIT_FUNCCONTEXT (1 << 2) -#define XDL_MMB_READONLY (1 << 0) - -#define XDL_MMF_ATOMIC (1 << 0) - -#define XDL_BDOP_INS 1 -#define XDL_BDOP_CPY 2 -#define XDL_BDOP_INSB 3 - /* merge simplification levels */ #define XDL_MERGE_MINIMAL 0 #define XDL_MERGE_EAGER 1 @@ -73,20 +68,25 @@ extern "C" { /* merge output styles */ #define XDL_MERGE_DIFF3 1 +#define XDL_MERGE_ZEALOUS_DIFF3 2 typedef struct s_mmfile { char *ptr; - size_t size; + long size; } mmfile_t; typedef struct s_mmbuffer { char *ptr; - size_t size; + long size; } mmbuffer_t; typedef struct s_xpparam { unsigned long flags; + /* -I<regex> */ + xdl_regex_t **ignore_regex; + size_t ignore_regex_nr; + /* See Documentation/diff-options.txt. */ char **anchors; size_t anchors_nr; @@ -94,7 +94,11 @@ typedef struct s_xpparam { typedef struct s_xdemitcb { void *priv; - int (*outf)(void *, mmbuffer_t *, int); + int (*out_hunk)(void *, + long old_begin, long old_nr, + long new_begin, long new_nr, + const char *func, long funclen); + int (*out_line)(void *, mmbuffer_t *, int); } xdemitcb_t; typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv); @@ -117,10 +121,6 @@ typedef struct s_bdiffparam { } bdiffparam_t; -#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/src/xdiff/xdiffi.c b/src/xdiff/xdiffi.c index 916295b44..af31b7f4b 100644 --- a/src/xdiff/xdiffi.c +++ b/src/xdiff/xdiffi.c @@ -21,8 +21,6 @@ */ #include "xinclude.h" -#include "integer.h" - #define XDL_MAX_COST_MIN 256 #define XDL_HEUR_MIN_COST 256 @@ -30,41 +28,19 @@ #define XDL_SNAKE_CNT 20 #define XDL_K_HEUR 4 -/** Declare a function as always inlined. */ -#if defined(_MSC_VER) -# define XDL_INLINE(type) static __inline type -#elif defined(__GNUC__) -# define XDL_INLINE(type) static __inline__ type -#else -# define XDL_INLINE(type) static type -#endif - typedef struct s_xdpsplit { long i1, i2; int min_lo, min_hi; } xdpsplit_t; - - - -static long xdl_split(unsigned long const *ha1, long off1, long lim1, - unsigned long const *ha2, long off2, long lim2, - long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl, - xdalgoenv_t *xenv); -static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2); - - - - - /* * See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers. * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both * the forward diagonal starting from (off1, off2) and the backward diagonal * starting from (lim1, lim2). If the K values on the same diagonal crosses - * returns the furthest point of reach. We might end up having to expensive - * cases using this algorithm is full, so a little bit of heuristic is needed - * to cut the search and to return a suboptimal point. + * returns the furthest point of reach. We might encounter expensive edge cases + * using this algorithm, so a little bit of heuristic is needed to cut the + * search and to return a suboptimal point. */ static long xdl_split(unsigned long const *ha1, long off1, long lim1, unsigned long const *ha2, long off2, long lim2, @@ -87,11 +63,13 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, int got_snake = 0; /* - * We need to extent the diagonal "domain" by one. If the next + * We need to extend the diagonal "domain" by one. If the next * values exits the box boundaries we need to change it in the - * opposite direction because (max - min) must be a power of two. + * opposite direction because (max - min) must be a power of + * two. + * * Also we initialize the external K value to -1 so that we can - * avoid extra conditions check inside the core loop. + * avoid extra conditions in the check inside the core loop. */ if (fmin > dmin) kvdf[--fmin - 1] = -1; @@ -122,11 +100,13 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, } /* - * We need to extent the diagonal "domain" by one. If the next + * We need to extend the diagonal "domain" by one. If the next * values exits the box boundaries we need to change it in the - * opposite direction because (max - min) must be a power of two. + * opposite direction because (max - min) must be a power of + * two. + * * Also we initialize the external K value to -1 so that we can - * avoid extra conditions check inside the core loop. + * avoid extra conditions in the check inside the core loop. */ if (bmin > dmin) kvdb[--bmin - 1] = XDL_LINE_MAX; @@ -162,7 +142,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, /* * If the edit cost is above the heuristic trigger and if * we got a good snake, we sample current diagonals to see - * if some of the, have reached an "interesting" path. Our + * if some of them have reached an "interesting" path. Our * measure is a function of the distance from the diagonal * corner (i1 + i2) penalized with the distance from the * mid diagonal itself. If this value is above the current @@ -220,8 +200,9 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, } /* - * Enough is enough. We spent too much time here and now we collect - * the furthest reaching path using the (i1 + i2) measure. + * Enough is enough. We spent too much time here and now we + * collect the furthest reaching path using the (i1 + i2) + * measure. */ if (ec >= xenv->mxcost) { long fbest, fbest1, bbest, bbest1; @@ -268,9 +249,9 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, /* - * Rule: "Divide et Impera". Recursively split the box in sub-boxes by calling - * the box splitting function. Note that the real job (marking changed lines) - * is done in the two boundary reaching checks. + * Rule: "Divide et Impera" (divide & conquer). Recursively split the box in + * sub-boxes by calling the box splitting function. Note that the real job + * (marking changed lines) is done in the two boundary reaching checks. */ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1, diffdata_t *dd2, long off2, long lim2, @@ -330,7 +311,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) { - size_t ndiags, allocsize; + long ndiags; long *kvd, *kvdf, *kvdb; xdalgoenv_t xenv; diffdata_t dd1, dd2; @@ -347,15 +328,14 @@ 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. + * 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. */ - GIT_ERROR_CHECK_ALLOC_ADD3(&ndiags, xe->xdf1.nreff, xe->xdf2.nreff, 3); - GIT_ERROR_CHECK_ALLOC_MULTIPLY(&allocsize, ndiags, 2); - GIT_ERROR_CHECK_ALLOC_ADD(&allocsize, allocsize, 2); - GIT_ERROR_CHECK_ALLOC_MULTIPLY(&allocsize, allocsize, sizeof(long)); + ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3; + if (!(kvd = (long *) xdl_malloc((2 * ndiags + 2) * sizeof(long)))) { - if (!(kvd = (long *) xdl_malloc(allocsize))) { xdl_free_env(xe); return -1; } @@ -410,19 +390,16 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, } -static int recs_match(xrecord_t *rec1, xrecord_t *rec2, long flags) +static int recs_match(xrecord_t *rec1, xrecord_t *rec2) { - return (rec1->ha == rec2->ha && - xdl_recmatch(rec1->ptr, rec1->size, - rec2->ptr, rec2->size, - flags)); + return (rec1->ha == rec2->ha); } /* * If a line is indented more than this, get_indent() just returns this value. * This avoids having to do absurd amounts of work for data that are not - * human-readable text, and also ensures that the output of get_indent fits within - * an int. + * human-readable text, and also ensures that the output of get_indent fits + * within an int. */ #define MAX_INDENT 200 @@ -456,9 +433,9 @@ static int get_indent(xrecord_t *rec) } /* - * If more than this number of consecutive blank rows are found, just return this - * value. This avoids requiring O(N^2) work for pathological cases, and also - * ensures that the output of score_split fits in an int. + * If more than this number of consecutive blank rows are found, just return + * this value. This avoids requiring O(N^2) work for pathological cases, and + * also ensures that the output of score_split fits in an int. */ #define MAX_BLANKS 20 @@ -470,8 +447,8 @@ struct split_measurement { int end_of_file; /* - * How much is the line immediately following the split indented (or -1 if - * the line is blank): + * How much is the line immediately following the split indented (or -1 + * if the line is blank): */ int indent; @@ -481,8 +458,8 @@ struct split_measurement { int pre_blank; /* - * How much is the nearest non-blank line above the split indented (or -1 - * if there is no such line)? + * How much is the nearest non-blank line above the split indented (or + * -1 if there is no such line)? */ int pre_indent; @@ -602,14 +579,19 @@ static void measure_split(const xdfile_t *xdf, long split, #define INDENT_WEIGHT 60 /* + * How far do we slide a hunk at most? + */ +#define INDENT_HEURISTIC_MAX_SLIDING 100 + +/* * Compute a badness score for the hypothetical split whose measurements are - * stored in m. The weight factors were determined empirically using the tools and - * corpus described in + * stored in m. The weight factors were determined empirically using the tools + * and corpus described in * * https://github.com/mhagger/diff-slider-tools * - * Also see that project if you want to improve the weights based on, for example, - * a larger or more diverse corpus. + * Also see that project if you want to improve the weights based on, for + * example, a larger or more diverse corpus. */ static void score_add_split(const struct split_measurement *m, struct split_score *s) { @@ -741,7 +723,7 @@ static void group_init(xdfile_t *xdf, struct xdlgroup *g) * Move g to describe the next (possibly empty) group in xdf and return 0. If g * is already at the end of the file, do nothing and return -1. */ -XDL_INLINE(int) group_next(xdfile_t *xdf, struct xdlgroup *g) +static inline int group_next(xdfile_t *xdf, struct xdlgroup *g) { if (g->end == xdf->nrec) return -1; @@ -757,7 +739,7 @@ XDL_INLINE(int) group_next(xdfile_t *xdf, struct xdlgroup *g) * Move g to describe the previous (possibly empty) group in xdf and return 0. * If g is already at the beginning of the file, do nothing and return -1. */ -XDL_INLINE(int) group_previous(xdfile_t *xdf, struct xdlgroup *g) +static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g) { if (g->start == 0) return -1; @@ -774,10 +756,10 @@ XDL_INLINE(int) group_previous(xdfile_t *xdf, struct xdlgroup *g) * following group, expand this group to include it. Return 0 on success or -1 * if g cannot be slid down. */ -static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g, long flags) +static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g) { if (g->end < xdf->nrec && - recs_match(xdf->recs[g->start], xdf->recs[g->end], flags)) { + recs_match(xdf->recs[g->start], xdf->recs[g->end])) { xdf->rchg[g->start++] = 0; xdf->rchg[g->end++] = 1; @@ -795,10 +777,10 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g, long flags) * into a previous group, expand this group to include it. Return 0 on success * or -1 if g cannot be slid up. */ -static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g, long flags) +static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g) { if (g->start > 0 && - recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1], flags)) { + recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1])) { xdf->rchg[--g->start] = 1; xdf->rchg[--g->end] = 0; @@ -811,12 +793,6 @@ static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g, long flags) } } -static void xdl_bug(const char *msg) -{ - fprintf(stderr, "BUG: %s\n", msg); - exit(1); -} - /* * Move back and forward change groups for a consistent and pretty diff output. * This also helps in finding joinable change groups and reducing the diff @@ -831,13 +807,16 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { group_init(xdfo, &go); while (1) { - /* If the group is empty in the to-be-compacted file, skip it: */ + /* + * If the group is empty in the to-be-compacted file, skip it: + */ if (g.end == g.start) goto next; /* * Now shift the change up and then down as far as possible in - * each direction. If it bumps into any other changes, merge them. + * each direction. If it bumps into any other changes, merge + * them. */ do { groupsize = g.end - g.start; @@ -851,9 +830,9 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { end_matching_other = -1; /* Shift the group backward as much as possible: */ - while (!group_slide_up(xdf, &g, flags)) + while (!group_slide_up(xdf, &g)) if (group_previous(xdfo, &go)) - xdl_bug("group sync broken sliding up"); + XDL_BUG("group sync broken sliding up"); /* * This is this highest that this group can be shifted. @@ -866,10 +845,10 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { /* Now shift the group forward as far as possible: */ while (1) { - if (group_slide_down(xdf, &g, flags)) + if (group_slide_down(xdf, &g)) break; if (group_next(xdfo, &go)) - xdl_bug("group sync broken sliding down"); + XDL_BUG("group sync broken sliding down"); if (go.end > go.start) end_matching_other = g.end; @@ -880,40 +859,46 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { * If the group can be shifted, then we can possibly use this * freedom to produce a more intuitive diff. * - * The group is currently shifted as far down as possible, so the - * heuristics below only have to handle upwards shifts. + * The group is currently shifted as far down as possible, so + * the heuristics below only have to handle upwards shifts. */ if (g.end == earliest_end) { /* no shifting was possible */ } else if (end_matching_other != -1) { /* - * Move the possibly merged group of changes back to line - * up with the last group of changes from the other file - * that it can align with. + * Move the possibly merged group of changes back to + * line up with the last group of changes from the + * other file that it can align with. */ while (go.end == go.start) { - if (group_slide_up(xdf, &g, flags)) - xdl_bug("match disappeared"); + if (group_slide_up(xdf, &g)) + XDL_BUG("match disappeared"); if (group_previous(xdfo, &go)) - xdl_bug("group sync broken sliding to match"); + XDL_BUG("group sync broken sliding to match"); } } else if (flags & XDF_INDENT_HEURISTIC) { /* * Indent heuristic: a group of pure add/delete lines - * implies two splits, one between the end of the "before" - * context and the start of the group, and another between - * the end of the group and the beginning of the "after" - * context. Some splits are aesthetically better and some - * are worse. We compute a badness "score" for each split, - * and add the scores for the two splits to define a - * "score" for each position that the group can be shifted - * to. Then we pick the shift with the lowest score. + * implies two splits, one between the end of the + * "before" context and the start of the group, and + * another between the end of the group and the + * beginning of the "after" context. Some splits are + * aesthetically better and some are worse. We compute + * a badness "score" for each split, and add the scores + * for the two splits to define a "score" for each + * position that the group can be shifted to. Then we + * pick the shift with the lowest score. */ long shift, best_shift = -1; struct split_score best_score; - for (shift = earliest_end; shift <= g.end; shift++) { + shift = earliest_end; + if (g.end - groupsize - 1 > shift) + shift = g.end - groupsize - 1; + if (g.end - INDENT_HEURISTIC_MAX_SLIDING > shift) + shift = g.end - INDENT_HEURISTIC_MAX_SLIDING; + for (; shift <= g.end; shift++) { struct split_measurement m; struct split_score score = {0, 0}; @@ -930,10 +915,10 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { } while (g.end > best_shift) { - if (group_slide_up(xdf, &g, flags)) - xdl_bug("best shift unreached"); + if (group_slide_up(xdf, &g)) + XDL_BUG("best shift unreached"); if (group_previous(xdfo, &go)) - xdl_bug("group sync broken sliding to blank line"); + XDL_BUG("group sync broken sliding to blank line"); } } @@ -942,11 +927,11 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { if (group_next(xdf, &g)) break; if (group_next(xdfo, &go)) - xdl_bug("group sync broken moving to next group"); + XDL_BUG("group sync broken moving to next group"); } if (!group_next(xdfo, &go)) - xdl_bug("group sync broken at end of file"); + XDL_BUG("group sync broken at end of file"); return 0; } @@ -992,8 +977,6 @@ 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) @@ -1006,7 +989,7 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, return 0; } -static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags) +static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags) { xdchange_t *xch; @@ -1027,6 +1010,46 @@ static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags) } } +static int record_matches_regex(xrecord_t *rec, xpparam_t const *xpp) { + xdl_regmatch_t regmatch; + int i; + + for (i = 0; i < xpp->ignore_regex_nr; i++) + if (!xdl_regexec_buf(xpp->ignore_regex[i], rec->ptr, rec->size, 1, + ®match, 0)) + return 1; + + return 0; +} + +static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe, + xpparam_t const *xpp) +{ + xdchange_t *xch; + + for (xch = xscr; xch; xch = xch->next) { + xrecord_t **rec; + int ignore = 1; + long i; + + /* + * Do not override --ignore-blank-lines. + */ + if (xch->ignore) + continue; + + rec = &xe->xdf1.recs[xch->i1]; + for (i = 0; i < xch->chg1 && ignore; i++) + ignore = record_matches_regex(rec[i], xpp); + + rec = &xe->xdf2.recs[xch->i2]; + for (i = 0; i < xch->chg2 && ignore; i++) + ignore = record_matches_regex(rec[i], xpp); + + 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; @@ -1046,7 +1069,10 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, } if (xscr) { if (xpp->flags & XDF_IGNORE_BLANK_LINES) - xdl_mark_ignorable(xscr, &xe, xpp->flags); + xdl_mark_ignorable_lines(xscr, &xe, xpp->flags); + + if (xpp->ignore_regex) + xdl_mark_ignorable_regex(xscr, &xe, xpp); if (ef(&xe, xscr, ecb, xecfg) < 0) { diff --git a/src/xdiff/xemit.c b/src/xdiff/xemit.c index 0ffa6553a..1cbf2b982 100644 --- a/src/xdiff/xemit.c +++ b/src/xdiff/xemit.c @@ -31,7 +31,7 @@ static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) { static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) { - long size, psize = (long)strlen(pre); + long size, psize = strlen(pre); char const *rec; size = xdl_get_rec(xdf, ri, &rec); @@ -81,7 +81,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) > (unsigned long)max_common) { + xch->i1 + ignored - (lxch->i1 + lxch->chg1) > max_common) { break; } else if (!xch->ignore) { lxch = xch; @@ -97,8 +97,6 @@ xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) static long def_ff(const char *rec, long len, char *buf, long sz, void *priv) { - (void)priv; - if (len > 0 && (isalpha((unsigned char)*rec) || /* identifier? */ *rec == '_' || /* also identifier? */ @@ -174,10 +172,12 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, struct func_line func_line = { 0 }; for (xch = xscr; xch; xch = xche->next) { + xdchange_t *xchp = xch; xche = xdl_get_hunk(&xch, xecfg); if (!xch) break; +pre_context_calculation: s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0); s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0); @@ -212,8 +212,23 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, if (fs1 < 0) fs1 = 0; if (fs1 < s1) { - s2 -= s1 - fs1; + s2 = XDL_MAX(s2 - (s1 - fs1), 0); s1 = fs1; + + /* + * Did we extend context upwards into an + * ignored change? + */ + while (xchp != xch && + xchp->i1 + xchp->chg1 <= s1 && + xchp->i2 + xchp->chg2 <= s2) + xchp = xchp->next; + + /* If so, show it after all. */ + if (xchp != xch) { + xch = xchp; + goto pre_context_calculation; + } } } @@ -234,7 +249,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, if (fe1 < 0) fe1 = xe->xdf1.nrec; if (fe1 > e1) { - e2 += fe1 - e1; + e2 = XDL_MIN(e2 + (fe1 - e1), xe->xdf2.nrec); e1 = fe1; } @@ -263,7 +278,8 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, s1 - 1, funclineprev); funclineprev = s1 - 1; } - if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2, + if (!(xecfg->flags & XDL_EMIT_NO_HUNK_HDR) && + xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2, func_line.buf, func_line.len, ecb) < 0) return -1; diff --git a/src/xdiff/xhistogram.c b/src/xdiff/xhistogram.c index 00bbfcec4..80794748b 100644 --- a/src/xdiff/xhistogram.c +++ b/src/xdiff/xhistogram.c @@ -42,8 +42,6 @@ */ #include "xinclude.h" -#include "xtypes.h" -#include "xdiff.h" #define MAX_PTR UINT_MAX #define MAX_CNT UINT_MAX @@ -90,27 +88,21 @@ struct region { #define REC(env, s, l) \ (env->xdf##s.recs[l - 1]) -static int cmp_recs(xpparam_t const *xpp, - xrecord_t *r1, xrecord_t *r2) +static int cmp_recs(xrecord_t *r1, xrecord_t *r2) { - return r1->ha == r2->ha && - xdl_recmatch(r1->ptr, r1->size, r2->ptr, r2->size, - xpp->flags); -} + return r1->ha == r2->ha; -#define CMP_ENV(xpp, env, s1, l1, s2, l2) \ - (cmp_recs(xpp, REC(env, s1, l1), REC(env, s2, l2))) +} #define CMP(i, s1, l1, s2, l2) \ - (cmp_recs(i->xpp, REC(i->env, s1, l1), REC(i->env, s2, l2))) + (cmp_recs(REC(i->env, s1, l1), REC(i->env, s2, l2))) #define TABLE_HASH(index, side, line) \ XDL_HASHLONG((REC(index->env, side, line))->ha, index->table_bits) -static int scanA(struct histindex *index, unsigned int line1, unsigned int count1) +static int scanA(struct histindex *index, int line1, int count1) { - unsigned int ptr; - unsigned int tbl_idx; + unsigned int ptr, tbl_idx; unsigned int chain_len; struct record **rec_chain, *rec; @@ -161,10 +153,8 @@ continue_scan: return 0; } -static int try_lcs( - struct histindex *index, struct region *lcs, unsigned int b_ptr, - unsigned int line1, unsigned int count1, - unsigned int line2, unsigned int count2) +static int try_lcs(struct histindex *index, struct region *lcs, int b_ptr, + int line1, int count1, int line2, int count2) { unsigned int b_next = b_ptr + 1; struct record *rec = index->records[TABLE_HASH(index, 2, b_ptr)]; @@ -236,59 +226,33 @@ static int try_lcs( return b_next; } -static int find_lcs( - struct histindex *index, struct region *lcs, - unsigned int line1, unsigned int count1, - unsigned int line2, unsigned int count2) +static int fall_back_to_classic_diff(xpparam_t const *xpp, xdfenv_t *env, + int line1, int count1, int line2, int count2) { - unsigned int b_ptr; - - if (scanA(index, line1, count1)) - return -1; - - index->cnt = index->max_chain_length + 1; + xpparam_t xpparam; - for (b_ptr = line2; b_ptr <= LINE_END(2); ) - b_ptr = try_lcs(index, lcs, b_ptr, line1, count1, line2, count2); + memset(&xpparam, 0, sizeof(xpparam)); + xpparam.flags = xpp->flags & ~XDF_DIFF_ALGORITHM_MASK; - return index->has_common && index->max_chain_length < index->cnt; + return xdl_fall_back_diff(env, &xpparam, + line1, count1, line2, count2); } -static int fall_back_to_classic_diff(struct histindex *index, - int line1, int count1, int line2, int count2) +static inline void free_index(struct histindex *index) { - xpparam_t xpp; - xpp.flags = index->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK; - - return xdl_fall_back_diff(index->env, &xpp, - line1, count1, line2, count2); + xdl_free(index->records); + xdl_free(index->line_map); + xdl_free(index->next_ptrs); + xdl_cha_free(&index->rcha); } -static int histogram_diff( - xpparam_t const *xpp, xdfenv_t *env, - unsigned int line1, unsigned int count1, - unsigned int line2, unsigned int count2) +static int find_lcs(xpparam_t const *xpp, xdfenv_t *env, + struct region *lcs, + int line1, int count1, int line2, int count2) { + int b_ptr; + int sz, ret = -1; struct histindex index; - struct region lcs; - size_t sz; - int result = -1; - - if (count1 <= 0 && count2 <= 0) - return 0; - - if (LINE_END(1) >= MAX_PTR) - return -1; - - if (!count1) { - while(count2--) - env->xdf2.rchg[line2++ - 1] = 1; - return 0; - } else if (!count2) { - while(count1--) - env->xdf1.rchg[line1++ - 1] = 1; - return 0; - } memset(&index, 0, sizeof(index)); @@ -302,8 +266,7 @@ static int histogram_diff( index.table_bits = xdl_hashbits(count1); sz = index.records_size = 1 << index.table_bits; - GIT_ERROR_CHECK_ALLOC_MULTIPLY(&sz, sz, sizeof(struct record *)); - + sz *= sizeof(struct record *); if (!(index.records = (struct record **) xdl_malloc(sz))) goto cleanup; memset(index.records, 0, sz); @@ -327,9 +290,55 @@ static int histogram_diff( index.ptr_shift = line1; index.max_chain_length = 64; + if (scanA(&index, line1, count1)) + goto cleanup; + + index.cnt = index.max_chain_length + 1; + + for (b_ptr = line2; b_ptr <= LINE_END(2); ) + b_ptr = try_lcs(&index, lcs, b_ptr, line1, count1, line2, count2); + + if (index.has_common && index.max_chain_length < index.cnt) + ret = 1; + else + ret = 0; + +cleanup: + free_index(&index); + return ret; +} + +static int histogram_diff(xpparam_t const *xpp, xdfenv_t *env, + int line1, int count1, int line2, int count2) +{ + struct region lcs; + int lcs_found; + int result; +redo: + result = -1; + + if (count1 <= 0 && count2 <= 0) + return 0; + + if (LINE_END(1) >= MAX_PTR) + return -1; + + if (!count1) { + while(count2--) + env->xdf2.rchg[line2++ - 1] = 1; + return 0; + } else if (!count2) { + while(count1--) + env->xdf1.rchg[line1++ - 1] = 1; + return 0; + } + memset(&lcs, 0, sizeof(lcs)); - if (find_lcs(&index, &lcs, line1, count1, line2, count2)) - result = fall_back_to_classic_diff(&index, line1, count1, line2, count2); + lcs_found = find_lcs(xpp, env, &lcs, line1, count1, line2, count2); + if (lcs_found < 0) + goto out; + else if (lcs_found) + result = fall_back_to_classic_diff(xpp, env, line1, count1, line2, count2); else { if (lcs.begin1 == 0 && lcs.begin2 == 0) { while (count1--) @@ -342,21 +351,21 @@ static int histogram_diff( line1, lcs.begin1 - line1, line2, lcs.begin2 - line2); if (result) - goto cleanup; - result = histogram_diff(xpp, env, - lcs.end1 + 1, LINE_END(1) - lcs.end1, - lcs.end2 + 1, LINE_END(2) - lcs.end2); - if (result) - goto cleanup; + goto out; + /* + * result = histogram_diff(xpp, env, + * lcs.end1 + 1, LINE_END(1) - lcs.end1, + * lcs.end2 + 1, LINE_END(2) - lcs.end2); + * but let's optimize tail recursion ourself: + */ + count1 = LINE_END(1) - lcs.end1; + line1 = lcs.end1 + 1; + count2 = LINE_END(2) - lcs.end2; + line2 = lcs.end2 + 1; + goto redo; } } - -cleanup: - xdl_free(index.records); - xdl_free(index.line_map); - xdl_free(index.next_ptrs); - xdl_cha_free(&index.rcha); - +out: return result; } diff --git a/src/xdiff/xinclude.h b/src/xdiff/xinclude.h index 068ce42f8..75db1d8f3 100644 --- a/src/xdiff/xinclude.h +++ b/src/xdiff/xinclude.h @@ -23,17 +23,7 @@ #if !defined(XINCLUDE_H) #define XINCLUDE_H -#include <ctype.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <limits.h> - -#ifdef _WIN32 -#else -#include <unistd.h> -#endif - +#include "git-xdiff.h" #include "xmacros.h" #include "xdiff.h" #include "xtypes.h" @@ -42,6 +32,5 @@ #include "xdiffi.h" #include "xemit.h" -#include "common.h" #endif /* #if !defined(XINCLUDE_H) */ diff --git a/src/xdiff/xmerge.c b/src/xdiff/xmerge.c index 278cbe124..fff0b594f 100644 --- a/src/xdiff/xmerge.c +++ b/src/xdiff/xmerge.c @@ -109,53 +109,44 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2, return 0; } -static int xdl_recs_copy_0(size_t *out, int use_orig, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest) +static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest) { xrecord_t **recs; - size_t size = 0; - - *out = 0; + int size = 0; recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i; if (count < 1) return 0; - for (i = 0; i < count; ) { + for (i = 0; i < count; size += recs[i++]->size) if (dest) memcpy(dest + size, recs[i]->ptr, recs[i]->size); - - GIT_ERROR_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 (needs_cr) { if (dest) dest[size] = '\r'; - GIT_ERROR_CHECK_ALLOC_ADD(&size, size, 1); + size++; } if (dest) dest[size] = '\n'; - - GIT_ERROR_CHECK_ALLOC_ADD(&size, size, 1); + size++; } } - - *out = size; - return 0; + return size; } -static int xdl_recs_copy(size_t *out, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest) +static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest) { - return xdl_recs_copy_0(out, 0, xe, i, count, needs_cr, add_nl, dest); + return xdl_recs_copy_0(0, xe, i, count, needs_cr, add_nl, dest); } -static int xdl_orig_copy(size_t *out, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest) +static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest) { - return xdl_recs_copy_0(out, 1, xe, i, count, needs_cr, add_nl, dest); + return xdl_recs_copy_0(1, xe, i, count, needs_cr, add_nl, dest); } /* @@ -202,32 +193,26 @@ static int is_cr_needed(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m) return needs_cr < 0 ? 0 : needs_cr; } -static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1, +static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, xdfenv_t *xe2, const char *name2, const char *name3, - size_t size, int i, int style, + int 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); + int marker1_size = (name1 ? strlen(name1) + 1 : 0); + int marker2_size = (name2 ? strlen(name2) + 1 : 0); + int marker3_size = (name3 ? strlen(name3) + 1 : 0); int needs_cr = is_cr_needed(xe1, xe2, m); - size_t copied; - - *out = 0; if (marker_size <= 0) marker_size = DEFAULT_CONFLICT_MARKER_SIZE; /* Before conflicting part */ - if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0, 0, - dest ? dest + size : NULL) < 0) - return -1; - - GIT_ERROR_CHECK_ALLOC_ADD(&size, size, copied); + size += xdl_recs_copy(xe1, i, m->i1 - i, 0, 0, + dest ? dest + size : NULL); if (!dest) { - GIT_ERROR_CHECK_ALLOC_ADD5(&size, size, marker_size, 1, needs_cr, marker1_size); + size += marker_size + 1 + needs_cr + marker1_size; } else { memset(dest + size, '<', marker_size); size += marker_size; @@ -242,16 +227,13 @@ static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1, } /* Postimage from side #1 */ - if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, needs_cr, 1, - dest ? dest + size : NULL) < 0) - return -1; + size += xdl_recs_copy(xe1, m->i1, m->chg1, needs_cr, 1, + dest ? dest + size : NULL); - GIT_ERROR_CHECK_ALLOC_ADD(&size, size, copied); - - if (style == XDL_MERGE_DIFF3) { + if (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_DIFF3) { /* Shared preimage */ if (!dest) { - GIT_ERROR_CHECK_ALLOC_ADD5(&size, size, marker_size, 1, needs_cr, marker3_size); + size += marker_size + 1 + needs_cr + marker3_size; } else { memset(dest + size, '|', marker_size); size += marker_size; @@ -264,15 +246,12 @@ static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1, dest[size++] = '\r'; dest[size++] = '\n'; } - - if (xdl_orig_copy(&copied, xe1, m->i0, m->chg0, needs_cr, 1, - dest ? dest + size : NULL) < 0) - return -1; - GIT_ERROR_CHECK_ALLOC_ADD(&size, size, copied); + size += xdl_orig_copy(xe1, m->i0, m->chg0, needs_cr, 1, + dest ? dest + size : NULL); } if (!dest) { - GIT_ERROR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, needs_cr); + size += marker_size + 1 + needs_cr; } else { memset(dest + size, '=', marker_size); size += marker_size; @@ -282,14 +261,10 @@ static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1, } /* Postimage from side #2 */ - - if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, needs_cr, 1, - dest ? dest + size : NULL) < 0) - return -1; - GIT_ERROR_CHECK_ALLOC_ADD(&size, size, copied); - + size += xdl_recs_copy(xe2, m->i2, m->chg2, needs_cr, 1, + dest ? dest + size : NULL); if (!dest) { - GIT_ERROR_CHECK_ALLOC_ADD5(&size, size, marker_size, 1, needs_cr, marker2_size); + size += marker_size + 1 + needs_cr + marker2_size; } else { memset(dest + size, '>', marker_size); size += marker_size; @@ -302,71 +277,83 @@ static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1, dest[size++] = '\r'; dest[size++] = '\n'; } - - *out = size; - return 0; + return size; } -static int xdl_fill_merge_buffer(size_t *out, - xdfenv_t *xe1, const char *name1, +static int xdl_fill_merge_buffer(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) { - size_t size, copied; - int i; - - *out = 0; + int size, i; for (size = i = 0; m; m = m->next) { if (favor && !m->mode) m->mode = favor; - if (m->mode == 0) { - if (fill_conflict_hunk(&size, xe1, name1, xe2, name2, + if (m->mode == 0) + size = fill_conflict_hunk(xe1, name1, xe2, name2, ancestor_name, size, i, style, m, dest, - marker_size) < 0) - return -1; - } + marker_size); else if (m->mode & 3) { /* Before conflicting part */ - if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0, 0, - dest ? dest + size : NULL) < 0) - return -1; - GIT_ERROR_CHECK_ALLOC_ADD(&size, size, copied); - + size += xdl_recs_copy(xe1, i, m->i1 - i, 0, 0, + dest ? dest + size : NULL); /* Postimage from side #1 */ if (m->mode & 1) { int needs_cr = is_cr_needed(xe1, xe2, m); - if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, needs_cr, (m->mode & 2), - dest ? dest + size : NULL) < 0) - return -1; - GIT_ERROR_CHECK_ALLOC_ADD(&size, size, copied); + size += xdl_recs_copy(xe1, m->i1, m->chg1, needs_cr, (m->mode & 2), + dest ? dest + size : NULL); } - /* Postimage from side #2 */ - if (m->mode & 2) { - if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, 0, 0, - dest ? dest + size : NULL) < 0) - return -1; - GIT_ERROR_CHECK_ALLOC_ADD(&size, size, copied); - } + if (m->mode & 2) + size += xdl_recs_copy(xe2, m->i2, m->chg2, 0, 0, + dest ? dest + size : NULL); } else continue; i = m->i1 + m->chg1; } + size += xdl_recs_copy(xe1, i, xe1->xdf2.nrec - i, 0, 0, + dest ? dest + size : NULL); + return size; +} - if (xdl_recs_copy(&copied, xe1, i, xe1->xdf2.nrec - i, 0, 0, - dest ? dest + size : NULL) < 0) - return -1; - GIT_ERROR_CHECK_ALLOC_ADD(&size, size, copied); +static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags) +{ + return xdl_recmatch(rec1->ptr, rec1->size, + rec2->ptr, rec2->size, flags); +} - *out = size; - return 0; +/* + * Remove any common lines from the beginning and end of the conflicted region. + */ +static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m, + xpparam_t const *xpp) +{ + xrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs; + for (; m; m = m->next) { + /* let's handle just the conflicts */ + if (m->mode) + continue; + + while(m->chg1 && m->chg2 && + recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) { + m->chg1--; + m->chg2--; + m->i1++; + m->i2++; + } + while (m->chg1 && m->chg2 && + recmatch(rec1[m->i1 + m->chg1 - 1], + rec2[m->i2 + m->chg2 - 1], xpp->flags)) { + m->chg1--; + m->chg2--; + } + } } /* @@ -529,7 +516,22 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, int style = xmp->style; int favor = xmp->favor; - if (style == XDL_MERGE_DIFF3) { + /* + * XDL_MERGE_DIFF3 does not attempt to refine conflicts by looking + * at common areas of sides 1 & 2, because the base (side 0) does + * not match and is being shown. Similarly, simplification of + * non-conflicts is also skipped due to the skipping of conflict + * refinement. + * + * XDL_MERGE_ZEALOUS_DIFF3, on the other hand, will attempt to + * refine conflicts looking for common areas of sides 1 & 2. + * However, since the base is being shown and does not match, + * it will only look for common areas at the beginning or end + * of the conflict block. Since XDL_MERGE_ZEALOUS_DIFF3's + * conflict refinement is much more limited in this fashion, the + * conflict simplification will be skipped. + */ + if (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_DIFF3) { /* * "diff3 -m" output does not make sense for anything * more aggressive than XDL_MERGE_EAGER. @@ -650,34 +652,31 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, if (!changes) changes = c; /* refine conflicts */ - if (XDL_MERGE_ZEALOUS <= level && - (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 || - xdl_simplify_non_conflicts(xe1, changes, - XDL_MERGE_ZEALOUS < level) < 0)) { + if (style == XDL_MERGE_ZEALOUS_DIFF3) { + xdl_refine_zdiff3_conflicts(xe1, xe2, changes, xpp); + } else if (XDL_MERGE_ZEALOUS <= level && + (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 || + xdl_simplify_non_conflicts(xe1, changes, + XDL_MERGE_ZEALOUS < level) < 0)) { xdl_cleanup_merge(changes); return -1; } /* output */ if (result) { int marker_size = xmp->marker_size; - size_t size; - - if (xdl_fill_merge_buffer(&size, xe1, name1, xe2, name2, + int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2, ancestor_name, favor, changes, NULL, style, - marker_size) < 0) - return -1; - + marker_size); result->ptr = xdl_malloc(size); if (!result->ptr) { xdl_cleanup_merge(changes); return -1; } result->size = size; - if (xdl_fill_merge_buffer(&size, xe1, name1, xe2, name2, + xdl_fill_merge_buffer(xe1, name1, xe2, name2, ancestor_name, favor, changes, - result->ptr, style, marker_size) < 0) - return -1; + result->ptr, style, marker_size); } return xdl_cleanup_merge(changes); } @@ -717,22 +716,10 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2, status = 0; if (!xscr1) { result->ptr = xdl_malloc(mf2->size); - if (!result->ptr) { - xdl_free_script(xscr2); - xdl_free_env(&xe1); - xdl_free_env(&xe2); - return -1; - } memcpy(result->ptr, mf2->ptr, mf2->size); result->size = mf2->size; } else if (!xscr2) { result->ptr = xdl_malloc(mf1->size); - if (!result->ptr) { - xdl_free_script(xscr1); - xdl_free_env(&xe1); - xdl_free_env(&xe2); - return -1; - } memcpy(result->ptr, mf1->ptr, mf1->size); result->size = mf1->size; } else { diff --git a/src/xdiff/xpatience.c b/src/xdiff/xpatience.c index 53b7d5fd1..c5d48e80a 100644 --- a/src/xdiff/xpatience.c +++ b/src/xdiff/xpatience.c @@ -20,8 +20,6 @@ * */ #include "xinclude.h" -#include "xtypes.h" -#include "xdiff.h" /* * The basic idea of patience diff is to find lines that are unique in @@ -78,7 +76,7 @@ struct hashmap { static int is_anchor(xpparam_t const *xpp, const char *line) { - unsigned long i; + int i; for (i = 0; i < xpp->anchors_nr; i++) { if (!strncmp(line, xpp->anchors[i], strlen(xpp->anchors[i]))) return 1; @@ -92,7 +90,7 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map, { xrecord_t **records = pass == 1 ? map->env->xdf1.recs : map->env->xdf2.recs; - xrecord_t *record = records[line - 1], *other; + xrecord_t *record = records[line - 1]; /* * After xdl_prepare_env() (or more precisely, due to * xdl_classify_record()), the "ha" member of the records (AKA lines) @@ -106,11 +104,7 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map, int index = (int)((record->ha << 1) % map->alloc); while (map->entries[index].line1) { - other = map->env->xdf1.recs[map->entries[index].line1 - 1]; - if (map->entries[index].hash != record->ha || - !xdl_recmatch(record->ptr, record->size, - other->ptr, other->size, - map->xpp->flags)) { + if (map->entries[index].hash != record->ha) { if (++index >= map->alloc) index = 0; continue; @@ -217,9 +211,6 @@ static struct entry *find_longest_common_sequence(struct hashmap *map) */ int anchor_i = -1; - if (!sequence) - return NULL; - for (entry = map->first; entry; entry = entry->next) { if (!entry->line2 || entry->line2 == NON_UNIQUE) continue; @@ -258,8 +249,7 @@ static int match(struct hashmap *map, int line1, int line2) { xrecord_t *record1 = map->env->xdf1.recs[line1 - 1]; xrecord_t *record2 = map->env->xdf2.recs[line2 - 1]; - return xdl_recmatch(record1->ptr, record1->size, - record2->ptr, record2->size, map->xpp->flags); + return record1->ha == record2->ha; } static int patience_diff(mmfile_t *file1, mmfile_t *file2, @@ -294,9 +284,6 @@ static int walk_common_sequence(struct hashmap *map, struct entry *first, /* Recurse */ if (next1 > line1 || next2 > line2) { - struct hashmap submap; - - memset(&submap, 0, sizeof(submap)); if (patience_diff(map->file1, map->file2, map->xpp, map->env, line1, next1 - line1, @@ -323,6 +310,8 @@ static int fall_back_to_classic_diff(struct hashmap *map, int line1, int count1, int line2, int count2) { xpparam_t xpp; + + memset(&xpp, 0, sizeof(xpp)); xpp.flags = map->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK; return xdl_fall_back_diff(map->env, &xpp, diff --git a/src/xdiff/xprepare.c b/src/xdiff/xprepare.c index abeb8fb84..4527a4a07 100644 --- a/src/xdiff/xprepare.c +++ b/src/xdiff/xprepare.c @@ -181,15 +181,11 @@ 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 (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF) - hbits = hsize = 0; - else { - hbits = xdl_hashbits((unsigned int) narec); - hsize = 1 << hbits; - if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *)))) - goto abort; - memset(rhash, 0, hsize * sizeof(xrecord_t *)); - } + hbits = xdl_hashbits((unsigned int) narec); + hsize = 1 << hbits; + if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *)))) + goto abort; + memset(rhash, 0, hsize * sizeof(xrecord_t *)); nrec = 0; if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) { @@ -208,9 +204,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_ crec->size = (long) (cur - prev); crec->ha = hav; recs[nrec++] = crec; - - if ((XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) && - xdl_classify_record(pass, cf, rhash, hbits, crec) < 0) + if (xdl_classify_record(pass, cf, rhash, hbits, crec) < 0) goto abort; } } @@ -219,10 +213,13 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_ goto abort; memset(rchg, 0, (nrec + 2) * sizeof(char)); - if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long)))) - goto abort; - if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long)))) - goto abort; + if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) && + (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) { + if (!(rindex = xdl_malloc((nrec + 1) * sizeof(*rindex)))) + goto abort; + if (!(ha = xdl_malloc((nrec + 1) * sizeof(*ha)))) + goto abort; + } xdf->nrec = nrec; xdf->recs = recs; @@ -279,8 +276,7 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, enl1 = xdl_guess_lines(mf1, sample) + 1; enl2 = xdl_guess_lines(mf2, sample) + 1; - if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF && - xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) + if (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) return -1; if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) { @@ -305,8 +301,7 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, return -1; } - if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) - xdl_free_classifier(&cf); + xdl_free_classifier(&cf); return 0; } diff --git a/src/xdiff/xutils.c b/src/xdiff/xutils.c index 17c9ae184..cfa6e2220 100644 --- a/src/xdiff/xutils.c +++ b/src/xdiff/xutils.c @@ -23,8 +23,6 @@ #include "xinclude.h" - - long xdl_bogosqrt(long n) { long i; @@ -52,7 +50,7 @@ int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize, mb[2].size = strlen(mb[2].ptr); i++; } - if (ecb->outf(ecb->priv, mb, i) < 0) { + if (ecb->out_line(ecb->priv, mb, i) < 0) { return -1; } @@ -342,8 +340,9 @@ int xdl_num_out(char *out, long val) { return str - out; } -int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, - const char *func, long funclen, xdemitcb_t *ecb) { +static int xdl_format_hunk_hdr(long s1, long c1, long s2, long c2, + const char *func, long funclen, + xdemitcb_t *ecb) { int nb = 0; mmbuffer_t mb; char buf[128]; @@ -376,7 +375,7 @@ int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, nb += 3; if (func && funclen) { buf[nb++] = ' '; - if (funclen > (long)(sizeof(buf) - nb - 1)) + if (funclen > sizeof(buf) - nb - 1) funclen = sizeof(buf) - nb - 1; memcpy(buf + nb, func, funclen); nb += funclen; @@ -385,9 +384,21 @@ int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, mb.ptr = buf; mb.size = nb; - if (ecb->outf(ecb->priv, &mb, 1) < 0) + if (ecb->out_line(ecb->priv, &mb, 1) < 0) return -1; + return 0; +} +int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, + const char *func, long funclen, + xdemitcb_t *ecb) { + if (!ecb->out_hunk) + return xdl_format_hunk_hdr(s1, c1, s2, c2, func, funclen, ecb); + if (ecb->out_hunk(ecb->priv, + c1 ? s1 : s1 - 1, c1, + c2 ? s2 : s2 - 1, c2, + func, funclen) < 0) + return -1; return 0; } diff --git a/tests/cherrypick/workdir.c b/tests/cherrypick/workdir.c index 868b9d5ff..8fd1ecbdf 100644 --- a/tests/cherrypick/workdir.c +++ b/tests/cherrypick/workdir.c @@ -170,9 +170,9 @@ void test_cherrypick_workdir__conflicts(void) cl_assert(strcmp(git_str_cstr(&mergemsg_buf), "Change all files\n" \ "\n" \ - "Conflicts:\n" \ - "\tfile2.txt\n" \ - "\tfile3.txt\n") == 0); + "#Conflicts:\n" \ + "#\tfile2.txt\n" \ + "#\tfile3.txt\n") == 0); cl_git_pass(git_futils_readbuffer(&conflicting_buf, TEST_REPO_PATH "/file2.txt")); @@ -352,10 +352,10 @@ void test_cherrypick_workdir__both_renamed(void) cl_assert(strcmp(git_str_cstr(&mergemsg_buf), "Renamed file3.txt -> file3.txt.renamed\n" \ "\n" \ - "Conflicts:\n" \ - "\tfile3.txt\n" \ - "\tfile3.txt.renamed\n" \ - "\tfile3.txt.renamed_on_branch\n") == 0); + "#Conflicts:\n" \ + "#\tfile3.txt\n" \ + "#\tfile3.txt.renamed\n" \ + "#\tfile3.txt.renamed_on_branch\n") == 0); git_str_dispose(&mergemsg_buf); git_commit_free(commit); diff --git a/tests/core/futils.c b/tests/core/futils.c index b87ea183b..3501765f6 100644 --- a/tests/core/futils.c +++ b/tests/core/futils.c @@ -87,3 +87,29 @@ void test_core_futils__recursive_rmdir_keeps_symlink_targets(void) cl_must_pass(p_rmdir("dir-target")); cl_must_pass(p_unlink("file-target")); } + +void test_core_futils__mktmp_umask(void) +{ +#ifdef GIT_WIN32 + cl_skip(); +#else + git_str path = GIT_STR_INIT; + struct stat st; + int fd; + + umask(0); + cl_assert((fd = git_futils_mktmp(&path, "foo", 0777)) >= 0); + cl_must_pass(p_fstat(fd, &st)); + cl_assert_equal_i(st.st_mode & 0777, 0777); + cl_must_pass(p_unlink(path.ptr)); + close(fd); + + umask(077); + cl_assert((fd = git_futils_mktmp(&path, "foo", 0777)) >= 0); + cl_must_pass(p_fstat(fd, &st)); + cl_assert_equal_i(st.st_mode & 0777, 0700); + cl_must_pass(p_unlink(path.ptr)); + close(fd); + git_str_dispose(&path); +#endif +} diff --git a/tests/merge/conflict_data.h b/tests/merge/conflict_data.h index 27f19c1b0..0b1e7ee03 100644 --- a/tests/merge/conflict_data.h +++ b/tests/merge/conflict_data.h @@ -36,6 +36,15 @@ "this file is changed in branch and master\n" \ ">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n" +#define CONFLICTING_ZDIFF3_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 fbc54e11e..6296f3b7b 100644 --- a/tests/merge/files.c +++ b/tests/merge/files.c @@ -424,3 +424,42 @@ void test_merge_files__crlf_conflict_markers_for_crlf_files(void) cl_assert(memcmp(expected_diff3, result.ptr, expected_len) == 0); git_merge_file_result_free(&result); } + +void test_merge_files__conflicts_in_zdiff3(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_zdiff3 = + "1,\nfoo,\nbar,\n" \ + "<<<<<<< file.txt\n" \ + "||||||| file.txt\n# add more here\n" \ + "=======\nquux,\nwoot,\n" \ + ">>>>>>> file.txt\nbaz,\n3,\n"; + size_t expected_zdiff3_len = strlen(expected_zdiff3); + + ancestor.ptr = "1,\n# add more here\n3,\n"; + ancestor.size = strlen(ancestor.ptr); + ancestor.path = "file.txt"; + ancestor.mode = 0100644; + + ours.ptr = "1,\nfoo,\nbar,\nbaz,\n3,\n"; + ours.size = strlen(ours.ptr); + ours.path = "file.txt"; + ours.mode = 0100644; + + theirs.ptr = "1,\nfoo,\nbar,\nquux,\nwoot,\nbaz,\n3,\n"; + theirs.size = strlen(theirs.ptr); + theirs.path = "file.txt"; + theirs.mode = 0100644; + + opts.flags |= GIT_MERGE_FILE_STYLE_ZDIFF3; + cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts)); + cl_assert_equal_i(0, result.automergeable); + cl_assert_equal_i(expected_zdiff3_len, result.len); + cl_assert(memcmp(expected_zdiff3, result.ptr, expected_zdiff3_len) == 0); + git_merge_file_result_free(&result); +} diff --git a/tests/merge/workdir/simple.c b/tests/merge/workdir/simple.c index f51ff09a7..b9d3fc24c 100644 --- a/tests/merge/workdir/simple.c +++ b/tests/merge/workdir/simple.c @@ -278,8 +278,8 @@ void test_merge_workdir_simple__mergefile(void) cl_assert(strcmp(git_str_cstr(&mergemsg_buf), "Merge commit '7cb63eed597130ba4abb87b3e544b85021905520'\n" \ "\n" \ - "Conflicts:\n" \ - "\tconflicting.txt\n") == 0); + "#Conflicts:\n" \ + "#\tconflicting.txt\n") == 0); git_str_dispose(&conflicting_buf); git_str_dispose(&mergemsg_buf); @@ -323,6 +323,42 @@ void test_merge_workdir_simple__diff3(void) cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3)); } +void test_merge_workdir_simple__zdiff3(void) +{ + git_str conflicting_buf = GIT_STR_INIT; + + struct merge_index_entry merge_index_entries[] = { + ADDED_IN_MASTER_INDEX_ENTRY, + AUTOMERGEABLE_INDEX_ENTRY, + CHANGED_IN_BRANCH_INDEX_ENTRY, + CHANGED_IN_MASTER_INDEX_ENTRY, + + { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" }, + { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" }, + { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" }, + + UNCHANGED_INDEX_ENTRY, + }; + + struct merge_reuc_entry merge_reuc_entries[] = { + AUTOMERGEABLE_REUC_ENTRY, + REMOVED_IN_BRANCH_REUC_ENTRY, + REMOVED_IN_MASTER_REUC_ENTRY + }; + + set_core_autocrlf_to(repo, false); + + merge_simple_branch(0, GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3); + + cl_git_pass(git_futils_readbuffer(&conflicting_buf, + TEST_REPO_PATH "/conflicting.txt")); + cl_assert_equal_s(CONFLICTING_ZDIFF3_FILE, conflicting_buf.ptr); + git_str_dispose(&conflicting_buf); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 8)); + cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3)); +} + void test_merge_workdir_simple__union(void) { git_str conflicting_buf = GIT_STR_INIT; @@ -436,6 +472,48 @@ void test_merge_workdir_simple__diff3_from_config(void) git_config_free(config); } +void test_merge_workdir_simple__zdiff3_from_config(void) +{ + git_config *config; + git_str conflicting_buf = GIT_STR_INIT; + + struct merge_index_entry merge_index_entries[] = { + ADDED_IN_MASTER_INDEX_ENTRY, + AUTOMERGEABLE_INDEX_ENTRY, + CHANGED_IN_BRANCH_INDEX_ENTRY, + CHANGED_IN_MASTER_INDEX_ENTRY, + + { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" }, + { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" }, + { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" }, + + UNCHANGED_INDEX_ENTRY, + }; + + struct merge_reuc_entry merge_reuc_entries[] = { + AUTOMERGEABLE_REUC_ENTRY, + REMOVED_IN_BRANCH_REUC_ENTRY, + REMOVED_IN_MASTER_REUC_ENTRY + }; + + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_set_string(config, "merge.conflictstyle", "zdiff3")); + + set_core_autocrlf_to(repo, false); + + merge_simple_branch(0, 0); + + cl_git_pass(git_futils_readbuffer(&conflicting_buf, + TEST_REPO_PATH "/conflicting.txt")); + cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_ZDIFF3_FILE) == 0); + git_str_dispose(&conflicting_buf); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 8)); + cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3)); + + git_config_free(config); +} + void test_merge_workdir_simple__merge_overrides_config(void) { git_config *config; diff --git a/tests/refs/crashes.c b/tests/refs/crashes.c index 228f479a0..4f508aed2 100644 --- a/tests/refs/crashes.c +++ b/tests/refs/crashes.c @@ -1,4 +1,5 @@ #include "clar_libgit2.h" +#include "refs.h" void test_refs_crashes__double_free(void) { @@ -18,3 +19,26 @@ void test_refs_crashes__double_free(void) cl_git_sandbox_cleanup(); } + +void test_refs_crashes__empty_packedrefs(void) +{ + git_repository *repo; + git_reference *ref; + const char *REFNAME = "refs/heads/xxx"; + git_str temp_path = GIT_STR_INIT; + int fd = 0; + + repo = cl_git_sandbox_init("empty_bare.git"); + + /* create zero-length packed-refs file */ + cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(repo), GIT_PACKEDREFS_FILE)); + cl_git_pass(((fd = p_creat(temp_path.ptr, 0644)) < 0)); + cl_git_pass(p_close(fd)); + + /* should fail gracefully */ + cl_git_fail_with( + GIT_ENOTFOUND, git_reference_lookup(&ref, repo, REFNAME)); + + cl_git_sandbox_cleanup(); + git_str_dispose(&temp_path); +} diff --git a/tests/refs/revparse.c b/tests/refs/revparse.c index 5fb758504..0bd2ae5bc 100644 --- a/tests/refs/revparse.c +++ b/tests/refs/revparse.c @@ -881,3 +881,10 @@ void test_refs_revparse__uneven_sizes(void) test_object("a65fedf39aefe402d3bb6e24df4d", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); } + +void test_refs_revparse__parses_at_head(void) +{ + test_id("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVSPEC_SINGLE); + test_id("@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVSPEC_SINGLE); + test_id("@", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVSPEC_SINGLE); +} diff --git a/tests/resources/peeled.git/packed-refs b/tests/resources/peeled.git/packed-refs index ad053d550..078f237ed 100644 --- a/tests/resources/peeled.git/packed-refs +++ b/tests/resources/peeled.git/packed-refs @@ -1,4 +1,4 @@ -# pack-refs with: peeled fully-peeled +# pack-refs with: peeled fully-peeled sorted c2596aa0151888587ec5c0187f261e63412d9e11 refs/foo/tag-outside-tags ^0df1a5865c8abfc09f1f2182e6a31be550e99f07 0df1a5865c8abfc09f1f2182e6a31be550e99f07 refs/heads/master diff --git a/tests/resources/testrepo.git/packed-refs b/tests/resources/testrepo.git/packed-refs index 52f5e876f..fadd7e88a 100644 --- a/tests/resources/testrepo.git/packed-refs +++ b/tests/resources/testrepo.git/packed-refs @@ -1,3 +1,3 @@ -# pack-refs with: peeled +# pack-refs with: peeled sorted 41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/heads/packed 5b5b025afb0b4c913b4c338a42934a3863bf3644 refs/heads/packed-test diff --git a/tests/resources/testrepo/.gitted/packed-refs b/tests/resources/testrepo/.gitted/packed-refs index 6018a19d2..b57898695 100644 --- a/tests/resources/testrepo/.gitted/packed-refs +++ b/tests/resources/testrepo/.gitted/packed-refs @@ -1,4 +1,4 @@ -# pack-refs with: peeled +# pack-refs with: peeled sorted 41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/heads/packed 5b5b025afb0b4c913b4c338a42934a3863bf3644 refs/heads/packed-test b25fa35b38051e4ae45d4222e795f9df2e43f1d1 refs/tags/packed-tag diff --git a/tests/resources/testrepo2/.gitted/packed-refs b/tests/resources/testrepo2/.gitted/packed-refs index 97ea6a848..01de87173 100644 --- a/tests/resources/testrepo2/.gitted/packed-refs +++ b/tests/resources/testrepo2/.gitted/packed-refs @@ -1,4 +1,4 @@ -# pack-refs with: peeled fully-peeled +# pack-refs with: peeled fully-peeled sorted 36060c58702ed4c2a40832c51758d5344201d89a refs/remotes/origin/master 41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/remotes/origin/packed 5b5b025afb0b4c913b4c338a42934a3863bf3644 refs/tags/v0.9 diff --git a/tests/revert/workdir.c b/tests/revert/workdir.c index 0c9810814..3f54280ca 100644 --- a/tests/revert/workdir.c +++ b/tests/revert/workdir.c @@ -119,8 +119,8 @@ void test_revert_workdir__conflicts(void) "\n" \ "This reverts commit 72333f47d4e83616630ff3b0ffe4c0faebcc3c45.\n" "\n" \ - "Conflicts:\n" \ - "\tfile1.txt\n") == 0); + "#Conflicts:\n" \ + "#\tfile1.txt\n") == 0); git_commit_free(commit); git_commit_free(head); |