| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -90,6 +90,8 @@ set(GIT2CPP_SRC | |||
| 90 | 90 | ${GIT2CPP_SOURCE_DIR}/utils/ansi_code.hpp | |
| 91 | 91 | ${GIT2CPP_SOURCE_DIR}/utils/common.cpp | |
| 92 | 92 | ${GIT2CPP_SOURCE_DIR}/utils/common.hpp | |
| 93 | + ${GIT2CPP_SOURCE_DIR}/utils/config_overrides.cpp | ||
| 94 | + ${GIT2CPP_SOURCE_DIR}/utils/config_overrides.hpp | ||
| 93 | 95 | ${GIT2CPP_SOURCE_DIR}/utils/credentials.cpp | |
| 94 | 96 | ${GIT2CPP_SOURCE_DIR}/utils/credentials.hpp | |
| 95 | 97 | ${GIT2CPP_SOURCE_DIR}/utils/git_exception.cpp | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,51 @@ | |||
| 1 | + #include "config_overrides.hpp" | ||
| 2 | + | ||
| 3 | + #ifdef EMSCRIPTEN | ||
| 4 | + # include <iterator> | ||
| 5 | + | ||
| 6 | + # include <git2/sys/config.h> | ||
| 7 | + | ||
| 8 | + # include "git_exception.hpp" | ||
| 9 | + #endif | ||
| 10 | + | ||
| 11 | + void apply_config_overrides([[maybe_unused]] git_repository* repo) | ||
| 12 | + { | ||
| 13 | + #ifdef EMSCRIPTEN | ||
| 14 | + // Config values to override, each in "name=value" form. core.filemode is disabled because | ||
| 15 | + // the emscripten filesystem does support executable file permissions but some JupyterLite | ||
| 16 | + // drive implementations do not. | ||
| 17 | + const char* values[] = {"core.filemode=false"}; | ||
| 18 | + | ||
| 19 | + // Create an in-memory config backend holding these values. This backend is read-only, so | ||
| 20 | + // any config writes (e.g. `git config` or values set by clone/init) skip it and go to the | ||
| 21 | + // highest-priority writable backend instead, which is the repository's own config file. | ||
| 22 | + // The overrides are therefore never persisted to disk. backend_type is a label that | ||
| 23 | + // identifies where these entries came from when inspecting a config entry's origin. | ||
| 24 | + git_config_backend* backend = nullptr; | ||
| 25 | + git_config_backend_memory_options opts = GIT_CONFIG_BACKEND_MEMORY_OPTIONS_INIT; | ||
| 26 | + opts.backend_type = "git2cpp-overrides"; | ||
| 27 | + throw_if_error(git_config_backend_from_values(&backend, values, std::size(values), &opts)); | ||
| 28 | + | ||
| 29 | + // git_repository_config returns the repository's cached, shared config object rather than a | ||
| 30 | + // copy, so adding a backend to it affects all later config lookups through this repository. | ||
| 31 | + // GIT_CONFIG_LEVEL_APP is the highest priority level, above the local, global and system | ||
| 32 | + // config files, so these values win over any existing setting. The repo is passed so that | ||
| 33 | + // conditional includes can be evaluated, and force=0 means fail rather than replace if a | ||
| 34 | + // backend already exists at this level. | ||
| 35 | + git_config* cfg = nullptr; | ||
| 36 | + int error = git_repository_config(&cfg, repo); | ||
| 37 | + if (error == 0) | ||
| 38 | + { | ||
| 39 | + error = git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_APP, repo, 0); | ||
| 40 | + // Only releases our reference; the repository still holds the config and its backends. | ||
| 41 | + git_config_free(cfg); | ||
| 42 | + } | ||
| 43 | + if (error < 0) | ||
| 44 | + { | ||
| 45 | + // On success ownership of the backend passes to the config, but on failure it is still | ||
| 46 | + // ours to free. | ||
| 47 | + backend->free(backend); | ||
| 48 | + throw_if_error(error); | ||
| 49 | + } | ||
| 50 | + #endif | ||
| 51 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,8 @@ | |||
| 1 | + #pragma once | ||
| 2 | + | ||
| 3 | + #include <git2.h> | ||
| 4 | + | ||
| 5 | + // Apply in-memory config overrides to a repository's config. These have a higher priority than | ||
| 6 | + // the repository's config file but are never written to it. Only used in WebAssembly, where | ||
| 7 | + // core.filemode=false is set as the filesystem does not always support executable file permissions. | ||
| 8 | + void apply_config_overrides(git_repository* repo); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,6 +4,7 @@ | |||
| 4 | 4 | #include <fstream> | |
| 5 | 5 | #include <iostream> | |
| 6 | 6 | ||
| 7 | + #include "../utils/config_overrides.hpp" | ||
| 7 | 8 | #include "../utils/git_exception.hpp" | |
| 8 | 9 | #include "../wrapper/commit_wrapper.hpp" | |
| 9 | 10 | #include "../wrapper/config_wrapper.hpp" | |
@@ -22,20 +23,23 @@ repository_wrapper repository_wrapper::open(std::string_view directory) | |||
| 22 | 23 | { | |
| 23 | 24 | repository_wrapper rw; | |
| 24 | 25 | throw_if_error(git_repository_open(&(rw.p_resource), directory.data())); | |
| 26 | + apply_config_overrides(rw.p_resource); | ||
| 25 | 27 | return rw; | |
| 26 | 28 | } | |
| 27 | 29 | ||
| 28 | 30 | repository_wrapper repository_wrapper::init(std::string_view directory, bool bare) | |
| 29 | 31 | { | |
| 30 | 32 | repository_wrapper rw; | |
| 31 | 33 | throw_if_error(git_repository_init(&(rw.p_resource), directory.data(), bare)); | |
| 34 | + apply_config_overrides(rw.p_resource); | ||
| 32 | 35 | return rw; | |
| 33 | 36 | } | |
| 34 | 37 | ||
| 35 | 38 | repository_wrapper repository_wrapper::init_ext(std::string_view directory, git_repository_init_options* opts) | |
| 36 | 39 | { | |
| 37 | 40 | repository_wrapper rw; | |
| 38 | 41 | throw_if_error(git_repository_init_ext(&(rw.p_resource), directory.data(), opts)); | |
| 42 | + apply_config_overrides(rw.p_resource); | ||
| 39 | 43 | return rw; | |
| 40 | 44 | } | |
| 41 | 45 | ||
@@ -44,6 +48,7 @@ repository_wrapper::clone(std::string_view url, std::string_view path, const git | |||
| 44 | 48 | { | |
| 45 | 49 | repository_wrapper rw; | |
| 46 | 50 | throw_if_error(git_clone(&(rw.p_resource), url.data(), path.data(), &opts)); | |
| 51 | + apply_config_overrides(rw.p_resource); | ||
| 47 | 52 | return rw; | |
| 48 | 53 | } | |
| 49 | 54 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -98,6 +98,21 @@ def private_test_repo(): | |||
| 98 | 98 | # Functions not fixtures below here. | |
| 99 | 99 | ||
| 100 | 100 | ||
| 101 | + def get_filemode_lines(git2cpp_path, cwd): | ||
| 102 | + # Return all core.filemode lines from config list. | ||
| 103 | + cmd_list = [git2cpp_path, "config", "list"] | ||
| 104 | + p_list = subprocess.run(cmd_list, capture_output=True, cwd=cwd, text=True) | ||
| 105 | + assert p_list.returncode == 0 | ||
| 106 | + return [line for line in p_list.stdout.splitlines() if line.startswith("core.filemode")] | ||
| 107 | + | ||
| 108 | + | ||
| 109 | + def set_filemode(git2cpp_path, cwd, value): | ||
| 110 | + # Set core.filemode in the repository's config file. | ||
| 111 | + cmd_set = [git2cpp_path, "config", "set", "core.filemode", value] | ||
| 112 | + p_set = subprocess.run(cmd_set, capture_output=True, cwd=cwd, text=True) | ||
| 113 | + assert p_set.returncode == 0 | ||
| 114 | + | ||
| 115 | + | ||
| 101 | 116 | def strip_ansi_colours(text): | |
| 102 | 117 | # Strip ansi colour code sequences from a string. | |
| 103 | 118 | return re.sub(r"\x1b\[[^m]*m", "", text) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -170,11 +170,11 @@ def subprocess_run( | |||
| 170 | 170 | raise RuntimeError(f"Error setting cwd to {cwd}") | |
| 171 | 171 | ||
| 172 | 172 | def maybe_wrap_arg(s: str | MockPath) -> str: | |
| 173 | - # An argument containing spaces needs to be wrapped in quotes if it is not already, due | ||
| 174 | - # to how the command is passed to cockle as a single string. | ||
| 173 | + # An argument containing whitespace (including newlines) needs to be wrapped in quotes if | ||
| 174 | + # it is not already, due to how the command is passed to cockle as a single string. | ||
| 175 | 175 | # Could do better here. | |
| 176 | 176 | s = str(s) | |
| 177 | - if " " in s and not s.endswith("'"): | ||
| 177 | + if re.search(r"\s", s) and not s.endswith("'"): | ||
| 178 | 178 | return "'" + s + "'" | |
| 179 | 179 | return s | |
| 180 | 180 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,7 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | 3 | import pytest | |
| 4 | 4 | ||
| 5 | - from .conftest import GIT2CPP_TEST_WASM | ||
| 5 | + from .conftest import GIT2CPP_TEST_WASM, get_filemode_lines, set_filemode | ||
| 6 | 6 | ||
| 7 | 7 | xsimd_url = "https://github.com/xtensor-stack/xsimd.git" | |
| 8 | 8 | xtl_url = "https://github.com/xtensor-stack/xtl.git" | |
@@ -229,3 +229,34 @@ def test_clone_large_repo(git2cpp_path, tmp_path, run_in_tmp_path): | |||
| 229 | 229 | assert (tmp_path / "xsimd").exists() | |
| 230 | 230 | assert (tmp_path / "xsimd/include").exists() | |
| 231 | 231 | assert (tmp_path / "xsimd/xsimdConfig.cmake.in").exists() | |
| 232 | + | ||
| 233 | + | ||
| 234 | + def test_clone_filemode(git2cpp_path, tmp_path, run_in_tmp_path): | ||
| 235 | + clone_cmd = [git2cpp_path, "clone", xtl_url] | ||
| 236 | + p_clone = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True) | ||
| 237 | + assert p_clone.returncode == 0 | ||
| 238 | + | ||
| 239 | + xtl_path = tmp_path / "xtl" | ||
| 240 | + | ||
| 241 | + filemode_lines = get_filemode_lines(git2cpp_path, xtl_path) | ||
| 242 | + if GIT2CPP_TEST_WASM: | ||
| 243 | + # In WebAssembly core.filemode=false is set by an in-memory config override that has higher | ||
| 244 | + # priority than the repository's config file, so is listed after it. | ||
| 245 | + assert filemode_lines == ["core.filemode=true", "core.filemode=false"] | ||
| 246 | + else: | ||
| 247 | + assert filemode_lines == ["core.filemode=true"] | ||
| 248 | + | ||
| 249 | + # Setting writes to the repository's config file, not the in-memory override. | ||
| 250 | + set_filemode(git2cpp_path, xtl_path, "true") | ||
| 251 | + filemode_lines = get_filemode_lines(git2cpp_path, xtl_path) | ||
| 252 | + if GIT2CPP_TEST_WASM: | ||
| 253 | + assert filemode_lines == ["core.filemode=true", "core.filemode=false"] | ||
| 254 | + else: | ||
| 255 | + assert filemode_lines == ["core.filemode=true"] | ||
| 256 | + | ||
| 257 | + set_filemode(git2cpp_path, xtl_path, "false") | ||
| 258 | + filemode_lines = get_filemode_lines(git2cpp_path, xtl_path) | ||
| 259 | + if GIT2CPP_TEST_WASM: | ||
| 260 | + assert filemode_lines == ["core.filemode=false", "core.filemode=false"] | ||
| 261 | + else: | ||
| 262 | + assert filemode_lines == ["core.filemode=false"] | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,6 +1,8 @@ | |||
| 1 | 1 | import subprocess | |
| 2 | 2 | from pathlib import Path | |
| 3 | 3 | ||
| 4 | + from .conftest import GIT2CPP_TEST_WASM, get_filemode_lines, set_filemode | ||
| 5 | + | ||
| 4 | 6 | ||
| 5 | 7 | def test_init_in_directory(git2cpp_path, tmp_path): | |
| 6 | 8 | # tmp_path exists and is empty. | |
@@ -155,3 +157,32 @@ def test_init_initial_branch_bare(git2cpp_path, tmp_path): | |||
| 155 | 157 | ||
| 156 | 158 | head = (tmp_path / "HEAD").read_text() | |
| 157 | 159 | assert "refs/heads/main" in head | |
| 160 | + | ||
| 161 | + | ||
| 162 | + def test_init_filemode(git2cpp_path, tmp_path): | ||
| 163 | + cmd_init = [git2cpp_path, "init", "."] | ||
| 164 | + p_init = subprocess.run(cmd_init, capture_output=True, cwd=tmp_path, text=True) | ||
| 165 | + assert p_init.returncode == 0 | ||
| 166 | + | ||
| 167 | + filemode_lines = get_filemode_lines(git2cpp_path, tmp_path) | ||
| 168 | + if GIT2CPP_TEST_WASM: | ||
| 169 | + # In WebAssembly core.filemode=false is set by an in-memory config override that has higher | ||
| 170 | + # priority than the repository's config file, so is listed after it. | ||
| 171 | + assert filemode_lines == ["core.filemode=true", "core.filemode=false"] | ||
| 172 | + else: | ||
| 173 | + assert filemode_lines == ["core.filemode=true"] | ||
| 174 | + | ||
| 175 | + # Setting writes to the repository's config file, not the in-memory override. | ||
| 176 | + set_filemode(git2cpp_path, tmp_path, "true") | ||
| 177 | + filemode_lines = get_filemode_lines(git2cpp_path, tmp_path) | ||
| 178 | + if GIT2CPP_TEST_WASM: | ||
| 179 | + assert filemode_lines == ["core.filemode=true", "core.filemode=false"] | ||
| 180 | + else: | ||
| 181 | + assert filemode_lines == ["core.filemode=true"] | ||
| 182 | + | ||
| 183 | + set_filemode(git2cpp_path, tmp_path, "false") | ||
| 184 | + filemode_lines = get_filemode_lines(git2cpp_path, tmp_path) | ||
| 185 | + if GIT2CPP_TEST_WASM: | ||
| 186 | + assert filemode_lines == ["core.filemode=false", "core.filemode=false"] | ||
| 187 | + else: | ||
| 188 | + assert filemode_lines == ["core.filemode=false"] | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments