Skip to content

Commit ae23edf

Browse files
committed
Merge branch 'en/rust-xdiff' into seen
Rust! Comments? * en/rust-xdiff: ivec: create a vector type that is interoperable between C and Rust github workflows: upload Cargo.lock win+Meson: do allow linking with the Rust-built xdiff github workflows: install rust xdiff: introduce rust doc: add a policy for using Rust
2 parents 9e16568 + ea44558 commit ae23edf

24 files changed

Lines changed: 1209 additions & 32 deletions

.github/workflows/main.yml

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ jobs:
2626
outputs:
2727
enabled: ${{ steps.check-ref.outputs.enabled }}${{ steps.skip-if-redundant.outputs.enabled }}
2828
skip_concurrent: ${{ steps.check-ref.outputs.skip_concurrent }}
29+
rust_version_minimum: 1.61.0
30+
rust_version_windows: 1.78.0
31+
rust_version_musl: 1.72.0
32+
## the rust target is inferred by rustup unless specified
33+
rust_target_windows_make: x86_64-pc-windows-gnu
34+
rust_target_windows_meson: x86_64-pc-windows-msvc
35+
rust_target_32bit_linux: i686-unknown-linux-gnu
2936
steps:
3037
- name: try to clone ci-config branch
3138
run: |
@@ -108,12 +115,34 @@ jobs:
108115
needs: ci-config
109116
if: needs.ci-config.outputs.enabled == 'yes'
110117
runs-on: windows-latest
118+
env:
119+
CARGO_HOME: "/c/Users/runneradmin/.cargo"
111120
concurrency:
112121
group: windows-build-${{ github.ref }}
113122
cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
114123
steps:
115124
- uses: actions/checkout@v4
116125
- uses: git-for-windows/setup-git-for-windows-sdk@v1
126+
- name: ensure that libuserenv.a is present
127+
shell: bash
128+
run: |
129+
cd /mingw64/lib && {
130+
test -f libuserenv.a ||
131+
/c/Program\ Files/Git/mingw64/bin/curl -Lo libuserenv.a \
132+
https://github.com/git-for-windows/git-sdk-64/raw/HEAD/mingw64/lib/libuserenv.a
133+
}
134+
- name: Install rustup via github actions
135+
uses: actions-rs/toolchain@v1
136+
with:
137+
toolchain: stable
138+
profile: minimal
139+
override: false
140+
- name: Install Rust toolchain
141+
shell: bash
142+
env:
143+
RUST_VERSION: ${{ needs.ci-config.outputs.rust_version_windows }}
144+
RUST_TARGET: ${{ needs.ci-config.outputs.rust_target_windows_make }}
145+
run: ci/install-rust-toolchain.sh
117146
- name: build
118147
shell: bash
119148
env:
@@ -127,6 +156,11 @@ jobs:
127156
with:
128157
name: windows-artifacts
129158
path: artifacts
159+
- name: upload Cargo.lock
160+
uses: actions/upload-artifact@v4
161+
with:
162+
name: cargo-lock-windows
163+
path: rust/Cargo.lock
130164
windows-test:
131165
name: win test
132166
runs-on: windows-latest
@@ -254,12 +288,26 @@ jobs:
254288
needs: ci-config
255289
if: needs.ci-config.outputs.enabled == 'yes'
256290
runs-on: windows-latest
291+
env:
292+
CARGO_HOME: "/c/Users/runneradmin/.cargo"
257293
concurrency:
258294
group: windows-meson-build-${{ github.ref }}
259295
cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
260296
steps:
261297
- uses: actions/checkout@v4
262298
- uses: actions/setup-python@v5
299+
- name: Install rustup via github actions
300+
uses: actions-rs/toolchain@v1
301+
with:
302+
toolchain: stable
303+
profile: minimal
304+
override: false
305+
- name: Install Rust toolchain
306+
shell: bash
307+
env:
308+
RUST_VERSION: ${{ needs.ci-config.outputs.rust_version_windows }}
309+
RUST_TARGET: ${{ needs.ci-config.outputs.rust_target_windows_meson }}
310+
run: ci/install-rust-toolchain.sh
263311
- name: Set up dependencies
264312
shell: pwsh
265313
run: pip install meson ninja
@@ -274,6 +322,11 @@ jobs:
274322
with:
275323
name: windows-meson-artifacts
276324
path: build
325+
- name: Upload Cargo.lock
326+
uses: actions/upload-artifact@v4
327+
with:
328+
name: cargo-lock-windows-meson
329+
path: rust/Cargo.lock
277330
windows-meson-test:
278331
name: win+Meson test
279332
runs-on: windows-latest
@@ -329,11 +382,24 @@ jobs:
329382
jobname: ${{matrix.vector.jobname}}
330383
CI_JOB_IMAGE: ${{matrix.vector.pool}}
331384
TEST_OUTPUT_DIRECTORY: ${{github.workspace}}/t
385+
CARGO_HOME: "/Users/runner/.cargo"
332386
runs-on: ${{matrix.vector.pool}}
333387
steps:
334388
- uses: actions/checkout@v4
335389
- run: ci/install-dependencies.sh
336-
- run: ci/run-build-and-tests.sh
390+
- name: Install rustup via github actions
391+
uses: actions-rs/toolchain@v1
392+
with:
393+
toolchain: stable
394+
profile: minimal
395+
override: false
396+
- name: Install Rust toolchain
397+
shell: bash
398+
env:
399+
RUST_VERSION: ${{ needs.ci-config.outputs.rust_version_minimum }}
400+
run: ci/install-rust-toolchain.sh
401+
- name: Run build and tests
402+
run: ci/run-build-and-tests.sh
337403
- name: print test failures
338404
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
339405
run: ci/print-test-failures.sh
@@ -343,6 +409,11 @@ jobs:
343409
with:
344410
name: failed-tests-${{matrix.vector.jobname}}
345411
path: ${{env.FAILED_TEST_ARTIFACTS}}
412+
- name: Upload Cargo.lock
413+
uses: actions/upload-artifact@v4
414+
with:
415+
name: cargo-lock-${{matrix.vector.jobname}}
416+
path: rust/Cargo.lock
346417
fuzz-smoke-test:
347418
name: fuzz smoke test
348419
needs: ci-config
@@ -393,9 +464,11 @@ jobs:
393464
cc: gcc
394465
- jobname: linux-musl-meson
395466
image: alpine:latest
467+
rust_version_override: ${{ needs.ci-config.outputs.rust_version_musl }}
396468
# Supported until 2025-04-02.
397469
- jobname: linux32
398470
image: i386/ubuntu:focal
471+
rust_target_override: ${{ needs.ci-config.outputs.rust_target_32bit_linux }}
399472
- jobname: pedantic
400473
image: fedora:latest
401474
# A RHEL 8 compatible distro. Supported until 2029-05-31.
@@ -408,7 +481,9 @@ jobs:
408481
jobname: ${{matrix.vector.jobname}}
409482
CC: ${{matrix.vector.cc}}
410483
CI_JOB_IMAGE: ${{matrix.vector.image}}
484+
CI_IS_DOCKER: "true"
411485
CUSTOM_PATH: /custom
486+
CARGO_HOME: /home/builder/.cargo
412487
runs-on: ubuntu-latest
413488
container: ${{matrix.vector.image}}
414489
steps:
@@ -433,6 +508,13 @@ jobs:
433508
- run: ci/install-dependencies.sh
434509
- run: useradd builder --create-home
435510
- run: chown -R builder .
511+
- name: Install rustup via script
512+
run: sudo --preserve-env --set-home --user=builder ci/install-rustup.sh
513+
- name: Install Rust toolchain
514+
env:
515+
RUST_VERSION: ${{ matrix.vector.rust_version_override || needs.ci-config.outputs.rust_version_minimum }}
516+
RUST_TARGET: ${{ matrix.vector.rust_target_override || '' }}
517+
run: sudo --preserve-env --set-home --user=builder ci/install-rust-toolchain.sh
436518
- run: sudo --preserve-env --set-home --user=builder ci/run-build-and-tests.sh
437519
- name: print test failures
438520
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
@@ -443,6 +525,11 @@ jobs:
443525
with:
444526
name: failed-tests-${{matrix.vector.jobname}}
445527
path: ${{env.FAILED_TEST_ARTIFACTS}}
528+
- name: Upload Cargo.lock
529+
uses: actions/upload-artifact@v4
530+
with:
531+
name: cargo-lock-${{matrix.vector.jobname}}
532+
path: rust/Cargo.lock
446533
static-analysis:
447534
needs: ci-config
448535
if: needs.ci-config.outputs.enabled == 'yes'

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,3 +256,6 @@ Release/
256256
/contrib/buildsystems/out
257257
/contrib/libgit-rs/target
258258
/contrib/libgit-sys/target
259+
/.idea/
260+
/rust/target/
261+
/rust/Cargo.lock

Documentation/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ TECH_DOCS += technical/parallel-checkout
127127
TECH_DOCS += technical/partial-clone
128128
TECH_DOCS += technical/platform-support
129129
TECH_DOCS += technical/racy-git
130+
TECH_DOCS += technical/rust-support
130131
TECH_DOCS += technical/reftable
131132
TECH_DOCS += technical/scalar
132133
TECH_DOCS += technical/send-pack-pipeline

Documentation/technical/platform-support.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ meet the following minimum requirements:
3333
3434
* Has active security support (taking security releases of dependencies, etc)
3535
36+
* Supports Rust and the toolchain version specified in link:rust-support.adoc[].
37+
3638
These requirements are a starting point, and not sufficient on their own for the
3739
Git community to be enthusiastic about supporting your platform. Maintainers of
3840
platforms which do meet these requirements can follow the steps below to make it
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
Usage of Rust in Git
2+
====================
3+
4+
Objective
5+
---------
6+
Introduce Rust into Git incrementally to improve security and maintainability.
7+
8+
Background
9+
----------
10+
Git has historically been written primarily in C, with some portions in shell,
11+
Perl, or other languages. At the time it was originally written, this was
12+
important for portability and was a logical choice for software development.
13+
14+
:0: link:https://security.googleblog.com/2024/09/eliminating-memory-safety-vulnerabilities-Android.html
15+
:1: link:https://www.cisa.gov/resources-tools/resources/product-security-bad-practices
16+
17+
However, as time has progressed, we've seen an increased concern with memory
18+
safety vulnerabilities and the development of newer languages, such as Rust,
19+
that substantially limit or eliminate this class of vulnerabilities.
20+
Development in a variety of projects has found that memory safety
21+
vulnerabilities constitute about 70% of vulnerabilities of software in
22+
languages that are not memory safe. For instance, {0}[one survey of Android]
23+
found that memory safety vulnerabilities decreased from 76% to 24% over six
24+
years due to an increase in memory safe code. Similarly, the U.S. government
25+
is {1}[proposing to classify development in memory unsafe languages as a
26+
Product Security Bad Practice"].
27+
28+
These risks are even more substantial when we consider the fact that Git is a
29+
network-facing service. Many organizations run Git servers internally or use a
30+
cloud-based forge, and the risk of accidental exposure or compromise of user
31+
data is substantial. It's important to ensure that Git, whether it's used
32+
locally or remotely, is robustly secure.
33+
34+
In addition, C is a difficult language to write well and concisely. While it
35+
is of course possible to do anything with C, it lacks built-in support for
36+
niceties found in modern languages, such as hash tables, generics, typed
37+
errors, and automatic destruction, and most modern language offer shorter, more
38+
ergonomic syntax for expressing code. This is valuable functionality that can
39+
allow Git to be developed more rapidly, more easily, by more developers of a
40+
variety of levels, and with more confidence in the correctness of the code.
41+
42+
For these reasons, adding Rust to Git is a sensible and prudent move that will
43+
allow us to improve the quality of the code and potentially attract new developers.
44+
45+
Goals
46+
-----
47+
1. Git continues to build, run, and pass tests on a wide variety of operating
48+
systems and architectures.
49+
2. Transition from C to Rust is incremental; that is, code can be ported as it
50+
is convenient and Git does not need to transition all at once.
51+
3. Git continues to support older operating systems in conformance with the
52+
platform support policy.
53+
54+
Non-Goals
55+
---------
56+
1. Support for every possible operating system and architecture. Git already
57+
has a platform support policy which defines what is supported and we already
58+
exclude some operating systems for various reasons (e.g., lacking enough POSIX
59+
tools to pass the test suite).
60+
2. Implementing C-only versions of Rust code or compiling a C-only Git. This
61+
would be difficult to maintain and would not offer the ergonomic benefits we
62+
desire.
63+
64+
Design
65+
------
66+
Git will adopt Rust incrementally. This transition will start with the
67+
creation of a static library that can be linked into the existing Git binaries.
68+
At some point, we may wish to expose a dynamic library and compile the Git
69+
binaries themselves using Rust. Using an incremental approach allows us to
70+
determine as we go along how to structure our code in the best way for the
71+
project and avoids the need to make hard, potentially disruptive, transitions
72+
caused by porting a binary wholesale from one language to another that might
73+
introduce bugs.
74+
75+
Crates like libc or rustix define types like c_long, but in ways that are not
76+
safe across platforms.
77+
From https://docs.rs/rustix/latest/rustix/ffi/type.c_long.html:
78+
79+
This type will always be i32 or i64. Most notably, many Linux-based
80+
systems assume an i64, but Windows assumes i32. The C standard technically
81+
only requires that this type be a signed integer that is at least 32 bits
82+
and at least the size of an int, although in practice, no system would
83+
have a long that is neither an i32 nor i64.
84+
85+
Also, note that other locations, such as
86+
https://docs.rs/libc/latest/libc/type.c_long.html, just hardcode c_long as i64
87+
even though C may mean i32 on some platforms.
88+
89+
As such, using the c_long type would give us portability issues, and
90+
perpetuate some of the bugs git has faced across platforms. Avoid using C's
91+
types (long, unsigned, char, etc.), and switch to unambiguous types (e.g. i32
92+
or i64) before trying to make C and Rust interoperate.
93+
94+
Crates like libc and rustix may have also traditionally aided interoperability
95+
with older versions of Rust (e.g. when worrying about stat[64] system calls),
96+
but the Rust standard library in newer versions of Rust handle these concerns
97+
in a platform agnostic way. There may arise cases where we need to consider
98+
these crates, but for now we omit them.
99+
100+
Tools like bindgen and cbindgen create C-styled unsafe Rust code rather than
101+
idiomatic Rust; where possible, we prefer to switch to idiomatic Rust. Any
102+
standard C library functions that are needed can be manually wrapped on the
103+
Rust side.
104+
105+
Rust upstream releases every six weeks and only supports the latest stable
106+
release. While it is nice that upstream is active, we would like our software
107+
releases to have a lifespan exceeding six weeks. To allow compiling our code
108+
on a variety of systems, we will support the version of Rust in Debian stable,
109+
plus, for a year after a new Debian stable is released, the version in Debian
110+
oldstable.
111+
112+
This provides an approximately three-year lifespan of support for a Rust
113+
release and allows us to support a variety of operating systems and
114+
architectures, including those for which Rust upstream does not build binaries.
115+
Debian stable is the benchmark distribution used by many Rust projects when
116+
determining supported Rust versions, and it is an extremely portable and
117+
popular free software operating system that is available to the public at no
118+
charge, which makes it a sensible choice for us as well.
119+
120+
We may change this policy if the Rust project issues long-term support releases
121+
or the Rust community and distributors agree on releases to target as if they
122+
were long-term support releases.
123+
124+
This version support policy necessitates that we be very careful about the
125+
dependencies we include, since many Rust projects support only the latest
126+
stable version. However, we typically have been careful about dependencies in
127+
the first place, so this should not be a major departure from existing policy,
128+
although it may be a change for some existing Rust developers.
129+
130+
We will avoid including the `Cargo.lock` file in the repository and instead
131+
specify minimum dependency versions in the `Cargo.toml` file. We want to allow
132+
people to use newer versions of dependencies if necessary to support newer
133+
platforms without needing to force upgrades of dependencies on all users, and
134+
it provides additional flexibility for distribution maintainers.
135+
136+
We do not plan to support beta or nightly versions of the Rust compiler. These
137+
versions may change rapidly and especially parts of the toolchain such as
138+
Clippy, the lint tool, can have false positives or add additional warnings with
139+
too great of a frequency to be supportable by the project. However, we do plan
140+
to support alternate compilers, such as the rust_codegen_gcc backend and gccrs
141+
when they are stable and support our desired release versions. This will
142+
provide greater support for more operating systems and architectures.

0 commit comments

Comments
 (0)