FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Override `core.filemode=false` in wasm build (#189) · QuantStack/git2cpp@36b094a · GitHub

Repository navigation

Commit 36b094a

Browse files
authored
Override core.filemode=false in wasm build (#189)
* Override core.filemode=false in wasm build * Harden wasm tests
1 parent d4542e3 commit 36b094a

8 files changed

Lines changed: 147 additions & 4 deletions

File tree

‎CMakeLists.txt‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ set(GIT2CPP_SRC
9090
${GIT2CPP_SOURCE_DIR}/utils/ansi_code.hpp
9191
${GIT2CPP_SOURCE_DIR}/utils/common.cpp
9292
${GIT2CPP_SOURCE_DIR}/utils/common.hpp
93+
${GIT2CPP_SOURCE_DIR}/utils/config_overrides.cpp
94+
${GIT2CPP_SOURCE_DIR}/utils/config_overrides.hpp
9395
${GIT2CPP_SOURCE_DIR}/utils/credentials.cpp
9496
${GIT2CPP_SOURCE_DIR}/utils/credentials.hpp
9597
${GIT2CPP_SOURCE_DIR}/utils/git_exception.cpp

‎src/utils/config_overrides.cpp‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff 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+
}

‎src/utils/config_overrides.hpp‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff 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);

‎src/wrapper/repository_wrapper.cpp‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <fstream>
55
#include <iostream>
66

7+
#include "../utils/config_overrides.hpp"
78
#include "../utils/git_exception.hpp"
89
#include "../wrapper/commit_wrapper.hpp"
910
#include "../wrapper/config_wrapper.hpp"
@@ -22,20 +23,23 @@ repository_wrapper repository_wrapper::open(std::string_view directory)
2223
{
2324
repository_wrapper rw;
2425
throw_if_error(git_repository_open(&(rw.p_resource), directory.data()));
26+
apply_config_overrides(rw.p_resource);
2527
return rw;
2628
}
2729

2830
repository_wrapper repository_wrapper::init(std::string_view directory, bool bare)
2931
{
3032
repository_wrapper rw;
3133
throw_if_error(git_repository_init(&(rw.p_resource), directory.data(), bare));
34+
apply_config_overrides(rw.p_resource);
3235
return rw;
3336
}
3437

3538
repository_wrapper repository_wrapper::init_ext(std::string_view directory, git_repository_init_options* opts)
3639
{
3740
repository_wrapper rw;
3841
throw_if_error(git_repository_init_ext(&(rw.p_resource), directory.data(), opts));
42+
apply_config_overrides(rw.p_resource);
3943
return rw;
4044
}
4145

@@ -44,6 +48,7 @@ repository_wrapper::clone(std::string_view url, std::string_view path, const git
4448
{
4549
repository_wrapper rw;
4650
throw_if_error(git_clone(&(rw.p_resource), url.data(), path.data(), &opts));
51+
apply_config_overrides(rw.p_resource);
4752
return rw;
4853
}
4954

‎test/conftest.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,21 @@ def private_test_repo():
9898
# Functions not fixtures below here.
9999

100100

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+
101116
def strip_ansi_colours(text):
102117
# Strip ansi colour code sequences from a string.
103118
return re.sub(r"\x1b\[[^m]*m", "", text)

‎test/conftest_wasm.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,11 @@ def subprocess_run(
170170
raise RuntimeError(f"Error setting cwd to {cwd}")
171171

172172
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.
175175
# Could do better here.
176176
s = str(s)
177-
if " " in s and not s.endswith("'"):
177+
if re.search(r"\s", s) and not s.endswith("'"):
178178
return "'" + s + "'"
179179
return s
180180

‎test/test_clone.py‎

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import pytest
44

5-
from .conftest import GIT2CPP_TEST_WASM
5+
from .conftest import GIT2CPP_TEST_WASM, get_filemode_lines, set_filemode
66

77
xsimd_url = "https://github.com/xtensor-stack/xsimd.git"
88
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):
229229
assert (tmp_path / "xsimd").exists()
230230
assert (tmp_path / "xsimd/include").exists()
231231
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"]

‎test/test_init.py‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import subprocess
22
from pathlib import Path
33

4+
from .conftest import GIT2CPP_TEST_WASM, get_filemode_lines, set_filemode
5+
46

57
def test_init_in_directory(git2cpp_path, tmp_path):
68
# tmp_path exists and is empty.
@@ -155,3 +157,32 @@ def test_init_initial_branch_bare(git2cpp_path, tmp_path):
155157

156158
head = (tmp_path / "HEAD").read_text()
157159
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"]

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL